summaryrefslogtreecommitdiff
path: root/ruby_1_8_5/lib
diff options
context:
space:
mode:
Diffstat (limited to 'ruby_1_8_5/lib')
-rw-r--r--ruby_1_8_5/lib/.document103
-rw-r--r--ruby_1_8_5/lib/English.rb155
-rw-r--r--ruby_1_8_5/lib/Env.rb18
-rw-r--r--ruby_1_8_5/lib/README96
-rw-r--r--ruby_1_8_5/lib/abbrev.rb103
-rw-r--r--ruby_1_8_5/lib/base64.rb133
-rw-r--r--ruby_1_8_5/lib/benchmark.rb569
-rw-r--r--ruby_1_8_5/lib/cgi-lib.rb272
-rw-r--r--ruby_1_8_5/lib/cgi.rb2303
-rw-r--r--ruby_1_8_5/lib/cgi/.document2
-rw-r--r--ruby_1_8_5/lib/cgi/session.rb484
-rw-r--r--ruby_1_8_5/lib/cgi/session/pstore.rb111
-rw-r--r--ruby_1_8_5/lib/complex.rb631
-rw-r--r--ruby_1_8_5/lib/csv.rb992
-rw-r--r--ruby_1_8_5/lib/date.rb1328
-rw-r--r--ruby_1_8_5/lib/date/format.rb603
-rw-r--r--ruby_1_8_5/lib/date2.rb5
-rw-r--r--ruby_1_8_5/lib/debug.rb947
-rw-r--r--ruby_1_8_5/lib/delegate.rb334
-rw-r--r--ruby_1_8_5/lib/drb.rb2
-rw-r--r--ruby_1_8_5/lib/drb/acl.rb144
-rw-r--r--ruby_1_8_5/lib/drb/drb.rb1763
-rw-r--r--ruby_1_8_5/lib/drb/eq.rb16
-rw-r--r--ruby_1_8_5/lib/drb/extserv.rb64
-rw-r--r--ruby_1_8_5/lib/drb/extservm.rb96
-rw-r--r--ruby_1_8_5/lib/drb/gw.rb122
-rw-r--r--ruby_1_8_5/lib/drb/invokemethod.rb36
-rw-r--r--ruby_1_8_5/lib/drb/observer.rb22
-rw-r--r--ruby_1_8_5/lib/drb/ssl.rb190
-rw-r--r--ruby_1_8_5/lib/drb/timeridconv.rb91
-rw-r--r--ruby_1_8_5/lib/drb/unix.rb108
-rw-r--r--ruby_1_8_5/lib/e2mmap.rb195
-rw-r--r--ruby_1_8_5/lib/erb.rb826
-rw-r--r--ruby_1_8_5/lib/eregex.rb37
-rw-r--r--ruby_1_8_5/lib/fileutils.rb1578
-rw-r--r--ruby_1_8_5/lib/finalize.rb193
-rw-r--r--ruby_1_8_5/lib/find.rb79
-rw-r--r--ruby_1_8_5/lib/forwardable.rb218
-rw-r--r--ruby_1_8_5/lib/ftools.rb261
-rw-r--r--ruby_1_8_5/lib/generator.rb380
-rw-r--r--ruby_1_8_5/lib/getoptlong.rb621
-rw-r--r--ruby_1_8_5/lib/getopts.rb127
-rw-r--r--ruby_1_8_5/lib/gserver.rb253
-rw-r--r--ruby_1_8_5/lib/importenv.rb33
-rw-r--r--ruby_1_8_5/lib/ipaddr.rb766
-rw-r--r--ruby_1_8_5/lib/irb.rb342
-rw-r--r--ruby_1_8_5/lib/irb/cmd/chws.rb33
-rw-r--r--ruby_1_8_5/lib/irb/cmd/fork.rb39
-rw-r--r--ruby_1_8_5/lib/irb/cmd/help.rb34
-rw-r--r--ruby_1_8_5/lib/irb/cmd/load.rb67
-rw-r--r--ruby_1_8_5/lib/irb/cmd/nop.rb39
-rw-r--r--ruby_1_8_5/lib/irb/cmd/pushws.rb39
-rw-r--r--ruby_1_8_5/lib/irb/cmd/subirb.rb43
-rw-r--r--ruby_1_8_5/lib/irb/completion.rb205
-rw-r--r--ruby_1_8_5/lib/irb/context.rb255
-rw-r--r--ruby_1_8_5/lib/irb/ext/change-ws.rb62
-rw-r--r--ruby_1_8_5/lib/irb/ext/history.rb110
-rw-r--r--ruby_1_8_5/lib/irb/ext/loader.rb120
-rw-r--r--ruby_1_8_5/lib/irb/ext/math-mode.rb37
-rw-r--r--ruby_1_8_5/lib/irb/ext/multi-irb.rb241
-rw-r--r--ruby_1_8_5/lib/irb/ext/save-history.rb85
-rw-r--r--ruby_1_8_5/lib/irb/ext/tracer.rb61
-rw-r--r--ruby_1_8_5/lib/irb/ext/use-loader.rb65
-rw-r--r--ruby_1_8_5/lib/irb/ext/workspaces.rb56
-rw-r--r--ruby_1_8_5/lib/irb/extend-command.rb264
-rw-r--r--ruby_1_8_5/lib/irb/frame.rb67
-rw-r--r--ruby_1_8_5/lib/irb/help.rb33
-rw-r--r--ruby_1_8_5/lib/irb/init.rb259
-rw-r--r--ruby_1_8_5/lib/irb/input-method.rb120
-rw-r--r--ruby_1_8_5/lib/irb/lc/error.rb30
-rw-r--r--ruby_1_8_5/lib/irb/lc/help-message35
-rw-r--r--ruby_1_8_5/lib/irb/lc/ja/error.rb27
-rw-r--r--ruby_1_8_5/lib/irb/lc/ja/help-message36
-rw-r--r--ruby_1_8_5/lib/irb/locale.rb184
-rw-r--r--ruby_1_8_5/lib/irb/notifier.rb145
-rw-r--r--ruby_1_8_5/lib/irb/output-method.rb85
-rw-r--r--ruby_1_8_5/lib/irb/ruby-lex.rb1149
-rw-r--r--ruby_1_8_5/lib/irb/ruby-token.rb273
-rw-r--r--ruby_1_8_5/lib/irb/slex.rb285
-rw-r--r--ruby_1_8_5/lib/irb/version.rb16
-rw-r--r--ruby_1_8_5/lib/irb/workspace.rb107
-rw-r--r--ruby_1_8_5/lib/irb/ws-for-case-2.rb15
-rw-r--r--ruby_1_8_5/lib/irb/xmp.rb86
-rw-r--r--ruby_1_8_5/lib/jcode.rb220
-rw-r--r--ruby_1_8_5/lib/logger.rb703
-rw-r--r--ruby_1_8_5/lib/mailread.rb62
-rw-r--r--ruby_1_8_5/lib/mathn.rb308
-rw-r--r--ruby_1_8_5/lib/matrix.rb1272
-rw-r--r--ruby_1_8_5/lib/mkmf.rb1529
-rw-r--r--ruby_1_8_5/lib/monitor.rb352
-rw-r--r--ruby_1_8_5/lib/mutex_m.rb122
-rw-r--r--ruby_1_8_5/lib/net/ftp.rb923
-rw-r--r--ruby_1_8_5/lib/net/http.rb2274
-rw-r--r--ruby_1_8_5/lib/net/https.rb173
-rw-r--r--ruby_1_8_5/lib/net/imap.rb3357
-rw-r--r--ruby_1_8_5/lib/net/pop.rb879
-rw-r--r--ruby_1_8_5/lib/net/protocol.rb390
-rw-r--r--ruby_1_8_5/lib/net/smtp.rb697
-rw-r--r--ruby_1_8_5/lib/net/telnet.rb742
-rw-r--r--ruby_1_8_5/lib/observer.rb192
-rw-r--r--ruby_1_8_5/lib/open-uri.rb688
-rw-r--r--ruby_1_8_5/lib/open3.rb82
-rw-r--r--ruby_1_8_5/lib/optparse.rb1727
-rw-r--r--ruby_1_8_5/lib/optparse/date.rb17
-rw-r--r--ruby_1_8_5/lib/optparse/shellwords.rb6
-rw-r--r--ruby_1_8_5/lib/optparse/time.rb10
-rw-r--r--ruby_1_8_5/lib/optparse/uri.rb6
-rw-r--r--ruby_1_8_5/lib/optparse/version.rb70
-rw-r--r--ruby_1_8_5/lib/ostruct.rb146
-rw-r--r--ruby_1_8_5/lib/parsearg.rb87
-rw-r--r--ruby_1_8_5/lib/parsedate.rb15
-rw-r--r--ruby_1_8_5/lib/pathname.rb1062
-rw-r--r--ruby_1_8_5/lib/ping.rb53
-rw-r--r--ruby_1_8_5/lib/pp.rb647
-rw-r--r--ruby_1_8_5/lib/prettyprint.rb896
-rw-r--r--ruby_1_8_5/lib/profile.rb6
-rw-r--r--ruby_1_8_5/lib/profiler.rb59
-rw-r--r--ruby_1_8_5/lib/pstore.rb396
-rw-r--r--ruby_1_8_5/lib/racc/parser.rb442
-rw-r--r--ruby_1_8_5/lib/rational.rb530
-rw-r--r--ruby_1_8_5/lib/rdoc/README489
-rw-r--r--ruby_1_8_5/lib/rdoc/code_objects.rb765
-rw-r--r--ruby_1_8_5/lib/rdoc/diagram.rb335
-rw-r--r--ruby_1_8_5/lib/rdoc/dot/dot.rb255
-rw-r--r--ruby_1_8_5/lib/rdoc/generators/chm_generator.rb112
-rw-r--r--ruby_1_8_5/lib/rdoc/generators/html_generator.rb1509
-rw-r--r--ruby_1_8_5/lib/rdoc/generators/ri_generator.rb268
-rw-r--r--ruby_1_8_5/lib/rdoc/generators/template/chm/chm.rb87
-rw-r--r--ruby_1_8_5/lib/rdoc/generators/template/html/hefss.rb418
-rw-r--r--ruby_1_8_5/lib/rdoc/generators/template/html/html.rb711
-rw-r--r--ruby_1_8_5/lib/rdoc/generators/template/html/kilmer.rb435
-rw-r--r--ruby_1_8_5/lib/rdoc/generators/template/html/old_html.rb728
-rw-r--r--ruby_1_8_5/lib/rdoc/generators/template/html/one_page_html.rb122
-rw-r--r--ruby_1_8_5/lib/rdoc/generators/template/xml/rdf.rb112
-rw-r--r--ruby_1_8_5/lib/rdoc/generators/template/xml/xml.rb112
-rw-r--r--ruby_1_8_5/lib/rdoc/generators/xml_generator.rb130
-rw-r--r--ruby_1_8_5/lib/rdoc/markup/.document2
-rw-r--r--ruby_1_8_5/lib/rdoc/markup/sample/rdoc2latex.rb16
-rw-r--r--ruby_1_8_5/lib/rdoc/markup/sample/sample.rb42
-rw-r--r--ruby_1_8_5/lib/rdoc/markup/simple_markup.rb476
-rw-r--r--ruby_1_8_5/lib/rdoc/markup/simple_markup/fragments.rb328
-rw-r--r--ruby_1_8_5/lib/rdoc/markup/simple_markup/inline.rb340
-rw-r--r--ruby_1_8_5/lib/rdoc/markup/simple_markup/lines.rb151
-rw-r--r--ruby_1_8_5/lib/rdoc/markup/simple_markup/preprocess.rb73
-rw-r--r--ruby_1_8_5/lib/rdoc/markup/simple_markup/to_flow.rb188
-rw-r--r--ruby_1_8_5/lib/rdoc/markup/simple_markup/to_html.rb289
-rw-r--r--ruby_1_8_5/lib/rdoc/markup/simple_markup/to_latex.rb333
-rw-r--r--ruby_1_8_5/lib/rdoc/markup/test/AllTests.rb2
-rw-r--r--ruby_1_8_5/lib/rdoc/markup/test/TestInline.rb154
-rw-r--r--ruby_1_8_5/lib/rdoc/markup/test/TestParse.rb503
-rw-r--r--ruby_1_8_5/lib/rdoc/options.rb575
-rw-r--r--ruby_1_8_5/lib/rdoc/parsers/parse_c.rb697
-rw-r--r--ruby_1_8_5/lib/rdoc/parsers/parse_f95.rb1841
-rw-r--r--ruby_1_8_5/lib/rdoc/parsers/parse_rb.rb2605
-rw-r--r--ruby_1_8_5/lib/rdoc/parsers/parse_simple.rb41
-rw-r--r--ruby_1_8_5/lib/rdoc/parsers/parserfactory.rb99
-rw-r--r--ruby_1_8_5/lib/rdoc/rdoc.rb278
-rw-r--r--ruby_1_8_5/lib/rdoc/ri/ri_cache.rb187
-rw-r--r--ruby_1_8_5/lib/rdoc/ri/ri_descriptions.rb154
-rw-r--r--ruby_1_8_5/lib/rdoc/ri/ri_display.rb255
-rw-r--r--ruby_1_8_5/lib/rdoc/ri/ri_driver.rb143
-rw-r--r--ruby_1_8_5/lib/rdoc/ri/ri_formatter.rb674
-rw-r--r--ruby_1_8_5/lib/rdoc/ri/ri_options.rb313
-rw-r--r--ruby_1_8_5/lib/rdoc/ri/ri_paths.rb80
-rw-r--r--ruby_1_8_5/lib/rdoc/ri/ri_reader.rb100
-rw-r--r--ruby_1_8_5/lib/rdoc/ri/ri_util.rb75
-rw-r--r--ruby_1_8_5/lib/rdoc/ri/ri_writer.rb62
-rw-r--r--ruby_1_8_5/lib/rdoc/template.rb234
-rw-r--r--ruby_1_8_5/lib/rdoc/tokenstream.rb25
-rw-r--r--ruby_1_8_5/lib/rdoc/usage.rb210
-rw-r--r--ruby_1_8_5/lib/readbytes.rb41
-rw-r--r--ruby_1_8_5/lib/resolv-replace.rb62
-rw-r--r--ruby_1_8_5/lib/resolv.rb1884
-rw-r--r--ruby_1_8_5/lib/rexml/attlistdecl.rb62
-rw-r--r--ruby_1_8_5/lib/rexml/attribute.rb163
-rw-r--r--ruby_1_8_5/lib/rexml/cdata.rb72
-rw-r--r--ruby_1_8_5/lib/rexml/child.rb96
-rw-r--r--ruby_1_8_5/lib/rexml/comment.rb88
-rw-r--r--ruby_1_8_5/lib/rexml/doctype.rb278
-rw-r--r--ruby_1_8_5/lib/rexml/document.rb193
-rw-r--r--ruby_1_8_5/lib/rexml/dtd/attlistdecl.rb10
-rw-r--r--ruby_1_8_5/lib/rexml/dtd/dtd.rb51
-rw-r--r--ruby_1_8_5/lib/rexml/dtd/elementdecl.rb17
-rw-r--r--ruby_1_8_5/lib/rexml/dtd/entitydecl.rb56
-rw-r--r--ruby_1_8_5/lib/rexml/dtd/notationdecl.rb39
-rw-r--r--ruby_1_8_5/lib/rexml/element.rb1243
-rw-r--r--ruby_1_8_5/lib/rexml/encoding.rb64
-rw-r--r--ruby_1_8_5/lib/rexml/encodings/CP-1252.rb98
-rw-r--r--ruby_1_8_5/lib/rexml/encodings/EUC-JP.rb35
-rw-r--r--ruby_1_8_5/lib/rexml/encodings/ICONV.rb22
-rw-r--r--ruby_1_8_5/lib/rexml/encodings/ISO-8859-1.rb7
-rw-r--r--ruby_1_8_5/lib/rexml/encodings/ISO-8859-15.rb69
-rw-r--r--ruby_1_8_5/lib/rexml/encodings/SHIFT-JIS.rb37
-rw-r--r--ruby_1_8_5/lib/rexml/encodings/SHIFT_JIS.rb1
-rw-r--r--ruby_1_8_5/lib/rexml/encodings/UNILE.rb34
-rw-r--r--ruby_1_8_5/lib/rexml/encodings/US-ASCII.rb30
-rw-r--r--ruby_1_8_5/lib/rexml/encodings/UTF-16.rb34
-rw-r--r--ruby_1_8_5/lib/rexml/encodings/UTF-8.rb18
-rw-r--r--ruby_1_8_5/lib/rexml/entity.rb159
-rw-r--r--ruby_1_8_5/lib/rexml/functions.rb367
-rw-r--r--ruby_1_8_5/lib/rexml/instruction.rb66
-rw-r--r--ruby_1_8_5/lib/rexml/light/node.rb196
-rw-r--r--ruby_1_8_5/lib/rexml/namespace.rb47
-rw-r--r--ruby_1_8_5/lib/rexml/node.rb66
-rw-r--r--ruby_1_8_5/lib/rexml/output.rb24
-rw-r--r--ruby_1_8_5/lib/rexml/parent.rb166
-rw-r--r--ruby_1_8_5/lib/rexml/parseexception.rb51
-rw-r--r--ruby_1_8_5/lib/rexml/parsers/baseparser.rb456
-rw-r--r--ruby_1_8_5/lib/rexml/parsers/lightparser.rb60
-rw-r--r--ruby_1_8_5/lib/rexml/parsers/pullparser.rb196
-rw-r--r--ruby_1_8_5/lib/rexml/parsers/sax2parser.rb232
-rw-r--r--ruby_1_8_5/lib/rexml/parsers/streamparser.rb46
-rw-r--r--ruby_1_8_5/lib/rexml/parsers/treeparser.rb94
-rw-r--r--ruby_1_8_5/lib/rexml/parsers/ultralightparser.rb56
-rw-r--r--ruby_1_8_5/lib/rexml/parsers/xpathparser.rb692
-rw-r--r--ruby_1_8_5/lib/rexml/quickpath.rb266
-rw-r--r--ruby_1_8_5/lib/rexml/rexml.rb29
-rw-r--r--ruby_1_8_5/lib/rexml/sax2listener.rb97
-rw-r--r--ruby_1_8_5/lib/rexml/source.rb233
-rw-r--r--ruby_1_8_5/lib/rexml/streamlistener.rb92
-rw-r--r--ruby_1_8_5/lib/rexml/syncenumerator.rb33
-rw-r--r--ruby_1_8_5/lib/rexml/text.rb342
-rw-r--r--ruby_1_8_5/lib/rexml/validation/relaxng.rb559
-rw-r--r--ruby_1_8_5/lib/rexml/validation/validation.rb155
-rw-r--r--ruby_1_8_5/lib/rexml/validation/validationexception.rb9
-rw-r--r--ruby_1_8_5/lib/rexml/xmldecl.rb114
-rw-r--r--ruby_1_8_5/lib/rexml/xmltokens.rb18
-rw-r--r--ruby_1_8_5/lib/rexml/xpath.rb66
-rw-r--r--ruby_1_8_5/lib/rexml/xpath_parser.rb741
-rw-r--r--ruby_1_8_5/lib/rinda/rinda.rb283
-rw-r--r--ruby_1_8_5/lib/rinda/ring.rb271
-rw-r--r--ruby_1_8_5/lib/rinda/tuplespace.rb589
-rw-r--r--ruby_1_8_5/lib/rss.rb16
-rw-r--r--ruby_1_8_5/lib/rss/0.9.rb423
-rw-r--r--ruby_1_8_5/lib/rss/1.0.rb451
-rw-r--r--ruby_1_8_5/lib/rss/2.0.rb111
-rw-r--r--ruby_1_8_5/lib/rss/content.rb38
-rw-r--r--ruby_1_8_5/lib/rss/converter.rb158
-rw-r--r--ruby_1_8_5/lib/rss/dublincore.rb154
-rw-r--r--ruby_1_8_5/lib/rss/image.rb193
-rw-r--r--ruby_1_8_5/lib/rss/maker.rb37
-rw-r--r--ruby_1_8_5/lib/rss/maker/0.9.rb224
-rw-r--r--ruby_1_8_5/lib/rss/maker/1.0.rb204
-rw-r--r--ruby_1_8_5/lib/rss/maker/2.0.rb168
-rw-r--r--ruby_1_8_5/lib/rss/maker/base.rb546
-rw-r--r--ruby_1_8_5/lib/rss/maker/content.rb29
-rw-r--r--ruby_1_8_5/lib/rss/maker/dublincore.rb169
-rw-r--r--ruby_1_8_5/lib/rss/maker/image.rb145
-rw-r--r--ruby_1_8_5/lib/rss/maker/syndication.rb27
-rw-r--r--ruby_1_8_5/lib/rss/maker/taxonomy.rb182
-rw-r--r--ruby_1_8_5/lib/rss/maker/trackback.rb130
-rw-r--r--ruby_1_8_5/lib/rss/parser.rb476
-rw-r--r--ruby_1_8_5/lib/rss/rexmlparser.rb54
-rw-r--r--ruby_1_8_5/lib/rss/rss.rb1015
-rw-r--r--ruby_1_8_5/lib/rss/syndication.rb64
-rw-r--r--ruby_1_8_5/lib/rss/taxonomy.rb145
-rw-r--r--ruby_1_8_5/lib/rss/trackback.rb288
-rw-r--r--ruby_1_8_5/lib/rss/utils.rb37
-rw-r--r--ruby_1_8_5/lib/rss/xml-stylesheet.rb101
-rw-r--r--ruby_1_8_5/lib/rss/xmlparser.rb93
-rw-r--r--ruby_1_8_5/lib/rss/xmlscanner.rb121
-rw-r--r--ruby_1_8_5/lib/rubyunit.rb6
-rw-r--r--ruby_1_8_5/lib/runit/assert.rb73
-rw-r--r--ruby_1_8_5/lib/runit/cui/testrunner.rb51
-rw-r--r--ruby_1_8_5/lib/runit/error.rb9
-rw-r--r--ruby_1_8_5/lib/runit/testcase.rb45
-rw-r--r--ruby_1_8_5/lib/runit/testresult.rb44
-rw-r--r--ruby_1_8_5/lib/runit/testsuite.rb26
-rw-r--r--ruby_1_8_5/lib/runit/topublic.rb8
-rw-r--r--ruby_1_8_5/lib/scanf.rb702
-rw-r--r--ruby_1_8_5/lib/set.rb1220
-rw-r--r--ruby_1_8_5/lib/shell.rb269
-rw-r--r--ruby_1_8_5/lib/shell/builtin-command.rb154
-rw-r--r--ruby_1_8_5/lib/shell/command-processor.rb592
-rw-r--r--ruby_1_8_5/lib/shell/error.rb26
-rw-r--r--ruby_1_8_5/lib/shell/filter.rb110
-rw-r--r--ruby_1_8_5/lib/shell/process-controller.rb260
-rw-r--r--ruby_1_8_5/lib/shell/system-command.rb168
-rw-r--r--ruby_1_8_5/lib/shell/version.rb16
-rw-r--r--ruby_1_8_5/lib/shellwords.rb60
-rw-r--r--ruby_1_8_5/lib/singleton.rb360
-rw-r--r--ruby_1_8_5/lib/soap/attachment.rb107
-rw-r--r--ruby_1_8_5/lib/soap/baseData.rb942
-rw-r--r--ruby_1_8_5/lib/soap/element.rb258
-rw-r--r--ruby_1_8_5/lib/soap/encodingstyle/aspDotNetHandler.rb213
-rw-r--r--ruby_1_8_5/lib/soap/encodingstyle/handler.rb100
-rw-r--r--ruby_1_8_5/lib/soap/encodingstyle/literalHandler.rb226
-rw-r--r--ruby_1_8_5/lib/soap/encodingstyle/soapHandler.rb582
-rw-r--r--ruby_1_8_5/lib/soap/generator.rb268
-rw-r--r--ruby_1_8_5/lib/soap/header/handler.rb57
-rw-r--r--ruby_1_8_5/lib/soap/header/handlerset.rb70
-rw-r--r--ruby_1_8_5/lib/soap/header/simplehandler.rb44
-rw-r--r--ruby_1_8_5/lib/soap/httpconfigloader.rb119
-rw-r--r--ruby_1_8_5/lib/soap/mapping.rb10
-rw-r--r--ruby_1_8_5/lib/soap/mapping/factory.rb355
-rw-r--r--ruby_1_8_5/lib/soap/mapping/mapping.rb381
-rw-r--r--ruby_1_8_5/lib/soap/mapping/registry.rb541
-rw-r--r--ruby_1_8_5/lib/soap/mapping/rubytypeFactory.rb475
-rw-r--r--ruby_1_8_5/lib/soap/mapping/typeMap.rb50
-rw-r--r--ruby_1_8_5/lib/soap/mapping/wsdlencodedregistry.rb280
-rw-r--r--ruby_1_8_5/lib/soap/mapping/wsdlliteralregistry.rb418
-rw-r--r--ruby_1_8_5/lib/soap/marshal.rb59
-rw-r--r--ruby_1_8_5/lib/soap/mimemessage.rb240
-rw-r--r--ruby_1_8_5/lib/soap/netHttpClient.rb190
-rw-r--r--ruby_1_8_5/lib/soap/parser.rb251
-rw-r--r--ruby_1_8_5/lib/soap/processor.rb66
-rw-r--r--ruby_1_8_5/lib/soap/property.rb333
-rw-r--r--ruby_1_8_5/lib/soap/rpc/cgistub.rb206
-rw-r--r--ruby_1_8_5/lib/soap/rpc/driver.rb254
-rw-r--r--ruby_1_8_5/lib/soap/rpc/element.rb325
-rw-r--r--ruby_1_8_5/lib/soap/rpc/httpserver.rb129
-rw-r--r--ruby_1_8_5/lib/soap/rpc/proxy.rb497
-rw-r--r--ruby_1_8_5/lib/soap/rpc/router.rb594
-rw-r--r--ruby_1_8_5/lib/soap/rpc/rpc.rb25
-rw-r--r--ruby_1_8_5/lib/soap/rpc/soaplet.rb162
-rw-r--r--ruby_1_8_5/lib/soap/rpc/standaloneServer.rb43
-rw-r--r--ruby_1_8_5/lib/soap/soap.rb140
-rw-r--r--ruby_1_8_5/lib/soap/streamHandler.rb229
-rw-r--r--ruby_1_8_5/lib/soap/wsdlDriver.rb575
-rw-r--r--ruby_1_8_5/lib/sync.rb311
-rw-r--r--ruby_1_8_5/lib/tempfile.rb193
-rw-r--r--ruby_1_8_5/lib/test/unit.rb280
-rw-r--r--ruby_1_8_5/lib/test/unit/assertionfailederror.rb14
-rw-r--r--ruby_1_8_5/lib/test/unit/assertions.rb622
-rw-r--r--ruby_1_8_5/lib/test/unit/autorunner.rb204
-rw-r--r--ruby_1_8_5/lib/test/unit/collector.rb43
-rw-r--r--ruby_1_8_5/lib/test/unit/collector/dir.rb92
-rw-r--r--ruby_1_8_5/lib/test/unit/collector/objectspace.rb34
-rw-r--r--ruby_1_8_5/lib/test/unit/error.rb56
-rw-r--r--ruby_1_8_5/lib/test/unit/failure.rb51
-rw-r--r--ruby_1_8_5/lib/test/unit/testcase.rb152
-rw-r--r--ruby_1_8_5/lib/test/unit/testresult.rb80
-rw-r--r--ruby_1_8_5/lib/test/unit/testsuite.rb76
-rw-r--r--ruby_1_8_5/lib/test/unit/ui/console/testrunner.rb127
-rw-r--r--ruby_1_8_5/lib/test/unit/ui/fox/testrunner.rb268
-rw-r--r--ruby_1_8_5/lib/test/unit/ui/gtk/testrunner.rb416
-rw-r--r--ruby_1_8_5/lib/test/unit/ui/gtk2/testrunner.rb465
-rw-r--r--ruby_1_8_5/lib/test/unit/ui/testrunnermediator.rb68
-rw-r--r--ruby_1_8_5/lib/test/unit/ui/testrunnerutilities.rb46
-rw-r--r--ruby_1_8_5/lib/test/unit/ui/tk/testrunner.rb260
-rw-r--r--ruby_1_8_5/lib/test/unit/util/backtracefilter.rb40
-rw-r--r--ruby_1_8_5/lib/test/unit/util/observable.rb90
-rw-r--r--ruby_1_8_5/lib/test/unit/util/procwrapper.rb48
-rw-r--r--ruby_1_8_5/lib/thread.rb484
-rw-r--r--ruby_1_8_5/lib/thwait.rb169
-rw-r--r--ruby_1_8_5/lib/time.rb797
-rw-r--r--ruby_1_8_5/lib/timeout.rb105
-rw-r--r--ruby_1_8_5/lib/tmpdir.rb45
-rw-r--r--ruby_1_8_5/lib/tracer.rb167
-rw-r--r--ruby_1_8_5/lib/tsort.rb290
-rw-r--r--ruby_1_8_5/lib/un.rb235
-rw-r--r--ruby_1_8_5/lib/uri.rb28
-rw-r--r--ruby_1_8_5/lib/uri/common.rb607
-rw-r--r--ruby_1_8_5/lib/uri/ftp.rb148
-rw-r--r--ruby_1_8_5/lib/uri/generic.rb1118
-rw-r--r--ruby_1_8_5/lib/uri/http.rb65
-rw-r--r--ruby_1_8_5/lib/uri/https.rb16
-rw-r--r--ruby_1_8_5/lib/uri/ldap.rb190
-rw-r--r--ruby_1_8_5/lib/uri/mailto.rb239
-rw-r--r--ruby_1_8_5/lib/weakref.rb94
-rw-r--r--ruby_1_8_5/lib/webrick.rb29
-rw-r--r--ruby_1_8_5/lib/webrick/accesslog.rb67
-rw-r--r--ruby_1_8_5/lib/webrick/cgi.rb257
-rw-r--r--ruby_1_8_5/lib/webrick/compat.rb15
-rw-r--r--ruby_1_8_5/lib/webrick/config.rb97
-rw-r--r--ruby_1_8_5/lib/webrick/cookie.rb104
-rw-r--r--ruby_1_8_5/lib/webrick/htmlutils.rb25
-rw-r--r--ruby_1_8_5/lib/webrick/httpauth.rb45
-rw-r--r--ruby_1_8_5/lib/webrick/httpauth/authenticator.rb79
-rw-r--r--ruby_1_8_5/lib/webrick/httpauth/basicauth.rb65
-rw-r--r--ruby_1_8_5/lib/webrick/httpauth/digestauth.rb343
-rw-r--r--ruby_1_8_5/lib/webrick/httpauth/htdigest.rb91
-rw-r--r--ruby_1_8_5/lib/webrick/httpauth/htgroup.rb61
-rw-r--r--ruby_1_8_5/lib/webrick/httpauth/htpasswd.rb83
-rw-r--r--ruby_1_8_5/lib/webrick/httpauth/userdb.rb29
-rw-r--r--ruby_1_8_5/lib/webrick/httpproxy.rb254
-rw-r--r--ruby_1_8_5/lib/webrick/httprequest.rb365
-rw-r--r--ruby_1_8_5/lib/webrick/httpresponse.rb327
-rw-r--r--ruby_1_8_5/lib/webrick/https.rb63
-rw-r--r--ruby_1_8_5/lib/webrick/httpserver.rb210
-rw-r--r--ruby_1_8_5/lib/webrick/httpservlet.rb22
-rw-r--r--ruby_1_8_5/lib/webrick/httpservlet/abstract.rb71
-rw-r--r--ruby_1_8_5/lib/webrick/httpservlet/cgi_runner.rb45
-rw-r--r--ruby_1_8_5/lib/webrick/httpservlet/cgihandler.rb104
-rw-r--r--ruby_1_8_5/lib/webrick/httpservlet/erbhandler.rb54
-rw-r--r--ruby_1_8_5/lib/webrick/httpservlet/filehandler.rb398
-rw-r--r--ruby_1_8_5/lib/webrick/httpservlet/prochandler.rb33
-rw-r--r--ruby_1_8_5/lib/webrick/httpstatus.rb126
-rw-r--r--ruby_1_8_5/lib/webrick/httputils.rb399
-rw-r--r--ruby_1_8_5/lib/webrick/httpversion.rb49
-rw-r--r--ruby_1_8_5/lib/webrick/log.rb88
-rw-r--r--ruby_1_8_5/lib/webrick/server.rb200
-rw-r--r--ruby_1_8_5/lib/webrick/ssl.rb126
-rw-r--r--ruby_1_8_5/lib/webrick/utils.rb100
-rw-r--r--ruby_1_8_5/lib/webrick/version.rb13
-rw-r--r--ruby_1_8_5/lib/wsdl/binding.rb65
-rw-r--r--ruby_1_8_5/lib/wsdl/data.rb64
-rw-r--r--ruby_1_8_5/lib/wsdl/definitions.rb250
-rw-r--r--ruby_1_8_5/lib/wsdl/documentation.rb32
-rw-r--r--ruby_1_8_5/lib/wsdl/import.rb80
-rw-r--r--ruby_1_8_5/lib/wsdl/importer.rb38
-rw-r--r--ruby_1_8_5/lib/wsdl/info.rb39
-rw-r--r--ruby_1_8_5/lib/wsdl/message.rb54
-rw-r--r--ruby_1_8_5/lib/wsdl/operation.rb130
-rw-r--r--ruby_1_8_5/lib/wsdl/operationBinding.rb108
-rw-r--r--ruby_1_8_5/lib/wsdl/param.rb85
-rw-r--r--ruby_1_8_5/lib/wsdl/parser.rb163
-rw-r--r--ruby_1_8_5/lib/wsdl/part.rb52
-rw-r--r--ruby_1_8_5/lib/wsdl/port.rb84
-rw-r--r--ruby_1_8_5/lib/wsdl/portType.rb73
-rw-r--r--ruby_1_8_5/lib/wsdl/service.rb61
-rw-r--r--ruby_1_8_5/lib/wsdl/soap/address.rb40
-rw-r--r--ruby_1_8_5/lib/wsdl/soap/binding.rb49
-rw-r--r--ruby_1_8_5/lib/wsdl/soap/body.rb56
-rw-r--r--ruby_1_8_5/lib/wsdl/soap/cgiStubCreator.rb76
-rw-r--r--ruby_1_8_5/lib/wsdl/soap/classDefCreator.rb314
-rw-r--r--ruby_1_8_5/lib/wsdl/soap/classDefCreatorSupport.rb126
-rw-r--r--ruby_1_8_5/lib/wsdl/soap/clientSkeltonCreator.rb78
-rw-r--r--ruby_1_8_5/lib/wsdl/soap/complexType.rb161
-rw-r--r--ruby_1_8_5/lib/wsdl/soap/data.rb42
-rw-r--r--ruby_1_8_5/lib/wsdl/soap/definitions.rb149
-rw-r--r--ruby_1_8_5/lib/wsdl/soap/driverCreator.rb95
-rw-r--r--ruby_1_8_5/lib/wsdl/soap/element.rb28
-rw-r--r--ruby_1_8_5/lib/wsdl/soap/fault.rb56
-rw-r--r--ruby_1_8_5/lib/wsdl/soap/header.rb86
-rw-r--r--ruby_1_8_5/lib/wsdl/soap/headerfault.rb56
-rw-r--r--ruby_1_8_5/lib/wsdl/soap/mappingRegistryCreator.rb92
-rw-r--r--ruby_1_8_5/lib/wsdl/soap/methodDefCreator.rb228
-rw-r--r--ruby_1_8_5/lib/wsdl/soap/operation.rb122
-rw-r--r--ruby_1_8_5/lib/wsdl/soap/servantSkeltonCreator.rb67
-rw-r--r--ruby_1_8_5/lib/wsdl/soap/standaloneServerStubCreator.rb85
-rw-r--r--ruby_1_8_5/lib/wsdl/soap/wsdl2ruby.rb176
-rw-r--r--ruby_1_8_5/lib/wsdl/types.rb43
-rw-r--r--ruby_1_8_5/lib/wsdl/wsdl.rb23
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/all.rb69
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/annotation.rb34
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/any.rb56
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/attribute.rb127
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/choice.rb69
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/complexContent.rb92
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/complexType.rb139
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/content.rb96
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/data.rb80
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/element.rb154
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/enumeration.rb36
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/import.rb65
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/importer.rb87
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/include.rb54
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/length.rb35
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/parser.rb166
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/pattern.rb36
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/schema.rb143
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/sequence.rb69
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/simpleContent.rb65
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/simpleExtension.rb54
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/simpleRestriction.rb73
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/simpleType.rb73
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/unique.rb34
-rw-r--r--ruby_1_8_5/lib/wsdl/xmlSchema/xsd2ruby.rb107
-rw-r--r--ruby_1_8_5/lib/xmlrpc/.document1
-rw-r--r--ruby_1_8_5/lib/xmlrpc/README.txt31
-rw-r--r--ruby_1_8_5/lib/xmlrpc/base64.rb81
-rw-r--r--ruby_1_8_5/lib/xmlrpc/client.rb616
-rw-r--r--ruby_1_8_5/lib/xmlrpc/config.rb40
-rw-r--r--ruby_1_8_5/lib/xmlrpc/create.rb290
-rw-r--r--ruby_1_8_5/lib/xmlrpc/datetime.rb142
-rw-r--r--ruby_1_8_5/lib/xmlrpc/httpserver.rb178
-rw-r--r--ruby_1_8_5/lib/xmlrpc/marshal.rb76
-rw-r--r--ruby_1_8_5/lib/xmlrpc/parser.rb813
-rw-r--r--ruby_1_8_5/lib/xmlrpc/server.rb780
-rw-r--r--ruby_1_8_5/lib/xmlrpc/utils.rb165
-rw-r--r--ruby_1_8_5/lib/xsd/charset.rb187
-rw-r--r--ruby_1_8_5/lib/xsd/codegen.rb12
-rw-r--r--ruby_1_8_5/lib/xsd/codegen/classdef.rb203
-rw-r--r--ruby_1_8_5/lib/xsd/codegen/commentdef.rb34
-rw-r--r--ruby_1_8_5/lib/xsd/codegen/gensupport.rb166
-rw-r--r--ruby_1_8_5/lib/xsd/codegen/methoddef.rb63
-rw-r--r--ruby_1_8_5/lib/xsd/codegen/moduledef.rb191
-rw-r--r--ruby_1_8_5/lib/xsd/datatypes.rb1269
-rw-r--r--ruby_1_8_5/lib/xsd/datatypes1999.rb20
-rw-r--r--ruby_1_8_5/lib/xsd/iconvcharset.rb33
-rw-r--r--ruby_1_8_5/lib/xsd/mapping.rb42
-rw-r--r--ruby_1_8_5/lib/xsd/namedelements.rb95
-rw-r--r--ruby_1_8_5/lib/xsd/ns.rb140
-rw-r--r--ruby_1_8_5/lib/xsd/qname.rb78
-rw-r--r--ruby_1_8_5/lib/xsd/xmlparser.rb61
-rw-r--r--ruby_1_8_5/lib/xsd/xmlparser/parser.rb96
-rw-r--r--ruby_1_8_5/lib/xsd/xmlparser/rexmlparser.rb54
-rw-r--r--ruby_1_8_5/lib/xsd/xmlparser/xmlparser.rb50
-rw-r--r--ruby_1_8_5/lib/xsd/xmlparser/xmlscanner.rb147
-rw-r--r--ruby_1_8_5/lib/yaml.rb436
-rw-r--r--ruby_1_8_5/lib/yaml/baseemitter.rb247
-rw-r--r--ruby_1_8_5/lib/yaml/basenode.rb216
-rw-r--r--ruby_1_8_5/lib/yaml/constants.rb45
-rw-r--r--ruby_1_8_5/lib/yaml/dbm.rb111
-rw-r--r--ruby_1_8_5/lib/yaml/encoding.rb33
-rw-r--r--ruby_1_8_5/lib/yaml/error.rb34
-rw-r--r--ruby_1_8_5/lib/yaml/loader.rb14
-rw-r--r--ruby_1_8_5/lib/yaml/rubytypes.rb408
-rw-r--r--ruby_1_8_5/lib/yaml/store.rb29
-rw-r--r--ruby_1_8_5/lib/yaml/stream.rb40
-rw-r--r--ruby_1_8_5/lib/yaml/stringio.rb83
-rw-r--r--ruby_1_8_5/lib/yaml/syck.rb19
-rw-r--r--ruby_1_8_5/lib/yaml/tag.rb91
-rw-r--r--ruby_1_8_5/lib/yaml/types.rb194
-rw-r--r--ruby_1_8_5/lib/yaml/yamlnode.rb54
-rw-r--r--ruby_1_8_5/lib/yaml/ypath.rb52
507 files changed, 120488 insertions, 0 deletions
diff --git a/ruby_1_8_5/lib/.document b/ruby_1_8_5/lib/.document
new file mode 100644
index 0000000000..542ca3764c
--- /dev/null
+++ b/ruby_1_8_5/lib/.document
@@ -0,0 +1,103 @@
+# We only run RDoc on the top-level files in here: we skip
+# all the helper stuff in sub-directories
+
+# Eventually, we hope to see...
+# *.rb
+
+# But for now
+
+English.rb
+Env.rb
+abbrev.rb
+base64.rb
+benchmark.rb
+cgi
+cgi.rb
+cgi-lib.rb
+complex.rb
+csv.rb
+date
+date.rb
+date2.rb
+debug.rb
+delegate.rb
+drb
+drb.rb
+erb.rb
+eregex.rb
+fileutils.rb
+finalize.rb
+find.rb
+forwardable.rb
+ftools.rb
+generator.rb
+getoptlong.rb
+getopts.rb
+gserver.rb
+importenv.rb
+ipaddr.rb
+irb.rb
+jcode.rb
+logger.rb
+mailread.rb
+mathn.rb
+matrix.rb
+mkmf.rb
+monitor.rb
+mutex_m.rb
+net
+observer.rb
+open-uri.rb
+open3.rb
+optparse
+optparse.rb
+ostruct.rb
+parsearg.rb
+parsedate.rb
+pathname.rb
+ping.rb
+pp.rb
+prettyprint.rb
+profile.rb
+profiler.rb
+pstore.rb
+racc
+rational.rb
+rdoc
+readbytes.rb
+resolv-replace.rb
+resolv.rb
+rexml
+rinda
+rss
+rss.rb
+rubyunit.rb
+runit
+scanf.rb
+set.rb
+shell
+shell.rb
+shellwords.rb
+singleton.rb
+soap
+sync.rb
+tempfile.rb
+test
+thread.rb
+thwait.rb
+time.rb
+timeout.rb
+tmpdir.rb
+tracer.rb
+tsort.rb
+un.rb
+uri
+uri.rb
+weakref.rb
+webrick
+webrick.rb
+wsdl
+xmlrpc
+xsd
+yaml
+yaml.rb
diff --git a/ruby_1_8_5/lib/English.rb b/ruby_1_8_5/lib/English.rb
new file mode 100644
index 0000000000..1a0e11de74
--- /dev/null
+++ b/ruby_1_8_5/lib/English.rb
@@ -0,0 +1,155 @@
+# Include the English library file in a Ruby script, and you can
+# reference the global variables such as \VAR{\$\_} using less
+# cryptic names, listed in the following table.% \vref{tab:english}.
+#
+# Without 'English':
+#
+# $\ = ' -- '
+# "waterbuffalo" =~ /buff/
+# print $", $', $$, "\n"
+#
+# With English:
+#
+# require "English"
+#
+# $OUTPUT_FIELD_SEPARATOR = ' -- '
+# "waterbuffalo" =~ /buff/
+# print $LOADED_FEATURES, $POSTMATCH, $PID, "\n"
+
+
+# The exception object passed to +raise+.
+alias $ERROR_INFO $!
+
+# The stack backtrace generated by the last
+# exception. <tt>See Kernel.caller</tt> for details. Thread local.
+alias $ERROR_POSITION $@
+
+# The default separator pattern used by <tt>String.split</tt>. May be
+# set from the command line using the <tt>-F</tt> flag.
+alias $FS $;
+
+# The default separator pattern used by <tt>String.split</tt>. May be
+# set from the command line using the <tt>-F</tt> flag.
+alias $FIELD_SEPARATOR $;
+
+# The separator string output between the parameters to methods such
+# as <tt>Kernel.print</tt> and <tt>Array.join</tt>. Defaults to +nil+,
+# which adds no text.
+alias $OFS $,
+
+# The separator string output between the parameters to methods such
+# as <tt>Kernel.print</tt> and <tt>Array.join</tt>. Defaults to +nil+,
+# which adds no text.
+alias $OUTPUT_FIELD_SEPARATOR $,
+
+# The input record separator (newline by default). This is the value
+# that routines such as <tt>Kernel.gets</tt> use to determine record
+# boundaries. If set to +nil+, +gets+ will read the entire file.
+alias $RS $/
+
+# The input record separator (newline by default). This is the value
+# that routines such as <tt>Kernel.gets</tt> use to determine record
+# boundaries. If set to +nil+, +gets+ will read the entire file.
+alias $INPUT_RECORD_SEPARATOR $/
+
+# The string appended to the output of every call to methods such as
+# <tt>Kernel.print</tt> and <tt>IO.write</tt>. The default value is
+# +nil+.
+alias $ORS $\
+
+# The string appended to the output of every call to methods such as
+# <tt>Kernel.print</tt> and <tt>IO.write</tt>. The default value is
+# +nil+.
+alias $OUTPUT_RECORD_SEPARATOR $\
+
+# The number of the last line read from the current input file.
+alias $INPUT_LINE_NUMBER $.
+
+# The number of the last line read from the current input file.
+alias $NR $.
+
+# The last line read by <tt>Kernel.gets</tt> or
+# <tt>Kernel.readline</tt>. Many string-related functions in the
+# +Kernel+ module operate on <tt>$_</tt> by default. The variable is
+# local to the current scope. Thread local.
+alias $LAST_READ_LINE $_
+
+# The destination of output for <tt>Kernel.print</tt>
+# and <tt>Kernel.printf</tt>. The default value is
+# <tt>$stdout</tt>.
+alias $DEFAULT_OUTPUT $>
+
+# An object that provides access to the concatenation
+# of the contents of all the files
+# given as command-line arguments, or <tt>$stdin</tt>
+# (in the case where there are no
+# arguments). <tt>$<</tt> supports methods similar to a
+# +File+ object:
+# +inmode+, +close+,
+# <tt>closed?</tt>, +each+,
+# <tt>each_byte</tt>, <tt>each_line</tt>,
+# +eof+, <tt>eof?</tt>, +file+,
+# +filename+, +fileno+,
+# +getc+, +gets+, +lineno+,
+# <tt>lineno=</tt>, +path+,
+# +pos+, <tt>pos=</tt>,
+# +read+, +readchar+,
+# +readline+, +readlines+,
+# +rewind+, +seek+, +skip+,
+# +tell+, <tt>to_a</tt>, <tt>to_i</tt>,
+# <tt>to_io</tt>, <tt>to_s</tt>, along with the
+# methods in +Enumerable+. The method +file+
+# returns a +File+ object for the file currently
+# being read. This may change as <tt>$<</tt> reads
+# through the files on the command line. Read only.
+alias $DEFAULT_INPUT $<
+
+# The process number of the program being executed. Read only.
+alias $PID $$
+
+# The process number of the program being executed. Read only.
+alias $PROCESS_ID $$
+
+# The exit status of the last child process to terminate. Read
+# only. Thread local.
+alias $CHILD_STATUS $?
+
+# A +MatchData+ object that encapsulates the results of a successful
+# pattern match. The variables <tt>$&</tt>, <tt>$`</tt>, <tt>$'</tt>,
+# and <tt>$1</tt> to <tt>$9</tt> are all derived from
+# <tt>$~</tt>. Assigning to <tt>$~</tt> changes the values of these
+# derived variables. This variable is local to the current
+# scope. Thread local.
+alias $LAST_MATCH_INFO $~
+
+# If set to any value apart from +nil+ or +false+, all pattern matches
+# will be case insensitive, string comparisons will ignore case, and
+# string hash values will be case insensitive. Deprecated
+alias $IGNORECASE $=
+
+# An array of strings containing the command-line
+# options from the invocation of the program. Options
+# used by the Ruby interpreter will have been
+# removed. Read only. Also known simply as +ARGV+.
+alias $ARGV $*
+
+# The string matched by the last successful pattern
+# match. This variable is local to the current
+# scope. Read only. Thread local.
+alias $MATCH $&
+
+# The string preceding the match in the last
+# successful pattern match. This variable is local to
+# the current scope. Read only. Thread local.
+alias $PREMATCH $`
+
+# The string following the match in the last
+# successful pattern match. This variable is local to
+# the current scope. Read only. Thread local.
+alias $POSTMATCH $'
+
+# The contents of the highest-numbered group matched in the last
+# successful pattern match. Thus, in <tt>"cat" =~ /(c|a)(t|z)/</tt>,
+# <tt>$+</tt> will be set to "t". This variable is local to the
+# current scope. Read only. Thread local.
+alias $LAST_PAREN_MATCH $+
diff --git a/ruby_1_8_5/lib/Env.rb b/ruby_1_8_5/lib/Env.rb
new file mode 100644
index 0000000000..452a28659e
--- /dev/null
+++ b/ruby_1_8_5/lib/Env.rb
@@ -0,0 +1,18 @@
+# Env.rb -- imports environment variables as global variables, Perlish ;(
+# Usage:
+#
+# require 'Env'
+# p $USER
+# $USER = "matz"
+# p ENV["USER"]
+
+require 'importenv'
+
+if __FILE__ == $0
+ p $TERM
+ $TERM = nil
+ p $TERM
+ p ENV["TERM"]
+ $TERM = "foo"
+ p ENV["TERM"]
+end
diff --git a/ruby_1_8_5/lib/README b/ruby_1_8_5/lib/README
new file mode 100644
index 0000000000..19d6309166
--- /dev/null
+++ b/ruby_1_8_5/lib/README
@@ -0,0 +1,96 @@
+English.rb lets Perl'ish global variables have English names
+Env.rb loads importenv.rb
+README this file
+base64.rb encodes/decodes base64 (obsolete)
+benchmark.rb a benchmark utility
+cgi-lib.rb simple CGI support library (old style)
+cgi.rb CGI support library
+cgi/session.rb CGI session class
+complex.rb complex number suppor
+csv.rb CSV parser/generator
+date.rb date object
+date/format.rb date parsing and formatting
+date2.rb date object (obsolete; use date)
+debug.rb ruby debugger
+delegate.rb delegates messages to other object
+drb.rb distributed Ruby
+e2mmap.rb exception utilities
+erb.rb tiny eRuby library
+eregex.rb extended regular expression (just a proof of concept)
+fileutils.rb file utilities
+finalize.rb adds finalizer to the object
+find.rb traverses directory tree
+forwardable.rb explicit delegation library
+ftools.rb file tools
+getoptlong.rb GNU getoptlong compatible
+getopts.rb parses command line options (use getoptlong)
+gserver.rb general TCP server
+importenv.rb imports environment variables as global variables
+ipaddr.rb defines the IPAddr class
+irb.rb interactive ruby
+jcode.rb Japanese text handling (replace String methods)
+logger.rb simple logging utility
+mailread.rb reads mail headers
+mathn.rb extended math operation
+matrix.rb matrix calculation library
+mkmf.rb Makefile maker
+monitor.rb exclusive region monitor for thread
+mutex_m.rb mutex mixin
+net/ftp.rb ftp access
+net/http.rb HTTP access
+net/imap.rb IMAP4 access
+net/pop.rb POP3 access
+net/protocol.rb abstract class for net library (DO NOT USE)
+net/smtp.rb SMTP access
+net/telnet.rb telnet library
+observer.rb observer desing pattern library (provides Observable)
+open-uri.rb easy-to-use network interface using URI and Net
+open3.rb opens subprocess connection stdin/stdout/stderr
+optparse.rb command line option analysis
+ostruct.rb python style object
+parsearg.rb argument parser using getopts
+parsedate.rb parses date string
+pathname.rb Object-Oriented Pathname Class
+ping.rb checks whether host is up, using TCP echo.
+pp.rb pretty print objects
+prettyprint.rb pretty printing algorithm
+profile.rb runs ruby profiler
+profiler.rb ruby profiler module
+pstore.rb persistent object strage using marshal
+racc/parser.rb racc (Ruby yACC) runtime
+rational.rb rational number support
+rdoc source-code documentation tool
+readbytes.rb define IO#readbytes
+resolv-replace.rb replace Socket DNS by resolve.rb
+resolv.rb DNS resolver in Ruby
+rexml an XML parser for Ruby, in Ruby
+rubyunit.rb original Ruby Unit testing framework
+scanf.rb scanf for Ruby
+set.rb defines the Set class
+shell.rb runs commands and does pipeline operations like shell
+shellwords.rb split into words like shell
+singleton.rb singleton design pattern library
+soap SOAP 1.1 implementation
+sync.rb 2 phase lock
+tempfile.rb temporary file with automatic removal
+test/unit Ruby Unit Testing Framework
+thread.rb thread support
+thwait.rb thread syncronization class
+time.rb RFC2822, RFC2616, ISO8601 style time formatting/parsing
+timeout.rb provides timeout
+tmpdir.rb retrieve temporary directory path
+tracer.rb execution tracer
+tsort.rb topological sorting
+un.rb Utilities to replace common UNIX commands in Makefiles etc
+uri.rb URI support
+uri/ftp.rb ftp scheme support
+uri/http.rb http scheme support
+uri/https.rb https scheme support
+uri/ldap.rb ldap scheme support
+uri/mailto.rb mailto scheme support
+weakref.rb weak reference class
+webrick.rb WEB server toolkit
+wsdl WSDL 1.1 implementation
+xmlrpc XML-RPC implementation
+xsd XML Schema Datatypes implementation
+yaml.rb YAML implementation
diff --git a/ruby_1_8_5/lib/abbrev.rb b/ruby_1_8_5/lib/abbrev.rb
new file mode 100644
index 0000000000..bf489526cf
--- /dev/null
+++ b/ruby_1_8_5/lib/abbrev.rb
@@ -0,0 +1,103 @@
+#!/usr/bin/env ruby
+=begin
+#
+# Copyright (c) 2001,2003 Akinori MUSHA <knu@iDaemons.org>
+#
+# All rights reserved. You can redistribute and/or modify it under
+# the same terms as Ruby.
+#
+# $Idaemons: /home/cvs/rb/abbrev.rb,v 1.2 2001/05/30 09:37:45 knu Exp $
+# $RoughId: abbrev.rb,v 1.4 2003/10/14 19:45:42 knu Exp $
+# $Id: abbrev.rb,v 1.1.2.1 2004/01/20 05:27:12 dave Exp $
+=end
+
+# Calculate the set of unique abbreviations for a given set of strings.
+#
+# require 'abbrev'
+# require 'pp'
+#
+# pp Abbrev::abbrev(['ruby', 'rules']).sort
+#
+# <i>Generates:</i>
+#
+# [["rub", "ruby"],
+# ["ruby", "ruby"],
+# ["rul", "rules"],
+# ["rule", "rules"],
+# ["rules", "rules"]]
+#
+# Also adds an +abbrev+ method to class +Array+.
+
+module Abbrev
+
+ # Given a set of strings, calculate the set of unambiguous
+ # abbreviations for those strings, and return a hash where the keys
+ # are all the possible abbreviations and the values are the full
+ # strings. Thus, given input of "car" and "cone", the keys pointing
+ # to "car" would be "ca" and "car", while those pointing to "cone"
+ # would be "co", "con", and "cone".
+ #
+ # The optional +pattern+ parameter is a pattern or a string. Only
+ # those input strings matching the pattern, or begging the string,
+ # are considered for inclusion in the output hash
+
+ def abbrev(words, pattern = nil)
+ table = {}
+ seen = Hash.new(0)
+
+ if pattern.is_a?(String)
+ pattern = /^#{Regexp.quote(pattern)}/ # regard as a prefix
+ end
+
+ words.each do |word|
+ next if (abbrev = word).empty?
+ while (len = abbrev.rindex(/[\w\W]\z/)) > 0
+ abbrev = word[0,len]
+
+ next if pattern && pattern !~ abbrev
+
+ case seen[abbrev] += 1
+ when 1
+ table[abbrev] = word
+ when 2
+ table.delete(abbrev)
+ else
+ break
+ end
+ end
+ end
+
+ words.each do |word|
+ next if pattern && pattern !~ word
+
+ table[word] = word
+ end
+
+ table
+ end
+
+ module_function :abbrev
+end
+
+class Array
+ # Calculates the set of unambiguous abbreviations for the strings in
+ # +self+. If passed a pattern or a string, only the strings matching
+ # the pattern or starting with the string are considered.
+ #
+ # %w{ car cone }.abbrev #=> { "ca" => "car", "car" => "car",
+ # "co" => "cone", "con" => cone",
+ # "cone" => "cone" }
+ def abbrev(pattern = nil)
+ Abbrev::abbrev(self, pattern)
+ end
+end
+
+if $0 == __FILE__
+ while line = gets
+ hash = line.split.abbrev
+
+ hash.sort.each do |k, v|
+ puts "#{k} => #{v}"
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/base64.rb b/ruby_1_8_5/lib/base64.rb
new file mode 100644
index 0000000000..d80eb5a570
--- /dev/null
+++ b/ruby_1_8_5/lib/base64.rb
@@ -0,0 +1,133 @@
+#
+# = base64.rb: methods for base64-encoding and -decoding stings
+#
+# Author:: Yukihiro Matsumoto
+# Documentation:: Dave Thomas and Gavin Sinclair
+#
+# Until Ruby 1.8.1, these methods were defined at the top-level. Now
+# they are in the Base64 module but included in the top-level, where
+# their usage is deprecated.
+#
+# See Base64 for documentation.
+#
+
+require "kconv"
+
+
+# The Base64 module provides for the encoding (#encode64) and decoding
+# (#decode64) of binary data using a Base64 representation.
+#
+# The following particular features are also provided:
+# - encode into lines of a given length (#b64encode)
+# - decode the special format specified in RFC2047 for the
+# representation of email headers (decode_b)
+#
+# == Example
+#
+# A simple encoding and decoding.
+#
+# require "base64"
+#
+# enc = Base64.encode64('Send reinforcements')
+# # -> "U2VuZCByZWluZm9yY2VtZW50cw==\n"
+# plain = Base64.decode64(enc)
+# # -> "Send reinforcements"
+#
+# The purpose of using base64 to encode data is that it translates any
+# binary data into purely printable characters. It is specified in
+# RFC 2045 (http://www.faqs.org/rfcs/rfc2045.html).
+
+module Base64
+ module_function
+
+ # Returns the Base64-decoded version of +str+.
+ #
+ # require 'base64'
+ # str = 'VGhpcyBpcyBsaW5lIG9uZQpUaGlzIG' +
+ # 'lzIGxpbmUgdHdvClRoaXMgaXMgbGlu' +
+ # 'ZSB0aHJlZQpBbmQgc28gb24uLi4K'
+ # puts Base64.decode64(str)
+ #
+ # <i>Generates:</i>
+ #
+ # This is line one
+ # This is line two
+ # This is line three
+ # And so on...
+
+ def decode64(str)
+ str.unpack("m")[0]
+ end
+
+
+ # Decodes text formatted using a subset of RFC2047 (the one used for
+ # mime-encoding mail headers).
+ #
+ # Only supports an encoding type of 'b' (base 64), and only supports
+ # the character sets ISO-2022-JP and SHIFT_JIS (so the only two
+ # encoded word sequences recognized are <tt>=?ISO-2022-JP?B?...=</tt> and
+ # <tt>=?SHIFT_JIS?B?...=</tt>). Recognition of these sequences is case
+ # insensitive.
+
+ def decode_b(str)
+ str.gsub!(/=\?ISO-2022-JP\?B\?([!->@-~]+)\?=/i) {
+ decode64($1)
+ }
+ str = Kconv::toeuc(str)
+ str.gsub!(/=\?SHIFT_JIS\?B\?([!->@-~]+)\?=/i) {
+ decode64($1)
+ }
+ str = Kconv::toeuc(str)
+ str.gsub!(/\n/, ' ')
+ str.gsub!(/\0/, '')
+ str
+ end
+
+ # Returns the Base64-encoded version of +str+.
+ #
+ # require 'base64'
+ # Base64.b64encode("Now is the time for all good coders\nto learn Ruby")
+ #
+ # <i>Generates:</i>
+ #
+ # Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g
+ # UnVieQ==
+
+ def encode64(bin)
+ [bin].pack("m")
+ end
+
+ # _Prints_ the Base64 encoded version of +bin+ (a +String+) in lines of
+ # +len+ (default 60) characters.
+ #
+ # require 'base64'
+ # data = "Now is the time for all good coders\nto learn Ruby"
+ # puts Base64.b64encode(data)
+ #
+ # <i>Generates:</i>
+ #
+ # Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g
+ # UnVieQ==
+
+ def b64encode(bin, len = 60)
+ encode64(bin).scan(/.{1,#{len}}/) do
+ print $&, "\n"
+ end
+ end
+
+
+ module Deprecated # :nodoc:
+ include Base64
+
+ for m in Base64.private_instance_methods(false)
+ module_eval %{
+ def #{m}(*args)
+ warn("\#{caller(1)[0]}: #{m} is deprecated; use Base64.#{m} instead")
+ super
+ end
+ }
+ end
+ end
+end
+
+include Base64::Deprecated
diff --git a/ruby_1_8_5/lib/benchmark.rb b/ruby_1_8_5/lib/benchmark.rb
new file mode 100644
index 0000000000..ea62dcd208
--- /dev/null
+++ b/ruby_1_8_5/lib/benchmark.rb
@@ -0,0 +1,569 @@
+=begin
+#
+# benchmark.rb - a performance benchmarking library
+#
+# $Id: benchmark.rb,v 1.8.2.2 2004/04/18 23:20:32 nobu Exp $
+#
+# Created by Gotoken (gotoken@notwork.org).
+#
+# Documentation by Gotoken (original RD), Lyle Johnson (RDoc conversion), and
+# Gavin Sinclair (editing).
+#
+=end
+
+# == Overview
+#
+# The Benchmark module provides methods for benchmarking Ruby code, giving
+# detailed reports on the time taken for each task.
+#
+
+# The Benchmark module provides methods to measure and report the time
+# used to execute Ruby code.
+#
+# * Measure the time to construct the string given by the expression
+# <tt>"a"*1_000_000</tt>:
+#
+# require 'benchmark'
+#
+# puts Benchmark.measure { "a"*1_000_000 }
+#
+# On my machine (FreeBSD 3.2 on P5, 100MHz) this generates:
+#
+# 1.166667 0.050000 1.216667 ( 0.571355)
+#
+# This report shows the user CPU time, system CPU time, the sum of
+# the user and system CPU times, and the elapsed real time. The unit
+# of time is seconds.
+#
+# * Do some experiments sequentially using the #bm method:
+#
+# require 'benchmark'
+#
+# n = 50000
+# Benchmark.bm do |x|
+# x.report { for i in 1..n; a = "1"; end }
+# x.report { n.times do ; a = "1"; end }
+# x.report { 1.upto(n) do ; a = "1"; end }
+# end
+#
+# The result:
+#
+# user system total real
+# 1.033333 0.016667 1.016667 ( 0.492106)
+# 1.483333 0.000000 1.483333 ( 0.694605)
+# 1.516667 0.000000 1.516667 ( 0.711077)
+#
+# * Continuing the previous example, put a label in each report:
+#
+# require 'benchmark'
+#
+# n = 50000
+# Benchmark.bm(7) do |x|
+# x.report("for:") { for i in 1..n; a = "1"; end }
+# x.report("times:") { n.times do ; a = "1"; end }
+# x.report("upto:") { 1.upto(n) do ; a = "1"; end }
+# end
+#
+# The result:
+#
+# user system total real
+# for: 1.050000 0.000000 1.050000 ( 0.503462)
+# times: 1.533333 0.016667 1.550000 ( 0.735473)
+# upto: 1.500000 0.016667 1.516667 ( 0.711239)
+#
+#
+# * The times for some benchmarks depend on the order in which items
+# are run. These differences are due to the cost of memory
+# allocation and garbage collection. To avoid these discrepancies,
+# the #bmbm method is provided. For example, to compare ways to
+# sort an array of floats:
+#
+# require 'benchmark'
+#
+# array = (1..1000000).map { rand }
+#
+# Benchmark.bmbm do |x|
+# x.report("sort!") { array.dup.sort! }
+# x.report("sort") { array.dup.sort }
+# end
+#
+# The result:
+#
+# Rehearsal -----------------------------------------
+# sort! 11.928000 0.010000 11.938000 ( 12.756000)
+# sort 13.048000 0.020000 13.068000 ( 13.857000)
+# ------------------------------- total: 25.006000sec
+#
+# user system total real
+# sort! 12.959000 0.010000 12.969000 ( 13.793000)
+# sort 12.007000 0.000000 12.007000 ( 12.791000)
+#
+#
+# * Report statistics of sequential experiments with unique labels,
+# using the #benchmark method:
+#
+# require 'benchmark'
+#
+# n = 50000
+# Benchmark.benchmark(" "*7 + CAPTION, 7, FMTSTR, ">total:", ">avg:") do |x|
+# tf = x.report("for:") { for i in 1..n; a = "1"; end }
+# tt = x.report("times:") { n.times do ; a = "1"; end }
+# tu = x.report("upto:") { 1.upto(n) do ; a = "1"; end }
+# [tf+tt+tu, (tf+tt+tu)/3]
+# end
+#
+# The result:
+#
+# user system total real
+# for: 1.016667 0.016667 1.033333 ( 0.485749)
+# times: 1.450000 0.016667 1.466667 ( 0.681367)
+# upto: 1.533333 0.000000 1.533333 ( 0.722166)
+# >total: 4.000000 0.033333 4.033333 ( 1.889282)
+# >avg: 1.333333 0.011111 1.344444 ( 0.629761)
+
+module Benchmark
+
+ BENCHMARK_VERSION = "2002-04-25" #:nodoc"
+
+ def Benchmark::times() # :nodoc:
+ Process::times()
+ end
+
+
+ # Invokes the block with a <tt>Benchmark::Report</tt> object, which
+ # may be used to collect and report on the results of individual
+ # benchmark tests. Reserves <i>label_width</i> leading spaces for
+ # labels on each line. Prints _caption_ at the top of the
+ # report, and uses _fmt_ to format each line.
+ # If the block returns an array of
+ # <tt>Benchmark::Tms</tt> objects, these will be used to format
+ # additional lines of output. If _label_ parameters are
+ # given, these are used to label these extra lines.
+ #
+ # _Note_: Other methods provide a simpler interface to this one, and are
+ # suitable for nearly all benchmarking requirements. See the examples in
+ # Benchmark, and the #bm and #bmbm methods.
+ #
+ # Example:
+ #
+ # require 'benchmark'
+ # include Benchmark # we need the CAPTION and FMTSTR constants
+ #
+ # n = 50000
+ # Benchmark.benchmark(" "*7 + CAPTION, 7, FMTSTR, ">total:", ">avg:") do |x|
+ # tf = x.report("for:") { for i in 1..n; a = "1"; end }
+ # tt = x.report("times:") { n.times do ; a = "1"; end }
+ # tu = x.report("upto:") { 1.upto(n) do ; a = "1"; end }
+ # [tf+tt+tu, (tf+tt+tu)/3]
+ # end
+ #
+ # <i>Generates:</i>
+ #
+ # user system total real
+ # for: 1.016667 0.016667 1.033333 ( 0.485749)
+ # times: 1.450000 0.016667 1.466667 ( 0.681367)
+ # upto: 1.533333 0.000000 1.533333 ( 0.722166)
+ # >total: 4.000000 0.033333 4.033333 ( 1.889282)
+ # >avg: 1.333333 0.011111 1.344444 ( 0.629761)
+ #
+
+ def benchmark(caption = "", label_width = nil, fmtstr = nil, *labels) # :yield: report
+ sync = STDOUT.sync
+ STDOUT.sync = true
+ label_width ||= 0
+ fmtstr ||= FMTSTR
+ raise ArgumentError, "no block" unless iterator?
+ print caption
+ results = yield(Report.new(label_width, fmtstr))
+ Array === results and results.grep(Tms).each {|t|
+ print((labels.shift || t.label || "").ljust(label_width),
+ t.format(fmtstr))
+ }
+ STDOUT.sync = sync
+ end
+
+
+ # A simple interface to the #benchmark method, #bm is generates sequential reports
+ # with labels. The parameters have the same meaning as for #benchmark.
+ #
+ # require 'benchmark'
+ #
+ # n = 50000
+ # Benchmark.bm(7) do |x|
+ # x.report("for:") { for i in 1..n; a = "1"; end }
+ # x.report("times:") { n.times do ; a = "1"; end }
+ # x.report("upto:") { 1.upto(n) do ; a = "1"; end }
+ # end
+ #
+ # <i>Generates:</i>
+ #
+ # user system total real
+ # for: 1.050000 0.000000 1.050000 ( 0.503462)
+ # times: 1.533333 0.016667 1.550000 ( 0.735473)
+ # upto: 1.500000 0.016667 1.516667 ( 0.711239)
+ #
+
+ def bm(label_width = 0, *labels, &blk) # :yield: report
+ benchmark(" "*label_width + CAPTION, label_width, FMTSTR, *labels, &blk)
+ end
+
+
+ # Sometimes benchmark results are skewed because code executed
+ # earlier encounters different garbage collection overheads than
+ # that run later. #bmbm attempts to minimize this effect by running
+ # the tests twice, the first time as a rehearsal in order to get the
+ # runtime environment stable, the second time for
+ # real. <tt>GC.start</tt> is executed before the start of each of
+ # the real timings; the cost of this is not included in the
+ # timings. In reality, though, there's only so much that #bmbm can
+ # do, and the results are not guaranteed to be isolated from garbage
+ # collection and other effects.
+ #
+ # Because #bmbm takes two passes through the tests, it can
+ # calculate the required label width.
+ #
+ # require 'benchmark'
+ #
+ # array = (1..1000000).map { rand }
+ #
+ # Benchmark.bmbm do |x|
+ # x.report("sort!") { array.dup.sort! }
+ # x.report("sort") { array.dup.sort }
+ # end
+ #
+ # <i>Generates:</i>
+ #
+ # Rehearsal -----------------------------------------
+ # sort! 11.928000 0.010000 11.938000 ( 12.756000)
+ # sort 13.048000 0.020000 13.068000 ( 13.857000)
+ # ------------------------------- total: 25.006000sec
+ #
+ # user system total real
+ # sort! 12.959000 0.010000 12.969000 ( 13.793000)
+ # sort 12.007000 0.000000 12.007000 ( 12.791000)
+ #
+ # #bmbm yields a Benchmark::Job object and returns an array of
+ # Benchmark::Tms objects.
+ #
+ def bmbm(width = 0, &blk) # :yield: job
+ job = Job.new(width)
+ yield(job)
+ width = job.width
+ sync = STDOUT.sync
+ STDOUT.sync = true
+
+ # rehearsal
+ print "Rehearsal "
+ puts '-'*(width+CAPTION.length - "Rehearsal ".length)
+ list = []
+ job.list.each{|label,item|
+ print(label.ljust(width))
+ res = Benchmark::measure(&item)
+ print res.format()
+ list.push res
+ }
+ sum = Tms.new; list.each{|i| sum += i}
+ ets = sum.format("total: %tsec")
+ printf("%s %s\n\n",
+ "-"*(width+CAPTION.length-ets.length-1), ets)
+
+ # take
+ print ' '*width, CAPTION
+ list = []
+ ary = []
+ job.list.each{|label,item|
+ GC::start
+ print label.ljust(width)
+ res = Benchmark::measure(&item)
+ print res.format()
+ ary.push res
+ list.push [label, res]
+ }
+
+ STDOUT.sync = sync
+ ary
+ end
+
+ #
+ # Returns the time used to execute the given block as a
+ # Benchmark::Tms object.
+ #
+ def measure(label = "") # :yield:
+ t0, r0 = Benchmark.times, Time.now
+ yield
+ t1, r1 = Benchmark.times, Time.now
+ Benchmark::Tms.new(t1.utime - t0.utime,
+ t1.stime - t0.stime,
+ t1.cutime - t0.cutime,
+ t1.cstime - t0.cstime,
+ r1.to_f - r0.to_f,
+ label)
+ end
+
+ #
+ # Returns the elapsed real time used to execute the given block.
+ #
+ def realtime(&blk) # :yield:
+ Benchmark::measure(&blk).real
+ end
+
+
+
+ #
+ # A Job is a sequence of labelled blocks to be processed by the
+ # Benchmark.bmbm method. It is of little direct interest to the user.
+ #
+ class Job # :nodoc:
+ #
+ # Returns an initialized Job instance.
+ # Usually, one doesn't call this method directly, as new
+ # Job objects are created by the #bmbm method.
+ # _width_ is a initial value for the label offset used in formatting;
+ # the #bmbm method passes its _width_ argument to this constructor.
+ #
+ def initialize(width)
+ @width = width
+ @list = []
+ end
+
+ #
+ # Registers the given label and block pair in the job list.
+ #
+ def item(label = "", &blk) # :yield:
+ raise ArgmentError, "no block" unless block_given?
+ label.concat ' '
+ w = label.length
+ @width = w if @width < w
+ @list.push [label, blk]
+ self
+ end
+
+ alias report item
+
+ # An array of 2-element arrays, consisting of label and block pairs.
+ attr_reader :list
+
+ # Length of the widest label in the #list, plus one.
+ attr_reader :width
+ end
+
+ module_function :benchmark, :measure, :realtime, :bm, :bmbm
+
+
+
+ #
+ # This class is used by the Benchmark.benchmark and Benchmark.bm methods.
+ # It is of little direct interest to the user.
+ #
+ class Report # :nodoc:
+ #
+ # Returns an initialized Report instance.
+ # Usually, one doesn't call this method directly, as new
+ # Report objects are created by the #benchmark and #bm methods.
+ # _width_ and _fmtstr_ are the label offset and
+ # format string used by Tms#format.
+ #
+ def initialize(width = 0, fmtstr = nil)
+ @width, @fmtstr = width, fmtstr
+ end
+
+ #
+ # Prints the _label_ and measured time for the block,
+ # formatted by _fmt_. See Tms#format for the
+ # formatting rules.
+ #
+ def item(label = "", *fmt, &blk) # :yield:
+ print label.ljust(@width)
+ res = Benchmark::measure(&blk)
+ print res.format(@fmtstr, *fmt)
+ res
+ end
+
+ alias report item
+ end
+
+
+
+ #
+ # A data object, representing the times associated with a benchmark
+ # measurement.
+ #
+ class Tms
+ CAPTION = " user system total real\n"
+ FMTSTR = "%10.6u %10.6y %10.6t %10.6r\n"
+
+ # User CPU time
+ attr_reader :utime
+
+ # System CPU time
+ attr_reader :stime
+
+ # User CPU time of children
+ attr_reader :cutime
+
+ # System CPU time of children
+ attr_reader :cstime
+
+ # Elapsed real time
+ attr_reader :real
+
+ # Total time, that is _utime_ + _stime_ + _cutime_ + _cstime_
+ attr_reader :total
+
+ # Label
+ attr_reader :label
+
+ #
+ # Returns an initialized Tms object which has
+ # _u_ as the user CPU time, _s_ as the system CPU time,
+ # _cu_ as the children's user CPU time, _cs_ as the children's
+ # system CPU time, _real_ as the elapsed real time and _l_
+ # as the label.
+ #
+ def initialize(u = 0.0, s = 0.0, cu = 0.0, cs = 0.0, real = 0.0, l = nil)
+ @utime, @stime, @cutime, @cstime, @real, @label = u, s, cu, cs, real, l
+ @total = @utime + @stime + @cutime + @cstime
+ end
+
+ #
+ # Returns a new Tms object whose times are the sum of the times for this
+ # Tms object, plus the time required to execute the code block (_blk_).
+ #
+ def add(&blk) # :yield:
+ self + Benchmark::measure(&blk)
+ end
+
+ #
+ # An in-place version of #add.
+ #
+ def add!
+ t = Benchmark::measure(&blk)
+ @utime = utime + t.utime
+ @stime = stime + t.stime
+ @cutime = cutime + t.cutime
+ @cstime = cstime + t.cstime
+ @real = real + t.real
+ self
+ end
+
+ #
+ # Returns a new Tms object obtained by memberwise summation
+ # of the individual times for this Tms object with those of the other
+ # Tms object.
+ # This method and #/() are useful for taking statistics.
+ #
+ def +(other); memberwise(:+, other) end
+
+ #
+ # Returns a new Tms object obtained by memberwise subtraction
+ # of the individual times for the other Tms object from those of this
+ # Tms object.
+ #
+ def -(other); memberwise(:-, other) end
+
+ #
+ # Returns a new Tms object obtained by memberwise multiplication
+ # of the individual times for this Tms object by _x_.
+ #
+ def *(x); memberwise(:*, x) end
+
+ #
+ # Returns a new Tms object obtained by memberwise division
+ # of the individual times for this Tms object by _x_.
+ # This method and #+() are useful for taking statistics.
+ #
+ def /(x); memberwise(:/, x) end
+
+ #
+ # Returns the contents of this Tms object as
+ # a formatted string, according to a format string
+ # like that passed to Kernel.format. In addition, #format
+ # accepts the following extensions:
+ #
+ # <tt>%u</tt>:: Replaced by the user CPU time, as reported by Tms#utime.
+ # <tt>%y</tt>:: Replaced by the system CPU time, as reported by #stime (Mnemonic: y of "s*y*stem")
+ # <tt>%U</tt>:: Replaced by the children's user CPU time, as reported by Tms#cutime
+ # <tt>%Y</tt>:: Replaced by the children's system CPU time, as reported by Tms#cstime
+ # <tt>%t</tt>:: Replaced by the total CPU time, as reported by Tms#total
+ # <tt>%r</tt>:: Replaced by the elapsed real time, as reported by Tms#real
+ # <tt>%n</tt>:: Replaced by the label string, as reported by Tms#label (Mnemonic: n of "*n*ame")
+ #
+ # If _fmtstr_ is not given, FMTSTR is used as default value, detailing the
+ # user, system and real elapsed time.
+ #
+ def format(arg0 = nil, *args)
+ fmtstr = (arg0 || FMTSTR).dup
+ fmtstr.gsub!(/(%[-+\.\d]*)n/){"#{$1}s" % label}
+ fmtstr.gsub!(/(%[-+\.\d]*)u/){"#{$1}f" % utime}
+ fmtstr.gsub!(/(%[-+\.\d]*)y/){"#{$1}f" % stime}
+ fmtstr.gsub!(/(%[-+\.\d]*)U/){"#{$1}f" % cutime}
+ fmtstr.gsub!(/(%[-+\.\d]*)Y/){"#{$1}f" % cstime}
+ fmtstr.gsub!(/(%[-+\.\d]*)t/){"#{$1}f" % total}
+ fmtstr.gsub!(/(%[-+\.\d]*)r/){"(#{$1}f)" % real}
+ arg0 ? Kernel::format(fmtstr, *args) : fmtstr
+ end
+
+ #
+ # Same as #format.
+ #
+ def to_s
+ format
+ end
+
+ #
+ # Returns a new 6-element array, consisting of the
+ # label, user CPU time, system CPU time, children's
+ # user CPU time, children's system CPU time and elapsed
+ # real time.
+ #
+ def to_a
+ [@label, @utime, @stime, @cutime, @cstime, @real]
+ end
+
+ protected
+ def memberwise(op, x)
+ case x
+ when Benchmark::Tms
+ Benchmark::Tms.new(utime.__send__(op, x.utime),
+ stime.__send__(op, x.stime),
+ cutime.__send__(op, x.cutime),
+ cstime.__send__(op, x.cstime),
+ real.__send__(op, x.real)
+ )
+ else
+ Benchmark::Tms.new(utime.__send__(op, x),
+ stime.__send__(op, x),
+ cutime.__send__(op, x),
+ cstime.__send__(op, x),
+ real.__send__(op, x)
+ )
+ end
+ end
+ end
+
+ # The default caption string (heading above the output times).
+ CAPTION = Benchmark::Tms::CAPTION
+
+ # The default format string used to display times. See also Benchmark::Tms#format.
+ FMTSTR = Benchmark::Tms::FMTSTR
+end
+
+if __FILE__ == $0
+ include Benchmark
+
+ n = ARGV[0].to_i.nonzero? || 50000
+ puts %Q([#{n} times iterations of `a = "1"'])
+ benchmark(" " + CAPTION, 7, FMTSTR) do |x|
+ x.report("for:") {for i in 1..n; a = "1"; end} # Benchmark::measure
+ x.report("times:") {n.times do ; a = "1"; end}
+ x.report("upto:") {1.upto(n) do ; a = "1"; end}
+ end
+
+ benchmark do
+ [
+ measure{for i in 1..n; a = "1"; end}, # Benchmark::measure
+ measure{n.times do ; a = "1"; end},
+ measure{1.upto(n) do ; a = "1"; end}
+ ]
+ end
+end
diff --git a/ruby_1_8_5/lib/cgi-lib.rb b/ruby_1_8_5/lib/cgi-lib.rb
new file mode 100644
index 0000000000..d6b60d66cc
--- /dev/null
+++ b/ruby_1_8_5/lib/cgi-lib.rb
@@ -0,0 +1,272 @@
+warn "Warning:#{caller[0].sub(/:in `.*'\z/, '')}: cgi-lib is deprecated after Ruby 1.8.1; use cgi instead"
+
+=begin
+
+= simple CGI support library
+
+= example
+
+== get form values
+
+ require "cgi-lib.rb"
+ query = CGI.new
+ query['field'] # <== value of 'field'
+ query.keys # <== array of fields
+
+and query has Hash class methods
+
+
+== get cookie values
+
+ require "cgi-lib.rb"
+ query = CGI.new
+ query.cookie['name'] # <== cookie value of 'name'
+ query.cookie.keys # <== all cookie names
+
+and query.cookie has Hash class methods
+
+
+== print HTTP header and HTML string to $>
+
+ require "cgi-lib.rb"
+ CGI::print{
+ CGI::tag("HTML"){
+ CGI::tag("HEAD"){ CGI::tag("TITLE"){"TITLE"} } +
+ CGI::tag("BODY"){
+ CGI::tag("FORM", {"ACTION"=>"test.rb", "METHOD"=>"POST"}){
+ CGI::tag("INPUT", {"TYPE"=>"submit", "VALUE"=>"submit"})
+ } +
+ CGI::tag("HR")
+ }
+ }
+ }
+
+
+== make raw cookie string
+
+ require "cgi-lib.rb"
+ cookie1 = CGI::cookie({'name' => 'name',
+ 'value' => 'value',
+ 'path' => 'path', # optional
+ 'domain' => 'domain', # optional
+ 'expires' => Time.now, # optional
+ 'secure' => true # optional
+ })
+
+ CGI::print("Content-Type: text/html", cookie1, cookie2){ "string" }
+
+
+== print HTTP header and string to $>
+
+ require "cgi-lib.rb"
+ CGI::print{ "string" }
+ # == CGI::print("Content-Type: text/html"){ "string" }
+ CGI::print("Content-Type: text/html", cookie1, cookie2){ "string" }
+
+
+=== NPH (no-parse-header) mode
+
+ require "cgi-lib.rb"
+ CGI::print("nph"){ "string" }
+ # == CGI::print("nph", "Content-Type: text/html"){ "string" }
+ CGI::print("nph", "Content-Type: text/html", cookie1, cookie2){ "string" }
+
+
+== make HTML tag string
+
+ require "cgi-lib.rb"
+ CGI::tag("element", {"attribute_name"=>"attribute_value"}){"content"}
+
+
+== make HTTP header string
+
+ require "cgi-lib.rb"
+ CGI::header # == CGI::header("Content-Type: text/html")
+ CGI::header("Content-Type: text/html", cookie1, cookie2)
+
+
+=== NPH (no-parse-header) mode
+
+ CGI::header("nph") # == CGI::header("nph", "Content-Type: text/html")
+ CGI::header("nph", "Content-Type: text/html", cookie1, cookie2)
+
+
+== escape url encode
+
+ require "cgi-lib.rb"
+ url_encoded_string = CGI::escape("string")
+
+
+== unescape url encoded
+
+ require "cgi-lib.rb"
+ string = CGI::unescape("url encoded string")
+
+
+== escape HTML &"<>
+
+ require "cgi-lib.rb"
+ CGI::escapeHTML("string")
+
+
+=end
+
+require "delegate"
+
+class CGI < SimpleDelegator
+
+ CR = "\015"
+ LF = "\012"
+ EOL = CR + LF
+
+ 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 ]
+
+ # make rfc1123 date string
+ 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
+
+ # escape url encode
+ def CGI::escape(str)
+ str.gsub(/[^a-zA-Z0-9_\-.]/n){ sprintf("%%%02X", $&.unpack("C")[0]) }
+ end
+
+ # unescape url encoded
+ def CGI::unescape(str)
+ str.gsub(/\+/, ' ').gsub(/%([0-9a-fA-F]{2})/){ [$1.hex].pack("c") }
+ end
+
+ # escape HTML
+ def CGI::escapeHTML(str)
+ str.gsub(/&/, "&amp;").gsub(/\"/, "&quot;").gsub(/>/, "&gt;").gsub(/</, "&lt;")
+ end
+
+ # offline mode. read name=value pairs on standard input.
+ def read_from_cmdline
+ require "shellwords.rb"
+ words = Shellwords.shellwords(
+ if not ARGV.empty?
+ ARGV.join(' ')
+ else
+ STDERR.print "(offline mode: enter name=value pairs on standard input)\n" if STDIN.tty?
+ readlines.join(' ').gsub(/\n/, '')
+ end.gsub(/\\=/, '%3D').gsub(/\\&/, '%26'))
+
+ if words.find{|x| x =~ /=/} then words.join('&') else words.join('+') end
+ end
+
+ def initialize(input = $stdin)
+
+ @inputs = {}
+ @cookie = {}
+
+ case ENV['REQUEST_METHOD']
+ when "GET"
+ ENV['QUERY_STRING'] or ""
+ when "POST"
+ input.read(Integer(ENV['CONTENT_LENGTH'])) or ""
+ else
+ read_from_cmdline
+ end.split(/[&;]/).each do |x|
+ key, val = x.split(/=/,2).collect{|x|CGI::unescape(x)}
+ if @inputs.include?(key)
+ @inputs[key] += "\0" + (val or "")
+ else
+ @inputs[key] = (val or "")
+ end
+ end
+
+ super(@inputs)
+
+ if ENV.has_key?('HTTP_COOKIE') or ENV.has_key?('COOKIE')
+ (ENV['HTTP_COOKIE'] or ENV['COOKIE']).split(/; /).each do |x|
+ key, val = x.split(/=/,2)
+ key = CGI::unescape(key)
+ val = val.split(/&/).collect{|x|CGI::unescape(x)}.join("\0")
+ if @cookie.include?(key)
+ @cookie[key] += "\0" + val
+ else
+ @cookie[key] = val
+ end
+ end
+ end
+ end
+
+ attr("inputs")
+ attr("cookie")
+
+ # make HTML tag string
+ def CGI::tag(element, attributes = {})
+ "<" + escapeHTML(element) + attributes.collect{|name, value|
+ " " + escapeHTML(name) + '="' + escapeHTML(value) + '"'
+ }.to_s + ">" +
+ (iterator? ? yield.to_s + "</" + escapeHTML(element) + ">" : "")
+ end
+
+ # make raw cookie string
+ def CGI::cookie(options)
+ "Set-Cookie: " + options['name'] + '=' + escape(options['value']) +
+ (options['domain'] ? '; domain=' + options['domain'] : '') +
+ (options['path'] ? '; path=' + options['path'] : '') +
+ (options['expires'] ? '; expires=' + rfc1123_date(options['expires']) : '') +
+ (options['secure'] ? '; secure' : '')
+ end
+
+ # make HTTP header string
+ def CGI::header(*options)
+ if defined?(MOD_RUBY)
+ options.each{|option|
+ option.sub(/(.*?): (.*)/){
+ Apache::request.headers_out[$1] = $2
+ }
+ }
+ Apache::request.send_http_header
+ ''
+ else
+ if options.delete("nph") or (ENV['SERVER_SOFTWARE'] =~ /IIS/)
+ [(ENV['SERVER_PROTOCOL'] or "HTTP/1.0") + " 200 OK",
+ "Date: " + rfc1123_date(Time.now),
+ "Server: " + (ENV['SERVER_SOFTWARE'] or ""),
+ "Connection: close"] +
+ (options.empty? ? ["Content-Type: text/html"] : options)
+ else
+ options.empty? ? ["Content-Type: text/html"] : options
+ end.join(EOL) + EOL + EOL
+ end
+ end
+
+ # print HTTP header and string to $>
+ def CGI::print(*options)
+ $>.print CGI::header(*options) + yield.to_s
+ end
+
+ # print message to $>
+ def CGI::message(message, title = "", header = ["Content-Type: text/html"])
+ if message.kind_of?(Hash)
+ title = message['title']
+ header = message['header']
+ message = message['body']
+ end
+ CGI::print(*header){
+ CGI::tag("HTML"){
+ CGI::tag("HEAD"){ CGI.tag("TITLE"){ title } } +
+ CGI::tag("BODY"){ message }
+ }
+ }
+ true
+ end
+
+ # print error message to $> and exit
+ def CGI::error
+ CGI::message({'title'=>'ERROR', 'body'=>
+ CGI::tag("PRE"){
+ "ERROR: " + CGI::tag("STRONG"){ escapeHTML($!.to_s) } + "\n" + escapeHTML($@.join("\n"))
+ }
+ })
+ exit
+ end
+end
diff --git a/ruby_1_8_5/lib/cgi.rb b/ruby_1_8_5/lib/cgi.rb
new file mode 100644
index 0000000000..a5f79b14ac
--- /dev/null
+++ b/ruby_1_8_5/lib/cgi.rb
@@ -0,0 +1,2303 @@
+#
+# cgi.rb - cgi support library
+#
+# Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+#
+# Copyright (C) 2000 Information-technology Promotion Agency, Japan
+#
+# Author: Wakou Aoyama <wakou@ruby-lang.org>
+#
+# Documentation: Wakou Aoyama (RDoc'd and embellished by William Webber)
+#
+# == Overview
+#
+# The Common Gateway Interface (CGI) is a simple protocol
+# for passing an HTTP request from a web server to a
+# standalone program, and returning the output to the web
+# browser. Basically, a CGI program is called with the
+# parameters of the request passed in either in the
+# environment (GET) or via $stdin (POST), and everything
+# it prints to $stdout is returned to the client.
+#
+# This file holds the +CGI+ class. This class provides
+# functionality for retrieving HTTP request parameters,
+# managing cookies, and generating HTML output. See the
+# class documentation for more details and examples of use.
+#
+# The file cgi/session.rb provides session management
+# functionality; see that file for more details.
+#
+# See http://www.w3.org/CGI/ for more information on the CGI
+# protocol.
+
+raise "Please, use ruby 1.5.4 or later." if RUBY_VERSION < "1.5.4"
+
+require 'English'
+
+# CGI class. See documentation for the file cgi.rb for an overview
+# of the CGI protocol.
+#
+# == Introduction
+#
+# CGI is a large class, providing several categories of methods, many of which
+# are mixed in from other modules. Some of the documentation is in this class,
+# some in the modules CGI::QueryExtension and CGI::HtmlExtension. See
+# CGI::Cookie for specific information on handling cookies, and cgi/session.rb
+# (CGI::Session) for information on sessions.
+#
+# For queries, CGI provides methods to get at environmental variables,
+# parameters, cookies, and multipart request data. For responses, CGI provides
+# methods for writing output and generating HTML.
+#
+# Read on for more details. Examples are provided at the bottom.
+#
+# == Queries
+#
+# The CGI class dynamically mixes in parameter and cookie-parsing
+# functionality, environmental variable access, and support for
+# parsing multipart requests (including uploaded files) from the
+# CGI::QueryExtension module.
+#
+# === Environmental Variables
+#
+# The standard CGI environmental variables are available as read-only
+# attributes of a CGI object. The following is a list of these variables:
+#
+#
+# AUTH_TYPE HTTP_HOST REMOTE_IDENT
+# CONTENT_LENGTH HTTP_NEGOTIATE REMOTE_USER
+# CONTENT_TYPE HTTP_PRAGMA REQUEST_METHOD
+# GATEWAY_INTERFACE HTTP_REFERER SCRIPT_NAME
+# HTTP_ACCEPT HTTP_USER_AGENT SERVER_NAME
+# HTTP_ACCEPT_CHARSET PATH_INFO SERVER_PORT
+# HTTP_ACCEPT_ENCODING PATH_TRANSLATED SERVER_PROTOCOL
+# HTTP_ACCEPT_LANGUAGE QUERY_STRING SERVER_SOFTWARE
+# HTTP_CACHE_CONTROL REMOTE_ADDR
+# HTTP_FROM REMOTE_HOST
+#
+#
+# For each of these variables, there is a corresponding attribute with the
+# same name, except all lower case and without a preceding HTTP_.
+# +content_length+ and +server_port+ are integers; the rest are strings.
+#
+# === Parameters
+#
+# The method #params() returns a hash of all parameters in the request as
+# name/value-list pairs, where the value-list is an Array of one or more
+# values. The CGI object itself also behaves as a hash of parameter names
+# to values, but only returns a single value (as a String) for each
+# parameter name.
+#
+# For instance, suppose the request contains the parameter
+# "favourite_colours" with the multiple values "blue" and "green". The
+# following behaviour would occur:
+#
+# cgi.params["favourite_colours"] # => ["blue", "green"]
+# cgi["favourite_colours"] # => "blue"
+#
+# If a parameter does not exist, the former method will return an empty
+# array, the latter an empty string. The simplest way to test for existence
+# of a parameter is by the #has_key? method.
+#
+# === Cookies
+#
+# HTTP Cookies are automatically parsed from the request. They are available
+# from the #cookies() accessor, which returns a hash from cookie name to
+# CGI::Cookie object.
+#
+# === Multipart requests
+#
+# If a request's method is POST and its content type is multipart/form-data,
+# then it may contain uploaded files. These are stored by the QueryExtension
+# module in the parameters of the request. The parameter name is the name
+# attribute of the file input field, as usual. However, the value is not
+# a string, but an IO object, either an IOString for small files, or a
+# Tempfile for larger ones. This object also has the additional singleton
+# methods:
+#
+# #local_path():: the path of the uploaded file on the local filesystem
+# #original_filename():: the name of the file on the client computer
+# #content_type():: the content type of the file
+#
+# == Responses
+#
+# The CGI class provides methods for sending header and content output to
+# the HTTP client, and mixes in methods for programmatic HTML generation
+# from CGI::HtmlExtension and CGI::TagMaker modules. The precise version of HTML
+# to use for HTML generation is specified at object creation time.
+#
+# === Writing output
+#
+# The simplest way to send output to the HTTP client is using the #out() method.
+# This takes the HTTP headers as a hash parameter, and the body content
+# via a block. The headers can be generated as a string using the #header()
+# method. The output stream can be written directly to using the #print()
+# method.
+#
+# === Generating HTML
+#
+# Each HTML element has a corresponding method for generating that
+# element as a String. The name of this method is the same as that
+# of the element, all lowercase. The attributes of the element are
+# passed in as a hash, and the body as a no-argument block that evaluates
+# to a String. The HTML generation module knows which elements are
+# always empty, and silently drops any passed-in body. It also knows
+# which elements require matching closing tags and which don't. However,
+# it does not know what attributes are legal for which elements.
+#
+# There are also some additional HTML generation methods mixed in from
+# the CGI::HtmlExtension module. These include individual methods for the
+# different types of form inputs, and methods for elements that commonly
+# take particular attributes where the attributes can be directly specified
+# as arguments, rather than via a hash.
+#
+# == Examples of use
+#
+# === Get form values
+#
+# require "cgi"
+# cgi = CGI.new
+# value = cgi['field_name'] # <== value string for '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.has_key?('field_name')
+# cgi.include?('field_name')
+#
+# CAUTION! cgi['field_name'] returned an Array with the old
+# cgi.rb(included in ruby 1.6)
+#
+# === 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
+# value = cgi['field_name'] # <== value string for 'field_name'
+# value.read # <== body of value
+# value.local_path # <== path to local file of value
+# value.original_filename # <== original filename of value
+# value.content_type # <== content_type of value
+#
+# and value has StringIO or Tempfile 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) {"string"}
+#
+# cgi.cookies # { "name1" => cookie1, "name2" => cookie2, ... }
+#
+# require "cgi"
+# cgi = CGI.new
+# cgi.cookies['name'].expires = Time.now + 30
+# cgi.out("cookie" => cgi.cookies['name']) {"string"}
+#
+# === Print http header and html string to $DEFAULT_OUTPUT ($>)
+#
+# require "cgi"
+# cgi = CGI.new("html3") # add HTML generation methods
+# cgi.out() do
+# cgi.html() do
+# cgi.head{ cgi.title{"TITLE"} } +
+# cgi.body() do
+# cgi.form() do
+# cgi.textarea("get_text") +
+# cgi.br +
+# cgi.submit
+# end +
+# cgi.pre() do
+# CGI::escapeHTML(
+# "params: " + cgi.params.inspect + "\n" +
+# "cookies: " + cgi.cookies.inspect + "\n" +
+# ENV.collect() do |key, value|
+# key + " --> " + value + "\n"
+# end.join("")
+# )
+# end
+# end
+# end
+# end
+#
+# # add HTML generation methods
+# CGI.new("html3") # html3.2
+# CGI.new("html4") # html4.01 (Strict)
+# CGI.new("html4Tr") # html4.01 Transitional
+# CGI.new("html4Fr") # html4.01 Frameset
+#
+class CGI
+
+ # :stopdoc:
+
+ # String for carriage return
+ CR = "\015"
+
+ # String for linefeed
+ LF = "\012"
+
+ # Standard internet newline sequence
+ EOL = CR + LF
+
+ REVISION = '$Id: cgi.rb,v 1.68.2.16.2.2 2006/12/03 08:06:27 shugo Exp $' #:nodoc:
+
+ NEEDS_BINMODE = true if /WIN/ni.match(RUBY_PLATFORM)
+
+ # Path separators in different environments.
+ PATH_SEPARATOR = {'UNIX'=>'/', 'WINDOWS'=>'\\', 'MACINTOSH'=>':'}
+
+ # HTTP status codes.
+ 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"
+ }
+
+ # Abbreviated day-of-week names specified by RFC 822
+ RFC822_DAYS = %w[ Sun Mon Tue Wed Thu Fri Sat ]
+
+ # Abbreviated month names specified by RFC 822
+ RFC822_MONTHS = %w[ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ]
+
+ # :startdoc:
+
+ def env_table
+ ENV
+ end
+
+ def stdinput
+ $stdin
+ end
+
+ def stdoutput
+ $DEFAULT_OUTPUT
+ end
+
+ private :env_table, :stdinput, :stdoutput
+
+ # URL-encode a string.
+ # url_encoded_string = CGI::escape("'Stop!' said Fred")
+ # # => "%27Stop%21%27+said+Fred"
+ def CGI::escape(string)
+ string.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
+ '%' + $1.unpack('H2' * $1.size).join('%').upcase
+ end.tr(' ', '+')
+ end
+
+
+ # URL-decode a string.
+ # string = CGI::unescape("%27Stop%21%27+said+Fred")
+ # # => "'Stop!' said Fred"
+ def CGI::unescape(string)
+ string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n) do
+ [$1.delete('%')].pack('H*')
+ end
+ end
+
+
+ # Escape special characters in HTML, namely &\"<>
+ # CGI::escapeHTML('Usage: foo "bar" <baz>')
+ # # => "Usage: foo &quot;bar&quot; &lt;baz&gt;"
+ def CGI::escapeHTML(string)
+ string.gsub(/&/n, '&amp;').gsub(/\"/n, '&quot;').gsub(/>/n, '&gt;').gsub(/</n, '&lt;')
+ end
+
+
+ # Unescape a string that has been HTML-escaped
+ # CGI::unescapeHTML("Usage: foo &quot;bar&quot; &lt;baz&gt;")
+ # # => "Usage: foo \"bar\" <baz>"
+ def CGI::unescapeHTML(string)
+ string.gsub(/&(.*?);/n) do
+ match = $1.dup
+ case match
+ when /\Aamp\z/ni then '&'
+ when /\Aquot\z/ni then '"'
+ when /\Agt\z/ni then '>'
+ when /\Alt\z/ni then '<'
+ when /\A#0*(\d+)\z/n then
+ if Integer($1) < 256
+ Integer($1).chr
+ else
+ if Integer($1) < 65536 and ($KCODE[0] == ?u or $KCODE[0] == ?U)
+ [Integer($1)].pack("U")
+ else
+ "&##{$1};"
+ end
+ end
+ when /\A#x([0-9a-f]+)\z/ni then
+ if $1.hex < 256
+ $1.hex.chr
+ else
+ if $1.hex < 65536 and ($KCODE[0] == ?u or $KCODE[0] == ?U)
+ [$1.hex].pack("U")
+ else
+ "&#x#{$1};"
+ end
+ end
+ else
+ "&#{match};"
+ end
+ end
+ end
+
+
+ # Escape only the tags of certain HTML elements in +string+.
+ #
+ # Takes an element or elements or array of elements. Each element
+ # is specified by the name of the element, without angle brackets.
+ # This matches both the start and the end tag of that element.
+ # The attribute list of the open tag will also be escaped (for
+ # instance, the double-quotes surrounding attribute values).
+ #
+ # print CGI::escapeElement('<BR><A HREF="url"></A>', "A", "IMG")
+ # # "<BR>&lt;A HREF=&quot;url&quot;&gt;&lt;/A&gt"
+ #
+ # print CGI::escapeElement('<BR><A HREF="url"></A>', ["A", "IMG"])
+ # # "<BR>&lt;A HREF=&quot;url&quot;&gt;&lt;/A&gt"
+ def CGI::escapeElement(string, *elements)
+ elements = elements[0] if elements[0].kind_of?(Array)
+ unless elements.empty?
+ string.gsub(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/ni) do
+ CGI::escapeHTML($&)
+ end
+ else
+ string
+ end
+ end
+
+
+ # Undo escaping such as that done by CGI::escapeElement()
+ #
+ # print CGI::unescapeElement(
+ # CGI::escapeHTML('<BR><A HREF="url"></A>'), "A", "IMG")
+ # # "&lt;BR&gt;<A HREF="url"></A>"
+ #
+ # print CGI::unescapeElement(
+ # CGI::escapeHTML('<BR><A HREF="url"></A>'), ["A", "IMG"])
+ # # "&lt;BR&gt;<A HREF="url"></A>"
+ def CGI::unescapeElement(string, *elements)
+ elements = elements[0] if elements[0].kind_of?(Array)
+ unless elements.empty?
+ string.gsub(/&lt;\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?&gt;/ni) do
+ CGI::unescapeHTML($&)
+ end
+ else
+ string
+ end
+ end
+
+
+ # Format a +Time+ object as a String using the format specified by RFC 1123.
+ #
+ # CGI::rfc1123_date(Time.now)
+ # # Sat, 01 Jan 2000 00:00:00 GMT
+ def CGI::rfc1123_date(time)
+ t = time.clone.gmtime
+ return format("%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
+ RFC822_DAYS[t.wday], t.day, RFC822_MONTHS[t.month-1], t.year,
+ t.hour, t.min, t.sec)
+ end
+
+
+ # Create an HTTP header block as a string.
+ #
+ # Includes the empty line that ends the header block.
+ #
+ # +options+ can be a string specifying the Content-Type (defaults
+ # to text/html), or a hash of header key/value pairs. The following
+ # header keys are recognized:
+ #
+ # type:: the Content-Type header. Defaults to "text/html"
+ # charset:: the charset of the body, appended to the Content-Type header.
+ # nph:: a boolean value. If true, prepend protocol string and status code, and
+ # date; and sets default values for "server" and "connection" if not
+ # explicitly set.
+ # status:: the HTTP status code, returned as the Status header. See the
+ # list of available status codes below.
+ # server:: the server software, returned as the Server header.
+ # connection:: the connection type, returned as the Connection header (for
+ # instance, "close".
+ # length:: the length of the content that will be sent, returned as the
+ # Content-Length header.
+ # language:: the language of the content, returned as the Content-Language
+ # header.
+ # expires:: the time on which the current content expires, as a +Time+
+ # object, returned as the Expires header.
+ # cookie:: a cookie or cookies, returned as one or more Set-Cookie headers.
+ # The value can be the literal string of the cookie; a CGI::Cookie
+ # object; an Array of literal cookie strings or Cookie objects; or a
+ # hash all of whose values are literal cookie strings or Cookie objects.
+ # These cookies are in addition to the cookies held in the
+ # @output_cookies field.
+ #
+ # Other header lines can also be set; they are appended as key: value.
+ #
+ # 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
+ # "length" => 103,
+ # "language" => "ja",
+ # "expires" => Time.now + 30,
+ # "cookie" => [cookie1, cookie2],
+ # "my_header1" => "my_value"
+ # "my_header2" => "my_value")
+ #
+ # The status codes are:
+ #
+ # "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 Precondition 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"
+ #
+ # This method does not perform charset conversion.
+ #
+ def header(options = "text/html")
+
+ buf = ""
+
+ case options
+ when String
+ options = { "type" => options }
+ when Hash
+ options = options.dup
+ end
+
+ unless options.has_key?("type")
+ options["type"] = "text/html"
+ end
+
+ if options.has_key?("charset")
+ options["type"] += "; charset=" + options.delete("charset")
+ end
+
+ options.delete("nph") if defined?(MOD_RUBY)
+ if options.delete("nph") or
+ (/IIS\/(\d+)/n.match(env_table['SERVER_SOFTWARE']) and $1.to_i < 5)
+ buf += (env_table["SERVER_PROTOCOL"] or "HTTP/1.0") + " " +
+ (HTTP_STATUS[options["status"]] or options["status"] or "200 OK") +
+ EOL +
+ "Date: " + CGI::rfc1123_date(Time.now) + EOL
+
+ unless options.has_key?("server")
+ options["server"] = (env_table['SERVER_SOFTWARE'] or "")
+ end
+
+ unless options.has_key?("connection")
+ options["connection"] = "close"
+ end
+
+ options.delete("status")
+ end
+
+ if options.has_key?("status")
+ buf += "Status: " +
+ (HTTP_STATUS[options["status"]] or options["status"]) + EOL
+ options.delete("status")
+ end
+
+ if options.has_key?("server")
+ buf += "Server: " + options.delete("server") + EOL
+ end
+
+ if options.has_key?("connection")
+ buf += "Connection: " + options.delete("connection") + EOL
+ end
+
+ buf += "Content-Type: " + options.delete("type") + EOL
+
+ if options.has_key?("length")
+ buf += "Content-Length: " + options.delete("length").to_s + EOL
+ end
+
+ if options.has_key?("language")
+ buf += "Content-Language: " + options.delete("language") + EOL
+ end
+
+ if options.has_key?("expires")
+ buf += "Expires: " + CGI::rfc1123_date( options.delete("expires") ) + EOL
+ end
+
+ if options.has_key?("cookie")
+ if options["cookie"].kind_of?(String) or
+ options["cookie"].kind_of?(Cookie)
+ buf += "Set-Cookie: " + options.delete("cookie").to_s + EOL
+ elsif options["cookie"].kind_of?(Array)
+ options.delete("cookie").each{|cookie|
+ buf += "Set-Cookie: " + cookie.to_s + EOL
+ }
+ elsif options["cookie"].kind_of?(Hash)
+ options.delete("cookie").each_value{|cookie|
+ buf += "Set-Cookie: " + cookie.to_s + EOL
+ }
+ end
+ end
+ if @output_cookies
+ for cookie in @output_cookies
+ buf += "Set-Cookie: " + cookie.to_s + EOL
+ end
+ end
+
+ options.each{|key, value|
+ buf += key + ": " + value.to_s + EOL
+ }
+
+ if defined?(MOD_RUBY)
+ table = Apache::request.headers_out
+ buf.scan(/([^:]+): (.+)#{EOL}/n){ |name, value|
+ warn sprintf("name:%s value:%s\n", name, value) if $DEBUG
+ case name
+ when 'Set-Cookie'
+ table.add(name, value)
+ when /^status$/ni
+ Apache::request.status_line = value
+ Apache::request.status = value.to_i
+ when /^content-type$/ni
+ Apache::request.content_type = value
+ when /^content-encoding$/ni
+ Apache::request.content_encoding = value
+ when /^location$/ni
+ if Apache::request.status == 200
+ Apache::request.status = 302
+ end
+ Apache::request.headers_out[name] = value
+ else
+ Apache::request.headers_out[name] = value
+ end
+ }
+ Apache::request.send_http_header
+ ''
+ else
+ buf + EOL
+ end
+
+ end # header()
+
+
+ # Print an HTTP header and body to $DEFAULT_OUTPUT ($>)
+ #
+ # The header is provided by +options+, as for #header().
+ # The body of the document is that returned by the passed-
+ # in block. This block takes no arguments. It is required.
+ #
+ # 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" }
+ #
+ # Content-Length is automatically calculated from the size of
+ # the String returned by the content block.
+ #
+ # If ENV['REQUEST_METHOD'] == "HEAD", then only the header
+ # is outputted (the content block is still required, but it
+ # is ignored).
+ #
+ # If the charset is "iso-2022-jp" or "euc-jp" or "shift_jis" then
+ # the content is converted to this charset, and the language is set
+ # to "ja".
+ def out(options = "text/html") # :yield:
+
+ options = { "type" => options } if options.kind_of?(String)
+ content = yield
+
+ if options.has_key?("charset")
+ require "nkf"
+ case options["charset"]
+ when /iso-2022-jp/ni
+ content = NKF::nkf('-j', content)
+ options["language"] = "ja" unless options.has_key?("language")
+ when /euc-jp/ni
+ content = NKF::nkf('-e', content)
+ options["language"] = "ja" unless options.has_key?("language")
+ when /shift_jis/ni
+ content = NKF::nkf('-s', content)
+ options["language"] = "ja" unless options.has_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 unless "HEAD" == env_table['REQUEST_METHOD']
+ end
+
+
+ # Print an argument or list of arguments to the default output stream
+ #
+ # cgi = CGI.new
+ # cgi.print # default: cgi.print == $DEFAULT_OUTPUT.print
+ def print(*options)
+ stdoutput.print(*options)
+ end
+
+ require "delegate"
+
+ # Class representing an HTTP cookie.
+ #
+ # In addition to its specific fields and methods, a Cookie instance
+ # is a delegator to the array of its values.
+ #
+ # See RFC 2965.
+ #
+ # == Examples of use
+ # 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
+ class Cookie < DelegateClass(Array)
+
+ # Create a new CGI::Cookie object.
+ #
+ # The contents of the cookie can be specified as a +name+ and one
+ # or more +value+ arguments. Alternatively, the contents can
+ # be specified as a single hash argument. The possible keywords of
+ # this hash are as follows:
+ #
+ # name:: the name of the cookie. Required.
+ # value:: the cookie's value or list of values.
+ # path:: the path for which this cookie applies. Defaults to the
+ # base directory of the CGI script.
+ # domain:: the domain for which this cookie applies.
+ # expires:: the time at which this cookie expires, as a +Time+ object.
+ # secure:: whether this cookie is a secure cookie or not (default to
+ # false). Secure cookies are only transmitted to HTTPS
+ # servers.
+ #
+ # These keywords correspond to attributes of the cookie object.
+ def initialize(name = "", *value)
+ options = if name.kind_of?(String)
+ { "name" => name, "value" => value }
+ else
+ name
+ end
+ unless options.has_key?("name")
+ raise ArgumentError, "`name' required"
+ end
+
+ @name = options["name"]
+ @value = Array(options["value"])
+ # simple support for IE
+ if options["path"]
+ @path = options["path"]
+ else
+ %r|^(.*/)|.match(ENV["SCRIPT_NAME"])
+ @path = ($1 or "")
+ end
+ @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")
+
+ # Set whether the Cookie is a secure cookie or not.
+ #
+ # +val+ must be a boolean.
+ def secure=(val)
+ @secure = val if val == true or val == false
+ @secure
+ end
+
+ # Convert the Cookie to its string representation.
+ def to_s
+ buf = ""
+ buf += @name + '='
+
+ if @value.kind_of?(String)
+ buf += CGI::escape(@value)
+ else
+ buf += @value.collect{|v| CGI::escape(v) }.join("&")
+ end
+
+ if @domain
+ buf += '; domain=' + @domain
+ end
+
+ if @path
+ buf += '; path=' + @path
+ end
+
+ if @expires
+ buf += '; expires=' + CGI::rfc1123_date(@expires)
+ end
+
+ if @secure == true
+ buf += '; secure'
+ end
+
+ buf
+ end
+
+ end # class Cookie
+
+
+ # Parse a raw cookie string into a hash of cookie-name=>Cookie
+ # pairs.
+ #
+ # cookies = CGI::Cookie::parse("raw_cookie_string")
+ # # { "name1" => cookie1, "name2" => cookie2, ... }
+ #
+ def Cookie::parse(raw_cookie)
+ cookies = Hash.new([])
+ return cookies unless raw_cookie
+
+ raw_cookie.split(/[;,]\s?/).each do |pairs|
+ name, values = pairs.split('=',2)
+ next unless name and values
+ name = CGI::unescape(name)
+ values ||= ""
+ values = values.split('&').collect{|v| CGI::unescape(v) }
+ if cookies.has_key?(name)
+ values = cookies[name].value + values
+ end
+ cookies[name] = Cookie::new({ "name" => name, "value" => values })
+ end
+
+ cookies
+ end
+
+ # Parse an HTTP query string into a hash of key=>value pairs.
+ #
+ # params = CGI::parse("query_string")
+ # # {"name1" => ["value1", "value2", ...],
+ # # "name2" => ["value1", "value2", ...], ... }
+ #
+ def CGI::parse(query)
+ params = Hash.new([].freeze)
+
+ query.split(/[&;]/n).each do |pairs|
+ key, value = pairs.split('=',2).collect{|v| CGI::unescape(v) }
+ if params.has_key?(key)
+ params[key].push(value)
+ else
+ params[key] = [value]
+ end
+ end
+
+ params
+ end
+
+ # Mixin module. It provides the follow functionality groups:
+ #
+ # 1. Access to CGI environment variables as methods. See
+ # documentation to the CGI class for a list of these variables.
+ #
+ # 2. Access to cookies, including the cookies attribute.
+ #
+ # 3. Access to parameters, including the params attribute, and overloading
+ # [] to perform parameter value lookup by key.
+ #
+ # 4. The initialize_query method, for initialising the above
+ # mechanisms, handling multipart forms, and allowing the
+ # class to be used in "offline" mode.
+ #
+ module QueryExtension
+
+ %w[ CONTENT_LENGTH SERVER_PORT ].each do |env|
+ define_method(env.sub(/^HTTP_/n, '').downcase) do
+ (val = env_table[env]) && Integer(val)
+ 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 do |env|
+ define_method(env.sub(/^HTTP_/n, '').downcase) do
+ env_table[env]
+ end
+ end
+
+ # Get the raw cookies as a string.
+ def raw_cookie
+ env_table["HTTP_COOKIE"]
+ end
+
+ # Get the raw RFC2965 cookies as a string.
+ def raw_cookie2
+ env_table["HTTP_COOKIE2"]
+ end
+
+ # Get the cookies as a hash of cookie-name=>Cookie pairs.
+ attr_accessor("cookies")
+
+ # Get the parameters as a hash of name=>values pairs, where
+ # values is an Array.
+ attr("params")
+
+ # Set all the parameters.
+ def params=(hash)
+ @params.clear
+ @params.update(hash)
+ end
+
+ def read_multipart(boundary, content_length)
+ params = Hash.new([])
+ boundary = "--" + boundary
+ quoted_boundary = Regexp.quote(boundary, "n")
+ buf = ""
+ bufsize = 10 * 1024
+ boundary_end=""
+
+ # start multipart/form-data
+ stdinput.binmode if defined? stdinput.binmode
+ boundary_size = boundary.size + EOL.size
+ content_length -= boundary_size
+ status = stdinput.read(boundary_size)
+ if nil == status
+ raise EOFError, "no content body"
+ elsif boundary + EOL != status
+ raise EOFError, "bad content body"
+ end
+
+ loop do
+ head = nil
+ if 10240 < content_length
+ require "tempfile"
+ body = Tempfile.new("CGI")
+ else
+ begin
+ require "stringio"
+ body = StringIO.new
+ rescue LoadError
+ require "tempfile"
+ body = Tempfile.new("CGI")
+ end
+ end
+ body.binmode if defined? body.binmode
+
+ until head and /#{quoted_boundary}(?:#{EOL}|--)/n.match(buf)
+
+ if (not head) and /#{EOL}#{EOL}/n.match(buf)
+ buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do
+ head = $1.dup
+ ""
+ end
+ 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)
+ else
+ stdinput.read(content_length)
+ end
+ if c.nil? || c.empty?
+ raise EOFError, "bad content body"
+ end
+ buf.concat(c)
+ content_length -= c.size
+ end
+
+ buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do
+ body.print $1
+ if "--" == $2
+ content_length = -1
+ end
+ boundary_end = $2.dup
+ ""
+ end
+
+ body.rewind
+
+ /Content-Disposition:.* filename="?([^\";]*)"?/ni.match(head)
+ filename = ($1 or "")
+ if /Mac/ni.match(env_table['HTTP_USER_AGENT']) and
+ /Mozilla/ni.match(env_table['HTTP_USER_AGENT']) and
+ (not /MSIE/ni.match(env_table['HTTP_USER_AGENT']))
+ filename = CGI::unescape(filename)
+ end
+
+ /Content-Type: (.*)/ni.match(head)
+ content_type = ($1 or "")
+
+ (class << body; self; end).class_eval do
+ alias local_path path
+ define_method(:original_filename) {filename.dup.taint}
+ define_method(:content_type) {content_type.dup.taint}
+ end
+
+ /Content-Disposition:.* name="?([^\";]*)"?/ni.match(head)
+ name = $1.dup
+
+ if params.has_key?(name)
+ params[name].push(body)
+ else
+ params[name] = [body]
+ end
+ break if buf.size == 0
+ break if content_length === -1
+ end
+ raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/
+
+ params
+ end # read_multipart
+ private :read_multipart
+
+ # offline mode. read name=value pairs on standard input.
+ def read_from_cmdline
+ require "shellwords"
+
+ string = unless ARGV.empty?
+ ARGV.join(' ')
+ else
+ if STDIN.tty?
+ STDERR.print(
+ %|(offline mode: enter name=value pairs on standard input)\n|
+ )
+ end
+ readlines.join(' ').gsub(/\n/n, '')
+ end.gsub(/\\=/n, '%3D').gsub(/\\&/n, '%26')
+
+ words = Shellwords.shellwords(string)
+
+ if words.find{|x| /=/n.match(x) }
+ words.join('&')
+ else
+ words.join('+')
+ end
+ end
+ private :read_from_cmdline
+
+ # Initialize the data from the query.
+ #
+ # Handles multipart forms (in particular, forms that involve file uploads).
+ # Reads query parameters in the @params field, and cookies into @cookies.
+ def initialize_query()
+ if ("POST" == env_table['REQUEST_METHOD']) and
+ %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n.match(env_table['CONTENT_TYPE'])
+ boundary = $1.dup
+ @multipart = true
+ @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH']))
+ else
+ @multipart = false
+ @params = CGI::parse(
+ case env_table['REQUEST_METHOD']
+ when "GET", "HEAD"
+ if defined?(MOD_RUBY)
+ Apache::request.args or ""
+ else
+ env_table['QUERY_STRING'] or ""
+ end
+ when "POST"
+ stdinput.binmode if defined? 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']))
+ end
+ private :initialize_query
+
+ def multipart?
+ @multipart
+ end
+
+ module Value # :nodoc:
+ def set_params(params)
+ @params = params
+ end
+ def [](idx, *args)
+ if args.size == 0
+ warn "#{caller(1)[0]}:CAUTION! cgi['key'] == cgi.params['key'][0]; if want Array, use cgi.params['key']"
+ @params[idx]
+ else
+ super[idx,*args]
+ end
+ end
+ def first
+ warn "#{caller(1)[0]}:CAUTION! cgi['key'] == cgi.params['key'][0]; if want Array, use cgi.params['key']"
+ self
+ end
+ alias last first
+ def to_a
+ @params || [self]
+ end
+ alias to_ary to_a # to be rhs of multiple assignment
+ end
+
+ # Get the value for the parameter with a given key.
+ #
+ # If the parameter has multiple values, only the first will be
+ # retrieved; use #params() to get the array of values.
+ def [](key)
+ params = @params[key]
+ return '' unless params
+ value = params[0]
+ if @multipart
+ if value
+ return value
+ elsif defined? StringIO
+ StringIO.new("")
+ else
+ Tempfile.new("CGI")
+ end
+ else
+ str = if value then value.dup else "" end
+ str.extend(Value)
+ str.set_params(params)
+ str
+ end
+ end
+
+ # Return all parameter keys as an array.
+ def keys(*args)
+ @params.keys(*args)
+ end
+
+ # Returns true if a given parameter key exists in the query.
+ def has_key?(*args)
+ @params.has_key?(*args)
+ end
+ alias key? has_key?
+ alias include? has_key?
+
+ end # QueryExtension
+
+
+ # Prettify (indent) an HTML string.
+ #
+ # +string+ is the HTML string to indent. +shift+ is the indentation
+ # unit to use; it defaults to two spaces.
+ #
+ # print CGI::pretty("<HTML><BODY></BODY></HTML>")
+ # # <HTML>
+ # # <BODY>
+ # # </BODY>
+ # # </HTML>
+ #
+ # print CGI::pretty("<HTML><BODY></BODY></HTML>", "\t")
+ # # <HTML>
+ # # <BODY>
+ # # </BODY>
+ # # </HTML>
+ #
+ def CGI::pretty(string, shift = " ")
+ lines = string.gsub(/(?!\A)<(?:.|\n)*?>/n, "\n\\0").gsub(/<(?:.|\n)*?>(?!\n)/n, "\\0\n")
+ end_pos = 0
+ while end_pos = lines.index(/^<\/(\w+)/n, end_pos)
+ element = $1.dup
+ start_pos = lines.rindex(/^\s*<#{element}/ni, end_pos)
+ lines[start_pos ... end_pos] = "__" + lines[start_pos ... end_pos].gsub(/\n(?!\z)/n, "\n" + shift) + "__"
+ end
+ lines.gsub(/^((?:#{Regexp::quote(shift)})*)__(?=<\/?\w)/n, '\1')
+ end
+
+
+ # Base module for HTML-generation mixins.
+ #
+ # Provides methods for code generation for tags following
+ # the various DTD element types.
+ module TagMaker # :nodoc:
+
+ # Generate code for an element with required start and end tags.
+ #
+ # - -
+ def nn_element_def(element)
+ nOE_element_def(element, <<-END)
+ if block_given?
+ yield.to_s
+ else
+ ""
+ end +
+ "</#{element.upcase}>"
+ END
+ end
+
+ # Generate code for an empty element.
+ #
+ # - O EMPTY
+ def nOE_element_def(element, append = nil)
+ s = <<-END
+ "<#{element.upcase}" + attributes.collect{|name, value|
+ next unless value
+ " " + CGI::escapeHTML(name) +
+ if true == value
+ ""
+ else
+ '="' + CGI::escapeHTML(value) + '"'
+ end
+ }.to_s + ">"
+ END
+ s.sub!(/\Z/, " +") << append if append
+ s
+ end
+
+ # Generate code for an element for which the end (and possibly the
+ # start) tag is optional.
+ #
+ # O O or - O
+ def nO_element_def(element)
+ nOE_element_def(element, <<-END)
+ if block_given?
+ yield.to_s + "</#{element.upcase}>"
+ else
+ ""
+ end
+ END
+ end
+
+ end # TagMaker
+
+
+ #
+ # Mixin module providing HTML generation methods.
+ #
+ # For example,
+ # cgi.a("http://www.example.com") { "Example" }
+ # # => "<A HREF=\"http://www.example.com\">Example</A>"
+ #
+ # Modules Http3, Http4, etc., contain more basic HTML-generation methods
+ # (:title, :center, etc.).
+ #
+ # See class CGI for a detailed example.
+ #
+ module HtmlExtension
+
+
+ # Generate an Anchor element as a string.
+ #
+ # +href+ can either be a string, giving the URL
+ # for the HREF attribute, or it can be a hash of
+ # the element's attributes.
+ #
+ # The body of the element is the string returned by the no-argument
+ # block passed in.
+ #
+ # a("http://www.example.com") { "Example" }
+ # # => "<A HREF=\"http://www.example.com\">Example</A>"
+ #
+ # a("HREF" => "http://www.example.com", "TARGET" => "_top") { "Example" }
+ # # => "<A HREF=\"http://www.example.com\" TARGET=\"_top\">Example</A>"
+ #
+ def a(href = "") # :yield:
+ attributes = if href.kind_of?(String)
+ { "HREF" => href }
+ else
+ href
+ end
+ if block_given?
+ super(attributes){ yield }
+ else
+ super(attributes)
+ end
+ end
+
+ # Generate a Document Base URI element as a String.
+ #
+ # +href+ can either by a string, giving the base URL for the HREF
+ # attribute, or it can be a has of the element's attributes.
+ #
+ # The passed-in no-argument block is ignored.
+ #
+ # base("http://www.example.com/cgi")
+ # # => "<BASE HREF=\"http://www.example.com/cgi\">"
+ def base(href = "") # :yield:
+ attributes = if href.kind_of?(String)
+ { "HREF" => href }
+ else
+ href
+ end
+ if block_given?
+ super(attributes){ yield }
+ else
+ super(attributes)
+ end
+ end
+
+ # Generate a BlockQuote element as a string.
+ #
+ # +cite+ can either be a string, give the URI for the source of
+ # the quoted text, or a hash, giving all attributes of the element,
+ # or it can be omitted, in which case the element has no attributes.
+ #
+ # The body is provided by the passed-in no-argument block
+ #
+ # blockquote("http://www.example.com/quotes/foo.html") { "Foo!" }
+ # #=> "<BLOCKQUOTE CITE=\"http://www.example.com/quotes/foo.html\">Foo!</BLOCKQUOTE>
+ def blockquote(cite = nil) # :yield:
+ attributes = if cite.kind_of?(String)
+ { "CITE" => cite }
+ else
+ cite or ""
+ end
+ if block_given?
+ super(attributes){ yield }
+ else
+ super(attributes)
+ end
+ end
+
+
+ # Generate a Table Caption element as a string.
+ #
+ # +align+ can be a string, giving the alignment of the caption
+ # (one of top, bottom, left, or right). It can be a hash of
+ # all the attributes of the element. Or it can be omitted.
+ #
+ # The body of the element is provided by the passed-in no-argument block.
+ #
+ # caption("left") { "Capital Cities" }
+ # # => <CAPTION ALIGN=\"left\">Capital Cities</CAPTION>
+ def caption(align = nil) # :yield:
+ attributes = if align.kind_of?(String)
+ { "ALIGN" => align }
+ else
+ align or ""
+ end
+ if block_given?
+ super(attributes){ yield }
+ else
+ super(attributes)
+ end
+ end
+
+
+ # Generate a Checkbox Input element as a string.
+ #
+ # The attributes of the element can be specified as three arguments,
+ # +name+, +value+, and +checked+. +checked+ is a boolean value;
+ # if true, the CHECKED attribute will be included in the element.
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # checkbox("name")
+ # # = checkbox("NAME" => "name")
+ #
+ # checkbox("name", "value")
+ # # = checkbox("NAME" => "name", "VALUE" => "value")
+ #
+ # checkbox("name", "value", true)
+ # # = checkbox("NAME" => "name", "VALUE" => "value", "CHECKED" => true)
+ 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
+
+ # Generate a sequence of checkbox elements, as a String.
+ #
+ # The checkboxes will all have the same +name+ attribute.
+ # Each checkbox is followed by a label.
+ # There will be one checkbox for each value. Each value
+ # can be specified as a String, which will be used both
+ # as the value of the VALUE attribute and as the label
+ # for that checkbox. A single-element array has the
+ # same effect.
+ #
+ # Each value can also be specified as a three-element array.
+ # The first element is the VALUE attribute; the second is the
+ # label; and the third is a boolean specifying whether this
+ # checkbox is CHECKED.
+ #
+ # Each value can also be specified as a two-element
+ # array, by omitting either the value element (defaults
+ # to the same as the label), or the boolean checked element
+ # (defaults to false).
+ #
+ # checkbox_group("name", "foo", "bar", "baz")
+ # # <INPUT TYPE="checkbox" NAME="name" VALUE="foo">foo
+ # # <INPUT TYPE="checkbox" NAME="name" VALUE="bar">bar
+ # # <INPUT TYPE="checkbox" NAME="name" VALUE="baz">baz
+ #
+ # checkbox_group("name", ["foo"], ["bar", true], "baz")
+ # # <INPUT TYPE="checkbox" NAME="name" VALUE="foo">foo
+ # # <INPUT TYPE="checkbox" CHECKED NAME="name" VALUE="bar">bar
+ # # <INPUT TYPE="checkbox" NAME="name" VALUE="baz">baz
+ #
+ # checkbox_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
+ # # <INPUT TYPE="checkbox" NAME="name" VALUE="1">Foo
+ # # <INPUT TYPE="checkbox" CHECKED NAME="name" VALUE="2">Bar
+ # # <INPUT TYPE="checkbox" NAME="name" VALUE="Baz">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"])
+ 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
+
+
+ # Generate an File Upload Input element as a string.
+ #
+ # The attributes of the element can be specified as three arguments,
+ # +name+, +size+, and +maxlength+. +maxlength+ is the maximum length
+ # of the file's _name_, not of the file's _contents_.
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # See #multipart_form() for forms that include file uploads.
+ #
+ # file_field("name")
+ # # <INPUT TYPE="file" NAME="name" SIZE="20">
+ #
+ # file_field("name", 40)
+ # # <INPUT TYPE="file" NAME="name" SIZE="40">
+ #
+ # file_field("name", 40, 100)
+ # # <INPUT TYPE="file" NAME="name" SIZE="40" MAXLENGTH="100">
+ #
+ # file_field("NAME" => "name", "SIZE" => 40)
+ # # <INPUT TYPE="file" NAME="name" SIZE="40">
+ 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
+
+
+ # Generate a Form element as a string.
+ #
+ # +method+ should be either "get" or "post", and defaults to the latter.
+ # +action+ defaults to the current CGI script name. +enctype+
+ # defaults to "application/x-www-form-urlencoded".
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # See also #multipart_form() for forms that include file uploads.
+ #
+ # form{ "string" }
+ # # <FORM METHOD="post" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
+ #
+ # form("get") { "string" }
+ # # <FORM METHOD="get" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
+ #
+ # form("get", "url") { "string" }
+ # # <FORM METHOD="get" ACTION="url" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
+ #
+ # form("METHOD" => "post", "ENCTYPE" => "enctype") { "string" }
+ # # <FORM METHOD="post" ENCTYPE="enctype">string</FORM>
+ def form(method = "post", action = script_name, enctype = "application/x-www-form-urlencoded")
+ attributes = if method.kind_of?(String)
+ { "METHOD" => method, "ACTION" => action,
+ "ENCTYPE" => enctype }
+ else
+ unless method.has_key?("METHOD")
+ method["METHOD"] = "post"
+ end
+ unless method.has_key?("ENCTYPE")
+ method["ENCTYPE"] = enctype
+ end
+ method
+ end
+ if block_given?
+ body = yield
+ else
+ body = ""
+ end
+ if @output_hidden
+ body += @output_hidden.collect{|k,v|
+ "<INPUT TYPE=\"HIDDEN\" NAME=\"#{k}\" VALUE=\"#{v}\">"
+ }.to_s
+ end
+ super(attributes){body}
+ end
+
+ # Generate a Hidden Input element as a string.
+ #
+ # The attributes of the element can be specified as two arguments,
+ # +name+ and +value+.
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # hidden("name")
+ # # <INPUT TYPE="hidden" NAME="name">
+ #
+ # hidden("name", "value")
+ # # <INPUT TYPE="hidden" NAME="name" VALUE="value">
+ #
+ # hidden("NAME" => "name", "VALUE" => "reset", "ID" => "foo")
+ # # <INPUT TYPE="hidden" NAME="name" VALUE="value" ID="foo">
+ 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
+
+ # Generate a top-level HTML element as a string.
+ #
+ # The attributes of the element are specified as a hash. The
+ # pseudo-attribute "PRETTY" can be used to specify that the generated
+ # HTML string should be indented. "PRETTY" can also be specified as
+ # a string as the sole argument to this method. The pseudo-attribute
+ # "DOCTYPE", if given, is used as the leading DOCTYPE SGML tag; it
+ # should include the entire text of this tag, including angle brackets.
+ #
+ # The body of the html element is supplied as a block.
+ #
+ # html{ "string" }
+ # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML>string</HTML>
+ #
+ # html("LANG" => "ja") { "string" }
+ # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML LANG="ja">string</HTML>
+ #
+ # html("DOCTYPE" => false) { "string" }
+ # # <HTML>string</HTML>
+ #
+ # html("DOCTYPE" => '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">') { "string" }
+ # # <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"><HTML>string</HTML>
+ #
+ # html("PRETTY" => " ") { "<BODY></BODY>" }
+ # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+ # # <HTML>
+ # # <BODY>
+ # # </BODY>
+ # # </HTML>
+ #
+ # html("PRETTY" => "\t") { "<BODY></BODY>" }
+ # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+ # # <HTML>
+ # # <BODY>
+ # # </BODY>
+ # # </HTML>
+ #
+ # html("PRETTY") { "<BODY></BODY>" }
+ # # = html("PRETTY" => " ") { "<BODY></BODY>" }
+ #
+ # html(if $VERBOSE then "PRETTY" end) { "HTML string" }
+ #
+ def html(attributes = {}) # :yield:
+ if nil == attributes
+ attributes = {}
+ elsif "PRETTY" == attributes
+ attributes = { "PRETTY" => true }
+ end
+ pretty = attributes.delete("PRETTY")
+ pretty = " " if true == pretty
+ buf = ""
+
+ if attributes.has_key?("DOCTYPE")
+ if attributes["DOCTYPE"]
+ buf += attributes.delete("DOCTYPE")
+ else
+ attributes.delete("DOCTYPE")
+ end
+ else
+ buf += doctype
+ end
+
+ if block_given?
+ buf += super(attributes){ yield }
+ else
+ buf += super(attributes)
+ end
+
+ if pretty
+ CGI::pretty(buf, pretty)
+ else
+ buf
+ end
+
+ end
+
+ # Generate an Image Button Input element as a string.
+ #
+ # +src+ is the URL of the image to use for the button. +name+
+ # is the input name. +alt+ is the alternative text for the image.
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # image_button("url")
+ # # <INPUT TYPE="image" SRC="url">
+ #
+ # image_button("url", "name", "string")
+ # # <INPUT TYPE="image" SRC="url" NAME="name" ALT="string">
+ #
+ # image_button("SRC" => "url", "ATL" => "strng")
+ # # <INPUT TYPE="image" SRC="url" ALT="string">
+ def image_button(src = "", name = nil, alt = nil)
+ attributes = if src.kind_of?(String)
+ { "TYPE" => "image", "SRC" => src, "NAME" => name,
+ "ALT" => alt }
+ else
+ src["TYPE"] = "image"
+ src["SRC"] ||= ""
+ src
+ end
+ input(attributes)
+ end
+
+
+ # Generate an Image element as a string.
+ #
+ # +src+ is the URL of the image. +alt+ is the alternative text for
+ # the image. +width+ is the width of the image, and +height+ is
+ # its height.
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # img("src", "alt", 100, 50)
+ # # <IMG SRC="src" ALT="alt" WIDTH="100" HEIGHT="50">
+ #
+ # img("SRC" => "src", "ALT" => "alt", "WIDTH" => 100, "HEIGHT" => 50)
+ # # <IMG SRC="src" ALT="alt" WIDTH="100" HEIGHT="50">
+ 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
+
+
+ # Generate a Form element with multipart encoding as a String.
+ #
+ # Multipart encoding is used for forms that include file uploads.
+ #
+ # +action+ is the action to perform. +enctype+ is the encoding
+ # type, which defaults to "multipart/form-data".
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # multipart_form{ "string" }
+ # # <FORM METHOD="post" ENCTYPE="multipart/form-data">string</FORM>
+ #
+ # multipart_form("url") { "string" }
+ # # <FORM METHOD="post" ACTION="url" ENCTYPE="multipart/form-data">string</FORM>
+ 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.has_key?("METHOD")
+ action["METHOD"] = "post"
+ end
+ unless action.has_key?("ENCTYPE")
+ action["ENCTYPE"] = enctype
+ end
+ action
+ end
+ if block_given?
+ form(attributes){ yield }
+ else
+ form(attributes)
+ end
+ end
+
+
+ # Generate a Password Input element as a string.
+ #
+ # +name+ is the name of the input field. +value+ is its default
+ # value. +size+ is the size of the input field display. +maxlength+
+ # is the maximum length of the inputted password.
+ #
+ # Alternatively, attributes can be specified as a hash.
+ #
+ # password_field("name")
+ # # <INPUT TYPE="password" NAME="name" SIZE="40">
+ #
+ # password_field("name", "value")
+ # # <INPUT TYPE="password" NAME="name" VALUE="value" SIZE="40">
+ #
+ # password_field("password", "value", 80, 200)
+ # # <INPUT TYPE="password" NAME="name" VALUE="value" SIZE="80" MAXLENGTH="200">
+ #
+ # password_field("NAME" => "name", "VALUE" => "value")
+ # # <INPUT TYPE="password" NAME="name" VALUE="value">
+ 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
+
+ # Generate a Select element as a string.
+ #
+ # +name+ is the name of the element. The +values+ are the options that
+ # can be selected from the Select menu. Each value can be a String or
+ # a one, two, or three-element Array. If a String or a one-element
+ # Array, this is both the value of that option and the text displayed for
+ # it. If a three-element Array, the elements are the option value, displayed
+ # text, and a boolean value specifying whether this option starts as selected.
+ # The two-element version omits either the option value (defaults to the same
+ # as the display text) or the boolean selected specifier (defaults to false).
+ #
+ # The attributes and options can also be specified as a hash. In this
+ # case, options are specified as an array of values as described above,
+ # with the hash key of "VALUES".
+ #
+ # popup_menu("name", "foo", "bar", "baz")
+ # # <SELECT NAME="name">
+ # # <OPTION VALUE="foo">foo</OPTION>
+ # # <OPTION VALUE="bar">bar</OPTION>
+ # # <OPTION VALUE="baz">baz</OPTION>
+ # # </SELECT>
+ #
+ # popup_menu("name", ["foo"], ["bar", true], "baz")
+ # # <SELECT NAME="name">
+ # # <OPTION VALUE="foo">foo</OPTION>
+ # # <OPTION VALUE="bar" SELECTED>bar</OPTION>
+ # # <OPTION VALUE="baz">baz</OPTION>
+ # # </SELECT>
+ #
+ # popup_menu("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
+ # # <SELECT NAME="name">
+ # # <OPTION VALUE="1">Foo</OPTION>
+ # # <OPTION SELECTED VALUE="2">Bar</OPTION>
+ # # <OPTION VALUE="Baz">Baz</OPTION>
+ # # </SELECT>
+ #
+ # popup_menu("NAME" => "name", "SIZE" => 2, "MULTIPLE" => true,
+ # "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"])
+ # # <SELECT NAME="name" MULTIPLE SIZE="2">
+ # # <OPTION VALUE="1">Foo</OPTION>
+ # # <OPTION SELECTED VALUE="2">Bar</OPTION>
+ # # <OPTION VALUE="Baz">Baz</OPTION>
+ # # </SELECT>
+ 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
+
+ # Generates a radio-button Input element.
+ #
+ # +name+ is the name of the input field. +value+ is the value of
+ # the field if checked. +checked+ specifies whether the field
+ # starts off checked.
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # radio_button("name", "value")
+ # # <INPUT TYPE="radio" NAME="name" VALUE="value">
+ #
+ # radio_button("name", "value", true)
+ # # <INPUT TYPE="radio" NAME="name" VALUE="value" CHECKED>
+ #
+ # radio_button("NAME" => "name", "VALUE" => "value", "ID" => "foo")
+ # # <INPUT TYPE="radio" NAME="name" VALUE="value" ID="foo">
+ 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
+
+ # Generate a sequence of radio button Input elements, as a String.
+ #
+ # This works the same as #checkbox_group(). However, it is not valid
+ # to have more than one radiobutton in a group checked.
+ #
+ # radio_group("name", "foo", "bar", "baz")
+ # # <INPUT TYPE="radio" NAME="name" VALUE="foo">foo
+ # # <INPUT TYPE="radio" NAME="name" VALUE="bar">bar
+ # # <INPUT TYPE="radio" NAME="name" VALUE="baz">baz
+ #
+ # radio_group("name", ["foo"], ["bar", true], "baz")
+ # # <INPUT TYPE="radio" NAME="name" VALUE="foo">foo
+ # # <INPUT TYPE="radio" CHECKED NAME="name" VALUE="bar">bar
+ # # <INPUT TYPE="radio" NAME="name" VALUE="baz">baz
+ #
+ # radio_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
+ # # <INPUT TYPE="radio" NAME="name" VALUE="1">Foo
+ # # <INPUT TYPE="radio" CHECKED NAME="name" VALUE="2">Bar
+ # # <INPUT TYPE="radio" NAME="name" VALUE="Baz">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"])
+ 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
+
+ # Generate a reset button Input element, as a String.
+ #
+ # This resets the values on a form to their initial values. +value+
+ # is the text displayed on the button. +name+ is the name of this button.
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # reset
+ # # <INPUT TYPE="reset">
+ #
+ # reset("reset")
+ # # <INPUT TYPE="reset" VALUE="reset">
+ #
+ # reset("VALUE" => "reset", "ID" => "foo")
+ # # <INPUT TYPE="reset" VALUE="reset" ID="foo">
+ 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
+
+ alias scrolling_list popup_menu
+
+ # Generate a submit button Input element, as a String.
+ #
+ # +value+ is the text to display on the button. +name+ is the name
+ # of the input.
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # submit
+ # # <INPUT TYPE="submit">
+ #
+ # submit("ok")
+ # # <INPUT TYPE="submit" VALUE="ok">
+ #
+ # submit("ok", "button1")
+ # # <INPUT TYPE="submit" VALUE="ok" NAME="button1">
+ #
+ # submit("VALUE" => "ok", "NAME" => "button1", "ID" => "foo")
+ # # <INPUT TYPE="submit" VALUE="ok" NAME="button1" ID="foo">
+ 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
+
+ # Generate a text field Input element, as a String.
+ #
+ # +name+ is the name of the input field. +value+ is its initial
+ # value. +size+ is the size of the input area. +maxlength+
+ # is the maximum length of input accepted.
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # text_field("name")
+ # # <INPUT TYPE="text" NAME="name" SIZE="40">
+ #
+ # text_field("name", "value")
+ # # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="40">
+ #
+ # text_field("name", "value", 80)
+ # # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="80">
+ #
+ # text_field("name", "value", 80, 200)
+ # # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="80" MAXLENGTH="200">
+ #
+ # text_field("NAME" => "name", "VALUE" => "value")
+ # # <INPUT TYPE="text" NAME="name" VALUE="value">
+ 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
+
+ # Generate a TextArea element, as a String.
+ #
+ # +name+ is the name of the textarea. +cols+ is the number of
+ # columns and +rows+ is the number of rows in the display.
+ #
+ # Alternatively, the attributes can be specified as a hash.
+ #
+ # The body is provided by the passed-in no-argument block
+ #
+ # textarea("name")
+ # # = textarea("NAME" => "name", "COLS" => 70, "ROWS" => 10)
+ #
+ # textarea("name", 40, 5)
+ # # = textarea("NAME" => "name", "COLS" => 40, "ROWS" => 5)
+ def textarea(name = "", cols = 70, rows = 10) # :yield:
+ attributes = if name.kind_of?(String)
+ { "NAME" => name, "COLS" => cols.to_s,
+ "ROWS" => rows.to_s }
+ else
+ name
+ end
+ if block_given?
+ super(attributes){ yield }
+ else
+ super(attributes)
+ end
+ end
+
+ end # HtmlExtension
+
+
+ # Mixin module for HTML version 3 generation methods.
+ module Html3 # :nodoc:
+
+ # The DOCTYPE declaration for this version of HTML
+ def doctype
+ %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">|
+ end
+
+ # Initialise the HTML generation methods for this version.
+ def element_init
+ extend TagMaker
+ methods = ""
+ # - -
+ for element in %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 ]
+ methods += <<-BEGIN + nn_element_def(element) + <<-END
+ def #{element.downcase}(attributes = {})
+ BEGIN
+ end
+ END
+ end
+
+ # - O EMPTY
+ for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT
+ ISINDEX META ]
+ methods += <<-BEGIN + nOE_element_def(element) + <<-END
+ def #{element.downcase}(attributes = {})
+ BEGIN
+ end
+ END
+ end
+
+ # O O or - O
+ for element in %w[ HTML HEAD BODY P PLAINTEXT DT DD LI OPTION tr
+ th td ]
+ methods += <<-BEGIN + nO_element_def(element) + <<-END
+ def #{element.downcase}(attributes = {})
+ BEGIN
+ end
+ END
+ end
+ eval(methods)
+ end
+
+ end # Html3
+
+
+ # Mixin module for HTML version 4 generation methods.
+ module Html4 # :nodoc:
+
+ # The DOCTYPE declaration for this version of HTML
+ def doctype
+ %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">|
+ end
+
+ # Initialise the HTML generation methods for this version.
+ def element_init
+ extend TagMaker
+ methods = ""
+ # - -
+ for element in %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 ]
+ methods += <<-BEGIN + nn_element_def(element) + <<-END
+ def #{element.downcase}(attributes = {})
+ BEGIN
+ end
+ END
+ end
+
+ # - O EMPTY
+ for element in %w[ IMG BASE BR AREA LINK PARAM HR INPUT COL META ]
+ methods += <<-BEGIN + nOE_element_def(element) + <<-END
+ def #{element.downcase}(attributes = {})
+ BEGIN
+ end
+ END
+ end
+
+ # O O or - O
+ for element in %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY
+ COLGROUP TR TH TD HEAD]
+ methods += <<-BEGIN + nO_element_def(element) + <<-END
+ def #{element.downcase}(attributes = {})
+ BEGIN
+ end
+ END
+ end
+ eval(methods)
+ end
+
+ end # Html4
+
+
+ # Mixin module for HTML version 4 transitional generation methods.
+ module Html4Tr # :nodoc:
+
+ # The DOCTYPE declaration for this version of HTML
+ def doctype
+ %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">|
+ end
+
+ # Initialise the HTML generation methods for this version.
+ def element_init
+ extend TagMaker
+ methods = ""
+ # - -
+ for element in %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 ]
+ methods += <<-BEGIN + nn_element_def(element) + <<-END
+ def #{element.downcase}(attributes = {})
+ BEGIN
+ end
+ END
+ end
+
+ # - O EMPTY
+ for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT
+ COL ISINDEX META ]
+ methods += <<-BEGIN + nOE_element_def(element) + <<-END
+ def #{element.downcase}(attributes = {})
+ BEGIN
+ end
+ END
+ end
+
+ # O O or - O
+ for element in %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY
+ COLGROUP TR TH TD HEAD ]
+ methods += <<-BEGIN + nO_element_def(element) + <<-END
+ def #{element.downcase}(attributes = {})
+ BEGIN
+ end
+ END
+ end
+ eval(methods)
+ end
+
+ end # Html4Tr
+
+
+ # Mixin module for generating HTML version 4 with framesets.
+ module Html4Fr # :nodoc:
+
+ # The DOCTYPE declaration for this version of HTML
+ def doctype
+ %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">|
+ end
+
+ # Initialise the HTML generation methods for this version.
+ def element_init
+ methods = ""
+ # - -
+ for element in %w[ FRAMESET ]
+ methods += <<-BEGIN + nn_element_def(element) + <<-END
+ def #{element.downcase}(attributes = {})
+ BEGIN
+ end
+ END
+ end
+
+ # - O EMPTY
+ for element in %w[ FRAME ]
+ methods += <<-BEGIN + nOE_element_def(element) + <<-END
+ def #{element.downcase}(attributes = {})
+ BEGIN
+ end
+ END
+ end
+ eval(methods)
+ end
+
+ end # Html4Fr
+
+
+ # Creates a new CGI instance.
+ #
+ # +type+ specifies which version of HTML to load the HTML generation
+ # methods for. The following versions of HTML are supported:
+ #
+ # html3:: HTML 3.x
+ # html4:: HTML 4.0
+ # html4Tr:: HTML 4.0 Transitional
+ # html4Fr:: HTML 4.0 with Framesets
+ #
+ # If not specified, no HTML generation methods will be loaded.
+ #
+ # If the CGI object is not created in a standard CGI call environment
+ # (that is, it can't locate REQUEST_METHOD in its environment), then
+ # it will run in "offline" mode. In this mode, it reads its parameters
+ # from the command line or (failing that) from standard input. Otherwise,
+ # cookies and other parameters are parsed automatically from the standard
+ # CGI locations, which varies according to the REQUEST_METHOD.
+ def initialize(type = "query")
+ if defined?(MOD_RUBY) && !ENV.key?("GATEWAY_INTERFACE")
+ Apache.request.setup_cgi_env
+ end
+
+ extend QueryExtension
+ @multipart = false
+ if defined?(CGI_PARAMS)
+ warn "do not use CGI_PARAMS and CGI_COOKIES"
+ @params = CGI_PARAMS.dup
+ @cookies = CGI_COOKIES.dup
+ else
+ initialize_query() # set @params, @cookies
+ end
+ @output_cookies = nil
+ @output_hidden = nil
+
+ case type
+ when "html3"
+ extend Html3
+ element_init()
+ extend HtmlExtension
+ when "html4"
+ extend Html4
+ element_init()
+ extend HtmlExtension
+ when "html4Tr"
+ extend Html4Tr
+ element_init()
+ extend HtmlExtension
+ when "html4Fr"
+ extend Html4Tr
+ element_init()
+ extend Html4Fr
+ element_init()
+ extend HtmlExtension
+ end
+ end
+
+end # class CGI
diff --git a/ruby_1_8_5/lib/cgi/.document b/ruby_1_8_5/lib/cgi/.document
new file mode 100644
index 0000000000..107f2f317c
--- /dev/null
+++ b/ruby_1_8_5/lib/cgi/.document
@@ -0,0 +1,2 @@
+session.rb
+
diff --git a/ruby_1_8_5/lib/cgi/session.rb b/ruby_1_8_5/lib/cgi/session.rb
new file mode 100644
index 0000000000..82eb7534d8
--- /dev/null
+++ b/ruby_1_8_5/lib/cgi/session.rb
@@ -0,0 +1,484 @@
+#
+# cgi/session.rb - session support for cgi scripts
+#
+# Copyright (C) 2001 Yukihiro "Matz" Matsumoto
+# Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+# Copyright (C) 2000 Information-technology Promotion Agency, Japan
+#
+# Author: Yukihiro "Matz" Matsumoto
+#
+# Documentation: William Webber (william@williamwebber.com)
+#
+# == Overview
+#
+# This file provides the +CGI::Session+ class, which provides session
+# support for CGI scripts. A session is a sequence of HTTP requests
+# and responses linked together and associated with a single client.
+# Information associated with the session is stored
+# on the server between requests. A session id is passed between client
+# and server with every request and response, transparently
+# to the user. This adds state information to the otherwise stateless
+# HTTP request/response protocol.
+#
+# See the documentation to the +CGI::Session+ class for more details
+# and examples of usage. See cgi.rb for the +CGI+ class itself.
+
+require 'cgi'
+require 'tmpdir'
+
+class CGI
+
+ # Class representing an HTTP session. See documentation for the file
+ # cgi/session.rb for an introduction to HTTP sessions.
+ #
+ # == Lifecycle
+ #
+ # A CGI::Session instance is created from a CGI object. By default,
+ # this CGI::Session instance will start a new session if none currently
+ # exists, or continue the current session for this client if one does
+ # exist. The +new_session+ option can be used to either always or
+ # never create a new session. See #new() for more details.
+ #
+ # #delete() deletes a session from session storage. It
+ # does not however remove the session id from the client. If the client
+ # makes another request with the same id, the effect will be to start
+ # a new session with the old session's id.
+ #
+ # == Setting and retrieving session data.
+ #
+ # The Session class associates data with a session as key-value pairs.
+ # This data can be set and retrieved by indexing the Session instance
+ # using '[]', much the same as hashes (although other hash methods
+ # are not supported).
+ #
+ # When session processing has been completed for a request, the
+ # session should be closed using the close() method. This will
+ # store the session's state to persistent storage. If you want
+ # to store the session's state to persistent storage without
+ # finishing session processing for this request, call the update()
+ # method.
+ #
+ # == Storing session state
+ #
+ # The caller can specify what form of storage to use for the session's
+ # data with the +database_manager+ option to CGI::Session::new. The
+ # following storage classes are provided as part of the standard library:
+ #
+ # CGI::Session::FileStore:: stores data as plain text in a flat file. Only
+ # works with String data. This is the default
+ # storage type.
+ # CGI::Session::MemoryStore:: stores data in an in-memory hash. The data
+ # only persists for as long as the current ruby
+ # interpreter instance does.
+ # CGI::Session::PStore:: stores data in Marshalled format. Provided by
+ # cgi/session/pstore.rb. Supports data of any type,
+ # and provides file-locking and transaction support.
+ #
+ # Custom storage types can also be created by defining a class with
+ # the following methods:
+ #
+ # new(session, options)
+ # restore # returns hash of session data.
+ # update
+ # close
+ # delete
+ #
+ # Changing storage type mid-session does not work. Note in particular
+ # that by default the FileStore and PStore session data files have the
+ # same name. If your application switches from one to the other without
+ # making sure that filenames will be different
+ # and clients still have old sessions lying around in cookies, then
+ # things will break nastily!
+ #
+ # == Maintaining the session id.
+ #
+ # Most session state is maintained on the server. However, a session
+ # id must be passed backwards and forwards between client and server
+ # to maintain a reference to this session state.
+ #
+ # The simplest way to do this is via cookies. The CGI::Session class
+ # provides transparent support for session id communication via cookies
+ # if the client has cookies enabled.
+ #
+ # If the client has cookies disabled, the session id must be included
+ # as a parameter of all requests sent by the client to the server. The
+ # CGI::Session class in conjunction with the CGI class will transparently
+ # add the session id as a hidden input field to all forms generated
+ # using the CGI#form() HTML generation method. No built-in support is
+ # provided for other mechanisms, such as URL re-writing. The caller is
+ # responsible for extracting the session id from the session_id
+ # attribute and manually encoding it in URLs and adding it as a hidden
+ # input to HTML forms created by other mechanisms. Also, session expiry
+ # is not automatically handled.
+ #
+ # == Examples of use
+ #
+ # === Setting the user's name
+ #
+ # require 'cgi'
+ # require 'cgi/session'
+ # require 'cgi/session/pstore' # provides CGI::Session::PStore
+ #
+ # cgi = CGI.new("html4")
+ #
+ # session = CGI::Session.new(cgi,
+ # 'database_manager' => CGI::Session::PStore, # use PStore
+ # 'session_key' => '_rb_sess_id', # custom session key
+ # 'session_expires' => Time.now + 30 * 60, # 30 minute timeout
+ # 'prefix' => 'pstore_sid_') # PStore option
+ # if cgi.has_key?('user_name') and cgi['user_name'] != ''
+ # # coerce to String: cgi[] returns the
+ # # string-like CGI::QueryExtension::Value
+ # session['user_name'] = cgi['user_name'].to_s
+ # elsif !session['user_name']
+ # session['user_name'] = "guest"
+ # end
+ # session.close
+ #
+ # === Creating a new session safely
+ #
+ # require 'cgi'
+ # require 'cgi/session'
+ #
+ # cgi = CGI.new("html4")
+ #
+ # # We make sure to delete an old session if one exists,
+ # # not just to free resources, but to prevent the session
+ # # from being maliciously hijacked later on.
+ # begin
+ # session = CGI::Session.new(cgi, 'new_session' => false)
+ # session.delete
+ # rescue ArgumentError # if no old session
+ # end
+ # session = CGI::Session.new(cgi, 'new_session' => true)
+ # session.close
+ #
+ class Session
+
+ class NoSession < RuntimeError #:nodoc:
+ end
+
+ # The id of this session.
+ attr_reader :session_id, :new_session
+
+ def Session::callback(dbman) #:nodoc:
+ Proc.new{
+ dbman[0].close unless dbman.empty?
+ }
+ end
+
+ # Create a new session id.
+ #
+ # The session id is an MD5 hash based upon the time,
+ # a random number, and a constant string. This routine
+ # is used internally for automatically generated
+ # session ids.
+ def create_new_id
+ require 'digest/md5'
+ md5 = Digest::MD5::new
+ now = Time::now
+ md5.update(now.to_s)
+ md5.update(String(now.usec))
+ md5.update(String(rand(0)))
+ md5.update(String($$))
+ md5.update('foobar')
+ @new_session = true
+ md5.hexdigest
+ end
+ private :create_new_id
+
+ # Create a new CGI::Session object for +request+.
+ #
+ # +request+ is an instance of the +CGI+ class (see cgi.rb).
+ # +option+ is a hash of options for initialising this
+ # CGI::Session instance. The following options are
+ # recognised:
+ #
+ # session_key:: the parameter name used for the session id.
+ # Defaults to '_session_id'.
+ # session_id:: the session id to use. If not provided, then
+ # it is retrieved from the +session_key+ parameter
+ # of the request, or automatically generated for
+ # a new session.
+ # new_session:: if true, force creation of a new session. If not set,
+ # a new session is only created if none currently
+ # exists. If false, a new session is never created,
+ # and if none currently exists and the +session_id+
+ # option is not set, an ArgumentError is raised.
+ # database_manager:: the name of the class providing storage facilities
+ # for session state persistence. Built-in support
+ # is provided for +FileStore+ (the default),
+ # +MemoryStore+, and +PStore+ (from
+ # cgi/session/pstore.rb). See the documentation for
+ # these classes for more details.
+ #
+ # The following options are also recognised, but only apply if the
+ # session id is stored in a cookie.
+ #
+ # session_expires:: the time the current session expires, as a
+ # +Time+ object. If not set, the session will terminate
+ # when the user's browser is closed.
+ # session_domain:: the hostname domain for which this session is valid.
+ # If not set, defaults to the hostname of the server.
+ # session_secure:: if +true+, this session will only work over HTTPS.
+ # session_path:: the path for which this session applies. Defaults
+ # to the directory of the CGI script.
+ #
+ # +option+ is also passed on to the session storage class initialiser; see
+ # the documentation for each session storage class for the options
+ # they support.
+ #
+ # The retrieved or created session is automatically added to +request+
+ # as a cookie, and also to its +output_hidden+ table, which is used
+ # to add hidden input elements to forms.
+ #
+ # *WARNING* the +output_hidden+
+ # fields are surrounded by a <fieldset> tag in HTML 4 generation, which
+ # is _not_ invisible on many browsers; you may wish to disable the
+ # use of fieldsets with code similar to the following
+ # (see http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/37805)
+ #
+ # cgi = CGI.new("html4")
+ # class << cgi
+ # undef_method :fieldset
+ # end
+ #
+ def initialize(request, option={})
+ @new_session = false
+ session_key = option['session_key'] || '_session_id'
+ session_id = option['session_id']
+ unless session_id
+ if option['new_session']
+ session_id = create_new_id
+ end
+ end
+ unless session_id
+ if request.key?(session_key)
+ session_id = request[session_key]
+ session_id = session_id.read if session_id.respond_to?(:read)
+ end
+ unless session_id
+ session_id, = request.cookies[session_key]
+ end
+ unless session_id
+ unless option.fetch('new_session', true)
+ raise ArgumentError, "session_key `%s' should be supplied"%session_key
+ end
+ session_id = create_new_id
+ end
+ end
+ @session_id = session_id
+ dbman = option['database_manager'] || FileStore
+ begin
+ @dbman = dbman::new(self, option)
+ rescue NoSession
+ unless option.fetch('new_session', true)
+ raise ArgumentError, "invalid session_id `%s'"%session_id
+ end
+ session_id = @session_id = create_new_id
+ retry
+ end
+ request.instance_eval do
+ @output_hidden = {session_key => session_id} unless option['no_hidden']
+ @output_cookies = [
+ Cookie::new("name" => session_key,
+ "value" => session_id,
+ "expires" => option['session_expires'],
+ "domain" => option['session_domain'],
+ "secure" => option['session_secure'],
+ "path" => if option['session_path'] then
+ option['session_path']
+ elsif ENV["SCRIPT_NAME"] then
+ File::dirname(ENV["SCRIPT_NAME"])
+ else
+ ""
+ end)
+ ] unless option['no_cookies']
+ end
+ @dbprot = [@dbman]
+ ObjectSpace::define_finalizer(self, Session::callback(@dbprot))
+ end
+
+ # Retrieve the session data for key +key+.
+ def [](key)
+ @data ||= @dbman.restore
+ @data[key]
+ end
+
+ # Set the session date for key +key+.
+ def []=(key, val)
+ @write_lock ||= true
+ @data ||= @dbman.restore
+ @data[key] = val
+ end
+
+ # Store session data on the server. For some session storage types,
+ # this is a no-op.
+ def update
+ @dbman.update
+ end
+
+ # Store session data on the server and close the session storage.
+ # For some session storage types, this is a no-op.
+ def close
+ @dbman.close
+ @dbprot.clear
+ end
+
+ # Delete the session from storage. Also closes the storage.
+ #
+ # Note that the session's data is _not_ automatically deleted
+ # upon the session expiring.
+ def delete
+ @dbman.delete
+ @dbprot.clear
+ end
+
+ # File-based session storage class.
+ #
+ # Implements session storage as a flat file of 'key=value' values.
+ # This storage type only works directly with String values; the
+ # user is responsible for converting other types to Strings when
+ # storing and from Strings when retrieving.
+ class FileStore
+ # Create a new FileStore instance.
+ #
+ # This constructor is used internally by CGI::Session. The
+ # user does not generally need to call it directly.
+ #
+ # +session+ is the session for which this instance is being
+ # created. The session id must only contain alphanumeric
+ # characters; automatically generated session ids observe
+ # this requirement.
+ #
+ # +option+ is a hash of options for the initialiser. The
+ # following options are recognised:
+ #
+ # tmpdir:: the directory to use for storing the FileStore
+ # file. Defaults to Dir::tmpdir (generally "/tmp"
+ # on Unix systems).
+ # prefix:: the prefix to add to the session id when generating
+ # the filename for this session's FileStore file.
+ # Defaults to the empty string.
+ # suffix:: the prefix to add to the session id when generating
+ # the filename for this session's FileStore file.
+ # Defaults to the empty string.
+ #
+ # This session's FileStore file will be created if it does
+ # not exist, or opened if it does.
+ def initialize(session, option={})
+ dir = option['tmpdir'] || Dir::tmpdir
+ prefix = option['prefix'] || ''
+ suffix = option['suffix'] || ''
+ id = session.session_id
+ require 'digest/md5'
+ md5 = Digest::MD5.hexdigest(id)[0,16]
+ @path = dir+"/"+prefix+md5+suffix
+ if File::exist? @path
+ @hash = nil
+ else
+ unless session.new_session
+ raise CGI::Session::NoSession, "uninitialized session"
+ end
+ @hash = {}
+ end
+ end
+
+ # Restore session state from the session's FileStore file.
+ #
+ # Returns the session state as a hash.
+ def restore
+ unless @hash
+ @hash = {}
+ begin
+ f = File.open(@path, 'r')
+ f.flock File::LOCK_SH
+ for line in f
+ line.chomp!
+ k, v = line.split('=',2)
+ @hash[CGI::unescape(k)] = CGI::unescape(v)
+ end
+ ensure
+ f.close unless f.nil?
+ end
+ end
+ @hash
+ end
+
+ # Save session state to the session's FileStore file.
+ def update
+ return unless @hash
+ begin
+ f = File.open(@path, File::CREAT|File::TRUNC|File::RDWR, 0600)
+ f.flock File::LOCK_EX
+ for k,v in @hash
+ f.printf "%s=%s\n", CGI::escape(k), CGI::escape(String(v))
+ end
+ ensure
+ f.close unless f.nil?
+ end
+ end
+
+ # Update and close the session's FileStore file.
+ def close
+ update
+ end
+
+ # Close and delete the session's FileStore file.
+ def delete
+ File::unlink @path
+ rescue Errno::ENOENT
+ end
+ end
+
+ # In-memory session storage class.
+ #
+ # Implements session storage as a global in-memory hash. Session
+ # data will only persist for as long as the ruby interpreter
+ # instance does.
+ class MemoryStore
+ GLOBAL_HASH_TABLE = {} #:nodoc:
+
+ # Create a new MemoryStore instance.
+ #
+ # +session+ is the session this instance is associated with.
+ # +option+ is a list of initialisation options. None are
+ # currently recognised.
+ def initialize(session, option=nil)
+ @session_id = session.session_id
+ unless GLOBAL_HASH_TABLE.key?(@session_id)
+ unless session.new_session
+ raise CGI::Session::NoSession, "uninitialized session"
+ end
+ GLOBAL_HASH_TABLE[@session_id] = {}
+ end
+ end
+
+ # Restore session state.
+ #
+ # Returns session data as a hash.
+ def restore
+ GLOBAL_HASH_TABLE[@session_id]
+ end
+
+ # Update session state.
+ #
+ # A no-op.
+ def update
+ # don't need to update; hash is shared
+ end
+
+ # Close session storage.
+ #
+ # A no-op.
+ def close
+ # don't need to close
+ end
+
+ # Delete the session state.
+ def delete
+ GLOBAL_HASH_TABLE.delete(@session_id)
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/cgi/session/pstore.rb b/ruby_1_8_5/lib/cgi/session/pstore.rb
new file mode 100644
index 0000000000..bd93d0a6ff
--- /dev/null
+++ b/ruby_1_8_5/lib/cgi/session/pstore.rb
@@ -0,0 +1,111 @@
+#
+# cgi/session/pstore.rb - persistent storage of marshalled session data
+#
+# Documentation: William Webber (william@williamwebber.com)
+#
+# == Overview
+#
+# This file provides the CGI::Session::PStore class, which builds
+# persistent of session data on top of the pstore library. See
+# cgi/session.rb for more details on session storage managers.
+
+require 'cgi/session'
+require 'pstore'
+
+class CGI
+ class Session
+ # PStore-based session storage class.
+ #
+ # This builds upon the top-level PStore class provided by the
+ # library file pstore.rb. Session data is marshalled and stored
+ # in a file. File locking and transaction services are provided.
+ class PStore
+ # Create a new CGI::Session::PStore instance
+ #
+ # This constructor is used internally by CGI::Session. The
+ # user does not generally need to call it directly.
+ #
+ # +session+ is the session for which this instance is being
+ # created. The session id must only contain alphanumeric
+ # characters; automatically generated session ids observe
+ # this requirement.
+ #
+ # +option+ is a hash of options for the initialiser. The
+ # following options are recognised:
+ #
+ # tmpdir:: the directory to use for storing the PStore
+ # file. Defaults to Dir::tmpdir (generally "/tmp"
+ # on Unix systems).
+ # prefix:: the prefix to add to the session id when generating
+ # the filename for this session's PStore file.
+ # Defaults to the empty string.
+ #
+ # This session's PStore file will be created if it does
+ # not exist, or opened if it does.
+ def initialize(session, option={})
+ dir = option['tmpdir'] || Dir::tmpdir
+ prefix = option['prefix'] || ''
+ id = session.session_id
+ require 'digest/md5'
+ md5 = Digest::MD5.hexdigest(id)[0,16]
+ path = dir+"/"+prefix+md5
+ path.untaint
+ if File::exist?(path)
+ @hash = nil
+ else
+ unless session.new_session
+ raise CGI::Session::NoSession, "uninitialized session"
+ end
+ @hash = {}
+ end
+ @p = ::PStore.new(path)
+ @p.transaction do |p|
+ File.chmod(0600, p.path)
+ end
+ end
+
+ # Restore session state from the session's PStore file.
+ #
+ # Returns the session state as a hash.
+ def restore
+ unless @hash
+ @p.transaction do
+ @hash = @p['hash'] || {}
+ end
+ end
+ @hash
+ end
+
+ # Save session state to the session's PStore file.
+ def update
+ @p.transaction do
+ @p['hash'] = @hash
+ end
+ end
+
+ # Update and close the session's PStore file.
+ def close
+ update
+ end
+
+ # Close and delete the session's PStore file.
+ def delete
+ path = @p.path
+ File::unlink path
+ end
+
+ end
+ end
+end
+
+if $0 == __FILE__
+ # :enddoc:
+ STDIN.reopen("/dev/null")
+ cgi = CGI.new
+ session = CGI::Session.new(cgi, 'database_manager' => CGI::Session::PStore)
+ session['key'] = {'k' => 'v'}
+ puts session['key'].class
+ fail unless Hash === session['key']
+ puts session['key'].inspect
+ fail unless session['key'].inspect == '{"k"=>"v"}'
+end
diff --git a/ruby_1_8_5/lib/complex.rb b/ruby_1_8_5/lib/complex.rb
new file mode 100644
index 0000000000..9300f391e8
--- /dev/null
+++ b/ruby_1_8_5/lib/complex.rb
@@ -0,0 +1,631 @@
+#
+# complex.rb -
+# $Release Version: 0.5 $
+# $Revision: 1.3 $
+# $Date: 1998/07/08 10:05:28 $
+# by Keiju ISHITSUKA(SHL Japan Inc.)
+#
+# ----
+#
+# complex.rb implements the Complex class for complex numbers. Additionally,
+# some methods in other Numeric classes are redefined or added to allow greater
+# interoperability with Complex numbers.
+#
+# Complex numbers can be created in the following manner:
+# - <tt>Complex(a, b)</tt>
+# - <tt>Complex.polar(radius, theta)</tt>
+#
+# Additionally, note the following:
+# - <tt>Complex::I</tt> (the mathematical constant <i>i</i>)
+# - <tt>Numeric#im</tt> (e.g. <tt>5.im -> 0+5i</tt>)
+#
+# The following +Math+ module methods are redefined to handle Complex arguments.
+# They will work as normal with non-Complex arguments.
+# sqrt exp cos sin tan log log10
+# cosh sinh tanh acos asin atan atan2 acosh asinh atanh
+#
+
+
+#
+# Numeric is a built-in class on which Fixnum, Bignum, etc., are based. Here
+# some methods are added so that all number types can be treated to some extent
+# as Complex numbers.
+#
+class Numeric
+ #
+ # Returns a Complex number <tt>(0,<i>self</i>)</tt>.
+ #
+ def im
+ Complex(0, self)
+ end
+
+ #
+ # The real part of a complex number, i.e. <i>self</i>.
+ #
+ def real
+ self
+ end
+
+ #
+ # The imaginary part of a complex number, i.e. 0.
+ #
+ def image
+ 0
+ end
+ alias imag image
+
+ #
+ # See Complex#arg.
+ #
+ def arg
+ if self >= 0
+ return 0
+ else
+ return Math::PI
+ end
+ end
+ alias angle arg
+
+ #
+ # See Complex#polar.
+ #
+ def polar
+ return abs, arg
+ end
+
+ #
+ # See Complex#conjugate (short answer: returns <i>self</i>).
+ #
+ def conjugate
+ self
+ end
+ alias conj conjugate
+end
+
+
+#
+# Creates a Complex number. +a+ and +b+ should be Numeric. The result will be
+# <tt>a+bi</tt>.
+#
+def Complex(a, b = 0)
+ if b == 0 and (a.kind_of?(Complex) or defined? Complex::Unify)
+ a
+ else
+ Complex.new( a.real-b.imag, a.imag+b.real )
+ end
+end
+
+#
+# The complex number class. See complex.rb for an overview.
+#
+class Complex < Numeric
+ @RCS_ID='-$Id: complex.rb,v 1.3 1998/07/08 10:05:28 keiju Exp keiju $-'
+
+ undef step
+
+ def Complex.generic?(other) # :nodoc:
+ other.kind_of?(Integer) or
+ other.kind_of?(Float) or
+ (defined?(Rational) and other.kind_of?(Rational))
+ end
+
+ #
+ # Creates a +Complex+ number in terms of +r+ (radius) and +theta+ (angle).
+ #
+ def Complex.polar(r, theta)
+ Complex(r*Math.cos(theta), r*Math.sin(theta))
+ end
+
+ #
+ # Creates a +Complex+ number <tt>a</tt>+<tt>b</tt><i>i</i>.
+ #
+ def Complex.new!(a, b=0)
+ new(a,b)
+ end
+
+ def initialize(a, b)
+ raise TypeError, "non numeric 1st arg `#{a.inspect}'" if !a.kind_of? Numeric
+ raise TypeError, "`#{a.inspect}' for 1st arg" if a.kind_of? Complex
+ raise TypeError, "non numeric 2nd arg `#{b.inspect}'" if !b.kind_of? Numeric
+ raise TypeError, "`#{b.inspect}' for 2nd arg" if b.kind_of? Complex
+ @real = a
+ @image = b
+ end
+
+ #
+ # Addition with real or complex number.
+ #
+ def + (other)
+ if other.kind_of?(Complex)
+ re = @real + other.real
+ im = @image + other.image
+ Complex(re, im)
+ elsif Complex.generic?(other)
+ Complex(@real + other, @image)
+ else
+ x , y = other.coerce(self)
+ x + y
+ end
+ end
+
+ #
+ # Subtraction with real or complex number.
+ #
+ def - (other)
+ if other.kind_of?(Complex)
+ re = @real - other.real
+ im = @image - other.image
+ Complex(re, im)
+ elsif Complex.generic?(other)
+ Complex(@real - other, @image)
+ else
+ x , y = other.coerce(self)
+ x - y
+ end
+ end
+
+ #
+ # Multiplication with real or complex number.
+ #
+ def * (other)
+ if other.kind_of?(Complex)
+ re = @real*other.real - @image*other.image
+ im = @real*other.image + @image*other.real
+ Complex(re, im)
+ elsif Complex.generic?(other)
+ Complex(@real * other, @image * other)
+ else
+ x , y = other.coerce(self)
+ x * y
+ end
+ end
+
+ #
+ # Division by real or complex number.
+ #
+ def / (other)
+ if other.kind_of?(Complex)
+ self*other.conjugate/other.abs2
+ elsif Complex.generic?(other)
+ Complex(@real/other, @image/other)
+ else
+ x, y = other.coerce(self)
+ x/y
+ end
+ end
+
+ #
+ # Raise this complex number to the given (real or complex) power.
+ #
+ def ** (other)
+ if other == 0
+ return Complex(1)
+ end
+ if other.kind_of?(Complex)
+ r, theta = polar
+ ore = other.real
+ oim = other.image
+ nr = Math.exp!(ore*Math.log!(r) - oim * theta)
+ ntheta = theta*ore + oim*Math.log!(r)
+ Complex.polar(nr, ntheta)
+ elsif other.kind_of?(Integer)
+ if other > 0
+ x = self
+ z = x
+ n = other - 1
+ while n != 0
+ while (div, mod = n.divmod(2)
+ mod == 0)
+ x = Complex(x.real*x.real - x.image*x.image, 2*x.real*x.image)
+ n = div
+ end
+ z *= x
+ n -= 1
+ end
+ z
+ else
+ if defined? Rational
+ (Rational(1) / self) ** -other
+ else
+ self ** Float(other)
+ end
+ end
+ elsif Complex.generic?(other)
+ r, theta = polar
+ Complex.polar(r**other, theta*other)
+ else
+ x, y = other.coerce(self)
+ x**y
+ end
+ end
+
+ #
+ # Remainder after division by a real or complex number.
+ #
+ def % (other)
+ if other.kind_of?(Complex)
+ Complex(@real % other.real, @image % other.image)
+ elsif Complex.generic?(other)
+ Complex(@real % other, @image % other)
+ else
+ x , y = other.coerce(self)
+ x % y
+ end
+ end
+
+#--
+# def divmod(other)
+# if other.kind_of?(Complex)
+# rdiv, rmod = @real.divmod(other.real)
+# idiv, imod = @image.divmod(other.image)
+# return Complex(rdiv, idiv), Complex(rmod, rmod)
+# elsif Complex.generic?(other)
+# Complex(@real.divmod(other), @image.divmod(other))
+# else
+# x , y = other.coerce(self)
+# x.divmod(y)
+# end
+# end
+#++
+
+ #
+ # Absolute value (aka modulus): distance from the zero point on the complex
+ # plane.
+ #
+ def abs
+ Math.hypot(@real, @image)
+ end
+
+ #
+ # Square of the absolute value.
+ #
+ def abs2
+ @real*@real + @image*@image
+ end
+
+ #
+ # Argument (angle from (1,0) on the complex plane).
+ #
+ def arg
+ Math.atan2!(@image, @real)
+ end
+ alias angle arg
+
+ #
+ # Returns the absolute value _and_ the argument.
+ #
+ def polar
+ return abs, arg
+ end
+
+ #
+ # Complex conjugate (<tt>z + z.conjugate = 2 * z.real</tt>).
+ #
+ def conjugate
+ Complex(@real, -@image)
+ end
+ alias conj conjugate
+
+ #
+ # Compares the absolute values of the two numbers.
+ #
+ def <=> (other)
+ self.abs <=> other.abs
+ end
+
+ #
+ # Test for numerical equality (<tt>a == a + 0<i>i</i></tt>).
+ #
+ def == (other)
+ if other.kind_of?(Complex)
+ @real == other.real and @image == other.image
+ elsif Complex.generic?(other)
+ @real == other and @image == 0
+ else
+ other == self
+ end
+ end
+
+ #
+ # Attempts to coerce +other+ to a Complex number.
+ #
+ def coerce(other)
+ if Complex.generic?(other)
+ return Complex.new!(other), self
+ else
+ super
+ end
+ end
+
+ #
+ # FIXME
+ #
+ def denominator
+ @real.denominator.lcm(@image.denominator)
+ end
+
+ #
+ # FIXME
+ #
+ def numerator
+ cd = denominator
+ Complex(@real.numerator*(cd/@real.denominator),
+ @image.numerator*(cd/@image.denominator))
+ end
+
+ #
+ # Standard string representation of the complex number.
+ #
+ def to_s
+ if @real != 0
+ if defined?(Rational) and @image.kind_of?(Rational) and @image.denominator != 1
+ if @image >= 0
+ @real.to_s+"+("+@image.to_s+")i"
+ else
+ @real.to_s+"-("+(-@image).to_s+")i"
+ end
+ else
+ if @image >= 0
+ @real.to_s+"+"+@image.to_s+"i"
+ else
+ @real.to_s+"-"+(-@image).to_s+"i"
+ end
+ end
+ else
+ if defined?(Rational) and @image.kind_of?(Rational) and @image.denominator != 1
+ "("+@image.to_s+")i"
+ else
+ @image.to_s+"i"
+ end
+ end
+ end
+
+ #
+ # Returns a hash code for the complex number.
+ #
+ def hash
+ @real.hash ^ @image.hash
+ end
+
+ #
+ # Returns "<tt>Complex(<i>real</i>, <i>image</i>)</tt>".
+ #
+ def inspect
+ sprintf("Complex(%s, %s)", @real.inspect, @image.inspect)
+ end
+
+
+ #
+ # +I+ is the imaginary number. It exists at point (0,1) on the complex plane.
+ #
+ I = Complex(0,1)
+
+ # The real part of a complex number.
+ attr :real
+
+ # The imaginary part of a complex number.
+ attr :image
+ alias imag image
+
+end
+
+
+
+
+module Math
+ alias sqrt! sqrt
+ alias exp! exp
+ alias log! log
+ alias log10! log10
+ alias cos! cos
+ alias sin! sin
+ alias tan! tan
+ alias cosh! cosh
+ alias sinh! sinh
+ alias tanh! tanh
+ alias acos! acos
+ alias asin! asin
+ alias atan! atan
+ alias atan2! atan2
+ alias acosh! acosh
+ alias asinh! asinh
+ alias atanh! atanh
+
+ # Redefined to handle a Complex argument.
+ def sqrt(z)
+ if Complex.generic?(z)
+ if z >= 0
+ sqrt!(z)
+ else
+ Complex(0,sqrt!(-z))
+ end
+ else
+ if z.image < 0
+ sqrt(z.conjugate).conjugate
+ else
+ r = z.abs
+ x = z.real
+ Complex( sqrt!((r+x)/2), sqrt!((r-x)/2) )
+ end
+ end
+ end
+
+ # Redefined to handle a Complex argument.
+ def exp(z)
+ if Complex.generic?(z)
+ exp!(z)
+ else
+ Complex(exp!(z.real) * cos!(z.image), exp!(z.real) * sin!(z.image))
+ end
+ end
+
+ # Redefined to handle a Complex argument.
+ def cos(z)
+ if Complex.generic?(z)
+ cos!(z)
+ else
+ Complex(cos!(z.real)*cosh!(z.image),
+ -sin!(z.real)*sinh!(z.image))
+ end
+ end
+
+ # Redefined to handle a Complex argument.
+ def sin(z)
+ if Complex.generic?(z)
+ sin!(z)
+ else
+ Complex(sin!(z.real)*cosh!(z.image),
+ cos!(z.real)*sinh!(z.image))
+ end
+ end
+
+ # Redefined to handle a Complex argument.
+ def tan(z)
+ if Complex.generic?(z)
+ tan!(z)
+ else
+ sin(z)/cos(z)
+ end
+ end
+
+ def sinh(z)
+ if Complex.generic?(z)
+ sinh!(z)
+ else
+ Complex( sinh!(z.real)*cos!(z.image), cosh!(z.real)*sin!(z.image) )
+ end
+ end
+
+ def cosh(z)
+ if Complex.generic?(z)
+ cosh!(z)
+ else
+ Complex( cosh!(z.real)*cos!(z.image), sinh!(z.real)*sin!(z.image) )
+ end
+ end
+
+ def tanh(z)
+ if Complex.generic?(z)
+ tanh!(z)
+ else
+ sinh(z)/cosh(z)
+ end
+ end
+
+ # Redefined to handle a Complex argument.
+ def log(z)
+ if Complex.generic?(z) and z >= 0
+ log!(z)
+ else
+ r, theta = z.polar
+ Complex(log!(r.abs), theta)
+ end
+ end
+
+ # Redefined to handle a Complex argument.
+ def log10(z)
+ if Complex.generic?(z)
+ log10!(z)
+ else
+ log(z)/log!(10)
+ end
+ end
+
+ def acos(z)
+ if Complex.generic?(z) and z >= -1 and z <= 1
+ acos!(z)
+ else
+ -1.0.im * log( z + 1.0.im * sqrt(1.0-z*z) )
+ end
+ end
+
+ def asin(z)
+ if Complex.generic?(z) and z >= -1 and z <= 1
+ asin!(z)
+ else
+ -1.0.im * log( 1.0.im * z + sqrt(1.0-z*z) )
+ end
+ end
+
+ def atan(z)
+ if Complex.generic?(z)
+ atan!(z)
+ else
+ 1.0.im * log( (1.0.im+z) / (1.0.im-z) ) / 2.0
+ end
+ end
+
+ def atan2(y,x)
+ if Complex.generic?(y) and Complex.generic?(x)
+ atan2!(y,x)
+ else
+ -1.0.im * log( (x+1.0.im*y) / sqrt(x*x+y*y) )
+ end
+ end
+
+ def acosh(z)
+ if Complex.generic?(z) and z >= 1
+ acosh!(z)
+ else
+ log( z + sqrt(z*z-1.0) )
+ end
+ end
+
+ def asinh(z)
+ if Complex.generic?(z)
+ asinh!(z)
+ else
+ log( z + sqrt(1.0+z*z) )
+ end
+ end
+
+ def atanh(z)
+ if Complex.generic?(z) and z >= -1 and z <= 1
+ atanh!(z)
+ else
+ log( (1.0+z) / (1.0-z) ) / 2.0
+ end
+ end
+
+ module_function :sqrt!
+ module_function :sqrt
+ module_function :exp!
+ module_function :exp
+ module_function :log!
+ module_function :log
+ module_function :log10!
+ module_function :log10
+ module_function :cosh!
+ module_function :cosh
+ module_function :cos!
+ module_function :cos
+ module_function :sinh!
+ module_function :sinh
+ module_function :sin!
+ module_function :sin
+ module_function :tan!
+ module_function :tan
+ module_function :tanh!
+ module_function :tanh
+ module_function :acos!
+ module_function :acos
+ module_function :asin!
+ module_function :asin
+ module_function :atan!
+ module_function :atan
+ module_function :atan2!
+ module_function :atan2
+ module_function :acosh!
+ module_function :acosh
+ module_function :asinh!
+ module_function :asinh
+ module_function :atanh!
+ module_function :atanh
+
+end
+
+# Documentation comments:
+# - source: original (researched from pickaxe)
+# - a couple of fixme's
+# - RDoc output for Bignum etc. is a bit short, with nothing but an
+# (undocumented) alias. No big deal.
diff --git a/ruby_1_8_5/lib/csv.rb b/ruby_1_8_5/lib/csv.rb
new file mode 100644
index 0000000000..31b698f08c
--- /dev/null
+++ b/ruby_1_8_5/lib/csv.rb
@@ -0,0 +1,992 @@
+# CSV -- module for generating/parsing CSV data.
+# Copyright (C) 2000-2004 NAKAMURA, Hiroshi <nakahiro@sarion.co.jp>.
+
+# $Id: csv.rb,v 1.4.2.4 2004/05/27 14:39:10 nahi Exp $
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+class CSV
+ class IllegalFormatError < RuntimeError; end
+
+ # deprecated
+ class Cell < String
+ def initialize(data = "", is_null = false)
+ super(is_null ? "" : data)
+ end
+
+ def data
+ to_s
+ end
+ end
+
+ # deprecated
+ class Row < Array
+ end
+
+ # Open a CSV formatted file for reading or writing.
+ #
+ # For reading.
+ #
+ # EXAMPLE 1
+ # CSV.open('csvfile.csv', 'r') do |row|
+ # p row
+ # end
+ #
+ # EXAMPLE 2
+ # reader = CSV.open('csvfile.csv', 'r')
+ # row1 = reader.shift
+ # row2 = reader.shift
+ # if row2.empty?
+ # p 'row2 not find.'
+ # end
+ # reader.close
+ #
+ # ARGS
+ # filename: filename to parse.
+ # col_sep: Column separator. ?, by default. If you want to separate
+ # fields with semicolon, give ?; here.
+ # row_sep: Row separator. nil by default. nil means "\r\n or \n". If you
+ # want to separate records with \r, give ?\r here.
+ #
+ # RETURNS
+ # reader instance. To get parse result, see CSV::Reader#each.
+ #
+ #
+ # For writing.
+ #
+ # EXAMPLE 1
+ # CSV.open('csvfile.csv', 'w') do |writer|
+ # writer << ['r1c1', 'r1c2']
+ # writer << ['r2c1', 'r2c2']
+ # writer << [nil, nil]
+ # end
+ #
+ # EXAMPLE 2
+ # writer = CSV.open('csvfile.csv', 'w')
+ # writer << ['r1c1', 'r1c2'] << ['r2c1', 'r2c2'] << [nil, nil]
+ # writer.close
+ #
+ # ARGS
+ # filename: filename to generate.
+ # col_sep: Column separator. ?, by default. If you want to separate
+ # fields with semicolon, give ?; here.
+ # row_sep: Row separator. nil by default. nil means "\r\n or \n". If you
+ # want to separate records with \r, give ?\r here.
+ #
+ # RETURNS
+ # writer instance. See CSV::Writer#<< and CSV::Writer#add_row to know how
+ # to generate CSV string.
+ #
+ def CSV.open(path, mode, fs = nil, rs = nil, &block)
+ if mode == 'r' or mode == 'rb'
+ open_reader(path, mode, fs, rs, &block)
+ elsif mode == 'w' or mode == 'wb'
+ open_writer(path, mode, fs, rs, &block)
+ else
+ raise ArgumentError.new("'mode' must be 'r', 'rb', 'w', or 'wb'")
+ end
+ end
+
+ def CSV.foreach(path, rs = nil, &block)
+ open_reader(path, 'r', ',', rs, &block)
+ end
+
+ def CSV.read(path, length = nil, offset = nil)
+ CSV.parse(IO.read(path, length, offset))
+ end
+
+ def CSV.readlines(path, rs = nil)
+ reader = open_reader(path, 'r', ',', rs)
+ begin
+ reader.collect { |row| row }
+ ensure
+ reader.close
+ end
+ end
+
+ def CSV.generate(path, fs = nil, rs = nil, &block)
+ open_writer(path, 'w', fs, rs, &block)
+ end
+
+ # Parse lines from given string or stream. Return rows as an Array of Arrays.
+ def CSV.parse(str_or_readable, fs = nil, rs = nil, &block)
+ if File.exist?(str_or_readable)
+ STDERR.puts("CSV.parse(filename) is deprecated." +
+ " Use CSV.open(filename, 'r') instead.")
+ return open_reader(str_or_readable, 'r', fs, rs, &block)
+ end
+ if block
+ CSV::Reader.parse(str_or_readable, fs, rs) do |row|
+ yield(row)
+ end
+ nil
+ else
+ CSV::Reader.create(str_or_readable, fs, rs).collect { |row| row }
+ end
+ end
+
+ # Parse a line from given string. Bear in mind it parses ONE LINE. Rest of
+ # the string is ignored for example "a,b\r\nc,d" => ['a', 'b'] and the
+ # second line 'c,d' is ignored.
+ #
+ # If you don't know whether a target string to parse is exactly 1 line or
+ # not, use CSV.parse_row instead of this method.
+ def CSV.parse_line(src, fs = nil, rs = nil)
+ fs ||= ','
+ if fs.is_a?(Fixnum)
+ fs = fs.chr
+ end
+ if !rs.nil? and rs.is_a?(Fixnum)
+ rs = rs.chr
+ end
+ idx = 0
+ res_type = :DT_COLSEP
+ row = []
+ begin
+ while res_type == :DT_COLSEP
+ res_type, idx, cell = parse_body(src, idx, fs, rs)
+ row << cell
+ end
+ rescue IllegalFormatError
+ return []
+ end
+ row
+ end
+
+ # Create a line from cells. each cell is stringified by to_s.
+ def CSV.generate_line(row, fs = nil, rs = nil)
+ if row.size == 0
+ return ''
+ end
+ fs ||= ','
+ if fs.is_a?(Fixnum)
+ fs = fs.chr
+ end
+ if !rs.nil? and rs.is_a?(Fixnum)
+ rs = rs.chr
+ end
+ res_type = :DT_COLSEP
+ result_str = ''
+ idx = 0
+ while true
+ generate_body(row[idx], result_str, fs, rs)
+ idx += 1
+ if (idx == row.size)
+ break
+ end
+ generate_separator(:DT_COLSEP, result_str, fs, rs)
+ end
+ result_str
+ end
+
+ # Parse a line from string. Consider using CSV.parse_line instead.
+ # To parse lines in CSV string, see EXAMPLE below.
+ #
+ # EXAMPLE
+ # src = "a,b\r\nc,d\r\ne,f"
+ # idx = 0
+ # begin
+ # parsed = []
+ # parsed_cells, idx = CSV.parse_row(src, idx, parsed)
+ # puts "Parsed #{ parsed_cells } cells."
+ # p parsed
+ # end while parsed_cells > 0
+ #
+ # ARGS
+ # src: a CSV data to be parsed. Must respond '[](idx)'.
+ # src[](idx) must return a char. (Not a string such as 'a', but 97).
+ # src[](idx_out_of_bounds) must return nil. A String satisfies this
+ # requirement.
+ # idx: index of parsing location of 'src'. 0 origin.
+ # out_dev: buffer for parsed cells. Must respond '<<(aString)'.
+ # col_sep: Column separator. ?, by default. If you want to separate
+ # fields with semicolon, give ?; here.
+ # row_sep: Row separator. nil by default. nil means "\r\n or \n". If you
+ # want to separate records with \r, give ?\r here.
+ #
+ # RETURNS
+ # parsed_cells: num of parsed cells.
+ # idx: index of next parsing location of 'src'.
+ #
+ def CSV.parse_row(src, idx, out_dev, fs = nil, rs = nil)
+ fs ||= ','
+ if fs.is_a?(Fixnum)
+ fs = fs.chr
+ end
+ if !rs.nil? and rs.is_a?(Fixnum)
+ rs = rs.chr
+ end
+ idx_backup = idx
+ parsed_cells = 0
+ res_type = :DT_COLSEP
+ begin
+ while res_type != :DT_ROWSEP
+ res_type, idx, cell = parse_body(src, idx, fs, rs)
+ if res_type == :DT_EOS
+ if idx == idx_backup #((parsed_cells == 0) and cell.nil?)
+ return 0, 0
+ end
+ res_type = :DT_ROWSEP
+ end
+ parsed_cells += 1
+ out_dev << cell
+ end
+ rescue IllegalFormatError
+ return 0, 0
+ end
+ return parsed_cells, idx
+ end
+
+ # Convert a line from cells data to string. Consider using CSV.generate_line
+ # instead. To generate multi-row CSV string, see EXAMPLE below.
+ #
+ # EXAMPLE
+ # row1 = ['a', 'b']
+ # row2 = ['c', 'd']
+ # row3 = ['e', 'f']
+ # src = [row1, row2, row3]
+ # buf = ''
+ # src.each do |row|
+ # parsed_cells = CSV.generate_row(row, 2, buf)
+ # puts "Created #{ parsed_cells } cells."
+ # end
+ # p buf
+ #
+ # ARGS
+ # src: an Array of String to be converted to CSV string. Must respond to
+ # 'size' and '[](idx)'. src[idx] must return String.
+ # cells: num of cells in a line.
+ # out_dev: buffer for generated CSV string. Must respond to '<<(string)'.
+ # col_sep: Column separator. ?, by default. If you want to separate
+ # fields with semicolon, give ?; here.
+ # row_sep: Row separator. nil by default. nil means "\r\n or \n". If you
+ # want to separate records with \r, give ?\r here.
+ #
+ # RETURNS
+ # parsed_cells: num of converted cells.
+ #
+ def CSV.generate_row(src, cells, out_dev, fs = nil, rs = nil)
+ fs ||= ','
+ if fs.is_a?(Fixnum)
+ fs = fs.chr
+ end
+ if !rs.nil? and rs.is_a?(Fixnum)
+ rs = rs.chr
+ end
+ src_size = src.size
+ if (src_size == 0)
+ if cells == 0
+ generate_separator(:DT_ROWSEP, out_dev, fs, rs)
+ end
+ return 0
+ end
+ res_type = :DT_COLSEP
+ parsed_cells = 0
+ generate_body(src[parsed_cells], out_dev, fs, rs)
+ parsed_cells += 1
+ while ((parsed_cells < cells) and (parsed_cells != src_size))
+ generate_separator(:DT_COLSEP, out_dev, fs, rs)
+ generate_body(src[parsed_cells], out_dev, fs, rs)
+ parsed_cells += 1
+ end
+ if (parsed_cells == cells)
+ generate_separator(:DT_ROWSEP, out_dev, fs, rs)
+ else
+ generate_separator(:DT_COLSEP, out_dev, fs, rs)
+ end
+ parsed_cells
+ end
+
+ # Private class methods.
+ class << self
+ private
+
+ def open_reader(path, mode, fs, rs, &block)
+ file = File.open(path, mode)
+ if block
+ begin
+ CSV::Reader.parse(file, fs, rs) do |row|
+ yield(row)
+ end
+ ensure
+ file.close
+ end
+ nil
+ else
+ reader = CSV::Reader.create(file, fs, rs)
+ reader.close_on_terminate
+ reader
+ end
+ end
+
+ def open_writer(path, mode, fs, rs, &block)
+ file = File.open(path, mode)
+ if block
+ begin
+ CSV::Writer.generate(file, fs, rs) do |writer|
+ yield(writer)
+ end
+ ensure
+ file.close
+ end
+ nil
+ else
+ writer = CSV::Writer.create(file, fs, rs)
+ writer.close_on_terminate
+ writer
+ end
+ end
+
+ def parse_body(src, idx, fs, rs)
+ fs_str = fs
+ fs_size = fs_str.size
+ rs_str = rs || "\n"
+ rs_size = rs_str.size
+ fs_idx = rs_idx = 0
+ cell = Cell.new
+ state = :ST_START
+ quoted = cr = false
+ c = nil
+ last_idx = idx
+ while c = src[idx]
+ unless quoted
+ fschar = (c == fs_str[fs_idx])
+ rschar = (c == rs_str[rs_idx])
+ # simple 1 char backtrack
+ if !fschar and c == fs_str[0]
+ fs_idx = 0
+ fschar = true
+ if state == :ST_START
+ state = :ST_DATA
+ elsif state == :ST_QUOTE
+ raise IllegalFormatError
+ end
+ end
+ if !rschar and c == rs_str[0]
+ rs_idx = 0
+ rschar = true
+ if state == :ST_START
+ state = :ST_DATA
+ elsif state == :ST_QUOTE
+ raise IllegalFormatError
+ end
+ end
+ end
+ if c == ?"
+ fs_idx = rs_idx = 0
+ if cr
+ raise IllegalFormatError
+ end
+ cell << src[last_idx, (idx - last_idx)]
+ last_idx = idx
+ if state == :ST_DATA
+ if quoted
+ last_idx += 1
+ quoted = false
+ state = :ST_QUOTE
+ else
+ raise IllegalFormatError
+ end
+ elsif state == :ST_QUOTE
+ cell << c.chr
+ last_idx += 1
+ quoted = true
+ state = :ST_DATA
+ else # :ST_START
+ quoted = true
+ last_idx += 1
+ state = :ST_DATA
+ end
+ elsif fschar or rschar
+ if fschar
+ fs_idx += 1
+ end
+ if rschar
+ rs_idx += 1
+ end
+ sep = nil
+ if fs_idx == fs_size
+ if state == :ST_START and rs_idx > 0 and fs_idx < rs_idx
+ state = :ST_DATA
+ end
+ cell << src[last_idx, (idx - last_idx - (fs_size - 1))]
+ last_idx = idx
+ fs_idx = rs_idx = 0
+ if cr
+ raise IllegalFormatError
+ end
+ sep = :DT_COLSEP
+ elsif rs_idx == rs_size
+ if state == :ST_START and fs_idx > 0 and rs_idx < fs_idx
+ state = :ST_DATA
+ end
+ if !(rs.nil? and cr)
+ cell << src[last_idx, (idx - last_idx - (rs_size - 1))]
+ last_idx = idx
+ end
+ fs_idx = rs_idx = 0
+ sep = :DT_ROWSEP
+ end
+ if sep
+ if state == :ST_DATA
+ return sep, idx + 1, cell;
+ elsif state == :ST_QUOTE
+ return sep, idx + 1, cell;
+ else # :ST_START
+ return sep, idx + 1, nil
+ end
+ end
+ elsif rs.nil? and c == ?\r
+ # special \r treatment for backward compatibility
+ fs_idx = rs_idx = 0
+ if cr
+ raise IllegalFormatError
+ end
+ cell << src[last_idx, (idx - last_idx)]
+ last_idx = idx
+ if quoted
+ state = :ST_DATA
+ else
+ cr = true
+ end
+ else
+ fs_idx = rs_idx = 0
+ if state == :ST_DATA or state == :ST_START
+ if cr
+ raise IllegalFormatError
+ end
+ state = :ST_DATA
+ else # :ST_QUOTE
+ raise IllegalFormatError
+ end
+ end
+ idx += 1
+ end
+ if state == :ST_START
+ if fs_idx > 0 or rs_idx > 0
+ state = :ST_DATA
+ else
+ return :DT_EOS, idx, nil
+ end
+ elsif quoted
+ raise IllegalFormatError
+ elsif cr
+ raise IllegalFormatError
+ end
+ cell << src[last_idx, (idx - last_idx)]
+ last_idx = idx
+ return :DT_EOS, idx, cell
+ end
+
+ def generate_body(cell, out_dev, fs, rs)
+ if cell.nil?
+ # empty
+ else
+ cell = cell.to_s
+ row_data = cell.dup
+ if (row_data.gsub!('"', '""') or
+ row_data.index(fs) or
+ (rs and row_data.index(rs)) or
+ (/[\r\n]/ =~ row_data) or
+ (cell.empty?))
+ out_dev << '"' << row_data << '"'
+ else
+ out_dev << row_data
+ end
+ end
+ end
+
+ def generate_separator(type, out_dev, fs, rs)
+ case type
+ when :DT_COLSEP
+ out_dev << fs
+ when :DT_ROWSEP
+ out_dev << (rs || "\n")
+ end
+ end
+ end
+
+
+ # CSV formatted string/stream reader.
+ #
+ # EXAMPLE
+ # read CSV lines untill the first column is 'stop'.
+ #
+ # CSV::Reader.parse(File.open('bigdata', 'rb')) do |row|
+ # p row
+ # break if !row[0].is_null && row[0].data == 'stop'
+ # end
+ #
+ class Reader
+ include Enumerable
+
+ # Parse CSV data and get lines. Given block is called for each parsed row.
+ # Block value is always nil. Rows are not cached for performance reason.
+ def Reader.parse(str_or_readable, fs = ',', rs = nil, &block)
+ reader = Reader.create(str_or_readable, fs, rs)
+ if block
+ reader.each do |row|
+ yield(row)
+ end
+ reader.close
+ nil
+ else
+ reader
+ end
+ end
+
+ # Returns reader instance.
+ def Reader.create(str_or_readable, fs = ',', rs = nil)
+ case str_or_readable
+ when IO
+ IOReader.new(str_or_readable, fs, rs)
+ when String
+ StringReader.new(str_or_readable, fs, rs)
+ else
+ IOReader.new(str_or_readable, fs, rs)
+ end
+ end
+
+ def each
+ while true
+ row = []
+ parsed_cells = get_row(row)
+ if parsed_cells == 0
+ break
+ end
+ yield(row)
+ end
+ nil
+ end
+
+ def shift
+ row = []
+ parsed_cells = get_row(row)
+ row
+ end
+
+ def close
+ terminate
+ end
+
+ private
+
+ def initialize(dev)
+ raise RuntimeError.new('Do not instanciate this class directly.')
+ end
+
+ def get_row(row)
+ raise NotImplementedError.new('Method get_row must be defined in a derived class.')
+ end
+
+ def terminate
+ # Define if needed.
+ end
+ end
+
+
+ class StringReader < Reader
+ def initialize(string, fs = ',', rs = nil)
+ @fs = fs
+ @rs = rs
+ @dev = string
+ @idx = 0
+ if @dev[0, 3] == "\xef\xbb\xbf"
+ @idx += 3
+ end
+ end
+
+ private
+
+ def get_row(row)
+ parsed_cells, next_idx = CSV.parse_row(@dev, @idx, row, @fs, @rs)
+ if parsed_cells == 0 and next_idx == 0 and @idx != @dev.size
+ raise IllegalFormatError.new
+ end
+ @idx = next_idx
+ parsed_cells
+ end
+ end
+
+
+ class IOReader < Reader
+ def initialize(io, fs = ',', rs = nil)
+ @io = io
+ @fs = fs
+ @rs = rs
+ @dev = CSV::IOBuf.new(@io)
+ @idx = 0
+ if @dev[0] == 0xef and @dev[1] == 0xbb and @dev[2] == 0xbf
+ @idx += 3
+ end
+ @close_on_terminate = false
+ end
+
+ # Tell this reader to close the IO when terminated (Triggered by invoking
+ # CSV::IOReader#close).
+ def close_on_terminate
+ @close_on_terminate = true
+ end
+
+ private
+
+ def get_row(row)
+ parsed_cells, next_idx = CSV.parse_row(@dev, @idx, row, @fs, @rs)
+ if parsed_cells == 0 and next_idx == 0 and !@dev.is_eos?
+ raise IllegalFormatError.new
+ end
+ dropped = @dev.drop(next_idx)
+ @idx = next_idx - dropped
+ parsed_cells
+ end
+
+ def terminate
+ if @close_on_terminate
+ @io.close
+ end
+
+ if @dev
+ @dev.close
+ end
+ end
+ end
+
+
+ # CSV formatted string/stream writer.
+ #
+ # EXAMPLE
+ # Write rows to 'csvout' file.
+ #
+ # outfile = File.open('csvout', 'wb')
+ # CSV::Writer.generate(outfile) do |csv|
+ # csv << ['c1', nil, '', '"', "\r\n", 'c2']
+ # ...
+ # end
+ #
+ # outfile.close
+ #
+ class Writer
+ # Given block is called with the writer instance. str_or_writable must
+ # handle '<<(string)'.
+ def Writer.generate(str_or_writable, fs = ',', rs = nil, &block)
+ writer = Writer.create(str_or_writable, fs, rs)
+ if block
+ yield(writer)
+ writer.close
+ nil
+ else
+ writer
+ end
+ end
+
+ # str_or_writable must handle '<<(string)'.
+ def Writer.create(str_or_writable, fs = ',', rs = nil)
+ BasicWriter.new(str_or_writable, fs, rs)
+ end
+
+ # dump CSV stream to the device. argument must be an Array of String.
+ def <<(row)
+ CSV.generate_row(row, row.size, @dev, @fs, @rs)
+ self
+ end
+ alias add_row <<
+
+ def close
+ terminate
+ end
+
+ private
+
+ def initialize(dev)
+ raise RuntimeError.new('Do not instanciate this class directly.')
+ end
+
+ def terminate
+ # Define if needed.
+ end
+ end
+
+
+ class BasicWriter < Writer
+ def initialize(str_or_writable, fs = ',', rs = nil)
+ @fs = fs
+ @rs = rs
+ @dev = str_or_writable
+ @close_on_terminate = false
+ end
+
+ # Tell this writer to close the IO when terminated (Triggered by invoking
+ # CSV::BasicWriter#close).
+ def close_on_terminate
+ @close_on_terminate = true
+ end
+
+ private
+
+ def terminate
+ if @close_on_terminate
+ @dev.close
+ end
+ end
+ end
+
+private
+
+ # Buffered stream.
+ #
+ # EXAMPLE 1 -- an IO.
+ # class MyBuf < StreamBuf
+ # # Do initialize myself before a super class. Super class might call my
+ # # method 'read'. (Could be awful for C++ user. :-)
+ # def initialize(s)
+ # @s = s
+ # super()
+ # end
+ #
+ # # define my own 'read' method.
+ # # CAUTION: Returning nil means EnfOfStream.
+ # def read(size)
+ # @s.read(size)
+ # end
+ #
+ # # release buffers. in Ruby which has GC, you do not have to call this...
+ # def terminate
+ # @s = nil
+ # super()
+ # end
+ # end
+ #
+ # buf = MyBuf.new(STDIN)
+ # my_str = ''
+ # p buf[0, 0] # => '' (null string)
+ # p buf[0] # => 97 (char code of 'a')
+ # p buf[0, 1] # => 'a'
+ # my_str = buf[0, 5]
+ # p my_str # => 'abcde' (5 chars)
+ # p buf[0, 6] # => "abcde\n" (6 chars)
+ # p buf[0, 7] # => "abcde\n" (6 chars)
+ # p buf.drop(3) # => 3 (dropped chars)
+ # p buf.get(0, 2) # => 'de' (2 chars)
+ # p buf.is_eos? # => false (is not EOS here)
+ # p buf.drop(5) # => 3 (dropped chars)
+ # p buf.is_eos? # => true (is EOS here)
+ # p buf[0] # => nil (is EOS here)
+ #
+ # EXAMPLE 2 -- String.
+ # This is a conceptual example. No pros with this.
+ #
+ # class StrBuf < StreamBuf
+ # def initialize(s)
+ # @str = s
+ # @idx = 0
+ # super()
+ # end
+ #
+ # def read(size)
+ # str = @str[@idx, size]
+ # @idx += str.size
+ # str
+ # end
+ # end
+ #
+ class StreamBuf
+ # get a char or a partial string from the stream.
+ # idx: index of a string to specify a start point of a string to get.
+ # unlike String instance, idx < 0 returns nil.
+ # n: size of a string to get.
+ # returns char at idx if n == nil.
+ # returns a partial string, from idx to (idx + n) if n != nil. at EOF,
+ # the string size could not equal to arg n.
+ def [](idx, n = nil)
+ if idx < 0
+ return nil
+ end
+ if (idx_is_eos?(idx))
+ if n and (@offset + idx == buf_size(@cur_buf))
+ # Like a String, 'abc'[4, 1] returns nil and
+ # 'abc'[3, 1] returns '' not nil.
+ return ''
+ else
+ return nil
+ end
+ end
+ my_buf = @cur_buf
+ my_offset = @offset
+ next_idx = idx
+ while (my_offset + next_idx >= buf_size(my_buf))
+ if (my_buf == @buf_tail_idx)
+ unless add_buf
+ break
+ end
+ end
+ next_idx = my_offset + next_idx - buf_size(my_buf)
+ my_buf += 1
+ my_offset = 0
+ end
+ loc = my_offset + next_idx
+ if !n
+ return @buf_list[my_buf][loc] # Fixnum of char code.
+ elsif (loc + n - 1 < buf_size(my_buf))
+ return @buf_list[my_buf][loc, n] # String.
+ else # should do loop insted of (tail) recursive call...
+ res = @buf_list[my_buf][loc, BufSize]
+ size_added = buf_size(my_buf) - loc
+ if size_added > 0
+ idx += size_added
+ n -= size_added
+ ret = self[idx, n]
+ if ret
+ res << ret
+ end
+ end
+ return res
+ end
+ end
+ alias get []
+
+ # drop a string from the stream.
+ # returns dropped size. at EOF, dropped size might not equals to arg n.
+ # Once you drop the head of the stream, access to the dropped part via []
+ # or get returns nil.
+ def drop(n)
+ if is_eos?
+ return 0
+ end
+ size_dropped = 0
+ while (n > 0)
+ if !@is_eos or (@cur_buf != @buf_tail_idx)
+ if (@offset + n < buf_size(@cur_buf))
+ size_dropped += n
+ @offset += n
+ n = 0
+ else
+ size = buf_size(@cur_buf) - @offset
+ size_dropped += size
+ n -= size
+ @offset = 0
+ unless rel_buf
+ unless add_buf
+ break
+ end
+ @cur_buf = @buf_tail_idx
+ end
+ end
+ end
+ end
+ size_dropped
+ end
+
+ def is_eos?
+ return idx_is_eos?(0)
+ end
+
+ # WARN: Do not instantiate this class directly. Define your own class
+ # which derives this class and define 'read' instance method.
+ def initialize
+ @buf_list = []
+ @cur_buf = @buf_tail_idx = -1
+ @offset = 0
+ @is_eos = false
+ add_buf
+ @cur_buf = @buf_tail_idx
+ end
+
+ protected
+
+ def terminate
+ while (rel_buf); end
+ end
+
+ # protected method 'read' must be defined in derived classes.
+ # CAUTION: Returning a string which size is not equal to 'size' means
+ # EnfOfStream. When it is not at EOS, you must block the callee, try to
+ # read and return the sized string.
+ def read(size) # raise EOFError
+ raise NotImplementedError.new('Method read must be defined in a derived class.')
+ end
+
+ private
+
+ def buf_size(idx)
+ @buf_list[idx].size
+ end
+
+ def add_buf
+ if @is_eos
+ return false
+ end
+ begin
+ str_read = read(BufSize)
+ rescue EOFError
+ str_read = nil
+ rescue
+ terminate
+ raise
+ end
+ if str_read.nil?
+ @is_eos = true
+ @buf_list.push('')
+ @buf_tail_idx += 1
+ false
+ else
+ @buf_list.push(str_read)
+ @buf_tail_idx += 1
+ true
+ end
+ end
+
+ def rel_buf
+ if (@cur_buf < 0)
+ return false
+ end
+ @buf_list[@cur_buf] = nil
+ if (@cur_buf == @buf_tail_idx)
+ @cur_buf = -1
+ return false
+ else
+ @cur_buf += 1
+ return true
+ end
+ end
+
+ def idx_is_eos?(idx)
+ (@is_eos and ((@cur_buf < 0) or (@cur_buf == @buf_tail_idx)))
+ end
+
+ BufSize = 1024 * 8
+ end
+
+ # Buffered IO.
+ #
+ # EXAMPLE
+ # # File 'bigdata' could be a giga-byte size one!
+ # buf = CSV::IOBuf.new(File.open('bigdata', 'rb'))
+ # CSV::Reader.new(buf).each do |row|
+ # p row
+ # break if row[0].data == 'admin'
+ # end
+ #
+ class IOBuf < StreamBuf
+ def initialize(s)
+ @s = s
+ super()
+ end
+
+ def close
+ terminate
+ end
+
+ private
+
+ def read(size)
+ @s.read(size)
+ end
+
+ def terminate
+ super()
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/date.rb b/ruby_1_8_5/lib/date.rb
new file mode 100644
index 0000000000..8639b31575
--- /dev/null
+++ b/ruby_1_8_5/lib/date.rb
@@ -0,0 +1,1328 @@
+#
+# date.rb - date and time library
+#
+# Author: Tadayoshi Funaba 1998-2006
+#
+# Documentation: William Webber <william@williamwebber.com>
+#
+#--
+# $Id: date.rb,v 2.15 2005-02-06 11:09:53+09 tadf Exp $
+#++
+#
+# == Overview
+#
+# This file provides two classes for working with
+# dates and times.
+#
+# The first class, Date, represents dates.
+# It works with years, months, weeks, and days.
+# See the Date class documentation for more details.
+#
+# The second, DateTime, extends Date to include hours,
+# minutes, seconds, and fractions of a second. It
+# provides basic support for time zones. See the
+# DateTime class documentation for more details.
+#
+# === Ways of calculating the date.
+#
+# In common usage, the date is reckoned in years since or
+# before the Common Era (CE/BCE, also known as AD/BC), then
+# as a month and day-of-the-month within the current year.
+# This is known as the *Civil* *Date*, and abbreviated
+# as +civil+ in the Date class.
+#
+# Instead of year, month-of-the-year, and day-of-the-month,
+# the date can also be reckoned in terms of year and
+# day-of-the-year. This is known as the *Ordinal* *Date*,
+# and is abbreviated as +ordinal+ in the Date class. (Note
+# that referring to this as the Julian date is incorrect.)
+#
+# The date can also be reckoned in terms of year, week-of-the-year,
+# and day-of-the-week. This is known as the *Commercial*
+# *Date*, and is abbreviated as +commercial+ in the
+# Date class. The commercial week runs Monday (day-of-the-week
+# 1) to Sunday (day-of-the-week 7), in contrast to the civil
+# week which runs Sunday (day-of-the-week 0) to Saturday
+# (day-of-the-week 6). The first week of the commercial year
+# starts on the Monday on or before January 1, and the commercial
+# year itself starts on this Monday, not January 1.
+#
+# For scientific purposes, it is convenient to refer to a date
+# simply as a day count, counting from an arbitrary initial
+# day. The date first chosen for this was January 1, 4713 BCE.
+# A count of days from this date is the *Julian* *Day* *Number*
+# or *Julian* *Date*, which is abbreviated as +jd+ in the
+# Date class. This is in local time, and counts from midnight
+# on the initial day. The stricter usage is in UTC, and counts
+# from midday on the initial day. This is referred to in the
+# Date class as the *Astronomical* *Julian* *Day* *Number*, and
+# abbreviated as +ajd+. In the Date class, the Astronomical
+# Julian Day Number includes fractional days.
+#
+# Another absolute day count is the *Modified* *Julian* *Day*
+# *Number*, which takes November 17, 1858 as its initial day.
+# This is abbreviated as +mjd+ in the Date class. There
+# is also an *Astronomical* *Modified* *Julian* *Day* *Number*,
+# which is in UTC and includes fractional days. This is
+# abbreviated as +amjd+ in the Date class. Like the Modified
+# Julian Day Number (and unlike the Astronomical Julian
+# Day Number), it counts from midnight.
+#
+# Alternative calendars such as the Chinese Lunar Calendar,
+# the Islamic Calendar, or the French Revolutionary Calendar
+# are not supported by the Date class; nor are calendars that
+# are based on an Era different from the Common Era, such as
+# the Japanese Imperial Calendar or the Republic of China
+# Calendar.
+#
+# === Calendar Reform
+#
+# The standard civil year is 365 days long. However, the
+# solar year is fractionally longer than this. To account
+# for this, a *leap* *year* is occasionally inserted. This
+# is a year with 366 days, the extra day falling on February 29.
+# In the early days of the civil calendar, every fourth
+# year without exception was a leap year. This way of
+# reckoning leap years is the *Julian* *Calendar*.
+#
+# However, the solar year is marginally shorter than 365 1/4
+# days, and so the *Julian* *Calendar* gradually ran slow
+# over the centuries. To correct this, every 100th year
+# (but not every 400th year) was excluded as a leap year.
+# This way of reckoning leap years, which we use today, is
+# the *Gregorian* *Calendar*.
+#
+# The Gregorian Calendar was introduced at different times
+# in different regions. The day on which it was introduced
+# for a particular region is the *Day* *of* *Calendar*
+# *Reform* for that region. This is abbreviated as +sg+
+# (for Start of Gregorian calendar) in the Date class.
+#
+# Two such days are of particular
+# significance. The first is October 15, 1582, which was
+# the Day of Calendar Reform for Italy and most Catholic
+# countries. The second is September 14, 1752, which was
+# the Day of Calendar Reform for England and its colonies
+# (including what is now the United States). These two
+# dates are available as the constants Date::ITALY and
+# Date::ENGLAND, respectively. (By comparison, Germany and
+# Holland, less Catholic than Italy but less stubborn than
+# England, changed over in 1698; Sweden in 1753; Russia not
+# till 1918, after the Revolution; and Greece in 1923. Many
+# Orthodox churches still use the Julian Calendar. A complete
+# list of Days of Calendar Reform can be found at
+# http://www.polysyllabic.com/GregConv.html.)
+#
+# Switching from the Julian to the Gregorian calendar
+# involved skipping a number of days to make up for the
+# accumulated lag, and the later the switch was (or is)
+# done, the more days need to be skipped. So in 1582 in Italy,
+# 4th October was followed by 15th October, skipping 10 days; in 1752
+# in England, 2nd September was followed by 14th September, skipping
+# 11 days; and if I decided to switch from Julian to Gregorian
+# Calendar this midnight, I would go from 27th July 2003 (Julian)
+# today to 10th August 2003 (Gregorian) tomorrow, skipping
+# 13 days. The Date class is aware of this gap, and a supposed
+# date that would fall in the middle of it is regarded as invalid.
+#
+# The Day of Calendar Reform is relevant to all date representations
+# involving years. It is not relevant to the Julian Day Numbers,
+# except for converting between them and year-based representations.
+#
+# In the Date and DateTime classes, the Day of Calendar Reform or
+# +sg+ can be specified a number of ways. First, it can be as
+# the Julian Day Number of the Day of Calendar Reform. Second,
+# it can be using the constants Date::ITALY or Date::ENGLAND; these
+# are in fact the Julian Day Numbers of the Day of Calendar Reform
+# of the respective regions. Third, it can be as the constant
+# Date::JULIAN, which means to always use the Julian Calendar.
+# Finally, it can be as the constant Date::GREGORIAN, which means
+# to always use the Gregorian Calendar.
+#
+# Note: in the Julian Calendar, New Years Day was March 25. The
+# Date class does not follow this convention.
+#
+# === Time Zones
+#
+# DateTime objects support a simple representation
+# of time zones. Time zones are represented as an offset
+# from UTC, as a fraction of a day. This offset is the
+# how much local time is later (or earlier) than UTC.
+# UTC offset 0 is centred on England (also known as GMT).
+# As you travel east, the offset increases until you
+# reach the dateline in the middle of the Pacific Ocean;
+# as you travel west, the offset decreases. This offset
+# is abbreviated as +of+ in the Date class.
+#
+# This simple representation of time zones does not take
+# into account the common practice of Daylight Savings
+# Time or Summer Time.
+#
+# Most DateTime methods return the date and the
+# time in local time. The two exceptions are
+# #ajd() and #amjd(), which return the date and time
+# in UTC time, including fractional days.
+#
+# The Date class does not support time zone offsets, in that
+# there is no way to create a Date object with a time zone.
+# However, methods of the Date class when used by a
+# DateTime instance will use the time zone offset of this
+# instance.
+#
+# == Examples of use
+#
+# === Print out the date of every Sunday between two dates.
+#
+# def print_sundays(d1, d2)
+# d1 +=1 while (d1.wday != 0)
+# d1.step(d2, 7) do |date|
+# puts "#{Date::MONTHNAMES[date.mon]} #{date.day}"
+# end
+# end
+#
+# print_sundays(Date::civil(2003, 4, 8), Date::civil(2003, 5, 23))
+#
+# === Calculate how many seconds to go till midnight on New Year's Day.
+#
+# def secs_to_new_year(now = DateTime::now())
+# new_year = DateTime.new(now.year + 1, 1, 1)
+# dif = new_year - now
+# hours, mins, secs, ignore_fractions = Date::day_fraction_to_time(dif)
+# return hours * 60 * 60 + mins * 60 + secs
+# end
+#
+# puts secs_to_new_year()
+
+require 'rational'
+require 'date/format'
+
+# Class representing a date.
+#
+# See the documentation to the file date.rb for an overview.
+#
+# Internally, the date is represented as an Astronomical
+# Julian Day Number, +ajd+. The Day of Calendar Reform, +sg+, is
+# also stored, for conversions to other date formats. (There
+# is also an +of+ field for a time zone offset, but this
+# is only for the use of the DateTime subclass.)
+#
+# A new Date object is created using one of the object creation
+# class methods named after the corresponding date format, and the
+# arguments appropriate to that date format; for instance,
+# Date::civil() (aliased to Date::new()) with year, month,
+# and day-of-month, or Date::ordinal() with year and day-of-year.
+# All of these object creation class methods also take the
+# Day of Calendar Reform as an optional argument.
+#
+# Date objects are immutable once created.
+#
+# Once a Date has been created, date values
+# can be retrieved for the different date formats supported
+# using instance methods. For instance, #mon() gives the
+# Civil month, #cwday() gives the Commercial day of the week,
+# and #yday() gives the Ordinal day of the year. Date values
+# can be retrieved in any format, regardless of what format
+# was used to create the Date instance.
+#
+# The Date class includes the Comparable module, allowing
+# date objects to be compared and sorted, ranges of dates
+# to be created, and so forth.
+class Date
+
+ include Comparable
+
+ # Full month names, in English. Months count from 1 to 12; a
+ # month's numerical representation indexed into this array
+ # gives the name of that month (hence the first element is nil).
+ MONTHNAMES = [nil] + %w(January February March April May June July
+ August September October November December)
+
+ # Full names of days of the week, in English. Days of the week
+ # count from 0 to 6 (except in the commercial week); a day's numerical
+ # representation indexed into this array gives the name of that day.
+ DAYNAMES = %w(Sunday Monday Tuesday Wednesday Thursday Friday Saturday)
+
+ # Abbreviated month names, in English.
+ ABBR_MONTHNAMES = [nil] + %w(Jan Feb Mar Apr May Jun
+ Jul Aug Sep Oct Nov Dec)
+
+ # Abbreviated day names, in English.
+ ABBR_DAYNAMES = %w(Sun Mon Tue Wed Thu Fri Sat)
+
+ # The Julian Day Number of the Day of Calendar Reform for Italy
+ # and the Catholic countries.
+ ITALY = 2299161 # 1582-10-15
+
+ # The Julian Day Number of the Day of Calendar Reform for England
+ # and her Colonies.
+ ENGLAND = 2361222 # 1752-09-14
+
+ # A constant used to indicate that a Date should always use the
+ # Julian calendar.
+ JULIAN = false
+
+ # A constant used to indicate that a Date should always use the
+ # Gregorian calendar.
+ GREGORIAN = true
+
+ # Does a given Julian Day Number fall inside the old-style (Julian)
+ # calendar?
+ #
+ # +jd+ is the Julian Day Number in question. +sg+ may be Date::GREGORIAN,
+ # in which case the answer is false; it may be Date::JULIAN, in which case
+ # the answer is true; or it may a number representing the Day of
+ # Calendar Reform. Date::ENGLAND and Date::ITALY are two possible such
+ # days.
+ def self.os? (jd, sg)
+ case sg
+ when Numeric; jd < sg
+ else; not sg
+ end
+ end
+
+ # Does a given Julian Day Number fall inside the new-style (Gregorian)
+ # calendar?
+ #
+ # The reverse of self.os? See the documentation for that method for
+ # more details.
+ def self.ns? (jd, sg) not os?(jd, sg) end
+
+ # Convert a Civil Date to a Julian Day Number.
+ # +y+, +m+, and +d+ are the year, month, and day of the
+ # month. +sg+ specifies the Day of Calendar Reform.
+ #
+ # Returns the corresponding Julian Day Number.
+ def self.civil_to_jd(y, m, d, sg=GREGORIAN)
+ if m <= 2
+ y -= 1
+ m += 12
+ end
+ a = (y / 100.0).floor
+ b = 2 - a + (a / 4.0).floor
+ jd = (365.25 * (y + 4716)).floor +
+ (30.6001 * (m + 1)).floor +
+ d + b - 1524
+ if os?(jd, sg)
+ jd -= b
+ end
+ jd
+ end
+
+ # Convert a Julian Day Number to a Civil Date. +jd+ is
+ # the Julian Day Number. +sg+ specifies the Day of
+ # Calendar Reform.
+ #
+ # Returns the corresponding [year, month, day_of_month]
+ # as a three-element array.
+ def self.jd_to_civil(jd, sg=GREGORIAN)
+ if os?(jd, sg)
+ a = jd
+ else
+ x = ((jd - 1867216.25) / 36524.25).floor
+ a = jd + 1 + x - (x / 4.0).floor
+ end
+ b = a + 1524
+ c = ((b - 122.1) / 365.25).floor
+ d = (365.25 * c).floor
+ e = ((b - d) / 30.6001).floor
+ dom = b - d - (30.6001 * e).floor
+ if e <= 13
+ m = e - 1
+ y = c - 4716
+ else
+ m = e - 13
+ y = c - 4715
+ end
+ return y, m, dom
+ end
+
+ # Convert an Ordinal Date to a Julian Day Number.
+ #
+ # +y+ and +d+ are the year and day-of-year to convert.
+ # +sg+ specifies the Day of Calendar Reform.
+ #
+ # Returns the corresponding Julian Day Number.
+ def self.ordinal_to_jd(y, d, sg=GREGORIAN)
+ civil_to_jd(y, 1, d, sg)
+ end
+
+ # Convert a Julian Day Number to an Ordinal Date.
+ #
+ # +jd+ is the Julian Day Number to convert.
+ # +sg+ specifies the Day of Calendar Reform.
+ #
+ # Returns the corresponding Ordinal Date as
+ # [year, day_of_year]
+ def self.jd_to_ordinal(jd, sg=GREGORIAN)
+ y = jd_to_civil(jd, sg)[0]
+ doy = jd - civil_to_jd(y - 1, 12, 31, ns?(jd, sg))
+ return y, doy
+ end
+
+ # Convert a Julian Day Number to a Commercial Date
+ #
+ # +jd+ is the Julian Day Number to convert.
+ # +sg+ specifies the Day of Calendar Reform.
+ #
+ # Returns the corresponding Commercial Date as
+ # [commercial_year, week_of_year, day_of_week]
+ def self.jd_to_commercial(jd, sg=GREGORIAN)
+ ns = ns?(jd, sg)
+ a = jd_to_civil(jd - 3, ns)[0]
+ y = if jd >= commercial_to_jd(a + 1, 1, 1, ns) then a + 1 else a end
+ w = 1 + ((jd - commercial_to_jd(y, 1, 1, ns)) / 7).floor
+ d = (jd + 1) % 7
+ if d.zero? then d = 7 end
+ return y, w, d
+ end
+
+ # Convert a Commercial Date to a Julian Day Number.
+ #
+ # +y+, +w+, and +d+ are the (commercial) year, week of the year,
+ # and day of the week of the Commercial Date to convert.
+ # +sg+ specifies the Day of Calendar Reform.
+ def self.commercial_to_jd(y, w, d, ns=GREGORIAN)
+ jd = civil_to_jd(y, 1, 4, ns)
+ (jd - (((jd - 1) + 1) % 7)) +
+ 7 * (w - 1) +
+ (d - 1)
+ end
+
+ %w(self.clfloor clfloor).each do |name|
+ module_eval <<-"end;"
+ def #{name}(x, y=1)
+ q, r = x.divmod(y)
+ q = q.to_i
+ return q, r
+ end
+ end;
+ end
+
+ private_class_method :clfloor
+ private :clfloor
+
+
+ # Convert an Astronomical Julian Day Number to a (civil) Julian
+ # Day Number.
+ #
+ # +ajd+ is the Astronomical Julian Day Number to convert.
+ # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
+ #
+ # Returns the (civil) Julian Day Number as [day_number,
+ # fraction] where +fraction+ is always 1/2.
+ def self.ajd_to_jd(ajd, of=0) clfloor(ajd + of + 1.to_r/2) end
+
+ # Convert a (civil) Julian Day Number to an Astronomical Julian
+ # Day Number.
+ #
+ # +jd+ is the Julian Day Number to convert, and +fr+ is a
+ # fractional day.
+ # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
+ #
+ # Returns the Astronomical Julian Day Number as a single
+ # numeric value.
+ def self.jd_to_ajd(jd, fr, of=0) jd + fr - of - 1.to_r/2 end
+
+ # Convert a fractional day +fr+ to [hours, minutes, seconds,
+ # fraction_of_a_second]
+ def self.day_fraction_to_time(fr)
+ h, fr = clfloor(fr, 1.to_r/24)
+ min, fr = clfloor(fr, 1.to_r/1440)
+ s, fr = clfloor(fr, 1.to_r/86400)
+ return h, min, s, fr
+ end
+
+ # Convert an +h+ hour, +min+ minutes, +s+ seconds period
+ # to a fractional day.
+ def self.time_to_day_fraction(h, min, s)
+ h.to_r/24 + min.to_r/1440 + s.to_r/86400
+ end
+
+ # Convert an Astronomical Modified Julian Day Number to an
+ # Astronomical Julian Day Number.
+ def self.amjd_to_ajd(amjd) amjd + 4800001.to_r/2 end
+
+ # Convert an Astronomical Julian Day Number to an
+ # Astronomical Modified Julian Day Number.
+ def self.ajd_to_amjd(ajd) ajd - 4800001.to_r/2 end
+
+ # Convert a Modified Julian Day Number to a Julian
+ # Day Number.
+ def self.mjd_to_jd(mjd) mjd + 2400001 end
+
+ # Convert a Julian Day Number to a Modified Julian Day
+ # Number.
+ def self.jd_to_mjd(jd) jd - 2400001 end
+
+ # Convert a count of the number of days since the adoption
+ # of the Gregorian Calendar (in Italy) to a Julian Day Number.
+ def self.ld_to_jd(ld) ld + 2299160 end
+
+ # Convert a Julian Day Number to the number of days since
+ # the adoption of the Gregorian Calendar (in Italy).
+ def self.jd_to_ld(jd) jd - 2299160 end
+
+ # Convert a Julian Day Number to the day of the week.
+ #
+ # Sunday is day-of-week 0; Saturday is day-of-week 6.
+ def self.jd_to_wday(jd) (jd + 1) % 7 end
+
+ # Is a year a leap year in the Julian calendar?
+ #
+ # All years divisible by 4 are leap years in the Julian calendar.
+ def self.julian_leap? (y) y % 4 == 0 end
+
+ # Is a year a leap year in the Gregorian calendar?
+ #
+ # All years divisible by 4 are leap years in the Gregorian calendar,
+ # except for years divisible by 100 and not by 400.
+ def self.gregorian_leap? (y) y % 4 == 0 and y % 100 != 0 or y % 400 == 0 end
+
+ class << self; alias_method :leap?, :gregorian_leap? end
+ class << self; alias_method :new0, :new end
+
+ # Is +jd+ a valid Julian Day Number?
+ #
+ # If it is, returns it. In fact, any value is treated as a valid
+ # Julian Day Number.
+ def self.valid_jd? (jd, sg=ITALY) jd end
+
+ # Create a new Date object from a Julian Day Number.
+ #
+ # +jd+ is the Julian Day Number; if not specified, it defaults to
+ # 0.
+ # +sg+ specifies the Day of Calendar Reform.
+ def self.jd(jd=0, sg=ITALY)
+ jd = valid_jd?(jd, sg)
+ new0(jd_to_ajd(jd, 0, 0), 0, sg)
+ end
+
+ # Do the year +y+ and day-of-year +d+ make a valid Ordinal Date?
+ # Returns the corresponding Julian Day Number if they do, or
+ # nil if they don't.
+ #
+ # +d+ can be a negative number, in which case it counts backwards
+ # from the end of the year (-1 being the last day of the year).
+ # No year wraparound is performed, however, so valid values of
+ # +d+ are -365 .. -1, 1 .. 365 on a non-leap-year,
+ # -366 .. -1, 1 .. 366 on a leap year.
+ # A date falling in the period skipped in the Day of Calendar Reform
+ # adjustment is not valid.
+ #
+ # +sg+ specifies the Day of Calendar Reform.
+ def self.valid_ordinal? (y, d, sg=ITALY)
+ if d < 0
+ ny, = clfloor(y + 1, 1)
+ jd = ordinal_to_jd(ny, d + 1, sg)
+ ns = ns?(jd, sg)
+ return unless [y] == jd_to_ordinal(jd, sg)[0..0]
+ return unless [ny, 1] == jd_to_ordinal(jd - d, ns)
+ else
+ jd = ordinal_to_jd(y, d, sg)
+ return unless [y, d] == jd_to_ordinal(jd, sg)
+ end
+ jd
+ end
+
+ # Create a new Date object from an Ordinal Date, specified
+ # by year +y+ and day-of-year +d+. +d+ can be negative,
+ # in which it counts backwards from the end of the year.
+ # No year wraparound is performed, however. An invalid
+ # value for +d+ results in an ArgumentError being raised.
+ #
+ # +y+ defaults to -4712, and +d+ to 1; this is Julian Day
+ # Number day 0.
+ #
+ # +sg+ specifies the Day of Calendar Reform.
+ def self.ordinal(y=-4712, d=1, sg=ITALY)
+ unless jd = valid_ordinal?(y, d, sg)
+ raise ArgumentError, 'invalid date'
+ end
+ new0(jd_to_ajd(jd, 0, 0), 0, sg)
+ end
+
+ # Do year +y+, month +m+, and day-of-month +d+ make a
+ # valid Civil Date? Returns the corresponding Julian
+ # Day Number if they do, nil if they don't.
+ #
+ # +m+ and +d+ can be negative, in which case they count
+ # backwards from the end of the year and the end of the
+ # month respectively. No wraparound is performed, however,
+ # and invalid values cause an ArgumentError to be raised.
+ # A date falling in the period skipped in the Day of Calendar
+ # Reform adjustment is not valid.
+ #
+ # +sg+ specifies the Day of Calendar Reform.
+ def self.valid_civil? (y, m, d, sg=ITALY)
+ if m < 0
+ m += 13
+ end
+ if d < 0
+ ny, nm = clfloor(y * 12 + m, 12)
+ nm, = clfloor(nm + 1, 1)
+ jd = civil_to_jd(ny, nm, d + 1, sg)
+ ns = ns?(jd, sg)
+ return unless [y, m] == jd_to_civil(jd, sg)[0..1]
+ return unless [ny, nm, 1] == jd_to_civil(jd - d, ns)
+ else
+ jd = civil_to_jd(y, m, d, sg)
+ return unless [y, m, d] == jd_to_civil(jd, sg)
+ end
+ jd
+ end
+
+ class << self; alias_method :valid_date?, :valid_civil? end
+
+ # Create a new Date object for the Civil Date specified by
+ # year +y+, month +m+, and day-of-month +d+.
+ #
+ # +m+ and +d+ can be negative, in which case they count
+ # backwards from the end of the year and the end of the
+ # month respectively. No wraparound is performed, however,
+ # and invalid values cause an ArgumentError to be raised.
+ # can be negative
+ #
+ # +y+ defaults to -4712, +m+ to 1, and +d+ to 1; this is
+ # Julian Day Number day 0.
+ #
+ # +sg+ specifies the Day of Calendar Reform.
+ def self.civil(y=-4712, m=1, d=1, sg=ITALY)
+ unless jd = valid_civil?(y, m, d, sg)
+ raise ArgumentError, 'invalid date'
+ end
+ new0(jd_to_ajd(jd, 0, 0), 0, sg)
+ end
+
+ class << self; alias_method :new, :civil end
+
+ # Do year +y+, week-of-year +w+, and day-of-week +d+ make a
+ # valid Commercial Date? Returns the corresponding Julian
+ # Day Number if they do, nil if they don't.
+ #
+ # Monday is day-of-week 1; Sunday is day-of-week 7.
+ #
+ # +w+ and +d+ can be negative, in which case they count
+ # backwards from the end of the year and the end of the
+ # week respectively. No wraparound is performed, however,
+ # and invalid values cause an ArgumentError to be raised.
+ # A date falling in the period skipped in the Day of Calendar
+ # Reform adjustment is not valid.
+ #
+ # +sg+ specifies the Day of Calendar Reform.
+ def self.valid_commercial? (y, w, d, sg=ITALY)
+ if d < 0
+ d += 8
+ end
+ if w < 0
+ w = jd_to_commercial(commercial_to_jd(y + 1, 1, 1) + w * 7)[1]
+ end
+ jd = commercial_to_jd(y, w, d)
+ return unless ns?(jd, sg)
+ return unless [y, w, d] == jd_to_commercial(jd)
+ jd
+ end
+
+ # Create a new Date object for the Commercial Date specified by
+ # year +y+, week-of-year +w+, and day-of-week +d+.
+ #
+ # Monday is day-of-week 1; Sunday is day-of-week 7.
+ #
+ # +w+ and +d+ can be negative, in which case they count
+ # backwards from the end of the year and the end of the
+ # week respectively. No wraparound is performed, however,
+ # and invalid values cause an ArgumentError to be raised.
+ #
+ # +y+ defaults to 1582, +w+ to 41, and +d+ to 5, the Day of
+ # Calendar Reform for Italy and the Catholic countries.
+ #
+ # +sg+ specifies the Day of Calendar Reform.
+ def self.commercial(y=1582, w=41, d=5, sg=ITALY)
+ unless jd = valid_commercial?(y, w, d, sg)
+ raise ArgumentError, 'invalid date'
+ end
+ new0(jd_to_ajd(jd, 0, 0), 0, sg)
+ end
+
+ def self.new_with_hash(elem, sg)
+ elem ||= {}
+ y, m, d = elem.values_at(:year, :mon, :mday)
+ if [y, m, d].include? nil
+ raise ArgumentError, '3 elements of civil date are necessary'
+ else
+ civil(y, m, d, sg)
+ end
+ end
+
+ private_class_method :new_with_hash
+
+ # Create a new Date object by parsing from a String
+ # according to a specified format.
+ #
+ # +str+ is a String holding a date representation.
+ # +fmt+ is the format that the date is in. See
+ # date/format.rb for details on supported formats.
+ #
+ # The default +str+ is '-4712-01-01', and the default
+ # +fmt+ is '%F', which means Year-Month-Day_of_Month.
+ # This gives Julian Day Number day 0.
+ #
+ # +sg+ specifies the Day of Calendar Reform.
+ #
+ # An ArgumentError will be raised if +str+ cannot be
+ # parsed.
+ def self.strptime(str='-4712-01-01', fmt='%F', sg=ITALY)
+ elem = _strptime(str, fmt)
+ new_with_hash(elem, sg)
+ end
+
+ # Create a new Date object by parsing from a String,
+ # without specifying the format.
+ #
+ # +str+ is a String holding a date representation.
+ # +comp+ specifies whether to interpret 2-digit years
+ # as 19XX (>= 69) or 20XX (< 69); the default is not to.
+ # The method will attempt to parse a date from the String
+ # using various heuristics; see #_parse in date/format.rb
+ # for more details. If parsing fails, an ArgumentError
+ # will be raised.
+ #
+ # The default +str+ is '-4712-01-01'; this is Julian
+ # Day Number day 0.
+ #
+ # +sg+ specifies the Day of Calendar Reform.
+ def self.parse(str='-4712-01-01', comp=false, sg=ITALY)
+ elem = _parse(str, comp)
+ new_with_hash(elem, sg)
+ end
+
+ # Create a new Date object representing today.
+ #
+ # +sg+ specifies the Day of Calendar Reform.
+ def self.today(sg=ITALY)
+ jd = civil_to_jd(*(Time.now.to_a[3..5].reverse << sg))
+ new0(jd_to_ajd(jd, 0, 0), 0, sg)
+ end
+
+ class << self
+
+ def once(*ids) # :nodoc:
+ for id in ids
+ module_eval <<-"end;", __FILE__, __LINE__
+ alias_method :__#{id.to_i}__, :#{id.to_s}
+ private :__#{id.to_i}__
+ def #{id.to_s}(*args, &block)
+ if defined? @__#{id.to_i}__
+ @__#{id.to_i}__
+ elsif ! self.frozen?
+ @__#{id.to_i}__ ||= __#{id.to_i}__(*args, &block)
+ else
+ __#{id.to_i}__(*args, &block)
+ end
+ end
+ end;
+ end
+ end
+
+ private :once
+
+ end
+
+ # *NOTE* this is the documentation for the method new0(). If
+ # you are reading this as the documentation for new(), that is
+ # because rdoc doesn't fully support the aliasing of the
+ # initialize() method.
+ # new() is in
+ # fact an alias for #civil(): read the documentation for that
+ # method instead.
+ #
+ # Create a new Date object.
+ #
+ # +ajd+ is the Astronomical Julian Day Number.
+ # +of+ is the offset from UTC as a fraction of a day.
+ # Both default to 0.
+ #
+ # +sg+ specifies the Day of Calendar Reform to use for this
+ # Date object.
+ #
+ # Using one of the factory methods such as Date::civil is
+ # generally easier and safer.
+ def initialize(ajd=0, of=0, sg=ITALY) @ajd, @of, @sg = ajd, of, sg end
+
+ # Get the date as an Astronomical Julian Day Number.
+ def ajd() @ajd end
+
+ # Get the date as an Astronomical Modified Julian Day Number.
+ def amjd() self.class.ajd_to_amjd(@ajd) end
+
+ once :amjd
+
+ # Get the date as a Julian Day Number.
+ def jd() self.class.ajd_to_jd(@ajd, @of)[0] end
+
+ # Get any fractional day part of the date.
+ def day_fraction() self.class.ajd_to_jd(@ajd, @of)[1] end
+
+ # Get the date as a Modified Julian Day Number.
+ def mjd() self.class.jd_to_mjd(jd) end
+
+ # Get the date as the number of days since the Day of Calendar
+ # Reform (in Italy and the Catholic countries).
+ def ld() self.class.jd_to_ld(jd) end
+
+ once :jd, :day_fraction, :mjd, :ld
+
+ # Get the date as a Civil Date, [year, month, day_of_month]
+ def civil() self.class.jd_to_civil(jd, @sg) end
+
+ # Get the date as an Ordinal Date, [year, day_of_year]
+ def ordinal() self.class.jd_to_ordinal(jd, @sg) end
+
+ # Get the date as a Commercial Date, [year, week_of_year, day_of_week]
+ def commercial() self.class.jd_to_commercial(jd, @sg) end
+
+ once :civil, :ordinal, :commercial
+ private :civil, :ordinal, :commercial
+
+ # Get the year of this date.
+ def year() civil[0] end
+
+ # Get the day-of-the-year of this date.
+ #
+ # January 1 is day-of-the-year 1
+ def yday() ordinal[1] end
+
+ # Get the month of this date.
+ #
+ # January is month 1.
+ def mon() civil[1] end
+
+ # Get the day-of-the-month of this date.
+ def mday() civil[2] end
+
+ alias_method :month, :mon
+ alias_method :day, :mday
+
+ # Get the time of this date as [hours, minutes, seconds,
+ # fraction_of_a_second]
+ def time() self.class.day_fraction_to_time(day_fraction) end
+
+ once :time
+ private :time
+
+ # Get the hour of this date.
+ def hour() time[0] end
+
+ # Get the minute of this date.
+ def min() time[1] end
+
+ # Get the second of this date.
+ def sec() time[2] end
+
+ # Get the fraction-of-a-second of this date.
+ def sec_fraction() time[3] end
+
+ private :hour, :min, :sec, :sec_fraction
+
+ def zone() strftime('%Z') end
+
+ private :zone
+
+ # Get the commercial year of this date. See *Commercial* *Date*
+ # in the introduction for how this differs from the normal year.
+ def cwyear() commercial[0] end
+
+ # Get the commercial week of the year of this date.
+ def cweek() commercial[1] end
+
+ # Get the commercial day of the week of this date. Monday is
+ # commercial day-of-week 1; Sunday is commercial day-of-week 7.
+ def cwday() commercial[2] end
+
+ # Get the week day of this date. Sunday is day-of-week 0;
+ # Saturday is day-of-week 6.
+ def wday() self.class.jd_to_wday(jd) end
+
+ once :wday
+
+ # Is the current date old-style (Julian Calendar)?
+ def os? () self.class.os?(jd, @sg) end
+
+ # Is the current date new-style (Gregorian Calendar)?
+ def ns? () self.class.ns?(jd, @sg) end
+
+ once :os?, :ns?
+
+ # Is this a leap year?
+ def leap?
+ self.class.jd_to_civil(self.class.civil_to_jd(year, 3, 1, ns?) - 1,
+ ns?)[-1] == 29
+ end
+
+ once :leap?
+
+ # When is the Day of Calendar Reform for this Date object?
+ def start() @sg end
+
+ # Create a copy of this Date object using a new Day of Calendar Reform.
+ def new_start(sg=self.class::ITALY) self.class.new0(@ajd, @of, sg) end
+
+ # Create a copy of this Date object that uses the Italian/Catholic
+ # Day of Calendar Reform.
+ def italy() new_start(self.class::ITALY) end
+
+ # Create a copy of this Date object that uses the English/Colonial
+ # Day of Calendar Reform.
+ def england() new_start(self.class::ENGLAND) end
+
+ # Create a copy of this Date object that always uses the Julian
+ # Calendar.
+ def julian() new_start(self.class::JULIAN) end
+
+ # Create a copy of this Date object that always uses the Gregorian
+ # Calendar.
+ def gregorian() new_start(self.class::GREGORIAN) end
+
+ def offset() @of end
+ def new_offset(of=0) self.class.new0(@ajd, of, @sg) end
+
+ private :offset, :new_offset
+
+ # Return a new Date object that is +n+ days later than the
+ # current one.
+ #
+ # +n+ may be a negative value, in which case the new Date
+ # is earlier than the current one; however, #-() might be
+ # more intuitive.
+ #
+ # If +n+ is not a Numeric, a TypeError will be thrown. In
+ # particular, two Dates cannot be added to each other.
+ def + (n)
+ case n
+ when Numeric; return self.class.new0(@ajd + n, @of, @sg)
+ end
+ raise TypeError, 'expected numeric'
+ end
+
+ # If +x+ is a Numeric value, create a new Date object that is
+ # +x+ days earlier than the current one.
+ #
+ # If +x+ is a Date, return the number of days between the
+ # two dates; or, more precisely, how many days later the current
+ # date is than +x+.
+ #
+ # If +x+ is neither Numeric nor a Date, a TypeError is raised.
+ def - (x)
+ case x
+ when Numeric; return self.class.new0(@ajd - x, @of, @sg)
+ when Date; return @ajd - x.ajd
+ end
+ raise TypeError, 'expected numeric or date'
+ end
+
+ # Compare this date with another date.
+ #
+ # +other+ can also be a Numeric value, in which case it is
+ # interpreted as an Astronomical Julian Day Number.
+ #
+ # Comparison is by Astronomical Julian Day Number, including
+ # fractional days. This means that both the time and the
+ # timezone offset are taken into account when comparing
+ # two DateTime instances. When comparing a DateTime instance
+ # with a Date instance, the time of the latter will be
+ # considered as falling on midnight UTC.
+ def <=> (other)
+ case other
+ when Numeric; return @ajd <=> other
+ when Date; return @ajd <=> other.ajd
+ end
+ nil
+ end
+
+ # The relationship operator for Date.
+ #
+ # Compares dates by Julian Day Number. When comparing
+ # two DateTime instances, or a DateTime with a Date,
+ # the instances will be regarded as equivalent if they
+ # fall on the same date in local time.
+ def === (other)
+ case other
+ when Numeric; return jd == other
+ when Date; return jd == other.jd
+ end
+ false
+ end
+
+ # Return a new Date object that is +n+ months later than
+ # the current one.
+ #
+ # If the day-of-the-month of the current Date is greater
+ # than the last day of the target month, the day-of-the-month
+ # of the returned Date will be the last day of the target month.
+ def >> (n)
+ y, m = clfloor(year * 12 + (mon - 1) + n, 12)
+ m, = clfloor(m + 1, 1)
+ d = mday
+ d -= 1 until jd2 = self.class.valid_civil?(y, m, d, ns?)
+ self + (jd2 - jd)
+ end
+
+ # Return a new Date object that is +n+ months earlier than
+ # the current one.
+ #
+ # If the day-of-the-month of the current Date is greater
+ # than the last day of the target month, the day-of-the-month
+ # of the returned Date will be the last day of the target month.
+ def << (n) self >> -n end
+
+ # Step the current date forward +step+ days at a
+ # time (or backward, if +step+ is negative) until
+ # we reach +limit+ (inclusive), yielding the resultant
+ # date at each step.
+ def step(limit, step) # :yield: date
+ da = self
+ op = [:-,:<=,:>=][step<=>0]
+ while da.__send__(op, limit)
+ yield da
+ da += step
+ end
+ self
+ end
+
+ # Step forward one day at a time until we reach +max+
+ # (inclusive), yielding each date as we go.
+ def upto(max, &block) # :yield: date
+ step(max, +1, &block)
+ end
+
+ # Step backward one day at a time until we reach +min+
+ # (inclusive), yielding each date as we go.
+ def downto(min, &block) # :yield: date
+ step(min, -1, &block)
+ end
+
+ # Return a new Date one day after this one.
+ def succ() self + 1 end
+
+ alias_method :next, :succ
+
+ # Is this Date equal to +other+?
+ #
+ # +other+ must both be a Date object, and represent the same date.
+ def eql? (other) Date === other and self == other end
+
+ # Calculate a hash value for this date.
+ def hash() @ajd.hash end
+
+ # Return internal object state as a programmer-readable string.
+ def inspect() format('#<%s: %s,%s,%s>', self.class, @ajd, @of, @sg) end
+
+ # Return the date as a human-readable string.
+ #
+ # The format used is YYYY-MM-DD.
+ def to_s() strftime end
+
+ # Dump to Marshal format.
+ def _dump(limit) Marshal.dump([@ajd, @of, @sg], -1) end
+
+# def self._load(str) new0(*Marshal.load(str)) end
+
+ # Load from Marshall format.
+ def self._load(str)
+ a = Marshal.load(str)
+ if a.size == 2
+ ajd, sg = a
+ of = 0
+ ajd -= 1.to_r/2
+ else
+ ajd, of, sg = a
+ end
+ new0(ajd, of, sg)
+ end
+
+end
+
+# Class representing a date and time.
+#
+# See the documentation to the file date.rb for an overview.
+#
+# DateTime objects are immutable once created.
+#
+# == Other methods.
+#
+# The following methods are defined in Date, but declared private
+# there. They are made public in DateTime. They are documented
+# here.
+#
+# === hour()
+#
+# Get the hour-of-the-day of the time. This is given
+# using the 24-hour clock, counting from midnight. The first
+# hour after midnight is hour 0; the last hour of the day is
+# hour 23.
+#
+# === min()
+#
+# Get the minute-of-the-hour of the time.
+#
+# === sec()
+#
+# Get the second-of-the-minute of the time.
+#
+# === sec_fraction()
+#
+# Get the fraction of a second of the time. This is returned as
+# a +Rational+.
+#
+# === zone()
+#
+# Get the time zone as a String. This is representation of the
+# time offset such as "+1000", not the true time-zone name.
+#
+# === offset()
+#
+# Get the time zone offset as a fraction of a day. This is returned
+# as a +Rational+.
+#
+# === new_offset(of=0)
+#
+# Create a new DateTime object, identical to the current one, except
+# with a new time zone offset of +of+. +of+ is the new offset from
+# UTC as a fraction of a day.
+#
+class DateTime < Date
+
+ # Do hour +h+, minute +min+, and second +s+ constitute a valid time?
+ #
+ # If they do, returns their value as a fraction of a day. If not,
+ # returns nil.
+ #
+ # The 24-hour clock is used. Negative values of +h+, +min+, and
+ # +sec+ are treating as counting backwards from the end of the
+ # next larger unit (e.g. a +min+ of -2 is treated as 58). No
+ # wraparound is performed.
+ def self.valid_time? (h, min, s)
+ h += 24 if h < 0
+ min += 60 if min < 0
+ s += 60 if s < 0
+ return unless (0..24) === h and
+ (0..59) === min and
+ (0..59) === s
+ time_to_day_fraction(h, min, s)
+ end
+
+ # Create a new DateTime object corresponding to the specified
+ # Julian Day Number +jd+ and hour +h+, minute +min+, second +s+.
+ #
+ # The 24-hour clock is used. Negative values of +h+, +min+, and
+ # +sec+ are treating as counting backwards from the end of the
+ # next larger unit (e.g. a +min+ of -2 is treated as 58). No
+ # wraparound is performed. If an invalid time portion is specified,
+ # an ArgumentError is raised.
+ #
+ # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
+ # +sg+ specifies the Day of Calendar Reform.
+ #
+ # All day/time values default to 0.
+ def self.jd(jd=0, h=0, min=0, s=0, of=0, sg=ITALY)
+ unless (jd = valid_jd?(jd, sg)) and
+ (fr = valid_time?(h, min, s))
+ raise ArgumentError, 'invalid date'
+ end
+ new0(jd_to_ajd(jd, fr, of), of, sg)
+ end
+
+ # Create a new DateTime object corresponding to the specified
+ # Ordinal Date and hour +h+, minute +min+, second +s+.
+ #
+ # The 24-hour clock is used. Negative values of +h+, +min+, and
+ # +sec+ are treating as counting backwards from the end of the
+ # next larger unit (e.g. a +min+ of -2 is treated as 58). No
+ # wraparound is performed. If an invalid time portion is specified,
+ # an ArgumentError is raised.
+ #
+ # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
+ # +sg+ specifies the Day of Calendar Reform.
+ #
+ # +y+ defaults to -4712, and +d+ to 1; this is Julian Day Number
+ # day 0. The time values default to 0.
+ def self.ordinal(y=-4712, d=1, h=0, min=0, s=0, of=0, sg=ITALY)
+ unless (jd = valid_ordinal?(y, d, sg)) and
+ (fr = valid_time?(h, min, s))
+ raise ArgumentError, 'invalid date'
+ end
+ new0(jd_to_ajd(jd, fr, of), of, sg)
+ end
+
+ # Create a new DateTime object corresponding to the specified
+ # Civil Date and hour +h+, minute +min+, second +s+.
+ #
+ # The 24-hour clock is used. Negative values of +h+, +min+, and
+ # +sec+ are treating as counting backwards from the end of the
+ # next larger unit (e.g. a +min+ of -2 is treated as 58). No
+ # wraparound is performed. If an invalid time portion is specified,
+ # an ArgumentError is raised.
+ #
+ # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
+ # +sg+ specifies the Day of Calendar Reform.
+ #
+ # +y+ defaults to -4712, +m+ to 1, and +d+ to 1; this is Julian Day
+ # Number day 0. The time values default to 0.
+ def self.civil(y=-4712, m=1, d=1, h=0, min=0, s=0, of=0, sg=ITALY)
+ unless (jd = valid_civil?(y, m, d, sg)) and
+ (fr = valid_time?(h, min, s))
+ raise ArgumentError, 'invalid date'
+ end
+ new0(jd_to_ajd(jd, fr, of), of, sg)
+ end
+
+ class << self; alias_method :new, :civil end
+
+ # Create a new DateTime object corresponding to the specified
+ # Commercial Date and hour +h+, minute +min+, second +s+.
+ #
+ # The 24-hour clock is used. Negative values of +h+, +min+, and
+ # +sec+ are treating as counting backwards from the end of the
+ # next larger unit (e.g. a +min+ of -2 is treated as 58). No
+ # wraparound is performed. If an invalid time portion is specified,
+ # an ArgumentError is raised.
+ #
+ # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
+ # +sg+ specifies the Day of Calendar Reform.
+ #
+ # +y+ defaults to 1582, +w+ to 41, and +d+ to 5; this is the Day of
+ # Calendar Reform for Italy and the Catholic countries.
+ # The time values default to 0.
+ def self.commercial(y=1582, w=41, d=5, h=0, min=0, s=0, of=0, sg=ITALY)
+ unless (jd = valid_commercial?(y, w, d, sg)) and
+ (fr = valid_time?(h, min, s))
+ raise ArgumentError, 'invalid date'
+ end
+ new0(jd_to_ajd(jd, fr, of), of, sg)
+ end
+
+ def self.new_with_hash(elem, sg)
+ elem ||= {}
+ y, m, d, h, min, s, fr, of =
+ elem.values_at(:year, :mon, :mday,
+ :hour, :min, :sec, :sec_fraction, :offset)
+ h ||= 0
+ min ||= 0
+ s ||= 0
+ fr ||= 0
+ of ||= 0
+ if [y, m, d].include? nil
+ raise ArgumentError, '3 elements of civil date are necessary'
+ else
+ civil(y, m, d, h, min, s, of.to_r/86400, sg) + (fr/86400)
+ end
+ end
+
+ private_class_method :new_with_hash
+
+ # Create a new DateTime object by parsing from a String
+ # according to a specified format.
+ #
+ # +str+ is a String holding a date-time representation.
+ # +fmt+ is the format that the date-time is in. See
+ # date/format.rb for details on supported formats.
+ #
+ # The default +str+ is '-4712-01-01T00:00:00Z', and the default
+ # +fmt+ is '%FT%T%Z'. This gives midnight on Julian Day Number day 0.
+ #
+ # +sg+ specifies the Day of Calendar Reform.
+ #
+ # An ArgumentError will be raised if +str+ cannot be
+ # parsed.
+ def self.strptime(str='-4712-01-01T00:00:00Z', fmt='%FT%T%Z', sg=ITALY)
+ elem = _strptime(str, fmt)
+ new_with_hash(elem, sg)
+ end
+
+ # Create a new DateTime object by parsing from a String,
+ # without specifying the format.
+ #
+ # +str+ is a String holding a date-time representation.
+ # +comp+ specifies whether to interpret 2-digit years
+ # as 19XX (>= 69) or 20XX (< 69); the default is not to.
+ # The method will attempt to parse a date-time from the String
+ # using various heuristics; see #_parse in date/format.rb
+ # for more details. If parsing fails, an ArgumentError
+ # will be raised.
+ #
+ # The default +str+ is '-4712-01-01T00:00:00Z'; this is Julian
+ # Day Number day 0.
+ #
+ # +sg+ specifies the Day of Calendar Reform.
+ def self.parse(str='-4712-01-01T00:00:00Z', comp=false, sg=ITALY)
+ elem = _parse(str, comp)
+ new_with_hash(elem, sg)
+ end
+
+ class << self; undef_method :today end rescue nil
+
+ # Create a new DateTime object representing the current time.
+ #
+ # +sg+ specifies the Day of Calendar Reform.
+ def self.now(sg=ITALY)
+ i = Time.now
+ a = i.to_a[0..5].reverse
+ jd = civil_to_jd(*(a[0,3] << sg))
+ fr = time_to_day_fraction(*(a[3,3])) + i.usec.to_r/86400000000
+ of = i.utc_offset.to_r/86400
+ new0(jd_to_ajd(jd, fr, of), of, sg)
+ end
+
+ public :hour, :min, :sec, :sec_fraction, :zone, :offset, :new_offset
+
+end
+
+class Date
+
+ [ %w(exist1? valid_jd?),
+ %w(exist2? valid_ordinal?),
+ %w(exist3? valid_date?),
+ %w(exist? valid_date?),
+ %w(existw? valid_commercial?),
+ %w(new1 jd),
+ %w(new2 ordinal),
+ %w(new3 new),
+ %w(neww commercial)
+ ].each do |old, new|
+ module_eval <<-"end;"
+ def self.#{old}(*args, &block)
+ if $VERBOSE
+ warn("\#{caller.shift.sub(/:in .*/, '')}: " \
+ "warning: \#{self}::#{old} is deprecated; " \
+ "use \#{self}::#{new}")
+ end
+ #{new}(*args, &block)
+ end
+ end;
+ end
+
+ [ %w(sg start),
+ %w(newsg new_start),
+ %w(of offset),
+ %w(newof new_offset)
+ ].each do |old, new|
+ module_eval <<-"end;"
+ def #{old}(*args, &block)
+ if $VERBOSE
+ warn("\#{caller.shift.sub(/:in .*/, '')}: " \
+ "warning: \#{self.class}\##{old} is deprecated; " \
+ "use \#{self.class}\##{new}")
+ end
+ #{new}(*args, &block)
+ end
+ end;
+ end
+
+ private :of, :newof
+
+end
+
+class DateTime < Date
+
+ public :of, :newof
+
+end
diff --git a/ruby_1_8_5/lib/date/format.rb b/ruby_1_8_5/lib/date/format.rb
new file mode 100644
index 0000000000..4425635d33
--- /dev/null
+++ b/ruby_1_8_5/lib/date/format.rb
@@ -0,0 +1,603 @@
+# format.rb: Written by Tadayoshi Funaba 1999-2006
+# $Id: format.rb,v 2.15 2005-02-06 13:28:48+09 tadf Exp $
+
+require 'rational'
+
+class Date
+
+ MONTHS = {
+ 'january' => 1, 'february' => 2, 'march' => 3, 'april' => 4,
+ 'may' => 5, 'june' => 6, 'july' => 7, 'august' => 8,
+ 'september'=> 9, 'october' =>10, 'november' =>11, 'december' =>12
+ }
+
+ DAYS = {
+ 'sunday' => 0, 'monday' => 1, 'tuesday' => 2, 'wednesday'=> 3,
+ 'thursday' => 4, 'friday' => 5, 'saturday' => 6
+ }
+
+ ABBR_MONTHS = {
+ 'jan' => 1, 'feb' => 2, 'mar' => 3, 'apr' => 4,
+ 'may' => 5, 'jun' => 6, 'jul' => 7, 'aug' => 8,
+ 'sep' => 9, 'oct' =>10, 'nov' =>11, 'dec' =>12
+ }
+
+ ABBR_DAYS = {
+ 'sun' => 0, 'mon' => 1, 'tue' => 2, 'wed' => 3,
+ 'thu' => 4, 'fri' => 5, 'sat' => 6
+ }
+
+ ZONES = {
+ 'ut' => 0*3600, 'gmt' => 0*3600, 'est' => -5*3600, 'edt' => -4*3600,
+ 'cst' => -6*3600, 'cdt' => -5*3600, 'mst' => -7*3600, 'mdt' => -6*3600,
+ 'pst' => -8*3600, 'pdt' => -7*3600,
+ 'a' => 1*3600, 'b' => 2*3600, 'c' => 3*3600, 'd' => 4*3600,
+ 'e' => 5*3600, 'f' => 6*3600, 'g' => 7*3600, 'h' => 8*3600,
+ 'i' => 9*3600, 'k' => 10*3600, 'l' => 11*3600, 'm' => 12*3600,
+ 'n' => -1*3600, 'o' => -2*3600, 'p' => -3*3600, 'q' => -4*3600,
+ 'r' => -5*3600, 's' => -6*3600, 't' => -7*3600, 'u' => -8*3600,
+ 'v' => -9*3600, 'w' =>-10*3600, 'x' =>-11*3600, 'y' =>-12*3600,
+ 'z' => 0*3600,
+ 'utc' => 0*3600, 'wet' => 0*3600, 'bst' => 1*3600, 'wat' => -1*3600,
+ 'at' => -2*3600, 'ast' => -4*3600, 'adt' => -3*3600, 'yst' => -9*3600,
+ 'ydt' => -8*3600, 'hst' =>-10*3600, 'hdt' => -9*3600, 'cat' =>-10*3600,
+ 'ahst'=>-10*3600, 'nt' =>-11*3600, 'idlw'=>-12*3600, 'cet' => 1*3600,
+ 'met' => 1*3600, 'mewt'=> 1*3600, 'mest'=> 2*3600, 'mesz'=> 2*3600,
+ 'swt' => 1*3600, 'sst' => 2*3600, 'fwt' => 1*3600, 'fst' => 2*3600,
+ 'eet' => 2*3600, 'bt' => 3*3600, 'zp4' => 4*3600, 'zp5' => 5*3600,
+ 'zp6' => 6*3600, 'wast'=> 7*3600, 'wadt'=> 8*3600, 'cct' => 8*3600,
+ 'jst' => 9*3600, 'east'=> 10*3600, 'eadt'=> 11*3600, 'gst' => 10*3600,
+ 'nzt' => 12*3600, 'nzst'=> 12*3600, 'nzdt'=> 13*3600, 'idle'=> 12*3600
+ }
+
+ def self.__strptime(str, fmt, elem)
+ fmt.scan(/%[EO]?.|./mo) do |c|
+ cc = c.sub(/\A%[EO]?(.)\z/mo, '%\\1')
+ case cc
+ when /\A\s/o
+ str.sub!(/\A[\s\v]+/o, '')
+ when '%A', '%a'
+ return unless str.sub!(/\A([a-z]+)\b/io, '')
+ val = DAYS[$1.downcase] || ABBR_DAYS[$1.downcase]
+ return unless val
+ elem[:wday] = val
+ when '%B', '%b', '%h'
+ return unless str.sub!(/\A([a-z]+)\b/io, '')
+ val = MONTHS[$1.downcase] || ABBR_MONTHS[$1.downcase]
+ return unless val
+ elem[:mon] = val
+ when '%C'
+ return unless str.sub!(/\A(\d+)/o, '')
+ val = $1.to_i
+ elem[:cent] = val
+ when '%c'
+ return unless __strptime(str, '%a %b %e %H:%M:%S %Y', elem)
+ when '%D'
+ return unless __strptime(str, '%m/%d/%y', elem)
+ when '%d', '%e'
+ return unless str.sub!(/\A ?(\d+)/o, '')
+ val = $1.to_i
+ return unless (1..31) === val
+ elem[:mday] = val
+ when '%F'
+ return unless __strptime(str, '%Y-%m-%d', elem)
+ when '%G'
+ return unless str.sub!(/\A([-+]?\d+)/o, '')
+ val = $1.to_i
+ elem[:cwyear] = val
+ when '%g'
+ return unless str.sub!(/\A(\d+)/o, '')
+ val = $1.to_i
+ return unless (0..99) === val
+ elem[:cwyear] = val
+ elem[:cent] ||= if val >= 69 then 19 else 20 end
+ when '%H', '%k'
+ return unless str.sub!(/\A ?(\d+)/o, '')
+ val = $1.to_i
+ return unless (0..24) === val
+ elem[:hour] = val
+ when '%I', '%l'
+ return unless str.sub!(/\A ?(\d+)/o, '')
+ val = $1.to_i
+ return unless (1..12) === val
+ elem[:hour] = val
+ when '%j'
+ return unless str.sub!(/\A(\d+)/o, '')
+ val = $1.to_i
+ return unless (1..366) === val
+ elem[:yday] = val
+ when '%M'
+ return unless str.sub!(/\A(\d+)/o, '')
+ val = $1.to_i
+ return unless (0..59) === val
+ elem[:min] = val
+ when '%m'
+ return unless str.sub!(/\A(\d+)/o, '')
+ val = $1.to_i
+ return unless (1..12) === val
+ elem[:mon] = val
+=begin
+ when '%N'
+ return unless str.sub!(/\A(\d+)/o, '')
+ val = $1.to_i.to_r / (10**9)
+ elem[:sec_fraction] = val
+=end
+ when '%n'
+ return unless __strptime(str, ' ', elem)
+ when '%p', '%P'
+ return unless str.sub!(/\A([ap])(?:m\b|\.m\.)/io, '')
+ elem[:merid] = if $1.downcase == 'a' then 0 else 12 end
+ when '%R'
+ return unless __strptime(str, '%H:%M', elem)
+ when '%r'
+ return unless __strptime(str, '%I:%M:%S %p', elem)
+ when '%S'
+ return unless str.sub!(/\A(\d+)/o, '')
+ val = $1.to_i
+ return unless (0..60) === val
+ elem[:sec] = val
+ when '%s'
+ return unless str.sub!(/\A(\d+)/o, '')
+ val = $1.to_i
+ elem[:seconds] = val
+ when '%T'
+ return unless __strptime(str, '%H:%M:%S', elem)
+ when '%t'
+ return unless __strptime(str, ' ', elem)
+ when '%U', '%W'
+ return unless str.sub!(/\A(\d+)/o, '')
+ val = $1.to_i
+ return unless (0..53) === val
+ elem[if c[-1,1] == 'U' then :wnum0 else :wnum1 end] = val
+ when '%u'
+ return unless str.sub!(/\A(\d+)/o, '')
+ val = $1.to_i
+ return unless (1..7) === val
+ elem[:cwday] = val
+ when '%V'
+ return unless str.sub!(/\A(\d+)/o, '')
+ val = $1.to_i
+ return unless (1..53) === val
+ elem[:cweek] = val
+ when '%v'
+ return unless __strptime(str, '%e-%b-%Y', elem)
+ when '%w'
+ return unless str.sub!(/\A(\d+)/o, '')
+ val = $1.to_i
+ return unless (0..6) === val
+ elem[:wday] = val
+ when '%X'
+ return unless __strptime(str, '%H:%M:%S', elem)
+ when '%x'
+ return unless __strptime(str, '%m/%d/%y', elem)
+ when '%Y'
+ return unless str.sub!(/\A([-+]?\d+)/o, '')
+ val = $1.to_i
+ elem[:year] = val
+ when '%y'
+ return unless str.sub!(/\A(\d+)/o, '')
+ val = $1.to_i
+ return unless (0..99) === val
+ elem[:year] = val
+ elem[:cent] ||= if val >= 69 then 19 else 20 end
+ when '%Z', '%z'
+ return unless str.sub!(/\A([-+:a-z0-9]+(?:\s+dst\b)?)/io, '')
+ val = $1
+ elem[:zone] = val
+ offset = zone_to_diff(val)
+ elem[:offset] = offset
+ when '%%'
+ return unless str.sub!(/\A%/o, '')
+ when '%+'
+ return unless __strptime(str, '%a %b %e %H:%M:%S %Z %Y', elem)
+=begin
+ when '%.'
+ return unless str.sub!(/\A(\d+)/o, '')
+ val = $1.to_i.to_r / (10**$1.size)
+ elem[:sec_fraction] = val
+=end
+ when '%1'
+ if $VERBOSE
+ warn("warning: %1 is deprecated; forget this")
+ end
+ return unless str.sub!(/\A(\d+)/o, '')
+ val = $1.to_i
+ elem[:jd] = val
+ when '%2'
+ if $VERBOSE
+ warn("warning: %2 is deprecated; use '%Y-%j'")
+ end
+ return unless __strptime(str, '%Y-%j', elem)
+ when '%3'
+ if $VERBOSE
+ warn("warning: %3 is deprecated; use '%F'")
+ end
+ return unless __strptime(str, '%F', elem)
+ when /\A%(.)/m
+ return unless str.sub!(Regexp.new('\\A' + Regexp.quote($1)), '')
+ else
+ return unless str.sub!(Regexp.new('\\A' + Regexp.quote(c)), '')
+ end
+ end
+
+ if cent = elem.delete(:cent)
+ if elem[:cwyear]
+ elem[:cwyear] += cent * 100
+ end
+ if elem[:year]
+ elem[:year] += cent * 100
+ end
+ end
+
+ if merid = elem.delete(:merid)
+ if elem[:hour]
+ elem[:hour] %= 12
+ elem[:hour] += merid
+ end
+ end
+
+ str
+ end
+
+ private_class_method :__strptime
+
+ def self._strptime(str, fmt='%F')
+ elem = {}
+ elem if __strptime(str.dup, fmt, elem)
+ end
+
+ PARSE_MONTHPAT = ABBR_MONTHS.keys.join('|')
+ PARSE_DAYPAT = ABBR_DAYS. keys.join('|')
+
+ def self._parse(str, comp=false)
+ str = str.dup
+
+ str.gsub!(/[^-+,.\/:0-9a-z]+/ino, ' ')
+
+ # day
+ if str.sub!(/(#{PARSE_DAYPAT})\S*/ino, ' ')
+ wday = ABBR_DAYS[$1.downcase]
+ end
+
+ # time
+ if str.sub!(
+ /(\d+):(\d+)
+ (?:
+ :(\d+)(?:[,.](\d*))?
+ )?
+ (?:
+ \s*
+ ([ap])(?:m\b|\.m\.)
+ )?
+ (?:
+ \s*
+ (
+ [a-z]+(?:\s+dst)?\b
+ |
+ [-+]\d+(?::?\d+)
+ )
+ )?
+ /inox,
+ ' ')
+ hour = $1.to_i
+ min = $2.to_i
+ sec = $3.to_i if $3
+ if $4
+ sec_fraction = $4.to_i.to_r / (10**$4.size)
+ end
+
+ if $5
+ hour %= 12
+ if $5.downcase == 'p'
+ hour += 12
+ end
+ end
+
+ if $6
+ zone = $6
+ end
+ end
+
+ # eu
+ if str.sub!(
+ /(\d+)\S*
+ \s+
+ (#{PARSE_MONTHPAT})\S*
+ (?:
+ \s+
+ (-?\d+)
+ )?
+ /inox,
+ ' ')
+ mday = $1.to_i
+ mon = ABBR_MONTHS[$2.downcase]
+
+ if $3
+ year = $3.to_i
+ if $3.size > 2
+ comp = false
+ end
+ end
+
+ # us
+ elsif str.sub!(
+ /(#{PARSE_MONTHPAT})\S*
+ \s+
+ (\d+)\S*
+ (?:
+ \s+
+ (-?\d+)
+ )?
+ /inox,
+ ' ')
+ mon = ABBR_MONTHS[$1.downcase]
+ mday = $2.to_i
+
+ if $3
+ year = $3.to_i
+ if $3.size > 2
+ comp = false
+ end
+ end
+
+ # iso
+ elsif str.sub!(/([-+]?\d+)-(\d+)-(-?\d+)/no, ' ')
+ year = $1.to_i
+ mon = $2.to_i
+ mday = $3.to_i
+
+ if $1.size > 2
+ comp = false
+ elsif $3.size > 2
+ comp = false
+ mday, mon, year = year, mon, mday
+ end
+
+ # jis
+ elsif str.sub!(/([MTSH])(\d+)\.(\d+)\.(\d+)/ino, ' ')
+ e = { 'm'=>1867,
+ 't'=>1911,
+ 's'=>1925,
+ 'h'=>1988
+ }[$1.downcase]
+ year = $2.to_i + e
+ mon = $3.to_i
+ mday = $4.to_i
+
+ # vms
+ elsif str.sub!(/(-?\d+)-(#{PARSE_MONTHPAT})[^-]*-(-?\d+)/ino, ' ')
+ mday = $1.to_i
+ mon = ABBR_MONTHS[$2.downcase]
+ year = $3.to_i
+
+ if $1.size > 2
+ comp = false
+ year, mon, mday = mday, mon, year
+ elsif $3.size > 2
+ comp = false
+ end
+
+ # sla
+ elsif str.sub!(%r|(-?\d+)/(\d+)(?:/(-?\d+))?|no, ' ')
+ mon = $1.to_i
+ mday = $2.to_i
+
+ if $3
+ year = $3.to_i
+ if $3.size > 2
+ comp = false
+ end
+ end
+
+ if $3 && $1.size > 2
+ comp = false
+ year, mon, mday = mon, mday, year
+ end
+
+ # ddd
+ elsif str.sub!(
+ /([-+]?)(\d{4,14})
+ (?:
+ \s*
+ T?
+ \s*
+ (\d{2,6})(?:[,.](\d*))?
+ )?
+ (?:
+ \s*
+ (
+ Z
+ |
+ [-+]\d{2,4}
+ )
+ \b
+ )?
+ /inox,
+ ' ')
+ case $2.size
+ when 4
+ mon = $2[ 0, 2].to_i
+ mday = $2[ 2, 2].to_i
+ when 6
+ year = ($1 + $2[ 0, 2]).to_i
+ mon = $2[ 2, 2].to_i
+ mday = $2[ 4, 2].to_i
+ when 8, 10, 12, 14
+ year = ($1 + $2[ 0, 4]).to_i
+ mon = $2[ 4, 2].to_i
+ mday = $2[ 6, 2].to_i
+ hour = $2[ 8, 2].to_i if $2.size >= 10
+ min = $2[10, 2].to_i if $2.size >= 12
+ sec = $2[12, 2].to_i if $2.size >= 14
+ comp = false
+ end
+ if $3
+ case $3.size
+ when 2, 4, 6
+ hour = $3[ 0, 2].to_i
+ min = $3[ 2, 2].to_i if $3.size >= 4
+ sec = $3[ 4, 2].to_i if $3.size >= 6
+ end
+ end
+ if $4
+ sec_fraction = $4.to_i.to_r / (10**$4.size)
+ end
+ if $5
+ zone = $5
+ end
+ end
+
+ if str.sub!(/\b(bc\b|bce\b|b\.c\.|b\.c\.e\.)/ino, ' ')
+ if year
+ year = -year + 1
+ end
+ end
+
+ if comp and year
+ if year >= 0 and year <= 99
+ if year >= 69
+ year += 1900
+ else
+ year += 2000
+ end
+ end
+ end
+
+ elem = {}
+ elem[:year] = year if year
+ elem[:mon] = mon if mon
+ elem[:mday] = mday if mday
+ elem[:hour] = hour if hour
+ elem[:min] = min if min
+ elem[:sec] = sec if sec
+ elem[:sec_fraction] = sec_fraction if sec_fraction
+ elem[:zone] = zone if zone
+ offset = zone_to_diff(zone) if zone
+ elem[:offset] = offset if offset
+ elem[:wday] = wday if wday
+ elem
+ end
+
+ def self.zone_to_diff(str)
+ abb, dst = str.downcase.split(/\s+/o, 2)
+ if ZONES.include?(abb)
+ offset = ZONES[abb]
+ offset += 3600 if dst
+ elsif /\A([-+])(\d{2}):?(\d{2})?\Z/no =~ str
+ offset = $2.to_i * 3600 + $3.to_i * 60
+ offset *= -1 if $1 == '-'
+ end
+ offset
+ end
+
+ def strftime(fmt='%F')
+ o = ''
+ fmt.scan(/%[EO]?.|./mo) do |c|
+ cc = c.sub(/\A%[EO]?(.)\z/mo, '%\\1')
+ case cc
+ when '%A'; o << DAYNAMES[wday]
+ when '%a'; o << ABBR_DAYNAMES[wday]
+ when '%B'; o << MONTHNAMES[mon]
+ when '%b'; o << ABBR_MONTHNAMES[mon]
+ when '%C'; o << '%02d' % (year / 100.0).floor # P2,ID
+ when '%c'; o << strftime('%a %b %e %H:%M:%S %Y')
+ when '%D'; o << strftime('%m/%d/%y') # P2,ID
+ when '%d'; o << '%02d' % mday
+ when '%e'; o << '%2d' % mday
+ when '%F'; o << strftime('%Y-%m-%d') # ID
+ when '%G'; o << '%.4d' % cwyear # ID
+ when '%g'; o << '%02d' % (cwyear % 100) # ID
+ when '%H'; o << '%02d' % hour
+ when '%h'; o << strftime('%b') # P2,ID
+ when '%I'; o << '%02d' % ((hour % 12).nonzero? or 12)
+ when '%j'; o << '%03d' % yday
+ when '%k'; o << '%2d' % hour # AR,TZ,GL
+ when '%l'; o << '%2d' % ((hour % 12).nonzero? or 12) # AR,TZ,GL
+ when '%M'; o << '%02d' % min
+ when '%m'; o << '%02d' % mon
+=begin
+ when '%N' # GNU date
+ o << '%09d' % (sec_fraction / (1.to_r/86400/(10**9)))
+=end
+ when '%n'; o << "\n" # P2,ID
+ when '%P'; o << if hour < 12 then 'am' else 'pm' end # GL
+ when '%p'; o << if hour < 12 then 'AM' else 'PM' end
+ when '%R'; o << strftime('%H:%M') # ID
+ when '%r'; o << strftime('%I:%M:%S %p') # P2,ID
+ when '%S'; o << '%02d' % sec
+ when '%s' # TZ,GL
+ d = ajd - self.class.jd_to_ajd(self.class.civil_to_jd(1970,1,1), 0)
+ s = (d * 86400).to_i
+ o << '%d' % s
+ when '%T'; o << strftime('%H:%M:%S') # P2,ID
+ when '%t'; o << "\t" # P2,ID
+ when '%U', '%W'
+ a = self.class.civil_to_jd(year, 1, 1, ns?) + 6
+ k = if c[-1,1] == 'U' then 0 else 1 end
+ w = (jd - (a - ((a - k) + 1) % 7) + 7) / 7
+ o << '%02d' % w
+ when '%u'; o << '%d' % cwday # P2,ID
+ when '%V'; o << '%02d' % cweek # P2,ID
+ when '%v'; o << strftime('%e-%b-%Y') # AR,TZ
+ when '%w'; o << '%d' % wday
+ when '%X'; o << strftime('%H:%M:%S')
+ when '%x'; o << strftime('%m/%d/%y')
+ when '%Y'; o << '%.4d' % year
+ when '%y'; o << '%02d' % (year % 100)
+ when '%Z'; o << (if offset.zero? then 'Z' else strftime('%z') end)
+ when '%z' # ID
+ o << if offset < 0 then '-' else '+' end
+ of = offset.abs
+ hh, fr = of.divmod(1.to_r/24)
+ mm = fr / (1.to_r/1440)
+ o << '%02d' % hh
+ o << '%02d' % mm
+ when '%%'; o << '%'
+ when '%+'; o << strftime('%a %b %e %H:%M:%S %Z %Y') # TZ
+=begin
+ when '%.'
+ o << '%06d' % (sec_fraction / (1.to_r/86400/(10**6)))
+=end
+ when '%1'
+ if $VERBOSE
+ warn("warning: %1 is deprecated; forget this")
+ end
+ o << '%d' % jd
+ when '%2'
+ if $VERBOSE
+ warn("warning: %2 is deprecated; use '%Y-%j'")
+ end
+ o << strftime('%Y-%j')
+ when '%3'
+ if $VERBOSE
+ warn("warning: %3 is deprecated; use '%F'")
+ end
+ o << strftime('%F')
+ when /\A%(.)/m
+ o << $1
+ else
+ o << c
+ end
+ end
+ o
+ end
+
+# alias_method :format, :strftime
+
+ def asctime() strftime('%c') end
+
+ alias_method :ctime, :asctime
+
+end
+
+class DateTime < Date
+
+ def self._strptime(str, fmt='%FT%T%Z')
+ super(str, fmt)
+ end
+
+ def strftime(fmt='%FT%T%Z')
+ super(fmt)
+ end
+
+end
diff --git a/ruby_1_8_5/lib/date2.rb b/ruby_1_8_5/lib/date2.rb
new file mode 100644
index 0000000000..f7da78f650
--- /dev/null
+++ b/ruby_1_8_5/lib/date2.rb
@@ -0,0 +1,5 @@
+# date2 was overridden by date.
+# To be precise, date was overridden by date2,
+# and date2 was renamed to date.
+
+require 'date'
diff --git a/ruby_1_8_5/lib/debug.rb b/ruby_1_8_5/lib/debug.rb
new file mode 100644
index 0000000000..9ae119f8fb
--- /dev/null
+++ b/ruby_1_8_5/lib/debug.rb
@@ -0,0 +1,947 @@
+# Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+# Copyright (C) 2000 Information-technology Promotion Agency, Japan
+# Copyright (C) 2000-2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>
+
+if $SAFE > 0
+ STDERR.print "-r debug.rb is not available in safe mode\n"
+ exit 1
+end
+
+require 'tracer'
+require 'pp'
+
+class Tracer
+ def Tracer.trace_func(*vars)
+ Single.trace_func(*vars)
+ end
+end
+
+SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
+
+class DEBUGGER__
+class Mutex
+ def initialize
+ @locker = nil
+ @waiting = []
+ @locked = false;
+ end
+
+ def locked?
+ @locked
+ end
+
+ def lock
+ return if Thread.critical
+ return if @locker == Thread.current
+ while (Thread.critical = true; @locked)
+ @waiting.push Thread.current
+ Thread.stop
+ end
+ @locked = true
+ @locker = Thread.current
+ Thread.critical = false
+ self
+ end
+
+ def unlock
+ return if Thread.critical
+ return unless @locked
+ unless @locker == Thread.current
+ raise RuntimeError, "unlocked by other"
+ end
+ Thread.critical = true
+ t = @waiting.shift
+ @locked = false
+ @locker = nil
+ Thread.critical = false
+ t.run if t
+ self
+ end
+end
+MUTEX = Mutex.new
+
+class Context
+ DEBUG_LAST_CMD = []
+
+ begin
+ require 'readline'
+ def readline(prompt, hist)
+ Readline::readline(prompt, hist)
+ end
+ rescue LoadError
+ def readline(prompt, hist)
+ STDOUT.print prompt
+ STDOUT.flush
+ line = STDIN.gets
+ exit unless line
+ line.chomp!
+ line
+ end
+ USE_READLINE = false
+ end
+
+ def initialize
+ if Thread.current == Thread.main
+ @stop_next = 1
+ else
+ @stop_next = 0
+ end
+ @last_file = nil
+ @file = nil
+ @line = nil
+ @no_step = nil
+ @frames = []
+ @finish_pos = 0
+ @trace = false
+ @catch = "StandardError"
+ @suspend_next = false
+ end
+
+ def stop_next(n=1)
+ @stop_next = n
+ end
+
+ def set_suspend
+ @suspend_next = true
+ end
+
+ def clear_suspend
+ @suspend_next = false
+ end
+
+ def suspend_all
+ DEBUGGER__.suspend
+ end
+
+ def resume_all
+ DEBUGGER__.resume
+ end
+
+ def check_suspend
+ return if Thread.critical
+ while (Thread.critical = true; @suspend_next)
+ DEBUGGER__.waiting.push Thread.current
+ @suspend_next = false
+ Thread.stop
+ end
+ Thread.critical = false
+ end
+
+ def trace?
+ @trace
+ end
+
+ def set_trace(arg)
+ @trace = arg
+ end
+
+ def stdout
+ DEBUGGER__.stdout
+ end
+
+ def break_points
+ DEBUGGER__.break_points
+ end
+
+ def display
+ DEBUGGER__.display
+ end
+
+ def context(th)
+ DEBUGGER__.context(th)
+ end
+
+ def set_trace_all(arg)
+ DEBUGGER__.set_trace(arg)
+ end
+
+ def set_last_thread(th)
+ DEBUGGER__.set_last_thread(th)
+ end
+
+ def debug_eval(str, binding)
+ begin
+ val = eval(str, binding)
+ rescue StandardError, ScriptError => e
+ at = eval("caller(1)", binding)
+ stdout.printf "%s:%s\n", at.shift, e.to_s.sub(/\(eval\):1:(in `.*?':)?/, '')
+ for i in at
+ stdout.printf "\tfrom %s\n", i
+ end
+ throw :debug_error
+ end
+ end
+
+ def debug_silent_eval(str, binding)
+ begin
+ eval(str, binding)
+ rescue StandardError, ScriptError
+ nil
+ end
+ end
+
+ def var_list(ary, binding)
+ ary.sort!
+ for v in ary
+ stdout.printf " %s => %s\n", v, eval(v, binding).inspect
+ end
+ end
+
+ def debug_variable_info(input, binding)
+ case input
+ when /^\s*g(?:lobal)?\s*$/
+ var_list(global_variables, binding)
+
+ when /^\s*l(?:ocal)?\s*$/
+ var_list(eval("local_variables", binding), binding)
+
+ when /^\s*i(?:nstance)?\s+/
+ obj = debug_eval($', binding)
+ var_list(obj.instance_variables, obj.instance_eval{binding()})
+
+ when /^\s*c(?:onst(?:ant)?)?\s+/
+ obj = debug_eval($', binding)
+ unless obj.kind_of? Module
+ stdout.print "Should be Class/Module: ", $', "\n"
+ else
+ var_list(obj.constants, obj.module_eval{binding()})
+ end
+ end
+ end
+
+ def debug_method_info(input, binding)
+ case input
+ when /^i(:?nstance)?\s+/
+ obj = debug_eval($', binding)
+
+ len = 0
+ for v in obj.methods.sort
+ len += v.size + 1
+ if len > 70
+ len = v.size + 1
+ stdout.print "\n"
+ end
+ stdout.print v, " "
+ end
+ stdout.print "\n"
+
+ else
+ obj = debug_eval(input, binding)
+ unless obj.kind_of? Module
+ stdout.print "Should be Class/Module: ", input, "\n"
+ else
+ len = 0
+ for v in obj.instance_methods(false).sort
+ len += v.size + 1
+ if len > 70
+ len = v.size + 1
+ stdout.print "\n"
+ end
+ stdout.print v, " "
+ end
+ stdout.print "\n"
+ end
+ end
+ end
+
+ def thnum
+ num = DEBUGGER__.instance_eval{@thread_list[Thread.current]}
+ unless num
+ DEBUGGER__.make_thread_list
+ num = DEBUGGER__.instance_eval{@thread_list[Thread.current]}
+ end
+ num
+ end
+
+ def debug_command(file, line, id, binding)
+ MUTEX.lock
+ unless defined?($debugger_restart) and $debugger_restart
+ callcc{|c| $debugger_restart = c}
+ end
+ set_last_thread(Thread.current)
+ frame_pos = 0
+ binding_file = file
+ binding_line = line
+ previous_line = nil
+ if ENV['EMACS']
+ stdout.printf "\032\032%s:%d:\n", binding_file, binding_line
+ else
+ stdout.printf "%s:%d:%s", binding_file, binding_line,
+ line_at(binding_file, binding_line)
+ end
+ @frames[0] = [binding, file, line, id]
+ display_expressions(binding)
+ prompt = true
+ while prompt and input = readline("(rdb:%d) "%thnum(), true)
+ catch(:debug_error) do
+ if input == ""
+ next unless DEBUG_LAST_CMD[0]
+ input = DEBUG_LAST_CMD[0]
+ stdout.print input, "\n"
+ else
+ DEBUG_LAST_CMD[0] = input
+ end
+
+ case input
+ when /^\s*tr(?:ace)?(?:\s+(on|off))?(?:\s+(all))?$/
+ if defined?( $2 )
+ if $1 == 'on'
+ set_trace_all true
+ else
+ set_trace_all false
+ end
+ elsif defined?( $1 )
+ if $1 == 'on'
+ set_trace true
+ else
+ set_trace false
+ end
+ end
+ if trace?
+ stdout.print "Trace on.\n"
+ else
+ stdout.print "Trace off.\n"
+ end
+
+ when /^\s*b(?:reak)?\s+(?:(.+):)?([^.:]+)$/
+ pos = $2
+ if $1
+ klass = debug_silent_eval($1, binding)
+ file = $1
+ end
+ if pos =~ /^\d+$/
+ pname = pos
+ pos = pos.to_i
+ else
+ pname = pos = pos.intern.id2name
+ end
+ break_points.push [true, 0, klass || file, pos]
+ stdout.printf "Set breakpoint %d at %s:%s\n", break_points.size, klass || file, pname
+
+ when /^\s*b(?:reak)?\s+(.+)[#.]([^.:]+)$/
+ pos = $2.intern.id2name
+ klass = debug_eval($1, binding)
+ break_points.push [true, 0, klass, pos]
+ stdout.printf "Set breakpoint %d at %s.%s\n", break_points.size, klass, pos
+
+ when /^\s*wat(?:ch)?\s+(.+)$/
+ exp = $1
+ break_points.push [true, 1, exp]
+ stdout.printf "Set watchpoint %d:%s\n", break_points.size, exp
+
+ when /^\s*b(?:reak)?$/
+ if break_points.find{|b| b[1] == 0}
+ n = 1
+ stdout.print "Breakpoints:\n"
+ for b in break_points
+ if b[0] and b[1] == 0
+ stdout.printf " %d %s:%s\n", n, b[2], b[3]
+ end
+ n += 1
+ end
+ end
+ if break_points.find{|b| b[1] == 1}
+ n = 1
+ stdout.print "\n"
+ stdout.print "Watchpoints:\n"
+ for b in break_points
+ if b[0] and b[1] == 1
+ stdout.printf " %d %s\n", n, b[2]
+ end
+ n += 1
+ end
+ end
+ if break_points.size == 0
+ stdout.print "No breakpoints\n"
+ else
+ stdout.print "\n"
+ end
+
+ when /^\s*del(?:ete)?(?:\s+(\d+))?$/
+ pos = $1
+ unless pos
+ input = readline("Clear all breakpoints? (y/n) ", false)
+ if input == "y"
+ for b in break_points
+ b[0] = false
+ end
+ end
+ else
+ pos = pos.to_i
+ if break_points[pos-1]
+ break_points[pos-1][0] = false
+ else
+ stdout.printf "Breakpoint %d is not defined\n", pos
+ end
+ end
+
+ when /^\s*disp(?:lay)?\s+(.+)$/
+ exp = $1
+ display.push [true, exp]
+ stdout.printf "%d: ", display.size
+ display_expression(exp, binding)
+
+ when /^\s*disp(?:lay)?$/
+ display_expressions(binding)
+
+ when /^\s*undisp(?:lay)?(?:\s+(\d+))?$/
+ pos = $1
+ unless pos
+ input = readline("Clear all expressions? (y/n) ", false)
+ if input == "y"
+ for d in display
+ d[0] = false
+ end
+ end
+ else
+ pos = pos.to_i
+ if display[pos-1]
+ display[pos-1][0] = false
+ else
+ stdout.printf "Display expression %d is not defined\n", pos
+ end
+ end
+
+ when /^\s*c(?:ont)?$/
+ prompt = false
+
+ when /^\s*s(?:tep)?(?:\s+(\d+))?$/
+ if $1
+ lev = $1.to_i
+ else
+ lev = 1
+ end
+ @stop_next = lev
+ prompt = false
+
+ when /^\s*n(?:ext)?(?:\s+(\d+))?$/
+ if $1
+ lev = $1.to_i
+ else
+ lev = 1
+ end
+ @stop_next = lev
+ @no_step = @frames.size - frame_pos
+ prompt = false
+
+ when /^\s*w(?:here)?$/, /^\s*f(?:rame)?$/
+ display_frames(frame_pos)
+
+ when /^\s*l(?:ist)?(?:\s+(.+))?$/
+ if not $1
+ b = previous_line ? previous_line + 10 : binding_line - 5
+ e = b + 9
+ elsif $1 == '-'
+ b = previous_line ? previous_line - 10 : binding_line - 5
+ e = b + 9
+ else
+ b, e = $1.split(/[-,]/)
+ if e
+ b = b.to_i
+ e = e.to_i
+ else
+ b = b.to_i - 5
+ e = b + 9
+ end
+ end
+ previous_line = b
+ display_list(b, e, binding_file, binding_line)
+
+ when /^\s*up(?:\s+(\d+))?$/
+ previous_line = nil
+ if $1
+ lev = $1.to_i
+ else
+ lev = 1
+ end
+ frame_pos += lev
+ if frame_pos >= @frames.size
+ frame_pos = @frames.size - 1
+ stdout.print "At toplevel\n"
+ end
+ binding, binding_file, binding_line = @frames[frame_pos]
+ stdout.print format_frame(frame_pos)
+
+ when /^\s*down(?:\s+(\d+))?$/
+ previous_line = nil
+ if $1
+ lev = $1.to_i
+ else
+ lev = 1
+ end
+ frame_pos -= lev
+ if frame_pos < 0
+ frame_pos = 0
+ stdout.print "At stack bottom\n"
+ end
+ binding, binding_file, binding_line = @frames[frame_pos]
+ stdout.print format_frame(frame_pos)
+
+ when /^\s*fin(?:ish)?$/
+ if frame_pos == @frames.size
+ stdout.print "\"finish\" not meaningful in the outermost frame.\n"
+ else
+ @finish_pos = @frames.size - frame_pos
+ frame_pos = 0
+ prompt = false
+ end
+
+ when /^\s*cat(?:ch)?(?:\s+(.+))?$/
+ if $1
+ excn = $1
+ if excn == 'off'
+ @catch = nil
+ stdout.print "Clear catchpoint.\n"
+ else
+ @catch = excn
+ stdout.printf "Set catchpoint %s.\n", @catch
+ end
+ else
+ if @catch
+ stdout.printf "Catchpoint %s.\n", @catch
+ else
+ stdout.print "No catchpoint.\n"
+ end
+ end
+
+ when /^\s*q(?:uit)?$/
+ input = readline("Really quit? (y/n) ", false)
+ if input == "y"
+ exit! # exit -> exit!: No graceful way to stop threads...
+ end
+
+ when /^\s*v(?:ar)?\s+/
+ debug_variable_info($', binding)
+
+ when /^\s*m(?:ethod)?\s+/
+ debug_method_info($', binding)
+
+ when /^\s*th(?:read)?\s+/
+ if DEBUGGER__.debug_thread_info($', binding) == :cont
+ prompt = false
+ end
+
+ when /^\s*pp\s+/
+ PP.pp(debug_eval($', binding), stdout)
+
+ when /^\s*p\s+/
+ stdout.printf "%s\n", debug_eval($', binding).inspect
+
+ when /^\s*r(?:estart)?$/
+ $debugger_restart.call
+
+ when /^\s*h(?:elp)?$/
+ debug_print_help()
+
+ else
+ v = debug_eval(input, binding)
+ stdout.printf "%s\n", v.inspect
+ end
+ end
+ end
+ MUTEX.unlock
+ resume_all
+ end
+
+ def debug_print_help
+ stdout.print <<EOHELP
+Debugger help v.-0.002b
+Commands
+ b[reak] [file:|class:]<line|method>
+ b[reak] [class.]<line|method>
+ set breakpoint to some position
+ wat[ch] <expression> set watchpoint to some expression
+ cat[ch] (<exception>|off) set catchpoint to an exception
+ b[reak] list breakpoints
+ cat[ch] show catchpoint
+ del[ete][ nnn] delete some or all breakpoints
+ disp[lay] <expression> add expression into display expression list
+ undisp[lay][ nnn] delete one particular or all display expressions
+ c[ont] run until program ends or hit breakpoint
+ s[tep][ nnn] step (into methods) one line or till line nnn
+ n[ext][ nnn] go over one line or till line nnn
+ w[here] display frames
+ f[rame] alias for where
+ l[ist][ (-|nn-mm)] list program, - lists backwards
+ nn-mm lists given lines
+ up[ nn] move to higher frame
+ down[ nn] move to lower frame
+ fin[ish] return to outer frame
+ tr[ace] (on|off) set trace mode of current thread
+ tr[ace] (on|off) all set trace mode of all threads
+ q[uit] exit from debugger
+ v[ar] g[lobal] show global variables
+ v[ar] l[ocal] show local variables
+ v[ar] i[nstance] <object> show instance variables of object
+ v[ar] c[onst] <object> show constants of object
+ m[ethod] i[nstance] <obj> show methods of object
+ m[ethod] <class|module> show instance methods of class or module
+ th[read] l[ist] list all threads
+ th[read] c[ur[rent]] show current thread
+ th[read] [sw[itch]] <nnn> switch thread context to nnn
+ th[read] stop <nnn> stop thread nnn
+ th[read] resume <nnn> resume thread nnn
+ p expression evaluate expression and print its value
+ h[elp] print this help
+ <everything else> evaluate
+EOHELP
+ end
+
+ def display_expressions(binding)
+ n = 1
+ for d in display
+ if d[0]
+ stdout.printf "%d: ", n
+ display_expression(d[1], binding)
+ end
+ n += 1
+ end
+ end
+
+ def display_expression(exp, binding)
+ stdout.printf "%s = %s\n", exp, debug_silent_eval(exp, binding).to_s
+ end
+
+ def frame_set_pos(file, line)
+ if @frames[0]
+ @frames[0][1] = file
+ @frames[0][2] = line
+ end
+ end
+
+ def display_frames(pos)
+ 0.upto(@frames.size - 1) do |n|
+ if n == pos
+ stdout.print "--> "
+ else
+ stdout.print " "
+ end
+ stdout.print format_frame(n)
+ end
+ end
+
+ def format_frame(pos)
+ bind, file, line, id = @frames[pos]
+ sprintf "#%d %s:%s%s\n", pos + 1, file, line,
+ (id ? ":in `#{id.id2name}'" : "")
+ end
+
+ def display_list(b, e, file, line)
+ stdout.printf "[%d, %d] in %s\n", b, e, file
+ if lines = SCRIPT_LINES__[file] and lines != true
+ n = 0
+ b.upto(e) do |n|
+ if n > 0 && lines[n-1]
+ if n == line
+ stdout.printf "=> %d %s\n", n, lines[n-1].chomp
+ else
+ stdout.printf " %d %s\n", n, lines[n-1].chomp
+ end
+ end
+ end
+ else
+ stdout.printf "No sourcefile available for %s\n", file
+ end
+ end
+
+ def line_at(file, line)
+ lines = SCRIPT_LINES__[file]
+ if lines
+ return "\n" if lines == true
+ line = lines[line-1]
+ return "\n" unless line
+ return line
+ end
+ return "\n"
+ end
+
+ def debug_funcname(id)
+ if id.nil?
+ "toplevel"
+ else
+ id.id2name
+ end
+ end
+
+ def check_break_points(file, klass, pos, binding, id)
+ return false if break_points.empty?
+ n = 1
+ for b in break_points
+ if b[0] # valid
+ if b[1] == 0 # breakpoint
+ if (b[2] == file and b[3] == pos) or
+ (klass and b[2] == klass and b[3] == pos)
+ stdout.printf "Breakpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos
+ return true
+ end
+ elsif b[1] == 1 # watchpoint
+ if debug_silent_eval(b[2], binding)
+ stdout.printf "Watchpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos
+ return true
+ end
+ end
+ end
+ n += 1
+ end
+ return false
+ end
+
+ def excn_handle(file, line, id, binding)
+ if $!.class <= SystemExit
+ set_trace_func nil
+ exit
+ end
+
+ if @catch and ($!.class.ancestors.find { |e| e.to_s == @catch })
+ stdout.printf "%s:%d: `%s' (%s)\n", file, line, $!, $!.class
+ fs = @frames.size
+ tb = caller(0)[-fs..-1]
+ if tb
+ for i in tb
+ stdout.printf "\tfrom %s\n", i
+ end
+ end
+ suspend_all
+ debug_command(file, line, id, binding)
+ end
+ end
+
+ def trace_func(event, file, line, id, binding, klass)
+ Tracer.trace_func(event, file, line, id, binding, klass) if trace?
+ context(Thread.current).check_suspend
+ @file = file
+ @line = line
+ case event
+ when 'line'
+ frame_set_pos(file, line)
+ if !@no_step or @frames.size == @no_step
+ @stop_next -= 1
+ @stop_next = -1 if @stop_next < 0
+ elsif @frames.size < @no_step
+ @stop_next = 0 # break here before leaving...
+ else
+ # nothing to do. skipped.
+ end
+ if @stop_next == 0 or check_break_points(file, nil, line, binding, id)
+ @no_step = nil
+ suspend_all
+ debug_command(file, line, id, binding)
+ end
+
+ when 'call'
+ @frames.unshift [binding, file, line, id]
+ if check_break_points(file, klass, id.id2name, binding, id)
+ suspend_all
+ debug_command(file, line, id, binding)
+ end
+
+ when 'c-call'
+ frame_set_pos(file, line)
+
+ when 'class'
+ @frames.unshift [binding, file, line, id]
+
+ when 'return', 'end'
+ if @frames.size == @finish_pos
+ @stop_next = 1
+ @finish_pos = 0
+ end
+ @frames.shift
+
+ when 'end'
+ @frames.shift
+
+ when 'raise'
+ excn_handle(file, line, id, binding)
+
+ end
+ @last_file = file
+ end
+end
+
+trap("INT") { DEBUGGER__.interrupt }
+@last_thread = Thread::main
+@max_thread = 1
+@thread_list = {Thread::main => 1}
+@break_points = []
+@display = []
+@waiting = []
+@stdout = STDOUT
+
+class << DEBUGGER__
+ def stdout
+ @stdout
+ end
+
+ def stdout=(s)
+ @stdout = s
+ end
+
+ def display
+ @display
+ end
+
+ def break_points
+ @break_points
+ end
+
+ def waiting
+ @waiting
+ end
+
+ def set_trace( arg )
+ saved_crit = Thread.critical
+ Thread.critical = true
+ make_thread_list
+ for th, in @thread_list
+ context(th).set_trace arg
+ end
+ Thread.critical = saved_crit
+ arg
+ end
+
+ def set_last_thread(th)
+ @last_thread = th
+ end
+
+ def suspend
+ saved_crit = Thread.critical
+ Thread.critical = true
+ make_thread_list
+ for th, in @thread_list
+ next if th == Thread.current
+ context(th).set_suspend
+ end
+ Thread.critical = saved_crit
+ # Schedule other threads to suspend as soon as possible.
+ Thread.pass unless Thread.critical
+ end
+
+ def resume
+ saved_crit = Thread.critical
+ Thread.critical = true
+ make_thread_list
+ for th, in @thread_list
+ next if th == Thread.current
+ context(th).clear_suspend
+ end
+ waiting.each do |th|
+ th.run
+ end
+ waiting.clear
+ Thread.critical = saved_crit
+ # Schedule other threads to restart as soon as possible.
+ Thread.pass
+ end
+
+ def context(thread=Thread.current)
+ c = thread[:__debugger_data__]
+ unless c
+ thread[:__debugger_data__] = c = Context.new
+ end
+ c
+ end
+
+ def interrupt
+ context(@last_thread).stop_next
+ end
+
+ def get_thread(num)
+ th = @thread_list.index(num)
+ unless th
+ @stdout.print "No thread ##{num}\n"
+ throw :debug_error
+ end
+ th
+ end
+
+ def thread_list(num)
+ th = get_thread(num)
+ if th == Thread.current
+ @stdout.print "+"
+ else
+ @stdout.print " "
+ end
+ @stdout.printf "%d ", num
+ @stdout.print th.inspect, "\t"
+ file = context(th).instance_eval{@file}
+ if file
+ @stdout.print file,":",context(th).instance_eval{@line}
+ end
+ @stdout.print "\n"
+ end
+
+ def thread_list_all
+ for th in @thread_list.values.sort
+ thread_list(th)
+ end
+ end
+
+ def make_thread_list
+ hash = {}
+ for th in Thread::list
+ if @thread_list.key? th
+ hash[th] = @thread_list[th]
+ else
+ @max_thread += 1
+ hash[th] = @max_thread
+ end
+ end
+ @thread_list = hash
+ end
+
+ def debug_thread_info(input, binding)
+ case input
+ when /^l(?:ist)?/
+ make_thread_list
+ thread_list_all
+
+ when /^c(?:ur(?:rent)?)?$/
+ make_thread_list
+ thread_list(@thread_list[Thread.current])
+
+ when /^(?:sw(?:itch)?\s+)?(\d+)/
+ make_thread_list
+ th = get_thread($1.to_i)
+ if th == Thread.current
+ @stdout.print "It's the current thread.\n"
+ else
+ thread_list(@thread_list[th])
+ context(th).stop_next
+ th.run
+ return :cont
+ end
+
+ when /^stop\s+(\d+)/
+ make_thread_list
+ th = get_thread($1.to_i)
+ if th == Thread.current
+ @stdout.print "It's the current thread.\n"
+ elsif th.stop?
+ @stdout.print "Already stopped.\n"
+ else
+ thread_list(@thread_list[th])
+ context(th).suspend
+ end
+
+ when /^resume\s+(\d+)/
+ make_thread_list
+ th = get_thread($1.to_i)
+ if th == Thread.current
+ @stdout.print "It's the current thread.\n"
+ elsif !th.stop?
+ @stdout.print "Already running."
+ else
+ thread_list(@thread_list[th])
+ th.run
+ end
+ end
+ end
+end
+
+stdout.printf "Debug.rb\n"
+stdout.printf "Emacs support available.\n\n"
+set_trace_func proc { |event, file, line, id, binding, klass, *rest|
+ DEBUGGER__.context.trace_func event, file, line, id, binding, klass
+}
+end
diff --git a/ruby_1_8_5/lib/delegate.rb b/ruby_1_8_5/lib/delegate.rb
new file mode 100644
index 0000000000..93c9803a18
--- /dev/null
+++ b/ruby_1_8_5/lib/delegate.rb
@@ -0,0 +1,334 @@
+# = delegate -- Support for the Delegation Pattern
+#
+# Documentation by James Edward Gray II and Gavin Sinclair
+#
+# == Introduction
+#
+# This library provides three different ways to delegate method calls to an
+# object. The easiest to use is SimpleDelegator. Pass an object to the
+# constructor and all methods supported by the object will be delegated. This
+# object can be changed later.
+#
+# Going a step further, the top level DelegateClass method allows you to easily
+# setup delegation through class inheritance. This is considerably more
+# flexible and thus probably the most common use for this library.
+#
+# Finally, if you need full control over the delegation scheme, you can inherit
+# from the abstract class Delegator and customize as needed. (If you find
+# yourself needing this control, have a look at _forwardable_, also in the
+# standard library. It may suit your needs better.)
+#
+# == Notes
+#
+# Be advised, RDoc will not detect delegated methods.
+#
+# <b>delegate.rb provides full-class delegation via the
+# DelegateClass() method. For single-method delegation via
+# def_delegator(), see forwardable.rb.</b>
+#
+# == Examples
+#
+# === SimpleDelegator
+#
+# Here's a simple example that takes advantage of the fact that
+# SimpleDelegator's delegation object can be changed at any time.
+#
+# class Stats
+# def initialize
+# @source = SimpleDelegator.new([])
+# end
+#
+# def stats( records )
+# @source.__setobj__(records)
+#
+# "Elements: #{@source.size}\n" +
+# " Non-Nil: #{@source.compact.size}\n" +
+# " Unique: #{@source.uniq.size}\n"
+# end
+# end
+#
+# s = Stats.new
+# puts s.stats(%w{James Edward Gray II})
+# puts
+# puts s.stats([1, 2, 3, nil, 4, 5, 1, 2])
+#
+# <i>Prints:</i>
+#
+# Elements: 4
+# Non-Nil: 4
+# Unique: 4
+#
+# Elements: 8
+# Non-Nil: 7
+# Unique: 6
+#
+# === DelegateClass()
+#
+# Here's a sample of use from <i>tempfile.rb</i>.
+#
+# A _Tempfile_ object is really just a _File_ object with a few special rules
+# about storage location and/or when the File should be deleted. That makes for
+# an almost textbook perfect example of how to use delegation.
+#
+# class Tempfile < DelegateClass(File)
+# # constant and class member data initialization...
+#
+# def initialize(basename, tmpdir=Dir::tmpdir)
+# # build up file path/name in var tmpname...
+#
+# @tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600)
+#
+# # ...
+#
+# super(@tmpfile)
+#
+# # below this point, all methods of File are supported...
+# end
+#
+# # ...
+# end
+#
+# === Delegator
+#
+# SimpleDelegator's implementation serves as a nice example here.
+#
+# class SimpleDelegator < Delegator
+# def initialize(obj)
+# super # pass obj to Delegator constructor, required
+# @_sd_obj = obj # store obj for future use
+# end
+#
+# def __getobj__
+# @_sd_obj # return object we are delegating to, required
+# end
+#
+# def __setobj__(obj)
+# @_sd_obj = obj # change delegation object, a feature we're providing
+# end
+#
+# # ...
+# end
+
+#
+# Delegator is an abstract class used to build delegator pattern objects from
+# subclasses. Subclasses should redefine \_\_getobj\_\_. For a concrete
+# implementation, see SimpleDelegator.
+#
+class Delegator
+
+ #
+ # Pass in the _obj_ to delegate method calls to. All methods supported by
+ # _obj_ will be delegated to.
+ #
+ def initialize(obj)
+ preserved = ::Kernel.public_instance_methods(false)
+ preserved -= ["to_s","to_a","inspect","==","=~","==="]
+ for t in self.class.ancestors
+ preserved |= t.public_instance_methods(false)
+ preserved |= t.private_instance_methods(false)
+ preserved |= t.protected_instance_methods(false)
+ break if t == Delegator
+ end
+ preserved << "singleton_method_added"
+ for method in obj.methods
+ next if preserved.include? method
+ begin
+ eval <<-EOS
+ def self.#{method}(*args, &block)
+ begin
+ __getobj__.__send__(:#{method}, *args, &block)
+ rescue Exception
+ $@.delete_if{|s| /:in `__getobj__'$/ =~ s} #`
+ $@.delete_if{|s| /^\\(eval\\):/ =~ s}
+ Kernel::raise
+ end
+ end
+ EOS
+ rescue SyntaxError
+ raise NameError, "invalid identifier %s" % method, caller(4)
+ end
+ end
+ end
+ alias initialize_methods initialize
+
+ # Handles the magic of delegation through \_\_getobj\_\_.
+ def method_missing(m, *args)
+ target = self.__getobj__
+ unless target.respond_to?(m)
+ super(m, *args)
+ end
+ target.__send__(m, *args)
+ end
+
+ #
+ # Checks for a method provided by this the delegate object by fowarding the
+ # call through \_\_getobj\_\_.
+ #
+ def respond_to?(m)
+ return true if super
+ return self.__getobj__.respond_to?(m)
+ end
+
+ #
+ # This method must be overridden by subclasses and should return the object
+ # method calls are being delegated to.
+ #
+ def __getobj__
+ raise NotImplementedError, "need to define `__getobj__'"
+ end
+
+ # Serialization support for the object returned by \_\_getobj\_\_.
+ def marshal_dump
+ __getobj__
+ end
+ # Reinitializes delegation from a serialized object.
+ def marshal_load(obj)
+ initialize_methods(obj)
+ end
+end
+
+#
+# A concrete implementation of Delegator, this class provides the means to
+# delegate all supported method calls to the object passed into the constructor
+# and even to change the object being delegated to at a later time with
+# \_\_setobj\_\_ .
+#
+class SimpleDelegator<Delegator
+
+ # Pass in the _obj_ you would like to delegate method calls to.
+ def initialize(obj)
+ super
+ @_sd_obj = obj
+ end
+
+ # Returns the current object method calls are being delegated to.
+ def __getobj__
+ @_sd_obj
+ end
+
+ #
+ # Changes the delegate object to _obj_.
+ #
+ # It's important to note that this does *not* cause SimpleDelegator's methods
+ # to change. Because of this, you probably only want to change delegation
+ # to objects of the same type as the original delegate.
+ #
+ # Here's an example of changing the delegation object.
+ #
+ # names = SimpleDelegator.new(%w{James Edward Gray II})
+ # puts names[1] # => Edward
+ # names.__setobj__(%w{Gavin Sinclair})
+ # puts names[1] # => Sinclair
+ #
+ def __setobj__(obj)
+ raise ArgumentError, "cannot delegate to self" if self.equal?(obj)
+ @_sd_obj = obj
+ end
+
+ # Clone support for the object returned by \_\_getobj\_\_.
+ def clone
+ super
+ __setobj__(__getobj__.clone)
+ end
+ # Duplication support for the object returned by \_\_getobj\_\_.
+ def dup(obj)
+ super
+ __setobj__(__getobj__.dup)
+ end
+end
+
+# :stopdoc:
+# backward compatibility ^_^;;;
+Delegater = Delegator
+SimpleDelegater = SimpleDelegator
+# :startdoc:
+
+#
+# The primary interface to this library. Use to setup delegation when defining
+# your class.
+#
+# class MyClass < DelegateClass( ClassToDelegateTo ) # Step 1
+# def initiaize
+# super(obj_of_ClassToDelegateTo) # Step 2
+# end
+# end
+#
+def DelegateClass(superclass)
+ klass = Class.new
+ methods = superclass.public_instance_methods(true)
+ methods -= ::Kernel.public_instance_methods(false)
+ methods |= ["to_s","to_a","inspect","==","=~","==="]
+ klass.module_eval {
+ def initialize(obj) # :nodoc:
+ @_dc_obj = obj
+ end
+ def method_missing(m, *args) # :nodoc:
+ unless @_dc_obj.respond_to?(m)
+ super(m, *args)
+ end
+ @_dc_obj.__send__(m, *args)
+ end
+ def respond_to?(m) # :nodoc:
+ return true if super
+ return @_dc_obj.respond_to?(m)
+ end
+ def __getobj__ # :nodoc:
+ @_dc_obj
+ end
+ def __setobj__(obj) # :nodoc:
+ raise ArgumentError, "cannot delegate to self" if self.equal?(obj)
+ @_dc_obj = obj
+ end
+ def clone # :nodoc:
+ super
+ __setobj__(__getobj__.clone)
+ end
+ def dup # :nodoc:
+ super
+ __setobj__(__getobj__.dup)
+ end
+ }
+ for method in methods
+ begin
+ klass.module_eval <<-EOS
+ def #{method}(*args, &block)
+ begin
+ @_dc_obj.__send__(:#{method}, *args, &block)
+ rescue
+ $@[0,2] = nil
+ raise
+ end
+ end
+ EOS
+ rescue SyntaxError
+ raise NameError, "invalid identifier %s" % method, caller(3)
+ end
+ end
+ return klass
+end
+
+# :enddoc:
+
+if __FILE__ == $0
+ class ExtArray<DelegateClass(Array)
+ def initialize()
+ super([])
+ end
+ end
+
+ ary = ExtArray.new
+ p ary.class
+ ary.push 25
+ p ary
+
+ foo = Object.new
+ def foo.test
+ 25
+ end
+ def foo.error
+ raise 'this is OK'
+ end
+ foo2 = SimpleDelegator.new(foo)
+ p foo.test == foo2.test # => true
+ foo2.error # raise error!
+end
diff --git a/ruby_1_8_5/lib/drb.rb b/ruby_1_8_5/lib/drb.rb
new file mode 100644
index 0000000000..93cc811e14
--- /dev/null
+++ b/ruby_1_8_5/lib/drb.rb
@@ -0,0 +1,2 @@
+require 'drb/drb'
+
diff --git a/ruby_1_8_5/lib/drb/acl.rb b/ruby_1_8_5/lib/drb/acl.rb
new file mode 100644
index 0000000000..aa86dbe70f
--- /dev/null
+++ b/ruby_1_8_5/lib/drb/acl.rb
@@ -0,0 +1,144 @@
+# acl-2.0 - simple Access Control List
+#
+# Copyright (c) 2000,2002,2003 Masatoshi SEKI
+#
+# acl.rb is copyrighted free software by Masatoshi SEKI.
+# You can redistribute it and/or modify it under the same terms as Ruby.
+
+require 'ipaddr'
+
+class ACL
+ VERSION=["2.0.0"]
+ class ACLEntry
+ def initialize(str)
+ if str == '*' or str == 'all'
+ @pat = [:all]
+ else
+ begin
+ @pat = [:ip, IPAddr.new(str)]
+ rescue ArgumentError
+ @pat = [:name, dot_pat(str)]
+ end
+ end
+ end
+
+ private
+ def dot_pat_str(str)
+ list = str.split('.').collect { |s|
+ (s == '*') ? '.+' : s
+ }
+ list.join("\\.")
+ end
+
+ private
+ def dot_pat(str)
+ exp = "^" + dot_pat_str(str) + "$"
+ Regexp.new(exp)
+ end
+
+ public
+ def match(addr)
+ case @pat[0]
+ when :all
+ true
+ when :ip
+ begin
+ ipaddr = IPAddr.new(addr[3])
+ ipaddr = ipaddr.ipv4_mapped if @pat[1].ipv6? && ipaddr.ipv4?
+ rescue ArgumentError
+ return false
+ end
+ (@pat[1].include?(ipaddr)) ? true : false
+ when :name
+ (@pat[1] =~ addr[2]) ? true : false
+ else
+ false
+ end
+ end
+ end
+
+ class ACLList
+ def initialize
+ @list = []
+ end
+
+ public
+ def match(addr)
+ @list.each do |e|
+ return true if e.match(addr)
+ end
+ false
+ end
+
+ public
+ def add(str)
+ @list.push(ACLEntry.new(str))
+ end
+ end
+
+ DENY_ALLOW = 0
+ ALLOW_DENY = 1
+
+ def initialize(list=nil, order = DENY_ALLOW)
+ @order = order
+ @deny = ACLList.new
+ @allow = ACLList.new
+ install_list(list) if list
+ end
+
+ public
+ def allow_socket?(soc)
+ allow_addr?(soc.peeraddr)
+ end
+
+ public
+ def allow_addr?(addr)
+ case @order
+ when DENY_ALLOW
+ return true if @allow.match(addr)
+ return false if @deny.match(addr)
+ return true
+ when ALLOW_DENY
+ return false if @deny.match(addr)
+ return true if @allow.match(addr)
+ return false
+ else
+ false
+ end
+ end
+
+ public
+ def install_list(list)
+ i = 0
+ while i < list.size
+ permission, domain = list.slice(i,2)
+ case permission.downcase
+ when 'allow'
+ @allow.add(domain)
+ when 'deny'
+ @deny.add(domain)
+ else
+ raise "Invalid ACL entry #{list.to_s}"
+ end
+ i += 2
+ end
+ end
+end
+
+if __FILE__ == $0
+ # example
+ list = %w(deny all
+ allow 192.168.1.1
+ allow ::ffff:192.168.1.2
+ allow 192.168.1.3
+ )
+
+ addr = ["AF_INET", 10, "lc630", "192.168.1.3"]
+
+ acl = ACL.new
+ p acl.allow_addr?(addr)
+
+ acl = ACL.new(list, ACL::DENY_ALLOW)
+ p acl.allow_addr?(addr)
+end
+
diff --git a/ruby_1_8_5/lib/drb/drb.rb b/ruby_1_8_5/lib/drb/drb.rb
new file mode 100644
index 0000000000..25fbb3f788
--- /dev/null
+++ b/ruby_1_8_5/lib/drb/drb.rb
@@ -0,0 +1,1763 @@
+#
+# = drb/drb.rb
+#
+# Distributed Ruby: _dRuby_ version 2.0.4
+#
+# Copyright (c) 1999-2003 Masatoshi SEKI. You can redistribute it and/or
+# modify it under the same terms as Ruby.
+#
+# Author:: Masatoshi SEKI
+#
+# Documentation:: William Webber (william@williamwebber.com)
+#
+# == Overview
+#
+# dRuby is a distributed object system for Ruby. It allows an object in one
+# Ruby process to invoke methods on an object in another Ruby process on the
+# same or a different machine.
+#
+# The Ruby standard library contains the core classes of the dRuby package.
+# However, the full package also includes access control lists and the
+# Rinda tuple-space distributed task management system, as well as a
+# large number of samples. The full dRuby package can be downloaded from
+# the dRuby home page (see *References*).
+#
+# For an introduction and examples of usage see the documentation to the
+# DRb module.
+#
+# == References
+#
+# [http://www2a.biglobe.ne.jp/~seki/ruby/druby.html]
+# The dRuby home page, in Japanese. Contains the full dRuby package
+# and links to other Japanese-language sources.
+#
+# [http://www2a.biglobe.ne.jp/~seki/ruby/druby.en.html]
+# The English version of the dRuby home page.
+#
+# [http://www.chadfowler.com/ruby/drb.html]
+# A quick tutorial introduction to using dRuby by Chad Fowler.
+#
+# [http://www.linux-mag.com/2002-09/ruby_05.html]
+# A tutorial introduction to dRuby in Linux Magazine by Dave Thomas.
+# Includes a discussion of Rinda.
+#
+# [http://www.eng.cse.dmu.ac.uk/~hgs/ruby/dRuby/]
+# Links to English-language Ruby material collected by Hugh Sasse.
+#
+# [http://www.rubycentral.com/book/ospace.html]
+# The chapter from *Programming* *Ruby* by Dave Thomas and Andy Hunt
+# which discusses dRuby.
+#
+# [http://www.clio.ne.jp/home/web-i31s/Flotuard/Ruby/PRC2K_seki/dRuby.en.html]
+# Translation of presentation on Ruby by Masatoshi Seki.
+
+require 'socket'
+require 'thread'
+require 'fcntl'
+require 'drb/eq'
+
+#
+# == Overview
+#
+# dRuby is a distributed object system for Ruby. It is written in
+# pure Ruby and uses its own protocol. No add-in services are needed
+# beyond those provided by the Ruby runtime, such as TCP sockets. It
+# does not rely on or interoperate with other distributed object
+# systems such as CORBA, RMI, or .NET.
+#
+# dRuby allows methods to be called in one Ruby process upon a Ruby
+# object located in another Ruby process, even on another machine.
+# References to objects can be passed between processes. Method
+# arguments and return values are dumped and loaded in marshalled
+# format. All of this is done transparently to both the caller of the
+# remote method and the object that it is called upon.
+#
+# An object in a remote process is locally represented by a
+# DRb::DRbObject instance. This acts as a sort of proxy for the
+# remote object. Methods called upon this DRbObject instance are
+# forwarded to its remote object. This is arranged dynamically at run
+# time. There are no statically declared interfaces for remote
+# objects, such as CORBA's IDL.
+#
+# dRuby calls made into a process are handled by a DRb::DRbServer
+# instance within that process. This reconstitutes the method call,
+# invokes it upon the specified local object, and returns the value to
+# the remote caller. Any object can receive calls over dRuby. There
+# is no need to implement a special interface, or mixin special
+# functionality. Nor, in the general case, does an object need to
+# explicitly register itself with a DRbServer in order to receive
+# dRuby calls.
+#
+# One process wishing to make dRuby calls upon another process must
+# somehow obtain an initial reference to an object in the remote
+# process by some means other than as the return value of a remote
+# method call, as there is initially no remote object reference it can
+# invoke a method upon. This is done by attaching to the server by
+# URI. Each DRbServer binds itself to a URI such as
+# 'druby://example.com:8787'. A DRbServer can have an object attached
+# to it that acts as the server's *front* *object*. A DRbObject can
+# be explicitly created from the server's URI. This DRbObject's
+# remote object will be the server's front object. This front object
+# can then return references to other Ruby objects in the DRbServer's
+# process.
+#
+# Method calls made over dRuby behave largely the same as normal Ruby
+# method calls made within a process. Method calls with blocks are
+# supported, as are raising exceptions. In addition to a method's
+# standard errors, a dRuby call may also raise one of the
+# dRuby-specific errors, all of which are subclasses of DRb::DRbError.
+#
+# Any type of object can be passed as an argument to a dRuby call or
+# returned as its return value. By default, such objects are dumped
+# or marshalled at the local end, then loaded or unmarshalled at the
+# remote end. The remote end therefore receives a copy of the local
+# object, not a distributed reference to it; methods invoked upon this
+# copy are executed entirely in the remote process, not passed on to
+# the local original. This has semantics similar to pass-by-value.
+#
+# However, if an object cannot be marshalled, a dRuby reference to it
+# is passed or returned instead. This will turn up at the remote end
+# as a DRbObject instance. All methods invoked upon this remote proxy
+# are forwarded to the local object, as described in the discussion of
+# DRbObjects. This has semantics similar to the normal Ruby
+# pass-by-reference.
+#
+# The easiest way to signal that we want an otherwise marshallable
+# object to be passed or returned as a DRbObject reference, rather
+# than marshalled and sent as a copy, is to include the
+# DRb::DRbUndumped mixin module.
+#
+# dRuby supports calling remote methods with blocks. As blocks (or
+# rather the Proc objects that represent them) are not marshallable,
+# the block executes in the local, not the remote, context. Each
+# value yielded to the block is passed from the remote object to the
+# local block, then the value returned by each block invocation is
+# passed back to the remote execution context to be collected, before
+# the collected values are finally returned to the local context as
+# the return value of the method invocation.
+#
+# == Examples of usage
+#
+# For more dRuby samples, see the +samples+ directory in the full
+# dRuby distribution.
+#
+# === dRuby in client/server mode
+#
+# This illustrates setting up a simple client-server drb
+# system. Run the server and client code in different terminals,
+# starting the server code first.
+#
+# ==== Server code
+#
+# require 'drb/drb'
+#
+# # The URI for the server to connect to
+# URI="druby://localhost:8787"
+#
+# class TimeServer
+#
+# def get_current_time
+# return Time.now
+# end
+#
+# end
+#
+# # The object that handles requests on the server
+# FRONT_OBJECT=TimeServer.new
+#
+# $SAFE = 1 # disable eval() and friends
+#
+# DRb.start_service(URI, FRONT_OBJECT)
+# # Wait for the drb server thread to finish before exiting.
+# DRb.thread.join
+#
+# ==== Client code
+#
+# require 'drb/drb'
+#
+# # The URI to connect to
+# SERVER_URI="druby://localhost:8787"
+#
+# # Start a local DRbServer to handle callbacks.
+# #
+# # Not necessary for this small example, but will be required
+# # as soon as we pass a non-marshallable object as an argument
+# # to a dRuby call.
+# DRb.start_service
+#
+# timeserver = DRbObject.new_with_uri(SERVER_URI)
+# puts timeserver.get_current_time
+#
+# === Remote objects under dRuby
+#
+# This example illustrates returning a reference to an object
+# from a dRuby call. The Logger instances live in the server
+# process. References to them are returned to the client process,
+# where methods can be invoked upon them. These methods are
+# executed in the server process.
+#
+# ==== Server code
+#
+# require 'drb/drb'
+#
+# URI="druby://localhost:8787"
+#
+# class Logger
+#
+# # Make dRuby send Logger instances as dRuby references,
+# # not copies.
+# include DRb::DRbUndumped
+#
+# def initialize(n, fname)
+# @name = n
+# @filename = fname
+# end
+#
+# def log(message)
+# File.open(@filename, "a") do |f|
+# f.puts("#{Time.now}: #{@name}: #{message}")
+# end
+# end
+#
+# end
+#
+# # We have a central object for creating and retrieving loggers.
+# # This retains a local reference to all loggers created. This
+# # is so an existing logger can be looked up by name, but also
+# # to prevent loggers from being garbage collected. A dRuby
+# # reference to an object is not sufficient to prevent it being
+# # garbage collected!
+# class LoggerFactory
+#
+# def initialize(bdir)
+# @basedir = bdir
+# @loggers = {}
+# end
+#
+# def get_logger(name)
+# if !@loggers.has_key? name
+# # make the filename safe, then declare it to be so
+# fname = name.gsub(/[.\/]/, "_").untaint
+# @loggers[name] = Logger.new(name, @basedir + "/" + fname)
+# end
+# return @loggers[name]
+# end
+#
+# end
+#
+# FRONT_OBJECT=LoggerFactory.new("/tmp/dlog")
+#
+# $SAFE = 1 # disable eval() and friends
+#
+# DRb.start_service(URI, FRONT_OBJECT)
+# DRb.thread.join
+#
+# ==== Client code
+#
+# require 'drb/drb'
+#
+# SERVER_URI="druby://localhost:8787"
+#
+# DRb.start_service
+#
+# log_service=DRbObject.new_with_uri(SERVER_URI)
+#
+# ["loga", "logb", "logc"].each do |logname|
+#
+# logger=log_service.get_logger(logname)
+#
+# logger.log("Hello, world!")
+# logger.log("Goodbye, world!")
+# logger.log("=== EOT ===")
+#
+# end
+#
+# == Security
+#
+# As with all network services, security needs to be considered when
+# using dRuby. By allowing external access to a Ruby object, you are
+# not only allowing outside clients to call the methods you have
+# defined for that object, but by default to execute arbitrary Ruby
+# code on your server. Consider the following:
+#
+# # !!! UNSAFE CODE !!!
+# ro = DRbObject::new_with_uri("druby://your.server.com:8989")
+# class << ro
+# undef :instance_eval # force call to be passed to remote object
+# end
+# ro.instance_eval("`rm -rf *`")
+#
+# The dangers posed by instance_eval and friends are such that a
+# DRbServer should generally be run with $SAFE set to at least
+# level 1. This will disable eval() and related calls on strings
+# passed across the wire. The sample usage code given above follows
+# this practice.
+#
+# A DRbServer can be configured with an access control list to
+# selectively allow or deny access from specified IP addresses. The
+# main druby distribution provides the ACL class for this purpose. In
+# general, this mechanism should only be used alongside, rather than
+# as a replacement for, a good firewall.
+#
+# == dRuby internals
+#
+# dRuby is implemented using three main components: a remote method
+# call marshaller/unmarshaller; a transport protocol; and an
+# ID-to-object mapper. The latter two can be directly, and the first
+# indirectly, replaced, in order to provide different behaviour and
+# capabilities.
+#
+# Marshalling and unmarshalling of remote method calls is performed by
+# a DRb::DRbMessage instance. This uses the Marshal module to dump
+# the method call before sending it over the transport layer, then
+# reconstitute it at the other end. There is normally no need to
+# replace this component, and no direct way is provided to do so.
+# However, it is possible to implement an alternative marshalling
+# scheme as part of an implementation of the transport layer.
+#
+# The transport layer is responsible for opening client and server
+# network connections and forwarding dRuby request across them.
+# Normally, it uses DRb::DRbMessage internally to manage marshalling
+# and unmarshalling. The transport layer is managed by
+# DRb::DRbProtocol. Multiple protocols can be installed in
+# DRbProtocol at the one time; selection between them is determined by
+# the scheme of a dRuby URI. The default transport protocol is
+# selected by the scheme 'druby:', and implemented by
+# DRb::DRbTCPSocket. This uses plain TCP/IP sockets for
+# communication. An alternative protocol, using UNIX domain sockets,
+# is implemented by DRb::DRbUNIXSocket in the file drb/unix.rb, and
+# selected by the scheme 'drbunix:'. A sample implementation over
+# HTTP can be found in the samples accompanying the main dRuby
+# distribution.
+#
+# The ID-to-object mapping component maps dRuby object ids to the
+# objects they refer to, and vice versa. The implementation to use
+# can be specified as part of a DRb::DRbServer's configuration. The
+# default implementation is provided by DRb::DRbIdConv. It uses an
+# object's ObjectSpace id as its dRuby id. This means that the dRuby
+# reference to that object only remains meaningful for the lifetime of
+# the object's process and the lifetime of the object within that
+# process. A modified implementation is provided by DRb::TimerIdConv
+# in the file drb/timeridconv.rb. This implementation retains a local
+# reference to all objects exported over dRuby for a configurable
+# period of time (defaulting to ten minutes), to prevent them being
+# garbage-collected within this time. Another sample implementation
+# is provided in sample/name.rb in the main dRuby distribution. This
+# allows objects to specify their own id or "name". A dRuby reference
+# can be made persistent across processes by having each process
+# register an object using the same dRuby name.
+#
+module DRb
+
+ # Superclass of all errors raised in the DRb module.
+ class DRbError < RuntimeError; end
+
+ # Error raised when an error occurs on the underlying communication
+ # protocol.
+ class DRbConnError < DRbError; end
+
+ # Class responsible for converting between an object and its id.
+ #
+ # This, the default implementation, uses an object's local ObjectSpace
+ # __id__ as its id. This means that an object's identification over
+ # drb remains valid only while that object instance remains alive
+ # within the server runtime.
+ #
+ # For alternative mechanisms, see DRb::TimerIdConv in rdb/timeridconv.rb
+ # and DRbNameIdConv in sample/name.rb in the full drb distribution.
+ class DRbIdConv
+
+ # Convert an object reference id to an object.
+ #
+ # This implementation looks up the reference id in the local object
+ # space and returns the object it refers to.
+ def to_obj(ref)
+ ObjectSpace._id2ref(ref)
+ end
+
+ # Convert an object into a reference id.
+ #
+ # This implementation returns the object's __id__ in the local
+ # object space.
+ def to_id(obj)
+ obj.nil? ? nil : obj.__id__
+ end
+ end
+
+ # Mixin module making an object undumpable or unmarshallable.
+ #
+ # If an object which includes this module is returned by method
+ # called over drb, then the object remains in the server space
+ # and a reference to the object is returned, rather than the
+ # object being marshalled and moved into the client space.
+ module DRbUndumped
+ def _dump(dummy) # :nodoc:
+ raise TypeError, 'can\'t dump'
+ end
+ end
+
+ # Error raised by the DRb module when an attempt is made to refer to
+ # the context's current drb server but the context does not have one.
+ # See #current_server.
+ class DRbServerNotFound < DRbError; end
+
+ # Error raised by the DRbProtocol module when it cannot find any
+ # protocol implementation support the scheme specified in a URI.
+ class DRbBadURI < DRbError; end
+
+ # Error raised by a dRuby protocol when it doesn't support the
+ # scheme specified in a URI. See DRb::DRbProtocol.
+ class DRbBadScheme < DRbError; end
+
+ # An exception wrapping a DRb::DRbUnknown object
+ class DRbUnknownError < DRbError
+
+ # Create a new DRbUnknownError for the DRb::DRbUnknown object +unknown+
+ def initialize(unknown)
+ @unknown = unknown
+ super(unknown.name)
+ end
+
+ # Get the wrapped DRb::DRbUnknown object.
+ attr_reader :unknown
+
+ def self._load(s) # :nodoc:
+ Marshal::load(s)
+ end
+
+ def _dump(lv) # :nodoc:
+ Marshal::dump(@unknown)
+ end
+ end
+
+ # An exception wrapping an error object
+ class DRbRemoteError < DRbError
+ def initialize(error)
+ @reason = error.class.to_s
+ super("#{error.message} (#{error.class})")
+ set_backtrace(error.backtrace)
+ end
+
+ # the class of the error, as a string.
+ attr_reader :reason
+ end
+
+ # Class wrapping a marshalled object whose type is unknown locally.
+ #
+ # If an object is returned by a method invoked over drb, but the
+ # class of the object is unknown in the client namespace, or
+ # the object is a constant unknown in the client namespace, then
+ # the still-marshalled object is returned wrapped in a DRbUnknown instance.
+ #
+ # If this object is passed as an argument to a method invoked over
+ # drb, then the wrapped object is passed instead.
+ #
+ # The class or constant name of the object can be read from the
+ # +name+ attribute. The marshalled object is held in the +buf+
+ # attribute.
+ class DRbUnknown
+
+ # Create a new DRbUnknown object.
+ #
+ # +buf+ is a string containing a marshalled object that could not
+ # be unmarshalled. +err+ is the error message that was raised
+ # when the unmarshalling failed. It is used to determine the
+ # name of the unmarshalled object.
+ def initialize(err, buf)
+ case err.to_s
+ when /uninitialized constant (\S+)/
+ @name = $1
+ when /undefined class\/module (\S+)/
+ @name = $1
+ else
+ @name = nil
+ end
+ @buf = buf
+ end
+
+ # The name of the unknown thing.
+ #
+ # Class name for unknown objects; variable name for unknown
+ # constants.
+ attr_reader :name
+
+ # Buffer contained the marshalled, unknown object.
+ attr_reader :buf
+
+ def self._load(s) # :nodoc:
+ begin
+ Marshal::load(s)
+ rescue NameError, ArgumentError
+ DRbUnknown.new($!, s)
+ end
+ end
+
+ def _dump(lv) # :nodoc:
+ @buf
+ end
+
+ # Attempt to load the wrapped marshalled object again.
+ #
+ # If the class of the object is now known locally, the object
+ # will be unmarshalled and returned. Otherwise, a new
+ # but identical DRbUnknown object will be returned.
+ def reload
+ self.class._load(@buf)
+ end
+
+ # Create a DRbUnknownError exception containing this object.
+ def exception
+ DRbUnknownError.new(self)
+ end
+ end
+
+ class DRbArray
+ def initialize(ary)
+ @ary = ary.collect { |obj|
+ if obj.kind_of? DRbUndumped
+ DRbObject.new(obj)
+ else
+ begin
+ Marshal.dump(obj)
+ obj
+ rescue
+ DRbObject.new(obj)
+ end
+ end
+ }
+ end
+
+ def self._load(s)
+ Marshal::load(s)
+ end
+
+ def _dump(lv)
+ Marshal.dump(@ary)
+ end
+ end
+
+ # Handler for sending and receiving drb messages.
+ #
+ # This takes care of the low-level marshalling and unmarshalling
+ # of drb requests and responses sent over the wire between server
+ # and client. This relieves the implementor of a new drb
+ # protocol layer with having to deal with these details.
+ #
+ # The user does not have to directly deal with this object in
+ # normal use.
+ class DRbMessage
+ def initialize(config) # :nodoc:
+ @load_limit = config[:load_limit]
+ @argc_limit = config[:argc_limit]
+ end
+
+ def dump(obj, error=false) # :nodoc:
+ obj = make_proxy(obj, error) if obj.kind_of? DRbUndumped
+ begin
+ str = Marshal::dump(obj)
+ rescue
+ str = Marshal::dump(make_proxy(obj, error))
+ end
+ [str.size].pack('N') + str
+ end
+
+ def load(soc) # :nodoc:
+ begin
+ sz = soc.read(4) # sizeof (N)
+ rescue
+ raise(DRbConnError, $!.message, $!.backtrace)
+ end
+ raise(DRbConnError, 'connection closed') if sz.nil?
+ raise(DRbConnError, 'premature header') if sz.size < 4
+ sz = sz.unpack('N')[0]
+ raise(DRbConnError, "too large packet #{sz}") if @load_limit < sz
+ begin
+ str = soc.read(sz)
+ rescue
+ raise(DRbConnError, $!.message, $!.backtrace)
+ end
+ raise(DRbConnError, 'connection closed') if str.nil?
+ raise(DRbConnError, 'premature marshal format(can\'t read)') if str.size < sz
+ Thread.exclusive do
+ begin
+ save = Thread.current[:drb_untaint]
+ Thread.current[:drb_untaint] = []
+ Marshal::load(str)
+ rescue NameError, ArgumentError
+ DRbUnknown.new($!, str)
+ ensure
+ Thread.current[:drb_untaint].each do |x|
+ x.untaint
+ end
+ Thread.current[:drb_untaint] = save
+ end
+ end
+ end
+
+ def send_request(stream, ref, msg_id, arg, b) # :nodoc:
+ ary = []
+ ary.push(dump(ref.__drbref))
+ ary.push(dump(msg_id.id2name))
+ ary.push(dump(arg.length))
+ arg.each do |e|
+ ary.push(dump(e))
+ end
+ ary.push(dump(b))
+ stream.write(ary.join(''))
+ rescue
+ raise(DRbConnError, $!.message, $!.backtrace)
+ end
+
+ def recv_request(stream) # :nodoc:
+ ref = load(stream)
+ ro = DRb.to_obj(ref)
+ msg = load(stream)
+ argc = load(stream)
+ raise ArgumentError, 'too many arguments' if @argc_limit < argc
+ argv = Array.new(argc, nil)
+ argc.times do |n|
+ argv[n] = load(stream)
+ end
+ block = load(stream)
+ return ro, msg, argv, block
+ end
+
+ def send_reply(stream, succ, result) # :nodoc:
+ stream.write(dump(succ) + dump(result, !succ))
+ rescue
+ raise(DRbConnError, $!.message, $!.backtrace)
+ end
+
+ def recv_reply(stream) # :nodoc:
+ succ = load(stream)
+ result = load(stream)
+ [succ, result]
+ end
+
+ private
+ def make_proxy(obj, error=false)
+ if error
+ DRbRemoteError.new(obj)
+ else
+ DRbObject.new(obj)
+ end
+ end
+ end
+
+ # Module managing the underlying network protocol(s) used by drb.
+ #
+ # By default, drb uses the DRbTCPSocket protocol. Other protocols
+ # can be defined. A protocol must define the following class methods:
+ #
+ # [open(uri, config)] Open a client connection to the server at +uri+,
+ # using configuration +config+. Return a protocol
+ # instance for this connection.
+ # [open_server(uri, config)] Open a server listening at +uri+,
+ # using configuration +config+. Return a
+ # protocol instance for this listener.
+ # [uri_option(uri, config)] Take a URI, possibly containing an option
+ # component (e.g. a trailing '?param=val'),
+ # and return a [uri, option] tuple.
+ #
+ # All of these methods should raise a DRbBadScheme error if the URI
+ # does not identify the protocol they support (e.g. "druby:" for
+ # the standard Ruby protocol). This is how the DRbProtocol module,
+ # given a URI, determines which protocol implementation serves that
+ # protocol.
+ #
+ # The protocol instance returned by #open_server must have the
+ # following methods:
+ #
+ # [accept] Accept a new connection to the server. Returns a protocol
+ # instance capable of communicating with the client.
+ # [close] Close the server connection.
+ # [uri] Get the URI for this server.
+ #
+ # The protocol instance returned by #open must have the following methods:
+ #
+ # [send_request (ref, msg_id, arg, b)]
+ # Send a request to +ref+ with the given message id and arguments.
+ # This is most easily implemented by calling DRbMessage.send_request,
+ # providing a stream that sits on top of the current protocol.
+ # [recv_reply]
+ # Receive a reply from the server and return it as a [success-boolean,
+ # reply-value] pair. This is most easily implemented by calling
+ # DRb.recv_reply, providing a stream that sits on top of the
+ # current protocol.
+ # [alive?]
+ # Is this connection still alive?
+ # [close]
+ # Close this connection.
+ #
+ # The protocol instance returned by #open_server().accept() must have
+ # the following methods:
+ #
+ # [recv_request]
+ # Receive a request from the client and return a [object, message,
+ # args, block] tuple. This is most easily implemented by calling
+ # DRbMessage.recv_request, providing a stream that sits on top of
+ # the current protocol.
+ # [send_reply(succ, result)]
+ # Send a reply to the client. This is most easily implemented
+ # by calling DRbMessage.send_reply, providing a stream that sits
+ # on top of the current protocol.
+ # [close]
+ # Close this connection.
+ #
+ # A new protocol is registered with the DRbProtocol module using
+ # the add_protocol method.
+ #
+ # For examples of other protocols, see DRbUNIXSocket in drb/unix.rb,
+ # and HTTP0 in sample/http0.rb and sample/http0serv.rb in the full
+ # drb distribution.
+ module DRbProtocol
+
+ # Add a new protocol to the DRbProtocol module.
+ def add_protocol(prot)
+ @protocol.push(prot)
+ end
+ module_function :add_protocol
+
+ # Open a client connection to +uri+ with the configuration +config+.
+ #
+ # The DRbProtocol module asks each registered protocol in turn to
+ # try to open the URI. Each protocol signals that it does not handle that
+ # URI by raising a DRbBadScheme error. If no protocol recognises the
+ # URI, then a DRbBadURI error is raised. If a protocol accepts the
+ # URI, but an error occurs in opening it, a DRbConnError is raised.
+ def open(uri, config, first=true)
+ @protocol.each do |prot|
+ begin
+ return prot.open(uri, config)
+ rescue DRbBadScheme
+ rescue DRbConnError
+ raise($!)
+ rescue
+ raise(DRbConnError, "#{uri} - #{$!.inspect}")
+ end
+ end
+ if first && (config[:auto_load] != false)
+ auto_load(uri, config)
+ return open(uri, config, false)
+ end
+ raise DRbBadURI, 'can\'t parse uri:' + uri
+ end
+ module_function :open
+
+ # Open a server listening for connections at +uri+ with
+ # configuration +config+.
+ #
+ # The DRbProtocol module asks each registered protocol in turn to
+ # try to open a server at the URI. Each protocol signals that it does
+ # not handle that URI by raising a DRbBadScheme error. If no protocol
+ # recognises the URI, then a DRbBadURI error is raised. If a protocol
+ # accepts the URI, but an error occurs in opening it, the underlying
+ # error is passed on to the caller.
+ def open_server(uri, config, first=true)
+ @protocol.each do |prot|
+ begin
+ return prot.open_server(uri, config)
+ rescue DRbBadScheme
+ end
+ end
+ if first && (config[:auto_load] != false)
+ auto_load(uri, config)
+ return open_server(uri, config, false)
+ end
+ raise DRbBadURI, 'can\'t parse uri:' + uri
+ end
+ module_function :open_server
+
+ # Parse +uri+ into a [uri, option] pair.
+ #
+ # The DRbProtocol module asks each registered protocol in turn to
+ # try to parse the URI. Each protocol signals that it does not handle that
+ # URI by raising a DRbBadScheme error. If no protocol recognises the
+ # URI, then a DRbBadURI error is raised.
+ def uri_option(uri, config, first=true)
+ @protocol.each do |prot|
+ begin
+ uri, opt = prot.uri_option(uri, config)
+ # opt = nil if opt == ''
+ return uri, opt
+ rescue DRbBadScheme
+ end
+ end
+ if first && (config[:auto_load] != false)
+ auto_load(uri, config)
+ return uri_option(uri, config, false)
+ end
+ raise DRbBadURI, 'can\'t parse uri:' + uri
+ end
+ module_function :uri_option
+
+ def auto_load(uri, config) # :nodoc:
+ if uri =~ /^drb([a-z0-9]+):/
+ require("drb/#{$1}") rescue nil
+ end
+ end
+ module_function :auto_load
+ end
+
+ # The default drb protocol.
+ #
+ # Communicates over a TCP socket.
+ class DRbTCPSocket
+ private
+ def self.parse_uri(uri)
+ if uri =~ /^druby:\/\/(.*?):(\d+)(\?(.*))?$/
+ host = $1
+ port = $2.to_i
+ option = $4
+ [host, port, option]
+ else
+ raise(DRbBadScheme, uri) unless uri =~ /^druby:/
+ raise(DRbBadURI, 'can\'t parse uri:' + uri)
+ end
+ end
+
+ public
+
+ # Open a client connection to +uri+ using configuration +config+.
+ def self.open(uri, config)
+ host, port, option = parse_uri(uri)
+ host.untaint
+ port.untaint
+ soc = TCPSocket.open(host, port)
+ self.new(uri, soc, config)
+ end
+
+ def self.getservername
+ host = Socket::gethostname
+ begin
+ Socket::gethostbyname(host)[0]
+ rescue
+ 'localhost'
+ end
+ end
+
+ def self.open_server_inaddr_any(host, port)
+ infos = Socket::getaddrinfo(host, nil,
+ Socket::AF_UNSPEC,
+ Socket::SOCK_STREAM,
+ 0,
+ Socket::AI_PASSIVE)
+ family = infos.collect { |af, *_| af }.uniq
+ case family
+ when ['AF_INET']
+ return TCPServer.open('0.0.0.0', port)
+ when ['AF_INET6']
+ return TCPServer.open('::', port)
+ else
+ return TCPServer.open(port)
+ end
+ end
+
+ # Open a server listening for connections at +uri+ using
+ # configuration +config+.
+ def self.open_server(uri, config)
+ uri = 'druby://:0' unless uri
+ host, port, opt = parse_uri(uri)
+ if host.size == 0
+ host = getservername
+ soc = open_server_inaddr_any(host, port)
+ else
+ soc = TCPServer.open(host, port)
+ end
+ port = soc.addr[1] if port == 0
+ uri = "druby://#{host}:#{port}"
+ self.new(uri, soc, config)
+ end
+
+ # Parse +uri+ into a [uri, option] pair.
+ def self.uri_option(uri, config)
+ host, port, option = parse_uri(uri)
+ return "druby://#{host}:#{port}", option
+ end
+
+ # Create a new DRbTCPSocket instance.
+ #
+ # +uri+ is the URI we are connected to.
+ # +soc+ is the tcp socket we are bound to. +config+ is our
+ # configuration.
+ def initialize(uri, soc, config={})
+ @uri = uri
+ @socket = soc
+ @config = config
+ @acl = config[:tcp_acl]
+ @msg = DRbMessage.new(config)
+ set_sockopt(@socket)
+ end
+
+ # Get the URI that we are connected to.
+ attr_reader :uri
+
+ # Get the address of our TCP peer (the other end of the socket
+ # we are bound to.
+ def peeraddr
+ @socket.peeraddr
+ end
+
+ # Get the socket.
+ def stream; @socket; end
+
+ # On the client side, send a request to the server.
+ def send_request(ref, msg_id, arg, b)
+ @msg.send_request(stream, ref, msg_id, arg, b)
+ end
+
+ # On the server side, receive a request from the client.
+ def recv_request
+ @msg.recv_request(stream)
+ end
+
+ # On the server side, send a reply to the client.
+ def send_reply(succ, result)
+ @msg.send_reply(stream, succ, result)
+ end
+
+ # On the client side, receive a reply from the server.
+ def recv_reply
+ @msg.recv_reply(stream)
+ end
+
+ public
+
+ # Close the connection.
+ #
+ # If this is an instance returned by #open_server, then this stops
+ # listening for new connections altogether. If this is an instance
+ # returned by #open or by #accept, then it closes this particular
+ # client-server session.
+ def close
+ if @socket
+ @socket.close
+ @socket = nil
+ end
+ end
+
+ # On the server side, for an instance returned by #open_server,
+ # accept a client connection and return a new instance to handle
+ # the server's side of this client-server session.
+ def accept
+ while true
+ s = @socket.accept
+ break if (@acl ? @acl.allow_socket?(s) : true)
+ s.close
+ end
+ self.class.new(nil, s, @config)
+ end
+
+ # Check to see if this connection is alive.
+ def alive?
+ return false unless @socket
+ if IO.select([@socket], nil, nil, 0)
+ close
+ return false
+ end
+ true
+ end
+
+ def set_sockopt(soc) # :nodoc:
+ soc.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
+ soc.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::FD_CLOEXEC
+ end
+ end
+
+ module DRbProtocol
+ @protocol = [DRbTCPSocket] # default
+ end
+
+ class DRbURIOption # :nodoc: I don't understand the purpose of this class...
+ def initialize(option)
+ @option = option.to_s
+ end
+ attr :option
+ def to_s; @option; end
+
+ def ==(other)
+ return false unless DRbURIOption === other
+ @option == other.option
+ end
+
+ def hash
+ @option.hash
+ end
+
+ alias eql? ==
+ end
+
+ # Object wrapping a reference to a remote drb object.
+ #
+ # Method calls on this object are relayed to the remote
+ # object that this object is a stub for.
+ class DRbObject
+
+ # Unmarshall a marshalled DRbObject.
+ #
+ # If the referenced object is located within the local server, then
+ # the object itself is returned. Otherwise, a new DRbObject is
+ # created to act as a stub for the remote referenced object.
+ def self._load(s)
+ uri, ref = Marshal.load(s)
+
+ if DRb.here?(uri)
+ obj = DRb.to_obj(ref)
+ if ((! obj.tainted?) && Thread.current[:drb_untaint])
+ Thread.current[:drb_untaint].push(obj)
+ end
+ return obj
+ end
+
+ self.new_with(uri, ref)
+ end
+
+ def self.new_with(uri, ref)
+ it = self.allocate
+ it.instance_variable_set('@uri', uri)
+ it.instance_variable_set('@ref', ref)
+ it
+ end
+
+ # Create a new DRbObject from a URI alone.
+ def self.new_with_uri(uri)
+ self.new(nil, uri)
+ end
+
+ # Marshall this object.
+ #
+ # The URI and ref of the object are marshalled.
+ def _dump(lv)
+ Marshal.dump([@uri, @ref])
+ end
+
+ # Create a new remote object stub.
+ #
+ # +obj+ is the (local) object we want to create a stub for. Normally
+ # this is +nil+. +uri+ is the URI of the remote object that this
+ # will be a stub for.
+ def initialize(obj, uri=nil)
+ @uri = nil
+ @ref = nil
+ if obj.nil?
+ return if uri.nil?
+ @uri, option = DRbProtocol.uri_option(uri, DRb.config)
+ @ref = DRbURIOption.new(option) unless option.nil?
+ else
+ @uri = uri ? uri : (DRb.uri rescue nil)
+ @ref = obj ? DRb.to_id(obj) : nil
+ end
+ end
+
+ # Get the URI of the remote object.
+ def __drburi
+ @uri
+ end
+
+ # Get the reference of the object, if local.
+ def __drbref
+ @ref
+ end
+
+ undef :to_s
+ undef :to_a if respond_to?(:to_a)
+
+ def respond_to?(msg_id, priv=false)
+ case msg_id
+ when :_dump
+ true
+ when :marshal_dump
+ false
+ else
+ method_missing(:respond_to?, msg_id, priv)
+ end
+ end
+
+ # Routes method calls to the referenced object.
+ def method_missing(msg_id, *a, &b)
+ if DRb.here?(@uri)
+ obj = DRb.to_obj(@ref)
+ DRb.current_server.check_insecure_method(obj, msg_id)
+ return obj.__send__(msg_id, *a, &b)
+ end
+
+ succ, result = self.class.with_friend(@uri) do
+ DRbConn.open(@uri) do |conn|
+ conn.send_message(self, msg_id, a, b)
+ end
+ end
+
+ if succ
+ return result
+ elsif DRbUnknown === result
+ raise result
+ else
+ bt = self.class.prepare_backtrace(@uri, result)
+ result.set_backtrace(bt + caller)
+ raise result
+ end
+ end
+
+ def self.with_friend(uri)
+ friend = DRb.fetch_server(uri)
+ return yield() unless friend
+
+ save = Thread.current['DRb']
+ Thread.current['DRb'] = { 'server' => friend }
+ return yield
+ ensure
+ Thread.current['DRb'] = save if friend
+ end
+
+ def self.prepare_backtrace(uri, result)
+ prefix = "(#{uri}) "
+ bt = []
+ result.backtrace.each do |x|
+ break if /`__send__'$/ =~ x
+ if /^\(druby:\/\// =~ x
+ bt.push(x)
+ else
+ bt.push(prefix + x)
+ end
+ end
+ bt
+ end
+
+ def pretty_print(q) # :nodoc:
+ q.pp_object(self)
+ end
+
+ def pretty_print_cycle(q) # :nodoc:
+ q.object_address_group(self) {
+ q.breakable
+ q.text '...'
+ }
+ end
+ end
+
+ # Class handling the connection between a DRbObject and the
+ # server the real object lives on.
+ #
+ # This class maintains a pool of connections, to reduce the
+ # overhead of starting and closing down connections for each
+ # method call.
+ #
+ # This class is used internally by DRbObject. The user does
+ # not normally need to deal with it directly.
+ class DRbConn
+ POOL_SIZE = 16 # :nodoc:
+ @mutex = Mutex.new
+ @pool = []
+
+ def self.open(remote_uri) # :nodoc:
+ begin
+ conn = nil
+
+ @mutex.synchronize do
+ #FIXME
+ new_pool = []
+ @pool.each do |c|
+ if conn.nil? and c.uri == remote_uri
+ conn = c if c.alive?
+ else
+ new_pool.push c
+ end
+ end
+ @pool = new_pool
+ end
+
+ conn = self.new(remote_uri) unless conn
+ succ, result = yield(conn)
+ return succ, result
+
+ ensure
+ if conn
+ if succ
+ @mutex.synchronize do
+ @pool.unshift(conn)
+ @pool.pop.close while @pool.size > POOL_SIZE
+ end
+ else
+ conn.close
+ end
+ end
+ end
+ end
+
+ def initialize(remote_uri) # :nodoc:
+ @uri = remote_uri
+ @protocol = DRbProtocol.open(remote_uri, DRb.config)
+ end
+ attr_reader :uri # :nodoc:
+
+ def send_message(ref, msg_id, arg, block) # :nodoc:
+ @protocol.send_request(ref, msg_id, arg, block)
+ @protocol.recv_reply
+ end
+
+ def close # :nodoc:
+ @protocol.close
+ @protocol = nil
+ end
+
+ def alive? # :nodoc:
+ @protocol.alive?
+ end
+ end
+
+ # Class representing a drb server instance.
+ #
+ # A DRbServer must be running in the local process before any incoming
+ # dRuby calls can be accepted, or any local objects can be passed as
+ # dRuby references to remote processes, even if those local objects are
+ # never actually called remotely. You do not need to start a DRbServer
+ # in the local process if you are only making outgoing dRuby calls
+ # passing marshalled parameters.
+ #
+ # Unless multiple servers are being used, the local DRbServer is normally
+ # started by calling DRb.start_service.
+ class DRbServer
+ @@acl = nil
+ @@idconv = DRbIdConv.new
+ @@secondary_server = nil
+ @@argc_limit = 256
+ @@load_limit = 256 * 102400
+ @@verbose = false
+ @@safe_level = 0
+
+ # Set the default value for the :argc_limit option.
+ #
+ # See #new(). The initial default value is 256.
+ def self.default_argc_limit(argc)
+ @@argc_limit = argc
+ end
+
+ # Set the default value for the :load_limit option.
+ #
+ # See #new(). The initial default value is 25 MB.
+ def self.default_load_limit(sz)
+ @@load_limit = sz
+ end
+
+ # Set the default value for the :acl option.
+ #
+ # See #new(). The initial default value is nil.
+ def self.default_acl(acl)
+ @@acl = acl
+ end
+
+ # Set the default value for the :id_conv option.
+ #
+ # See #new(). The initial default value is a DRbIdConv instance.
+ def self.default_id_conv(idconv)
+ @@idconv = idconv
+ end
+
+ def self.default_safe_level(level)
+ @@safe_level = level
+ end
+
+ # Set the default value of the :verbose option.
+ #
+ # See #new(). The initial default value is false.
+ def self.verbose=(on)
+ @@verbose = on
+ end
+
+ # Get the default value of the :verbose option.
+ def self.verbose
+ @@verbose
+ end
+
+ def self.make_config(hash={}) # :nodoc:
+ default_config = {
+ :idconv => @@idconv,
+ :verbose => @@verbose,
+ :tcp_acl => @@acl,
+ :load_limit => @@load_limit,
+ :argc_limit => @@argc_limit,
+ :safe_level => @@safe_level
+ }
+ default_config.update(hash)
+ end
+
+ # Create a new DRbServer instance.
+ #
+ # +uri+ is the URI to bind to. This is normally of the form
+ # 'druby://<hostname>:<port>' where <hostname> is a hostname of
+ # the local machine. If nil, then the system's default hostname
+ # will be bound to, on a port selected by the system; these value
+ # can be retrieved from the +uri+ attribute. 'druby:' specifies
+ # the default dRuby transport protocol: another protocol, such
+ # as 'drbunix:', can be specified instead.
+ #
+ # +front+ is the front object for the server, that is, the object
+ # to which remote method calls on the server will be passed. If
+ # nil, then the server will not accept remote method calls.
+ #
+ # If +config_or_acl+ is a hash, it is the configuration to
+ # use for this server. The following options are recognised:
+ #
+ # :idconv :: an id-to-object conversion object. This defaults
+ # to an instance of the class DRb::DRbIdConv.
+ # :verbose :: if true, all unsuccessful remote calls on objects
+ # in the server will be logged to $stdout. false
+ # by default.
+ # :tcp_acl :: the access control list for this server. See
+ # the ACL class from the main dRuby distribution.
+ # :load_limit :: the maximum message size in bytes accepted by
+ # the server. Defaults to 25 MB (26214400).
+ # :argc_limit :: the maximum number of arguments to a remote
+ # method accepted by the server. Defaults to
+ # 256.
+ #
+ # The default values of these options can be modified on
+ # a class-wide basis by the class methods #default_argc_limit,
+ # #default_load_limit, #default_acl, #default_id_conv,
+ # and #verbose=
+ #
+ # If +config_or_acl+ is not a hash, but is not nil, it is
+ # assumed to be the access control list for this server.
+ # See the :tcp_acl option for more details.
+ #
+ # If no other server is currently set as the primary server,
+ # this will become the primary server.
+ #
+ # The server will immediately start running in its own thread.
+ def initialize(uri=nil, front=nil, config_or_acl=nil)
+ if Hash === config_or_acl
+ config = config_or_acl.dup
+ else
+ acl = config_or_acl || @@acl
+ config = {
+ :tcp_acl => acl
+ }
+ end
+
+ @config = self.class.make_config(config)
+
+ @protocol = DRbProtocol.open_server(uri, @config)
+ @uri = @protocol.uri
+
+ @front = front
+ @idconv = @config[:idconv]
+ @safe_level = @config[:safe_level]
+
+ @grp = ThreadGroup.new
+ @thread = run
+
+ DRb.regist_server(self)
+ end
+
+ # The URI of this DRbServer.
+ attr_reader :uri
+
+ # The main thread of this DRbServer.
+ #
+ # This is the thread that listens for and accepts connections
+ # from clients, not that handles each client's request-response
+ # session.
+ attr_reader :thread
+
+ # The front object of the DRbServer.
+ #
+ # This object receives remote method calls made on the server's
+ # URI alone, with an object id.
+ attr_reader :front
+
+ # The configuration of this DRbServer
+ attr_reader :config
+
+ attr_reader :safe_level
+
+ # Set whether to operate in verbose mode.
+ #
+ # In verbose mode, failed calls are logged to stdout.
+ def verbose=(v); @config[:verbose]=v; end
+
+ # Get whether the server is in verbose mode.
+ #
+ # In verbose mode, failed calls are logged to stdout.
+ def verbose; @config[:verbose]; end
+
+ # Is this server alive?
+ def alive?
+ @thread.alive?
+ end
+
+ # Stop this server.
+ def stop_service
+ DRb.remove_server(self)
+ if Thread.current['DRb'] && Thread.current['DRb']['server'] == self
+ Thread.current['DRb']['stop_service'] = true
+ else
+ @thread.kill
+ end
+ end
+
+ # Convert a dRuby reference to the local object it refers to.
+ def to_obj(ref)
+ return front if ref.nil?
+ return front[ref.to_s] if DRbURIOption === ref
+ @idconv.to_obj(ref)
+ end
+
+ # Convert a local object to a dRuby reference.
+ def to_id(obj)
+ return nil if obj.__id__ == front.__id__
+ @idconv.to_id(obj)
+ end
+
+ private
+ def kill_sub_thread
+ Thread.new do
+ grp = ThreadGroup.new
+ grp.add(Thread.current)
+ list = @grp.list
+ while list.size > 0
+ list.each do |th|
+ th.kill if th.alive?
+ end
+ list = @grp.list
+ end
+ end
+ end
+
+ def run
+ Thread.start do
+ begin
+ while true
+ main_loop
+ end
+ ensure
+ @protocol.close if @protocol
+ kill_sub_thread
+ end
+ end
+ end
+
+ # List of insecure methods.
+ #
+ # These methods are not callable via dRuby.
+ INSECURE_METHOD = [
+ :__send__
+ ]
+
+ # Has a method been included in the list of insecure methods?
+ def insecure_method?(msg_id)
+ INSECURE_METHOD.include?(msg_id)
+ end
+
+ # Coerce an object to a string, providing our own representation if
+ # to_s is not defined for the object.
+ def any_to_s(obj)
+ obj.to_s + ":#{obj.class}"
+ rescue
+ sprintf("#<%s:0x%lx>", obj.class, obj.__id__)
+ end
+
+ # Check that a method is callable via dRuby.
+ #
+ # +obj+ is the object we want to invoke the method on. +msg_id+ is the
+ # method name, as a Symbol.
+ #
+ # If the method is an insecure method (see #insecure_method?) a
+ # SecurityError is thrown. If the method is private or undefined,
+ # a NameError is thrown.
+ def check_insecure_method(obj, msg_id)
+ return true if Proc === obj && msg_id == :__drb_yield
+ raise(ArgumentError, "#{any_to_s(msg_id)} is not a symbol") unless Symbol == msg_id.class
+ raise(SecurityError, "insecure method `#{msg_id}'") if insecure_method?(msg_id)
+
+ if obj.private_methods.include?(msg_id.to_s)
+ desc = any_to_s(obj)
+ raise NoMethodError, "private method `#{msg_id}' called for #{desc}"
+ elsif obj.protected_methods.include?(msg_id.to_s)
+ desc = any_to_s(obj)
+ raise NoMethodError, "protected method `#{msg_id}' called for #{desc}"
+ else
+ true
+ end
+ end
+ public :check_insecure_method
+
+ class InvokeMethod # :nodoc:
+ def initialize(drb_server, client)
+ @drb_server = drb_server
+ @safe_level = drb_server.safe_level
+ @client = client
+ end
+
+ def perform
+ @result = nil
+ @succ = false
+ setup_message
+
+ if $SAFE < @safe_level
+ info = Thread.current['DRb']
+ if @block
+ @result = Thread.new {
+ Thread.current['DRb'] = info
+ $SAFE = @safe_level
+ perform_with_block
+ }.value
+ else
+ @result = Thread.new {
+ Thread.current['DRb'] = info
+ $SAFE = @safe_level
+ perform_without_block
+ }.value
+ end
+ else
+ if @block
+ @result = perform_with_block
+ else
+ @result = perform_without_block
+ end
+ end
+ @succ = true
+ if @msg_id == :to_ary && @result.class == Array
+ @result = DRbArray.new(@result)
+ end
+ return @succ, @result
+ rescue StandardError, ScriptError, Interrupt
+ @result = $!
+ return @succ, @result
+ end
+
+ private
+ def init_with_client
+ obj, msg, argv, block = @client.recv_request
+ @obj = obj
+ @msg_id = msg.intern
+ @argv = argv
+ @block = block
+ end
+
+ def check_insecure_method
+ @drb_server.check_insecure_method(@obj, @msg_id)
+ end
+
+ def setup_message
+ init_with_client
+ check_insecure_method
+ end
+
+ def perform_without_block
+ if Proc === @obj && @msg_id == :__drb_yield
+ if @argv.size == 1
+ ary = @argv
+ else
+ ary = [@argv]
+ end
+ ary.collect(&@obj)[0]
+ else
+ @obj.__send__(@msg_id, *@argv)
+ end
+ end
+
+ end
+
+ if RUBY_VERSION >= '1.8'
+ require 'drb/invokemethod'
+ class InvokeMethod
+ include InvokeMethod18Mixin
+ end
+ else
+ require 'drb/invokemethod16'
+ class InvokeMethod
+ include InvokeMethod16Mixin
+ end
+ end
+
+ # The main loop performed by a DRbServer's internal thread.
+ #
+ # Accepts a connection from a client, and starts up its own
+ # thread to handle it. This thread loops, receiving requests
+ # from the client, invoking them on a local object, and
+ # returning responses, until the client closes the connection
+ # or a local method call fails.
+ def main_loop
+ Thread.start(@protocol.accept) do |client|
+ @grp.add Thread.current
+ Thread.current['DRb'] = { 'client' => client ,
+ 'server' => self }
+ loop do
+ begin
+ succ = false
+ invoke_method = InvokeMethod.new(self, client)
+ succ, result = invoke_method.perform
+ if !succ && verbose
+ p result
+ result.backtrace.each do |x|
+ puts x
+ end
+ end
+ client.send_reply(succ, result) rescue nil
+ ensure
+ client.close unless succ
+ if Thread.current['DRb']['stop_service']
+ Thread.new { stop_service }
+ end
+ break unless succ
+ end
+ end
+ end
+ end
+ end
+
+ @primary_server = nil
+
+ # Start a dRuby server locally.
+ #
+ # The new dRuby server will become the primary server, even
+ # if another server is currently the primary server.
+ #
+ # +uri+ is the URI for the server to bind to. If nil,
+ # the server will bind to random port on the default local host
+ # name and use the default dRuby protocol.
+ #
+ # +front+ is the server's front object. This may be nil.
+ #
+ # +config+ is the configuration for the new server. This may
+ # be nil.
+ #
+ # See DRbServer::new.
+ def start_service(uri=nil, front=nil, config=nil)
+ @primary_server = DRbServer.new(uri, front, config)
+ end
+ module_function :start_service
+
+ # The primary local dRuby server.
+ #
+ # This is the server created by the #start_service call.
+ attr_accessor :primary_server
+ module_function :primary_server=, :primary_server
+
+ # Get the 'current' server.
+ #
+ # In the context of execution taking place within the main
+ # thread of a dRuby server (typically, as a result of a remote
+ # call on the server or one of its objects), the current
+ # server is that server. Otherwise, the current server is
+ # the primary server.
+ #
+ # If the above rule fails to find a server, a DRbServerNotFound
+ # error is raised.
+ def current_server
+ drb = Thread.current['DRb']
+ server = (drb && drb['server']) ? drb['server'] : @primary_server
+ raise DRbServerNotFound unless server
+ return server
+ end
+ module_function :current_server
+
+ # Stop the local dRuby server.
+ #
+ # This operates on the primary server. If there is no primary
+ # server currently running, it is a noop.
+ def stop_service
+ @primary_server.stop_service if @primary_server
+ @primary_server = nil
+ end
+ module_function :stop_service
+
+ # Get the URI defining the local dRuby space.
+ #
+ # This is the URI of the current server. See #current_server.
+ def uri
+ current_server.uri
+ end
+ module_function :uri
+
+ # Is +uri+ the URI for the current local server?
+ def here?(uri)
+ (current_server.uri rescue nil) == uri
+ end
+ module_function :here?
+
+ # Get the configuration of the current server.
+ #
+ # If there is no current server, this returns the default configuration.
+ # See #current_server and DRbServer::make_config.
+ def config
+ current_server.config
+ rescue
+ DRbServer.make_config
+ end
+ module_function :config
+
+ # Get the front object of the current server.
+ #
+ # This raises a DRbServerNotFound error if there is no current server.
+ # See #current_server.
+ def front
+ current_server.front
+ end
+ module_function :front
+
+ # Convert a reference into an object using the current server.
+ #
+ # This raises a DRbServerNotFound error if there is no current server.
+ # See #current_server.
+ def to_obj(ref)
+ current_server.to_obj(ref)
+ end
+
+ # Get a reference id for an object using the current server.
+ #
+ # This raises a DRbServerNotFound error if there is no current server.
+ # See #current_server.
+ def to_id(obj)
+ current_server.to_id(obj)
+ end
+ module_function :to_id
+ module_function :to_obj
+
+ # Get the thread of the primary server.
+ #
+ # This returns nil if there is no primary server. See #primary_server.
+ def thread
+ @primary_server ? @primary_server.thread : nil
+ end
+ module_function :thread
+
+ # Set the default id conv object.
+ #
+ # See DRbServer#default_id_conv.
+ def install_id_conv(idconv)
+ DRbServer.default_id_conv(idconv)
+ end
+ module_function :install_id_conv
+
+ # Set the default acl.
+ #
+ # See DRb::DRbServer.default_acl.
+ def install_acl(acl)
+ DRbServer.default_acl(acl)
+ end
+ module_function :install_acl
+
+ @server = {}
+ def regist_server(server)
+ @server[server.uri] = server
+ Thread.exclusive do
+ @primary_server = server unless @primary_server
+ end
+ end
+ module_function :regist_server
+
+ def remove_server(server)
+ @server.delete(server.uri)
+ end
+ module_function :remove_server
+
+ def fetch_server(uri)
+ @server[uri]
+ end
+ module_function :fetch_server
+end
+
+DRbObject = DRb::DRbObject
+DRbUndumped = DRb::DRbUndumped
+DRbIdConv = DRb::DRbIdConv
diff --git a/ruby_1_8_5/lib/drb/eq.rb b/ruby_1_8_5/lib/drb/eq.rb
new file mode 100644
index 0000000000..e24512d6a7
--- /dev/null
+++ b/ruby_1_8_5/lib/drb/eq.rb
@@ -0,0 +1,16 @@
+require 'drb/drb'
+
+module DRb
+ class DRbObject
+ def ==(other)
+ return false unless DRbObject === other
+ (@ref == other.__drbref) && (@uri == other.__drburi)
+ end
+
+ def hash
+ [@uri, @ref].hash
+ end
+
+ alias eql? ==
+ end
+end
diff --git a/ruby_1_8_5/lib/drb/extserv.rb b/ruby_1_8_5/lib/drb/extserv.rb
new file mode 100644
index 0000000000..7da8130c2b
--- /dev/null
+++ b/ruby_1_8_5/lib/drb/extserv.rb
@@ -0,0 +1,64 @@
+=begin
+ external service
+ Copyright (c) 2000,2002 Masatoshi SEKI
+=end
+
+require 'drb/drb'
+
+module DRb
+ class ExtServ
+ include DRbUndumped
+
+ def initialize(there, name, server=nil)
+ @server = server || DRb::primary_server
+ @name = name
+ ro = DRbObject.new(nil, there)
+ @invoker = ro.regist(name, DRbObject.new(self, @server.uri))
+ end
+ attr_reader :server
+
+ def front
+ DRbObject.new(nil, @server.uri)
+ end
+
+ def stop_service
+ @invoker.unregist(@name)
+ server = @server
+ @server = nil
+ server.stop_service
+ true
+ end
+
+ def alive?
+ @server ? @server.alive? : false
+ end
+ end
+end
+
+if __FILE__ == $0
+ class Foo
+ include DRbUndumped
+
+ def initialize(str)
+ @str = str
+ end
+
+ def hello(it)
+ "#{it}: #{self}"
+ end
+
+ def to_s
+ @str
+ end
+ end
+
+ cmd = ARGV.shift
+ case cmd
+ when 'itest1', 'itest2'
+ front = Foo.new(cmd)
+ manager = DRb::DRbServer.new(nil, front)
+ es = DRb::ExtServ.new(ARGV.shift, ARGV.shift, manager)
+ es.server.thread.join
+ end
+end
+
diff --git a/ruby_1_8_5/lib/drb/extservm.rb b/ruby_1_8_5/lib/drb/extservm.rb
new file mode 100644
index 0000000000..7066f84c65
--- /dev/null
+++ b/ruby_1_8_5/lib/drb/extservm.rb
@@ -0,0 +1,96 @@
+=begin
+ external service manager
+ Copyright (c) 2000 Masatoshi SEKI
+=end
+
+require 'drb/drb'
+require 'thread'
+
+module DRb
+ class ExtServManager
+ include DRbUndumped
+
+ @@command = {}
+
+ def self.command
+ @@command
+ end
+
+ def self.command=(cmd)
+ @@command = cmd
+ end
+
+ def initialize
+ @servers = {}
+ @waiting = []
+ @queue = Queue.new
+ @thread = invoke_thread
+ @uri = nil
+ end
+ attr_accessor :uri
+
+ def service(name)
+ while true
+ server = nil
+ Thread.exclusive do
+ server = @servers[name] if @servers[name]
+ end
+ return server if server && server.alive?
+ invoke_service(name)
+ end
+ end
+
+ def regist(name, ro)
+ ary = nil
+ Thread.exclusive do
+ @servers[name] = ro
+ ary = @waiting
+ @waiting = []
+ end
+ ary.each do |th|
+ begin
+ th.run
+ rescue ThreadError
+ end
+ end
+ self
+ end
+
+ def unregist(name)
+ Thread.exclusive do
+ @servers.delete(name)
+ end
+ end
+
+ private
+ def invoke_thread
+ Thread.new do
+ while true
+ name = @queue.pop
+ invoke_service_command(name, @@command[name])
+ end
+ end
+ end
+
+ def invoke_service(name)
+ Thread.critical = true
+ @waiting.push Thread.current
+ @queue.push name
+ Thread.stop
+ end
+
+ def invoke_service_command(name, command)
+ raise "invalid command. name: #{name}" unless command
+ Thread.exclusive do
+ return if @servers.include?(name)
+ @servers[name] = false
+ end
+ uri = @uri || DRb.uri
+ if RUBY_PLATFORM =~ /mswin32/ && /NT/ =~ ENV["OS"]
+ system(%Q'cmd /c start "ruby" /b #{command} #{uri} #{name}')
+ else
+ system("#{command} #{uri} #{name} &")
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/drb/gw.rb b/ruby_1_8_5/lib/drb/gw.rb
new file mode 100644
index 0000000000..b7a5f5383f
--- /dev/null
+++ b/ruby_1_8_5/lib/drb/gw.rb
@@ -0,0 +1,122 @@
+require 'drb/drb'
+require 'monitor'
+
+module DRb
+ class GWIdConv < DRbIdConv
+ def to_obj(ref)
+ if Array === ref && ref[0] == :DRbObject
+ return DRbObject.new_with(ref[1], ref[2])
+ end
+ super(ref)
+ end
+ end
+
+ class GW
+ include MonitorMixin
+ def initialize
+ super()
+ @hash = {}
+ end
+
+ def [](key)
+ synchronize do
+ @hash[key]
+ end
+ end
+
+ def []=(key, v)
+ synchronize do
+ @hash[key] = v
+ end
+ end
+ end
+
+ class DRbObject
+ def self._load(s)
+ uri, ref = Marshal.load(s)
+ if DRb.uri == uri
+ return ref ? DRb.to_obj(ref) : DRb.front
+ end
+
+ self.new_with(DRb.uri, [:DRbObject, uri, ref])
+ end
+
+ def _dump(lv)
+ if DRb.uri == @uri
+ if Array === @ref && @ref[0] == :DRbObject
+ Marshal.dump([@ref[1], @ref[2]])
+ else
+ Marshal.dump([@uri, @ref]) # ??
+ end
+ else
+ Marshal.dump([DRb.uri, [:DRbObject, @uri, @ref]])
+ end
+ end
+ end
+end
+
+=begin
+DRb.install_id_conv(DRb::GWIdConv.new)
+
+front = DRb::GW.new
+
+s1 = DRb::DRbServer.new('drbunix:/tmp/gw_b_a', front)
+s2 = DRb::DRbServer.new('drbunix:/tmp/gw_b_c', front)
+
+s1.thread.join
+s2.thread.join
+=end
+
+=begin
+# foo.rb
+
+require 'drb/drb'
+
+class Foo
+ include DRbUndumped
+ def initialize(name, peer=nil)
+ @name = name
+ @peer = peer
+ end
+
+ def ping(obj)
+ puts "#{@name}: ping: #{obj.inspect}"
+ @peer.ping(self) if @peer
+ end
+end
+=end
+
+=begin
+# gw_a.rb
+require 'drb/unix'
+require 'foo'
+
+obj = Foo.new('a')
+DRb.start_service("drbunix:/tmp/gw_a", obj)
+
+robj = DRbObject.new_with_uri('drbunix:/tmp/gw_b_a')
+robj[:a] = obj
+
+DRb.thread.join
+=end
+
+=begin
+# gw_c.rb
+require 'drb/unix'
+require 'foo'
+
+foo = Foo.new('c', nil)
+
+DRb.start_service("drbunix:/tmp/gw_c", nil)
+
+robj = DRbObject.new_with_uri("drbunix:/tmp/gw_b_c")
+
+puts "c->b"
+a = robj[:a]
+sleep 2
+
+a.ping(foo)
+
+DRb.thread.join
+=end
+
diff --git a/ruby_1_8_5/lib/drb/invokemethod.rb b/ruby_1_8_5/lib/drb/invokemethod.rb
new file mode 100644
index 0000000000..412b2ab9b5
--- /dev/null
+++ b/ruby_1_8_5/lib/drb/invokemethod.rb
@@ -0,0 +1,36 @@
+# for ruby-1.8.0
+
+module DRb
+ class DRbServer
+ module InvokeMethod18Mixin
+ def block_yield(x)
+ if x.size == 1 && x[0].class == Array
+ x[0] = DRbArray.new(x[0])
+ end
+ block_value = @block.call(*x)
+ end
+
+ def perform_with_block
+ @obj.__send__(@msg_id, *@argv) do |*x|
+ jump_error = nil
+ begin
+ block_value = block_yield(x)
+ rescue LocalJumpError
+ jump_error = $!
+ end
+ if jump_error
+ case jump_error.reason
+ when :retry
+ retry
+ when :break
+ break(jump_error.exit_value)
+ else
+ raise jump_error
+ end
+ end
+ block_value
+ end
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/drb/observer.rb b/ruby_1_8_5/lib/drb/observer.rb
new file mode 100644
index 0000000000..e7f1668c52
--- /dev/null
+++ b/ruby_1_8_5/lib/drb/observer.rb
@@ -0,0 +1,22 @@
+require 'observer'
+
+module DRb
+ module DRbObservable
+ include Observable
+
+ def notify_observers(*arg)
+ if defined? @observer_state and @observer_state
+ if defined? @observer_peers
+ for i in @observer_peers.dup
+ begin
+ i.update(*arg)
+ rescue
+ delete_observer(i)
+ end
+ end
+ end
+ @observer_state = false
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/drb/ssl.rb b/ruby_1_8_5/lib/drb/ssl.rb
new file mode 100644
index 0000000000..58d6b7d1e0
--- /dev/null
+++ b/ruby_1_8_5/lib/drb/ssl.rb
@@ -0,0 +1,190 @@
+require 'socket'
+require 'openssl'
+require 'drb/drb'
+require 'singleton'
+
+module DRb
+
+ class DRbSSLSocket < DRbTCPSocket
+
+ class SSLConfig
+
+ DEFAULT = {
+ :SSLCertificate => nil,
+ :SSLPrivateKey => nil,
+ :SSLClientCA => nil,
+ :SSLCACertificatePath => nil,
+ :SSLCACertificateFile => nil,
+ :SSLVerifyMode => ::OpenSSL::SSL::VERIFY_NONE,
+ :SSLVerifyDepth => nil,
+ :SSLVerifyCallback => nil, # custom verification
+ :SSLCertificateStore => nil,
+ # Must specify if you use auto generated certificate.
+ :SSLCertName => nil, # e.g. [["CN","fqdn.example.com"]]
+ :SSLCertComment => "Generated by Ruby/OpenSSL"
+ }
+
+ def initialize(config)
+ @config = config
+ @cert = config[:SSLCertificate]
+ @pkey = config[:SSLPrivateKey]
+ @ssl_ctx = nil
+ end
+
+ def [](key);
+ @config[key] || DEFAULT[key]
+ end
+
+ def connect(tcp)
+ ssl = ::OpenSSL::SSL::SSLSocket.new(tcp, @ssl_ctx)
+ ssl.sync = true
+ ssl.connect
+ ssl
+ end
+
+ def accept(tcp)
+ ssl = OpenSSL::SSL::SSLSocket.new(tcp, @ssl_ctx)
+ ssl.sync = true
+ ssl.accept
+ ssl
+ end
+
+ def setup_certificate
+ if @cert && @pkey
+ return
+ end
+
+ rsa = OpenSSL::PKey::RSA.new(512){|p, n|
+ next unless self[:verbose]
+ case p
+ when 0; $stderr.putc "." # BN_generate_prime
+ when 1; $stderr.putc "+" # BN_generate_prime
+ when 2; $stderr.putc "*" # searching good prime,
+ # n = #of try,
+ # but also data from BN_generate_prime
+ when 3; $stderr.putc "\n" # found good prime, n==0 - p, n==1 - q,
+ # but also data from BN_generate_prime
+ else; $stderr.putc "*" # BN_generate_prime
+ end
+ }
+
+ cert = OpenSSL::X509::Certificate.new
+ cert.version = 3
+ cert.serial = 0
+ name = OpenSSL::X509::Name.new(self[:SSLCertName])
+ cert.subject = name
+ cert.issuer = name
+ cert.not_before = Time.now
+ cert.not_after = Time.now + (365*24*60*60)
+ cert.public_key = rsa.public_key
+
+ ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
+ cert.extensions = [
+ ef.create_extension("basicConstraints","CA:FALSE"),
+ ef.create_extension("subjectKeyIdentifier", "hash") ]
+ ef.issuer_certificate = cert
+ cert.add_extension(ef.create_extension("authorityKeyIdentifier",
+ "keyid:always,issuer:always"))
+ if comment = self[:SSLCertComment]
+ cert.add_extension(ef.create_extension("nsComment", comment))
+ end
+ cert.sign(rsa, OpenSSL::Digest::SHA1.new)
+
+ @cert = cert
+ @pkey = rsa
+ end
+
+ def setup_ssl_context
+ ctx = ::OpenSSL::SSL::SSLContext.new
+ ctx.cert = @cert
+ ctx.key = @pkey
+ ctx.client_ca = self[:SSLClientCA]
+ ctx.ca_path = self[:SSLCACertificatePath]
+ ctx.ca_file = self[:SSLCACertificateFile]
+ ctx.verify_mode = self[:SSLVerifyMode]
+ ctx.verify_depth = self[:SSLVerifyDepth]
+ ctx.verify_callback = self[:SSLVerifyCallback]
+ ctx.cert_store = self[:SSLCertificateStore]
+ @ssl_ctx = ctx
+ end
+ end
+
+ def self.parse_uri(uri)
+ if uri =~ /^drbssl:\/\/(.*?):(\d+)(\?(.*))?$/
+ host = $1
+ port = $2.to_i
+ option = $4
+ [host, port, option]
+ else
+ raise(DRbBadScheme, uri) unless uri =~ /^drbssl:/
+ raise(DRbBadURI, 'can\'t parse uri:' + uri)
+ end
+ end
+
+ def self.open(uri, config)
+ host, port, option = parse_uri(uri)
+ host.untaint
+ port.untaint
+ soc = TCPSocket.open(host, port)
+ ssl_conf = SSLConfig::new(config)
+ ssl_conf.setup_ssl_context
+ ssl = ssl_conf.connect(soc)
+ self.new(uri, ssl, ssl_conf, true)
+ end
+
+ def self.open_server(uri, config)
+ uri = 'drbssl://:0' unless uri
+ host, port, opt = parse_uri(uri)
+ if host.size == 0
+ host = getservername
+ soc = open_server_inaddr_any(host, port)
+ else
+ soc = TCPServer.open(host, port)
+ end
+ port = soc.addr[1] if port == 0
+ @uri = "drbssl://#{host}:#{port}"
+
+ ssl_conf = SSLConfig.new(config)
+ ssl_conf.setup_certificate
+ ssl_conf.setup_ssl_context
+ self.new(@uri, soc, ssl_conf, false)
+ end
+
+ def self.uri_option(uri, config)
+ host, port, option = parse_uri(uri)
+ return "drbssl://#{host}:#{port}", option
+ end
+
+ def initialize(uri, soc, config, is_established)
+ @ssl = is_established ? soc : nil
+ super(uri, soc.to_io, config)
+ end
+
+ def stream; @ssl; end
+
+ def close
+ if @ssl
+ @ssl.close
+ @ssl = nil
+ end
+ super
+ end
+
+ def accept
+ begin
+ while true
+ soc = @socket.accept
+ break if (@acl ? @acl.allow_socket?(soc) : true)
+ soc.close
+ end
+ ssl = @config.accept(soc)
+ self.class.new(uri, ssl, @config, true)
+ rescue OpenSSL::SSL::SSLError
+ warn("#{__FILE__}:#{__LINE__}: warning: #{$!.message} (#{$!.class})") if @config[:verbose]
+ retry
+ end
+ end
+ end
+
+ DRbProtocol.add_protocol(DRbSSLSocket)
+end
diff --git a/ruby_1_8_5/lib/drb/timeridconv.rb b/ruby_1_8_5/lib/drb/timeridconv.rb
new file mode 100644
index 0000000000..bb2c48d528
--- /dev/null
+++ b/ruby_1_8_5/lib/drb/timeridconv.rb
@@ -0,0 +1,91 @@
+require 'drb/drb'
+require 'monitor'
+
+module DRb
+ class TimerIdConv < DRbIdConv
+ class TimerHolder2
+ include MonitorMixin
+
+ class InvalidIndexError < RuntimeError; end
+
+ def initialize(timeout=600)
+ super()
+ @sentinel = Object.new
+ @gc = {}
+ @curr = {}
+ @renew = {}
+ @timeout = timeout
+ @keeper = keeper
+ end
+
+ def add(obj)
+ synchronize do
+ key = obj.__id__
+ @curr[key] = obj
+ return key
+ end
+ end
+
+ def fetch(key, dv=@sentinel)
+ synchronize do
+ obj = peek(key)
+ if obj == @sentinel
+ return dv unless dv == @sentinel
+ raise InvalidIndexError
+ end
+ @renew[key] = obj # KeepIt
+ return obj
+ end
+ end
+
+ def include?(key)
+ synchronize do
+ obj = peek(key)
+ return false if obj == @sentinel
+ true
+ end
+ end
+
+ def peek(key)
+ synchronize do
+ return @curr.fetch(key, @renew.fetch(key, @gc.fetch(key, @sentinel)))
+ end
+ end
+
+ private
+ def alternate
+ synchronize do
+ @gc = @curr # GCed
+ @curr = @renew
+ @renew = {}
+ end
+ end
+
+ def keeper
+ Thread.new do
+ loop do
+ size = alternate
+ sleep(@timeout)
+ end
+ end
+ end
+ end
+
+ def initialize(timeout=600)
+ @holder = TimerHolder2.new(timeout)
+ end
+
+ def to_obj(ref)
+ return super if ref.nil?
+ @holder.fetch(ref)
+ rescue TimerHolder2::InvalidIndexError
+ raise "invalid reference"
+ end
+
+ def to_id(obj)
+ return @holder.add(obj)
+ end
+ end
+end
+
+# DRb.install_id_conv(TimerIdConv.new)
diff --git a/ruby_1_8_5/lib/drb/unix.rb b/ruby_1_8_5/lib/drb/unix.rb
new file mode 100644
index 0000000000..989ec57eed
--- /dev/null
+++ b/ruby_1_8_5/lib/drb/unix.rb
@@ -0,0 +1,108 @@
+require 'socket'
+require 'drb/drb'
+require 'tmpdir'
+
+raise(LoadError, "UNIXServer is required") unless defined?(UNIXServer)
+
+module DRb
+
+ class DRbUNIXSocket < DRbTCPSocket
+ def self.parse_uri(uri)
+ if /^drbunix:(.*?)(\?(.*))?$/ =~ uri
+ filename = $1
+ option = $3
+ [filename, option]
+ else
+ raise(DRbBadScheme, uri) unless uri =~ /^drbunix:/
+ raise(DRbBadURI, 'can\'t parse uri:' + uri)
+ end
+ end
+
+ def self.open(uri, config)
+ filename, option = parse_uri(uri)
+ filename.untaint
+ soc = UNIXSocket.open(filename)
+ self.new(uri, soc, config)
+ end
+
+ def self.open_server(uri, config)
+ filename, option = parse_uri(uri)
+ if filename.size == 0
+ soc = temp_server
+ filename = soc.path
+ uri = 'drbunix:' + soc.path
+ else
+ soc = UNIXServer.open(filename)
+ end
+ owner = config[:UNIXFileOwner]
+ group = config[:UNIXFileGroup]
+ if owner || group
+ require 'etc'
+ owner = Etc.getpwnam( owner ).uid if owner
+ group = Etc.getgrnam( group ).gid if group
+ File.chown owner, group, filename
+ end
+ mode = config[:UNIXFileMode]
+ File.chmod(mode, filename) if mode
+
+ self.new(uri, soc, config, true)
+ end
+
+ def self.uri_option(uri, config)
+ filename, option = parse_uri(uri)
+ return "drbunix:#{filename}", option
+ end
+
+ def initialize(uri, soc, config={}, server_mode = false)
+ super(uri, soc, config)
+ set_sockopt(@socket)
+ @server_mode = server_mode
+ @acl = nil
+ end
+
+ # import from tempfile.rb
+ Max_try = 10
+ private
+ def self.temp_server
+ tmpdir = Dir::tmpdir
+ n = 0
+ while true
+ begin
+ tmpname = sprintf('%s/druby%d.%d', tmpdir, $$, n)
+ lock = tmpname + '.lock'
+ unless File.exist?(tmpname) or File.exist?(lock)
+ Dir.mkdir(lock)
+ break
+ end
+ rescue
+ raise "cannot generate tempfile `%s'" % tmpname if n >= Max_try
+ #sleep(1)
+ end
+ n += 1
+ end
+ soc = UNIXServer.new(tmpname)
+ Dir.rmdir(lock)
+ soc
+ end
+
+ public
+ def close
+ return unless @socket
+ path = @socket.path
+ @socket.close
+ File.unlink(path) if @server_mode
+ @socket = nil
+ end
+
+ def accept
+ s = @socket.accept
+ self.class.new(nil, s, @config)
+ end
+
+ def set_sockopt(soc)
+ soc.fcntl(Fcntl::F_SETFL, Fcntl::FD_CLOEXEC) if defined? Fcntl::FD_CLOEXEC
+ end
+ end
+
+ DRbProtocol.add_protocol(DRbUNIXSocket)
+end
diff --git a/ruby_1_8_5/lib/e2mmap.rb b/ruby_1_8_5/lib/e2mmap.rb
new file mode 100644
index 0000000000..3e2604af5d
--- /dev/null
+++ b/ruby_1_8_5/lib/e2mmap.rb
@@ -0,0 +1,195 @@
+#
+# e2mmap.rb - for ruby 1.1
+# $Release Version: 2.0$
+# $Revision: 1.10 $
+# $Date: 1999/02/17 12:33:17 $
+# by Keiju ISHITSUKA
+#
+# --
+# Usage:
+#
+# U1)
+# class Foo
+# extend Exception2MessageMapper
+# def_e2message ExistingExceptionClass, "message..."
+# def_exception :NewExceptionClass, "message..."[, superclass]
+# ...
+# end
+#
+# U2)
+# module Error
+# extend Exception2MessageMapper
+# def_e2meggage ExistingExceptionClass, "message..."
+# def_exception :NewExceptionClass, "message..."[, superclass]
+# ...
+# end
+# class Foo
+# include Error
+# ...
+# end
+#
+# foo = Foo.new
+# foo.Fail ....
+#
+# U3)
+# module Error
+# extend Exception2MessageMapper
+# def_e2message ExistingExceptionClass, "message..."
+# def_exception :NewExceptionClass, "message..."[, superclass]
+# ...
+# end
+# class Foo
+# extend Exception2MessageMapper
+# include Error
+# ...
+# end
+#
+# Foo.Fail NewExceptionClass, arg...
+# Foo.Fail ExistingExceptionClass, arg...
+#
+#
+fail "Use Ruby 1.1" if VERSION < "1.1"
+
+module Exception2MessageMapper
+ @RCS_ID='-$Id: e2mmap.rb,v 1.10 1999/02/17 12:33:17 keiju Exp keiju $-'
+
+ E2MM = Exception2MessageMapper
+
+ def E2MM.extend_object(cl)
+ super
+ cl.bind(self) unless cl == E2MM
+ end
+
+ # backward compatibility
+ def E2MM.extend_to(b)
+ c = eval("self", b)
+ c.extend(self)
+ end
+
+ def bind(cl)
+ self.module_eval %[
+ def Raise(err = nil, *rest)
+ Exception2MessageMapper.Raise(self.class, err, *rest)
+ end
+ alias Fail Raise
+
+ def self.included(mod)
+ mod.extend Exception2MessageMapper
+ end
+ ]
+ end
+
+ # Fail(err, *rest)
+ # err: exception
+ # rest: message arguments
+ #
+ def Raise(err = nil, *rest)
+ E2MM.Raise(self, err, *rest)
+ end
+ alias Fail Raise
+
+ # backward compatibility
+ alias fail! fail
+ def fail(err = nil, *rest)
+ begin
+ E2MM.Fail(self, err, *rest)
+ rescue E2MM::ErrNotRegisteredException
+ super
+ end
+ end
+ class << self
+ public :fail
+ end
+
+
+ # def_e2message(c, m)
+ # c: exception
+ # m: message_form
+ # define exception c with message m.
+ #
+ def def_e2message(c, m)
+ E2MM.def_e2message(self, c, m)
+ end
+
+ # def_exception(n, m, s)
+ # n: exception_name
+ # m: message_form
+ # s: superclass(default: StandardError)
+ # define exception named ``c'' with message m.
+ #
+ def def_exception(n, m, s = StandardError)
+ E2MM.def_exception(self, n, m, s)
+ end
+
+ #
+ # Private definitions.
+ #
+ # {[class, exp] => message, ...}
+ @MessageMap = {}
+
+ # E2MM.def_exception(k, e, m)
+ # k: class to define exception under.
+ # e: exception
+ # m: message_form
+ # define exception c with message m.
+ #
+ def E2MM.def_e2message(k, c, m)
+ E2MM.instance_eval{@MessageMap[[k, c]] = m}
+ c
+ end
+
+ # E2MM.def_exception(k, n, m, s)
+ # k: class to define exception under.
+ # n: exception_name
+ # m: message_form
+ # s: superclass(default: StandardError)
+ # define exception named ``c'' with message m.
+ #
+ def E2MM.def_exception(k, n, m, s = StandardError)
+ n = n.id2name if n.kind_of?(Fixnum)
+ e = Class.new(s)
+ E2MM.instance_eval{@MessageMap[[k, e]] = m}
+ k.const_set(n, e)
+ end
+
+ # Fail(klass, err, *rest)
+ # klass: class to define exception under.
+ # err: exception
+ # rest: message arguments
+ #
+ def E2MM.Raise(klass = E2MM, err = nil, *rest)
+ if form = e2mm_message(klass, err)
+ $! = err.new(sprintf(form, *rest))
+ $@ = caller(1) if $@.nil?
+ #p $@
+ #p __FILE__
+ $@.shift if $@[0] =~ /^#{Regexp.quote(__FILE__)}:/
+ raise
+ else
+ E2MM.Fail E2MM, ErrNotRegisteredException, err.inspect
+ end
+ end
+ class <<E2MM
+ alias Fail Raise
+ end
+
+ def E2MM.e2mm_message(klass, exp)
+ for c in klass.ancestors
+ if mes = @MessageMap[[c,exp]]
+ #p mes
+ m = klass.instance_eval('"' + mes + '"')
+ return m
+ end
+ end
+ nil
+ end
+ class <<self
+ alias message e2mm_message
+ end
+
+ E2MM.def_exception(E2MM,
+ :ErrNotRegisteredException,
+ "not registerd exception(%s)")
+end
+
+
diff --git a/ruby_1_8_5/lib/erb.rb b/ruby_1_8_5/lib/erb.rb
new file mode 100644
index 0000000000..bf4b441028
--- /dev/null
+++ b/ruby_1_8_5/lib/erb.rb
@@ -0,0 +1,826 @@
+# = ERB -- Ruby Templating
+#
+# Author:: Masatoshi SEKI
+# Documentation:: James Edward Gray II and Gavin Sinclair
+#
+# See ERB for primary documentation and ERB::Util for a couple of utility
+# routines.
+#
+# Copyright (c) 1999-2000,2002,2003 Masatoshi SEKI
+#
+# You can redistribute it and/or modify it under the same terms as Ruby.
+
+#
+# = ERB -- Ruby Templating
+#
+# == Introduction
+#
+# ERB provides an easy to use but powerful templating system for Ruby. Using
+# ERB, actual Ruby code can be added to any plain text document for the
+# purposes of generating document information details and/or flow control.
+#
+# A very simple example is this:
+#
+# require 'erb'
+#
+# x = 42
+# template = ERB.new <<-EOF
+# The value of x is: <%= x %>
+# EOF
+# puts template.result(binding)
+#
+# <em>Prints:</em> The value of x is: 42
+#
+# More complex examples are given below.
+#
+#
+# == Recognized Tags
+#
+# ERB recognizes certain tags in the provided template and converts them based
+# on the rules below:
+#
+# <% Ruby code -- inline with output %>
+# <%= Ruby expression -- replace with result %>
+# <%# comment -- ignored -- useful in testing %>
+# % a line of Ruby code -- treated as <% line %> (optional -- see ERB.new)
+# %% replaced with % if first thing on a line and % processing is used
+# <%% or %%> -- replace with <% or %> respectively
+#
+# All other text is passed through ERB filtering unchanged.
+#
+#
+# == Options
+#
+# There are several settings you can change when you use ERB:
+# * the nature of the tags that are recognized;
+# * the value of <tt>$SAFE</tt> under which the template is run;
+# * the binding used to resolve local variables in the template.
+#
+# See the ERB.new and ERB#result methods for more detail.
+#
+#
+# == Examples
+#
+# === Plain Text
+#
+# ERB is useful for any generic templating situation. Note that in this example, we use the
+# convenient "% at start of line" tag, and we quote the template literally with
+# <tt>%q{...}</tt> to avoid trouble with the backslash.
+#
+# require "erb"
+#
+# # Create template.
+# template = %q{
+# From: James Edward Gray II <james@grayproductions.net>
+# To: <%= to %>
+# Subject: Addressing Needs
+#
+# <%= to[/\w+/] %>:
+#
+# Just wanted to send a quick note assuring that your needs are being
+# addressed.
+#
+# I want you to know that my team will keep working on the issues,
+# especially:
+#
+# <%# ignore numerous minor requests -- focus on priorities %>
+# % priorities.each do |priority|
+# * <%= priority %>
+# % end
+#
+# Thanks for your patience.
+#
+# James Edward Gray II
+# }.gsub(/^ /, '')
+#
+# message = ERB.new(template, 0, "%<>")
+#
+# # Set up template data.
+# to = "Community Spokesman <spokesman@ruby_community.org>"
+# priorities = [ "Run Ruby Quiz",
+# "Document Modules",
+# "Answer Questions on Ruby Talk" ]
+#
+# # Produce result.
+# email = message.result
+# puts email
+#
+# <i>Generates:</i>
+#
+# From: James Edward Gray II <james@grayproductions.net>
+# To: Community Spokesman <spokesman@ruby_community.org>
+# Subject: Addressing Needs
+#
+# Community:
+#
+# Just wanted to send a quick note assuring that your needs are being addressed.
+#
+# I want you to know that my team will keep working on the issues, especially:
+#
+# * Run Ruby Quiz
+# * Document Modules
+# * Answer Questions on Ruby Talk
+#
+# Thanks for your patience.
+#
+# James Edward Gray II
+#
+# === Ruby in HTML
+#
+# ERB is often used in <tt>.rhtml</tt> files (HTML with embedded Ruby). Notice the need in
+# this example to provide a special binding when the template is run, so that the instance
+# variables in the Product object can be resolved.
+#
+# require "erb"
+#
+# # Build template data class.
+# class Product
+# def initialize( code, name, desc, cost )
+# @code = code
+# @name = name
+# @desc = desc
+# @cost = cost
+#
+# @features = [ ]
+# end
+#
+# def add_feature( feature )
+# @features << feature
+# end
+#
+# # Support templating of member data.
+# def get_binding
+# binding
+# end
+#
+# # ...
+# end
+#
+# # Create template.
+# template = %{
+# <html>
+# <head><title>Ruby Toys -- <%= @name %></title></head>
+# <body>
+#
+# <h1><%= @name %> (<%= @code %>)</h1>
+# <p><%= @desc %></p>
+#
+# <ul>
+# <% @features.each do |f| %>
+# <li><b><%= f %></b></li>
+# <% end %>
+# </ul>
+#
+# <p>
+# <% if @cost < 10 %>
+# <b>Only <%= @cost %>!!!</b>
+# <% else %>
+# Call for a price, today!
+# <% end %>
+# </p>
+#
+# </body>
+# </html>
+# }.gsub(/^ /, '')
+#
+# rhtml = ERB.new(template)
+#
+# # Set up template data.
+# toy = Product.new( "TZ-1002",
+# "Rubysapien",
+# "Geek's Best Friend! Responds to Ruby commands...",
+# 999.95 )
+# toy.add_feature("Listens for verbal commands in the Ruby language!")
+# toy.add_feature("Ignores Perl, Java, and all C variants.")
+# toy.add_feature("Karate-Chop Action!!!")
+# toy.add_feature("Matz signature on left leg.")
+# toy.add_feature("Gem studded eyes... Rubies, of course!")
+#
+# # Produce result.
+# rhtml.run(toy.get_binding)
+#
+# <i>Generates (some blank lines removed):</i>
+#
+# <html>
+# <head><title>Ruby Toys -- Rubysapien</title></head>
+# <body>
+#
+# <h1>Rubysapien (TZ-1002)</h1>
+# <p>Geek's Best Friend! Responds to Ruby commands...</p>
+#
+# <ul>
+# <li><b>Listens for verbal commands in the Ruby language!</b></li>
+# <li><b>Ignores Perl, Java, and all C variants.</b></li>
+# <li><b>Karate-Chop Action!!!</b></li>
+# <li><b>Matz signature on left leg.</b></li>
+# <li><b>Gem studded eyes... Rubies, of course!</b></li>
+# </ul>
+#
+# <p>
+# Call for a price, today!
+# </p>
+#
+# </body>
+# </html>
+#
+#
+# == Notes
+#
+# There are a variety of templating solutions available in various Ruby projects:
+# * ERB's big brother, eRuby, works the same but is written in C for speed;
+# * Amrita (smart at producing HTML/XML);
+# * cs/Template (written in C for speed);
+# * RDoc, distributed with Ruby, uses its own template engine, which can be reused elsewhere;
+# * and others; search the RAA.
+#
+# Rails, the web application framework, uses ERB to create views.
+#
+class ERB
+ Revision = '$Date: 2006/02/12 15:09:25 $' #'
+
+ # Returns revision information for the erb.rb module.
+ def self.version
+ "erb.rb [2.0.4 #{ERB::Revision.split[1]}]"
+ end
+end
+
+#--
+# ERB::Compiler
+class ERB
+ class Compiler # :nodoc:
+ class PercentLine # :nodoc:
+ def initialize(str)
+ @value = str
+ end
+ attr_reader :value
+ alias :to_s :value
+ end
+
+ class Scanner # :nodoc:
+ SplitRegexp = /(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>)|(\n)/
+
+ @scanner_map = {}
+ def self.regist_scanner(klass, trim_mode, percent)
+ @scanner_map[[trim_mode, percent]] = klass
+ end
+
+ def self.default_scanner=(klass)
+ @default_scanner = klass
+ end
+
+ def self.make_scanner(src, trim_mode, percent)
+ klass = @scanner_map.fetch([trim_mode, percent], @default_scanner)
+ klass.new(src, trim_mode, percent)
+ end
+
+ def initialize(src, trim_mode, percent)
+ @src = src
+ @stag = nil
+ end
+ attr_accessor :stag
+
+ def scan; end
+ end
+
+ class TrimScanner < Scanner # :nodoc:
+ TrimSplitRegexp = /(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>\n)|(%>)|(\n)/
+
+ def initialize(src, trim_mode, percent)
+ super
+ @trim_mode = trim_mode
+ @percent = percent
+ if @trim_mode == '>'
+ @scan_line = self.method(:trim_line1)
+ elsif @trim_mode == '<>'
+ @scan_line = self.method(:trim_line2)
+ elsif @trim_mode == '-'
+ @scan_line = self.method(:explicit_trim_line)
+ else
+ @scan_line = self.method(:scan_line)
+ end
+ end
+ attr_accessor :stag
+
+ def scan(&block)
+ @stag = nil
+ if @percent
+ @src.each do |line|
+ percent_line(line, &block)
+ end
+ else
+ @src.each do |line|
+ @scan_line.call(line, &block)
+ end
+ end
+ nil
+ end
+
+ def percent_line(line, &block)
+ if @stag || line[0] != ?%
+ return @scan_line.call(line, &block)
+ end
+
+ line[0] = ''
+ if line[0] == ?%
+ @scan_line.call(line, &block)
+ else
+ yield(PercentLine.new(line.chomp))
+ end
+ end
+
+ def scan_line(line)
+ line.split(SplitRegexp).each do |token|
+ next if token.empty?
+ yield(token)
+ end
+ end
+
+ def trim_line1(line)
+ line.split(TrimSplitRegexp).each do |token|
+ next if token.empty?
+ if token == "%>\n"
+ yield('%>')
+ yield(:cr)
+ break
+ end
+ yield(token)
+ end
+ end
+
+ def trim_line2(line)
+ head = nil
+ line.split(TrimSplitRegexp).each do |token|
+ next if token.empty?
+ head = token unless head
+ if token == "%>\n"
+ yield('%>')
+ if is_erb_stag?(head)
+ yield(:cr)
+ else
+ yield("\n")
+ end
+ break
+ end
+ yield(token)
+ end
+ end
+
+ ExplicitTrimRegexp = /(^[ \t]*<%-)|(-%>\n?\z)|(<%-)|(-%>)|(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>)|(\n)/
+ def explicit_trim_line(line)
+ line.split(ExplicitTrimRegexp).each do |token|
+ next if token.empty?
+ if @stag.nil? && /[ \t]*<%-/ =~ token
+ yield('<%')
+ elsif @stag && /-%>\n/ =~ token
+ yield('%>')
+ yield(:cr)
+ elsif @stag && token == '-%>'
+ yield('%>')
+ else
+ yield(token)
+ end
+ end
+ end
+
+ ERB_STAG = %w(<%= <%# <%)
+ def is_erb_stag?(s)
+ ERB_STAG.member?(s)
+ end
+ end
+
+ Scanner.default_scanner = TrimScanner
+
+ class SimpleScanner < Scanner # :nodoc:
+ def scan
+ @src.each do |line|
+ line.split(SplitRegexp).each do |token|
+ next if token.empty?
+ yield(token)
+ end
+ end
+ end
+ end
+
+ Scanner.regist_scanner(SimpleScanner, nil, false)
+
+ begin
+ require 'strscan'
+ class SimpleScanner2 < Scanner # :nodoc:
+ def scan
+ stag_reg = /(.*?)(<%%|<%=|<%#|<%|\n|\z)/
+ etag_reg = /(.*?)(%%>|%>|\n|\z)/
+ scanner = StringScanner.new(@src)
+ while ! scanner.eos?
+ scanner.scan(@stag ? etag_reg : stag_reg)
+ text = scanner[1]
+ elem = scanner[2]
+ yield(text) unless text.empty?
+ yield(elem) unless elem.empty?
+ end
+ end
+ end
+ Scanner.regist_scanner(SimpleScanner2, nil, false)
+
+ class PercentScanner < Scanner # :nodoc:
+ def scan
+ new_line = true
+ stag_reg = /(.*?)(<%%|<%=|<%#|<%|\n|\z)/
+ etag_reg = /(.*?)(%%>|%>|\n|\z)/
+ scanner = StringScanner.new(@src)
+ while ! scanner.eos?
+ if new_line && @stag.nil?
+ if scanner.scan(/%%/)
+ yield('%')
+ new_line = false
+ next
+ elsif scanner.scan(/%/)
+ yield(PercentLine.new(scanner.scan(/.*?(\n|\z)/).chomp))
+ next
+ end
+ end
+ scanner.scan(@stag ? etag_reg : stag_reg)
+ text = scanner[1]
+ elem = scanner[2]
+ yield(text) unless text.empty?
+ yield(elem) unless elem.empty?
+ new_line = (elem == "\n")
+ end
+ end
+ end
+ Scanner.regist_scanner(PercentScanner, nil, true)
+
+ class ExplicitScanner < Scanner # :nodoc:
+ def scan
+ new_line = true
+ stag_reg = /(.*?)(<%%|<%=|<%#|<%-|<%|\n|\z)/
+ etag_reg = /(.*?)(%%>|-%>|%>|\n|\z)/
+ scanner = StringScanner.new(@src)
+ while ! scanner.eos?
+ if new_line && @stag.nil? && scanner.scan(/[ \t]*<%-/)
+ yield('<%')
+ new_line = false
+ next
+ end
+ scanner.scan(@stag ? etag_reg : stag_reg)
+ text = scanner[1]
+ elem = scanner[2]
+ new_line = (elem == "\n")
+ yield(text) unless text.empty?
+ if elem == '-%>'
+ yield('%>')
+ if scanner.scan(/(\n|\z)/)
+ yield(:cr)
+ new_line = true
+ end
+ elsif elem == '<%-'
+ yield('<%')
+ else
+ yield(elem) unless elem.empty?
+ end
+ end
+ end
+ end
+ Scanner.regist_scanner(ExplicitScanner, '-', false)
+
+ rescue LoadError
+ end
+
+ class Buffer # :nodoc:
+ def initialize(compiler)
+ @compiler = compiler
+ @line = []
+ @script = ""
+ @compiler.pre_cmd.each do |x|
+ push(x)
+ end
+ end
+ attr_reader :script
+
+ def push(cmd)
+ @line << cmd
+ end
+
+ def cr
+ @script << (@line.join('; '))
+ @line = []
+ @script << "\n"
+ end
+
+ def close
+ return unless @line
+ @compiler.post_cmd.each do |x|
+ push(x)
+ end
+ @script << (@line.join('; '))
+ @line = nil
+ end
+ end
+
+ def compile(s)
+ out = Buffer.new(self)
+
+ content = ''
+ scanner = make_scanner(s)
+ scanner.scan do |token|
+ if scanner.stag.nil?
+ case token
+ when PercentLine
+ out.push("#{@put_cmd} #{content.dump}") if content.size > 0
+ content = ''
+ out.push(token.to_s)
+ out.cr
+ when :cr
+ out.cr
+ when '<%', '<%=', '<%#'
+ scanner.stag = token
+ out.push("#{@put_cmd} #{content.dump}") if content.size > 0
+ content = ''
+ when "\n"
+ content << "\n"
+ out.push("#{@put_cmd} #{content.dump}")
+ out.cr
+ content = ''
+ when '<%%'
+ content << '<%'
+ else
+ content << token
+ end
+ else
+ case token
+ when '%>'
+ case scanner.stag
+ when '<%'
+ if content[-1] == ?\n
+ content.chop!
+ out.push(content)
+ out.cr
+ else
+ out.push(content)
+ end
+ when '<%='
+ out.push("#{@insert_cmd}((#{content}).to_s)")
+ when '<%#'
+ # out.push("# #{content.dump}")
+ end
+ scanner.stag = nil
+ content = ''
+ when '%%>'
+ content << '%>'
+ else
+ content << token
+ end
+ end
+ end
+ out.push("#{@put_cmd} #{content.dump}") if content.size > 0
+ out.close
+ out.script
+ end
+
+ def prepare_trim_mode(mode)
+ case mode
+ when 1
+ return [false, '>']
+ when 2
+ return [false, '<>']
+ when 0
+ return [false, nil]
+ when String
+ perc = mode.include?('%')
+ if mode.include?('-')
+ return [perc, '-']
+ elsif mode.include?('<>')
+ return [perc, '<>']
+ elsif mode.include?('>')
+ return [perc, '>']
+ else
+ [perc, nil]
+ end
+ else
+ return [false, nil]
+ end
+ end
+
+ def make_scanner(src)
+ Scanner.make_scanner(src, @trim_mode, @percent)
+ end
+
+ def initialize(trim_mode)
+ @percent, @trim_mode = prepare_trim_mode(trim_mode)
+ @put_cmd = 'print'
+ @insert_cmd = @put_cmd
+ @pre_cmd = []
+ @post_cmd = []
+ end
+ attr_reader :percent, :trim_mode
+ attr_accessor :put_cmd, :insert_cmd, :pre_cmd, :post_cmd
+ end
+end
+
+#--
+# ERB
+class ERB
+ #
+ # Constructs a new ERB object with the template specified in _str_.
+ #
+ # An ERB object works by building a chunk of Ruby code that will output
+ # the completed template when run. If _safe_level_ is set to a non-nil value,
+ # ERB code will be run in a separate thread with <b>$SAFE</b> set to the
+ # provided level.
+ #
+ # If _trim_mode_ is passed a String containing one or more of the following
+ # modifiers, ERB will adjust its code generation as listed:
+ #
+ # % enables Ruby code processing for lines beginning with %
+ # <> omit newline for lines starting with <% and ending in %>
+ # > omit newline for lines ending in %>
+ #
+ # _eoutvar_ can be used to set the name of the variable ERB will build up
+ # its output in. This is useful when you need to run multiple ERB
+ # templates through the same binding and/or when you want to control where
+ # output ends up. Pass the name of the variable to be used inside a String.
+ #
+ # === Example
+ #
+ # require "erb"
+ #
+ # # build data class
+ # class Listings
+ # PRODUCT = { :name => "Chicken Fried Steak",
+ # :desc => "A well messages pattie, breaded and fried.",
+ # :cost => 9.95 }
+ #
+ # attr_reader :product, :price
+ #
+ # def initialize( product = "", price = "" )
+ # @product = product
+ # @price = price
+ # end
+ #
+ # def build
+ # b = binding
+ # # create and run templates, filling member data variebles
+ # ERB.new(<<-'END_PRODUCT'.gsub(/^\s+/, ""), 0, "", "@product").result b
+ # <%= PRODUCT[:name] %>
+ # <%= PRODUCT[:desc] %>
+ # END_PRODUCT
+ # ERB.new(<<-'END_PRICE'.gsub(/^\s+/, ""), 0, "", "@price").result b
+ # <%= PRODUCT[:name] %> -- <%= PRODUCT[:cost] %>
+ # <%= PRODUCT[:desc] %>
+ # END_PRICE
+ # end
+ # end
+ #
+ # # setup template data
+ # listings = Listings.new
+ # listings.build
+ #
+ # puts listings.product + "\n" + listings.price
+ #
+ # _Generates_
+ #
+ # Chicken Fried Steak
+ # A well messages pattie, breaded and fried.
+ #
+ # Chicken Fried Steak -- 9.95
+ # A well messages pattie, breaded and fried.
+ #
+ def initialize(str, safe_level=nil, trim_mode=nil, eoutvar='_erbout')
+ @safe_level = safe_level
+ compiler = ERB::Compiler.new(trim_mode)
+ set_eoutvar(compiler, eoutvar)
+ @src = compiler.compile(str)
+ @filename = nil
+ end
+
+ # The Ruby code generated by ERB
+ attr_reader :src
+
+ # The optional _filename_ argument passed to Kernel#eval when the ERB code
+ # is run
+ attr_accessor :filename
+
+ #
+ # Can be used to set _eoutvar_ as described in ERB#new. It's probably easier
+ # to just use the constructor though, since calling this method requires the
+ # setup of an ERB _compiler_ object.
+ #
+ def set_eoutvar(compiler, eoutvar = '_erbout')
+ compiler.put_cmd = "#{eoutvar}.concat"
+ compiler.insert_cmd = "#{eoutvar}.concat"
+
+ cmd = []
+ cmd.push "#{eoutvar} = ''"
+
+ compiler.pre_cmd = cmd
+
+ cmd = []
+ cmd.push(eoutvar)
+
+ compiler.post_cmd = cmd
+ end
+
+ # Generate results and print them. (see ERB#result)
+ def run(b=TOPLEVEL_BINDING)
+ print self.result(b)
+ end
+
+ #
+ # Executes the generated ERB code to produce a completed template, returning
+ # the results of that code. (See ERB#new for details on how this process can
+ # be affected by _safe_level_.)
+ #
+ # _b_ accepts a Binding or Proc object which is used to set the context of
+ # code evaluation.
+ #
+ def result(b=TOPLEVEL_BINDING)
+ if @safe_level
+ th = Thread.start {
+ $SAFE = @safe_level
+ eval(@src, b, (@filename || '(erb)'), 1)
+ }
+ return th.value
+ else
+ return eval(@src, b, (@filename || '(erb)'), 1)
+ end
+ end
+
+ def def_method(mod, methodname, fname='(ERB)') # :nodoc:
+ mod.module_eval("def #{methodname}\n" + self.src + "\nend\n", fname, 0)
+ end
+
+ def def_module(methodname='erb') # :nodoc:
+ mod = Module.new
+ def_method(mod, methodname)
+ mod
+ end
+
+ def def_class(superklass=Object, methodname='result') # :nodoc:
+ cls = Class.new(superklass)
+ def_method(cls, methodname)
+ cls
+ end
+end
+
+#--
+# ERB::Util
+class ERB
+ # A utility module for conversion routines, often handy in HTML generation.
+ module Util
+ public
+ #
+ # A utility method for escaping HTML tag characters in _s_.
+ #
+ # require "erb"
+ # include ERB::Util
+ #
+ # puts html_escape("is a > 0 & a < 10?")
+ #
+ # _Generates_
+ #
+ # is a &gt; 0 &amp; a &lt; 10?
+ #
+ def html_escape(s)
+ s.to_s.gsub(/&/, "&amp;").gsub(/\"/, "&quot;").gsub(/>/, "&gt;").gsub(/</, "&lt;")
+ end
+ alias h html_escape
+ module_function :h
+ module_function :html_escape
+
+ #
+ # A utility method for encoding the String _s_ as a URL.
+ #
+ # require "erb"
+ # include ERB::Util
+ #
+ # puts url_encode("Programming Ruby: The Pragmatic Programmer's Guide")
+ #
+ # _Generates_
+ #
+ # Programming%20Ruby%3A%20%20The%20Pragmatic%20Programmer%27s%20Guide
+ #
+ def url_encode(s)
+ s.to_s.gsub(/[^a-zA-Z0-9_\-.]/n){ sprintf("%%%02X", $&.unpack("C")[0]) }
+ end
+ alias u url_encode
+ module_function :u
+ module_function :url_encode
+ end
+end
+
+#--
+# ERB::DefMethod
+class ERB
+ module DefMethod # :nodoc:
+ public
+ def def_erb_method(methodname, erb)
+ if erb.kind_of? String
+ fname = erb
+ File.open(fname) {|f| erb = ERB.new(f.read) }
+ erb.def_method(self, methodname, fname)
+ else
+ erb.def_method(self, methodname)
+ end
+ end
+ module_function :def_erb_method
+ end
+end
diff --git a/ruby_1_8_5/lib/eregex.rb b/ruby_1_8_5/lib/eregex.rb
new file mode 100644
index 0000000000..cc7a7f6f46
--- /dev/null
+++ b/ruby_1_8_5/lib/eregex.rb
@@ -0,0 +1,37 @@
+# this is just a proof of concept toy.
+
+class RegOr
+ def initialize(re1, re2)
+ @re1 = re1
+ @re2 = re2
+ end
+
+ def =~ (str)
+ @re1 =~ str or @re2 =~ str
+ end
+end
+
+class RegAnd
+ def initialize(re1, re2)
+ @re1 = re1
+ @re2 = re2
+ end
+
+ def =~ (str)
+ @re1 =~ str and @re2 =~ str
+ end
+end
+
+class Regexp
+ def |(other)
+ RegOr.new(self, other)
+ end
+ def &(other)
+ RegAnd.new(self, other)
+ end
+end
+
+if __FILE__ == $0
+ p "abc" =~ /b/|/c/
+ p "abc" =~ /b/&/c/
+end
diff --git a/ruby_1_8_5/lib/fileutils.rb b/ruby_1_8_5/lib/fileutils.rb
new file mode 100644
index 0000000000..043a24d2b5
--- /dev/null
+++ b/ruby_1_8_5/lib/fileutils.rb
@@ -0,0 +1,1578 @@
+#
+# = fileutils.rb
+#
+# Copyright (c) 2000-2005 Minero Aoki <aamine@loveruby.net>
+#
+# This program is free software.
+# You can distribute/modify this program under the same terms of ruby.
+#
+# == module FileUtils
+#
+# Namespace for several file utility methods for copying, moving, removing, etc.
+#
+# === Module Functions
+#
+# cd(dir, options)
+# cd(dir, options) {|dir| .... }
+# pwd()
+# mkdir(dir, options)
+# mkdir(list, options)
+# mkdir_p(dir, options)
+# mkdir_p(list, options)
+# rmdir(dir, options)
+# rmdir(list, options)
+# ln(old, new, options)
+# ln(list, destdir, options)
+# ln_s(old, new, options)
+# ln_s(list, destdir, options)
+# ln_sf(src, dest, options)
+# cp(src, dest, options)
+# cp(list, dir, options)
+# cp_r(src, dest, options)
+# cp_r(list, dir, options)
+# mv(src, dest, options)
+# mv(list, dir, options)
+# rm(list, options)
+# rm_r(list, options)
+# rm_rf(list, options)
+# install(src, dest, mode = <src's>, options)
+# chmod(mode, list, options)
+# chmod_R(mode, list, options)
+# chown(user, group, list, options)
+# chown_R(user, group, list, options)
+# touch(list, options)
+#
+# The <tt>options</tt> parameter is a hash of options, taken from the list
+# <tt>:force</tt>, <tt>:noop</tt>, <tt>:preserve</tt>, and <tt>:verbose</tt>.
+# <tt>:noop</tt> means that no changes are made. The other two are obvious.
+# Each method documents the options that it honours.
+#
+# All methods that have the concept of a "source" file or directory can take
+# either one file or a list of files in that argument. See the method
+# documentation for examples.
+#
+# There are some `low level' methods, which do not accept any option:
+#
+# copy_entry(src, dest, preserve = false, dereference = false)
+# copy_file(src, dest, preserve = false, dereference = true)
+# copy_stream(srcstream, deststream)
+# remove_entry(path, force = false)
+# remove_entry_secure(path, force = false)
+# remove_file(path, force = false)
+# compare_file(path_a, path_b)
+# compare_stream(stream_a, stream_b)
+# uptodate?(file, cmp_list)
+#
+# == module FileUtils::Verbose
+#
+# This module has all methods of FileUtils module, but it outputs messages
+# before acting. This equates to passing the <tt>:verbose</tt> flag to methods
+# in FileUtils.
+#
+# == module FileUtils::NoWrite
+#
+# This module has all methods of FileUtils module, but never changes
+# files/directories. This equates to passing the <tt>:noop</tt> flag to methods
+# in FileUtils.
+#
+# == module FileUtils::DryRun
+#
+# This module has all methods of FileUtils module, but never changes
+# files/directories. This equates to passing the <tt>:noop</tt> and
+# <tt>:verbose</tt> flags to methods in FileUtils.
+#
+
+module FileUtils
+
+ def self.private_module_function(name) #:nodoc:
+ module_function name
+ private_class_method name
+ end
+
+ # This hash table holds command options.
+ OPT_TABLE = {} #:nodoc: internal use only
+
+ #
+ # Options: (none)
+ #
+ # Returns the name of the current directory.
+ #
+ def pwd
+ Dir.pwd
+ end
+ module_function :pwd
+
+ alias getwd pwd
+ module_function :getwd
+
+ #
+ # Options: verbose
+ #
+ # Changes the current directory to the directory +dir+.
+ #
+ # If this method is called with block, resumes to the old
+ # working directory after the block execution finished.
+ #
+ # FileUtils.cd('/', :verbose => true) # chdir and report it
+ #
+ def cd(dir, options = {}, &block) # :yield: dir
+ fu_check_options options, :verbose
+ fu_output_message "cd #{dir}" if options[:verbose]
+ Dir.chdir(dir, &block)
+ fu_output_message 'cd -' if options[:verbose] and block
+ end
+ module_function :cd
+
+ alias chdir cd
+ module_function :chdir
+
+ OPT_TABLE['cd'] =
+ OPT_TABLE['chdir'] = %w( verbose )
+
+ #
+ # Options: (none)
+ #
+ # Returns true if +newer+ is newer than all +old_list+.
+ # Non-existent files are older than any file.
+ #
+ # FileUtils.uptodate?('hello.o', %w(hello.c hello.h)) or \
+ # system 'make hello.o'
+ #
+ def uptodate?(new, old_list, options = nil)
+ raise ArgumentError, 'uptodate? does not accept any option' if options
+
+ return false unless File.exist?(new)
+ new_time = File.mtime(new)
+ old_list.each do |old|
+ if File.exist?(old)
+ return false unless new_time > File.mtime(old)
+ end
+ end
+ true
+ end
+ module_function :uptodate?
+
+ #
+ # Options: mode noop verbose
+ #
+ # Creates one or more directories.
+ #
+ # FileUtils.mkdir 'test'
+ # FileUtils.mkdir %w( tmp data )
+ # FileUtils.mkdir 'notexist', :noop => true # Does not really create.
+ # FileUtils.mkdir 'tmp', :mode => 0700
+ #
+ def mkdir(list, options = {})
+ fu_check_options options, :mode, :noop, :verbose
+ list = fu_list(list)
+ fu_output_message "mkdir #{options[:mode] ? ('-m %03o ' % options[:mode]) : ''}#{list.join ' '}" if options[:verbose]
+ return if options[:noop]
+
+ list.each do |dir|
+ fu_mkdir dir, options[:mode]
+ end
+ end
+ module_function :mkdir
+
+ OPT_TABLE['mkdir'] = %w( noop verbose mode )
+
+ #
+ # Options: mode noop verbose
+ #
+ # Creates a directory and all its parent directories.
+ # For example,
+ #
+ # FileUtils.mkdir_p '/usr/local/lib/ruby'
+ #
+ # causes to make following directories, if it does not exist.
+ # * /usr
+ # * /usr/local
+ # * /usr/local/lib
+ # * /usr/local/lib/ruby
+ #
+ # You can pass several directories at a time in a list.
+ #
+ def mkdir_p(list, options = {})
+ fu_check_options options, :mode, :noop, :verbose
+ list = fu_list(list)
+ fu_output_message "mkdir -p #{options[:mode] ? ('-m %03o ' % options[:mode]) : ''}#{list.join ' '}" if options[:verbose]
+ return *list if options[:noop]
+
+ list.map {|path| path.sub(%r</\z>, '') }.each do |path|
+ # optimize for the most common case
+ begin
+ fu_mkdir path, options[:mode]
+ next
+ rescue SystemCallError
+ next if File.directory?(path)
+ end
+
+ stack = []
+ until path == stack.last # dirname("/")=="/", dirname("C:/")=="C:/"
+ stack.push path
+ path = File.dirname(path)
+ end
+ stack.reverse_each do |path|
+ begin
+ fu_mkdir path, options[:mode]
+ rescue SystemCallError => err
+ raise unless File.directory?(path)
+ end
+ end
+ end
+
+ return *list
+ end
+ module_function :mkdir_p
+
+ alias mkpath mkdir_p
+ alias makedirs mkdir_p
+ module_function :mkpath
+ module_function :makedirs
+
+ OPT_TABLE['mkdir_p'] =
+ OPT_TABLE['mkpath'] =
+ OPT_TABLE['makedirs'] = %w( noop verbose )
+
+ def fu_mkdir(path, mode) #:nodoc:
+ path = path.sub(%r</\z>, '')
+ if mode
+ Dir.mkdir path, mode
+ File.chmod mode, path
+ else
+ Dir.mkdir path
+ end
+ end
+ private_module_function :fu_mkdir
+
+ #
+ # Options: noop, verbose
+ #
+ # Removes one or more directories.
+ #
+ # FileUtils.rmdir 'somedir'
+ # FileUtils.rmdir %w(somedir anydir otherdir)
+ # # Does not really remove directory; outputs message.
+ # FileUtils.rmdir 'somedir', :verbose => true, :noop => true
+ #
+ def rmdir(list, options = {})
+ fu_check_options options, :noop, :verbose
+ list = fu_list(list)
+ fu_output_message "rmdir #{list.join ' '}" if options[:verbose]
+ return if options[:noop]
+ list.each do |dir|
+ Dir.rmdir dir.sub(%r</\z>, '')
+ end
+ end
+ module_function :rmdir
+
+ OPT_TABLE['rmdir'] = %w( noop verbose )
+
+ #
+ # Options: force noop verbose
+ #
+ # <b><tt>ln(old, new, options = {})</tt></b>
+ #
+ # Creates a hard link +new+ which points to +old+.
+ # If +new+ already exists and it is a directory, creates a link +new/old+.
+ # If +new+ already exists and it is not a directory, raises Errno::EEXIST.
+ # But if :force option is set, overwrite +new+.
+ #
+ # FileUtils.ln 'gcc', 'cc', :verbose => true
+ # FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs'
+ #
+ # <b><tt>ln(list, destdir, options = {})</tt></b>
+ #
+ # Creates several hard links in a directory, with each one pointing to the
+ # item in +list+. If +destdir+ is not a directory, raises Errno::ENOTDIR.
+ #
+ # include FileUtils
+ # cd '/sbin'
+ # FileUtils.ln %w(cp mv mkdir), '/bin' # Now /sbin/cp and /bin/cp are linked.
+ #
+ def ln(src, dest, options = {})
+ fu_check_options options, :force, :noop, :verbose
+ fu_output_message "ln#{options[:force] ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
+ return if options[:noop]
+ fu_each_src_dest0(src, dest) do |s,d|
+ remove_file d, true if options[:force]
+ File.link s, d
+ end
+ end
+ module_function :ln
+
+ alias link ln
+ module_function :link
+
+ OPT_TABLE['ln'] =
+ OPT_TABLE['link'] = %w( noop verbose force )
+
+ #
+ # Options: force noop verbose
+ #
+ # <b><tt>ln_s(old, new, options = {})</tt></b>
+ #
+ # Creates a symbolic link +new+ which points to +old+. If +new+ already
+ # exists and it is a directory, creates a symbolic link +new/old+. If +new+
+ # already exists and it is not a directory, raises Errno::EEXIST. But if
+ # :force option is set, overwrite +new+.
+ #
+ # FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby'
+ # FileUtils.ln_s 'verylongsourcefilename.c', 'c', :force => true
+ #
+ # <b><tt>ln_s(list, destdir, options = {})</tt></b>
+ #
+ # Creates several symbolic links in a directory, with each one pointing to the
+ # item in +list+. If +destdir+ is not a directory, raises Errno::ENOTDIR.
+ #
+ # If +destdir+ is not a directory, raises Errno::ENOTDIR.
+ #
+ # FileUtils.ln_s Dir.glob('bin/*.rb'), '/home/aamine/bin'
+ #
+ def ln_s(src, dest, options = {})
+ fu_check_options options, :force, :noop, :verbose
+ fu_output_message "ln -s#{options[:force] ? 'f' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
+ return if options[:noop]
+ fu_each_src_dest0(src, dest) do |s,d|
+ remove_file d, true if options[:force]
+ File.symlink s, d
+ end
+ end
+ module_function :ln_s
+
+ alias symlink ln_s
+ module_function :symlink
+
+ OPT_TABLE['ln_s'] =
+ OPT_TABLE['symlink'] = %w( noop verbose force )
+
+ #
+ # Options: noop verbose
+ #
+ # Same as
+ # #ln_s(src, dest, :force)
+ #
+ def ln_sf(src, dest, options = {})
+ fu_check_options options, :noop, :verbose
+ options = options.dup
+ options[:force] = true
+ ln_s src, dest, options
+ end
+ module_function :ln_sf
+
+ OPT_TABLE['ln_sf'] = %w( noop verbose )
+
+ #
+ # Options: preserve noop verbose
+ #
+ # Copies a file content +src+ to +dest+. If +dest+ is a directory,
+ # copies +src+ to +dest/src+.
+ #
+ # If +src+ is a list of files, then +dest+ must be a directory.
+ #
+ # FileUtils.cp 'eval.c', 'eval.c.org'
+ # FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6'
+ # FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', :verbose => true
+ # FileUtils.cp 'symlink', 'dest' # copy content, "dest" is not a symlink
+ #
+ def cp(src, dest, options = {})
+ fu_check_options options, :preserve, :noop, :verbose
+ fu_output_message "cp#{options[:preserve] ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
+ return if options[:noop]
+ fu_each_src_dest(src, dest) do |s, d|
+ copy_file s, d, options[:preserve]
+ end
+ end
+ module_function :cp
+
+ alias copy cp
+ module_function :copy
+
+ OPT_TABLE['cp'] =
+ OPT_TABLE['copy'] = %w( noop verbose preserve )
+
+ #
+ # Options: preserve noop verbose dereference_root
+ #
+ # Copies +src+ to +dest+. If +src+ is a directory, this method copies
+ # all its contents recursively. If +dest+ is a directory, copies
+ # +src+ to +dest/src+.
+ #
+ # +src+ can be a list of files.
+ #
+ # # Installing ruby library "mylib" under the site_ruby
+ # FileUtils.rm_r site_ruby + '/mylib', :force
+ # FileUtils.cp_r 'lib/', site_ruby + '/mylib'
+ #
+ # # Examples of copying several files to target directory.
+ # FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail'
+ # FileUtils.cp_r Dir.glob('*.rb'), '/home/aamine/lib/ruby', :noop => true, :verbose => true
+ #
+ # # If you want to copy all contents of a directory instead of the
+ # # directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
+ # # use following code.
+ # FileUtils.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes src/dest,
+ # # but this doesn't.
+ #
+ def cp_r(src, dest, options = {})
+ fu_check_options options, :preserve, :noop, :verbose, :dereference_root
+ fu_output_message "cp -r#{options[:preserve] ? 'p' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
+ return if options[:noop]
+ options[:dereference_root] = true unless options.key?(:dereference_root)
+ fu_each_src_dest(src, dest) do |s, d|
+ copy_entry s, d, options[:preserve], options[:dereference_root]
+ end
+ end
+ module_function :cp_r
+
+ OPT_TABLE['cp_r'] = %w( noop verbose preserve dereference_root )
+
+ #
+ # Copies a file system entry +src+ to +dest+.
+ # If +src+ is a directory, this method copies its contents recursively.
+ # This method preserves file types, c.f. symlink, directory...
+ # (FIFO, device files and etc. are not supported yet)
+ #
+ # Both of +src+ and +dest+ must be a path name.
+ # +src+ must exist, +dest+ must not exist.
+ #
+ # If +preserve+ is true, this method preserves owner, group, permissions
+ # and modified time.
+ #
+ # If +dereference_root+ is true, this method dereference tree root.
+ #
+ def copy_entry(src, dest, preserve = false, dereference_root = false)
+ Entry_.new(src, nil, dereference_root).traverse do |ent|
+ destent = Entry_.new(dest, ent.rel, false)
+ ent.copy destent.path
+ ent.copy_metadata destent.path if preserve
+ end
+ end
+ module_function :copy_entry
+
+ #
+ # Copies file contents of +src+ to +dest+.
+ # Both of +src+ and +dest+ must be a path name.
+ #
+ def copy_file(src, dest, preserve = false, dereference = true)
+ ent = Entry_.new(src, nil, dereference)
+ ent.copy_file dest
+ ent.copy_metadata dest if preserve
+ end
+ module_function :copy_file
+
+ #
+ # Copies stream +src+ to +dest+.
+ # +src+ must respond to #read(n) and
+ # +dest+ must respond to #write(str).
+ #
+ def copy_stream(src, dest)
+ fu_copy_stream0 src, dest, fu_stream_blksize(src, dest)
+ end
+ module_function :copy_stream
+
+ #
+ # Options: force noop verbose
+ #
+ # Moves file(s) +src+ to +dest+. If +file+ and +dest+ exist on the different
+ # disk partition, the file is copied instead.
+ #
+ # FileUtils.mv 'badname.rb', 'goodname.rb'
+ # FileUtils.mv 'stuff.rb', '/notexist/lib/ruby', :force => true # no error
+ #
+ # FileUtils.mv %w(junk.txt dust.txt), '/home/aamine/.trash/'
+ # FileUtils.mv Dir.glob('test*.rb'), 'test', :noop => true, :verbose => true
+ #
+ def mv(src, dest, options = {})
+ fu_check_options options, :force, :noop, :verbose, :secure
+ fu_output_message "mv#{options[:force] ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
+ return if options[:noop]
+ fu_each_src_dest(src, dest) do |s, d|
+ destent = Entry_.new(d, nil, true)
+ begin
+ if destent.exist?
+ if destent.directory?
+ raise Errno::EEXIST, dest
+ else
+ destent.remove_file if rename_cannot_overwrite_file?
+ end
+ end
+ begin
+ File.rename s, d
+ rescue Errno::EXDEV
+ copy_entry s, d, true
+ if options[:secure]
+ remove_entry_secure s, options[:force]
+ else
+ remove_entry s, options[:force]
+ end
+ end
+ rescue SystemCallError
+ raise unless options[:force]
+ end
+ end
+ end
+ module_function :mv
+
+ alias move mv
+ module_function :move
+
+ OPT_TABLE['mv'] =
+ OPT_TABLE['move'] = %w[force noop verbose secure]
+
+ def rename_cannot_overwrite_file? #:nodoc:
+ /djgpp|cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
+ end
+ private_module_function :rename_cannot_overwrite_file?
+
+ #
+ # Options: force noop verbose
+ #
+ # Remove file(s) specified in +list+. This method cannot remove directories.
+ # All StandardErrors are ignored when the :force option is set.
+ #
+ # FileUtils.rm %w( junk.txt dust.txt )
+ # FileUtils.rm Dir.glob('*.so')
+ # FileUtils.rm 'NotExistFile', :force => true # never raises exception
+ #
+ def rm(list, options = {})
+ fu_check_options options, :force, :noop, :verbose
+ list = fu_list(list)
+ fu_output_message "rm#{options[:force] ? ' -f' : ''} #{list.join ' '}" if options[:verbose]
+ return if options[:noop]
+
+ list.each do |path|
+ remove_file path, options[:force]
+ end
+ end
+ module_function :rm
+
+ alias remove rm
+ module_function :remove
+
+ OPT_TABLE['rm'] =
+ OPT_TABLE['remove'] = %w( noop verbose force )
+
+ #
+ # Options: noop verbose
+ #
+ # Equivalent to
+ #
+ # #rm(list, :force => true)
+ #
+ def rm_f(list, options = {})
+ fu_check_options options, :noop, :verbose
+ options = options.dup
+ options[:force] = true
+ rm list, options
+ end
+ module_function :rm_f
+
+ alias safe_unlink rm_f
+ module_function :safe_unlink
+
+ OPT_TABLE['rm_f'] =
+ OPT_TABLE['safe_unlink'] = %w( noop verbose )
+
+ #
+ # Options: force noop verbose secure
+ #
+ # remove files +list+[0] +list+[1]... If +list+[n] is a directory,
+ # removes its all contents recursively. This method ignores
+ # StandardError when :force option is set.
+ #
+ # FileUtils.rm_r Dir.glob('/tmp/*')
+ # FileUtils.rm_r '/', :force => true # :-)
+ #
+ # WARNING: This method causes local vulnerability
+ # if one of parent directories or removing directory tree are world
+ # writable (including /tmp, whose permission is 1777), and the current
+ # process has strong privilege such as Unix super user (root), and the
+ # system has symbolic link. For secure removing, read the documentation
+ # of #remove_entry_secure carefully, and set :secure option to true.
+ # Default is :secure=>false.
+ #
+ # NOTE: This method calls #remove_entry_secure if :secure option is set.
+ # See also #remove_entry_secure.
+ #
+ def rm_r(list, options = {})
+ fu_check_options options, :force, :noop, :verbose, :secure
+ # options[:secure] = true unless options.key?(:secure)
+ list = fu_list(list)
+ fu_output_message "rm -r#{options[:force] ? 'f' : ''} #{list.join ' '}" if options[:verbose]
+ return if options[:noop]
+ list.each do |path|
+ if options[:secure]
+ remove_entry_secure path, options[:force]
+ else
+ remove_entry path, options[:force]
+ end
+ end
+ end
+ module_function :rm_r
+
+ OPT_TABLE['rm_r'] = %w( noop verbose force secure )
+
+ #
+ # Options: noop verbose secure
+ #
+ # Equivalent to
+ #
+ # #rm_r(list, :force => true)
+ #
+ # WARNING: This method causes local vulnerability.
+ # Read the documentation of #rm_r first.
+ #
+ def rm_rf(list, options = {})
+ fu_check_options options, :noop, :verbose, :secure
+ options = options.dup
+ options[:force] = true
+ rm_r list, options
+ end
+ module_function :rm_rf
+
+ alias rmtree rm_rf
+ module_function :rmtree
+
+ OPT_TABLE['rm_rf'] =
+ OPT_TABLE['rmtree'] = %w( noop verbose secure )
+
+ #
+ # This method removes a file system entry +path+. +path+ shall be a
+ # regular file, a directory, or something. If +path+ is a directory,
+ # remove it recursively. This method is required to avoid TOCTTOU
+ # (time-of-check-to-time-of-use) local security vulnerability of #rm_r.
+ # #rm_r causes security hole when:
+ #
+ # * Parent directory is world writable (including /tmp).
+ # * Removing directory tree includes world writable directory.
+ # * The system has symbolic link.
+ #
+ # To avoid this security hole, this method applies special preprocess.
+ # If +path+ is a directory, this method chown(2) and chmod(2) all
+ # removing directories. This requires the current process is the
+ # owner of the removing whole directory tree, or is the super user (root).
+ #
+ # WARNING: You must ensure that *ALL* parent directories are not
+ # world writable. Otherwise this method does not work.
+ # Only exception is temporary directory like /tmp and /var/tmp,
+ # whose permission is 1777.
+ #
+ # WARNING: Only the owner of the removing directory tree, or Unix super
+ # user (root) should invoke this method. Otherwise this method does not
+ # work.
+ #
+ # For details of this security vulnerability, see Perl's case:
+ #
+ # http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448
+ # http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452
+ #
+ # For fileutils.rb, this vulnerability is reported in [ruby-dev:26100].
+ #
+ def remove_entry_secure(path, force = false)
+ unless fu_have_symlink?
+ remove_entry path, force
+ return
+ end
+ fullpath = File.expand_path(path)
+ st = File.lstat(fullpath)
+ unless st.directory?
+ File.unlink fullpath
+ return
+ end
+ # is a directory.
+ parent_st = File.stat(File.dirname(fullpath))
+ unless fu_world_writable?(parent_st)
+ remove_entry path, force
+ return
+ end
+ unless parent_st.sticky?
+ raise ArgumentError, "parent directory is world writable, FileUtils#remove_entry_secure does not work; abort: #{path.inspect} (parent directory mode #{'%o' % parent_st.mode})"
+ end
+ # freeze tree root
+ euid = Process.euid
+ File.open(fullpath + '/.') {|f|
+ unless fu_stat_identical_entry?(st, f.stat)
+ # symlink (TOC-to-TOU attack?)
+ File.unlink fullpath
+ return
+ end
+ f.chown euid, -1
+ f.chmod 0700
+ }
+ # ---- tree root is frozen ----
+ root = Entry_.new(path)
+ root.preorder_traverse do |ent|
+ if ent.directory?
+ ent.chown euid, -1
+ ent.chmod 0700
+ end
+ end
+ root.postorder_traverse do |ent|
+ begin
+ ent.remove
+ rescue
+ raise unless force
+ end
+ end
+ rescue
+ raise unless force
+ end
+ module_function :remove_entry_secure
+
+ def fu_world_writable?(st)
+ (st.mode & 0002) != 0
+ end
+ private_module_function :fu_world_writable?
+
+ def fu_have_symlink? #:nodoc
+ File.symlink nil, nil
+ rescue NotImplementedError
+ return false
+ rescue
+ return true
+ end
+ private_module_function :fu_have_symlink?
+
+ def fu_stat_identical_entry?(a, b) #:nodoc:
+ a.dev == b.dev and a.ino == b.ino
+ end
+ private_module_function :fu_stat_identical_entry?
+
+ #
+ # This method removes a file system entry +path+.
+ # +path+ might be a regular file, a directory, or something.
+ # If +path+ is a directory, remove it recursively.
+ #
+ # See also #remove_entry_secure.
+ #
+ def remove_entry(path, force = false)
+ Entry_.new(path).postorder_traverse do |ent|
+ begin
+ ent.remove
+ rescue
+ raise unless force
+ end
+ end
+ rescue
+ raise unless force
+ end
+ module_function :remove_entry
+
+ #
+ # Removes a file +path+.
+ # This method ignores StandardError if +force+ is true.
+ #
+ def remove_file(path, force = false)
+ Entry_.new(path).remove_file
+ rescue
+ raise unless force
+ end
+ module_function :remove_file
+
+ #
+ # Removes a directory +dir+ and its contents recursively.
+ # This method ignores StandardError if +force+ is true.
+ #
+ def remove_dir(path, force = false)
+ remove_entry path, force # FIXME?? check if it is a directory
+ end
+ module_function :remove_dir
+
+ #
+ # Returns true if the contents of a file A and a file B are identical.
+ #
+ # FileUtils.compare_file('somefile', 'somefile') #=> true
+ # FileUtils.compare_file('/bin/cp', '/bin/mv') #=> maybe false
+ #
+ def compare_file(a, b)
+ return false unless File.size(a) == File.size(b)
+ File.open(a, 'rb') {|fa|
+ File.open(b, 'rb') {|fb|
+ return compare_stream(fa, fb)
+ }
+ }
+ end
+ module_function :compare_file
+
+ alias identical? compare_file
+ alias cmp compare_file
+ module_function :identical?
+ module_function :cmp
+
+ #
+ # Returns true if the contents of a stream +a+ and +b+ are identical.
+ #
+ def compare_stream(a, b)
+ bsize = fu_stream_blksize(a, b)
+ sa = sb = nil
+ while sa == sb
+ sa = a.read(bsize)
+ sb = b.read(bsize)
+ unless sa and sb
+ if sa.nil? and sb.nil?
+ return true
+ end
+ end
+ end
+ false
+ end
+ module_function :compare_stream
+
+ #
+ # Options: mode noop verbose
+ #
+ # If +src+ is not same as +dest+, copies it and changes the permission
+ # mode to +mode+. If +dest+ is a directory, destination is +dest+/+src+.
+ #
+ # FileUtils.install 'ruby', '/usr/local/bin/ruby', :mode => 0755, :verbose => true
+ # FileUtils.install 'lib.rb', '/usr/local/lib/ruby/site_ruby', :verbose => true
+ #
+ def install(src, dest, options = {})
+ fu_check_options options, :mode, :preserve, :noop, :verbose
+ fu_output_message "install -c#{options[:preserve] && ' -p'}#{options[:mode] ? (' -m 0%o' % options[:mode]) : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
+ return if options[:noop]
+ fu_each_src_dest(src, dest) do |s, d|
+ unless File.exist?(d) and compare_file(s, d)
+ remove_file d, true
+ st = File.stat(s) if options[:preserve]
+ copy_file s, d
+ File.utime st.atime, st.mtime, d if options[:preserve]
+ File.chmod options[:mode], d if options[:mode]
+ end
+ end
+ end
+ module_function :install
+
+ OPT_TABLE['install'] = %w( noop verbose preserve mode )
+
+ #
+ # Options: noop verbose
+ #
+ # Changes permission bits on the named files (in +list+) to the bit pattern
+ # represented by +mode+.
+ #
+ # FileUtils.chmod 0755, 'somecommand'
+ # FileUtils.chmod 0644, %w(my.rb your.rb his.rb her.rb)
+ # FileUtils.chmod 0755, '/usr/bin/ruby', :verbose => true
+ #
+ def chmod(mode, list, options = {})
+ fu_check_options options, :noop, :verbose
+ list = fu_list(list)
+ fu_output_message sprintf('chmod %o %s', mode, list.join(' ')) if options[:verbose]
+ return if options[:noop]
+ list.each do |path|
+ Entry_.new(path).chmod mode
+ end
+ end
+ module_function :chmod
+
+ OPT_TABLE['chmod'] = %w( noop verbose )
+
+ #
+ # Options: noop verbose force
+ #
+ # Changes permission bits on the named files (in +list+)
+ # to the bit pattern represented by +mode+.
+ #
+ # FileUtils.chmod_R 0700, "/tmp/app.#{$$}"
+ #
+ def chmod_R(mode, list, options = {})
+ fu_check_options options, :noop, :verbose, :force
+ list = fu_list(list)
+ fu_output_message sprintf('chmod -R%s %o %s',
+ (options[:force] ? 'f' : ''),
+ mode, list.join(' ')) if options[:verbose]
+ return if options[:noop]
+ list.each do |root|
+ Entry_.new(root).traverse do |ent|
+ begin
+ ent.chmod mode
+ rescue
+ raise unless options[:force]
+ end
+ end
+ end
+ end
+ module_function :chmod_R
+
+ OPT_TABLE['chmod_R'] = %w( noop verbose )
+
+ #
+ # Options: noop verbose
+ #
+ # Changes owner and group on the named files (in +list+)
+ # to the user +user+ and the group +group+. +user+ and +group+
+ # may be an ID (Integer/String) or a name (String).
+ # If +user+ or +group+ is nil, this method does not change
+ # the attribute.
+ #
+ # FileUtils.chown 'root', 'staff', '/usr/local/bin/ruby'
+ # FileUtils.chown nil, 'bin', Dir.glob('/usr/bin/*'), :verbose => true
+ #
+ def chown(user, group, list, options = {})
+ fu_check_options options, :noop, :verbose
+ list = fu_list(list)
+ fu_output_message sprintf('chown %s%s',
+ [user,group].compact.join(':') + ' ',
+ list.join(' ')) if options[:verbose]
+ return if options[:noop]
+ uid = fu_get_uid(user)
+ gid = fu_get_gid(group)
+ list.each do |path|
+ Entry_.new(path).chown uid, gid
+ end
+ end
+ module_function :chown
+
+ OPT_TABLE['chown'] = %w( noop verbose )
+
+ #
+ # Options: noop verbose force
+ #
+ # Changes owner and group on the named files (in +list+)
+ # to the user +user+ and the group +group+ recursively.
+ # +user+ and +group+ may be an ID (Integer/String) or
+ # a name (String). If +user+ or +group+ is nil, this
+ # method does not change the attribute.
+ #
+ # FileUtils.chown_R 'www', 'www', '/var/www/htdocs'
+ # FileUtils.chown_R 'cvs', 'cvs', '/var/cvs', :verbose => true
+ #
+ def chown_R(user, group, list, options = {})
+ fu_check_options options, :noop, :verbose, :force
+ list = fu_list(list)
+ fu_output_message sprintf('chown -R%s %s%s',
+ (options[:force] ? 'f' : ''),
+ [user,group].compact.join(':') + ' ',
+ list.join(' ')) if options[:verbose]
+ return if options[:noop]
+ uid = fu_get_uid(user)
+ gid = fu_get_gid(group)
+ return unless uid or gid
+ list.each do |root|
+ Entry_.new(root).traverse do |ent|
+ begin
+ ent.chown uid, gid
+ rescue
+ raise unless options[:force]
+ end
+ end
+ end
+ end
+ module_function :chown_R
+
+ OPT_TABLE['chown_R'] = %w( noop verbose )
+
+ begin
+ require 'etc'
+
+ def fu_get_uid(user) #:nodoc:
+ return nil unless user
+ user = user.to_s
+ if /\A\d+\z/ =~ user
+ then user.to_i
+ else Etc.getpwnam(user).uid
+ end
+ end
+ private_module_function :fu_get_uid
+
+ def fu_get_gid(group) #:nodoc:
+ return nil unless group
+ if /\A\d+\z/ =~ group
+ then group.to_i
+ else Etc.getgrnam(group).gid
+ end
+ end
+ private_module_function :fu_get_gid
+
+ rescue LoadError
+ # need Win32 support???
+
+ def fu_get_uid(user) #:nodoc:
+ user # FIXME
+ end
+ private_module_function :fu_get_uid
+
+ def fu_get_gid(group) #:nodoc:
+ group # FIXME
+ end
+ private_module_function :fu_get_gid
+ end
+
+ #
+ # Options: noop verbose
+ #
+ # Updates modification time (mtime) and access time (atime) of file(s) in
+ # +list+. Files are created if they don't exist.
+ #
+ # FileUtils.touch 'timestamp'
+ # FileUtils.touch Dir.glob('*.c'); system 'make'
+ #
+ def touch(list, options = {})
+ fu_check_options options, :noop, :verbose
+ list = fu_list(list)
+ fu_output_message "touch #{list.join ' '}" if options[:verbose]
+ return if options[:noop]
+ t = Time.now
+ list.each do |path|
+ begin
+ File.utime(t, t, path)
+ rescue Errno::ENOENT
+ File.open(path, 'a') {
+ ;
+ }
+ end
+ end
+ end
+ module_function :touch
+
+ OPT_TABLE['touch'] = %w( noop verbose )
+
+ private
+
+ module StreamUtils_
+ private
+
+ def fu_windows?
+ /mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
+ end
+
+ def fu_copy_stream0(src, dest, blksize) #:nodoc:
+ # FIXME: readpartial?
+ while s = src.read(blksize)
+ dest.write s
+ end
+ end
+
+ def fu_stream_blksize(*streams)
+ streams.each do |s|
+ next unless s.respond_to?(:stat)
+ size = fu_blksize(s.stat)
+ return size if size
+ end
+ fu_default_blksize()
+ end
+
+ def fu_blksize(st)
+ s = st.blksize
+ return nil unless s
+ return nil if s == 0
+ s
+ end
+
+ def fu_default_blksize
+ 1024
+ end
+ end
+
+ include StreamUtils_
+ extend StreamUtils_
+
+ class Entry_ #:nodoc: internal use only
+ include StreamUtils_
+
+ def initialize(a, b = nil, deref = false)
+ @prefix = @rel = @path = nil
+ if b
+ @prefix = a
+ @rel = b
+ else
+ @path = a
+ end
+ @deref = deref
+ @stat = nil
+ @lstat = nil
+ end
+
+ def inspect
+ "\#<#{self.class} #{path()}>"
+ end
+
+ def path
+ if @path
+ @path.to_str
+ else
+ join(@prefix, @rel)
+ end
+ end
+
+ def prefix
+ @prefix || @path
+ end
+
+ def rel
+ @rel
+ end
+
+ def dereference?
+ @deref
+ end
+
+ def exist?
+ lstat! ? true : false
+ end
+
+ def file?
+ s = lstat!
+ s and s.file?
+ end
+
+ def directory?
+ s = lstat!
+ s and s.directory?
+ end
+
+ def symlink?
+ s = lstat!
+ s and s.symlink?
+ end
+
+ def chardev?
+ s = lstat!
+ s and s.chardev?
+ end
+
+ def blockdev?
+ s = lstat!
+ s and s.blockdev?
+ end
+
+ def socket?
+ s = lstat!
+ s and s.socket?
+ end
+
+ def pipe?
+ s = lstat!
+ s and s.pipe?
+ end
+
+ S_IF_DOOR = 0xD000
+
+ def door?
+ s = lstat!
+ s and (s.mode & 0xF000 == S_IF_DOOR)
+ end
+
+ def entries
+ Dir.entries(path())\
+ .reject {|n| n == '.' or n == '..' }\
+ .map {|n| Entry_.new(prefix(), join(rel(), n.untaint)) }
+ end
+
+ def stat
+ return @stat if @stat
+ if lstat() and lstat().symlink?
+ @stat = File.stat(path())
+ else
+ @stat = lstat()
+ end
+ @stat
+ end
+
+ def stat!
+ return @stat if @stat
+ if lstat! and lstat!.symlink?
+ @stat = File.stat(path())
+ else
+ @stat = lstat!
+ end
+ @stat
+ rescue SystemCallError
+ nil
+ end
+
+ def lstat
+ if dereference?
+ @lstat ||= File.stat(path())
+ else
+ @lstat ||= File.lstat(path())
+ end
+ end
+
+ def lstat!
+ lstat()
+ rescue SystemCallError
+ nil
+ end
+
+ def chmod(mode)
+ if symlink?
+ File.lchmod mode, path() if have_lchmod?
+ else
+ File.chmod mode, path()
+ end
+ end
+
+ def chown(uid, gid)
+ if symlink?
+ File.lchown uid, gid, path() if have_lchown?
+ else
+ File.chown uid, gid, path()
+ end
+ end
+
+ def copy(dest)
+ case
+ when file?
+ copy_file dest
+ when directory?
+ begin
+ Dir.mkdir dest
+ rescue
+ raise unless File.directory?(dest)
+ end
+ when symlink?
+ File.symlink File.readlink(path()), dest
+ when chardev?
+ raise "cannot handle device file" unless File.respond_to?(:mknod)
+ mknod dest, ?c, 0666, lstat().rdev
+ when blockdev?
+ raise "cannot handle device file" unless File.respond_to?(:mknod)
+ mknod dest, ?b, 0666, lstat().rdev
+ when socket?
+ raise "cannot handle socket" unless File.respond_to?(:mknod)
+ mknod dest, nil, lstat().mode, 0
+ when pipe?
+ raise "cannot handle FIFO" unless File.respond_to?(:mkfifo)
+ mkfifo dest, 0666
+ when door?
+ raise "cannot handle door: #{path()}"
+ else
+ raise "unknown file type: #{path()}"
+ end
+ end
+
+ def copy_file(dest)
+ st = stat()
+ File.open(path(), 'rb') {|r|
+ File.open(dest, 'wb', st.mode) {|w|
+ fu_copy_stream0 r, w, (fu_blksize(st) || fu_default_blksize())
+ }
+ }
+ end
+
+ def copy_metadata(path)
+ st = lstat()
+ File.utime st.atime, st.mtime, path
+ begin
+ File.chown st.uid, st.gid, path
+ rescue Errno::EPERM
+ # clear setuid/setgid
+ File.chmod st.mode & 01777, path
+ else
+ File.chmod st.mode, path
+ end
+ end
+
+ def remove
+ if directory?
+ remove_dir1
+ else
+ remove_file
+ end
+ end
+
+ def remove_dir1
+ platform_support {
+ Dir.rmdir path().sub(%r</\z>, '')
+ }
+ end
+
+ def remove_file
+ platform_support {
+ File.unlink path
+ }
+ end
+
+ def platform_support
+ return yield unless fu_windows?
+ first_time_p = true
+ begin
+ yield
+ rescue Errno::ENOENT
+ raise
+ rescue => err
+ if first_time_p
+ first_time_p = false
+ begin
+ File.chmod 0700, path() # Windows does not have symlink
+ retry
+ rescue SystemCallError
+ end
+ end
+ raise err
+ end
+ end
+
+ def preorder_traverse
+ stack = [self]
+ while ent = stack.pop
+ yield ent
+ stack.concat ent.entries.reverse if ent.directory?
+ end
+ end
+
+ alias traverse preorder_traverse
+
+ def postorder_traverse
+ if directory?
+ entries().each do |ent|
+ ent.postorder_traverse do |e|
+ yield e
+ end
+ end
+ end
+ yield self
+ end
+
+ private
+
+ $fileutils_rb_have_lchmod = nil
+
+ def have_lchmod?
+ # This is not MT-safe, but it does not matter.
+ if $fileutils_rb_have_lchmod == nil
+ $fileutils_rb_have_lchmod = check_have_lchmod?
+ end
+ $fileutils_rb_have_lchmod
+ end
+
+ def check_have_lchmod?
+ return false unless File.respond_to?(:lchmod)
+ File.lchmod 0
+ return true
+ rescue NotImplementedError
+ return false
+ end
+
+ $fileutils_rb_have_lchown = nil
+
+ def have_lchown?
+ # This is not MT-safe, but it does not matter.
+ if $fileutils_rb_have_lchown == nil
+ $fileutils_rb_have_lchown = check_have_lchown?
+ end
+ $fileutils_rb_have_lchown
+ end
+
+ def check_have_lchown?
+ return false unless File.respond_to?(:lchown)
+ File.lchown nil, nil
+ return true
+ rescue NotImplementedError
+ return false
+ end
+
+ def join(dir, base)
+ return dir.to_str if not base or base == '.'
+ return base.to_str if not dir or dir == '.'
+ File.join(dir, base)
+ end
+ end # class Entry_
+
+ def fu_list(arg) #:nodoc:
+ [arg].flatten.map {|path| path.to_str }
+ end
+ private_module_function :fu_list
+
+ def fu_each_src_dest(src, dest) #:nodoc:
+ fu_each_src_dest0(src, dest) do |s, d|
+ raise ArgumentError, "same file: #{s} and #{d}" if fu_same?(s, d)
+ yield s, d
+ end
+ end
+ private_module_function :fu_each_src_dest
+
+ def fu_each_src_dest0(src, dest) #:nodoc:
+ if src.is_a?(Array)
+ src.each do |s|
+ s = s.to_str
+ yield s, File.join(dest, File.basename(s))
+ end
+ else
+ src = src.to_str
+ if File.directory?(dest)
+ yield src, File.join(dest, File.basename(src))
+ else
+ yield src, dest.to_str
+ end
+ end
+ end
+ private_module_function :fu_each_src_dest0
+
+ def fu_same?(a, b) #:nodoc:
+ if fu_have_st_ino?
+ st1 = File.stat(a)
+ st2 = File.stat(b)
+ st1.dev == st2.dev and st1.ino == st2.ino
+ else
+ File.expand_path(a) == File.expand_path(b)
+ end
+ rescue Errno::ENOENT
+ return false
+ end
+ private_module_function :fu_same?
+
+ def fu_have_st_ino? #:nodoc:
+ not fu_windows?
+ end
+ private_module_function :fu_have_st_ino?
+
+ def fu_check_options(options, *optdecl) #:nodoc:
+ h = options.dup
+ optdecl.each do |name|
+ h.delete name
+ end
+ raise ArgumentError, "no such option: #{h.keys.join(' ')}" unless h.empty?
+ end
+ private_module_function :fu_check_options
+
+ def fu_update_option(args, new) #:nodoc:
+ if args.last.is_a?(Hash)
+ args[-1] = args.last.dup.update(new)
+ else
+ args.push new
+ end
+ args
+ end
+ private_module_function :fu_update_option
+
+ @fileutils_output = $stderr
+ @fileutils_label = ''
+
+ def fu_output_message(msg) #:nodoc:
+ @fileutils_output ||= $stderr
+ @fileutils_label ||= ''
+ @fileutils_output.puts @fileutils_label + msg
+ end
+ private_module_function :fu_output_message
+
+ METHODS = singleton_methods() - ['private_module_function']
+
+ #
+ # Returns an Array of method names which have any options.
+ #
+ # p FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...]
+ #
+ def FileUtils.commands
+ OPT_TABLE.keys
+ end
+
+ #
+ # Returns an Array of option names.
+ #
+ # p FileUtils.options #=> ["noop", "force", "verbose", "preserve", "mode"]
+ #
+ def FileUtils.options
+ OPT_TABLE.values.flatten.uniq
+ end
+
+ #
+ # Returns true if the method +mid+ have an option +opt+.
+ #
+ # p FileUtils.have_option?(:cp, :noop) #=> true
+ # p FileUtils.have_option?(:rm, :force) #=> true
+ # p FileUtils.have_option?(:rm, :perserve) #=> false
+ #
+ def FileUtils.have_option?(mid, opt)
+ li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such method: #{mid}"
+ li.include?(opt.to_s)
+ end
+
+ #
+ # Returns an Array of option names of the method +mid+.
+ #
+ # p FileUtils.options(:rm) #=> ["noop", "verbose", "force"]
+ #
+ def FileUtils.options_of(mid)
+ OPT_TABLE[mid.to_s]
+ end
+
+ #
+ # Returns an Array of method names which have the option +opt+.
+ #
+ # p FileUtils.collect_method(:preserve) #=> ["cp", "cp_r", "copy", "install"]
+ #
+ def FileUtils.collect_method(opt)
+ OPT_TABLE.keys.select {|m| OPT_TABLE[m].include?(opt.to_s) }
+ end
+
+ #
+ # This module has all methods of FileUtils module, but it outputs messages
+ # before acting. This equates to passing the <tt>:verbose</tt> flag to
+ # methods in FileUtils.
+ #
+ module Verbose
+ include FileUtils
+ @fileutils_output = $stderr
+ @fileutils_label = ''
+ ::FileUtils.collect_method('verbose').each do |name|
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
+ def #{name}(*args)
+ super(*fu_update_option(args, :verbose => true))
+ end
+ private :#{name}
+ EOS
+ end
+ extend self
+ class << self
+ ::FileUtils::METHODS.each do |m|
+ public m
+ end
+ end
+ end
+
+ #
+ # This module has all methods of FileUtils module, but never changes
+ # files/directories. This equates to passing the <tt>:noop</tt> flag
+ # to methods in FileUtils.
+ #
+ module NoWrite
+ include FileUtils
+ @fileutils_output = $stderr
+ @fileutils_label = ''
+ ::FileUtils.collect_method('noop').each do |name|
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
+ def #{name}(*args)
+ super(*fu_update_option(args, :noop => true))
+ end
+ private :#{name}
+ EOS
+ end
+ extend self
+ class << self
+ ::FileUtils::METHODS.each do |m|
+ public m
+ end
+ end
+ end
+
+ #
+ # This module has all methods of FileUtils module, but never changes
+ # files/directories, with printing message before acting.
+ # This equates to passing the <tt>:noop</tt> and <tt>:verbose</tt> flag
+ # to methods in FileUtils.
+ #
+ module DryRun
+ include FileUtils
+ @fileutils_output = $stderr
+ @fileutils_label = ''
+ ::FileUtils.collect_method('noop').each do |name|
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
+ def #{name}(*args)
+ super(*fu_update_option(args, :noop => true, :verbose => true))
+ end
+ private :#{name}
+ EOS
+ end
+ extend self
+ class << self
+ ::FileUtils::METHODS.each do |m|
+ public m
+ end
+ end
+ end
+
+end
diff --git a/ruby_1_8_5/lib/finalize.rb b/ruby_1_8_5/lib/finalize.rb
new file mode 100644
index 0000000000..9b6b302cac
--- /dev/null
+++ b/ruby_1_8_5/lib/finalize.rb
@@ -0,0 +1,193 @@
+#--
+# finalizer.rb -
+# $Release Version: 0.3$
+# $Revision: 1.4 $
+# $Date: 1998/02/27 05:34:33 $
+# by Keiju ISHITSUKA
+#++
+#
+# Usage:
+#
+# add dependency R_method(obj, dependant)
+# add(obj, dependant, method = :finalize, *opt)
+# add_dependency(obj, dependant, method = :finalize, *opt)
+#
+# delete dependency R_method(obj, dependant)
+# delete(obj_or_id, dependant, method = :finalize)
+# delete_dependency(obj_or_id, dependant, method = :finalize)
+#
+# delete dependency R_*(obj, dependant)
+# delete_all_dependency(obj_or_id, dependant)
+#
+# delete dependency R_method(*, dependant)
+# delete_by_dependant(dependant, method = :finalize)
+#
+# delete dependency R_*(*, dependant)
+# delete_all_by_dependant(dependant)
+#
+# delete all dependency R_*(*, *)
+# delete_all
+#
+# finalize the dependant connected by dependency R_method(obj, dependtant).
+# finalize(obj_or_id, dependant, method = :finalize)
+# finalize_dependency(obj_or_id, dependant, method = :finalize)
+#
+# finalize all dependants connected by dependency R_*(obj, dependtant).
+# finalize_all_dependency(obj_or_id, dependant)
+#
+# finalize the dependant connected by dependency R_method(*, dependtant).
+# finalize_by_dependant(dependant, method = :finalize)
+#
+# finalize all dependants connected by dependency R_*(*, dependant).
+# finalize_all_by_dependant(dependant)
+#
+# finalize all dependency registered to the Finalizer.
+# finalize_all
+#
+# stop invoking Finalizer on GC.
+# safe{..}
+#
+
+module Finalizer
+ RCS_ID='-$Id: finalize.rb,v 1.4 1998/02/27 05:34:33 keiju Exp keiju $-'
+
+ class <<self
+ # @dependency: {id => [[dependant, method, *opt], ...], ...}
+
+ # add dependency R_method(obj, dependant)
+ def add_dependency(obj, dependant, method = :finalize, *opt)
+ ObjectSpace.call_finalizer(obj)
+ method = method.intern unless method.kind_of?(Integer)
+ assoc = [dependant, method].concat(opt)
+ if dep = @dependency[obj.object_id]
+ dep.push assoc
+ else
+ @dependency[obj.object_id] = [assoc]
+ end
+ end
+ alias add add_dependency
+
+ # delete dependency R_method(obj, dependant)
+ def delete_dependency(id, dependant, method = :finalize)
+ id = id.object_id unless id.kind_of?(Integer)
+ method = method.intern unless method.kind_of?(Integer)
+ for assoc in @dependency[id]
+ assoc.delete_if do
+ |d, m, *o|
+ d == dependant && m == method
+ end
+ @dependency.delete(id) if assoc.empty?
+ end
+ end
+ alias delete delete_dependency
+
+ # delete dependency R_*(obj, dependant)
+ def delete_all_dependency(id, dependant)
+ id = id.object_id unless id.kind_of?(Integer)
+ method = method.intern unless method.kind_of?(Integer)
+ for assoc in @dependency[id]
+ assoc.delete_if do
+ |d, m, *o|
+ d == dependant
+ end
+ @dependency.delete(id) if assoc.empty?
+ end
+ end
+
+ # delete dependency R_method(*, dependant)
+ def delete_by_dependant(dependant, method = :finalize)
+ method = method.intern unless method.kind_of?(Integer)
+ for id in @dependency.keys
+ delete(id, dependant, method)
+ end
+ end
+
+ # delete dependency R_*(*, dependant)
+ def delete_all_by_dependant(dependant)
+ for id in @dependency.keys
+ delete_all_dependency(id, dependant)
+ end
+ end
+
+ # finalize the depandant connected by dependency R_method(obj, dependtant)
+ def finalize_dependency(id, dependant, method = :finalize)
+ id = id.object_id unless id.kind_of?(Integer)
+ method = method.intern unless method.kind_of?(Integer)
+ for assocs in @dependency[id]
+ assocs.delete_if do
+ |d, m, *o|
+ d.send(m, id, *o) if ret = d == dependant && m == method
+ ret
+ end
+ @dependency.delete(id) if assoc.empty?
+ end
+ end
+ alias finalize finalize_dependency
+
+ # finalize all dependants connected by dependency R_*(obj, dependtant)
+ def finalize_all_dependency(id, dependant)
+ id = id.object_id unless id.kind_of?(Integer)
+ method = method.intern unless method.kind_of?(Integer)
+ for assoc in @dependency[id]
+ assoc.delete_if do
+ |d, m, *o|
+ d.send(m, id, *o) if ret = d == dependant
+ end
+ @dependency.delete(id) if assoc.empty?
+ end
+ end
+
+ # finalize the dependant connected by dependency R_method(*, dependtant)
+ def finalize_by_dependant(dependant, method = :finalize)
+ method = method.intern unless method.kind_of?(Integer)
+ for id in @dependency.keys
+ finalize(id, dependant, method)
+ end
+ end
+
+ # finalize all dependants connected by dependency R_*(*, dependtant)
+ def finalize_all_by_dependant(dependant)
+ for id in @dependency.keys
+ finalize_all_dependency(id, dependant)
+ end
+ end
+
+ # finalize all dependants registered to the Finalizer.
+ def finalize_all
+ for id, assocs in @dependency
+ for dependant, method, *opt in assocs
+ dependant.send(method, id, *opt)
+ end
+ assocs.clear
+ end
+ end
+
+ # method to call finalize_* safely.
+ def safe
+ old_status = Thread.critical
+ Thread.critical = true
+ ObjectSpace.remove_finalizer(@proc)
+ begin
+ yield
+ ensure
+ ObjectSpace.add_finalizer(@proc)
+ Thread.critical = old_status
+ end
+ end
+
+ private
+
+ # registering function to ObjectSpace#add_finalizer
+ def final_of(id)
+ if assocs = @dependency.delete(id)
+ for dependant, method, *opt in assocs
+ dependant.send(method, id, *opt)
+ end
+ end
+ end
+
+ end
+ @dependency = Hash.new
+ @proc = proc{|id| final_of(id)}
+ ObjectSpace.add_finalizer(@proc)
+end
diff --git a/ruby_1_8_5/lib/find.rb b/ruby_1_8_5/lib/find.rb
new file mode 100644
index 0000000000..0d22dd62d6
--- /dev/null
+++ b/ruby_1_8_5/lib/find.rb
@@ -0,0 +1,79 @@
+#
+# find.rb: the Find module for processing all files under a given directory.
+#
+
+#
+# The +Find+ module supports the top-down traversal of a set of file paths.
+#
+# For example, to total the size of all files under your home directory,
+# ignoring anything in a "dot" directory (e.g. $HOME/.ssh):
+#
+# require 'find'
+#
+# total_size = 0
+#
+# Find.find(ENV["HOME"]) do |path|
+# if FileTest.directory?(path)
+# if File.basename(path)[0] == ?.
+# Find.prune # Don't look any further into this directory.
+# else
+# next
+# end
+# else
+# total_size += FileTest.size(path)
+# end
+# end
+#
+module Find
+
+ #
+ # Calls the associated block with the name of every file and directory listed
+ # as arguments, then recursively on their subdirectories, and so on.
+ #
+ # See the +Find+ module documentation for an example.
+ #
+ def find(*paths) # :yield: path
+ paths.collect!{|d| d.dup}
+ while file = paths.shift
+ catch(:prune) do
+ yield file.dup.taint
+ next unless File.exist? file
+ begin
+ if File.lstat(file).directory? then
+ d = Dir.open(file)
+ begin
+ for f in d
+ next if f == "." or f == ".."
+ if File::ALT_SEPARATOR and file =~ /^(?:[\/\\]|[A-Za-z]:[\/\\]?)$/ then
+ f = file + f
+ elsif file == "/" then
+ f = "/" + f
+ else
+ f = File.join(file, f)
+ end
+ paths.unshift f.untaint
+ end
+ ensure
+ d.close
+ end
+ end
+ rescue Errno::ENOENT, Errno::EACCES
+ end
+ end
+ end
+ end
+
+ #
+ # Skips the current file or directory, restarting the loop with the next
+ # entry. If the current file is a directory, that directory will not be
+ # recursively entered. Meaningful only within the block associated with
+ # Find::find.
+ #
+ # See the +Find+ module documentation for an example.
+ #
+ def prune
+ throw :prune
+ end
+
+ module_function :find, :prune
+end
diff --git a/ruby_1_8_5/lib/forwardable.rb b/ruby_1_8_5/lib/forwardable.rb
new file mode 100644
index 0000000000..4383ce9bbb
--- /dev/null
+++ b/ruby_1_8_5/lib/forwardable.rb
@@ -0,0 +1,218 @@
+# = forwardable - Support for the Delegation Pattern
+#
+# $Release Version: 1.1$
+# $Revision: 1.2.2.2 $
+# $Date: 2006/06/02 13:23:01 $
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
+#
+# Documentation by James Edward Gray II and Gavin Sinclair
+#
+# == Introduction
+#
+# This library allows you delegate method calls to an object, on a method by
+# method basis. You can use Forwardable to setup this delegation at the class
+# level, or SingleForwardable to handle it at the object level.
+#
+# == Notes
+#
+# Be advised, RDoc will not detect delegated methods.
+#
+# <b>forwardable.rb provides single-method delegation via the
+# def_delegator() and def_delegators() methods. For full-class
+# delegation via DelegateClass(), see delegate.rb.</b>
+#
+# == Examples
+#
+# === Forwardable
+#
+# Forwardable makes building a new class based on existing work, with a proper
+# interface, almost trivial. We want to rely on what has come before obviously,
+# but with delegation we can take just the methods we need and even rename them
+# as appropriate. In many cases this is preferable to inheritance, which gives
+# us the entire old interface, even if much of it isn't needed.
+#
+# class Queue
+# extend Forwardable
+#
+# def initialize
+# @q = [ ] # prepare delegate object
+# end
+#
+# # setup prefered interface, enq() and deq()...
+# def_delegator :@q, :push, :enq
+# def_delegator :@q, :shift, :deq
+#
+# # support some general Array methods that fit Queues well
+# def_delegators :@q, :clear, :first, :push, :shift, :size
+# end
+#
+# q = Queue.new
+# q.enq 1, 2, 3, 4, 5
+# q.push 6
+#
+# q.shift # => 1
+# while q.size > 0
+# puts q.deq
+# end
+#
+# q.enq "Ruby", "Perl", "Python"
+# puts q.first
+# q.clear
+# puts q.first
+#
+# <i>Prints:</i>
+#
+# 2
+# 3
+# 4
+# 5
+# 6
+# Ruby
+# nil
+#
+# === SingleForwardable
+#
+# printer = String.new
+# printer.extend SingleForwardable # prepare object for delegation
+# printer.def_delegator "STDOUT", "puts" # add delegation for STDOUT.puts()
+# printer.puts "Howdy!"
+#
+# <i>Prints:</i>
+#
+# Howdy!
+
+#
+# The Forwardable module provides delegation of specified
+# methods to a designated object, using the methods #def_delegator
+# and #def_delegators.
+#
+# For example, say you have a class RecordCollection which
+# contains an array <tt>@records</tt>. You could provide the lookup method
+# #record_number(), which simply calls #[] on the <tt>@records</tt>
+# array, like this:
+#
+# class RecordCollection
+# extend Forwardable
+# def_delegator :@records, :[], :record_number
+# end
+#
+# Further, if you wish to provide the methods #size, #<<, and #map,
+# all of which delegate to @records, this is how you can do it:
+#
+# class RecordCollection
+# # extend Forwardable, but we did that above
+# def_delegators :@records, :size, :<<, :map
+# end
+#
+# Also see the example at forwardable.rb.
+#
+module Forwardable
+
+ @debug = nil
+ class<<self
+ # force Forwardable to show up in stack backtraces of delegated methods
+ attr_accessor :debug
+ end
+
+ #
+ # Shortcut for defining multiple delegator methods, but with no
+ # provision for using a different name. The following two code
+ # samples have the same effect:
+ #
+ # def_delegators :@records, :size, :<<, :map
+ #
+ # def_delegator :@records, :size
+ # def_delegator :@records, :<<
+ # def_delegator :@records, :map
+ #
+ # See the examples at Forwardable and forwardable.rb.
+ #
+ def def_instance_delegators(accessor, *methods)
+ for method in methods
+ def_instance_delegator(accessor, method)
+ end
+ end
+
+ #
+ # Defines a method _method_ which delegates to _obj_ (i.e. it calls
+ # the method of the same name in _obj_). If _new_name_ is
+ # provided, it is used as the name for the delegate method.
+ #
+ # See the examples at Forwardable and forwardable.rb.
+ #
+ def def_instance_delegator(accessor, method, ali = method)
+ accessor = accessor.id2name if accessor.kind_of?(Integer)
+ method = method.id2name if method.kind_of?(Integer)
+ ali = ali.id2name if ali.kind_of?(Integer)
+
+ module_eval(<<-EOS, "(__FORWARDABLE__)", 1)
+ def #{ali}(*args, &block)
+ begin
+ #{accessor}.__send__(:#{method}, *args, &block)
+ rescue Exception
+ $@.delete_if{|s| /^\\(__FORWARDABLE__\\):/ =~ s} unless Forwardable::debug
+ Kernel::raise
+ end
+ end
+ EOS
+ end
+
+ alias def_delegators def_instance_delegators
+ alias def_delegator def_instance_delegator
+end
+
+#
+# The SingleForwardable module provides delegation of specified
+# methods to a designated object, using the methods #def_delegator
+# and #def_delegators. This module is similar to Forwardable, but it works on
+# objects themselves, instead of their defining classes.
+#
+# Also see the example at forwardable.rb.
+#
+module SingleForwardable
+ #
+ # Shortcut for defining multiple delegator methods, but with no
+ # provision for using a different name. The following two code
+ # samples have the same effect:
+ #
+ # single_forwardable.def_delegators :@records, :size, :<<, :map
+ #
+ # single_forwardable.def_delegator :@records, :size
+ # single_forwardable.def_delegator :@records, :<<
+ # single_forwardable.def_delegator :@records, :map
+ #
+ # See the example at forwardable.rb.
+ #
+ def def_singleton_delegators(accessor, *methods)
+ for method in methods
+ def_singleton_delegator(accessor, method)
+ end
+ end
+
+ #
+ # Defines a method _method_ which delegates to _obj_ (i.e. it calls
+ # the method of the same name in _obj_). If _new_name_ is
+ # provided, it is used as the name for the delegate method.
+ #
+ # See the example at forwardable.rb.
+ #
+ def def_singleton_delegator(accessor, method, ali = method)
+ accessor = accessor.id2name if accessor.kind_of?(Integer)
+ method = method.id2name if method.kind_of?(Integer)
+ ali = ali.id2name if ali.kind_of?(Integer)
+
+ instance_eval(<<-EOS, "(__FORWARDABLE__)", 1)
+ def #{ali}(*args, &block)
+ begin
+ #{accessor}.__send__(:#{method}, *args,&block)
+ rescue Exception
+ $@.delete_if{|s| /^\\(__FORWARDABLE__\\):/ =~ s} unless Forwardable::debug
+ Kernel::raise
+ end
+ end
+ EOS
+ end
+
+ alias def_delegators def_singleton_delegators
+ alias def_delegator def_singleton_delegator
+end
diff --git a/ruby_1_8_5/lib/ftools.rb b/ruby_1_8_5/lib/ftools.rb
new file mode 100644
index 0000000000..5f082331fb
--- /dev/null
+++ b/ruby_1_8_5/lib/ftools.rb
@@ -0,0 +1,261 @@
+#
+# = ftools.rb: Extra tools for the File class
+#
+# Author:: WATANABE, Hirofumi
+# Documentation:: Zachary Landau
+#
+# This library can be distributed under the terms of the Ruby license.
+# You can freely distribute/modify this library.
+#
+# It is included in the Ruby standard library.
+#
+# == Description
+#
+# ftools adds several (class, not instance) methods to the File class, for
+# copying, moving, deleting, installing, and comparing files, as well as
+# creating a directory path. See the File class for details.
+#
+# FileUtils contains all or nearly all the same functionality and more, and
+# is a recommended option over ftools
+#
+# When you
+#
+# require 'ftools'
+#
+# then the File class aquires some utility methods for copying, moving, and
+# deleting files, and more.
+#
+# See the method descriptions below, and consider using FileUtils as it is
+# more comprehensive.
+#
+class File
+end
+
+class << File
+
+ BUFSIZE = 8 * 1024
+
+ #
+ # If +to+ is a valid directory, +from+ will be appended to +to+, adding
+ # and escaping backslashes as necessary. Otherwise, +to+ will be returned.
+ # Useful for appending +from+ to +to+ only if the filename was not specified
+ # in +to+.
+ #
+ def catname(from, to)
+ if directory? to
+ join to.sub(%r([/\\]$), ''), basename(from)
+ else
+ to
+ end
+ end
+
+ #
+ # Copies a file +from+ to +to+. If +to+ is a directory, copies +from+
+ # to <tt>to/from</tt>.
+ #
+ def syscopy(from, to)
+ to = catname(from, to)
+
+ fmode = stat(from).mode
+ tpath = to
+ not_exist = !exist?(tpath)
+
+ from = open(from, "rb")
+ to = open(to, "wb")
+
+ begin
+ while true
+ to.syswrite from.sysread(BUFSIZE)
+ end
+ rescue EOFError
+ ret = true
+ rescue
+ ret = false
+ ensure
+ to.close
+ from.close
+ end
+ chmod(fmode, tpath) if not_exist
+ ret
+ end
+
+ #
+ # Copies a file +from+ to +to+ using #syscopy. If +to+ is a directory,
+ # copies +from+ to <tt>to/from</tt>. If +verbose+ is true, <tt>from -> to</tt>
+ # is printed.
+ #
+ def copy(from, to, verbose = false)
+ $stderr.print from, " -> ", catname(from, to), "\n" if verbose
+ syscopy from, to
+ end
+
+ alias cp copy
+
+ #
+ # Moves a file +from+ to +to+ using #syscopy. If +to+ is a directory,
+ # copies from +from+ to <tt>to/from</tt>. If +verbose+ is true, <tt>from ->
+ # to</tt> is printed.
+ #
+ def move(from, to, verbose = false)
+ to = catname(from, to)
+ $stderr.print from, " -> ", to, "\n" if verbose
+
+ if RUBY_PLATFORM =~ /djgpp|(cyg|ms|bcc)win|mingw/ and file? to
+ unlink to
+ end
+ fstat = stat(from)
+ begin
+ rename from, to
+ rescue
+ begin
+ symlink readlink(from), to and unlink from
+ rescue
+ from_stat = stat(from)
+ syscopy from, to and unlink from
+ utime(from_stat.atime, from_stat.mtime, to)
+ begin
+ chown(fstat.uid, fstat.gid, to)
+ rescue
+ end
+ end
+ end
+ end
+
+ alias mv move
+
+ #
+ # Returns +true+ if and only if the contents of files +from+ and +to+ are
+ # identical. If +verbose+ is +true+, <tt>from <=> to</tt> is printed.
+ #
+ def compare(from, to, verbose = false)
+ $stderr.print from, " <=> ", to, "\n" if verbose
+
+ return false if stat(from).size != stat(to).size
+
+ from = open(from, "rb")
+ to = open(to, "rb")
+
+ ret = false
+ fr = tr = ''
+
+ begin
+ while fr == tr
+ fr = from.read(BUFSIZE)
+ if fr
+ tr = to.read(fr.size)
+ else
+ ret = to.read(BUFSIZE)
+ ret = !ret || ret.length == 0
+ break
+ end
+ end
+ rescue
+ ret = false
+ ensure
+ to.close
+ from.close
+ end
+ ret
+ end
+
+ alias cmp compare
+
+ #
+ # Removes a list of files. Each parameter should be the name of the file to
+ # delete. If the last parameter isn't a String, verbose mode will be enabled.
+ # Returns the number of files deleted.
+ #
+ def safe_unlink(*files)
+ verbose = if files[-1].is_a? String then false else files.pop end
+ files.each do |file|
+ begin
+ unlink file
+ $stderr.print "removing ", file, "\n" if verbose
+ rescue Errno::EACCES # for Windows
+ continue if symlink? file
+ begin
+ mode = stat(file).mode
+ o_chmod mode | 0200, file
+ unlink file
+ $stderr.print "removing ", file, "\n" if verbose
+ rescue
+ o_chmod mode, file rescue nil
+ end
+ rescue
+ end
+ end
+ end
+
+ alias rm_f safe_unlink
+
+ #
+ # Creates a directory and all its parent directories.
+ # For example,
+ #
+ # File.makedirs '/usr/lib/ruby'
+ #
+ # causes the following directories to be made, if they do not exist.
+ # * /usr
+ # * /usr/lib
+ # * /usr/lib/ruby
+ #
+ # You can pass several directories, each as a parameter. If the last
+ # parameter isn't a String, verbose mode will be enabled.
+ #
+ def makedirs(*dirs)
+ verbose = if dirs[-1].is_a? String then false else dirs.pop end
+ mode = 0755
+ for dir in dirs
+ parent = dirname(dir)
+ next if parent == dir or directory? dir
+ makedirs parent unless directory? parent
+ $stderr.print "mkdir ", dir, "\n" if verbose
+ if basename(dir) != ""
+ begin
+ Dir.mkdir dir, mode
+ rescue SystemCallError
+ raise unless directory? dir
+ end
+ end
+ end
+ end
+
+ alias mkpath makedirs
+
+ alias o_chmod chmod
+
+ vsave, $VERBOSE = $VERBOSE, false
+
+ #
+ # Changes permission bits on +files+ to the bit pattern represented
+ # by +mode+. If the last parameter isn't a String, verbose mode will
+ # be enabled.
+ #
+ # File.chmod 0755, 'somecommand'
+ # File.chmod 0644, 'my.rb', 'your.rb', true
+ #
+ def chmod(mode, *files)
+ verbose = if files[-1].is_a? String then false else files.pop end
+ $stderr.printf "chmod %04o %s\n", mode, files.join(" ") if verbose
+ o_chmod mode, *files
+ end
+ $VERBOSE = vsave
+
+ #
+ # If +src+ is not the same as +dest+, copies it and changes the permission
+ # mode to +mode+. If +dest+ is a directory, destination is <tt>dest/src</tt>.
+ # If +mode+ is not set, default is used. If +verbose+ is set to true, the
+ # name of each file copied will be printed.
+ #
+ def install(from, to, mode = nil, verbose = false)
+ to = catname(from, to)
+ unless exist? to and cmp from, to
+ safe_unlink to if exist? to
+ cp from, to, verbose
+ chmod mode, to, verbose if mode
+ end
+ end
+
+end
+
+# vi:set sw=2:
diff --git a/ruby_1_8_5/lib/generator.rb b/ruby_1_8_5/lib/generator.rb
new file mode 100644
index 0000000000..21970981fc
--- /dev/null
+++ b/ruby_1_8_5/lib/generator.rb
@@ -0,0 +1,380 @@
+#!/usr/bin/env ruby
+#--
+# $Idaemons: /home/cvs/rb/generator.rb,v 1.8 2001/10/03 08:54:32 knu Exp $
+# $RoughId: generator.rb,v 1.10 2003/10/14 19:36:58 knu Exp $
+# $Id: generator.rb,v 1.2.2.2 2004/05/07 08:48:23 matz Exp $
+#++
+#
+# = generator.rb: convert an internal iterator to an external one
+#
+# Copyright (c) 2001,2003 Akinori MUSHA <knu@iDaemons.org>
+#
+# All rights reserved. You can redistribute and/or modify it under
+# the same terms as Ruby.
+#
+# == Overview
+#
+# This library provides the Generator class, which converts an
+# internal iterator (i.e. an Enumerable object) to an external
+# iterator. In that form, you can roll many iterators independently.
+#
+# The SyncEnumerator class, which is implemented using Generator,
+# makes it easy to roll many Enumerable objects synchronously.
+#
+# See the respective classes for examples of usage.
+
+
+#
+# Generator converts an internal iterator (i.e. an Enumerable object)
+# to an external iterator.
+#
+# Note that it is not very fast since it is implemented using
+# continuations, which are currently slow.
+#
+# == Example
+#
+# require 'generator'
+#
+# # Generator from an Enumerable object
+# g = Generator.new(['A', 'B', 'C', 'Z'])
+#
+# while g.next?
+# puts g.next
+# end
+#
+# # Generator from a block
+# g = Generator.new { |g|
+# for i in 'A'..'C'
+# g.yield i
+# end
+#
+# g.yield 'Z'
+# }
+#
+# # The same result as above
+# while g.next?
+# puts g.next
+# end
+#
+class Generator
+ include Enumerable
+
+ # Creates a new generator either from an Enumerable object or from a
+ # block.
+ #
+ # In the former, block is ignored even if given.
+ #
+ # In the latter, the given block is called with the generator
+ # itself, and expected to call the +yield+ method for each element.
+ def initialize(enum = nil, &block)
+ if enum
+ @block = proc { |g|
+ enum.each { |x| g.yield x }
+ }
+ else
+ @block = block
+ end
+
+ @index = 0
+ @queue = []
+ @cont_next = @cont_yield = @cont_endp = nil
+
+ if @cont_next = callcc { |c| c }
+ @block.call(self)
+
+ @cont_endp.call(nil) if @cont_endp
+ end
+
+ self
+ end
+
+ # Yields an element to the generator.
+ def yield(value)
+ if @cont_yield = callcc { |c| c }
+ @queue << value
+ @cont_next.call(nil)
+ end
+
+ self
+ end
+
+ # Returns true if the generator has reached the end.
+ def end?()
+ if @cont_endp = callcc { |c| c }
+ @cont_yield.nil? && @queue.empty?
+ else
+ @queue.empty?
+ end
+ end
+
+ # Returns true if the generator has not reached the end yet.
+ def next?()
+ !end?
+ end
+
+ # Returns the current index (position) counting from zero.
+ def index()
+ @index
+ end
+
+ # Returns the current index (position) counting from zero.
+ def pos()
+ @index
+ end
+
+ # Returns the element at the current position and moves forward.
+ def next()
+ if end?
+ raise EOFError, "no more elements available"
+ end
+
+ if @cont_next = callcc { |c| c }
+ @cont_yield.call(nil) if @cont_yield
+ end
+
+ @index += 1
+
+ @queue.shift
+ end
+
+ # Returns the element at the current position.
+ def current()
+ if @queue.empty?
+ raise EOFError, "no more elements available"
+ end
+
+ @queue.first
+ end
+
+ # Rewinds the generator.
+ def rewind()
+ initialize(nil, &@block) if @index.nonzero?
+
+ self
+ end
+
+ # Rewinds the generator and enumerates the elements.
+ def each
+ rewind
+
+ until end?
+ yield self.next
+ end
+
+ self
+ end
+end
+
+#
+# SyncEnumerator creates an Enumerable object from multiple Enumerable
+# objects and enumerates them synchronously.
+#
+# == Example
+#
+# require 'generator'
+#
+# s = SyncEnumerator.new([1,2,3], ['a', 'b', 'c'])
+#
+# # Yields [1, 'a'], [2, 'b'], and [3,'c']
+# s.each { |row| puts row.join(', ') }
+#
+class SyncEnumerator
+ include Enumerable
+
+ # Creates a new SyncEnumerator which enumerates rows of given
+ # Enumerable objects.
+ def initialize(*enums)
+ @gens = enums.map { |e| Generator.new(e) }
+ end
+
+ # Returns the number of enumerated Enumerable objects, i.e. the size
+ # of each row.
+ def size
+ @gens.size
+ end
+
+ # Returns the number of enumerated Enumerable objects, i.e. the size
+ # of each row.
+ def length
+ @gens.length
+ end
+
+ # Returns true if the given nth Enumerable object has reached the
+ # end. If no argument is given, returns true if any of the
+ # Enumerable objects has reached the end.
+ def end?(i = nil)
+ if i.nil?
+ @gens.detect { |g| g.end? } ? true : false
+ else
+ @gens[i].end?
+ end
+ end
+
+ # Enumerates rows of the Enumerable objects.
+ def each
+ @gens.each { |g| g.rewind }
+
+ loop do
+ count = 0
+
+ ret = @gens.map { |g|
+ if g.end?
+ count += 1
+ nil
+ else
+ g.next
+ end
+ }
+
+ if count == @gens.size
+ break
+ end
+
+ yield ret
+ end
+
+ self
+ end
+end
+
+if $0 == __FILE__
+ eval DATA.read, nil, $0, __LINE__+4
+end
+
+__END__
+
+require 'test/unit'
+
+class TC_Generator < Test::Unit::TestCase
+ def test_block1
+ g = Generator.new { |g|
+ # no yield's
+ }
+
+ assert_equal(0, g.pos)
+ assert_raises(EOFError) { g.current }
+ end
+
+ def test_block2
+ g = Generator.new { |g|
+ for i in 'A'..'C'
+ g.yield i
+ end
+
+ g.yield 'Z'
+ }
+
+ assert_equal(0, g.pos)
+ assert_equal('A', g.current)
+
+ assert_equal(true, g.next?)
+ assert_equal(0, g.pos)
+ assert_equal('A', g.current)
+ assert_equal(0, g.pos)
+ assert_equal('A', g.next)
+
+ assert_equal(1, g.pos)
+ assert_equal(true, g.next?)
+ assert_equal(1, g.pos)
+ assert_equal('B', g.current)
+ assert_equal(1, g.pos)
+ assert_equal('B', g.next)
+
+ assert_equal(g, g.rewind)
+
+ assert_equal(0, g.pos)
+ assert_equal('A', g.current)
+
+ assert_equal(true, g.next?)
+ assert_equal(0, g.pos)
+ assert_equal('A', g.current)
+ assert_equal(0, g.pos)
+ assert_equal('A', g.next)
+
+ assert_equal(1, g.pos)
+ assert_equal(true, g.next?)
+ assert_equal(1, g.pos)
+ assert_equal('B', g.current)
+ assert_equal(1, g.pos)
+ assert_equal('B', g.next)
+
+ assert_equal(2, g.pos)
+ assert_equal(true, g.next?)
+ assert_equal(2, g.pos)
+ assert_equal('C', g.current)
+ assert_equal(2, g.pos)
+ assert_equal('C', g.next)
+
+ assert_equal(3, g.pos)
+ assert_equal(true, g.next?)
+ assert_equal(3, g.pos)
+ assert_equal('Z', g.current)
+ assert_equal(3, g.pos)
+ assert_equal('Z', g.next)
+
+ assert_equal(4, g.pos)
+ assert_equal(false, g.next?)
+ assert_raises(EOFError) { g.next }
+ end
+
+ def test_each
+ a = [5, 6, 7, 8, 9]
+
+ g = Generator.new(a)
+
+ i = 0
+
+ g.each { |x|
+ assert_equal(a[i], x)
+
+ i += 1
+
+ break if i == 3
+ }
+
+ assert_equal(3, i)
+
+ i = 0
+
+ g.each { |x|
+ assert_equal(a[i], x)
+
+ i += 1
+ }
+
+ assert_equal(5, i)
+ end
+end
+
+class TC_SyncEnumerator < Test::Unit::TestCase
+ def test_each
+ r = ['a'..'f', 1..10, 10..20]
+ ra = r.map { |x| x.to_a }
+
+ a = (0...(ra.map {|x| x.size}.max)).map { |i| ra.map { |x| x[i] } }
+
+ s = SyncEnumerator.new(*r)
+
+ i = 0
+
+ s.each { |x|
+ assert_equal(a[i], x)
+
+ i += 1
+
+ break if i == 3
+ }
+
+ assert_equal(3, i)
+
+ i = 0
+
+ s.each { |x|
+ assert_equal(a[i], x)
+
+ i += 1
+ }
+
+ assert_equal(a.size, i)
+ end
+end
diff --git a/ruby_1_8_5/lib/getoptlong.rb b/ruby_1_8_5/lib/getoptlong.rb
new file mode 100644
index 0000000000..4d004419b1
--- /dev/null
+++ b/ruby_1_8_5/lib/getoptlong.rb
@@ -0,0 +1,621 @@
+#
+# GetoptLong for Ruby
+#
+# Copyright (C) 1998, 1999, 2000 Motoyuki Kasahara.
+#
+# You may redistribute and/or modify this library under the same license
+# terms as Ruby.
+#
+# See GetoptLong for documentation.
+#
+# Additional documents and the latest version of `getoptlong.rb' can be
+# found at http://www.sra.co.jp/people/m-kasahr/ruby/getoptlong/
+
+# The GetoptLong class allows you to parse command line options similarly to
+# the GNU getopt_long() C library call. Note, however, that GetoptLong is a
+# pure Ruby implementation.
+#
+# GetoptLong allows for POSIX-style options like <tt>--file</tt> as well
+# as single letter options like <tt>-f</tt>
+#
+# The empty option <tt>--</tt> (two minus symbols) is used to end option
+# processing. This can be particularly important if options have optional
+# arguments.
+#
+# Here is a simple example of usage:
+#
+# # == Synopsis
+# #
+# # hello: greets user, demonstrates command line parsing
+# #
+# # == Usage
+# #
+# # hello [OPTION] ... DIR
+# #
+# # -h, --help:
+# # show help
+# #
+# # --repeat x, -n x:
+# # repeat x times
+# #
+# # --name [name]:
+# # greet user by name, if name not supplied default is John
+# #
+# # DIR: The directory in which to issue the greeting.
+#
+# require 'getoptlong'
+# require 'rdoc/usage'
+#
+# opts = GetoptLong.new(
+# [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
+# [ '--repeat', '-n', GetoptLong::REQUIRED_ARGUMENT ],
+# [ '--name', GetoptLong::OPTIONAL_ARGUMENT ]
+# )
+#
+# dir = nil
+# name = nil
+# repetitions = 1
+# opts.each do |opt, arg|
+# case opt
+# when '--help'
+# RDoc::usage
+# when '--repeat'
+# repetitions = arg.to_i
+# when '--name'
+# if arg == ''
+# name = 'John'
+# else
+# name = arg
+# end
+# end
+# end
+#
+# if ARGV.length != 1
+# puts "Missing dir argument (try --help)"
+# exit 0
+# end
+#
+# dir = ARGV.shift
+#
+# Dir.chdir(dir)
+# for i in (1..repetitions)
+# print "Hello"
+# if name
+# print ", #{name}"
+# end
+# puts
+# end
+#
+# Example command line:
+#
+# hello -n 6 --name -- /tmp
+#
+class GetoptLong
+ #
+ # Orderings.
+ #
+ ORDERINGS = [REQUIRE_ORDER = 0, PERMUTE = 1, RETURN_IN_ORDER = 2]
+
+ #
+ # Argument flags.
+ #
+ ARGUMENT_FLAGS = [NO_ARGUMENT = 0, REQUIRED_ARGUMENT = 1,
+ OPTIONAL_ARGUMENT = 2]
+
+ #
+ # Status codes.
+ #
+ STATUS_YET, STATUS_STARTED, STATUS_TERMINATED = 0, 1, 2
+
+ #
+ # Error types.
+ #
+ class Error < StandardError; end
+ class AmbigousOption < Error; end
+ class NeedlessArgument < Error; end
+ class MissingArgument < Error; end
+ class InvalidOption < Error; end
+
+ #
+ # Set up option processing.
+ #
+ # The options to support are passed to new() as an array of arrays.
+ # Each sub-array contains any number of String option names which carry
+ # the same meaning, and one of the following flags:
+ #
+ # GetoptLong::NO_ARGUMENT :: Option does not take an argument.
+ #
+ # GetoptLong::REQUIRED_ARGUMENT :: Option always takes an argument.
+ #
+ # GetoptLong::OPTIONAL_ARGUMENT :: Option may or may not take an argument.
+ #
+ # The first option name is considered to be the preferred (canonical) name.
+ # Other than that, the elements of each sub-array can be in any order.
+ #
+ def initialize(*arguments)
+ #
+ # Current ordering.
+ #
+ if ENV.include?('POSIXLY_CORRECT')
+ @ordering = REQUIRE_ORDER
+ else
+ @ordering = PERMUTE
+ end
+
+ #
+ # Hash table of option names.
+ # Keys of the table are option names, and their values are canonical
+ # names of the options.
+ #
+ @canonical_names = Hash.new
+
+ #
+ # Hash table of argument flags.
+ # Keys of the table are option names, and their values are argument
+ # flags of the options.
+ #
+ @argument_flags = Hash.new
+
+ #
+ # Whether error messages are output to $deferr.
+ #
+ @quiet = FALSE
+
+ #
+ # Status code.
+ #
+ @status = STATUS_YET
+
+ #
+ # Error code.
+ #
+ @error = nil
+
+ #
+ # Error message.
+ #
+ @error_message = nil
+
+ #
+ # Rest of catenated short options.
+ #
+ @rest_singles = ''
+
+ #
+ # List of non-option-arguments.
+ # Append them to ARGV when option processing is terminated.
+ #
+ @non_option_arguments = Array.new
+
+ if 0 < arguments.length
+ set_options(*arguments)
+ end
+ end
+
+ #
+ # Set the handling of the ordering of options and arguments.
+ # A RuntimeError is raised if option processing has already started.
+ #
+ # The supplied value must be a member of GetoptLong::ORDERINGS. It alters
+ # the processing of options as follows:
+ #
+ # <b>REQUIRE_ORDER</b> :
+ #
+ # Options are required to occur before non-options.
+ #
+ # Processing of options ends as soon as a word is encountered that has not
+ # been preceded by an appropriate option flag.
+ #
+ # For example, if -a and -b are options which do not take arguments,
+ # parsing command line arguments of '-a one -b two' would result in
+ # 'one', '-b', 'two' being left in ARGV, and only ('-a', '') being
+ # processed as an option/arg pair.
+ #
+ # This is the default ordering, if the environment variable
+ # POSIXLY_CORRECT is set. (This is for compatibility with GNU getopt_long.)
+ #
+ # <b>PERMUTE</b> :
+ #
+ # Options can occur anywhere in the command line parsed. This is the
+ # default behavior.
+ #
+ # Every sequence of words which can be interpreted as an option (with or
+ # without argument) is treated as an option; non-option words are skipped.
+ #
+ # For example, if -a does not require an argument and -b optionally takes
+ # an argument, parsing '-a one -b two three' would result in ('-a','') and
+ # ('-b', 'two') being processed as option/arg pairs, and 'one','three'
+ # being left in ARGV.
+ #
+ # If the ordering is set to PERMUTE but the environment variable
+ # POSIXLY_CORRECT is set, REQUIRE_ORDER is used instead. This is for
+ # compatibility with GNU getopt_long.
+ #
+ # <b>RETURN_IN_ORDER</b> :
+ #
+ # All words on the command line are processed as options. Words not
+ # preceded by a short or long option flag are passed as arguments
+ # with an option of '' (empty string).
+ #
+ # For example, if -a requires an argument but -b does not, a command line
+ # of '-a one -b two three' would result in option/arg pairs of ('-a', 'one')
+ # ('-b', ''), ('', 'two'), ('', 'three') being processed.
+ #
+ def ordering=(ordering)
+ #
+ # The method is failed if option processing has already started.
+ #
+ if @status != STATUS_YET
+ set_error(ArgumentError, "argument error")
+ raise RuntimeError,
+ "invoke ordering=, but option processing has already started"
+ end
+
+ #
+ # Check ordering.
+ #
+ if !ORDERINGS.include?(ordering)
+ raise ArgumentError, "invalid ordering `#{ordering}'"
+ end
+ if ordering == PERMUTE && ENV.include?('POSIXLY_CORRECT')
+ @ordering = REQUIRE_ORDER
+ else
+ @ordering = ordering
+ end
+ end
+
+ #
+ # Return ordering.
+ #
+ attr_reader :ordering
+
+ #
+ # Set options. Takes the same argument as GetoptLong.new.
+ #
+ # Raises a RuntimeError if option processing has already started.
+ #
+ def set_options(*arguments)
+ #
+ # The method is failed if option processing has already started.
+ #
+ if @status != STATUS_YET
+ raise RuntimeError,
+ "invoke set_options, but option processing has already started"
+ end
+
+ #
+ # Clear tables of option names and argument flags.
+ #
+ @canonical_names.clear
+ @argument_flags.clear
+
+ arguments.each do |arg|
+ #
+ # Each argument must be an Array.
+ #
+ if !arg.is_a?(Array)
+ raise ArgumentError, "the option list contains non-Array argument"
+ end
+
+ #
+ # Find an argument flag and it set to `argument_flag'.
+ #
+ argument_flag = nil
+ arg.each do |i|
+ if ARGUMENT_FLAGS.include?(i)
+ if argument_flag != nil
+ raise ArgumentError, "too many argument-flags"
+ end
+ argument_flag = i
+ end
+ end
+ raise ArgumentError, "no argument-flag" if argument_flag == nil
+
+ canonical_name = nil
+ arg.each do |i|
+ #
+ # Check an option name.
+ #
+ next if i == argument_flag
+ begin
+ if !i.is_a?(String) || i !~ /^-([^-]|-.+)$/
+ raise ArgumentError, "an invalid option `#{i}'"
+ end
+ if (@canonical_names.include?(i))
+ raise ArgumentError, "option redefined `#{i}'"
+ end
+ rescue
+ @canonical_names.clear
+ @argument_flags.clear
+ raise
+ end
+
+ #
+ # Register the option (`i') to the `@canonical_names' and
+ # `@canonical_names' Hashes.
+ #
+ if canonical_name == nil
+ canonical_name = i
+ end
+ @canonical_names[i] = canonical_name
+ @argument_flags[i] = argument_flag
+ end
+ raise ArgumentError, "no option name" if canonical_name == nil
+ end
+ return self
+ end
+
+ #
+ # Set/Unset `quiet' mode.
+ #
+ attr_writer :quiet
+
+ #
+ # Return the flag of `quiet' mode.
+ #
+ attr_reader :quiet
+
+ #
+ # `quiet?' is an alias of `quiet'.
+ #
+ alias quiet? quiet
+
+ #
+ # Explicitly terminate option processing.
+ #
+ def terminate
+ return nil if @status == STATUS_TERMINATED
+ raise RuntimeError, "an error has occured" if @error != nil
+
+ @status = STATUS_TERMINATED
+ @non_option_arguments.reverse_each do |argument|
+ ARGV.unshift(argument)
+ end
+
+ @canonical_names = nil
+ @argument_flags = nil
+ @rest_singles = nil
+ @non_option_arguments = nil
+
+ return self
+ end
+
+ #
+ # Returns true if option processing has terminated, false otherwise.
+ #
+ def terminated?
+ return @status == STATUS_TERMINATED
+ end
+
+ #
+ # Set an error (protected).
+ #
+ def set_error(type, message)
+ $deferr.print("#{$0}: #{message}\n") if !@quiet
+
+ @error = type
+ @error_message = message
+ @canonical_names = nil
+ @argument_flags = nil
+ @rest_singles = nil
+ @non_option_arguments = nil
+
+ raise type, message
+ end
+ protected :set_error
+
+ #
+ # Examine whether an option processing is failed.
+ #
+ attr_reader :error
+
+ #
+ # `error?' is an alias of `error'.
+ #
+ alias error? error
+
+ # Return the appropriate error message in POSIX-defined format.
+ # If no error has occurred, returns nil.
+ #
+ def error_message
+ return @error_message
+ end
+
+ #
+ # Get next option name and its argument, as an Array of two elements.
+ #
+ # The option name is always converted to the first (preferred)
+ # name given in the original options to GetoptLong.new.
+ #
+ # Example: ['--option', 'value']
+ #
+ # Returns nil if the processing is complete (as determined by
+ # STATUS_TERMINATED).
+ #
+ def get
+ option_name, option_argument = nil, ''
+
+ #
+ # Check status.
+ #
+ return nil if @error != nil
+ case @status
+ when STATUS_YET
+ @status = STATUS_STARTED
+ when STATUS_TERMINATED
+ return nil
+ end
+
+ #
+ # Get next option argument.
+ #
+ if 0 < @rest_singles.length
+ argument = '-' + @rest_singles
+ elsif (ARGV.length == 0)
+ terminate
+ return nil
+ elsif @ordering == PERMUTE
+ while 0 < ARGV.length && ARGV[0] !~ /^-./
+ @non_option_arguments.push(ARGV.shift)
+ end
+ if ARGV.length == 0
+ terminate
+ return nil
+ end
+ argument = ARGV.shift
+ elsif @ordering == REQUIRE_ORDER
+ if (ARGV[0] !~ /^-./)
+ terminate
+ return nil
+ end
+ argument = ARGV.shift
+ else
+ argument = ARGV.shift
+ end
+
+ #
+ # Check the special argument `--'.
+ # `--' indicates the end of the option list.
+ #
+ if argument == '--' && @rest_singles.length == 0
+ terminate
+ return nil
+ end
+
+ #
+ # Check for long and short options.
+ #
+ if argument =~ /^(--[^=]+)/ && @rest_singles.length == 0
+ #
+ # This is a long style option, which start with `--'.
+ #
+ pattern = $1
+ if @canonical_names.include?(pattern)
+ option_name = pattern
+ else
+ #
+ # The option `option_name' is not registered in `@canonical_names'.
+ # It may be an abbreviated.
+ #
+ match_count = 0
+ @canonical_names.each_key do |key|
+ if key.index(pattern) == 0
+ option_name = key
+ match_count += 1
+ end
+ end
+ if 2 <= match_count
+ set_error(AmbigousOption, "option `#{argument}' is ambiguous")
+ elsif match_count == 0
+ set_error(InvalidOption, "unrecognized option `#{argument}'")
+ end
+ end
+
+ #
+ # Check an argument to the option.
+ #
+ if @argument_flags[option_name] == REQUIRED_ARGUMENT
+ if argument =~ /=(.*)$/
+ option_argument = $1
+ elsif 0 < ARGV.length
+ option_argument = ARGV.shift
+ else
+ set_error(MissingArgument,
+ "option `#{argument}' requires an argument")
+ end
+ elsif @argument_flags[option_name] == OPTIONAL_ARGUMENT
+ if argument =~ /=(.*)$/
+ option_argument = $1
+ elsif 0 < ARGV.length && ARGV[0] !~ /^-./
+ option_argument = ARGV.shift
+ else
+ option_argument = ''
+ end
+ elsif argument =~ /=(.*)$/
+ set_error(NeedlessArgument,
+ "option `#{option_name}' doesn't allow an argument")
+ end
+
+ elsif argument =~ /^(-(.))(.*)/
+ #
+ # This is a short style option, which start with `-' (not `--').
+ # Short options may be catenated (e.g. `-l -g' is equivalent to
+ # `-lg').
+ #
+ option_name, ch, @rest_singles = $1, $2, $3
+
+ if @canonical_names.include?(option_name)
+ #
+ # The option `option_name' is found in `@canonical_names'.
+ # Check its argument.
+ #
+ if @argument_flags[option_name] == REQUIRED_ARGUMENT
+ if 0 < @rest_singles.length
+ option_argument = @rest_singles
+ @rest_singles = ''
+ elsif 0 < ARGV.length
+ option_argument = ARGV.shift
+ else
+ # 1003.2 specifies the format of this message.
+ set_error(MissingArgument, "option requires an argument -- #{ch}")
+ end
+ elsif @argument_flags[option_name] == OPTIONAL_ARGUMENT
+ if 0 < @rest_singles.length
+ option_argument = @rest_singles
+ @rest_singles = ''
+ elsif 0 < ARGV.length && ARGV[0] !~ /^-./
+ option_argument = ARGV.shift
+ else
+ option_argument = ''
+ end
+ end
+ else
+ #
+ # This is an invalid option.
+ # 1003.2 specifies the format of this message.
+ #
+ if ENV.include?('POSIXLY_CORRECT')
+ set_error(InvalidOption, "illegal option -- #{ch}")
+ else
+ set_error(InvalidOption, "invalid option -- #{ch}")
+ end
+ end
+ else
+ #
+ # This is a non-option argument.
+ # Only RETURN_IN_ORDER falled into here.
+ #
+ return '', argument
+ end
+
+ return @canonical_names[option_name], option_argument
+ end
+
+ #
+ # `get_option' is an alias of `get'.
+ #
+ alias get_option get
+
+ # Iterator version of `get'.
+ #
+ # The block is called repeatedly with two arguments:
+ # The first is the option name.
+ # The second is the argument which followed it (if any).
+ # Example: ('--opt', 'value')
+ #
+ # The option name is always converted to the first (preferred)
+ # name given in the original options to GetoptLong.new.
+ #
+ def each
+ loop do
+ option_name, option_argument = get_option
+ break if option_name == nil
+ yield option_name, option_argument
+ end
+ end
+
+ #
+ # `each_option' is an alias of `each'.
+ #
+ alias each_option each
+end
diff --git a/ruby_1_8_5/lib/getopts.rb b/ruby_1_8_5/lib/getopts.rb
new file mode 100644
index 0000000000..0e21bf96c7
--- /dev/null
+++ b/ruby_1_8_5/lib/getopts.rb
@@ -0,0 +1,127 @@
+#
+# getopts.rb -
+# $Release Version: $
+# $Revision: 1.8.2.4 $
+# $Date: 2006/08/04 22:00:21 $
+# by Yasuo OHBA(SHL Japan Inc. Technology Dept.)
+#
+# --
+# this is obsolete; use getoptlong
+#
+# 2000-03-21
+# modified by Minero Aoki <aamine@dp.u-netsurf.ne.jp>
+#
+# 2002-03-05
+# rewritten by Akinori MUSHA <knu@ruby-lang.org>
+#
+
+warn "Warning:#{caller[0].sub(/:in `.*'\z/, '')}: getopts is deprecated after Ruby 1.8.1; use optparse instead" if caller[0] and $VERBOSE
+
+$RCS_ID=%q$Header: /src/ruby/lib/getopts.rb,v 1.8.2.4 2006/08/04 22:00:21 drbrain Exp $
+
+# getopts is obsolete. Use GetoptLong.
+
+def getopts(single_options, *options)
+ boolopts = {}
+ valopts = {}
+
+ #
+ # set defaults
+ #
+ single_options.scan(/.:?/) do |opt|
+ if opt.size == 1
+ boolopts[opt] = false
+ else
+ valopts[opt[0, 1]] = nil
+ end
+ end if single_options
+
+ options.each do |arg|
+ opt, val = arg.split(':', 2)
+
+ if val
+ valopts[opt] = val.empty? ? nil : val
+ else
+ boolopts[opt] = false
+ end
+ end
+
+ #
+ # scan
+ #
+ c = 0
+ argv = ARGV
+
+ while arg = argv.shift
+ case arg
+ when /\A--(.*)/
+ if $1.empty? # xinit -- -bpp 24
+ break
+ end
+
+ opt, val = $1.split('=', 2)
+
+ if opt.size == 1
+ argv.unshift arg
+ return nil
+ elsif valopts.key? opt # imclean --src +trash
+ valopts[opt] = val || argv.shift or return nil
+ elsif boolopts.key? opt # ruby --verbose
+ boolopts[opt] = true
+ else
+ argv.unshift arg
+ return nil
+ end
+
+ c += 1
+ when /\A-(.+)/
+ opts = $1
+
+ until opts.empty?
+ opt = opts.slice!(0, 1)
+
+ if valopts.key? opt
+ val = opts
+
+ if val.empty? # ruby -e 'p $:'
+ valopts[opt] = argv.shift or return nil
+ else # cc -ohello ...
+ valopts[opt] = val
+ end
+
+ c += 1
+ break
+ elsif boolopts.key? opt
+ boolopts[opt] = true # ruby -h
+ c += 1
+ else
+ argv.unshift arg
+ return nil
+ end
+ end
+ else
+ argv.unshift arg
+ break
+ end
+ end
+
+ #
+ # set
+ #
+ $OPT = {}
+
+ boolopts.each do |opt, val|
+ $OPT[opt] = val
+
+ sopt = opt.gsub(/[^A-Za-z0-9_]/, '_')
+ eval "$OPT_#{sopt} = val"
+ end
+ valopts.each do |opt, val|
+ $OPT[opt] = val
+
+ sopt = opt.gsub(/[^A-Za-z0-9_]/, '_')
+ eval "$OPT_#{sopt} = val"
+ end
+
+ c
+end
diff --git a/ruby_1_8_5/lib/gserver.rb b/ruby_1_8_5/lib/gserver.rb
new file mode 100644
index 0000000000..eb5f31b7b3
--- /dev/null
+++ b/ruby_1_8_5/lib/gserver.rb
@@ -0,0 +1,253 @@
+#
+# Copyright (C) 2001 John W. Small All Rights Reserved
+#
+# Author:: John W. Small
+# Documentation:: Gavin Sinclair
+# Licence:: Freeware.
+#
+# See the class GServer for documentation.
+#
+
+require "socket"
+require "thread"
+
+#
+# GServer implements a generic server, featuring thread pool management,
+# simple logging, and multi-server management. See HttpServer in
+# <tt>xmlrpc/httpserver.rb</tt> in the Ruby standard library for an example of
+# GServer in action.
+#
+# Any kind of application-level server can be implemented using this class.
+# It accepts multiple simultaneous connections from clients, up to an optional
+# maximum number. Several _services_ (i.e. one service per TCP port) can be
+# run simultaneously, and stopped at any time through the class method
+# <tt>GServer.stop(port)</tt>. All the threading issues are handled, saving
+# you the effort. All events are optionally logged, but you can provide your
+# own event handlers if you wish.
+#
+# === Example
+#
+# Using GServer is simple. Below we implement a simple time server, run it,
+# query it, and shut it down. Try this code in +irb+:
+#
+# require 'gserver'
+#
+# #
+# # A server that returns the time in seconds since 1970.
+# #
+# class TimeServer < GServer
+# def initialize(port=10001, *args)
+# super(port, *args)
+# end
+# def serve(io)
+# io.puts(Time.now.to_i)
+# end
+# end
+#
+# # Run the server with logging enabled (it's a separate thread).
+# server = TimeServer.new
+# server.audit = true # Turn logging on.
+# server.start
+#
+# # *** Now point your browser to http://localhost:10001 to see it working ***
+#
+# # See if it's still running.
+# GServer.in_service?(10001) # -> true
+# server.stopped? # -> false
+#
+# # Shut the server down gracefully.
+# server.shutdown
+#
+# # Alternatively, stop it immediately.
+# GServer.stop(10001)
+# # or, of course, "server.stop".
+#
+# All the business of accepting connections and exception handling is taken
+# care of. All we have to do is implement the method that actually serves the
+# client.
+#
+# === Advanced
+#
+# As the example above shows, the way to use GServer is to subclass it to
+# create a specific server, overriding the +serve+ method. You can override
+# other methods as well if you wish, perhaps to collect statistics, or emit
+# more detailed logging.
+#
+# connecting
+# disconnecting
+# starting
+# stopping
+#
+# The above methods are only called if auditing is enabled.
+#
+# You can also override +log+ and +error+ if, for example, you wish to use a
+# more sophisticated logging system.
+#
+class GServer
+
+ DEFAULT_HOST = "127.0.0.1"
+
+ def serve(io)
+ end
+
+ @@services = {} # Hash of opened ports, i.e. services
+ @@servicesMutex = Mutex.new
+
+ def GServer.stop(port, host = DEFAULT_HOST)
+ @@servicesMutex.synchronize {
+ @@services[host][port].stop
+ }
+ end
+
+ def GServer.in_service?(port, host = DEFAULT_HOST)
+ @@services.has_key?(host) and
+ @@services[host].has_key?(port)
+ end
+
+ def stop
+ @connectionsMutex.synchronize {
+ if @tcpServerThread
+ @tcpServerThread.raise "stop"
+ end
+ }
+ end
+
+ def stopped?
+ @tcpServerThread == nil
+ end
+
+ def shutdown
+ @shutdown = true
+ end
+
+ def connections
+ @connections.size
+ end
+
+ def join
+ @tcpServerThread.join if @tcpServerThread
+ end
+
+ attr_reader :port, :host, :maxConnections
+ attr_accessor :stdlog, :audit, :debug
+
+ def connecting(client)
+ addr = client.peeraddr
+ log("#{self.class.to_s} #{@host}:#{@port} client:#{addr[1]} " +
+ "#{addr[2]}<#{addr[3]}> connect")
+ true
+ end
+
+ def disconnecting(clientPort)
+ log("#{self.class.to_s} #{@host}:#{@port} " +
+ "client:#{clientPort} disconnect")
+ end
+
+ protected :connecting, :disconnecting
+
+ def starting()
+ log("#{self.class.to_s} #{@host}:#{@port} start")
+ end
+
+ def stopping()
+ log("#{self.class.to_s} #{@host}:#{@port} stop")
+ end
+
+ protected :starting, :stopping
+
+ def error(detail)
+ log(detail.backtrace.join("\n"))
+ end
+
+ def log(msg)
+ if @stdlog
+ @stdlog.puts("[#{Time.new.ctime}] %s" % msg)
+ @stdlog.flush
+ end
+ end
+
+ protected :error, :log
+
+ def initialize(port, host = DEFAULT_HOST, maxConnections = 4,
+ stdlog = $stderr, audit = false, debug = false)
+ @tcpServerThread = nil
+ @port = port
+ @host = host
+ @maxConnections = maxConnections
+ @connections = []
+ @connectionsMutex = Mutex.new
+ @connectionsCV = ConditionVariable.new
+ @stdlog = stdlog
+ @audit = audit
+ @debug = debug
+ end
+
+ def start(maxConnections = -1)
+ raise "running" if !stopped?
+ @shutdown = false
+ @maxConnections = maxConnections if maxConnections > 0
+ @@servicesMutex.synchronize {
+ if GServer.in_service?(@port,@host)
+ raise "Port already in use: #{host}:#{@port}!"
+ end
+ @tcpServer = TCPServer.new(@host,@port)
+ @port = @tcpServer.addr[1]
+ @@services[@host] = {} unless @@services.has_key?(@host)
+ @@services[@host][@port] = self;
+ }
+ @tcpServerThread = Thread.new {
+ begin
+ starting if @audit
+ while !@shutdown
+ @connectionsMutex.synchronize {
+ while @connections.size >= @maxConnections
+ @connectionsCV.wait(@connectionsMutex)
+ end
+ }
+ client = @tcpServer.accept
+ @connections << Thread.new(client) { |myClient|
+ begin
+ myPort = myClient.peeraddr[1]
+ serve(myClient) if !@audit or connecting(myClient)
+ rescue => detail
+ error(detail) if @debug
+ ensure
+ begin
+ myClient.close
+ rescue
+ end
+ @connectionsMutex.synchronize {
+ @connections.delete(Thread.current)
+ @connectionsCV.signal
+ }
+ disconnecting(myPort) if @audit
+ end
+ }
+ end
+ rescue => detail
+ error(detail) if @debug
+ ensure
+ begin
+ @tcpServer.close
+ rescue
+ end
+ if @shutdown
+ @connectionsMutex.synchronize {
+ while @connections.size > 0
+ @connectionsCV.wait(@connectionsMutex)
+ end
+ }
+ else
+ @connections.each { |c| c.raise "stop" }
+ end
+ @tcpServerThread = nil
+ @@servicesMutex.synchronize {
+ @@services[@host].delete(@port)
+ }
+ stopping if @audit
+ end
+ }
+ self
+ end
+
+end
diff --git a/ruby_1_8_5/lib/importenv.rb b/ruby_1_8_5/lib/importenv.rb
new file mode 100644
index 0000000000..8e1ba33b1d
--- /dev/null
+++ b/ruby_1_8_5/lib/importenv.rb
@@ -0,0 +1,33 @@
+# importenv.rb -- imports environment variables as global variables, Perlish ;(
+#
+# Usage:
+#
+# require 'importenv'
+# p $USER
+# $USER = "matz"
+# p ENV["USER"]
+
+warn "Warning:#{caller[0].sub(/:in `.*'\z/, '')}: importenv is deprecated after Ruby 1.8.1 (no replacement)"
+
+for k,v in ENV
+ next unless /^[a-zA-Z][_a-zA-Z0-9]*/ =~ k
+ eval <<EOS
+ $#{k} = v
+ trace_var "$#{k}", proc{|v|
+ ENV[%q!#{k}!] = v
+ $#{k} = v
+ if v == nil
+ untrace_var "$#{k}"
+ end
+ }
+EOS
+end
+
+if __FILE__ == $0
+ p $TERM
+ $TERM = nil
+ p $TERM
+ p ENV["TERM"]
+ $TERM = "foo"
+ p ENV["TERM"]
+end
diff --git a/ruby_1_8_5/lib/ipaddr.rb b/ruby_1_8_5/lib/ipaddr.rb
new file mode 100644
index 0000000000..67e2b65332
--- /dev/null
+++ b/ruby_1_8_5/lib/ipaddr.rb
@@ -0,0 +1,766 @@
+#
+# ipaddr.rb - A class to manipulate an IP address
+#
+# Copyright (c) 2002 Hajimu UMEMOTO <ume@mahoroba.org>.
+# All rights reserved.
+#
+# You can redistribute and/or modify it under the same terms as Ruby.
+#
+# $Id: ipaddr.rb,v 1.5.2.3 2006/08/04 22:00:21 drbrain Exp $
+#
+# TODO:
+# - scope_id support
+require 'socket'
+
+unless Socket.const_defined? "AF_INET6"
+ class Socket
+ AF_INET6 = Object.new
+ end
+
+ class << IPSocket
+ def valid_v4?(addr)
+ if /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/ =~ addr
+ return $~.captures.all? {|i| i.to_i < 256}
+ end
+ return false
+ end
+
+ def valid_v6?(addr)
+ # IPv6 (normal)
+ return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*\Z/ =~ addr
+ return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr
+ return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr
+ # IPv6 (IPv4 compat)
+ return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:/ =~ addr && valid_v4?($')
+ return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_v4?($')
+ return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_v4?($')
+
+ false
+ end
+
+ def valid?(addr)
+ valid_v4?(addr) || valid_v6?(addr)
+ end
+
+ alias getaddress_orig getaddress
+ def getaddress(s)
+ if valid?(s)
+ s
+ elsif /\A[-A-Za-z\d.]+\Z/ =~ s
+ getaddress_orig(s)
+ else
+ raise ArgumentError, "invalid address"
+ end
+ end
+ end
+end
+
+# IPAddr provides a set of methods to manipulate an IP address. Both IPv4 and
+# IPv6 are supported.
+#
+# == Example
+#
+# require 'ipaddr'
+#
+# ipaddr1 = IPAddr.new "3ffe:505:2::1"
+#
+# p ipaddr1 #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0001/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>
+#
+# p ipaddr1.to_s #=> "3ffe:505:2::1"
+#
+# ipaddr2 = ipaddr1.mask(48) #=> #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000>
+#
+# p ipaddr2.to_s #=> "3ffe:505:2::"
+#
+# ipaddr3 = IPAddr.new "192.168.2.0/24"
+#
+# p ipaddr3 #=> #<IPAddr: IPv4:192.168.2.0/255.255.255.0>
+
+class IPAddr
+
+ IN4MASK = 0xffffffff
+ IN6MASK = 0xffffffffffffffffffffffffffffffff
+ IN6FORMAT = (["%.4x"] * 8).join(':')
+
+ # Returns the address family of this IP address.
+ attr :family
+
+ # Creates a new ipaddr containing the given network byte ordered
+ # string form of an IP address.
+ def IPAddr::new_ntoh(addr)
+ return IPAddr.new(IPAddr::ntop(addr))
+ end
+
+ # Convert a network byte ordered string form of an IP address into
+ # human readable form.
+ def IPAddr::ntop(addr)
+ case addr.size
+ when 4
+ s = addr.unpack('C4').join('.')
+ when 16
+ s = IN6FORMAT % addr.unpack('n8')
+ else
+ raise ArgumentError, "unsupported address family"
+ end
+ return s
+ end
+
+ # Returns a new ipaddr built by bitwise AND.
+ def &(other)
+ return self.clone.set(@addr & other.to_i)
+ end
+
+ # Returns a new ipaddr built by bitwise OR.
+ def |(other)
+ return self.clone.set(@addr | other.to_i)
+ end
+
+ # Returns a new ipaddr built by bitwise right-shift.
+ def >>(num)
+ return self.clone.set(@addr >> num)
+ end
+
+ # Returns a new ipaddr built by bitwise left shift.
+ def <<(num)
+ return self.clone.set(addr_mask(@addr << num))
+ end
+
+ # Returns a new ipaddr built by bitwise negation.
+ def ~
+ return self.clone.set(addr_mask(~@addr))
+ end
+
+ # Returns true if two ipaddr are equal.
+ def ==(other)
+ if other.kind_of?(IPAddr) && @family != other.family
+ return false
+ end
+ return (@addr == other.to_i)
+ end
+
+ # Returns a new ipaddr built by masking IP address with the given
+ # prefixlen/netmask. (e.g. 8, 64, "255.255.255.0", etc.)
+ def mask(prefixlen)
+ return self.clone.mask!(prefixlen)
+ end
+
+ # Returns true if the given ipaddr is in the range.
+ #
+ # e.g.:
+ # require 'ipaddr'
+ # net1 = IPAddr.new("192.168.2.0/24")
+ # p net1.include?(IPAddr.new("192.168.2.0")) #=> true
+ # p net1.include?(IPAddr.new("192.168.2.255")) #=> true
+ # p net1.include?(IPAddr.new("192.168.3.0")) #=> false
+ def include?(other)
+ if ipv4_mapped?
+ if (@mask_addr >> 32) != 0xffffffffffffffffffffffff
+ return false
+ end
+ mask_addr = (@mask_addr & IN4MASK)
+ addr = (@addr & IN4MASK)
+ family = Socket::AF_INET
+ else
+ mask_addr = @mask_addr
+ addr = @addr
+ family = @family
+ end
+ if other.kind_of?(IPAddr)
+ if other.ipv4_mapped?
+ other_addr = (other.to_i & IN4MASK)
+ other_family = Socket::AF_INET
+ else
+ other_addr = other.to_i
+ other_family = other.family
+ end
+ else # Not IPAddr - assume integer in same family as us
+ other_addr = other.to_i
+ other_family = family
+ end
+
+ if family != other_family
+ return false
+ end
+ return ((addr & mask_addr) == (other_addr & mask_addr))
+ end
+ alias === include?
+
+ # Returns the integer representation of the ipaddr.
+ def to_i
+ return @addr
+ end
+
+ # Returns a string containing the IP address representation.
+ def to_s
+ str = to_string
+ return str if ipv4?
+
+ str.gsub!(/\b0{1,3}([\da-f]+)\b/i, '\1')
+ loop do
+ break if str.sub!(/\A0:0:0:0:0:0:0:0\Z/, '::')
+ break if str.sub!(/\b0:0:0:0:0:0:0\b/, ':')
+ break if str.sub!(/\b0:0:0:0:0:0\b/, ':')
+ break if str.sub!(/\b0:0:0:0:0\b/, ':')
+ break if str.sub!(/\b0:0:0:0\b/, ':')
+ break if str.sub!(/\b0:0:0\b/, ':')
+ break if str.sub!(/\b0:0\b/, ':')
+ break
+ end
+ str.sub!(/:{3,}/, '::')
+
+ if /\A::(ffff:)?([\da-f]{1,4}):([\da-f]{1,4})\Z/i =~ str
+ str = sprintf('::%s%d.%d.%d.%d', $1, $2.hex / 256, $2.hex % 256, $3.hex / 256, $3.hex % 256)
+ end
+
+ str
+ end
+
+ # Returns a string containing the IP address representation in
+ # canonical form.
+ def to_string
+ return _to_string(@addr)
+ end
+
+ # Returns a network byte ordered string form of the IP address.
+ def hton
+ case @family
+ when Socket::AF_INET
+ return [@addr].pack('N')
+ when Socket::AF_INET6
+ return (0..7).map { |i|
+ (@addr >> (112 - 16 * i)) & 0xffff
+ }.pack('n8')
+ else
+ raise "unsupported address family"
+ end
+ end
+
+ # Returns true if the ipaddr is an IPv4 address.
+ def ipv4?
+ return @family == Socket::AF_INET
+ end
+
+ # Returns true if the ipaddr is an IPv6 address.
+ def ipv6?
+ return @family == Socket::AF_INET6
+ end
+
+ # Returns true if the ipaddr is an IPv4-mapped IPv6 address.
+ def ipv4_mapped?
+ return ipv6? && (@addr >> 32) == 0xffff
+ end
+
+ # Returns true if the ipaddr is an IPv4-compatible IPv6 address.
+ def ipv4_compat?
+ if !ipv6? || (@addr >> 32) != 0
+ return false
+ end
+ a = (@addr & IN4MASK)
+ return a != 0 && a != 1
+ end
+
+ # Returns a new ipaddr built by converting the native IPv4 address
+ # into an IPv4-mapped IPv6 address.
+ def ipv4_mapped
+ if !ipv4?
+ raise ArgumentError, "not an IPv4 address"
+ end
+ return self.clone.set(@addr | 0xffff00000000, Socket::AF_INET6)
+ end
+
+ # Returns a new ipaddr built by converting the native IPv4 address
+ # into an IPv4-compatible IPv6 address.
+ def ipv4_compat
+ if !ipv4?
+ raise ArgumentError, "not an IPv4 address"
+ end
+ return self.clone.set(@addr, Socket::AF_INET6)
+ end
+
+ # Returns a new ipaddr built by converting the IPv6 address into a
+ # native IPv4 address. If the IP address is not an IPv4-mapped or
+ # IPv4-compatible IPv6 address, returns self.
+ def native
+ if !ipv4_mapped? && !ipv4_compat?
+ return self
+ end
+ return self.clone.set(@addr & IN4MASK, Socket::AF_INET)
+ end
+
+ # Returns a string for DNS reverse lookup. It returns a string in
+ # RFC3172 form for an IPv6 address.
+ def reverse
+ case @family
+ when Socket::AF_INET
+ return _reverse + ".in-addr.arpa"
+ when Socket::AF_INET6
+ return ip6_arpa
+ else
+ raise "unsupported address family"
+ end
+ end
+
+ # Returns a string for DNS reverse lookup compatible with RFC3172.
+ def ip6_arpa
+ if !ipv6?
+ raise ArgumentError, "not an IPv6 address"
+ end
+ return _reverse + ".ip6.arpa"
+ end
+
+ # Returns a string for DNS reverse lookup compatible with RFC1886.
+ def ip6_int
+ if !ipv6?
+ raise ArgumentError, "not an IPv6 address"
+ end
+ return _reverse + ".ip6.int"
+ end
+
+ # Returns a string containing a human-readable representation of the
+ # ipaddr. ("#<IPAddr: family:address/mask>")
+ def inspect
+ case @family
+ when Socket::AF_INET
+ af = "IPv4"
+ when Socket::AF_INET6
+ af = "IPv6"
+ else
+ raise "unsupported address family"
+ end
+ return sprintf("#<%s: %s:%s/%s>", self.class.name,
+ af, _to_string(@addr), _to_string(@mask_addr))
+ end
+
+ protected
+
+ def set(addr, *family)
+ case family[0] ? family[0] : @family
+ when Socket::AF_INET
+ if addr < 0 || addr > IN4MASK
+ raise ArgumentError, "invalid address"
+ end
+ when Socket::AF_INET6
+ if addr < 0 || addr > IN6MASK
+ raise ArgumentError, "invalid address"
+ end
+ else
+ raise ArgumentError, "unsupported address family"
+ end
+ @addr = addr
+ if family[0]
+ @family = family[0]
+ end
+ return self
+ end
+
+ def mask!(mask)
+ if mask.kind_of?(String)
+ if mask =~ /^\d+$/
+ prefixlen = mask.to_i
+ else
+ m = IPAddr.new(mask)
+ if m.family != @family
+ raise ArgumentError, "address family is not same"
+ end
+ @mask_addr = m.to_i
+ @addr &= @mask_addr
+ return self
+ end
+ else
+ prefixlen = mask
+ end
+ case @family
+ when Socket::AF_INET
+ if prefixlen < 0 || prefixlen > 32
+ raise ArgumentError, "invalid length"
+ end
+ masklen = 32 - prefixlen
+ @mask_addr = ((IN4MASK >> masklen) << masklen)
+ when Socket::AF_INET6
+ if prefixlen < 0 || prefixlen > 128
+ raise ArgumentError, "invalid length"
+ end
+ masklen = 128 - prefixlen
+ @mask_addr = ((IN6MASK >> masklen) << masklen)
+ else
+ raise "unsupported address family"
+ end
+ @addr = ((@addr >> masklen) << masklen)
+ return self
+ end
+
+ private
+
+ # Creates a new ipaddr containing the given human readable form of
+ # an IP address. It also accepts `address/prefixlen' and
+ # `address/mask'. When prefixlen or mask is specified, it returns a
+ # masked ipaddr. IPv6 address may beenclosed with `[' and `]'.
+ #
+ # Although an address family is determined automatically from a
+ # specified address, you can specify an address family explicitly by
+ # the optional second argument.
+ def initialize(addr = '::', family = Socket::AF_UNSPEC)
+ if !addr.kind_of?(String)
+ if family != Socket::AF_INET6 && family != Socket::AF_INET
+ raise ArgumentError, "unsupported address family"
+ end
+ set(addr, family)
+ @mask_addr = (family == Socket::AF_INET) ? IN4MASK : IN6MASK
+ return
+ end
+ prefix, prefixlen = addr.split('/')
+ if prefix =~ /^\[(.*)\]$/i
+ prefix = $1
+ family = Socket::AF_INET6
+ end
+ # It seems AI_NUMERICHOST doesn't do the job.
+ #Socket.getaddrinfo(left, nil, Socket::AF_INET6, Socket::SOCK_STREAM, nil,
+ # Socket::AI_NUMERICHOST)
+ begin
+ IPSocket.getaddress(prefix) # test if address is vaild
+ rescue
+ raise ArgumentError, "invalid address"
+ end
+ @addr = @family = nil
+ if family == Socket::AF_UNSPEC || family == Socket::AF_INET
+ @addr = in_addr(prefix)
+ if @addr
+ @family = Socket::AF_INET
+ end
+ end
+ if !@addr && (family == Socket::AF_UNSPEC || family == Socket::AF_INET6)
+ @addr = in6_addr(prefix)
+ @family = Socket::AF_INET6
+ end
+ if family != Socket::AF_UNSPEC && @family != family
+ raise ArgumentError, "address family unmatch"
+ end
+ if prefixlen
+ mask!(prefixlen)
+ else
+ @mask_addr = (family == Socket::AF_INET) ? IN4MASK : IN6MASK
+ end
+ end
+
+ def in_addr(addr)
+ if addr =~ /^\d+\.\d+\.\d+\.\d+$/
+ n = 0
+ addr.split('.').each { |i|
+ n <<= 8
+ n += i.to_i
+ }
+ return n
+ end
+ return nil
+ end
+
+ def in6_addr(left)
+ case left
+ when /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i
+ return in_addr($1) + 0xffff00000000
+ when /^::(\d+\.\d+\.\d+\.\d+)$/i
+ return in_addr($1)
+ when /[^0-9a-f:]/i
+ raise ArgumentError, "invalid address"
+ when /^(.*)::(.*)$/
+ left, right = $1, $2
+ else
+ right = ''
+ end
+ l = left.split(':')
+ r = right.split(':')
+ rest = 8 - l.size - r.size
+ if rest < 0
+ return nil
+ end
+ a = [l, Array.new(rest, '0'), r].flatten!
+ n = 0
+ a.each { |i|
+ n <<= 16
+ n += i.hex
+ }
+ return n
+ end
+
+ def addr_mask(addr)
+ case @family
+ when Socket::AF_INET
+ addr &= IN4MASK
+ when Socket::AF_INET6
+ addr &= IN6MASK
+ else
+ raise "unsupported address family"
+ end
+ return addr
+ end
+
+ def _reverse
+ case @family
+ when Socket::AF_INET
+ return (0..3).map { |i|
+ (@addr >> (8 * i)) & 0xff
+ }.join('.')
+ when Socket::AF_INET6
+ return ("%.32x" % @addr).reverse!.gsub!(/.(?!$)/, '\&.')
+ else
+ raise "unsupported address family"
+ end
+ end
+
+ def _to_string(addr)
+ case @family
+ when Socket::AF_INET
+ return (0..3).map { |i|
+ (addr >> (24 - 8 * i)) & 0xff
+ }.join('.')
+ when Socket::AF_INET6
+ return (("%.32x" % addr).gsub!(/.{4}(?!$)/, '\&:'))
+ else
+ raise "unsupported address family"
+ end
+ end
+
+end
+
+if $0 == __FILE__
+ eval DATA.read, nil, $0, __LINE__+4
+end
+
+__END__
+
+require 'test/unit'
+require 'test/unit/ui/console/testrunner'
+
+class TC_IPAddr < Test::Unit::TestCase
+ def test_s_new
+ assert_nothing_raised {
+ IPAddr.new("3FFE:505:ffff::/48")
+ IPAddr.new("0:0:0:1::")
+ IPAddr.new("2001:200:300::/48")
+ }
+
+ a = IPAddr.new
+ assert_equal("::", a.to_s)
+ assert_equal("0000:0000:0000:0000:0000:0000:0000:0000", a.to_string)
+ assert_equal(Socket::AF_INET6, a.family)
+
+ a = IPAddr.new("0123:4567:89ab:cdef:0ABC:DEF0:1234:5678")
+ assert_equal("123:4567:89ab:cdef:abc:def0:1234:5678", a.to_s)
+ assert_equal("0123:4567:89ab:cdef:0abc:def0:1234:5678", a.to_string)
+ assert_equal(Socket::AF_INET6, a.family)
+
+ a = IPAddr.new("3ffe:505:2::/48")
+ assert_equal("3ffe:505:2::", a.to_s)
+ assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string)
+ assert_equal(Socket::AF_INET6, a.family)
+ assert_equal(false, a.ipv4?)
+ assert_equal(true, a.ipv6?)
+ assert_equal("#<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/ffff:ffff:ffff:0000:0000:0000:0000:0000>", a.inspect)
+
+ a = IPAddr.new("3ffe:505:2::/ffff:ffff:ffff::")
+ assert_equal("3ffe:505:2::", a.to_s)
+ assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string)
+ assert_equal(Socket::AF_INET6, a.family)
+
+ a = IPAddr.new("0.0.0.0")
+ assert_equal("0.0.0.0", a.to_s)
+ assert_equal("0.0.0.0", a.to_string)
+ assert_equal(Socket::AF_INET, a.family)
+
+ a = IPAddr.new("192.168.1.2")
+ assert_equal("192.168.1.2", a.to_s)
+ assert_equal("192.168.1.2", a.to_string)
+ assert_equal(Socket::AF_INET, a.family)
+ assert_equal(true, a.ipv4?)
+ assert_equal(false, a.ipv6?)
+
+ a = IPAddr.new("192.168.1.2/24")
+ assert_equal("192.168.1.0", a.to_s)
+ assert_equal("192.168.1.0", a.to_string)
+ assert_equal(Socket::AF_INET, a.family)
+ assert_equal("#<IPAddr: IPv4:192.168.1.0/255.255.255.0>", a.inspect)
+
+ a = IPAddr.new("192.168.1.2/255.255.255.0")
+ assert_equal("192.168.1.0", a.to_s)
+ assert_equal("192.168.1.0", a.to_string)
+ assert_equal(Socket::AF_INET, a.family)
+
+ assert_equal("0:0:0:1::", IPAddr.new("0:0:0:1::").to_s)
+ assert_equal("2001:200:300::", IPAddr.new("2001:200:300::/48").to_s)
+
+ assert_equal("2001:200:300::", IPAddr.new("[2001:200:300::]/48").to_s)
+
+ [
+ ["fe80::1%fxp0"],
+ ["::1/255.255.255.0"],
+ ["::1:192.168.1.2/120"],
+ [IPAddr.new("::1").to_i],
+ ["::ffff:192.168.1.2/120", Socket::AF_INET],
+ ["[192.168.1.2]/120"],
+ ].each { |args|
+ assert_raises(ArgumentError) {
+ IPAddr.new(*args)
+ }
+ }
+ end
+
+ def test_s_new_ntoh
+ addr = ''
+ IPAddr.new("1234:5678:9abc:def0:1234:5678:9abc:def0").hton.each_byte { |c|
+ addr += sprintf("%02x", c)
+ }
+ assert_equal("123456789abcdef0123456789abcdef0", addr)
+ addr = ''
+ IPAddr.new("123.45.67.89").hton.each_byte { |c|
+ addr += sprintf("%02x", c)
+ }
+ assert_equal(sprintf("%02x%02x%02x%02x", 123, 45, 67, 89), addr)
+ a = IPAddr.new("3ffe:505:2::")
+ assert_equal("3ffe:505:2::", IPAddr.new_ntoh(a.hton).to_s)
+ a = IPAddr.new("192.168.2.1")
+ assert_equal("192.168.2.1", IPAddr.new_ntoh(a.hton).to_s)
+ end
+
+ def test_ipv4_compat
+ a = IPAddr.new("::192.168.1.2")
+ assert_equal("::192.168.1.2", a.to_s)
+ assert_equal("0000:0000:0000:0000:0000:0000:c0a8:0102", a.to_string)
+ assert_equal(Socket::AF_INET6, a.family)
+ assert_equal(true, a.ipv4_compat?)
+ b = a.native
+ assert_equal("192.168.1.2", b.to_s)
+ assert_equal(Socket::AF_INET, b.family)
+ assert_equal(false, b.ipv4_compat?)
+
+ a = IPAddr.new("192.168.1.2")
+ b = a.ipv4_compat
+ assert_equal("::192.168.1.2", b.to_s)
+ assert_equal(Socket::AF_INET6, b.family)
+ end
+
+ def test_ipv4_mapped
+ a = IPAddr.new("::ffff:192.168.1.2")
+ assert_equal("::ffff:192.168.1.2", a.to_s)
+ assert_equal("0000:0000:0000:0000:0000:ffff:c0a8:0102", a.to_string)
+ assert_equal(Socket::AF_INET6, a.family)
+ assert_equal(true, a.ipv4_mapped?)
+ b = a.native
+ assert_equal("192.168.1.2", b.to_s)
+ assert_equal(Socket::AF_INET, b.family)
+ assert_equal(false, b.ipv4_mapped?)
+
+ a = IPAddr.new("192.168.1.2")
+ b = a.ipv4_mapped
+ assert_equal("::ffff:192.168.1.2", b.to_s)
+ assert_equal(Socket::AF_INET6, b.family)
+ end
+
+ def test_reverse
+ assert_equal("f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa", IPAddr.new("3ffe:505:2::f").reverse)
+ assert_equal("1.2.168.192.in-addr.arpa", IPAddr.new("192.168.2.1").reverse)
+ end
+
+ def test_ip6_arpa
+ assert_equal("f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa", IPAddr.new("3ffe:505:2::f").ip6_arpa)
+ assert_raises(ArgumentError) {
+ IPAddr.new("192.168.2.1").ip6_arpa
+ }
+ end
+
+ def test_ip6_int
+ assert_equal("f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.int", IPAddr.new("3ffe:505:2::f").ip6_int)
+ assert_raises(ArgumentError) {
+ IPAddr.new("192.168.2.1").ip6_int
+ }
+ end
+
+ def test_to_s
+ assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0001", IPAddr.new("3ffe:505:2::1").to_string)
+ assert_equal("3ffe:505:2::1", IPAddr.new("3ffe:505:2::1").to_s)
+ end
+end
+
+class TC_Operator < Test::Unit::TestCase
+
+ IN6MASK32 = "ffff:ffff::"
+ IN6MASK128 = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
+
+ def setup
+ @in6_addr_any = IPAddr.new()
+ @a = IPAddr.new("3ffe:505:2::/48")
+ @b = IPAddr.new("0:0:0:1::")
+ @c = IPAddr.new(IN6MASK32)
+ end
+ alias set_up setup
+
+ def test_or
+ assert_equal("3ffe:505:2:1::", (@a | @b).to_s)
+ a = @a
+ a |= @b
+ assert_equal("3ffe:505:2:1::", a.to_s)
+ assert_equal("3ffe:505:2::", @a.to_s)
+ assert_equal("3ffe:505:2:1::",
+ (@a | 0x00000000000000010000000000000000).to_s)
+ end
+
+ def test_and
+ assert_equal("3ffe:505::", (@a & @c).to_s)
+ a = @a
+ a &= @c
+ assert_equal("3ffe:505::", a.to_s)
+ assert_equal("3ffe:505:2::", @a.to_s)
+ assert_equal("3ffe:505::", (@a & 0xffffffff000000000000000000000000).to_s)
+ end
+
+ def test_shift_right
+ assert_equal("0:3ffe:505:2::", (@a >> 16).to_s)
+ a = @a
+ a >>= 16
+ assert_equal("0:3ffe:505:2::", a.to_s)
+ assert_equal("3ffe:505:2::", @a.to_s)
+ end
+
+ def test_shift_left
+ assert_equal("505:2::", (@a << 16).to_s)
+ a = @a
+ a <<= 16
+ assert_equal("505:2::", a.to_s)
+ assert_equal("3ffe:505:2::", @a.to_s)
+ end
+
+ def test_carrot
+ a = ~@in6_addr_any
+ assert_equal(IN6MASK128, a.to_s)
+ assert_equal("::", @in6_addr_any.to_s)
+ end
+
+ def test_equal
+ assert_equal(true, @a == IPAddr.new("3ffe:505:2::"))
+ assert_equal(false, @a == IPAddr.new("3ffe:505:3::"))
+ assert_equal(true, @a != IPAddr.new("3ffe:505:3::"))
+ assert_equal(false, @a != IPAddr.new("3ffe:505:2::"))
+ end
+
+ def test_mask
+ a = @a.mask(32)
+ assert_equal("3ffe:505::", a.to_s)
+ assert_equal("3ffe:505:2::", @a.to_s)
+ end
+
+ def test_include?
+ assert_equal(true, @a.include?(IPAddr.new("3ffe:505:2::")))
+ assert_equal(true, @a.include?(IPAddr.new("3ffe:505:2::1")))
+ assert_equal(false, @a.include?(IPAddr.new("3ffe:505:3::")))
+ net1 = IPAddr.new("192.168.2.0/24")
+ assert_equal(true, net1.include?(IPAddr.new("192.168.2.0")))
+ assert_equal(true, net1.include?(IPAddr.new("192.168.2.255")))
+ assert_equal(false, net1.include?(IPAddr.new("192.168.3.0")))
+ # test with integer parameter
+ int = (192 << 24) + (168 << 16) + (2 << 8) + 13
+
+ assert_equal(true, net1.include?(int))
+ assert_equal(false, net1.include?(int+255))
+
+ end
+
+end
diff --git a/ruby_1_8_5/lib/irb.rb b/ruby_1_8_5/lib/irb.rb
new file mode 100644
index 0000000000..65b1512740
--- /dev/null
+++ b/ruby_1_8_5/lib/irb.rb
@@ -0,0 +1,342 @@
+#
+# irb.rb - irb main module
+# $Release Version: 0.9.5 $
+# $Revision: 1.7.2.2 $
+# $Date: 2005/10/25 06:38:25 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+require "e2mmap"
+
+require "irb/init"
+require "irb/context"
+require "irb/extend-command"
+#require "irb/workspace"
+
+require "irb/ruby-lex"
+require "irb/input-method"
+require "irb/locale"
+
+STDOUT.sync = true
+
+module IRB
+ @RCS_ID='-$Id: irb.rb,v 1.7.2.2 2005/10/25 06:38:25 matz Exp $-'
+
+ class Abort < Exception;end
+
+ #
+ @CONF = {}
+
+ def IRB.conf
+ @CONF
+ end
+
+ # IRB version method
+ def IRB.version
+ if v = @CONF[:VERSION] then return v end
+
+ require "irb/version"
+ rv = @RELEASE_VERSION.sub(/\.0/, "")
+ @CONF[:VERSION] = format("irb %s(%s)", rv, @LAST_UPDATE_DATE)
+ end
+
+ def IRB.CurrentContext
+ IRB.conf[:MAIN_CONTEXT]
+ end
+
+ # initialize IRB and start TOP_LEVEL irb
+ def IRB.start(ap_path = nil)
+ $0 = File::basename(ap_path, ".rb") if ap_path
+
+ IRB.setup(ap_path)
+
+ if @CONF[:SCRIPT]
+ irb = Irb.new(nil, @CONF[:SCRIPT])
+ else
+ irb = Irb.new
+ end
+
+ @CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
+ @CONF[:MAIN_CONTEXT] = irb.context
+
+ trap("SIGINT") do
+ irb.signal_handle
+ end
+
+ catch(:IRB_EXIT) do
+ irb.eval_input
+ end
+# print "\n"
+ end
+
+ def IRB.irb_exit(irb, ret)
+ throw :IRB_EXIT, ret
+ end
+
+ def IRB.irb_abort(irb, exception = Abort)
+ if defined? Thread
+ irb.context.thread.raise exception, "abort then interrupt!!"
+ else
+ raise exception, "abort then interrupt!!"
+ end
+ end
+
+ #
+ # irb interpriter main routine
+ #
+ class Irb
+ def initialize(workspace = nil, input_method = nil, output_method = nil)
+ @context = Context.new(self, workspace, input_method, output_method)
+ @context.main.extend ExtendCommandBundle
+ @signal_status = :IN_IRB
+
+ @scanner = RubyLex.new
+ @scanner.exception_on_syntax_error = false
+ end
+ attr_reader :context
+ attr_accessor :scanner
+
+ def eval_input
+ @scanner.set_prompt do
+ |ltype, indent, continue, line_no|
+ if ltype
+ f = @context.prompt_s
+ elsif continue
+ f = @context.prompt_c
+ elsif indent > 0
+ f = @context.prompt_n
+ else @context.prompt_i
+ f = @context.prompt_i
+ end
+ f = "" unless f
+ if @context.prompting?
+ @context.io.prompt = p = prompt(f, ltype, indent, line_no)
+ else
+ @context.io.prompt = p = ""
+ end
+ if @context.auto_indent_mode
+ unless ltype
+ ind = prompt(@context.prompt_i, ltype, indent, line_no)[/.*\z/].size +
+ indent * 2 - p.size
+ ind += 2 if continue
+ @context.io.prompt = p + " " * ind if ind > 0
+ end
+ end
+ end
+
+ @scanner.set_input(@context.io) do
+ signal_status(:IN_INPUT) do
+ if l = @context.io.gets
+ print l if @context.verbose?
+ else
+ if @context.ignore_eof? and @context.io.readable_atfer_eof?
+ l = "\n"
+ if @context.verbose?
+ printf "Use \"exit\" to leave %s\n", @context.ap_name
+ end
+ end
+ end
+ l
+ end
+ end
+
+ @scanner.each_top_level_statement do |line, line_no|
+ signal_status(:IN_EVAL) do
+ begin
+ line.untaint
+ @context.evaluate(line, line_no)
+ output_value if @context.echo?
+ rescue StandardError, ScriptError, Abort
+ $! = RuntimeError.new("unknown exception raised") unless $!
+ print $!.class, ": ", $!, "\n"
+ if $@[0] =~ /irb(2)?(\/.*|-.*|\.rb)?:/ && $!.class.to_s !~ /^IRB/
+ irb_bug = true
+ else
+ irb_bug = false
+ end
+
+ messages = []
+ lasts = []
+ levels = 0
+ for m in $@
+ m = @context.workspace.filter_backtrace(m) unless irb_bug
+ if m
+ if messages.size < @context.back_trace_limit
+ messages.push "\tfrom "+m
+ else
+ lasts.push "\tfrom "+m
+ if lasts.size > @context.back_trace_limit
+ lasts.shift
+ levels += 1
+ end
+ end
+ end
+ end
+ print messages.join("\n"), "\n"
+ unless lasts.empty?
+ printf "... %d levels...\n", levels if levels > 0
+ print lasts.join("\n")
+ end
+ print "Maybe IRB bug!!\n" if irb_bug
+ end
+ if $SAFE > 2
+ warn "Error: irb does not work for $SAFE level higher than 2"
+ exit 1
+ end
+ end
+ end
+ end
+
+ def suspend_name(path = nil, name = nil)
+ @context.irb_path, back_path = path, @context.irb_path if path
+ @context.irb_name, back_name = name, @context.irb_name if name
+ begin
+ yield back_path, back_name
+ ensure
+ @context.irb_path = back_path if path
+ @context.irb_name = back_name if name
+ end
+ end
+
+ def suspend_workspace(workspace)
+ @context.workspace, back_workspace = workspace, @context.workspace
+ begin
+ yield back_workspace
+ ensure
+ @context.workspace = back_workspace
+ end
+ end
+
+ def suspend_input_method(input_method)
+ back_io = @context.io
+ @context.instance_eval{@io = input_method}
+ begin
+ yield back_io
+ ensure
+ @context.instance_eval{@io = back_io}
+ end
+ end
+
+ def suspend_context(context)
+ @context, back_context = context, @context
+ begin
+ yield back_context
+ ensure
+ @context = back_context
+ end
+ end
+
+ def signal_handle
+ unless @context.ignore_sigint?
+ print "\nabort!!\n" if @context.verbose?
+ exit
+ end
+
+ case @signal_status
+ when :IN_INPUT
+ print "^C\n"
+ raise RubyLex::TerminateLineInput
+ when :IN_EVAL
+ IRB.irb_abort(self)
+ when :IN_LOAD
+ IRB.irb_abort(self, LoadAbort)
+ when :IN_IRB
+ # ignore
+ else
+ # ignore other cases as well
+ end
+ end
+
+ def signal_status(status)
+ return yield if @signal_status == :IN_LOAD
+
+ signal_status_back = @signal_status
+ @signal_status = status
+ begin
+ yield
+ ensure
+ @signal_status = signal_status_back
+ end
+ end
+
+ def prompt(prompt, ltype, indent, line_no)
+ p = prompt.dup
+ p.gsub!(/%([0-9]+)?([a-zA-Z])/) do
+ case $2
+ when "N"
+ @context.irb_name
+ when "m"
+ @context.main.to_s
+ when "M"
+ @context.main.inspect
+ when "l"
+ ltype
+ when "i"
+ if $1
+ format("%" + $1 + "d", indent)
+ else
+ indent.to_s
+ end
+ when "n"
+ if $1
+ format("%" + $1 + "d", line_no)
+ else
+ line_no.to_s
+ end
+ when "%"
+ "%"
+ end
+ end
+ p
+ end
+
+ def output_value
+ if @context.inspect?
+ printf @context.return_format, @context.last_value.inspect
+ else
+ printf @context.return_format, @context.last_value
+ end
+ end
+
+ def inspect
+ ary = []
+ for iv in instance_variables
+ case iv
+ when "@signal_status"
+ ary.push format("%s=:%s", iv, @signal_status.id2name)
+ when "@context"
+ ary.push format("%s=%s", iv, eval(iv).__to_s__)
+ else
+ ary.push format("%s=%s", iv, eval(iv))
+ end
+ end
+ format("#<%s: %s>", self.class, ary.join(", "))
+ end
+ end
+
+ # Singleton method
+ def @CONF.inspect
+ IRB.version unless self[:VERSION]
+
+ array = []
+ for k, v in sort{|a1, a2| a1[0].id2name <=> a2[0].id2name}
+ case k
+ when :MAIN_CONTEXT, :__TMP__EHV__
+ array.push format("CONF[:%s]=...myself...", k.id2name)
+ when :PROMPT
+ s = v.collect{
+ |kk, vv|
+ ss = vv.collect{|kkk, vvv| ":#{kkk.id2name}=>#{vvv.inspect}"}
+ format(":%s=>{%s}", kk.id2name, ss.join(", "))
+ }
+ array.push format("CONF[:%s]={%s}", k.id2name, s.join(", "))
+ else
+ array.push format("CONF[:%s]=%s", k.id2name, v.inspect)
+ end
+ end
+ array.join("\n")
+ end
+end
diff --git a/ruby_1_8_5/lib/irb/cmd/chws.rb b/ruby_1_8_5/lib/irb/cmd/chws.rb
new file mode 100644
index 0000000000..63be940f7a
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/cmd/chws.rb
@@ -0,0 +1,33 @@
+#
+# change-ws.rb -
+# $Release Version: 0.9.5$
+# $Revision: 1.1.2.1 $
+# $Date: 2005/04/19 19:24:58 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+
+require "irb/cmd/nop.rb"
+require "irb/ext/change-ws.rb"
+
+module IRB
+ module ExtendCommand
+
+ class CurrentWorkingWorkspace<Nop
+ def execute(*obj)
+ irb_context.main
+ end
+ end
+
+ class ChangeWorkspace<Nop
+ def execute(*obj)
+ irb_context.change_workspace(*obj)
+ irb_context.main
+ end
+ end
+ end
+end
+
diff --git a/ruby_1_8_5/lib/irb/cmd/fork.rb b/ruby_1_8_5/lib/irb/cmd/fork.rb
new file mode 100644
index 0000000000..67dee460f1
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/cmd/fork.rb
@@ -0,0 +1,39 @@
+#
+# fork.rb -
+# $Release Version: 0.9.5 $
+# $Revision: 1.2.2.1 $
+# $Date: 2005/04/19 19:24:58 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+
+@RCS_ID='-$Id: fork.rb,v 1.2.2.1 2005/04/19 19:24:58 keiju Exp $-'
+
+
+module IRB
+ module ExtendCommand
+ class Fork<Nop
+ def execute(&block)
+ pid = send ExtendCommand.irb_original_method_name("fork")
+ unless pid
+ class<<self
+ alias_method :exit, ExtendCommand.irb_original_method_name('exit')
+ end
+ if iterator?
+ begin
+ yield
+ ensure
+ exit
+ end
+ end
+ end
+ pid
+ end
+ end
+ end
+end
+
+
diff --git a/ruby_1_8_5/lib/irb/cmd/help.rb b/ruby_1_8_5/lib/irb/cmd/help.rb
new file mode 100644
index 0000000000..578d9e63a6
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/cmd/help.rb
@@ -0,0 +1,34 @@
+#
+# help.rb - helper using ri
+# $Release Version: 0.9.5$
+# $Revision: 1.2.4.1 $
+# $Date: 2005/04/19 19:24:58 $
+#
+# --
+#
+#
+#
+
+require 'rdoc/ri/ri_driver'
+
+module IRB
+ module ExtendCommand
+ module Help
+ begin
+ @ri = RiDriver.new
+ rescue SystemExit
+ else
+ def self.execute(context, *names)
+ names.each do |name|
+ begin
+ @ri.get_info_for(name.to_s)
+ rescue RiError
+ puts $!.message
+ end
+ end
+ nil
+ end
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/irb/cmd/load.rb b/ruby_1_8_5/lib/irb/cmd/load.rb
new file mode 100644
index 0000000000..aec5d8a391
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/cmd/load.rb
@@ -0,0 +1,67 @@
+#
+# load.rb -
+# $Release Version: 0.9.5$
+# $Revision: 1.1.2.1 $
+# $Date: 2005/04/19 19:24:58 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+
+require "irb/cmd/nop.rb"
+require "irb/ext/loader"
+
+module IRB
+ module ExtendCommand
+ class Load<Nop
+ include IrbLoader
+
+ def execute(file_name, priv = nil)
+# return ruby_load(file_name) unless IRB.conf[:USE_LOADER]
+ return irb_load(file_name, priv)
+ end
+ end
+
+ class Require<Nop
+ include IrbLoader
+
+ def execute(file_name)
+# return ruby_require(file_name) unless IRB.conf[:USE_LOADER]
+
+ rex = Regexp.new("#{Regexp.quote(file_name)}(\.o|\.rb)?")
+ return false if $".find{|f| f =~ rex}
+
+ case file_name
+ when /\.rb$/
+ begin
+ if irb_load(file_name)
+ $".push file_name
+ return true
+ end
+ rescue LoadError
+ end
+ when /\.(so|o|sl)$/
+ return ruby_require(file_name)
+ end
+
+ begin
+ irb_load(f = file_name + ".rb")
+ $".push f
+ return true
+ rescue LoadError
+ return ruby_require(file_name)
+ end
+ end
+ end
+
+ class Source<Nop
+ include IrbLoader
+ def execute(file_name)
+ source_file(file_name)
+ end
+ end
+ end
+
+end
diff --git a/ruby_1_8_5/lib/irb/cmd/nop.rb b/ruby_1_8_5/lib/irb/cmd/nop.rb
new file mode 100644
index 0000000000..f0e786e8d4
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/cmd/nop.rb
@@ -0,0 +1,39 @@
+#
+# nop.rb -
+# $Release Version: 0.9.5$
+# $Revision: 1.1.2.1 $
+# $Date: 2005/04/19 19:24:58 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+module IRB
+ module ExtendCommand
+ class Nop
+
+ @RCS_ID='-$Id: nop.rb,v 1.1.2.1 2005/04/19 19:24:58 keiju Exp $-'
+
+ def self.execute(conf, *opts)
+ command = new(conf)
+ command.execute(*opts)
+ end
+
+ def initialize(conf)
+ @irb_context = conf
+ end
+
+ attr_reader :irb_context
+
+ def irb
+ @irb_context.irb
+ end
+
+ def execute(*opts)
+ #nop
+ end
+ end
+ end
+end
+
diff --git a/ruby_1_8_5/lib/irb/cmd/pushws.rb b/ruby_1_8_5/lib/irb/cmd/pushws.rb
new file mode 100644
index 0000000000..7acfee094f
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/cmd/pushws.rb
@@ -0,0 +1,39 @@
+#
+# change-ws.rb -
+# $Release Version: 0.9.5$
+# $Revision: 1.1.2.1 $
+# $Date: 2005/04/19 19:24:58 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+
+require "irb/cmd/nop.rb"
+require "irb/ext/workspaces.rb"
+
+module IRB
+ module ExtendCommand
+ class Workspaces<Nop
+ def execute(*obj)
+ irb_context.workspaces.collect{|ws| ws.main}
+ end
+ end
+
+ class PushWorkspace<Workspaces
+ def execute(*obj)
+ irb_context.push_workspace(*obj)
+ super
+ end
+ end
+
+ class PopWorkspace<Workspaces
+ def execute(*obj)
+ irb_context.pop_workspace(*obj)
+ super
+ end
+ end
+ end
+end
+
diff --git a/ruby_1_8_5/lib/irb/cmd/subirb.rb b/ruby_1_8_5/lib/irb/cmd/subirb.rb
new file mode 100644
index 0000000000..3ea9db33a0
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/cmd/subirb.rb
@@ -0,0 +1,43 @@
+#!/usr/local/bin/ruby
+#
+# multi.rb -
+# $Release Version: 0.9.5$
+# $Revision: 1.1.2.1 $
+# $Date: 2005/04/19 19:24:58 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+
+require "irb/cmd/nop.rb"
+require "irb/ext/multi-irb"
+
+module IRB
+ module ExtendCommand
+ class IrbCommand<Nop
+ def execute(*obj)
+ IRB.irb(nil, *obj)
+ end
+ end
+
+ class Jobs<Nop
+ def execute
+ IRB.JobManager
+ end
+ end
+
+ class Foreground<Nop
+ def execute(key)
+ IRB.JobManager.switch(key)
+ end
+ end
+
+ class Kill<Nop
+ def execute(*keys)
+ IRB.JobManager.kill(*keys)
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/irb/completion.rb b/ruby_1_8_5/lib/irb/completion.rb
new file mode 100644
index 0000000000..49fb4556fa
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/completion.rb
@@ -0,0 +1,205 @@
+#
+# irb/completor.rb -
+# $Release Version: 0.9$
+# $Revision: 1.8.2.3 $
+# $Date: 2006/07/19 15:08:56 $
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
+# From Original Idea of shugo@ruby-lang.org
+#
+
+require "readline"
+
+module IRB
+ module InputCompletor
+
+ @RCS_ID='-$Id: completion.rb,v 1.8.2.3 2006/07/19 15:08:56 keiju Exp $-'
+
+ ReservedWords = [
+ "BEGIN", "END",
+ "alias", "and",
+ "begin", "break",
+ "case", "class",
+ "def", "defined", "do",
+ "else", "elsif", "end", "ensure",
+ "false", "for",
+ "if", "in",
+ "module",
+ "next", "nil", "not",
+ "or",
+ "redo", "rescue", "retry", "return",
+ "self", "super",
+ "then", "true",
+ "undef", "unless", "until",
+ "when", "while",
+ "yield",
+ ]
+
+ CompletionProc = proc { |input|
+ bind = IRB.conf[:MAIN_CONTEXT].workspace.binding
+
+# puts "input: #{input}"
+
+ case input
+ when /^(\/[^\/]*\/)\.([^.]*)$/
+ # Regexp
+ receiver = $1
+ message = Regexp.quote($2)
+
+ candidates = Regexp.instance_methods(true)
+ select_message(receiver, message, candidates)
+
+ when /^([^\]]*\])\.([^.]*)$/
+ # Array
+ receiver = $1
+ message = Regexp.quote($2)
+
+ candidates = Array.instance_methods(true)
+ select_message(receiver, message, candidates)
+
+ when /^([^\}]*\})\.([^.]*)$/
+ # Proc or Hash
+ receiver = $1
+ message = Regexp.quote($2)
+
+ candidates = Proc.instance_methods(true) | Hash.instance_methods(true)
+ select_message(receiver, message, candidates)
+
+ when /^(:[^:.]*)$/
+ # Symbol
+ if Symbol.respond_to?(:all_symbols)
+ sym = $1
+ candidates = Symbol.all_symbols.collect{|s| ":" + s.id2name}
+ candidates.grep(/^#{sym}/)
+ else
+ []
+ end
+
+ when /^::([A-Z][^:\.\(]*)$/
+ # Absolute Constant or class methods
+ receiver = $1
+ candidates = Object.constants
+ candidates.grep(/^#{receiver}/).collect{|e| "::" + e}
+
+ when /^(((::)?[A-Z][^:.\(]*)+)::?([^:.]*)$/
+ # Constant or class methods
+ receiver = $1
+ message = Regexp.quote($4)
+ begin
+ candidates = eval("#{receiver}.constants | #{receiver}.methods", bind)
+ rescue Exception
+ candidates = []
+ end
+ candidates.grep(/^#{message}/).collect{|e| receiver + "::" + e}
+
+ when /^(:[^:.]+)\.([^.]*)$/
+ # Symbol
+ receiver = $1
+ message = Regexp.quote($2)
+
+ candidates = Symbol.instance_methods(true)
+ select_message(receiver, message, candidates)
+
+ when /^(-?(0[dbo])?[0-9_]+(\.[0-9_]+)?([eE]-?[0-9]+)?)\.([^.]*)$/
+ # Numeric
+ receiver = $1
+ message = Regexp.quote($5)
+
+ begin
+ candidates = eval(receiver, bind).methods
+ rescue Exception
+ candidates = []
+ end
+ select_message(receiver, message, candidates)
+
+ when /^(-?0x[0-9a-fA-F_]+)\.([^.]*)$/
+ # Numeric(0xFFFF)
+ receiver = $1
+ message = Regexp.quote($2)
+
+ begin
+ candidates = eval(receiver, bind).methods
+ rescue Exception
+ candidates = []
+ end
+ select_message(receiver, message, candidates)
+
+ when /^(\$[^.]*)$/
+ candidates = global_variables.grep(Regexp.new(Regexp.quote($1)))
+
+# when /^(\$?(\.?[^.]+)+)\.([^.]*)$/
+ when /^((\.?[^.]+)+)\.([^.]*)$/
+ # variable
+ receiver = $1
+ message = Regexp.quote($3)
+
+ gv = eval("global_variables", bind)
+ lv = eval("local_variables", bind)
+ cv = eval("self.class.constants", bind)
+
+ if (gv | lv | cv).include?(receiver)
+ # foo.func and foo is local var.
+ candidates = eval("#{receiver}.methods", bind)
+ elsif /^[A-Z]/ =~ receiver and /\./ !~ receiver
+ # Foo::Bar.func
+ begin
+ candidates = eval("#{receiver}.methods", bind)
+ rescue Exception
+ candidates = []
+ end
+ else
+ # func1.func2
+ candidates = []
+ ObjectSpace.each_object(Module){|m|
+ begin
+ name = m.name
+ rescue Exception
+ name = ""
+ end
+ next if name != "IRB::Context" and
+ /^(IRB|SLex|RubyLex|RubyToken)/ =~ name
+ candidates.concat m.instance_methods(false)
+ }
+ candidates.sort!
+ candidates.uniq!
+ end
+ select_message(receiver, message, candidates)
+
+ when /^\.([^.]*)$/
+ # unknown(maybe String)
+
+ receiver = ""
+ message = Regexp.quote($1)
+
+ candidates = String.instance_methods(true)
+ select_message(receiver, message, candidates)
+
+ else
+ candidates = eval("methods | private_methods | local_variables | self.class.constants", bind)
+
+ (candidates|ReservedWords).grep(/^#{Regexp.quote(input)}/)
+ end
+ }
+
+ Operators = ["%", "&", "*", "**", "+", "-", "/",
+ "<", "<<", "<=", "<=>", "==", "===", "=~", ">", ">=", ">>",
+ "[]", "[]=", "^",]
+
+ def self.select_message(receiver, message, candidates)
+ candidates.grep(/^#{message}/).collect do |e|
+ case e
+ when /^[a-zA-Z_]/
+ receiver + "." + e
+ when /^[0-9]/
+ when *Operators
+ #receiver + " " + e
+ end
+ end
+ end
+ end
+end
+
+if Readline.respond_to?("basic_word_break_characters=")
+ Readline.basic_word_break_characters= " \t\n\"\\'`><=;|&{("
+end
+Readline.completion_append_character = nil
+Readline.completion_proc = IRB::InputCompletor::CompletionProc
diff --git a/ruby_1_8_5/lib/irb/context.rb b/ruby_1_8_5/lib/irb/context.rb
new file mode 100644
index 0000000000..c82be24db1
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/context.rb
@@ -0,0 +1,255 @@
+#
+# irb/context.rb - irb context
+# $Release Version: 0.9.5$
+# $Revision: 1.8.2.3 $
+# $Date: 2005/07/31 15:10:26 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+require "irb/workspace"
+
+module IRB
+ class Context
+ #
+ # Arguments:
+ # input_method: nil -- stdin or readline
+ # String -- File
+ # other -- using this as InputMethod
+ #
+ def initialize(irb, workspace = nil, input_method = nil, output_method = nil)
+ @irb = irb
+ if workspace
+ @workspace = workspace
+ else
+ @workspace = WorkSpace.new
+ end
+ @thread = Thread.current if defined? Thread
+# @irb_level = 0
+
+ # copy of default configuration
+ @ap_name = IRB.conf[:AP_NAME]
+ @rc = IRB.conf[:RC]
+ @load_modules = IRB.conf[:LOAD_MODULES]
+
+ @use_readline = IRB.conf[:USE_READLINE]
+ @inspect_mode = IRB.conf[:INSPECT_MODE]
+
+ self.math_mode = IRB.conf[:MATH_MODE] if IRB.conf[:MATH_MODE]
+ self.use_tracer = IRB.conf[:USE_TRACER] if IRB.conf[:USE_TRACER]
+ self.use_loader = IRB.conf[:USE_LOADER] if IRB.conf[:USE_LOADER]
+ self.eval_history = IRB.conf[:EVAL_HISTORY] if IRB.conf[:EVAL_HISTORY]
+
+ @ignore_sigint = IRB.conf[:IGNORE_SIGINT]
+ @ignore_eof = IRB.conf[:IGNORE_EOF]
+
+ @back_trace_limit = IRB.conf[:BACK_TRACE_LIMIT]
+
+ self.prompt_mode = IRB.conf[:PROMPT_MODE]
+
+ if IRB.conf[:SINGLE_IRB] or !defined?(JobManager)
+ @irb_name = IRB.conf[:IRB_NAME]
+ else
+ @irb_name = "irb#"+IRB.JobManager.n_jobs.to_s
+ end
+ @irb_path = "(" + @irb_name + ")"
+
+ case input_method
+ when nil
+ case use_readline?
+ when nil
+ if (defined?(ReadlineInputMethod) && STDIN.tty? &&
+ IRB.conf[:PROMPT_MODE] != :INF_RUBY)
+ @io = ReadlineInputMethod.new
+ else
+ @io = StdioInputMethod.new
+ end
+ when false
+ @io = StdioInputMethod.new
+ when true
+ if defined?(ReadlineInputMethod)
+ @io = ReadlineInputMethod.new
+ else
+ @io = StdioInputMethod.new
+ end
+ end
+
+ when String
+ @io = FileInputMethod.new(input_method)
+ @irb_name = File.basename(input_method)
+ @irb_path = input_method
+ else
+ @io = input_method
+ end
+ self.save_history = IRB.conf[:SAVE_HISTORY] if IRB.conf[:SAVE_HISTORY]
+
+ if output_method
+ @output_method = output_method
+ else
+ @output_method = StdioOutputMethod.new
+ end
+
+ @verbose = IRB.conf[:VERBOSE]
+ @echo = IRB.conf[:ECHO]
+ if @echo.nil?
+ @echo = true
+ end
+ @debug_level = IRB.conf[:DEBUG_LEVEL]
+ end
+
+ def main
+ @workspace.main
+ end
+
+ attr_reader :workspace_home
+ attr_accessor :workspace
+ attr_reader :thread
+ attr_accessor :io
+
+ attr_accessor :irb
+ attr_accessor :ap_name
+ attr_accessor :rc
+ attr_accessor :load_modules
+ attr_accessor :irb_name
+ attr_accessor :irb_path
+
+ attr_reader :use_readline
+ attr_reader :inspect_mode
+
+ attr_reader :prompt_mode
+ attr_accessor :prompt_i
+ attr_accessor :prompt_s
+ attr_accessor :prompt_c
+ attr_accessor :prompt_n
+ attr_accessor :auto_indent_mode
+ attr_accessor :return_format
+
+ attr_accessor :ignore_sigint
+ attr_accessor :ignore_eof
+ attr_accessor :echo
+ attr_accessor :verbose
+ attr_reader :debug_level
+
+ attr_accessor :back_trace_limit
+
+ alias use_readline? use_readline
+ alias rc? rc
+ alias ignore_sigint? ignore_sigint
+ alias ignore_eof? ignore_eof
+ alias echo? echo
+
+ def verbose?
+ if @verbose.nil?
+ if defined?(ReadlineInputMethod) && @io.kind_of?(ReadlineInputMethod)
+ false
+ elsif !STDIN.tty? or @io.kind_of?(FileInputMethod)
+ true
+ else
+ false
+ end
+ end
+ end
+
+ def prompting?
+ verbose? || (STDIN.tty? && @io.kind_of?(StdioInputMethod) ||
+ (defined?(ReadlineInputMethod) && @io.kind_of?(ReadlineInputMethod)))
+ end
+
+ attr_reader :last_value
+
+ def set_last_value(value)
+ @last_value = value
+ @workspace.evaluate self, "_ = IRB.CurrentContext.last_value"
+ end
+
+ attr_reader :irb_name
+
+ def prompt_mode=(mode)
+ @prompt_mode = mode
+ pconf = IRB.conf[:PROMPT][mode]
+ @prompt_i = pconf[:PROMPT_I]
+ @prompt_s = pconf[:PROMPT_S]
+ @prompt_c = pconf[:PROMPT_C]
+ @prompt_n = pconf[:PROMPT_N]
+ @return_format = pconf[:RETURN]
+ if ai = pconf.include?(:AUTO_INDENT)
+ @auto_indent_mode = ai
+ else
+ @auto_indent_mode = IRB.conf[:AUTO_INDENT]
+ end
+ end
+
+ def inspect?
+ @inspect_mode.nil? or @inspect_mode
+ end
+
+ def file_input?
+ @io.class == FileInputMethod
+ end
+
+ def inspect_mode=(opt)
+ if opt
+ @inspect_mode = opt
+ else
+ @inspect_mode = !@inspect_mode
+ end
+ print "Switch to#{unless @inspect_mode; ' non';end} inspect mode.\n" if verbose?
+ @inspect_mode
+ end
+
+ def use_readline=(opt)
+ @use_readline = opt
+ print "use readline module\n" if @use_readline
+ end
+
+ def debug_level=(value)
+ @debug_level = value
+ RubyLex.debug_level = value
+ SLex.debug_level = value
+ end
+
+ def debug?
+ @debug_level > 0
+ end
+
+ def evaluate(line, line_no)
+ @line_no = line_no
+ set_last_value(@workspace.evaluate(self, line, irb_path, line_no))
+# @workspace.evaluate("_ = IRB.conf[:MAIN_CONTEXT]._")
+# @_ = @workspace.evaluate(line, irb_path, line_no)
+ end
+
+ alias __exit__ exit
+ def exit(ret = 0)
+ IRB.irb_exit(@irb, ret)
+ end
+
+ NOPRINTING_IVARS = ["@last_value"]
+ NO_INSPECTING_IVARS = ["@irb", "@io"]
+ IDNAME_IVARS = ["@prompt_mode"]
+
+ alias __inspect__ inspect
+ def inspect
+ array = []
+ for ivar in instance_variables.sort{|e1, e2| e1 <=> e2}
+ name = ivar.sub(/^@(.*)$/){$1}
+ val = instance_eval(ivar)
+ case ivar
+ when *NOPRINTING_IVARS
+ array.push format("conf.%s=%s", name, "...")
+ when *NO_INSPECTING_IVARS
+ array.push format("conf.%s=%s", name, val.to_s)
+ when *IDNAME_IVARS
+ array.push format("conf.%s=:%s", name, val.id2name)
+ else
+ array.push format("conf.%s=%s", name, val.inspect)
+ end
+ end
+ array.join("\n")
+ end
+ alias __to_s__ to_s
+ alias to_s inspect
+ end
+end
diff --git a/ruby_1_8_5/lib/irb/ext/change-ws.rb b/ruby_1_8_5/lib/irb/ext/change-ws.rb
new file mode 100644
index 0000000000..d75fe485c8
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/ext/change-ws.rb
@@ -0,0 +1,62 @@
+#
+# irb/ext/cb.rb -
+# $Release Version: 0.9.5$
+# $Revision: 1.1.2.1 $
+# $Date: 2005/04/19 19:24:58 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+
+module IRB
+ class Context
+
+ def home_workspace
+ if defined? @home_workspace
+ @home_workspace
+ else
+ @home_workspace = @workspace
+ end
+ end
+
+ def change_workspace(*_main)
+ if _main.empty?
+ @workspace = home_workspace
+ return main
+ end
+
+ @workspace = WorkSpace.new(_main[0])
+
+ if !(class<<main;ancestors;end).include?(ExtendCommandBundle)
+ main.extend ExtendCommandBundle
+ end
+ end
+
+# def change_binding(*_main)
+# back = @workspace
+# @workspace = WorkSpace.new(*_main)
+# unless _main.empty?
+# begin
+# main.extend ExtendCommandBundle
+# rescue
+# print "can't change binding to: ", main.inspect, "\n"
+# @workspace = back
+# return nil
+# end
+# end
+# @irb_level += 1
+# begin
+# catch(:SU_EXIT) do
+# @irb.eval_input
+# end
+# ensure
+# @irb_level -= 1
+# @workspace = back
+# end
+# end
+# alias change_workspace change_binding
+ end
+end
+
diff --git a/ruby_1_8_5/lib/irb/ext/history.rb b/ruby_1_8_5/lib/irb/ext/history.rb
new file mode 100644
index 0000000000..2354ca96f7
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/ext/history.rb
@@ -0,0 +1,110 @@
+#
+# history.rb -
+# $Release Version: 0.9.5$
+# $Revision: 1.1.2.2 $
+# $Date: 2005/04/19 19:24:58 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+
+module IRB
+
+ class Context
+
+ NOPRINTING_IVARS.push "@eval_history_values"
+
+ alias _set_last_value set_last_value
+
+ def set_last_value(value)
+ _set_last_value(value)
+
+# @workspace.evaluate self, "_ = IRB.CurrentContext.last_value"
+ if @eval_history #and !@eval_history_values.equal?(llv)
+ @eval_history_values.push @line_no, @last_value
+ @workspace.evaluate self, "__ = IRB.CurrentContext.instance_eval{@eval_history_values}"
+ end
+
+ @last_value
+ end
+
+ attr_reader :eval_history
+ def eval_history=(no)
+ if no
+ if defined?(@eval_history) && @eval_history
+ @eval_history_values.size(no)
+ else
+ @eval_history_values = History.new(no)
+ IRB.conf[:__TMP__EHV__] = @eval_history_values
+ @workspace.evaluate(self, "__ = IRB.conf[:__TMP__EHV__]")
+ IRB.conf.delete(:__TMP_EHV__)
+ end
+ else
+ @eval_history_values = nil
+ end
+ @eval_history = no
+ end
+ end
+
+ class History
+ @RCS_ID='-$Id: history.rb,v 1.1.2.2 2005/04/19 19:24:58 keiju Exp $-'
+
+ def initialize(size = 16)
+ @size = size
+ @contents = []
+ end
+
+ def size(size)
+ if size != 0 && size < @size
+ @contents = @contents[@size - size .. @size]
+ end
+ @size = size
+ end
+
+ def [](idx)
+ begin
+ if idx >= 0
+ @contents.find{|no, val| no == idx}[1]
+ else
+ @contents[idx][1]
+ end
+ rescue NameError
+ nil
+ end
+ end
+
+ def push(no, val)
+ @contents.push [no, val]
+ @contents.shift if @size != 0 && @contents.size > @size
+ end
+
+ alias real_inspect inspect
+
+ def inspect
+ if @contents.empty?
+ return real_inspect
+ end
+
+ unless (last = @contents.pop)[1].equal?(self)
+ @contents.push last
+ last = nil
+ end
+ str = @contents.collect{|no, val|
+ if val.equal?(self)
+ "#{no} ...self-history..."
+ else
+ "#{no} #{val.inspect}"
+ end
+ }.join("\n")
+ if str == ""
+ str = "Empty."
+ end
+ @contents.push last if last
+ str
+ end
+ end
+end
+
+
diff --git a/ruby_1_8_5/lib/irb/ext/loader.rb b/ruby_1_8_5/lib/irb/ext/loader.rb
new file mode 100644
index 0000000000..67fdc31c8e
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/ext/loader.rb
@@ -0,0 +1,120 @@
+#
+# loader.rb -
+# $Release Version: 0.9.5$
+# $Revision: 1.1.2.1 $
+# $Date: 2005/04/19 19:24:58 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+
+
+module IRB
+ class LoadAbort < Exception;end
+
+ module IrbLoader
+ @RCS_ID='-$Id: loader.rb,v 1.1.2.1 2005/04/19 19:24:58 keiju Exp $-'
+
+ alias ruby_load load
+ alias ruby_require require
+
+ def irb_load(fn, priv = nil)
+ path = search_file_from_ruby_path(fn)
+ raise LoadError, "No such file to load -- #{fn}" unless path
+
+ load_file(path, priv)
+ end
+
+ def search_file_from_ruby_path(fn)
+ if /^#{Regexp.quote(File::Separator)}/ =~ fn
+ return fn if File.exist?(fn)
+ return nil
+ end
+
+ for path in $:
+ if File.exist?(f = File.join(path, fn))
+ return f
+ end
+ end
+ return nil
+ end
+
+ def source_file(path)
+ irb.suspend_name(path, File.basename(path)) do
+ irb.suspend_input_method(FileInputMethod.new(path)) do
+ |back_io|
+ irb.signal_status(:IN_LOAD) do
+ if back_io.kind_of?(FileInputMethod)
+ irb.eval_input
+ else
+ begin
+ irb.eval_input
+ rescue LoadAbort
+ print "load abort!!\n"
+ end
+ end
+ end
+ end
+ end
+ end
+
+ def load_file(path, priv = nil)
+ irb.suspend_name(path, File.basename(path)) do
+
+ if priv
+ ws = WorkSpace.new(Module.new)
+ else
+ ws = WorkSpace.new
+ end
+ irb.suspend_workspace(ws) do
+ irb.suspend_input_method(FileInputMethod.new(path)) do
+ |back_io|
+ irb.signal_status(:IN_LOAD) do
+# p irb.conf
+ if back_io.kind_of?(FileInputMethod)
+ irb.eval_input
+ else
+ begin
+ irb.eval_input
+ rescue LoadAbort
+ print "load abort!!\n"
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ def old
+ back_io = @io
+ back_path = @irb_path
+ back_name = @irb_name
+ back_scanner = @irb.scanner
+ begin
+ @io = FileInputMethod.new(path)
+ @irb_name = File.basename(path)
+ @irb_path = path
+ @irb.signal_status(:IN_LOAD) do
+ if back_io.kind_of?(FileInputMethod)
+ @irb.eval_input
+ else
+ begin
+ @irb.eval_input
+ rescue LoadAbort
+ print "load abort!!\n"
+ end
+ end
+ end
+ ensure
+ @io = back_io
+ @irb_name = back_name
+ @irb_path = back_path
+ @irb.scanner = back_scanner
+ end
+ end
+ end
+end
+
diff --git a/ruby_1_8_5/lib/irb/ext/math-mode.rb b/ruby_1_8_5/lib/irb/ext/math-mode.rb
new file mode 100644
index 0000000000..dfb01f8202
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/ext/math-mode.rb
@@ -0,0 +1,37 @@
+#
+# math-mode.rb -
+# $Release Version: 0.9.5$
+# $Revision: 1.2.2.1 $
+# $Date: 2005/04/19 19:24:58 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+require "mathn"
+
+module IRB
+ class Context
+ attr_reader :math_mode
+ alias math? math_mode
+
+ def math_mode=(opt)
+ if @math_mode == true && opt == false
+ IRB.fail CantReturnToNormalMode
+ return
+ end
+
+ @math_mode = opt
+ if math_mode
+ main.extend Math
+ print "start math mode\n" if verbose?
+ end
+ end
+
+ def inspect?
+ @inspect_mode.nil? && !@math_mode or @inspect_mode
+ end
+ end
+end
+
diff --git a/ruby_1_8_5/lib/irb/ext/multi-irb.rb b/ruby_1_8_5/lib/irb/ext/multi-irb.rb
new file mode 100644
index 0000000000..946840fd40
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/ext/multi-irb.rb
@@ -0,0 +1,241 @@
+#
+# irb/multi-irb.rb - multiple irb module
+# $Release Version: 0.9.5$
+# $Revision: 1.2.2.1 $
+# $Date: 2005/04/19 19:24:58 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+IRB.fail CantShiftToMultiIrbMode unless defined?(Thread)
+require "thread"
+
+module IRB
+ # job management class
+ class JobManager
+ @RCS_ID='-$Id: multi-irb.rb,v 1.2.2.1 2005/04/19 19:24:58 keiju Exp $-'
+
+ def initialize
+ # @jobs = [[thread, irb],...]
+ @jobs = []
+ @current_job = nil
+ end
+
+ attr_accessor :current_job
+
+ def n_jobs
+ @jobs.size
+ end
+
+ def thread(key)
+ th, irb = search(key)
+ th
+ end
+
+ def irb(key)
+ th, irb = search(key)
+ irb
+ end
+
+ def main_thread
+ @jobs[0][0]
+ end
+
+ def main_irb
+ @jobs[0][1]
+ end
+
+ def insert(irb)
+ @jobs.push [Thread.current, irb]
+ end
+
+ def switch(key)
+ th, irb = search(key)
+ IRB.fail IrbAlreadyDead unless th.alive?
+ IRB.fail IrbSwitchedToCurrentThread if th == Thread.current
+ @current_job = irb
+ th.run
+ Thread.stop
+ @current_job = irb(Thread.current)
+ end
+
+ def kill(*keys)
+ for key in keys
+ th, irb = search(key)
+ IRB.fail IrbAlreadyDead unless th.alive?
+ th.exit
+ end
+ end
+
+ def search(key)
+ case key
+ when Integer
+ @jobs[key]
+ when Irb
+ @jobs.find{|k, v| v.equal?(key)}
+ when Thread
+ @jobs.assoc(key)
+ else
+ assoc = @jobs.find{|k, v| v.context.main.equal?(key)}
+ IRB.fail NoSuchJob, key if assoc.nil?
+ assoc
+ end
+ end
+
+ def delete(key)
+ case key
+ when Integer
+ IRB.fail NoSuchJob, key unless @jobs[key]
+ @jobs[key] = nil
+ else
+ catch(:EXISTS) do
+ @jobs.each_index do
+ |i|
+ if @jobs[i] and (@jobs[i][0] == key ||
+ @jobs[i][1] == key ||
+ @jobs[i][1].context.main.equal?(key))
+ @jobs[i] = nil
+ throw :EXISTS
+ end
+ end
+ IRB.fail NoSuchJob, key
+ end
+ end
+ until assoc = @jobs.pop; end unless @jobs.empty?
+ @jobs.push assoc
+ end
+
+ def inspect
+ ary = []
+ @jobs.each_index do
+ |i|
+ th, irb = @jobs[i]
+ next if th.nil?
+
+ if th.alive?
+ if th.stop?
+ t_status = "stop"
+ else
+ t_status = "running"
+ end
+ else
+ t_status = "exited"
+ end
+ ary.push format("#%d->%s on %s (%s: %s)",
+ i,
+ irb.context.irb_name,
+ irb.context.main,
+ th,
+ t_status)
+ end
+ ary.join("\n")
+ end
+ end
+
+ @JobManager = JobManager.new
+
+ def IRB.JobManager
+ @JobManager
+ end
+
+ def IRB.CurrentContext
+ IRB.JobManager.irb(Thread.current).context
+ end
+
+ # invoke multi-irb
+ def IRB.irb(file = nil, *main)
+ workspace = WorkSpace.new(*main)
+ parent_thread = Thread.current
+ Thread.start do
+ begin
+ irb = Irb.new(workspace, file)
+ rescue
+ print "Subirb can't start with context(self): ", workspace.main.inspect, "\n"
+ print "return to main irb\n"
+ Thread.pass
+ Thread.main.wakeup
+ Thread.exit
+ end
+ @CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
+ @JobManager.insert(irb)
+ @JobManager.current_job = irb
+ begin
+ system_exit = false
+ catch(:IRB_EXIT) do
+ irb.eval_input
+ end
+ rescue SystemExit
+ system_exit = true
+ raise
+ #fail
+ ensure
+ unless system_exit
+ @JobManager.delete(irb)
+ if parent_thread.alive?
+ @JobManager.current_job = @JobManager.irb(parent_thread)
+ parent_thread.run
+ else
+ @JobManager.current_job = @JobManager.main_irb
+ @JobManager.main_thread.run
+ end
+ end
+ end
+ end
+ Thread.stop
+ @JobManager.current_job = @JobManager.irb(Thread.current)
+ end
+
+# class Context
+# def set_last_value(value)
+# @last_value = value
+# @workspace.evaluate "_ = IRB.JobManager.irb(Thread.current).context.last_value"
+# if @eval_history #and !@__.equal?(@last_value)
+# @eval_history_values.push @line_no, @last_value
+# @workspace.evaluate "__ = IRB.JobManager.irb(Thread.current).context.instance_eval{@eval_history_values}"
+# end
+# @last_value
+# end
+# end
+
+# module ExtendCommand
+# def irb_context
+# IRB.JobManager.irb(Thread.current).context
+# end
+# # alias conf irb_context
+# end
+
+ @CONF[:SINGLE_IRB_MODE] = false
+ @JobManager.insert(@CONF[:MAIN_CONTEXT].irb)
+ @JobManager.current_job = @CONF[:MAIN_CONTEXT].irb
+
+ class Irb
+ def signal_handle
+ unless @context.ignore_sigint?
+ print "\nabort!!\n" if @context.verbose?
+ exit
+ end
+
+ case @signal_status
+ when :IN_INPUT
+ print "^C\n"
+ IRB.JobManager.thread(self).raise RubyLex::TerminateLineInput
+ when :IN_EVAL
+ IRB.irb_abort(self)
+ when :IN_LOAD
+ IRB.irb_abort(self, LoadAbort)
+ when :IN_IRB
+ # ignore
+ else
+ # ignore other cases as well
+ end
+ end
+ end
+
+ trap("SIGINT") do
+ @JobManager.current_job.signal_handle
+ Thread.stop
+ end
+
+end
diff --git a/ruby_1_8_5/lib/irb/ext/save-history.rb b/ruby_1_8_5/lib/irb/ext/save-history.rb
new file mode 100644
index 0000000000..889d81d5be
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/ext/save-history.rb
@@ -0,0 +1,85 @@
+#!/usr/local/bin/ruby
+#
+# save-history.rb -
+# $Release Version: 0.9.5$
+# $Revision: 1.2.4.1 $
+# $Date: 2005/04/19 19:24:58 $
+# by Keiju ISHITSUKAkeiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+
+require "readline"
+
+module IRB
+ module HistorySavingAbility
+ @RCS_ID='-$Id: save-history.rb,v 1.2.4.1 2005/04/19 19:24:58 keiju Exp $-'
+ end
+
+ class Context
+ def init_save_history
+ unless (class<<@io;self;end).include?(HistorySavingAbility)
+ @io.extend(HistorySavingAbility)
+ end
+ end
+
+ def save_history
+ IRB.conf[:SAVE_HISTORY]
+ end
+
+ def save_history=(val)
+ IRB.conf[:SAVE_HISTORY] = val
+ if val
+ main_context = IRB.conf[:MAIN_CONTEXT]
+ main_context = self unless main_context
+ main_context.init_save_history
+ end
+ end
+
+ def history_file
+ IRB.conf[:HISTORY_FILE]
+ end
+
+ def history_file=(hist)
+ IRB.conf[:HISTORY_FILE] = hist
+ end
+ end
+
+ module HistorySavingAbility
+ include Readline
+
+ def HistorySavingAbility.create_finalizer
+ proc do
+ if num = IRB.conf[:SAVE_HISTORY] and (num = num.to_i) > 0
+ if hf = IRB.conf[:HISTORY_FILE]
+ file = File.expand_path(hf)
+ end
+ file = IRB.rc_file("_history") unless file
+ open(file, 'w' ) do |f|
+ hist = HISTORY.to_a
+ f.puts(hist[-num..-1] || hist)
+ end
+ end
+ end
+ end
+
+ def HistorySavingAbility.extended(obj)
+ ObjectSpace.define_finalizer(obj, HistorySavingAbility.create_finalizer)
+ obj.load_history
+ obj
+ end
+
+ def load_history
+ hist = IRB.conf[:HISTORY_FILE]
+ hist = IRB.rc_file("_history") unless hist
+ if File.exist?(hist)
+ open(hist) do |f|
+ f.each {|l| HISTORY << l.chomp}
+ end
+ end
+ end
+ end
+end
+
diff --git a/ruby_1_8_5/lib/irb/ext/tracer.rb b/ruby_1_8_5/lib/irb/ext/tracer.rb
new file mode 100644
index 0000000000..8660bfed17
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/ext/tracer.rb
@@ -0,0 +1,61 @@
+#
+# irb/lib/tracer.rb -
+# $Release Version: 0.9.5$
+# $Revision: 1.1.2.1 $
+# $Date: 2005/04/19 19:24:58 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+require "tracer"
+
+module IRB
+
+ # initialize tracing function
+ def IRB.initialize_tracer
+ Tracer.verbose = false
+ Tracer.add_filter {
+ |event, file, line, id, binding, *rests|
+ /^#{Regexp.quote(@CONF[:IRB_LIB_PATH])}/ !~ file and
+ File::basename(file) != "irb.rb"
+ }
+ end
+
+ class Context
+ attr_reader :use_tracer
+ alias use_tracer? use_tracer
+
+ def use_tracer=(opt)
+ if opt
+ Tracer.set_get_line_procs(@irb_path) {
+ |line_no, *rests|
+ @io.line(line_no)
+ }
+ elsif !opt && @use_tracer
+ Tracer.off
+ end
+ @use_tracer=opt
+ end
+ end
+
+ class WorkSpace
+ alias __evaluate__ evaluate
+ def evaluate(context, statements, file = nil, line = nil)
+ if context.use_tracer? && file != nil && line != nil
+ Tracer.on
+ begin
+ __evaluate__(context, statements, file, line)
+ ensure
+ Tracer.off
+ end
+ else
+ __evaluate__(context, statements, file || __FILE__, line || __LINE__)
+ end
+ end
+ end
+
+ IRB.initialize_tracer
+end
+
diff --git a/ruby_1_8_5/lib/irb/ext/use-loader.rb b/ruby_1_8_5/lib/irb/ext/use-loader.rb
new file mode 100644
index 0000000000..68bd1dd592
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/ext/use-loader.rb
@@ -0,0 +1,65 @@
+#
+# use-loader.rb -
+# $Release Version: 0.9.5$
+# $Revision: 1.1.2.1 $
+# $Date: 2005/04/19 19:24:58 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+
+require "irb/cmd/load"
+require "irb/ext/loader"
+
+class Object
+ alias __original__load__IRB_use_loader__ load
+ alias __original__require__IRB_use_loader__ require
+end
+
+module IRB
+ module ExtendCommandBundle
+ def irb_load(*opts, &b)
+ ExtendCommand::Load.execute(irb_context, *opts, &b)
+ end
+ def irb_require(*opts, &b)
+ ExtendCommand::Require.execute(irb_context, *opts, &b)
+ end
+ end
+
+ class Context
+
+ IRB.conf[:USE_LOADER] = false
+
+ def use_loader
+ IRB.conf[:USE_LOADER]
+ end
+
+ alias use_loader? use_loader
+
+ def use_loader=(opt)
+
+ if IRB.conf[:USE_LOADER] != opt
+ IRB.conf[:USE_LOADER] = opt
+ if opt
+ if !$".include?("irb/cmd/load")
+ end
+ (class<<@workspace.main;self;end).instance_eval {
+ alias_method :load, :irb_load
+ alias_method :require, :irb_require
+ }
+ else
+ (class<<@workspace.main;self;end).instance_eval {
+ alias_method :load, :__original__load__IRB_use_loader__
+ alias_method :require, :__original__require__IRB_use_loader__
+ }
+ end
+ end
+ print "Switch to load/require#{unless use_loader; ' non';end} trace mode.\n" if verbose?
+ opt
+ end
+ end
+end
+
+
diff --git a/ruby_1_8_5/lib/irb/ext/workspaces.rb b/ruby_1_8_5/lib/irb/ext/workspaces.rb
new file mode 100644
index 0000000000..b09e52ca34
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/ext/workspaces.rb
@@ -0,0 +1,56 @@
+#
+# push-ws.rb -
+# $Release Version: 0.9.5$
+# $Revision: 1.1.2.1 $
+# $Date: 2005/04/19 19:24:58 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+
+module IRB
+ class Context
+
+ def irb_level
+ workspace_stack.size
+ end
+
+ def workspaces
+ if defined? @workspaces
+ @workspaces
+ else
+ @workspaces = []
+ end
+ end
+
+ def push_workspace(*_main)
+ if _main.empty?
+ if workspaces.empty?
+ print "No other workspace\n"
+ return nil
+ end
+ ws = workspaces.pop
+ workspaces.push @workspace
+ @workspace = ws
+ return workspaces
+ end
+
+ workspaces.push @workspace
+ @workspace = WorkSpace.new(@workspace.binding, _main[0])
+ if !(class<<main;ancestors;end).include?(ExtendCommandBundle)
+ main.extend ExtendCommandBundle
+ end
+ end
+
+ def pop_workspace
+ if workspaces.empty?
+ print "workspace stack empty\n"
+ return
+ end
+ @workspace = workspaces.pop
+ end
+ end
+end
+
diff --git a/ruby_1_8_5/lib/irb/extend-command.rb b/ruby_1_8_5/lib/irb/extend-command.rb
new file mode 100644
index 0000000000..934f9f6fbb
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/extend-command.rb
@@ -0,0 +1,264 @@
+#
+# irb/extend-command.rb - irb extend command
+# $Release Version: 0.9.5$
+# $Revision: 1.4.2.3 $
+# $Date: 2006/08/08 15:16:21 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+module IRB
+ #
+ # IRB extended command
+ #
+ module ExtendCommandBundle
+ EXCB = ExtendCommandBundle
+
+ NO_OVERRIDE = 0
+ OVERRIDE_PRIVATE_ONLY = 0x01
+ OVERRIDE_ALL = 0x02
+
+ def irb_exit(ret = 0)
+ irb_context.exit(ret)
+ end
+
+ def irb_context
+ IRB.CurrentContext
+ end
+
+ @ALIASES = [
+ [:context, :irb_context, NO_OVERRIDE],
+ [:conf, :irb_context, NO_OVERRIDE],
+ [:irb_quit, :irb_exit, OVERRIDE_PRIVATE_ONLY],
+ [:exit, :irb_exit, OVERRIDE_PRIVATE_ONLY],
+ [:quit, :irb_exit, OVERRIDE_PRIVATE_ONLY],
+ ]
+
+ @EXTEND_COMMANDS = [
+ [:irb_current_working_workspace, :CurrentWorkingWorkspace, "irb/cmd/chws",
+ [:irb_print_working_workspace, OVERRIDE_ALL],
+ [:irb_cwws, OVERRIDE_ALL],
+ [:irb_pwws, OVERRIDE_ALL],
+# [:irb_cww, OVERRIDE_ALL],
+# [:irb_pww, OVERRIDE_ALL],
+ [:cwws, NO_OVERRIDE],
+ [:pwws, NO_OVERRIDE],
+# [:cww, NO_OVERRIDE],
+# [:pww, NO_OVERRIDE],
+ [:irb_current_working_binding, OVERRIDE_ALL],
+ [:irb_print_working_binding, OVERRIDE_ALL],
+ [:irb_cwb, OVERRIDE_ALL],
+ [:irb_pwb, OVERRIDE_ALL],
+# [:cwb, NO_OVERRIDE],
+# [:pwb, NO_OVERRIDE]
+ ],
+ [:irb_change_workspace, :ChangeWorkspace, "irb/cmd/chws",
+ [:irb_chws, OVERRIDE_ALL],
+# [:irb_chw, OVERRIDE_ALL],
+ [:irb_cws, OVERRIDE_ALL],
+# [:irb_cw, OVERRIDE_ALL],
+ [:chws, NO_OVERRIDE],
+# [:chw, NO_OVERRIDE],
+ [:cws, NO_OVERRIDE],
+# [:cw, NO_OVERRIDE],
+ [:irb_change_binding, OVERRIDE_ALL],
+ [:irb_cb, OVERRIDE_ALL],
+ [:cb, NO_OVERRIDE]],
+
+ [:irb_workspaces, :Workspaces, "irb/cmd/pushws",
+ [:workspaces, NO_OVERRIDE],
+ [:irb_bindings, OVERRIDE_ALL],
+ [:bindings, NO_OVERRIDE]],
+ [:irb_push_workspace, :PushWorkspace, "irb/cmd/pushws",
+ [:irb_pushws, OVERRIDE_ALL],
+# [:irb_pushw, OVERRIDE_ALL],
+ [:pushws, NO_OVERRIDE],
+# [:pushw, NO_OVERRIDE],
+ [:irb_push_binding, OVERRIDE_ALL],
+ [:irb_pushb, OVERRIDE_ALL],
+ [:pushb, NO_OVERRIDE]],
+ [:irb_pop_workspace, :PopWorkspace, "irb/cmd/pushws",
+ [:irb_popws, OVERRIDE_ALL],
+# [:irb_popw, OVERRIDE_ALL],
+ [:popws, NO_OVERRIDE],
+# [:popw, NO_OVERRIDE],
+ [:irb_pop_binding, OVERRIDE_ALL],
+ [:irb_popb, OVERRIDE_ALL],
+ [:popb, NO_OVERRIDE]],
+
+ [:irb_load, :Load, "irb/cmd/load"],
+ [:irb_require, :Require, "irb/cmd/load"],
+ [:irb_source, :Source, "irb/cmd/load",
+ [:source, NO_OVERRIDE]],
+
+ [:irb, :IrbCommand, "irb/cmd/subirb"],
+ [:irb_jobs, :Jobs, "irb/cmd/subirb",
+ [:jobs, NO_OVERRIDE]],
+ [:irb_fg, :Foreground, "irb/cmd/subirb",
+ [:fg, NO_OVERRIDE]],
+ [:irb_kill, :Kill, "irb/cmd/subirb",
+ [:kill, OVERRIDE_PRIVATE_ONLY]],
+
+ [:irb_help, :Help, "irb/cmd/help",
+ [:help, NO_OVERRIDE]],
+
+ ]
+
+ def self.install_extend_commands
+ for args in @EXTEND_COMMANDS
+ def_extend_command(*args)
+ end
+ end
+
+ # aliases = [commans_alias, flag], ...
+ def self.def_extend_command(cmd_name, cmd_class, load_file = nil, *aliases)
+ case cmd_class
+ when Symbol
+ cmd_class = cmd_class.id2name
+ when String
+ when Class
+ cmd_class = cmd_class.name
+ end
+
+ if load_file
+ eval %[
+ def #{cmd_name}(*opts, &b)
+ require "#{load_file}"
+ eval %[
+ def #{cmd_name}(*opts, &b)
+ ExtendCommand::#{cmd_class}.execute(irb_context, *opts, &b)
+ end
+ ]
+ send :#{cmd_name}, *opts, &b
+ end
+ ]
+ else
+ eval %[
+ def #{cmd_name}(*opts, &b)
+ ExtendCommand::#{cmd_class}.execute(irb_context, *opts, &b)
+ end
+ ]
+ end
+
+ for ali, flag in aliases
+ @ALIASES.push [ali, cmd_name, flag]
+ end
+ end
+
+ # override = {NO_OVERRIDE, OVERRIDE_PRIVATE_ONLY, OVERRIDE_ALL}
+ def install_alias_method(to, from, override = NO_OVERRIDE)
+ to = to.id2name unless to.kind_of?(String)
+ from = from.id2name unless from.kind_of?(String)
+
+ if override == OVERRIDE_ALL or
+ (override == OVERRIDE_PRIVATE_ONLY) && !respond_to?(to) or
+ (override == NO_OVERRIDE) && !respond_to?(to, true)
+ target = self
+ (class<<self;self;end).instance_eval{
+ if target.respond_to?(to, true) &&
+ !target.respond_to?(EXCB.irb_original_method_name(to), true)
+ alias_method(EXCB.irb_original_method_name(to), to)
+ end
+ alias_method to, from
+ }
+ else
+ print "irb: warn: can't alias #{to} from #{from}.\n"
+ end
+ end
+
+ def self.irb_original_method_name(method_name)
+ "irb_" + method_name + "_org"
+ end
+
+ def self.extend_object(obj)
+ unless (class<<obj;ancestors;end).include?(EXCB)
+ super
+ for ali, com, flg in @ALIASES
+ obj.install_alias_method(ali, com, flg)
+ end
+ end
+ end
+
+ install_extend_commands
+ end
+
+ # extension support for Context
+ module ContextExtender
+ CE = ContextExtender
+
+ @EXTEND_COMMANDS = [
+ [:eval_history=, "irb/ext/history.rb"],
+ [:use_tracer=, "irb/ext/tracer.rb"],
+ [:math_mode=, "irb/ext/math-mode.rb"],
+ [:use_loader=, "irb/ext/use-loader.rb"],
+ [:save_history=, "irb/ext/save-history.rb"],
+ ]
+
+ def self.install_extend_commands
+ for args in @EXTEND_COMMANDS
+ def_extend_command(*args)
+ end
+ end
+
+ def self.def_extend_command(cmd_name, load_file, *aliases)
+ Context.module_eval %[
+ def #{cmd_name}(*opts, &b)
+ Context.module_eval {remove_method(:#{cmd_name})}
+ require "#{load_file}"
+ send :#{cmd_name}, *opts, &b
+ end
+ for ali in aliases
+ alias_method ali, cmd_name
+ end
+ ]
+ end
+
+ CE.install_extend_commands
+ end
+
+ module MethodExtender
+ def def_pre_proc(base_method, extend_method)
+ base_method = base_method.to_s
+ extend_method = extend_method.to_s
+
+ alias_name = new_alias_name(base_method)
+ module_eval %[
+ alias_method alias_name, base_method
+ def #{base_method}(*opts)
+ send :#{extend_method}, *opts
+ send :#{alias_name}, *opts
+ end
+ ]
+ end
+
+ def def_post_proc(base_method, extend_method)
+ base_method = base_method.to_s
+ extend_method = extend_method.to_s
+
+ alias_name = new_alias_name(base_method)
+ module_eval %[
+ alias_method alias_name, base_method
+ def #{base_method}(*opts)
+ send :#{alias_name}, *opts
+ send :#{extend_method}, *opts
+ end
+ ]
+ end
+
+ # return #{prefix}#{name}#{postfix}<num>
+ def new_alias_name(name, prefix = "__alias_of__", postfix = "__")
+ base_name = "#{prefix}#{name}#{postfix}"
+ all_methods = instance_methods(true) + private_instance_methods(true)
+ same_methods = all_methods.grep(/^#{Regexp.quote(base_name)}[0-9]*$/)
+ return base_name if same_methods.empty?
+ no = same_methods.size
+ while !same_methods.include?(alias_name = base_name + no)
+ no += 1
+ end
+ alias_name
+ end
+ end
+end
+
diff --git a/ruby_1_8_5/lib/irb/frame.rb b/ruby_1_8_5/lib/irb/frame.rb
new file mode 100644
index 0000000000..a6f15d23e9
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/frame.rb
@@ -0,0 +1,67 @@
+#
+# frame.rb -
+# $Release Version: 0.9$
+# $Revision: 1.4 $
+# $Date: 2002/07/09 11:17:16 $
+# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
+#
+# --
+#
+#
+#
+
+require "e2mmap"
+
+module IRB
+ class Frame
+ extend Exception2MessageMapper
+ def_exception :FrameOverflow, "frame overflow"
+ def_exception :FrameUnderflow, "frame underflow"
+
+ INIT_STACK_TIMES = 3
+ CALL_STACK_OFFSET = 3
+
+ def initialize
+ @frames = [TOPLEVEL_BINDING] * INIT_STACK_TIMES
+ end
+
+ def trace_func(event, file, line, id, binding)
+ case event
+ when 'call', 'class'
+ @frames.push binding
+ when 'return', 'end'
+ @frames.pop
+ end
+ end
+
+ def top(n = 0)
+ bind = @frames[-(n + CALL_STACK_OFFSET)]
+ Fail FrameUnderflow unless bind
+ bind
+ end
+
+ def bottom(n = 0)
+ bind = @frames[n]
+ Fail FrameOverflow unless bind
+ bind
+ end
+
+ # singleton functions
+ def Frame.bottom(n = 0)
+ @backtrace.bottom(n)
+ end
+
+ def Frame.top(n = 0)
+ @backtrace.top(n)
+ end
+
+ def Frame.sender
+ eval "self", @backtrace.top
+ end
+
+ @backtrace = Frame.new
+ set_trace_func proc{|event, file, line, id, binding, klass|
+ @backtrace.trace_func(event, file, line, id, binding)
+ }
+ end
+end
diff --git a/ruby_1_8_5/lib/irb/help.rb b/ruby_1_8_5/lib/irb/help.rb
new file mode 100644
index 0000000000..e239fdda4c
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/help.rb
@@ -0,0 +1,33 @@
+#
+# irb/help.rb - print usase module
+# $Release Version: 0.9.5$
+# $Revision: 1.2.2.1 $
+# $Date: 2005/04/19 19:24:57 $
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
+#
+# --
+#
+#
+#
+
+module IRB
+ def IRB.print_usage
+ lc = IRB.conf[:LC_MESSAGES]
+ path = lc.find("irb/help-message")
+ space_line = false
+ File.foreach(path) do
+ |l|
+ if /^\s*$/ =~ l
+ lc.puts l unless space_line
+ space_line = true
+ next
+ end
+ space_line = false
+
+ l.sub!(/#.*$/, "")
+ next if /^\s*$/ =~ l
+ lc.puts l
+ end
+ end
+end
+
diff --git a/ruby_1_8_5/lib/irb/init.rb b/ruby_1_8_5/lib/irb/init.rb
new file mode 100644
index 0000000000..938e6e3d29
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/init.rb
@@ -0,0 +1,259 @@
+#
+# irb/init.rb - irb initialize module
+# $Release Version: 0.9.5$
+# $Revision: 1.6.2.6 $
+# $Date: 2006/08/04 10:15:49 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+
+module IRB
+
+ # initialize config
+ def IRB.setup(ap_path)
+ IRB.init_config(ap_path)
+ IRB.init_error
+ IRB.parse_opts
+ IRB.run_config
+ IRB.load_modules
+
+ unless @CONF[:PROMPT][@CONF[:PROMPT_MODE]]
+ IRB.fail(UndefinedPromptMode, @CONF[:PROMPT_MODE])
+ end
+ end
+
+ # @CONF default setting
+ def IRB.init_config(ap_path)
+ # class instance variables
+ @TRACER_INITIALIZED = false
+
+ # default configurations
+ unless ap_path and @CONF[:AP_NAME]
+ ap_path = File.join(File.dirname(File.dirname(__FILE__)), "irb.rb")
+ end
+ @CONF[:AP_NAME] = File::basename(ap_path, ".rb")
+
+ @CONF[:IRB_NAME] = "irb"
+ @CONF[:IRB_LIB_PATH] = File.dirname(__FILE__)
+
+ @CONF[:RC] = true
+ @CONF[:LOAD_MODULES] = []
+ @CONF[:IRB_RC] = nil
+
+ @CONF[:MATH_MODE] = false
+ @CONF[:USE_READLINE] = false unless defined?(ReadlineInputMethod)
+ @CONF[:INSPECT_MODE] = nil
+ @CONF[:USE_TRACER] = false
+ @CONF[:USE_LOADER] = false
+ @CONF[:IGNORE_SIGINT] = true
+ @CONF[:IGNORE_EOF] = false
+ @CONF[:ECHO] = nil
+ @CONF[:VERBOSE] = nil
+
+ @CONF[:EVAL_HISTORY] = nil
+ @CONF[:SAVE_HISTORY] = nil
+
+ @CONF[:BACK_TRACE_LIMIT] = 16
+
+ @CONF[:PROMPT] = {
+ :NULL => {
+ :PROMPT_I => nil,
+ :PROMPT_N => nil,
+ :PROMPT_S => nil,
+ :PROMPT_C => nil,
+ :RETURN => "%s\n"
+ },
+ :DEFAULT => {
+ :PROMPT_I => "%N(%m):%03n:%i> ",
+ :PROMPT_N => "%N(%m):%03n:%i> ",
+ :PROMPT_S => "%N(%m):%03n:%i%l ",
+ :PROMPT_C => "%N(%m):%03n:%i* ",
+ :RETURN => "=> %s\n"
+ },
+ :CLASSIC => {
+ :PROMPT_I => "%N(%m):%03n:%i> ",
+ :PROMPT_N => "%N(%m):%03n:%i> ",
+ :PROMPT_S => "%N(%m):%03n:%i%l ",
+ :PROMPT_C => "%N(%m):%03n:%i* ",
+ :RETURN => "%s\n"
+ },
+ :SIMPLE => {
+ :PROMPT_I => ">> ",
+ :PROMPT_N => ">> ",
+ :PROMPT_S => nil,
+ :PROMPT_C => "?> ",
+ :RETURN => "=> %s\n"
+ },
+ :INF_RUBY => {
+ :PROMPT_I => "%N(%m):%03n:%i> ",
+# :PROMPT_N => "%N(%m):%03n:%i> ",
+ :PROMPT_N => nil,
+ :PROMPT_S => nil,
+ :PROMPT_C => nil,
+ :RETURN => "%s\n",
+ :AUTO_INDENT => true
+ },
+ :XMP => {
+ :PROMPT_I => nil,
+ :PROMPT_N => nil,
+ :PROMPT_S => nil,
+ :PROMPT_C => nil,
+ :RETURN => " ==>%s\n"
+ }
+ }
+
+ @CONF[:PROMPT_MODE] = (STDIN.tty? ? :DEFAULT : :NULL)
+ @CONF[:AUTO_INDENT] = false
+
+ @CONF[:CONTEXT_MODE] = 3 # use binding in function on TOPLEVEL_BINDING
+ @CONF[:SINGLE_IRB] = false
+
+# @CONF[:LC_MESSAGES] = "en"
+ @CONF[:LC_MESSAGES] = Locale.new
+
+ @CONF[:DEBUG_LEVEL] = 1
+ end
+
+ def IRB.init_error
+ @CONF[:LC_MESSAGES].load("irb/error.rb")
+ end
+
+ FEATURE_IOPT_CHANGE_VERSION = "1.9.0"
+
+ # option analyzing
+ def IRB.parse_opts
+ load_path = []
+ while opt = ARGV.shift
+ case opt
+ when "-f"
+ @CONF[:RC] = false
+ when "-m"
+ @CONF[:MATH_MODE] = true
+ when "-d"
+ $DEBUG = true
+ when /^-r(.+)?/
+ opt = $1 || ARGV.shift
+ @CONF[:LOAD_MODULES].push opt if opt
+ when /^-I(.+)?/
+ opt = $1 || ARGV.shift
+ load_path.concat(opt.split(File::PATH_SEPARATOR)) if opt
+ when /^-K(.)/
+ $KCODE = $1
+ when "--inspect"
+ @CONF[:INSPECT_MODE] = true
+ when "--noinspect"
+ @CONF[:INSPECT_MODE] = false
+ when "--readline"
+ @CONF[:USE_READLINE] = true
+ when "--noreadline"
+ @CONF[:USE_READLINE] = false
+ when "--echo"
+ @CONF[:ECHO] = true
+ when "--noecho"
+ @CONF[:ECHO] = false
+ when "--verbose"
+ @CONF[:VERBOSE] = true
+ when "--noverbose"
+ @CONF[:VERBOSE] = false
+ when "--prompt-mode", "--prompt"
+ prompt_mode = ARGV.shift.upcase.tr("-", "_").intern
+ @CONF[:PROMPT_MODE] = prompt_mode
+ when "--noprompt"
+ @CONF[:PROMPT_MODE] = :NULL
+ when "--inf-ruby-mode"
+ @CONF[:PROMPT_MODE] = :INF_RUBY
+ when "--sample-book-mode", "--simple-prompt"
+ @CONF[:PROMPT_MODE] = :SIMPLE
+ when "--tracer"
+ @CONF[:USE_TRACER] = true
+ when "--back-trace-limit"
+ @CONF[:BACK_TRACE_LIMIT] = ARGV.shift.to_i
+ when "--context-mode"
+ @CONF[:CONTEXT_MODE] = ARGV.shift.to_i
+ when "--single-irb"
+ @CONF[:SINGLE_IRB] = true
+ when "--irb_debug"
+ @CONF[:DEBUG_LEVEL] = ARGV.shift.to_i
+ when "-v", "--version"
+ print IRB.version, "\n"
+ exit 0
+ when "-h", "--help"
+ require "irb/help"
+ IRB.print_usage
+ exit 0
+ when /^-/
+ IRB.fail UnrecognizedSwitch, opt
+ else
+ @CONF[:SCRIPT] = opt
+ $0 = opt
+ break
+ end
+ end
+ if RUBY_VERSION >= FEATURE_IOPT_CHANGE_VERSION
+ load_path.collect! do |path|
+ /\A\.\// =~ path ? path : File.expand_path(path)
+ end
+ end
+ $LOAD_PATH.unshift(*load_path)
+ end
+
+ # running config
+ def IRB.run_config
+ if @CONF[:RC]
+ begin
+ load rc_file
+ rescue LoadError, Errno::ENOENT
+ rescue
+ print "load error: #{rc_file}\n"
+ print $!.class, ": ", $!, "\n"
+ for err in $@[0, $@.size - 2]
+ print "\t", err, "\n"
+ end
+ end
+ end
+ end
+
+ IRBRC_EXT = "rc"
+ def IRB.rc_file(ext = IRBRC_EXT)
+ if !@CONF[:RC_NAME_GENERATOR]
+ rc_file_generators do |rcgen|
+ @CONF[:RC_NAME_GENERATOR] ||= rcgen
+ if File.exist?(rcgen.call(IRBRC_EXT))
+ @CONF[:RC_NAME_GENERATOR] = rcgen
+ break
+ end
+ end
+ end
+ @CONF[:RC_NAME_GENERATOR].call ext
+ end
+
+ # enumerate possible rc-file base name generators
+ def IRB.rc_file_generators
+ if irbrc = ENV["IRBRC"]
+ yield proc{|rc| rc == "rc" ? irbrc : irbrc+rc}
+ end
+ if home = ENV["HOME"]
+ yield proc{|rc| home+"/.irb#{rc}"}
+ end
+ home = Dir.pwd
+ yield proc{|rc| home+"/.irb#{rc}"}
+ yield proc{|rc| home+"/irb#{rc.sub(/\A_?/, '.')}"}
+ yield proc{|rc| home+"/_irb#{rc}"}
+ yield proc{|rc| home+"/$irb#{rc}"}
+ end
+
+ # loading modules
+ def IRB.load_modules
+ for m in @CONF[:LOAD_MODULES]
+ begin
+ require m
+ rescue
+ print $@[0], ":", $!.class, ": ", $!, "\n"
+ end
+ end
+ end
+
+end
diff --git a/ruby_1_8_5/lib/irb/input-method.rb b/ruby_1_8_5/lib/irb/input-method.rb
new file mode 100644
index 0000000000..d6abadba76
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/input-method.rb
@@ -0,0 +1,120 @@
+#
+# irb/input-method.rb - input methods used irb
+# $Release Version: 0.9.5$
+# $Revision: 1.4.2.2 $
+# $Date: 2005/05/25 13:53:41 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+module IRB
+ #
+ # InputMethod
+ # StdioInputMethod
+ # FileInputMethod
+ # (ReadlineInputMethod)
+ #
+ STDIN_FILE_NAME = "(line)"
+ class InputMethod
+ @RCS_ID='-$Id: input-method.rb,v 1.4.2.2 2005/05/25 13:53:41 shugo Exp $-'
+
+ def initialize(file = STDIN_FILE_NAME)
+ @file_name = file
+ end
+ attr_reader :file_name
+
+ attr_accessor :prompt
+
+ def gets
+ IRB.fail NotImplementedError, "gets"
+ end
+ public :gets
+
+ def readable_atfer_eof?
+ false
+ end
+ end
+
+ class StdioInputMethod < InputMethod
+ def initialize
+ super
+ @line_no = 0
+ @line = []
+ end
+
+ def gets
+ print @prompt
+ @line[@line_no += 1] = $stdin.gets
+ end
+
+ def eof?
+ $stdin.eof?
+ end
+
+ def readable_atfer_eof?
+ true
+ end
+
+ def line(line_no)
+ @line[line_no]
+ end
+ end
+
+ class FileInputMethod < InputMethod
+ def initialize(file)
+ super
+ @io = open(file)
+ end
+ attr_reader :file_name
+
+ def eof?
+ @io.eof?
+ end
+
+ def gets
+ print @prompt
+ l = @io.gets
+# print @prompt, l
+ l
+ end
+ end
+
+ begin
+ require "readline"
+ class ReadlineInputMethod < InputMethod
+ include Readline
+ def initialize
+ super
+
+ @line_no = 0
+ @line = []
+ @eof = false
+ end
+
+ def gets
+ if l = readline(@prompt, false)
+ HISTORY.push(l) if !l.empty?
+ @line[@line_no += 1] = l + "\n"
+ else
+ @eof = true
+ l
+ end
+ end
+
+ def eof?
+ @eof
+ end
+
+ def readable_atfer_eof?
+ true
+ end
+
+ def line(line_no)
+ @line[line_no]
+ end
+ end
+ rescue LoadError
+ end
+end
diff --git a/ruby_1_8_5/lib/irb/lc/error.rb b/ruby_1_8_5/lib/irb/lc/error.rb
new file mode 100644
index 0000000000..54420e3508
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/lc/error.rb
@@ -0,0 +1,30 @@
+#
+# irb/lc/error.rb -
+# $Release Version: 0.9.5$
+# $Revision: 1.5.2.1 $
+# $Date: 2005/04/19 19:24:59 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+require "e2mmap"
+
+module IRB
+
+ # exceptions
+ extend Exception2MessageMapper
+ def_exception :UnrecognizedSwitch, "Unrecognized switch: %s"
+ def_exception :NotImplementedError, "Need to define `%s'"
+ def_exception :CantReturnToNormalMode, "Can't return to normal mode."
+ def_exception :IllegalParameter, "Illegal parameter(%s)."
+ def_exception :IrbAlreadyDead, "Irb is already dead."
+ def_exception :IrbSwitchedToCurrentThread, "Switched to current thread."
+ def_exception :NoSuchJob, "No such job(%s)."
+ def_exception :CantShiftToMultiIrbMode, "Can't shift to multi irb mode."
+ def_exception :CantChangeBinding, "Can't change binding to (%s)."
+ def_exception :UndefinedPromptMode, "Undefined prompt mode(%s)."
+
+end
+
diff --git a/ruby_1_8_5/lib/irb/lc/help-message b/ruby_1_8_5/lib/irb/lc/help-message
new file mode 100644
index 0000000000..f33d95e543
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/lc/help-message
@@ -0,0 +1,35 @@
+#
+# irb/lc/help-message.rb -
+# $Release Version: 0.9.5$
+# $Revision: 1.4.2.2 $
+# $Date: 2005/04/19 19:24:59 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+Usage: irb.rb [options] [programfile] [arguments]
+ -f Suppress read of ~/.irbrc
+ -m Bc mode (load mathn, fraction or matrix are available)
+ -d Set $DEBUG to true (same as `ruby -d')
+ -r load-module Same as `ruby -r'
+ -I path Specify $LOAD_PATH directory
+ --inspect Use `inspect' for output (default except for bc mode)
+ --noinspect Don't use inspect for output
+ --readline Use Readline extension module
+ --noreadline Don't use Readline extension module
+ --prompt prompt-mode
+ --prompt-mode prompt-mode
+ Switch prompt mode. Pre-defined prompt modes are
+ `default', `simple', `xmp' and `inf-ruby'
+ --inf-ruby-mode Use prompt appropriate for inf-ruby-mode on emacs.
+ Suppresses --readline.
+ --simple-prompt Simple prompt mode
+ --noprompt No prompt mode
+ --tracer Display trace for each execution of commands.
+ --back-trace-limit n
+ Display backtrace top n and tail n. The default
+ value is 16.
+ --irb_debug n Set internal debug level to n (not for popular use)
+ -v, --version Print the version of irb
diff --git a/ruby_1_8_5/lib/irb/lc/ja/error.rb b/ruby_1_8_5/lib/irb/lc/ja/error.rb
new file mode 100644
index 0000000000..25f8af68e0
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/lc/ja/error.rb
@@ -0,0 +1,27 @@
+#
+# irb/lc/ja/error.rb -
+# $Release Version: 0.9.5$
+# $Revision: 1.5.2.1 $
+# $Date: 2005/04/19 19:24:59 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+require "e2mmap"
+
+module IRB
+ # exceptions
+ extend Exception2MessageMapper
+ def_exception :UnrecognizedSwitch, '$B%9%$%C%A(B(%s)$B$,J,$j$^$;$s(B'
+ def_exception :NotImplementedError, '`%s\'$B$NDj5A$,I,MW$G$9(B'
+ def_exception :CantReturnToNormalMode, 'Normal$B%b!<%I$KLa$l$^$;$s(B.'
+ def_exception :IllegalParameter, '$B%Q%i%a!<%?(B(%s)$B$,4V0c$C$F$$$^$9(B.'
+ def_exception :IrbAlreadyDead, 'Irb$B$O4{$K;`$s$G$$$^$9(B.'
+ def_exception :IrbSwitchedToCurrentThread, '$B%+%l%s%H%9%l%C%I$K@Z$jBX$o$j$^$7$?(B.'
+ def_exception :NoSuchJob, '$B$=$N$h$&$J%8%g%V(B(%s)$B$O$"$j$^$;$s(B.'
+ def_exception :CantShiftToMultiIrbMode, 'multi-irb mode$B$K0\$l$^$;$s(B.'
+ def_exception :CantChangeBinding, '$B%P%$%s%G%#%s%0(B(%s)$B$KJQ99$G$-$^$;$s(B.'
+ def_exception :UndefinedPromptMode, '$B%W%m%s%W%H%b!<%I(B(%s)$B$ODj5A$5$l$F$$$^$;$s(B.'
+end
diff --git a/ruby_1_8_5/lib/irb/lc/ja/help-message b/ruby_1_8_5/lib/irb/lc/ja/help-message
new file mode 100644
index 0000000000..606f9e8984
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/lc/ja/help-message
@@ -0,0 +1,36 @@
+#
+# irb/lc/ja/help-message.rb -
+# $Release Version: 0.9.5$
+# $Revision: 1.3.2.2 $
+# $Date: 2005/04/19 19:24:59 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+Usage: irb.rb [options] [programfile] [arguments]
+ -f ~/.irbrc $B$rFI$_9~$^$J$$(B.
+ -m bc$B%b!<%I(B($BJ,?t(B, $B9TNs$N7W;;$,$G$-$k(B)
+ -d $DEBUG $B$r(Btrue$B$K$9$k(B(ruby -d $B$HF1$8(B)
+ -r load-module ruby -r $B$HF1$8(B.
+ -I path $LOAD_PATH $B$K(B path $B$rDI2C$9$k(B.
+ --inspect $B7k2L=PNO$K(Binspect$B$rMQ$$$k(B(bc$B%b!<%I0J30$O%G%U%)%k%H(B).
+ --noinspect $B7k2L=PNO$K(Binspect$B$rMQ$$$J$$(B.
+ --readline readline$B%i%$%V%i%j$rMxMQ$9$k(B.
+ --noreadline readline$B%i%$%V%i%j$rMxMQ$7$J$$(B.
+ --prompt prompt-mode/--prompt-mode prompt-mode
+ $B%W%m%s%W%H%b!<%I$r@ZBX$($^$9(B. $B8=:_Dj5A$5$l$F$$$k%W(B
+ $B%m%s%W%H%b!<%I$O(B, default, simple, xmp, inf-ruby$B$,(B
+ $BMQ0U$5$l$F$$$^$9(B.
+ --inf-ruby-mode emacs$B$N(Binf-ruby-mode$BMQ$N%W%m%s%W%HI=<($r9T$J$&(B. $BFC(B
+ $B$K;XDj$,$J$$8B$j(B, readline$B%i%$%V%i%j$O;H$o$J$/$J$k(B.
+ --simple-prompt $BHs>o$K%7%s%W%k$J%W%m%s%W%H$rMQ$$$k%b!<%I$G$9(B.
+ --noprompt $B%W%m%s%W%HI=<($r9T$J$o$J$$(B.
+ --tracer $B%3%^%s%I<B9T;~$K%H%l!<%9$r9T$J$&(B.
+ --back-trace-limit n
+ $B%P%C%/%H%l!<%9I=<($r%P%C%/%H%l!<%9$NF,$+$i(B n, $B8e$m(B
+ $B$+$i(Bn$B$@$19T$J$&(B. $B%G%U%)%k%H$O(B16
+ --irb_debug n irb$B$N%G%P%C%0%G%P%C%0%l%Y%k$r(Bn$B$K@_Dj$9$k(B($BMxMQ$7$J(B
+ $B$$J}$,L5Fq$G$7$g$&(B).
+ -v, --version irb$B$N%P!<%8%g%s$rI=<($9$k(B
diff --git a/ruby_1_8_5/lib/irb/locale.rb b/ruby_1_8_5/lib/irb/locale.rb
new file mode 100644
index 0000000000..1f4f9f67d8
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/locale.rb
@@ -0,0 +1,184 @@
+#
+# irb/locale.rb - internationalization module
+# $Release Version: 0.9.5$
+# $Revision: 1.6.2.3 $
+# $Date: 2005/09/01 18:30:46 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+
+autoload :Kconv, "kconv"
+
+module IRB
+ class Locale
+ @RCS_ID='-$Id: locale.rb,v 1.6.2.3 2005/09/01 18:30:46 keiju Exp $-'
+
+ JPDefaultLocale = "ja"
+ LOCALE_DIR = "/lc/"
+
+ def initialize(locale = nil)
+ @lang = locale || ENV["IRB_LANG"] || ENV["LC_MESSAGES"] || ENV["LC_ALL"] || ENV["LANG"] || "C"
+ end
+
+ attr_reader :lang
+
+ def lc2kconv(lang)
+ case lang
+ when "ja_JP.ujis", "ja_JP.euc", "ja_JP.eucJP"
+ Kconv::EUC
+ when "ja_JP.sjis", "ja_JP.SJIS"
+ Kconv::SJIS
+ when /ja_JP.utf-?8/i
+ Kconv::UTF8
+ end
+ end
+ private :lc2kconv
+
+ def String(mes)
+ mes = super(mes)
+ case @lang
+ when /^ja/
+ mes = Kconv::kconv(mes, lc2kconv(@lang))
+ else
+ mes
+ end
+ mes
+ end
+
+ def format(*opts)
+ String(super(*opts))
+ end
+
+ def gets(*rs)
+ String(super(*rs))
+ end
+
+ def readline(*rs)
+ String(super(*rs))
+ end
+
+ def print(*opts)
+ ary = opts.collect{|opt| String(opt)}
+ super(*ary)
+ end
+
+ def printf(*opts)
+ s = format(*opts)
+ print s
+ end
+
+ def puts(*opts)
+ ary = opts.collect{|opt| String(opts)}
+ super(*ary)
+ end
+
+ def require(file, priv = nil)
+ rex = Regexp.new("lc/#{Regexp.quote(file)}\.(so|o|sl|rb)?")
+ return false if $".find{|f| f =~ rex}
+
+ case file
+ when /\.rb$/
+ begin
+ load(file, priv)
+ $".push file
+ return true
+ rescue LoadError
+ end
+ when /\.(so|o|sl)$/
+ return super
+ end
+
+ begin
+ load(f = file + ".rb")
+ $".push f #"
+ return true
+ rescue LoadError
+ return ruby_require(file)
+ end
+ end
+
+ alias toplevel_load load
+
+ def load(file, priv=nil)
+ dir = File.dirname(file)
+ dir = "" if dir == "."
+ base = File.basename(file)
+
+ if /^ja(_JP)?$/ =~ @lang
+ back, @lang = @lang, "C"
+ end
+ begin
+ if dir[0] == ?/ #/
+ lc_path = search_file(dir, base)
+ return real_load(lc_path, priv) if lc_path
+ end
+
+ for path in $:
+ lc_path = search_file(path + "/" + dir, base)
+ return real_load(lc_path, priv) if lc_path
+ end
+ ensure
+ @lang = back if back
+ end
+ raise LoadError, "No such file to load -- #{file}"
+ end
+
+ def real_load(path, priv)
+ src = self.String(File.read(path))
+ if priv
+ eval("self", TOPLEVEL_BINDING).extend(Module.new {eval(src, nil, path)})
+ else
+ eval(src, TOPLEVEL_BINDING, path)
+ end
+ end
+ private :real_load
+
+ def find(file , paths = $:)
+ dir = File.dirname(file)
+ dir = "" if dir == "."
+ base = File.basename(file)
+ if dir[0] == ?/ #/
+ return lc_path = search_file(dir, base)
+ else
+ for path in $:
+ if lc_path = search_file(path + "/" + dir, base)
+ return lc_path
+ end
+ end
+ end
+ nil
+ end
+
+ def search_file(path, file)
+ if File.exists?(p1 = path + lc_path(file, "C"))
+ if File.exists?(p2 = path + lc_path(file))
+ return p2
+ else
+ end
+ return p1
+ else
+ end
+ nil
+ end
+ private :search_file
+
+ def lc_path(file = "", lc = @lang)
+ case lc
+ when "C"
+ LOCALE_DIR + file
+ when /^ja/
+ LOCALE_DIR + "ja/" + file
+ else
+ LOCALE_DIR + @lang + "/" + file
+ end
+ end
+ private :lc_path
+ end
+end
+
+
+
+
diff --git a/ruby_1_8_5/lib/irb/notifier.rb b/ruby_1_8_5/lib/irb/notifier.rb
new file mode 100644
index 0000000000..b03f445873
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/notifier.rb
@@ -0,0 +1,145 @@
+#
+# notifier.rb - optput methods used by irb
+# $Release Version: 0.9.5$
+# $Revision: 1.2.4.1 $
+# $Date: 2005/04/19 19:24:57 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+
+require "e2mmap"
+require "irb/output-method"
+
+module IRB
+ module Notifier
+ extend Exception2MessageMapper
+ def_exception :ErrUndefinedNotifier,
+ "undefined notifier level: %d is specified"
+ def_exception :ErrUnrecognizedLevel,
+ "unrecognized notifier level: %s is specified"
+
+ def def_notifier(prefix = "", output_method = StdioOutputMethod.new)
+ CompositeNotifier.new(prefix, output_method)
+ end
+ module_function :def_notifier
+
+ class AbstructNotifier
+ def initialize(prefix, base_notifier)
+ @prefix = prefix
+ @base_notifier = base_notifier
+ end
+
+ attr_reader :prefix
+
+ def notify?
+ true
+ end
+
+ def print(*opts)
+ @base_notifier.print prefix, *opts if notify?
+ end
+
+ def printn(*opts)
+ @base_notifier.printn prefix, *opts if notify?
+ end
+
+ def printf(format, *opts)
+ @base_notifier.printf(prefix + format, *opts) if notify?
+ end
+
+ def puts(*objs)
+ if notify?
+ @base_notifier.puts(*objs.collect{|obj| prefix + obj.to_s})
+ end
+ end
+
+ def pp(*objs)
+ if notify?
+ @base_notifier.ppx @prefix, *objs
+ end
+ end
+
+ def ppx(prefix, *objs)
+ if notify?
+ @base_notifier.ppx @prefix+prefix, *objs
+ end
+ end
+
+ def exec_if
+ yield(@base_notifier) if notify?
+ end
+ end
+
+ class CompositeNotifier<AbstructNotifier
+ def initialize(prefix, base_notifier)
+ super
+
+ @notifiers = [D_NOMSG]
+ @level_notifier = D_NOMSG
+ end
+
+ attr_reader :notifiers
+
+ def def_notifier(level, prefix = "")
+ notifier = LeveledNotifier.new(self, level, prefix)
+ @notifiers[level] = notifier
+ notifier
+ end
+
+ attr_reader :level_notifier
+ alias level level_notifier
+
+ def level_notifier=(value)
+ case value
+ when AbstructNotifier
+ @level_notifier = value
+ when Integer
+ l = @notifiers[value]
+ Notifier.Raise ErrUndefinedNotifer, value unless l
+ @level_notifier = l
+ else
+ Notifier.Raise ErrUnrecognizedLevel, value unless l
+ end
+ end
+
+ alias level= level_notifier=
+ end
+
+ class LeveledNotifier<AbstructNotifier
+ include Comparable
+
+ def initialize(base, level, prefix)
+ super(prefix, base)
+
+ @level = level
+ end
+
+ attr_reader :level
+
+ def <=>(other)
+ @level <=> other.level
+ end
+
+ def notify?
+ @base_notifier.level >= self
+ end
+ end
+
+ class NoMsgNotifier<LeveledNotifier
+ def initialize
+ @base_notifier = nil
+ @level = 0
+ @prefix = ""
+ end
+
+ def notify?
+ false
+ end
+ end
+
+ D_NOMSG = NoMsgNotifier.new
+ end
+end
diff --git a/ruby_1_8_5/lib/irb/output-method.rb b/ruby_1_8_5/lib/irb/output-method.rb
new file mode 100644
index 0000000000..01bfefb5af
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/output-method.rb
@@ -0,0 +1,85 @@
+#
+# output-method.rb - optput methods used by irb
+# $Release Version: 0.9.5$
+# $Revision: 1.2.4.1 $
+# $Date: 2005/04/19 19:24:57 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+
+require "e2mmap"
+
+module IRB
+ # OutputMethod
+ # StdioOutputMethod
+
+ class OutputMethod
+ @RCS_ID='-$Id: output-method.rb,v 1.2.4.1 2005/04/19 19:24:57 keiju Exp $-'
+
+ def print(*opts)
+ IRB.fail NotImplementError, "print"
+ end
+
+ def printn(*opts)
+ print opts.join(" "), "\n"
+ end
+
+ # extend printf
+ def printf(format, *opts)
+ if /(%*)%I/ =~ format
+ format, opts = parse_printf_format(format, opts)
+ end
+ print sprintf(format, *opts)
+ end
+
+ # %
+ # <¥Õ¥é¥°> [#0- +]
+ # <ºÇ¾®¥Õ¥£¡¼¥ë¥ÉÉý> (\*|\*[1-9][0-9]*\$|[1-9][0-9]*)
+ # <ÀºÅÙ>.(\*|\*[1-9][0-9]*\$|[1-9][0-9]*|)?
+ # #<Ťµ½¤ÀµÊ¸»ú>(hh|h|l|ll|L|q|j|z|t)
+ # <ÊÑ´¹½¤ÀµÊ¸»ú>[diouxXeEfgGcsb%]
+ def parse_printf_format(format, opts)
+ return format, opts if $1.size % 2 == 1
+ end
+
+ def foo(format)
+ pos = 0
+ inspects = []
+ format.scan(/%[#0\-+ ]?(\*(?=[^0-9])|\*[1-9][0-9]*\$|[1-9][0-9]*(?=[^0-9]))?(\.(\*(?=[^0-9])|\*[1-9][0-9]*\$|[1-9][0-9]*(?=[^0-9])))?(([1-9][0-9]*\$)*)([diouxXeEfgGcsb%])/) {|f, p, pp, pos, new_pos, c|
+ puts [f, p, pp, pos, new_pos, c].join("!")
+ pos = new_pos if new_pos
+ if c == "I"
+ inspects.push pos.to_i
+ (f||"")+(p||"")+(pp||"")+(pos||"")+"s"
+ else
+ $&
+ end
+ }
+ end
+
+ def puts(*objs)
+ for obj in objs
+ print(*obj)
+ print "\n"
+ end
+ end
+
+ def pp(*objs)
+ puts(*objs.collect{|obj| obj.inspect})
+ end
+
+ def ppx(prefix, *objs)
+ puts(*objs.collect{|obj| prefix+obj.inspect})
+ end
+
+ end
+
+ class StdioOutputMethod<OutputMethod
+ def print(*opts)
+ STDOUT.print(*opts)
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/irb/ruby-lex.rb b/ruby_1_8_5/lib/irb/ruby-lex.rb
new file mode 100644
index 0000000000..6310fb626b
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/ruby-lex.rb
@@ -0,0 +1,1149 @@
+#
+# irb/ruby-lex.rb - ruby lexcal analizer
+# $Release Version: 0.9.5$
+# $Revision: 1.22.2.6 $
+# $Date: 2006/08/04 10:15:49 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+
+require "e2mmap"
+require "irb/slex"
+require "irb/ruby-token"
+
+class RubyLex
+ @RCS_ID='-$Id: ruby-lex.rb,v 1.22.2.6 2006/08/04 10:15:49 keiju Exp $-'
+
+ extend Exception2MessageMapper
+ def_exception(:AlreadyDefinedToken, "Already defined token(%s)")
+ def_exception(:TkReading2TokenNoKey, "key nothing(key='%s')")
+ def_exception(:TkSymbol2TokenNoKey, "key nothing(key='%s')")
+ def_exception(:TkReading2TokenDuplicateError,
+ "key duplicate(token_n='%s', key='%s')")
+ def_exception(:SyntaxError, "%s")
+
+ def_exception(:TerminateLineInput, "Terminate Line Input")
+
+ include RubyToken
+
+ class << self
+ attr_accessor :debug_level
+ def debug?
+ @debug_level > 0
+ end
+ end
+ @debug_level = 0
+
+ def initialize
+ lex_init
+ set_input(STDIN)
+
+ @seek = 0
+ @exp_line_no = @line_no = 1
+ @base_char_no = 0
+ @char_no = 0
+ @rests = []
+ @readed = []
+ @here_readed = []
+
+ @indent = 0
+ @indent_stack = []
+ @lex_state = EXPR_BEG
+ @space_seen = false
+ @here_header = false
+
+ @continue = false
+ @line = ""
+
+ @skip_space = false
+ @readed_auto_clean_up = false
+ @exception_on_syntax_error = true
+
+ @prompt = nil
+ end
+
+ attr_accessor :skip_space
+ attr_accessor :readed_auto_clean_up
+ attr_accessor :exception_on_syntax_error
+
+ attr_reader :seek
+ attr_reader :char_no
+ attr_reader :line_no
+ attr_reader :indent
+
+ # io functions
+ def set_input(io, p = nil, &block)
+ @io = io
+ if p.respond_to?(:call)
+ @input = p
+ elsif block_given?
+ @input = block
+ else
+ @input = Proc.new{@io.gets}
+ end
+ end
+
+ def get_readed
+ if idx = @readed.reverse.index("\n")
+ @base_char_no = idx
+ else
+ @base_char_no += @readed.size
+ end
+
+ readed = @readed.join("")
+ @readed = []
+ readed
+ end
+
+ def getc
+ while @rests.empty?
+# return nil unless buf_input
+ @rests.push nil unless buf_input
+ end
+ c = @rests.shift
+ if @here_header
+ @here_readed.push c
+ else
+ @readed.push c
+ end
+ @seek += 1
+ if c == "\n"
+ @line_no += 1
+ @char_no = 0
+ else
+ @char_no += 1
+ end
+ c
+ end
+
+ def gets
+ l = ""
+ while c = getc
+ l.concat(c)
+ break if c == "\n"
+ end
+ return nil if l == "" and c.nil?
+ l
+ end
+
+ def eof?
+ @io.eof?
+ end
+
+ def getc_of_rests
+ if @rests.empty?
+ nil
+ else
+ getc
+ end
+ end
+
+ def ungetc(c = nil)
+ if @here_readed.empty?
+ c2 = @readed.pop
+ else
+ c2 = @here_readed.pop
+ end
+ c = c2 unless c
+ @rests.unshift c #c =
+ @seek -= 1
+ if c == "\n"
+ @line_no -= 1
+ if idx = @readed.reverse.index("\n")
+ @char_no = @readed.size - idx
+ else
+ @char_no = @base_char_no + @readed.size
+ end
+ else
+ @char_no -= 1
+ end
+ end
+
+ def peek_equal?(str)
+ chrs = str.split(//)
+ until @rests.size >= chrs.size
+ return false unless buf_input
+ end
+ @rests[0, chrs.size] == chrs
+ end
+
+ def peek_match?(regexp)
+ while @rests.empty?
+ return false unless buf_input
+ end
+ regexp =~ @rests.join("")
+ end
+
+ def peek(i = 0)
+ while @rests.size <= i
+ return nil unless buf_input
+ end
+ @rests[i]
+ end
+
+ def buf_input
+ prompt
+ line = @input.call
+ return nil unless line
+ @rests.concat line.split(//)
+ true
+ end
+ private :buf_input
+
+ def set_prompt(p = nil, &block)
+ p = block if block_given?
+ if p.respond_to?(:call)
+ @prompt = p
+ else
+ @prompt = Proc.new{print p}
+ end
+ end
+
+ def prompt
+ if @prompt
+ @prompt.call(@ltype, @indent, @continue, @line_no)
+ end
+ end
+
+ def initialize_input
+ @ltype = nil
+ @quoted = nil
+ @indent = 0
+ @indent_stack = []
+ @lex_state = EXPR_BEG
+ @space_seen = false
+ @here_header = false
+
+ @continue = false
+ prompt
+
+ @line = ""
+ @exp_line_no = @line_no
+ end
+
+ def each_top_level_statement
+ initialize_input
+ catch(:TERM_INPUT) do
+ loop do
+ begin
+ @continue = false
+ prompt
+ unless l = lex
+ throw :TERM_INPUT if @line == ''
+ else
+ #p l
+ @line.concat l
+ if @ltype or @continue or @indent > 0
+ next
+ end
+ end
+ if @line != "\n"
+ yield @line, @exp_line_no
+ end
+ break unless l
+ @line = ''
+ @exp_line_no = @line_no
+
+ @indent = 0
+ @indent_stack = []
+ prompt
+ rescue TerminateLineInput
+ initialize_input
+ prompt
+ get_readed
+ end
+ end
+ end
+ end
+
+ def lex
+ until (((tk = token).kind_of?(TkNL) || tk.kind_of?(TkEND_OF_SCRIPT)) &&
+ !@continue or
+ tk.nil?)
+ #p tk
+ #p @lex_state
+ #p self
+ end
+ line = get_readed
+ # print self.inspect
+ if line == "" and tk.kind_of?(TkEND_OF_SCRIPT) || tk.nil?
+ nil
+ else
+ line
+ end
+ end
+
+ def token
+ # require "tracer"
+ # Tracer.on
+ @prev_seek = @seek
+ @prev_line_no = @line_no
+ @prev_char_no = @char_no
+ begin
+ begin
+ tk = @OP.match(self)
+ @space_seen = tk.kind_of?(TkSPACE)
+ rescue SyntaxError
+ raise if @exception_on_syntax_error
+ tk = TkError.new(@seek, @line_no, @char_no)
+ end
+ end while @skip_space and tk.kind_of?(TkSPACE)
+ if @readed_auto_clean_up
+ get_readed
+ end
+ # Tracer.off
+ tk
+ end
+
+ ENINDENT_CLAUSE = [
+ "case", "class", "def", "do", "for", "if",
+ "module", "unless", "until", "while", "begin" #, "when"
+ ]
+ DEINDENT_CLAUSE = ["end" #, "when"
+ ]
+
+ PERCENT_LTYPE = {
+ "q" => "\'",
+ "Q" => "\"",
+ "x" => "\`",
+ "r" => "/",
+ "w" => "]",
+ "W" => "]",
+ "s" => ":"
+ }
+
+ PERCENT_PAREN = {
+ "{" => "}",
+ "[" => "]",
+ "<" => ">",
+ "(" => ")"
+ }
+
+ Ltype2Token = {
+ "\'" => TkSTRING,
+ "\"" => TkSTRING,
+ "\`" => TkXSTRING,
+ "/" => TkREGEXP,
+ "]" => TkDSTRING,
+ ":" => TkSYMBOL
+ }
+ DLtype2Token = {
+ "\"" => TkDSTRING,
+ "\`" => TkDXSTRING,
+ "/" => TkDREGEXP,
+ }
+
+ def lex_init()
+ @OP = IRB::SLex.new
+ @OP.def_rules("\0", "\004", "\032") do |op, io|
+ Token(TkEND_OF_SCRIPT)
+ end
+
+ @OP.def_rules(" ", "\t", "\f", "\r", "\13") do |op, io|
+ @space_seen = true
+ while getc =~ /[ \t\f\r\13]/; end
+ ungetc
+ Token(TkSPACE)
+ end
+
+ @OP.def_rule("#") do |op, io|
+ identify_comment
+ end
+
+ @OP.def_rule("=begin",
+ proc{|op, io| @prev_char_no == 0 && peek(0) =~ /\s/}) do
+ |op, io|
+ @ltype = "="
+ until getc == "\n"; end
+ until peek_equal?("=end") && peek(4) =~ /\s/
+ until getc == "\n"; end
+ end
+ gets
+ @ltype = nil
+ Token(TkRD_COMMENT)
+ end
+
+ @OP.def_rule("\n") do |op, io|
+ print "\\n\n" if RubyLex.debug?
+ case @lex_state
+ when EXPR_BEG, EXPR_FNAME, EXPR_DOT
+ @continue = true
+ else
+ @continue = false
+ @lex_state = EXPR_BEG
+ until (@indent_stack.empty? ||
+ [TkLPAREN, TkLBRACK, TkLBRACE,
+ TkfLPAREN, TkfLBRACK, TkfLBRACE].include?(@indent_stack.last))
+ @indent_stack.pop
+ end
+ end
+ @here_header = false
+ @here_readed = []
+ Token(TkNL)
+ end
+
+ @OP.def_rules("*", "**",
+ "=", "==", "===",
+ "=~", "<=>",
+ "<", "<=",
+ ">", ">=", ">>") do
+ |op, io|
+ case @lex_state
+ when EXPR_FNAME, EXPR_DOT
+ @lex_state = EXPR_ARG
+ else
+ @lex_state = EXPR_BEG
+ end
+ Token(op)
+ end
+
+ @OP.def_rules("!", "!=", "!~") do
+ |op, io|
+ @lex_state = EXPR_BEG
+ Token(op)
+ end
+
+ @OP.def_rules("<<") do
+ |op, io|
+ tk = nil
+ if @lex_state != EXPR_END && @lex_state != EXPR_CLASS &&
+ (@lex_state != EXPR_ARG || @space_seen)
+ c = peek(0)
+ if /\S/ =~ c && (/["'`]/ =~ c || /[\w_]/ =~ c || c == "-")
+ tk = identify_here_document
+ end
+ end
+ unless tk
+ tk = Token(op)
+ case @lex_state
+ when EXPR_FNAME, EXPR_DOT
+ @lex_state = EXPR_ARG
+ else
+ @lex_state = EXPR_BEG
+ end
+ end
+ tk
+ end
+
+ @OP.def_rules("'", '"') do
+ |op, io|
+ identify_string(op)
+ end
+
+ @OP.def_rules("`") do
+ |op, io|
+ if @lex_state == EXPR_FNAME
+ @lex_state = EXPR_END
+ Token(op)
+ else
+ identify_string(op)
+ end
+ end
+
+ @OP.def_rules('?') do
+ |op, io|
+ if @lex_state == EXPR_END
+ @lex_state = EXPR_BEG
+ Token(TkQUESTION)
+ else
+ ch = getc
+ if @lex_state == EXPR_ARG && ch =~ /\s/
+ ungetc
+ @lex_state = EXPR_BEG;
+ Token(TkQUESTION)
+ else
+ if (ch == '\\')
+ read_escape
+ end
+ @lex_state = EXPR_END
+ Token(TkINTEGER)
+ end
+ end
+ end
+
+ @OP.def_rules("&", "&&", "|", "||") do
+ |op, io|
+ @lex_state = EXPR_BEG
+ Token(op)
+ end
+
+ @OP.def_rules("+=", "-=", "*=", "**=",
+ "&=", "|=", "^=", "<<=", ">>=", "||=", "&&=") do
+ |op, io|
+ @lex_state = EXPR_BEG
+ op =~ /^(.*)=$/
+ Token(TkOPASGN, $1)
+ end
+
+ @OP.def_rule("+@", proc{|op, io| @lex_state == EXPR_FNAME}) do
+ |op, io|
+ @lex_state = EXPR_ARG
+ Token(op)
+ end
+
+ @OP.def_rule("-@", proc{|op, io| @lex_state == EXPR_FNAME}) do
+ |op, io|
+ @lex_state = EXPR_ARG
+ Token(op)
+ end
+
+ @OP.def_rules("+", "-") do
+ |op, io|
+ catch(:RET) do
+ if @lex_state == EXPR_ARG
+ if @space_seen and peek(0) =~ /[0-9]/
+ throw :RET, identify_number
+ else
+ @lex_state = EXPR_BEG
+ end
+ elsif @lex_state != EXPR_END and peek(0) =~ /[0-9]/
+ throw :RET, identify_number
+ else
+ @lex_state = EXPR_BEG
+ end
+ Token(op)
+ end
+ end
+
+ @OP.def_rule(".") do
+ |op, io|
+ @lex_state = EXPR_BEG
+ if peek(0) =~ /[0-9]/
+ ungetc
+ identify_number
+ else
+ # for "obj.if" etc.
+ @lex_state = EXPR_DOT
+ Token(TkDOT)
+ end
+ end
+
+ @OP.def_rules("..", "...") do
+ |op, io|
+ @lex_state = EXPR_BEG
+ Token(op)
+ end
+
+ lex_int2
+ end
+
+ def lex_int2
+ @OP.def_rules("]", "}", ")") do
+ |op, io|
+ @lex_state = EXPR_END
+ @indent -= 1
+ @indent_stack.pop
+ Token(op)
+ end
+
+ @OP.def_rule(":") do
+ |op, io|
+ if @lex_state == EXPR_END || peek(0) =~ /\s/
+ @lex_state = EXPR_BEG
+ Token(TkCOLON)
+ else
+ @lex_state = EXPR_FNAME;
+ Token(TkSYMBEG)
+ end
+ end
+
+ @OP.def_rule("::") do
+ |op, io|
+# p @lex_state.id2name, @space_seen
+ if @lex_state == EXPR_BEG or @lex_state == EXPR_ARG && @space_seen
+ @lex_state = EXPR_BEG
+ Token(TkCOLON3)
+ else
+ @lex_state = EXPR_DOT
+ Token(TkCOLON2)
+ end
+ end
+
+ @OP.def_rule("/") do
+ |op, io|
+ if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
+ identify_string(op)
+ elsif peek(0) == '='
+ getc
+ @lex_state = EXPR_BEG
+ Token(TkOPASGN, "/") #/)
+ elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/
+ identify_string(op)
+ else
+ @lex_state = EXPR_BEG
+ Token("/") #/)
+ end
+ end
+
+ @OP.def_rules("^") do
+ |op, io|
+ @lex_state = EXPR_BEG
+ Token("^")
+ end
+
+ # @OP.def_rules("^=") do
+ # @lex_state = EXPR_BEG
+ # Token(OP_ASGN, :^)
+ # end
+
+ @OP.def_rules(",") do
+ |op, io|
+ @lex_state = EXPR_BEG
+ Token(op)
+ end
+
+ @OP.def_rules(";") do
+ |op, io|
+ @lex_state = EXPR_BEG
+ until (@indent_stack.empty? ||
+ [TkLPAREN, TkLBRACK, TkLBRACE,
+ TkfLPAREN, TkfLBRACK, TkfLBRACE].include?(@indent_stack.last))
+ @indent_stack.pop
+ end
+ Token(op)
+ end
+
+ @OP.def_rule("~") do
+ |op, io|
+ @lex_state = EXPR_BEG
+ Token("~")
+ end
+
+ @OP.def_rule("~@", proc{|op, io| @lex_state == EXPR_FNAME}) do
+ |op, io|
+ @lex_state = EXPR_BEG
+ Token("~")
+ end
+
+ @OP.def_rule("(") do
+ |op, io|
+ @indent += 1
+ if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
+ @lex_state = EXPR_BEG
+ tk_c = TkfLPAREN
+ else
+ @lex_state = EXPR_BEG
+ tk_c = TkLPAREN
+ end
+ @indent_stack.push tk_c
+ tk = Token(tk_c)
+ end
+
+ @OP.def_rule("[]", proc{|op, io| @lex_state == EXPR_FNAME}) do
+ |op, io|
+ @lex_state = EXPR_ARG
+ Token("[]")
+ end
+
+ @OP.def_rule("[]=", proc{|op, io| @lex_state == EXPR_FNAME}) do
+ |op, io|
+ @lex_state = EXPR_ARG
+ Token("[]=")
+ end
+
+ @OP.def_rule("[") do
+ |op, io|
+ @indent += 1
+ if @lex_state == EXPR_FNAME
+ tk_c = TkfLBRACK
+ else
+ if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
+ tk_c = TkLBRACK
+ elsif @lex_state == EXPR_ARG && @space_seen
+ tk_c = TkLBRACK
+ else
+ tk_c = TkfLBRACK
+ end
+ @lex_state = EXPR_BEG
+ end
+ @indent_stack.push tk_c
+ Token(tk_c)
+ end
+
+ @OP.def_rule("{") do
+ |op, io|
+ @indent += 1
+ if @lex_state != EXPR_END && @lex_state != EXPR_ARG
+ tk_c = TkLBRACE
+ else
+ tk_c = TkfLBRACE
+ end
+ @lex_state = EXPR_BEG
+ @indent_stack.push tk_c
+ Token(tk_c)
+ end
+
+ @OP.def_rule('\\') do
+ |op, io|
+ if getc == "\n"
+ @space_seen = true
+ @continue = true
+ Token(TkSPACE)
+ else
+ ungetc
+ Token("\\")
+ end
+ end
+
+ @OP.def_rule('%') do
+ |op, io|
+ if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
+ identify_quotation
+ elsif peek(0) == '='
+ getc
+ Token(TkOPASGN, :%)
+ elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/
+ identify_quotation
+ else
+ @lex_state = EXPR_BEG
+ Token("%") #))
+ end
+ end
+
+ @OP.def_rule('$') do
+ |op, io|
+ identify_gvar
+ end
+
+ @OP.def_rule('@') do
+ |op, io|
+ if peek(0) =~ /[\w_@]/
+ ungetc
+ identify_identifier
+ else
+ Token("@")
+ end
+ end
+
+ # @OP.def_rule("def", proc{|op, io| /\s/ =~ io.peek(0)}) do
+ # |op, io|
+ # @indent += 1
+ # @lex_state = EXPR_FNAME
+ # # @lex_state = EXPR_END
+ # # until @rests[0] == "\n" or @rests[0] == ";"
+ # # rests.shift
+ # # end
+ # end
+
+ @OP.def_rule("") do
+ |op, io|
+ printf "MATCH: start %s: %s\n", op, io.inspect if RubyLex.debug?
+ if peek(0) =~ /[0-9]/
+ t = identify_number
+ elsif peek(0) =~ /[\w_]/
+ t = identify_identifier
+ end
+ printf "MATCH: end %s: %s\n", op, io.inspect if RubyLex.debug?
+ t
+ end
+
+ p @OP if RubyLex.debug?
+ end
+
+ def identify_gvar
+ @lex_state = EXPR_END
+
+ case ch = getc
+ when /[~_*$?!@\/\\;,=:<>".]/ #"
+ Token(TkGVAR, "$" + ch)
+ when "-"
+ Token(TkGVAR, "$-" + getc)
+ when "&", "`", "'", "+"
+ Token(TkBACK_REF, "$"+ch)
+ when /[1-9]/
+ while getc =~ /[0-9]/; end
+ ungetc
+ Token(TkNTH_REF)
+ when /\w/
+ ungetc
+ ungetc
+ identify_identifier
+ else
+ ungetc
+ Token("$")
+ end
+ end
+
+ def identify_identifier
+ token = ""
+ if peek(0) =~ /[$@]/
+ token.concat(c = getc)
+ if c == "@" and peek(0) == "@"
+ token.concat getc
+ end
+ end
+
+ while (ch = getc) =~ /\w|_/
+ print ":", ch, ":" if RubyLex.debug?
+ token.concat ch
+ end
+ ungetc
+
+ if (ch == "!" || ch == "?") && token[0,1] =~ /\w/ && peek(0) != "="
+ token.concat getc
+ end
+
+ # almost fix token
+
+ case token
+ when /^\$/
+ return Token(TkGVAR, token)
+ when /^\@\@/
+ @lex_state = EXPR_END
+ # p Token(TkCVAR, token)
+ return Token(TkCVAR, token)
+ when /^\@/
+ @lex_state = EXPR_END
+ return Token(TkIVAR, token)
+ end
+
+ if @lex_state != EXPR_DOT
+ print token, "\n" if RubyLex.debug?
+
+ token_c, *trans = TkReading2Token[token]
+ if token_c
+ # reserved word?
+
+ if (@lex_state != EXPR_BEG &&
+ @lex_state != EXPR_FNAME &&
+ trans[1])
+ # modifiers
+ token_c = TkSymbol2Token[trans[1]]
+ @lex_state = trans[0]
+ else
+ if @lex_state != EXPR_FNAME
+ if ENINDENT_CLAUSE.include?(token)
+ # check for ``class = val'' etc.
+ valid = true
+ case token
+ when "class"
+ valid = false unless peek_match?(/^\s*(<<|\w|::)/)
+ when "def"
+ valid = false if peek_match?(/^\s*(([+-\/*&\|^]|<<|>>|\|\||\&\&)=|\&\&|\|\|)/)
+ when "do"
+ valid = false if peek_match?(/^\s*([+-\/*]?=|\*|<|>|\&)/)
+ when *ENINDENT_CLAUSE
+ valid = false if peek_match?(/^\s*([+-\/*]?=|\*|<|>|\&|\|)/)
+ else
+ # no nothing
+ end
+ if valid
+ if token == "do"
+ if ![TkFOR, TkWHILE, TkUNTIL].include?(@indent_stack.last)
+ @indent += 1
+ @indent_stack.push token_c
+ end
+ else
+ @indent += 1
+ @indent_stack.push token_c
+ end
+# p @indent_stack
+ end
+
+ elsif DEINDENT_CLAUSE.include?(token)
+ @indent -= 1
+ @indent_stack.pop
+ end
+ @lex_state = trans[0]
+ else
+ @lex_state = EXPR_END
+ end
+ end
+ return Token(token_c, token)
+ end
+ end
+
+ if @lex_state == EXPR_FNAME
+ @lex_state = EXPR_END
+ if peek(0) == '='
+ token.concat getc
+ end
+ elsif @lex_state == EXPR_BEG || @lex_state == EXPR_DOT
+ @lex_state = EXPR_ARG
+ else
+ @lex_state = EXPR_END
+ end
+
+ if token[0, 1] =~ /[A-Z]/
+ return Token(TkCONSTANT, token)
+ elsif token[token.size - 1, 1] =~ /[!?]/
+ return Token(TkFID, token)
+ else
+ return Token(TkIDENTIFIER, token)
+ end
+ end
+
+ def identify_here_document
+ ch = getc
+# if lt = PERCENT_LTYPE[ch]
+ if ch == "-"
+ ch = getc
+ indent = true
+ end
+ if /['"`]/ =~ ch
+ lt = ch
+ quoted = ""
+ while (c = getc) && c != lt
+ quoted.concat c
+ end
+ else
+ lt = '"'
+ quoted = ch.dup
+ while (c = getc) && c =~ /\w/
+ quoted.concat c
+ end
+ ungetc
+ end
+
+ ltback, @ltype = @ltype, lt
+ reserve = []
+ while ch = getc
+ reserve.push ch
+ if ch == "\\"
+ reserve.push ch = getc
+ elsif ch == "\n"
+ break
+ end
+ end
+
+ @here_header = false
+ while l = gets
+ l = l.sub(/(:?\r)?\n\z/, '')
+ if (indent ? l.strip : l) == quoted
+ break
+ end
+ end
+
+ @here_header = true
+ @here_readed.concat reserve
+ while ch = reserve.pop
+ ungetc ch
+ end
+
+ @ltype = ltback
+ @lex_state = EXPR_END
+ Token(Ltype2Token[lt])
+ end
+
+ def identify_quotation
+ ch = getc
+ if lt = PERCENT_LTYPE[ch]
+ ch = getc
+ elsif ch =~ /\W/
+ lt = "\""
+ else
+ RubyLex.fail SyntaxError, "unknown type of %string"
+ end
+# if ch !~ /\W/
+# ungetc
+# next
+# end
+ #@ltype = lt
+ @quoted = ch unless @quoted = PERCENT_PAREN[ch]
+ identify_string(lt, @quoted)
+ end
+
+ def identify_number
+ @lex_state = EXPR_END
+
+ if peek(0) == "0" && peek(1) !~ /[.eE]/
+ getc
+ case peek(0)
+ when /[xX]/
+ ch = getc
+ match = /[0-9a-fA-F_]/
+ when /[bB]/
+ ch = getc
+ match = /[01_]/
+ when /[oO]/
+ ch = getc
+ match = /[0-7_]/
+ when /[dD]/
+ ch = getc
+ match = /[0-9_]/
+ when /[0-7]/
+ match = /[0-7_]/
+ when /[89]/
+ RubyLex.fail SyntaxError, "Illegal octal digit"
+ else
+ return Token(TkINTEGER)
+ end
+
+ len0 = true
+ non_digit = false
+ while ch = getc
+ if match =~ ch
+ if ch == "_"
+ if non_digit
+ RubyLex.fail SyntaxError, "trailing `#{ch}' in number"
+ else
+ non_digit = ch
+ end
+ else
+ non_digit = false
+ len0 = false
+ end
+ else
+ ungetc
+ if len0
+ RubyLex.fail SyntaxError, "numeric literal without digits"
+ end
+ if non_digit
+ RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number"
+ end
+ break
+ end
+ end
+ return Token(TkINTEGER)
+ end
+
+ type = TkINTEGER
+ allow_point = true
+ allow_e = true
+ non_digit = false
+ while ch = getc
+ case ch
+ when /[0-9]/
+ non_digit = false
+ when "_"
+ non_digit = ch
+ when allow_point && "."
+ if non_digit
+ RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number"
+ end
+ type = TkFLOAT
+ if peek(0) !~ /[0-9]/
+ type = TkINTEGER
+ ungetc
+ break
+ end
+ allow_point = false
+ when allow_e && "e", allow_e && "E"
+ if non_digit
+ RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number"
+ end
+ type = TkFLOAT
+ if peek(0) =~ /[+-]/
+ getc
+ end
+ allow_e = false
+ allow_point = false
+ non_digit = ch
+ else
+ if non_digit
+ RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number"
+ end
+ ungetc
+ break
+ end
+ end
+ Token(type)
+ end
+
+ def identify_string(ltype, quoted = ltype)
+ @ltype = ltype
+ @quoted = quoted
+ subtype = nil
+ begin
+ nest = 0
+ while ch = getc
+ if @quoted == ch and nest == 0
+ break
+ elsif @ltype != "'" && @ltype != "]" && @ltype != ":" and ch == "#"
+ subtype = true
+ elsif ch == '\\' #'
+ read_escape
+ end
+ if PERCENT_PAREN.values.include?(@quoted)
+ if PERCENT_PAREN[ch] == @quoted
+ nest += 1
+ elsif ch == @quoted
+ nest -= 1
+ end
+ end
+ end
+ if @ltype == "/"
+ if peek(0) =~ /i|m|x|o|e|s|u|n/
+ getc
+ end
+ end
+ if subtype
+ Token(DLtype2Token[ltype])
+ else
+ Token(Ltype2Token[ltype])
+ end
+ ensure
+ @ltype = nil
+ @quoted = nil
+ @lex_state = EXPR_END
+ end
+ end
+
+ def identify_comment
+ @ltype = "#"
+
+ while ch = getc
+# if ch == "\\" #"
+# read_escape
+# end
+ if ch == "\n"
+ @ltype = nil
+ ungetc
+ break
+ end
+ end
+ return Token(TkCOMMENT)
+ end
+
+ def read_escape
+ case ch = getc
+ when "\n", "\r", "\f"
+ when "\\", "n", "t", "r", "f", "v", "a", "e", "b", "s" #"
+ when /[0-7]/
+ ungetc ch
+ 3.times do
+ case ch = getc
+ when /[0-7]/
+ when nil
+ break
+ else
+ ungetc
+ break
+ end
+ end
+
+ when "x"
+ 2.times do
+ case ch = getc
+ when /[0-9a-fA-F]/
+ when nil
+ break
+ else
+ ungetc
+ break
+ end
+ end
+
+ when "M"
+ if (ch = getc) != '-'
+ ungetc
+ else
+ if (ch = getc) == "\\" #"
+ read_escape
+ end
+ end
+
+ when "C", "c" #, "^"
+ if ch == "C" and (ch = getc) != "-"
+ ungetc
+ elsif (ch = getc) == "\\" #"
+ read_escape
+ end
+ else
+ # other characters
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/irb/ruby-token.rb b/ruby_1_8_5/lib/irb/ruby-token.rb
new file mode 100644
index 0000000000..4d56754d36
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/ruby-token.rb
@@ -0,0 +1,273 @@
+#
+# irb/ruby-token.rb - ruby tokens
+# $Release Version: 0.9.5$
+# $Revision: 1.5.2.1 $
+# $Date: 2005/04/19 19:24:57 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+module RubyToken
+ EXPR_BEG = :EXPR_BEG
+ EXPR_MID = :EXPR_MID
+ EXPR_END = :EXPR_END
+ EXPR_ARG = :EXPR_ARG
+ EXPR_FNAME = :EXPR_FNAME
+ EXPR_DOT = :EXPR_DOT
+ EXPR_CLASS = :EXPR_CLASS
+
+ # for ruby 1.4X
+ if !defined?(Symbol)
+ Symbol = Integer
+ end
+
+ class Token
+ def initialize(seek, line_no, char_no)
+ @seek = seek
+ @line_no = line_no
+ @char_no = char_no
+ end
+ attr :seek
+ attr :line_no
+ attr :char_no
+ end
+
+ class TkNode < Token
+ def initialize(seek, line_no, char_no)
+ super
+ end
+ attr :node
+ end
+
+ class TkId < Token
+ def initialize(seek, line_no, char_no, name)
+ super(seek, line_no, char_no)
+ @name = name
+ end
+ attr :name
+ end
+
+ class TkVal < Token
+ def initialize(seek, line_no, char_no, value = nil)
+ super(seek, line_no, char_no)
+ @value = value
+ end
+ attr :value
+ end
+
+ class TkOp < Token
+ attr :name, true
+ end
+
+ class TkOPASGN < TkOp
+ def initialize(seek, line_no, char_no, op)
+ super(seek, line_no, char_no)
+ op = TkReading2Token[op][0] unless op.kind_of?(Symbol)
+ @op = op
+ end
+ attr :op
+ end
+
+ class TkUnknownChar < Token
+ def initialize(seek, line_no, char_no, id)
+ super(seek, line_no, char_no)
+ @name = name
+ end
+ attr :name
+ end
+
+ class TkError < Token
+ end
+
+ def Token(token, value = nil)
+ case token
+ when String
+ if (tk = TkReading2Token[token]).nil?
+ IRB.fail TkReading2TokenNoKey, token
+ end
+ tk = Token(tk[0], value)
+ if tk.kind_of?(TkOp)
+ tk.name = token
+ end
+ return tk
+ when Symbol
+ if (tk = TkSymbol2Token[token]).nil?
+ IRB.fail TkSymbol2TokenNoKey, token
+ end
+ return Token(tk[0], value)
+ else
+ if (token.ancestors & [TkId, TkVal, TkOPASGN, TkUnknownChar]).empty?
+ token.new(@prev_seek, @prev_line_no, @prev_char_no)
+ else
+ token.new(@prev_seek, @prev_line_no, @prev_char_no, value)
+ end
+ end
+ end
+
+ TokenDefinitions = [
+ [:TkCLASS, TkId, "class", EXPR_CLASS],
+ [:TkMODULE, TkId, "module", EXPR_BEG],
+ [:TkDEF, TkId, "def", EXPR_FNAME],
+ [:TkUNDEF, TkId, "undef", EXPR_FNAME],
+ [:TkBEGIN, TkId, "begin", EXPR_BEG],
+ [:TkRESCUE, TkId, "rescue", EXPR_MID],
+ [:TkENSURE, TkId, "ensure", EXPR_BEG],
+ [:TkEND, TkId, "end", EXPR_END],
+ [:TkIF, TkId, "if", EXPR_BEG, :TkIF_MOD],
+ [:TkUNLESS, TkId, "unless", EXPR_BEG, :TkUNLESS_MOD],
+ [:TkTHEN, TkId, "then", EXPR_BEG],
+ [:TkELSIF, TkId, "elsif", EXPR_BEG],
+ [:TkELSE, TkId, "else", EXPR_BEG],
+ [:TkCASE, TkId, "case", EXPR_BEG],
+ [:TkWHEN, TkId, "when", EXPR_BEG],
+ [:TkWHILE, TkId, "while", EXPR_BEG, :TkWHILE_MOD],
+ [:TkUNTIL, TkId, "until", EXPR_BEG, :TkUNTIL_MOD],
+ [:TkFOR, TkId, "for", EXPR_BEG],
+ [:TkBREAK, TkId, "break", EXPR_END],
+ [:TkNEXT, TkId, "next", EXPR_END],
+ [:TkREDO, TkId, "redo", EXPR_END],
+ [:TkRETRY, TkId, "retry", EXPR_END],
+ [:TkIN, TkId, "in", EXPR_BEG],
+ [:TkDO, TkId, "do", EXPR_BEG],
+ [:TkRETURN, TkId, "return", EXPR_MID],
+ [:TkYIELD, TkId, "yield", EXPR_END],
+ [:TkSUPER, TkId, "super", EXPR_END],
+ [:TkSELF, TkId, "self", EXPR_END],
+ [:TkNIL, TkId, "nil", EXPR_END],
+ [:TkTRUE, TkId, "true", EXPR_END],
+ [:TkFALSE, TkId, "false", EXPR_END],
+ [:TkAND, TkId, "and", EXPR_BEG],
+ [:TkOR, TkId, "or", EXPR_BEG],
+ [:TkNOT, TkId, "not", EXPR_BEG],
+ [:TkIF_MOD, TkId],
+ [:TkUNLESS_MOD, TkId],
+ [:TkWHILE_MOD, TkId],
+ [:TkUNTIL_MOD, TkId],
+ [:TkALIAS, TkId, "alias", EXPR_FNAME],
+ [:TkDEFINED, TkId, "defined?", EXPR_END],
+ [:TklBEGIN, TkId, "BEGIN", EXPR_END],
+ [:TklEND, TkId, "END", EXPR_END],
+ [:Tk__LINE__, TkId, "__LINE__", EXPR_END],
+ [:Tk__FILE__, TkId, "__FILE__", EXPR_END],
+
+ [:TkIDENTIFIER, TkId],
+ [:TkFID, TkId],
+ [:TkGVAR, TkId],
+ [:TkCVAR, TkId],
+ [:TkIVAR, TkId],
+ [:TkCONSTANT, TkId],
+
+ [:TkINTEGER, TkVal],
+ [:TkFLOAT, TkVal],
+ [:TkSTRING, TkVal],
+ [:TkXSTRING, TkVal],
+ [:TkREGEXP, TkVal],
+ [:TkSYMBOL, TkVal],
+
+ [:TkDSTRING, TkNode],
+ [:TkDXSTRING, TkNode],
+ [:TkDREGEXP, TkNode],
+ [:TkNTH_REF, TkNode],
+ [:TkBACK_REF, TkNode],
+
+ [:TkUPLUS, TkOp, "+@"],
+ [:TkUMINUS, TkOp, "-@"],
+ [:TkPOW, TkOp, "**"],
+ [:TkCMP, TkOp, "<=>"],
+ [:TkEQ, TkOp, "=="],
+ [:TkEQQ, TkOp, "==="],
+ [:TkNEQ, TkOp, "!="],
+ [:TkGEQ, TkOp, ">="],
+ [:TkLEQ, TkOp, "<="],
+ [:TkANDOP, TkOp, "&&"],
+ [:TkOROP, TkOp, "||"],
+ [:TkMATCH, TkOp, "=~"],
+ [:TkNMATCH, TkOp, "!~"],
+ [:TkDOT2, TkOp, ".."],
+ [:TkDOT3, TkOp, "..."],
+ [:TkAREF, TkOp, "[]"],
+ [:TkASET, TkOp, "[]="],
+ [:TkLSHFT, TkOp, "<<"],
+ [:TkRSHFT, TkOp, ">>"],
+ [:TkCOLON2, TkOp],
+ [:TkCOLON3, TkOp],
+# [:OPASGN, TkOp], # +=, -= etc. #
+ [:TkASSOC, TkOp, "=>"],
+ [:TkQUESTION, TkOp, "?"], #?
+ [:TkCOLON, TkOp, ":"], #:
+
+ [:TkfLPAREN], # func( #
+ [:TkfLBRACK], # func[ #
+ [:TkfLBRACE], # func{ #
+ [:TkSTAR], # *arg
+ [:TkAMPER], # &arg #
+ [:TkSYMBEG], # :SYMBOL
+
+ [:TkGT, TkOp, ">"],
+ [:TkLT, TkOp, "<"],
+ [:TkPLUS, TkOp, "+"],
+ [:TkMINUS, TkOp, "-"],
+ [:TkMULT, TkOp, "*"],
+ [:TkDIV, TkOp, "/"],
+ [:TkMOD, TkOp, "%"],
+ [:TkBITOR, TkOp, "|"],
+ [:TkBITXOR, TkOp, "^"],
+ [:TkBITAND, TkOp, "&"],
+ [:TkBITNOT, TkOp, "~"],
+ [:TkNOTOP, TkOp, "!"],
+
+ [:TkBACKQUOTE, TkOp, "`"],
+
+ [:TkASSIGN, Token, "="],
+ [:TkDOT, Token, "."],
+ [:TkLPAREN, Token, "("], #(exp)
+ [:TkLBRACK, Token, "["], #[arry]
+ [:TkLBRACE, Token, "{"], #{hash}
+ [:TkRPAREN, Token, ")"],
+ [:TkRBRACK, Token, "]"],
+ [:TkRBRACE, Token, "}"],
+ [:TkCOMMA, Token, ","],
+ [:TkSEMICOLON, Token, ";"],
+
+ [:TkCOMMENT],
+ [:TkRD_COMMENT],
+ [:TkSPACE],
+ [:TkNL],
+ [:TkEND_OF_SCRIPT],
+
+ [:TkBACKSLASH, TkUnknownChar, "\\"],
+ [:TkAT, TkUnknownChar, "@"],
+ [:TkDOLLAR, TkUnknownChar, "$"],
+ ]
+
+ # {reading => token_class}
+ # {reading => [token_class, *opt]}
+ TkReading2Token = {}
+ TkSymbol2Token = {}
+
+ def RubyToken.def_token(token_n, super_token = Token, reading = nil, *opts)
+ token_n = token_n.id2name if token_n.kind_of?(Symbol)
+ if RubyToken.const_defined?(token_n)
+ IRB.fail AlreadyDefinedToken, token_n
+ end
+ token_c = eval("class #{token_n} < #{super_token}; end; #{token_n}")
+
+ if reading
+ if TkReading2Token[reading]
+ IRB.fail TkReading2TokenDuplicateError, token_n, reading
+ end
+ if opts.empty?
+ TkReading2Token[reading] = [token_c]
+ else
+ TkReading2Token[reading] = [token_c].concat(opts)
+ end
+ end
+ TkSymbol2Token[token_n.intern] = token_c
+ end
+
+ for defs in TokenDefinitions
+ def_token(*defs)
+ end
+end
diff --git a/ruby_1_8_5/lib/irb/slex.rb b/ruby_1_8_5/lib/irb/slex.rb
new file mode 100644
index 0000000000..ed99e8fc86
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/slex.rb
@@ -0,0 +1,285 @@
+#
+# irb/slex.rb - symple lex analizer
+# $Release Version: 0.9.5$
+# $Revision: 1.6.2.3 $
+# $Date: 2005/04/19 19:24:57 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+
+require "e2mmap"
+require "irb/notifier"
+
+module IRB
+ class SLex
+ @RCS_ID='-$Id: slex.rb,v 1.6.2.3 2005/04/19 19:24:57 keiju Exp $-'
+
+ extend Exception2MessageMapper
+ def_exception :ErrNodeNothing, "node nothing"
+ def_exception :ErrNodeAlreadyExists, "node already exists"
+
+ DOUT = Notifier::def_notifier("SLex::")
+ D_WARN = DOUT::def_notifier(1, "Warn: ")
+ D_DEBUG = DOUT::def_notifier(2, "Debug: ")
+ D_DETAIL = DOUT::def_notifier(4, "Detail: ")
+
+ DOUT.level = Notifier::D_NOMSG
+
+ def initialize
+ @head = Node.new("")
+ end
+
+ def def_rule(token, preproc = nil, postproc = nil, &block)
+ D_DETAIL.pp token
+
+ postproc = block if block_given?
+ node = create(token, preproc, postproc)
+ end
+
+ def def_rules(*tokens, &block)
+ if block_given?
+ p = block
+ end
+ for token in tokens
+ def_rule(token, nil, p)
+ end
+ end
+
+ def preproc(token, proc)
+ node = search(token)
+ node.preproc=proc
+ end
+
+ #$BMW%A%'%C%/(B?
+ def postproc(token)
+ node = search(token, proc)
+ node.postproc=proc
+ end
+
+ def search(token)
+ @head.search(token.split(//))
+ end
+
+ def create(token, preproc = nil, postproc = nil)
+ @head.create_subnode(token.split(//), preproc, postproc)
+ end
+
+ def match(token)
+ case token
+ when Array
+ when String
+ return match(token.split(//))
+ else
+ return @head.match_io(token)
+ end
+ ret = @head.match(token)
+ D_DETAIL.exec_if{D_DEATIL.printf "match end: %s:%s\n", ret, token.inspect}
+ ret
+ end
+
+ def inspect
+ format("<SLex: @head = %s>", @head.inspect)
+ end
+
+ #----------------------------------------------------------------------
+ #
+ # class Node -
+ #
+ #----------------------------------------------------------------------
+ class Node
+ # if postproc is nil, this node is an abstract node.
+ # if postproc is non-nil, this node is a real node.
+ def initialize(preproc = nil, postproc = nil)
+ @Tree = {}
+ @preproc = preproc
+ @postproc = postproc
+ end
+
+ attr_accessor :preproc
+ attr_accessor :postproc
+
+ def search(chrs, opt = nil)
+ return self if chrs.empty?
+ ch = chrs.shift
+ if node = @Tree[ch]
+ node.search(chrs, opt)
+ else
+ if opt
+ chrs.unshift ch
+ self.create_subnode(chrs)
+ else
+ SLex.fail ErrNodeNothing
+ end
+ end
+ end
+
+ def create_subnode(chrs, preproc = nil, postproc = nil)
+ if chrs.empty?
+ if @postproc
+ D_DETAIL.pp node
+ SLex.fail ErrNodeAlreadyExists
+ else
+ D_DEBUG.puts "change abstract node to real node."
+ @preproc = preproc
+ @postproc = postproc
+ end
+ return self
+ end
+
+ ch = chrs.shift
+ if node = @Tree[ch]
+ if chrs.empty?
+ if node.postproc
+ DebugLogger.pp node
+ DebugLogger.pp self
+ DebugLogger.pp ch
+ DebugLogger.pp chrs
+ SLex.fail ErrNodeAlreadyExists
+ else
+ D_WARN.puts "change abstract node to real node"
+ node.preproc = preproc
+ node.postproc = postproc
+ end
+ else
+ node.create_subnode(chrs, preproc, postproc)
+ end
+ else
+ if chrs.empty?
+ node = Node.new(preproc, postproc)
+ else
+ node = Node.new
+ node.create_subnode(chrs, preproc, postproc)
+ end
+ @Tree[ch] = node
+ end
+ node
+ end
+
+ #
+ # chrs: String
+ # character array
+ # io must have getc()/ungetc(); and ungetc() must be
+ # able to be called arbitrary number of times.
+ #
+ def match(chrs, op = "")
+ D_DETAIL.print "match>: ", chrs, "op:", op, "\n"
+ if chrs.empty?
+ if @preproc.nil? || @preproc.call(op, chrs)
+ DOUT.printf(D_DETAIL, "op1: %s\n", op)
+ @postproc.call(op, chrs)
+ else
+ nil
+ end
+ else
+ ch = chrs.shift
+ if node = @Tree[ch]
+ if ret = node.match(chrs, op+ch)
+ return ret
+ else
+ chrs.unshift ch
+ if @postproc and @preproc.nil? || @preproc.call(op, chrs)
+ DOUT.printf(D_DETAIL, "op2: %s\n", op.inspect)
+ ret = @postproc.call(op, chrs)
+ return ret
+ else
+ return nil
+ end
+ end
+ else
+ chrs.unshift ch
+ if @postproc and @preproc.nil? || @preproc.call(op, chrs)
+ DOUT.printf(D_DETAIL, "op3: %s\n", op)
+ @postproc.call(op, chrs)
+ return ""
+ else
+ return nil
+ end
+ end
+ end
+ end
+
+ def match_io(io, op = "")
+ if op == ""
+ ch = io.getc
+ if ch == nil
+ return nil
+ end
+ else
+ ch = io.getc_of_rests
+ end
+ if ch.nil?
+ if @preproc.nil? || @preproc.call(op, io)
+ D_DETAIL.printf("op1: %s\n", op)
+ @postproc.call(op, io)
+ else
+ nil
+ end
+ else
+ if node = @Tree[ch]
+ if ret = node.match_io(io, op+ch)
+ ret
+ else
+ io.ungetc ch
+ if @postproc and @preproc.nil? || @preproc.call(op, io)
+ DOUT.exec_if{D_DETAIL.printf "op2: %s\n", op.inspect}
+ @postproc.call(op, io)
+ else
+ nil
+ end
+ end
+ else
+ io.ungetc ch
+ if @postproc and @preproc.nil? || @preproc.call(op, io)
+ D_DETAIL.printf("op3: %s\n", op)
+ @postproc.call(op, io)
+ else
+ nil
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+SLex=IRB::SLex
+
+if $0 == __FILE__
+ # Tracer.on
+ case $1
+ when "1"
+ tr = SLex.new
+ print "0: ", tr.inspect, "\n"
+ tr.def_rule("=") {print "=\n"}
+ print "1: ", tr.inspect, "\n"
+ tr.def_rule("==") {print "==\n"}
+ print "2: ", tr.inspect, "\n"
+
+ print "case 1:\n"
+ print tr.match("="), "\n"
+ print "case 2:\n"
+ print tr.match("=="), "\n"
+ print "case 3:\n"
+ print tr.match("=>"), "\n"
+
+ when "2"
+ tr = SLex.new
+ print "0: ", tr.inspect, "\n"
+ tr.def_rule("=") {print "=\n"}
+ print "1: ", tr.inspect, "\n"
+ tr.def_rule("==", proc{false}) {print "==\n"}
+ print "2: ", tr.inspect, "\n"
+
+ print "case 1:\n"
+ print tr.match("="), "\n"
+ print "case 2:\n"
+ print tr.match("=="), "\n"
+ print "case 3:\n"
+ print tr.match("=>"), "\n"
+ end
+ exit
+end
+
diff --git a/ruby_1_8_5/lib/irb/version.rb b/ruby_1_8_5/lib/irb/version.rb
new file mode 100644
index 0000000000..95192669db
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/version.rb
@@ -0,0 +1,16 @@
+#
+# irb/version.rb - irb version definition file
+# $Release Version: 0.9.5$
+# $Revision: 1.4.2.1 $
+# $Date: 2005/04/19 19:24:57 $
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
+#
+# --
+#
+#
+#
+
+module IRB
+ @RELEASE_VERSION = "0.9.5"
+ @LAST_UPDATE_DATE = "05/04/13"
+end
diff --git a/ruby_1_8_5/lib/irb/workspace.rb b/ruby_1_8_5/lib/irb/workspace.rb
new file mode 100644
index 0000000000..364820f997
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/workspace.rb
@@ -0,0 +1,107 @@
+#
+# irb/workspace-binding.rb -
+# $Release Version: 0.9.5$
+# $Revision: 1.5.2.2 $
+# $Date: 2005/04/19 19:24:57 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+module IRB
+ class WorkSpace
+ # create new workspace. set self to main if specified, otherwise
+ # inherit main from TOPLEVEL_BINDING.
+ def initialize(*main)
+ if main[0].kind_of?(Binding)
+ @binding = main.shift
+ elsif IRB.conf[:SINGLE_IRB]
+ @binding = TOPLEVEL_BINDING
+ else
+ case IRB.conf[:CONTEXT_MODE]
+ when 0 # binding in proc on TOPLEVEL_BINDING
+ @binding = eval("proc{binding}.call",
+ TOPLEVEL_BINDING,
+ __FILE__,
+ __LINE__)
+ when 1 # binding in loaded file
+ require "tempfile"
+ f = Tempfile.open("irb-binding")
+ f.print <<EOF
+ $binding = binding
+EOF
+ f.close
+ load f.path
+ @binding = $binding
+
+ when 2 # binding in loaded file(thread use)
+ unless defined? BINDING_QUEUE
+ require "thread"
+
+ IRB.const_set("BINDING_QUEUE", SizedQueue.new(1))
+ Thread.abort_on_exception = true
+ Thread.start do
+ eval "require \"irb/ws-for-case-2\"", TOPLEVEL_BINDING, __FILE__, __LINE__
+ end
+ Thread.pass
+ end
+ @binding = BINDING_QUEUE.pop
+
+ when 3 # binging in function on TOPLEVEL_BINDING(default)
+ @binding = eval("def irb_binding; binding; end; irb_binding",
+ TOPLEVEL_BINDING,
+ __FILE__,
+ __LINE__ - 3)
+ end
+ end
+ if main.empty?
+ @main = eval("self", @binding)
+ else
+ @main = main[0]
+ IRB.conf[:__MAIN__] = @main
+ case @main
+ when Module
+ @binding = eval("IRB.conf[:__MAIN__].module_eval('binding', __FILE__, __LINE__)", @binding, __FILE__, __LINE__)
+ else
+ begin
+ @binding = eval("IRB.conf[:__MAIN__].instance_eval('binding', __FILE__, __LINE__)", @binding, __FILE__, __LINE__)
+ rescue TypeError
+ IRB.fail CantChangeBinding, @main.inspect
+ end
+ end
+ end
+ eval("_=nil", @binding)
+ end
+
+ attr_reader :binding
+ attr_reader :main
+
+ def evaluate(context, statements, file = __FILE__, line = __LINE__)
+ eval(statements, @binding, file, line)
+ end
+
+ # error message manipulator
+ def filter_backtrace(bt)
+ case IRB.conf[:CONTEXT_MODE]
+ when 0
+ return nil if bt =~ /\(irb_local_binding\)/
+ when 1
+ if(bt =~ %r!/tmp/irb-binding! or
+ bt =~ %r!irb/.*\.rb! or
+ bt =~ /irb\.rb/)
+ return nil
+ end
+ when 2
+ return nil if bt =~ /irb\/.*\.rb/
+ when 3
+ return nil if bt =~ /irb\/.*\.rb/
+ bt.sub!(/:\s*in `irb_binding'/){""}
+ end
+ bt
+ end
+
+ def IRB.delete_caller
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/irb/ws-for-case-2.rb b/ruby_1_8_5/lib/irb/ws-for-case-2.rb
new file mode 100644
index 0000000000..ca30eb34c0
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/ws-for-case-2.rb
@@ -0,0 +1,15 @@
+#
+# irb/ws-for-case-2.rb -
+# $Release Version: 0.9.5$
+# $Revision: 1.2.2.1 $
+# $Date: 2005/04/19 19:24:57 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+
+while true
+ IRB::BINDING_QUEUE.push b = binding
+end
diff --git a/ruby_1_8_5/lib/irb/xmp.rb b/ruby_1_8_5/lib/irb/xmp.rb
new file mode 100644
index 0000000000..0a0883245b
--- /dev/null
+++ b/ruby_1_8_5/lib/irb/xmp.rb
@@ -0,0 +1,86 @@
+#
+# xmp.rb - irb version of gotoken xmp
+# $Release Version: 0.9$
+# $Revision: 1.3 $
+# $Date: 2002/07/09 11:17:16 $
+# by Keiju ISHITSUKA(Nippon Rational Inc.)
+#
+# --
+#
+#
+#
+
+require "irb"
+require "irb/frame"
+
+class XMP
+ @RCS_ID='-$Id: xmp.rb,v 1.3 2002/07/09 11:17:16 keiju Exp $-'
+
+ def initialize(bind = nil)
+ IRB.init_config(nil)
+ #IRB.parse_opts
+ #IRB.load_modules
+
+ IRB.conf[:PROMPT_MODE] = :XMP
+
+ bind = IRB::Frame.top(1) unless bind
+ ws = IRB::WorkSpace.new(bind)
+ @io = StringInputMethod.new
+ @irb = IRB::Irb.new(ws, @io)
+ @irb.context.ignore_sigint = false
+
+# IRB.conf[:IRB_RC].call(@irb.context) if IRB.conf[:IRB_RC]
+ IRB.conf[:MAIN_CONTEXT] = @irb.context
+ end
+
+ def puts(exps)
+ @io.puts exps
+
+ if @irb.context.ignore_sigint
+ begin
+ trap_proc_b = trap("SIGINT"){@irb.signal_handle}
+ catch(:IRB_EXIT) do
+ @irb.eval_input
+ end
+ ensure
+ trap("SIGINT", trap_proc_b)
+ end
+ else
+ catch(:IRB_EXIT) do
+ @irb.eval_input
+ end
+ end
+ end
+
+ class StringInputMethod < IRB::InputMethod
+ def initialize
+ super
+ @exps = []
+ end
+
+ def eof?
+ @exps.empty?
+ end
+
+ def gets
+ while l = @exps.shift
+ next if /^\s+$/ =~ l
+ l.concat "\n"
+ print @prompt, l
+ break
+ end
+ l
+ end
+
+ def puts(exps)
+ @exps.concat exps.split(/\n/)
+ end
+ end
+end
+
+def xmp(exps, bind = nil)
+ bind = IRB::Frame.top(1) unless bind
+ xmp = XMP.new(bind)
+ xmp.puts exps
+ xmp
+end
diff --git a/ruby_1_8_5/lib/jcode.rb b/ruby_1_8_5/lib/jcode.rb
new file mode 100644
index 0000000000..e5367e815b
--- /dev/null
+++ b/ruby_1_8_5/lib/jcode.rb
@@ -0,0 +1,220 @@
+# jcode.rb - ruby code to handle japanese (EUC/SJIS) string
+
+if $VERBOSE && $KCODE == "NONE"
+ warn "Warning: $KCODE is NONE."
+end
+
+$vsave, $VERBOSE = $VERBOSE, false
+class String
+ warn "feel free for some warnings:\n" if $VERBOSE
+
+ def _regex_quote(str)
+ str.gsub(/(\\[\[\]\-\\])|\\(.)|([\[\]\\])/) do
+ $1 || $2 || '\\' + $3
+ end
+ end
+ private :_regex_quote
+
+ 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, 0, 'n')
+ RE_EUC = Regexp.new(PATTERN_EUC, 0, 'n')
+ RE_UTF8 = Regexp.new(PATTERN_UTF8, 0, '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 =~ RE_SJIS
+ when ?e, ?E
+ self =~ RE_EUC
+ when ?u, ?U
+ self =~ RE_UTF8
+ else
+ nil
+ end
+ end
+
+ def end_regexp
+ case $KCODE[0]
+ when ?s, ?S
+ /#{PATTERN_SJIS}$/on
+ when ?e, ?E
+ /#{PATTERN_EUC}$/on
+ when ?u, ?U
+ /#{PATTERN_UTF8}$/on
+ else
+ /.$/on
+ end
+ end
+
+ alias original_succ! succ!
+ private :original_succ!
+
+ 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
+ original_succ!
+ end
+ end
+
+ def succ
+ str = self.dup
+ str.succ! or str
+ end
+
+ private
+
+ def _expand_ch str
+ a = []
+ str.scan(/(?:\\(.)|([^\\]))-(?:\\(.)|([^\\]))|(?:\\(.)|(.))/m) do
+ from = $1 || $2
+ to = $3 || $4
+ one = $5 || $6
+ if one
+ a.push one
+ elsif from.length != to.length
+ next
+ elsif from.length == 1
+ from[0].upto(to[0]) { |c| a.push c.chr }
+ else
+ from.upto(to) { |c| a.push c }
+ end
+ end
+ a
+ end
+
+ def expand_ch_hash from, to
+ h = {}
+ afrom = _expand_ch(from)
+ ato = _expand_ch(to)
+ afrom.each_with_index do |x,i| h[x] = ato[i] || ato[-1] end
+ h
+ end
+
+ HashCache = {}
+ TrPatternCache = {}
+ DeletePatternCache = {}
+ SqueezePatternCache = {}
+
+ public
+
+ def tr!(from, to)
+ return nil if from == ""
+ return self.delete!(from) if to == ""
+
+ pattern = TrPatternCache[from] ||= /[#{_regex_quote(from)}]/
+ if from[0] == ?^
+ last = /.$/.match(to)[0]
+ self.gsub!(pattern, last)
+ else
+ h = HashCache[from + "1-0" + to] ||= expand_ch_hash(from, to)
+ self.gsub!(pattern) do |c| h[c] end
+ end
+ end
+
+ def tr(from, to)
+ (str = self.dup).tr!(from, to) or str
+ end
+
+ def delete!(del)
+ return nil if del == ""
+ self.gsub!(DeletePatternCache[del] ||= /[#{_regex_quote(del)}]+/, '')
+ end
+
+ def delete(del)
+ (str = self.dup).delete!(del) or str
+ end
+
+ def squeeze!(del=nil)
+ return nil if del == ""
+ pattern =
+ if del
+ SqueezePatternCache[del] ||= /([#{_regex_quote(del)}])\1+/
+ else
+ /(.|\n)\1+/
+ end
+ self.gsub!(pattern, '\1')
+ end
+
+ def squeeze(del=nil)
+ (str = self.dup).squeeze!(del) or str
+ end
+
+ def tr_s!(from, to)
+ return self.delete!(from) if to.length == 0
+
+ pattern = SqueezePatternCache[from] ||= /([#{_regex_quote(from)}])\1*/
+ if from[0] == ?^
+ last = /.$/.match(to)[0]
+ self.gsub!(pattern, last)
+ else
+ h = HashCache[from + "1-0" + to] ||= expand_ch_hash(from, to)
+ self.gsub!(pattern) do h[$1] end
+ end
+ end
+
+ def tr_s(from, to)
+ (str = self.dup).tr_s!(from,to) or str
+ end
+
+ def chop!
+ self.gsub!(/(?:.|\r?\n)\z/, '')
+ end
+
+ def chop
+ (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 block_given?
+ scan(/./m) do |x|
+ yield x
+ end
+ else
+ scan(/./m)
+ end
+ end
+
+end
+$VERBOSE = $vsave
diff --git a/ruby_1_8_5/lib/logger.rb b/ruby_1_8_5/lib/logger.rb
new file mode 100644
index 0000000000..3012ac2cc7
--- /dev/null
+++ b/ruby_1_8_5/lib/logger.rb
@@ -0,0 +1,703 @@
+# logger.rb - saimple logging utility
+# Copyright (C) 2000-2003, 2005 NAKAMURA, Hiroshi <nakahiro@sarion.co.jp>.
+
+require 'monitor'
+
+# Simple logging utility.
+#
+# Author:: NAKAMURA, Hiroshi <nakahiro@sarion.co.jp>
+# Documentation:: NAKAMURA, Hiroshi and Gavin Sinclair
+# License::
+# You can redistribute it and/or modify it under the same terms of Ruby's
+# license; either the dual license version in 2003, or any later version.
+# Revision:: $Id: logger.rb,v 1.5.2.9 2006/08/04 22:00:21 drbrain Exp $
+#
+# == Description
+#
+# The Logger class provides a simple but sophisticated logging utility that
+# anyone can use because it's included in the Ruby 1.8.x standard library.
+#
+# The HOWTOs below give a code-based overview of Logger's usage, but the basic
+# concept is as follows. You create a Logger object (output to a file or
+# elsewhere), and use it to log messages. The messages will have varying
+# levels (+info+, +error+, etc), reflecting their varying importance. The
+# levels, and their meanings, are:
+#
+# +FATAL+:: an unhandleable error that results in a program crash
+# +ERROR+:: a handleable error condition
+# +WARN+:: a warning
+# +INFO+:: generic (useful) information about system operation
+# +DEBUG+:: low-level information for developers
+#
+# So each message has a level, and the Logger itself has a level, which acts
+# as a filter, so you can control the amount of information emitted from the
+# logger without having to remove actual messages.
+#
+# For instance, in a production system, you may have your logger(s) set to
+# +INFO+ (or +WARN+ if you don't want the log files growing large with
+# repetitive information). When you are developing it, though, you probably
+# want to know about the program's internal state, and would set them to
+# +DEBUG+.
+#
+# === Example
+#
+# A simple example demonstrates the above explanation:
+#
+# log = Logger.new(STDOUT)
+# log.level = Logger::WARN
+#
+# log.debug("Created logger")
+# log.info("Program started")
+# log.warn("Nothing to do!")
+#
+# begin
+# File.each_line(path) do |line|
+# unless line =~ /^(\w+) = (.*)$/
+# log.error("Line in wrong format: #{line}")
+# end
+# end
+# rescue => err
+# log.fatal("Caught exception; exiting")
+# log.fatal(err)
+# end
+#
+# Because the Logger's level is set to +WARN+, only the warning, error, and
+# fatal messages are recorded. The debug and info messages are silently
+# discarded.
+#
+# === Features
+#
+# There are several interesting features that Logger provides, like
+# auto-rolling of log files, setting the format of log messages, and
+# specifying a program name in conjunction with the message. The next section
+# shows you how to achieve these things.
+#
+#
+# == HOWTOs
+#
+# === How to create a logger
+#
+# The options below give you various choices, in more or less increasing
+# complexity.
+#
+# 1. Create a logger which logs messages to STDERR/STDOUT.
+#
+# logger = Logger.new(STDERR)
+# logger = Logger.new(STDOUT)
+#
+# 2. Create a logger for the file which has the specified name.
+#
+# logger = Logger.new('logfile.log')
+#
+# 3. Create a logger for the specified file.
+#
+# file = File.open('foo.log', File::WRONLY | File::APPEND)
+# # To create new (and to remove old) logfile, add File::CREAT like;
+# # file = open('foo.log', File::WRONLY | File::APPEND | File::CREAT)
+# logger = Logger.new(file)
+#
+# 4. Create a logger which ages logfile once it reaches a certain size. Leave
+# 10 "old log files" and each file is about 1,024,000 bytes.
+#
+# logger = Logger.new('foo.log', 10, 1024000)
+#
+# 5. Create a logger which ages logfile daily/weekly/monthly.
+#
+# logger = Logger.new('foo.log', 'daily')
+# logger = Logger.new('foo.log', 'weekly')
+# logger = Logger.new('foo.log', 'monthly')
+#
+# === How to log a message
+#
+# Notice the different methods (+fatal+, +error+, +info+) being used to log
+# messages of various levels. Other methods in this family are +warn+ and
+# +debug+. +add+ is used below to log a message of an arbitrary (perhaps
+# dynamic) level.
+#
+# 1. Message in block.
+#
+# logger.fatal { "Argument 'foo' not given." }
+#
+# 2. Message as a string.
+#
+# logger.error "Argument #{ @foo } mismatch."
+#
+# 3. With progname.
+#
+# logger.info('initialize') { "Initializing..." }
+#
+# 4. With severity.
+#
+# logger.add(Logger::FATAL) { 'Fatal error!' }
+#
+# === How to close a logger
+#
+# logger.close
+#
+# === Setting severity threshold
+#
+# 1. Original interface.
+#
+# logger.sev_threshold = Logger::WARN
+#
+# 2. Log4r (somewhat) compatible interface.
+#
+# logger.level = Logger::INFO
+#
+# DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN
+#
+#
+# == Format
+#
+# Log messages are rendered in the output stream in a certain format. The
+# default format and a sample are shown below:
+#
+# Log format:
+# SeverityID, [Date Time mSec #pid] SeverityLabel -- ProgName: message
+#
+# Log sample:
+# I, [Wed Mar 03 02:34:24 JST 1999 895701 #19074] INFO -- Main: info.
+#
+# You may change the date and time format in this manner:
+#
+# logger.datetime_format = "%Y-%m-%d %H:%M:%S"
+# # e.g. "2004-01-03 00:54:26"
+#
+# There is currently no supported way to change the overall format, but you may
+# have some luck hacking the Format constant.
+#
+
+
+class Logger
+ VERSION = "1.2.6"
+ /: (\S+),v (\S+)/ =~ %q$Id: logger.rb,v 1.5.2.9 2006/08/04 22:00:21 drbrain Exp $
+ ProgName = "#{$1}/#{$2}"
+
+ class Error < RuntimeError; end
+ class ShiftingError < Error; end
+
+ # Logging severity.
+ module Severity
+ DEBUG = 0
+ INFO = 1
+ WARN = 2
+ ERROR = 3
+ FATAL = 4
+ UNKNOWN = 5
+ end
+ include Severity
+
+ # Logging severity threshold (e.g. <tt>Logger::INFO</tt>).
+ attr_accessor :level
+
+ # Logging program name.
+ attr_accessor :progname
+
+ # Logging date-time format (string passed to +strftime+).
+ def datetime_format=(datetime_format)
+ @default_formatter.datetime_format = datetime_format
+ end
+
+ def datetime_format
+ @default_formatter.datetime_format
+ end
+
+ # Logging formatter. formatter#call is invoked with 4 arguments; severity,
+ # time, progname and msg for each log. Bear in mind that time is a Time and
+ # msg is an Object that user passed and it could not be a String. It is
+ # expected to return a logdev#write-able Object. Default formatter is used
+ # when no formatter is set.
+ attr_accessor :formatter
+
+ alias sev_threshold level
+ alias sev_threshold= level=
+
+ # Returns +true+ iff the current severity level allows for the printing of
+ # +DEBUG+ messages.
+ def debug?; @level <= DEBUG; end
+
+ # Returns +true+ iff the current severity level allows for the printing of
+ # +INFO+ messages.
+ def info?; @level <= INFO; end
+
+ # Returns +true+ iff the current severity level allows for the printing of
+ # +WARN+ messages.
+ def warn?; @level <= WARN; end
+
+ # Returns +true+ iff the current severity level allows for the printing of
+ # +ERROR+ messages.
+ def error?; @level <= ERROR; end
+
+ # Returns +true+ iff the current severity level allows for the printing of
+ # +FATAL+ messages.
+ def fatal?; @level <= FATAL; end
+
+ #
+ # === Synopsis
+ #
+ # Logger.new(name, shift_age = 7, shift_size = 1048576)
+ # Logger.new(name, shift_age = 'weekly')
+ #
+ # === Args
+ #
+ # +logdev+::
+ # The log device. This is a filename (String) or IO object (typically
+ # +STDOUT+, +STDERR+, or an open file).
+ # +shift_age+::
+ # Number of old log files to keep, *or* frequency of rotation (+daily+,
+ # +weekly+ or +monthly+).
+ # +shift_size+::
+ # Maximum logfile size (only applies when +shift_age+ is a number).
+ #
+ # === Description
+ #
+ # Create an instance.
+ #
+ def initialize(logdev, shift_age = 0, shift_size = 1048576)
+ @progname = nil
+ @level = DEBUG
+ @default_formatter = Formatter.new
+ @formatter = nil
+ @logdev = nil
+ if logdev
+ @logdev = LogDevice.new(logdev, :shift_age => shift_age,
+ :shift_size => shift_size)
+ end
+ end
+
+ #
+ # === Synopsis
+ #
+ # Logger#add(severity, message = nil, progname = nil) { ... }
+ #
+ # === Args
+ #
+ # +severity+::
+ # Severity. Constants are defined in Logger namespace: +DEBUG+, +INFO+,
+ # +WARN+, +ERROR+, +FATAL+, or +UNKNOWN+.
+ # +message+::
+ # The log message. A String or Exception.
+ # +progname+::
+ # Program name string. Can be omitted. Treated as a message if no +message+ and
+ # +block+ are given.
+ # +block+::
+ # Can be omitted. Called to get a message string if +message+ is nil.
+ #
+ # === Return
+ #
+ # +true+ if successful, +false+ otherwise.
+ #
+ # When the given severity is not high enough (for this particular logger), log
+ # no message, and return +true+.
+ #
+ # === Description
+ #
+ # Log a message if the given severity is high enough. This is the generic
+ # logging method. Users will be more inclined to use #debug, #info, #warn,
+ # #error, and #fatal.
+ #
+ # <b>Message format</b>: +message+ can be any object, but it has to be
+ # converted to a String in order to log it. Generally, +inspect+ is used
+ # if the given object is not a String.
+ # A special case is an +Exception+ object, which will be printed in detail,
+ # including message, class, and backtrace. See #msg2str for the
+ # implementation if required.
+ #
+ # === Bugs
+ #
+ # * Logfile is not locked.
+ # * Append open does not need to lock file.
+ # * But on the OS which supports multi I/O, records possibly be mixed.
+ #
+ def add(severity, message = nil, progname = nil, &block)
+ severity ||= UNKNOWN
+ if @logdev.nil? or severity < @level
+ return true
+ end
+ progname ||= @progname
+ if message.nil?
+ if block_given?
+ message = yield
+ else
+ message = progname
+ progname = @progname
+ end
+ end
+ @logdev.write(
+ format_message(format_severity(severity), Time.now, progname, message))
+ true
+ end
+ alias log add
+
+ #
+ # Dump given message to the log device without any formatting. If no log
+ # device exists, return +nil+.
+ #
+ def <<(msg)
+ unless @logdev.nil?
+ @logdev.write(msg)
+ end
+ end
+
+ #
+ # Log a +DEBUG+ message.
+ #
+ # See #info for more information.
+ #
+ def debug(progname = nil, &block)
+ add(DEBUG, nil, progname, &block)
+ end
+
+ #
+ # Log an +INFO+ message.
+ #
+ # The message can come either from the +progname+ argument or the +block+. If
+ # both are provided, then the +block+ is used as the message, and +progname+
+ # is used as the program name.
+ #
+ # === Examples
+ #
+ # logger.info("MainApp") { "Received connection from #{ip}" }
+ # # ...
+ # logger.info "Waiting for input from user"
+ # # ...
+ # logger.info { "User typed #{input}" }
+ #
+ # You'll probably stick to the second form above, unless you want to provide a
+ # program name (which you can do with <tt>Logger#progname=</tt> as well).
+ #
+ # === Return
+ #
+ # See #add.
+ #
+ def info(progname = nil, &block)
+ add(INFO, nil, progname, &block)
+ end
+
+ #
+ # Log a +WARN+ message.
+ #
+ # See #info for more information.
+ #
+ def warn(progname = nil, &block)
+ add(WARN, nil, progname, &block)
+ end
+
+ #
+ # Log an +ERROR+ message.
+ #
+ # See #info for more information.
+ #
+ def error(progname = nil, &block)
+ add(ERROR, nil, progname, &block)
+ end
+
+ #
+ # Log a +FATAL+ message.
+ #
+ # See #info for more information.
+ #
+ def fatal(progname = nil, &block)
+ add(FATAL, nil, progname, &block)
+ end
+
+ #
+ # Log an +UNKNOWN+ message. This will be printed no matter what the logger
+ # level.
+ #
+ # See #info for more information.
+ #
+ def unknown(progname = nil, &block)
+ add(UNKNOWN, nil, progname, &block)
+ end
+
+ #
+ # Close the logging device.
+ #
+ def close
+ @logdev.close if @logdev
+ end
+
+private
+
+ # Severity label for logging. (max 5 char)
+ SEV_LABEL = %w(DEBUG INFO WARN ERROR FATAL ANY)
+
+ def format_severity(severity)
+ SEV_LABEL[severity] || 'ANY'
+ end
+
+ def format_message(severity, datetime, progname, msg)
+ (@formatter || @default_formatter).call(severity, datetime, progname, msg)
+ end
+
+
+ class Formatter
+ Format = "%s, [%s#%d] %5s -- %s: %s\n"
+
+ attr_accessor :datetime_format
+
+ def initialize
+ @datetime_format = nil
+ end
+
+ def call(severity, time, progname, msg)
+ Format % [severity[0..0], format_datetime(time), $$, severity, progname,
+ msg2str(msg)]
+ end
+
+ private
+
+ def format_datetime(time)
+ if @datetime_format.nil?
+ time.strftime("%Y-%m-%dT%H:%M:%S.") << "%06d " % time.usec
+ else
+ time.strftime(@datetime_format)
+ end
+ end
+
+ def msg2str(msg)
+ case msg
+ when ::String
+ msg
+ when ::Exception
+ "#{ msg.message } (#{ msg.class })\n" <<
+ (msg.backtrace || []).join("\n")
+ else
+ msg.inspect
+ end
+ end
+ end
+
+
+ class LogDevice
+ attr_reader :dev
+ attr_reader :filename
+
+ class LogDeviceMutex
+ include MonitorMixin
+ end
+
+ def initialize(log = nil, opt = {})
+ @dev = @filename = @shift_age = @shift_size = nil
+ @mutex = LogDeviceMutex.new
+ if log.respond_to?(:write) and log.respond_to?(:close)
+ @dev = log
+ else
+ @dev = open_logfile(log)
+ @dev.sync = true
+ @filename = log
+ @shift_age = opt[:shift_age] || 7
+ @shift_size = opt[:shift_size] || 1048576
+ end
+ end
+
+ def write(message)
+ @mutex.synchronize do
+ if @shift_age and @dev.respond_to?(:stat)
+ begin
+ check_shift_log
+ rescue
+ raise Logger::ShiftingError.new("Shifting failed. #{$!}")
+ end
+ end
+ @dev.write(message)
+ end
+ end
+
+ def close
+ @mutex.synchronize do
+ @dev.close
+ end
+ end
+
+ private
+
+ def open_logfile(filename)
+ if (FileTest.exist?(filename))
+ open(filename, (File::WRONLY | File::APPEND))
+ else
+ create_logfile(filename)
+ end
+ end
+
+ def create_logfile(filename)
+ logdev = open(filename, (File::WRONLY | File::APPEND | File::CREAT))
+ logdev.sync = true
+ add_log_header(logdev)
+ logdev
+ end
+
+ def add_log_header(file)
+ file.write(
+ "# Logfile created on %s by %s\n" % [Time.now.to_s, Logger::ProgName]
+ )
+ end
+
+ SiD = 24 * 60 * 60
+
+ def check_shift_log
+ if @shift_age.is_a?(Integer)
+ # Note: always returns false if '0'.
+ if @filename && (@shift_age > 0) && (@dev.stat.size > @shift_size)
+ shift_log_age
+ end
+ else
+ now = Time.now
+ if @dev.stat.mtime <= previous_period_end(now)
+ shift_log_period(now)
+ end
+ end
+ end
+
+ def shift_log_age
+ (@shift_age-3).downto(0) do |i|
+ if FileTest.exist?("#{@filename}.#{i}")
+ File.rename("#{@filename}.#{i}", "#{@filename}.#{i+1}")
+ end
+ end
+ @dev.close
+ File.rename("#{@filename}", "#{@filename}.0")
+ @dev = create_logfile(@filename)
+ return true
+ end
+
+ def shift_log_period(now)
+ postfix = previous_period_end(now).strftime("%Y%m%d") # YYYYMMDD
+ age_file = "#{@filename}.#{postfix}"
+ if FileTest.exist?(age_file)
+ raise RuntimeError.new("'#{ age_file }' already exists.")
+ end
+ @dev.close
+ File.rename("#{@filename}", age_file)
+ @dev = create_logfile(@filename)
+ return true
+ end
+
+ def previous_period_end(now)
+ case @shift_age
+ when /^daily$/
+ eod(now - 1 * SiD)
+ when /^weekly$/
+ eod(now - ((now.wday + 1) * SiD))
+ when /^monthly$/
+ eod(now - now.mday * SiD)
+ else
+ now
+ end
+ end
+
+ def eod(t)
+ Time.mktime(t.year, t.month, t.mday, 23, 59, 59)
+ end
+ end
+
+
+ #
+ # == Description
+ #
+ # Application -- Add logging support to your application.
+ #
+ # == Usage
+ #
+ # 1. Define your application class as a sub-class of this class.
+ # 2. Override 'run' method in your class to do many things.
+ # 3. Instantiate it and invoke 'start'.
+ #
+ # == Example
+ #
+ # class FooApp < Application
+ # def initialize(foo_app, application_specific, arguments)
+ # super('FooApp') # Name of the application.
+ # end
+ #
+ # def run
+ # ...
+ # log(WARN, 'warning', 'my_method1')
+ # ...
+ # @log.error('my_method2') { 'Error!' }
+ # ...
+ # end
+ # end
+ #
+ # status = FooApp.new(....).start
+ #
+ class Application
+ include Logger::Severity
+
+ attr_reader :appname
+ attr_reader :logdev
+
+ #
+ # == Synopsis
+ #
+ # Application.new(appname = '')
+ #
+ # == Args
+ #
+ # +appname+:: Name of the application.
+ #
+ # == Description
+ #
+ # Create an instance. Log device is +STDERR+ by default. This can be
+ # changed with #set_log.
+ #
+ def initialize(appname = nil)
+ @appname = appname
+ @log = Logger.new(STDERR)
+ @log.progname = @appname
+ @level = @log.level
+ end
+
+ #
+ # Start the application. Return the status code.
+ #
+ def start
+ status = -1
+ begin
+ log(INFO, "Start of #{ @appname }.")
+ status = run
+ rescue
+ log(FATAL, "Detected an exception. Stopping ... #{$!} (#{$!.class})\n" << $@.join("\n"))
+ ensure
+ log(INFO, "End of #{ @appname }. (status: #{ status.to_s })")
+ end
+ status
+ end
+
+ #
+ # Sets the log device for this application. See the class Logger for an
+ # explanation of the arguments.
+ #
+ def set_log(logdev, shift_age = 0, shift_size = 1024000)
+ @log = Logger.new(logdev, shift_age, shift_size)
+ @log.progname = @appname
+ @log.level = @level
+ end
+
+ def log=(logdev)
+ set_log(logdev)
+ end
+
+ #
+ # Set the logging threshold, just like <tt>Logger#level=</tt>.
+ #
+ def level=(level)
+ @level = level
+ @log.level = @level
+ end
+
+ #
+ # See Logger#add. This application's +appname+ is used.
+ #
+ def log(severity, message = nil, &block)
+ @log.add(severity, message, @appname, &block) if @log
+ end
+
+ private
+
+ def run
+ raise RuntimeError.new('Method run must be defined in the derived class.')
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/mailread.rb b/ruby_1_8_5/lib/mailread.rb
new file mode 100644
index 0000000000..08b33c92a4
--- /dev/null
+++ b/ruby_1_8_5/lib/mailread.rb
@@ -0,0 +1,62 @@
+# The Mail class represents an internet mail message (as per RFC822, RFC2822)
+# with headers and a body.
+class Mail
+
+ # Create a new Mail where +f+ is either a stream which responds to gets(),
+ # or a path to a file. If +f+ is a path it will be opened.
+ #
+ # The whole message is read so it can be made available through the #header,
+ # #[] and #body methods.
+ #
+ # The "From " line is ignored if the mail is in mbox format.
+ def initialize(f)
+ unless defined? f.gets
+ f = open(f, "r")
+ opened = true
+ end
+
+ @header = {}
+ @body = []
+ begin
+ while line = f.gets()
+ line.chop!
+ next if /^From /=~line # skip From-line
+ break if /^$/=~line # end of header
+
+ if /^(\S+?):\s*(.*)/=~line
+ (attr = $1).capitalize!
+ @header[attr] = $2
+ elsif attr
+ line.sub!(/^\s*/, '')
+ @header[attr] += "\n" + line
+ end
+ end
+
+ return unless line
+
+ while line = f.gets()
+ break if /^From /=~line
+ @body.push(line)
+ end
+ ensure
+ f.close if opened
+ end
+ end
+
+ # Return the headers as a Hash.
+ def header
+ return @header
+ end
+
+ # Return the message body as an Array of lines
+ def body
+ return @body
+ end
+
+ # Return the header corresponding to +field+.
+ #
+ # Matching is case-insensitive.
+ def [](field)
+ @header[field.capitalize]
+ end
+end
diff --git a/ruby_1_8_5/lib/mathn.rb b/ruby_1_8_5/lib/mathn.rb
new file mode 100644
index 0000000000..a5a121c6c6
--- /dev/null
+++ b/ruby_1_8_5/lib/mathn.rb
@@ -0,0 +1,308 @@
+#
+# mathn.rb -
+# $Release Version: 0.5 $
+# $Revision: 1.1.1.1.4.1 $
+# $Date: 1998/01/16 12:36:05 $
+# by Keiju ISHITSUKA(SHL Japan Inc.)
+#
+# --
+#
+#
+#
+
+require "complex.rb"
+require "rational.rb"
+require "matrix.rb"
+
+class Integer
+
+ def gcd2(int)
+ a = self.abs
+ b = int.abs
+ a, b = b, a if a < b
+
+ pd_a = a.prime_division
+ pd_b = b.prime_division
+
+ gcd = 1
+ for pair in pd_a
+ as = pd_b.assoc(pair[0])
+ if as
+ gcd *= as[0] ** [as[1], pair[1]].min
+ end
+ end
+ return gcd
+ end
+
+ def Integer.from_prime_division(pd)
+ value = 1
+ for prime, index in pd
+ value *= prime**index
+ end
+ value
+ end
+
+ def prime_division
+ raise ZeroDivisionError if self == 0
+ ps = Prime.new
+ value = self
+ pv = []
+ for prime in ps
+ count = 0
+ while (value1, mod = value.divmod(prime)
+ mod) == 0
+ value = value1
+ count += 1
+ end
+ if count != 0
+ pv.push [prime, count]
+ end
+ break if prime * prime >= value
+ end
+ if value > 1
+ pv.push [value, 1]
+ end
+ return pv
+ end
+end
+
+class Prime
+ include Enumerable
+
+ def initialize
+ @seed = 1
+ @primes = []
+ @counts = []
+ end
+
+ def succ
+ i = -1
+ size = @primes.size
+ while i < size
+ if i == -1
+ @seed += 1
+ i += 1
+ else
+ while @seed > @counts[i]
+ @counts[i] += @primes[i]
+ end
+ if @seed != @counts[i]
+ i += 1
+ else
+ i = -1
+ end
+ end
+ end
+ @primes.push @seed
+ @counts.push @seed + @seed
+ return @seed
+ end
+ alias next succ
+
+ def each
+ loop do
+ yield succ
+ end
+ end
+end
+
+class Fixnum
+ alias / quo
+end
+
+class Bignum
+ alias / quo
+end
+
+class Rational
+ Unify = true
+
+ def inspect
+ format "%s/%s", numerator.inspect, denominator.inspect
+ end
+
+ alias power! **
+
+ def ** (other)
+ if other.kind_of?(Rational)
+ other2 = other
+ if self < 0
+ return Complex.new!(self, 0) ** other
+ elsif other == 0
+ return Rational(1,1)
+ elsif self == 0
+ return Rational(0,1)
+ elsif self == 1
+ return Rational(1,1)
+ end
+
+ npd = numerator.prime_division
+ dpd = denominator.prime_division
+ if other < 0
+ other = -other
+ npd, dpd = dpd, npd
+ end
+
+ for elm in npd
+ elm[1] = elm[1] * other
+ if !elm[1].kind_of?(Integer) and elm[1].denominator != 1
+ return Float(self) ** other2
+ end
+ elm[1] = elm[1].to_i
+ end
+
+ for elm in dpd
+ elm[1] = elm[1] * other
+ if !elm[1].kind_of?(Integer) and elm[1].denominator != 1
+ return Float(self) ** other2
+ end
+ elm[1] = elm[1].to_i
+ end
+
+ num = Integer.from_prime_division(npd)
+ den = Integer.from_prime_division(dpd)
+
+ Rational(num,den)
+
+ elsif other.kind_of?(Integer)
+ if other > 0
+ num = numerator ** other
+ den = denominator ** other
+ elsif other < 0
+ num = denominator ** -other
+ den = numerator ** -other
+ elsif other == 0
+ num = 1
+ den = 1
+ end
+ Rational.new!(num, den)
+ elsif other.kind_of?(Float)
+ Float(self) ** other
+ else
+ x , y = other.coerce(self)
+ x ** y
+ end
+ end
+
+ def power2(other)
+ if other.kind_of?(Rational)
+ if self < 0
+ return Complex(self, 0) ** other
+ elsif other == 0
+ return Rational(1,1)
+ elsif self == 0
+ return Rational(0,1)
+ elsif self == 1
+ return Rational(1,1)
+ end
+
+ dem = nil
+ x = self.denominator.to_f.to_i
+ neard = self.denominator.to_f ** (1.0/other.denominator.to_f)
+ loop do
+ if (neard**other.denominator == self.denominator)
+ dem = neaed
+ break
+ end
+ end
+ nearn = self.numerator.to_f ** (1.0/other.denominator.to_f)
+ Rational(num,den)
+
+ elsif other.kind_of?(Integer)
+ if other > 0
+ num = numerator ** other
+ den = denominator ** other
+ elsif other < 0
+ num = denominator ** -other
+ den = numerator ** -other
+ elsif other == 0
+ num = 1
+ den = 1
+ end
+ Rational.new!(num, den)
+ elsif other.kind_of?(Float)
+ Float(self) ** other
+ else
+ x , y = other.coerce(self)
+ x ** y
+ end
+ end
+end
+
+module Math
+ def sqrt(a)
+ if a.kind_of?(Complex)
+ abs = sqrt(a.real*a.real + a.image*a.image)
+# if not abs.kind_of?(Rational)
+# return a**Rational(1,2)
+# end
+ x = sqrt((a.real + abs)/Rational(2))
+ y = sqrt((-a.real + abs)/Rational(2))
+# if !(x.kind_of?(Rational) and y.kind_of?(Rational))
+# return a**Rational(1,2)
+# end
+ if a.image >= 0
+ Complex(x, y)
+ else
+ Complex(x, -y)
+ end
+ elsif a >= 0
+ rsqrt(a)
+ else
+ Complex(0,rsqrt(-a))
+ end
+ end
+
+ def rsqrt(a)
+ if a.kind_of?(Float)
+ sqrt!(a)
+ elsif a.kind_of?(Rational)
+ rsqrt(a.numerator)/rsqrt(a.denominator)
+ else
+ src = a
+ max = 2 ** 32
+ byte_a = [src & 0xffffffff]
+ # ruby's bug
+ while (src >= max) and (src >>= 32)
+ byte_a.unshift src & 0xffffffff
+ end
+
+ answer = 0
+ main = 0
+ side = 0
+ for elm in byte_a
+ main = (main << 32) + elm
+ side <<= 16
+ if answer != 0
+ if main * 4 < side * side
+ applo = main.div(side)
+ else
+ applo = ((sqrt!(side * side + 4 * main) - side)/2.0).to_i + 1
+ end
+ else
+ applo = sqrt!(main).to_i + 1
+ end
+
+ while (x = (side + applo) * applo) > main
+ applo -= 1
+ end
+ main -= x
+ answer = (answer << 16) + applo
+ side += applo * 2
+ end
+ if main == 0
+ answer
+ else
+ sqrt!(a)
+ end
+ end
+ end
+
+ module_function :sqrt
+ module_function :rsqrt
+end
+
+class Complex
+ Unify = true
+end
+
diff --git a/ruby_1_8_5/lib/matrix.rb b/ruby_1_8_5/lib/matrix.rb
new file mode 100644
index 0000000000..c62acdf9aa
--- /dev/null
+++ b/ruby_1_8_5/lib/matrix.rb
@@ -0,0 +1,1272 @@
+#!/usr/local/bin/ruby
+#--
+# matrix.rb -
+# $Release Version: 1.0$
+# $Revision: 1.11 $
+# $Date: 1999/10/06 11:01:53 $
+# Original Version from Smalltalk-80 version
+# on July 23, 1985 at 8:37:17 am
+# by Keiju ISHITSUKA
+#++
+#
+# = matrix.rb
+#
+# An implementation of Matrix and Vector classes.
+#
+# Author:: Keiju ISHITSUKA
+# Documentation:: Gavin Sinclair (sourced from <i>Ruby in a Nutshell</i> (Matsumoto, O'Reilly))
+#
+# See classes Matrix and Vector for documentation.
+#
+
+
+require "e2mmap.rb"
+
+module ExceptionForMatrix # :nodoc:
+ extend Exception2MessageMapper
+ def_e2message(TypeError, "wrong argument type %s (expected %s)")
+ def_e2message(ArgumentError, "Wrong # of arguments(%d for %d)")
+
+ def_exception("ErrDimensionMismatch", "\#{self.name} dimension mismatch")
+ def_exception("ErrNotRegular", "Not Regular Matrix")
+ def_exception("ErrOperationNotDefined", "This operation(%s) can\\'t defined")
+end
+
+#
+# The +Matrix+ class represents a mathematical matrix, and provides methods for creating
+# special-case matrices (zero, identity, diagonal, singular, vector), operating on them
+# arithmetically and algebraically, and determining their mathematical properties (trace, rank,
+# inverse, determinant).
+#
+# Note that although matrices should theoretically be rectangular, this is not
+# enforced by the class.
+#
+# Also note that the determinant of integer matrices may be incorrectly calculated unless you
+# also <tt>require 'mathn'</tt>. This may be fixed in the future.
+#
+# == Method Catalogue
+#
+# To create a matrix:
+# * <tt> Matrix[*rows] </tt>
+# * <tt> Matrix.[](*rows) </tt>
+# * <tt> Matrix.rows(rows, copy = true) </tt>
+# * <tt> Matrix.columns(columns) </tt>
+# * <tt> Matrix.diagonal(*values) </tt>
+# * <tt> Matrix.scalar(n, value) </tt>
+# * <tt> Matrix.scalar(n, value) </tt>
+# * <tt> Matrix.identity(n) </tt>
+# * <tt> Matrix.unit(n) </tt>
+# * <tt> Matrix.I(n) </tt>
+# * <tt> Matrix.zero(n) </tt>
+# * <tt> Matrix.row_vector(row) </tt>
+# * <tt> Matrix.column_vector(column) </tt>
+#
+# To access Matrix elements/columns/rows/submatrices/properties:
+# * <tt> [](i, j) </tt>
+# * <tt> #row_size </tt>
+# * <tt> #column_size </tt>
+# * <tt> #row(i) </tt>
+# * <tt> #column(j) </tt>
+# * <tt> #collect </tt>
+# * <tt> #map </tt>
+# * <tt> #minor(*param) </tt>
+#
+# Properties of a matrix:
+# * <tt> #regular? </tt>
+# * <tt> #singular? </tt>
+# * <tt> #square? </tt>
+#
+# Matrix arithmetic:
+# * <tt> *(m) </tt>
+# * <tt> +(m) </tt>
+# * <tt> -(m) </tt>
+# * <tt> #/(m) </tt>
+# * <tt> #inverse </tt>
+# * <tt> #inv </tt>
+# * <tt> ** </tt>
+#
+# Matrix functions:
+# * <tt> #determinant </tt>
+# * <tt> #det </tt>
+# * <tt> #rank </tt>
+# * <tt> #trace </tt>
+# * <tt> #tr </tt>
+# * <tt> #transpose </tt>
+# * <tt> #t </tt>
+#
+# Conversion to other data types:
+# * <tt> #coerce(other) </tt>
+# * <tt> #row_vectors </tt>
+# * <tt> #column_vectors </tt>
+# * <tt> #to_a </tt>
+#
+# String representations:
+# * <tt> #to_s </tt>
+# * <tt> #inspect </tt>
+#
+class Matrix
+ @RCS_ID='-$Id: matrix.rb,v 1.11 1999/10/06 11:01:53 keiju Exp keiju $-'
+
+# extend Exception2MessageMapper
+ include ExceptionForMatrix
+
+ # instance creations
+ private_class_method :new
+
+ #
+ # Creates a matrix where each argument is a row.
+ # Matrix[ [25, 93], [-1, 66] ]
+ # => 25 93
+ # -1 66
+ #
+ def Matrix.[](*rows)
+ new(:init_rows, rows, false)
+ end
+
+ #
+ # Creates a matrix where +rows+ is an array of arrays, each of which is a row
+ # to the matrix. If the optional argument +copy+ is false, use the given
+ # arrays as the internal structure of the matrix without copying.
+ # Matrix.rows([[25, 93], [-1, 66]])
+ # => 25 93
+ # -1 66
+ def Matrix.rows(rows, copy = true)
+ new(:init_rows, rows, copy)
+ end
+
+ #
+ # Creates a matrix using +columns+ as an array of column vectors.
+ # Matrix.columns([[25, 93], [-1, 66]])
+ # => 25 -1
+ # 93 66
+ #
+ #
+ def Matrix.columns(columns)
+ rows = (0 .. columns[0].size - 1).collect {
+ |i|
+ (0 .. columns.size - 1).collect {
+ |j|
+ columns[j][i]
+ }
+ }
+ Matrix.rows(rows, false)
+ end
+
+ #
+ # Creates a matrix where the diagonal elements are composed of +values+.
+ # Matrix.diagonal(9, 5, -3)
+ # => 9 0 0
+ # 0 5 0
+ # 0 0 -3
+ #
+ def Matrix.diagonal(*values)
+ size = values.size
+ rows = (0 .. size - 1).collect {
+ |j|
+ row = Array.new(size).fill(0, 0, size)
+ row[j] = values[j]
+ row
+ }
+ rows(rows, false)
+ end
+
+ #
+ # Creates an +n+ by +n+ diagonal matrix where each diagonal element is
+ # +value+.
+ # Matrix.scalar(2, 5)
+ # => 5 0
+ # 0 5
+ #
+ def Matrix.scalar(n, value)
+ Matrix.diagonal(*Array.new(n).fill(value, 0, n))
+ end
+
+ #
+ # Creates an +n+ by +n+ identity matrix.
+ # Matrix.identity(2)
+ # => 1 0
+ # 0 1
+ #
+ def Matrix.identity(n)
+ Matrix.scalar(n, 1)
+ end
+ class << Matrix
+ alias unit identity
+ alias I identity
+ end
+
+ #
+ # Creates an +n+ by +n+ zero matrix.
+ # Matrix.zero(2)
+ # => 0 0
+ # 0 0
+ #
+ def Matrix.zero(n)
+ Matrix.scalar(n, 0)
+ end
+
+ #
+ # Creates a single-row matrix where the values of that row are as given in
+ # +row+.
+ # Matrix.row_vector([4,5,6])
+ # => 4 5 6
+ #
+ def Matrix.row_vector(row)
+ case row
+ when Vector
+ Matrix.rows([row.to_a], false)
+ when Array
+ Matrix.rows([row.dup], false)
+ else
+ Matrix.rows([[row]], false)
+ end
+ end
+
+ #
+ # Creates a single-column matrix where the values of that column are as given
+ # in +column+.
+ # Matrix.column_vector([4,5,6])
+ # => 4
+ # 5
+ # 6
+ #
+ def Matrix.column_vector(column)
+ case column
+ when Vector
+ Matrix.columns([column.to_a])
+ when Array
+ Matrix.columns([column])
+ else
+ Matrix.columns([[column]])
+ end
+ end
+
+ #
+ # This method is used by the other methods that create matrices, and is of no
+ # use to general users.
+ #
+ def initialize(init_method, *argv)
+ self.send(init_method, *argv)
+ end
+
+ def init_rows(rows, copy)
+ if copy
+ @rows = rows.collect{|row| row.dup}
+ else
+ @rows = rows
+ end
+ self
+ end
+ private :init_rows
+
+ #
+ # Returns element (+i+,+j+) of the matrix. That is: row +i+, column +j+.
+ #
+ def [](i, j)
+ @rows[i][j]
+ end
+
+ #
+ # Returns the number of rows.
+ #
+ def row_size
+ @rows.size
+ end
+
+ #
+ # Returns the number of columns. Note that it is possible to construct a
+ # matrix with uneven columns (e.g. Matrix[ [1,2,3], [4,5] ]), but this is
+ # mathematically unsound. This method uses the first row to determine the
+ # result.
+ #
+ def column_size
+ @rows[0].size
+ end
+
+ #
+ # Returns row vector number +i+ of the matrix as a Vector (starting at 0 like
+ # an array). When a block is given, the elements of that vector are iterated.
+ #
+ def row(i) # :yield: e
+ if block_given?
+ for e in @rows[i]
+ yield e
+ end
+ else
+ Vector.elements(@rows[i])
+ end
+ end
+
+ #
+ # Returns column vector number +j+ of the matrix as a Vector (starting at 0
+ # like an array). When a block is given, the elements of that vector are
+ # iterated.
+ #
+ def column(j) # :yield: e
+ if block_given?
+ 0.upto(row_size - 1) do
+ |i|
+ yield @rows[i][j]
+ end
+ else
+ col = (0 .. row_size - 1).collect {
+ |i|
+ @rows[i][j]
+ }
+ Vector.elements(col, false)
+ end
+ end
+
+ #
+ # Returns a matrix that is the result of iteration of the given block over all
+ # elements of the matrix.
+ # Matrix[ [1,2], [3,4] ].collect { |i| i**2 }
+ # => 1 4
+ # 9 16
+ #
+ def collect # :yield: e
+ rows = @rows.collect{|row| row.collect{|e| yield e}}
+ Matrix.rows(rows, false)
+ end
+ alias map collect
+
+ #
+ # Returns a section of the matrix. The parameters are either:
+ # * start_row, nrows, start_col, ncols; OR
+ # * col_range, row_range
+ #
+ # Matrix.diagonal(9, 5, -3).minor(0..1, 0..2)
+ # => 9 0 0
+ # 0 5 0
+ #
+ def minor(*param)
+ case param.size
+ when 2
+ from_row = param[0].first
+ size_row = param[0].end - from_row
+ size_row += 1 unless param[0].exclude_end?
+ from_col = param[1].first
+ size_col = param[1].end - from_col
+ size_col += 1 unless param[1].exclude_end?
+ when 4
+ from_row = param[0]
+ size_row = param[1]
+ from_col = param[2]
+ size_col = param[3]
+ else
+ Matrix.Raise ArgumentError, param.inspect
+ end
+
+ rows = @rows[from_row, size_row].collect{
+ |row|
+ row[from_col, size_col]
+ }
+ Matrix.rows(rows, false)
+ end
+
+ #--
+ # TESTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ #++
+
+ #
+ # Returns +true+ if this is a regular matrix.
+ #
+ def regular?
+ square? and rank == column_size
+ end
+
+ #
+ # Returns +true+ is this is a singular (i.e. non-regular) matrix.
+ #
+ def singular?
+ not regular?
+ end
+
+ #
+ # Returns +true+ is this is a square matrix. See note in column_size about this
+ # being unreliable, though.
+ #
+ def square?
+ column_size == row_size
+ end
+
+ #--
+ # OBJECT METHODS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ #++
+
+ #
+ # Returns +true+ if and only if the two matrices contain equal elements.
+ #
+ def ==(other)
+ return false unless Matrix === other
+
+ other.compare_by_row_vectors(@rows)
+ end
+ alias eql? ==
+
+ #
+ # Not really intended for general consumption.
+ #
+ def compare_by_row_vectors(rows)
+ return false unless @rows.size == rows.size
+
+ 0.upto(@rows.size - 1) do
+ |i|
+ return false unless @rows[i] == rows[i]
+ end
+ true
+ end
+
+ #
+ # Returns a clone of the matrix, so that the contents of each do not reference
+ # identical objects.
+ #
+ def clone
+ Matrix.rows(@rows)
+ end
+
+ #
+ # Returns a hash-code for the matrix.
+ #
+ def hash
+ value = 0
+ for row in @rows
+ for e in row
+ value ^= e.hash
+ end
+ end
+ return value
+ end
+
+ #--
+ # ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ #++
+
+ #
+ # Matrix multiplication.
+ # Matrix[[2,4], [6,8]] * Matrix.identity(2)
+ # => 2 4
+ # 6 8
+ #
+ def *(m) # m is matrix or vector or number
+ case(m)
+ when Numeric
+ rows = @rows.collect {
+ |row|
+ row.collect {
+ |e|
+ e * m
+ }
+ }
+ return Matrix.rows(rows, false)
+ when Vector
+ m = Matrix.column_vector(m)
+ r = self * m
+ return r.column(0)
+ when Matrix
+ Matrix.Raise ErrDimensionMismatch if column_size != m.row_size
+
+ rows = (0 .. row_size - 1).collect {
+ |i|
+ (0 .. m.column_size - 1).collect {
+ |j|
+ vij = 0
+ 0.upto(column_size - 1) do
+ |k|
+ vij += self[i, k] * m[k, j]
+ end
+ vij
+ }
+ }
+ return Matrix.rows(rows, false)
+ else
+ x, y = m.coerce(self)
+ return x * y
+ end
+ end
+
+ #
+ # Matrix addition.
+ # Matrix.scalar(2,5) + Matrix[[1,0], [-4,7]]
+ # => 6 0
+ # -4 12
+ #
+ def +(m)
+ case m
+ when Numeric
+ Matrix.Raise ErrOperationNotDefined, "+"
+ when Vector
+ m = Matrix.column_vector(m)
+ when Matrix
+ else
+ x, y = m.coerce(self)
+ return x + y
+ end
+
+ Matrix.Raise ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size
+
+ rows = (0 .. row_size - 1).collect {
+ |i|
+ (0 .. column_size - 1).collect {
+ |j|
+ self[i, j] + m[i, j]
+ }
+ }
+ Matrix.rows(rows, false)
+ end
+
+ #
+ # Matrix subtraction.
+ # Matrix[[1,5], [4,2]] - Matrix[[9,3], [-4,1]]
+ # => -8 2
+ # 8 1
+ #
+ def -(m)
+ case m
+ when Numeric
+ Matrix.Raise ErrOperationNotDefined, "-"
+ when Vector
+ m = Matrix.column_vector(m)
+ when Matrix
+ else
+ x, y = m.coerce(self)
+ return x - y
+ end
+
+ Matrix.Raise ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size
+
+ rows = (0 .. row_size - 1).collect {
+ |i|
+ (0 .. column_size - 1).collect {
+ |j|
+ self[i, j] - m[i, j]
+ }
+ }
+ Matrix.rows(rows, false)
+ end
+
+ #
+ # Matrix division (multiplication by the inverse).
+ # Matrix[[7,6], [3,9]] / Matrix[[2,9], [3,1]]
+ # => -7 1
+ # -3 -6
+ #
+ def /(other)
+ case other
+ when Numeric
+ rows = @rows.collect {
+ |row|
+ row.collect {
+ |e|
+ e / other
+ }
+ }
+ return Matrix.rows(rows, false)
+ when Matrix
+ return self * other.inverse
+ else
+ x, y = other.coerce(self)
+ rerurn x / y
+ end
+ end
+
+ #
+ # Returns the inverse of the matrix.
+ # Matrix[[1, 2], [2, 1]].inverse
+ # => -1 1
+ # 0 -1
+ #
+ def inverse
+ Matrix.Raise ErrDimensionMismatch unless square?
+ Matrix.I(row_size).inverse_from(self)
+ end
+ alias inv inverse
+
+ #
+ # Not for public consumption?
+ #
+ def inverse_from(src)
+ size = row_size - 1
+ a = src.to_a
+
+ for k in 0..size
+ if (akk = a[k][k]) == 0
+ i = k
+ begin
+ Matrix.Raise ErrNotRegular if (i += 1) > size
+ end while a[i][k] == 0
+ a[i], a[k] = a[k], a[i]
+ @rows[i], @rows[k] = @rows[k], @rows[i]
+ akk = a[k][k]
+ end
+
+ for i in 0 .. size
+ next if i == k
+ q = a[i][k] / akk
+ a[i][k] = 0
+
+ (k + 1).upto(size) do
+ |j|
+ a[i][j] -= a[k][j] * q
+ end
+ 0.upto(size) do
+ |j|
+ @rows[i][j] -= @rows[k][j] * q
+ end
+ end
+
+ (k + 1).upto(size) do
+ |j|
+ a[k][j] /= akk
+ end
+ 0.upto(size) do
+ |j|
+ @rows[k][j] /= akk
+ end
+ end
+ self
+ end
+ #alias reciprocal inverse
+
+ #
+ # Matrix exponentiation. Defined for integer powers only. Equivalent to
+ # multiplying the matrix by itself N times.
+ # Matrix[[7,6], [3,9]] ** 2
+ # => 67 96
+ # 48 99
+ #
+ def ** (other)
+ if other.kind_of?(Integer)
+ x = self
+ if other <= 0
+ x = self.inverse
+ return Matrix.identity(self.column_size) if other == 0
+ other = -other
+ end
+ z = x
+ n = other - 1
+ while n != 0
+ while (div, mod = n.divmod(2)
+ mod == 0)
+ x = x * x
+ n = div
+ end
+ z *= x
+ n -= 1
+ end
+ z
+ elsif other.kind_of?(Float) || defined?(Rational) && other.kind_of?(Rational)
+ Matrix.Raise ErrOperationNotDefined, "**"
+ else
+ Matrix.Raise ErrOperationNotDefined, "**"
+ end
+ end
+
+ #--
+ # MATRIX FUNCTIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ #++
+
+ #
+ # Returns the determinant of the matrix. If the matrix is not square, the
+ # result is 0.
+ # Matrix[[7,6], [3,9]].determinant
+ # => 63
+ #
+ def determinant
+ return 0 unless square?
+
+ size = row_size - 1
+ a = to_a
+
+ det = 1
+ k = 0
+ begin
+ if (akk = a[k][k]) == 0
+ i = k
+ begin
+ return 0 if (i += 1) > size
+ end while a[i][k] == 0
+ a[i], a[k] = a[k], a[i]
+ akk = a[k][k]
+ det *= -1
+ end
+ (k + 1).upto(size) do
+ |i|
+ q = a[i][k] / akk
+ (k + 1).upto(size) do
+ |j|
+ a[i][j] -= a[k][j] * q
+ end
+ end
+ det *= akk
+ end while (k += 1) <= size
+ det
+ end
+ alias det determinant
+
+ #
+ # Returns the rank of the matrix. Beware that using Float values, with their
+ # usual lack of precision, can affect the value returned by this method. Use
+ # Rational values instead if this is important to you.
+ # Matrix[[7,6], [3,9]].rank
+ # => 2
+ #
+ def rank
+ if column_size > row_size
+ a = transpose.to_a
+ a_column_size = row_size
+ a_row_size = column_size
+ else
+ a = to_a
+ a_column_size = column_size
+ a_row_size = row_size
+ end
+ rank = 0
+ k = 0
+ begin
+ if (akk = a[k][k]) == 0
+ i = k
+ exists = true
+ begin
+ if (i += 1) > a_column_size - 1
+ exists = false
+ break
+ end
+ end while a[i][k] == 0
+ if exists
+ a[i], a[k] = a[k], a[i]
+ akk = a[k][k]
+ else
+ i = k
+ exists = true
+ begin
+ if (i += 1) > a_row_size - 1
+ exists = false
+ break
+ end
+ end while a[k][i] == 0
+ if exists
+ k.upto(a_column_size - 1) do
+ |j|
+ a[j][k], a[j][i] = a[j][i], a[j][k]
+ end
+ akk = a[k][k]
+ else
+ next
+ end
+ end
+ end
+ (k + 1).upto(a_row_size - 1) do
+ |i|
+ q = a[i][k] / akk
+ (k + 1).upto(a_column_size - 1) do
+ |j|
+ a[i][j] -= a[k][j] * q
+ end
+ end
+ rank += 1
+ end while (k += 1) <= a_column_size - 1
+ return rank
+ end
+
+ #
+ # Returns the trace (sum of diagonal elements) of the matrix.
+ # Matrix[[7,6], [3,9]].trace
+ # => 16
+ #
+ def trace
+ tr = 0
+ 0.upto(column_size - 1) do
+ |i|
+ tr += @rows[i][i]
+ end
+ tr
+ end
+ alias tr trace
+
+ #
+ # Returns the transpose of the matrix.
+ # Matrix[[1,2], [3,4], [5,6]]
+ # => 1 2
+ # 3 4
+ # 5 6
+ # Matrix[[1,2], [3,4], [5,6]].transpose
+ # => 1 3 5
+ # 2 4 6
+ #
+ def transpose
+ Matrix.columns(@rows)
+ end
+ alias t transpose
+
+ #--
+ # CONVERTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ #++
+
+ #
+ # FIXME: describe #coerce.
+ #
+ def coerce(other)
+ case other
+ when Numeric
+ return Scalar.new(other), self
+ else
+ raise TypeError, "#{self.class} can't be coerced into #{other.class}"
+ end
+ end
+
+ #
+ # Returns an array of the row vectors of the matrix. See Vector.
+ #
+ def row_vectors
+ rows = (0 .. row_size - 1).collect {
+ |i|
+ row(i)
+ }
+ rows
+ end
+
+ #
+ # Returns an array of the column vectors of the matrix. See Vector.
+ #
+ def column_vectors
+ columns = (0 .. column_size - 1).collect {
+ |i|
+ column(i)
+ }
+ columns
+ end
+
+ #
+ # Returns an array of arrays that describe the rows of the matrix.
+ #
+ def to_a
+ @rows.collect{|row| row.collect{|e| e}}
+ end
+
+ #--
+ # PRINTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ #++
+
+ #
+ # Overrides Object#to_s
+ #
+ def to_s
+ "Matrix[" + @rows.collect{
+ |row|
+ "[" + row.collect{|e| e.to_s}.join(", ") + "]"
+ }.join(", ")+"]"
+ end
+
+ #
+ # Overrides Object#inspect
+ #
+ def inspect
+ "Matrix"+@rows.inspect
+ end
+
+ # Private CLASS
+
+ class Scalar < Numeric # :nodoc:
+ include ExceptionForMatrix
+
+ def initialize(value)
+ @value = value
+ end
+
+ # ARITHMETIC
+ def +(other)
+ case other
+ when Numeric
+ Scalar.new(@value + other)
+ when Vector, Matrix
+ Scalar.Raise WrongArgType, other.class, "Numeric or Scalar"
+ when Scalar
+ Scalar.new(@value + other.value)
+ else
+ x, y = other.coerce(self)
+ x + y
+ end
+ end
+
+ def -(other)
+ case other
+ when Numeric
+ Scalar.new(@value - other)
+ when Vector, Matrix
+ Scalar.Raise WrongArgType, other.class, "Numeric or Scalar"
+ when Scalar
+ Scalar.new(@value - other.value)
+ else
+ x, y = other.coerce(self)
+ x - y
+ end
+ end
+
+ def *(other)
+ case other
+ when Numeric
+ Scalar.new(@value * other)
+ when Vector, Matrix
+ other.collect{|e| @value * e}
+ else
+ x, y = other.coerce(self)
+ x * y
+ end
+ end
+
+ def / (other)
+ case other
+ when Numeric
+ Scalar.new(@value / other)
+ when Vector
+ Scalar.Raise WrongArgType, other.class, "Numeric or Scalar or Matrix"
+ when Matrix
+ self * _M.inverse
+ else
+ x, y = other.coerce(self)
+ x / y
+ end
+ end
+
+ def ** (other)
+ case other
+ when Numeric
+ Scalar.new(@value ** other)
+ when Vector
+ Scalar.Raise WrongArgType, other.class, "Numeric or Scalar or Matrix"
+ when Matrix
+ other.powered_by(self)
+ else
+ x, y = other.coerce(self)
+ x ** y
+ end
+ end
+ end
+end
+
+
+#
+# The +Vector+ class represents a mathematical vector, which is useful in its own right, and
+# also constitutes a row or column of a Matrix.
+#
+# == Method Catalogue
+#
+# To create a Vector:
+# * <tt> Vector.[](*array) </tt>
+# * <tt> Vector.elements(array, copy = true) </tt>
+#
+# To access elements:
+# * <tt> [](i) </tt>
+#
+# To enumerate the elements:
+# * <tt> #each2(v) </tt>
+# * <tt> #collect2(v) </tt>
+#
+# Vector arithmetic:
+# * <tt> *(x) "is matrix or number" </tt>
+# * <tt> +(v) </tt>
+# * <tt> -(v) </tt>
+#
+# Vector functions:
+# * <tt> #inner_product(v) </tt>
+# * <tt> #collect </tt>
+# * <tt> #map </tt>
+# * <tt> #map2(v) </tt>
+# * <tt> #r </tt>
+# * <tt> #size </tt>
+#
+# Conversion to other data types:
+# * <tt> #covector </tt>
+# * <tt> #to_a </tt>
+# * <tt> #coerce(other) </tt>
+#
+# String representations:
+# * <tt> #to_s </tt>
+# * <tt> #inspect </tt>
+#
+class Vector
+ include ExceptionForMatrix
+
+ #INSTANCE CREATION
+
+ private_class_method :new
+
+ #
+ # Creates a Vector from a list of elements.
+ # Vector[7, 4, ...]
+ #
+ def Vector.[](*array)
+ new(:init_elements, array, copy = false)
+ end
+
+ #
+ # Creates a vector from an Array. The optional second argument specifies
+ # whether the array itself or a copy is used internally.
+ #
+ def Vector.elements(array, copy = true)
+ new(:init_elements, array, copy)
+ end
+
+ #
+ # For internal use.
+ #
+ def initialize(method, array, copy)
+ self.send(method, array, copy)
+ end
+
+ #
+ # For internal use.
+ #
+ def init_elements(array, copy)
+ if copy
+ @elements = array.dup
+ else
+ @elements = array
+ end
+ end
+
+ # ACCESSING
+
+ #
+ # Returns element number +i+ (starting at zero) of the vector.
+ #
+ def [](i)
+ @elements[i]
+ end
+
+ #
+ # Returns the number of elements in the vector.
+ #
+ def size
+ @elements.size
+ end
+
+ #--
+ # ENUMERATIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ #++
+
+ #
+ # Iterate over the elements of this vector and +v+ in conjunction.
+ #
+ def each2(v) # :yield: e1, e2
+ Vector.Raise ErrDimensionMismatch if size != v.size
+ 0.upto(size - 1) do
+ |i|
+ yield @elements[i], v[i]
+ end
+ end
+
+ #
+ # Collects (as in Enumerable#collect) over the elements of this vector and +v+
+ # in conjunction.
+ #
+ def collect2(v) # :yield: e1, e2
+ Vector.Raise ErrDimensionMismatch if size != v.size
+ (0 .. size - 1).collect do
+ |i|
+ yield @elements[i], v[i]
+ end
+ end
+
+ #--
+ # COMPARING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ #++
+
+ #
+ # Returns +true+ iff the two vectors have the same elements in the same order.
+ #
+ def ==(other)
+ return false unless Vector === other
+
+ other.compare_by(@elements)
+ end
+ alias eqn? ==
+
+ #
+ # For internal use.
+ #
+ def compare_by(elements)
+ @elements == elements
+ end
+
+ #
+ # Return a copy of the vector.
+ #
+ def clone
+ Vector.elements(@elements)
+ end
+
+ #
+ # Return a hash-code for the vector.
+ #
+ def hash
+ @elements.hash
+ end
+
+ #--
+ # ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ #++
+
+ #
+ # Multiplies the vector by +x+, where +x+ is a number or another vector.
+ #
+ def *(x)
+ case x
+ when Numeric
+ els = @elements.collect{|e| e * x}
+ Vector.elements(els, false)
+ when Matrix
+ Matrix.column_vector(self) * x
+ else
+ s, x = x.coerce(self)
+ s * x
+ end
+ end
+
+ #
+ # Vector addition.
+ #
+ def +(v)
+ case v
+ when Vector
+ Vector.Raise ErrDimensionMismatch if size != v.size
+ els = collect2(v) {
+ |v1, v2|
+ v1 + v2
+ }
+ Vector.elements(els, false)
+ when Matrix
+ Matrix.column_vector(self) + v
+ else
+ s, x = v.coerce(self)
+ s + x
+ end
+ end
+
+ #
+ # Vector subtraction.
+ #
+ def -(v)
+ case v
+ when Vector
+ Vector.Raise ErrDimensionMismatch if size != v.size
+ els = collect2(v) {
+ |v1, v2|
+ v1 - v2
+ }
+ Vector.elements(els, false)
+ when Matrix
+ Matrix.column_vector(self) - v
+ else
+ s, x = v.coerce(self)
+ s - x
+ end
+ end
+
+ #--
+ # VECTOR FUNCTIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ #++
+
+ #
+ # Returns the inner product of this vector with the other.
+ # Vector[4,7].inner_product Vector[10,1] => 47
+ #
+ def inner_product(v)
+ Vector.Raise ErrDimensionMismatch if size != v.size
+
+ p = 0
+ each2(v) {
+ |v1, v2|
+ p += v1 * v2
+ }
+ p
+ end
+
+ #
+ # Like Array#collect.
+ #
+ def collect # :yield: e
+ els = @elements.collect {
+ |v|
+ yield v
+ }
+ Vector.elements(els, false)
+ end
+ alias map collect
+
+ #
+ # Like Vector#collect2, but returns a Vector instead of an Array.
+ #
+ def map2(v) # :yield: e1, e2
+ els = collect2(v) {
+ |v1, v2|
+ yield v1, v2
+ }
+ Vector.elements(els, false)
+ end
+
+ #
+ # Returns the modulus (Pythagorean distance) of the vector.
+ # Vector[5,8,2].r => 9.643650761
+ #
+ def r
+ v = 0
+ for e in @elements
+ v += e*e
+ end
+ return Math.sqrt(v)
+ end
+
+ #--
+ # CONVERTING
+ #++
+
+ #
+ # Creates a single-row matrix from this vector.
+ #
+ def covector
+ Matrix.row_vector(self)
+ end
+
+ #
+ # Returns the elements of the vector in an array.
+ #
+ def to_a
+ @elements.dup
+ end
+
+ #
+ # FIXME: describe Vector#coerce.
+ #
+ def coerce(other)
+ case other
+ when Numeric
+ return Scalar.new(other), self
+ else
+ raise TypeError, "#{self.class} can't be coerced into #{other.class}"
+ end
+ end
+
+ #--
+ # PRINTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ #++
+
+ #
+ # Overrides Object#to_s
+ #
+ def to_s
+ "Vector[" + @elements.join(", ") + "]"
+ end
+
+ #
+ # Overrides Object#inspect
+ #
+ def inspect
+ str = "Vector"+@elements.inspect
+ end
+end
+
+
+# Documentation comments:
+# - Matrix#coerce and Vector#coerce need to be documented
diff --git a/ruby_1_8_5/lib/mkmf.rb b/ruby_1_8_5/lib/mkmf.rb
new file mode 100644
index 0000000000..b1b90829f0
--- /dev/null
+++ b/ruby_1_8_5/lib/mkmf.rb
@@ -0,0 +1,1529 @@
+# module to create Makefile for extension modules
+# invoke like: ruby -r mkmf extconf.rb
+
+require 'rbconfig'
+require 'fileutils'
+require 'shellwords'
+
+CONFIG = Config::MAKEFILE_CONFIG
+ORIG_LIBPATH = ENV['LIB']
+
+CXX_EXT = %w[cc cxx cpp]
+if /mswin|bccwin|mingw|msdosdjgpp|human|os2/ !~ CONFIG['build_os']
+ CXX_EXT.concat(%w[C])
+end
+SRC_EXT = %w[c m] << CXX_EXT
+$static = $config_h = nil
+$default_static = $static
+
+unless defined? $configure_args
+ $configure_args = {}
+ args = CONFIG["configure_args"]
+ if ENV["CONFIGURE_ARGS"]
+ args << " " << ENV["CONFIGURE_ARGS"]
+ end
+ for arg in Shellwords::shellwords(args)
+ arg, val = arg.split('=', 2)
+ next unless arg
+ arg.tr!('_', '-')
+ if arg.sub!(/^(?!--)/, '--')
+ val or next
+ arg.downcase!
+ end
+ next if /^--(?:top|topsrc|src|cur)dir$/ =~ arg
+ $configure_args[arg] = val || true
+ end
+ for arg in ARGV
+ arg, val = arg.split('=', 2)
+ next unless arg
+ arg.tr!('_', '-')
+ if arg.sub!(/^(?!--)/, '--')
+ val or next
+ arg.downcase!
+ end
+ $configure_args[arg] = val || true
+ end
+end
+
+$libdir = CONFIG["libdir"]
+$rubylibdir = CONFIG["rubylibdir"]
+$archdir = CONFIG["archdir"]
+$sitedir = CONFIG["sitedir"]
+$sitelibdir = CONFIG["sitelibdir"]
+$sitearchdir = CONFIG["sitearchdir"]
+
+$mswin = /mswin/ =~ RUBY_PLATFORM
+$bccwin = /bccwin/ =~ RUBY_PLATFORM
+$mingw = /mingw/ =~ RUBY_PLATFORM
+$cygwin = /cygwin/ =~ RUBY_PLATFORM
+$human = /human/ =~ RUBY_PLATFORM
+$netbsd = /netbsd/ =~ RUBY_PLATFORM
+$os2 = /os2/ =~ RUBY_PLATFORM
+$beos = /beos/ =~ RUBY_PLATFORM
+$solaris = /solaris/ =~ RUBY_PLATFORM
+$dest_prefix_pattern = (File::PATH_SEPARATOR == ';' ? /\A([[:alpha:]]:)?/ : /\A/)
+
+def config_string(key, config = CONFIG)
+ s = config[key] and !s.empty? and block_given? ? yield(s) : s
+end
+
+def dir_re(dir)
+ Regexp.new('\$(?:\('+dir+'\)|\{'+dir+'\})(?:\$\(target_prefix\)|\{target_prefix\})?')
+end
+
+INSTALL_DIRS = [
+ [dir_re('commondir'), "$(RUBYCOMMONDIR)"],
+ [dir_re("sitedir"), "$(RUBYCOMMONDIR)"],
+ [dir_re('rubylibdir'), "$(RUBYLIBDIR)"],
+ [dir_re('archdir'), "$(RUBYARCHDIR)"],
+ [dir_re('sitelibdir'), "$(RUBYLIBDIR)"],
+ [dir_re('sitearchdir'), "$(RUBYARCHDIR)"]
+]
+
+def install_dirs(target_prefix = nil)
+ if $extout
+ dirs = [
+ ['RUBYCOMMONDIR', '$(extout)'],
+ ['RUBYLIBDIR', '$(extout)$(target_prefix)'],
+ ['RUBYARCHDIR', '$(extout)/$(arch)$(target_prefix)'],
+ ['extout', "#$extout"],
+ ['extout_prefix', "#$extout_prefix"],
+ ]
+ elsif $extmk
+ dirs = [
+ ['RUBYCOMMONDIR', '$(rubylibdir)'],
+ ['RUBYLIBDIR', '$(rubylibdir)$(target_prefix)'],
+ ['RUBYARCHDIR', '$(archdir)$(target_prefix)'],
+ ]
+ else
+ dirs = [
+ ['RUBYCOMMONDIR', '$(sitedir)$(target_prefix)'],
+ ['RUBYLIBDIR', '$(sitelibdir)$(target_prefix)'],
+ ['RUBYARCHDIR', '$(sitearchdir)$(target_prefix)'],
+ ]
+ end
+ dirs << ['target_prefix', (target_prefix ? "/#{target_prefix}" : "")]
+ dirs
+end
+
+def map_dir(dir, map = nil)
+ map ||= INSTALL_DIRS
+ map.inject(dir) {|dir, (orig, new)| dir.gsub(orig, new)}
+end
+
+topdir = File.dirname(libdir = File.dirname(__FILE__))
+extdir = File.expand_path("ext", topdir)
+$extmk = File.expand_path($0)[0, extdir.size+1] == extdir+"/"
+if not $extmk and File.exist?(Config::CONFIG["archdir"] + "/ruby.h")
+ $hdrdir = $topdir = Config::CONFIG["archdir"]
+elsif File.exist?(($top_srcdir ||= topdir) + "/ruby.h") and
+ File.exist?(($topdir ||= Config::CONFIG["topdir"]) + "/config.h")
+ $hdrdir = $top_srcdir
+else
+ abort "can't find header files for ruby."
+end
+
+OUTFLAG = CONFIG['OUTFLAG']
+CPPOUTFILE = CONFIG['CPPOUTFILE']
+
+CONFTEST_C = "conftest.c"
+
+class String
+ def quote
+ /\s/ =~ self ? "\"#{self}\"" : self
+ end
+end
+class Array
+ def quote
+ map {|s| s.quote}
+ end
+end
+
+def rm_f(*files)
+ FileUtils.rm_f(Dir[files.join("\0")])
+end
+
+def modified?(target, times)
+ (t = File.mtime(target)) rescue return nil
+ Array === times or times = [times]
+ t if times.all? {|n| n <= t}
+end
+
+def merge_libs(*libs)
+ libs.inject([]) do |x, y|
+ xy = x & y
+ xn = yn = 0
+ y = y.inject([]) {|ary, e| ary.last == e ? ary : ary << e}
+ y.each_with_index do |v, yi|
+ if xy.include?(v)
+ xi = [x.index(v), xn].max()
+ x[xi, 1] = y[yn..yi]
+ xn, yn = xi + (yi - yn + 1), yi + 1
+ end
+ end
+ x.concat(y[yn..-1] || [])
+ end
+end
+
+module Logging
+ @log = nil
+ @logfile = 'mkmf.log'
+ @orgerr = $stderr.dup
+ @orgout = $stdout.dup
+ @postpone = 0
+
+ def self::open
+ @log ||= File::open(@logfile, 'w')
+ @log.sync = true
+ $stderr.reopen(@log)
+ $stdout.reopen(@log)
+ yield
+ ensure
+ $stderr.reopen(@orgerr)
+ $stdout.reopen(@orgout)
+ end
+
+ def self::message(*s)
+ @log ||= File::open(@logfile, 'w')
+ @log.sync = true
+ @log.printf(*s)
+ end
+
+ def self::logfile file
+ @logfile = file
+ if @log and not @log.closed?
+ @log.flush
+ @log.close
+ @log = nil
+ end
+ end
+
+ def self::postpone
+ tmplog = "mkmftmp#{@postpone += 1}.log"
+ open do
+ log, *save = @log, @logfile, @orgout, @orgerr
+ @log, @logfile, @orgout, @orgerr = nil, tmplog, log, log
+ begin
+ log.print(open {yield})
+ @log.close
+ File::open(tmplog) {|t| FileUtils.copy_stream(t, log)}
+ ensure
+ @log, @logfile, @orgout, @orgerr = log, *save
+ @postpone -= 1
+ rm_f tmplog
+ end
+ end
+ end
+end
+
+def xsystem command
+ Logging::open do
+ puts command.quote
+ system(command)
+ end
+end
+
+def xpopen command, *mode, &block
+ Logging::open do
+ case mode[0]
+ when nil, /^r/
+ puts "#{command} |"
+ else
+ puts "| #{command}"
+ end
+ IO.popen(command, *mode, &block)
+ end
+end
+
+def log_src(src)
+ src = src.split(/^/)
+ fmt = "%#{src.size.to_s.size}d: %s"
+ Logging::message <<"EOM"
+checked program was:
+/* begin */
+EOM
+ src.each_with_index {|line, no| Logging::message fmt, no+1, line}
+ Logging::message <<"EOM"
+/* end */
+
+EOM
+end
+
+def create_tmpsrc(src)
+ src = yield(src) if block_given?
+ src = src.gsub(/[ \t]+$/, '').gsub(/\A\n+|^\n+$/, '').sub(/[^\n]\z/, "\\&\n")
+ open(CONFTEST_C, "wb") do |cfile|
+ cfile.print src
+ end
+ src
+end
+
+def try_do(src, command, &b)
+ src = create_tmpsrc(src, &b)
+ xsystem(command)
+ensure
+ log_src(src)
+end
+
+def link_command(ldflags, opt="", libpath=$LIBPATH)
+ conf = Config::CONFIG.merge('hdrdir' => $hdrdir.quote,
+ 'src' => CONFTEST_C,
+ 'INCFLAGS' => $INCFLAGS,
+ 'CPPFLAGS' => $CPPFLAGS,
+ 'CFLAGS' => "#$CFLAGS",
+ 'ARCH_FLAG' => "#$ARCH_FLAG",
+ 'LDFLAGS' => "#$LDFLAGS #{ldflags}",
+ 'LIBPATH' => libpathflag(libpath),
+ 'LOCAL_LIBS' => "#$LOCAL_LIBS #$libs",
+ 'LIBS' => "#$LIBRUBYARG_STATIC #{opt} #$LIBS")
+ Config::expand(TRY_LINK.dup, conf)
+end
+
+def cc_command(opt="")
+ conf = Config::CONFIG.merge('hdrdir' => $hdrdir.quote, 'srcdir' => $srcdir.quote)
+ Config::expand("$(CC) #$INCFLAGS #$CPPFLAGS #$CFLAGS #$ARCH_FLAG #{opt} -c #{CONFTEST_C}",
+ conf)
+end
+
+def cpp_command(outfile, opt="")
+ conf = Config::CONFIG.merge('hdrdir' => $hdrdir.quote, 'srcdir' => $srcdir.quote)
+ Config::expand("$(CPP) #$INCFLAGS #$CPPFLAGS #$CFLAGS #{opt} #{CONFTEST_C} #{outfile}",
+ conf)
+end
+
+def libpathflag(libpath=$LIBPATH)
+ libpath.map{|x|
+ (x == "$(topdir)" ? LIBPATHFLAG : LIBPATHFLAG+RPATHFLAG) % x.quote
+ }.join
+end
+
+def try_link0(src, opt="", &b)
+ try_do(src, link_command("", opt), &b)
+end
+
+def try_link(src, opt="", &b)
+ try_link0(src, opt, &b)
+ensure
+ rm_f "conftest*", "c0x32*"
+end
+
+def try_compile(src, opt="", &b)
+ try_do(src, cc_command(opt), &b)
+ensure
+ rm_f "conftest*"
+end
+
+def try_cpp(src, opt="", &b)
+ try_do(src, cpp_command(CPPOUTFILE, opt), &b)
+ensure
+ rm_f "conftest*"
+end
+
+def cpp_include(header)
+ if header
+ header = [header] unless header.kind_of? Array
+ header.map {|h| "#include <#{h}>\n"}.join
+ else
+ ""
+ end
+end
+
+def with_cppflags(flags)
+ cppflags = $CPPFLAGS
+ $CPPFLAGS = flags
+ ret = yield
+ensure
+ $CPPFLAGS = cppflags unless ret
+end
+
+def with_cflags(flags)
+ cflags = $CFLAGS
+ $CFLAGS = flags
+ ret = yield
+ensure
+ $CFLAGS = cflags unless ret
+end
+
+def with_ldflags(flags)
+ ldflags = $LDFLAGS
+ $LDFLAGS = flags
+ ret = yield
+ensure
+ $LDFLAGS = ldflags unless ret
+end
+
+def try_static_assert(expr, headers = nil, opt = "", &b)
+ headers = cpp_include(headers)
+ try_compile(<<SRC, opt, &b)
+#{COMMON_HEADERS}
+#{headers}
+/*top*/
+int conftest_const[(#{expr}) ? 1 : -1];
+SRC
+end
+
+def try_constant(const, headers = nil, opt = "", &b)
+ includes = cpp_include(headers)
+ if CROSS_COMPILING
+ if try_static_assert("#{const} > 0", headers, opt)
+ # positive constant
+ elsif try_static_assert("#{const} < 0", headers, opt)
+ neg = true
+ const = "-(#{const})"
+ elsif try_static_assert("#{const} == 0", headers, opt)
+ return 0
+ else
+ # not a constant
+ return nil
+ end
+ upper = 1
+ lower = 0
+ until try_static_assert("#{const} <= #{upper}", headers, opt)
+ lower = upper
+ upper <<= 1
+ end
+ return nil unless lower
+ while upper > lower + 1
+ mid = (upper + lower) / 2
+ if try_static_assert("#{const} > #{mid}", headers, opt)
+ lower = mid
+ else
+ upper = mid
+ end
+ end
+ upper = -upper if neg
+ return upper
+ else
+ src = %{#{COMMON_HEADERS}
+#{includes}
+#include <stdio.h>
+/*top*/
+int conftest_const = (int)(#{const});
+int main() {printf("%d\\n", conftest_const); return 0;}
+}
+ if try_link0(src, opt, &b)
+ xpopen("./conftest") do |f|
+ return Integer(f.gets)
+ end
+ end
+ end
+ nil
+end
+
+def try_func(func, libs, headers = nil, &b)
+ headers = cpp_include(headers)
+ try_link(<<"SRC", libs, &b) or try_link(<<"SRC", libs, &b)
+#{headers}
+/*top*/
+int main() { return 0; }
+int t() { #{func}(); return 0; }
+SRC
+#{COMMON_HEADERS}
+#{headers}
+/*top*/
+int main() { return 0; }
+int t() { void ((*volatile p)()); p = (void ((*)()))#{func}; return 0; }
+SRC
+end
+
+def try_var(var, headers = nil, &b)
+ headers = cpp_include(headers)
+ try_compile(<<"SRC", &b)
+#{COMMON_HEADERS}
+#{headers}
+/*top*/
+int main() { return 0; }
+int t() { const volatile void *volatile p; p = (void *)&#{var}; return 0; }
+SRC
+end
+
+def egrep_cpp(pat, src, opt = "", &b)
+ src = create_tmpsrc(src, &b)
+ xpopen(cpp_command('', opt)) do |f|
+ if Regexp === pat
+ puts(" ruby -ne 'print if #{pat.inspect}'")
+ f.grep(pat) {|l|
+ puts "#{f.lineno}: #{l}"
+ return true
+ }
+ false
+ else
+ puts(" egrep '#{pat}'")
+ begin
+ stdin = $stdin.dup
+ $stdin.reopen(f)
+ system("egrep", pat)
+ ensure
+ $stdin.reopen(stdin)
+ end
+ end
+ end
+ensure
+ rm_f "conftest*"
+ log_src(src)
+end
+
+def macro_defined?(macro, src, opt = "", &b)
+ src = src.sub(/[^\n]\z/, "\\&\n")
+ try_compile(src + <<"SRC", opt, &b)
+/*top*/
+#ifndef #{macro}
+# error
+>>>>>> #{macro} undefined <<<<<<
+#endif
+SRC
+end
+
+def try_run(src, opt = "", &b)
+ if try_link0(src, opt, &b)
+ xsystem("./conftest")
+ else
+ nil
+ end
+ensure
+ rm_f "conftest*"
+end
+
+def install_files(mfile, ifiles, map = nil, srcprefix = nil)
+ ifiles or return
+ srcprefix ||= '$(srcdir)'
+ Config::expand(srcdir = srcprefix.dup)
+ dirs = []
+ path = Hash.new {|h, i| h[i] = dirs.push([i])[-1]}
+ ifiles.each do |files, dir, prefix|
+ dir = map_dir(dir, map)
+ prefix = %r|\A#{Regexp.quote(prefix)}/?| if prefix
+ if /\A\.\// =~ files
+ # install files which are in current working directory.
+ files = files[2..-1]
+ len = nil
+ else
+ # install files which are under the $(srcdir).
+ files = File.join(srcdir, files)
+ len = srcdir.size
+ end
+ f = nil
+ Dir.glob(files) do |f|
+ f[0..len] = "" if len
+ d = File.dirname(f)
+ d.sub!(prefix, "") if prefix
+ d = (d.empty? || d == ".") ? dir : File.join(dir, d)
+ f = File.join(srcprefix, f) if len
+ path[d] << f
+ end
+ unless len or f
+ d = File.dirname(files)
+ d.sub!(prefix, "") if prefix
+ d = (d.empty? || d == ".") ? dir : File.join(dir, d)
+ path[d] << files
+ end
+ end
+ dirs
+end
+
+def install_rb(mfile, dest, srcdir = nil)
+ install_files(mfile, [["lib/**/*.rb", dest, "lib"]], nil, srcdir)
+end
+
+def append_library(libs, lib)
+ format(LIBARG, lib) + " " + libs
+end
+
+def message(*s)
+ unless $extmk and not $VERBOSE
+ printf(*s)
+ $stdout.flush
+ end
+end
+
+def checking_for(m, fmt = nil)
+ f = caller[0][/in `(.*)'$/, 1] and f << ": " #` for vim
+ m = "checking #{'for ' if /\Acheck/ !~ f}#{m}... "
+ message "%s", m
+ a = r = nil
+ Logging::postpone do
+ r = yield
+ a = (fmt ? fmt % r : r ? "yes" : "no") << "\n"
+ "#{f}#{m}-------------------- #{a}\n"
+ end
+ message(a)
+ Logging::message "--------------------\n\n"
+ r
+end
+
+# Returns whether or not +macro+ is defined either in the common header
+# files or within any +headers+ you provide.
+#
+# Any options you pass to +opt+ are passed along to the compiler.
+#
+def have_macro(macro, headers = nil, opt = "", &b)
+ m = "#{macro}"
+ m << " in #{headers.inspect}" if headers
+ checking_for m do
+ macro_defined?(macro, cpp_include(headers), opt, &b)
+ end
+end
+
+# Returns whether or not the given entry point +func+ can be found within
+# +lib+. If +func+ is nil, the 'main()' entry point is used by default.
+# If found, it adds the library to list of libraries to be used when linking
+# your extension.
+#
+# If +headers+ are provided, it will include those header files as the
+# header files it looks in when searching for +func+.
+#
+# Real name of the library to be linked can be altered by
+# '--with-FOOlib' configuration option.
+#
+def have_library(lib, func = nil, headers = nil, &b)
+ func = "main" if !func or func.empty?
+ lib = with_config(lib+'lib', lib)
+ checking_for "#{func}() in #{LIBARG%lib}" do
+ if COMMON_LIBS.include?(lib)
+ true
+ else
+ libs = append_library($libs, lib)
+ if try_func(func, libs, headers, &b)
+ $libs = libs
+ true
+ else
+ false
+ end
+ end
+ end
+end
+
+# Returns whether or not the entry point +func+ can be found within the library
+# +lib+ in one of the +paths+ specified, where +paths+ is an array of strings.
+# If +func+ is nil , then the main() function is used as the entry point.
+#
+# If +lib+ is found, then the path it was found on is added to the list of
+# library paths searched and linked against.
+#
+def find_library(lib, func, *paths, &b)
+ func = "main" if !func or func.empty?
+ lib = with_config(lib+'lib', lib)
+ paths = paths.collect {|path| path.split(File::PATH_SEPARATOR)}.flatten
+ checking_for "#{func}() in #{LIBARG%lib}" do
+ libpath = $LIBPATH
+ libs = append_library($libs, lib)
+ begin
+ until r = try_func(func, libs, &b) or paths.empty?
+ $LIBPATH = libpath | [paths.shift]
+ end
+ if r
+ $libs = libs
+ libpath = nil
+ end
+ ensure
+ $LIBPATH = libpath if libpath
+ end
+ r
+ end
+end
+
+# Returns whether or not the function +func+ can be found in the common
+# header files, or within any +headers+ that you provide. If found, a
+# macro is passed as a preprocessor constant to the compiler using the
+# function name, in uppercase, prepended with 'HAVE_'.
+#
+# For example, if have_func('foo') returned true, then the HAVE_FOO
+# preprocessor macro would be passed to the compiler.
+#
+def have_func(func, headers = nil, &b)
+ checking_for "#{func}()" do
+ if try_func(func, $libs, headers, &b)
+ $defs.push(format("-DHAVE_%s", func.upcase))
+ true
+ else
+ false
+ end
+ end
+end
+
+# Returns whether or not the variable +var+ can be found in the common
+# header files, or within any +headers+ that you provide. If found, a
+# macro is passed as a preprocessor constant to the compiler using the
+# variable name, in uppercase, prepended with 'HAVE_'.
+#
+# For example, if have_var('foo') returned true, then the HAVE_FOO
+# preprocessor macro would be passed to the compiler.
+#
+def have_var(var, headers = nil, &b)
+ checking_for "#{var}" do
+ if try_var(var, headers, &b)
+ $defs.push(format("-DHAVE_%s", var.upcase))
+ true
+ else
+ false
+ end
+ end
+end
+
+# Returns whether or not the given +header+ file can be found on your system.
+# If found, a macro is passed as a preprocessor constant to the compiler using
+# the header file name, in uppercase, prepended with 'HAVE_'.
+#
+# For example, if have_header('foo.h') returned true, then the HAVE_FOO_H
+# preprocessor macro would be passed to the compiler.
+#
+def have_header(header, &b)
+ checking_for header do
+ if try_cpp(cpp_include(header), &b)
+ $defs.push(format("-DHAVE_%s", header.tr("a-z./\055", "A-Z___")))
+ true
+ else
+ false
+ end
+ end
+end
+
+# Instructs mkmf to search for the given +header+ in any of the +paths+
+# provided, and returns whether or not it was found in those paths.
+#
+# If the header is found then the path it was found on is added to the list
+# of included directories that are sent to the compiler (via the -I switch).
+#
+def find_header(header, *paths)
+ header = cpp_include(header)
+ checking_for header do
+ if try_cpp(header)
+ true
+ else
+ found = false
+ paths.each do |dir|
+ opt = "-I#{dir}".quote
+ if try_cpp(header, opt)
+ $INCFLAGS << " " << opt
+ found = true
+ break
+ end
+ end
+ found
+ end
+ end
+end
+
+# Returns whether or not the struct of type +type+ contains +member+. If
+# it does not, or the struct type can't be found, then false is returned. You
+# may optionally specify additional +headers+ in which to look for the struct
+# (in addition to the common header files).
+#
+# If found, a macro is passed as a preprocessor constant to the compiler using
+# the member name, in uppercase, prepended with 'HAVE_ST_'.
+#
+# For example, if have_struct_member('foo', 'bar') returned true, then the
+# HAVE_ST_BAR preprocessor macro would be passed to the compiler.
+#
+def have_struct_member(type, member, headers = nil, &b)
+ checking_for "#{type}.#{member}" do
+ if try_compile(<<"SRC", &b)
+#{COMMON_HEADERS}
+#{cpp_include(headers)}
+/*top*/
+int main() { return 0; }
+int s = (char *)&((#{type}*)0)->#{member} - (char *)0;
+SRC
+ $defs.push(format("-DHAVE_ST_%s", member.upcase))
+ true
+ else
+ false
+ end
+ end
+end
+
+# Returns whether or not the static type +type+ is defined. You may
+# optionally pass additional +headers+ to check against in addition to the
+# common header files.
+#
+# You may also pass additional flags to +opt+ which are then passed along to
+# the compiler.
+#
+# If found, a macro is passed as a preprocessor constant to the compiler using
+# the type name, in uppercase, prepended with 'HAVE_TYPE_'.
+#
+# For example, if have_type('foo') returned true, then the HAVE_TYPE_FOO
+# preprocessor macro would be passed to the compiler.
+#
+def have_type(type, headers = nil, opt = "", &b)
+ checking_for type do
+ headers = cpp_include(headers)
+ if try_compile(<<"SRC", opt, &b) or (/\A\w+\z/n =~ type && try_compile(<<"SRC", opt, &b))
+#{COMMON_HEADERS}
+#{headers}
+/*top*/
+static #{type} t;
+SRC
+#{COMMON_HEADERS}
+#{headers}
+/*top*/
+static #{type} *t;
+SRC
+ $defs.push(format("-DHAVE_TYPE_%s", type.strip.upcase.tr_s("^A-Z0-9_", "_")))
+ true
+ else
+ false
+ end
+ end
+end
+
+# Returns the size of the given +type+. You may optionally specify additional
+# +headers+ to search in for the +type+.
+#
+# If found, a macro is passed as a preprocessor constant to the compiler using
+# the type name, in uppercase, prepended with 'SIZEOF_', followed by the type
+# name, followed by '=X' where 'X' is the actual size.
+#
+# For example, if check_sizeof('mystruct') returned 12, then the
+# SIZEOF_MYSTRUCT=12 preprocessor macro would be passed to the compiler.
+#
+def check_sizeof(type, headers = nil, &b)
+ expr = "sizeof(#{type})"
+ fmt = "%d"
+ def fmt.%(x)
+ x ? super : "failed"
+ end
+ checking_for("size of #{type}", fmt) do
+ if size = try_constant(expr, headers, &b)
+ $defs.push(format("-DSIZEOF_%s=%d", type.upcase.tr_s("^A-Z0-9_", "_"), size))
+ size
+ end
+ end
+end
+
+def scalar_ptr_type?(type, member = nil, headers = nil, &b)
+ try_compile(<<"SRC", &b) # pointer
+#{COMMON_HEADERS}
+#{cpp_include(headers)}
+/*top*/
+volatile #{type} conftestval;
+int main() { return 0; }
+int t() {return (int)(1-*(conftestval#{member ? ".#{member}" : ""}));}
+SRC
+end
+
+def scalar_type?(type, member = nil, headers = nil, &b)
+ try_compile(<<"SRC", &b) # pointer
+#{COMMON_HEADERS}
+#{cpp_include(headers)}
+/*top*/
+volatile #{type} conftestval;
+int main() { return 0; }
+int t() {return (int)(1-(conftestval#{member ? ".#{member}" : ""}));}
+SRC
+end
+
+def what_type?(type, member = nil, headers = nil, &b)
+ m = "#{type}"
+ name = type
+ if member
+ m << "." << member
+ name = "(((#{type} *)0)->#{member})"
+ end
+ m << " in #{headers.inspect}" if headers
+ fmt = "seems %s"
+ def fmt.%(x)
+ x ? super : "unknown"
+ end
+ checking_for m, fmt do
+ if scalar_ptr_type?(type, member, headers, &b)
+ if try_static_assert("sizeof(*#{name}) == 1", headers)
+ "string"
+ end
+ elsif scalar_type?(type, member, headers, &b)
+ if try_static_assert("sizeof(#{name}) > sizeof(long)", headers)
+ "long long"
+ elsif try_static_assert("sizeof(#{name}) > sizeof(int)", headers)
+ "long"
+ elsif try_static_assert("sizeof(#{name}) > sizeof(short)", headers)
+ "int"
+ elsif try_static_assert("sizeof(#{name}) > 1", headers)
+ "short"
+ else
+ "char"
+ end
+ end
+ end
+end
+
+def find_executable0(bin, path = nil)
+ ext = config_string('EXEEXT')
+ if File.expand_path(bin) == bin
+ return bin if File.executable?(bin)
+ return file if ext and File.executable?(file = bin + ext)
+ return nil
+ end
+ if path ||= ENV['PATH']
+ path = path.split(File::PATH_SEPARATOR)
+ else
+ path = %w[/usr/local/bin /usr/ucb /usr/bin /bin]
+ end
+ file = nil
+ path.each do |dir|
+ return file if File.executable?(file = File.join(dir, bin))
+ return file if ext and File.executable?(file << ext)
+ end
+ nil
+end
+
+def find_executable(bin, path = nil)
+ checking_for bin do
+ find_executable0(bin, path)
+ end
+end
+
+def arg_config(config, *defaults, &block)
+ $arg_config << [config, *defaults]
+ defaults << nil if !block and defaults.empty?
+ $configure_args.fetch(config.tr('_', '-'), *defaults, &block)
+end
+
+def with_config(config, *defaults)
+ config = config.sub(/^--with[-_]/, '')
+ val = arg_config("--with-"+config) do
+ if arg_config("--without-"+config)
+ false
+ elsif block_given?
+ yield(config, *defaults)
+ else
+ break *defaults
+ end
+ end
+ case val
+ when "yes"
+ true
+ when "no"
+ false
+ else
+ val
+ end
+end
+
+def enable_config(config, *defaults)
+ if arg_config("--enable-"+config)
+ true
+ elsif arg_config("--disable-"+config)
+ false
+ elsif block_given?
+ yield(config, *defaults)
+ else
+ return *defaults
+ end
+end
+
+def create_header(header = "extconf.h")
+ message "creating %s\n", header
+ sym = header.tr("a-z./\055", "A-Z___")
+ hdr = ["#ifndef #{sym}\n#define #{sym}\n"]
+ for line in $defs
+ case line
+ when /^-D([^=]+)(?:=(.*))?/
+ hdr << "#define #$1 #{$2 ? Shellwords.shellwords($2)[0] : 1}\n"
+ when /^-U(.*)/
+ hdr << "#undef #$1\n"
+ end
+ end
+ hdr << "#endif\n"
+ hdr = hdr.join
+ unless (IO.read(header) == hdr rescue false)
+ open(header, "w") do |hfile|
+ hfile.write(hdr)
+ end
+ end
+ $extconf_h = header
+end
+
+# Sets a +target+ name that the user can then use to configure various 'with'
+# options with on the command line by using that name. For example, if the
+# target is set to "foo", then the user could use the --with-foo-dir command
+# line option.
+#
+# You may pass along additional 'include' or 'lib' defaults via the +idefault+
+# and +ldefault+ parameters, respectively.
+#
+# Note that dir_config only adds to the list of places to search for libraries
+# and include files. It does not link the libraries into your application.
+#
+def dir_config(target, idefault=nil, ldefault=nil)
+ if dir = with_config(target + "-dir", (idefault unless ldefault))
+ defaults = Array === dir ? dir : dir.split(File::PATH_SEPARATOR)
+ idefault = ldefault = nil
+ end
+
+ idir = with_config(target + "-include", idefault)
+ $arg_config.last[1] ||= "${#{target}-dir}/include"
+ ldir = with_config(target + "-lib", ldefault)
+ $arg_config.last[1] ||= "${#{target}-dir}/lib"
+
+ idirs = idir ? Array === idir ? idir : idir.split(File::PATH_SEPARATOR) : []
+ if defaults
+ idirs.concat(defaults.collect {|dir| dir + "/include"})
+ idir = ([idir] + idirs).compact.join(File::PATH_SEPARATOR)
+ end
+ unless idirs.empty?
+ idirs.collect! {|dir| "-I" + dir}
+ idirs -= Shellwords.shellwords($CPPFLAGS)
+ unless idirs.empty?
+ $CPPFLAGS = (idirs.quote << $CPPFLAGS).join(" ")
+ end
+ end
+
+ ldirs = ldir ? Array === ldir ? ldir : ldir.split(File::PATH_SEPARATOR) : []
+ if defaults
+ ldirs.concat(defaults.collect {|dir| dir + "/lib"})
+ ldir = ([ldir] + ldirs).compact.join(File::PATH_SEPARATOR)
+ end
+ $LIBPATH = ldirs | $LIBPATH
+
+ [idir, ldir]
+end
+
+def pkg_config(pkg)
+ if pkgconfig = with_config("#{pkg}-config") and find_executable0(pkgconfig)
+ # iff package specific config command is given
+ get = proc {|opt| `#{pkgconfig} --#{opt}`.chomp}
+ elsif ($PKGCONFIG ||=
+ (pkgconfig = with_config("pkg-config", ("pkg-config" unless CROSS_COMPILING))) &&
+ find_executable0(pkgconfig) && pkgconfig) and
+ system("#{$PKGCONFIG} --exists #{pkg}")
+ # default to pkg-config command
+ get = proc {|opt| `#{$PKGCONFIG} --#{opt} #{pkg}`.chomp}
+ elsif find_executable0(pkgconfig = "#{pkg}-config")
+ # default to package specific config command, as a last resort.
+ get = proc {|opt| `#{pkgconfig} --#{opt}`.chomp}
+ end
+ if get
+ cflags = get['cflags']
+ ldflags = get['libs']
+ libs = get['libs-only-l']
+ ldflags = (Shellwords.shellwords(ldflags) - Shellwords.shellwords(libs)).quote.join(" ")
+ $CFLAGS += " " << cflags
+ $LDFLAGS += " " << ldflags
+ $libs += " " << libs
+ Logging::message "package configuration for %s\n", pkg
+ Logging::message "cflags: %s\nldflags: %s\nlibs: %s\n\n",
+ cflags, ldflags, libs
+ [cflags, ldflags, libs]
+ else
+ Logging::message "package configuration for %s is not found\n", pkg
+ nil
+ end
+end
+
+def with_destdir(dir)
+ dir = dir.sub($dest_prefix_pattern, '')
+ /\A\$[\(\{]/ =~ dir ? dir : "$(DESTDIR)"+dir
+end
+
+def winsep(s)
+ s.tr('/', '\\')
+end
+
+def configuration(srcdir)
+ mk = []
+ vpath = %w[$(srcdir) $(topdir) $(hdrdir)]
+ if !CROSS_COMPILING
+ case CONFIG['build_os']
+ when 'cygwin'
+ if CONFIG['target_os'] != 'cygwin'
+ vpath.each {|p| p.sub!(/.*/, '$(shell cygpath -u \&)')}
+ end
+ when 'msdosdjgpp', 'mingw32'
+ CONFIG['PATH_SEPARATOR'] = ';'
+ end
+ end
+ mk << %{
+SHELL = /bin/sh
+
+#### Start of system configuration section. ####
+
+srcdir = #{srcdir.gsub(/\$\((srcdir)\)|\$\{(srcdir)\}/) {CONFIG[$1||$2]}.quote}
+topdir = #{($extmk ? CONFIG["topdir"] : $topdir).quote}
+hdrdir = #{$extmk ? CONFIG["hdrdir"].quote : '$(topdir)'}
+VPATH = #{vpath.join(CONFIG['PATH_SEPARATOR'])}
+}
+ if destdir = CONFIG["prefix"][$dest_prefix_pattern, 1]
+ mk << "\nDESTDIR = #{destdir}\n"
+ end
+ CONFIG.each do |key, var|
+ next unless /prefix$/ =~ key
+ mk << "#{key} = #{with_destdir(var)}\n"
+ end
+ CONFIG.each do |key, var|
+ next if /^abs_/ =~ key
+ next unless /^(?:src|top|hdr|(.*))dir$/ =~ key and $1
+ mk << "#{key} = #{with_destdir(var)}\n"
+ end
+ if !$extmk and !$configure_args.has_key?('--ruby') and
+ sep = config_string('BUILD_FILE_SEPARATOR')
+ sep = ":/=#{sep}"
+ else
+ sep = ""
+ end
+ extconf_h = $extconf_h ? "-DRUBY_EXTCONF_H=\\\"$(RUBY_EXTCONF_H)\\\" " : $defs.join(" ")<<" "
+ mk << %{
+CC = #{CONFIG['CC']}
+LIBRUBY = #{CONFIG['LIBRUBY']}
+LIBRUBY_A = #{CONFIG['LIBRUBY_A']}
+LIBRUBYARG_SHARED = #$LIBRUBYARG_SHARED
+LIBRUBYARG_STATIC = #$LIBRUBYARG_STATIC
+
+RUBY_EXTCONF_H = #{$extconf_h}
+CFLAGS = #{CONFIG['CCDLFLAGS'] unless $static} #$CFLAGS #$ARCH_FLAG
+INCFLAGS = -I. #$INCFLAGS
+CPPFLAGS = #{extconf_h}#{$CPPFLAGS}
+CXXFLAGS = $(CFLAGS) #{CONFIG['CXXFLAGS']}
+DLDFLAGS = #$LDFLAGS #$DLDFLAGS #$ARCH_FLAG
+LDSHARED = #{CONFIG['LDSHARED']}
+AR = #{CONFIG['AR']}
+EXEEXT = #{CONFIG['EXEEXT']}
+
+RUBY_INSTALL_NAME = #{CONFIG['RUBY_INSTALL_NAME']}
+RUBY_SO_NAME = #{CONFIG['RUBY_SO_NAME']}
+arch = #{CONFIG['arch']}
+sitearch = #{CONFIG['sitearch']}
+ruby_version = #{Config::CONFIG['ruby_version']}
+ruby = #{$ruby}
+RUBY = $(ruby#{sep})
+RM = #{config_string('RM') || '$(RUBY) -run -e rm -- -f'}
+MAKEDIRS = #{config_string('MAKEDIRS') || '@$(RUBY) -run -e mkdir -- -p'}
+INSTALL = #{config_string('INSTALL') || '@$(RUBY) -run -e install -- -vp'}
+INSTALL_PROG = #{config_string('INSTALL_PROG') || '$(INSTALL) -m 0755'}
+INSTALL_DATA = #{config_string('INSTALL_DATA') || '$(INSTALL) -m 0644'}
+COPY = #{config_string('CP') || '@$(RUBY) -run -e cp -- -v'}
+
+#### End of system configuration section. ####
+
+preload = #{$preload.join(" ") if $preload}
+}
+ if $nmake == ?b
+ mk.each do |x|
+ x.gsub!(/^(MAKEDIRS|INSTALL_(?:PROG|DATA))+\s*=.*\n/) do
+ "!ifndef " + $1 + "\n" +
+ $& +
+ "!endif\n"
+ end
+ end
+ end
+ mk
+end
+
+def dummy_makefile(srcdir)
+ configuration(srcdir) << <<RULES << CLEANINGS
+CLEANFILES = #{$cleanfiles.join(' ')}
+DISTCLEANFILES = #{$distcleanfiles.join(' ')}
+
+all install static install-so install-rb: Makefile
+
+RULES
+end
+
+# Generates the Makefile for your extension, passing along any options and
+# preprocessor constants that you may have generated through other methods.
+#
+# The +target+ name should correspond the name of the global function name
+# defined within your C extension, minus the 'Init_'. For example, if your
+# C extension is defined as 'Init_foo', then your target would simply be 'foo'.
+#
+# If any '/' characters are present in the target name, only the last name
+# is interpreted as the target name, and the rest are considered toplevel
+# directory names, and the generated Makefile will be altered accordingly to
+# follow that directory structure.
+#
+# For example, if you pass 'test/foo' as a target name, your extension will
+# be installed under the 'test' directory. This means that in order to
+# load the file within a Ruby program later, that directory structure will
+# have to be followed, e.g. "require 'test/foo'".
+#
+def create_makefile(target, srcprefix = nil)
+ $target = target
+ libpath = $LIBPATH
+ message "creating Makefile\n"
+ rm_f "conftest*"
+ if CONFIG["DLEXT"] == $OBJEXT
+ for lib in libs = $libs.split
+ lib.sub!(/-l(.*)/, %%"lib\\1.#{$LIBEXT}"%)
+ end
+ $defs.push(format("-DEXTLIB='%s'", libs.join(",")))
+ end
+
+ if target.include?('/')
+ target_prefix, target = File.split(target)
+ target_prefix[0,0] = '/'
+ else
+ target_prefix = ""
+ end
+
+ srcprefix ||= '$(srcdir)'
+ Config::expand(srcdir = srcprefix.dup)
+
+ if not $objs
+ $objs = []
+ srcs = Dir[File.join(srcdir, "*.{#{SRC_EXT.join(%q{,})}}")]
+ for f in srcs
+ obj = File.basename(f, ".*") << ".o"
+ $objs.push(obj) unless $objs.index(obj)
+ end
+ elsif !(srcs = $srcs)
+ srcs = $objs.collect {|obj| obj.sub(/\.o\z/, '.c')}
+ end
+ for i in $objs
+ i.sub!(/\.o\z/, ".#{$OBJEXT}")
+ end
+ $objs = $objs.join(" ")
+
+ target = nil if $objs == ""
+
+ if target and EXPORT_PREFIX
+ if File.exist?(File.join(srcdir, target + '.def'))
+ deffile = "$(srcdir)/$(TARGET).def"
+ unless EXPORT_PREFIX.empty?
+ makedef = %{-pe "sub!(/^(?=\\w)/,'#{EXPORT_PREFIX}') unless 1../^EXPORTS$/i"}
+ end
+ else
+ makedef = %{-e "puts 'EXPORTS', '#{EXPORT_PREFIX}Init_$(TARGET)'"}
+ end
+ if makedef
+ $distcleanfiles << '$(DEFFILE)'
+ origdef = deffile
+ deffile = "$(TARGET)-$(arch).def"
+ end
+ end
+
+ libpath = libpathflag(libpath)
+
+ dllib = target ? "$(TARGET).#{CONFIG['DLEXT']}" : ""
+ staticlib = target ? "$(TARGET).#$LIBEXT" : ""
+ mfile = open("Makefile", "wb")
+ mfile.print configuration(srcprefix)
+ mfile.print %{
+libpath = #{$LIBPATH.join(" ")}
+LIBPATH = #{libpath}
+DEFFILE = #{deffile}
+
+CLEANFILES = #{$cleanfiles.join(' ')}
+DISTCLEANFILES = #{$distcleanfiles.join(' ')}
+
+extout = #{$extout}
+extout_prefix = #{$extout_prefix}
+target_prefix = #{target_prefix}
+LOCAL_LIBS = #{$LOCAL_LIBS}
+LIBS = #{$LIBRUBYARG} #{$libs} #{$LIBS}
+SRCS = #{srcs.collect(&File.method(:basename)).join(' ')}
+OBJS = #{$objs}
+TARGET = #{target}
+DLLIB = #{dllib}
+EXTSTATIC = #{$static || ""}
+STATIC_LIB = #{staticlib unless $static.nil?}
+
+}
+ install_dirs.each {|d| mfile.print("%-14s= %s\n" % d) if /^[[:upper:]]/ =~ d[0]}
+ n = ($extout ? '$(RUBYARCHDIR)/' : '') + '$(TARGET).'
+ mfile.print %{
+TARGET_SO = #{($extout ? '$(RUBYARCHDIR)/' : '')}$(DLLIB)
+CLEANLIBS = #{n}#{CONFIG['DLEXT']} #{n}il? #{n}tds #{n}map
+CLEANOBJS = *.#{$OBJEXT} *.#{$LIBEXT} *.s[ol] *.pdb *.exp *.bak
+
+all: #{$extout ? "install" : target ? "$(DLLIB)" : "Makefile"}
+static: $(STATIC_LIB)#{$extout ? " install-rb" : ""}
+}
+ mfile.print CLEANINGS
+ dirs = []
+ mfile.print "install: install-so install-rb\n\n"
+ sodir = (dir = "$(RUBYARCHDIR)").dup
+ mfile.print("install-so: #{dir}\n")
+ if target
+ f = "$(DLLIB)"
+ dest = "#{dir}/#{f}"
+ mfile.print "install-so: #{dest}\n"
+ unless $extout
+ mfile.print "#{dest}: #{f}\n"
+ if (sep = config_string('BUILD_FILE_SEPARATOR'))
+ f.gsub!("/", sep)
+ dir.gsub!("/", sep)
+ sep = ":/="+sep
+ f.gsub!(/(\$\(\w+)(\))/) {$1+sep+$2}
+ f.gsub!(/(\$\{\w+)(\})/) {$1+sep+$2}
+ dir.gsub!(/(\$\(\w+)(\))/) {$1+sep+$2}
+ dir.gsub!(/(\$\{\w+)(\})/) {$1+sep+$2}
+ end
+ mfile.print "\t$(INSTALL_PROG) #{f} #{dir}\n"
+ end
+ end
+ mfile.print("install-rb: pre-install-rb install-rb-default\n")
+ mfile.print("install-rb-default: pre-install-rb-default\n")
+ mfile.print("pre-install-rb: Makefile\n")
+ mfile.print("pre-install-rb-default: Makefile\n")
+ for sfx, i in [["-default", [["lib/**/*.rb", "$(RUBYLIBDIR)", "lib"]]], ["", $INSTALLFILES]]
+ files = install_files(mfile, i, nil, srcprefix) or next
+ for dir, *files in files
+ unless dirs.include?(dir)
+ dirs << dir
+ mfile.print "pre-install-rb#{sfx}: #{dir}\n"
+ end
+ files.each do |f|
+ dest = "#{dir}/#{File.basename(f)}"
+ mfile.print("install-rb#{sfx}: #{dest}\n")
+ mfile.print("#{dest}: #{f}\n\t$(#{$extout ? 'COPY' : 'INSTALL_DATA'}) ")
+ sep = config_string('BUILD_FILE_SEPARATOR')
+ if sep
+ f = f.gsub("/", sep)
+ sep = ":/="+sep
+ f = f.gsub(/(\$\(\w+)(\))/) {$1+sep+$2}
+ f = f.gsub(/(\$\{\w+)(\})/) {$1+sep+$2}
+ else
+ sep = ""
+ end
+ mfile.print("#{f} $(@D#{sep})\n")
+ end
+ end
+ end
+ dirs.unshift(sodir) if target and !dirs.include?(sodir)
+ dirs.each {|dir| mfile.print "#{dir}:\n\t$(MAKEDIRS) $@\n"}
+
+ mfile.print <<-SITEINSTALL
+
+site-install: site-install-so site-install-rb
+site-install-so: install-so
+site-install-rb: install-rb
+
+ SITEINSTALL
+
+ return unless target
+
+ mfile.puts SRC_EXT.collect {|ext| ".path.#{ext} = $(VPATH)"} if $nmake == ?b
+ mfile.print ".SUFFIXES: .#{SRC_EXT.join(' .')} .#{$OBJEXT}\n"
+ mfile.print "\n"
+
+ CXX_EXT.each do |ext|
+ COMPILE_RULES.each do |rule|
+ mfile.printf(rule, ext, $OBJEXT)
+ mfile.printf("\n\t%s\n\n", COMPILE_CXX)
+ end
+ end
+ %w[c].each do |ext|
+ COMPILE_RULES.each do |rule|
+ mfile.printf(rule, ext, $OBJEXT)
+ mfile.printf("\n\t%s\n\n", COMPILE_C)
+ end
+ end
+
+ mfile.print "$(RUBYARCHDIR)/" if $extout
+ mfile.print "$(DLLIB): ", (makedef ? "$(DEFFILE) " : ""), "$(OBJS)\n"
+ mfile.print "\t@-$(RM) $@\n"
+ mfile.print "\t@-$(MAKEDIRS) $(@D)\n" if $extout
+ link_so = LINK_SO.gsub(/^/, "\t")
+ mfile.print link_so, "\n\n"
+ unless $static.nil?
+ mfile.print "$(STATIC_LIB): $(OBJS)\n\t"
+ mfile.print "$(AR) #{config_string('ARFLAGS') || 'cru '}$@ $(OBJS)"
+ config_string('RANLIB') do |ranlib|
+ mfile.print "\n\t@-#{ranlib} $(DLLIB) 2> /dev/null || true"
+ end
+ end
+ mfile.print "\n\n"
+ if makedef
+ mfile.print "$(DEFFILE): #{origdef}\n"
+ mfile.print "\t$(RUBY) #{makedef} #{origdef} > $@\n\n"
+ end
+
+ depend = File.join(srcdir, "depend")
+ if File.exist?(depend)
+ suffixes = []
+ depout = []
+ open(depend, "r") do |dfile|
+ mfile.printf "###\n"
+ cont = implicit = nil
+ impconv = proc do
+ COMPILE_RULES.each {|rule| depout << (rule % implicit[0]) << implicit[1]}
+ implicit = nil
+ end
+ ruleconv = proc do |line|
+ if implicit
+ if /\A\t/ =~ line
+ implicit[1] << line
+ next
+ else
+ impconv[]
+ end
+ end
+ if m = /\A\.(\w+)\.(\w+)(?:\s*:)/.match(line)
+ suffixes << m[1] << m[2]
+ implicit = [[m[1], m[2]], [m.post_match]]
+ next
+ elsif RULE_SUBST and /\A(?!\s*\w+\s*=)[$\w][^#]*:/ =~ line
+ line.gsub!(%r"(\s)(?!\.)([^$(){}+=:\s\/\\,]+)(?=\s|\z)") {$1 + RULE_SUBST % $2}
+ end
+ depout << line
+ end
+ while line = dfile.gets()
+ line.gsub!(/\.o\b/, ".#{$OBJEXT}")
+ line.gsub!(/\$\(hdrdir\)\/config.h/, $config_h) if $config_h
+ if /(?:^|[^\\])(?:\\\\)*\\$/ =~ line
+ (cont ||= []) << line
+ next
+ elsif cont
+ line = (cont << line).join
+ cont = nil
+ end
+ ruleconv.call(line)
+ end
+ if cont
+ ruleconv.call(cont.join)
+ elsif implicit
+ impconv.call
+ end
+ end
+ unless suffixes.empty?
+ mfile.print ".SUFFIXES: .", suffixes.uniq.join(" ."), "\n\n"
+ end
+ mfile.print "$(OBJS): $(RUBY_EXTCONF_H)\n\n" if $extconf_h
+ mfile.print depout
+ else
+ headers = %w[ruby.h defines.h]
+ if RULE_SUBST
+ headers.each {|h| h.sub!(/.*/) {|*m| RULE_SUBST % m}}
+ end
+ headers << $config_h if $config_h
+ headers << "$(RUBY_EXTCONF_H)" if $extconf_h
+ mfile.print "$(OBJS): ", headers.join(' '), "\n"
+ end
+
+ $makefile_created = true
+ensure
+ mfile.close if mfile
+end
+
+def init_mkmf(config = CONFIG)
+ $makefile_created = false
+ $arg_config = []
+ $enable_shared = config['ENABLE_SHARED'] == 'yes'
+ $defs = []
+ $extconf_h = nil
+ $CFLAGS = with_config("cflags", arg_config("CFLAGS", config["CFLAGS"])).dup
+ $ARCH_FLAG = with_config("arch_flag", arg_config("ARCH_FLAG", config["ARCH_FLAG"])).dup
+ $CPPFLAGS = with_config("cppflags", arg_config("CPPFLAGS", config["CPPFLAGS"])).dup
+ $LDFLAGS = (with_config("ldflags") || "").dup
+ $INCFLAGS = "-I$(topdir) -I$(hdrdir) -I$(srcdir)"
+ $DLDFLAGS = with_config("dldflags", arg_config("DLDFLAGS", config["DLDFLAGS"])).dup
+ $LIBEXT = config['LIBEXT'].dup
+ $OBJEXT = config["OBJEXT"].dup
+ $LIBS = "#{config['LIBS']} #{config['DLDLIBS']}"
+ $LIBRUBYARG = ""
+ $LIBRUBYARG_STATIC = config['LIBRUBYARG_STATIC']
+ $LIBRUBYARG_SHARED = config['LIBRUBYARG_SHARED']
+ $LIBPATH = $extmk ? ["$(topdir)"] : CROSS_COMPILING ? [] : ["$(libdir)"]
+ $INSTALLFILES = nil
+
+ $objs = nil
+ $srcs = nil
+ $libs = ""
+ if $enable_shared or Config.expand(config["LIBRUBY"].dup) != Config.expand(config["LIBRUBY_A"].dup)
+ $LIBRUBYARG = config['LIBRUBYARG']
+ end
+
+ $LOCAL_LIBS = ""
+
+ $cleanfiles = config_string('CLEANFILES') {|s| Shellwords.shellwords(s)} || []
+ $cleanfiles << "mkmf.log"
+ $distcleanfiles = config_string('DISTCLEANFILES') {|s| Shellwords.shellwords(s)} || []
+
+ $extout ||= nil
+ $extout_prefix ||= nil
+
+ $arg_config.clear
+ dir_config("opt")
+end
+
+FailedMessage = <<MESSAGE
+Could not create Makefile due to some reason, probably lack of
+necessary libraries and/or headers. Check the mkmf.log file for more
+details. You may need configuration options.
+
+Provided configuration options:
+MESSAGE
+
+def mkmf_failed(path)
+ unless $makefile_created or File.exist?("Makefile")
+ opts = $arg_config.collect {|t, n| "\t#{t}#{"=#{n}" if n}\n"}
+ abort "*** #{path} failed ***\n" + FailedMessage + opts.join
+ end
+end
+
+init_mkmf
+
+$make = with_config("make-prog", ENV["MAKE"] || "make")
+make, = Shellwords.shellwords($make)
+$nmake = nil
+case
+when $mswin
+ $nmake = ?m if /nmake/i =~ make
+when $bccwin
+ $nmake = ?b if /Borland/i =~ `#{make} -h`
+end
+
+Config::CONFIG["srcdir"] = CONFIG["srcdir"] =
+ $srcdir = arg_config("--srcdir", File.dirname($0))
+$configure_args["--topsrcdir"] ||= $srcdir
+if $curdir = arg_config("--curdir")
+ Config.expand(curdir = $curdir.dup)
+else
+ curdir = $curdir = "."
+end
+unless File.expand_path(Config::CONFIG["topdir"]) == File.expand_path(curdir)
+ CONFIG["topdir"] = $curdir
+ Config::CONFIG["topdir"] = curdir
+end
+$configure_args["--topdir"] ||= $curdir
+$ruby = arg_config("--ruby", File.join(Config::CONFIG["bindir"], CONFIG["ruby_install_name"]))
+
+split = Shellwords.method(:shellwords).to_proc
+
+EXPORT_PREFIX = config_string('EXPORT_PREFIX') {|s| s.strip}
+
+hdr = []
+config_string('COMMON_MACROS') do |s|
+ Shellwords.shellwords(s).each do |s|
+ /(.*?)(?:=(.*))/ =~ s
+ hdr << "#define #$1 #$2"
+ end
+end
+config_string('COMMON_HEADERS') do |s|
+ Shellwords.shellwords(s).each {|s| hdr << "#include <#{s}>"}
+end
+COMMON_HEADERS = (hdr.join("\n") unless hdr.empty?)
+COMMON_LIBS = config_string('COMMON_LIBS', &split) || []
+
+COMPILE_RULES = config_string('COMPILE_RULES', &split) || %w[.%s.%s:]
+RULE_SUBST = config_string('RULE_SUBST')
+COMPILE_C = config_string('COMPILE_C') || '$(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) -c $<'
+COMPILE_CXX = config_string('COMPILE_CXX') || '$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<'
+TRY_LINK = config_string('TRY_LINK') ||
+ "$(CC) #{OUTFLAG}conftest $(INCFLAGS) $(CPPFLAGS) " \
+ "$(CFLAGS) $(src) $(LIBPATH) $(LDFLAGS) $(ARCH_FLAG) $(LOCAL_LIBS) $(LIBS)"
+LINK_SO = config_string('LINK_SO') ||
+ if CONFIG["DLEXT"] == $OBJEXT
+ "ld $(DLDFLAGS) -r -o $@ $(OBJS)\n"
+ else
+ "$(LDSHARED) $(DLDFLAGS) $(LIBPATH) #{OUTFLAG}$@ " \
+ "$(OBJS) $(LOCAL_LIBS) $(LIBS)"
+ end
+LIBPATHFLAG = config_string('LIBPATHFLAG') || ' -L"%s"'
+RPATHFLAG = config_string('RPATHFLAG') || ''
+LIBARG = config_string('LIBARG') || '-l%s'
+
+sep = config_string('BUILD_FILE_SEPARATOR') {|sep| ":/=#{sep}" if sep != "/"} || ""
+CLEANINGS = "
+clean:
+ @-$(RM) $(CLEANLIBS#{sep}) $(CLEANOBJS#{sep}) $(CLEANFILES#{sep})
+
+distclean: clean
+ @-$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
+ @-$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES#{sep})
+
+realclean: distclean
+"
+
+if not $extmk and /\A(extconf|makefile).rb\z/ =~ File.basename($0)
+ END {mkmf_failed($0)}
+end
diff --git a/ruby_1_8_5/lib/monitor.rb b/ruby_1_8_5/lib/monitor.rb
new file mode 100644
index 0000000000..4c146c8086
--- /dev/null
+++ b/ruby_1_8_5/lib/monitor.rb
@@ -0,0 +1,352 @@
+=begin
+
+= monitor.rb
+
+Copyright (C) 2001 Shugo Maeda <shugo@ruby-lang.org>
+
+This library is distributed under the terms of the Ruby license.
+You can freely distribute/modify this library.
+
+== example
+
+This is a simple example.
+
+ require 'monitor.rb'
+
+ buf = []
+ buf.extend(MonitorMixin)
+ empty_cond = buf.new_cond
+
+ # consumer
+ Thread.start do
+ loop do
+ buf.synchronize do
+ empty_cond.wait_while { buf.empty? }
+ print buf.shift
+ end
+ end
+ end
+
+ # producer
+ while line = ARGF.gets
+ buf.synchronize do
+ buf.push(line)
+ empty_cond.signal
+ end
+ end
+
+The consumer thread waits for the producer thread to push a line
+to buf while buf.empty?, and the producer thread (main thread)
+reads a line from ARGF and push it to buf, then call
+empty_cond.signal.
+
+=end
+
+
+#
+# Adds monitor functionality to an arbitrary object by mixing the module with
+# +include+. For example:
+#
+# require 'monitor.rb'
+#
+# buf = []
+# buf.extend(MonitorMixin)
+# empty_cond = buf.new_cond
+#
+# # consumer
+# Thread.start do
+# loop do
+# buf.synchronize do
+# empty_cond.wait_while { buf.empty? }
+# print buf.shift
+# end
+# end
+# end
+#
+# # producer
+# while line = ARGF.gets
+# buf.synchronize do
+# buf.push(line)
+# empty_cond.signal
+# end
+# end
+#
+# The consumer thread waits for the producer thread to push a line
+# to buf while buf.empty?, and the producer thread (main thread)
+# reads a line from ARGF and push it to buf, then call
+# empty_cond.signal.
+#
+module MonitorMixin
+ #
+ # FIXME: This isn't documented in Nutshell.
+ #
+ # Since MonitorMixin.new_cond returns a ConditionVariable, and the example
+ # above calls while_wait and signal, this class should be documented.
+ #
+ class ConditionVariable
+ class Timeout < Exception; end
+
+ # Create a new timer with the argument timeout, and add the
+ # current thread to the list of waiters. Then the thread is
+ # stopped. It will be resumed when a corresponding #signal
+ # occurs.
+ def wait(timeout = nil)
+ @monitor.instance_eval {mon_check_owner()}
+ timer = create_timer(timeout)
+
+ Thread.critical = true
+ count = @monitor.instance_eval {mon_exit_for_cond()}
+ @waiters.push(Thread.current)
+
+ begin
+ Thread.stop
+ return true
+ rescue Timeout
+ return false
+ ensure
+ Thread.critical = true
+ begin
+ if timer && timer.alive?
+ Thread.kill(timer)
+ end
+ if @waiters.include?(Thread.current) # interrupted?
+ @waiters.delete(Thread.current)
+ end
+ @monitor.instance_eval {mon_enter_for_cond(count)}
+ ensure
+ Thread.critical = false
+ end
+ end
+ end
+
+
+ # call #wait while the supplied block returns +true+.
+ def wait_while
+ while yield
+ wait
+ end
+ end
+
+ # call #wait until the supplied block returns +true+.
+ def wait_until
+ until yield
+ wait
+ end
+ end
+
+ # Wake up and run the next waiter
+ def signal
+ @monitor.instance_eval {mon_check_owner()}
+ Thread.critical = true
+ t = @waiters.shift
+ t.wakeup if t
+ Thread.critical = false
+ Thread.pass
+ end
+
+ # Wake up all the waiters.
+ def broadcast
+ @monitor.instance_eval {mon_check_owner()}
+ Thread.critical = true
+ for t in @waiters
+ t.wakeup
+ end
+ @waiters.clear
+ Thread.critical = false
+ Thread.pass
+ end
+
+ def count_waiters
+ return @waiters.length
+ end
+
+ private
+
+ def initialize(monitor)
+ @monitor = monitor
+ @waiters = []
+ end
+
+ def create_timer(timeout)
+ if timeout
+ waiter = Thread.current
+ return Thread.start {
+ Thread.pass
+ sleep(timeout)
+ Thread.critical = true
+ waiter.raise(Timeout.new)
+ }
+ else
+ return nil
+ end
+ end
+ end
+
+ def self.extend_object(obj)
+ super(obj)
+ obj.instance_eval {mon_initialize()}
+ end
+
+ #
+ # Attempts to enter exclusive section. Returns +false+ if lock fails.
+ #
+ def mon_try_enter
+ result = false
+ Thread.critical = true
+ if @mon_owner.nil?
+ @mon_owner = Thread.current
+ end
+ if @mon_owner == Thread.current
+ @mon_count += 1
+ result = true
+ end
+ Thread.critical = false
+ return result
+ end
+ # For backward compatibility
+ alias try_mon_enter mon_try_enter
+
+ #
+ # Enters exclusive section.
+ #
+ def mon_enter
+ Thread.critical = true
+ mon_acquire(@mon_entering_queue)
+ @mon_count += 1
+ ensure
+ Thread.critical = false
+ end
+
+ #
+ # Leaves exclusive section.
+ #
+ def mon_exit
+ mon_check_owner
+ Thread.critical = true
+ @mon_count -= 1
+ if @mon_count == 0
+ mon_release
+ end
+ Thread.critical = false
+ Thread.pass
+ end
+
+ #
+ # Enters exclusive section and executes the block. Leaves the exclusive
+ # section automatically when the block exits. See example under
+ # +MonitorMixin+.
+ #
+ def mon_synchronize
+ mon_enter
+ begin
+ yield
+ ensure
+ mon_exit
+ end
+ end
+ alias synchronize mon_synchronize
+
+ #
+ # FIXME: This isn't documented in Nutshell.
+ #
+ # Create a new condition variable for this monitor.
+ # This facilitates control of the monitor with #signal and #wait.
+ #
+ def new_cond
+ return ConditionVariable.new(self)
+ end
+
+ private
+
+ def initialize(*args)
+ super
+ mon_initialize
+ end
+
+ # called by initialize method to set defaults for instance variables.
+ def mon_initialize
+ @mon_owner = nil
+ @mon_count = 0
+ @mon_entering_queue = []
+ @mon_waiting_queue = []
+ end
+
+ # Throw a ThreadError exception if the current thread
+ # does't own the monitor
+ def mon_check_owner
+ if @mon_owner != Thread.current
+ raise ThreadError, "current thread not owner"
+ end
+ end
+
+ def mon_acquire(queue)
+ while @mon_owner && @mon_owner != Thread.current
+ queue.push(Thread.current)
+ Thread.stop
+ Thread.critical = true
+ end
+ @mon_owner = Thread.current
+ end
+
+ def mon_release
+ @mon_owner = nil
+ t = @mon_waiting_queue.shift
+ t = @mon_entering_queue.shift unless t
+ t.wakeup if t
+ end
+
+ def mon_enter_for_cond(count)
+ mon_acquire(@mon_waiting_queue)
+ @mon_count = count
+ end
+
+ def mon_exit_for_cond
+ count = @mon_count
+ @mon_count = 0
+ return count
+ ensure
+ mon_release
+ end
+end
+
+# Monitors provide means of mutual exclusion for Thread programming.
+# A critical region is created by means of the synchronize method,
+# which takes a block.
+# The condition variables (created with #new_cond) may be used
+# to control the execution of a monitor with #signal and #wait.
+#
+# the Monitor class wraps MonitorMixin, and provides aliases
+# alias try_enter try_mon_enter
+# alias enter mon_enter
+# alias exit mon_exit
+# to access its methods more concisely.
+class Monitor
+ include MonitorMixin
+ alias try_enter try_mon_enter
+ alias enter mon_enter
+ alias exit mon_exit
+end
+
+
+# Documentation comments:
+# - All documentation comes from Nutshell.
+# - MonitorMixin.new_cond appears in the example, but is not documented in
+# Nutshell.
+# - All the internals (internal modules Accessible and Initializable, class
+# ConditionVariable) appear in RDoc. It might be good to hide them, by
+# making them private, or marking them :nodoc:, etc.
+# - The entire example from the RD section at the top is replicated in the RDoc
+# comment for MonitorMixin. Does the RD section need to remain?
+# - RDoc doesn't recognise aliases, so we have mon_synchronize documented, but
+# not synchronize.
+# - mon_owner is in Nutshell, but appears as an accessor in a separate module
+# here, so is hard/impossible to RDoc. Some other useful accessors
+# (mon_count and some queue stuff) are also in this module, and don't appear
+# directly in the RDoc output.
+# - in short, it may be worth changing the code layout in this file to make the
+# documentation easier
+
+# Local variables:
+# mode: Ruby
+# tab-width: 8
+# End:
diff --git a/ruby_1_8_5/lib/mutex_m.rb b/ruby_1_8_5/lib/mutex_m.rb
new file mode 100644
index 0000000000..8e0d42bc8d
--- /dev/null
+++ b/ruby_1_8_5/lib/mutex_m.rb
@@ -0,0 +1,122 @@
+#--
+# mutex_m.rb -
+# $Release Version: 3.0$
+# $Revision: 1.7 $
+# $Date: 1998/02/27 04:28:57 $
+# Original from mutex.rb
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
+# modified by matz
+# patched by akira yamada
+#++
+#
+# == Usage
+#
+# Extend an object and use it like a Mutex object:
+#
+# require "mutex_m.rb"
+# obj = Object.new
+# obj.extend Mutex_m
+# # ...
+#
+# Or, include Mutex_m in a class to have its instances behave like a Mutex
+# object:
+#
+# class Foo
+# include Mutex_m
+# # ...
+# end
+#
+# obj = Foo.new
+
+module Mutex_m
+ def Mutex_m.define_aliases(cl)
+ cl.module_eval %q{
+ alias locked? mu_locked?
+ alias lock mu_lock
+ alias unlock mu_unlock
+ alias try_lock mu_try_lock
+ alias synchronize mu_synchronize
+ }
+ end
+
+ def Mutex_m.append_features(cl)
+ super
+ define_aliases(cl) unless cl.instance_of?(Module)
+ end
+
+ def Mutex_m.extend_object(obj)
+ super
+ obj.mu_extended
+ end
+
+ def mu_extended
+ unless (defined? locked? and
+ defined? lock and
+ defined? unlock and
+ defined? try_lock and
+ defined? synchronize)
+ Mutex_m.define_aliases(class<<self;self;end)
+ end
+ mu_initialize
+ end
+
+ # locking
+ def mu_synchronize
+ begin
+ mu_lock
+ yield
+ ensure
+ mu_unlock
+ end
+ end
+
+ def mu_locked?
+ @mu_locked
+ end
+
+ def mu_try_lock
+ result = false
+ Thread.critical = true
+ unless @mu_locked
+ @mu_locked = true
+ result = true
+ end
+ Thread.critical = false
+ result
+ end
+
+ def mu_lock
+ while (Thread.critical = true; @mu_locked)
+ @mu_waiting.push Thread.current
+ Thread.stop
+ end
+ @mu_locked = true
+ Thread.critical = false
+ self
+ end
+
+ def mu_unlock
+ return unless @mu_locked
+ Thread.critical = true
+ wait = @mu_waiting
+ @mu_waiting = []
+ @mu_locked = false
+ Thread.critical = false
+ for w in wait
+ w.run
+ end
+ self
+ end
+
+ private
+
+ def mu_initialize
+ @mu_waiting = []
+ @mu_locked = false;
+ end
+
+ def initialize(*args)
+ mu_initialize
+ super
+ end
+end
diff --git a/ruby_1_8_5/lib/net/ftp.rb b/ruby_1_8_5/lib/net/ftp.rb
new file mode 100644
index 0000000000..dfbcf1499f
--- /dev/null
+++ b/ruby_1_8_5/lib/net/ftp.rb
@@ -0,0 +1,923 @@
+#
+# = net/ftp.rb - FTP Client Library
+#
+# Written by Shugo Maeda <shugo@ruby-lang.org>.
+#
+# Documentation by Gavin Sinclair, sourced from "Programming Ruby" (Hunt/Thomas)
+# and "Ruby In a Nutshell" (Matsumoto), used with permission.
+#
+# This library is distributed under the terms of the Ruby license.
+# You can freely distribute/modify this library.
+#
+# It is included in the Ruby standard library.
+#
+# See the Net::FTP class for an overview.
+#
+
+require "socket"
+require "monitor"
+
+module Net
+
+ # :stopdoc:
+ class FTPError < StandardError; end
+ class FTPReplyError < FTPError; end
+ class FTPTempError < FTPError; end
+ class FTPPermError < FTPError; end
+ class FTPProtoError < FTPError; end
+ # :startdoc:
+
+ #
+ # This class implements the File Transfer Protocol. If you have used a
+ # command-line FTP program, and are familiar with the commands, you will be
+ # able to use this class easily. Some extra features are included to take
+ # advantage of Ruby's style and strengths.
+ #
+ # == Example
+ #
+ # require 'net/ftp'
+ #
+ # === Example 1
+ #
+ # ftp = Net::FTP.new('ftp.netlab.co.jp')
+ # ftp.login
+ # files = ftp.chdir('pub/lang/ruby/contrib')
+ # files = ftp.list('n*')
+ # ftp.getbinaryfile('nif.rb-0.91.gz', 'nif.gz', 1024)
+ # ftp.close
+ #
+ # === Example 2
+ #
+ # Net::FTP.open('ftp.netlab.co.jp') do |ftp|
+ # ftp.login
+ # files = ftp.chdir('pub/lang/ruby/contrib')
+ # files = ftp.list('n*')
+ # ftp.getbinaryfile('nif.rb-0.91.gz', 'nif.gz', 1024)
+ # end
+ #
+ # == Major Methods
+ #
+ # The following are the methods most likely to be useful to users:
+ # - FTP.open
+ # - #getbinaryfile
+ # - #gettextfile
+ # - #putbinaryfile
+ # - #puttextfile
+ # - #chdir
+ # - #nlst
+ # - #size
+ # - #rename
+ # - #delete
+ #
+ class FTP
+ include MonitorMixin
+
+ # :stopdoc:
+ FTP_PORT = 21
+ CRLF = "\r\n"
+ DEFAULT_BLOCKSIZE = 4096
+ # :startdoc:
+
+ # When +true+, transfers are performed in binary mode. Default: +true+.
+ attr_accessor :binary
+
+ # When +true+, the connection is in passive mode. Default: +false+.
+ attr_accessor :passive
+
+ # When +true+, all traffic to and from the server is written
+ # to +$stdout+. Default: +false+.
+ attr_accessor :debug_mode
+
+ # Sets or retrieves the +resume+ status, which decides whether incomplete
+ # transfers are resumed or restarted. Default: +false+.
+ attr_accessor :resume
+
+ # The server's welcome message.
+ attr_reader :welcome
+
+ # The server's last response code.
+ attr_reader :last_response_code
+ alias lastresp last_response_code
+
+ # The server's last response.
+ attr_reader :last_response
+
+ #
+ # A synonym for <tt>FTP.new</tt>, but with a mandatory host parameter.
+ #
+ # If a block is given, it is passed the +FTP+ object, which will be closed
+ # when the block finishes, or when an exception is raised.
+ #
+ def FTP.open(host, user = nil, passwd = nil, acct = nil)
+ if block_given?
+ ftp = new(host, user, passwd, acct)
+ begin
+ yield ftp
+ ensure
+ ftp.close
+ end
+ else
+ new(host, user, passwd, acct)
+ end
+ end
+
+ #
+ # Creates and returns a new +FTP+ object. If a +host+ is given, a connection
+ # is made. Additionally, if the +user+ is given, the given user name,
+ # password, and (optionally) account are used to log in. See #login.
+ #
+ def initialize(host = nil, user = nil, passwd = nil, acct = nil)
+ super()
+ @binary = true
+ @passive = false
+ @debug_mode = false
+ @resume = false
+ if host
+ connect(host)
+ if user
+ login(user, passwd, acct)
+ end
+ end
+ end
+
+ # Obsolete
+ def return_code
+ $stderr.puts("warning: Net::FTP#return_code is obsolete and do nothing")
+ return "\n"
+ end
+
+ # Obsolete
+ def return_code=(s)
+ $stderr.puts("warning: Net::FTP#return_code= is obsolete and do nothing")
+ end
+
+ def open_socket(host, port)
+ if defined? SOCKSsocket and ENV["SOCKS_SERVER"]
+ @passive = true
+ return SOCKSsocket.open(host, port)
+ else
+ return TCPSocket.open(host, port)
+ end
+ end
+ private :open_socket
+
+ #
+ # Establishes an FTP connection to host, optionally overriding the default
+ # port. If the environment variable +SOCKS_SERVER+ is set, sets up the
+ # connection through a SOCKS proxy. Raises an exception (typically
+ # <tt>Errno::ECONNREFUSED</tt>) if the connection cannot be established.
+ #
+ def connect(host, port = FTP_PORT)
+ if @debug_mode
+ print "connect: ", host, ", ", port, "\n"
+ end
+ synchronize do
+ @sock = open_socket(host, port)
+ voidresp
+ end
+ end
+
+ #
+ # WRITEME or make private
+ #
+ def set_socket(sock, get_greeting = true)
+ synchronize do
+ @sock = sock
+ if get_greeting
+ voidresp
+ end
+ end
+ end
+
+ def sanitize(s)
+ if s =~ /^PASS /i
+ return s[0, 5] + "*" * (s.length - 5)
+ else
+ return s
+ end
+ end
+ private :sanitize
+
+ def putline(line)
+ if @debug_mode
+ print "put: ", sanitize(line), "\n"
+ end
+ line = line + CRLF
+ @sock.write(line)
+ end
+ private :putline
+
+ def getline
+ line = @sock.readline # if get EOF, raise EOFError
+ line.sub!(/(\r\n|\n|\r)\z/n, "")
+ if @debug_mode
+ print "get: ", sanitize(line), "\n"
+ end
+ return line
+ end
+ private :getline
+
+ def getmultiline
+ line = getline
+ buff = line
+ if line[3] == ?-
+ code = line[0, 3]
+ begin
+ line = getline
+ buff << "\n" << line
+ end until line[0, 3] == code and line[3] != ?-
+ end
+ return buff << "\n"
+ end
+ private :getmultiline
+
+ def getresp
+ @last_response = getmultiline
+ @last_response_code = @last_response[0, 3]
+ case @last_response_code
+ when /\A[123]/
+ return @last_response
+ when /\A4/
+ raise FTPTempError, @last_response
+ when /\A5/
+ raise FTPPermError, @last_response
+ else
+ raise FTPProtoError, @last_response
+ end
+ end
+ private :getresp
+
+ def voidresp
+ resp = getresp
+ if resp[0] != ?2
+ raise FTPReplyError, resp
+ end
+ end
+ private :voidresp
+
+ #
+ # Sends a command and returns the response.
+ #
+ def sendcmd(cmd)
+ synchronize do
+ putline(cmd)
+ return getresp
+ end
+ end
+
+ #
+ # Sends a command and expect a response beginning with '2'.
+ #
+ def voidcmd(cmd)
+ synchronize do
+ putline(cmd)
+ voidresp
+ end
+ end
+
+ def sendport(host, port)
+ af = (@sock.peeraddr)[0]
+ if af == "AF_INET"
+ hbytes = host.split(".")
+ pbytes = [port / 256, port % 256]
+ bytes = hbytes + pbytes
+ cmd = "PORT " + bytes.join(",")
+ elsif af == "AF_INET6"
+ cmd = "EPRT |2|" + host + "|" + sprintf("%d", port) + "|"
+ else
+ raise FTPProtoError, host
+ end
+ voidcmd(cmd)
+ end
+ private :sendport
+
+ def makeport
+ sock = TCPServer.open(@sock.addr[3], 0)
+ port = sock.addr[1]
+ host = sock.addr[3]
+ resp = sendport(host, port)
+ return sock
+ end
+ private :makeport
+
+ def makepasv
+ if @sock.peeraddr[0] == "AF_INET"
+ host, port = parse227(sendcmd("PASV"))
+ else
+ host, port = parse229(sendcmd("EPSV"))
+ # host, port = parse228(sendcmd("LPSV"))
+ end
+ return host, port
+ end
+ private :makepasv
+
+ def transfercmd(cmd, rest_offset = nil)
+ if @passive
+ host, port = makepasv
+ conn = open_socket(host, port)
+ if @resume and rest_offset
+ resp = sendcmd("REST " + rest_offset.to_s)
+ if resp[0] != ?3
+ raise FTPReplyError, resp
+ end
+ end
+ resp = sendcmd(cmd)
+ if resp[0] != ?1
+ raise FTPReplyError, resp
+ end
+ else
+ sock = makeport
+ if @resume and rest_offset
+ resp = sendcmd("REST " + rest_offset.to_s)
+ if resp[0] != ?3
+ raise FTPReplyError, resp
+ end
+ end
+ resp = sendcmd(cmd)
+ if resp[0] != ?1
+ raise FTPReplyError, resp
+ end
+ conn = sock.accept
+ sock.close
+ end
+ return conn
+ end
+ private :transfercmd
+
+ def getaddress
+ thishost = Socket.gethostname
+ if not thishost.index(".")
+ thishost = Socket.gethostbyname(thishost)[0]
+ end
+ if ENV.has_key?("LOGNAME")
+ realuser = ENV["LOGNAME"]
+ elsif ENV.has_key?("USER")
+ realuser = ENV["USER"]
+ else
+ realuser = "anonymous"
+ end
+ return realuser + "@" + thishost
+ end
+ private :getaddress
+
+ #
+ # Logs in to the remote host. The session must have been previously
+ # connected. If +user+ is the string "anonymous" and the +password+ is
+ # +nil+, a password of <tt>user@host</tt> is synthesized. If the +acct+
+ # parameter is not +nil+, an FTP ACCT command is sent following the
+ # successful login. Raises an exception on error (typically
+ # <tt>Net::FTPPermError</tt>).
+ #
+ def login(user = "anonymous", passwd = nil, acct = nil)
+ if user == "anonymous" and passwd == nil
+ passwd = getaddress
+ end
+
+ resp = ""
+ synchronize do
+ resp = sendcmd('USER ' + user)
+ if resp[0] == ?3
+ resp = sendcmd('PASS ' + passwd)
+ end
+ if resp[0] == ?3
+ resp = sendcmd('ACCT ' + acct)
+ end
+ end
+ if resp[0] != ?2
+ raise FTPReplyError, resp
+ end
+ @welcome = resp
+ end
+
+ #
+ # Puts the connection into binary (image) mode, issues the given command,
+ # and fetches the data returned, passing it to the associated block in
+ # chunks of +blocksize+ characters. Note that +cmd+ is a server command
+ # (such as "RETR myfile").
+ #
+ def retrbinary(cmd, blocksize, rest_offset = nil) # :yield: data
+ synchronize do
+ voidcmd("TYPE I")
+ conn = transfercmd(cmd, rest_offset)
+ loop do
+ data = conn.read(blocksize)
+ break if data == nil
+ yield(data)
+ end
+ conn.close
+ voidresp
+ end
+ end
+
+ #
+ # Puts the connection into ASCII (text) mode, issues the given command, and
+ # passes the resulting data, one line at a time, to the associated block. If
+ # no block is given, prints the lines. Note that +cmd+ is a server command
+ # (such as "RETR myfile").
+ #
+ def retrlines(cmd) # :yield: line
+ synchronize do
+ voidcmd("TYPE A")
+ conn = transfercmd(cmd)
+ loop do
+ line = conn.gets
+ break if line == nil
+ if line[-2, 2] == CRLF
+ line = line[0 .. -3]
+ elsif line[-1] == ?\n
+ line = line[0 .. -2]
+ end
+ yield(line)
+ end
+ conn.close
+ voidresp
+ end
+ end
+
+ #
+ # Puts the connection into binary (image) mode, issues the given server-side
+ # command (such as "STOR myfile"), and sends the contents of the file named
+ # +file+ to the server. If the optional block is given, it also passes it
+ # the data, in chunks of +blocksize+ characters.
+ #
+ def storbinary(cmd, file, blocksize, rest_offset = nil, &block) # :yield: data
+ if rest_offset
+ file.seek(rest_offset, IO::SEEK_SET)
+ end
+ synchronize do
+ voidcmd("TYPE I")
+ conn = transfercmd(cmd, rest_offset)
+ loop do
+ buf = file.read(blocksize)
+ break if buf == nil
+ conn.write(buf)
+ yield(buf) if block
+ end
+ conn.close
+ voidresp
+ end
+ end
+
+ #
+ # Puts the connection into ASCII (text) mode, issues the given server-side
+ # command (such as "STOR myfile"), and sends the contents of the file
+ # named +file+ to the server, one line at a time. If the optional block is
+ # given, it also passes it the lines.
+ #
+ def storlines(cmd, file, &block) # :yield: line
+ synchronize do
+ voidcmd("TYPE A")
+ conn = transfercmd(cmd)
+ loop do
+ buf = file.gets
+ break if buf == nil
+ if buf[-2, 2] != CRLF
+ buf = buf.chomp + CRLF
+ end
+ conn.write(buf)
+ yield(buf) if block
+ end
+ conn.close
+ voidresp
+ end
+ end
+
+ #
+ # Retrieves +remotefile+ in binary mode, storing the result in +localfile+.
+ # If a block is supplied, it is passed the retrieved data in +blocksize+
+ # chunks.
+ #
+ def getbinaryfile(remotefile, localfile = File.basename(remotefile),
+ blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data
+ if @resume
+ rest_offset = File.size?(localfile)
+ f = open(localfile, "a")
+ else
+ rest_offset = nil
+ f = open(localfile, "w")
+ end
+ begin
+ f.binmode
+ retrbinary("RETR " + remotefile, blocksize, rest_offset) do |data|
+ f.write(data)
+ yield(data) if block
+ end
+ ensure
+ f.close
+ end
+ end
+
+ #
+ # Retrieves +remotefile+ in ASCII (text) mode, storing the result in
+ # +localfile+. If a block is supplied, it is passed the retrieved data one
+ # line at a time.
+ #
+ def gettextfile(remotefile, localfile = File.basename(remotefile), &block) # :yield: line
+ f = open(localfile, "w")
+ begin
+ retrlines("RETR " + remotefile) do |line|
+ f.puts(line)
+ yield(line) if block
+ end
+ ensure
+ f.close
+ end
+ end
+
+ #
+ # Retrieves +remotefile+ in whatever mode the session is set (text or
+ # binary). See #gettextfile and #getbinaryfile.
+ #
+ def get(remotefile, localfile = File.basename(remotefile),
+ blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data
+ unless @binary
+ gettextfile(remotefile, localfile, &block)
+ else
+ getbinaryfile(remotefile, localfile, blocksize, &block)
+ end
+ end
+
+ #
+ # Transfers +localfile+ to the server in binary mode, storing the result in
+ # +remotefile+. If a block is supplied, calls it, passing in the transmitted
+ # data in +blocksize+ chunks.
+ #
+ def putbinaryfile(localfile, remotefile = File.basename(localfile),
+ blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data
+ if @resume
+ begin
+ rest_offset = size(remotefile)
+ rescue Net::FTPPermError
+ rest_offset = nil
+ end
+ else
+ rest_offset = nil
+ end
+ f = open(localfile)
+ begin
+ f.binmode
+ storbinary("STOR " + remotefile, f, blocksize, rest_offset, &block)
+ ensure
+ f.close
+ end
+ end
+
+ #
+ # Transfers +localfile+ to the server in ASCII (text) mode, storing the result
+ # in +remotefile+. If callback or an associated block is supplied, calls it,
+ # passing in the transmitted data one line at a time.
+ #
+ def puttextfile(localfile, remotefile = File.basename(localfile), &block) # :yield: line
+ f = open(localfile)
+ begin
+ storlines("STOR " + remotefile, f, &block)
+ ensure
+ f.close
+ end
+ end
+
+ #
+ # Transfers +localfile+ to the server in whatever mode the session is set
+ # (text or binary). See #puttextfile and #putbinaryfile.
+ #
+ def put(localfile, remotefile = File.basename(localfile),
+ blocksize = DEFAULT_BLOCKSIZE, &block)
+ unless @binary
+ puttextfile(localfile, remotefile, &block)
+ else
+ putbinaryfile(localfile, remotefile, blocksize, &block)
+ end
+ end
+
+ #
+ # Sends the ACCT command. TODO: more info.
+ #
+ def acct(account)
+ cmd = "ACCT " + account
+ voidcmd(cmd)
+ end
+
+ #
+ # Returns an array of filenames in the remote directory.
+ #
+ def nlst(dir = nil)
+ cmd = "NLST"
+ if dir
+ cmd = cmd + " " + dir
+ end
+ files = []
+ retrlines(cmd) do |line|
+ files.push(line)
+ end
+ return files
+ end
+
+ #
+ # Returns an array of file information in the directory (the output is like
+ # `ls -l`). If a block is given, it iterates through the listing.
+ #
+ def list(*args, &block) # :yield: line
+ cmd = "LIST"
+ args.each do |arg|
+ cmd = cmd + " " + arg
+ end
+ if block
+ retrlines(cmd, &block)
+ else
+ lines = []
+ retrlines(cmd) do |line|
+ lines << line
+ end
+ return lines
+ end
+ end
+ alias ls list
+ alias dir list
+
+ #
+ # Renames a file on the server.
+ #
+ def rename(fromname, toname)
+ resp = sendcmd("RNFR " + fromname)
+ if resp[0] != ?3
+ raise FTPReplyError, resp
+ end
+ voidcmd("RNTO " + toname)
+ end
+
+ #
+ # Deletes a file on the server.
+ #
+ def delete(filename)
+ resp = sendcmd("DELE " + filename)
+ if resp[0, 3] == "250"
+ return
+ elsif resp[0] == ?5
+ raise FTPPermError, resp
+ else
+ raise FTPReplyError, resp
+ end
+ end
+
+ #
+ # Changes the (remote) directory.
+ #
+ def chdir(dirname)
+ if dirname == ".."
+ begin
+ voidcmd("CDUP")
+ return
+ rescue FTPPermError
+ if $![0, 3] != "500"
+ raise FTPPermError, $!
+ end
+ end
+ end
+ cmd = "CWD " + dirname
+ voidcmd(cmd)
+ end
+
+ #
+ # Returns the size of the given (remote) filename.
+ #
+ def size(filename)
+ voidcmd("TYPE I")
+ resp = sendcmd("SIZE " + filename)
+ if resp[0, 3] != "213"
+ raise FTPReplyError, resp
+ end
+ return resp[3..-1].strip.to_i
+ end
+
+ MDTM_REGEXP = /^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/ # :nodoc:
+
+ #
+ # Returns the last modification time of the (remote) file. If +local+ is
+ # +true+, it is returned as a local time, otherwise it's a UTC time.
+ #
+ def mtime(filename, local = false)
+ str = mdtm(filename)
+ ary = str.scan(MDTM_REGEXP)[0].collect {|i| i.to_i}
+ return local ? Time.local(*ary) : Time.gm(*ary)
+ end
+
+ #
+ # Creates a remote directory.
+ #
+ def mkdir(dirname)
+ resp = sendcmd("MKD " + dirname)
+ return parse257(resp)
+ end
+
+ #
+ # Removes a remote directory.
+ #
+ def rmdir(dirname)
+ voidcmd("RMD " + dirname)
+ end
+
+ #
+ # Returns the current remote directory.
+ #
+ def pwd
+ resp = sendcmd("PWD")
+ return parse257(resp)
+ end
+ alias getdir pwd
+
+ #
+ # Returns system information.
+ #
+ def system
+ resp = sendcmd("SYST")
+ if resp[0, 3] != "215"
+ raise FTPReplyError, resp
+ end
+ return resp[4 .. -1]
+ end
+
+ #
+ # Aborts the previous command (ABOR command).
+ #
+ def abort
+ line = "ABOR" + CRLF
+ print "put: ABOR\n" if @debug_mode
+ @sock.send(line, Socket::MSG_OOB)
+ resp = getmultiline
+ unless ["426", "226", "225"].include?(resp[0, 3])
+ raise FTPProtoError, resp
+ end
+ return resp
+ end
+
+ #
+ # Returns the status (STAT command).
+ #
+ def status
+ line = "STAT" + CRLF
+ print "put: STAT\n" if @debug_mode
+ @sock.send(line, Socket::MSG_OOB)
+ return getresp
+ end
+
+ #
+ # Issues the MDTM command. TODO: more info.
+ #
+ def mdtm(filename)
+ resp = sendcmd("MDTM " + filename)
+ if resp[0, 3] == "213"
+ return resp[3 .. -1].strip
+ end
+ end
+
+ #
+ # Issues the HELP command.
+ #
+ def help(arg = nil)
+ cmd = "HELP"
+ if arg
+ cmd = cmd + " " + arg
+ end
+ sendcmd(cmd)
+ end
+
+ #
+ # Exits the FTP session.
+ #
+ def quit
+ voidcmd("QUIT")
+ end
+
+ #
+ # Issues a NOOP command.
+ #
+ def noop
+ voidcmd("NOOP")
+ end
+
+ #
+ # Issues a SITE command.
+ #
+ def site(arg)
+ cmd = "SITE " + arg
+ voidcmd(cmd)
+ end
+
+ #
+ # Closes the connection. Further operations are impossible until you open
+ # a new connection with #connect.
+ #
+ def close
+ @sock.close if @sock and not @sock.closed?
+ end
+
+ #
+ # Returns +true+ iff the connection is closed.
+ #
+ def closed?
+ @sock == nil or @sock.closed?
+ end
+
+ def parse227(resp)
+ if resp[0, 3] != "227"
+ raise FTPReplyError, resp
+ end
+ left = resp.index("(")
+ right = resp.index(")")
+ if left == nil or right == nil
+ raise FTPProtoError, resp
+ end
+ numbers = resp[left + 1 .. right - 1].split(",")
+ if numbers.length != 6
+ raise FTPProtoError, resp
+ end
+ host = numbers[0, 4].join(".")
+ port = (numbers[4].to_i << 8) + numbers[5].to_i
+ return host, port
+ end
+ private :parse227
+
+ def parse228(resp)
+ if resp[0, 3] != "228"
+ raise FTPReplyError, resp
+ end
+ left = resp.index("(")
+ right = resp.index(")")
+ if left == nil or right == nil
+ raise FTPProtoError, resp
+ end
+ numbers = resp[left + 1 .. right - 1].split(",")
+ if numbers[0] == "4"
+ if numbers.length != 9 || numbers[1] != "4" || numbers[2 + 4] != "2"
+ raise FTPProtoError, resp
+ end
+ host = numbers[2, 4].join(".")
+ port = (numbers[7].to_i << 8) + numbers[8].to_i
+ elsif numbers[0] == "6"
+ if numbers.length != 21 || numbers[1] != "16" || numbers[2 + 16] != "2"
+ raise FTPProtoError, resp
+ end
+ v6 = ["", "", "", "", "", "", "", ""]
+ for i in 0 .. 7
+ v6[i] = sprintf("%02x%02x", numbers[(i * 2) + 2].to_i,
+ numbers[(i * 2) + 3].to_i)
+ end
+ host = v6[0, 8].join(":")
+ port = (numbers[19].to_i << 8) + numbers[20].to_i
+ end
+ return host, port
+ end
+ private :parse228
+
+ def parse229(resp)
+ if resp[0, 3] != "229"
+ raise FTPReplyError, resp
+ end
+ left = resp.index("(")
+ right = resp.index(")")
+ if left == nil or right == nil
+ raise FTPProtoError, resp
+ end
+ numbers = resp[left + 1 .. right - 1].split(resp[left + 1, 1])
+ if numbers.length != 4
+ raise FTPProtoError, resp
+ end
+ port = numbers[3].to_i
+ host = (@sock.peeraddr())[3]
+ return host, port
+ end
+ private :parse229
+
+ def parse257(resp)
+ if resp[0, 3] != "257"
+ raise FTPReplyError, resp
+ end
+ if resp[3, 2] != ' "'
+ return ""
+ end
+ dirname = ""
+ i = 5
+ n = resp.length
+ while i < n
+ c = resp[i, 1]
+ i = i + 1
+ if c == '"'
+ if i > n or resp[i, 1] != '"'
+ break
+ end
+ i = i + 1
+ end
+ dirname = dirname + c
+ end
+ return dirname
+ end
+ private :parse257
+ end
+
+end
+
+
+# Documentation comments:
+# - sourced from pickaxe and nutshell, with improvements (hopefully)
+# - three methods should be private (search WRITEME)
+# - two methods need more information (search TODO)
diff --git a/ruby_1_8_5/lib/net/http.rb b/ruby_1_8_5/lib/net/http.rb
new file mode 100644
index 0000000000..b62f97d374
--- /dev/null
+++ b/ruby_1_8_5/lib/net/http.rb
@@ -0,0 +1,2274 @@
+#
+# = net/http.rb
+#
+# Copyright (c) 1999-2006 Yukihiro Matsumoto
+# Copyright (c) 1999-2006 Minero Aoki
+# Copyright (c) 2001 GOTOU Yuuzou
+#
+# Written and maintained by Minero Aoki <aamine@loveruby.net>.
+# HTTPS support added by GOTOU Yuuzou <gotoyuzo@notwork.org>.
+#
+# This file is derived from "http-access.rb".
+#
+# Documented by Minero Aoki; converted to RDoc by William Webber.
+#
+# This program is free software. You can re-distribute and/or
+# modify this program under the same terms of ruby itself ---
+# Ruby Distribution License or GNU General Public License.
+#
+# See Net::HTTP for an overview and examples.
+#
+# NOTE: You can find Japanese version of this document here:
+# http://www.ruby-lang.org/ja/man/?cmd=view;name=net%2Fhttp.rb
+#
+#--
+# $Id: http.rb,v 1.100.2.14 2006/07/26 13:27:18 aamine Exp $
+#++
+
+require 'net/protocol'
+require 'uri'
+
+module Net #:nodoc:
+
+ # :stopdoc:
+ class HTTPBadResponse < StandardError; end
+ class HTTPHeaderSyntaxError < StandardError; end
+ # :startdoc:
+
+ # == What Is This Library?
+ #
+ # This library provides your program functions to access WWW
+ # documents via HTTP, Hyper Text Transfer Protocol version 1.1.
+ # For details of HTTP, refer [RFC2616]
+ # (http://www.ietf.org/rfc/rfc2616.txt).
+ #
+ # == Examples
+ #
+ # === Getting Document From WWW Server
+ #
+ # Example #1: Simple GET+print
+ #
+ # require 'net/http'
+ # Net::HTTP.get_print 'www.example.com', '/index.html'
+ #
+ # Example #2: Simple GET+print by URL
+ #
+ # require 'net/http'
+ # require 'uri'
+ # Net::HTTP.get_print URI.parse('http://www.example.com/index.html')
+ #
+ # Example #3: More generic GET+print
+ #
+ # require 'net/http'
+ # require 'uri'
+ #
+ # url = URI.parse('http://www.example.com/index.html')
+ # res = Net::HTTP.start(url.host, url.port) {|http|
+ # http.get('/index.html')
+ # }
+ # puts res.body
+ #
+ # Example #4: More generic GET+print
+ #
+ # require 'net/http'
+ #
+ # url = URI.parse('http://www.example.com/index.html')
+ # req = Net::HTTP::Get.new(url.path)
+ # res = Net::HTTP.start(url.host, url.port) {|http|
+ # http.request(req)
+ # }
+ # puts res.body
+ #
+ # === Posting Form Data
+ #
+ # require 'net/http'
+ # require 'uri'
+ #
+ # #1: Simple POST
+ # res = Net::HTTP.post_form(URI.parse('http://www.example.com/search.cgi'),
+ # {'q'=>'ruby', 'max'=>'50'})
+ # puts res.body
+ #
+ # #2: POST with basic authentication
+ # res = Net::HTTP.post_form(URI.parse('http://jack:pass@www.example.com/todo.cgi'),
+ # {'from'=>'2005-01-01', 'to'=>'2005-03-31'})
+ # puts res.body
+ #
+ # #3: Detailed control
+ # url = URI.parse('http://www.example.com/todo.cgi')
+ # req = Net::HTTP::Post.new(url.path)
+ # req.basic_auth 'jack', 'pass'
+ # req.set_form_data({'from'=>'2005-01-01', 'to'=>'2005-03-31'}, ';')
+ # res = Net::HTTP.new(url.host, url.port).start {|http| http.request(req) }
+ # case res
+ # when Net::HTTPSuccess, Net::HTTPRedirection
+ # # OK
+ # else
+ # res.error!
+ # end
+ #
+ # === Accessing via Proxy
+ #
+ # Net::HTTP.Proxy creates http proxy class. It has same
+ # methods of Net::HTTP but its instances always connect to
+ # proxy, instead of given host.
+ #
+ # require 'net/http'
+ #
+ # proxy_addr = 'your.proxy.host'
+ # proxy_port = 8080
+ # :
+ # Net::HTTP::Proxy(proxy_addr, proxy_port).start('www.example.com') {|http|
+ # # always connect to your.proxy.addr:8080
+ # :
+ # }
+ #
+ # Since Net::HTTP.Proxy returns Net::HTTP itself when proxy_addr is nil,
+ # there's no need to change code if there's proxy or not.
+ #
+ # There are two additional parameters in Net::HTTP.Proxy which allow to
+ # specify proxy user name and password:
+ #
+ # Net::HTTP::Proxy(proxy_addr, proxy_port, proxy_user = nil, proxy_pass = nil)
+ #
+ # You may use them to work with authorization-enabled proxies:
+ #
+ # require 'net/http'
+ # require 'uri'
+ #
+ # proxy_host = 'your.proxy.host'
+ # proxy_port = 8080
+ # uri = URI.parse(ENV['http_proxy'])
+ # proxy_user, proxy_pass = uri.userinfo.split(/:/) if uri.userinfo
+ # Net::HTTP::Proxy(proxy_host, proxy_port,
+ # proxy_user, proxy_pass).start('www.example.com') {|http|
+ # # always connect to your.proxy.addr:8080 using specified username and password
+ # :
+ # }
+ #
+ # Note that net/http never rely on HTTP_PROXY environment variable.
+ # If you want to use proxy, set it explicitly.
+ #
+ # === Following Redirection
+ #
+ # require 'net/http'
+ # require 'uri'
+ #
+ # def fetch(uri_str, limit = 10)
+ # # You should choose better exception.
+ # raise ArgumentError, 'HTTP redirect too deep' if limit == 0
+ #
+ # response = Net::HTTP.get_response(URI.parse(uri_str))
+ # case response
+ # when Net::HTTPSuccess then response
+ # when Net::HTTPRedirection then fetch(response['location'], limit - 1)
+ # else
+ # response.error!
+ # end
+ # end
+ #
+ # print fetch('http://www.ruby-lang.org')
+ #
+ # Net::HTTPSuccess and Net::HTTPRedirection is a HTTPResponse class.
+ # All HTTPResponse objects belong to its own response class which
+ # indicate HTTP result status. For details of response classes,
+ # see section "HTTP Response Classes".
+ #
+ # === Basic Authentication
+ #
+ # require 'net/http'
+ #
+ # Net::HTTP.start('www.example.com') {|http|
+ # req = Net::HTTP::Get.new('/secret-page.html')
+ # req.basic_auth 'account', 'password'
+ # response = http.request(req)
+ # print response.body
+ # }
+ #
+ # === HTTP Request Classes
+ #
+ # Here is HTTP request class hierarchy.
+ #
+ # Net::HTTPRequest
+ # Net::HTTP::Get
+ # Net::HTTP::Head
+ # Net::HTTP::Post
+ # Net::HTTP::Put
+ # Net::HTTP::Proppatch
+ # Net::HTTP::Lock
+ # Net::HTTP::Unlock
+ # Net::HTTP::Options
+ # Net::HTTP::Propfind
+ # Net::HTTP::Delete
+ # Net::HTTP::Move
+ # Net::HTTP::Copy
+ # Net::HTTP::Mkcol
+ # Net::HTTP::Trace
+ #
+ # === HTTP Response Classes
+ #
+ # Here is HTTP response class hierarchy.
+ # All classes are defined in Net module.
+ #
+ # HTTPResponse
+ # HTTPUnknownResponse
+ # HTTPInformation # 1xx
+ # HTTPContinue # 100
+ # HTTPSwitchProtocl # 101
+ # HTTPSuccess # 2xx
+ # HTTPOK # 200
+ # HTTPCreated # 201
+ # HTTPAccepted # 202
+ # HTTPNonAuthoritativeInformation # 203
+ # HTTPNoContent # 204
+ # HTTPResetContent # 205
+ # HTTPPartialContent # 206
+ # HTTPRedirection # 3xx
+ # HTTPMultipleChoice # 300
+ # HTTPMovedPermanently # 301
+ # HTTPFound # 302
+ # HTTPSeeOther # 303
+ # HTTPNotModified # 304
+ # HTTPUseProxy # 305
+ # HTTPTemporaryRedirect # 307
+ # HTTPClientError # 4xx
+ # HTTPBadRequest # 400
+ # HTTPUnauthorized # 401
+ # HTTPPaymentRequired # 402
+ # HTTPForbidden # 403
+ # HTTPNotFound # 404
+ # HTTPMethodNotAllowed # 405
+ # HTTPNotAcceptable # 406
+ # HTTPProxyAuthenticationRequired # 407
+ # HTTPRequestTimeOut # 408
+ # HTTPConflict # 409
+ # HTTPGone # 410
+ # HTTPLengthRequired # 411
+ # HTTPPreconditionFailed # 412
+ # HTTPRequestEntityTooLarge # 413
+ # HTTPRequestURITooLong # 414
+ # HTTPUnsupportedMediaType # 415
+ # HTTPRequestedRangeNotSatisfiable # 416
+ # HTTPExpectationFailed # 417
+ # HTTPServerError # 5xx
+ # HTTPInternalServerError # 500
+ # HTTPNotImplemented # 501
+ # HTTPBadGateway # 502
+ # HTTPServiceUnavailable # 503
+ # HTTPGatewayTimeOut # 504
+ # HTTPVersionNotSupported # 505
+ #
+ # == Switching Net::HTTP versions
+ #
+ # You can use net/http.rb 1.1 features (bundled with Ruby 1.6)
+ # by calling HTTP.version_1_1. Calling Net::HTTP.version_1_2
+ # allows you to use 1.2 features again.
+ #
+ # # example
+ # Net::HTTP.start {|http1| ...(http1 has 1.2 features)... }
+ #
+ # Net::HTTP.version_1_1
+ # Net::HTTP.start {|http2| ...(http2 has 1.1 features)... }
+ #
+ # Net::HTTP.version_1_2
+ # Net::HTTP.start {|http3| ...(http3 has 1.2 features)... }
+ #
+ # This function is NOT thread-safe.
+ #
+ class HTTP < Protocol
+
+ # :stopdoc:
+ Revision = %q$Revision: 1.100.2.14 $.split[1]
+ HTTPVersion = '1.1'
+ @newimpl = true
+ # :startdoc:
+
+ # Turns on net/http 1.2 (ruby 1.8) features.
+ # Defaults to ON in ruby 1.8.
+ #
+ # I strongly recommend to call this method always.
+ #
+ # require 'net/http'
+ # Net::HTTP.version_1_2
+ #
+ def HTTP.version_1_2
+ @newimpl = true
+ end
+
+ # Turns on net/http 1.1 (ruby 1.6) features.
+ # Defaults to OFF in ruby 1.8.
+ def HTTP.version_1_1
+ @newimpl = false
+ end
+
+ # true if net/http is in version 1.2 mode.
+ # Defaults to true.
+ def HTTP.version_1_2?
+ @newimpl
+ end
+
+ # true if net/http is in version 1.1 compatible mode.
+ # Defaults to true.
+ def HTTP.version_1_1?
+ not @newimpl
+ end
+
+ class << HTTP
+ alias is_version_1_1? version_1_1? #:nodoc:
+ alias is_version_1_2? version_1_2? #:nodoc:
+ end
+
+ #
+ # short cut methods
+ #
+
+ #
+ # Get body from target and output it to +$stdout+. The
+ # target can either be specified as (+uri+), or as
+ # (+host+, +path+, +port+ = 80); so:
+ #
+ # Net::HTTP.get_print URI.parse('http://www.example.com/index.html')
+ #
+ # or:
+ #
+ # Net::HTTP.get_print 'www.example.com', '/index.html'
+ #
+ def HTTP.get_print(uri_or_host, path = nil, port = nil)
+ get_response(uri_or_host, path, port) {|res|
+ res.read_body do |chunk|
+ $stdout.print chunk
+ end
+ }
+ nil
+ end
+
+ # Send a GET request to the target and return the response
+ # as a string. The target can either be specified as
+ # (+uri+), or as (+host+, +path+, +port+ = 80); so:
+ #
+ # print Net::HTTP.get(URI.parse('http://www.example.com/index.html'))
+ #
+ # or:
+ #
+ # print Net::HTTP.get('www.example.com', '/index.html')
+ #
+ def HTTP.get(uri_or_host, path = nil, port = nil)
+ get_response(uri_or_host, path, port).body
+ end
+
+ # Send a GET request to the target and return the response
+ # as a Net::HTTPResponse object. The target can either be specified as
+ # (+uri+), or as (+host+, +path+, +port+ = 80); so:
+ #
+ # res = Net::HTTP.get_response(URI.parse('http://www.example.com/index.html'))
+ # print res.body
+ #
+ # or:
+ #
+ # res = Net::HTTP.get_response('www.example.com', '/index.html')
+ # print res.body
+ #
+ def HTTP.get_response(uri_or_host, path = nil, port = nil, &block)
+ if path
+ host = uri_or_host
+ new(host, port || HTTP.default_port).start {|http|
+ return http.request_get(path, &block)
+ }
+ else
+ uri = uri_or_host
+ new(uri.host, uri.port).start {|http|
+ return http.request_get(uri.request_uri, &block)
+ }
+ end
+ end
+
+ # Posts HTML form data to the +URL+.
+ # Form data must be represented as a Hash of String to String, e.g:
+ #
+ # { "cmd" => "search", "q" => "ruby", "max" => "50" }
+ #
+ # This method also does Basic Authentication iff +URL+.user exists.
+ #
+ # Example:
+ #
+ # require 'net/http'
+ # require 'uri'
+ #
+ # HTTP.post_form URI.parse('http://www.example.com/search.cgi'),
+ # { "q" => "ruby", "max" => "50" }
+ #
+ def HTTP.post_form(url, params)
+ req = Post.new(url.path)
+ req.form_data = params
+ req.basic_auth url.user, url.password if url.user
+ new(url.host, url.port).start {|http|
+ http.request(req)
+ }
+ end
+
+ #
+ # HTTP session management
+ #
+
+ # The default port to use for HTTP requests; defaults to 80.
+ def HTTP.default_port
+ http_default_port()
+ end
+
+ # The default port to use for HTTP requests; defaults to 80.
+ def HTTP.http_default_port
+ 80
+ end
+
+ # The default port to use for HTTPS requests; defaults to 443.
+ def HTTP.https_default_port
+ 443
+ end
+
+ def HTTP.socket_type #:nodoc: obsolete
+ BufferedIO
+ end
+
+ # creates a new Net::HTTP object and opens its TCP connection and
+ # HTTP session. If the optional block is given, the newly
+ # created Net::HTTP object is passed to it and closed when the
+ # block finishes. In this case, the return value of this method
+ # is the return value of the block. If no block is given, the
+ # return value of this method is the newly created Net::HTTP object
+ # itself, and the caller is responsible for closing it upon completion.
+ def HTTP.start(address, port = nil, p_addr = nil, p_port = nil, p_user = nil, p_pass = nil, &block) # :yield: +http+
+ new(address, port, p_addr, p_port, p_user, p_pass).start(&block)
+ end
+
+ class << HTTP
+ alias newobj new
+ end
+
+ # Creates a new Net::HTTP object.
+ # If +proxy_addr+ is given, creates an Net::HTTP object with proxy support.
+ # This method does not open the TCP connection.
+ def HTTP.new(address, port = nil, p_addr = nil, p_port = nil, p_user = nil, p_pass = nil)
+ h = Proxy(p_addr, p_port, p_user, p_pass).newobj(address, port)
+ h.instance_eval {
+ @newimpl = ::Net::HTTP.version_1_2?
+ }
+ h
+ end
+
+ # Creates a new Net::HTTP object for the specified +address+.
+ # This method does not open the TCP connection.
+ def initialize(address, port = nil)
+ @address = address
+ @port = (port || HTTP.default_port)
+ @curr_http_version = HTTPVersion
+ @seems_1_0_server = false
+ @close_on_empty_response = false
+ @socket = nil
+ @started = false
+ @open_timeout = nil
+ @read_timeout = 60
+ @debug_output = nil
+ @use_ssl = false
+ @ssl_context = nil
+ end
+
+ def inspect
+ "#<#{self.class} #{@address}:#{@port} open=#{started?}>"
+ end
+
+ # *WARNING* This method causes serious security hole.
+ # Never use this method in production code.
+ #
+ # Set an output stream for debugging.
+ #
+ # http = Net::HTTP.new
+ # http.set_debug_output $stderr
+ # http.start { .... }
+ #
+ def set_debug_output(output)
+ warn 'Net::HTTP#set_debug_output called after HTTP started' if started?
+ @debug_output = output
+ end
+
+ # The host name to connect to.
+ attr_reader :address
+
+ # The port number to connect to.
+ attr_reader :port
+
+ # Seconds to wait until connection is opened.
+ # If the HTTP object cannot open a connection in this many seconds,
+ # it raises a TimeoutError exception.
+ attr_accessor :open_timeout
+
+ # Seconds to wait until reading one block (by one read(2) call).
+ # If the HTTP object cannot open a connection in this many seconds,
+ # it raises a TimeoutError exception.
+ attr_reader :read_timeout
+
+ # Setter for the read_timeout attribute.
+ def read_timeout=(sec)
+ @socket.read_timeout = sec if @socket
+ @read_timeout = sec
+ end
+
+ # returns true if the HTTP session is started.
+ def started?
+ @started
+ end
+
+ alias active? started? #:nodoc: obsolete
+
+ attr_accessor :close_on_empty_response
+
+ # returns true if use SSL/TLS with HTTP.
+ def use_ssl?
+ false # redefined in net/https
+ end
+
+ # Opens TCP connection and HTTP session.
+ #
+ # When this method is called with block, gives a HTTP object
+ # to the block and closes the TCP connection / HTTP session
+ # after the block executed.
+ #
+ # When called with a block, returns the return value of the
+ # block; otherwise, returns self.
+ #
+ def start # :yield: http
+ raise IOError, 'HTTP session already opened' if @started
+ if block_given?
+ begin
+ do_start
+ return yield(self)
+ ensure
+ do_finish
+ end
+ end
+ do_start
+ self
+ end
+
+ def do_start
+ connect
+ @started = true
+ end
+ private :do_start
+
+ def connect
+ D "opening connection to #{conn_address()}..."
+ s = timeout(@open_timeout) { TCPSocket.open(conn_address(), conn_port()) }
+ D "opened"
+ if use_ssl?
+ unless @ssl_context.verify_mode
+ warn "warning: peer certificate won't be verified in this SSL session"
+ @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ end
+ s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
+ s.sync_close = true
+ end
+ @socket = BufferedIO.new(s)
+ @socket.read_timeout = @read_timeout
+ @socket.debug_output = @debug_output
+ if use_ssl?
+ if proxy?
+ @socket.writeline sprintf('CONNECT %s:%s HTTP/%s',
+ @address, @port, HTTPVersion)
+ @socket.writeline "Host: #{@address}:#{@port}"
+ if proxy_user
+ credential = ["#{proxy_user}:#{proxy_pass}"].pack('m')
+ credential.delete!("\r\n")
+ @socket.writeline "Proxy-Authorization: Basic #{credential}"
+ end
+ @socket.writeline ''
+ HTTPResponse.read_new(@socket).value
+ end
+ s.connect
+ end
+ on_connect
+ end
+ private :connect
+
+ def on_connect
+ end
+ private :on_connect
+
+ # Finishes HTTP session and closes TCP connection.
+ # Raises IOError if not started.
+ def finish
+ raise IOError, 'HTTP session not yet started' unless started?
+ do_finish
+ end
+
+ def do_finish
+ @started = false
+ @socket.close if @socket and not @socket.closed?
+ @socket = nil
+ end
+ private :do_finish
+
+ #
+ # proxy
+ #
+
+ public
+
+ # no proxy
+ @is_proxy_class = false
+ @proxy_addr = nil
+ @proxy_port = nil
+ @proxy_user = nil
+ @proxy_pass = nil
+
+ # Creates an HTTP proxy class.
+ # Arguments are address/port of proxy host and username/password
+ # if authorization on proxy server is required.
+ # You can replace the HTTP class with created proxy class.
+ #
+ # If ADDRESS is nil, this method returns self (Net::HTTP).
+ #
+ # # Example
+ # proxy_class = Net::HTTP::Proxy('proxy.example.com', 8080)
+ # :
+ # proxy_class.start('www.ruby-lang.org') {|http|
+ # # connecting proxy.foo.org:8080
+ # :
+ # }
+ #
+ def HTTP.Proxy(p_addr, p_port = nil, p_user = nil, p_pass = nil)
+ return self unless p_addr
+ delta = ProxyDelta
+ proxyclass = Class.new(self)
+ proxyclass.module_eval {
+ include delta
+ # with proxy
+ @is_proxy_class = true
+ @proxy_address = p_addr
+ @proxy_port = p_port || default_port()
+ @proxy_user = p_user
+ @proxy_pass = p_pass
+ }
+ proxyclass
+ end
+
+ class << HTTP
+ # returns true if self is a class which was created by HTTP::Proxy.
+ def proxy_class?
+ @is_proxy_class
+ end
+
+ attr_reader :proxy_address
+ attr_reader :proxy_port
+ attr_reader :proxy_user
+ attr_reader :proxy_pass
+ end
+
+ # True if self is a HTTP proxy class.
+ def proxy?
+ self.class.proxy_class?
+ end
+
+ # Address of proxy host. If self does not use a proxy, nil.
+ def proxy_address
+ self.class.proxy_address
+ end
+
+ # Port number of proxy host. If self does not use a proxy, nil.
+ def proxy_port
+ self.class.proxy_port
+ end
+
+ # User name for accessing proxy. If self does not use a proxy, nil.
+ def proxy_user
+ self.class.proxy_user
+ end
+
+ # User password for accessing proxy. If self does not use a proxy, nil.
+ def proxy_pass
+ self.class.proxy_pass
+ end
+
+ alias proxyaddr proxy_address #:nodoc: obsolete
+ alias proxyport proxy_port #:nodoc: obsolete
+
+ private
+
+ # without proxy
+
+ def conn_address
+ address()
+ end
+
+ def conn_port
+ port()
+ end
+
+ def edit_path(path)
+ path
+ end
+
+ module ProxyDelta #:nodoc: internal use only
+ private
+
+ def conn_address
+ proxy_address()
+ end
+
+ def conn_port
+ proxy_port()
+ end
+
+ def edit_path(path)
+ use_ssl? ? path : "http://#{addr_port()}#{path}"
+ end
+ end
+
+ #
+ # HTTP operations
+ #
+
+ public
+
+ # Gets data from +path+ on the connected-to host.
+ # +header+ must be a Hash like { 'Accept' => '*/*', ... }.
+ #
+ # In version 1.1 (ruby 1.6), this method returns a pair of objects,
+ # a Net::HTTPResponse object and the entity body string.
+ # In version 1.2 (ruby 1.8), this method returns a Net::HTTPResponse
+ # object.
+ #
+ # If called with a block, yields each fragment of the
+ # entity body in turn as a string as it is read from
+ # the socket. Note that in this case, the returned response
+ # object will *not* contain a (meaningful) body.
+ #
+ # +dest+ argument is obsolete.
+ # It still works but you must not use it.
+ #
+ # In version 1.1, this method might raise an exception for
+ # 3xx (redirect). In this case you can get a HTTPResponse object
+ # by "anException.response".
+ #
+ # In version 1.2, this method never raises exception.
+ #
+ # # version 1.1 (bundled with Ruby 1.6)
+ # response, body = http.get('/index.html')
+ #
+ # # version 1.2 (bundled with Ruby 1.8 or later)
+ # response = http.get('/index.html')
+ #
+ # # using block
+ # File.open('result.txt', 'w') {|f|
+ # http.get('/~foo/') do |str|
+ # f.write str
+ # end
+ # }
+ #
+ def get(path, initheader = nil, dest = nil, &block) # :yield: +body_segment+
+ res = nil
+ request(Get.new(path, initheader)) {|r|
+ r.read_body dest, &block
+ res = r
+ }
+ unless @newimpl
+ res.value
+ return res, res.body
+ end
+
+ res
+ end
+
+ # Gets only the header from +path+ on the connected-to host.
+ # +header+ is a Hash like { 'Accept' => '*/*', ... }.
+ #
+ # This method returns a Net::HTTPResponse object.
+ #
+ # In version 1.1, this method might raise an exception for
+ # 3xx (redirect). On the case you can get a HTTPResponse object
+ # by "anException.response".
+ # In version 1.2, this method never raises an exception.
+ #
+ # response = nil
+ # Net::HTTP.start('some.www.server', 80) {|http|
+ # response = http.head('/index.html')
+ # }
+ # p response['content-type']
+ #
+ def head(path, initheader = nil)
+ res = request(Head.new(path, initheader))
+ res.value unless @newimpl
+ res
+ end
+
+ # Posts +data+ (must be a String) to +path+. +header+ must be a Hash
+ # like { 'Accept' => '*/*', ... }.
+ #
+ # In version 1.1 (ruby 1.6), this method returns a pair of objects, a
+ # Net::HTTPResponse object and an entity body string.
+ # In version 1.2 (ruby 1.8), this method returns a Net::HTTPResponse object.
+ #
+ # If called with a block, yields each fragment of the
+ # entity body in turn as a string as it are read from
+ # the socket. Note that in this case, the returned response
+ # object will *not* contain a (meaningful) body.
+ #
+ # +dest+ argument is obsolete.
+ # It still works but you must not use it.
+ #
+ # In version 1.1, this method might raise an exception for
+ # 3xx (redirect). In this case you can get an HTTPResponse object
+ # by "anException.response".
+ # In version 1.2, this method never raises exception.
+ #
+ # # version 1.1
+ # response, body = http.post('/cgi-bin/search.rb', 'query=foo')
+ #
+ # # version 1.2
+ # response = http.post('/cgi-bin/search.rb', 'query=foo')
+ #
+ # # using block
+ # File.open('result.txt', 'w') {|f|
+ # http.post('/cgi-bin/search.rb', 'query=foo') do |str|
+ # f.write str
+ # end
+ # }
+ #
+ # You should set Content-Type: header field for POST.
+ # If no Content-Type: field given, this method uses
+ # "application/x-www-form-urlencoded" by default.
+ #
+ def post(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+
+ res = nil
+ request(Post.new(path, initheader), data) {|r|
+ r.read_body dest, &block
+ res = r
+ }
+ unless @newimpl
+ res.value
+ return res, res.body
+ end
+ res
+ end
+
+ def put(path, data, initheader = nil) #:nodoc:
+ res = request(Put.new(path, initheader), data)
+ res.value unless @newimpl
+ res
+ end
+
+ # Sends a PROPPATCH request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def proppatch(path, body, initheader = nil)
+ request(Proppatch.new(path, initheader), body)
+ end
+
+ # Sends a LOCK request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def lock(path, body, initheader = nil)
+ request(Lock.new(path, initheader), body)
+ end
+
+ # Sends a UNLOCK request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def unlock(path, body, initheader = nil)
+ request(Unlock.new(path, initheader), body)
+ end
+
+ # Sends a OPTIONS request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def options(path, initheader = nil)
+ request(Options.new(path, initheader))
+ end
+
+ # Sends a PROPFIND request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def propfind(path, body = nil, initheader = {'Depth' => '0'})
+ request(Propfind.new(path, initheader), body)
+ end
+
+ # Sends a DELETE request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def delete(path, initheader = {'Depth' => 'Infinity'})
+ request(Delete.new(path, initheader))
+ end
+
+ # Sends a MOVE request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def move(path, initheader = nil)
+ request(Move.new(path, initheader))
+ end
+
+ # Sends a COPY request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def copy(path, initheader = nil)
+ request(Copy.new(path, initheader))
+ end
+
+ # Sends a MKCOL request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def mkcol(path, body = nil, initheader = nil)
+ request(Mkcol.new(path, initheader), body)
+ end
+
+ # Sends a TRACE request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ def trace(path, initheader = nil)
+ request(Trace.new(path, initheader))
+ end
+
+ # Sends a GET request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ #
+ # When called with a block, yields an HTTPResponse object.
+ # The body of this response will not have been read yet;
+ # the caller can process it using HTTPResponse#read_body,
+ # if desired.
+ #
+ # Returns the response.
+ #
+ # This method never raises Net::* exceptions.
+ #
+ # response = http.request_get('/index.html')
+ # # The entity body is already read here.
+ # p response['content-type']
+ # puts response.body
+ #
+ # # using block
+ # http.request_get('/index.html') {|response|
+ # p response['content-type']
+ # response.read_body do |str| # read body now
+ # print str
+ # end
+ # }
+ #
+ def request_get(path, initheader = nil, &block) # :yield: +response+
+ request(Get.new(path, initheader), &block)
+ end
+
+ # Sends a HEAD request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ #
+ # Returns the response.
+ #
+ # This method never raises Net::* exceptions.
+ #
+ # response = http.request_head('/index.html')
+ # p response['content-type']
+ #
+ def request_head(path, initheader = nil, &block)
+ request(Head.new(path, initheader), &block)
+ end
+
+ # Sends a POST request to the +path+ and gets a response,
+ # as an HTTPResponse object.
+ #
+ # When called with a block, yields an HTTPResponse object.
+ # The body of this response will not have been read yet;
+ # the caller can process it using HTTPResponse#read_body,
+ # if desired.
+ #
+ # Returns the response.
+ #
+ # This method never raises Net::* exceptions.
+ #
+ # # example
+ # response = http.request_post('/cgi-bin/nice.rb', 'datadatadata...')
+ # p response.status
+ # puts response.body # body is already read
+ #
+ # # using block
+ # http.request_post('/cgi-bin/nice.rb', 'datadatadata...') {|response|
+ # p response.status
+ # p response['content-type']
+ # response.read_body do |str| # read body now
+ # print str
+ # end
+ # }
+ #
+ def request_post(path, data, initheader = nil, &block) # :yield: +response+
+ request Post.new(path, initheader), data, &block
+ end
+
+ def request_put(path, data, initheader = nil, &block) #:nodoc:
+ request Put.new(path, initheader), data, &block
+ end
+
+ alias get2 request_get #:nodoc: obsolete
+ alias head2 request_head #:nodoc: obsolete
+ alias post2 request_post #:nodoc: obsolete
+ alias put2 request_put #:nodoc: obsolete
+
+
+ # Sends an HTTP request to the HTTP server.
+ # This method also sends DATA string if DATA is given.
+ #
+ # Returns a HTTPResponse object.
+ #
+ # This method never raises Net::* exceptions.
+ #
+ # response = http.send_request('GET', '/index.html')
+ # puts response.body
+ #
+ def send_request(name, path, data = nil, header = nil)
+ r = HTTPGenericRequest.new(name,(data ? true : false),true,path,header)
+ request r, data
+ end
+
+ # Sends an HTTPRequest object REQUEST to the HTTP server.
+ # This method also sends DATA string if REQUEST is a post/put request.
+ # Giving DATA for get/head request causes ArgumentError.
+ #
+ # When called with a block, yields an HTTPResponse object.
+ # The body of this response will not have been read yet;
+ # the caller can process it using HTTPResponse#read_body,
+ # if desired.
+ #
+ # Returns a HTTPResponse object.
+ #
+ # This method never raises Net::* exceptions.
+ #
+ def request(req, body = nil, &block) # :yield: +response+
+ unless started?
+ start {
+ req['connection'] ||= 'close'
+ return request(req, body, &block)
+ }
+ end
+ if proxy_user()
+ unless use_ssl?
+ req.proxy_basic_auth proxy_user(), proxy_pass()
+ end
+ end
+
+ req.set_body_internal body
+ begin_transport req
+ req.exec @socket, @curr_http_version, edit_path(req.path)
+ begin
+ res = HTTPResponse.read_new(@socket)
+ end while res.kind_of?(HTTPContinue)
+ res.reading_body(@socket, req.response_body_permitted?) {
+ yield res if block_given?
+ }
+ end_transport req, res
+
+ res
+ end
+
+ private
+
+ def begin_transport(req)
+ if @socket.closed?
+ connect
+ end
+ if @seems_1_0_server
+ req['connection'] ||= 'close'
+ end
+ if not req.response_body_permitted? and @close_on_empty_response
+ req['connection'] ||= 'close'
+ end
+ req['host'] ||= addr_port()
+ end
+
+ def end_transport(req, res)
+ @curr_http_version = res.http_version
+ if not res.body and @close_on_empty_response
+ D 'Conn close'
+ @socket.close
+ elsif keep_alive?(req, res)
+ D 'Conn keep-alive'
+ if @socket.closed?
+ D 'Conn (but seems 1.0 server)'
+ @seems_1_0_server = true
+ end
+ else
+ D 'Conn close'
+ @socket.close
+ end
+ end
+
+ def keep_alive?(req, res)
+ return false if /close/i =~ req['connection'].to_s
+ return false if @seems_1_0_server
+ return true if /keep-alive/i =~ res['connection'].to_s
+ return false if /close/i =~ res['connection'].to_s
+ return true if /keep-alive/i =~ res['proxy-connection'].to_s
+ return false if /close/i =~ res['proxy-connection'].to_s
+ (@curr_http_version == '1.1')
+ end
+
+ #
+ # utils
+ #
+
+ private
+
+ def addr_port
+ if use_ssl?
+ address() + (port == HTTP.https_default_port ? '' : ":#{port()}")
+ else
+ address() + (port == HTTP.http_default_port ? '' : ":#{port()}")
+ end
+ end
+
+ def D(msg)
+ return unless @debug_output
+ @debug_output << msg
+ @debug_output << "\n"
+ end
+
+ end
+
+ HTTPSession = HTTP
+
+
+ #
+ # Header module.
+ #
+ # Provides access to @header in the mixed-into class as a hash-like
+ # object, except with case-insensitive keys. Also provides
+ # methods for accessing commonly-used header values in a more
+ # convenient format.
+ #
+ module HTTPHeader
+
+ def initialize_http_header(initheader)
+ @header = {}
+ return unless initheader
+ initheader.each do |key, value|
+ warn "net/http: warning: duplicated HTTP header: #{key}" if key?(key) and $VERBOSE
+ @header[key.downcase] = [value.strip]
+ end
+ end
+
+ def size #:nodoc: obsolete
+ @header.size
+ end
+
+ alias length size #:nodoc: obsolete
+
+ # Returns the header field corresponding to the case-insensitive key.
+ # For example, a key of "Content-Type" might return "text/html"
+ def [](key)
+ a = @header[key.downcase] or return nil
+ a.join(', ')
+ end
+
+ # Sets the header field corresponding to the case-insensitive key.
+ def []=(key, val)
+ unless val
+ @header.delete key.downcase
+ return val
+ end
+ @header[key.downcase] = [val]
+ end
+
+ # [Ruby 1.8.3]
+ # Adds header field instead of replace.
+ # Second argument +val+ must be a String.
+ # See also #[]=, #[] and #get_fields.
+ #
+ # request.add_field 'X-My-Header', 'a'
+ # p request['X-My-Header'] #=> "a"
+ # p request.get_fields('X-My-Header') #=> ["a"]
+ # request.add_field 'X-My-Header', 'b'
+ # p request['X-My-Header'] #=> "a, b"
+ # p request.get_fields('X-My-Header') #=> ["a", "b"]
+ # request.add_field 'X-My-Header', 'c'
+ # p request['X-My-Header'] #=> "a, b, c"
+ # p request.get_fields('X-My-Header') #=> ["a", "b", "c"]
+ #
+ def add_field(key, val)
+ if @header.key?(key.downcase)
+ @header[key.downcase].push val
+ else
+ @header[key.downcase] = [val]
+ end
+ end
+
+ # [Ruby 1.8.3]
+ # Returns an array of header field strings corresponding to the
+ # case-insensitive +key+. This method allows you to get duplicated
+ # header fields without any processing. See also #[].
+ #
+ # p response.get_fields('Set-Cookie')
+ # #=> ["session=al98axx; expires=Fri, 31-Dec-1999 23:58:23",
+ # "query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"]
+ # p response['Set-Cookie']
+ # #=> "session=al98axx; expires=Fri, 31-Dec-1999 23:58:23, query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"
+ #
+ def get_fields(key)
+ return nil unless @header[key.downcase]
+ @header[key.downcase].dup
+ end
+
+ # Returns the header field corresponding to the case-insensitive key.
+ # Returns the default value +args+, or the result of the block, or nil,
+ # if there's no header field named key. See Hash#fetch
+ def fetch(key, *args, &block) #:yield: +key+
+ a = @header.fetch(key.downcase, *args, &block)
+ a.join(', ')
+ end
+
+ # Iterates for each header names and values.
+ def each_header #:yield: +key+, +value+
+ @header.each do |k,va|
+ yield k, va.join(', ')
+ end
+ end
+
+ alias each each_header
+
+ # Iterates for each header names.
+ def each_name(&block) #:yield: +key+
+ @header.each_key(&block)
+ end
+
+ alias each_key each_name
+
+ # Iterates for each capitalized header names.
+ def each_capitalized_name(&block) #:yield: +key+
+ @header.each_key do |k|
+ yield capitalize(k)
+ end
+ end
+
+ # Iterates for each header values.
+ def each_value #:yield: +value+
+ @header.each_value do |va|
+ yield va.join(', ')
+ end
+ end
+
+ # Removes a header field.
+ def delete(key)
+ @header.delete(key.downcase)
+ end
+
+ # true if +key+ header exists.
+ def key?(key)
+ @header.key?(key.downcase)
+ end
+
+ # Returns a Hash consist of header names and values.
+ def to_hash
+ @header.dup
+ end
+
+ # As for #each_header, except the keys are provided in capitalized form.
+ def each_capitalized
+ @header.each do |k,v|
+ yield capitalize(k), v.join(', ')
+ end
+ end
+
+ alias canonical_each each_capitalized
+
+ def capitalize(name)
+ name.split(/-/).map {|s| s.capitalize }.join('-')
+ end
+ private :capitalize
+
+ # Returns an Array of Range objects which represents Range: header field,
+ # or +nil+ if there is no such header.
+ def range
+ return nil unless @header['range']
+ self['Range'].split(/,/).map {|spec|
+ m = /bytes\s*=\s*(\d+)?\s*-\s*(\d+)?/i.match(spec) or
+ raise HTTPHeaderSyntaxError, "wrong Range: #{spec}"
+ d1 = m[1].to_i
+ d2 = m[2].to_i
+ if m[1] and m[2] then d1..d2
+ elsif m[1] then d1..-1
+ elsif m[2] then -d2..-1
+ else
+ raise HTTPHeaderSyntaxError, 'range is not specified'
+ end
+ }
+ end
+
+ # Set Range: header from Range (arg r) or beginning index and
+ # length from it (arg idx&len).
+ #
+ # req.range = (0..1023)
+ # req.set_range 0, 1023
+ #
+ def set_range(r, e = nil)
+ unless r
+ @header.delete 'range'
+ return r
+ end
+ r = (r...r+e) if e
+ case r
+ when Numeric
+ n = r.to_i
+ rangestr = (n > 0 ? "0-#{n-1}" : "-#{-n}")
+ when Range
+ first = r.first
+ last = r.last
+ last -= 1 if r.exclude_end?
+ if last == -1
+ rangestr = (first > 0 ? "#{first}-" : "-#{-first}")
+ else
+ raise HTTPHeaderSyntaxError, 'range.first is negative' if first < 0
+ raise HTTPHeaderSyntaxError, 'range.last is negative' if last < 0
+ raise HTTPHeaderSyntaxError, 'must be .first < .last' if first > last
+ rangestr = "#{first}-#{last}"
+ end
+ else
+ raise TypeError, 'Range/Integer is required'
+ end
+ @header['range'] = ["bytes=#{rangestr}"]
+ r
+ end
+
+ alias range= set_range
+
+ # Returns an Integer object which represents the Content-Length: header field
+ # or +nil+ if that field is not provided.
+ def content_length
+ return nil unless key?('Content-Length')
+ len = self['Content-Length'].slice(/\d+/) or
+ raise HTTPHeaderSyntaxError, 'wrong Content-Length format'
+ len.to_i
+ end
+
+ def content_length=(len)
+ unless len
+ @header.delete 'content-length'
+ return nil
+ end
+ @header['content-length'] = [len.to_i.to_s]
+ end
+
+ # Returns "true" if the "transfer-encoding" header is present and
+ # set to "chunked". This is an HTTP/1.1 feature, allowing the
+ # the content to be sent in "chunks" without at the outset
+ # stating the entire content length.
+ def chunked?
+ return false unless @header['transfer-encoding']
+ field = self['Transfer-Encoding']
+ (/(?:\A|[^\-\w])chunked(?![\-\w])/i =~ field) ? true : false
+ end
+
+ # Returns a Range object which represents Content-Range: header field.
+ # This indicates, for a partial entity body, where this fragment
+ # fits inside the full entity body, as range of byte offsets.
+ def content_range
+ return nil unless @header['content-range']
+ m = %r<bytes\s+(\d+)-(\d+)/(\d+|\*)>i.match(self['Content-Range']) or
+ raise HTTPHeaderSyntaxError, 'wrong Content-Range format'
+ m[1].to_i .. m[2].to_i + 1
+ end
+
+ # The length of the range represented in Content-Range: header.
+ def range_length
+ r = content_range() or return nil
+ r.end - r.begin
+ end
+
+ # Returns a content type string such as "text/html".
+ # This method returns nil if Content-Type: header field does not exist.
+ def content_type
+ return nil unless main_type()
+ if sub_type()
+ then "#{main_type()}/#{sub_type()}"
+ else main_type()
+ end
+ end
+
+ # Returns a content type string such as "text".
+ # This method returns nil if Content-Type: header field does not exist.
+ def main_type
+ return nil unless @header['content-type']
+ self['Content-Type'].split(';').first.to_s.split('/')[0].to_s.strip
+ end
+
+ # Returns a content type string such as "html".
+ # This method returns nil if Content-Type: header field does not exist
+ # or sub-type is not given (e.g. "Content-Type: text").
+ def sub_type
+ return nil unless @header['content-type']
+ main, sub = *self['Content-Type'].split(';').first.to_s.split('/')
+ return nil unless sub
+ sub.strip
+ end
+
+ # Returns content type parameters as a Hash as like
+ # {"charset" => "iso-2022-jp"}.
+ def type_params
+ result = {}
+ list = self['Content-Type'].to_s.split(';')
+ list.shift
+ list.each do |param|
+ k, v = *param.split('=', 2)
+ result[k.strip] = v.strip
+ end
+ result
+ end
+
+ # Set Content-Type: header field by +type+ and +params+.
+ # +type+ must be a String, +params+ must be a Hash.
+ def set_content_type(type, params = {})
+ @header['content-type'] = [type + params.map{|k,v|"; #{k}=#{v}"}.join('')]
+ end
+
+ alias content_type= set_content_type
+
+ # Set header fields and a body from HTML form data.
+ # +params+ should be a Hash containing HTML form data.
+ # Optional argument +sep+ means data record separator.
+ #
+ # This method also set Content-Type: header field to
+ # application/x-www-form-urlencoded.
+ def set_form_data(params, sep = '&')
+ self.body = params.map {|k,v| "#{urlencode(k.to_s)}=#{urlencode(v.to_s)}" }.join(sep)
+ self.content_type = 'application/x-www-form-urlencoded'
+ end
+
+ alias form_data= set_form_data
+
+ def urlencode(str)
+ str.gsub(/[^a-zA-Z0-9_\.\-]/n) {|s| sprintf('%%%02x', s[0]) }
+ end
+ private :urlencode
+
+ # Set the Authorization: header for "Basic" authorization.
+ def basic_auth(account, password)
+ @header['authorization'] = [basic_encode(account, password)]
+ end
+
+ # Set Proxy-Authorization: header for "Basic" authorization.
+ def proxy_basic_auth(account, password)
+ @header['proxy-authorization'] = [basic_encode(account, password)]
+ end
+
+ def basic_encode(account, password)
+ 'Basic ' + ["#{account}:#{password}"].pack('m').delete("\r\n")
+ end
+ private :basic_encode
+
+ end
+
+
+ #
+ # Parent of HTTPRequest class. Do not use this directly; use
+ # a subclass of HTTPRequest.
+ #
+ # Mixes in the HTTPHeader module.
+ #
+ class HTTPGenericRequest
+
+ include HTTPHeader
+
+ def initialize(m, reqbody, resbody, path, initheader = nil)
+ @method = m
+ @request_has_body = reqbody
+ @response_has_body = resbody
+ raise ArgumentError, "HTTP request path is empty" if path.empty?
+ @path = path
+ initialize_http_header initheader
+ self['Accept'] ||= '*/*'
+ @body = nil
+ @body_stream = nil
+ end
+
+ attr_reader :method
+ attr_reader :path
+
+ def inspect
+ "\#<#{self.class} #{@method}>"
+ end
+
+ def request_body_permitted?
+ @request_has_body
+ end
+
+ def response_body_permitted?
+ @response_has_body
+ end
+
+ def body_exist?
+ warn "Net::HTTPRequest#body_exist? is obsolete; use response_body_permitted?" if $VERBOSE
+ response_body_permitted?
+ end
+
+ attr_reader :body
+
+ def body=(str)
+ @body = str
+ @body_stream = nil
+ str
+ end
+
+ attr_reader :body_stream
+
+ def body_stream=(input)
+ @body = nil
+ @body_stream = input
+ input
+ end
+
+ def set_body_internal(str) #:nodoc: internal use only
+ raise ArgumentError, "both of body argument and HTTPRequest#body set" if str and (@body or @body_stream)
+ self.body = str if str
+ end
+
+ #
+ # write
+ #
+
+ def exec(sock, ver, path) #:nodoc: internal use only
+ if @body
+ send_request_with_body sock, ver, path, @body
+ elsif @body_stream
+ send_request_with_body_stream sock, ver, path, @body_stream
+ else
+ write_header sock, ver, path
+ end
+ end
+
+ private
+
+ def send_request_with_body(sock, ver, path, body)
+ self.content_length = body.length
+ delete 'Transfer-Encoding'
+ supply_default_content_type
+ write_header sock, ver, path
+ sock.write body
+ end
+
+ def send_request_with_body_stream(sock, ver, path, f)
+ unless content_length() or chunked?
+ raise ArgumentError,
+ "Content-Length not given and Transfer-Encoding is not `chunked'"
+ end
+ supply_default_content_type
+ write_header sock, ver, path
+ if chunked?
+ while s = f.read(1024)
+ sock.write(sprintf("%x\r\n", s.length) << s << "\r\n")
+ end
+ sock.write "0\r\n\r\n"
+ else
+ while s = f.read(1024)
+ sock.write s
+ end
+ end
+ end
+
+ def supply_default_content_type
+ return if content_type()
+ warn 'net/http: warning: Content-Type did not set; using application/x-www-form-urlencoded' if $VERBOSE
+ set_content_type 'application/x-www-form-urlencoded'
+ end
+
+ def write_header(sock, ver, path)
+ buf = "#{@method} #{path} HTTP/#{ver}\r\n"
+ each_capitalized do |k,v|
+ buf << "#{k}: #{v}\r\n"
+ end
+ buf << "\r\n"
+ sock.write buf
+ end
+
+ end
+
+
+ #
+ # HTTP request class. This class wraps request header and entity path.
+ # You *must* use its subclass, Net::HTTP::Get, Post, Head.
+ #
+ class HTTPRequest < HTTPGenericRequest
+
+ # Creates HTTP request object.
+ def initialize(path, initheader = nil)
+ super self.class::METHOD,
+ self.class::REQUEST_HAS_BODY,
+ self.class::RESPONSE_HAS_BODY,
+ path, initheader
+ end
+ end
+
+
+ class HTTP # reopen
+ #
+ # HTTP 1.1 methods --- RFC2616
+ #
+
+ class Get < HTTPRequest
+ METHOD = 'GET'
+ REQUEST_HAS_BODY = false
+ RESPONSE_HAS_BODY = true
+ end
+
+ class Head < HTTPRequest
+ METHOD = 'HEAD'
+ REQUEST_HAS_BODY = false
+ RESPONSE_HAS_BODY = false
+ end
+
+ class Post < HTTPRequest
+ METHOD = 'POST'
+ REQUEST_HAS_BODY = true
+ RESPONSE_HAS_BODY = true
+ end
+
+ class Put < HTTPRequest
+ METHOD = 'PUT'
+ REQUEST_HAS_BODY = true
+ RESPONSE_HAS_BODY = true
+ end
+
+ class Delete < HTTPRequest
+ METHOD = 'DELETE'
+ REQUEST_HAS_BODY = false
+ RESPONSE_HAS_BODY = true
+ end
+
+ class Options < HTTPRequest
+ METHOD = 'OPTIONS'
+ REQUEST_HAS_BODY = false
+ RESPONSE_HAS_BODY = false
+ end
+
+ class Trace < HTTPRequest
+ METHOD = 'TRACE'
+ REQUEST_HAS_BODY = false
+ RESPONSE_HAS_BODY = true
+ end
+
+ #
+ # WebDAV methods --- RFC2518
+ #
+
+ class Propfind < HTTPRequest
+ METHOD = 'PROPFIND'
+ REQUEST_HAS_BODY = true
+ RESPONSE_HAS_BODY = true
+ end
+
+ class Proppatch < HTTPRequest
+ METHOD = 'PROPPATCH'
+ REQUEST_HAS_BODY = true
+ RESPONSE_HAS_BODY = true
+ end
+
+ class Mkcol < HTTPRequest
+ METHOD = 'MKCOL'
+ REQUEST_HAS_BODY = true
+ RESPONSE_HAS_BODY = true
+ end
+
+ class Copy < HTTPRequest
+ METHOD = 'COPY'
+ REQUEST_HAS_BODY = false
+ RESPONSE_HAS_BODY = true
+ end
+
+ class Move < HTTPRequest
+ METHOD = 'MOVE'
+ REQUEST_HAS_BODY = false
+ RESPONSE_HAS_BODY = true
+ end
+
+ class Lock < HTTPRequest
+ METHOD = 'LOCK'
+ REQUEST_HAS_BODY = true
+ RESPONSE_HAS_BODY = true
+ end
+
+ class Unlock < HTTPRequest
+ METHOD = 'UNLOCK'
+ REQUEST_HAS_BODY = true
+ RESPONSE_HAS_BODY = true
+ end
+ end
+
+
+ ###
+ ### Response
+ ###
+
+ # HTTP exception class.
+ # You must use its subclasses.
+ module HTTPExceptions
+ def initialize(msg, res) #:nodoc:
+ super msg
+ @response = res
+ end
+ attr_reader :response
+ alias data response #:nodoc: obsolete
+ end
+ class HTTPError < ProtocolError
+ include HTTPExceptions
+ end
+ class HTTPRetriableError < ProtoRetriableError
+ include HTTPExceptions
+ end
+ class HTTPServerException < ProtoServerError
+ # We cannot use the name "HTTPServerError", it is the name of the response.
+ include HTTPExceptions
+ end
+ class HTTPFatalError < ProtoFatalError
+ include HTTPExceptions
+ end
+
+
+ # HTTP response class. This class wraps response header and entity.
+ # Mixes in the HTTPHeader module, which provides access to response
+ # header values both via hash-like methods and individual readers.
+ # Note that each possible HTTP response code defines its own
+ # HTTPResponse subclass. These are listed below.
+ # All classes are
+ # defined under the Net module. Indentation indicates inheritance.
+ #
+ # xxx HTTPResponse
+ #
+ # 1xx HTTPInformation
+ # 100 HTTPContinue
+ # 101 HTTPSwitchProtocol
+ #
+ # 2xx HTTPSuccess
+ # 200 HTTPOK
+ # 201 HTTPCreated
+ # 202 HTTPAccepted
+ # 203 HTTPNonAuthoritativeInformation
+ # 204 HTTPNoContent
+ # 205 HTTPResetContent
+ # 206 HTTPPartialContent
+ #
+ # 3xx HTTPRedirection
+ # 300 HTTPMultipleChoice
+ # 301 HTTPMovedPermanently
+ # 302 HTTPFound
+ # 303 HTTPSeeOther
+ # 304 HTTPNotModified
+ # 305 HTTPUseProxy
+ # 307 HTTPTemporaryRedirect
+ #
+ # 4xx HTTPClientError
+ # 400 HTTPBadRequest
+ # 401 HTTPUnauthorized
+ # 402 HTTPPaymentRequired
+ # 403 HTTPForbidden
+ # 404 HTTPNotFound
+ # 405 HTTPMethodNotAllowed
+ # 406 HTTPNotAcceptable
+ # 407 HTTPProxyAuthenticationRequired
+ # 408 HTTPRequestTimeOut
+ # 409 HTTPConflict
+ # 410 HTTPGone
+ # 411 HTTPLengthRequired
+ # 412 HTTPPreconditionFailed
+ # 413 HTTPRequestEntityTooLarge
+ # 414 HTTPRequestURITooLong
+ # 415 HTTPUnsupportedMediaType
+ # 416 HTTPRequestedRangeNotSatisfiable
+ # 417 HTTPExpectationFailed
+ #
+ # 5xx HTTPServerError
+ # 500 HTTPInternalServerError
+ # 501 HTTPNotImplemented
+ # 502 HTTPBadGateway
+ # 503 HTTPServiceUnavailable
+ # 504 HTTPGatewayTimeOut
+ # 505 HTTPVersionNotSupported
+ #
+ # xxx HTTPUnknownResponse
+ #
+ class HTTPResponse
+ # true if the response has body.
+ def HTTPResponse.body_permitted?
+ self::HAS_BODY
+ end
+
+ def HTTPResponse.exception_type # :nodoc: internal use only
+ self::EXCEPTION_TYPE
+ end
+ end # reopened after
+
+ # :stopdoc:
+
+ class HTTPUnknownResponse < HTTPResponse
+ HAS_BODY = true
+ EXCEPTION_TYPE = HTTPError
+ end
+ class HTTPInformation < HTTPResponse # 1xx
+ HAS_BODY = false
+ EXCEPTION_TYPE = HTTPError
+ end
+ class HTTPSuccess < HTTPResponse # 2xx
+ HAS_BODY = true
+ EXCEPTION_TYPE = HTTPError
+ end
+ class HTTPRedirection < HTTPResponse # 3xx
+ HAS_BODY = true
+ EXCEPTION_TYPE = HTTPRetriableError
+ end
+ class HTTPClientError < HTTPResponse # 4xx
+ HAS_BODY = true
+ EXCEPTION_TYPE = HTTPServerException # for backward compatibility
+ end
+ class HTTPServerError < HTTPResponse # 5xx
+ HAS_BODY = true
+ EXCEPTION_TYPE = HTTPFatalError # for backward compatibility
+ end
+
+ class HTTPContinue < HTTPInformation # 100
+ HAS_BODY = false
+ end
+ class HTTPSwitchProtocol < HTTPInformation # 101
+ HAS_BODY = false
+ end
+
+ class HTTPOK < HTTPSuccess # 200
+ HAS_BODY = true
+ end
+ class HTTPCreated < HTTPSuccess # 201
+ HAS_BODY = true
+ end
+ class HTTPAccepted < HTTPSuccess # 202
+ HAS_BODY = true
+ end
+ class HTTPNonAuthoritativeInformation < HTTPSuccess # 203
+ HAS_BODY = true
+ end
+ class HTTPNoContent < HTTPSuccess # 204
+ HAS_BODY = false
+ end
+ class HTTPResetContent < HTTPSuccess # 205
+ HAS_BODY = false
+ end
+ class HTTPPartialContent < HTTPSuccess # 206
+ HAS_BODY = true
+ end
+
+ class HTTPMultipleChoice < HTTPRedirection # 300
+ HAS_BODY = true
+ end
+ class HTTPMovedPermanently < HTTPRedirection # 301
+ HAS_BODY = true
+ end
+ class HTTPFound < HTTPRedirection # 302
+ HAS_BODY = true
+ end
+ HTTPMovedTemporarily = HTTPFound
+ class HTTPSeeOther < HTTPRedirection # 303
+ HAS_BODY = true
+ end
+ class HTTPNotModified < HTTPRedirection # 304
+ HAS_BODY = false
+ end
+ class HTTPUseProxy < HTTPRedirection # 305
+ HAS_BODY = false
+ end
+ # 306 unused
+ class HTTPTemporaryRedirect < HTTPRedirection # 307
+ HAS_BODY = true
+ end
+
+ class HTTPBadRequest < HTTPClientError # 400
+ HAS_BODY = true
+ end
+ class HTTPUnauthorized < HTTPClientError # 401
+ HAS_BODY = true
+ end
+ class HTTPPaymentRequired < HTTPClientError # 402
+ HAS_BODY = true
+ end
+ class HTTPForbidden < HTTPClientError # 403
+ HAS_BODY = true
+ end
+ class HTTPNotFound < HTTPClientError # 404
+ HAS_BODY = true
+ end
+ class HTTPMethodNotAllowed < HTTPClientError # 405
+ HAS_BODY = true
+ end
+ class HTTPNotAcceptable < HTTPClientError # 406
+ HAS_BODY = true
+ end
+ class HTTPProxyAuthenticationRequired < HTTPClientError # 407
+ HAS_BODY = true
+ end
+ class HTTPRequestTimeOut < HTTPClientError # 408
+ HAS_BODY = true
+ end
+ class HTTPConflict < HTTPClientError # 409
+ HAS_BODY = true
+ end
+ class HTTPGone < HTTPClientError # 410
+ HAS_BODY = true
+ end
+ class HTTPLengthRequired < HTTPClientError # 411
+ HAS_BODY = true
+ end
+ class HTTPPreconditionFailed < HTTPClientError # 412
+ HAS_BODY = true
+ end
+ class HTTPRequestEntityTooLarge < HTTPClientError # 413
+ HAS_BODY = true
+ end
+ class HTTPRequestURITooLong < HTTPClientError # 414
+ HAS_BODY = true
+ end
+ HTTPRequestURITooLarge = HTTPRequestURITooLong
+ class HTTPUnsupportedMediaType < HTTPClientError # 415
+ HAS_BODY = true
+ end
+ class HTTPRequestedRangeNotSatisfiable < HTTPClientError # 416
+ HAS_BODY = true
+ end
+ class HTTPExpectationFailed < HTTPClientError # 417
+ HAS_BODY = true
+ end
+
+ class HTTPInternalServerError < HTTPServerError # 500
+ HAS_BODY = true
+ end
+ class HTTPNotImplemented < HTTPServerError # 501
+ HAS_BODY = true
+ end
+ class HTTPBadGateway < HTTPServerError # 502
+ HAS_BODY = true
+ end
+ class HTTPServiceUnavailable < HTTPServerError # 503
+ HAS_BODY = true
+ end
+ class HTTPGatewayTimeOut < HTTPServerError # 504
+ HAS_BODY = true
+ end
+ class HTTPVersionNotSupported < HTTPServerError # 505
+ HAS_BODY = true
+ end
+
+ # :startdoc:
+
+
+ class HTTPResponse # reopen
+
+ CODE_CLASS_TO_OBJ = {
+ '1' => HTTPInformation,
+ '2' => HTTPSuccess,
+ '3' => HTTPRedirection,
+ '4' => HTTPClientError,
+ '5' => HTTPServerError
+ }
+ CODE_TO_OBJ = {
+ '100' => HTTPContinue,
+ '101' => HTTPSwitchProtocol,
+
+ '200' => HTTPOK,
+ '201' => HTTPCreated,
+ '202' => HTTPAccepted,
+ '203' => HTTPNonAuthoritativeInformation,
+ '204' => HTTPNoContent,
+ '205' => HTTPResetContent,
+ '206' => HTTPPartialContent,
+
+ '300' => HTTPMultipleChoice,
+ '301' => HTTPMovedPermanently,
+ '302' => HTTPFound,
+ '303' => HTTPSeeOther,
+ '304' => HTTPNotModified,
+ '305' => HTTPUseProxy,
+ '307' => HTTPTemporaryRedirect,
+
+ '400' => HTTPBadRequest,
+ '401' => HTTPUnauthorized,
+ '402' => HTTPPaymentRequired,
+ '403' => HTTPForbidden,
+ '404' => HTTPNotFound,
+ '405' => HTTPMethodNotAllowed,
+ '406' => HTTPNotAcceptable,
+ '407' => HTTPProxyAuthenticationRequired,
+ '408' => HTTPRequestTimeOut,
+ '409' => HTTPConflict,
+ '410' => HTTPGone,
+ '411' => HTTPLengthRequired,
+ '412' => HTTPPreconditionFailed,
+ '413' => HTTPRequestEntityTooLarge,
+ '414' => HTTPRequestURITooLong,
+ '415' => HTTPUnsupportedMediaType,
+ '416' => HTTPRequestedRangeNotSatisfiable,
+ '417' => HTTPExpectationFailed,
+
+ '500' => HTTPInternalServerError,
+ '501' => HTTPNotImplemented,
+ '502' => HTTPBadGateway,
+ '503' => HTTPServiceUnavailable,
+ '504' => HTTPGatewayTimeOut,
+ '505' => HTTPVersionNotSupported
+ }
+
+ class << HTTPResponse
+ def read_new(sock) #:nodoc: internal use only
+ httpv, code, msg = read_status_line(sock)
+ res = response_class(code).new(httpv, code, msg)
+ each_response_header(sock) do |k,v|
+ res.add_field k, v
+ end
+ res
+ end
+
+ private
+
+ def read_status_line(sock)
+ str = sock.readline
+ m = /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)\s*(.*)\z/in.match(str) or
+ raise HTTPBadResponse, "wrong status line: #{str.dump}"
+ m.captures
+ end
+
+ def response_class(code)
+ CODE_TO_OBJ[code] or
+ CODE_CLASS_TO_OBJ[code[0,1]] or
+ HTTPUnknownResponse
+ end
+
+ def each_response_header(sock)
+ while true
+ line = sock.readuntil("\n", true).sub(/\s+\z/, '')
+ break if line.empty?
+ m = /\A([^:]+):\s*/.match(line) or
+ raise HTTPBadResponse, 'wrong header line format'
+ yield m[1], m.post_match
+ end
+ end
+ end
+
+ # next is to fix bug in RDoc, where the private inside class << self
+ # spills out.
+ public
+
+ include HTTPHeader
+
+ def initialize(httpv, code, msg) #:nodoc: internal use only
+ @http_version = httpv
+ @code = code
+ @message = msg
+ initialize_http_header nil
+ @body = nil
+ @read = false
+ end
+
+ # The HTTP version supported by the server.
+ attr_reader :http_version
+
+ # HTTP result code string. For example, '302'. You can also
+ # determine the response type by which response subclass the
+ # response object is an instance of.
+ attr_reader :code
+
+ # HTTP result message. For example, 'Not Found'.
+ attr_reader :message
+ alias msg message # :nodoc: obsolete
+
+ def inspect
+ "#<#{self.class} #{@code} #{@message} readbody=#{@read}>"
+ end
+
+ # For backward compatibility.
+ # To allow Net::HTTP 1.1 style assignment
+ # e.g.
+ # response, body = Net::HTTP.get(....)
+ #
+ def to_ary
+ warn "net/http.rb: warning: Net::HTTP v1.1 style assignment found at #{caller(1)[0]}; use `response = http.get(...)' instead." if $VERBOSE
+ res = self.dup
+ class << res
+ undef to_ary
+ end
+ [res, res.body]
+ end
+
+ #
+ # response <-> exception relationship
+ #
+
+ def code_type #:nodoc:
+ self.class
+ end
+
+ def error! #:nodoc:
+ raise error_type().new(@code + ' ' + @message.dump, self)
+ end
+
+ def error_type #:nodoc:
+ self.class::EXCEPTION_TYPE
+ end
+
+ # Raises HTTP error if the response is not 2xx.
+ def value
+ error! unless self.kind_of?(HTTPSuccess)
+ end
+
+ #
+ # header (for backward compatibility only; DO NOT USE)
+ #
+
+ def response #:nodoc:
+ warn "#{caller(1)[0]}: warning: HTTPResponse#response is obsolete" if $VERBOSE
+ self
+ end
+
+ def header #:nodoc:
+ warn "#{caller(1)[0]}: warning: HTTPResponse#header is obsolete" if $VERBOSE
+ self
+ end
+
+ def read_header #:nodoc:
+ warn "#{caller(1)[0]}: warning: HTTPResponse#read_header is obsolete" if $VERBOSE
+ self
+ end
+
+ #
+ # body
+ #
+
+ def reading_body(sock, reqmethodallowbody) #:nodoc: internal use only
+ @socket = sock
+ @body_exist = reqmethodallowbody && self.class.body_permitted?
+ begin
+ yield
+ self.body # ensure to read body
+ ensure
+ @socket = nil
+ end
+ end
+
+ # Gets entity body. If the block given, yields it to +block+.
+ # The body is provided in fragments, as it is read in from the socket.
+ #
+ # Calling this method a second or subsequent time will return the
+ # already read string.
+ #
+ # http.request_get('/index.html') {|res|
+ # puts res.read_body
+ # }
+ #
+ # http.request_get('/index.html') {|res|
+ # p res.read_body.object_id # 538149362
+ # p res.read_body.object_id # 538149362
+ # }
+ #
+ # # using iterator
+ # http.request_get('/index.html') {|res|
+ # res.read_body do |segment|
+ # print segment
+ # end
+ # }
+ #
+ def read_body(dest = nil, &block)
+ if @read
+ raise IOError, "#{self.class}\#read_body called twice" if dest or block
+ return @body
+ end
+ to = procdest(dest, block)
+ stream_check
+ if @body_exist
+ read_body_0 to
+ @body = to
+ else
+ @body = nil
+ end
+ @read = true
+
+ @body
+ end
+
+ # Returns the entity body.
+ #
+ # Calling this method a second or subsequent time will return the
+ # already read string.
+ #
+ # http.request_get('/index.html') {|res|
+ # puts res.body
+ # }
+ #
+ # http.request_get('/index.html') {|res|
+ # p res.body.object_id # 538149362
+ # p res.body.object_id # 538149362
+ # }
+ #
+ def body
+ read_body()
+ end
+
+ alias entity body #:nodoc: obsolete
+
+ private
+
+ def read_body_0(dest)
+ if chunked?
+ read_chunked dest
+ return
+ end
+ clen = content_length()
+ if clen
+ @socket.read clen, dest, true # ignore EOF
+ return
+ end
+ clen = range_length()
+ if clen
+ @socket.read clen, dest
+ return
+ end
+ @socket.read_all dest
+ end
+
+ def read_chunked(dest)
+ len = nil
+ total = 0
+ while true
+ line = @socket.readline
+ hexlen = line.slice(/[0-9a-fA-F]+/) or
+ raise HTTPBadResponse, "wrong chunk size line: #{line}"
+ len = hexlen.hex
+ break if len == 0
+ @socket.read len, dest; total += len
+ @socket.read 2 # \r\n
+ end
+ until @socket.readline.empty?
+ # none
+ end
+ end
+
+ def stream_check
+ raise IOError, 'attempt to read body out of block' if @socket.closed?
+ end
+
+ def procdest(dest, block)
+ raise ArgumentError, 'both arg and block given for HTTP method' \
+ if dest and block
+ if block
+ ReadAdapter.new(block)
+ else
+ dest || ''
+ end
+ end
+
+ end
+
+
+ # :enddoc:
+
+ #--
+ # for backward compatibility
+ class HTTP
+ ProxyMod = ProxyDelta
+ end
+ module NetPrivate
+ HTTPRequest = ::Net::HTTPRequest
+ end
+
+ HTTPInformationCode = HTTPInformation
+ HTTPSuccessCode = HTTPSuccess
+ HTTPRedirectionCode = HTTPRedirection
+ HTTPRetriableCode = HTTPRedirection
+ HTTPClientErrorCode = HTTPClientError
+ HTTPFatalErrorCode = HTTPClientError
+ HTTPServerErrorCode = HTTPServerError
+ HTTPResponceReceiver = HTTPResponse
+
+end # module Net
diff --git a/ruby_1_8_5/lib/net/https.rb b/ruby_1_8_5/lib/net/https.rb
new file mode 100644
index 0000000000..1111e94590
--- /dev/null
+++ b/ruby_1_8_5/lib/net/https.rb
@@ -0,0 +1,173 @@
+=begin
+
+= $RCSfile: https.rb,v $ -- SSL/TLS enhancement for Net::HTTP.
+
+== Info
+ 'OpenSSL for Ruby 2' project
+ Copyright (C) 2001 GOTOU Yuuzou <gotoyuzo@notwork.org>
+ All rights reserved.
+
+== Licence
+ This program is licenced under the same licence as Ruby.
+ (See the file 'LICENCE'.)
+
+== Requirements
+ This program requires Net 1.2.0 or higher version.
+ You can get it from RAA or Ruby's CVS repository.
+
+== Version
+ $Id: https.rb,v 1.3.4.2 2006/02/05 09:56:34 aamine Exp $
+
+ 2001-11-06: Contiributed to Ruby/OpenSSL project.
+ 2004-03-06: Some code is merged in to net/http.
+
+== Example
+
+Here is a simple HTTP client:
+
+ require 'net/http'
+ require 'uri'
+
+ uri = URI.parse(ARGV[0] || 'http://localhost/')
+ http = Net::HTTP.new(uri.host, uri.port)
+ http.start {
+ http.request_get(uri.path) {|res|
+ print res.body
+ }
+ }
+
+It can be replaced by the following code:
+
+ require 'net/https'
+ require 'uri'
+
+ uri = URI.parse(ARGV[0] || 'https://localhost/')
+ http = Net::HTTP.new(uri.host, uri.port)
+ http.use_ssl = true if uri.scheme == "https" # enable SSL/TLS
+ http.start {
+ http.request_get(uri.path) {|res|
+ print res.body
+ }
+ }
+
+== class Net::HTTP
+
+=== Instance Methods
+
+: use_ssl?
+ returns true if use SSL/TLS with HTTP.
+
+: use_ssl=((|true_or_false|))
+ sets use_ssl.
+
+: peer_cert
+ return the X.509 certificates the server presented.
+
+: key, key=((|key|))
+ Sets an OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object.
+ (This method is appeared in Michal Rokos's OpenSSL extention.)
+
+: cert, cert=((|cert|))
+ Sets an OpenSSL::X509::Certificate object as client certificate
+ (This method is appeared in Michal Rokos's OpenSSL extention).
+
+: ca_file, ca_file=((|path|))
+ Sets path of a CA certification file in PEM format.
+ The file can contrain several CA certificats.
+
+: ca_path, ca_path=((|path|))
+ Sets path of a CA certification directory containing certifications
+ in PEM format.
+
+: verify_mode, verify_mode=((|mode|))
+ Sets the flags for server the certification verification at
+ begining of SSL/TLS session.
+ OpenSSL::SSL::VERIFY_NONE or OpenSSL::SSL::VERIFY_PEER is acceptable.
+
+: verify_callback, verify_callback=((|proc|))
+ Sets the verify callback for the server certification verification.
+
+: verify_depth, verify_depth=((|num|))
+ Sets the maximum depth for the certificate chain verification.
+
+: cert_store, cert_store=((|store|))
+ Sets the X509::Store to verify peer certificate.
+
+: ssl_timeout, ssl_timeout=((|sec|))
+ Sets the SSL timeout seconds.
+
+=end
+
+require 'net/http'
+require 'openssl'
+
+module Net
+
+ class HTTP
+ remove_method :use_ssl?
+ def use_ssl?
+ @use_ssl
+ end
+
+ # For backward compatibility.
+ alias use_ssl use_ssl?
+
+ # Turn on/off SSL.
+ # This flag must be set before starting session.
+ # If you change use_ssl value after session started,
+ # a Net::HTTP object raises IOError.
+ def use_ssl=(flag)
+ flag = (flag ? true : false)
+ raise IOError, "use_ssl value changed, but session already started" \
+ if started? and @use_ssl != flag
+ if flag and not @ssl_context
+ @ssl_context = OpenSSL::SSL::SSLContext.new
+ end
+ @use_ssl = flag
+ end
+
+ def self.ssl_context_accessor(name)
+ module_eval(<<-End, __FILE__, __LINE__ + 1)
+ def #{name}
+ return nil unless @ssl_context
+ @ssl_context.#{name}
+ end
+
+ def #{name}=(val)
+ @ssl_context ||= OpenSSL::SSL::SSLContext.new
+ @ssl_context.#{name} = val
+ end
+ End
+ end
+
+ ssl_context_accessor :key
+ ssl_context_accessor :cert
+ ssl_context_accessor :ca_file
+ ssl_context_accessor :ca_path
+ ssl_context_accessor :verify_mode
+ ssl_context_accessor :verify_callback
+ ssl_context_accessor :verify_depth
+ ssl_context_accessor :cert_store
+
+ def ssl_timeout
+ return nil unless @ssl_context
+ @ssl_context.timeout
+ end
+
+ def ssl_timeout=(sec)
+ raise ArgumentError, 'Net::HTTP#ssl_timeout= called but use_ssl=false' \
+ unless use_ssl?
+ @ssl_context ||= OpenSSL::SSL::SSLContext.new
+ @ssl_context.timeout = sec
+ end
+
+ # For backward compatibility
+ alias timeout= ssl_timeout=
+
+ def peer_cert
+ return nil if not use_ssl? or not @socket
+ @socket.io.peer_cert
+ end
+ end
+
+end
diff --git a/ruby_1_8_5/lib/net/imap.rb b/ruby_1_8_5/lib/net/imap.rb
new file mode 100644
index 0000000000..586f3fe4c0
--- /dev/null
+++ b/ruby_1_8_5/lib/net/imap.rb
@@ -0,0 +1,3357 @@
+#
+# = net/imap.rb
+#
+# Copyright (C) 2000 Shugo Maeda <shugo@ruby-lang.org>
+#
+# This library is distributed under the terms of the Ruby license.
+# You can freely distribute/modify this library.
+#
+# Documentation: Shugo Maeda, with RDoc conversion and overview by William
+# Webber.
+#
+# See Net::IMAP for documentation.
+#
+
+
+require "socket"
+require "monitor"
+require "digest/md5"
+begin
+ require "openssl"
+rescue LoadError
+end
+
+module Net
+
+ #
+ # Net::IMAP implements Internet Message Access Protocol (IMAP) client
+ # functionality. The protocol is described in [IMAP].
+ #
+ # == IMAP Overview
+ #
+ # An IMAP client connects to a server, and then authenticates
+ # itself using either #authenticate() or #login(). Having
+ # authenticated itself, there is a range of commands
+ # available to it. Most work with mailboxes, which may be
+ # arranged in an hierarchical namespace, and each of which
+ # contains zero or more messages. How this is implemented on
+ # the server is implementation-dependent; on a UNIX server, it
+ # will frequently be implemented as a files in mailbox format
+ # within a hierarchy of directories.
+ #
+ # To work on the messages within a mailbox, the client must
+ # first select that mailbox, using either #select() or (for
+ # read-only access) #examine(). Once the client has successfully
+ # selected a mailbox, they enter _selected_ state, and that
+ # mailbox becomes the _current_ mailbox, on which mail-item
+ # related commands implicitly operate.
+ #
+ # Messages have two sorts of identifiers: message sequence
+ # numbers, and UIDs.
+ #
+ # Message sequence numbers number messages within a mail box
+ # from 1 up to the number of items in the mail box. If new
+ # message arrives during a session, it receives a sequence
+ # number equal to the new size of the mail box. If messages
+ # are expunged from the mailbox, remaining messages have their
+ # sequence numbers "shuffled down" to fill the gaps.
+ #
+ # UIDs, on the other hand, are permanently guaranteed not to
+ # identify another message within the same mailbox, even if
+ # the existing message is deleted. UIDs are required to
+ # be assigned in ascending (but not necessarily sequential)
+ # order within a mailbox; this means that if a non-IMAP client
+ # rearranges the order of mailitems within a mailbox, the
+ # UIDs have to be reassigned. An IMAP client cannot thus
+ # rearrange message orders.
+ #
+ # == Examples of Usage
+ #
+ # === List sender and subject of all recent messages in the default mailbox
+ #
+ # imap = Net::IMAP.new('mail.example.com')
+ # imap.authenticate('LOGIN', 'joe_user', 'joes_password')
+ # imap.examine('INBOX')
+ # imap.search(["RECENT"]).each do |message_id|
+ # envelope = imap.fetch(message_id, "ENVELOPE")[0].attr["ENVELOPE"]
+ # puts "#{envelope.from[0].name}: \t#{envelope.subject}"
+ # end
+ #
+ # === Move all messages from April 2003 from "Mail/sent-mail" to "Mail/sent-apr03"
+ #
+ # imap = Net::IMAP.new('mail.example.com')
+ # imap.authenticate('LOGIN', 'joe_user', 'joes_password')
+ # imap.select('Mail/sent-mail')
+ # if not imap.list('Mail/', 'sent-apr03')
+ # imap.create('Mail/sent-apr03')
+ # end
+ # imap.search(["BEFORE", "30-Apr-2003", "SINCE", "1-Apr-2003"]).each do |message_id|
+ # imap.copy(message_id, "Mail/sent-apr03")
+ # imap.store(message_id, "+FLAGS", [:Deleted])
+ # end
+ # imap.expunge
+ #
+ # == Thread Safety
+ #
+ # Net::IMAP supports concurrent threads. For example,
+ #
+ # imap = Net::IMAP.new("imap.foo.net", "imap2")
+ # imap.authenticate("cram-md5", "bar", "password")
+ # imap.select("inbox")
+ # fetch_thread = Thread.start { imap.fetch(1..-1, "UID") }
+ # search_result = imap.search(["BODY", "hello"])
+ # fetch_result = fetch_thread.value
+ # imap.disconnect
+ #
+ # This script invokes the FETCH command and the SEARCH command concurrently.
+ #
+ # == Errors
+ #
+ # An IMAP server can send three different types of responses to indicate
+ # failure:
+ #
+ # NO:: the attempted command could not be successfully completed. For
+ # instance, the username/password used for logging in are incorrect;
+ # the selected mailbox does not exists; etc.
+ #
+ # BAD:: the request from the client does not follow the server's
+ # understanding of the IMAP protocol. This includes attempting
+ # commands from the wrong client state; for instance, attempting
+ # to perform a SEARCH command without having SELECTed a current
+ # mailbox. It can also signal an internal server
+ # failure (such as a disk crash) has occurred.
+ #
+ # BYE:: the server is saying goodbye. This can be part of a normal
+ # logout sequence, and can be used as part of a login sequence
+ # to indicate that the server is (for some reason) unwilling
+ # to accept our connection. As a response to any other command,
+ # it indicates either that the server is shutting down, or that
+ # the server is timing out the client connection due to inactivity.
+ #
+ # These three error response are represented by the errors
+ # Net::IMAP::NoResponseError, Net::IMAP::BadResponseError, and
+ # Net::IMAP::ByeResponseError, all of which are subclasses of
+ # Net::IMAP::ResponseError. Essentially, all methods that involve
+ # sending a request to the server can generate one of these errors.
+ # Only the most pertinent instances have been documented below.
+ #
+ # Because the IMAP class uses Sockets for communication, its methods
+ # are also susceptible to the various errors that can occur when
+ # working with sockets. These are generally represented as
+ # Errno errors. For instance, any method that involves sending a
+ # request to the server and/or receiving a response from it could
+ # raise an Errno::EPIPE error if the network connection unexpectedly
+ # goes down. See the socket(7), ip(7), tcp(7), socket(2), connect(2),
+ # and associated man pages.
+ #
+ # Finally, a Net::IMAP::DataFormatError is thrown if low-level data
+ # is found to be in an incorrect format (for instance, when converting
+ # between UTF-8 and UTF-16), and Net::IMAP::ResponseParseError is
+ # thrown if a server response is non-parseable.
+ #
+ #
+ # == References
+ #
+ # [[IMAP]]
+ # M. Crispin, "INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1",
+ # RFC 2060, December 1996. (Note: since obsoleted by RFC 3501)
+ #
+ # [[LANGUAGE-TAGS]]
+ # Alvestrand, H., "Tags for the Identification of
+ # Languages", RFC 1766, March 1995.
+ #
+ # [[MD5]]
+ # Myers, J., and M. Rose, "The Content-MD5 Header Field", RFC
+ # 1864, October 1995.
+ #
+ # [[MIME-IMB]]
+ # Freed, N., and N. Borenstein, "MIME (Multipurpose Internet
+ # Mail Extensions) Part One: Format of Internet Message Bodies", RFC
+ # 2045, November 1996.
+ #
+ # [[RFC-822]]
+ # Crocker, D., "Standard for the Format of ARPA Internet Text
+ # Messages", STD 11, RFC 822, University of Delaware, August 1982.
+ #
+ # [[RFC-2087]]
+ # Myers, J., "IMAP4 QUOTA extension", RFC 2087, January 1997.
+ #
+ # [[RFC-2086]]
+ # Myers, J., "IMAP4 ACL extension", RFC 2086, January 1997.
+ #
+ # [[RFC-2195]]
+ # Klensin, J., Catoe, R., and Krumviede, P., "IMAP/POP AUTHorize Extension
+ # for Simple Challenge/Response", RFC 2195, September 1997.
+ #
+ # [[SORT-THREAD-EXT]]
+ # Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL - SORT and THREAD
+ # Extensions", draft-ietf-imapext-sort, May 2003.
+ #
+ # [[OSSL]]
+ # http://www.openssl.org
+ #
+ # [[RSSL]]
+ # http://savannah.gnu.org/projects/rubypki
+ #
+ # [[UTF7]]
+ # Goldsmith, D. and Davis, M., "UTF-7: A Mail-Safe Transformation Format of
+ # Unicode", RFC 2152, May 1997.
+ #
+ class IMAP
+ include MonitorMixin
+ if defined?(OpenSSL)
+ include OpenSSL
+ include SSL
+ end
+
+ # Returns an initial greeting response from the server.
+ attr_reader :greeting
+
+ # Returns recorded untagged responses. For example:
+ #
+ # imap.select("inbox")
+ # p imap.responses["EXISTS"][-1]
+ # #=> 2
+ # p imap.responses["UIDVALIDITY"][-1]
+ # #=> 968263756
+ attr_reader :responses
+
+ # Returns all response handlers.
+ attr_reader :response_handlers
+
+ # The thread to receive exceptions.
+ attr_accessor :client_thread
+
+ # Flag indicating a message has been seen
+ SEEN = :Seen
+
+ # Flag indicating a message has been answered
+ ANSWERED = :Answered
+
+ # Flag indicating a message has been flagged for special or urgent
+ # attention
+ FLAGGED = :Flagged
+
+ # Flag indicating a message has been marked for deletion. This
+ # will occur when the mailbox is closed or expunged.
+ DELETED = :Deleted
+
+ # Flag indicating a message is only a draft or work-in-progress version.
+ DRAFT = :Draft
+
+ # Flag indicating that the message is "recent", meaning that this
+ # session is the first session in which the client has been notified
+ # of this message.
+ RECENT = :Recent
+
+ # Flag indicating that a mailbox context name cannot contain
+ # children.
+ NOINFERIORS = :Noinferiors
+
+ # Flag indicating that a mailbox is not selected.
+ NOSELECT = :Noselect
+
+ # Flag indicating that a mailbox has been marked "interesting" by
+ # the server; this commonly indicates that the mailbox contains
+ # new messages.
+ MARKED = :Marked
+
+ # Flag indicating that the mailbox does not contains new messages.
+ UNMARKED = :Unmarked
+
+ # Returns the debug mode.
+ def self.debug
+ return @@debug
+ end
+
+ # Sets the debug mode.
+ def self.debug=(val)
+ return @@debug = val
+ end
+
+ # Adds an authenticator for Net::IMAP#authenticate. +auth_type+
+ # is the type of authentication this authenticator supports
+ # (for instance, "LOGIN"). The +authenticator+ is an object
+ # which defines a process() method to handle authentication with
+ # the server. See Net::IMAP::LoginAuthenticator and
+ # Net::IMAP::CramMD5Authenticator for examples.
+ #
+ # If +auth_type+ refers to an existing authenticator, it will be
+ # replaced by the new one.
+ def self.add_authenticator(auth_type, authenticator)
+ @@authenticators[auth_type] = authenticator
+ end
+
+ # Disconnects from the server.
+ def disconnect
+ if SSL::SSLSocket === @sock
+ @sock.io.shutdown
+ else
+ @sock.shutdown
+ end
+ @receiver_thread.join
+ @sock.close
+ end
+
+ # Returns true if disconnected from the server.
+ def disconnected?
+ return @sock.closed?
+ end
+
+ # Sends a CAPABILITY command, and returns an array of
+ # capabilities that the server supports. Each capability
+ # is a string. See [IMAP] for a list of possible
+ # capabilities.
+ #
+ # Note that the Net::IMAP class does not modify its
+ # behaviour according to the capabilities of the server;
+ # it is up to the user of the class to ensure that
+ # a certain capability is supported by a server before
+ # using it.
+ def capability
+ synchronize do
+ send_command("CAPABILITY")
+ return @responses.delete("CAPABILITY")[-1]
+ end
+ end
+
+ # Sends a NOOP command to the server. It does nothing.
+ def noop
+ send_command("NOOP")
+ end
+
+ # Sends a LOGOUT command to inform the server that the client is
+ # done with the connection.
+ def logout
+ send_command("LOGOUT")
+ end
+
+ # Sends an AUTHENTICATE command to authenticate the client.
+ # The +auth_type+ parameter is a string that represents
+ # the authentication mechanism to be used. Currently Net::IMAP
+ # supports authentication mechanisms:
+ #
+ # LOGIN:: login using cleartext user and password.
+ # CRAM-MD5:: login with cleartext user and encrypted password
+ # (see [RFC-2195] for a full description). This
+ # mechanism requires that the server have the user's
+ # password stored in clear-text password.
+ #
+ # For both these mechanisms, there should be two +args+: username
+ # and (cleartext) password. A server may not support one or other
+ # of these mechanisms; check #capability() for a capability of
+ # the form "AUTH=LOGIN" or "AUTH=CRAM-MD5".
+ #
+ # Authentication is done using the appropriate authenticator object:
+ # see @@authenticators for more information on plugging in your own
+ # authenticator.
+ #
+ # For example:
+ #
+ # imap.authenticate('LOGIN', user, password)
+ #
+ # A Net::IMAP::NoResponseError is raised if authentication fails.
+ def authenticate(auth_type, *args)
+ auth_type = auth_type.upcase
+ unless @@authenticators.has_key?(auth_type)
+ raise ArgumentError,
+ format('unknown auth type - "%s"', auth_type)
+ end
+ authenticator = @@authenticators[auth_type].new(*args)
+ send_command("AUTHENTICATE", auth_type) do |resp|
+ if resp.instance_of?(ContinuationRequest)
+ data = authenticator.process(resp.data.text.unpack("m")[0])
+ s = [data].pack("m").gsub(/\n/, "")
+ send_string_data(s)
+ put_string(CRLF)
+ end
+ end
+ end
+
+ # Sends a LOGIN command to identify the client and carries
+ # the plaintext +password+ authenticating this +user+. Note
+ # that, unlike calling #authenticate() with an +auth_type+
+ # of "LOGIN", #login() does *not* use the login authenticator.
+ #
+ # A Net::IMAP::NoResponseError is raised if authentication fails.
+ def login(user, password)
+ send_command("LOGIN", user, password)
+ end
+
+ # Sends a SELECT command to select a +mailbox+ so that messages
+ # in the +mailbox+ can be accessed.
+ #
+ # After you have selected a mailbox, you may retrieve the
+ # number of items in that mailbox from @responses["EXISTS"][-1],
+ # and the number of recent messages from @responses["RECENT"][-1].
+ # Note that these values can change if new messages arrive
+ # during a session; see #add_response_handler() for a way of
+ # detecting this event.
+ #
+ # A Net::IMAP::NoResponseError is raised if the mailbox does not
+ # exist or is for some reason non-selectable.
+ def select(mailbox)
+ synchronize do
+ @responses.clear
+ send_command("SELECT", mailbox)
+ end
+ end
+
+ # Sends a EXAMINE command to select a +mailbox+ so that messages
+ # in the +mailbox+ can be accessed. Behaves the same as #select(),
+ # except that the selected +mailbox+ is identified as read-only.
+ #
+ # A Net::IMAP::NoResponseError is raised if the mailbox does not
+ # exist or is for some reason non-examinable.
+ def examine(mailbox)
+ synchronize do
+ @responses.clear
+ send_command("EXAMINE", mailbox)
+ end
+ end
+
+ # Sends a CREATE command to create a new +mailbox+.
+ #
+ # A Net::IMAP::NoResponseError is raised if a mailbox with that name
+ # cannot be created.
+ def create(mailbox)
+ send_command("CREATE", mailbox)
+ end
+
+ # Sends a DELETE command to remove the +mailbox+.
+ #
+ # A Net::IMAP::NoResponseError is raised if a mailbox with that name
+ # cannot be deleted, either because it does not exist or because the
+ # client does not have permission to delete it.
+ def delete(mailbox)
+ send_command("DELETE", mailbox)
+ end
+
+ # Sends a RENAME command to change the name of the +mailbox+ to
+ # +newname+.
+ #
+ # A Net::IMAP::NoResponseError is raised if a mailbox with the
+ # name +mailbox+ cannot be renamed to +newname+ for whatever
+ # reason; for instance, because +mailbox+ does not exist, or
+ # because there is already a mailbox with the name +newname+.
+ def rename(mailbox, newname)
+ send_command("RENAME", mailbox, newname)
+ end
+
+ # Sends a SUBSCRIBE command to add the specified +mailbox+ name to
+ # the server's set of "active" or "subscribed" mailboxes as returned
+ # by #lsub().
+ #
+ # A Net::IMAP::NoResponseError is raised if +mailbox+ cannot be
+ # subscribed to, for instance because it does not exist.
+ def subscribe(mailbox)
+ send_command("SUBSCRIBE", mailbox)
+ end
+
+ # Sends a UNSUBSCRIBE command to remove the specified +mailbox+ name
+ # from the server's set of "active" or "subscribed" mailboxes.
+ #
+ # A Net::IMAP::NoResponseError is raised if +mailbox+ cannot be
+ # unsubscribed from, for instance because the client is not currently
+ # subscribed to it.
+ def unsubscribe(mailbox)
+ send_command("UNSUBSCRIBE", mailbox)
+ end
+
+ # Sends a LIST command, and returns a subset of names from
+ # the complete set of all names available to the client.
+ # +refname+ provides a context (for instance, a base directory
+ # in a directory-based mailbox hierarchy). +mailbox+ specifies
+ # a mailbox or (via wildcards) mailboxes under that context.
+ # Two wildcards may be used in +mailbox+: '*', which matches
+ # all characters *including* the hierarchy delimiter (for instance,
+ # '/' on a UNIX-hosted directory-based mailbox hierarchy); and '%',
+ # which matches all characters *except* the hierarchy delimiter.
+ #
+ # If +refname+ is empty, +mailbox+ is used directly to determine
+ # which mailboxes to match. If +mailbox+ is empty, the root
+ # name of +refname+ and the hierarchy delimiter are returned.
+ #
+ # The return value is an array of +Net::IMAP::MailboxList+. For example:
+ #
+ # imap.create("foo/bar")
+ # imap.create("foo/baz")
+ # p imap.list("", "foo/%")
+ # #=> [#<Net::IMAP::MailboxList attr=[:Noselect], delim="/", name="foo/">, \\
+ # #<Net::IMAP::MailboxList attr=[:Noinferiors, :Marked], delim="/", name="foo/bar">, \\
+ # #<Net::IMAP::MailboxList attr=[:Noinferiors], delim="/", name="foo/baz">]
+ def list(refname, mailbox)
+ synchronize do
+ send_command("LIST", refname, mailbox)
+ return @responses.delete("LIST")
+ end
+ end
+
+ # Sends the GETQUOTAROOT command along with specified +mailbox+.
+ # This command is generally available to both admin and user.
+ # If mailbox exists, returns an array containing objects of
+ # Net::IMAP::MailboxQuotaRoot and Net::IMAP::MailboxQuota.
+ def getquotaroot(mailbox)
+ synchronize do
+ send_command("GETQUOTAROOT", mailbox)
+ result = []
+ result.concat(@responses.delete("QUOTAROOT"))
+ result.concat(@responses.delete("QUOTA"))
+ return result
+ end
+ end
+
+ # Sends the GETQUOTA command along with specified +mailbox+.
+ # If this mailbox exists, then an array containing a
+ # Net::IMAP::MailboxQuota object is returned. This
+ # command generally is only available to server admin.
+ def getquota(mailbox)
+ synchronize do
+ send_command("GETQUOTA", mailbox)
+ return @responses.delete("QUOTA")
+ end
+ end
+
+ # Sends a SETQUOTA command along with the specified +mailbox+ and
+ # +quota+. If +quota+ is nil, then quota will be unset for that
+ # mailbox. Typically one needs to be logged in as server admin
+ # for this to work. The IMAP quota commands are described in
+ # [RFC-2087].
+ def setquota(mailbox, quota)
+ if quota.nil?
+ data = '()'
+ else
+ data = '(STORAGE ' + quota.to_s + ')'
+ end
+ send_command("SETQUOTA", mailbox, RawData.new(data))
+ end
+
+ # Sends the SETACL command along with +mailbox+, +user+ and the
+ # +rights+ that user is to have on that mailbox. If +rights+ is nil,
+ # then that user will be stripped of any rights to that mailbox.
+ # The IMAP ACL commands are described in [RFC-2086].
+ def setacl(mailbox, user, rights)
+ if rights.nil?
+ send_command("SETACL", mailbox, user, "")
+ else
+ send_command("SETACL", mailbox, user, rights)
+ end
+ end
+
+ # Send the GETACL command along with specified +mailbox+.
+ # If this mailbox exists, an array containing objects of
+ # Net::IMAP::MailboxACLItem will be returned.
+ def getacl(mailbox)
+ synchronize do
+ send_command("GETACL", mailbox)
+ return @responses.delete("ACL")[-1]
+ end
+ end
+
+ # Sends a LSUB command, and returns a subset of names from the set
+ # of names that the user has declared as being "active" or
+ # "subscribed". +refname+ and +mailbox+ are interpreted as
+ # for #list().
+ # The return value is an array of +Net::IMAP::MailboxList+.
+ def lsub(refname, mailbox)
+ synchronize do
+ send_command("LSUB", refname, mailbox)
+ return @responses.delete("LSUB")
+ end
+ end
+
+ # Sends a STATUS command, and returns the status of the indicated
+ # +mailbox+. +attr+ is a list of one or more attributes that
+ # we are request the status of. Supported attributes include:
+ #
+ # MESSAGES:: the number of messages in the mailbox.
+ # RECENT:: the number of recent messages in the mailbox.
+ # UNSEEN:: the number of unseen messages in the mailbox.
+ #
+ # The return value is a hash of attributes. For example:
+ #
+ # p imap.status("inbox", ["MESSAGES", "RECENT"])
+ # #=> {"RECENT"=>0, "MESSAGES"=>44}
+ #
+ # A Net::IMAP::NoResponseError is raised if status values
+ # for +mailbox+ cannot be returned, for instance because it
+ # does not exist.
+ def status(mailbox, attr)
+ synchronize do
+ send_command("STATUS", mailbox, attr)
+ return @responses.delete("STATUS")[-1].attr
+ end
+ end
+
+ # Sends a APPEND command to append the +message+ to the end of
+ # the +mailbox+. The optional +flags+ argument is an array of
+ # flags to initially passing to the new message. The optional
+ # +date_time+ argument specifies the creation time to assign to the
+ # new message; it defaults to the current time.
+ # For example:
+ #
+ # imap.append("inbox", <<EOF.gsub(/\n/, "\r\n"), [:Seen], Time.now)
+ # Subject: hello
+ # From: shugo@ruby-lang.org
+ # To: shugo@ruby-lang.org
+ #
+ # hello world
+ # EOF
+ #
+ # A Net::IMAP::NoResponseError is raised if the mailbox does
+ # not exist (it is not created automatically), or if the flags,
+ # date_time, or message arguments contain errors.
+ def append(mailbox, message, flags = nil, date_time = nil)
+ args = []
+ if flags
+ args.push(flags)
+ end
+ args.push(date_time) if date_time
+ args.push(Literal.new(message))
+ send_command("APPEND", mailbox, *args)
+ end
+
+ # Sends a CHECK command to request a checkpoint of the currently
+ # selected mailbox. This performs implementation-specific
+ # housekeeping, for instance, reconciling the mailbox's
+ # in-memory and on-disk state.
+ def check
+ send_command("CHECK")
+ end
+
+ # Sends a CLOSE command to close the currently selected mailbox.
+ # The CLOSE command permanently removes from the mailbox all
+ # messages that have the \Deleted flag set.
+ def close
+ send_command("CLOSE")
+ end
+
+ # Sends a EXPUNGE command to permanently remove from the currently
+ # selected mailbox all messages that have the \Deleted flag set.
+ def expunge
+ synchronize do
+ send_command("EXPUNGE")
+ return @responses.delete("EXPUNGE")
+ end
+ end
+
+ # Sends a SEARCH command to search the mailbox for messages that
+ # match the given searching criteria, and returns message sequence
+ # numbers. +keys+ can either be a string holding the entire
+ # search string, or a single-dimension array of search keywords and
+ # arguments. The following are some common search criteria;
+ # see [IMAP] section 6.4.4 for a full list.
+ #
+ # <message set>:: a set of message sequence numbers. ',' indicates
+ # an interval, ':' indicates a range. For instance,
+ # '2,10:12,15' means "2,10,11,12,15".
+ #
+ # BEFORE <date>:: messages with an internal date strictly before
+ # <date>. The date argument has a format similar
+ # to 8-Aug-2002.
+ #
+ # BODY <string>:: messages that contain <string> within their body.
+ #
+ # CC <string>:: messages containing <string> in their CC field.
+ #
+ # FROM <string>:: messages that contain <string> in their FROM field.
+ #
+ # NEW:: messages with the \Recent, but not the \Seen, flag set.
+ #
+ # NOT <search-key>:: negate the following search key.
+ #
+ # OR <search-key> <search-key>:: "or" two search keys together.
+ #
+ # ON <date>:: messages with an internal date exactly equal to <date>,
+ # which has a format similar to 8-Aug-2002.
+ #
+ # SINCE <date>:: messages with an internal date on or after <date>.
+ #
+ # SUBJECT <string>:: messages with <string> in their subject.
+ #
+ # TO <string>:: messages with <string> in their TO field.
+ #
+ # For example:
+ #
+ # p imap.search(["SUBJECT", "hello", "NOT", "NEW"])
+ # #=> [1, 6, 7, 8]
+ def search(keys, charset = nil)
+ return search_internal("SEARCH", keys, charset)
+ end
+
+ # As for #search(), but returns unique identifiers.
+ def uid_search(keys, charset = nil)
+ return search_internal("UID SEARCH", keys, charset)
+ end
+
+ # Sends a FETCH command to retrieve data associated with a message
+ # in the mailbox. The +set+ parameter is a number or an array of
+ # numbers or a Range object. The number is a message sequence
+ # number. +attr+ is a list of attributes to fetch; see the
+ # documentation for Net::IMAP::FetchData for a list of valid
+ # attributes.
+ # The return value is an array of Net::IMAP::FetchData. For example:
+ #
+ # p imap.fetch(6..8, "UID")
+ # #=> [#<Net::IMAP::FetchData seqno=6, attr={"UID"=>98}>, \\
+ # #<Net::IMAP::FetchData seqno=7, attr={"UID"=>99}>, \\
+ # #<Net::IMAP::FetchData seqno=8, attr={"UID"=>100}>]
+ # p imap.fetch(6, "BODY[HEADER.FIELDS (SUBJECT)]")
+ # #=> [#<Net::IMAP::FetchData seqno=6, attr={"BODY[HEADER.FIELDS (SUBJECT)]"=>"Subject: test\r\n\r\n"}>]
+ # data = imap.uid_fetch(98, ["RFC822.SIZE", "INTERNALDATE"])[0]
+ # p data.seqno
+ # #=> 6
+ # p data.attr["RFC822.SIZE"]
+ # #=> 611
+ # p data.attr["INTERNALDATE"]
+ # #=> "12-Oct-2000 22:40:59 +0900"
+ # p data.attr["UID"]
+ # #=> 98
+ def fetch(set, attr)
+ return fetch_internal("FETCH", set, attr)
+ end
+
+ # As for #fetch(), but +set+ contains unique identifiers.
+ def uid_fetch(set, attr)
+ return fetch_internal("UID FETCH", set, attr)
+ end
+
+ # Sends a STORE command to alter data associated with messages
+ # in the mailbox, in particular their flags. The +set+ parameter
+ # is a number or an array of numbers or a Range object. Each number
+ # is a message sequence number. +attr+ is the name of a data item
+ # to store: 'FLAGS' means to replace the message's flag list
+ # with the provided one; '+FLAGS' means to add the provided flags;
+ # and '-FLAGS' means to remove them. +flags+ is a list of flags.
+ #
+ # The return value is an array of Net::IMAP::FetchData. For example:
+ #
+ # p imap.store(6..8, "+FLAGS", [:Deleted])
+ # #=> [#<Net::IMAP::FetchData seqno=6, attr={"FLAGS"=>[:Seen, :Deleted]}>, \\
+ # #<Net::IMAP::FetchData seqno=7, attr={"FLAGS"=>[:Seen, :Deleted]}>, \\
+ # #<Net::IMAP::FetchData seqno=8, attr={"FLAGS"=>[:Seen, :Deleted]}>]
+ def store(set, attr, flags)
+ return store_internal("STORE", set, attr, flags)
+ end
+
+ # As for #store(), but +set+ contains unique identifiers.
+ def uid_store(set, attr, flags)
+ return store_internal("UID STORE", set, attr, flags)
+ end
+
+ # Sends a COPY command to copy the specified message(s) to the end
+ # of the specified destination +mailbox+. The +set+ parameter is
+ # a number or an array of numbers or a Range object. The number is
+ # a message sequence number.
+ def copy(set, mailbox)
+ copy_internal("COPY", set, mailbox)
+ end
+
+ # As for #copy(), but +set+ contains unique identifiers.
+ def uid_copy(set, mailbox)
+ copy_internal("UID COPY", set, mailbox)
+ end
+
+ # Sends a SORT command to sort messages in the mailbox.
+ # Returns an array of message sequence numbers. For example:
+ #
+ # p imap.sort(["FROM"], ["ALL"], "US-ASCII")
+ # #=> [1, 2, 3, 5, 6, 7, 8, 4, 9]
+ # p imap.sort(["DATE"], ["SUBJECT", "hello"], "US-ASCII")
+ # #=> [6, 7, 8, 1]
+ #
+ # See [SORT-THREAD-EXT] for more details.
+ def sort(sort_keys, search_keys, charset)
+ return sort_internal("SORT", sort_keys, search_keys, charset)
+ end
+
+ # As for #sort(), but returns an array of unique identifiers.
+ def uid_sort(sort_keys, search_keys, charset)
+ return sort_internal("UID SORT", sort_keys, search_keys, charset)
+ end
+
+ # Adds a response handler. For example, to detect when
+ # the server sends us a new EXISTS response (which normally
+ # indicates new messages being added to the mail box),
+ # you could add the following handler after selecting the
+ # mailbox.
+ #
+ # imap.add_response_handler { |resp|
+ # if resp.kind_of?(Net::IMAP::UntaggedResponse) and resp.name == "EXISTS"
+ # puts "Mailbox now has #{resp.data} messages"
+ # end
+ # }
+ #
+ def add_response_handler(handler = Proc.new)
+ @response_handlers.push(handler)
+ end
+
+ # Removes the response handler.
+ def remove_response_handler(handler)
+ @response_handlers.delete(handler)
+ end
+
+ # As for #search(), but returns message sequence numbers in threaded
+ # format, as a Net::IMAP::ThreadMember tree. The supported algorithms
+ # are:
+ #
+ # ORDEREDSUBJECT:: split into single-level threads according to subject,
+ # ordered by date.
+ # REFERENCES:: split into threads by parent/child relationships determined
+ # by which message is a reply to which.
+ #
+ # Unlike #search(), +charset+ is a required argument. US-ASCII
+ # and UTF-8 are sample values.
+ #
+ # See [SORT-THREAD-EXT] for more details.
+ def thread(algorithm, search_keys, charset)
+ return thread_internal("THREAD", algorithm, search_keys, charset)
+ end
+
+ # As for #thread(), but returns unique identifiers instead of
+ # message sequence numbers.
+ def uid_thread(algorithm, search_keys, charset)
+ return thread_internal("UID THREAD", algorithm, search_keys, charset)
+ end
+
+ # Decode a string from modified UTF-7 format to UTF-8.
+ #
+ # UTF-7 is a 7-bit encoding of Unicode [UTF7]. IMAP uses a
+ # slightly modified version of this to encode mailbox names
+ # containing non-ASCII characters; see [IMAP] section 5.1.3.
+ #
+ # Net::IMAP does _not_ automatically encode and decode
+ # mailbox names to and from utf7.
+ def self.decode_utf7(s)
+ return s.gsub(/&(.*?)-/n) {
+ if $1.empty?
+ "&"
+ else
+ base64 = $1.tr(",", "/")
+ x = base64.length % 4
+ if x > 0
+ base64.concat("=" * (4 - x))
+ end
+ u16tou8(base64.unpack("m")[0])
+ end
+ }
+ end
+
+ # Encode a string from UTF-8 format to modified UTF-7.
+ def self.encode_utf7(s)
+ return s.gsub(/(&)|([^\x20-\x25\x27-\x7e]+)/n) { |x|
+ if $1
+ "&-"
+ else
+ base64 = [u8tou16(x)].pack("m")
+ "&" + base64.delete("=\n").tr("/", ",") + "-"
+ end
+ }
+ end
+
+ private
+
+ CRLF = "\r\n" # :nodoc:
+ PORT = 143 # :nodoc:
+
+ @@debug = false
+ @@authenticators = {}
+
+ # Creates a new Net::IMAP object and connects it to the specified
+ # +port+ (143 by default) on the named +host+. If +usessl+ is true,
+ # then an attempt will
+ # be made to use SSL (now TLS) to connect to the server. For this
+ # to work OpenSSL [OSSL] and the Ruby OpenSSL [RSSL]
+ # extensions need to be installed. The +certs+ parameter indicates
+ # the path or file containing the CA cert of the server, and the
+ # +verify+ parameter is for the OpenSSL verification callback.
+ #
+ # The most common errors are:
+ #
+ # Errno::ECONNREFUSED:: connection refused by +host+ or an intervening
+ # firewall.
+ # Errno::ETIMEDOUT:: connection timed out (possibly due to packets
+ # being dropped by an intervening firewall).
+ # Errno::ENETUNREACH:: there is no route to that network.
+ # SocketError:: hostname not known or other socket error.
+ # Net::IMAP::ByeResponseError:: we connected to the host, but they
+ # immediately said goodbye to us.
+ def initialize(host, port = PORT, usessl = false, certs = nil, verify = false)
+ super()
+ @host = host
+ @port = port
+ @tag_prefix = "RUBY"
+ @tagno = 0
+ @parser = ResponseParser.new
+ @sock = TCPSocket.open(host, port)
+ if usessl
+ unless defined?(OpenSSL)
+ raise "SSL extension not installed"
+ end
+ @usessl = true
+
+ # verify the server.
+ context = SSLContext::new()
+ context.ca_file = certs if certs && FileTest::file?(certs)
+ context.ca_path = certs if certs && FileTest::directory?(certs)
+ context.verify_mode = VERIFY_PEER if verify
+ if defined?(VerifyCallbackProc)
+ context.verify_callback = VerifyCallbackProc
+ end
+ @sock = SSLSocket.new(@sock, context)
+ @sock.connect # start ssl session.
+ else
+ @usessl = false
+ end
+ @responses = Hash.new([].freeze)
+ @tagged_responses = {}
+ @response_handlers = []
+ @tagged_response_arrival = new_cond
+ @continuation_request_arrival = new_cond
+ @logout_command_tag = nil
+ @debug_output_bol = true
+
+ @greeting = get_response
+ if @greeting.name == "BYE"
+ @sock.close
+ raise ByeResponseError, @greeting.raw_data
+ end
+
+ @client_thread = Thread.current
+ @receiver_thread = Thread.start {
+ receive_responses
+ }
+ end
+
+ def receive_responses
+ while true
+ begin
+ resp = get_response
+ rescue Exception
+ @sock.close
+ @client_thread.raise($!)
+ break
+ end
+ break unless resp
+ begin
+ synchronize do
+ case resp
+ when TaggedResponse
+ @tagged_responses[resp.tag] = resp
+ @tagged_response_arrival.broadcast
+ if resp.tag == @logout_command_tag
+ return
+ end
+ when UntaggedResponse
+ record_response(resp.name, resp.data)
+ if resp.data.instance_of?(ResponseText) &&
+ (code = resp.data.code)
+ record_response(code.name, code.data)
+ end
+ if resp.name == "BYE" && @logout_command_tag.nil?
+ @sock.close
+ raise ByeResponseError, resp.raw_data
+ end
+ when ContinuationRequest
+ @continuation_request_arrival.signal
+ end
+ @response_handlers.each do |handler|
+ handler.call(resp)
+ end
+ end
+ rescue Exception
+ @client_thread.raise($!)
+ end
+ end
+ end
+
+ def get_tagged_response(tag, cmd)
+ until @tagged_responses.key?(tag)
+ @tagged_response_arrival.wait
+ end
+ resp = @tagged_responses.delete(tag)
+ case resp.name
+ when /\A(?:NO)\z/ni
+ raise NoResponseError, resp.data.text
+ when /\A(?:BAD)\z/ni
+ raise BadResponseError, resp.data.text
+ else
+ return resp
+ end
+ end
+
+ def get_response
+ buff = ""
+ while true
+ s = @sock.gets(CRLF)
+ break unless s
+ buff.concat(s)
+ if /\{(\d+)\}\r\n/n =~ s
+ s = @sock.read($1.to_i)
+ buff.concat(s)
+ else
+ break
+ end
+ end
+ return nil if buff.length == 0
+ if @@debug
+ $stderr.print(buff.gsub(/^/n, "S: "))
+ end
+ return @parser.parse(buff)
+ end
+
+ def record_response(name, data)
+ unless @responses.has_key?(name)
+ @responses[name] = []
+ end
+ @responses[name].push(data)
+ end
+
+ def send_command(cmd, *args, &block)
+ synchronize do
+ tag = generate_tag
+ put_string(tag + " " + cmd)
+ args.each do |i|
+ put_string(" ")
+ send_data(i)
+ end
+ put_string(CRLF)
+ if cmd == "LOGOUT"
+ @logout_command_tag = tag
+ end
+ if block
+ add_response_handler(block)
+ end
+ begin
+ return get_tagged_response(tag, cmd)
+ ensure
+ if block
+ remove_response_handler(block)
+ end
+ end
+ end
+ end
+
+ def generate_tag
+ @tagno += 1
+ return format("%s%04d", @tag_prefix, @tagno)
+ end
+
+ def put_string(str)
+ @sock.print(str)
+ if @@debug
+ if @debug_output_bol
+ $stderr.print("C: ")
+ end
+ $stderr.print(str.gsub(/\n(?!\z)/n, "\nC: "))
+ if /\r\n\z/n.match(str)
+ @debug_output_bol = true
+ else
+ @debug_output_bol = false
+ end
+ end
+ end
+
+ def send_data(data)
+ case data
+ when nil
+ put_string("NIL")
+ when String
+ send_string_data(data)
+ when Integer
+ send_number_data(data)
+ when Array
+ send_list_data(data)
+ when Time
+ send_time_data(data)
+ when Symbol
+ send_symbol_data(data)
+ else
+ data.send_data(self)
+ end
+ end
+
+ def send_string_data(str)
+ case str
+ when ""
+ put_string('""')
+ when /[\x80-\xff\r\n]/n
+ # literal
+ send_literal(str)
+ when /[(){ \x00-\x1f\x7f%*"\\]/n
+ # quoted string
+ send_quoted_string(str)
+ else
+ put_string(str)
+ end
+ end
+
+ def send_quoted_string(str)
+ put_string('"' + str.gsub(/["\\]/n, "\\\\\\&") + '"')
+ end
+
+ def send_literal(str)
+ put_string("{" + str.length.to_s + "}" + CRLF)
+ @continuation_request_arrival.wait
+ put_string(str)
+ end
+
+ def send_number_data(num)
+ if num < 0 || num >= 4294967296
+ raise DataFormatError, num.to_s
+ end
+ put_string(num.to_s)
+ end
+
+ def send_list_data(list)
+ put_string("(")
+ first = true
+ list.each do |i|
+ if first
+ first = false
+ else
+ put_string(" ")
+ end
+ send_data(i)
+ end
+ put_string(")")
+ end
+
+ DATE_MONTH = %w(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)
+
+ def send_time_data(time)
+ t = time.dup.gmtime
+ s = format('"%2d-%3s-%4d %02d:%02d:%02d +0000"',
+ t.day, DATE_MONTH[t.month - 1], t.year,
+ t.hour, t.min, t.sec)
+ put_string(s)
+ end
+
+ def send_symbol_data(symbol)
+ put_string("\\" + symbol.to_s)
+ end
+
+ def search_internal(cmd, keys, charset)
+ if keys.instance_of?(String)
+ keys = [RawData.new(keys)]
+ else
+ normalize_searching_criteria(keys)
+ end
+ synchronize do
+ if charset
+ send_command(cmd, "CHARSET", charset, *keys)
+ else
+ send_command(cmd, *keys)
+ end
+ return @responses.delete("SEARCH")[-1]
+ end
+ end
+
+ def fetch_internal(cmd, set, attr)
+ if attr.instance_of?(String)
+ attr = RawData.new(attr)
+ end
+ synchronize do
+ @responses.delete("FETCH")
+ send_command(cmd, MessageSet.new(set), attr)
+ return @responses.delete("FETCH")
+ end
+ end
+
+ def store_internal(cmd, set, attr, flags)
+ if attr.instance_of?(String)
+ attr = RawData.new(attr)
+ end
+ synchronize do
+ @responses.delete("FETCH")
+ send_command(cmd, MessageSet.new(set), attr, flags)
+ return @responses.delete("FETCH")
+ end
+ end
+
+ def copy_internal(cmd, set, mailbox)
+ send_command(cmd, MessageSet.new(set), mailbox)
+ end
+
+ def sort_internal(cmd, sort_keys, search_keys, charset)
+ if search_keys.instance_of?(String)
+ search_keys = [RawData.new(search_keys)]
+ else
+ normalize_searching_criteria(search_keys)
+ end
+ normalize_searching_criteria(search_keys)
+ synchronize do
+ send_command(cmd, sort_keys, charset, *search_keys)
+ return @responses.delete("SORT")[-1]
+ end
+ end
+
+ def thread_internal(cmd, algorithm, search_keys, charset)
+ if search_keys.instance_of?(String)
+ search_keys = [RawData.new(search_keys)]
+ else
+ normalize_searching_criteria(search_keys)
+ end
+ normalize_searching_criteria(search_keys)
+ send_command(cmd, algorithm, charset, *search_keys)
+ return @responses.delete("THREAD")[-1]
+ end
+
+ def normalize_searching_criteria(keys)
+ keys.collect! do |i|
+ case i
+ when -1, Range, Array
+ MessageSet.new(i)
+ else
+ i
+ end
+ end
+ end
+
+ def self.u16tou8(s)
+ len = s.length
+ if len < 2
+ return ""
+ end
+ buf = ""
+ i = 0
+ while i < len
+ c = s[i] << 8 | s[i + 1]
+ i += 2
+ if c == 0xfeff
+ next
+ elsif c < 0x0080
+ buf.concat(c)
+ elsif c < 0x0800
+ b2 = c & 0x003f
+ b1 = c >> 6
+ buf.concat(b1 | 0xc0)
+ buf.concat(b2 | 0x80)
+ elsif c >= 0xdc00 && c < 0xe000
+ raise DataFormatError, "invalid surrogate detected"
+ elsif c >= 0xd800 && c < 0xdc00
+ if i + 2 > len
+ raise DataFormatError, "invalid surrogate detected"
+ end
+ low = s[i] << 8 | s[i + 1]
+ i += 2
+ if low < 0xdc00 || low > 0xdfff
+ raise DataFormatError, "invalid surrogate detected"
+ end
+ c = (((c & 0x03ff)) << 10 | (low & 0x03ff)) + 0x10000
+ b4 = c & 0x003f
+ b3 = (c >> 6) & 0x003f
+ b2 = (c >> 12) & 0x003f
+ b1 = c >> 18;
+ buf.concat(b1 | 0xf0)
+ buf.concat(b2 | 0x80)
+ buf.concat(b3 | 0x80)
+ buf.concat(b4 | 0x80)
+ else # 0x0800-0xffff
+ b3 = c & 0x003f
+ b2 = (c >> 6) & 0x003f
+ b1 = c >> 12
+ buf.concat(b1 | 0xe0)
+ buf.concat(b2 | 0x80)
+ buf.concat(b3 | 0x80)
+ end
+ end
+ return buf
+ end
+ private_class_method :u16tou8
+
+ def self.u8tou16(s)
+ len = s.length
+ buf = ""
+ i = 0
+ while i < len
+ c = s[i]
+ if (c & 0x80) == 0
+ buf.concat(0x00)
+ buf.concat(c)
+ i += 1
+ elsif (c & 0xe0) == 0xc0 &&
+ len >= 2 &&
+ (s[i + 1] & 0xc0) == 0x80
+ if c == 0xc0 || c == 0xc1
+ raise DataFormatError, format("non-shortest UTF-8 sequence (%02x)", c)
+ end
+ u = ((c & 0x1f) << 6) | (s[i + 1] & 0x3f)
+ buf.concat(u >> 8)
+ buf.concat(u & 0x00ff)
+ i += 2
+ elsif (c & 0xf0) == 0xe0 &&
+ i + 2 < len &&
+ (s[i + 1] & 0xc0) == 0x80 &&
+ (s[i + 2] & 0xc0) == 0x80
+ if c == 0xe0 && s[i + 1] < 0xa0
+ raise DataFormatError, format("non-shortest UTF-8 sequence (%02x)", c)
+ end
+ u = ((c & 0x0f) << 12) | ((s[i + 1] & 0x3f) << 6) | (s[i + 2] & 0x3f)
+ # surrogate chars
+ if u >= 0xd800 && u <= 0xdfff
+ raise DataFormatError, format("none-UTF-16 char detected (%04x)", u)
+ end
+ buf.concat(u >> 8)
+ buf.concat(u & 0x00ff)
+ i += 3
+ elsif (c & 0xf8) == 0xf0 &&
+ i + 3 < len &&
+ (s[i + 1] & 0xc0) == 0x80 &&
+ (s[i + 2] & 0xc0) == 0x80 &&
+ (s[i + 3] & 0xc0) == 0x80
+ if c == 0xf0 && s[i + 1] < 0x90
+ raise DataFormatError, format("non-shortest UTF-8 sequence (%02x)", c)
+ end
+ u = ((c & 0x07) << 18) | ((s[i + 1] & 0x3f) << 12) |
+ ((s[i + 2] & 0x3f) << 6) | (s[i + 3] & 0x3f)
+ if u < 0x10000
+ buf.concat(u >> 8)
+ buf.concat(u & 0x00ff)
+ elsif u < 0x110000
+ high = ((u - 0x10000) >> 10) | 0xd800
+ low = (u & 0x03ff) | 0xdc00
+ buf.concat(high >> 8)
+ buf.concat(high & 0x00ff)
+ buf.concat(low >> 8)
+ buf.concat(low & 0x00ff)
+ else
+ raise DataFormatError, format("none-UTF-16 char detected (%04x)", u)
+ end
+ i += 4
+ else
+ raise DataFormatError, format("illegal UTF-8 sequence (%02x)", c)
+ end
+ end
+ return buf
+ end
+ private_class_method :u8tou16
+
+ class RawData # :nodoc:
+ def send_data(imap)
+ imap.send(:put_string, @data)
+ end
+
+ private
+
+ def initialize(data)
+ @data = data
+ end
+ end
+
+ class Atom # :nodoc:
+ def send_data(imap)
+ imap.send(:put_string, @data)
+ end
+
+ private
+
+ def initialize(data)
+ @data = data
+ end
+ end
+
+ class QuotedString # :nodoc:
+ def send_data(imap)
+ imap.send(:send_quoted_string, @data)
+ end
+
+ private
+
+ def initialize(data)
+ @data = data
+ end
+ end
+
+ class Literal # :nodoc:
+ def send_data(imap)
+ imap.send(:send_literal, @data)
+ end
+
+ private
+
+ def initialize(data)
+ @data = data
+ end
+ end
+
+ class MessageSet # :nodoc:
+ def send_data(imap)
+ imap.send(:put_string, format_internal(@data))
+ end
+
+ private
+
+ def initialize(data)
+ @data = data
+ end
+
+ def format_internal(data)
+ case data
+ when "*"
+ return data
+ when Integer
+ ensure_nz_number(data)
+ if data == -1
+ return "*"
+ else
+ return data.to_s
+ end
+ when Range
+ return format_internal(data.first) +
+ ":" + format_internal(data.last)
+ when Array
+ return data.collect {|i| format_internal(i)}.join(",")
+ when ThreadMember
+ return data.seqno.to_s +
+ ":" + data.children.collect {|i| format_internal(i).join(",")}
+ else
+ raise DataFormatError, data.inspect
+ end
+ end
+
+ def ensure_nz_number(num)
+ if num < -1 || num == 0 || num >= 4294967296
+ msg = "nz_number must be non-zero unsigned 32-bit integer: " +
+ num.inspect
+ raise DataFormatError, msg
+ end
+ end
+ end
+
+ # Net::IMAP::ContinuationRequest represents command continuation requests.
+ #
+ # The command continuation request response is indicated by a "+" token
+ # instead of a tag. This form of response indicates that the server is
+ # ready to accept the continuation of a command from the client. The
+ # remainder of this response is a line of text.
+ #
+ # continue_req ::= "+" SPACE (resp_text / base64)
+ #
+ # ==== Fields:
+ #
+ # data:: Returns the data (Net::IMAP::ResponseText).
+ #
+ # raw_data:: Returns the raw data string.
+ ContinuationRequest = Struct.new(:data, :raw_data)
+
+ # Net::IMAP::UntaggedResponse represents untagged responses.
+ #
+ # Data transmitted by the server to the client and status responses
+ # that do not indicate command completion are prefixed with the token
+ # "*", and are called untagged responses.
+ #
+ # response_data ::= "*" SPACE (resp_cond_state / resp_cond_bye /
+ # mailbox_data / message_data / capability_data)
+ #
+ # ==== Fields:
+ #
+ # name:: Returns the name such as "FLAGS", "LIST", "FETCH"....
+ #
+ # data:: Returns the data such as an array of flag symbols,
+ # a ((<Net::IMAP::MailboxList>)) object....
+ #
+ # raw_data:: Returns the raw data string.
+ UntaggedResponse = Struct.new(:name, :data, :raw_data)
+
+ # Net::IMAP::TaggedResponse represents tagged responses.
+ #
+ # The server completion result response indicates the success or
+ # failure of the operation. It is tagged with the same tag as the
+ # client command which began the operation.
+ #
+ # response_tagged ::= tag SPACE resp_cond_state CRLF
+ #
+ # tag ::= 1*<any ATOM_CHAR except "+">
+ #
+ # resp_cond_state ::= ("OK" / "NO" / "BAD") SPACE resp_text
+ #
+ # ==== Fields:
+ #
+ # tag:: Returns the tag.
+ #
+ # name:: Returns the name. the name is one of "OK", "NO", "BAD".
+ #
+ # data:: Returns the data. See ((<Net::IMAP::ResponseText>)).
+ #
+ # raw_data:: Returns the raw data string.
+ #
+ TaggedResponse = Struct.new(:tag, :name, :data, :raw_data)
+
+ # Net::IMAP::ResponseText represents texts of responses.
+ # The text may be prefixed by the response code.
+ #
+ # resp_text ::= ["[" resp_text_code "]" SPACE] (text_mime2 / text)
+ # ;; text SHOULD NOT begin with "[" or "="
+ #
+ # ==== Fields:
+ #
+ # code:: Returns the response code. See ((<Net::IMAP::ResponseCode>)).
+ #
+ # text:: Returns the text.
+ #
+ ResponseText = Struct.new(:code, :text)
+
+ #
+ # Net::IMAP::ResponseCode represents response codes.
+ #
+ # resp_text_code ::= "ALERT" / "PARSE" /
+ # "PERMANENTFLAGS" SPACE "(" #(flag / "\*") ")" /
+ # "READ-ONLY" / "READ-WRITE" / "TRYCREATE" /
+ # "UIDVALIDITY" SPACE nz_number /
+ # "UNSEEN" SPACE nz_number /
+ # atom [SPACE 1*<any TEXT_CHAR except "]">]
+ #
+ # ==== Fields:
+ #
+ # name:: Returns the name such as "ALERT", "PERMANENTFLAGS", "UIDVALIDITY"....
+ #
+ # data:: Returns the data if it exists.
+ #
+ ResponseCode = Struct.new(:name, :data)
+
+ # Net::IMAP::MailboxList represents contents of the LIST response.
+ #
+ # mailbox_list ::= "(" #("\Marked" / "\Noinferiors" /
+ # "\Noselect" / "\Unmarked" / flag_extension) ")"
+ # SPACE (<"> QUOTED_CHAR <"> / nil) SPACE mailbox
+ #
+ # ==== Fields:
+ #
+ # attr:: Returns the name attributes. Each name attribute is a symbol
+ # capitalized by String#capitalize, such as :Noselect (not :NoSelect).
+ #
+ # delim:: Returns the hierarchy delimiter
+ #
+ # name:: Returns the mailbox name.
+ #
+ MailboxList = Struct.new(:attr, :delim, :name)
+
+ # Net::IMAP::MailboxQuota represents contents of GETQUOTA response.
+ # This object can also be a response to GETQUOTAROOT. In the syntax
+ # specification below, the delimiter used with the "#" construct is a
+ # single space (SPACE).
+ #
+ # quota_list ::= "(" #quota_resource ")"
+ #
+ # quota_resource ::= atom SPACE number SPACE number
+ #
+ # quota_response ::= "QUOTA" SPACE astring SPACE quota_list
+ #
+ # ==== Fields:
+ #
+ # mailbox:: The mailbox with the associated quota.
+ #
+ # usage:: Current storage usage of mailbox.
+ #
+ # quota:: Quota limit imposed on mailbox.
+ #
+ MailboxQuota = Struct.new(:mailbox, :usage, :quota)
+
+ # Net::IMAP::MailboxQuotaRoot represents part of the GETQUOTAROOT
+ # response. (GETQUOTAROOT can also return Net::IMAP::MailboxQuota.)
+ #
+ # quotaroot_response ::= "QUOTAROOT" SPACE astring *(SPACE astring)
+ #
+ # ==== Fields:
+ #
+ # mailbox:: The mailbox with the associated quota.
+ #
+ # quotaroots:: Zero or more quotaroots that effect the quota on the
+ # specified mailbox.
+ #
+ MailboxQuotaRoot = Struct.new(:mailbox, :quotaroots)
+
+ # Net::IMAP::MailboxACLItem represents response from GETACL.
+ #
+ # acl_data ::= "ACL" SPACE mailbox *(SPACE identifier SPACE rights)
+ #
+ # identifier ::= astring
+ #
+ # rights ::= astring
+ #
+ # ==== Fields:
+ #
+ # user:: Login name that has certain rights to the mailbox
+ # that was specified with the getacl command.
+ #
+ # rights:: The access rights the indicated user has to the
+ # mailbox.
+ #
+ MailboxACLItem = Struct.new(:user, :rights)
+
+ # Net::IMAP::StatusData represents contents of the STATUS response.
+ #
+ # ==== Fields:
+ #
+ # mailbox:: Returns the mailbox name.
+ #
+ # attr:: Returns a hash. Each key is one of "MESSAGES", "RECENT", "UIDNEXT",
+ # "UIDVALIDITY", "UNSEEN". Each value is a number.
+ #
+ StatusData = Struct.new(:mailbox, :attr)
+
+ # Net::IMAP::FetchData represents contents of the FETCH response.
+ #
+ # ==== Fields:
+ #
+ # seqno:: Returns the message sequence number.
+ # (Note: not the unique identifier, even for the UID command response.)
+ #
+ # attr:: Returns a hash. Each key is a data item name, and each value is
+ # its value.
+ #
+ # The current data items are:
+ #
+ # [BODY]
+ # A form of BODYSTRUCTURE without extension data.
+ # [BODY[<section>]<<origin_octet>>]
+ # A string expressing the body contents of the specified section.
+ # [BODYSTRUCTURE]
+ # An object that describes the [MIME-IMB] body structure of a message.
+ # See Net::IMAP::BodyTypeBasic, Net::IMAP::BodyTypeText,
+ # Net::IMAP::BodyTypeMessage, Net::IMAP::BodyTypeMultipart.
+ # [ENVELOPE]
+ # A Net::IMAP::Envelope object that describes the envelope
+ # structure of a message.
+ # [FLAGS]
+ # A array of flag symbols that are set for this message. flag symbols
+ # are capitalized by String#capitalize.
+ # [INTERNALDATE]
+ # A string representing the internal date of the message.
+ # [RFC822]
+ # Equivalent to BODY[].
+ # [RFC822.HEADER]
+ # Equivalent to BODY.PEEK[HEADER].
+ # [RFC822.SIZE]
+ # A number expressing the [RFC-822] size of the message.
+ # [RFC822.TEXT]
+ # Equivalent to BODY[TEXT].
+ # [UID]
+ # A number expressing the unique identifier of the message.
+ #
+ FetchData = Struct.new(:seqno, :attr)
+
+ # Net::IMAP::Envelope represents envelope structures of messages.
+ #
+ # ==== Fields:
+ #
+ # date:: Returns a string that represents the date.
+ #
+ # subject:: Returns a string that represents the subject.
+ #
+ # from:: Returns an array of Net::IMAP::Address that represents the from.
+ #
+ # sender:: Returns an array of Net::IMAP::Address that represents the sender.
+ #
+ # reply_to:: Returns an array of Net::IMAP::Address that represents the reply-to.
+ #
+ # to:: Returns an array of Net::IMAP::Address that represents the to.
+ #
+ # cc:: Returns an array of Net::IMAP::Address that represents the cc.
+ #
+ # bcc:: Returns an array of Net::IMAP::Address that represents the bcc.
+ #
+ # in_reply_to:: Returns a string that represents the in-reply-to.
+ #
+ # message_id:: Returns a string that represents the message-id.
+ #
+ Envelope = Struct.new(:date, :subject, :from, :sender, :reply_to,
+ :to, :cc, :bcc, :in_reply_to, :message_id)
+
+ #
+ # Net::IMAP::Address represents electronic mail addresses.
+ #
+ # ==== Fields:
+ #
+ # name:: Returns the phrase from [RFC-822] mailbox.
+ #
+ # route:: Returns the route from [RFC-822] route-addr.
+ #
+ # mailbox:: nil indicates end of [RFC-822] group.
+ # If non-nil and host is nil, returns [RFC-822] group name.
+ # Otherwise, returns [RFC-822] local-part
+ #
+ # host:: nil indicates [RFC-822] group syntax.
+ # Otherwise, returns [RFC-822] domain name.
+ #
+ Address = Struct.new(:name, :route, :mailbox, :host)
+
+ #
+ # Net::IMAP::ContentDisposition represents Content-Disposition fields.
+ #
+ # ==== Fields:
+ #
+ # dsp_type:: Returns the disposition type.
+ #
+ # param:: Returns a hash that represents parameters of the Content-Disposition
+ # field.
+ #
+ ContentDisposition = Struct.new(:dsp_type, :param)
+
+ # Net::IMAP::ThreadMember represents a thread-node returned
+ # by Net::IMAP#thread
+ #
+ # ==== Fields:
+ #
+ # seqno:: The sequence number of this message.
+ #
+ # children:: an array of Net::IMAP::ThreadMember objects for mail
+ # items that are children of this in the thread.
+ #
+ ThreadMember = Struct.new(:seqno, :children)
+
+ # Net::IMAP::BodyTypeBasic represents basic body structures of messages.
+ #
+ # ==== Fields:
+ #
+ # media_type:: Returns the content media type name as defined in [MIME-IMB].
+ #
+ # subtype:: Returns the content subtype name as defined in [MIME-IMB].
+ #
+ # param:: Returns a hash that represents parameters as defined in [MIME-IMB].
+ #
+ # content_id:: Returns a string giving the content id as defined in [MIME-IMB].
+ #
+ # description:: Returns a string giving the content description as defined in
+ # [MIME-IMB].
+ #
+ # encoding:: Returns a string giving the content transfer encoding as defined in
+ # [MIME-IMB].
+ #
+ # size:: Returns a number giving the size of the body in octets.
+ #
+ # md5:: Returns a string giving the body MD5 value as defined in [MD5].
+ #
+ # disposition:: Returns a Net::IMAP::ContentDisposition object giving
+ # the content disposition.
+ #
+ # language:: Returns a string or an array of strings giving the body
+ # language value as defined in [LANGUAGE-TAGS].
+ #
+ # extension:: Returns extension data.
+ #
+ # multipart?:: Returns false.
+ #
+ class BodyTypeBasic < Struct.new(:media_type, :subtype,
+ :param, :content_id,
+ :description, :encoding, :size,
+ :md5, :disposition, :language,
+ :extension)
+ def multipart?
+ return false
+ end
+
+ # Obsolete: use +subtype+ instead. Calling this will
+ # generate a warning message to +stderr+, then return
+ # the value of +subtype+.
+ def media_subtype
+ $stderr.printf("warning: media_subtype is obsolete.\n")
+ $stderr.printf(" use subtype instead.\n")
+ return subtype
+ end
+ end
+
+ # Net::IMAP::BodyTypeText represents TEXT body structures of messages.
+ #
+ # ==== Fields:
+ #
+ # lines:: Returns the size of the body in text lines.
+ #
+ # And Net::IMAP::BodyTypeText has all fields of Net::IMAP::BodyTypeBasic.
+ #
+ class BodyTypeText < Struct.new(:media_type, :subtype,
+ :param, :content_id,
+ :description, :encoding, :size,
+ :lines,
+ :md5, :disposition, :language,
+ :extension)
+ def multipart?
+ return false
+ end
+
+ # Obsolete: use +subtype+ instead. Calling this will
+ # generate a warning message to +stderr+, then return
+ # the value of +subtype+.
+ def media_subtype
+ $stderr.printf("warning: media_subtype is obsolete.\n")
+ $stderr.printf(" use subtype instead.\n")
+ return subtype
+ end
+ end
+
+ # Net::IMAP::BodyTypeMessage represents MESSAGE/RFC822 body structures of messages.
+ #
+ # ==== Fields:
+ #
+ # envelope:: Returns a Net::IMAP::Envelope giving the envelope structure.
+ #
+ # body:: Returns an object giving the body structure.
+ #
+ # And Net::IMAP::BodyTypeMessage has all methods of Net::IMAP::BodyTypeText.
+ #
+ class BodyTypeMessage < Struct.new(:media_type, :subtype,
+ :param, :content_id,
+ :description, :encoding, :size,
+ :envelope, :body, :lines,
+ :md5, :disposition, :language,
+ :extension)
+ def multipart?
+ return false
+ end
+
+ # Obsolete: use +subtype+ instead. Calling this will
+ # generate a warning message to +stderr+, then return
+ # the value of +subtype+.
+ def media_subtype
+ $stderr.printf("warning: media_subtype is obsolete.\n")
+ $stderr.printf(" use subtype instead.\n")
+ return subtype
+ end
+ end
+
+ # Net::IMAP::BodyTypeMultipart represents multipart body structures
+ # of messages.
+ #
+ # ==== Fields:
+ #
+ # media_type:: Returns the content media type name as defined in [MIME-IMB].
+ #
+ # subtype:: Returns the content subtype name as defined in [MIME-IMB].
+ #
+ # parts:: Returns multiple parts.
+ #
+ # param:: Returns a hash that represents parameters as defined in [MIME-IMB].
+ #
+ # disposition:: Returns a Net::IMAP::ContentDisposition object giving
+ # the content disposition.
+ #
+ # language:: Returns a string or an array of strings giving the body
+ # language value as defined in [LANGUAGE-TAGS].
+ #
+ # extension:: Returns extension data.
+ #
+ # multipart?:: Returns true.
+ #
+ class BodyTypeMultipart < Struct.new(:media_type, :subtype,
+ :parts,
+ :param, :disposition, :language,
+ :extension)
+ def multipart?
+ return true
+ end
+
+ # Obsolete: use +subtype+ instead. Calling this will
+ # generate a warning message to +stderr+, then return
+ # the value of +subtype+.
+ def media_subtype
+ $stderr.printf("warning: media_subtype is obsolete.\n")
+ $stderr.printf(" use subtype instead.\n")
+ return subtype
+ end
+ end
+
+ class ResponseParser # :nodoc:
+ def parse(str)
+ @str = str
+ @pos = 0
+ @lex_state = EXPR_BEG
+ @token = nil
+ return response
+ end
+
+ private
+
+ EXPR_BEG = :EXPR_BEG
+ EXPR_DATA = :EXPR_DATA
+ EXPR_TEXT = :EXPR_TEXT
+ EXPR_RTEXT = :EXPR_RTEXT
+ EXPR_CTEXT = :EXPR_CTEXT
+
+ T_SPACE = :SPACE
+ T_NIL = :NIL
+ T_NUMBER = :NUMBER
+ T_ATOM = :ATOM
+ T_QUOTED = :QUOTED
+ T_LPAR = :LPAR
+ T_RPAR = :RPAR
+ T_BSLASH = :BSLASH
+ T_STAR = :STAR
+ T_LBRA = :LBRA
+ T_RBRA = :RBRA
+ T_LITERAL = :LITERAL
+ T_PLUS = :PLUS
+ T_PERCENT = :PERCENT
+ T_CRLF = :CRLF
+ T_EOF = :EOF
+ T_TEXT = :TEXT
+
+ BEG_REGEXP = /\G(?:\
+(?# 1: SPACE )( )|\
+(?# 2: NIL )(NIL)(?=[\x80-\xff(){ \x00-\x1f\x7f%*"\\\[\]+])|\
+(?# 3: NUMBER )(\d+)(?=[\x80-\xff(){ \x00-\x1f\x7f%*"\\\[\]+])|\
+(?# 4: ATOM )([^\x80-\xff(){ \x00-\x1f\x7f%*"\\\[\]+]+)|\
+(?# 5: QUOTED )"((?:[^\x00\r\n"\\]|\\["\\])*)"|\
+(?# 6: LPAR )(\()|\
+(?# 7: RPAR )(\))|\
+(?# 8: BSLASH )(\\)|\
+(?# 9: STAR )(\*)|\
+(?# 10: LBRA )(\[)|\
+(?# 11: RBRA )(\])|\
+(?# 12: LITERAL )\{(\d+)\}\r\n|\
+(?# 13: PLUS )(\+)|\
+(?# 14: PERCENT )(%)|\
+(?# 15: CRLF )(\r\n)|\
+(?# 16: EOF )(\z))/ni
+
+ DATA_REGEXP = /\G(?:\
+(?# 1: SPACE )( )|\
+(?# 2: NIL )(NIL)|\
+(?# 3: NUMBER )(\d+)|\
+(?# 4: QUOTED )"((?:[^\x00\r\n"\\]|\\["\\])*)"|\
+(?# 5: LITERAL )\{(\d+)\}\r\n|\
+(?# 6: LPAR )(\()|\
+(?# 7: RPAR )(\)))/ni
+
+ TEXT_REGEXP = /\G(?:\
+(?# 1: TEXT )([^\x00\r\n]*))/ni
+
+ RTEXT_REGEXP = /\G(?:\
+(?# 1: LBRA )(\[)|\
+(?# 2: TEXT )([^\x00\r\n]*))/ni
+
+ CTEXT_REGEXP = /\G(?:\
+(?# 1: TEXT )([^\x00\r\n\]]*))/ni
+
+ Token = Struct.new(:symbol, :value)
+
+ def response
+ token = lookahead
+ case token.symbol
+ when T_PLUS
+ result = continue_req
+ when T_STAR
+ result = response_untagged
+ else
+ result = response_tagged
+ end
+ match(T_CRLF)
+ match(T_EOF)
+ return result
+ end
+
+ def continue_req
+ match(T_PLUS)
+ match(T_SPACE)
+ return ContinuationRequest.new(resp_text, @str)
+ end
+
+ def response_untagged
+ match(T_STAR)
+ match(T_SPACE)
+ token = lookahead
+ if token.symbol == T_NUMBER
+ return numeric_response
+ elsif token.symbol == T_ATOM
+ case token.value
+ when /\A(?:OK|NO|BAD|BYE|PREAUTH)\z/ni
+ return response_cond
+ when /\A(?:FLAGS)\z/ni
+ return flags_response
+ when /\A(?:LIST|LSUB)\z/ni
+ return list_response
+ when /\A(?:QUOTA)\z/ni
+ return getquota_response
+ when /\A(?:QUOTAROOT)\z/ni
+ return getquotaroot_response
+ when /\A(?:ACL)\z/ni
+ return getacl_response
+ when /\A(?:SEARCH|SORT)\z/ni
+ return search_response
+ when /\A(?:THREAD)\z/ni
+ return thread_response
+ when /\A(?:STATUS)\z/ni
+ return status_response
+ when /\A(?:CAPABILITY)\z/ni
+ return capability_response
+ else
+ return text_response
+ end
+ else
+ parse_error("unexpected token %s", token.symbol)
+ end
+ end
+
+ def response_tagged
+ tag = atom
+ match(T_SPACE)
+ token = match(T_ATOM)
+ name = token.value.upcase
+ match(T_SPACE)
+ return TaggedResponse.new(tag, name, resp_text, @str)
+ end
+
+ def response_cond
+ token = match(T_ATOM)
+ name = token.value.upcase
+ match(T_SPACE)
+ return UntaggedResponse.new(name, resp_text, @str)
+ end
+
+ def numeric_response
+ n = number
+ match(T_SPACE)
+ token = match(T_ATOM)
+ name = token.value.upcase
+ case name
+ when "EXISTS", "RECENT", "EXPUNGE"
+ return UntaggedResponse.new(name, n, @str)
+ when "FETCH"
+ shift_token
+ match(T_SPACE)
+ data = FetchData.new(n, msg_att)
+ return UntaggedResponse.new(name, data, @str)
+ end
+ end
+
+ def msg_att
+ match(T_LPAR)
+ attr = {}
+ while true
+ token = lookahead
+ case token.symbol
+ when T_RPAR
+ shift_token
+ break
+ when T_SPACE
+ shift_token
+ token = lookahead
+ end
+ case token.value
+ when /\A(?:ENVELOPE)\z/ni
+ name, val = envelope_data
+ when /\A(?:FLAGS)\z/ni
+ name, val = flags_data
+ when /\A(?:INTERNALDATE)\z/ni
+ name, val = internaldate_data
+ when /\A(?:RFC822(?:\.HEADER|\.TEXT)?)\z/ni
+ name, val = rfc822_text
+ when /\A(?:RFC822\.SIZE)\z/ni
+ name, val = rfc822_size
+ when /\A(?:BODY(?:STRUCTURE)?)\z/ni
+ name, val = body_data
+ when /\A(?:UID)\z/ni
+ name, val = uid_data
+ else
+ parse_error("unknown attribute `%s'", token.value)
+ end
+ attr[name] = val
+ end
+ return attr
+ end
+
+ def envelope_data
+ token = match(T_ATOM)
+ name = token.value.upcase
+ match(T_SPACE)
+ return name, envelope
+ end
+
+ def envelope
+ @lex_state = EXPR_DATA
+ token = lookahead
+ if token.symbol == T_NIL
+ shift_token
+ result = nil
+ else
+ match(T_LPAR)
+ date = nstring
+ match(T_SPACE)
+ subject = nstring
+ match(T_SPACE)
+ from = address_list
+ match(T_SPACE)
+ sender = address_list
+ match(T_SPACE)
+ reply_to = address_list
+ match(T_SPACE)
+ to = address_list
+ match(T_SPACE)
+ cc = address_list
+ match(T_SPACE)
+ bcc = address_list
+ match(T_SPACE)
+ in_reply_to = nstring
+ match(T_SPACE)
+ message_id = nstring
+ match(T_RPAR)
+ result = Envelope.new(date, subject, from, sender, reply_to,
+ to, cc, bcc, in_reply_to, message_id)
+ end
+ @lex_state = EXPR_BEG
+ return result
+ end
+
+ def flags_data
+ token = match(T_ATOM)
+ name = token.value.upcase
+ match(T_SPACE)
+ return name, flag_list
+ end
+
+ def internaldate_data
+ token = match(T_ATOM)
+ name = token.value.upcase
+ match(T_SPACE)
+ token = match(T_QUOTED)
+ return name, token.value
+ end
+
+ def rfc822_text
+ token = match(T_ATOM)
+ name = token.value.upcase
+ match(T_SPACE)
+ return name, nstring
+ end
+
+ def rfc822_size
+ token = match(T_ATOM)
+ name = token.value.upcase
+ match(T_SPACE)
+ return name, number
+ end
+
+ def body_data
+ token = match(T_ATOM)
+ name = token.value.upcase
+ token = lookahead
+ if token.symbol == T_SPACE
+ shift_token
+ return name, body
+ end
+ name.concat(section)
+ token = lookahead
+ if token.symbol == T_ATOM
+ name.concat(token.value)
+ shift_token
+ end
+ match(T_SPACE)
+ data = nstring
+ return name, data
+ end
+
+ def body
+ @lex_state = EXPR_DATA
+ token = lookahead
+ if token.symbol == T_NIL
+ shift_token
+ result = nil
+ else
+ match(T_LPAR)
+ token = lookahead
+ if token.symbol == T_LPAR
+ result = body_type_mpart
+ else
+ result = body_type_1part
+ end
+ match(T_RPAR)
+ end
+ @lex_state = EXPR_BEG
+ return result
+ end
+
+ def body_type_1part
+ token = lookahead
+ case token.value
+ when /\A(?:TEXT)\z/ni
+ return body_type_text
+ when /\A(?:MESSAGE)\z/ni
+ return body_type_msg
+ else
+ return body_type_basic
+ end
+ end
+
+ def body_type_basic
+ mtype, msubtype = media_type
+ token = lookahead
+ if token.symbol == T_RPAR
+ return BodyTypeBasic.new(mtype, msubtype)
+ end
+ match(T_SPACE)
+ param, content_id, desc, enc, size = body_fields
+ md5, disposition, language, extension = body_ext_1part
+ return BodyTypeBasic.new(mtype, msubtype,
+ param, content_id,
+ desc, enc, size,
+ md5, disposition, language, extension)
+ end
+
+ def body_type_text
+ mtype, msubtype = media_type
+ match(T_SPACE)
+ param, content_id, desc, enc, size = body_fields
+ match(T_SPACE)
+ lines = number
+ md5, disposition, language, extension = body_ext_1part
+ return BodyTypeText.new(mtype, msubtype,
+ param, content_id,
+ desc, enc, size,
+ lines,
+ md5, disposition, language, extension)
+ end
+
+ def body_type_msg
+ mtype, msubtype = media_type
+ match(T_SPACE)
+ param, content_id, desc, enc, size = body_fields
+ match(T_SPACE)
+ env = envelope
+ match(T_SPACE)
+ b = body
+ match(T_SPACE)
+ lines = number
+ md5, disposition, language, extension = body_ext_1part
+ return BodyTypeMessage.new(mtype, msubtype,
+ param, content_id,
+ desc, enc, size,
+ env, b, lines,
+ md5, disposition, language, extension)
+ end
+
+ def body_type_mpart
+ parts = []
+ while true
+ token = lookahead
+ if token.symbol == T_SPACE
+ shift_token
+ break
+ end
+ parts.push(body)
+ end
+ mtype = "MULTIPART"
+ msubtype = case_insensitive_string
+ param, disposition, language, extension = body_ext_mpart
+ return BodyTypeMultipart.new(mtype, msubtype, parts,
+ param, disposition, language,
+ extension)
+ end
+
+ def media_type
+ mtype = case_insensitive_string
+ match(T_SPACE)
+ msubtype = case_insensitive_string
+ return mtype, msubtype
+ end
+
+ def body_fields
+ param = body_fld_param
+ match(T_SPACE)
+ content_id = nstring
+ match(T_SPACE)
+ desc = nstring
+ match(T_SPACE)
+ enc = case_insensitive_string
+ match(T_SPACE)
+ size = number
+ return param, content_id, desc, enc, size
+ end
+
+ def body_fld_param
+ token = lookahead
+ if token.symbol == T_NIL
+ shift_token
+ return nil
+ end
+ match(T_LPAR)
+ param = {}
+ while true
+ token = lookahead
+ case token.symbol
+ when T_RPAR
+ shift_token
+ break
+ when T_SPACE
+ shift_token
+ end
+ name = case_insensitive_string
+ match(T_SPACE)
+ val = string
+ param[name] = val
+ end
+ return param
+ end
+
+ def body_ext_1part
+ token = lookahead
+ if token.symbol == T_SPACE
+ shift_token
+ else
+ return nil
+ end
+ md5 = nstring
+
+ token = lookahead
+ if token.symbol == T_SPACE
+ shift_token
+ else
+ return md5
+ end
+ disposition = body_fld_dsp
+
+ token = lookahead
+ if token.symbol == T_SPACE
+ shift_token
+ else
+ return md5, disposition
+ end
+ language = body_fld_lang
+
+ token = lookahead
+ if token.symbol == T_SPACE
+ shift_token
+ else
+ return md5, disposition, language
+ end
+
+ extension = body_extensions
+ return md5, disposition, language, extension
+ end
+
+ def body_ext_mpart
+ token = lookahead
+ if token.symbol == T_SPACE
+ shift_token
+ else
+ return nil
+ end
+ param = body_fld_param
+
+ token = lookahead
+ if token.symbol == T_SPACE
+ shift_token
+ else
+ return param
+ end
+ disposition = body_fld_dsp
+ match(T_SPACE)
+ language = body_fld_lang
+
+ token = lookahead
+ if token.symbol == T_SPACE
+ shift_token
+ else
+ return param, disposition, language
+ end
+
+ extension = body_extensions
+ return param, disposition, language, extension
+ end
+
+ def body_fld_dsp
+ token = lookahead
+ if token.symbol == T_NIL
+ shift_token
+ return nil
+ end
+ match(T_LPAR)
+ dsp_type = case_insensitive_string
+ match(T_SPACE)
+ param = body_fld_param
+ match(T_RPAR)
+ return ContentDisposition.new(dsp_type, param)
+ end
+
+ def body_fld_lang
+ token = lookahead
+ if token.symbol == T_LPAR
+ shift_token
+ result = []
+ while true
+ token = lookahead
+ case token.symbol
+ when T_RPAR
+ shift_token
+ return result
+ when T_SPACE
+ shift_token
+ end
+ result.push(case_insensitive_string)
+ end
+ else
+ lang = nstring
+ if lang
+ return lang.upcase
+ else
+ return lang
+ end
+ end
+ end
+
+ def body_extensions
+ result = []
+ while true
+ token = lookahead
+ case token.symbol
+ when T_RPAR
+ return result
+ when T_SPACE
+ shift_token
+ end
+ result.push(body_extension)
+ end
+ end
+
+ def body_extension
+ token = lookahead
+ case token.symbol
+ when T_LPAR
+ shift_token
+ result = body_extensions
+ match(T_RPAR)
+ return result
+ when T_NUMBER
+ return number
+ else
+ return nstring
+ end
+ end
+
+ def section
+ str = ""
+ token = match(T_LBRA)
+ str.concat(token.value)
+ token = match(T_ATOM, T_NUMBER, T_RBRA)
+ if token.symbol == T_RBRA
+ str.concat(token.value)
+ return str
+ end
+ str.concat(token.value)
+ token = lookahead
+ if token.symbol == T_SPACE
+ shift_token
+ str.concat(token.value)
+ token = match(T_LPAR)
+ str.concat(token.value)
+ while true
+ token = lookahead
+ case token.symbol
+ when T_RPAR
+ str.concat(token.value)
+ shift_token
+ break
+ when T_SPACE
+ shift_token
+ str.concat(token.value)
+ end
+ str.concat(format_string(astring))
+ end
+ end
+ token = match(T_RBRA)
+ str.concat(token.value)
+ return str
+ end
+
+ def format_string(str)
+ case str
+ when ""
+ return '""'
+ when /[\x80-\xff\r\n]/n
+ # literal
+ return "{" + str.length.to_s + "}" + CRLF + str
+ when /[(){ \x00-\x1f\x7f%*"\\]/n
+ # quoted string
+ return '"' + str.gsub(/["\\]/n, "\\\\\\&") + '"'
+ else
+ # atom
+ return str
+ end
+ end
+
+ def uid_data
+ token = match(T_ATOM)
+ name = token.value.upcase
+ match(T_SPACE)
+ return name, number
+ end
+
+ def text_response
+ token = match(T_ATOM)
+ name = token.value.upcase
+ match(T_SPACE)
+ @lex_state = EXPR_TEXT
+ token = match(T_TEXT)
+ @lex_state = EXPR_BEG
+ return UntaggedResponse.new(name, token.value)
+ end
+
+ def flags_response
+ token = match(T_ATOM)
+ name = token.value.upcase
+ match(T_SPACE)
+ return UntaggedResponse.new(name, flag_list, @str)
+ end
+
+ def list_response
+ token = match(T_ATOM)
+ name = token.value.upcase
+ match(T_SPACE)
+ return UntaggedResponse.new(name, mailbox_list, @str)
+ end
+
+ def mailbox_list
+ attr = flag_list
+ match(T_SPACE)
+ token = match(T_QUOTED, T_NIL)
+ if token.symbol == T_NIL
+ delim = nil
+ else
+ delim = token.value
+ end
+ match(T_SPACE)
+ name = astring
+ return MailboxList.new(attr, delim, name)
+ end
+
+ def getquota_response
+ # If quota never established, get back
+ # `NO Quota root does not exist'.
+ # If quota removed, get `()' after the
+ # folder spec with no mention of `STORAGE'.
+ token = match(T_ATOM)
+ name = token.value.upcase
+ match(T_SPACE)
+ mailbox = astring
+ match(T_SPACE)
+ match(T_LPAR)
+ token = lookahead
+ case token.symbol
+ when T_RPAR
+ shift_token
+ data = MailboxQuota.new(mailbox, nil, nil)
+ return UntaggedResponse.new(name, data, @str)
+ when T_ATOM
+ shift_token
+ match(T_SPACE)
+ token = match(T_NUMBER)
+ usage = token.value
+ match(T_SPACE)
+ token = match(T_NUMBER)
+ quota = token.value
+ match(T_RPAR)
+ data = MailboxQuota.new(mailbox, usage, quota)
+ return UntaggedResponse.new(name, data, @str)
+ else
+ parse_error("unexpected token %s", token.symbol)
+ end
+ end
+
+ def getquotaroot_response
+ # Similar to getquota, but only admin can use getquota.
+ token = match(T_ATOM)
+ name = token.value.upcase
+ match(T_SPACE)
+ mailbox = astring
+ quotaroots = []
+ while true
+ token = lookahead
+ break unless token.symbol == T_SPACE
+ shift_token
+ quotaroots.push(astring)
+ end
+ data = MailboxQuotaRoot.new(mailbox, quotaroots)
+ return UntaggedResponse.new(name, data, @str)
+ end
+
+ def getacl_response
+ token = match(T_ATOM)
+ name = token.value.upcase
+ match(T_SPACE)
+ mailbox = astring
+ data = []
+ token = lookahead
+ if token.symbol == T_SPACE
+ shift_token
+ while true
+ token = lookahead
+ case token.symbol
+ when T_CRLF
+ break
+ when T_SPACE
+ shift_token
+ end
+ user = astring
+ match(T_SPACE)
+ rights = astring
+ ##XXX data.push([user, rights])
+ data.push(MailboxACLItem.new(user, rights))
+ end
+ end
+ return UntaggedResponse.new(name, data, @str)
+ end
+
+ def search_response
+ token = match(T_ATOM)
+ name = token.value.upcase
+ token = lookahead
+ if token.symbol == T_SPACE
+ shift_token
+ data = []
+ while true
+ token = lookahead
+ case token.symbol
+ when T_CRLF
+ break
+ when T_SPACE
+ shift_token
+ end
+ data.push(number)
+ end
+ else
+ data = []
+ end
+ return UntaggedResponse.new(name, data, @str)
+ end
+
+ def thread_response
+ token = match(T_ATOM)
+ name = token.value.upcase
+ token = lookahead
+
+ if token.symbol == T_SPACE
+ threads = []
+
+ while true
+ shift_token
+ token = lookahead
+
+ case token.symbol
+ when T_LPAR
+ threads << thread_branch(token)
+ when T_CRLF
+ break
+ end
+ end
+ else
+ # no member
+ threads = []
+ end
+
+ return UntaggedResponse.new(name, threads, @str)
+ end
+
+ def thread_branch(token)
+ rootmember = nil
+ lastmember = nil
+
+ while true
+ shift_token # ignore first T_LPAR
+ token = lookahead
+
+ case token.symbol
+ when T_NUMBER
+ # new member
+ newmember = ThreadMember.new(number, [])
+ if rootmember.nil?
+ rootmember = newmember
+ else
+ lastmember.children << newmember
+ end
+ lastmember = newmember
+ when T_SPACE
+ # do nothing
+ when T_LPAR
+ if rootmember.nil?
+ # dummy member
+ lastmember = rootmember = ThreadMember.new(nil, [])
+ end
+
+ lastmember.children << thread_branch(token)
+ when T_RPAR
+ break
+ end
+ end
+
+ return rootmember
+ end
+
+ def status_response
+ token = match(T_ATOM)
+ name = token.value.upcase
+ match(T_SPACE)
+ mailbox = astring
+ match(T_SPACE)
+ match(T_LPAR)
+ attr = {}
+ while true
+ token = lookahead
+ case token.symbol
+ when T_RPAR
+ shift_token
+ break
+ when T_SPACE
+ shift_token
+ end
+ token = match(T_ATOM)
+ key = token.value.upcase
+ match(T_SPACE)
+ val = number
+ attr[key] = val
+ end
+ data = StatusData.new(mailbox, attr)
+ return UntaggedResponse.new(name, data, @str)
+ end
+
+ def capability_response
+ token = match(T_ATOM)
+ name = token.value.upcase
+ match(T_SPACE)
+ data = []
+ while true
+ token = lookahead
+ case token.symbol
+ when T_CRLF
+ break
+ when T_SPACE
+ shift_token
+ end
+ data.push(atom.upcase)
+ end
+ return UntaggedResponse.new(name, data, @str)
+ end
+
+ def resp_text
+ @lex_state = EXPR_RTEXT
+ token = lookahead
+ if token.symbol == T_LBRA
+ code = resp_text_code
+ else
+ code = nil
+ end
+ token = match(T_TEXT)
+ @lex_state = EXPR_BEG
+ return ResponseText.new(code, token.value)
+ end
+
+ def resp_text_code
+ @lex_state = EXPR_BEG
+ match(T_LBRA)
+ token = match(T_ATOM)
+ name = token.value.upcase
+ case name
+ when /\A(?:ALERT|PARSE|READ-ONLY|READ-WRITE|TRYCREATE)\z/n
+ result = ResponseCode.new(name, nil)
+ when /\A(?:PERMANENTFLAGS)\z/n
+ match(T_SPACE)
+ result = ResponseCode.new(name, flag_list)
+ when /\A(?:UIDVALIDITY|UIDNEXT|UNSEEN)\z/n
+ match(T_SPACE)
+ result = ResponseCode.new(name, number)
+ else
+ match(T_SPACE)
+ @lex_state = EXPR_CTEXT
+ token = match(T_TEXT)
+ @lex_state = EXPR_BEG
+ result = ResponseCode.new(name, token.value)
+ end
+ match(T_RBRA)
+ @lex_state = EXPR_RTEXT
+ return result
+ end
+
+ def address_list
+ token = lookahead
+ if token.symbol == T_NIL
+ shift_token
+ return nil
+ else
+ result = []
+ match(T_LPAR)
+ while true
+ token = lookahead
+ case token.symbol
+ when T_RPAR
+ shift_token
+ break
+ when T_SPACE
+ shift_token
+ end
+ result.push(address)
+ end
+ return result
+ end
+ end
+
+ ADDRESS_REGEXP = /\G\
+(?# 1: NAME )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)") \
+(?# 2: ROUTE )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)") \
+(?# 3: MAILBOX )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)") \
+(?# 4: HOST )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)")\
+\)/ni
+
+ def address
+ match(T_LPAR)
+ if @str.index(ADDRESS_REGEXP, @pos)
+ # address does not include literal.
+ @pos = $~.end(0)
+ name = $1
+ route = $2
+ mailbox = $3
+ host = $4
+ for s in [name, route, mailbox, host]
+ if s
+ s.gsub!(/\\(["\\])/n, "\\1")
+ end
+ end
+ else
+ name = nstring
+ match(T_SPACE)
+ route = nstring
+ match(T_SPACE)
+ mailbox = nstring
+ match(T_SPACE)
+ host = nstring
+ match(T_RPAR)
+ end
+ return Address.new(name, route, mailbox, host)
+ end
+
+# def flag_list
+# result = []
+# match(T_LPAR)
+# while true
+# token = lookahead
+# case token.symbol
+# when T_RPAR
+# shift_token
+# break
+# when T_SPACE
+# shift_token
+# end
+# result.push(flag)
+# end
+# return result
+# end
+
+# def flag
+# token = lookahead
+# if token.symbol == T_BSLASH
+# shift_token
+# token = lookahead
+# if token.symbol == T_STAR
+# shift_token
+# return token.value.intern
+# else
+# return atom.intern
+# end
+# else
+# return atom
+# end
+# end
+
+ FLAG_REGEXP = /\
+(?# FLAG )\\([^\x80-\xff(){ \x00-\x1f\x7f%"\\]+)|\
+(?# ATOM )([^\x80-\xff(){ \x00-\x1f\x7f%*"\\]+)/n
+
+ def flag_list
+ if @str.index(/\(([^)]*)\)/ni, @pos)
+ @pos = $~.end(0)
+ return $1.scan(FLAG_REGEXP).collect { |flag, atom|
+ atom || flag.capitalize.intern
+ }
+ else
+ parse_error("invalid flag list")
+ end
+ end
+
+ def nstring
+ token = lookahead
+ if token.symbol == T_NIL
+ shift_token
+ return nil
+ else
+ return string
+ end
+ end
+
+ def astring
+ token = lookahead
+ if string_token?(token)
+ return string
+ else
+ return atom
+ end
+ end
+
+ def string
+ token = lookahead
+ if token.symbol == T_NIL
+ shift_token
+ return nil
+ end
+ token = match(T_QUOTED, T_LITERAL)
+ return token.value
+ end
+
+ STRING_TOKENS = [T_QUOTED, T_LITERAL, T_NIL]
+
+ def string_token?(token)
+ return STRING_TOKENS.include?(token.symbol)
+ end
+
+ def case_insensitive_string
+ token = lookahead
+ if token.symbol == T_NIL
+ shift_token
+ return nil
+ end
+ token = match(T_QUOTED, T_LITERAL)
+ return token.value.upcase
+ end
+
+ def atom
+ result = ""
+ while true
+ token = lookahead
+ if atom_token?(token)
+ result.concat(token.value)
+ shift_token
+ else
+ if result.empty?
+ parse_error("unexpected token %s", token.symbol)
+ else
+ return result
+ end
+ end
+ end
+ end
+
+ ATOM_TOKENS = [
+ T_ATOM,
+ T_NUMBER,
+ T_NIL,
+ T_LBRA,
+ T_RBRA,
+ T_PLUS
+ ]
+
+ def atom_token?(token)
+ return ATOM_TOKENS.include?(token.symbol)
+ end
+
+ def number
+ token = lookahead
+ if token.symbol == T_NIL
+ shift_token
+ return nil
+ end
+ token = match(T_NUMBER)
+ return token.value.to_i
+ end
+
+ def nil_atom
+ match(T_NIL)
+ return nil
+ end
+
+ def match(*args)
+ token = lookahead
+ unless args.include?(token.symbol)
+ parse_error('unexpected token %s (expected %s)',
+ token.symbol.id2name,
+ args.collect {|i| i.id2name}.join(" or "))
+ end
+ shift_token
+ return token
+ end
+
+ def lookahead
+ unless @token
+ @token = next_token
+ end
+ return @token
+ end
+
+ def shift_token
+ @token = nil
+ end
+
+ def next_token
+ case @lex_state
+ when EXPR_BEG
+ if @str.index(BEG_REGEXP, @pos)
+ @pos = $~.end(0)
+ if $1
+ return Token.new(T_SPACE, $+)
+ elsif $2
+ return Token.new(T_NIL, $+)
+ elsif $3
+ return Token.new(T_NUMBER, $+)
+ elsif $4
+ return Token.new(T_ATOM, $+)
+ elsif $5
+ return Token.new(T_QUOTED,
+ $+.gsub(/\\(["\\])/n, "\\1"))
+ elsif $6
+ return Token.new(T_LPAR, $+)
+ elsif $7
+ return Token.new(T_RPAR, $+)
+ elsif $8
+ return Token.new(T_BSLASH, $+)
+ elsif $9
+ return Token.new(T_STAR, $+)
+ elsif $10
+ return Token.new(T_LBRA, $+)
+ elsif $11
+ return Token.new(T_RBRA, $+)
+ elsif $12
+ len = $+.to_i
+ val = @str[@pos, len]
+ @pos += len
+ return Token.new(T_LITERAL, val)
+ elsif $13
+ return Token.new(T_PLUS, $+)
+ elsif $14
+ return Token.new(T_PERCENT, $+)
+ elsif $15
+ return Token.new(T_CRLF, $+)
+ elsif $16
+ return Token.new(T_EOF, $+)
+ else
+ parse_error("[Net::IMAP BUG] BEG_REGEXP is invalid")
+ end
+ else
+ @str.index(/\S*/n, @pos)
+ parse_error("unknown token - %s", $&.dump)
+ end
+ when EXPR_DATA
+ if @str.index(DATA_REGEXP, @pos)
+ @pos = $~.end(0)
+ if $1
+ return Token.new(T_SPACE, $+)
+ elsif $2
+ return Token.new(T_NIL, $+)
+ elsif $3
+ return Token.new(T_NUMBER, $+)
+ elsif $4
+ return Token.new(T_QUOTED,
+ $+.gsub(/\\(["\\])/n, "\\1"))
+ elsif $5
+ len = $+.to_i
+ val = @str[@pos, len]
+ @pos += len
+ return Token.new(T_LITERAL, val)
+ elsif $6
+ return Token.new(T_LPAR, $+)
+ elsif $7
+ return Token.new(T_RPAR, $+)
+ else
+ parse_error("[Net::IMAP BUG] DATA_REGEXP is invalid")
+ end
+ else
+ @str.index(/\S*/n, @pos)
+ parse_error("unknown token - %s", $&.dump)
+ end
+ when EXPR_TEXT
+ if @str.index(TEXT_REGEXP, @pos)
+ @pos = $~.end(0)
+ if $1
+ return Token.new(T_TEXT, $+)
+ else
+ parse_error("[Net::IMAP BUG] TEXT_REGEXP is invalid")
+ end
+ else
+ @str.index(/\S*/n, @pos)
+ parse_error("unknown token - %s", $&.dump)
+ end
+ when EXPR_RTEXT
+ if @str.index(RTEXT_REGEXP, @pos)
+ @pos = $~.end(0)
+ if $1
+ return Token.new(T_LBRA, $+)
+ elsif $2
+ return Token.new(T_TEXT, $+)
+ else
+ parse_error("[Net::IMAP BUG] RTEXT_REGEXP is invalid")
+ end
+ else
+ @str.index(/\S*/n, @pos)
+ parse_error("unknown token - %s", $&.dump)
+ end
+ when EXPR_CTEXT
+ if @str.index(CTEXT_REGEXP, @pos)
+ @pos = $~.end(0)
+ if $1
+ return Token.new(T_TEXT, $+)
+ else
+ parse_error("[Net::IMAP BUG] CTEXT_REGEXP is invalid")
+ end
+ else
+ @str.index(/\S*/n, @pos) #/
+ parse_error("unknown token - %s", $&.dump)
+ end
+ else
+ parse_error("illegal @lex_state - %s", @lex_state.inspect)
+ end
+ end
+
+ def parse_error(fmt, *args)
+ if IMAP.debug
+ $stderr.printf("@str: %s\n", @str.dump)
+ $stderr.printf("@pos: %d\n", @pos)
+ $stderr.printf("@lex_state: %s\n", @lex_state)
+ if @token
+ $stderr.printf("@token.symbol: %s\n", @token.symbol)
+ $stderr.printf("@token.value: %s\n", @token.value.inspect)
+ end
+ end
+ raise ResponseParseError, format(fmt, *args)
+ end
+ end
+
+ # Authenticator for the "LOGIN" authentication type. See
+ # #authenticate().
+ class LoginAuthenticator
+ def process(data)
+ case @state
+ when STATE_USER
+ @state = STATE_PASSWORD
+ return @user
+ when STATE_PASSWORD
+ return @password
+ end
+ end
+
+ private
+
+ STATE_USER = :USER
+ STATE_PASSWORD = :PASSWORD
+
+ def initialize(user, password)
+ @user = user
+ @password = password
+ @state = STATE_USER
+ end
+ end
+ add_authenticator "LOGIN", LoginAuthenticator
+
+ # Authenticator for the "CRAM-MD5" authentication type. See
+ # #authenticate().
+ class CramMD5Authenticator
+ def process(challenge)
+ digest = hmac_md5(challenge, @password)
+ return @user + " " + digest
+ end
+
+ private
+
+ def initialize(user, password)
+ @user = user
+ @password = password
+ end
+
+ def hmac_md5(text, key)
+ if key.length > 64
+ key = Digest::MD5.digest(key)
+ end
+
+ k_ipad = key + "\0" * (64 - key.length)
+ k_opad = key + "\0" * (64 - key.length)
+ for i in 0..63
+ k_ipad[i] ^= 0x36
+ k_opad[i] ^= 0x5c
+ end
+
+ digest = Digest::MD5.digest(k_ipad + text)
+
+ return Digest::MD5.hexdigest(k_opad + digest)
+ end
+ end
+ add_authenticator "CRAM-MD5", CramMD5Authenticator
+
+ # Superclass of IMAP errors.
+ class Error < StandardError
+ end
+
+ # Error raised when data is in the incorrect format.
+ class DataFormatError < Error
+ end
+
+ # Error raised when a response from the server is non-parseable.
+ class ResponseParseError < Error
+ end
+
+ # Superclass of all errors used to encapsulate "fail" responses
+ # from the server.
+ class ResponseError < Error
+ end
+
+ # Error raised upon a "NO" response from the server, indicating
+ # that the client command could not be completed successfully.
+ class NoResponseError < ResponseError
+ end
+
+ # Error raised upon a "BAD" response from the server, indicating
+ # that the client command violated the IMAP protocol, or an internal
+ # server failure has occurred.
+ class BadResponseError < ResponseError
+ end
+
+ # Error raised upon a "BYE" response from the server, indicating
+ # that the client is not being allowed to login, or has been timed
+ # out due to inactivity.
+ class ByeResponseError < ResponseError
+ end
+ end
+end
+
+if __FILE__ == $0
+ # :enddoc:
+ require "getoptlong"
+
+ $stdout.sync = true
+ $port = nil
+ $user = ENV["USER"] || ENV["LOGNAME"]
+ $auth = "login"
+ $ssl = false
+
+ def usage
+ $stderr.print <<EOF
+usage: #{$0} [options] <host>
+
+ --help print this message
+ --port=PORT specifies port
+ --user=USER specifies user
+ --auth=AUTH specifies auth type
+ --ssl use ssl
+EOF
+ end
+
+ def get_password
+ print "password: "
+ system("stty", "-echo")
+ begin
+ return gets.chop
+ ensure
+ system("stty", "echo")
+ print "\n"
+ end
+ end
+
+ def get_command
+ printf("%s@%s> ", $user, $host)
+ if line = gets
+ return line.strip.split(/\s+/)
+ else
+ return nil
+ end
+ end
+
+ parser = GetoptLong.new
+ parser.set_options(['--debug', GetoptLong::NO_ARGUMENT],
+ ['--help', GetoptLong::NO_ARGUMENT],
+ ['--port', GetoptLong::REQUIRED_ARGUMENT],
+ ['--user', GetoptLong::REQUIRED_ARGUMENT],
+ ['--auth', GetoptLong::REQUIRED_ARGUMENT],
+ ['--ssl', GetoptLong::NO_ARGUMENT])
+ begin
+ parser.each_option do |name, arg|
+ case name
+ when "--port"
+ $port = arg
+ when "--user"
+ $user = arg
+ when "--auth"
+ $auth = arg
+ when "--ssl"
+ $ssl = true
+ when "--debug"
+ Net::IMAP.debug = true
+ when "--help"
+ usage
+ exit(1)
+ end
+ end
+ rescue
+ usage
+ exit(1)
+ end
+
+ $host = ARGV.shift
+ unless $host
+ usage
+ exit(1)
+ end
+ $port ||= $ssl ? 993 : 143
+
+ imap = Net::IMAP.new($host, $port, $ssl)
+ begin
+ password = get_password
+ imap.authenticate($auth, $user, password)
+ while true
+ cmd, *args = get_command
+ break unless cmd
+ begin
+ case cmd
+ when "list"
+ for mbox in imap.list("", args[0] || "*")
+ if mbox.attr.include?(Net::IMAP::NOSELECT)
+ prefix = "!"
+ elsif mbox.attr.include?(Net::IMAP::MARKED)
+ prefix = "*"
+ else
+ prefix = " "
+ end
+ print prefix, mbox.name, "\n"
+ end
+ when "select"
+ imap.select(args[0] || "inbox")
+ print "ok\n"
+ when "close"
+ imap.close
+ print "ok\n"
+ when "summary"
+ unless messages = imap.responses["EXISTS"][-1]
+ puts "not selected"
+ next
+ end
+ if messages > 0
+ for data in imap.fetch(1..-1, ["ENVELOPE"])
+ print data.seqno, ": ", data.attr["ENVELOPE"].subject, "\n"
+ end
+ else
+ puts "no message"
+ end
+ when "fetch"
+ if args[0]
+ data = imap.fetch(args[0].to_i, ["RFC822.HEADER", "RFC822.TEXT"])[0]
+ puts data.attr["RFC822.HEADER"]
+ puts data.attr["RFC822.TEXT"]
+ else
+ puts "missing argument"
+ end
+ when "logout", "exit", "quit"
+ break
+ when "help", "?"
+ print <<EOF
+list [pattern] list mailboxes
+select [mailbox] select mailbox
+close close mailbox
+summary display summary
+fetch [msgno] display message
+logout logout
+help, ? display help message
+EOF
+ else
+ print "unknown command: ", cmd, "\n"
+ end
+ rescue Net::IMAP::Error
+ puts $!
+ end
+ end
+ ensure
+ imap.logout
+ imap.disconnect
+ end
+end
+
diff --git a/ruby_1_8_5/lib/net/pop.rb b/ruby_1_8_5/lib/net/pop.rb
new file mode 100644
index 0000000000..b76124156b
--- /dev/null
+++ b/ruby_1_8_5/lib/net/pop.rb
@@ -0,0 +1,879 @@
+# = net/pop.rb
+#
+# Copyright (c) 1999-2003 Yukihiro Matsumoto.
+#
+# Copyright (c) 1999-2003 Minero Aoki.
+#
+# Written & maintained by Minero Aoki <aamine@loveruby.net>.
+#
+# Documented by William Webber and Minero Aoki.
+#
+# This program is free software. You can re-distribute and/or
+# modify this program under the same terms as Ruby itself,
+# Ruby Distribute License or GNU General Public License.
+#
+# NOTE: You can find Japanese version of this document in
+# the doc/net directory of the standard ruby interpreter package.
+#
+# $Id: pop.rb,v 1.62.2.4 2005/09/13 07:27:18 aamine Exp $
+#
+# See Net::POP3 for documentation.
+#
+
+require 'net/protocol'
+require 'digest/md5'
+
+module Net
+
+ # Non-authentication POP3 protocol error
+ # (reply code "-ERR", except authentication).
+ class POPError < ProtocolError; end
+
+ # POP3 authentication error.
+ class POPAuthenticationError < ProtoAuthError; end
+
+ # Unexpected response from the server.
+ class POPBadResponse < POPError; end
+
+ #
+ # = Net::POP3
+ #
+ # == What is This Library?
+ #
+ # This library provides functionality for retrieving
+ # email via POP3, the Post Office Protocol version 3. For details
+ # of POP3, see [RFC1939] (http://www.ietf.org/rfc/rfc1939.txt).
+ #
+ # == Examples
+ #
+ # === Retrieving Messages
+ #
+ # This example retrieves messages from the server and deletes them
+ # on the server.
+ #
+ # Messages are written to files named 'inbox/1', 'inbox/2', ....
+ # Replace 'pop.example.com' with your POP3 server address, and
+ # 'YourAccount' and 'YourPassword' with the appropriate account
+ # details.
+ #
+ # require 'net/pop'
+ #
+ # pop = Net::POP3.new('pop.example.com')
+ # pop.start('YourAccount', 'YourPassword') # (1)
+ # if pop.mails.empty?
+ # puts 'No mail.'
+ # else
+ # i = 0
+ # pop.each_mail do |m| # or "pop.mails.each ..." # (2)
+ # File.open("inbox/#{i}", 'w') do |f|
+ # f.write m.pop
+ # end
+ # m.delete
+ # i += 1
+ # end
+ # puts "#{pop.mails.size} mails popped."
+ # end
+ # pop.finish # (3)
+ #
+ # 1. Call Net::POP3#start and start POP session.
+ # 2. Access messages by using POP3#each_mail and/or POP3#mails.
+ # 3. Close POP session by calling POP3#finish or use the block form of #start.
+ #
+ # === Shortened Code
+ #
+ # The example above is very verbose. You can shorten the code by using
+ # some utility methods. First, the block form of Net::POP3.start can
+ # be used instead of POP3.new, POP3#start and POP3#finish.
+ #
+ # require 'net/pop'
+ #
+ # Net::POP3.start('pop.example.com', 110,
+ # 'YourAccount', 'YourPassword') do |pop|
+ # if pop.mails.empty?
+ # puts 'No mail.'
+ # else
+ # i = 0
+ # pop.each_mail do |m| # or "pop.mails.each ..."
+ # File.open("inbox/#{i}", 'w') do |f|
+ # f.write m.pop
+ # end
+ # m.delete
+ # i += 1
+ # end
+ # puts "#{pop.mails.size} mails popped."
+ # end
+ # end
+ #
+ # POP3#delete_all is an alternative for #each_mail and #delete.
+ #
+ # require 'net/pop'
+ #
+ # Net::POP3.start('pop.example.com', 110,
+ # 'YourAccount', 'YourPassword') do |pop|
+ # if pop.mails.empty?
+ # puts 'No mail.'
+ # else
+ # i = 1
+ # pop.delete_all do |m|
+ # File.open("inbox/#{i}", 'w') do |f|
+ # f.write m.pop
+ # end
+ # i += 1
+ # end
+ # end
+ # end
+ #
+ # And here is an even shorter example.
+ #
+ # require 'net/pop'
+ #
+ # i = 0
+ # Net::POP3.delete_all('pop.example.com', 110,
+ # 'YourAccount', 'YourPassword') do |m|
+ # File.open("inbox/#{i}", 'w') do |f|
+ # f.write m.pop
+ # end
+ # i += 1
+ # end
+ #
+ # === Memory Space Issues
+ #
+ # All the examples above get each message as one big string.
+ # This example avoids this.
+ #
+ # require 'net/pop'
+ #
+ # i = 1
+ # Net::POP3.delete_all('pop.example.com', 110,
+ # 'YourAccount', 'YourPassword') do |m|
+ # File.open("inbox/#{i}", 'w') do |f|
+ # m.pop do |chunk| # get a message little by little.
+ # f.write chunk
+ # end
+ # i += 1
+ # end
+ # end
+ #
+ # === Using APOP
+ #
+ # The net/pop library supports APOP authentication.
+ # To use APOP, use the Net::APOP class instead of the Net::POP3 class.
+ # You can use the utility method, Net::POP3.APOP(). For example:
+ #
+ # require 'net/pop'
+ #
+ # # Use APOP authentication if $isapop == true
+ # pop = Net::POP3.APOP($is_apop).new('apop.example.com', 110)
+ # pop.start(YourAccount', 'YourPassword') do |pop|
+ # # Rest of the code is the same.
+ # end
+ #
+ # === Fetch Only Selected Mail Using 'UIDL' POP Command
+ #
+ # If your POP server provides UIDL functionality,
+ # you can grab only selected mails from the POP server.
+ # e.g.
+ #
+ # def need_pop?( id )
+ # # determine if we need pop this mail...
+ # end
+ #
+ # Net::POP3.start('pop.example.com', 110,
+ # 'Your account', 'Your password') do |pop|
+ # pop.mails.select { |m| need_pop?(m.unique_id) }.each do |m|
+ # do_something(m.pop)
+ # end
+ # end
+ #
+ # The POPMail#unique_id() method returns the unique-id of the message as a
+ # String. Normally the unique-id is a hash of the message.
+ #
+ class POP3 < Protocol
+
+ Revision = %q$Revision: 1.62.2.4 $.split[1]
+
+ #
+ # Class Parameters
+ #
+
+ # The default port for POP3 connections, port 110
+ def POP3.default_port
+ 110
+ end
+
+ def POP3.socket_type #:nodoc: obsolete
+ Net::InternetMessageIO
+ end
+
+ #
+ # Utilities
+ #
+
+ # Returns the APOP class if +isapop+ is true; otherwise, returns
+ # the POP class. For example:
+ #
+ # # Example 1
+ # pop = Net::POP3::APOP($is_apop).new(addr, port)
+ #
+ # # Example 2
+ # Net::POP3::APOP($is_apop).start(addr, port) do |pop|
+ # ....
+ # end
+ #
+ def POP3.APOP( isapop )
+ isapop ? APOP : POP3
+ end
+
+ # Starts a POP3 session and iterates over each POPMail object,
+ # yielding it to the +block+.
+ # This method is equivalent to:
+ #
+ # Net::POP3.start(address, port, account, password) do |pop|
+ # pop.each_mail do |m|
+ # yield m
+ # end
+ # end
+ #
+ # This method raises a POPAuthenticationError if authentication fails.
+ #
+ # === Example
+ #
+ # Net::POP3.foreach('pop.example.com', 110,
+ # 'YourAccount', 'YourPassword') do |m|
+ # file.write m.pop
+ # m.delete if $DELETE
+ # end
+ #
+ def POP3.foreach( address, port = nil,
+ account = nil, password = nil,
+ isapop = false, &block ) # :yields: message
+ start(address, port, account, password, isapop) {|pop|
+ pop.each_mail(&block)
+ }
+ end
+
+ # Starts a POP3 session and deletes all messages on the server.
+ # If a block is given, each POPMail object is yielded to it before
+ # being deleted.
+ #
+ # This method raises a POPAuthenticationError if authentication fails.
+ #
+ # === Example
+ #
+ # Net::POP3.delete_all('pop.example.com', 110,
+ # 'YourAccount', 'YourPassword') do |m|
+ # file.write m.pop
+ # end
+ #
+ def POP3.delete_all( address, port = nil,
+ account = nil, password = nil,
+ isapop = false, &block )
+ start(address, port, account, password, isapop) {|pop|
+ pop.delete_all(&block)
+ }
+ end
+
+ # Opens a POP3 session, attempts authentication, and quits.
+ #
+ # This method raises POPAuthenticationError if authentication fails.
+ #
+ # === Example: normal POP3
+ #
+ # Net::POP3.auth_only('pop.example.com', 110,
+ # 'YourAccount', 'YourPassword')
+ #
+ # === Example: APOP
+ #
+ # Net::POP3.auth_only('pop.example.com', 110,
+ # 'YourAccount', 'YourPassword', true)
+ #
+ def POP3.auth_only( address, port = nil,
+ account = nil, password = nil,
+ isapop = false )
+ new(address, port, isapop).auth_only account, password
+ end
+
+ # Starts a pop3 session, attempts authentication, and quits.
+ # This method must not be called while POP3 session is opened.
+ # This method raises POPAuthenticationError if authentication fails.
+ def auth_only( account, password )
+ raise IOError, 'opening previously opened POP session' if started?
+ start(account, password) {
+ ;
+ }
+ end
+
+ #
+ # Session management
+ #
+
+ # Creates a new POP3 object and open the connection. Equivalent to
+ #
+ # Net::POP3.new(address, port, isapop).start(account, password)
+ #
+ # If +block+ is provided, yields the newly-opened POP3 object to it,
+ # and automatically closes it at the end of the session.
+ #
+ # === Example
+ #
+ # Net::POP3.start(addr, port, account, password) do |pop|
+ # pop.each_mail do |m|
+ # file.write m.pop
+ # m.delete
+ # end
+ # end
+ #
+ def POP3.start( address, port = nil,
+ account = nil, password = nil,
+ isapop = false, &block ) # :yield: pop
+ new(address, port, isapop).start(account, password, &block)
+ end
+
+ # Creates a new POP3 object.
+ #
+ # +address+ is the hostname or ip address of your POP3 server.
+ #
+ # The optional +port+ is the port to connect to; it defaults to 110.
+ #
+ # The optional +isapop+ specifies whether this connection is going
+ # to use APOP authentication; it defaults to +false+.
+ #
+ # This method does *not* open the TCP connection.
+ def initialize( addr, port = nil, isapop = false )
+ @address = addr
+ @port = port || self.class.default_port
+ @apop = isapop
+
+ @command = nil
+ @socket = nil
+ @started = false
+ @open_timeout = 30
+ @read_timeout = 60
+ @debug_output = nil
+
+ @mails = nil
+ @n_mails = nil
+ @n_bytes = nil
+ end
+
+ # Does this instance use APOP authentication?
+ def apop?
+ @apop
+ end
+
+ # Provide human-readable stringification of class state.
+ def inspect
+ "#<#{self.class} #{@address}:#{@port} open=#{@started}>"
+ end
+
+ # *WARNING*: This method causes a serious security hole.
+ # Use this method only for debugging.
+ #
+ # Set an output stream for debugging.
+ #
+ # === Example
+ #
+ # pop = Net::POP.new(addr, port)
+ # pop.set_debug_output $stderr
+ # pop.start(account, passwd) do |pop|
+ # ....
+ # end
+ #
+ def set_debug_output( arg )
+ @debug_output = arg
+ end
+
+ # The address to connect to.
+ attr_reader :address
+
+ # The port number to connect to.
+ attr_reader :port
+
+ # Seconds to wait until a connection is opened.
+ # If the POP3 object cannot open a connection within this time,
+ # it raises a TimeoutError exception.
+ attr_accessor :open_timeout
+
+ # Seconds to wait until reading one block (by one read(1) call).
+ # If the POP3 object cannot complete a read() within this time,
+ # it raises a TimeoutError exception.
+ attr_reader :read_timeout
+
+ # Set the read timeout.
+ def read_timeout=( sec )
+ @command.socket.read_timeout = sec if @command
+ @read_timeout = sec
+ end
+
+ # +true+ if the POP3 session has started.
+ def started?
+ @started
+ end
+
+ alias active? started? #:nodoc: obsolete
+
+ # Starts a POP3 session.
+ #
+ # When called with block, gives a POP3 object to the block and
+ # closes the session after block call finishes.
+ #
+ # This method raises a POPAuthenticationError if authentication fails.
+ def start( account, password ) # :yield: pop
+ raise IOError, 'POP session already started' if @started
+
+ if block_given?
+ begin
+ do_start account, password
+ return yield(self)
+ ensure
+ do_finish
+ end
+ else
+ do_start account, password
+ return self
+ end
+ end
+
+ def do_start( account, password )
+ @socket = self.class.socket_type.old_open(@address, @port,
+ @open_timeout, @read_timeout, @debug_output)
+ on_connect
+ @command = POP3Command.new(@socket)
+ if apop?
+ @command.apop account, password
+ else
+ @command.auth account, password
+ end
+ @started = true
+ ensure
+ do_finish if not @started
+ end
+ private :do_start
+
+ def on_connect
+ end
+ private :on_connect
+
+ # Finishes a POP3 session and closes TCP connection.
+ def finish
+ raise IOError, 'POP session not yet started' unless started?
+ do_finish
+ end
+
+ def do_finish
+ @mails = nil
+ @command.quit if @command
+ ensure
+ @started = false
+ @command = nil
+ @socket.close if @socket and not @socket.closed?
+ @socket = nil
+ end
+ private :do_finish
+
+ def command
+ raise IOError, 'POP session not opened yet' \
+ if not @socket or @socket.closed?
+ @command
+ end
+ private :command
+
+ #
+ # POP protocol wrapper
+ #
+
+ # Returns the number of messages on the POP server.
+ def n_mails
+ return @n_mails if @n_mails
+ @n_mails, @n_bytes = command().stat
+ @n_mails
+ end
+
+ # Returns the total size in bytes of all the messages on the POP server.
+ def n_bytes
+ return @n_bytes if @n_bytes
+ @n_mails, @n_bytes = command().stat
+ @n_bytes
+ end
+
+ # Returns an array of Net::POPMail objects, representing all the
+ # messages on the server. This array is renewed when the session
+ # restarts; otherwise, it is fetched from the server the first time
+ # this method is called (directly or indirectly) and cached.
+ #
+ # This method raises a POPError if an error occurs.
+ def mails
+ return @mails.dup if @mails
+ if n_mails() == 0
+ # some popd raises error for LIST on the empty mailbox.
+ @mails = []
+ return []
+ end
+
+ @mails = command().list.map {|num, size|
+ POPMail.new(num, size, self, command())
+ }
+ @mails.dup
+ end
+
+ # Yields each message to the passed-in block in turn.
+ # Equivalent to:
+ #
+ # pop3.mails.each do |popmail|
+ # ....
+ # end
+ #
+ # This method raises a POPError if an error occurs.
+ def each_mail( &block ) # :yield: message
+ mails().each(&block)
+ end
+
+ alias each each_mail
+
+ # Deletes all messages on the server.
+ #
+ # If called with a block, yields each message in turn before deleting it.
+ #
+ # === Example
+ #
+ # n = 1
+ # pop.delete_all do |m|
+ # File.open("inbox/#{n}") do |f|
+ # f.write m.pop
+ # end
+ # n += 1
+ # end
+ #
+ # This method raises a POPError if an error occurs.
+ #
+ def delete_all # :yield: message
+ mails().each do |m|
+ yield m if block_given?
+ m.delete unless m.deleted?
+ end
+ end
+
+ # Resets the session. This clears all "deleted" marks from messages.
+ #
+ # This method raises a POPError if an error occurs.
+ def reset
+ command().rset
+ mails().each do |m|
+ m.instance_eval {
+ @deleted = false
+ }
+ end
+ end
+
+ def set_all_uids #:nodoc: internal use only (called from POPMail#uidl)
+ command().uidl.each do |num, uid|
+ @mails.find {|m| m.number == num }.uid = uid
+ end
+ end
+
+ end # class POP3
+
+ # class aliases
+ POP = POP3
+ POPSession = POP3
+ POP3Session = POP3
+
+ #
+ # This class is equivalent to POP3, except that it uses APOP authentication.
+ #
+ class APOP < POP3
+ # Always returns true.
+ def apop?
+ true
+ end
+ end
+
+ # class aliases
+ APOPSession = APOP
+
+ #
+ # This class represents a message which exists on the POP server.
+ # Instances of this class are created by the POP3 class; they should
+ # not be directly created by the user.
+ #
+ class POPMail
+
+ def initialize( num, len, pop, cmd ) #:nodoc:
+ @number = num
+ @length = len
+ @pop = pop
+ @command = cmd
+ @deleted = false
+ @uid = nil
+ end
+
+ # The sequence number of the message on the server.
+ attr_reader :number
+
+ # The length of the message in octets.
+ attr_reader :length
+ alias size length
+
+ # Provide human-readable stringification of class state.
+ def inspect
+ "#<#{self.class} #{@number}#{@deleted ? ' deleted' : ''}>"
+ end
+
+ #
+ # This method fetches the message. If called with a block, the
+ # message is yielded to the block one chunk at a time. If called
+ # without a block, the message is returned as a String. The optional
+ # +dest+ argument will be prepended to the returned String; this
+ # argument is essentially obsolete.
+ #
+ # === Example without block
+ #
+ # POP3.start('pop.example.com', 110,
+ # 'YourAccount, 'YourPassword') do |pop|
+ # n = 1
+ # pop.mails.each do |popmail|
+ # File.open("inbox/#{n}", 'w') do |f|
+ # f.write popmail.pop
+ # end
+ # popmail.delete
+ # n += 1
+ # end
+ # end
+ #
+ # === Example with block
+ #
+ # POP3.start('pop.example.com', 110,
+ # 'YourAccount, 'YourPassword') do |pop|
+ # n = 1
+ # pop.mails.each do |popmail|
+ # File.open("inbox/#{n}", 'w') do |f|
+ # popmail.pop do |chunk| ####
+ # f.write chunk
+ # end
+ # end
+ # n += 1
+ # end
+ # end
+ #
+ # This method raises a POPError if an error occurs.
+ #
+ def pop( dest = '', &block ) # :yield: message_chunk
+ if block_given?
+ @command.retr(@number, &block)
+ nil
+ else
+ @command.retr(@number) do |chunk|
+ dest << chunk
+ end
+ dest
+ end
+ end
+
+ alias all pop #:nodoc: obsolete
+ alias mail pop #:nodoc: obsolete
+
+ # Fetches the message header and +lines+ lines of body.
+ #
+ # The optional +dest+ argument is obsolete.
+ #
+ # This method raises a POPError if an error occurs.
+ def top( lines, dest = '' )
+ @command.top(@number, lines) do |chunk|
+ dest << chunk
+ end
+ dest
+ end
+
+ # Fetches the message header.
+ #
+ # The optional +dest+ argument is obsolete.
+ #
+ # This method raises a POPError if an error occurs.
+ def header( dest = '' )
+ top(0, dest)
+ end
+
+ # Marks a message for deletion on the server. Deletion does not
+ # actually occur until the end of the session; deletion may be
+ # cancelled for _all_ marked messages by calling POP3#reset().
+ #
+ # This method raises a POPError if an error occurs.
+ #
+ # === Example
+ #
+ # POP3.start('pop.example.com', 110,
+ # 'YourAccount, 'YourPassword') do |pop|
+ # n = 1
+ # pop.mails.each do |popmail|
+ # File.open("inbox/#{n}", 'w') do |f|
+ # f.write popmail.pop
+ # end
+ # popmail.delete ####
+ # n += 1
+ # end
+ # end
+ #
+ def delete
+ @command.dele @number
+ @deleted = true
+ end
+
+ alias delete! delete #:nodoc: obsolete
+
+ # True if the mail has been deleted.
+ def deleted?
+ @deleted
+ end
+
+ # Returns the unique-id of the message.
+ # Normally the unique-id is a hash string of the message.
+ #
+ # This method raises a POPError if an error occurs.
+ def unique_id
+ return @uid if @uid
+ @pop.set_all_uids
+ @uid
+ end
+
+ alias uidl unique_id
+
+ def uid=( uid ) #:nodoc: internal use only (used from POP3#set_all_uids)
+ @uid = uid
+ end
+
+ end # class POPMail
+
+
+ class POP3Command #:nodoc: internal use only
+
+ def initialize( sock )
+ @socket = sock
+ @error_occured = false
+ res = check_response(critical { recv_response() })
+ @apop_stamp = res.slice(/<.+>/)
+ end
+
+ def inspect
+ "#<#{self.class} socket=#{@socket}>"
+ end
+
+ def auth( account, password )
+ check_response_auth(critical {
+ check_response_auth(get_response('USER %s', account))
+ get_response('PASS %s', password)
+ })
+ end
+
+ def apop( account, password )
+ raise POPAuthenticationError, 'not APOP server; cannot login' \
+ unless @apop_stamp
+ check_response_auth(critical {
+ get_response('APOP %s %s',
+ account,
+ Digest::MD5.hexdigest(@apop_stamp + password))
+ })
+ end
+
+ def list
+ critical {
+ getok 'LIST'
+ list = []
+ @socket.each_list_item do |line|
+ m = /\A(\d+)[ \t]+(\d+)/.match(line) or
+ raise POPBadResponse, "bad response: #{line}"
+ list.push [m[1].to_i, m[2].to_i]
+ end
+ return list
+ }
+ end
+
+ def stat
+ res = check_response(critical { get_response('STAT') })
+ m = /\A\+OK\s+(\d+)\s+(\d+)/.match(res) or
+ raise POPBadResponse, "wrong response format: #{res}"
+ [m[1].to_i, m[2].to_i]
+ end
+
+ def rset
+ check_response(critical { get_response 'RSET' })
+ end
+
+ def top( num, lines = 0, &block )
+ critical {
+ getok('TOP %d %d', num, lines)
+ @socket.each_message_chunk(&block)
+ }
+ end
+
+ def retr( num, &block )
+ critical {
+ getok('RETR %d', num)
+ @socket.each_message_chunk(&block)
+ }
+ end
+
+ def dele( num )
+ check_response(critical { get_response('DELE %d', num) })
+ end
+
+ def uidl( num = nil )
+ if num
+ res = check_response(critical { get_response('UIDL %d', num) })
+ return res.split(/ /)[1]
+ else
+ critical {
+ getok('UIDL')
+ table = {}
+ @socket.each_list_item do |line|
+ num, uid = line.split
+ table[num.to_i] = uid
+ end
+ return table
+ }
+ end
+ end
+
+ def quit
+ check_response(critical { get_response('QUIT') })
+ end
+
+ private
+
+ def getok( fmt, *fargs )
+ @socket.writeline sprintf(fmt, *fargs)
+ check_response(recv_response())
+ end
+
+ def get_response( fmt, *fargs )
+ @socket.writeline sprintf(fmt, *fargs)
+ recv_response()
+ end
+
+ def recv_response
+ @socket.readline
+ end
+
+ def check_response( res )
+ raise POPError, res unless /\A\+OK/i === res
+ res
+ end
+
+ def check_response_auth( res )
+ raise POPAuthenticationError, res unless /\A\+OK/i === res
+ res
+ end
+
+ def critical
+ return '+OK dummy ok response' if @error_occured
+ begin
+ return yield()
+ rescue Exception
+ @error_occured = true
+ raise
+ end
+ end
+
+ end # class POP3Command
+
+end # module Net
+
diff --git a/ruby_1_8_5/lib/net/protocol.rb b/ruby_1_8_5/lib/net/protocol.rb
new file mode 100644
index 0000000000..0fee78c63a
--- /dev/null
+++ b/ruby_1_8_5/lib/net/protocol.rb
@@ -0,0 +1,390 @@
+#
+# = net/protocol.rb
+#
+#--
+# Copyright (c) 1999-2005 Yukihiro Matsumoto
+# Copyright (c) 1999-2005 Minero Aoki
+#
+# written and maintained by Minero Aoki <aamine@loveruby.net>
+#
+# This program is free software. You can re-distribute and/or
+# modify this program under the same terms as Ruby itself,
+# Ruby Distribute License or GNU General Public License.
+#
+# $Id: protocol.rb,v 1.73.2.3 2005/09/13 07:27:18 aamine Exp $
+#++
+#
+# WARNING: This file is going to remove.
+# Do not rely on the implementation written in this file.
+#
+
+require 'socket'
+require 'timeout'
+
+module Net # :nodoc:
+
+ class Protocol #:nodoc: internal use only
+ private
+ def Protocol.protocol_param(name, val)
+ module_eval(<<-End, __FILE__, __LINE__ + 1)
+ def #{name}
+ #{val}
+ end
+ End
+ end
+ end
+
+
+ class ProtocolError < StandardError; end
+ class ProtoSyntaxError < ProtocolError; end
+ class ProtoFatalError < ProtocolError; end
+ class ProtoUnknownError < ProtocolError; end
+ class ProtoServerError < ProtocolError; end
+ class ProtoAuthError < ProtocolError; end
+ class ProtoCommandError < ProtocolError; end
+ class ProtoRetriableError < ProtocolError; end
+ ProtocRetryError = ProtoRetriableError
+
+
+ class BufferedIO #:nodoc: internal use only
+ def initialize(io)
+ @io = io
+ @read_timeout = 60
+ @debug_output = nil
+ @rbuf = ''
+ end
+
+ attr_reader :io
+ attr_accessor :read_timeout
+ attr_accessor :debug_output
+
+ def inspect
+ "#<#{self.class} io=#{@io}>"
+ end
+
+ def closed?
+ @io.closed?
+ end
+
+ def close
+ @io.close
+ end
+
+ #
+ # Read
+ #
+
+ public
+
+ def read(len, dest = '', ignore_eof = false)
+ LOG "reading #{len} bytes..."
+ read_bytes = 0
+ begin
+ while read_bytes + @rbuf.size < len
+ dest << (s = rbuf_consume(@rbuf.size))
+ read_bytes += s.size
+ rbuf_fill
+ end
+ dest << (s = rbuf_consume(len - read_bytes))
+ read_bytes += s.size
+ rescue EOFError
+ raise unless ignore_eof
+ end
+ LOG "read #{read_bytes} bytes"
+ dest
+ end
+
+ def read_all(dest = '')
+ LOG 'reading all...'
+ read_bytes = 0
+ begin
+ while true
+ dest << (s = rbuf_consume(@rbuf.size))
+ read_bytes += s.size
+ rbuf_fill
+ end
+ rescue EOFError
+ ;
+ end
+ LOG "read #{read_bytes} bytes"
+ dest
+ end
+
+ def readuntil(terminator, ignore_eof = false)
+ begin
+ until idx = @rbuf.index(terminator)
+ rbuf_fill
+ end
+ return rbuf_consume(idx + terminator.size)
+ rescue EOFError
+ raise unless ignore_eof
+ return rbuf_consume(@rbuf.size)
+ end
+ end
+
+ def readline
+ readuntil("\n").chop
+ end
+
+ private
+
+ def rbuf_fill
+ timeout(@read_timeout) {
+ @rbuf << @io.sysread(1024)
+ }
+ end
+
+ def rbuf_consume(len)
+ s = @rbuf.slice!(0, len)
+ @debug_output << %Q[-> #{s.dump}\n] if @debug_output
+ s
+ end
+
+ #
+ # Write
+ #
+
+ public
+
+ def write(str)
+ writing {
+ write0 str
+ }
+ end
+
+ def writeline(str)
+ writing {
+ write0 str + "\r\n"
+ }
+ end
+
+ private
+
+ def writing
+ @written_bytes = 0
+ @debug_output << '<- ' if @debug_output
+ yield
+ @debug_output << "\n" if @debug_output
+ bytes = @written_bytes
+ @written_bytes = nil
+ bytes
+ end
+
+ def write0(str)
+ @debug_output << str.dump if @debug_output
+ len = @io.write(str)
+ @written_bytes += len
+ len
+ end
+
+ #
+ # Logging
+ #
+
+ private
+
+ def LOG_off
+ @save_debug_out = @debug_output
+ @debug_output = nil
+ end
+
+ def LOG_on
+ @debug_output = @save_debug_out
+ end
+
+ def LOG(msg)
+ return unless @debug_output
+ @debug_output << msg + "\n"
+ end
+ end
+
+
+ class InternetMessageIO < BufferedIO #:nodoc: internal use only
+ def InternetMessageIO.old_open(addr, port,
+ open_timeout = nil, read_timeout = nil, debug_output = nil)
+ debug_output << "opening connection to #{addr}...\n" if debug_output
+ s = timeout(open_timeout) { TCPsocket.new(addr, port) }
+ io = new(s)
+ io.read_timeout = read_timeout
+ io.debug_output = debug_output
+ io
+ end
+
+ def initialize(io)
+ super
+ @wbuf = nil
+ end
+
+ #
+ # Read
+ #
+
+ def each_message_chunk
+ LOG 'reading message...'
+ LOG_off()
+ read_bytes = 0
+ while (line = readuntil("\r\n")) != ".\r\n"
+ read_bytes += line.size
+ yield line.sub(/\A\./, '')
+ end
+ LOG_on()
+ LOG "read message (#{read_bytes} bytes)"
+ end
+
+ # *library private* (cannot handle 'break')
+ def each_list_item
+ while (str = readuntil("\r\n")) != ".\r\n"
+ yield str.chop
+ end
+ end
+
+ def write_message_0(src)
+ prev = @written_bytes
+ each_crlf_line(src) do |line|
+ write0 line.sub(/\A\./, '..')
+ end
+ @written_bytes - prev
+ end
+
+ #
+ # Write
+ #
+
+ def write_message(src)
+ LOG "writing message from #{src.class}"
+ LOG_off()
+ len = writing {
+ using_each_crlf_line {
+ write_message_0 src
+ }
+ }
+ LOG_on()
+ LOG "wrote #{len} bytes"
+ len
+ end
+
+ def write_message_by_block(&block)
+ LOG 'writing message from block'
+ LOG_off()
+ len = writing {
+ using_each_crlf_line {
+ begin
+ block.call(WriteAdapter.new(self, :write_message_0))
+ rescue LocalJumpError
+ # allow `break' from writer block
+ end
+ }
+ }
+ LOG_on()
+ LOG "wrote #{len} bytes"
+ len
+ end
+
+ private
+
+ def using_each_crlf_line
+ @wbuf = ''
+ yield
+ if not @wbuf.empty? # unterminated last line
+ write0 @wbuf.chomp + "\r\n"
+ elsif @written_bytes == 0 # empty src
+ write0 "\r\n"
+ end
+ write0 ".\r\n"
+ @wbuf = nil
+ end
+
+ def each_crlf_line(src)
+ buffer_filling(@wbuf, src) do
+ while line = @wbuf.slice!(/\A.*(?:\n|\r\n|\r(?!\z))/n)
+ yield line.chomp("\n") + "\r\n"
+ end
+ end
+ end
+
+ def buffer_filling(buf, src)
+ case src
+ when String # for speeding up.
+ 0.step(src.size - 1, 1024) do |i|
+ buf << src[i, 1024]
+ yield
+ end
+ when File # for speeding up.
+ while s = src.read(1024)
+ buf << s
+ yield
+ end
+ else # generic reader
+ src.each do |s|
+ buf << s
+ yield if buf.size > 1024
+ end
+ yield unless buf.empty?
+ end
+ end
+ end
+
+
+ #
+ # The writer adapter class
+ #
+ class WriteAdapter
+ def initialize(socket, method)
+ @socket = socket
+ @method_id = method
+ end
+
+ def inspect
+ "#<#{self.class} socket=#{@socket.inspect}>"
+ end
+
+ def write(str)
+ @socket.__send__(@method_id, str)
+ end
+
+ alias print write
+
+ def <<(str)
+ write str
+ self
+ end
+
+ def puts(str = '')
+ write str.chomp("\n") + "\n"
+ end
+
+ def printf(*args)
+ write sprintf(*args)
+ end
+ end
+
+
+ class ReadAdapter #:nodoc: internal use only
+ def initialize(block)
+ @block = block
+ end
+
+ def inspect
+ "#<#{self.class}>"
+ end
+
+ def <<(str)
+ call_block(str, &@block) if @block
+ end
+
+ private
+
+ # This method is needed because @block must be called by yield,
+ # not Proc#call. You can see difference when using `break' in
+ # the block.
+ def call_block(str)
+ yield str
+ end
+ end
+
+
+ module NetPrivate #:nodoc: obsolete
+ Socket = ::Net::InternetMessageIO
+ end
+
+end # module Net
diff --git a/ruby_1_8_5/lib/net/smtp.rb b/ruby_1_8_5/lib/net/smtp.rb
new file mode 100644
index 0000000000..89929b1c6e
--- /dev/null
+++ b/ruby_1_8_5/lib/net/smtp.rb
@@ -0,0 +1,697 @@
+# = net/smtp.rb
+#
+# Copyright (c) 1999-2003 Yukihiro Matsumoto.
+#
+# Copyright (c) 1999-2003 Minero Aoki.
+#
+# Written & maintained by Minero Aoki <aamine@loveruby.net>.
+#
+# Documented by William Webber and Minero Aoki.
+#
+# This program is free software. You can re-distribute and/or
+# modify this program under the same terms as Ruby itself,
+# Ruby Distribute License or GNU General Public License.
+#
+# NOTE: You can find Japanese version of this document in
+# the doc/net directory of the standard ruby interpreter package.
+#
+# $Id: smtp.rb,v 1.69.2.3 2005/09/13 07:27:18 aamine Exp $
+#
+# See Net::SMTP for documentation.
+#
+
+require 'net/protocol'
+require 'digest/md5'
+
+module Net
+
+ # Module mixed in to all SMTP error classes
+ module SMTPError
+ # This *class* is module for some reason.
+ # In ruby 1.9.x, this module becomes a class.
+ end
+
+ # Represents an SMTP authentication error.
+ class SMTPAuthenticationError < ProtoAuthError
+ include SMTPError
+ end
+
+ # Represents SMTP error code 420 or 450, a temporary error.
+ class SMTPServerBusy < ProtoServerError
+ include SMTPError
+ end
+
+ # Represents an SMTP command syntax error (error code 500)
+ class SMTPSyntaxError < ProtoSyntaxError
+ include SMTPError
+ end
+
+ # Represents a fatal SMTP error (error code 5xx, except for 500)
+ class SMTPFatalError < ProtoFatalError
+ include SMTPError
+ end
+
+ # Unexpected reply code returned from server.
+ class SMTPUnknownError < ProtoUnknownError
+ include SMTPError
+ end
+
+ #
+ # = Net::SMTP
+ #
+ # == What is This Library?
+ #
+ # This library provides functionality to send internet
+ # mail via SMTP, the Simple Mail Transfer Protocol. For details of
+ # SMTP itself, see [RFC2821] (http://www.ietf.org/rfc/rfc2821.txt).
+ #
+ # == What is This Library NOT?
+ #
+ # This library does NOT provide functions to compose internet mails.
+ # You must create them by yourself. If you want better mail support,
+ # try RubyMail or TMail. You can get both libraries from RAA.
+ # (http://www.ruby-lang.org/en/raa.html)
+ #
+ # FYI: the official documentation on internet mail is: [RFC2822] (http://www.ietf.org/rfc/rfc2822.txt).
+ #
+ # == Examples
+ #
+ # === Sending Messages
+ #
+ # You must open a connection to an SMTP server before sending messages.
+ # The first argument is the address of your SMTP server, and the second
+ # argument is the port number. Using SMTP.start with a block is the simplest
+ # way to do this. This way, the SMTP connection is closed automatically
+ # after the block is executed.
+ #
+ # require 'net/smtp'
+ # Net::SMTP.start('your.smtp.server', 25) do |smtp|
+ # # Use the SMTP object smtp only in this block.
+ # end
+ #
+ # Replace 'your.smtp.server' with your SMTP server. Normally
+ # your system manager or internet provider supplies a server
+ # for you.
+ #
+ # Then you can send messages.
+ #
+ # msgstr = <<END_OF_MESSAGE
+ # From: Your Name <your@mail.address>
+ # To: Destination Address <someone@example.com>
+ # Subject: test message
+ # Date: Sat, 23 Jun 2001 16:26:43 +0900
+ # Message-Id: <unique.message.id.string@example.com>
+ #
+ # This is a test message.
+ # END_OF_MESSAGE
+ #
+ # require 'net/smtp'
+ # Net::SMTP.start('your.smtp.server', 25) do |smtp|
+ # smtp.send_message msgstr,
+ # 'your@mail.address',
+ # 'his_addess@example.com'
+ # end
+ #
+ # === Closing the Session
+ #
+ # You MUST close the SMTP session after sending messages, by calling
+ # the #finish method:
+ #
+ # # using SMTP#finish
+ # smtp = Net::SMTP.start('your.smtp.server', 25)
+ # smtp.send_message msgstr, 'from@address', 'to@address'
+ # smtp.finish
+ #
+ # You can also use the block form of SMTP.start/SMTP#start. This closes
+ # the SMTP session automatically:
+ #
+ # # using block form of SMTP.start
+ # Net::SMTP.start('your.smtp.server', 25) do |smtp|
+ # smtp.send_message msgstr, 'from@address', 'to@address'
+ # end
+ #
+ # I strongly recommend this scheme. This form is simpler and more robust.
+ #
+ # === HELO domain
+ #
+ # In almost all situations, you must provide a third argument
+ # to SMTP.start/SMTP#start. This is the domain name which you are on
+ # (the host to send mail from). It is called the "HELO domain".
+ # The SMTP server will judge whether it should send or reject
+ # the SMTP session by inspecting the HELO domain.
+ #
+ # Net::SMTP.start('your.smtp.server', 25,
+ # 'mail.from.domain') { |smtp| ... }
+ #
+ # === SMTP Authentication
+ #
+ # The Net::SMTP class supports three authentication schemes;
+ # PLAIN, LOGIN and CRAM MD5. (SMTP Authentication: [RFC2554])
+ # To use SMTP authentication, pass extra arguments to
+ # SMTP.start/SMTP#start.
+ #
+ # # PLAIN
+ # Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain',
+ # 'Your Account', 'Your Password', :plain)
+ # # LOGIN
+ # Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain',
+ # 'Your Account', 'Your Password', :login)
+ #
+ # # CRAM MD5
+ # Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain',
+ # 'Your Account', 'Your Password', :cram_md5)
+ #
+ class SMTP
+
+ Revision = %q$Revision: 1.69.2.3 $.split[1]
+
+ # The default SMTP port, port 25.
+ def SMTP.default_port
+ 25
+ end
+
+ #
+ # Creates a new Net::SMTP object.
+ #
+ # +address+ is the hostname or ip address of your SMTP
+ # server. +port+ is the port to connect to; it defaults to
+ # port 25.
+ #
+ # This method does not open the TCP connection. You can use
+ # SMTP.start instead of SMTP.new if you want to do everything
+ # at once. Otherwise, follow SMTP.new with SMTP#start.
+ #
+ def initialize( address, port = nil )
+ @address = address
+ @port = (port || SMTP.default_port)
+ @esmtp = true
+ @socket = nil
+ @started = false
+ @open_timeout = 30
+ @read_timeout = 60
+ @error_occured = false
+ @debug_output = nil
+ end
+
+ # Provide human-readable stringification of class state.
+ def inspect
+ "#<#{self.class} #{@address}:#{@port} started=#{@started}>"
+ end
+
+ # +true+ if the SMTP object uses ESMTP (which it does by default).
+ def esmtp?
+ @esmtp
+ end
+
+ #
+ # Set whether to use ESMTP or not. This should be done before
+ # calling #start. Note that if #start is called in ESMTP mode,
+ # and the connection fails due to a ProtocolError, the SMTP
+ # object will automatically switch to plain SMTP mode and
+ # retry (but not vice versa).
+ #
+ def esmtp=( bool )
+ @esmtp = bool
+ end
+
+ alias esmtp esmtp?
+
+ # The address of the SMTP server to connect to.
+ attr_reader :address
+
+ # The port number of the SMTP server to connect to.
+ attr_reader :port
+
+ # Seconds to wait while attempting to open a connection.
+ # If the connection cannot be opened within this time, a
+ # TimeoutError is raised.
+ attr_accessor :open_timeout
+
+ # Seconds to wait while reading one block (by one read(2) call).
+ # If the read(2) call does not complete within this time, a
+ # TimeoutError is raised.
+ attr_reader :read_timeout
+
+ # Set the number of seconds to wait until timing-out a read(2)
+ # call.
+ def read_timeout=( sec )
+ @socket.read_timeout = sec if @socket
+ @read_timeout = sec
+ end
+
+ #
+ # WARNING: This method causes serious security holes.
+ # Use this method for only debugging.
+ #
+ # Set an output stream for debug logging.
+ # You must call this before #start.
+ #
+ # # example
+ # smtp = Net::SMTP.new(addr, port)
+ # smtp.set_debug_output $stderr
+ # smtp.start do |smtp|
+ # ....
+ # end
+ #
+ def set_debug_output( arg )
+ @debug_output = arg
+ end
+
+ #
+ # SMTP session control
+ #
+
+ #
+ # Creates a new Net::SMTP object and connects to the server.
+ #
+ # This method is equivalent to:
+ #
+ # Net::SMTP.new(address, port).start(helo_domain, account, password, authtype)
+ #
+ # === Example
+ #
+ # Net::SMTP.start('your.smtp.server') do |smtp|
+ # smtp.send_message msgstr, 'from@example.com', ['dest@example.com']
+ # end
+ #
+ # === Block Usage
+ #
+ # If called with a block, the newly-opened Net::SMTP object is yielded
+ # to the block, and automatically closed when the block finishes. If called
+ # without a block, the newly-opened Net::SMTP object is returned to
+ # the caller, and it is the caller's responsibility to close it when
+ # finished.
+ #
+ # === Parameters
+ #
+ # +address+ is the hostname or ip address of your smtp server.
+ #
+ # +port+ is the port to connect to; it defaults to port 25.
+ #
+ # +helo+ is the _HELO_ _domain_ provided by the client to the
+ # server (see overview comments); it defaults to 'localhost.localdomain'.
+ #
+ # The remaining arguments are used for SMTP authentication, if required
+ # or desired. +user+ is the account name; +secret+ is your password
+ # or other authentication token; and +authtype+ is the authentication
+ # type, one of :plain, :login, or :cram_md5. See the discussion of
+ # SMTP Authentication in the overview notes.
+ #
+ # === Errors
+ #
+ # This method may raise:
+ #
+ # * Net::SMTPAuthenticationError
+ # * Net::SMTPServerBusy
+ # * Net::SMTPSyntaxError
+ # * Net::SMTPFatalError
+ # * Net::SMTPUnknownError
+ # * IOError
+ # * TimeoutError
+ #
+ def SMTP.start( address, port = nil,
+ helo = 'localhost.localdomain',
+ user = nil, secret = nil, authtype = nil,
+ &block) # :yield: smtp
+ new(address, port).start(helo, user, secret, authtype, &block)
+ end
+
+ # +true+ if the SMTP session has been started.
+ def started?
+ @started
+ end
+
+ #
+ # Opens a TCP connection and starts the SMTP session.
+ #
+ # === Parameters
+ #
+ # +helo+ is the _HELO_ _domain_ that you'll dispatch mails from; see
+ # the discussion in the overview notes.
+ #
+ # If both of +user+ and +secret+ are given, SMTP authentication
+ # will be attempted using the AUTH command. +authtype+ specifies
+ # the type of authentication to attempt; it must be one of
+ # :login, :plain, and :cram_md5. See the notes on SMTP Authentication
+ # in the overview.
+ #
+ # === Block Usage
+ #
+ # When this methods is called with a block, the newly-started SMTP
+ # object is yielded to the block, and automatically closed after
+ # the block call finishes. Otherwise, it is the caller's
+ # responsibility to close the session when finished.
+ #
+ # === Example
+ #
+ # This is very similar to the class method SMTP.start.
+ #
+ # require 'net/smtp'
+ # smtp = Net::SMTP.new('smtp.mail.server', 25)
+ # smtp.start(helo_domain, account, password, authtype) do |smtp|
+ # smtp.send_message msgstr, 'from@example.com', ['dest@example.com']
+ # end
+ #
+ # The primary use of this method (as opposed to SMTP.start)
+ # is probably to set debugging (#set_debug_output) or ESMTP
+ # (#esmtp=), which must be done before the session is
+ # started.
+ #
+ # === Errors
+ #
+ # If session has already been started, an IOError will be raised.
+ #
+ # This method may raise:
+ #
+ # * Net::SMTPAuthenticationError
+ # * Net::SMTPServerBusy
+ # * Net::SMTPSyntaxError
+ # * Net::SMTPFatalError
+ # * Net::SMTPUnknownError
+ # * IOError
+ # * TimeoutError
+ #
+ def start( helo = 'localhost.localdomain',
+ user = nil, secret = nil, authtype = nil ) # :yield: smtp
+ if block_given?
+ begin
+ do_start(helo, user, secret, authtype)
+ return yield(self)
+ ensure
+ do_finish
+ end
+ else
+ do_start(helo, user, secret, authtype)
+ return self
+ end
+ end
+
+ def do_start( helodomain, user, secret, authtype )
+ raise IOError, 'SMTP session already started' if @started
+ check_auth_args user, secret, authtype if user or secret
+
+ @socket = InternetMessageIO.old_open(@address, @port,
+ @open_timeout, @read_timeout,
+ @debug_output)
+ check_response(critical { recv_response() })
+ begin
+ if @esmtp
+ ehlo helodomain
+ else
+ helo helodomain
+ end
+ rescue ProtocolError
+ if @esmtp
+ @esmtp = false
+ @error_occured = false
+ retry
+ end
+ raise
+ end
+ authenticate user, secret, authtype if user
+ @started = true
+ ensure
+ @socket.close if not @started and @socket and not @socket.closed?
+ end
+ private :do_start
+
+ # Finishes the SMTP session and closes TCP connection.
+ # Raises IOError if not started.
+ def finish
+ raise IOError, 'not yet started' unless started?
+ do_finish
+ end
+
+ def do_finish
+ quit if @socket and not @socket.closed? and not @error_occured
+ ensure
+ @started = false
+ @error_occured = false
+ @socket.close if @socket and not @socket.closed?
+ @socket = nil
+ end
+ private :do_finish
+
+ #
+ # message send
+ #
+
+ public
+
+ #
+ # Sends +msgstr+ as a message. Single CR ("\r") and LF ("\n") found
+ # in the +msgstr+, are converted into the CR LF pair. You cannot send a
+ # binary message with this method. +msgstr+ should include both
+ # the message headers and body.
+ #
+ # +from_addr+ is a String representing the source mail address.
+ #
+ # +to_addr+ is a String or Strings or Array of Strings, representing
+ # the destination mail address or addresses.
+ #
+ # === Example
+ #
+ # Net::SMTP.start('smtp.example.com') do |smtp|
+ # smtp.send_message msgstr,
+ # 'from@example.com',
+ # ['dest@example.com', 'dest2@example.com']
+ # end
+ #
+ # === Errors
+ #
+ # This method may raise:
+ #
+ # * Net::SMTPServerBusy
+ # * Net::SMTPSyntaxError
+ # * Net::SMTPFatalError
+ # * Net::SMTPUnknownError
+ # * IOError
+ # * TimeoutError
+ #
+ def send_message( msgstr, from_addr, *to_addrs )
+ send0(from_addr, to_addrs.flatten) {
+ @socket.write_message msgstr
+ }
+ end
+
+ alias send_mail send_message
+ alias sendmail send_message # obsolete
+
+ #
+ # Opens a message writer stream and gives it to the block.
+ # The stream is valid only in the block, and has these methods:
+ #
+ # puts(str = ''):: outputs STR and CR LF.
+ # print(str):: outputs STR.
+ # printf(fmt, *args):: outputs sprintf(fmt,*args).
+ # write(str):: outputs STR and returns the length of written bytes.
+ # <<(str):: outputs STR and returns self.
+ #
+ # If a single CR ("\r") or LF ("\n") is found in the message,
+ # it is converted to the CR LF pair. You cannot send a binary
+ # message with this method.
+ #
+ # === Parameters
+ #
+ # +from_addr+ is a String representing the source mail address.
+ #
+ # +to_addr+ is a String or Strings or Array of Strings, representing
+ # the destination mail address or addresses.
+ #
+ # === Example
+ #
+ # Net::SMTP.start('smtp.example.com', 25) do |smtp|
+ # smtp.open_message_stream('from@example.com', ['dest@example.com']) do |f|
+ # f.puts 'From: from@example.com'
+ # f.puts 'To: dest@example.com'
+ # f.puts 'Subject: test message'
+ # f.puts
+ # f.puts 'This is a test message.'
+ # end
+ # end
+ #
+ # === Errors
+ #
+ # This method may raise:
+ #
+ # * Net::SMTPServerBusy
+ # * Net::SMTPSyntaxError
+ # * Net::SMTPFatalError
+ # * Net::SMTPUnknownError
+ # * IOError
+ # * TimeoutError
+ #
+ def open_message_stream( from_addr, *to_addrs, &block ) # :yield: stream
+ send0(from_addr, to_addrs.flatten) {
+ @socket.write_message_by_block(&block)
+ }
+ end
+
+ alias ready open_message_stream # obsolete
+
+ private
+
+ def send0( from_addr, to_addrs )
+ raise IOError, 'closed session' unless @socket
+ raise ArgumentError, 'mail destination not given' if to_addrs.empty?
+ if $SAFE > 0
+ raise SecurityError, 'tainted from_addr' if from_addr.tainted?
+ to_addrs.each do |to|
+ raise SecurityError, 'tainted to_addr' if to.tainted?
+ end
+ end
+
+ mailfrom from_addr
+ to_addrs.each do |to|
+ rcptto to
+ end
+ res = critical {
+ check_response(get_response('DATA'), true)
+ yield
+ recv_response()
+ }
+ check_response(res)
+ end
+
+ #
+ # auth
+ #
+
+ private
+
+ def check_auth_args( user, secret, authtype )
+ raise ArgumentError, 'both user and secret are required'\
+ unless user and secret
+ auth_method = "auth_#{authtype || 'cram_md5'}"
+ raise ArgumentError, "wrong auth type #{authtype}"\
+ unless respond_to?(auth_method, true)
+ end
+
+ def authenticate( user, secret, authtype )
+ __send__("auth_#{authtype || 'cram_md5'}", user, secret)
+ end
+
+ def auth_plain( user, secret )
+ res = critical { get_response('AUTH PLAIN %s',
+ base64_encode("\0#{user}\0#{secret}")) }
+ raise SMTPAuthenticationError, res unless /\A2../ === res
+ end
+
+ def auth_login( user, secret )
+ res = critical {
+ check_response(get_response('AUTH LOGIN'), true)
+ check_response(get_response(base64_encode(user)), true)
+ get_response(base64_encode(secret))
+ }
+ raise SMTPAuthenticationError, res unless /\A2../ === res
+ end
+
+ def auth_cram_md5( user, secret )
+ # CRAM-MD5: [RFC2195]
+ res = nil
+ critical {
+ res = check_response(get_response('AUTH CRAM-MD5'), true)
+ challenge = res.split(/ /)[1].unpack('m')[0]
+ secret = Digest::MD5.digest(secret) if secret.size > 64
+
+ isecret = secret + "\0" * (64 - secret.size)
+ osecret = isecret.dup
+ 0.upto(63) do |i|
+ isecret[i] ^= 0x36
+ osecret[i] ^= 0x5c
+ end
+ tmp = Digest::MD5.digest(isecret + challenge)
+ tmp = Digest::MD5.hexdigest(osecret + tmp)
+
+ res = get_response(base64_encode(user + ' ' + tmp))
+ }
+ raise SMTPAuthenticationError, res unless /\A2../ === res
+ end
+
+ def base64_encode( str )
+ # expects "str" may not become too long
+ [str].pack('m').gsub(/\s+/, '')
+ end
+
+ #
+ # SMTP command dispatcher
+ #
+
+ private
+
+ def helo( domain )
+ getok('HELO %s', domain)
+ end
+
+ def ehlo( domain )
+ getok('EHLO %s', domain)
+ end
+
+ def mailfrom( fromaddr )
+ getok('MAIL FROM:<%s>', fromaddr)
+ end
+
+ def rcptto( to )
+ getok('RCPT TO:<%s>', to)
+ end
+
+ def quit
+ getok('QUIT')
+ end
+
+ #
+ # row level library
+ #
+
+ private
+
+ def getok( fmt, *args )
+ res = critical {
+ @socket.writeline sprintf(fmt, *args)
+ recv_response()
+ }
+ return check_response(res)
+ end
+
+ def get_response( fmt, *args )
+ @socket.writeline sprintf(fmt, *args)
+ recv_response()
+ end
+
+ def recv_response
+ res = ''
+ while true
+ line = @socket.readline
+ res << line << "\n"
+ break unless line[3] == ?- # "210-PIPELINING"
+ end
+ res
+ end
+
+ def check_response( res, allow_continue = false )
+ return res if /\A2/ === res
+ return res if allow_continue and /\A3/ === res
+ err = case res
+ when /\A4/ then SMTPServerBusy
+ when /\A50/ then SMTPSyntaxError
+ when /\A55/ then SMTPFatalError
+ else SMTPUnknownError
+ end
+ raise err, res
+ end
+
+ def critical( &block )
+ return '200 dummy reply code' if @error_occured
+ begin
+ return yield()
+ rescue Exception
+ @error_occured = true
+ raise
+ end
+ end
+
+ end # class SMTP
+
+ SMTPSession = SMTP
+
+end # module Net
diff --git a/ruby_1_8_5/lib/net/telnet.rb b/ruby_1_8_5/lib/net/telnet.rb
new file mode 100644
index 0000000000..a2f6065dd7
--- /dev/null
+++ b/ruby_1_8_5/lib/net/telnet.rb
@@ -0,0 +1,742 @@
+# = net/telnet.rb - Simple Telnet Client Library
+#
+# Author:: Wakou Aoyama <wakou@ruby-lang.org>
+# Documentation:: William Webber and Wakou Aoyama
+#
+# This file holds the class Net::Telnet, which provides client-side
+# telnet functionality.
+#
+# For documentation, see Net::Telnet.
+#
+
+require "socket"
+require "delegate"
+require "timeout"
+require "English"
+
+module Net
+
+ #
+ # == Net::Telnet
+ #
+ # Provides telnet client functionality.
+ #
+ # This class also has, through delegation, all the methods of a
+ # socket object (by default, a +TCPSocket+, but can be set by the
+ # +Proxy+ option to <tt>new()</tt>). This provides methods such as
+ # <tt>close()</tt> to end the session and <tt>sysread()</tt> to read
+ # data directly from the host, instead of via the <tt>waitfor()</tt>
+ # mechanism. Note that if you do use <tt>sysread()</tt> directly
+ # when in telnet mode, you should probably pass the output through
+ # <tt>preprocess()</tt> to extract telnet command sequences.
+ #
+ # == Overview
+ #
+ # The telnet protocol allows a client to login remotely to a user
+ # account on a server and execute commands via a shell. The equivalent
+ # is done by creating a Net::Telnet class with the +Host+ option
+ # set to your host, calling #login() with your user and password,
+ # issuing one or more #cmd() calls, and then calling #close()
+ # to end the session. The #waitfor(), #print(), #puts(), and
+ # #write() methods, which #cmd() is implemented on top of, are
+ # only needed if you are doing something more complicated.
+ #
+ # A Net::Telnet object can also be used to connect to non-telnet
+ # services, such as SMTP or HTTP. In this case, you normally
+ # want to provide the +Port+ option to specify the port to
+ # connect to, and set the +Telnetmode+ option to false to prevent
+ # the client from attempting to interpret telnet command sequences.
+ # Generally, #login() will not work with other protocols, and you
+ # have to handle authentication yourself.
+ #
+ # For some protocols, it will be possible to specify the +Prompt+
+ # option once when you create the Telnet object and use #cmd() calls;
+ # for others, you will have to specify the response sequence to
+ # look for as the Match option to every #cmd() call, or call
+ # #puts() and #waitfor() directly; for yet others, you will have
+ # to use #sysread() instead of #waitfor() and parse server
+ # responses yourself.
+ #
+ # It is worth noting that when you create a new Net::Telnet object,
+ # you can supply a proxy IO channel via the Proxy option. This
+ # can be used to attach the Telnet object to other Telnet objects,
+ # to already open sockets, or to any read-write IO object. This
+ # can be useful, for instance, for setting up a test fixture for
+ # unit testing.
+ #
+ # == Examples
+ #
+ # === Log in and send a command, echoing all output to stdout
+ #
+ # localhost = Net::Telnet::new("Host" => "localhost",
+ # "Timeout" => 10,
+ # "Prompt" => /[$%#>] \z/n)
+ # localhost.login("username", "password") { |c| print c }
+ # localhost.cmd("command") { |c| print c }
+ # localhost.close
+ #
+ #
+ # === Check a POP server to see if you have mail
+ #
+ # pop = Net::Telnet::new("Host" => "your_destination_host_here",
+ # "Port" => 110,
+ # "Telnetmode" => false,
+ # "Prompt" => /^\+OK/n)
+ # pop.cmd("user " + "your_username_here") { |c| print c }
+ # pop.cmd("pass " + "your_password_here") { |c| print c }
+ # pop.cmd("list") { |c| print c }
+ #
+ # == References
+ #
+ # There are a large number of RFCs relevant to the Telnet protocol.
+ # RFCs 854-861 define the base protocol. For a complete listing
+ # of relevant RFCs, see
+ # http://www.omnifarious.org/~hopper/technical/telnet-rfc.html
+ #
+ class Telnet < SimpleDelegator
+
+ # :stopdoc:
+ IAC = 255.chr # "\377" # "\xff" # interpret as command
+ DONT = 254.chr # "\376" # "\xfe" # you are not to use option
+ DO = 253.chr # "\375" # "\xfd" # please, you use option
+ WONT = 252.chr # "\374" # "\xfc" # I won't use option
+ WILL = 251.chr # "\373" # "\xfb" # I will use option
+ SB = 250.chr # "\372" # "\xfa" # interpret as subnegotiation
+ GA = 249.chr # "\371" # "\xf9" # you may reverse the line
+ EL = 248.chr # "\370" # "\xf8" # erase the current line
+ EC = 247.chr # "\367" # "\xf7" # erase the current character
+ AYT = 246.chr # "\366" # "\xf6" # are you there
+ AO = 245.chr # "\365" # "\xf5" # abort output--but let prog finish
+ IP = 244.chr # "\364" # "\xf4" # interrupt process--permanently
+ BREAK = 243.chr # "\363" # "\xf3" # break
+ DM = 242.chr # "\362" # "\xf2" # data mark--for connect. cleaning
+ NOP = 241.chr # "\361" # "\xf1" # nop
+ SE = 240.chr # "\360" # "\xf0" # end sub negotiation
+ EOR = 239.chr # "\357" # "\xef" # end of record (transparent mode)
+ ABORT = 238.chr # "\356" # "\xee" # Abort process
+ SUSP = 237.chr # "\355" # "\xed" # Suspend process
+ EOF = 236.chr # "\354" # "\xec" # End of file
+ SYNCH = 242.chr # "\362" # "\xf2" # for telfunc calls
+
+ OPT_BINARY = 0.chr # "\000" # "\x00" # Binary Transmission
+ OPT_ECHO = 1.chr # "\001" # "\x01" # Echo
+ OPT_RCP = 2.chr # "\002" # "\x02" # Reconnection
+ OPT_SGA = 3.chr # "\003" # "\x03" # Suppress Go Ahead
+ OPT_NAMS = 4.chr # "\004" # "\x04" # Approx Message Size Negotiation
+ OPT_STATUS = 5.chr # "\005" # "\x05" # Status
+ OPT_TM = 6.chr # "\006" # "\x06" # Timing Mark
+ OPT_RCTE = 7.chr # "\a" # "\x07" # Remote Controlled Trans and Echo
+ OPT_NAOL = 8.chr # "\010" # "\x08" # Output Line Width
+ OPT_NAOP = 9.chr # "\t" # "\x09" # Output Page Size
+ OPT_NAOCRD = 10.chr # "\n" # "\x0a" # Output Carriage-Return Disposition
+ OPT_NAOHTS = 11.chr # "\v" # "\x0b" # Output Horizontal Tab Stops
+ OPT_NAOHTD = 12.chr # "\f" # "\x0c" # Output Horizontal Tab Disposition
+ OPT_NAOFFD = 13.chr # "\r" # "\x0d" # Output Formfeed Disposition
+ OPT_NAOVTS = 14.chr # "\016" # "\x0e" # Output Vertical Tabstops
+ OPT_NAOVTD = 15.chr # "\017" # "\x0f" # Output Vertical Tab Disposition
+ OPT_NAOLFD = 16.chr # "\020" # "\x10" # Output Linefeed Disposition
+ OPT_XASCII = 17.chr # "\021" # "\x11" # Extended ASCII
+ OPT_LOGOUT = 18.chr # "\022" # "\x12" # Logout
+ OPT_BM = 19.chr # "\023" # "\x13" # Byte Macro
+ OPT_DET = 20.chr # "\024" # "\x14" # Data Entry Terminal
+ OPT_SUPDUP = 21.chr # "\025" # "\x15" # SUPDUP
+ OPT_SUPDUPOUTPUT = 22.chr # "\026" # "\x16" # SUPDUP Output
+ OPT_SNDLOC = 23.chr # "\027" # "\x17" # Send Location
+ OPT_TTYPE = 24.chr # "\030" # "\x18" # Terminal Type
+ OPT_EOR = 25.chr # "\031" # "\x19" # End of Record
+ OPT_TUID = 26.chr # "\032" # "\x1a" # TACACS User Identification
+ OPT_OUTMRK = 27.chr # "\e" # "\x1b" # Output Marking
+ OPT_TTYLOC = 28.chr # "\034" # "\x1c" # Terminal Location Number
+ OPT_3270REGIME = 29.chr # "\035" # "\x1d" # Telnet 3270 Regime
+ OPT_X3PAD = 30.chr # "\036" # "\x1e" # X.3 PAD
+ OPT_NAWS = 31.chr # "\037" # "\x1f" # Negotiate About Window Size
+ OPT_TSPEED = 32.chr # " " # "\x20" # Terminal Speed
+ OPT_LFLOW = 33.chr # "!" # "\x21" # Remote Flow Control
+ OPT_LINEMODE = 34.chr # "\"" # "\x22" # Linemode
+ OPT_XDISPLOC = 35.chr # "#" # "\x23" # X Display Location
+ OPT_OLD_ENVIRON = 36.chr # "$" # "\x24" # Environment Option
+ OPT_AUTHENTICATION = 37.chr # "%" # "\x25" # Authentication Option
+ OPT_ENCRYPT = 38.chr # "&" # "\x26" # Encryption Option
+ OPT_NEW_ENVIRON = 39.chr # "'" # "\x27" # New Environment Option
+ OPT_EXOPL = 255.chr # "\377" # "\xff" # Extended-Options-List
+
+ NULL = "\000"
+ CR = "\015"
+ LF = "\012"
+ EOL = CR + LF
+ REVISION = '$Id: telnet.rb,v 1.23.2.4 2005/09/14 15:21:31 matz Exp $'
+ # :startdoc:
+
+ #
+ # Creates a new Net::Telnet object.
+ #
+ # Attempts to connect to the host (unless the Proxy option is
+ # provided: see below). If a block is provided, it is yielded
+ # status messages on the attempt to connect to the server, of
+ # the form:
+ #
+ # Trying localhost...
+ # Connected to localhost.
+ #
+ # +options+ is a hash of options. The following example lists
+ # all options and their default values.
+ #
+ # host = Net::Telnet::new(
+ # "Host" => "localhost", # default: "localhost"
+ # "Port" => 23, # default: 23
+ # "Binmode" => false, # default: false
+ # "Output_log" => "output_log", # default: nil (no output)
+ # "Dump_log" => "dump_log", # default: nil (no output)
+ # "Prompt" => /[$%#>] \z/n, # default: /[$%#>] \z/n
+ # "Telnetmode" => true, # default: true
+ # "Timeout" => 10, # default: 10
+ # # if ignore timeout then set "Timeout" to false.
+ # "Waittime" => 0, # default: 0
+ # "Proxy" => proxy # default: nil
+ # # proxy is Net::Telnet or IO object
+ # )
+ #
+ # The options have the following meanings:
+ #
+ # Host:: the hostname or IP address of the host to connect to, as a String.
+ # Defaults to "localhost".
+ #
+ # Port:: the port to connect to. Defaults to 23.
+ #
+ # Binmode:: if false (the default), newline substitution is performed.
+ # Outgoing LF is
+ # converted to CRLF, and incoming CRLF is converted to LF. If
+ # true, this substitution is not performed. This value can
+ # also be set with the #binmode() method. The
+ # outgoing conversion only applies to the #puts() and #print()
+ # methods, not the #write() method. The precise nature of
+ # the newline conversion is also affected by the telnet options
+ # SGA and BIN.
+ #
+ # Output_log:: the name of the file to write connection status messages
+ # and all received traffic to. In the case of a proper
+ # Telnet session, this will include the client input as
+ # echoed by the host; otherwise, it only includes server
+ # responses. Output is appended verbatim to this file.
+ # By default, no output log is kept.
+ #
+ # Dump_log:: as for Output_log, except that output is written in hexdump
+ # format (16 bytes per line as hex pairs, followed by their
+ # printable equivalent), with connection status messages
+ # preceded by '#', sent traffic preceded by '>', and
+ # received traffic preceded by '<'. By default, not dump log
+ # is kept.
+ #
+ # Prompt:: a regular expression matching the host's command-line prompt
+ # sequence. This is needed by the Telnet class to determine
+ # when the output from a command has finished and the host is
+ # ready to receive a new command. By default, this regular
+ # expression is /[$%#>] \z/n.
+ #
+ # Telnetmode:: a boolean value, true by default. In telnet mode,
+ # traffic received from the host is parsed for special
+ # command sequences, and these sequences are escaped
+ # in outgoing traffic sent using #puts() or #print()
+ # (but not #write()). If you are using the Net::Telnet
+ # object to connect to a non-telnet service (such as
+ # SMTP or POP), this should be set to "false" to prevent
+ # undesired data corruption. This value can also be set
+ # by the #telnetmode() method.
+ #
+ # Timeout:: the number of seconds to wait before timing out both the
+ # initial attempt to connect to host (in this constructor),
+ # and all attempts to read data from the host (in #waitfor(),
+ # #cmd(), and #login()). Exceeding this timeout causes a
+ # TimeoutError to be raised. The default value is 10 seconds.
+ # You can disable the timeout by setting this value to false.
+ # In this case, the connect attempt will eventually timeout
+ # on the underlying connect(2) socket call with an
+ # Errno::ETIMEDOUT error (but generally only after a few
+ # minutes), but other attempts to read data from the host
+ # will hand indefinitely if no data is forthcoming.
+ #
+ # Waittime:: the amount of time to wait after seeing what looks like a
+ # prompt (that is, received data that matches the Prompt
+ # option regular expression) to see if more data arrives.
+ # If more data does arrive in this time, Net::Telnet assumes
+ # that what it saw was not really a prompt. This is to try to
+ # avoid false matches, but it can also lead to missing real
+ # prompts (if, for instance, a background process writes to
+ # the terminal soon after the prompt is displayed). By
+ # default, set to 0, meaning not to wait for more data.
+ #
+ # Proxy:: a proxy object to used instead of opening a direct connection
+ # to the host. Must be either another Net::Telnet object or
+ # an IO object. If it is another Net::Telnet object, this
+ # instance will use that one's socket for communication. If an
+ # IO object, it is used directly for communication. Any other
+ # kind of object will cause an error to be raised.
+ #
+ def initialize(options) # :yield: mesg
+ @options = options
+ @options["Host"] = "localhost" unless @options.has_key?("Host")
+ @options["Port"] = 23 unless @options.has_key?("Port")
+ @options["Prompt"] = /[$%#>] \z/n unless @options.has_key?("Prompt")
+ @options["Timeout"] = 10 unless @options.has_key?("Timeout")
+ @options["Waittime"] = 0 unless @options.has_key?("Waittime")
+ unless @options.has_key?("Binmode")
+ @options["Binmode"] = false
+ else
+ unless (true == @options["Binmode"] or false == @options["Binmode"])
+ raise ArgumentError, "Binmode option must be true or false"
+ end
+ end
+
+ unless @options.has_key?("Telnetmode")
+ @options["Telnetmode"] = true
+ else
+ unless (true == @options["Telnetmode"] or false == @options["Telnetmode"])
+ raise ArgumentError, "Telnetmode option must be true or false"
+ end
+ end
+
+ @telnet_option = { "SGA" => false, "BINARY" => false }
+
+ if @options.has_key?("Output_log")
+ @log = File.open(@options["Output_log"], 'a+')
+ @log.sync = true
+ @log.binmode
+ end
+
+ if @options.has_key?("Dump_log")
+ @dumplog = File.open(@options["Dump_log"], 'a+')
+ @dumplog.sync = true
+ @dumplog.binmode
+ def @dumplog.log_dump(dir, x) # :nodoc:
+ len = x.length
+ addr = 0
+ offset = 0
+ while 0 < len
+ if len < 16
+ line = x[offset, len]
+ else
+ line = x[offset, 16]
+ end
+ hexvals = line.unpack('H*')[0]
+ hexvals += ' ' * (32 - hexvals.length)
+ hexvals = format("%s %s %s %s " * 4, *hexvals.unpack('a2' * 16))
+ line = line.gsub(/[\000-\037\177-\377]/n, '.')
+ printf "%s 0x%5.5x: %s%s\n", dir, addr, hexvals, line
+ addr += 16
+ offset += 16
+ len -= 16
+ end
+ print "\n"
+ end
+ end
+
+ if @options.has_key?("Proxy")
+ if @options["Proxy"].kind_of?(Net::Telnet)
+ @sock = @options["Proxy"].sock
+ elsif @options["Proxy"].kind_of?(IO)
+ @sock = @options["Proxy"]
+ else
+ raise "Error: Proxy must be an instance of Net::Telnet or IO."
+ end
+ else
+ message = "Trying " + @options["Host"] + "...\n"
+ yield(message) if block_given?
+ @log.write(message) if @options.has_key?("Output_log")
+ @dumplog.log_dump('#', message) if @options.has_key?("Dump_log")
+
+ begin
+ if @options["Timeout"] == false
+ @sock = TCPSocket.open(@options["Host"], @options["Port"])
+ else
+ timeout(@options["Timeout"]) do
+ @sock = TCPSocket.open(@options["Host"], @options["Port"])
+ end
+ end
+ rescue TimeoutError
+ raise TimeoutError, "timed out while opening a connection to the host"
+ rescue
+ @log.write($ERROR_INFO.to_s + "\n") if @options.has_key?("Output_log")
+ @dumplog.log_dump('#', $ERROR_INFO.to_s + "\n") if @options.has_key?("Dump_log")
+ raise
+ end
+ @sock.sync = true
+ @sock.binmode
+
+ message = "Connected to " + @options["Host"] + ".\n"
+ yield(message) if block_given?
+ @log.write(message) if @options.has_key?("Output_log")
+ @dumplog.log_dump('#', message) if @options.has_key?("Dump_log")
+ end
+
+ super(@sock)
+ end # initialize
+
+ # The socket the Telnet object is using. Note that this object becomes
+ # a delegate of the Telnet object, so normally you invoke its methods
+ # directly on the Telnet object.
+ attr :sock
+
+ # Set telnet command interpretation on (+mode+ == true) or off
+ # (+mode+ == false), or return the current value (+mode+ not
+ # provided). It should be on for true telnet sessions, off if
+ # using Net::Telnet to connect to a non-telnet service such
+ # as SMTP.
+ def telnetmode(mode = nil)
+ case mode
+ when nil
+ @options["Telnetmode"]
+ when true, false
+ @options["Telnetmode"] = mode
+ else
+ raise ArgumentError, "argument must be true or false, or missing"
+ end
+ end
+
+ # Turn telnet command interpretation on (true) or off (false). It
+ # should be on for true telnet sessions, off if using Net::Telnet
+ # to connect to a non-telnet service such as SMTP.
+ def telnetmode=(mode)
+ if (true == mode or false == mode)
+ @options["Telnetmode"] = mode
+ else
+ raise ArgumentError, "argument must be true or false"
+ end
+ end
+
+ # Turn newline conversion on (+mode+ == false) or off (+mode+ == true),
+ # or return the current value (+mode+ is not specified).
+ def binmode(mode = nil)
+ case mode
+ when nil
+ @options["Binmode"]
+ when true, false
+ @options["Binmode"] = mode
+ else
+ raise ArgumentError, "argument must be true or false"
+ end
+ end
+
+ # Turn newline conversion on (false) or off (true).
+ def binmode=(mode)
+ if (true == mode or false == mode)
+ @options["Binmode"] = mode
+ else
+ raise ArgumentError, "argument must be true or false"
+ end
+ end
+
+ # Preprocess received data from the host.
+ #
+ # Performs newline conversion and detects telnet command sequences.
+ # Called automatically by #waitfor(). You should only use this
+ # method yourself if you have read input directly using sysread()
+ # or similar, and even then only if in telnet mode.
+ def preprocess(string)
+ # combine CR+NULL into CR
+ string = string.gsub(/#{CR}#{NULL}/no, CR) if @options["Telnetmode"]
+
+ # combine EOL into "\n"
+ string = string.gsub(/#{EOL}/no, "\n") unless @options["Binmode"]
+
+ string.gsub(/#{IAC}(
+ [#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]|
+ [#{DO}#{DONT}#{WILL}#{WONT}]
+ [#{OPT_BINARY}-#{OPT_NEW_ENVIRON}#{OPT_EXOPL}]|
+ #{SB}[^#{IAC}]*#{IAC}#{SE}
+ )/xno) do
+ if IAC == $1 # handle escaped IAC characters
+ IAC
+ elsif AYT == $1 # respond to "IAC AYT" (are you there)
+ self.write("nobody here but us pigeons" + EOL)
+ ''
+ elsif DO[0] == $1[0] # respond to "IAC DO x"
+ if OPT_BINARY[0] == $1[1]
+ @telnet_option["BINARY"] = true
+ self.write(IAC + WILL + OPT_BINARY)
+ else
+ self.write(IAC + WONT + $1[1..1])
+ end
+ ''
+ elsif DONT[0] == $1[0] # respond to "IAC DON'T x" with "IAC WON'T x"
+ self.write(IAC + WONT + $1[1..1])
+ ''
+ elsif WILL[0] == $1[0] # respond to "IAC WILL x"
+ if OPT_BINARY[0] == $1[1]
+ self.write(IAC + DO + OPT_BINARY)
+ elsif OPT_ECHO[0] == $1[1]
+ self.write(IAC + DO + OPT_ECHO)
+ elsif OPT_SGA[0] == $1[1]
+ @telnet_option["SGA"] = true
+ self.write(IAC + DO + OPT_SGA)
+ else
+ self.write(IAC + DONT + $1[1..1])
+ end
+ ''
+ elsif WONT[0] == $1[0] # respond to "IAC WON'T x"
+ if OPT_ECHO[0] == $1[1]
+ self.write(IAC + DONT + OPT_ECHO)
+ elsif OPT_SGA[0] == $1[1]
+ @telnet_option["SGA"] = false
+ self.write(IAC + DONT + OPT_SGA)
+ else
+ self.write(IAC + DONT + $1[1..1])
+ end
+ ''
+ else
+ ''
+ end
+ end
+ end # preprocess
+
+ # Read data from the host until a certain sequence is matched.
+ #
+ # If a block is given, the received data will be yielded as it
+ # is read in (not necessarily all in one go), or nil if EOF
+ # occurs before any data is received. Whether a block is given
+ # or not, all data read will be returned in a single string, or again
+ # nil if EOF occurs before any data is received. Note that
+ # received data includes the matched sequence we were looking for.
+ #
+ # +options+ can be either a regular expression or a hash of options.
+ # If a regular expression, this specifies the data to wait for.
+ # If a hash, this can specify the following options:
+ #
+ # Match:: a regular expression, specifying the data to wait for.
+ # Prompt:: as for Match; used only if Match is not specified.
+ # String:: as for Match, except a string that will be converted
+ # into a regular expression. Used only if Match and
+ # Prompt are not specified.
+ # Timeout:: the number of seconds to wait for data from the host
+ # before raising a TimeoutError. If set to false,
+ # no timeout will occur. If not specified, the
+ # Timeout option value specified when this instance
+ # was created will be used, or, failing that, the
+ # default value of 10 seconds.
+ # Waittime:: the number of seconds to wait after matching against
+ # the input data to see if more data arrives. If more
+ # data arrives within this time, we will judge ourselves
+ # not to have matched successfully, and will continue
+ # trying to match. If not specified, the Waittime option
+ # value specified when this instance was created will be
+ # used, or, failing that, the default value of 0 seconds,
+ # which means not to wait for more input.
+ #
+ def waitfor(options) # :yield: recvdata
+ time_out = @options["Timeout"]
+ waittime = @options["Waittime"]
+
+ if options.kind_of?(Hash)
+ prompt = if options.has_key?("Match")
+ options["Match"]
+ elsif options.has_key?("Prompt")
+ options["Prompt"]
+ elsif options.has_key?("String")
+ Regexp.new( Regexp.quote(options["String"]) )
+ end
+ time_out = options["Timeout"] if options.has_key?("Timeout")
+ waittime = options["Waittime"] if options.has_key?("Waittime")
+ else
+ prompt = options
+ end
+
+ if time_out == false
+ time_out = nil
+ end
+
+ line = ''
+ buf = ''
+ rest = ''
+ until(prompt === line and not IO::select([@sock], nil, nil, waittime))
+ unless IO::select([@sock], nil, nil, time_out)
+ raise TimeoutError, "timed out while waiting for more data"
+ end
+ begin
+ c = @sock.readpartial(1024 * 1024)
+ @dumplog.log_dump('<', c) if @options.has_key?("Dump_log")
+ if @options["Telnetmode"]
+ c = rest + c
+ if Integer(c.rindex(/#{IAC}#{SE}/no)) <
+ Integer(c.rindex(/#{IAC}#{SB}/no))
+ buf = preprocess(c[0 ... c.rindex(/#{IAC}#{SB}/no)])
+ rest = c[c.rindex(/#{IAC}#{SB}/no) .. -1]
+ elsif pt = c.rindex(/#{IAC}[^#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]?\z/no)
+ buf = preprocess(c[0 ... pt])
+ rest = c[pt .. -1]
+ else
+ buf = preprocess(c)
+ rest = ''
+ end
+ else
+ # Not Telnetmode.
+ #
+ # We cannot use preprocess() on this data, because that
+ # method makes some Telnetmode-specific assumptions.
+ buf = c
+ buf.gsub!(/#{EOL}/no, "\n") unless @options["Binmode"]
+ rest = ''
+ end
+ @log.print(buf) if @options.has_key?("Output_log")
+ line += buf
+ yield buf if block_given?
+ rescue EOFError # End of file reached
+ if line == ''
+ line = nil
+ yield nil if block_given?
+ end
+ break
+ end
+ end
+ line
+ end
+
+ # Write +string+ to the host.
+ #
+ # Does not perform any conversions on +string+. Will log +string+ to the
+ # dumplog, if the Dump_log option is set.
+ def write(string)
+ length = string.length
+ while 0 < length
+ IO::select(nil, [@sock])
+ @dumplog.log_dump('>', string[-length..-1]) if @options.has_key?("Dump_log")
+ length -= @sock.syswrite(string[-length..-1])
+ end
+ end
+
+ # Sends a string to the host.
+ #
+ # This does _not_ automatically append a newline to the string. Embedded
+ # newlines may be converted and telnet command sequences escaped
+ # depending upon the values of telnetmode, binmode, and telnet options
+ # set by the host.
+ def print(string)
+ string = string.gsub(/#{IAC}/no, IAC + IAC) if @options["Telnetmode"]
+
+ if @options["Binmode"]
+ self.write(string)
+ else
+ if @telnet_option["BINARY"] and @telnet_option["SGA"]
+ # IAC WILL SGA IAC DO BIN send EOL --> CR
+ self.write(string.gsub(/\n/n, CR))
+ elsif @telnet_option["SGA"]
+ # IAC WILL SGA send EOL --> CR+NULL
+ self.write(string.gsub(/\n/n, CR + NULL))
+ else
+ # NONE send EOL --> CR+LF
+ self.write(string.gsub(/\n/n, EOL))
+ end
+ end
+ end
+
+ # Sends a string to the host.
+ #
+ # Same as #print(), but appends a newline to the string.
+ def puts(string)
+ self.print(string + "\n")
+ end
+
+ # Send a command to the host.
+ #
+ # More exactly, sends a string to the host, and reads in all received
+ # data until is sees the prompt or other matched sequence.
+ #
+ # If a block is given, the received data will be yielded to it as
+ # it is read in. Whether a block is given or not, the received data
+ # will be return as a string. Note that the received data includes
+ # the prompt and in most cases the host's echo of our command.
+ #
+ # +options+ is either a String, specified the string or command to
+ # send to the host; or it is a hash of options. If a hash, the
+ # following options can be specified:
+ #
+ # String:: the command or other string to send to the host.
+ # Match:: a regular expression, the sequence to look for in
+ # the received data before returning. If not specified,
+ # the Prompt option value specified when this instance
+ # was created will be used, or, failing that, the default
+ # prompt of /[$%#>] \z/n.
+ # Timeout:: the seconds to wait for data from the host before raising
+ # a Timeout error. If not specified, the Timeout option
+ # value specified when this instance was created will be
+ # used, or, failing that, the default value of 10 seconds.
+ #
+ # The command or other string will have the newline sequence appended
+ # to it.
+ def cmd(options) # :yield: recvdata
+ match = @options["Prompt"]
+ time_out = @options["Timeout"]
+
+ if options.kind_of?(Hash)
+ string = options["String"]
+ match = options["Match"] if options.has_key?("Match")
+ time_out = options["Timeout"] if options.has_key?("Timeout")
+ else
+ string = options
+ end
+
+ self.puts(string)
+ if block_given?
+ waitfor({"Prompt" => match, "Timeout" => time_out}){|c| yield c }
+ else
+ waitfor({"Prompt" => match, "Timeout" => time_out})
+ end
+ end
+
+ # Login to the host with a given username and password.
+ #
+ # The username and password can either be provided as two string
+ # arguments in that order, or as a hash with keys "Name" and
+ # "Password".
+ #
+ # This method looks for the strings "login" and "Password" from the
+ # host to determine when to send the username and password. If the
+ # login sequence does not follow this pattern (for instance, you
+ # are connecting to a service other than telnet), you will need
+ # to handle login yourself.
+ #
+ # The password can be omitted, either by only
+ # provided one String argument, which will be used as the username,
+ # or by providing a has that has no "Password" key. In this case,
+ # the method will not look for the "Password:" prompt; if it is
+ # sent, it will have to be dealt with by later calls.
+ #
+ # The method returns all data received during the login process from
+ # the host, including the echoed username but not the password (which
+ # the host should not echo). If a block is passed in, this received
+ # data is also yielded to the block as it is received.
+ def login(options, password = nil) # :yield: recvdata
+ login_prompt = /[Ll]ogin[: ]*\z/n
+ password_prompt = /Password[: ]*\z/n
+ if options.kind_of?(Hash)
+ username = options["Name"]
+ password = options["Password"]
+ login_prompt = options["LoginPrompt"] if options["LoginPrompt"]
+ password_prompt = options["PasswordPrompt"] if options["PasswordPrompt"]
+ else
+ username = options
+ end
+
+ if block_given?
+ line = waitfor(login_prompt){|c| yield c }
+ if password
+ line += cmd({"String" => username,
+ "Match" => password_prompt}){|c| yield c }
+ line += cmd(password){|c| yield c }
+ else
+ line += cmd(username){|c| yield c }
+ end
+ else
+ line = waitfor(login_prompt)
+ if password
+ line += cmd({"String" => username,
+ "Match" => password_prompt})
+ line += cmd(password)
+ else
+ line += cmd(username)
+ end
+ end
+ line
+ end
+
+ end # class Telnet
+end # module Net
+
diff --git a/ruby_1_8_5/lib/observer.rb b/ruby_1_8_5/lib/observer.rb
new file mode 100644
index 0000000000..64c7d81351
--- /dev/null
+++ b/ruby_1_8_5/lib/observer.rb
@@ -0,0 +1,192 @@
+#
+# observer.rb implements the _Observer_ object-oriented design pattern. The
+# following documentation is copied, with modifications, from "Programming
+# Ruby", by Hunt and Thomas; http://www.rubycentral.com/book/lib_patterns.html.
+#
+# == About
+#
+# The Observer pattern, also known as Publish/Subscribe, provides a simple
+# mechanism for one object to inform a set of interested third-party objects
+# when its state changes.
+#
+# == Mechanism
+#
+# In the Ruby implementation, the notifying class mixes in the +Observable+
+# module, which provides the methods for managing the associated observer
+# objects.
+#
+# The observers must implement the +update+ method to receive notifications.
+#
+# The observable object must:
+# * assert that it has +changed+
+# * call +notify_observers+
+#
+# == Example
+#
+# The following example demonstrates this nicely. A +Ticker+, when run,
+# continually receives the stock +Price+ for its +@symbol+. A +Warner+ is a
+# general observer of the price, and two warners are demonstrated, a +WarnLow+
+# and a +WarnHigh+, which print a warning if the price is below or above their
+# set limits, respectively.
+#
+# The +update+ callback allows the warners to run without being explicitly
+# called. The system is set up with the +Ticker+ and several observers, and the
+# observers do their duty without the top-level code having to interfere.
+#
+# Note that the contract between publisher and subscriber (observable and
+# observer) is not declared or enforced. The +Ticker+ publishes a time and a
+# price, and the warners receive that. But if you don't ensure that your
+# contracts are correct, nothing else can warn you.
+#
+# require "observer"
+#
+# class Ticker ### Periodically fetch a stock price.
+# include Observable
+#
+# def initialize(symbol)
+# @symbol = symbol
+# end
+#
+# def run
+# lastPrice = nil
+# loop do
+# price = Price.fetch(@symbol)
+# print "Current price: #{price}\n"
+# if price != lastPrice
+# changed # notify observers
+# lastPrice = price
+# notify_observers(Time.now, price)
+# end
+# sleep 1
+# end
+# end
+# end
+#
+# class Price ### A mock class to fetch a stock price (60 - 140).
+# def Price.fetch(symbol)
+# 60 + rand(80)
+# end
+# end
+#
+# class Warner ### An abstract observer of Ticker objects.
+# def initialize(ticker, limit)
+# @limit = limit
+# ticker.add_observer(self)
+# end
+# end
+#
+# class WarnLow < Warner
+# def update(time, price) # callback for observer
+# if price < @limit
+# print "--- #{time.to_s}: Price below #@limit: #{price}\n"
+# end
+# end
+# end
+#
+# class WarnHigh < Warner
+# def update(time, price) # callback for observer
+# if price > @limit
+# print "+++ #{time.to_s}: Price above #@limit: #{price}\n"
+# end
+# end
+# end
+#
+# ticker = Ticker.new("MSFT")
+# WarnLow.new(ticker, 80)
+# WarnHigh.new(ticker, 120)
+# ticker.run
+#
+# Produces:
+#
+# Current price: 83
+# Current price: 75
+# --- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 75
+# Current price: 90
+# Current price: 134
+# +++ Sun Jun 09 00:10:25 CDT 2002: Price above 120: 134
+# Current price: 134
+# Current price: 112
+# Current price: 79
+# --- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 79
+
+
+#
+# Implements the Observable design pattern as a mixin so that other objects can
+# be notified of changes in state. See observer.rb for details and an example.
+#
+module Observable
+
+ #
+ # Add +observer+ as an observer on this object. +observer+ will now receive
+ # notifications.
+ #
+ def add_observer(observer)
+ @observer_peers = [] unless defined? @observer_peers
+ unless observer.respond_to? :update
+ raise NoMethodError, "observer needs to respond to `update'"
+ end
+ @observer_peers.push observer
+ end
+
+ #
+ # Delete +observer+ as an observer on this object. It will no longer receive
+ # notifications.
+ #
+ def delete_observer(observer)
+ @observer_peers.delete observer if defined? @observer_peers
+ end
+
+ #
+ # Delete all observers associated with this object.
+ #
+ def delete_observers
+ @observer_peers.clear if defined? @observer_peers
+ end
+
+ #
+ # Return the number of observers associated with this object.
+ #
+ def count_observers
+ if defined? @observer_peers
+ @observer_peers.size
+ else
+ 0
+ end
+ end
+
+ #
+ # Set the changed state of this object. Notifications will be sent only if
+ # the changed +state+ is +true+.
+ #
+ def changed(state=true)
+ @observer_state = state
+ end
+
+ #
+ # Query the changed state of this object.
+ #
+ def changed?
+ if defined? @observer_state and @observer_state
+ true
+ else
+ false
+ end
+ end
+
+ #
+ # If this object's changed state is +true+, invoke the update method in each
+ # currently associated observer in turn, passing it the given arguments. The
+ # changed state is then set to +false+.
+ #
+ def notify_observers(*arg)
+ if defined? @observer_state and @observer_state
+ if defined? @observer_peers
+ for i in @observer_peers.dup
+ i.update(*arg)
+ end
+ end
+ @observer_state = false
+ end
+ end
+
+end
diff --git a/ruby_1_8_5/lib/open-uri.rb b/ruby_1_8_5/lib/open-uri.rb
new file mode 100644
index 0000000000..d69f7dbe41
--- /dev/null
+++ b/ruby_1_8_5/lib/open-uri.rb
@@ -0,0 +1,688 @@
+require 'uri'
+require 'stringio'
+require 'time'
+
+module Kernel
+ private
+ alias open_uri_original_open open # :nodoc:
+
+ # makes possible to open various resources including URIs.
+ # If the first argument respond to `open' method,
+ # the method is called with the rest arguments.
+ #
+ # If the first argument is a string which begins with xxx://,
+ # it is parsed by URI.parse. If the parsed object respond to `open' method,
+ # the method is called with the rest arguments.
+ #
+ # Otherwise original open is called.
+ #
+ # Since open-uri.rb provides URI::HTTP#open, URI::HTTPS#open and
+ # URI::FTP#open,
+ # Kernel[#.]open can accepts such URIs and strings which begins with
+ # http://, https:// and ftp://.
+ # In these case, the opened file object is extended by OpenURI::Meta.
+ def open(name, *rest, &block) # :doc:
+ if name.respond_to?(:open)
+ name.open(*rest, &block)
+ elsif name.respond_to?(:to_str) &&
+ %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ name &&
+ (uri = URI.parse(name)).respond_to?(:open)
+ uri.open(*rest, &block)
+ else
+ open_uri_original_open(name, *rest, &block)
+ end
+ end
+ module_function :open
+end
+
+# OpenURI is an easy-to-use wrapper for net/http, net/https and net/ftp.
+#
+#== Example
+#
+# It is possible to open http/https/ftp URL as usual like opening a file:
+#
+# open("http://www.ruby-lang.org/") {|f|
+# f.each_line {|line| p line}
+# }
+#
+# The opened file has several methods for meta information as follows since
+# it is extended by OpenURI::Meta.
+#
+# open("http://www.ruby-lang.org/en") {|f|
+# f.each_line {|line| p line}
+# p f.base_uri # <URI::HTTP:0x40e6ef2 URL:http://www.ruby-lang.org/en/>
+# p f.content_type # "text/html"
+# p f.charset # "iso-8859-1"
+# p f.content_encoding # []
+# p f.last_modified # Thu Dec 05 02:45:02 UTC 2002
+# }
+#
+# Additional header fields can be specified by an optional hash argument.
+#
+# open("http://www.ruby-lang.org/en/",
+# "User-Agent" => "Ruby/#{RUBY_VERSION}",
+# "From" => "foo@bar.invalid",
+# "Referer" => "http://www.ruby-lang.org/") {|f|
+# # ...
+# }
+#
+# The environment variables such as http_proxy, https_proxy and ftp_proxy
+# are in effect by default. :proxy => nil disables proxy.
+#
+# open("http://www.ruby-lang.org/en/raa.html", :proxy => nil) {|f|
+# # ...
+# }
+#
+# URI objects can be opened in a similar way.
+#
+# uri = URI.parse("http://www.ruby-lang.org/en/")
+# uri.open {|f|
+# # ...
+# }
+#
+# URI objects can be read directly. The returned string is also extended by
+# OpenURI::Meta.
+#
+# str = uri.read
+# p str.base_uri
+#
+# Author:: Tanaka Akira <akr@m17n.org>
+
+module OpenURI
+ Options = {
+ :proxy => true,
+ :progress_proc => true,
+ :content_length_proc => true,
+ :http_basic_authentication => true,
+ }
+
+ def OpenURI.check_options(options) # :nodoc:
+ options.each {|k, v|
+ next unless Symbol === k
+ unless Options.include? k
+ raise ArgumentError, "unrecognized option: #{k}"
+ end
+ }
+ end
+
+ def OpenURI.scan_open_optional_arguments(*rest) # :nodoc:
+ if !rest.empty? && (String === rest.first || Integer === rest.first)
+ mode = rest.shift
+ if !rest.empty? && Integer === rest.first
+ perm = rest.shift
+ end
+ end
+ return mode, perm, rest
+ end
+
+ def OpenURI.open_uri(name, *rest) # :nodoc:
+ uri = URI::Generic === name ? name : URI.parse(name)
+ mode, perm, rest = OpenURI.scan_open_optional_arguments(*rest)
+ options = rest.shift if !rest.empty? && Hash === rest.first
+ raise ArgumentError.new("extra arguments") if !rest.empty?
+ options ||= {}
+ OpenURI.check_options(options)
+
+ unless mode == nil ||
+ mode == 'r' || mode == 'rb' ||
+ mode == File::RDONLY
+ raise ArgumentError.new("invalid access mode #{mode} (#{uri.class} resource is read only.)")
+ end
+
+ io = open_loop(uri, options)
+ if block_given?
+ begin
+ yield io
+ ensure
+ io.close
+ end
+ else
+ io
+ end
+ end
+
+ def OpenURI.open_loop(uri, options) # :nodoc:
+ case opt_proxy = options.fetch(:proxy, true)
+ when true
+ find_proxy = lambda {|u| u.find_proxy}
+ when nil, false
+ find_proxy = lambda {|u| nil}
+ when String
+ opt_proxy = URI.parse(opt_proxy)
+ find_proxy = lambda {|u| opt_proxy}
+ when URI::Generic
+ find_proxy = lambda {|u| opt_proxy}
+ else
+ raise ArgumentError.new("Invalid proxy option: #{opt_proxy}")
+ end
+
+ uri_set = {}
+ buf = nil
+ while true
+ redirect = catch(:open_uri_redirect) {
+ buf = Buffer.new
+ uri.buffer_open(buf, find_proxy.call(uri), options)
+ nil
+ }
+ if redirect
+ if redirect.relative?
+ # Although it violates RFC2616, Location: field may have relative
+ # URI. It is converted to absolute URI using uri as a base URI.
+ redirect = uri + redirect
+ end
+ unless OpenURI.redirectable?(uri, redirect)
+ raise "redirection forbidden: #{uri} -> #{redirect}"
+ end
+ if options.include? :http_basic_authentication
+ # send authentication only for the URI directly specified.
+ options = options.dup
+ options.delete :http_basic_authentication
+ end
+ uri = redirect
+ raise "HTTP redirection loop: #{uri}" if uri_set.include? uri.to_s
+ uri_set[uri.to_s] = true
+ else
+ break
+ end
+ end
+ io = buf.io
+ io.base_uri = uri
+ io
+ end
+
+ def OpenURI.redirectable?(uri1, uri2) # :nodoc:
+ # This test is intended to forbid a redirection from http://... to
+ # file:///etc/passwd.
+ # However this is ad hoc. It should be extensible/configurable.
+ uri1.scheme.downcase == uri2.scheme.downcase ||
+ (/\A(?:http|ftp)\z/i =~ uri1.scheme && /\A(?:http|ftp)\z/i =~ uri2.scheme)
+ end
+
+ def OpenURI.open_http(buf, target, proxy, options) # :nodoc:
+ if proxy
+ raise "Non-HTTP proxy URI: #{proxy}" if proxy.class != URI::HTTP
+ end
+
+ if target.userinfo && "1.9.0" <= RUBY_VERSION
+ # don't raise for 1.8 because compatibility.
+ raise ArgumentError, "userinfo not supported. [RFC3986]"
+ end
+
+ require 'net/http'
+ klass = Net::HTTP
+ if URI::HTTP === target
+ # HTTP or HTTPS
+ if proxy
+ klass = Net::HTTP::Proxy(proxy.host, proxy.port)
+ end
+ target_host = target.host
+ target_port = target.port
+ request_uri = target.request_uri
+ else
+ # FTP over HTTP proxy
+ target_host = proxy.host
+ target_port = proxy.port
+ request_uri = target.to_s
+ end
+
+ http = klass.new(target_host, target_port)
+ if target.class == URI::HTTPS
+ require 'net/https'
+ http.use_ssl = true
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ store = OpenSSL::X509::Store.new
+ store.set_default_paths
+ http.cert_store = store
+ end
+
+ header = {}
+ options.each {|k, v| header[k] = v if String === k }
+
+ resp = nil
+ http.start {
+ if target.class == URI::HTTPS
+ # xxx: information hiding violation
+ sock = http.instance_variable_get(:@socket)
+ if sock.respond_to?(:io)
+ sock = sock.io # 1.9
+ else
+ sock = sock.instance_variable_get(:@socket) # 1.8
+ end
+ sock.post_connection_check(target_host)
+ end
+ req = Net::HTTP::Get.new(request_uri, header)
+ if options.include? :http_basic_authentication
+ user, pass = options[:http_basic_authentication]
+ req.basic_auth user, pass
+ end
+ http.request(req) {|response|
+ resp = response
+ if options[:content_length_proc] && Net::HTTPSuccess === resp
+ if resp.key?('Content-Length')
+ options[:content_length_proc].call(resp['Content-Length'].to_i)
+ else
+ options[:content_length_proc].call(nil)
+ end
+ end
+ resp.read_body {|str|
+ buf << str
+ if options[:progress_proc] && Net::HTTPSuccess === resp
+ options[:progress_proc].call(buf.size)
+ end
+ }
+ }
+ }
+ io = buf.io
+ io.rewind
+ io.status = [resp.code, resp.message]
+ resp.each {|name,value| buf.io.meta_add_field name, value }
+ case resp
+ when Net::HTTPSuccess
+ when Net::HTTPMovedPermanently, # 301
+ Net::HTTPFound, # 302
+ Net::HTTPSeeOther, # 303
+ Net::HTTPTemporaryRedirect # 307
+ throw :open_uri_redirect, URI.parse(resp['location'])
+ else
+ raise OpenURI::HTTPError.new(io.status.join(' '), io)
+ end
+ end
+
+ class HTTPError < StandardError
+ def initialize(message, io)
+ super(message)
+ @io = io
+ end
+ attr_reader :io
+ end
+
+ class Buffer # :nodoc:
+ def initialize
+ @io = StringIO.new
+ @size = 0
+ end
+ attr_reader :size
+
+ StringMax = 10240
+ def <<(str)
+ @io << str
+ @size += str.length
+ if StringIO === @io && StringMax < @size
+ require 'tempfile'
+ io = Tempfile.new('open-uri')
+ io.binmode
+ Meta.init io, @io if @io.respond_to? :meta
+ io << @io.string
+ @io = io
+ end
+ end
+
+ def io
+ Meta.init @io unless @io.respond_to? :meta
+ @io
+ end
+ end
+
+ # Mixin for holding meta-information.
+ module Meta
+ def Meta.init(obj, src=nil) # :nodoc:
+ obj.extend Meta
+ obj.instance_eval {
+ @base_uri = nil
+ @meta = {}
+ }
+ if src
+ obj.status = src.status
+ obj.base_uri = src.base_uri
+ src.meta.each {|name, value|
+ obj.meta_add_field(name, value)
+ }
+ end
+ end
+
+ # returns an Array which consists status code and message.
+ attr_accessor :status
+
+ # returns a URI which is base of relative URIs in the data.
+ # It may differ from the URI supplied by a user because redirection.
+ attr_accessor :base_uri
+
+ # returns a Hash which represents header fields.
+ # The Hash keys are downcased for canonicalization.
+ attr_reader :meta
+
+ def meta_add_field(name, value) # :nodoc:
+ @meta[name.downcase] = value
+ end
+
+ # returns a Time which represents Last-Modified field.
+ def last_modified
+ if v = @meta['last-modified']
+ Time.httpdate(v)
+ else
+ nil
+ end
+ end
+
+ RE_LWS = /[\r\n\t ]+/n
+ RE_TOKEN = %r{[^\x00- ()<>@,;:\\"/\[\]?={}\x7f]+}n
+ RE_QUOTED_STRING = %r{"(?:[\r\n\t !#-\[\]-~\x80-\xff]|\\[\x00-\x7f])*"}n
+ RE_PARAMETERS = %r{(?:;#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?=#{RE_LWS}?(?:#{RE_TOKEN}|#{RE_QUOTED_STRING})#{RE_LWS}?)*}n
+
+ def content_type_parse # :nodoc:
+ v = @meta['content-type']
+ # The last (?:;#{RE_LWS}?)? matches extra ";" which violates RFC2045.
+ if v && %r{\A#{RE_LWS}?(#{RE_TOKEN})#{RE_LWS}?/(#{RE_TOKEN})#{RE_LWS}?(#{RE_PARAMETERS})(?:;#{RE_LWS}?)?\z}no =~ v
+ type = $1.downcase
+ subtype = $2.downcase
+ parameters = []
+ $3.scan(/;#{RE_LWS}?(#{RE_TOKEN})#{RE_LWS}?=#{RE_LWS}?(?:(#{RE_TOKEN})|(#{RE_QUOTED_STRING}))/no) {|att, val, qval|
+ val = qval.gsub(/[\r\n\t !#-\[\]-~\x80-\xff]+|(\\[\x00-\x7f])/) { $1 ? $1[1,1] : $& } if qval
+ parameters << [att.downcase, val]
+ }
+ ["#{type}/#{subtype}", *parameters]
+ else
+ nil
+ end
+ end
+
+ # returns "type/subtype" which is MIME Content-Type.
+ # It is downcased for canonicalization.
+ # Content-Type parameters are stripped.
+ def content_type
+ type, *parameters = content_type_parse
+ type || 'application/octet-stream'
+ end
+
+ # returns a charset parameter in Content-Type field.
+ # It is downcased for canonicalization.
+ #
+ # If charset parameter is not given but a block is given,
+ # the block is called and its result is returned.
+ # It can be used to guess charset.
+ #
+ # If charset parameter and block is not given,
+ # nil is returned except text type in HTTP.
+ # In that case, "iso-8859-1" is returned as defined by RFC2616 3.7.1.
+ def charset
+ type, *parameters = content_type_parse
+ if pair = parameters.assoc('charset')
+ pair.last.downcase
+ elsif block_given?
+ yield
+ elsif type && %r{\Atext/} =~ type &&
+ @base_uri && /\Ahttp\z/i =~ @base_uri.scheme
+ "iso-8859-1" # RFC2616 3.7.1
+ else
+ nil
+ end
+ end
+
+ # returns a list of encodings in Content-Encoding field
+ # as an Array of String.
+ # The encodings are downcased for canonicalization.
+ def content_encoding
+ v = @meta['content-encoding']
+ if v && %r{\A#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?(?:,#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?)*}o =~ v
+ v.scan(RE_TOKEN).map {|content_coding| content_coding.downcase}
+ else
+ []
+ end
+ end
+ end
+
+ # Mixin for HTTP and FTP URIs.
+ module OpenRead
+ # OpenURI::OpenRead#open provides `open' for URI::HTTP and URI::FTP.
+ #
+ # OpenURI::OpenRead#open takes optional 3 arguments as:
+ # OpenURI::OpenRead#open([mode [, perm]] [, options]) [{|io| ... }]
+ #
+ # `mode', `perm' is same as Kernel#open.
+ #
+ # However, `mode' must be read mode because OpenURI::OpenRead#open doesn't
+ # support write mode (yet).
+ # Also `perm' is just ignored because it is meaningful only for file
+ # creation.
+ #
+ # `options' must be a hash.
+ #
+ # Each pairs which key is a string in the hash specify a extra header
+ # field for HTTP.
+ # I.e. it is ignored for FTP without HTTP proxy.
+ #
+ # The hash may include other options which key is a symbol:
+ #
+ # [:proxy]
+ # Synopsis:
+ # :proxy => "http://proxy.foo.com:8000/"
+ # :proxy => URI.parse("http://proxy.foo.com:8000/")
+ # :proxy => true
+ # :proxy => false
+ # :proxy => nil
+ #
+ # If :proxy option is specified, the value should be String, URI,
+ # boolean or nil.
+ # When String or URI is given, it is treated as proxy URI.
+ # When true is given or the option itself is not specified,
+ # environment variable `scheme_proxy' is examined.
+ # `scheme' is replaced by `http', `https' or `ftp'.
+ # When false or nil is given, the environment variables are ignored and
+ # connection will be made to a server directly.
+ #
+ # [:http_basic_authentication]
+ # Synopsis:
+ # :http_basic_authentication=>[user, password]
+ #
+ # If :http_basic_authentication is specified,
+ # the value should be an array which contains 2 strings:
+ # username and password.
+ # It is used for HTTP Basic authentication defined by RFC 2617.
+ #
+ # [:content_length_proc]
+ # Synopsis:
+ # :content_length_proc => lambda {|content_length| ... }
+ #
+ # If :content_length_proc option is specified, the option value procedure
+ # is called before actual transfer is started.
+ # It takes one argument which is expected content length in bytes.
+ #
+ # If two or more transfer is done by HTTP redirection, the procedure
+ # is called only one for a last transfer.
+ #
+ # When expected content length is unknown, the procedure is called with
+ # nil.
+ # It is happen when HTTP response has no Content-Length header.
+ #
+ # [:progress_proc]
+ # Synopsis:
+ # :progress_proc => lambda {|size| ...}
+ #
+ # If :progress_proc option is specified, the proc is called with one
+ # argument each time when `open' gets content fragment from network.
+ # The argument `size' `size' is a accumulated transfered size in bytes.
+ #
+ # If two or more transfer is done by HTTP redirection, the procedure
+ # is called only one for a last transfer.
+ #
+ # :progress_proc and :content_length_proc are intended to be used for
+ # progress bar.
+ # For example, it can be implemented as follows using Ruby/ProgressBar.
+ #
+ # pbar = nil
+ # open("http://...",
+ # :content_length_proc => lambda {|t|
+ # if t && 0 < t
+ # pbar = ProgressBar.new("...", t)
+ # pbar.file_transfer_mode
+ # end
+ # },
+ # :progress_proc => lambda {|s|
+ # pbar.set s if pbar
+ # }) {|f| ... }
+ #
+ # OpenURI::OpenRead#open returns an IO like object if block is not given.
+ # Otherwise it yields the IO object and return the value of the block.
+ # The IO object is extended with OpenURI::Meta.
+ def open(*rest, &block)
+ OpenURI.open_uri(self, *rest, &block)
+ end
+
+ # OpenURI::OpenRead#read([options]) reads a content referenced by self and
+ # returns the content as string.
+ # The string is extended with OpenURI::Meta.
+ # The argument `options' is same as OpenURI::OpenRead#open.
+ def read(options={})
+ self.open(options) {|f|
+ str = f.read
+ Meta.init str, f
+ str
+ }
+ end
+ end
+end
+
+module URI
+ class Generic
+ # returns a proxy URI.
+ # The proxy URI is obtained from environment variables such as http_proxy,
+ # ftp_proxy, no_proxy, etc.
+ # If there is no proper proxy, nil is returned.
+ #
+ # Note that capitalized variables (HTTP_PROXY, FTP_PROXY, NO_PROXY, etc.)
+ # are examined too.
+ #
+ # But http_proxy and HTTP_PROXY is treated specially under CGI environment.
+ # It's because HTTP_PROXY may be set by Proxy: header.
+ # So HTTP_PROXY is not used.
+ # http_proxy is not used too if the variable is case insensitive.
+ # CGI_HTTP_PROXY can be used instead.
+ def find_proxy
+ name = self.scheme.downcase + '_proxy'
+ proxy_uri = nil
+ if name == 'http_proxy' && ENV.include?('REQUEST_METHOD') # CGI?
+ # HTTP_PROXY conflicts with *_proxy for proxy settings and
+ # HTTP_* for header information in CGI.
+ # So it should be careful to use it.
+ pairs = ENV.reject {|k, v| /\Ahttp_proxy\z/i !~ k }
+ case pairs.length
+ when 0 # no proxy setting anyway.
+ proxy_uri = nil
+ when 1
+ k, v = pairs.shift
+ if k == 'http_proxy' && ENV[k.upcase] == nil
+ # http_proxy is safe to use because ENV is case sensitive.
+ proxy_uri = ENV[name]
+ else
+ proxy_uri = nil
+ end
+ else # http_proxy is safe to use because ENV is case sensitive.
+ proxy_uri = ENV[name]
+ end
+ if !proxy_uri
+ # Use CGI_HTTP_PROXY. cf. libwww-perl.
+ proxy_uri = ENV["CGI_#{name.upcase}"]
+ end
+ elsif name == 'http_proxy'
+ unless proxy_uri = ENV[name]
+ if proxy_uri = ENV[name.upcase]
+ warn 'The environment variable HTTP_PROXY is discouraged. Use http_proxy.'
+ end
+ end
+ else
+ proxy_uri = ENV[name] || ENV[name.upcase]
+ end
+
+ if proxy_uri && self.host
+ require 'socket'
+ begin
+ addr = IPSocket.getaddress(self.host)
+ proxy_uri = nil if /\A127\.|\A::1\z/ =~ addr
+ rescue SocketError
+ end
+ end
+
+ if proxy_uri
+ proxy_uri = URI.parse(proxy_uri)
+ name = 'no_proxy'
+ if no_proxy = ENV[name] || ENV[name.upcase]
+ no_proxy.scan(/([^:,]*)(?::(\d+))?/) {|host, port|
+ if /(\A|\.)#{Regexp.quote host}\z/i =~ self.host &&
+ (!port || self.port == port.to_i)
+ proxy_uri = nil
+ break
+ end
+ }
+ end
+ proxy_uri
+ else
+ nil
+ end
+ end
+ end
+
+ class HTTP
+ def buffer_open(buf, proxy, options) # :nodoc:
+ OpenURI.open_http(buf, self, proxy, options)
+ end
+
+ include OpenURI::OpenRead
+ end
+
+ class FTP
+ def buffer_open(buf, proxy, options) # :nodoc:
+ if proxy
+ OpenURI.open_http(buf, self, proxy, options)
+ return
+ end
+ require 'net/ftp'
+
+ directories = self.path.split(%r{/}, -1)
+ directories.shift if directories[0] == '' # strip a field before leading slash
+ directories.each {|d|
+ d.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/) { [$1].pack("H2") }
+ }
+ unless filename = directories.pop
+ raise ArgumentError, "no filename: #{self.inspect}"
+ end
+ directories.each {|d|
+ if /[\r\n]/ =~ d
+ raise ArgumentError, "invalid directory: #{d.inspect}"
+ end
+ }
+ if /[\r\n]/ =~ filename
+ raise ArgumentError, "invalid filename: #{filename.inspect}"
+ end
+ typecode = self.typecode
+ if typecode && /\A[aid]\z/ !~ typecode
+ raise ArgumentError, "invalid typecode: #{typecode.inspect}"
+ end
+
+ # The access sequence is defined by RFC 1738
+ ftp = Net::FTP.open(self.host)
+ # todo: extract user/passwd from .netrc.
+ user = 'anonymous'
+ passwd = nil
+ user, passwd = self.userinfo.split(/:/) if self.userinfo
+ ftp.login(user, passwd)
+ directories.each {|cwd|
+ ftp.voidcmd("CWD #{cwd}")
+ }
+ if typecode
+ # xxx: typecode D is not handled.
+ ftp.voidcmd("TYPE #{typecode.upcase}")
+ end
+ if options[:content_length_proc]
+ options[:content_length_proc].call(ftp.size(filename))
+ end
+ ftp.retrbinary("RETR #{filename}", 4096) { |str|
+ buf << str
+ options[:progress_proc].call(buf.size) if options[:progress_proc]
+ }
+ ftp.close
+ buf.io.rewind
+ end
+
+ include OpenURI::OpenRead
+ end
+end
diff --git a/ruby_1_8_5/lib/open3.rb b/ruby_1_8_5/lib/open3.rb
new file mode 100644
index 0000000000..f722252b1c
--- /dev/null
+++ b/ruby_1_8_5/lib/open3.rb
@@ -0,0 +1,82 @@
+# open3.rb: Spawn a program like popen, but with stderr, too. You might also
+# want to use this if you want to bypass the shell. (By passing multiple args,
+# which IO#popen does not allow)
+#
+# Usage:
+#
+# require "open3"
+#
+# stdin, stdout, stderr = Open3.popen3('nroff -man')
+#
+# or:
+#
+# include Open3
+#
+# stdin, stdout, stderr = popen3('nroff -man')
+#
+# popen3 can also take a block which will receive stdin, stdout and stderr as
+# parameters. This ensures stdin, stdout and stderr are closed once the block
+# exits.
+#
+# Such as:
+#
+# Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... }
+
+module Open3
+ #[stdin, stdout, stderr] = popen3(command);
+ def popen3(*cmd)
+ pw = IO::pipe # pipe[0] for read, pipe[1] for write
+ pr = IO::pipe
+ pe = IO::pipe
+
+ pid = fork{
+ # child
+ fork{
+ # grandchild
+ pw[1].close
+ STDIN.reopen(pw[0])
+ pw[0].close
+
+ pr[0].close
+ STDOUT.reopen(pr[1])
+ pr[1].close
+
+ pe[0].close
+ STDERR.reopen(pe[1])
+ pe[1].close
+
+ exec(*cmd)
+ }
+ exit!(0)
+ }
+
+ pw[0].close
+ pr[1].close
+ pe[1].close
+ Process.waitpid(pid)
+ pi = [pw[1], pr[0], pe[0]]
+ pw[1].sync = true
+ if defined? yield
+ begin
+ return yield(*pi)
+ ensure
+ pi.each{|p| p.close unless p.closed?}
+ end
+ end
+ pi
+ end
+ module_function :popen3
+end
+
+if $0 == __FILE__
+ a = Open3.popen3("nroff -man")
+ Thread.start do
+ while line = gets
+ a[0].print line
+ end
+ a[0].close
+ end
+ while line = a[1].gets
+ print ":", line
+ end
+end
diff --git a/ruby_1_8_5/lib/optparse.rb b/ruby_1_8_5/lib/optparse.rb
new file mode 100644
index 0000000000..c1df9586c8
--- /dev/null
+++ b/ruby_1_8_5/lib/optparse.rb
@@ -0,0 +1,1727 @@
+#
+# optparse.rb - command-line option analysis with the OptionParser class.
+#
+# Author:: Nobu Nakada
+# Documentation:: Nobu Nakada and Gavin Sinclair.
+#
+# See OptionParser for documentation.
+#
+
+
+# == Developer Documentation (not for RDoc output)
+#
+# === Class tree
+#
+# - OptionParser:: front end
+# - OptionParser::Switch:: each switches
+# - OptionParser::List:: options list
+# - OptionParser::ParseError:: errors on parsing
+# - OptionParser::AmbiguousOption
+# - OptionParser::NeedlessArgument
+# - OptionParser::MissingArgument
+# - OptionParser::InvalidOption
+# - OptionParser::InvalidArgument
+# - OptionParser::AmbiguousArgument
+#
+# === Object relationship diagram
+#
+# +--------------+
+# | OptionParser |<>-----+
+# +--------------+ | +--------+
+# | ,-| Switch |
+# on_head -------->+---------------+ / +--------+
+# accept/reject -->| List |<|>-
+# | |<|>- +----------+
+# on ------------->+---------------+ `-| argument |
+# : : | class |
+# +---------------+ |==========|
+# on_tail -------->| | |pattern |
+# +---------------+ |----------|
+# OptionParser.accept ->| DefaultList | |converter |
+# reject |(shared between| +----------+
+# | all instances)|
+# +---------------+
+#
+# == OptionParser
+#
+# === Introduction
+#
+# OptionParser is a class for command-line option analysis. It is much more
+# advanced, yet also easier to use, than GetoptLong, and is a more Ruby-oriented
+# solution.
+#
+# === Features
+#
+# 1. The argument specification and the code to handle it are written in the
+# same place.
+# 2. It can output an option summary; you don't need to maintain this string
+# separately.
+# 3. Optional and mandatory arguments are specified very gracefully.
+# 4. Arguments can be automatically converted to a specified class.
+# 5. Arguments can be restricted to a certain set.
+#
+# All of these features are demonstrated in the examples below.
+#
+# === Minimal example
+#
+# require 'optparse'
+#
+# options = {}
+# OptionParser.new do |opts|
+# opts.banner = "Usage: example.rb [options]"
+#
+# opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
+# options[:verbose] = v
+# end
+# end.parse!
+#
+# p options
+# p ARGV
+#
+# === Complete example
+#
+# The following example is a complete Ruby program. You can run it and see the
+# effect of specifying various options. This is probably the best way to learn
+# the features of +optparse+.
+#
+# require 'optparse'
+# require 'optparse/time'
+# require 'ostruct'
+# require 'pp'
+#
+# class OptparseExample
+#
+# CODES = %w[iso-2022-jp shift_jis euc-jp utf8 binary]
+# CODE_ALIASES = { "jis" => "iso-2022-jp", "sjis" => "shift_jis" }
+#
+# #
+# # Return a structure describing the options.
+# #
+# def self.parse(args)
+# # The options specified on the command line will be collected in *options*.
+# # We set default values here.
+# options = OpenStruct.new
+# options.library = []
+# options.inplace = false
+# options.encoding = "utf8"
+# options.transfer_type = :auto
+# options.verbose = false
+#
+# opts = OptionParser.new do |opts|
+# opts.banner = "Usage: example.rb [options]"
+#
+# opts.separator ""
+# opts.separator "Specific options:"
+#
+# # Mandatory argument.
+# opts.on("-r", "--require LIBRARY",
+# "Require the LIBRARY before executing your script") do |lib|
+# options.library << lib
+# end
+#
+# # Optional argument; multi-line description.
+# opts.on("-i", "--inplace [EXTENSION]",
+# "Edit ARGV files in place",
+# " (make backup if EXTENSION supplied)") do |ext|
+# options.inplace = true
+# options.extension = ext || ''
+# options.extension.sub!(/\A\.?(?=.)/, ".") # Ensure extension begins with dot.
+# end
+#
+# # Cast 'delay' argument to a Float.
+# opts.on("--delay N", Float, "Delay N seconds before executing") do |n|
+# options.delay = n
+# end
+#
+# # Cast 'time' argument to a Time object.
+# opts.on("-t", "--time [TIME]", Time, "Begin execution at given time") do |time|
+# options.time = time
+# end
+#
+# # Cast to octal integer.
+# opts.on("-F", "--irs [OCTAL]", OptionParser::OctalInteger,
+# "Specify record separator (default \\0)") do |rs|
+# options.record_separator = rs
+# end
+#
+# # List of arguments.
+# opts.on("--list x,y,z", Array, "Example 'list' of arguments") do |list|
+# options.list = list
+# end
+#
+# # Keyword completion. We are specifying a specific set of arguments (CODES
+# # and CODE_ALIASES - notice the latter is a Hash), and the user may provide
+# # the shortest unambiguous text.
+# code_list = (CODE_ALIASES.keys + CODES).join(',')
+# opts.on("--code CODE", CODES, CODE_ALIASES, "Select encoding",
+# " (#{code_list})") do |encoding|
+# options.encoding = encoding
+# end
+#
+# # Optional argument with keyword completion.
+# opts.on("--type [TYPE]", [:text, :binary, :auto],
+# "Select transfer type (text, binary, auto)") do |t|
+# options.transfer_type = t
+# end
+#
+# # Boolean switch.
+# opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
+# options.verbose = v
+# end
+#
+# opts.separator ""
+# opts.separator "Common options:"
+#
+# # No argument, shows at tail. This will print an options summary.
+# # Try it and see!
+# opts.on_tail("-h", "--help", "Show this message") do
+# puts opts
+# exit
+# end
+#
+# # Another typical switch to print the version.
+# opts.on_tail("--version", "Show version") do
+# puts OptionParser::Version.join('.')
+# exit
+# end
+# end
+#
+# opts.parse!(args)
+# options
+# end # parse()
+#
+# end # class OptparseExample
+#
+# options = OptparseExample.parse(ARGV)
+# pp options
+#
+# === Further documentation
+#
+# The above examples should be enough to learn how to use this class. If you
+# have any questions, email me (gsinclair@soyabean.com.au) and I will update
+# this document.
+#
+class OptionParser
+ # :stopdoc:
+ RCSID = %w$Id: optparse.rb,v 1.40.2.12 2006/08/04 22:00:21 drbrain Exp $[1..-1].each {|s| s.freeze}.freeze
+ Version = (RCSID[1].split('.').collect {|s| s.to_i}.extend(Comparable).freeze if RCSID[1])
+ LastModified = (Time.gm(*RCSID[2, 2].join('-').scan(/\d+/).collect {|s| s.to_i}) if RCSID[2])
+ Release = RCSID[2]
+
+ NoArgument = [NO_ARGUMENT = :NONE, nil].freeze
+ RequiredArgument = [REQUIRED_ARGUMENT = :REQUIRED, true].freeze
+ OptionalArgument = [OPTIONAL_ARGUMENT = :OPTIONAL, false].freeze
+ # :startdoc:
+
+ #
+ # Keyword completion module. This allows partial arguments to be specified
+ # and resolved against a list of acceptable values.
+ #
+ module Completion
+ def complete(key, icase = false, pat = nil)
+ pat ||= Regexp.new('\A' + Regexp.quote(key).gsub(/\w+\b/, '\&\w*'),
+ icase)
+ canon, sw, k, v, cn = nil
+ candidates = []
+ each do |k, *v|
+ (if Regexp === k
+ kn = nil
+ k === key
+ else
+ kn = defined?(k.id2name) ? k.id2name : k
+ pat === kn
+ end) or next
+ v << k if v.empty?
+ candidates << [k, v, kn]
+ end
+ candidates = candidates.sort_by {|k, v, kn| kn.size}
+ if candidates.size == 1
+ canon, sw, * = candidates[0]
+ elsif candidates.size > 1
+ canon, sw, cn = candidates.shift
+ candidates.each do |k, v, kn|
+ next if sw == v
+ if String === cn and String === kn
+ if cn.rindex(kn, 0)
+ canon, sw, cn = k, v, kn
+ next
+ elsif kn.rindex(cn, 0)
+ next
+ end
+ end
+ throw :ambiguous, key
+ end
+ end
+ if canon
+ block_given? or return key, *sw
+ yield(key, *sw)
+ end
+ end
+
+ def convert(opt = nil, val = nil, *)
+ val
+ end
+ end
+
+
+ #
+ # Map from option/keyword string to object with completion.
+ #
+ class OptionMap < Hash
+ include Completion
+ end
+
+
+ #
+ # Individual switch class. Not important to the user.
+ #
+ # Defined within Switch are several Switch-derived classes: NoArgument,
+ # RequiredArgument, etc.
+ #
+ class Switch
+ attr_reader :pattern, :conv, :short, :long, :arg, :desc, :block
+
+ #
+ # Guesses argument style from +arg+. Returns corresponding
+ # OptionParser::Switch class (OptionalArgument, etc.).
+ #
+ def self.guess(arg)
+ case arg
+ when ""
+ t = self
+ when /\A=?\[/
+ t = Switch::OptionalArgument
+ when /\A\s+\[/
+ t = Switch::PlacedArgument
+ else
+ t = Switch::RequiredArgument
+ end
+ self >= t or incompatible_argument_styles(arg, t)
+ t
+ end
+
+ def self.incompatible_argument_styles(arg, t)
+ raise ArgumentError, "#{arg}: incompatible argument styles\n #{self}, #{t}"
+ end
+
+ def self.pattern
+ NilClass
+ end
+
+ def initialize(pattern = nil, conv = nil,
+ short = nil, long = nil, arg = nil,
+ desc = ([] if short or long), block = Proc.new)
+ raise if Array === pattern
+ @pattern, @conv, @short, @long, @arg, @desc, @block =
+ pattern, conv, short, long, arg, desc, block
+ end
+
+ #
+ # Parses +arg+ and returns rest of +arg+ and matched portion to the
+ # argument pattern. Yields when the pattern doesn't match substring.
+ #
+ def parse_arg(arg)
+ pattern or return nil, arg
+ unless m = pattern.match(arg)
+ yield(InvalidArgument, arg)
+ return arg, nil
+ end
+ if String === m
+ m = [s = m]
+ else
+ m = m.to_a
+ s = m[0]
+ return nil, m unless String === s
+ end
+ raise InvalidArgument, arg unless arg.rindex(s, 0)
+ return nil, m if s.length == arg.length
+ yield(InvalidArgument, arg) # didn't match whole arg
+ return arg[s.length..-1], m
+ end
+ private :parse_arg
+
+ #
+ # Parses argument, converts and returns +arg+, +block+ and result of
+ # conversion. Yields at semi-error condition instead of raising an
+ # exception.
+ #
+ def conv_arg(arg, val = nil)
+ if block
+ if conv
+ val = conv.call(*val)
+ else
+ val = *val
+ end
+ return arg, block, val
+ else
+ return arg, nil
+ end
+ end
+ private :conv_arg
+
+ #
+ # Produces the summary text. Each line of the summary is yielded to the
+ # block (without newline).
+ #
+ # +sdone+:: Already summarized short style options keyed hash.
+ # +ldone+:: Already summarized long style options keyed hash.
+ # +width+:: Width of left side (option part). In other words, the right
+ # side (description part) starts after +width+ columns.
+ # +max+:: Maximum width of left side -> the options are filled within
+ # +max+ columns.
+ # +indent+:: Prefix string indents all summarized lines.
+ #
+ def summarize(sdone = [], ldone = [], width = 1, max = width - 1, indent = "")
+ sopts, lopts, s = [], [], nil
+ @short.each {|s| sdone.fetch(s) {sopts << s}; sdone[s] = true} if @short
+ @long.each {|s| ldone.fetch(s) {lopts << s}; ldone[s] = true} if @long
+ return if sopts.empty? and lopts.empty? # completely hidden
+
+ left = [sopts.join(', ')]
+ right = desc.dup
+
+ while s = lopts.shift
+ l = left[-1].length + s.length
+ l += arg.length if left.size == 1 && arg
+ l < max or left << ''
+ left[-1] << if left[-1].empty? then ' ' * 4 else ', ' end << s
+ end
+
+ left[0] << arg if arg
+ mlen = left.collect {|s| s.length}.max.to_i
+ while mlen > width and l = left.shift
+ mlen = left.collect {|s| s.length}.max.to_i if l.length == mlen
+ yield(indent + l)
+ end
+
+ while (l = left.shift; r = right.shift; l or r)
+ l = l.to_s.ljust(width) + ' ' + r if r and !r.empty?
+ yield(indent + l)
+ end
+
+ self
+ end
+
+ #
+ # Switch that takes no arguments.
+ #
+ class NoArgument < self
+
+ #
+ # Raises an exception if any arguments given.
+ #
+ def parse(arg, argv, &error)
+ yield(NeedlessArgument, arg) if arg
+ conv_arg(arg)
+ end
+
+ def self.incompatible_argument_styles(*)
+ end
+
+ def self.pattern
+ Object
+ end
+ end
+
+ #
+ # Switch that takes an argument.
+ #
+ class RequiredArgument < self
+
+ #
+ # Raises an exception if argument is not present.
+ #
+ def parse(arg, argv)
+ unless arg
+ raise MissingArgument if argv.empty?
+ arg = argv.shift
+ end
+ conv_arg(*parse_arg(arg) {|*exc| raise(*exc)})
+ end
+ end
+
+ #
+ # Switch that can omit argument.
+ #
+ class OptionalArgument < self
+
+ #
+ # Parses argument if given, or uses default value.
+ #
+ def parse(arg, argv, &error)
+ if arg
+ conv_arg(*parse_arg(arg, &error))
+ else
+ conv_arg(arg)
+ end
+ end
+ end
+
+ #
+ # Switch that takes an argument, which does not begin with '-'.
+ #
+ class PlacedArgument < self
+
+ #
+ # Returns nil if argument is not present or begins with '-'.
+ #
+ def parse(arg, argv, &error)
+ if !(val = arg) and (argv.empty? or /\A-/ =~ (val = argv[0]))
+ return nil, block, nil
+ end
+ opt = (val = parse_arg(val, &error))[1]
+ val = conv_arg(*val)
+ if opt and !arg
+ argv.shift
+ else
+ val[0] = nil
+ end
+ val
+ end
+ end
+ end
+
+ #
+ # Simple option list providing mapping from short and/or long option
+ # string to OptionParser::Switch and mapping from acceptable argument to
+ # matching pattern and converter pair. Also provides summary feature.
+ #
+ class List
+ # Map from acceptable argument types to pattern and converter pairs.
+ attr_reader :atype
+
+ # Map from short style option switches to actual switch objects.
+ attr_reader :short
+
+ # Map from long style option switches to actual switch objects.
+ attr_reader :long
+
+ # List of all switches and summary string.
+ attr_reader :list
+
+ #
+ # Just initializes all instance variables.
+ #
+ def initialize
+ @atype = {}
+ @short = OptionMap.new
+ @long = OptionMap.new
+ @list = []
+ end
+
+ #
+ # See OptionParser.accept.
+ #
+ def accept(t, pat = /.*/nm, &block)
+ if pat
+ pat.respond_to?(:match) or raise TypeError, "has no `match'"
+ else
+ pat = t if t.respond_to?(:match)
+ end
+ unless block
+ block = pat.method(:convert).to_proc if pat.respond_to?(:convert)
+ end
+ @atype[t] = [pat, block]
+ end
+
+ #
+ # See OptionParser.reject.
+ #
+ def reject(t)
+ @atype.delete(t)
+ end
+
+ #
+ # Adds +sw+ according to +sopts+, +lopts+ and +nlopts+.
+ #
+ # +sw+:: OptionParser::Switch instance to be added.
+ # +sopts+:: Short style option list.
+ # +lopts+:: Long style option list.
+ # +nlopts+:: Negated long style options list.
+ #
+ def update(sw, sopts, lopts, nsw = nil, nlopts = nil)
+ o = nil
+ sopts.each {|o| @short[o] = sw} if sopts
+ lopts.each {|o| @long[o] = sw} if lopts
+ nlopts.each {|o| @long[o] = nsw} if nsw and nlopts
+ used = @short.invert.update(@long.invert)
+ @list.delete_if {|o| Switch === o and !used[o]}
+ end
+ private :update
+
+ #
+ # Inserts +switch+ at the head of the list, and associates short, long
+ # and negated long options. Arguments are:
+ #
+ # +switch+:: OptionParser::Switch instance to be inserted.
+ # +short_opts+:: List of short style options.
+ # +long_opts+:: List of long style options.
+ # +nolong_opts+:: List of long style options with "no-" prefix.
+ #
+ # prepend(switch, short_opts, long_opts, nolong_opts)
+ #
+ def prepend(*args)
+ update(*args)
+ @list.unshift(args[0])
+ end
+
+ #
+ # Appends +switch+ at the tail of the list, and associates short, long
+ # and negated long options. Arguments are:
+ #
+ # +switch+:: OptionParser::Switch instance to be inserted.
+ # +short_opts+:: List of short style options.
+ # +long_opts+:: List of long style options.
+ # +nolong_opts+:: List of long style options with "no-" prefix.
+ #
+ # append(switch, short_opts, long_opts, nolong_opts)
+ #
+ def append(*args)
+ update(*args)
+ @list.push(args[0])
+ end
+
+ #
+ # Searches +key+ in +id+ list. The result is returned or yielded if a
+ # block is given. If it isn't found, nil is returned.
+ #
+ def search(id, key)
+ if list = __send__(id)
+ val = list.fetch(key) {return nil}
+ return val unless block_given?
+ yield(val)
+ end
+ end
+
+ #
+ # Searches list +id+ for +opt+ and the optional patterns for completion
+ # +pat+. If +icase+ is true, the search is case insensitive. The result
+ # is returned or yielded if a block is given. If it isn't found, nil is
+ # returned.
+ #
+ def complete(id, opt, icase = false, *pat, &block)
+ __send__(id).complete(opt, icase, *pat, &block)
+ end
+
+ #
+ # Creates the summary table, passing each line to the +block+ (without
+ # newline). The arguments +args+ are passed along to the summarize
+ # method which is called on every option.
+ #
+ def summarize(*args, &block)
+ list.each do |opt|
+ if opt.respond_to?(:summarize) # perhaps OptionParser::Switch
+ opt.summarize(*args, &block)
+ elsif opt.empty?
+ yield("")
+ else
+ opt.each(&block)
+ end
+ end
+ end
+ end
+
+ #
+ # Hash with completion search feature. See OptionParser::Completion.
+ #
+ class CompletingHash < Hash
+ include Completion
+
+ #
+ # Completion for hash key.
+ #
+ def match(key)
+ return key, *fetch(key) {
+ raise AmbiguousArgument, catch(:ambiguous) {return complete(key)}
+ }
+ end
+ end
+
+ # :stopdoc:
+
+ #
+ # Enumeration of acceptable argument styles. Possible values are:
+ #
+ # NO_ARGUMENT:: The switch takes no arguments. (:NONE)
+ # REQUIRED_ARGUMENT:: The switch requires an argument. (:REQUIRED)
+ # OPTIONAL_ARGUMENT:: The switch requires an optional argument. (:OPTIONAL)
+ #
+ # Use like --switch=argument (long style) or -Xargument (short style). For
+ # short style, only portion matched to argument pattern is dealed as
+ # argument.
+ #
+ ArgumentStyle = {}
+ NoArgument.each {|el| ArgumentStyle[el] = Switch::NoArgument}
+ RequiredArgument.each {|el| ArgumentStyle[el] = Switch::RequiredArgument}
+ OptionalArgument.each {|el| ArgumentStyle[el] = Switch::OptionalArgument}
+ ArgumentStyle.freeze
+
+ #
+ # Switches common used such as '--', and also provides default
+ # argument classes
+ #
+ DefaultList = List.new
+ DefaultList.short['-'] = Switch::NoArgument.new {}
+ DefaultList.long[''] = Switch::NoArgument.new {throw :terminate}
+
+ #
+ # Default options for ARGV, which never appear in option summary.
+ #
+ Officious = {}
+
+ #
+ # --help
+ # Shows option summary.
+ #
+ Officious['help'] = proc do |parser|
+ Switch::NoArgument.new do
+ puts parser.help
+ exit
+ end
+ end
+
+ #
+ # --version
+ # Shows version string if Version is defined.
+ #
+ Officious['version'] = proc do |parser|
+ Switch::OptionalArgument.new do |pkg|
+ if pkg
+ begin
+ require 'optparse/version'
+ rescue LoadError
+ else
+ show_version(*pkg.split(/,/)) or
+ abort("#{parser.program_name}: no version found in package #{pkg}")
+ exit
+ end
+ end
+ v = parser.ver or abort("#{parser.program_name}: version unknown")
+ puts v
+ exit
+ end
+ end
+
+ # :startdoc:
+
+ #
+ # Class methods
+ #
+
+ #
+ # Initializes a new instance and evaluates the optional block in context
+ # of the instance. Arguments +args+ are passed to #new, see there for
+ # description of parameters.
+ #
+ # This method is *deprecated*, its behavior corresponds to the older #new
+ # method.
+ #
+ def self.with(*args, &block)
+ opts = new(*args)
+ opts.instance_eval(&block)
+ opts
+ end
+
+ #
+ # Returns an incremented value of +default+ according to +arg+.
+ #
+ def self.inc(arg, default = nil)
+ case arg
+ when Integer
+ arg.nonzero?
+ when nil
+ default.to_i + 1
+ end
+ end
+ def inc(*args)
+ self.class.inc(*args)
+ end
+
+ #
+ # Initializes the instance and yields itself if called with a block.
+ #
+ # +banner+:: Banner message.
+ # +width+:: Summary width.
+ # +indent+:: Summary indent.
+ #
+ def initialize(banner = nil, width = 32, indent = ' ' * 4)
+ @stack = [DefaultList, List.new, List.new]
+ @program_name = nil
+ @banner = banner
+ @summary_width = width
+ @summary_indent = indent
+ @default_argv = ARGV
+ add_officious
+ yield self if block_given?
+ end
+
+ def add_officious # :nodoc:
+ list = base()
+ Officious.each_pair do |opt, block|
+ list.long[opt] ||= block.call(self)
+ end
+ end
+
+ #
+ # Terminates option parsing. Optional parameter +arg+ is a string pushed
+ # back to be the first non-option argument.
+ #
+ def terminate(arg = nil)
+ self.class.terminate(arg)
+ end
+ def self.terminate(arg = nil)
+ throw :terminate, arg
+ end
+
+ @stack = [DefaultList]
+ def self.top() DefaultList end
+
+ #
+ # Directs to accept specified class +t+. The argument string is passed to
+ # the block in which it should be converted to the desired class.
+ #
+ # +t+:: Argument class specifier, any object including Class.
+ # +pat+:: Pattern for argument, defaults to +t+ if it responds to match.
+ #
+ # accept(t, pat, &block)
+ #
+ def accept(*args, &blk) top.accept(*args, &blk) end
+ #
+ # See #accept.
+ #
+ def self.accept(*args, &blk) top.accept(*args, &blk) end
+
+ #
+ # Directs to reject specified class argument.
+ #
+ # +t+:: Argument class speficier, any object including Class.
+ #
+ # reject(t)
+ #
+ def reject(*args, &blk) top.reject(*args, &blk) end
+ #
+ # See #reject.
+ #
+ def self.reject(*args, &blk) top.reject(*args, &blk) end
+
+ #
+ # Instance methods
+ #
+
+ # Heading banner preceding summary.
+ attr_writer :banner
+
+ # Program name to be emitted in error message and default banner,
+ # defaults to $0.
+ attr_writer :program_name
+
+ # Width for option list portion of summary. Must be Numeric.
+ attr_accessor :summary_width
+
+ # Indentation for summary. Must be String (or have + String method).
+ attr_accessor :summary_indent
+
+ # Strings to be parsed in default.
+ attr_accessor :default_argv
+
+ #
+ # Heading banner preceding summary.
+ #
+ def banner
+ @banner ||= "Usage: #{program_name} [options]"
+ end
+
+ #
+ # Program name to be emitted in error message and default banner, defaults
+ # to $0.
+ #
+ def program_name
+ @program_name || File.basename($0, '.*')
+ end
+
+ # for experimental cascading :-)
+ alias set_banner banner=
+ alias set_program_name program_name=
+ alias set_summary_width summary_width=
+ alias set_summary_indent summary_indent=
+
+ # Version
+ attr_writer :version
+ # Release code
+ attr_writer :release
+
+ #
+ # Version
+ #
+ def version
+ @version || (defined?(::Version) && ::Version)
+ end
+
+ #
+ # Release code
+ #
+ def release
+ @release || (defined?(::Release) && ::Release) || (defined?(::RELEASE) && ::RELEASE)
+ end
+
+ #
+ # Returns version string from program_name, version and release.
+ #
+ def ver
+ if v = version
+ str = "#{program_name} #{[v].join('.')}"
+ str << " (#{v})" if v = release
+ str
+ end
+ end
+
+ def warn(mesg = $!)
+ super("#{program_name}: #{mesg}")
+ end
+
+ def abort(mesg = $!)
+ super("#{program_name}: #{mesg}")
+ end
+
+ #
+ # Subject of #on / #on_head, #accept / #reject
+ #
+ def top
+ @stack[-1]
+ end
+
+ #
+ # Subject of #on_tail.
+ #
+ def base
+ @stack[1]
+ end
+
+ #
+ # Pushes a new List.
+ #
+ def new
+ @stack.push(List.new)
+ if block_given?
+ yield self
+ else
+ self
+ end
+ end
+
+ #
+ # Removes the last List.
+ #
+ def remove
+ @stack.pop
+ end
+
+ #
+ # Puts option summary into +to+ and returns +to+. Yields each line if
+ # a block is given.
+ #
+ # +to+:: Output destination, which must have method <<. Defaults to [].
+ # +width+:: Width of left side, defaults to @summary_width.
+ # +max+:: Maximum length allowed for left side, defaults to +width+ - 1.
+ # +indent+:: Indentation, defaults to @summary_indent.
+ #
+ def summarize(to = [], width = @summary_width, max = width - 1, indent = @summary_indent, &blk)
+ visit(:summarize, {}, {}, width, max, indent, &(blk || proc {|l| to << l + $/}))
+ to
+ end
+
+ #
+ # Returns option summary string.
+ #
+ def help; summarize(banner.to_s.sub(/\n?\z/, "\n")) end
+ alias to_s help
+
+ #
+ # Returns option summary list.
+ #
+ def to_a; summarize(banner.to_a.dup) end
+
+ #
+ # Checks if an argument is given twice, in which case an ArgumentError is
+ # raised. Called from OptionParser#switch only.
+ #
+ # +obj+:: New argument.
+ # +prv+:: Previously specified argument.
+ # +msg+:: Exception message.
+ #
+ def notwice(obj, prv, msg)
+ unless !prv or prv == obj
+ begin
+ raise ArgumentError, "argument #{msg} given twice: #{obj}"
+ rescue
+ $@[0, 2] = nil
+ raise
+ end
+ end
+ obj
+ end
+ private :notwice
+
+ #
+ # Creates an OptionParser::Switch from the parameters. The parsed argument
+ # value is passed to the given block, where it can be processed.
+ #
+ # See at the beginning of OptionParser for some full examples.
+ #
+ # +opts+ can include the following elements:
+ #
+ # [Argument style:]
+ # One of the following:
+ # :NONE, :REQUIRED, :OPTIONAL
+ #
+ # [Argument pattern:]
+ # Acceptable option argument format, must be pre-defined with
+ # OptionParser.accept or OptionParser#accept, or Regexp. This can appear
+ # once or assigned as String if not present, otherwise causes an
+ # ArgumentError. Examples:
+ # Float, Time, Array
+ #
+ # [Possible argument values:]
+ # Hash or Array.
+ # [:text, :binary, :auto]
+ # %w[iso-2022-jp shift_jis euc-jp utf8 binary]
+ # { "jis" => "iso-2022-jp", "sjis" => "shift_jis" }
+ #
+ # [Long style switch:]
+ # Specifies a long style switch which takes a mandatory, optional or no
+ # argument. It's a string of the following form:
+ # "--switch=MANDATORY" or "--switch MANDATORY"
+ # "--switch[=OPTIONAL]"
+ # "--switch"
+ #
+ # [Short style switch:]
+ # Specifies short style switch which takes a mandatory, optional or no
+ # argument. It's a string of the following form:
+ # "-xMANDATORY"
+ # "-x[OPTIONAL]"
+ # "-x"
+ # There is also a special form which matches character range (not full
+ # set of regural expression):
+ # "-[a-z]MANDATORY"
+ # "-[a-z][OPTIONAL]"
+ # "-[a-z]"
+ #
+ # [Argument style and description:]
+ # Instead of specifying mandatory or optional orguments directly in the
+ # switch parameter, this separate parameter can be used.
+ # "=MANDATORY"
+ # "=[OPTIONAL]"
+ #
+ # [Description:]
+ # Description string for the option.
+ # "Run verbosely"
+ #
+ # [Handler:]
+ # Handler for the parsed argument value. Either give a block or pass a
+ # Proc or Method as an argument.
+ #
+ def make_switch(*opts, &block)
+ short, long, nolong, style, pattern, conv, not_pattern, not_conv, not_style = [], [], []
+ ldesc, sdesc, desc, arg = [], [], []
+ default_style = Switch::NoArgument
+ default_pattern = nil
+ klass = nil
+ o = nil
+ n, q, a = nil
+
+ opts.each do |o|
+ # argument class
+ next if search(:atype, o) do |pat, c|
+ klass = notwice(o, klass, 'type')
+ if not_style and not_style != Switch::NoArgument
+ not_pattern, not_conv = pat, c
+ else
+ default_pattern, conv = pat, c
+ end
+ end
+
+ # directly specified pattern(any object possible to match)
+ if !(String === o) and o.respond_to?(:match)
+ pattern = notwice(o, pattern, 'pattern')
+ conv = (pattern.method(:convert).to_proc if pattern.respond_to?(:convert))
+ next
+ end
+
+ # anything others
+ case o
+ when Proc, Method
+ block = notwice(o, block, 'block')
+ when Array, Hash
+ case pattern
+ when CompletingHash
+ when nil
+ pattern = CompletingHash.new
+ conv = (pattern.method(:convert).to_proc if pattern.respond_to?(:convert))
+ else
+ raise ArgumentError, "argument pattern given twice"
+ end
+ o.each {|(o, *v)| pattern[o] = v.fetch(0) {o}}
+ when Module
+ raise ArgumentError, "unsupported argument type: #{o}"
+ when *ArgumentStyle.keys
+ style = notwice(ArgumentStyle[o], style, 'style')
+ when /^--no-([^\[\]=\s]*)(.+)?/
+ q, a = $1, $2
+ o = notwice(a ? Object : TrueClass, klass, 'type')
+ not_pattern, not_conv = search(:atype, o) unless not_style
+ not_style = (not_style || default_style).guess(arg = a) if a
+ default_style = Switch::NoArgument
+ default_pattern, conv = search(:atype, FalseClass) unless default_pattern
+ ldesc << "--no-#{q}"
+ long << 'no-' + (q = q.downcase)
+ nolong << q
+ when /^--\[no-\]([^\[\]=\s]*)(.+)?/
+ q, a = $1, $2
+ o = notwice(a ? Object : TrueClass, klass, 'type')
+ if a
+ default_style = default_style.guess(arg = a)
+ default_pattern, conv = search(:atype, o) unless default_pattern
+ end
+ ldesc << "--[no-]#{q}"
+ long << (o = q.downcase)
+ not_pattern, not_conv = search(:atype, FalseClass) unless not_style
+ not_style = Switch::NoArgument
+ nolong << 'no-' + o
+ when /^--([^\[\]=\s]*)(.+)?/
+ q, a = $1, $2
+ if a
+ o = notwice(NilClass, klass, 'type')
+ default_style = default_style.guess(arg = a)
+ default_pattern, conv = search(:atype, o) unless default_pattern
+ end
+ ldesc << "--#{q}"
+ long << (o = q.downcase)
+ when /^-(\[\^?\]?(?:[^\\\]]|\\.)*\])(.+)?/
+ q, a = $1, $2
+ o = notwice(Object, klass, 'type')
+ if a
+ default_style = default_style.guess(arg = a)
+ default_pattern, conv = search(:atype, o) unless default_pattern
+ end
+ sdesc << "-#{q}"
+ short << Regexp.new(q)
+ when /^-(.)(.+)?/
+ q, a = $1, $2
+ if a
+ o = notwice(NilClass, klass, 'type')
+ default_style = default_style.guess(arg = a)
+ default_pattern, conv = search(:atype, o) unless default_pattern
+ end
+ sdesc << "-#{q}"
+ short << q
+ when /^=/
+ style = notwice(default_style.guess(arg = o), style, 'style')
+ default_pattern, conv = search(:atype, Object) unless default_pattern
+ else
+ desc.push(o)
+ end
+ end
+
+ default_pattern, conv = search(:atype, default_style.pattern) unless default_pattern
+ s = if short.empty? and long.empty?
+ raise ArgumentError, "no switch given" if style or pattern or block
+ desc
+ else
+ (style || default_style).new(pattern || default_pattern,
+ conv, sdesc, ldesc, arg, desc, block)
+ end
+ return s, short, long,
+ (not_style.new(not_pattern, not_conv, sdesc, ldesc, nil, desc, block) if not_style),
+ nolong
+ end
+
+ def define(*opts, &block)
+ top.append(*(sw = make_switch(*opts, &block)))
+ sw[0]
+ end
+
+ #
+ # Add option switch and handler. See #make_switch for an explanation of
+ # parameters.
+ #
+ def on(*opts, &block)
+ define(*opts, &block)
+ self
+ end
+ alias def_option define
+
+ def define_head(*opts, &block)
+ top.prepend(*(sw = make_switch(*opts, &block)))
+ sw[0]
+ end
+
+ #
+ # Add option switch like with #on, but at head of summary.
+ #
+ def on_head(*opts, &block)
+ define_head(*opts, &block)
+ self
+ end
+ alias def_head_option define_head
+
+ def define_tail(*opts, &block)
+ base.append(*(sw = make_switch(*opts, &block)))
+ sw[0]
+ end
+
+ #
+ # Add option switch like with #on, but at tail of summary.
+ #
+ def on_tail(*opts, &block)
+ define_tail(*opts, &block)
+ self
+ end
+ alias def_tail_option define_tail
+
+ #
+ # Add separator in summary.
+ #
+ def separator(string)
+ top.append(string, nil, nil)
+ end
+
+ #
+ # Parses command line arguments +argv+ in order. When a block is given,
+ # each non-option argument is yielded.
+ #
+ # Returns the rest of +argv+ left unparsed.
+ #
+ def order(*argv, &block)
+ argv = argv[0].dup if argv.size == 1 and Array === argv[0]
+ order!(argv, &block)
+ end
+
+ #
+ # Same as #order, but removes switches destructively.
+ #
+ def order!(argv = default_argv, &nonopt)
+ opt, arg, sw, val, rest = nil
+ nonopt ||= proc {|arg| throw :terminate, arg}
+ argv.unshift(arg) if arg = catch(:terminate) {
+ while arg = argv.shift
+ case arg
+ # long option
+ when /\A--([^=]*)(?:=(.*))?/nm
+ opt, rest = $1, $2
+ begin
+ sw, = complete(:long, opt, true)
+ rescue ParseError
+ raise $!.set_option(arg, true)
+ end
+ begin
+ opt, sw, val = sw.parse(rest, argv) {|*exc| raise(*exc)}
+ sw.call(val) if sw
+ rescue ParseError
+ raise $!.set_option(arg, rest)
+ end
+
+ # short option
+ when /\A-(.)((=).*|.+)?/nm
+ opt, has_arg, eq, val, rest = $1, $3, $3, $2, $2
+ begin
+ unless sw = search(:short, opt)
+ begin
+ sw, = complete(:short, opt)
+ # short option matched.
+ val = arg.sub(/\A-/, '')
+ has_arg = true
+ rescue InvalidOption
+ # if no short options match, try completion with long
+ # options.
+ sw, = complete(:long, opt)
+ eq ||= !rest
+ end
+ end
+ rescue ParseError
+ raise $!.set_option(arg, true)
+ end
+ begin
+ opt, sw, val = sw.parse(val, argv) {|*exc| raise(*exc) if eq}
+ raise InvalidOption, arg if has_arg and !eq and arg == "-#{opt}"
+ argv.unshift(opt) if opt and (opt = opt.sub(/\A-*/, '-')) != '-'
+ sw.call(val) if sw
+ rescue ParseError
+ raise $!.set_option(arg, arg.length > 2)
+ end
+
+ # non-option argument
+ else
+ nonopt.call(arg)
+ end
+ end
+
+ nil
+ }
+
+ argv
+ end
+
+ #
+ # Parses command line arguments +argv+ in permutation mode and returns
+ # list of non-option arguments.
+ #
+ def permute(*argv)
+ argv = argv[0].dup if argv.size == 1 and Array === argv[0]
+ permute!(argv)
+ end
+
+ #
+ # Same as #permute, but removes switches destructively.
+ #
+ def permute!(argv = default_argv)
+ nonopts = []
+ arg = nil
+ order!(argv) {|arg| nonopts << arg}
+ argv[0, 0] = nonopts
+ argv
+ end
+
+ #
+ # Parses command line arguments +argv+ in order when environment variable
+ # POSIXLY_CORRECT is set, and in permutation mode otherwise.
+ #
+ def parse(*argv)
+ argv = argv[0].dup if argv.size == 1 and Array === argv[0]
+ parse!(argv)
+ end
+
+ #
+ # Same as #parse, but removes switches destructively.
+ #
+ def parse!(argv = default_argv)
+ if ENV.include?('POSIXLY_CORRECT')
+ order!(argv)
+ else
+ permute!(argv)
+ end
+ end
+
+ #
+ # Wrapper method for getopts.rb.
+ #
+ def getopts(argv, single_options, *long_options)
+ result = {}
+
+ single_options.scan(/(.)(:)?/) do |opt, val|
+ if val
+ result[opt] = nil
+ define("-#{opt} VAL") {|val| result[opt] = val}
+ else
+ result[opt] = false
+ define("-#{opt}") {result[opt] = true}
+ end
+ end if single_options
+
+ long_options.each do |arg|
+ opt, val = arg.split(':', 2)
+ if val
+ result[opt] = val.empty? ? nil : val
+ define("--#{opt} VAL") {|val| result[opt] = val}
+ else
+ result[opt] = false
+ define("--#{opt}") {result[opt] = true}
+ end
+ end
+
+ order!(argv)
+ result
+ end
+
+ #
+ # See #getopts.
+ #
+ def self.getopts(*args)
+ new.getopts(*args)
+ end
+
+ #
+ # Traverses @stack, sending each element method +id+ with +args+ and
+ # +block+.
+ #
+ def visit(id, *args, &block)
+ el = nil
+ @stack.reverse_each do |el|
+ el.send(id, *args, &block)
+ end
+ nil
+ end
+ private :visit
+
+ #
+ # Searches key +k+ in @stack for +id+ hash and returns or yields the result.
+ #
+ def search(id, k)
+ visit(:search, id, k) do |k|
+ return k unless block_given?
+ return yield(k)
+ end
+ end
+ private :search
+
+ #
+ # Completes shortened long style option switch and returns pair of
+ # canonical switch and switch descriptor OptionParser::Switch.
+ #
+ # +id+:: Searching table.
+ # +opt+:: Searching key.
+ # +icase+:: Search case insensitive if true.
+ # +pat+:: Optional pattern for completion.
+ #
+ def complete(typ, opt, icase = false, *pat)
+ if pat.empty?
+ search(typ, opt) {|sw| return [sw, opt]} # exact match or...
+ end
+ raise AmbiguousOption, catch(:ambiguous) {
+ visit(:complete, typ, opt, icase, *pat) {|opt, *sw| return sw}
+ raise InvalidOption, opt
+ }
+ end
+ private :complete
+
+ #
+ # Loads options from file names as +filename+. Does nothing when the file
+ # is not present. Returns whether successfully loaded.
+ #
+ # +filename+ defaults to basename of the program without suffix in a
+ # directory ~/.options.
+ #
+ def load(filename = nil)
+ begin
+ filename ||= File.expand_path(File.basename($0, '.*'), '~/.options')
+ rescue
+ return false
+ end
+ begin
+ parse(*IO.readlines(filename).each {|s| s.chomp!})
+ true
+ rescue Errno::ENOENT, Errno::ENOTDIR
+ false
+ end
+ end
+
+ #
+ # Parses environment variable +env+ or its uppercase with splitting like a
+ # shell.
+ #
+ # +env+ defaults to the basename of the program.
+ #
+ def environment(env = File.basename($0, '.*'))
+ env = ENV[env] || ENV[env.upcase] or return
+ parse(*Shellwords.shellwords(env))
+ end
+
+ #
+ # Acceptable argument classes
+ #
+
+ #
+ # Any string and no conversion. This is fall-back.
+ #
+ accept(Object) {|s,|s or s.nil?}
+
+ accept(NilClass) {|s,|s}
+
+ #
+ # Any non-empty string, and no conversion.
+ #
+ accept(String, /.+/nm) {|s,*|s}
+
+ #
+ # Ruby/C-like integer, octal for 0-7 sequence, binary for 0b, hexadecimal
+ # for 0x, and decimal for others; with optional sign prefix. Converts to
+ # Integer.
+ #
+ decimal = '\d+(?:_\d+)*'
+ binary = 'b[01]+(?:_[01]+)*'
+ hex = 'x[\da-f]+(?:_[\da-f]+)*'
+ octal = "0(?:[0-7]*(?:_[0-7]+)*|#{binary}|#{hex})"
+ integer = "#{octal}|#{decimal}"
+ accept(Integer, %r"\A[-+]?(?:#{integer})"io) {|s,| Integer(s) if s}
+
+ #
+ # Float number format, and converts to Float.
+ #
+ float = "(?:#{decimal}(?:\\.(?:#{decimal})?)?|\\.#{decimal})(?:E[-+]?#{decimal})?"
+ floatpat = %r"\A[-+]?#{float}"io
+ accept(Float, floatpat) {|s,| s.to_f if s}
+
+ #
+ # Generic numeric format, converts to Integer for integer format, Float
+ # for float format.
+ #
+ accept(Numeric, %r"\A[-+]?(?:#{octal}|#{float})"io) {|s,| eval(s) if s}
+
+ #
+ # Decimal integer format, to be converted to Integer.
+ #
+ DecimalInteger = /\A[-+]?#{decimal}/io
+ accept(DecimalInteger) {|s,| s.to_i if s}
+
+ #
+ # Ruby/C like octal/hexadecimal/binary integer format, to be converted to
+ # Integer.
+ #
+ OctalInteger = /\A[-+]?(?:[0-7]+(?:_[0-7]+)*|0(?:#{binary}|#{hex}))/io
+ accept(OctalInteger) {|s,| s.oct if s}
+
+ #
+ # Decimal integer/float number format, to be converted to Integer for
+ # integer format, Float for float format.
+ #
+ DecimalNumeric = floatpat # decimal integer is allowed as float also.
+ accept(DecimalNumeric) {|s,| eval(s) if s}
+
+ #
+ # Boolean switch, which means whether it is present or not, whether it is
+ # absent or not with prefix no-, or it takes an argument
+ # yes/no/true/false/+/-.
+ #
+ yesno = CompletingHash.new
+ %w[- no false].each {|el| yesno[el] = false}
+ %w[+ yes true].each {|el| yesno[el] = true}
+ yesno['nil'] = false # shoud be nil?
+ accept(TrueClass, yesno) {|arg, val| val == nil or val}
+ #
+ # Similar to TrueClass, but defaults to false.
+ #
+ accept(FalseClass, yesno) {|arg, val| val != nil and val}
+
+ #
+ # List of strings separated by ",".
+ #
+ accept(Array) do |s,|
+ if s
+ s = s.split(',').collect {|s| s unless s.empty?}
+ end
+ s
+ end
+
+ #
+ # Regular expression with options.
+ #
+ accept(Regexp, %r"\A/((?:\\.|[^\\])*)/([[:alpha:]]+)?\z|.*") do |all, s, o|
+ f = 0
+ if o
+ f |= Regexp::IGNORECASE if /i/ =~ o
+ f |= Regexp::MULTILINE if /m/ =~ o
+ f |= Regexp::EXTENDED if /x/ =~ o
+ k = o.delete("^imx")
+ end
+ Regexp.new(s || all, f, k)
+ end
+
+ #
+ # Exceptions
+ #
+
+ #
+ # Base class of exceptions from OptionParser.
+ #
+ class ParseError < RuntimeError
+ # Reason which caused the error.
+ Reason = 'parse error'.freeze
+
+ def initialize(*args)
+ @args = args
+ @reason = nil
+ end
+
+ attr_reader :args
+ attr_writer :reason
+
+ #
+ # Pushes back erred argument(s) to +argv+.
+ #
+ def recover(argv)
+ argv[0, 0] = @args
+ argv
+ end
+
+ def set_option(opt, eq)
+ if eq
+ @args[0] = opt
+ else
+ @args.unshift(opt)
+ end
+ self
+ end
+
+ #
+ # Returns error reason. Override this for I18N.
+ #
+ def reason
+ @reason || self.class::Reason
+ end
+
+ def inspect
+ "#<#{self.class.to_s}: #{args.join(' ')}>"
+ end
+
+ #
+ # Default stringizing method to emit standard error message.
+ #
+ def message
+ reason + ': ' + args.join(' ')
+ end
+
+ alias to_s message
+ alias to_str message
+ end
+
+ #
+ # Raises when ambiguously completable string is encountered.
+ #
+ class AmbiguousOption < ParseError
+ const_set(:Reason, 'ambiguous option'.freeze)
+ end
+
+ #
+ # Raises when there is an argument for a switch which takes no argument.
+ #
+ class NeedlessArgument < ParseError
+ const_set(:Reason, 'needless argument'.freeze)
+ end
+
+ #
+ # Raises when a switch with mandatory argument has no argument.
+ #
+ class MissingArgument < ParseError
+ const_set(:Reason, 'missing argument'.freeze)
+ end
+
+ #
+ # Raises when switch is undefined.
+ #
+ class InvalidOption < ParseError
+ const_set(:Reason, 'invalid option'.freeze)
+ end
+
+ #
+ # Raises when the given argument does not match required format.
+ #
+ class InvalidArgument < ParseError
+ const_set(:Reason, 'invalid argument'.freeze)
+ end
+
+ #
+ # Raises when the given argument word can't be completed uniquely.
+ #
+ class AmbiguousArgument < InvalidArgument
+ const_set(:Reason, 'ambiguous argument'.freeze)
+ end
+
+ #
+ # Miscellaneous
+ #
+
+ #
+ # Extends command line arguments array (ARGV) to parse itself.
+ #
+ module Arguable
+
+ #
+ # Sets OptionParser object, when +opt+ is +false+ or +nil+, methods
+ # OptionParser::Arguable#options and OptionParser::Arguable#options= are
+ # undefined. Thus, there is no ways to access the OptionParser object
+ # via the receiver object.
+ #
+ def options=(opt)
+ unless @optparse = opt
+ class << self
+ undef_method(:options)
+ undef_method(:options=)
+ end
+ end
+ end
+
+ #
+ # Actual OptionParser object, automatically created if nonexistent.
+ #
+ # If called with a block, yields the OptionParser object and returns the
+ # result of the block. If an OptionParser::ParseError exception occurs
+ # in the block, it is rescued, a error message printed to STDERR and
+ # +nil+ returned.
+ #
+ def options
+ @optparse ||= OptionParser.new
+ @optparse.default_argv = self
+ block_given? or return @optparse
+ begin
+ yield @optparse
+ rescue ParseError
+ @optparse.warn $!
+ nil
+ end
+ end
+
+ #
+ # Parses +self+ destructively in order and returns +self+ containing the
+ # rest arguments left unparsed.
+ #
+ def order!(&blk) options.order!(self, &blk) end
+
+ #
+ # Parses +self+ destructively in permutation mode and returns +self+
+ # containing the rest arguments left unparsed.
+ #
+ def permute!() options.permute!(self) end
+
+ #
+ # Parses +self+ destructively and returns +self+ containing the
+ # rest arguments left unparsed.
+ #
+ def parse!() options.parse!(self) end
+
+ #
+ # Substitution of getopts is possible as follows. Also see
+ # OptionParser#getopts.
+ #
+ # def getopts(*args)
+ # ($OPT = ARGV.getopts(*args)).each do |opt, val|
+ # eval "$OPT_#{opt.gsub(/[^A-Za-z0-9_]/, '_')} = val"
+ # end
+ # rescue OptionParser::ParseError
+ # end
+ #
+ def getopts(*args)
+ options.getopts(self, *args)
+ end
+
+ #
+ # Initializes instance variable.
+ #
+ def self.extend_object(obj)
+ super
+ obj.instance_eval {@optparse = nil}
+ end
+ def initialize(*args)
+ super
+ @optparse = nil
+ end
+ end
+
+ #
+ # Acceptable argument classes. Now contains DecimalInteger, OctalInteger
+ # and DecimalNumeric. See Acceptable argument classes (in source code).
+ #
+ module Acceptables
+ const_set(:DecimalInteger, OptionParser::DecimalInteger)
+ const_set(:OctalInteger, OptionParser::OctalInteger)
+ const_set(:DecimalNumeric, OptionParser::DecimalNumeric)
+ end
+end
+
+# ARGV is arguable by OptionParser
+ARGV.extend(OptionParser::Arguable)
+
+if $0 == __FILE__
+ Version = OptionParser::Version
+ ARGV.options {|q|
+ q.parse!.empty? or puts "what's #{ARGV.join(' ')}?"
+ } or exit 1
+end
diff --git a/ruby_1_8_5/lib/optparse/date.rb b/ruby_1_8_5/lib/optparse/date.rb
new file mode 100644
index 0000000000..d680559f37
--- /dev/null
+++ b/ruby_1_8_5/lib/optparse/date.rb
@@ -0,0 +1,17 @@
+require 'optparse'
+require 'date'
+
+OptionParser.accept(DateTime) do |s,|
+ begin
+ DateTime.parse(s) if s
+ rescue ArgumentError
+ raise OptionParser::InvalidArgument, s
+ end
+end
+OptionParser.accept(Date) do |s,|
+ begin
+ Date.parse(s) if s
+ rescue ArgumentError
+ raise OptionParser::InvalidArgument, s
+ end
+end
diff --git a/ruby_1_8_5/lib/optparse/shellwords.rb b/ruby_1_8_5/lib/optparse/shellwords.rb
new file mode 100644
index 0000000000..0422d7c887
--- /dev/null
+++ b/ruby_1_8_5/lib/optparse/shellwords.rb
@@ -0,0 +1,6 @@
+# -*- ruby -*-
+
+require 'shellwords'
+require 'optparse'
+
+OptionParser.accept(Shellwords) {|s,| Shellwords.shellwords(s)}
diff --git a/ruby_1_8_5/lib/optparse/time.rb b/ruby_1_8_5/lib/optparse/time.rb
new file mode 100644
index 0000000000..402cadcf16
--- /dev/null
+++ b/ruby_1_8_5/lib/optparse/time.rb
@@ -0,0 +1,10 @@
+require 'optparse'
+require 'time'
+
+OptionParser.accept(Time) do |s,|
+ begin
+ (Time.httpdate(s) rescue Time.parse(s)) if s
+ rescue
+ raise OptionParser::InvalidArgument, s
+ end
+end
diff --git a/ruby_1_8_5/lib/optparse/uri.rb b/ruby_1_8_5/lib/optparse/uri.rb
new file mode 100644
index 0000000000..024dc69eac
--- /dev/null
+++ b/ruby_1_8_5/lib/optparse/uri.rb
@@ -0,0 +1,6 @@
+# -*- ruby -*-
+
+require 'optparse'
+require 'uri'
+
+OptionParser.accept(URI) {|s,| URI.parse(s) if s}
diff --git a/ruby_1_8_5/lib/optparse/version.rb b/ruby_1_8_5/lib/optparse/version.rb
new file mode 100644
index 0000000000..558d9d710b
--- /dev/null
+++ b/ruby_1_8_5/lib/optparse/version.rb
@@ -0,0 +1,70 @@
+# OptionParser internal utility
+
+class << OptionParser
+ def show_version(*pkg)
+ progname = ARGV.options.program_name
+ result = false
+ show = proc do |klass, cname, version|
+ str = "#{progname}"
+ unless klass == ::Object and cname == :VERSION
+ version = version.join(".") if Array === version
+ str << ": #{klass}" unless klass == Object
+ str << " version #{version}"
+ end
+ [:Release, :RELEASE].find do |rel|
+ if klass.const_defined?(rel)
+ str << " (#{klass.const_get(rel)})"
+ end
+ end
+ puts str
+ result = true
+ end
+ if pkg.size == 1 and pkg[0] == "all"
+ self.search_const(::Object, /\AV(?:ERSION|ersion)\z/) do |klass, cname, version|
+ unless cname[1] == ?e and klass.const_defined?(:Version)
+ show.call(klass, cname.intern, version)
+ end
+ end
+ else
+ pkg.each do |pkg|
+ begin
+ pkg = pkg.split(/::|\//).inject(::Object) {|m, c| m.const_get(c)}
+ v = case
+ when pkg.const_defined?(:Version)
+ pkg.const_get(n = :Version)
+ when pkg.const_defined?(:VERSION)
+ pkg.const_get(n = :VERSION)
+ else
+ n = nil
+ "unknown"
+ end
+ show.call(pkg, n, v)
+ rescue NameError
+ end
+ end
+ end
+ result
+ end
+
+ def each_const(path, klass = ::Object)
+ path.split(/::|\//).inject(klass) do |klass, name|
+ raise NameError, path unless Module === klass
+ klass.constants.grep(/#{name}/i) do |c|
+ klass.const_defined?(c) or next
+ c = klass.const_get(c)
+ end
+ end
+ end
+
+ def search_const(klass, name)
+ klasses = [klass]
+ while klass = klasses.shift
+ klass.constants.each do |cname|
+ klass.const_defined?(cname) or next
+ const = klass.const_get(cname)
+ yield klass, cname, const if name === cname
+ klasses << const if Module === const and const != ::Object
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/ostruct.rb b/ruby_1_8_5/lib/ostruct.rb
new file mode 100644
index 0000000000..6af5bbdac0
--- /dev/null
+++ b/ruby_1_8_5/lib/ostruct.rb
@@ -0,0 +1,146 @@
+#
+# = ostruct.rb: OpenStruct implementation
+#
+# Author:: Yukihiro Matsumoto
+# Documentation:: Gavin Sinclair
+#
+# OpenStruct allows the creation of data objects with arbitrary attributes.
+# See OpenStruct for an example.
+#
+
+#
+# OpenStruct allows you to create data objects and set arbitrary attributes.
+# For example:
+#
+# require 'ostruct'
+#
+# record = OpenStruct.new
+# record.name = "John Smith"
+# record.age = 70
+# record.pension = 300
+#
+# puts record.name # -> "John Smith"
+# puts record.address # -> nil
+#
+# It is like a hash with a different way to access the data. In fact, it is
+# implemented with a hash, and you can initialize it with one.
+#
+# hash = { "country" => "Australia", :population => 20_000_000 }
+# data = OpenStruct.new(hash)
+#
+# p data # -> <OpenStruct country="Australia" population=20000000>
+#
+class OpenStruct
+ #
+ # Create a new OpenStruct object. The optional +hash+, if given, will
+ # generate attributes and values. For example.
+ #
+ # require 'ostruct'
+ # hash = { "country" => "Australia", :population => 20_000_000 }
+ # data = OpenStruct.new(hash)
+ #
+ # p data # -> <OpenStruct country="Australia" population=20000000>
+ #
+ # By default, the resulting OpenStruct object will have no attributes.
+ #
+ def initialize(hash=nil)
+ @table = {}
+ if hash
+ for k,v in hash
+ @table[k.to_sym] = v
+ new_ostruct_member(k)
+ end
+ end
+ end
+
+ # Duplicate an OpenStruct object members.
+ def initialize_copy(orig)
+ super
+ @table = @table.dup
+ end
+
+ def marshal_dump
+ @table
+ end
+ def marshal_load(x)
+ @table = x
+ @table.each_key{|key| new_ostruct_member(key)}
+ end
+
+ def new_ostruct_member(name)
+ name = name.to_sym
+ unless self.respond_to?(name)
+ meta = class << self; self; end
+ meta.send(:define_method, name) { @table[name] }
+ meta.send(:define_method, :"#{name}=") { |x| @table[name] = x }
+ end
+ end
+
+ def method_missing(mid, *args) # :nodoc:
+ mname = mid.id2name
+ len = args.length
+ if mname =~ /=$/
+ if len != 1
+ raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1)
+ end
+ if self.frozen?
+ raise TypeError, "can't modify frozen #{self.class}", caller(1)
+ end
+ mname.chop!
+ self.new_ostruct_member(mname)
+ @table[mname.intern] = args[0]
+ elsif len == 0
+ @table[mid]
+ else
+ raise NoMethodError, "undefined method `#{mname}' for #{self}", caller(1)
+ end
+ end
+
+ #
+ # Remove the named field from the object.
+ #
+ def delete_field(name)
+ @table.delete name.to_sym
+ end
+
+ InspectKey = :__inspect_key__ # :nodoc:
+
+ #
+ # Returns a string containing a detailed summary of the keys and values.
+ #
+ def inspect
+ str = "#<#{self.class}"
+
+ Thread.current[InspectKey] ||= []
+ if Thread.current[InspectKey].include?(self) then
+ str << " ..."
+ else
+ first = true
+ for k,v in @table
+ str << "," unless first
+ first = false
+
+ Thread.current[InspectKey] << v
+ begin
+ str << " #{k}=#{v.inspect}"
+ ensure
+ Thread.current[InspectKey].pop
+ end
+ end
+ end
+
+ str << ">"
+ end
+ alias :to_s :inspect
+
+ attr_reader :table # :nodoc:
+ protected :table
+
+ #
+ # Compare this object and +other+ for equality.
+ #
+ def ==(other)
+ return false unless(other.kind_of?(OpenStruct))
+ return @table == other.table
+ end
+end
diff --git a/ruby_1_8_5/lib/parsearg.rb b/ruby_1_8_5/lib/parsearg.rb
new file mode 100644
index 0000000000..b7e18c3afd
--- /dev/null
+++ b/ruby_1_8_5/lib/parsearg.rb
@@ -0,0 +1,87 @@
+#
+# parsearg.rb - parse arguments
+# $Release Version: $
+# $Revision: 1.2.2.2 $
+# $Date: 2006/08/04 22:00:21 $
+# by Yasuo OHBA(SHL Japan Inc. Technology Dept.)
+#
+# --
+#
+#
+#
+
+warn "Warning:#{caller[0].sub(/:in `.*'\z/, '')}: parsearg is deprecated after Ruby 1.8.1; use optparse instead"
+
+$RCS_ID=%q$Header: /src/ruby/lib/parsearg.rb,v 1.2.2.2 2006/08/04 22:00:21 drbrain Exp $
+
+require "getopts"
+
+def printUsageAndExit()
+ if $USAGE
+ eval($USAGE)
+ end
+ exit()
+end
+
+def setParenthesis(ex, opt, c)
+ if opt != ""
+ ex = sprintf("%s$OPT_%s%s", ex, opt, c)
+ else
+ ex = sprintf("%s%s", ex, c)
+ end
+ return ex
+end
+
+def setOrAnd(ex, opt, c)
+ if opt != ""
+ ex = sprintf("%s$OPT_%s %s%s ", ex, opt, c, c)
+ else
+ ex = sprintf("%s %s%s ", ex, c, c)
+ end
+ return ex
+end
+
+def setExpression(ex, opt, op)
+ if !op
+ ex = sprintf("%s$OPT_%s", ex, opt)
+ return ex
+ end
+ case op.chr
+ when "(", ")"
+ ex = setParenthesis(ex, opt, op.chr)
+ when "|", "&"
+ ex = setOrAnd(ex, opt, op.chr)
+ else
+ return nil
+ end
+ return ex
+end
+
+# parseArgs is obsolete. Use OptionParser instead.
+
+def parseArgs(argc, nopt, single_opts, *opts)
+ if (noOptions = getopts(single_opts, *opts)) == nil
+ printUsageAndExit()
+ end
+ if nopt
+ ex = nil
+ pos = 0
+ for o in nopt.split(/[()|&]/)
+ pos += o.length
+ ex = setExpression(ex, o, nopt[pos])
+ pos += 1
+ end
+ begin
+ if !eval(ex)
+ printUsageAndExit()
+ end
+ rescue
+ print "Format Error!! : \"" + nopt + "\"\t[parseArgs]\n"
+ exit!(-1)
+ end
+ end
+ if ARGV.length < argc
+ printUsageAndExit()
+ end
+ return noOptions
+end
diff --git a/ruby_1_8_5/lib/parsedate.rb b/ruby_1_8_5/lib/parsedate.rb
new file mode 100644
index 0000000000..405ab46907
--- /dev/null
+++ b/ruby_1_8_5/lib/parsedate.rb
@@ -0,0 +1,15 @@
+# parsedate.rb: Written by Tadayoshi Funaba 2001, 2002
+# $Id: parsedate.rb,v 2.6 2002-05-14 07:43:18+09 tadf Exp $
+
+require 'date/format'
+
+module ParseDate
+
+ def parsedate(str, comp=false)
+ Date._parse(str, comp).
+ values_at(:year, :mon, :mday, :hour, :min, :sec, :zone, :wday)
+ end
+
+ module_function :parsedate
+
+end
diff --git a/ruby_1_8_5/lib/pathname.rb b/ruby_1_8_5/lib/pathname.rb
new file mode 100644
index 0000000000..e4ca5489ce
--- /dev/null
+++ b/ruby_1_8_5/lib/pathname.rb
@@ -0,0 +1,1062 @@
+#
+# = pathname.rb
+#
+# Object-Oriented Pathname Class
+#
+# Author:: Tanaka Akira <akr@m17n.org>
+# Documentation:: Author and Gavin Sinclair
+#
+# For documentation, see class Pathname.
+#
+# <tt>pathname.rb</tt> is distributed with Ruby since 1.8.0.
+#
+
+#
+# == Pathname
+#
+# Pathname represents a pathname which locates a file in a filesystem.
+# The pathname depends on OS: Unix, Windows, etc.
+# Pathname library works with pathnames of local OS.
+# However non-Unix pathnames are supported experimentally.
+#
+# It does not represent the file itself.
+# A Pathname can be relative or absolute. It's not until you try to
+# reference the file that it even matters whether the file exists or not.
+#
+# Pathname is immutable. It has no method for destructive update.
+#
+# The value of this class is to manipulate file path information in a neater
+# way than standard Ruby provides. The examples below demonstrate the
+# difference. *All* functionality from File, FileTest, and some from Dir and
+# FileUtils is included, in an unsurprising way. It is essentially a facade for
+# all of these, and more.
+#
+# == Examples
+#
+# === Example 1: Using Pathname
+#
+# require 'pathname'
+# p = Pathname.new("/usr/bin/ruby")
+# size = p.size # 27662
+# isdir = p.directory? # false
+# dir = p.dirname # Pathname:/usr/bin
+# base = p.basename # Pathname:ruby
+# dir, base = p.split # [Pathname:/usr/bin, Pathname:ruby]
+# data = p.read
+# p.open { |f| _ }
+# p.each_line { |line| _ }
+#
+# === Example 2: Using standard Ruby
+#
+# p = "/usr/bin/ruby"
+# size = File.size(p) # 27662
+# isdir = File.directory?(p) # false
+# dir = File.dirname(p) # "/usr/bin"
+# base = File.basename(p) # "ruby"
+# dir, base = File.split(p) # ["/usr/bin", "ruby"]
+# data = File.read(p)
+# File.open(p) { |f| _ }
+# File.foreach(p) { |line| _ }
+#
+# === Example 3: Special features
+#
+# p1 = Pathname.new("/usr/lib") # Pathname:/usr/lib
+# p2 = p1 + "ruby/1.8" # Pathname:/usr/lib/ruby/1.8
+# p3 = p1.parent # Pathname:/usr
+# p4 = p2.relative_path_from(p3) # Pathname:lib/ruby/1.8
+# pwd = Pathname.pwd # Pathname:/home/gavin
+# pwd.absolute? # true
+# p5 = Pathname.new "." # Pathname:.
+# p5 = p5 + "music/../articles" # Pathname:music/../articles
+# p5.cleanpath # Pathname:articles
+# p5.realpath # Pathname:/home/gavin/articles
+# p5.children # [Pathname:/home/gavin/articles/linux, ...]
+#
+# == Breakdown of functionality
+#
+# === Core methods
+#
+# These methods are effectively manipulating a String, because that's all a path
+# is. Except for #mountpoint?, #children, and #realpath, they don't access the
+# filesystem.
+#
+# - +
+# - #join
+# - #parent
+# - #root?
+# - #absolute?
+# - #relative?
+# - #relative_path_from
+# - #each_filename
+# - #cleanpath
+# - #realpath
+# - #children
+# - #mountpoint?
+#
+# === File status predicate methods
+#
+# These methods are a facade for FileTest:
+# - #blockdev?
+# - #chardev?
+# - #directory?
+# - #executable?
+# - #executable_real?
+# - #exist?
+# - #file?
+# - #grpowned?
+# - #owned?
+# - #pipe?
+# - #readable?
+# - #world_readable?
+# - #readable_real?
+# - #setgid?
+# - #setuid?
+# - #size
+# - #size?
+# - #socket?
+# - #sticky?
+# - #symlink?
+# - #writable?
+# - #world_writable?
+# - #writable_real?
+# - #zero?
+#
+# === File property and manipulation methods
+#
+# These methods are a facade for File:
+# - #atime
+# - #ctime
+# - #mtime
+# - #chmod(mode)
+# - #lchmod(mode)
+# - #chown(owner, group)
+# - #lchown(owner, group)
+# - #fnmatch(pattern, *args)
+# - #fnmatch?(pattern, *args)
+# - #ftype
+# - #make_link(old)
+# - #open(*args, &block)
+# - #readlink
+# - #rename(to)
+# - #stat
+# - #lstat
+# - #make_symlink(old)
+# - #truncate(length)
+# - #utime(atime, mtime)
+# - #basename(*args)
+# - #dirname
+# - #extname
+# - #expand_path(*args)
+# - #split
+#
+# === Directory methods
+#
+# These methods are a facade for Dir:
+# - Pathname.glob(*args)
+# - Pathname.getwd / Pathname.pwd
+# - #rmdir
+# - #entries
+# - #each_entry(&block)
+# - #mkdir(*args)
+# - #opendir(*args)
+#
+# === IO
+#
+# These methods are a facade for IO:
+# - #each_line(*args, &block)
+# - #read(*args)
+# - #readlines(*args)
+# - #sysopen(*args)
+#
+# === Utilities
+#
+# These methods are a mixture of Find, FileUtils, and others:
+# - #find(&block)
+# - #mkpath
+# - #rmtree
+# - #unlink / #delete
+#
+#
+# == Method documentation
+#
+# As the above section shows, most of the methods in Pathname are facades. The
+# documentation for these methods generally just says, for instance, "See
+# FileTest.writable?", as you should be familiar with the original method
+# anyway, and its documentation (e.g. through +ri+) will contain more
+# information. In some cases, a brief description will follow.
+#
+class Pathname
+
+ # :stopdoc:
+ if RUBY_VERSION < "1.9"
+ TO_PATH = :to_str
+ else
+ # to_path is implemented so Pathname objects are usable with File.open, etc.
+ TO_PATH = :to_path
+ end
+ # :startdoc:
+
+ #
+ # Create a Pathname object from the given String (or String-like object).
+ # If +path+ contains a NUL character (<tt>\0</tt>), an ArgumentError is raised.
+ #
+ def initialize(path)
+ path = path.__send__(TO_PATH) if path.respond_to? TO_PATH
+ @path = path.dup
+
+ if /\0/ =~ @path
+ raise ArgumentError, "pathname contains \\0: #{@path.inspect}"
+ end
+
+ self.taint if @path.tainted?
+ end
+
+ def freeze() super; @path.freeze; self end
+ def taint() super; @path.taint; self end
+ def untaint() super; @path.untaint; self end
+
+ #
+ # Compare this pathname with +other+. The comparison is string-based.
+ # Be aware that two different paths (<tt>foo.txt</tt> and <tt>./foo.txt</tt>)
+ # can refer to the same file.
+ #
+ def ==(other)
+ return false unless Pathname === other
+ other.to_s == @path
+ end
+ alias === ==
+ alias eql? ==
+
+ # Provides for comparing pathnames, case-sensitively.
+ def <=>(other)
+ return nil unless Pathname === other
+ @path.tr('/', "\0") <=> other.to_s.tr('/', "\0")
+ end
+
+ def hash # :nodoc:
+ @path.hash
+ end
+
+ # Return the path as a String.
+ def to_s
+ @path.dup
+ end
+
+ # to_path is implemented so Pathname objects are usable with File.open, etc.
+ alias_method TO_PATH, :to_s
+
+ def inspect # :nodoc:
+ "#<#{self.class}:#{@path}>"
+ end
+
+ # Return a pathname which is substituted by String#sub.
+ def sub(pattern, *rest, &block)
+ self.class.new(@path.sub(pattern, *rest, &block))
+ end
+
+ if File::ALT_SEPARATOR
+ SEPARATOR_PAT = /[#{Regexp.quote File::ALT_SEPARATOR}#{Regexp.quote File::SEPARATOR}]/
+ else
+ SEPARATOR_PAT = /#{Regexp.quote File::SEPARATOR}/
+ end
+
+ # chop_basename(path) -> [pre-basename, basename] or nil
+ def chop_basename(path)
+ base = File.basename(path)
+ if /\A#{SEPARATOR_PAT}?\z/ =~ base
+ return nil
+ else
+ return path[0, path.rindex(base)], base
+ end
+ end
+ private :chop_basename
+
+ # split_names(path) -> prefix, [name, ...]
+ def split_names(path)
+ names = []
+ while r = chop_basename(path)
+ path, basename = r
+ names.unshift basename
+ end
+ return path, names
+ end
+ private :split_names
+
+ def prepend_prefix(prefix, relpath)
+ if relpath.empty?
+ File.dirname(prefix)
+ elsif /#{SEPARATOR_PAT}/ =~ prefix
+ prefix = File.dirname(prefix)
+ prefix = File.join(prefix, "") if File.basename(prefix + 'a') != 'a'
+ prefix + relpath
+ else
+ prefix + relpath
+ end
+ end
+ private :prepend_prefix
+
+ # Returns clean pathname of +self+ with consecutive slashes and useless dots
+ # removed. The filesystem is not accessed.
+ #
+ # If +consider_symlink+ is +true+, then a more conservative algorithm is used
+ # to avoid breaking symbolic linkages. This may retain more <tt>..</tt>
+ # entries than absolutely necessary, but without accessing the filesystem,
+ # this can't be avoided. See #realpath.
+ #
+ def cleanpath(consider_symlink=false)
+ if consider_symlink
+ cleanpath_conservative
+ else
+ cleanpath_aggressive
+ end
+ end
+
+ #
+ # Clean the path simply by resolving and removing excess "." and ".." entries.
+ # Nothing more, nothing less.
+ #
+ def cleanpath_aggressive
+ path = @path
+ names = []
+ pre = path
+ while r = chop_basename(pre)
+ pre, base = r
+ case base
+ when '.'
+ when '..'
+ names.unshift base
+ else
+ if names[0] == '..'
+ names.shift
+ else
+ names.unshift base
+ end
+ end
+ end
+ if /#{SEPARATOR_PAT}/o =~ File.basename(pre)
+ names.shift while names[0] == '..'
+ end
+ self.class.new(prepend_prefix(pre, File.join(*names)))
+ end
+ private :cleanpath_aggressive
+
+ # has_trailing_separator?(path) -> bool
+ def has_trailing_separator?(path)
+ if r = chop_basename(path)
+ pre, basename = r
+ pre.length + basename.length < path.length
+ else
+ false
+ end
+ end
+ private :has_trailing_separator?
+
+ # add_trailing_separator(path) -> path
+ def add_trailing_separator(path)
+ if File.basename(path + 'a') == 'a'
+ path
+ else
+ File.join(path, "") # xxx: Is File.join is appropriate to add separator?
+ end
+ end
+ private :add_trailing_separator
+
+ def del_trailing_separator(path)
+ if r = chop_basename(path)
+ pre, basename = r
+ pre + basename
+ elsif /#{SEPARATOR_PAT}+\z/o =~ path
+ $` + File.dirname(path)[/#{SEPARATOR_PAT}*\z/o]
+ else
+ path
+ end
+ end
+ private :del_trailing_separator
+
+ def cleanpath_conservative
+ path = @path
+ names = []
+ pre = path
+ while r = chop_basename(pre)
+ pre, base = r
+ names.unshift base if base != '.'
+ end
+ if /#{SEPARATOR_PAT}/o =~ File.basename(pre)
+ names.shift while names[0] == '..'
+ end
+ if names.empty?
+ self.class.new(File.dirname(pre))
+ else
+ if names.last != '..' && File.basename(path) == '.'
+ names << '.'
+ end
+ result = prepend_prefix(pre, File.join(*names))
+ if /\A(?:\.|\.\.)\z/ !~ names.last && has_trailing_separator?(path)
+ self.class.new(add_trailing_separator(result))
+ else
+ self.class.new(result)
+ end
+ end
+ end
+ private :cleanpath_conservative
+
+ def realpath_rec(prefix, unresolved, h)
+ resolved = []
+ until unresolved.empty?
+ n = unresolved.shift
+ if n == '.'
+ next
+ elsif n == '..'
+ resolved.pop
+ else
+ path = prepend_prefix(prefix, File.join(*(resolved + [n])))
+ if h.include? path
+ if h[path] == :resolving
+ raise Errno::ELOOP.new(path)
+ else
+ prefix, *resolved = h[path]
+ end
+ else
+ s = File.lstat(path)
+ if s.symlink?
+ h[path] = :resolving
+ link_prefix, link_names = split_names(File.readlink(path))
+ if link_prefix == ''
+ prefix, *resolved = h[path] = realpath_rec(prefix, resolved + link_names, h)
+ else
+ prefix, *resolved = h[path] = realpath_rec(link_prefix, link_names, h)
+ end
+ else
+ resolved << n
+ h[path] = [prefix, *resolved]
+ end
+ end
+ end
+ end
+ return prefix, *resolved
+ end
+ private :realpath_rec
+
+ #
+ # Returns a real (absolute) pathname of +self+ in the actual filesystem.
+ # The real pathname doesn't contain symlinks or useless dots.
+ #
+ # No arguments should be given; the old behaviour is *obsoleted*.
+ #
+ def realpath
+ path = @path
+ prefix, names = split_names(path)
+ if prefix == ''
+ prefix, names2 = split_names(Dir.pwd)
+ names = names2 + names
+ end
+ prefix, *names = realpath_rec(prefix, names, {})
+ self.class.new(prepend_prefix(prefix, File.join(*names)))
+ end
+
+ # #parent returns the parent directory.
+ #
+ # This is same as <tt>self + '..'</tt>.
+ def parent
+ self + '..'
+ end
+
+ # #mountpoint? returns +true+ if <tt>self</tt> points to a mountpoint.
+ def mountpoint?
+ begin
+ stat1 = self.lstat
+ stat2 = self.parent.lstat
+ stat1.dev == stat2.dev && stat1.ino == stat2.ino ||
+ stat1.dev != stat2.dev
+ rescue Errno::ENOENT
+ false
+ end
+ end
+
+ #
+ # #root? is a predicate for root directories. I.e. it returns +true+ if the
+ # pathname consists of consecutive slashes.
+ #
+ # It doesn't access actual filesystem. So it may return +false+ for some
+ # pathnames which points to roots such as <tt>/usr/..</tt>.
+ #
+ def root?
+ !!(chop_basename(@path) == nil && /#{SEPARATOR_PAT}/o =~ @path)
+ end
+
+ # Predicate method for testing whether a path is absolute.
+ # It returns +true+ if the pathname begins with a slash.
+ def absolute?
+ !relative?
+ end
+
+ # The opposite of #absolute?
+ def relative?
+ path = @path
+ while r = chop_basename(path)
+ path, basename = r
+ end
+ path == ''
+ end
+
+ #
+ # Iterates over each component of the path.
+ #
+ # Pathname.new("/usr/bin/ruby").each_filename {|filename| ... }
+ # # yields "usr", "bin", and "ruby".
+ #
+ def each_filename # :yield: filename
+ prefix, names = split_names(@path)
+ names.each {|filename| yield filename }
+ nil
+ end
+
+ # Iterates over and yields a new Pathname object
+ # for each element in the given path in descending order.
+ #
+ # Pathname.new('/path/to/some/file.rb').descend {|v| p v}
+ # #<Pathname:/>
+ # #<Pathname:/path>
+ # #<Pathname:/path/to>
+ # #<Pathname:/path/to/some>
+ # #<Pathname:/path/to/some/file.rb>
+ #
+ # Pathname.new('path/to/some/file.rb').descend {|v| p v}
+ # #<Pathname:path>
+ # #<Pathname:path/to>
+ # #<Pathname:path/to/some>
+ # #<Pathname:path/to/some/file.rb>
+ #
+ # It doesn't access actual filesystem.
+ #
+ # This method is available since 1.8.5.
+ #
+ def descend
+ vs = []
+ ascend {|v| vs << v }
+ vs.reverse_each {|v| yield v }
+ nil
+ end
+
+ # Iterates over and yields a new Pathname object
+ # for each element in the given path in ascending order.
+ #
+ # Pathname.new('/path/to/some/file.rb').ascend {|v| p v}
+ # #<Pathname:/path/to/some/file.rb>
+ # #<Pathname:/path/to/some>
+ # #<Pathname:/path/to>
+ # #<Pathname:/path>
+ # #<Pathname:/>
+ #
+ # Pathname.new('path/to/some/file.rb').ascend {|v| p v}
+ # #<Pathname:path/to/some/file.rb>
+ # #<Pathname:path/to/some>
+ # #<Pathname:path/to>
+ # #<Pathname:path>
+ #
+ # It doesn't access actual filesystem.
+ #
+ # This method is available since 1.8.5.
+ #
+ def ascend
+ path = @path
+ yield self
+ while r = chop_basename(path)
+ path, name = r
+ break if path.empty?
+ yield self.class.new(del_trailing_separator(path))
+ end
+ end
+
+ #
+ # Pathname#+ appends a pathname fragment to this one to produce a new Pathname
+ # object.
+ #
+ # p1 = Pathname.new("/usr") # Pathname:/usr
+ # p2 = p1 + "bin/ruby" # Pathname:/usr/bin/ruby
+ # p3 = p1 + "/etc/passwd" # Pathname:/etc/passwd
+ #
+ # This method doesn't access the file system; it is pure string manipulation.
+ #
+ def +(other)
+ other = Pathname.new(other) unless Pathname === other
+ Pathname.new(plus(@path, other.to_s))
+ end
+
+ def plus(path1, path2) # -> path
+ prefix2 = path2
+ index_list2 = []
+ basename_list2 = []
+ while r2 = chop_basename(prefix2)
+ prefix2, basename2 = r2
+ index_list2.unshift prefix2.length
+ basename_list2.unshift basename2
+ end
+ return path2 if prefix2 != ''
+ prefix1 = path1
+ while true
+ while !basename_list2.empty? && basename_list2.first == '.'
+ index_list2.shift
+ basename_list2.shift
+ end
+ break unless r1 = chop_basename(prefix1)
+ prefix1, basename1 = r1
+ next if basename1 == '.'
+ if basename1 == '..' || basename_list2.empty? || basename_list2.first != '..'
+ prefix1 = prefix1 + basename1
+ break
+ end
+ index_list2.shift
+ basename_list2.shift
+ end
+ r1 = chop_basename(prefix1)
+ if !r1 && /#{SEPARATOR_PAT}/o =~ File.basename(prefix1)
+ while !basename_list2.empty? && basename_list2.first == '..'
+ index_list2.shift
+ basename_list2.shift
+ end
+ end
+ if !basename_list2.empty?
+ suffix2 = path2[index_list2.first..-1]
+ r1 ? File.join(prefix1, suffix2) : prefix1 + suffix2
+ else
+ r1 ? prefix1 : File.dirname(prefix1)
+ end
+ end
+ private :plus
+
+ #
+ # Pathname#join joins pathnames.
+ #
+ # <tt>path0.join(path1, ..., pathN)</tt> is the same as
+ # <tt>path0 + path1 + ... + pathN</tt>.
+ #
+ def join(*args)
+ args.unshift self
+ result = args.pop
+ result = Pathname.new(result) unless Pathname === result
+ return result if result.absolute?
+ args.reverse_each {|arg|
+ arg = Pathname.new(arg) unless Pathname === arg
+ result = arg + result
+ return result if result.absolute?
+ }
+ result
+ end
+
+ #
+ # Returns the children of the directory (files and subdirectories, not
+ # recursive) as an array of Pathname objects. By default, the returned
+ # pathnames will have enough information to access the files. If you set
+ # +with_directory+ to +false+, then the returned pathnames will contain the
+ # filename only.
+ #
+ # For example:
+ # p = Pathname("/usr/lib/ruby/1.8")
+ # p.children
+ # # -> [ Pathname:/usr/lib/ruby/1.8/English.rb,
+ # Pathname:/usr/lib/ruby/1.8/Env.rb,
+ # Pathname:/usr/lib/ruby/1.8/abbrev.rb, ... ]
+ # p.children(false)
+ # # -> [ Pathname:English.rb, Pathname:Env.rb, Pathname:abbrev.rb, ... ]
+ #
+ # Note that the result never contain the entries <tt>.</tt> and <tt>..</tt> in
+ # the directory because they are not children.
+ #
+ # This method has existed since 1.8.1.
+ #
+ def children(with_directory=true)
+ with_directory = false if @path == '.'
+ result = []
+ Dir.foreach(@path) {|e|
+ next if e == '.' || e == '..'
+ if with_directory
+ result << self.class.new(File.join(@path, e))
+ else
+ result << self.class.new(e)
+ end
+ }
+ result
+ end
+
+ #
+ # #relative_path_from returns a relative path from the argument to the
+ # receiver. If +self+ is absolute, the argument must be absolute too. If
+ # +self+ is relative, the argument must be relative too.
+ #
+ # #relative_path_from doesn't access the filesystem. It assumes no symlinks.
+ #
+ # ArgumentError is raised when it cannot find a relative path.
+ #
+ # This method has existed since 1.8.1.
+ #
+ def relative_path_from(base_directory)
+ dest_directory = self.cleanpath.to_s
+ base_directory = base_directory.cleanpath.to_s
+ dest_prefix = dest_directory
+ dest_names = []
+ while r = chop_basename(dest_prefix)
+ dest_prefix, basename = r
+ dest_names.unshift basename if basename != '.'
+ end
+ base_prefix = base_directory
+ base_names = []
+ while r = chop_basename(base_prefix)
+ base_prefix, basename = r
+ base_names.unshift basename if basename != '.'
+ end
+ if dest_prefix != base_prefix
+ raise ArgumentError, "different prefix: #{dest_prefix.inspect} and #{base_directory.inspect}"
+ end
+ while !dest_names.empty? &&
+ !base_names.empty? &&
+ dest_names.first == base_names.first
+ dest_names.shift
+ base_names.shift
+ end
+ if base_names.include? '..'
+ raise ArgumentError, "base_directory has ..: #{base_directory.inspect}"
+ end
+ base_names.fill('..')
+ relpath_names = base_names + dest_names
+ if relpath_names.empty?
+ Pathname.new('.')
+ else
+ Pathname.new(File.join(*relpath_names))
+ end
+ end
+end
+
+class Pathname # * IO *
+ #
+ # #each_line iterates over the line in the file. It yields a String object
+ # for each line.
+ #
+ # This method has existed since 1.8.1.
+ #
+ def each_line(*args, &block) # :yield: line
+ IO.foreach(@path, *args, &block)
+ end
+
+ # Pathname#foreachline is *obsoleted* at 1.8.1. Use #each_line.
+ def foreachline(*args, &block)
+ warn "Pathname#foreachline is obsoleted. Use Pathname#each_line."
+ each_line(*args, &block)
+ end
+
+ # See <tt>IO.read</tt>. Returns all the bytes from the file, or the first +N+
+ # if specified.
+ def read(*args) IO.read(@path, *args) end
+
+ # See <tt>IO.readlines</tt>. Returns all the lines from the file.
+ def readlines(*args) IO.readlines(@path, *args) end
+
+ # See <tt>IO.sysopen</tt>.
+ def sysopen(*args) IO.sysopen(@path, *args) end
+end
+
+
+class Pathname # * File *
+
+ # See <tt>File.atime</tt>. Returns last access time.
+ def atime() File.atime(@path) end
+
+ # See <tt>File.ctime</tt>. Returns last (directory entry, not file) change time.
+ def ctime() File.ctime(@path) end
+
+ # See <tt>File.mtime</tt>. Returns last modification time.
+ def mtime() File.mtime(@path) end
+
+ # See <tt>File.chmod</tt>. Changes permissions.
+ def chmod(mode) File.chmod(mode, @path) end
+
+ # See <tt>File.lchmod</tt>.
+ def lchmod(mode) File.lchmod(mode, @path) end
+
+ # See <tt>File.chown</tt>. Change owner and group of file.
+ def chown(owner, group) File.chown(owner, group, @path) end
+
+ # See <tt>File.lchown</tt>.
+ def lchown(owner, group) File.lchown(owner, group, @path) end
+
+ # See <tt>File.fnmatch</tt>. Return +true+ if the receiver matches the given
+ # pattern.
+ def fnmatch(pattern, *args) File.fnmatch(pattern, @path, *args) end
+
+ # See <tt>File.fnmatch?</tt> (same as #fnmatch).
+ def fnmatch?(pattern, *args) File.fnmatch?(pattern, @path, *args) end
+
+ # See <tt>File.ftype</tt>. Returns "type" of file ("file", "directory",
+ # etc).
+ def ftype() File.ftype(@path) end
+
+ # See <tt>File.link</tt>. Creates a hard link.
+ def make_link(old) File.link(old, @path) end
+
+ # See <tt>File.open</tt>. Opens the file for reading or writing.
+ def open(*args, &block) # :yield: file
+ File.open(@path, *args, &block)
+ end
+
+ # See <tt>File.readlink</tt>. Read symbolic link.
+ def readlink() self.class.new(File.readlink(@path)) end
+
+ # See <tt>File.rename</tt>. Rename the file.
+ def rename(to) File.rename(@path, to) end
+
+ # See <tt>File.stat</tt>. Returns a <tt>File::Stat</tt> object.
+ def stat() File.stat(@path) end
+
+ # See <tt>File.lstat</tt>.
+ def lstat() File.lstat(@path) end
+
+ # See <tt>File.symlink</tt>. Creates a symbolic link.
+ def make_symlink(old) File.symlink(old, @path) end
+
+ # See <tt>File.truncate</tt>. Truncate the file to +length+ bytes.
+ def truncate(length) File.truncate(@path, length) end
+
+ # See <tt>File.utime</tt>. Update the access and modification times.
+ def utime(atime, mtime) File.utime(atime, mtime, @path) end
+
+ # See <tt>File.basename</tt>. Returns the last component of the path.
+ def basename(*args) self.class.new(File.basename(@path, *args)) end
+
+ # See <tt>File.dirname</tt>. Returns all but the last component of the path.
+ def dirname() self.class.new(File.dirname(@path)) end
+
+ # See <tt>File.extname</tt>. Returns the file's extension.
+ def extname() File.extname(@path) end
+
+ # See <tt>File.expand_path</tt>.
+ def expand_path(*args) self.class.new(File.expand_path(@path, *args)) end
+
+ # See <tt>File.split</tt>. Returns the #dirname and the #basename in an
+ # Array.
+ def split() File.split(@path).map {|f| self.class.new(f) } end
+
+ # Pathname#link is confusing and *obsoleted* because the receiver/argument
+ # order is inverted to corresponding system call.
+ def link(old)
+ warn 'Pathname#link is obsoleted. Use Pathname#make_link.'
+ File.link(old, @path)
+ end
+
+ # Pathname#symlink is confusing and *obsoleted* because the receiver/argument
+ # order is inverted to corresponding system call.
+ def symlink(old)
+ warn 'Pathname#symlink is obsoleted. Use Pathname#make_symlink.'
+ File.symlink(old, @path)
+ end
+end
+
+
+class Pathname # * FileTest *
+
+ # See <tt>FileTest.blockdev?</tt>.
+ def blockdev?() FileTest.blockdev?(@path) end
+
+ # See <tt>FileTest.chardev?</tt>.
+ def chardev?() FileTest.chardev?(@path) end
+
+ # See <tt>FileTest.executable?</tt>.
+ def executable?() FileTest.executable?(@path) end
+
+ # See <tt>FileTest.executable_real?</tt>.
+ def executable_real?() FileTest.executable_real?(@path) end
+
+ # See <tt>FileTest.exist?</tt>.
+ def exist?() FileTest.exist?(@path) end
+
+ # See <tt>FileTest.grpowned?</tt>.
+ def grpowned?() FileTest.grpowned?(@path) end
+
+ # See <tt>FileTest.directory?</tt>.
+ def directory?() FileTest.directory?(@path) end
+
+ # See <tt>FileTest.file?</tt>.
+ def file?() FileTest.file?(@path) end
+
+ # See <tt>FileTest.pipe?</tt>.
+ def pipe?() FileTest.pipe?(@path) end
+
+ # See <tt>FileTest.socket?</tt>.
+ def socket?() FileTest.socket?(@path) end
+
+ # See <tt>FileTest.owned?</tt>.
+ def owned?() FileTest.owned?(@path) end
+
+ # See <tt>FileTest.readable?</tt>.
+ def readable?() FileTest.readable?(@path) end
+
+ # See <tt>FileTest.world_readable?</tt>.
+ def world_readable?() FileTest.world_readable?(@path) end
+
+ # See <tt>FileTest.readable_real?</tt>.
+ def readable_real?() FileTest.readable_real?(@path) end
+
+ # See <tt>FileTest.setuid?</tt>.
+ def setuid?() FileTest.setuid?(@path) end
+
+ # See <tt>FileTest.setgid?</tt>.
+ def setgid?() FileTest.setgid?(@path) end
+
+ # See <tt>FileTest.size</tt>.
+ def size() FileTest.size(@path) end
+
+ # See <tt>FileTest.size?</tt>.
+ def size?() FileTest.size?(@path) end
+
+ # See <tt>FileTest.sticky?</tt>.
+ def sticky?() FileTest.sticky?(@path) end
+
+ # See <tt>FileTest.symlink?</tt>.
+ def symlink?() FileTest.symlink?(@path) end
+
+ # See <tt>FileTest.writable?</tt>.
+ def writable?() FileTest.writable?(@path) end
+
+ # See <tt>FileTest.world_writable?</tt>.
+ def world_writable?() FileTest.world_writable?(@path) end
+
+ # See <tt>FileTest.writable_real?</tt>.
+ def writable_real?() FileTest.writable_real?(@path) end
+
+ # See <tt>FileTest.zero?</tt>.
+ def zero?() FileTest.zero?(@path) end
+end
+
+
+class Pathname # * Dir *
+ # See <tt>Dir.glob</tt>. Returns or yields Pathname objects.
+ def Pathname.glob(*args) # :yield: p
+ if block_given?
+ Dir.glob(*args) {|f| yield self.new(f) }
+ else
+ Dir.glob(*args).map {|f| self.new(f) }
+ end
+ end
+
+ # See <tt>Dir.getwd</tt>. Returns the current working directory as a Pathname.
+ def Pathname.getwd() self.new(Dir.getwd) end
+ class << self; alias pwd getwd end
+
+ # Pathname#chdir is *obsoleted* at 1.8.1.
+ def chdir(&block)
+ warn "Pathname#chdir is obsoleted. Use Dir.chdir."
+ Dir.chdir(@path, &block)
+ end
+
+ # Pathname#chroot is *obsoleted* at 1.8.1.
+ def chroot
+ warn "Pathname#chroot is obsoleted. Use Dir.chroot."
+ Dir.chroot(@path)
+ end
+
+ # Return the entries (files and subdirectories) in the directory, each as a
+ # Pathname object.
+ def entries() Dir.entries(@path).map {|f| self.class.new(f) } end
+
+ # Iterates over the entries (files and subdirectories) in the directory. It
+ # yields a Pathname object for each entry.
+ #
+ # This method has existed since 1.8.1.
+ def each_entry(&block) # :yield: p
+ Dir.foreach(@path) {|f| yield self.class.new(f) }
+ end
+
+ # Pathname#dir_foreach is *obsoleted* at 1.8.1.
+ def dir_foreach(*args, &block)
+ warn "Pathname#dir_foreach is obsoleted. Use Pathname#each_entry."
+ each_entry(*args, &block)
+ end
+
+ # See <tt>Dir.mkdir</tt>. Create the referenced directory.
+ def mkdir(*args) Dir.mkdir(@path, *args) end
+
+ # See <tt>Dir.rmdir</tt>. Remove the referenced directory.
+ def rmdir() Dir.rmdir(@path) end
+
+ # See <tt>Dir.open</tt>.
+ def opendir(&block) # :yield: dir
+ Dir.open(@path, &block)
+ end
+end
+
+
+class Pathname # * Find *
+ #
+ # Pathname#find is an iterator to traverse a directory tree in a depth first
+ # manner. It yields a Pathname for each file under "this" directory.
+ #
+ # Since it is implemented by <tt>find.rb</tt>, <tt>Find.prune</tt> can be used
+ # to control the traverse.
+ #
+ # If +self+ is <tt>.</tt>, yielded pathnames begin with a filename in the
+ # current directory, not <tt>./</tt>.
+ #
+ def find(&block) # :yield: p
+ require 'find'
+ if @path == '.'
+ Find.find(@path) {|f| yield self.class.new(f.sub(%r{\A\./}, '')) }
+ else
+ Find.find(@path) {|f| yield self.class.new(f) }
+ end
+ end
+end
+
+
+class Pathname # * FileUtils *
+ # See <tt>FileUtils.mkpath</tt>. Creates a full path, including any
+ # intermediate directories that don't yet exist.
+ def mkpath
+ require 'fileutils'
+ FileUtils.mkpath(@path)
+ nil
+ end
+
+ # See <tt>FileUtils.rm_r</tt>. Deletes a directory and all beneath it.
+ def rmtree
+ # The name "rmtree" is borrowed from File::Path of Perl.
+ # File::Path provides "mkpath" and "rmtree".
+ require 'fileutils'
+ FileUtils.rm_r(@path)
+ nil
+ end
+end
+
+
+class Pathname # * mixed *
+ # Removes a file or directory, using <tt>File.unlink</tt> or
+ # <tt>Dir.unlink</tt> as necessary.
+ def unlink()
+ begin
+ Dir.unlink @path
+ rescue Errno::ENOTDIR
+ File.unlink @path
+ end
+ end
+ alias delete unlink
+
+ # This method is *obsoleted* at 1.8.1. Use #each_line or #each_entry.
+ def foreach(*args, &block)
+ warn "Pathname#foreach is obsoleted. Use each_line or each_entry."
+ if FileTest.directory? @path
+ # For polymorphism between Dir.foreach and IO.foreach,
+ # Pathname#foreach doesn't yield Pathname object.
+ Dir.foreach(@path, *args, &block)
+ else
+ IO.foreach(@path, *args, &block)
+ end
+ end
+end
+
+module Kernel
+ # create a pathname object.
+ #
+ # This method is available since 1.8.5.
+ def Pathname(path) # :doc:
+ Pathname.new(path)
+ end
+ private :Pathname
+end
diff --git a/ruby_1_8_5/lib/ping.rb b/ruby_1_8_5/lib/ping.rb
new file mode 100644
index 0000000000..7728ece979
--- /dev/null
+++ b/ruby_1_8_5/lib/ping.rb
@@ -0,0 +1,53 @@
+#
+# ping.rb -- check a host for upness
+#
+
+require 'timeout'
+require "socket"
+
+#= SYNOPSIS
+#
+# require 'ping'
+#
+# puts "'jimmy' is alive and kicking" if Ping.pingecho('jimmy', 10)
+#
+#= DESCRIPTION
+#
+# This module contains routines to test for the reachability of remote hosts.
+# Currently the only routine implemented is pingecho().
+#
+# pingecho() uses a TCP echo (_not_ an ICMP echo) to determine if the
+# remote host is reachable. This is usually adequate to tell that a remote
+# host is available to rsh(1), ftp(1), or telnet(1) to.
+#
+#= WARNING
+#
+# pingecho() may block for a long period if name resolution is slow. Require
+# 'resolv-replace' to use non-blocking name resolution.
+#
+module Ping
+
+ # return true if we can open a connection to the hostname or IP address
+ # +host+ on port +service+ (which defaults to the "echo" port) waiting up to
+ # +timeout+ seconds.
+ def pingecho(host, timeout=5, service="echo")
+ begin
+ timeout(timeout) do
+ s = TCPSocket.new(host, service)
+ s.close
+ end
+ rescue Errno::ECONNREFUSED
+ return true
+ rescue Timeout::Error, StandardError
+ return false
+ end
+ return true
+ end
+ module_function :pingecho
+end
+
+if $0 == __FILE__
+ host = ARGV[0]
+ host ||= "localhost"
+ printf("%s alive? - %s\n", host, Ping::pingecho(host, 5))
+end
diff --git a/ruby_1_8_5/lib/pp.rb b/ruby_1_8_5/lib/pp.rb
new file mode 100644
index 0000000000..8080d879f9
--- /dev/null
+++ b/ruby_1_8_5/lib/pp.rb
@@ -0,0 +1,647 @@
+# == Pretty-printer for Ruby objects.
+#
+# = Which seems better?
+#
+# non-pretty-printed output by #p is:
+# #<PP:0x81fedf0 @genspace=#<Proc:0x81feda0>, @group_queue=#<PrettyPrint::GroupQueue:0x81fed3c @queue=[[#<PrettyPrint::Group:0x81fed78 @breakables=[], @depth=0, @break=false>], []]>, @buffer=[], @newline="\n", @group_stack=[#<PrettyPrint::Group:0x81fed78 @breakables=[], @depth=0, @break=false>], @buffer_width=0, @indent=0, @maxwidth=79, @output_width=2, @output=#<IO:0x8114ee4>>
+#
+# pretty-printed output by #pp is:
+# #<PP:0x81fedf0
+# @buffer=[],
+# @buffer_width=0,
+# @genspace=#<Proc:0x81feda0>,
+# @group_queue=
+# #<PrettyPrint::GroupQueue:0x81fed3c
+# @queue=
+# [[#<PrettyPrint::Group:0x81fed78 @break=false, @breakables=[], @depth=0>],
+# []]>,
+# @group_stack=
+# [#<PrettyPrint::Group:0x81fed78 @break=false, @breakables=[], @depth=0>],
+# @indent=0,
+# @maxwidth=79,
+# @newline="\n",
+# @output=#<IO:0x8114ee4>,
+# @output_width=2>
+#
+# I like the latter. If you do too, this library is for you.
+#
+# = Usage
+#
+# pp(obj)
+#
+# output +obj+ to +$>+ in pretty printed format.
+#
+# It returns +nil+.
+#
+# = Output Customization
+# To define your customized pretty printing function for your classes,
+# redefine a method #pretty_print(+pp+) in the class.
+# It takes an argument +pp+ which is an instance of the class PP.
+# The method should use PP#text, PP#breakable, PP#nest, PP#group and
+# PP#pp to print the object.
+#
+# = Author
+# Tanaka Akira <akr@m17n.org>
+
+require 'prettyprint'
+
+module Kernel
+ # returns a pretty printed object as a string.
+ def pretty_inspect
+ PP.pp(self, '')
+ end
+
+ private
+ # prints arguments in pretty form.
+ #
+ # pp returns nil.
+ def pp(*objs) # :doc:
+ objs.each {|obj|
+ PP.pp(obj)
+ }
+ nil
+ end
+ module_function :pp
+end
+
+class PP < PrettyPrint
+ # Outputs +obj+ to +out+ in pretty printed format of
+ # +width+ columns in width.
+ #
+ # If +out+ is omitted, +$>+ is assumed.
+ # If +width+ is omitted, 79 is assumed.
+ #
+ # PP.pp returns +out+.
+ def PP.pp(obj, out=$>, width=79)
+ q = PP.new(out, width)
+ q.guard_inspect_key {q.pp obj}
+ q.flush
+ #$pp = q
+ out << "\n"
+ end
+
+ # Outputs +obj+ to +out+ like PP.pp but with no indent and
+ # newline.
+ #
+ # PP.singleline_pp returns +out+.
+ def PP.singleline_pp(obj, out=$>)
+ q = SingleLine.new(out)
+ q.guard_inspect_key {q.pp obj}
+ q.flush
+ out
+ end
+
+ # :stopdoc:
+ def PP.mcall(obj, mod, meth, *args, &block)
+ mod.instance_method(meth).bind(obj).call(*args, &block)
+ end
+ # :startdoc:
+
+ @sharing_detection = false
+ class << self
+ # Returns the sharing detection flag as a boolean value.
+ # It is false by default.
+ attr_accessor :sharing_detection
+ end
+
+ module PPMethods
+ InspectKey = :__inspect_key__
+
+ def guard_inspect_key
+ if Thread.current[InspectKey] == nil
+ Thread.current[InspectKey] = []
+ end
+
+ save = Thread.current[InspectKey]
+
+ begin
+ Thread.current[InspectKey] = []
+ yield
+ ensure
+ Thread.current[InspectKey] = save
+ end
+ end
+
+ # Adds +obj+ to the pretty printing buffer
+ # using Object#pretty_print or Object#pretty_print_cycle.
+ #
+ # Object#pretty_print_cycle is used when +obj+ is already
+ # printed, a.k.a the object reference chain has a cycle.
+ def pp(obj)
+ id = obj.__id__
+
+ if Thread.current[InspectKey].include? id
+ group {obj.pretty_print_cycle self}
+ return
+ end
+
+ begin
+ Thread.current[InspectKey] << id
+ group {obj.pretty_print self}
+ ensure
+ Thread.current[InspectKey].pop unless PP.sharing_detection
+ end
+ end
+
+ # A convenience method which is same as follows:
+ #
+ # group(1, '#<' + obj.class.name, '>') { ... }
+ def object_group(obj, &block) # :yield:
+ group(1, '#<' + obj.class.name, '>', &block)
+ end
+
+ def object_address_group(obj, &block)
+ id = "%x" % (obj.__id__ * 2)
+ id.sub!(/\Af(?=[[:xdigit:]]{2}+\z)/, '') if id.sub!(/\A\.\./, '')
+ group(1, "\#<#{obj.class}:0x#{id}", '>', &block)
+ end
+
+ # A convenience method which is same as follows:
+ #
+ # text ','
+ # breakable
+ def comma_breakable
+ text ','
+ breakable
+ end
+
+ # Adds a separated list.
+ # The list is separated by comma with breakable space, by default.
+ #
+ # #seplist iterates the +list+ using +iter_method+.
+ # It yields each object to the block given for #seplist.
+ # The procedure +separator_proc+ is called between each yields.
+ #
+ # If the iteration is zero times, +separator_proc+ is not called at all.
+ #
+ # If +separator_proc+ is nil or not given,
+ # +lambda { comma_breakable }+ is used.
+ # If +iter_method+ is not given, :each is used.
+ #
+ # For example, following 3 code fragments has similar effect.
+ #
+ # q.seplist([1,2,3]) {|v| xxx v }
+ #
+ # q.seplist([1,2,3], lambda { comma_breakable }, :each) {|v| xxx v }
+ #
+ # xxx 1
+ # q.comma_breakable
+ # xxx 2
+ # q.comma_breakable
+ # xxx 3
+ def seplist(list, sep=nil, iter_method=:each) # :yield: element
+ sep ||= lambda { comma_breakable }
+ first = true
+ list.__send__(iter_method) {|*v|
+ if first
+ first = false
+ else
+ sep.call
+ end
+ yield(*v)
+ }
+ end
+
+ def pp_object(obj)
+ object_address_group(obj) {
+ seplist(obj.pretty_print_instance_variables, lambda { text ',' }) {|v|
+ breakable
+ v = v.to_s if Symbol === v
+ text v
+ text '='
+ group(1) {
+ breakable ''
+ pp(obj.instance_eval(v))
+ }
+ }
+ }
+ end
+
+ def pp_hash(obj)
+ group(1, '{', '}') {
+ seplist(obj, nil, :each_pair) {|k, v|
+ group {
+ pp k
+ text '=>'
+ group(1) {
+ breakable ''
+ pp v
+ }
+ }
+ }
+ }
+ end
+ end
+
+ include PPMethods
+
+ class SingleLine < PrettyPrint::SingleLine
+ include PPMethods
+ end
+
+ module ObjectMixin
+ # 1. specific pretty_print
+ # 2. specific inspect
+ # 3. specific to_s if instance variable is empty
+ # 4. generic pretty_print
+
+ # A default pretty printing method for general objects.
+ # It calls #pretty_print_instance_variables to list instance variables.
+ #
+ # If +self+ has a customized (redefined) #inspect method,
+ # the result of self.inspect is used but it obviously has no
+ # line break hints.
+ #
+ # This module provides predefined #pretty_print methods for some of
+ # the most commonly used built-in classes for convenience.
+ def pretty_print(q)
+ if /\(Kernel\)#/ !~ method(:inspect).inspect
+ q.text self.inspect
+ elsif /\(Kernel\)#/ !~ method(:to_s).inspect && instance_variables.empty?
+ q.text self.to_s
+ else
+ q.pp_object(self)
+ end
+ end
+
+ # A default pretty printing method for general objects that are
+ # detected as part of a cycle.
+ def pretty_print_cycle(q)
+ q.object_address_group(self) {
+ q.breakable
+ q.text '...'
+ }
+ end
+
+ # Returns a sorted array of instance variable names.
+ #
+ # This method should return an array of names of instance variables as symbols or strings as:
+ # +[:@a, :@b]+.
+ def pretty_print_instance_variables
+ instance_variables.sort
+ end
+
+ # Is #inspect implementation using #pretty_print.
+ # If you implement #pretty_print, it can be used as follows.
+ #
+ # alias inspect pretty_print_inspect
+ #
+ # However, doing this requires that every class that #inspect is called on
+ # implement #pretty_print, or a RuntimeError will be raised.
+ def pretty_print_inspect
+ if /\(PP::ObjectMixin\)#/ =~ method(:pretty_print).inspect
+ raise "pretty_print is not overridden for #{self.class}"
+ end
+ PP.singleline_pp(self, '')
+ end
+ end
+end
+
+class Array
+ def pretty_print(q)
+ q.group(1, '[', ']') {
+ q.seplist(self) {|v|
+ q.pp v
+ }
+ }
+ end
+
+ def pretty_print_cycle(q)
+ q.text(empty? ? '[]' : '[...]')
+ end
+end
+
+class Hash
+ def pretty_print(q)
+ q.pp_hash self
+ end
+
+ def pretty_print_cycle(q)
+ q.text(empty? ? '{}' : '{...}')
+ end
+end
+
+class << ENV
+ def pretty_print(q)
+ q.pp_hash self
+ end
+end
+
+class Struct
+ def pretty_print(q)
+ q.group(1, '#<struct ' + PP.mcall(self, Kernel, :class).name, '>') {
+ q.seplist(PP.mcall(self, Struct, :members), lambda { q.text "," }) {|member|
+ q.breakable
+ q.text member.to_s
+ q.text '='
+ q.group(1) {
+ q.breakable ''
+ q.pp self[member]
+ }
+ }
+ }
+ end
+
+ def pretty_print_cycle(q)
+ q.text sprintf("#<struct %s:...>", PP.mcall(self, Kernel, :class).name)
+ end
+end
+
+class Range
+ def pretty_print(q)
+ q.pp self.begin
+ q.breakable ''
+ q.text(self.exclude_end? ? '...' : '..')
+ q.breakable ''
+ q.pp self.end
+ end
+end
+
+class File
+ class Stat
+ def pretty_print(q)
+ require 'etc.so'
+ q.object_group(self) {
+ q.breakable
+ q.text sprintf("dev=0x%x", self.dev); q.comma_breakable
+ q.text "ino="; q.pp self.ino; q.comma_breakable
+ q.group {
+ m = self.mode
+ q.text sprintf("mode=0%o", m)
+ q.breakable
+ q.text sprintf("(%s %c%c%c%c%c%c%c%c%c)",
+ self.ftype,
+ (m & 0400 == 0 ? ?- : ?r),
+ (m & 0200 == 0 ? ?- : ?w),
+ (m & 0100 == 0 ? (m & 04000 == 0 ? ?- : ?S) :
+ (m & 04000 == 0 ? ?x : ?s)),
+ (m & 0040 == 0 ? ?- : ?r),
+ (m & 0020 == 0 ? ?- : ?w),
+ (m & 0010 == 0 ? (m & 02000 == 0 ? ?- : ?S) :
+ (m & 02000 == 0 ? ?x : ?s)),
+ (m & 0004 == 0 ? ?- : ?r),
+ (m & 0002 == 0 ? ?- : ?w),
+ (m & 0001 == 0 ? (m & 01000 == 0 ? ?- : ?T) :
+ (m & 01000 == 0 ? ?x : ?t)))
+ }
+ q.comma_breakable
+ q.text "nlink="; q.pp self.nlink; q.comma_breakable
+ q.group {
+ q.text "uid="; q.pp self.uid
+ begin
+ pw = Etc.getpwuid(self.uid)
+ rescue ArgumentError
+ end
+ if pw
+ q.breakable; q.text "(#{pw.name})"
+ end
+ }
+ q.comma_breakable
+ q.group {
+ q.text "gid="; q.pp self.gid
+ begin
+ gr = Etc.getgrgid(self.gid)
+ rescue ArgumentError
+ end
+ if gr
+ q.breakable; q.text "(#{gr.name})"
+ end
+ }
+ q.comma_breakable
+ q.group {
+ q.text sprintf("rdev=0x%x", self.rdev)
+ q.breakable
+ q.text sprintf('(%d, %d)', self.rdev_major, self.rdev_minor)
+ }
+ q.comma_breakable
+ q.text "size="; q.pp self.size; q.comma_breakable
+ q.text "blksize="; q.pp self.blksize; q.comma_breakable
+ q.text "blocks="; q.pp self.blocks; q.comma_breakable
+ q.group {
+ t = self.atime
+ q.text "atime="; q.pp t
+ q.breakable; q.text "(#{t.tv_sec})"
+ }
+ q.comma_breakable
+ q.group {
+ t = self.mtime
+ q.text "mtime="; q.pp t
+ q.breakable; q.text "(#{t.tv_sec})"
+ }
+ q.comma_breakable
+ q.group {
+ t = self.ctime
+ q.text "ctime="; q.pp t
+ q.breakable; q.text "(#{t.tv_sec})"
+ }
+ }
+ end
+ end
+end
+
+class MatchData
+ def pretty_print(q)
+ q.object_group(self) {
+ q.breakable
+ q.seplist(1..self.size, lambda { q.breakable }) {|i|
+ q.pp self[i-1]
+ }
+ }
+ end
+end
+
+class Object
+ include PP::ObjectMixin
+end
+
+[Numeric, Symbol, FalseClass, TrueClass, NilClass, Module].each {|c|
+ c.class_eval {
+ def pretty_print_cycle(q)
+ q.text inspect
+ end
+ }
+}
+
+[Numeric, FalseClass, TrueClass, Module].each {|c|
+ c.class_eval {
+ def pretty_print(q)
+ q.text inspect
+ end
+ }
+}
+
+# :enddoc:
+if __FILE__ == $0
+ require 'test/unit'
+
+ class PPTest < Test::Unit::TestCase
+ def test_list0123_12
+ assert_equal("[0, 1, 2, 3]\n", PP.pp([0,1,2,3], '', 12))
+ end
+
+ def test_list0123_11
+ assert_equal("[0,\n 1,\n 2,\n 3]\n", PP.pp([0,1,2,3], '', 11))
+ end
+
+ OverriddenStruct = Struct.new("OverriddenStruct", :members, :class)
+ def test_struct_override_members # [ruby-core:7865]
+ a = OverriddenStruct.new(1,2)
+ assert_equal("#<struct Struct::OverriddenStruct members=1, class=2>\n", PP.pp(a, ''))
+ end
+ end
+
+ class HasInspect
+ def initialize(a)
+ @a = a
+ end
+
+ def inspect
+ return "<inspect:#{@a.inspect}>"
+ end
+ end
+
+ class HasPrettyPrint
+ def initialize(a)
+ @a = a
+ end
+
+ def pretty_print(q)
+ q.text "<pretty_print:"
+ q.pp @a
+ q.text ">"
+ end
+ end
+
+ class HasBoth
+ def initialize(a)
+ @a = a
+ end
+
+ def inspect
+ return "<inspect:#{@a.inspect}>"
+ end
+
+ def pretty_print(q)
+ q.text "<pretty_print:"
+ q.pp @a
+ q.text ">"
+ end
+ end
+
+ class PrettyPrintInspect < HasPrettyPrint
+ alias inspect pretty_print_inspect
+ end
+
+ class PrettyPrintInspectWithoutPrettyPrint
+ alias inspect pretty_print_inspect
+ end
+
+ class PPInspectTest < Test::Unit::TestCase
+ def test_hasinspect
+ a = HasInspect.new(1)
+ assert_equal("<inspect:1>\n", PP.pp(a, ''))
+ end
+
+ def test_hasprettyprint
+ a = HasPrettyPrint.new(1)
+ assert_equal("<pretty_print:1>\n", PP.pp(a, ''))
+ end
+
+ def test_hasboth
+ a = HasBoth.new(1)
+ assert_equal("<pretty_print:1>\n", PP.pp(a, ''))
+ end
+
+ def test_pretty_print_inspect
+ a = PrettyPrintInspect.new(1)
+ assert_equal("<pretty_print:1>", a.inspect)
+ a = PrettyPrintInspectWithoutPrettyPrint.new
+ assert_raise(RuntimeError) { a.inspect }
+ end
+
+ def test_proc
+ a = proc {1}
+ assert_equal("#{a.inspect}\n", PP.pp(a, ''))
+ end
+
+ def test_to_s_with_iv
+ a = Object.new
+ def a.to_s() "aaa" end
+ a.instance_eval { @a = nil }
+ result = PP.pp(a, '')
+ assert_equal("#{a.inspect}\n", result)
+ assert_match(/\A#<Object.*>\n\z/m, result)
+ a = 1.0
+ a.instance_eval { @a = nil }
+ result = PP.pp(a, '')
+ assert_equal("#{a.inspect}\n", result)
+ end
+
+ def test_to_s_without_iv
+ a = Object.new
+ def a.to_s() "aaa" end
+ result = PP.pp(a, '')
+ assert_equal("#{a.inspect}\n", result)
+ assert_equal("aaa\n", result)
+ end
+ end
+
+ class PPCycleTest < Test::Unit::TestCase
+ def test_array
+ a = []
+ a << a
+ assert_equal("[[...]]\n", PP.pp(a, ''))
+ assert_equal("#{a.inspect}\n", PP.pp(a, ''))
+ end
+
+ def test_hash
+ a = {}
+ a[0] = a
+ assert_equal("{0=>{...}}\n", PP.pp(a, ''))
+ assert_equal("#{a.inspect}\n", PP.pp(a, ''))
+ end
+
+ S = Struct.new("S", :a, :b)
+ def test_struct
+ a = S.new(1,2)
+ a.b = a
+ assert_equal("#<struct Struct::S a=1, b=#<struct Struct::S:...>>\n", PP.pp(a, ''))
+ assert_equal("#{a.inspect}\n", PP.pp(a, ''))
+ end
+
+ def test_object
+ a = Object.new
+ a.instance_eval {@a = a}
+ assert_equal(a.inspect + "\n", PP.pp(a, ''))
+ end
+
+ def test_anonymous
+ a = Class.new.new
+ assert_equal(a.inspect + "\n", PP.pp(a, ''))
+ end
+
+ def test_withinspect
+ a = []
+ a << HasInspect.new(a)
+ assert_equal("[<inspect:[...]>]\n", PP.pp(a, ''))
+ assert_equal("#{a.inspect}\n", PP.pp(a, ''))
+ end
+
+ def test_share_nil
+ begin
+ PP.sharing_detection = true
+ a = [nil, nil]
+ assert_equal("[nil, nil]\n", PP.pp(a, ''))
+ ensure
+ PP.sharing_detection = false
+ end
+ end
+ end
+
+ class PPSingleLineTest < Test::Unit::TestCase
+ def test_hash
+ assert_equal("{1=>1}", PP.singleline_pp({ 1 => 1}, '')) # [ruby-core:02699]
+ assert_equal("[1#{', 1'*99}]", PP.singleline_pp([1]*100, ''))
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/prettyprint.rb b/ruby_1_8_5/lib/prettyprint.rb
new file mode 100644
index 0000000000..507a05e65c
--- /dev/null
+++ b/ruby_1_8_5/lib/prettyprint.rb
@@ -0,0 +1,896 @@
+# $Id$
+
+# This class implements a pretty printing algorithm. It finds line breaks and
+# nice indentations for grouped structure.
+#
+# By default, the class assumes that primitive elements are strings and each
+# byte in the strings have single column in width. But it can be used for
+# other situations by giving suitable arguments for some methods:
+# * newline object and space generation block for PrettyPrint.new
+# * optional width argument for PrettyPrint#text
+# * PrettyPrint#breakable
+#
+# There are several candidate uses:
+# * text formatting using proportional fonts
+# * multibyte characters which has columns different to number of bytes
+# * non-string formatting
+#
+# == Bugs
+# * Box based formatting?
+# * Other (better) model/algorithm?
+#
+# == References
+# Christian Lindig, Strictly Pretty, March 2000,
+# http://www.st.cs.uni-sb.de/~lindig/papers/#pretty
+#
+# Philip Wadler, A prettier printer, March 1998,
+# http://homepages.inf.ed.ac.uk/wadler/topics/language-design.html#prettier
+#
+# == Author
+# Tanaka Akira <akr@m17n.org>
+#
+class PrettyPrint
+
+ # This is a convenience method which is same as follows:
+ #
+ # begin
+ # q = PrettyPrint.new(output, maxwidth, newline, &genspace)
+ # ...
+ # q.flush
+ # output
+ # end
+ #
+ def PrettyPrint.format(output='', maxwidth=79, newline="\n", genspace=lambda {|n| ' ' * n})
+ q = PrettyPrint.new(output, maxwidth, newline, &genspace)
+ yield q
+ q.flush
+ output
+ end
+
+ # This is similar to PrettyPrint::format but the result has no breaks.
+ #
+ # +maxwidth+, +newline+ and +genspace+ are ignored.
+ #
+ # The invocation of +breakable+ in the block doesn't break a line and is
+ # treated as just an invocation of +text+.
+ #
+ def PrettyPrint.singleline_format(output='', maxwidth=nil, newline=nil, genspace=nil)
+ q = SingleLine.new(output)
+ yield q
+ output
+ end
+
+ # Creates a buffer for pretty printing.
+ #
+ # +output+ is an output target. If it is not specified, '' is assumed. It
+ # should have a << method which accepts the first argument +obj+ of
+ # PrettyPrint#text, the first argument +sep+ of PrettyPrint#breakable, the
+ # first argument +newline+ of PrettyPrint.new, and the result of a given
+ # block for PrettyPrint.new.
+ #
+ # +maxwidth+ specifies maximum line length. If it is not specified, 79 is
+ # assumed. However actual outputs may overflow +maxwidth+ if long
+ # non-breakable texts are provided.
+ #
+ # +newline+ is used for line breaks. "\n" is used if it is not specified.
+ #
+ # The block is used to generate spaces. {|width| ' ' * width} is used if it
+ # is not given.
+ #
+ def initialize(output='', maxwidth=79, newline="\n", &genspace)
+ @output = output
+ @maxwidth = maxwidth
+ @newline = newline
+ @genspace = genspace || lambda {|n| ' ' * n}
+
+ @output_width = 0
+ @buffer_width = 0
+ @buffer = []
+
+ root_group = Group.new(0)
+ @group_stack = [root_group]
+ @group_queue = GroupQueue.new(root_group)
+ @indent = 0
+ end
+ attr_reader :output, :maxwidth, :newline, :genspace
+ attr_reader :indent, :group_queue
+
+ def current_group
+ @group_stack.last
+ end
+
+ # first? is a predicate to test the call is a first call to first? with
+ # current group.
+ #
+ # It is useful to format comma separated values as:
+ #
+ # q.group(1, '[', ']') {
+ # xxx.each {|yyy|
+ # unless q.first?
+ # q.text ','
+ # q.breakable
+ # end
+ # ... pretty printing yyy ...
+ # }
+ # }
+ #
+ # first? is obsoleted in 1.8.2.
+ #
+ def first?
+ warn "PrettyPrint#first? is obsoleted at 1.8.2."
+ current_group.first?
+ end
+
+ def break_outmost_groups
+ while @maxwidth < @output_width + @buffer_width
+ return unless group = @group_queue.deq
+ until group.breakables.empty?
+ data = @buffer.shift
+ @output_width = data.output(@output, @output_width)
+ @buffer_width -= data.width
+ end
+ while !@buffer.empty? && Text === @buffer.first
+ text = @buffer.shift
+ @output_width = text.output(@output, @output_width)
+ @buffer_width -= text.width
+ end
+ end
+ end
+
+ # This adds +obj+ as a text of +width+ columns in width.
+ #
+ # If +width+ is not specified, obj.length is used.
+ #
+ def text(obj, width=obj.length)
+ if @buffer.empty?
+ @output << obj
+ @output_width += width
+ else
+ text = @buffer.last
+ unless Text === text
+ text = Text.new
+ @buffer << text
+ end
+ text.add(obj, width)
+ @buffer_width += width
+ break_outmost_groups
+ end
+ end
+
+ def fill_breakable(sep=' ', width=sep.length)
+ group { breakable sep, width }
+ end
+
+ # This tells "you can break a line here if necessary", and a +width+\-column
+ # text +sep+ is inserted if a line is not broken at the point.
+ #
+ # If +sep+ is not specified, " " is used.
+ #
+ # If +width+ is not specified, +sep.length+ is used. You will have to
+ # specify this when +sep+ is a multibyte character, for example.
+ #
+ def breakable(sep=' ', width=sep.length)
+ group = @group_stack.last
+ if group.break?
+ flush
+ @output << @newline
+ @output << @genspace.call(@indent)
+ @output_width = @indent
+ @buffer_width = 0
+ else
+ @buffer << Breakable.new(sep, width, self)
+ @buffer_width += width
+ break_outmost_groups
+ end
+ end
+
+ # Groups line break hints added in the block. The line break hints are all
+ # to be used or not.
+ #
+ # If +indent+ is specified, the method call is regarded as nested by
+ # nest(indent) { ... }.
+ #
+ # If +open_obj+ is specified, <tt>text open_obj, open_width</tt> is called
+ # before grouping. If +close_obj+ is specified, <tt>text close_obj,
+ # close_width</tt> is called after grouping.
+ #
+ def group(indent=0, open_obj='', close_obj='', open_width=open_obj.length, close_width=close_obj.length)
+ text open_obj, open_width
+ group_sub {
+ nest(indent) {
+ yield
+ }
+ }
+ text close_obj, close_width
+ end
+
+ def group_sub
+ group = Group.new(@group_stack.last.depth + 1)
+ @group_stack.push group
+ @group_queue.enq group
+ begin
+ yield
+ ensure
+ @group_stack.pop
+ if group.breakables.empty?
+ @group_queue.delete group
+ end
+ end
+ end
+
+ # Increases left margin after newline with +indent+ for line breaks added in
+ # the block.
+ #
+ def nest(indent)
+ @indent += indent
+ begin
+ yield
+ ensure
+ @indent -= indent
+ end
+ end
+
+ # outputs buffered data.
+ #
+ def flush
+ @buffer.each {|data|
+ @output_width = data.output(@output, @output_width)
+ }
+ @buffer.clear
+ @buffer_width = 0
+ end
+
+ class Text
+ def initialize
+ @objs = []
+ @width = 0
+ end
+ attr_reader :width
+
+ def output(out, output_width)
+ @objs.each {|obj| out << obj}
+ output_width + @width
+ end
+
+ def add(obj, width)
+ @objs << obj
+ @width += width
+ end
+ end
+
+ class Breakable
+ def initialize(sep, width, q)
+ @obj = sep
+ @width = width
+ @pp = q
+ @indent = q.indent
+ @group = q.current_group
+ @group.breakables.push self
+ end
+ attr_reader :obj, :width, :indent
+
+ def output(out, output_width)
+ @group.breakables.shift
+ if @group.break?
+ out << @pp.newline
+ out << @pp.genspace.call(@indent)
+ @indent
+ else
+ @pp.group_queue.delete @group if @group.breakables.empty?
+ out << @obj
+ output_width + @width
+ end
+ end
+ end
+
+ class Group
+ def initialize(depth)
+ @depth = depth
+ @breakables = []
+ @break = false
+ end
+ attr_reader :depth, :breakables
+
+ def break
+ @break = true
+ end
+
+ def break?
+ @break
+ end
+
+ def first?
+ if defined? @first
+ false
+ else
+ @first = false
+ true
+ end
+ end
+ end
+
+ class GroupQueue
+ def initialize(*groups)
+ @queue = []
+ groups.each {|g| enq g}
+ end
+
+ def enq(group)
+ depth = group.depth
+ @queue << [] until depth < @queue.length
+ @queue[depth] << group
+ end
+
+ def deq
+ @queue.each {|gs|
+ (gs.length-1).downto(0) {|i|
+ unless gs[i].breakables.empty?
+ group = gs.slice!(i, 1).first
+ group.break
+ return group
+ end
+ }
+ gs.each {|group| group.break}
+ gs.clear
+ }
+ return nil
+ end
+
+ def delete(group)
+ @queue[group.depth].delete(group)
+ end
+ end
+
+ class SingleLine
+ def initialize(output, maxwidth=nil, newline=nil)
+ @output = output
+ @first = [true]
+ end
+
+ def text(obj, width=nil)
+ @output << obj
+ end
+
+ def breakable(sep=' ', width=nil)
+ @output << sep
+ end
+
+ def nest(indent)
+ yield
+ end
+
+ def group(indent=nil, open_obj='', close_obj='', open_width=nil, close_width=nil)
+ @first.push true
+ @output << open_obj
+ yield
+ @output << close_obj
+ @first.pop
+ end
+
+ def flush
+ end
+
+ def first?
+ result = @first[-1]
+ @first[-1] = false
+ result
+ end
+ end
+end
+
+if __FILE__ == $0
+ require 'test/unit'
+
+ class WadlerExample < Test::Unit::TestCase
+ def setup
+ @tree = Tree.new("aaaa", Tree.new("bbbbb", Tree.new("ccc"),
+ Tree.new("dd")),
+ Tree.new("eee"),
+ Tree.new("ffff", Tree.new("gg"),
+ Tree.new("hhh"),
+ Tree.new("ii")))
+ end
+
+ def hello(width)
+ PrettyPrint.format('', width) {|hello|
+ hello.group {
+ hello.group {
+ hello.group {
+ hello.group {
+ hello.text 'hello'
+ hello.breakable; hello.text 'a'
+ }
+ hello.breakable; hello.text 'b'
+ }
+ hello.breakable; hello.text 'c'
+ }
+ hello.breakable; hello.text 'd'
+ }
+ }
+ end
+
+ def test_hello_00_06
+ expected = <<'End'.chomp
+hello
+a
+b
+c
+d
+End
+ assert_equal(expected, hello(0))
+ assert_equal(expected, hello(6))
+ end
+
+ def test_hello_07_08
+ expected = <<'End'.chomp
+hello a
+b
+c
+d
+End
+ assert_equal(expected, hello(7))
+ assert_equal(expected, hello(8))
+ end
+
+ def test_hello_09_10
+ expected = <<'End'.chomp
+hello a b
+c
+d
+End
+ out = hello(9); assert_equal(expected, out)
+ out = hello(10); assert_equal(expected, out)
+ end
+
+ def test_hello_11_12
+ expected = <<'End'.chomp
+hello a b c
+d
+End
+ assert_equal(expected, hello(11))
+ assert_equal(expected, hello(12))
+ end
+
+ def test_hello_13
+ expected = <<'End'.chomp
+hello a b c d
+End
+ assert_equal(expected, hello(13))
+ end
+
+ def tree(width)
+ PrettyPrint.format('', width) {|q| @tree.show(q)}
+ end
+
+ def test_tree_00_19
+ expected = <<'End'.chomp
+aaaa[bbbbb[ccc,
+ dd],
+ eee,
+ ffff[gg,
+ hhh,
+ ii]]
+End
+ assert_equal(expected, tree(0))
+ assert_equal(expected, tree(19))
+ end
+
+ def test_tree_20_22
+ expected = <<'End'.chomp
+aaaa[bbbbb[ccc, dd],
+ eee,
+ ffff[gg,
+ hhh,
+ ii]]
+End
+ assert_equal(expected, tree(20))
+ assert_equal(expected, tree(22))
+ end
+
+ def test_tree_23_43
+ expected = <<'End'.chomp
+aaaa[bbbbb[ccc, dd],
+ eee,
+ ffff[gg, hhh, ii]]
+End
+ assert_equal(expected, tree(23))
+ assert_equal(expected, tree(43))
+ end
+
+ def test_tree_44
+ assert_equal(<<'End'.chomp, tree(44))
+aaaa[bbbbb[ccc, dd], eee, ffff[gg, hhh, ii]]
+End
+ end
+
+ def tree_alt(width)
+ PrettyPrint.format('', width) {|q| @tree.altshow(q)}
+ end
+
+ def test_tree_alt_00_18
+ expected = <<'End'.chomp
+aaaa[
+ bbbbb[
+ ccc,
+ dd
+ ],
+ eee,
+ ffff[
+ gg,
+ hhh,
+ ii
+ ]
+]
+End
+ assert_equal(expected, tree_alt(0))
+ assert_equal(expected, tree_alt(18))
+ end
+
+ def test_tree_alt_19_20
+ expected = <<'End'.chomp
+aaaa[
+ bbbbb[ ccc, dd ],
+ eee,
+ ffff[
+ gg,
+ hhh,
+ ii
+ ]
+]
+End
+ assert_equal(expected, tree_alt(19))
+ assert_equal(expected, tree_alt(20))
+ end
+
+ def test_tree_alt_20_49
+ expected = <<'End'.chomp
+aaaa[
+ bbbbb[ ccc, dd ],
+ eee,
+ ffff[ gg, hhh, ii ]
+]
+End
+ assert_equal(expected, tree_alt(21))
+ assert_equal(expected, tree_alt(49))
+ end
+
+ def test_tree_alt_50
+ expected = <<'End'.chomp
+aaaa[ bbbbb[ ccc, dd ], eee, ffff[ gg, hhh, ii ] ]
+End
+ assert_equal(expected, tree_alt(50))
+ end
+
+ class Tree
+ def initialize(string, *children)
+ @string = string
+ @children = children
+ end
+
+ def show(q)
+ q.group {
+ q.text @string
+ q.nest(@string.length) {
+ unless @children.empty?
+ q.text '['
+ q.nest(1) {
+ first = true
+ @children.each {|t|
+ if first
+ first = false
+ else
+ q.text ','
+ q.breakable
+ end
+ t.show(q)
+ }
+ }
+ q.text ']'
+ end
+ }
+ }
+ end
+
+ def altshow(q)
+ q.group {
+ q.text @string
+ unless @children.empty?
+ q.text '['
+ q.nest(2) {
+ q.breakable
+ first = true
+ @children.each {|t|
+ if first
+ first = false
+ else
+ q.text ','
+ q.breakable
+ end
+ t.altshow(q)
+ }
+ }
+ q.breakable
+ q.text ']'
+ end
+ }
+ end
+
+ end
+ end
+
+ class StrictPrettyExample < Test::Unit::TestCase
+ def prog(width)
+ PrettyPrint.format('', width) {|q|
+ q.group {
+ q.group {q.nest(2) {
+ q.text "if"; q.breakable;
+ q.group {
+ q.nest(2) {
+ q.group {q.text "a"; q.breakable; q.text "=="}
+ q.breakable; q.text "b"}}}}
+ q.breakable
+ q.group {q.nest(2) {
+ q.text "then"; q.breakable;
+ q.group {
+ q.nest(2) {
+ q.group {q.text "a"; q.breakable; q.text "<<"}
+ q.breakable; q.text "2"}}}}
+ q.breakable
+ q.group {q.nest(2) {
+ q.text "else"; q.breakable;
+ q.group {
+ q.nest(2) {
+ q.group {q.text "a"; q.breakable; q.text "+"}
+ q.breakable; q.text "b"}}}}}
+ }
+ end
+
+ def test_00_04
+ expected = <<'End'.chomp
+if
+ a
+ ==
+ b
+then
+ a
+ <<
+ 2
+else
+ a
+ +
+ b
+End
+ assert_equal(expected, prog(0))
+ assert_equal(expected, prog(4))
+ end
+
+ def test_05
+ expected = <<'End'.chomp
+if
+ a
+ ==
+ b
+then
+ a
+ <<
+ 2
+else
+ a +
+ b
+End
+ assert_equal(expected, prog(5))
+ end
+
+ def test_06
+ expected = <<'End'.chomp
+if
+ a ==
+ b
+then
+ a <<
+ 2
+else
+ a +
+ b
+End
+ assert_equal(expected, prog(6))
+ end
+
+ def test_07
+ expected = <<'End'.chomp
+if
+ a ==
+ b
+then
+ a <<
+ 2
+else
+ a + b
+End
+ assert_equal(expected, prog(7))
+ end
+
+ def test_08
+ expected = <<'End'.chomp
+if
+ a == b
+then
+ a << 2
+else
+ a + b
+End
+ assert_equal(expected, prog(8))
+ end
+
+ def test_09
+ expected = <<'End'.chomp
+if a == b
+then
+ a << 2
+else
+ a + b
+End
+ assert_equal(expected, prog(9))
+ end
+
+ def test_10
+ expected = <<'End'.chomp
+if a == b
+then
+ a << 2
+else a + b
+End
+ assert_equal(expected, prog(10))
+ end
+
+ def test_11_31
+ expected = <<'End'.chomp
+if a == b
+then a << 2
+else a + b
+End
+ assert_equal(expected, prog(11))
+ assert_equal(expected, prog(15))
+ assert_equal(expected, prog(31))
+ end
+
+ def test_32
+ expected = <<'End'.chomp
+if a == b then a << 2 else a + b
+End
+ assert_equal(expected, prog(32))
+ end
+
+ end
+
+ class TailGroup < Test::Unit::TestCase
+ def test_1
+ out = PrettyPrint.format('', 10) {|q|
+ q.group {
+ q.group {
+ q.text "abc"
+ q.breakable
+ q.text "def"
+ }
+ q.group {
+ q.text "ghi"
+ q.breakable
+ q.text "jkl"
+ }
+ }
+ }
+ assert_equal("abc defghi\njkl", out)
+ end
+ end
+
+ class NonString < Test::Unit::TestCase
+ def format(width)
+ PrettyPrint.format([], width, 'newline', lambda {|n| "#{n} spaces"}) {|q|
+ q.text(3, 3)
+ q.breakable(1, 1)
+ q.text(3, 3)
+ }
+ end
+
+ def test_6
+ assert_equal([3, "newline", "0 spaces", 3], format(6))
+ end
+
+ def test_7
+ assert_equal([3, 1, 3], format(7))
+ end
+
+ end
+
+ class Fill < Test::Unit::TestCase
+ def format(width)
+ PrettyPrint.format('', width) {|q|
+ q.group {
+ q.text 'abc'
+ q.fill_breakable
+ q.text 'def'
+ q.fill_breakable
+ q.text 'ghi'
+ q.fill_breakable
+ q.text 'jkl'
+ q.fill_breakable
+ q.text 'mno'
+ q.fill_breakable
+ q.text 'pqr'
+ q.fill_breakable
+ q.text 'stu'
+ }
+ }
+ end
+
+ def test_00_06
+ expected = <<'End'.chomp
+abc
+def
+ghi
+jkl
+mno
+pqr
+stu
+End
+ assert_equal(expected, format(0))
+ assert_equal(expected, format(6))
+ end
+
+ def test_07_10
+ expected = <<'End'.chomp
+abc def
+ghi jkl
+mno pqr
+stu
+End
+ assert_equal(expected, format(7))
+ assert_equal(expected, format(10))
+ end
+
+ def test_11_14
+ expected = <<'End'.chomp
+abc def ghi
+jkl mno pqr
+stu
+End
+ assert_equal(expected, format(11))
+ assert_equal(expected, format(14))
+ end
+
+ def test_15_18
+ expected = <<'End'.chomp
+abc def ghi jkl
+mno pqr stu
+End
+ assert_equal(expected, format(15))
+ assert_equal(expected, format(18))
+ end
+
+ def test_19_22
+ expected = <<'End'.chomp
+abc def ghi jkl mno
+pqr stu
+End
+ assert_equal(expected, format(19))
+ assert_equal(expected, format(22))
+ end
+
+ def test_23_26
+ expected = <<'End'.chomp
+abc def ghi jkl mno pqr
+stu
+End
+ assert_equal(expected, format(23))
+ assert_equal(expected, format(26))
+ end
+
+ def test_27
+ expected = <<'End'.chomp
+abc def ghi jkl mno pqr stu
+End
+ assert_equal(expected, format(27))
+ end
+
+ end
+end
diff --git a/ruby_1_8_5/lib/profile.rb b/ruby_1_8_5/lib/profile.rb
new file mode 100644
index 0000000000..104cb205b9
--- /dev/null
+++ b/ruby_1_8_5/lib/profile.rb
@@ -0,0 +1,6 @@
+require 'profiler'
+
+END {
+ Profiler__::print_profile(STDERR)
+}
+Profiler__::start_profile
diff --git a/ruby_1_8_5/lib/profiler.rb b/ruby_1_8_5/lib/profiler.rb
new file mode 100644
index 0000000000..9762fa1181
--- /dev/null
+++ b/ruby_1_8_5/lib/profiler.rb
@@ -0,0 +1,59 @@
+module Profiler__
+ # internal values
+ @@start = @@stack = @@map = nil
+ PROFILE_PROC = proc{|event, file, line, id, binding, klass|
+ case event
+ when "call", "c-call"
+ now = Process.times[0]
+ @@stack.push [now, 0.0]
+ when "return", "c-return"
+ now = Process.times[0]
+ key = [klass, id]
+ if tick = @@stack.pop
+ data = (@@map[key] ||= [0, 0.0, 0.0, key])
+ data[0] += 1
+ cost = now - tick[0]
+ data[1] += cost
+ data[2] += cost - tick[1]
+ @@stack[-1][1] += cost if @@stack[-1]
+ end
+ end
+ }
+module_function
+ def start_profile
+ @@start = Process.times[0]
+ @@stack = []
+ @@map = {}
+ set_trace_func PROFILE_PROC
+ end
+ def stop_profile
+ set_trace_func nil
+ end
+ def print_profile(f)
+ stop_profile
+ total = Process.times[0] - @@start
+ if total == 0 then total = 0.01 end
+ data = @@map.values
+ data.sort!{|a,b| b[2] <=> a[2]}
+ sum = 0
+ f.printf " %% cumulative self self total\n"
+ f.printf " time seconds seconds calls ms/call ms/call name\n"
+ for d in data
+ sum += d[2]
+ f.printf "%6.2f %8.2f %8.2f %8d ", d[2]/total*100, sum, d[2], d[0]
+ f.printf "%8.2f %8.2f %s\n", d[2]*1000/d[0], d[1]*1000/d[0], get_name(*d[3])
+ end
+ f.printf "%6.2f %8.2f %8.2f %8d ", 0.0, total, 0.0, 1 # ???
+ f.printf "%8.2f %8.2f %s\n", 0.0, total*1000, "#toplevel" # ???
+ end
+ def get_name(klass, id)
+ name = klass.to_s || ""
+ if klass.kind_of? Class
+ name += "#"
+ else
+ name += "."
+ end
+ name + id.id2name
+ end
+ private :get_name
+end
diff --git a/ruby_1_8_5/lib/pstore.rb b/ruby_1_8_5/lib/pstore.rb
new file mode 100644
index 0000000000..46123bf0a7
--- /dev/null
+++ b/ruby_1_8_5/lib/pstore.rb
@@ -0,0 +1,396 @@
+# = PStore -- Transactional File Storage for Ruby Objects
+#
+# pstore.rb -
+# originally by matz
+# documentation by Kev Jackson and James Edward Gray II
+#
+# See PStore for documentation.
+
+
+require "fileutils"
+require "digest/md5"
+
+#
+# PStore implements a file based persistance mechanism based on a Hash. User
+# code can store hierarchies of Ruby objects (values) into the data store file
+# by name (keys). An object hierarchy may be just a single object. User code
+# may later read values back from the data store or even update data, as needed.
+#
+# The transactional behavior ensures that any changes succeed or fail together.
+# This can be used to ensure that the data store is not left in a transitory
+# state, where some values were upated but others were not.
+#
+# Behind the scenes, Ruby objects are stored to the data store file with
+# Marshal. That carries the usual limitations. Proc objects cannot be
+# marshalled, for example.
+#
+# == Usage example:
+#
+# require "pstore"
+#
+# # a mock wiki object...
+# class WikiPage
+# def initialize( page_name, author, contents )
+# @page_name = page_name
+# @revisions = Array.new
+#
+# add_revision(author, contents)
+# end
+#
+# attr_reader :page_name
+#
+# def add_revision( author, contents )
+# @revisions << { :created => Time.now,
+# :author => author,
+# :contents => contents }
+# end
+#
+# def wiki_page_references
+# [@page_name] + @revisions.last[:contents].scan(/\b(?:[A-Z]+[a-z]+){2,}/)
+# end
+#
+# # ...
+# end
+#
+# # create a new page...
+# home_page = WikiPage.new( "HomePage", "James Edward Gray II",
+# "A page about the JoysOfDocumentation..." )
+#
+# # then we want to update page data and the index together, or not at all...
+# wiki = PStore.new("wiki_pages.pstore")
+# wiki.transaction do # begin transaction; do all of this or none of it
+# # store page...
+# wiki[home_page.page_name] = home_page
+# # ensure that an index has been created...
+# wiki[:wiki_index] ||= Array.new
+# # update wiki index...
+# wiki[:wiki_index].push(*home_page.wiki_page_references)
+# end # commit changes to wiki data store file
+#
+# ### Some time later... ###
+#
+# # read wiki data...
+# wiki.transaction(true) do # begin read-only transaction, no changes allowed
+# wiki.roots.each do |data_root_name|
+# p data_root_name
+# p wiki[data_root_name]
+# end
+# end
+#
+class PStore
+ # The error type thrown by all PStore methods.
+ class Error < StandardError
+ end
+
+ #
+ # To construct a PStore object, pass in the _file_ path where you would like
+ # the data to be stored.
+ #
+ def initialize(file)
+ dir = File::dirname(file)
+ unless File::directory? dir
+ raise PStore::Error, format("directory %s does not exist", dir)
+ end
+ if File::exist? file and not File::readable? file
+ raise PStore::Error, format("file %s not readable", file)
+ end
+ @transaction = false
+ @filename = file
+ @abort = false
+ end
+
+ # Raises PStore::Error if the calling code is not in a PStore#transaction.
+ def in_transaction
+ raise PStore::Error, "not in transaction" unless @transaction
+ end
+ #
+ # Raises PStore::Error if the calling code is not in a PStore#transaction or
+ # if the code is in a read-only PStore#transaction.
+ #
+ def in_transaction_wr()
+ in_transaction()
+ raise PStore::Error, "in read-only transaction" if @rdonly
+ end
+ private :in_transaction, :in_transaction_wr
+
+ #
+ # Retrieves a value from the PStore file data, by _name_. The hierarchy of
+ # Ruby objects stored under that root _name_ will be returned.
+ #
+ # *WARNING*: This method is only valid in a PStore#transaction. It will
+ # raise PStore::Error if called at any other time.
+ #
+ def [](name)
+ in_transaction
+ @table[name]
+ end
+ #
+ # This method is just like PStore#[], save that you may also provide a
+ # _default_ value for the object. In the event the specified _name_ is not
+ # found in the data store, your _default_ will be returned instead. If you do
+ # not specify a default, PStore::Error will be raised if the object is not
+ # found.
+ #
+ # *WARNING*: This method is only valid in a PStore#transaction. It will
+ # raise PStore::Error if called at any other time.
+ #
+ def fetch(name, default=PStore::Error)
+ in_transaction
+ unless @table.key? name
+ if default==PStore::Error
+ raise PStore::Error, format("undefined root name `%s'", name)
+ else
+ return default
+ end
+ end
+ @table[name]
+ end
+ #
+ # Stores an individual Ruby object or a hierarchy of Ruby objects in the data
+ # store file under the root _name_. Assigning to a _name_ already in the data
+ # store clobbers the old data.
+ #
+ # == Example:
+ #
+ # require "pstore"
+ #
+ # store = PStore.new("data_file.pstore")
+ # store.transaction do # begin transaction
+ # # load some data into the store...
+ # store[:single_object] = "My data..."
+ # store[:obj_heirarchy] = { "Kev Jackson" => ["rational.rb", "pstore.rb"],
+ # "James Gray" => ["erb.rb", "pstore.rb"] }
+ # end # commit changes to data store file
+ #
+ # *WARNING*: This method is only valid in a PStore#transaction and it cannot
+ # be read-only. It will raise PStore::Error if called at any other time.
+ #
+ def []=(name, value)
+ in_transaction_wr()
+ @table[name] = value
+ end
+ #
+ # Removes an object hierarchy from the data store, by _name_.
+ #
+ # *WARNING*: This method is only valid in a PStore#transaction and it cannot
+ # be read-only. It will raise PStore::Error if called at any other time.
+ #
+ def delete(name)
+ in_transaction_wr()
+ @table.delete name
+ end
+
+ #
+ # Returns the names of all object hierarchies currently in the store.
+ #
+ # *WARNING*: This method is only valid in a PStore#transaction. It will
+ # raise PStore::Error if called at any other time.
+ #
+ def roots
+ in_transaction
+ @table.keys
+ end
+ #
+ # Returns true if the supplied _name_ is currently in the data store.
+ #
+ # *WARNING*: This method is only valid in a PStore#transaction. It will
+ # raise PStore::Error if called at any other time.
+ #
+ def root?(name)
+ in_transaction
+ @table.key? name
+ end
+ # Returns the path to the data store file.
+ def path
+ @filename
+ end
+
+ #
+ # Ends the current PStore#transaction, committing any changes to the data
+ # store immediately.
+ #
+ # == Example:
+ #
+ # require "pstore"
+ #
+ # store = PStore.new("data_file.pstore")
+ # store.transaction do # begin transaction
+ # # load some data into the store...
+ # store[:one] = 1
+ # store[:two] = 2
+ #
+ # store.commit # end transaction here, committing changes
+ #
+ # store[:three] = 3 # this change is never reached
+ # end
+ #
+ # *WARNING*: This method is only valid in a PStore#transaction. It will
+ # raise PStore::Error if called at any other time.
+ #
+ def commit
+ in_transaction
+ @abort = false
+ throw :pstore_abort_transaction
+ end
+ #
+ # Ends the current PStore#transaction, discarding any changes to the data
+ # store.
+ #
+ # == Example:
+ #
+ # require "pstore"
+ #
+ # store = PStore.new("data_file.pstore")
+ # store.transaction do # begin transaction
+ # store[:one] = 1 # this change is not applied, see below...
+ # store[:two] = 2 # this change is not applied, see below...
+ #
+ # store.abort # end transaction here, discard all changes
+ #
+ # store[:three] = 3 # this change is never reached
+ # end
+ #
+ # *WARNING*: This method is only valid in a PStore#transaction. It will
+ # raise PStore::Error if called at any other time.
+ #
+ def abort
+ in_transaction
+ @abort = true
+ throw :pstore_abort_transaction
+ end
+
+ #
+ # Opens a new transaction for the data store. Code executed inside a block
+ # passed to this method may read and write data to and from the data store
+ # file.
+ #
+ # At the end of the block, changes are committed to the data store
+ # automatically. You may exit the transaction early with a call to either
+ # PStore#commit or PStore#abort. See those methods for details about how
+ # changes are handled. Raising an uncaught Exception in the block is
+ # equivalent to calling PStore#abort.
+ #
+ # If _read_only_ is set to +true+, you will only be allowed to read from the
+ # data store during the transaction and any attempts to change the data will
+ # raise a PStore::Error.
+ #
+ # Note that PStore does not support nested transactions.
+ #
+ def transaction(read_only=false) # :yields: pstore
+ raise PStore::Error, "nested transaction" if @transaction
+ begin
+ @rdonly = read_only
+ @abort = false
+ @transaction = true
+ value = nil
+ new_file = @filename + ".new"
+
+ content = nil
+ unless read_only
+ file = File.open(@filename, File::RDWR | File::CREAT)
+ file.binmode
+ file.flock(File::LOCK_EX)
+ commit_new(file) if FileTest.exist?(new_file)
+ content = file.read()
+ else
+ begin
+ file = File.open(@filename, File::RDONLY)
+ file.binmode
+ file.flock(File::LOCK_SH)
+ content = (File.read(new_file) rescue file.read())
+ rescue Errno::ENOENT
+ content = ""
+ end
+ end
+
+ if content != ""
+ @table = load(content)
+ if !read_only
+ size = content.size
+ md5 = Digest::MD5.digest(content)
+ end
+ else
+ @table = {}
+ end
+ content = nil # unreference huge data
+
+ begin
+ catch(:pstore_abort_transaction) do
+ value = yield(self)
+ end
+ rescue Exception
+ @abort = true
+ raise
+ ensure
+ if !read_only and !@abort
+ tmp_file = @filename + ".tmp"
+ content = dump(@table)
+ if !md5 || size != content.size || md5 != Digest::MD5.digest(content)
+ File.open(tmp_file, "w") {|t|
+ t.binmode
+ t.write(content)
+ }
+ File.rename(tmp_file, new_file)
+ commit_new(file)
+ end
+ content = nil # unreference huge data
+ end
+ end
+ ensure
+ @table = nil
+ @transaction = false
+ file.close if file
+ end
+ value
+ end
+
+ # This method is just a wrapped around Marshal.dump.
+ def dump(table) # :nodoc:
+ Marshal::dump(table)
+ end
+
+ # This method is just a wrapped around Marshal.load.
+ def load(content) # :nodoc:
+ Marshal::load(content)
+ end
+
+ # This method is just a wrapped around Marshal.load.
+ def load_file(file) # :nodoc:
+ Marshal::load(file)
+ end
+
+ private
+ # Commits changes to the data store file.
+ def commit_new(f)
+ f.truncate(0)
+ f.rewind
+ new_file = @filename + ".new"
+ File.open(new_file) do |nf|
+ nf.binmode
+ FileUtils.copy_stream(nf, f)
+ end
+ File.unlink(new_file)
+ end
+end
+
+# :enddoc:
+
+if __FILE__ == $0
+ db = PStore.new("/tmp/foo")
+ db.transaction do
+ p db.roots
+ ary = db["root"] = [1,2,3,4]
+ ary[1] = [1,1.5]
+ end
+
+ 1000.times do
+ db.transaction do
+ db["root"][0] += 1
+ p db["root"][0]
+ end
+ end
+
+ db.transaction(true) do
+ p db["root"]
+ end
+end
diff --git a/ruby_1_8_5/lib/racc/parser.rb b/ruby_1_8_5/lib/racc/parser.rb
new file mode 100644
index 0000000000..a7408dd00a
--- /dev/null
+++ b/ruby_1_8_5/lib/racc/parser.rb
@@ -0,0 +1,442 @@
+#
+# $originalId: parser.rb,v 1.8 2006/07/06 11:42:07 aamine Exp $
+#
+# Copyright (c) 1999-2006 Minero Aoki
+#
+# This program is free software.
+# You can distribute/modify this program under the same terms of ruby.
+#
+# As a special exception, when this code is copied by Racc
+# into a Racc output file, you may use that output file
+# without restriction.
+#
+
+unless defined?(NotImplementedError)
+ NotImplementedError = NotImplementError
+end
+
+module Racc
+ class ParseError < StandardError; end
+end
+unless defined?(::ParseError)
+ ParseError = Racc::ParseError
+end
+
+module Racc
+
+ unless defined?(Racc_No_Extentions)
+ Racc_No_Extentions = false
+ end
+
+ class Parser
+
+ Racc_Runtime_Version = '1.4.5'
+ Racc_Runtime_Revision = '$originalRevision: 1.8 $'.split[1]
+
+ Racc_Runtime_Core_Version_R = '1.4.5'
+ Racc_Runtime_Core_Revision_R = '$originalRevision: 1.8 $'.split[1]
+ begin
+ require 'racc/cparse'
+ # Racc_Runtime_Core_Version_C = (defined in extention)
+ Racc_Runtime_Core_Revision_C = Racc_Runtime_Core_Id_C.split[2]
+ unless new.respond_to?(:_racc_do_parse_c, true)
+ raise LoadError, 'old cparse.so'
+ end
+ if Racc_No_Extentions
+ raise LoadError, 'selecting ruby version of racc runtime core'
+ end
+
+ Racc_Main_Parsing_Routine = :_racc_do_parse_c
+ Racc_YY_Parse_Method = :_racc_yyparse_c
+ Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_C
+ Racc_Runtime_Core_Revision = Racc_Runtime_Core_Revision_C
+ Racc_Runtime_Type = 'c'
+ rescue LoadError
+ Racc_Main_Parsing_Routine = :_racc_do_parse_rb
+ Racc_YY_Parse_Method = :_racc_yyparse_rb
+ Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_R
+ Racc_Runtime_Core_Revision = Racc_Runtime_Core_Revision_R
+ Racc_Runtime_Type = 'ruby'
+ end
+
+ def Parser.racc_runtime_type
+ Racc_Runtime_Type
+ end
+
+ private
+
+ def _racc_setup
+ @yydebug = false unless self.class::Racc_debug_parser
+ @yydebug = false unless defined?(@yydebug)
+ if @yydebug
+ @racc_debug_out = $stderr unless defined?(@racc_debug_out)
+ @racc_debug_out ||= $stderr
+ end
+ arg = self.class::Racc_arg
+ arg[13] = true if arg.size < 14
+ arg
+ end
+
+ def _racc_init_sysvars
+ @racc_state = [0]
+ @racc_tstack = []
+ @racc_vstack = []
+
+ @racc_t = nil
+ @racc_val = nil
+
+ @racc_read_next = true
+
+ @racc_user_yyerror = false
+ @racc_error_status = 0
+ end
+
+ ###
+ ### do_parse
+ ###
+
+ def do_parse
+ __send__(Racc_Main_Parsing_Routine, _racc_setup(), false)
+ end
+
+ def next_token
+ raise NotImplementedError, "#{self.class}\#next_token is not defined"
+ end
+
+ def _racc_do_parse_rb(arg, in_debug)
+ action_table, action_check, action_default, action_pointer,
+ goto_table, goto_check, goto_default, goto_pointer,
+ nt_base, reduce_table, token_table, shift_n,
+ reduce_n, use_result, * = arg
+
+ _racc_init_sysvars
+ tok = act = i = nil
+ nerr = 0
+
+ catch(:racc_end_parse) {
+ while true
+ if i = action_pointer[@racc_state[-1]]
+ if @racc_read_next
+ if @racc_t != 0 # not EOF
+ tok, @racc_val = next_token()
+ unless tok # EOF
+ @racc_t = 0
+ else
+ @racc_t = (token_table[tok] or 1) # error token
+ end
+ racc_read_token(@racc_t, tok, @racc_val) if @yydebug
+ @racc_read_next = false
+ end
+ end
+ i += @racc_t
+ unless i >= 0 and
+ act = action_table[i] and
+ action_check[i] == @racc_state[-1]
+ act = action_default[@racc_state[-1]]
+ end
+ else
+ act = action_default[@racc_state[-1]]
+ end
+ while act = _racc_evalact(act, arg)
+ ;
+ end
+ end
+ }
+ end
+
+ ###
+ ### yyparse
+ ###
+
+ def yyparse(recv, mid)
+ __send__(Racc_YY_Parse_Method, recv, mid, _racc_setup(), true)
+ end
+
+ def _racc_yyparse_rb(recv, mid, arg, c_debug)
+ action_table, action_check, action_default, action_pointer,
+ goto_table, goto_check, goto_default, goto_pointer,
+ nt_base, reduce_table, token_table, shift_n,
+ reduce_n, use_result, * = arg
+
+ _racc_init_sysvars
+ tok = nil
+ act = nil
+ i = nil
+ nerr = 0
+
+ catch(:racc_end_parse) {
+ until i = action_pointer[@racc_state[-1]]
+ while act = _racc_evalact(action_default[@racc_state[-1]], arg)
+ ;
+ end
+ end
+ recv.__send__(mid) do |tok, val|
+ unless tok
+ @racc_t = 0
+ else
+ @racc_t = (token_table[tok] or 1) # error token
+ end
+ @racc_val = val
+ @racc_read_next = false
+
+ i += @racc_t
+ unless i >= 0 and
+ act = action_table[i] and
+ action_check[i] == @racc_state[-1]
+ act = action_default[@racc_state[-1]]
+ end
+ while act = _racc_evalact(act, arg)
+ ;
+ end
+
+ while not (i = action_pointer[@racc_state[-1]]) or
+ not @racc_read_next or
+ @racc_t == 0 # $
+ unless i and i += @racc_t and
+ i >= 0 and
+ act = action_table[i] and
+ action_check[i] == @racc_state[-1]
+ act = action_default[@racc_state[-1]]
+ end
+ while act = _racc_evalact(act, arg)
+ ;
+ end
+ end
+ end
+ }
+ end
+
+ ###
+ ### common
+ ###
+
+ def _racc_evalact(act, arg)
+ action_table, action_check, action_default, action_pointer,
+ goto_table, goto_check, goto_default, goto_pointer,
+ nt_base, reduce_table, token_table, shift_n,
+ reduce_n, use_result, * = arg
+ nerr = 0 # tmp
+
+ if act > 0 and act < shift_n
+ #
+ # shift
+ #
+ if @racc_error_status > 0
+ @racc_error_status -= 1 unless @racc_t == 1 # error token
+ end
+ @racc_vstack.push @racc_val
+ @racc_state.push act
+ @racc_read_next = true
+ if @yydebug
+ @racc_tstack.push @racc_t
+ racc_shift @racc_t, @racc_tstack, @racc_vstack
+ end
+
+ elsif act < 0 and act > -reduce_n
+ #
+ # reduce
+ #
+ code = catch(:racc_jump) {
+ @racc_state.push _racc_do_reduce(arg, act)
+ false
+ }
+ if code
+ case code
+ when 1 # yyerror
+ @racc_user_yyerror = true # user_yyerror
+ return -reduce_n
+ when 2 # yyaccept
+ return shift_n
+ else
+ raise '[Racc Bug] unknown jump code'
+ end
+ end
+
+ elsif act == shift_n
+ #
+ # accept
+ #
+ racc_accept if @yydebug
+ throw :racc_end_parse, @racc_vstack[0]
+
+ elsif act == -reduce_n
+ #
+ # error
+ #
+ case @racc_error_status
+ when 0
+ unless arg[21] # user_yyerror
+ nerr += 1
+ on_error @racc_t, @racc_val, @racc_vstack
+ end
+ when 3
+ if @racc_t == 0 # is $
+ throw :racc_end_parse, nil
+ end
+ @racc_read_next = true
+ end
+ @racc_user_yyerror = false
+ @racc_error_status = 3
+ while true
+ if i = action_pointer[@racc_state[-1]]
+ i += 1 # error token
+ if i >= 0 and
+ (act = action_table[i]) and
+ action_check[i] == @racc_state[-1]
+ break
+ end
+ end
+ throw :racc_end_parse, nil if @racc_state.size <= 1
+ @racc_state.pop
+ @racc_vstack.pop
+ if @yydebug
+ @racc_tstack.pop
+ racc_e_pop @racc_state, @racc_tstack, @racc_vstack
+ end
+ end
+ return act
+
+ else
+ raise "[Racc Bug] unknown action #{act.inspect}"
+ end
+
+ racc_next_state(@racc_state[-1], @racc_state) if @yydebug
+
+ nil
+ end
+
+ def _racc_do_reduce(arg, act)
+ action_table, action_check, action_default, action_pointer,
+ goto_table, goto_check, goto_default, goto_pointer,
+ nt_base, reduce_table, token_table, shift_n,
+ reduce_n, use_result, * = arg
+ state = @racc_state
+ vstack = @racc_vstack
+ tstack = @racc_tstack
+
+ i = act * -3
+ len = reduce_table[i]
+ reduce_to = reduce_table[i+1]
+ method_id = reduce_table[i+2]
+ void_array = []
+
+ tmp_t = tstack[-len, len] if @yydebug
+ tmp_v = vstack[-len, len]
+ tstack[-len, len] = void_array if @yydebug
+ vstack[-len, len] = void_array
+ state[-len, len] = void_array
+
+ # tstack must be updated AFTER method call
+ if use_result
+ vstack.push __send__(method_id, tmp_v, vstack, tmp_v[0])
+ else
+ vstack.push __send__(method_id, tmp_v, vstack)
+ end
+ tstack.push reduce_to
+
+ racc_reduce(tmp_t, reduce_to, tstack, vstack) if @yydebug
+
+ k1 = reduce_to - nt_base
+ if i = goto_pointer[k1]
+ i += state[-1]
+ if i >= 0 and (curstate = goto_table[i]) and goto_check[i] == k1
+ return curstate
+ end
+ end
+ goto_default[k1]
+ end
+
+ def on_error(t, val, vstack)
+ raise ParseError, sprintf("\nparse error on value %s (%s)",
+ val.inspect, token_to_str(t) || '?')
+ end
+
+ def yyerror
+ throw :racc_jump, 1
+ end
+
+ def yyaccept
+ throw :racc_jump, 2
+ end
+
+ def yyerrok
+ @racc_error_status = 0
+ end
+
+ #
+ # for debugging output
+ #
+
+ def racc_read_token(t, tok, val)
+ @racc_debug_out.print 'read '
+ @racc_debug_out.print tok.inspect, '(', racc_token2str(t), ') '
+ @racc_debug_out.puts val.inspect
+ @racc_debug_out.puts
+ end
+
+ def racc_shift(tok, tstack, vstack)
+ @racc_debug_out.puts "shift #{racc_token2str tok}"
+ racc_print_stacks tstack, vstack
+ @racc_debug_out.puts
+ end
+
+ def racc_reduce(toks, sim, tstack, vstack)
+ out = @racc_debug_out
+ out.print 'reduce '
+ if toks.empty?
+ out.print ' <none>'
+ else
+ toks.each {|t| out.print ' ', racc_token2str(t) }
+ end
+ out.puts " --> #{racc_token2str(sim)}"
+
+ racc_print_stacks tstack, vstack
+ @racc_debug_out.puts
+ end
+
+ def racc_accept
+ @racc_debug_out.puts 'accept'
+ @racc_debug_out.puts
+ end
+
+ def racc_e_pop(state, tstack, vstack)
+ @racc_debug_out.puts 'error recovering mode: pop token'
+ racc_print_states state
+ racc_print_stacks tstack, vstack
+ @racc_debug_out.puts
+ end
+
+ def racc_next_state(curstate, state)
+ @racc_debug_out.puts "goto #{curstate}"
+ racc_print_states state
+ @racc_debug_out.puts
+ end
+
+ def racc_print_stacks(t, v)
+ out = @racc_debug_out
+ out.print ' ['
+ t.each_index do |i|
+ out.print ' (', racc_token2str(t[i]), ' ', v[i].inspect, ')'
+ end
+ out.puts ' ]'
+ end
+
+ def racc_print_states(s)
+ out = @racc_debug_out
+ out.print ' ['
+ s.each {|st| out.print ' ', st }
+ out.puts ' ]'
+ end
+
+ def racc_token2str(tok)
+ self.class::Racc_token_to_s_table[tok] or
+ raise "[Racc Bug] can't convert token #{tok} to string"
+ end
+
+ def token_to_str(t)
+ self.class::Racc_token_to_s_table[t]
+ end
+
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rational.rb b/ruby_1_8_5/lib/rational.rb
new file mode 100644
index 0000000000..ce754cfa3c
--- /dev/null
+++ b/ruby_1_8_5/lib/rational.rb
@@ -0,0 +1,530 @@
+#
+# rational.rb -
+# $Release Version: 0.5 $
+# $Revision: 1.7 $
+# $Date: 1999/08/24 12:49:28 $
+# by Keiju ISHITSUKA(SHL Japan Inc.)
+#
+# Documentation by Kevin Jackson and Gavin Sinclair.
+#
+# When you <tt>require 'rational'</tt>, all interactions between numbers
+# potentially return a rational result. For example:
+#
+# 1.quo(2) # -> 0.5
+# require 'rational'
+# 1.quo(2) # -> Rational(1,2)
+#
+# See Rational for full documentation.
+#
+
+
+#
+# Creates a Rational number (i.e. a fraction). +a+ and +b+ should be Integers:
+#
+# Rational(1,3) # -> 1/3
+#
+# Note: trying to construct a Rational with floating point or real values
+# produces errors:
+#
+# Rational(1.1, 2.3) # -> NoMethodError
+#
+def Rational(a, b = 1)
+ if a.kind_of?(Rational) && b == 1
+ a
+ else
+ Rational.reduce(a, b)
+ end
+end
+
+#
+# Rational implements a rational class for numbers.
+#
+# <em>A rational number is a number that can be expressed as a fraction p/q
+# where p and q are integers and q != 0. A rational number p/q is said to have
+# numerator p and denominator q. Numbers that are not rational are called
+# irrational numbers.</em> (http://mathworld.wolfram.com/RationalNumber.html)
+#
+# To create a Rational Number:
+# Rational(a,b) # -> a/b
+# Rational.new!(a,b) # -> a/b
+#
+# Examples:
+# Rational(5,6) # -> 5/6
+# Rational(5) # -> 5/1
+#
+# Rational numbers are reduced to their lowest terms:
+# Rational(6,10) # -> 3/5
+#
+# But not if you use the unusual method "new!":
+# Rational.new!(6,10) # -> 6/10
+#
+# Division by zero is obviously not allowed:
+# Rational(3,0) # -> ZeroDivisionError
+#
+class Rational < Numeric
+ @RCS_ID='-$Id: rational.rb,v 1.7 1999/08/24 12:49:28 keiju Exp keiju $-'
+
+ #
+ # Reduces the given numerator and denominator to their lowest terms. Use
+ # Rational() instead.
+ #
+ def Rational.reduce(num, den = 1)
+ raise ZeroDivisionError, "denominator is zero" if den == 0
+
+ if den < 0
+ num = -num
+ den = -den
+ end
+ gcd = num.gcd(den)
+ num = num.div(gcd)
+ den = den.div(gcd)
+ if den == 1 && defined?(Unify)
+ num
+ else
+ new!(num, den)
+ end
+ end
+
+ #
+ # Implements the constructor. This method does not reduce to lowest terms or
+ # check for division by zero. Therefore #Rational() should be preferred in
+ # normal use.
+ #
+ def Rational.new!(num, den = 1)
+ new(num, den)
+ end
+
+ private_class_method :new
+
+ #
+ # This method is actually private.
+ #
+ def initialize(num, den)
+ if den < 0
+ num = -num
+ den = -den
+ end
+ if num.kind_of?(Integer) and den.kind_of?(Integer)
+ @numerator = num
+ @denominator = den
+ else
+ @numerator = num.to_i
+ @denominator = den.to_i
+ end
+ end
+
+ #
+ # Returns the addition of this value and +a+.
+ #
+ # Examples:
+ # r = Rational(3,4) # -> Rational(3,4)
+ # r + 1 # -> Rational(7,4)
+ # r + 0.5 # -> 1.25
+ #
+ def + (a)
+ if a.kind_of?(Rational)
+ num = @numerator * a.denominator
+ num_a = a.numerator * @denominator
+ Rational(num + num_a, @denominator * a.denominator)
+ elsif a.kind_of?(Integer)
+ self + Rational.new!(a, 1)
+ elsif a.kind_of?(Float)
+ Float(self) + a
+ else
+ x, y = a.coerce(self)
+ x + y
+ end
+ end
+
+ #
+ # Returns the difference of this value and +a+.
+ # subtracted.
+ #
+ # Examples:
+ # r = Rational(3,4) # -> Rational(3,4)
+ # r - 1 # -> Rational(-1,4)
+ # r - 0.5 # -> 0.25
+ #
+ def - (a)
+ if a.kind_of?(Rational)
+ num = @numerator * a.denominator
+ num_a = a.numerator * @denominator
+ Rational(num - num_a, @denominator*a.denominator)
+ elsif a.kind_of?(Integer)
+ self - Rational.new!(a, 1)
+ elsif a.kind_of?(Float)
+ Float(self) - a
+ else
+ x, y = a.coerce(self)
+ x - y
+ end
+ end
+
+ #
+ # Returns the product of this value and +a+.
+ #
+ # Examples:
+ # r = Rational(3,4) # -> Rational(3,4)
+ # r * 2 # -> Rational(3,2)
+ # r * 4 # -> Rational(3,1)
+ # r * 0.5 # -> 0.375
+ # r * Rational(1,2) # -> Rational(3,8)
+ #
+ def * (a)
+ if a.kind_of?(Rational)
+ num = @numerator * a.numerator
+ den = @denominator * a.denominator
+ Rational(num, den)
+ elsif a.kind_of?(Integer)
+ self * Rational.new!(a, 1)
+ elsif a.kind_of?(Float)
+ Float(self) * a
+ else
+ x, y = a.coerce(self)
+ x * y
+ end
+ end
+
+ #
+ # Returns the quotient of this value and +a+.
+ # r = Rational(3,4) # -> Rational(3,4)
+ # r / 2 # -> Rational(3,8)
+ # r / 2.0 # -> 0.375
+ # r / Rational(1,2) # -> Rational(3,2)
+ #
+ def / (a)
+ if a.kind_of?(Rational)
+ num = @numerator * a.denominator
+ den = @denominator * a.numerator
+ Rational(num, den)
+ elsif a.kind_of?(Integer)
+ raise ZeroDivisionError, "division by zero" if a == 0
+ self / Rational.new!(a, 1)
+ elsif a.kind_of?(Float)
+ Float(self) / a
+ else
+ x, y = a.coerce(self)
+ x / y
+ end
+ end
+
+ #
+ # Returns this value raised to the given power.
+ #
+ # Examples:
+ # r = Rational(3,4) # -> Rational(3,4)
+ # r ** 2 # -> Rational(9,16)
+ # r ** 2.0 # -> 0.5625
+ # r ** Rational(1,2) # -> 0.866025403784439
+ #
+ def ** (other)
+ if other.kind_of?(Rational)
+ Float(self) ** other
+ elsif other.kind_of?(Integer)
+ if other > 0
+ num = @numerator ** other
+ den = @denominator ** other
+ elsif other < 0
+ num = @denominator ** -other
+ den = @numerator ** -other
+ elsif other == 0
+ num = 1
+ den = 1
+ end
+ Rational.new!(num, den)
+ elsif other.kind_of?(Float)
+ Float(self) ** other
+ else
+ x, y = other.coerce(self)
+ x ** y
+ end
+ end
+
+ #
+ # Returns the remainder when this value is divided by +other+.
+ #
+ # Examples:
+ # r = Rational(7,4) # -> Rational(7,4)
+ # r % Rational(1,2) # -> Rational(1,4)
+ # r % 1 # -> Rational(3,4)
+ # r % Rational(1,7) # -> Rational(1,28)
+ # r % 0.26 # -> 0.19
+ #
+ def % (other)
+ value = (self / other).to_i
+ return self - other * value
+ end
+
+ #
+ # Returns the quotient _and_ remainder.
+ #
+ # Examples:
+ # r = Rational(7,4) # -> Rational(7,4)
+ # r.divmod Rational(1,2) # -> [3, Rational(1,4)]
+ #
+ def divmod(other)
+ value = (self / other).to_i
+ return value, self - other * value
+ end
+
+ #
+ # Returns the absolute value.
+ #
+ def abs
+ if @numerator > 0
+ Rational.new!(@numerator, @denominator)
+ else
+ Rational.new!(-@numerator, @denominator)
+ end
+ end
+
+ #
+ # Returns +true+ iff this value is numerically equal to +other+.
+ #
+ # But beware:
+ # Rational(1,2) == Rational(4,8) # -> true
+ # Rational(1,2) == Rational.new!(4,8) # -> false
+ #
+ # Don't use Rational.new!
+ #
+ def == (other)
+ if other.kind_of?(Rational)
+ @numerator == other.numerator and @denominator == other.denominator
+ elsif other.kind_of?(Integer)
+ self == Rational.new!(other, 1)
+ elsif other.kind_of?(Float)
+ Float(self) == other
+ else
+ other == self
+ end
+ end
+
+ #
+ # Standard comparison operator.
+ #
+ def <=> (other)
+ if other.kind_of?(Rational)
+ num = @numerator * other.denominator
+ num_a = other.numerator * @denominator
+ v = num - num_a
+ if v > 0
+ return 1
+ elsif v < 0
+ return -1
+ else
+ return 0
+ end
+ elsif other.kind_of?(Integer)
+ return self <=> Rational.new!(other, 1)
+ elsif other.kind_of?(Float)
+ return Float(self) <=> other
+ elsif defined? other.coerce
+ x, y = other.coerce(self)
+ return x <=> y
+ else
+ return nil
+ end
+ end
+
+ def coerce(other)
+ if other.kind_of?(Float)
+ return other, self.to_f
+ elsif other.kind_of?(Integer)
+ return Rational.new!(other, 1), self
+ else
+ super
+ end
+ end
+
+ #
+ # Converts the rational to an Integer. Not the _nearest_ integer, the
+ # truncated integer. Study the following example carefully:
+ # Rational(+7,4).to_i # -> 1
+ # Rational(-7,4).to_i # -> -2
+ # (-1.75).to_i # -> -1
+ #
+ # In other words:
+ # Rational(-7,4) == -1.75 # -> true
+ # Rational(-7,4).to_i == (-1.75).to_i # false
+ #
+ def to_i
+ Integer(@numerator.div(@denominator))
+ end
+
+ #
+ # Converts the rational to a Float.
+ #
+ def to_f
+ @numerator.to_f/@denominator.to_f
+ end
+
+ #
+ # Returns a string representation of the rational number.
+ #
+ # Example:
+ # Rational(3,4).to_s # "3/4"
+ # Rational(8).to_s # "8"
+ #
+ def to_s
+ if @denominator == 1
+ @numerator.to_s
+ else
+ @numerator.to_s+"/"+@denominator.to_s
+ end
+ end
+
+ #
+ # Returns +self+.
+ #
+ def to_r
+ self
+ end
+
+ #
+ # Returns a reconstructable string representation:
+ #
+ # Rational(5,8).inspect # -> "Rational(5, 8)"
+ #
+ def inspect
+ sprintf("Rational(%s, %s)", @numerator.inspect, @denominator.inspect)
+ end
+
+ #
+ # Returns a hash code for the object.
+ #
+ def hash
+ @numerator.hash ^ @denominator.hash
+ end
+
+ attr :numerator
+ attr :denominator
+
+ private :initialize
+end
+
+class Integer
+ #
+ # In an integer, the value _is_ the numerator of its rational equivalent.
+ # Therefore, this method returns +self+.
+ #
+ def numerator
+ self
+ end
+
+ #
+ # In an integer, the denominator is 1. Therefore, this method returns 1.
+ #
+ def denominator
+ 1
+ end
+
+ #
+ # Returns a Rational representation of this integer.
+ #
+ def to_r
+ Rational(self, 1)
+ end
+
+ #
+ # Returns the <em>greatest common denominator</em> of the two numbers (+self+
+ # and +n+).
+ #
+ # Examples:
+ # 72.gcd 168 # -> 24
+ # 19.gcd 36 # -> 1
+ #
+ # The result is positive, no matter the sign of the arguments.
+ #
+ def gcd(other)
+ min = self.abs
+ max = other.abs
+ while min > 0
+ tmp = min
+ min = max % min
+ max = tmp
+ end
+ max
+ end
+
+ #
+ # Returns the <em>lowest common multiple</em> (LCM) of the two arguments
+ # (+self+ and +other+).
+ #
+ # Examples:
+ # 6.lcm 7 # -> 42
+ # 6.lcm 9 # -> 18
+ #
+ def lcm(other)
+ if self.zero? or other.zero?
+ 0
+ else
+ (self.div(self.gcd(other)) * other).abs
+ end
+ end
+
+ #
+ # Returns the GCD _and_ the LCM (see #gcd and #lcm) of the two arguments
+ # (+self+ and +other+). This is more efficient than calculating them
+ # separately.
+ #
+ # Example:
+ # 6.gcdlcm 9 # -> [3, 18]
+ #
+ def gcdlcm(other)
+ gcd = self.gcd(other)
+ if self.zero? or other.zero?
+ [gcd, 0]
+ else
+ [gcd, (self.div(gcd) * other).abs]
+ end
+ end
+end
+
+class Fixnum
+ undef quo
+ # If Rational is defined, returns a Rational number instead of a Fixnum.
+ def quo(other)
+ Rational.new!(self,1) / other
+ end
+ alias rdiv quo
+
+ # Returns a Rational number if the result is in fact rational (i.e. +other+ < 0).
+ def rpower (other)
+ if other >= 0
+ self.power!(other)
+ else
+ Rational.new!(self,1)**other
+ end
+ end
+
+ unless defined? 1.power!
+ alias power! **
+ alias ** rpower
+ end
+end
+
+class Bignum
+ unless defined? Complex
+ alias power! **
+ end
+
+ undef quo
+ # If Rational is defined, returns a Rational number instead of a Bignum.
+ def quo(other)
+ Rational.new!(self,1) / other
+ end
+ alias rdiv quo
+
+ # Returns a Rational number if the result is in fact rational (i.e. +other+ < 0).
+ def rpower (other)
+ if other >= 0
+ self.power!(other)
+ else
+ Rational.new!(self, 1)**other
+ end
+ end
+
+ unless defined? Complex
+ alias ** rpower
+ end
+end
diff --git a/ruby_1_8_5/lib/rdoc/README b/ruby_1_8_5/lib/rdoc/README
new file mode 100644
index 0000000000..89ea0fbd3f
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/README
@@ -0,0 +1,489 @@
+= RDOC - Ruby Documentation System
+
+This package contains Rdoc and SimpleMarkup. Rdoc is an application
+that produces documentation for one or more Ruby source files. We work
+similarly to JavaDoc, parsing the source, and extracting the
+definition for classes, modules, and methods (along with includes and
+requires). We associate with these optional documentation contained
+in the immediately preceding comment block, and then render the result
+using a pluggable output formatter. (Currently, HTML is the only
+supported format. Markup is a library that converts plain text into
+various output formats. The Markup library is used to interpret the
+comment blocks that Rdoc uses to document methods, classes, and so on.
+
+This library contains two packages, rdoc itself and a text markup
+library, 'markup'.
+
+== Roadmap
+
+* If you want to use Rdoc to create documentation for your Ruby source
+ files, read on.
+* If you want to include extensions written in C, see rdoc/parsers/parse_c.rb.
+* For information on the various markups available in comment
+ blocks, see markup/simple_markup.rb.
+* If you want to drive Rdoc programatically, see RDoc::RDoc.
+* If you want to use the library to format text blocks into HTML,
+ have a look at SM::SimpleMarkup.
+* If you want to try writing your own HTML output template, see
+ RDoc::Page.
+
+== Summary
+
+Once installed, you can create documentation using the 'rdoc' command
+(the command is 'rdoc.bat' under Windows)
+
+ % rdoc [options] [names...]
+
+Type "rdoc --help" for an up-to-date option summary.
+
+A typical use might be to generate documentation for a package of Ruby
+source (such as rdoc itself).
+
+ % rdoc
+
+This command generates documentation for all the Ruby and C source
+files in and below the current directory. These will be stored in a
+documentation tree starting in the subdirectory 'doc'.
+
+You can make this slightly more useful for your readers by having the
+index page contain the documentation for the primary file. In our
+case, we could type
+
+ % rdoc --main rdoc/rdoc.rb
+
+You'll find information on the various formatting tricks you can use
+in comment blocks in the documentation this generates.
+
+RDoc uses file extensions to determine how to process each file. File
+names ending <tt>.rb</tt> and <tt>.rbw</tt> are assumed to be Ruby
+source. Files ending <tt>.c</tt> are parsed as C files. All other
+files are assumed to contain just SimpleMarkup-style markup (with or
+without leading '#' comment markers). If directory names are passed to
+RDoc, they are scanned recursively for C and Ruby source files only.
+
+== Credits
+
+* The Ruby parser in rdoc/parse.rb is based heavily on the outstanding
+ work of Keiju ISHITSUKA of Nippon Rational Inc, who produced the Ruby
+ parser for irb and the rtags package.
+
+* Code to diagram classes and modules was written by Sergey A Yanovitsky
+ (Jah) of Enticla.
+
+* Charset patch from MoonWolf.
+
+* Rich Kilmer wrote the kilmer.rb output template.
+
+* Dan Brickley led the design of the RDF format.
+
+== License
+
+RDoc is Copyright (c) 2001-2003 Dave Thomas, The Pragmatic Programmers. It
+is free software, and may be redistributed under the terms specified
+in the README file of the Ruby distribution.
+
+= Usage
+
+RDoc is invoked from the command line using:
+
+ % rdoc <options> [name...]
+
+Files are parsed, and the information they contain collected, before
+any output is produced. This allows cross references between all files
+to be resolved. If a name is a directory, it is traversed. If no
+names are specified, all Ruby files in the current directory (and
+subdirectories) are processed.
+
+Options are:
+
+[<tt>--accessor</tt> <i>name[,name...]</i>]
+ specifies the name(s) of additional methods that should be treated
+ as if they were <tt>attr_</tt><i>xxx</i> methods. Specifying
+ "--accessor db_opt" means lines such as
+
+ db_opt :name, :age
+
+ will get parsed and displayed in the documentation. Each name may have an
+ optional "=flagtext" appended, in which case the given flagtext will appear
+ where (for example) the 'rw' appears for attr_accessor.
+
+[<tt>--all</tt>]
+ include protected and private methods in the output (by default
+ only public methods are included)
+
+[<tt>--charset</tt> _charset_]
+ Set the character set for the generated HTML.
+
+[<tt>--diagram</tt>]
+ include diagrams showing modules and classes. This is currently
+ an experimental feature, and may not be supported by all output
+ templates. You need dot V1.8.6 or later to use the --diagram
+ option correctly (http://www.research.att.com/sw/tools/graphviz/).
+
+[<tt>--exclude</tt> <i>pattern</i>]
+ exclude files and directories matching this pattern from processing
+
+[<tt>--extension</tt> <i>new=old</i>]
+ treat files ending <i>.new</i> as if they ended
+ <i>.old</i>. Saying '--extension cgi=rb' causes RDoc to treat .cgi
+ files as Ruby source.
+
+[<tt>fileboxes</tt>]
+ Classes are put in boxes which represents files, where these
+ classes reside. Classes shared between more than one file are
+ shown with list of files that sharing them. Silently discarded if
+ --diagram is not given Experimental.
+
+[<tt>--fmt</tt> _fmt_]
+ generate output in a particular format.
+
+[<tt>--help</tt>]
+ generate a usage summary.
+
+[<tt>--help-output</tt>]
+ explain the various output options.
+
+[<tt>--image-format</tt> <i>gif/png/jpg/jpeg</i>]
+ sets output image format for diagrams. Can be png, gif, jpeg,
+ jpg. If this option is omitted, png is used. Requires --diagram.
+
+[<tt>--include</tt> <i>dir,...</i>]
+ specify one or more directories to be searched when satisfying
+ :+include+: directives. Multiple <tt>--include</tt> options may be
+ given. The directory containing the file currently being processed
+ is always searched.
+
+[<tt>--inline-source</tt>]
+ By default, the source code of methods is shown in a popup. With
+ this option, it's displayed inline.
+
+[<tt>line-numbers</tt>]
+ include line numbers in the source code
+
+[<tt>--main</tt> _name_]
+ the class of module _name_ will appear on the index page. If you
+ want to set a particular file as a main page (a README, for
+ example) simply specifiy its name as the first on the command
+ line.
+
+[<tt>--merge</tt>]
+ when generating _ri_ output, if classes being processed already
+ exist in the destination directory, merge in the current details
+ rather than overwrite them.
+
+[<tt>--one-file</tt>]
+ place all the output into a single file
+
+[<tt>--op</tt> _dir_]
+ set the output directory to _dir_ (the default is the directory
+ "doc")
+
+[<tt>--op-name</tt> _name_]
+ set the name of the output. Has no effect for HTML.
+ "doc")
+
+[<tt>--opname</tt> _name_]
+ set the output name (has no effect for HTML).
+
+[<tt>--promiscuous</tt>]
+ If a module or class is defined in more than one source file, and
+ you click on a particular file's name in the top navigation pane,
+ RDoc will normally only show you the inner classes and modules of
+ that class that are defined in the particular file. Using this
+ option makes it show all classes and modules defined in the class,
+ regardless of the file they were defined in.
+
+[<tt>--quiet</tt>]
+ do not display progress messages
+
+[<tt>--ri</tt>, <tt>--ri-site</tt>, _and_ <tt>--ri-system</tt>]
+ generate output than can be read by the _ri_ command-line tool.
+ By default --ri places its output in ~/.rdoc, --ri-site in
+ $datadir/ri/<ver>/site, and --ri-system in
+ $datadir/ri/<ver>/system. All can be overridden with a subsequent
+ --op option. All default directories are in ri's default search
+ path.
+
+[<tt>--show-hash</tt>]
+ A name of the form #name in a comment is a possible hyperlink to
+ an instance method name. When displayed, the '#' is removed unless
+ this option is specified
+
+[<tt>--style</tt> <i>stylesheet url</i>]
+ specifies the URL of an external stylesheet to use (rather than
+ generating one of our own)
+
+[<tt>tab-width</tt> _n_]
+ set the width of tab characters (default 8)
+
+[<tt>--template</tt> <i>name</i>]
+ specify an alternate template to use when generating output (the
+ default is 'standard'). This template should be in a directory
+ accessible via $: as rdoc/generators/xxxx_template, where 'xxxx'
+ depends on the output formatter.
+
+[<tt>--version</tt>]
+ display RDoc's version
+
+[<tt>--webcvs</tt> _url_]
+ Specify a URL for linking to a web frontend to CVS. If the URL
+ contains a '\%s', the name of the current file will be
+ substituted; if the URL doesn't contain a '\%s', the filename will
+ be appended to it.
+
+= Example
+
+A typical small Ruby program commented using RDoc might be as follows. You
+can see the formatted result in EXAMPLE.rb and Anagram.
+
+ :include: EXAMPLE.rb
+
+= Markup
+
+Comment blocks can be written fairly naturally, either using '#' on
+successive lines of the comment, or by including the comment in
+an =begin/=end block. If you use the latter form, the =begin line
+must be flagged with an RDoc tag:
+
+ =begin rdoc
+ Documentation to
+ be processed by RDoc.
+ =end
+
+Paragraphs are lines that share the left margin. Text indented past
+this margin are formatted verbatim.
+
+1. Lists are typed as indented paragraphs with:
+ * a '*' or '-' (for bullet lists)
+ * a digit followed by a period for
+ numbered lists
+ * an upper or lower case letter followed
+ by a period for alpha lists.
+
+ For example, the input that produced the above paragraph looked like
+ 1. Lists are typed as indented
+ paragraphs with:
+ * a '*' or '-' (for bullet lists)
+ * a digit followed by a period for
+ numbered lists
+ * an upper or lower case letter followed
+ by a period for alpha lists.
+
+2. Labeled lists (sometimes called description
+ lists) are typed using square brackets for the label.
+ [cat] small domestic animal
+ [+cat+] command to copy standard input
+
+3. Labeled lists may also be produced by putting a double colon
+ after the label. This sets the result in tabular form, so the
+ descriptions all line up. This was used to create the 'author'
+ block at the bottom of this description.
+ cat:: small domestic animal
+ +cat+:: command to copy standard input
+
+ For both kinds of labeled lists, if the body text starts on the same
+ line as the label, then the start of that text determines the block
+ indent for the rest of the body. The text may also start on the line
+ following the label, indented from the start of the label. This is
+ often preferable if the label is long. Both the following are
+ valid labeled list entries:
+
+ <tt>--output</tt> <i>name [, name]</i>::
+ specify the name of one or more output files. If multiple
+ files are present, the first is used as the index.
+
+ <tt>--quiet:</tt>:: do not output the names, sizes, byte counts,
+ index areas, or bit ratios of units as
+ they are processed.
+
+4. Headings are entered using equals signs
+
+ = Level One Heading
+ == Level Two Heading
+ and so on
+
+5. Rules (horizontal lines) are entered using three or
+ more hyphens.
+
+6. Non-verbatim text can be marked up:
+
+ _italic_:: \_word_ or \<em>text</em>
+ *bold*:: \*word* or \<b>text</b>
+ +typewriter+:: \+word+ or \<tt>text</tt>
+
+ The first form only works around 'words', where a word is a
+ sequence of upper and lower case letters and underscores. Putting a
+ backslash before inline markup stops it being interpreted, which is
+ how I created the table above:
+
+ _italic_:: \_word_ or \<em>text</em>
+ *bold*:: \*word* or \<b>text</b>
+ +typewriter+:: \+word+ or \<tt>text</tt>
+
+7. Names of classes, source files, and any method names
+ containing an underscore or preceded by a hash
+ character are automatically hyperlinked from
+ comment text to their description.
+
+8. Hyperlinks to the web starting http:, mailto:, ftp:, or www. are
+ recognized. An HTTP url that references an external image file is
+ converted into an inline <IMG..>. Hyperlinks starting 'link:' are
+ assumed to refer to local files whose path is relative to the --op
+ directory.
+
+ Hyperlinks can also be of the form <tt>label</tt>[url], in which
+ case the label is used in the displayed text, and <tt>url</tt> is
+ used as the target. If <tt>label</tt> contains multiple words,
+ put it in braces: <em>{multi word label}[</em>url<em>]</em>.
+
+9. Method parameter lists are extracted and displayed with
+ the method description. If a method calls +yield+, then
+ the parameters passed to yield will also be displayed:
+
+ def fred
+ ...
+ yield line, address
+
+ This will get documented as
+
+ fred() { |line, address| ... }
+
+ You can override this using a comment containing
+ ':yields: ...' immediately after the method definition
+
+ def fred # :yields: index, position
+ ...
+ yield line, address
+
+ which will get documented as
+
+ fred() { |index, position| ... }
+
+
+10. ':yields:' is an example of a documentation modifier. These appear
+ immediately after the start of the document element they are modifying.
+ Other modifiers include
+
+ [<tt>:nodoc:</tt><i>[all]</i>]
+ don't include this element in the documentation. For classes
+ and modules, the methods, aliases, constants, and attributes
+ directly within the affected class or module will also be
+ omitted. By default, though, modules and classes within that
+ class of module _will_ be documented. This is turned off by
+ adding the +all+ modifier.
+
+ module SM #:nodoc:
+ class Input
+ end
+ end
+ module Markup #:nodoc: all
+ class Output
+ end
+ end
+
+ In the above code, only class <tt>SM::Input</tt> will be
+ documented.
+
+ [<tt>:doc:</tt>]
+ force a method or attribute to be documented even if it
+ wouldn't otherwise be. Useful if, for example, you want to
+ include documentation of a particular private method.
+
+ [<tt>:notnew:</tt>]
+ only applicable to the +initialize+ instance method. Normally
+ RDoc assumes that the documentation and parameters for
+ #initialize are actually for the ::new method, and so fakes
+ out a ::new for the class. THe :notnew: modifier stops
+ this. Remember that #initialize is protected, so you won't
+ see the documentation unless you use the -a command line
+ option.
+
+
+11. RDoc stops processing comments if it finds a comment
+ line containing '<tt>#--</tt>'. This can be used to
+ separate external from internal comments, or
+ to stop a comment being associated with a method,
+ class, or module. Commenting can be turned back on with
+ a line that starts '<tt>#++</tt>'.
+
+ # Extract the age and calculate the
+ # date-of-birth.
+ #--
+ # FIXME: fails if the birthday falls on
+ # February 29th
+ #++
+ # The DOB is returned as a Time object.
+
+ def get_dob(person)
+ ...
+
+12. Comment blocks can contain other directives:
+
+ [<tt>:section: title</tt>]
+ Starts a new section in the output. The title following
+ <tt>:section:</tt> is used as the section heading, and the
+ remainder of the comment containing the section is used as
+ introductory text. Subsequent methods, aliases, attributes,
+ and classes will be documented in this section. A :section:
+ comment block may have one or more lines before the :section:
+ directive. These will be removed, and any identical lines at
+ the end of the block are also removed. This allows you to add
+ visual cues such as
+
+ # ----------------------------------------
+ # :section: My Section
+ # This is the section that I wrote.
+ # See it glisten in the noon-day sun.
+ # ----------------------------------------
+
+ [<tt>call-seq:</tt>]
+ lines up to the next blank line in the comment are treated as
+ the method's calling sequence, overriding the
+ default parsing of method parameters and yield arguments.
+
+ [<tt>:include:</tt><i>filename</i>]
+ include the contents of the named file at this point. The
+ file will be searched for in the directories listed by
+ the <tt>--include</tt> option, or in the current
+ directory by default. The contents of the file will be
+ shifted to have the same indentation as the ':' at the
+ start of the :include: directive.
+
+ [<tt>:title:</tt><i>text</i>]
+ Sets the title for the document. Equivalent to the --title command
+ line parameter. (The command line parameter overrides any :title:
+ directive in the source).
+
+ [<tt>:enddoc:</tt>]
+ Document nothing further at the current level.
+
+ [<tt>:main:</tt><i>name</i>]
+ Equivalent to the --main command line parameter.
+
+ [<tt>:stopdoc: / :startdoc:</tt>]
+ Stop and start adding new documentation elements to the
+ current container. For example, if a class has a number of
+ constants that you don't want to document, put a
+ <tt>:stopdoc:</tt> before the first, and a
+ <tt>:startdoc:</tt> after the last. If you don't specifiy a
+ <tt>:startdoc:</tt> by the end of the container, disables
+ documentation for the entire class or module.
+
+
+---
+
+See also markup/simple_markup.rb.
+
+= Other stuff
+
+Author:: Dave Thomas <dave@pragmaticprogrammer.com>
+Requires:: Ruby 1.8.1 or later
+License:: Copyright (c) 2001-2003 Dave Thomas.
+ Released under the same license as Ruby.
+
+== Warranty
+
+This software is provided "as is" and without any express or
+implied warranties, including, without limitation, the implied
+warranties of merchantibility and fitness for a particular
+purpose.
diff --git a/ruby_1_8_5/lib/rdoc/code_objects.rb b/ruby_1_8_5/lib/rdoc/code_objects.rb
new file mode 100644
index 0000000000..d6c4f1bdb9
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/code_objects.rb
@@ -0,0 +1,765 @@
+# We represent the various high-level code constructs that appear
+# in Ruby programs: classes, modules, methods, and so on.
+
+require 'rdoc/tokenstream'
+
+module RDoc
+
+
+ # We contain the common stuff for contexts (which are containers)
+ # and other elements (methods, attributes and so on)
+ #
+ class CodeObject
+
+ attr_accessor :parent
+
+ # We are the model of the code, but we know that at some point
+ # we will be worked on by viewers. By implementing the Viewable
+ # protocol, viewers can associated themselves with these objects.
+
+ attr_accessor :viewer
+
+ # are we done documenting (ie, did we come across a :enddoc:)?
+
+ attr_accessor :done_documenting
+
+ # Which section are we in
+
+ attr_accessor :section
+
+ # do we document ourselves?
+
+ attr_reader :document_self
+
+ def document_self=(val)
+ @document_self = val
+ if !val
+ remove_methods_etc
+ end
+ end
+
+ # set and cleared by :startdoc: and :enddoc:, this is used to toggle
+ # the capturing of documentation
+ def start_doc
+ @document_self = true
+ @document_children = true
+ end
+
+ def stop_doc
+ @document_self = false
+ @document_children = false
+ end
+
+ # do we document ourselves and our children
+
+ attr_reader :document_children
+
+ def document_children=(val)
+ @document_children = val
+ if !val
+ remove_classes_and_modules
+ end
+ end
+
+ # Do we _force_ documentation, even is we wouldn't normally show the entity
+ attr_accessor :force_documentation
+
+ # Default callbacks to nothing, but this is overridden for classes
+ # and modules
+ def remove_classes_and_modules
+ end
+
+ def remove_methods_etc
+ end
+
+ def initialize
+ @document_self = true
+ @document_children = true
+ @force_documentation = false
+ @done_documenting = false
+ end
+
+ # Access the code object's comment
+ attr_reader :comment
+
+ # Update the comment, but don't overwrite a real comment
+ # with an empty one
+ def comment=(comment)
+ @comment = comment unless comment.empty?
+ end
+
+ # There's a wee trick we pull. Comment blocks can have directives that
+ # override the stuff we extract during the parse. So, we have a special
+ # class method, attr_overridable, that lets code objects list
+ # those directives. Wehn a comment is assigned, we then extract
+ # out any matching directives and update our object
+
+ def CodeObject.attr_overridable(name, *aliases)
+ @overridables ||= {}
+
+ attr_accessor name
+
+ aliases.unshift name
+ aliases.each do |directive_name|
+ @overridables[directive_name.to_s] = name
+ end
+ end
+
+ end
+
+ # A Context is something that can hold modules, classes, methods,
+ # attributes, aliases, requires, and includes. Classes, modules, and
+ # files are all Contexts.
+
+ class Context < CodeObject
+ attr_reader :name, :method_list, :attributes, :aliases, :constants
+ attr_reader :requires, :includes, :in_files, :visibility
+
+ attr_reader :sections
+
+ class Section
+ attr_reader :title, :comment, :sequence
+
+ @@sequence = "SEC00000"
+
+ def initialize(title, comment)
+ @title = title
+ @@sequence.succ!
+ @sequence = @@sequence.dup
+ set_comment(comment)
+ end
+
+ private
+
+ # Set the comment for this section from the original comment block
+ # If the first line contains :section:, strip it and use the rest. Otherwise
+ # remove lines up to the line containing :section:, and look for
+ # those lines again at the end and remove them. This lets us write
+ #
+ # # ---------------------
+ # # :SECTION: The title
+ # # The body
+ # # ---------------------
+
+ def set_comment(comment)
+ return unless comment
+
+ if comment =~ /^.*?:section:.*$/
+ start = $`
+ rest = $'
+ if start.empty?
+ @comment = rest
+ else
+ @comment = rest.sub(/#{start.chomp}\Z/, '')
+ end
+ else
+ @comment = comment
+ end
+ @comment = nil if @comment.empty?
+ end
+ end
+
+
+ def initialize
+ super()
+
+ @in_files = []
+
+ @name ||= "unknown"
+ @comment ||= ""
+ @parent = nil
+ @visibility = :public
+
+ @current_section = Section.new(nil, nil)
+ @sections = [ @current_section ]
+
+ initialize_methods_etc
+ initialize_classes_and_modules
+ end
+
+ # map the class hash to an array externally
+ def classes
+ @classes.values
+ end
+
+ # map the module hash to an array externally
+ def modules
+ @modules.values
+ end
+
+ # Change the default visibility for new methods
+ def ongoing_visibility=(vis)
+ @visibility = vis
+ end
+
+ # Given an array +methods+ of method names, set the
+ # visibility of the corresponding AnyMethod object
+
+ def set_visibility_for(methods, vis, singleton=false)
+ count = 0
+ @method_list.each do |m|
+ if methods.include?(m.name) && m.singleton == singleton
+ m.visibility = vis
+ count += 1
+ end
+ end
+
+ return if count == methods.size || singleton
+
+ # perhaps we need to look at attributes
+
+ @attributes.each do |a|
+ if methods.include?(a.name)
+ a.visibility = vis
+ count += 1
+ end
+ end
+ end
+
+ # Record the file that we happen to find it in
+ def record_location(toplevel)
+ @in_files << toplevel unless @in_files.include?(toplevel)
+ end
+
+ # Return true if at least part of this thing was defined in +file+
+ def defined_in?(file)
+ @in_files.include?(file)
+ end
+
+ def add_class(class_type, name, superclass)
+ add_class_or_module(@classes, class_type, name, superclass)
+ end
+
+ def add_module(class_type, name)
+ add_class_or_module(@modules, class_type, name, nil)
+ end
+
+ def add_method(a_method)
+ puts "Adding #@visibility method #{a_method.name} to #@name" if $DEBUG
+ a_method.visibility = @visibility
+ add_to(@method_list, a_method)
+ end
+
+ def add_attribute(an_attribute)
+ add_to(@attributes, an_attribute)
+ end
+
+ def add_alias(an_alias)
+ meth = find_instance_method_named(an_alias.old_name)
+ if meth
+ new_meth = AnyMethod.new(an_alias.text, an_alias.new_name)
+ new_meth.is_alias_for = meth
+ new_meth.singleton = meth.singleton
+ new_meth.params = meth.params
+ new_meth.comment = "Alias for \##{meth.name}"
+ meth.add_alias(new_meth)
+ add_method(new_meth)
+ else
+ add_to(@aliases, an_alias)
+ end
+ end
+
+ def add_include(an_include)
+ add_to(@includes, an_include)
+ end
+
+ def add_constant(const)
+ add_to(@constants, const)
+ end
+
+ # Requires always get added to the top-level (file) context
+ def add_require(a_require)
+ if self.kind_of? TopLevel
+ add_to(@requires, a_require)
+ else
+ parent.add_require(a_require)
+ end
+ end
+
+ def add_class_or_module(collection, class_type, name, superclass=nil)
+ cls = collection[name]
+ if cls
+ puts "Reusing class/module #{name}" if $DEBUG
+ else
+ cls = class_type.new(name, superclass)
+ puts "Adding class/module #{name} to #@name" if $DEBUG
+# collection[name] = cls if @document_self && !@done_documenting
+ collection[name] = cls if !@done_documenting
+ cls.parent = self
+ cls.section = @current_section
+ end
+ cls
+ end
+
+ def add_to(array, thing)
+ array << thing if @document_self && !@done_documenting
+ thing.parent = self
+ thing.section = @current_section
+ end
+
+ # If a class's documentation is turned off after we've started
+ # collecting methods etc., we need to remove the ones
+ # we have
+
+ def remove_methods_etc
+ initialize_methods_etc
+ end
+
+ def initialize_methods_etc
+ @method_list = []
+ @attributes = []
+ @aliases = []
+ @requires = []
+ @includes = []
+ @constants = []
+ end
+
+ # and remove classes and modules when we see a :nodoc: all
+ def remove_classes_and_modules
+ initialize_classes_and_modules
+ end
+
+ def initialize_classes_and_modules
+ @classes = {}
+ @modules = {}
+ end
+
+ # Find a named module
+ def find_module_named(name)
+ return self if self.name == name
+ res = @modules[name] || @classes[name]
+ return res if res
+ find_enclosing_module_named(name)
+ end
+
+ # find a module at a higher scope
+ def find_enclosing_module_named(name)
+ parent && parent.find_module_named(name)
+ end
+
+ # Iterate over all the classes and modules in
+ # this object
+
+ def each_classmodule
+ @modules.each_value {|m| yield m}
+ @classes.each_value {|c| yield c}
+ end
+
+ def each_method
+ @method_list.each {|m| yield m}
+ end
+
+ def each_attribute
+ @attributes.each {|a| yield a}
+ end
+
+ def each_constant
+ @constants.each {|c| yield c}
+ end
+
+ # Return the toplevel that owns us
+
+ def toplevel
+ return @toplevel if defined? @toplevel
+ @toplevel = self
+ @toplevel = @toplevel.parent until TopLevel === @toplevel
+ @toplevel
+ end
+
+ # allow us to sort modules by name
+ def <=>(other)
+ name <=> other.name
+ end
+
+ # Look up the given symbol. If method is non-nil, then
+ # we assume the symbol references a module that
+ # contains that method
+ def find_symbol(symbol, method=nil)
+ result = nil
+ case symbol
+ when /^::(.*)/
+ result = toplevel.find_symbol($1)
+ when /::/
+ modules = symbol.split(/::/)
+ unless modules.empty?
+ module_name = modules.shift
+ result = find_module_named(module_name)
+ if result
+ modules.each do |module_name|
+ result = result.find_module_named(module_name)
+ break unless result
+ end
+ end
+ end
+ else
+ # if a method is specified, then we're definitely looking for
+ # a module, otherwise it could be any symbol
+ if method
+ result = find_module_named(symbol)
+ else
+ result = find_local_symbol(symbol)
+ if result.nil?
+ if symbol =~ /^[A-Z]/
+ result = parent
+ while result && result.name != symbol
+ result = result.parent
+ end
+ end
+ end
+ end
+ end
+ if result && method
+ if !result.respond_to?(:find_local_symbol)
+ p result.name
+ p method
+ fail
+ end
+ result = result.find_local_symbol(method)
+ end
+ result
+ end
+
+ def find_local_symbol(symbol)
+ res = find_method_named(symbol) ||
+ find_constant_named(symbol) ||
+ find_attribute_named(symbol) ||
+ find_module_named(symbol)
+ end
+
+ # Handle sections
+
+ def set_current_section(title, comment)
+ @current_section = Section.new(title, comment)
+ @sections << @current_section
+ end
+
+ private
+
+ # Find a named method, or return nil
+ def find_method_named(name)
+ @method_list.find {|meth| meth.name == name}
+ end
+
+ # Find a named instance method, or return nil
+ def find_instance_method_named(name)
+ @method_list.find {|meth| meth.name == name && !meth.singleton}
+ end
+
+ # Find a named constant, or return nil
+ def find_constant_named(name)
+ @constants.find {|m| m.name == name}
+ end
+
+ # Find a named attribute, or return nil
+ def find_attribute_named(name)
+ @attributes.find {|m| m.name == name}
+ end
+
+ end
+
+
+ # A TopLevel context is a source file
+
+ class TopLevel < Context
+ attr_accessor :file_stat
+ attr_accessor :file_relative_name
+ attr_accessor :file_absolute_name
+ attr_accessor :diagram
+
+ @@all_classes = {}
+ @@all_modules = {}
+
+ def TopLevel::reset
+ @@all_classes = {}
+ @@all_modules = {}
+ end
+
+ def initialize(file_name)
+ super()
+ @name = "TopLevel"
+ @file_relative_name = file_name
+ @file_absolute_name = file_name
+ @file_stat = File.stat(file_name)
+ @diagram = nil
+ end
+
+ def full_name
+ nil
+ end
+
+ # Adding a class or module to a TopLevel is special, as we only
+ # want one copy of a particular top-level class. For example,
+ # if both file A and file B implement class C, we only want one
+ # ClassModule object for C. This code arranges to share
+ # classes and modules between files.
+
+ def add_class_or_module(collection, class_type, name, superclass)
+ cls = collection[name]
+ if cls
+ puts "Reusing class/module #{name}" if $DEBUG
+ else
+ if class_type == NormalModule
+ all = @@all_modules
+ else
+ all = @@all_classes
+ end
+ cls = all[name]
+ if !cls
+ cls = class_type.new(name, superclass)
+ all[name] = cls unless @done_documenting
+ end
+ puts "Adding class/module #{name} to #@name" if $DEBUG
+ collection[name] = cls unless @done_documenting
+ cls.parent = self
+ end
+ cls
+ end
+
+ def TopLevel.all_classes_and_modules
+ @@all_classes.values + @@all_modules.values
+ end
+
+ def TopLevel.find_class_named(name)
+ @@all_classes.each_value do |c|
+ res = c.find_class_named(name)
+ return res if res
+ end
+ nil
+ end
+
+ def find_local_symbol(symbol)
+ find_class_or_module_named(symbol) || super
+ end
+
+ def find_class_or_module_named(symbol)
+ @@all_classes.each_value {|c| return c if c.name == symbol}
+ @@all_modules.each_value {|m| return m if m.name == symbol}
+ nil
+ end
+
+ # Find a named module
+ def find_module_named(name)
+ find_class_or_module_named(name) || find_enclosing_module_named(name)
+ end
+
+
+ end
+
+ # ClassModule is the base class for objects representing either a
+ # class or a module.
+
+ class ClassModule < Context
+
+ attr_reader :superclass
+ attr_accessor :diagram
+
+ def initialize(name, superclass = nil)
+ @name = name
+ @diagram = nil
+ @superclass = superclass
+ @comment = ""
+ super()
+ end
+
+ # Return the fully qualified name of this class or module
+ def full_name
+ if @parent && @parent.full_name
+ @parent.full_name + "::" + @name
+ else
+ @name
+ end
+ end
+
+ def http_url(prefix)
+ path = full_name.split("::")
+ File.join(prefix, *path) + ".html"
+ end
+
+ # Return +true+ if this object represents a module
+ def is_module?
+ false
+ end
+
+ # to_s is simply for debugging
+ def to_s
+ res = self.class.name + ": " + @name
+ res << @comment.to_s
+ res << super
+ res
+ end
+
+ def find_class_named(name)
+ return self if full_name == name
+ @classes.each_value {|c| return c if c.find_class_named(name) }
+ nil
+ end
+ end
+
+ # Anonymous classes
+ class AnonClass < ClassModule
+ end
+
+ # Normal classes
+ class NormalClass < ClassModule
+ end
+
+ # Singleton classes
+ class SingleClass < ClassModule
+ end
+
+ # Module
+ class NormalModule < ClassModule
+ def is_module?
+ true
+ end
+ end
+
+
+ # AnyMethod is the base class for objects representing methods
+
+ class AnyMethod < CodeObject
+ attr_accessor :name
+ attr_accessor :visibility
+ attr_accessor :block_params
+ attr_accessor :dont_rename_initialize
+ attr_accessor :singleton
+ attr_reader :aliases # list of other names for this method
+ attr_accessor :is_alias_for # or a method we're aliasing
+
+ attr_overridable :params, :param, :parameters, :parameter
+
+ attr_accessor :call_seq
+
+
+ include TokenStream
+
+ def initialize(text, name)
+ super()
+ @text = text
+ @name = name
+ @token_stream = nil
+ @visibility = :public
+ @dont_rename_initialize = false
+ @block_params = nil
+ @aliases = []
+ @is_alias_for = nil
+ @comment = ""
+ @call_seq = nil
+ end
+
+ def <=>(other)
+ @name <=> other.name
+ end
+
+ def to_s
+ res = self.class.name + ": " + @name + " (" + @text + ")\n"
+ res << @comment.to_s
+ res
+ end
+
+ def param_seq
+ p = params.gsub(/\s*\#.*/, '')
+ p = p.tr("\n", " ").squeeze(" ")
+ p = "(" + p + ")" unless p[0] == ?(
+
+ if (block = block_params)
+ # If this method has explicit block parameters, remove any
+ # explicit &block
+$stderr.puts p
+ p.sub!(/,?\s*&\w+/)
+$stderr.puts p
+
+ block.gsub!(/\s*\#.*/, '')
+ block = block.tr("\n", " ").squeeze(" ")
+ if block[0] == ?(
+ block.sub!(/^\(/, '').sub!(/\)/, '')
+ end
+ p << " {|#{block}| ...}"
+ end
+ p
+ end
+
+ def add_alias(method)
+ @aliases << method
+ end
+ end
+
+
+ # Represent an alias, which is an old_name/ new_name pair associated
+ # with a particular context
+ class Alias < CodeObject
+ attr_accessor :text, :old_name, :new_name, :comment
+
+ def initialize(text, old_name, new_name, comment)
+ super()
+ @text = text
+ @old_name = old_name
+ @new_name = new_name
+ self.comment = comment
+ end
+
+ def to_s
+ "alias: #{self.old_name} -> #{self.new_name}\n#{self.comment}"
+ end
+ end
+
+ # Represent a constant
+ class Constant < CodeObject
+ attr_accessor :name, :value
+
+ def initialize(name, value, comment)
+ super()
+ @name = name
+ @value = value
+ self.comment = comment
+ end
+ end
+
+ # Represent attributes
+ class Attr < CodeObject
+ attr_accessor :text, :name, :rw, :visibility
+
+ def initialize(text, name, rw, comment)
+ super()
+ @text = text
+ @name = name
+ @rw = rw
+ @visibility = :public
+ self.comment = comment
+ end
+
+ def to_s
+ "attr: #{self.name} #{self.rw}\n#{self.comment}"
+ end
+
+ def <=>(other)
+ self.name <=> other.name
+ end
+ end
+
+ # a required file
+
+ class Require < CodeObject
+ attr_accessor :name
+
+ def initialize(name, comment)
+ super()
+ @name = name.gsub(/'|"/, "") #'
+ self.comment = comment
+ end
+
+ end
+
+ # an included module
+ class Include < CodeObject
+ attr_accessor :name
+
+ def initialize(name, comment)
+ super()
+ @name = name
+ self.comment = comment
+ end
+
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rdoc/diagram.rb b/ruby_1_8_5/lib/rdoc/diagram.rb
new file mode 100644
index 0000000000..9fdc49c02e
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/diagram.rb
@@ -0,0 +1,335 @@
+# A wonderful hack by to draw package diagrams using the dot package.
+# Originally written by Jah, team Enticla.
+#
+# You must have the V1.7 or later in your path
+# http://www.research.att.com/sw/tools/graphviz/
+
+require "rdoc/dot/dot"
+require 'rdoc/options'
+
+module RDoc
+
+ # Draw a set of diagrams representing the modules and classes in the
+ # system. We draw one diagram for each file, and one for each toplevel
+ # class or module. This means there will be overlap. However, it also
+ # means that you'll get better context for objects.
+ #
+ # To use, simply
+ #
+ # d = Diagram.new(info) # pass in collection of top level infos
+ # d.draw
+ #
+ # The results will be written to the +dot+ subdirectory. The process
+ # also sets the +diagram+ attribute in each object it graphs to
+ # the name of the file containing the image. This can be used
+ # by output generators to insert images.
+
+ class Diagram
+
+ FONT = "Arial"
+
+ DOT_PATH = "dot"
+
+ # Pass in the set of top level objects. The method also creates
+ # the subdirectory to hold the images
+
+ def initialize(info, options)
+ @info = info
+ @options = options
+ @counter = 0
+ File.makedirs(DOT_PATH)
+ @diagram_cache = {}
+ end
+
+ # Draw the diagrams. We traverse the files, drawing a diagram for
+ # each. We also traverse each top-level class and module in that
+ # file drawing a diagram for these too.
+
+ def draw
+ unless @options.quiet
+ $stderr.print "Diagrams: "
+ $stderr.flush
+ end
+
+ @info.each_with_index do |i, file_count|
+ @done_modules = {}
+ @local_names = find_names(i)
+ @global_names = []
+ @global_graph = graph = DOT::DOTDigraph.new('name' => 'TopLevel',
+ 'fontname' => FONT,
+ 'fontsize' => '8',
+ 'bgcolor' => 'lightcyan1',
+ 'compound' => 'true')
+
+ # it's a little hack %) i'm too lazy to create a separate class
+ # for default node
+ graph << DOT::DOTNode.new('name' => 'node',
+ 'fontname' => FONT,
+ 'color' => 'black',
+ 'fontsize' => 8)
+
+ i.modules.each do |mod|
+ draw_module(mod, graph, true, i.file_relative_name)
+ end
+ add_classes(i, graph, i.file_relative_name)
+
+ i.diagram = convert_to_png("f_#{file_count}", graph)
+
+ # now go through and document each top level class and
+ # module independently
+ i.modules.each_with_index do |mod, count|
+ @done_modules = {}
+ @local_names = find_names(mod)
+ @global_names = []
+
+ @global_graph = graph = DOT::DOTDigraph.new('name' => 'TopLevel',
+ 'fontname' => FONT,
+ 'fontsize' => '8',
+ 'bgcolor' => 'lightcyan1',
+ 'compound' => 'true')
+
+ graph << DOT::DOTNode.new('name' => 'node',
+ 'fontname' => FONT,
+ 'color' => 'black',
+ 'fontsize' => 8)
+ draw_module(mod, graph, true)
+ mod.diagram = convert_to_png("m_#{file_count}_#{count}",
+ graph)
+ end
+ end
+ $stderr.puts unless @options.quiet
+ end
+
+ #######
+ private
+ #######
+
+ def find_names(mod)
+ return [mod.full_name] + mod.classes.collect{|cl| cl.full_name} +
+ mod.modules.collect{|m| find_names(m)}.flatten
+ end
+
+ def find_full_name(name, mod)
+ full_name = name.dup
+ return full_name if @local_names.include?(full_name)
+ mod_path = mod.full_name.split('::')[0..-2]
+ unless mod_path.nil?
+ until mod_path.empty?
+ full_name = mod_path.pop + '::' + full_name
+ return full_name if @local_names.include?(full_name)
+ end
+ end
+ return name
+ end
+
+ def draw_module(mod, graph, toplevel = false, file = nil)
+ return if @done_modules[mod.full_name] and not toplevel
+
+ @counter += 1
+ url = mod.http_url("classes")
+ m = DOT::DOTSubgraph.new('name' => "cluster_#{mod.full_name.gsub( /:/,'_' )}",
+ 'label' => mod.name,
+ 'fontname' => FONT,
+ 'color' => 'blue',
+ 'style' => 'filled',
+ 'URL' => %{"#{url}"},
+ 'fillcolor' => toplevel ? 'palegreen1' : 'palegreen3')
+
+ @done_modules[mod.full_name] = m
+ add_classes(mod, m, file)
+ graph << m
+
+ unless mod.includes.empty?
+ mod.includes.each do |m|
+ m_full_name = find_full_name(m.name, mod)
+ if @local_names.include?(m_full_name)
+ @global_graph << DOT::DOTEdge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
+ 'to' => "#{mod.full_name.gsub( /:/,'_' )}",
+ 'ltail' => "cluster_#{m_full_name.gsub( /:/,'_' )}",
+ 'lhead' => "cluster_#{mod.full_name.gsub( /:/,'_' )}")
+ else
+ unless @global_names.include?(m_full_name)
+ path = m_full_name.split("::")
+ url = File.join('classes', *path) + ".html"
+ @global_graph << DOT::DOTNode.new('name' => "#{m_full_name.gsub( /:/,'_' )}",
+ 'shape' => 'box',
+ 'label' => "#{m_full_name}",
+ 'URL' => %{"#{url}"})
+ @global_names << m_full_name
+ end
+ @global_graph << DOT::DOTEdge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
+ 'to' => "#{mod.full_name.gsub( /:/,'_' )}",
+ 'lhead' => "cluster_#{mod.full_name.gsub( /:/,'_' )}")
+ end
+ end
+ end
+ end
+
+ def add_classes(container, graph, file = nil )
+
+ use_fileboxes = Options.instance.fileboxes
+
+ files = {}
+
+ # create dummy node (needed if empty and for module includes)
+ if container.full_name
+ graph << DOT::DOTNode.new('name' => "#{container.full_name.gsub( /:/,'_' )}",
+ 'label' => "",
+ 'width' => (container.classes.empty? and
+ container.modules.empty?) ?
+ '0.75' : '0.01',
+ 'height' => '0.01',
+ 'shape' => 'plaintext')
+ end
+ container.classes.each_with_index do |cl, cl_index|
+ last_file = cl.in_files[-1].file_relative_name
+
+ if use_fileboxes && !files.include?(last_file)
+ @counter += 1
+ files[last_file] =
+ DOT::DOTSubgraph.new('name' => "cluster_#{@counter}",
+ 'label' => "#{last_file}",
+ 'fontname' => FONT,
+ 'color'=>
+ last_file == file ? 'red' : 'black')
+ end
+
+ next if cl.name == 'Object' || cl.name[0,2] == "<<"
+
+ url = cl.http_url("classes")
+
+ label = cl.name.dup
+ if use_fileboxes && cl.in_files.length > 1
+ label << '\n[' +
+ cl.in_files.collect {|i|
+ i.file_relative_name
+ }.sort.join( '\n' ) +
+ ']'
+ end
+
+ attrs = {
+ 'name' => "#{cl.full_name.gsub( /:/, '_' )}",
+ 'fontcolor' => 'black',
+ 'style'=>'filled',
+ 'color'=>'palegoldenrod',
+ 'label' => label,
+ 'shape' => 'ellipse',
+ 'URL' => %{"#{url}"}
+ }
+
+ c = DOT::DOTNode.new(attrs)
+
+ if use_fileboxes
+ files[last_file].push c
+ else
+ graph << c
+ end
+ end
+
+ if use_fileboxes
+ files.each_value do |val|
+ graph << val
+ end
+ end
+
+ unless container.classes.empty?
+ container.classes.each_with_index do |cl, cl_index|
+ cl.includes.each do |m|
+ m_full_name = find_full_name(m.name, cl)
+ if @local_names.include?(m_full_name)
+ @global_graph << DOT::DOTEdge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
+ 'to' => "#{cl.full_name.gsub( /:/,'_' )}",
+ 'ltail' => "cluster_#{m_full_name.gsub( /:/,'_' )}")
+ else
+ unless @global_names.include?(m_full_name)
+ path = m_full_name.split("::")
+ url = File.join('classes', *path) + ".html"
+ @global_graph << DOT::DOTNode.new('name' => "#{m_full_name.gsub( /:/,'_' )}",
+ 'shape' => 'box',
+ 'label' => "#{m_full_name}",
+ 'URL' => %{"#{url}"})
+ @global_names << m_full_name
+ end
+ @global_graph << DOT::DOTEdge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
+ 'to' => "#{cl.full_name.gsub( /:/, '_')}")
+ end
+ end
+
+ sclass = cl.superclass
+ next if sclass.nil? || sclass == 'Object'
+ sclass_full_name = find_full_name(sclass,cl)
+ unless @local_names.include?(sclass_full_name) or @global_names.include?(sclass_full_name)
+ path = sclass_full_name.split("::")
+ url = File.join('classes', *path) + ".html"
+ @global_graph << DOT::DOTNode.new(
+ 'name' => "#{sclass_full_name.gsub( /:/, '_' )}",
+ 'label' => sclass_full_name,
+ 'URL' => %{"#{url}"})
+ @global_names << sclass_full_name
+ end
+ @global_graph << DOT::DOTEdge.new('from' => "#{sclass_full_name.gsub( /:/,'_' )}",
+ 'to' => "#{cl.full_name.gsub( /:/, '_')}")
+ end
+ end
+
+ container.modules.each do |submod|
+ draw_module(submod, graph)
+ end
+
+ end
+
+ def convert_to_png(file_base, graph)
+ str = graph.to_s
+ return @diagram_cache[str] if @diagram_cache[str]
+ op_type = Options.instance.image_format
+ dotfile = File.join(DOT_PATH, file_base)
+ src = dotfile + ".dot"
+ dot = dotfile + "." + op_type
+
+ unless @options.quiet
+ $stderr.print "."
+ $stderr.flush
+ end
+
+ File.open(src, 'w+' ) do |f|
+ f << str << "\n"
+ end
+
+ system "dot", "-T#{op_type}", src, "-o", dot
+
+ # Now construct the imagemap wrapper around
+ # that png
+
+ ret = wrap_in_image_map(src, dot)
+ @diagram_cache[str] = ret
+ return ret
+ end
+
+ # Extract the client-side image map from dot, and use it
+ # to generate the imagemap proper. Return the whole
+ # <map>..<img> combination, suitable for inclusion on
+ # the page
+
+ def wrap_in_image_map(src, dot)
+ res = %{<map id="map" name="map">\n}
+ dot_map = `dot -Tismap #{src}`
+ dot_map.each do |area|
+ unless area =~ /^rectangle \((\d+),(\d+)\) \((\d+),(\d+)\) ([\/\w.]+)\s*(.*)/
+ $stderr.puts "Unexpected output from dot:\n#{area}"
+ return nil
+ end
+
+ xs, ys = [$1.to_i, $3.to_i], [$2.to_i, $4.to_i]
+ url, area_name = $5, $6
+
+ res << %{ <area shape="rect" coords="#{xs.min},#{ys.min},#{xs.max},#{ys.max}" }
+ res << %{ href="#{url}" alt="#{area_name}" />\n}
+ end
+ res << "</map>\n"
+# map_file = src.sub(/.dot/, '.map')
+# system("dot -Timap #{src} -o #{map_file}")
+ res << %{<img src="#{dot}" usemap="#map" border="0" alt="#{dot}">}
+ return res
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rdoc/dot/dot.rb b/ruby_1_8_5/lib/rdoc/dot/dot.rb
new file mode 100644
index 0000000000..6dbb7cb237
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/dot/dot.rb
@@ -0,0 +1,255 @@
+module DOT
+
+ # these glogal vars are used to make nice graph source
+ $tab = ' '
+ $tab2 = $tab * 2
+
+ # if we don't like 4 spaces, we can change it any time
+ def change_tab( t )
+ $tab = t
+ $tab2 = t * 2
+ end
+
+ # options for node declaration
+ NODE_OPTS = [
+ 'bgcolor',
+ 'color',
+ 'fontcolor',
+ 'fontname',
+ 'fontsize',
+ 'height',
+ 'width',
+ 'label',
+ 'layer',
+ 'rank',
+ 'shape',
+ 'shapefile',
+ 'style',
+ 'URL',
+ ]
+
+ # options for edge declaration
+ EDGE_OPTS = [
+ 'color',
+ 'decorate',
+ 'dir',
+ 'fontcolor',
+ 'fontname',
+ 'fontsize',
+ 'id',
+ 'label',
+ 'layer',
+ 'lhead',
+ 'ltail',
+ 'minlen',
+ 'style',
+ 'weight'
+ ]
+
+ # options for graph declaration
+ GRAPH_OPTS = [
+ 'bgcolor',
+ 'center',
+ 'clusterrank',
+ 'color',
+ 'compound',
+ 'concentrate',
+ 'fillcolor',
+ 'fontcolor',
+ 'fontname',
+ 'fontsize',
+ 'label',
+ 'layerseq',
+ 'margin',
+ 'mclimit',
+ 'nodesep',
+ 'nslimit',
+ 'ordering',
+ 'orientation',
+ 'page',
+ 'rank',
+ 'rankdir',
+ 'ranksep',
+ 'ratio',
+ 'size',
+ 'style',
+ 'URL'
+ ]
+
+ # a root class for any element in dot notation
+ class DOTSimpleElement
+ attr_accessor :name
+
+ def initialize( params = {} )
+ @label = params['name'] ? params['name'] : ''
+ end
+
+ def to_s
+ @name
+ end
+ end
+
+ # an element that has options ( node, edge or graph )
+ class DOTElement < DOTSimpleElement
+ #attr_reader :parent
+ attr_accessor :name, :options
+
+ def initialize( params = {}, option_list = [] )
+ super( params )
+ @name = params['name'] ? params['name'] : nil
+ @parent = params['parent'] ? params['parent'] : nil
+ @options = {}
+ option_list.each{ |i|
+ @options[i] = params[i] if params[i]
+ }
+ @options['label'] ||= @name if @name != 'node'
+ end
+
+ def each_option
+ @options.each{ |i| yield i }
+ end
+
+ def each_option_pair
+ @options.each_pair{ |key, val| yield key, val }
+ end
+
+ #def parent=( thing )
+ # @parent.delete( self ) if defined?( @parent ) and @parent
+ # @parent = thing
+ #end
+ end
+
+
+ # this is used when we build nodes that have shape=record
+ # ports don't have options :)
+ class DOTPort < DOTSimpleElement
+ attr_accessor :label
+
+ def initialize( params = {} )
+ super( params )
+ @name = params['label'] ? params['label'] : ''
+ end
+ def to_s
+ ( @name && @name != "" ? "<#{@name}>" : "" ) + "#{@label}"
+ end
+ end
+
+ # node element
+ class DOTNode < DOTElement
+
+ def initialize( params = {}, option_list = NODE_OPTS )
+ super( params, option_list )
+ @ports = params['ports'] ? params['ports'] : []
+ end
+
+ def each_port
+ @ports.each{ |i| yield i }
+ end
+
+ def << ( thing )
+ @ports << thing
+ end
+
+ def push ( thing )
+ @ports.push( thing )
+ end
+
+ def pop
+ @ports.pop
+ end
+
+ def to_s( t = '' )
+
+ label = @options['shape'] != 'record' && @ports.length == 0 ?
+ @options['label'] ?
+ t + $tab + "label = \"#{@options['label']}\"\n" :
+ '' :
+ t + $tab + 'label = "' + " \\\n" +
+ t + $tab2 + "#{@options['label']}| \\\n" +
+ @ports.collect{ |i|
+ t + $tab2 + i.to_s
+ }.join( "| \\\n" ) + " \\\n" +
+ t + $tab + '"' + "\n"
+
+ t + "#{@name} [\n" +
+ @options.to_a.collect{ |i|
+ i[1] && i[0] != 'label' ?
+ t + $tab + "#{i[0]} = #{i[1]}" : nil
+ }.compact.join( ",\n" ) + ( label != '' ? ",\n" : "\n" ) +
+ label +
+ t + "]\n"
+ end
+ end
+
+ # subgraph element is the same to graph, but has another header in dot
+ # notation
+ class DOTSubgraph < DOTElement
+
+ def initialize( params = {}, option_list = GRAPH_OPTS )
+ super( params, option_list )
+ @nodes = params['nodes'] ? params['nodes'] : []
+ @dot_string = 'subgraph'
+ end
+
+ def each_node
+ @nodes.each{ |i| yield i }
+ end
+
+ def << ( thing )
+ @nodes << thing
+ end
+
+ def push( thing )
+ @nodes.push( thing )
+ end
+
+ def pop
+ @nodes.pop
+ end
+
+ def to_s( t = '' )
+ hdr = t + "#{@dot_string} #{@name} {\n"
+
+ options = @options.to_a.collect{ |name, val|
+ val && name != 'label' ?
+ t + $tab + "#{name} = #{val}" :
+ name ? t + $tab + "#{name} = \"#{val}\"" : nil
+ }.compact.join( "\n" ) + "\n"
+
+ nodes = @nodes.collect{ |i|
+ i.to_s( t + $tab )
+ }.join( "\n" ) + "\n"
+ hdr + options + nodes + t + "}\n"
+ end
+ end
+
+ # this is graph
+ class DOTDigraph < DOTSubgraph
+ def initialize( params = {}, option_list = GRAPH_OPTS )
+ super( params, option_list )
+ @dot_string = 'digraph'
+ end
+ end
+
+ # this is edge
+ class DOTEdge < DOTElement
+ attr_accessor :from, :to
+ def initialize( params = {}, option_list = EDGE_OPTS )
+ super( params, option_list )
+ @from = params['from'] ? params['from'] : nil
+ @to = params['to'] ? params['to'] : nil
+ end
+
+ def to_s( t = '' )
+ t + "#{@from} -> #{to} [\n" +
+ @options.to_a.collect{ |i|
+ i[1] && i[0] != 'label' ?
+ t + $tab + "#{i[0]} = #{i[1]}" :
+ i[1] ? t + $tab + "#{i[0]} = \"#{i[1]}\"" : nil
+ }.compact.join( "\n" ) + "\n" + t + "]\n"
+ end
+ end
+end
+
+
+
diff --git a/ruby_1_8_5/lib/rdoc/generators/chm_generator.rb b/ruby_1_8_5/lib/rdoc/generators/chm_generator.rb
new file mode 100644
index 0000000000..51eeda8dd1
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/generators/chm_generator.rb
@@ -0,0 +1,112 @@
+require 'rdoc/generators/html_generator'
+
+module Generators
+
+ class CHMGenerator < HTMLGenerator
+
+ HHC_PATH = "c:/Program Files/HTML Help Workshop/hhc.exe"
+
+ # Standard generator factory
+ def CHMGenerator.for(options)
+ CHMGenerator.new(options)
+ end
+
+
+ def initialize(*args)
+ super
+ @op_name = @options.op_name || "rdoc"
+ check_for_html_help_workshop
+ end
+
+ def check_for_html_help_workshop
+ stat = File.stat(HHC_PATH)
+ rescue
+ $stderr <<
+ "\n.chm output generation requires that Microsoft's Html Help\n" <<
+ "Workshop is installed. RDoc looks for it in:\n\n " <<
+ HHC_PATH <<
+ "\n\nYou can download a copy for free from:\n\n" <<
+ " http://msdn.microsoft.com/library/default.asp?" <<
+ "url=/library/en-us/htmlhelp/html/hwMicrosoftHTMLHelpDownloads.asp\n\n"
+
+ exit 99
+ end
+
+ # Generate the html as normal, then wrap it
+ # in a help project
+ def generate(info)
+ super
+ @project_name = @op_name + ".hhp"
+ create_help_project
+ end
+
+ # The project contains the project file, a table of contents
+ # and an index
+ def create_help_project
+ create_project_file
+ create_contents_and_index
+ compile_project
+ end
+
+ # The project file links together all the various
+ # files that go to make up the help.
+
+ def create_project_file
+ template = TemplatePage.new(RDoc::Page::HPP_FILE)
+ values = { "title" => @options.title, "opname" => @op_name }
+ files = []
+ @files.each do |f|
+ files << { "html_file_name" => f.path }
+ end
+
+ values['all_html_files'] = files
+
+ File.open(@project_name, "w") do |f|
+ template.write_html_on(f, values)
+ end
+ end
+
+ # The contents is a list of all files and modules.
+ # For each we include as sub-entries the list
+ # of methods they contain. As we build the contents
+ # we also build an index file
+
+ def create_contents_and_index
+ contents = []
+ index = []
+
+ (@files+@classes).sort.each do |entry|
+ content_entry = { "c_name" => entry.name, "ref" => entry.path }
+ index << { "name" => entry.name, "aref" => entry.path }
+
+ internals = []
+
+ methods = entry.build_method_summary_list(entry.path)
+
+ content_entry["methods"] = methods unless methods.empty?
+ contents << content_entry
+ index.concat methods
+ end
+
+ values = { "contents" => contents }
+ template = TemplatePage.new(RDoc::Page::CONTENTS)
+ File.open("contents.hhc", "w") do |f|
+ template.write_html_on(f, values)
+ end
+
+ values = { "index" => index }
+ template = TemplatePage.new(RDoc::Page::CHM_INDEX)
+ File.open("index.hhk", "w") do |f|
+ template.write_html_on(f, values)
+ end
+ end
+
+ # Invoke the windows help compiler to compiler the project
+ def compile_project
+ system(HHC_PATH, @project_name)
+ end
+
+ end
+
+
+end
diff --git a/ruby_1_8_5/lib/rdoc/generators/html_generator.rb b/ruby_1_8_5/lib/rdoc/generators/html_generator.rb
new file mode 100644
index 0000000000..1f9b808e8d
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/generators/html_generator.rb
@@ -0,0 +1,1509 @@
+# We're responsible for generating all the HTML files
+# from the object tree defined in code_objects.rb. We
+# generate:
+#
+# [files] an html file for each input file given. These
+# input files appear as objects of class
+# TopLevel
+#
+# [classes] an html file for each class or module encountered.
+# These classes are not grouped by file: if a file
+# contains four classes, we'll generate an html
+# file for the file itself, and four html files
+# for the individual classes.
+#
+# [indices] we generate three indices for files, classes,
+# and methods. These are displayed in a browser
+# like window with three index panes across the
+# top and the selected description below
+#
+# Method descriptions appear in whatever entity (file, class,
+# or module) that contains them.
+#
+# We generate files in a structure below a specified subdirectory,
+# normally +doc+.
+#
+# opdir
+# |
+# |___ files
+# | |__ per file summaries
+# |
+# |___ classes
+# |__ per class/module descriptions
+#
+# HTML is generated using the Template class.
+#
+
+require 'ftools'
+
+require 'rdoc/options'
+require 'rdoc/template'
+require 'rdoc/markup/simple_markup'
+require 'rdoc/markup/simple_markup/to_html'
+require 'cgi'
+
+module Generators
+
+ # Name of sub-direcories that hold file and class/module descriptions
+
+ FILE_DIR = "files"
+ CLASS_DIR = "classes"
+ CSS_NAME = "rdoc-style.css"
+
+
+ ##
+ # Build a hash of all items that can be cross-referenced.
+ # This is used when we output required and included names:
+ # if the names appear in this hash, we can generate
+ # an html cross reference to the appropriate description.
+ # We also use this when parsing comment blocks: any decorated
+ # words matching an entry in this list are hyperlinked.
+
+ class AllReferences
+ @@refs = {}
+
+ def AllReferences::reset
+ @@refs = {}
+ end
+
+ def AllReferences.add(name, html_class)
+ @@refs[name] = html_class
+ end
+
+ def AllReferences.[](name)
+ @@refs[name]
+ end
+
+ def AllReferences.keys
+ @@refs.keys
+ end
+ end
+
+
+ ##
+ # Subclass of the SM::ToHtml class that supports looking
+ # up words in the AllReferences list. Those that are
+ # found (like AllReferences in this comment) will
+ # be hyperlinked
+
+ class HyperlinkHtml < SM::ToHtml
+ # We need to record the html path of our caller so we can generate
+ # correct relative paths for any hyperlinks that we find
+ def initialize(from_path, context)
+ super()
+ @from_path = from_path
+
+ @parent_name = context.parent_name
+ @parent_name += "::" if @parent_name
+ @context = context
+ end
+
+ # We're invoked when any text matches the CROSSREF pattern
+ # (defined in MarkUp). If we fine the corresponding reference,
+ # generate a hyperlink. If the name we're looking for contains
+ # no punctuation, we look for it up the module/class chain. For
+ # example, HyperlinkHtml is found, even without the Generators::
+ # prefix, because we look for it in module Generators first.
+
+ def handle_special_CROSSREF(special)
+ name = special.text
+ if name[0,1] == '#'
+ lookup = name[1..-1]
+ name = lookup unless Options.instance.show_hash
+ else
+ lookup = name
+ end
+
+ # Find class, module, or method in class or module.
+ if /([A-Z]\w*)[.\#](\w+[!?=]?)/ =~ lookup
+ container = $1
+ method = $2
+ ref = @context.find_symbol(container, method)
+ elsif /([A-Za-z]\w*)[.\#](\w+(\([\.\w+\*\/\+\-\=\<\>]+\))?)/ =~ lookup
+ container = $1
+ method = $2
+ ref = @context.find_symbol(container, method)
+ else
+ ref = @context.find_symbol(lookup)
+ end
+
+ if ref and ref.document_self
+ "<a href=\"#{ref.as_href(@from_path)}\">#{name}</a>"
+ else
+ name
+ end
+ end
+
+
+ # Generate a hyperlink for url, labeled with text. Handle the
+ # special cases for img: and link: described under handle_special_HYPEDLINK
+ def gen_url(url, text)
+ if url =~ /([A-Za-z]+):(.*)/
+ type = $1
+ path = $2
+ else
+ type = "http"
+ path = url
+ url = "http://#{url}"
+ end
+
+ if type == "link"
+ if path[0,1] == '#' # is this meaningful?
+ url = path
+ else
+ url = HTMLGenerator.gen_url(@from_path, path)
+ end
+ end
+
+ if (type == "http" || type == "link") &&
+ url =~ /\.(gif|png|jpg|jpeg|bmp)$/
+
+ "<img src=\"#{url}\" />"
+ else
+ "<a href=\"#{url}\">#{text.sub(%r{^#{type}:/*}, '')}</a>"
+ end
+ end
+
+ # And we're invoked with a potential external hyperlink mailto:
+ # just gets inserted. http: links are checked to see if they
+ # reference an image. If so, that image gets inserted using an
+ # <img> tag. Otherwise a conventional <a href> is used. We also
+ # support a special type of hyperlink, link:, which is a reference
+ # to a local file whose path is relative to the --op directory.
+
+ def handle_special_HYPERLINK(special)
+ url = special.text
+ gen_url(url, url)
+ end
+
+ # HEre's a hypedlink where the label is different to the URL
+ # <label>[url]
+ #
+
+ def handle_special_TIDYLINK(special)
+ text = special.text
+# unless text =~ /(\S+)\[(.*?)\]/
+ unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/
+ return text
+ end
+ label = $1
+ url = $2
+ gen_url(url, label)
+ end
+
+ end
+
+
+
+ #####################################################################
+ #
+ # Handle common markup tasks for the various Html classes
+ #
+
+ module MarkUp
+
+ # Convert a string in markup format into HTML. We keep a cached
+ # SimpleMarkup object lying around after the first time we're
+ # called per object.
+
+ def markup(str, remove_para=false)
+ return '' unless str
+ unless defined? @markup
+ @markup = SM::SimpleMarkup.new
+
+ # class names, variable names, or instance variables
+ @markup.add_special(/(
+ \w+(::\w+)*[.\#]\w+(\([\.\w+\*\/\+\-\=\<\>]+\))? # A::B.meth(**) (for operator in Fortran95)
+ | \#\w+(\([.\w\*\/\+\-\=\<\>]+\))? # meth(**) (for operator in Fortran95)
+ | \b([A-Z]\w*(::\w+)*[.\#]\w+) # A::B.meth
+ | \b([A-Z]\w+(::\w+)*) # A::B..
+ | \#\w+[!?=]? # #meth_name
+ | \b\w+([_\/\.]+\w+)*[!?=]? # meth_name
+ )/x,
+ :CROSSREF)
+
+ # external hyperlinks
+ @markup.add_special(/((link:|https?:|mailto:|ftp:|www\.)\S+\w)/, :HYPERLINK)
+
+ # and links of the form <text>[<url>]
+ @markup.add_special(/(((\{.*?\})|\b\S+?)\[\S+?\.\S+?\])/, :TIDYLINK)
+# @markup.add_special(/\b(\S+?\[\S+?\.\S+?\])/, :TIDYLINK)
+
+ end
+ unless defined? @html_formatter
+ @html_formatter = HyperlinkHtml.new(self.path, self)
+ end
+
+ # Convert leading comment markers to spaces, but only
+ # if all non-blank lines have them
+
+ if str =~ /^(?>\s*)[^\#]/
+ content = str
+ else
+ content = str.gsub(/^\s*(#+)/) { $1.tr('#',' ') }
+ end
+
+ res = @markup.convert(content, @html_formatter)
+ if remove_para
+ res.sub!(/^<p>/, '')
+ res.sub!(/<\/p>$/, '')
+ end
+ res
+ end
+
+ # Qualify a stylesheet URL; if if +css_name+ does not begin with '/' or
+ # 'http[s]://', prepend a prefix relative to +path+. Otherwise, return it
+ # unmodified.
+
+ def style_url(path, css_name=nil)
+# $stderr.puts "style_url( #{path.inspect}, #{css_name.inspect} )"
+ css_name ||= CSS_NAME
+ if %r{^(https?:/)?/} =~ css_name
+ return css_name
+ else
+ return HTMLGenerator.gen_url(path, css_name)
+ end
+ end
+
+ # Build a webcvs URL with the given 'url' argument. URLs with a '%s' in them
+ # get the file's path sprintfed into them; otherwise they're just catenated
+ # together.
+
+ def cvs_url(url, full_path)
+ if /%s/ =~ url
+ return sprintf( url, full_path )
+ else
+ return url + full_path
+ end
+ end
+ end
+
+
+ #####################################################################
+ #
+ # A Context is built by the parser to represent a container: contexts
+ # hold classes, modules, methods, require lists and include lists.
+ # ClassModule and TopLevel are the context objects we process here
+ #
+ class ContextUser
+
+ include MarkUp
+
+ attr_reader :context
+
+ def initialize(context, options)
+ @context = context
+ @options = options
+ end
+
+ # convenience method to build a hyperlink
+ def href(link, cls, name)
+ %{<a href="#{link}" class="#{cls}">#{name}</a>} #"
+ end
+
+ # return a reference to outselves to be used as an href=
+ # the form depends on whether we're all in one file
+ # or in multiple files
+
+ def as_href(from_path)
+ if @options.all_one_file
+ "#" + path
+ else
+ HTMLGenerator.gen_url(from_path, path)
+ end
+ end
+
+ # Create a list of HtmlMethod objects for each method
+ # in the corresponding context object. If the @options.show_all
+ # variable is set (corresponding to the <tt>--all</tt> option,
+ # we include all methods, otherwise just the public ones.
+
+ def collect_methods
+ list = @context.method_list
+ unless @options.show_all
+ list = list.find_all {|m| m.visibility == :public || m.visibility == :protected || m.force_documentation }
+ end
+ @methods = list.collect {|m| HtmlMethod.new(m, self, @options) }
+ end
+
+ # Build a summary list of all the methods in this context
+ def build_method_summary_list(path_prefix="")
+ collect_methods unless @methods
+ meths = @methods.sort
+ res = []
+ meths.each do |meth|
+ res << {
+ "name" => CGI.escapeHTML(meth.name),
+ "aref" => "#{path_prefix}\##{meth.aref}"
+ }
+ end
+ res
+ end
+
+
+ # Build a list of aliases for which we couldn't find a
+ # corresponding method
+ def build_alias_summary_list(section)
+ values = []
+ @context.aliases.each do |al|
+ next unless al.section == section
+ res = {
+ 'old_name' => al.old_name,
+ 'new_name' => al.new_name,
+ }
+ if al.comment && !al.comment.empty?
+ res['desc'] = markup(al.comment, true)
+ end
+ values << res
+ end
+ values
+ end
+
+ # Build a list of constants
+ def build_constants_summary_list(section)
+ values = []
+ @context.constants.each do |co|
+ next unless co.section == section
+ res = {
+ 'name' => co.name,
+ 'value' => CGI.escapeHTML(co.value)
+ }
+ res['desc'] = markup(co.comment, true) if co.comment && !co.comment.empty?
+ values << res
+ end
+ values
+ end
+
+ def build_requires_list(context)
+ potentially_referenced_list(context.requires) {|fn| [fn + ".rb"] }
+ end
+
+ def build_include_list(context)
+ potentially_referenced_list(context.includes)
+ end
+
+ # Build a list from an array of <i>Htmlxxx</i> items. Look up each
+ # in the AllReferences hash: if we find a corresponding entry,
+ # we generate a hyperlink to it, otherwise just output the name.
+ # However, some names potentially need massaging. For example,
+ # you may require a Ruby file without the .rb extension,
+ # but the file names we know about may have it. To deal with
+ # this, we pass in a block which performs the massaging,
+ # returning an array of alternative names to match
+
+ def potentially_referenced_list(array)
+ res = []
+ array.each do |i|
+ ref = AllReferences[i.name]
+# if !ref
+# container = @context.parent
+# while !ref && container
+# name = container.name + "::" + i.name
+# ref = AllReferences[name]
+# container = container.parent
+# end
+# end
+
+ ref = @context.find_symbol(i.name)
+ ref = ref.viewer if ref
+
+ if !ref && block_given?
+ possibles = yield(i.name)
+ while !ref and !possibles.empty?
+ ref = AllReferences[possibles.shift]
+ end
+ end
+ h_name = CGI.escapeHTML(i.name)
+ if ref and ref.document_self
+ path = url(ref.path)
+ res << { "name" => h_name, "aref" => path }
+ else
+ res << { "name" => h_name }
+ end
+ end
+ res
+ end
+
+ # Build an array of arrays of method details. The outer array has up
+ # to six entries, public, private, and protected for both class
+ # methods, the other for instance methods. The inner arrays contain
+ # a hash for each method
+
+ def build_method_detail_list(section)
+ outer = []
+
+ methods = @methods.sort
+ for singleton in [true, false]
+ for vis in [ :public, :protected, :private ]
+ res = []
+ methods.each do |m|
+ if m.section == section and
+ m.document_self and
+ m.visibility == vis and
+ m.singleton == singleton
+ row = {}
+ if m.call_seq
+ row["callseq"] = m.call_seq.gsub(/->/, '&rarr;')
+ else
+ row["name"] = CGI.escapeHTML(m.name)
+ row["params"] = m.params
+ end
+ desc = m.description.strip
+ row["m_desc"] = desc unless desc.empty?
+ row["aref"] = m.aref
+ row["visibility"] = m.visibility.to_s
+
+ alias_names = []
+ m.aliases.each do |other|
+ if other.viewer # won't be if the alias is private
+ alias_names << {
+ 'name' => other.name,
+ 'aref' => other.viewer.as_href(path)
+ }
+ end
+ end
+ unless alias_names.empty?
+ row["aka"] = alias_names
+ end
+
+ if @options.inline_source
+ code = m.source_code
+ row["sourcecode"] = code if code
+ else
+ code = m.src_url
+ if code
+ row["codeurl"] = code
+ row["imgurl"] = m.img_url
+ end
+ end
+ res << row
+ end
+ end
+ if res.size > 0
+ outer << {
+ "type" => vis.to_s.capitalize,
+ "category" => singleton ? "Class" : "Instance",
+ "methods" => res
+ }
+ end
+ end
+ end
+ outer
+ end
+
+ # Build the structured list of classes and modules contained
+ # in this context.
+
+ def build_class_list(level, from, section, infile=nil)
+ res = ""
+ prefix = "&nbsp;&nbsp;::" * level;
+
+ from.modules.sort.each do |mod|
+ next unless mod.section == section
+ next if infile && !mod.defined_in?(infile)
+ if mod.document_self
+ res <<
+ prefix <<
+ "Module " <<
+ href(url(mod.viewer.path), "link", mod.full_name) <<
+ "<br />\n" <<
+ build_class_list(level + 1, mod, section, infile)
+ end
+ end
+
+ from.classes.sort.each do |cls|
+ next unless cls.section == section
+ next if infile && !cls.defined_in?(infile)
+ if cls.document_self
+ res <<
+ prefix <<
+ "Class " <<
+ href(url(cls.viewer.path), "link", cls.full_name) <<
+ "<br />\n" <<
+ build_class_list(level + 1, cls, section, infile)
+ end
+ end
+
+ res
+ end
+
+ def url(target)
+ HTMLGenerator.gen_url(path, target)
+ end
+
+ def aref_to(target)
+ if @options.all_one_file
+ "#" + target
+ else
+ url(target)
+ end
+ end
+
+ def document_self
+ @context.document_self
+ end
+
+ def diagram_reference(diagram)
+ res = diagram.gsub(/((?:src|href)=")(.*?)"/) {
+ $1 + url($2) + '"'
+ }
+ res
+ end
+
+
+ # Find a symbol in ourselves or our parent
+ def find_symbol(symbol, method=nil)
+ res = @context.find_symbol(symbol, method)
+ if res
+ res = res.viewer
+ end
+ res
+ end
+
+ # create table of contents if we contain sections
+
+ def add_table_of_sections
+ toc = []
+ @context.sections.each do |section|
+ if section.title
+ toc << {
+ 'secname' => section.title,
+ 'href' => section.sequence
+ }
+ end
+ end
+
+ @values['toc'] = toc unless toc.empty?
+ end
+
+
+ end
+
+ #####################################################################
+ #
+ # Wrap a ClassModule context
+
+ class HtmlClass < ContextUser
+
+ attr_reader :path
+
+ def initialize(context, html_file, prefix, options)
+ super(context, options)
+
+ @html_file = html_file
+ @is_module = context.is_module?
+ @values = {}
+
+ context.viewer = self
+
+ if options.all_one_file
+ @path = context.full_name
+ else
+ @path = http_url(context.full_name, prefix)
+ end
+
+ collect_methods
+
+ AllReferences.add(name, self)
+ end
+
+ # return the relative file name to store this class in,
+ # which is also its url
+ def http_url(full_name, prefix)
+ path = full_name.dup
+ if path['<<']
+ path.gsub!(/<<\s*(\w*)/) { "from-#$1" }
+ end
+ File.join(prefix, path.split("::")) + ".html"
+ end
+
+
+ def name
+ @context.full_name
+ end
+
+ def parent_name
+ @context.parent.full_name
+ end
+
+ def index_name
+ name
+ end
+
+ def write_on(f)
+ value_hash
+ template = TemplatePage.new(RDoc::Page::BODY,
+ RDoc::Page::CLASS_PAGE,
+ RDoc::Page::METHOD_LIST)
+ template.write_html_on(f, @values)
+ end
+
+ def value_hash
+ class_attribute_values
+ add_table_of_sections
+
+ @values["charset"] = @options.charset
+ @values["style_url"] = style_url(path, @options.css)
+
+ d = markup(@context.comment)
+ @values["description"] = d unless d.empty?
+
+ ml = build_method_summary_list
+ @values["methods"] = ml unless ml.empty?
+
+ il = build_include_list(@context)
+ @values["includes"] = il unless il.empty?
+
+ @values["sections"] = @context.sections.map do |section|
+
+ secdata = {
+ "sectitle" => section.title,
+ "secsequence" => section.sequence,
+ "seccomment" => markup(section.comment)
+ }
+
+ al = build_alias_summary_list(section)
+ secdata["aliases"] = al unless al.empty?
+
+ co = build_constants_summary_list(section)
+ secdata["constants"] = co unless co.empty?
+
+ al = build_attribute_list(section)
+ secdata["attributes"] = al unless al.empty?
+
+ cl = build_class_list(0, @context, section)
+ secdata["classlist"] = cl unless cl.empty?
+
+ mdl = build_method_detail_list(section)
+ secdata["method_list"] = mdl unless mdl.empty?
+
+ secdata
+ end
+
+ @values
+ end
+
+ def build_attribute_list(section)
+ atts = @context.attributes.sort
+ res = []
+ atts.each do |att|
+ next unless att.section == section
+ if att.visibility == :public || att.visibility == :protected || @options.show_all
+ entry = {
+ "name" => CGI.escapeHTML(att.name),
+ "rw" => att.rw,
+ "a_desc" => markup(att.comment, true)
+ }
+ unless att.visibility == :public || att.visibility == :protected
+ entry["rw"] << "-"
+ end
+ res << entry
+ end
+ end
+ res
+ end
+
+ def class_attribute_values
+ h_name = CGI.escapeHTML(name)
+
+ @values["classmod"] = @is_module ? "Module" : "Class"
+ @values["title"] = "#{@values['classmod']}: #{h_name}"
+
+ c = @context
+ c = c.parent while c and !c.diagram
+ if c && c.diagram
+ @values["diagram"] = diagram_reference(c.diagram)
+ end
+
+ @values["full_name"] = h_name
+
+ parent_class = @context.superclass
+
+ if parent_class
+ @values["parent"] = CGI.escapeHTML(parent_class)
+
+ if parent_name
+ lookup = parent_name + "::" + parent_class
+ else
+ lookup = parent_class
+ end
+
+ parent_url = AllReferences[lookup] || AllReferences[parent_class]
+
+ if parent_url and parent_url.document_self
+ @values["par_url"] = aref_to(parent_url.path)
+ end
+ end
+
+ files = []
+ @context.in_files.each do |f|
+ res = {}
+ full_path = CGI.escapeHTML(f.file_absolute_name)
+
+ res["full_path"] = full_path
+ res["full_path_url"] = aref_to(f.viewer.path) if f.document_self
+
+ if @options.webcvs
+ res["cvsurl"] = cvs_url( @options.webcvs, full_path )
+ end
+
+ files << res
+ end
+
+ @values['infiles'] = files
+ end
+
+ def <=>(other)
+ self.name <=> other.name
+ end
+
+ end
+
+ #####################################################################
+ #
+ # Handles the mapping of a file's information to HTML. In reality,
+ # a file corresponds to a +TopLevel+ object, containing modules,
+ # classes, and top-level methods. In theory it _could_ contain
+ # attributes and aliases, but we ignore these for now.
+
+ class HtmlFile < ContextUser
+
+ attr_reader :path
+ attr_reader :name
+
+ def initialize(context, options, file_dir)
+ super(context, options)
+
+ @values = {}
+
+ if options.all_one_file
+ @path = filename_to_label
+ else
+ @path = http_url(file_dir)
+ end
+
+ @name = @context.file_relative_name
+
+ collect_methods
+ AllReferences.add(name, self)
+ context.viewer = self
+ end
+
+ def http_url(file_dir)
+ File.join(file_dir, @context.file_relative_name.tr('.', '_')) +
+ ".html"
+ end
+
+ def filename_to_label
+ @context.file_relative_name.gsub(/%|\/|\?|\#/) {|s| '%' + ("%x" % s[0]) }
+ end
+
+ def index_name
+ name
+ end
+
+ def parent_name
+ nil
+ end
+
+ def value_hash
+ file_attribute_values
+ add_table_of_sections
+
+ @values["charset"] = @options.charset
+ @values["href"] = path
+ @values["style_url"] = style_url(path, @options.css)
+
+ if @context.comment
+ d = markup(@context.comment)
+ @values["description"] = d if d.size > 0
+ end
+
+ ml = build_method_summary_list
+ @values["methods"] = ml unless ml.empty?
+
+ il = build_include_list(@context)
+ @values["includes"] = il unless il.empty?
+
+ rl = build_requires_list(@context)
+ @values["requires"] = rl unless rl.empty?
+
+ if @options.promiscuous
+ file_context = nil
+ else
+ file_context = @context
+ end
+
+
+ @values["sections"] = @context.sections.map do |section|
+
+ secdata = {
+ "sectitle" => section.title,
+ "secsequence" => section.sequence,
+ "seccomment" => markup(section.comment)
+ }
+
+ cl = build_class_list(0, @context, section, file_context)
+ @values["classlist"] = cl unless cl.empty?
+
+ mdl = build_method_detail_list(section)
+ secdata["method_list"] = mdl unless mdl.empty?
+
+ al = build_alias_summary_list(section)
+ secdata["aliases"] = al unless al.empty?
+
+ co = build_constants_summary_list(section)
+ @values["constants"] = co unless co.empty?
+
+ secdata
+ end
+
+ @values
+ end
+
+ def write_on(f)
+ value_hash
+ template = TemplatePage.new(RDoc::Page::BODY,
+ RDoc::Page::FILE_PAGE,
+ RDoc::Page::METHOD_LIST)
+ template.write_html_on(f, @values)
+ end
+
+ def file_attribute_values
+ full_path = @context.file_absolute_name
+ short_name = File.basename(full_path)
+
+ @values["title"] = CGI.escapeHTML("File: #{short_name}")
+
+ if @context.diagram
+ @values["diagram"] = diagram_reference(@context.diagram)
+ end
+
+ @values["short_name"] = CGI.escapeHTML(short_name)
+ @values["full_path"] = CGI.escapeHTML(full_path)
+ @values["dtm_modified"] = @context.file_stat.mtime.to_s
+
+ if @options.webcvs
+ @values["cvsurl"] = cvs_url( @options.webcvs, @values["full_path"] )
+ end
+ end
+
+ def <=>(other)
+ self.name <=> other.name
+ end
+ end
+
+ #####################################################################
+
+ class HtmlMethod
+ include MarkUp
+
+ attr_reader :context
+ attr_reader :src_url
+ attr_reader :img_url
+ attr_reader :source_code
+
+ @@seq = "M000000"
+
+ @@all_methods = []
+
+ def HtmlMethod::reset
+ @@all_methods = []
+ end
+
+ def initialize(context, html_class, options)
+ @context = context
+ @html_class = html_class
+ @options = options
+ @@seq = @@seq.succ
+ @seq = @@seq
+ @@all_methods << self
+
+ context.viewer = self
+
+ if (ts = @context.token_stream)
+ @source_code = markup_code(ts)
+ unless @options.inline_source
+ @src_url = create_source_code_file(@source_code)
+ @img_url = HTMLGenerator.gen_url(path, 'source.png')
+ end
+ end
+
+ AllReferences.add(name, self)
+ end
+
+ # return a reference to outselves to be used as an href=
+ # the form depends on whether we're all in one file
+ # or in multiple files
+
+ def as_href(from_path)
+ if @options.all_one_file
+ "#" + path
+ else
+ HTMLGenerator.gen_url(from_path, path)
+ end
+ end
+
+ def name
+ @context.name
+ end
+
+ def section
+ @context.section
+ end
+
+ def index_name
+ "#{@context.name} (#{@html_class.name})"
+ end
+
+ def parent_name
+ if @context.parent.parent
+ @context.parent.parent.full_name
+ else
+ nil
+ end
+ end
+
+ def aref
+ @seq
+ end
+
+ def path
+ if @options.all_one_file
+ aref
+ else
+ @html_class.path + "#" + aref
+ end
+ end
+
+ def description
+ markup(@context.comment)
+ end
+
+ def visibility
+ @context.visibility
+ end
+
+ def singleton
+ @context.singleton
+ end
+
+ def call_seq
+ cs = @context.call_seq
+ if cs
+ cs.gsub(/\n/, "<br />\n")
+ else
+ nil
+ end
+ end
+
+ def params
+ # params coming from a call-seq in 'C' will start with the
+ # method name
+ p = @context.params
+ if p !~ /^\w/
+ p = @context.params.gsub(/\s*\#.*/, '')
+ p = p.tr("\n", " ").squeeze(" ")
+ p = "(" + p + ")" unless p[0] == ?(
+
+ if (block = @context.block_params)
+ # If this method has explicit block parameters, remove any
+ # explicit &block
+
+ p.sub!(/,?\s*&\w+/, '')
+
+ block.gsub!(/\s*\#.*/, '')
+ block = block.tr("\n", " ").squeeze(" ")
+ if block[0] == ?(
+ block.sub!(/^\(/, '').sub!(/\)/, '')
+ end
+ p << " {|#{block.strip}| ...}"
+ end
+ end
+ CGI.escapeHTML(p)
+ end
+
+ def create_source_code_file(code_body)
+ meth_path = @html_class.path.sub(/\.html$/, '.src')
+ File.makedirs(meth_path)
+ file_path = File.join(meth_path, @seq) + ".html"
+
+ template = TemplatePage.new(RDoc::Page::SRC_PAGE)
+ File.open(file_path, "w") do |f|
+ values = {
+ 'title' => CGI.escapeHTML(index_name),
+ 'code' => code_body,
+ 'style_url' => style_url(file_path, @options.css),
+ 'charset' => @options.charset
+ }
+ template.write_html_on(f, values)
+ end
+ HTMLGenerator.gen_url(path, file_path)
+ end
+
+ def HtmlMethod.all_methods
+ @@all_methods
+ end
+
+ def <=>(other)
+ @context <=> other.context
+ end
+
+ ##
+ # Given a sequence of source tokens, mark up the source code
+ # to make it look purty.
+
+
+ def markup_code(tokens)
+ src = ""
+ tokens.each do |t|
+ next unless t
+ # p t.class
+# style = STYLE_MAP[t.class]
+ style = case t
+ when RubyToken::TkCONSTANT then "ruby-constant"
+ when RubyToken::TkKW then "ruby-keyword kw"
+ when RubyToken::TkIVAR then "ruby-ivar"
+ when RubyToken::TkOp then "ruby-operator"
+ when RubyToken::TkId then "ruby-identifier"
+ when RubyToken::TkNode then "ruby-node"
+ when RubyToken::TkCOMMENT then "ruby-comment cmt"
+ when RubyToken::TkREGEXP then "ruby-regexp re"
+ when RubyToken::TkSTRING then "ruby-value str"
+ when RubyToken::TkVal then "ruby-value"
+ else
+ nil
+ end
+
+ text = CGI.escapeHTML(t.text)
+
+ if style
+ src << "<span class=\"#{style}\">#{text}</span>"
+ else
+ src << text
+ end
+ end
+
+ add_line_numbers(src) if Options.instance.include_line_numbers
+ src
+ end
+
+ # we rely on the fact that the first line of a source code
+ # listing has
+ # # File xxxxx, line dddd
+
+ def add_line_numbers(src)
+ if src =~ /\A.*, line (\d+)/
+ first = $1.to_i - 1
+ last = first + src.count("\n")
+ size = last.to_s.length
+ real_fmt = "%#{size}d: "
+ fmt = " " * (size+2)
+ src.gsub!(/^/) do
+ res = sprintf(fmt, first)
+ first += 1
+ fmt = real_fmt
+ res
+ end
+ end
+ end
+
+ def document_self
+ @context.document_self
+ end
+
+ def aliases
+ @context.aliases
+ end
+
+ def find_symbol(symbol, method=nil)
+ res = @context.parent.find_symbol(symbol, method)
+ if res
+ res = res.viewer
+ end
+ res
+ end
+ end
+
+ #####################################################################
+
+ class HTMLGenerator
+
+ include MarkUp
+
+ ##
+ # convert a target url to one that is relative to a given
+ # path
+
+ def HTMLGenerator.gen_url(path, target)
+ from = File.dirname(path)
+ to, to_file = File.split(target)
+
+ from = from.split("/")
+ to = to.split("/")
+
+ while from.size > 0 and to.size > 0 and from[0] == to[0]
+ from.shift
+ to.shift
+ end
+
+ from.fill("..")
+ from.concat(to)
+ from << to_file
+ File.join(*from)
+ end
+
+ # Generators may need to return specific subclasses depending
+ # on the options they are passed. Because of this
+ # we create them using a factory
+
+ def HTMLGenerator.for(options)
+ AllReferences::reset
+ HtmlMethod::reset
+
+ if options.all_one_file
+ HTMLGeneratorInOne.new(options)
+ else
+ HTMLGenerator.new(options)
+ end
+ end
+
+ class <<self
+ protected :new
+ end
+
+ # Set up a new HTML generator. Basically all we do here is load
+ # up the correct output temlate
+
+ def initialize(options) #:not-new:
+ @options = options
+ load_html_template
+ end
+
+
+ ##
+ # Build the initial indices and output objects
+ # based on an array of TopLevel objects containing
+ # the extracted information.
+
+ def generate(toplevels)
+ @toplevels = toplevels
+ @files = []
+ @classes = []
+
+ write_style_sheet
+ gen_sub_directories()
+ build_indices
+ generate_html
+ end
+
+ private
+
+ ##
+ # Load up the HTML template specified in the options.
+ # If the template name contains a slash, use it literally
+ #
+ def load_html_template
+ template = @options.template
+ unless template =~ %r{/|\\}
+ template = File.join("rdoc/generators/template",
+ @options.generator.key, template)
+ end
+ require template
+ extend RDoc::Page
+ rescue LoadError
+ $stderr.puts "Could not find HTML template '#{template}'"
+ exit 99
+ end
+
+ ##
+ # Write out the style sheet used by the main frames
+ #
+
+ def write_style_sheet
+ template = TemplatePage.new(RDoc::Page::STYLE)
+ unless @options.css
+ File.open(CSS_NAME, "w") do |f|
+ values = { "fonts" => RDoc::Page::FONTS }
+ template.write_html_on(f, values)
+ end
+ end
+ end
+
+ ##
+ # See the comments at the top for a description of the
+ # directory structure
+
+ def gen_sub_directories
+ File.makedirs(FILE_DIR, CLASS_DIR)
+ rescue
+ $stderr.puts $!.message
+ exit 1
+ end
+
+ ##
+ # Generate:
+ #
+ # * a list of HtmlFile objects for each TopLevel object.
+ # * a list of HtmlClass objects for each first level
+ # class or module in the TopLevel objects
+ # * a complete list of all hyperlinkable terms (file,
+ # class, module, and method names)
+
+ def build_indices
+
+ @toplevels.each do |toplevel|
+ @files << HtmlFile.new(toplevel, @options, FILE_DIR)
+ end
+
+ RDoc::TopLevel.all_classes_and_modules.each do |cls|
+ build_class_list(cls, @files[0], CLASS_DIR)
+ end
+ end
+
+ def build_class_list(from, html_file, class_dir)
+ @classes << HtmlClass.new(from, html_file, class_dir, @options)
+ from.each_classmodule do |mod|
+ build_class_list(mod, html_file, class_dir)
+ end
+ end
+
+ ##
+ # Generate all the HTML
+ #
+ def generate_html
+ # the individual descriptions for files and classes
+ gen_into(@files)
+ gen_into(@classes)
+ # and the index files
+ gen_file_index
+ gen_class_index
+ gen_method_index
+ gen_main_index
+
+ # this method is defined in the template file
+ write_extra_pages if defined? write_extra_pages
+ end
+
+ def gen_into(list)
+ list.each do |item|
+ if item.document_self
+ op_file = item.path
+ File.makedirs(File.dirname(op_file))
+ File.open(op_file, "w") { |file| item.write_on(file) }
+ end
+ end
+
+ end
+
+ def gen_file_index
+ gen_an_index(@files, 'Files',
+ RDoc::Page::FILE_INDEX,
+ "fr_file_index.html")
+ end
+
+ def gen_class_index
+ gen_an_index(@classes, 'Classes',
+ RDoc::Page::CLASS_INDEX,
+ "fr_class_index.html")
+ end
+
+ def gen_method_index
+ gen_an_index(HtmlMethod.all_methods, 'Methods',
+ RDoc::Page::METHOD_INDEX,
+ "fr_method_index.html")
+ end
+
+
+ def gen_an_index(collection, title, template, filename)
+ template = TemplatePage.new(RDoc::Page::FR_INDEX_BODY, template)
+ res = []
+ collection.sort.each do |f|
+ if f.document_self
+ res << { "href" => f.path, "name" => f.index_name }
+ end
+ end
+
+ values = {
+ "entries" => res,
+ 'list_title' => CGI.escapeHTML(title),
+ 'index_url' => main_url,
+ 'charset' => @options.charset,
+ 'style_url' => style_url('', @options.css),
+ }
+
+ File.open(filename, "w") do |f|
+ template.write_html_on(f, values)
+ end
+ end
+
+ # The main index page is mostly a template frameset, but includes
+ # the initial page. If the <tt>--main</tt> option was given,
+ # we use this as our main page, otherwise we use the
+ # first file specified on the command line.
+
+ def gen_main_index
+ template = TemplatePage.new(RDoc::Page::INDEX)
+ File.open("index.html", "w") do |f|
+ values = {
+ "initial_page" => main_url,
+ 'title' => CGI.escapeHTML(@options.title),
+ 'charset' => @options.charset
+ }
+ if @options.inline_source
+ values['inline_source'] = true
+ end
+ template.write_html_on(f, values)
+ end
+ end
+
+ # return the url of the main page
+ def main_url
+ main_page = @options.main_page
+ ref = nil
+ if main_page
+ ref = AllReferences[main_page]
+ if ref
+ ref = ref.path
+ else
+ $stderr.puts "Could not find main page #{main_page}"
+ end
+ end
+
+ unless ref
+ for file in @files
+ if file.document_self
+ ref = file.path
+ break
+ end
+ end
+ end
+
+ unless ref
+ $stderr.puts "Couldn't find anything to document"
+ $stderr.puts "Perhaps you've used :stopdoc: in all classes"
+ exit(1)
+ end
+
+ ref
+ end
+
+
+ end
+
+
+ ######################################################################
+
+
+ class HTMLGeneratorInOne < HTMLGenerator
+
+ def initialize(*args)
+ super
+ end
+
+ ##
+ # Build the initial indices and output objects
+ # based on an array of TopLevel objects containing
+ # the extracted information.
+
+ def generate(info)
+ @toplevels = info
+ @files = []
+ @classes = []
+ @hyperlinks = {}
+
+ build_indices
+ generate_xml
+ end
+
+
+ ##
+ # Generate:
+ #
+ # * a list of HtmlFile objects for each TopLevel object.
+ # * a list of HtmlClass objects for each first level
+ # class or module in the TopLevel objects
+ # * a complete list of all hyperlinkable terms (file,
+ # class, module, and method names)
+
+ def build_indices
+
+ @toplevels.each do |toplevel|
+ @files << HtmlFile.new(toplevel, @options, FILE_DIR)
+ end
+
+ RDoc::TopLevel.all_classes_and_modules.each do |cls|
+ build_class_list(cls, @files[0], CLASS_DIR)
+ end
+ end
+
+ def build_class_list(from, html_file, class_dir)
+ @classes << HtmlClass.new(from, html_file, class_dir, @options)
+ from.each_classmodule do |mod|
+ build_class_list(mod, html_file, class_dir)
+ end
+ end
+
+ ##
+ # Generate all the HTML. For the one-file case, we generate
+ # all the information in to one big hash
+ #
+ def generate_xml
+ values = {
+ 'charset' => @options.charset,
+ 'files' => gen_into(@files),
+ 'classes' => gen_into(@classes),
+ 'title' => CGI.escapeHTML(@options.title),
+ }
+
+ # this method is defined in the template file
+ write_extra_pages if defined? write_extra_pages
+
+ template = TemplatePage.new(RDoc::Page::ONE_PAGE)
+
+ if @options.op_name
+ opfile = File.open(@options.op_name, "w")
+ else
+ opfile = $stdout
+ end
+ template.write_html_on(opfile, values)
+ end
+
+ def gen_into(list)
+ res = []
+ list.each do |item|
+ res << item.value_hash
+ end
+ res
+ end
+
+ def gen_file_index
+ gen_an_index(@files, 'Files')
+ end
+
+ def gen_class_index
+ gen_an_index(@classes, 'Classes')
+ end
+
+ def gen_method_index
+ gen_an_index(HtmlMethod.all_methods, 'Methods')
+ end
+
+
+ def gen_an_index(collection, title)
+ res = []
+ collection.sort.each do |f|
+ if f.document_self
+ res << { "href" => f.path, "name" => f.index_name }
+ end
+ end
+
+ return {
+ "entries" => res,
+ 'list_title' => title,
+ 'index_url' => main_url,
+ }
+ end
+
+ end
+end
diff --git a/ruby_1_8_5/lib/rdoc/generators/ri_generator.rb b/ruby_1_8_5/lib/rdoc/generators/ri_generator.rb
new file mode 100644
index 0000000000..c4b4a7e17c
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/generators/ri_generator.rb
@@ -0,0 +1,268 @@
+# We're responsible for generating all the HTML files
+# from the object tree defined in code_objects.rb. We
+# generate:
+#
+# [files] an html file for each input file given. These
+# input files appear as objects of class
+# TopLevel
+#
+# [classes] an html file for each class or module encountered.
+# These classes are not grouped by file: if a file
+# contains four classes, we'll generate an html
+# file for the file itself, and four html files
+# for the individual classes.
+#
+# [indices] we generate three indices for files, classes,
+# and methods. These are displayed in a browser
+# like window with three index panes across the
+# top and the selected description below
+#
+# Method descriptions appear in whatever entity (file, class,
+# or module) that contains them.
+#
+# We generate files in a structure below a specified subdirectory,
+# normally +doc+.
+#
+# opdir
+# |
+# |___ files
+# | |__ per file summaries
+# |
+# |___ classes
+# |__ per class/module descriptions
+#
+# HTML is generated using the Template class.
+#
+
+require 'ftools'
+
+require 'rdoc/options'
+require 'rdoc/template'
+require 'rdoc/markup/simple_markup'
+require 'rdoc/markup/simple_markup/to_flow'
+require 'cgi'
+
+require 'rdoc/ri/ri_cache'
+require 'rdoc/ri/ri_reader'
+require 'rdoc/ri/ri_writer'
+require 'rdoc/ri/ri_descriptions'
+
+module Generators
+
+
+ class RIGenerator
+
+ # Generators may need to return specific subclasses depending
+ # on the options they are passed. Because of this
+ # we create them using a factory
+
+ def RIGenerator.for(options)
+ new(options)
+ end
+
+ class <<self
+ protected :new
+ end
+
+ # Set up a new HTML generator. Basically all we do here is load
+ # up the correct output temlate
+
+ def initialize(options) #:not-new:
+ @options = options
+ @ri_writer = RI::RiWriter.new(options.op_dir)
+ @markup = SM::SimpleMarkup.new
+ @to_flow = SM::ToFlow.new
+ end
+
+
+ ##
+ # Build the initial indices and output objects
+ # based on an array of TopLevel objects containing
+ # the extracted information.
+
+ def generate(toplevels)
+ RDoc::TopLevel.all_classes_and_modules.each do |cls|
+ process_class(cls)
+ end
+ end
+
+ def process_class(from_class)
+ generate_class_info(from_class)
+
+ # now recure into this classes constituent classess
+ from_class.each_classmodule do |mod|
+ process_class(mod)
+ end
+ end
+
+ def generate_class_info(cls)
+ if cls === RDoc::NormalModule
+ cls_desc = RI::ModuleDescription.new
+ else
+ cls_desc = RI::ClassDescription.new
+ cls_desc.superclass = cls.superclass
+ end
+ cls_desc.name = cls.name
+ cls_desc.full_name = cls.full_name
+ cls_desc.comment = markup(cls.comment)
+
+ cls_desc.attributes =cls.attributes.sort.map do |a|
+ RI::Attribute.new(a.name, a.rw, markup(a.comment))
+ end
+
+ cls_desc.constants = cls.constants.map do |c|
+ RI::Constant.new(c.name, c.value, markup(c.comment))
+ end
+
+ cls_desc.includes = cls.includes.map do |i|
+ RI::IncludedModule.new(i.name)
+ end
+
+ class_methods, instance_methods = method_list(cls)
+
+ cls_desc.class_methods = class_methods.map do |m|
+ RI::MethodSummary.new(m.name)
+ end
+ cls_desc.instance_methods = instance_methods.map do |m|
+ RI::MethodSummary.new(m.name)
+ end
+
+ update_or_replace(cls_desc)
+
+ class_methods.each do |m|
+ generate_method_info(cls_desc, m)
+ end
+
+ instance_methods.each do |m|
+ generate_method_info(cls_desc, m)
+ end
+ end
+
+
+ def generate_method_info(cls_desc, method)
+ meth_desc = RI::MethodDescription.new
+ meth_desc.name = method.name
+ meth_desc.full_name = cls_desc.full_name
+ if method.singleton
+ meth_desc.full_name += "::"
+ else
+ meth_desc.full_name += "#"
+ end
+ meth_desc.full_name << method.name
+
+ meth_desc.comment = markup(method.comment)
+ meth_desc.params = params_of(method)
+ meth_desc.visibility = method.visibility.to_s
+ meth_desc.is_singleton = method.singleton
+ meth_desc.block_params = method.block_params
+
+ meth_desc.aliases = method.aliases.map do |a|
+ RI::AliasName.new(a.name)
+ end
+
+ @ri_writer.add_method(cls_desc, meth_desc)
+ end
+
+ private
+
+ # return a list of class and instance methods that we'll be
+ # documenting
+
+ def method_list(cls)
+ list = cls.method_list
+ unless @options.show_all
+ list = list.find_all do |m|
+ m.visibility == :public || m.visibility == :protected || m.force_documentation
+ end
+ end
+
+ c = []
+ i = []
+ list.sort.each do |m|
+ if m.singleton
+ c << m
+ else
+ i << m
+ end
+ end
+ return c,i
+ end
+
+ def params_of(method)
+ if method.call_seq
+ method.call_seq
+ else
+ params = method.params || ""
+
+ p = params.gsub(/\s*\#.*/, '')
+ p = p.tr("\n", " ").squeeze(" ")
+ p = "(" + p + ")" unless p[0] == ?(
+
+ if (block = method.block_params)
+ block.gsub!(/\s*\#.*/, '')
+ block = block.tr("\n", " ").squeeze(" ")
+ if block[0] == ?(
+ block.sub!(/^\(/, '').sub!(/\)/, '')
+ end
+ p << " {|#{block.strip}| ...}"
+ end
+ p
+ end
+ end
+
+ def markup(comment)
+ return nil if !comment || comment.empty?
+
+ # Convert leading comment markers to spaces, but only
+ # if all non-blank lines have them
+
+ if comment =~ /^(?>\s*)[^\#]/
+ content = comment
+ else
+ content = comment.gsub(/^\s*(#+)/) { $1.tr('#',' ') }
+ end
+ @markup.convert(content, @to_flow)
+ end
+
+
+ # By default we replace existing classes with the
+ # same name. If the --merge option was given, we instead
+ # merge this definition into an existing class. We add
+ # our methods, aliases, etc to that class, but do not
+ # change the class's description.
+
+ def update_or_replace(cls_desc)
+ old_cls = nil
+
+ if @options.merge
+ rdr = RI::RiReader.new(RI::RiCache.new(@options.op_dir))
+
+ namespace = rdr.top_level_namespace
+ namespace = rdr.lookup_namespace_in(cls_desc.name, namespace)
+ if namespace.empty?
+ $stderr.puts "You asked me to merge this source into existing "
+ $stderr.puts "documentation. This file references a class or "
+ $stderr.puts "module called #{cls_desc.name} which I don't"
+ $stderr.puts "have existing documentation for."
+ $stderr.puts
+ $stderr.puts "Perhaps you need to generate its documentation first"
+ exit 1
+ else
+ old_cls = namespace[0]
+ end
+ end
+
+ if old_cls.nil?
+ # no merge: simply overwrite
+ @ri_writer.remove_class(cls_desc)
+ @ri_writer.add_class(cls_desc)
+ else
+ # existing class: merge in
+ old_desc = rdr.get_class(old_cls)
+
+ old_desc.merge_in(cls_desc)
+ @ri_writer.add_class(old_desc)
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rdoc/generators/template/chm/chm.rb b/ruby_1_8_5/lib/rdoc/generators/template/chm/chm.rb
new file mode 100644
index 0000000000..4a89c26520
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/generators/template/chm/chm.rb
@@ -0,0 +1,87 @@
+module RDoc
+module Page
+
+require "rdoc/generators/template/html/html"
+
+# This is a nasty little hack, but hhc doesn't support the <?xml
+# tag, so...
+
+BODY.sub!(/<\?xml.*\?>/, '')
+SRC_PAGE.sub!(/<\?xml.*\?>/, '')
+
+HPP_FILE = %{
+[OPTIONS]
+Auto Index = Yes
+Compatibility=1.1 or later
+Compiled file=%opname%.chm
+Contents file=contents.hhc
+Full-text search=Yes
+Index file=index.hhk
+Language=0x409 English(United States)
+Title=%title%
+
+[FILES]
+START:all_html_files
+%html_file_name%
+END:all_html_files
+}
+
+CONTENTS = %{
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<HTML>
+<HEAD>
+<meta name="GENERATOR" content="Microsoft&reg; HTML Help Workshop 4.1">
+<!-- Sitemap 1.0 -->
+</HEAD><BODY>
+<OBJECT type="text/site properties">
+ <param name="Foreground" value="0x80">
+ <param name="Window Styles" value="0x800025">
+ <param name="ImageType" value="Folder">
+</OBJECT>
+<UL>
+START:contents
+ <LI> <OBJECT type="text/sitemap">
+ <param name="Name" value="%c_name%">
+ <param name="Local" value="%ref%">
+ </OBJECT>
+IF:methods
+<ul>
+START:methods
+ <LI> <OBJECT type="text/sitemap">
+ <param name="Name" value="%name%">
+ <param name="Local" value="%aref%">
+ </OBJECT>
+END:methods
+</ul>
+ENDIF:methods
+ </LI>
+END:contents
+</UL>
+</BODY></HTML>
+}
+
+
+CHM_INDEX = %{
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<HTML>
+<HEAD>
+<meta name="GENERATOR" content="Microsoft&reg; HTML Help Workshop 4.1">
+<!-- Sitemap 1.0 -->
+</HEAD><BODY>
+<OBJECT type="text/site properties">
+ <param name="Foreground" value="0x80">
+ <param name="Window Styles" value="0x800025">
+ <param name="ImageType" value="Folder">
+</OBJECT>
+<UL>
+START:index
+ <LI> <OBJECT type="text/sitemap">
+ <param name="Name" value="%name%">
+ <param name="Local" value="%aref%">
+ </OBJECT>
+END:index
+</UL>
+</BODY></HTML>
+}
+end
+end
diff --git a/ruby_1_8_5/lib/rdoc/generators/template/html/hefss.rb b/ruby_1_8_5/lib/rdoc/generators/template/html/hefss.rb
new file mode 100644
index 0000000000..e68ca85823
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/generators/template/html/hefss.rb
@@ -0,0 +1,418 @@
+module RDoc
+module Page
+
+
+FONTS = "Verdana, Arial, Helvetica, sans-serif"
+
+STYLE = %{
+body,p { font-family: Verdana, Arial, Helvetica, sans-serif;
+ color: #000040; background: #BBBBBB;
+}
+
+td { font-family: Verdana, Arial, Helvetica, sans-serif;
+ color: #000040;
+}
+
+.attr-rw { font-size: small; color: #444488 }
+
+.title-row {color: #eeeeff;
+ background: #BBBBDD;
+}
+
+.big-title-font { color: white;
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+ font-size: large;
+ height: 50px}
+
+.small-title-font { color: purple;
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+ font-size: small; }
+
+.aqua { color: purple }
+
+.method-name, attr-name {
+ font-family: monospace; font-weight: bold;
+}
+
+.tablesubtitle {
+ width: 100%;
+ margin-top: 1ex;
+ margin-bottom: .5ex;
+ padding: 5px 0px 5px 20px;
+ font-size: large;
+ color: purple;
+ background: #BBBBCC;
+}
+
+.tablesubsubtitle {
+ width: 100%;
+ margin-top: 1ex;
+ margin-bottom: .5ex;
+ padding: 5px 0px 5px 20px;
+ font-size: medium;
+ color: white;
+ background: #BBBBCC;
+}
+
+.name-list {
+ font-family: monospace;
+ margin-left: 40px;
+ margin-bottom: 2ex;
+ line-height: 140%;
+}
+
+.description {
+ margin-left: 40px;
+ margin-bottom: 2ex;
+ line-height: 140%;
+}
+
+.methodtitle {
+ font-size: medium;
+ text_decoration: none;
+ padding: 3px 3px 3px 20px;
+ color: #0000AA;
+}
+
+.column-title {
+ font-size: medium;
+ font-weight: bold;
+ text_decoration: none;
+ padding: 3px 3px 3px 20px;
+ color: #3333CC;
+ }
+
+.variable-name {
+ font-family: monospace;
+ font-size: medium;
+ text_decoration: none;
+ padding: 3px 3px 3px 20px;
+ color: #0000AA;
+}
+
+.row-name {
+ font-size: medium;
+ font-weight: medium;
+ font-family: monospace;
+ text_decoration: none;
+ padding: 3px 3px 3px 20px;
+}
+
+.paramsig {
+ font-size: small;
+}
+
+.srcbut { float: right }
+
+}
+
+
+############################################################################
+
+
+BODY = %{
+<html><head>
+ <title>%title%</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=%charset%">
+ <link rel="stylesheet" href="%style_url%" type="text/css" media="screen" />
+ <script type="text/javascript" language="JavaScript">
+ <!--
+ function popCode(url) {
+ parent.frames.source.location = url
+ }
+ //-->
+ </script>
+</head>
+<body bgcolor="#BBBBBB">
+
+!INCLUDE! <!-- banner header -->
+
+IF:diagram
+<table width="100%"><tr><td align="center">
+%diagram%
+</td></tr></table>
+ENDIF:diagram
+
+IF:description
+<div class="description">%description%</div>
+ENDIF:description
+
+IF:requires
+<table cellpadding="5" width="100%">
+<tr><td class="tablesubtitle">Required files</td></tr>
+</table><br />
+<div class="name-list">
+START:requires
+HREF:aref:name:
+END:requires
+ENDIF:requires
+</div>
+
+IF:methods
+<table cellpadding="5" width="100%">
+<tr><td class="tablesubtitle">Subroutines and Functions</td></tr>
+</table><br />
+<div class="name-list">
+START:methods
+HREF:aref:name:,
+END:methods
+</div>
+ENDIF:methods
+
+IF:attributes
+<table cellpadding="5" width="100%">
+<tr><td class="tablesubtitle">Arguments</td></tr>
+</table><br />
+<table cellspacing="5">
+START:attributes
+ <tr valign="top">
+IF:rw
+ <td align="center" class="attr-rw">&nbsp;[%rw%]&nbsp;</td>
+ENDIF:rw
+IFNOT:rw
+ <td></td>
+ENDIF:rw
+ <td class="attr-name">%name%</td>
+ <td>%a_desc%</td>
+ </tr>
+END:attributes
+</table>
+ENDIF:attributes
+
+IF:classlist
+<table cellpadding="5" width="100%">
+<tr><td class="tablesubtitle">Modules</td></tr>
+</table><br />
+%classlist%<br />
+ENDIF:classlist
+
+ !INCLUDE! <!-- method descriptions -->
+
+</body>
+</html>
+}
+
+###############################################################################
+
+FILE_PAGE = <<_FILE_PAGE_
+<table width="100%">
+ <tr class="title-row">
+ <td><table width="100%"><tr>
+ <td class="big-title-font" colspan="2"><font size="-3"><b>File</b><br /></font>%short_name%</td>
+ <td align="right"><table cellspacing="0" cellpadding="2">
+ <tr>
+ <td class="small-title-font">Path:</td>
+ <td class="small-title-font">%full_path%
+IF:cvsurl
+ &nbsp;(<a href="%cvsurl%"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
+ENDIF:cvsurl
+ </td>
+ </tr>
+ <tr>
+ <td class="small-title-font">Modified:</td>
+ <td class="small-title-font">%dtm_modified%</td>
+ </tr>
+ </table>
+ </td></tr></table></td>
+ </tr>
+</table><br />
+_FILE_PAGE_
+
+###################################################################
+
+CLASS_PAGE = %{
+<table width="100%" border="0" cellspacing="0">
+ <tr class="title-row">
+ <td class="big-title-font">
+ <font size="-3"><b>%classmod%</b><br /></font>%full_name%
+ </td>
+ <td align="right">
+ <table cellspacing="0" cellpadding="2">
+ <tr valign="top">
+ <td class="small-title-font">In:</td>
+ <td class="small-title-font">
+START:infiles
+HREF:full_path_url:full_path:
+IF:cvsurl
+&nbsp;(<a href="%cvsurl%"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
+ENDIF:cvsurl
+END:infiles
+ </td>
+ </tr>
+IF:parent
+ <tr>
+ <td class="small-title-font">Parent:</td>
+ <td class="small-title-font">
+IF:par_url
+ <a href="%par_url%" class="cyan">
+ENDIF:par_url
+%parent%
+IF:par_url
+ </a>
+ENDIF:par_url
+ </td>
+ </tr>
+ENDIF:parent
+ </table>
+ </td>
+ </tr>
+</table><br />
+}
+
+###################################################################
+
+METHOD_LIST = %{
+IF:includes
+<div class="tablesubsubtitle">Uses</div><br />
+<div class="name-list">
+START:includes
+ <span class="method-name">HREF:aref:name:</span>
+END:includes
+</div>
+ENDIF:includes
+
+IF:method_list
+START:method_list
+IF:methods
+<table cellpadding="5" width="100%">
+<tr><td class="tablesubtitle">%type% %category% methods</td></tr>
+</table>
+START:methods
+<table width="100%" cellspacing="0" cellpadding="5" border="0">
+<tr><td class="methodtitle">
+<a name="%aref%">
+<b>%name%</b>%params%
+IF:codeurl
+<a href="%codeurl%" target="source" class="srclink">src</a>
+ENDIF:codeurl
+</a></td></tr>
+</table>
+IF:m_desc
+<div class="description">
+%m_desc%
+</div>
+ENDIF:m_desc
+END:methods
+ENDIF:methods
+END:method_list
+ENDIF:method_list
+}
+
+=begin
+=end
+
+########################## Source code ##########################
+
+SRC_PAGE = %{
+<html>
+<head><title>%title%</title>
+<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
+<style type="text/css">
+ .kw { color: #3333FF; font-weight: bold }
+ .cmt { color: green; font-style: italic }
+ .str { color: #662222; font-style: italic }
+ .re { color: #662222; }
+.ruby-comment { color: green; font-style: italic }
+.ruby-constant { color: #4433aa; font-weight: bold; }
+.ruby-identifier { color: #222222; }
+.ruby-ivar { color: #2233dd; }
+.ruby-keyword { color: #3333FF; font-weight: bold }
+.ruby-node { color: #777777; }
+.ruby-operator { color: #111111; }
+.ruby-regexp { color: #662222; }
+.ruby-value { color: #662222; font-style: italic }
+</style>
+</head>
+<body bgcolor="#BBBBBB">
+<pre>%code%</pre>
+</body>
+</html>
+}
+
+########################## Index ################################
+
+FR_INDEX_BODY = %{
+!INCLUDE!
+}
+
+FILE_INDEX = %{
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
+<style type="text/css">
+<!--
+ body {
+background-color: #bbbbbb;
+ font-family: #{FONTS};
+ font-size: 11px;
+ font-style: normal;
+ line-height: 14px;
+ color: #000040;
+ }
+div.banner {
+ background: #bbbbcc;
+ color: white;
+ padding: 1;
+ margin: 0;
+ font-size: 90%;
+ font-weight: bold;
+ line-height: 1.1;
+ text-align: center;
+ width: 100%;
+}
+
+-->
+</style>
+<base target="docwin">
+</head>
+<body>
+<div class="banner">%list_title%</div>
+START:entries
+<a href="%href%">%name%</a><br />
+END:entries
+</body></html>
+}
+
+CLASS_INDEX = FILE_INDEX
+METHOD_INDEX = FILE_INDEX
+
+INDEX = %{
+<html>
+<head>
+ <title>%title%</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=%charset%">
+</head>
+
+<frameset cols="20%,*">
+ <frameset rows="15%,35%,50%">
+ <frame src="fr_file_index.html" title="Files" name="Files">
+ <frame src="fr_class_index.html" name="Modules">
+ <frame src="fr_method_index.html" name="Subroutines and Functions">
+ </frameset>
+ <frameset rows="80%,20%">
+ <frame src="%initial_page%" name="docwin">
+ <frame src="blank.html" name="source">
+ </frameset>
+ <noframes>
+ <body bgcolor="#BBBBBB">
+ Click <a href="html/index.html">here</a> for a non-frames
+ version of this page.
+ </body>
+ </noframes>
+</frameset>
+
+</html>
+}
+
+# and a blank page to use as a target
+BLANK = %{
+<html><body bgcolor="#BBBBBB"></body></html>
+}
+
+def write_extra_pages
+ template = TemplatePage.new(BLANK)
+ File.open("blank.html", "w") { |f| template.write_html_on(f, {}) }
+end
+
+end
+end
diff --git a/ruby_1_8_5/lib/rdoc/generators/template/html/html.rb b/ruby_1_8_5/lib/rdoc/generators/template/html/html.rb
new file mode 100644
index 0000000000..7f9e599465
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/generators/template/html/html.rb
@@ -0,0 +1,711 @@
+#
+# = CSS2 RDoc HTML template
+#
+# This is a template for RDoc that uses XHTML 1.0 Transitional and dictates a
+# bit more of the appearance of the output to cascading stylesheets than the
+# default. It was designed for clean inline code display, and uses DHTMl to
+# toggle the visbility of each method's source with each click on the '[source]'
+# link.
+#
+# == Authors
+#
+# * Michael Granger <ged@FaerieMUD.org>
+#
+# Copyright (c) 2002, 2003 The FaerieMUD Consortium. Some rights reserved.
+#
+# This work is licensed under the Creative Commons Attribution License. To view
+# a copy of this license, visit http://creativecommons.org/licenses/by/1.0/ or
+# send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California
+# 94305, USA.
+#
+
+module RDoc
+ module Page
+
+ FONTS = "Verdana,Arial,Helvetica,sans-serif"
+
+STYLE = %{
+body {
+ font-family: Verdana,Arial,Helvetica,sans-serif;
+ font-size: 90%;
+ margin: 0;
+ margin-left: 40px;
+ padding: 0;
+ background: white;
+}
+
+h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; }
+h1 { font-size: 150%; }
+h2,h3,h4 { margin-top: 1em; }
+
+a { background: #eef; color: #039; text-decoration: none; }
+a:hover { background: #039; color: #eef; }
+
+/* Override the base stylesheet's Anchor inside a table cell */
+td > a {
+ background: transparent;
+ color: #039;
+ text-decoration: none;
+}
+
+/* and inside a section title */
+.section-title > a {
+ background: transparent;
+ color: #eee;
+ text-decoration: none;
+}
+
+/* === Structural elements =================================== */
+
+div#index {
+ margin: 0;
+ margin-left: -40px;
+ padding: 0;
+ font-size: 90%;
+}
+
+
+div#index a {
+ margin-left: 0.7em;
+}
+
+div#index .section-bar {
+ margin-left: 0px;
+ padding-left: 0.7em;
+ background: #ccc;
+ font-size: small;
+}
+
+
+div#classHeader, div#fileHeader {
+ width: auto;
+ color: white;
+ padding: 0.5em 1.5em 0.5em 1.5em;
+ margin: 0;
+ margin-left: -40px;
+ border-bottom: 3px solid #006;
+}
+
+div#classHeader a, div#fileHeader a {
+ background: inherit;
+ color: white;
+}
+
+div#classHeader td, div#fileHeader td {
+ background: inherit;
+ color: white;
+}
+
+
+div#fileHeader {
+ background: #057;
+}
+
+div#classHeader {
+ background: #048;
+}
+
+
+.class-name-in-header {
+ font-size: 180%;
+ font-weight: bold;
+}
+
+
+div#bodyContent {
+ padding: 0 1.5em 0 1.5em;
+}
+
+div#description {
+ padding: 0.5em 1.5em;
+ background: #efefef;
+ border: 1px dotted #999;
+}
+
+div#description h1,h2,h3,h4,h5,h6 {
+ color: #125;;
+ background: transparent;
+}
+
+div#validator-badges {
+ text-align: center;
+}
+div#validator-badges img { border: 0; }
+
+div#copyright {
+ color: #333;
+ background: #efefef;
+ font: 0.75em sans-serif;
+ margin-top: 5em;
+ margin-bottom: 0;
+ padding: 0.5em 2em;
+}
+
+
+/* === Classes =================================== */
+
+table.header-table {
+ color: white;
+ font-size: small;
+}
+
+.type-note {
+ font-size: small;
+ color: #DEDEDE;
+}
+
+.xxsection-bar {
+ background: #eee;
+ color: #333;
+ padding: 3px;
+}
+
+.section-bar {
+ color: #333;
+ border-bottom: 1px solid #999;
+ margin-left: -20px;
+}
+
+
+.section-title {
+ background: #79a;
+ color: #eee;
+ padding: 3px;
+ margin-top: 2em;
+ margin-left: -30px;
+ border: 1px solid #999;
+}
+
+.top-aligned-row { vertical-align: top }
+.bottom-aligned-row { vertical-align: bottom }
+
+/* --- Context section classes ----------------------- */
+
+.context-row { }
+.context-item-name { font-family: monospace; font-weight: bold; color: black; }
+.context-item-value { font-size: small; color: #448; }
+.context-item-desc { color: #333; padding-left: 2em; }
+
+/* --- Method classes -------------------------- */
+.method-detail {
+ background: #efefef;
+ padding: 0;
+ margin-top: 0.5em;
+ margin-bottom: 1em;
+ border: 1px dotted #ccc;
+}
+.method-heading {
+ color: black;
+ background: #ccc;
+ border-bottom: 1px solid #666;
+ padding: 0.2em 0.5em 0 0.5em;
+}
+.method-signature { color: black; background: inherit; }
+.method-name { font-weight: bold; }
+.method-args { font-style: italic; }
+.method-description { padding: 0 0.5em 0 0.5em; }
+
+/* --- Source code sections -------------------- */
+
+a.source-toggle { font-size: 90%; }
+div.method-source-code {
+ background: #262626;
+ color: #ffdead;
+ margin: 1em;
+ padding: 0.5em;
+ border: 1px dashed #999;
+ overflow: hidden;
+}
+
+div.method-source-code pre { color: #ffdead; overflow: hidden; }
+
+/* --- Ruby keyword styles --------------------- */
+
+.standalone-code { background: #221111; color: #ffdead; overflow: hidden; }
+
+.ruby-constant { color: #7fffd4; background: transparent; }
+.ruby-keyword { color: #00ffff; background: transparent; }
+.ruby-ivar { color: #eedd82; background: transparent; }
+.ruby-operator { color: #00ffee; background: transparent; }
+.ruby-identifier { color: #ffdead; background: transparent; }
+.ruby-node { color: #ffa07a; background: transparent; }
+.ruby-comment { color: #b22222; font-weight: bold; background: transparent; }
+.ruby-regexp { color: #ffa07a; background: transparent; }
+.ruby-value { color: #7fffd4; background: transparent; }
+}
+
+
+#####################################################################
+### H E A D E R T E M P L A T E
+#####################################################################
+
+XHTML_PREAMBLE = %{<?xml version="1.0" encoding="%charset%"?>
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+}
+
+HEADER = XHTML_PREAMBLE + %{
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>%title%</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
+ <meta http-equiv="Content-Script-Type" content="text/javascript" />
+ <link rel="stylesheet" href="%style_url%" type="text/css" media="screen" />
+ <script type="text/javascript">
+ // <![CDATA[
+
+ function popupCode( url ) {
+ window.open(url, "Code", "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400")
+ }
+
+ function toggleCode( id ) {
+ if ( document.getElementById )
+ elem = document.getElementById( id );
+ else if ( document.all )
+ elem = eval( "document.all." + id );
+ else
+ return false;
+
+ elemStyle = elem.style;
+
+ if ( elemStyle.display != "block" ) {
+ elemStyle.display = "block"
+ } else {
+ elemStyle.display = "none"
+ }
+
+ return true;
+ }
+
+ // Make codeblocks hidden by default
+ document.writeln( "<style type=\\"text/css\\">div.method-source-code { display: none }</style>" )
+
+ // ]]>
+ </script>
+
+</head>
+<body>
+}
+
+
+#####################################################################
+### C O N T E X T C O N T E N T T E M P L A T E
+#####################################################################
+
+CONTEXT_CONTENT = %{
+}
+
+
+#####################################################################
+### F O O T E R T E M P L A T E
+#####################################################################
+FOOTER = %{
+<div id="validator-badges">
+ <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
+</div>
+
+</body>
+</html>
+}
+
+
+#####################################################################
+### F I L E P A G E H E A D E R T E M P L A T E
+#####################################################################
+
+FILE_PAGE = %{
+ <div id="fileHeader">
+ <h1>%short_name%</h1>
+ <table class="header-table">
+ <tr class="top-aligned-row">
+ <td><strong>Path:</strong></td>
+ <td>%full_path%
+IF:cvsurl
+ &nbsp;(<a href="%cvsurl%"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
+ENDIF:cvsurl
+ </td>
+ </tr>
+ <tr class="top-aligned-row">
+ <td><strong>Last Update:</strong></td>
+ <td>%dtm_modified%</td>
+ </tr>
+ </table>
+ </div>
+}
+
+
+#####################################################################
+### C L A S S P A G E H E A D E R T E M P L A T E
+#####################################################################
+
+CLASS_PAGE = %{
+ <div id="classHeader">
+ <table class="header-table">
+ <tr class="top-aligned-row">
+ <td><strong>%classmod%</strong></td>
+ <td class="class-name-in-header">%full_name%</td>
+ </tr>
+ <tr class="top-aligned-row">
+ <td><strong>In:</strong></td>
+ <td>
+START:infiles
+IF:full_path_url
+ <a href="%full_path_url%">
+ENDIF:full_path_url
+ %full_path%
+IF:full_path_url
+ </a>
+ENDIF:full_path_url
+IF:cvsurl
+ &nbsp;(<a href="%cvsurl%"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
+ENDIF:cvsurl
+ <br />
+END:infiles
+ </td>
+ </tr>
+
+IF:parent
+ <tr class="top-aligned-row">
+ <td><strong>Parent:</strong></td>
+ <td>
+IF:par_url
+ <a href="%par_url%">
+ENDIF:par_url
+ %parent%
+IF:par_url
+ </a>
+ENDIF:par_url
+ </td>
+ </tr>
+ENDIF:parent
+ </table>
+ </div>
+}
+
+
+#####################################################################
+### M E T H O D L I S T T E M P L A T E
+#####################################################################
+
+METHOD_LIST = %{
+
+ <div id="contextContent">
+IF:diagram
+ <div id="diagram">
+ %diagram%
+ </div>
+ENDIF:diagram
+
+IF:description
+ <div id="description">
+ %description%
+ </div>
+ENDIF:description
+
+IF:requires
+ <div id="requires-list">
+ <h3 class="section-bar">Required files</h3>
+
+ <div class="name-list">
+START:requires
+ HREF:aref:name:&nbsp;&nbsp;
+END:requires
+ </div>
+ </div>
+ENDIF:requires
+
+IF:toc
+ <div id="contents-list">
+ <h3 class="section-bar">Contents</h3>
+ <ul>
+START:toc
+ <li><a href="#%href%">%secname%</a></li>
+END:toc
+ </ul>
+ENDIF:toc
+ </div>
+
+IF:methods
+ <div id="method-list">
+ <h3 class="section-bar">Methods</h3>
+
+ <div class="name-list">
+START:methods
+ HREF:aref:name:&nbsp;&nbsp;
+END:methods
+ </div>
+ </div>
+ENDIF:methods
+
+ </div>
+
+
+ <!-- if includes -->
+IF:includes
+ <div id="includes">
+ <h3 class="section-bar">Included Modules</h3>
+
+ <div id="includes-list">
+START:includes
+ <span class="include-name">HREF:aref:name:</span>
+END:includes
+ </div>
+ </div>
+ENDIF:includes
+
+START:sections
+ <div id="section">
+IF:sectitle
+ <h2 class="section-title"><a name="%secsequence%">%sectitle%</a></h2>
+IF:seccomment
+ <div class="section-comment">
+ %seccomment%
+ </div>
+ENDIF:seccomment
+ENDIF:sectitle
+
+IF:classlist
+ <div id="class-list">
+ <h3 class="section-bar">Classes and Modules</h3>
+
+ %classlist%
+ </div>
+ENDIF:classlist
+
+IF:constants
+ <div id="constants-list">
+ <h3 class="section-bar">Constants</h3>
+
+ <div class="name-list">
+ <table summary="Constants">
+START:constants
+ <tr class="top-aligned-row context-row">
+ <td class="context-item-name">%name%</td>
+ <td>=</td>
+ <td class="context-item-value">%value%</td>
+IF:desc
+ <td width="3em">&nbsp;</td>
+ <td class="context-item-desc">%desc%</td>
+ENDIF:desc
+ </tr>
+END:constants
+ </table>
+ </div>
+ </div>
+ENDIF:constants
+
+IF:aliases
+ <div id="aliases-list">
+ <h3 class="section-bar">External Aliases</h3>
+
+ <div class="name-list">
+ <table summary="aliases">
+START:aliases
+ <tr class="top-aligned-row context-row">
+ <td class="context-item-name">%old_name%</td>
+ <td>-&gt;</td>
+ <td class="context-item-value">%new_name%</td>
+ </tr>
+IF:desc
+ <tr class="top-aligned-row context-row">
+ <td>&nbsp;</td>
+ <td colspan="2" class="context-item-desc">%desc%</td>
+ </tr>
+ENDIF:desc
+END:aliases
+ </table>
+ </div>
+ </div>
+ENDIF:aliases
+
+
+IF:attributes
+ <div id="attribute-list">
+ <h3 class="section-bar">Attributes</h3>
+
+ <div class="name-list">
+ <table>
+START:attributes
+ <tr class="top-aligned-row context-row">
+ <td class="context-item-name">%name%</td>
+IF:rw
+ <td class="context-item-value">&nbsp;[%rw%]&nbsp;</td>
+ENDIF:rw
+IFNOT:rw
+ <td class="context-item-value">&nbsp;&nbsp;</td>
+ENDIF:rw
+ <td class="context-item-desc">%a_desc%</td>
+ </tr>
+END:attributes
+ </table>
+ </div>
+ </div>
+ENDIF:attributes
+
+
+
+ <!-- if method_list -->
+IF:method_list
+ <div id="methods">
+START:method_list
+IF:methods
+ <h3 class="section-bar">%type% %category% methods</h3>
+
+START:methods
+ <div id="method-%aref%" class="method-detail">
+ <a name="%aref%"></a>
+
+ <div class="method-heading">
+IF:codeurl
+ <a href="%codeurl%" target="Code" class="method-signature"
+ onclick="popupCode('%codeurl%');return false;">
+ENDIF:codeurl
+IF:sourcecode
+ <a href="#%aref%" class="method-signature">
+ENDIF:sourcecode
+IF:callseq
+ <span class="method-name">%callseq%</span>
+ENDIF:callseq
+IFNOT:callseq
+ <span class="method-name">%name%</span><span class="method-args">%params%</span>
+ENDIF:callseq
+IF:codeurl
+ </a>
+ENDIF:codeurl
+IF:sourcecode
+ </a>
+ENDIF:sourcecode
+ </div>
+
+ <div class="method-description">
+IF:m_desc
+ %m_desc%
+ENDIF:m_desc
+IF:sourcecode
+ <p><a class="source-toggle" href="#"
+ onclick="toggleCode('%aref%-source');return false;">[Source]</a></p>
+ <div class="method-source-code" id="%aref%-source">
+<pre>
+%sourcecode%
+</pre>
+ </div>
+ENDIF:sourcecode
+ </div>
+ </div>
+
+END:methods
+ENDIF:methods
+END:method_list
+
+ </div>
+ENDIF:method_list
+END:sections
+}
+
+
+#####################################################################
+### B O D Y T E M P L A T E
+#####################################################################
+
+BODY = HEADER + %{
+
+!INCLUDE! <!-- banner header -->
+
+ <div id="bodyContent">
+
+} + METHOD_LIST + %{
+
+ </div>
+
+} + FOOTER
+
+
+
+#####################################################################
+### S O U R C E C O D E T E M P L A T E
+#####################################################################
+
+SRC_PAGE = XHTML_PREAMBLE + %{
+<html>
+<head>
+ <title>%title%</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
+ <link rel="stylesheet" href="%style_url%" type="text/css" media="screen" />
+</head>
+<body class="standalone-code">
+ <pre>%code%</pre>
+</body>
+</html>
+}
+
+
+#####################################################################
+### I N D E X F I L E T E M P L A T E S
+#####################################################################
+
+FR_INDEX_BODY = %{
+!INCLUDE!
+}
+
+FILE_INDEX = XHTML_PREAMBLE + %{
+<!--
+
+ %list_title%
+
+ -->
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>%list_title%</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
+ <link rel="stylesheet" href="%style_url%" type="text/css" />
+ <base target="docwin" />
+</head>
+<body>
+<div id="index">
+ <h1 class="section-bar">%list_title%</h1>
+ <div id="index-entries">
+START:entries
+ <a href="%href%">%name%</a><br />
+END:entries
+ </div>
+</div>
+</body>
+</html>
+}
+
+CLASS_INDEX = FILE_INDEX
+METHOD_INDEX = FILE_INDEX
+
+INDEX = %{<?xml version="1.0" encoding="%charset%"?>
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
+
+<!--
+
+ %title%
+
+ -->
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>%title%</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
+</head>
+<frameset rows="20%, 80%">
+ <frameset cols="25%,35%,45%">
+ <frame src="fr_file_index.html" title="Files" name="Files" />
+ <frame src="fr_class_index.html" name="Classes" />
+ <frame src="fr_method_index.html" name="Methods" />
+ </frameset>
+ <frame src="%initial_page%" name="docwin" />
+</frameset>
+</html>
+}
+
+
+
+ end # module Page
+end # class RDoc
+
+require 'rdoc/generators/template/html/one_page_html'
diff --git a/ruby_1_8_5/lib/rdoc/generators/template/html/kilmer.rb b/ruby_1_8_5/lib/rdoc/generators/template/html/kilmer.rb
new file mode 100644
index 0000000000..55071fc026
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/generators/template/html/kilmer.rb
@@ -0,0 +1,435 @@
+module RDoc
+module Page
+
+
+FONTS = "Verdana, Arial, Helvetica, sans-serif"
+
+STYLE = %{
+body,td,p { font-family: %fonts%;
+ color: #000040;
+}
+
+.attr-rw { font-size: xx-small; color: #444488 }
+
+.title-row { background-color: #CCCCFF;
+ color: #000010;
+}
+
+.big-title-font {
+ color: black;
+ font-weight: bold;
+ font-family: %fonts%;
+ font-size: large;
+ height: 60px;
+ padding: 10px 3px 10px 3px;
+}
+
+.small-title-font { color: black;
+ font-family: %fonts%;
+ font-size:10; }
+
+.aqua { color: black }
+
+.method-name, .attr-name {
+ font-family: font-family: %fonts%;
+ font-weight: bold;
+ font-size: small;
+ margin-left: 20px;
+ color: #000033;
+}
+
+.tablesubtitle, .tablesubsubtitle {
+ width: 100%;
+ margin-top: 1ex;
+ margin-bottom: .5ex;
+ padding: 5px 0px 5px 3px;
+ font-size: large;
+ color: black;
+ background-color: #CCCCFF;
+ border: thin;
+}
+
+.name-list {
+ margin-left: 5px;
+ margin-bottom: 2ex;
+ line-height: 105%;
+}
+
+.description {
+ margin-left: 5px;
+ margin-bottom: 2ex;
+ line-height: 105%;
+ font-size: small;
+}
+
+.methodtitle {
+ font-size: small;
+ font-weight: bold;
+ text-decoration: none;
+ color: #000033;
+ background-color: white;
+}
+
+.srclink {
+ font-size: small;
+ font-weight: bold;
+ text-decoration: none;
+ color: #0000DD;
+ background-color: white;
+}
+
+.paramsig {
+ font-size: small;
+}
+
+.srcbut { float: right }
+
+}
+
+
+############################################################################
+
+
+BODY = %{
+<html><head>
+ <title>%title%</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=%charset%">
+ <link rel="stylesheet" href="%style_url%" type="text/css" media="screen" />
+ <script type="text/javascript" language="JavaScript">
+ <!--
+ function popCode(url) {
+ parent.frames.source.location = url
+ }
+ //-->
+ </script>
+</head>
+<body bgcolor="white">
+
+!INCLUDE! <!-- banner header -->
+
+IF:diagram
+<table width="100%"><tr><td align="center">
+%diagram%
+</td></tr></table>
+ENDIF:diagram
+
+IF:description
+<div class="description">%description%</div>
+ENDIF:description
+
+IF:requires
+<table cellpadding="5" width="100%">
+<tr><td class="tablesubtitle">Required files</td></tr>
+</table><br />
+<div class="name-list">
+START:requires
+HREF:aref:name:
+END:requires
+ENDIF:requires
+</div>
+
+IF:methods
+<table cellpadding="5" width="100%">
+<tr><td class="tablesubtitle">Methods</td></tr>
+</table><br />
+<div class="name-list">
+START:methods
+HREF:aref:name:,
+END:methods
+</div>
+ENDIF:methods
+
+
+START:sections
+ <div id="section">
+IF:sectitle
+ <h2 class="section-title"><a name="%secsequence%">%sectitle%</a></h2>
+IF:seccomment
+ <div class="section-comment">
+ %seccomment%
+ </div>
+ENDIF:seccomment
+ENDIF:sectitle
+
+IF:attributes
+<table cellpadding="5" width="100%">
+<tr><td class="tablesubtitle">Attributes</td></tr>
+</table><br />
+<table cellspacing="5">
+START:attributes
+ <tr valign="top">
+IF:rw
+ <td align="center" class="attr-rw">&nbsp;[%rw%]&nbsp;</td>
+ENDIF:rw
+IFNOT:rw
+ <td></td>
+ENDIF:rw
+ <td class="attr-name">%name%</td>
+ <td>%a_desc%</td>
+ </tr>
+END:attributes
+</table>
+ENDIF:attributes
+
+IF:classlist
+<table cellpadding="5" width="100%">
+<tr><td class="tablesubtitle">Classes and Modules</td></tr>
+</table><br />
+%classlist%<br />
+ENDIF:classlist
+
+ !INCLUDE! <!-- method descriptions -->
+
+END:sections
+
+</body>
+</html>
+}
+
+###############################################################################
+
+FILE_PAGE = <<_FILE_PAGE_
+<table width="100%">
+ <tr class="title-row">
+ <td><table width="100%"><tr>
+ <td class="big-title-font" colspan="2"><font size="-3"><b>File</b><br /></font>%short_name%</td>
+ <td align="right"><table cellspacing="0" cellpadding="2">
+ <tr>
+ <td class="small-title-font">Path:</td>
+ <td class="small-title-font">%full_path%
+IF:cvsurl
+ &nbsp;(<a href="%cvsurl%"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
+ENDIF:cvsurl
+ </td>
+ </tr>
+ <tr>
+ <td class="small-title-font">Modified:</td>
+ <td class="small-title-font">%dtm_modified%</td>
+ </tr>
+ </table>
+ </td></tr></table></td>
+ </tr>
+</table><br />
+_FILE_PAGE_
+
+###################################################################
+
+CLASS_PAGE = %{
+<table width="100%" border="0" cellspacing="0">
+ <tr class="title-row">
+ <td class="big-title-font">
+ <font size="-3"><b>%classmod%</b><br /></font>%full_name%
+ </td>
+ <td align="right">
+ <table cellspacing="0" cellpadding="2">
+ <tr valign="top">
+ <td class="small-title-font">In:</td>
+ <td class="small-title-font">
+START:infiles
+HREF:full_path_url:full_path:
+IF:cvsurl
+&nbsp;(<a href="%cvsurl%"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
+ENDIF:cvsurl
+END:infiles
+ </td>
+ </tr>
+IF:parent
+ <tr>
+ <td class="small-title-font">Parent:</td>
+ <td class="small-title-font">
+IF:par_url
+ <a href="%par_url%" class="cyan">
+ENDIF:par_url
+%parent%
+IF:par_url
+ </a>
+ENDIF:par_url
+ </td>
+ </tr>
+ENDIF:parent
+ </table>
+ </td>
+ </tr>
+</table><br />
+}
+
+###################################################################
+
+METHOD_LIST = %{
+IF:includes
+<div class="tablesubsubtitle">Included modules</div><br />
+<div class="name-list">
+START:includes
+ <span class="method-name">HREF:aref:name:</span>
+END:includes
+</div>
+ENDIF:includes
+
+IF:method_list
+START:method_list
+IF:methods
+<table cellpadding=5 width="100%">
+<tr><td class="tablesubtitle">%type% %category% methods</td></tr>
+</table>
+START:methods
+<table width="100%" cellspacing="0" cellpadding="5" border="0">
+<tr><td class="methodtitle">
+<a name="%aref%">
+IF:callseq
+<b>%callseq%</b>
+ENDIF:callseq
+IFNOT:callseq
+ <b>%name%</b>%params%
+ENDIF:callseq
+IF:codeurl
+<a href="%codeurl%" target="source" class="srclink">src</a>
+ENDIF:codeurl
+</a></td></tr>
+</table>
+IF:m_desc
+<div class="description">
+%m_desc%
+</div>
+ENDIF:m_desc
+IF:aka
+<div class="aka">
+This method is also aliased as
+START:aka
+<a href="%aref%">%name%</a>
+END:aka
+</div>
+ENDIF:aka
+IF:sourcecode
+<pre class="source">
+%sourcecode%
+</pre>
+ENDIF:sourcecode
+END:methods
+ENDIF:methods
+END:method_list
+ENDIF:method_list
+}
+
+=begin
+=end
+
+########################## Source code ##########################
+
+SRC_PAGE = %{
+<html>
+<head><title>%title%</title>
+<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
+<style type="text/css">
+.ruby-comment { color: green; font-style: italic }
+.ruby-constant { color: #4433aa; font-weight: bold; }
+.ruby-identifier { color: #222222; }
+.ruby-ivar { color: #2233dd; }
+.ruby-keyword { color: #3333FF; font-weight: bold }
+.ruby-node { color: #777777; }
+.ruby-operator { color: #111111; }
+.ruby-regexp { color: #662222; }
+.ruby-value { color: #662222; font-style: italic }
+ .kw { color: #3333FF; font-weight: bold }
+ .cmt { color: green; font-style: italic }
+ .str { color: #662222; font-style: italic }
+ .re { color: #662222; }
+</style>
+</head>
+<body bgcolor="white">
+<pre>%code%</pre>
+</body>
+</html>
+}
+
+########################## Index ################################
+
+FR_INDEX_BODY = %{
+!INCLUDE!
+}
+
+FILE_INDEX = %{
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
+<style>
+<!--
+ body {
+background-color: #ddddff;
+ font-family: #{FONTS};
+ font-size: 11px;
+ font-style: normal;
+ line-height: 14px;
+ color: #000040;
+ }
+div.banner {
+ background: #0000aa;
+ color: white;
+ padding: 1;
+ margin: 0;
+ font-size: 90%;
+ font-weight: bold;
+ line-height: 1.1;
+ text-align: center;
+ width: 100%;
+}
+
+-->
+</style>
+<base target="docwin">
+</head>
+<body>
+<div class="banner">%list_title%</div>
+START:entries
+<a href="%href%">%name%</a><br />
+END:entries
+</body></html>
+}
+
+CLASS_INDEX = FILE_INDEX
+METHOD_INDEX = FILE_INDEX
+
+INDEX = %{
+<html>
+<head>
+ <title>%title%</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=%charset%">
+</head>
+
+<frameset cols="20%,*">
+ <frameset rows="15%,35%,50%">
+ <frame src="fr_file_index.html" title="Files" name="Files">
+ <frame src="fr_class_index.html" name="Classes">
+ <frame src="fr_method_index.html" name="Methods">
+ </frameset>
+IF:inline_source
+ <frame src="%initial_page%" name="docwin">
+ENDIF:inline_source
+IFNOT:inline_source
+ <frameset rows="80%,20%">
+ <frame src="%initial_page%" name="docwin">
+ <frame src="blank.html" name="source">
+ </frameset>
+ENDIF:inline_source
+ <noframes>
+ <body bgcolor="white">
+ Click <a href="html/index.html">here</a> for a non-frames
+ version of this page.
+ </body>
+ </noframes>
+</frameset>
+
+</html>
+}
+
+# and a blank page to use as a target
+BLANK = %{
+<html><body bgcolor="white"></body></html>
+}
+
+def write_extra_pages
+ template = TemplatePage.new(BLANK)
+ File.open("blank.html", "w") { |f| template.write_html_on(f, {}) }
+end
+
+end
+end
diff --git a/ruby_1_8_5/lib/rdoc/generators/template/html/old_html.rb b/ruby_1_8_5/lib/rdoc/generators/template/html/old_html.rb
new file mode 100644
index 0000000000..ca66302a08
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/generators/template/html/old_html.rb
@@ -0,0 +1,728 @@
+module RDoc
+
+# This is how you define the HTML that RDoc generates. Simply create
+# a file in rdoc/generators/html_templates that creates the
+# module RDoc::Page and populate it as described below. Then invoke
+# rdoc using the --template <name of your file> option, and
+# your template will be used.
+#
+# The constants defining pages use a simple templating system:
+#
+# * The templating system is passed a hash. Keys in the hash correspond
+# to tags on this page. The tag %abc% is looked up in the hash,
+# and is replaced by the corresponding hash value.
+#
+# * Some tags are optional. You can detect this using IF/ENDIF
+#
+# IF: title
+# The value of title is %title%
+# ENDIF: title
+#
+# * Some entries in the hash have values that are arrays, where each
+# entry in the array is itself a hash. These are used to generate
+# lists using the START: construct. For example, given a hash
+# containing
+#
+# { 'people' => [ { 'name' => 'Fred', 'age' => '12' },
+# { 'name' => 'Mary', 'age' => '21' } ]
+#
+# You could generate a simple table using
+#
+# <table>
+# START:people
+# <tr><td>%name%<td>%age%</tr>
+# END:people
+# </table>
+#
+# These lists can be nested to an arbitrary depth
+#
+# * the construct HREF:url:name: generates <a href="%url%">%name%</a>
+# if +url+ is defined in the hash, or %name% otherwise.
+#
+#
+# Your file must contain the following constants
+#
+# [*FONTS*] a list of fonts to be used
+# [*STYLE*] a CSS section (without the <style> or comments). This is
+# used to generate a style.css file
+#
+# [*BODY*]
+# The main body of all non-index RDoc pages. BODY will contain
+# two !INCLUDE!s. The first is used to include a document-type
+# specific header (FILE_PAGE or CLASS_PAGE). The second include
+# is for the method list (METHOD_LIST). THe body is passed:
+#
+# %title%::
+# the page's title
+#
+# %style_url%::
+# the url of a style sheet for this page
+#
+# %diagram%::
+# the optional URL of a diagram for this page
+#
+# %description%::
+# a (potentially multi-paragraph) string containing the
+# description for th file/class/module.
+#
+# %requires%::
+# an optional list of %aref%/%name% pairs, one for each module
+# required by this file.
+#
+# %methods%::
+# an optional list of %aref%/%name%, one for each method
+# documented on this page. This is intended to be an index.
+#
+# %attributes%::
+# An optional list. For each attribute it contains:
+# %name%:: the attribute name
+# %rw%:: r/o, w/o, or r/w
+# %a_desc%:: description of the attribute
+#
+# %classlist%::
+# An optional string containing an already-formatted list of
+# classes and modules documented in this file
+#
+# For FILE_PAGE entries, the body will be passed
+#
+# %short_name%::
+# The name of the file
+#
+# %full_path%::
+# The full path to the file
+#
+# %dtm_modified%::
+# The date/time the file was last changed
+#
+# For class and module pages, the body will be passed
+#
+# %classmod%::
+# The name of the class or module
+#
+# %files%::
+# A list. For each file this class is defined in, it contains:
+# %full_path_url%:: an (optional) URL of the RDoc page
+# for this file
+# %full_path%:: the name of the file
+#
+# %par_url%::
+# The (optional) URL of the RDoc page documenting this class's
+# parent class
+#
+# %parent%::
+# The name of this class's parent.
+#
+# For both files and classes, the body is passed the following information
+# on includes and methods:
+#
+# %includes%::
+# Optional list of included modules. For each, it receives
+# %aref%:: optional URL to RDoc page for the module
+# %name%:: the name of the module
+#
+# %method_list%::
+# Optional list of methods of a particular class and category.
+#
+# Each method list entry contains:
+#
+# %type%:: public/private/protected
+# %category%:: instance/class
+# %methods%:: a list of method descriptions
+#
+# Each method description contains:
+#
+# %aref%:: a target aref, used when referencing this method
+# description. You should code this as <a name="%aref%">
+# %codeurl%:: the optional URL to the page containing this method's
+# source code.
+# %name%:: the method's name
+# %params%:: the method's parameters
+# %callseq%:: a full calling sequence
+# %m_desc%:: the (potentially multi-paragraph) description of
+# this method.
+#
+# [*CLASS_PAGE*]
+# Header for pages documenting classes and modules. See
+# BODY above for the available parameters.
+#
+# [*FILE_PAGE*]
+# Header for pages documenting files. See
+# BODY above for the available parameters.
+#
+# [*METHOD_LIST*]
+# Controls the display of the listing of methods. See BODY for
+# parameters.
+#
+# [*INDEX*]
+# The top-level index page. For a browser-like environment
+# define a frame set that includes the file, class, and
+# method indices. Passed
+# %title%:: title of page
+# %initial_page% :: url of initial page to display
+#
+# [*CLASS_INDEX*]
+# Individual files for the three indexes. Passed:
+# %index_url%:: URL of main index page
+# %entries%:: List of
+# %name%:: name of an index entry
+# %href%:: url of corresponding page
+# [*METHOD_INDEX*]
+# Same as CLASS_INDEX for methods
+#
+# [*FILE_INDEX*]
+# Same as CLASS_INDEX for methods
+#
+# [*FR_INDEX_BODY*]
+# A wrapper around CLASS_INDEX, METHOD_INDEX, and FILE_INDEX.
+# If those index strings contain the complete HTML for the
+# output, then FR_INDEX_BODY can simply be !INCLUDE!
+#
+# [*SRC_PAGE*]
+# Page used to display source code. Passed %title% and %code%,
+# the latter being a multi-line string of code.
+
+module Page
+
+FONTS = "Verdana, Arial, Helvetica, sans-serif"
+
+STYLE = %{
+body,td,p { font-family: %fonts%;
+ color: #000040;
+}
+
+.attr-rw { font-size: x-small; color: #444488 }
+
+.title-row { background: #0000aa;
+ color: #eeeeff;
+}
+
+.big-title-font { color: white;
+ font-family: %fonts%;
+ font-size: large;
+ height: 50px}
+
+.small-title-font { color: aqua;
+ font-family: %fonts%;
+ font-size: xx-small; }
+
+.aqua { color: aqua }
+
+.method-name, attr-name {
+ font-family: monospace; font-weight: bold;
+}
+
+.tablesubtitle, .tablesubsubtitle {
+ width: 100%;
+ margin-top: 1ex;
+ margin-bottom: .5ex;
+ padding: 5px 0px 5px 20px;
+ font-size: large;
+ color: aqua;
+ background: #3333cc;
+}
+
+.name-list {
+ font-family: monospace;
+ margin-left: 40px;
+ margin-bottom: 2ex;
+ line-height: 140%;
+}
+
+.description {
+ margin-left: 40px;
+ margin-top: -2ex;
+ margin-bottom: 2ex;
+}
+
+.description p {
+ line-height: 140%;
+}
+
+.aka {
+ margin-left: 40px;
+ margin-bottom: 2ex;
+ line-height: 100%;
+ font-size: small;
+ color: #808080;
+}
+
+.methodtitle {
+ font-size: medium;
+ text-decoration: none;
+ color: #0000AA;
+ background: white;
+}
+
+.paramsig {
+ font-size: small;
+}
+
+.srcbut { float: right }
+
+pre { font-size: 1.2em; }
+tt { font-size: 1.2em; }
+
+pre.source {
+ border-style: groove;
+ background-color: #ddddff;
+ margin-left: 40px;
+ padding: 1em 0em 1em 2em;
+}
+
+.classlist {
+ margin-left: 40px;
+ margin-bottom: 2ex;
+ line-height: 140%;
+}
+
+li {
+ display: list-item;
+ margin-top: .6em;
+}
+
+.ruby-comment { color: green; font-style: italic }
+.ruby-constant { color: #4433aa; font-weight: bold; }
+.ruby-identifier { color: #222222; }
+.ruby-ivar { color: #2233dd; }
+.ruby-keyword { color: #3333FF; font-weight: bold }
+.ruby-node { color: #777777; }
+.ruby-operator { color: #111111; }
+.ruby-regexp { color: #662222; }
+.ruby-value { color: #662222; font-style: italic }
+
+}
+
+
+############################################################################
+
+
+HEADER = %{
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>%title%</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
+ <link rel=StyleSheet href="%style_url%" type="text/css" media="screen" />
+ <script type="text/javascript" language="JavaScript">
+ <!--
+ function popCode(url) {
+ window.open(url, "Code",
+ "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400")
+ }
+ //-->
+ </script>
+</head>
+}
+
+
+###################################################################
+
+METHOD_LIST = %{
+IF:includes
+<table summary="Included modules" cellpadding="5" width="100%">
+<tr><td class="tablesubtitle">Included modules</td></tr>
+</table>
+<div class="name-list">
+START:includes
+ <span class="method-name">HREF:aref:name:</span>
+END:includes
+</div>
+ENDIF:includes
+
+IF:method_list
+START:method_list
+IF:methods
+<table summary="Method list" cellpadding="5" width="100%">
+<tr><td class="tablesubtitle">%type% %category% methods</td></tr>
+</table>
+START:methods
+<table summary="method" width="100%" cellspacing="0" cellpadding="5" border="0">
+<tr><td class="methodtitle">
+<a name="%aref%"></a>
+IF:codeurl
+<a href="%codeurl%" target="Code" class="methodtitle"
+ onClick="popCode('%codeurl%');return false;">
+ENDIF:codeurl
+IF:callseq
+<b>%callseq%</b>
+ENDIF:callseq
+IFNOT:callseq
+<b>%name%</b>%params%
+ENDIF:callseq
+IF:codeurl
+</a>
+ENDIF:codeurl
+</td></tr>
+</table>
+IF:m_desc
+<div class="description">
+%m_desc%
+</div>
+ENDIF:m_desc
+IF:aka
+<div class="aka">
+This method is also aliased as
+START:aka
+<a href="%aref%">%name%</a>
+END:aka
+</div>
+ENDIF:aka
+IF:sourcecode
+<pre class="source">
+%sourcecode%
+</pre>
+ENDIF:sourcecode
+END:methods
+ENDIF:methods
+END:method_list
+ENDIF:method_list
+}
+
+###################################################################
+
+CONTEXT_CONTENT = %{
+IF:diagram
+<table summary="Diagram of classes and modules" width="100%">
+<tr><td align="center">
+%diagram%
+</td></tr></table>
+ENDIF:diagram
+
+
+IF:description
+<div class="description">%description%</div>
+ENDIF:description
+
+IF:requires
+<table summary="Requires" cellpadding="5" width="100%">
+<tr><td class="tablesubtitle">Required files</td></tr>
+</table>
+<div class="name-list">
+START:requires
+HREF:aref:name:&nbsp; &nbsp;
+END:requires
+</div>
+ENDIF:requires
+
+IF:methods
+<table summary="Methods" cellpadding="5" width="100%">
+<tr><td class="tablesubtitle">Methods</td></tr>
+</table>
+<div class="name-list">
+START:methods
+HREF:aref:name:&nbsp; &nbsp;
+END:methods
+</div>
+ENDIF:methods
+
+IF:constants
+<table summary="Constants" cellpadding="5" width="100%">
+<tr><td class="tablesubtitle">Constants</td></tr>
+</table>
+<table cellpadding="5">
+START:constants
+<tr valign="top"><td>%name%</td><td>=</td><td>%value%</td></tr>
+IF:desc
+<tr><td></td><td></td><td>%desc%</td></tr>
+ENDIF:desc
+END:constants
+</table>
+ENDIF:constants
+
+IF:aliases
+<table summary="Aliases" cellpadding="5" width="100%">
+<tr><td class="tablesubtitle">External Aliases</td></tr>
+</table>
+<div class="name-list">
+START:aliases
+%old_name% -> %new_name%<br />
+END:aliases
+</div>
+ENDIF:aliases
+
+IF:attributes
+<table summary="Attributes" cellpadding="5" width="100%">
+<tr><td class="tablesubtitle">Attributes</td></tr>
+</table>
+<table summary="Attribute details" cellspacing="5">
+START:attributes
+ <tr valign="top">
+ <td class="attr-name">%name%</td>
+IF:rw
+ <td align="center" class="attr-rw">&nbsp;[%rw%]&nbsp;</td>
+ENDIF:rw
+IFNOT:rw
+ <td></td>
+ENDIF:rw
+ <td>%a_desc%</td>
+ </tr>
+END:attributes
+</table>
+ENDIF:attributes
+
+IF:classlist
+<table summary="List of classes" cellpadding="5" width="100%">
+<tr><td class="tablesubtitle">Classes and Modules</td></tr>
+</table>
+<div class="classlist">
+%classlist%
+</div>
+ENDIF:classlist
+}
+
+###############################################################################
+
+BODY = HEADER + %{
+<body bgcolor="white">
+!INCLUDE! <!-- banner header -->
+} +
+CONTEXT_CONTENT + METHOD_LIST +
+%{
+</body>
+</html>
+}
+
+
+###############################################################################
+
+FILE_PAGE = <<_FILE_PAGE_
+<table summary="Information on file" width="100%">
+ <tr class="title-row">
+ <td><table summary="layout" width="100%"><tr>
+ <td class="big-title-font" colspan="2">%short_name%</td>
+ <td align="right"><table summary="layout" cellspacing="0" cellpadding="2">
+ <tr>
+ <td class="small-title-font">Path:</td>
+ <td class="small-title-font">%full_path%
+IF:cvsurl
+ &nbsp;(<a href="%cvsurl%"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
+ENDIF:cvsurl
+ </td>
+ </tr>
+ <tr>
+ <td class="small-title-font">Modified:</td>
+ <td class="small-title-font">%dtm_modified%</td>
+ </tr>
+ </table>
+ </td></tr></table></td>
+ </tr>
+</table>
+_FILE_PAGE_
+
+###################################################################
+
+CLASS_PAGE = %{
+<table summary="Information on class" width="100%" border="0" cellspacing="0">
+ <tr class="title-row">
+ <td class="big-title-font">
+ <sup><font color="aqua">%classmod%</font></sup> %full_name%
+ </td>
+ <td align="right">
+ <table summary="layout" cellspacing="0" cellpadding="2">
+ <tr valign="top">
+ <td class="small-title-font">In:</td>
+ <td class="small-title-font">
+START:infiles
+IF:full_path_url
+ <a href="%full_path_url%" class="aqua">
+ENDIF:full_path_url
+%full_path%
+IF:full_path_url
+ </a>
+ENDIF:full_path_url
+IF:cvsurl
+ &nbsp;(<a href="%cvsurl%"><acronym title="Concurrent Versioning System">CVS</acronym></a>)
+ENDIF:cvsurl
+<br />
+END:infiles
+ </td>
+ </tr>
+IF:parent
+ <tr>
+ <td class="small-title-font">Parent:</td>
+ <td class="small-title-font">
+IF:par_url
+ <a href="%par_url%" class="aqua">
+ENDIF:par_url
+%parent%
+IF:par_url
+ </a>
+ENDIF:par_url
+ </td>
+ </tr>
+ENDIF:parent
+ </table>
+ </td>
+ </tr>
+</table>
+}
+
+=begin
+=end
+
+########################## Source code ##########################
+
+SRC_PAGE = %{
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
+<title>%title%</title>
+<link rel="stylesheet" href="%style_url%" type="text/css" media="screen" />
+</head>
+<body bgcolor="white">
+<pre>%code%</pre>
+</body>
+</html>
+}
+
+########################## Index ################################
+
+FR_INDEX_BODY = %{
+!INCLUDE!
+}
+
+FILE_INDEX = %{
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
+<title>%list_title%</title>
+<style type="text/css">
+<!--
+ body {
+background-color: #ddddff;
+ font-family: #{FONTS};
+ font-size: 11px;
+ font-style: normal;
+ line-height: 14px;
+ color: #000040;
+ }
+div.banner {
+ background: #0000aa;
+ color: white;
+ padding: 1;
+ margin: 0;
+ font-size: 90%;
+ font-weight: bold;
+ line-height: 1.1;
+ text-align: center;
+ width: 100%;
+}
+
+A.xx { color: white; font-weight: bold; }
+-->
+</style>
+<base target="docwin">
+</head>
+<body>
+<div class="banner"><a href="%index_url%" class="xx">%list_title%</a></div>
+START:entries
+<a href="%href%">%name%</a><br />
+END:entries
+</body></html>
+}
+
+CLASS_INDEX = FILE_INDEX
+METHOD_INDEX = FILE_INDEX
+
+INDEX = %{
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=%charset%">
+<title>%title%</title></head>
+
+<frameset rows="20%, 80%">
+ <frameset cols="25%,35%,45%">
+ <frame src="fr_file_index.html" title="Files" name="Files">
+ <frame src="fr_class_index.html" name="Classes">
+ <frame src="fr_method_index.html" name="Methods">
+ </frameset>
+ <frame src="%initial_page%" name="docwin">
+ <noframes>
+ <body bgcolor="white">
+ Sorry, RDoc currently only generates HTML using frames.
+ </body>
+ </noframes>
+</frameset>
+
+</html>
+}
+
+######################################################################
+#
+# The following is used for the -1 option
+#
+
+CONTENTS_XML = %{
+IF:description
+%description%
+ENDIF:description
+
+IF:requires
+<h4>Requires:</h4>
+<ul>
+START:requires
+IF:aref
+<li><a href="%aref%">%name%</a></li>
+ENDIF:aref
+IFNOT:aref
+<li>%name%</li>
+ENDIF:aref
+END:requires
+</ul>
+ENDIF:requires
+
+IF:attributes
+<h4>Attributes</h4>
+<table>
+START:attributes
+<tr><td>%name%</td><td>%rw%</td><td>%a_desc%</td></tr>
+END:attributes
+</table>
+ENDIF:attributes
+
+IF:includes
+<h4>Includes</h4>
+<ul>
+START:includes
+IF:aref
+<li><a href="%aref%">%name%</a></li>
+ENDIF:aref
+IFNOT:aref
+<li>%name%</li>
+ENDIF:aref
+END:includes
+</ul>
+ENDIF:includes
+
+IF:method_list
+<h3>Methods</h3>
+START:method_list
+IF:methods
+START:methods
+<h4>%type% %category% method: <a name="%aref%">%name%%params%</a></h4>
+
+IF:m_desc
+%m_desc%
+ENDIF:m_desc
+
+IF:sourcecode
+<blockquote><pre>
+%sourcecode%
+</pre></blockquote>
+ENDIF:sourcecode
+END:methods
+ENDIF:methods
+END:method_list
+ENDIF:method_list
+}
+
+
+end
+end
+
+require 'rdoc/generators/template/html/one_page_html'
diff --git a/ruby_1_8_5/lib/rdoc/generators/template/html/one_page_html.rb b/ruby_1_8_5/lib/rdoc/generators/template/html/one_page_html.rb
new file mode 100644
index 0000000000..19441f4725
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/generators/template/html/one_page_html.rb
@@ -0,0 +1,122 @@
+module RDoc
+module Page
+######################################################################
+#
+# The following is used for the -1 option
+#
+
+CONTENTS_XML = %{
+IF:description
+%description%
+ENDIF:description
+
+IF:requires
+<h4>Requires:</h4>
+<ul>
+START:requires
+IF:aref
+<li><a href="%aref%">%name%</a></li>
+ENDIF:aref
+IFNOT:aref
+<li>%name%</li>
+ENDIF:aref
+END:requires
+</ul>
+ENDIF:requires
+
+IF:attributes
+<h4>Attributes</h4>
+<table>
+START:attributes
+<tr><td>%name%</td><td>%rw%</td><td>%a_desc%</td></tr>
+END:attributes
+</table>
+ENDIF:attributes
+
+IF:includes
+<h4>Includes</h4>
+<ul>
+START:includes
+IF:aref
+<li><a href="%aref%">%name%</a></li>
+ENDIF:aref
+IFNOT:aref
+<li>%name%</li>
+ENDIF:aref
+END:includes
+</ul>
+ENDIF:includes
+
+IF:method_list
+<h3>Methods</h3>
+START:method_list
+IF:methods
+START:methods
+<h4>%type% %category% method:
+IF:callseq
+<a name="%aref%">%callseq%</a>
+ENDIF:callseq
+IFNOT:callseq
+<a name="%aref%">%name%%params%</a></h4>
+ENDIF:callseq
+
+IF:m_desc
+%m_desc%
+ENDIF:m_desc
+
+IF:sourcecode
+<blockquote><pre>
+%sourcecode%
+</pre></blockquote>
+ENDIF:sourcecode
+END:methods
+ENDIF:methods
+END:method_list
+ENDIF:method_list
+}
+
+########################################################################
+
+ONE_PAGE = %{
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <title>%title%</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=%charset%" />
+</head>
+<body>
+START:files
+<h2>File: %short_name%</h2>
+<table>
+ <tr><td>Path:</td><td>%full_path%</td></tr>
+ <tr><td>Modified:</td><td>%dtm_modified%</td></tr>
+</table>
+} + CONTENTS_XML + %{
+END:files
+
+IF:classes
+<h2>Classes</h2>
+START:classes
+IF:parent
+<h3>%classmod% %full_name% &lt; HREF:par_url:parent:</h3>
+ENDIF:parent
+IFNOT:parent
+<h3>%classmod% %full_name%</h3>
+ENDIF:parent
+
+IF:infiles
+(in files
+START:infiles
+HREF:full_path_url:full_path:
+END:infiles
+)
+ENDIF:infiles
+} + CONTENTS_XML + %{
+END:classes
+ENDIF:classes
+</body>
+</html>
+}
+
+end
+end
diff --git a/ruby_1_8_5/lib/rdoc/generators/template/xml/rdf.rb b/ruby_1_8_5/lib/rdoc/generators/template/xml/rdf.rb
new file mode 100644
index 0000000000..1545d81a2f
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/generators/template/xml/rdf.rb
@@ -0,0 +1,112 @@
+module RDoc
+module Page
+
+
+
+CONTENTS_RDF = %{
+IF:description
+ <description rd:parseType="Literal">
+%description%
+ </description>
+ENDIF:description
+
+IF:requires
+START:requires
+ <rd:required-file rd:name="%name%" />
+END:requires
+ENDIF:requires
+
+IF:attributes
+START:attributes
+ <contents>
+ <Attribute rd:name="%name%">
+IF:rw
+ <attribute-rw>%rw%</attribute-rw>
+ENDIF:rw
+ <description rdf:parseType="Literal">%a_desc%</description>
+ </Attribute>
+ </contents>
+END:attributes
+ENDIF:attributes
+
+IF:includes
+ <IncludedModuleList>
+START:includes
+ <included-module rd:name="%name%" />
+END:includes
+ </IncludedModuleList>
+ENDIF:includes
+
+IF:method_list
+START:method_list
+IF:methods
+START:methods
+ <contents>
+ <Method rd:name="%name%" rd:visibility="%type%"
+ rd:category="%category%" rd:id="%aref%">
+ <parameters>%params%</parameters>
+IF:m_desc
+ <description rdf:parseType="Literal">
+%m_desc%
+ </description>
+ENDIF:m_desc
+IF:sourcecode
+ <source-code-listing rdf:parseType="Literal">
+%sourcecode%
+ </source-code-listing>
+ENDIF:sourcecode
+ </Method>
+ </contents>
+END:methods
+ENDIF:methods
+END:method_list
+ENDIF:method_list
+ <!-- end method list -->
+}
+
+########################################################################
+
+ONE_PAGE = %{<?xml version="1.0" encoding="utf-8"?>
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://pragprog.com/rdoc/rdoc.rdf#"
+ xmlns:rd="http://pragprog.com/rdoc/rdoc.rdf#">
+
+<!-- RDoc -->
+START:files
+ <rd:File rd:name="%short_name%" rd:id="%href%">
+ <path>%full_path%</path>
+ <dtm-modified>%dtm_modified%</dtm-modified>
+} + CONTENTS_RDF + %{
+ </rd:File>
+END:files
+START:classes
+ <%classmod% rd:name="%full_name%" rd:id="%full_name%">
+ <classmod-info>
+IF:infiles
+ <InFiles>
+START:infiles
+ <infile>
+ <File rd:name="%full_path%"
+IF:full_path_url
+ rdf:about="%full_path_url%"
+ENDIF:full_path_url
+ />
+ </infile>
+END:infiles
+ </InFiles>
+ENDIF:infiles
+IF:parent
+ <superclass>HREF:par_url:parent:</superclass>
+ENDIF:parent
+ </classmod-info>
+} + CONTENTS_RDF + %{
+ </%classmod%>
+END:classes
+<!-- /RDoc -->
+</rdf:RDF>
+}
+
+
+end
+end
+
diff --git a/ruby_1_8_5/lib/rdoc/generators/template/xml/xml.rb b/ruby_1_8_5/lib/rdoc/generators/template/xml/xml.rb
new file mode 100644
index 0000000000..4a0c8c9ac4
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/generators/template/xml/xml.rb
@@ -0,0 +1,112 @@
+module RDoc
+module Page
+
+
+
+CONTENTS_XML = %{
+IF:description
+ <description>
+%description%
+ </description>
+ENDIF:description
+ <contents>
+IF:requires
+ <required-file-list>
+START:requires
+ <required-file name="%name%"
+IF:aref
+ href="%aref%"
+ENDIF:aref
+ />
+END:requires
+ </required-file-list>
+ENDIF:requires
+IF:attributes
+ <attribute-list>
+START:attributes
+ <attribute name="%name%">
+IF:rw
+ <attribute-rw>%rw%</attribute-rw>
+ENDIF:rw
+ <description>%a_desc%</description>
+ </attribute>
+END:attributes
+ </attribute-list>
+ENDIF:attributes
+IF:includes
+ <included-module-list>
+START:includes
+ <included-module name="%name%"
+IF:aref
+ href="%aref%"
+ENDIF:aref
+ />
+END:includes
+ </included-module-list>
+ENDIF:includes
+IF:method_list
+ <method-list>
+START:method_list
+IF:methods
+START:methods
+ <method name="%name%" type="%type%" category="%category%" id="%aref%">
+ <parameters>%params%</parameters>
+IF:m_desc
+ <description>
+%m_desc%
+ </description>
+ENDIF:m_desc
+IF:sourcecode
+ <source-code-listing>
+%sourcecode%
+ </source-code-listing>
+ENDIF:sourcecode
+ </method>
+END:methods
+ENDIF:methods
+END:method_list
+ </method-list>
+ENDIF:method_list
+ </contents>
+}
+
+########################################################################
+
+ONE_PAGE = %{<?xml version="1.0" encoding="utf-8"?>
+<rdoc>
+<file-list>
+START:files
+ <file name="%short_name%" id="%href%">
+ <file-info>
+ <path>%full_path%</path>
+ <dtm-modified>%dtm_modified%</dtm-modified>
+ </file-info>
+} + CONTENTS_XML + %{
+ </file>
+END:files
+</file-list>
+<class-module-list>
+START:classes
+ <%classmod% name="%full_name%" id="%full_name%">
+ <classmod-info>
+IF:infiles
+ <infiles>
+START:infiles
+ <infile>HREF:full_path_url:full_path:</infile>
+END:infiles
+ </infiles>
+ENDIF:infiles
+IF:parent
+ <superclass>HREF:par_url:parent:</superclass>
+ENDIF:parent
+ </classmod-info>
+} + CONTENTS_XML + %{
+ </%classmod%>
+END:classes
+</class-module-list>
+</rdoc>
+}
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/rdoc/generators/xml_generator.rb b/ruby_1_8_5/lib/rdoc/generators/xml_generator.rb
new file mode 100644
index 0000000000..8c1a76d62b
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/generators/xml_generator.rb
@@ -0,0 +1,130 @@
+
+require 'ftools'
+
+require 'rdoc/options'
+require 'rdoc/markup/simple_markup'
+require 'rdoc/markup/simple_markup/to_html'
+require 'rdoc/generators/html_generator'
+
+module Generators
+
+ # Generate XML output as one big file
+
+ class XMLGenerator < HTMLGenerator
+
+ # Standard generator factory
+ def XMLGenerator.for(options)
+ XMLGenerator.new(options)
+ end
+
+
+ def initialize(*args)
+ super
+ end
+
+ ##
+ # Build the initial indices and output objects
+ # based on an array of TopLevel objects containing
+ # the extracted information.
+
+ def generate(info)
+ @info = info
+ @files = []
+ @classes = []
+ @hyperlinks = {}
+
+ build_indices
+ generate_xml
+ end
+
+
+ ##
+ # Generate:
+ #
+ # * a list of HtmlFile objects for each TopLevel object.
+ # * a list of HtmlClass objects for each first level
+ # class or module in the TopLevel objects
+ # * a complete list of all hyperlinkable terms (file,
+ # class, module, and method names)
+
+ def build_indices
+
+ @info.each do |toplevel|
+ @files << HtmlFile.new(toplevel, @options, FILE_DIR)
+ end
+
+ RDoc::TopLevel.all_classes_and_modules.each do |cls|
+ build_class_list(cls, @files[0], CLASS_DIR)
+ end
+ end
+
+ def build_class_list(from, html_file, class_dir)
+ @classes << HtmlClass.new(from, html_file, class_dir, @options)
+ from.each_classmodule do |mod|
+ build_class_list(mod, html_file, class_dir)
+ end
+ end
+
+ ##
+ # Generate all the HTML. For the one-file case, we generate
+ # all the information in to one big hash
+ #
+ def generate_xml
+ values = {
+ 'charset' => @options.charset,
+ 'files' => gen_into(@files),
+ 'classes' => gen_into(@classes)
+ }
+
+ # this method is defined in the template file
+ write_extra_pages if defined? write_extra_pages
+
+ template = TemplatePage.new(RDoc::Page::ONE_PAGE)
+
+ if @options.op_name
+ opfile = File.open(@options.op_name, "w")
+ else
+ opfile = $stdout
+ end
+ template.write_html_on(opfile, values)
+ end
+
+ def gen_into(list)
+ res = []
+ list.each do |item|
+ res << item.value_hash
+ end
+ res
+ end
+
+ def gen_file_index
+ gen_an_index(@files, 'Files')
+ end
+
+ def gen_class_index
+ gen_an_index(@classes, 'Classes')
+ end
+
+ def gen_method_index
+ gen_an_index(HtmlMethod.all_methods, 'Methods')
+ end
+
+
+ def gen_an_index(collection, title)
+ res = []
+ collection.sort.each do |f|
+ if f.document_self
+ res << { "href" => f.path, "name" => f.index_name }
+ end
+ end
+
+ return {
+ "entries" => res,
+ 'list_title' => title,
+ 'index_url' => main_url,
+ }
+ end
+
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rdoc/markup/.document b/ruby_1_8_5/lib/rdoc/markup/.document
new file mode 100644
index 0000000000..3cf4f21bd7
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/markup/.document
@@ -0,0 +1,2 @@
+simple_markup
+simple_markup.rb
diff --git a/ruby_1_8_5/lib/rdoc/markup/sample/rdoc2latex.rb b/ruby_1_8_5/lib/rdoc/markup/sample/rdoc2latex.rb
new file mode 100644
index 0000000000..26563b75da
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/markup/sample/rdoc2latex.rb
@@ -0,0 +1,16 @@
+#!/usr/local/bin/ruby
+# Illustration of a script to convert an RDoc-style file to a LaTeX
+# document
+
+require 'rdoc/markup/simple_markup'
+require 'rdoc/markup/simple_markup/to_latex'
+
+p = SM::SimpleMarkup.new
+h = SM::ToLaTeX.new
+
+#puts "\\documentclass{report}"
+#puts "\\usepackage{tabularx}"
+#puts "\\usepackage{parskip}"
+#puts "\\begin{document}"
+puts p.convert(ARGF.read, h)
+#puts "\\end{document}"
diff --git a/ruby_1_8_5/lib/rdoc/markup/sample/sample.rb b/ruby_1_8_5/lib/rdoc/markup/sample/sample.rb
new file mode 100644
index 0000000000..a375b54564
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/markup/sample/sample.rb
@@ -0,0 +1,42 @@
+# This program illustrates the basic use of the SimpleMarkup
+# class. It extracts the first comment block from the
+# simple_markup.rb file and converts it into HTML on
+# standard output. Run it using
+#
+# % ruby sample.rb
+#
+# You should be in the sample/ directory when you do this,
+# as it hardwires the path to the files it needs to require.
+# This isn't necessary in the code you write once you've
+# installed the package.
+#
+# For a better way of formatting code comment blocks (and more)
+# see the rdoc package.
+#
+
+$:.unshift "../../.."
+
+require 'rdoc/markup/simple_markup'
+require 'rdoc/markup/simple_markup/to_html'
+
+# Extract the comment block from the source file
+
+input_string = ""
+
+File.foreach("../simple_markup.rb") do |line|
+ break unless line.gsub!(/^\# ?/, '')
+ input_string << line
+end
+
+# Create a markup object
+markup = SM::SimpleMarkup.new
+
+# Attach it to an HTML formatter
+h = SM::ToHtml.new
+
+# And convert out comment block to html. Wrap it a body
+# tag pair to let browsers view it
+
+puts "<html><body>"
+puts markup.convert(input_string, h)
+puts "</body></html>"
diff --git a/ruby_1_8_5/lib/rdoc/markup/simple_markup.rb b/ruby_1_8_5/lib/rdoc/markup/simple_markup.rb
new file mode 100644
index 0000000000..8193ca02d4
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/markup/simple_markup.rb
@@ -0,0 +1,476 @@
+# = Introduction
+#
+# SimpleMarkup parses plain text documents and attempts to decompose
+# them into their constituent parts. Some of these parts are high-level:
+# paragraphs, chunks of verbatim text, list entries and the like. Other
+# parts happen at the character level: a piece of bold text, a word in
+# code font. This markup is similar in spirit to that used on WikiWiki
+# webs, where folks create web pages using a simple set of formatting
+# rules.
+#
+# SimpleMarkup itself does no output formatting: this is left to a
+# different set of classes.
+#
+# SimpleMarkup is extendable at runtime: you can add new markup
+# elements to be recognised in the documents that SimpleMarkup parses.
+#
+# SimpleMarkup is intended to be the basis for a family of tools which
+# share the common requirement that simple, plain-text should be
+# rendered in a variety of different output formats and media. It is
+# envisaged that SimpleMarkup could be the basis for formating RDoc
+# style comment blocks, Wiki entries, and online FAQs.
+#
+# = Basic Formatting
+#
+# * SimpleMarkup looks for a document's natural left margin. This is
+# used as the initial margin for the document.
+#
+# * Consecutive lines starting at this margin are considered to be a
+# paragraph.
+#
+# * If a paragraph starts with a "*", "-", or with "<digit>.", then it is
+# taken to be the start of a list. The margin in increased to be the
+# first non-space following the list start flag. Subsequent lines
+# should be indented to this new margin until the list ends. For
+# example:
+#
+# * this is a list with three paragraphs in
+# the first item. This is the first paragraph.
+#
+# And this is the second paragraph.
+#
+# 1. This is an indented, numbered list.
+# 2. This is the second item in that list
+#
+# This is the third conventional paragraph in the
+# first list item.
+#
+# * This is the second item in the original list
+#
+# * You can also construct labeled lists, sometimes called description
+# or definition lists. Do this by putting the label in square brackets
+# and indenting the list body:
+#
+# [cat] a small furry mammal
+# that seems to sleep a lot
+#
+# [ant] a little insect that is known
+# to enjoy picnics
+#
+# A minor variation on labeled lists uses two colons to separate the
+# label from the list body:
+#
+# cat:: a small furry mammal
+# that seems to sleep a lot
+#
+# ant:: a little insect that is known
+# to enjoy picnics
+#
+# This latter style guarantees that the list bodies' left margins are
+# aligned: think of them as a two column table.
+#
+# * Any line that starts to the right of the current margin is treated
+# as verbatim text. This is useful for code listings. The example of a
+# list above is also verbatim text.
+#
+# * A line starting with an equals sign (=) is treated as a
+# heading. Level one headings have one equals sign, level two headings
+# have two,and so on.
+#
+# * A line starting with three or more hyphens (at the current indent)
+# generates a horizontal rule. THe more hyphens, the thicker the rule
+# (within reason, and if supported by the output device)
+#
+# * You can use markup within text (except verbatim) to change the
+# appearance of parts of that text. Out of the box, SimpleMarkup
+# supports word-based and general markup.
+#
+# Word-based markup uses flag characters around individual words:
+#
+# [\*word*] displays word in a *bold* font
+# [\_word_] displays word in an _emphasized_ font
+# [\+word+] displays word in a +code+ font
+#
+# General markup affects text between a start delimiter and and end
+# delimiter. Not surprisingly, these delimiters look like HTML markup.
+#
+# [\<b>text...</b>] displays word in a *bold* font
+# [\<em>text...</em>] displays word in an _emphasized_ font
+# [\<i>text...</i>] displays word in an _emphasized_ font
+# [\<tt>text...</tt>] displays word in a +code+ font
+#
+# Unlike conventional Wiki markup, general markup can cross line
+# boundaries. You can turn off the interpretation of markup by
+# preceding the first character with a backslash, so \\\<b>bold
+# text</b> and \\\*bold* produce \<b>bold text</b> and \*bold
+# respectively.
+#
+# = Using SimpleMarkup
+#
+# For information on using SimpleMarkup programatically,
+# see SM::SimpleMarkup.
+#
+# Author:: Dave Thomas, dave@pragmaticprogrammer.com
+# Version:: 0.0
+# License:: Ruby license
+
+
+
+require 'rdoc/markup/simple_markup/fragments'
+require 'rdoc/markup/simple_markup/lines.rb'
+
+module SM #:nodoc:
+
+ # == Synopsis
+ #
+ # This code converts <tt>input_string</tt>, which is in the format
+ # described in markup/simple_markup.rb, to HTML. The conversion
+ # takes place in the +convert+ method, so you can use the same
+ # SimpleMarkup object to convert multiple input strings.
+ #
+ # require 'rdoc/markup/simple_markup'
+ # require 'rdoc/markup/simple_markup/to_html'
+ #
+ # p = SM::SimpleMarkup.new
+ # h = SM::ToHtml.new
+ #
+ # puts p.convert(input_string, h)
+ #
+ # You can extend the SimpleMarkup parser to recognise new markup
+ # sequences, and to add special processing for text that matches a
+ # regular epxression. Here we make WikiWords significant to the parser,
+ # and also make the sequences {word} and \<no>text...</no> signify
+ # strike-through text. When then subclass the HTML output class to deal
+ # with these:
+ #
+ # require 'rdoc/markup/simple_markup'
+ # require 'rdoc/markup/simple_markup/to_html'
+ #
+ # class WikiHtml < SM::ToHtml
+ # def handle_special_WIKIWORD(special)
+ # "<font color=red>" + special.text + "</font>"
+ # end
+ # end
+ #
+ # p = SM::SimpleMarkup.new
+ # p.add_word_pair("{", "}", :STRIKE)
+ # p.add_html("no", :STRIKE)
+ #
+ # p.add_special(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD)
+ #
+ # h = WikiHtml.new
+ # h.add_tag(:STRIKE, "<strike>", "</strike>")
+ #
+ # puts "<body>" + p.convert(ARGF.read, h) + "</body>"
+ #
+ # == Output Formatters
+ #
+ # _missing_
+ #
+ #
+
+ class SimpleMarkup
+
+ SPACE = ?\s
+
+ # List entries look like:
+ # * text
+ # 1. text
+ # [label] text
+ # label:: text
+ #
+ # Flag it as a list entry, and
+ # work out the indent for subsequent lines
+
+ SIMPLE_LIST_RE = /^(
+ ( \* (?# bullet)
+ |- (?# bullet)
+ |\d+\. (?# numbered )
+ |[A-Za-z]\. (?# alphabetically numbered )
+ )
+ \s+
+ )\S/x
+
+ LABEL_LIST_RE = /^(
+ ( \[.*?\] (?# labeled )
+ |\S.*:: (?# note )
+ )(?:\s+|$)
+ )/x
+
+
+ ##
+ # take a block of text and use various heuristics to determine
+ # it's structure (paragraphs, lists, and so on). Invoke an
+ # event handler as we identify significant chunks.
+ #
+
+ def initialize
+ @am = AttributeManager.new
+ @output = nil
+ end
+
+ ##
+ # Add to the sequences used to add formatting to an individual word
+ # (such as *bold*). Matching entries will generate attibutes
+ # that the output formatters can recognize by their +name+
+
+ def add_word_pair(start, stop, name)
+ @am.add_word_pair(start, stop, name)
+ end
+
+ ##
+ # Add to the sequences recognized as general markup
+ #
+
+ def add_html(tag, name)
+ @am.add_html(tag, name)
+ end
+
+ ##
+ # Add to other inline sequences. For example, we could add
+ # WikiWords using something like:
+ #
+ # parser.add_special(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD)
+ #
+ # Each wiki word will be presented to the output formatter
+ # via the accept_special method
+ #
+
+ def add_special(pattern, name)
+ @am.add_special(pattern, name)
+ end
+
+
+ # We take a string, split it into lines, work out the type of
+ # each line, and from there deduce groups of lines (for example
+ # all lines in a paragraph). We then invoke the output formatter
+ # using a Visitor to display the result
+
+ def convert(str, op)
+ @lines = Lines.new(str.split(/\r?\n/).collect { |aLine|
+ Line.new(aLine) })
+ return "" if @lines.empty?
+ @lines.normalize
+ assign_types_to_lines
+ group = group_lines
+ # call the output formatter to handle the result
+ # group.to_a.each {|i| p i}
+ group.accept(@am, op)
+ end
+
+
+ #######
+ private
+ #######
+
+
+ ##
+ # Look through the text at line indentation. We flag each line as being
+ # Blank, a paragraph, a list element, or verbatim text
+ #
+
+ def assign_types_to_lines(margin = 0, level = 0)
+
+ while line = @lines.next
+ if line.isBlank?
+ line.stamp(Line::BLANK, level)
+ next
+ end
+
+ # if a line contains non-blanks before the margin, then it must belong
+ # to an outer level
+
+ text = line.text
+
+ for i in 0...margin
+ if text[i] != SPACE
+ @lines.unget
+ return
+ end
+ end
+
+ active_line = text[margin..-1]
+
+ # Rules (horizontal lines) look like
+ #
+ # --- (three or more hyphens)
+ #
+ # The more hyphens, the thicker the rule
+ #
+
+ if /^(---+)\s*$/ =~ active_line
+ line.stamp(Line::RULE, level, $1.length-2)
+ next
+ end
+
+ # Then look for list entries. First the ones that have to have
+ # text following them (* xxx, - xxx, and dd. xxx)
+
+ if SIMPLE_LIST_RE =~ active_line
+
+ offset = margin + $1.length
+ prefix = $2
+ prefix_length = prefix.length
+
+ flag = case prefix
+ when "*","-" then ListBase::BULLET
+ when /^\d/ then ListBase::NUMBER
+ when /^[A-Z]/ then ListBase::UPPERALPHA
+ when /^[a-z]/ then ListBase::LOWERALPHA
+ else raise "Invalid List Type: #{self.inspect}"
+ end
+
+ line.stamp(Line::LIST, level+1, prefix, flag)
+ text[margin, prefix_length] = " " * prefix_length
+ assign_types_to_lines(offset, level + 1)
+ next
+ end
+
+
+ if LABEL_LIST_RE =~ active_line
+ offset = margin + $1.length
+ prefix = $2
+ prefix_length = prefix.length
+
+ next if handled_labeled_list(line, level, margin, offset, prefix)
+ end
+
+ # Headings look like
+ # = Main heading
+ # == Second level
+ # === Third
+ #
+ # Headings reset the level to 0
+
+ if active_line[0] == ?= and active_line =~ /^(=+)\s*(.*)/
+ prefix_length = $1.length
+ prefix_length = 6 if prefix_length > 6
+ line.stamp(Line::HEADING, 0, prefix_length)
+ line.strip_leading(margin + prefix_length)
+ next
+ end
+
+ # If the character's a space, then we have verbatim text,
+ # otherwise
+
+ if active_line[0] == SPACE
+ line.strip_leading(margin) if margin > 0
+ line.stamp(Line::VERBATIM, level)
+ else
+ line.stamp(Line::PARAGRAPH, level)
+ end
+ end
+ end
+
+ # Handle labeled list entries, We have a special case
+ # to deal with. Because the labels can be long, they force
+ # the remaining block of text over the to right:
+ #
+ # this is a long label that I wrote:: and here is the
+ # block of text with
+ # a silly margin
+ #
+ # So we allow the special case. If the label is followed
+ # by nothing, and if the following line is indented, then
+ # we take the indent of that line as the new margin
+ #
+ # this is a long label that I wrote::
+ # here is a more reasonably indented block which
+ # will ab attached to the label.
+ #
+
+ def handled_labeled_list(line, level, margin, offset, prefix)
+ prefix_length = prefix.length
+ text = line.text
+ flag = nil
+ case prefix
+ when /^\[/
+ flag = ListBase::LABELED
+ prefix = prefix[1, prefix.length-2]
+ when /:$/
+ flag = ListBase::NOTE
+ prefix.chop!
+ else raise "Invalid List Type: #{self.inspect}"
+ end
+
+ # body is on the next line
+
+ if text.length <= offset
+ original_line = line
+ line = @lines.next
+ return(false) unless line
+ text = line.text
+
+ for i in 0..margin
+ if text[i] != SPACE
+ @lines.unget
+ return false
+ end
+ end
+ i = margin
+ i += 1 while text[i] == SPACE
+ if i >= text.length
+ @lines.unget
+ return false
+ else
+ offset = i
+ prefix_length = 0
+ @lines.delete(original_line)
+ end
+ end
+
+ line.stamp(Line::LIST, level+1, prefix, flag)
+ text[margin, prefix_length] = " " * prefix_length
+ assign_types_to_lines(offset, level + 1)
+ return true
+ end
+
+ # Return a block consisting of fragments which are
+ # paragraphs, list entries or verbatim text. We merge consecutive
+ # lines of the same type and level together. We are also slightly
+ # tricky with lists: the lines following a list introduction
+ # look like paragraph lines at the next level, and we remap them
+ # into list entries instead
+
+ def group_lines
+ @lines.rewind
+
+ inList = false
+ wantedType = wantedLevel = nil
+
+ block = LineCollection.new
+ group = nil
+
+ while line = @lines.next
+ if line.level == wantedLevel and line.type == wantedType
+ group.add_text(line.text)
+ else
+ group = block.fragment_for(line)
+ block.add(group)
+ if line.type == Line::LIST
+ wantedType = Line::PARAGRAPH
+ else
+ wantedType = line.type
+ end
+ wantedLevel = line.type == Line::HEADING ? line.param : line.level
+ end
+ end
+
+ block.normalize
+ block
+ end
+
+ ## for debugging, we allow access to our line contents as text
+ def content
+ @lines.as_text
+ end
+ public :content
+
+ ## for debugging, return the list of line types
+ def get_line_types
+ @lines.line_types
+ end
+ public :get_line_types
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rdoc/markup/simple_markup/fragments.rb b/ruby_1_8_5/lib/rdoc/markup/simple_markup/fragments.rb
new file mode 100644
index 0000000000..6ca06382ab
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/markup/simple_markup/fragments.rb
@@ -0,0 +1,328 @@
+require 'rdoc/markup/simple_markup/lines.rb'
+#require 'rdoc/markup/simple_markup/to_flow.rb'
+
+module SM
+
+ ##
+ # A Fragment is a chunk of text, subclassed as a paragraph, a list
+ # entry, or verbatim text
+
+ class Fragment
+ attr_reader :level, :param, :txt
+ attr_accessor :type
+
+ def initialize(level, param, type, txt)
+ @level = level
+ @param = param
+ @type = type
+ @txt = ""
+ add_text(txt) if txt
+ end
+
+ def add_text(txt)
+ @txt << " " if @txt.length > 0
+ @txt << txt.tr_s("\n ", " ").strip
+ end
+
+ def to_s
+ "L#@level: #{self.class.name.split('::')[-1]}\n#@txt"
+ end
+
+ ######
+ # This is a simple factory system that lets us associate fragement
+ # types (a string) with a subclass of fragment
+
+ TYPE_MAP = {}
+
+ def Fragment.type_name(name)
+ TYPE_MAP[name] = self
+ end
+
+ def Fragment.for(line)
+ klass = TYPE_MAP[line.type] ||
+ raise("Unknown line type: '#{line.type.inspect}:' '#{line.text}'")
+ return klass.new(line.level, line.param, line.flag, line.text)
+ end
+ end
+
+ ##
+ # A paragraph is a fragment which gets wrapped to fit. We remove all
+ # newlines when we're created, and have them put back on output
+
+ class Paragraph < Fragment
+ type_name Line::PARAGRAPH
+ end
+
+ class BlankLine < Paragraph
+ type_name Line::BLANK
+ end
+
+ class Heading < Paragraph
+ type_name Line::HEADING
+
+ def head_level
+ @param.to_i
+ end
+ end
+
+ ##
+ # A List is a fragment with some kind of label
+ #
+
+ class ListBase < Paragraph
+ # List types
+ BULLET = :BULLET
+ NUMBER = :NUMBER
+ UPPERALPHA = :UPPERALPHA
+ LOWERALPHA = :LOWERALPHA
+ LABELED = :LABELED
+ NOTE = :NOTE
+ end
+
+ class ListItem < ListBase
+ type_name Line::LIST
+
+ # def label
+ # am = AttributeManager.new(@param)
+ # am.flow
+ # end
+ end
+
+ class ListStart < ListBase
+ def initialize(level, param, type)
+ super(level, param, type, nil)
+ end
+ end
+
+ class ListEnd < ListBase
+ def initialize(level, type)
+ super(level, "", type, nil)
+ end
+ end
+
+ ##
+ # Verbatim code contains lines that don't get wrapped.
+
+ class Verbatim < Fragment
+ type_name Line::VERBATIM
+
+ def add_text(txt)
+ @txt << txt.chomp << "\n"
+ end
+
+ end
+
+ ##
+ # A horizontal rule
+ class Rule < Fragment
+ type_name Line::RULE
+ end
+
+
+ # Collect groups of lines together. Each group
+ # will end up containing a flow of text
+
+ class LineCollection
+
+ def initialize
+ @fragments = []
+ end
+
+ def add(fragment)
+ @fragments << fragment
+ end
+
+ def each(&b)
+ @fragments.each(&b)
+ end
+
+ # For testing
+ def to_a
+ @fragments.map {|fragment| fragment.to_s}
+ end
+
+ # Factory for different fragment types
+ def fragment_for(*args)
+ Fragment.for(*args)
+ end
+
+ # tidy up at the end
+ def normalize
+ change_verbatim_blank_lines
+ add_list_start_and_ends
+ add_list_breaks
+ tidy_blank_lines
+ end
+
+ def to_s
+ @fragments.join("\n----\n")
+ end
+
+ def accept(am, visitor)
+
+ visitor.start_accepting
+
+ @fragments.each do |fragment|
+ case fragment
+ when Verbatim
+ visitor.accept_verbatim(am, fragment)
+ when Rule
+ visitor.accept_rule(am, fragment)
+ when ListStart
+ visitor.accept_list_start(am, fragment)
+ when ListEnd
+ visitor.accept_list_end(am, fragment)
+ when ListItem
+ visitor.accept_list_item(am, fragment)
+ when BlankLine
+ visitor.accept_blank_line(am, fragment)
+ when Heading
+ visitor.accept_heading(am, fragment)
+ when Paragraph
+ visitor.accept_paragraph(am, fragment)
+ end
+ end
+
+ visitor.end_accepting
+ end
+ #######
+ private
+ #######
+
+ # If you have:
+ #
+ # normal paragraph text.
+ #
+ # this is code
+ #
+ # and more code
+ #
+ # You'll end up with the fragments Paragraph, BlankLine,
+ # Verbatim, BlankLine, Verbatim, BlankLine, etc
+ #
+ # The BlankLine in the middle of the verbatim chunk needs to
+ # be changed to a real verbatim newline, and the two
+ # verbatim blocks merged
+ #
+ #
+ def change_verbatim_blank_lines
+ frag_block = nil
+ blank_count = 0
+ @fragments.each_with_index do |frag, i|
+ if frag_block.nil?
+ frag_block = frag if Verbatim === frag
+ else
+ case frag
+ when Verbatim
+ blank_count.times { frag_block.add_text("\n") }
+ blank_count = 0
+ frag_block.add_text(frag.txt)
+ @fragments[i] = nil # remove out current fragment
+ when BlankLine
+ if frag_block
+ blank_count += 1
+ @fragments[i] = nil
+ end
+ else
+ frag_block = nil
+ blank_count = 0
+ end
+ end
+ end
+ @fragments.compact!
+ end
+
+ # List nesting is implicit given the level of
+ # Make it explicit, just to make life a tad
+ # easier for the output processors
+
+ def add_list_start_and_ends
+ level = 0
+ res = []
+ type_stack = []
+
+ @fragments.each do |fragment|
+ # $stderr.puts "#{level} : #{fragment.class.name} : #{fragment.level}"
+ new_level = fragment.level
+ while (level < new_level)
+ level += 1
+ type = fragment.type
+ res << ListStart.new(level, fragment.param, type) if type
+ type_stack.push type
+ # $stderr.puts "Start: #{level}"
+ end
+
+ while level > new_level
+ type = type_stack.pop
+ res << ListEnd.new(level, type) if type
+ level -= 1
+ # $stderr.puts "End: #{level}, #{type}"
+ end
+
+ res << fragment
+ level = fragment.level
+ end
+ level.downto(1) do |i|
+ type = type_stack.pop
+ res << ListEnd.new(i, type) if type
+ end
+
+ @fragments = res
+ end
+
+ # now insert start/ends between list entries at the
+ # same level that have different element types
+
+ def add_list_breaks
+ res = @fragments
+
+ @fragments = []
+ list_stack = []
+
+ res.each do |fragment|
+ case fragment
+ when ListStart
+ list_stack.push fragment
+ when ListEnd
+ start = list_stack.pop
+ fragment.type = start.type
+ when ListItem
+ l = list_stack.last
+ if fragment.type != l.type
+ @fragments << ListEnd.new(l.level, l.type)
+ start = ListStart.new(l.level, fragment.param, fragment.type)
+ @fragments << start
+ list_stack.pop
+ list_stack.push start
+ end
+ else
+ ;
+ end
+ @fragments << fragment
+ end
+ end
+
+ # Finally tidy up the blank lines:
+ # * change Blank/ListEnd into ListEnd/Blank
+ # * remove blank lines at the front
+
+ def tidy_blank_lines
+ (@fragments.size - 1).times do |i|
+ if @fragments[i].kind_of?(BlankLine) and
+ @fragments[i+1].kind_of?(ListEnd)
+ @fragments[i], @fragments[i+1] = @fragments[i+1], @fragments[i]
+ end
+ end
+
+ # remove leading blanks
+ @fragments.each_with_index do |f, i|
+ break unless f.kind_of? BlankLine
+ @fragments[i] = nil
+ end
+
+ @fragments.compact!
+ end
+
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rdoc/markup/simple_markup/inline.rb b/ruby_1_8_5/lib/rdoc/markup/simple_markup/inline.rb
new file mode 100644
index 0000000000..d54fe1e667
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/markup/simple_markup/inline.rb
@@ -0,0 +1,340 @@
+module SM
+
+ # We manage a set of attributes. Each attribute has a symbol name
+ # and a bit value
+
+ class Attribute
+ SPECIAL = 1
+
+ @@name_to_bitmap = { :_SPECIAL_ => SPECIAL }
+ @@next_bitmap = 2
+
+ def Attribute.bitmap_for(name)
+ bitmap = @@name_to_bitmap[name]
+ if !bitmap
+ bitmap = @@next_bitmap
+ @@next_bitmap <<= 1
+ @@name_to_bitmap[name] = bitmap
+ end
+ bitmap
+ end
+
+ def Attribute.as_string(bitmap)
+ return "none" if bitmap.zero?
+ res = []
+ @@name_to_bitmap.each do |name, bit|
+ res << name if (bitmap & bit) != 0
+ end
+ res.join(",")
+ end
+
+ def Attribute.each_name_of(bitmap)
+ @@name_to_bitmap.each do |name, bit|
+ next if bit == SPECIAL
+ yield name.to_s if (bitmap & bit) != 0
+ end
+ end
+ end
+
+
+ # An AttrChanger records a change in attributes. It contains
+ # a bitmap of the attributes to turn on, and a bitmap of those to
+ # turn off
+
+ AttrChanger = Struct.new(:turn_on, :turn_off)
+ class AttrChanger
+ def to_s
+ "Attr: +#{Attribute.as_string(@turn_on)}/-#{Attribute.as_string(@turn_on)}"
+ end
+ end
+
+ # An array of attributes which parallels the characters in a string
+ class AttrSpan
+ def initialize(length)
+ @attrs = Array.new(length, 0)
+ end
+
+ def set_attrs(start, length, bits)
+ for i in start ... (start+length)
+ @attrs[i] |= bits
+ end
+ end
+
+ def [](n)
+ @attrs[n]
+ end
+ end
+
+ ##
+ # Hold details of a special sequence
+
+ class Special
+ attr_reader :type
+ attr_accessor :text
+
+ def initialize(type, text)
+ @type, @text = type, text
+ end
+
+ def ==(o)
+ self.text == o.text && self.type == o.type
+ end
+
+ def to_s
+ "Special: type=#{type}, text=#{text.dump}"
+ end
+ end
+
+ class AttributeManager
+
+ NULL = "\000".freeze
+
+ ##
+ # We work by substituting non-printing characters in to the
+ # text. For now I'm assuming that I can substitute
+ # a character in the range 0..8 for a 7 bit character
+ # without damaging the encoded string, but this might
+ # be optimistic
+ #
+
+ A_PROTECT = 004
+ PROTECT_ATTR = A_PROTECT.chr
+
+ # This maps delimiters that occur around words (such as
+ # *bold* or +tt+) where the start and end delimiters
+ # and the same. This lets us optimize the regexp
+ MATCHING_WORD_PAIRS = {}
+
+ # And this is used when the delimiters aren't the same. In this
+ # case the hash maps a pattern to the attribute character
+ WORD_PAIR_MAP = {}
+
+ # This maps HTML tags to the corresponding attribute char
+ HTML_TAGS = {}
+
+ # And this maps _special_ sequences to a name. A special sequence
+ # is something like a WikiWord
+ SPECIAL = {}
+
+ # Return an attribute object with the given turn_on
+ # and turn_off bits set
+
+ def attribute(turn_on, turn_off)
+ AttrChanger.new(turn_on, turn_off)
+ end
+
+
+ def change_attribute(current, new)
+ diff = current ^ new
+ attribute(new & diff, current & diff)
+ end
+
+ def changed_attribute_by_name(current_set, new_set)
+ current = new = 0
+ current_set.each {|name| current |= Attribute.bitmap_for(name) }
+ new_set.each {|name| new |= Attribute.bitmap_for(name) }
+ change_attribute(current, new)
+ end
+
+ def copy_string(start_pos, end_pos)
+ res = @str[start_pos...end_pos]
+ res.gsub!(/\000/, '')
+ res
+ end
+
+ # Map attributes like <b>text</b>to the sequence \001\002<char>\001\003<char>,
+ # where <char> is a per-attribute specific character
+
+ def convert_attrs(str, attrs)
+ # first do matching ones
+ tags = MATCHING_WORD_PAIRS.keys.join("")
+ re = "(^|\\W)([#{tags}])([A-Za-z_]+?)\\2(\\W|\$)"
+# re = "(^|\\W)([#{tags}])(\\S+?)\\2(\\W|\$)"
+ 1 while str.gsub!(Regexp.new(re)) {
+ attr = MATCHING_WORD_PAIRS[$2];
+ attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr)
+ $1 + NULL*$2.length + $3 + NULL*$2.length + $4
+ }
+
+ # then non-matching
+ unless WORD_PAIR_MAP.empty?
+ WORD_PAIR_MAP.each do |regexp, attr|
+ str.gsub!(regexp) {
+ attrs.set_attrs($`.length + $1.length, $2.length, attr)
+ NULL*$1.length + $2 + NULL*$3.length
+ }
+ end
+ end
+ end
+
+ def convert_html(str, attrs)
+ tags = HTML_TAGS.keys.join("|")
+ re = "<(#{tags})>(.*?)</\\1>"
+ 1 while str.gsub!(Regexp.new(re, Regexp::IGNORECASE)) {
+ attr = HTML_TAGS[$1.downcase]
+ html_length = $1.length + 2
+ seq = NULL * html_length
+ attrs.set_attrs($`.length + html_length, $2.length, attr)
+ seq + $2 + seq + NULL
+ }
+ end
+
+ def convert_specials(str, attrs)
+ unless SPECIAL.empty?
+ SPECIAL.each do |regexp, attr|
+ str.scan(regexp) do
+ attrs.set_attrs($`.length, $&.length, attr | Attribute::SPECIAL)
+ end
+ end
+ end
+ end
+
+ # A \ in front of a character that would normally be
+ # processed turns off processing. We do this by turning
+ # \< into <#{PROTECT}
+
+ PROTECTABLE = [ "<" << "\\" ] #"
+
+
+ def mask_protected_sequences
+ protect_pattern = Regexp.new("\\\\([#{Regexp.escape(PROTECTABLE.join(''))}])")
+ @str.gsub!(protect_pattern, "\\1#{PROTECT_ATTR}")
+ end
+
+ def unmask_protected_sequences
+ @str.gsub!(/(.)#{PROTECT_ATTR}/, "\\1\000")
+ end
+
+ def initialize
+ add_word_pair("*", "*", :BOLD)
+ add_word_pair("_", "_", :EM)
+ add_word_pair("+", "+", :TT)
+
+ add_html("em", :EM)
+ add_html("i", :EM)
+ add_html("b", :BOLD)
+ add_html("tt", :TT)
+ add_html("code", :TT)
+
+ add_special(/<!--(.*?)-->/, :COMMENT)
+ end
+
+ def add_word_pair(start, stop, name)
+ raise "Word flags may not start '<'" if start[0] == ?<
+ bitmap = Attribute.bitmap_for(name)
+ if start == stop
+ MATCHING_WORD_PAIRS[start] = bitmap
+ else
+ pattern = Regexp.new("(" + Regexp.escape(start) + ")" +
+# "([A-Za-z]+)" +
+ "(\\S+)" +
+ "(" + Regexp.escape(stop) +")")
+ WORD_PAIR_MAP[pattern] = bitmap
+ end
+ PROTECTABLE << start[0,1]
+ PROTECTABLE.uniq!
+ end
+
+ def add_html(tag, name)
+ HTML_TAGS[tag.downcase] = Attribute.bitmap_for(name)
+ end
+
+ def add_special(pattern, name)
+ SPECIAL[pattern] = Attribute.bitmap_for(name)
+ end
+
+ def flow(str)
+ @str = str
+
+ puts("Before flow, str='#{@str.dump}'") if $DEBUG
+ mask_protected_sequences
+
+ @attrs = AttrSpan.new(@str.length)
+
+ puts("After protecting, str='#{@str.dump}'") if $DEBUG
+ convert_attrs(@str, @attrs)
+ convert_html(@str, @attrs)
+ convert_specials(str, @attrs)
+ unmask_protected_sequences
+ puts("After flow, str='#{@str.dump}'") if $DEBUG
+ return split_into_flow
+ end
+
+ def display_attributes
+ puts
+ puts @str.tr(NULL, "!")
+ bit = 1
+ 16.times do |bno|
+ line = ""
+ @str.length.times do |i|
+ if (@attrs[i] & bit) == 0
+ line << " "
+ else
+ if bno.zero?
+ line << "S"
+ else
+ line << ("%d" % (bno+1))
+ end
+ end
+ end
+ puts(line) unless line =~ /^ *$/
+ bit <<= 1
+ end
+ end
+
+ def split_into_flow
+
+ display_attributes if $DEBUG
+
+ res = []
+ current_attr = 0
+ str = ""
+
+
+ str_len = @str.length
+
+ # skip leading invisible text
+ i = 0
+ i += 1 while i < str_len and @str[i].zero?
+ start_pos = i
+
+ # then scan the string, chunking it on attribute changes
+ while i < str_len
+ new_attr = @attrs[i]
+ if new_attr != current_attr
+ if i > start_pos
+ res << copy_string(start_pos, i)
+ start_pos = i
+ end
+
+ res << change_attribute(current_attr, new_attr)
+ current_attr = new_attr
+
+ if (current_attr & Attribute::SPECIAL) != 0
+ i += 1 while i < str_len and (@attrs[i] & Attribute::SPECIAL) != 0
+ res << Special.new(current_attr, copy_string(start_pos, i))
+ start_pos = i
+ next
+ end
+ end
+
+ # move on, skipping any invisible characters
+ begin
+ i += 1
+ end while i < str_len and @str[i].zero?
+ end
+
+ # tidy up trailing text
+ if start_pos < str_len
+ res << copy_string(start_pos, str_len)
+ end
+
+ # and reset to all attributes off
+ res << change_attribute(current_attr, 0) if current_attr != 0
+
+ return res
+ end
+
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rdoc/markup/simple_markup/lines.rb b/ruby_1_8_5/lib/rdoc/markup/simple_markup/lines.rb
new file mode 100644
index 0000000000..4e294f27dc
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/markup/simple_markup/lines.rb
@@ -0,0 +1,151 @@
+##########################################################################
+#
+# We store the lines we're working on as objects of class Line.
+# These contain the text of the line, along with a flag indicating the
+# line type, and an indentation level
+
+module SM
+
+ class Line
+ INFINITY = 9999
+
+ BLANK = :BLANK
+ HEADING = :HEADING
+ LIST = :LIST
+ RULE = :RULE
+ PARAGRAPH = :PARAGRAPH
+ VERBATIM = :VERBATIM
+
+ # line type
+ attr_accessor :type
+
+ # The indentation nesting level
+ attr_accessor :level
+
+ # The contents
+ attr_accessor :text
+
+ # A prefix or parameter. For LIST lines, this is
+ # the text that introduced the list item (the label)
+ attr_accessor :param
+
+ # A flag. For list lines, this is the type of the list
+ attr_accessor :flag
+
+ # the number of leading spaces
+ attr_accessor :leading_spaces
+
+ # true if this line has been deleted from the list of lines
+ attr_accessor :deleted
+
+
+ def initialize(text)
+ @text = text.dup
+ @deleted = false
+
+ # expand tabs
+ 1 while @text.gsub!(/\t+/) { ' ' * (8*$&.length - $`.length % 8)} && $~ #`
+
+ # Strip trailing whitespace
+ @text.sub!(/\s+$/, '')
+
+ # and look for leading whitespace
+ if @text.length > 0
+ @text =~ /^(\s*)/
+ @leading_spaces = $1.length
+ else
+ @leading_spaces = INFINITY
+ end
+ end
+
+ # Return true if this line is blank
+ def isBlank?
+ @text.length.zero?
+ end
+
+ # stamp a line with a type, a level, a prefix, and a flag
+ def stamp(type, level, param="", flag=nil)
+ @type, @level, @param, @flag = type, level, param, flag
+ end
+
+ ##
+ # Strip off the leading margin
+ #
+
+ def strip_leading(size)
+ if @text.size > size
+ @text[0,size] = ""
+ else
+ @text = ""
+ end
+ end
+
+ def to_s
+ "#@type#@level: #@text"
+ end
+ end
+
+ ###############################################################################
+ #
+ # A container for all the lines
+ #
+
+ class Lines
+ include Enumerable
+
+ attr_reader :lines # for debugging
+
+ def initialize(lines)
+ @lines = lines
+ rewind
+ end
+
+ def empty?
+ @lines.size.zero?
+ end
+
+ def each
+ @lines.each do |line|
+ yield line unless line.deleted
+ end
+ end
+
+# def [](index)
+# @lines[index]
+# end
+
+ def rewind
+ @nextline = 0
+ end
+
+ def next
+ begin
+ res = @lines[@nextline]
+ @nextline += 1 if @nextline < @lines.size
+ end while res and res.deleted and @nextline < @lines.size
+ res
+ end
+
+ def unget
+ @nextline -= 1
+ end
+
+ def delete(a_line)
+ a_line.deleted = true
+ end
+
+ def normalize
+ margin = @lines.collect{|l| l.leading_spaces}.min
+ margin = 0 if margin == Line::INFINITY
+ @lines.each {|line| line.strip_leading(margin) } if margin > 0
+ end
+
+ def as_text
+ @lines.map {|l| l.text}.join("\n")
+ end
+
+ def line_types
+ @lines.map {|l| l.type }
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rdoc/markup/simple_markup/preprocess.rb b/ruby_1_8_5/lib/rdoc/markup/simple_markup/preprocess.rb
new file mode 100644
index 0000000000..101c9bdeb1
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/markup/simple_markup/preprocess.rb
@@ -0,0 +1,73 @@
+module SM
+
+ ##
+ # Handle common directives that can occur in a block of text:
+ #
+ # : include : filename
+ #
+
+ class PreProcess
+
+ def initialize(input_file_name, include_path)
+ @input_file_name = input_file_name
+ @include_path = include_path
+ end
+
+ # Look for common options in a chunk of text. Options that
+ # we don't handle are passed back to our caller
+ # as |directive, param|
+
+ def handle(text)
+ text.gsub!(/^([ \t#]*):(\w+):\s*(.+)?\n/) do
+ prefix = $1
+ directive = $2.downcase
+ param = $3
+
+ case directive
+ when "include"
+ filename = param.split[0]
+ include_file(filename, prefix)
+
+ else
+ yield(directive, param)
+ end
+ end
+ end
+
+ #######
+ private
+ #######
+
+ # Include a file, indenting it correctly
+
+ def include_file(name, indent)
+ if (full_name = find_include_file(name))
+ content = File.open(full_name) {|f| f.read}
+ # strip leading '#'s, but only if all lines start with them
+ if content =~ /^[^#]/
+ content.gsub(/^/, indent)
+ else
+ content.gsub(/^#?/, indent)
+ end
+ else
+ $stderr.puts "Couldn't find file to include: '#{name}'"
+ ''
+ end
+ end
+
+ # Look for the given file in the directory containing the current
+ # file, and then in each of the directories specified in the
+ # RDOC_INCLUDE path
+
+ def find_include_file(name)
+ to_search = [ File.dirname(@input_file_name) ].concat @include_path
+ to_search.each do |dir|
+ full_name = File.join(dir, name)
+ stat = File.stat(full_name) rescue next
+ return full_name if stat.readable?
+ end
+ nil
+ end
+
+ end
+end
diff --git a/ruby_1_8_5/lib/rdoc/markup/simple_markup/to_flow.rb b/ruby_1_8_5/lib/rdoc/markup/simple_markup/to_flow.rb
new file mode 100644
index 0000000000..048e71abce
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/markup/simple_markup/to_flow.rb
@@ -0,0 +1,188 @@
+require 'rdoc/markup/simple_markup/fragments'
+require 'rdoc/markup/simple_markup/inline'
+require 'cgi'
+
+module SM
+
+ module Flow
+ P = Struct.new(:body)
+ VERB = Struct.new(:body)
+ RULE = Struct.new(:width)
+ class LIST
+ attr_reader :type, :contents
+ def initialize(type)
+ @type = type
+ @contents = []
+ end
+ def <<(stuff)
+ @contents << stuff
+ end
+ end
+ LI = Struct.new(:label, :body)
+ H = Struct.new(:level, :text)
+ end
+
+ class ToFlow
+ LIST_TYPE_TO_HTML = {
+ SM::ListBase::BULLET => [ "<ul>", "</ul>" ],
+ SM::ListBase::NUMBER => [ "<ol>", "</ol>" ],
+ SM::ListBase::UPPERALPHA => [ "<ol>", "</ol>" ],
+ SM::ListBase::LOWERALPHA => [ "<ol>", "</ol>" ],
+ SM::ListBase::LABELED => [ "<dl>", "</dl>" ],
+ SM::ListBase::NOTE => [ "<table>", "</table>" ],
+ }
+
+ InlineTag = Struct.new(:bit, :on, :off)
+
+ def initialize
+ init_tags
+ end
+
+ ##
+ # Set up the standard mapping of attributes to HTML tags
+ #
+ def init_tags
+ @attr_tags = [
+ InlineTag.new(SM::Attribute.bitmap_for(:BOLD), "<b>", "</b>"),
+ InlineTag.new(SM::Attribute.bitmap_for(:TT), "<tt>", "</tt>"),
+ InlineTag.new(SM::Attribute.bitmap_for(:EM), "<em>", "</em>"),
+ ]
+ end
+
+ ##
+ # Add a new set of HTML tags for an attribute. We allow
+ # separate start and end tags for flexibility
+ #
+ def add_tag(name, start, stop)
+ @attr_tags << InlineTag.new(SM::Attribute.bitmap_for(name), start, stop)
+ end
+
+ ##
+ # Given an HTML tag, decorate it with class information
+ # and the like if required. This is a no-op in the base
+ # class, but is overridden in HTML output classes that
+ # implement style sheets
+
+ def annotate(tag)
+ tag
+ end
+
+ ##
+ # Here's the client side of the visitor pattern
+
+ def start_accepting
+ @res = []
+ @list_stack = []
+ end
+
+ def end_accepting
+ @res
+ end
+
+ def accept_paragraph(am, fragment)
+ @res << Flow::P.new((convert_flow(am.flow(fragment.txt))))
+ end
+
+ def accept_verbatim(am, fragment)
+ @res << Flow::VERB.new((convert_flow(am.flow(fragment.txt))))
+ end
+
+ def accept_rule(am, fragment)
+ size = fragment.param
+ size = 10 if size > 10
+ @res << Flow::RULE.new(size)
+ end
+
+ def accept_list_start(am, fragment)
+ @list_stack.push(@res)
+ list = Flow::LIST.new(fragment.type)
+ @res << list
+ @res = list
+ end
+
+ def accept_list_end(am, fragment)
+ @res = @list_stack.pop
+ end
+
+ def accept_list_item(am, fragment)
+ @res << Flow::LI.new(fragment.param, convert_flow(am.flow(fragment.txt)))
+ end
+
+ def accept_blank_line(am, fragment)
+ # @res << annotate("<p />") << "\n"
+ end
+
+ def accept_heading(am, fragment)
+ @res << Flow::H.new(fragment.head_level, convert_flow(am.flow(fragment.txt)))
+ end
+
+
+ #######################################################################
+
+ private
+
+ #######################################################################
+
+ def on_tags(res, item)
+ attr_mask = item.turn_on
+ return if attr_mask.zero?
+
+ @attr_tags.each do |tag|
+ if attr_mask & tag.bit != 0
+ res << annotate(tag.on)
+ end
+ end
+ end
+
+ def off_tags(res, item)
+ attr_mask = item.turn_off
+ return if attr_mask.zero?
+
+ @attr_tags.reverse_each do |tag|
+ if attr_mask & tag.bit != 0
+ res << annotate(tag.off)
+ end
+ end
+ end
+
+ def convert_flow(flow)
+ res = ""
+ flow.each do |item|
+ case item
+ when String
+ res << convert_string(item)
+ when AttrChanger
+ off_tags(res, item)
+ on_tags(res, item)
+ when Special
+ res << convert_special(item)
+ else
+ raise "Unknown flow element: #{item.inspect}"
+ end
+ end
+ res
+ end
+
+ # some of these patterns are taken from SmartyPants...
+
+ def convert_string(item)
+ CGI.escapeHTML(item)
+ end
+
+ def convert_special(special)
+ handled = false
+ Attribute.each_name_of(special.type) do |name|
+ method_name = "handle_special_#{name}"
+ if self.respond_to? method_name
+ special.text = send(method_name, special)
+ handled = true
+ end
+ end
+ raise "Unhandled special: #{special}" unless handled
+ special.text
+ end
+
+
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rdoc/markup/simple_markup/to_html.rb b/ruby_1_8_5/lib/rdoc/markup/simple_markup/to_html.rb
new file mode 100644
index 0000000000..26b5f4ce70
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/markup/simple_markup/to_html.rb
@@ -0,0 +1,289 @@
+require 'rdoc/markup/simple_markup/fragments'
+require 'rdoc/markup/simple_markup/inline'
+
+require 'cgi'
+
+module SM
+
+ class ToHtml
+
+ LIST_TYPE_TO_HTML = {
+ ListBase::BULLET => [ "<ul>", "</ul>" ],
+ ListBase::NUMBER => [ "<ol>", "</ol>" ],
+ ListBase::UPPERALPHA => [ "<ol>", "</ol>" ],
+ ListBase::LOWERALPHA => [ "<ol>", "</ol>" ],
+ ListBase::LABELED => [ "<dl>", "</dl>" ],
+ ListBase::NOTE => [ "<table>", "</table>" ],
+ }
+
+ InlineTag = Struct.new(:bit, :on, :off)
+
+ def initialize
+ init_tags
+ end
+
+ ##
+ # Set up the standard mapping of attributes to HTML tags
+ #
+ def init_tags
+ @attr_tags = [
+ InlineTag.new(SM::Attribute.bitmap_for(:BOLD), "<b>", "</b>"),
+ InlineTag.new(SM::Attribute.bitmap_for(:TT), "<tt>", "</tt>"),
+ InlineTag.new(SM::Attribute.bitmap_for(:EM), "<em>", "</em>"),
+ ]
+ end
+
+ ##
+ # Add a new set of HTML tags for an attribute. We allow
+ # separate start and end tags for flexibility
+ #
+ def add_tag(name, start, stop)
+ @attr_tags << InlineTag.new(SM::Attribute.bitmap_for(name), start, stop)
+ end
+
+ ##
+ # Given an HTML tag, decorate it with class information
+ # and the like if required. This is a no-op in the base
+ # class, but is overridden in HTML output classes that
+ # implement style sheets
+
+ def annotate(tag)
+ tag
+ end
+
+ ##
+ # Here's the client side of the visitor pattern
+
+ def start_accepting
+ @res = ""
+ @in_list_entry = []
+ end
+
+ def end_accepting
+ @res
+ end
+
+ def accept_paragraph(am, fragment)
+ @res << annotate("<p>") + "\n"
+ @res << wrap(convert_flow(am.flow(fragment.txt)))
+ @res << annotate("</p>") + "\n"
+ end
+
+ def accept_verbatim(am, fragment)
+ @res << annotate("<pre>") + "\n"
+ @res << CGI.escapeHTML(fragment.txt)
+ @res << annotate("</pre>") << "\n"
+ end
+
+ def accept_rule(am, fragment)
+ size = fragment.param
+ size = 10 if size > 10
+ @res << "<hr size=\"#{size}\"></hr>"
+ end
+
+ def accept_list_start(am, fragment)
+ @res << html_list_name(fragment.type, true) <<"\n"
+ @in_list_entry.push false
+ end
+
+ def accept_list_end(am, fragment)
+ if tag = @in_list_entry.pop
+ @res << annotate(tag) << "\n"
+ end
+ @res << html_list_name(fragment.type, false) <<"\n"
+ end
+
+ def accept_list_item(am, fragment)
+ if tag = @in_list_entry.last
+ @res << annotate(tag) << "\n"
+ end
+ @res << list_item_start(am, fragment)
+ @res << wrap(convert_flow(am.flow(fragment.txt))) << "\n"
+ @in_list_entry[-1] = list_end_for(fragment.type)
+ end
+
+ def accept_blank_line(am, fragment)
+ # @res << annotate("<p />") << "\n"
+ end
+
+ def accept_heading(am, fragment)
+ @res << convert_heading(fragment.head_level, am.flow(fragment.txt))
+ end
+
+ # This is a higher speed (if messier) version of wrap
+
+ def wrap(txt, line_len = 76)
+ res = ""
+ sp = 0
+ ep = txt.length
+ while sp < ep
+ # scan back for a space
+ p = sp + line_len - 1
+ if p >= ep
+ p = ep
+ else
+ while p > sp and txt[p] != ?\s
+ p -= 1
+ end
+ if p <= sp
+ p = sp + line_len
+ while p < ep and txt[p] != ?\s
+ p += 1
+ end
+ end
+ end
+ res << txt[sp...p] << "\n"
+ sp = p
+ sp += 1 while sp < ep and txt[sp] == ?\s
+ end
+ res
+ end
+
+ #######################################################################
+
+ private
+
+ #######################################################################
+
+ def on_tags(res, item)
+ attr_mask = item.turn_on
+ return if attr_mask.zero?
+
+ @attr_tags.each do |tag|
+ if attr_mask & tag.bit != 0
+ res << annotate(tag.on)
+ end
+ end
+ end
+
+ def off_tags(res, item)
+ attr_mask = item.turn_off
+ return if attr_mask.zero?
+
+ @attr_tags.reverse_each do |tag|
+ if attr_mask & tag.bit != 0
+ res << annotate(tag.off)
+ end
+ end
+ end
+
+ def convert_flow(flow)
+ res = ""
+ flow.each do |item|
+ case item
+ when String
+ res << convert_string(item)
+ when AttrChanger
+ off_tags(res, item)
+ on_tags(res, item)
+ when Special
+ res << convert_special(item)
+ else
+ raise "Unknown flow element: #{item.inspect}"
+ end
+ end
+ res
+ end
+
+ # some of these patterns are taken from SmartyPants...
+
+ def convert_string(item)
+ CGI.escapeHTML(item).
+
+
+ # convert -- to em-dash, (-- to en-dash)
+ gsub(/---?/, '&#8212;'). #gsub(/--/, '&#8211;').
+
+ # convert ... to elipsis (and make sure .... becomes .<elipsis>)
+ gsub(/\.\.\.\./, '.&#8230;').gsub(/\.\.\./, '&#8230;').
+
+ # convert single closing quote
+ gsub(%r{([^ \t\r\n\[\{\(])\'}) { "#$1&#8217;" }.
+ gsub(%r{\'(?=\W|s\b)}) { "&#8217;" }.
+
+ # convert single opening quote
+ gsub(/'/, '&#8216;').
+
+ # convert double closing quote
+ gsub(%r{([^ \t\r\n\[\{\(])\'(?=\W)}) { "#$1&#8221;" }.
+
+ # convert double opening quote
+ gsub(/'/, '&#8220;').
+
+ # convert copyright
+ gsub(/\(c\)/, '&#169;').
+
+ # convert and registered trademark
+ gsub(/\(r\)/, '&#174;')
+
+ end
+
+ def convert_special(special)
+ handled = false
+ Attribute.each_name_of(special.type) do |name|
+ method_name = "handle_special_#{name}"
+ if self.respond_to? method_name
+ special.text = send(method_name, special)
+ handled = true
+ end
+ end
+ raise "Unhandled special: #{special}" unless handled
+ special.text
+ end
+
+ def convert_heading(level, flow)
+ res =
+ annotate("<h#{level}>") +
+ convert_flow(flow) +
+ annotate("</h#{level}>\n")
+ end
+
+ def html_list_name(list_type, is_open_tag)
+ tags = LIST_TYPE_TO_HTML[list_type] || raise("Invalid list type: #{list_type.inspect}")
+ annotate(tags[ is_open_tag ? 0 : 1])
+ end
+
+ def list_item_start(am, fragment)
+ case fragment.type
+ when ListBase::BULLET, ListBase::NUMBER
+ annotate("<li>")
+
+ when ListBase::UPPERALPHA
+ annotate("<li type=\"A\">")
+
+ when ListBase::LOWERALPHA
+ annotate("<li type=\"a\">")
+
+ when ListBase::LABELED
+ annotate("<dt>") +
+ convert_flow(am.flow(fragment.param)) +
+ annotate("</dt>") +
+ annotate("<dd>")
+
+ when ListBase::NOTE
+ annotate("<tr>") +
+ annotate("<td valign=\"top\">") +
+ convert_flow(am.flow(fragment.param)) +
+ annotate("</td>") +
+ annotate("<td>")
+ else
+ raise "Invalid list type"
+ end
+ end
+
+ def list_end_for(fragment_type)
+ case fragment_type
+ when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA, ListBase::LOWERALPHA
+ "</li>"
+ when ListBase::LABELED
+ "</dd>"
+ when ListBase::NOTE
+ "</td></tr>"
+ else
+ raise "Invalid list type"
+ end
+ end
+
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rdoc/markup/simple_markup/to_latex.rb b/ruby_1_8_5/lib/rdoc/markup/simple_markup/to_latex.rb
new file mode 100644
index 0000000000..6c16278652
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/markup/simple_markup/to_latex.rb
@@ -0,0 +1,333 @@
+require 'rdoc/markup/simple_markup/fragments'
+require 'rdoc/markup/simple_markup/inline'
+
+require 'cgi'
+
+module SM
+
+ # Convert SimpleMarkup to basic LaTeX report format
+
+ class ToLaTeX
+
+ BS = "\020" # \
+ OB = "\021" # {
+ CB = "\022" # }
+ DL = "\023" # Dollar
+
+ BACKSLASH = "#{BS}symbol#{OB}92#{CB}"
+ HAT = "#{BS}symbol#{OB}94#{CB}"
+ BACKQUOTE = "#{BS}symbol#{OB}0#{CB}"
+ TILDE = "#{DL}#{BS}sim#{DL}"
+ LESSTHAN = "#{DL}<#{DL}"
+ GREATERTHAN = "#{DL}>#{DL}"
+
+ def self.l(str)
+ str.tr('\\', BS).tr('{', OB).tr('}', CB).tr('$', DL)
+ end
+
+ def l(arg)
+ SM::ToLaTeX.l(arg)
+ end
+
+ LIST_TYPE_TO_LATEX = {
+ ListBase::BULLET => [ l("\\begin{itemize}"), l("\\end{itemize}") ],
+ ListBase::NUMBER => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\arabic" ],
+ ListBase::UPPERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\Alph" ],
+ ListBase::LOWERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\alph" ],
+ ListBase::LABELED => [ l("\\begin{description}"), l("\\end{description}") ],
+ ListBase::NOTE => [
+ l("\\begin{tabularx}{\\linewidth}{@{} l X @{}}"),
+ l("\\end{tabularx}") ],
+ }
+
+ InlineTag = Struct.new(:bit, :on, :off)
+
+ def initialize
+ init_tags
+ @list_depth = 0
+ @prev_list_types = []
+ end
+
+ ##
+ # Set up the standard mapping of attributes to LaTeX
+ #
+ def init_tags
+ @attr_tags = [
+ InlineTag.new(SM::Attribute.bitmap_for(:BOLD), l("\\textbf{"), l("}")),
+ InlineTag.new(SM::Attribute.bitmap_for(:TT), l("\\texttt{"), l("}")),
+ InlineTag.new(SM::Attribute.bitmap_for(:EM), l("\\emph{"), l("}")),
+ ]
+ end
+
+ ##
+ # Escape a LaTeX string
+ def escape(str)
+# $stderr.print "FE: ", str
+ s = str.
+# sub(/\s+$/, '').
+ gsub(/([_\${}&%#])/, "#{BS}\\1").
+ gsub(/\\/, BACKSLASH).
+ gsub(/\^/, HAT).
+ gsub(/~/, TILDE).
+ gsub(/</, LESSTHAN).
+ gsub(/>/, GREATERTHAN).
+ gsub(/,,/, ",{},").
+ gsub(/\`/, BACKQUOTE)
+# $stderr.print "-> ", s, "\n"
+ s
+ end
+
+ ##
+ # Add a new set of LaTeX tags for an attribute. We allow
+ # separate start and end tags for flexibility
+ #
+ def add_tag(name, start, stop)
+ @attr_tags << InlineTag.new(SM::Attribute.bitmap_for(name), start, stop)
+ end
+
+
+ ##
+ # Here's the client side of the visitor pattern
+
+ def start_accepting
+ @res = ""
+ @in_list_entry = []
+ end
+
+ def end_accepting
+ @res.tr(BS, '\\').tr(OB, '{').tr(CB, '}').tr(DL, '$')
+ end
+
+ def accept_paragraph(am, fragment)
+ @res << wrap(convert_flow(am.flow(fragment.txt)))
+ @res << "\n"
+ end
+
+ def accept_verbatim(am, fragment)
+ @res << "\n\\begin{code}\n"
+ @res << fragment.txt.sub(/[\n\s]+\Z/, '')
+ @res << "\n\\end{code}\n\n"
+ end
+
+ def accept_rule(am, fragment)
+ size = fragment.param
+ size = 10 if size > 10
+ @res << "\n\n\\rule{\\linewidth}{#{size}pt}\n\n"
+ end
+
+ def accept_list_start(am, fragment)
+ @res << list_name(fragment.type, true) <<"\n"
+ @in_list_entry.push false
+ end
+
+ def accept_list_end(am, fragment)
+ if tag = @in_list_entry.pop
+ @res << tag << "\n"
+ end
+ @res << list_name(fragment.type, false) <<"\n"
+ end
+
+ def accept_list_item(am, fragment)
+ if tag = @in_list_entry.last
+ @res << tag << "\n"
+ end
+ @res << list_item_start(am, fragment)
+ @res << wrap(convert_flow(am.flow(fragment.txt))) << "\n"
+ @in_list_entry[-1] = list_end_for(fragment.type)
+ end
+
+ def accept_blank_line(am, fragment)
+ # @res << "\n"
+ end
+
+ def accept_heading(am, fragment)
+ @res << convert_heading(fragment.head_level, am.flow(fragment.txt))
+ end
+
+ # This is a higher speed (if messier) version of wrap
+
+ def wrap(txt, line_len = 76)
+ res = ""
+ sp = 0
+ ep = txt.length
+ while sp < ep
+ # scan back for a space
+ p = sp + line_len - 1
+ if p >= ep
+ p = ep
+ else
+ while p > sp and txt[p] != ?\s
+ p -= 1
+ end
+ if p <= sp
+ p = sp + line_len
+ while p < ep and txt[p] != ?\s
+ p += 1
+ end
+ end
+ end
+ res << txt[sp...p] << "\n"
+ sp = p
+ sp += 1 while sp < ep and txt[sp] == ?\s
+ end
+ res
+ end
+
+ #######################################################################
+
+ private
+
+ #######################################################################
+
+ def on_tags(res, item)
+ attr_mask = item.turn_on
+ return if attr_mask.zero?
+
+ @attr_tags.each do |tag|
+ if attr_mask & tag.bit != 0
+ res << tag.on
+ end
+ end
+ end
+
+ def off_tags(res, item)
+ attr_mask = item.turn_off
+ return if attr_mask.zero?
+
+ @attr_tags.reverse_each do |tag|
+ if attr_mask & tag.bit != 0
+ res << tag.off
+ end
+ end
+ end
+
+ def convert_flow(flow)
+ res = ""
+ flow.each do |item|
+ case item
+ when String
+# $stderr.puts "Converting '#{item}'"
+ res << convert_string(item)
+ when AttrChanger
+ off_tags(res, item)
+ on_tags(res, item)
+ when Special
+ res << convert_special(item)
+ else
+ raise "Unknown flow element: #{item.inspect}"
+ end
+ end
+ res
+ end
+
+ # some of these patterns are taken from SmartyPants...
+
+ def convert_string(item)
+
+ escape(item).
+
+
+ # convert ... to elipsis (and make sure .... becomes .<elipsis>)
+ gsub(/\.\.\.\./, '.\ldots{}').gsub(/\.\.\./, '\ldots{}').
+
+ # convert single closing quote
+ gsub(%r{([^ \t\r\n\[\{\(])\'}) { "#$1'" }.
+ gsub(%r{\'(?=\W|s\b)}) { "'" }.
+
+ # convert single opening quote
+ gsub(/'/, '`').
+
+ # convert double closing quote
+ gsub(%r{([^ \t\r\n\[\{\(])\"(?=\W)}) { "#$1''" }.
+
+ # convert double opening quote
+ gsub(/"/, "``").
+
+ # convert copyright
+ gsub(/\(c\)/, '\copyright{}')
+
+ end
+
+ def convert_special(special)
+ handled = false
+ Attribute.each_name_of(special.type) do |name|
+ method_name = "handle_special_#{name}"
+ if self.respond_to? method_name
+ special.text = send(method_name, special)
+ handled = true
+ end
+ end
+ raise "Unhandled special: #{special}" unless handled
+ special.text
+ end
+
+ def convert_heading(level, flow)
+ res =
+ case level
+ when 1 then "\\chapter{"
+ when 2 then "\\section{"
+ when 3 then "\\subsection{"
+ when 4 then "\\subsubsection{"
+ else "\\paragraph{"
+ end +
+ convert_flow(flow) +
+ "}\n"
+ end
+
+ def list_name(list_type, is_open_tag)
+ tags = LIST_TYPE_TO_LATEX[list_type] || raise("Invalid list type: #{list_type.inspect}")
+ if tags[2] # enumerate
+ if is_open_tag
+ @list_depth += 1
+ if @prev_list_types[@list_depth] != tags[2]
+ case @list_depth
+ when 1
+ roman = "i"
+ when 2
+ roman = "ii"
+ when 3
+ roman = "iii"
+ when 4
+ roman = "iv"
+ else
+ raise("Too deep list: level #{@list_depth}")
+ end
+ @prev_list_types[@list_depth] = tags[2]
+ return l("\\renewcommand{\\labelenum#{roman}}{#{tags[2]}{enum#{roman}}}") + "\n" + tags[0]
+ end
+ else
+ @list_depth -= 1
+ end
+ end
+ tags[ is_open_tag ? 0 : 1]
+ end
+
+ def list_item_start(am, fragment)
+ case fragment.type
+ when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA, ListBase::LOWERALPHA
+ "\\item "
+
+ when ListBase::LABELED
+ "\\item[" + convert_flow(am.flow(fragment.param)) + "] "
+
+ when ListBase::NOTE
+ convert_flow(am.flow(fragment.param)) + " & "
+ else
+ raise "Invalid list type"
+ end
+ end
+
+ def list_end_for(fragment_type)
+ case fragment_type
+ when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA, ListBase::LOWERALPHA, ListBase::LABELED
+ ""
+ when ListBase::NOTE
+ "\\\\\n"
+ else
+ raise "Invalid list type"
+ end
+ end
+
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rdoc/markup/test/AllTests.rb b/ruby_1_8_5/lib/rdoc/markup/test/AllTests.rb
new file mode 100644
index 0000000000..b9c8c9dfcc
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/markup/test/AllTests.rb
@@ -0,0 +1,2 @@
+require 'TestParse.rb'
+require 'TestInline.rb'
diff --git a/ruby_1_8_5/lib/rdoc/markup/test/TestInline.rb b/ruby_1_8_5/lib/rdoc/markup/test/TestInline.rb
new file mode 100644
index 0000000000..a067d4c24c
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/markup/test/TestInline.rb
@@ -0,0 +1,154 @@
+require "test/unit"
+
+$:.unshift "../../.."
+
+require "rdoc/markup/simple_markup/inline"
+
+class TestInline < Test::Unit::TestCase
+
+
+ def setup
+ @am = SM::AttributeManager.new
+
+ @bold_on = @am.changed_attribute_by_name([], [:BOLD])
+ @bold_off = @am.changed_attribute_by_name([:BOLD], [])
+
+ @tt_on = @am.changed_attribute_by_name([], [:TT])
+ @tt_off = @am.changed_attribute_by_name([:TT], [])
+
+ @em_on = @am.changed_attribute_by_name([], [:EM])
+ @em_off = @am.changed_attribute_by_name([:EM], [])
+
+ @bold_em_on = @am.changed_attribute_by_name([], [:BOLD] | [:EM])
+ @bold_em_off = @am.changed_attribute_by_name([:BOLD] | [:EM], [])
+
+ @em_then_bold = @am.changed_attribute_by_name([:EM], [:EM] | [:BOLD])
+
+ @em_to_bold = @am.changed_attribute_by_name([:EM], [:BOLD])
+
+ @am.add_word_pair("{", "}", :WOMBAT)
+ @wombat_on = @am.changed_attribute_by_name([], [:WOMBAT])
+ @wombat_off = @am.changed_attribute_by_name([:WOMBAT], [])
+ end
+
+ def crossref(text)
+ [ @am.changed_attribute_by_name([], [:CROSSREF] | [:_SPECIAL_]),
+ SM::Special.new(33, text),
+ @am.changed_attribute_by_name([:CROSSREF] | [:_SPECIAL_], [])
+ ]
+ end
+
+ def test_special
+ # class names, variable names, file names, or instance variables
+ @am.add_special(/(
+ \b([A-Z]\w+(::\w+)*)
+ | \#\w+[!?=]?
+ | \b\w+([_\/\.]+\w+)+[!?=]?
+ )/x,
+ :CROSSREF)
+
+ assert_equal(["cat"], @am.flow("cat"))
+
+ assert_equal(["cat ", crossref("#fred"), " dog"].flatten,
+ @am.flow("cat #fred dog"))
+
+ assert_equal([crossref("#fred"), " dog"].flatten,
+ @am.flow("#fred dog"))
+
+ assert_equal(["cat ", crossref("#fred")].flatten, @am.flow("cat #fred"))
+ end
+
+ def test_basic
+ assert_equal(["cat"], @am.flow("cat"))
+
+ assert_equal(["cat ", @bold_on, "and", @bold_off, " dog"],
+ @am.flow("cat *and* dog"))
+
+ assert_equal(["cat ", @bold_on, "AND", @bold_off, " dog"],
+ @am.flow("cat *AND* dog"))
+
+ assert_equal(["cat ", @em_on, "And", @em_off, " dog"],
+ @am.flow("cat _And_ dog"))
+
+ assert_equal(["cat *and dog*"], @am.flow("cat *and dog*"))
+
+ assert_equal(["*cat and* dog"], @am.flow("*cat and* dog"))
+
+ assert_equal(["cat *and ", @bold_on, "dog", @bold_off],
+ @am.flow("cat *and *dog*"))
+
+ assert_equal(["cat ", @em_on, "and", @em_off, " dog"],
+ @am.flow("cat _and_ dog"))
+
+ assert_equal(["cat_and_dog"],
+ @am.flow("cat_and_dog"))
+
+ assert_equal(["cat ", @tt_on, "and", @tt_off, " dog"],
+ @am.flow("cat +and+ dog"))
+
+ assert_equal(["cat ", @bold_on, "a_b_c", @bold_off, " dog"],
+ @am.flow("cat *a_b_c* dog"))
+
+ assert_equal(["cat __ dog"],
+ @am.flow("cat __ dog"))
+
+ assert_equal(["cat ", @em_on, "_", @em_off, " dog"],
+ @am.flow("cat ___ dog"))
+
+ end
+
+ def test_combined
+ assert_equal(["cat ", @em_on, "and", @em_off, " ", @bold_on, "dog", @bold_off],
+ @am.flow("cat _and_ *dog*"))
+
+ assert_equal(["cat ", @em_on, "a__nd", @em_off, " ", @bold_on, "dog", @bold_off],
+ @am.flow("cat _a__nd_ *dog*"))
+ end
+
+ def test_html_like
+ assert_equal(["cat ", @tt_on, "dog", @tt_off], @am.flow("cat <tt>dog</Tt>"))
+
+ assert_equal(["cat ", @em_on, "and", @em_off, " ", @bold_on, "dog", @bold_off],
+ @am.flow("cat <i>and</i> <B>dog</b>"))
+
+ assert_equal(["cat ", @em_on, "and ", @em_then_bold, "dog", @bold_em_off],
+ @am.flow("cat <i>and <B>dog</B></I>"))
+
+ assert_equal(["cat ", @em_on, "and ", @em_to_bold, "dog", @bold_off],
+ @am.flow("cat <i>and </i><b>dog</b>"))
+
+ assert_equal(["cat ", @em_on, "and ", @em_to_bold, "dog", @bold_off],
+ @am.flow("cat <i>and <b></i>dog</b>"))
+
+ assert_equal([@tt_on, "cat", @tt_off, " ", @em_on, "and ", @em_to_bold, "dog", @bold_off],
+ @am.flow("<tt>cat</tt> <i>and <b></i>dog</b>"))
+
+ assert_equal(["cat ", @em_on, "and ", @em_then_bold, "dog", @bold_em_off],
+ @am.flow("cat <i>and <b>dog</b></i>"))
+
+ assert_equal(["cat ", @bold_em_on, "and", @bold_em_off, " dog"],
+ @am.flow("cat <i><b>and</b></i> dog"))
+
+
+ end
+
+ def test_protect
+ assert_equal(['cat \\ dog'], @am.flow('cat \\ dog'))
+
+ assert_equal(["cat <tt>dog</Tt>"], @am.flow("cat \\<tt>dog</Tt>"))
+
+ assert_equal(["cat ", @em_on, "and", @em_off, " <B>dog</b>"],
+ @am.flow("cat <i>and</i> \\<B>dog</b>"))
+
+ assert_equal(["*word* or <b>text</b>"], @am.flow("\\*word* or \\<b>text</b>"))
+
+ assert_equal(["_cat_", @em_on, "dog", @em_off],
+ @am.flow("\\_cat_<i>dog</i>"))
+ end
+
+ def test_adding
+ assert_equal(["cat ", @wombat_on, "and", @wombat_off, " dog" ],
+ @am.flow("cat {and} dog"))
+# assert_equal(["cat {and} dog" ], @am.flow("cat \\{and} dog"))
+ end
+end
diff --git a/ruby_1_8_5/lib/rdoc/markup/test/TestParse.rb b/ruby_1_8_5/lib/rdoc/markup/test/TestParse.rb
new file mode 100644
index 0000000000..3ec541ce7a
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/markup/test/TestParse.rb
@@ -0,0 +1,503 @@
+require 'test/unit'
+
+$:.unshift "../../.."
+
+require 'rdoc/markup/simple_markup'
+
+include SM
+
+class TestParse < Test::Unit::TestCase
+
+ class MockOutput
+ def start_accepting
+ @res = []
+ end
+
+ def end_accepting
+ @res
+ end
+
+ def accept_paragraph(am, fragment)
+ @res << fragment.to_s
+ end
+
+ def accept_verbatim(am, fragment)
+ @res << fragment.to_s
+ end
+
+ def accept_list_start(am, fragment)
+ @res << fragment.to_s
+ end
+
+ def accept_list_end(am, fragment)
+ @res << fragment.to_s
+ end
+
+ def accept_list_item(am, fragment)
+ @res << fragment.to_s
+ end
+
+ def accept_blank_line(am, fragment)
+ @res << fragment.to_s
+ end
+
+ def accept_heading(am, fragment)
+ @res << fragment.to_s
+ end
+
+ def accept_rule(am, fragment)
+ @res << fragment.to_s
+ end
+
+ end
+
+ def basic_conv(str)
+ sm = SimpleMarkup.new
+ mock = MockOutput.new
+ sm.convert(str, mock)
+ sm.content
+ end
+
+ def line_types(str, expected)
+ p = SimpleMarkup.new
+ mock = MockOutput.new
+ p.convert(str, mock)
+ assert_equal(expected, p.get_line_types.map{|type| type.to_s[0,1]}.join(''))
+ end
+
+ def line_groups(str, expected)
+ p = SimpleMarkup.new
+ mock = MockOutput.new
+
+ block = p.convert(str, mock)
+
+ if block != expected
+ rows = (0...([expected.size, block.size].max)).collect{|i|
+ [expected[i]||"nil", block[i]||"nil"]
+ }
+ printf "\n\n%35s %35s\n", "Expected", "Got"
+ rows.each {|e,g| printf "%35s %35s\n", e.dump, g.dump }
+ end
+
+ assert_equal(expected, block)
+ end
+
+ def test_tabs
+ str = "hello\n dave"
+ assert_equal(str, basic_conv(str))
+ str = "hello\n\tdave"
+ assert_equal("hello\n dave", basic_conv(str))
+ str = "hello\n \tdave"
+ assert_equal("hello\n dave", basic_conv(str))
+ str = "hello\n \tdave"
+ assert_equal("hello\n dave", basic_conv(str))
+ str = "hello\n \tdave"
+ assert_equal("hello\n dave", basic_conv(str))
+ str = "hello\n \tdave"
+ assert_equal("hello\n dave", basic_conv(str))
+ str = "hello\n \tdave"
+ assert_equal("hello\n dave", basic_conv(str))
+ str = "hello\n \tdave"
+ assert_equal("hello\n dave", basic_conv(str))
+ str = "hello\n \tdave"
+ assert_equal("hello\n dave", basic_conv(str))
+ str = "hello\n \tdave"
+ assert_equal("hello\n dave", basic_conv(str))
+ str = ".\t\t."
+ assert_equal(". .", basic_conv(str))
+ end
+
+ def test_whitespace
+ assert_equal("hello", basic_conv("hello"))
+ assert_equal("hello", basic_conv(" hello "))
+ assert_equal("hello", basic_conv(" \t \t hello\t\t"))
+
+ assert_equal("1\n 2\n 3", basic_conv("1\n 2\n 3"))
+ assert_equal("1\n 2\n 3", basic_conv(" 1\n 2\n 3"))
+
+ assert_equal("1\n 2\n 3\n1\n 2", basic_conv("1\n 2\n 3\n1\n 2"))
+ assert_equal("1\n 2\n 3\n1\n 2", basic_conv(" 1\n 2\n 3\n 1\n 2"))
+
+ assert_equal("1\n 2\n\n 3", basic_conv(" 1\n 2\n\n 3"))
+ end
+
+ def test_types
+ str = "now is the time"
+ line_types(str, 'P')
+
+ str = "now is the time\nfor all good men"
+ line_types(str, 'PP')
+
+ str = "now is the time\n code\nfor all good men"
+ line_types(str, 'PVP')
+
+ str = "now is the time\n code\n more code\nfor all good men"
+ line_types(str, 'PVVP')
+
+ str = "now is\n---\nthe time"
+ line_types(str, 'PRP')
+
+ str = %{\
+ now is
+ * l1
+ * l2
+ the time}
+ line_types(str, 'PLLP')
+
+ str = %{\
+ now is
+ * l1
+ l1+
+ * l2
+ the time}
+ line_types(str, 'PLPLP')
+
+ str = %{\
+ now is
+ * l1
+ * l1.1
+ * l2
+ the time}
+ line_types(str, 'PLLLP')
+
+ str = %{\
+ now is
+ * l1
+ * l1.1
+ text
+ code
+ code
+
+ text
+ * l2
+ the time}
+ line_types(str, 'PLLPVVBPLP')
+
+ str = %{\
+ now is
+ 1. l1
+ * l1.1
+ 2. l2
+ the time}
+ line_types(str, 'PLLLP')
+
+ str = %{\
+ now is
+ [cat] l1
+ * l1.1
+ [dog] l2
+ the time}
+ line_types(str, 'PLLLP')
+
+ str = %{\
+ now is
+ [cat] l1
+ continuation
+ [dog] l2
+ the time}
+ line_types(str, 'PLPLP')
+ end
+
+ def test_groups
+ str = "now is the time"
+ line_groups(str, ["L0: Paragraph\nnow is the time"] )
+
+ str = "now is the time\nfor all good men"
+ line_groups(str, ["L0: Paragraph\nnow is the time for all good men"] )
+
+ str = %{\
+ now is the time
+ code _line_ here
+ for all good men}
+
+ line_groups(str,
+ [ "L0: Paragraph\nnow is the time",
+ "L0: Verbatim\n code _line_ here\n",
+ "L0: Paragraph\nfor all good men"
+ ] )
+
+ str = "now is the time\n code\n more code\nfor all good men"
+ line_groups(str,
+ [ "L0: Paragraph\nnow is the time",
+ "L0: Verbatim\n code\n more code\n",
+ "L0: Paragraph\nfor all good men"
+ ] )
+
+ str = %{\
+ now is
+ * l1
+ * l2
+ the time}
+ line_groups(str,
+ [ "L0: Paragraph\nnow is",
+ "L1: ListStart\n",
+ "L1: ListItem\nl1",
+ "L1: ListItem\nl2",
+ "L1: ListEnd\n",
+ "L0: Paragraph\nthe time"
+ ])
+
+ str = %{\
+ now is
+ * l1
+ l1+
+ * l2
+ the time}
+ line_groups(str,
+ [ "L0: Paragraph\nnow is",
+ "L1: ListStart\n",
+ "L1: ListItem\nl1 l1+",
+ "L1: ListItem\nl2",
+ "L1: ListEnd\n",
+ "L0: Paragraph\nthe time"
+ ])
+
+ str = %{\
+ now is
+ * l1
+ * l1.1
+ * l2
+ the time}
+ line_groups(str,
+ [ "L0: Paragraph\nnow is",
+ "L1: ListStart\n",
+ "L1: ListItem\nl1",
+ "L2: ListStart\n",
+ "L2: ListItem\nl1.1",
+ "L2: ListEnd\n",
+ "L1: ListItem\nl2",
+ "L1: ListEnd\n",
+ "L0: Paragraph\nthe time"
+ ])
+
+
+ str = %{\
+ now is
+ * l1
+ * l1.1
+ text
+ code
+ code
+
+ text
+ * l2
+ the time}
+ line_groups(str,
+ [ "L0: Paragraph\nnow is",
+ "L1: ListStart\n",
+ "L1: ListItem\nl1",
+ "L2: ListStart\n",
+ "L2: ListItem\nl1.1 text",
+ "L2: Verbatim\n code\n code\n",
+ "L2: Paragraph\ntext",
+ "L2: ListEnd\n",
+ "L1: ListItem\nl2",
+ "L1: ListEnd\n",
+ "L0: Paragraph\nthe time"
+ ])
+
+
+ str = %{\
+ now is
+ 1. l1
+ * l1.1
+ 2. l2
+ the time}
+ line_groups(str,
+ [ "L0: Paragraph\nnow is",
+ "L1: ListStart\n",
+ "L1: ListItem\nl1",
+ "L2: ListStart\n",
+ "L2: ListItem\nl1.1",
+ "L2: ListEnd\n",
+ "L1: ListItem\nl2",
+ "L1: ListEnd\n",
+ "L0: Paragraph\nthe time"
+ ])
+
+ str = %{\
+ now is
+ [cat] l1
+ * l1.1
+ [dog] l2
+ the time}
+ line_groups(str,
+ [ "L0: Paragraph\nnow is",
+ "L1: ListStart\n",
+ "L1: ListItem\nl1",
+ "L2: ListStart\n",
+ "L2: ListItem\nl1.1",
+ "L2: ListEnd\n",
+ "L1: ListItem\nl2",
+ "L1: ListEnd\n",
+ "L0: Paragraph\nthe time"
+ ])
+
+ str = %{\
+ now is
+ [cat] l1
+ continuation
+ [dog] l2
+ the time}
+ line_groups(str,
+ [ "L0: Paragraph\nnow is",
+ "L1: ListStart\n",
+ "L1: ListItem\nl1 continuation",
+ "L1: ListItem\nl2",
+ "L1: ListEnd\n",
+ "L0: Paragraph\nthe time"
+ ])
+
+
+ end
+
+ def test_verbatim_merge
+ str = %{\
+ now is
+ code
+ the time}
+
+ line_groups(str,
+ [ "L0: Paragraph\nnow is",
+ "L0: Verbatim\n code\n",
+ "L0: Paragraph\nthe time"
+ ])
+
+
+ str = %{\
+ now is
+ code
+ code1
+ the time}
+
+ line_groups(str,
+ [ "L0: Paragraph\nnow is",
+ "L0: Verbatim\n code\n code1\n",
+ "L0: Paragraph\nthe time"
+ ])
+
+
+ str = %{\
+ now is
+ code
+
+ code1
+ the time}
+
+ line_groups(str,
+ [ "L0: Paragraph\nnow is",
+ "L0: Verbatim\n code\n\n code1\n",
+ "L0: Paragraph\nthe time"
+ ])
+
+
+ str = %{\
+ now is
+ code
+
+ code1
+
+ the time}
+
+ line_groups(str,
+ [ "L0: Paragraph\nnow is",
+ "L0: Verbatim\n code\n\n code1\n",
+ "L0: Paragraph\nthe time"
+ ])
+
+
+ str = %{\
+ now is
+ code
+
+ code1
+
+ code2
+ the time}
+
+ line_groups(str,
+ [ "L0: Paragraph\nnow is",
+ "L0: Verbatim\n code\n\n code1\n\n code2\n",
+ "L0: Paragraph\nthe time"
+ ])
+
+
+ # Folds multiple blank lines
+ str = %{\
+ now is
+ code
+
+
+ code1
+
+ the time}
+
+ line_groups(str,
+ [ "L0: Paragraph\nnow is",
+ "L0: Verbatim\n code\n\n code1\n",
+ "L0: Paragraph\nthe time"
+ ])
+
+
+ end
+
+ def test_list_split
+ str = %{\
+ now is
+ * l1
+ 1. n1
+ 2. n2
+ * l2
+ the time}
+ line_groups(str,
+ [ "L0: Paragraph\nnow is",
+ "L1: ListStart\n",
+ "L1: ListItem\nl1",
+ "L1: ListEnd\n",
+ "L1: ListStart\n",
+ "L1: ListItem\nn1",
+ "L1: ListItem\nn2",
+ "L1: ListEnd\n",
+ "L1: ListStart\n",
+ "L1: ListItem\nl2",
+ "L1: ListEnd\n",
+ "L0: Paragraph\nthe time"
+ ])
+
+ end
+
+
+ def test_headings
+ str = "= heading one"
+ line_groups(str,
+ [ "L0: Heading\nheading one"
+ ])
+
+ str = "=== heading three"
+ line_groups(str,
+ [ "L0: Heading\nheading three"
+ ])
+
+ str = "text\n === heading three"
+ line_groups(str,
+ [ "L0: Paragraph\ntext",
+ "L0: Verbatim\n === heading three\n"
+ ])
+
+ str = "text\n code\n === heading three"
+ line_groups(str,
+ [ "L0: Paragraph\ntext",
+ "L0: Verbatim\n code\n === heading three\n"
+ ])
+
+ str = "text\n code\n=== heading three"
+ line_groups(str,
+ [ "L0: Paragraph\ntext",
+ "L0: Verbatim\n code\n",
+ "L0: Heading\nheading three"
+ ])
+
+ end
+
+
+end
diff --git a/ruby_1_8_5/lib/rdoc/options.rb b/ruby_1_8_5/lib/rdoc/options.rb
new file mode 100644
index 0000000000..53eee992e2
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/options.rb
@@ -0,0 +1,575 @@
+# We handle the parsing of options, and subsequently as a singleton
+# object to be queried for option values
+
+require "rdoc/ri/ri_paths"
+
+class Options
+
+ require 'singleton'
+ require 'getoptlong'
+
+ include Singleton
+
+ # files matching this pattern will be excluded
+ attr_accessor :exclude
+
+ # the name of the output directory
+ attr_accessor :op_dir
+
+ # the name to use for the output
+ attr_reader :op_name
+
+ # include private and protected methods in the
+ # output
+ attr_accessor :show_all
+
+ # name of the file, class or module to display in
+ # the initial index page (if not specified
+ # the first file we encounter is used)
+ attr_accessor :main_page
+
+ # merge into classes of the name name when generating ri
+ attr_reader :merge
+
+ # Don't display progress as we process the files
+ attr_reader :quiet
+
+ # description of the output generator (set with the <tt>-fmt</tt>
+ # option
+ attr_accessor :generator
+
+ # and the list of files to be processed
+ attr_reader :files
+
+ # array of directories to search for files to satisfy an :include:
+ attr_reader :rdoc_include
+
+ # title to be used out the output
+ #attr_writer :title
+
+ # template to be used when generating output
+ attr_reader :template
+
+ # should diagrams be drawn
+ attr_reader :diagram
+
+ # should we draw fileboxes in diagrams
+ attr_reader :fileboxes
+
+ # include the '#' at the front of hyperlinked instance method names
+ attr_reader :show_hash
+
+ # image format for diagrams
+ attr_reader :image_format
+
+ # character-set
+ attr_reader :charset
+
+ # should source code be included inline, or displayed in a popup
+ attr_reader :inline_source
+
+ # should the output be placed into a single file
+ attr_reader :all_one_file
+
+ # the number of columns in a tab
+ attr_reader :tab_width
+
+ # include line numbers in the source listings
+ attr_reader :include_line_numbers
+
+ # pattern for additional attr_... style methods
+ attr_reader :extra_accessors
+ attr_reader :extra_accessor_flags
+
+ # URL of stylesheet
+ attr_reader :css
+
+ # URL of web cvs frontend
+ attr_reader :webcvs
+
+ # Are we promiscuous about showing module contents across
+ # multiple files
+ attr_reader :promiscuous
+
+ module OptionList
+
+ OPTION_LIST = [
+ [ "--accessor", "-A", "accessorname[,..]",
+ "comma separated list of additional class methods\n" +
+ "that should be treated like 'attr_reader' and\n" +
+ "friends. Option may be repeated. Each accessorname\n" +
+ "may have '=text' appended, in which case that text\n" +
+ "appears where the r/w/rw appears for normal accessors."],
+
+ [ "--all", "-a", nil,
+ "include all methods (not just public)\nin the output" ],
+
+ [ "--charset", "-c", "charset",
+ "specifies HTML character-set" ],
+
+ [ "--debug", "-D", nil,
+ "displays lots on internal stuff" ],
+
+ [ "--diagram", "-d", nil,
+ "Generate diagrams showing modules and classes.\n" +
+ "You need dot V1.8.6 or later to use the --diagram\n" +
+ "option correctly. Dot is available from\n"+
+ "http://www.research.att.com/sw/tools/graphviz/" ],
+
+ [ "--exclude", "-x", "pattern",
+ "do not process files or directories matching\n" +
+ "pattern. Files given explicitly on the command\n" +
+ "line will never be excluded." ],
+
+ [ "--extension", "-E", "new=old",
+ "Treat files ending with .new as if they ended with\n" +
+ ".old. Using '-E cgi=rb' will cause xxx.cgi to be\n" +
+ "parsed as a Ruby file"],
+
+ [ "--fileboxes", "-F", nil,
+ "classes are put in boxes which represents\n" +
+ "files, where these classes reside. Classes\n" +
+ "shared between more than one file are\n" +
+ "shown with list of files that sharing them.\n" +
+ "Silently discarded if --diagram is not given\n" +
+ "Experimental." ],
+
+ [ "--fmt", "-f", "format name",
+ "set the output formatter (see below)" ],
+
+ [ "--help", "-h", nil,
+ "you're looking at it" ],
+
+ [ "--help-output", "-O", nil,
+ "explain the various output options" ],
+
+ [ "--image-format", "-I", "gif/png/jpg/jpeg",
+ "Sets output image format for diagrams. Can\n" +
+ "be png, gif, jpeg, jpg. If this option is\n" +
+ "omitted, png is used. Requires --diagram." ],
+
+ [ "--include", "-i", "dir[,dir...]",
+ "set (or add to) the list of directories\n" +
+ "to be searched when satisfying :include:\n" +
+ "requests. Can be used more than once." ],
+
+ [ "--inline-source", "-S", nil,
+ "Show method source code inline, rather\n" +
+ "than via a popup link" ],
+
+ [ "--line-numbers", "-N", nil,
+ "Include line numbers in the source code" ],
+
+ [ "--main", "-m", "name",
+ "'name' will be the initial page displayed" ],
+
+ [ "--merge", "-M", nil,
+ "when creating ri output, merge processed classes\n" +
+ "into previously documented classes of the name name"],
+
+ [ "--one-file", "-1", nil,
+ "put all the output into a single file" ],
+
+ [ "--op", "-o", "dir",
+ "set the output directory" ],
+
+ [ "--opname", "-n", "name",
+ "Set the 'name' of the output. Has no\n" +
+ "effect for HTML." ],
+
+ [ "--promiscuous", "-p", nil,
+ "When documenting a file that contains a module\n" +
+ "or class also defined in other files, show\n" +
+ "all stuff for that module/class in each files\n" +
+ "page. By default, only show stuff defined in\n" +
+ "that particular file." ],
+
+ [ "--quiet", "-q", nil,
+ "don't show progress as we parse" ],
+
+ [ "--ri", "-r", nil,
+ "generate output for use by 'ri.' The files are\n" +
+ "stored in the '.rdoc' directory under your home\n"+
+ "directory unless overridden by a subsequent\n" +
+ "--op parameter, so no special privileges are needed." ],
+
+ [ "--ri-site", "-R", nil,
+ "generate output for use by 'ri.' The files are\n" +
+ "stored in a site-wide directory, making them accessible\n"+
+ "to others, so special privileges are needed." ],
+
+ [ "--ri-system", "-Y", nil,
+ "generate output for use by 'ri.' The files are\n" +
+ "stored in a system-level directory, making them accessible\n"+
+ "to others, so special privileges are needed. This option\n"+
+ "is intended to be used during Ruby installations" ],
+
+ [ "--show-hash", "-H", nil,
+ "A name of the form #name in a comment\n" +
+ "is a possible hyperlink to an instance\n" +
+ "method name. When displayed, the '#' is\n" +
+ "removed unless this option is specified" ],
+
+ [ "--style", "-s", "stylesheet url",
+ "specifies the URL of a separate stylesheet." ],
+
+ [ "--tab-width", "-w", "n",
+ "Set the width of tab characters (default 8)"],
+
+ [ "--template", "-T", "template name",
+ "Set the template used when generating output" ],
+
+ [ "--title", "-t", "text",
+ "Set 'txt' as the title for the output" ],
+
+ [ "--version", "-v", nil,
+ "display RDoc's version" ],
+
+ [ "--webcvs", "-W", "url",
+ "Specify a URL for linking to a web frontend\n" +
+ "to CVS. If the URL contains a '\%s', the\n" +
+ "name of the current file will be substituted;\n" +
+ "if the URL doesn't contain a '\%s', the\n" +
+ "filename will be appended to it." ],
+ ]
+
+ def OptionList.options
+ OPTION_LIST.map do |long, short, arg,|
+ [ long,
+ short,
+ arg ? GetoptLong::REQUIRED_ARGUMENT : GetoptLong::NO_ARGUMENT
+ ]
+ end
+ end
+
+
+ def OptionList.strip_output(text)
+ text =~ /^\s+/
+ leading_spaces = $&
+ text.gsub!(/^#{leading_spaces}/, '')
+ $stdout.puts text
+ end
+
+
+ # Show an error and exit
+
+ def OptionList.error(msg)
+ $stderr.puts
+ $stderr.puts msg
+ $stderr.puts "\nFor help on options, try 'rdoc --help'\n\n"
+ exit 1
+ end
+
+ # Show usage and exit
+
+ def OptionList.usage(generator_names)
+
+ puts
+ puts(VERSION_STRING)
+ puts
+
+ name = File.basename($0)
+ OptionList.strip_output(<<-EOT)
+ Usage:
+
+ #{name} [options] [names...]
+
+ Files are parsed, and the information they contain
+ collected, before any output is produced. This allows cross
+ references between all files to be resolved. If a name is a
+ directory, it is traversed. If no names are specified, all
+ Ruby files in the current directory (and subdirectories) are
+ processed.
+
+ Options:
+
+ EOT
+
+ OPTION_LIST.each do |long, short, arg, desc|
+ opt = sprintf("%20s", "#{long}, #{short}")
+ oparg = sprintf("%-7s", arg)
+ print "#{opt} #{oparg}"
+ desc = desc.split("\n")
+ if arg.nil? || arg.length < 7
+ puts desc.shift
+ else
+ puts
+ end
+ desc.each do |line|
+ puts(" "*28 + line)
+ end
+ puts
+ end
+
+ puts "\nAvailable output formatters: " +
+ generator_names.sort.join(', ') + "\n\n"
+
+ puts "For information on where the output goes, use\n\n"
+ puts " rdoc --help-output\n\n"
+
+ exit 0
+ end
+
+ def OptionList.help_output
+ OptionList.strip_output(<<-EOT)
+ How RDoc generates output depends on the output formatter being
+ used, and on the options you give.
+
+ - HTML output is normally produced into a number of separate files
+ (one per class, module, and file, along with various indices).
+ These files will appear in the directory given by the --op
+ option (doc/ by default).
+
+ - XML output by default is written to standard output. If a
+ --opname option is given, the output will instead be written
+ to a file with that name in the output directory.
+
+ - .chm files (Windows help files) are written in the --op directory.
+ If an --opname parameter is present, that name is used, otherwise
+ the file will be called rdoc.chm.
+
+ For information on other RDoc options, use "rdoc --help".
+ EOT
+ exit 0
+ end
+ end
+
+ # Parse command line options. We're passed a hash containing
+ # output generators, keyed by the generator name
+
+ def parse(argv, generators)
+ old_argv = ARGV.dup
+ begin
+ ARGV.replace(argv)
+ @op_dir = "doc"
+ @op_name = nil
+ @show_all = false
+ @main_page = nil
+ @marge = false
+ @exclude = []
+ @quiet = false
+ @generator_name = 'html'
+ @generator = generators[@generator_name]
+ @rdoc_include = []
+ @title = nil
+ @template = nil
+ @diagram = false
+ @fileboxes = false
+ @show_hash = false
+ @image_format = 'png'
+ @inline_source = false
+ @all_one_file = false
+ @tab_width = 8
+ @include_line_numbers = false
+ @extra_accessor_flags = {}
+ @promiscuous = false
+
+ @css = nil
+ @webcvs = nil
+
+ @charset = case $KCODE
+ when /^S/i
+ 'Shift_JIS'
+ when /^E/i
+ 'EUC-JP'
+ else
+ 'iso-8859-1'
+ end
+
+ accessors = []
+
+ go = GetoptLong.new(*OptionList.options)
+ go.quiet = true
+
+ go.each do |opt, arg|
+ case opt
+ when "--all" then @show_all = true
+ when "--charset" then @charset = arg
+ when "--debug" then $DEBUG = true
+ when "--exclude" then @exclude << Regexp.new(arg)
+ when "--inline-source" then @inline_source = true
+ when "--line-numbers" then @include_line_numbers = true
+ when "--main" then @main_page = arg
+ when "--merge" then @merge = true
+ when "--one-file" then @all_one_file = @inline_source = true
+ when "--op" then @op_dir = arg
+ when "--opname" then @op_name = arg
+ when "--promiscuous" then @promiscuous = true
+ when "--quiet" then @quiet = true
+ when "--show-hash" then @show_hash = true
+ when "--style" then @css = arg
+ when "--template" then @template = arg
+ when "--title" then @title = arg
+ when "--webcvs" then @webcvs = arg
+
+ when "--accessor"
+ arg.split(/,/).each do |accessor|
+ if accessor =~ /^(\w+)(=(.*))?$/
+ accessors << $1
+ @extra_accessor_flags[$1] = $3
+ end
+ end
+
+ when "--diagram"
+ check_diagram
+ @diagram = true
+
+ when "--fileboxes"
+ @fileboxes = true if @diagram
+
+ when "--fmt"
+ @generator_name = arg.downcase
+ setup_generator(generators)
+
+ when "--help"
+ OptionList.usage(generators.keys)
+
+ when "--help-output"
+ OptionList.help_output
+
+ when "--image-format"
+ if ['gif', 'png', 'jpeg', 'jpg'].include?(arg)
+ @image_format = arg
+ else
+ raise GetoptLong::InvalidOption.new("unknown image format: #{arg}")
+ end
+
+ when "--include"
+ @rdoc_include.concat arg.split(/\s*,\s*/)
+
+ when "--ri", "--ri-site", "--ri-system"
+ @generator_name = "ri"
+ @op_dir = case opt
+ when "--ri" then RI::Paths::HOMEDIR
+ when "--ri-site" then RI::Paths::SITEDIR
+ when "--ri-system" then RI::Paths::SYSDIR
+ else fail opt
+ end
+ setup_generator(generators)
+
+ when "--tab-width"
+ begin
+ @tab_width = Integer(arg)
+ rescue
+ $stderr.puts "Invalid tab width: '#{arg}'"
+ exit 1
+ end
+
+ when "--extension"
+ new, old = arg.split(/=/, 2)
+ OptionList.error("Invalid parameter to '-E'") unless new && old
+ unless RDoc::ParserFactory.alias_extension(old, new)
+ OptionList.error("Unknown extension .#{old} to -E")
+ end
+
+ when "--version"
+ puts VERSION_STRING
+ exit
+ end
+
+ end
+
+ @files = ARGV.dup
+
+ @rdoc_include << "." if @rdoc_include.empty?
+
+ if @exclude.empty?
+ @exclude = nil
+ else
+ @exclude = Regexp.new(@exclude.join("|"))
+ end
+
+ check_files
+
+ # If no template was specified, use the default
+ # template for the output formatter
+
+ @template ||= @generator_name
+
+ # Generate a regexp from the accessors
+ unless accessors.empty?
+ re = '^(' + accessors.map{|a| Regexp.quote(a)}.join('|') + ')$'
+ @extra_accessors = Regexp.new(re)
+ end
+
+ rescue GetoptLong::InvalidOption, GetoptLong::MissingArgument => error
+ OptionList.error(error.message)
+
+ ensure
+ ARGV.replace(old_argv)
+ end
+ end
+
+
+ def title
+ @title ||= "RDoc Documentation"
+ end
+
+ # Set the title, but only if not already set. This means that a title set from
+ # the command line trumps one set in a source file
+
+ def title=(string)
+ @title ||= string
+ end
+
+
+ private
+
+ # Set up an output generator for the format in @generator_name
+ def setup_generator(generators)
+ @generator = generators[@generator_name]
+ if !@generator
+ OptionList.error("Invalid output formatter")
+ end
+
+ if @generator_name == "xml"
+ @all_one_file = true
+ @inline_source = true
+ end
+ end
+
+ # Check that the right version of 'dot' is available.
+ # Unfortuately this doesn't work correctly under Windows NT,
+ # so we'll bypass the test under Windows
+
+ def check_diagram
+ return if RUBY_PLATFORM =~ /win/
+
+ ok = false
+ ver = nil
+ IO.popen("dot -V 2>&1") do |io|
+ ver = io.read
+ if ver =~ /dot\s+version(?:\s+gviz)?\s+(\d+)\.(\d+)/
+ ok = ($1.to_i > 1) || ($1.to_i == 1 && $2.to_i >= 8)
+ end
+ end
+ unless ok
+ if ver =~ /^dot version/
+ $stderr.puts "Warning: You may need dot V1.8.6 or later to use\n",
+ "the --diagram option correctly. You have:\n\n ",
+ ver,
+ "\nDiagrams might have strange background colors.\n\n"
+ else
+ $stderr.puts "You need the 'dot' program to produce diagrams.",
+ "(see http://www.research.att.com/sw/tools/graphviz/)\n\n"
+ exit
+ end
+# exit
+ end
+ end
+
+ # Check that the files on the command line exist
+
+ def check_files
+ @files.each do |f|
+ stat = File.stat f rescue error("File not found: #{f}")
+ error("File '#{f}' not readable") unless stat.readable?
+ end
+ end
+
+ def error(str)
+ $stderr.puts str
+ exit(1)
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rdoc/parsers/parse_c.rb b/ruby_1_8_5/lib/rdoc/parsers/parse_c.rb
new file mode 100644
index 0000000000..fdec9c6b23
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/parsers/parse_c.rb
@@ -0,0 +1,697 @@
+ # We attempt to parse C extension files. Basically we look for
+ # the standard patterns that you find in extensions: <tt>rb_define_class,
+ # rb_define_method</tt> and so on. We also try to find the corresponding
+ # C source for the methods and extract comments, but if we fail
+ # we don't worry too much.
+ #
+ # The comments associated with a Ruby method are extracted from the C
+ # comment block associated with the routine that _implements_ that
+ # method, that is to say the method whose name is given in the
+ # <tt>rb_define_method</tt> call. For example, you might write:
+ #
+ # /*
+ # * Returns a new array that is a one-dimensional flattening of this
+ # * array (recursively). That is, for every element that is an array,
+ # * extract its elements into the new array.
+ # *
+ # * s = [ 1, 2, 3 ] #=> [1, 2, 3]
+ # * t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]]
+ # * a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
+ # * a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ # */
+ # static VALUE
+ # rb_ary_flatten(ary)
+ # VALUE ary;
+ # {
+ # ary = rb_obj_dup(ary);
+ # rb_ary_flatten_bang(ary);
+ # return ary;
+ # }
+ #
+ # ...
+ #
+ # void
+ # Init_Array()
+ # {
+ # ...
+ # rb_define_method(rb_cArray, "flatten", rb_ary_flatten, 0);
+ #
+ # Here RDoc will determine from the rb_define_method line that there's a
+ # method called "flatten" in class Array, and will look for the implementation
+ # in the method rb_ary_flatten. It will then use the comment from that
+ # method in the HTML output. This method must be in the same source file
+ # as the rb_define_method.
+ #
+ # C classes can be diagramed (see /tc/dl/ruby/ruby/error.c), and RDoc
+ # integrates C and Ruby source into one tree
+ #
+ # The comment blocks may include special direcives:
+ #
+ # [Document-class: <i>name</i>]
+ # This comment block is documentation for the given class. Use this
+ # when the <tt>Init_xxx</tt> method is not named after the class.
+ #
+ # [Document-method: <i>name</i>]
+ # This comment documents the named method. Use when RDoc cannot outomatically
+ # find the method from it's declaration
+ #
+ # [call-seq: <i>text up to an empty line</i>]
+ # Because C source doesn't give descripive names to Ruby-level parameters,
+ # you need to document the calling sequence explicitly
+ #
+ # In additon, RDoc assumes by default that the C method implementing a
+ # Ruby function is in the same source file as the rb_define_method call.
+ # If this isn't the case, add the comment
+ #
+ # rb_define_method(....); // in: filename
+ #
+ # As an example, we might have an extension that defines multiple classes
+ # in its Init_xxx method. We could document them using
+ #
+ #
+ # /*
+ # * Document-class: MyClass
+ # *
+ # * Encapsulate the writing and reading of the configuration
+ # * file. ...
+ # */
+ #
+ # /*
+ # * Document-method: read_value
+ # *
+ # * call-seq:
+ # * cfg.read_value(key) -> value
+ # * cfg.read_value(key} { |key| } -> value
+ # *
+ # * Return the value corresponding to +key+ from the configuration.
+ # * In the second form, if the key isn't found, invoke the
+ # * block and return its value.
+ # */
+ #
+
+
+ # Classes and modules built in to the interpreter. We need
+ # these to define superclasses of user objects
+
+require "rdoc/code_objects"
+require "rdoc/parsers/parserfactory"
+
+
+module RDoc
+
+ KNOWN_CLASSES = {
+ "rb_cObject" => "Object",
+ "rb_cArray" => "Array",
+ "rb_cBignum" => "Bignum",
+ "rb_cClass" => "Class",
+ "rb_cDir" => "Dir",
+ "rb_cData" => "Data",
+ "rb_cFalseClass" => "FalseClass",
+ "rb_cFile" => "File",
+ "rb_cFixnum" => "Fixnum",
+ "rb_cFloat" => "Float",
+ "rb_cHash" => "Hash",
+ "rb_cInteger" => "Integer",
+ "rb_cIO" => "IO",
+ "rb_cModule" => "Module",
+ "rb_cNilClass" => "NilClass",
+ "rb_cNumeric" => "Numeric",
+ "rb_cProc" => "Proc",
+ "rb_cRange" => "Range",
+ "rb_cRegexp" => "Regexp",
+ "rb_cString" => "String",
+ "rb_cSymbol" => "Symbol",
+ "rb_cThread" => "Thread",
+ "rb_cTime" => "Time",
+ "rb_cTrueClass" => "TrueClass",
+ "rb_cStruct" => "Struct",
+ "rb_eException" => "Exception",
+ "rb_eStandardError" => "StandardError",
+ "rb_eSystemExit" => "SystemExit",
+ "rb_eInterrupt" => "Interrupt",
+ "rb_eSignal" => "Signal",
+ "rb_eFatal" => "Fatal",
+ "rb_eArgError" => "ArgError",
+ "rb_eEOFError" => "EOFError",
+ "rb_eIndexError" => "IndexError",
+ "rb_eRangeError" => "RangeError",
+ "rb_eIOError" => "IOError",
+ "rb_eRuntimeError" => "RuntimeError",
+ "rb_eSecurityError" => "SecurityError",
+ "rb_eSystemCallError" => "SystemCallError",
+ "rb_eTypeError" => "TypeError",
+ "rb_eZeroDivError" => "ZeroDivError",
+ "rb_eNotImpError" => "NotImpError",
+ "rb_eNoMemError" => "NoMemError",
+ "rb_eFloatDomainError" => "FloatDomainError",
+ "rb_eScriptError" => "ScriptError",
+ "rb_eNameError" => "NameError",
+ "rb_eSyntaxError" => "SyntaxError",
+ "rb_eLoadError" => "LoadError",
+
+ "rb_mKernel" => "Kernel",
+ "rb_mComparable" => "Comparable",
+ "rb_mEnumerable" => "Enumerable",
+ "rb_mPrecision" => "Precision",
+ "rb_mErrno" => "Errno",
+ "rb_mFileTest" => "FileTest",
+ "rb_mGC" => "GC",
+ "rb_mMath" => "Math",
+ "rb_mProcess" => "Process"
+
+ }
+
+ # See rdoc/c_parse.rb
+
+ class C_Parser
+
+
+ extend ParserFactory
+ parse_files_matching(/\.(?:([CcHh])\1?|c([+xp])\2|y)\z/)
+
+ @@known_bodies = {}
+
+ # prepare to parse a C file
+ def initialize(top_level, file_name, body, options, stats)
+ @known_classes = KNOWN_CLASSES.dup
+ @body = handle_tab_width(handle_ifdefs_in(body))
+ @options = options
+ @stats = stats
+ @top_level = top_level
+ @classes = Hash.new
+ @file_dir = File.dirname(file_name)
+ @progress = $stderr unless options.quiet
+ end
+
+ # Extract the classes/modules and methods from a C file
+ # and return the corresponding top-level object
+ def scan
+ remove_commented_out_lines
+ do_classes
+ do_constants
+ do_methods
+ do_includes
+ do_aliases
+ @top_level
+ end
+
+ #######
+ private
+ #######
+
+ def progress(char)
+ unless @options.quiet
+ @progress.print(char)
+ @progress.flush
+ end
+ end
+
+ def warn(msg)
+ $stderr.puts
+ $stderr.puts msg
+ $stderr.flush
+ end
+
+ def remove_private_comments(comment)
+ comment.gsub!(/\/?\*--(.*?)\/?\*\+\+/m, '')
+ comment.sub!(/\/?\*--.*/m, '')
+ end
+
+ # remove lines that are commented out that might otherwise get
+ # picked up when scanning for classes and methods
+
+ def remove_commented_out_lines
+ @body.gsub!(%r{//.*rb_define_}, '//')
+ end
+
+ def handle_class_module(var_name, class_mod, class_name, parent, in_module)
+ progress(class_mod[0, 1])
+
+ parent_name = @known_classes[parent] || parent
+
+ if in_module
+ enclosure = @classes[in_module]
+ unless enclosure
+ if enclosure = @known_classes[in_module]
+ handle_class_module(in_module, (/^rb_m/ =~ in_module ? "module" : "class"),
+ enclosure, nil, nil)
+ enclosure = @classes[in_module]
+ end
+ end
+ unless enclosure
+ warn("Enclosing class/module '#{in_module}' for " +
+ "#{class_mod} #{class_name} not known")
+ return
+ end
+ else
+ enclosure = @top_level
+ end
+
+ if class_mod == "class"
+ cm = enclosure.add_class(NormalClass, class_name, parent_name)
+ @stats.num_classes += 1
+ else
+ cm = enclosure.add_module(NormalModule, class_name)
+ @stats.num_modules += 1
+ end
+ cm.record_location(enclosure.toplevel)
+
+ find_class_comment(cm.full_name, cm)
+ @classes[var_name] = cm
+ @known_classes[var_name] = cm.full_name
+ end
+
+
+ ############################################################
+
+ def find_class_comment(class_name, class_meth)
+ comment = nil
+ if @body =~ %r{((?>/\*.*?\*/\s+))
+ (static\s+)?void\s+Init_#{class_name}\s*(?:_\(\s*)?\(\s*(?:void\s*)?\)}xmi
+ comment = $1
+ elsif @body =~ %r{Document-(class|module):\s#{class_name}\s*?\n((?>.*?\*/))}m
+ comment = $2
+ end
+ class_meth.comment = mangle_comment(comment) if comment
+ end
+
+ ############################################################
+
+ def do_classes
+ @body.scan(/(\w+)\s* = \s*rb_define_module\s*\(\s*"(\w+)"\s*\)/mx) do
+ |var_name, class_name|
+ handle_class_module(var_name, "module", class_name, nil, nil)
+ end
+
+ # The '.' lets us handle SWIG-generated files
+ @body.scan(/([\w\.]+)\s* = \s*rb_define_class\s*
+ \(
+ \s*"(\w+)",
+ \s*(\w+)\s*
+ \)/mx) do
+
+ |var_name, class_name, parent|
+ handle_class_module(var_name, "class", class_name, parent, nil)
+ end
+
+ @body.scan(/(\w+)\s*=\s*boot_defclass\s*\(\s*"(\w+?)",\s*(\w+?)\s*\)/) do
+ |var_name, class_name, parent|
+ parent = nil if parent == "0"
+ handle_class_module(var_name, "class", class_name, parent, nil)
+ end
+
+ @body.scan(/(\w+)\s* = \s*rb_define_module_under\s*
+ \(
+ \s*(\w+),
+ \s*"(\w+)"
+ \s*\)/mx) do
+
+ |var_name, in_module, class_name|
+ handle_class_module(var_name, "module", class_name, nil, in_module)
+ end
+
+ @body.scan(/([\w\.]+)\s* = \s*rb_define_class_under\s*
+ \(
+ \s*(\w+),
+ \s*"(\w+)",
+ \s*(\w+)\s*
+ \s*\)/mx) do
+
+ |var_name, in_module, class_name, parent|
+ handle_class_module(var_name, "class", class_name, parent, in_module)
+ end
+
+ end
+
+ ###########################################################
+
+ def do_constants
+ @body.scan(%r{\Wrb_define_
+ (
+ variable |
+ readonly_variable |
+ const |
+ global_const |
+ )
+ \s*\(
+ (?:\s*(\w+),)?
+ \s*"(\w+)",
+ \s*(.*?)\s*\)\s*;
+ }xm) do
+
+ |type, var_name, const_name, definition|
+ var_name = "rb_cObject" if !var_name or var_name == "rb_mKernel"
+ handle_constants(type, var_name, const_name, definition)
+ end
+ end
+
+ ############################################################
+
+ def do_methods
+
+ @body.scan(%r{rb_define_
+ (
+ singleton_method |
+ method |
+ module_function |
+ private_method
+ )
+ \s*\(\s*([\w\.]+),
+ \s*"([^"]+)",
+ \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
+ \s*(-?\w+)\s*\)
+ (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))?
+ }xm) do
+ |type, var_name, meth_name, meth_body, param_count, source_file|
+ #"
+
+ # Ignore top-object and weird struct.c dynamic stuff
+ next if var_name == "ruby_top_self"
+ next if var_name == "nstr"
+ next if var_name == "envtbl"
+ next if var_name == "argf" # it'd be nice to handle this one
+
+ var_name = "rb_cObject" if var_name == "rb_mKernel"
+ handle_method(type, var_name, meth_name,
+ meth_body, param_count, source_file)
+ end
+
+ @body.scan(%r{rb_define_attr\(
+ \s*([\w\.]+),
+ \s*"([^"]+)",
+ \s*(\d+),
+ \s*(\d+)\s*\);
+ }xm) do #"
+ |var_name, attr_name, attr_reader, attr_writer|
+
+ #var_name = "rb_cObject" if var_name == "rb_mKernel"
+ handle_attr(var_name, attr_name,
+ attr_reader.to_i != 0,
+ attr_writer.to_i != 0)
+ end
+
+ @body.scan(%r{rb_define_global_function\s*\(
+ \s*"([^"]+)",
+ \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
+ \s*(-?\w+)\s*\)
+ (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))?
+ }xm) do #"
+ |meth_name, meth_body, param_count, source_file|
+ handle_method("method", "rb_mKernel", meth_name,
+ meth_body, param_count, source_file)
+ end
+
+ @body.scan(/define_filetest_function\s*\(
+ \s*"([^"]+)",
+ \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
+ \s*(-?\w+)\s*\)/xm) do #"
+ |meth_name, meth_body, param_count|
+
+ handle_method("method", "rb_mFileTest", meth_name, meth_body, param_count)
+ handle_method("singleton_method", "rb_cFile", meth_name, meth_body, param_count)
+ end
+ end
+
+ ############################################################
+
+ def do_aliases
+ @body.scan(%r{rb_define_alias\s*\(\s*(\w+),\s*"([^"]+)",\s*"([^"]+)"\s*\)}m) do
+ |var_name, new_name, old_name|
+ @stats.num_methods += 1
+ class_name = @known_classes[var_name] || var_name
+ class_obj = find_class(var_name, class_name)
+
+ class_obj.add_alias(Alias.new("", old_name, new_name, ""))
+ end
+ end
+
+ ############################################################
+
+ def handle_constants(type, var_name, const_name, definition)
+ #@stats.num_constants += 1
+ class_name = @known_classes[var_name]
+
+ return unless class_name
+
+ class_obj = find_class(var_name, class_name)
+
+ unless class_obj
+ warn("Enclosing class/module '#{const_name}' for not known")
+ return
+ end
+
+ comment = find_const_comment(type, const_name)
+
+ con = Constant.new(const_name, definition, mangle_comment(comment))
+ class_obj.add_constant(con)
+ end
+
+ ###########################################################
+
+ def find_const_comment(type, const_name)
+ if @body =~ %r{((?>/\*.*?\*/\s+))
+ rb_define_#{type}\((?:\s*(\w+),)?\s*"#{const_name}"\s*,.*?\)\s*;}xmi
+ $1
+ elsif @body =~ %r{Document-(?:const|global|variable):\s#{const_name}\s*?\n((?>.*?\*/))}m
+ $1
+ else
+ ''
+ end
+ end
+
+ ###########################################################
+
+ def handle_attr(var_name, attr_name, reader, writer)
+ rw = ''
+ if reader
+ #@stats.num_methods += 1
+ rw << 'R'
+ end
+ if writer
+ #@stats.num_methods += 1
+ rw << 'W'
+ end
+
+ class_name = @known_classes[var_name]
+
+ return unless class_name
+
+ class_obj = find_class(var_name, class_name)
+
+ if class_obj
+ comment = find_attr_comment(attr_name)
+ unless comment.empty?
+ comment = mangle_comment(comment)
+ end
+ att = Attr.new('', attr_name, rw, comment)
+ class_obj.add_attribute(att)
+ end
+
+ end
+
+ ###########################################################
+
+ def find_attr_comment(attr_name)
+ if @body =~ %r{((?>/\*.*?\*/\s+))
+ rb_define_attr\((?:\s*(\w+),)?\s*"#{attr_name}"\s*,.*?\)\s*;}xmi
+ $1
+ elsif @body =~ %r{Document-attr:\s#{attr_name}\s*?\n((?>.*?\*/))}m
+ $1
+ else
+ ''
+ end
+ end
+
+ ###########################################################
+
+ def handle_method(type, var_name, meth_name,
+ meth_body, param_count, source_file = nil)
+ progress(".")
+
+ @stats.num_methods += 1
+ class_name = @known_classes[var_name]
+
+ return unless class_name
+
+ class_obj = find_class(var_name, class_name)
+
+ if class_obj
+ if meth_name == "initialize"
+ meth_name = "new"
+ type = "singleton_method"
+ end
+ meth_obj = AnyMethod.new("", meth_name)
+ meth_obj.singleton =
+ %w{singleton_method module_function}.include?(type)
+
+ p_count = (Integer(param_count) rescue -1)
+
+ if p_count < 0
+ meth_obj.params = "(...)"
+ elsif p_count == 0
+ meth_obj.params = "()"
+ else
+ meth_obj.params = "(" +
+ (1..p_count).map{|i| "p#{i}"}.join(", ") +
+ ")"
+ end
+
+ if source_file
+ file_name = File.join(@file_dir, source_file)
+ body = (@@known_bodies[source_file] ||= File.read(file_name))
+ else
+ body = @body
+ end
+ if find_body(meth_body, meth_obj, body) and meth_obj.document_self
+ class_obj.add_method(meth_obj)
+ end
+ end
+ end
+
+ ############################################################
+
+ # Find the C code corresponding to a Ruby method
+ def find_body(meth_name, meth_obj, body, quiet = false)
+ case body
+ when %r{((?>/\*.*?\*/\s*))(?:static\s+)?VALUE\s+#{meth_name}
+ \s*(\(.*?\)).*?^}xm
+ comment, params = $1, $2
+ body_text = $&
+
+ remove_private_comments(comment) if comment
+
+ # see if we can find the whole body
+
+ re = Regexp.escape(body_text) + '[^(]*^\{.*?^\}'
+ if Regexp.new(re, Regexp::MULTILINE).match(body)
+ body_text = $&
+ end
+
+ # The comment block may have been overridden with a
+ # 'Document-method' block. This happens in the interpreter
+ # when multiple methods are vectored through to the same
+ # C method but those methods are logically distinct (for
+ # example Kernel.hash and Kernel.object_id share the same
+ # implementation
+
+ override_comment = find_override_comment(meth_obj.name)
+ comment = override_comment if override_comment
+
+ find_modifiers(comment, meth_obj) if comment
+
+# meth_obj.params = params
+ meth_obj.start_collecting_tokens
+ meth_obj.add_token(RubyToken::Token.new(1,1).set_text(body_text))
+ meth_obj.comment = mangle_comment(comment)
+ when %r{((?>/\*.*?\*/\s*))^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m
+ comment = $1
+ find_body($2, meth_obj, body, true)
+ find_modifiers(comment, meth_obj)
+ meth_obj.comment = mangle_comment(comment) + meth_obj.comment
+ when %r{^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m
+ unless find_body($1, meth_obj, body, true)
+ warn "No definition for #{meth_name}" unless quiet
+ return false
+ end
+ else
+
+ # No body, but might still have an override comment
+ comment = find_override_comment(meth_obj.name)
+
+ if comment
+ find_modifiers(comment, meth_obj)
+ meth_obj.comment = mangle_comment(comment)
+ else
+ warn "No definition for #{meth_name}" unless quiet
+ return false
+ end
+ end
+ true
+ end
+
+
+ ##################################################
+ #
+ # If the comment block contains a section that looks like
+ # call-seq:
+ # Array.new
+ # Array.new(10)
+ # use it for the parameters
+ def find_modifiers(comment, meth_obj)
+ if comment.sub!(/:nodoc:\s*^\s*\*?\s*$/m, '') or
+ comment.sub!(/\A\/\*\s*:nodoc:\s*\*\/\Z/, '')
+ meth_obj.document_self = false
+ end
+ if comment.sub!(/call-seq:(.*?)^\s*\*?\s*$/m, '') or
+ comment.sub!(/\A\/\*\s*call-seq:(.*?)\*\/\Z/, '')
+ seq = $1
+ seq.gsub!(/^\s*\*\s*/, '')
+ meth_obj.call_seq = seq
+ end
+ end
+
+ ############################################################
+
+ def find_override_comment(meth_name)
+ name = Regexp.escape(meth_name)
+ if @body =~ %r{Document-method:\s#{name}\s*?\n((?>.*?\*/))}m
+ $1
+ end
+ end
+
+ ############################################################
+
+ # Look for includes of the form
+ # rb_include_module(rb_cArray, rb_mEnumerable);
+ def do_includes
+ @body.scan(/rb_include_module\s*\(\s*(\w+?),\s*(\w+?)\s*\)/) do |c,m|
+ if cls = @classes[c]
+ m = @known_classes[m] || m
+ cls.add_include(Include.new(m, ""))
+ end
+ end
+ end
+
+ ############################################################
+
+ # Remove the /*'s and leading asterisks from C comments
+
+ def mangle_comment(comment)
+ comment.sub!(%r{/\*+}) { " " * $&.length }
+ comment.sub!(%r{\*+/}) { " " * $&.length }
+ comment.gsub!(/^[ \t]*\*/m) { " " * $&.length }
+ comment
+ end
+
+ def find_class(raw_name, name)
+ unless @classes[raw_name]
+ if raw_name =~ /^rb_m/
+ @classes[raw_name] = @top_level.add_module(NormalModule, name)
+ else
+ @classes[raw_name] = @top_level.add_class(NormalClass, name, nil)
+ end
+ end
+ @classes[raw_name]
+ end
+
+ def handle_tab_width(body)
+ if /\t/ =~ body
+ tab_width = Options.instance.tab_width
+ body.split(/\n/).map do |line|
+ 1 while line.gsub!(/\t+/) { ' ' * (tab_width*$&.length - $`.length % tab_width)} && $~ #`
+ line
+ end .join("\n")
+ else
+ body
+ end
+ end
+
+ # Remove #ifdefs that would otherwise confuse us
+
+ def handle_ifdefs_in(body)
+ body.gsub(/^#ifdef HAVE_PROTOTYPES.*?#else.*?\n(.*?)#endif.*?\n/m) { $1 }
+ end
+
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rdoc/parsers/parse_f95.rb b/ruby_1_8_5/lib/rdoc/parsers/parse_f95.rb
new file mode 100644
index 0000000000..f3f6d76103
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/parsers/parse_f95.rb
@@ -0,0 +1,1841 @@
+#= parse_f95.rb - Fortran95 Parser
+#
+#== Overview
+#
+#"parse_f95.rb" parses Fortran95 files with suffixes "f90", "F90", "f95"
+#and "F95". Fortran95 files are expected to be conformed to Fortran95
+#standards.
+#
+#== Rules
+#
+#Fundamental rules are same as that of the Ruby parser.
+#But comment markers are '!' not '#'.
+#
+#=== Correspondence between RDoc documentation and Fortran95 programs
+#
+#"parse_f95.rb" parses main programs, modules, subroutines, functions,
+#derived-types, public variables, public constants,
+#defined operators and defined assignments.
+#These components are described in items of RDoc documentation, as follows.
+#
+#Files :: Files (same as Ruby)
+#Classes :: Modules
+#Methods :: Subroutines, functions, variables, constants, derived-types, defined operators, defined assignments
+#Required files :: Files in which imported modules, external subroutines and external functions are defined.
+#Included Modules :: List of imported modules
+#Attributes :: List of derived-types, List of imported modules all of whose components are published again
+#
+#Components listed in 'Methods' (subroutines, functions, ...)
+#defined in modules are described in the item of 'Classes'.
+#On the other hand, components defined in main programs or
+#as external procedures are described in the item of 'Files'.
+#
+#=== Components parsed by default
+#
+#By default, documentation on public components (subroutines, functions,
+#variables, constants, derived-types, defined operators,
+#defined assignments) are generated.
+#With "--all" option, documentation on all components
+#are generated (almost same as the Ruby parser).
+#
+#=== Information parsed automatically
+#
+#The following information is automatically parsed.
+#
+#* Types of arguments
+#* Types of variables and constants
+#* Types of variables in the derived types, and initial values
+#* NAMELISTs and types of variables in them, and initial values
+#
+#Aliases by interface statement are described in the item of 'Methods'.
+#
+#Components which are imported from other modules and published again
+#are described in the item of 'Methods'.
+#
+#=== Format of comment blocks
+#
+#Comment blocks should be written as follows.
+#Comment blocks are considered to be ended when the line without '!'
+#appears.
+#The indentation is not necessary.
+#
+# ! (Top of file)
+# !
+# ! Comment blocks for the files.
+# !
+# !--
+# ! The comment described in the part enclosed by
+# ! "!--" and "!++" is ignored.
+# !++
+# !
+# module hogehoge
+# !
+# ! Comment blocks for the modules (or the programs).
+# !
+#
+# private
+#
+# logical :: a ! a private variable
+# real, public :: b ! a public variable
+# integer, parameter :: c = 0 ! a public constant
+#
+# public :: c
+# public :: MULTI_ARRAY
+# public :: hoge, foo
+#
+# type MULTI_ARRAY
+# !
+# ! Comment blocks for the derived-types.
+# !
+# real, pointer :: var(:) =>null() ! Comments block for the variables.
+# integer :: num = 0
+# end type MULTI_ARRAY
+#
+# contains
+#
+# subroutine hoge( in, & ! Comment blocks between continuation lines are ignored.
+# & out )
+# !
+# ! Comment blocks for the subroutines or functions
+# !
+# character(*),intent(in):: in ! Comment blocks for the arguments.
+# character(*),intent(out),allocatable,target :: in
+# ! Comment blocks can be
+# ! written under Fortran statements.
+#
+# character(32) :: file ! This comment parsed as a variable in below NAMELIST.
+# integer :: id
+#
+# namelist /varinfo_nml/ file, id
+# !
+# ! Comment blocks for the NAMELISTs.
+# ! Information about variables are described above.
+# !
+#
+# ....
+#
+# end subroutine hoge
+#
+# integer function foo( in )
+# !
+# ! This part is considered as comment block.
+#
+# ! Comment blocks under blank lines are ignored.
+# !
+# integer, intent(in):: inA ! This part is considered as comment block.
+#
+# ! This part is ignored.
+#
+# end function foo
+#
+# subroutine hide( in, &
+# & out ) !:nodoc:
+# !
+# ! If "!:nodoc:" is described at end-of-line in subroutine
+# ! statement as above, the subroutine is ignored.
+# ! This assignment can be used to modules, subroutines,
+# ! functions, variables, constants, derived-types,
+# ! defined operators, defined assignments,
+# ! list of imported modules ("use" statement).
+# !
+#
+# ....
+#
+# end subroutine hide
+#
+# end module hogehoge
+#
+
+
+require "rdoc/code_objects"
+
+module RDoc
+
+ class Token
+
+ NO_TEXT = "??".freeze
+
+ def initialize(line_no, char_no)
+ @line_no = line_no
+ @char_no = char_no
+ @text = NO_TEXT
+ end
+ # Because we're used in contexts that expect to return a token,
+ # we set the text string and then return ourselves
+ def set_text(text)
+ @text = text
+ self
+ end
+
+ attr_reader :line_no, :char_no, :text
+
+ end
+
+ # See rdoc/parsers/parse_f95.rb
+
+ class Fortran95parser
+
+ extend ParserFactory
+ parse_files_matching(/\.((f|F)9(0|5)|F)$/)
+
+ @@external_aliases = []
+ @@public_methods = []
+
+ # "false":: Comments are below source code
+ # "true" :: Comments are upper source code
+ COMMENTS_ARE_UPPER = false
+
+ # Internal alias message
+ INTERNAL_ALIAS_MES = "Alias for"
+
+ # External alias message
+ EXTERNAL_ALIAS_MES = "The entity is"
+
+ # prepare to parse a Fortran 95 file
+ def initialize(top_level, file_name, body, options, stats)
+ @body = body
+ @stats = stats
+ @file_name = file_name
+ @options = options
+ @top_level = top_level
+ @progress = $stderr unless options.quiet
+ end
+
+ # devine code constructs
+ def scan
+
+ # remove private comment
+ remaining_code = remove_private_comments(@body)
+
+ # continuation lines are united to one line
+ remaining_code = united_to_one_line(remaining_code)
+
+ # semicolons are replaced to line feed
+ remaining_code = semicolon_to_linefeed(remaining_code)
+
+ # collect comment for file entity
+ whole_comment, remaining_code = collect_first_comment(remaining_code)
+ @top_level.comment = whole_comment
+
+ # String "remaining_code" is converted to Array "remaining_lines"
+ remaining_lines = remaining_code.split("\n")
+
+ # "module" or "program" parts are parsed (new)
+ #
+ level_depth = 0
+ block_searching_flag = nil
+ block_searching_lines = []
+ pre_comment = []
+ module_program_trailing = ""
+ module_program_name = ""
+ other_block_level_depth = 0
+ other_block_searching_flag = nil
+ remaining_lines.collect!{|line|
+ if !block_searching_flag && !other_block_searching_flag
+ if line =~ /^\s*?module\s+(\w+)\s*?(!.*?)?$/i
+ block_searching_flag = :module
+ block_searching_lines << line
+ module_program_name = $1
+ module_program_trailing = find_comments($2)
+ next false
+ elsif line =~ /^\s*?program\s+(\w+)\s*?(!.*?)?$/i ||
+ line =~ /^\s*?\w/ && !block_start?(line)
+ block_searching_flag = :program
+ block_searching_lines << line
+ module_program_name = $1 || ""
+ module_program_trailing = find_comments($2)
+ next false
+
+ elsif block_start?(line)
+ other_block_searching_flag = true
+ next line
+
+ elsif line =~ /^\s*?!\s?(.*)/
+ pre_comment << line
+ next line
+ else
+ pre_comment = []
+ next line
+ end
+ elsif other_block_searching_flag
+ other_block_level_depth += 1 if block_start?(line)
+ other_block_level_depth -= 1 if block_end?(line)
+ if other_block_level_depth < 0
+ other_block_level_depth = 0
+ other_block_searching_flag = nil
+ end
+ next line
+ end
+
+ block_searching_lines << line
+ level_depth += 1 if block_start?(line)
+ level_depth -= 1 if block_end?(line)
+ if level_depth >= 0
+ next false
+ end
+
+ # "module_program_code" is formatted.
+ # ":nodoc:" flag is checked.
+ #
+ module_program_code = block_searching_lines.join("\n")
+ module_program_code = remove_empty_head_lines(module_program_code)
+ if module_program_trailing =~ /^:nodoc:/
+ # next loop to search next block
+ level_depth = 0
+ block_searching_flag = false
+ block_searching_lines = []
+ pre_comment = []
+ next false
+ end
+
+ # NormalClass is created, and added to @top_level
+ #
+ if block_searching_flag == :module
+ module_name = module_program_name
+ module_code = module_program_code
+ module_trailing = module_program_trailing
+ progress "m"
+ @stats.num_modules += 1
+ f9x_module = @top_level.add_module NormalClass, module_name
+ f9x_module.record_location @top_level
+
+ f9x_comment = COMMENTS_ARE_UPPER ?
+ find_comments(pre_comment.join("\n")) + "\n" + module_trailing :
+ module_trailing + "\n" + find_comments(module_code.sub(/^.*$\n/i, ''))
+ f9x_module.comment = f9x_comment
+ parse_program_or_module(f9x_module, module_code)
+
+ TopLevel.all_files.each do |name, toplevel|
+ if toplevel.include_includes?(module_name, @options.ignore_case)
+ if !toplevel.include_requires?(@file_name, @options.ignore_case)
+ toplevel.add_require(Require.new(@file_name, ""))
+ end
+ end
+ toplevel.each_classmodule{|m|
+ if m.include_includes?(module_name, @options.ignore_case)
+ if !m.include_requires?(@file_name, @options.ignore_case)
+ m.add_require(Require.new(@file_name, ""))
+ end
+ end
+ }
+ end
+ elsif block_searching_flag == :program
+ program_name = module_program_name
+ program_code = module_program_code
+ program_trailing = module_program_trailing
+ progress "p"
+ program_comment = COMMENTS_ARE_UPPER ?
+ find_comments(pre_comment.join("\n")) + "\n" + program_trailing :
+ program_trailing + "\n" + find_comments(program_code.sub(/^.*$\n/i, ''))
+ program_comment = "\n\n= <i>Program</i> <tt>#{program_name}</tt>\n\n" \
+ + program_comment
+ @top_level.comment << program_comment
+ parse_program_or_module(@top_level, program_code, :private)
+ end
+
+ # next loop to search next block
+ level_depth = 0
+ block_searching_flag = false
+ block_searching_lines = []
+ pre_comment = []
+ next false
+ }
+
+ remaining_lines.delete_if{ |line|
+ line == false
+ }
+
+ # External subprograms and functions are parsed
+ #
+ parse_program_or_module(@top_level, remaining_lines.join("\n"),
+ :public, true)
+
+ @top_level
+ end # End of scan
+
+ private
+
+ def parse_program_or_module(container, code,
+ visibility=:public, external=nil)
+ return unless container
+ return unless code
+ remaining_lines = code.split("\n")
+ remaining_code = "#{code}"
+
+ #
+ # Parse variables before "contains" in module
+ #
+ level_depth = 0
+ before_contains_lines = []
+ before_contains_code = nil
+ before_contains_flag = nil
+ remaining_lines.each{ |line|
+ if !before_contains_flag
+ if line =~ /^\s*?module\s+\w+\s*?(!.*?)?$/i
+ before_contains_flag = true
+ end
+ else
+ break if line =~ /^\s*?contains\s*?(!.*?)?$/i
+ level_depth += 1 if block_start?(line)
+ level_depth -= 1 if block_end?(line)
+ break if level_depth < 0
+ before_contains_lines << line
+ end
+ }
+ before_contains_code = before_contains_lines.join("\n")
+ if before_contains_code
+ before_contains_code.gsub!(/^\s*?interface\s+.*?\s+end\s+interface.*?$/im, "")
+ before_contains_code.gsub!(/^\s*?type[\s\,]+.*?\s+end\s+type.*?$/im, "")
+ end
+
+ #
+ # Parse global "use"
+ #
+ use_check_code = "#{before_contains_code}"
+ cascaded_modules_list = []
+ while use_check_code =~ /^\s*?use\s+(\w+)(.*?)(!.*?)?$/i
+ use_check_code = $~.pre_match
+ use_check_code << $~.post_match
+ used_mod_name = $1.strip.chomp
+ used_list = $2 || ""
+ used_trailing = $3 || ""
+ next if used_trailing =~ /!:nodoc:/
+ if !container.include_includes?(used_mod_name, @options.ignore_case)
+ progress "."
+ container.add_include Include.new(used_mod_name, "")
+ end
+ if ! (used_list =~ /\,\s*?only\s*?:/i )
+ cascaded_modules_list << "\#" + used_mod_name
+ end
+ end
+
+ #
+ # Parse public and private, and store information.
+ # This information is used when "add_method" and
+ # "set_visibility_for" are called.
+ #
+ visibility_default, visibility_info =
+ parse_visibility(remaining_lines.join("\n"), visibility, container)
+ @@public_methods.concat visibility_info
+ if visibility_default == :public
+ if !cascaded_modules_list.empty?
+ cascaded_modules =
+ Attr.new("Cascaded Modules",
+ "Imported modules all of whose components are published again",
+ "",
+ cascaded_modules_list.join(", "))
+ container.add_attribute(cascaded_modules)
+ end
+ end
+
+ #
+ # Check rename elements
+ #
+ use_check_code = "#{before_contains_code}"
+ while use_check_code =~ /^\s*?use\s+(\w+)\s*?\,(.+)$/i
+ use_check_code = $~.pre_match
+ use_check_code << $~.post_match
+ used_mod_name = $1.strip.chomp
+ used_elements = $2.sub(/\s*?only\s*?:\s*?/i, '')
+ used_elements.split(",").each{ |used|
+ if /\s*?(\w+)\s*?=>\s*?(\w+)\s*?/ =~ used
+ local = $1
+ org = $2
+ @@public_methods.collect!{ |pub_meth|
+ if local == pub_meth["name"] ||
+ local.upcase == pub_meth["name"].upcase &&
+ @options.ignore_case
+ pub_meth["name"] = org
+ pub_meth["local_name"] = local
+ end
+ pub_meth
+ }
+ end
+ }
+ end
+
+ #
+ # Parse private "use"
+ #
+ use_check_code = remaining_lines.join("\n")
+ while use_check_code =~ /^\s*?use\s+(\w+)(.*?)(!.*?)?$/i
+ use_check_code = $~.pre_match
+ use_check_code << $~.post_match
+ used_mod_name = $1.strip.chomp
+ used_trailing = $3 || ""
+ next if used_trailing =~ /!:nodoc:/
+ if !container.include_includes?(used_mod_name, @options.ignore_case)
+ progress "."
+ container.add_include Include.new(used_mod_name, "")
+ end
+ end
+
+ container.each_includes{ |inc|
+ TopLevel.all_files.each do |name, toplevel|
+ indicated_mod = toplevel.find_symbol(inc.name,
+ nil, @options.ignore_case)
+ if indicated_mod
+ indicated_name = indicated_mod.parent.file_relative_name
+ if !container.include_requires?(indicated_name, @options.ignore_case)
+ container.add_require(Require.new(indicated_name, ""))
+ end
+ break
+ end
+ end
+ }
+
+ #
+ # Parse derived-types definitions
+ #
+ derived_types_comment = ""
+ remaining_code = remaining_lines.join("\n")
+ while remaining_code =~ /^\s*?
+ type[\s\,]+(public|private)?\s*?(::)?\s*?
+ (\w+)\s*?(!.*?)?$
+ (.*?)
+ ^\s*?end\s+type.*?$
+ /imx
+ remaining_code = $~.pre_match
+ remaining_code << $~.post_match
+ typename = $3.chomp.strip
+ type_elements = $5 || ""
+ type_code = remove_empty_head_lines($&)
+ type_trailing = find_comments($4)
+ next if type_trailing =~ /^:nodoc:/
+ type_visibility = $1
+ type_comment = COMMENTS_ARE_UPPER ?
+ find_comments($~.pre_match) + "\n" + type_trailing :
+ type_trailing + "\n" + find_comments(type_code.sub(/^.*$\n/i, ''))
+ type_element_visibility_public = true
+ type_code.split("\n").each{ |line|
+ if /^\s*?private\s*?$/ =~ line
+ type_element_visibility_public = nil
+ break
+ end
+ } if type_code
+
+ args_comment = ""
+ type_args_info = nil
+
+ if @options.show_all
+ args_comment = find_arguments(nil, type_code, true)
+ else
+ type_public_args_list = []
+ type_args_info = definition_info(type_code)
+ type_args_info.each{ |arg|
+ arg_is_public = type_element_visibility_public
+ arg_is_public = true if arg.include_attr?("public")
+ arg_is_public = nil if arg.include_attr?("private")
+ type_public_args_list << arg.varname if arg_is_public
+ }
+ args_comment = find_arguments(type_public_args_list, type_code)
+ end
+
+ type = AnyMethod.new("type #{typename}", typename)
+ type.singleton = false
+ type.params = ""
+ type.comment = "<b><em> Derived Type </em></b> :: <tt></tt>\n"
+ type.comment << args_comment if args_comment
+ type.comment << type_comment if type_comment
+ progress "t"
+ @stats.num_methods += 1
+ container.add_method type
+
+ set_visibility(container, typename, visibility_default, @@public_methods)
+
+ if type_visibility
+ type_visibility.gsub!(/\s/,'')
+ type_visibility.gsub!(/\,/,'')
+ type_visibility.gsub!(/:/,'')
+ type_visibility.downcase!
+ if type_visibility == "public"
+ container.set_visibility_for([typename], :public)
+ elsif type_visibility == "private"
+ container.set_visibility_for([typename], :private)
+ end
+ end
+
+ check_public_methods(type, container.name)
+
+ if @options.show_all
+ derived_types_comment << ", " unless derived_types_comment.empty?
+ derived_types_comment << typename
+ else
+ if type.visibility == :public
+ derived_types_comment << ", " unless derived_types_comment.empty?
+ derived_types_comment << typename
+ end
+ end
+
+ end
+
+ if !derived_types_comment.empty?
+ derived_types_table =
+ Attr.new("Derived Types", "Derived_Types", "",
+ derived_types_comment)
+ container.add_attribute(derived_types_table)
+ end
+
+ #
+ # move interface scope
+ #
+ interface_code = ""
+ while remaining_code =~ /^\s*?
+ interface(
+ \s+\w+ |
+ \s+operator\s*?\(.*?\) |
+ \s+assignment\s*?\(\s*?=\s*?\)
+ )?\s*?$
+ (.*?)
+ ^\s*?end\s+interface.*?$
+ /imx
+ interface_code << remove_empty_head_lines($&) + "\n"
+ remaining_code = $~.pre_match
+ remaining_code << $~.post_match
+ end
+
+ #
+ # Parse global constants or variables in modules
+ #
+ const_var_defs = definition_info(before_contains_code)
+ const_var_defs.each{|defitem|
+ next if defitem.nodoc
+ const_or_var_type = "Variable"
+ const_or_var_progress = "v"
+ if defitem.include_attr?("parameter")
+ const_or_var_type = "Constant"
+ const_or_var_progress = "c"
+ end
+ const_or_var = AnyMethod.new(const_or_var_type, defitem.varname)
+ const_or_var.singleton = false
+ const_or_var.params = ""
+ self_comment = find_arguments([defitem.varname], before_contains_code)
+ const_or_var.comment = "<b><em>" + const_or_var_type + "</em></b> :: <tt></tt>\n"
+ const_or_var.comment << self_comment if self_comment
+ progress const_or_var_progress
+ @stats.num_methods += 1
+ container.add_method const_or_var
+
+ set_visibility(container, defitem.varname, visibility_default, @@public_methods)
+
+ if defitem.include_attr?("public")
+ container.set_visibility_for([defitem.varname], :public)
+ elsif defitem.include_attr?("private")
+ container.set_visibility_for([defitem.varname], :private)
+ end
+
+ check_public_methods(const_or_var, container.name)
+
+ } if const_var_defs
+
+ remaining_lines = remaining_code.split("\n")
+
+ # "subroutine" or "function" parts are parsed (new)
+ #
+ level_depth = 0
+ block_searching_flag = nil
+ block_searching_lines = []
+ pre_comment = []
+ procedure_trailing = ""
+ procedure_name = ""
+ procedure_params = ""
+ procedure_prefix = ""
+ procedure_result_arg = ""
+ procedure_type = ""
+ contains_lines = []
+ contains_flag = nil
+ remaining_lines.collect!{|line|
+ if !block_searching_flag
+ # subroutine
+ if line =~ /^\s*?
+ (recursive|pure|elemental)?\s*?
+ subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
+ /ix
+ block_searching_flag = :subroutine
+ block_searching_lines << line
+
+ procedure_name = $2.chomp.strip
+ procedure_params = $3 || ""
+ procedure_prefix = $1 || ""
+ procedure_trailing = $4 || "!"
+ next false
+
+ # function
+ elsif line =~ /^\s*?
+ (recursive|pure|elemental)?\s*?
+ (
+ character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | type\s*?\([\w\s]+?\)\s+
+ | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | double\s+precision\s+
+ | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ )?
+ function\s+(\w+)\s*?
+ (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
+ /ix
+ block_searching_flag = :function
+ block_searching_lines << line
+
+ procedure_prefix = $1 || ""
+ procedure_type = $2 ? $2.chomp.strip : nil
+ procedure_name = $8.chomp.strip
+ procedure_params = $9 || ""
+ procedure_result_arg = $11 ? $11.chomp.strip : procedure_name
+ procedure_trailing = $12 || "!"
+ next false
+ elsif line =~ /^\s*?!\s?(.*)/
+ pre_comment << line
+ next line
+ else
+ pre_comment = []
+ next line
+ end
+ end
+ contains_flag = true if line =~ /^\s*?contains\s*?(!.*?)?$/
+ block_searching_lines << line
+ contains_lines << line if contains_flag
+
+ level_depth += 1 if block_start?(line)
+ level_depth -= 1 if block_end?(line)
+ if level_depth >= 0
+ next false
+ end
+
+ # "procedure_code" is formatted.
+ # ":nodoc:" flag is checked.
+ #
+ procedure_code = block_searching_lines.join("\n")
+ procedure_code = remove_empty_head_lines(procedure_code)
+ if procedure_trailing =~ /^!:nodoc:/
+ # next loop to search next block
+ level_depth = 0
+ block_searching_flag = nil
+ block_searching_lines = []
+ pre_comment = []
+ procedure_trailing = ""
+ procedure_name = ""
+ procedure_params = ""
+ procedure_prefix = ""
+ procedure_result_arg = ""
+ procedure_type = ""
+ contains_lines = []
+ contains_flag = nil
+ next false
+ end
+
+ # AnyMethod is created, and added to container
+ #
+ subroutine_function = nil
+ if block_searching_flag == :subroutine
+ subroutine_prefix = procedure_prefix
+ subroutine_name = procedure_name
+ subroutine_params = procedure_params
+ subroutine_trailing = procedure_trailing
+ subroutine_code = procedure_code
+
+ subroutine_comment = COMMENTS_ARE_UPPER ?
+ pre_comment.join("\n") + "\n" + subroutine_trailing :
+ subroutine_trailing + "\n" + subroutine_code.sub(/^.*$\n/i, '')
+ subroutine = AnyMethod.new("subroutine", subroutine_name)
+ parse_subprogram(subroutine, subroutine_params,
+ subroutine_comment, subroutine_code,
+ before_contains_code, nil, subroutine_prefix)
+ progress "s"
+ @stats.num_methods += 1
+ container.add_method subroutine
+ subroutine_function = subroutine
+
+ elsif block_searching_flag == :function
+ function_prefix = procedure_prefix
+ function_type = procedure_type
+ function_name = procedure_name
+ function_params_org = procedure_params
+ function_result_arg = procedure_result_arg
+ function_trailing = procedure_trailing
+ function_code_org = procedure_code
+
+ function_comment = COMMENTS_ARE_UPPER ?
+ pre_comment.join("\n") + "\n" + function_trailing :
+ function_trailing + "\n " + function_code_org.sub(/^.*$\n/i, '')
+
+ function_code = "#{function_code_org}"
+ if function_type
+ function_code << "\n" + function_type + " :: " + function_result_arg
+ end
+
+ function_params =
+ function_params_org.sub(/^\(/, "\(#{function_result_arg}, ")
+
+ function = AnyMethod.new("function", function_name)
+ parse_subprogram(function, function_params,
+ function_comment, function_code,
+ before_contains_code, true, function_prefix)
+
+ # Specific modification due to function
+ function.params.sub!(/\(\s*?#{function_result_arg}\s*?,\s*?/, "\( ")
+ function.params << " result(" + function_result_arg + ")"
+ function.start_collecting_tokens
+ function.add_token Token.new(1,1).set_text(function_code_org)
+
+ progress "f"
+ @stats.num_methods += 1
+ container.add_method function
+ subroutine_function = function
+
+ end
+
+ # The visibility of procedure is specified
+ #
+ set_visibility(container, procedure_name,
+ visibility_default, @@public_methods)
+
+ # The alias for this procedure from external modules
+ #
+ check_external_aliases(procedure_name,
+ subroutine_function.params,
+ subroutine_function.comment, subroutine_function) if external
+ check_public_methods(subroutine_function, container.name)
+
+
+ # contains_lines are parsed as private procedures
+ if contains_flag
+ parse_program_or_module(container,
+ contains_lines.join("\n"), :private)
+ end
+
+ # next loop to search next block
+ level_depth = 0
+ block_searching_flag = nil
+ block_searching_lines = []
+ pre_comment = []
+ procedure_trailing = ""
+ procedure_name = ""
+ procedure_params = ""
+ procedure_prefix = ""
+ procedure_result_arg = ""
+ contains_lines = []
+ contains_flag = nil
+ next false
+ } # End of remaining_lines.collect!{|line|
+
+ # Array remains_lines is converted to String remains_code again
+ #
+ remaining_code = remaining_lines.join("\n")
+
+ #
+ # Parse interface
+ #
+ interface_scope = false
+ generic_name = ""
+ interface_code.split("\n").each{ |line|
+ if /^\s*?
+ interface(
+ \s+\w+|
+ \s+operator\s*?\(.*?\)|
+ \s+assignment\s*?\(\s*?=\s*?\)
+ )?
+ \s*?(!.*?)?$
+ /ix =~ line
+ generic_name = $1 ? $1.strip.chomp : nil
+ interface_trailing = $2 || "!"
+ interface_scope = true
+ interface_scope = false if interface_trailing =~ /!:nodoc:/
+# if generic_name =~ /operator\s*?\((.*?)\)/i
+# operator_name = $1
+# if operator_name && !operator_name.empty?
+# generic_name = "#{operator_name}"
+# end
+# end
+# if generic_name =~ /assignment\s*?\((.*?)\)/i
+# assignment_name = $1
+# if assignment_name && !assignment_name.empty?
+# generic_name = "#{assignment_name}"
+# end
+# end
+ end
+ if /^\s*?end\s+interface/i =~ line
+ interface_scope = false
+ generic_name = nil
+ end
+ # internal alias
+ if interface_scope && /^\s*?module\s+procedure\s+(.*?)(!.*?)?$/i =~ line
+ procedures = $1.strip.chomp
+ procedures_trailing = $2 || "!"
+ next if procedures_trailing =~ /!:nodoc:/
+ procedures.split(",").each{ |proc|
+ proc.strip!
+ proc.chomp!
+ next if generic_name == proc || !generic_name
+ old_meth = container.find_symbol(proc, nil, @options.ignore_case)
+ next if !old_meth
+ nolink = old_meth.visibility == :private ? true : nil
+ nolink = nil if @options.show_all
+ new_meth =
+ initialize_external_method(generic_name, proc,
+ old_meth.params, nil,
+ old_meth.comment,
+ old_meth.clone.token_stream[0].text,
+ true, nolink)
+ new_meth.singleton = old_meth.singleton
+
+ progress "i"
+ @stats.num_methods += 1
+ container.add_method new_meth
+
+ set_visibility(container, generic_name, visibility_default, @@public_methods)
+
+ check_public_methods(new_meth, container.name)
+
+ }
+ end
+
+ # external aliases
+ if interface_scope
+ # subroutine
+ proc = nil
+ params = nil
+ procedures_trailing = nil
+ if line =~ /^\s*?
+ (recursive|pure|elemental)?\s*?
+ subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
+ /ix
+ proc = $2.chomp.strip
+ generic_name = proc unless generic_name
+ params = $3 || ""
+ procedures_trailing = $4 || "!"
+
+ # function
+ elsif line =~ /^\s*?
+ (recursive|pure|elemental)?\s*?
+ (
+ character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | type\s*?\([\w\s]+?\)\s+
+ | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | double\s+precision\s+
+ | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ )?
+ function\s+(\w+)\s*?
+ (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
+ /ix
+ proc = $8.chomp.strip
+ generic_name = proc unless generic_name
+ params = $9 || ""
+ procedures_trailing = $12 || "!"
+ else
+ next
+ end
+ next if procedures_trailing =~ /!:nodoc:/
+ indicated_method = nil
+ indicated_file = nil
+ TopLevel.all_files.each do |name, toplevel|
+ indicated_method = toplevel.find_local_symbol(proc, @options.ignore_case)
+ indicated_file = name
+ break if indicated_method
+ end
+
+ if indicated_method
+ external_method =
+ initialize_external_method(generic_name, proc,
+ indicated_method.params,
+ indicated_file,
+ indicated_method.comment)
+
+ progress "e"
+ @stats.num_methods += 1
+ container.add_method external_method
+ set_visibility(container, generic_name, visibility_default, @@public_methods)
+ if !container.include_requires?(indicated_file, @options.ignore_case)
+ container.add_require(Require.new(indicated_file, ""))
+ end
+ check_public_methods(external_method, container.name)
+
+ else
+ @@external_aliases << {
+ "new_name" => generic_name,
+ "old_name" => proc,
+ "file_or_module" => container,
+ "visibility" => find_visibility(container, generic_name, @@public_methods) || visibility_default
+ }
+ end
+ end
+
+ } if interface_code # End of interface_code.split("\n").each ...
+
+ #
+ # Already imported methods are removed from @@public_methods.
+ # Remainders are assumed to be imported from other modules.
+ #
+ @@public_methods.delete_if{ |method| method["entity_is_discovered"]}
+
+ @@public_methods.each{ |pub_meth|
+ next unless pub_meth["file_or_module"].name == container.name
+ pub_meth["used_modules"].each{ |used_mod|
+ TopLevel.all_classes_and_modules.each{ |modules|
+ if modules.name == used_mod ||
+ modules.name.upcase == used_mod.upcase &&
+ @options.ignore_case
+ modules.method_list.each{ |meth|
+ if meth.name == pub_meth["name"] ||
+ meth.name.upcase == pub_meth["name"].upcase &&
+ @options.ignore_case
+ new_meth = initialize_public_method(meth,
+ modules.name)
+ if pub_meth["local_name"]
+ new_meth.name = pub_meth["local_name"]
+ end
+ progress "e"
+ @stats.num_methods += 1
+ container.add_method new_meth
+ end
+ }
+ end
+ }
+ }
+ }
+
+ container
+ end # End of parse_program_or_module
+
+ #
+ # Parse arguments, comment, code of subroutine and function.
+ # Return AnyMethod object.
+ #
+ def parse_subprogram(subprogram, params, comment, code,
+ before_contains=nil, function=nil, prefix=nil)
+ subprogram.singleton = false
+ prefix = "" if !prefix
+ arguments = params.sub(/\(/, "").sub(/\)/, "").split(",") if params
+ args_comment, params_opt =
+ find_arguments(arguments, code.sub(/^s*?contains\s*?(!.*?)?$.*/im, ""),
+ nil, nil, true)
+ params_opt = "( " + params_opt + " ) " if params_opt
+ subprogram.params = params_opt || ""
+ namelist_comment = find_namelists(code, before_contains)
+
+ block_comment = find_comments comment
+ if function
+ subprogram.comment = "<b><em> Function </em></b> :: <em>#{prefix}</em>\n"
+ else
+ subprogram.comment = "<b><em> Subroutine </em></b> :: <em>#{prefix}</em>\n"
+ end
+ subprogram.comment << args_comment if args_comment
+ subprogram.comment << block_comment if block_comment
+ subprogram.comment << namelist_comment if namelist_comment
+
+ # For output source code
+ subprogram.start_collecting_tokens
+ subprogram.add_token Token.new(1,1).set_text(code)
+
+ subprogram
+ end
+
+ #
+ # Collect comment for file entity
+ #
+ def collect_first_comment(body)
+ comment = ""
+ not_comment = ""
+ comment_start = false
+ comment_end = false
+ body.split("\n").each{ |line|
+ if comment_end
+ not_comment << line
+ not_comment << "\n"
+ elsif /^\s*?!\s?(.*)$/i =~ line
+ comment_start = true
+ comment << $1
+ comment << "\n"
+ elsif /^\s*?$/i =~ line
+ comment_end = true if comment_start && COMMENTS_ARE_UPPER
+ else
+ comment_end = true
+ not_comment << line
+ not_comment << "\n"
+ end
+ }
+ return comment, not_comment
+ end
+
+
+ # Return comments of definitions of arguments
+ #
+ # If "all" argument is true, information of all arguments are returned.
+ # If "modified_params" is true, list of arguments are decorated,
+ # for exameple, optional arguments are parenthetic as "[arg]".
+ #
+ def find_arguments(args, text, all=nil, indent=nil, modified_params=nil)
+ return unless args || all
+ indent = "" unless indent
+ args = ["all"] if all
+ params = "" if modified_params
+ comma = ""
+ return unless text
+ args_rdocforms = "\n"
+ remaining_lines = "#{text}"
+ definitions = definition_info(remaining_lines)
+ args.each{ |arg|
+ arg.strip!
+ arg.chomp!
+ definitions.each { |defitem|
+ if arg == defitem.varname.strip.chomp || all
+ args_rdocforms << <<-"EOF"
+
+#{indent}<tt><b>#{defitem.varname.chomp.strip}#{defitem.arraysuffix}</b> #{defitem.inivalue}</tt> ::
+#{indent} <tt>#{defitem.types.chomp.strip}</tt>
+EOF
+ if !defitem.comment.chomp.strip.empty?
+ comment = ""
+ defitem.comment.split("\n").each{ |line|
+ comment << " " + line + "\n"
+ }
+ args_rdocforms << <<-"EOF"
+
+#{indent} <tt></tt> ::
+#{indent} <tt></tt>
+#{indent} #{comment.chomp.strip}
+EOF
+ end
+
+ if modified_params
+ if defitem.include_attr?("optional")
+ params << "#{comma}[#{arg}]"
+ else
+ params << "#{comma}#{arg}"
+ end
+ comma = ", "
+ end
+ end
+ }
+ }
+ if modified_params
+ return args_rdocforms, params
+ else
+ return args_rdocforms
+ end
+ end
+
+ # Return comments of definitions of namelists
+ #
+ def find_namelists(text, before_contains=nil)
+ return nil if !text
+ result = ""
+ lines = "#{text}"
+ before_contains = "" if !before_contains
+ while lines =~ /^\s*?namelist\s+\/\s*?(\w+)\s*?\/([\s\w\,]+)$/i
+ lines = $~.post_match
+ nml_comment = COMMENTS_ARE_UPPER ?
+ find_comments($~.pre_match) : find_comments($~.post_match)
+ nml_name = $1
+ nml_args = $2.split(",")
+ result << "\n\n=== NAMELIST <tt><b>" + nml_name + "</tt></b>\n\n"
+ result << nml_comment + "\n" if nml_comment
+ if lines.split("\n")[0] =~ /^\//i
+ lines = "namelist " + lines
+ end
+ result << find_arguments(nml_args, "#{text}" + "\n" + before_contains)
+ end
+ return result
+ end
+
+ #
+ # Comments just after module or subprogram, or arguments are
+ # returnd. If "COMMENTS_ARE_UPPER" is true, comments just before
+ # modules or subprograms are returnd
+ #
+ def find_comments text
+ return "" unless text
+ lines = text.split("\n")
+ lines.reverse! if COMMENTS_ARE_UPPER
+ comment_block = Array.new
+ lines.each do |line|
+ break if line =~ /^\s*?\w/ || line =~ /^\s*?$/
+ if COMMENTS_ARE_UPPER
+ comment_block.unshift line.sub(/^\s*?!\s?/,"")
+ else
+ comment_block.push line.sub(/^\s*?!\s?/,"")
+ end
+ end
+ nice_lines = comment_block.join("\n").split "\n\s*?\n"
+ nice_lines[0] ||= ""
+ nice_lines.shift
+ end
+
+ def progress(char)
+ unless @options.quiet
+ @progress.print(char)
+ @progress.flush
+ end
+ end
+
+ #
+ # Create method for internal alias
+ #
+ def initialize_public_method(method, parent)
+ return if !method || !parent
+
+ new_meth = AnyMethod.new("External Alias for module", method.name)
+ new_meth.singleton = method.singleton
+ new_meth.params = method.params.clone
+ new_meth.comment = remove_trailing_alias(method.comment.clone)
+ new_meth.comment << "\n\n#{EXTERNAL_ALIAS_MES} #{parent.strip.chomp}\##{method.name}"
+
+ return new_meth
+ end
+
+ #
+ # Create method for external alias
+ #
+ # If argument "internal" is true, file is ignored.
+ #
+ def initialize_external_method(new, old, params, file, comment, token=nil,
+ internal=nil, nolink=nil)
+ return nil unless new || old
+
+ if internal
+ external_alias_header = "#{INTERNAL_ALIAS_MES} "
+ external_alias_text = external_alias_header + old
+ elsif file
+ external_alias_header = "#{EXTERNAL_ALIAS_MES} "
+ external_alias_text = external_alias_header + file + "#" + old
+ else
+ return nil
+ end
+ external_meth = AnyMethod.new(external_alias_text, new)
+ external_meth.singleton = false
+ external_meth.params = params
+ external_comment = remove_trailing_alias(comment) + "\n\n" if comment
+ external_meth.comment = external_comment || ""
+ if nolink && token
+ external_meth.start_collecting_tokens
+ external_meth.add_token Token.new(1,1).set_text(token)
+ else
+ external_meth.comment << external_alias_text
+ end
+
+ return external_meth
+ end
+
+
+
+ #
+ # Parse visibility
+ #
+ def parse_visibility(code, default, container)
+ result = []
+ visibility_default = default || :public
+
+ used_modules = []
+ container.includes.each{|i| used_modules << i.name} if container
+
+ remaining_code = code.gsub(/^\s*?type[\s\,]+.*?\s+end\s+type.*?$/im, "")
+ remaining_code.split("\n").each{ |line|
+ if /^\s*?private\s*?$/ =~ line
+ visibility_default = :private
+ break
+ end
+ } if remaining_code
+
+ remaining_code.split("\n").each{ |line|
+ if /^\s*?private\s*?(::)?\s+(.*)\s*?(!.*?)?/i =~ line
+ methods = $2.sub(/!.*$/, '')
+ methods.split(",").each{ |meth|
+ meth.sub!(/!.*$/, '')
+ meth.gsub!(/:/, '')
+ result << {
+ "name" => meth.chomp.strip,
+ "visibility" => :private,
+ "used_modules" => used_modules.clone,
+ "file_or_module" => container,
+ "entity_is_discovered" => nil,
+ "local_name" => nil
+ }
+ }
+ elsif /^\s*?public\s*?(::)?\s+(.*)\s*?(!.*?)?/i =~ line
+ methods = $2.sub(/!.*$/, '')
+ methods.split(",").each{ |meth|
+ meth.sub!(/!.*$/, '')
+ meth.gsub!(/:/, '')
+ result << {
+ "name" => meth.chomp.strip,
+ "visibility" => :public,
+ "used_modules" => used_modules.clone,
+ "file_or_module" => container,
+ "entity_is_discovered" => nil,
+ "local_name" => nil
+ }
+ }
+ end
+ } if remaining_code
+
+ if container
+ result.each{ |vis_info|
+ vis_info["parent"] = container.name
+ }
+ end
+
+ return visibility_default, result
+ end
+
+ #
+ # Set visibility
+ #
+ # "subname" element of "visibility_info" is deleted.
+ #
+ def set_visibility(container, subname, visibility_default, visibility_info)
+ return unless container || subname || visibility_default || visibility_info
+ not_found = true
+ visibility_info.collect!{ |info|
+ if info["name"] == subname ||
+ @options.ignore_case && info["name"].upcase == subname.upcase
+ if info["file_or_module"].name == container.name
+ container.set_visibility_for([subname], info["visibility"])
+ info["entity_is_discovered"] = true
+ not_found = false
+ end
+ end
+ info
+ }
+ if not_found
+ return container.set_visibility_for([subname], visibility_default)
+ else
+ return container
+ end
+ end
+
+ #
+ # Find visibility
+ #
+ def find_visibility(container, subname, visibility_info)
+ return nil if !subname || !visibility_info
+ visibility_info.each{ |info|
+ if info["name"] == subname ||
+ @options.ignore_case && info["name"].upcase == subname.upcase
+ if info["parent"] == container.name
+ return info["visibility"]
+ end
+ end
+ }
+ return nil
+ end
+
+ #
+ # Check external aliases
+ #
+ def check_external_aliases(subname, params, comment, test=nil)
+ @@external_aliases.each{ |alias_item|
+ if subname == alias_item["old_name"] ||
+ subname.upcase == alias_item["old_name"].upcase &&
+ @options.ignore_case
+
+ new_meth = initialize_external_method(alias_item["new_name"],
+ subname, params, @file_name,
+ comment)
+ new_meth.visibility = alias_item["visibility"]
+
+ progress "e"
+ @stats.num_methods += 1
+ alias_item["file_or_module"].add_method(new_meth)
+
+ if !alias_item["file_or_module"].include_requires?(@file_name, @options.ignore_case)
+ alias_item["file_or_module"].add_require(Require.new(@file_name, ""))
+ end
+ end
+ }
+ end
+
+ #
+ # Check public_methods
+ #
+ def check_public_methods(method, parent)
+ return if !method || !parent
+ @@public_methods.each{ |alias_item|
+ parent_is_used_module = nil
+ alias_item["used_modules"].each{ |used_module|
+ if used_module == parent ||
+ used_module.upcase == parent.upcase &&
+ @options.ignore_case
+ parent_is_used_module = true
+ end
+ }
+ next if !parent_is_used_module
+
+ if method.name == alias_item["name"] ||
+ method.name.upcase == alias_item["name"].upcase &&
+ @options.ignore_case
+
+ new_meth = initialize_public_method(method, parent)
+ if alias_item["local_name"]
+ new_meth.name = alias_item["local_name"]
+ end
+
+ progress "e"
+ @stats.num_methods += 1
+ alias_item["file_or_module"].add_method new_meth
+ end
+ }
+ end
+
+ #
+ # Continuous lines are united.
+ #
+ # Comments in continuous lines are removed.
+ #
+ def united_to_one_line(f90src)
+ return "" unless f90src
+ lines = f90src.split("\n")
+ previous_continuing = false
+ now_continuing = false
+ body = ""
+ lines.each{ |line|
+ words = line.split("")
+ next if words.empty? && previous_continuing
+ commentout = false
+ brank_flag = true ; brank_char = ""
+ squote = false ; dquote = false
+ ignore = false
+ words.collect! { |char|
+ if previous_continuing && brank_flag
+ now_continuing = true
+ ignore = true
+ case char
+ when "!" ; break
+ when " " ; brank_char << char ; next ""
+ when "&"
+ brank_flag = false
+ now_continuing = false
+ next ""
+ else
+ brank_flag = false
+ now_continuing = false
+ ignore = false
+ next brank_char + char
+ end
+ end
+ ignore = false
+
+ if now_continuing
+ next ""
+ elsif !(squote) && !(dquote) && !(commentout)
+ case char
+ when "!" ; commentout = true ; next char
+ when "\""; dquote = true ; next char
+ when "\'"; squote = true ; next char
+ when "&" ; now_continuing = true ; next ""
+ else next char
+ end
+ elsif commentout
+ next char
+ elsif squote
+ case char
+ when "\'"; squote = false ; next char
+ else next char
+ end
+ elsif dquote
+ case char
+ when "\""; dquote = false ; next char
+ else next char
+ end
+ end
+ }
+ if !ignore && !previous_continuing || !brank_flag
+ if previous_continuing
+ body << words.join("")
+ else
+ body << "\n" + words.join("")
+ end
+ end
+ previous_continuing = now_continuing ? true : nil
+ now_continuing = nil
+ }
+ return body
+ end
+
+
+ #
+ # Continuous line checker
+ #
+ def continuous_line?(line)
+ continuous = false
+ if /&\s*?(!.*)?$/ =~ line
+ continuous = true
+ if comment_out?($~.pre_match)
+ continuous = false
+ end
+ end
+ return continuous
+ end
+
+ #
+ # Comment out checker
+ #
+ def comment_out?(line)
+ return nil unless line
+ commentout = false
+ squote = false ; dquote = false
+ line.split("").each { |char|
+ if !(squote) && !(dquote)
+ case char
+ when "!" ; commentout = true ; break
+ when "\""; dquote = true
+ when "\'"; squote = true
+ else next
+ end
+ elsif squote
+ case char
+ when "\'"; squote = false
+ else next
+ end
+ elsif dquote
+ case char
+ when "\""; dquote = false
+ else next
+ end
+ end
+ }
+ return commentout
+ end
+
+ #
+ # Semicolons are replaced to line feed.
+ #
+ def semicolon_to_linefeed(text)
+ return "" unless text
+ lines = text.split("\n")
+ lines.collect!{ |line|
+ words = line.split("")
+ commentout = false
+ squote = false ; dquote = false
+ words.collect! { |char|
+ if !(squote) && !(dquote) && !(commentout)
+ case char
+ when "!" ; commentout = true ; next char
+ when "\""; dquote = true ; next char
+ when "\'"; squote = true ; next char
+ when ";" ; "\n"
+ else next char
+ end
+ elsif commentout
+ next char
+ elsif squote
+ case char
+ when "\'"; squote = false ; next char
+ else next char
+ end
+ elsif dquote
+ case char
+ when "\""; dquote = false ; next char
+ else next char
+ end
+ end
+ }
+ words.join("")
+ }
+ return lines.join("\n")
+ end
+
+ #
+ # Which "line" is start of block (module, program, block data,
+ # subroutine, function) statement ?
+ #
+ def block_start?(line)
+ return nil if !line
+
+ if line =~ /^\s*?module\s+(\w+)\s*?(!.*?)?$/i ||
+ line =~ /^\s*?program\s+(\w+)\s*?(!.*?)?$/i ||
+ line =~ /^\s*?block\s+data(\s+\w+)?\s*?(!.*?)?$/i ||
+ line =~ \
+ /^\s*?
+ (recursive|pure|elemental)?\s*?
+ subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$
+ /ix ||
+ line =~ \
+ /^\s*?
+ (recursive|pure|elemental)?\s*?
+ (
+ character\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | type\s*?\([\w\s]+?\)\s+
+ | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | double\s+precision\s+
+ | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+
+ )?
+ function\s+(\w+)\s*?
+ (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$
+ /ix
+ return true
+ end
+
+ return nil
+ end
+
+ #
+ # Which "line" is end of block (module, program, block data,
+ # subroutine, function) statement ?
+ #
+ def block_end?(line)
+ return nil if !line
+
+ if line =~ /^\s*?end\s*?(!.*?)?$/i ||
+ line =~ /^\s*?end\s+module(\s+\w+)?\s*?(!.*?)?$/i ||
+ line =~ /^\s*?end\s+program(\s+\w+)?\s*?(!.*?)?$/i ||
+ line =~ /^\s*?end\s+block\s+data(\s+\w+)?\s*?(!.*?)?$/i ||
+ line =~ /^\s*?end\s+subroutine(\s+\w+)?\s*?(!.*?)?$/i ||
+ line =~ /^\s*?end\s+function(\s+\w+)?\s*?(!.*?)?$/i
+ return true
+ end
+
+ return nil
+ end
+
+ #
+ # Remove "Alias for" in end of comments
+ #
+ def remove_trailing_alias(text)
+ return "" if !text
+ lines = text.split("\n").reverse
+ comment_block = Array.new
+ checked = false
+ lines.each do |line|
+ if !checked
+ if /^\s?#{INTERNAL_ALIAS_MES}/ =~ line ||
+ /^\s?#{EXTERNAL_ALIAS_MES}/ =~ line
+ checked = true
+ next
+ end
+ end
+ comment_block.unshift line
+ end
+ nice_lines = comment_block.join("\n")
+ nice_lines ||= ""
+ return nice_lines
+ end
+
+ # Empty lines in header are removed
+ def remove_empty_head_lines(text)
+ return "" unless text
+ lines = text.split("\n")
+ header = true
+ lines.delete_if{ |line|
+ header = false if /\S/ =~ line
+ header && /^\s*?$/ =~ line
+ }
+ lines.join("\n")
+ end
+
+
+ # header marker "=", "==", ... are removed
+ def remove_header_marker(text)
+ return text.gsub(/^\s?(=+)/, '<tt></tt>\1')
+ end
+
+ def remove_private_comments(body)
+ body.gsub!(/^\s*!--\s*?$.*?^\s*!\+\+\s*?$/m, '')
+ return body
+ end
+
+
+ #
+ # Information of arguments of subroutines and functions in Fortran95
+ #
+ class Fortran95Definition
+
+ # Name of variable
+ #
+ attr_reader :varname
+
+ # Types of variable
+ #
+ attr_reader :types
+
+ # Initial Value
+ #
+ attr_reader :inivalue
+
+ # Suffix of array
+ #
+ attr_reader :arraysuffix
+
+ # Comments
+ #
+ attr_accessor :comment
+
+ # Flag of non documentation
+ #
+ attr_accessor :nodoc
+
+ def initialize(varname, types, inivalue, arraysuffix, comment,
+ nodoc=false)
+ @varname = varname
+ @types = types
+ @inivalue = inivalue
+ @arraysuffix = arraysuffix
+ @comment = comment
+ @nodoc = nodoc
+ end
+
+ def to_s
+ return <<-EOF
+<Fortran95Definition:
+ varname=#{@varname}, types=#{types},
+ inivalue=#{@inivalue}, arraysuffix=#{@arraysuffix}, nodoc=#{@nodoc},
+ comment=
+#{@comment}
+>
+EOF
+ end
+
+ #
+ # If attr is included, true is returned
+ #
+ def include_attr?(attr)
+ return if !attr
+ @types.split(",").each{ |type|
+ return true if type.strip.chomp.upcase == attr.strip.chomp.upcase
+ }
+ return nil
+ end
+
+ end # End of Fortran95Definition
+
+ #
+ # Parse string argument "text", and Return Array of
+ # Fortran95Definition object
+ #
+ def definition_info(text)
+ return nil unless text
+ lines = "#{text}"
+ defs = Array.new
+ comment = ""
+ trailing_comment = ""
+ under_comment_valid = false
+ lines.split("\n").each{ |line|
+ if /^\s*?!\s?(.*)/ =~ line
+ if COMMENTS_ARE_UPPER
+ comment << remove_header_marker($1)
+ comment << "\n"
+ elsif defs[-1] && under_comment_valid
+ defs[-1].comment << "\n"
+ defs[-1].comment << remove_header_marker($1)
+ end
+ next
+ elsif /^\s*?$/ =~ line
+ comment = ""
+ under_comment_valid = false
+ next
+ end
+ type = ""
+ characters = ""
+ if line =~ /^\s*?
+ (
+ character\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
+ | type\s*?\([\w\s]+?\)[\s\,]*
+ | integer\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
+ | real\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
+ | double\s+precision[\s\,]*
+ | logical\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
+ | complex\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]*
+ )
+ (.*?::)?
+ (.+)$
+ /ix
+ characters = $8
+ type = $1
+ type << $7.gsub(/::/, '').gsub(/^\s*?\,/, '') if $7
+ else
+ under_comment_valid = false
+ next
+ end
+ squote = false ; dquote = false ; bracket = 0
+ iniflag = false; commentflag = false
+ varname = "" ; arraysuffix = "" ; inivalue = ""
+ start_pos = defs.size
+ characters.split("").each { |char|
+ if !(squote) && !(dquote) && bracket <= 0 && !(iniflag) && !(commentflag)
+ case char
+ when "!" ; commentflag = true
+ when "(" ; bracket += 1 ; arraysuffix = char
+ when "\""; dquote = true
+ when "\'"; squote = true
+ when "=" ; iniflag = true ; inivalue << char
+ when ","
+ defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
+ varname = "" ; arraysuffix = "" ; inivalue = ""
+ under_comment_valid = true
+ when " " ; next
+ else ; varname << char
+ end
+ elsif commentflag
+ comment << remove_header_marker(char)
+ trailing_comment << remove_header_marker(char)
+ elsif iniflag
+ if dquote
+ case char
+ when "\"" ; dquote = false ; inivalue << char
+ else ; inivalue << char
+ end
+ elsif squote
+ case char
+ when "\'" ; squote = false ; inivalue << char
+ else ; inivalue << char
+ end
+ elsif bracket > 0
+ case char
+ when "(" ; bracket += 1 ; inivalue << char
+ when ")" ; bracket -= 1 ; inivalue << char
+ else ; inivalue << char
+ end
+ else
+ case char
+ when ","
+ defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
+ varname = "" ; arraysuffix = "" ; inivalue = ""
+ iniflag = false
+ under_comment_valid = true
+ when "(" ; bracket += 1 ; inivalue << char
+ when "\""; dquote = true ; inivalue << char
+ when "\'"; squote = true ; inivalue << char
+ when "!" ; commentflag = true
+ else ; inivalue << char
+ end
+ end
+ elsif !(squote) && !(dquote) && bracket > 0
+ case char
+ when "(" ; bracket += 1 ; arraysuffix << char
+ when ")" ; bracket -= 1 ; arraysuffix << char
+ else ; arraysuffix << char
+ end
+ elsif squote
+ case char
+ when "\'"; squote = false ; inivalue << char
+ else ; inivalue << char
+ end
+ elsif dquote
+ case char
+ when "\""; dquote = false ; inivalue << char
+ else ; inivalue << char
+ end
+ end
+ }
+ defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment)
+ if trailing_comment =~ /^:nodoc:/
+ defs[start_pos..-1].collect!{ |defitem|
+ defitem.nodoc = true
+ }
+ end
+ varname = "" ; arraysuffix = "" ; inivalue = ""
+ comment = ""
+ under_comment_valid = true
+ trailing_comment = ""
+ }
+ return defs
+ end
+
+
+ end # class Fortran95parser
+
+end # module RDoc
diff --git a/ruby_1_8_5/lib/rdoc/parsers/parse_rb.rb b/ruby_1_8_5/lib/rdoc/parsers/parse_rb.rb
new file mode 100644
index 0000000000..dde017be7d
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/parsers/parse_rb.rb
@@ -0,0 +1,2605 @@
+#!/usr/local/bin/ruby
+
+# Parse a Ruby source file, building a set of objects
+# representing the modules, classes, methods,
+# requires, and includes we find (these classes
+# are defined in code_objects.rb).
+
+# This file contains stuff stolen outright from:
+#
+# rtags.rb -
+# ruby-lex.rb - ruby lexcal analizer
+# ruby-token.rb - ruby tokens
+# by Keiju ISHITSUKA (Nippon Rational Inc.)
+#
+
+require "e2mmap"
+require "irb/slex"
+
+require "rdoc/code_objects"
+require "rdoc/tokenstream"
+
+require "rdoc/markup/simple_markup/preprocess"
+
+require "rdoc/parsers/parserfactory"
+
+$TOKEN_DEBUG = $DEBUG
+
+# Definitions of all tokens involved in the lexical analysis
+
+module RubyToken
+ EXPR_BEG = :EXPR_BEG
+ EXPR_MID = :EXPR_MID
+ EXPR_END = :EXPR_END
+ EXPR_ARG = :EXPR_ARG
+ EXPR_FNAME = :EXPR_FNAME
+ EXPR_DOT = :EXPR_DOT
+ EXPR_CLASS = :EXPR_CLASS
+
+ class Token
+ NO_TEXT = "??".freeze
+ attr :text
+
+ def initialize(line_no, char_no)
+ @line_no = line_no
+ @char_no = char_no
+ @text = NO_TEXT
+ end
+
+ # Because we're used in contexts that expect to return a token,
+ # we set the text string and then return ourselves
+ def set_text(text)
+ @text = text
+ self
+ end
+
+ attr_reader :line_no, :char_no, :text
+ end
+
+ class TkNode < Token
+ attr :node
+ end
+
+ class TkId < Token
+ def initialize(line_no, char_no, name)
+ super(line_no, char_no)
+ @name = name
+ end
+ attr :name
+ end
+
+ class TkKW < TkId
+ end
+
+ class TkVal < Token
+ def initialize(line_no, char_no, value = nil)
+ super(line_no, char_no)
+ set_text(value)
+ end
+ end
+
+ class TkOp < Token
+ def name
+ self.class.op_name
+ end
+ end
+
+ class TkOPASGN < TkOp
+ def initialize(line_no, char_no, op)
+ super(line_no, char_no)
+ op = TkReading2Token[op] unless op.kind_of?(Symbol)
+ @op = op
+ end
+ attr :op
+ end
+
+ class TkUnknownChar < Token
+ def initialize(line_no, char_no, id)
+ super(line_no, char_no)
+ @name = char_no.chr
+ end
+ attr :name
+ end
+
+ class TkError < Token
+ end
+
+ def set_token_position(line, char)
+ @prev_line_no = line
+ @prev_char_no = char
+ end
+
+ def Token(token, value = nil)
+ tk = nil
+ case token
+ when String, Symbol
+ source = token.kind_of?(String) ? TkReading2Token : TkSymbol2Token
+ if (tk = source[token]).nil?
+ IRB.fail TkReading2TokenNoKey, token
+ end
+ tk = Token(tk[0], value)
+ else
+ tk = if (token.ancestors & [TkId, TkVal, TkOPASGN, TkUnknownChar]).empty?
+ token.new(@prev_line_no, @prev_char_no)
+ else
+ token.new(@prev_line_no, @prev_char_no, value)
+ end
+ end
+ tk
+ end
+
+ TokenDefinitions = [
+ [:TkCLASS, TkKW, "class", EXPR_CLASS],
+ [:TkMODULE, TkKW, "module", EXPR_BEG],
+ [:TkDEF, TkKW, "def", EXPR_FNAME],
+ [:TkUNDEF, TkKW, "undef", EXPR_FNAME],
+ [:TkBEGIN, TkKW, "begin", EXPR_BEG],
+ [:TkRESCUE, TkKW, "rescue", EXPR_MID],
+ [:TkENSURE, TkKW, "ensure", EXPR_BEG],
+ [:TkEND, TkKW, "end", EXPR_END],
+ [:TkIF, TkKW, "if", EXPR_BEG, :TkIF_MOD],
+ [:TkUNLESS, TkKW, "unless", EXPR_BEG, :TkUNLESS_MOD],
+ [:TkTHEN, TkKW, "then", EXPR_BEG],
+ [:TkELSIF, TkKW, "elsif", EXPR_BEG],
+ [:TkELSE, TkKW, "else", EXPR_BEG],
+ [:TkCASE, TkKW, "case", EXPR_BEG],
+ [:TkWHEN, TkKW, "when", EXPR_BEG],
+ [:TkWHILE, TkKW, "while", EXPR_BEG, :TkWHILE_MOD],
+ [:TkUNTIL, TkKW, "until", EXPR_BEG, :TkUNTIL_MOD],
+ [:TkFOR, TkKW, "for", EXPR_BEG],
+ [:TkBREAK, TkKW, "break", EXPR_END],
+ [:TkNEXT, TkKW, "next", EXPR_END],
+ [:TkREDO, TkKW, "redo", EXPR_END],
+ [:TkRETRY, TkKW, "retry", EXPR_END],
+ [:TkIN, TkKW, "in", EXPR_BEG],
+ [:TkDO, TkKW, "do", EXPR_BEG],
+ [:TkRETURN, TkKW, "return", EXPR_MID],
+ [:TkYIELD, TkKW, "yield", EXPR_END],
+ [:TkSUPER, TkKW, "super", EXPR_END],
+ [:TkSELF, TkKW, "self", EXPR_END],
+ [:TkNIL, TkKW, "nil", EXPR_END],
+ [:TkTRUE, TkKW, "true", EXPR_END],
+ [:TkFALSE, TkKW, "false", EXPR_END],
+ [:TkAND, TkKW, "and", EXPR_BEG],
+ [:TkOR, TkKW, "or", EXPR_BEG],
+ [:TkNOT, TkKW, "not", EXPR_BEG],
+ [:TkIF_MOD, TkKW],
+ [:TkUNLESS_MOD, TkKW],
+ [:TkWHILE_MOD, TkKW],
+ [:TkUNTIL_MOD, TkKW],
+ [:TkALIAS, TkKW, "alias", EXPR_FNAME],
+ [:TkDEFINED, TkKW, "defined?", EXPR_END],
+ [:TklBEGIN, TkKW, "BEGIN", EXPR_END],
+ [:TklEND, TkKW, "END", EXPR_END],
+ [:Tk__LINE__, TkKW, "__LINE__", EXPR_END],
+ [:Tk__FILE__, TkKW, "__FILE__", EXPR_END],
+
+ [:TkIDENTIFIER, TkId],
+ [:TkFID, TkId],
+ [:TkGVAR, TkId],
+ [:TkIVAR, TkId],
+ [:TkCONSTANT, TkId],
+
+ [:TkINTEGER, TkVal],
+ [:TkFLOAT, TkVal],
+ [:TkSTRING, TkVal],
+ [:TkXSTRING, TkVal],
+ [:TkREGEXP, TkVal],
+ [:TkCOMMENT, TkVal],
+
+ [:TkDSTRING, TkNode],
+ [:TkDXSTRING, TkNode],
+ [:TkDREGEXP, TkNode],
+ [:TkNTH_REF, TkId],
+ [:TkBACK_REF, TkId],
+
+ [:TkUPLUS, TkOp, "+@"],
+ [:TkUMINUS, TkOp, "-@"],
+ [:TkPOW, TkOp, "**"],
+ [:TkCMP, TkOp, "<=>"],
+ [:TkEQ, TkOp, "=="],
+ [:TkEQQ, TkOp, "==="],
+ [:TkNEQ, TkOp, "!="],
+ [:TkGEQ, TkOp, ">="],
+ [:TkLEQ, TkOp, "<="],
+ [:TkANDOP, TkOp, "&&"],
+ [:TkOROP, TkOp, "||"],
+ [:TkMATCH, TkOp, "=~"],
+ [:TkNMATCH, TkOp, "!~"],
+ [:TkDOT2, TkOp, ".."],
+ [:TkDOT3, TkOp, "..."],
+ [:TkAREF, TkOp, "[]"],
+ [:TkASET, TkOp, "[]="],
+ [:TkLSHFT, TkOp, "<<"],
+ [:TkRSHFT, TkOp, ">>"],
+ [:TkCOLON2, TkOp],
+ [:TkCOLON3, TkOp],
+# [:OPASGN, TkOp], # +=, -= etc. #
+ [:TkASSOC, TkOp, "=>"],
+ [:TkQUESTION, TkOp, "?"], #?
+ [:TkCOLON, TkOp, ":"], #:
+
+ [:TkfLPAREN], # func( #
+ [:TkfLBRACK], # func[ #
+ [:TkfLBRACE], # func{ #
+ [:TkSTAR], # *arg
+ [:TkAMPER], # &arg #
+ [:TkSYMBOL, TkId], # :SYMBOL
+ [:TkSYMBEG, TkId],
+ [:TkGT, TkOp, ">"],
+ [:TkLT, TkOp, "<"],
+ [:TkPLUS, TkOp, "+"],
+ [:TkMINUS, TkOp, "-"],
+ [:TkMULT, TkOp, "*"],
+ [:TkDIV, TkOp, "/"],
+ [:TkMOD, TkOp, "%"],
+ [:TkBITOR, TkOp, "|"],
+ [:TkBITXOR, TkOp, "^"],
+ [:TkBITAND, TkOp, "&"],
+ [:TkBITNOT, TkOp, "~"],
+ [:TkNOTOP, TkOp, "!"],
+
+ [:TkBACKQUOTE, TkOp, "`"],
+
+ [:TkASSIGN, Token, "="],
+ [:TkDOT, Token, "."],
+ [:TkLPAREN, Token, "("], #(exp)
+ [:TkLBRACK, Token, "["], #[arry]
+ [:TkLBRACE, Token, "{"], #{hash}
+ [:TkRPAREN, Token, ")"],
+ [:TkRBRACK, Token, "]"],
+ [:TkRBRACE, Token, "}"],
+ [:TkCOMMA, Token, ","],
+ [:TkSEMICOLON, Token, ";"],
+
+ [:TkRD_COMMENT],
+ [:TkSPACE],
+ [:TkNL],
+ [:TkEND_OF_SCRIPT],
+
+ [:TkBACKSLASH, TkUnknownChar, "\\"],
+ [:TkAT, TkUnknownChar, "@"],
+ [:TkDOLLAR, TkUnknownChar, "\$"], #"
+ ]
+
+ # {reading => token_class}
+ # {reading => [token_class, *opt]}
+ TkReading2Token = {}
+ TkSymbol2Token = {}
+
+ def RubyToken.def_token(token_n, super_token = Token, reading = nil, *opts)
+ token_n = token_n.id2name unless token_n.kind_of?(String)
+ if RubyToken.const_defined?(token_n)
+ IRB.fail AlreadyDefinedToken, token_n
+ end
+
+ token_c = Class.new super_token
+ RubyToken.const_set token_n, token_c
+# token_c.inspect
+
+ if reading
+ if TkReading2Token[reading]
+ IRB.fail TkReading2TokenDuplicateError, token_n, reading
+ end
+ if opts.empty?
+ TkReading2Token[reading] = [token_c]
+ else
+ TkReading2Token[reading] = [token_c].concat(opts)
+ end
+ end
+ TkSymbol2Token[token_n.intern] = token_c
+
+ if token_c <= TkOp
+ token_c.class_eval %{
+ def self.op_name; "#{reading}"; end
+ }
+ end
+ end
+
+ for defs in TokenDefinitions
+ def_token(*defs)
+ end
+
+ NEWLINE_TOKEN = TkNL.new(0,0)
+ NEWLINE_TOKEN.set_text("\n")
+
+end
+
+
+
+# Lexical analyzer for Ruby source
+
+class RubyLex
+
+ ######################################################################
+ #
+ # Read an input stream character by character. We allow for unlimited
+ # ungetting of characters just read.
+ #
+ # We simplify the implementation greatly by reading the entire input
+ # into a buffer initially, and then simply traversing it using
+ # pointers.
+ #
+ # We also have to allow for the <i>here document diversion</i>. This
+ # little gem comes about when the lexer encounters a here
+ # document. At this point we effectively need to split the input
+ # stream into two parts: one to read the body of the here document,
+ # the other to read the rest of the input line where the here
+ # document was initially encountered. For example, we might have
+ #
+ # do_something(<<-A, <<-B)
+ # stuff
+ # for
+ # A
+ # stuff
+ # for
+ # B
+ #
+ # When the lexer encounters the <<A, it reads until the end of the
+ # line, and keeps it around for later. It then reads the body of the
+ # here document. Once complete, it needs to read the rest of the
+ # original line, but then skip the here document body.
+ #
+
+ class BufferedReader
+
+ attr_reader :line_num
+
+ def initialize(content)
+ if /\t/ =~ content
+ tab_width = Options.instance.tab_width
+ content = content.split(/\n/).map do |line|
+ 1 while line.gsub!(/\t+/) { ' ' * (tab_width*$&.length - $`.length % tab_width)} && $~ #`
+ line
+ end .join("\n")
+ end
+ @content = content
+ @content << "\n" unless @content[-1,1] == "\n"
+ @size = @content.size
+ @offset = 0
+ @hwm = 0
+ @line_num = 1
+ @read_back_offset = 0
+ @last_newline = 0
+ @newline_pending = false
+ end
+
+ def column
+ @offset - @last_newline
+ end
+
+ def getc
+ return nil if @offset >= @size
+ ch = @content[@offset, 1]
+
+ @offset += 1
+ @hwm = @offset if @hwm < @offset
+
+ if @newline_pending
+ @line_num += 1
+ @last_newline = @offset - 1
+ @newline_pending = false
+ end
+
+ if ch == "\n"
+ @newline_pending = true
+ end
+ ch
+ end
+
+ def getc_already_read
+ getc
+ end
+
+ def ungetc(ch)
+ raise "unget past beginning of file" if @offset <= 0
+ @offset -= 1
+ if @content[@offset] == ?\n
+ @newline_pending = false
+ end
+ end
+
+ def get_read
+ res = @content[@read_back_offset...@offset]
+ @read_back_offset = @offset
+ res
+ end
+
+ def peek(at)
+ pos = @offset + at
+ if pos >= @size
+ nil
+ else
+ @content[pos, 1]
+ end
+ end
+
+ def peek_equal(str)
+ @content[@offset, str.length] == str
+ end
+
+ def divert_read_from(reserve)
+ @content[@offset, 0] = reserve
+ @size = @content.size
+ end
+ end
+
+ # end of nested class BufferedReader
+
+ extend Exception2MessageMapper
+ def_exception(:AlreadyDefinedToken, "Already defined token(%s)")
+ def_exception(:TkReading2TokenNoKey, "key nothing(key='%s')")
+ def_exception(:TkSymbol2TokenNoKey, "key nothing(key='%s')")
+ def_exception(:TkReading2TokenDuplicateError,
+ "key duplicate(token_n='%s', key='%s')")
+ def_exception(:SyntaxError, "%s")
+
+ include RubyToken
+ include IRB
+
+ attr_reader :continue
+ attr_reader :lex_state
+
+ def RubyLex.debug?
+ false
+ end
+
+ def initialize(content)
+ lex_init
+
+ @reader = BufferedReader.new(content)
+
+ @exp_line_no = @line_no = 1
+ @base_char_no = 0
+ @indent = 0
+
+ @ltype = nil
+ @quoted = nil
+ @lex_state = EXPR_BEG
+ @space_seen = false
+
+ @continue = false
+ @line = ""
+
+ @skip_space = false
+ @read_auto_clean_up = false
+ @exception_on_syntax_error = true
+ end
+
+ attr :skip_space, true
+ attr :read_auto_clean_up, true
+ attr :exception_on_syntax_error, true
+
+ attr :indent
+
+ # io functions
+ def line_no
+ @reader.line_num
+ end
+
+ def char_no
+ @reader.column
+ end
+
+ def get_read
+ @reader.get_read
+ end
+
+ def getc
+ @reader.getc
+ end
+
+ def getc_of_rests
+ @reader.getc_already_read
+ end
+
+ def gets
+ c = getc or return
+ l = ""
+ begin
+ l.concat c unless c == "\r"
+ break if c == "\n"
+ end while c = getc
+ l
+ end
+
+
+ def ungetc(c = nil)
+ @reader.ungetc(c)
+ end
+
+ def peek_equal?(str)
+ @reader.peek_equal(str)
+ end
+
+ def peek(i = 0)
+ @reader.peek(i)
+ end
+
+ def lex
+ until (((tk = token).kind_of?(TkNL) || tk.kind_of?(TkEND_OF_SCRIPT)) &&
+ !@continue or
+ tk.nil?)
+ end
+ line = get_read
+
+ if line == "" and tk.kind_of?(TkEND_OF_SCRIPT) || tk.nil?
+ nil
+ else
+ line
+ end
+ end
+
+ def token
+ set_token_position(line_no, char_no)
+ begin
+ begin
+ tk = @OP.match(self)
+ @space_seen = tk.kind_of?(TkSPACE)
+ rescue SyntaxError
+ abort if @exception_on_syntax_error
+ tk = TkError.new(line_no, char_no)
+ end
+ end while @skip_space and tk.kind_of?(TkSPACE)
+ if @read_auto_clean_up
+ get_read
+ end
+# throw :eof unless tk
+ p tk if $DEBUG
+ tk
+ end
+
+ ENINDENT_CLAUSE = [
+ "case", "class", "def", "do", "for", "if",
+ "module", "unless", "until", "while", "begin" #, "when"
+ ]
+ DEINDENT_CLAUSE = ["end" #, "when"
+ ]
+
+ PERCENT_LTYPE = {
+ "q" => "\'",
+ "Q" => "\"",
+ "x" => "\`",
+ "r" => "/",
+ "w" => "]"
+ }
+
+ PERCENT_PAREN = {
+ "{" => "}",
+ "[" => "]",
+ "<" => ">",
+ "(" => ")"
+ }
+
+ Ltype2Token = {
+ "\'" => TkSTRING,
+ "\"" => TkSTRING,
+ "\`" => TkXSTRING,
+ "/" => TkREGEXP,
+ "]" => TkDSTRING
+ }
+ Ltype2Token.default = TkSTRING
+
+ DLtype2Token = {
+ "\"" => TkDSTRING,
+ "\`" => TkDXSTRING,
+ "/" => TkDREGEXP,
+ }
+
+ def lex_init()
+ @OP = SLex.new
+ @OP.def_rules("\0", "\004", "\032") do |chars, io|
+ Token(TkEND_OF_SCRIPT).set_text(chars)
+ end
+
+ @OP.def_rules(" ", "\t", "\f", "\r", "\13") do |chars, io|
+ @space_seen = TRUE
+ while (ch = getc) =~ /[ \t\f\r\13]/
+ chars << ch
+ end
+ ungetc
+ Token(TkSPACE).set_text(chars)
+ end
+
+ @OP.def_rule("#") do
+ |op, io|
+ identify_comment
+ end
+
+ @OP.def_rule("=begin", proc{@prev_char_no == 0 && peek(0) =~ /\s/}) do
+ |op, io|
+ str = op
+ @ltype = "="
+
+
+ begin
+ line = ""
+ begin
+ ch = getc
+ line << ch
+ end until ch == "\n"
+ str << line
+ end until line =~ /^=end/
+
+ ungetc
+
+ @ltype = nil
+
+ if str =~ /\A=begin\s+rdoc/i
+ str.sub!(/\A=begin.*\n/, '')
+ str.sub!(/^=end.*/m, '')
+ Token(TkCOMMENT).set_text(str)
+ else
+ Token(TkRD_COMMENT)#.set_text(str)
+ end
+ end
+
+ @OP.def_rule("\n") do
+ print "\\n\n" if RubyLex.debug?
+ case @lex_state
+ when EXPR_BEG, EXPR_FNAME, EXPR_DOT
+ @continue = TRUE
+ else
+ @continue = FALSE
+ @lex_state = EXPR_BEG
+ end
+ Token(TkNL).set_text("\n")
+ end
+
+ @OP.def_rules("*", "**",
+ "!", "!=", "!~",
+ "=", "==", "===",
+ "=~", "<=>",
+ "<", "<=",
+ ">", ">=", ">>") do
+ |op, io|
+ @lex_state = EXPR_BEG
+ Token(op).set_text(op)
+ end
+
+ @OP.def_rules("<<") do
+ |op, io|
+ tk = nil
+ if @lex_state != EXPR_END && @lex_state != EXPR_CLASS &&
+ (@lex_state != EXPR_ARG || @space_seen)
+ c = peek(0)
+ if /[-\w_\"\'\`]/ =~ c
+ tk = identify_here_document
+ end
+ end
+ if !tk
+ @lex_state = EXPR_BEG
+ tk = Token(op).set_text(op)
+ end
+ tk
+ end
+
+ @OP.def_rules("'", '"') do
+ |op, io|
+ identify_string(op)
+ end
+
+ @OP.def_rules("`") do
+ |op, io|
+ if @lex_state == EXPR_FNAME
+ Token(op).set_text(op)
+ else
+ identify_string(op)
+ end
+ end
+
+ @OP.def_rules('?') do
+ |op, io|
+ if @lex_state == EXPR_END
+ @lex_state = EXPR_BEG
+ Token(TkQUESTION).set_text(op)
+ else
+ ch = getc
+ if @lex_state == EXPR_ARG && ch !~ /\s/
+ ungetc
+ @lex_state = EXPR_BEG;
+ Token(TkQUESTION).set_text(op)
+ else
+ str = op
+ str << ch
+ if (ch == '\\') #'
+ str << read_escape
+ end
+ @lex_state = EXPR_END
+ Token(TkINTEGER).set_text(str)
+ end
+ end
+ end
+
+ @OP.def_rules("&", "&&", "|", "||") do
+ |op, io|
+ @lex_state = EXPR_BEG
+ Token(op).set_text(op)
+ end
+
+ @OP.def_rules("+=", "-=", "*=", "**=",
+ "&=", "|=", "^=", "<<=", ">>=", "||=", "&&=") do
+ |op, io|
+ @lex_state = EXPR_BEG
+ op =~ /^(.*)=$/
+ Token(TkOPASGN, $1).set_text(op)
+ end
+
+ @OP.def_rule("+@", proc{@lex_state == EXPR_FNAME}) do |op, io|
+ Token(TkUPLUS).set_text(op)
+ end
+
+ @OP.def_rule("-@", proc{@lex_state == EXPR_FNAME}) do |op, io|
+ Token(TkUMINUS).set_text(op)
+ end
+
+ @OP.def_rules("+", "-") do
+ |op, io|
+ catch(:RET) do
+ if @lex_state == EXPR_ARG
+ if @space_seen and peek(0) =~ /[0-9]/
+ throw :RET, identify_number(op)
+ else
+ @lex_state = EXPR_BEG
+ end
+ elsif @lex_state != EXPR_END and peek(0) =~ /[0-9]/
+ throw :RET, identify_number(op)
+ else
+ @lex_state = EXPR_BEG
+ end
+ Token(op).set_text(op)
+ end
+ end
+
+ @OP.def_rule(".") do
+ @lex_state = EXPR_BEG
+ if peek(0) =~ /[0-9]/
+ ungetc
+ identify_number("")
+ else
+ # for obj.if
+ @lex_state = EXPR_DOT
+ Token(TkDOT).set_text(".")
+ end
+ end
+
+ @OP.def_rules("..", "...") do
+ |op, io|
+ @lex_state = EXPR_BEG
+ Token(op).set_text(op)
+ end
+
+ lex_int2
+ end
+
+ def lex_int2
+ @OP.def_rules("]", "}", ")") do
+ |op, io|
+ @lex_state = EXPR_END
+ @indent -= 1
+ Token(op).set_text(op)
+ end
+
+ @OP.def_rule(":") do
+ if @lex_state == EXPR_END || peek(0) =~ /\s/
+ @lex_state = EXPR_BEG
+ tk = Token(TkCOLON)
+ else
+ @lex_state = EXPR_FNAME;
+ tk = Token(TkSYMBEG)
+ end
+ tk.set_text(":")
+ end
+
+ @OP.def_rule("::") do
+# p @lex_state.id2name, @space_seen
+ if @lex_state == EXPR_BEG or @lex_state == EXPR_ARG && @space_seen
+ @lex_state = EXPR_BEG
+ tk = Token(TkCOLON3)
+ else
+ @lex_state = EXPR_DOT
+ tk = Token(TkCOLON2)
+ end
+ tk.set_text("::")
+ end
+
+ @OP.def_rule("/") do
+ |op, io|
+ if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
+ identify_string(op)
+ elsif peek(0) == '='
+ getc
+ @lex_state = EXPR_BEG
+ Token(TkOPASGN, :/).set_text("/=") #")
+ elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/
+ identify_string(op)
+ else
+ @lex_state = EXPR_BEG
+ Token("/").set_text(op)
+ end
+ end
+
+ @OP.def_rules("^") do
+ @lex_state = EXPR_BEG
+ Token("^").set_text("^")
+ end
+
+ # @OP.def_rules("^=") do
+ # @lex_state = EXPR_BEG
+ # Token(TkOPASGN, :^)
+ # end
+
+ @OP.def_rules(",", ";") do
+ |op, io|
+ @lex_state = EXPR_BEG
+ Token(op).set_text(op)
+ end
+
+ @OP.def_rule("~") do
+ @lex_state = EXPR_BEG
+ Token("~").set_text("~")
+ end
+
+ @OP.def_rule("~@", proc{@lex_state = EXPR_FNAME}) do
+ @lex_state = EXPR_BEG
+ Token("~").set_text("~@")
+ end
+
+ @OP.def_rule("(") do
+ @indent += 1
+ if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
+ @lex_state = EXPR_BEG
+ tk = Token(TkfLPAREN)
+ else
+ @lex_state = EXPR_BEG
+ tk = Token(TkLPAREN)
+ end
+ tk.set_text("(")
+ end
+
+ @OP.def_rule("[]", proc{@lex_state == EXPR_FNAME}) do
+ Token("[]").set_text("[]")
+ end
+
+ @OP.def_rule("[]=", proc{@lex_state == EXPR_FNAME}) do
+ Token("[]=").set_text("[]=")
+ end
+
+ @OP.def_rule("[") do
+ @indent += 1
+ if @lex_state == EXPR_FNAME
+ t = Token(TkfLBRACK)
+ else
+ if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
+ t = Token(TkLBRACK)
+ elsif @lex_state == EXPR_ARG && @space_seen
+ t = Token(TkLBRACK)
+ else
+ t = Token(TkfLBRACK)
+ end
+ @lex_state = EXPR_BEG
+ end
+ t.set_text("[")
+ end
+
+ @OP.def_rule("{") do
+ @indent += 1
+ if @lex_state != EXPR_END && @lex_state != EXPR_ARG
+ t = Token(TkLBRACE)
+ else
+ t = Token(TkfLBRACE)
+ end
+ @lex_state = EXPR_BEG
+ t.set_text("{")
+ end
+
+ @OP.def_rule('\\') do #'
+ if getc == "\n"
+ @space_seen = true
+ @continue = true
+ Token(TkSPACE).set_text("\\\n")
+ else
+ ungetc
+ Token("\\").set_text("\\") #"
+ end
+ end
+
+ @OP.def_rule('%') do
+ |op, io|
+ if @lex_state == EXPR_BEG || @lex_state == EXPR_MID
+ identify_quotation('%')
+ elsif peek(0) == '='
+ getc
+ Token(TkOPASGN, "%").set_text("%=")
+ elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/
+ identify_quotation('%')
+ else
+ @lex_state = EXPR_BEG
+ Token("%").set_text("%")
+ end
+ end
+
+ @OP.def_rule('$') do #'
+ identify_gvar
+ end
+
+ @OP.def_rule('@') do
+ if peek(0) =~ /[@\w_]/
+ ungetc
+ identify_identifier
+ else
+ Token("@").set_text("@")
+ end
+ end
+
+ # @OP.def_rule("def", proc{|op, io| /\s/ =~ io.peek(0)}) do
+ # |op, io|
+ # @indent += 1
+ # @lex_state = EXPR_FNAME
+ # # @lex_state = EXPR_END
+ # # until @rests[0] == "\n" or @rests[0] == ";"
+ # # rests.shift
+ # # end
+ # end
+
+ @OP.def_rule("__END__", proc{@prev_char_no == 0 && peek(0) =~ /[\r\n]/}) do
+ throw :eof
+ end
+
+ @OP.def_rule("") do
+ |op, io|
+ printf "MATCH: start %s: %s\n", op, io.inspect if RubyLex.debug?
+ if peek(0) =~ /[0-9]/
+ t = identify_number("")
+ elsif peek(0) =~ /[\w_]/
+ t = identify_identifier
+ end
+ printf "MATCH: end %s: %s\n", op, io.inspect if RubyLex.debug?
+ t
+ end
+
+ p @OP if RubyLex.debug?
+ end
+
+ def identify_gvar
+ @lex_state = EXPR_END
+ str = "$"
+
+ tk = case ch = getc
+ when /[~_*$?!@\/\\;,=:<>".]/ #"
+ str << ch
+ Token(TkGVAR, str)
+
+ when "-"
+ str << "-" << getc
+ Token(TkGVAR, str)
+
+ when "&", "`", "'", "+"
+ str << ch
+ Token(TkBACK_REF, str)
+
+ when /[1-9]/
+ str << ch
+ while (ch = getc) =~ /[0-9]/
+ str << ch
+ end
+ ungetc
+ Token(TkNTH_REF)
+ when /\w/
+ ungetc
+ ungetc
+ return identify_identifier
+ else
+ ungetc
+ Token("$")
+ end
+ tk.set_text(str)
+ end
+
+ def identify_identifier
+ token = ""
+ token.concat getc if peek(0) =~ /[$@]/
+ token.concat getc if peek(0) == "@"
+
+ while (ch = getc) =~ /\w|_/
+ print ":", ch, ":" if RubyLex.debug?
+ token.concat ch
+ end
+ ungetc
+
+ if ch == "!" or ch == "?"
+ token.concat getc
+ end
+ # fix token
+
+ # $stderr.puts "identifier - #{token}, state = #@lex_state"
+
+ case token
+ when /^\$/
+ return Token(TkGVAR, token).set_text(token)
+ when /^\@/
+ @lex_state = EXPR_END
+ return Token(TkIVAR, token).set_text(token)
+ end
+
+ if @lex_state != EXPR_DOT
+ print token, "\n" if RubyLex.debug?
+
+ token_c, *trans = TkReading2Token[token]
+ if token_c
+ # reserved word?
+
+ if (@lex_state != EXPR_BEG &&
+ @lex_state != EXPR_FNAME &&
+ trans[1])
+ # modifiers
+ token_c = TkSymbol2Token[trans[1]]
+ @lex_state = trans[0]
+ else
+ if @lex_state != EXPR_FNAME
+ if ENINDENT_CLAUSE.include?(token)
+ @indent += 1
+ elsif DEINDENT_CLAUSE.include?(token)
+ @indent -= 1
+ end
+ @lex_state = trans[0]
+ else
+ @lex_state = EXPR_END
+ end
+ end
+ return Token(token_c, token).set_text(token)
+ end
+ end
+
+ if @lex_state == EXPR_FNAME
+ @lex_state = EXPR_END
+ if peek(0) == '='
+ token.concat getc
+ end
+ elsif @lex_state == EXPR_BEG || @lex_state == EXPR_DOT
+ @lex_state = EXPR_ARG
+ else
+ @lex_state = EXPR_END
+ end
+
+ if token[0, 1] =~ /[A-Z]/
+ return Token(TkCONSTANT, token).set_text(token)
+ elsif token[token.size - 1, 1] =~ /[!?]/
+ return Token(TkFID, token).set_text(token)
+ else
+ return Token(TkIDENTIFIER, token).set_text(token)
+ end
+ end
+
+ def identify_here_document
+ ch = getc
+ if ch == "-"
+ ch = getc
+ indent = true
+ end
+ if /['"`]/ =~ ch # '
+ lt = ch
+ quoted = ""
+ while (c = getc) && c != lt
+ quoted.concat c
+ end
+ else
+ lt = '"'
+ quoted = ch.dup
+ while (c = getc) && c =~ /\w/
+ quoted.concat c
+ end
+ ungetc
+ end
+
+ ltback, @ltype = @ltype, lt
+ reserve = ""
+
+ while ch = getc
+ reserve << ch
+ if ch == "\\" #"
+ ch = getc
+ reserve << ch
+ elsif ch == "\n"
+ break
+ end
+ end
+
+ str = ""
+ while (l = gets)
+ l.chomp!
+ l.strip! if indent
+ break if l == quoted
+ str << l.chomp << "\n"
+ end
+
+ @reader.divert_read_from(reserve)
+
+ @ltype = ltback
+ @lex_state = EXPR_END
+ Token(Ltype2Token[lt], str).set_text(str.dump)
+ end
+
+ def identify_quotation(initial_char)
+ ch = getc
+ if lt = PERCENT_LTYPE[ch]
+ initial_char += ch
+ ch = getc
+ elsif ch =~ /\W/
+ lt = "\""
+ else
+ RubyLex.fail SyntaxError, "unknown type of %string ('#{ch}')"
+ end
+# if ch !~ /\W/
+# ungetc
+# next
+# end
+ #@ltype = lt
+ @quoted = ch unless @quoted = PERCENT_PAREN[ch]
+ identify_string(lt, @quoted, ch, initial_char)
+ end
+
+ def identify_number(start)
+ str = start.dup
+
+ if start == "+" or start == "-" or start == ""
+ start = getc
+ str << start
+ end
+
+ @lex_state = EXPR_END
+
+ if start == "0"
+ if peek(0) == "x"
+ ch = getc
+ str << ch
+ match = /[0-9a-f_]/
+ else
+ match = /[0-7_]/
+ end
+ while ch = getc
+ if ch !~ match
+ ungetc
+ break
+ else
+ str << ch
+ end
+ end
+ return Token(TkINTEGER).set_text(str)
+ end
+
+ type = TkINTEGER
+ allow_point = TRUE
+ allow_e = TRUE
+ while ch = getc
+ case ch
+ when /[0-9_]/
+ str << ch
+
+ when allow_point && "."
+ type = TkFLOAT
+ if peek(0) !~ /[0-9]/
+ ungetc
+ break
+ end
+ str << ch
+ allow_point = false
+
+ when allow_e && "e", allow_e && "E"
+ str << ch
+ type = TkFLOAT
+ if peek(0) =~ /[+-]/
+ str << getc
+ end
+ allow_e = false
+ allow_point = false
+ else
+ ungetc
+ break
+ end
+ end
+ Token(type).set_text(str)
+ end
+
+ def identify_string(ltype, quoted = ltype, opener=nil, initial_char = nil)
+ @ltype = ltype
+ @quoted = quoted
+ subtype = nil
+
+ str = ""
+ str << initial_char if initial_char
+ str << (opener||quoted)
+
+ nest = 0
+ begin
+ while ch = getc
+ str << ch
+ if @quoted == ch
+ if nest == 0
+ break
+ else
+ nest -= 1
+ end
+ elsif opener == ch
+ nest += 1
+ elsif @ltype != "'" && @ltype != "]" and ch == "#"
+ ch = getc
+ if ch == "{"
+ subtype = true
+ str << ch << skip_inner_expression
+ else
+ ungetc(ch)
+ end
+ elsif ch == '\\' #'
+ str << read_escape
+ end
+ end
+ if @ltype == "/"
+ if peek(0) =~ /i|o|n|e|s/
+ str << getc
+ end
+ end
+ if subtype
+ Token(DLtype2Token[ltype], str)
+ else
+ Token(Ltype2Token[ltype], str)
+ end.set_text(str)
+ ensure
+ @ltype = nil
+ @quoted = nil
+ @lex_state = EXPR_END
+ end
+ end
+
+ def skip_inner_expression
+ res = ""
+ nest = 0
+ while (ch = getc)
+ res << ch
+ if ch == '}'
+ break if nest.zero?
+ nest -= 1
+ elsif ch == '{'
+ nest += 1
+ end
+ end
+ res
+ end
+
+ def identify_comment
+ @ltype = "#"
+ comment = "#"
+ while ch = getc
+ if ch == "\\"
+ ch = getc
+ if ch == "\n"
+ ch = " "
+ else
+ comment << "\\"
+ end
+ else
+ if ch == "\n"
+ @ltype = nil
+ ungetc
+ break
+ end
+ end
+ comment << ch
+ end
+ return Token(TkCOMMENT).set_text(comment)
+ end
+
+ def read_escape
+ res = ""
+ case ch = getc
+ when /[0-7]/
+ ungetc ch
+ 3.times do
+ case ch = getc
+ when /[0-7]/
+ when nil
+ break
+ else
+ ungetc
+ break
+ end
+ res << ch
+ end
+
+ when "x"
+ res << ch
+ 2.times do
+ case ch = getc
+ when /[0-9a-fA-F]/
+ when nil
+ break
+ else
+ ungetc
+ break
+ end
+ res << ch
+ end
+
+ when "M"
+ res << ch
+ if (ch = getc) != '-'
+ ungetc
+ else
+ res << ch
+ if (ch = getc) == "\\" #"
+ res << ch
+ res << read_escape
+ else
+ res << ch
+ end
+ end
+
+ when "C", "c" #, "^"
+ res << ch
+ if ch == "C" and (ch = getc) != "-"
+ ungetc
+ else
+ res << ch
+ if (ch = getc) == "\\" #"
+ res << ch
+ res << read_escape
+ else
+ res << ch
+ end
+ end
+ else
+ res << ch
+ end
+ res
+ end
+end
+
+
+
+# Extract code elements from a source file, returning a TopLevel
+# object containing the constituent file elements.
+#
+# This file is based on rtags
+
+module RDoc
+
+ GENERAL_MODIFIERS = [ 'nodoc' ].freeze
+
+ CLASS_MODIFIERS = GENERAL_MODIFIERS
+
+ ATTR_MODIFIERS = GENERAL_MODIFIERS
+
+ CONSTANT_MODIFIERS = GENERAL_MODIFIERS
+
+ METHOD_MODIFIERS = GENERAL_MODIFIERS +
+ [ 'arg', 'args', 'yield', 'yields', 'notnew', 'not-new', 'not_new', 'doc' ]
+
+
+ class RubyParser
+ include RubyToken
+ include TokenStream
+
+ extend ParserFactory
+
+ parse_files_matching(/\.rbw?$/)
+
+
+ def initialize(top_level, file_name, content, options, stats)
+ @options = options
+ @stats = stats
+ @size = 0
+ @token_listeners = nil
+ @input_file_name = file_name
+ @scanner = RubyLex.new(content)
+ @scanner.exception_on_syntax_error = false
+ @top_level = top_level
+ @progress = $stderr unless options.quiet
+ end
+
+ def scan
+ @tokens = []
+ @unget_read = []
+ @read = []
+ catch(:eof) do
+ catch(:enddoc) do
+ begin
+ parse_toplevel_statements(@top_level)
+ rescue Exception => e
+ $stderr.puts "\n\n"
+ $stderr.puts "RDoc failure in #@input_file_name at or around " +
+ "line #{@scanner.line_no} column #{@scanner.char_no}"
+ $stderr.puts
+ $stderr.puts "Before reporting this, could you check that the file"
+ $stderr.puts "you're documenting compiles cleanly--RDoc is not a"
+ $stderr.puts "full Ruby parser, and gets confused easily if fed"
+ $stderr.puts "invalid programs."
+ $stderr.puts
+ $stderr.puts "The internal error was:\n\n"
+
+ e.set_backtrace(e.backtrace[0,4])
+ raise
+ end
+ end
+ end
+ @top_level
+ end
+
+ private
+
+ def make_message(msg)
+ prefix = "\n" + @input_file_name + ":"
+ if @scanner
+ prefix << "#{@scanner.line_no}:#{@scanner.char_no}: "
+ end
+ return prefix + msg
+ end
+
+ def warn(msg)
+ return if @options.quiet
+ msg = make_message msg
+ $stderr.puts msg
+ end
+
+ def error(msg)
+ msg = make_message msg
+ $stderr.puts msg
+ exit(1)
+ end
+
+ def progress(char)
+ unless @options.quiet
+ @progress.print(char)
+ @progress.flush
+ end
+ end
+
+ def add_token_listener(obj)
+ @token_listeners ||= []
+ @token_listeners << obj
+ end
+
+ def remove_token_listener(obj)
+ @token_listeners.delete(obj)
+ end
+
+ def get_tk
+ tk = nil
+ if @tokens.empty?
+ tk = @scanner.token
+ @read.push @scanner.get_read
+ puts "get_tk1 => #{tk.inspect}" if $TOKEN_DEBUG
+ else
+ @read.push @unget_read.shift
+ tk = @tokens.shift
+ puts "get_tk2 => #{tk.inspect}" if $TOKEN_DEBUG
+ end
+
+ if tk.kind_of?(TkSYMBEG)
+ set_token_position(tk.line_no, tk.char_no)
+ tk1 = get_tk
+ if tk1.kind_of?(TkId) || tk1.kind_of?(TkOp)
+ tk = Token(TkSYMBOL).set_text(":" + tk1.name)
+ # remove the identifier we just read (we're about to
+ # replace it with a symbol)
+ @token_listeners.each do |obj|
+ obj.pop_token
+ end if @token_listeners
+ else
+ warn("':' not followed by identified or operator")
+ tk = tk1
+ end
+ end
+
+ # inform any listeners of our shiny new token
+ @token_listeners.each do |obj|
+ obj.add_token(tk)
+ end if @token_listeners
+
+ tk
+ end
+
+ def peek_tk
+ unget_tk(tk = get_tk)
+ tk
+ end
+
+ def unget_tk(tk)
+ @tokens.unshift tk
+ @unget_read.unshift @read.pop
+
+ # Remove this token from any listeners
+ @token_listeners.each do |obj|
+ obj.pop_token
+ end if @token_listeners
+ end
+
+ def skip_tkspace(skip_nl = true)
+ tokens = []
+ while ((tk = get_tk).kind_of?(TkSPACE) ||
+ (skip_nl && tk.kind_of?(TkNL)))
+ tokens.push tk
+ end
+ unget_tk(tk)
+ tokens
+ end
+
+ def get_tkread
+ read = @read.join("")
+ @read = []
+ read
+ end
+
+ def peek_read
+ @read.join('')
+ end
+
+ NORMAL = "::"
+ SINGLE = "<<"
+
+ # Look for the first comment in a file that isn't
+ # a shebang line.
+
+ def collect_first_comment
+ skip_tkspace
+ res = ''
+ first_line = true
+
+ tk = get_tk
+ while tk.kind_of?(TkCOMMENT)
+ if first_line && tk.text[0,2] == "#!"
+ skip_tkspace
+ tk = get_tk
+ else
+ res << tk.text << "\n"
+ tk = get_tk
+ if tk.kind_of? TkNL
+ skip_tkspace(false)
+ tk = get_tk
+ end
+ end
+ first_line = false
+ end
+ unget_tk(tk)
+ res
+ end
+
+ def parse_toplevel_statements(container)
+ comment = collect_first_comment
+ look_for_directives_in(container, comment)
+ container.comment = comment unless comment.empty?
+ parse_statements(container, NORMAL, nil, comment)
+ end
+
+ def parse_statements(container, single=NORMAL, current_method=nil, comment='')
+ nest = 1
+ save_visibility = container.visibility
+
+# if container.kind_of?(TopLevel)
+# else
+# comment = ''
+# end
+
+ non_comment_seen = true
+
+ while tk = get_tk
+
+ keep_comment = false
+
+ non_comment_seen = true unless tk.kind_of?(TkCOMMENT)
+
+ case tk
+
+ when TkNL
+ skip_tkspace(true) # Skip blanks and newlines
+ tk = get_tk
+ if tk.kind_of?(TkCOMMENT)
+ if non_comment_seen
+ comment = ''
+ non_comment_seen = false
+ end
+ while tk.kind_of?(TkCOMMENT)
+ comment << tk.text << "\n"
+ tk = get_tk # this is the newline
+ skip_tkspace(false) # leading spaces
+ tk = get_tk
+ end
+ unless comment.empty?
+ look_for_directives_in(container, comment)
+ if container.done_documenting
+ container.ongoing_visibility = save_visibility
+# return
+ end
+ end
+ keep_comment = true
+ else
+ non_comment_seen = true
+ end
+ unget_tk(tk)
+ keep_comment = true
+
+
+ when TkCLASS
+ if container.document_children
+ parse_class(container, single, tk, comment)
+ else
+ nest += 1
+ end
+
+ when TkMODULE
+ if container.document_children
+ parse_module(container, single, tk, comment)
+ else
+ nest += 1
+ end
+
+ when TkDEF
+ if container.document_self
+ parse_method(container, single, tk, comment)
+ else
+ nest += 1
+ end
+
+ when TkCONSTANT
+ if container.document_self
+ parse_constant(container, single, tk, comment)
+ end
+
+ when TkALIAS
+ if container.document_self
+ parse_alias(container, single, tk, comment)
+ end
+
+ when TkYIELD
+ if current_method.nil?
+ warn("Warning: yield outside of method") if container.document_self
+ else
+ parse_yield(container, single, tk, current_method)
+ end
+
+ # Until and While can have a 'do', which shouldn't increas
+ # the nesting. We can't solve the general case, but we can
+ # handle most occurrences by ignoring a do at the end of a line
+
+ when TkUNTIL, TkWHILE
+ nest += 1
+ puts "FOUND #{tk.class} in #{container.name}, nest = #{nest}, " +
+ "line #{tk.line_no}" if $DEBUG
+ skip_optional_do_after_expression
+
+ # 'for' is trickier
+ when TkFOR
+ nest += 1
+ puts "FOUND #{tk.class} in #{container.name}, nest = #{nest}, " +
+ "line #{tk.line_no}" if $DEBUG
+ skip_for_variable
+ skip_optional_do_after_expression
+
+ when TkCASE, TkDO, TkIF, TkUNLESS, TkBEGIN
+ nest += 1
+ puts "Found #{tk.class} in #{container.name}, nest = #{nest}, " +
+ "line #{tk.line_no}" if $DEBUG
+
+ when TkIDENTIFIER
+ if nest == 1 and current_method.nil?
+ case tk.name
+ when "private", "protected", "public",
+ "private_class_method", "public_class_method"
+ parse_visibility(container, single, tk)
+ keep_comment = true
+ when "attr"
+ parse_attr(container, single, tk, comment)
+ when /^attr_(reader|writer|accessor)$/, @options.extra_accessors
+ parse_attr_accessor(container, single, tk, comment)
+ when "alias_method"
+ if container.document_self
+ parse_alias(container, single, tk, comment)
+ end
+ end
+ end
+
+ case tk.name
+ when "require"
+ parse_require(container, comment)
+ when "include"
+ parse_include(container, comment)
+ end
+
+
+ when TkEND
+ nest -= 1
+ puts "Found 'end' in #{container.name}, nest = #{nest}, line #{tk.line_no}" if $DEBUG
+ puts "Method = #{current_method.name}" if $DEBUG and current_method
+ if nest == 0
+ read_documentation_modifiers(container, CLASS_MODIFIERS)
+ container.ongoing_visibility = save_visibility
+ return
+ end
+
+ end
+
+ comment = '' unless keep_comment
+ begin
+ get_tkread
+ skip_tkspace(false)
+ end while peek_tk == TkNL
+
+ end
+ end
+
+ def parse_class(container, single, tk, comment, &block)
+ progress("c")
+
+ @stats.num_classes += 1
+
+ container, name_t = get_class_or_module(container)
+
+ case name_t
+ when TkCONSTANT
+ name = name_t.name
+ superclass = "Object"
+
+ if peek_tk.kind_of?(TkLT)
+ get_tk
+ skip_tkspace(true)
+ superclass = get_class_specification
+ superclass = "<unknown>" if superclass.empty?
+ end
+
+ if single == SINGLE
+ cls_type = SingleClass
+ else
+ cls_type = NormalClass
+ end
+
+ cls = container.add_class(cls_type, name, superclass)
+ read_documentation_modifiers(cls, CLASS_MODIFIERS)
+ cls.record_location(@top_level)
+ parse_statements(cls)
+ cls.comment = comment
+
+ when TkLSHFT
+ case name = get_class_specification
+ when "self", container.name
+ parse_statements(container, SINGLE, &block)
+ else
+ other = TopLevel.find_class_named(name)
+ unless other
+# other = @top_level.add_class(NormalClass, name, nil)
+# other.record_location(@top_level)
+# other.comment = comment
+ other = NormalClass.new("Dummy", nil)
+ end
+ read_documentation_modifiers(other, CLASS_MODIFIERS)
+ parse_statements(other, SINGLE, &block)
+ end
+
+ else
+ warn("Expected class name or '<<'. Got #{name_t.class}: #{name_t.text.inspect}")
+ end
+ end
+
+ def parse_module(container, single, tk, comment)
+ progress("m")
+ @stats.num_modules += 1
+ container, name_t = get_class_or_module(container)
+# skip_tkspace
+ name = name_t.name
+ mod = container.add_module(NormalModule, name)
+ mod.record_location(@top_level)
+ read_documentation_modifiers(mod, CLASS_MODIFIERS)
+ parse_statements(mod)
+ mod.comment = comment
+ end
+
+ # Look for the name of a class of module (optionally with a leading :: or
+ # with :: separated named) and return the ultimate name and container
+
+ def get_class_or_module(container)
+ skip_tkspace
+ name_t = get_tk
+
+ # class ::A -> A is in the top level
+ if name_t.kind_of?(TkCOLON2)
+ name_t = get_tk
+ container = @top_level
+ end
+
+ skip_tkspace(false)
+
+ while peek_tk.kind_of?(TkCOLON2)
+ prev_container = container
+ container = container.find_module_named(name_t.name)
+ if !container
+# warn("Couldn't find module #{name_t.name}")
+ container = prev_container.add_module(NormalModule, name_t.name)
+ end
+ get_tk
+ name_t = get_tk
+ end
+ skip_tkspace(false)
+ return [container, name_t]
+ end
+
+ def parse_constant(container, single, tk, comment)
+ name = tk.name
+ skip_tkspace(false)
+ eq_tk = get_tk
+
+ unless eq_tk.kind_of?(TkASSIGN)
+ unget_tk(eq_tk)
+ return
+ end
+
+
+ nest = 0
+ get_tkread
+
+ tk = get_tk
+ if tk.kind_of? TkGT
+ unget_tk(tk)
+ unget_tk(eq_tk)
+ return
+ end
+
+ loop do
+ puts("Param: #{tk}, #{@scanner.continue} " +
+ "#{@scanner.lex_state} #{nest}") if $DEBUG
+
+ case tk
+ when TkSEMICOLON
+ break
+ when TkLPAREN, TkfLPAREN
+ nest += 1
+ when TkRPAREN
+ nest -= 1
+ when TkCOMMENT
+ if nest <= 0 && @scanner.lex_state == EXPR_END
+ unget_tk(tk)
+ break
+ end
+ when TkNL
+ if (@scanner.lex_state == EXPR_END and nest <= 0) || !@scanner.continue
+ unget_tk(tk)
+ break
+ end
+ end
+ tk = get_tk
+ end
+
+ res = get_tkread.tr("\n", " ").strip
+ res = "" if res == ";"
+ con = Constant.new(name, res, comment)
+ read_documentation_modifiers(con, CONSTANT_MODIFIERS)
+ if con.document_self
+ container.add_constant(con)
+ end
+ end
+
+ def parse_method(container, single, tk, comment)
+ progress(".")
+ @stats.num_methods += 1
+ line_no = tk.line_no
+ column = tk.char_no
+
+ start_collecting_tokens
+ add_token(tk)
+ add_token_listener(self)
+
+ @scanner.instance_eval{@lex_state = EXPR_FNAME}
+ skip_tkspace(false)
+ name_t = get_tk
+ back_tk = skip_tkspace
+ meth = nil
+ added_container = false
+
+ dot = get_tk
+ if dot.kind_of?(TkDOT) or dot.kind_of?(TkCOLON2)
+ @scanner.instance_eval{@lex_state = EXPR_FNAME}
+ skip_tkspace
+ name_t2 = get_tk
+ case name_t
+ when TkSELF
+ name = name_t2.name
+ when TkCONSTANT
+ name = name_t2.name
+ prev_container = container
+ container = container.find_module_named(name_t.name)
+ if !container
+ added_container = true
+ obj = name_t.name.split("::").inject(Object) do |state, item|
+ state.const_get(item)
+ end rescue nil
+
+ type = obj.class == Class ? NormalClass : NormalModule
+ if not [Class, Module].include?(obj.class)
+ warn("Couldn't find #{name_t.name}. Assuming it's a module")
+ end
+
+ if type == NormalClass then
+ container = prev_container.add_class(type, name_t.name, obj.superclass.name)
+ else
+ container = prev_container.add_module(type, name_t.name)
+ end
+ end
+ else
+ # warn("Unexpected token '#{name_t2.inspect}'")
+ # break
+ skip_method(container)
+ return
+ end
+ meth = AnyMethod.new(get_tkread, name)
+ meth.singleton = true
+ else
+ unget_tk dot
+ back_tk.reverse_each do
+ |tk|
+ unget_tk tk
+ end
+ name = name_t.name
+
+ meth = AnyMethod.new(get_tkread, name)
+ meth.singleton = (single == SINGLE)
+ end
+
+ remove_token_listener(self)
+
+ meth.start_collecting_tokens
+ indent = TkSPACE.new(1,1)
+ indent.set_text(" " * column)
+
+ meth.add_tokens([TkCOMMENT.new(line_no,
+ 1,
+ "# File #{@top_level.file_absolute_name}, line #{line_no}"),
+ NEWLINE_TOKEN,
+ indent])
+
+ meth.add_tokens(@token_stream)
+
+ add_token_listener(meth)
+
+ @scanner.instance_eval{@continue = false}
+ parse_method_parameters(meth)
+
+ if meth.document_self
+ container.add_method(meth)
+ elsif added_container
+ container.document_self = false
+ end
+
+ # Having now read the method parameters and documentation modifiers, we
+ # now know whether we have to rename #initialize to ::new
+
+ if name == "initialize" && !meth.singleton
+ if meth.dont_rename_initialize
+ meth.visibility = :protected
+ else
+ meth.singleton = true
+ meth.name = "new"
+ meth.visibility = :public
+ end
+ end
+
+ parse_statements(container, single, meth)
+
+ remove_token_listener(meth)
+
+ # Look for a 'call-seq' in the comment, and override the
+ # normal parameter stuff
+
+ if comment.sub!(/:?call-seq:(.*?)^\s*\#?\s*$/m, '')
+ seq = $1
+ seq.gsub!(/^\s*\#\s*/, '')
+ meth.call_seq = seq
+ end
+
+ meth.comment = comment
+
+ end
+
+ def skip_method(container)
+ meth = AnyMethod.new("", "anon")
+ parse_method_parameters(meth)
+ parse_statements(container, false, meth)
+ end
+
+ # Capture the method's parameters. Along the way,
+ # look for a comment containing
+ #
+ # # yields: ....
+ #
+ # and add this as the block_params for the method
+
+ def parse_method_parameters(method)
+ res = parse_method_or_yield_parameters(method)
+ res = "(" + res + ")" unless res[0] == ?(
+ method.params = res unless method.params
+ if method.block_params.nil?
+ skip_tkspace(false)
+ read_documentation_modifiers(method, METHOD_MODIFIERS)
+ end
+ end
+
+ def parse_method_or_yield_parameters(method=nil, modifiers=METHOD_MODIFIERS)
+ skip_tkspace(false)
+ tk = get_tk
+
+ # Little hack going on here. In the statement
+ # f = 2*(1+yield)
+ # We see the RPAREN as the next token, so we need
+ # to exit early. This still won't catch all cases
+ # (such as "a = yield + 1"
+ end_token = case tk
+ when TkLPAREN, TkfLPAREN
+ TkRPAREN
+ when TkRPAREN
+ return ""
+ else
+ TkNL
+ end
+ nest = 0
+
+ loop do
+ puts("Param: #{tk.inspect}, #{@scanner.continue} " +
+ "#{@scanner.lex_state} #{nest}") if $DEBUG
+ case tk
+ when TkSEMICOLON
+ break
+ when TkLBRACE
+ nest += 1
+ when TkRBRACE
+ # we might have a.each {|i| yield i }
+ unget_tk(tk) if nest.zero?
+ nest -= 1
+ break if nest <= 0
+ when TkLPAREN, TkfLPAREN
+ nest += 1
+ when end_token
+ if end_token == TkRPAREN
+ nest -= 1
+ break if @scanner.lex_state == EXPR_END and nest <= 0
+ else
+ break unless @scanner.continue
+ end
+ when method && method.block_params.nil? && TkCOMMENT
+ unget_tk(tk)
+ read_documentation_modifiers(method, modifiers)
+ end
+ tk = get_tk
+ end
+ res = get_tkread.tr("\n", " ").strip
+ res = "" if res == ";"
+ res
+ end
+
+ # skip the var [in] part of a 'for' statement
+ def skip_for_variable
+ skip_tkspace(false)
+ tk = get_tk
+ skip_tkspace(false)
+ tk = get_tk
+ unget_tk(tk) unless tk.kind_of?(TkIN)
+ end
+
+ # while, until, and for have an optional
+ def skip_optional_do_after_expression
+ skip_tkspace(false)
+ tk = get_tk
+ case tk
+ when TkLPAREN, TkfLPAREN
+ end_token = TkRPAREN
+ else
+ end_token = TkNL
+ end
+
+ nest = 0
+ @scanner.instance_eval{@continue = false}
+
+ loop do
+ puts("\nWhile: #{tk}, #{@scanner.continue} " +
+ "#{@scanner.lex_state} #{nest}") if $DEBUG
+ case tk
+ when TkSEMICOLON
+ break
+ when TkLPAREN, TkfLPAREN
+ nest += 1
+ when TkDO
+ break if nest.zero?
+ when end_token
+ if end_token == TkRPAREN
+ nest -= 1
+ break if @scanner.lex_state == EXPR_END and nest.zero?
+ else
+ break unless @scanner.continue
+ end
+ end
+ tk = get_tk
+ end
+ skip_tkspace(false)
+ if peek_tk.kind_of? TkDO
+ get_tk
+ end
+ end
+
+ # Return a superclass, which can be either a constant
+ # of an expression
+
+ def get_class_specification
+ tk = get_tk
+ return "self" if tk.kind_of?(TkSELF)
+
+ res = ""
+ while tk.kind_of?(TkCOLON2) ||
+ tk.kind_of?(TkCOLON3) ||
+ tk.kind_of?(TkCONSTANT)
+
+ res += tk.text
+ tk = get_tk
+ end
+
+ unget_tk(tk)
+ skip_tkspace(false)
+
+ get_tkread # empty out read buffer
+
+ tk = get_tk
+
+ case tk
+ when TkNL, TkCOMMENT, TkSEMICOLON
+ unget_tk(tk)
+ return res
+ end
+
+ res += parse_call_parameters(tk)
+ res
+ end
+
+ def parse_call_parameters(tk)
+
+ end_token = case tk
+ when TkLPAREN, TkfLPAREN
+ TkRPAREN
+ when TkRPAREN
+ return ""
+ else
+ TkNL
+ end
+ nest = 0
+
+ loop do
+ puts("Call param: #{tk}, #{@scanner.continue} " +
+ "#{@scanner.lex_state} #{nest}") if $DEBUG
+ case tk
+ when TkSEMICOLON
+ break
+ when TkLPAREN, TkfLPAREN
+ nest += 1
+ when end_token
+ if end_token == TkRPAREN
+ nest -= 1
+ break if @scanner.lex_state == EXPR_END and nest <= 0
+ else
+ break unless @scanner.continue
+ end
+ when TkCOMMENT
+ unget_tk(tk)
+ break
+ end
+ tk = get_tk
+ end
+ res = get_tkread.tr("\n", " ").strip
+ res = "" if res == ";"
+ res
+ end
+
+
+ # Parse a constant, which might be qualified by
+ # one or more class or module names
+
+ def get_constant
+ res = ""
+ skip_tkspace(false)
+ tk = get_tk
+
+ while tk.kind_of?(TkCOLON2) ||
+ tk.kind_of?(TkCOLON3) ||
+ tk.kind_of?(TkCONSTANT)
+
+ res += tk.text
+ tk = get_tk
+ end
+
+# if res.empty?
+# warn("Unexpected token #{tk} in constant")
+# end
+ unget_tk(tk)
+ res
+ end
+
+ # Get a constant that may be surrounded by parens
+
+ def get_constant_with_optional_parens
+ skip_tkspace(false)
+ nest = 0
+ while (tk = peek_tk).kind_of?(TkLPAREN) || tk.kind_of?(TkfLPAREN)
+ get_tk
+ skip_tkspace(true)
+ nest += 1
+ end
+
+ name = get_constant
+
+ while nest > 0
+ skip_tkspace(true)
+ tk = get_tk
+ nest -= 1 if tk.kind_of?(TkRPAREN)
+ end
+ name
+ end
+
+ # Directives are modifier comments that can appear after class, module,
+ # or method names. For example
+ #
+ # def fred # :yields: a, b
+ #
+ # or
+ #
+ # class SM # :nodoc:
+ #
+ # we return the directive name and any parameters as a two element array
+
+ def read_directive(allowed)
+ tk = get_tk
+ puts "directive: #{tk.inspect}" if $DEBUG
+ result = nil
+ if tk.kind_of?(TkCOMMENT)
+ if tk.text =~ /\s*:?(\w+):\s*(.*)/
+ directive = $1.downcase
+ if allowed.include?(directive)
+ result = [directive, $2]
+ end
+ end
+ else
+ unget_tk(tk)
+ end
+ result
+ end
+
+
+ def read_documentation_modifiers(context, allow)
+ dir = read_directive(allow)
+
+ case dir[0]
+
+ when "notnew", "not_new", "not-new"
+ context.dont_rename_initialize = true
+
+ when "nodoc"
+ context.document_self = false
+ if dir[1].downcase == "all"
+ context.document_children = false
+ end
+
+ when "doc"
+ context.document_self = true
+ context.force_documentation = true
+
+ when "yield", "yields"
+ unless context.params.nil?
+ context.params.sub!(/(,|)\s*&\w+/,'') # remove parameter &proc
+ end
+ context.block_params = dir[1]
+
+ when "arg", "args"
+ context.params = dir[1]
+ end if dir
+ end
+
+
+ # Look for directives in a normal comment block:
+ #
+ # #-- - don't display comment from this point forward
+ #
+ #
+ # This routine modifies it's parameter
+
+ def look_for_directives_in(context, comment)
+
+ preprocess = SM::PreProcess.new(@input_file_name,
+ @options.rdoc_include)
+
+ preprocess.handle(comment) do |directive, param|
+ case directive
+ when "stopdoc"
+ context.stop_doc
+ ""
+ when "startdoc"
+ context.start_doc
+ context.force_documentation = true
+ ""
+
+ when "enddoc"
+ #context.done_documenting = true
+ #""
+ throw :enddoc
+
+ when "main"
+ options = Options.instance
+ options.main_page = param
+ ""
+
+ when "title"
+ options = Options.instance
+ options.title = param
+ ""
+
+ when "section"
+ context.set_current_section(param, comment)
+ comment.replace("") # 1.8 doesn't support #clear
+ break
+ else
+ warn "Unrecognized directive '#{directive}'"
+ break
+ end
+ end
+
+ remove_private_comments(comment)
+ end
+
+ def remove_private_comments(comment)
+ comment.gsub!(/^#--.*?^#\+\+/m, '')
+ comment.sub!(/^#--.*/m, '')
+ end
+
+
+
+ def get_symbol_or_name
+ tk = get_tk
+ case tk
+ when TkSYMBOL
+ tk.text.sub(/^:/, '')
+ when TkId, TkOp
+ tk.name
+ when TkSTRING
+ tk.text
+ else
+ raise "Name or symbol expected (got #{tk})"
+ end
+ end
+
+ def parse_alias(context, single, tk, comment)
+ skip_tkspace
+ if (peek_tk.kind_of? TkLPAREN)
+ get_tk
+ skip_tkspace
+ end
+ new_name = get_symbol_or_name
+ @scanner.instance_eval{@lex_state = EXPR_FNAME}
+ skip_tkspace
+ if (peek_tk.kind_of? TkCOMMA)
+ get_tk
+ skip_tkspace
+ end
+ old_name = get_symbol_or_name
+
+ al = Alias.new(get_tkread, old_name, new_name, comment)
+ read_documentation_modifiers(al, ATTR_MODIFIERS)
+ if al.document_self
+ context.add_alias(al)
+ end
+ end
+
+ def parse_yield_parameters
+ parse_method_or_yield_parameters
+ end
+
+ def parse_yield(context, single, tk, method)
+ if method.block_params.nil?
+ get_tkread
+ @scanner.instance_eval{@continue = false}
+ method.block_params = parse_yield_parameters
+ end
+ end
+
+ def parse_require(context, comment)
+ skip_tkspace_comment
+ tk = get_tk
+ if tk.kind_of? TkLPAREN
+ skip_tkspace_comment
+ tk = get_tk
+ end
+
+ name = nil
+ case tk
+ when TkSTRING
+ name = tk.text
+# when TkCONSTANT, TkIDENTIFIER, TkIVAR, TkGVAR
+# name = tk.name
+ when TkDSTRING
+ warn "Skipping require of dynamic string: #{tk.text}"
+ # else
+ # warn "'require' used as variable"
+ end
+ if name
+ context.add_require(Require.new(name, comment))
+ else
+ unget_tk(tk)
+ end
+ end
+
+ def parse_include(context, comment)
+ loop do
+ skip_tkspace_comment
+ name = get_constant_with_optional_parens
+ unless name.empty?
+ context.add_include(Include.new(name, comment))
+ end
+ return unless peek_tk.kind_of?(TkCOMMA)
+ get_tk
+ end
+ end
+
+ def get_bool
+ skip_tkspace
+ tk = get_tk
+ case tk
+ when TkTRUE
+ true
+ when TkFALSE, TkNIL
+ false
+ else
+ unget_tk tk
+ true
+ end
+ end
+
+ def parse_attr(context, single, tk, comment)
+ args = parse_symbol_arg(1)
+ if args.size > 0
+ name = args[0]
+ rw = "R"
+ skip_tkspace(false)
+ tk = get_tk
+ if tk.kind_of? TkCOMMA
+ rw = "RW" if get_bool
+ else
+ unget_tk tk
+ end
+ att = Attr.new(get_tkread, name, rw, comment)
+ read_documentation_modifiers(att, ATTR_MODIFIERS)
+ if att.document_self
+ context.add_attribute(att)
+ end
+ else
+ warn("'attr' ignored - looks like a variable")
+ end
+
+ end
+
+ def parse_visibility(container, single, tk)
+ singleton = (single == SINGLE)
+ vis = case tk.name
+ when "private" then :private
+ when "protected" then :protected
+ when "public" then :public
+ when "private_class_method"
+ singleton = true
+ :private
+ when "public_class_method"
+ singleton = true
+ :public
+ else raise "Invalid visibility: #{tk.name}"
+ end
+
+ skip_tkspace_comment(false)
+ case peek_tk
+ # Ryan Davis suggested the extension to ignore modifiers, because he
+ # often writes
+ #
+ # protected unless $TESTING
+ #
+ when TkNL, TkUNLESS_MOD, TkIF_MOD
+# error("Missing argument") if singleton
+ container.ongoing_visibility = vis
+ else
+ args = parse_symbol_arg
+ container.set_visibility_for(args, vis, singleton)
+ end
+ end
+
+ def parse_attr_accessor(context, single, tk, comment)
+ args = parse_symbol_arg
+ read = get_tkread
+ rw = "?"
+
+ # If nodoc is given, don't document any of them
+
+ tmp = CodeObject.new
+ read_documentation_modifiers(tmp, ATTR_MODIFIERS)
+ return unless tmp.document_self
+
+ case tk.name
+ when "attr_reader" then rw = "R"
+ when "attr_writer" then rw = "W"
+ when "attr_accessor" then rw = "RW"
+ else
+ rw = @options.extra_accessor_flags[tk.name]
+ end
+
+ for name in args
+ att = Attr.new(get_tkread, name, rw, comment)
+ context.add_attribute(att)
+ end
+ end
+
+ def skip_tkspace_comment(skip_nl = true)
+ loop do
+ skip_tkspace(skip_nl)
+ return unless peek_tk.kind_of? TkCOMMENT
+ get_tk
+ end
+ end
+
+ def parse_symbol_arg(no = nil)
+
+ args = []
+ skip_tkspace_comment
+ case tk = get_tk
+ when TkLPAREN
+ loop do
+ skip_tkspace_comment
+ if tk1 = parse_symbol_in_arg
+ args.push tk1
+ break if no and args.size >= no
+ end
+
+ skip_tkspace_comment
+ case tk2 = get_tk
+ when TkRPAREN
+ break
+ when TkCOMMA
+ else
+ warn("unexpected token: '#{tk2.inspect}'") if $DEBUG
+ break
+ end
+ end
+ else
+ unget_tk tk
+ if tk = parse_symbol_in_arg
+ args.push tk
+ return args if no and args.size >= no
+ end
+
+ loop do
+# skip_tkspace_comment(false)
+ skip_tkspace(false)
+
+ tk1 = get_tk
+ unless tk1.kind_of?(TkCOMMA)
+ unget_tk tk1
+ break
+ end
+
+ skip_tkspace_comment
+ if tk = parse_symbol_in_arg
+ args.push tk
+ break if no and args.size >= no
+ end
+ end
+ end
+ args
+ end
+
+ def parse_symbol_in_arg
+ case tk = get_tk
+ when TkSYMBOL
+ tk.text.sub(/^:/, '')
+ when TkSTRING
+ eval @read[-1]
+ else
+ warn("Expected symbol or string, got #{tk.inspect}") if $DEBUG
+ nil
+ end
+ end
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rdoc/parsers/parse_simple.rb b/ruby_1_8_5/lib/rdoc/parsers/parse_simple.rb
new file mode 100644
index 0000000000..3f1a546964
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/parsers/parse_simple.rb
@@ -0,0 +1,41 @@
+# Parse a non-source file. We basically take the whole thing
+# as one big comment. If the first character in the file
+# is '#', we strip leading pound signs.
+
+
+require "rdoc/code_objects"
+require "rdoc/markup/simple_markup/preprocess"
+
+module RDoc
+ # See rdoc/parsers/parse_c.rb
+
+ class SimpleParser
+
+ # prepare to parse a plain file
+ def initialize(top_level, file_name, body, options, stats)
+
+ preprocess = SM::PreProcess.new(file_name, options.rdoc_include)
+
+ preprocess.handle(body) do |directive, param|
+ $stderr.puts "Unrecognized directive '#{directive}' in #{file_name}"
+ end
+
+ @body = body
+ @options = options
+ @top_level = top_level
+ end
+
+ # Extract the file contents and attach them to the toplevel as a
+ # comment
+
+ def scan
+ # @body.gsub(/^(\s\n)+/, '')
+ @top_level.comment = remove_private_comments(@body)
+ @top_level
+ end
+
+ def remove_private_comments(comment)
+ comment.gsub(/^--.*?^\+\+/m, '').sub(/^--.*/m, '')
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rdoc/parsers/parserfactory.rb b/ruby_1_8_5/lib/rdoc/parsers/parserfactory.rb
new file mode 100644
index 0000000000..00a82cf4b1
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/parsers/parserfactory.rb
@@ -0,0 +1,99 @@
+require "rdoc/parsers/parse_simple"
+
+module RDoc
+
+ # A parser is simple a class that implements
+ #
+ # #initialize(file_name, body, options)
+ #
+ # and
+ #
+ # #scan
+ #
+ # The initialize method takes a file name to be used, the body of the
+ # file, and an RDoc::Options object. The scan method is then called
+ # to return an appropriately parsed TopLevel code object.
+ #
+ # The ParseFactory is used to redirect to the correct parser given a filename
+ # extension. This magic works because individual parsers have to register
+ # themselves with us as they are loaded in. The do this using the following
+ # incantation
+ #
+ #
+ # require "rdoc/parsers/parsefactory"
+ #
+ # module RDoc
+ #
+ # class XyzParser
+ # extend ParseFactory <<<<
+ # parse_files_matching /\.xyz$/ <<<<
+ #
+ # def initialize(file_name, body, options)
+ # ...
+ # end
+ #
+ # def scan
+ # ...
+ # end
+ # end
+ # end
+ #
+ # Just to make life interesting, if we suspect a plain text file, we
+ # also look for a shebang line just in case it's a potential
+ # shell script
+
+
+
+ module ParserFactory
+
+ @@parsers = []
+
+ Parsers = Struct.new(:regexp, :parser)
+
+ # Record the fact that a particular class parses files that
+ # match a given extension
+
+ def parse_files_matching(regexp)
+ @@parsers.unshift Parsers.new(regexp, self)
+ end
+
+ # Return a parser that can handle a particular extension
+
+ def ParserFactory.can_parse(file_name)
+ @@parsers.find {|p| p.regexp.match(file_name) }
+ end
+
+ # Alias an extension to another extension. After this call,
+ # files ending "new_ext" will be parsed using the same parser
+ # as "old_ext"
+
+ def ParserFactory.alias_extension(old_ext, new_ext)
+ parser = ParserFactory.can_parse("xxx.#{old_ext}")
+ return false unless parser
+ @@parsers.unshift Parsers.new(Regexp.new("\\.#{new_ext}$"), parser.parser)
+ true
+ end
+
+ # Find the correct parser for a particular file name. Return a
+ # SimpleParser for ones that we don't know
+
+ def ParserFactory.parser_for(top_level, file_name, body, options, stats)
+ # If no extension, look for shebang
+ if file_name !~ /\.\w+$/ && body =~ %r{\A#!(.+)}
+ shebang = $1
+ case shebang
+ when %r{env\s+ruby}, %r{/ruby}
+ file_name = "dummy.rb"
+ end
+ end
+ parser_description = can_parse(file_name)
+ if parser_description
+ parser = parser_description.parser
+ else
+ parser = SimpleParser
+ end
+
+ parser.new(top_level, file_name, body, options, stats)
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rdoc/rdoc.rb b/ruby_1_8_5/lib/rdoc/rdoc.rb
new file mode 100644
index 0000000000..cb5d6501d8
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/rdoc.rb
@@ -0,0 +1,278 @@
+# See README.
+#
+
+
+VERSION_STRING = %{RDoc V1.0.1 - 20041108}
+
+
+require 'rdoc/parsers/parse_rb.rb'
+require 'rdoc/parsers/parse_c.rb'
+require 'rdoc/parsers/parse_f95.rb'
+
+require 'rdoc/parsers/parse_simple.rb'
+require 'rdoc/options'
+
+require 'rdoc/diagram'
+
+require 'find'
+require 'ftools'
+
+# We put rdoc stuff in the RDoc module to avoid namespace
+# clutter.
+#
+# ToDo: This isn't universally true.
+#
+# :include: README
+
+module RDoc
+
+ # Name of the dotfile that contains the description of files to be
+ # processed in the current directory
+ DOT_DOC_FILENAME = ".document"
+
+ # Simple stats collector
+ class Stats
+ attr_accessor :num_files, :num_classes, :num_modules, :num_methods
+ def initialize
+ @num_files = @num_classes = @num_modules = @num_methods = 0
+ @start = Time.now
+ end
+ def print
+ puts "Files: #@num_files"
+ puts "Classes: #@num_classes"
+ puts "Modules: #@num_modules"
+ puts "Methods: #@num_methods"
+ puts "Elapsed: " + sprintf("%0.3fs", Time.now - @start)
+ end
+ end
+
+
+ # Exception thrown by any rdoc error. Only the #message part is
+ # of use externally.
+
+ class RDocError < Exception
+ end
+
+ # Encapsulate the production of rdoc documentation. Basically
+ # you can use this as you would invoke rdoc from the command
+ # line:
+ #
+ # rdoc = RDoc::RDoc.new
+ # rdoc.document(args)
+ #
+ # where _args_ is an array of strings, each corresponding to
+ # an argument you'd give rdoc on the command line. See rdoc/rdoc.rb
+ # for details.
+
+ class RDoc
+
+ ##
+ # This is the list of output generators that we
+ # support
+
+ Generator = Struct.new(:file_name, :class_name, :key)
+
+ GENERATORS = {}
+ $:.collect {|d|
+ File::expand_path(d)
+ }.find_all {|d|
+ File::directory?("#{d}/rdoc/generators")
+ }.each {|dir|
+ Dir::entries("#{dir}/rdoc/generators").each {|gen|
+ next unless /(\w+)_generator.rb$/ =~ gen
+ type = $1
+ unless GENERATORS.has_key? type
+ GENERATORS[type] = Generator.new("rdoc/generators/#{gen}",
+ "#{type.upcase}Generator".intern,
+ type)
+ end
+ }
+ }
+
+ #######
+ private
+ #######
+
+ ##
+ # Report an error message and exit
+
+ def error(msg)
+ raise RDocError.new(msg)
+ end
+
+ ##
+ # Create an output dir if it doesn't exist. If it does
+ # exist, but doesn't contain the flag file <tt>created.rid</tt>
+ # then we refuse to use it, as we may clobber some
+ # manually generated documentation
+
+ def setup_output_dir(op_dir)
+ flag_file = File.join(op_dir, "created.rid")
+ if File.exist?(op_dir)
+ unless File.directory?(op_dir)
+ error "'#{op_dir}' exists, and is not a directory"
+ end
+ unless File.file?(flag_file)
+ error "\nDirectory #{op_dir} already exists, but it looks like it\n" +
+ "isn't an RDoc directory. Because RDoc doesn't want to risk\n" +
+ "destroying any of your existing files, you'll need to\n" +
+ "specify a different output directory name (using the\n" +
+ "--op <dir> option).\n\n"
+ end
+ else
+ File.makedirs(op_dir)
+ end
+ File.open(flag_file, "w") {|f| f.puts Time.now }
+ end
+
+
+ # The .document file contains a list of file and directory name
+ # patterns, representing candidates for documentation. It may
+ # also contain comments (starting with '#')
+ def parse_dot_doc_file(in_dir, filename, options)
+ # read and strip comments
+ patterns = File.read(filename).gsub(/#.*/, '')
+
+ result = []
+
+ patterns.split.each do |patt|
+ candidates = Dir.glob(File.join(in_dir, patt))
+ result.concat(normalized_file_list(options, candidates))
+ end
+ result
+ end
+
+
+ # Given a list of files and directories, create a list
+ # of all the Ruby files they contain.
+ #
+ # If +force_doc+ is true, we always add the given files.
+ # If false, only add files that we guarantee we can parse
+ # It is true when looking at files given on the command line,
+ # false when recursing through subdirectories.
+ #
+ # The effect of this is that if you want a file with a non-
+ # standard extension parsed, you must name it explicity.
+ #
+
+ def normalized_file_list(options, relative_files, force_doc = false, exclude_pattern=nil)
+ file_list = []
+
+ relative_files.each do |rel_file_name|
+ next if exclude_pattern && exclude_pattern =~ rel_file_name
+ case type = File.stat(rel_file_name).ftype
+ when "file"
+ file_list << rel_file_name.sub(/^\.\//, '') if force_doc || ParserFactory.can_parse(rel_file_name)
+ when "directory"
+ next if rel_file_name == "CVS" || rel_file_name == ".svn"
+ dot_doc = File.join(rel_file_name, DOT_DOC_FILENAME)
+ if File.file?(dot_doc)
+ file_list.concat(parse_dot_doc_file(rel_file_name, dot_doc, options))
+ else
+ file_list.concat(list_files_in_directory(rel_file_name, options))
+ end
+ else
+ raise RDocError.new("I can't deal with a #{type} #{rel_file_name}")
+ end
+ end
+ file_list
+ end
+
+ # Return a list of the files to be processed in
+ # a directory. We know that this directory doesn't have
+ # a .document file, so we're looking for real files. However
+ # we may well contain subdirectories which must
+ # be tested for .document files
+ def list_files_in_directory(dir, options)
+ normalized_file_list(options, Dir.glob(File.join(dir, "*")), false, options.exclude)
+ end
+
+
+ # Parse each file on the command line, recursively entering
+ # directories
+
+ def parse_files(options)
+
+ file_info = []
+
+ files = options.files
+ files = ["."] if files.empty?
+
+ file_list = normalized_file_list(options, files, true)
+
+ file_list.each do |fn|
+ $stderr.printf("\n%35s: ", File.basename(fn)) unless options.quiet
+
+ content = File.open(fn, "r") {|f| f.read}
+
+ top_level = TopLevel.new(fn)
+ parser = ParserFactory.parser_for(top_level, fn, content, options, @stats)
+ file_info << parser.scan
+ @stats.num_files += 1
+ end
+
+ file_info
+ end
+
+
+ public
+
+ ###################################################################
+ #
+ # Format up one or more files according to the given arguments.
+ # For simplicity, _argv_ is an array of strings, equivalent to the
+ # strings that would be passed on the command line. (This isn't a
+ # coincidence, as we _do_ pass in ARGV when running
+ # interactively). For a list of options, see rdoc/rdoc.rb. By
+ # default, output will be stored in a directory called +doc+ below
+ # the current directory, so make sure you're somewhere writable
+ # before invoking.
+ #
+ # Throws: RDocError on error
+
+ def document(argv)
+
+ TopLevel::reset
+
+ @stats = Stats.new
+
+ options = Options.instance
+ options.parse(argv, GENERATORS)
+
+ unless options.all_one_file
+ setup_output_dir(options.op_dir)
+ end
+
+ file_info = parse_files(options)
+
+ gen = options.generator
+
+ $stderr.puts "\nGenerating #{gen.key.upcase}..." unless options.quiet
+
+ require gen.file_name
+
+ gen_class = Generators.const_get(gen.class_name)
+
+ unless file_info.empty?
+ gen = gen_class.for(options)
+
+ pwd = Dir.pwd
+
+ Dir.chdir(options.op_dir) unless options.all_one_file
+
+ begin
+ Diagram.new(file_info, options).draw if options.diagram
+ gen.generate(file_info)
+ ensure
+ Dir.chdir(pwd)
+ end
+ end
+
+ unless options.quiet
+ puts
+ @stats.print
+ end
+ end
+ end
+end
+
diff --git a/ruby_1_8_5/lib/rdoc/ri/ri_cache.rb b/ruby_1_8_5/lib/rdoc/ri/ri_cache.rb
new file mode 100644
index 0000000000..1844ac969e
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/ri/ri_cache.rb
@@ -0,0 +1,187 @@
+module RI
+
+ class ClassEntry
+
+ attr_reader :name
+ attr_reader :path_names
+
+ def initialize(path_name, name, in_class)
+ @path_names = [ path_name ]
+ @name = name
+ @in_class = in_class
+ @class_methods = []
+ @instance_methods = []
+ @inferior_classes = []
+ end
+
+ # We found this class in more tha one place, so add
+ # in the name from there.
+ def add_path(path)
+ @path_names << path
+ end
+
+ # read in our methods and any classes
+ # and modules in our namespace. Methods are
+ # stored in files called name-c|i.yaml,
+ # where the 'name' portion is the external
+ # form of the method name and the c|i is a class|instance
+ # flag
+
+ def load_from(dir)
+ Dir.foreach(dir) do |name|
+ next if name =~ /^\./
+
+ # convert from external to internal form, and
+ # extract the instance/class flag
+
+ if name =~ /^(.*?)-(c|i).yaml$/
+ external_name = $1
+ is_class_method = $2 == "c"
+ internal_name = RiWriter.external_to_internal(external_name)
+ list = is_class_method ? @class_methods : @instance_methods
+ path = File.join(dir, name)
+ list << MethodEntry.new(path, internal_name, is_class_method, self)
+ else
+ full_name = File.join(dir, name)
+ if File.directory?(full_name)
+ inf_class = @inferior_classes.find {|c| c.name == name }
+ if inf_class
+ inf_class.add_path(full_name)
+ else
+ inf_class = ClassEntry.new(full_name, name, self)
+ @inferior_classes << inf_class
+ end
+ inf_class.load_from(full_name)
+ end
+ end
+ end
+ end
+
+ # Return a list of any classes or modules that we contain
+ # that match a given string
+
+ def contained_modules_matching(name)
+ @inferior_classes.find_all {|c| c.name[name]}
+ end
+
+ def classes_and_modules
+ @inferior_classes
+ end
+
+ # Return an exact match to a particular name
+ def contained_class_named(name)
+ @inferior_classes.find {|c| c.name == name}
+ end
+
+ # return the list of local methods matching name
+ # We're split into two because we need distinct behavior
+ # when called from the _toplevel_
+ def methods_matching(name, is_class_method)
+ local_methods_matching(name, is_class_method)
+ end
+
+ # Find methods matching 'name' in ourselves and in
+ # any classes we contain
+ def recursively_find_methods_matching(name, is_class_method)
+ res = local_methods_matching(name, is_class_method)
+ @inferior_classes.each do |c|
+ res.concat(c.recursively_find_methods_matching(name, is_class_method))
+ end
+ res
+ end
+
+
+ # Return our full name
+ def full_name
+ res = @in_class.full_name
+ res << "::" unless res.empty?
+ res << @name
+ end
+
+ # Return a list of all out method names
+ def all_method_names
+ res = @class_methods.map {|m| m.full_name }
+ @instance_methods.each {|m| res << m.full_name}
+ res
+ end
+
+ private
+
+ # Return a list of all our methods matching a given string.
+ # Is +is_class_methods+ if 'nil', we don't care if the method
+ # is a class method or not, otherwise we only return
+ # those methods that match
+ def local_methods_matching(name, is_class_method)
+
+ list = case is_class_method
+ when nil then @class_methods + @instance_methods
+ when true then @class_methods
+ when false then @instance_methods
+ else fail "Unknown is_class_method: #{is_class_method.inspect}"
+ end
+
+ list.find_all {|m| m.name; m.name[name]}
+ end
+ end
+
+ # A TopLevelEntry is like a class entry, but when asked to search
+ # for methods searches all classes, not just itself
+
+ class TopLevelEntry < ClassEntry
+ def methods_matching(name, is_class_method)
+ res = recursively_find_methods_matching(name, is_class_method)
+ end
+
+ def full_name
+ ""
+ end
+
+ def module_named(name)
+
+ end
+
+ end
+
+ class MethodEntry
+ attr_reader :name
+ attr_reader :path_name
+
+ def initialize(path_name, name, is_class_method, in_class)
+ @path_name = path_name
+ @name = name
+ @is_class_method = is_class_method
+ @in_class = in_class
+ end
+
+ def full_name
+ res = @in_class.full_name
+ unless res.empty?
+ if @is_class_method
+ res << "::"
+ else
+ res << "#"
+ end
+ end
+ res << @name
+ end
+ end
+
+ # We represent everything know about all 'ri' files
+ # accessible to this program
+
+ class RiCache
+
+ attr_reader :toplevel
+
+ def initialize(dirs)
+ # At the top level we have a dummy module holding the
+ # overall namespace
+ @toplevel = TopLevelEntry.new('', '::', nil)
+
+ dirs.each do |dir|
+ @toplevel.load_from(dir)
+ end
+ end
+
+ end
+end
diff --git a/ruby_1_8_5/lib/rdoc/ri/ri_descriptions.rb b/ruby_1_8_5/lib/rdoc/ri/ri_descriptions.rb
new file mode 100644
index 0000000000..e5ea9f2fbf
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/ri/ri_descriptions.rb
@@ -0,0 +1,154 @@
+require 'yaml'
+require 'rdoc/markup/simple_markup/fragments'
+
+# Descriptions are created by RDoc (in ri_generator) and
+# written out in serialized form into the documentation
+# tree. ri then reads these to generate the documentation
+
+module RI
+ class NamedThing
+ attr_reader :name
+ def initialize(name)
+ @name = name
+ end
+ def <=>(other)
+ @name <=> other.name
+ end
+
+ def hash
+ @name.hash
+ end
+
+ def eql?(other)
+ @name.eql?(other)
+ end
+ end
+
+# Alias = Struct.new(:old_name, :new_name)
+
+ class AliasName < NamedThing
+ end
+
+ class Attribute < NamedThing
+ attr_reader :rw, :comment
+ def initialize(name, rw, comment)
+ super(name)
+ @rw = rw
+ @comment = comment
+ end
+ end
+
+ class Constant < NamedThing
+ attr_reader :value, :comment
+ def initialize(name, value, comment)
+ super(name)
+ @value = value
+ @comment = comment
+ end
+ end
+
+ class IncludedModule < NamedThing
+ end
+
+
+ class MethodSummary < NamedThing
+ def initialize(name="")
+ super
+ end
+ end
+
+
+
+ class Description
+ attr_accessor :name
+ attr_accessor :full_name
+ attr_accessor :comment
+
+ def serialize
+ self.to_yaml
+ end
+
+ def Description.deserialize(from)
+ YAML.load(from)
+ end
+
+ def <=>(other)
+ @name <=> other.name
+ end
+ end
+
+ class ModuleDescription < Description
+
+ attr_accessor :class_methods
+ attr_accessor :instance_methods
+ attr_accessor :attributes
+ attr_accessor :constants
+ attr_accessor :includes
+
+ # merge in another class desscription into this one
+ def merge_in(old)
+ merge(@class_methods, old.class_methods)
+ merge(@instance_methods, old.instance_methods)
+ merge(@attributes, old.attributes)
+ merge(@constants, old.constants)
+ merge(@includes, old.includes)
+ if @comment.nil? || @comment.empty?
+ @comment = old.comment
+ else
+ unless old.comment.nil? or old.comment.empty? then
+ @comment << SM::Flow::RULE.new
+ @comment.concat old.comment
+ end
+ end
+ end
+
+ def display_name
+ "Module"
+ end
+
+ # the 'ClassDescription' subclass overrides this
+ # to format up the name of a parent
+ def superclass_string
+ nil
+ end
+
+ private
+
+ def merge(into, from)
+ names = {}
+ into.each {|i| names[i.name] = i }
+ from.each {|i| names[i.name] = i }
+ into.replace(names.keys.sort.map {|n| names[n]})
+ end
+ end
+
+ class ClassDescription < ModuleDescription
+ attr_accessor :superclass
+
+ def display_name
+ "Class"
+ end
+
+ def superclass_string
+ if @superclass && @superclass != "Object"
+ @superclass
+ else
+ nil
+ end
+ end
+ end
+
+
+ class MethodDescription < Description
+
+ attr_accessor :is_class_method
+ attr_accessor :visibility
+ attr_accessor :block_params
+ attr_accessor :is_singleton
+ attr_accessor :aliases
+ attr_accessor :is_alias_for
+ attr_accessor :params
+
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rdoc/ri/ri_display.rb b/ruby_1_8_5/lib/rdoc/ri/ri_display.rb
new file mode 100644
index 0000000000..67962fc2c1
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/ri/ri_display.rb
@@ -0,0 +1,255 @@
+require 'rdoc/ri/ri_util'
+require 'rdoc/ri/ri_formatter'
+require 'rdoc/ri/ri_options'
+
+
+# This is a kind of 'flag' module. If you want to write your
+# own 'ri' display module (perhaps because you'r writing
+# an IDE or somesuch beast), you simply write a class
+# which implements the various 'display' methods in 'DefaultDisplay',
+# and include the 'RiDisplay' module in that class.
+#
+# To access your class from the command line, you can do
+#
+# ruby -r <your source file> ../ri ....
+#
+# If folks _really_ want to do this from the command line,
+# I'll build an option in
+
+module RiDisplay
+ @@display_class = nil
+
+ def RiDisplay.append_features(display_class)
+ @@display_class = display_class
+ end
+
+ def RiDisplay.new(*args)
+ @@display_class.new(*args)
+ end
+end
+
+######################################################################
+#
+# A paging display module. Uses the ri_formatter class to do the
+# actual presentation
+#
+
+class DefaultDisplay
+
+ include RiDisplay
+
+ def initialize(options)
+ @options = options
+ @formatter = @options.formatter.new(@options, " ")
+ end
+
+
+ ######################################################################
+
+ def display_usage
+ page do
+ RI::Options::OptionList.usage(short_form=true)
+ end
+ end
+
+
+ ######################################################################
+
+ def display_method_info(method)
+ page do
+ @formatter.draw_line(method.full_name)
+ display_params(method)
+ @formatter.draw_line
+ display_flow(method.comment)
+ if method.aliases && !method.aliases.empty?
+ @formatter.blankline
+ aka = "(also known as "
+ aka << method.aliases.map {|a| a.name }.join(", ")
+ aka << ")"
+ @formatter.wrap(aka)
+ end
+ end
+ end
+
+ ######################################################################
+
+ def display_class_info(klass, ri_reader)
+ page do
+ superclass = klass.superclass_string
+
+ if superclass
+ superclass = " < " + superclass
+ else
+ superclass = ""
+ end
+
+ @formatter.draw_line(klass.display_name + ": " +
+ klass.full_name + superclass)
+
+ display_flow(klass.comment)
+ @formatter.draw_line
+
+ unless klass.includes.empty?
+ @formatter.blankline
+ @formatter.display_heading("Includes:", 2, "")
+ incs = []
+ klass.includes.each do |inc|
+ inc_desc = ri_reader.find_class_by_name(inc.name)
+ if inc_desc
+ str = inc.name + "("
+ str << inc_desc.instance_methods.map{|m| m.name}.join(", ")
+ str << ")"
+ incs << str
+ else
+ incs << inc.name
+ end
+ end
+ @formatter.wrap(incs.sort.join(', '))
+ end
+
+ unless klass.constants.empty?
+ @formatter.blankline
+ @formatter.display_heading("Constants:", 2, "")
+ len = 0
+ klass.constants.each { |c| len = c.name.length if c.name.length > len }
+ len += 2
+ klass.constants.each do |c|
+ @formatter.wrap(c.value,
+ @formatter.indent+((c.name+":").ljust(len)))
+ end
+ end
+
+ unless klass.class_methods.empty?
+ @formatter.blankline
+ @formatter.display_heading("Class methods:", 2, "")
+ @formatter.wrap(klass.class_methods.map{|m| m.name}.sort.join(', '))
+ end
+
+ unless klass.instance_methods.empty?
+ @formatter.blankline
+ @formatter.display_heading("Instance methods:", 2, "")
+ @formatter.wrap(klass.instance_methods.map{|m| m.name}.sort.join(', '))
+ end
+
+ unless klass.attributes.empty?
+ @formatter.blankline
+ @formatter.wrap("Attributes:", "")
+ @formatter.wrap(klass.attributes.map{|a| a.name}.sort.join(', '))
+ end
+ end
+ end
+
+ ######################################################################
+
+ # Display a list of method names
+
+ def display_method_list(methods)
+ page do
+ puts "More than one method matched your request. You can refine"
+ puts "your search by asking for information on one of:\n\n"
+ @formatter.wrap(methods.map {|m| m.full_name} .join(", "))
+ end
+ end
+
+ ######################################################################
+
+ def display_class_list(namespaces)
+ page do
+ puts "More than one class or module matched your request. You can refine"
+ puts "your search by asking for information on one of:\n\n"
+ @formatter.wrap(namespaces.map {|m| m.full_name}.join(", "))
+ end
+ end
+
+ ######################################################################
+
+ def list_known_classes(classes)
+ if classes.empty?
+ warn_no_database
+ else
+ page do
+ @formatter.draw_line("Known classes and modules")
+ @formatter.blankline
+ @formatter.wrap(classes.sort.join(", "))
+ end
+ end
+ end
+
+ ######################################################################
+
+ def list_known_names(names)
+ if names.empty?
+ warn_no_database
+ else
+ page do
+ names.each {|n| @formatter.raw_print_line(n)}
+ end
+ end
+ end
+
+ ######################################################################
+
+ private
+
+ ######################################################################
+
+ def page
+ return yield unless pager = setup_pager
+ begin
+ save_stdout = STDOUT.clone
+ STDOUT.reopen(pager)
+ yield
+ ensure
+ STDOUT.reopen(save_stdout)
+ save_stdout.close
+ pager.close
+ end
+ end
+
+ ######################################################################
+
+ def setup_pager
+ unless @options.use_stdout
+ for pager in [ ENV['PAGER'], "less", "more", 'pager' ].compact.uniq
+ return IO.popen(pager, "w") rescue nil
+ end
+ @options.use_stdout = true
+ nil
+ end
+ end
+
+ ######################################################################
+
+ def display_params(method)
+
+ params = method.params
+
+ if params[0,1] == "("
+ if method.is_singleton
+ params = method.full_name + params
+ else
+ params = method.name + params
+ end
+ end
+ params.split(/\n/).each do |p|
+ @formatter.wrap(p)
+ @formatter.break_to_newline
+ end
+ end
+ ######################################################################
+
+ def display_flow(flow)
+ if !flow || flow.empty?
+ @formatter.wrap("(no description...)")
+ else
+ @formatter.display_flow(flow)
+ end
+ end
+
+ ######################################################################
+
+ def warn_no_database
+ puts "Before using ri, you need to generate documentation"
+ puts "using 'rdoc' with the --ri option"
+ end
+end # class RiDisplay
diff --git a/ruby_1_8_5/lib/rdoc/ri/ri_driver.rb b/ruby_1_8_5/lib/rdoc/ri/ri_driver.rb
new file mode 100644
index 0000000000..a00f20ee3b
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/ri/ri_driver.rb
@@ -0,0 +1,143 @@
+require 'rdoc/ri/ri_paths'
+require 'rdoc/usage'
+require 'rdoc/ri/ri_cache'
+require 'rdoc/ri/ri_util'
+require 'rdoc/ri/ri_reader'
+require 'rdoc/ri/ri_formatter'
+require 'rdoc/ri/ri_options'
+
+
+######################################################################
+
+class RiDriver
+
+ def initialize
+ @options = RI::Options.instance
+
+ args = ARGV
+ if ENV["RI"]
+ args = ENV["RI"].split.concat(ARGV)
+ end
+
+ @options.parse(args)
+
+ path = @options.path
+ report_missing_documentation @options.raw_path if path.empty?
+
+ @ri_reader = RI::RiReader.new(RI::RiCache.new(path))
+ @display = @options.displayer
+ end
+
+ # Couldn't find documentation in +path+, so tell the user what to do
+
+ def report_missing_documentation(path)
+ STDERR.puts "No ri documentation found in:"
+ path.each do |d|
+ STDERR.puts " #{d}"
+ end
+ STDERR.puts "\nWas rdoc run to create documentation?\n\n"
+ RDoc::usage("Installing Documentation")
+ end
+
+ ######################################################################
+
+ # If the list of matching methods contains exactly one entry, or
+ # if it contains an entry that exactly matches the requested method,
+ # then display that entry, otherwise display the list of
+ # matching method names
+
+ def report_method_stuff(requested_method_name, methods)
+ if methods.size == 1
+ method = @ri_reader.get_method(methods[0])
+ @display.display_method_info(method)
+ else
+ entries = methods.find_all {|m| m.name == requested_method_name}
+ if entries.size == 1
+ method = @ri_reader.get_method(entries[0])
+ @display.display_method_info(method)
+ else
+ @display.display_method_list(methods)
+ end
+ end
+ end
+
+ ######################################################################
+
+ def report_class_stuff(namespaces)
+ if namespaces.size == 1
+ klass = @ri_reader.get_class(namespaces[0])
+ @display.display_class_info(klass, @ri_reader)
+ else
+# entries = namespaces.find_all {|m| m.full_name == requested_class_name}
+# if entries.size == 1
+# klass = @ri_reader.get_class(entries[0])
+# @display.display_class_info(klass, @ri_reader)
+# else
+ @display.display_class_list(namespaces)
+# end
+ end
+ end
+
+ ######################################################################
+
+
+ def get_info_for(arg)
+ desc = NameDescriptor.new(arg)
+
+ namespaces = @ri_reader.top_level_namespace
+
+ for class_name in desc.class_names
+ namespaces = @ri_reader.lookup_namespace_in(class_name, namespaces)
+ if namespaces.empty?
+ raise RiError.new("Nothing known about #{arg}")
+ end
+ end
+
+ # at this point, if we have multiple possible namespaces, but one
+ # is an exact match for our requested class, prune down to just it
+
+ full_class_name = desc.full_class_name
+ entries = namespaces.find_all {|m| m.full_name == full_class_name}
+ namespaces = entries if entries.size == 1
+
+ if desc.method_name.nil?
+ report_class_stuff(namespaces)
+ else
+ methods = @ri_reader.find_methods(desc.method_name,
+ desc.is_class_method,
+ namespaces)
+
+ if methods.empty?
+ raise RiError.new("Nothing known about #{arg}")
+ else
+ report_method_stuff(desc.method_name, methods)
+ end
+ end
+ end
+
+ ######################################################################
+
+ def process_args
+ if @options.list_classes
+ classes = @ri_reader.full_class_names
+ @display.list_known_classes(classes)
+ elsif @options.list_names
+ names = @ri_reader.all_names
+ @display.list_known_names(names)
+ else
+ if ARGV.size.zero?
+ @display.display_usage
+ else
+ begin
+ ARGV.each do |arg|
+ get_info_for(arg)
+ end
+ rescue RiError => e
+ STDERR.puts(e.message)
+ exit(1)
+ end
+ end
+ end
+ end
+
+end # class RiDriver
diff --git a/ruby_1_8_5/lib/rdoc/ri/ri_formatter.rb b/ruby_1_8_5/lib/rdoc/ri/ri_formatter.rb
new file mode 100644
index 0000000000..56a1fb4665
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/ri/ri_formatter.rb
@@ -0,0 +1,674 @@
+module RI
+ class TextFormatter
+
+ attr_reader :indent
+
+ def initialize(options, indent)
+ @options = options
+ @width = options.width
+ @indent = indent
+ end
+
+
+ ######################################################################
+
+ def draw_line(label=nil)
+ len = @width
+ len -= (label.size+1) if label
+ print "-"*len
+ if label
+ print(" ")
+ bold_print(label)
+ end
+ puts
+ end
+
+ ######################################################################
+
+ def wrap(txt, prefix=@indent, linelen=@width)
+ return unless txt && !txt.empty?
+ work = conv_markup(txt)
+ textLen = linelen - prefix.length
+ patt = Regexp.new("^(.{0,#{textLen}})[ \n]")
+ next_prefix = prefix.tr("^ ", " ")
+
+ res = []
+
+ while work.length > textLen
+ if work =~ patt
+ res << $1
+ work.slice!(0, $&.length)
+ else
+ res << work.slice!(0, textLen)
+ end
+ end
+ res << work if work.length.nonzero?
+ puts(prefix + res.join("\n" + next_prefix))
+ end
+
+ ######################################################################
+
+ def blankline
+ puts
+ end
+
+ ######################################################################
+
+ # called when we want to ensure a nbew 'wrap' starts on a newline
+ # Only needed for HtmlFormatter, because the rest do their
+ # own line breaking
+
+ def break_to_newline
+ end
+
+ ######################################################################
+
+ def bold_print(txt)
+ print txt
+ end
+
+ ######################################################################
+
+ def raw_print_line(txt)
+ puts txt
+ end
+
+ ######################################################################
+
+ # convert HTML entities back to ASCII
+ def conv_html(txt)
+ txt.
+ gsub(/&gt;/, '>').
+ gsub(/&lt;/, '<').
+ gsub(/&quot;/, '"').
+ gsub(/&amp;/, '&')
+
+ end
+
+ # convert markup into display form
+ def conv_markup(txt)
+ txt.
+ gsub(%r{<tt>(.*?)</tt>}) { "+#$1+" } .
+ gsub(%r{<code>(.*?)</code>}) { "+#$1+" } .
+ gsub(%r{<b>(.*?)</b>}) { "*#$1*" } .
+ gsub(%r{<em>(.*?)</em>}) { "_#$1_" }
+ end
+
+ ######################################################################
+
+ def display_list(list)
+ case list.type
+
+ when SM::ListBase::BULLET
+ prefixer = proc { |ignored| @indent + "* " }
+
+ when SM::ListBase::NUMBER,
+ SM::ListBase::UPPERALPHA,
+ SM::ListBase::LOWERALPHA
+
+ start = case list.type
+ when SM::ListBase::NUMBER then 1
+ when SM::ListBase::UPPERALPHA then 'A'
+ when SM::ListBase::LOWERALPHA then 'a'
+ end
+ prefixer = proc do |ignored|
+ res = @indent + "#{start}.".ljust(4)
+ start = start.succ
+ res
+ end
+
+ when SM::ListBase::LABELED
+ prefixer = proc do |li|
+ li.label
+ end
+
+ when SM::ListBase::NOTE
+ longest = 0
+ list.contents.each do |item|
+ if item.kind_of?(SM::Flow::LI) && item.label.length > longest
+ longest = item.label.length
+ end
+ end
+
+ prefixer = proc do |li|
+ @indent + li.label.ljust(longest+1)
+ end
+
+ else
+ fail "unknown list type"
+
+ end
+
+ list.contents.each do |item|
+ if item.kind_of? SM::Flow::LI
+ prefix = prefixer.call(item)
+ display_flow_item(item, prefix)
+ else
+ display_flow_item(item)
+ end
+ end
+ end
+
+ ######################################################################
+
+ def display_flow_item(item, prefix=@indent)
+ case item
+ when SM::Flow::P, SM::Flow::LI
+ wrap(conv_html(item.body), prefix)
+ blankline
+
+ when SM::Flow::LIST
+ display_list(item)
+
+ when SM::Flow::VERB
+ display_verbatim_flow_item(item, @indent)
+
+ when SM::Flow::H
+ display_heading(conv_html(item.text), item.level, @indent)
+
+ when SM::Flow::RULE
+ draw_line
+
+ else
+ fail "Unknown flow element: #{item.class}"
+ end
+ end
+
+ ######################################################################
+
+ def display_verbatim_flow_item(item, prefix=@indent)
+ item.body.split(/\n/).each do |line|
+ print @indent, conv_html(line), "\n"
+ end
+ blankline
+ end
+
+ ######################################################################
+
+ def display_heading(text, level, indent)
+ text = strip_attributes(text)
+ case level
+ when 1
+ ul = "=" * text.length
+ puts
+ puts text.upcase
+ puts ul
+# puts
+
+ when 2
+ ul = "-" * text.length
+ puts
+ puts text
+ puts ul
+# puts
+ else
+ print indent, text, "\n"
+ end
+ end
+
+
+ def display_flow(flow)
+ flow.each do |f|
+ display_flow_item(f)
+ end
+ end
+
+ def strip_attributes(txt)
+ tokens = txt.split(%r{(</?(?:b|code|em|i|tt)>)})
+ text = []
+ attributes = 0
+ tokens.each do |tok|
+ case tok
+ when %r{^</(\w+)>$}, %r{^<(\w+)>$}
+ ;
+ else
+ text << tok
+ end
+ end
+ text.join
+ end
+
+
+ end
+
+
+ ######################################################################
+ # Handle text with attributes. We're a base class: there are
+ # different presentation classes (one, for example, uses overstrikes
+ # to handle bold and underlining, while another using ANSI escape
+ # sequences
+
+ class AttributeFormatter < TextFormatter
+
+ BOLD = 1
+ ITALIC = 2
+ CODE = 4
+
+ ATTR_MAP = {
+ "b" => BOLD,
+ "code" => CODE,
+ "em" => ITALIC,
+ "i" => ITALIC,
+ "tt" => CODE
+ }
+
+ # TODO: struct?
+ class AttrChar
+ attr_reader :char
+ attr_reader :attr
+
+ def initialize(char, attr)
+ @char = char
+ @attr = attr
+ end
+ end
+
+
+ class AttributeString
+ attr_reader :txt
+
+ def initialize
+ @txt = []
+ @optr = 0
+ end
+
+ def <<(char)
+ @txt << char
+ end
+
+ def empty?
+ @optr >= @txt.length
+ end
+
+ # accept non space, then all following spaces
+ def next_word
+ start = @optr
+ len = @txt.length
+
+ while @optr < len && @txt[@optr].char != " "
+ @optr += 1
+ end
+
+ while @optr < len && @txt[@optr].char == " "
+ @optr += 1
+ end
+
+ @txt[start...@optr]
+ end
+ end
+
+ ######################################################################
+ # overrides base class. Looks for <tt>...</tt> etc sequences
+ # and generates an array of AttrChars. This array is then used
+ # as the basis for the split
+
+ def wrap(txt, prefix=@indent, linelen=@width)
+ return unless txt && !txt.empty?
+
+ txt = add_attributes_to(txt)
+ next_prefix = prefix.tr("^ ", " ")
+ linelen -= prefix.size
+
+ line = []
+
+ until txt.empty?
+ word = txt.next_word
+ if word.size + line.size > linelen
+ write_attribute_text(prefix, line)
+ prefix = next_prefix
+ line = []
+ end
+ line.concat(word)
+ end
+
+ write_attribute_text(prefix, line) if line.length > 0
+ end
+
+ protected
+
+ # overridden in specific formatters
+
+ def write_attribute_text(prefix, line)
+ print prefix
+ line.each do |achar|
+ print achar.char
+ end
+ puts
+ end
+
+ # again, overridden
+
+ def bold_print(txt)
+ print txt
+ end
+
+ private
+
+ def add_attributes_to(txt)
+ tokens = txt.split(%r{(</?(?:b|code|em|i|tt)>)})
+ text = AttributeString.new
+ attributes = 0
+ tokens.each do |tok|
+ case tok
+ when %r{^</(\w+)>$} then attributes &= ~(ATTR_MAP[$1]||0)
+ when %r{^<(\w+)>$} then attributes |= (ATTR_MAP[$1]||0)
+ else
+ tok.split(//).each {|ch| text << AttrChar.new(ch, attributes)}
+ end
+ end
+ text
+ end
+
+ end
+
+
+ ##################################################
+
+ # This formatter generates overstrike-style formatting, which
+ # works with pagers such as man and less.
+
+ class OverstrikeFormatter < AttributeFormatter
+
+ BS = "\C-h"
+
+ def write_attribute_text(prefix, line)
+ print prefix
+ line.each do |achar|
+ attr = achar.attr
+ if (attr & (ITALIC+CODE)) != 0
+ print "_", BS
+ end
+ if (attr & BOLD) != 0
+ print achar.char, BS
+ end
+ print achar.char
+ end
+ puts
+ end
+
+ # draw a string in bold
+ def bold_print(text)
+ text.split(//).each do |ch|
+ print ch, BS, ch
+ end
+ end
+ end
+
+ ##################################################
+
+ # This formatter uses ANSI escape sequences
+ # to colorize stuff
+ # works with pages such as man and less.
+
+ class AnsiFormatter < AttributeFormatter
+
+ def initialize(*args)
+ print "\033[0m"
+ super
+ end
+
+ def write_attribute_text(prefix, line)
+ print prefix
+ curr_attr = 0
+ line.each do |achar|
+ attr = achar.attr
+ if achar.attr != curr_attr
+ update_attributes(achar.attr)
+ curr_attr = achar.attr
+ end
+ print achar.char
+ end
+ update_attributes(0) unless curr_attr.zero?
+ puts
+ end
+
+
+ def bold_print(txt)
+ print "\033[1m#{txt}\033[m"
+ end
+
+ HEADINGS = {
+ 1 => [ "\033[1;32m", "\033[m" ] ,
+ 2 => ["\033[4;32m", "\033[m" ],
+ 3 => ["\033[32m", "\033[m" ]
+ }
+
+ def display_heading(text, level, indent)
+ level = 3 if level > 3
+ heading = HEADINGS[level]
+ print indent
+ print heading[0]
+ print strip_attributes(text)
+ puts heading[1]
+ end
+
+ private
+
+ ATTR_MAP = {
+ BOLD => "1",
+ ITALIC => "33",
+ CODE => "36"
+ }
+
+ def update_attributes(attr)
+ str = "\033["
+ for quality in [ BOLD, ITALIC, CODE]
+ unless (attr & quality).zero?
+ str << ATTR_MAP[quality]
+ end
+ end
+ print str, "m"
+ end
+ end
+
+ ##################################################
+
+ # This formatter uses HTML.
+
+ class HtmlFormatter < AttributeFormatter
+
+ def initialize(*args)
+ super
+ end
+
+ def write_attribute_text(prefix, line)
+ curr_attr = 0
+ line.each do |achar|
+ attr = achar.attr
+ if achar.attr != curr_attr
+ update_attributes(curr_attr, achar.attr)
+ curr_attr = achar.attr
+ end
+ print(escape(achar.char))
+ end
+ update_attributes(curr_attr, 0) unless curr_attr.zero?
+ end
+
+ def draw_line(label=nil)
+ if label != nil
+ bold_print(label)
+ end
+ puts("<hr>")
+ end
+
+ def bold_print(txt)
+ tag("b") { txt }
+ end
+
+ def blankline()
+ puts("<p>")
+ end
+
+ def break_to_newline
+ puts("<br>")
+ end
+
+ def display_heading(text, level, indent)
+ level = 4 if level > 4
+ tag("h#{level}") { text }
+ puts
+ end
+
+ ######################################################################
+
+ def display_list(list)
+
+ case list.type
+ when SM::ListBase::BULLET
+ list_type = "ul"
+ prefixer = proc { |ignored| "<li>" }
+
+ when SM::ListBase::NUMBER,
+ SM::ListBase::UPPERALPHA,
+ SM::ListBase::LOWERALPHA
+ list_type = "ol"
+ prefixer = proc { |ignored| "<li>" }
+
+ when SM::ListBase::LABELED
+ list_type = "dl"
+ prefixer = proc do |li|
+ "<dt><b>" + escape(li.label) + "</b><dd>"
+ end
+
+ when SM::ListBase::NOTE
+ list_type = "table"
+ prefixer = proc do |li|
+ %{<tr valign="top"><td>#{li.label.gsub(/ /, '&nbsp;')}</td><td>}
+ end
+ else
+ fail "unknown list type"
+ end
+
+ print "<#{list_type}>"
+ list.contents.each do |item|
+ if item.kind_of? SM::Flow::LI
+ prefix = prefixer.call(item)
+ print prefix
+ display_flow_item(item, prefix)
+ else
+ display_flow_item(item)
+ end
+ end
+ print "</#{list_type}>"
+ end
+
+ def display_verbatim_flow_item(item, prefix=@indent)
+ print("<pre>")
+ item.body.split(/\n/).each do |line|
+ puts conv_html(line)
+ end
+ puts("</pre>")
+ end
+
+ private
+
+ ATTR_MAP = {
+ BOLD => "b>",
+ ITALIC => "i>",
+ CODE => "tt>"
+ }
+
+ def update_attributes(current, wanted)
+ str = ""
+ # first turn off unwanted ones
+ off = current & ~wanted
+ for quality in [ BOLD, ITALIC, CODE]
+ if (off & quality) > 0
+ str << "</" + ATTR_MAP[quality]
+ end
+ end
+
+ # now turn on wanted
+ for quality in [ BOLD, ITALIC, CODE]
+ unless (wanted & quality).zero?
+ str << "<" << ATTR_MAP[quality]
+ end
+ end
+ print str
+ end
+
+ def tag(code)
+ print("<#{code}>")
+ print(yield)
+ print("</#{code}>")
+ end
+
+ def escape(str)
+ str.
+ gsub(/&/n, '&amp;').
+ gsub(/\"/n, '&quot;').
+ gsub(/>/n, '&gt;').
+ gsub(/</n, '&lt;')
+ end
+
+ end
+
+ ##################################################
+
+ # This formatter reduces extra lines for a simpler output.
+ # It improves way output looks for tools like IRC bots.
+
+ class SimpleFormatter < TextFormatter
+
+ ######################################################################
+
+ # No extra blank lines
+
+ def blankline
+ end
+
+ ######################################################################
+
+ # Display labels only, no lines
+
+ def draw_line(label=nil)
+ unless label.nil? then
+ bold_print(label)
+ puts
+ end
+ end
+
+ ######################################################################
+
+ # Place heading level indicators inline with heading.
+
+ def display_heading(text, level, indent)
+ text = strip_attributes(text)
+ case level
+ when 1
+ puts "= " + text.upcase
+ when 2
+ puts "-- " + text
+ else
+ print indent, text, "\n"
+ end
+ end
+
+ end
+
+
+ # Finally, fill in the list of known formatters
+
+ class TextFormatter
+
+ FORMATTERS = {
+ "ansi" => AnsiFormatter,
+ "bs" => OverstrikeFormatter,
+ "html" => HtmlFormatter,
+ "plain" => TextFormatter,
+ "simple" => SimpleFormatter,
+ }
+
+ def TextFormatter.list
+ FORMATTERS.keys.sort.join(", ")
+ end
+
+ def TextFormatter.for(name)
+ FORMATTERS[name.downcase]
+ end
+
+ end
+
+end
+
+
diff --git a/ruby_1_8_5/lib/rdoc/ri/ri_options.rb b/ruby_1_8_5/lib/rdoc/ri/ri_options.rb
new file mode 100644
index 0000000000..db9f4afecf
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/ri/ri_options.rb
@@ -0,0 +1,313 @@
+# We handle the parsing of options, and subsequently as a singleton
+# object to be queried for option values
+
+module RI
+
+ require 'rdoc/ri/ri_paths'
+ require 'rdoc/ri/ri_display'
+
+ VERSION_STRING = "ri v1.0.1 - 20041108"
+
+ class Options
+
+ require 'singleton'
+ require 'getoptlong'
+
+ include Singleton
+
+ # No not use a pager. Writable, because ri sets it if it
+ # can't find a pager
+ attr_accessor :use_stdout
+
+ # should we just display a class list and exit
+ attr_reader :list_classes
+
+ # should we display a list of all names
+ attr_reader :list_names
+
+ # The width of the output line
+ attr_reader :width
+
+ # the formatting we apply to the output
+ attr_reader :formatter
+
+ # the directory we search for original documentation
+ attr_reader :doc_dir
+
+ module OptionList
+
+ OPTION_LIST = [
+ [ "--help", "-h", nil,
+ "you're looking at it" ],
+
+ [ "--classes", "-c", nil,
+ "Display the names of classes and modules we\n" +
+ "know about"],
+
+ [ "--doc-dir", "-d", "<dirname>",
+ "A directory to search for documentation. If not\n" +
+ "specified, we search the standard rdoc/ri directories.\n" +
+ "May be repeated."],
+
+ [ "--system", nil, nil,
+ "Include documentation from Ruby's standard library:\n " +
+ RI::Paths::SYSDIR ],
+
+ [ "--site", nil, nil,
+ "Include documentation from libraries installed in site_lib:\n " +
+ RI::Paths::SITEDIR ],
+
+ [ "--home", nil, nil,
+ "Include documentation stored in ~/.rdoc:\n " +
+ (RI::Paths::HOMEDIR || "No ~/.rdoc found") ],
+
+ [ "--gems", nil, nil,
+ "Include documentation from Rubygems:\n " +
+ (RI::Paths::GEMDIRS ? "#{Gem.path}/doc/*/ri" :
+ "No Rubygems ri found.") ],
+
+ [ "--format", "-f", "<name>",
+ "Format to use when displaying output:\n" +
+ " " + RI::TextFormatter.list + "\n" +
+ "Use 'bs' (backspace) with most pager programs.\n" +
+ "To use ANSI, either also use the -T option, or\n" +
+ "tell your pager to allow control characters\n" +
+ "(for example using the -R option to less)"],
+
+ [ "--list-names", "-l", nil,
+ "List all the names known to RDoc, one per line"
+ ],
+
+ [ "--no-pager", "-T", nil,
+ "Send output directly to stdout."
+ ],
+
+ [ "--width", "-w", "output width",
+ "Set the width of the output" ],
+
+ [ "--version", "-v", nil,
+ "Display the version of ri"
+ ],
+
+ ]
+
+ def OptionList.options
+ OPTION_LIST.map do |long, short, arg,|
+ option = []
+ option << long
+ option << short unless short.nil?
+ option << (arg ? GetoptLong::REQUIRED_ARGUMENT :
+ GetoptLong::NO_ARGUMENT)
+ option
+ end
+ end
+
+
+ def OptionList.strip_output(text)
+ text =~ /^\s+/
+ leading_spaces = $&
+ text.gsub!(/^#{leading_spaces}/, '')
+ $stdout.puts text
+ end
+
+
+ # Show an error and exit
+
+ def OptionList.error(msg)
+ $stderr.puts
+ $stderr.puts msg
+ $stderr.puts "\nFor help on options, try 'ri --help'\n\n"
+ exit 1
+ end
+
+ # Show usage and exit
+
+ def OptionList.usage(short_form=false)
+
+ puts
+ puts(RI::VERSION_STRING)
+ puts
+
+ name = File.basename($0)
+
+ directories = [
+ RI::Paths::SYSDIR,
+ RI::Paths::SITEDIR,
+ RI::Paths::HOMEDIR
+ ]
+
+ directories << "#{Gem.path}/doc/*/ri" if RI::Paths::GEMDIRS
+
+ directories = directories.join("\n ")
+
+ OptionList.strip_output(<<-EOT)
+ Usage:
+
+ #{name} [options] [names...]
+
+ Display information on Ruby classes, modules, and methods.
+ Give the names of classes or methods to see their documentation.
+ Partial names may be given: if the names match more than
+ one entity, a list will be shown, otherwise details on
+ that entity will be displayed.
+
+ Nested classes and modules can be specified using the normal
+ Name::Name notation, and instance methods can be distinguished
+ from class methods using "." (or "#") instead of "::".
+
+ For example:
+
+ ri File
+ ri File.new
+ ri F.n
+ ri zip
+
+ Note that shell quoting may be required for method names
+ containing punctuation:
+
+ ri 'Array.[]'
+ ri compact\\!
+
+ By default ri searches for documentation in the following
+ directories:
+
+ #{directories}
+
+ Specifying the --system, --site, --home, --gems or --doc-dir
+ options will limit ri to searching only the specified
+ directories.
+
+ EOT
+
+ if short_form
+ puts "For help on options, type 'ri -h'"
+ puts "For a list of classes I know about, type 'ri -c'"
+ else
+ puts "Options:\n\n"
+ OPTION_LIST.each do|long, short, arg, desc|
+ opt = ''
+ opt << (short ? sprintf("%15s", "#{long}, #{short}") :
+ sprintf("%15s", long))
+ if arg
+ opt << " " << arg
+ end
+ print opt
+ desc = desc.split("\n")
+ if opt.size < 17
+ print " "*(18-opt.size)
+ puts desc.shift
+ else
+ puts
+ end
+ desc.each do |line|
+ puts(" "*18 + line)
+ end
+ puts
+ end
+ puts "Options may also be passed in the 'RI' environment variable"
+ exit 0
+ end
+ end
+ end
+
+ # Show the version and exit
+ def show_version
+ puts VERSION_STRING
+ exit(0)
+ end
+
+ def initialize
+ @use_stdout = !STDOUT.tty?
+ @width = 72
+ @formatter = RI::TextFormatter.for("plain")
+ @list_classes = false
+ @list_names = false
+
+ # By default all paths are used. If any of these are true, only those
+ # directories are used.
+ @use_system = false
+ @use_site = false
+ @use_home = false
+ @use_gems = false
+ @doc_dirs = []
+ end
+
+ # Parse command line options.
+
+ def parse(args)
+
+ old_argv = ARGV.dup
+
+ ARGV.replace(args)
+
+ begin
+
+ go = GetoptLong.new(*OptionList.options)
+ go.quiet = true
+
+ go.each do |opt, arg|
+ case opt
+ when "--help" then OptionList.usage
+ when "--version" then show_version
+ when "--list-names" then @list_names = true
+ when "--no-pager" then @use_stdout = true
+ when "--classes" then @list_classes = true
+
+ when "--system" then @use_system = true
+ when "--site" then @use_site = true
+ when "--home" then @use_home = true
+ when "--gems" then @use_gems = true
+
+ when "--doc-dir"
+ if File.directory?(arg)
+ @doc_dirs << arg
+ else
+ $stderr.puts "Invalid directory: #{arg}"
+ exit 1
+ end
+
+ when "--format"
+ @formatter = RI::TextFormatter.for(arg)
+ unless @formatter
+ $stderr.print "Invalid formatter (should be one of "
+ $stderr.puts RI::TextFormatter.list + ")"
+ exit 1
+ end
+ when "--width"
+ begin
+ @width = Integer(arg)
+ rescue
+ $stderr.puts "Invalid width: '#{arg}'"
+ exit 1
+ end
+ end
+ end
+
+ rescue GetoptLong::InvalidOption, GetoptLong::MissingArgument => error
+ OptionList.error(error.message)
+
+ end
+ end
+
+ # Return the selected documentation directories.
+
+ def path
+ RI::Paths.path(@use_system, @use_site, @use_home, @use_gems, *@doc_dirs)
+ end
+
+ def raw_path
+ RI::Paths.raw_path(@use_system, @use_site, @use_home, @use_gems,
+ *@doc_dirs)
+ end
+
+ # Return an instance of the displayer (the thing that actually writes
+ # the information). This allows us to load in new displayer classes
+ # at runtime (for example to help with IDE integration)
+
+ def displayer
+ ::RiDisplay.new(self)
+ end
+ end
+
+end
+
diff --git a/ruby_1_8_5/lib/rdoc/ri/ri_paths.rb b/ruby_1_8_5/lib/rdoc/ri/ri_paths.rb
new file mode 100644
index 0000000000..32363bf70a
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/ri/ri_paths.rb
@@ -0,0 +1,80 @@
+module RI
+
+ # Encapsulate all the strangeness to do with finding out
+ # where to find RDoc files
+ #
+ # We basically deal with three directories:
+ #
+ # 1. The 'system' documentation directory, which holds
+ # the documentation distributed with Ruby, and which
+ # is managed by the Ruby install process
+ # 2. The 'site' directory, which contains site-wide
+ # documentation added locally.
+ # 3. The 'user' documentation directory, stored under the
+ # user's own home directory.
+ #
+ # There's contention about all this, but for now:
+ #
+ # system:: $datadir/ri/<ver>/system/...
+ # site:: $datadir/ri/<ver>/site/...
+ # user:: ~/.rdoc
+
+ module Paths
+
+ #:stopdoc:
+ require 'rbconfig'
+
+ DOC_DIR = "doc/rdoc"
+
+ version = Config::CONFIG['ruby_version']
+
+ base = File.join(Config::CONFIG['datadir'], "ri", version)
+ SYSDIR = File.join(base, "system")
+ SITEDIR = File.join(base, "site")
+ homedir = ENV['HOME'] || ENV['USERPROFILE'] || ENV['HOMEPATH']
+
+ if homedir
+ HOMEDIR = File.join(homedir, ".rdoc")
+ else
+ HOMEDIR = nil
+ end
+
+ # This is the search path for 'ri'
+ PATH = [ SYSDIR, SITEDIR, HOMEDIR ].find_all {|p| p && File.directory?(p)}
+
+ begin
+ require 'rubygems'
+ GEMDIRS = Dir["#{Gem.path}/doc/*/ri"]
+ GEMDIRS.each { |path| RI::Paths::PATH << path }
+ rescue LoadError
+ GEMDIRS = nil
+ end
+
+ # Returns the selected documentation directories as an Array, or PATH if no
+ # overriding directories were given.
+
+ def self.path(use_system, use_site, use_home, use_gems, *extra_dirs)
+ path = raw_path(use_system, use_site, use_home, use_gems, *extra_dirs)
+ return path.select { |path| File.directory? path }
+ end
+
+ # Returns the selected documentation directories including nonexistent
+ # directories. Used to print out what paths were searched if no ri was
+ # found.
+
+ def self.raw_path(use_system, use_site, use_home, use_gems, *extra_dirs)
+ return PATH unless use_system or use_site or use_home or use_gems or
+ not extra_dirs.empty?
+
+ path = []
+ path << extra_dirs unless extra_dirs.empty?
+ path << RI::Paths::SYSDIR if use_system
+ path << RI::Paths::SITEDIR if use_site
+ path << RI::Paths::HOMEDIR if use_home
+ path << RI::Paths::GEMDIRS if use_gems
+
+ return path.flatten.compact
+ end
+
+ end
+end
diff --git a/ruby_1_8_5/lib/rdoc/ri/ri_reader.rb b/ruby_1_8_5/lib/rdoc/ri/ri_reader.rb
new file mode 100644
index 0000000000..fb2c373e38
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/ri/ri_reader.rb
@@ -0,0 +1,100 @@
+require 'rdoc/ri/ri_descriptions'
+require 'rdoc/ri/ri_writer'
+require 'rdoc/markup/simple_markup/to_flow'
+
+module RI
+ class RiReader
+
+ def initialize(ri_cache)
+ @cache = ri_cache
+ end
+
+ def top_level_namespace
+ [ @cache.toplevel ]
+ end
+
+ def lookup_namespace_in(target, namespaces)
+ result = []
+ for n in namespaces
+ result.concat(n.contained_modules_matching(target))
+ end
+ result
+ end
+
+ def find_class_by_name(full_name)
+ names = full_name.split(/::/)
+ ns = @cache.toplevel
+ for name in names
+ ns = ns.contained_class_named(name)
+ return nil if ns.nil?
+ end
+ get_class(ns)
+ end
+
+ def find_methods(name, is_class_method, namespaces)
+ result = []
+ namespaces.each do |ns|
+ result.concat ns.methods_matching(name, is_class_method)
+ end
+ result
+ end
+
+ # return the MethodDescription for a given MethodEntry
+ # by deserializing the YAML
+ def get_method(method_entry)
+ path = method_entry.path_name
+ File.open(path) { |f| RI::Description.deserialize(f) }
+ end
+
+ # Return a class description
+ def get_class(class_entry)
+ result = nil
+ for path in class_entry.path_names
+ path = RiWriter.class_desc_path(path, class_entry)
+ desc = File.open(path) {|f| RI::Description.deserialize(f) }
+ if result
+ result.merge_in(desc)
+ else
+ result = desc
+ end
+ end
+ result
+ end
+
+ # return the names of all classes and modules
+ def full_class_names
+ res = []
+ find_classes_in(res, @cache.toplevel)
+ end
+
+ # return a list of all classes, modules, and methods
+ def all_names
+ res = []
+ find_names_in(res, @cache.toplevel)
+ end
+
+ # ----
+ private
+ # ----
+
+ def find_classes_in(res, klass)
+ classes = klass.classes_and_modules
+ for c in classes
+ res << c.full_name
+ find_classes_in(res, c)
+ end
+ res
+ end
+
+ def find_names_in(res, klass)
+ classes = klass.classes_and_modules
+ for c in classes
+ res << c.full_name
+ res.concat c.all_method_names
+ find_names_in(res, c)
+ end
+ res
+ end
+
+ end
+end
diff --git a/ruby_1_8_5/lib/rdoc/ri/ri_util.rb b/ruby_1_8_5/lib/rdoc/ri/ri_util.rb
new file mode 100644
index 0000000000..8a01255897
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/ri/ri_util.rb
@@ -0,0 +1,75 @@
+######################################################################
+
+class RiError < Exception; end
+#
+# Break argument into its constituent class or module names, an
+# optional method type, and a method name
+
+class NameDescriptor
+
+ attr_reader :class_names
+ attr_reader :method_name
+
+ # true and false have the obvious meaning. nil means we don't care
+ attr_reader :is_class_method
+
+ # arg may be
+ # 1. a class or module name (optionally qualified with other class
+ # or module names (Kernel, File::Stat etc)
+ # 2. a method name
+ # 3. a method name qualified by a optionally fully qualified class
+ # or module name
+ #
+ # We're fairly casual about delimiters: folks can say Kernel::puts,
+ # Kernel.puts, or Kernel\#puts for example. There's one exception:
+ # if you say IO::read, we look for a class method, but if you
+ # say IO.read, we look for an instance method
+
+ def initialize(arg)
+ @class_names = []
+ separator = nil
+
+ tokens = arg.split(/(\.|::|#)/)
+
+ # Skip leading '::', '#' or '.', but remember it might
+ # be a method name qualifier
+ separator = tokens.shift if tokens[0] =~ /^(\.|::|#)/
+
+ # Skip leading '::', but remember we potentially have an inst
+
+ # leading stuff must be class names
+
+ while tokens[0] =~ /^[A-Z]/
+ @class_names << tokens.shift
+ unless tokens.empty?
+ separator = tokens.shift
+ break unless separator == "::"
+ end
+ end
+
+ # Now must have a single token, the method name, or an empty
+ # array
+ unless tokens.empty?
+ @method_name = tokens.shift
+ # We may now have a trailing !, ?, or = to roll into
+ # the method name
+ if !tokens.empty? && tokens[0] =~ /^[!?=]$/
+ @method_name << tokens.shift
+ end
+
+ if @method_name =~ /::|\.|#/ or !tokens.empty?
+ raise RiError.new("Bad argument: #{arg}")
+ end
+ if separator && separator != '.'
+ @is_class_method = separator == "::"
+ end
+ end
+ end
+
+ # Return the full class name (with '::' between the components)
+ # or "" if there's no class name
+
+ def full_class_name
+ @class_names.join("::")
+ end
+end
diff --git a/ruby_1_8_5/lib/rdoc/ri/ri_writer.rb b/ruby_1_8_5/lib/rdoc/ri/ri_writer.rb
new file mode 100644
index 0000000000..78c68e8409
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/ri/ri_writer.rb
@@ -0,0 +1,62 @@
+require 'fileutils'
+
+module RI
+ class RiWriter
+
+ def RiWriter.class_desc_path(dir, class_desc)
+ File.join(dir, "cdesc-" + class_desc.name + ".yaml")
+ end
+
+
+ # Convert a name from internal form (containing punctuation)
+ # to an external form (where punctuation is replaced
+ # by %xx)
+
+ def RiWriter.internal_to_external(name)
+ name.gsub(/\W/) { sprintf("%%%02x", $&[0]) }
+ end
+
+ # And the reverse operation
+ def RiWriter.external_to_internal(name)
+ name.gsub(/%([0-9a-f]{2,2})/) { $1.to_i(16).chr }
+ end
+
+ def initialize(base_dir)
+ @base_dir = base_dir
+ end
+
+ def remove_class(class_desc)
+ FileUtils.rm_rf(path_to_dir(class_desc.full_name))
+ end
+
+ def add_class(class_desc)
+ dir = path_to_dir(class_desc.full_name)
+ FileUtils.mkdir_p(dir)
+ class_file_name = RiWriter.class_desc_path(dir, class_desc)
+ File.open(class_file_name, "w") do |f|
+ f.write(class_desc.serialize)
+ end
+ end
+
+ def add_method(class_desc, method_desc)
+ dir = path_to_dir(class_desc.full_name)
+ file_name = RiWriter.internal_to_external(method_desc.name)
+ meth_file_name = File.join(dir, file_name)
+ if method_desc.is_singleton
+ meth_file_name += "-c.yaml"
+ else
+ meth_file_name += "-i.yaml"
+ end
+
+ File.open(meth_file_name, "w") do |f|
+ f.write(method_desc.serialize)
+ end
+ end
+
+ private
+
+ def path_to_dir(class_name)
+ File.join(@base_dir, *class_name.split('::'))
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rdoc/template.rb b/ruby_1_8_5/lib/rdoc/template.rb
new file mode 100644
index 0000000000..469e10fb4b
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/template.rb
@@ -0,0 +1,234 @@
+# Cheap-n-cheerful HTML page template system. You create a
+# template containing:
+#
+# * variable names between percent signs (<tt>%fred%</tt>)
+# * blocks of repeating stuff:
+#
+# START:key
+# ... stuff
+# END:key
+#
+# You feed the code a hash. For simple variables, the values
+# are resolved directly from the hash. For blocks, the hash entry
+# corresponding to +key+ will be an array of hashes. The block will
+# be generated once for each entry. Blocks can be nested arbitrarily
+# deeply.
+#
+# The template may also contain
+#
+# IF:key
+# ... stuff
+# ENDIF:key
+#
+# _stuff_ will only be included in the output if the corresponding
+# key is set in the value hash.
+#
+# Usage: Given a set of templates <tt>T1, T2,</tt> etc
+#
+# values = { "name" => "Dave", state => "TX" }
+#
+# t = TemplatePage.new(T1, T2, T3)
+# File.open(name, "w") {|f| t.write_html_on(f, values)}
+# or
+# res = ''
+# t.write_html_on(res, values)
+#
+#
+
+class TemplatePage
+
+ ##########
+ # A context holds a stack of key/value pairs (like a symbol
+ # table). When asked to resolve a key, it first searches the top of
+ # the stack, then the next level, and so on until it finds a match
+ # (or runs out of entries)
+
+ class Context
+ def initialize
+ @stack = []
+ end
+
+ def push(hash)
+ @stack.push(hash)
+ end
+
+ def pop
+ @stack.pop
+ end
+
+ # Find a scalar value, throwing an exception if not found. This
+ # method is used when substituting the %xxx% constructs
+
+ def find_scalar(key)
+ @stack.reverse_each do |level|
+ if val = level[key]
+ return val unless val.kind_of? Array
+ end
+ end
+ raise "Template error: can't find variable '#{key}'"
+ end
+
+ # Lookup any key in the stack of hashes
+
+ def lookup(key)
+ @stack.reverse_each do |level|
+ val = level[key]
+ return val if val
+ end
+ nil
+ end
+ end
+
+ #########
+ # Simple class to read lines out of a string
+
+ class LineReader
+ # we're initialized with an array of lines
+ def initialize(lines)
+ @lines = lines
+ end
+
+ # read the next line
+ def read
+ @lines.shift
+ end
+
+ # Return a list of lines up to the line that matches
+ # a pattern. That last line is discarded.
+ def read_up_to(pattern)
+ res = []
+ while line = read
+ if pattern.match(line)
+ return LineReader.new(res)
+ else
+ res << line
+ end
+ end
+ raise "Missing end tag in template: #{pattern.source}"
+ end
+
+ # Return a copy of ourselves that can be modified without
+ # affecting us
+ def dup
+ LineReader.new(@lines.dup)
+ end
+ end
+
+
+
+ # +templates+ is an array of strings containing the templates.
+ # We start at the first, and substitute in subsequent ones
+ # where the string <tt>!INCLUDE!</tt> occurs. For example,
+ # we could have the overall page template containing
+ #
+ # <html><body>
+ # <h1>Master</h1>
+ # !INCLUDE!
+ # </bost></html>
+ #
+ # and substitute subpages in to it by passing [master, sub_page].
+ # This gives us a cheap way of framing pages
+
+ def initialize(*templates)
+ result = "!INCLUDE!"
+ templates.each do |content|
+ result.sub!(/!INCLUDE!/, content)
+ end
+ @lines = LineReader.new(result.split($/))
+ end
+
+ # Render the templates into HTML, storing the result on +op+
+ # using the method <tt><<</tt>. The <tt>value_hash</tt> contains
+ # key/value pairs used to drive the substitution (as described above)
+
+ def write_html_on(op, value_hash)
+ @context = Context.new
+ op << substitute_into(@lines, value_hash).tr("\000", '\\')
+ end
+
+
+ # Substitute a set of key/value pairs into the given template.
+ # Keys with scalar values have them substituted directly into
+ # the page. Those with array values invoke <tt>substitute_array</tt>
+ # (below), which examples a block of the template once for each
+ # row in the array.
+ #
+ # This routine also copes with the <tt>IF:</tt>_key_ directive,
+ # removing chunks of the template if the corresponding key
+ # does not appear in the hash, and the START: directive, which
+ # loops its contents for each value in an array
+
+ def substitute_into(lines, values)
+ @context.push(values)
+ skip_to = nil
+ result = []
+
+ while line = lines.read
+
+ case line
+
+ when /^IF:(\w+)/
+ lines.read_up_to(/^ENDIF:#$1/) unless @context.lookup($1)
+
+ when /^IFNOT:(\w+)/
+ lines.read_up_to(/^ENDIF:#$1/) if @context.lookup($1)
+
+ when /^ENDIF:/
+ ;
+
+ when /^START:(\w+)/
+ tag = $1
+ body = lines.read_up_to(/^END:#{tag}/)
+ inner_values = @context.lookup(tag)
+ raise "unknown tag: #{tag}" unless inner_values
+ raise "not array: #{tag}" unless inner_values.kind_of?(Array)
+ inner_values.each do |vals|
+ result << substitute_into(body.dup, vals)
+ end
+ else
+ result << expand_line(line.dup)
+ end
+ end
+
+ @context.pop
+
+ result.join("\n")
+ end
+
+ # Given an individual line, we look for %xxx% constructs and
+ # HREF:ref:name: constructs, substituting for each.
+
+ def expand_line(line)
+ # Generate a cross reference if a reference is given,
+ # otherwise just fill in the name part
+
+ line.gsub!(/HREF:(\w+?):(\w+?):/) {
+ ref = @context.lookup($1)
+ name = @context.find_scalar($2)
+
+ if ref and !ref.kind_of?(Array)
+ "<a href=\"#{ref}\">#{name}</a>"
+ else
+ name
+ end
+ }
+
+ # Substitute in values for %xxx% constructs. This is made complex
+ # because the replacement string may contain characters that are
+ # meaningful to the regexp (like \1)
+
+ line = line.gsub(/%([a-zA-Z]\w*)%/) {
+ val = @context.find_scalar($1)
+ val.tr('\\', "\000")
+ }
+
+
+ line
+ rescue Exception => e
+ $stderr.puts "Error in template: #{e}"
+ $stderr.puts "Original line: #{line}"
+ exit
+ end
+
+end
+
diff --git a/ruby_1_8_5/lib/rdoc/tokenstream.rb b/ruby_1_8_5/lib/rdoc/tokenstream.rb
new file mode 100644
index 0000000000..0a0720d8a9
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/tokenstream.rb
@@ -0,0 +1,25 @@
+# A TokenStream is a list of tokens, gathered during the parse
+# of some entity (say a method). Entities populate these streams
+# by being registered with the lexer. Any class can collect tokens
+# by including TokenStream. From the outside, you use such an object
+# by calling the start_collecting_tokens method, followed by calls
+# to add_token and pop_token
+
+module TokenStream
+ def token_stream
+ @token_stream
+ end
+
+ def start_collecting_tokens
+ @token_stream = []
+ end
+ def add_token(tk)
+ @token_stream << tk
+ end
+ def add_tokens(tks)
+ tks.each {|tk| add_token(tk)}
+ end
+ def pop_token
+ @token_stream.pop
+ end
+end
diff --git a/ruby_1_8_5/lib/rdoc/usage.rb b/ruby_1_8_5/lib/rdoc/usage.rb
new file mode 100644
index 0000000000..def516b3d7
--- /dev/null
+++ b/ruby_1_8_5/lib/rdoc/usage.rb
@@ -0,0 +1,210 @@
+# = Synopsis
+#
+# This library allows command-line tools to encapsulate their usage
+# as a comment at the top of the main file. Calling <tt>RDoc::usage</tt>
+# then displays some or all of that comment, and optionally exits
+# the program with an exit status. We always look for the comment
+# in the main program file, so it is safe to call this method
+# from anywhere in the executing program.
+#
+# = Usage
+#
+# RDoc::usage( [ exit_status ], [ section, ...])
+# RDoc::usage_no_exit( [ section, ...])
+#
+# where:
+#
+# exit_status::
+# the integer exit code (default zero). RDoc::usage will exit
+# the calling program with this status.
+#
+# section::
+# an optional list of section names. If specified, only the
+# sections with the given names as headings will be output.
+# For example, this section is named 'Usage', and the next
+# section is named 'Examples'. The section names are case
+# insensitive.
+#
+# = Examples
+#
+# # Comment block describing usage
+# # with (optional) section headings
+# # . . .
+#
+# require 'rdoc/usage'
+#
+# # Display all usage and exit with a status of 0
+#
+# RDoc::usage
+#
+# # Display all usage and exit with a status of 99
+#
+# RDoc::usage(99)
+#
+# # Display usage in the 'Summary' section only, then
+# # exit with a status of 99
+#
+# RDoc::usage(99, 'Summary')
+#
+# # Display information in the Author and Copyright
+# # sections, then exit 0.
+#
+# RDoc::usage('Author', 'Copyright')
+#
+# # Display information in the Author and Copyright
+# # sections, but don't exit
+#
+# RDoc::usage_no_exit('Author', 'Copyright')
+#
+# = Author
+#
+# Dave Thomas, The Pragmatic Programmers, LLC
+#
+# = Copyright
+#
+# Copyright (c) 2004 Dave Thomas.
+# Licensed under the same terms as Ruby
+#
+
+require 'rdoc/markup/simple_markup'
+require 'rdoc/markup/simple_markup/to_flow'
+require 'rdoc/ri/ri_formatter'
+require 'rdoc/ri/ri_options'
+
+module RDoc
+
+ # Display usage information from the comment at the top of
+ # the file. String arguments identify specific sections of the
+ # comment to display. An optional integer first argument
+ # specifies the exit status (defaults to 0)
+
+ def RDoc.usage(*args)
+ exit_code = 0
+
+ if args.size > 0
+ status = args[0]
+ if status.respond_to?(:to_int)
+ exit_code = status.to_int
+ args.shift
+ end
+ end
+
+ # display the usage and exit with the given code
+ usage_no_exit(*args)
+ exit(exit_code)
+ end
+
+ # Display usage
+ def RDoc.usage_no_exit(*args)
+ main_program_file = caller[-1].sub(/:\d+$/, '')
+ comment = File.open(main_program_file) do |file|
+ find_comment(file)
+ end
+
+ comment = comment.gsub(/^\s*#/, '')
+
+ markup = SM::SimpleMarkup.new
+ flow_convertor = SM::ToFlow.new
+
+ flow = markup.convert(comment, flow_convertor)
+
+ format = "plain"
+
+ unless args.empty?
+ flow = extract_sections(flow, args)
+ end
+
+ options = RI::Options.instance
+ if args = ENV["RI"]
+ options.parse(args.split)
+ end
+ formatter = options.formatter.new(options, "")
+ formatter.display_flow(flow)
+ end
+
+ ######################################################################
+
+ private
+
+ # Find the first comment in the file (that isn't a shebang line)
+ # If the file doesn't start with a comment, report the fact
+ # and return empty string
+
+ def RDoc.gets(file)
+ if (line = file.gets) && (line =~ /^#!/) # shebang
+ throw :exit, find_comment(file)
+ else
+ line
+ end
+ end
+
+ def RDoc.find_comment(file)
+ catch(:exit) do
+ # skip leading blank lines
+ 0 while (line = gets(file)) && (line =~ /^\s*$/)
+
+ comment = []
+ while line && line =~ /^\s*#/
+ comment << line
+ line = gets(file)
+ end
+
+ 0 while line && (line = gets(file))
+ return no_comment if comment.empty?
+ return comment.join
+ end
+ end
+
+
+ #####
+ # Given an array of flow items and an array of section names, extract those
+ # sections from the flow which have headings corresponding to
+ # a section name in the list. Return them in the order
+ # of names in the +sections+ array.
+
+ def RDoc.extract_sections(flow, sections)
+ result = []
+ sections.each do |name|
+ name = name.downcase
+ copy_upto_level = nil
+
+ flow.each do |item|
+ case item
+ when SM::Flow::H
+ if copy_upto_level && item.level >= copy_upto_level
+ copy_upto_level = nil
+ else
+ if item.text.downcase == name
+ result << item
+ copy_upto_level = item.level
+ end
+ end
+ else
+ if copy_upto_level
+ result << item
+ end
+ end
+ end
+ end
+ if result.empty?
+ puts "Note to developer: requested section(s) [#{sections.join(', ')}] " +
+ "not found"
+ result = flow
+ end
+ result
+ end
+
+ #####
+ # Report the fact that no doc comment count be found
+ def RDoc.no_comment
+ $stderr.puts "No usage information available for this program"
+ ""
+ end
+end
+
+
+if $0 == __FILE__
+
+ RDoc::usage(*ARGV)
+
+end
diff --git a/ruby_1_8_5/lib/readbytes.rb b/ruby_1_8_5/lib/readbytes.rb
new file mode 100644
index 0000000000..ebb5fc0bd4
--- /dev/null
+++ b/ruby_1_8_5/lib/readbytes.rb
@@ -0,0 +1,41 @@
+# TruncatedDataError is raised when IO#readbytes fails to read enough data.
+
+class TruncatedDataError<IOError
+ def initialize(mesg, data) # :nodoc:
+ @data = data
+ super(mesg)
+ end
+
+ # The read portion of an IO#readbytes attempt.
+ attr_reader :data
+end
+
+class IO
+ # Reads exactly +n+ bytes.
+ #
+ # If the data read is nil an EOFError is raised.
+ #
+ # If the data read is too short a TruncatedDataError is raised and the read
+ # data is obtainable via its #data method.
+ def readbytes(n)
+ str = read(n)
+ if str == nil
+ raise EOFError, "End of file reached"
+ end
+ if str.size < n
+ raise TruncatedDataError.new("data truncated", str)
+ end
+ str
+ end
+end
+
+if __FILE__ == $0
+ begin
+ loop do
+ print STDIN.readbytes(6)
+ end
+ rescue TruncatedDataError
+ p $!.data
+ raise
+ end
+end
diff --git a/ruby_1_8_5/lib/resolv-replace.rb b/ruby_1_8_5/lib/resolv-replace.rb
new file mode 100644
index 0000000000..5d15b4577c
--- /dev/null
+++ b/ruby_1_8_5/lib/resolv-replace.rb
@@ -0,0 +1,62 @@
+require 'socket'
+require 'resolv'
+
+class << IPSocket
+ alias original_resolv_getaddress getaddress
+ def getaddress(host)
+ begin
+ return Resolv.getaddress(host).to_s
+ rescue Resolv::ResolvError
+ raise SocketError, "Hostname not known: #{host}"
+ end
+ end
+end
+
+class TCPSocket
+ alias original_resolv_initialize initialize
+ def initialize(host, serv, *rest)
+ rest[0] = IPSocket.getaddress(rest[0]) unless rest.empty?
+ original_resolv_initialize(IPSocket.getaddress(host), serv, *rest)
+ end
+end
+
+class UDPSocket
+ alias original_resolv_bind bind
+ def bind(host, port)
+ original_resolv_bind(IPSocket.getaddress(host), port)
+ end
+
+ alias original_resolv_connect connect
+ def connect(host, port)
+ original_resolv_connect(IPSocket.getaddress(host), port)
+ end
+
+ alias original_resolv_send send
+ def send(mesg, flags, *rest)
+ if rest.length == 2
+ host, port = rest
+ begin
+ addrs = Resolv.getaddresses(host)
+ rescue Resolv::ResolvError
+ raise SocketError, "Hostname not known: #{host}"
+ end
+ err = nil
+ addrs[0...-1].each {|addr|
+ begin
+ return original_resolv_send(mesg, flags, addr, port)
+ rescue SystemCallError
+ end
+ }
+ original_resolv_send(mesg, flags, addrs[-1], port)
+ else
+ original_resolv_send(mesg, flags, *rest)
+ end
+ end
+end
+
+class SOCKSSocket
+ alias original_resolv_initialize initialize
+ def initialize(host, serv)
+ original_resolv_initialize(IPSocket.getaddress(host), port)
+ end
+end if defined? SOCKSSocket
diff --git a/ruby_1_8_5/lib/resolv.rb b/ruby_1_8_5/lib/resolv.rb
new file mode 100644
index 0000000000..49e40bdf07
--- /dev/null
+++ b/ruby_1_8_5/lib/resolv.rb
@@ -0,0 +1,1884 @@
+=begin
+= resolv library
+resolv.rb is a resolver library written in Ruby.
+Since it is written in Ruby, it is thread-aware.
+I.e. it can resolv many hostnames concurrently.
+
+It is possible to lookup various resources of DNS using DNS module directly.
+
+== example
+ p Resolv.getaddress("www.ruby-lang.org")
+ p Resolv.getname("210.251.121.214")
+
+ Resolv::DNS.open {|dns|
+ p dns.getresources("www.ruby-lang.org", Resolv::DNS::Resource::IN::A).collect {|r| r.address}
+ p dns.getresources("ruby-lang.org", Resolv::DNS::Resource::IN::MX).collect {|r| [r.exchange.to_s, r.preference]}
+ }
+
+== Resolv class
+
+=== class methods
+--- Resolv.getaddress(name)
+--- Resolv.getaddresses(name)
+--- Resolv.each_address(name) {|address| ...}
+ They lookups IP addresses of ((|name|)) which represents a hostname
+ as a string by default resolver.
+
+ getaddress returns first entry of lookupped addresses.
+ getaddresses returns lookupped addresses as an array.
+ each_address iterates over lookupped addresses.
+
+--- Resolv.getname(address)
+--- Resolv.getnames(address)
+--- Resolv.each_name(address) {|name| ...}
+ lookups hostnames of ((|address|)) which represents IP address as a string.
+
+ getname returns first entry of lookupped names.
+ getnames returns lookupped names as an array.
+ each_names iterates over lookupped names.
+
+== Resolv::Hosts class
+hostname resolver using /etc/hosts format.
+
+=== class methods
+--- Resolv::Hosts.new(hosts='/etc/hosts')
+
+=== methods
+--- Resolv::Hosts#getaddress(name)
+--- Resolv::Hosts#getaddresses(name)
+--- Resolv::Hosts#each_address(name) {|address| ...}
+ address lookup methods.
+
+--- Resolv::Hosts#getname(address)
+--- Resolv::Hosts#getnames(address)
+--- Resolv::Hosts#each_name(address) {|name| ...}
+ hostnames lookup methods.
+
+== Resolv::DNS class
+DNS stub resolver.
+
+=== class methods
+--- Resolv::DNS.new(config_info=nil)
+
+ ((|config_info|)) should be nil, a string or a hash.
+ If nil is given, /etc/resolv.conf and platform specific information is used.
+ If a string is given, it should be a filename which format is same as /etc/resolv.conf.
+ If a hash is given, it may contains information for nameserver, search and ndots as follows.
+
+ Resolv::DNS.new({:nameserver=>["210.251.121.21"], :search=>["ruby-lang.org"], :ndots=>1})
+
+--- Resolv::DNS.open(config_info=nil)
+--- Resolv::DNS.open(config_info=nil) {|dns| ...}
+
+=== methods
+--- Resolv::DNS#close
+
+--- Resolv::DNS#getaddress(name)
+--- Resolv::DNS#getaddresses(name)
+--- Resolv::DNS#each_address(name) {|address| ...}
+ address lookup methods.
+
+ ((|name|)) must be a instance of Resolv::DNS::Name or String. Lookupped
+ address is represented as an instance of Resolv::IPv4 or Resolv::IPv6.
+
+--- Resolv::DNS#getname(address)
+--- Resolv::DNS#getnames(address)
+--- Resolv::DNS#each_name(address) {|name| ...}
+ hostnames lookup methods.
+
+ ((|address|)) must be a instance of Resolv::IPv4, Resolv::IPv6 or String.
+ Lookupped name is represented as an instance of Resolv::DNS::Name.
+
+--- Resolv::DNS#getresource(name, typeclass)
+--- Resolv::DNS#getresources(name, typeclass)
+--- Resolv::DNS#each_resource(name, typeclass) {|resource| ...}
+ They lookup DNS resources of ((|name|)).
+ ((|name|)) must be a instance of Resolv::Name or String.
+
+ ((|typeclass|)) should be one of follows:
+ * Resolv::DNS::Resource::IN::ANY
+ * Resolv::DNS::Resource::IN::NS
+ * Resolv::DNS::Resource::IN::CNAME
+ * Resolv::DNS::Resource::IN::SOA
+ * Resolv::DNS::Resource::IN::HINFO
+ * Resolv::DNS::Resource::IN::MINFO
+ * Resolv::DNS::Resource::IN::MX
+ * Resolv::DNS::Resource::IN::TXT
+ * Resolv::DNS::Resource::IN::ANY
+ * Resolv::DNS::Resource::IN::A
+ * Resolv::DNS::Resource::IN::WKS
+ * Resolv::DNS::Resource::IN::PTR
+ * Resolv::DNS::Resource::IN::AAAA
+
+ Lookupped resource is represented as an instance of (a subclass of)
+ Resolv::DNS::Resource.
+ (Resolv::DNS::Resource::IN::A, etc.)
+
+== Resolv::DNS::Resource::IN::NS class
+--- name
+== Resolv::DNS::Resource::IN::CNAME class
+--- name
+== Resolv::DNS::Resource::IN::SOA class
+--- mname
+--- rname
+--- serial
+--- refresh
+--- retry
+--- expire
+--- minimum
+== Resolv::DNS::Resource::IN::HINFO class
+--- cpu
+--- os
+== Resolv::DNS::Resource::IN::MINFO class
+--- rmailbx
+--- emailbx
+== Resolv::DNS::Resource::IN::MX class
+--- preference
+--- exchange
+== Resolv::DNS::Resource::IN::TXT class
+--- data
+== Resolv::DNS::Resource::IN::A class
+--- address
+== Resolv::DNS::Resource::IN::WKS class
+--- address
+--- protocol
+--- bitmap
+== Resolv::DNS::Resource::IN::PTR class
+--- name
+== Resolv::DNS::Resource::IN::AAAA class
+--- address
+
+== Resolv::DNS::Name class
+
+=== class methods
+--- Resolv::DNS::Name.create(name)
+
+=== methods
+--- Resolv::DNS::Name#to_s
+
+== Resolv::DNS::Resource class
+
+== Resolv::IPv4 class
+=== class methods
+--- Resolv::IPv4.create(address)
+
+=== methods
+--- Resolv::IPv4#to_s
+--- Resolv::IPv4#to_name
+
+=== constants
+--- Resolv::IPv4::Regex
+ regular expression for IPv4 address.
+
+== Resolv::IPv6 class
+=== class methods
+--- Resolv::IPv6.create(address)
+
+=== methods
+--- Resolv::IPv6#to_s
+--- Resolv::IPv6#to_name
+
+=== constants
+--- Resolv::IPv6::Regex
+ regular expression for IPv6 address.
+
+== Bugs
+* NIS is not supported.
+* /etc/nsswitch.conf is not supported.
+* IPv6 is not supported.
+
+=end
+
+require 'socket'
+require 'fcntl'
+require 'timeout'
+require 'thread'
+
+class Resolv
+ def self.getaddress(name)
+ DefaultResolver.getaddress(name)
+ end
+
+ def self.getaddresses(name)
+ DefaultResolver.getaddresses(name)
+ end
+
+ def self.each_address(name, &block)
+ DefaultResolver.each_address(name, &block)
+ end
+
+ def self.getname(address)
+ DefaultResolver.getname(address)
+ end
+
+ def self.getnames(address)
+ DefaultResolver.getnames(address)
+ end
+
+ def self.each_name(address, &proc)
+ DefaultResolver.each_name(address, &proc)
+ end
+
+ def initialize(resolvers=[Hosts.new, DNS.new])
+ @resolvers = resolvers
+ end
+
+ def getaddress(name)
+ each_address(name) {|address| return address}
+ raise ResolvError.new("no address for #{name}")
+ end
+
+ def getaddresses(name)
+ ret = []
+ each_address(name) {|address| ret << address}
+ return ret
+ end
+
+ def each_address(name)
+ if AddressRegex =~ name
+ yield name
+ return
+ end
+ yielded = false
+ @resolvers.each {|r|
+ r.each_address(name) {|address|
+ yield address.to_s
+ yielded = true
+ }
+ return if yielded
+ }
+ end
+
+ def getname(address)
+ each_name(address) {|name| return name}
+ raise ResolvError.new("no name for #{address}")
+ end
+
+ def getnames(address)
+ ret = []
+ each_name(address) {|name| ret << name}
+ return ret
+ end
+
+ def each_name(address)
+ yielded = false
+ @resolvers.each {|r|
+ r.each_name(address) {|name|
+ yield name.to_s
+ yielded = true
+ }
+ return if yielded
+ }
+ end
+
+ class ResolvError < StandardError
+ end
+
+ class ResolvTimeout < TimeoutError
+ end
+
+ class Hosts
+ if /mswin32|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM
+ require 'win32/resolv'
+ DefaultFileName = Win32::Resolv.get_hosts_path
+ else
+ DefaultFileName = '/etc/hosts'
+ end
+
+ def initialize(filename = DefaultFileName)
+ @filename = filename
+ @mutex = Mutex.new
+ @initialized = nil
+ end
+
+ def lazy_initialize
+ @mutex.synchronize {
+ unless @initialized
+ @name2addr = {}
+ @addr2name = {}
+ open(@filename) {|f|
+ f.each {|line|
+ line.sub!(/#.*/, '')
+ addr, hostname, *aliases = line.split(/\s+/)
+ next unless addr
+ addr.untaint
+ hostname.untaint
+ @addr2name[addr] = [] unless @addr2name.include? addr
+ @addr2name[addr] << hostname
+ @addr2name[addr] += aliases
+ @name2addr[hostname] = [] unless @name2addr.include? hostname
+ @name2addr[hostname] << addr
+ aliases.each {|n|
+ n.untaint
+ @name2addr[n] = [] unless @name2addr.include? n
+ @name2addr[n] << addr
+ }
+ }
+ }
+ @name2addr.each {|name, arr| arr.reverse!}
+ @initialized = true
+ end
+ }
+ self
+ end
+
+ def getaddress(name)
+ each_address(name) {|address| return address}
+ raise ResolvError.new("#{@filename} has no name: #{name}")
+ end
+
+ def getaddresses(name)
+ ret = []
+ each_address(name) {|address| ret << address}
+ return ret
+ end
+
+ def each_address(name, &proc)
+ lazy_initialize
+ if @name2addr.include?(name)
+ @name2addr[name].each(&proc)
+ end
+ end
+
+ def getname(address)
+ each_name(address) {|name| return name}
+ raise ResolvError.new("#{@filename} has no address: #{address}")
+ end
+
+ def getnames(address)
+ ret = []
+ each_name(address) {|name| ret << name}
+ return ret
+ end
+
+ def each_name(address, &proc)
+ lazy_initialize
+ if @addr2name.include?(address)
+ @addr2name[address].each(&proc)
+ end
+ end
+ end
+
+ class DNS
+ # STD0013 (RFC 1035, etc.)
+ # ftp://ftp.isi.edu/in-notes/iana/assignments/dns-parameters
+
+ Port = 53
+ UDPSize = 512
+
+ DNSThreadGroup = ThreadGroup.new
+
+ def self.open(*args)
+ dns = new(*args)
+ return dns unless block_given?
+ begin
+ yield dns
+ ensure
+ dns.close
+ end
+ end
+
+ def initialize(config_info=nil)
+ @mutex = Mutex.new
+ @config = Config.new(config_info)
+ @initialized = nil
+ end
+
+ def lazy_initialize
+ @mutex.synchronize {
+ unless @initialized
+ @config.lazy_initialize
+
+ if nameserver = @config.single?
+ @requester = Requester::ConnectedUDP.new(nameserver)
+ else
+ @requester = Requester::UnconnectedUDP.new
+ end
+
+ @initialized = true
+ end
+ }
+ self
+ end
+
+ def close
+ @mutex.synchronize {
+ if @initialized
+ @requester.close if @requester
+ @requester = nil
+ @initialized = false
+ end
+ }
+ end
+
+ def getaddress(name)
+ each_address(name) {|address| return address}
+ raise ResolvError.new("DNS result has no information for #{name}")
+ end
+
+ def getaddresses(name)
+ ret = []
+ each_address(name) {|address| ret << address}
+ return ret
+ end
+
+ def each_address(name)
+ each_resource(name, Resource::IN::A) {|resource| yield resource.address}
+ end
+
+ def getname(address)
+ each_name(address) {|name| return name}
+ raise ResolvError.new("DNS result has no information for #{address}")
+ end
+
+ def getnames(address)
+ ret = []
+ each_name(address) {|name| ret << name}
+ return ret
+ end
+
+ def each_name(address)
+ case address
+ when Name
+ ptr = address
+ when IPv4::Regex
+ ptr = IPv4.create(address).to_name
+ when IPv6::Regex
+ ptr = IPv6.create(address).to_name
+ else
+ raise ResolvError.new("cannot interpret as address: #{address}")
+ end
+ each_resource(ptr, Resource::IN::PTR) {|resource| yield resource.name}
+ end
+
+ def getresource(name, typeclass)
+ each_resource(name, typeclass) {|resource| return resource}
+ raise ResolvError.new("DNS result has no information for #{name}")
+ end
+
+ def getresources(name, typeclass)
+ ret = []
+ each_resource(name, typeclass) {|resource| ret << resource}
+ return ret
+ end
+
+ def each_resource(name, typeclass, &proc)
+ lazy_initialize
+ q = Queue.new
+ senders = {}
+ begin
+ @config.resolv(name) {|candidate, tout, nameserver|
+ msg = Message.new
+ msg.rd = 1
+ msg.add_question(candidate, typeclass)
+ unless sender = senders[[candidate, nameserver]]
+ sender = senders[[candidate, nameserver]] =
+ @requester.sender(msg, candidate, q, nameserver)
+ end
+ sender.send
+ reply = reply_name = nil
+ timeout(tout, ResolvTimeout) { reply, reply_name = q.pop }
+ case reply.rcode
+ when RCode::NoError
+ extract_resources(reply, reply_name, typeclass, &proc)
+ return
+ when RCode::NXDomain
+ raise Config::NXDomain.new(reply_name.to_s)
+ else
+ raise Config::OtherResolvError.new(reply_name.to_s)
+ end
+ }
+ ensure
+ @requester.delete(q)
+ end
+ end
+
+ def extract_resources(msg, name, typeclass)
+ if typeclass < Resource::ANY
+ n0 = Name.create(name)
+ msg.each_answer {|n, ttl, data|
+ yield data if n0 == n
+ }
+ end
+ yielded = false
+ n0 = Name.create(name)
+ msg.each_answer {|n, ttl, data|
+ if n0 == n
+ case data
+ when typeclass
+ yield data
+ yielded = true
+ when Resource::CNAME
+ n0 = data.name
+ end
+ end
+ }
+ return if yielded
+ msg.each_answer {|n, ttl, data|
+ if n0 == n
+ case data
+ when typeclass
+ yield data
+ end
+ end
+ }
+ end
+
+ class Requester
+ def initialize
+ @senders = {}
+ end
+
+ def close
+ thread, sock, @thread, @sock = @thread, @sock
+ begin
+ if thread
+ thread.kill
+ thread.join
+ end
+ ensure
+ sock.close if sock
+ end
+ end
+
+ def delete(arg)
+ case arg
+ when Sender
+ @senders.delete_if {|k, s| s == arg }
+ when Queue
+ @senders.delete_if {|k, s| s.queue == arg }
+ else
+ raise ArgumentError.new("neither Sender or Queue: #{arg}")
+ end
+ end
+
+ class Sender
+ def initialize(msg, data, sock, queue)
+ @msg = msg
+ @data = data
+ @sock = sock
+ @queue = queue
+ end
+ attr_reader :queue
+
+ def recv(msg)
+ @queue.push([msg, @data])
+ end
+ end
+
+ class UnconnectedUDP < Requester
+ def initialize
+ super()
+ @sock = UDPSocket.new
+ @sock.fcntl(Fcntl::F_SETFD, 1) if defined? Fcntl::F_SETFD
+ @id = {}
+ @id.default = -1
+ @thread = Thread.new {
+ DNSThreadGroup.add Thread.current
+ loop {
+ reply, from = @sock.recvfrom(UDPSize)
+ msg = begin
+ Message.decode(reply)
+ rescue DecodeError
+ STDERR.print("DNS message decoding error: #{reply.inspect}\n")
+ next
+ end
+ if s = @senders[[[from[3],from[1]],msg.id]]
+ s.recv msg
+ else
+ #STDERR.print("non-handled DNS message: #{msg.inspect} from #{from.inspect}\n")
+ end
+ }
+ }
+ end
+
+ def sender(msg, data, queue, host, port=Port)
+ service = [host, port]
+ id = Thread.exclusive {
+ @id[service] = (@id[service] + 1) & 0xffff
+ }
+ request = msg.encode
+ request[0,2] = [id].pack('n')
+ return @senders[[service, id]] =
+ Sender.new(request, data, @sock, host, port, queue)
+ end
+
+ class Sender < Requester::Sender
+ def initialize(msg, data, sock, host, port, queue)
+ super(msg, data, sock, queue)
+ @host = host
+ @port = port
+ end
+
+ def send
+ @sock.send(@msg, 0, @host, @port)
+ end
+ end
+ end
+
+ class ConnectedUDP < Requester
+ def initialize(host, port=Port)
+ super()
+ @host = host
+ @port = port
+ @sock = UDPSocket.new(host.index(':') ? Socket::AF_INET6 : Socket::AF_INET)
+ @sock.connect(host, port)
+ @sock.fcntl(Fcntl::F_SETFD, 1) if defined? Fcntl::F_SETFD
+ @id = -1
+ @thread = Thread.new {
+ DNSThreadGroup.add Thread.current
+ loop {
+ reply = @sock.recv(UDPSize)
+ msg = begin
+ Message.decode(reply)
+ rescue DecodeError
+ STDERR.print("DNS message decoding error: #{reply.inspect}")
+ next
+ end
+ if s = @senders[msg.id]
+ s.recv msg
+ else
+ #STDERR.print("non-handled DNS message: #{msg.inspect}")
+ end
+ }
+ }
+ end
+
+ def sender(msg, data, queue, host=@host, port=@port)
+ unless host == @host && port == @port
+ raise RequestError.new("host/port don't match: #{host}:#{port}")
+ end
+ id = Thread.exclusive { @id = (@id + 1) & 0xffff }
+ request = msg.encode
+ request[0,2] = [id].pack('n')
+ return @senders[id] = Sender.new(request, data, @sock, queue)
+ end
+
+ class Sender < Requester::Sender
+ def send
+ @sock.send(@msg, 0)
+ end
+ end
+ end
+
+ class TCP < Requester
+ def initialize(host, port=Port)
+ super()
+ @host = host
+ @port = port
+ @sock = TCPSocket.new
+ @sock.connect(host, port)
+ @sock.fcntl(Fcntl::F_SETFD, 1) if defined? Fcntl::F_SETFD
+ @id = -1
+ @senders = {}
+ @thread = Thread.new {
+ DNSThreadGroup.add Thread.current
+ loop {
+ len = @sock.read(2).unpack('n')
+ reply = @sock.read(len)
+ msg = begin
+ Message.decode(reply)
+ rescue DecodeError
+ STDERR.print("DNS message decoding error: #{reply.inspect}")
+ next
+ end
+ if s = @senders[msg.id]
+ s.push msg
+ else
+ #STDERR.print("non-handled DNS message: #{msg.inspect}")
+ end
+ }
+ }
+ end
+
+ def sender(msg, data, queue, host=@host, port=@port)
+ unless host == @host && port == @port
+ raise RequestError.new("host/port don't match: #{host}:#{port}")
+ end
+ id = Thread.exclusive { @id = (@id + 1) & 0xffff }
+ request = msg.encode
+ request[0,2] = [request.length, id].pack('nn')
+ return @senders[id] = Sender.new(request, data, @sock, queue)
+ end
+
+ class Sender < Requester::Sender
+ def send
+ @sock.print(@msg)
+ @sock.flush
+ end
+ end
+ end
+
+ class RequestError < StandardError
+ end
+ end
+
+ class Config
+ def initialize(config_info=nil)
+ @mutex = Mutex.new
+ @config_info = config_info
+ @initialized = nil
+ end
+
+ def Config.parse_resolv_conf(filename)
+ nameserver = []
+ search = nil
+ ndots = 1
+ open(filename) {|f|
+ f.each {|line|
+ line.sub!(/[#;].*/, '')
+ keyword, *args = line.split(/\s+/)
+ args.each { |arg|
+ arg.untaint
+ }
+ next unless keyword
+ case keyword
+ when 'nameserver'
+ nameserver += args
+ when 'domain'
+ next if args.empty?
+ search = [args[0]]
+ when 'search'
+ next if args.empty?
+ search = args
+ when 'options'
+ args.each {|arg|
+ case arg
+ when /\Andots:(\d+)\z/
+ ndots = $1.to_i
+ end
+ }
+ end
+ }
+ }
+ return { :nameserver => nameserver, :search => search, :ndots => ndots }
+ end
+
+ def Config.default_config_hash(filename="/etc/resolv.conf")
+ if File.exist? filename
+ config_hash = Config.parse_resolv_conf(filename)
+ else
+ if /mswin32|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM
+ search, nameserver = Win32::Resolv.get_resolv_info
+ config_hash = {}
+ config_hash[:nameserver] = nameserver if nameserver
+ config_hash[:search] = [search].flatten if search
+ end
+ end
+ config_hash
+ end
+
+ def lazy_initialize
+ @mutex.synchronize {
+ unless @initialized
+ @nameserver = []
+ @search = nil
+ @ndots = 1
+ case @config_info
+ when nil
+ config_hash = Config.default_config_hash
+ when String
+ config_hash = Config.parse_resolv_conf(@config_info)
+ when Hash
+ config_hash = @config_info.dup
+ if String === config_hash[:nameserver]
+ config_hash[:nameserver] = [config_hash[:nameserver]]
+ end
+ if String === config_hash[:search]
+ config_hash[:search] = [config_hash[:search]]
+ end
+ else
+ raise ArgumentError.new("invalid resolv configuration: #{@config_info.inspect}")
+ end
+ @nameserver = config_hash[:nameserver] if config_hash.include? :nameserver
+ @search = config_hash[:search] if config_hash.include? :search
+ @ndots = config_hash[:ndots] if config_hash.include? :ndots
+
+ @nameserver = ['0.0.0.0'] if @nameserver.empty?
+ if @search
+ @search = @search.map {|arg| Label.split(arg) }
+ else
+ hostname = Socket.gethostname
+ if /\./ =~ hostname
+ @search = [Label.split($')]
+ else
+ @search = [[]]
+ end
+ end
+
+ if !@nameserver.kind_of?(Array) ||
+ !@nameserver.all? {|ns| String === ns }
+ raise ArgumentError.new("invalid nameserver config: #{@nameserver.inspect}")
+ end
+
+ if !@search.kind_of?(Array) ||
+ !@search.all? {|ls| ls.all? {|l| Label::Str === l } }
+ raise ArgumentError.new("invalid search config: #{@search.inspect}")
+ end
+
+ if !@ndots.kind_of?(Integer)
+ raise ArgumentError.new("invalid ndots config: #{@ndots.inspect}")
+ end
+
+ @initialized = true
+ end
+ }
+ self
+ end
+
+ def single?
+ lazy_initialize
+ if @nameserver.length == 1
+ return @nameserver[0]
+ else
+ return nil
+ end
+ end
+
+ def generate_candidates(name)
+ candidates = nil
+ name = Name.create(name)
+ if name.absolute?
+ candidates = [name]
+ else
+ if @ndots <= name.length - 1
+ candidates = [Name.new(name.to_a)]
+ else
+ candidates = []
+ end
+ candidates.concat(@search.map {|domain| Name.new(name.to_a + domain)})
+ end
+ return candidates
+ end
+
+ InitialTimeout = 5
+
+ def generate_timeouts
+ ts = [InitialTimeout]
+ ts << ts[-1] * 2 / @nameserver.length
+ ts << ts[-1] * 2
+ ts << ts[-1] * 2
+ return ts
+ end
+
+ def resolv(name)
+ candidates = generate_candidates(name)
+ timeouts = generate_timeouts
+ begin
+ candidates.each {|candidate|
+ begin
+ timeouts.each {|tout|
+ @nameserver.each {|nameserver|
+ begin
+ yield candidate, tout, nameserver
+ rescue ResolvTimeout
+ end
+ }
+ }
+ raise ResolvError.new("DNS resolv timeout: #{name}")
+ rescue NXDomain
+ end
+ }
+ rescue ResolvError
+ end
+ end
+
+ class NXDomain < ResolvError
+ end
+
+ class OtherResolvError < ResolvError
+ end
+ end
+
+ module OpCode
+ Query = 0
+ IQuery = 1
+ Status = 2
+ Notify = 4
+ Update = 5
+ end
+
+ module RCode
+ NoError = 0
+ FormErr = 1
+ ServFail = 2
+ NXDomain = 3
+ NotImp = 4
+ Refused = 5
+ YXDomain = 6
+ YXRRSet = 7
+ NXRRSet = 8
+ NotAuth = 9
+ NotZone = 10
+ BADVERS = 16
+ BADSIG = 16
+ BADKEY = 17
+ BADTIME = 18
+ BADMODE = 19
+ BADNAME = 20
+ BADALG = 21
+ end
+
+ class DecodeError < StandardError
+ end
+
+ class EncodeError < StandardError
+ end
+
+ module Label
+ def self.split(arg)
+ labels = []
+ arg.scan(/[^\.]+/) {labels << Str.new($&)}
+ return labels
+ end
+
+ class Str
+ def initialize(string)
+ @string = string
+ @downcase = string.downcase
+ end
+ attr_reader :string, :downcase
+
+ def to_s
+ return @string
+ end
+
+ def inspect
+ return "#<#{self.class} #{self.to_s}>"
+ end
+
+ def ==(other)
+ return @downcase == other.downcase
+ end
+
+ def eql?(other)
+ return self == other
+ end
+
+ def hash
+ return @downcase.hash
+ end
+ end
+ end
+
+ class Name
+ def self.create(arg)
+ case arg
+ when Name
+ return arg
+ when String
+ return Name.new(Label.split(arg), /\.\z/ =~ arg ? true : false)
+ else
+ raise ArgumentError.new("cannot interpret as DNS name: #{arg.inspect}")
+ end
+ end
+
+ def initialize(labels, absolute=true)
+ @labels = labels
+ @absolute = absolute
+ end
+
+ def inspect
+ "#<#{self.class}: #{self.to_s}#{@absolute ? '.' : ''}>"
+ end
+
+ def absolute?
+ return @absolute
+ end
+
+ def ==(other)
+ return false unless Name === other
+ return @labels == other.to_a && @absolute == other.absolute?
+ end
+ alias eql? ==
+
+ # tests subdomain-of relation.
+ #
+ # domain = Resolv::DNS::Name.create("y.z")
+ # p Resolv::DNS::Name.create("w.x.y.z").subdomain_of?(domain) #=> true
+ # p Resolv::DNS::Name.create("x.y.z").subdomain_of?(domain) #=> true
+ # p Resolv::DNS::Name.create("y.z").subdomain_of?(domain) #=> false
+ # p Resolv::DNS::Name.create("z").subdomain_of?(domain) #=> false
+ # p Resolv::DNS::Name.create("x.y.z.").subdomain_of?(domain) #=> false
+ # p Resolv::DNS::Name.create("w.z").subdomain_of?(domain) #=> false
+ #
+ def subdomain_of?(other)
+ raise ArgumentError, "not a domain name: #{other.inspect}" unless Name === other
+ return false if @absolute != other.absolute?
+ other_len = other.length
+ return false if @labels.length <= other_len
+ return @labels[-other_len, other_len] == other.to_a
+ end
+
+ def hash
+ return @labels.hash ^ @absolute.hash
+ end
+
+ def to_a
+ return @labels
+ end
+
+ def length
+ return @labels.length
+ end
+
+ def [](i)
+ return @labels[i]
+ end
+
+ # returns the domain name as a string.
+ #
+ # The domain name doesn't have a trailing dot even if the name object is
+ # absolute.
+ #
+ # p Resolv::DNS::Name.create("x.y.z.").to_s #=> "x.y.z"
+ # p Resolv::DNS::Name.create("x.y.z").to_s #=> "x.y.z"
+ #
+ def to_s
+ return @labels.join('.')
+ end
+ end
+
+ class Message
+ @@identifier = -1
+
+ def initialize(id = (@@identifier += 1) & 0xffff)
+ @id = id
+ @qr = 0
+ @opcode = 0
+ @aa = 0
+ @tc = 0
+ @rd = 0 # recursion desired
+ @ra = 0 # recursion available
+ @rcode = 0
+ @question = []
+ @answer = []
+ @authority = []
+ @additional = []
+ end
+
+ attr_accessor :id, :qr, :opcode, :aa, :tc, :rd, :ra, :rcode
+ attr_reader :question, :answer, :authority, :additional
+
+ def ==(other)
+ return @id == other.id &&
+ @qr == other.qr &&
+ @opcode == other.opcode &&
+ @aa == other.aa &&
+ @tc == other.tc &&
+ @rd == other.rd &&
+ @ra == other.ra &&
+ @rcode == other.rcode &&
+ @question == other.question &&
+ @answer == other.answer &&
+ @authority == other.authority &&
+ @additional == other.additional
+ end
+
+ def add_question(name, typeclass)
+ @question << [Name.create(name), typeclass]
+ end
+
+ def each_question
+ @question.each {|name, typeclass|
+ yield name, typeclass
+ }
+ end
+
+ def add_answer(name, ttl, data)
+ @answer << [Name.create(name), ttl, data]
+ end
+
+ def each_answer
+ @answer.each {|name, ttl, data|
+ yield name, ttl, data
+ }
+ end
+
+ def add_authority(name, ttl, data)
+ @authority << [Name.create(name), ttl, data]
+ end
+
+ def each_authority
+ @authority.each {|name, ttl, data|
+ yield name, ttl, data
+ }
+ end
+
+ def add_additional(name, ttl, data)
+ @additional << [Name.create(name), ttl, data]
+ end
+
+ def each_additional
+ @additional.each {|name, ttl, data|
+ yield name, ttl, data
+ }
+ end
+
+ def each_resource
+ each_answer {|name, ttl, data| yield name, ttl, data}
+ each_authority {|name, ttl, data| yield name, ttl, data}
+ each_additional {|name, ttl, data| yield name, ttl, data}
+ end
+
+ def encode
+ return MessageEncoder.new {|msg|
+ msg.put_pack('nnnnnn',
+ @id,
+ (@qr & 1) << 15 |
+ (@opcode & 15) << 11 |
+ (@aa & 1) << 10 |
+ (@tc & 1) << 9 |
+ (@rd & 1) << 8 |
+ (@ra & 1) << 7 |
+ (@rcode & 15),
+ @question.length,
+ @answer.length,
+ @authority.length,
+ @additional.length)
+ @question.each {|q|
+ name, typeclass = q
+ msg.put_name(name)
+ msg.put_pack('nn', typeclass::TypeValue, typeclass::ClassValue)
+ }
+ [@answer, @authority, @additional].each {|rr|
+ rr.each {|r|
+ name, ttl, data = r
+ msg.put_name(name)
+ msg.put_pack('nnN', data.class::TypeValue, data.class::ClassValue, ttl)
+ msg.put_length16 {data.encode_rdata(msg)}
+ }
+ }
+ }.to_s
+ end
+
+ class MessageEncoder
+ def initialize
+ @data = ''
+ @names = {}
+ yield self
+ end
+
+ def to_s
+ return @data
+ end
+
+ def put_bytes(d)
+ @data << d
+ end
+
+ def put_pack(template, *d)
+ @data << d.pack(template)
+ end
+
+ def put_length16
+ length_index = @data.length
+ @data << "\0\0"
+ data_start = @data.length
+ yield
+ data_end = @data.length
+ @data[length_index, 2] = [data_end - data_start].pack("n")
+ end
+
+ def put_string(d)
+ self.put_pack("C", d.length)
+ @data << d
+ end
+
+ def put_string_list(ds)
+ ds.each {|d|
+ self.put_string(d)
+ }
+ end
+
+ def put_name(d)
+ put_labels(d.to_a)
+ end
+
+ def put_labels(d)
+ d.each_index {|i|
+ domain = d[i..-1]
+ if idx = @names[domain]
+ self.put_pack("n", 0xc000 | idx)
+ return
+ else
+ @names[domain] = @data.length
+ self.put_label(d[i])
+ end
+ }
+ @data << "\0"
+ end
+
+ def put_label(d)
+ self.put_string(d.string)
+ end
+ end
+
+ def Message.decode(m)
+ o = Message.new(0)
+ MessageDecoder.new(m) {|msg|
+ id, flag, qdcount, ancount, nscount, arcount =
+ msg.get_unpack('nnnnnn')
+ o.id = id
+ o.qr = (flag >> 15) & 1
+ o.opcode = (flag >> 11) & 15
+ o.aa = (flag >> 10) & 1
+ o.tc = (flag >> 9) & 1
+ o.rd = (flag >> 8) & 1
+ o.ra = (flag >> 7) & 1
+ o.rcode = flag & 15
+ (1..qdcount).each {
+ name, typeclass = msg.get_question
+ o.add_question(name, typeclass)
+ }
+ (1..ancount).each {
+ name, ttl, data = msg.get_rr
+ o.add_answer(name, ttl, data)
+ }
+ (1..nscount).each {
+ name, ttl, data = msg.get_rr
+ o.add_authority(name, ttl, data)
+ }
+ (1..arcount).each {
+ name, ttl, data = msg.get_rr
+ o.add_additional(name, ttl, data)
+ }
+ }
+ return o
+ end
+
+ class MessageDecoder
+ def initialize(data)
+ @data = data
+ @index = 0
+ @limit = data.length
+ yield self
+ end
+
+ def get_length16
+ len, = self.get_unpack('n')
+ save_limit = @limit
+ @limit = @index + len
+ d = yield(len)
+ if @index < @limit
+ raise DecodeError.new("junk exists")
+ elsif @limit < @index
+ raise DecodeError.new("limit exceeded")
+ end
+ @limit = save_limit
+ return d
+ end
+
+ def get_bytes(len = @limit - @index)
+ d = @data[@index, len]
+ @index += len
+ return d
+ end
+
+ def get_unpack(template)
+ len = 0
+ template.each_byte {|byte|
+ case byte
+ when ?c, ?C
+ len += 1
+ when ?n
+ len += 2
+ when ?N
+ len += 4
+ else
+ raise StandardError.new("unsupported template: '#{byte.chr}' in '#{template}'")
+ end
+ }
+ raise DecodeError.new("limit exceeded") if @limit < @index + len
+ arr = @data.unpack("@#{@index}#{template}")
+ @index += len
+ return arr
+ end
+
+ def get_string
+ len = @data[@index]
+ raise DecodeError.new("limit exceeded") if @limit < @index + 1 + len
+ d = @data[@index + 1, len]
+ @index += 1 + len
+ return d
+ end
+
+ def get_string_list
+ strings = []
+ while @index < @limit
+ strings << self.get_string
+ end
+ strings
+ end
+
+ def get_name
+ return Name.new(self.get_labels)
+ end
+
+ def get_labels(limit=nil)
+ limit = @index if !limit || @index < limit
+ d = []
+ while true
+ case @data[@index]
+ when 0
+ @index += 1
+ return d
+ when 192..255
+ idx = self.get_unpack('n')[0] & 0x3fff
+ if limit <= idx
+ raise DecodeError.new("non-backward name pointer")
+ end
+ save_index = @index
+ @index = idx
+ d += self.get_labels(limit)
+ @index = save_index
+ return d
+ else
+ d << self.get_label
+ end
+ end
+ return d
+ end
+
+ def get_label
+ return Label::Str.new(self.get_string)
+ end
+
+ def get_question
+ name = self.get_name
+ type, klass = self.get_unpack("nn")
+ return name, Resource.get_class(type, klass)
+ end
+
+ def get_rr
+ name = self.get_name
+ type, klass, ttl = self.get_unpack('nnN')
+ typeclass = Resource.get_class(type, klass)
+ return name, ttl, self.get_length16 {typeclass.decode_rdata(self)}
+ end
+ end
+ end
+
+ class Query
+ def encode_rdata(msg)
+ raise EncodeError.new("#{self.class} is query.")
+ end
+
+ def self.decode_rdata(msg)
+ raise DecodeError.new("#{self.class} is query.")
+ end
+ end
+
+ class Resource < Query
+ ClassHash = {}
+
+ def encode_rdata(msg)
+ raise NotImplementedError.new
+ end
+
+ def self.decode_rdata(msg)
+ raise NotImplementedError.new
+ end
+
+ def ==(other)
+ return self.class == other.class &&
+ self.instance_variables == other.instance_variables &&
+ self.instance_variables.collect {|name| self.instance_eval name} ==
+ other.instance_variables.collect {|name| other.instance_eval name}
+ end
+
+ def eql?(other)
+ return self == other
+ end
+
+ def hash
+ h = 0
+ self.instance_variables.each {|name|
+ h ^= self.instance_eval("#{name}.hash")
+ }
+ return h
+ end
+
+ def self.get_class(type_value, class_value)
+ return ClassHash[[type_value, class_value]] ||
+ Generic.create(type_value, class_value)
+ end
+
+ class Generic < Resource
+ def initialize(data)
+ @data = data
+ end
+ attr_reader :data
+
+ def encode_rdata(msg)
+ msg.put_bytes(data)
+ end
+
+ def self.decode_rdata(msg)
+ return self.new(msg.get_bytes)
+ end
+
+ def self.create(type_value, class_value)
+ c = Class.new(Generic)
+ c.const_set(:TypeValue, type_value)
+ c.const_set(:ClassValue, class_value)
+ Generic.const_set("Type#{type_value}_Class#{class_value}", c)
+ ClassHash[[type_value, class_value]] = c
+ return c
+ end
+ end
+
+ class DomainName < Resource
+ def initialize(name)
+ @name = name
+ end
+ attr_reader :name
+
+ def encode_rdata(msg)
+ msg.put_name(@name)
+ end
+
+ def self.decode_rdata(msg)
+ return self.new(msg.get_name)
+ end
+ end
+
+ # Standard (class generic) RRs
+ ClassValue = nil
+
+ class NS < DomainName
+ TypeValue = 2
+ end
+
+ class CNAME < DomainName
+ TypeValue = 5
+ end
+
+ class SOA < Resource
+ TypeValue = 6
+
+ def initialize(mname, rname, serial, refresh, retry_, expire, minimum)
+ @mname = mname
+ @rname = rname
+ @serial = serial
+ @refresh = refresh
+ @retry = retry_
+ @expire = expire
+ @minimum = minimum
+ end
+ attr_reader :mname, :rname, :serial, :refresh, :retry, :expire, :minimum
+
+ def encode_rdata(msg)
+ msg.put_name(@mname)
+ msg.put_name(@rname)
+ msg.put_pack('NNNNN', @serial, @refresh, @retry, @expire, @minimum)
+ end
+
+ def self.decode_rdata(msg)
+ mname = msg.get_name
+ rname = msg.get_name
+ serial, refresh, retry_, expire, minimum = msg.get_unpack('NNNNN')
+ return self.new(
+ mname, rname, serial, refresh, retry_, expire, minimum)
+ end
+ end
+
+ class PTR < DomainName
+ TypeValue = 12
+ end
+
+ class HINFO < Resource
+ TypeValue = 13
+
+ def initialize(cpu, os)
+ @cpu = cpu
+ @os = os
+ end
+ attr_reader :cpu, :os
+
+ def encode_rdata(msg)
+ msg.put_string(@cpu)
+ msg.put_string(@os)
+ end
+
+ def self.decode_rdata(msg)
+ cpu = msg.get_string
+ os = msg.get_string
+ return self.new(cpu, os)
+ end
+ end
+
+ class MINFO < Resource
+ TypeValue = 14
+
+ def initialize(rmailbx, emailbx)
+ @rmailbx = rmailbx
+ @emailbx = emailbx
+ end
+ attr_reader :rmailbx, :emailbx
+
+ def encode_rdata(msg)
+ msg.put_name(@rmailbx)
+ msg.put_name(@emailbx)
+ end
+
+ def self.decode_rdata(msg)
+ rmailbx = msg.get_string
+ emailbx = msg.get_string
+ return self.new(rmailbx, emailbx)
+ end
+ end
+
+ class MX < Resource
+ TypeValue= 15
+
+ def initialize(preference, exchange)
+ @preference = preference
+ @exchange = exchange
+ end
+ attr_reader :preference, :exchange
+
+ def encode_rdata(msg)
+ msg.put_pack('n', @preference)
+ msg.put_name(@exchange)
+ end
+
+ def self.decode_rdata(msg)
+ preference, = msg.get_unpack('n')
+ exchange = msg.get_name
+ return self.new(preference, exchange)
+ end
+ end
+
+ class TXT < Resource
+ TypeValue = 16
+
+ def initialize(first_string, *rest_strings)
+ @strings = [first_string, *rest_strings]
+ end
+ attr_reader :strings
+
+ def data
+ @strings[0]
+ end
+
+ def encode_rdata(msg)
+ msg.put_string_list(@strings)
+ end
+
+ def self.decode_rdata(msg)
+ strings = msg.get_string_list
+ return self.new(*strings)
+ end
+ end
+
+ class ANY < Query
+ TypeValue = 255
+ end
+
+ ClassInsensitiveTypes = [
+ NS, CNAME, SOA, PTR, HINFO, MINFO, MX, TXT, ANY
+ ]
+
+ # ARPA Internet specific RRs
+ module IN
+ ClassValue = 1
+
+ ClassInsensitiveTypes.each {|s|
+ c = Class.new(s)
+ c.const_set(:TypeValue, s::TypeValue)
+ c.const_set(:ClassValue, ClassValue)
+ ClassHash[[s::TypeValue, ClassValue]] = c
+ self.const_set(s.name.sub(/.*::/, ''), c)
+ }
+
+ class A < Resource
+ ClassHash[[TypeValue = 1, ClassValue = ClassValue]] = self
+
+ def initialize(address)
+ @address = IPv4.create(address)
+ end
+ attr_reader :address
+
+ def encode_rdata(msg)
+ msg.put_bytes(@address.address)
+ end
+
+ def self.decode_rdata(msg)
+ return self.new(IPv4.new(msg.get_bytes(4)))
+ end
+ end
+
+ class WKS < Resource
+ ClassHash[[TypeValue = 11, ClassValue = ClassValue]] = self
+
+ def initialize(address, protocol, bitmap)
+ @address = IPv4.create(address)
+ @protocol = protocol
+ @bitmap = bitmap
+ end
+ attr_reader :address, :protocol, :bitmap
+
+ def encode_rdata(msg)
+ msg.put_bytes(@address.address)
+ msg.put_pack("n", @protocol)
+ msg.put_bytes(@bitmap)
+ end
+
+ def self.decode_rdata(msg)
+ address = IPv4.new(msg.get_bytes(4))
+ protocol, = msg.get_unpack("n")
+ bitmap = msg.get_bytes
+ return self.new(address, protocol, bitmap)
+ end
+ end
+
+ class AAAA < Resource
+ ClassHash[[TypeValue = 28, ClassValue = ClassValue]] = self
+
+ def initialize(address)
+ @address = IPv6.create(address)
+ end
+ attr_reader :address
+
+ def encode_rdata(msg)
+ msg.put_bytes(@address.address)
+ end
+
+ def self.decode_rdata(msg)
+ return self.new(IPv6.new(msg.get_bytes(16)))
+ end
+ end
+
+ # SRV resource record defined in RFC 2782
+ #
+ # These records identify the hostname and port that a service is
+ # available at.
+ #
+ # The format is:
+ # _Service._Proto.Name TTL Class SRV Priority Weight Port Target
+ #
+ # The fields specific to SRV are defined in RFC 2782 as meaning:
+ # - +priority+ The priority of this target host. A client MUST attempt
+ # to contact the target host with the lowest-numbered priority it can
+ # reach; target hosts with the same priority SHOULD be tried in an
+ # order defined by the weight field. The range is 0-65535. Note that
+ # it is not widely implemented and should be set to zero.
+ #
+ # - +weight+ A server selection mechanism. The weight field specifies
+ # a relative weight for entries with the same priority. Larger weights
+ # SHOULD be given a proportionately higher probability of being
+ # selected. The range of this number is 0-65535. Domain administrators
+ # SHOULD use Weight 0 when there isn't any server selection to do, to
+ # make the RR easier to read for humans (less noisy). Note that it is
+ # not widely implemented and should be set to zero.
+ #
+ # - +port+ The port on this target host of this service. The range is 0-
+ # 65535.
+ #
+ # - +target+ The domain name of the target host. A target of "." means
+ # that the service is decidedly not available at this domain.
+ class SRV < Resource
+ ClassHash[[TypeValue = 33, ClassValue = ClassValue]] = self
+
+ # Create a SRV resource record.
+ def initialize(priority, weight, port, target)
+ @priority = priority.to_int
+ @weight = weight.to_int
+ @port = port.to_int
+ @target = Name.create(target)
+ end
+
+ attr_reader :priority, :weight, :port, :target
+
+ def encode_rdata(msg)
+ msg.put_pack("n", @priority)
+ msg.put_pack("n", @weight)
+ msg.put_pack("n", @port)
+ msg.put_name(@target)
+ end
+
+ def self.decode_rdata(msg)
+ priority, = msg.get_unpack("n")
+ weight, = msg.get_unpack("n")
+ port, = msg.get_unpack("n")
+ target = msg.get_name
+ return self.new(priority, weight, port, target)
+ end
+ end
+
+ end
+ end
+ end
+
+ class IPv4
+ Regex = /\A(\d+)\.(\d+)\.(\d+)\.(\d+)\z/
+
+ def self.create(arg)
+ case arg
+ when IPv4
+ return arg
+ when Regex
+ if (0..255) === (a = $1.to_i) &&
+ (0..255) === (b = $2.to_i) &&
+ (0..255) === (c = $3.to_i) &&
+ (0..255) === (d = $4.to_i)
+ return self.new([a, b, c, d].pack("CCCC"))
+ else
+ raise ArgumentError.new("IPv4 address with invalid value: " + arg)
+ end
+ else
+ raise ArgumentError.new("cannot interpret as IPv4 address: #{arg.inspect}")
+ end
+ end
+
+ def initialize(address)
+ unless address.kind_of?(String) && address.length == 4
+ raise ArgumentError.new('IPv4 address must be 4 bytes')
+ end
+ @address = address
+ end
+ attr_reader :address
+
+ def to_s
+ return sprintf("%d.%d.%d.%d", *@address.unpack("CCCC"))
+ end
+
+ def inspect
+ return "#<#{self.class} #{self.to_s}>"
+ end
+
+ def to_name
+ return DNS::Name.create(
+ '%d.%d.%d.%d.in-addr.arpa.' % @address.unpack('CCCC').reverse)
+ end
+
+ def ==(other)
+ return @address == other.address
+ end
+
+ def eql?(other)
+ return self == other
+ end
+
+ def hash
+ return @address.hash
+ end
+ end
+
+ class IPv6
+ Regex_8Hex = /\A
+ (?:[0-9A-Fa-f]{1,4}:){7}
+ [0-9A-Fa-f]{1,4}
+ \z/x
+
+ Regex_CompressedHex = /\A
+ ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::
+ ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)
+ \z/x
+
+ Regex_6Hex4Dec = /\A
+ ((?:[0-9A-Fa-f]{1,4}:){6,6})
+ (\d+)\.(\d+)\.(\d+)\.(\d+)
+ \z/x
+
+ Regex_CompressedHex4Dec = /\A
+ ((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::
+ ((?:[0-9A-Fa-f]{1,4}:)*)
+ (\d+)\.(\d+)\.(\d+)\.(\d+)
+ \z/x
+
+ Regex = /
+ (?:#{Regex_8Hex}) |
+ (?:#{Regex_CompressedHex}) |
+ (?:#{Regex_6Hex4Dec}) |
+ (?:#{Regex_CompressedHex4Dec})/x
+
+ def self.create(arg)
+ case arg
+ when IPv6
+ return arg
+ when String
+ address = ''
+ if Regex_8Hex =~ arg
+ arg.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')}
+ elsif Regex_CompressedHex =~ arg
+ prefix = $1
+ suffix = $2
+ a1 = ''
+ a2 = ''
+ prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')}
+ suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')}
+ omitlen = 16 - a1.length - a2.length
+ address << a1 << "\0" * omitlen << a2
+ elsif Regex_6Hex4Dec =~ arg
+ prefix, a, b, c, d = $1, $2.to_i, $3.to_i, $4.to_i, $5.to_i
+ if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d
+ prefix.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')}
+ address << [a, b, c, d].pack('CCCC')
+ else
+ raise ArgumentError.new("not numeric IPv6 address: " + arg)
+ end
+ elsif Regex_CompressedHex4Dec =~ arg
+ prefix, suffix, a, b, c, d = $1, $2, $3.to_i, $4.to_i, $5.to_i, $6.to_i
+ if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d
+ a1 = ''
+ a2 = ''
+ prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')}
+ suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')}
+ omitlen = 12 - a1.length - a2.length
+ address << a1 << "\0" * omitlen << a2 << [a, b, c, d].pack('CCCC')
+ else
+ raise ArgumentError.new("not numeric IPv6 address: " + arg)
+ end
+ else
+ raise ArgumentError.new("not numeric IPv6 address: " + arg)
+ end
+ return IPv6.new(address)
+ else
+ raise ArgumentError.new("cannot interpret as IPv6 address: #{arg.inspect}")
+ end
+ end
+
+ def initialize(address)
+ unless address.kind_of?(String) && address.length == 16
+ raise ArgumentError.new('IPv6 address must be 16 bytes')
+ end
+ @address = address
+ end
+ attr_reader :address
+
+ def to_s
+ address = sprintf("%X:%X:%X:%X:%X:%X:%X:%X", *@address.unpack("nnnnnnnn"))
+ unless address.sub!(/(^|:)0(:0)+(:|$)/, '::')
+ address.sub!(/(^|:)0(:|$)/, '::')
+ end
+ return address
+ end
+
+ def inspect
+ return "#<#{self.class} #{self.to_s}>"
+ end
+
+ def to_name
+ # ip6.arpa should be searched too. [RFC3152]
+ return DNS::Name.new(
+ @address.unpack("H32")[0].split(//).reverse + ['ip6', 'int'])
+ end
+
+ def ==(other)
+ return @address == other.address
+ end
+
+ def eql?(other)
+ return self == other
+ end
+
+ def hash
+ return @address.hash
+ end
+ end
+
+ DefaultResolver = self.new
+ AddressRegex = /(?:#{IPv4::Regex})|(?:#{IPv6::Regex})/
+end
diff --git a/ruby_1_8_5/lib/rexml/attlistdecl.rb b/ruby_1_8_5/lib/rexml/attlistdecl.rb
new file mode 100644
index 0000000000..d4b5c38af6
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/attlistdecl.rb
@@ -0,0 +1,62 @@
+#vim:ts=2 sw=2 noexpandtab:
+require 'rexml/child'
+require 'rexml/source'
+
+module REXML
+ # This class needs:
+ # * Documentation
+ # * Work! Not all types of attlists are intelligently parsed, so we just
+ # spew back out what we get in. This works, but it would be better if
+ # we formatted the output ourselves.
+ #
+ # AttlistDecls provide *just* enough support to allow namespace
+ # declarations. If you need some sort of generalized support, or have an
+ # interesting idea about how to map the hideous, terrible design of DTD
+ # AttlistDecls onto an intuitive Ruby interface, let me know. I'm desperate
+ # for anything to make DTDs more palateable.
+ class AttlistDecl < Child
+ include Enumerable
+
+ # What is this? Got me.
+ attr_reader :element_name
+
+ # Create an AttlistDecl, pulling the information from a Source. Notice
+ # that this isn't very convenient; to create an AttlistDecl, you basically
+ # have to format it yourself, and then have the initializer parse it.
+ # Sorry, but for the forseeable future, DTD support in REXML is pretty
+ # weak on convenience. Have I mentioned how much I hate DTDs?
+ def initialize(source)
+ super()
+ if (source.kind_of? Array)
+ @element_name, @pairs, @contents = *source
+ end
+ end
+
+ # Access the attlist attribute/value pairs.
+ # value = attlist_decl[ attribute_name ]
+ def [](key)
+ @pairs[key]
+ end
+
+ # Whether an attlist declaration includes the given attribute definition
+ # if attlist_decl.include? "xmlns:foobar"
+ def include?(key)
+ @pairs.keys.include? key
+ end
+
+ # Itterate over the key/value pairs:
+ # attlist_decl.each { |attribute_name, attribute_value| ... }
+ def each(&block)
+ @pairs.each(&block)
+ end
+
+ # Write out exactly what we got in.
+ def write out, indent=-1
+ out << @contents
+ end
+
+ def node_type
+ :attlistdecl
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/attribute.rb b/ruby_1_8_5/lib/rexml/attribute.rb
new file mode 100644
index 0000000000..a169148f32
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/attribute.rb
@@ -0,0 +1,163 @@
+require "rexml/namespace"
+require 'rexml/text'
+
+module REXML
+ # Defines an Element Attribute; IE, a attribute=value pair, as in:
+ # <element attribute="value"/>. Attributes can be in their own
+ # namespaces. General users of REXML will not interact with the
+ # Attribute class much.
+ class Attribute
+ include Node
+ include Namespace
+
+ # The element to which this attribute belongs
+ attr_reader :element
+ # The normalized value of this attribute. That is, the attribute with
+ # entities intact.
+ attr_writer :normalized
+ PATTERN = /\s*(#{NAME_STR})\s*=\s*(["'])(.*?)\2/um
+
+ # Constructor.
+ #
+ # Attribute.new( attribute_to_clone )
+ # Attribute.new( source )
+ # Attribute.new( "attr", "attr_value" )
+ # Attribute.new( "attr", "attr_value", parent_element )
+ def initialize( first, second=nil, parent=nil )
+ @normalized = @unnormalized = @element = nil
+ if first.kind_of? Attribute
+ self.name = first.expanded_name
+ @value = first.value
+ if second.kind_of? Element
+ @element = second
+ else
+ @element = first.element
+ end
+ elsif first.kind_of? String
+ @element = parent if parent.kind_of? Element
+ self.name = first
+ @value = second.to_s
+ else
+ raise "illegal argument #{first.class.name} to Attribute constructor"
+ end
+ end
+
+ # Returns the namespace of the attribute.
+ #
+ # e = Element.new( "elns:myelement" )
+ # e.add_attribute( "nsa:a", "aval" )
+ # e.add_attribute( "b", "bval" )
+ # e.attributes.get_attribute( "a" ).prefix # -> "nsa"
+ # e.attributes.get_attribute( "b" ).prefix # -> "elns"
+ # a = Attribute.new( "x", "y" )
+ # a.prefix # -> ""
+ def prefix
+ pf = super
+ if pf == ""
+ pf = @element.prefix if @element
+ end
+ pf
+ end
+
+ # Returns the namespace URL, if defined, or nil otherwise
+ #
+ # e = Element.new("el")
+ # e.add_attributes({"xmlns:ns", "http://url"})
+ # e.namespace( "ns" ) # -> "http://url"
+ def namespace arg=nil
+ arg = prefix if arg.nil?
+ @element.namespace arg
+ end
+
+ # Returns true if other is an Attribute and has the same name and value,
+ # false otherwise.
+ def ==( other )
+ other.kind_of?(Attribute) and other.name==name and other.value==@value
+ end
+
+ # Creates (and returns) a hash from both the name and value
+ def hash
+ name.hash + value.hash
+ end
+
+ # Returns this attribute out as XML source, expanding the name
+ #
+ # a = Attribute.new( "x", "y" )
+ # a.to_string # -> "x='y'"
+ # b = Attribute.new( "ns:x", "y" )
+ # b.to_string # -> "ns:x='y'"
+ def to_string
+ "#@expanded_name='#{to_s().gsub(/'/, '&apos;')}'"
+ end
+
+ # Returns the attribute value, with entities replaced
+ def to_s
+ return @normalized if @normalized
+
+ doctype = nil
+ if @element
+ doc = @element.document
+ doctype = doc.doctype if doc
+ end
+
+ @unnormalized = nil
+ @normalized = Text::normalize( @value, doctype )
+ end
+
+ # Returns the UNNORMALIZED value of this attribute. That is, entities
+ # have been expanded to their values
+ def value
+ return @unnormalized if @unnormalized
+ doctype = nil
+ if @element
+ doc = @element.document
+ doctype = doc.doctype if doc
+ end
+ @normalized = nil
+ @unnormalized = Text::unnormalize( @value, doctype )
+ end
+
+ # Returns a copy of this attribute
+ def clone
+ Attribute.new self
+ end
+
+ # Sets the element of which this object is an attribute. Normally, this
+ # is not directly called.
+ #
+ # Returns this attribute
+ def element=( element )
+ @element = element
+ self
+ end
+
+ # Removes this Attribute from the tree, and returns true if successfull
+ #
+ # This method is usually not called directly.
+ def remove
+ @element.attributes.delete self.name unless @element.nil?
+ end
+
+ # Writes this attribute (EG, puts 'key="value"' to the output)
+ def write( output, indent=-1 )
+ output << to_string
+ end
+
+ def node_type
+ :attribute
+ end
+
+ def inspect
+ rv = ""
+ write( rv )
+ rv
+ end
+
+ def xpath
+ path = @element.xpath
+ path += "/@#{self.expanded_name}"
+ return path
+ end
+ end
+end
+#vim:ts=2 sw=2 noexpandtab:
diff --git a/ruby_1_8_5/lib/rexml/cdata.rb b/ruby_1_8_5/lib/rexml/cdata.rb
new file mode 100644
index 0000000000..046012ba61
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/cdata.rb
@@ -0,0 +1,72 @@
+require "rexml/text"
+
+module REXML
+ class CData < Text
+ START = '<![CDATA['
+ STOP = ']]>'
+ ILLEGAL = /(\]\]>)/
+
+ # Constructor. CData is data between <![CDATA[ ... ]]>
+ #
+ # _Examples_
+ # CData.new( source )
+ # CData.new( "Here is some CDATA" )
+ # CData.new( "Some unprocessed data", respect_whitespace_TF, parent_element )
+ def initialize( first, whitespace=true, parent=nil )
+ super( first, whitespace, parent, true, true, ILLEGAL )
+ end
+
+ # Make a copy of this object
+ #
+ # _Examples_
+ # c = CData.new( "Some text" )
+ # d = c.clone
+ # d.to_s # -> "Some text"
+ def clone
+ CData.new self
+ end
+
+ # Returns the content of this CData object
+ #
+ # _Examples_
+ # c = CData.new( "Some text" )
+ # c.to_s # -> "Some text"
+ def to_s
+ @string
+ end
+
+ def value
+ @string
+ end
+
+ # Generates XML output of this object
+ #
+ # output::
+ # Where to write the string. Defaults to $stdout
+ # indent::
+ # An integer. If -1, no indenting will be used; otherwise, the
+ # indentation will be this number of spaces, and children will be
+ # indented an additional amount. Defaults to -1.
+ # transitive::
+ # If transitive is true and indent is >= 0, then the output will be
+ # pretty-printed in such a way that the added whitespace does not affect
+ # the absolute *value* of the document -- that is, it leaves the value
+ # and number of Text nodes in the document unchanged.
+ # ie_hack::
+ # Internet Explorer is the worst piece of crap to have ever been
+ # written, with the possible exception of Windows itself. Since IE is
+ # unable to parse proper XML, we have to provide a hack to generate XML
+ # that IE's limited abilities can handle. This hack inserts a space
+ # before the /> on empty tags.
+ #
+ # _Examples_
+ # c = CData.new( " Some text " )
+ # c.write( $stdout ) #-> <![CDATA[ Some text ]]>
+ def write( output=$stdout, indent=-1, transitive=false, ie_hack=false )
+ #indent( output, indent ) unless transitive
+ output << START
+ output << @string
+ output << STOP
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/child.rb b/ruby_1_8_5/lib/rexml/child.rb
new file mode 100644
index 0000000000..6d3c9df5e6
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/child.rb
@@ -0,0 +1,96 @@
+require "rexml/node"
+
+module REXML
+ ##
+ # A Child object is something contained by a parent, and this class
+ # contains methods to support that. Most user code will not use this
+ # class directly.
+ class Child
+ include Node
+ attr_reader :parent # The Parent of this object
+
+ # Constructor. Any inheritors of this class should call super to make
+ # sure this method is called.
+ # parent::
+ # if supplied, the parent of this child will be set to the
+ # supplied value, and self will be added to the parent
+ def initialize( parent = nil )
+ @parent = nil
+ # Declare @parent, but don't define it. The next line sets the
+ # parent.
+ parent.add( self ) if parent
+ end
+
+ # Replaces this object with another object. Basically, calls
+ # Parent.replace_child
+ #
+ # Returns:: self
+ def replace_with( child )
+ @parent.replace_child( self, child )
+ self
+ end
+
+ # Removes this child from the parent.
+ #
+ # Returns:: self
+ def remove
+ unless @parent.nil?
+ @parent.delete self
+ end
+ self
+ end
+
+ # Sets the parent of this child to the supplied argument.
+ #
+ # other::
+ # Must be a Parent object. If this object is the same object as the
+ # existing parent of this child, no action is taken. Otherwise, this
+ # child is removed from the current parent (if one exists), and is added
+ # to the new parent.
+ # Returns:: The parent added
+ def parent=( other )
+ return @parent if @parent == other
+ @parent.delete self if defined? @parent and @parent
+ @parent = other
+ end
+
+ alias :next_sibling :next_sibling_node
+ alias :previous_sibling :previous_sibling_node
+
+ # Sets the next sibling of this child. This can be used to insert a child
+ # after some other child.
+ # a = Element.new("a")
+ # b = a.add_element("b")
+ # c = Element.new("c")
+ # b.next_sibling = c
+ # # => <a><b/><c/></a>
+ def next_sibling=( other )
+ parent.insert_after self, other
+ end
+
+ # Sets the previous sibling of this child. This can be used to insert a
+ # child before some other child.
+ # a = Element.new("a")
+ # b = a.add_element("b")
+ # c = Element.new("c")
+ # b.previous_sibling = c
+ # # => <a><b/><c/></a>
+ def previous_sibling=(other)
+ parent.insert_before self, other
+ end
+
+ # Returns:: the document this child belongs to, or nil if this child
+ # belongs to no document
+ def document
+ return parent.document unless parent.nil?
+ nil
+ end
+
+ # This doesn't yet handle encodings
+ def bytes
+ encoding = document.encoding
+
+ to_s
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/comment.rb b/ruby_1_8_5/lib/rexml/comment.rb
new file mode 100644
index 0000000000..cb91d47f8c
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/comment.rb
@@ -0,0 +1,88 @@
+require "rexml/child"
+
+module REXML
+ ##
+ # Represents an XML comment; that is, text between \<!-- ... -->
+ class Comment < Child
+ include Comparable
+ START = "<!--"
+ STOP = "-->"
+
+ # The content text
+
+ attr_accessor :string
+
+ ##
+ # Constructor. The first argument can be one of three types:
+ # @param first If String, the contents of this comment are set to the
+ # argument. If Comment, the argument is duplicated. If
+ # Source, the argument is scanned for a comment.
+ # @param second If the first argument is a Source, this argument
+ # should be nil, not supplied, or a Parent to be set as the parent
+ # of this object
+ def initialize( first, second = nil )
+ #puts "IN COMMENT CONSTRUCTOR; SECOND IS #{second.type}"
+ super(second)
+ if first.kind_of? String
+ @string = first
+ elsif first.kind_of? Comment
+ @string = first.string
+ end
+ end
+
+ def clone
+ Comment.new self
+ end
+
+ # output::
+ # Where to write the string
+ # indent::
+ # An integer. If -1, no indenting will be used; otherwise, the
+ # indentation will be this number of spaces, and children will be
+ # indented an additional amount.
+ # transitive::
+ # If transitive is true and indent is >= 0, then the output will be
+ # pretty-printed in such a way that the added whitespace does not affect
+ # the absolute *value* of the document -- that is, it leaves the value
+ # and number of Text nodes in the document unchanged.
+ # ie_hack::
+ # Internet Explorer is the worst piece of crap to have ever been
+ # written, with the possible exception of Windows itself. Since IE is
+ # unable to parse proper XML, we have to provide a hack to generate XML
+ # that IE's limited abilities can handle. This hack inserts a space
+ # before the /> on empty tags.
+ #
+ def write( output, indent=-1, transitive=false, ie_hack=false )
+ indent( output, indent )
+ output << START
+ output << @string
+ if indent>-1
+ output << "\n"
+ indent( output, indent )
+ end
+ output << STOP
+ end
+
+ alias :to_s :string
+
+ ##
+ # Compares this Comment to another; the contents of the comment are used
+ # in the comparison.
+ def <=>(other)
+ other.to_s <=> @string
+ end
+
+ ##
+ # Compares this Comment to another; the contents of the comment are used
+ # in the comparison.
+ def ==( other )
+ other.kind_of? Comment and
+ (other <=> self) == 0
+ end
+
+ def node_type
+ :comment
+ end
+ end
+end
+#vim:ts=2 sw=2 noexpandtab:
diff --git a/ruby_1_8_5/lib/rexml/doctype.rb b/ruby_1_8_5/lib/rexml/doctype.rb
new file mode 100644
index 0000000000..4a1ffb4336
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/doctype.rb
@@ -0,0 +1,278 @@
+require "rexml/parent"
+require "rexml/parseexception"
+require "rexml/namespace"
+require 'rexml/entity'
+require 'rexml/attlistdecl'
+require 'rexml/xmltokens'
+
+module REXML
+ # Represents an XML DOCTYPE declaration; that is, the contents of <!DOCTYPE
+ # ... >. DOCTYPES can be used to declare the DTD of a document, as well as
+ # being used to declare entities used in the document.
+ class DocType < Parent
+ include XMLTokens
+ START = "<!DOCTYPE"
+ STOP = ">"
+ SYSTEM = "SYSTEM"
+ PUBLIC = "PUBLIC"
+ DEFAULT_ENTITIES = {
+ 'gt'=>EntityConst::GT,
+ 'lt'=>EntityConst::LT,
+ 'quot'=>EntityConst::QUOT,
+ "apos"=>EntityConst::APOS
+ }
+
+ # name is the name of the doctype
+ # external_id is the referenced DTD, if given
+ attr_reader :name, :external_id, :entities, :namespaces
+
+ # Constructor
+ #
+ # dt = DocType.new( 'foo', '-//I/Hate/External/IDs' )
+ # # <!DOCTYPE foo '-//I/Hate/External/IDs'>
+ # dt = DocType.new( doctype_to_clone )
+ # # Incomplete. Shallow clone of doctype
+ #
+ # +Note+ that the constructor:
+ #
+ # Doctype.new( Source.new( "<!DOCTYPE foo 'bar'>" ) )
+ #
+ # is _deprecated_. Do not use it. It will probably disappear.
+ def initialize( first, parent=nil )
+ @entities = DEFAULT_ENTITIES
+ @long_name = @uri = nil
+ if first.kind_of? String
+ super()
+ @name = first
+ @external_id = parent
+ elsif first.kind_of? DocType
+ super( parent )
+ @name = first.name
+ @external_id = first.external_id
+ elsif first.kind_of? Array
+ super( parent )
+ @name = first[0]
+ @external_id = first[1]
+ @long_name = first[2]
+ @uri = first[3]
+ elsif first.kind_of? Source
+ super( parent )
+ parser = Parsers::BaseParser.new( first )
+ event = parser.pull
+ if event[0] == :start_doctype
+ @name, @external_id, @long_name, @uri, = event[1..-1]
+ end
+ else
+ super()
+ end
+ end
+
+ def node_type
+ :doctype
+ end
+
+ def attributes_of element
+ rv = []
+ each do |child|
+ child.each do |key,val|
+ rv << Attribute.new(key,val)
+ end if child.kind_of? AttlistDecl and child.element_name == element
+ end
+ rv
+ end
+
+ def attribute_of element, attribute
+ att_decl = find do |child|
+ child.kind_of? AttlistDecl and
+ child.element_name == element and
+ child.include? attribute
+ end
+ return nil unless att_decl
+ att_decl[attribute]
+ end
+
+ def clone
+ DocType.new self
+ end
+
+ # output::
+ # Where to write the string
+ # indent::
+ # An integer. If -1, no indenting will be used; otherwise, the
+ # indentation will be this number of spaces, and children will be
+ # indented an additional amount.
+ # transitive::
+ # If transitive is true and indent is >= 0, then the output will be
+ # pretty-printed in such a way that the added whitespace does not affect
+ # the absolute *value* of the document -- that is, it leaves the value
+ # and number of Text nodes in the document unchanged.
+ # ie_hack::
+ # Internet Explorer is the worst piece of crap to have ever been
+ # written, with the possible exception of Windows itself. Since IE is
+ # unable to parse proper XML, we have to provide a hack to generate XML
+ # that IE's limited abilities can handle. This hack inserts a space
+ # before the /> on empty tags.
+ #
+ def write( output, indent=0, transitive=false, ie_hack=false )
+ indent( output, indent )
+ output << START
+ output << ' '
+ output << @name
+ output << " #@external_id" if @external_id
+ output << " #@long_name" if @long_name
+ output << " #@uri" if @uri
+ unless @children.empty?
+ next_indent = indent + 1
+ output << ' ['
+ child = nil # speed
+ @children.each { |child|
+ output << "\n"
+ child.write( output, next_indent )
+ }
+ #output << ' '*next_indent
+ output << "\n]"
+ end
+ output << STOP
+ end
+
+ def context
+ @parent.context
+ end
+
+ def entity( name )
+ @entities[name].unnormalized if @entities[name]
+ end
+
+ def add child
+ super(child)
+ @entities = DEFAULT_ENTITIES.clone if @entities == DEFAULT_ENTITIES
+ @entities[ child.name ] = child if child.kind_of? Entity
+ end
+
+ # This method retrieves the public identifier identifying the document's
+ # DTD.
+ #
+ # Method contributed by Henrik Martensson
+ def public
+ case @external_id
+ when "SYSTEM"
+ nil
+ when "PUBLIC"
+ strip_quotes(@long_name)
+ end
+ end
+
+ # This method retrieves the system identifier identifying the document's DTD
+ #
+ # Method contributed by Henrik Martensson
+ def system
+ case @external_id
+ when "SYSTEM"
+ strip_quotes(@long_name)
+ when "PUBLIC"
+ @uri.kind_of?(String) ? strip_quotes(@uri) : nil
+ end
+ end
+
+ # This method returns a list of notations that have been declared in the
+ # _internal_ DTD subset. Notations in the external DTD subset are not
+ # listed.
+ #
+ # Method contributed by Henrik Martensson
+ def notations
+ children().select {|node| node.kind_of?(REXML::NotationDecl)}
+ end
+
+ # Retrieves a named notation. Only notations declared in the internal
+ # DTD subset can be retrieved.
+ #
+ # Method contributed by Henrik Martensson
+ def notation(name)
+ notations.find { |notation_decl|
+ notation_decl.name == name
+ }
+ end
+
+ private
+
+ # Method contributed by Henrik Martensson
+ def strip_quotes(quoted_string)
+ quoted_string =~ /^[\'\"].*[\´\"]$/ ?
+ quoted_string[1, quoted_string.length-2] :
+ quoted_string
+ end
+ end
+
+ # We don't really handle any of these since we're not a validating
+ # parser, so we can be pretty dumb about them. All we need to be able
+ # to do is spew them back out on a write()
+
+ # This is an abstract class. You never use this directly; it serves as a
+ # parent class for the specific declarations.
+ class Declaration < Child
+ def initialize src
+ super()
+ @string = src
+ end
+
+ def to_s
+ @string+'>'
+ end
+
+ def write( output, indent )
+ output << (' '*indent) if indent > 0
+ output << to_s
+ end
+ end
+
+ public
+ class ElementDecl < Declaration
+ def initialize( src )
+ super
+ end
+ end
+
+ class ExternalEntity < Child
+ def initialize( src )
+ super()
+ @entity = src
+ end
+ def to_s
+ @entity
+ end
+ def write( output, indent )
+ output << @entity
+ end
+ end
+
+ class NotationDecl < Child
+ attr_accessor :public, :system
+ def initialize name, middle, pub, sys
+ super(nil)
+ @name = name
+ @middle = middle
+ @public = pub
+ @system = sys
+ end
+
+ def to_s
+ "<!NOTATION #@name #@middle#{
+ @public ? ' ' + public.inspect : ''
+ }#{
+ @system ? ' ' +@system.inspect : ''
+ }>"
+ end
+
+ def write( output, indent=-1 )
+ output << (' '*indent) if indent > 0
+ output << to_s
+ end
+
+ # This method retrieves the name of the notation.
+ #
+ # Method contributed by Henrik Martensson
+ def name
+ @name
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/document.rb b/ruby_1_8_5/lib/rexml/document.rb
new file mode 100644
index 0000000000..619a844257
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/document.rb
@@ -0,0 +1,193 @@
+require "rexml/element"
+require "rexml/xmldecl"
+require "rexml/source"
+require "rexml/comment"
+require "rexml/doctype"
+require "rexml/instruction"
+require "rexml/rexml"
+require "rexml/parseexception"
+require "rexml/output"
+require "rexml/parsers/baseparser"
+require "rexml/parsers/streamparser"
+require "rexml/parsers/treeparser"
+
+module REXML
+ # Represents a full XML document, including PIs, a doctype, etc. A
+ # Document has a single child that can be accessed by root().
+ # Note that if you want to have an XML declaration written for a document
+ # you create, you must add one; REXML documents do not write a default
+ # declaration for you. See |DECLARATION| and |write|.
+ class Document < Element
+ # A convenient default XML declaration. If you want an XML declaration,
+ # the easiest way to add one is mydoc << Document::DECLARATION
+ # +DEPRECATED+
+ # Use: mydoc << XMLDecl.default
+ DECLARATION = XMLDecl.default
+
+ # Constructor
+ # @param source if supplied, must be a Document, String, or IO.
+ # Documents have their context and Element attributes cloned.
+ # Strings are expected to be valid XML documents. IOs are expected
+ # to be sources of valid XML documents.
+ # @param context if supplied, contains the context of the document;
+ # this should be a Hash.
+ # NOTE that I'm not sure what the context is for; I cloned it out of
+ # the Electric XML API (in which it also seems to do nothing), and it
+ # is now legacy. It may do something, someday... it may disappear.
+ def initialize( source = nil, context = {} )
+ super()
+ @context = context
+ return if source.nil?
+ if source.kind_of? Document
+ @context = source.context
+ super source
+ else
+ build( source )
+ end
+ end
+
+ def node_type
+ :document
+ end
+
+ # Should be obvious
+ def clone
+ Document.new self
+ end
+
+ # According to the XML spec, a root node has no expanded name
+ def expanded_name
+ ''
+ #d = doc_type
+ #d ? d.name : "UNDEFINED"
+ end
+
+ alias :name :expanded_name
+
+ # We override this, because XMLDecls and DocTypes must go at the start
+ # of the document
+ def add( child )
+ if child.kind_of? XMLDecl
+ @children.unshift child
+ elsif child.kind_of? DocType
+ # Find first Element or DocType node and insert the decl right
+ # before it. If there is no such node, just insert the child at the
+ # end. If there is a child and it is an DocType, then replace it.
+ insert_before_index = 0
+ @children.find { |x|
+ insert_before_index += 1
+ x.kind_of?(Element) || x.kind_of?(DocType)
+ }
+ if @children[ insert_before_index ] # Not null = not end of list
+ if @children[ insert_before_index ].kind_of DocType
+ @children[ insert_before_index ] = child
+ else
+ @children[ index_before_index-1, 0 ] = child
+ end
+ else # Insert at end of list
+ @children[insert_before_index] = child
+ end
+ child.parent = self
+ else
+ rv = super
+ raise "attempted adding second root element to document" if @elements.size > 1
+ rv
+ end
+ end
+ alias :<< :add
+
+ def add_element(arg=nil, arg2=nil)
+ rv = super
+ raise "attempted adding second root element to document" if @elements.size > 1
+ rv
+ end
+
+ # @return the root Element of the document, or nil if this document
+ # has no children.
+ def root
+ elements[1]
+ #self
+ #@children.find { |item| item.kind_of? Element }
+ end
+
+ # @return the DocType child of the document, if one exists,
+ # and nil otherwise.
+ def doctype
+ @children.find { |item| item.kind_of? DocType }
+ end
+
+ # @return the XMLDecl of this document; if no XMLDecl has been
+ # set, the default declaration is returned.
+ def xml_decl
+ rv = @children[0]
+ return rv if rv.kind_of? XMLDecl
+ rv = @children.unshift(XMLDecl.default)[0]
+ end
+
+ # @return the XMLDecl version of this document as a String.
+ # If no XMLDecl has been set, returns the default version.
+ def version
+ xml_decl().version
+ end
+
+ # @return the XMLDecl encoding of this document as a String.
+ # If no XMLDecl has been set, returns the default encoding.
+ def encoding
+ xml_decl().encoding
+ end
+
+ # @return the XMLDecl standalone value of this document as a String.
+ # If no XMLDecl has been set, returns the default setting.
+ def stand_alone?
+ xml_decl().stand_alone?
+ end
+
+ # Write the XML tree out, optionally with indent. This writes out the
+ # entire XML document, including XML declarations, doctype declarations,
+ # and processing instructions (if any are given).
+ # A controversial point is whether Document should always write the XML
+ # declaration (<?xml version='1.0'?>) whether or not one is given by the
+ # user (or source document). REXML does not write one if one was not
+ # specified, because it adds unneccessary bandwidth to applications such
+ # as XML-RPC.
+ #
+ #
+ # output::
+ # output an object which supports '<< string'; this is where the
+ # document will be written.
+ # indent::
+ # An integer. If -1, no indenting will be used; otherwise, the
+ # indentation will be this number of spaces, and children will be
+ # indented an additional amount. Defaults to -1
+ # transitive::
+ # If transitive is true and indent is >= 0, then the output will be
+ # pretty-printed in such a way that the added whitespace does not affect
+ # the absolute *value* of the document -- that is, it leaves the value
+ # and number of Text nodes in the document unchanged.
+ # ie_hack::
+ # Internet Explorer is the worst piece of crap to have ever been
+ # written, with the possible exception of Windows itself. Since IE is
+ # unable to parse proper XML, we have to provide a hack to generate XML
+ # that IE's limited abilities can handle. This hack inserts a space
+ # before the /> on empty tags. Defaults to false
+ def write( output=$stdout, indent=-1, transitive=false, ie_hack=false )
+ output = Output.new( output, xml_decl.encoding ) if xml_decl.encoding != "UTF-8" && !output.kind_of?(Output)
+ @children.each { |node|
+ indent( output, indent ) if node.node_type == :element
+ if node.write( output, indent, transitive, ie_hack )
+ output << "\n" unless indent<0 or node == @children[-1]
+ end
+ }
+ end
+
+
+ def Document::parse_stream( source, listener )
+ Parsers::StreamParser.new( source, listener ).parse
+ end
+
+ private
+ def build( source )
+ Parsers::TreeParser.new( source, self ).parse
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/dtd/attlistdecl.rb b/ruby_1_8_5/lib/rexml/dtd/attlistdecl.rb
new file mode 100644
index 0000000000..e176bb0749
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/dtd/attlistdecl.rb
@@ -0,0 +1,10 @@
+require "rexml/child"
+module REXML
+ module DTD
+ class AttlistDecl < Child
+ START = "<!ATTLIST"
+ START_RE = /^\s*#{START}/um
+ PATTERN_RE = /\s*(#{START}.*?>)/um
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/dtd/dtd.rb b/ruby_1_8_5/lib/rexml/dtd/dtd.rb
new file mode 100644
index 0000000000..4f735d4812
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/dtd/dtd.rb
@@ -0,0 +1,51 @@
+require "rexml/dtd/elementdecl"
+require "rexml/dtd/entitydecl"
+require "rexml/comment"
+require "rexml/dtd/notationdecl"
+require "rexml/dtd/attlistdecl"
+require "rexml/parent"
+
+module REXML
+ module DTD
+ class Parser
+ def Parser.parse( input )
+ case input
+ when String
+ parse_helper input
+ when File
+ parse_helper input.read
+ end
+ end
+
+ # Takes a String and parses it out
+ def Parser.parse_helper( input )
+ contents = Parent.new
+ while input.size > 0
+ case input
+ when ElementDecl.PATTERN_RE
+ match = $&
+ source = $'
+ contents << ElementDecl.new( match )
+ when AttlistDecl.PATTERN_RE
+ matchdata = $~
+ source = $'
+ contents << AttlistDecl.new( matchdata )
+ when EntityDecl.PATTERN_RE
+ matchdata = $~
+ source = $'
+ contents << EntityDecl.new( matchdata )
+ when Comment.PATTERN_RE
+ matchdata = $~
+ source = $'
+ contents << Comment.new( matchdata )
+ when NotationDecl.PATTERN_RE
+ matchdata = $~
+ source = $'
+ contents << NotationDecl.new( matchdata )
+ end
+ end
+ contents
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/dtd/elementdecl.rb b/ruby_1_8_5/lib/rexml/dtd/elementdecl.rb
new file mode 100644
index 0000000000..c4e620f389
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/dtd/elementdecl.rb
@@ -0,0 +1,17 @@
+require "rexml/child"
+module REXML
+ module DTD
+ class ElementDecl < Child
+ START = "<!ELEMENT"
+ START_RE = /^\s*#{START}/um
+ PATTERN_RE = /^\s*(#{START}.*?)>/um
+ PATTERN_RE = /^\s*#{START}\s+((?:[:\w_][-\.\w_]*:)?[-!\*\.\w_]*)(.*?)>/
+ #\s*((((["']).*?\5)|[^\/'">]*)*?)(\/)?>/um, true)
+
+ def initialize match
+ @name = match[1]
+ @rest = match[2]
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/dtd/entitydecl.rb b/ruby_1_8_5/lib/rexml/dtd/entitydecl.rb
new file mode 100644
index 0000000000..a5f1520f2b
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/dtd/entitydecl.rb
@@ -0,0 +1,56 @@
+require "rexml/child"
+module REXML
+ module DTD
+ class EntityDecl < Child
+ START = "<!ENTITY"
+ START_RE = /^\s*#{START}/um
+ PUBLIC = /^\s*#{START}\s+(?:%\s+)?(\w+)\s+PUBLIC\s+((["']).*?\3)\s+((["']).*?\5)\s*>/um
+ SYSTEM = /^\s*#{START}\s+(?:%\s+)?(\w+)\s+SYSTEM\s+((["']).*?\3)(?:\s+NDATA\s+\w+)?\s*>/um
+ PLAIN = /^\s*#{START}\s+(\w+)\s+((["']).*?\3)\s*>/um
+ PERCENT = /^\s*#{START}\s+%\s+(\w+)\s+((["']).*?\3)\s*>/um
+ # <!ENTITY name SYSTEM "...">
+ # <!ENTITY name "...">
+ def initialize src
+ super()
+ md = nil
+ if src.match( PUBLIC )
+ md = src.match( PUBLIC, true )
+ @middle = "PUBLIC"
+ @content = "#{md[2]} #{md[4]}"
+ elsif src.match( SYSTEM )
+ md = src.match( SYSTEM, true )
+ @middle = "SYSTEM"
+ @content = md[2]
+ elsif src.match( PLAIN )
+ md = src.match( PLAIN, true )
+ @middle = ""
+ @content = md[2]
+ elsif src.match( PERCENT )
+ md = src.match( PERCENT, true )
+ @middle = ""
+ @content = md[2]
+ end
+ raise ParseException.new("failed Entity match", src) if md.nil?
+ @name = md[1]
+ end
+
+ def to_s
+ rv = "<!ENTITY #@name "
+ rv << "#@middle " if @middle.size > 0
+ rv << @content
+ rv
+ end
+
+ def write( output, indent )
+ indent( output, indent )
+ output << to_s
+ end
+
+ def EntityDecl.parse_source source, listener
+ md = source.match( PATTERN_RE, true )
+ thing = md[0].squeeze(" \t\n\r")
+ listener.send inspect.downcase, thing
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/dtd/notationdecl.rb b/ruby_1_8_5/lib/rexml/dtd/notationdecl.rb
new file mode 100644
index 0000000000..a47ff8f24b
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/dtd/notationdecl.rb
@@ -0,0 +1,39 @@
+require "rexml/child"
+module REXML
+ module DTD
+ class NotationDecl < Child
+ START = "<!NOTATION"
+ START_RE = /^\s*#{START}/um
+ PUBLIC = /^\s*#{START}\s+(\w[\w-]*)\s+(PUBLIC)\s+((["']).*?\4)\s*>/um
+ SYSTEM = /^\s*#{START}\s+(\w[\w-]*)\s+(SYSTEM)\s+((["']).*?\4)\s*>/um
+ def initialize src
+ super()
+ if src.match( PUBLIC )
+ md = src.match( PUBLIC, true )
+ elsif src.match( SYSTEM )
+ md = src.match( SYSTEM, true )
+ else
+ raise ParseException.new( "error parsing notation: no matching pattern", src )
+ end
+ @name = md[1]
+ @middle = md[2]
+ @rest = md[3]
+ end
+
+ def to_s
+ "<!NOTATION #@name #@middle #@rest>"
+ end
+
+ def write( output, indent )
+ indent( output, indent )
+ output << to_s
+ end
+
+ def NotationDecl.parse_source source, listener
+ md = source.match( PATTERN_RE, true )
+ thing = md[0].squeeze(" \t\n\r")
+ listener.send inspect.downcase, thing
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/element.rb b/ruby_1_8_5/lib/rexml/element.rb
new file mode 100644
index 0000000000..80463d95b7
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/element.rb
@@ -0,0 +1,1243 @@
+require "rexml/parent"
+require "rexml/namespace"
+require "rexml/attribute"
+require "rexml/cdata"
+require "rexml/xpath"
+require "rexml/parseexception"
+
+module REXML
+ # An implementation note about namespaces:
+ # As we parse, when we find namespaces we put them in a hash and assign
+ # them a unique ID. We then convert the namespace prefix for the node
+ # to the unique ID. This makes namespace lookup much faster for the
+ # cost of extra memory use. We save the namespace prefix for the
+ # context node and convert it back when we write it.
+ @@namespaces = {}
+
+ # Represents a tagged XML element. Elements are characterized by
+ # having children, attributes, and names, and can themselves be
+ # children.
+ class Element < Parent
+ include Namespace
+
+ UNDEFINED = "UNDEFINED"; # The default name
+
+ # Mechanisms for accessing attributes and child elements of this
+ # element.
+ attr_reader :attributes, :elements
+ # The context holds information about the processing environment, such as
+ # whitespace handling.
+ attr_accessor :context
+
+ # Constructor
+ # arg::
+ # if not supplied, will be set to the default value.
+ # If a String, the name of this object will be set to the argument.
+ # If an Element, the object will be shallowly cloned; name,
+ # attributes, and namespaces will be copied. Children will +not+ be
+ # copied.
+ # parent::
+ # if supplied, must be a Parent, and will be used as
+ # the parent of this object.
+ # context::
+ # If supplied, must be a hash containing context items. Context items
+ # include:
+ # * <tt>:respect_whitespace</tt> the value of this is :+all+ or an array of
+ # strings being the names of the elements to respect
+ # whitespace for. Defaults to :+all+.
+ # * <tt>:compress_whitespace</tt> the value can be :+all+ or an array of
+ # strings being the names of the elements to ignore whitespace on.
+ # Overrides :+respect_whitespace+.
+ # * <tt>:ignore_whitespace_nodes</tt> the value can be :+all+ or an array
+ # of strings being the names of the elements in which to ignore
+ # whitespace-only nodes. If this is set, Text nodes which contain only
+ # whitespace will not be added to the document tree.
+ # * <tt>:raw</tt> can be :+all+, or an array of strings being the names of
+ # the elements to process in raw mode. In raw mode, special
+ # characters in text is not converted to or from entities.
+ def initialize( arg = UNDEFINED, parent=nil, context=nil )
+ super(parent)
+
+ @elements = Elements.new(self)
+ @attributes = Attributes.new(self)
+ @context = context
+
+ if arg.kind_of? String
+ self.name = arg
+ elsif arg.kind_of? Element
+ self.name = arg.expanded_name
+ arg.attributes.each_attribute{ |attribute|
+ @attributes << Attribute.new( attribute )
+ }
+ @context = arg.context
+ end
+ end
+
+ def inspect
+ rv = "<#@expanded_name"
+
+ @attributes.each_attribute do |attr|
+ rv << " "
+ attr.write( rv, 0 )
+ end
+
+ if children.size > 0
+ rv << "> ... </>"
+ else
+ rv << "/>"
+ end
+ end
+
+
+ # Creates a shallow copy of self.
+ # d = Document.new "<a><b/><b/><c><d/></c></a>"
+ # new_a = d.root.clone
+ # puts new_a # => "<a/>"
+ def clone
+ Element.new self
+ end
+
+ # Evaluates to the root node of the document that this element
+ # belongs to. If this element doesn't belong to a document, but does
+ # belong to another Element, the parent's root will be returned, until the
+ # earliest ancestor is found.
+ #
+ # Note that this is not the same as the document element.
+ # In the following example, <a> is the document element, and the root
+ # node is the parent node of the document element. You may ask yourself
+ # why the root node is useful: consider the doctype and XML declaration,
+ # and any processing instructions before the document element... they
+ # are children of the root node, or siblings of the document element.
+ # The only time this isn't true is when an Element is created that is
+ # not part of any Document. In this case, the ancestor that has no
+ # parent acts as the root node.
+ # d = Document.new '<a><b><c/></b></a>'
+ # a = d[1] ; c = a[1][1]
+ # d.root_node == d # TRUE
+ # a.root_node # namely, d
+ # c.root_node # again, d
+ def root_node
+ parent.nil? ? self : parent.root_node
+ end
+
+ def root
+ return elements[1] if self.kind_of? Document
+ return self if parent.kind_of? Document or parent.nil?
+ return parent.root
+ end
+
+ # Evaluates to the document to which this element belongs, or nil if this
+ # element doesn't belong to a document.
+ def document
+ rt = root
+ rt.parent if rt
+ end
+
+ # Evaluates to +true+ if whitespace is respected for this element. This
+ # is the case if:
+ # 1. Neither :+respect_whitespace+ nor :+compress_whitespace+ has any value
+ # 2. The context has :+respect_whitespace+ set to :+all+ or
+ # an array containing the name of this element, and
+ # :+compress_whitespace+ isn't set to :+all+ or an array containing the
+ # name of this element.
+ # The evaluation is tested against +expanded_name+, and so is namespace
+ # sensitive.
+ def whitespace
+ @whitespace = nil
+ if @context
+ if @context[:respect_whitespace]
+ @whitespace = (@context[:respect_whitespace] == :all or
+ @context[:respect_whitespace].include? expanded_name)
+ end
+ @whitespace = false if (@context[:compress_whitespace] and
+ (@context[:compress_whitespace] == :all or
+ @context[:compress_whitespace].include? expanded_name)
+ )
+ end
+ @whitespace = true unless @whitespace == false
+ @whitespace
+ end
+
+ def ignore_whitespace_nodes
+ @ignore_whitespace_nodes = false
+ if @context
+ if @context[:ignore_whitespace_nodes]
+ @ignore_whitespace_nodes =
+ (@context[:ignore_whitespace_nodes] == :all or
+ @context[:ignore_whitespace_nodes].include? expanded_name)
+ end
+ end
+ end
+
+ # Evaluates to +true+ if raw mode is set for this element. This
+ # is the case if the context has :+raw+ set to :+all+ or
+ # an array containing the name of this element.
+ #
+ # The evaluation is tested against +expanded_name+, and so is namespace
+ # sensitive.
+ def raw
+ @raw = (@context and @context[:raw] and
+ (@context[:raw] == :all or
+ @context[:raw].include? expanded_name))
+ @raw
+ end
+
+ #once :whitespace, :raw, :ignore_whitespace_nodes
+
+ #################################################
+ # Namespaces #
+ #################################################
+
+ # Evaluates to an +Array+ containing the prefixes (names) of all defined
+ # namespaces at this context node.
+ # doc = Document.new("<a xmlns:x='1' xmlns:y='2'><b/><c xmlns:z='3'/></a>")
+ # doc.elements['//b'].prefixes # -> ['x', 'y']
+ def prefixes
+ prefixes = []
+ prefixes = parent.prefixes if parent
+ prefixes |= attributes.prefixes
+ return prefixes
+ end
+
+ def namespaces
+ namespaces = []
+ namespaces = parent.namespaces if parent
+ namespaces |= attributes.namespaces
+ return namespaces
+ end
+
+ # Evalutas to the URI for a prefix, or the empty string if no such
+ # namespace is declared for this element. Evaluates recursively for
+ # ancestors. Returns the default namespace, if there is one.
+ # prefix::
+ # the prefix to search for. If not supplied, returns the default
+ # namespace if one exists
+ # Returns::
+ # the namespace URI as a String, or nil if no such namespace
+ # exists. If the namespace is undefined, returns an empty string
+ # doc = Document.new("<a xmlns='1' xmlns:y='2'><b/><c xmlns:z='3'/></a>")
+ # b = doc.elements['//b']
+ # b.namespace # -> '1'
+ # b.namespace("y") # -> '2'
+ def namespace(prefix=nil)
+ if prefix.nil?
+ prefix = prefix()
+ end
+ if prefix == ''
+ prefix = "xmlns"
+ else
+ prefix = "xmlns:#{prefix}" unless prefix[0,5] == 'xmlns'
+ end
+ ns = attributes[ prefix ]
+ ns = parent.namespace(prefix) if ns.nil? and parent
+ ns = '' if ns.nil? and prefix == 'xmlns'
+ return ns
+ end
+
+ # Adds a namespace to this element.
+ # prefix::
+ # the prefix string, or the namespace URI if +uri+ is not
+ # supplied
+ # uri::
+ # the namespace URI. May be nil, in which +prefix+ is used as
+ # the URI
+ # Evaluates to: this Element
+ # a = Element.new("a")
+ # a.add_namespace("xmlns:foo", "bar" )
+ # a.add_namespace("foo", "bar") # shorthand for previous line
+ # a.add_namespace("twiddle")
+ # puts a #-> <a xmlns:foo='bar' xmlns='twiddle'/>
+ def add_namespace( prefix, uri=nil )
+ unless uri
+ @attributes["xmlns"] = prefix
+ else
+ prefix = "xmlns:#{prefix}" unless prefix =~ /^xmlns:/
+ @attributes[ prefix ] = uri
+ end
+ self
+ end
+
+ # Removes a namespace from this node. This only works if the namespace is
+ # actually declared in this node. If no argument is passed, deletes the
+ # default namespace.
+ #
+ # Evaluates to: this element
+ # doc = Document.new "<a xmlns:foo='bar' xmlns='twiddle'/>"
+ # doc.root.delete_namespace
+ # puts doc # -> <a xmlns:foo='bar'/>
+ # doc.root.delete_namespace 'foo'
+ # puts doc # -> <a/>
+ def delete_namespace namespace="xmlns"
+ namespace = "xmlns:#{namespace}" unless namespace == 'xmlns'
+ attribute = attributes.get_attribute(namespace)
+ attribute.remove unless attribute.nil?
+ self
+ end
+
+ #################################################
+ # Elements #
+ #################################################
+
+ # Adds a child to this element, optionally setting attributes in
+ # the element.
+ # element::
+ # optional. If Element, the element is added.
+ # Otherwise, a new Element is constructed with the argument (see
+ # Element.initialize).
+ # attrs::
+ # If supplied, must be a Hash containing String name,value
+ # pairs, which will be used to set the attributes of the new Element.
+ # Returns:: the Element that was added
+ # el = doc.add_element 'my-tag'
+ # el = doc.add_element 'my-tag', {'attr1'=>'val1', 'attr2'=>'val2'}
+ # el = Element.new 'my-tag'
+ # doc.add_element el
+ def add_element element, attrs=nil
+ raise "First argument must be either an element name, or an Element object" if element.nil?
+ el = @elements.add(element)
+ if attrs.kind_of? Hash
+ attrs.each do |key, value|
+ el.attributes[key]=value if key =~ /^xmlns:/
+ end
+ attrs.each do |key, value|
+ el.attributes[key]=value if key !~ /^xmlns:/
+ end
+ end
+ el
+ end
+
+ # Deletes a child element.
+ # element::
+ # Must be an +Element+, +String+, or +Integer+. If Element,
+ # the element is removed. If String, the element is found (via XPath)
+ # and removed. <em>This means that any parent can remove any
+ # descendant.<em> If Integer, the Element indexed by that number will be
+ # removed.
+ # Returns:: the element that was removed.
+ # doc.delete_element "/a/b/c[@id='4']"
+ # doc.delete_element doc.elements["//k"]
+ # doc.delete_element 1
+ def delete_element element
+ @elements.delete element
+ end
+
+ # Evaluates to +true+ if this element has at least one child Element
+ # doc = Document.new "<a><b/><c>Text</c></a>"
+ # doc.root.has_elements # -> true
+ # doc.elements["/a/b"].has_elements # -> false
+ # doc.elements["/a/c"].has_elements # -> false
+ def has_elements?
+ !@elements.empty?
+ end
+
+ # Iterates through the child elements, yielding for each Element that
+ # has a particular attribute set.
+ # key::
+ # the name of the attribute to search for
+ # value::
+ # the value of the attribute
+ # max::
+ # (optional) causes this method to return after yielding
+ # for this number of matching children
+ # name::
+ # (optional) if supplied, this is an XPath that filters
+ # the children to check.
+ #
+ # doc = Document.new "<a><b @id='1'/><c @id='2'/><d @id='1'/><e/></a>"
+ # # Yields b, c, d
+ # doc.root.each_element_with_attribute( 'id' ) {|e| p e}
+ # # Yields b, d
+ # doc.root.each_element_with_attribute( 'id', '1' ) {|e| p e}
+ # # Yields b
+ # doc.root.each_element_with_attribute( 'id', '1', 1 ) {|e| p e}
+ # # Yields d
+ # doc.root.each_element_with_attribute( 'id', '1', 0, 'd' ) {|e| p e}
+ def each_element_with_attribute( key, value=nil, max=0, name=nil, &block ) # :yields: Element
+ each_with_something( proc {|child|
+ if value.nil?
+ child.attributes[key] != nil
+ else
+ child.attributes[key]==value
+ end
+ }, max, name, &block )
+ end
+
+ # Iterates through the children, yielding for each Element that
+ # has a particular text set.
+ # text::
+ # the text to search for. If nil, or not supplied, will itterate
+ # over all +Element+ children that contain at least one +Text+ node.
+ # max::
+ # (optional) causes this method to return after yielding
+ # for this number of matching children
+ # name::
+ # (optional) if supplied, this is an XPath that filters
+ # the children to check.
+ #
+ # doc = Document.new '<a><b>b</b><c>b</c><d>d</d><e/></a>'
+ # # Yields b, c, d
+ # doc.each_element_with_text {|e|p e}
+ # # Yields b, c
+ # doc.each_element_with_text('b'){|e|p e}
+ # # Yields b
+ # doc.each_element_with_text('b', 1){|e|p e}
+ # # Yields d
+ # doc.each_element_with_text(nil, 0, 'd'){|e|p e}
+ def each_element_with_text( text=nil, max=0, name=nil, &block ) # :yields: Element
+ each_with_something( proc {|child|
+ if text.nil?
+ child.has_text?
+ else
+ child.text == text
+ end
+ }, max, name, &block )
+ end
+
+ # Synonym for Element.elements.each
+ def each_element( xpath=nil, &block ) # :yields: Element
+ @elements.each( xpath, &block )
+ end
+
+ # Synonym for Element.to_a
+ # This is a little slower than calling elements.each directly.
+ # xpath:: any XPath by which to search for elements in the tree
+ # Returns:: an array of Elements that match the supplied path
+ def get_elements( xpath )
+ @elements.to_a( xpath )
+ end
+
+ # Returns the next sibling that is an element, or nil if there is
+ # no Element sibling after this one
+ # doc = Document.new '<a><b/>text<c/></a>'
+ # doc.root.elements['b'].next_element #-> <c/>
+ # doc.root.elements['c'].next_element #-> nil
+ def next_element
+ element = next_sibling
+ element = element.next_sibling until element.nil? or element.kind_of? Element
+ return element
+ end
+
+ # Returns the previous sibling that is an element, or nil if there is
+ # no Element sibling prior to this one
+ # doc = Document.new '<a><b/>text<c/></a>'
+ # doc.root.elements['c'].previous_element #-> <b/>
+ # doc.root.elements['b'].previous_element #-> nil
+ def previous_element
+ element = previous_sibling
+ element = element.previous_sibling until element.nil? or element.kind_of? Element
+ return element
+ end
+
+
+ #################################################
+ # Text #
+ #################################################
+
+ # Evaluates to +true+ if this element has at least one Text child
+ def has_text?
+ not text().nil?
+ end
+
+ # A convenience method which returns the String value of the _first_
+ # child text element, if one exists, and +nil+ otherwise.
+ #
+ # <em>Note that an element may have multiple Text elements, perhaps
+ # separated by other children</em>. Be aware that this method only returns
+ # the first Text node.
+ #
+ # This method returns the +value+ of the first text child node, which
+ # ignores the +raw+ setting, so always returns normalized text. See
+ # the Text::value documentation.
+ #
+ # doc = Document.new "<p>some text <b>this is bold!</b> more text</p>"
+ # # The element 'p' has two text elements, "some text " and " more text".
+ # doc.root.text #-> "some text "
+ def text( path = nil )
+ rv = get_text(path)
+ return rv.value unless rv.nil?
+ nil
+ end
+
+ # Returns the first child Text node, if any, or +nil+ otherwise.
+ # This method returns the actual +Text+ node, rather than the String content.
+ # doc = Document.new "<p>some text <b>this is bold!</b> more text</p>"
+ # # The element 'p' has two text elements, "some text " and " more text".
+ # doc.root.get_text.value #-> "some text "
+ def get_text path = nil
+ rv = nil
+ if path
+ element = @elements[ path ]
+ rv = element.get_text unless element.nil?
+ else
+ rv = @children.find { |node| node.kind_of? Text }
+ end
+ return rv
+ end
+
+ # Sets the first Text child of this object. See text() for a
+ # discussion about Text children.
+ #
+ # If a Text child already exists, the child is replaced by this
+ # content. This means that Text content can be deleted by calling
+ # this method with a nil argument. In this case, the next Text
+ # child becomes the first Text child. In no case is the order of
+ # any siblings disturbed.
+ # text::
+ # If a String, a new Text child is created and added to
+ # this Element as the first Text child. If Text, the text is set
+ # as the first Child element. If nil, then any existing first Text
+ # child is removed.
+ # Returns:: this Element.
+ # doc = Document.new '<a><b/></a>'
+ # doc.root.text = 'Sean' #-> '<a><b/>Sean</a>'
+ # doc.root.text = 'Elliott' #-> '<a><b/>Elliott</a>'
+ # doc.root.add_element 'c' #-> '<a><b/>Elliott<c/></a>'
+ # doc.root.text = 'Russell' #-> '<a><b/>Russell<c/></a>'
+ # doc.root.text = nil #-> '<a><b/><c/></a>'
+ def text=( text )
+ if text.kind_of? String
+ text = Text.new( text, whitespace(), nil, raw() )
+ elsif text and !text.kind_of? Text
+ text = Text.new( text.to_s, whitespace(), nil, raw() )
+ end
+
+ old_text = get_text
+ if text.nil?
+ old_text.remove unless old_text.nil?
+ else
+ if old_text.nil?
+ self << text
+ else
+ old_text.replace_with( text )
+ end
+ end
+ return self
+ end
+
+ # A helper method to add a Text child. Actual Text instances can
+ # be added with regular Parent methods, such as add() and <<()
+ # text::
+ # if a String, a new Text instance is created and added
+ # to the parent. If Text, the object is added directly.
+ # Returns:: this Element
+ # e = Element.new('a') #-> <e/>
+ # e.add_text 'foo' #-> <e>foo</e>
+ # e.add_text Text.new(' bar') #-> <e>foo bar</e>
+ # Note that at the end of this example, the branch has <b>3</b> nodes; the 'e'
+ # element and <b>2</b> Text node children.
+ def add_text( text )
+ if text.kind_of? String
+ if @children[-1].kind_of? Text
+ @children[-1] << text
+ return
+ end
+ text = Text.new( text, whitespace(), nil, raw() )
+ end
+ self << text unless text.nil?
+ return self
+ end
+
+ def node_type
+ :element
+ end
+
+ def xpath
+ path_elements = []
+ cur = self
+ path_elements << __to_xpath_helper( self )
+ while cur.parent
+ cur = cur.parent
+ path_elements << __to_xpath_helper( cur )
+ end
+ return path_elements.reverse.join( "/" )
+ end
+
+ #################################################
+ # Attributes #
+ #################################################
+
+ def attribute( name, namespace=nil )
+ prefix = ''
+ if namespace
+ prefix = attributes.prefixes.each { |prefix|
+ return "#{prefix}:" if namespace( prefix ) == namespace
+ } || ''
+ end
+ attributes.get_attribute( "#{prefix}#{name}" )
+ end
+
+ # Evaluates to +true+ if this element has any attributes set, false
+ # otherwise.
+ def has_attributes?
+ return !@attributes.empty?
+ end
+
+ # Adds an attribute to this element, overwriting any existing attribute
+ # by the same name.
+ # key::
+ # can be either an Attribute or a String. If an Attribute,
+ # the attribute is added to the list of Element attributes. If String,
+ # the argument is used as the name of the new attribute, and the value
+ # parameter must be supplied.
+ # value::
+ # Required if +key+ is a String, and ignored if the first argument is
+ # an Attribute. This is a String, and is used as the value
+ # of the new Attribute.
+ # Returns:: the Attribute added
+ # e = Element.new 'e'
+ # e.add_attribute( 'a', 'b' ) #-> <e a='b'/>
+ # e.add_attribute( 'x:a', 'c' ) #-> <e a='b' x:a='c'/>
+ # e.add_attribute Attribute.new('b', 'd') #-> <e a='b' x:a='c' b='d'/>
+ def add_attribute( key, value=nil )
+ if key.kind_of? Attribute
+ @attributes << key
+ else
+ @attributes[key] = value
+ end
+ end
+
+ # Add multiple attributes to this element.
+ # hash:: is either a hash, or array of arrays
+ # el.add_attributes( {"name1"=>"value1", "name2"=>"value2"} )
+ # el.add_attributes( [ ["name1","value1"], ["name2"=>"value2"] ] )
+ def add_attributes hash
+ if hash.kind_of? Hash
+ hash.each_pair {|key, value| @attributes[key] = value }
+ elsif hash.kind_of? Array
+ hash.each { |value| @attributes[ value[0] ] = value[1] }
+ end
+ end
+
+ # Removes an attribute
+ # key::
+ # either an Attribute or a String. In either case, the
+ # attribute is found by matching the attribute name to the argument,
+ # and then removed. If no attribute is found, no action is taken.
+ # Returns::
+ # the attribute removed, or nil if this Element did not contain
+ # a matching attribute
+ # e = Element.new('E')
+ # e.add_attribute( 'name', 'Sean' ) #-> <E name='Sean'/>
+ # r = e.add_attribute( 'sur:name', 'Russell' ) #-> <E name='Sean' sur:name='Russell'/>
+ # e.delete_attribute( 'name' ) #-> <E sur:name='Russell'/>
+ # e.delete_attribute( r ) #-> <E/>
+ def delete_attribute(key)
+ attr = @attributes.get_attribute(key)
+ attr.remove unless attr.nil?
+ end
+
+ #################################################
+ # Other Utilities #
+ #################################################
+
+ # Get an array of all CData children.
+ # IMMUTABLE
+ def cdatas
+ find_all { |child| child.kind_of? CData }.freeze
+ end
+
+ # Get an array of all Comment children.
+ # IMMUTABLE
+ def comments
+ find_all { |child| child.kind_of? Comment }.freeze
+ end
+
+ # Get an array of all Instruction children.
+ # IMMUTABLE
+ def instructions
+ find_all { |child| child.kind_of? Instruction }.freeze
+ end
+
+ # Get an array of all Text children.
+ # IMMUTABLE
+ def texts
+ find_all { |child| child.kind_of? Text }.freeze
+ end
+
+ # Writes out this element, and recursively, all children.
+ # output::
+ # output an object which supports '<< string'; this is where the
+ # document will be written.
+ # indent::
+ # An integer. If -1, no indenting will be used; otherwise, the
+ # indentation will be this number of spaces, and children will be
+ # indented an additional amount. Defaults to -1
+ # transitive::
+ # If transitive is true and indent is >= 0, then the output will be
+ # pretty-printed in such a way that the added whitespace does not affect
+ # the parse tree of the document
+ # ie_hack::
+ # Internet Explorer is the worst piece of crap to have ever been
+ # written, with the possible exception of Windows itself. Since IE is
+ # unable to parse proper XML, we have to provide a hack to generate XML
+ # that IE's limited abilities can handle. This hack inserts a space
+ # before the /> on empty tags. Defaults to false
+ #
+ # out = ''
+ # doc.write( out ) #-> doc is written to the string 'out'
+ # doc.write( $stdout ) #-> doc written to the console
+ def write(writer=$stdout, indent=-1, transitive=false, ie_hack=false)
+ #print "ID:#{indent}"
+ writer << "<#@expanded_name"
+
+ @attributes.each_attribute do |attr|
+ writer << " "
+ attr.write( writer, indent )
+ end unless @attributes.empty?
+
+ if @children.empty?
+ if transitive and indent>-1
+ writer << "\n"
+ indent( writer, indent )
+ elsif ie_hack
+ writer << " "
+ end
+ writer << "/"
+ else
+ if transitive and indent>-1 and !@children[0].kind_of? Text
+ writer << "\n"
+ indent writer, indent+1
+ end
+ writer << ">"
+ write_children( writer, indent, transitive, ie_hack )
+ writer << "</#{expanded_name}"
+ end
+ if transitive and indent>-1 and !@children.empty?
+ writer << "\n"
+ indent -= 1 if next_sibling.nil?
+ indent(writer, indent)
+ end
+ writer << ">"
+ end
+
+
+ private
+ def __to_xpath_helper node
+ rv = node.expanded_name.clone
+ if node.parent
+ results = node.parent.find_all {|n|
+ n.kind_of?(REXML::Element) and n.expanded_name == node.expanded_name
+ }
+ if results.length > 1
+ idx = results.index( node )
+ rv << "[#{idx+1}]"
+ end
+ end
+ rv
+ end
+
+ # A private helper method
+ def each_with_something( test, max=0, name=nil )
+ num = 0
+ child=nil
+ @elements.each( name ){ |child|
+ yield child if test.call(child) and num += 1
+ return if max>0 and num == max
+ }
+ end
+
+ # A private helper method
+ def write_children( writer, indent, transitive, ie_hack )
+ cr = (indent < 0) ? '' : "\n"
+ if indent == -1
+ each { |child| child.write( writer, indent, transitive, ie_hack ) }
+ else
+ next_indent = indent+1
+ last_child=nil
+ each { |child|
+ unless child.kind_of? Text or last_child.kind_of? Text or transitive
+ writer << cr
+ indent(writer, next_indent)
+ end
+ child.write( writer, next_indent, transitive, ie_hack )
+ last_child = child
+ }
+ unless last_child.kind_of? Text or transitive
+ writer << cr
+ indent( writer, indent )
+ end
+ end
+ end
+ end
+
+ ########################################################################
+ # ELEMENTS #
+ ########################################################################
+
+ # A class which provides filtering of children for Elements, and
+ # XPath search support. You are expected to only encounter this class as
+ # the <tt>element.elements</tt> object. Therefore, you are
+ # _not_ expected to instantiate this yourself.
+ class Elements
+ include Enumerable
+ # Constructor
+ # parent:: the parent Element
+ def initialize parent
+ @element = parent
+ end
+
+ # Fetches a child element. Filters only Element children, regardless of
+ # the XPath match.
+ # index::
+ # the search parameter. This is either an Integer, which
+ # will be used to find the index'th child Element, or an XPath,
+ # which will be used to search for the Element. <em>Because
+ # of the nature of XPath searches, any element in the connected XML
+ # document can be fetched through any other element.</em> <b>The
+ # Integer index is 1-based, not 0-based.</b> This means that the first
+ # child element is at index 1, not 0, and the +n+th element is at index
+ # +n+, not <tt>n-1</tt>. This is because XPath indexes element children
+ # starting from 1, not 0, and the indexes should be the same.
+ # name::
+ # optional, and only used in the first argument is an
+ # Integer. In that case, the index'th child Element that has the
+ # supplied name will be returned. Note again that the indexes start at 1.
+ # Returns:: the first matching Element, or nil if no child matched
+ # doc = Document.new '<a><b/><c id="1"/><c id="2"/><d/></a>'
+ # doc.root.elements[1] #-> <b/>
+ # doc.root.elements['c'] #-> <c id="1"/>
+ # doc.root.elements[2,'c'] #-> <c id="2"/>
+ def []( index, name=nil)
+ if index.kind_of? Integer
+ raise "index (#{index}) must be >= 1" if index < 1
+ name = literalize(name) if name
+ num = 0
+ child = nil
+ @element.find { |child|
+ child.kind_of? Element and
+ (name.nil? ? true : child.has_name?( name )) and
+ (num += 1) == index
+ }
+ else
+ return XPath::first( @element, index )
+ #{ |element|
+ # return element if element.kind_of? Element
+ #}
+ #return nil
+ end
+ end
+
+ # Sets an element, replacing any previous matching element. If no
+ # existing element is found ,the element is added.
+ # index:: Used to find a matching element to replace. See []().
+ # element::
+ # The element to replace the existing element with
+ # the previous element
+ # Returns:: nil if no previous element was found.
+ #
+ # doc = Document.new '<a/>'
+ # doc.root.elements[10] = Element.new('b') #-> <a><b/></a>
+ # doc.root.elements[1] #-> <b/>
+ # doc.root.elements[1] = Element.new('c') #-> <a><c/></a>
+ # doc.root.elements['c'] = Element.new('d') #-> <a><d/></a>
+ def []=( index, element )
+ previous = self[index]
+ if previous.nil?
+ @element.add element
+ else
+ previous.replace_with element
+ end
+ return previous
+ end
+
+ # Returns +true+ if there are no +Element+ children, +false+ otherwise
+ def empty?
+ @element.find{ |child| child.kind_of? Element}.nil?
+ end
+
+ # Returns the index of the supplied child (starting at 1), or -1 if
+ # the element is not a child
+ # element:: an +Element+ child
+ def index element
+ rv = 0
+ found = @element.find do |child|
+ child.kind_of? Element and
+ (rv += 1) and
+ child == element
+ end
+ return rv if found == element
+ return -1
+ end
+
+ # Deletes a child Element
+ # element::
+ # Either an Element, which is removed directly; an
+ # xpath, where the first matching child is removed; or an Integer,
+ # where the n'th Element is removed.
+ # Returns:: the removed child
+ # doc = Document.new '<a><b/><c/><c id="1"/></a>'
+ # b = doc.root.elements[1]
+ # doc.root.elements.delete b #-> <a><c/><c id="1"/></a>
+ # doc.elements.delete("a/c[@id='1']") #-> <a><c/></a>
+ # doc.root.elements.delete 1 #-> <a/>
+ def delete element
+ if element.kind_of? Element
+ @element.delete element
+ else
+ el = self[element]
+ el.remove if el
+ end
+ end
+
+ # Removes multiple elements. Filters for Element children, regardless of
+ # XPath matching.
+ # xpath:: all elements matching this String path are removed.
+ # Returns:: an Array of Elements that have been removed
+ # doc = Document.new '<a><c/><c/><c/><c/></a>'
+ # deleted = doc.elements.delete_all 'a/c' #-> [<c/>, <c/>, <c/>, <c/>]
+ def delete_all( xpath )
+ rv = []
+ XPath::each( @element, xpath) {|element|
+ rv << element if element.kind_of? Element
+ }
+ rv.each do |element|
+ @element.delete element
+ element.remove
+ end
+ return rv
+ end
+
+ # Adds an element
+ # element::
+ # if supplied, is either an Element, String, or
+ # Source (see Element.initialize). If not supplied or nil, a
+ # new, default Element will be constructed
+ # Returns:: the added Element
+ # a = Element.new 'a'
+ # a.elements.add Element.new 'b' #-> <a><b/></a>
+ # a.elements.add 'c' #-> <a><b/><c/></a>
+ def add element=nil
+ rv = nil
+ if element.nil?
+ Element.new "", self, @element.context
+ elsif not element.kind_of?(Element)
+ Element.new element, self, @element.context
+ else
+ @element << element
+ element.context = @element.context
+ element
+ end
+ end
+
+ alias :<< :add
+
+ # Iterates through all of the child Elements, optionally filtering
+ # them by a given XPath
+ # xpath::
+ # optional. If supplied, this is a String XPath, and is used to
+ # filter the children, so that only matching children are yielded. Note
+ # that XPaths are automatically filtered for Elements, so that
+ # non-Element children will not be yielded
+ # doc = Document.new '<a><b/><c/><d/>sean<b/><c/><d/></a>'
+ # doc.root.each {|e|p e} #-> Yields b, c, d, b, c, d elements
+ # doc.root.each('b') {|e|p e} #-> Yields b, b elements
+ # doc.root.each('child::node()') {|e|p e}
+ # #-> Yields <b/>, <c/>, <d/>, <b/>, <c/>, <d/>
+ # XPath.each(doc.root, 'child::node()', &block)
+ # #-> Yields <b/>, <c/>, <d/>, sean, <b/>, <c/>, <d/>
+ def each( xpath=nil, &block)
+ XPath::each( @element, xpath ) {|e| yield e if e.kind_of? Element }
+ end
+
+ # Returns the number of +Element+ children of the parent object.
+ # doc = Document.new '<a>sean<b/>elliott<b/>russell<b/></a>'
+ # doc.root.size #-> 6, 3 element and 3 text nodes
+ # doc.root.elements.size #-> 3
+ def size
+ count = 0
+ @element.each {|child| count+=1 if child.kind_of? Element }
+ count
+ end
+
+ # Returns an Array of Element children. An XPath may be supplied to
+ # filter the children. Only Element children are returned, even if the
+ # supplied XPath matches non-Element children.
+ # doc = Document.new '<a>sean<b/>elliott<c/></a>'
+ # doc.root.elements.to_a #-> [ <b/>, <c/> ]
+ # doc.root.elements.to_a("child::node()") #-> [ <b/>, <c/> ]
+ # XPath.match(doc.root, "child::node()") #-> [ sean, <b/>, elliott, <c/> ]
+ def to_a( xpath=nil )
+ rv = XPath.match( @element, xpath )
+ return rv.find_all{|e| e.kind_of? Element} if xpath
+ rv
+ end
+
+ private
+ # Private helper class. Removes quotes from quoted strings
+ def literalize name
+ name = name[1..-2] if name[0] == ?' or name[0] == ?" #'
+ name
+ end
+ end
+
+ ########################################################################
+ # ATTRIBUTES #
+ ########################################################################
+
+ # A class that defines the set of Attributes of an Element and provides
+ # operations for accessing elements in that set.
+ class Attributes < Hash
+ # Constructor
+ # element:: the Element of which this is an Attribute
+ def initialize element
+ @element = element
+ end
+
+ # Fetches an attribute value. If you want to get the Attribute itself,
+ # use get_attribute()
+ # name:: an XPath attribute name. Namespaces are relevant here.
+ # Returns::
+ # the String value of the matching attribute, or +nil+ if no
+ # matching attribute was found.
+ #
+ # doc = Document.new "<a foo:att='1' bar:att='2' att='3'/>"
+ # doc.root.attributes['att'] #-> '3'
+ # doc.root.attributes['bar:att'] #-> '2'
+ def [](name)
+ attr = get_attribute(name)
+ return attr.value unless attr.nil?
+ return nil
+ end
+
+ def to_a
+ values.flatten
+ end
+
+ # Returns the number of attributes the owning Element contains.
+ # doc = Document "<a x='1' y='2' foo:x='3'/>"
+ # doc.root.attributes.length #-> 3
+ def length
+ c = 0
+ each_attribute { c+=1 }
+ c
+ end
+ alias :size :length
+
+ # Itterates over the attributes of an Element. Yields actual Attribute
+ # nodes, not String values.
+ #
+ # doc = Document.new '<a x="1" y="2"/>'
+ # doc.root.attributes.each_attribute {|attr|
+ # p attr.expanded_name+" => "+attr.value
+ # }
+ def each_attribute # :yields: attribute
+ each_value do |val|
+ if val.kind_of? Attribute
+ yield val
+ else
+ val.each_value { |atr| yield atr }
+ end
+ end
+ end
+
+ # Itterates over each attribute of an Element, yielding the expanded name
+ # and value as a pair of Strings.
+ #
+ # doc = Document.new '<a x="1" y="2"/>'
+ # doc.root.attributes.each {|name, value| p name+" => "+value }
+ def each
+ each_attribute do |attr|
+ yield attr.expanded_name, attr.value
+ end
+ end
+
+ # Fetches an attribute
+ # name::
+ # the name by which to search for the attribute. Can be a
+ # <tt>prefix:name</tt> namespace name.
+ # Returns:: The first matching attribute, or nil if there was none. This
+ # value is an Attribute node, not the String value of the attribute.
+ # doc = Document.new '<a x:foo="1" foo="2" bar="3"/>'
+ # doc.root.attributes.get_attribute("foo").value #-> "2"
+ # doc.root.attributes.get_attribute("x:foo").value #-> "1"
+ def get_attribute( name )
+ attr = fetch( name, nil )
+ if attr.nil?
+ return nil if name.nil?
+ # Look for prefix
+ name =~ Namespace::NAMESPLIT
+ prefix, n = $1, $2
+ if prefix
+ attr = fetch( n, nil )
+ # check prefix
+ if attr == nil
+ elsif attr.kind_of? Attribute
+ return attr if prefix == attr.prefix
+ else
+ attr = attr[ prefix ]
+ return attr
+ end
+ end
+ element_document = @element.document
+ if element_document and element_document.doctype
+ expn = @element.expanded_name
+ expn = element_document.doctype.name if expn.size == 0
+ attr_val = element_document.doctype.attribute_of(expn, name)
+ return Attribute.new( name, attr_val ) if attr_val
+ end
+ return nil
+ end
+ if attr.kind_of? Hash
+ attr = attr[ @element.prefix ]
+ end
+ return attr
+ end
+
+ # Sets an attribute, overwriting any existing attribute value by the
+ # same name. Namespace is significant.
+ # name:: the name of the attribute
+ # value::
+ # (optional) If supplied, the value of the attribute. If
+ # nil, any existing matching attribute is deleted.
+ # Returns::
+ # Owning element
+ # doc = Document.new "<a x:foo='1' foo='3'/>"
+ # doc.root.attributes['y:foo'] = '2'
+ # doc.root.attributes['foo'] = '4'
+ # doc.root.attributes['x:foo'] = nil
+ def []=( name, value )
+ if value.nil? # Delete the named attribute
+ attr = get_attribute(name)
+ delete attr
+ return
+ end
+ value = Attribute.new(name, value) unless value.kind_of? Attribute
+ value.element = @element
+ old_attr = fetch(value.name, nil)
+ if old_attr.nil?
+ store(value.name, value)
+ elsif old_attr.kind_of? Hash
+ old_attr[value.prefix] = value
+ elsif old_attr.prefix != value.prefix
+ # Check for conflicting namespaces
+ raise ParseException.new(
+ "Namespace conflict in adding attribute \"#{value.name}\": "+
+ "Prefix \"#{old_attr.prefix}\" = "+
+ "\"#{@element.namespace(old_attr.prefix)}\" and prefix "+
+ "\"#{value.prefix}\" = \"#{@element.namespace(value.prefix)}\"") if
+ value.prefix != "xmlns" and old_attr.prefix != "xmlns" and
+ @element.namespace( old_attr.prefix ) ==
+ @element.namespace( value.prefix )
+ store value.name, { old_attr.prefix => old_attr,
+ value.prefix => value }
+ else
+ store value.name, value
+ end
+ return @element
+ end
+
+ # Returns an array of Strings containing all of the prefixes declared
+ # by this set of # attributes. The array does not include the default
+ # namespace declaration, if one exists.
+ # doc = Document.new("<a xmlns='foo' xmlns:x='bar' xmlns:y='twee' "+
+ # "z='glorp' p:k='gru'/>")
+ # prefixes = doc.root.attributes.prefixes #-> ['x', 'y']
+ def prefixes
+ ns = []
+ each_attribute do |attribute|
+ ns << attribute.name if attribute.prefix == 'xmlns'
+ end
+ if @element.document and @element.document.doctype
+ expn = @element.expanded_name
+ expn = @element.document.doctype.name if expn.size == 0
+ @element.document.doctype.attributes_of(expn).each {
+ |attribute|
+ ns << attribute.name if attribute.prefix == 'xmlns'
+ }
+ end
+ ns
+ end
+
+ def namespaces
+ namespaces = []
+ each_attribute do |attribute|
+ namespaces << attribute.value if attribute.prefix == 'xmlns' or attribute.name == 'xmlns'
+ end
+ if @element.document and @element.document.doctype
+ expn = @element.expanded_name
+ expn = @element.document.doctype.name if expn.size == 0
+ @element.document.doctype.attributes_of(expn).each {
+ |attribute|
+ namespaces << attribute.value if attribute.prefix == 'xmlns' or attribute.name == 'xmlns'
+ }
+ end
+ namespaces
+ end
+
+ # Removes an attribute
+ # attribute::
+ # either a String, which is the name of the attribute to remove --
+ # namespaces are significant here -- or the attribute to remove.
+ # Returns:: the owning element
+ # doc = Document.new "<a y:foo='0' x:foo='1' foo='3' z:foo='4'/>"
+ # doc.root.attributes.delete 'foo' #-> <a y:foo='0' x:foo='1' z:foo='4'/>"
+ # doc.root.attributes.delete 'x:foo' #-> <a y:foo='0' z:foo='4'/>"
+ # attr = doc.root.attributes.get_attribute('y:foo')
+ # doc.root.attributes.delete attr #-> <a z:foo='4'/>"
+ def delete( attribute )
+ name = nil
+ prefix = nil
+ if attribute.kind_of? Attribute
+ name = attribute.name
+ prefix = attribute.prefix
+ else
+ attribute =~ Namespace::NAMESPLIT
+ prefix, name = $1, $2
+ prefix = '' unless prefix
+ end
+ old = fetch(name, nil)
+ attr = nil
+ if old.kind_of? Hash # the supplied attribute is one of many
+ attr = old.delete(prefix)
+ if old.size == 1
+ repl = nil
+ old.each_value{|v| repl = v}
+ store name, repl
+ end
+ elsif old.nil?
+ return @element
+ else # the supplied attribute is a top-level one
+ attr = old
+ res = super(name)
+ end
+ @element
+ end
+
+ # Adds an attribute, overriding any existing attribute by the
+ # same name. Namespaces are significant.
+ # attribute:: An Attribute
+ def add( attribute )
+ self[attribute.name] = attribute
+ end
+
+ alias :<< :add
+
+ # Deletes all attributes matching a name. Namespaces are significant.
+ # name::
+ # A String; all attributes that match this path will be removed
+ # Returns:: an Array of the Attributes that were removed
+ def delete_all( name )
+ rv = []
+ each_attribute { |attribute|
+ rv << attribute if attribute.expanded_name == name
+ }
+ rv.each{ |attr| attr.remove }
+ return rv
+ end
+
+ # The +get_attribute_ns+ method retrieves a method by its namespace
+ # and name. Thus it is possible to reliably identify an attribute
+ # even if an XML processor has changed the prefix.
+ #
+ # Method contributed by Henrik Martensson
+ def get_attribute_ns(namespace, name)
+ each_attribute() { |attribute|
+ if name == attribute.name &&
+ namespace == attribute.namespace()
+ return attribute
+ end
+ }
+ nil
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/encoding.rb b/ruby_1_8_5/lib/rexml/encoding.rb
new file mode 100644
index 0000000000..644957439e
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/encoding.rb
@@ -0,0 +1,64 @@
+# -*- mode: ruby; ruby-indent-level: 2; indent-tabs-mode: t; tab-width: 2 -*- vim: sw=2 ts=2
+module REXML
+ module Encoding
+ @encoding_methods = {}
+ def self.register(enc, &block)
+ @encoding_methods[enc] = block
+ end
+ def self.apply(obj, enc)
+ @encoding_methods[enc][obj]
+ end
+ def self.encoding_method(enc)
+ @encoding_methods[enc]
+ end
+
+ # Native, default format is UTF-8, so it is declared here rather than in
+ # an encodings/ definition.
+ UTF_8 = 'UTF-8'
+ UTF_16 = 'UTF-16'
+ UNILE = 'UNILE'
+
+ # ID ---> Encoding name
+ attr_reader :encoding
+ def encoding=( enc )
+ old_verbosity = $VERBOSE
+ begin
+ $VERBOSE = false
+ return if defined? @encoding and enc == @encoding
+ if enc and enc != UTF_8
+ @encoding = enc.upcase
+ begin
+ require 'rexml/encodings/ICONV.rb'
+ Encoding.apply(self, "ICONV")
+ rescue LoadError, Exception => err
+ raise ArgumentError, "Bad encoding name #@encoding" unless @encoding =~ /^[\w-]+$/
+ @encoding.untaint
+ enc_file = File.join( "rexml", "encodings", "#@encoding.rb" )
+ begin
+ require enc_file
+ Encoding.apply(self, @encoding)
+ rescue LoadError
+ puts $!.message
+ raise ArgumentError, "No decoder found for encoding #@encoding. Please install iconv."
+ end
+ end
+ else
+ @encoding = UTF_8
+ require 'rexml/encodings/UTF-8.rb'
+ Encoding.apply(self, @encoding)
+ end
+ ensure
+ $VERBOSE = old_verbosity
+ end
+ end
+
+ def check_encoding str
+ # We have to recognize UTF-16, LSB UTF-16, and UTF-8
+ return UTF_16 if str[0] == 254 && str[1] == 255
+ return UNILE if str[0] == 255 && str[1] == 254
+ str =~ /^\s*<?xml\s*version=(['"]).*?\2\s*encoding=(["'])(.*?)\2/um
+ return $1.upcase if $1
+ return UTF_8
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/encodings/CP-1252.rb b/ruby_1_8_5/lib/rexml/encodings/CP-1252.rb
new file mode 100644
index 0000000000..51179f119f
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/encodings/CP-1252.rb
@@ -0,0 +1,98 @@
+#
+# This class was contributed by Mikko Tiihonen mikko DOT tiihonen AT hut DOT fi
+#
+module REXML
+ module Encoding
+ @@__REXML_encoding_methods = %q~
+ # Convert from UTF-8
+ def encode content
+ array_utf8 = content.unpack('U*')
+ array_enc = []
+ array_utf8.each do |num|
+ case num
+ # shortcut first bunch basic characters
+ when 0..0xFF: array_enc << num
+ # characters added compared to iso-8859-1
+ when 0x20AC: array_enc << 0x80 # 0xe2 0x82 0xac
+ when 0x201A: array_enc << 0x82 # 0xe2 0x82 0x9a
+ when 0x0192: array_enc << 0x83 # 0xc6 0x92
+ when 0x201E: array_enc << 0x84 # 0xe2 0x82 0x9e
+ when 0x2026: array_enc << 0x85 # 0xe2 0x80 0xa6
+ when 0x2020: array_enc << 0x86 # 0xe2 0x80 0xa0
+ when 0x2021: array_enc << 0x87 # 0xe2 0x80 0xa1
+ when 0x02C6: array_enc << 0x88 # 0xcb 0x86
+ when 0x2030: array_enc << 0x89 # 0xe2 0x80 0xb0
+ when 0x0160: array_enc << 0x8A # 0xc5 0xa0
+ when 0x2039: array_enc << 0x8B # 0xe2 0x80 0xb9
+ when 0x0152: array_enc << 0x8C # 0xc5 0x92
+ when 0x017D: array_enc << 0x8E # 0xc5 0xbd
+ when 0x2018: array_enc << 0x91 # 0xe2 0x80 0x98
+ when 0x2019: array_enc << 0x92 # 0xe2 0x80 0x99
+ when 0x201C: array_enc << 0x93 # 0xe2 0x80 0x9c
+ when 0x201D: array_enc << 0x94 # 0xe2 0x80 0x9d
+ when 0x2022: array_enc << 0x95 # 0xe2 0x80 0xa2
+ when 0x2013: array_enc << 0x96 # 0xe2 0x80 0x93
+ when 0x2014: array_enc << 0x97 # 0xe2 0x80 0x94
+ when 0x02DC: array_enc << 0x98 # 0xcb 0x9c
+ when 0x2122: array_enc << 0x99 # 0xe2 0x84 0xa2
+ when 0x0161: array_enc << 0x9A # 0xc5 0xa1
+ when 0x203A: array_enc << 0x9B # 0xe2 0x80 0xba
+ when 0x0152: array_enc << 0x9C # 0xc5 0x93
+ when 0x017E: array_enc << 0x9E # 0xc5 0xbe
+ when 0x0178: array_enc << 0x9F # 0xc5 0xb8
+ else
+ # all remaining basic characters can be used directly
+ if num <= 0xFF
+ array_enc << num
+ else
+ # Numeric entity (&#nnnn;); shard by Stefan Scholl
+ array_enc.concat "&\##{num};".unpack('C*')
+ end
+ end
+ end
+ array_enc.pack('C*')
+ end
+
+ # Convert to UTF-8
+ def decode(str)
+ array_latin9 = str.unpack('C*')
+ array_enc = []
+ array_latin9.each do |num|
+ case num
+ # characters that added compared to iso-8859-1
+ when 0x80: array_enc << 0x20AC # 0xe2 0x82 0xac
+ when 0x82: array_enc << 0x201A # 0xe2 0x82 0x9a
+ when 0x83: array_enc << 0x0192 # 0xc6 0x92
+ when 0x84: array_enc << 0x201E # 0xe2 0x82 0x9e
+ when 0x85: array_enc << 0x2026 # 0xe2 0x80 0xa6
+ when 0x86: array_enc << 0x2020 # 0xe2 0x80 0xa0
+ when 0x87: array_enc << 0x2021 # 0xe2 0x80 0xa1
+ when 0x88: array_enc << 0x02C6 # 0xcb 0x86
+ when 0x89: array_enc << 0x2030 # 0xe2 0x80 0xb0
+ when 0x8A: array_enc << 0x0160 # 0xc5 0xa0
+ when 0x8B: array_enc << 0x2039 # 0xe2 0x80 0xb9
+ when 0x8C: array_enc << 0x0152 # 0xc5 0x92
+ when 0x8E: array_enc << 0x017D # 0xc5 0xbd
+ when 0x91: array_enc << 0x2018 # 0xe2 0x80 0x98
+ when 0x92: array_enc << 0x2019 # 0xe2 0x80 0x99
+ when 0x93: array_enc << 0x201C # 0xe2 0x80 0x9c
+ when 0x94: array_enc << 0x201D # 0xe2 0x80 0x9d
+ when 0x95: array_enc << 0x2022 # 0xe2 0x80 0xa2
+ when 0x96: array_enc << 0x2013 # 0xe2 0x80 0x93
+ when 0x97: array_enc << 0x2014 # 0xe2 0x80 0x94
+ when 0x98: array_enc << 0x02DC # 0xcb 0x9c
+ when 0x99: array_enc << 0x2122 # 0xe2 0x84 0xa2
+ when 0x9A: array_enc << 0x0161 # 0xc5 0xa1
+ when 0x9B: array_enc << 0x203A # 0xe2 0x80 0xba
+ when 0x9C: array_enc << 0x0152 # 0xc5 0x93
+ when 0x9E: array_enc << 0x017E # 0xc5 0xbe
+ when 0x9F: array_enc << 0x0178 # 0xc5 0xb8
+ else
+ array_enc << num
+ end
+ end
+ array_enc.pack('U*')
+ end
+ ~
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/encodings/EUC-JP.rb b/ruby_1_8_5/lib/rexml/encodings/EUC-JP.rb
new file mode 100644
index 0000000000..db37b6bf0d
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/encodings/EUC-JP.rb
@@ -0,0 +1,35 @@
+module REXML
+ module Encoding
+ begin
+ require 'uconv'
+
+ def decode_eucjp(str)
+ Uconv::euctou8(str)
+ end
+
+ def encode_eucjp content
+ Uconv::u8toeuc(content)
+ end
+ rescue LoadError
+ require 'nkf'
+
+ EUCTOU8 = '-Ewm0'
+ U8TOEUC = '-Wem0'
+
+ def decode_eucjp(str)
+ NKF.nkf(EUCTOU8, str)
+ end
+
+ def encode_eucjp content
+ NKF.nkf(U8TOEUC, content)
+ end
+ end
+
+ register("EUC-JP") do |obj|
+ class << obj
+ alias decode decode_eucjp
+ alias encode encode_eucjp
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/encodings/ICONV.rb b/ruby_1_8_5/lib/rexml/encodings/ICONV.rb
new file mode 100644
index 0000000000..172fba7cd1
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/encodings/ICONV.rb
@@ -0,0 +1,22 @@
+require "iconv"
+raise LoadError unless defined? Iconv
+
+module REXML
+ module Encoding
+ def decode_iconv(str)
+ Iconv.conv(UTF_8, @encoding, str)
+ end
+
+ def encode_iconv(content)
+ Iconv.conv(@encoding, UTF_8, content)
+ end
+
+ register("ICONV") do |obj|
+ Iconv.conv(UTF_8, obj.encoding, nil)
+ class << obj
+ alias decode decode_iconv
+ alias encode encode_iconv
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/encodings/ISO-8859-1.rb b/ruby_1_8_5/lib/rexml/encodings/ISO-8859-1.rb
new file mode 100644
index 0000000000..2873d13bf0
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/encodings/ISO-8859-1.rb
@@ -0,0 +1,7 @@
+require 'rexml/encodings/US-ASCII'
+
+module REXML
+ module Encoding
+ register("ISO-8859-1", &encoding_method("US-ASCII"))
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/encodings/ISO-8859-15.rb b/ruby_1_8_5/lib/rexml/encodings/ISO-8859-15.rb
new file mode 100644
index 0000000000..ce565e7dd5
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/encodings/ISO-8859-15.rb
@@ -0,0 +1,69 @@
+#
+# This class was contributed by Mikko Tiihonen mikko DOT tiihonen AT hut DOT fi
+#
+module REXML
+ module Encoding
+ @@__REXML_encoding_methods = %q~
+ # Convert from UTF-8
+ def to_iso_8859_15 content
+ array_utf8 = content.unpack('U*')
+ array_enc = []
+ array_utf8.each do |num|
+ case num
+ # shortcut first bunch basic characters
+ when 0..0xA3: array_enc << num
+ # characters removed compared to iso-8859-1
+ when 0xA4: array_enc << '&#164;'
+ when 0xA6: array_enc << '&#166;'
+ when 0xA8: array_enc << '&#168;'
+ when 0xB4: array_enc << '&#180;'
+ when 0xB8: array_enc << '&#184;'
+ when 0xBC: array_enc << '&#188;'
+ when 0xBD: array_enc << '&#189;'
+ when 0xBE: array_enc << '&#190;'
+ # characters added compared to iso-8859-1
+ when 0x20AC: array_enc << 0xA4 # 0xe2 0x82 0xac
+ when 0x0160: array_enc << 0xA6 # 0xc5 0xa0
+ when 0x0161: array_enc << 0xA8 # 0xc5 0xa1
+ when 0x017D: array_enc << 0xB4 # 0xc5 0xbd
+ when 0x017E: array_enc << 0xB8 # 0xc5 0xbe
+ when 0x0152: array_enc << 0xBC # 0xc5 0x92
+ when 0x0153: array_enc << 0xBD # 0xc5 0x93
+ when 0x0178: array_enc << 0xBE # 0xc5 0xb8
+ else
+ # all remaining basic characters can be used directly
+ if num <= 0xFF
+ array_enc << num
+ else
+ # Numeric entity (&#nnnn;); shard by Stefan Scholl
+ array_enc.concat "&\##{num};".unpack('C*')
+ end
+ end
+ end
+ array_enc.pack('C*')
+ end
+
+ # Convert to UTF-8
+ def from_iso_8859_15(str)
+ array_latin9 = str.unpack('C*')
+ array_enc = []
+ array_latin9.each do |num|
+ case num
+ # characters that differ compared to iso-8859-1
+ when 0xA4: array_enc << 0x20AC
+ when 0xA6: array_enc << 0x0160
+ when 0xA8: array_enc << 0x0161
+ when 0xB4: array_enc << 0x017D
+ when 0xB8: array_enc << 0x017E
+ when 0xBC: array_enc << 0x0152
+ when 0xBD: array_enc << 0x0153
+ when 0xBE: array_enc << 0x0178
+ else
+ array_enc << num
+ end
+ end
+ array_enc.pack('U*')
+ end
+ ~
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/encodings/SHIFT-JIS.rb b/ruby_1_8_5/lib/rexml/encodings/SHIFT-JIS.rb
new file mode 100644
index 0000000000..93c7877afd
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/encodings/SHIFT-JIS.rb
@@ -0,0 +1,37 @@
+module REXML
+ module Encoding
+ begin
+ require 'uconv'
+
+ def decode_sjis content
+ Uconv::sjistou8(content)
+ end
+
+ def encode_sjis(str)
+ Uconv::u8tosjis(str)
+ end
+ rescue LoadError
+ require 'nkf'
+
+ SJISTOU8 = '-Swm0'
+ U8TOSJIS = '-Wsm0'
+
+ def decode_sjis(str)
+ NKF.nkf(SJISTOU8, str)
+ end
+
+ def encode_sjis content
+ NKF.nkf(U8TOSJIS, content)
+ end
+ end
+
+ b = proc do |obj|
+ class << obj
+ alias decode decode_sjis
+ alias encode encode_sjis
+ end
+ end
+ register("SHIFT-JIS", &b)
+ register("SHIFT_JIS", &b)
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/encodings/SHIFT_JIS.rb b/ruby_1_8_5/lib/rexml/encodings/SHIFT_JIS.rb
new file mode 100644
index 0000000000..e355704a7c
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/encodings/SHIFT_JIS.rb
@@ -0,0 +1 @@
+require 'rexml/encodings/SHIFT-JIS'
diff --git a/ruby_1_8_5/lib/rexml/encodings/UNILE.rb b/ruby_1_8_5/lib/rexml/encodings/UNILE.rb
new file mode 100644
index 0000000000..0560a08361
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/encodings/UNILE.rb
@@ -0,0 +1,34 @@
+module REXML
+ module Encoding
+ def encode_unile content
+ array_utf8 = content.unpack("U*")
+ array_enc = []
+ array_utf8.each do |num|
+ if ((num>>16) > 0)
+ array_enc << ??
+ array_enc << 0
+ else
+ array_enc << (num & 0xFF)
+ array_enc << (num >> 8)
+ end
+ end
+ array_enc.pack('C*')
+ end
+
+ def decode_unile(str)
+ array_enc=str.unpack('C*')
+ array_utf8 = []
+ 2.step(array_enc.size-1, 2){|i|
+ array_utf8 << (array_enc.at(i) + array_enc.at(i+1)*0x100)
+ }
+ array_utf8.pack('U*')
+ end
+
+ register(UNILE) do |obj|
+ class << obj
+ alias decode decode_unile
+ alias encode encode_unile
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/encodings/US-ASCII.rb b/ruby_1_8_5/lib/rexml/encodings/US-ASCII.rb
new file mode 100644
index 0000000000..fb4c217074
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/encodings/US-ASCII.rb
@@ -0,0 +1,30 @@
+module REXML
+ module Encoding
+ # Convert from UTF-8
+ def encode_ascii content
+ array_utf8 = content.unpack('U*')
+ array_enc = []
+ array_utf8.each do |num|
+ if num <= 0x7F
+ array_enc << num
+ else
+ # Numeric entity (&#nnnn;); shard by Stefan Scholl
+ array_enc.concat "&\##{num};".unpack('C*')
+ end
+ end
+ array_enc.pack('C*')
+ end
+
+ # Convert to UTF-8
+ def decode_ascii(str)
+ str.unpack('C*').pack('U*')
+ end
+
+ register("US-ASCII") do |obj|
+ class << obj
+ alias decode decode_ascii
+ alias encode encode_ascii
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/encodings/UTF-16.rb b/ruby_1_8_5/lib/rexml/encodings/UTF-16.rb
new file mode 100644
index 0000000000..972169755e
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/encodings/UTF-16.rb
@@ -0,0 +1,34 @@
+module REXML
+ module Encoding
+ def encode_utf16 content
+ array_utf8 = content.unpack("U*")
+ array_enc = []
+ array_utf8.each do |num|
+ if ((num>>16) > 0)
+ array_enc << 0
+ array_enc << ??
+ else
+ array_enc << (num >> 8)
+ array_enc << (num & 0xFF)
+ end
+ end
+ array_enc.pack('C*')
+ end
+
+ def decode_utf16(str)
+ array_enc=str.unpack('C*')
+ array_utf8 = []
+ 2.step(array_enc.size-1, 2){|i|
+ array_utf8 << (array_enc.at(i+1) + array_enc.at(i)*0x100)
+ }
+ array_utf8.pack('U*')
+ end
+
+ register(UTF_16) do |obj|
+ class << obj
+ alias decode decode_utf16
+ alias encode encode_utf16
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/encodings/UTF-8.rb b/ruby_1_8_5/lib/rexml/encodings/UTF-8.rb
new file mode 100644
index 0000000000..bb08f44100
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/encodings/UTF-8.rb
@@ -0,0 +1,18 @@
+module REXML
+ module Encoding
+ def encode_utf8 content
+ content
+ end
+
+ def decode_utf8(str)
+ str
+ end
+
+ register(UTF_8) do |obj|
+ class << obj
+ alias decode decode_utf8
+ alias encode encode_utf8
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/entity.rb b/ruby_1_8_5/lib/rexml/entity.rb
new file mode 100644
index 0000000000..4b88a3c553
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/entity.rb
@@ -0,0 +1,159 @@
+require 'rexml/child'
+require 'rexml/source'
+require 'rexml/xmltokens'
+
+module REXML
+ # God, I hate DTDs. I really do. Why this idiot standard still
+ # plagues us is beyond me.
+ class Entity < Child
+ include XMLTokens
+ PUBIDCHAR = "\x20\x0D\x0Aa-zA-Z0-9\\-()+,./:=?;!*@$_%#"
+ SYSTEMLITERAL = %Q{((?:"[^"]*")|(?:'[^']*'))}
+ PUBIDLITERAL = %Q{("[#{PUBIDCHAR}']*"|'[#{PUBIDCHAR}]*')}
+ EXTERNALID = "(?:(?:(SYSTEM)\\s+#{SYSTEMLITERAL})|(?:(PUBLIC)\\s+#{PUBIDLITERAL}\\s+#{SYSTEMLITERAL}))"
+ NDATADECL = "\\s+NDATA\\s+#{NAME}"
+ PEREFERENCE = "%#{NAME};"
+ ENTITYVALUE = %Q{((?:"(?:[^%&"]|#{PEREFERENCE}|#{REFERENCE})*")|(?:'([^%&']|#{PEREFERENCE}|#{REFERENCE})*'))}
+ PEDEF = "(?:#{ENTITYVALUE}|#{EXTERNALID})"
+ ENTITYDEF = "(?:#{ENTITYVALUE}|(?:#{EXTERNALID}(#{NDATADECL})?))"
+ PEDECL = "<!ENTITY\\s+(%)\\s+#{NAME}\\s+#{PEDEF}\\s*>"
+ GEDECL = "<!ENTITY\\s+#{NAME}\\s+#{ENTITYDEF}\\s*>"
+ ENTITYDECL = /\s*(?:#{GEDECL})|(?:#{PEDECL})/um
+
+ attr_reader :name, :external, :ref, :ndata, :pubid
+
+ # Create a new entity. Simple entities can be constructed by passing a
+ # name, value to the constructor; this creates a generic, plain entity
+ # reference. For anything more complicated, you have to pass a Source to
+ # the constructor with the entity definiton, or use the accessor methods.
+ # +WARNING+: There is no validation of entity state except when the entity
+ # is read from a stream. If you start poking around with the accessors,
+ # you can easily create a non-conformant Entity. The best thing to do is
+ # dump the stupid DTDs and use XMLSchema instead.
+ #
+ # e = Entity.new( 'amp', '&' )
+ def initialize stream, value=nil, parent=nil, reference=false
+ super(parent)
+ @ndata = @pubid = @value = @external = nil
+ if stream.kind_of? Array
+ @name = stream[1]
+ if stream[-1] == '%'
+ @reference = true
+ stream.pop
+ else
+ @reference = false
+ end
+ if stream[2] =~ /SYSTEM|PUBLIC/
+ @external = stream[2]
+ if @external == 'SYSTEM'
+ @ref = stream[3]
+ @ndata = stream[4] if stream.size == 5
+ else
+ @pubid = stream[3]
+ @ref = stream[4]
+ end
+ else
+ @value = stream[2]
+ end
+ else
+ @reference = reference
+ @external = nil
+ @name = stream
+ @value = value
+ end
+ end
+
+ # Evaluates whether the given string matchs an entity definition,
+ # returning true if so, and false otherwise.
+ def Entity::matches? string
+ (ENTITYDECL =~ string) == 0
+ end
+
+ # Evaluates to the unnormalized value of this entity; that is, replacing
+ # all entities -- both %ent; and &ent; entities. This differs from
+ # +value()+ in that +value+ only replaces %ent; entities.
+ def unnormalized
+ v = value()
+ return nil if v.nil?
+ @unnormalized = Text::unnormalize(v, parent)
+ @unnormalized
+ end
+
+ #once :unnormalized
+
+ # Returns the value of this entity unprocessed -- raw. This is the
+ # normalized value; that is, with all %ent; and &ent; entities intact
+ def normalized
+ @value
+ end
+
+ # Write out a fully formed, correct entity definition (assuming the Entity
+ # object itself is valid.)
+ def write out, indent=-1
+ out << '<!ENTITY '
+ out << '% ' if @reference
+ out << @name
+ out << ' '
+ if @external
+ out << @external << ' '
+ if @pubid
+ q = @pubid.include?('"')?"'":'"'
+ out << q << @pubid << q << ' '
+ end
+ q = @ref.include?('"')?"'":'"'
+ out << q << @ref << q
+ out << ' NDATA ' << @ndata if @ndata
+ else
+ q = @value.include?('"')?"'":'"'
+ out << q << @value << q
+ end
+ out << '>'
+ end
+
+ # Returns this entity as a string. See write().
+ def to_s
+ rv = ''
+ write rv
+ rv
+ end
+
+ PEREFERENCE_RE = /#{PEREFERENCE}/um
+ # Returns the value of this entity. At the moment, only internal entities
+ # are processed. If the value contains internal references (IE,
+ # %blah;), those are replaced with their values. IE, if the doctype
+ # contains:
+ # <!ENTITY % foo "bar">
+ # <!ENTITY yada "nanoo %foo; nanoo>
+ # then:
+ # doctype.entity('yada').value #-> "nanoo bar nanoo"
+ def value
+ if @value
+ matches = @value.scan(PEREFERENCE_RE)
+ rv = @value.clone
+ if @parent
+ matches.each do |entity_reference|
+ entity_value = @parent.entity( entity_reference[0] )
+ rv.gsub!( /%#{entity_reference};/um, entity_value )
+ end
+ end
+ return rv
+ end
+ nil
+ end
+ end
+
+ # This is a set of entity constants -- the ones defined in the XML
+ # specification. These are +gt+, +lt+, +amp+, +quot+ and +apos+.
+ module EntityConst
+ # +>+
+ GT = Entity.new( 'gt', '>' )
+ # +<+
+ LT = Entity.new( 'lt', '<' )
+ # +&+
+ AMP = Entity.new( 'amp', '&' )
+ # +"+
+ QUOT = Entity.new( 'quot', '"' )
+ # +'+
+ APOS = Entity.new( 'apos', "'" )
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/functions.rb b/ruby_1_8_5/lib/rexml/functions.rb
new file mode 100644
index 0000000000..c09ffdeae7
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/functions.rb
@@ -0,0 +1,367 @@
+module REXML
+ # If you add a method, keep in mind two things:
+ # (1) the first argument will always be a list of nodes from which to
+ # filter. In the case of context methods (such as position), the function
+ # should return an array with a value for each child in the array.
+ # (2) all method calls from XML will have "-" replaced with "_".
+ # Therefore, in XML, "local-name()" is identical (and actually becomes)
+ # "local_name()"
+ module Functions
+ @@context = nil
+ @@namespace_context = {}
+ @@variables = {}
+
+ def Functions::namespace_context=(x) ; @@namespace_context=x ; end
+ def Functions::variables=(x) ; @@variables=x ; end
+ def Functions::namespace_context ; @@namespace_context ; end
+ def Functions::variables ; @@variables ; end
+
+ def Functions::context=(value); @@context = value; end
+
+ def Functions::text( )
+ if @@context[:node].node_type == :element
+ return @@context[:node].find_all{|n| n.node_type == :text}.collect{|n| n.value}
+ elsif @@context[:node].node_type == :text
+ return @@context[:node].value
+ else
+ return false
+ end
+ end
+
+ def Functions::last( )
+ @@context[:size]
+ end
+
+ def Functions::position( )
+ @@context[:index]
+ end
+
+ def Functions::count( node_set )
+ node_set.size
+ end
+
+ # Since REXML is non-validating, this method is not implemented as it
+ # requires a DTD
+ def Functions::id( object )
+ end
+
+ # UNTESTED
+ def Functions::local_name( node_set=nil )
+ get_namespace( node_set ) do |node|
+ return node.local_name
+ end
+ end
+
+ def Functions::namespace_uri( node_set=nil )
+ get_namespace( node_set ) {|node| node.namespace}
+ end
+
+ def Functions::name( node_set=nil )
+ get_namespace( node_set ) do |node|
+ node.expanded_name
+ end
+ end
+
+ # Helper method.
+ def Functions::get_namespace( node_set = nil )
+ if node_set == nil
+ yield @@context[:node] if defined? @@context[:node].namespace
+ else
+ if node_set.respond_to? :each
+ node_set.each { |node| yield node if defined? node.namespace }
+ elsif node_set.respond_to? :namespace
+ yield node_set
+ end
+ end
+ end
+
+ # A node-set is converted to a string by returning the string-value of the
+ # node in the node-set that is first in document order. If the node-set is
+ # empty, an empty string is returned.
+ #
+ # A number is converted to a string as follows
+ #
+ # NaN is converted to the string NaN
+ #
+ # positive zero is converted to the string 0
+ #
+ # negative zero is converted to the string 0
+ #
+ # positive infinity is converted to the string Infinity
+ #
+ # negative infinity is converted to the string -Infinity
+ #
+ # if the number is an integer, the number is represented in decimal form
+ # as a Number with no decimal point and no leading zeros, preceded by a
+ # minus sign (-) if the number is negative
+ #
+ # otherwise, the number is represented in decimal form as a Number
+ # including a decimal point with at least one digit before the decimal
+ # point and at least one digit after the decimal point, preceded by a
+ # minus sign (-) if the number is negative; there must be no leading zeros
+ # before the decimal point apart possibly from the one required digit
+ # immediately before the decimal point; beyond the one required digit
+ # after the decimal point there must be as many, but only as many, more
+ # digits as are needed to uniquely distinguish the number from all other
+ # IEEE 754 numeric values.
+ #
+ # The boolean false value is converted to the string false. The boolean
+ # true value is converted to the string true.
+ #
+ # An object of a type other than the four basic types is converted to a
+ # string in a way that is dependent on that type.
+ def Functions::string( object=nil )
+ #object = @context unless object
+ if object.instance_of? Array
+ string( object[0] )
+ elsif defined? object.node_type
+ if object.node_type == :attribute
+ object.value
+ elsif object.node_type == :element
+ object.text
+ else
+ object.to_s
+ end
+ else
+ object.to_s
+ end
+ end
+
+ # UNTESTED
+ def Functions::concat( *objects )
+ objects.join
+ end
+
+ # Fixed by Mike Stok
+ def Functions::starts_with( string, test )
+ string(string).index(string(test)) == 0
+ end
+
+ # Fixed by Mike Stok
+ def Functions::contains( string, test )
+ string(string).include? string(test)
+ end
+
+ # Kouhei fixed this
+ def Functions::substring_before( string, test )
+ ruby_string = string(string)
+ ruby_index = ruby_string.index(string(test))
+ if ruby_index.nil?
+ ""
+ else
+ ruby_string[ 0...ruby_index ]
+ end
+ end
+
+ # Kouhei fixed this too
+ def Functions::substring_after( string, test )
+ ruby_string = string(string)
+ test_string = string(test)
+ return $1 if ruby_string =~ /#{test}(.*)/
+ ""
+ end
+
+ # Take equal portions of Mike Stok and Sean Russell; mix
+ # vigorously, and pour into a tall, chilled glass. Serves 10,000.
+ def Functions::substring( string, start, length=nil )
+ ruby_string = string(string)
+ ruby_length = if length.nil?
+ ruby_string.length.to_f
+ else
+ number(length)
+ end
+ ruby_start = number(start)
+
+ # Handle the special cases
+ return '' if (
+ ruby_length.nan? or
+ ruby_start.nan? or
+ ruby_start.infinite?
+ )
+
+ infinite_length = ruby_length.infinite? == 1
+ ruby_length = ruby_string.length if infinite_length
+
+ # Now, get the bounds. The XPath bounds are 1..length; the ruby bounds
+ # are 0..length. Therefore, we have to offset the bounds by one.
+ ruby_start = ruby_start.round - 1
+ ruby_length = ruby_length.round
+
+ if ruby_start < 0
+ ruby_length += ruby_start unless infinite_length
+ ruby_start = 0
+ end
+ return '' if ruby_length <= 0
+ ruby_string[ruby_start,ruby_length]
+ end
+
+ # UNTESTED
+ def Functions::string_length( string )
+ string(string).length
+ end
+
+ # UNTESTED
+ def Functions::normalize_space( string=nil )
+ string = string(@@context[:node]) if string.nil?
+ if string.kind_of? Array
+ string.collect{|x| string.to_s.strip.gsub(/\s+/um, ' ') if string}
+ else
+ string.to_s.strip.gsub(/\s+/um, ' ')
+ end
+ end
+
+ # This is entirely Mike Stok's beast
+ def Functions::translate( string, tr1, tr2 )
+ from = string(tr1)
+ to = string(tr2)
+
+ # the map is our translation table.
+ #
+ # if a character occurs more than once in the
+ # from string then we ignore the second &
+ # subsequent mappings
+ #
+ # if a charactcer maps to nil then we delete it
+ # in the output. This happens if the from
+ # string is longer than the to string
+ #
+ # there's nothing about - or ^ being special in
+ # http://www.w3.org/TR/xpath#function-translate
+ # so we don't build ranges or negated classes
+
+ map = Hash.new
+ 0.upto(from.length - 1) { |pos|
+ from_char = from[pos]
+ unless map.has_key? from_char
+ map[from_char] =
+ if pos < to.length
+ to[pos]
+ else
+ nil
+ end
+ end
+ }
+
+ string(string).unpack('U*').collect { |c|
+ if map.has_key? c then map[c] else c end
+ }.compact.pack('U*')
+ end
+
+ # UNTESTED
+ def Functions::boolean( object=nil )
+ if object.kind_of? String
+ if object =~ /\d+/u
+ return object.to_f != 0
+ else
+ return object.size > 0
+ end
+ elsif object.kind_of? Array
+ object = object.find{|x| x and true}
+ end
+ return object ? true : false
+ end
+
+ # UNTESTED
+ def Functions::not( object )
+ not boolean( object )
+ end
+
+ # UNTESTED
+ def Functions::true( )
+ true
+ end
+
+ # UNTESTED
+ def Functions::false( )
+ false
+ end
+
+ # UNTESTED
+ def Functions::lang( language )
+ lang = false
+ node = @@context[:node]
+ attr = nil
+ until node.nil?
+ if node.node_type == :element
+ attr = node.attributes["xml:lang"]
+ unless attr.nil?
+ lang = compare_language(string(language), attr)
+ break
+ else
+ end
+ end
+ node = node.parent
+ end
+ lang
+ end
+
+ def Functions::compare_language lang1, lang2
+ lang2.downcase.index(lang1.downcase) == 0
+ end
+
+ # a string that consists of optional whitespace followed by an optional
+ # minus sign followed by a Number followed by whitespace is converted to
+ # the IEEE 754 number that is nearest (according to the IEEE 754
+ # round-to-nearest rule) to the mathematical value represented by the
+ # string; any other string is converted to NaN
+ #
+ # boolean true is converted to 1; boolean false is converted to 0
+ #
+ # a node-set is first converted to a string as if by a call to the string
+ # function and then converted in the same way as a string argument
+ #
+ # an object of a type other than the four basic types is converted to a
+ # number in a way that is dependent on that type
+ def Functions::number( object=nil )
+ object = @@context[:node] unless object
+ case object
+ when true
+ Float(1)
+ when false
+ Float(0)
+ when Array
+ number(string( object ))
+ when Numeric
+ object.to_f
+ else
+ str = string( object )
+ #puts "STRING OF #{object.inspect} = #{str}"
+ if str =~ /^\d+/
+ object.to_s.to_f
+ else
+ (0.0 / 0.0)
+ end
+ end
+ end
+
+ def Functions::sum( nodes )
+ nodes = [nodes] unless nodes.kind_of? Array
+ nodes.inject(0) { |r,n| r += number(string(n)) }
+ end
+
+ def Functions::floor( number )
+ number(number).floor
+ end
+
+ def Functions::ceiling( number )
+ number(number).ceil
+ end
+
+ def Functions::round( number )
+ begin
+ number(number).round
+ rescue FloatDomainError
+ number(number)
+ end
+ end
+
+ def Functions::processing_instruction( node )
+ node.node_type == :processing_instruction
+ end
+
+ def Functions::method_missing( id )
+ puts "METHOD MISSING #{id.id2name}"
+ XPath.match( @@context[:node], id.id2name )
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/instruction.rb b/ruby_1_8_5/lib/rexml/instruction.rb
new file mode 100644
index 0000000000..f24f7786f7
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/instruction.rb
@@ -0,0 +1,66 @@
+require "rexml/child"
+require "rexml/source"
+
+module REXML
+ # Represents an XML Instruction; IE, <? ... ?>
+ # TODO: Add parent arg (3rd arg) to constructor
+ class Instruction < Child
+ START = '<\?'
+ STOP = '\?>'
+
+ # target is the "name" of the Instruction; IE, the "tag" in <?tag ...?>
+ # content is everything else.
+ attr_accessor :target, :content
+
+ # Constructs a new Instruction
+ # @param target can be one of a number of things. If String, then
+ # the target of this instruction is set to this. If an Instruction,
+ # then the Instruction is shallowly cloned (target and content are
+ # copied). If a Source, then the source is scanned and parsed for
+ # an Instruction declaration.
+ # @param content Must be either a String, or a Parent. Can only
+ # be a Parent if the target argument is a Source. Otherwise, this
+ # String is set as the content of this instruction.
+ def initialize(target, content=nil)
+ if target.kind_of? String
+ super()
+ @target = target
+ @content = content
+ elsif target.kind_of? Instruction
+ super(content)
+ @target = target.target
+ @content = target.content
+ end
+ @content.strip! if @content
+ end
+
+ def clone
+ Instruction.new self
+ end
+
+ def write writer, indent=-1, transitive=false, ie_hack=false
+ indent(writer, indent)
+ writer << START.sub(/\\/u, '')
+ writer << @target
+ writer << ' '
+ writer << @content
+ writer << STOP.sub(/\\/u, '')
+ end
+
+ # @return true if other is an Instruction, and the content and target
+ # of the other matches the target and content of this object.
+ def ==( other )
+ other.kind_of? Instruction and
+ other.target == @target and
+ other.content == @content
+ end
+
+ def node_type
+ :processing_instruction
+ end
+
+ def inspect
+ "<?p-i #{target} ...?>"
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/light/node.rb b/ruby_1_8_5/lib/rexml/light/node.rb
new file mode 100644
index 0000000000..943ec5f1a0
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/light/node.rb
@@ -0,0 +1,196 @@
+require 'rexml/xmltokens'
+require 'rexml/light/node'
+
+# [ :element, parent, name, attributes, children* ]
+ # a = Node.new
+ # a << "B" # => <a>B</a>
+ # a.b # => <a>B<b/></a>
+ # a.b[1] # => <a>B<b/><b/><a>
+ # a.b[1]["x"] = "y" # => <a>B<b/><b x="y"/></a>
+ # a.b[0].c # => <a>B<b><c/></b><b x="y"/></a>
+ # a.b.c << "D" # => <a>B<b><c>D</c></b><b x="y"/></a>
+module REXML
+ module Light
+ # Represents a tagged XML element. Elements are characterized by
+ # having children, attributes, and names, and can themselves be
+ # children.
+ class Node
+ NAMESPLIT = /^(?:(#{XMLTokens::NCNAME_STR}):)?(#{XMLTokens::NCNAME_STR})/u
+ PARENTS = [ :element, :document, :doctype ]
+ # Create a new element.
+ def initialize node=nil
+ @node = node
+ if node.kind_of? String
+ node = [ :text, node ]
+ elsif node.nil?
+ node = [ :document, nil, nil ]
+ elsif node[0] == :start_element
+ node[0] = :element
+ elsif node[0] == :start_doctype
+ node[0] = :doctype
+ elsif node[0] == :start_document
+ node[0] = :document
+ end
+ end
+
+ def size
+ if PARENTS.include? @node[0]
+ @node[-1].size
+ else
+ 0
+ end
+ end
+
+ def each( &block )
+ size.times { |x| yield( at(x+4) ) }
+ end
+
+ def name
+ at(2)
+ end
+
+ def name=( name_str, ns=nil )
+ pfx = ''
+ pfx = "#{prefix(ns)}:" if ns
+ _old_put(2, "#{pfx}#{name_str}")
+ end
+
+ def parent=( node )
+ _old_put(1,node)
+ end
+
+ def local_name
+ namesplit
+ @name
+ end
+
+ def local_name=( name_str )
+ _old_put( 1, "#@prefix:#{name_str}" )
+ end
+
+ def prefix( namespace=nil )
+ prefix_of( self, namespace )
+ end
+
+ def namespace( prefix=prefix() )
+ namespace_of( self, prefix )
+ end
+
+ def namespace=( namespace )
+ @prefix = prefix( namespace )
+ pfx = ''
+ pfx = "#@prefix:" if @prefix.size > 0
+ _old_put(1, "#{pfx}#@name")
+ end
+
+ def []( reference, ns=nil )
+ if reference.kind_of? String
+ pfx = ''
+ pfx = "#{prefix(ns)}:" if ns
+ at(3)["#{pfx}#{reference}"]
+ elsif reference.kind_of? Range
+ _old_get( Range.new(4+reference.begin, reference.end, reference.exclude_end?) )
+ else
+ _old_get( 4+reference )
+ end
+ end
+
+ def =~( path )
+ XPath.match( self, path )
+ end
+
+ # Doesn't handle namespaces yet
+ def []=( reference, ns, value=nil )
+ if reference.kind_of? String
+ value = ns unless value
+ at( 3 )[reference] = value
+ elsif reference.kind_of? Range
+ _old_put( Range.new(3+reference.begin, reference.end, reference.exclude_end?), ns )
+ else
+ if value
+ _old_put( 4+reference, ns, value )
+ else
+ _old_put( 4+reference, ns )
+ end
+ end
+ end
+
+ # Append a child to this element, optionally under a provided namespace.
+ # The namespace argument is ignored if the element argument is an Element
+ # object. Otherwise, the element argument is a string, the namespace (if
+ # provided) is the namespace the element is created in.
+ def << element
+ if node_type() == :text
+ at(-1) << element
+ else
+ newnode = Node.new( element )
+ newnode.parent = self
+ self.push( newnode )
+ end
+ at(-1)
+ end
+
+ def node_type
+ _old_get(0)
+ end
+
+ def text=( foo )
+ replace = at(4).kind_of?(String)? 1 : 0
+ self._old_put(4,replace, normalizefoo)
+ end
+
+ def root
+ context = self
+ context = context.at(1) while context.at(1)
+ end
+
+ def has_name?( name, namespace = '' )
+ at(3) == name and namespace() == namespace
+ end
+
+ def children
+ self
+ end
+
+ def parent
+ at(1)
+ end
+
+ def to_s
+
+ end
+
+ private
+
+ def namesplit
+ return if @name.defined?
+ at(2) =~ NAMESPLIT
+ @prefix = '' || $1
+ @name = $2
+ end
+
+ def namespace_of( node, prefix=nil )
+ if not prefix
+ name = at(2)
+ name =~ NAMESPLIT
+ prefix = $1
+ end
+ to_find = 'xmlns'
+ to_find = "xmlns:#{prefix}" if not prefix.nil?
+ ns = at(3)[ to_find ]
+ ns ? ns : namespace_of( @node[0], prefix )
+ end
+
+ def prefix_of( node, namespace=nil )
+ if not namespace
+ name = node.name
+ name =~ NAMESPLIT
+ $1
+ else
+ ns = at(3).find { |k,v| v == namespace }
+ ns ? ns : prefix_of( node.parent, namespace )
+ end
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/namespace.rb b/ruby_1_8_5/lib/rexml/namespace.rb
new file mode 100644
index 0000000000..3e8790580b
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/namespace.rb
@@ -0,0 +1,47 @@
+require 'rexml/xmltokens'
+
+module REXML
+ # Adds named attributes to an object.
+ module Namespace
+ # The name of the object, valid if set
+ attr_reader :name, :expanded_name
+ # The expanded name of the object, valid if name is set
+ attr_accessor :prefix
+ include XMLTokens
+ NAMESPLIT = /^(?:(#{NCNAME_STR}):)?(#{NCNAME_STR})/u
+
+ # Sets the name and the expanded name
+ def name=( name )
+ @expanded_name = name
+ name =~ NAMESPLIT
+ if $1
+ @prefix = $1
+ else
+ @prefix = ""
+ @namespace = ""
+ end
+ @name = $2
+ end
+
+ # Compares names optionally WITH namespaces
+ def has_name?( other, ns=nil )
+ if ns
+ return (namespace() == ns and name() == other)
+ elsif other.include? ":"
+ return fully_expanded_name == other
+ else
+ return name == other
+ end
+ end
+
+ alias :local_name :name
+
+ # Fully expand the name, even if the prefix wasn't specified in the
+ # source file.
+ def fully_expanded_name
+ ns = prefix
+ return "#{ns}:#@name" if ns.size > 0
+ return @name
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/node.rb b/ruby_1_8_5/lib/rexml/node.rb
new file mode 100644
index 0000000000..e5dec72a9d
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/node.rb
@@ -0,0 +1,66 @@
+require "rexml/parseexception"
+
+module REXML
+ # Represents a node in the tree. Nodes are never encountered except as
+ # superclasses of other objects. Nodes have siblings.
+ module Node
+ # @return the next sibling (nil if unset)
+ def next_sibling_node
+ return nil if @parent.nil?
+ @parent[ @parent.index(self) + 1 ]
+ end
+
+ # @return the previous sibling (nil if unset)
+ def previous_sibling_node
+ return nil if @parent.nil?
+ ind = @parent.index(self)
+ return nil if ind == 0
+ @parent[ ind - 1 ]
+ end
+
+ def to_s indent=-1
+ rv = ""
+ write rv,indent
+ rv
+ end
+
+ def indent to, ind
+ if @parent and @parent.context and not @parent.context[:indentstyle].nil? then
+ indentstyle = @parent.context[:indentstyle]
+ else
+ indentstyle = ' '
+ end
+ to << indentstyle*ind unless ind<1
+ end
+
+ def parent?
+ false;
+ end
+
+
+ # Visit all subnodes of +self+ recursively
+ def each_recursive(&block) # :yields: node
+ self.elements.each {|node|
+ block.call(node)
+ node.each_recursive(&block)
+ }
+ end
+
+ # Find (and return) first subnode (recursively) for which the block
+ # evaluates to true. Returns +nil+ if none was found.
+ def find_first_recursive(&block) # :yields: node
+ each_recursive {|node|
+ return node if block.call(node)
+ }
+ return nil
+ end
+
+ # Returns the index that +self+ has in its parent's elements array, so that
+ # the following equation holds true:
+ #
+ # node == node.parent.elements[node.index_in_parent]
+ def index_in_parent
+ parent.index(self)+1
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/output.rb b/ruby_1_8_5/lib/rexml/output.rb
new file mode 100644
index 0000000000..be4d23d42d
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/output.rb
@@ -0,0 +1,24 @@
+require 'rexml/encoding'
+
+module REXML
+ class Output
+ include Encoding
+
+ attr_reader :encoding
+
+ def initialize real_IO, encd="iso-8859-1"
+ @output = real_IO
+ self.encoding = encd
+
+ @to_utf = encd == UTF_8 ? false : true
+ end
+
+ def <<( content )
+ @output << (@to_utf ? self.encode(content) : content)
+ end
+
+ def to_s
+ "Output[#{encoding}]"
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/parent.rb b/ruby_1_8_5/lib/rexml/parent.rb
new file mode 100644
index 0000000000..a20aaaef6b
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/parent.rb
@@ -0,0 +1,166 @@
+require "rexml/child"
+
+module REXML
+ # A parent has children, and has methods for accessing them. The Parent
+ # class is never encountered except as the superclass for some other
+ # object.
+ class Parent < Child
+ include Enumerable
+
+ # Constructor
+ # @param parent if supplied, will be set as the parent of this object
+ def initialize parent=nil
+ super(parent)
+ @children = []
+ end
+
+ def add( object )
+ #puts "PARENT GOTS #{size} CHILDREN"
+ object.parent = self
+ @children << object
+ #puts "PARENT NOW GOTS #{size} CHILDREN"
+ object
+ end
+
+ alias :push :add
+ alias :<< :push
+
+ def unshift( object )
+ object.parent = self
+ @children.unshift object
+ end
+
+ def delete( object )
+ found = false
+ @children.delete_if {|c| c.equal?(object) and found = true }
+ object.parent = nil if found
+ end
+
+ def each(&block)
+ @children.each(&block)
+ end
+
+ def delete_if( &block )
+ @children.delete_if(&block)
+ end
+
+ def delete_at( index )
+ @children.delete_at index
+ end
+
+ def each_index( &block )
+ @children.each_index(&block)
+ end
+
+ # Fetches a child at a given index
+ # @param index the Integer index of the child to fetch
+ def []( index )
+ @children[index]
+ end
+
+ alias :each_child :each
+
+
+
+ # Set an index entry. See Array.[]=
+ # @param index the index of the element to set
+ # @param opt either the object to set, or an Integer length
+ # @param child if opt is an Integer, this is the child to set
+ # @return the parent (self)
+ def []=( *args )
+ args[-1].parent = self
+ @children[*args[0..-2]] = args[-1]
+ end
+
+ # Inserts an child before another child
+ # @param child1 this is either an xpath or an Element. If an Element,
+ # child2 will be inserted before child1 in the child list of the parent.
+ # If an xpath, child2 will be inserted before the first child to match
+ # the xpath.
+ # @param child2 the child to insert
+ # @return the parent (self)
+ def insert_before( child1, child2 )
+ if child1.kind_of? String
+ child1 = XPath.first( self, child1 )
+ child1.parent.insert_before child1, child2
+ else
+ ind = index(child1)
+ child2.parent.delete(child2) if child2.parent
+ @children[ind,0] = child2
+ child2.parent = self
+ end
+ self
+ end
+
+ # Inserts an child after another child
+ # @param child1 this is either an xpath or an Element. If an Element,
+ # child2 will be inserted after child1 in the child list of the parent.
+ # If an xpath, child2 will be inserted after the first child to match
+ # the xpath.
+ # @param child2 the child to insert
+ # @return the parent (self)
+ def insert_after( child1, child2 )
+ if child1.kind_of? String
+ child1 = XPath.first( self, child1 )
+ child1.parent.insert_after child1, child2
+ else
+ ind = index(child1)+1
+ child2.parent.delete(child2) if child2.parent
+ @children[ind,0] = child2
+ child2.parent = self
+ end
+ self
+ end
+
+ def to_a
+ @children.dup
+ end
+
+ # Fetches the index of a given child
+ # @param child the child to get the index of
+ # @return the index of the child, or nil if the object is not a child
+ # of this parent.
+ def index( child )
+ count = -1
+ @children.find { |i| count += 1 ; i.hash == child.hash }
+ count
+ end
+
+ # @return the number of children of this parent
+ def size
+ @children.size
+ end
+
+ alias :length :size
+
+ # Replaces one child with another, making sure the nodelist is correct
+ # @param to_replace the child to replace (must be a Child)
+ # @param replacement the child to insert into the nodelist (must be a
+ # Child)
+ def replace_child( to_replace, replacement )
+ @children.map! {|c| c.equal?( to_replace ) ? replacement : c }
+ to_replace.parent = nil
+ replacement.parent = self
+ end
+
+ # Deeply clones this object. This creates a complete duplicate of this
+ # Parent, including all descendants.
+ def deep_clone
+ cl = clone()
+ each do |child|
+ if child.kind_of? Parent
+ cl << child.deep_clone
+ else
+ cl << child.clone
+ end
+ end
+ cl
+ end
+
+ alias :children :to_a
+
+ def parent?
+ true
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/parseexception.rb b/ruby_1_8_5/lib/rexml/parseexception.rb
new file mode 100644
index 0000000000..feb7a7e638
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/parseexception.rb
@@ -0,0 +1,51 @@
+module REXML
+ class ParseException < RuntimeError
+ attr_accessor :source, :parser, :continued_exception
+
+ def initialize( message, source=nil, parser=nil, exception=nil )
+ super(message)
+ @source = source
+ @parser = parser
+ @continued_exception = exception
+ end
+
+ def to_s
+ # Quote the original exception, if there was one
+ if @continued_exception
+ err = @continued_exception.inspect
+ err << "\n"
+ err << @continued_exception.backtrace.join("\n")
+ err << "\n...\n"
+ else
+ err = ""
+ end
+
+ # Get the stack trace and error message
+ err << super
+
+ # Add contextual information
+ if @source
+ err << "\nLine: #{line}\n"
+ err << "Position: #{position}\n"
+ err << "Last 80 unconsumed characters:\n"
+ err << @source.buffer[0..80].gsub(/\n/, ' ')
+ end
+
+ err
+ end
+
+ def position
+ @source.current_line[0] if @source and defined? @source.current_line and
+ @source.current_line
+ end
+
+ def line
+ @source.current_line[2] if @source and defined? @source.current_line and
+ @source.current_line
+ end
+
+ def context
+ @source.current_line
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/parsers/baseparser.rb b/ruby_1_8_5/lib/rexml/parsers/baseparser.rb
new file mode 100644
index 0000000000..bce4ba4c20
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/parsers/baseparser.rb
@@ -0,0 +1,456 @@
+require 'rexml/parseexception'
+require 'rexml/source'
+
+module REXML
+ module Parsers
+ # = Using the Pull Parser
+ # <em>This API is experimental, and subject to change.</em>
+ # parser = PullParser.new( "<a>text<b att='val'/>txet</a>" )
+ # while parser.has_next?
+ # res = parser.next
+ # puts res[1]['att'] if res.start_tag? and res[0] == 'b'
+ # end
+ # See the PullEvent class for information on the content of the results.
+ # The data is identical to the arguments passed for the various events to
+ # the StreamListener API.
+ #
+ # Notice that:
+ # parser = PullParser.new( "<a>BAD DOCUMENT" )
+ # while parser.has_next?
+ # res = parser.next
+ # raise res[1] if res.error?
+ # end
+ #
+ # Nat Price gave me some good ideas for the API.
+ class BaseParser
+ NCNAME_STR= '[\w:][\-\w\d.]*'
+ NAME_STR= "(?:#{NCNAME_STR}:)?#{NCNAME_STR}"
+
+ NAMECHAR = '[\-\w\d\.:]'
+ NAME = "([\\w:]#{NAMECHAR}*)"
+ NMTOKEN = "(?:#{NAMECHAR})+"
+ NMTOKENS = "#{NMTOKEN}(\\s+#{NMTOKEN})*"
+ REFERENCE = "(?:&#{NAME};|&#\\d+;|&#x[0-9a-fA-F]+;)"
+ REFERENCE_RE = /#{REFERENCE}/
+
+ DOCTYPE_START = /\A\s*<!DOCTYPE\s/um
+ DOCTYPE_PATTERN = /\s*<!DOCTYPE\s+(.*?)(\[|>)/um
+ ATTRIBUTE_PATTERN = /\s*(#{NAME_STR})\s*=\s*(["'])(.*?)\2/um
+ COMMENT_START = /\A<!--/u
+ COMMENT_PATTERN = /<!--(.*?)-->/um
+ CDATA_START = /\A<!\[CDATA\[/u
+ CDATA_END = /^\s*\]\s*>/um
+ CDATA_PATTERN = /<!\[CDATA\[(.*?)\]\]>/um
+ XMLDECL_START = /\A<\?xml\s/u;
+ XMLDECL_PATTERN = /<\?xml\s+(.*?)\?>/um
+ INSTRUCTION_START = /\A<\?/u
+ INSTRUCTION_PATTERN = /<\?(.*?)(\s+.*?)?\?>/um
+ TAG_MATCH = /^<((?>#{NAME_STR}))\s*((?>\s+#{NAME_STR}\s*=\s*(["']).*?\3)*)\s*(\/)?>/um
+ CLOSE_MATCH = /^\s*<\/(#{NAME_STR})\s*>/um
+
+ VERSION = /\bversion\s*=\s*["'](.*?)['"]/um
+ ENCODING = /\bencoding=["'](.*?)['"]/um
+ STANDALONE = /\bstandalone=["'](.*?)['"]/um
+
+ ENTITY_START = /^\s*<!ENTITY/
+ IDENTITY = /^([!\*\w\-]+)(\s+#{NCNAME_STR})?(\s+["'].*?['"])?(\s+['"].*?["'])?/u
+ ELEMENTDECL_START = /^\s*<!ELEMENT/um
+ ELEMENTDECL_PATTERN = /^\s*(<!ELEMENT.*?)>/um
+ SYSTEMENTITY = /^\s*(%.*?;)\s*$/um
+ ENUMERATION = "\\(\\s*#{NMTOKEN}(?:\\s*\\|\\s*#{NMTOKEN})*\\s*\\)"
+ NOTATIONTYPE = "NOTATION\\s+\\(\\s*#{NAME}(?:\\s*\\|\\s*#{NAME})*\\s*\\)"
+ ENUMERATEDTYPE = "(?:(?:#{NOTATIONTYPE})|(?:#{ENUMERATION}))"
+ ATTTYPE = "(CDATA|ID|IDREF|IDREFS|ENTITY|ENTITIES|NMTOKEN|NMTOKENS|#{ENUMERATEDTYPE})"
+ ATTVALUE = "(?:\"((?:[^<&\"]|#{REFERENCE})*)\")|(?:'((?:[^<&']|#{REFERENCE})*)')"
+ DEFAULTDECL = "(#REQUIRED|#IMPLIED|(?:(#FIXED\\s+)?#{ATTVALUE}))"
+ ATTDEF = "\\s+#{NAME}\\s+#{ATTTYPE}\\s+#{DEFAULTDECL}"
+ ATTDEF_RE = /#{ATTDEF}/
+ ATTLISTDECL_START = /^\s*<!ATTLIST/um
+ ATTLISTDECL_PATTERN = /^\s*<!ATTLIST\s+#{NAME}(?:#{ATTDEF})*\s*>/um
+ NOTATIONDECL_START = /^\s*<!NOTATION/um
+ PUBLIC = /^\s*<!NOTATION\s+(\w[\-\w]*)\s+(PUBLIC)\s+(["'])(.*?)\3(?:\s+(["'])(.*?)\5)?\s*>/um
+ SYSTEM = /^\s*<!NOTATION\s+(\w[\-\w]*)\s+(SYSTEM)\s+(["'])(.*?)\3\s*>/um
+
+ TEXT_PATTERN = /\A([^<]*)/um
+
+ # Entity constants
+ PUBIDCHAR = "\x20\x0D\x0Aa-zA-Z0-9\\-()+,./:=?;!*@$_%#"
+ SYSTEMLITERAL = %Q{((?:"[^"]*")|(?:'[^']*'))}
+ PUBIDLITERAL = %Q{("[#{PUBIDCHAR}']*"|'[#{PUBIDCHAR}]*')}
+ EXTERNALID = "(?:(?:(SYSTEM)\\s+#{SYSTEMLITERAL})|(?:(PUBLIC)\\s+#{PUBIDLITERAL}\\s+#{SYSTEMLITERAL}))"
+ NDATADECL = "\\s+NDATA\\s+#{NAME}"
+ PEREFERENCE = "%#{NAME};"
+ ENTITYVALUE = %Q{((?:"(?:[^%&"]|#{PEREFERENCE}|#{REFERENCE})*")|(?:'([^%&']|#{PEREFERENCE}|#{REFERENCE})*'))}
+ PEDEF = "(?:#{ENTITYVALUE}|#{EXTERNALID})"
+ ENTITYDEF = "(?:#{ENTITYVALUE}|(?:#{EXTERNALID}(#{NDATADECL})?))"
+ PEDECL = "<!ENTITY\\s+(%)\\s+#{NAME}\\s+#{PEDEF}\\s*>"
+ GEDECL = "<!ENTITY\\s+#{NAME}\\s+#{ENTITYDEF}\\s*>"
+ ENTITYDECL = /\s*(?:#{GEDECL})|(?:#{PEDECL})/um
+
+ EREFERENCE = /&(?!#{NAME};)/
+
+ DEFAULT_ENTITIES = {
+ 'gt' => [/&gt;/, '&gt;', '>', />/],
+ 'lt' => [/&lt;/, '&lt;', '<', /</],
+ 'quot' => [/&quot;/, '&quot;', '"', /"/],
+ "apos" => [/&apos;/, "&apos;", "'", /'/]
+ }
+
+ def initialize( source )
+ self.stream = source
+ end
+
+ def add_listener( listener )
+ if !defined?(@listeners) or !@listeners
+ @listeners = []
+ instance_eval <<-EOL
+ alias :_old_pull :pull
+ def pull
+ event = _old_pull
+ @listeners.each do |listener|
+ listener.receive event
+ end
+ event
+ end
+ EOL
+ end
+ @listeners << listener
+ end
+
+ attr_reader :source
+
+ def stream=( source )
+ @source = SourceFactory.create_from( source )
+ @closed = nil
+ @document_status = nil
+ @tags = []
+ @stack = []
+ @entities = []
+ end
+
+ def position
+ if @source.respond_to? :position
+ @source.position
+ else
+ # FIXME
+ 0
+ end
+ end
+
+ # Returns true if there are no more events
+ def empty?
+ #STDERR.puts "@source.empty? = #{@source.empty?}"
+ #STDERR.puts "@stack.empty? = #{@stack.empty?}"
+ return (@source.empty? and @stack.empty?)
+ end
+
+ # Returns true if there are more events. Synonymous with !empty?
+ def has_next?
+ return !(@source.empty? and @stack.empty?)
+ end
+
+ # Push an event back on the head of the stream. This method
+ # has (theoretically) infinite depth.
+ def unshift token
+ @stack.unshift(token)
+ end
+
+ # Peek at the +depth+ event in the stack. The first element on the stack
+ # is at depth 0. If +depth+ is -1, will parse to the end of the input
+ # stream and return the last event, which is always :end_document.
+ # Be aware that this causes the stream to be parsed up to the +depth+
+ # event, so you can effectively pre-parse the entire document (pull the
+ # entire thing into memory) using this method.
+ def peek depth=0
+ raise %Q[Illegal argument "#{depth}"] if depth < -1
+ temp = []
+ if depth == -1
+ temp.push(pull()) until empty?
+ else
+ while @stack.size+temp.size < depth+1
+ temp.push(pull())
+ end
+ end
+ @stack += temp if temp.size > 0
+ @stack[depth]
+ end
+
+ # Returns the next event. This is a +PullEvent+ object.
+ def pull
+ if @closed
+ x, @closed = @closed, nil
+ return [ :end_element, x ]
+ end
+ return [ :end_document ] if empty?
+ return @stack.shift if @stack.size > 0
+ @source.read if @source.buffer.size<2
+ #STDERR.puts "BUFFER = #{@source.buffer.inspect}"
+ if @document_status == nil
+ #@source.consume( /^\s*/um )
+ word = @source.match( /^((?:\s+)|(?:<[^>]*>))/um )
+ word = word[1] unless word.nil?
+ #STDERR.puts "WORD = #{word.inspect}"
+ case word
+ when COMMENT_START
+ return [ :comment, @source.match( COMMENT_PATTERN, true )[1] ]
+ when XMLDECL_START
+ #STDERR.puts "XMLDECL"
+ results = @source.match( XMLDECL_PATTERN, true )[1]
+ version = VERSION.match( results )
+ version = version[1] unless version.nil?
+ encoding = ENCODING.match(results)
+ encoding = encoding[1] unless encoding.nil?
+ @source.encoding = encoding
+ standalone = STANDALONE.match(results)
+ standalone = standalone[1] unless standalone.nil?
+ return [ :xmldecl, version, encoding, standalone ]
+ when INSTRUCTION_START
+ return [ :processing_instruction, *@source.match(INSTRUCTION_PATTERN, true)[1,2] ]
+ when DOCTYPE_START
+ md = @source.match( DOCTYPE_PATTERN, true )
+ identity = md[1]
+ close = md[2]
+ identity =~ IDENTITY
+ name = $1
+ raise REXML::ParseException("DOCTYPE is missing a name") if name.nil?
+ pub_sys = $2.nil? ? nil : $2.strip
+ long_name = $3.nil? ? nil : $3.strip
+ uri = $4.nil? ? nil : $4.strip
+ args = [ :start_doctype, name, pub_sys, long_name, uri ]
+ if close == ">"
+ @document_status = :after_doctype
+ @source.read if @source.buffer.size<2
+ md = @source.match(/^\s*/um, true)
+ @stack << [ :end_doctype ]
+ else
+ @document_status = :in_doctype
+ end
+ return args
+ when /^\s+/
+ else
+ @document_status = :after_doctype
+ @source.read if @source.buffer.size<2
+ md = @source.match(/\s*/um, true)
+ end
+ end
+ if @document_status == :in_doctype
+ md = @source.match(/\s*(.*?>)/um)
+ case md[1]
+ when SYSTEMENTITY
+ match = @source.match( SYSTEMENTITY, true )[1]
+ return [ :externalentity, match ]
+
+ when ELEMENTDECL_START
+ return [ :elementdecl, @source.match( ELEMENTDECL_PATTERN, true )[1] ]
+
+ when ENTITY_START
+ match = @source.match( ENTITYDECL, true ).to_a.compact
+ match[0] = :entitydecl
+ ref = false
+ if match[1] == '%'
+ ref = true
+ match.delete_at 1
+ end
+ # Now we have to sort out what kind of entity reference this is
+ if match[2] == 'SYSTEM'
+ # External reference
+ match[3] = match[3][1..-2] # PUBID
+ match.delete_at(4) if match.size > 4 # Chop out NDATA decl
+ # match is [ :entity, name, SYSTEM, pubid(, ndata)? ]
+ elsif match[2] == 'PUBLIC'
+ # External reference
+ match[3] = match[3][1..-2] # PUBID
+ match[4] = match[4][1..-2] # HREF
+ # match is [ :entity, name, PUBLIC, pubid, href ]
+ else
+ match[2] = match[2][1..-2]
+ match.pop if match.size == 4
+ # match is [ :entity, name, value ]
+ end
+ match << '%' if ref
+ return match
+ when ATTLISTDECL_START
+ md = @source.match( ATTLISTDECL_PATTERN, true )
+ raise REXML::ParseException.new( "Bad ATTLIST declaration!", @source ) if md.nil?
+ element = md[1]
+ contents = md[0]
+
+ pairs = {}
+ values = md[0].scan( ATTDEF_RE )
+ values.each do |attdef|
+ unless attdef[3] == "#IMPLIED"
+ attdef.compact!
+ val = attdef[3]
+ val = attdef[4] if val == "#FIXED "
+ pairs[attdef[0]] = val
+ end
+ end
+ return [ :attlistdecl, element, pairs, contents ]
+ when NOTATIONDECL_START
+ md = nil
+ if @source.match( PUBLIC )
+ md = @source.match( PUBLIC, true )
+ vals = [md[1],md[2],md[4],md[6]]
+ elsif @source.match( SYSTEM )
+ md = @source.match( SYSTEM, true )
+ vals = [md[1],md[2],nil,md[4]]
+ else
+ raise REXML::ParseException.new( "error parsing notation: no matching pattern", @source )
+ end
+ return [ :notationdecl, *vals ]
+ when CDATA_END
+ @document_status = :after_doctype
+ @source.match( CDATA_END, true )
+ return [ :end_doctype ]
+ end
+ end
+ begin
+ if @source.buffer[0] == ?<
+ if @source.buffer[1] == ?/
+ last_tag = @tags.pop
+ #md = @source.match_to_consume( '>', CLOSE_MATCH)
+ md = @source.match( CLOSE_MATCH, true )
+ raise REXML::ParseException.new( "Missing end tag for "+
+ "'#{last_tag}' (got \"#{md[1]}\")",
+ @source) unless last_tag == md[1]
+ return [ :end_element, last_tag ]
+ elsif @source.buffer[1] == ?!
+ md = @source.match(/\A(\s*[^>]*>)/um)
+ #STDERR.puts "SOURCE BUFFER = #{source.buffer}, #{source.buffer.size}"
+ raise REXML::ParseException.new("Malformed node", @source) unless md
+ if md[0][2] == ?-
+ md = @source.match( COMMENT_PATTERN, true )
+ return [ :comment, md[1] ] if md
+ else
+ md = @source.match( CDATA_PATTERN, true )
+ return [ :cdata, md[1] ] if md
+ end
+ raise REXML::ParseException.new( "Declarations can only occur "+
+ "in the doctype declaration.", @source)
+ elsif @source.buffer[1] == ??
+ md = @source.match( INSTRUCTION_PATTERN, true )
+ return [ :processing_instruction, md[1], md[2] ] if md
+ raise REXML::ParseException.new( "Bad instruction declaration",
+ @source)
+ else
+ # Get the next tag
+ md = @source.match(TAG_MATCH, true)
+ raise REXML::ParseException.new("malformed XML: missing tag start", @source) unless md
+ attrs = []
+ if md[2].size > 0
+ attrs = md[2].scan( ATTRIBUTE_PATTERN )
+ raise REXML::ParseException.new( "error parsing attributes: [#{attrs.join ', '}], excess = \"#$'\"", @source) if $' and $'.strip.size > 0
+ end
+
+ if md[4]
+ @closed = md[1]
+ else
+ @tags.push( md[1] )
+ end
+ attributes = {}
+ attrs.each { |a,b,c| attributes[a] = c }
+ return [ :start_element, md[1], attributes ]
+ end
+ else
+ md = @source.match( TEXT_PATTERN, true )
+ if md[0].length == 0
+ puts "EMPTY = #{empty?}"
+ puts "BUFFER = \"#{@source.buffer}\""
+ @source.match( /(\s+)/, true )
+ end
+ #STDERR.puts "GOT #{md[1].inspect}" unless md[0].length == 0
+ #return [ :text, "" ] if md[0].length == 0
+ # unnormalized = Text::unnormalize( md[1], self )
+ # return PullEvent.new( :text, md[1], unnormalized )
+ return [ :text, md[1] ]
+ end
+ rescue REXML::ParseException
+ raise
+ rescue Exception, NameError => error
+ raise REXML::ParseException.new( "Exception parsing",
+ @source, self, (error ? error : $!) )
+ end
+ return [ :dummy ]
+ end
+
+ def entity( reference, entities )
+ value = nil
+ value = entities[ reference ] if entities
+ if not value
+ value = DEFAULT_ENTITIES[ reference ]
+ value = value[2] if value
+ end
+ unnormalize( value, entities ) if value
+ end
+
+ # Escapes all possible entities
+ def normalize( input, entities=nil, entity_filter=nil )
+ copy = input.clone
+ # Doing it like this rather than in a loop improves the speed
+ copy.gsub!( EREFERENCE, '&amp;' )
+ entities.each do |key, value|
+ copy.gsub!( value, "&#{key};" ) unless entity_filter and
+ entity_filter.include?(entity)
+ end if entities
+ copy.gsub!( EREFERENCE, '&amp;' )
+ DEFAULT_ENTITIES.each do |key, value|
+ copy.gsub!( value[3], value[1] )
+ end
+ copy
+ end
+
+ # Unescapes all possible entities
+ def unnormalize( string, entities=nil, filter=nil )
+ rv = string.clone
+ rv.gsub!( /\r\n?/, "\n" )
+ matches = rv.scan( REFERENCE_RE )
+ return rv if matches.size == 0
+ rv.gsub!( /&#0*((?:\d+)|(?:x[a-fA-F0-9]+));/ ) {|m|
+ m=$1
+ m = "0#{m}" if m[0] == ?x
+ [Integer(m)].pack('U*')
+ }
+ matches.collect!{|x|x[0]}.compact!
+ if matches.size > 0
+ matches.each do |entity_reference|
+ unless filter and filter.include?(entity_reference)
+ entity_value = entity( entity_reference, entities )
+ if entity_value
+ re = /&#{entity_reference};/
+ rv.gsub!( re, entity_value )
+ end
+ end
+ end
+ matches.each do |entity_reference|
+ unless filter and filter.include?(entity_reference)
+ er = DEFAULT_ENTITIES[entity_reference]
+ rv.gsub!( er[0], er[2] ) if er
+ end
+ end
+ rv.gsub!( /&amp;/, '&' )
+ end
+ rv
+ end
+ end
+ end
+end
+
+=begin
+ case event[0]
+ when :start_element
+ when :text
+ when :end_element
+ when :processing_instruction
+ when :cdata
+ when :comment
+ when :xmldecl
+ when :start_doctype
+ when :end_doctype
+ when :externalentity
+ when :elementdecl
+ when :entity
+ when :attlistdecl
+ when :notationdecl
+ when :end_doctype
+ end
+=end
diff --git a/ruby_1_8_5/lib/rexml/parsers/lightparser.rb b/ruby_1_8_5/lib/rexml/parsers/lightparser.rb
new file mode 100644
index 0000000000..0f35034993
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/parsers/lightparser.rb
@@ -0,0 +1,60 @@
+require 'rexml/parsers/streamparser'
+require 'rexml/parsers/baseparser'
+require 'rexml/light/node'
+
+module REXML
+ module Parsers
+ class LightParser
+ def initialize stream
+ @stream = stream
+ @parser = REXML::Parsers::BaseParser.new( stream )
+ end
+
+ def add_listener( listener )
+ @parser.add_listener( listener )
+ end
+
+ def rewind
+ @stream.rewind
+ @parser.stream = @stream
+ end
+
+ def parse
+ root = context = [ :document ]
+ while true
+ event = @parser.pull
+ case event[0]
+ when :end_document
+ break
+ when :end_doctype
+ context = context[1]
+ when :start_element, :start_doctype
+ new_node = event
+ context << new_node
+ new_node[1,0] = [context]
+ context = new_node
+ when :end_element, :end_doctype
+ context = context[1]
+ else
+ new_node = event
+ context << new_node
+ new_node[1,0] = [context]
+ end
+ end
+ root
+ end
+ end
+
+ # An element is an array. The array contains:
+ # 0 The parent element
+ # 1 The tag name
+ # 2 A hash of attributes
+ # 3..-1 The child elements
+ # An element is an array of size > 3
+ # Text is a String
+ # PIs are [ :processing_instruction, target, data ]
+ # Comments are [ :comment, data ]
+ # DocTypes are DocType structs
+ # The root is an array with XMLDecls, Text, DocType, Array, Text
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/parsers/pullparser.rb b/ruby_1_8_5/lib/rexml/parsers/pullparser.rb
new file mode 100644
index 0000000000..36dc7160c3
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/parsers/pullparser.rb
@@ -0,0 +1,196 @@
+require 'forwardable'
+
+require 'rexml/parseexception'
+require 'rexml/parsers/baseparser'
+require 'rexml/xmltokens'
+
+module REXML
+ module Parsers
+ # = Using the Pull Parser
+ # <em>This API is experimental, and subject to change.</em>
+ # parser = PullParser.new( "<a>text<b att='val'/>txet</a>" )
+ # while parser.has_next?
+ # res = parser.next
+ # puts res[1]['att'] if res.start_tag? and res[0] == 'b'
+ # end
+ # See the PullEvent class for information on the content of the results.
+ # The data is identical to the arguments passed for the various events to
+ # the StreamListener API.
+ #
+ # Notice that:
+ # parser = PullParser.new( "<a>BAD DOCUMENT" )
+ # while parser.has_next?
+ # res = parser.next
+ # raise res[1] if res.error?
+ # end
+ #
+ # Nat Price gave me some good ideas for the API.
+ class PullParser
+ include XMLTokens
+ extend Forwardable
+
+ def_delegators( :@parser, :has_next? )
+ def_delegators( :@parser, :entity )
+ def_delegators( :@parser, :empty? )
+ def_delegators( :@parser, :source )
+
+ def initialize stream
+ @entities = {}
+ @listeners = nil
+ @parser = BaseParser.new( stream )
+ @my_stack = []
+ end
+
+ def add_listener( listener )
+ @listeners = [] unless @listeners
+ @listeners << listener
+ end
+
+ def each
+ while has_next?
+ yield self.pull
+ end
+ end
+
+ def peek depth=0
+ if @my_stack.length <= depth
+ (depth - @my_stack.length + 1).times {
+ e = PullEvent.new(@parser.pull)
+ @my_stack.push(e)
+ }
+ end
+ @my_stack[depth]
+ end
+
+ def pull
+ return @my_stack.shift if @my_stack.length > 0
+
+ event = @parser.pull
+ case event[0]
+ when :entitydecl
+ @entities[ event[1] ] =
+ event[2] unless event[2] =~ /PUBLIC|SYSTEM/
+ when :text
+ unnormalized = @parser.unnormalize( event[1], @entities )
+ event << unnormalized
+ end
+ PullEvent.new( event )
+ end
+
+ def unshift token
+ @my_stack.unshift token
+ end
+ end
+
+ # A parsing event. The contents of the event are accessed as an +Array?,
+ # and the type is given either by the ...? methods, or by accessing the
+ # +type+ accessor. The contents of this object vary from event to event,
+ # but are identical to the arguments passed to +StreamListener+s for each
+ # event.
+ class PullEvent
+ # The type of this event. Will be one of :tag_start, :tag_end, :text,
+ # :processing_instruction, :comment, :doctype, :attlistdecl, :entitydecl,
+ # :notationdecl, :entity, :cdata, :xmldecl, or :error.
+ def initialize(arg)
+ @contents = arg
+ end
+
+ def []( start, endd=nil)
+ if start.kind_of? Range
+ @contents.slice( start.begin+1 .. start.end )
+ elsif start.kind_of? Numeric
+ if endd.nil?
+ @contents.slice( start+1 )
+ else
+ @contents.slice( start+1, endd )
+ end
+ else
+ raise "Illegal argument #{start.inspect} (#{start.class})"
+ end
+ end
+
+ def event_type
+ @contents[0]
+ end
+
+ # Content: [ String tag_name, Hash attributes ]
+ def start_element?
+ @contents[0] == :start_element
+ end
+
+ # Content: [ String tag_name ]
+ def end_element?
+ @contents[0] == :end_element
+ end
+
+ # Content: [ String raw_text, String unnormalized_text ]
+ def text?
+ @contents[0] == :text
+ end
+
+ # Content: [ String text ]
+ def instruction?
+ @contents[0] == :processing_instruction
+ end
+
+ # Content: [ String text ]
+ def comment?
+ @contents[0] == :comment
+ end
+
+ # Content: [ String name, String pub_sys, String long_name, String uri ]
+ def doctype?
+ @contents[0] == :start_doctype
+ end
+
+ # Content: [ String text ]
+ def attlistdecl?
+ @contents[0] == :attlistdecl
+ end
+
+ # Content: [ String text ]
+ def elementdecl?
+ @contents[0] == :elementdecl
+ end
+
+ # Due to the wonders of DTDs, an entity declaration can be just about
+ # anything. There's no way to normalize it; you'll have to interpret the
+ # content yourself. However, the following is true:
+ #
+ # * If the entity declaration is an internal entity:
+ # [ String name, String value ]
+ # Content: [ String text ]
+ def entitydecl?
+ @contents[0] == :entitydecl
+ end
+
+ # Content: [ String text ]
+ def notationdecl?
+ @contents[0] == :notationdecl
+ end
+
+ # Content: [ String text ]
+ def entity?
+ @contents[0] == :entity
+ end
+
+ # Content: [ String text ]
+ def cdata?
+ @contents[0] == :cdata
+ end
+
+ # Content: [ String version, String encoding, String standalone ]
+ def xmldecl?
+ @contents[0] == :xmldecl
+ end
+
+ def error?
+ @contents[0] == :error
+ end
+
+ def inspect
+ @contents[0].to_s + ": " + @contents[1..-1].inspect
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/parsers/sax2parser.rb b/ruby_1_8_5/lib/rexml/parsers/sax2parser.rb
new file mode 100644
index 0000000000..61a216cec1
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/parsers/sax2parser.rb
@@ -0,0 +1,232 @@
+require 'rexml/parsers/baseparser'
+require 'rexml/parseexception'
+require 'rexml/namespace'
+require 'rexml/text'
+
+module REXML
+ module Parsers
+ # SAX2Parser
+ class SAX2Parser
+ def initialize source
+ @parser = BaseParser.new(source)
+ @listeners = []
+ @procs = []
+ @namespace_stack = []
+ @has_listeners = false
+ @tag_stack = []
+ @entities = {}
+ end
+
+ def add_listener( listener )
+ @parser.add_listener( listener )
+ end
+
+ # Listen arguments:
+ #
+ # Symbol, Array, Block
+ # Listen to Symbol events on Array elements
+ # Symbol, Block
+ # Listen to Symbol events
+ # Array, Listener
+ # Listen to all events on Array elements
+ # Array, Block
+ # Listen to :start_element events on Array elements
+ # Listener
+ # Listen to All events
+ #
+ # Symbol can be one of: :start_element, :end_element,
+ # :start_prefix_mapping, :end_prefix_mapping, :characters,
+ # :processing_instruction, :doctype, :attlistdecl, :elementdecl,
+ # :entitydecl, :notationdecl, :cdata, :xmldecl, :comment
+ #
+ # There is an additional symbol that can be listened for: :progress.
+ # This will be called for every event generated, passing in the current
+ # stream position.
+ #
+ # Array contains regular expressions or strings which will be matched
+ # against fully qualified element names.
+ #
+ # Listener must implement the methods in SAX2Listener
+ #
+ # Block will be passed the same arguments as a SAX2Listener method would
+ # be, where the method name is the same as the matched Symbol.
+ # See the SAX2Listener for more information.
+ def listen( *args, &blok )
+ if args[0].kind_of? Symbol
+ if args.size == 2
+ args[1].each { |match| @procs << [args[0], match, blok] }
+ else
+ add( [args[0], nil, blok] )
+ end
+ elsif args[0].kind_of? Array
+ if args.size == 2
+ args[0].each { |match| add( [nil, match, args[1]] ) }
+ else
+ args[0].each { |match| add( [ :start_element, match, blok ] ) }
+ end
+ else
+ add([nil, nil, args[0]])
+ end
+ end
+
+ def deafen( listener=nil, &blok )
+ if listener
+ @listeners.delete_if {|item| item[-1] == listener }
+ @has_listeners = false if @listeners.size == 0
+ else
+ @procs.delete_if {|item| item[-1] == blok }
+ end
+ end
+
+ def parse
+ @procs.each { |sym,match,block| block.call if sym == :start_document }
+ @listeners.each { |sym,match,block|
+ block.start_document if sym == :start_document or sym.nil?
+ }
+ root = context = []
+ while true
+ event = @parser.pull
+ case event[0]
+ when :end_document
+ handle( :end_document )
+ break
+ when :end_doctype
+ context = context[1]
+ when :start_element
+ @tag_stack.push(event[1])
+ # find the observers for namespaces
+ procs = get_procs( :start_prefix_mapping, event[1] )
+ listeners = get_listeners( :start_prefix_mapping, event[1] )
+ if procs or listeners
+ # break out the namespace declarations
+ # The attributes live in event[2]
+ event[2].each {|n, v| event[2][n] = @parser.normalize(v)}
+ nsdecl = event[2].find_all { |n, value| n =~ /^xmlns(:|$)/ }
+ nsdecl.collect! { |n, value| [ n[6..-1], value ] }
+ @namespace_stack.push({})
+ nsdecl.each do |n,v|
+ @namespace_stack[-1][n] = v
+ # notify observers of namespaces
+ procs.each { |ob| ob.call( n, v ) } if procs
+ listeners.each { |ob| ob.start_prefix_mapping(n, v) } if listeners
+ end
+ end
+ event[1] =~ Namespace::NAMESPLIT
+ prefix = $1
+ local = $2
+ uri = get_namespace(prefix)
+ # find the observers for start_element
+ procs = get_procs( :start_element, event[1] )
+ listeners = get_listeners( :start_element, event[1] )
+ # notify observers
+ procs.each { |ob| ob.call( uri, local, event[1], event[2] ) } if procs
+ listeners.each { |ob|
+ ob.start_element( uri, local, event[1], event[2] )
+ } if listeners
+ when :end_element
+ @tag_stack.pop
+ event[1] =~ Namespace::NAMESPLIT
+ prefix = $1
+ local = $2
+ uri = get_namespace(prefix)
+ # find the observers for start_element
+ procs = get_procs( :end_element, event[1] )
+ listeners = get_listeners( :end_element, event[1] )
+ # notify observers
+ procs.each { |ob| ob.call( uri, local, event[1] ) } if procs
+ listeners.each { |ob|
+ ob.end_element( uri, local, event[1] )
+ } if listeners
+
+ namespace_mapping = @namespace_stack.pop
+ # find the observers for namespaces
+ procs = get_procs( :end_prefix_mapping, event[1] )
+ listeners = get_listeners( :end_prefix_mapping, event[1] )
+ if procs or listeners
+ namespace_mapping.each do |prefix, uri|
+ # notify observers of namespaces
+ procs.each { |ob| ob.call( prefix ) } if procs
+ listeners.each { |ob| ob.end_prefix_mapping(prefix) } if listeners
+ end
+ end
+ when :text
+ #normalized = @parser.normalize( event[1] )
+ #handle( :characters, normalized )
+ copy = event[1].clone
+ @entities.each { |key, value| copy = copy.gsub("&#{key};", value) }
+ copy.gsub!( Text::NUMERICENTITY ) {|m|
+ m=$1
+ m = "0#{m}" if m[0] == ?x
+ [Integer(m)].pack('U*')
+ }
+ handle( :characters, copy )
+ when :entitydecl
+ @entities[ event[1] ] = event[2] if event.size == 3
+ handle( *event )
+ when :processing_instruction, :comment, :doctype, :attlistdecl,
+ :elementdecl, :cdata, :notationdecl, :xmldecl
+ handle( *event )
+ end
+ handle( :progress, @parser.position )
+ end
+ end
+
+ private
+ def handle( symbol, *arguments )
+ tag = @tag_stack[-1]
+ procs = get_procs( symbol, tag )
+ listeners = get_listeners( symbol, tag )
+ # notify observers
+ procs.each { |ob| ob.call( *arguments ) } if procs
+ listeners.each { |l|
+ l.send( symbol.to_s, *arguments )
+ } if listeners
+ end
+
+ # The following methods are duplicates, but it is faster than using
+ # a helper
+ def get_procs( symbol, name )
+ return nil if @procs.size == 0
+ @procs.find_all do |sym, match, block|
+ #puts sym.inspect+"=="+symbol.inspect+ "\t"+match.inspect+"=="+name.inspect+ "\t"+( (sym.nil? or symbol == sym) and ((name.nil? and match.nil?) or match.nil? or ( (name == match) or (match.kind_of? Regexp and name =~ match)))).to_s
+ (
+ (sym.nil? or symbol == sym) and
+ ((name.nil? and match.nil?) or match.nil? or (
+ (name == match) or
+ (match.kind_of? Regexp and name =~ match)
+ )
+ )
+ )
+ end.collect{|x| x[-1]}
+ end
+ def get_listeners( symbol, name )
+ return nil if @listeners.size == 0
+ @listeners.find_all do |sym, match, block|
+ (
+ (sym.nil? or symbol == sym) and
+ ((name.nil? and match.nil?) or match.nil? or (
+ (name == match) or
+ (match.kind_of? Regexp and name =~ match)
+ )
+ )
+ )
+ end.collect{|x| x[-1]}
+ end
+
+ def add( pair )
+ if pair[-1].respond_to? :call
+ @procs << pair unless @procs.include? pair
+ else
+ @listeners << pair unless @listeners.include? pair
+ @has_listeners = true
+ end
+ end
+
+ def get_namespace( prefix )
+ uris = (@namespace_stack.find_all { |ns| not ns[prefix].nil? }) ||
+ (@namespace_stack.find { |ns| not ns[nil].nil? })
+ uris[-1][prefix] unless uris.nil? or 0 == uris.size
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/parsers/streamparser.rb b/ruby_1_8_5/lib/rexml/parsers/streamparser.rb
new file mode 100644
index 0000000000..256d0f611c
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/parsers/streamparser.rb
@@ -0,0 +1,46 @@
+module REXML
+ module Parsers
+ class StreamParser
+ def initialize source, listener
+ @listener = listener
+ @parser = BaseParser.new( source )
+ end
+
+ def add_listener( listener )
+ @parser.add_listener( listener )
+ end
+
+ def parse
+ # entity string
+ while true
+ event = @parser.pull
+ case event[0]
+ when :end_document
+ return
+ when :start_element
+ attrs = event[2].each do |n, v|
+ event[2][n] = @parser.unnormalize( v )
+ end
+ @listener.tag_start( event[1], attrs )
+ when :end_element
+ @listener.tag_end( event[1] )
+ when :text
+ normalized = @parser.unnormalize( event[1] )
+ @listener.text( normalized )
+ when :processing_instruction
+ @listener.instruction( *event[1,2] )
+ when :start_doctype
+ @listener.doctype( *event[1..-1] )
+ when :end_doctype
+ # FIXME: remove this condition for milestone:3.2
+ @listener.doctype_end if @listener.respond_to? :doctype_end
+ when :comment, :attlistdecl, :cdata, :xmldecl, :elementdecl
+ @listener.send( event[0].to_s, *event[1..-1] )
+ when :entitydecl, :notationdecl
+ @listener.send( event[0].to_s, event[1..-1] )
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/parsers/treeparser.rb b/ruby_1_8_5/lib/rexml/parsers/treeparser.rb
new file mode 100644
index 0000000000..500a53f426
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/parsers/treeparser.rb
@@ -0,0 +1,94 @@
+require 'rexml/validation/validationexception'
+
+module REXML
+ module Parsers
+ class TreeParser
+ def initialize( source, build_context = Document.new )
+ @build_context = build_context
+ @parser = Parsers::BaseParser.new( source )
+ end
+
+ def add_listener( listener )
+ @parser.add_listener( listener )
+ end
+
+ def parse
+ tag_stack = []
+ in_doctype = false
+ entities = nil
+ begin
+ while true
+ event = @parser.pull
+ #STDERR.puts "TREEPARSER GOT #{event.inspect}"
+ case event[0]
+ when :end_document
+ unless tag_stack.empty?
+ raise ParseException.new("No close tag for #{tag_stack.inspect}")
+ end
+ return
+ when :start_element
+ tag_stack.push(event[1])
+ # find the observers for namespaces
+ @build_context = @build_context.add_element( event[1], event[2] )
+ when :end_element
+ tag_stack.pop
+ @build_context = @build_context.parent
+ when :text
+ if not in_doctype
+ if @build_context[-1].instance_of? Text
+ @build_context[-1] << event[1]
+ else
+ @build_context.add(
+ Text.new(event[1], @build_context.whitespace, nil, true)
+ ) unless (
+ @build_context.ignore_whitespace_nodes and
+ event[1].strip.size==0
+ )
+ end
+ end
+ when :comment
+ c = Comment.new( event[1] )
+ @build_context.add( c )
+ when :cdata
+ c = CData.new( event[1] )
+ @build_context.add( c )
+ when :processing_instruction
+ @build_context.add( Instruction.new( event[1], event[2] ) )
+ when :end_doctype
+ in_doctype = false
+ entities.each { |k,v| entities[k] = @build_context.entities[k].value }
+ @build_context = @build_context.parent
+ when :start_doctype
+ doctype = DocType.new( event[1..-1], @build_context )
+ @build_context = doctype
+ entities = {}
+ in_doctype = true
+ when :attlistdecl
+ n = AttlistDecl.new( event[1..-1] )
+ @build_context.add( n )
+ when :externalentity
+ n = ExternalEntity.new( event[1] )
+ @build_context.add( n )
+ when :elementdecl
+ n = ElementDecl.new( event[1] )
+ @build_context.add(n)
+ when :entitydecl
+ entities[ event[1] ] = event[2] unless event[2] =~ /PUBLIC|SYSTEM/
+ @build_context.add(Entity.new(event))
+ when :notationdecl
+ n = NotationDecl.new( *event[1..-1] )
+ @build_context.add( n )
+ when :xmldecl
+ x = XMLDecl.new( event[1], event[2], event[3] )
+ @build_context.add( x )
+ end
+ end
+ rescue REXML::Validation::ValidationException
+ raise
+ rescue
+ raise ParseException.new( $!.message, @parser.source, @parser, $! )
+ end
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/parsers/ultralightparser.rb b/ruby_1_8_5/lib/rexml/parsers/ultralightparser.rb
new file mode 100644
index 0000000000..adc4af18e2
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/parsers/ultralightparser.rb
@@ -0,0 +1,56 @@
+require 'rexml/parsers/streamparser'
+require 'rexml/parsers/baseparser'
+
+module REXML
+ module Parsers
+ class UltraLightParser
+ def initialize stream
+ @stream = stream
+ @parser = REXML::Parsers::BaseParser.new( stream )
+ end
+
+ def add_listener( listener )
+ @parser.add_listener( listener )
+ end
+
+ def rewind
+ @stream.rewind
+ @parser.stream = @stream
+ end
+
+ def parse
+ root = context = []
+ while true
+ event = @parser.pull
+ case event[0]
+ when :end_document
+ break
+ when :end_doctype
+ context = context[1]
+ when :start_element, :doctype
+ context << event
+ event[1,0] = [context]
+ context = event
+ when :end_element
+ context = context[1]
+ else
+ context << event
+ end
+ end
+ root
+ end
+ end
+
+ # An element is an array. The array contains:
+ # 0 The parent element
+ # 1 The tag name
+ # 2 A hash of attributes
+ # 3..-1 The child elements
+ # An element is an array of size > 3
+ # Text is a String
+ # PIs are [ :processing_instruction, target, data ]
+ # Comments are [ :comment, data ]
+ # DocTypes are DocType structs
+ # The root is an array with XMLDecls, Text, DocType, Array, Text
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/parsers/xpathparser.rb b/ruby_1_8_5/lib/rexml/parsers/xpathparser.rb
new file mode 100644
index 0000000000..6bac852d6b
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/parsers/xpathparser.rb
@@ -0,0 +1,692 @@
+require 'rexml/namespace'
+require 'rexml/xmltokens'
+
+module REXML
+ module Parsers
+ # You don't want to use this class. Really. Use XPath, which is a wrapper
+ # for this class. Believe me. You don't want to poke around in here.
+ # There is strange, dark magic at work in this code. Beware. Go back! Go
+ # back while you still can!
+ class XPathParser
+ include XMLTokens
+ LITERAL = /^'([^']*)'|^"([^"]*)"/u
+
+ def namespaces=( namespaces )
+ Functions::namespace_context = namespaces
+ @namespaces = namespaces
+ end
+
+ def parse path
+ path.gsub!(/([\(\[])\s+/, '\1') # Strip ignorable spaces
+ path.gsub!( /\s+([\]\)])/, '\1' )
+ parsed = []
+ path = OrExpr(path, parsed)
+ parsed
+ end
+
+ def predicate path
+ parsed = []
+ Predicate( "[#{path}]", parsed )
+ parsed
+ end
+
+ def abbreviate( path )
+ path = path.kind_of?(String) ? parse( path ) : path
+ string = ""
+ document = false
+ while path.size > 0
+ op = path.shift
+ case op
+ when :node
+ when :attribute
+ string << "/" if string.size > 0
+ string << "@"
+ when :child
+ string << "/" if string.size > 0
+ when :descendant_or_self
+ string << "/"
+ when :self
+ string << "."
+ when :parent
+ string << ".."
+ when :any
+ string << "*"
+ when :text
+ string << "text()"
+ when :following, :following_sibling,
+ :ancestor, :ancestor_or_self, :descendant,
+ :namespace, :preceding, :preceding_sibling
+ string << "/" unless string.size == 0
+ string << op.to_s.tr("_", "-")
+ string << "::"
+ when :qname
+ prefix = path.shift
+ name = path.shift
+ string << prefix+":" if prefix.size > 0
+ string << name
+ when :predicate
+ string << '['
+ string << predicate_to_string( path.shift ) {|x| abbreviate( x ) }
+ string << ']'
+ when :document
+ document = true
+ when :function
+ string << path.shift
+ string << "( "
+ string << predicate_to_string( path.shift[0] ) {|x| abbreviate( x )}
+ string << " )"
+ when :literal
+ string << %Q{ "#{path.shift}" }
+ else
+ string << "/" unless string.size == 0
+ string << "UNKNOWN("
+ string << op.inspect
+ string << ")"
+ end
+ end
+ string = "/"+string if document
+ return string
+ end
+
+ def expand( path )
+ path = path.kind_of?(String) ? parse( path ) : path
+ string = ""
+ document = false
+ while path.size > 0
+ op = path.shift
+ case op
+ when :node
+ string << "node()"
+ when :attribute, :child, :following, :following_sibling,
+ :ancestor, :ancestor_or_self, :descendant, :descendant_or_self,
+ :namespace, :preceding, :preceding_sibling, :self, :parent
+ string << "/" unless string.size == 0
+ string << op.to_s.tr("_", "-")
+ string << "::"
+ when :any
+ string << "*"
+ when :qname
+ prefix = path.shift
+ name = path.shift
+ string << prefix+":" if prefix.size > 0
+ string << name
+ when :predicate
+ string << '['
+ string << predicate_to_string( path.shift ) { |x| expand(x) }
+ string << ']'
+ when :document
+ document = true
+ else
+ string << "/" unless string.size == 0
+ string << "UNKNOWN("
+ string << op.inspect
+ string << ")"
+ end
+ end
+ string = "/"+string if document
+ return string
+ end
+
+ def predicate_to_string( path, &block )
+ string = ""
+ case path[0]
+ when :and, :or, :mult, :plus, :minus, :neq, :eq, :lt, :gt, :lteq, :gteq, :div, :mod, :union
+ op = path.shift
+ case op
+ when :eq
+ op = "="
+ when :lt
+ op = "<"
+ when :gt
+ op = ">"
+ when :lteq
+ op = "<="
+ when :gteq
+ op = ">="
+ when :neq
+ op = "!="
+ when :union
+ op = "|"
+ end
+ left = predicate_to_string( path.shift, &block )
+ right = predicate_to_string( path.shift, &block )
+ string << " "
+ string << left
+ string << " "
+ string << op.to_s
+ string << " "
+ string << right
+ string << " "
+ when :function
+ path.shift
+ name = path.shift
+ string << name
+ string << "( "
+ string << predicate_to_string( path.shift, &block )
+ string << " )"
+ when :literal
+ path.shift
+ string << " "
+ string << path.shift.inspect
+ string << " "
+ else
+ string << " "
+ string << yield( path )
+ string << " "
+ end
+ return string.squeeze(" ")
+ end
+
+ private
+ #LocationPath
+ # | RelativeLocationPath
+ # | '/' RelativeLocationPath?
+ # | '//' RelativeLocationPath
+ def LocationPath path, parsed
+ #puts "LocationPath '#{path}'"
+ path = path.strip
+ if path[0] == ?/
+ parsed << :document
+ if path[1] == ?/
+ parsed << :descendant_or_self
+ parsed << :node
+ path = path[2..-1]
+ else
+ path = path[1..-1]
+ end
+ end
+ #puts parsed.inspect
+ return RelativeLocationPath( path, parsed ) if path.size > 0
+ end
+
+ #RelativeLocationPath
+ # | Step
+ # | (AXIS_NAME '::' | '@' | '') AxisSpecifier
+ # NodeTest
+ # Predicate
+ # | '.' | '..' AbbreviatedStep
+ # | RelativeLocationPath '/' Step
+ # | RelativeLocationPath '//' Step
+ AXIS = /^(ancestor|ancestor-or-self|attribute|child|descendant|descendant-or-self|following|following-sibling|namespace|parent|preceding|preceding-sibling|self)::/
+ def RelativeLocationPath path, parsed
+ #puts "RelativeLocationPath #{path}"
+ while path.size > 0
+ # (axis or @ or <child::>) nodetest predicate >
+ # OR > / Step
+ # (. or ..) >
+ if path[0] == ?.
+ if path[1] == ?.
+ parsed << :parent
+ parsed << :node
+ path = path[2..-1]
+ else
+ parsed << :self
+ parsed << :node
+ path = path[1..-1]
+ end
+ else
+ if path[0] == ?@
+ #puts "ATTRIBUTE"
+ parsed << :attribute
+ path = path[1..-1]
+ # Goto Nodetest
+ elsif path =~ AXIS
+ parsed << $1.tr('-','_').intern
+ path = $'
+ # Goto Nodetest
+ else
+ parsed << :child
+ end
+
+ #puts "NODETESTING '#{path}'"
+ n = []
+ path = NodeTest( path, n)
+ #puts "NODETEST RETURNED '#{path}'"
+
+ if path[0] == ?[
+ path = Predicate( path, n )
+ end
+
+ parsed.concat(n)
+ end
+
+ if path.size > 0
+ if path[0] == ?/
+ if path[1] == ?/
+ parsed << :descendant_or_self
+ parsed << :node
+ path = path[2..-1]
+ else
+ path = path[1..-1]
+ end
+ else
+ return path
+ end
+ end
+ end
+ return path
+ end
+
+ # Returns a 1-1 map of the nodeset
+ # The contents of the resulting array are either:
+ # true/false, if a positive match
+ # String, if a name match
+ #NodeTest
+ # | ('*' | NCNAME ':' '*' | QNAME) NameTest
+ # | NODE_TYPE '(' ')' NodeType
+ # | PI '(' LITERAL ')' PI
+ # | '[' expr ']' Predicate
+ NCNAMETEST= /^(#{NCNAME_STR}):\*/u
+ QNAME = Namespace::NAMESPLIT
+ NODE_TYPE = /^(comment|text|node)\(\s*\)/m
+ PI = /^processing-instruction\(/
+ def NodeTest path, parsed
+ #puts "NodeTest with #{path}"
+ res = nil
+ case path
+ when /^\*/
+ path = $'
+ parsed << :any
+ when NODE_TYPE
+ type = $1
+ path = $'
+ parsed << type.tr('-', '_').intern
+ when PI
+ path = $'
+ literal = nil
+ if path !~ /^\s*\)/
+ path =~ LITERAL
+ literal = $1
+ path = $'
+ raise ParseException.new("Missing ')' after processing instruction") if path[0] != ?)
+ path = path[1..-1]
+ end
+ parsed << :processing_instruction
+ parsed << (literal || '')
+ when NCNAMETEST
+ #puts "NCNAMETEST"
+ prefix = $1
+ path = $'
+ parsed << :namespace
+ parsed << prefix
+ when QNAME
+ #puts "QNAME"
+ prefix = $1
+ name = $2
+ path = $'
+ prefix = "" unless prefix
+ parsed << :qname
+ parsed << prefix
+ parsed << name
+ end
+ return path
+ end
+
+ # Filters the supplied nodeset on the predicate(s)
+ def Predicate path, parsed
+ #puts "PREDICATE with #{path}"
+ return nil unless path[0] == ?[
+ predicates = []
+ while path[0] == ?[
+ path, expr = get_group(path)
+ predicates << expr[1..-2] if expr
+ end
+ #puts "PREDICATES = #{predicates.inspect}"
+ predicates.each{ |expr|
+ #puts "ORING #{expr}"
+ preds = []
+ parsed << :predicate
+ parsed << preds
+ OrExpr(expr, preds)
+ }
+ #puts "PREDICATES = #{predicates.inspect}"
+ path
+ end
+
+ # The following return arrays of true/false, a 1-1 mapping of the
+ # supplied nodeset, except for axe(), which returns a filtered
+ # nodeset
+
+ #| OrExpr S 'or' S AndExpr
+ #| AndExpr
+ def OrExpr path, parsed
+ #puts "OR >>> #{path}"
+ n = []
+ rest = AndExpr( path, n )
+ #puts "OR <<< #{rest}"
+ if rest != path
+ while rest =~ /^\s*( or )/
+ n = [ :or, n, [] ]
+ rest = AndExpr( $', n[-1] )
+ end
+ end
+ if parsed.size == 0 and n.size != 0
+ parsed.replace(n)
+ elsif n.size > 0
+ parsed << n
+ end
+ rest
+ end
+
+ #| AndExpr S 'and' S EqualityExpr
+ #| EqualityExpr
+ def AndExpr path, parsed
+ #puts "AND >>> #{path}"
+ n = []
+ rest = EqualityExpr( path, n )
+ #puts "AND <<< #{rest}"
+ if rest != path
+ while rest =~ /^\s*( and )/
+ n = [ :and, n, [] ]
+ #puts "AND >>> #{rest}"
+ rest = EqualityExpr( $', n[-1] )
+ #puts "AND <<< #{rest}"
+ end
+ end
+ if parsed.size == 0 and n.size != 0
+ parsed.replace(n)
+ elsif n.size > 0
+ parsed << n
+ end
+ rest
+ end
+
+ #| EqualityExpr ('=' | '!=') RelationalExpr
+ #| RelationalExpr
+ def EqualityExpr path, parsed
+ #puts "EQUALITY >>> #{path}"
+ n = []
+ rest = RelationalExpr( path, n )
+ #puts "EQUALITY <<< #{rest}"
+ if rest != path
+ while rest =~ /^\s*(!?=)\s*/
+ if $1[0] == ?!
+ n = [ :neq, n, [] ]
+ else
+ n = [ :eq, n, [] ]
+ end
+ rest = RelationalExpr( $', n[-1] )
+ end
+ end
+ if parsed.size == 0 and n.size != 0
+ parsed.replace(n)
+ elsif n.size > 0
+ parsed << n
+ end
+ rest
+ end
+
+ #| RelationalExpr ('<' | '>' | '<=' | '>=') AdditiveExpr
+ #| AdditiveExpr
+ def RelationalExpr path, parsed
+ #puts "RELATION >>> #{path}"
+ n = []
+ rest = AdditiveExpr( path, n )
+ #puts "RELATION <<< #{rest}"
+ if rest != path
+ while rest =~ /^\s*([<>]=?)\s*/
+ if $1[0] == ?<
+ sym = "lt"
+ else
+ sym = "gt"
+ end
+ sym << "eq" if $1[-1] == ?=
+ n = [ sym.intern, n, [] ]
+ rest = AdditiveExpr( $', n[-1] )
+ end
+ end
+ if parsed.size == 0 and n.size != 0
+ parsed.replace(n)
+ elsif n.size > 0
+ parsed << n
+ end
+ rest
+ end
+
+ #| AdditiveExpr ('+' | S '-') MultiplicativeExpr
+ #| MultiplicativeExpr
+ def AdditiveExpr path, parsed
+ #puts "ADDITIVE >>> #{path}"
+ n = []
+ rest = MultiplicativeExpr( path, n )
+ #puts "ADDITIVE <<< #{rest}"
+ if rest != path
+ while rest =~ /^\s*(\+| -)\s*/
+ if $1[0] == ?+
+ n = [ :plus, n, [] ]
+ else
+ n = [ :minus, n, [] ]
+ end
+ rest = MultiplicativeExpr( $', n[-1] )
+ end
+ end
+ if parsed.size == 0 and n.size != 0
+ parsed.replace(n)
+ elsif n.size > 0
+ parsed << n
+ end
+ rest
+ end
+
+ #| MultiplicativeExpr ('*' | S ('div' | 'mod') S) UnaryExpr
+ #| UnaryExpr
+ def MultiplicativeExpr path, parsed
+ #puts "MULT >>> #{path}"
+ n = []
+ rest = UnaryExpr( path, n )
+ #puts "MULT <<< #{rest}"
+ if rest != path
+ while rest =~ /^\s*(\*| div | mod )\s*/
+ if $1[0] == ?*
+ n = [ :mult, n, [] ]
+ elsif $1.include?( "div" )
+ n = [ :div, n, [] ]
+ else
+ n = [ :mod, n, [] ]
+ end
+ rest = UnaryExpr( $', n[-1] )
+ end
+ end
+ if parsed.size == 0 and n.size != 0
+ parsed.replace(n)
+ elsif n.size > 0
+ parsed << n
+ end
+ rest
+ end
+
+ #| '-' UnaryExpr
+ #| UnionExpr
+ def UnaryExpr path, parsed
+ path =~ /^(\-*)/
+ path = $'
+ if $1 and (($1.size % 2) != 0)
+ mult = -1
+ else
+ mult = 1
+ end
+ parsed << :neg if mult < 0
+
+ #puts "UNARY >>> #{path}"
+ n = []
+ path = UnionExpr( path, n )
+ #puts "UNARY <<< #{path}"
+ parsed.concat( n )
+ path
+ end
+
+ #| UnionExpr '|' PathExpr
+ #| PathExpr
+ def UnionExpr path, parsed
+ #puts "UNION >>> #{path}"
+ n = []
+ rest = PathExpr( path, n )
+ #puts "UNION <<< #{rest}"
+ if rest != path
+ while rest =~ /^\s*(\|)\s*/
+ n = [ :union, n, [] ]
+ rest = PathExpr( $', n[-1] )
+ end
+ end
+ if parsed.size == 0 and n.size != 0
+ parsed.replace( n )
+ elsif n.size > 0
+ parsed << n
+ end
+ rest
+ end
+
+ #| LocationPath
+ #| FilterExpr ('/' | '//') RelativeLocationPath
+ def PathExpr path, parsed
+ path =~ /^\s*/
+ path = $'
+ #puts "PATH >>> #{path}"
+ n = []
+ rest = FilterExpr( path, n )
+ #puts "PATH <<< '#{rest}'"
+ if rest != path
+ if rest and rest[0] == ?/
+ return RelativeLocationPath(rest, n)
+ end
+ end
+ #puts "BEFORE WITH '#{rest}'"
+ rest = LocationPath(rest, n) if rest =~ /^[\/\.\@\[\w_*]/
+ parsed.concat(n)
+ return rest
+ end
+
+ #| FilterExpr Predicate
+ #| PrimaryExpr
+ def FilterExpr path, parsed
+ #puts "FILTER >>> #{path}"
+ n = []
+ path = PrimaryExpr( path, n )
+ #puts "FILTER <<< #{path}"
+ path = Predicate(path, n) if path and path[0] == ?[
+ #puts "FILTER <<< #{path}"
+ parsed.concat(n)
+ path
+ end
+
+ #| VARIABLE_REFERENCE
+ #| '(' expr ')'
+ #| LITERAL
+ #| NUMBER
+ #| FunctionCall
+ VARIABLE_REFERENCE = /^\$(#{NAME_STR})/u
+ NUMBER = /^(\d*\.?\d+)/
+ NT = /^comment|text|processing-instruction|node$/
+ def PrimaryExpr path, parsed
+ arry = []
+ case path
+ when VARIABLE_REFERENCE
+ varname = $1
+ path = $'
+ parsed << :variable
+ parsed << varname
+ #arry << @variables[ varname ]
+ when /^(\w[-\w]*)(?:\()/
+ #puts "PrimaryExpr :: Function >>> #$1 -- '#$''"
+ fname = $1
+ tmp = $'
+ #puts "#{fname} =~ #{NT.inspect}"
+ return path if fname =~ NT
+ path = tmp
+ parsed << :function
+ parsed << fname
+ path = FunctionCall(path, parsed)
+ when LITERAL, NUMBER
+ #puts "LITERAL or NUMBER: #$1"
+ varname = $1.nil? ? $2 : $1
+ path = $'
+ parsed << :literal
+ parsed << varname
+ when /^\(/ #/
+ path, contents = get_group(path)
+ contents = contents[1..-2]
+ n = []
+ OrExpr( contents, n )
+ parsed.concat(n)
+ end
+ path
+ end
+
+ #| FUNCTION_NAME '(' ( expr ( ',' expr )* )? ')'
+ def FunctionCall rest, parsed
+ path, arguments = parse_args(rest)
+ argset = []
+ for argument in arguments
+ args = []
+ OrExpr( argument, args )
+ argset << args
+ end
+ parsed << argset
+ path
+ end
+
+ # get_group( '[foo]bar' ) -> ['bar', '[foo]']
+ def get_group string
+ ind = 0
+ depth = 0
+ st = string[0,1]
+ en = (st == "(" ? ")" : "]")
+ begin
+ case string[ind,1]
+ when st
+ depth += 1
+ when en
+ depth -= 1
+ end
+ ind += 1
+ end while depth > 0 and ind < string.length
+ return nil unless depth==0
+ [string[ind..-1], string[0..ind-1]]
+ end
+
+ def parse_args( string )
+ arguments = []
+ ind = 0
+ inquot = false
+ inapos = false
+ depth = 1
+ begin
+ case string[ind]
+ when ?"
+ inquot = !inquot unless inapos
+ when ?'
+ inapos = !inapos unless inquot
+ else
+ unless inquot or inapos
+ case string[ind]
+ when ?(
+ depth += 1
+ if depth == 1
+ string = string[1..-1]
+ ind -= 1
+ end
+ when ?)
+ depth -= 1
+ if depth == 0
+ s = string[0,ind].strip
+ arguments << s unless s == ""
+ string = string[ind+1..-1]
+ end
+ when ?,
+ if depth == 1
+ s = string[0,ind].strip
+ arguments << s unless s == ""
+ string = string[ind+1..-1]
+ ind = -1
+ end
+ end
+ end
+ end
+ ind += 1
+ end while depth > 0 and ind < string.length
+ return nil unless depth==0
+ [string,arguments]
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/quickpath.rb b/ruby_1_8_5/lib/rexml/quickpath.rb
new file mode 100644
index 0000000000..c099db8579
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/quickpath.rb
@@ -0,0 +1,266 @@
+require 'rexml/functions'
+require 'rexml/xmltokens'
+
+module REXML
+ class QuickPath
+ include Functions
+ include XMLTokens
+
+ EMPTY_HASH = {}
+
+ def QuickPath::first element, path, namespaces=EMPTY_HASH
+ match(element, path, namespaces)[0]
+ end
+
+ def QuickPath::each element, path, namespaces=EMPTY_HASH, &block
+ path = "*" unless path
+ match(element, path, namespaces).each( &block )
+ end
+
+ def QuickPath::match element, path, namespaces=EMPTY_HASH
+ raise "nil is not a valid xpath" unless path
+ results = nil
+ Functions::namespace_context = namespaces
+ case path
+ when /^\/([^\/]|$)/u
+ # match on root
+ path = path[1..-1]
+ return [element.root.parent] if path == ''
+ results = filter([element.root], path)
+ when /^[-\w]*::/u
+ results = filter([element], path)
+ when /^\*/u
+ results = filter(element.to_a, path)
+ when /^[\[!\w:]/u
+ # match on child
+ matches = []
+ children = element.to_a
+ results = filter(children, path)
+ else
+ results = filter([element], path)
+ end
+ return results
+ end
+
+ # Given an array of nodes it filters the array based on the path. The
+ # result is that when this method returns, the array will contain elements
+ # which match the path
+ def QuickPath::filter elements, path
+ return elements if path.nil? or path == '' or elements.size == 0
+ case path
+ when /^\/\//u # Descendant
+ return axe( elements, "descendant-or-self", $' )
+ when /^\/?\b(\w[-\w]*)\b::/u # Axe
+ axe_name = $1
+ rest = $'
+ return axe( elements, $1, $' )
+ when /^\/(?=\b([:!\w][-\.\w]*:)?[-!\*\.\w]*\b([^:(]|$)|\*)/u # Child
+ rest = $'
+ results = []
+ elements.each do |element|
+ results |= filter( element.to_a, rest )
+ end
+ return results
+ when /^\/?(\w[-\w]*)\(/u # / Function
+ return function( elements, $1, $' )
+ when Namespace::NAMESPLIT # Element name
+ name = $2
+ ns = $1
+ rest = $'
+ elements.delete_if do |element|
+ !(element.kind_of? Element and
+ (element.expanded_name == name or
+ (element.name == name and
+ element.namespace == Functions.namespace_context[ns])))
+ end
+ return filter( elements, rest )
+ when /^\/\[/u
+ matches = []
+ elements.each do |element|
+ matches |= predicate( element.to_a, path[1..-1] ) if element.kind_of? Element
+ end
+ return matches
+ when /^\[/u # Predicate
+ return predicate( elements, path )
+ when /^\/?\.\.\./u # Ancestor
+ return axe( elements, "ancestor", $' )
+ when /^\/?\.\./u # Parent
+ return filter( elements.collect{|e|e.parent}, $' )
+ when /^\/?\./u # Self
+ return filter( elements, $' )
+ when /^\*/u # Any
+ results = []
+ elements.each do |element|
+ results |= filter( [element], $' ) if element.kind_of? Element
+ #if element.kind_of? Element
+ # children = element.to_a
+ # children.delete_if { |child| !child.kind_of?(Element) }
+ # results |= filter( children, $' )
+ #end
+ end
+ return results
+ end
+ return []
+ end
+
+ def QuickPath::axe( elements, axe_name, rest )
+ matches = []
+ matches = filter( elements.dup, rest ) if axe_name =~ /-or-self$/u
+ case axe_name
+ when /^descendant/u
+ elements.each do |element|
+ matches |= filter( element.to_a, "descendant-or-self::#{rest}" ) if element.kind_of? Element
+ end
+ when /^ancestor/u
+ elements.each do |element|
+ while element.parent
+ matches << element.parent
+ element = element.parent
+ end
+ end
+ matches = filter( matches, rest )
+ when "self"
+ matches = filter( elements, rest )
+ when "child"
+ elements.each do |element|
+ matches |= filter( element.to_a, rest ) if element.kind_of? Element
+ end
+ when "attribute"
+ elements.each do |element|
+ matches << element.attributes[ rest ] if element.kind_of? Element
+ end
+ when "parent"
+ matches = filter(elements.collect{|element| element.parent}.uniq, rest)
+ when "following-sibling"
+ matches = filter(elements.collect{|element| element.next_sibling}.uniq,
+ rest)
+ when "previous-sibling"
+ matches = filter(elements.collect{|element|
+ element.previous_sibling}.uniq, rest )
+ end
+ return matches.uniq
+ end
+
+ # A predicate filters a node-set with respect to an axis to produce a
+ # new node-set. For each node in the node-set to be filtered, the
+ # PredicateExpr is evaluated with that node as the context node, with
+ # the number of nodes in the node-set as the context size, and with the
+ # proximity position of the node in the node-set with respect to the
+ # axis as the context position; if PredicateExpr evaluates to true for
+ # that node, the node is included in the new node-set; otherwise, it is
+ # not included.
+ #
+ # A PredicateExpr is evaluated by evaluating the Expr and converting
+ # the result to a boolean. If the result is a number, the result will
+ # be converted to true if the number is equal to the context position
+ # and will be converted to false otherwise; if the result is not a
+ # number, then the result will be converted as if by a call to the
+ # boolean function. Thus a location path para[3] is equivalent to
+ # para[position()=3].
+ def QuickPath::predicate( elements, path )
+ ind = 1
+ bcount = 1
+ while bcount > 0
+ bcount += 1 if path[ind] == ?[
+ bcount -= 1 if path[ind] == ?]
+ ind += 1
+ end
+ ind -= 1
+ predicate = path[1..ind-1]
+ rest = path[ind+1..-1]
+
+ # have to change 'a [=<>] b [=<>] c' into 'a [=<>] b and b [=<>] c'
+ predicate.gsub!( /([^\s(and)(or)<>=]+)\s*([<>=])\s*([^\s(and)(or)<>=]+)\s*([<>=])\s*([^\s(and)(or)<>=]+)/u ) {
+ "#$1 #$2 #$3 and #$3 #$4 #$5"
+ }
+ # Let's do some Ruby trickery to avoid some work:
+ predicate.gsub!( /&/u, "&&" )
+ predicate.gsub!( /=/u, "==" )
+ predicate.gsub!( /@(\w[-\w.]*)/u ) {
+ "attribute(\"#$1\")"
+ }
+ predicate.gsub!( /\bmod\b/u, "%" )
+ predicate.gsub!( /\b(\w[-\w.]*\()/u ) {
+ fname = $1
+ fname.gsub( /-/u, "_" )
+ }
+
+ Functions.pair = [ 0, elements.size ]
+ results = []
+ elements.each do |element|
+ Functions.pair[0] += 1
+ Functions.node = element
+ res = eval( predicate )
+ case res
+ when true
+ results << element
+ when Fixnum
+ results << element if Functions.pair[0] == res
+ when String
+ results << element
+ end
+ end
+ return filter( results, rest )
+ end
+
+ def QuickPath::attribute( name )
+ return Functions.node.attributes[name] if Functions.node.kind_of? Element
+ end
+
+ def QuickPath::name()
+ return Functions.node.name if Functions.node.kind_of? Element
+ end
+
+ def QuickPath::method_missing( id, *args )
+ begin
+ Functions.send( id.id2name, *args )
+ rescue Exception
+ raise "METHOD: #{id.id2name}(#{args.join ', '})\n#{$!.message}"
+ end
+ end
+
+ def QuickPath::function( elements, fname, rest )
+ args = parse_args( elements, rest )
+ Functions.pair = [0, elements.size]
+ results = []
+ elements.each do |element|
+ Functions.pair[0] += 1
+ Functions.node = element
+ res = Functions.send( fname, *args )
+ case res
+ when true
+ results << element
+ when Fixnum
+ results << element if Functions.pair[0] == res
+ end
+ end
+ return results
+ end
+
+ def QuickPath::parse_args( element, string )
+ # /.*?(?:\)|,)/
+ arguments = []
+ buffer = ""
+ while string and string != ""
+ c = string[0]
+ string.sub!(/^./u, "")
+ case c
+ when ?,
+ # if depth = 1, then we start a new argument
+ arguments << evaluate( buffer )
+ #arguments << evaluate( string[0..count] )
+ when ?(
+ # start a new method call
+ function( element, buffer, string )
+ buffer = ""
+ when ?)
+ # close the method call and return arguments
+ return arguments
+ else
+ buffer << c
+ end
+ end
+ ""
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/rexml.rb b/ruby_1_8_5/lib/rexml/rexml.rb
new file mode 100644
index 0000000000..ca154443b5
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/rexml.rb
@@ -0,0 +1,29 @@
+# REXML is an XML toolkit for Ruby[http://www.ruby-lang.org], in Ruby.
+#
+# REXML is a _pure_ Ruby, XML 1.0 conforming,
+# non-validating[http://www.w3.org/TR/2004/REC-xml-20040204/#sec-conformance]
+# toolkit with an intuitive API. REXML passes 100% of the non-validating Oasis
+# tests[http://www.oasis-open.org/committees/xml-conformance/xml-test-suite.shtml],
+# and provides tree, stream, SAX2, pull, and lightweight APIs. REXML also
+# includes a full XPath[http://www.w3c.org/tr/xpath] 1.0 implementation. Since
+# Ruby 1.8, REXML is included in the standard Ruby distribution.
+#
+# Main page:: http://www.germane-software.com/software/rexml
+# Author:: Sean Russell <serATgermaneHYPHENsoftwareDOTcom>
+# Version:: 3.1.4
+# Date:: 2006/104
+#
+# This API documentation can be downloaded from the REXML home page, or can
+# be accessed online[http://www.germane-software.com/software/rexml_doc]
+#
+# A tutorial is available in the REXML distribution in docs/tutorial.html,
+# or can be accessed
+# online[http://www.germane-software.com/software/rexml/docs/tutorial.html]
+module REXML
+ COPYRIGHT = "Copyright © 2001-2006 Sean Russell <ser@germane-software.com>"
+ DATE = "2006/104"
+ VERSION = "3.1.4"
+
+ Copyright = COPYRIGHT
+ Version = VERSION
+end
diff --git a/ruby_1_8_5/lib/rexml/sax2listener.rb b/ruby_1_8_5/lib/rexml/sax2listener.rb
new file mode 100644
index 0000000000..9a992917e6
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/sax2listener.rb
@@ -0,0 +1,97 @@
+module REXML
+ # A template for stream parser listeners.
+ # Note that the declarations (attlistdecl, elementdecl, etc) are trivially
+ # processed; REXML doesn't yet handle doctype entity declarations, so you
+ # have to parse them out yourself.
+ # === Missing methods from SAX2
+ # ignorable_whitespace
+ # === Methods extending SAX2
+ # +WARNING+
+ # These methods are certainly going to change, until DTDs are fully
+ # supported. Be aware of this.
+ # start_document
+ # end_document
+ # doctype
+ # elementdecl
+ # attlistdecl
+ # entitydecl
+ # notationdecl
+ # cdata
+ # xmldecl
+ # comment
+ module SAX2Listener
+ def start_document
+ end
+ def end_document
+ end
+ def start_prefix_mapping prefix, uri
+ end
+ def end_prefix_mapping prefix
+ end
+ def start_element uri, localname, qname, attributes
+ end
+ def end_element uri, localname, qname
+ end
+ def characters text
+ end
+ def processing_instruction target, data
+ end
+ # Handles a doctype declaration. Any attributes of the doctype which are
+ # not supplied will be nil. # EG, <!DOCTYPE me PUBLIC "foo" "bar">
+ # @p name the name of the doctype; EG, "me"
+ # @p pub_sys "PUBLIC", "SYSTEM", or nil. EG, "PUBLIC"
+ # @p long_name the supplied long name, or nil. EG, "foo"
+ # @p uri the uri of the doctype, or nil. EG, "bar"
+ def doctype name, pub_sys, long_name, uri
+ end
+ # If a doctype includes an ATTLIST declaration, it will cause this
+ # method to be called. The content is the declaration itself, unparsed.
+ # EG, <!ATTLIST el attr CDATA #REQUIRED> will come to this method as "el
+ # attr CDATA #REQUIRED". This is the same for all of the .*decl
+ # methods.
+ def attlistdecl(element, pairs, contents)
+ end
+ # <!ELEMENT ...>
+ def elementdecl content
+ end
+ # <!ENTITY ...>
+ # The argument passed to this method is an array of the entity
+ # declaration. It can be in a number of formats, but in general it
+ # returns (example, result):
+ # <!ENTITY % YN '"Yes"'>
+ # ["%", "YN", "'\"Yes\"'", "\""]
+ # <!ENTITY % YN 'Yes'>
+ # ["%", "YN", "'Yes'", "s"]
+ # <!ENTITY WhatHeSaid "He said %YN;">
+ # ["WhatHeSaid", "\"He said %YN;\"", "YN"]
+ # <!ENTITY open-hatch SYSTEM "http://www.textuality.com/boilerplate/OpenHatch.xml">
+ # ["open-hatch", "SYSTEM", "\"http://www.textuality.com/boilerplate/OpenHatch.xml\""]
+ # <!ENTITY open-hatch PUBLIC "-//Textuality//TEXT Standard open-hatch boilerplate//EN" "http://www.textuality.com/boilerplate/OpenHatch.xml">
+ # ["open-hatch", "PUBLIC", "\"-//Textuality//TEXT Standard open-hatch boilerplate//EN\"", "\"http://www.textuality.com/boilerplate/OpenHatch.xml\""]
+ # <!ENTITY hatch-pic SYSTEM "../grafix/OpenHatch.gif" NDATA gif>
+ # ["hatch-pic", "SYSTEM", "\"../grafix/OpenHatch.gif\"", "\n\t\t\t\t\t\t\tNDATA gif", "gif"]
+ def entitydecl content
+ end
+ # <!NOTATION ...>
+ def notationdecl content
+ end
+ # Called when <![CDATA[ ... ]]> is encountered in a document.
+ # @p content "..."
+ def cdata content
+ end
+ # Called when an XML PI is encountered in the document.
+ # EG: <?xml version="1.0" encoding="utf"?>
+ # @p version the version attribute value. EG, "1.0"
+ # @p encoding the encoding attribute value, or nil. EG, "utf"
+ # @p standalone the standalone attribute value, or nil. EG, nil
+ # @p spaced the declaration is followed by a line break
+ def xmldecl version, encoding, standalone
+ end
+ # Called when a comment is encountered.
+ # @p comment The content of the comment
+ def comment comment
+ end
+ def progress position
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/source.rb b/ruby_1_8_5/lib/rexml/source.rb
new file mode 100644
index 0000000000..cacab221db
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/source.rb
@@ -0,0 +1,233 @@
+require 'rexml/encoding'
+
+module REXML
+ # Generates Source-s. USE THIS CLASS.
+ class SourceFactory
+ # Generates a Source object
+ # @param arg Either a String, or an IO
+ # @return a Source, or nil if a bad argument was given
+ def SourceFactory::create_from arg#, slurp=true
+ if arg.kind_of? String
+ Source.new(arg)
+ elsif arg.respond_to? :read and
+ arg.respond_to? :readline and
+ arg.respond_to? :nil? and
+ arg.respond_to? :eof?
+ IOSource.new(arg)
+ elsif arg.kind_of? Source
+ arg
+ else
+ raise "#{source.class} is not a valid input stream. It must walk \n"+
+ "like either a String, IO, or Source."
+ end
+ end
+ end
+
+ # A Source can be searched for patterns, and wraps buffers and other
+ # objects and provides consumption of text
+ class Source
+ include Encoding
+ # The current buffer (what we're going to read next)
+ attr_reader :buffer
+ # The line number of the last consumed text
+ attr_reader :line
+ attr_reader :encoding
+
+ # Constructor
+ # @param arg must be a String, and should be a valid XML document
+ def initialize(arg)
+ @orig = @buffer = arg
+ self.encoding = check_encoding( @buffer )
+ @line = 0
+ end
+
+ # Inherited from Encoding
+ # Overridden to support optimized en/decoding
+ def encoding=(enc)
+ super
+ @line_break = encode( '>' )
+ if enc != UTF_8
+ @buffer = decode(@buffer)
+ @to_utf = true
+ else
+ @to_utf = false
+ end
+ end
+
+ # Scans the source for a given pattern. Note, that this is not your
+ # usual scan() method. For one thing, the pattern argument has some
+ # requirements; for another, the source can be consumed. You can easily
+ # confuse this method. Originally, the patterns were easier
+ # to construct and this method more robust, because this method
+ # generated search regexes on the fly; however, this was
+ # computationally expensive and slowed down the entire REXML package
+ # considerably, since this is by far the most commonly called method.
+ # @param pattern must be a Regexp, and must be in the form of
+ # /^\s*(#{your pattern, with no groups})(.*)/. The first group
+ # will be returned; the second group is used if the consume flag is
+ # set.
+ # @param consume if true, the pattern returned will be consumed, leaving
+ # everything after it in the Source.
+ # @return the pattern, if found, or nil if the Source is empty or the
+ # pattern is not found.
+ def scan(pattern, cons=false)
+ return nil if @buffer.nil?
+ rv = @buffer.scan(pattern)
+ @buffer = $' if cons and rv.size>0
+ rv
+ end
+
+ def read
+ end
+
+ def consume( pattern )
+ @buffer = $' if pattern.match( @buffer )
+ end
+
+ def match_to( char, pattern )
+ return pattern.match(@buffer)
+ end
+
+ def match_to_consume( char, pattern )
+ md = pattern.match(@buffer)
+ @buffer = $'
+ return md
+ end
+
+ def match(pattern, cons=false)
+ md = pattern.match(@buffer)
+ @buffer = $' if cons and md
+ return md
+ end
+
+ # @return true if the Source is exhausted
+ def empty?
+ @buffer == ""
+ end
+
+ def position
+ @orig.index( @buffer )
+ end
+
+ # @return the current line in the source
+ def current_line
+ lines = @orig.split
+ res = lines.grep @buffer[0..30]
+ res = res[-1] if res.kind_of? Array
+ lines.index( res ) if res
+ end
+ end
+
+ # A Source that wraps an IO. See the Source class for method
+ # documentation
+ class IOSource < Source
+ #attr_reader :block_size
+
+ # block_size has been deprecated
+ def initialize(arg, block_size=500)
+ @er_source = @source = arg
+ @to_utf = false
+ # Determining the encoding is a deceptively difficult issue to resolve.
+ # First, we check the first two bytes for UTF-16. Then we
+ # assume that the encoding is at least ASCII enough for the '>', and
+ # we read until we get one of those. This gives us the XML declaration,
+ # if there is one. If there isn't one, the file MUST be UTF-8, as per
+ # the XML spec. If there is one, we can determine the encoding from
+ # it.
+ str = @source.read( 2 )
+ if (str[0] == 254 && str[1] == 255) || (str[0] == 255 && str[1] == 254)
+ @encoding = check_encoding( str )
+ @line_break = encode( '>' )
+ else
+ @line_break = '>'
+ end
+ super str+@source.readline( @line_break )
+ end
+
+ def scan(pattern, cons=false)
+ rv = super
+ # You'll notice that this next section is very similar to the same
+ # section in match(), but just a liiittle different. This is
+ # because it is a touch faster to do it this way with scan()
+ # than the way match() does it; enough faster to warrent duplicating
+ # some code
+ if rv.size == 0
+ until @buffer =~ pattern or @source.nil?
+ begin
+ # READLINE OPT
+ #str = @source.read(@block_size)
+ str = @source.readline(@line_break)
+ str = decode(str) if @to_utf and str
+ @buffer << str
+ rescue
+ @source = nil
+ end
+ end
+ rv = super
+ end
+ rv.taint
+ rv
+ end
+
+ def read
+ begin
+ str = @source.readline(@line_break)
+ str = decode(str) if @to_utf and str
+ @buffer << str
+ rescue Exception, NameError
+ @source = nil
+ end
+ end
+
+ def consume( pattern )
+ match( pattern, true )
+ end
+
+ def match( pattern, cons=false )
+ rv = pattern.match(@buffer)
+ @buffer = $' if cons and rv
+ while !rv and @source
+ begin
+ str = @source.readline(@line_break)
+ str = decode(str) if @to_utf and str
+ @buffer << str
+ rv = pattern.match(@buffer)
+ @buffer = $' if cons and rv
+ rescue
+ @source = nil
+ end
+ end
+ rv.taint
+ rv
+ end
+
+ def empty?
+ super and ( @source.nil? || @source.eof? )
+ end
+
+ def position
+ @er_source.stat.pipe? ? 0 : @er_source.pos
+ end
+
+ # @return the current line in the source
+ def current_line
+ begin
+ pos = @er_source.pos # The byte position in the source
+ lineno = @er_source.lineno # The XML < position in the source
+ @er_source.rewind
+ line = 0 # The \r\n position in the source
+ begin
+ while @er_source.pos < pos
+ @er_source.readline
+ line += 1
+ end
+ rescue
+ end
+ rescue IOError
+ pos = -1
+ line = -1
+ end
+ [pos, lineno, line]
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/streamlistener.rb b/ruby_1_8_5/lib/rexml/streamlistener.rb
new file mode 100644
index 0000000000..6f401125b5
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/streamlistener.rb
@@ -0,0 +1,92 @@
+module REXML
+ # A template for stream parser listeners.
+ # Note that the declarations (attlistdecl, elementdecl, etc) are trivially
+ # processed; REXML doesn't yet handle doctype entity declarations, so you
+ # have to parse them out yourself.
+ module StreamListener
+ # Called when a tag is encountered.
+ # @p name the tag name
+ # @p attrs an array of arrays of attribute/value pairs, suitable for
+ # use with assoc or rassoc. IE, <tag attr1="value1" attr2="value2">
+ # will result in
+ # tag_start( "tag", # [["attr1","value1"],["attr2","value2"]])
+ def tag_start name, attrs
+ end
+ # Called when the end tag is reached. In the case of <tag/>, tag_end
+ # will be called immidiately after tag_start
+ # @p the name of the tag
+ def tag_end name
+ end
+ # Called when text is encountered in the document
+ # @p text the text content.
+ def text text
+ end
+ # Called when an instruction is encountered. EG: <?xsl sheet='foo'?>
+ # @p name the instruction name; in the example, "xsl"
+ # @p instruction the rest of the instruction. In the example,
+ # "sheet='foo'"
+ def instruction name, instruction
+ end
+ # Called when a comment is encountered.
+ # @p comment The content of the comment
+ def comment comment
+ end
+ # Handles a doctype declaration. Any attributes of the doctype which are
+ # not supplied will be nil. # EG, <!DOCTYPE me PUBLIC "foo" "bar">
+ # @p name the name of the doctype; EG, "me"
+ # @p pub_sys "PUBLIC", "SYSTEM", or nil. EG, "PUBLIC"
+ # @p long_name the supplied long name, or nil. EG, "foo"
+ # @p uri the uri of the doctype, or nil. EG, "bar"
+ def doctype name, pub_sys, long_name, uri
+ end
+ # Called when the doctype is done
+ def doctype_end
+ end
+ # If a doctype includes an ATTLIST declaration, it will cause this
+ # method to be called. The content is the declaration itself, unparsed.
+ # EG, <!ATTLIST el attr CDATA #REQUIRED> will come to this method as "el
+ # attr CDATA #REQUIRED". This is the same for all of the .*decl
+ # methods.
+ def attlistdecl element_name, attributes, raw_content
+ end
+ # <!ELEMENT ...>
+ def elementdecl content
+ end
+ # <!ENTITY ...>
+ # The argument passed to this method is an array of the entity
+ # declaration. It can be in a number of formats, but in general it
+ # returns (example, result):
+ # <!ENTITY % YN '"Yes"'>
+ # ["%", "YN", "'\"Yes\"'", "\""]
+ # <!ENTITY % YN 'Yes'>
+ # ["%", "YN", "'Yes'", "s"]
+ # <!ENTITY WhatHeSaid "He said %YN;">
+ # ["WhatHeSaid", "\"He said %YN;\"", "YN"]
+ # <!ENTITY open-hatch SYSTEM "http://www.textuality.com/boilerplate/OpenHatch.xml">
+ # ["open-hatch", "SYSTEM", "\"http://www.textuality.com/boilerplate/OpenHatch.xml\""]
+ # <!ENTITY open-hatch PUBLIC "-//Textuality//TEXT Standard open-hatch boilerplate//EN" "http://www.textuality.com/boilerplate/OpenHatch.xml">
+ # ["open-hatch", "PUBLIC", "\"-//Textuality//TEXT Standard open-hatch boilerplate//EN\"", "\"http://www.textuality.com/boilerplate/OpenHatch.xml\""]
+ # <!ENTITY hatch-pic SYSTEM "../grafix/OpenHatch.gif" NDATA gif>
+ # ["hatch-pic", "SYSTEM", "\"../grafix/OpenHatch.gif\"", "\n\t\t\t\t\t\t\tNDATA gif", "gif"]
+ def entitydecl content
+ end
+ # <!NOTATION ...>
+ def notationdecl content
+ end
+ # Called when %foo; is encountered in a doctype declaration.
+ # @p content "foo"
+ def entity content
+ end
+ # Called when <![CDATA[ ... ]]> is encountered in a document.
+ # @p content "..."
+ def cdata content
+ end
+ # Called when an XML PI is encountered in the document.
+ # EG: <?xml version="1.0" encoding="utf"?>
+ # @p version the version attribute value. EG, "1.0"
+ # @p encoding the encoding attribute value, or nil. EG, "utf"
+ # @p standalone the standalone attribute value, or nil. EG, nil
+ def xmldecl version, encoding, standalone
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/syncenumerator.rb b/ruby_1_8_5/lib/rexml/syncenumerator.rb
new file mode 100644
index 0000000000..955e006cb2
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/syncenumerator.rb
@@ -0,0 +1,33 @@
+module REXML
+ class SyncEnumerator
+ include Enumerable
+
+ # Creates a new SyncEnumerator which enumerates rows of given
+ # Enumerable objects.
+ def initialize(*enums)
+ @gens = enums
+ @biggest = @gens[0]
+ @gens.each {|x| @biggest = x if x.size > @biggest.size }
+ end
+
+ # Returns the number of enumerated Enumerable objects, i.e. the size
+ # of each row.
+ def size
+ @gens.size
+ end
+
+ # Returns the number of enumerated Enumerable objects, i.e. the size
+ # of each row.
+ def length
+ @gens.length
+ end
+
+ # Enumerates rows of the Enumerable objects.
+ def each
+ @biggest.zip( *@gens ) {|a|
+ yield(*a[1..-1])
+ }
+ self
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/text.rb b/ruby_1_8_5/lib/rexml/text.rb
new file mode 100644
index 0000000000..55bc9f50f8
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/text.rb
@@ -0,0 +1,342 @@
+require 'rexml/entity'
+require 'rexml/doctype'
+require 'rexml/child'
+require 'rexml/doctype'
+require 'rexml/parseexception'
+
+module REXML
+ # Represents text nodes in an XML document
+ class Text < Child
+ include Comparable
+ # The order in which the substitutions occur
+ SPECIALS = [ /&(?!#?[\w-]+;)/u, /</u, />/u, /"/u, /'/u, /\r/u ]
+ SUBSTITUTES = ['&amp;', '&lt;', '&gt;', '&quot;', '&apos;', '&#13;']
+ # Characters which are substituted in written strings
+ SLAICEPS = [ '<', '>', '"', "'", '&' ]
+ SETUTITSBUS = [ /&lt;/u, /&gt;/u, /&quot;/u, /&apos;/u, /&amp;/u ]
+
+ # If +raw+ is true, then REXML leaves the value alone
+ attr_accessor :raw
+
+ ILLEGAL = /(<|&(?!(#{Entity::NAME})|(#0*((?:\d+)|(?:x[a-fA-F0-9]+)));))/um
+ NUMERICENTITY = /&#0*((?:\d+)|(?:x[a-fA-F0-9]+));/
+
+ # Constructor
+ # +arg+ if a String, the content is set to the String. If a Text,
+ # the object is shallowly cloned.
+ #
+ # +respect_whitespace+ (boolean, false) if true, whitespace is
+ # respected
+ #
+ # +parent+ (nil) if this is a Parent object, the parent
+ # will be set to this.
+ #
+ # +raw+ (nil) This argument can be given three values.
+ # If true, then the value of used to construct this object is expected to
+ # contain no unescaped XML markup, and REXML will not change the text. If
+ # this value is false, the string may contain any characters, and REXML will
+ # escape any and all defined entities whose values are contained in the
+ # text. If this value is nil (the default), then the raw value of the
+ # parent will be used as the raw value for this node. If there is no raw
+ # value for the parent, and no value is supplied, the default is false.
+ # Use this field if you have entities defined for some text, and you don't
+ # want REXML to escape that text in output.
+ # Text.new( "<&", false, nil, false ) #-> "&lt;&amp;"
+ # Text.new( "<&", false, nil, true ) #-> Parse exception
+ # Text.new( "&lt;&amp;", false, nil, true ) #-> "&lt;&amp;"
+ # # Assume that the entity "s" is defined to be "sean"
+ # # and that the entity "r" is defined to be "russell"
+ # Text.new( "sean russell" ) #-> "&s; &r;"
+ # Text.new( "sean russell", false, nil, true ) #-> "sean russell"
+ #
+ # +entity_filter+ (nil) This can be an array of entities to match in the
+ # supplied text. This argument is only useful if +raw+ is set to false.
+ # Text.new( "sean russell", false, nil, false, ["s"] ) #-> "&s; russell"
+ # Text.new( "sean russell", false, nil, true, ["s"] ) #-> "sean russell"
+ # In the last example, the +entity_filter+ argument is ignored.
+ #
+ # +pattern+ INTERNAL USE ONLY
+ def initialize(arg, respect_whitespace=false, parent=nil, raw=nil,
+ entity_filter=nil, illegal=ILLEGAL )
+
+ @raw = false
+
+ if parent
+ super( parent )
+ @raw = parent.raw
+ else
+ @parent = nil
+ end
+
+ @raw = raw unless raw.nil?
+ @entity_filter = entity_filter
+ @normalized = @unnormalized = nil
+
+ if arg.kind_of? String
+ @string = arg.clone
+ @string.squeeze!(" \n\t") unless respect_whitespace
+ elsif arg.kind_of? Text
+ @string = arg.to_s
+ @raw = arg.raw
+ elsif
+ raise "Illegal argument of type #{arg.type} for Text constructor (#{arg})"
+ end
+
+ @string.gsub!( /\r\n?/, "\n" )
+
+ # check for illegal characters
+ if @raw
+ if @string =~ illegal
+ raise "Illegal character '#{$1}' in raw string \"#{@string}\""
+ end
+ end
+ end
+
+ def node_type
+ :text
+ end
+
+ def empty?
+ @string.size==0
+ end
+
+
+ def clone
+ return Text.new(self)
+ end
+
+
+ # Appends text to this text node. The text is appended in the +raw+ mode
+ # of this text node.
+ def <<( to_append )
+ @string << to_append.gsub( /\r\n?/, "\n" )
+ end
+
+
+ # +other+ a String or a Text
+ # +returns+ the result of (to_s <=> arg.to_s)
+ def <=>( other )
+ to_s() <=> other.to_s
+ end
+
+ REFERENCE = /#{Entity::REFERENCE}/
+ # Returns the string value of this text node. This string is always
+ # escaped, meaning that it is a valid XML text node string, and all
+ # entities that can be escaped, have been inserted. This method respects
+ # the entity filter set in the constructor.
+ #
+ # # Assume that the entity "s" is defined to be "sean", and that the
+ # # entity "r" is defined to be "russell"
+ # t = Text.new( "< & sean russell", false, nil, false, ['s'] )
+ # t.to_s #-> "&lt; &amp; &s; russell"
+ # t = Text.new( "< & &s; russell", false, nil, false )
+ # t.to_s #-> "&lt; &amp; &s; russell"
+ # u = Text.new( "sean russell", false, nil, true )
+ # u.to_s #-> "sean russell"
+ def to_s
+ return @string if @raw
+ return @normalized if @normalized
+
+ doctype = nil
+ if @parent
+ doc = @parent.document
+ doctype = doc.doctype if doc
+ end
+
+ @normalized = Text::normalize( @string, doctype, @entity_filter )
+ end
+
+ def inspect
+ @string.inspect
+ end
+
+ # Returns the string value of this text. This is the text without
+ # entities, as it might be used programmatically, or printed to the
+ # console. This ignores the 'raw' attribute setting, and any
+ # entity_filter.
+ #
+ # # Assume that the entity "s" is defined to be "sean", and that the
+ # # entity "r" is defined to be "russell"
+ # t = Text.new( "< & sean russell", false, nil, false, ['s'] )
+ # t.value #-> "< & sean russell"
+ # t = Text.new( "< & &s; russell", false, nil, false )
+ # t.value #-> "< & sean russell"
+ # u = Text.new( "sean russell", false, nil, true )
+ # u.value #-> "sean russell"
+ def value
+ @unnormalized if @unnormalized
+ doctype = nil
+ if @parent
+ doc = @parent.document
+ doctype = doc.doctype if doc
+ end
+ @unnormalized = Text::unnormalize( @string, doctype )
+ end
+
+ def wrap(string, width, addnewline=false)
+ # Recursivly wrap string at width.
+ return string if string.length <= width
+ place = string.rindex(' ', width) # Position in string with last ' ' before cutoff
+ if addnewline then
+ return "\n" + string[0,place] + "\n" + wrap(string[place+1..-1], width)
+ else
+ return string[0,place] + "\n" + wrap(string[place+1..-1], width)
+ end
+ end
+
+ # Sets the contents of this text node. This expects the text to be
+ # unnormalized. It returns self.
+ #
+ # e = Element.new( "a" )
+ # e.add_text( "foo" ) # <a>foo</a>
+ # e[0].value = "bar" # <a>bar</a>
+ # e[0].value = "<a>" # <a>&lt;a&gt;</a>
+ def value=( val )
+ @string = val.gsub( /\r\n?/, "\n" )
+ @unnormalized = nil
+ @normalized = nil
+ @raw = false
+ end
+
+ def indent_text(string, level=1, style="\t", indentfirstline=true)
+ return string if level < 0
+ new_string = ''
+ string.each { |line|
+ indent_string = style * level
+ new_line = (indent_string + line).sub(/[\s]+$/,'')
+ new_string << new_line
+ }
+ new_string.strip! unless indentfirstline
+ return new_string
+ end
+
+ def write( writer, indent=-1, transitive=false, ie_hack=false )
+ s = to_s()
+ if not (@parent and @parent.whitespace) then
+ s = wrap(s, 60, false) if @parent and @parent.context[:wordwrap] == :all
+ if @parent and not @parent.context[:indentstyle].nil? and indent > 0 and s.count("\n") > 0
+ s = indent_text(s, indent, @parent.context[:indentstyle], false)
+ end
+ s.squeeze!(" \n\t") if @parent and !@parent.whitespace
+ end
+ writer << s
+ end
+
+ # FIXME
+ # This probably won't work properly
+ def xpath
+ path = @parent.xpath
+ path += "/text()"
+ return path
+ end
+
+ # Writes out text, substituting special characters beforehand.
+ # +out+ A String, IO, or any other object supporting <<( String )
+ # +input+ the text to substitute and the write out
+ #
+ # z=utf8.unpack("U*")
+ # ascOut=""
+ # z.each{|r|
+ # if r < 0x100
+ # ascOut.concat(r.chr)
+ # else
+ # ascOut.concat(sprintf("&#x%x;", r))
+ # end
+ # }
+ # puts ascOut
+ def write_with_substitution out, input
+ copy = input.clone
+ # Doing it like this rather than in a loop improves the speed
+ copy.gsub!( SPECIALS[0], SUBSTITUTES[0] )
+ copy.gsub!( SPECIALS[1], SUBSTITUTES[1] )
+ copy.gsub!( SPECIALS[2], SUBSTITUTES[2] )
+ copy.gsub!( SPECIALS[3], SUBSTITUTES[3] )
+ copy.gsub!( SPECIALS[4], SUBSTITUTES[4] )
+ copy.gsub!( SPECIALS[5], SUBSTITUTES[5] )
+ out << copy
+ end
+
+ # Reads text, substituting entities
+ def Text::read_with_substitution( input, illegal=nil )
+ copy = input.clone
+
+ if copy =~ illegal
+ raise ParseException.new( "malformed text: Illegal character #$& in \"#{copy}\"" )
+ end if illegal
+
+ copy.gsub!( /\r\n?/, "\n" )
+ if copy.include? ?&
+ copy.gsub!( SETUTITSBUS[0], SLAICEPS[0] )
+ copy.gsub!( SETUTITSBUS[1], SLAICEPS[1] )
+ copy.gsub!( SETUTITSBUS[2], SLAICEPS[2] )
+ copy.gsub!( SETUTITSBUS[3], SLAICEPS[3] )
+ copy.gsub!( SETUTITSBUS[4], SLAICEPS[4] )
+ copy.gsub!( /&#0*((?:\d+)|(?:x[a-f0-9]+));/ ) {|m|
+ m=$1
+ #m='0' if m==''
+ m = "0#{m}" if m[0] == ?x
+ [Integer(m)].pack('U*')
+ }
+ end
+ copy
+ end
+
+ EREFERENCE = /&(?!#{Entity::NAME};)/
+ # Escapes all possible entities
+ def Text::normalize( input, doctype=nil, entity_filter=nil )
+ copy = input
+ # Doing it like this rather than in a loop improves the speed
+ if doctype
+ # Replace all ampersands that aren't part of an entity
+ copy = copy.gsub( EREFERENCE, '&amp;' )
+ doctype.entities.each_value do |entity|
+ copy = copy.gsub( entity.value,
+ "&#{entity.name};" ) if entity.value and
+ not( entity_filter and entity_filter.include?(entity) )
+ end
+ else
+ # Replace all ampersands that aren't part of an entity
+ copy = copy.gsub( EREFERENCE, '&amp;' )
+ DocType::DEFAULT_ENTITIES.each_value do |entity|
+ copy = copy.gsub(entity.value, "&#{entity.name};" )
+ end
+ end
+ copy
+ end
+
+ # Unescapes all possible entities
+ def Text::unnormalize( string, doctype=nil, filter=nil, illegal=nil )
+ rv = string.clone
+ rv.gsub!( /\r\n?/, "\n" )
+ matches = rv.scan( REFERENCE )
+ return rv if matches.size == 0
+ rv.gsub!( NUMERICENTITY ) {|m|
+ m=$1
+ m = "0#{m}" if m[0] == ?x
+ [Integer(m)].pack('U*')
+ }
+ matches.collect!{|x|x[0]}.compact!
+ if matches.size > 0
+ if doctype
+ matches.each do |entity_reference|
+ unless filter and filter.include?(entity_reference)
+ entity_value = doctype.entity( entity_reference )
+ re = /&#{entity_reference};/
+ rv.gsub!( re, entity_value ) if entity_value
+ end
+ end
+ else
+ matches.each do |entity_reference|
+ unless filter and filter.include?(entity_reference)
+ entity_value = DocType::DEFAULT_ENTITIES[ entity_reference ]
+ re = /&#{entity_reference};/
+ rv.gsub!( re, entity_value.value ) if entity_value
+ end
+ end
+ end
+ rv.gsub!( /&amp;/, '&' )
+ end
+ rv
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/validation/relaxng.rb b/ruby_1_8_5/lib/rexml/validation/relaxng.rb
new file mode 100644
index 0000000000..969f51bc95
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/validation/relaxng.rb
@@ -0,0 +1,559 @@
+require "rexml/validation/validation"
+require "rexml/parsers/baseparser"
+
+module REXML
+ module Validation
+ # Implemented:
+ # * empty
+ # * element
+ # * attribute
+ # * text
+ # * optional
+ # * choice
+ # * oneOrMore
+ # * zeroOrMore
+ # * group
+ # * value
+ # * interleave
+ # * mixed
+ # * ref
+ # * grammar
+ # * start
+ # * define
+ #
+ # Not implemented:
+ # * data
+ # * param
+ # * include
+ # * externalRef
+ # * notAllowed
+ # * anyName
+ # * nsName
+ # * except
+ # * name
+ class RelaxNG
+ include Validator
+
+ INFINITY = 1.0 / 0.0
+ EMPTY = Event.new( nil )
+ TEXT = [:start_element, "text"]
+ attr_accessor :current
+ attr_accessor :count
+ attr_reader :references
+
+ # FIXME: Namespaces
+ def initialize source
+ parser = REXML::Parsers::BaseParser.new( source )
+
+ @count = 0
+ @references = {}
+ @root = @current = Sequence.new(self)
+ @root.previous = true
+ states = [ @current ]
+ begin
+ event = parser.pull
+ case event[0]
+ when :start_element
+ case event[1]
+ when "empty"
+ when "element", "attribute", "text", "value"
+ states[-1] << event
+ when "optional"
+ states << Optional.new( self )
+ states[-2] << states[-1]
+ when "choice"
+ states << Choice.new( self )
+ states[-2] << states[-1]
+ when "oneOrMore"
+ states << OneOrMore.new( self )
+ states[-2] << states[-1]
+ when "zeroOrMore"
+ states << ZeroOrMore.new( self )
+ states[-2] << states[-1]
+ when "group"
+ states << Sequence.new( self )
+ states[-2] << states[-1]
+ when "interleave"
+ states << Interleave.new( self )
+ states[-2] << states[-1]
+ when "mixed"
+ states << Interleave.new( self )
+ states[-2] << states[-1]
+ states[-1] << TEXT
+ when "define"
+ states << [ event[2]["name"] ]
+ when "ref"
+ states[-1] << Ref.new( event[2]["name"] )
+ when "anyName"
+ states << AnyName.new( self )
+ states[-2] << states[-1]
+ when "nsName"
+ when "except"
+ when "name"
+ when "data"
+ when "param"
+ when "include"
+ when "grammar"
+ when "start"
+ when "externalRef"
+ when "notAllowed"
+ end
+ when :end_element
+ case event[1]
+ when "element", "attribute"
+ states[-1] << event
+ when "zeroOrMore", "oneOrMore", "choice", "optional",
+ "interleave", "group", "mixed"
+ states.pop
+ when "define"
+ ref = states.pop
+ @references[ ref.shift ] = ref
+ #when "empty"
+ end
+ when :end_document
+ states[-1] << event
+ when :text
+ states[-1] << event
+ end
+ end while event[0] != :end_document
+ end
+
+ def receive event
+ validate( event )
+ end
+ end
+
+ class State
+ def initialize( context )
+ @previous = []
+ @events = []
+ @current = 0
+ @count = context.count += 1
+ @references = context.references
+ @value = false
+ end
+
+ def reset
+ return if @current == 0
+ @current = 0
+ @events.each {|s| s.reset if s.kind_of? State }
+ end
+
+ def previous=( previous )
+ @previous << previous
+ end
+
+ def next( event )
+ #print "In next with #{event.inspect}. "
+ #puts "Next (#@current) is #{@events[@current]}"
+ #p @previous
+ return @previous.pop.next( event ) if @events[@current].nil?
+ expand_ref_in( @events, @current ) if @events[@current].class == Ref
+ if ( @events[@current].kind_of? State )
+ @current += 1
+ @events[@current-1].previous = self
+ return @events[@current-1].next( event )
+ end
+ #puts "Current isn't a state"
+ if ( @events[@current].matches?(event) )
+ @current += 1
+ if @events[@current].nil?
+ #puts "#{inspect[0,5]} 1RETURNING #{@previous.inspect[0,5]}"
+ return @previous.pop
+ elsif @events[@current].kind_of? State
+ @current += 1
+ #puts "#{inspect[0,5]} 2RETURNING (#{@current-1}) #{@events[@current-1].inspect[0,5]}; on return, next is #{@events[@current]}"
+ @events[@current-1].previous = self
+ return @events[@current-1]
+ else
+ #puts "#{inspect[0,5]} RETURNING self w/ next(#@current) = #{@events[@current]}"
+ return self
+ end
+ else
+ return nil
+ end
+ end
+
+ def to_s
+ # Abbreviated:
+ self.class.name =~ /(?:::)(\w)\w+$/
+ # Full:
+ #self.class.name =~ /(?:::)(\w+)$/
+ "#$1.#@count"
+ end
+
+ def inspect
+ "< #{to_s} #{@events.collect{|e|
+ pre = e == @events[@current] ? '#' : ''
+ pre + e.inspect unless self == e
+ }.join(', ')} >"
+ end
+
+ def expected
+ return [@events[@current]]
+ end
+
+ def <<( event )
+ add_event_to_arry( @events, event )
+ end
+
+
+ protected
+ def expand_ref_in( arry, ind )
+ new_events = []
+ @references[ arry[ind].to_s ].each{ |evt|
+ add_event_to_arry(new_events,evt)
+ }
+ arry[ind,1] = new_events
+ end
+
+ def add_event_to_arry( arry, evt )
+ evt = generate_event( evt )
+ if evt.kind_of? String
+ arry[-1].event_arg = evt if arry[-1].kind_of? Event and @value
+ @value = false
+ else
+ arry << evt
+ end
+ end
+
+ def generate_event( event )
+ return event if event.kind_of? State or event.class == Ref
+ evt = nil
+ arg = nil
+ case event[0]
+ when :start_element
+ case event[1]
+ when "element"
+ evt = :start_element
+ arg = event[2]["name"]
+ when "attribute"
+ evt = :start_attribute
+ arg = event[2]["name"]
+ when "text"
+ evt = :text
+ when "value"
+ evt = :text
+ @value = true
+ end
+ when :text
+ return event[1]
+ when :end_document
+ return Event.new( event[0] )
+ else # then :end_element
+ case event[1]
+ when "element"
+ evt = :end_element
+ when "attribute"
+ evt = :end_attribute
+ end
+ end
+ return Event.new( evt, arg )
+ end
+ end
+
+
+ class Sequence < State
+ def matches?(event)
+ @events[@current].matches?( event )
+ end
+ end
+
+
+ class Optional < State
+ def next( event )
+ if @current == 0
+ rv = super
+ return rv if rv
+ @prior = @previous.pop
+ return @prior.next( event )
+ end
+ super
+ end
+
+ def matches?(event)
+ @events[@current].matches?(event) ||
+ (@current == 0 and @previous[-1].matches?(event))
+ end
+
+ def expected
+ return [ @prior.expected, @events[0] ].flatten if @current == 0
+ return [@events[@current]]
+ end
+ end
+
+
+ class ZeroOrMore < Optional
+ def next( event )
+ expand_ref_in( @events, @current ) if @events[@current].class == Ref
+ if ( @events[@current].matches?(event) )
+ @current += 1
+ if @events[@current].nil?
+ @current = 0
+ return self
+ elsif @events[@current].kind_of? State
+ @current += 1
+ @events[@current-1].previous = self
+ return @events[@current-1]
+ else
+ return self
+ end
+ else
+ @prior = @previous.pop
+ return @prior.next( event ) if @current == 0
+ return nil
+ end
+ end
+
+ def expected
+ return [ @prior.expected, @events[0] ].flatten if @current == 0
+ return [@events[@current]]
+ end
+ end
+
+
+ class OneOrMore < State
+ def initialize context
+ super
+ @ord = 0
+ end
+
+ def reset
+ super
+ @ord = 0
+ end
+
+ def next( event )
+ expand_ref_in( @events, @current ) if @events[@current].class == Ref
+ if ( @events[@current].matches?(event) )
+ @current += 1
+ @ord += 1
+ if @events[@current].nil?
+ @current = 0
+ return self
+ elsif @events[@current].kind_of? State
+ @current += 1
+ @events[@current-1].previous = self
+ return @events[@current-1]
+ else
+ return self
+ end
+ else
+ return @previous.pop.next( event ) if @current == 0 and @ord > 0
+ return nil
+ end
+ end
+
+ def matches?( event )
+ @events[@current].matches?(event) ||
+ (@current == 0 and @ord > 0 and @previous[-1].matches?(event))
+ end
+
+ def expected
+ if @current == 0 and @ord > 0
+ return [@previous[-1].expected, @events[0]].flatten
+ else
+ return [@events[@current]]
+ end
+ end
+ end
+
+
+ class Choice < State
+ def initialize context
+ super
+ @choices = []
+ end
+
+ def reset
+ super
+ @events = []
+ @choices.each { |c| c.each { |s| s.reset if s.kind_of? State } }
+ end
+
+ def <<( event )
+ add_event_to_arry( @choices, event )
+ end
+
+ def next( event )
+ # Make the choice if we haven't
+ if @events.size == 0
+ c = 0 ; max = @choices.size
+ while c < max
+ if @choices[c][0].class == Ref
+ expand_ref_in( @choices[c], 0 )
+ @choices += @choices[c]
+ @choices.delete( @choices[c] )
+ max -= 1
+ else
+ c += 1
+ end
+ end
+ @events = @choices.find { |evt| evt[0].matches? event }
+ # Remove the references
+ # Find the events
+ end
+ #puts "In next with #{event.inspect}."
+ #puts "events is #{@events.inspect}"
+ unless @events
+ @events = []
+ return nil
+ end
+ #puts "current = #@current"
+ super
+ end
+
+ def matches?( event )
+ return @events[@current].matches?( event ) if @events.size > 0
+ !@choices.find{|evt| evt[0].matches?(event)}.nil?
+ end
+
+ def expected
+ #puts "IN CHOICE EXPECTED"
+ #puts "EVENTS = #{@events.inspect}"
+ return [@events[@current]] if @events.size > 0
+ return @choices.collect do |x|
+ if x[0].kind_of? State
+ x[0].expected
+ else
+ x[0]
+ end
+ end.flatten
+ end
+
+ def inspect
+ "< #{to_s} #{@choices.collect{|e| e.collect{|f|f.to_s}.join(', ')}.join(' or ')} >"
+ end
+
+ protected
+ def add_event_to_arry( arry, evt )
+ if evt.kind_of? State or evt.class == Ref
+ arry << [evt]
+ elsif evt[0] == :text
+ if arry[-1] and
+ arry[-1][-1].kind_of?( Event ) and
+ arry[-1][-1].event_type == :text and @value
+
+ arry[-1][-1].event_arg = evt[1]
+ @value = false
+ end
+ else
+ arry << [] if evt[0] == :start_element
+ arry[-1] << generate_event( evt )
+ end
+ end
+ end
+
+
+ class Interleave < Choice
+ def initialize context
+ super
+ @choice = 0
+ end
+
+ def reset
+ @choice = 0
+ end
+
+ def next_current( event )
+ # Expand references
+ c = 0 ; max = @choices.size
+ while c < max
+ if @choices[c][0].class == Ref
+ expand_ref_in( @choices[c], 0 )
+ @choices += @choices[c]
+ @choices.delete( @choices[c] )
+ max -= 1
+ else
+ c += 1
+ end
+ end
+ @events = @choices[@choice..-1].find { |evt| evt[0].matches? event }
+ @current = 0
+ if @events
+ # reorder the choices
+ old = @choices[@choice]
+ idx = @choices.index( @events )
+ @choices[@choice] = @events
+ @choices[idx] = old
+ @choice += 1
+ end
+
+ #puts "In next with #{event.inspect}."
+ #puts "events is #{@events.inspect}"
+ @events = [] unless @events
+ end
+
+
+ def next( event )
+ # Find the next series
+ next_current(event) unless @events[@current]
+ return nil unless @events[@current]
+
+ expand_ref_in( @events, @current ) if @events[@current].class == Ref
+ #puts "In next with #{event.inspect}."
+ #puts "Next (#@current) is #{@events[@current]}"
+ if ( @events[@current].kind_of? State )
+ @current += 1
+ @events[@current-1].previous = self
+ return @events[@current-1].next( event )
+ end
+ #puts "Current isn't a state"
+ return @previous.pop.next( event ) if @events[@current].nil?
+ if ( @events[@current].matches?(event) )
+ @current += 1
+ if @events[@current].nil?
+ #puts "#{inspect[0,5]} 1RETURNING self" unless @choices[@choice].nil?
+ return self unless @choices[@choice].nil?
+ #puts "#{inspect[0,5]} 1RETURNING #{@previous[-1].inspect[0,5]}"
+ return @previous.pop
+ elsif @events[@current].kind_of? State
+ @current += 1
+ #puts "#{inspect[0,5]} 2RETURNING (#{@current-1}) #{@events[@current-1].inspect[0,5]}; on return, next is #{@events[@current]}"
+ @events[@current-1].previous = self
+ return @events[@current-1]
+ else
+ #puts "#{inspect[0,5]} RETURNING self w/ next(#@current) = #{@events[@current]}"
+ return self
+ end
+ else
+ return nil
+ end
+ end
+
+ def matches?( event )
+ return @events[@current].matches?( event ) if @events[@current]
+ !@choices[@choice..-1].find{|evt| evt[0].matches?(event)}.nil?
+ end
+
+ def expected
+ #puts "IN CHOICE EXPECTED"
+ #puts "EVENTS = #{@events.inspect}"
+ return [@events[@current]] if @events[@current]
+ return @choices[@choice..-1].collect do |x|
+ if x[0].kind_of? State
+ x[0].expected
+ else
+ x[0]
+ end
+ end.flatten
+ end
+
+ def inspect
+ "< #{to_s} #{@choices.collect{|e| e.collect{|f|f.to_s}.join(', ')}.join(' and ')} >"
+ end
+ end
+
+ class Ref
+ def initialize value
+ @value = value
+ end
+ def to_s
+ @value
+ end
+ def inspect
+ "{#{to_s}}"
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/validation/validation.rb b/ruby_1_8_5/lib/rexml/validation/validation.rb
new file mode 100644
index 0000000000..160ea96b31
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/validation/validation.rb
@@ -0,0 +1,155 @@
+require 'rexml/validation/validationexception'
+
+module REXML
+ module Validation
+ module Validator
+ NILEVENT = [ nil ]
+ def reset
+ @current = @root
+ @root.reset
+ @root.previous = true
+ @attr_stack = []
+ self
+ end
+ def dump
+ puts @root.inspect
+ end
+ def validate( event )
+ #puts "Current: #@current"
+ #puts "Event: #{event.inspect}"
+ @attr_stack = [] unless defined? @attr_stack
+ match = @current.next(event)
+ raise ValidationException.new( "Validation error. Expected: "+
+ @current.expected.join( " or " )+" from #{@current.inspect} "+
+ " but got #{Event.new( event[0], event[1] ).inspect}" ) unless match
+ @current = match
+
+ # Check for attributes
+ case event[0]
+ when :start_element
+ #puts "Checking attributes"
+ @attr_stack << event[2]
+ begin
+ sattr = [:start_attribute, nil]
+ eattr = [:end_attribute]
+ text = [:text, nil]
+ k,v = event[2].find { |k,v|
+ sattr[1] = k
+ #puts "Looking for #{sattr.inspect}"
+ m = @current.next( sattr )
+ #puts "Got #{m.inspect}"
+ if m
+ # If the state has text children...
+ #puts "Looking for #{eattr.inspect}"
+ #puts "Expect #{m.expected}"
+ if m.matches?( eattr )
+ #puts "Got end"
+ @current = m
+ else
+ #puts "Didn't get end"
+ text[1] = v
+ #puts "Looking for #{text.inspect}"
+ m = m.next( text )
+ #puts "Got #{m.inspect}"
+ text[1] = nil
+ return false unless m
+ @current = m if m
+ end
+ m = @current.next( eattr )
+ if m
+ @current = m
+ true
+ else
+ false
+ end
+ else
+ false
+ end
+ }
+ event[2].delete(k) if k
+ end while k
+ when :end_element
+ attrs = @attr_stack.pop
+ raise ValidationException.new( "Validation error. Illegal "+
+ " attributes: #{attrs.inspect}") if attrs.length > 0
+ end
+ end
+ end
+
+ class Event
+ def initialize(event_type, event_arg=nil )
+ @event_type = event_type
+ @event_arg = event_arg
+ end
+
+ attr_reader :event_type
+ attr_accessor :event_arg
+
+ def done?
+ @done
+ end
+
+ def single?
+ return (@event_type != :start_element and @event_type != :start_attribute)
+ end
+
+ def matches?( event )
+ #puts "#@event_type =? #{event[0]} && #@event_arg =? #{event[1]} "
+ return false unless event[0] == @event_type
+ case event[0]
+ when nil
+ return true
+ when :start_element
+ return true if event[1] == @event_arg
+ when :end_element
+ return true
+ when :start_attribute
+ return true if event[1] == @event_arg
+ when :end_attribute
+ return true
+ when :end_document
+ return true
+ when :text
+ return (@event_arg.nil? or @event_arg == event[1])
+=begin
+ when :processing_instruction
+ false
+ when :xmldecl
+ false
+ when :start_doctype
+ false
+ when :end_doctype
+ false
+ when :externalentity
+ false
+ when :elementdecl
+ false
+ when :entity
+ false
+ when :attlistdecl
+ false
+ when :notationdecl
+ false
+ when :end_doctype
+ false
+=end
+ else
+ false
+ end
+ end
+
+ def ==( other )
+ return false unless other.kind_of? Event
+ @event_type == other.event_type and @event_arg == other.event_arg
+ end
+
+ def to_s
+ inspect
+ end
+
+ def inspect
+ "#{@event_type.inspect}( #@event_arg )"
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/validation/validationexception.rb b/ruby_1_8_5/lib/rexml/validation/validationexception.rb
new file mode 100644
index 0000000000..4723d9e4d3
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/validation/validationexception.rb
@@ -0,0 +1,9 @@
+module REXML
+ module Validation
+ class ValidationException < RuntimeError
+ def initialize msg
+ super
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/xmldecl.rb b/ruby_1_8_5/lib/rexml/xmldecl.rb
new file mode 100644
index 0000000000..b65604b762
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/xmldecl.rb
@@ -0,0 +1,114 @@
+require 'rexml/encoding'
+require 'rexml/source'
+
+module REXML
+ # NEEDS DOCUMENTATION
+ class XMLDecl < Child
+ include Encoding
+
+ DEFAULT_VERSION = "1.0";
+ DEFAULT_ENCODING = "UTF-8";
+ DEFAULT_STANDALONE = "no";
+ START = '<\?xml';
+ STOP = '\?>';
+
+ attr_accessor :version, :standalone
+ attr_reader :writeencoding
+
+ def initialize(version=DEFAULT_VERSION, encoding=nil, standalone=nil)
+ @writethis = true
+ @writeencoding = !encoding.nil?
+ if version.kind_of? XMLDecl
+ super()
+ @version = version.version
+ self.encoding = version.encoding
+ @writeencoding = version.writeencoding
+ @standalone = version.standalone
+ else
+ super()
+ @version = version
+ self.encoding = encoding
+ @standalone = standalone
+ end
+ @version = DEFAULT_VERSION if @version.nil?
+ end
+
+ def clone
+ XMLDecl.new(self)
+ end
+
+ def write writer, indent=-1, transitive=false, ie_hack=false
+ return nil unless @writethis or writer.kind_of? Output
+ indent( writer, indent )
+ writer << START.sub(/\\/u, '')
+ if writer.kind_of? Output
+ writer << " #{content writer.encoding}"
+ else
+ writer << " #{content encoding}"
+ end
+ writer << STOP.sub(/\\/u, '')
+ end
+
+ def ==( other )
+ other.kind_of?(XMLDecl) and
+ other.version == @version and
+ other.encoding == self.encoding and
+ other.standalone == @standalone
+ end
+
+ def xmldecl version, encoding, standalone
+ @version = version
+ self.encoding = encoding
+ @standalone = standalone
+ end
+
+ def node_type
+ :xmldecl
+ end
+
+ alias :stand_alone? :standalone
+ alias :old_enc= :encoding=
+
+ def encoding=( enc )
+ if enc.nil?
+ self.old_enc = "UTF-8"
+ @writeencoding = false
+ else
+ self.old_enc = enc
+ @writeencoding = true
+ end
+ self.dowrite
+ end
+
+ # Only use this if you do not want the XML declaration to be written;
+ # this object is ignored by the XML writer. Otherwise, instantiate your
+ # own XMLDecl and add it to the document.
+ #
+ # Note that XML 1.1 documents *must* include an XML declaration
+ def XMLDecl.default
+ rv = XMLDecl.new( "1.0" )
+ rv.nowrite
+ rv
+ end
+
+ def nowrite
+ @writethis = false
+ end
+
+ def dowrite
+ @writethis = true
+ end
+
+ def inspect
+ START.sub(/\\/u, '') + " ... " + STOP.sub(/\\/u, '')
+ end
+
+ private
+ def content(enc)
+ rv = "version='#@version'"
+ rv << " encoding='#{enc}'" if @writeencoding || enc !~ /utf-8/i
+ rv << " standalone='#@standalone'" if @standalone
+ rv
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/xmltokens.rb b/ruby_1_8_5/lib/rexml/xmltokens.rb
new file mode 100644
index 0000000000..6bbe5b07d5
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/xmltokens.rb
@@ -0,0 +1,18 @@
+module REXML
+ # Defines a number of tokens used for parsing XML. Not for general
+ # consumption.
+ module XMLTokens
+ NCNAME_STR= '[\w:][\-\w\d.]*'
+ NAME_STR= "(?:#{NCNAME_STR}:)?#{NCNAME_STR}"
+
+ NAMECHAR = '[\-\w\d\.:]'
+ NAME = "([\\w:]#{NAMECHAR}*)"
+ NMTOKEN = "(?:#{NAMECHAR})+"
+ NMTOKENS = "#{NMTOKEN}(\\s+#{NMTOKEN})*"
+ REFERENCE = "(?:&#{NAME};|&#\\d+;|&#x[0-9a-fA-F]+;)"
+
+ #REFERENCE = "(?:#{ENTITYREF}|#{CHARREF})"
+ #ENTITYREF = "&#{NAME};"
+ #CHARREF = "&#\\d+;|&#x[0-9a-fA-F]+;"
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/xpath.rb b/ruby_1_8_5/lib/rexml/xpath.rb
new file mode 100644
index 0000000000..1ed440868b
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/xpath.rb
@@ -0,0 +1,66 @@
+require 'rexml/functions'
+require 'rexml/xpath_parser'
+
+module REXML
+ # Wrapper class. Use this class to access the XPath functions.
+ class XPath
+ include Functions
+ EMPTY_HASH = {}
+
+ # Finds and returns the first node that matches the supplied xpath.
+ # element::
+ # The context element
+ # path::
+ # The xpath to search for. If not supplied or nil, returns the first
+ # node matching '*'.
+ # namespaces::
+ # If supplied, a Hash which defines a namespace mapping.
+ #
+ # XPath.first( node )
+ # XPath.first( doc, "//b"} )
+ # XPath.first( node, "a/x:b", { "x"=>"http://doofus" } )
+ def XPath::first element, path=nil, namespaces={}, variables={}
+ raise "The namespaces argument, if supplied, must be a hash object." unless namespaces.kind_of? Hash
+ raise "The variables argument, if supplied, must be a hash object." unless variables.kind_of? Hash
+ parser = XPathParser.new
+ parser.namespaces = namespaces
+ parser.variables = variables
+ path = "*" unless path
+ element = [element] unless element.kind_of? Array
+ parser.parse(path, element).flatten[0]
+ end
+
+ # Itterates over nodes that match the given path, calling the supplied
+ # block with the match.
+ # element::
+ # The context element
+ # path::
+ # The xpath to search for. If not supplied or nil, defaults to '*'
+ # namespaces::
+ # If supplied, a Hash which defines a namespace mapping
+ #
+ # XPath.each( node ) { |el| ... }
+ # XPath.each( node, '/*[@attr='v']' ) { |el| ... }
+ # XPath.each( node, 'ancestor::x' ) { |el| ... }
+ def XPath::each element, path=nil, namespaces={}, variables={}, &block
+ raise "The namespaces argument, if supplied, must be a hash object." unless namespaces.kind_of? Hash
+ raise "The variables argument, if supplied, must be a hash object." unless variables.kind_of? Hash
+ parser = XPathParser.new
+ parser.namespaces = namespaces
+ parser.variables = variables
+ path = "*" unless path
+ element = [element] unless element.kind_of? Array
+ parser.parse(path, element).each( &block )
+ end
+
+ # Returns an array of nodes matching a given XPath.
+ def XPath::match element, path=nil, namespaces={}, variables={}
+ parser = XPathParser.new
+ parser.namespaces = namespaces
+ parser.variables = variables
+ path = "*" unless path
+ element = [element] unless element.kind_of? Array
+ parser.parse(path,element)
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rexml/xpath_parser.rb b/ruby_1_8_5/lib/rexml/xpath_parser.rb
new file mode 100644
index 0000000000..98ed70cc10
--- /dev/null
+++ b/ruby_1_8_5/lib/rexml/xpath_parser.rb
@@ -0,0 +1,741 @@
+require 'rexml/namespace'
+require 'rexml/xmltokens'
+require 'rexml/attribute'
+require 'rexml/syncenumerator'
+require 'rexml/parsers/xpathparser'
+
+class Object
+ def dclone
+ clone
+ end
+end
+class Symbol
+ def dclone
+ self
+ end
+end
+class Array
+ def dclone
+ klone = self.clone
+ klone.clear
+ self.each{|v| klone << v.dclone}
+ klone
+ end
+end
+
+module REXML
+ # You don't want to use this class. Really. Use XPath, which is a wrapper
+ # for this class. Believe me. You don't want to poke around in here.
+ # There is strange, dark magic at work in this code. Beware. Go back! Go
+ # back while you still can!
+ class XPathParser
+ include XMLTokens
+ LITERAL = /^'([^']*)'|^"([^"]*)"/u
+
+ def initialize( )
+ @parser = REXML::Parsers::XPathParser.new
+ @namespaces = {}
+ @variables = {}
+ end
+
+ def namespaces=( namespaces={} )
+ Functions::namespace_context = namespaces
+ @namespaces = namespaces
+ end
+
+ def variables=( vars={} )
+ Functions::variables = vars
+ @variables = vars
+ end
+
+ def parse path, nodeset
+ #puts "#"*40
+ path_stack = @parser.parse( path )
+ #puts "PARSE: #{path} => #{path_stack.inspect}"
+ #puts "PARSE: nodeset = #{nodeset.inspect}"
+ match( path_stack, nodeset )
+ end
+
+ def get_first path, nodeset
+ #puts "#"*40
+ path_stack = @parser.parse( path )
+ #puts "PARSE: #{path} => #{path_stack.inspect}"
+ #puts "PARSE: nodeset = #{nodeset.inspect}"
+ first( path_stack, nodeset )
+ end
+
+ def predicate path, nodeset
+ path_stack = @parser.parse( path )
+ expr( path_stack, nodeset )
+ end
+
+ def []=( variable_name, value )
+ @variables[ variable_name ] = value
+ end
+
+
+ # Performs a depth-first (document order) XPath search, and returns the
+ # first match. This is the fastest, lightest way to return a single result.
+ #
+ # FIXME: This method is incomplete!
+ def first( path_stack, node )
+ #puts "#{depth}) Entering match( #{path.inspect}, #{tree.inspect} )"
+ return nil if path.size == 0
+
+ case path[0]
+ when :document
+ # do nothing
+ return first( path[1..-1], node )
+ when :child
+ for c in node.children
+ #puts "#{depth}) CHILD checking #{name(c)}"
+ r = first( path[1..-1], c )
+ #puts "#{depth}) RETURNING #{r.inspect}" if r
+ return r if r
+ end
+ when :qname
+ name = path[2]
+ #puts "#{depth}) QNAME #{name(tree)} == #{name} (path => #{path.size})"
+ if node.name == name
+ #puts "#{depth}) RETURNING #{tree.inspect}" if path.size == 3
+ return node if path.size == 3
+ return first( path[3..-1], node )
+ else
+ return nil
+ end
+ when :descendant_or_self
+ r = first( path[1..-1], node )
+ return r if r
+ for c in node.children
+ r = first( path, c )
+ return r if r
+ end
+ when :node
+ return first( path[1..-1], node )
+ when :any
+ return first( path[1..-1], node )
+ end
+ return nil
+ end
+
+
+ def match( path_stack, nodeset )
+ #puts "MATCH: path_stack = #{path_stack.inspect}"
+ #puts "MATCH: nodeset = #{nodeset.inspect}"
+ r = expr( path_stack, nodeset )
+ #puts "MAIN EXPR => #{r.inspect}"
+ r
+ end
+
+ private
+
+
+ # Expr takes a stack of path elements and a set of nodes (either a Parent
+ # or an Array and returns an Array of matching nodes
+ ALL = [ :attribute, :element, :text, :processing_instruction, :comment ]
+ ELEMENTS = [ :element ]
+ def expr( path_stack, nodeset, context=nil )
+ #puts "#"*15
+ #puts "In expr with #{path_stack.inspect}"
+ #puts "Returning" if path_stack.length == 0 || nodeset.length == 0
+ node_types = ELEMENTS
+ return nodeset if path_stack.length == 0 || nodeset.length == 0
+ while path_stack.length > 0
+ #puts "Path stack = #{path_stack.inspect}"
+ #puts "Nodeset is #{nodeset.inspect}"
+ case (op = path_stack.shift)
+ when :document
+ nodeset = [ nodeset[0].root_node ]
+ #puts ":document, nodeset = #{nodeset.inspect}"
+
+ when :qname
+ #puts "IN QNAME"
+ prefix = path_stack.shift
+ name = path_stack.shift
+ default_ns = @namespaces[prefix]
+ default_ns = default_ns ? default_ns : ''
+ nodeset.delete_if do |node|
+ ns = default_ns
+ # FIXME: This DOUBLES the time XPath searches take
+ ns = node.namespace( prefix ) if node.node_type == :element and ns == ''
+ #puts "NS = #{ns.inspect}"
+ #puts "node.node_type == :element => #{node.node_type == :element}"
+ if node.node_type == :element
+ #puts "node.name == #{name} => #{node.name == name}"
+ if node.name == name
+ #puts "node.namespace == #{ns.inspect} => #{node.namespace == ns}"
+ end
+ end
+ !(node.node_type == :element and
+ node.name == name and
+ node.namespace == ns )
+ end
+ node_types = ELEMENTS
+
+ when :any
+ #puts "ANY 1: nodeset = #{nodeset.inspect}"
+ #puts "ANY 1: node_types = #{node_types.inspect}"
+ nodeset.delete_if { |node| !node_types.include?(node.node_type) }
+ #puts "ANY 2: nodeset = #{nodeset.inspect}"
+
+ when :self
+ # This space left intentionally blank
+
+ when :processing_instruction
+ target = path_stack.shift
+ nodeset.delete_if do |node|
+ (node.node_type != :processing_instruction) or
+ ( target!='' and ( node.target != target ) )
+ end
+
+ when :text
+ nodeset.delete_if { |node| node.node_type != :text }
+
+ when :comment
+ nodeset.delete_if { |node| node.node_type != :comment }
+
+ when :node
+ # This space left intentionally blank
+ node_types = ALL
+
+ when :child
+ new_nodeset = []
+ nt = nil
+ for node in nodeset
+ nt = node.node_type
+ new_nodeset += node.children if nt == :element or nt == :document
+ end
+ nodeset = new_nodeset
+ node_types = ELEMENTS
+
+ when :literal
+ literal = path_stack.shift
+ if literal =~ /^\d+(\.\d+)?$/
+ return ($1 ? literal.to_f : literal.to_i)
+ end
+ return literal
+
+ when :attribute
+ new_nodeset = []
+ case path_stack.shift
+ when :qname
+ prefix = path_stack.shift
+ name = path_stack.shift
+ for element in nodeset
+ if element.node_type == :element
+ #puts element.name
+ attr = element.attribute( name, @namespaces[prefix] )
+ new_nodeset << attr if attr
+ end
+ end
+ when :any
+ #puts "ANY"
+ for element in nodeset
+ if element.node_type == :element
+ new_nodeset += element.attributes.to_a
+ end
+ end
+ end
+ nodeset = new_nodeset
+
+ when :parent
+ #puts "PARENT 1: nodeset = #{nodeset}"
+ nodeset = nodeset.collect{|n| n.parent}.compact
+ #nodeset = expr(path_stack.dclone, nodeset.collect{|n| n.parent}.compact)
+ #puts "PARENT 2: nodeset = #{nodeset.inspect}"
+ node_types = ELEMENTS
+
+ when :ancestor
+ new_nodeset = []
+ for node in nodeset
+ while node.parent
+ node = node.parent
+ new_nodeset << node unless new_nodeset.include? node
+ end
+ end
+ nodeset = new_nodeset
+ node_types = ELEMENTS
+
+ when :ancestor_or_self
+ new_nodeset = []
+ for node in nodeset
+ if node.node_type == :element
+ new_nodeset << node
+ while ( node.parent )
+ node = node.parent
+ new_nodeset << node unless new_nodeset.include? node
+ end
+ end
+ end
+ nodeset = new_nodeset
+ node_types = ELEMENTS
+
+ when :predicate
+ new_nodeset = []
+ subcontext = { :size => nodeset.size }
+ pred = path_stack.shift
+ nodeset.each_with_index { |node, index|
+ subcontext[ :node ] = node
+ #puts "PREDICATE SETTING CONTEXT INDEX TO #{index+1}"
+ subcontext[ :index ] = index+1
+ pc = pred.dclone
+ #puts "#{node.hash}) Recursing with #{pred.inspect} and [#{node.inspect}]"
+ result = expr( pc, [node], subcontext )
+ result = result[0] if result.kind_of? Array and result.length == 1
+ #puts "#{node.hash}) Result = #{result.inspect} (#{result.class.name})"
+ if result.kind_of? Numeric
+ #puts "Adding node #{node.inspect}" if result == (index+1)
+ new_nodeset << node if result == (index+1)
+ elsif result.instance_of? Array
+ #puts "Adding node #{node.inspect}" if result.size > 0
+ new_nodeset << node if result.size > 0
+ else
+ #puts "Adding node #{node.inspect}" if result
+ new_nodeset << node if result
+ end
+ }
+ #puts "New nodeset = #{new_nodeset.inspect}"
+ #puts "Path_stack = #{path_stack.inspect}"
+ nodeset = new_nodeset
+=begin
+ predicate = path_stack.shift
+ ns = nodeset.clone
+ result = expr( predicate, ns )
+ #puts "Result = #{result.inspect} (#{result.class.name})"
+ #puts "nodeset = #{nodeset.inspect}"
+ if result.kind_of? Array
+ nodeset = result.zip(ns).collect{|m,n| n if m}.compact
+ else
+ nodeset = result ? nodeset : []
+ end
+ #puts "Outgoing NS = #{nodeset.inspect}"
+=end
+
+ when :descendant_or_self
+ rv = descendant_or_self( path_stack, nodeset )
+ path_stack.clear
+ nodeset = rv
+ node_types = ELEMENTS
+
+ when :descendant
+ results = []
+ nt = nil
+ for node in nodeset
+ nt = node.node_type
+ results += expr( path_stack.dclone.unshift( :descendant_or_self ),
+ node.children ) if nt == :element or nt == :document
+ end
+ nodeset = results
+ node_types = ELEMENTS
+
+ when :following_sibling
+ #puts "FOLLOWING_SIBLING 1: nodeset = #{nodeset}"
+ results = []
+ for node in nodeset
+ all_siblings = node.parent.children
+ current_index = all_siblings.index( node )
+ following_siblings = all_siblings[ current_index+1 .. -1 ]
+ results += expr( path_stack.dclone, following_siblings )
+ end
+ #puts "FOLLOWING_SIBLING 2: nodeset = #{nodeset}"
+ nodeset = results
+
+ when :preceding_sibling
+ results = []
+ for node in nodeset
+ all_siblings = node.parent.children
+ current_index = all_siblings.index( node )
+ preceding_siblings = all_siblings[ 0 .. current_index-1 ].reverse
+ #results += expr( path_stack.dclone, preceding_siblings )
+ end
+ nodeset = preceding_siblings || []
+ node_types = ELEMENTS
+
+ when :preceding
+ new_nodeset = []
+ for node in nodeset
+ new_nodeset += preceding( node )
+ end
+ #puts "NEW NODESET => #{new_nodeset.inspect}"
+ nodeset = new_nodeset
+ node_types = ELEMENTS
+
+ when :following
+ new_nodeset = []
+ for node in nodeset
+ new_nodeset += following( node )
+ end
+ nodeset = new_nodeset
+ node_types = ELEMENTS
+
+ when :namespace
+ new_set = []
+ for node in nodeset
+ new_nodeset << node.namespace if node.node_type == :element or node.node_type == :attribute
+ end
+ nodeset = new_nodeset
+
+ when :variable
+ var_name = path_stack.shift
+ return @variables[ var_name ]
+
+ # :and, :or, :eq, :neq, :lt, :lteq, :gt, :gteq
+ # TODO: Special case for :or and :and -- not evaluate the right
+ # operand if the left alone determines result (i.e. is true for
+ # :or and false for :and).
+ when :eq, :neq, :lt, :lteq, :gt, :gteq, :and, :or
+ left = expr( path_stack.shift, nodeset.dup, context )
+ #puts "LEFT => #{left.inspect} (#{left.class.name})"
+ right = expr( path_stack.shift, nodeset.dup, context )
+ #puts "RIGHT => #{right.inspect} (#{right.class.name})"
+ res = equality_relational_compare( left, op, right )
+ #puts "RES => #{res.inspect}"
+ return res
+
+ when :div
+ left = Functions::number(expr(path_stack.shift, nodeset, context)).to_f
+ right = Functions::number(expr(path_stack.shift, nodeset, context)).to_f
+ return (left / right)
+
+ when :mod
+ left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
+ right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
+ return (left % right)
+
+ when :mult
+ left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
+ right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
+ return (left * right)
+
+ when :plus
+ left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
+ right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
+ return (left + right)
+
+ when :minus
+ left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
+ right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
+ return (left - right)
+
+ when :union
+ left = expr( path_stack.shift, nodeset, context )
+ right = expr( path_stack.shift, nodeset, context )
+ return (left | right)
+
+ when :neg
+ res = expr( path_stack, nodeset, context )
+ return -(res.to_f)
+
+ when :not
+ when :function
+ func_name = path_stack.shift.tr('-','_')
+ arguments = path_stack.shift
+ #puts "FUNCTION 0: #{func_name}(#{arguments.collect{|a|a.inspect}.join(', ')})"
+ subcontext = context ? nil : { :size => nodeset.size }
+
+ res = []
+ cont = context
+ nodeset.each_with_index { |n, i|
+ if subcontext
+ subcontext[:node] = n
+ subcontext[:index] = i
+ cont = subcontext
+ end
+ arg_clone = arguments.dclone
+ args = arg_clone.collect { |arg|
+ #puts "FUNCTION 1: Calling expr( #{arg.inspect}, [#{n.inspect}] )"
+ expr( arg, [n], cont )
+ }
+ #puts "FUNCTION 2: #{func_name}(#{args.collect{|a|a.inspect}.join(', ')})"
+ Functions.context = cont
+ res << Functions.send( func_name, *args )
+ #puts "FUNCTION 3: #{res[-1].inspect}"
+ }
+ return res
+
+ end
+ end # while
+ #puts "EXPR returning #{nodeset.inspect}"
+ return nodeset
+ end
+
+
+ ##########################################################
+ # FIXME
+ # The next two methods are BAD MOJO!
+ # This is my achilles heel. If anybody thinks of a better
+ # way of doing this, be my guest. This really sucks, but
+ # it took me three days to get it to work at all.
+ # ########################################################
+
+ def descendant_or_self( path_stack, nodeset )
+ rs = []
+ #puts "#"*80
+ #puts "PATH_STACK = #{path_stack.inspect}"
+ #puts "NODESET = #{nodeset.collect{|n|n.inspect}.inspect}"
+ d_o_s( path_stack, nodeset, rs )
+ #puts "RS = #{rs.collect{|n|n.inspect}.inspect}"
+ document_order(rs.flatten.compact)
+ #rs.flatten.compact
+ end
+
+ def d_o_s( p, ns, r )
+ #puts "IN DOS with #{ns.inspect}; ALREADY HAVE #{r.inspect}"
+ nt = nil
+ ns.each_index do |i|
+ n = ns[i]
+ #puts "P => #{p.inspect}"
+ x = expr( p.dclone, [ n ] )
+ nt = n.node_type
+ d_o_s( p, n.children, x ) if nt == :element or nt == :document and n.children.size > 0
+ r.concat(x) if x.size > 0
+ end
+ end
+
+
+ # Reorders an array of nodes so that they are in document order
+ # It tries to do this efficiently.
+ #
+ # FIXME: I need to get rid of this, but the issue is that most of the XPath
+ # interpreter functions as a filter, which means that we lose context going
+ # in and out of function calls. If I knew what the index of the nodes was,
+ # I wouldn't have to do this. Maybe add a document IDX for each node?
+ # Problems with mutable documents. Or, rewrite everything.
+ def document_order( array_of_nodes )
+ new_arry = []
+ array_of_nodes.each { |node|
+ node_idx = []
+ np = node.node_type == :attribute ? node.element : node
+ while np.parent and np.parent.node_type == :element
+ node_idx << np.parent.index( np )
+ np = np.parent
+ end
+ new_arry << [ node_idx.reverse, node ]
+ }
+ #puts "new_arry = #{new_arry.inspect}"
+ new_arry.sort{ |s1, s2| s1[0] <=> s2[0] }.collect{ |s| s[1] }
+ end
+
+
+ def recurse( nodeset, &block )
+ for node in nodeset
+ yield node
+ recurse( node, &block ) if node.node_type == :element
+ end
+ end
+
+
+
+ # Builds a nodeset of all of the preceding nodes of the supplied node,
+ # in reverse document order
+ # preceding:: includes every element in the document that precedes this node,
+ # except for ancestors
+ def preceding( node )
+ #puts "IN PRECEDING"
+ ancestors = []
+ p = node.parent
+ while p
+ ancestors << p
+ p = p.parent
+ end
+
+ acc = []
+ p = preceding_node_of( node )
+ #puts "P = #{p.inspect}"
+ while p
+ if ancestors.include? p
+ ancestors.delete(p)
+ else
+ acc << p
+ end
+ p = preceding_node_of( p )
+ #puts "P = #{p.inspect}"
+ end
+ acc
+ end
+
+ def preceding_node_of( node )
+ #puts "NODE: #{node.inspect}"
+ #puts "PREVIOUS NODE: #{node.previous_sibling_node.inspect}"
+ #puts "PARENT NODE: #{node.parent}"
+ psn = node.previous_sibling_node
+ if psn.nil?
+ if node.parent.nil? or node.parent.class == Document
+ return nil
+ end
+ return node.parent
+ #psn = preceding_node_of( node.parent )
+ end
+ while psn and psn.kind_of? Element and psn.children.size > 0
+ psn = psn.children[-1]
+ end
+ psn
+ end
+
+ def following( node )
+ #puts "IN PRECEDING"
+ acc = []
+ p = next_sibling_node( node )
+ #puts "P = #{p.inspect}"
+ while p
+ acc << p
+ p = following_node_of( p )
+ #puts "P = #{p.inspect}"
+ end
+ acc
+ end
+
+ def following_node_of( node )
+ #puts "NODE: #{node.inspect}"
+ #puts "PREVIOUS NODE: #{node.previous_sibling_node.inspect}"
+ #puts "PARENT NODE: #{node.parent}"
+ if node.kind_of? Element and node.children.size > 0
+ return node.children[0]
+ end
+ return next_sibling_node(node)
+ end
+
+ def next_sibling_node(node)
+ psn = node.next_sibling_node
+ while psn.nil?
+ if node.parent.nil? or node.parent.class == Document
+ return nil
+ end
+ node = node.parent
+ psn = node.next_sibling_node
+ #puts "psn = #{psn.inspect}"
+ end
+ return psn
+ end
+
+ def norm b
+ case b
+ when true, false
+ return b
+ when 'true', 'false'
+ return Functions::boolean( b )
+ when /^\d+(\.\d+)?$/
+ return Functions::number( b )
+ else
+ return Functions::string( b )
+ end
+ end
+
+ def equality_relational_compare( set1, op, set2 )
+ #puts "EQ_REL_COMP(#{set1.inspect} #{op.inspect} #{set2.inspect})"
+ if set1.kind_of? Array and set2.kind_of? Array
+ #puts "#{set1.size} & #{set2.size}"
+ if set1.size == 1 and set2.size == 1
+ set1 = set1[0]
+ set2 = set2[0]
+ elsif set1.size == 0 or set2.size == 0
+ nd = set1.size==0 ? set2 : set1
+ rv = nd.collect { |il| compare( il, op, nil ) }
+ #puts "RV = #{rv.inspect}"
+ return rv
+ else
+ res = []
+ enum = SyncEnumerator.new( set1, set2 ).each { |i1, i2|
+ #puts "i1 = #{i1.inspect} (#{i1.class.name})"
+ #puts "i2 = #{i2.inspect} (#{i2.class.name})"
+ i1 = norm( i1 )
+ i2 = norm( i2 )
+ res << compare( i1, op, i2 )
+ }
+ return res
+ end
+ end
+ #puts "EQ_REL_COMP: #{set1.inspect} (#{set1.class.name}), #{op}, #{set2.inspect} (#{set2.class.name})"
+ #puts "COMPARING VALUES"
+ # If one is nodeset and other is number, compare number to each item
+ # in nodeset s.t. number op number(string(item))
+ # If one is nodeset and other is string, compare string to each item
+ # in nodeset s.t. string op string(item)
+ # If one is nodeset and other is boolean, compare boolean to each item
+ # in nodeset s.t. boolean op boolean(item)
+ if set1.kind_of? Array or set2.kind_of? Array
+ #puts "ISA ARRAY"
+ if set1.kind_of? Array
+ a = set1
+ b = set2
+ else
+ a = set2
+ b = set1
+ end
+
+ case b
+ when true, false
+ return a.collect {|v| compare( Functions::boolean(v), op, b ) }
+ when Numeric
+ return a.collect {|v| compare( Functions::number(v), op, b )}
+ when /^\d+(\.\d+)?$/
+ b = Functions::number( b )
+ #puts "B = #{b.inspect}"
+ return a.collect {|v| compare( Functions::number(v), op, b )}
+ else
+ #puts "Functions::string( #{b}(#{b.class.name}) ) = #{Functions::string(b)}"
+ b = Functions::string( b )
+ return a.collect { |v| compare( Functions::string(v), op, b ) }
+ end
+ else
+ # If neither is nodeset,
+ # If op is = or !=
+ # If either boolean, convert to boolean
+ # If either number, convert to number
+ # Else, convert to string
+ # Else
+ # Convert both to numbers and compare
+ s1 = set1.to_s
+ s2 = set2.to_s
+ #puts "EQ_REL_COMP: #{set1}=>#{s1}, #{set2}=>#{s2}"
+ if s1 == 'true' or s1 == 'false' or s2 == 'true' or s2 == 'false'
+ #puts "Functions::boolean(#{set1})=>#{Functions::boolean(set1)}"
+ #puts "Functions::boolean(#{set2})=>#{Functions::boolean(set2)}"
+ set1 = Functions::boolean( set1 )
+ set2 = Functions::boolean( set2 )
+ else
+ if op == :eq or op == :neq
+ if s1 =~ /^\d+(\.\d+)?$/ or s2 =~ /^\d+(\.\d+)?$/
+ set1 = Functions::number( s1 )
+ set2 = Functions::number( s2 )
+ else
+ set1 = Functions::string( set1 )
+ set2 = Functions::string( set2 )
+ end
+ else
+ set1 = Functions::number( set1 )
+ set2 = Functions::number( set2 )
+ end
+ end
+ #puts "EQ_REL_COMP: #{set1} #{op} #{set2}"
+ #puts ">>> #{compare( set1, op, set2 )}"
+ return compare( set1, op, set2 )
+ end
+ return false
+ end
+
+ def compare a, op, b
+ #puts "COMPARE #{a.inspect}(#{a.class.name}) #{op} #{b.inspect}(#{b.class.name})"
+ case op
+ when :eq
+ a == b
+ when :neq
+ a != b
+ when :lt
+ a < b
+ when :lteq
+ a <= b
+ when :gt
+ a > b
+ when :gteq
+ a >= b
+ when :and
+ a and b
+ when :or
+ a or b
+ else
+ false
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rinda/rinda.rb b/ruby_1_8_5/lib/rinda/rinda.rb
new file mode 100644
index 0000000000..6c59e68654
--- /dev/null
+++ b/ruby_1_8_5/lib/rinda/rinda.rb
@@ -0,0 +1,283 @@
+require 'drb/drb'
+require 'thread'
+
+##
+# A module to implement the Linda distributed computing paradigm in Ruby.
+#
+# Rinda is part of DRb (dRuby).
+#
+# == Example(s)
+#
+# See the sample/drb/ directory in the Ruby distribution, from 1.8.2 onwards.
+#
+#--
+# TODO
+# == Introduction to Linda/rinda?
+#
+# == Why is this library separate from DRb?
+
+module Rinda
+
+ ##
+ # Rinda error base class
+
+ class RindaError < RuntimeError; end
+
+ ##
+ # Raised when a hash-based tuple has an invalid key.
+
+ class InvalidHashTupleKey < RindaError; end
+
+ ##
+ # Raised when trying to use a canceled tuple.
+
+ class RequestCanceledError < ThreadError; end
+
+ ##
+ # Raised when trying to use an expired tuple.
+
+ class RequestExpiredError < ThreadError; end
+
+ ##
+ # A tuple is the elementary object in Rinda programming.
+ # Tuples may be matched against templates if the tuple and
+ # the template are the same size.
+
+ class Tuple
+
+ ##
+ # Creates a new Tuple from +ary_or_hash+ which must be an Array or Hash.
+
+ def initialize(ary_or_hash)
+ if hash?(ary_or_hash)
+ init_with_hash(ary_or_hash)
+ else
+ init_with_ary(ary_or_hash)
+ end
+ end
+
+ ##
+ # The number of elements in the tuple.
+
+ def size
+ @tuple.size
+ end
+
+ ##
+ # Accessor method for elements of the tuple.
+
+ def [](k)
+ @tuple[k]
+ end
+
+ ##
+ # Fetches item +k+ from the tuple.
+
+ def fetch(k)
+ @tuple.fetch(k)
+ end
+
+ ##
+ # Iterate through the tuple, yielding the index or key, and the
+ # value, thus ensuring arrays are iterated similarly to hashes.
+
+ def each # FIXME
+ if Hash === @tuple
+ @tuple.each { |k, v| yield(k, v) }
+ else
+ @tuple.each_with_index { |v, k| yield(k, v) }
+ end
+ end
+
+ ##
+ # Return the tuple itself
+ def value
+ @tuple
+ end
+
+ private
+
+ def hash?(ary_or_hash)
+ ary_or_hash.respond_to?(:keys)
+ end
+
+ ##
+ # Munges +ary+ into a valid Tuple.
+
+ def init_with_ary(ary)
+ @tuple = Array.new(ary.size)
+ @tuple.size.times do |i|
+ @tuple[i] = ary[i]
+ end
+ end
+
+ ##
+ # Ensures +hash+ is a valid Tuple.
+
+ def init_with_hash(hash)
+ @tuple = Hash.new
+ hash.each do |k, v|
+ raise InvalidHashTupleKey unless String === k
+ @tuple[k] = v
+ end
+ end
+
+ end
+
+ ##
+ # Templates are used to match tuples in Rinda.
+
+ class Template < Tuple
+
+ ##
+ # Matches this template against +tuple+. The +tuple+ must be the same
+ # size as the template. An element with a +nil+ value in a template acts
+ # as a wildcard, matching any value in the corresponding position in the
+ # tuple. Elements of the template match the +tuple+ if the are #== or
+ # #===.
+ #
+ # Template.new([:foo, 5]).match Tuple.new([:foo, 5]) # => true
+ # Template.new([:foo, nil]).match Tuple.new([:foo, 5]) # => true
+ # Template.new([String]).match Tuple.new(['hello']) # => true
+ #
+ # Template.new([:foo]).match Tuple.new([:foo, 5]) # => false
+ # Template.new([:foo, 6]).match Tuple.new([:foo, 5]) # => false
+ # Template.new([:foo, nil]).match Tuple.new([:foo]) # => false
+ # Template.new([:foo, 6]).match Tuple.new([:foo]) # => false
+
+ def match(tuple)
+ return false unless tuple.respond_to?(:size)
+ return false unless tuple.respond_to?(:fetch)
+ return false unless self.size == tuple.size
+ each do |k, v|
+ begin
+ it = tuple.fetch(k)
+ rescue
+ return false
+ end
+ next if v.nil?
+ next if v == it
+ next if v === it
+ return false
+ end
+ return true
+ end
+
+ ##
+ # Alias for #match.
+
+ def ===(tuple)
+ match(tuple)
+ end
+
+ end
+
+ ##
+ # <i>Documentation?</i>
+
+ class DRbObjectTemplate
+
+ ##
+ # Creates a new DRbObjectTemplate that will match against +uri+ and +ref+.
+
+ def initialize(uri=nil, ref=nil)
+ @drb_uri = uri
+ @drb_ref = ref
+ end
+
+ ##
+ # This DRbObjectTemplate matches +ro+ if the remote object's drburi and
+ # drbref are the same. +nil+ is used as a wildcard.
+
+ def ===(ro)
+ return true if super(ro)
+ unless @drb_uri.nil?
+ return false unless (@drb_uri === ro.__drburi rescue false)
+ end
+ unless @drb_ref.nil?
+ return false unless (@drb_ref === ro.__drbref rescue false)
+ end
+ true
+ end
+
+ end
+
+ ##
+ # TupleSpaceProxy allows a remote Tuplespace to appear as local.
+
+ class TupleSpaceProxy
+
+ ##
+ # Creates a new TupleSpaceProxy to wrap +ts+.
+
+ def initialize(ts)
+ @ts = ts
+ end
+
+ ##
+ # Adds +tuple+ to the proxied TupleSpace. See TupleSpace#write.
+
+ def write(tuple, sec=nil)
+ @ts.write(tuple, sec)
+ end
+
+ ##
+ # Takes +tuple+ from the proxied TupleSpace. See TupleSpace#take.
+
+ def take(tuple, sec=nil, &block)
+ port = []
+ @ts.move(DRbObject.new(port), tuple, sec, &block)
+ port[0]
+ end
+
+ ##
+ # Reads +tuple+ from the proxied TupleSpace. See TupleSpace#read.
+
+ def read(tuple, sec=nil, &block)
+ @ts.read(tuple, sec, &block)
+ end
+
+ ##
+ # Reads all tuples matching +tuple+ from the proxied TupleSpace. See
+ # TupleSpace#read_all.
+
+ def read_all(tuple)
+ @ts.read_all(tuple)
+ end
+
+ ##
+ # Registers for notifications of event +ev+ on the proxied TupleSpace.
+ # See TupleSpace#notify
+
+ def notify(ev, tuple, sec=nil)
+ @ts.notify(ev, tuple, sec)
+ end
+
+ end
+
+ ##
+ # An SimpleRenewer allows a TupleSpace to check if a TupleEntry is still
+ # alive.
+
+ class SimpleRenewer
+
+ include DRbUndumped
+
+ ##
+ # Creates a new SimpleRenewer that keeps an object alive for another +sec+
+ # seconds.
+
+ def initialize(sec=180)
+ @sec = sec
+ end
+
+ ##
+ # Called by the TupleSpace to check if the object is still alive.
+
+ def renew
+ @sec
+ end
+ end
+
+end
+
diff --git a/ruby_1_8_5/lib/rinda/ring.rb b/ruby_1_8_5/lib/rinda/ring.rb
new file mode 100644
index 0000000000..ac8a716216
--- /dev/null
+++ b/ruby_1_8_5/lib/rinda/ring.rb
@@ -0,0 +1,271 @@
+#
+# Note: Rinda::Ring API is unstable.
+#
+require 'drb/drb'
+require 'rinda/rinda'
+require 'thread'
+
+module Rinda
+
+ ##
+ # The default port Ring discovery will use.
+
+ Ring_PORT = 7647
+
+ ##
+ # A RingServer allows a Rinda::TupleSpace to be located via UDP broadcasts.
+ # Service location uses the following steps:
+ #
+ # 1. A RingServer begins listening on the broadcast UDP address.
+ # 2. A RingFinger sends a UDP packet containing the DRb URI where it will
+ # listen for a reply.
+ # 3. The RingServer recieves the UDP packet and connects back to the
+ # provided DRb URI with the DRb service.
+
+ class RingServer
+
+ include DRbUndumped
+
+ ##
+ # Advertises +ts+ on the UDP broadcast address at +port+.
+
+ def initialize(ts, port=Ring_PORT)
+ @ts = ts
+ @soc = UDPSocket.open
+ @soc.bind('', port)
+ @w_service = write_service
+ @r_service = reply_service
+ end
+
+ ##
+ # Creates a thread that picks up UDP packets and passes them to do_write
+ # for decoding.
+
+ def write_service
+ Thread.new do
+ loop do
+ msg = @soc.recv(1024)
+ do_write(msg)
+ end
+ end
+ end
+
+ ##
+ # Extracts the response URI from +msg+ and adds it to TupleSpace where it
+ # will be picked up by +reply_service+ for notification.
+
+ def do_write(msg)
+ Thread.new do
+ begin
+ tuple, sec = Marshal.load(msg)
+ @ts.write(tuple, sec)
+ rescue
+ end
+ end
+ end
+
+ ##
+ # Creates a thread that notifies waiting clients from the TupleSpace.
+
+ def reply_service
+ Thread.new do
+ loop do
+ do_reply
+ end
+ end
+ end
+
+ ##
+ # Pulls lookup tuples out of the TupleSpace and sends their DRb object the
+ # address of the local TupleSpace.
+
+ def do_reply
+ tuple = @ts.take([:lookup_ring, nil])
+ Thread.new { tuple[1].call(@ts) rescue nil}
+ rescue
+ end
+
+ end
+
+ ##
+ # RingFinger is used by RingServer clients to discover the RingServer's
+ # TupleSpace. Typically, all a client needs to do is call
+ # RingFinger.primary to retrieve the remote TupleSpace, which it can then
+ # begin using.
+
+ class RingFinger
+
+ @@broadcast_list = ['<broadcast>', 'localhost']
+
+ @@finger = nil
+
+ ##
+ # Creates a singleton RingFinger and looks for a RingServer. Returns the
+ # created RingFinger.
+
+ def self.finger
+ unless @@finger
+ @@finger = self.new
+ @@finger.lookup_ring_any
+ end
+ @@finger
+ end
+
+ ##
+ # Returns the first advertised TupleSpace.
+
+ def self.primary
+ finger.primary
+ end
+
+ ##
+ # Contains all discoverd TupleSpaces except for the primary.
+
+ def self.to_a
+ finger.to_a
+ end
+
+ ##
+ # The list of addresses where RingFinger will send query packets.
+
+ attr_accessor :broadcast_list
+
+ ##
+ # The port that RingFinger will send query packets to.
+
+ attr_accessor :port
+
+ ##
+ # Contain the first advertised TupleSpace after lookup_ring_any is called.
+
+ attr_accessor :primary
+
+ ##
+ # Creates a new RingFinger that will look for RingServers at +port+ on
+ # the addresses in +broadcast_list+.
+
+ def initialize(broadcast_list=@@broadcast_list, port=Ring_PORT)
+ @broadcast_list = broadcast_list || ['localhost']
+ @port = port
+ @primary = nil
+ @rings = []
+ end
+
+ ##
+ # Contains all discovered TupleSpaces except for the primary.
+
+ def to_a
+ @rings
+ end
+
+ ##
+ # Iterates over all discovered TupleSpaces starting with the primary.
+
+ def each
+ lookup_ring_any unless @primary
+ return unless @primary
+ yield(@primary)
+ @rings.each { |x| yield(x) }
+ end
+
+ ##
+ # Looks up RingServers waiting +timeout+ seconds. RingServers will be
+ # given +block+ as a callback, which will be called with the remote
+ # TupleSpace.
+
+ def lookup_ring(timeout=5, &block)
+ return lookup_ring_any(timeout) unless block_given?
+
+ msg = Marshal.dump([[:lookup_ring, DRbObject.new(block)], timeout])
+ @broadcast_list.each do |it|
+ soc = UDPSocket.open
+ begin
+ soc.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
+ soc.send(msg, 0, it, @port)
+ rescue
+ nil
+ ensure
+ soc.close
+ end
+ end
+ sleep(timeout)
+ end
+
+ ##
+ # Returns the first found remote TupleSpace. Any further recovered
+ # TupleSpaces can be found by calling +to_a+.
+
+ def lookup_ring_any(timeout=5)
+ queue = Queue.new
+
+ th = Thread.new do
+ self.lookup_ring(timeout) do |ts|
+ queue.push(ts)
+ end
+ queue.push(nil)
+ while it = queue.pop
+ @rings.push(it)
+ end
+ end
+
+ @primary = queue.pop
+ raise('RingNotFound') if @primary.nil?
+ @primary
+ end
+
+ end
+
+ ##
+ # RingProvider uses a RingServer advertised TupleSpace as a name service.
+ # TupleSpace clients can register themselves with the remote TupleSpace and
+ # look up other provided services via the remote TupleSpace.
+ #
+ # Services are registered with a tuple of the format [:name, klass,
+ # DRbObject, description].
+
+ class RingProvider
+
+ ##
+ # Creates a RingProvider that will provide a +klass+ service running on
+ # +front+, with a +description+. +renewer+ is optional.
+
+ def initialize(klass, front, desc, renewer = nil)
+ @tuple = [:name, klass, front, desc]
+ @renewer = renewer || Rinda::SimpleRenewer.new
+ end
+
+ ##
+ # Advertises this service on the primary remote TupleSpace.
+
+ def provide
+ ts = Rinda::RingFinger.primary
+ ts.write(@tuple, @renewer)
+ end
+
+ end
+
+end
+
+if __FILE__ == $0
+ DRb.start_service
+ case ARGV.shift
+ when 's'
+ require 'rinda/tuplespace'
+ ts = Rinda::TupleSpace.new
+ place = Rinda::RingServer.new(ts)
+ $stdin.gets
+ when 'w'
+ finger = Rinda::RingFinger.new(nil)
+ finger.lookup_ring do |ts|
+ p ts
+ ts.write([:hello, :world])
+ end
+ when 'r'
+ finger = Rinda::RingFinger.new(nil)
+ finger.lookup_ring do |ts|
+ p ts
+ p ts.take([nil, nil])
+ end
+ end
+end
+
diff --git a/ruby_1_8_5/lib/rinda/tuplespace.rb b/ruby_1_8_5/lib/rinda/tuplespace.rb
new file mode 100644
index 0000000000..73e79bb401
--- /dev/null
+++ b/ruby_1_8_5/lib/rinda/tuplespace.rb
@@ -0,0 +1,589 @@
+require 'monitor'
+require 'thread'
+require 'drb/drb'
+require 'rinda/rinda'
+
+module Rinda
+
+ ##
+ # A TupleEntry is a Tuple (i.e. a possible entry in some Tuplespace)
+ # together with expiry and cancellation data.
+
+ class TupleEntry
+
+ include DRbUndumped
+
+ attr_accessor :expires
+
+ ##
+ # Creates a TupleEntry based on +ary+ with an optional renewer or expiry
+ # time +sec+.
+ #
+ # A renewer must implement the +renew+ method which returns a Numeric,
+ # nil, or true to indicate when the tuple has expired.
+
+ def initialize(ary, sec=nil)
+ @cancel = false
+ @expires = nil
+ @tuple = make_tuple(ary)
+ @renewer = nil
+ renew(sec)
+ end
+
+ ##
+ # Marks this TupleEntry as canceled.
+
+ def cancel
+ @cancel = true
+ end
+
+ ##
+ # A TupleEntry is dead when it is canceled or expired.
+
+ def alive?
+ !canceled? && !expired?
+ end
+
+ ##
+ # Return the object which makes up the tuple itself: the Array
+ # or Hash.
+
+ def value; @tuple.value; end
+
+ ##
+ # Returns the canceled status.
+
+ def canceled?; @cancel; end
+
+ ##
+ # Has this tuple expired? (true/false).
+ #
+ # A tuple has expired when its expiry timer based on the +sec+ argument to
+ # #initialize runs out.
+
+ def expired?
+ return true unless @expires
+ return false if @expires > Time.now
+ return true if @renewer.nil?
+ renew(@renewer)
+ return true unless @expires
+ return @expires < Time.now
+ end
+
+ ##
+ # Reset the expiry time according to +sec_or_renewer+.
+ #
+ # +nil+:: it is set to expire in the far future.
+ # +false+:: it has expired.
+ # Numeric:: it will expire in that many seconds.
+ #
+ # Otherwise the argument refers to some kind of renewer object
+ # which will reset its expiry time.
+
+ def renew(sec_or_renewer)
+ sec, @renewer = get_renewer(sec_or_renewer)
+ @expires = make_expires(sec)
+ end
+
+ ##
+ # Returns an expiry Time based on +sec+ which can be one of:
+ # Numeric:: +sec+ seconds into the future
+ # +true+:: the expiry time is the start of 1970 (i.e. expired)
+ # +nil+:: it is Tue Jan 19 03:14:07 GMT Standard Time 2038 (i.e. when
+ # UNIX clocks will die)
+
+ def make_expires(sec=nil)
+ case sec
+ when Numeric
+ Time.now + sec
+ when true
+ Time.at(1)
+ when nil
+ Time.at(2**31-1)
+ end
+ end
+
+ ##
+ # Retrieves +key+ from the tuple.
+
+ def [](key)
+ @tuple[key]
+ end
+
+ ##
+ # Fetches +key+ from the tuple.
+
+ def fetch(key)
+ @tuple.fetch(key)
+ end
+
+ ##
+ # The size of the tuple.
+
+ def size
+ @tuple.size
+ end
+
+ ##
+ # Creates a Rinda::Tuple for +ary+.
+
+ def make_tuple(ary)
+ Rinda::Tuple.new(ary)
+ end
+
+ private
+
+ ##
+ # Returns a valid argument to make_expires and the renewer or nil.
+ #
+ # Given +true+, +nil+, or Numeric, returns that value and +nil+ (no actual
+ # renewer). Otherwise it returns an expiry value from calling +it.renew+
+ # and the renewer.
+
+ def get_renewer(it)
+ case it
+ when Numeric, true, nil
+ return it, nil
+ else
+ begin
+ return it.renew, it
+ rescue Exception
+ return it, nil
+ end
+ end
+ end
+
+ end
+
+ ##
+ # A TemplateEntry is a Template together with expiry and cancellation data.
+
+ class TemplateEntry < TupleEntry
+ ##
+ # Matches this TemplateEntry against +tuple+. See Template#match for
+ # details on how a Template matches a Tuple.
+
+ def match(tuple)
+ @tuple.match(tuple)
+ end
+
+ alias === match
+
+ def make_tuple(ary) # :nodoc:
+ Rinda::Template.new(ary)
+ end
+
+ end
+
+ ##
+ # <i>Documentation?</i>
+
+ class WaitTemplateEntry < TemplateEntry
+
+ attr_reader :found
+
+ def initialize(place, ary, expires=nil)
+ super(ary, expires)
+ @place = place
+ @cond = place.new_cond
+ @found = nil
+ end
+
+ def cancel
+ super
+ signal
+ end
+
+ def wait
+ @cond.wait
+ end
+
+ def read(tuple)
+ @found = tuple
+ signal
+ end
+
+ def signal
+ @place.synchronize do
+ @cond.signal
+ end
+ end
+
+ end
+
+ ##
+ # A NotifyTemplateEntry is returned by TupleSpace#notify and is notified of
+ # TupleSpace changes. You may receive either your subscribed event or the
+ # 'close' event when iterating over notifications.
+ #
+ # See TupleSpace#notify_event for valid notification types.
+ #
+ # == Example
+ #
+ # ts = Rinda::TupleSpace.new
+ # observer = ts.notify 'write', [nil]
+ #
+ # Thread.start do
+ # observer.each { |t| p t }
+ # end
+ #
+ # 3.times { |i| ts.write [i] }
+ #
+ # Outputs:
+ #
+ # ['write', [0]]
+ # ['write', [1]]
+ # ['write', [2]]
+
+ class NotifyTemplateEntry < TemplateEntry
+
+ ##
+ # Creates a new NotifyTemplateEntry that watches +place+ for +event+s that
+ # match +tuple+.
+
+ def initialize(place, event, tuple, expires=nil)
+ ary = [event, Rinda::Template.new(tuple)]
+ super(ary, expires)
+ @queue = Queue.new
+ @done = false
+ end
+
+ ##
+ # Called by TupleSpace to notify this NotifyTemplateEntry of a new event.
+
+ def notify(ev)
+ @queue.push(ev)
+ end
+
+ ##
+ # Retrieves a notification. Raises RequestExpiredError when this
+ # NotifyTemplateEntry expires.
+
+ def pop
+ raise RequestExpiredError if @done
+ it = @queue.pop
+ @done = true if it[0] == 'close'
+ return it
+ end
+
+ ##
+ # Yields event/tuple pairs until this NotifyTemplateEntry expires.
+
+ def each # :yields: event, tuple
+ while !@done
+ it = pop
+ yield(it)
+ end
+ rescue
+ ensure
+ cancel
+ end
+
+ end
+
+ ##
+ # TupleBag is an unordered collection of tuples. It is the basis
+ # of Tuplespace.
+
+ class TupleBag
+
+ def initialize # :nodoc:
+ @hash = {}
+ end
+
+ ##
+ # +true+ if the TupleBag to see if it has any expired entries.
+
+ def has_expires?
+ @hash.each do |k, v|
+ v.each do |tuple|
+ return true if tuple.expires
+ end
+ end
+ false
+ end
+
+ ##
+ # Add +ary+ to the TupleBag.
+
+ def push(ary)
+ size = ary.size
+ @hash[size] ||= []
+ @hash[size].push(ary)
+ end
+
+ ##
+ # Removes +ary+ from the TupleBag.
+
+ def delete(ary)
+ size = ary.size
+ @hash.fetch(size, []).delete(ary)
+ end
+
+ ##
+ # Finds all live tuples that match +template+.
+
+ def find_all(template)
+ @hash.fetch(template.size, []).find_all do |tuple|
+ tuple.alive? && template.match(tuple)
+ end
+ end
+
+ ##
+ # Finds a live tuple that matches +template+.
+
+ def find(template)
+ @hash.fetch(template.size, []).find do |tuple|
+ tuple.alive? && template.match(tuple)
+ end
+ end
+
+ ##
+ # Finds all tuples in the TupleBag which when treated as templates, match
+ # +tuple+ and are alive.
+
+ def find_all_template(tuple)
+ @hash.fetch(tuple.size, []).find_all do |template|
+ template.alive? && template.match(tuple)
+ end
+ end
+
+ ##
+ # Delete tuples which dead tuples from the TupleBag, returning the deleted
+ # tuples.
+
+ def delete_unless_alive
+ deleted = []
+ @hash.keys.each do |size|
+ ary = []
+ @hash[size].each do |tuple|
+ if tuple.alive?
+ ary.push(tuple)
+ else
+ deleted.push(tuple)
+ end
+ end
+ @hash[size] = ary
+ end
+ deleted
+ end
+
+ end
+
+ ##
+ # The Tuplespace manages access to the tuples it contains,
+ # ensuring mutual exclusion requirements are met.
+ #
+ # The +sec+ option for the write, take, move, read and notify methods may
+ # either be a number of seconds or a Renewer object.
+
+ class TupleSpace
+
+ include DRbUndumped
+ include MonitorMixin
+
+ ##
+ # Creates a new TupleSpace. +period+ is used to control how often to look
+ # for dead tuples after modifications to the TupleSpace.
+ #
+ # If no dead tuples are found +period+ seconds after the last
+ # modification, the TupleSpace will stop looking for dead tuples.
+
+ def initialize(period=60)
+ super()
+ @bag = TupleBag.new
+ @read_waiter = TupleBag.new
+ @take_waiter = TupleBag.new
+ @notify_waiter = TupleBag.new
+ @period = period
+ @keeper = nil
+ end
+
+ ##
+ # Adds +tuple+
+
+ def write(tuple, sec=nil)
+ entry = TupleEntry.new(tuple, sec)
+ start_keeper
+ synchronize do
+ if entry.expired?
+ @read_waiter.find_all_template(entry).each do |template|
+ template.read(tuple)
+ end
+ notify_event('write', entry.value)
+ notify_event('delete', entry.value)
+ else
+ @bag.push(entry)
+ @read_waiter.find_all_template(entry).each do |template|
+ template.read(tuple)
+ end
+ @take_waiter.find_all_template(entry).each do |template|
+ template.signal
+ end
+ notify_event('write', entry.value)
+ end
+ end
+ entry
+ end
+
+ ##
+ # Removes +tuple+
+
+ def take(tuple, sec=nil, &block)
+ move(nil, tuple, sec, &block)
+ end
+
+ ##
+ # Moves +tuple+ to +port+.
+
+ def move(port, tuple, sec=nil)
+ template = WaitTemplateEntry.new(self, tuple, sec)
+ yield(template) if block_given?
+ start_keeper
+ synchronize do
+ entry = @bag.find(template)
+ if entry
+ port.push(entry.value) if port
+ @bag.delete(entry)
+ notify_event('take', entry.value)
+ return entry.value
+ end
+ raise RequestExpiredError if template.expired?
+
+ begin
+ @take_waiter.push(template)
+ while true
+ raise RequestCanceledError if template.canceled?
+ raise RequestExpiredError if template.expired?
+ entry = @bag.find(template)
+ if entry
+ port.push(entry.value) if port
+ @bag.delete(entry)
+ notify_event('take', entry.value)
+ return entry.value
+ end
+ template.wait
+ end
+ ensure
+ @take_waiter.delete(template)
+ end
+ end
+ end
+
+ ##
+ # Reads +tuple+, but does not remove it.
+
+ def read(tuple, sec=nil)
+ template = WaitTemplateEntry.new(self, tuple, sec)
+ yield(template) if block_given?
+ start_keeper
+ synchronize do
+ entry = @bag.find(template)
+ return entry.value if entry
+ raise RequestExpiredError if template.expired?
+
+ begin
+ @read_waiter.push(template)
+ template.wait
+ raise RequestCanceledError if template.canceled?
+ raise RequestExpiredError if template.expired?
+ return template.found
+ ensure
+ @read_waiter.delete(template)
+ end
+ end
+ end
+
+ ##
+ # Returns all tuples matching +tuple+. Does not remove the found tuples.
+
+ def read_all(tuple)
+ template = WaitTemplateEntry.new(self, tuple, nil)
+ synchronize do
+ entry = @bag.find_all(template)
+ entry.collect do |e|
+ e.value
+ end
+ end
+ end
+
+ ##
+ # Registers for notifications of +event+. Returns a NotifyTemplateEntry.
+ # See NotifyTemplateEntry for examples of how to listen for notifications.
+ #
+ # +event+ can be:
+ # 'write':: A tuple was added
+ # 'take':: A tuple was taken or moved
+ # 'delete':: A tuple was lost after being overwritten or expiring
+ #
+ # The TupleSpace will also notify you of the 'close' event when the
+ # NotifyTemplateEntry has expired.
+
+ def notify(event, tuple, sec=nil)
+ template = NotifyTemplateEntry.new(self, event, tuple, sec)
+ synchronize do
+ @notify_waiter.push(template)
+ end
+ template
+ end
+
+ private
+
+ ##
+ # Removes dead tuples.
+
+ def keep_clean
+ synchronize do
+ @read_waiter.delete_unless_alive.each do |e|
+ e.signal
+ end
+ @take_waiter.delete_unless_alive.each do |e|
+ e.signal
+ end
+ @notify_waiter.delete_unless_alive.each do |e|
+ e.notify(['close'])
+ end
+ @bag.delete_unless_alive.each do |e|
+ notify_event('delete', e.value)
+ end
+ end
+ end
+
+ ##
+ # Notifies all registered listeners for +event+ of a status change of
+ # +tuple+.
+
+ def notify_event(event, tuple)
+ ev = [event, tuple]
+ @notify_waiter.find_all_template(ev).each do |template|
+ template.notify(ev)
+ end
+ end
+
+ ##
+ # Creates a thread that scans the tuplespace for expired tuples.
+
+ def start_keeper
+ return if @keeper && @keeper.alive?
+ @keeper = Thread.new do
+ while need_keeper?
+ keep_clean
+ sleep(@period)
+ end
+ end
+ end
+
+ ##
+ # Checks the tuplespace to see if it needs cleaning.
+
+ def need_keeper?
+ return true if @bag.has_expires?
+ return true if @read_waiter.has_expires?
+ return true if @take_waiter.has_expires?
+ return true if @notify_waiter.has_expires?
+ end
+
+ end
+
+end
+
diff --git a/ruby_1_8_5/lib/rss.rb b/ruby_1_8_5/lib/rss.rb
new file mode 100644
index 0000000000..495edb1b98
--- /dev/null
+++ b/ruby_1_8_5/lib/rss.rb
@@ -0,0 +1,16 @@
+# Copyright (c) 2003-2005 Kouhei Sutou. You can redistribute it and/or
+# modify it under the same terms as Ruby.
+#
+# Author:: Kouhei Sutou <kou@cozmixng.org>
+# Tutorial:: http://www.cozmixng.org/~rwiki/?cmd=view;name=RSS+Parser%3A%3ATutorial.en
+
+require 'rss/1.0'
+require 'rss/2.0'
+require 'rss/content'
+require 'rss/dublincore'
+require 'rss/image'
+require 'rss/syndication'
+#require 'rss/taxonomy'
+require 'rss/trackback'
+
+require "rss/maker"
diff --git a/ruby_1_8_5/lib/rss/0.9.rb b/ruby_1_8_5/lib/rss/0.9.rb
new file mode 100644
index 0000000000..eb2d828102
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/0.9.rb
@@ -0,0 +1,423 @@
+require "rss/parser"
+
+module RSS
+
+ module RSS09
+ NSPOOL = {}
+ ELEMENTS = []
+
+ def self.append_features(klass)
+ super
+
+ klass.install_must_call_validator('', "")
+ end
+ end
+
+ class Rss < Element
+
+ include RSS09
+ include RootElementMixin
+ include XMLStyleSheetMixin
+
+ %w(channel).each do |name|
+ install_have_child_element(name, "", nil)
+ end
+
+ attr_accessor :rss_version, :version, :encoding, :standalone
+
+ def initialize(rss_version, version=nil, encoding=nil, standalone=nil)
+ super
+ end
+
+ def items
+ if @channel
+ @channel.items
+ else
+ []
+ end
+ end
+
+ def image
+ if @channel
+ @channel.image
+ else
+ nil
+ end
+ end
+
+ def textinput
+ if @channel
+ @channel.textInput
+ else
+ nil
+ end
+ end
+
+ def setup_maker_elements(maker)
+ super
+ items.each do |item|
+ item.setup_maker(maker.items)
+ end
+ end
+
+ private
+ def _attrs
+ [
+ ["version", true, "rss_version"],
+ ]
+ end
+
+ class Channel < Element
+
+ include RSS09
+
+ [
+ ["title", nil, :text],
+ ["link", nil, :text],
+ ["description", nil, :text],
+ ["language", nil, :text],
+ ["copyright", "?", :text],
+ ["managingEditor", "?", :text],
+ ["webMaster", "?", :text],
+ ["rating", "?", :text],
+ ["pubDate", "?", :date, :rfc822],
+ ["lastBuildDate", "?", :date, :rfc822],
+ ["docs", "?", :text],
+ ["cloud", "?", :have_attribute],
+ ["skipDays", "?", :have_child],
+ ["skipHours", "?", :have_child],
+ ["image", nil, :have_child],
+ ["item", "*", :have_children],
+ ["textInput", "?", :have_child],
+ ].each do |name, occurs, type, *args|
+ __send__("install_#{type}_element", name, "", occurs, name, *args)
+ end
+ alias date pubDate
+ alias date= pubDate=
+
+ private
+ def maker_target(maker)
+ maker.channel
+ end
+
+ def setup_maker_elements(channel)
+ super
+ [
+ [skipDays, "day"],
+ [skipHours, "hour"],
+ ].each do |skip, key|
+ if skip
+ skip.__send__("#{key}s").each do |val|
+ target_skips = channel.__send__("skip#{key.capitalize}s")
+ new_target = target_skips.__send__("new_#{key}")
+ new_target.content = val.content
+ end
+ end
+ end
+ end
+
+ def not_need_to_call_setup_maker_variables
+ %w(image textInput)
+ end
+
+ class SkipDays < Element
+ include RSS09
+
+ [
+ ["day", "*"]
+ ].each do |name, occurs|
+ install_have_children_element(name, "", occurs)
+ end
+
+ class Day < Element
+ include RSS09
+
+ content_setup
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.content = args[0]
+ end
+ end
+
+ end
+
+ end
+
+ class SkipHours < Element
+ include RSS09
+
+ [
+ ["hour", "*"]
+ ].each do |name, occurs|
+ install_have_children_element(name, "", occurs)
+ end
+
+ class Hour < Element
+ include RSS09
+
+ content_setup(:integer)
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.content = args[0]
+ end
+ end
+ end
+
+ end
+
+ class Image < Element
+
+ include RSS09
+
+ %w(url title link).each do |name|
+ install_text_element(name, "", nil)
+ end
+ [
+ ["width", :integer],
+ ["height", :integer],
+ ["description"],
+ ].each do |name, type|
+ install_text_element(name, "", "?", name, type)
+ end
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.url = args[0]
+ self.title = args[1]
+ self.link = args[2]
+ self.width = args[3]
+ self.height = args[4]
+ self.description = args[5]
+ end
+ end
+
+ private
+ def maker_target(maker)
+ maker.image
+ end
+ end
+
+ class Cloud < Element
+
+ include RSS09
+
+ [
+ ["domain", "", true],
+ ["port", "", true, :integer],
+ ["path", "", true],
+ ["registerProcedure", "", true],
+ ["protocol", "", true],
+ ].each do |name, uri, required, type|
+ install_get_attribute(name, uri, required, type)
+ end
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.domain = args[0]
+ self.port = args[1]
+ self.path = args[2]
+ self.registerProcedure = args[3]
+ self.protocol = args[4]
+ end
+ end
+ end
+
+ class Item < Element
+
+ include RSS09
+
+ [
+ ["title", '?', :text],
+ ["link", '?', :text],
+ ["description", '?', :text],
+ ["category", '*', :have_children, "categories"],
+ ["source", '?', :have_child],
+ ["enclosure", '?', :have_child],
+ ].each do |tag, occurs, type, *args|
+ __send__("install_#{type}_element", tag, "", occurs, tag, *args)
+ end
+
+ private
+ def maker_target(items)
+ if items.respond_to?("items")
+ # For backward compatibility
+ items = items.items
+ end
+ items.new_item
+ end
+
+ def setup_maker_element(item)
+ super
+ @enclosure.setup_maker(item) if @enclosure
+ @source.setup_maker(item) if @source
+ end
+
+ class Source < Element
+
+ include RSS09
+
+ [
+ ["url", "", true]
+ ].each do |name, uri, required|
+ install_get_attribute(name, uri, required)
+ end
+
+ content_setup
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.url = args[0]
+ self.content = args[1]
+ end
+ end
+
+ private
+ def maker_target(item)
+ item.source
+ end
+
+ def setup_maker_attributes(source)
+ source.url = url
+ source.content = content
+ end
+ end
+
+ class Enclosure < Element
+
+ include RSS09
+
+ [
+ ["url", "", true],
+ ["length", "", true, :integer],
+ ["type", "", true],
+ ].each do |name, uri, required, type|
+ install_get_attribute(name, uri, required, type)
+ end
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.url = args[0]
+ self.length = args[1]
+ self.type = args[2]
+ end
+ end
+
+ private
+ def maker_target(item)
+ item.enclosure
+ end
+
+ def setup_maker_attributes(enclosure)
+ enclosure.url = url
+ enclosure.length = length
+ enclosure.type = type
+ end
+ end
+
+ class Category < Element
+
+ include RSS09
+
+ [
+ ["domain", "", false]
+ ].each do |name, uri, required|
+ install_get_attribute(name, uri, required)
+ end
+
+ content_setup
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.domain = args[0]
+ self.content = args[1]
+ end
+ end
+
+ private
+ def maker_target(item)
+ item.new_category
+ end
+
+ def setup_maker_attributes(category)
+ category.domain = domain
+ category.content = content
+ end
+
+ end
+
+ end
+
+ class TextInput < Element
+
+ include RSS09
+
+ %w(title description name link).each do |name|
+ install_text_element(name, "", nil)
+ end
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.title = args[0]
+ self.description = args[1]
+ self.name = args[2]
+ self.link = args[3]
+ end
+ end
+
+ private
+ def maker_target(maker)
+ maker.textinput
+ end
+ end
+
+ end
+
+ end
+
+ RSS09::ELEMENTS.each do |name|
+ BaseListener.install_get_text_element("", name, "#{name}=")
+ end
+
+ module ListenerMixin
+ private
+ def start_rss(tag_name, prefix, attrs, ns)
+ check_ns(tag_name, prefix, ns, "")
+
+ @rss = Rss.new(attrs['version'], @version, @encoding, @standalone)
+ @rss.do_validate = @do_validate
+ @rss.xml_stylesheets = @xml_stylesheets
+ @last_element = @rss
+ @proc_stack.push Proc.new { |text, tags|
+ @rss.validate_for_stream(tags, @ignore_unknown_element) if @do_validate
+ }
+ end
+
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rss/1.0.rb b/ruby_1_8_5/lib/rss/1.0.rb
new file mode 100644
index 0000000000..a945434fbf
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/1.0.rb
@@ -0,0 +1,451 @@
+require "rss/parser"
+
+module RSS
+
+ module RSS10
+ NSPOOL = {}
+ ELEMENTS = []
+
+ def self.append_features(klass)
+ super
+
+ klass.install_must_call_validator('', ::RSS::URI)
+ end
+
+ end
+
+ class RDF < Element
+
+ include RSS10
+ include RootElementMixin
+
+ class << self
+
+ def required_uri
+ URI
+ end
+
+ end
+
+ @tag_name = 'RDF'
+
+ PREFIX = 'rdf'
+ URI = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+
+ install_ns('', ::RSS::URI)
+ install_ns(PREFIX, URI)
+
+ [
+ ["channel", nil],
+ ["image", "?"],
+ ["item", "+", :children],
+ ["textinput", "?"],
+ ].each do |tag, occurs, type|
+ type ||= :child
+ __send__("install_have_#{type}_element", tag, ::RSS::URI, occurs)
+ end
+
+ attr_accessor :rss_version, :version, :encoding, :standalone
+
+ def initialize(version=nil, encoding=nil, standalone=nil)
+ super('1.0', version, encoding, standalone)
+ end
+
+ def full_name
+ tag_name_with_prefix(PREFIX)
+ end
+
+ class Li < Element
+
+ include RSS10
+
+ class << self
+ def required_uri
+ URI
+ end
+ end
+
+ [
+ ["resource", [URI, ""], true]
+ ].each do |name, uri, required|
+ install_get_attribute(name, uri, required)
+ end
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.resource = args[0]
+ end
+ end
+
+ def full_name
+ tag_name_with_prefix(PREFIX)
+ end
+ end
+
+ class Seq < Element
+
+ include RSS10
+
+ Li = ::RSS::RDF::Li
+
+ class << self
+ def required_uri
+ URI
+ end
+ end
+
+ @tag_name = 'Seq'
+
+ install_have_children_element("li", URI, "*")
+ install_must_call_validator('rdf', ::RSS::RDF::URI)
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ @li = args[0] if args[0]
+ end
+ end
+
+ def full_name
+ tag_name_with_prefix(PREFIX)
+ end
+
+ def setup_maker(target)
+ lis.each do |li|
+ target << li.resource
+ end
+ end
+ end
+
+ class Bag < Element
+
+ include RSS10
+
+ Li = ::RSS::RDF::Li
+
+ class << self
+ def required_uri
+ URI
+ end
+ end
+
+ @tag_name = 'Bag'
+
+ install_have_children_element("li", URI, "*")
+ install_must_call_validator('rdf', URI)
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ @li = args[0] if args[0]
+ end
+ end
+
+ def full_name
+ tag_name_with_prefix(PREFIX)
+ end
+
+ def setup_maker(target)
+ lis.each do |li|
+ target << li.resource
+ end
+ end
+ end
+
+ class Channel < Element
+
+ include RSS10
+
+ class << self
+
+ def required_uri
+ ::RSS::URI
+ end
+
+ end
+
+ [
+ ["about", URI, true]
+ ].each do |name, uri, required|
+ install_get_attribute(name, uri, required, nil, nil,
+ "#{PREFIX}:#{name}")
+ end
+
+ [
+ ['title', nil, :text],
+ ['link', nil, :text],
+ ['description', nil, :text],
+ ['image', '?', :have_child],
+ ['items', nil, :have_child],
+ ['textinput', '?', :have_child],
+ ].each do |tag, occurs, type|
+ __send__("install_#{type}_element", tag, ::RSS::URI, occurs)
+ end
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.about = args[0]
+ end
+ end
+
+ private
+ def maker_target(maker)
+ maker.channel
+ end
+
+ def setup_maker_attributes(channel)
+ channel.about = about
+ end
+
+ class Image < Element
+
+ include RSS10
+
+ class << self
+
+ def required_uri
+ ::RSS::URI
+ end
+
+ end
+
+ [
+ ["resource", URI, true]
+ ].each do |name, uri, required|
+ install_get_attribute(name, uri, required, nil, nil,
+ "#{PREFIX}:#{name}")
+ end
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.resource = args[0]
+ end
+ end
+ end
+
+ class Textinput < Element
+
+ include RSS10
+
+ class << self
+
+ def required_uri
+ ::RSS::URI
+ end
+
+ end
+
+ [
+ ["resource", URI, true]
+ ].each do |name, uri, required|
+ install_get_attribute(name, uri, required, nil, nil,
+ "#{PREFIX}:#{name}")
+ end
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.resource = args[0]
+ end
+ end
+ end
+
+ class Items < Element
+
+ include RSS10
+
+ Seq = ::RSS::RDF::Seq
+
+ class << self
+
+ def required_uri
+ ::RSS::URI
+ end
+
+ end
+
+ install_have_child_element("Seq", URI, nil)
+ install_must_call_validator('rdf', URI)
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.Seq = args[0]
+ end
+ self.Seq ||= Seq.new
+ end
+
+ def resources
+ if @Seq
+ @Seq.lis.collect do |li|
+ li.resource
+ end
+ else
+ []
+ end
+ end
+ end
+ end
+
+ class Image < Element
+
+ include RSS10
+
+ class << self
+
+ def required_uri
+ ::RSS::URI
+ end
+
+ end
+
+ [
+ ["about", URI, true]
+ ].each do |name, uri, required|
+ install_get_attribute(name, uri, required, nil, nil,
+ "#{PREFIX}:#{name}")
+ end
+
+ %w(title url link).each do |name|
+ install_text_element(name, ::RSS::URI, nil)
+ end
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.about = args[0]
+ end
+ end
+
+ private
+ def maker_target(maker)
+ maker.image
+ end
+ end
+
+ class Item < Element
+
+ include RSS10
+
+ class << self
+
+ def required_uri
+ ::RSS::URI
+ end
+
+ end
+
+
+ [
+ ["about", URI, true]
+ ].each do |name, uri, required|
+ install_get_attribute(name, uri, required, nil, nil,
+ "#{PREFIX}:#{name}")
+ end
+
+ [
+ ["title", nil],
+ ["link", nil],
+ ["description", "?"],
+ ].each do |tag, occurs|
+ install_text_element(tag, ::RSS::URI, occurs)
+ end
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.about = args[0]
+ end
+ end
+
+ private
+ def maker_target(items)
+ if items.respond_to?("items")
+ # For backward compatibility
+ items = items.items
+ end
+ items.new_item
+ end
+ end
+
+ class Textinput < Element
+
+ include RSS10
+
+ class << self
+
+ def required_uri
+ ::RSS::URI
+ end
+
+ end
+
+ [
+ ["about", URI, true]
+ ].each do |name, uri, required|
+ install_get_attribute(name, uri, required, nil, nil,
+ "#{PREFIX}:#{name}")
+ end
+
+ %w(title description name link).each do |name|
+ install_text_element(name, ::RSS::URI, nil)
+ end
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.about = args[0]
+ end
+ end
+
+ private
+ def maker_target(maker)
+ maker.textinput
+ end
+ end
+
+ end
+
+ RSS10::ELEMENTS.each do |name|
+ BaseListener.install_get_text_element(URI, name, "#{name}=")
+ end
+
+ module ListenerMixin
+ private
+ def start_RDF(tag_name, prefix, attrs, ns)
+ check_ns(tag_name, prefix, ns, RDF::URI)
+
+ @rss = RDF.new(@version, @encoding, @standalone)
+ @rss.do_validate = @do_validate
+ @rss.xml_stylesheets = @xml_stylesheets
+ @last_element = @rss
+ @proc_stack.push Proc.new { |text, tags|
+ @rss.validate_for_stream(tags, @ignore_unknown_element) if @do_validate
+ }
+ end
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rss/2.0.rb b/ruby_1_8_5/lib/rss/2.0.rb
new file mode 100644
index 0000000000..44bdb4f1ae
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/2.0.rb
@@ -0,0 +1,111 @@
+require "rss/0.9"
+
+module RSS
+
+ class Rss
+
+ class Channel
+
+ [
+ ["generator"],
+ ["ttl", :integer],
+ ].each do |name, type|
+ install_text_element(name, "", "?", name, type)
+ end
+
+ [
+ %w(category categories),
+ ].each do |name, plural_name|
+ install_have_children_element(name, "", "*", name, plural_name)
+ end
+
+ [
+ ["image", "?"],
+ ["language", "?"],
+ ].each do |name, occurs|
+ install_model(name, "", occurs)
+ end
+
+ Category = Item::Category
+
+ class Item
+
+ [
+ ["comments", "?"],
+ ["author", "?"],
+ ].each do |name, occurs|
+ install_text_element(name, "", occurs)
+ end
+
+ [
+ ["pubDate", '?'],
+ ].each do |name, occurs|
+ install_date_element(name, "", occurs, name, 'rfc822')
+ end
+ alias date pubDate
+ alias date= pubDate=
+
+ [
+ ["guid", '?'],
+ ].each do |name, occurs|
+ install_have_child_element(name, "", occurs)
+ end
+
+ private
+ alias _setup_maker_element setup_maker_element
+ def setup_maker_element(item)
+ _setup_maker_element(item)
+ @guid.setup_maker(item) if @guid
+ end
+
+ class Guid < Element
+
+ include RSS09
+
+ [
+ ["isPermaLink", "", false, :boolean]
+ ].each do |name, uri, required, type|
+ install_get_attribute(name, uri, required, type)
+ end
+
+ content_setup
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.isPermaLink = args[0]
+ self.content = args[1]
+ end
+ end
+
+ alias_method :_PermaLink?, :PermaLink?
+ private :_PermaLink?
+ def PermaLink?
+ perma = _PermaLink?
+ perma or perma.nil?
+ end
+
+ private
+ def maker_target(item)
+ item.guid
+ end
+
+ def setup_maker_attributes(guid)
+ guid.isPermaLink = isPermaLink
+ guid.content = content
+ end
+ end
+
+ end
+
+ end
+
+ end
+
+ RSS09::ELEMENTS.each do |name|
+ BaseListener.install_get_text_element("", name, "#{name}=")
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rss/content.rb b/ruby_1_8_5/lib/rss/content.rb
new file mode 100644
index 0000000000..1b13f39fcf
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/content.rb
@@ -0,0 +1,38 @@
+require "rss/1.0"
+
+module RSS
+
+ CONTENT_PREFIX = 'content'
+ CONTENT_URI = "http://purl.org/rss/1.0/modules/content/"
+
+ RDF.install_ns(CONTENT_PREFIX, CONTENT_URI)
+
+ module ContentModel
+
+ extend BaseModel
+
+ ELEMENTS = []
+
+ def self.append_features(klass)
+ super
+
+ klass.install_must_call_validator(CONTENT_PREFIX, CONTENT_URI)
+ %w(encoded).each do |name|
+ klass.install_text_element(name, CONTENT_URI, "?",
+ "#{CONTENT_PREFIX}_#{name}")
+ end
+ end
+ end
+
+ class RDF
+ class Item; include ContentModel; end
+ end
+
+ prefix_size = CONTENT_PREFIX.size + 1
+ ContentModel::ELEMENTS.uniq!
+ ContentModel::ELEMENTS.each do |full_name|
+ name = full_name[prefix_size..-1]
+ BaseListener.install_get_text_element(CONTENT_URI, name, "#{full_name}=")
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rss/converter.rb b/ruby_1_8_5/lib/rss/converter.rb
new file mode 100644
index 0000000000..d928c48223
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/converter.rb
@@ -0,0 +1,158 @@
+require "rss/utils"
+
+module RSS
+
+ class Converter
+
+ include Utils
+
+ def initialize(to_enc, from_enc=nil)
+ normalized_to_enc = to_enc.downcase.gsub(/-/, '_')
+ from_enc ||= 'utf-8'
+ normalized_from_enc = from_enc.downcase.gsub(/-/, '_')
+ if normalized_to_enc == normalized_from_enc
+ def_same_enc()
+ else
+ def_diff_enc = "def_to_#{normalized_to_enc}_from_#{normalized_from_enc}"
+ if respond_to?(def_diff_enc)
+ __send__(def_diff_enc)
+ else
+ def_else_enc(to_enc, from_enc)
+ end
+ end
+ end
+
+ def convert(value)
+ value
+ end
+
+ def def_convert(depth=0)
+ instance_eval(<<-EOC, *get_file_and_line_from_caller(depth))
+ def convert(value)
+ if value.kind_of?(String)
+ #{yield('value')}
+ else
+ value
+ end
+ end
+ EOC
+ end
+
+ def def_iconv_convert(to_enc, from_enc, depth=0)
+ begin
+ require "iconv"
+ @iconv = Iconv.new(to_enc, from_enc)
+ def_convert(depth+1) do |value|
+ <<-EOC
+ begin
+ @iconv.iconv(#{value})
+ rescue Iconv::Failure
+ raise ConversionError.new(#{value}, "#{to_enc}", "#{from_enc}")
+ end
+ EOC
+ end
+ rescue LoadError, ArgumentError, SystemCallError
+ raise UnknownConversionMethodError.new(to_enc, from_enc)
+ end
+ end
+
+ def def_else_enc(to_enc, from_enc)
+ def_iconv_convert(to_enc, from_enc, 0)
+ end
+
+ def def_same_enc()
+ def_convert do |value|
+ value
+ end
+ end
+
+ def def_uconv_convert_if_can(meth, to_enc, from_enc, nkf_arg)
+ begin
+ require "uconv"
+ def_convert(1) do |value|
+ <<-EOC
+ begin
+ Uconv.#{meth}(#{value})
+ rescue Uconv::Error
+ raise ConversionError.new(#{value}, "#{to_enc}", "#{from_enc}")
+ end
+ EOC
+ end
+ rescue LoadError
+ require 'nkf'
+ def_convert(1) do |value|
+ "NKF.nkf(#{nkf_arg.dump}, #{value})"
+ end
+ end
+ end
+
+ def def_to_euc_jp_from_utf_8
+ def_uconv_convert_if_can('u8toeuc', 'EUC-JP', 'UTF-8', '-We')
+ end
+
+ def def_to_utf_8_from_euc_jp
+ def_uconv_convert_if_can('euctou8', 'UTF-8', 'EUC-JP', '-Ew')
+ end
+
+ def def_to_shift_jis_from_utf_8
+ def_uconv_convert_if_can('u8tosjis', 'Shift_JIS', 'UTF-8', '-Ws')
+ end
+
+ def def_to_utf_8_from_shift_jis
+ def_uconv_convert_if_can('sjistou8', 'UTF-8', 'Shift_JIS', '-Sw')
+ end
+
+ def def_to_euc_jp_from_shift_jis
+ require "nkf"
+ def_convert do |value|
+ "NKF.nkf('-Se', #{value})"
+ end
+ end
+
+ def def_to_shift_jis_from_euc_jp
+ require "nkf"
+ def_convert do |value|
+ "NKF.nkf('-Es', #{value})"
+ end
+ end
+
+ def def_to_euc_jp_from_iso_2022_jp
+ require "nkf"
+ def_convert do |value|
+ "NKF.nkf('-Je', #{value})"
+ end
+ end
+
+ def def_to_iso_2022_jp_from_euc_jp
+ require "nkf"
+ def_convert do |value|
+ "NKF.nkf('-Ej', #{value})"
+ end
+ end
+
+ def def_to_utf_8_from_iso_8859_1
+ def_convert do |value|
+ "#{value}.unpack('C*').pack('U*')"
+ end
+ end
+
+ def def_to_iso_8859_1_from_utf_8
+ def_convert do |value|
+ <<-EOC
+ array_utf8 = #{value}.unpack('U*')
+ array_enc = []
+ array_utf8.each do |num|
+ if num <= 0xFF
+ array_enc << num
+ else
+ array_enc.concat "&\#\#{num};".unpack('C*')
+ end
+ end
+ array_enc.pack('C*')
+ EOC
+ end
+ end
+
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rss/dublincore.rb b/ruby_1_8_5/lib/rss/dublincore.rb
new file mode 100644
index 0000000000..8a4afd4dd9
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/dublincore.rb
@@ -0,0 +1,154 @@
+require "rss/1.0"
+
+module RSS
+
+ DC_PREFIX = 'dc'
+ DC_URI = "http://purl.org/dc/elements/1.1/"
+
+ RDF.install_ns(DC_PREFIX, DC_URI)
+
+ module BaseDublinCoreModel
+ def append_features(klass)
+ super
+
+ return if klass.instance_of?(Module)
+ DublinCoreModel::ELEMENT_NAME_INFOS.each do |name, plural_name|
+ plural = plural_name || "#{name}s"
+ full_name = "#{DC_PREFIX}_#{name}"
+ full_plural_name = "#{DC_PREFIX}_#{plural}"
+ klass_name = "DublinCore#{Utils.to_class_name(name)}"
+ klass.install_must_call_validator(DC_PREFIX, DC_URI)
+ klass.install_have_children_element(name, DC_URI, "*",
+ full_name, full_plural_name)
+ klass.module_eval(<<-EOC, *get_file_and_line_from_caller(0))
+ remove_method :#{full_name}
+ remove_method :#{full_name}=
+ remove_method :set_#{full_name}
+
+ def #{full_name}
+ @#{full_name}.first and @#{full_name}.first.value
+ end
+
+ def #{full_name}=(new_value)
+ @#{full_name}[0] = Utils.new_with_value_if_need(#{klass_name}, new_value)
+ end
+ alias set_#{full_name} #{full_name}=
+ EOC
+ end
+ klass.module_eval(<<-EOC, *get_file_and_line_from_caller(0))
+ alias date #{DC_PREFIX}_date
+ alias date= #{DC_PREFIX}_date=
+ EOC
+ end
+ end
+
+ module DublinCoreModel
+
+ extend BaseModel
+ extend BaseDublinCoreModel
+
+ TEXT_ELEMENTS = {
+ "title" => nil,
+ "description" => nil,
+ "creator" => nil,
+ "subject" => nil,
+ "publisher" => nil,
+ "contributor" => nil,
+ "type" => nil,
+ "format" => nil,
+ "identifier" => nil,
+ "source" => nil,
+ "language" => nil,
+ "relation" => nil,
+ "coverage" => nil,
+ "rights" => "rightses" # FIXME
+ }
+
+ DATE_ELEMENTS = {
+ "date" => "w3cdtf",
+ }
+
+ ELEMENT_NAME_INFOS = DublinCoreModel::TEXT_ELEMENTS.to_a
+ DublinCoreModel::DATE_ELEMENTS.each do |name, |
+ ELEMENT_NAME_INFOS << [name, nil]
+ end
+
+ ELEMENTS = TEXT_ELEMENTS.keys + DATE_ELEMENTS.keys
+
+ ELEMENTS.each do |name, plural_name|
+ module_eval(<<-EOC, *get_file_and_line_from_caller(0))
+ class DublinCore#{Utils.to_class_name(name)} < Element
+ include RSS10
+
+ content_setup
+
+ class << self
+ def required_prefix
+ DC_PREFIX
+ end
+
+ def required_uri
+ DC_URI
+ end
+ end
+
+ @tag_name = #{name.dump}
+
+ alias_method(:value, :content)
+ alias_method(:value=, :content=)
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.content = args[0]
+ end
+ end
+
+ def full_name
+ tag_name_with_prefix(DC_PREFIX)
+ end
+
+ def maker_target(target)
+ target.new_#{name}
+ end
+
+ def setup_maker_attributes(#{name})
+ #{name}.content = content
+ end
+ end
+ EOC
+ end
+
+ DATE_ELEMENTS.each do |name, type|
+ module_eval(<<-EOC, *get_file_and_line_from_caller(0))
+ class DublinCore#{Utils.to_class_name(name)} < Element
+ remove_method(:content=)
+ remove_method(:value=)
+
+ date_writer("content", #{type.dump}, #{name.dump})
+
+ alias_method(:value=, :content=)
+ end
+ EOC
+ end
+ end
+
+ # For backward compatibility
+ DublincoreModel = DublinCoreModel
+
+ class RDF
+ class Channel; include DublinCoreModel; end
+ class Image; include DublinCoreModel; end
+ class Item; include DublinCoreModel; end
+ class Textinput; include DublinCoreModel; end
+ end
+
+ DublinCoreModel::ELEMENTS.each do |name|
+ class_name = Utils.to_class_name(name)
+ BaseListener.install_class_name(DC_URI, name, "DublinCore#{class_name}")
+ end
+
+ DublinCoreModel::ELEMENTS.collect! {|name| "#{DC_PREFIX}_#{name}"}
+end
diff --git a/ruby_1_8_5/lib/rss/image.rb b/ruby_1_8_5/lib/rss/image.rb
new file mode 100644
index 0000000000..a9e9e9094e
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/image.rb
@@ -0,0 +1,193 @@
+require 'rss/1.0'
+require 'rss/dublincore'
+
+module RSS
+
+ IMAGE_PREFIX = 'image'
+ IMAGE_URI = 'http://web.resource.org/rss/1.0/modules/image/'
+
+ RDF.install_ns(IMAGE_PREFIX, IMAGE_URI)
+
+ IMAGE_ELEMENTS = []
+
+ %w(item favicon).each do |name|
+ class_name = Utils.to_class_name(name)
+ BaseListener.install_class_name(IMAGE_URI, name, "Image#{class_name}")
+ IMAGE_ELEMENTS << "#{IMAGE_PREFIX}_#{name}"
+ end
+
+ module ImageModelUtils
+ def validate_one_tag_name(ignore_unknown_element, name, tags)
+ if !ignore_unknown_element
+ invalid = tags.find {|tag| tag != name}
+ raise UnknownTagError.new(invalid, IMAGE_URI) if invalid
+ end
+ raise TooMuchTagError.new(name, tag_name) if tags.size > 1
+ end
+ end
+
+ module ImageItemModel
+ include ImageModelUtils
+ extend BaseModel
+
+ def self.append_features(klass)
+ super
+
+ klass.install_have_child_element("item", IMAGE_URI, "?",
+ "#{IMAGE_PREFIX}_item")
+ klass.install_must_call_validator(IMAGE_PREFIX, IMAGE_URI)
+ end
+
+ class ImageItem < Element
+ include RSS10
+ include DublinCoreModel
+
+ @tag_name = "item"
+
+ class << self
+ def required_prefix
+ IMAGE_PREFIX
+ end
+
+ def required_uri
+ IMAGE_URI
+ end
+ end
+
+ install_must_call_validator(IMAGE_PREFIX, IMAGE_URI)
+
+ [
+ ["about", ::RSS::RDF::URI, true],
+ ["resource", ::RSS::RDF::URI, false],
+ ].each do |name, uri, required|
+ install_get_attribute(name, uri, required, nil, nil,
+ "#{::RSS::RDF::PREFIX}:#{name}")
+ end
+
+ %w(width height).each do |tag|
+ full_name = "#{IMAGE_PREFIX}_#{tag}"
+ disp_name = "#{IMAGE_PREFIX}:#{tag}"
+ install_text_element(tag, IMAGE_URI, "?",
+ full_name, :integer, disp_name)
+ BaseListener.install_get_text_element(IMAGE_URI, tag, "#{full_name}=")
+ end
+
+ alias width= image_width=
+ alias width image_width
+ alias height= image_height=
+ alias height image_height
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.about = args[0]
+ self.resource = args[1]
+ end
+ end
+
+ def full_name
+ tag_name_with_prefix(IMAGE_PREFIX)
+ end
+
+ private
+ def maker_target(target)
+ target.image_item
+ end
+
+ def setup_maker_attributes(item)
+ item.about = self.about
+ item.resource = self.resource
+ end
+ end
+ end
+
+ module ImageFaviconModel
+ include ImageModelUtils
+ extend BaseModel
+
+ def self.append_features(klass)
+ super
+
+ unless klass.class == Module
+ klass.install_have_child_element("favicon", IMAGE_URI, "?",
+ "#{IMAGE_PREFIX}_favicon")
+ klass.install_must_call_validator(IMAGE_PREFIX, IMAGE_URI)
+ end
+ end
+
+ class ImageFavicon < Element
+ include RSS10
+ include DublinCoreModel
+
+ @tag_name = "favicon"
+
+ class << self
+ def required_prefix
+ IMAGE_PREFIX
+ end
+
+ def required_uri
+ IMAGE_URI
+ end
+ end
+
+ [
+ ["about", ::RSS::RDF::URI, true, ::RSS::RDF::PREFIX],
+ ["size", IMAGE_URI, true, IMAGE_PREFIX],
+ ].each do |name, uri, required, prefix|
+ install_get_attribute(name, uri, required, nil, nil,
+ "#{prefix}:#{name}")
+ end
+
+ AVAILABLE_SIZES = %w(small medium large)
+ alias_method :_size=, :size=
+ private :_size=
+ def size=(new_value)
+ if @do_validate and !new_value.nil?
+ new_value = new_value.strip
+ unless AVAILABLE_SIZES.include?(new_value)
+ attr_name = "#{IMAGE_PREFIX}:size"
+ raise NotAvailableValueError.new(full_name, new_value, attr_name)
+ end
+ end
+ __send__(:_size=, new_value)
+ end
+
+ alias image_size= size=
+ alias image_size size
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.about = args[0]
+ self.size = args[1]
+ end
+ end
+
+ def full_name
+ tag_name_with_prefix(IMAGE_PREFIX)
+ end
+
+ private
+ def maker_target(target)
+ target.image_favicon
+ end
+
+ def setup_maker_attributes(favicon)
+ favicon.about = self.about
+ favicon.size = self.size
+ end
+ end
+
+ end
+
+ class RDF
+ class Channel; include ImageFaviconModel; end
+ class Item; include ImageItemModel; end
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rss/maker.rb b/ruby_1_8_5/lib/rss/maker.rb
new file mode 100644
index 0000000000..9ed799ac7f
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/maker.rb
@@ -0,0 +1,37 @@
+require "rss/rss"
+
+module RSS
+
+ module Maker
+
+ MAKERS = {}
+
+ class << self
+ def make(version, &block)
+ maker(version).make(&block)
+ end
+
+ def maker(version)
+ MAKERS[version]
+ end
+
+ def add_maker(version, maker)
+ MAKERS[version] = maker
+ end
+
+ def filename_to_version(filename)
+ File.basename(filename, ".*")
+ end
+ end
+ end
+
+end
+
+require "rss/maker/1.0"
+require "rss/maker/2.0"
+require "rss/maker/content"
+require "rss/maker/dublincore"
+require "rss/maker/syndication"
+require "rss/maker/taxonomy"
+require "rss/maker/trackback"
+require "rss/maker/image"
diff --git a/ruby_1_8_5/lib/rss/maker/0.9.rb b/ruby_1_8_5/lib/rss/maker/0.9.rb
new file mode 100644
index 0000000000..b82585fb96
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/maker/0.9.rb
@@ -0,0 +1,224 @@
+require "rss/0.9"
+
+require "rss/maker/base"
+
+module RSS
+ module Maker
+
+ class RSS09 < RSSBase
+
+ def initialize(rss_version="0.91")
+ super
+ end
+
+ private
+ def make_rss
+ Rss.new(@rss_version, @version, @encoding, @standalone)
+ end
+
+ def setup_elements(rss)
+ setup_channel(rss)
+ end
+
+ class Channel < ChannelBase
+
+ def to_rss(rss)
+ channel = Rss::Channel.new
+ set = setup_values(channel)
+ if set
+ rss.channel = channel
+ setup_items(rss)
+ setup_image(rss)
+ setup_textinput(rss)
+ setup_other_elements(rss)
+ if rss.channel.image
+ rss
+ else
+ nil
+ end
+ elsif variable_is_set?
+ raise NotSetError.new("maker.channel", not_set_required_variables)
+ end
+ end
+
+ def have_required_values?
+ @title and @link and @description and @language
+ end
+
+ private
+ def setup_items(rss)
+ @maker.items.to_rss(rss)
+ end
+
+ def setup_image(rss)
+ @maker.image.to_rss(rss)
+ end
+
+ def setup_textinput(rss)
+ @maker.textinput.to_rss(rss)
+ end
+
+ def variables
+ super + ["pubDate"]
+ end
+
+ def required_variable_names
+ %w(title link description language)
+ end
+
+ class SkipDays < SkipDaysBase
+ def to_rss(rss, channel)
+ unless @days.empty?
+ skipDays = Rss::Channel::SkipDays.new
+ channel.skipDays = skipDays
+ @days.each do |day|
+ day.to_rss(rss, skipDays.days)
+ end
+ end
+ end
+
+ class Day < DayBase
+ def to_rss(rss, days)
+ day = Rss::Channel::SkipDays::Day.new
+ set = setup_values(day)
+ if set
+ days << day
+ setup_other_elements(rss)
+ end
+ end
+
+ def have_required_values?
+ @content
+ end
+ end
+ end
+
+ class SkipHours < SkipHoursBase
+ def to_rss(rss, channel)
+ unless @hours.empty?
+ skipHours = Rss::Channel::SkipHours.new
+ channel.skipHours = skipHours
+ @hours.each do |hour|
+ hour.to_rss(rss, skipHours.hours)
+ end
+ end
+ end
+
+ class Hour < HourBase
+ def to_rss(rss, hours)
+ hour = Rss::Channel::SkipHours::Hour.new
+ set = setup_values(hour)
+ if set
+ hours << hour
+ setup_other_elements(rss)
+ end
+ end
+
+ def have_required_values?
+ @content
+ end
+ end
+ end
+
+ class Cloud < CloudBase
+ def to_rss(*args)
+ end
+ end
+
+ class Categories < CategoriesBase
+ def to_rss(*args)
+ end
+
+ class Category < CategoryBase
+ end
+ end
+ end
+
+ class Image < ImageBase
+ def to_rss(rss)
+ image = Rss::Channel::Image.new
+ set = setup_values(image)
+ if set
+ image.link = link
+ rss.channel.image = image
+ setup_other_elements(rss)
+ end
+ end
+
+ def have_required_values?
+ @url and @title and link
+ end
+ end
+
+ class Items < ItemsBase
+ def to_rss(rss)
+ if rss.channel
+ normalize.each do |item|
+ item.to_rss(rss)
+ end
+ setup_other_elements(rss)
+ end
+ end
+
+ class Item < ItemBase
+ def to_rss(rss)
+ item = Rss::Channel::Item.new
+ set = setup_values(item)
+ if set
+ rss.items << item
+ setup_other_elements(rss)
+ end
+ end
+
+ private
+ def have_required_values?
+ @title and @link
+ end
+
+ class Guid < GuidBase
+ def to_rss(*args)
+ end
+ end
+
+ class Enclosure < EnclosureBase
+ def to_rss(*args)
+ end
+ end
+
+ class Source < SourceBase
+ def to_rss(*args)
+ end
+ end
+
+ class Categories < CategoriesBase
+ def to_rss(*args)
+ end
+
+ class Category < CategoryBase
+ end
+ end
+
+ end
+ end
+
+ class Textinput < TextinputBase
+ def to_rss(rss)
+ textInput = Rss::Channel::TextInput.new
+ set = setup_values(textInput)
+ if set
+ rss.channel.textInput = textInput
+ setup_other_elements(rss)
+ end
+ end
+
+ private
+ def have_required_values?
+ @title and @description and @name and @link
+ end
+ end
+ end
+
+ add_maker(filename_to_version(__FILE__), RSS09)
+ add_maker(filename_to_version(__FILE__) + "1", RSS09)
+ end
+end
diff --git a/ruby_1_8_5/lib/rss/maker/1.0.rb b/ruby_1_8_5/lib/rss/maker/1.0.rb
new file mode 100644
index 0000000000..3e6542a007
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/maker/1.0.rb
@@ -0,0 +1,204 @@
+require "rss/1.0"
+
+require "rss/maker/base"
+
+module RSS
+ module Maker
+
+ class RSS10 < RSSBase
+
+ def initialize
+ super("1.0")
+ end
+
+ private
+ def make_rss
+ RDF.new(@version, @encoding, @standalone)
+ end
+
+ def setup_elements(rss)
+ setup_channel(rss)
+ setup_image(rss)
+ setup_items(rss)
+ setup_textinput(rss)
+ end
+
+ class Channel < ChannelBase
+
+ def to_rss(rss)
+ set = false
+ if @about
+ channel = RDF::Channel.new(@about)
+ set = setup_values(channel)
+ if set
+ channel.dc_dates.clear
+ rss.channel = channel
+ setup_items(rss)
+ setup_image(rss)
+ setup_textinput(rss)
+ setup_other_elements(rss)
+ end
+ end
+
+ if (!@about or !set) and variable_is_set?
+ raise NotSetError.new("maker.channel", not_set_required_variables)
+ end
+ end
+
+ def have_required_values?
+ @about and @title and @link and @description
+ end
+
+ private
+ def setup_items(rss)
+ items = RDF::Channel::Items.new
+ seq = items.Seq
+ @maker.items.normalize.each do |item|
+ seq.lis << RDF::Channel::Items::Seq::Li.new(item.link)
+ end
+ rss.channel.items = items
+ end
+
+ def setup_image(rss)
+ if @maker.image.have_required_values?
+ rss.channel.image = RDF::Channel::Image.new(@maker.image.url)
+ end
+ end
+
+ def setup_textinput(rss)
+ if @maker.textinput.have_required_values?
+ textinput = RDF::Channel::Textinput.new(@maker.textinput.link)
+ rss.channel.textinput = textinput
+ end
+ end
+
+ def required_variable_names
+ %w(about title link description)
+ end
+
+ class SkipDays < SkipDaysBase
+ def to_rss(*args)
+ end
+
+ class Day < DayBase
+ end
+ end
+
+ class SkipHours < SkipHoursBase
+ def to_rss(*args)
+ end
+
+ class Hour < HourBase
+ end
+ end
+
+ class Cloud < CloudBase
+ def to_rss(*args)
+ end
+ end
+
+ class Categories < CategoriesBase
+ def to_rss(*args)
+ end
+
+ class Category < CategoryBase
+ end
+ end
+ end
+
+ class Image < ImageBase
+ def to_rss(rss)
+ if @url
+ image = RDF::Image.new(@url)
+ set = setup_values(image)
+ if set
+ rss.image = image
+ setup_other_elements(rss)
+ end
+ end
+ end
+
+ def have_required_values?
+ @url and @title and link and @maker.channel.have_required_values?
+ end
+
+ private
+ def variables
+ super + ["link"]
+ end
+ end
+
+ class Items < ItemsBase
+ def to_rss(rss)
+ if rss.channel
+ normalize.each do |item|
+ item.to_rss(rss)
+ end
+ setup_other_elements(rss)
+ end
+ end
+
+ class Item < ItemBase
+ def to_rss(rss)
+ if @link
+ item = RDF::Item.new(@link)
+ set = setup_values(item)
+ if set
+ item.dc_dates.clear
+ rss.items << item
+ setup_other_elements(rss)
+ end
+ end
+ end
+
+ def have_required_values?
+ @title and @link
+ end
+
+ class Guid < GuidBase
+ def to_rss(*args)
+ end
+ end
+
+ class Enclosure < EnclosureBase
+ def to_rss(*args)
+ end
+ end
+
+ class Source < SourceBase
+ def to_rss(*args)
+ end
+ end
+
+ class Categories < CategoriesBase
+ def to_rss(*args)
+ end
+
+ class Category < CategoryBase
+ end
+ end
+ end
+ end
+
+ class Textinput < TextinputBase
+ def to_rss(rss)
+ if @link
+ textinput = RDF::Textinput.new(@link)
+ set = setup_values(textinput)
+ if set
+ rss.textinput = textinput
+ setup_other_elements(rss)
+ end
+ end
+ end
+
+ def have_required_values?
+ @title and @description and @name and @link and
+ @maker.channel.have_required_values?
+ end
+ end
+ end
+
+ add_maker(filename_to_version(__FILE__), RSS10)
+ end
+end
diff --git a/ruby_1_8_5/lib/rss/maker/2.0.rb b/ruby_1_8_5/lib/rss/maker/2.0.rb
new file mode 100644
index 0000000000..a958661614
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/maker/2.0.rb
@@ -0,0 +1,168 @@
+require "rss/2.0"
+
+require "rss/maker/0.9"
+
+module RSS
+ module Maker
+
+ class RSS20 < RSS09
+
+ def initialize(rss_version="2.0")
+ super
+ end
+
+ class Channel < RSS09::Channel
+
+ def have_required_values?
+ @title and @link and @description
+ end
+
+ def required_variable_names
+ %w(title link description)
+ end
+
+ class SkipDays < RSS09::Channel::SkipDays
+ class Day < RSS09::Channel::SkipDays::Day
+ end
+ end
+
+ class SkipHours < RSS09::Channel::SkipHours
+ class Hour < RSS09::Channel::SkipHours::Hour
+ end
+ end
+
+ class Cloud < RSS09::Channel::Cloud
+ def to_rss(rss, channel)
+ cloud = Rss::Channel::Cloud.new
+ set = setup_values(cloud)
+ if set
+ channel.cloud = cloud
+ setup_other_elements(rss)
+ end
+ end
+
+ def have_required_values?
+ @domain and @port and @path and
+ @registerProcedure and @protocol
+ end
+ end
+
+ class Categories < RSS09::Channel::Categories
+ def to_rss(rss, channel)
+ @categories.each do |category|
+ category.to_rss(rss, channel)
+ end
+ end
+
+ class Category < RSS09::Channel::Categories::Category
+ def to_rss(rss, channel)
+ category = Rss::Channel::Category.new
+ set = setup_values(category)
+ if set
+ channel.categories << category
+ setup_other_elements(rss)
+ end
+ end
+
+ def have_required_values?
+ @content
+ end
+ end
+ end
+
+ end
+
+ class Image < RSS09::Image
+ end
+
+ class Items < RSS09::Items
+
+ class Item < RSS09::Items::Item
+
+ def have_required_values?
+ @title or @description
+ end
+
+ private
+ def variables
+ super + ["pubDate"]
+ end
+
+ class Guid < RSS09::Items::Item::Guid
+ def to_rss(rss, item)
+ guid = Rss::Channel::Item::Guid.new
+ set = setup_values(guid)
+ if set
+ item.guid = guid
+ setup_other_elements(rss)
+ end
+ end
+
+ def have_required_values?
+ @content
+ end
+ end
+
+ class Enclosure < RSS09::Items::Item::Enclosure
+ def to_rss(rss, item)
+ enclosure = Rss::Channel::Item::Enclosure.new
+ set = setup_values(enclosure)
+ if set
+ item.enclosure = enclosure
+ setup_other_elements(rss)
+ end
+ end
+
+ def have_required_values?
+ @url and @length and @type
+ end
+ end
+
+ class Source < RSS09::Items::Item::Source
+ def to_rss(rss, item)
+ source = Rss::Channel::Item::Source.new
+ set = setup_values(source)
+ if set
+ item.source = source
+ setup_other_elements(rss)
+ end
+ end
+
+ def have_required_values?
+ @url and @content
+ end
+ end
+
+ class Categories < RSS09::Items::Item::Categories
+ def to_rss(rss, item)
+ @categories.each do |category|
+ category.to_rss(rss, item)
+ end
+ end
+
+ class Category < RSS09::Items::Item::Categories::Category
+ def to_rss(rss, item)
+ category = Rss::Channel::Item::Category.new
+ set = setup_values(category)
+ if set
+ item.categories << category
+ setup_other_elements(rss)
+ end
+ end
+
+ def have_required_values?
+ @content
+ end
+ end
+ end
+ end
+
+ end
+
+ class Textinput < RSS09::Textinput
+ end
+ end
+
+ add_maker(filename_to_version(__FILE__), RSS20)
+ end
+end
diff --git a/ruby_1_8_5/lib/rss/maker/base.rb b/ruby_1_8_5/lib/rss/maker/base.rb
new file mode 100644
index 0000000000..2327dd98e4
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/maker/base.rb
@@ -0,0 +1,546 @@
+require 'forwardable'
+
+require 'rss/rss'
+
+module RSS
+ module Maker
+
+ module Base
+
+ def self.append_features(klass)
+ super
+
+ klass.module_eval(<<-EOC, __FILE__, __LINE__)
+
+ OTHER_ELEMENTS = []
+ NEED_INITIALIZE_VARIABLES = []
+
+ def self.inherited(subclass)
+ subclass.const_set("OTHER_ELEMENTS", [])
+ subclass.const_set("NEED_INITIALIZE_VARIABLES", [])
+
+ subclass.module_eval(<<-EOEOC, __FILE__, __LINE__)
+ def self.other_elements
+ OTHER_ELEMENTS + super
+ end
+
+ def self.need_initialize_variables
+ NEED_INITIALIZE_VARIABLES + super
+ end
+ EOEOC
+ end
+
+ def self.add_other_element(variable_name)
+ OTHER_ELEMENTS << variable_name
+ end
+
+ def self.other_elements
+ OTHER_ELEMENTS
+ end
+
+ def self.add_need_initialize_variable(variable_name, init_value="nil")
+ NEED_INITIALIZE_VARIABLES << [variable_name, init_value]
+ end
+
+ def self.need_initialize_variables
+ NEED_INITIALIZE_VARIABLES
+ end
+
+ def self.def_array_element(name)
+ include Enumerable
+ extend Forwardable
+
+ def_delegators("@\#{name}", :<<, :[], :[]=, :first, :last)
+ def_delegators("@\#{name}", :push, :pop, :shift, :unshift)
+ def_delegators("@\#{name}", :each, :size)
+
+ add_need_initialize_variable(name, "[]")
+ end
+ EOC
+ end
+
+ def initialize(maker)
+ @maker = maker
+ initialize_variables
+ end
+
+ def have_required_values?
+ true
+ end
+
+ private
+ def initialize_variables
+ self.class.need_initialize_variables.each do |variable_name, init_value|
+ instance_eval("@#{variable_name} = #{init_value}", __FILE__, __LINE__)
+ end
+ end
+
+ def setup_other_elements(rss)
+ self.class.other_elements.each do |element|
+ __send__("setup_#{element}", rss, current_element(rss))
+ end
+ end
+
+ def current_element(rss)
+ rss
+ end
+
+ def setup_values(target)
+ set = false
+ if have_required_values?
+ variables.each do |var|
+ setter = "#{var}="
+ if target.respond_to?(setter)
+ value = __send__(var)
+ if value
+ target.__send__(setter, value)
+ set = true
+ end
+ end
+ end
+ end
+ set
+ end
+
+ def variables
+ self.class.need_initialize_variables.find_all do |name, init|
+ "nil" == init
+ end.collect do |name, init|
+ name
+ end
+ end
+
+ def variable_is_set?
+ variables.find {|var| !__send__(var).nil?}
+ end
+
+ def not_set_required_variables
+ required_variable_names.find_all do |var|
+ __send__(var).nil?
+ end
+ end
+
+ def required_variables_are_set?
+ required_variable_names.each do |var|
+ return false if __send__(var).nil?
+ end
+ true
+ end
+
+ end
+
+ class RSSBase
+ include Base
+
+ class << self
+ def make(&block)
+ new.make(&block)
+ end
+ end
+
+ %w(xml_stylesheets channel image items textinput).each do |element|
+ attr_reader element
+ add_need_initialize_variable(element, "make_#{element}")
+ module_eval(<<-EOC, __FILE__, __LINE__)
+ private
+ def setup_#{element}(rss)
+ @#{element}.to_rss(rss)
+ end
+
+ def make_#{element}
+ self.class::#{Utils.to_class_name(element)}.new(self)
+ end
+EOC
+ end
+
+ attr_reader :rss_version
+ attr_accessor :version, :encoding, :standalone
+
+ def initialize(rss_version)
+ super(self)
+ @rss_version = rss_version
+ @version = "1.0"
+ @encoding = "UTF-8"
+ @standalone = nil
+ end
+
+ def make
+ if block_given?
+ yield(self)
+ to_rss
+ else
+ nil
+ end
+ end
+
+ def to_rss
+ rss = make_rss
+ setup_xml_stylesheets(rss)
+ setup_elements(rss)
+ setup_other_elements(rss)
+ if rss.channel
+ rss
+ else
+ nil
+ end
+ end
+
+ private
+ remove_method :make_xml_stylesheets
+ def make_xml_stylesheets
+ XMLStyleSheets.new(self)
+ end
+
+ end
+
+ class XMLStyleSheets
+ include Base
+
+ def_array_element("xml_stylesheets")
+
+ def to_rss(rss)
+ @xml_stylesheets.each do |xss|
+ xss.to_rss(rss)
+ end
+ end
+
+ def new_xml_stylesheet
+ xss = XMLStyleSheet.new(@maker)
+ @xml_stylesheets << xss
+ if block_given?
+ yield xss
+ else
+ xss
+ end
+ end
+
+ class XMLStyleSheet
+ include Base
+
+ ::RSS::XMLStyleSheet::ATTRIBUTES.each do |attribute|
+ attr_accessor attribute
+ add_need_initialize_variable(attribute)
+ end
+
+ def to_rss(rss)
+ xss = ::RSS::XMLStyleSheet.new
+ guess_type_if_need(xss)
+ set = setup_values(xss)
+ if set
+ rss.xml_stylesheets << xss
+ end
+ end
+
+ def have_required_values?
+ @href and @type
+ end
+
+ private
+ def guess_type_if_need(xss)
+ if @type.nil?
+ xss.href = @href
+ @type = xss.type
+ end
+ end
+ end
+ end
+
+ class ChannelBase
+ include Base
+
+ %w(cloud categories skipDays skipHours).each do |element|
+ attr_reader element
+ add_other_element(element)
+ add_need_initialize_variable(element, "make_#{element}")
+ module_eval(<<-EOC, __FILE__, __LINE__)
+ private
+ def setup_#{element}(rss, current)
+ @#{element}.to_rss(rss, current)
+ end
+
+ def make_#{element}
+ self.class::#{Utils.to_class_name(element)}.new(@maker)
+ end
+EOC
+ end
+
+ %w(about title link description language copyright
+ managingEditor webMaster rating docs date
+ lastBuildDate generator ttl).each do |element|
+ attr_accessor element
+ add_need_initialize_variable(element)
+ end
+
+ alias_method(:pubDate, :date)
+ alias_method(:pubDate=, :date=)
+
+ def current_element(rss)
+ rss.channel
+ end
+
+ class SkipDaysBase
+ include Base
+
+ def_array_element("days")
+
+ def new_day
+ day = self.class::Day.new(@maker)
+ @days << day
+ if block_given?
+ yield day
+ else
+ day
+ end
+ end
+
+ def current_element(rss)
+ rss.channel.skipDays
+ end
+
+ class DayBase
+ include Base
+
+ %w(content).each do |element|
+ attr_accessor element
+ add_need_initialize_variable(element)
+ end
+
+ def current_element(rss)
+ rss.channel.skipDays.last
+ end
+
+ end
+ end
+
+ class SkipHoursBase
+ include Base
+
+ def_array_element("hours")
+
+ def new_hour
+ hour = self.class::Hour.new(@maker)
+ @hours << hour
+ if block_given?
+ yield hour
+ else
+ hour
+ end
+ end
+
+ def current_element(rss)
+ rss.channel.skipHours
+ end
+
+ class HourBase
+ include Base
+
+ %w(content).each do |element|
+ attr_accessor element
+ add_need_initialize_variable(element)
+ end
+
+ def current_element(rss)
+ rss.channel.skipHours.last
+ end
+
+ end
+ end
+
+ class CloudBase
+ include Base
+
+ %w(domain port path registerProcedure protocol).each do |element|
+ attr_accessor element
+ add_need_initialize_variable(element)
+ end
+
+ def current_element(rss)
+ rss.channel.cloud
+ end
+
+ end
+
+ class CategoriesBase
+ include Base
+
+ def_array_element("categories")
+
+ def new_category
+ category = self.class::Category.new(@maker)
+ @categories << category
+ if block_given?
+ yield category
+ else
+ category
+ end
+ end
+
+ class CategoryBase
+ include Base
+
+ %w(domain content).each do |element|
+ attr_accessor element
+ add_need_initialize_variable(element)
+ end
+ end
+ end
+ end
+
+ class ImageBase
+ include Base
+
+ %w(title url width height description).each do |element|
+ attr_accessor element
+ add_need_initialize_variable(element)
+ end
+
+ def link
+ @maker.channel.link
+ end
+
+ def current_element(rss)
+ rss.image
+ end
+ end
+
+ class ItemsBase
+ include Base
+
+ def_array_element("items")
+
+ attr_accessor :do_sort, :max_size
+
+ def initialize(maker)
+ super
+ @do_sort = false
+ @max_size = -1
+ end
+
+ def normalize
+ if @max_size >= 0
+ sort_if_need[0...@max_size]
+ else
+ sort_if_need[0..@max_size]
+ end
+ end
+
+ def current_element(rss)
+ rss.items
+ end
+
+ def new_item
+ item = self.class::Item.new(@maker)
+ @items << item
+ if block_given?
+ yield item
+ else
+ item
+ end
+ end
+
+ private
+ def sort_if_need
+ if @do_sort.respond_to?(:call)
+ @items.sort do |x, y|
+ @do_sort.call(x, y)
+ end
+ elsif @do_sort
+ @items.sort do |x, y|
+ y <=> x
+ end
+ else
+ @items
+ end
+ end
+
+ class ItemBase
+ include Base
+
+ %w(guid enclosure source categories).each do |element|
+ attr_reader element
+ add_other_element(element)
+ add_need_initialize_variable(element, "make_#{element}")
+ module_eval(<<-EOC, __FILE__, __LINE__)
+ private
+ def setup_#{element}(rss, current)
+ @#{element}.to_rss(rss, current)
+ end
+
+ def make_#{element}
+ self.class::#{Utils.to_class_name(element)}.new(@maker)
+ end
+EOC
+ end
+
+ %w(title link description date author comments).each do |element|
+ attr_accessor element
+ add_need_initialize_variable(element)
+ end
+
+ alias_method(:pubDate, :date)
+ alias_method(:pubDate=, :date=)
+
+ def <=>(other)
+ if date and other.date
+ date <=> other.date
+ elsif date
+ 1
+ elsif other.date
+ -1
+ else
+ 0
+ end
+ end
+
+ def current_element(rss)
+ rss.items.last
+ end
+
+ class GuidBase
+ include Base
+
+ %w(isPermaLink content).each do |element|
+ attr_accessor element
+ add_need_initialize_variable(element)
+ end
+ end
+
+ class EnclosureBase
+ include Base
+
+ %w(url length type).each do |element|
+ attr_accessor element
+ add_need_initialize_variable(element)
+ end
+ end
+
+ class SourceBase
+ include Base
+
+ %w(url content).each do |element|
+ attr_accessor element
+ add_need_initialize_variable(element)
+ end
+ end
+
+ CategoriesBase = ChannelBase::CategoriesBase
+
+ end
+ end
+
+ class TextinputBase
+ include Base
+
+ %w(title description name link).each do |element|
+ attr_accessor element
+ add_need_initialize_variable(element)
+ end
+
+ def current_element(rss)
+ rss.textinput
+ end
+
+ end
+
+ end
+end
diff --git a/ruby_1_8_5/lib/rss/maker/content.rb b/ruby_1_8_5/lib/rss/maker/content.rb
new file mode 100644
index 0000000000..18590d0cf8
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/maker/content.rb
@@ -0,0 +1,29 @@
+require 'rss/content'
+require 'rss/maker/1.0'
+
+module RSS
+ module Maker
+ module ContentModel
+ def self.append_features(klass)
+ super
+
+ ::RSS::ContentModel::ELEMENTS.each do |element|
+ klass.add_need_initialize_variable(element)
+ klass.add_other_element(element)
+ klass.module_eval(<<-EOC, __FILE__, __LINE__+1)
+ attr_accessor :#{element}
+ def setup_#{element}(rss, current)
+ if #{element} and current.respond_to?(:#{element}=)
+ current.#{element} = @#{element} if @#{element}
+ end
+ end
+ EOC
+ end
+ end
+ end
+
+ class ItemsBase
+ class ItemBase; include ContentModel; end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rss/maker/dublincore.rb b/ruby_1_8_5/lib/rss/maker/dublincore.rb
new file mode 100644
index 0000000000..0cf1255e82
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/maker/dublincore.rb
@@ -0,0 +1,169 @@
+require 'rss/dublincore'
+require 'rss/maker/1.0'
+
+module RSS
+ module Maker
+ module DublinCoreModel
+ def self.append_features(klass)
+ super
+
+ ::RSS::DublinCoreModel::ELEMENT_NAME_INFOS.each do |name, plural_name|
+ plural_name ||= "#{name}s"
+ full_name = "#{RSS::DC_PREFIX}_#{name}"
+ full_plural_name = "#{RSS::DC_PREFIX}_#{plural_name}"
+ klass_name = Utils.to_class_name(name)
+ plural_klass_name = "DublinCore#{Utils.to_class_name(plural_name)}"
+ full_plural_klass_name = "self.class::#{plural_klass_name}"
+ full_klass_name = "#{full_plural_klass_name}::#{klass_name}"
+ klass.add_need_initialize_variable(full_plural_name,
+ "make_#{full_plural_name}")
+ klass.add_other_element(full_plural_name)
+ klass.module_eval(<<-EOC, __FILE__, __LINE__+1)
+ attr_accessor :#{full_plural_name}
+ def make_#{full_plural_name}
+ #{full_plural_klass_name}.new(@maker)
+ end
+
+ def setup_#{full_plural_name}(rss, current)
+ @#{full_plural_name}.to_rss(rss, current)
+ end
+
+ def #{full_name}
+ @#{full_plural_name}[0] and @#{full_plural_name}[0].value
+ end
+
+ def #{full_name}=(new_value)
+ @#{full_plural_name}[0] = #{full_klass_name}.new(self)
+ @#{full_plural_name}[0].value = new_value
+ end
+EOC
+ end
+ end
+
+ ::RSS::DublinCoreModel::ELEMENT_NAME_INFOS.each do |name, plural_name|
+ plural_name ||= "#{name}s"
+ klass_name = Utils.to_class_name(name)
+ plural_klass_name = "DublinCore#{Utils.to_class_name(plural_name)}"
+ module_eval(<<-EOC, __FILE__, __LINE__)
+ class #{plural_klass_name}Base
+ include Base
+
+ def_array_element(#{plural_name.dump})
+
+ def new_#{name}
+ #{name} = self.class::#{klass_name}.new(self)
+ @#{plural_name} << #{name}
+ if block_given?
+ yield #{name}
+ else
+ #{name}
+ end
+ end
+
+ def to_rss(rss, current)
+ @#{plural_name}.each do |#{name}|
+ #{name}.to_rss(rss, current)
+ end
+ end
+
+ class #{klass_name}Base
+ include Base
+
+ attr_accessor :value
+ add_need_initialize_variable("value")
+ alias_method(:content, :value)
+ alias_method(:content=, :value=)
+
+ def have_required_values?
+ @value
+ end
+ end
+ end
+ EOC
+ end
+
+ def self.install_dublin_core(klass)
+ ::RSS::DublinCoreModel::ELEMENT_NAME_INFOS.each do |name, plural_name|
+ plural_name ||= "#{name}s"
+ klass_name = Utils.to_class_name(name)
+ plural_klass_name = "DublinCore#{Utils.to_class_name(plural_name)}"
+ full_klass_name = "DublinCore#{klass_name}"
+ klass.module_eval(<<-EOC, *Utils.get_file_and_line_from_caller(1))
+ class #{plural_klass_name} < #{plural_klass_name}Base
+ class #{klass_name} < #{klass_name}Base
+ def to_rss(rss, current)
+ if value and current.respond_to?(:dc_#{name})
+ new_item = current.class::#{full_klass_name}.new(value)
+ current.dc_#{plural_name} << new_item
+ end
+ end
+ end
+ end
+EOC
+ end
+ end
+ end
+
+ class ChannelBase
+ include DublinCoreModel
+
+ remove_method(:date)
+ remove_method(:date=)
+ alias_method(:date, :dc_date)
+ alias_method(:date=, :dc_date=)
+ end
+
+ class ImageBase; include DublinCoreModel; end
+ class ItemsBase
+ class ItemBase
+ include DublinCoreModel
+
+ remove_method(:date)
+ remove_method(:date=)
+ alias_method(:date, :dc_date)
+ alias_method(:date=, :dc_date=)
+ end
+ end
+ class TextinputBase; include DublinCoreModel; end
+
+ class RSS10
+ class Channel
+ DublinCoreModel.install_dublin_core(self)
+ end
+
+ class Image
+ DublinCoreModel.install_dublin_core(self)
+ end
+
+ class Items
+ class Item
+ DublinCoreModel.install_dublin_core(self)
+ end
+ end
+
+ class Textinput
+ DublinCoreModel.install_dublin_core(self)
+ end
+ end
+
+ class RSS09
+ class Channel
+ DublinCoreModel.install_dublin_core(self)
+ end
+
+ class Image
+ DublinCoreModel.install_dublin_core(self)
+ end
+
+ class Items
+ class Item
+ DublinCoreModel.install_dublin_core(self)
+ end
+ end
+
+ class Textinput
+ DublinCoreModel.install_dublin_core(self)
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rss/maker/image.rb b/ruby_1_8_5/lib/rss/maker/image.rb
new file mode 100644
index 0000000000..ed51c8ecba
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/maker/image.rb
@@ -0,0 +1,145 @@
+require 'rss/image'
+require 'rss/maker/1.0'
+require 'rss/maker/dublincore'
+
+module RSS
+ module Maker
+ module ImageItemModel
+ def self.append_features(klass)
+ super
+
+ name = "#{RSS::IMAGE_PREFIX}_item"
+ klass.add_need_initialize_variable(name, "make_#{name}")
+ klass.add_other_element(name)
+ klass.module_eval(<<-EOC, __FILE__, __LINE__+1)
+ attr_reader :#{name}
+ def setup_#{name}(rss, current)
+ if @#{name}
+ @#{name}.to_rss(rss, current)
+ end
+ end
+
+ def make_#{name}
+ self.class::#{Utils.to_class_name(name)}.new(@maker)
+ end
+EOC
+ end
+
+ class ImageItemBase
+ include Base
+ include Maker::DublinCoreModel
+
+ attr_accessor :about, :resource, :image_width, :image_height
+ add_need_initialize_variable("about")
+ add_need_initialize_variable("resource")
+ add_need_initialize_variable("image_width")
+ add_need_initialize_variable("image_height")
+ alias width= image_width=
+ alias width image_width
+ alias height= image_height=
+ alias height image_height
+
+ def have_required_values?
+ @about
+ end
+ end
+ end
+
+ module ImageFaviconModel
+ def self.append_features(klass)
+ super
+
+ name = "#{RSS::IMAGE_PREFIX}_favicon"
+ klass.add_need_initialize_variable(name, "make_#{name}")
+ klass.add_other_element(name)
+ klass.module_eval(<<-EOC, __FILE__, __LINE__+1)
+ attr_reader :#{name}
+ def setup_#{name}(rss, current)
+ if @#{name}
+ @#{name}.to_rss(rss, current)
+ end
+ end
+
+ def make_#{name}
+ self.class::#{Utils.to_class_name(name)}.new(@maker)
+ end
+EOC
+ end
+
+ class ImageFaviconBase
+ include Base
+ include Maker::DublinCoreModel
+
+ attr_accessor :about, :image_size
+ add_need_initialize_variable("about")
+ add_need_initialize_variable("image_size")
+ alias size image_size
+ alias size= image_size=
+
+ def have_required_values?
+ @about and @image_size
+ end
+ end
+ end
+
+ class ChannelBase; include Maker::ImageFaviconModel; end
+
+ class ItemsBase
+ class ItemBase; include Maker::ImageItemModel; end
+ end
+
+ class RSS10
+ class Items
+ class Item
+ class ImageItem < ImageItemBase
+ DublinCoreModel.install_dublin_core(self)
+ def to_rss(rss, current)
+ if @about
+ item = ::RSS::ImageItemModel::ImageItem.new(@about, @resource)
+ setup_values(item)
+ setup_other_elements(item)
+ current.image_item = item
+ end
+ end
+ end
+ end
+ end
+
+ class Channel
+ class ImageFavicon < ImageFaviconBase
+ DublinCoreModel.install_dublin_core(self)
+ def to_rss(rss, current)
+ if @about and @image_size
+ args = [@about, @image_size]
+ favicon = ::RSS::ImageFaviconModel::ImageFavicon.new(*args)
+ setup_values(favicon)
+ setup_other_elements(favicon)
+ current.image_favicon = favicon
+ end
+ end
+ end
+ end
+ end
+
+ class RSS09
+ class Items
+ class Item
+ class ImageItem < ImageItemBase
+ DublinCoreModel.install_dublin_core(self)
+ def to_rss(*args)
+ end
+ end
+ end
+ end
+
+ class Channel
+ class ImageFavicon < ImageFaviconBase
+ DublinCoreModel.install_dublin_core(self)
+ def to_rss(*args)
+ end
+ end
+ end
+ end
+
+ end
+end
diff --git a/ruby_1_8_5/lib/rss/maker/syndication.rb b/ruby_1_8_5/lib/rss/maker/syndication.rb
new file mode 100644
index 0000000000..3717086257
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/maker/syndication.rb
@@ -0,0 +1,27 @@
+require 'rss/syndication'
+require 'rss/maker/1.0'
+
+module RSS
+ module Maker
+ module SyndicationModel
+ def self.append_features(klass)
+ super
+
+ ::RSS::SyndicationModel::ELEMENTS.each do |element|
+ klass.add_need_initialize_variable(element)
+ klass.add_other_element(element)
+ klass.module_eval(<<-EOC, __FILE__, __LINE__+1)
+ attr_accessor :#{element}
+ def setup_#{element}(rss, current)
+ if #{element} and current.respond_to?(:#{element}=)
+ current.#{element} = @#{element} if @#{element}
+ end
+ end
+ EOC
+ end
+ end
+ end
+
+ class ChannelBase; include SyndicationModel; end
+ end
+end
diff --git a/ruby_1_8_5/lib/rss/maker/taxonomy.rb b/ruby_1_8_5/lib/rss/maker/taxonomy.rb
new file mode 100644
index 0000000000..f272996581
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/maker/taxonomy.rb
@@ -0,0 +1,182 @@
+require 'rss/taxonomy'
+require 'rss/maker/1.0'
+require 'rss/maker/dublincore'
+
+module RSS
+ module Maker
+ module TaxonomyTopicsModel
+ def self.append_features(klass)
+ super
+
+ klass.add_need_initialize_variable("taxo_topics", "make_taxo_topics")
+ klass.add_other_element("taxo_topics")
+ klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1)
+ attr_reader :taxo_topics
+ def make_taxo_topics
+ self.class::TaxonomyTopics.new(@maker)
+ end
+
+ def setup_taxo_topics(rss, current)
+ @taxo_topics.to_rss(rss, current)
+ end
+EOC
+ end
+
+ def self.install_taxo_topics(klass)
+ klass.module_eval(<<-EOC, *Utils.get_file_and_line_from_caller(1))
+ class TaxonomyTopics < TaxonomyTopicsBase
+ def to_rss(rss, current)
+ if current.respond_to?(:taxo_topics)
+ topics = current.class::TaxonomyTopics.new
+ bag = topics.Bag
+ @resources.each do |resource|
+ bag.lis << RDF::Bag::Li.new(resource)
+ end
+ current.taxo_topics = topics
+ end
+ end
+ end
+EOC
+ end
+
+ class TaxonomyTopicsBase
+ include Base
+
+ attr_reader :resources
+ def_array_element("resources")
+ end
+ end
+
+ module TaxonomyTopicModel
+ def self.append_features(klass)
+ super
+
+ klass.add_need_initialize_variable("taxo_topics", "make_taxo_topics")
+ klass.add_other_element("taxo_topics")
+ klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1)
+ attr_reader :taxo_topics
+ def make_taxo_topics
+ self.class::TaxonomyTopics.new(@maker)
+ end
+
+ def setup_taxo_topics(rss, current)
+ @taxo_topics.to_rss(rss, current)
+ end
+
+ def taxo_topic
+ @taxo_topics[0] and @taxo_topics[0].value
+ end
+
+ def taxo_topic=(new_value)
+ @taxo_topic[0] = self.class::TaxonomyTopic.new(self)
+ @taxo_topic[0].value = new_value
+ end
+EOC
+ end
+
+ def self.install_taxo_topic(klass)
+ klass.module_eval(<<-EOC, *Utils.get_file_and_line_from_caller(1))
+ class TaxonomyTopics < TaxonomyTopicsBase
+ class TaxonomyTopic < TaxonomyTopicBase
+ DublinCoreModel.install_dublin_core(self)
+ TaxonomyTopicsModel.install_taxo_topics(self)
+
+ def to_rss(rss, current)
+ if current.respond_to?(:taxo_topics)
+ topic = current.class::TaxonomyTopic.new(value)
+ topic.taxo_link = value
+ taxo_topics.to_rss(rss, topic) if taxo_topics
+ current.taxo_topics << topic
+ setup_other_elements(rss)
+ end
+ end
+
+ def current_element(rss)
+ super.taxo_topics.last
+ end
+ end
+ end
+EOC
+ end
+
+ class TaxonomyTopicsBase
+ include Base
+
+ def_array_element("taxo_topics")
+
+ def new_taxo_topic
+ taxo_topic = self.class::TaxonomyTopic.new(self)
+ @taxo_topics << taxo_topic
+ if block_given?
+ yield taxo_topic
+ else
+ taxo_topic
+ end
+ end
+
+ def to_rss(rss, current)
+ @taxo_topics.each do |taxo_topic|
+ taxo_topic.to_rss(rss, current)
+ end
+ end
+
+ class TaxonomyTopicBase
+ include Base
+ include DublinCoreModel
+ include TaxonomyTopicsModel
+
+ attr_accessor :value
+ add_need_initialize_variable("value")
+ alias_method(:taxo_link, :value)
+ alias_method(:taxo_link=, :value=)
+
+ def have_required_values?
+ @value
+ end
+ end
+ end
+ end
+
+ class RSSBase
+ include TaxonomyTopicModel
+ end
+
+ class ChannelBase
+ include TaxonomyTopicsModel
+ end
+
+ class ItemsBase
+ class ItemBase
+ include TaxonomyTopicsModel
+ end
+ end
+
+ class RSS10
+ TaxonomyTopicModel.install_taxo_topic(self)
+
+ class Channel
+ TaxonomyTopicsModel.install_taxo_topics(self)
+ end
+
+ class Items
+ class Item
+ TaxonomyTopicsModel.install_taxo_topics(self)
+ end
+ end
+ end
+
+ class RSS09
+ TaxonomyTopicModel.install_taxo_topic(self)
+
+ class Channel
+ TaxonomyTopicsModel.install_taxo_topics(self)
+ end
+
+ class Items
+ class Item
+ TaxonomyTopicsModel.install_taxo_topics(self)
+ end
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rss/maker/trackback.rb b/ruby_1_8_5/lib/rss/maker/trackback.rb
new file mode 100644
index 0000000000..4ae6164f68
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/maker/trackback.rb
@@ -0,0 +1,130 @@
+require 'rss/trackback'
+require 'rss/maker/1.0'
+require 'rss/maker/2.0'
+
+module RSS
+ module Maker
+ module TrackBackModel
+ def self.append_features(klass)
+ super
+
+ name = "#{RSS::TRACKBACK_PREFIX}_ping"
+ klass.add_need_initialize_variable(name)
+ klass.add_other_element(name)
+ klass.module_eval(<<-EOC, __FILE__, __LINE__+1)
+ attr_accessor :#{name}
+ def setup_#{name}(rss, current)
+ if #{name} and current.respond_to?(:#{name}=)
+ current.#{name} = #{name}
+ end
+ end
+ EOC
+
+ name = "#{RSS::TRACKBACK_PREFIX}_abouts"
+ klass.add_need_initialize_variable(name, "make_#{name}")
+ klass.add_other_element(name)
+ klass.module_eval(<<-EOC, __FILE__, __LINE__+1)
+ attr_accessor :#{name}
+ def make_#{name}
+ self.class::TrackBackAbouts.new(self)
+ end
+
+ def setup_#{name}(rss, current)
+ @#{name}.to_rss(rss, current)
+ end
+ EOC
+ end
+
+ class TrackBackAboutsBase
+ include Base
+
+ def_array_element("abouts")
+
+ def new_about
+ about = self.class::TrackBackAbout.new(@maker)
+ @abouts << about
+ if block_given?
+ yield about
+ else
+ about
+ end
+ end
+
+ def to_rss(rss, current)
+ @abouts.each do |about|
+ about.to_rss(rss, current)
+ end
+ end
+
+ class TrackBackAboutBase
+ include Base
+
+ attr_accessor :value
+ add_need_initialize_variable("value")
+
+ alias_method(:resource, :value)
+ alias_method(:resource=, :value=)
+ alias_method(:content, :value)
+ alias_method(:content=, :value=)
+
+ def have_required_values?
+ @value
+ end
+
+ end
+ end
+ end
+
+ class ItemsBase
+ class ItemBase; include TrackBackModel; end
+ end
+
+ class RSS10
+ class Items
+ class Item
+ class TrackBackAbouts < TrackBackAboutsBase
+ class TrackBackAbout < TrackBackAboutBase
+ def to_rss(rss, current)
+ if resource
+ about = ::RSS::TrackBackModel10::TrackBackAbout.new(resource)
+ current.trackback_abouts << about
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ class RSS09
+ class Items
+ class Item
+ class TrackBackAbouts < TrackBackAboutsBase
+ def to_rss(*args)
+ end
+ class TrackBackAbout < TrackBackAboutBase
+ end
+ end
+ end
+ end
+ end
+
+ class RSS20
+ class Items
+ class Item
+ class TrackBackAbouts < TrackBackAboutsBase
+ class TrackBackAbout < TrackBackAboutBase
+ def to_rss(rss, current)
+ if content
+ about = ::RSS::TrackBackModel20::TrackBackAbout.new(content)
+ current.trackback_abouts << about
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ end
+end
diff --git a/ruby_1_8_5/lib/rss/parser.rb b/ruby_1_8_5/lib/rss/parser.rb
new file mode 100644
index 0000000000..033bc123aa
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/parser.rb
@@ -0,0 +1,476 @@
+require "forwardable"
+require "open-uri"
+
+require "rss/rss"
+
+module RSS
+
+ class NotWellFormedError < Error
+ attr_reader :line, :element
+
+ # Create a new NotWellFormedError for an error at +line+
+ # in +element+. If a block is given the return value of
+ # the block ends up in the error message.
+ def initialize(line=nil, element=nil)
+ message = "This is not well formed XML"
+ if element or line
+ message << "\nerror occurred"
+ message << " in #{element}" if element
+ message << " at about #{line} line" if line
+ end
+ message << "\n#{yield}" if block_given?
+ super(message)
+ end
+ end
+
+ class XMLParserNotFound < Error
+ def initialize
+ super("available XML parser was not found in " <<
+ "#{AVAILABLE_PARSER_LIBRARIES.inspect}.")
+ end
+ end
+
+ class NotValidXMLParser < Error
+ def initialize(parser)
+ super("#{parser} is not an available XML parser. " <<
+ "Available XML parser"<<
+ (AVAILABLE_PARSERS.size > 1 ? "s are ": " is ") <<
+ "#{AVAILABLE_PARSERS.inspect}.")
+ end
+ end
+
+ class NSError < InvalidRSSError
+ attr_reader :tag, :prefix, :uri
+ def initialize(tag, prefix, require_uri)
+ @tag, @prefix, @uri = tag, prefix, require_uri
+ super("prefix <#{prefix}> doesn't associate uri " <<
+ "<#{require_uri}> in tag <#{tag}>")
+ end
+ end
+
+ class Parser
+
+ extend Forwardable
+
+ class << self
+
+ @@default_parser = nil
+
+ def default_parser
+ @@default_parser || AVAILABLE_PARSERS.first
+ end
+
+ # Set @@default_parser to new_value if it is one of the
+ # available parsers. Else raise NotValidXMLParser error.
+ def default_parser=(new_value)
+ if AVAILABLE_PARSERS.include?(new_value)
+ @@default_parser = new_value
+ else
+ raise NotValidXMLParser.new(new_value)
+ end
+ end
+
+ def parse(rss, do_validate=true, ignore_unknown_element=true,
+ parser_class=default_parser)
+ parser = new(rss, parser_class)
+ parser.do_validate = do_validate
+ parser.ignore_unknown_element = ignore_unknown_element
+ parser.parse
+ end
+ end
+
+ def_delegators(:@parser, :parse, :rss,
+ :ignore_unknown_element,
+ :ignore_unknown_element=, :do_validate,
+ :do_validate=)
+
+ def initialize(rss, parser_class=self.class.default_parser)
+ @parser = parser_class.new(normalize_rss(rss))
+ end
+
+ private
+
+ # Try to get the XML associated with +rss+.
+ # Return +rss+ if it already looks like XML, or treat it as a URI,
+ # or a file to get the XML,
+ def normalize_rss(rss)
+ return rss if maybe_xml?(rss)
+
+ uri = to_uri(rss)
+
+ if uri.respond_to?(:read)
+ uri.read
+ elsif !rss.tainted? and File.readable?(rss)
+ File.open(rss) {|f| f.read}
+ else
+ rss
+ end
+ end
+
+ # maybe_xml? tests if source is a string that looks like XML.
+ def maybe_xml?(source)
+ source.is_a?(String) and /</ =~ source
+ end
+
+ # Attempt to convert rss to a URI, but just return it if
+ # there's a ::URI::Error
+ def to_uri(rss)
+ return rss if rss.is_a?(::URI::Generic)
+
+ begin
+ URI(rss)
+ rescue ::URI::Error
+ rss
+ end
+ end
+ end
+
+ class BaseParser
+
+ class << self
+ def raise_for_undefined_entity?
+ listener.raise_for_undefined_entity?
+ end
+ end
+
+ def initialize(rss)
+ @listener = self.class.listener.new
+ @rss = rss
+ end
+
+ def rss
+ @listener.rss
+ end
+
+ def ignore_unknown_element
+ @listener.ignore_unknown_element
+ end
+
+ def ignore_unknown_element=(new_value)
+ @listener.ignore_unknown_element = new_value
+ end
+
+ def do_validate
+ @listener.do_validate
+ end
+
+ def do_validate=(new_value)
+ @listener.do_validate = new_value
+ end
+
+ def parse
+ if @listener.rss.nil?
+ _parse
+ end
+ @listener.rss
+ end
+
+ end
+
+ class BaseListener
+
+ extend Utils
+
+ class << self
+
+ @@setters = {}
+ @@registered_uris = {}
+ @@class_names = {}
+
+ # return the setter for the uri, tag_name pair, or nil.
+ def setter(uri, tag_name)
+ begin
+ @@setters[uri][tag_name]
+ rescue NameError
+ nil
+ end
+ end
+
+
+ # return the tag_names for setters associated with uri
+ def available_tags(uri)
+ begin
+ @@setters[uri].keys
+ rescue NameError
+ []
+ end
+ end
+
+ # register uri against this name.
+ def register_uri(uri, name)
+ @@registered_uris[name] ||= {}
+ @@registered_uris[name][uri] = nil
+ end
+
+ # test if this uri is registered against this name
+ def uri_registered?(uri, name)
+ @@registered_uris[name].has_key?(uri)
+ end
+
+ # record class_name for the supplied uri and tag_name
+ def install_class_name(uri, tag_name, class_name)
+ @@class_names[uri] ||= {}
+ @@class_names[uri][tag_name] = class_name
+ end
+
+ # retrieve class_name for the supplied uri and tag_name
+ # If it doesn't exist, capitalize the tag_name
+ def class_name(uri, tag_name)
+ begin
+ @@class_names[uri][tag_name]
+ rescue NameError
+ tag_name[0,1].upcase + tag_name[1..-1]
+ end
+ end
+
+ def install_get_text_element(uri, name, setter)
+ install_setter(uri, name, setter)
+ def_get_text_element(uri, name, *get_file_and_line_from_caller(1))
+ end
+
+ def raise_for_undefined_entity?
+ true
+ end
+
+ private
+ # set the setter for the uri, tag_name pair
+ def install_setter(uri, tag_name, setter)
+ @@setters[uri] ||= {}
+ @@setters[uri][tag_name] = setter
+ end
+
+ def def_get_text_element(uri, name, file, line)
+ register_uri(uri, name)
+ unless private_instance_methods(false).include?("start_#{name}")
+ module_eval(<<-EOT, file, line)
+ def start_#{name}(name, prefix, attrs, ns)
+ uri = _ns(ns, prefix)
+ if self.class.uri_registered?(uri, #{name.inspect})
+ start_get_text_element(name, prefix, ns, uri)
+ else
+ start_else_element(name, prefix, attrs, ns)
+ end
+ end
+ EOT
+ __send__("private", "start_#{name}")
+ end
+ end
+
+ end
+
+ end
+
+ module ListenerMixin
+
+ attr_reader :rss
+
+ attr_accessor :ignore_unknown_element
+ attr_accessor :do_validate
+
+ def initialize
+ @rss = nil
+ @ignore_unknown_element = true
+ @do_validate = true
+ @ns_stack = [{}]
+ @tag_stack = [[]]
+ @text_stack = ['']
+ @proc_stack = []
+ @last_element = nil
+ @version = @encoding = @standalone = nil
+ @xml_stylesheets = []
+ end
+
+ # set instance vars for version, encoding, standalone
+ def xmldecl(version, encoding, standalone)
+ @version, @encoding, @standalone = version, encoding, standalone
+ end
+
+ def instruction(name, content)
+ if name == "xml-stylesheet"
+ params = parse_pi_content(content)
+ if params.has_key?("href")
+ @xml_stylesheets << XMLStyleSheet.new(*params)
+ end
+ end
+ end
+
+ def tag_start(name, attributes)
+ @text_stack.push('')
+
+ ns = @ns_stack.last.dup
+ attrs = {}
+ attributes.each do |n, v|
+ if /\Axmlns(?:\z|:)/ =~ n
+ ns[$POSTMATCH] = v
+ else
+ attrs[n] = v
+ end
+ end
+ @ns_stack.push(ns)
+
+ prefix, local = split_name(name)
+ @tag_stack.last.push([_ns(ns, prefix), local])
+ @tag_stack.push([])
+ if respond_to?("start_#{local}", true)
+ __send__("start_#{local}", local, prefix, attrs, ns.dup)
+ else
+ start_else_element(local, prefix, attrs, ns.dup)
+ end
+ end
+
+ def tag_end(name)
+ if DEBUG
+ p "end tag #{name}"
+ p @tag_stack
+ end
+ text = @text_stack.pop
+ tags = @tag_stack.pop
+ pr = @proc_stack.pop
+ pr.call(text, tags) unless pr.nil?
+ @ns_stack.pop
+ end
+
+ def text(data)
+ @text_stack.last << data
+ end
+
+ private
+ def _ns(ns, prefix)
+ ns.fetch(prefix, "")
+ end
+
+ CONTENT_PATTERN = /\s*([^=]+)=(["'])([^\2]+?)\2/
+ # Extract the first name="value" pair from content.
+ # Works with single quotes according to the constant
+ # CONTENT_PATTERN. Return a Hash.
+ def parse_pi_content(content)
+ params = {}
+ content.scan(CONTENT_PATTERN) do |name, quote, value|
+ params[name] = value
+ end
+ params
+ end
+
+ def start_else_element(local, prefix, attrs, ns)
+ class_name = self.class.class_name(_ns(ns, prefix), local)
+ current_class = @last_element.class
+ if current_class.constants.include?(class_name)
+ next_class = current_class.const_get(class_name)
+ start_have_something_element(local, prefix, attrs, ns, next_class)
+ else
+ if !@do_validate or @ignore_unknown_element
+ @proc_stack.push(nil)
+ else
+ parent = "ROOT ELEMENT???"
+ if current_class.tag_name
+ parent = current_class.tag_name
+ end
+ raise NotExpectedTagError.new(local, _ns(ns, prefix), parent)
+ end
+ end
+ end
+
+ NAMESPLIT = /^(?:([\w:][-\w\d.]*):)?([\w:][-\w\d.]*)/
+ def split_name(name)
+ name =~ NAMESPLIT
+ [$1 || '', $2]
+ end
+
+ def check_ns(tag_name, prefix, ns, require_uri)
+ if @do_validate
+ if _ns(ns, prefix) == require_uri
+ #ns.delete(prefix)
+ else
+ raise NSError.new(tag_name, prefix, require_uri)
+ end
+ end
+ end
+
+ def start_get_text_element(tag_name, prefix, ns, required_uri)
+ @proc_stack.push Proc.new {|text, tags|
+ setter = self.class.setter(required_uri, tag_name)
+ if @last_element.respond_to?(setter)
+ @last_element.__send__(setter, text.to_s)
+ else
+ if @do_validate and !@ignore_unknown_element
+ raise NotExpectedTagError.new(tag_name, _ns(ns, prefix),
+ @last_element.tag_name)
+ end
+ end
+ }
+ end
+
+ def start_have_something_element(tag_name, prefix, attrs, ns, klass)
+
+ check_ns(tag_name, prefix, ns, klass.required_uri)
+
+ attributes = {}
+ klass.get_attributes.each do |a_name, a_uri, required, element_name|
+
+ if a_uri.is_a?(String) or !a_uri.respond_to?(:include?)
+ a_uri = [a_uri]
+ end
+ unless a_uri == [""]
+ for prefix, uri in ns
+ if a_uri.include?(uri)
+ val = attrs["#{prefix}:#{a_name}"]
+ break if val
+ end
+ end
+ end
+ if val.nil? and a_uri.include?("")
+ val = attrs[a_name]
+ end
+
+ if @do_validate and required and val.nil?
+ unless a_uri.include?("")
+ for prefix, uri in ns
+ if a_uri.include?(uri)
+ a_name = "#{prefix}:#{a_name}"
+ end
+ end
+ end
+ raise MissingAttributeError.new(tag_name, a_name)
+ end
+
+ attributes[a_name] = val
+ end
+
+ previous = @last_element
+ next_element = klass.new(@do_validate, attributes)
+ previous.instance_eval {set_next_element(tag_name, next_element)}
+ @last_element = next_element
+ @proc_stack.push Proc.new { |text, tags|
+ p(@last_element.class) if DEBUG
+ @last_element.content = text if klass.have_content?
+ if @do_validate
+ @last_element.validate_for_stream(tags, @ignore_unknown_element)
+ end
+ @last_element = previous
+ }
+ end
+
+ end
+
+ unless const_defined? :AVAILABLE_PARSER_LIBRARIES
+ AVAILABLE_PARSER_LIBRARIES = [
+ ["rss/xmlparser", :XMLParserParser],
+ ["rss/xmlscanner", :XMLScanParser],
+ ["rss/rexmlparser", :REXMLParser],
+ ]
+ end
+
+ AVAILABLE_PARSERS = []
+
+ AVAILABLE_PARSER_LIBRARIES.each do |lib, parser|
+ begin
+ require lib
+ AVAILABLE_PARSERS.push(const_get(parser))
+ rescue LoadError
+ end
+ end
+
+ if AVAILABLE_PARSERS.empty?
+ raise XMLParserNotFound
+ end
+end
diff --git a/ruby_1_8_5/lib/rss/rexmlparser.rb b/ruby_1_8_5/lib/rss/rexmlparser.rb
new file mode 100644
index 0000000000..4dabf59199
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/rexmlparser.rb
@@ -0,0 +1,54 @@
+require "rexml/document"
+require "rexml/streamlistener"
+
+/\A(\d+)\.(\d+)(?:\.\d+)+\z/ =~ REXML::Version
+if ([$1.to_i, $2.to_i] <=> [2, 5]) < 0
+ raise LoadError, "needs REXML 2.5 or later (#{REXML::Version})"
+end
+
+module RSS
+
+ class REXMLParser < BaseParser
+
+ class << self
+ def listener
+ REXMLListener
+ end
+ end
+
+ private
+ def _parse
+ begin
+ REXML::Document.parse_stream(@rss, @listener)
+ rescue RuntimeError => e
+ raise NotWellFormedError.new{e.message}
+ rescue REXML::ParseException => e
+ context = e.context
+ line = context[0] if context
+ raise NotWellFormedError.new(line){e.message}
+ end
+ end
+
+ end
+
+ class REXMLListener < BaseListener
+
+ include REXML::StreamListener
+ include ListenerMixin
+
+ class << self
+ def raise_for_undefined_entity?
+ false
+ end
+ end
+
+ def xmldecl(version, encoding, standalone)
+ super(version, encoding, standalone == "yes")
+ # Encoding is converted to UTF-8 when REXML parse XML.
+ @encoding = 'UTF-8'
+ end
+
+ alias_method(:cdata, :text)
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rss/rss.rb b/ruby_1_8_5/lib/rss/rss.rb
new file mode 100644
index 0000000000..a06985af94
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/rss.rb
@@ -0,0 +1,1015 @@
+require "time"
+
+class Time
+ class << self
+ unless respond_to?(:w3cdtf)
+ def w3cdtf(date)
+ if /\A\s*
+ (-?\d+)-(\d\d)-(\d\d)
+ (?:T
+ (\d\d):(\d\d)(?::(\d\d))?
+ (\.\d+)?
+ (Z|[+-]\d\d:\d\d)?)?
+ \s*\z/ix =~ date and (($5 and $8) or (!$5 and !$8))
+ datetime = [$1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i]
+ datetime << $7.to_f * 1000000 if $7
+ if $8
+ Time.utc(*datetime) - zone_offset($8)
+ else
+ Time.local(*datetime)
+ end
+ else
+ raise ArgumentError.new("invalid date: #{date.inspect}")
+ end
+ end
+ end
+ end
+
+ unless instance_methods.include?("w3cdtf")
+ alias w3cdtf iso8601
+ end
+end
+
+require "English"
+require "rss/utils"
+require "rss/converter"
+require "rss/xml-stylesheet"
+
+module RSS
+
+ VERSION = "0.1.6"
+
+ URI = "http://purl.org/rss/1.0/"
+
+ DEBUG = false
+
+ class Error < StandardError; end
+
+ class OverlappedPrefixError < Error
+ attr_reader :prefix
+ def initialize(prefix)
+ @prefix = prefix
+ end
+ end
+
+ class InvalidRSSError < Error; end
+
+ class MissingTagError < InvalidRSSError
+ attr_reader :tag, :parent
+ def initialize(tag, parent)
+ @tag, @parent = tag, parent
+ super("tag <#{tag}> is missing in tag <#{parent}>")
+ end
+ end
+
+ class TooMuchTagError < InvalidRSSError
+ attr_reader :tag, :parent
+ def initialize(tag, parent)
+ @tag, @parent = tag, parent
+ super("tag <#{tag}> is too much in tag <#{parent}>")
+ end
+ end
+
+ class MissingAttributeError < InvalidRSSError
+ attr_reader :tag, :attribute
+ def initialize(tag, attribute)
+ @tag, @attribute = tag, attribute
+ super("attribute <#{attribute}> is missing in tag <#{tag}>")
+ end
+ end
+
+ class UnknownTagError < InvalidRSSError
+ attr_reader :tag, :uri
+ def initialize(tag, uri)
+ @tag, @uri = tag, uri
+ super("tag <#{tag}> is unknown in namespace specified by uri <#{uri}>")
+ end
+ end
+
+ class NotExpectedTagError < InvalidRSSError
+ attr_reader :tag, :uri, :parent
+ def initialize(tag, uri, parent)
+ @tag, @uri, @parent = tag, uri, parent
+ super("tag <{#{uri}}#{tag}> is not expected in tag <#{parent}>")
+ end
+ end
+ # For backward compatibility :X
+ NotExceptedTagError = NotExpectedTagError
+
+ class NotAvailableValueError < InvalidRSSError
+ attr_reader :tag, :value, :attribute
+ def initialize(tag, value, attribute=nil)
+ @tag, @value, @attribute = tag, value, attribute
+ message = "value <#{value}> of "
+ message << "attribute <#{attribute}> of " if attribute
+ message << "tag <#{tag}> is not available."
+ super(message)
+ end
+ end
+
+ class UnknownConversionMethodError < Error
+ attr_reader :to, :from
+ def initialize(to, from)
+ @to = to
+ @from = from
+ super("can't convert to #{to} from #{from}.")
+ end
+ end
+ # for backward compatibility
+ UnknownConvertMethod = UnknownConversionMethodError
+
+ class ConversionError < Error
+ attr_reader :string, :to, :from
+ def initialize(string, to, from)
+ @string = string
+ @to = to
+ @from = from
+ super("can't convert #{@string} to #{to} from #{from}.")
+ end
+ end
+
+ class NotSetError < Error
+ attr_reader :name, :variables
+ def initialize(name, variables)
+ @name = name
+ @variables = variables
+ super("required variables of #{@name} are not set: #{@variables.join(', ')}")
+ end
+ end
+
+ module BaseModel
+
+ include Utils
+
+ def install_have_child_element(tag_name, uri, occurs, name=nil)
+ name ||= tag_name
+ add_need_initialize_variable(name)
+ install_model(tag_name, uri, occurs, name)
+
+ attr_accessor name
+ install_element(name) do |n, elem_name|
+ <<-EOC
+ if @#{n}
+ "\#{@#{n}.to_s(need_convert, indent)}"
+ else
+ ''
+ end
+EOC
+ end
+ end
+ alias_method(:install_have_attribute_element, :install_have_child_element)
+
+ def install_have_children_element(tag_name, uri, occurs, name=nil, plural_name=nil)
+ name ||= tag_name
+ plural_name ||= "#{name}s"
+ add_have_children_element(name, plural_name)
+ add_plural_form(name, plural_name)
+ install_model(tag_name, uri, occurs, plural_name)
+
+ def_children_accessor(name, plural_name)
+ install_element(name, "s") do |n, elem_name|
+ <<-EOC
+ rv = []
+ @#{n}.each do |x|
+ value = "\#{x.to_s(need_convert, indent)}"
+ rv << value if /\\A\\s*\\z/ !~ value
+ end
+ rv.join("\n")
+EOC
+ end
+ end
+
+ def install_text_element(tag_name, uri, occurs, name=nil, type=nil, disp_name=nil)
+ name ||= tag_name
+ disp_name ||= name
+ self::ELEMENTS << name
+ add_need_initialize_variable(name)
+ install_model(tag_name, uri, occurs, name)
+
+ def_corresponded_attr_writer name, type, disp_name
+ convert_attr_reader name
+ install_element(name) do |n, elem_name|
+ <<-EOC
+ if @#{n}
+ rv = "\#{indent}<#{elem_name}>"
+ value = html_escape(@#{n})
+ if need_convert
+ rv << convert(value)
+ else
+ rv << value
+ end
+ rv << "</#{elem_name}>"
+ rv
+ else
+ ''
+ end
+EOC
+ end
+ end
+
+ def install_date_element(tag_name, uri, occurs, name=nil, type=nil, disp_name=nil)
+ name ||= tag_name
+ type ||= :w3cdtf
+ disp_name ||= name
+ self::ELEMENTS << name
+ add_need_initialize_variable(name)
+ install_model(tag_name, uri, occurs, name)
+
+ # accessor
+ convert_attr_reader name
+ date_writer(name, type, disp_name)
+
+ install_element(name) do |n, elem_name|
+ <<-EOC
+ if @#{n}
+ rv = "\#{indent}<#{elem_name}>"
+ value = html_escape(@#{n}.#{type})
+ if need_convert
+ rv << convert(value)
+ else
+ rv << value
+ end
+ rv << "</#{elem_name}>"
+ rv
+ else
+ ''
+ end
+EOC
+ end
+
+ end
+
+ private
+ def install_element(name, postfix="")
+ elem_name = name.sub('_', ':')
+ method_name = "#{name}_element#{postfix}"
+ add_to_element_method(method_name)
+ module_eval(<<-EOC, *get_file_and_line_from_caller(2))
+ def #{method_name}(need_convert=true, indent='')
+ #{yield(name, elem_name)}
+ end
+ private :#{method_name}
+EOC
+ end
+
+ def convert_attr_reader(*attrs)
+ attrs.each do |attr|
+ attr = attr.id2name if attr.kind_of?(Integer)
+ module_eval(<<-EOC, *get_file_and_line_from_caller(2))
+ def #{attr}
+ if @converter
+ @converter.convert(@#{attr})
+ else
+ @#{attr}
+ end
+ end
+EOC
+ end
+ end
+
+ def date_writer(name, type, disp_name=name)
+ module_eval(<<-EOC, *get_file_and_line_from_caller(2))
+ def #{name}=(new_value)
+ if new_value.nil? or new_value.kind_of?(Time)
+ @#{name} = new_value
+ else
+ if @do_validate
+ begin
+ @#{name} = Time.__send__('#{type}', new_value)
+ rescue ArgumentError
+ raise NotAvailableValueError.new('#{disp_name}', new_value)
+ end
+ else
+ @#{name} = nil
+ if /\\A\\s*\\z/ !~ new_value.to_s
+ begin
+ @#{name} = Time.parse(new_value)
+ rescue ArgumentError
+ end
+ end
+ end
+ end
+
+ # Is it need?
+ if @#{name}
+ class << @#{name}
+ undef_method(:to_s)
+ alias_method(:to_s, :#{type})
+ end
+ end
+
+ end
+EOC
+ end
+
+ def integer_writer(name, disp_name=name)
+ module_eval(<<-EOC, *get_file_and_line_from_caller(2))
+ def #{name}=(new_value)
+ if new_value.nil?
+ @#{name} = new_value
+ else
+ if @do_validate
+ begin
+ @#{name} = Integer(new_value)
+ rescue ArgumentError
+ raise NotAvailableValueError.new('#{disp_name}', new_value)
+ end
+ else
+ @#{name} = new_value.to_i
+ end
+ end
+ end
+EOC
+ end
+
+ def positive_integer_writer(name, disp_name=name)
+ module_eval(<<-EOC, *get_file_and_line_from_caller(2))
+ def #{name}=(new_value)
+ if new_value.nil?
+ @#{name} = new_value
+ else
+ if @do_validate
+ begin
+ tmp = Integer(new_value)
+ raise ArgumentError if tmp <= 0
+ @#{name} = tmp
+ rescue ArgumentError
+ raise NotAvailableValueError.new('#{disp_name}', new_value)
+ end
+ else
+ @#{name} = new_value.to_i
+ end
+ end
+ end
+EOC
+ end
+
+ def boolean_writer(name, disp_name=name)
+ module_eval(<<-EOC, *get_file_and_line_from_caller(2))
+ def #{name}=(new_value)
+ if new_value.nil?
+ @#{name} = new_value
+ else
+ if @do_validate and
+ ![true, false, "true", "false"].include?(new_value)
+ raise NotAvailableValueError.new('#{disp_name}', new_value)
+ end
+ if [true, false].include?(new_value)
+ @#{name} = new_value
+ else
+ @#{name} = new_value == "true"
+ end
+ end
+ end
+EOC
+ end
+
+ def def_children_accessor(accessor_name, plural_name)
+ module_eval(<<-EOC, *get_file_and_line_from_caller(2))
+ def #{plural_name}
+ @#{accessor_name}
+ end
+
+ def #{accessor_name}(*args)
+ if args.empty?
+ @#{accessor_name}.first
+ else
+ @#{accessor_name}[*args]
+ end
+ end
+
+ def #{accessor_name}=(*args)
+ warn("Warning:\#{caller.first.sub(/:in `.*'\z/, '')}: " \
+ "Don't use `#{accessor_name} = XXX'/`set_#{accessor_name}(XXX)'. " \
+ "Those APIs are not sense of Ruby. " \
+ "Use `#{plural_name} << XXX' instead of them.")
+ if args.size == 1
+ @#{accessor_name}.push(args[0])
+ else
+ @#{accessor_name}.__send__("[]=", *args)
+ end
+ end
+ alias_method(:set_#{accessor_name}, :#{accessor_name}=)
+EOC
+ end
+ end
+
+ class Element
+
+ extend BaseModel
+ include Utils
+
+ INDENT = " "
+
+ MUST_CALL_VALIDATORS = {}
+ MODELS = []
+ GET_ATTRIBUTES = []
+ HAVE_CHILDREN_ELEMENTS = []
+ TO_ELEMENT_METHODS = []
+ NEED_INITIALIZE_VARIABLES = []
+ PLURAL_FORMS = {}
+
+ class << self
+
+ def must_call_validators
+ MUST_CALL_VALIDATORS
+ end
+ def models
+ MODELS
+ end
+ def get_attributes
+ GET_ATTRIBUTES
+ end
+ def have_children_elements
+ HAVE_CHILDREN_ELEMENTS
+ end
+ def to_element_methods
+ TO_ELEMENT_METHODS
+ end
+ def need_initialize_variables
+ NEED_INITIALIZE_VARIABLES
+ end
+ def plural_forms
+ PLURAL_FORMS
+ end
+
+
+ def inherited(klass)
+ klass.const_set("MUST_CALL_VALIDATORS", {})
+ klass.const_set("MODELS", [])
+ klass.const_set("GET_ATTRIBUTES", [])
+ klass.const_set("HAVE_CHILDREN_ELEMENTS", [])
+ klass.const_set("TO_ELEMENT_METHODS", [])
+ klass.const_set("NEED_INITIALIZE_VARIABLES", [])
+ klass.const_set("PLURAL_FORMS", {})
+
+ klass.module_eval(<<-EOC)
+ public
+
+ @tag_name = name.split(/::/).last
+ @tag_name[0,1] = @tag_name[0,1].downcase
+ @have_content = false
+
+ def self.must_call_validators
+ super.merge(MUST_CALL_VALIDATORS)
+ end
+ def self.models
+ MODELS + super
+ end
+ def self.get_attributes
+ GET_ATTRIBUTES + super
+ end
+ def self.have_children_elements
+ HAVE_CHILDREN_ELEMENTS + super
+ end
+ def self.to_element_methods
+ TO_ELEMENT_METHODS + super
+ end
+ def self.need_initialize_variables
+ NEED_INITIALIZE_VARIABLES + super
+ end
+ def self.plural_forms
+ super.merge(PLURAL_FORMS)
+ end
+
+
+ def self.install_must_call_validator(prefix, uri)
+ MUST_CALL_VALIDATORS[uri] = prefix
+ end
+
+ def self.install_model(tag, uri, occurs=nil, getter=nil)
+ getter ||= tag
+ if m = MODELS.find {|t, u, o, g| t == tag and u == uri}
+ m[2] = occurs
+ else
+ MODELS << [tag, uri, occurs, getter]
+ end
+ end
+
+ def self.install_get_attribute(name, uri, required=true,
+ type=nil, disp_name=nil,
+ element_name=nil)
+ disp_name ||= name
+ element_name ||= name
+ def_corresponded_attr_writer name, type, disp_name
+ convert_attr_reader name
+ if type == :boolean and /^is/ =~ name
+ alias_method "\#{$POSTMATCH}?", name
+ end
+ GET_ATTRIBUTES << [name, uri, required, element_name]
+ add_need_initialize_variable(disp_name)
+ end
+
+ def self.def_corresponded_attr_writer(name, type=nil, disp_name=name)
+ case type
+ when :integer
+ integer_writer name, disp_name
+ when :positive_integer
+ positive_integer_writer name, disp_name
+ when :boolean
+ boolean_writer name, disp_name
+ when :w3cdtf, :rfc822, :rfc2822
+ date_writer name, type, disp_name
+ else
+ attr_writer name
+ end
+ end
+
+ def self.content_setup(type=nil)
+ def_corresponded_attr_writer "content", type
+ convert_attr_reader :content
+ @have_content = true
+ end
+
+ def self.have_content?
+ @have_content
+ end
+
+ def self.add_have_children_element(variable_name, plural_name)
+ HAVE_CHILDREN_ELEMENTS << [variable_name, plural_name]
+ end
+
+ def self.add_to_element_method(method_name)
+ TO_ELEMENT_METHODS << method_name
+ end
+
+ def self.add_need_initialize_variable(variable_name)
+ NEED_INITIALIZE_VARIABLES << variable_name
+ end
+
+ def self.add_plural_form(singular, plural)
+ PLURAL_FORMS[singular] = plural
+ end
+
+ EOC
+ end
+
+ def required_prefix
+ nil
+ end
+
+ def required_uri
+ ""
+ end
+
+ def install_ns(prefix, uri)
+ if self::NSPOOL.has_key?(prefix)
+ raise OverlappedPrefixError.new(prefix)
+ end
+ self::NSPOOL[prefix] = uri
+ end
+
+ def tag_name
+ @tag_name
+ end
+ end
+
+ attr_accessor :do_validate
+
+ def initialize(do_validate=true, attrs={})
+ @converter = nil
+ @do_validate = do_validate
+ initialize_variables(attrs)
+ end
+
+ def tag_name
+ self.class.tag_name
+ end
+
+ def full_name
+ tag_name
+ end
+
+ def converter=(converter)
+ @converter = converter
+ targets = children.dup
+ self.class.have_children_elements.each do |variable_name, plural_name|
+ targets.concat(__send__(plural_name))
+ end
+ targets.each do |target|
+ target.converter = converter unless target.nil?
+ end
+ end
+
+ def convert(value)
+ if @converter
+ @converter.convert(value)
+ else
+ value
+ end
+ end
+
+ def validate(ignore_unknown_element=true)
+ validate_attribute
+ __validate(ignore_unknown_element)
+ end
+
+ def validate_for_stream(tags, ignore_unknown_element=true)
+ validate_attribute
+ __validate(ignore_unknown_element, tags, false)
+ end
+
+ def setup_maker(maker)
+ target = maker_target(maker)
+ unless target.nil?
+ setup_maker_attributes(target)
+ setup_maker_element(target)
+ setup_maker_elements(target)
+ end
+ end
+
+ def to_s(need_convert=true, indent='')
+ if self.class.have_content?
+ return "" unless @content
+ rv = tag(indent) do |next_indent|
+ h(@content)
+ end
+ else
+ rv = tag(indent) do |next_indent|
+ self.class.to_element_methods.collect do |method_name|
+ __send__(method_name, false, next_indent)
+ end
+ end
+ end
+ rv = convert(rv) if need_convert
+ rv
+ end
+
+ private
+ def initialize_variables(attrs)
+ normalized_attrs = {}
+ attrs.each do |key, value|
+ normalized_attrs[key.to_s] = value
+ end
+ self.class.need_initialize_variables.each do |variable_name|
+ value = normalized_attrs[variable_name.to_s]
+ if value
+ __send__("#{variable_name}=", value)
+ else
+ instance_eval("@#{variable_name} = nil")
+ end
+ end
+ initialize_have_children_elements
+ @content = "" if self.class.have_content?
+ end
+
+ def initialize_have_children_elements
+ self.class.have_children_elements.each do |variable_name, plural_name|
+ instance_eval("@#{variable_name} = []")
+ end
+ end
+
+ def tag(indent, additional_attrs={}, &block)
+ next_indent = indent + INDENT
+
+ attrs = collect_attrs
+ return "" if attrs.nil?
+
+ attrs.update(additional_attrs)
+ start_tag = make_start_tag(indent, next_indent, attrs)
+
+ if block
+ content = block.call(next_indent)
+ else
+ content = []
+ end
+
+ if content.is_a?(String)
+ content = [content]
+ start_tag << ">"
+ end_tag = "</#{full_name}>"
+ else
+ content = content.reject{|x| x.empty?}
+ if content.empty?
+ end_tag = "/>"
+ else
+ start_tag << ">\n"
+ end_tag = "\n#{indent}</#{full_name}>"
+ end
+ end
+
+ start_tag + content.join("\n") + end_tag
+ end
+
+ def make_start_tag(indent, next_indent, attrs)
+ start_tag = ["#{indent}<#{full_name}"]
+ unless attrs.empty?
+ start_tag << attrs.collect do |key, value|
+ %Q[#{h key}="#{h value}"]
+ end.join("\n#{next_indent}")
+ end
+ start_tag.join(" ")
+ end
+
+ def collect_attrs
+ attrs = {}
+ _attrs.each do |name, required, alias_name|
+ value = __send__(alias_name || name)
+ return nil if required and value.nil?
+ next if value.nil?
+ return nil if attrs.has_key?(name)
+ attrs[name] = value
+ end
+ attrs
+ end
+
+ def tag_name_with_prefix(prefix)
+ "#{prefix}:#{tag_name}"
+ end
+
+ # For backward compatibility
+ def calc_indent
+ ''
+ end
+
+ def maker_target(maker)
+ nil
+ end
+
+ def setup_maker_attributes(target)
+ end
+
+ def setup_maker_element(target)
+ self.class.need_initialize_variables.each do |var|
+ value = __send__(var)
+ if value.respond_to?("setup_maker") and
+ !not_need_to_call_setup_maker_variables.include?(var)
+ value.setup_maker(target)
+ else
+ setter = "#{var}="
+ if target.respond_to?(setter)
+ target.__send__(setter, value)
+ end
+ end
+ end
+ end
+
+ def not_need_to_call_setup_maker_variables
+ []
+ end
+
+ def setup_maker_elements(parent)
+ self.class.have_children_elements.each do |name, plural_name|
+ if parent.respond_to?(plural_name)
+ target = parent.__send__(plural_name)
+ __send__(plural_name).each do |elem|
+ elem.setup_maker(target)
+ end
+ end
+ end
+ end
+
+ def set_next_element(tag_name, next_element)
+ klass = next_element.class
+ prefix = ""
+ prefix << "#{klass.required_prefix}_" if klass.required_prefix
+ key = "#{prefix}#{tag_name}"
+ if self.class.plural_forms.has_key?(key)
+ ary = __send__("#{self.class.plural_forms[key]}")
+ ary << next_element
+ else
+ __send__("#{prefix}#{tag_name}=", next_element)
+ end
+ end
+
+ def children
+ rv = []
+ self.class.models.each do |name, uri, occurs, getter|
+ value = __send__(getter)
+ next if value.nil?
+ value = [value] unless value.is_a?(Array)
+ value.each do |v|
+ rv << v if v.is_a?(Element)
+ end
+ end
+ rv
+ end
+
+ def _tags
+ rv = []
+ self.class.models.each do |name, uri, occurs, getter|
+ value = __send__(getter)
+ next if value.nil?
+ if value.is_a?(Array)
+ rv.concat([[uri, name]] * value.size)
+ else
+ rv << [uri, name]
+ end
+ end
+ rv
+ end
+
+ def _attrs
+ self.class.get_attributes.collect do |name, uri, required, element_name|
+ [element_name, required, name]
+ end
+ end
+
+ def __validate(ignore_unknown_element, tags=_tags, recursive=true)
+ if recursive
+ children.compact.each do |child|
+ child.validate
+ end
+ end
+ must_call_validators = self.class.must_call_validators
+ tags = tag_filter(tags.dup)
+ p tags if DEBUG
+ must_call_validators.each do |uri, prefix|
+ _validate(ignore_unknown_element, tags[uri], uri)
+ meth = "#{prefix}_validate"
+ if respond_to?(meth, true)
+ __send__(meth, ignore_unknown_element, tags[uri], uri)
+ end
+ end
+ end
+
+ def validate_attribute
+ _attrs.each do |a_name, required, alias_name|
+ if required and __send__(alias_name || a_name).nil?
+ raise MissingAttributeError.new(tag_name, a_name)
+ end
+ end
+ end
+
+ def _validate(ignore_unknown_element, tags, uri, models=self.class.models)
+ count = 1
+ do_redo = false
+ not_shift = false
+ tag = nil
+ models = models.find_all {|model| model[1] == uri}
+ element_names = models.collect {|model| model[0]}
+ if tags
+ tags_size = tags.size
+ tags = tags.sort_by {|x| element_names.index(x) || tags_size}
+ end
+
+ models.each_with_index do |model, i|
+ name, model_uri, occurs, getter = model
+
+ if DEBUG
+ p "before"
+ p tags
+ p model
+ end
+
+ if not_shift
+ not_shift = false
+ elsif tags
+ tag = tags.shift
+ end
+
+ if DEBUG
+ p "mid"
+ p count
+ end
+
+ case occurs
+ when '?'
+ if count > 2
+ raise TooMuchTagError.new(name, tag_name)
+ else
+ if name == tag
+ do_redo = true
+ else
+ not_shift = true
+ end
+ end
+ when '*'
+ if name == tag
+ do_redo = true
+ else
+ not_shift = true
+ end
+ when '+'
+ if name == tag
+ do_redo = true
+ else
+ if count > 1
+ not_shift = true
+ else
+ raise MissingTagError.new(name, tag_name)
+ end
+ end
+ else
+ if name == tag
+ if models[i+1] and models[i+1][0] != name and
+ tags and tags.first == name
+ raise TooMuchTagError.new(name, tag_name)
+ end
+ else
+ raise MissingTagError.new(name, tag_name)
+ end
+ end
+
+ if DEBUG
+ p "after"
+ p not_shift
+ p do_redo
+ p tag
+ end
+
+ if do_redo
+ do_redo = false
+ count += 1
+ redo
+ else
+ count = 1
+ end
+
+ end
+
+ if !ignore_unknown_element and !tags.nil? and !tags.empty?
+ raise NotExpectedTagError.new(tags.first, uri, tag_name)
+ end
+
+ end
+
+ def tag_filter(tags)
+ rv = {}
+ tags.each do |tag|
+ rv[tag[0]] = [] unless rv.has_key?(tag[0])
+ rv[tag[0]].push(tag[1])
+ end
+ rv
+ end
+
+ end
+
+ module RootElementMixin
+
+ include XMLStyleSheetMixin
+
+ attr_reader :output_encoding
+
+ def initialize(rss_version, version=nil, encoding=nil, standalone=nil)
+ super()
+ @rss_version = rss_version
+ @version = version || '1.0'
+ @encoding = encoding
+ @standalone = standalone
+ @output_encoding = nil
+ end
+
+ def output_encoding=(enc)
+ @output_encoding = enc
+ self.converter = Converter.new(@output_encoding, @encoding)
+ end
+
+ def setup_maker(maker)
+ maker.version = version
+ maker.encoding = encoding
+ maker.standalone = standalone
+
+ xml_stylesheets.each do |xss|
+ xss.setup_maker(maker)
+ end
+
+ setup_maker_elements(maker)
+ end
+
+ def to_xml(version=nil, &block)
+ if version.nil? or version == @rss_version
+ to_s
+ else
+ RSS::Maker.make(version) do |maker|
+ setup_maker(maker)
+ block.call(maker) if block
+ end.to_s
+ end
+ end
+
+ private
+ def tag(indent, attrs={}, &block)
+ rv = xmldecl + xml_stylesheet_pi
+ rv << super(indent, ns_declarations.merge(attrs), &block)
+ rv
+ end
+
+ def xmldecl
+ rv = %Q[<?xml version="#{@version}"]
+ if @output_encoding or @encoding
+ rv << %Q[ encoding="#{@output_encoding or @encoding}"]
+ end
+ rv << %Q[ standalone="yes"] if @standalone
+ rv << "?>\n"
+ rv
+ end
+
+ def ns_declarations
+ decls = {}
+ self.class::NSPOOL.collect do |prefix, uri|
+ prefix = ":#{prefix}" unless prefix.empty?
+ decls["xmlns#{prefix}"] = uri
+ end
+ decls
+ end
+
+ def setup_maker_elements(maker)
+ channel.setup_maker(maker) if channel
+ image.setup_maker(maker) if image
+ textinput.setup_maker(maker) if textinput
+ super(maker)
+ end
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rss/syndication.rb b/ruby_1_8_5/lib/rss/syndication.rb
new file mode 100644
index 0000000000..93d35c89a7
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/syndication.rb
@@ -0,0 +1,64 @@
+require "rss/1.0"
+
+module RSS
+
+ SY_PREFIX = 'sy'
+ SY_URI = "http://purl.org/rss/1.0/modules/syndication/"
+
+ RDF.install_ns(SY_PREFIX, SY_URI)
+
+ module SyndicationModel
+
+ extend BaseModel
+
+ ELEMENTS = []
+
+ def self.append_features(klass)
+ super
+
+ klass.install_must_call_validator(SY_PREFIX, SY_URI)
+ klass.module_eval do
+ [
+ ["updatePeriod"],
+ ["updateFrequency", :positive_integer]
+ ].each do |name, type|
+ install_text_element(name, SY_URI, "?",
+ "#{SY_PREFIX}_#{name}", type,
+ "#{SY_PREFIX}:#{name}")
+ end
+
+ %w(updateBase).each do |name|
+ install_date_element(name, SY_URI, "?",
+ "#{SY_PREFIX}_#{name}", 'w3cdtf', name)
+ end
+
+ alias_method(:_sy_updatePeriod=, :sy_updatePeriod=)
+ def sy_updatePeriod=(new_value)
+ new_value = new_value.strip
+ validate_sy_updatePeriod(new_value) if @do_validate
+ self._sy_updatePeriod = new_value
+ end
+ end
+ end
+
+ private
+ SY_UPDATEPERIOD_AVAILABLE_VALUES = %w(hourly daily weekly monthly yearly)
+ def validate_sy_updatePeriod(value)
+ unless SY_UPDATEPERIOD_AVAILABLE_VALUES.include?(value)
+ raise NotAvailableValueError.new("updatePeriod", value)
+ end
+ end
+ end
+
+ class RDF
+ class Channel; include SyndicationModel; end
+ end
+
+ prefix_size = SY_PREFIX.size + 1
+ SyndicationModel::ELEMENTS.uniq!
+ SyndicationModel::ELEMENTS.each do |full_name|
+ name = full_name[prefix_size..-1]
+ BaseListener.install_get_text_element(SY_URI, name, "#{full_name}=")
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rss/taxonomy.rb b/ruby_1_8_5/lib/rss/taxonomy.rb
new file mode 100644
index 0000000000..8caa25e2a4
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/taxonomy.rb
@@ -0,0 +1,145 @@
+require "rss/1.0"
+require "rss/dublincore"
+
+module RSS
+
+ TAXO_PREFIX = "taxo"
+ TAXO_URI = "http://purl.org/rss/1.0/modules/taxonomy/"
+
+ RDF.install_ns(TAXO_PREFIX, TAXO_URI)
+
+ TAXO_ELEMENTS = []
+
+ %w(link).each do |name|
+ full_name = "#{TAXO_PREFIX}_#{name}"
+ BaseListener.install_get_text_element(TAXO_URI, name, "#{full_name}=")
+ TAXO_ELEMENTS << "#{TAXO_PREFIX}_#{name}"
+ end
+
+ %w(topic topics).each do |name|
+ class_name = Utils.to_class_name(name)
+ BaseListener.install_class_name(TAXO_URI, name, "Taxonomy#{class_name}")
+ TAXO_ELEMENTS << "#{TAXO_PREFIX}_#{name}"
+ end
+
+ module TaxonomyTopicsModel
+ extend BaseModel
+
+ def self.append_features(klass)
+ super
+
+ klass.install_must_call_validator(TAXO_PREFIX, TAXO_URI)
+ %w(topics).each do |name|
+ klass.install_have_child_element(name, TAXO_URI, "?",
+ "#{TAXO_PREFIX}_#{name}")
+ end
+ end
+
+ class TaxonomyTopics < Element
+ include RSS10
+
+ Bag = ::RSS::RDF::Bag
+
+ class << self
+ def required_prefix
+ TAXO_PREFIX
+ end
+
+ def required_uri
+ TAXO_URI
+ end
+ end
+
+ @tag_name = "topics"
+
+ install_have_child_element("Bag", RDF::URI, nil)
+ install_must_call_validator('rdf', RDF::URI)
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.Bag = args[0]
+ end
+ self.Bag ||= Bag.new
+ end
+
+ def full_name
+ tag_name_with_prefix(TAXO_PREFIX)
+ end
+
+ def maker_target(target)
+ target.taxo_topics
+ end
+
+ def resources
+ if @Bag
+ @Bag.lis.collect do |li|
+ li.resource
+ end
+ else
+ []
+ end
+ end
+ end
+ end
+
+ module TaxonomyTopicModel
+ extend BaseModel
+
+ def self.append_features(klass)
+ super
+ var_name = "#{TAXO_PREFIX}_topic"
+ klass.install_have_children_element("topic", TAXO_URI, "*", var_name)
+ end
+
+ class TaxonomyTopic < Element
+ include RSS10
+
+ include DublinCoreModel
+ include TaxonomyTopicsModel
+
+ class << self
+ def required_prefix
+ TAXO_PREFIX
+ end
+
+ def required_uri
+ TAXO_URI
+ end
+ end
+
+ @tag_name = "topic"
+
+ install_get_attribute("about", ::RSS::RDF::URI, true, nil, nil,
+ "#{RDF::PREFIX}:about")
+ install_text_element("link", TAXO_URI, "?", "#{TAXO_PREFIX}_link")
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.about = args[0]
+ end
+ end
+
+ def full_name
+ tag_name_with_prefix(TAXO_PREFIX)
+ end
+
+ def maker_target(target)
+ target.new_taxo_topic
+ end
+ end
+ end
+
+ class RDF
+ include TaxonomyTopicModel
+ class Channel
+ include TaxonomyTopicsModel
+ end
+ class Item; include TaxonomyTopicsModel; end
+ end
+end
diff --git a/ruby_1_8_5/lib/rss/trackback.rb b/ruby_1_8_5/lib/rss/trackback.rb
new file mode 100644
index 0000000000..ee2491f332
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/trackback.rb
@@ -0,0 +1,288 @@
+require 'rss/1.0'
+require 'rss/2.0'
+
+module RSS
+
+ TRACKBACK_PREFIX = 'trackback'
+ TRACKBACK_URI = 'http://madskills.com/public/xml/rss/module/trackback/'
+
+ RDF.install_ns(TRACKBACK_PREFIX, TRACKBACK_URI)
+ Rss.install_ns(TRACKBACK_PREFIX, TRACKBACK_URI)
+
+ module TrackBackUtils
+ private
+ def trackback_validate(ignore_unknown_element, tags, uri)
+ return if tags.nil?
+ if tags.find {|tag| tag == "about"} and
+ !tags.find {|tag| tag == "ping"}
+ raise MissingTagError.new("#{TRACKBACK_PREFIX}:ping", tag_name)
+ end
+ end
+ end
+
+ module BaseTrackBackModel
+
+ ELEMENTS = %w(ping about)
+
+ def append_features(klass)
+ super
+
+ unless klass.class == Module
+ klass.module_eval {include TrackBackUtils}
+
+ klass.install_must_call_validator(TRACKBACK_PREFIX, TRACKBACK_URI)
+ %w(ping).each do |name|
+ var_name = "#{TRACKBACK_PREFIX}_#{name}"
+ klass_name = "TrackBack#{Utils.to_class_name(name)}"
+ klass.install_have_child_element(name, TRACKBACK_URI, "?", var_name)
+ klass.module_eval(<<-EOC, __FILE__, __LINE__)
+ remove_method :#{var_name}
+ def #{var_name}
+ @#{var_name} and @#{var_name}.value
+ end
+
+ remove_method :#{var_name}=
+ def #{var_name}=(value)
+ @#{var_name} = Utils.new_with_value_if_need(#{klass_name}, value)
+ end
+ EOC
+ end
+
+ [%w(about s)].each do |name, postfix|
+ var_name = "#{TRACKBACK_PREFIX}_#{name}"
+ klass_name = "TrackBack#{Utils.to_class_name(name)}"
+ klass.install_have_children_element(name, TRACKBACK_URI, "*",
+ var_name)
+ klass.module_eval(<<-EOC, __FILE__, __LINE__)
+ remove_method :#{var_name}
+ def #{var_name}(*args)
+ if args.empty?
+ @#{var_name}.first and @#{var_name}.first.value
+ else
+ ret = @#{var_name}.__send__("[]", *args)
+ if ret.is_a?(Array)
+ ret.collect {|x| x.value}
+ else
+ ret.value
+ end
+ end
+ end
+
+ remove_method :#{var_name}=
+ remove_method :set_#{var_name}
+ def #{var_name}=(*args)
+ if args.size == 1
+ item = Utils.new_with_value_if_need(#{klass_name}, args[0])
+ @#{var_name}.push(item)
+ else
+ new_val = args.last
+ if new_val.is_a?(Array)
+ new_val = new_value.collect do |val|
+ Utils.new_with_value_if_need(#{klass_name}, val)
+ end
+ else
+ new_val = Utils.new_with_value_if_need(#{klass_name}, new_val)
+ end
+ @#{var_name}.__send__("[]=", *(args[0..-2] + [new_val]))
+ end
+ end
+ alias set_#{var_name} #{var_name}=
+ EOC
+ end
+ end
+ end
+ end
+
+ module TrackBackModel10
+ extend BaseModel
+ extend BaseTrackBackModel
+
+ class TrackBackPing < Element
+ include RSS10
+
+ class << self
+
+ def required_prefix
+ TRACKBACK_PREFIX
+ end
+
+ def required_uri
+ TRACKBACK_URI
+ end
+
+ end
+
+ @tag_name = "ping"
+
+ [
+ ["resource", ::RSS::RDF::URI, true]
+ ].each do |name, uri, required|
+ install_get_attribute(name, uri, required, nil, nil,
+ "#{::RSS::RDF::PREFIX}:#{name}")
+ end
+
+ alias_method(:value, :resource)
+ alias_method(:value=, :resource=)
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.resource = args[0]
+ end
+ end
+
+ def full_name
+ tag_name_with_prefix(TRACKBACK_PREFIX)
+ end
+ end
+
+ class TrackBackAbout < Element
+ include RSS10
+
+ class << self
+
+ def required_prefix
+ TRACKBACK_PREFIX
+ end
+
+ def required_uri
+ TRACKBACK_URI
+ end
+
+ end
+
+ @tag_name = "about"
+
+ [
+ ["resource", ::RSS::RDF::URI, true]
+ ].each do |name, uri, required|
+ install_get_attribute(name, uri, required, nil, nil,
+ "#{::RSS::RDF::PREFIX}:#{name}")
+ end
+
+ alias_method(:value, :resource)
+ alias_method(:value=, :resource=)
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.resource = args[0]
+ end
+ end
+
+ def full_name
+ tag_name_with_prefix(TRACKBACK_PREFIX)
+ end
+
+ private
+ def maker_target(abouts)
+ abouts.new_about
+ end
+
+ def setup_maker_attributes(about)
+ about.resource = self.resource
+ end
+
+ end
+ end
+
+ module TrackBackModel20
+ extend BaseModel
+ extend BaseTrackBackModel
+
+ class TrackBackPing < Element
+ include RSS09
+
+ @tag_name = "ping"
+
+ content_setup
+
+ class << self
+
+ def required_prefix
+ TRACKBACK_PREFIX
+ end
+
+ def required_uri
+ TRACKBACK_URI
+ end
+
+ end
+
+ alias_method(:value, :content)
+ alias_method(:value=, :content=)
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.content = args[0]
+ end
+ end
+
+ def full_name
+ tag_name_with_prefix(TRACKBACK_PREFIX)
+ end
+
+ end
+
+ class TrackBackAbout < Element
+ include RSS09
+
+ @tag_name = "about"
+
+ content_setup
+
+ class << self
+
+ def required_prefix
+ TRACKBACK_PREFIX
+ end
+
+ def required_uri
+ TRACKBACK_URI
+ end
+
+ end
+
+ alias_method(:value, :content)
+ alias_method(:value=, :content=)
+
+ def initialize(*args)
+ if Utils.element_initialize_arguments?(args)
+ super
+ else
+ super()
+ self.content = args[0]
+ end
+ end
+
+ def full_name
+ tag_name_with_prefix(TRACKBACK_PREFIX)
+ end
+
+ end
+ end
+
+ class RDF
+ class Item; include TrackBackModel10; end
+ end
+
+ class Rss
+ class Channel
+ class Item; include TrackBackModel20; end
+ end
+ end
+
+ BaseTrackBackModel::ELEMENTS.each do |name|
+ class_name = Utils.to_class_name(name)
+ BaseListener.install_class_name(TRACKBACK_URI, name,
+ "TrackBack#{class_name}")
+ end
+
+ BaseTrackBackModel::ELEMENTS.collect! {|name| "#{TRACKBACK_PREFIX}_#{name}"}
+end
diff --git a/ruby_1_8_5/lib/rss/utils.rb b/ruby_1_8_5/lib/rss/utils.rb
new file mode 100644
index 0000000000..b242a72292
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/utils.rb
@@ -0,0 +1,37 @@
+module RSS
+ module Utils
+ module_function
+
+ # Convert a name_with_underscores to CamelCase.
+ def to_class_name(name)
+ name.split(/_/).collect do |part|
+ "#{part[0, 1].upcase}#{part[1..-1]}"
+ end.join("")
+ end
+
+ def get_file_and_line_from_caller(i=0)
+ file, line, = caller[i].split(':')
+ [file, line.to_i]
+ end
+
+ # escape '&', '"', '<' and '>' for use in HTML.
+ def html_escape(s)
+ s.to_s.gsub(/&/, "&amp;").gsub(/\"/, "&quot;").gsub(/>/, "&gt;").gsub(/</, "&lt;")
+ end
+ alias h html_escape
+
+ # If +value+ is an instance of class +klass+, return it, else
+ # create a new instance of +klass+ with value +value+.
+ def new_with_value_if_need(klass, value)
+ if value.is_a?(klass)
+ value
+ else
+ klass.new(value)
+ end
+ end
+
+ def element_initialize_arguments?(args)
+ [true, false].include?(args[0]) and args[1].is_a?(Hash)
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/rss/xml-stylesheet.rb b/ruby_1_8_5/lib/rss/xml-stylesheet.rb
new file mode 100644
index 0000000000..66e3161dd0
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/xml-stylesheet.rb
@@ -0,0 +1,101 @@
+require "rss/utils"
+
+module RSS
+
+ module XMLStyleSheetMixin
+ attr_accessor :xml_stylesheets
+ def initialize(*args)
+ super
+ @xml_stylesheets = []
+ end
+
+ private
+ def xml_stylesheet_pi
+ xsss = @xml_stylesheets.collect do |xss|
+ pi = xss.to_s
+ pi = nil if /\A\s*\z/ =~ pi
+ pi
+ end.compact
+ xsss.push("") unless xsss.empty?
+ xsss.join("\n")
+ end
+ end
+
+ class XMLStyleSheet
+
+ include Utils
+
+ ATTRIBUTES = %w(href type title media charset alternate)
+
+ GUESS_TABLE = {
+ "xsl" => "text/xsl",
+ "css" => "text/css",
+ }
+
+ attr_accessor(*ATTRIBUTES)
+ attr_accessor(:do_validate)
+ def initialize(*attrs)
+ @do_validate = true
+ ATTRIBUTES.each do |attr|
+ __send__("#{attr}=", nil)
+ end
+ vars = ATTRIBUTES.dup
+ vars.unshift(:do_validate)
+ attrs.each do |name, value|
+ if vars.include?(name.to_s)
+ __send__("#{name}=", value)
+ end
+ end
+ end
+
+ def to_s
+ rv = ""
+ if @href
+ rv << %Q[<?xml-stylesheet]
+ ATTRIBUTES.each do |name|
+ if __send__(name)
+ rv << %Q[ #{name}="#{h __send__(name)}"]
+ end
+ end
+ rv << %Q[?>]
+ end
+ rv
+ end
+
+ remove_method(:href=)
+ def href=(value)
+ @href = value
+ if @href and @type.nil?
+ @type = guess_type(@href)
+ end
+ @href
+ end
+
+ remove_method(:alternate=)
+ def alternate=(value)
+ if value.nil? or /\A(?:yes|no)\z/ =~ value
+ @alternate = value
+ else
+ if @do_validate
+ args = ["?xml-stylesheet?", %Q[alternate="#{value}"]]
+ raise NotAvailableValueError.new(*args)
+ end
+ end
+ @alternate
+ end
+
+ def setup_maker(maker)
+ xss = maker.xml_stylesheets.new_xml_stylesheet
+ ATTRIBUTES.each do |attr|
+ xss.__send__("#{attr}=", __send__(attr))
+ end
+ end
+
+ private
+ def guess_type(filename)
+ /\.([^.]+)$/ =~ filename
+ GUESS_TABLE[$1]
+ end
+
+ end
+end
diff --git a/ruby_1_8_5/lib/rss/xmlparser.rb b/ruby_1_8_5/lib/rss/xmlparser.rb
new file mode 100644
index 0000000000..3dfe7d461a
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/xmlparser.rb
@@ -0,0 +1,93 @@
+begin
+ require "xml/parser"
+rescue LoadError
+ require "xmlparser"
+end
+
+begin
+ require "xml/encoding-ja"
+rescue LoadError
+ require "xmlencoding-ja"
+ if defined?(Kconv)
+ module XMLEncoding_ja
+ class SJISHandler
+ include Kconv
+ end
+ end
+ end
+end
+
+module XML
+ class Parser
+ unless defined?(Error)
+ Error = ::XMLParserError
+ end
+ end
+end
+
+module RSS
+
+ class REXMLLikeXMLParser < ::XML::Parser
+
+ include ::XML::Encoding_ja
+
+ def listener=(listener)
+ @listener = listener
+ end
+
+ def startElement(name, attrs)
+ @listener.tag_start(name, attrs)
+ end
+
+ def endElement(name)
+ @listener.tag_end(name)
+ end
+
+ def character(data)
+ @listener.text(data)
+ end
+
+ def xmlDecl(version, encoding, standalone)
+ @listener.xmldecl(version, encoding, standalone == 1)
+ end
+
+ def processingInstruction(target, content)
+ @listener.instruction(target, content)
+ end
+
+ end
+
+ class XMLParserParser < BaseParser
+
+ class << self
+ def listener
+ XMLParserListener
+ end
+ end
+
+ private
+ def _parse
+ begin
+ parser = REXMLLikeXMLParser.new
+ parser.listener = @listener
+ parser.parse(@rss)
+ rescue ::XML::Parser::Error => e
+ raise NotWellFormedError.new(parser.line){e.message}
+ end
+ end
+
+ end
+
+ class XMLParserListener < BaseListener
+
+ include ListenerMixin
+
+ def xmldecl(version, encoding, standalone)
+ super
+ # Encoding is converted to UTF-8 when XMLParser parses XML.
+ @encoding = 'UTF-8'
+ end
+
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rss/xmlscanner.rb b/ruby_1_8_5/lib/rss/xmlscanner.rb
new file mode 100644
index 0000000000..61b9fa6bf4
--- /dev/null
+++ b/ruby_1_8_5/lib/rss/xmlscanner.rb
@@ -0,0 +1,121 @@
+require 'xmlscan/scanner'
+require 'stringio'
+
+module RSS
+
+ class XMLScanParser < BaseParser
+
+ class << self
+ def listener
+ XMLScanListener
+ end
+ end
+
+ private
+ def _parse
+ begin
+ if @rss.is_a?(String)
+ input = StringIO.new(@rss)
+ else
+ input = @rss
+ end
+ scanner = XMLScan::XMLScanner.new(@listener)
+ scanner.parse(input)
+ rescue XMLScan::Error => e
+ lineno = e.lineno || scanner.lineno || input.lineno
+ raise NotWellFormedError.new(lineno){e.message}
+ end
+ end
+
+ end
+
+ class XMLScanListener < BaseListener
+
+ include XMLScan::Visitor
+ include ListenerMixin
+
+ ENTITIES = {
+ 'lt' => '<',
+ 'gt' => '>',
+ 'amp' => '&',
+ 'quot' => '"',
+ 'apos' => '\''
+ }
+
+ def on_xmldecl_version(str)
+ @version = str
+ end
+
+ def on_xmldecl_encoding(str)
+ @encoding = str
+ end
+
+ def on_xmldecl_standalone(str)
+ @standalone = str
+ end
+
+ def on_xmldecl_end
+ xmldecl(@version, @encoding, @standalone == "yes")
+ end
+
+ alias_method(:on_pi, :instruction)
+ alias_method(:on_chardata, :text)
+ alias_method(:on_cdata, :text)
+
+ def on_etag(name)
+ tag_end(name)
+ end
+
+ def on_entityref(ref)
+ text(entity(ref))
+ end
+
+ def on_charref(code)
+ text([code].pack('U'))
+ end
+
+ alias_method(:on_charref_hex, :on_charref)
+
+ def on_stag(name)
+ @attrs = {}
+ end
+
+ def on_attribute(name)
+ @attrs[name] = @current_attr = ''
+ end
+
+ def on_attr_value(str)
+ @current_attr << str
+ end
+
+ def on_attr_entityref(ref)
+ @current_attr << entity(ref)
+ end
+
+ def on_attr_charref(code)
+ @current_attr << [code].pack('U')
+ end
+
+ alias_method(:on_attr_charref_hex, :on_attr_charref)
+
+ def on_stag_end(name)
+ tag_start(name, @attrs)
+ end
+
+ def on_stag_end_empty(name)
+ tag_start(name, @attrs)
+ tag_end(name)
+ end
+
+ private
+ def entity(ref)
+ ent = ENTITIES[ref]
+ if ent
+ ent
+ else
+ wellformed_error("undefined entity: #{ref}")
+ end
+ end
+ end
+
+end
diff --git a/ruby_1_8_5/lib/rubyunit.rb b/ruby_1_8_5/lib/rubyunit.rb
new file mode 100644
index 0000000000..1aca37864f
--- /dev/null
+++ b/ruby_1_8_5/lib/rubyunit.rb
@@ -0,0 +1,6 @@
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+require 'runit/testcase'
+require 'test/unit'
diff --git a/ruby_1_8_5/lib/runit/assert.rb b/ruby_1_8_5/lib/runit/assert.rb
new file mode 100644
index 0000000000..c752b19a25
--- /dev/null
+++ b/ruby_1_8_5/lib/runit/assert.rb
@@ -0,0 +1,73 @@
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+require 'test/unit/assertions'
+require 'runit/error'
+
+module RUNIT
+ module Assert
+ include Test::Unit::Assertions
+
+ def setup_assert
+ end
+
+ def assert_no_exception(*args, &block)
+ assert_nothing_raised(*args, &block)
+ end
+
+ # To deal with the fact that RubyUnit does not check that the
+ # regular expression is, indeed, a regular expression, if it is
+ # not, we do our own assertion using the same semantics as
+ # RubyUnit
+ def assert_match(actual_string, expected_re, message="")
+ _wrap_assertion {
+ full_message = build_message(message, "Expected <?> to match <?>", actual_string, expected_re)
+ assert_block(full_message) {
+ expected_re =~ actual_string
+ }
+ Regexp.last_match
+ }
+ end
+
+ def assert_not_nil(actual, message="")
+ assert(!actual.nil?, message)
+ end
+
+ def assert_not_match(actual_string, expected_re, message="")
+ assert_no_match(expected_re, actual_string, message)
+ end
+
+ def assert_matches(*args)
+ assert_match(*args)
+ end
+
+ def assert_fail(message="")
+ flunk(message)
+ end
+
+ def assert_equal_float(expected, actual, delta, message="")
+ assert_in_delta(expected, actual, delta, message)
+ end
+
+ def assert_send(object, method, *args)
+ super([object, method, *args])
+ end
+
+ def assert_exception(exception, message="", &block)
+ assert_raises(exception, message, &block)
+ end
+
+ def assert_respond_to(method, object, message="")
+ if (called_internally?)
+ super
+ else
+ super(object, method, message)
+ end
+ end
+
+ def called_internally?
+ /assertions\.rb/.match(caller[1])
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/runit/cui/testrunner.rb b/ruby_1_8_5/lib/runit/cui/testrunner.rb
new file mode 100644
index 0000000000..d521ec16ad
--- /dev/null
+++ b/ruby_1_8_5/lib/runit/cui/testrunner.rb
@@ -0,0 +1,51 @@
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+require 'test/unit/ui/console/testrunner'
+require 'runit/testresult'
+
+module RUNIT
+ module CUI
+ class TestRunner < Test::Unit::UI::Console::TestRunner
+ @@quiet_mode = false
+
+ def self.run(suite)
+ self.new().run(suite)
+ end
+
+ def initialize
+ super nil
+ end
+
+ def run(suite, quiet_mode=@@quiet_mode)
+ @suite = suite
+ def @suite.suite
+ self
+ end
+ @output_level = (quiet_mode ? Test::Unit::UI::PROGRESS_ONLY : Test::Unit::UI::VERBOSE)
+ start
+ end
+
+ def create_mediator(suite)
+ mediator = Test::Unit::UI::TestRunnerMediator.new(suite)
+ class << mediator
+ attr_writer :result_delegate
+ def create_result
+ return @result_delegate.create_result
+ end
+ end
+ mediator.result_delegate = self
+ return mediator
+ end
+
+ def create_result
+ return RUNIT::TestResult.new
+ end
+
+ def self.quiet_mode=(boolean)
+ @@quiet_mode = boolean
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/runit/error.rb b/ruby_1_8_5/lib/runit/error.rb
new file mode 100644
index 0000000000..4a727fb02b
--- /dev/null
+++ b/ruby_1_8_5/lib/runit/error.rb
@@ -0,0 +1,9 @@
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+require 'test/unit/assertionfailederror.rb'
+
+module RUNIT
+ AssertionFailedError = Test::Unit::AssertionFailedError
+end
diff --git a/ruby_1_8_5/lib/runit/testcase.rb b/ruby_1_8_5/lib/runit/testcase.rb
new file mode 100644
index 0000000000..4576cb8644
--- /dev/null
+++ b/ruby_1_8_5/lib/runit/testcase.rb
@@ -0,0 +1,45 @@
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+require 'runit/testresult'
+require 'runit/testsuite'
+require 'runit/assert'
+require 'runit/error'
+require 'test/unit/testcase'
+
+module RUNIT
+ class TestCase < Test::Unit::TestCase
+ include RUNIT::Assert
+
+ def self.suite
+ method_names = instance_methods(true)
+ tests = method_names.delete_if { |method_name| method_name !~ /^test/ }
+ suite = TestSuite.new(name)
+ tests.each {
+ |test|
+ catch(:invalid_test) {
+ suite << new(test, name)
+ }
+ }
+ return suite
+ end
+
+ def initialize(test_name, suite_name=self.class.name)
+ super(test_name)
+ end
+
+ def assert_equals(*args)
+ assert_equal(*args)
+ end
+
+ def name
+ super.sub(/^(.*?)\((.*)\)$/, '\2#\1')
+ end
+
+ def run(result, &progress_block)
+ progress_block = proc {} unless (block_given?)
+ super(result, &progress_block)
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/runit/testresult.rb b/ruby_1_8_5/lib/runit/testresult.rb
new file mode 100644
index 0000000000..7f70778171
--- /dev/null
+++ b/ruby_1_8_5/lib/runit/testresult.rb
@@ -0,0 +1,44 @@
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+require 'test/unit/testresult'
+
+module RUNIT
+ class TestResult < Test::Unit::TestResult
+ attr_reader(:errors, :failures)
+ def succeed?
+ return passed?
+ end
+ def failure_size
+ return failure_count
+ end
+ def run_asserts
+ return assertion_count
+ end
+ def error_size
+ return error_count
+ end
+ def run_tests
+ return run_count
+ end
+ def add_failure(failure)
+ def failure.at
+ return location
+ end
+ def failure.err
+ return message
+ end
+ super(failure)
+ end
+ def add_error(error)
+ def error.at
+ return location
+ end
+ def error.err
+ return exception
+ end
+ super(error)
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/runit/testsuite.rb b/ruby_1_8_5/lib/runit/testsuite.rb
new file mode 100644
index 0000000000..63baf65707
--- /dev/null
+++ b/ruby_1_8_5/lib/runit/testsuite.rb
@@ -0,0 +1,26 @@
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+require 'test/unit/testsuite'
+
+module RUNIT
+ class TestSuite < Test::Unit::TestSuite
+ def add_test(*args)
+ add(*args)
+ end
+
+ def add(*args)
+ self.<<(*args)
+ end
+
+ def count_test_cases
+ return size
+ end
+
+ def run(result, &progress_block)
+ progress_block = proc {} unless (block_given?)
+ super(result, &progress_block)
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/runit/topublic.rb b/ruby_1_8_5/lib/runit/topublic.rb
new file mode 100644
index 0000000000..566f0dd35c
--- /dev/null
+++ b/ruby_1_8_5/lib/runit/topublic.rb
@@ -0,0 +1,8 @@
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+module RUNIT
+ module ToPublic
+ end
+end
diff --git a/ruby_1_8_5/lib/scanf.rb b/ruby_1_8_5/lib/scanf.rb
new file mode 100644
index 0000000000..1e63051654
--- /dev/null
+++ b/ruby_1_8_5/lib/scanf.rb
@@ -0,0 +1,702 @@
+# scanf for Ruby
+#
+# $Revision: 1.2.2.1 $
+# $Id: scanf.rb,v 1.2.2.1 2004/03/20 11:57:10 dblack Exp $
+# $Author: dblack $
+# $Date: 2004/03/20 11:57:10 $
+#
+# A product of the Austin Ruby Codefest (Austin, Texas, August 2002)
+
+=begin
+
+=scanf for Ruby
+
+==Description
+
+scanf for Ruby is an implementation of the C function scanf(3),
+modified as necessary for Ruby compatibility.
+
+The methods provided are String#scanf, IO#scanf, and
+Kernel#scanf. Kernel#scanf is a wrapper around STDIN.scanf. IO#scanf
+can be used on any IO stream, including file handles and sockets.
+scanf can be called either with or without a block.
+
+scanf for Ruby scans an input string or stream according to a
+<b>format</b>, as described below ("Conversions"), and returns an
+array of matches between the format and the input. The format is
+defined in a string, and is similar (though not identical) to the
+formats used in Kernel#printf and Kernel#sprintf.
+
+The format may contain <b>conversion specifiers</b>, which tell scanf
+what form (type) each particular matched substring should be converted
+to (e.g., decimal integer, floating point number, literal string,
+etc.) The matches and conversions take place from left to right, and
+the conversions themselves are returned as an array.
+
+The format string may also contain characters other than those in the
+conversion specifiers. White space (blanks, tabs, or newlines) in the
+format string matches any amount of white space, including none, in
+the input. Everything else matches only itself.
+
+Scanning stops, and scanf returns, when any input character fails to
+match the specifications in the format string, or when input is
+exhausted, or when everything in the format string has been
+matched. All matches found up to the stopping point are returned in
+the return array (or yielded to the block, if a block was given).
+
+
+==Basic usage
+
+ require 'scanf.rb'
+
+ # String#scanf and IO#scanf take a single argument (a format string)
+ array = aString.scanf("%d%s")
+ array = anIO.scanf("%d%s")
+
+ # Kernel#scanf reads from STDIN
+ array = scanf("%d%s")
+
+==Block usage
+
+When called with a block, scanf keeps scanning the input, cycling back
+to the beginning of the format string, and yields a new array of
+conversions to the block every time the format string is matched
+(including partial matches, but not including complete failures). The
+actual return value of scanf when called with a block is an array
+containing the results of all the executions of the block.
+
+ str = "123 abc 456 def 789 ghi"
+ str.scanf("%d%s") { |num,str| [ num * 2, str.upcase ] }
+ # => [[246, "ABC"], [912, "DEF"], [1578, "GHI"]]
+
+==Conversions
+
+The single argument to scanf is a format string, which generally
+includes one or more conversion specifiers. Conversion specifiers
+begin with the percent character ('%') and include information about
+what scanf should next scan for (string, decimal number, single
+character, etc.).
+
+There may be an optional maximum field width, expressed as a decimal
+integer, between the % and the conversion. If no width is given, a
+default of `infinity' is used (with the exception of the %c specifier;
+see below). Otherwise, given a field width of <em>n</em> for a given
+conversion, at most <em>n</em> characters are scanned in processing
+that conversion. Before conversion begins, most conversions skip
+white space in the input string; this white space is not counted
+against the field width.
+
+The following conversions are available. (See the files EXAMPLES
+and <tt>tests/scanftests.rb</tt> for examples.)
+
+[%]
+ Matches a literal `%'. That is, `%%' in the format string matches a
+ single input `%' character. No conversion is done, and the resulting
+ '%' is not included in the return array.
+
+[d]
+ Matches an optionally signed decimal integer.
+
+[u]
+ Same as d.
+
+[i]
+ Matches an optionally signed integer. The integer is read in base
+ 16 if it begins with `0x' or `0X', in base 8 if it begins with `0',
+ and in base 10 other- wise. Only characters that correspond to the
+ base are recognized.
+
+[o]
+ Matches an optionally signed octal integer.
+
+[x,X]
+ Matches an optionally signed hexadecimal integer,
+
+[f,g,e,E]
+ Matches an optionally signed floating-point number.
+
+[s]
+ Matches a sequence of non-white-space character. The input string stops at
+ white space or at the maximum field width, whichever occurs first.
+
+[c]
+ Matches a single character, or a sequence of <em>n</em> characters if a
+ field width of <em>n</em> is specified. The usual skip of leading white
+ space is suppressed. To skip white space first, use an explicit space in
+ the format.
+
+[<tt>[</tt>]
+ Matches a nonempty sequence of characters from the specified set
+ of accepted characters. The usual skip of leading white space is
+ suppressed. This bracketed sub-expression is interpreted exactly like a
+ character class in a Ruby regular expression. (In fact, it is placed as-is
+ in a regular expression.) The matching against the input string ends with
+ the appearance of a character not in (or, with a circumflex, in) the set,
+ or when the field width runs out, whichever comes first.
+
+===Assignment suppression
+
+To require that a particular match occur, but without including the result
+in the return array, place the <b>assignment suppression flag</b>, which is
+the star character ('*'), immediately after the leading '%' of a format
+specifier (just before the field width, if any).
+
+==Examples
+
+See the files <tt>EXAMPLES</tt> and <tt>tests/scanftests.rb</tt>.
+
+==scanf for Ruby compared with scanf in C
+
+scanf for Ruby is based on the C function scanf(3), but with modifications,
+dictated mainly by the underlying differences between the languages.
+
+===Unimplemented flags and specifiers
+
+* The only flag implemented in scanf for Ruby is '<tt>*</tt>' (ignore
+ upcoming conversion). Many of the flags available in C versions of scanf(4)
+ have to do with the type of upcoming pointer arguments, and are literally
+ meaningless in Ruby.
+
+* The <tt>n</tt> specifier (store number of characters consumed so far in
+ next pointer) is not implemented.
+
+* The <tt>p</tt> specifier (match a pointer value) is not implemented.
+
+===Altered specifiers
+
+[o,u,x,X]
+ In scanf for Ruby, all of these specifiers scan for an optionally signed
+ integer, rather than for an unsigned integer like their C counterparts.
+
+===Return values
+
+scanf for Ruby returns an array of successful conversions, whereas
+scanf(3) returns the number of conversions successfully
+completed. (See below for more details on scanf for Ruby's return
+values.)
+
+==Return values
+
+Without a block, scanf returns an array containing all the conversions
+it has found. If none are found, scanf will return an empty array. An
+unsuccesful match is never ignored, but rather always signals the end
+of the scanning operation. If the first unsuccessful match takes place
+after one or more successful matches have already taken place, the
+returned array will contain the results of those successful matches.
+
+With a block scanf returns a 'map'-like array of transformations from
+the block -- that is, an array reflecting what the block did with each
+yielded result from the iterative scanf operation. (See "Block
+usage", above.)
+
+==Test suite
+
+scanf for Ruby includes a suite of unit tests (requiring the
+<tt>TestUnit</tt> package), which can be run with the command <tt>ruby
+tests/scanftests.rb</tt> or the command <tt>make test</tt>.
+
+==Current limitations and bugs
+
+When using IO#scanf under Windows, make sure you open your files in
+binary mode:
+
+ File.open("filename", "rb")
+
+so that scanf can keep track of characters correctly.
+
+Support for character classes is reasonably complete (since it
+essentially piggy-backs on Ruby's regular expression handling of
+character classes), but users are advised that character class testing
+has not been exhaustive, and that they should exercise some caution
+in using any of the more complex and/or arcane character class
+idioms.
+
+
+==Technical notes
+
+===Rationale behind scanf for Ruby
+
+The impetus for a scanf implementation in Ruby comes chiefly from the fact
+that existing pattern matching operations, such as Regexp#match and
+String#scan, return all results as strings, which have to be converted to
+integers or floats explicitly in cases where what's ultimately wanted are
+integer or float values.
+
+===Design of scanf for Ruby
+
+scanf for Ruby is essentially a <format string>-to-<regular
+expression> converter.
+
+When scanf is called, a FormatString object is generated from the
+format string ("%d%s...") argument. The FormatString object breaks the
+format string down into atoms ("%d", "%5f", "blah", etc.), and from
+each atom it creates a FormatSpecifier object, which it
+saves.
+
+Each FormatSpecifier has a regular expression fragment and a "handler"
+associated with it. For example, the regular expression fragment
+associated with the format "%d" is "([-+]?\d+)", and the handler
+associated with it is a wrapper around String#to_i. scanf itself calls
+FormatString#match, passing in the input string. FormatString#match
+iterates through its FormatSpecifiers; for each one, it matches the
+corresponding regular expression fragment against the string. If
+there's a match, it sends the matched string to the handler associated
+with the FormatSpecifier.
+
+Thus, to follow up the "%d" example: if "123" occurs in the input
+string when a FormatSpecifier consisting of "%d" is reached, the "123"
+will be matched against "([-+]?\d+)", and the matched string will be
+rendered into an integer by a call to to_i.
+
+The rendered match is then saved to an accumulator array, and the
+input string is reduced to the post-match substring. Thus the string
+is "eaten" from the left as the FormatSpecifiers are applied in
+sequence. (This is done to a duplicate string; the original string is
+not altered.)
+
+As soon as a regular expression fragment fails to match the string, or
+when the FormatString object runs out of FormatSpecifiers, scanning
+stops and results accumulated so far are returned in an array.
+
+==License and copyright
+
+Copyright:: (c) 2002-2003 David Alan Black
+License:: Distributed on the same licensing terms as Ruby itself
+
+==Warranty disclaimer
+
+This software is provided "as is" and without any express or implied
+warranties, including, without limitation, the implied warranties of
+merchantibility and fitness for a particular purpose.
+
+==Credits and acknowledgements
+
+scanf for Ruby was developed as the major activity of the Austin
+Ruby Codefest (Austin, Texas, August 2002).
+
+Principal author:: David Alan Black (mailto:dblack@superlink.net)
+Co-author:: Hal Fulton (mailto:hal9000@hypermetrics.com)
+Project contributors:: Nolan Darilek, Jason Johnston
+
+Thanks to Hal Fulton for hosting the Codefest.
+
+Thanks to Matz for suggestions about the class design.
+
+Thanks to Gavin Sinclair for some feedback on the documentation.
+
+The text for parts of this document, especially the Description and
+Conversions sections, above, were adapted from the Linux Programmer's
+Manual manpage for scanf(3), dated 1995-11-01.
+
+==Bugs and bug reports
+
+scanf for Ruby is based on something of an amalgam of C scanf
+implementations and documentation, rather than on a single canonical
+description. Suggestions for features and behaviors which appear in
+other scanfs, and would be meaningful in Ruby, are welcome, as are
+reports of suspicious behaviors and/or bugs. (Please see "Credits and
+acknowledgements", above, for email addresses.)
+
+=end
+
+module Scanf
+
+ class FormatSpecifier
+
+ attr_reader :re_string, :matched_string, :conversion, :matched
+
+ private
+
+ def skip; /^\s*%\*/.match(@spec_string); end
+
+ def extract_float(s); s.to_f if s &&! skip; end
+ def extract_decimal(s); s.to_i if s &&! skip; end
+ def extract_hex(s); s.hex if s &&! skip; end
+ def extract_octal(s); s.oct if s &&! skip; end
+ def extract_integer(s); Integer(s) if s &&! skip; end
+ def extract_plain(s); s unless skip; end
+
+ def nil_proc(s); nil; end
+
+ public
+
+ def to_s
+ @spec_string
+ end
+
+ def count_space?
+ /(?:\A|\S)%\*?\d*c|\[/.match(@spec_string)
+ end
+
+ def initialize(str)
+ @spec_string = str
+ h = '[A-Fa-f0-9]'
+
+ @re_string, @handler =
+ case @spec_string
+
+ # %[[:...:]]
+ when /%\*?(\[\[:[a-z]+:\]\])/
+ [ "(#{$1}+)", :extract_plain ]
+
+ # %5[[:...:]]
+ when /%\*?(\d+)(\[\[:[a-z]+:\]\])/
+ [ "(#{$2}{1,#{$1}})", :extract_plain ]
+
+ # %[...]
+ when /%\*?\[([^\]]*)\]/
+ yes = $1
+ if /^\^/.match(yes) then no = yes[1..-1] else no = '^' + yes end
+ [ "([#{yes}]+)(?=[#{no}]|\\z)", :extract_plain ]
+
+ # %5[...]
+ when /%\*?(\d+)\[([^\]]*)\]/
+ yes = $2
+ w = $1
+ [ "([#{yes}]{1,#{w}})", :extract_plain ]
+
+ # %i
+ when /%\*?i/
+ [ "([-+]?(?:(?:0[0-7]+)|(?:0[Xx]#{h}+)|(?:[1-9]\\d+)))", :extract_integer ]
+
+ # %5i
+ when /%\*?(\d+)i/
+ n = $1.to_i
+ s = "("
+ if n > 1 then s += "[1-9]\\d{1,#{n-1}}|" end
+ if n > 1 then s += "0[0-7]{1,#{n-1}}|" end
+ if n > 2 then s += "[-+]0[0-7]{1,#{n-2}}|" end
+ if n > 2 then s += "[-+][1-9]\\d{1,#{n-2}}|" end
+ if n > 2 then s += "0[Xx]#{h}{1,#{n-2}}|" end
+ if n > 3 then s += "[-+]0[Xx]#{h}{1,#{n-3}}|" end
+ s += "\\d"
+ s += ")"
+ [ s, :extract_integer ]
+
+ # %d, %u
+ when /%\*?[du]/
+ [ '([-+]?\d+)', :extract_decimal ]
+
+ # %5d, %5u
+ when /%\*?(\d+)[du]/
+ n = $1.to_i
+ s = "("
+ if n > 1 then s += "[-+]\\d{1,#{n-1}}|" end
+ s += "\\d{1,#{$1}})"
+ [ s, :extract_decimal ]
+
+ # %x
+ when /%\*?[Xx]/
+ [ "([-+]?(?:0[Xx])?#{h}+)", :extract_hex ]
+
+ # %5x
+ when /%\*?(\d+)[Xx]/
+ n = $1.to_i
+ s = "("
+ if n > 3 then s += "[-+]0[Xx]#{h}{1,#{n-3}}|" end
+ if n > 2 then s += "0[Xx]#{h}{1,#{n-2}}|" end
+ if n > 1 then s += "[-+]#{h}{1,#{n-1}}|" end
+ s += "#{h}{1,#{n}}"
+ s += ")"
+ [ s, :extract_hex ]
+
+ # %o
+ when /%\*?o/
+ [ '([-+]?[0-7]+)', :extract_octal ]
+
+ # %5o
+ when /%\*?(\d+)o/
+ [ "([-+][0-7]{1,#{$1.to_i-1}}|[0-7]{1,#{$1}})", :extract_octal ]
+
+ # %f
+ when /%\*?f/
+ [ '([-+]?((\d+(?>(?=[^\d.]|$)))|(\d*(\.(\d*([eE][-+]?\d+)?)))))', :extract_float ]
+
+ # %5f
+ when /%\*?(\d+)f/
+ [ "(\\S{1,#{$1}})", :extract_float ]
+
+ # %5s
+ when /%\*?(\d+)s/
+ [ "(\\S{1,#{$1}})", :extract_plain ]
+
+ # %s
+ when /%\*?s/
+ [ '(\S+)', :extract_plain ]
+
+ # %c
+ when /\s%\*?c/
+ [ "\\s*(.)", :extract_plain ]
+
+ # %c
+ when /%\*?c/
+ [ "(.)", :extract_plain ]
+
+ # %5c (whitespace issues are handled by the count_*_space? methods)
+ when /%\*?(\d+)c/
+ [ "(.{1,#{$1}})", :extract_plain ]
+
+ # %%
+ when /%%/
+ [ '(\s*%)', :nil_proc ]
+
+ # literal characters
+ else
+ [ "(#{Regexp.escape(@spec_string)})", :nil_proc ]
+ end
+
+ @re_string = '\A' + @re_string
+ end
+
+ def to_re
+ Regexp.new(@re_string,Regexp::MULTILINE)
+ end
+
+ def match(str)
+ @matched = false
+ s = str.dup
+ s.sub!(/\A\s+/,'') unless count_space?
+ res = to_re.match(s)
+ if res
+ @conversion = send(@handler, res[1])
+ @matched_string = @conversion.to_s
+ @matched = true
+ end
+ res
+ end
+
+ def letter
+ /%\*?\d*([a-z\[])/.match(@spec_string).to_a[1]
+ end
+
+ def width
+ w = /%\*?(\d+)/.match(@spec_string).to_a[1]
+ w && w.to_i
+ end
+
+ def mid_match?
+ return false unless @matched
+ cc_no_width = letter == '[' &&! width
+ c_or_cc_width = (letter == 'c' || letter == '[') && width
+ width_left = c_or_cc_width && (matched_string.size < width)
+
+ return width_left || cc_no_width
+ end
+
+ end
+
+ class FormatString
+
+ attr_reader :string_left, :last_spec_tried,
+ :last_match_tried, :matched_count, :space
+
+ SPECIFIERS = 'diuXxofeEgsc'
+ REGEX = /
+ # possible space, followed by...
+ (?:\s*
+ # percent sign, followed by...
+ %
+ # another percent sign, or...
+ (?:%|
+ # optional assignment suppression flag
+ \*?
+ # optional maximum field width
+ \d*
+ # named character class, ...
+ (?:\[\[:\w+:\]\]|
+ # traditional character class, or...
+ \[[^\]]*\]|
+ # specifier letter.
+ [#{SPECIFIERS}])))|
+ # or miscellaneous characters
+ [^%\s]+/ix
+
+ def initialize(str)
+ @specs = []
+ @i = 1
+ s = str.to_s
+ return unless /\S/.match(s)
+ @space = true if /\s\z/.match(s)
+ @specs.replace s.scan(REGEX).map {|spec| FormatSpecifier.new(spec) }
+ end
+
+ def to_s
+ @specs.join('')
+ end
+
+ def prune(n=matched_count)
+ n.times { @specs.shift }
+ end
+
+ def spec_count
+ @specs.size
+ end
+
+ def last_spec
+ @i == spec_count - 1
+ end
+
+ def match(str)
+ accum = []
+ @string_left = str
+ @matched_count = 0
+
+ @specs.each_with_index do |spec,@i|
+ @last_spec_tried = spec
+ @last_match_tried = spec.match(@string_left)
+ break unless @last_match_tried
+ @matched_count += 1
+
+ accum << spec.conversion
+
+ @string_left = @last_match_tried.post_match
+ break if @string_left.empty?
+ end
+ return accum.compact
+ end
+ end
+end
+
+class IO
+
+# The trick here is doing a match where you grab one *line*
+# of input at a time. The linebreak may or may not occur
+# at the boundary where the string matches a format specifier.
+# And if it does, some rule about whitespace may or may not
+# be in effect...
+#
+# That's why this is much more elaborate than the string
+# version.
+#
+# For each line:
+# Match succeeds (non-emptily)
+# and the last attempted spec/string sub-match succeeded:
+#
+# could the last spec keep matching?
+# yes: save interim results and continue (next line)
+#
+# The last attempted spec/string did not match:
+#
+# are we on the next-to-last spec in the string?
+# yes:
+# is fmt_string.string_left all spaces?
+# yes: does current spec care about input space?
+# yes: fatal failure
+# no: save interim results and continue
+# no: continue [this state could be analyzed further]
+#
+#
+
+ def scanf(str,&b)
+ return block_scanf(str,&b) if b
+ return [] unless str.size > 0
+
+ start_position = pos rescue 0
+ matched_so_far = 0
+ source_buffer = ""
+ result_buffer = []
+ final_result = []
+
+ fstr = Scanf::FormatString.new(str)
+
+ loop do
+ if eof || (tty? &&! fstr.match(source_buffer))
+ final_result.concat(result_buffer)
+ break
+ end
+
+ source_buffer << gets
+
+ current_match = fstr.match(source_buffer)
+
+ spec = fstr.last_spec_tried
+
+ if spec.matched
+ if spec.mid_match?
+ result_buffer.replace(current_match)
+ next
+ end
+
+ elsif (fstr.matched_count == fstr.spec_count - 1)
+ if /\A\s*\z/.match(fstr.string_left)
+ break if spec.count_space?
+ result_buffer.replace(current_match)
+ next
+ end
+ end
+
+ final_result.concat(current_match)
+
+ matched_so_far += source_buffer.size
+ source_buffer.replace(fstr.string_left)
+ matched_so_far -= source_buffer.size
+ break if fstr.last_spec
+ fstr.prune
+ end
+ seek(start_position + matched_so_far, IO::SEEK_SET) rescue Errno::ESPIPE
+ soak_up_spaces if fstr.last_spec && fstr.space
+
+ return final_result
+ end
+
+ private
+
+ def soak_up_spaces
+ c = getc
+ ungetc(c) if c
+ until eof ||! c || /\S/.match(c.chr)
+ c = getc
+ end
+ ungetc(c) if (c && /\S/.match(c.chr))
+ end
+
+ def block_scanf(str)
+ final = []
+# Sub-ideal, since another FS gets created in scanf.
+# But used here to determine the number of specifiers.
+ fstr = Scanf::FormatString.new(str)
+ last_spec = fstr.last_spec
+ begin
+ current = scanf(str)
+ break if current.empty?
+ final.push(yield(current))
+ end until eof || fstr.last_spec_tried == last_spec
+ return final
+ end
+end
+
+class String
+
+ def scanf(fstr,&b)
+ if b
+ block_scanf(fstr,&b)
+ else
+ fs =
+ if fstr.is_a? Scanf::FormatString
+ fstr
+ else
+ Scanf::FormatString.new(fstr)
+ end
+ fs.match(self)
+ end
+ end
+
+ def block_scanf(fstr,&b)
+ fs = Scanf::FormatString.new(fstr)
+ str = self.dup
+ final = []
+ begin
+ current = str.scanf(fs)
+ final.push(yield(current)) unless current.empty?
+ str = fs.string_left
+ end until current.empty? || str.empty?
+ return final
+ end
+end
+
+module Kernel
+ private
+ def scanf(fs,&b)
+ STDIN.scanf(fs,&b)
+ end
+end
diff --git a/ruby_1_8_5/lib/set.rb b/ruby_1_8_5/lib/set.rb
new file mode 100644
index 0000000000..36e718eaf7
--- /dev/null
+++ b/ruby_1_8_5/lib/set.rb
@@ -0,0 +1,1220 @@
+#!/usr/bin/env ruby
+#--
+# set.rb - defines the Set class
+#++
+# Copyright (c) 2002 Akinori MUSHA <knu@iDaemons.org>
+#
+# Documentation by Akinori MUSHA and Gavin Sinclair.
+#
+# All rights reserved. You can redistribute and/or modify it under the same
+# terms as Ruby.
+#
+# $Id: set.rb,v 1.20.2.7 2005/06/30 06:16:13 matz Exp $
+#
+# == Overview
+#
+# This library provides the Set class, which deals with a collection
+# of unordered values with no duplicates. It is a hybrid of Array's
+# intuitive inter-operation facilities and Hash's fast lookup. If you
+# need to keep values ordered, use the SortedSet class.
+#
+# The method +to_set+ is added to Enumerable for convenience.
+#
+# See the Set class for an example of usage.
+
+
+#
+# Set implements a collection of unordered values with no duplicates.
+# This is a hybrid of Array's intuitive inter-operation facilities and
+# Hash's fast lookup.
+#
+# Several methods accept any Enumerable object (implementing +each+)
+# for greater flexibility: new, replace, merge, subtract, |, &, -, ^.
+#
+# The equality of each couple of elements is determined according to
+# Object#eql? and Object#hash, since Set uses Hash as storage.
+#
+# Finally, if you are using class Set, you can also use Enumerable#to_set
+# for convenience.
+#
+# == Example
+#
+# require 'set'
+# s1 = Set.new [1, 2] # -> #<Set: {1, 2}>
+# s2 = [1, 2].to_set # -> #<Set: {1, 2}>
+# s1 == s2 # -> true
+# s1.add("foo") # -> #<Set: {1, 2, "foo"}>
+# s1.merge([2, 6]) # -> #<Set: {6, 1, 2, "foo"}>
+# s1.subset? s2 # -> false
+# s2.subset? s1 # -> true
+#
+class Set
+ include Enumerable
+
+ # Creates a new set containing the given objects.
+ def self.[](*ary)
+ new(ary)
+ end
+
+ # Creates a new set containing the elements of the given enumerable
+ # object.
+ #
+ # If a block is given, the elements of enum are preprocessed by the
+ # given block.
+ def initialize(enum = nil, &block) # :yields: o
+ @hash ||= Hash.new
+
+ enum.nil? and return
+
+ if block
+ enum.each { |o| add(block[o]) }
+ else
+ merge(enum)
+ end
+ end
+
+ # Copy internal hash.
+ def initialize_copy(orig)
+ @hash = orig.instance_eval{@hash}.dup
+ end
+
+ # Returns the number of elements.
+ def size
+ @hash.size
+ end
+ alias length size
+
+ # Returns true if the set contains no elements.
+ def empty?
+ @hash.empty?
+ end
+
+ # Removes all elements and returns self.
+ def clear
+ @hash.clear
+ self
+ end
+
+ # Replaces the contents of the set with the contents of the given
+ # enumerable object and returns self.
+ def replace(enum)
+ if enum.class == self.class
+ @hash.replace(enum.instance_eval { @hash })
+ else
+ enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
+ clear
+ enum.each { |o| add(o) }
+ end
+
+ self
+ end
+
+ # Converts the set to an array. The order of elements is uncertain.
+ def to_a
+ @hash.keys
+ end
+
+ def flatten_merge(set, seen = Set.new)
+ set.each { |e|
+ if e.is_a?(Set)
+ if seen.include?(e_id = e.object_id)
+ raise ArgumentError, "tried to flatten recursive Set"
+ end
+
+ seen.add(e_id)
+ flatten_merge(e, seen)
+ seen.delete(e_id)
+ else
+ add(e)
+ end
+ }
+
+ self
+ end
+ protected :flatten_merge
+
+ # Returns a new set that is a copy of the set, flattening each
+ # containing set recursively.
+ def flatten
+ self.class.new.flatten_merge(self)
+ end
+
+ # Equivalent to Set#flatten, but replaces the receiver with the
+ # result in place. Returns nil if no modifications were made.
+ def flatten!
+ if detect { |e| e.is_a?(Set) }
+ replace(flatten())
+ else
+ nil
+ end
+ end
+
+ # Returns true if the set contains the given object.
+ def include?(o)
+ @hash.include?(o)
+ end
+ alias member? include?
+
+ # Returns true if the set is a superset of the given set.
+ def superset?(set)
+ set.is_a?(Set) or raise ArgumentError, "value must be a set"
+ return false if size < set.size
+ set.all? { |o| include?(o) }
+ end
+
+ # Returns true if the set is a proper superset of the given set.
+ def proper_superset?(set)
+ set.is_a?(Set) or raise ArgumentError, "value must be a set"
+ return false if size <= set.size
+ set.all? { |o| include?(o) }
+ end
+
+ # Returns true if the set is a subset of the given set.
+ def subset?(set)
+ set.is_a?(Set) or raise ArgumentError, "value must be a set"
+ return false if set.size < size
+ all? { |o| set.include?(o) }
+ end
+
+ # Returns true if the set is a proper subset of the given set.
+ def proper_subset?(set)
+ set.is_a?(Set) or raise ArgumentError, "value must be a set"
+ return false if set.size <= size
+ all? { |o| set.include?(o) }
+ end
+
+ # Calls the given block once for each element in the set, passing
+ # the element as parameter.
+ def each
+ @hash.each_key { |o| yield(o) }
+ self
+ end
+
+ # Adds the given object to the set and returns self. Use +merge+ to
+ # add several elements at once.
+ def add(o)
+ @hash[o] = true
+ self
+ end
+ alias << add
+
+ # Adds the given object to the set and returns self. If the
+ # object is already in the set, returns nil.
+ def add?(o)
+ if include?(o)
+ nil
+ else
+ add(o)
+ end
+ end
+
+ # Deletes the given object from the set and returns self. Use +subtract+ to
+ # delete several items at once.
+ def delete(o)
+ @hash.delete(o)
+ self
+ end
+
+ # Deletes the given object from the set and returns self. If the
+ # object is not in the set, returns nil.
+ def delete?(o)
+ if include?(o)
+ delete(o)
+ else
+ nil
+ end
+ end
+
+ # Deletes every element of the set for which block evaluates to
+ # true, and returns self.
+ def delete_if
+ @hash.delete_if { |o,| yield(o) }
+ self
+ end
+
+ # Do collect() destructively.
+ def collect!
+ set = self.class.new
+ each { |o| set << yield(o) }
+ replace(set)
+ end
+ alias map! collect!
+
+ # Equivalent to Set#delete_if, but returns nil if no changes were
+ # made.
+ def reject!
+ n = size
+ delete_if { |o| yield(o) }
+ size == n ? nil : self
+ end
+
+ # Merges the elements of the given enumerable object to the set and
+ # returns self.
+ def merge(enum)
+ if enum.is_a?(Set)
+ @hash.update(enum.instance_eval { @hash })
+ else
+ enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
+ enum.each { |o| add(o) }
+ end
+
+ self
+ end
+
+ # Deletes every element that appears in the given enumerable object
+ # and returns self.
+ def subtract(enum)
+ enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
+ enum.each { |o| delete(o) }
+ self
+ end
+
+ # Returns a new set built by merging the set and the elements of the
+ # given enumerable object.
+ def |(enum)
+ enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
+ dup.merge(enum)
+ end
+ alias + | ##
+ alias union | ##
+
+ # Returns a new set built by duplicating the set, removing every
+ # element that appears in the given enumerable object.
+ def -(enum)
+ enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
+ dup.subtract(enum)
+ end
+ alias difference - ##
+
+ # Returns a new array containing elements common to the set and the
+ # given enumerable object.
+ def &(enum)
+ enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
+ n = self.class.new
+ enum.each { |o| n.add(o) if include?(o) }
+ n
+ end
+ alias intersection & ##
+
+ # Returns a new array containing elements exclusive between the set
+ # and the given enumerable object. (set ^ enum) is equivalent to
+ # ((set | enum) - (set & enum)).
+ def ^(enum)
+ enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
+ n = dup
+ enum.each { |o| if n.include?(o) then n.delete(o) else n.add(o) end }
+ n
+ end
+
+ # Returns true if two sets are equal. The equality of each couple
+ # of elements is defined according to Object#eql?.
+ def ==(set)
+ equal?(set) and return true
+
+ set.is_a?(Set) && size == set.size or return false
+
+ hash = @hash.dup
+ set.all? { |o| hash.include?(o) }
+ end
+
+ def hash # :nodoc:
+ @hash.hash
+ end
+
+ def eql?(o) # :nodoc:
+ return false unless o.is_a?(Set)
+ @hash.eql?(o.instance_eval{@hash})
+ end
+
+ # Classifies the set by the return value of the given block and
+ # returns a hash of {value => set of elements} pairs. The block is
+ # called once for each element of the set, passing the element as
+ # parameter.
+ #
+ # e.g.:
+ #
+ # require 'set'
+ # files = Set.new(Dir.glob("*.rb"))
+ # hash = files.classify { |f| File.mtime(f).year }
+ # p hash # => {2000=>#<Set: {"a.rb", "b.rb"}>,
+ # # 2001=>#<Set: {"c.rb", "d.rb", "e.rb"}>,
+ # # 2002=>#<Set: {"f.rb"}>}
+ def classify # :yields: o
+ h = {}
+
+ each { |i|
+ x = yield(i)
+ (h[x] ||= self.class.new).add(i)
+ }
+
+ h
+ end
+
+ # Divides the set into a set of subsets according to the commonality
+ # defined by the given block.
+ #
+ # If the arity of the block is 2, elements o1 and o2 are in common
+ # if block.call(o1, o2) is true. Otherwise, elements o1 and o2 are
+ # in common if block.call(o1) == block.call(o2).
+ #
+ # e.g.:
+ #
+ # require 'set'
+ # numbers = Set[1, 3, 4, 6, 9, 10, 11]
+ # set = numbers.divide { |i,j| (i - j).abs == 1 }
+ # p set # => #<Set: {#<Set: {1}>,
+ # # #<Set: {11, 9, 10}>,
+ # # #<Set: {3, 4}>,
+ # # #<Set: {6}>}>
+ def divide(&func)
+ if func.arity == 2
+ require 'tsort'
+
+ class << dig = {} # :nodoc:
+ include TSort
+
+ alias tsort_each_node each_key
+ def tsort_each_child(node, &block)
+ fetch(node).each(&block)
+ end
+ end
+
+ each { |u|
+ dig[u] = a = []
+ each{ |v| func.call(u, v) and a << v }
+ }
+
+ set = Set.new()
+ dig.each_strongly_connected_component { |css|
+ set.add(self.class.new(css))
+ }
+ set
+ else
+ Set.new(classify(&func).values)
+ end
+ end
+
+ InspectKey = :__inspect_key__ # :nodoc:
+
+ # Returns a string containing a human-readable representation of the
+ # set. ("#<Set: {element1, element2, ...}>")
+ def inspect
+ ids = (Thread.current[InspectKey] ||= [])
+
+ if ids.include?(object_id)
+ return sprintf('#<%s: {...}>', self.class.name)
+ end
+
+ begin
+ ids << object_id
+ return sprintf('#<%s: {%s}>', self.class, to_a.inspect[1..-2])
+ ensure
+ ids.pop
+ end
+ end
+
+ def pretty_print(pp) # :nodoc:
+ pp.text sprintf('#<%s: {', self.class.name)
+ pp.nest(1) {
+ pp.seplist(self) { |o|
+ pp.pp o
+ }
+ }
+ pp.text "}>"
+ end
+
+ def pretty_print_cycle(pp) # :nodoc:
+ pp.text sprintf('#<%s: {%s}>', self.class.name, empty? ? '' : '...')
+ end
+end
+
+# SortedSet implements a set which elements are sorted in order. See Set.
+class SortedSet < Set
+ @@setup = false
+
+ class << self
+ def [](*ary) # :nodoc:
+ new(ary)
+ end
+
+ def setup # :nodoc:
+ @@setup and return
+
+ module_eval {
+ # a hack to shut up warning
+ alias old_init initialize
+ remove_method :old_init
+ }
+ begin
+ require 'rbtree'
+
+ module_eval %{
+ def initialize(*args, &block)
+ @hash = RBTree.new
+ super
+ end
+ }
+ rescue LoadError
+ module_eval %{
+ def initialize(*args, &block)
+ @keys = nil
+ super
+ end
+
+ def clear
+ @keys = nil
+ super
+ end
+
+ def replace(enum)
+ @keys = nil
+ super
+ end
+
+ def add(o)
+ @keys = nil
+ @hash[o] = true
+ self
+ end
+ alias << add
+
+ def delete(o)
+ @keys = nil
+ @hash.delete(o)
+ self
+ end
+
+ def delete_if
+ n = @hash.size
+ @hash.delete_if { |o,| yield(o) }
+ @keys = nil if @hash.size != n
+ self
+ end
+
+ def merge(enum)
+ @keys = nil
+ super
+ end
+
+ def each
+ to_a.each { |o| yield(o) }
+ end
+
+ def to_a
+ (@keys = @hash.keys).sort! unless @keys
+ @keys
+ end
+ }
+ end
+
+ @@setup = true
+ end
+ end
+
+ def initialize(*args, &block) # :nodoc:
+ SortedSet.setup
+ initialize(*args, &block)
+ end
+end
+
+module Enumerable
+ # Makes a set from the enumerable object with given arguments.
+ # Needs to +require "set"+ to use this method.
+ def to_set(klass = Set, *args, &block)
+ klass.new(self, *args, &block)
+ end
+end
+
+# =begin
+# == RestricedSet class
+# RestricedSet implements a set with restrictions defined by a given
+# block.
+#
+# === Super class
+# Set
+#
+# === Class Methods
+# --- RestricedSet::new(enum = nil) { |o| ... }
+# --- RestricedSet::new(enum = nil) { |rset, o| ... }
+# Creates a new restricted set containing the elements of the given
+# enumerable object. Restrictions are defined by the given block.
+#
+# If the block's arity is 2, it is called with the RestrictedSet
+# itself and an object to see if the object is allowed to be put in
+# the set.
+#
+# Otherwise, the block is called with an object to see if the object
+# is allowed to be put in the set.
+#
+# === Instance Methods
+# --- restriction_proc
+# Returns the restriction procedure of the set.
+#
+# =end
+#
+# class RestricedSet < Set
+# def initialize(*args, &block)
+# @proc = block or raise ArgumentError, "missing a block"
+#
+# if @proc.arity == 2
+# instance_eval %{
+# def add(o)
+# @hash[o] = true if @proc.call(self, o)
+# self
+# end
+# alias << add
+#
+# def add?(o)
+# if include?(o) || !@proc.call(self, o)
+# nil
+# else
+# @hash[o] = true
+# self
+# end
+# end
+#
+# def replace(enum)
+# enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
+# clear
+# enum.each { |o| add(o) }
+#
+# self
+# end
+#
+# def merge(enum)
+# enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
+# enum.each { |o| add(o) }
+#
+# self
+# end
+# }
+# else
+# instance_eval %{
+# def add(o)
+# if @proc.call(o)
+# @hash[o] = true
+# end
+# self
+# end
+# alias << add
+#
+# def add?(o)
+# if include?(o) || !@proc.call(o)
+# nil
+# else
+# @hash[o] = true
+# self
+# end
+# end
+# }
+# end
+#
+# super(*args)
+# end
+#
+# def restriction_proc
+# @proc
+# end
+# end
+
+if $0 == __FILE__
+ eval DATA.read, nil, $0, __LINE__+4
+end
+
+__END__
+
+require 'test/unit'
+
+class TC_Set < Test::Unit::TestCase
+ def test_aref
+ assert_nothing_raised {
+ Set[]
+ Set[nil]
+ Set[1,2,3]
+ }
+
+ assert_equal(0, Set[].size)
+ assert_equal(1, Set[nil].size)
+ assert_equal(1, Set[[]].size)
+ assert_equal(1, Set[[nil]].size)
+
+ set = Set[2,4,6,4]
+ assert_equal(Set.new([2,4,6]), set)
+ end
+
+ def test_s_new
+ assert_nothing_raised {
+ Set.new()
+ Set.new(nil)
+ Set.new([])
+ Set.new([1,2])
+ Set.new('a'..'c')
+ Set.new('XYZ')
+ }
+ assert_raises(ArgumentError) {
+ Set.new(false)
+ }
+ assert_raises(ArgumentError) {
+ Set.new(1)
+ }
+ assert_raises(ArgumentError) {
+ Set.new(1,2)
+ }
+
+ assert_equal(0, Set.new().size)
+ assert_equal(0, Set.new(nil).size)
+ assert_equal(0, Set.new([]).size)
+ assert_equal(1, Set.new([nil]).size)
+
+ ary = [2,4,6,4]
+ set = Set.new(ary)
+ ary.clear
+ assert_equal(false, set.empty?)
+ assert_equal(3, set.size)
+
+ ary = [1,2,3]
+
+ s = Set.new(ary) { |o| o * 2 }
+ assert_equal([2,4,6], s.sort)
+ end
+
+ def test_clone
+ set1 = Set.new
+ set2 = set1.clone
+ set1 << 'abc'
+ assert_equal(Set.new, set2)
+ end
+
+ def test_dup
+ set1 = Set[1,2]
+ set2 = set1.dup
+
+ assert_not_same(set1, set2)
+
+ assert_equal(set1, set2)
+
+ set1.add(3)
+
+ assert_not_equal(set1, set2)
+ end
+
+ def test_size
+ assert_equal(0, Set[].size)
+ assert_equal(2, Set[1,2].size)
+ assert_equal(2, Set[1,2,1].size)
+ end
+
+ def test_empty?
+ assert_equal(true, Set[].empty?)
+ assert_equal(false, Set[1, 2].empty?)
+ end
+
+ def test_clear
+ set = Set[1,2]
+ ret = set.clear
+
+ assert_same(set, ret)
+ assert_equal(true, set.empty?)
+ end
+
+ def test_replace
+ set = Set[1,2]
+ ret = set.replace('a'..'c')
+
+ assert_same(set, ret)
+ assert_equal(Set['a','b','c'], set)
+ end
+
+ def test_to_a
+ set = Set[1,2,3,2]
+ ary = set.to_a
+
+ assert_equal([1,2,3], ary.sort)
+ end
+
+ def test_flatten
+ # test1
+ set1 = Set[
+ 1,
+ Set[
+ 5,
+ Set[7,
+ Set[0]
+ ],
+ Set[6,2],
+ 1
+ ],
+ 3,
+ Set[3,4]
+ ]
+
+ set2 = set1.flatten
+ set3 = Set.new(0..7)
+
+ assert_not_same(set2, set1)
+ assert_equal(set3, set2)
+
+ # test2; destructive
+ orig_set1 = set1
+ set1.flatten!
+
+ assert_same(orig_set1, set1)
+ assert_equal(set3, set1)
+
+ # test3; multiple occurrences of a set in an set
+ set1 = Set[1, 2]
+ set2 = Set[set1, Set[set1, 4], 3]
+
+ assert_nothing_raised {
+ set2.flatten!
+ }
+
+ assert_equal(Set.new(1..4), set2)
+
+ # test4; recursion
+ set2 = Set[]
+ set1 = Set[1, set2]
+ set2.add(set1)
+
+ assert_raises(ArgumentError) {
+ set1.flatten!
+ }
+
+ # test5; miscellaneous
+ empty = Set[]
+ set = Set[Set[empty, "a"],Set[empty, "b"]]
+
+ assert_nothing_raised {
+ set.flatten
+ }
+
+ set1 = empty.merge(Set["no_more", set])
+
+ assert_nil(Set.new(0..31).flatten!)
+
+ x = Set[Set[],Set[1,2]].flatten!
+ y = Set[1,2]
+
+ assert_equal(x, y)
+ end
+
+ def test_include?
+ set = Set[1,2,3]
+
+ assert_equal(true, set.include?(1))
+ assert_equal(true, set.include?(2))
+ assert_equal(true, set.include?(3))
+ assert_equal(false, set.include?(0))
+ assert_equal(false, set.include?(nil))
+
+ set = Set["1",nil,"2",nil,"0","1",false]
+ assert_equal(true, set.include?(nil))
+ assert_equal(true, set.include?(false))
+ assert_equal(true, set.include?("1"))
+ assert_equal(false, set.include?(0))
+ assert_equal(false, set.include?(true))
+ end
+
+ def test_superset?
+ set = Set[1,2,3]
+
+ assert_raises(ArgumentError) {
+ set.superset?()
+ }
+
+ assert_raises(ArgumentError) {
+ set.superset?(2)
+ }
+
+ assert_raises(ArgumentError) {
+ set.superset?([2])
+ }
+
+ assert_equal(true, set.superset?(Set[]))
+ assert_equal(true, set.superset?(Set[1,2]))
+ assert_equal(true, set.superset?(Set[1,2,3]))
+ assert_equal(false, set.superset?(Set[1,2,3,4]))
+ assert_equal(false, set.superset?(Set[1,4]))
+
+ assert_equal(true, Set[].superset?(Set[]))
+ end
+
+ def test_proper_superset?
+ set = Set[1,2,3]
+
+ assert_raises(ArgumentError) {
+ set.proper_superset?()
+ }
+
+ assert_raises(ArgumentError) {
+ set.proper_superset?(2)
+ }
+
+ assert_raises(ArgumentError) {
+ set.proper_superset?([2])
+ }
+
+ assert_equal(true, set.proper_superset?(Set[]))
+ assert_equal(true, set.proper_superset?(Set[1,2]))
+ assert_equal(false, set.proper_superset?(Set[1,2,3]))
+ assert_equal(false, set.proper_superset?(Set[1,2,3,4]))
+ assert_equal(false, set.proper_superset?(Set[1,4]))
+
+ assert_equal(false, Set[].proper_superset?(Set[]))
+ end
+
+ def test_subset?
+ set = Set[1,2,3]
+
+ assert_raises(ArgumentError) {
+ set.subset?()
+ }
+
+ assert_raises(ArgumentError) {
+ set.subset?(2)
+ }
+
+ assert_raises(ArgumentError) {
+ set.subset?([2])
+ }
+
+ assert_equal(true, set.subset?(Set[1,2,3,4]))
+ assert_equal(true, set.subset?(Set[1,2,3]))
+ assert_equal(false, set.subset?(Set[1,2]))
+ assert_equal(false, set.subset?(Set[]))
+
+ assert_equal(true, Set[].subset?(Set[1]))
+ assert_equal(true, Set[].subset?(Set[]))
+ end
+
+ def test_proper_subset?
+ set = Set[1,2,3]
+
+ assert_raises(ArgumentError) {
+ set.proper_subset?()
+ }
+
+ assert_raises(ArgumentError) {
+ set.proper_subset?(2)
+ }
+
+ assert_raises(ArgumentError) {
+ set.proper_subset?([2])
+ }
+
+ assert_equal(true, set.proper_subset?(Set[1,2,3,4]))
+ assert_equal(false, set.proper_subset?(Set[1,2,3]))
+ assert_equal(false, set.proper_subset?(Set[1,2]))
+ assert_equal(false, set.proper_subset?(Set[]))
+
+ assert_equal(false, Set[].proper_subset?(Set[]))
+ end
+
+ def test_each
+ ary = [1,3,5,7,10,20]
+ set = Set.new(ary)
+
+ assert_raises(LocalJumpError) {
+ set.each
+ }
+
+ assert_nothing_raised {
+ set.each { |o|
+ ary.delete(o) or raise "unexpected element: #{o}"
+ }
+
+ ary.empty? or raise "forgotten elements: #{ary.join(', ')}"
+ }
+ end
+
+ def test_add
+ set = Set[1,2,3]
+
+ ret = set.add(2)
+ assert_same(set, ret)
+ assert_equal(Set[1,2,3], set)
+
+ ret = set.add?(2)
+ assert_nil(ret)
+ assert_equal(Set[1,2,3], set)
+
+ ret = set.add(4)
+ assert_same(set, ret)
+ assert_equal(Set[1,2,3,4], set)
+
+ ret = set.add?(5)
+ assert_same(set, ret)
+ assert_equal(Set[1,2,3,4,5], set)
+ end
+
+ def test_delete
+ set = Set[1,2,3]
+
+ ret = set.delete(4)
+ assert_same(set, ret)
+ assert_equal(Set[1,2,3], set)
+
+ ret = set.delete?(4)
+ assert_nil(ret)
+ assert_equal(Set[1,2,3], set)
+
+ ret = set.delete(2)
+ assert_equal(set, ret)
+ assert_equal(Set[1,3], set)
+
+ ret = set.delete?(1)
+ assert_equal(set, ret)
+ assert_equal(Set[3], set)
+ end
+
+ def test_delete_if
+ set = Set.new(1..10)
+ ret = set.delete_if { |i| i > 10 }
+ assert_same(set, ret)
+ assert_equal(Set.new(1..10), set)
+
+ set = Set.new(1..10)
+ ret = set.delete_if { |i| i % 3 == 0 }
+ assert_same(set, ret)
+ assert_equal(Set[1,2,4,5,7,8,10], set)
+ end
+
+ def test_collect!
+ set = Set[1,2,3,'a','b','c',-1..1,2..4]
+
+ ret = set.collect! { |i|
+ case i
+ when Numeric
+ i * 2
+ when String
+ i.upcase
+ else
+ nil
+ end
+ }
+
+ assert_same(set, ret)
+ assert_equal(Set[2,4,6,'A','B','C',nil], set)
+ end
+
+ def test_reject!
+ set = Set.new(1..10)
+
+ ret = set.reject! { |i| i > 10 }
+ assert_nil(ret)
+ assert_equal(Set.new(1..10), set)
+
+ ret = set.reject! { |i| i % 3 == 0 }
+ assert_same(set, ret)
+ assert_equal(Set[1,2,4,5,7,8,10], set)
+ end
+
+ def test_merge
+ set = Set[1,2,3]
+
+ ret = set.merge([2,4,6])
+ assert_same(set, ret)
+ assert_equal(Set[1,2,3,4,6], set)
+ end
+
+ def test_subtract
+ set = Set[1,2,3]
+
+ ret = set.subtract([2,4,6])
+ assert_same(set, ret)
+ assert_equal(Set[1,3], set)
+ end
+
+ def test_plus
+ set = Set[1,2,3]
+
+ ret = set + [2,4,6]
+ assert_not_same(set, ret)
+ assert_equal(Set[1,2,3,4,6], ret)
+ end
+
+ def test_minus
+ set = Set[1,2,3]
+
+ ret = set - [2,4,6]
+ assert_not_same(set, ret)
+ assert_equal(Set[1,3], ret)
+ end
+
+ def test_and
+ set = Set[1,2,3,4]
+
+ ret = set & [2,4,6]
+ assert_not_same(set, ret)
+ assert_equal(Set[2,4], ret)
+ end
+
+ def test_eq
+ set1 = Set[2,3,1]
+ set2 = Set[1,2,3]
+
+ assert_equal(set1, set1)
+ assert_equal(set1, set2)
+ assert_not_equal(Set[1], [1])
+
+ set1 = Class.new(Set)["a", "b"]
+ set2 = Set["a", "b", set1]
+ set1 = set1.add(set1.clone)
+
+# assert_equal(set1, set2)
+# assert_equal(set2, set1)
+ assert_equal(set2, set2.clone)
+ assert_equal(set1.clone, set1)
+
+ assert_not_equal(Set[Exception.new,nil], Set[Exception.new,Exception.new], "[ruby-dev:26127]")
+ end
+
+ # def test_hash
+ # end
+
+ # def test_eql?
+ # end
+
+ def test_classify
+ set = Set.new(1..10)
+ ret = set.classify { |i| i % 3 }
+
+ assert_equal(3, ret.size)
+ assert_instance_of(Hash, ret)
+ ret.each_value { |value| assert_instance_of(Set, value) }
+ assert_equal(Set[3,6,9], ret[0])
+ assert_equal(Set[1,4,7,10], ret[1])
+ assert_equal(Set[2,5,8], ret[2])
+ end
+
+ def test_divide
+ set = Set.new(1..10)
+ ret = set.divide { |i| i % 3 }
+
+ assert_equal(3, ret.size)
+ n = 0
+ ret.each { |s| n += s.size }
+ assert_equal(set.size, n)
+ assert_equal(set, ret.flatten)
+
+ set = Set[7,10,5,11,1,3,4,9,0]
+ ret = set.divide { |a,b| (a - b).abs == 1 }
+
+ assert_equal(4, ret.size)
+ n = 0
+ ret.each { |s| n += s.size }
+ assert_equal(set.size, n)
+ assert_equal(set, ret.flatten)
+ ret.each { |s|
+ if s.include?(0)
+ assert_equal(Set[0,1], s)
+ elsif s.include?(3)
+ assert_equal(Set[3,4,5], s)
+ elsif s.include?(7)
+ assert_equal(Set[7], s)
+ elsif s.include?(9)
+ assert_equal(Set[9,10,11], s)
+ else
+ raise "unexpected group: #{s.inspect}"
+ end
+ }
+ end
+
+ def test_inspect
+ set1 = Set[1]
+
+ assert_equal('#<Set: {1}>', set1.inspect)
+
+ set2 = Set[Set[0], 1, 2, set1]
+ assert_equal(false, set2.inspect.include?('#<Set: {...}>'))
+
+ set1.add(set2)
+ assert_equal(true, set1.inspect.include?('#<Set: {...}>'))
+ end
+
+ # def test_pretty_print
+ # end
+
+ # def test_pretty_print_cycle
+ # end
+end
+
+class TC_SortedSet < Test::Unit::TestCase
+ def test_sortedset
+ s = SortedSet[4,5,3,1,2]
+
+ assert_equal([1,2,3,4,5], s.to_a)
+
+ prev = nil
+ s.each { |o| assert(prev < o) if prev; prev = o }
+ assert_not_nil(prev)
+
+ s.map! { |o| -2 * o }
+
+ assert_equal([-10,-8,-6,-4,-2], s.to_a)
+
+ prev = nil
+ s.each { |o| assert(prev < o) if prev; prev = o }
+ assert_not_nil(prev)
+
+ s = SortedSet.new([2,1,3]) { |o| o * -2 }
+ assert_equal([-6,-4,-2], s.to_a)
+ end
+end
+
+class TC_Enumerable < Test::Unit::TestCase
+ def test_to_set
+ ary = [2,5,4,3,2,1,3]
+
+ set = ary.to_set
+ assert_instance_of(Set, set)
+ assert_equal([1,2,3,4,5], set.sort)
+
+ set = ary.to_set { |o| o * -2 }
+ assert_instance_of(Set, set)
+ assert_equal([-10,-8,-6,-4,-2], set.sort)
+
+ set = ary.to_set(SortedSet)
+ assert_instance_of(SortedSet, set)
+ assert_equal([1,2,3,4,5], set.to_a)
+
+ set = ary.to_set(SortedSet) { |o| o * -2 }
+ assert_instance_of(SortedSet, set)
+ assert_equal([-10,-8,-6,-4,-2], set.sort)
+ end
+end
+
+# class TC_RestricedSet < Test::Unit::TestCase
+# def test_s_new
+# assert_raises(ArgumentError) { RestricedSet.new }
+#
+# s = RestricedSet.new([-1,2,3]) { |o| o > 0 }
+# assert_equal([2,3], s.sort)
+# end
+#
+# def test_restriction_proc
+# s = RestricedSet.new([-1,2,3]) { |o| o > 0 }
+#
+# f = s.restriction_proc
+# assert_instance_of(Proc, f)
+# assert(f[1])
+# assert(!f[0])
+# end
+#
+# def test_replace
+# s = RestricedSet.new(-3..3) { |o| o > 0 }
+# assert_equal([1,2,3], s.sort)
+#
+# s.replace([-2,0,3,4,5])
+# assert_equal([3,4,5], s.sort)
+# end
+#
+# def test_merge
+# s = RestricedSet.new { |o| o > 0 }
+# s.merge(-5..5)
+# assert_equal([1,2,3,4,5], s.sort)
+#
+# s.merge([10,-10,-8,8])
+# assert_equal([1,2,3,4,5,8,10], s.sort)
+# end
+# end
diff --git a/ruby_1_8_5/lib/shell.rb b/ruby_1_8_5/lib/shell.rb
new file mode 100644
index 0000000000..039f849ef5
--- /dev/null
+++ b/ruby_1_8_5/lib/shell.rb
@@ -0,0 +1,269 @@
+#
+# shell.rb -
+# $Release Version: 0.6.0 $
+# $Revision: 1.8 $
+# $Date: 2001/03/19 09:01:11 $
+# by Keiju ISHITSUKA(Nippon Rational Inc.)
+#
+# --
+#
+#
+#
+
+require "e2mmap"
+require "thread"
+
+require "shell/error"
+require "shell/command-processor"
+require "shell/process-controller"
+
+class Shell
+ @RCS_ID='-$Id: shell.rb,v 1.8 2001/03/19 09:01:11 keiju Exp keiju $-'
+
+ include Error
+ extend Exception2MessageMapper
+
+# @cascade = true
+ # debug: true -> normal debug
+ # debug: 1 -> eval definition debug
+ # debug: 2 -> detail inspect debug
+ @debug = false
+ @verbose = true
+
+ class << Shell
+ attr :cascade, true
+ attr :debug, true
+ attr :verbose, true
+
+# alias cascade? cascade
+ alias debug? debug
+ alias verbose? verbose
+ @verbose = true
+
+ def debug=(val)
+ @debug = val
+ @verbose = val if val
+ end
+
+ def cd(path)
+ sh = new
+ sh.cd path
+ sh
+ end
+
+ def default_system_path
+ if @default_system_path
+ @default_system_path
+ else
+ ENV["PATH"].split(":")
+ end
+ end
+
+ def default_system_path=(path)
+ @default_system_path = path
+ end
+
+ def default_record_separator
+ if @default_record_separator
+ @default_record_separator
+ else
+ $/
+ end
+ end
+
+ def default_record_separator=(rs)
+ @default_record_separator = rs
+ end
+ end
+
+ def initialize
+ @cwd = Dir.pwd
+ @dir_stack = []
+ @umask = nil
+
+ @system_path = Shell.default_system_path
+ @record_separator = Shell.default_record_separator
+
+ @command_processor = CommandProcessor.new(self)
+ @process_controller = ProcessController.new(self)
+
+ @verbose = Shell.verbose
+ @debug = Shell.debug
+ end
+
+ attr_reader :system_path
+
+ def system_path=(path)
+ @system_path = path
+ rehash
+ end
+
+ attr :umask, true
+ attr :record_separator, true
+
+ attr :verbose, true
+ attr :debug, true
+
+ def debug=(val)
+ @debug = val
+ @verbose = val if val
+ end
+
+ alias verbose? verbose
+ alias debug? debug
+
+ attr_reader :command_processor
+ attr_reader :process_controller
+
+ def expand_path(path)
+ File.expand_path(path, @cwd)
+ end
+
+ # Most Shell commands are defined via CommandProcessor
+
+ #
+ # Dir related methods
+ #
+ # Shell#cwd/dir/getwd/pwd
+ # Shell#chdir/cd
+ # Shell#pushdir/pushd
+ # Shell#popdir/popd
+ # Shell#mkdir
+ # Shell#rmdir
+
+ attr :cwd
+ alias dir cwd
+ alias getwd cwd
+ alias pwd cwd
+
+ attr :dir_stack
+ alias dirs dir_stack
+
+ # If called as iterator, it restores the current directory when the
+ # block ends.
+ def chdir(path = nil)
+ if iterator?
+ cwd_old = @cwd
+ begin
+ chdir(path)
+ yield
+ ensure
+ chdir(cwd_old)
+ end
+ else
+ path = "~" unless path
+ @cwd = expand_path(path)
+ notify "current dir: #{@cwd}"
+ rehash
+ self
+ end
+ end
+ alias cd chdir
+
+ def pushdir(path = nil)
+ if iterator?
+ pushdir(path)
+ begin
+ yield
+ ensure
+ popdir
+ end
+ elsif path
+ @dir_stack.push @cwd
+ chdir path
+ notify "dir stack: [#{@dir_stack.join ', '}]"
+ self
+ else
+ if pop = @dir_stack.pop
+ @dir_stack.push @cwd
+ chdir pop
+ notify "dir stack: [#{@dir_stack.join ', '}]"
+ self
+ else
+ Shell.Fail DirStackEmpty
+ end
+ end
+ end
+ alias pushd pushdir
+
+ def popdir
+ if pop = @dir_stack.pop
+ chdir pop
+ notify "dir stack: [#{@dir_stack.join ', '}]"
+ self
+ else
+ Shell.Fail DirStackEmpty
+ end
+ end
+ alias popd popdir
+
+
+ #
+ # process management
+ #
+ def jobs
+ @process_controller.jobs
+ end
+
+ def kill(sig, command)
+ @process_controller.kill_job(sig, command)
+ end
+
+ #
+ # command definitions
+ #
+ def Shell.def_system_command(command, path = command)
+ CommandProcessor.def_system_command(command, path)
+ end
+
+ def Shell.undef_system_command(command)
+ CommandProcessor.undef_system_command(command)
+ end
+
+ def Shell.alias_command(ali, command, *opts, &block)
+ CommandProcessor.alias_command(ali, command, *opts, &block)
+ end
+
+ def Shell.unalias_command(ali)
+ CommandProcessor.unalias_command(ali)
+ end
+
+ def Shell.install_system_commands(pre = "sys_")
+ CommandProcessor.install_system_commands(pre)
+ end
+
+ #
+ def inspect
+ if debug.kind_of?(Integer) && debug > 2
+ super
+ else
+ to_s
+ end
+ end
+
+ def self.notify(*opts, &block)
+ Thread.exclusive do
+ if opts[-1].kind_of?(String)
+ yorn = verbose?
+ else
+ yorn = opts.pop
+ end
+ return unless yorn
+
+ _head = true
+ print opts.collect{|mes|
+ mes = mes.dup
+ yield mes if iterator?
+ if _head
+ _head = false
+ "shell: " + mes
+ else
+ " " + mes
+ end
+ }.join("\n")+"\n"
+ end
+ end
+
+ CommandProcessor.initialize
+ CommandProcessor.run_config
+end
diff --git a/ruby_1_8_5/lib/shell/builtin-command.rb b/ruby_1_8_5/lib/shell/builtin-command.rb
new file mode 100644
index 0000000000..02025c5d42
--- /dev/null
+++ b/ruby_1_8_5/lib/shell/builtin-command.rb
@@ -0,0 +1,154 @@
+#
+# shell/builtin-command.rb -
+# $Release Version: 0.6.0 $
+# $Revision: 1.1 $
+# $Date: 2001/05/17 10:02:48 $
+# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
+#
+# --
+#
+#
+#
+
+require "shell/filter"
+
+class Shell
+ class BuiltInCommand<Filter
+ def wait?
+ false
+ end
+ def active?
+ true
+ end
+ end
+
+ class Echo < BuiltInCommand
+ def initialize(sh, *strings)
+ super sh
+ @strings = strings
+ end
+
+ def each(rs = nil)
+ rs = @shell.record_separator unless rs
+ for str in @strings
+ yield str + rs
+ end
+ end
+ end
+
+ class Cat < BuiltInCommand
+ def initialize(sh, *filenames)
+ super sh
+ @cat_files = filenames
+ end
+
+ def each(rs = nil)
+ if @cat_files.empty?
+ super
+ else
+ for src in @cat_files
+ @shell.foreach(src, rs){|l| yield l}
+ end
+ end
+ end
+ end
+
+ class Glob < BuiltInCommand
+ def initialize(sh, pattern)
+ super sh
+
+ @pattern = pattern
+ Thread.critical = true
+ back = Dir.pwd
+ begin
+ Dir.chdir @shell.cwd
+ @files = Dir[pattern]
+ ensure
+ Dir.chdir back
+ Thread.critical = false
+ end
+ end
+
+ def each(rs = nil)
+ rs = @shell.record_separator unless rs
+ for f in @files
+ yield f+rs
+ end
+ end
+ end
+
+# class Sort < Cat
+# def initialize(sh, *filenames)
+# super
+# end
+#
+# def each(rs = nil)
+# ary = []
+# super{|l| ary.push l}
+# for l in ary.sort!
+# yield l
+# end
+# end
+# end
+
+ class AppendIO < BuiltInCommand
+ def initialize(sh, io, filter)
+ super sh
+ @input = filter
+ @io = io
+ end
+
+ def input=(filter)
+ @input.input=filter
+ for l in @input
+ @io << l
+ end
+ end
+
+ end
+
+ class AppendFile < AppendIO
+ def initialize(sh, to_filename, filter)
+ @file_name = to_filename
+ io = sh.open(to_filename, "a")
+ super(sh, io, filter)
+ end
+
+ def input=(filter)
+ begin
+ super
+ ensure
+ @io.close
+ end
+ end
+ end
+
+ class Tee < BuiltInCommand
+ def initialize(sh, filename)
+ super sh
+ @to_filename = filename
+ end
+
+ def each(rs = nil)
+ to = @shell.open(@to_filename, "w")
+ begin
+ super{|l| to << l; yield l}
+ ensure
+ to.close
+ end
+ end
+ end
+
+ class Concat < BuiltInCommand
+ def initialize(sh, *jobs)
+ super(sh)
+ @jobs = jobs
+ end
+
+ def each(rs = nil)
+ while job = @jobs.shift
+ job.each{|l| yield l}
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/shell/command-processor.rb b/ruby_1_8_5/lib/shell/command-processor.rb
new file mode 100644
index 0000000000..f08440e457
--- /dev/null
+++ b/ruby_1_8_5/lib/shell/command-processor.rb
@@ -0,0 +1,592 @@
+#
+# shell/command-controller.rb -
+# $Release Version: 0.6.0 $
+# $Revision: 1.8.2.2 $
+# $Date: 2004/04/18 23:20:33 $
+# by Keiju ISHITSUKA(Nippon Rational Inc.)
+#
+# --
+#
+#
+#
+
+require "e2mmap"
+require "ftools"
+require "thread"
+
+require "shell/error"
+require "shell/filter"
+require "shell/system-command"
+require "shell/builtin-command"
+
+class Shell
+ class CommandProcessor
+# include Error
+
+ #
+ # initialize of Shell and related classes.
+ #
+ NoDelegateMethods = ["initialize", "expand_path"]
+ def self.initialize
+
+ install_builtin_commands
+
+ # define CommandProccessor#methods to Shell#methods and Filter#methods
+ for m in CommandProcessor.instance_methods(false) - NoDelegateMethods
+ add_delegate_command_to_shell(m)
+ end
+
+ def self.method_added(id)
+ add_delegate_command_to_shell(id)
+ end
+ end
+
+ #
+ # include run file.
+ #
+ def self.run_config
+ begin
+ load File.expand_path("~/.rb_shell") if ENV.key?("HOME")
+ rescue LoadError, Errno::ENOENT
+ rescue
+ print "load error: #{rc}\n"
+ print $!.class, ": ", $!, "\n"
+ for err in $@[0, $@.size - 2]
+ print "\t", err, "\n"
+ end
+ end
+ end
+
+ def initialize(shell)
+ @shell = shell
+ @system_commands = {}
+ end
+
+ #
+ # CommandProcessor#expand_path(path)
+ # path: String
+ # return: String
+ # returns the absolute path for <path>
+ #
+ def expand_path(path)
+ @shell.expand_path(path)
+ end
+
+ #
+ # File related commands
+ # Shell#foreach
+ # Shell#open
+ # Shell#unlink
+ # Shell#test
+ #
+ # -
+ #
+ # CommandProcessor#foreach(path, rs)
+ # path: String
+ # rs: String - record separator
+ # iterator
+ # Same as:
+ # File#foreach (when path is file)
+ # Dir#foreach (when path is directory)
+ # path is relative to pwd
+ #
+ def foreach(path = nil, *rs)
+ path = "." unless path
+ path = expand_path(path)
+
+ if File.directory?(path)
+ Dir.foreach(path){|fn| yield fn}
+ else
+ IO.foreach(path, *rs){|l| yield l}
+ end
+ end
+
+ #
+ # CommandProcessor#open(path, mode)
+ # path: String
+ # mode: String
+ # return: File or Dir
+ # Same as:
+ # File#open (when path is file)
+ # Dir#open (when path is directory)
+ # mode has an effect only when path is a file
+ #
+ def open(path, mode)
+ path = expand_path(path)
+ if File.directory?(path)
+ Dir.open(path)
+ else
+ effect_umask do
+ File.open(path, mode)
+ end
+ end
+ end
+ # public :open
+
+ #
+ # CommandProcessor#unlink(path)
+ # same as:
+ # Dir#unlink (when path is directory)
+ # File#unlink (when path is file)
+ #
+ def unlink(path)
+ path = expand_path(path)
+ if File.directory?(path)
+ Dir.unlink(path)
+ else
+ IO.unlink(path)
+ end
+ end
+
+ #
+ # CommandProcessor#test(command, file1, file2)
+ # CommandProcessor#[command, file1, file2]
+ # command: char or String or Symbol
+ # file1: String
+ # file2: String(optional)
+ # return: Boolean
+ # same as:
+ # test() (when command is char or length 1 string or symbol)
+ # FileTest.command (others)
+ # example:
+ # sh[?e, "foo"]
+ # sh[:e, "foo"]
+ # sh["e", "foo"]
+ # sh[:exists?, "foo"]
+ # sh["exists?", "foo"]
+ #
+ def test(command, file1, file2=nil)
+ file1 = expand_path(file1)
+ file2 = expand_path(file2) if file2
+ command = command.id2name if command.kind_of?(Symbol)
+
+ case command
+ when Integer
+ top_level_test(command, file1, file2)
+ when String
+ if command.size == 1
+ if file2
+ top_level_test(command, file1, file2)
+ else
+ top_level_test(command, file1)
+ end
+ else
+ if file2
+ FileTest.send(command, file1, file2)
+ else
+ FileTest.send(command, file1)
+ end
+ end
+ end
+ end
+ alias [] test
+
+ #
+ # Dir related methods
+ #
+ # Shell#mkdir
+ # Shell#rmdir
+ #
+ #--
+ #
+ # CommandProcessor#mkdir(*path)
+ # path: String
+ # same as Dir.mkdir()
+ #
+ def mkdir(*path)
+ for dir in path
+ Dir.mkdir(expand_path(dir))
+ end
+ end
+
+ #
+ # CommandProcessor#rmdir(*path)
+ # path: String
+ # same as Dir.rmdir()
+ #
+ def rmdir(*path)
+ for dir in path
+ Dir.rmdir(expand_path(dir))
+ end
+ end
+
+ #
+ # CommandProcessor#system(command, *opts)
+ # command: String
+ # opts: String
+ # return: SystemCommand
+ # Same as system() function
+ # example:
+ # print sh.system("ls", "-l")
+ # sh.system("ls", "-l") | sh.head > STDOUT
+ #
+ def system(command, *opts)
+ if opts.empty?
+ if command =~ /\*|\?|\{|\}|\[|\]|<|>|\(|\)|~|&|\||\\|\$|;|'|`|"|\n/
+ return SystemCommand.new(@shell, find_system_command("sh"), "-c", command)
+ else
+ command, *opts = command.split(/\s+/)
+ end
+ end
+ SystemCommand.new(@shell, find_system_command(command), *opts)
+ end
+
+ #
+ # ProcessCommand#rehash
+ # clear command hash table.
+ #
+ def rehash
+ @system_commands = {}
+ end
+
+ #
+ # ProcessCommand#transact
+ #
+ def check_point
+ @shell.process_controller.wait_all_jobs_execution
+ end
+ alias finish_all_jobs check_point
+
+ def transact(&block)
+ begin
+ @shell.instance_eval(&block)
+ ensure
+ check_point
+ end
+ end
+
+ #
+ # internal commands
+ #
+ def out(dev = STDOUT, &block)
+ dev.print transact(&block)
+ end
+
+ def echo(*strings)
+ Echo.new(@shell, *strings)
+ end
+
+ def cat(*filenames)
+ Cat.new(@shell, *filenames)
+ end
+
+ # def sort(*filenames)
+ # Sort.new(self, *filenames)
+ # end
+
+ def glob(pattern)
+ Glob.new(@shell, pattern)
+ end
+
+ def append(to, filter)
+ case to
+ when String
+ AppendFile.new(@shell, to, filter)
+ when IO
+ AppendIO.new(@shell, to, filter)
+ else
+ Shell.Fail Error::CantApplyMethod, "append", to.class
+ end
+ end
+
+ def tee(file)
+ Tee.new(@shell, file)
+ end
+
+ def concat(*jobs)
+ Concat.new(@shell, *jobs)
+ end
+
+ # %pwd, %cwd -> @pwd
+ def notify(*opts, &block)
+ Thread.exclusive do
+ Shell.notify(*opts) {|mes|
+ yield mes if iterator?
+
+ mes.gsub!("%pwd", "#{@cwd}")
+ mes.gsub!("%cwd", "#{@cwd}")
+ }
+ end
+ end
+
+ #
+ # private functions
+ #
+ def effect_umask
+ if @shell.umask
+ Thread.critical = true
+ save = File.umask
+ begin
+ yield
+ ensure
+ File.umask save
+ Thread.critical = false
+ end
+ else
+ yield
+ end
+ end
+ private :effect_umask
+
+ def find_system_command(command)
+ return command if /^\// =~ command
+ case path = @system_commands[command]
+ when String
+ if exists?(path)
+ return path
+ else
+ Shell.Fail Error::CommandNotFound, command
+ end
+ when false
+ Shell.Fail Error::CommandNotFound, command
+ end
+
+ for p in @shell.system_path
+ path = join(p, command)
+ if FileTest.exists?(path)
+ @system_commands[command] = path
+ return path
+ end
+ end
+ @system_commands[command] = false
+ Shell.Fail Error::CommandNotFound, command
+ end
+
+ #
+ # CommandProcessor.def_system_command(command, path)
+ # command: String
+ # path: String
+ # define 'command()' method as method.
+ #
+ def self.def_system_command(command, path = command)
+ begin
+ eval((d = %Q[def #{command}(*opts)
+ SystemCommand.new(@shell, '#{path}', *opts)
+ end]), nil, __FILE__, __LINE__ - 1)
+ rescue SyntaxError
+ Shell.notify "warn: Can't define #{command} path: #{path}."
+ end
+ Shell.notify "Define #{command} path: #{path}.", Shell.debug?
+ Shell.notify("Definition of #{command}: ", d,
+ Shell.debug.kind_of?(Integer) && Shell.debug > 1)
+ end
+
+ def self.undef_system_command(command)
+ command = command.id2name if command.kind_of?(Symbol)
+ remove_method(command)
+ Shell.module_eval{remove_method(command)}
+ Filter.module_eval{remove_method(command)}
+ self
+ end
+
+ # define command alias
+ # ex)
+ # def_alias_command("ls_c", "ls", "-C", "-F")
+ # def_alias_command("ls_c", "ls"){|*opts| ["-C", "-F", *opts]}
+ #
+ @alias_map = {}
+ def self.alias_map
+ @alias_map
+ end
+ def self.alias_command(ali, command, *opts, &block)
+ ali = ali.id2name if ali.kind_of?(Symbol)
+ command = command.id2name if command.kind_of?(Symbol)
+ begin
+ if iterator?
+ @alias_map[ali.intern] = proc
+
+ eval((d = %Q[def #{ali}(*opts)
+ @shell.__send__(:#{command},
+ *(CommandProcessor.alias_map[:#{ali}].call *opts))
+ end]), nil, __FILE__, __LINE__ - 1)
+
+ else
+ args = opts.collect{|opt| '"' + opt + '"'}.join(",")
+ eval((d = %Q[def #{ali}(*opts)
+ @shell.__send__(:#{command}, #{args}, *opts)
+ end]), nil, __FILE__, __LINE__ - 1)
+ end
+ rescue SyntaxError
+ Shell.notify "warn: Can't alias #{ali} command: #{command}."
+ Shell.notify("Definition of #{ali}: ", d)
+ raise
+ end
+ Shell.notify "Define #{ali} command: #{command}.", Shell.debug?
+ Shell.notify("Definition of #{ali}: ", d,
+ Shell.debug.kind_of?(Integer) && Shell.debug > 1)
+ self
+ end
+
+ def self.unalias_command(ali)
+ ali = ali.id2name if ali.kind_of?(Symbol)
+ @alias_map.delete ali.intern
+ undef_system_command(ali)
+ end
+
+ #
+ # CommandProcessor.def_builtin_commands(delegation_class, command_specs)
+ # delegation_class: Class or Module
+ # command_specs: [[command_name, [argument,...]],...]
+ # command_name: String
+ # arguments: String
+ # FILENAME?? -> expand_path(filename??)
+ # *FILENAME?? -> filename??.collect{|f|expand_path(f)}.join(", ")
+ # define command_name(argument,...) as
+ # delegation_class.command_name(argument,...)
+ #
+ def self.def_builtin_commands(delegation_class, command_specs)
+ for meth, args in command_specs
+ arg_str = args.collect{|arg| arg.downcase}.join(", ")
+ call_arg_str = args.collect{
+ |arg|
+ case arg
+ when /^(FILENAME.*)$/
+ format("expand_path(%s)", $1.downcase)
+ when /^(\*FILENAME.*)$/
+ # \*FILENAME* -> filenames.collect{|fn| expand_path(fn)}.join(", ")
+ $1.downcase + '.collect{|fn| expand_path(fn)}'
+ else
+ arg
+ end
+ }.join(", ")
+ d = %Q[def #{meth}(#{arg_str})
+ #{delegation_class}.#{meth}(#{call_arg_str})
+ end]
+ Shell.notify "Define #{meth}(#{arg_str})", Shell.debug?
+ Shell.notify("Definition of #{meth}: ", d,
+ Shell.debug.kind_of?(Integer) && Shell.debug > 1)
+ eval d
+ end
+ end
+
+ #
+ # CommandProcessor.install_system_commands(pre)
+ # pre: String - command name prefix
+ # defines every command which belongs in default_system_path via
+ # CommandProcessor.command(). It doesn't define already defined
+ # methods twice. By default, "pre_" is prefixes to each method
+ # name. Characters that may not be used in a method name are
+ # all converted to '_'. Definition errors are just ignored.
+ #
+ def self.install_system_commands(pre = "sys_")
+ defined_meth = {}
+ for m in Shell.methods
+ defined_meth[m] = true
+ end
+ sh = Shell.new
+ for path in Shell.default_system_path
+ next unless sh.directory? path
+ sh.cd path
+ sh.foreach do
+ |cn|
+ if !defined_meth[pre + cn] && sh.file?(cn) && sh.executable?(cn)
+ command = (pre + cn).gsub(/\W/, "_").sub(/^([0-9])/, '_\1')
+ begin
+ def_system_command(command, sh.expand_path(cn))
+ rescue
+ Shell.notify "warn: Can't define #{command} path: #{cn}"
+ end
+ defined_meth[command] = command
+ end
+ end
+ end
+ end
+
+ #----------------------------------------------------------------------
+ #
+ # class initializing methods -
+ #
+ #----------------------------------------------------------------------
+ def self.add_delegate_command_to_shell(id)
+ id = id.intern if id.kind_of?(String)
+ name = id.id2name
+ if Shell.method_defined?(id)
+ Shell.notify "warn: override definnition of Shell##{name}."
+ Shell.notify "warn: alias Shell##{name} to Shell##{name}_org.\n"
+ Shell.module_eval "alias #{name}_org #{name}"
+ end
+ Shell.notify "method added: Shell##{name}.", Shell.debug?
+ Shell.module_eval(%Q[def #{name}(*args, &block)
+ begin
+ @command_processor.__send__(:#{name}, *args, &block)
+ rescue Exception
+ $@.delete_if{|s| /:in `__getobj__'$/ =~ s} #`
+ $@.delete_if{|s| /^\\(eval\\):/ =~ s}
+ raise
+ end
+ end], __FILE__, __LINE__)
+
+ if Shell::Filter.method_defined?(id)
+ Shell.notify "warn: override definnition of Shell::Filter##{name}."
+ Shell.notify "warn: alias Shell##{name} to Shell::Filter##{name}_org."
+ Filter.module_eval "alias #{name}_org #{name}"
+ end
+ Shell.notify "method added: Shell::Filter##{name}.", Shell.debug?
+ Filter.module_eval(%Q[def #{name}(*args, &block)
+ begin
+ self | @shell.__send__(:#{name}, *args, &block)
+ rescue Exception
+ $@.delete_if{|s| /:in `__getobj__'$/ =~ s} #`
+ $@.delete_if{|s| /^\\(eval\\):/ =~ s}
+ raise
+ end
+ end], __FILE__, __LINE__)
+ end
+
+ #
+ # define default builtin commands
+ #
+ def self.install_builtin_commands
+ # method related File.
+ # (exclude open/foreach/unlink)
+ normal_delegation_file_methods = [
+ ["atime", ["FILENAME"]],
+ ["basename", ["fn", "*opts"]],
+ ["chmod", ["mode", "*FILENAMES"]],
+ ["chown", ["owner", "group", "*FILENAME"]],
+ ["ctime", ["FILENAMES"]],
+ ["delete", ["*FILENAMES"]],
+ ["dirname", ["FILENAME"]],
+ ["ftype", ["FILENAME"]],
+ ["join", ["*items"]],
+ ["link", ["FILENAME_O", "FILENAME_N"]],
+ ["lstat", ["FILENAME"]],
+ ["mtime", ["FILENAME"]],
+ ["readlink", ["FILENAME"]],
+ ["rename", ["FILENAME_FROM", "FILENAME_TO"]],
+ # ["size", ["FILENAME"]],
+ ["split", ["pathname"]],
+ ["stat", ["FILENAME"]],
+ ["symlink", ["FILENAME_O", "FILENAME_N"]],
+ ["truncate", ["FILENAME", "length"]],
+ ["utime", ["atime", "mtime", "*FILENAMES"]]]
+
+ def_builtin_commands(File, normal_delegation_file_methods)
+ alias_method :rm, :delete
+
+ # method related FileTest
+ def_builtin_commands(FileTest,
+ FileTest.singleton_methods(false).collect{|m| [m, ["FILENAME"]]})
+
+ # method related ftools
+ normal_delegation_ftools_methods = [
+ ["syscopy", ["FILENAME_FROM", "FILENAME_TO"]],
+ ["copy", ["FILENAME_FROM", "FILENAME_TO"]],
+ ["move", ["FILENAME_FROM", "FILENAME_TO"]],
+ ["compare", ["FILENAME_FROM", "FILENAME_TO"]],
+ ["safe_unlink", ["*FILENAMES"]],
+ ["makedirs", ["*FILENAMES"]],
+ # ["chmod", ["mode", "*FILENAMES"]],
+ ["install", ["FILENAME_FROM", "FILENAME_TO", "mode"]],
+ ]
+ def_builtin_commands(File,
+ normal_delegation_ftools_methods)
+ alias_method :cmp, :compare
+ alias_method :mv, :move
+ alias_method :cp, :copy
+ alias_method :rm_f, :safe_unlink
+ alias_method :mkpath, :makedirs
+ end
+
+ end
+end
diff --git a/ruby_1_8_5/lib/shell/error.rb b/ruby_1_8_5/lib/shell/error.rb
new file mode 100644
index 0000000000..2f176862a8
--- /dev/null
+++ b/ruby_1_8_5/lib/shell/error.rb
@@ -0,0 +1,26 @@
+#
+# shell/error.rb -
+# $Release Version: 0.6.0 $
+# $Revision: 1.2 $
+# $Date: 2003/02/07 19:00:21 $
+# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
+#
+# --
+#
+#
+#
+
+require "e2mmap"
+
+class Shell
+ module Error
+ extend Exception2MessageMapper
+ def_e2message TypeError, "wrong argument type %s (expected %s)"
+
+ def_exception :DirStackEmpty, "Directory stack empty."
+ def_exception :CantDefine, "Can't define method(%s, %s)."
+ def_exception :CantApplyMethod, "This method(%s) does not apply to this type(%s)."
+ def_exception :CommandNotFound, "Command not found(%s)."
+ end
+end
+
diff --git a/ruby_1_8_5/lib/shell/filter.rb b/ruby_1_8_5/lib/shell/filter.rb
new file mode 100644
index 0000000000..12cc5206f8
--- /dev/null
+++ b/ruby_1_8_5/lib/shell/filter.rb
@@ -0,0 +1,110 @@
+#
+# shell/filter.rb -
+# $Release Version: 0.6.0 $
+# $Revision: 1.3.2.1 $
+# $Date: 2004/03/21 12:21:11 $
+# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
+#
+# --
+#
+#
+#
+
+class Shell
+ #
+ # Filter
+ # A method to require
+ # each()
+ #
+ class Filter
+ include Enumerable
+
+ def initialize(sh)
+ @shell = sh # parent shell
+ @input = nil # input filter
+ end
+
+ attr_reader :input
+
+ def input=(filter)
+ @input = filter
+ end
+
+ def each(rs = nil)
+ rs = @shell.record_separator unless rs
+ if @input
+ @input.each(rs){|l| yield l}
+ end
+ end
+
+ def < (src)
+ case src
+ when String
+ cat = Cat.new(@shell, src)
+ cat | self
+ when IO
+ self.input = src
+ self
+ else
+ Shell.Fail Error::CantApplyMethod, "<", to.class
+ end
+ end
+
+ def > (to)
+ case to
+ when String
+ dst = @shell.open(to, "w")
+ begin
+ each(){|l| dst << l}
+ ensure
+ dst.close
+ end
+ when IO
+ each(){|l| to << l}
+ else
+ Shell.Fail Error::CantApplyMethod, ">", to.class
+ end
+ self
+ end
+
+ def >> (to)
+ begin
+ Shell.cd(@shell.pwd).append(to, self)
+ rescue CantApplyMethod
+ Shell.Fail Error::CantApplyMethod, ">>", to.class
+ end
+ end
+
+ def | (filter)
+ filter.input = self
+ if active?
+ @shell.process_controller.start_job filter
+ end
+ filter
+ end
+
+ def + (filter)
+ Join.new(@shell, self, filter)
+ end
+
+ def to_a
+ ary = []
+ each(){|l| ary.push l}
+ ary
+ end
+
+ def to_s
+ str = ""
+ each(){|l| str.concat l}
+ str
+ end
+
+ def inspect
+ if @shell.debug.kind_of?(Integer) && @shell.debug > 2
+ super
+ else
+ to_s
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/shell/process-controller.rb b/ruby_1_8_5/lib/shell/process-controller.rb
new file mode 100644
index 0000000000..573edb6829
--- /dev/null
+++ b/ruby_1_8_5/lib/shell/process-controller.rb
@@ -0,0 +1,260 @@
+#
+# shell/process-controller.rb -
+# $Release Version: 0.6.0 $
+# $Revision: 1.3 $
+# $Date: 2003/10/16 17:47:19 $
+# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
+#
+# --
+#
+#
+#
+
+require "mutex_m"
+require "monitor"
+require "sync"
+
+class Shell
+ class ProcessController
+
+ @ProcessControllers = {}
+ @ProcessControllers.extend Mutex_m
+
+ class<<self
+
+ def process_controllers_exclusive
+ begin
+ @ProcessControllers.lock unless Thread.critical
+ yield
+ ensure
+ @ProcessControllers.unlock unless Thread.critical
+ end
+ end
+
+ def activate(pc)
+ process_controllers_exclusive do
+ @ProcessControllers[pc] ||= 0
+ @ProcessControllers[pc] += 1
+ end
+ end
+
+ def inactivate(pc)
+ process_controllers_exclusive do
+ if @ProcessControllers[pc]
+ if (@ProcessControllers[pc] -= 1) == 0
+ @ProcessControllers.delete(pc)
+ end
+ end
+ end
+ end
+
+ def each_active_object
+ process_controllers_exclusive do
+ for ref in @ProcessControllers.keys
+ yield ref
+ end
+ end
+ end
+ end
+
+ def initialize(shell)
+ @shell = shell
+ @waiting_jobs = []
+ @active_jobs = []
+ @jobs_sync = Sync.new
+
+ @job_monitor = Mutex.new
+ @job_condition = ConditionVariable.new
+ end
+
+ def jobs
+ jobs = []
+ @jobs_sync.synchronize(:SH) do
+ jobs.concat @waiting_jobs
+ jobs.concat @active_jobs
+ end
+ jobs
+ end
+
+ def active_jobs
+ @active_jobs
+ end
+
+ def waiting_jobs
+ @waiting_jobs
+ end
+
+ def jobs_exist?
+ @jobs_sync.synchronize(:SH) do
+ @active_jobs.empty? or @waiting_jobs.empty?
+ end
+ end
+
+ def active_jobs_exist?
+ @jobs_sync.synchronize(:SH) do
+ @active_jobs.empty?
+ end
+ end
+
+ def waiting_jobs_exist?
+ @jobs_sync.synchronize(:SH) do
+ @waiting_jobs.empty?
+ end
+ end
+
+ # schedule a command
+ def add_schedule(command)
+ @jobs_sync.synchronize(:EX) do
+ ProcessController.activate(self)
+ if @active_jobs.empty?
+ start_job command
+ else
+ @waiting_jobs.push(command)
+ end
+ end
+ end
+
+ # start a job
+ def start_job(command = nil)
+ @jobs_sync.synchronize(:EX) do
+ if command
+ return if command.active?
+ @waiting_jobs.delete command
+ else
+ command = @waiting_jobs.shift
+ return unless command
+ end
+ @active_jobs.push command
+ command.start
+
+ # start all jobs that input from the job
+ for job in @waiting_jobs
+ start_job(job) if job.input == command
+ end
+ end
+ end
+
+ def waiting_job?(job)
+ @jobs_sync.synchronize(:SH) do
+ @waiting_jobs.include?(job)
+ end
+ end
+
+ def active_job?(job)
+ @jobs_sync.synchronize(:SH) do
+ @active_jobs.include?(job)
+ end
+ end
+
+ # terminate a job
+ def terminate_job(command)
+ @jobs_sync.synchronize(:EX) do
+ @active_jobs.delete command
+ ProcessController.inactivate(self)
+ if @active_jobs.empty?
+ start_job
+ end
+ end
+ end
+
+ # kill a job
+ def kill_job(sig, command)
+ @jobs_sync.synchronize(:SH) do
+ if @waiting_jobs.delete command
+ ProcessController.inactivate(self)
+ return
+ elsif @active_jobs.include?(command)
+ begin
+ r = command.kill(sig)
+ ProcessController.inactivate(self)
+ rescue
+ print "Shell: Warn: $!\n" if @shell.verbose?
+ return nil
+ end
+ @active_jobs.delete command
+ r
+ end
+ end
+ end
+
+ # wait for all jobs to terminate
+ def wait_all_jobs_execution
+ @job_monitor.synchronize do
+ begin
+ while !jobs.empty?
+ @job_condition.wait(@job_monitor)
+ end
+ ensure
+ redo unless jobs.empty?
+ end
+ end
+ end
+
+ # simple fork
+ def sfork(command, &block)
+ pipe_me_in, pipe_peer_out = IO.pipe
+ pipe_peer_in, pipe_me_out = IO.pipe
+ Thread.critical = true
+
+ STDOUT.flush
+ ProcessController.each_active_object do |pc|
+ for jobs in pc.active_jobs
+ jobs.flush
+ end
+ end
+
+ pid = fork {
+ Thread.critical = true
+
+ Thread.list.each do |th|
+ th.kill unless [Thread.main, Thread.current].include?(th)
+ end
+
+ STDIN.reopen(pipe_peer_in)
+ STDOUT.reopen(pipe_peer_out)
+
+ ObjectSpace.each_object(IO) do |io|
+ if ![STDIN, STDOUT, STDERR].include?(io)
+ io.close unless io.closed?
+ end
+ end
+ yield
+ }
+
+ pipe_peer_in.close
+ pipe_peer_out.close
+ command.notify "job(%name:##{pid}) start", @shell.debug?
+ Thread.critical = false
+
+ th = Thread.start {
+ Thread.critical = true
+ begin
+ _pid = nil
+ command.notify("job(%id) start to waiting finish.", @shell.debug?)
+ Thread.critical = false
+ _pid = Process.waitpid(pid, nil)
+ rescue Errno::ECHILD
+ command.notify "warn: job(%id) was done already waitipd."
+ _pid = true
+ ensure
+ # when the process ends, wait until the command termintes
+ if _pid
+ else
+ command.notify("notice: Process finishing...",
+ "wait for Job[%id] to finish.",
+ "You can use Shell#transact or Shell#check_point for more safe execution.")
+ redo
+ end
+ Thread.exclusive do
+ @job_monitor.synchronize do
+ terminate_job(command)
+ @job_condition.signal
+ command.notify "job(%id) finish.", @shell.debug?
+ end
+ end
+ end
+ }
+ return pid, pipe_me_in, pipe_me_out
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/shell/system-command.rb b/ruby_1_8_5/lib/shell/system-command.rb
new file mode 100644
index 0000000000..8b1cba98c8
--- /dev/null
+++ b/ruby_1_8_5/lib/shell/system-command.rb
@@ -0,0 +1,168 @@
+#
+# shell/system-command.rb -
+# $Release Version: 0.6.0 $
+# $Revision: 1.2.2.1 $
+# $Date: 2004/03/21 12:21:11 $
+# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
+#
+# --
+#
+#
+#
+
+require "shell/filter"
+
+class Shell
+ class SystemCommand < Filter
+ def initialize(sh, command, *opts)
+ if t = opts.find{|opt| !opt.kind_of?(String) && opt.class}
+ Shell.Fail Error::TypeError, t.class, "String"
+ end
+ super(sh)
+ @command = command
+ @opts = opts
+
+ @input_queue = Queue.new
+ @pid = nil
+
+ sh.process_controller.add_schedule(self)
+ end
+
+ attr_reader :command
+ alias name command
+
+ def wait?
+ @shell.process_controller.waiting_job?(self)
+ end
+
+ def active?
+ @shell.process_controller.active_job?(self)
+ end
+
+ def input=(inp)
+ super
+ if active?
+ start_export
+ end
+ end
+
+ def start
+ @pid, @pipe_in, @pipe_out = @shell.process_controller.sfork(self) {
+ Dir.chdir @shell.pwd
+ exec(@command, *@opts)
+ }
+ if @input
+ start_export
+ end
+ start_import
+ end
+
+ def flush
+ @pipe_out.flush if @pipe_out and !@pipe_out.closed?
+ end
+
+ def terminate
+ begin
+ @pipe_in.close
+ rescue IOError
+ end
+ begin
+ @pipe_out.close
+ rescue IOError
+ end
+ end
+
+ def kill(sig)
+ if @pid
+ Process.kill(sig, @pid)
+ end
+ end
+
+
+ def start_import
+# Thread.critical = true
+ notify "Job(%id) start imp-pipe.", @shell.debug?
+ rs = @shell.record_separator unless rs
+ _eop = true
+# Thread.critical = false
+ th = Thread.start {
+ Thread.critical = true
+ begin
+ Thread.critical = false
+ while l = @pipe_in.gets
+ @input_queue.push l
+ end
+ _eop = false
+ rescue Errno::EPIPE
+ _eop = false
+ ensure
+ if _eop
+ notify("warn: Process finishing...",
+ "wait for Job[%id] to finish pipe importing.",
+ "You can use Shell#transact or Shell#check_point for more safe execution.")
+# Tracer.on
+ Thread.current.run
+ redo
+ end
+ Thread.exclusive do
+ notify "job(%id}) close imp-pipe.", @shell.debug?
+ @input_queue.push :EOF
+ @pipe_in.close
+ end
+ end
+ }
+ end
+
+ def start_export
+ notify "job(%id) start exp-pipe.", @shell.debug?
+ _eop = true
+ th = Thread.start{
+ Thread.critical = true
+ begin
+ Thread.critical = false
+ @input.each{|l| @pipe_out.print l}
+ _eop = false
+ rescue Errno::EPIPE
+ _eop = false
+ ensure
+ if _eop
+ notify("shell: warn: Process finishing...",
+ "wait for Job(%id) to finish pipe exporting.",
+ "You can use Shell#transact or Shell#check_point for more safe execution.")
+# Tracer.on
+ redo
+ end
+ Thread.exclusive do
+ notify "job(%id) close exp-pipe.", @shell.debug?
+ @pipe_out.close
+ end
+ end
+ }
+ end
+
+ alias super_each each
+ def each(rs = nil)
+ while (l = @input_queue.pop) != :EOF
+ yield l
+ end
+ end
+
+ # ex)
+ # if you wish to output:
+ # "shell: job(#{@command}:#{@pid}) close pipe-out."
+ # then
+ # mes: "job(%id) close pipe-out."
+ # yorn: Boolean(@shell.debug? or @shell.verbose?)
+ def notify(*opts, &block)
+ Thread.exclusive do
+ @shell.notify(*opts) {|mes|
+ yield mes if iterator?
+
+ mes.gsub!("%id", "#{@command}:##{@pid}")
+ mes.gsub!("%name", "#{@command}")
+ mes.gsub!("%pid", "#{@pid}")
+ }
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/shell/version.rb b/ruby_1_8_5/lib/shell/version.rb
new file mode 100644
index 0000000000..46b3425afa
--- /dev/null
+++ b/ruby_1_8_5/lib/shell/version.rb
@@ -0,0 +1,16 @@
+#
+# version.rb - shell version definition file
+# $Release Version: 0.6.0$
+# $Revision: 1.1 $
+# $Date: 2001/05/17 10:02:48 $
+# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
+#
+# --
+#
+#
+#
+
+class Shell
+ @RELEASE_VERSION = "0.6.0"
+ @LAST_UPDATE_DATE = "01/03/19"
+end
diff --git a/ruby_1_8_5/lib/shellwords.rb b/ruby_1_8_5/lib/shellwords.rb
new file mode 100644
index 0000000000..e87b9e656b
--- /dev/null
+++ b/ruby_1_8_5/lib/shellwords.rb
@@ -0,0 +1,60 @@
+#
+# shellwords.rb: Split text into an array of tokens a la UNIX shell
+#
+
+#
+# This module is originally a port of shellwords.pl, but modified to
+# conform to POSIX / SUSv3 (IEEE Std 1003.1-2001).
+#
+# Examples:
+#
+# require 'shellwords'
+# words = Shellwords.shellwords(line)
+#
+# or
+#
+# require 'shellwords'
+# include Shellwords
+# words = shellwords(line)
+#
+module Shellwords
+
+ #
+ # Split text into an array of tokens in the same way the UNIX Bourne
+ # shell does.
+ #
+ # See the +Shellwords+ module documentation for an example.
+ #
+ def shellwords(line)
+ line = String.new(line) rescue
+ raise(ArgumentError, "Argument must be a string")
+ line.lstrip!
+ words = []
+ until line.empty?
+ field = ''
+ loop do
+ if line.sub!(/\A"(([^"\\]|\\.)*)"/, '') then
+ snippet = $1.gsub(/\\(.)/, '\1')
+ elsif line =~ /\A"/ then
+ raise ArgumentError, "Unmatched double quote: #{line}"
+ elsif line.sub!(/\A'([^']*)'/, '') then
+ snippet = $1
+ elsif line =~ /\A'/ then
+ raise ArgumentError, "Unmatched single quote: #{line}"
+ elsif line.sub!(/\A\\(.)?/, '') then
+ snippet = $1 || '\\'
+ elsif line.sub!(/\A([^\s\\'"]+)/, '') then
+ snippet = $1
+ else
+ line.lstrip!
+ break
+ end
+ field.concat(snippet)
+ end
+ words.push(field)
+ end
+ words
+ end
+
+ module_function :shellwords
+end
diff --git a/ruby_1_8_5/lib/singleton.rb b/ruby_1_8_5/lib/singleton.rb
new file mode 100644
index 0000000000..0ab8517275
--- /dev/null
+++ b/ruby_1_8_5/lib/singleton.rb
@@ -0,0 +1,360 @@
+# The Singleton module implements the Singleton pattern.
+#
+# Usage:
+# class Klass
+# include Singleton
+# # ...
+# end
+#
+# * this ensures that only one instance of Klass lets call it
+# ``the instance'' can be created.
+#
+# a,b = Klass.instance, Klass.instance
+# a == b # => true
+# a.new # NoMethodError - new is private ...
+#
+# * ``The instance'' is created at instantiation time, in other
+# words the first call of Klass.instance(), thus
+#
+# class OtherKlass
+# include Singleton
+# # ...
+# end
+# ObjectSpace.each_object(OtherKlass){} # => 0.
+#
+# * This behavior is preserved under inheritance and cloning.
+#
+#
+#
+# This is achieved by marking
+# * Klass.new and Klass.allocate - as private
+#
+# Providing (or modifying) the class methods
+# * Klass.inherited(sub_klass) and Klass.clone() -
+# to ensure that the Singleton pattern is properly
+# inherited and cloned.
+#
+# * Klass.instance() - returning ``the instance''. After a
+# successful self modifying (normally the first) call the
+# method body is a simple:
+#
+# def Klass.instance()
+# return @__instance__
+# end
+#
+# * Klass._load(str) - calling Klass.instance()
+#
+# * Klass._instantiate?() - returning ``the instance'' or
+# nil. This hook method puts a second (or nth) thread calling
+# Klass.instance() on a waiting loop. The return value
+# signifies the successful completion or premature termination
+# of the first, or more generally, current "instantiation thread".
+#
+#
+# The instance method of Singleton are
+# * clone and dup - raising TypeErrors to prevent cloning or duping
+#
+# * _dump(depth) - returning the empty string. Marshalling strips
+# by default all state information, e.g. instance variables and
+# taint state, from ``the instance''. Providing custom _load(str)
+# and _dump(depth) hooks allows the (partially) resurrections of
+# a previous state of ``the instance''.
+
+
+
+module Singleton
+ # disable build-in copying methods
+ def clone
+ raise TypeError, "can't clone instance of singleton #{self.class}"
+ end
+ def dup
+ raise TypeError, "can't dup instance of singleton #{self.class}"
+ end
+
+ private
+ # default marshalling strategy
+ def _dump(depth=-1)
+ ''
+ end
+end
+
+
+class << Singleton
+ # Method body of first instance call.
+ FirstInstanceCall = proc do
+ # @__instance__ takes on one of the following values
+ # * nil - before and after a failed creation
+ # * false - during creation
+ # * sub_class instance - after a successful creation
+ # the form makes up for the lack of returns in progs
+ Thread.critical = true
+ if @__instance__.nil?
+ @__instance__ = false
+ Thread.critical = false
+ begin
+ @__instance__ = new
+ ensure
+ if @__instance__
+ class <<self
+ remove_method :instance
+ def instance; @__instance__ end
+ end
+ else
+ @__instance__ = nil # failed instance creation
+ end
+ end
+ elsif _instantiate?()
+ Thread.critical = false
+ else
+ @__instance__ = false
+ Thread.critical = false
+ begin
+ @__instance__ = new
+ ensure
+ if @__instance__
+ class <<self
+ remove_method :instance
+ def instance; @__instance__ end
+ end
+ else
+ @__instance__ = nil
+ end
+ end
+ end
+ @__instance__
+ end
+
+ module SingletonClassMethods
+ # properly clone the Singleton pattern - did you know
+ # that duping doesn't copy class methods?
+ def clone
+ Singleton.__init__(super)
+ end
+
+ private
+
+ # ensure that the Singleton pattern is properly inherited
+ def inherited(sub_klass)
+ super
+ Singleton.__init__(sub_klass)
+ end
+
+ def _load(str)
+ instance
+ end
+
+ # waiting-loop hook
+ def _instantiate?()
+ while false.equal?(@__instance__)
+ Thread.critical = false
+ sleep(0.08) # timeout
+ Thread.critical = true
+ end
+ @__instance__
+ end
+ end
+
+ def __init__(klass)
+ klass.instance_eval { @__instance__ = nil }
+ class << klass
+ define_method(:instance,FirstInstanceCall)
+ end
+ klass
+ end
+
+ private
+ # extending an object with Singleton is a bad idea
+ undef_method :extend_object
+
+ def append_features(mod)
+ # help out people counting on transitive mixins
+ unless mod.instance_of?(Class)
+ raise TypeError, "Inclusion of the OO-Singleton module in module #{mod}"
+ end
+ super
+ end
+
+ def included(klass)
+ super
+ klass.private_class_method :new, :allocate
+ klass.extend SingletonClassMethods
+ Singleton.__init__(klass)
+ end
+end
+
+
+
+if __FILE__ == $0
+
+def num_of_instances(klass)
+ "#{ObjectSpace.each_object(klass){}} #{klass} instance(s)"
+end
+
+# The basic and most important example.
+
+class SomeSingletonClass
+ include Singleton
+end
+puts "There are #{num_of_instances(SomeSingletonClass)}"
+
+a = SomeSingletonClass.instance
+b = SomeSingletonClass.instance # a and b are same object
+puts "basic test is #{a == b}"
+
+begin
+ SomeSingletonClass.new
+rescue NoMethodError => mes
+ puts mes
+end
+
+
+
+puts "\nThreaded example with exception and customized #_instantiate?() hook"; p
+Thread.abort_on_exception = false
+
+class Ups < SomeSingletonClass
+ def initialize
+ self.class.__sleep
+ puts "initialize called by thread ##{Thread.current[:i]}"
+ end
+end
+
+class << Ups
+ def _instantiate?
+ @enter.push Thread.current[:i]
+ while false.equal?(@__instance__)
+ Thread.critical = false
+ sleep 0.08
+ Thread.critical = true
+ end
+ @leave.push Thread.current[:i]
+ @__instance__
+ end
+
+ def __sleep
+ sleep(rand(0.08))
+ end
+
+ def new
+ begin
+ __sleep
+ raise "boom - thread ##{Thread.current[:i]} failed to create instance"
+ ensure
+ # simple flip-flop
+ class << self
+ remove_method :new
+ end
+ end
+ end
+
+ def instantiate_all
+ @enter = []
+ @leave = []
+ 1.upto(9) {|i|
+ Thread.new {
+ begin
+ Thread.current[:i] = i
+ __sleep
+ instance
+ rescue RuntimeError => mes
+ puts mes
+ end
+ }
+ }
+ puts "Before there were #{num_of_instances(self)}"
+ sleep 3
+ puts "Now there is #{num_of_instances(self)}"
+ puts "#{@enter.join '; '} was the order of threads entering the waiting loop"
+ puts "#{@leave.join '; '} was the order of threads leaving the waiting loop"
+ end
+end
+
+
+Ups.instantiate_all
+# results in message like
+# Before there were 0 Ups instance(s)
+# boom - thread #6 failed to create instance
+# initialize called by thread #3
+# Now there is 1 Ups instance(s)
+# 3; 2; 1; 8; 4; 7; 5 was the order of threads entering the waiting loop
+# 3; 2; 1; 7; 4; 8; 5 was the order of threads leaving the waiting loop
+
+
+puts "\nLets see if class level cloning really works"
+Yup = Ups.clone
+def Yup.new
+ begin
+ __sleep
+ raise "boom - thread ##{Thread.current[:i]} failed to create instance"
+ ensure
+ # simple flip-flop
+ class << self
+ remove_method :new
+ end
+ end
+end
+Yup.instantiate_all
+
+
+puts "\n\n","Customized marshalling"
+class A
+ include Singleton
+ attr_accessor :persist, :die
+ def _dump(depth)
+ # this strips the @die information from the instance
+ Marshal.dump(@persist,depth)
+ end
+end
+
+def A._load(str)
+ instance.persist = Marshal.load(str)
+ instance
+end
+
+a = A.instance
+a.persist = ["persist"]
+a.die = "die"
+a.taint
+
+stored_state = Marshal.dump(a)
+# change state
+a.persist = nil
+a.die = nil
+b = Marshal.load(stored_state)
+p a == b # => true
+p a.persist # => ["persist"]
+p a.die # => nil
+
+
+puts "\n\nSingleton with overridden default #inherited() hook"
+class Up
+end
+def Up.inherited(sub_klass)
+ puts "#{sub_klass} subclasses #{self}"
+end
+
+
+class Middle < Up
+ include Singleton
+end
+
+class Down < Middle; end
+
+puts "and basic \"Down test\" is #{Down.instance == Down.instance}\n
+Various exceptions"
+
+begin
+ module AModule
+ include Singleton
+ end
+rescue TypeError => mes
+ puts mes #=> Inclusion of the OO-Singleton module in module AModule
+end
+
+begin
+ 'aString'.extend Singleton
+rescue NoMethodError => mes
+ puts mes #=> undefined method `extend_object' for Singleton:Module
+end
+
+end
diff --git a/ruby_1_8_5/lib/soap/attachment.rb b/ruby_1_8_5/lib/soap/attachment.rb
new file mode 100644
index 0000000000..1a59b14018
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/attachment.rb
@@ -0,0 +1,107 @@
+# soap/attachment.rb: SOAP4R - SwA implementation.
+# Copyright (C) 2002, 2003 Jamie Herre and NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'soap/baseData'
+require 'soap/mapping'
+
+
+module SOAP
+
+
+class SOAPAttachment < SOAPExternalReference
+ attr_reader :data
+
+ def initialize(value)
+ super()
+ @data = value
+ end
+
+private
+
+ def external_contentid
+ @data.contentid
+ end
+end
+
+
+class Attachment
+ attr_reader :io
+ attr_accessor :contenttype
+
+ def initialize(string_or_readable = nil)
+ @string_or_readable = string_or_readable
+ @contenttype = "application/octet-stream"
+ @contentid = nil
+ end
+
+ def contentid
+ @contentid ||= Attachment.contentid(self)
+ end
+
+ def contentid=(contentid)
+ @contentid = contentid
+ end
+
+ def mime_contentid
+ '<' + contentid + '>'
+ end
+
+ def content
+ if @content == nil and @string_or_readable != nil
+ @content = @string_or_readable.respond_to?(:read) ?
+ @string_or_readable.read : @string_or_readable
+ end
+ @content
+ end
+
+ def to_s
+ content
+ end
+
+ def write(out)
+ out.write(content)
+ end
+
+ def save(filename)
+ File.open(filename, "wb") do |f|
+ write(f)
+ end
+ end
+
+ def self.contentid(obj)
+ # this needs to be fixed
+ [obj.__id__.to_s, Process.pid.to_s].join('.')
+ end
+
+ def self.mime_contentid(obj)
+ '<' + contentid(obj) + '>'
+ end
+end
+
+
+module Mapping
+ class AttachmentFactory < SOAP::Mapping::Factory
+ def obj2soap(soap_class, obj, info, map)
+ soap_obj = soap_class.new(obj)
+ mark_marshalled_obj(obj, soap_obj)
+ soap_obj
+ end
+
+ def soap2obj(obj_class, node, info, map)
+ obj = node.data
+ mark_unmarshalled_obj(node, obj)
+ return true, obj
+ end
+ end
+
+ DefaultRegistry.add(::SOAP::Attachment, ::SOAP::SOAPAttachment,
+ AttachmentFactory.new, nil)
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/soap/baseData.rb b/ruby_1_8_5/lib/soap/baseData.rb
new file mode 100644
index 0000000000..0e8b00d450
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/baseData.rb
@@ -0,0 +1,942 @@
+# soap/baseData.rb: SOAP4R - Base type library
+# Copyright (C) 2000, 2001, 2003-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/datatypes'
+require 'soap/soap'
+
+
+module SOAP
+
+
+###
+## Mix-in module for SOAP base type classes.
+#
+module SOAPModuleUtils
+ include SOAP
+
+public
+
+ def decode(elename)
+ d = self.new
+ d.elename = elename
+ d
+ end
+end
+
+
+###
+## for SOAP type(base and compound)
+#
+module SOAPType
+ attr_accessor :encodingstyle
+ attr_accessor :elename
+ attr_accessor :id
+ attr_reader :precedents
+ attr_accessor :root
+ attr_accessor :parent
+ attr_accessor :position
+ attr_reader :extraattr
+ attr_accessor :definedtype
+
+ def initialize(*arg)
+ super
+ @encodingstyle = nil
+ @elename = XSD::QName::EMPTY
+ @id = nil
+ @precedents = []
+ @root = false
+ @parent = nil
+ @position = nil
+ @definedtype = nil
+ @extraattr = {}
+ end
+
+ def inspect
+ if self.is_a?(XSD::NSDBase)
+ sprintf("#<%s:0x%x %s %s>", self.class.name, __id__, self.elename, self.type)
+ else
+ sprintf("#<%s:0x%x %s>", self.class.name, __id__, self.elename)
+ end
+ end
+
+ def rootnode
+ node = self
+ while node = node.parent
+ break if SOAPEnvelope === node
+ end
+ node
+ end
+end
+
+
+###
+## for SOAP base type
+#
+module SOAPBasetype
+ include SOAPType
+ include SOAP
+
+ def initialize(*arg)
+ super
+ end
+end
+
+
+###
+## for SOAP compound type
+#
+module SOAPCompoundtype
+ include SOAPType
+ include SOAP
+
+ def initialize(*arg)
+ super
+ end
+end
+
+
+###
+## Convenience datatypes.
+#
+class SOAPReference < XSD::NSDBase
+ include SOAPBasetype
+ extend SOAPModuleUtils
+
+public
+
+ attr_accessor :refid
+
+ # Override the definition in SOAPBasetype.
+ def initialize(obj = nil)
+ super()
+ @type = XSD::QName::EMPTY
+ @refid = nil
+ @obj = nil
+ __setobj__(obj) if obj
+ end
+
+ def __getobj__
+ @obj
+ end
+
+ def __setobj__(obj)
+ @obj = obj
+ @refid = @obj.id || SOAPReference.create_refid(@obj)
+ @obj.id = @refid unless @obj.id
+ @obj.precedents << self
+ # Copies NSDBase information
+ @obj.type = @type unless @obj.type
+ end
+
+ # Why don't I use delegate.rb?
+ # -> delegate requires target object type at initialize time.
+ # Why don't I use forwardable.rb?
+ # -> forwardable requires a list of forwarding methods.
+ #
+ # ToDo: Maybe I should use forwardable.rb and give it a methods list like
+ # delegate.rb...
+ #
+ def method_missing(msg_id, *params)
+ if @obj
+ @obj.send(msg_id, *params)
+ else
+ nil
+ end
+ end
+
+ def refidstr
+ '#' + @refid
+ end
+
+ def self.create_refid(obj)
+ 'id' + obj.__id__.to_s
+ end
+
+ def self.decode(elename, refidstr)
+ if /\A#(.*)\z/ =~ refidstr
+ refid = $1
+ elsif /\Acid:(.*)\z/ =~ refidstr
+ refid = $1
+ else
+ raise ArgumentError.new("illegal refid #{refidstr}")
+ end
+ d = super(elename)
+ d.refid = refid
+ d
+ end
+end
+
+
+class SOAPExternalReference < XSD::NSDBase
+ include SOAPBasetype
+ extend SOAPModuleUtils
+
+ def initialize
+ super()
+ @type = XSD::QName::EMPTY
+ end
+
+ def referred
+ rootnode.external_content[external_contentid] = self
+ end
+
+ def refidstr
+ 'cid:' + external_contentid
+ end
+
+private
+
+ def external_contentid
+ raise NotImplementedError.new
+ end
+end
+
+
+class SOAPNil < XSD::XSDNil
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+# SOAPRawString is for sending raw string. In contrast to SOAPString,
+# SOAP4R does not do XML encoding and does not convert its CES. The string it
+# holds is embedded to XML instance directly as a 'xsd:string'.
+class SOAPRawString < XSD::XSDString
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+
+###
+## Basic datatypes.
+#
+class SOAPAnySimpleType < XSD::XSDAnySimpleType
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPString < XSD::XSDString
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPBoolean < XSD::XSDBoolean
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPDecimal < XSD::XSDDecimal
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPFloat < XSD::XSDFloat
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPDouble < XSD::XSDDouble
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPDuration < XSD::XSDDuration
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPDateTime < XSD::XSDDateTime
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPTime < XSD::XSDTime
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPDate < XSD::XSDDate
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPGYearMonth < XSD::XSDGYearMonth
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPGYear < XSD::XSDGYear
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPGMonthDay < XSD::XSDGMonthDay
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPGDay < XSD::XSDGDay
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPGMonth < XSD::XSDGMonth
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPHexBinary < XSD::XSDHexBinary
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPBase64 < XSD::XSDBase64Binary
+ include SOAPBasetype
+ extend SOAPModuleUtils
+ Type = QName.new(EncodingNamespace, Base64Literal)
+
+public
+ # Override the definition in SOAPBasetype.
+ def initialize(value = nil)
+ super(value)
+ @type = Type
+ end
+
+ def as_xsd
+ @type = XSD::XSDBase64Binary::Type
+ end
+end
+
+class SOAPAnyURI < XSD::XSDAnyURI
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPQName < XSD::XSDQName
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+
+class SOAPInteger < XSD::XSDInteger
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPNonPositiveInteger < XSD::XSDNonPositiveInteger
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPNegativeInteger < XSD::XSDNegativeInteger
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPLong < XSD::XSDLong
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPInt < XSD::XSDInt
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPShort < XSD::XSDShort
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPByte < XSD::XSDByte
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPNonNegativeInteger < XSD::XSDNonNegativeInteger
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPUnsignedLong < XSD::XSDUnsignedLong
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPUnsignedInt < XSD::XSDUnsignedInt
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPUnsignedShort < XSD::XSDUnsignedShort
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPUnsignedByte < XSD::XSDUnsignedByte
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPPositiveInteger < XSD::XSDPositiveInteger
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+
+###
+## Compound datatypes.
+#
+class SOAPStruct < XSD::NSDBase
+ include SOAPCompoundtype
+ include Enumerable
+
+public
+
+ def initialize(type = nil)
+ super()
+ @type = type || XSD::QName::EMPTY
+ @array = []
+ @data = []
+ end
+
+ def to_s()
+ str = ''
+ self.each do |key, data|
+ str << "#{key}: #{data}\n"
+ end
+ str
+ end
+
+ def add(name, value)
+ add_member(name, value)
+ end
+
+ def [](idx)
+ if idx.is_a?(Range)
+ @data[idx]
+ elsif idx.is_a?(Integer)
+ if (idx > @array.size)
+ raise ArrayIndexOutOfBoundsError.new('In ' << @type.name)
+ end
+ @data[idx]
+ else
+ if @array.include?(idx)
+ @data[@array.index(idx)]
+ else
+ nil
+ end
+ end
+ end
+
+ def []=(idx, data)
+ if @array.include?(idx)
+ data.parent = self if data.respond_to?(:parent=)
+ @data[@array.index(idx)] = data
+ else
+ add(idx, data)
+ end
+ end
+
+ def key?(name)
+ @array.include?(name)
+ end
+
+ def members
+ @array
+ end
+
+ def to_obj
+ hash = {}
+ proptype = {}
+ each do |k, v|
+ value = v.respond_to?(:to_obj) ? v.to_obj : v.to_s
+ case proptype[k]
+ when :single
+ hash[k] = [hash[k], value]
+ proptype[k] = :multi
+ when :multi
+ hash[k] << value
+ else
+ hash[k] = value
+ proptype[k] = :single
+ end
+ end
+ hash
+ end
+
+ def each
+ idx = 0
+ while idx < @array.length
+ yield(@array[idx], @data[idx])
+ idx += 1
+ end
+ end
+
+ def replace
+ members.each do |member|
+ self[member] = yield(self[member])
+ end
+ end
+
+ def self.decode(elename, type)
+ s = SOAPStruct.new(type)
+ s.elename = elename
+ s
+ end
+
+private
+
+ def add_member(name, value = nil)
+ value = SOAPNil.new() if value.nil?
+ @array.push(name)
+ value.elename = value.elename.dup_name(name)
+ @data.push(value)
+ value.parent = self if value.respond_to?(:parent=)
+ value
+ end
+end
+
+
+# SOAPElement is not typed so it is not derived from NSDBase.
+class SOAPElement
+ include Enumerable
+
+ attr_accessor :encodingstyle
+
+ attr_accessor :elename
+ attr_accessor :id
+ attr_reader :precedents
+ attr_accessor :root
+ attr_accessor :parent
+ attr_accessor :position
+ attr_accessor :extraattr
+
+ attr_accessor :qualified
+
+ def initialize(elename, text = nil)
+ if !elename.is_a?(XSD::QName)
+ elename = XSD::QName.new(nil, elename)
+ end
+ @encodingstyle = LiteralNamespace
+ @elename = elename
+ @id = nil
+ @precedents = []
+ @root = false
+ @parent = nil
+ @position = nil
+ @extraattr = {}
+
+ @qualified = nil
+
+ @array = []
+ @data = []
+ @text = text
+ end
+
+ def inspect
+ sprintf("#<%s:0x%x %s>", self.class.name, __id__, self.elename)
+ end
+
+ # Text interface.
+ attr_accessor :text
+ alias data text
+
+ # Element interfaces.
+ def add(value)
+ add_member(value.elename.name, value)
+ end
+
+ def [](idx)
+ if @array.include?(idx)
+ @data[@array.index(idx)]
+ else
+ nil
+ end
+ end
+
+ def []=(idx, data)
+ if @array.include?(idx)
+ data.parent = self if data.respond_to?(:parent=)
+ @data[@array.index(idx)] = data
+ else
+ add(data)
+ end
+ end
+
+ def key?(name)
+ @array.include?(name)
+ end
+
+ def members
+ @array
+ end
+
+ def to_obj
+ if members.empty?
+ @text
+ else
+ hash = {}
+ proptype = {}
+ each do |k, v|
+ value = v.respond_to?(:to_obj) ? v.to_obj : v.to_s
+ case proptype[k]
+ when :single
+ hash[k] = [hash[k], value]
+ proptype[k] = :multi
+ when :multi
+ hash[k] << value
+ else
+ hash[k] = value
+ proptype[k] = :single
+ end
+ end
+ hash
+ end
+ end
+
+ def each
+ idx = 0
+ while idx < @array.length
+ yield(@array[idx], @data[idx])
+ idx += 1
+ end
+ end
+
+ def self.decode(elename)
+ o = SOAPElement.new(elename)
+ o
+ end
+
+ def self.from_obj(obj, namespace = nil)
+ o = SOAPElement.new(nil)
+ case obj
+ when nil
+ o.text = nil
+ when Hash
+ obj.each do |elename, value|
+ if value.is_a?(Array)
+ value.each do |subvalue|
+ child = from_obj(subvalue, namespace)
+ child.elename = to_elename(elename, namespace)
+ o.add(child)
+ end
+ else
+ child = from_obj(value, namespace)
+ child.elename = to_elename(elename, namespace)
+ o.add(child)
+ end
+ end
+ else
+ o.text = obj.to_s
+ end
+ o
+ end
+
+ def self.to_elename(obj, namespace = nil)
+ if obj.is_a?(XSD::QName)
+ obj
+ elsif /\A(.+):([^:]+)\z/ =~ obj.to_s
+ XSD::QName.new($1, $2)
+ else
+ XSD::QName.new(namespace, obj.to_s)
+ end
+ end
+
+private
+
+ def add_member(name, value)
+ add_accessor(name)
+ @array.push(name)
+ @data.push(value)
+ value.parent = self if value.respond_to?(:parent=)
+ value
+ end
+
+ if RUBY_VERSION > "1.7.0"
+ def add_accessor(name)
+ methodname = name
+ if self.respond_to?(methodname)
+ methodname = safe_accessor_name(methodname)
+ end
+ Mapping.define_singleton_method(self, methodname) do
+ @data[@array.index(name)]
+ end
+ Mapping.define_singleton_method(self, methodname + '=') do |value|
+ @data[@array.index(name)] = value
+ end
+ end
+ else
+ def add_accessor(name)
+ methodname = safe_accessor_name(name)
+ instance_eval <<-EOS
+ def #{methodname}
+ @data[@array.index(#{name.dump})]
+ end
+
+ def #{methodname}=(value)
+ @data[@array.index(#{name.dump})] = value
+ end
+ EOS
+ end
+ end
+
+ def safe_accessor_name(name)
+ "var_" << name.gsub(/[^a-zA-Z0-9_]/, '')
+ end
+end
+
+
+class SOAPArray < XSD::NSDBase
+ include SOAPCompoundtype
+ include Enumerable
+
+public
+
+ attr_accessor :sparse
+
+ attr_reader :offset, :rank
+ attr_accessor :size, :size_fixed
+ attr_reader :arytype
+
+ def initialize(type = nil, rank = 1, arytype = nil)
+ super()
+ @type = type || ValueArrayName
+ @rank = rank
+ @data = Array.new
+ @sparse = false
+ @offset = Array.new(rank, 0)
+ @size = Array.new(rank, 0)
+ @size_fixed = false
+ @position = nil
+ @arytype = arytype
+ end
+
+ def offset=(var)
+ @offset = var
+ @sparse = true
+ end
+
+ def add(value)
+ self[*(@offset)] = value
+ end
+
+ def [](*idxary)
+ if idxary.size != @rank
+ raise ArgumentError.new("given #{idxary.size} params does not match rank: #{@rank}")
+ end
+
+ retrieve(idxary)
+ end
+
+ def []=(*idxary)
+ value = idxary.slice!(-1)
+
+ if idxary.size != @rank
+ raise ArgumentError.new("given #{idxary.size} params(#{idxary})" +
+ " does not match rank: #{@rank}")
+ end
+
+ idx = 0
+ while idx < idxary.size
+ if idxary[idx] + 1 > @size[idx]
+ @size[idx] = idxary[idx] + 1
+ end
+ idx += 1
+ end
+
+ data = retrieve(idxary[0, idxary.size - 1])
+ data[idxary.last] = value
+
+ if value.is_a?(SOAPType)
+ value.elename = ITEM_NAME
+ # Sync type
+ unless @type.name
+ @type = XSD::QName.new(value.type.namespace,
+ SOAPArray.create_arytype(value.type.name, @rank))
+ end
+ value.type ||= @type
+ end
+
+ @offset = idxary
+ value.parent = self if value.respond_to?(:parent=)
+ offsetnext
+ end
+
+ def each
+ @data.each do |data|
+ yield(data)
+ end
+ end
+
+ def to_a
+ @data.dup
+ end
+
+ def replace
+ @data = deep_map(@data) do |ele|
+ yield(ele)
+ end
+ end
+
+ def deep_map(ary, &block)
+ ary.collect do |ele|
+ if ele.is_a?(Array)
+ deep_map(ele, &block)
+ else
+ new_obj = block.call(ele)
+ new_obj.elename = ITEM_NAME
+ new_obj
+ end
+ end
+ end
+
+ def include?(var)
+ traverse_data(@data) do |v, *rank|
+ if v.is_a?(SOAPBasetype) && v.data == var
+ return true
+ end
+ end
+ false
+ end
+
+ def traverse
+ traverse_data(@data) do |v, *rank|
+ unless @sparse
+ yield(v)
+ else
+ yield(v, *rank) if v && !v.is_a?(SOAPNil)
+ end
+ end
+ end
+
+ def soap2array(ary)
+ traverse_data(@data) do |v, *position|
+ iteary = ary
+ rank = 1
+ while rank < position.size
+ idx = position[rank - 1]
+ if iteary[idx].nil?
+ iteary = iteary[idx] = Array.new
+ else
+ iteary = iteary[idx]
+ end
+ rank += 1
+ end
+ if block_given?
+ iteary[position.last] = yield(v)
+ else
+ iteary[position.last] = v
+ end
+ end
+ end
+
+ def position
+ @position
+ end
+
+private
+
+ ITEM_NAME = XSD::QName.new(nil, 'item')
+
+ def retrieve(idxary)
+ data = @data
+ rank = 1
+ while rank <= idxary.size
+ idx = idxary[rank - 1]
+ if data[idx].nil?
+ data = data[idx] = Array.new
+ else
+ data = data[idx]
+ end
+ rank += 1
+ end
+ data
+ end
+
+ def traverse_data(data, rank = 1)
+ idx = 0
+ while idx < ranksize(rank)
+ if rank < @rank
+ traverse_data(data[idx], rank + 1) do |*v|
+ v[1, 0] = idx
+ yield(*v)
+ end
+ else
+ yield(data[idx], idx)
+ end
+ idx += 1
+ end
+ end
+
+ def ranksize(rank)
+ @size[rank - 1]
+ end
+
+ def offsetnext
+ move = false
+ idx = @offset.size - 1
+ while !move && idx >= 0
+ @offset[idx] += 1
+ if @size_fixed
+ if @offset[idx] < @size[idx]
+ move = true
+ else
+ @offset[idx] = 0
+ idx -= 1
+ end
+ else
+ move = true
+ end
+ end
+ end
+
+ # Module function
+
+public
+
+ def self.decode(elename, type, arytype)
+ typestr, nofary = parse_type(arytype.name)
+ rank = nofary.count(',') + 1
+ plain_arytype = XSD::QName.new(arytype.namespace, typestr)
+ o = SOAPArray.new(type, rank, plain_arytype)
+ size = []
+ nofary.split(',').each do |s|
+ if s.empty?
+ size.clear
+ break
+ else
+ size << s.to_i
+ end
+ end
+ unless size.empty?
+ o.size = size
+ o.size_fixed = true
+ end
+ o.elename = elename
+ o
+ end
+
+private
+
+ def self.create_arytype(typename, rank)
+ "#{typename}[" << ',' * (rank - 1) << ']'
+ end
+
+ TypeParseRegexp = Regexp.new('^(.+)\[([\d,]*)\]$')
+
+ def self.parse_type(string)
+ TypeParseRegexp =~ string
+ return $1, $2
+ end
+end
+
+
+require 'soap/mapping/typeMap'
+
+
+end
diff --git a/ruby_1_8_5/lib/soap/element.rb b/ruby_1_8_5/lib/soap/element.rb
new file mode 100644
index 0000000000..cc58b5d341
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/element.rb
@@ -0,0 +1,258 @@
+# SOAP4R - SOAP elements library
+# Copyright (C) 2000, 2001, 2003-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/qname'
+require 'soap/baseData'
+
+
+module SOAP
+
+
+###
+## SOAP elements
+#
+module SOAPEnvelopeElement; end
+
+class SOAPFault < SOAPStruct
+ include SOAPEnvelopeElement
+ include SOAPCompoundtype
+
+public
+
+ def faultcode
+ self['faultcode']
+ end
+
+ def faultstring
+ self['faultstring']
+ end
+
+ def faultactor
+ self['faultactor']
+ end
+
+ def detail
+ self['detail']
+ end
+
+ def faultcode=(rhs)
+ self['faultcode'] = rhs
+ end
+
+ def faultstring=(rhs)
+ self['faultstring'] = rhs
+ end
+
+ def faultactor=(rhs)
+ self['faultactor'] = rhs
+ end
+
+ def detail=(rhs)
+ self['detail'] = rhs
+ end
+
+ def initialize(faultcode = nil, faultstring = nil, faultactor = nil, detail = nil)
+ super(EleFaultName)
+ @elename = EleFaultName
+ @encodingstyle = EncodingNamespace
+
+ if faultcode
+ self.faultcode = faultcode
+ self.faultstring = faultstring
+ self.faultactor = faultactor
+ self.detail = detail
+ self.faultcode.elename = EleFaultCodeName if self.faultcode
+ self.faultstring.elename = EleFaultStringName if self.faultstring
+ self.faultactor.elename = EleFaultActorName if self.faultactor
+ self.detail.elename = EleFaultDetailName if self.detail
+ end
+ faultcode.parent = self if faultcode
+ faultstring.parent = self if faultstring
+ faultactor.parent = self if faultactor
+ detail.parent = self if detail
+ end
+
+ def encode(generator, ns, attrs = {})
+ SOAPGenerator.assign_ns(attrs, ns, EnvelopeNamespace)
+ SOAPGenerator.assign_ns(attrs, ns, EncodingNamespace)
+ attrs[ns.name(AttrEncodingStyleName)] = EncodingNamespace
+ name = ns.name(@elename)
+ generator.encode_tag(name, attrs)
+ yield(self.faultcode)
+ yield(self.faultstring)
+ yield(self.faultactor)
+ yield(self.detail) if self.detail
+ generator.encode_tag_end(name, true)
+ end
+end
+
+
+class SOAPBody < SOAPStruct
+ include SOAPEnvelopeElement
+
+ def initialize(data = nil, is_fault = false)
+ super(nil)
+ @elename = EleBodyName
+ @encodingstyle = nil
+ if data
+ if data.respond_to?(:elename)
+ add(data.elename.name, data)
+ else
+ data.to_a.each do |datum|
+ add(datum.elename.name, datum)
+ end
+ end
+ end
+ @is_fault = is_fault
+ end
+
+ def encode(generator, ns, attrs = {})
+ name = ns.name(@elename)
+ generator.encode_tag(name, attrs)
+ if @is_fault
+ yield(@data)
+ else
+ @data.each do |data|
+ yield(data)
+ end
+ end
+ generator.encode_tag_end(name, true)
+ end
+
+ def root_node
+ @data.each do |node|
+ if node.root == 1
+ return node
+ end
+ end
+ # No specified root...
+ @data.each do |node|
+ if node.root != 0
+ return node
+ end
+ end
+
+ raise Parser::FormatDecodeError.new('no root element')
+ end
+end
+
+
+class SOAPHeaderItem < XSD::NSDBase
+ include SOAPEnvelopeElement
+ include SOAPCompoundtype
+
+public
+
+ attr_accessor :element
+ attr_accessor :mustunderstand
+ attr_accessor :encodingstyle
+
+ def initialize(element, mustunderstand = true, encodingstyle = nil)
+ super()
+ @type = nil
+ @element = element
+ @mustunderstand = mustunderstand
+ @encodingstyle = encodingstyle
+ element.parent = self if element
+ end
+
+ def encode(generator, ns, attrs = {})
+ attrs.each do |key, value|
+ @element.extraattr[key] = value
+ end
+ @element.extraattr[ns.name(AttrMustUnderstandName)] =
+ (@mustunderstand ? '1' : '0')
+ if @encodingstyle
+ @element.extraattr[ns.name(AttrEncodingStyleName)] = @encodingstyle
+ end
+ @element.encodingstyle = @encodingstyle if !@element.encodingstyle
+ yield(@element)
+ end
+end
+
+
+class SOAPHeader < SOAPStruct
+ include SOAPEnvelopeElement
+
+ def initialize
+ super(nil)
+ @elename = EleHeaderName
+ @encodingstyle = nil
+ end
+
+ def encode(generator, ns, attrs = {})
+ name = ns.name(@elename)
+ generator.encode_tag(name, attrs)
+ @data.each do |data|
+ yield(data)
+ end
+ generator.encode_tag_end(name, true)
+ end
+
+ def add(name, value)
+ mu = (value.extraattr[AttrMustUnderstandName] == '1')
+ encstyle = value.extraattr[AttrEncodingStyleName]
+ item = SOAPHeaderItem.new(value, mu, encstyle)
+ super(name, item)
+ end
+
+ def length
+ @data.length
+ end
+ alias size length
+end
+
+
+class SOAPEnvelope < XSD::NSDBase
+ include SOAPEnvelopeElement
+ include SOAPCompoundtype
+
+ attr_reader :header
+ attr_reader :body
+ attr_reader :external_content
+
+ def initialize(header = nil, body = nil)
+ super()
+ @type = nil
+ @elename = EleEnvelopeName
+ @encodingstyle = nil
+ @header = header
+ @body = body
+ @external_content = {}
+ header.parent = self if header
+ body.parent = self if body
+ end
+
+ def header=(header)
+ header.parent = self
+ @header = header
+ end
+
+ def body=(body)
+ body.parent = self
+ @body = body
+ end
+
+ def encode(generator, ns, attrs = {})
+ SOAPGenerator.assign_ns(attrs, ns, elename.namespace, SOAPNamespaceTag)
+ name = ns.name(@elename)
+ generator.encode_tag(name, attrs)
+
+ yield(@header) if @header and @header.length > 0
+ yield(@body)
+
+ generator.encode_tag_end(name, true)
+ end
+
+ def to_ary
+ [header, body]
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/soap/encodingstyle/aspDotNetHandler.rb b/ruby_1_8_5/lib/soap/encodingstyle/aspDotNetHandler.rb
new file mode 100644
index 0000000000..fd7e0fa217
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/encodingstyle/aspDotNetHandler.rb
@@ -0,0 +1,213 @@
+# SOAP4R - ASP.NET EncodingStyle handler library
+# Copyright (C) 2001, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'soap/encodingstyle/handler'
+
+
+module SOAP
+module EncodingStyle
+
+
+class ASPDotNetHandler < Handler
+ Namespace = 'http://tempuri.org/ASP.NET'
+ add_handler
+
+ def initialize(charset = nil)
+ super(charset)
+ @textbuf = ''
+ @decode_typemap = nil
+ end
+
+
+ ###
+ ## encode interface.
+ #
+ def encode_data(generator, ns, data, parent)
+ attrs = {}
+ # ASPDotNetHandler is intended to be used for accessing an ASP.NET doc/lit
+ # service as an rpc/encoded service. in the situation, local elements
+ # should be qualified. propagate parent's namespace to children.
+ if data.elename.namespace.nil?
+ data.elename.namespace = parent.elename.namespace
+ end
+ name = generator.encode_name(ns, data, attrs)
+ case data
+ when SOAPRawString
+ generator.encode_tag(name, attrs)
+ generator.encode_rawstring(data.to_s)
+ when XSD::XSDString
+ generator.encode_tag(name, attrs)
+ generator.encode_string(@charset ?
+ XSD::Charset.encoding_to_xml(data.to_s, @charset) : data.to_s)
+ when XSD::XSDAnySimpleType
+ generator.encode_tag(name, attrs)
+ generator.encode_string(data.to_s)
+ when SOAPStruct
+ generator.encode_tag(name, attrs)
+ data.each do |key, value|
+ generator.encode_child(ns, value, data)
+ end
+ when SOAPArray
+ generator.encode_tag(name, attrs)
+ data.traverse do |child, *rank|
+ data.position = nil
+ generator.encode_child(ns, child, data)
+ end
+ else
+ raise EncodingStyleError.new(
+ "unknown object:#{data} in this encodingStyle")
+ end
+ end
+
+ def encode_data_end(generator, ns, data, parent)
+ name = generator.encode_name_end(ns, data)
+ cr = data.is_a?(SOAPCompoundtype)
+ generator.encode_tag_end(name, cr)
+ end
+
+
+ ###
+ ## decode interface.
+ #
+ class SOAPTemporalObject
+ attr_accessor :parent
+
+ def initialize
+ @parent = nil
+ end
+ end
+
+ class SOAPUnknown < SOAPTemporalObject
+ def initialize(handler, elename)
+ super()
+ @handler = handler
+ @elename = elename
+ end
+
+ def as_struct
+ o = SOAPStruct.decode(@elename, XSD::AnyTypeName)
+ o.parent = @parent
+ o.type.name = @name
+ @handler.decode_parent(@parent, o)
+ o
+ end
+
+ def as_string
+ o = SOAPString.decode(@elename)
+ o.parent = @parent
+ @handler.decode_parent(@parent, o)
+ o
+ end
+
+ def as_nil
+ o = SOAPNil.decode(@elename)
+ o.parent = @parent
+ @handler.decode_parent(@parent, o)
+ o
+ end
+ end
+
+ def decode_tag(ns, elename, attrs, parent)
+ @textbuf = ''
+ o = SOAPUnknown.new(self, elename)
+ o.parent = parent
+ o
+ end
+
+ def decode_tag_end(ns, node)
+ o = node.node
+ if o.is_a?(SOAPUnknown)
+ newnode = o.as_string
+# if /\A\s*\z/ =~ @textbuf
+# o.as_struct
+# else
+# o.as_string
+# end
+ node.replace_node(newnode)
+ o = node.node
+ end
+
+ decode_textbuf(o)
+ @textbuf = ''
+ end
+
+ def decode_text(ns, text)
+ # @textbuf is set at decode_tag_end.
+ @textbuf << text
+ end
+
+ def decode_prologue
+ end
+
+ def decode_epilogue
+ end
+
+ def decode_parent(parent, node)
+ case parent.node
+ when SOAPUnknown
+ newparent = parent.node.as_struct
+ node.parent = newparent
+ parent.replace_node(newparent)
+ decode_parent(parent, node)
+
+ when SOAPStruct
+ data = parent.node[node.elename.name]
+ case data
+ when nil
+ parent.node.add(node.elename.name, node)
+ when SOAPArray
+ name, type_ns = node.elename.name, node.type.namespace
+ data.add(node)
+ node.elename, node.type.namespace = name, type_ns
+ else
+ parent.node[node.elename.name] = SOAPArray.new
+ name, type_ns = data.elename.name, data.type.namespace
+ parent.node[node.elename.name].add(data)
+ data.elename.name, data.type.namespace = name, type_ns
+ name, type_ns = node.elename.name, node.type.namespace
+ parent.node[node.elename.name].add(node)
+ node.elename.name, node.type.namespace = name, type_ns
+ end
+
+ when SOAPArray
+ if node.position
+ parent.node[*(decode_arypos(node.position))] = node
+ parent.node.sparse = true
+ else
+ parent.node.add(node)
+ end
+
+ when SOAPBasetype
+ raise EncodingStyleError.new("SOAP base type must not have a child")
+
+ else
+ # SOAPUnknown does not have parent.
+ # raise EncodingStyleError.new("illegal parent: #{parent}")
+ end
+ end
+
+private
+
+ def decode_textbuf(node)
+ if node.is_a?(XSD::XSDString)
+ if @charset
+ node.set(XSD::Charset.encoding_from_xml(@textbuf, @charset))
+ else
+ node.set(@textbuf)
+ end
+ else
+ # Nothing to do...
+ end
+ end
+end
+
+ASPDotNetHandler.new
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/soap/encodingstyle/handler.rb b/ruby_1_8_5/lib/soap/encodingstyle/handler.rb
new file mode 100644
index 0000000000..c015417435
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/encodingstyle/handler.rb
@@ -0,0 +1,100 @@
+# SOAP4R - EncodingStyle handler library
+# Copyright (C) 2001, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'soap/soap'
+require 'soap/baseData'
+require 'soap/element'
+
+
+module SOAP
+module EncodingStyle
+
+
+class Handler
+ @@handlers = {}
+
+ class EncodingStyleError < Error; end
+
+ class << self
+ def uri
+ self::Namespace
+ end
+
+ def handler(uri)
+ @@handlers[uri]
+ end
+
+ def each
+ @@handlers.each do |key, value|
+ yield(value)
+ end
+ end
+
+ private
+
+ def add_handler
+ @@handlers[self.uri] = self
+ end
+ end
+
+ attr_reader :charset
+ attr_accessor :generate_explicit_type
+ def decode_typemap=(definedtypes)
+ @decode_typemap = definedtypes
+ end
+
+ def initialize(charset)
+ @charset = charset
+ @generate_explicit_type = true
+ @decode_typemap = nil
+ end
+
+ ###
+ ## encode interface.
+ #
+ # Returns a XML instance as a string.
+ def encode_data(generator, ns, data, parent)
+ raise NotImplementError
+ end
+
+ def encode_data_end(generator, ns, data, parent)
+ raise NotImplementError
+ end
+
+ def encode_prologue
+ end
+
+ def encode_epilogue
+ end
+
+ ###
+ ## decode interface.
+ #
+ # Returns SOAP/OM data.
+ def decode_tag(ns, name, attrs, parent)
+ raise NotImplementError.new('Method decode_tag must be defined in derived class.')
+ end
+
+ def decode_tag_end(ns, name)
+ raise NotImplementError.new('Method decode_tag_end must be defined in derived class.')
+ end
+
+ def decode_text(ns, text)
+ raise NotImplementError.new('Method decode_text must be defined in derived class.')
+ end
+
+ def decode_prologue
+ end
+
+ def decode_epilogue
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/soap/encodingstyle/literalHandler.rb b/ruby_1_8_5/lib/soap/encodingstyle/literalHandler.rb
new file mode 100644
index 0000000000..59c7205366
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/encodingstyle/literalHandler.rb
@@ -0,0 +1,226 @@
+# SOAP4R - XML Literal EncodingStyle handler library
+# Copyright (C) 2001, 2003-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'soap/encodingstyle/handler'
+
+
+module SOAP
+module EncodingStyle
+
+
+class LiteralHandler < Handler
+ Namespace = SOAP::LiteralNamespace
+ add_handler
+
+ def initialize(charset = nil)
+ super(charset)
+ @textbuf = ''
+ end
+
+
+ ###
+ ## encode interface.
+ #
+ def encode_data(generator, ns, data, parent)
+ attrs = {}
+ name = generator.encode_name(ns, data, attrs)
+ data.extraattr.each do |k, v|
+ # ToDo: check generator.attributeformdefault here
+ if k.is_a?(XSD::QName)
+ if k.namespace
+ SOAPGenerator.assign_ns(attrs, ns, k.namespace)
+ k = ns.name(k)
+ else
+ k = k.name
+ end
+ end
+ attrs[k] = v
+ end
+ case data
+ when SOAPRawString
+ generator.encode_tag(name, attrs)
+ generator.encode_rawstring(data.to_s)
+ when XSD::XSDString
+ generator.encode_tag(name, attrs)
+ str = data.to_s
+ str = XSD::Charset.encoding_to_xml(str, @charset) if @charset
+ generator.encode_string(str)
+ when XSD::XSDAnySimpleType
+ generator.encode_tag(name, attrs)
+ generator.encode_string(data.to_s)
+ when SOAPStruct
+ generator.encode_tag(name, attrs)
+ data.each do |key, value|
+ generator.encode_child(ns, value, data)
+ end
+ when SOAPArray
+ generator.encode_tag(name, attrs)
+ data.traverse do |child, *rank|
+ data.position = nil
+ generator.encode_child(ns, child, data)
+ end
+ when SOAPElement
+ # passes 2 times for simplifying namespace definition
+ data.each do |key, value|
+ if value.elename.namespace
+ SOAPGenerator.assign_ns(attrs, ns, value.elename.namespace)
+ end
+ end
+ generator.encode_tag(name, attrs)
+ generator.encode_rawstring(data.text) if data.text
+ data.each do |key, value|
+ generator.encode_child(ns, value, data)
+ end
+ else
+ raise EncodingStyleError.new(
+ "unknown object:#{data} in this encodingStyle")
+ end
+ end
+
+ def encode_data_end(generator, ns, data, parent)
+ name = generator.encode_name_end(ns, data)
+ cr = (data.is_a?(SOAPCompoundtype) or
+ (data.is_a?(SOAPElement) and !data.text))
+ generator.encode_tag_end(name, cr)
+ end
+
+
+ ###
+ ## decode interface.
+ #
+ class SOAPTemporalObject
+ attr_accessor :parent
+
+ def initialize
+ @parent = nil
+ end
+ end
+
+ class SOAPUnknown < SOAPTemporalObject
+ def initialize(handler, elename, extraattr)
+ super()
+ @handler = handler
+ @elename = elename
+ @extraattr = extraattr
+ end
+
+ def as_element
+ o = SOAPElement.decode(@elename)
+ o.parent = @parent
+ o.extraattr.update(@extraattr)
+ @handler.decode_parent(@parent, o)
+ o
+ end
+
+ def as_string
+ o = SOAPString.decode(@elename)
+ o.parent = @parent
+ o.extraattr.update(@extraattr)
+ @handler.decode_parent(@parent, o)
+ o
+ end
+
+ def as_nil
+ o = SOAPNil.decode(@elename)
+ o.parent = @parent
+ o.extraattr.update(@extraattr)
+ @handler.decode_parent(@parent, o)
+ o
+ end
+ end
+
+ def decode_tag(ns, elename, attrs, parent)
+ @textbuf = ''
+ o = SOAPUnknown.new(self, elename, decode_attrs(ns, attrs))
+ o.parent = parent
+ o
+ end
+
+ def decode_tag_end(ns, node)
+ o = node.node
+ if o.is_a?(SOAPUnknown)
+ newnode = if /\A\s*\z/ =~ @textbuf
+ o.as_element
+ else
+ o.as_string
+ end
+ node.replace_node(newnode)
+ o = node.node
+ end
+
+ decode_textbuf(o)
+ @textbuf = ''
+ end
+
+ def decode_text(ns, text)
+ # @textbuf is set at decode_tag_end.
+ @textbuf << text
+ end
+
+ def decode_attrs(ns, attrs)
+ extraattr = {}
+ attrs.each do |key, value|
+ qname = ns.parse_local(key)
+ extraattr[qname] = value
+ end
+ extraattr
+ end
+
+ def decode_prologue
+ end
+
+ def decode_epilogue
+ end
+
+ def decode_parent(parent, node)
+ return unless parent.node
+ case parent.node
+ when SOAPUnknown
+ newparent = parent.node.as_element
+ node.parent = newparent
+ parent.replace_node(newparent)
+ decode_parent(parent, node)
+ when SOAPElement
+ parent.node.add(node)
+ node.parent = parent.node
+ when SOAPStruct
+ parent.node.add(node.elename.name, node)
+ node.parent = parent.node
+ when SOAPArray
+ if node.position
+ parent.node[*(decode_arypos(node.position))] = node
+ parent.node.sparse = true
+ else
+ parent.node.add(node)
+ end
+ node.parent = parent.node
+ else
+ raise EncodingStyleError.new("illegal parent: #{parent.node}")
+ end
+ end
+
+private
+
+ def decode_textbuf(node)
+ if node.is_a?(XSD::XSDString)
+ if @charset
+ node.set(XSD::Charset.encoding_from_xml(@textbuf, @charset))
+ else
+ node.set(@textbuf)
+ end
+ else
+ # Nothing to do...
+ end
+ end
+end
+
+LiteralHandler.new
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/soap/encodingstyle/soapHandler.rb b/ruby_1_8_5/lib/soap/encodingstyle/soapHandler.rb
new file mode 100644
index 0000000000..a522392625
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/encodingstyle/soapHandler.rb
@@ -0,0 +1,582 @@
+# SOAP4R - SOAP EncodingStyle handler library
+# Copyright (C) 2001, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'soap/encodingstyle/handler'
+
+
+module SOAP
+module EncodingStyle
+
+
+class SOAPHandler < Handler
+ Namespace = SOAP::EncodingNamespace
+ add_handler
+
+ def initialize(charset = nil)
+ super(charset)
+ @refpool = []
+ @idpool = []
+ @textbuf = ''
+ @is_first_top_ele = true
+ end
+
+
+ ###
+ ## encode interface.
+ #
+ def encode_data(generator, ns, data, parent)
+ attrs = encode_attrs(generator, ns, data, parent)
+ if parent && parent.is_a?(SOAPArray) && parent.position
+ attrs[ns.name(AttrPositionName)] = "[#{parent.position.join(',')}]"
+ end
+ name = generator.encode_name(ns, data, attrs)
+ case data
+ when SOAPReference
+ attrs['href'] = data.refidstr
+ generator.encode_tag(name, attrs)
+ when SOAPExternalReference
+ data.referred
+ attrs['href'] = data.refidstr
+ generator.encode_tag(name, attrs)
+ when SOAPRawString
+ generator.encode_tag(name, attrs)
+ generator.encode_rawstring(data.to_s)
+ when XSD::XSDString
+ generator.encode_tag(name, attrs)
+ generator.encode_string(@charset ?
+ XSD::Charset.encoding_to_xml(data.to_s, @charset) : data.to_s)
+ when XSD::XSDAnySimpleType
+ generator.encode_tag(name, attrs)
+ generator.encode_string(data.to_s)
+ when SOAPStruct
+ generator.encode_tag(name, attrs)
+ data.each do |key, value|
+ generator.encode_child(ns, value, data)
+ end
+ when SOAPArray
+ generator.encode_tag(name, attrs)
+ data.traverse do |child, *rank|
+ data.position = data.sparse ? rank : nil
+ generator.encode_child(ns, child, data)
+ end
+ else
+ raise EncodingStyleError.new(
+ "unknown object:#{data} in this encodingStyle")
+ end
+ end
+
+ def encode_data_end(generator, ns, data, parent)
+ name = generator.encode_name_end(ns, data)
+ cr = data.is_a?(SOAPCompoundtype)
+ generator.encode_tag_end(name, cr)
+ end
+
+
+ ###
+ ## decode interface.
+ #
+ class SOAPTemporalObject
+ attr_accessor :parent
+ attr_accessor :position
+ attr_accessor :id
+ attr_accessor :root
+
+ def initialize
+ @parent = nil
+ @position = nil
+ @id = nil
+ @root = nil
+ end
+ end
+
+ class SOAPUnknown < SOAPTemporalObject
+ attr_reader :type
+ attr_accessor :definedtype
+ attr_reader :extraattr
+
+ def initialize(handler, elename, type, extraattr)
+ super()
+ @handler = handler
+ @elename = elename
+ @type = type
+ @extraattr = extraattr
+ @definedtype = nil
+ end
+
+ def as_struct
+ o = SOAPStruct.decode(@elename, @type)
+ o.id = @id
+ o.root = @root
+ o.parent = @parent
+ o.position = @position
+ o.extraattr.update(@extraattr)
+ @handler.decode_parent(@parent, o)
+ o
+ end
+
+ def as_string
+ o = SOAPString.decode(@elename)
+ o.id = @id
+ o.root = @root
+ o.parent = @parent
+ o.position = @position
+ o.extraattr.update(@extraattr)
+ @handler.decode_parent(@parent, o)
+ o
+ end
+
+ def as_nil
+ o = SOAPNil.decode(@elename)
+ o.id = @id
+ o.root = @root
+ o.parent = @parent
+ o.position = @position
+ o.extraattr.update(@extraattr)
+ @handler.decode_parent(@parent, o)
+ o
+ end
+ end
+
+ def decode_tag(ns, elename, attrs, parent)
+ @textbuf = ''
+ is_nil, type, arytype, root, offset, position, href, id, extraattr =
+ decode_attrs(ns, attrs)
+ o = nil
+ if is_nil
+ o = SOAPNil.decode(elename)
+ elsif href
+ o = SOAPReference.decode(elename, href)
+ @refpool << o
+ elsif @decode_typemap
+ o = decode_tag_by_wsdl(ns, elename, type, parent.node, arytype, extraattr)
+ else
+ o = decode_tag_by_type(ns, elename, type, parent.node, arytype, extraattr)
+ end
+
+ if o.is_a?(SOAPArray)
+ if offset
+ o.offset = decode_arypos(offset)
+ o.sparse = true
+ else
+ o.sparse = false
+ end
+ end
+
+ o.parent = parent
+ o.id = id
+ o.root = root
+ o.position = position
+
+ unless o.is_a?(SOAPTemporalObject)
+ @idpool << o if o.id
+ decode_parent(parent, o)
+ end
+ o
+ end
+
+ def decode_tag_end(ns, node)
+ o = node.node
+ if o.is_a?(SOAPUnknown)
+ newnode = if /\A\s*\z/ =~ @textbuf
+ o.as_struct
+ else
+ o.as_string
+ end
+ if newnode.id
+ @idpool << newnode
+ end
+ node.replace_node(newnode)
+ o = node.node
+ end
+ decode_textbuf(o)
+ # unlink definedtype
+ o.definedtype = nil
+ end
+
+ def decode_text(ns, text)
+ @textbuf << text
+ end
+
+ def decode_prologue
+ @refpool.clear
+ @idpool.clear
+ @is_first_top_ele = true
+ end
+
+ def decode_epilogue
+ decode_resolve_id
+ end
+
+ def decode_parent(parent, node)
+ return unless parent.node
+ case parent.node
+ when SOAPUnknown
+ newparent = parent.node.as_struct
+ node.parent = newparent
+ if newparent.id
+ @idpool << newparent
+ end
+ parent.replace_node(newparent)
+ decode_parent(parent, node)
+ when SOAPStruct
+ parent.node.add(node.elename.name, node)
+ node.parent = parent.node
+ when SOAPArray
+ if node.position
+ parent.node[*(decode_arypos(node.position))] = node
+ parent.node.sparse = true
+ else
+ parent.node.add(node)
+ end
+ node.parent = parent.node
+ else
+ raise EncodingStyleError.new("illegal parent: #{parent.node}")
+ end
+ end
+
+private
+
+ def content_ranksize(typename)
+ typename.scan(/\[[\d,]*\]$/)[0]
+ end
+
+ def content_typename(typename)
+ typename.sub(/\[,*\]$/, '')
+ end
+
+ def create_arytype(ns, data)
+ XSD::QName.new(data.arytype.namespace,
+ content_typename(data.arytype.name) + "[#{data.size.join(',')}]")
+ end
+
+ def encode_attrs(generator, ns, data, parent)
+ attrs = {}
+ return attrs if data.is_a?(SOAPReference)
+
+ if !parent || parent.encodingstyle != EncodingNamespace
+ if @generate_explicit_type
+ SOAPGenerator.assign_ns(attrs, ns, EnvelopeNamespace)
+ attrs[ns.name(AttrEncodingStyleName)] = EncodingNamespace
+ end
+ data.encodingstyle = EncodingNamespace
+ end
+
+ if data.is_a?(SOAPNil)
+ attrs[ns.name(XSD::AttrNilName)] = XSD::NilValue
+ elsif @generate_explicit_type
+ if data.type.namespace
+ SOAPGenerator.assign_ns(attrs, ns, data.type.namespace)
+ end
+ if data.is_a?(SOAPArray)
+ if data.arytype.namespace
+ SOAPGenerator.assign_ns(attrs, ns, data.arytype.namespace)
+ end
+ SOAPGenerator.assign_ns(attrs, ns, EncodingNamespace)
+ attrs[ns.name(AttrArrayTypeName)] = ns.name(create_arytype(ns, data))
+ if data.type.name
+ attrs[ns.name(XSD::AttrTypeName)] = ns.name(data.type)
+ end
+ elsif parent && parent.is_a?(SOAPArray) && (parent.arytype == data.type)
+ # No need to add.
+ elsif !data.type.namespace
+ # No need to add.
+ else
+ attrs[ns.name(XSD::AttrTypeName)] = ns.name(data.type)
+ end
+ end
+
+ data.extraattr.each do |key, value|
+ SOAPGenerator.assign_ns(attrs, ns, key.namespace)
+ attrs[ns.name(key)] = encode_attr_value(generator, ns, key, value)
+ end
+ if data.id
+ attrs['id'] = data.id
+ end
+ attrs
+ end
+
+ def encode_attr_value(generator, ns, qname, value)
+ if value.is_a?(SOAPType)
+ ref = SOAPReference.new(value)
+ generator.add_reftarget(qname.name, value)
+ ref.refidstr
+ else
+ value.to_s
+ end
+ end
+
+ def decode_tag_by_wsdl(ns, elename, typestr, parent, arytypestr, extraattr)
+ o = nil
+ if parent.class == SOAPBody
+ # root element: should branch by root attribute?
+ if @is_first_top_ele
+ # Unqualified name is allowed here.
+ @is_first_top_ele = false
+ type = @decode_typemap[elename] ||
+ @decode_typemap.find_name(elename.name)
+ if type
+ o = SOAPStruct.new(elename)
+ o.definedtype = type
+ return o
+ end
+ end
+ # multi-ref element.
+ if typestr
+ typename = ns.parse(typestr)
+ typedef = @decode_typemap[typename]
+ if typedef
+ return decode_definedtype(elename, typename, typedef, arytypestr)
+ end
+ end
+ return decode_tag_by_type(ns, elename, typestr, parent, arytypestr,
+ extraattr)
+ end
+
+ if parent.type == XSD::AnyTypeName
+ return decode_tag_by_type(ns, elename, typestr, parent, arytypestr,
+ extraattr)
+ end
+
+ # parent.definedtype == nil means the parent is SOAPUnknown. SOAPUnknown
+ # is generated by decode_tag_by_type when its type is anyType.
+ parenttype = parent.definedtype || @decode_typemap[parent.type]
+ unless parenttype
+ return decode_tag_by_type(ns, elename, typestr, parent, arytypestr,
+ extraattr)
+ end
+
+ definedtype_name = parenttype.child_type(elename)
+ if definedtype_name and (klass = TypeMap[definedtype_name])
+ return decode_basetype(klass, elename)
+ elsif definedtype_name == XSD::AnyTypeName
+ return decode_tag_by_type(ns, elename, typestr, parent, arytypestr,
+ extraattr)
+ end
+
+ if definedtype_name
+ typedef = @decode_typemap[definedtype_name]
+ else
+ typedef = parenttype.child_defined_complextype(elename)
+ end
+ decode_definedtype(elename, definedtype_name, typedef, arytypestr)
+ end
+
+ def decode_definedtype(elename, typename, typedef, arytypestr)
+ unless typedef
+ raise EncodingStyleError.new("unknown type '#{typename}'")
+ end
+ if typedef.is_a?(::WSDL::XMLSchema::SimpleType)
+ decode_defined_simpletype(elename, typename, typedef, arytypestr)
+ else
+ decode_defined_complextype(elename, typename, typedef, arytypestr)
+ end
+ end
+
+ def decode_basetype(klass, elename)
+ klass.decode(elename)
+ end
+
+ def decode_defined_simpletype(elename, typename, typedef, arytypestr)
+ o = decode_basetype(TypeMap[typedef.base], elename)
+ o.definedtype = typedef
+ o
+ end
+
+ def decode_defined_complextype(elename, typename, typedef, arytypestr)
+ case typedef.compoundtype
+ when :TYPE_STRUCT, :TYPE_MAP
+ o = SOAPStruct.decode(elename, typename)
+ o.definedtype = typedef
+ return o
+ when :TYPE_ARRAY
+ expected_arytype = typedef.find_arytype
+ if arytypestr
+ actual_arytype = XSD::QName.new(expected_arytype.namespace,
+ content_typename(expected_arytype.name) <<
+ content_ranksize(arytypestr))
+ o = SOAPArray.decode(elename, typename, actual_arytype)
+ else
+ o = SOAPArray.new(typename, 1, expected_arytype)
+ o.elename = elename
+ end
+ o.definedtype = typedef
+ return o
+ when :TYPE_EMPTY
+ o = SOAPNil.decode(elename)
+ o.definedtype = typedef
+ return o
+ else
+ raise RuntimeError.new(
+ "Unknown kind of complexType: #{typedef.compoundtype}")
+ end
+ nil
+ end
+
+ def decode_tag_by_type(ns, elename, typestr, parent, arytypestr, extraattr)
+ if arytypestr
+ type = typestr ? ns.parse(typestr) : ValueArrayName
+ node = SOAPArray.decode(elename, type, ns.parse(arytypestr))
+ node.extraattr.update(extraattr)
+ return node
+ end
+
+ type = nil
+ if typestr
+ type = ns.parse(typestr)
+ elsif parent.is_a?(SOAPArray)
+ type = parent.arytype
+ else
+ # Since it's in dynamic(without any type) encoding process,
+ # assumes entity as its type itself.
+ # <SOAP-ENC:Array ...> => type Array in SOAP-ENC.
+ # <Country xmlns="foo"> => type Country in foo.
+ type = elename
+ end
+
+ if (klass = TypeMap[type])
+ node = decode_basetype(klass, elename)
+ node.extraattr.update(extraattr)
+ return node
+ end
+
+ # Unknown type... Struct or String
+ SOAPUnknown.new(self, elename, type, extraattr)
+ end
+
+ def decode_textbuf(node)
+ case node
+ when XSD::XSDHexBinary, XSD::XSDBase64Binary
+ node.set_encoded(@textbuf)
+ when XSD::XSDString
+ if @charset
+ @textbuf = XSD::Charset.encoding_from_xml(@textbuf, @charset)
+ end
+ if node.definedtype
+ node.definedtype.check_lexical_format(@textbuf)
+ end
+ node.set(@textbuf)
+ when SOAPNil
+ # Nothing to do.
+ when SOAPBasetype
+ node.set(@textbuf)
+ else
+ # Nothing to do...
+ end
+ @textbuf = ''
+ end
+
+ NilLiteralMap = {
+ 'true' => true,
+ '1' => true,
+ 'false' => false,
+ '0' => false
+ }
+ RootLiteralMap = {
+ '1' => 1,
+ '0' => 0
+ }
+ def decode_attrs(ns, attrs)
+ is_nil = false
+ type = nil
+ arytype = nil
+ root = nil
+ offset = nil
+ position = nil
+ href = nil
+ id = nil
+ extraattr = {}
+
+ attrs.each do |key, value|
+ qname = ns.parse(key)
+ case qname.namespace
+ when XSD::InstanceNamespace
+ case qname.name
+ when XSD::NilLiteral
+ is_nil = NilLiteralMap[value] or
+ raise EncodingStyleError.new("cannot accept attribute value: #{value} as the value of xsi:#{XSD::NilLiteral} (expected 'true', 'false', '1', or '0')")
+ next
+ when XSD::AttrType
+ type = value
+ next
+ end
+ when EncodingNamespace
+ case qname.name
+ when AttrArrayType
+ arytype = value
+ next
+ when AttrRoot
+ root = RootLiteralMap[value] or
+ raise EncodingStyleError.new(
+ "illegal root attribute value: #{value}")
+ next
+ when AttrOffset
+ offset = value
+ next
+ when AttrPosition
+ position = value
+ next
+ end
+ end
+ if key == 'href'
+ href = value
+ next
+ elsif key == 'id'
+ id = value
+ next
+ end
+ qname = ns.parse_local(key)
+ extraattr[qname] = decode_attr_value(ns, qname, value)
+ end
+
+ return is_nil, type, arytype, root, offset, position, href, id, extraattr
+ end
+
+ def decode_attr_value(ns, qname, value)
+ if /\A#/ =~ value
+ o = SOAPReference.decode(nil, value)
+ @refpool << o
+ o
+ else
+ value
+ end
+ end
+
+ def decode_arypos(position)
+ /^\[(.+)\]$/ =~ position
+ $1.split(',').collect { |s| s.to_i }
+ end
+
+ def decode_resolve_id
+ count = @refpool.length # To avoid infinite loop
+ while !@refpool.empty? && count > 0
+ @refpool = @refpool.find_all { |ref|
+ o = @idpool.find { |item|
+ item.id == ref.refid
+ }
+ if o.is_a?(SOAPReference)
+ true # link of link.
+ elsif o
+ ref.__setobj__(o)
+ false
+ elsif o = ref.rootnode.external_content[ref.refid]
+ ref.__setobj__(o)
+ false
+ else
+ raise EncodingStyleError.new("unresolved reference: #{ref.refid}")
+ end
+ }
+ count -= 1
+ end
+ end
+end
+
+SOAPHandler.new
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/soap/generator.rb b/ruby_1_8_5/lib/soap/generator.rb
new file mode 100644
index 0000000000..f179555e1d
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/generator.rb
@@ -0,0 +1,268 @@
+# SOAP4R - SOAP XML Instance Generator library.
+# Copyright (C) 2001, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/ns'
+require 'soap/soap'
+require 'soap/baseData'
+require 'soap/encodingstyle/handler'
+
+
+module SOAP
+
+
+###
+## CAUTION: MT-unsafe
+#
+class SOAPGenerator
+ include SOAP
+
+ class FormatEncodeError < Error; end
+
+public
+
+ attr_accessor :charset
+ attr_accessor :default_encodingstyle
+ attr_accessor :generate_explicit_type
+ attr_accessor :use_numeric_character_reference
+
+ def initialize(opt = {})
+ @reftarget = nil
+ @handlers = {}
+ @charset = opt[:charset] || XSD::Charset.xml_encoding_label
+ @default_encodingstyle = opt[:default_encodingstyle] || EncodingNamespace
+ @generate_explicit_type =
+ opt.key?(:generate_explicit_type) ? opt[:generate_explicit_type] : true
+ @elementformdefault = opt[:elementformdefault]
+ @attributeformdefault = opt[:attributeformdefault]
+ @use_numeric_character_reference = opt[:use_numeric_character_reference]
+ @indentstr = opt[:no_indent] ? '' : ' '
+ @buf = @indent = @curr = nil
+ end
+
+ def generate(obj, io = nil)
+ @buf = io || ''
+ @indent = ''
+
+ prologue
+ @handlers.each do |uri, handler|
+ handler.encode_prologue
+ end
+
+ ns = XSD::NS.new
+ @buf << xmldecl
+ encode_data(ns, obj, nil)
+
+ @handlers.each do |uri, handler|
+ handler.encode_epilogue
+ end
+ epilogue
+
+ @buf
+ end
+
+ def encode_data(ns, obj, parent)
+ if obj.is_a?(SOAPEnvelopeElement)
+ encode_element(ns, obj, parent)
+ return
+ end
+ if @reftarget && !obj.precedents.empty?
+ add_reftarget(obj.elename.name, obj)
+ ref = SOAPReference.new(obj)
+ ref.elename = ref.elename.dup_name(obj.elename.name)
+ obj.precedents.clear # Avoid cyclic delay.
+ obj.encodingstyle = parent.encodingstyle
+ # SOAPReference is encoded here.
+ obj = ref
+ end
+ encodingstyle = obj.encodingstyle
+ # Children's encodingstyle is derived from its parent.
+ encodingstyle ||= parent.encodingstyle if parent
+ obj.encodingstyle = encodingstyle
+ handler = find_handler(encodingstyle || @default_encodingstyle)
+ unless handler
+ raise FormatEncodeError.new("Unknown encodingStyle: #{ encodingstyle }.")
+ end
+ if !obj.elename.name
+ raise FormatEncodeError.new("Element name not defined: #{ obj }.")
+ end
+ handler.encode_data(self, ns, obj, parent)
+ handler.encode_data_end(self, ns, obj, parent)
+ end
+
+ def add_reftarget(name, node)
+ unless @reftarget
+ raise FormatEncodeError.new("Reftarget is not defined.")
+ end
+ @reftarget.add(name, node)
+ end
+
+ def encode_child(ns, child, parent)
+ indent_backup, @indent = @indent, @indent + @indentstr
+ encode_data(ns.clone_ns, child, parent)
+ @indent = indent_backup
+ end
+
+ def encode_element(ns, obj, parent)
+ attrs = {}
+ if obj.is_a?(SOAPBody)
+ @reftarget = obj
+ obj.encode(self, ns, attrs) do |child|
+ indent_backup, @indent = @indent, @indent + @indentstr
+ encode_data(ns.clone_ns, child, obj)
+ @indent = indent_backup
+ end
+ @reftarget = nil
+ else
+ if obj.is_a?(SOAPEnvelope)
+ # xsi:nil="true" can appear even if dumping without explicit type.
+ SOAPGenerator.assign_ns(attrs, ns,
+ XSD::InstanceNamespace, XSINamespaceTag)
+ if @generate_explicit_type
+ SOAPGenerator.assign_ns(attrs, ns, XSD::Namespace, XSDNamespaceTag)
+ end
+ end
+ obj.encode(self, ns, attrs) do |child|
+ indent_backup, @indent = @indent, @indent + @indentstr
+ encode_data(ns.clone_ns, child, obj)
+ @indent = indent_backup
+ end
+ end
+ end
+
+ def encode_name(ns, data, attrs)
+ if element_local?(data)
+ data.elename.name
+ else
+ if element_qualified?(data)
+ SOAPGenerator.assign_ns(attrs, ns, data.elename.namespace, '')
+ else
+ SOAPGenerator.assign_ns(attrs, ns, data.elename.namespace)
+ end
+ ns.name(data.elename)
+ end
+ end
+
+ def encode_name_end(ns, data)
+ if element_local?(data)
+ data.elename.name
+ else
+ ns.name(data.elename)
+ end
+ end
+
+ def encode_tag(elename, attrs = nil)
+ if !attrs or attrs.empty?
+ @buf << "\n#{ @indent }<#{ elename }>"
+ elsif attrs.size == 1
+ key, value = attrs.shift
+ @buf << %Q[\n#{ @indent }<#{ elename } #{ key }="#{ value }">]
+ else
+ @buf << "\n#{ @indent }<#{ elename } " <<
+ attrs.collect { |key, value|
+ %Q[#{ key }="#{ value }"]
+ }.join("\n#{ @indent }#{ @indentstr * 2 }") <<
+ '>'
+ end
+ end
+
+ def encode_tag_end(elename, cr = nil)
+ if cr
+ @buf << "\n#{ @indent }</#{ elename }>"
+ else
+ @buf << "</#{ elename }>"
+ end
+ end
+
+ def encode_rawstring(str)
+ @buf << str
+ end
+
+ EncodeMap = {
+ '&' => '&amp;',
+ '<' => '&lt;',
+ '>' => '&gt;',
+ '"' => '&quot;',
+ '\'' => '&apos;',
+ "\r" => '&#xd;'
+ }
+ EncodeCharRegexp = Regexp.new("[#{EncodeMap.keys.join}]")
+ def encode_string(str)
+ if @use_numeric_character_reference and !XSD::Charset.is_us_ascii(str)
+ str.gsub!(EncodeCharRegexp) { |c| EncodeMap[c] }
+ @buf << str.unpack("U*").collect { |c|
+ if c == 0x9 or c == 0xa or c == 0xd or (c >= 0x20 and c <= 0x7f)
+ c.chr
+ else
+ sprintf("&#x%x;", c)
+ end
+ }.join
+ else
+ @buf << str.gsub(EncodeCharRegexp) { |c| EncodeMap[c] }
+ end
+ end
+
+ def element_local?(element)
+ element.elename.namespace.nil?
+ end
+
+ def element_qualified?(element)
+ if element.respond_to?(:qualified)
+ if element.qualified.nil?
+ @elementformdefault
+ else
+ element.qualified
+ end
+ else
+ @elementformdefault
+ end
+ end
+
+ def self.assign_ns(attrs, ns, namespace, tag = nil)
+ if namespace.nil?
+ raise FormatEncodeError.new("empty namespace")
+ end
+ unless ns.assigned?(namespace)
+ tag = ns.assign(namespace, tag)
+ if tag == ''
+ attr = 'xmlns'
+ else
+ attr = "xmlns:#{tag}"
+ end
+ attrs[attr] = namespace
+ end
+ end
+
+private
+
+ def prologue
+ end
+
+ def epilogue
+ end
+
+ def find_handler(encodingstyle)
+ unless @handlers.key?(encodingstyle)
+ handler = SOAP::EncodingStyle::Handler.handler(encodingstyle).new(@charset)
+ handler.generate_explicit_type = @generate_explicit_type
+ handler.encode_prologue
+ @handlers[encodingstyle] = handler
+ end
+ @handlers[encodingstyle]
+ end
+
+ def xmldecl
+ if @charset
+ %Q[<?xml version="1.0" encoding="#{ @charset }" ?>]
+ else
+ %Q[<?xml version="1.0" ?>]
+ end
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/soap/header/handler.rb b/ruby_1_8_5/lib/soap/header/handler.rb
new file mode 100644
index 0000000000..7da2836e24
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/header/handler.rb
@@ -0,0 +1,57 @@
+# SOAP4R - SOAP Header handler item
+# Copyright (C) 2003, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'soap/element'
+
+
+module SOAP
+module Header
+
+
+class Handler
+ attr_reader :elename
+ attr_reader :mustunderstand
+ attr_reader :encodingstyle
+
+ def initialize(elename)
+ @elename = elename
+ @mustunderstand = false
+ @encodingstyle = nil
+ end
+
+ # Should return a SOAP/OM, a SOAPHeaderItem or nil.
+ def on_outbound
+ nil
+ end
+
+ # Given header is a SOAPHeaderItem or nil.
+ def on_inbound(header, mustunderstand = false)
+ # do something.
+ end
+
+ def on_outbound_headeritem
+ item = on_outbound
+ if item.nil?
+ nil
+ elsif item.is_a?(::SOAP::SOAPHeaderItem)
+ item.elename = @elename
+ item
+ else
+ item.elename = @elename
+ ::SOAP::SOAPHeaderItem.new(item, @mustunderstand, @encodingstyle)
+ end
+ end
+
+ def on_inbound_headeritem(header)
+ on_inbound(header.element, header.mustunderstand)
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/soap/header/handlerset.rb b/ruby_1_8_5/lib/soap/header/handlerset.rb
new file mode 100644
index 0000000000..a8eee03023
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/header/handlerset.rb
@@ -0,0 +1,70 @@
+# SOAP4R - SOAP Header handler set
+# Copyright (C) 2003, 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/namedelements'
+
+
+module SOAP
+module Header
+
+
+class HandlerSet
+ def initialize
+ @store = XSD::NamedElements.new
+ end
+
+ def dup
+ obj = HandlerSet.new
+ obj.store = @store.dup
+ obj
+ end
+
+ def add(handler)
+ @store << handler
+ end
+ alias << add
+
+ def delete(handler)
+ @store.delete(handler)
+ end
+
+ def include?(handler)
+ @store.include?(handler)
+ end
+
+ # returns: Array of SOAPHeaderItem
+ def on_outbound
+ @store.collect { |handler|
+ handler.on_outbound_headeritem
+ }.compact
+ end
+
+ # headers: SOAPHeaderItem enumerable object
+ def on_inbound(headers)
+ headers.each do |name, item|
+ handler = @store.find { |handler|
+ handler.elename == item.element.elename
+ }
+ if handler
+ handler.on_inbound_headeritem(item)
+ elsif item.mustunderstand
+ raise UnhandledMustUnderstandHeaderError.new(item.element.elename.to_s)
+ end
+ end
+ end
+
+protected
+
+ def store=(store)
+ @store = store
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/soap/header/simplehandler.rb b/ruby_1_8_5/lib/soap/header/simplehandler.rb
new file mode 100644
index 0000000000..7b206f77db
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/header/simplehandler.rb
@@ -0,0 +1,44 @@
+# SOAP4R - SOAP Simple header item handler
+# Copyright (C) 2003-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'soap/header/handler'
+require 'soap/baseData'
+
+
+module SOAP
+module Header
+
+
+class SimpleHandler < SOAP::Header::Handler
+ def initialize(elename)
+ super(elename)
+ end
+
+ # Should return a Hash, String or nil.
+ def on_simple_outbound
+ nil
+ end
+
+ # Given header is a Hash, String or nil.
+ def on_simple_inbound(header, mustunderstand)
+ end
+
+ def on_outbound
+ h = on_simple_outbound
+ h ? SOAPElement.from_obj(h, elename.namespace) : nil
+ end
+
+ def on_inbound(header, mustunderstand)
+ h = header.respond_to?(:to_obj) ? header.to_obj : header.data
+ on_simple_inbound(h, mustunderstand)
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/soap/httpconfigloader.rb b/ruby_1_8_5/lib/soap/httpconfigloader.rb
new file mode 100644
index 0000000000..cd7bca8a65
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/httpconfigloader.rb
@@ -0,0 +1,119 @@
+# SOAP4R - HTTP config loader.
+# Copyright (C) 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'soap/property'
+
+
+module SOAP
+
+
+module HTTPConfigLoader
+module_function
+
+ def set_options(client, options)
+ client.proxy = options["proxy"]
+ options.add_hook("proxy") do |key, value|
+ client.proxy = value
+ end
+ client.no_proxy = options["no_proxy"]
+ options.add_hook("no_proxy") do |key, value|
+ client.no_proxy = value
+ end
+ if client.respond_to?(:protocol_version=)
+ client.protocol_version = options["protocol_version"]
+ options.add_hook("protocol_version") do |key, value|
+ client.protocol_version = value
+ end
+ end
+ ssl_config = options["ssl_config"] ||= ::SOAP::Property.new
+ set_ssl_config(client, ssl_config)
+ ssl_config.add_hook(true) do |key, value|
+ set_ssl_config(client, ssl_config)
+ end
+ basic_auth = options["basic_auth"] ||= ::SOAP::Property.new
+ set_basic_auth(client, basic_auth)
+ basic_auth.add_hook do |key, value|
+ set_basic_auth(client, basic_auth)
+ end
+ options.add_hook("connect_timeout") do |key, value|
+ client.connect_timeout = value
+ end
+ options.add_hook("send_timeout") do |key, value|
+ client.send_timeout = value
+ end
+ options.add_hook("receive_timeout") do |key, value|
+ client.receive_timeout = value
+ end
+ end
+
+ def set_basic_auth(client, basic_auth)
+ basic_auth.values.each do |url, userid, passwd|
+ client.set_basic_auth(url, userid, passwd)
+ end
+ end
+
+ def set_ssl_config(client, ssl_config)
+ ssl_config.each do |key, value|
+ cfg = client.ssl_config
+ if cfg.nil?
+ raise NotImplementedError.new("SSL not supported")
+ end
+ case key
+ when 'client_cert'
+ cfg.client_cert = cert_from_file(value)
+ when 'client_key'
+ cfg.client_key = key_from_file(value)
+ when 'client_ca'
+ cfg.client_ca = value
+ when 'ca_path'
+ cfg.set_trust_ca(value)
+ when 'ca_file'
+ cfg.set_trust_ca(value)
+ when 'crl'
+ cfg.set_crl(value)
+ when 'verify_mode'
+ cfg.verify_mode = ssl_config_int(value)
+ when 'verify_depth'
+ cfg.verify_depth = ssl_config_int(value)
+ when 'options'
+ cfg.options = value
+ when 'ciphers'
+ cfg.ciphers = value
+ when 'verify_callback'
+ cfg.verify_callback = value
+ when 'cert_store'
+ cfg.cert_store = value
+ else
+ raise ArgumentError.new("unknown ssl_config property #{key}")
+ end
+ end
+ end
+
+ def ssl_config_int(value)
+ if value.nil? or value.to_s.empty?
+ nil
+ else
+ begin
+ Integer(value)
+ rescue ArgumentError
+ ::SOAP::Property::Util.const_from_name(value.to_s)
+ end
+ end
+ end
+
+ def cert_from_file(filename)
+ OpenSSL::X509::Certificate.new(File.open(filename) { |f| f.read })
+ end
+
+ def key_from_file(filename)
+ OpenSSL::PKey::RSA.new(File.open(filename) { |f| f.read })
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/soap/mapping.rb b/ruby_1_8_5/lib/soap/mapping.rb
new file mode 100644
index 0000000000..b83f8b484a
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/mapping.rb
@@ -0,0 +1,10 @@
+# SOAP4R - Ruby type mapping utility.
+# Copyright (C) 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'soap/mapping/mapping'
+require 'soap/mapping/registry'
diff --git a/ruby_1_8_5/lib/soap/mapping/factory.rb b/ruby_1_8_5/lib/soap/mapping/factory.rb
new file mode 100644
index 0000000000..978b303b3d
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/mapping/factory.rb
@@ -0,0 +1,355 @@
+# SOAP4R - Mapping factory.
+# Copyright (C) 2000, 2001, 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+module SOAP
+module Mapping
+
+
+class Factory
+ include TraverseSupport
+
+ def initialize
+ # nothing to do
+ end
+
+ def obj2soap(soap_class, obj, info, map)
+ raise NotImplementError.new
+ # return soap_obj
+ end
+
+ def soap2obj(obj_class, node, info, map)
+ raise NotImplementError.new
+ # return convert_succeeded_or_not, obj
+ end
+
+ def setiv2obj(obj, node, map)
+ return if node.nil?
+ if obj.is_a?(Array)
+ setiv2ary(obj, node, map)
+ else
+ setiv2struct(obj, node, map)
+ end
+ end
+
+ def setiv2soap(node, obj, map)
+ if obj.class.class_variables.include?('@@schema_element')
+ obj.class.class_eval('@@schema_element').each do |name, info|
+ type, qname = info
+ if qname
+ elename = qname.name
+ else
+ elename = Mapping.name2elename(name)
+ end
+ node.add(elename,
+ Mapping._obj2soap(obj.instance_variable_get('@' + name), map))
+ end
+ else
+ # should we sort instance_variables?
+ obj.instance_variables.each do |var|
+ name = var.sub(/^@/, '')
+ elename = Mapping.name2elename(name)
+ node.add(elename,
+ Mapping._obj2soap(obj.instance_variable_get(var), map))
+ end
+ end
+ end
+
+private
+
+ def setiv2ary(obj, node, map)
+ node.each do |name, value|
+ Array.instance_method(:<<).bind(obj).call(Mapping._soap2obj(value, map))
+ end
+ end
+
+ def setiv2struct(obj, node, map)
+ vars = {}
+ node.each do |name, value|
+ vars[Mapping.elename2name(name)] = Mapping._soap2obj(value, map)
+ end
+ Mapping.set_attributes(obj, vars)
+ end
+end
+
+class StringFactory_ < Factory
+ def initialize(allow_original_mapping = false)
+ super()
+ @allow_original_mapping = allow_original_mapping
+ end
+
+ def obj2soap(soap_class, obj, info, map)
+ if !@allow_original_mapping and !obj.instance_variables.empty?
+ return nil
+ end
+ begin
+ unless XSD::Charset.is_ces(obj, Thread.current[:SOAPExternalCES])
+ return nil
+ end
+ encoded = XSD::Charset.encoding_conv(obj,
+ Thread.current[:SOAPExternalCES], XSD::Charset.encoding)
+ soap_obj = soap_class.new(encoded)
+ rescue XSD::ValueSpaceError
+ return nil
+ end
+ mark_marshalled_obj(obj, soap_obj)
+ soap_obj
+ end
+
+ def soap2obj(obj_class, node, info, map)
+ obj = Mapping.create_empty_object(obj_class)
+ decoded = XSD::Charset.encoding_conv(node.data, XSD::Charset.encoding,
+ Thread.current[:SOAPExternalCES])
+ obj.replace(decoded)
+ mark_unmarshalled_obj(node, obj)
+ return true, obj
+ end
+end
+
+class BasetypeFactory_ < Factory
+ def initialize(allow_original_mapping = false)
+ super()
+ @allow_original_mapping = allow_original_mapping
+ end
+
+ def obj2soap(soap_class, obj, info, map)
+ if !@allow_original_mapping and !obj.instance_variables.empty?
+ return nil
+ end
+ soap_obj = nil
+ begin
+ soap_obj = soap_class.new(obj)
+ rescue XSD::ValueSpaceError
+ return nil
+ end
+ if @allow_original_mapping
+ # Basetype except String should not be multiref-ed in SOAP/1.1.
+ mark_marshalled_obj(obj, soap_obj)
+ end
+ soap_obj
+ end
+
+ def soap2obj(obj_class, node, info, map)
+ obj = node.data
+ mark_unmarshalled_obj(node, obj)
+ return true, obj
+ end
+end
+
+class DateTimeFactory_ < Factory
+ def initialize(allow_original_mapping = false)
+ super()
+ @allow_original_mapping = allow_original_mapping
+ end
+
+ def obj2soap(soap_class, obj, info, map)
+ if !@allow_original_mapping and
+ Time === obj and !obj.instance_variables.empty?
+ return nil
+ end
+ soap_obj = nil
+ begin
+ soap_obj = soap_class.new(obj)
+ rescue XSD::ValueSpaceError
+ return nil
+ end
+ mark_marshalled_obj(obj, soap_obj)
+ soap_obj
+ end
+
+ def soap2obj(obj_class, node, info, map)
+ if node.respond_to?(:to_obj)
+ obj = node.to_obj(obj_class)
+ return false if obj.nil?
+ mark_unmarshalled_obj(node, obj)
+ return true, obj
+ else
+ return false
+ end
+ end
+end
+
+class Base64Factory_ < Factory
+ def obj2soap(soap_class, obj, info, map)
+ return nil unless obj.instance_variables.empty?
+ soap_obj = soap_class.new(obj)
+ mark_marshalled_obj(obj, soap_obj) if soap_obj
+ soap_obj
+ end
+
+ def soap2obj(obj_class, node, info, map)
+ obj = node.string
+ mark_unmarshalled_obj(node, obj)
+ return true, obj
+ end
+end
+
+class URIFactory_ < Factory
+ def obj2soap(soap_class, obj, info, map)
+ soap_obj = soap_class.new(obj)
+ mark_marshalled_obj(obj, soap_obj) if soap_obj
+ soap_obj
+ end
+
+ def soap2obj(obj_class, node, info, map)
+ obj = node.data
+ mark_unmarshalled_obj(node, obj)
+ return true, obj
+ end
+end
+
+class ArrayFactory_ < Factory
+ def initialize(allow_original_mapping = false)
+ super()
+ @allow_original_mapping = allow_original_mapping
+ end
+
+ # [[1], [2]] is converted to Array of Array, not 2-D Array.
+ # To create M-D Array, you must call Mapping.ary2md.
+ def obj2soap(soap_class, obj, info, map)
+ if !@allow_original_mapping and !obj.instance_variables.empty?
+ return nil
+ end
+ arytype = Mapping.obj2element(obj)
+ if arytype.name
+ arytype.namespace ||= RubyTypeNamespace
+ else
+ arytype = XSD::AnyTypeName
+ end
+ soap_obj = SOAPArray.new(ValueArrayName, 1, arytype)
+ mark_marshalled_obj(obj, soap_obj)
+ obj.each do |item|
+ soap_obj.add(Mapping._obj2soap(item, map))
+ end
+ soap_obj
+ end
+
+ def soap2obj(obj_class, node, info, map)
+ obj = Mapping.create_empty_object(obj_class)
+ mark_unmarshalled_obj(node, obj)
+ node.soap2array(obj) do |elem|
+ elem ? Mapping._soap2obj(elem, map) : nil
+ end
+ return true, obj
+ end
+end
+
+class TypedArrayFactory_ < Factory
+ def initialize(allow_original_mapping = false)
+ super()
+ @allow_original_mapping = allow_original_mapping
+ end
+
+ def obj2soap(soap_class, obj, info, map)
+ if !@allow_original_mapping and !obj.instance_variables.empty?
+ return nil
+ end
+ arytype = info[:type] || info[0]
+ soap_obj = SOAPArray.new(ValueArrayName, 1, arytype)
+ mark_marshalled_obj(obj, soap_obj)
+ obj.each do |var|
+ soap_obj.add(Mapping._obj2soap(var, map))
+ end
+ soap_obj
+ end
+
+ def soap2obj(obj_class, node, info, map)
+ if node.rank > 1
+ return false
+ end
+ arytype = info[:type] || info[0]
+ unless node.arytype == arytype
+ return false
+ end
+ obj = Mapping.create_empty_object(obj_class)
+ mark_unmarshalled_obj(node, obj)
+ node.soap2array(obj) do |elem|
+ elem ? Mapping._soap2obj(elem, map) : nil
+ end
+ return true, obj
+ end
+end
+
+class TypedStructFactory_ < Factory
+ def obj2soap(soap_class, obj, info, map)
+ type = info[:type] || info[0]
+ soap_obj = soap_class.new(type)
+ mark_marshalled_obj(obj, soap_obj)
+ if obj.class <= SOAP::Marshallable
+ setiv2soap(soap_obj, obj, map)
+ else
+ setiv2soap(soap_obj, obj, map)
+ end
+ soap_obj
+ end
+
+ def soap2obj(obj_class, node, info, map)
+ type = info[:type] || info[0]
+ unless node.type == type
+ return false
+ end
+ obj = Mapping.create_empty_object(obj_class)
+ mark_unmarshalled_obj(node, obj)
+ setiv2obj(obj, node, map)
+ return true, obj
+ end
+end
+
+MapQName = XSD::QName.new(ApacheSOAPTypeNamespace, 'Map')
+class HashFactory_ < Factory
+ def initialize(allow_original_mapping = false)
+ super()
+ @allow_original_mapping = allow_original_mapping
+ end
+
+ def obj2soap(soap_class, obj, info, map)
+ if !@allow_original_mapping and !obj.instance_variables.empty?
+ return nil
+ end
+ if !obj.default.nil? or
+ (obj.respond_to?(:default_proc) and obj.default_proc)
+ return nil
+ end
+ soap_obj = SOAPStruct.new(MapQName)
+ mark_marshalled_obj(obj, soap_obj)
+ obj.each do |key, value|
+ elem = SOAPStruct.new
+ elem.add("key", Mapping._obj2soap(key, map))
+ elem.add("value", Mapping._obj2soap(value, map))
+ # ApacheAxis allows only 'item' here.
+ soap_obj.add("item", elem)
+ end
+ soap_obj
+ end
+
+ def soap2obj(obj_class, node, info, map)
+ unless node.type == MapQName
+ return false
+ end
+ if node.class == SOAPStruct and node.key?('default')
+ return false
+ end
+ obj = Mapping.create_empty_object(obj_class)
+ mark_unmarshalled_obj(node, obj)
+ if node.class == SOAPStruct
+ node.each do |key, value|
+ obj[Mapping._soap2obj(value['key'], map)] =
+ Mapping._soap2obj(value['value'], map)
+ end
+ else
+ node.each do |value|
+ obj[Mapping._soap2obj(value['key'], map)] =
+ Mapping._soap2obj(value['value'], map)
+ end
+ end
+ return true, obj
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/soap/mapping/mapping.rb b/ruby_1_8_5/lib/soap/mapping/mapping.rb
new file mode 100644
index 0000000000..65d6bb4d5b
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/mapping/mapping.rb
@@ -0,0 +1,381 @@
+# SOAP4R - Ruby type mapping utility.
+# Copyright (C) 2000, 2001, 2003-2005 NAKAMURA Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/codegen/gensupport'
+
+
+module SOAP
+
+
+module Mapping
+ RubyTypeNamespace = 'http://www.ruby-lang.org/xmlns/ruby/type/1.6'
+ RubyTypeInstanceNamespace =
+ 'http://www.ruby-lang.org/xmlns/ruby/type-instance'
+ RubyCustomTypeNamespace = 'http://www.ruby-lang.org/xmlns/ruby/type/custom'
+ ApacheSOAPTypeNamespace = 'http://xml.apache.org/xml-soap'
+
+
+ # TraverseSupport breaks following thread variables.
+ # Thread.current[:SOAPMarshalDataKey]
+ module TraverseSupport
+ def mark_marshalled_obj(obj, soap_obj)
+ raise if obj.nil?
+ Thread.current[:SOAPMarshalDataKey][obj.__id__] = soap_obj
+ end
+
+ def mark_unmarshalled_obj(node, obj)
+ return if obj.nil?
+ # node.id is not Object#id but SOAPReference#id
+ Thread.current[:SOAPMarshalDataKey][node.id] = obj
+ end
+ end
+
+
+ EMPTY_OPT = {}
+ def self.obj2soap(obj, registry = nil, type = nil, opt = EMPTY_OPT)
+ registry ||= Mapping::DefaultRegistry
+ soap_obj = nil
+ protect_threadvars(:SOAPMarshalDataKey, :SOAPExternalCES, :SOAPMarshalNoReference) do
+ Thread.current[:SOAPMarshalDataKey] = {}
+ Thread.current[:SOAPExternalCES] = opt[:external_ces] || $KCODE
+ Thread.current[:SOAPMarshalNoReference] = opt[:no_reference]
+ soap_obj = _obj2soap(obj, registry, type)
+ end
+ soap_obj
+ end
+
+ def self.soap2obj(node, registry = nil, klass = nil, opt = EMPTY_OPT)
+ registry ||= Mapping::DefaultRegistry
+ obj = nil
+ protect_threadvars(:SOAPMarshalDataKey, :SOAPExternalCES, :SOAPMarshalNoReference) do
+ Thread.current[:SOAPMarshalDataKey] = {}
+ Thread.current[:SOAPExternalCES] = opt[:external_ces] || $KCODE
+ Thread.current[:SOAPMarshalNoReference] = opt[:no_reference]
+ obj = _soap2obj(node, registry, klass)
+ end
+ obj
+ end
+
+ def self.ary2soap(ary, type_ns = XSD::Namespace, typename = XSD::AnyTypeLiteral, registry = nil, opt = EMPTY_OPT)
+ registry ||= Mapping::DefaultRegistry
+ type = XSD::QName.new(type_ns, typename)
+ soap_ary = SOAPArray.new(ValueArrayName, 1, type)
+ protect_threadvars(:SOAPMarshalDataKey, :SOAPExternalCES, :SOAPMarshalNoReference) do
+ Thread.current[:SOAPMarshalDataKey] = {}
+ Thread.current[:SOAPExternalCES] = opt[:external_ces] || $KCODE
+ Thread.current[:SOAPMarshalNoReference] = opt[:no_reference]
+ ary.each do |ele|
+ soap_ary.add(_obj2soap(ele, registry, type))
+ end
+ end
+ soap_ary
+ end
+
+ def self.ary2md(ary, rank, type_ns = XSD::Namespace, typename = XSD::AnyTypeLiteral, registry = nil, opt = EMPTY_OPT)
+ registry ||= Mapping::DefaultRegistry
+ type = XSD::QName.new(type_ns, typename)
+ md_ary = SOAPArray.new(ValueArrayName, rank, type)
+ protect_threadvars(:SOAPMarshalDataKey, :SOAPExternalCES, :SOAPMarshalNoReference) do
+ Thread.current[:SOAPMarshalDataKey] = {}
+ Thread.current[:SOAPExternalCES] = opt[:external_ces] || $KCODE
+ Thread.current[:SOAPMarshalNoReference] = opt[:no_reference]
+ add_md_ary(md_ary, ary, [], registry)
+ end
+ md_ary
+ end
+
+ def self.fault2exception(fault, registry = nil)
+ registry ||= Mapping::DefaultRegistry
+ detail = if fault.detail
+ soap2obj(fault.detail, registry) || ""
+ else
+ ""
+ end
+ if detail.is_a?(Mapping::SOAPException)
+ begin
+ e = detail.to_e
+ remote_backtrace = e.backtrace
+ e.set_backtrace(nil)
+ raise e # ruby sets current caller as local backtrace of e => e2.
+ rescue Exception => e
+ e.set_backtrace(remote_backtrace + e.backtrace[1..-1])
+ raise
+ end
+ else
+ fault.detail = detail
+ fault.set_backtrace(
+ if detail.is_a?(Array)
+ detail
+ else
+ [detail.to_s]
+ end
+ )
+ raise
+ end
+ end
+
+ def self._obj2soap(obj, registry, type = nil)
+ if referent = Thread.current[:SOAPMarshalDataKey][obj.__id__] and
+ !Thread.current[:SOAPMarshalNoReference]
+ SOAPReference.new(referent)
+ elsif registry
+ registry.obj2soap(obj, type)
+ else
+ raise MappingError.new("no mapping registry given")
+ end
+ end
+
+ def self._soap2obj(node, registry, klass = nil)
+ if node.nil?
+ return nil
+ elsif node.is_a?(SOAPReference)
+ target = node.__getobj__
+ # target.id is not Object#id but SOAPReference#id
+ if referent = Thread.current[:SOAPMarshalDataKey][target.id] and
+ !Thread.current[:SOAPMarshalNoReference]
+ return referent
+ else
+ return _soap2obj(target, registry, klass)
+ end
+ end
+ return registry.soap2obj(node, klass)
+ end
+
+ if Object.respond_to?(:allocate)
+ # ruby/1.7 or later.
+ def self.create_empty_object(klass)
+ klass.allocate
+ end
+ else
+ MARSHAL_TAG = {
+ String => ['"', 1],
+ Regexp => ['/', 2],
+ Array => ['[', 1],
+ Hash => ['{', 1]
+ }
+ def self.create_empty_object(klass)
+ if klass <= Struct
+ name = klass.name
+ return ::Marshal.load(sprintf("\004\006S:%c%s\000", name.length + 5, name))
+ end
+ if MARSHAL_TAG.has_key?(klass)
+ tag, terminate = MARSHAL_TAG[klass]
+ return ::Marshal.load(sprintf("\004\006%s%s", tag, "\000" * terminate))
+ end
+ MARSHAL_TAG.each do |k, v|
+ if klass < k
+ name = klass.name
+ tag, terminate = v
+ return ::Marshal.load(sprintf("\004\006C:%c%s%s%s", name.length + 5, name, tag, "\000" * terminate))
+ end
+ end
+ name = klass.name
+ ::Marshal.load(sprintf("\004\006o:%c%s\000", name.length + 5, name))
+ end
+ end
+
+ # Allow only (Letter | '_') (Letter | Digit | '-' | '_')* here.
+ # Caution: '.' is not allowed here.
+ # To follow XML spec., it should be NCName.
+ # (denied chars) => .[0-F][0-F]
+ # ex. a.b => a.2eb
+ #
+ def self.name2elename(name)
+ name.gsub(/([^a-zA-Z0-9:_\-]+)/n) {
+ '.' << $1.unpack('H2' * $1.size).join('.')
+ }.gsub(/::/n, '..')
+ end
+
+ def self.elename2name(name)
+ name.gsub(/\.\./n, '::').gsub(/((?:\.[0-9a-fA-F]{2})+)/n) {
+ [$1.delete('.')].pack('H*')
+ }
+ end
+
+ def self.const_from_name(name, lenient = false)
+ const = ::Object
+ name.sub(/\A::/, '').split('::').each do |const_str|
+ if XSD::CodeGen::GenSupport.safeconstname?(const_str)
+ if const.const_defined?(const_str)
+ const = const.const_get(const_str)
+ next
+ end
+ elsif lenient
+ const_str = XSD::CodeGen::GenSupport.safeconstname(const_str)
+ if const.const_defined?(const_str)
+ const = const.const_get(const_str)
+ next
+ end
+ end
+ return nil
+ end
+ const
+ end
+
+ def self.class_from_name(name, lenient = false)
+ const = const_from_name(name, lenient)
+ if const.is_a?(::Class)
+ const
+ else
+ nil
+ end
+ end
+
+ def self.module_from_name(name, lenient = false)
+ const = const_from_name(name, lenient)
+ if const.is_a?(::Module)
+ const
+ else
+ nil
+ end
+ end
+
+ def self.class2qname(klass)
+ name = schema_type_definition(klass)
+ namespace = schema_ns_definition(klass)
+ XSD::QName.new(namespace, name)
+ end
+
+ def self.class2element(klass)
+ type = Mapping.class2qname(klass)
+ type.name ||= Mapping.name2elename(klass.name)
+ type.namespace ||= RubyCustomTypeNamespace
+ type
+ end
+
+ def self.obj2element(obj)
+ name = namespace = nil
+ ivars = obj.instance_variables
+ if ivars.include?('@schema_type')
+ name = obj.instance_variable_get('@schema_type')
+ end
+ if ivars.include?('@schema_ns')
+ namespace = obj.instance_variable_get('@schema_ns')
+ end
+ if !name or !namespace
+ class2qname(obj.class)
+ else
+ XSD::QName.new(namespace, name)
+ end
+ end
+
+ def self.define_singleton_method(obj, name, &block)
+ sclass = (class << obj; self; end)
+ sclass.class_eval {
+ define_method(name, &block)
+ }
+ end
+
+ def self.get_attribute(obj, attr_name)
+ if obj.is_a?(::Hash)
+ obj[attr_name] || obj[attr_name.intern]
+ else
+ name = XSD::CodeGen::GenSupport.safevarname(attr_name)
+ if obj.instance_variables.include?('@' + name)
+ obj.instance_variable_get('@' + name)
+ elsif ((obj.is_a?(::Struct) or obj.is_a?(Marshallable)) and
+ obj.respond_to?(name))
+ obj.__send__(name)
+ end
+ end
+ end
+
+ def self.set_attributes(obj, values)
+ if obj.is_a?(::SOAP::Mapping::Object)
+ values.each do |attr_name, value|
+ obj.__add_xmlele_value(attr_name, value)
+ end
+ else
+ values.each do |attr_name, value|
+ name = XSD::CodeGen::GenSupport.safevarname(attr_name)
+ setter = name + "="
+ if obj.respond_to?(setter)
+ obj.__send__(setter, value)
+ else
+ obj.instance_variable_set('@' + name, value)
+ begin
+ define_attr_accessor(obj, name,
+ proc { instance_variable_get('@' + name) },
+ proc { |value| instance_variable_set('@' + name, value) })
+ rescue TypeError
+ # singleton class may not exist (e.g. Float)
+ end
+ end
+ end
+ end
+ end
+
+ def self.define_attr_accessor(obj, name, getterproc, setterproc = nil)
+ define_singleton_method(obj, name, &getterproc)
+ define_singleton_method(obj, name + '=', &setterproc) if setterproc
+ end
+
+ def self.schema_type_definition(klass)
+ class_schema_variable(:schema_type, klass)
+ end
+
+ def self.schema_ns_definition(klass)
+ class_schema_variable(:schema_ns, klass)
+ end
+
+ def self.schema_element_definition(klass)
+ schema_element = class_schema_variable(:schema_element, klass) or return nil
+ schema_ns = schema_ns_definition(klass)
+ elements = []
+ as_array = []
+ schema_element.each do |varname, definition|
+ class_name, name = definition
+ if /\[\]$/ =~ class_name
+ class_name = class_name.sub(/\[\]$/, '')
+ as_array << (name ? name.name : varname)
+ end
+ elements << [name || XSD::QName.new(schema_ns, varname), class_name]
+ end
+ [elements, as_array]
+ end
+
+ def self.schema_attribute_definition(klass)
+ class_schema_variable(:schema_attribute, klass)
+ end
+
+ class << Mapping
+ private
+
+ def class_schema_variable(sym, klass)
+ var = "@@#{sym}"
+ klass.class_variables.include?(var) ? klass.class_eval(var) : nil
+ end
+
+ def protect_threadvars(*symbols)
+ backup = {}
+ begin
+ symbols.each do |sym|
+ backup[sym] = Thread.current[sym]
+ end
+ yield
+ ensure
+ symbols.each do |sym|
+ Thread.current[sym] = backup[sym]
+ end
+ end
+ end
+
+ def add_md_ary(md_ary, ary, indices, registry)
+ for idx in 0..(ary.size - 1)
+ if ary[idx].is_a?(Array)
+ add_md_ary(md_ary, ary[idx], indices + [idx], registry)
+ else
+ md_ary[*(indices + [idx])] = _obj2soap(ary[idx], registry)
+ end
+ end
+ end
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/soap/mapping/registry.rb b/ruby_1_8_5/lib/soap/mapping/registry.rb
new file mode 100644
index 0000000000..823e80666d
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/mapping/registry.rb
@@ -0,0 +1,541 @@
+# SOAP4R - Mapping registry.
+# Copyright (C) 2000, 2001, 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'soap/baseData'
+require 'soap/mapping/mapping'
+require 'soap/mapping/typeMap'
+require 'soap/mapping/factory'
+require 'soap/mapping/rubytypeFactory'
+
+
+module SOAP
+
+
+module Marshallable
+ # @@type_ns = Mapping::RubyCustomTypeNamespace
+end
+
+
+module Mapping
+
+
+module MappedException; end
+
+
+RubyTypeName = XSD::QName.new(RubyTypeInstanceNamespace, 'rubyType')
+RubyExtendName = XSD::QName.new(RubyTypeInstanceNamespace, 'extends')
+RubyIVarName = XSD::QName.new(RubyTypeInstanceNamespace, 'ivars')
+
+
+# Inner class to pass an exception.
+class SOAPException; include Marshallable
+ attr_reader :excn_type_name, :cause
+ def initialize(e)
+ @excn_type_name = Mapping.name2elename(e.class.to_s)
+ @cause = e
+ end
+
+ def to_e
+ if @cause.is_a?(::Exception)
+ @cause.extend(::SOAP::Mapping::MappedException)
+ return @cause
+ elsif @cause.respond_to?(:message) and @cause.respond_to?(:backtrace)
+ e = RuntimeError.new(@cause.message)
+ e.set_backtrace(@cause.backtrace)
+ return e
+ end
+ klass = Mapping.class_from_name(Mapping.elename2name(@excn_type_name.to_s))
+ if klass.nil? or not klass <= ::Exception
+ return RuntimeError.new(@cause.inspect)
+ end
+ obj = klass.new(@cause.message)
+ obj.extend(::SOAP::Mapping::MappedException)
+ obj
+ end
+end
+
+
+# For anyType object: SOAP::Mapping::Object not ::Object
+class Object; include Marshallable
+ def initialize
+ @__xmlele_type = {}
+ @__xmlele = []
+ @__xmlattr = {}
+ end
+
+ def inspect
+ sprintf("#<%s:0x%x%s>", self.class.name, __id__,
+ @__xmlele.collect { |name, value| " #{name}=#{value.inspect}" }.join)
+ end
+
+ def __xmlattr
+ @__xmlattr
+ end
+
+ def __xmlele
+ @__xmlele
+ end
+
+ def [](qname)
+ unless qname.is_a?(XSD::QName)
+ qname = XSD::QName.new(nil, qname)
+ end
+ @__xmlele.each do |k, v|
+ return v if k == qname
+ end
+ # fallback
+ @__xmlele.each do |k, v|
+ return v if k.name == qname.name
+ end
+ nil
+ end
+
+ def []=(qname, value)
+ unless qname.is_a?(XSD::QName)
+ qname = XSD::QName.new(nil, qname)
+ end
+ found = false
+ @__xmlele.each do |pair|
+ if pair[0] == qname
+ found = true
+ pair[1] = value
+ end
+ end
+ unless found
+ __define_attr_accessor(qname)
+ @__xmlele << [qname, value]
+ end
+ @__xmlele_type[qname] = :single
+ end
+
+ def __add_xmlele_value(qname, value)
+ found = false
+ @__xmlele.map! do |k, v|
+ if k == qname
+ found = true
+ [k, __set_xmlele_value(k, v, value)]
+ else
+ [k, v]
+ end
+ end
+ unless found
+ __define_attr_accessor(qname)
+ @__xmlele << [qname, value]
+ @__xmlele_type[qname] = :single
+ end
+ value
+ end
+
+private
+
+ if RUBY_VERSION > "1.7.0"
+ def __define_attr_accessor(qname)
+ name = XSD::CodeGen::GenSupport.safemethodname(qname.name)
+ Mapping.define_attr_accessor(self, name,
+ proc { self[qname] },
+ proc { |value| self[qname] = value })
+ end
+ else
+ def __define_attr_accessor(qname)
+ name = XSD::CodeGen::GenSupport.safemethodname(qname.name)
+ instance_eval <<-EOS
+ def #{name}
+ self[#{qname.dump}]
+ end
+
+ def #{name}=(value)
+ self[#{qname.dump}] = value
+ end
+ EOS
+ end
+ end
+
+ def __set_xmlele_value(key, org, value)
+ case @__xmlele_type[key]
+ when :multi
+ org << value
+ org
+ when :single
+ @__xmlele_type[key] = :multi
+ [org, value]
+ else
+ raise RuntimeError.new("unknown type")
+ end
+ end
+end
+
+
+class MappingError < Error; end
+
+
+class Registry
+ class Map
+ def initialize(registry)
+ @obj2soap = {}
+ @soap2obj = {}
+ @registry = registry
+ end
+
+ def obj2soap(obj)
+ klass = obj.class
+ if map = @obj2soap[klass]
+ map.each do |soap_class, factory, info|
+ ret = factory.obj2soap(soap_class, obj, info, @registry)
+ return ret if ret
+ end
+ end
+ ancestors = klass.ancestors
+ ancestors.delete(klass)
+ ancestors.delete(::Object)
+ ancestors.delete(::Kernel)
+ ancestors.each do |klass|
+ if map = @obj2soap[klass]
+ map.each do |soap_class, factory, info|
+ if info[:derived_class]
+ ret = factory.obj2soap(soap_class, obj, info, @registry)
+ return ret if ret
+ end
+ end
+ end
+ end
+ nil
+ end
+
+ def soap2obj(node, klass = nil)
+ if map = @soap2obj[node.class]
+ map.each do |obj_class, factory, info|
+ next if klass and obj_class != klass
+ conv, obj = factory.soap2obj(obj_class, node, info, @registry)
+ return true, obj if conv
+ end
+ end
+ return false, nil
+ end
+
+ # Give priority to former entry.
+ def init(init_map = [])
+ clear
+ init_map.reverse_each do |obj_class, soap_class, factory, info|
+ add(obj_class, soap_class, factory, info)
+ end
+ end
+
+ # Give priority to latter entry.
+ def add(obj_class, soap_class, factory, info)
+ info ||= {}
+ (@obj2soap[obj_class] ||= []).unshift([soap_class, factory, info])
+ (@soap2obj[soap_class] ||= []).unshift([obj_class, factory, info])
+ end
+
+ def clear
+ @obj2soap.clear
+ @soap2obj.clear
+ end
+
+ def find_mapped_soap_class(target_obj_class)
+ map = @obj2soap[target_obj_class]
+ map.empty? ? nil : map[0][1]
+ end
+
+ def find_mapped_obj_class(target_soap_class)
+ map = @soap2obj[target_soap_class]
+ map.empty? ? nil : map[0][0]
+ end
+ end
+
+ StringFactory = StringFactory_.new
+ BasetypeFactory = BasetypeFactory_.new
+ DateTimeFactory = DateTimeFactory_.new
+ ArrayFactory = ArrayFactory_.new
+ Base64Factory = Base64Factory_.new
+ URIFactory = URIFactory_.new
+ TypedArrayFactory = TypedArrayFactory_.new
+ TypedStructFactory = TypedStructFactory_.new
+
+ HashFactory = HashFactory_.new
+
+ SOAPBaseMap = [
+ [::NilClass, ::SOAP::SOAPNil, BasetypeFactory],
+ [::TrueClass, ::SOAP::SOAPBoolean, BasetypeFactory],
+ [::FalseClass, ::SOAP::SOAPBoolean, BasetypeFactory],
+ [::String, ::SOAP::SOAPString, StringFactory],
+ [::DateTime, ::SOAP::SOAPDateTime, DateTimeFactory],
+ [::Date, ::SOAP::SOAPDate, DateTimeFactory],
+ [::Time, ::SOAP::SOAPDateTime, DateTimeFactory],
+ [::Time, ::SOAP::SOAPTime, DateTimeFactory],
+ [::Float, ::SOAP::SOAPDouble, BasetypeFactory,
+ {:derived_class => true}],
+ [::Float, ::SOAP::SOAPFloat, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPInt, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPLong, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPInteger, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPShort, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPByte, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPNonPositiveInteger, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPNegativeInteger, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPNonNegativeInteger, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPPositiveInteger, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPUnsignedLong, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPUnsignedInt, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPUnsignedShort, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPUnsignedByte, BasetypeFactory,
+ {:derived_class => true}],
+ [::URI::Generic, ::SOAP::SOAPAnyURI, URIFactory,
+ {:derived_class => true}],
+ [::String, ::SOAP::SOAPBase64, Base64Factory],
+ [::String, ::SOAP::SOAPHexBinary, Base64Factory],
+ [::String, ::SOAP::SOAPDecimal, BasetypeFactory],
+ [::String, ::SOAP::SOAPDuration, BasetypeFactory],
+ [::String, ::SOAP::SOAPGYearMonth, BasetypeFactory],
+ [::String, ::SOAP::SOAPGYear, BasetypeFactory],
+ [::String, ::SOAP::SOAPGMonthDay, BasetypeFactory],
+ [::String, ::SOAP::SOAPGDay, BasetypeFactory],
+ [::String, ::SOAP::SOAPGMonth, BasetypeFactory],
+ [::String, ::SOAP::SOAPQName, BasetypeFactory],
+
+ [::Hash, ::SOAP::SOAPArray, HashFactory],
+ [::Hash, ::SOAP::SOAPStruct, HashFactory],
+
+ [::Array, ::SOAP::SOAPArray, ArrayFactory,
+ {:derived_class => true}],
+
+ [::SOAP::Mapping::SOAPException,
+ ::SOAP::SOAPStruct, TypedStructFactory,
+ {:type => XSD::QName.new(RubyCustomTypeNamespace, "SOAPException")}],
+ ]
+
+ RubyOriginalMap = [
+ [::NilClass, ::SOAP::SOAPNil, BasetypeFactory],
+ [::TrueClass, ::SOAP::SOAPBoolean, BasetypeFactory],
+ [::FalseClass, ::SOAP::SOAPBoolean, BasetypeFactory],
+ [::String, ::SOAP::SOAPString, StringFactory],
+ [::DateTime, ::SOAP::SOAPDateTime, DateTimeFactory],
+ [::Date, ::SOAP::SOAPDate, DateTimeFactory],
+ [::Time, ::SOAP::SOAPDateTime, DateTimeFactory],
+ [::Time, ::SOAP::SOAPTime, DateTimeFactory],
+ [::Float, ::SOAP::SOAPDouble, BasetypeFactory,
+ {:derived_class => true}],
+ [::Float, ::SOAP::SOAPFloat, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPInt, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPLong, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPInteger, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPShort, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPByte, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPNonPositiveInteger, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPNegativeInteger, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPNonNegativeInteger, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPPositiveInteger, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPUnsignedLong, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPUnsignedInt, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPUnsignedShort, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPUnsignedByte, BasetypeFactory,
+ {:derived_class => true}],
+ [::URI::Generic, ::SOAP::SOAPAnyURI, URIFactory,
+ {:derived_class => true}],
+ [::String, ::SOAP::SOAPBase64, Base64Factory],
+ [::String, ::SOAP::SOAPHexBinary, Base64Factory],
+ [::String, ::SOAP::SOAPDecimal, BasetypeFactory],
+ [::String, ::SOAP::SOAPDuration, BasetypeFactory],
+ [::String, ::SOAP::SOAPGYearMonth, BasetypeFactory],
+ [::String, ::SOAP::SOAPGYear, BasetypeFactory],
+ [::String, ::SOAP::SOAPGMonthDay, BasetypeFactory],
+ [::String, ::SOAP::SOAPGDay, BasetypeFactory],
+ [::String, ::SOAP::SOAPGMonth, BasetypeFactory],
+ [::String, ::SOAP::SOAPQName, BasetypeFactory],
+
+ [::Hash, ::SOAP::SOAPArray, HashFactory],
+ [::Hash, ::SOAP::SOAPStruct, HashFactory],
+
+ # Does not allow Array's subclass here.
+ [::Array, ::SOAP::SOAPArray, ArrayFactory],
+
+ [::SOAP::Mapping::SOAPException,
+ ::SOAP::SOAPStruct, TypedStructFactory,
+ {:type => XSD::QName.new(RubyCustomTypeNamespace, "SOAPException")}],
+ ]
+
+ attr_accessor :default_factory
+ attr_accessor :excn_handler_obj2soap
+ attr_accessor :excn_handler_soap2obj
+
+ def initialize(config = {})
+ @config = config
+ @map = Map.new(self)
+ if @config[:allow_original_mapping]
+ @allow_original_mapping = true
+ @map.init(RubyOriginalMap)
+ else
+ @allow_original_mapping = false
+ @map.init(SOAPBaseMap)
+ end
+ @allow_untyped_struct = @config.key?(:allow_untyped_struct) ?
+ @config[:allow_untyped_struct] : true
+ @rubytype_factory = RubytypeFactory.new(
+ :allow_untyped_struct => @allow_untyped_struct,
+ :allow_original_mapping => @allow_original_mapping
+ )
+ @default_factory = @rubytype_factory
+ @excn_handler_obj2soap = nil
+ @excn_handler_soap2obj = nil
+ end
+
+ def add(obj_class, soap_class, factory, info = nil)
+ @map.add(obj_class, soap_class, factory, info)
+ end
+ alias set add
+
+ # general Registry ignores type_qname
+ def obj2soap(obj, type_qname = nil)
+ soap = _obj2soap(obj)
+ if @allow_original_mapping
+ addextend2soap(soap, obj)
+ end
+ soap
+ end
+
+ def soap2obj(node, klass = nil)
+ obj = _soap2obj(node, klass)
+ if @allow_original_mapping
+ addextend2obj(obj, node.extraattr[RubyExtendName])
+ addiv2obj(obj, node.extraattr[RubyIVarName])
+ end
+ obj
+ end
+
+ def find_mapped_soap_class(obj_class)
+ @map.find_mapped_soap_class(obj_class)
+ end
+
+ def find_mapped_obj_class(soap_class)
+ @map.find_mapped_obj_class(soap_class)
+ end
+
+private
+
+ def _obj2soap(obj)
+ ret = nil
+ if obj.is_a?(SOAPStruct) or obj.is_a?(SOAPArray)
+ obj.replace do |ele|
+ Mapping._obj2soap(ele, self)
+ end
+ return obj
+ elsif obj.is_a?(SOAPBasetype)
+ return obj
+ end
+ begin
+ ret = @map.obj2soap(obj) ||
+ @default_factory.obj2soap(nil, obj, nil, self)
+ return ret if ret
+ rescue MappingError
+ end
+ if @excn_handler_obj2soap
+ ret = @excn_handler_obj2soap.call(obj) { |yield_obj|
+ Mapping._obj2soap(yield_obj, self)
+ }
+ return ret if ret
+ end
+ raise MappingError.new("Cannot map #{ obj.class.name } to SOAP/OM.")
+ end
+
+ # Might return nil as a mapping result.
+ def _soap2obj(node, klass = nil)
+ if node.extraattr.key?(RubyTypeName)
+ conv, obj = @rubytype_factory.soap2obj(nil, node, nil, self)
+ return obj if conv
+ else
+ conv, obj = @map.soap2obj(node, klass)
+ return obj if conv
+ conv, obj = @default_factory.soap2obj(nil, node, nil, self)
+ return obj if conv
+ end
+ if @excn_handler_soap2obj
+ begin
+ return @excn_handler_soap2obj.call(node) { |yield_node|
+ Mapping._soap2obj(yield_node, self)
+ }
+ rescue Exception
+ end
+ end
+ raise MappingError.new("Cannot map #{ node.type.name } to Ruby object.")
+ end
+
+ def addiv2obj(obj, attr)
+ return unless attr
+ vars = {}
+ attr.__getobj__.each do |name, value|
+ vars[name] = Mapping._soap2obj(value, self)
+ end
+ Mapping.set_attributes(obj, vars)
+ end
+
+ if RUBY_VERSION >= '1.8.0'
+ def addextend2obj(obj, attr)
+ return unless attr
+ attr.split(/ /).reverse_each do |mstr|
+ obj.extend(Mapping.module_from_name(mstr))
+ end
+ end
+ else
+ # (class < false; self; end).ancestors includes "TrueClass" under 1.6...
+ def addextend2obj(obj, attr)
+ return unless attr
+ attr.split(/ /).reverse_each do |mstr|
+ m = Mapping.module_from_name(mstr)
+ obj.extend(m)
+ end
+ end
+ end
+
+ def addextend2soap(node, obj)
+ return if obj.is_a?(Symbol) or obj.is_a?(Fixnum)
+ list = (class << obj; self; end).ancestors - obj.class.ancestors
+ unless list.empty?
+ node.extraattr[RubyExtendName] = list.collect { |c|
+ if c.name.empty?
+ raise TypeError.new("singleton can't be dumped #{ obj }")
+ end
+ c.name
+ }.join(" ")
+ end
+ end
+
+end
+
+
+DefaultRegistry = Registry.new
+RubyOriginalRegistry = Registry.new(:allow_original_mapping => true)
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/soap/mapping/rubytypeFactory.rb b/ruby_1_8_5/lib/soap/mapping/rubytypeFactory.rb
new file mode 100644
index 0000000000..61c21d8b20
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/mapping/rubytypeFactory.rb
@@ -0,0 +1,475 @@
+# SOAP4R - Ruby type mapping factory.
+# Copyright (C) 2000-2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+module SOAP
+module Mapping
+
+
+class RubytypeFactory < Factory
+ TYPE_STRING = XSD::QName.new(RubyTypeNamespace, 'String')
+ TYPE_TIME = XSD::QName.new(RubyTypeNamespace, 'Time')
+ TYPE_ARRAY = XSD::QName.new(RubyTypeNamespace, 'Array')
+ TYPE_REGEXP = XSD::QName.new(RubyTypeNamespace, 'Regexp')
+ TYPE_RANGE = XSD::QName.new(RubyTypeNamespace, 'Range')
+ TYPE_CLASS = XSD::QName.new(RubyTypeNamespace, 'Class')
+ TYPE_MODULE = XSD::QName.new(RubyTypeNamespace, 'Module')
+ TYPE_SYMBOL = XSD::QName.new(RubyTypeNamespace, 'Symbol')
+ TYPE_STRUCT = XSD::QName.new(RubyTypeNamespace, 'Struct')
+ TYPE_HASH = XSD::QName.new(RubyTypeNamespace, 'Map')
+
+ def initialize(config = {})
+ @config = config
+ @allow_untyped_struct = @config.key?(:allow_untyped_struct) ?
+ @config[:allow_untyped_struct] : true
+ @allow_original_mapping = @config.key?(:allow_original_mapping) ?
+ @config[:allow_original_mapping] : false
+ @string_factory = StringFactory_.new(true)
+ @basetype_factory = BasetypeFactory_.new(true)
+ @datetime_factory = DateTimeFactory_.new(true)
+ @array_factory = ArrayFactory_.new(true)
+ @hash_factory = HashFactory_.new(true)
+ end
+
+ def obj2soap(soap_class, obj, info, map)
+ param = nil
+ case obj
+ when ::String
+ unless @allow_original_mapping
+ return nil
+ end
+ param = @string_factory.obj2soap(SOAPString, obj, info, map)
+ if obj.class != String
+ param.extraattr[RubyTypeName] = obj.class.name
+ end
+ addiv2soapattr(param, obj, map)
+ when ::Time
+ unless @allow_original_mapping
+ return nil
+ end
+ param = @datetime_factory.obj2soap(SOAPDateTime, obj, info, map)
+ if obj.class != Time
+ param.extraattr[RubyTypeName] = obj.class.name
+ end
+ addiv2soapattr(param, obj, map)
+ when ::Array
+ unless @allow_original_mapping
+ return nil
+ end
+ param = @array_factory.obj2soap(nil, obj, info, map)
+ if obj.class != Array
+ param.extraattr[RubyTypeName] = obj.class.name
+ end
+ addiv2soapattr(param, obj, map)
+ when ::NilClass
+ unless @allow_original_mapping
+ return nil
+ end
+ param = @basetype_factory.obj2soap(SOAPNil, obj, info, map)
+ addiv2soapattr(param, obj, map)
+ when ::FalseClass, ::TrueClass
+ unless @allow_original_mapping
+ return nil
+ end
+ param = @basetype_factory.obj2soap(SOAPBoolean, obj, info, map)
+ addiv2soapattr(param, obj, map)
+ when ::Integer
+ unless @allow_original_mapping
+ return nil
+ end
+ param = @basetype_factory.obj2soap(SOAPInt, obj, info, map)
+ param ||= @basetype_factory.obj2soap(SOAPInteger, obj, info, map)
+ param ||= @basetype_factory.obj2soap(SOAPDecimal, obj, info, map)
+ addiv2soapattr(param, obj, map)
+ when ::Float
+ unless @allow_original_mapping
+ return nil
+ end
+ param = @basetype_factory.obj2soap(SOAPDouble, obj, info, map)
+ if obj.class != Float
+ param.extraattr[RubyTypeName] = obj.class.name
+ end
+ addiv2soapattr(param, obj, map)
+ when ::Hash
+ unless @allow_original_mapping
+ return nil
+ end
+ if obj.respond_to?(:default_proc) && obj.default_proc
+ raise TypeError.new("cannot dump hash with default proc")
+ end
+ param = SOAPStruct.new(TYPE_HASH)
+ mark_marshalled_obj(obj, param)
+ if obj.class != Hash
+ param.extraattr[RubyTypeName] = obj.class.name
+ end
+ obj.each do |key, value|
+ elem = SOAPStruct.new # Undefined type.
+ elem.add("key", Mapping._obj2soap(key, map))
+ elem.add("value", Mapping._obj2soap(value, map))
+ param.add("item", elem)
+ end
+ param.add('default', Mapping._obj2soap(obj.default, map))
+ addiv2soapattr(param, obj, map)
+ when ::Regexp
+ unless @allow_original_mapping
+ return nil
+ end
+ param = SOAPStruct.new(TYPE_REGEXP)
+ mark_marshalled_obj(obj, param)
+ if obj.class != Regexp
+ param.extraattr[RubyTypeName] = obj.class.name
+ end
+ param.add('source', SOAPBase64.new(obj.source))
+ if obj.respond_to?('options')
+ # Regexp#options is from Ruby/1.7
+ options = obj.options
+ else
+ options = 0
+ obj.inspect.sub(/^.*\//, '').each_byte do |c|
+ options += case c
+ when ?i
+ 1
+ when ?x
+ 2
+ when ?m
+ 4
+ when ?n
+ 16
+ when ?e
+ 32
+ when ?s
+ 48
+ when ?u
+ 64
+ end
+ end
+ end
+ param.add('options', SOAPInt.new(options))
+ addiv2soapattr(param, obj, map)
+ when ::Range
+ unless @allow_original_mapping
+ return nil
+ end
+ param = SOAPStruct.new(TYPE_RANGE)
+ mark_marshalled_obj(obj, param)
+ if obj.class != Range
+ param.extraattr[RubyTypeName] = obj.class.name
+ end
+ param.add('begin', Mapping._obj2soap(obj.begin, map))
+ param.add('end', Mapping._obj2soap(obj.end, map))
+ param.add('exclude_end', SOAP::SOAPBoolean.new(obj.exclude_end?))
+ addiv2soapattr(param, obj, map)
+ when ::Class
+ unless @allow_original_mapping
+ return nil
+ end
+ if obj.to_s[0] == ?#
+ raise TypeError.new("can't dump anonymous class #{obj}")
+ end
+ param = SOAPStruct.new(TYPE_CLASS)
+ mark_marshalled_obj(obj, param)
+ param.add('name', SOAPString.new(obj.name))
+ addiv2soapattr(param, obj, map)
+ when ::Module
+ unless @allow_original_mapping
+ return nil
+ end
+ if obj.to_s[0] == ?#
+ raise TypeError.new("can't dump anonymous module #{obj}")
+ end
+ param = SOAPStruct.new(TYPE_MODULE)
+ mark_marshalled_obj(obj, param)
+ param.add('name', SOAPString.new(obj.name))
+ addiv2soapattr(param, obj, map)
+ when ::Symbol
+ unless @allow_original_mapping
+ return nil
+ end
+ param = SOAPStruct.new(TYPE_SYMBOL)
+ mark_marshalled_obj(obj, param)
+ param.add('id', SOAPString.new(obj.id2name))
+ addiv2soapattr(param, obj, map)
+ when ::Struct
+ unless @allow_original_mapping
+ # treat it as an user defined class. [ruby-talk:104980]
+ #param = unknownobj2soap(soap_class, obj, info, map)
+ param = SOAPStruct.new(XSD::AnyTypeName)
+ mark_marshalled_obj(obj, param)
+ obj.members.each do |member|
+ param.add(Mapping.name2elename(member),
+ Mapping._obj2soap(obj[member], map))
+ end
+ else
+ param = SOAPStruct.new(TYPE_STRUCT)
+ mark_marshalled_obj(obj, param)
+ param.add('type', ele_type = SOAPString.new(obj.class.to_s))
+ ele_member = SOAPStruct.new
+ obj.members.each do |member|
+ ele_member.add(Mapping.name2elename(member),
+ Mapping._obj2soap(obj[member], map))
+ end
+ param.add('member', ele_member)
+ addiv2soapattr(param, obj, map)
+ end
+ when ::IO, ::Binding, ::Continuation, ::Data, ::Dir, ::File::Stat,
+ ::MatchData, Method, ::Proc, ::Thread, ::ThreadGroup
+ # from 1.8: Process::Status, UnboundMethod
+ return nil
+ when ::SOAP::Mapping::Object
+ param = SOAPStruct.new(XSD::AnyTypeName)
+ mark_marshalled_obj(obj, param)
+ obj.__xmlele.each do |key, value|
+ param.add(key.name, Mapping._obj2soap(value, map))
+ end
+ obj.__xmlattr.each do |key, value|
+ param.extraattr[key] = value
+ end
+ when ::Exception
+ typestr = Mapping.name2elename(obj.class.to_s)
+ param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, typestr))
+ mark_marshalled_obj(obj, param)
+ param.add('message', Mapping._obj2soap(obj.message, map))
+ param.add('backtrace', Mapping._obj2soap(obj.backtrace, map))
+ addiv2soapattr(param, obj, map)
+ else
+ param = unknownobj2soap(soap_class, obj, info, map)
+ end
+ param
+ end
+
+ def soap2obj(obj_class, node, info, map)
+ rubytype = node.extraattr[RubyTypeName]
+ if rubytype or node.type.namespace == RubyTypeNamespace
+ rubytype2obj(node, info, map, rubytype)
+ elsif node.type == XSD::AnyTypeName or node.type == XSD::AnySimpleTypeName
+ anytype2obj(node, info, map)
+ else
+ unknowntype2obj(node, info, map)
+ end
+ end
+
+private
+
+ def addiv2soapattr(node, obj, map)
+ return if obj.instance_variables.empty?
+ ivars = SOAPStruct.new # Undefined type.
+ setiv2soap(ivars, obj, map)
+ node.extraattr[RubyIVarName] = ivars
+ end
+
+ def unknownobj2soap(soap_class, obj, info, map)
+ if obj.class.name.empty?
+ raise TypeError.new("can't dump anonymous class #{obj}")
+ end
+ singleton_class = class << obj; self; end
+ if !singleton_methods_true(obj).empty? or
+ !singleton_class.instance_variables.empty?
+ raise TypeError.new("singleton can't be dumped #{obj}")
+ end
+ if !(singleton_class.ancestors - obj.class.ancestors).empty?
+ typestr = Mapping.name2elename(obj.class.to_s)
+ type = XSD::QName.new(RubyTypeNamespace, typestr)
+ else
+ type = Mapping.class2element(obj.class)
+ end
+ param = SOAPStruct.new(type)
+ mark_marshalled_obj(obj, param)
+ setiv2soap(param, obj, map)
+ param
+ end
+
+ if RUBY_VERSION >= '1.8.0'
+ def singleton_methods_true(obj)
+ obj.singleton_methods(true)
+ end
+ else
+ def singleton_methods_true(obj)
+ obj.singleton_methods
+ end
+ end
+
+ def rubytype2obj(node, info, map, rubytype)
+ klass = rubytype ? Mapping.class_from_name(rubytype) : nil
+ obj = nil
+ case node
+ when SOAPString
+ return @string_factory.soap2obj(klass || String, node, info, map)
+ when SOAPDateTime
+ #return @datetime_factory.soap2obj(klass || Time, node, info, map)
+ klass ||= Time
+ t = node.to_time
+ arg = [t.year, t.month, t.mday, t.hour, t.min, t.sec, t.usec]
+ obj = t.gmt? ? klass.gm(*arg) : klass.local(*arg)
+ mark_unmarshalled_obj(node, obj)
+ return true, obj
+ when SOAPArray
+ return @array_factory.soap2obj(klass || Array, node, info, map)
+ when SOAPNil, SOAPBoolean, SOAPInt, SOAPInteger, SOAPDecimal, SOAPDouble
+ return @basetype_factory.soap2obj(nil, node, info, map)
+ when SOAPStruct
+ return rubytypestruct2obj(node, info, map, rubytype)
+ else
+ raise
+ end
+ end
+
+ def rubytypestruct2obj(node, info, map, rubytype)
+ klass = rubytype ? Mapping.class_from_name(rubytype) : nil
+ obj = nil
+ case node.type
+ when TYPE_HASH
+ klass = rubytype ? Mapping.class_from_name(rubytype) : Hash
+ obj = Mapping.create_empty_object(klass)
+ mark_unmarshalled_obj(node, obj)
+ node.each do |key, value|
+ next unless key == 'item'
+ obj[Mapping._soap2obj(value['key'], map)] =
+ Mapping._soap2obj(value['value'], map)
+ end
+ if node.key?('default')
+ obj.default = Mapping._soap2obj(node['default'], map)
+ end
+ when TYPE_REGEXP
+ klass = rubytype ? Mapping.class_from_name(rubytype) : Regexp
+ obj = Mapping.create_empty_object(klass)
+ mark_unmarshalled_obj(node, obj)
+ source = node['source'].string
+ options = node['options'].data || 0
+ Regexp.instance_method(:initialize).bind(obj).call(source, options)
+ when TYPE_RANGE
+ klass = rubytype ? Mapping.class_from_name(rubytype) : Range
+ obj = Mapping.create_empty_object(klass)
+ mark_unmarshalled_obj(node, obj)
+ first = Mapping._soap2obj(node['begin'], map)
+ last = Mapping._soap2obj(node['end'], map)
+ exclude_end = node['exclude_end'].data
+ Range.instance_method(:initialize).bind(obj).call(first, last, exclude_end)
+ when TYPE_CLASS
+ obj = Mapping.class_from_name(node['name'].data)
+ when TYPE_MODULE
+ obj = Mapping.class_from_name(node['name'].data)
+ when TYPE_SYMBOL
+ obj = node['id'].data.intern
+ when TYPE_STRUCT
+ typestr = Mapping.elename2name(node['type'].data)
+ klass = Mapping.class_from_name(typestr)
+ if klass.nil?
+ return false
+ end
+ unless klass <= ::Struct
+ return false
+ end
+ obj = Mapping.create_empty_object(klass)
+ mark_unmarshalled_obj(node, obj)
+ node['member'].each do |name, value|
+ obj[Mapping.elename2name(name)] = Mapping._soap2obj(value, map)
+ end
+ else
+ return unknowntype2obj(node, info, map)
+ end
+ return true, obj
+ end
+
+ def anytype2obj(node, info, map)
+ case node
+ when SOAPBasetype
+ return true, node.data
+ when SOAPStruct
+ klass = ::SOAP::Mapping::Object
+ obj = klass.new
+ mark_unmarshalled_obj(node, obj)
+ node.each do |name, value|
+ obj.__add_xmlele_value(XSD::QName.new(nil, name),
+ Mapping._soap2obj(value, map))
+ end
+ unless node.extraattr.empty?
+ obj.instance_variable_set('@__xmlattr', node.extraattr)
+ end
+ return true, obj
+ else
+ return false
+ end
+ end
+
+ def unknowntype2obj(node, info, map)
+ case node
+ when SOAPBasetype
+ return true, node.data
+ when SOAPArray
+ return @array_factory.soap2obj(Array, node, info, map)
+ when SOAPStruct
+ obj = unknownstruct2obj(node, info, map)
+ return true, obj if obj
+ if !@allow_untyped_struct
+ return false
+ end
+ return anytype2obj(node, info, map)
+ else
+ # Basetype which is not defined...
+ return false
+ end
+ end
+
+ def unknownstruct2obj(node, info, map)
+ unless node.type.name
+ return nil
+ end
+ typestr = Mapping.elename2name(node.type.name)
+ klass = Mapping.class_from_name(typestr)
+ if klass.nil? and @allow_untyped_struct
+ klass = Mapping.class_from_name(typestr, true) # lenient
+ end
+ if klass.nil?
+ return nil
+ end
+ if klass <= ::Exception
+ return exception2obj(klass, node, map)
+ end
+ klass_type = Mapping.class2qname(klass)
+ return nil unless node.type.match(klass_type)
+ obj = nil
+ begin
+ obj = Mapping.create_empty_object(klass)
+ rescue
+ # type name "data" tries Data.new which raises TypeError
+ nil
+ end
+ mark_unmarshalled_obj(node, obj)
+ setiv2obj(obj, node, map)
+ obj
+ end
+
+ def exception2obj(klass, node, map)
+ message = Mapping._soap2obj(node['message'], map)
+ backtrace = Mapping._soap2obj(node['backtrace'], map)
+ obj = Mapping.create_empty_object(klass)
+ obj = obj.exception(message)
+ mark_unmarshalled_obj(node, obj)
+ obj.set_backtrace(backtrace)
+ obj
+ end
+
+ # Only creates empty array. Do String#replace it with real string.
+ def array2obj(node, map, rubytype)
+ klass = rubytype ? Mapping.class_from_name(rubytype) : Array
+ obj = Mapping.create_empty_object(klass)
+ mark_unmarshalled_obj(node, obj)
+ obj
+ end
+
+ # Only creates empty string. Do String#replace it with real string.
+ def string2obj(node, map, rubytype)
+ klass = rubytype ? Mapping.class_from_name(rubytype) : String
+ obj = Mapping.create_empty_object(klass)
+ mark_unmarshalled_obj(node, obj)
+ obj
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/soap/mapping/typeMap.rb b/ruby_1_8_5/lib/soap/mapping/typeMap.rb
new file mode 100644
index 0000000000..34db19a5b6
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/mapping/typeMap.rb
@@ -0,0 +1,50 @@
+# SOAP4R - Base type mapping definition
+# Copyright (C) 2000, 2001, 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+module SOAP
+
+
+TypeMap = {
+ XSD::XSDAnySimpleType::Type => SOAPAnySimpleType,
+ XSD::XSDString::Type => SOAPString,
+ XSD::XSDBoolean::Type => SOAPBoolean,
+ XSD::XSDDecimal::Type => SOAPDecimal,
+ XSD::XSDFloat::Type => SOAPFloat,
+ XSD::XSDDouble::Type => SOAPDouble,
+ XSD::XSDDuration::Type => SOAPDuration,
+ XSD::XSDDateTime::Type => SOAPDateTime,
+ XSD::XSDTime::Type => SOAPTime,
+ XSD::XSDDate::Type => SOAPDate,
+ XSD::XSDGYearMonth::Type => SOAPGYearMonth,
+ XSD::XSDGYear::Type => SOAPGYear,
+ XSD::XSDGMonthDay::Type => SOAPGMonthDay,
+ XSD::XSDGDay::Type => SOAPGDay,
+ XSD::XSDGMonth::Type => SOAPGMonth,
+ XSD::XSDHexBinary::Type => SOAPHexBinary,
+ XSD::XSDBase64Binary::Type => SOAPBase64,
+ XSD::XSDAnyURI::Type => SOAPAnyURI,
+ XSD::XSDQName::Type => SOAPQName,
+ XSD::XSDInteger::Type => SOAPInteger,
+ XSD::XSDNonPositiveInteger::Type => SOAPNonPositiveInteger,
+ XSD::XSDNegativeInteger::Type => SOAPNegativeInteger,
+ XSD::XSDLong::Type => SOAPLong,
+ XSD::XSDInt::Type => SOAPInt,
+ XSD::XSDShort::Type => SOAPShort,
+ XSD::XSDByte::Type => SOAPByte,
+ XSD::XSDNonNegativeInteger::Type => SOAPNonNegativeInteger,
+ XSD::XSDUnsignedLong::Type => SOAPUnsignedLong,
+ XSD::XSDUnsignedInt::Type => SOAPUnsignedInt,
+ XSD::XSDUnsignedShort::Type => SOAPUnsignedShort,
+ XSD::XSDUnsignedByte::Type => SOAPUnsignedByte,
+ XSD::XSDPositiveInteger::Type => SOAPPositiveInteger,
+
+ SOAP::SOAPBase64::Type => SOAPBase64,
+}
+
+
+end
diff --git a/ruby_1_8_5/lib/soap/mapping/wsdlencodedregistry.rb b/ruby_1_8_5/lib/soap/mapping/wsdlencodedregistry.rb
new file mode 100644
index 0000000000..4efb60188f
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/mapping/wsdlencodedregistry.rb
@@ -0,0 +1,280 @@
+# SOAP4R - WSDL encoded mapping registry.
+# Copyright (C) 2000-2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/qname'
+require 'xsd/namedelements'
+require 'soap/baseData'
+require 'soap/mapping/mapping'
+require 'soap/mapping/typeMap'
+
+
+module SOAP
+module Mapping
+
+
+class WSDLEncodedRegistry < Registry
+ include TraverseSupport
+
+ attr_reader :definedelements
+ attr_reader :definedtypes
+ attr_accessor :excn_handler_obj2soap
+ attr_accessor :excn_handler_soap2obj
+
+ def initialize(definedtypes = XSD::NamedElements::Empty)
+ @definedtypes = definedtypes
+ # @definedelements = definedelements needed?
+ @excn_handler_obj2soap = nil
+ @excn_handler_soap2obj = nil
+ # For mapping AnyType element.
+ @rubytype_factory = RubytypeFactory.new(
+ :allow_untyped_struct => true,
+ :allow_original_mapping => true
+ )
+ @schema_element_cache = {}
+ end
+
+ def obj2soap(obj, qname = nil)
+ soap_obj = nil
+ if type = @definedtypes[qname]
+ soap_obj = obj2typesoap(obj, type)
+ else
+ soap_obj = any2soap(obj, qname)
+ end
+ return soap_obj if soap_obj
+ if @excn_handler_obj2soap
+ soap_obj = @excn_handler_obj2soap.call(obj) { |yield_obj|
+ Mapping._obj2soap(yield_obj, self)
+ }
+ return soap_obj if soap_obj
+ end
+ if qname
+ raise MappingError.new("cannot map #{obj.class.name} as #{qname}")
+ else
+ raise MappingError.new("cannot map #{obj.class.name} to SOAP/OM")
+ end
+ end
+
+ # map anything for now: must refer WSDL while mapping. [ToDo]
+ def soap2obj(node, obj_class = nil)
+ begin
+ return any2obj(node, obj_class)
+ rescue MappingError
+ end
+ if @excn_handler_soap2obj
+ begin
+ return @excn_handler_soap2obj.call(node) { |yield_node|
+ Mapping._soap2obj(yield_node, self)
+ }
+ rescue Exception
+ end
+ end
+ raise MappingError.new("cannot map #{node.type.name} to Ruby object")
+ end
+
+private
+
+ def any2soap(obj, qname)
+ if obj.nil?
+ SOAPNil.new
+ elsif qname.nil? or qname == XSD::AnyTypeName
+ @rubytype_factory.obj2soap(nil, obj, nil, self)
+ elsif obj.is_a?(XSD::NSDBase)
+ soap2soap(obj, qname)
+ elsif (type = TypeMap[qname])
+ base2soap(obj, type)
+ else
+ nil
+ end
+ end
+
+ def soap2soap(obj, type_qname)
+ if obj.is_a?(SOAPBasetype)
+ obj
+ elsif obj.is_a?(SOAPStruct) && (type = @definedtypes[type_qname])
+ soap_obj = obj
+ mark_marshalled_obj(obj, soap_obj)
+ elements2soap(obj, soap_obj, type.content.elements)
+ soap_obj
+ elsif obj.is_a?(SOAPArray) && (type = @definedtypes[type_qname])
+ soap_obj = obj
+ contenttype = type.child_type
+ mark_marshalled_obj(obj, soap_obj)
+ obj.replace do |ele|
+ Mapping._obj2soap(ele, self, contenttype)
+ end
+ soap_obj
+ else
+ nil
+ end
+ end
+
+ def obj2typesoap(obj, type)
+ if type.is_a?(::WSDL::XMLSchema::SimpleType)
+ simpleobj2soap(obj, type)
+ else
+ complexobj2soap(obj, type)
+ end
+ end
+
+ def simpleobj2soap(obj, type)
+ type.check_lexical_format(obj)
+ return SOAPNil.new if obj.nil? # ToDo: check nillable.
+ o = base2soap(obj, TypeMap[type.base])
+ o
+ end
+
+ def complexobj2soap(obj, type)
+ case type.compoundtype
+ when :TYPE_STRUCT
+ struct2soap(obj, type.name, type)
+ when :TYPE_ARRAY
+ array2soap(obj, type.name, type)
+ when :TYPE_MAP
+ map2soap(obj, type.name, type)
+ when :TYPE_SIMPLE
+ simpleobj2soap(obj, type.simplecontent)
+ when :TYPE_EMPTY
+ raise MappingError.new("should be empty") unless obj.nil?
+ SOAPNil.new
+ else
+ raise MappingError.new("unknown compound type: #{type.compoundtype}")
+ end
+ end
+
+ def base2soap(obj, type)
+ soap_obj = nil
+ if type <= XSD::XSDString
+ str = XSD::Charset.encoding_conv(obj.to_s,
+ Thread.current[:SOAPExternalCES], XSD::Charset.encoding)
+ soap_obj = type.new(str)
+ mark_marshalled_obj(obj, soap_obj)
+ else
+ soap_obj = type.new(obj)
+ end
+ soap_obj
+ end
+
+ def struct2soap(obj, type_qname, type)
+ return SOAPNil.new if obj.nil? # ToDo: check nillable.
+ soap_obj = SOAPStruct.new(type_qname)
+ unless obj.nil?
+ mark_marshalled_obj(obj, soap_obj)
+ elements2soap(obj, soap_obj, type.content.elements)
+ end
+ soap_obj
+ end
+
+ def array2soap(obj, type_qname, type)
+ return SOAPNil.new if obj.nil? # ToDo: check nillable.
+ arytype = type.child_type
+ soap_obj = SOAPArray.new(ValueArrayName, 1, arytype)
+ unless obj.nil?
+ mark_marshalled_obj(obj, soap_obj)
+ obj.each do |item|
+ soap_obj.add(Mapping._obj2soap(item, self, arytype))
+ end
+ end
+ soap_obj
+ end
+
+ MapKeyName = XSD::QName.new(nil, "key")
+ MapValueName = XSD::QName.new(nil, "value")
+ def map2soap(obj, type_qname, type)
+ return SOAPNil.new if obj.nil? # ToDo: check nillable.
+ keytype = type.child_type(MapKeyName) || XSD::AnyTypeName
+ valuetype = type.child_type(MapValueName) || XSD::AnyTypeName
+ soap_obj = SOAPStruct.new(MapQName)
+ unless obj.nil?
+ mark_marshalled_obj(obj, soap_obj)
+ obj.each do |key, value|
+ elem = SOAPStruct.new
+ elem.add("key", Mapping._obj2soap(key, self, keytype))
+ elem.add("value", Mapping._obj2soap(value, self, valuetype))
+ # ApacheAxis allows only 'item' here.
+ soap_obj.add("item", elem)
+ end
+ end
+ soap_obj
+ end
+
+ def elements2soap(obj, soap_obj, elements)
+ elements.each do |element|
+ name = element.name.name
+ child_obj = Mapping.get_attribute(obj, name)
+ soap_obj.add(name,
+ Mapping._obj2soap(child_obj, self, element.type || element.name))
+ end
+ end
+
+ def any2obj(node, obj_class)
+ unless obj_class
+ typestr = XSD::CodeGen::GenSupport.safeconstname(node.elename.name)
+ obj_class = Mapping.class_from_name(typestr)
+ end
+ if obj_class and obj_class.class_variables.include?('@@schema_element')
+ soap2stubobj(node, obj_class)
+ else
+ Mapping._soap2obj(node, Mapping::DefaultRegistry, obj_class)
+ end
+ end
+
+ def soap2stubobj(node, obj_class)
+ obj = Mapping.create_empty_object(obj_class)
+ unless node.is_a?(SOAPNil)
+ add_elements2stubobj(node, obj)
+ end
+ obj
+ end
+
+ def add_elements2stubobj(node, obj)
+ elements, as_array = schema_element_definition(obj.class)
+ vars = {}
+ node.each do |name, value|
+ item = elements.find { |k, v| k.name == name }
+ if item
+ elename, class_name = item
+ if klass = Mapping.class_from_name(class_name)
+ # klass must be a SOAPBasetype or a class
+ if klass.ancestors.include?(::SOAP::SOAPBasetype)
+ if value.respond_to?(:data)
+ child = klass.new(value.data).data
+ else
+ child = klass.new(nil).data
+ end
+ else
+ child = Mapping._soap2obj(value, self, klass)
+ end
+ elsif klass = Mapping.module_from_name(class_name)
+ # simpletype
+ if value.respond_to?(:data)
+ child = value.data
+ else
+ raise MappingError.new(
+ "cannot map to a module value: #{class_name}")
+ end
+ else
+ raise MappingError.new("unknown class: #{class_name}")
+ end
+ else # untyped element is treated as anyType.
+ child = Mapping._soap2obj(value, self)
+ end
+ vars[name] = child
+ end
+ Mapping.set_attributes(obj, vars)
+ end
+
+ # it caches @@schema_element. this means that @@schema_element must not be
+ # changed while a lifetime of a WSDLLiteralRegistry.
+ def schema_element_definition(klass)
+ @schema_element_cache[klass] ||= Mapping.schema_element_definition(klass)
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/soap/mapping/wsdlliteralregistry.rb b/ruby_1_8_5/lib/soap/mapping/wsdlliteralregistry.rb
new file mode 100644
index 0000000000..7bb8e12203
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/mapping/wsdlliteralregistry.rb
@@ -0,0 +1,418 @@
+# SOAP4R - WSDL literal mapping registry.
+# Copyright (C) 2004, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'soap/baseData'
+require 'soap/mapping/mapping'
+require 'soap/mapping/typeMap'
+require 'xsd/codegen/gensupport'
+require 'xsd/namedelements'
+
+
+module SOAP
+module Mapping
+
+
+class WSDLLiteralRegistry < Registry
+ attr_reader :definedelements
+ attr_reader :definedtypes
+ attr_accessor :excn_handler_obj2soap
+ attr_accessor :excn_handler_soap2obj
+
+ def initialize(definedtypes = XSD::NamedElements::Empty,
+ definedelements = XSD::NamedElements::Empty)
+ @definedtypes = definedtypes
+ @definedelements = definedelements
+ @excn_handler_obj2soap = nil
+ @excn_handler_soap2obj = nil
+ @schema_element_cache = {}
+ @schema_attribute_cache = {}
+ end
+
+ def obj2soap(obj, qname)
+ soap_obj = nil
+ if ele = @definedelements[qname]
+ soap_obj = obj2elesoap(obj, ele)
+ elsif type = @definedtypes[qname]
+ soap_obj = obj2typesoap(obj, type, true)
+ else
+ soap_obj = any2soap(obj, qname)
+ end
+ return soap_obj if soap_obj
+ if @excn_handler_obj2soap
+ soap_obj = @excn_handler_obj2soap.call(obj) { |yield_obj|
+ Mapping.obj2soap(yield_obj, nil, nil, MAPPING_OPT)
+ }
+ return soap_obj if soap_obj
+ end
+ raise MappingError.new("cannot map #{obj.class.name} as #{qname}")
+ end
+
+ # node should be a SOAPElement
+ def soap2obj(node, obj_class = nil)
+ # obj_class is given when rpc/literal service. but ignored for now.
+ begin
+ return any2obj(node)
+ rescue MappingError
+ end
+ if @excn_handler_soap2obj
+ begin
+ return @excn_handler_soap2obj.call(node) { |yield_node|
+ Mapping.soap2obj(yield_node, nil, nil, MAPPING_OPT)
+ }
+ rescue Exception
+ end
+ end
+ if node.respond_to?(:type)
+ raise MappingError.new("cannot map #{node.type.name} to Ruby object")
+ else
+ raise MappingError.new("cannot map #{node.elename.name} to Ruby object")
+ end
+ end
+
+private
+
+ MAPPING_OPT = { :no_reference => true }
+
+ def obj2elesoap(obj, ele)
+ o = nil
+ qualified = (ele.elementform == 'qualified')
+ if ele.type
+ if type = @definedtypes[ele.type]
+ o = obj2typesoap(obj, type, qualified)
+ elsif type = TypeMap[ele.type]
+ o = base2soap(obj, type)
+ else
+ raise MappingError.new("cannot find type #{ele.type}")
+ end
+ elsif ele.local_complextype
+ o = obj2typesoap(obj, ele.local_complextype, qualified)
+ add_attributes2soap(obj, o)
+ elsif ele.local_simpletype
+ o = obj2typesoap(obj, ele.local_simpletype, qualified)
+ else
+ raise MappingError.new('illegal schema?')
+ end
+ o.elename = ele.name
+ o
+ end
+
+ def obj2typesoap(obj, type, qualified)
+ if type.is_a?(::WSDL::XMLSchema::SimpleType)
+ simpleobj2soap(obj, type)
+ else
+ complexobj2soap(obj, type, qualified)
+ end
+ end
+
+ def simpleobj2soap(obj, type)
+ type.check_lexical_format(obj)
+ return SOAPNil.new if obj.nil? # ToDo: check nillable.
+ o = base2soap(obj, TypeMap[type.base])
+ o
+ end
+
+ def complexobj2soap(obj, type, qualified)
+ o = SOAPElement.new(type.name)
+ o.qualified = qualified
+ type.each_element do |child_ele|
+ child = Mapping.get_attribute(obj, child_ele.name.name)
+ if child.nil?
+ if child_ele.nillable
+ # ToDo: test
+ # add empty element
+ child_soap = obj2elesoap(nil, child_ele)
+ o.add(child_soap)
+ elsif Integer(child_ele.minoccurs) == 0
+ # nothing to do
+ else
+ raise MappingError.new("nil not allowed: #{child_ele.name.name}")
+ end
+ elsif child_ele.map_as_array?
+ child.each do |item|
+ child_soap = obj2elesoap(item, child_ele)
+ o.add(child_soap)
+ end
+ else
+ child_soap = obj2elesoap(child, child_ele)
+ o.add(child_soap)
+ end
+ end
+ o
+ end
+
+ def any2soap(obj, qname)
+ if obj.is_a?(SOAPElement)
+ obj
+ elsif obj.class.class_variables.include?('@@schema_element')
+ stubobj2soap(obj, qname)
+ elsif obj.is_a?(SOAP::Mapping::Object)
+ mappingobj2soap(obj, qname)
+ elsif obj.is_a?(Hash)
+ ele = SOAPElement.from_obj(obj)
+ ele.elename = qname
+ ele
+ else
+ # expected to be a basetype or an anyType.
+ # SOAPStruct, etc. is used instead of SOAPElement.
+ begin
+ ele = Mapping.obj2soap(obj, nil, nil, MAPPING_OPT)
+ ele.elename = qname
+ ele
+ rescue MappingError
+ ele = SOAPElement.new(qname, obj.to_s)
+ end
+ if obj.respond_to?(:__xmlattr)
+ obj.__xmlattr.each do |key, value|
+ ele.extraattr[key] = value
+ end
+ end
+ ele
+ end
+ end
+
+ def stubobj2soap(obj, qname)
+ ele = SOAPElement.new(qname)
+ ele.qualified =
+ (obj.class.class_variables.include?('@@schema_qualified') and
+ obj.class.class_eval('@@schema_qualified'))
+ add_elements2soap(obj, ele)
+ add_attributes2soap(obj, ele)
+ ele
+ end
+
+ def mappingobj2soap(obj, qname)
+ ele = SOAPElement.new(qname)
+ obj.__xmlele.each do |key, value|
+ if value.is_a?(::Array)
+ value.each do |item|
+ ele.add(obj2soap(item, key))
+ end
+ else
+ ele.add(obj2soap(value, key))
+ end
+ end
+ obj.__xmlattr.each do |key, value|
+ ele.extraattr[key] = value
+ end
+ ele
+ end
+
+ def add_elements2soap(obj, ele)
+ elements, as_array = schema_element_definition(obj.class)
+ if elements
+ elements.each do |elename, type|
+ if child = Mapping.get_attribute(obj, elename.name)
+ if as_array.include?(elename.name)
+ child.each do |item|
+ ele.add(obj2soap(item, elename))
+ end
+ else
+ ele.add(obj2soap(child, elename))
+ end
+ elsif obj.is_a?(::Array) and as_array.include?(elename.name)
+ obj.each do |item|
+ ele.add(obj2soap(item, elename))
+ end
+ end
+ end
+ end
+ end
+
+ def add_attributes2soap(obj, ele)
+ attributes = schema_attribute_definition(obj.class)
+ if attributes
+ attributes.each do |qname, param|
+ attr = obj.__send__('xmlattr_' +
+ XSD::CodeGen::GenSupport.safevarname(qname.name))
+ ele.extraattr[qname] = attr
+ end
+ end
+ end
+
+ def base2soap(obj, type)
+ soap_obj = nil
+ if type <= XSD::XSDString
+ str = XSD::Charset.encoding_conv(obj.to_s,
+ Thread.current[:SOAPExternalCES], XSD::Charset.encoding)
+ soap_obj = type.new(str)
+ else
+ soap_obj = type.new(obj)
+ end
+ soap_obj
+ end
+
+ def anytype2obj(node)
+ if node.is_a?(::SOAP::SOAPBasetype)
+ return node.data
+ end
+ klass = ::SOAP::Mapping::Object
+ obj = klass.new
+ obj
+ end
+
+ def any2obj(node, obj_class = nil)
+ unless obj_class
+ typestr = XSD::CodeGen::GenSupport.safeconstname(node.elename.name)
+ obj_class = Mapping.class_from_name(typestr)
+ end
+ if obj_class and obj_class.class_variables.include?('@@schema_element')
+ soapele2stubobj(node, obj_class)
+ elsif node.is_a?(SOAPElement) or node.is_a?(SOAPStruct)
+ # SOAPArray for literal?
+ soapele2plainobj(node)
+ else
+ obj = Mapping.soap2obj(node, nil, obj_class, MAPPING_OPT)
+ add_attributes2plainobj(node, obj)
+ obj
+ end
+ end
+
+ def soapele2stubobj(node, obj_class)
+ obj = Mapping.create_empty_object(obj_class)
+ add_elements2stubobj(node, obj)
+ add_attributes2stubobj(node, obj)
+ obj
+ end
+
+ def soapele2plainobj(node)
+ obj = anytype2obj(node)
+ add_elements2plainobj(node, obj)
+ add_attributes2plainobj(node, obj)
+ obj
+ end
+
+ def add_elements2stubobj(node, obj)
+ elements, as_array = schema_element_definition(obj.class)
+ vars = {}
+ node.each do |name, value|
+ item = elements.find { |k, v| k.name == name }
+ if item
+ elename, class_name = item
+ if klass = Mapping.class_from_name(class_name)
+ # klass must be a SOAPBasetype or a class
+ if klass.ancestors.include?(::SOAP::SOAPBasetype)
+ if value.respond_to?(:data)
+ child = klass.new(value.data).data
+ else
+ child = klass.new(nil).data
+ end
+ else
+ child = any2obj(value, klass)
+ end
+ elsif klass = Mapping.module_from_name(class_name)
+ # simpletype
+ if value.respond_to?(:data)
+ child = value.data
+ else
+ raise MappingError.new(
+ "cannot map to a module value: #{class_name}")
+ end
+ else
+ raise MappingError.new("unknown class/module: #{class_name}")
+ end
+ else # untyped element is treated as anyType.
+ child = any2obj(value)
+ end
+ if as_array.include?(elename.name)
+ (vars[name] ||= []) << child
+ else
+ vars[name] = child
+ end
+ end
+ Mapping.set_attributes(obj, vars)
+ end
+
+ def add_attributes2stubobj(node, obj)
+ if attributes = schema_attribute_definition(obj.class)
+ define_xmlattr(obj)
+ attributes.each do |qname, class_name|
+ attr = node.extraattr[qname]
+ next if attr.nil? or attr.empty?
+ klass = Mapping.class_from_name(class_name)
+ if klass.ancestors.include?(::SOAP::SOAPBasetype)
+ child = klass.new(attr).data
+ else
+ child = attr
+ end
+ obj.__xmlattr[qname] = child
+ define_xmlattr_accessor(obj, qname)
+ end
+ end
+ end
+
+ def add_elements2plainobj(node, obj)
+ node.each do |name, value|
+ obj.__add_xmlele_value(value.elename, any2obj(value))
+ end
+ end
+
+ def add_attributes2plainobj(node, obj)
+ return if node.extraattr.empty?
+ define_xmlattr(obj)
+ node.extraattr.each do |qname, value|
+ obj.__xmlattr[qname] = value
+ define_xmlattr_accessor(obj, qname)
+ end
+ end
+
+ if RUBY_VERSION > "1.7.0"
+ def define_xmlattr_accessor(obj, qname)
+ name = XSD::CodeGen::GenSupport.safemethodname(qname.name)
+ Mapping.define_attr_accessor(obj, 'xmlattr_' + name,
+ proc { @__xmlattr[qname] },
+ proc { |value| @__xmlattr[qname] = value })
+ end
+ else
+ def define_xmlattr_accessor(obj, qname)
+ name = XSD::CodeGen::GenSupport.safemethodname(qname.name)
+ obj.instance_eval <<-EOS
+ def #{name}
+ @__xmlattr[#{qname.dump}]
+ end
+
+ def #{name}=(value)
+ @__xmlattr[#{qname.dump}] = value
+ end
+ EOS
+ end
+ end
+
+ if RUBY_VERSION > "1.7.0"
+ def define_xmlattr(obj)
+ obj.instance_variable_set('@__xmlattr', {})
+ unless obj.respond_to?(:__xmlattr)
+ Mapping.define_attr_accessor(obj, :__xmlattr, proc { @__xmlattr })
+ end
+ end
+ else
+ def define_xmlattr(obj)
+ obj.instance_variable_set('@__xmlattr', {})
+ unless obj.respond_to?(:__xmlattr)
+ obj.instance_eval <<-EOS
+ def __xmlattr
+ @__xmlattr
+ end
+ EOS
+ end
+ end
+ end
+
+ # it caches @@schema_element. this means that @@schema_element must not be
+ # changed while a lifetime of a WSDLLiteralRegistry.
+ def schema_element_definition(klass)
+ @schema_element_cache[klass] ||= Mapping.schema_element_definition(klass)
+ end
+
+ def schema_attribute_definition(klass)
+ @schema_attribute_cache[klass] ||= Mapping.schema_attribute_definition(klass)
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/soap/marshal.rb b/ruby_1_8_5/lib/soap/marshal.rb
new file mode 100644
index 0000000000..1c3d5b01db
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/marshal.rb
@@ -0,0 +1,59 @@
+# SOAP4R - Marshalling/Unmarshalling Ruby's object using SOAP Encoding.
+# Copyright (C) 2001, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require "soap/mapping"
+require "soap/processor"
+
+
+module SOAP
+
+
+module Marshal
+ # Trying xsd:dateTime data to be recovered as aTime.
+ MarshalMappingRegistry = Mapping::Registry.new(
+ :allow_original_mapping => true)
+ MarshalMappingRegistry.add(
+ Time,
+ ::SOAP::SOAPDateTime,
+ ::SOAP::Mapping::Registry::DateTimeFactory
+ )
+
+ class << self
+ public
+ def dump(obj, io = nil)
+ marshal(obj, MarshalMappingRegistry, io)
+ end
+
+ def load(stream)
+ unmarshal(stream, MarshalMappingRegistry)
+ end
+
+ def marshal(obj, mapping_registry = MarshalMappingRegistry, io = nil)
+ elename = Mapping.name2elename(obj.class.to_s)
+ soap_obj = Mapping.obj2soap(obj, mapping_registry)
+ body = SOAPBody.new
+ body.add(elename, soap_obj)
+ env = SOAPEnvelope.new(nil, body)
+ SOAP::Processor.marshal(env, {}, io)
+ end
+
+ def unmarshal(stream, mapping_registry = MarshalMappingRegistry)
+ env = SOAP::Processor.unmarshal(stream)
+ if env.nil?
+ raise ArgumentError.new("Illegal SOAP marshal format.")
+ end
+ Mapping.soap2obj(env.body.root_node, mapping_registry)
+ end
+ end
+end
+
+
+end
+
+
+SOAPMarshal = SOAP::Marshal
diff --git a/ruby_1_8_5/lib/soap/mimemessage.rb b/ruby_1_8_5/lib/soap/mimemessage.rb
new file mode 100644
index 0000000000..acb4322e11
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/mimemessage.rb
@@ -0,0 +1,240 @@
+# SOAP4R - MIME Message implementation.
+# Copyright (C) 2002 Jamie Herre.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'soap/attachment'
+
+
+module SOAP
+
+
+# Classes for MIME message handling. Should be put somewhere else!
+# Tried using the 'tmail' module but found that I needed something
+# lighter in weight.
+
+
+class MIMEMessage
+ class MIMEMessageError < StandardError; end
+
+ MultipartContentType = 'multipart/\w+'
+
+ class Header
+ attr_accessor :str, :key, :root
+
+ def initialize
+ @attrs = {}
+ end
+
+ def [](key)
+ @attrs[key]
+ end
+
+ def []=(key, value)
+ @attrs[key] = value
+ end
+
+ def to_s
+ @key + ": " + @str
+ end
+ end
+
+ class Headers < Hash
+ def self.parse(str)
+ new.parse(str)
+ end
+
+ def parse(str)
+ header_cache = nil
+ str.each do |line|
+ case line
+ when /^\A[^\: \t]+:\s*.+$/
+ parse_line(header_cache) if header_cache
+ header_cache = line.sub(/\r?\n\z/, '')
+ when /^\A\s+(.*)$/
+ # a continuous line at the beginning line crashes here.
+ header_cache << line
+ else
+ raise RuntimeError.new("unexpected header: #{line.inspect}")
+ end
+ end
+ parse_line(header_cache) if header_cache
+ self
+ end
+
+ def parse_line(line)
+ if /^\A([^\: \t]+):\s*(.+)\z/ =~ line
+ header = parse_rhs($2.strip)
+ header.key = $1.strip
+ self[header.key.downcase] = header
+ else
+ raise RuntimeError.new("unexpected header line: #{line.inspect}")
+ end
+ end
+
+ def parse_rhs(str)
+ a = str.split(/;+\s+/)
+ header = Header.new
+ header.str = str
+ header.root = a.shift
+ a.each do |pair|
+ if pair =~ /(\w+)\s*=\s*"?([^"]+)"?/
+ header[$1.downcase] = $2
+ else
+ raise RuntimeError.new("unexpected header component: #{pair.inspect}")
+ end
+ end
+ header
+ end
+
+ def add(key, value)
+ if key != nil and value != nil
+ header = parse_rhs(value)
+ header.key = key
+ self[key.downcase] = header
+ end
+ end
+
+ def to_s
+ self.values.collect { |hdr|
+ hdr.to_s
+ }.join("\r\n")
+ end
+ end
+
+ class Part
+ attr_accessor :headers, :body
+
+ def initialize
+ @headers = Headers.new
+ @headers.add("Content-Transfer-Encoding", "8bit")
+ @body = nil
+ @contentid = nil
+ end
+
+ def self.parse(str)
+ new.parse(str)
+ end
+
+ def parse(str)
+ headers, body = str.split(/\r\n\r\n/s)
+ if headers != nil and body != nil
+ @headers = Headers.parse(headers)
+ @body = body.sub(/\r\n\z/, '')
+ else
+ raise RuntimeError.new("unexpected part: #{str.inspect}")
+ end
+ self
+ end
+
+ def contentid
+ if @contentid == nil and @headers.key?('content-id')
+ @contentid = @headers['content-id'].str
+ @contentid = $1 if @contentid =~ /^<(.+)>$/
+ end
+ @contentid
+ end
+
+ alias content body
+
+ def to_s
+ @headers.to_s + "\r\n\r\n" + @body
+ end
+ end
+
+ def initialize
+ @parts = []
+ @headers = Headers.new
+ @root = nil
+ end
+
+ def self.parse(head, str)
+ new.parse(head, str)
+ end
+
+ attr_reader :parts, :headers
+
+ def close
+ @headers.add(
+ "Content-Type",
+ "multipart/related; type=\"text/xml\"; boundary=\"#{boundary}\"; start=\"#{@parts[0].contentid}\""
+ )
+ end
+
+ def parse(head, str)
+ @headers = Headers.parse(head + "\r\n" + "From: jfh\r\n")
+ boundary = @headers['content-type']['boundary']
+ if boundary != nil
+ parts = str.split(/--#{Regexp.quote(boundary)}\s*(?:\r\n|--\r\n)/)
+ part = parts.shift # preamble must be ignored.
+ @parts = parts.collect { |part| Part.parse(part) }
+ else
+ @parts = [Part.parse(str)]
+ end
+ if @parts.length < 1
+ raise MIMEMessageError.new("This message contains no valid parts!")
+ end
+ self
+ end
+
+ def root
+ if @root == nil
+ start = @headers['content-type']['start']
+ @root = (start && @parts.find { |prt| prt.contentid == start }) ||
+ @parts[0]
+ end
+ @root
+ end
+
+ def boundary
+ if @boundary == nil
+ @boundary = "----=Part_" + __id__.to_s + rand.to_s
+ end
+ @boundary
+ end
+
+ def add_part(content)
+ part = Part.new
+ part.headers.add("Content-Type",
+ "text/xml; charset=" + XSD::Charset.xml_encoding_label)
+ part.headers.add("Content-ID", Attachment.contentid(part))
+ part.body = content
+ @parts.unshift(part)
+ end
+
+ def add_attachment(attach)
+ part = Part.new
+ part.headers.add("Content-Type", attach.contenttype)
+ part.headers.add("Content-ID", attach.mime_contentid)
+ part.body = attach.content
+ @parts.unshift(part)
+ end
+
+ def has_parts?
+ (@parts.length > 0)
+ end
+
+ def headers_str
+ @headers.to_s
+ end
+
+ def content_str
+ str = ''
+ @parts.each do |prt|
+ str << "--" + boundary + "\r\n"
+ str << prt.to_s + "\r\n"
+ end
+ str << '--' + boundary + "--\r\n"
+ str
+ end
+
+ def to_s
+ str = headers_str + "\r\n\r\n" + conent_str
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/soap/netHttpClient.rb b/ruby_1_8_5/lib/soap/netHttpClient.rb
new file mode 100644
index 0000000000..10d68e2a4c
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/netHttpClient.rb
@@ -0,0 +1,190 @@
+# SOAP4R - net/http wrapper
+# Copyright (C) 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'net/http'
+
+
+module SOAP
+
+
+class NetHttpClient
+
+ SSLEnabled = begin
+ require 'net/https'
+ true
+ rescue LoadError
+ false
+ end
+
+ attr_reader :proxy
+ attr_accessor :no_proxy
+ attr_accessor :debug_dev
+ attr_accessor :ssl_config # ignored for now.
+ attr_accessor :protocol_version # ignored for now.
+ attr_accessor :connect_timeout
+ attr_accessor :send_timeout # ignored for now.
+ attr_accessor :receive_timeout
+
+ def initialize(proxy = nil, agent = nil)
+ @proxy = proxy ? URI.parse(proxy) : nil
+ @agent = agent
+ @debug_dev = nil
+ @session_manager = SessionManager.new
+ @no_proxy = @ssl_config = @protocol_version = nil
+ @connect_timeout = @send_timeout = @receive_timeout = nil
+ end
+
+ def test_loopback_response
+ raise NotImplementedError.new("not supported for now")
+ end
+
+ def proxy=(proxy)
+ if proxy.nil?
+ @proxy = nil
+ else
+ if proxy.is_a?(URI)
+ @proxy = proxy
+ else
+ @proxy = URI.parse(proxy)
+ end
+ if @proxy.scheme == nil or @proxy.scheme.downcase != 'http' or
+ @proxy.host == nil or @proxy.port == nil
+ raise ArgumentError.new("unsupported proxy `#{proxy}'")
+ end
+ end
+ reset_all
+ @proxy
+ end
+
+ def set_basic_auth(uri, user_id, passwd)
+ # net/http does not handle url.
+ @basic_auth = [user_id, passwd]
+ raise NotImplementedError.new("basic_auth is not supported under soap4r + net/http.")
+ end
+
+ def set_cookie_store(filename)
+ raise NotImplementedError.new
+ end
+
+ def save_cookie_store(filename)
+ raise NotImplementedError.new
+ end
+
+ def reset(url)
+ # no persistent connection. ignored.
+ end
+
+ def reset_all
+ # no persistent connection. ignored.
+ end
+
+ def post(url, req_body, header = {})
+ unless url.is_a?(URI)
+ url = URI.parse(url)
+ end
+ extra = header.dup
+ extra['User-Agent'] = @agent if @agent
+ res = start(url) { |http|
+ http.post(url.request_uri, req_body, extra)
+ }
+ Response.new(res)
+ end
+
+ def get_content(url, header = {})
+ unless url.is_a?(URI)
+ url = URI.parse(url)
+ end
+ extra = header.dup
+ extra['User-Agent'] = @agent if @agent
+ res = start(url) { |http|
+ http.get(url.request_uri, extra)
+ }
+ res.body
+ end
+
+private
+
+ def start(url)
+ http = create_connection(url)
+ response = nil
+ http.start { |worker|
+ response = yield(worker)
+ worker.finish
+ }
+ @debug_dev << response.body if @debug_dev
+ response
+ end
+
+ def create_connection(url)
+ proxy_host = proxy_port = nil
+ unless no_proxy?(url)
+ proxy_host = @proxy.host
+ proxy_port = @proxy.port
+ end
+ http = Net::HTTP::Proxy(proxy_host, proxy_port).new(url.host, url.port)
+ if http.respond_to?(:set_debug_output)
+ http.set_debug_output(@debug_dev)
+ end
+ http.open_timeout = @connect_timeout if @connect_timeout
+ http.read_timeout = @receive_timeout if @receive_timeout
+ case url
+ when URI::HTTPS
+ if SSLEnabled
+ http.use_ssl = true
+ else
+ raise RuntimeError.new("Cannot connect to #{url} (OpenSSL is not installed.)")
+ end
+ when URI::HTTP
+ # OK
+ else
+ raise RuntimeError.new("Cannot connect to #{url} (Not HTTP.)")
+ end
+ http
+ end
+
+ NO_PROXY_HOSTS = ['localhost']
+
+ def no_proxy?(uri)
+ if !@proxy or NO_PROXY_HOSTS.include?(uri.host)
+ return true
+ end
+ if @no_proxy
+ @no_proxy.scan(/([^:,]*)(?::(\d+))?/) do |host, port|
+ if /(\A|\.)#{Regexp.quote(host)}\z/i =~ uri.host &&
+ (!port || uri.port == port.to_i)
+ return true
+ end
+ end
+ else
+ false
+ end
+ end
+
+ class SessionManager
+ attr_accessor :connect_timeout
+ attr_accessor :send_timeout
+ attr_accessor :receive_timeout
+ end
+
+ class Response
+ attr_reader :content
+ attr_reader :status
+ attr_reader :reason
+ attr_reader :contenttype
+
+ def initialize(res)
+ @status = res.code.to_i
+ @reason = res.message
+ @contenttype = res['content-type']
+ @content = res.body
+ end
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/soap/parser.rb b/ruby_1_8_5/lib/soap/parser.rb
new file mode 100644
index 0000000000..412fd4855a
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/parser.rb
@@ -0,0 +1,251 @@
+# SOAP4R - SOAP XML Instance Parser library.
+# Copyright (C) 2001, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/ns'
+require 'xsd/xmlparser'
+require 'soap/soap'
+require 'soap/baseData'
+require 'soap/encodingstyle/handler'
+
+
+module SOAP
+
+
+class Parser
+ include SOAP
+
+ class ParseError < Error; end
+ class FormatDecodeError < ParseError; end
+ class UnexpectedElementError < ParseError; end
+
+private
+
+ class ParseFrame
+ attr_reader :node
+ attr_reader :name
+ attr_reader :ns, :encodingstyle
+
+ class NodeContainer
+ def initialize(node)
+ @node = node
+ end
+
+ def node
+ @node
+ end
+
+ def replace_node(node)
+ @node = node
+ end
+ end
+
+ public
+
+ def initialize(ns, name, node, encodingstyle)
+ @ns = ns
+ @name = name
+ self.node = node
+ @encodingstyle = encodingstyle
+ end
+
+ def node=(node)
+ @node = NodeContainer.new(node)
+ end
+ end
+
+public
+
+ attr_accessor :envelopenamespace
+ attr_accessor :default_encodingstyle
+ attr_accessor :decode_typemap
+ attr_accessor :allow_unqualified_element
+
+ def initialize(opt = {})
+ @opt = opt
+ @parser = XSD::XMLParser.create_parser(self, opt)
+ @parsestack = nil
+ @lastnode = nil
+ @handlers = {}
+ @envelopenamespace = opt[:envelopenamespace] || EnvelopeNamespace
+ @default_encodingstyle = opt[:default_encodingstyle] || EncodingNamespace
+ @decode_typemap = opt[:decode_typemap] || nil
+ @allow_unqualified_element = opt[:allow_unqualified_element] || false
+ end
+
+ def charset
+ @parser.charset
+ end
+
+ def parse(string_or_readable)
+ @parsestack = []
+ @lastnode = nil
+
+ @handlers.each do |uri, handler|
+ handler.decode_prologue
+ end
+
+ @parser.do_parse(string_or_readable)
+
+ unless @parsestack.empty?
+ raise FormatDecodeError.new("Unbalanced tag in XML.")
+ end
+
+ @handlers.each do |uri, handler|
+ handler.decode_epilogue
+ end
+
+ @lastnode
+ end
+
+ def start_element(name, attrs)
+ lastframe = @parsestack.last
+ ns = parent = parent_encodingstyle = nil
+ if lastframe
+ ns = lastframe.ns.clone_ns
+ parent = lastframe.node
+ parent_encodingstyle = lastframe.encodingstyle
+ else
+ ns = XSD::NS.new
+ parent = ParseFrame::NodeContainer.new(nil)
+ parent_encodingstyle = nil
+ end
+
+ attrs = XSD::XMLParser.filter_ns(ns, attrs)
+ encodingstyle = find_encodingstyle(ns, attrs)
+
+ # Children's encodingstyle is derived from its parent.
+ if encodingstyle.nil?
+ if parent.node.is_a?(SOAPHeader)
+ encodingstyle = LiteralNamespace
+ else
+ encodingstyle = parent_encodingstyle || @default_encodingstyle
+ end
+ end
+
+ node = decode_tag(ns, name, attrs, parent, encodingstyle)
+
+ @parsestack << ParseFrame.new(ns, name, node, encodingstyle)
+ end
+
+ def characters(text)
+ lastframe = @parsestack.last
+ if lastframe
+ # Need not to be cloned because character does not have attr.
+ decode_text(lastframe.ns, text, lastframe.encodingstyle)
+ else
+ # Ignore Text outside of SOAP Envelope.
+ p text if $DEBUG
+ end
+ end
+
+ def end_element(name)
+ lastframe = @parsestack.pop
+ unless name == lastframe.name
+ raise UnexpectedElementError.new("Closing element name '#{ name }' does not match with opening element '#{ lastframe.name }'.")
+ end
+ decode_tag_end(lastframe.ns, lastframe.node, lastframe.encodingstyle)
+ @lastnode = lastframe.node.node
+ end
+
+private
+
+ def find_encodingstyle(ns, attrs)
+ attrs.each do |key, value|
+ if (ns.compare(@envelopenamespace, AttrEncodingStyle, key))
+ return value
+ end
+ end
+ nil
+ end
+
+ def decode_tag(ns, name, attrs, parent, encodingstyle)
+ ele = ns.parse(name)
+
+ # Envelope based parsing.
+ if ((ele.namespace == @envelopenamespace) ||
+ (@allow_unqualified_element && ele.namespace.nil?))
+ o = decode_soap_envelope(ns, ele, attrs, parent)
+ return o if o
+ end
+
+ # Encoding based parsing.
+ handler = find_handler(encodingstyle)
+ if handler
+ return handler.decode_tag(ns, ele, attrs, parent)
+ else
+ raise FormatDecodeError.new("Unknown encodingStyle: #{ encodingstyle }.")
+ end
+ end
+
+ def decode_tag_end(ns, node, encodingstyle)
+ return unless encodingstyle
+
+ handler = find_handler(encodingstyle)
+ if handler
+ return handler.decode_tag_end(ns, node)
+ else
+ raise FormatDecodeError.new("Unknown encodingStyle: #{ encodingstyle }.")
+ end
+ end
+
+ def decode_text(ns, text, encodingstyle)
+ handler = find_handler(encodingstyle)
+
+ if handler
+ handler.decode_text(ns, text)
+ else
+ # How should I do?
+ end
+ end
+
+ def decode_soap_envelope(ns, ele, attrs, parent)
+ o = nil
+ if ele.name == EleEnvelope
+ o = SOAPEnvelope.new
+ if ext = @opt[:external_content]
+ ext.each do |k, v|
+ o.external_content[k] = v
+ end
+ end
+ elsif ele.name == EleHeader
+ unless parent.node.is_a?(SOAPEnvelope)
+ raise FormatDecodeError.new("Header should be a child of Envelope.")
+ end
+ o = SOAPHeader.new
+ parent.node.header = o
+ elsif ele.name == EleBody
+ unless parent.node.is_a?(SOAPEnvelope)
+ raise FormatDecodeError.new("Body should be a child of Envelope.")
+ end
+ o = SOAPBody.new
+ parent.node.body = o
+ elsif ele.name == EleFault
+ unless parent.node.is_a?(SOAPBody)
+ raise FormatDecodeError.new("Fault should be a child of Body.")
+ end
+ o = SOAPFault.new
+ parent.node.fault = o
+ end
+ o
+ end
+
+ def find_handler(encodingstyle)
+ unless @handlers.key?(encodingstyle)
+ handler_factory = SOAP::EncodingStyle::Handler.handler(encodingstyle) ||
+ SOAP::EncodingStyle::Handler.handler(EncodingNamespace)
+ handler = handler_factory.new(@parser.charset)
+ handler.decode_typemap = @decode_typemap
+ handler.decode_prologue
+ @handlers[encodingstyle] = handler
+ end
+ @handlers[encodingstyle]
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/soap/processor.rb b/ruby_1_8_5/lib/soap/processor.rb
new file mode 100644
index 0000000000..3c6dbedf2f
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/processor.rb
@@ -0,0 +1,66 @@
+# SOAP4R - marshal/unmarshal interface.
+# Copyright (C) 2000, 2001, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/datatypes'
+require 'soap/soap'
+require 'soap/element'
+require 'soap/parser'
+require 'soap/generator'
+require 'soap/encodingstyle/soapHandler'
+require 'soap/encodingstyle/literalHandler'
+require 'soap/encodingstyle/aspDotNetHandler'
+
+
+module SOAP
+
+
+module Processor
+ @@default_parser_option = {}
+
+ class << self
+ public
+
+ def marshal(env, opt = {}, io = nil)
+ generator = create_generator(opt)
+ marshalled_str = generator.generate(env, io)
+ unless env.external_content.empty?
+ opt[:external_content] = env.external_content
+ end
+ marshalled_str
+ end
+
+ def unmarshal(stream, opt = {})
+ parser = create_parser(opt)
+ parser.parse(stream)
+ end
+
+ def default_parser_option=(rhs)
+ @@default_parser_option = rhs
+ end
+
+ def default_parser_option
+ @@default_parser_option
+ end
+
+ private
+
+ def create_generator(opt)
+ SOAPGenerator.new(opt)
+ end
+
+ def create_parser(opt)
+ if opt.empty?
+ opt = @@default_parser_option
+ end
+ ::SOAP::Parser.new(opt)
+ end
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/soap/property.rb b/ruby_1_8_5/lib/soap/property.rb
new file mode 100644
index 0000000000..882dcc6e28
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/property.rb
@@ -0,0 +1,333 @@
+# soap/property.rb: SOAP4R - Property implementation.
+# Copyright (C) 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+module SOAP
+
+
+# Property stream format:
+#
+# line separator is \r?\n. 1 line per a property.
+# line which begins with '#' is a comment line. empty line is ignored, too.
+# key/value separator is ':' or '='.
+# '\' as escape character. but line separator cannot be escaped.
+# \s at the head/tail of key/value are trimmed.
+#
+# '[' + key + ']' indicates property section. for example,
+#
+# [aaa.bbb]
+# ccc = ddd
+# eee.fff = ggg
+# []
+# aaa.hhh = iii
+#
+# is the same as;
+#
+# aaa.bbb.ccc = ddd
+# aaa.bbb.eee.fff = ggg
+# aaa.hhh = iii
+#
+class Property
+ FrozenError = (RUBY_VERSION >= "1.9.0") ? RuntimeError : TypeError
+
+ include Enumerable
+
+ module Util
+ def const_from_name(fqname)
+ fqname.split("::").inject(Kernel) { |klass, name| klass.const_get(name) }
+ end
+ module_function :const_from_name
+
+ def require_from_name(fqname)
+ require File.join(fqname.split("::").collect { |ele| ele.downcase })
+ end
+ module_function :require_from_name
+ end
+
+ def self.load(stream)
+ new.load(stream)
+ end
+
+ def self.loadproperty(propname)
+ new.loadproperty(propname)
+ end
+
+ def initialize
+ @store = Hash.new
+ @hook = Hash.new
+ @self_hook = Array.new
+ @locked = false
+ end
+
+ KEY_REGSRC = '([^=:\\\\]*(?:\\\\.[^=:\\\\]*)*)'
+ DEF_REGSRC = '\\s*' + KEY_REGSRC + '\\s*[=:]\\s*(.*)'
+ COMMENT_REGEXP = Regexp.new('^(?:#.*|)$')
+ CATDEF_REGEXP = Regexp.new("^\\[\\s*#{KEY_REGSRC}\\s*\\]$")
+ LINE_REGEXP = Regexp.new("^#{DEF_REGSRC}$")
+ def load(stream)
+ key_prefix = ""
+ stream.each_with_index do |line, lineno|
+ line.sub!(/\r?\n\z/, '')
+ case line
+ when COMMENT_REGEXP
+ next
+ when CATDEF_REGEXP
+ key_prefix = $1.strip
+ when LINE_REGEXP
+ key, value = $1.strip, $2.strip
+ key = "#{key_prefix}.#{key}" unless key_prefix.empty?
+ key, value = loadstr(key), loadstr(value)
+ self[key] = value
+ else
+ raise TypeError.new(
+ "property format error at line #{lineno + 1}: `#{line}'")
+ end
+ end
+ self
+ end
+
+ # find property from $:.
+ def loadproperty(propname)
+ return loadpropertyfile(propname) if File.file?(propname)
+ $:.each do |path|
+ if File.file?(file = File.join(path, propname))
+ return loadpropertyfile(file)
+ end
+ end
+ nil
+ end
+
+ # name: a Symbol, String or an Array
+ def [](name)
+ referent(name_to_a(name))
+ end
+
+ # name: a Symbol, String or an Array
+ # value: an Object
+ def []=(name, value)
+ name_pair = name_to_a(name).freeze
+ hooks = assign(name_pair, value)
+ hooks.each do |hook|
+ hook.call(name_pair, value)
+ end
+ value
+ end
+
+ # value: an Object
+ # key is generated by property
+ def <<(value)
+ self[generate_new_key] = value
+ end
+
+ # name: a Symbol, String or an Array; nil means hook to the root
+ # cascade: true/false; for cascading hook of sub key
+ # hook: block which will be called with 2 args, name and value
+ def add_hook(name = nil, cascade = false, &hook)
+ if name == nil or name == true or name == false
+ cascade = name
+ assign_self_hook(cascade, &hook)
+ else
+ assign_hook(name_to_a(name), cascade, &hook)
+ end
+ end
+
+ def each
+ @store.each do |key, value|
+ yield(key, value)
+ end
+ end
+
+ def empty?
+ @store.empty?
+ end
+
+ def keys
+ @store.keys
+ end
+
+ def values
+ @store.values
+ end
+
+ def lock(cascade = false)
+ if cascade
+ each_key do |key|
+ key.lock(cascade)
+ end
+ end
+ @locked = true
+ self
+ end
+
+ def unlock(cascade = false)
+ @locked = false
+ if cascade
+ each_key do |key|
+ key.unlock(cascade)
+ end
+ end
+ self
+ end
+
+ def locked?
+ @locked
+ end
+
+protected
+
+ def deref_key(key)
+ check_lock(key)
+ ref = @store[key] ||= self.class.new
+ unless propkey?(ref)
+ raise ArgumentError.new("key `#{key}' already defined as a value")
+ end
+ ref
+ end
+
+ def local_referent(key)
+ check_lock(key)
+ if propkey?(@store[key]) and @store[key].locked?
+ raise FrozenError.new("cannot split any key from locked property")
+ end
+ @store[key]
+ end
+
+ def local_assign(key, value)
+ check_lock(key)
+ if @locked
+ if propkey?(value)
+ raise FrozenError.new("cannot add any key to locked property")
+ elsif propkey?(@store[key])
+ raise FrozenError.new("cannot override any key in locked property")
+ end
+ end
+ @store[key] = value
+ end
+
+ def local_hook(key, direct)
+ hooks = []
+ (@self_hook + (@hook[key] || NO_HOOK)).each do |hook, cascade|
+ hooks << hook if direct or cascade
+ end
+ hooks
+ end
+
+ def local_assign_hook(key, cascade, &hook)
+ check_lock(key)
+ @store[key] ||= nil
+ (@hook[key] ||= []) << [hook, cascade]
+ end
+
+private
+
+ NO_HOOK = [].freeze
+
+ def referent(ary)
+ ary[0..-2].inject(self) { |ref, name|
+ ref.deref_key(to_key(name))
+ }.local_referent(to_key(ary.last))
+ end
+
+ def assign(ary, value)
+ ref = self
+ hook = NO_HOOK
+ ary[0..-2].each do |name|
+ key = to_key(name)
+ hook += ref.local_hook(key, false)
+ ref = ref.deref_key(key)
+ end
+ last_key = to_key(ary.last)
+ ref.local_assign(last_key, value)
+ hook + ref.local_hook(last_key, true)
+ end
+
+ def assign_hook(ary, cascade, &hook)
+ ary[0..-2].inject(self) { |ref, name|
+ ref.deref_key(to_key(name))
+ }.local_assign_hook(to_key(ary.last), cascade, &hook)
+ end
+
+ def assign_self_hook(cascade, &hook)
+ check_lock(nil)
+ @self_hook << [hook, cascade]
+ end
+
+ def each_key
+ self.each do |key, value|
+ if propkey?(value)
+ yield(value)
+ end
+ end
+ end
+
+ def check_lock(key)
+ if @locked and (key.nil? or !@store.key?(key))
+ raise FrozenError.new("cannot add any key to locked property")
+ end
+ end
+
+ def propkey?(value)
+ value.is_a?(::SOAP::Property)
+ end
+
+ def name_to_a(name)
+ case name
+ when Symbol
+ [name]
+ when String
+ name.scan(/[^.\\]+(?:\\.[^.\\])*/) # split with unescaped '.'
+ when Array
+ name
+ else
+ raise ArgumentError.new("Unknown name #{name}(#{name.class})")
+ end
+ end
+
+ def to_key(name)
+ name.to_s.downcase
+ end
+
+ def generate_new_key
+ if @store.empty?
+ "0"
+ else
+ (key_max + 1).to_s
+ end
+ end
+
+ def key_max
+ (@store.keys.max { |l, r| l.to_s.to_i <=> r.to_s.to_i }).to_s.to_i
+ end
+
+ def loadpropertyfile(file)
+ puts "find property at #{file}" if $DEBUG
+ File.open(file) do |f|
+ load(f)
+ end
+ end
+
+ def loadstr(str)
+ str.gsub(/\\./) { |c| eval("\"#{c}\"") }
+ end
+end
+
+
+end
+
+
+# for ruby/1.6.
+unless Enumerable.instance_methods.include?('inject')
+ module Enumerable
+ def inject(init)
+ result = init
+ each do |item|
+ result = yield(result, item)
+ end
+ result
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/soap/rpc/cgistub.rb b/ruby_1_8_5/lib/soap/rpc/cgistub.rb
new file mode 100644
index 0000000000..487f05a9bf
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/rpc/cgistub.rb
@@ -0,0 +1,206 @@
+# SOAP4R - CGI/mod_ruby stub library
+# Copyright (C) 2001, 2003-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'soap/streamHandler'
+require 'webrick/httpresponse'
+require 'webrick/httpstatus'
+require 'logger'
+require 'soap/rpc/soaplet'
+
+
+module SOAP
+module RPC
+
+
+###
+# SYNOPSIS
+# CGIStub.new
+#
+# DESCRIPTION
+# To be written...
+#
+class CGIStub < Logger::Application
+ include SOAP
+ include WEBrick
+
+ class SOAPRequest
+ attr_reader :body
+
+ def [](var); end
+
+ def meta_vars; end
+ end
+
+ class SOAPStdinRequest < SOAPRequest
+ attr_reader :body
+
+ def initialize(stream)
+ size = ENV['CONTENT_LENGTH'].to_i || 0
+ @body = stream.read(size)
+ end
+
+ def [](var)
+ ENV[var.gsub(/-/, '_').upcase]
+ end
+
+ def meta_vars
+ {
+ 'HTTP_SOAPACTION' => ENV['HTTP_SOAPAction']
+ }
+ end
+ end
+
+ class SOAPFCGIRequest < SOAPRequest
+ attr_reader :body
+
+ def initialize(request)
+ @request = request
+ @body = @request.in.read
+ end
+
+ def [](var)
+ @request.env[var.gsub(/-/, '_').upcase]
+ end
+
+ def meta_vars
+ {
+ 'HTTP_SOAPACTION' => @request.env['HTTP_SOAPAction']
+ }
+ end
+ end
+
+ def initialize(appname, default_namespace)
+ super(appname)
+ set_log(STDERR)
+ self.level = ERROR
+ @default_namespace = default_namespace
+ @remote_host = ENV['REMOTE_HOST'] || ENV['REMOTE_ADDR'] || 'unknown'
+ @router = ::SOAP::RPC::Router.new(self.class.name)
+ @soaplet = ::SOAP::RPC::SOAPlet.new(@router)
+ on_init
+ end
+
+ def on_init
+ # do extra initialization in a derived class if needed.
+ end
+
+ def mapping_registry
+ @router.mapping_registry
+ end
+
+ def mapping_registry=(value)
+ @router.mapping_registry = value
+ end
+
+ def generate_explicit_type
+ @router.generate_explicit_type
+ end
+
+ def generate_explicit_type=(generate_explicit_type)
+ @router.generate_explicit_type = generate_explicit_type
+ end
+
+ # servant entry interface
+
+ def add_rpc_servant(obj, namespace = @default_namespace)
+ @router.add_rpc_servant(obj, namespace)
+ end
+ alias add_servant add_rpc_servant
+
+ def add_headerhandler(obj)
+ @router.add_headerhandler(obj)
+ end
+ alias add_rpc_headerhandler add_headerhandler
+
+ # method entry interface
+
+ def add_rpc_method(obj, name, *param)
+ add_rpc_method_with_namespace_as(@default_namespace, obj, name, name, *param)
+ end
+ alias add_method add_rpc_method
+
+ def add_rpc_method_as(obj, name, name_as, *param)
+ add_rpc_method_with_namespace_as(@default_namespace, obj, name, name_as, *param)
+ end
+ alias add_method_as add_rpc_method_as
+
+ def add_rpc_method_with_namespace(namespace, obj, name, *param)
+ add_rpc_method_with_namespace_as(namespace, obj, name, name, *param)
+ end
+ alias add_method_with_namespace add_rpc_method_with_namespace
+
+ def add_rpc_method_with_namespace_as(namespace, obj, name, name_as, *param)
+ qname = XSD::QName.new(namespace, name_as)
+ soapaction = nil
+ param_def = SOAPMethod.derive_rpc_param_def(obj, name, *param)
+ @router.add_rpc_operation(obj, qname, soapaction, name, param_def)
+ end
+ alias add_method_with_namespace_as add_rpc_method_with_namespace_as
+
+ def add_rpc_operation(receiver, qname, soapaction, name, param_def, opt = {})
+ @router.add_rpc_operation(receiver, qname, soapaction, name, param_def, opt)
+ end
+
+ def add_document_operation(receiver, soapaction, name, param_def, opt = {})
+ @router.add_document_operation(receiver, soapaction, name, param_def, opt)
+ end
+
+ def set_fcgi_request(request)
+ @fcgi = request
+ end
+
+private
+
+ HTTPVersion = WEBrick::HTTPVersion.new('1.0') # dummy; ignored
+
+ def run
+ res = WEBrick::HTTPResponse.new({:HTTPVersion => HTTPVersion})
+ begin
+ @log.info { "received a request from '#{ @remote_host }'" }
+ if @fcgi
+ req = SOAPFCGIRequest.new(@fcgi)
+ else
+ req = SOAPStdinRequest.new($stdin)
+ end
+ @soaplet.do_POST(req, res)
+ rescue HTTPStatus::EOFError, HTTPStatus::RequestTimeout => ex
+ res.set_error(ex)
+ rescue HTTPStatus::Error => ex
+ res.set_error(ex)
+ rescue HTTPStatus::Status => ex
+ res.status = ex.code
+ rescue StandardError, NameError => ex # for Ruby 1.6
+ res.set_error(ex, true)
+ ensure
+ if defined?(MOD_RUBY)
+ r = Apache.request
+ r.status = res.status
+ r.content_type = res.content_type
+ r.send_http_header
+ buf = res.body
+ else
+ buf = ''
+ res.send_response(buf)
+ buf.sub!(/^[^\r]+\r\n/, '') # Trim status line.
+ end
+ @log.debug { "SOAP CGI Response:\n#{ buf }" }
+ if @fcgi
+ @fcgi.out.print buf
+ @fcgi.finish
+ @fcgi = nil
+ else
+ print buf
+ end
+ end
+ 0
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/soap/rpc/driver.rb b/ruby_1_8_5/lib/soap/rpc/driver.rb
new file mode 100644
index 0000000000..0fb4e82488
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/rpc/driver.rb
@@ -0,0 +1,254 @@
+# SOAP4R - SOAP RPC driver
+# Copyright (C) 2000, 2001, 2003-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'soap/soap'
+require 'soap/mapping'
+require 'soap/mapping/wsdlliteralregistry'
+require 'soap/rpc/rpc'
+require 'soap/rpc/proxy'
+require 'soap/rpc/element'
+require 'soap/streamHandler'
+require 'soap/property'
+require 'soap/header/handlerset'
+
+
+module SOAP
+module RPC
+
+
+class Driver
+ class << self
+ if RUBY_VERSION >= "1.7.0"
+ def __attr_proxy(symbol, assignable = false)
+ name = symbol.to_s
+ define_method(name) {
+ @proxy.__send__(name)
+ }
+ if assignable
+ aname = name + '='
+ define_method(aname) { |rhs|
+ @proxy.__send__(aname, rhs)
+ }
+ end
+ end
+ else
+ def __attr_proxy(symbol, assignable = false)
+ name = symbol.to_s
+ module_eval <<-EOS
+ def #{name}
+ @proxy.#{name}
+ end
+ EOS
+ if assignable
+ module_eval <<-EOS
+ def #{name}=(value)
+ @proxy.#{name} = value
+ end
+ EOS
+ end
+ end
+ end
+ end
+
+ __attr_proxy :endpoint_url, true
+ __attr_proxy :mapping_registry, true
+ __attr_proxy :default_encodingstyle, true
+ __attr_proxy :generate_explicit_type, true
+ __attr_proxy :allow_unqualified_element, true
+ __attr_proxy :headerhandler
+ __attr_proxy :streamhandler
+ __attr_proxy :test_loopback_response
+ __attr_proxy :reset_stream
+
+ attr_reader :proxy
+ attr_reader :options
+ attr_accessor :soapaction
+
+ def inspect
+ "#<#{self.class}:#{@proxy.inspect}>"
+ end
+
+ def httpproxy
+ options["protocol.http.proxy"]
+ end
+
+ def httpproxy=(httpproxy)
+ options["protocol.http.proxy"] = httpproxy
+ end
+
+ def wiredump_dev
+ options["protocol.http.wiredump_dev"]
+ end
+
+ def wiredump_dev=(wiredump_dev)
+ options["protocol.http.wiredump_dev"] = wiredump_dev
+ end
+
+ def mandatorycharset
+ options["protocol.mandatorycharset"]
+ end
+
+ def mandatorycharset=(mandatorycharset)
+ options["protocol.mandatorycharset"] = mandatorycharset
+ end
+
+ def wiredump_file_base
+ options["protocol.wiredump_file_base"]
+ end
+
+ def wiredump_file_base=(wiredump_file_base)
+ options["protocol.wiredump_file_base"] = wiredump_file_base
+ end
+
+ def initialize(endpoint_url, namespace = nil, soapaction = nil)
+ @namespace = namespace
+ @soapaction = soapaction
+ @options = setup_options
+ @wiredump_file_base = nil
+ @proxy = Proxy.new(endpoint_url, @soapaction, @options)
+ end
+
+ def loadproperty(propertyname)
+ unless options.loadproperty(propertyname)
+ raise LoadError.new("No such property to load -- #{propertyname}")
+ end
+ end
+
+ def add_rpc_method(name, *params)
+ add_rpc_method_with_soapaction_as(name, name, @soapaction, *params)
+ end
+
+ def add_rpc_method_as(name, name_as, *params)
+ add_rpc_method_with_soapaction_as(name, name_as, @soapaction, *params)
+ end
+
+ def add_rpc_method_with_soapaction(name, soapaction, *params)
+ add_rpc_method_with_soapaction_as(name, name, soapaction, *params)
+ end
+
+ def add_rpc_method_with_soapaction_as(name, name_as, soapaction, *params)
+ param_def = SOAPMethod.create_rpc_param_def(params)
+ qname = XSD::QName.new(@namespace, name_as)
+ @proxy.add_rpc_method(qname, soapaction, name, param_def)
+ add_rpc_method_interface(name, param_def)
+ end
+
+ # add_method is for shortcut of typical rpc/encoded method definition.
+ alias add_method add_rpc_method
+ alias add_method_as add_rpc_method_as
+ alias add_method_with_soapaction add_rpc_method_with_soapaction
+ alias add_method_with_soapaction_as add_rpc_method_with_soapaction_as
+
+ def add_document_method(name, soapaction, req_qname, res_qname)
+ param_def = SOAPMethod.create_doc_param_def(req_qname, res_qname)
+ @proxy.add_document_method(soapaction, name, param_def)
+ add_document_method_interface(name, param_def)
+ end
+
+ def add_rpc_operation(qname, soapaction, name, param_def, opt = {})
+ @proxy.add_rpc_operation(qname, soapaction, name, param_def, opt)
+ add_rpc_method_interface(name, param_def)
+ end
+
+ def add_document_operation(soapaction, name, param_def, opt = {})
+ @proxy.add_document_operation(soapaction, name, param_def, opt)
+ add_document_method_interface(name, param_def)
+ end
+
+ def invoke(headers, body)
+ if headers and !headers.is_a?(SOAPHeader)
+ headers = create_header(headers)
+ end
+ set_wiredump_file_base(body.elename.name)
+ env = @proxy.invoke(headers, body)
+ if env.nil?
+ return nil, nil
+ else
+ return env.header, env.body
+ end
+ end
+
+ def call(name, *params)
+ set_wiredump_file_base(name)
+ @proxy.call(name, *params)
+ end
+
+private
+
+ def set_wiredump_file_base(name)
+ if @wiredump_file_base
+ @proxy.set_wiredump_file_base("#{@wiredump_file_base}_#{name}")
+ end
+ end
+
+ def create_header(headers)
+ header = SOAPHeader.new()
+ headers.each do |content, mustunderstand, encodingstyle|
+ header.add(SOAPHeaderItem.new(content, mustunderstand, encodingstyle))
+ end
+ header
+ end
+
+ def setup_options
+ if opt = Property.loadproperty(::SOAP::PropertyName)
+ opt = opt["client"]
+ end
+ opt ||= Property.new
+ opt.add_hook("protocol.mandatorycharset") do |key, value|
+ @proxy.mandatorycharset = value
+ end
+ opt.add_hook("protocol.wiredump_file_base") do |key, value|
+ @wiredump_file_base = value
+ end
+ opt["protocol.http.charset"] ||= XSD::Charset.xml_encoding_label
+ opt["protocol.http.proxy"] ||= Env::HTTP_PROXY
+ opt["protocol.http.no_proxy"] ||= Env::NO_PROXY
+ opt
+ end
+
+ def add_rpc_method_interface(name, param_def)
+ param_count = RPC::SOAPMethod.param_count(param_def,
+ RPC::SOAPMethod::IN, RPC::SOAPMethod::INOUT)
+ add_method_interface(name, param_count)
+ end
+
+ def add_document_method_interface(name, param_def)
+ param_count = RPC::SOAPMethod.param_count(param_def, RPC::SOAPMethod::IN)
+ add_method_interface(name, param_count)
+ end
+
+ if RUBY_VERSION > "1.7.0"
+ def add_method_interface(name, param_count)
+ ::SOAP::Mapping.define_singleton_method(self, name) do |*arg|
+ unless arg.size == param_count
+ raise ArgumentError.new(
+ "wrong number of arguments (#{arg.size} for #{param_count})")
+ end
+ call(name, *arg)
+ end
+ self.method(name)
+ end
+ else
+ def add_method_interface(name, param_count)
+ instance_eval <<-EOS
+ def #{name}(*arg)
+ unless arg.size == #{param_count}
+ raise ArgumentError.new(
+ "wrong number of arguments (\#{arg.size} for #{param_count})")
+ end
+ call(#{name.dump}, *arg)
+ end
+ EOS
+ self.method(name)
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/soap/rpc/element.rb b/ruby_1_8_5/lib/soap/rpc/element.rb
new file mode 100644
index 0000000000..c224b03d0d
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/rpc/element.rb
@@ -0,0 +1,325 @@
+# SOAP4R - RPC element definition.
+# Copyright (C) 2000, 2001, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'soap/baseData'
+
+
+module SOAP
+
+# Add method definitions for RPC to common definition in element.rb
+class SOAPBody < SOAPStruct
+ public
+
+ def request
+ root_node
+ end
+
+ def response
+ root = root_node
+ if !@is_fault
+ if root.nil?
+ nil
+ elsif root.is_a?(SOAPBasetype)
+ root
+ else
+ # Initial element is [retval].
+ root[0]
+ end
+ else
+ root
+ end
+ end
+
+ def outparams
+ root = root_node
+ if !@is_fault and !root.nil? and !root.is_a?(SOAPBasetype)
+ op = root[1..-1]
+ op = nil if op && op.empty?
+ op
+ else
+ nil
+ end
+ end
+
+ def fault
+ if @is_fault
+ self['fault']
+ else
+ nil
+ end
+ end
+
+ def fault=(fault)
+ @is_fault = true
+ add_member('fault', fault)
+ end
+end
+
+
+module RPC
+
+
+class RPCError < Error; end
+class MethodDefinitionError < RPCError; end
+class ParameterError < RPCError; end
+
+class SOAPMethod < SOAPStruct
+ RETVAL = 'retval'
+ IN = 'in'
+ OUT = 'out'
+ INOUT = 'inout'
+
+ attr_reader :param_def
+ attr_reader :inparam
+ attr_reader :outparam
+ attr_reader :retval_name
+ attr_reader :retval_class_name
+
+ def initialize(qname, param_def = nil)
+ super(nil)
+ @elename = qname
+ @encodingstyle = nil
+
+ @param_def = param_def
+
+ @signature = []
+ @inparam_names = []
+ @inoutparam_names = []
+ @outparam_names = []
+
+ @inparam = {}
+ @outparam = {}
+ @retval_name = nil
+ @retval_class_name = nil
+
+ init_param(@param_def) if @param_def
+ end
+
+ def have_outparam?
+ @outparam_names.size > 0
+ end
+
+ def input_params
+ collect_params(IN, INOUT)
+ end
+
+ def output_params
+ collect_params(OUT, INOUT)
+ end
+
+ def set_param(params)
+ params.each do |param, data|
+ @inparam[param] = data
+ data.elename.name = param
+ data.parent = self
+ end
+ end
+
+ def set_outparam(params)
+ params.each do |param, data|
+ @outparam[param] = data
+ data.elename.name = param
+ end
+ end
+
+ def SOAPMethod.param_count(param_def, *type)
+ count = 0
+ param_def.each do |io_type, name, param_type|
+ if type.include?(io_type)
+ count += 1
+ end
+ end
+ count
+ end
+
+ def SOAPMethod.derive_rpc_param_def(obj, name, *param)
+ if param.size == 1 and param[0].is_a?(Array)
+ return param[0]
+ end
+ if param.empty?
+ method = obj.method(name)
+ param_names = (1..method.arity.abs).collect { |i| "p#{i}" }
+ else
+ param_names = param
+ end
+ create_rpc_param_def(param_names)
+ end
+
+ def SOAPMethod.create_rpc_param_def(param_names)
+ param_def = []
+ param_names.each do |param_name|
+ param_def.push([IN, param_name, nil])
+ end
+ param_def.push([RETVAL, 'return', nil])
+ param_def
+ end
+
+ def SOAPMethod.create_doc_param_def(req_qnames, res_qnames)
+ req_qnames = [req_qnames] if req_qnames.is_a?(XSD::QName)
+ res_qnames = [res_qnames] if res_qnames.is_a?(XSD::QName)
+ param_def = []
+ req_qnames.each do |qname|
+ param_def << [IN, qname.name, [nil, qname.namespace, qname.name]]
+ end
+ res_qnames.each do |qname|
+ param_def << [OUT, qname.name, [nil, qname.namespace, qname.name]]
+ end
+ param_def
+ end
+
+private
+
+ def collect_params(*type)
+ names = []
+ @signature.each do |io_type, name, param_type|
+ names << name if type.include?(io_type)
+ end
+ names
+ end
+
+ def init_param(param_def)
+ param_def.each do |io_type, name, param_type|
+ case io_type
+ when IN
+ @signature.push([IN, name, param_type])
+ @inparam_names.push(name)
+ when OUT
+ @signature.push([OUT, name, param_type])
+ @outparam_names.push(name)
+ when INOUT
+ @signature.push([INOUT, name, param_type])
+ @inoutparam_names.push(name)
+ when RETVAL
+ if @retval_name
+ raise MethodDefinitionError.new('duplicated retval')
+ end
+ @retval_name = name
+ @retval_class_name = nil
+ if param_type
+ if param_type[0].is_a?(String)
+ @retval_class_name = Mapping.class_from_name(param_type[0])
+ else
+ @retval_class_name = param_type[0]
+ end
+ end
+ else
+ raise MethodDefinitionError.new("unknown type: #{io_type}")
+ end
+ end
+ end
+end
+
+
+class SOAPMethodRequest < SOAPMethod
+ attr_accessor :soapaction
+
+ def SOAPMethodRequest.create_request(qname, *params)
+ param_def = []
+ param_value = []
+ i = 0
+ params.each do |param|
+ param_name = "p#{i}"
+ i += 1
+ param_def << [IN, param_name, nil]
+ param_value << [param_name, param]
+ end
+ param_def << [RETVAL, 'return', nil]
+ o = new(qname, param_def)
+ o.set_param(param_value)
+ o
+ end
+
+ def initialize(qname, param_def = nil, soapaction = nil)
+ check_elename(qname)
+ super(qname, param_def)
+ @soapaction = soapaction
+ end
+
+ def each
+ input_params.each do |name|
+ unless @inparam[name]
+ raise ParameterError.new("parameter: #{name} was not given")
+ end
+ yield(name, @inparam[name])
+ end
+ end
+
+ def dup
+ req = self.class.new(@elename.dup, @param_def, @soapaction)
+ req.encodingstyle = @encodingstyle
+ req
+ end
+
+ def create_method_response(response_name = nil)
+ response_name ||=
+ XSD::QName.new(@elename.namespace, @elename.name + 'Response')
+ SOAPMethodResponse.new(response_name, @param_def)
+ end
+
+private
+
+ def check_elename(qname)
+ # NCName & ruby's method name
+ unless /\A[\w_][\w\d_\-]*\z/ =~ qname.name
+ raise MethodDefinitionError.new("element name '#{qname.name}' not allowed")
+ end
+ end
+end
+
+
+class SOAPMethodResponse < SOAPMethod
+
+ def initialize(qname, param_def = nil)
+ super(qname, param_def)
+ @retval = nil
+ end
+
+ def retval=(retval)
+ @retval = retval
+ @retval.elename = @retval.elename.dup_name(@retval_name || 'return')
+ retval.parent = self
+ retval
+ end
+
+ def each
+ if @retval_name and !@retval.is_a?(SOAPVoid)
+ yield(@retval_name, @retval)
+ end
+
+ output_params.each do |name|
+ unless @outparam[name]
+ raise ParameterError.new("parameter: #{name} was not given")
+ end
+ yield(name, @outparam[name])
+ end
+ end
+end
+
+
+# To return(?) void explicitly.
+# def foo(input_var)
+# ...
+# return SOAP::RPC::SOAPVoid.new
+# end
+class SOAPVoid < XSD::XSDAnySimpleType
+ include SOAPBasetype
+ extend SOAPModuleUtils
+ Name = XSD::QName.new(Mapping::RubyCustomTypeNamespace, nil)
+
+public
+ def initialize()
+ @elename = Name
+ @id = nil
+ @precedents = []
+ @parent = nil
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/soap/rpc/httpserver.rb b/ruby_1_8_5/lib/soap/rpc/httpserver.rb
new file mode 100644
index 0000000000..6d2a72ebe3
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/rpc/httpserver.rb
@@ -0,0 +1,129 @@
+# SOAP4R - WEBrick HTTP Server
+# Copyright (C) 2003, 2004 by NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'logger'
+require 'soap/rpc/soaplet'
+require 'soap/streamHandler'
+require 'webrick'
+
+
+module SOAP
+module RPC
+
+
+class HTTPServer < Logger::Application
+ attr_reader :server
+ attr_accessor :default_namespace
+
+ def initialize(config)
+ super(config[:SOAPHTTPServerApplicationName] || self.class.name)
+ @default_namespace = config[:SOAPDefaultNamespace]
+ @webrick_config = config.dup
+ self.level = Logger::Severity::ERROR # keep silent by default
+ @webrick_config[:Logger] ||= @log
+ @log = @webrick_config[:Logger] # sync logger of App and HTTPServer
+ @router = ::SOAP::RPC::Router.new(self.class.name)
+ @soaplet = ::SOAP::RPC::SOAPlet.new(@router)
+ on_init
+ @server = WEBrick::HTTPServer.new(@webrick_config)
+ @server.mount('/', @soaplet)
+ end
+
+ def on_init
+ # do extra initialization in a derived class if needed.
+ end
+
+ def status
+ @server.status if @server
+ end
+
+ def shutdown
+ @server.shutdown if @server
+ end
+
+ def mapping_registry
+ @router.mapping_registry
+ end
+
+ def mapping_registry=(mapping_registry)
+ @router.mapping_registry = mapping_registry
+ end
+
+ def generate_explicit_type
+ @router.generate_explicit_type
+ end
+
+ def generate_explicit_type=(generate_explicit_type)
+ @router.generate_explicit_type = generate_explicit_type
+ end
+
+ # servant entry interface
+
+ def add_rpc_request_servant(factory, namespace = @default_namespace)
+ @router.add_rpc_request_servant(factory, namespace)
+ end
+
+ def add_rpc_servant(obj, namespace = @default_namespace)
+ @router.add_rpc_servant(obj, namespace)
+ end
+
+ def add_request_headerhandler(factory)
+ @router.add_request_headerhandler(factory)
+ end
+
+ def add_headerhandler(obj)
+ @router.add_headerhandler(obj)
+ end
+ alias add_rpc_headerhandler add_headerhandler
+
+ # method entry interface
+
+ def add_rpc_method(obj, name, *param)
+ add_rpc_method_as(obj, name, name, *param)
+ end
+ alias add_method add_rpc_method
+
+ def add_rpc_method_as(obj, name, name_as, *param)
+ qname = XSD::QName.new(@default_namespace, name_as)
+ soapaction = nil
+ param_def = SOAPMethod.derive_rpc_param_def(obj, name, *param)
+ @router.add_rpc_operation(obj, qname, soapaction, name, param_def)
+ end
+ alias add_method_as add_rpc_method_as
+
+ def add_document_method(obj, soapaction, name, req_qnames, res_qnames)
+ param_def = SOAPMethod.create_doc_param_def(req_qnames, res_qnames)
+ @router.add_document_operation(obj, soapaction, name, param_def)
+ end
+
+ def add_rpc_operation(receiver, qname, soapaction, name, param_def, opt = {})
+ @router.add_rpc_operation(receiver, qname, soapaction, name, param_def, opt)
+ end
+
+ def add_rpc_request_operation(factory, qname, soapaction, name, param_def, opt = {})
+ @router.add_rpc_request_operation(factory, qname, soapaction, name, param_def, opt)
+ end
+
+ def add_document_operation(receiver, soapaction, name, param_def, opt = {})
+ @router.add_document_operation(receiver, soapaction, name, param_def, opt)
+ end
+
+ def add_document_request_operation(factory, soapaction, name, param_def, opt = {})
+ @router.add_document_request_operation(factory, soapaction, name, param_def, opt)
+ end
+
+private
+
+ def run
+ @server.start
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/soap/rpc/proxy.rb b/ruby_1_8_5/lib/soap/rpc/proxy.rb
new file mode 100644
index 0000000000..7dfda62006
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/rpc/proxy.rb
@@ -0,0 +1,497 @@
+# SOAP4R - RPC Proxy library.
+# Copyright (C) 2000, 2003-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'soap/soap'
+require 'soap/processor'
+require 'soap/mapping'
+require 'soap/rpc/rpc'
+require 'soap/rpc/element'
+require 'soap/streamHandler'
+require 'soap/mimemessage'
+
+
+module SOAP
+module RPC
+
+
+class Proxy
+ include SOAP
+
+public
+
+ attr_accessor :soapaction
+ attr_accessor :mandatorycharset
+ attr_accessor :allow_unqualified_element
+ attr_accessor :default_encodingstyle
+ attr_accessor :generate_explicit_type
+ attr_reader :headerhandler
+ attr_reader :streamhandler
+
+ attr_accessor :mapping_registry
+ attr_accessor :literal_mapping_registry
+
+ attr_reader :operation
+
+ def initialize(endpoint_url, soapaction, options)
+ @endpoint_url = endpoint_url
+ @soapaction = soapaction
+ @options = options
+ @streamhandler = HTTPStreamHandler.new(
+ @options["protocol.http"] ||= ::SOAP::Property.new)
+ @operation = {}
+ @mandatorycharset = nil
+ @allow_unqualified_element = true
+ @default_encodingstyle = nil
+ @generate_explicit_type = true
+ @headerhandler = Header::HandlerSet.new
+ @mapping_registry = nil
+ @literal_mapping_registry = ::SOAP::Mapping::WSDLLiteralRegistry.new
+ end
+
+ def inspect
+ "#<#{self.class}:#{@endpoint_url}>"
+ end
+
+ def endpoint_url
+ @endpoint_url
+ end
+
+ def endpoint_url=(endpoint_url)
+ @endpoint_url = endpoint_url
+ reset_stream
+ end
+
+ def reset_stream
+ @streamhandler.reset(@endpoint_url)
+ end
+
+ def set_wiredump_file_base(wiredump_file_base)
+ @streamhandler.wiredump_file_base = wiredump_file_base
+ end
+
+ def test_loopback_response
+ @streamhandler.test_loopback_response
+ end
+
+ def add_rpc_operation(qname, soapaction, name, param_def, opt = {})
+ opt[:request_qname] = qname
+ opt[:request_style] ||= :rpc
+ opt[:response_style] ||= :rpc
+ opt[:request_use] ||= :encoded
+ opt[:response_use] ||= :encoded
+ @operation[name] = Operation.new(soapaction, param_def, opt)
+ end
+
+ def add_document_operation(soapaction, name, param_def, opt = {})
+ opt[:request_style] ||= :document
+ opt[:response_style] ||= :document
+ opt[:request_use] ||= :literal
+ opt[:response_use] ||= :literal
+ # default values of these values are unqualified in XML Schema.
+ # set true for backward compatibility.
+ unless opt.key?(:elementformdefault)
+ opt[:elementformdefault] = true
+ end
+ unless opt.key?(:attributeformdefault)
+ opt[:attributeformdefault] = true
+ end
+ @operation[name] = Operation.new(soapaction, param_def, opt)
+ end
+
+ # add_method is for shortcut of typical rpc/encoded method definition.
+ alias add_method add_rpc_operation
+ alias add_rpc_method add_rpc_operation
+ alias add_document_method add_document_operation
+
+ def invoke(req_header, req_body, opt = nil)
+ opt ||= create_encoding_opt
+ route(req_header, req_body, opt, opt)
+ end
+
+ def call(name, *params)
+ unless op_info = @operation[name]
+ raise MethodDefinitionError, "method: #{name} not defined"
+ end
+ mapping_opt = create_mapping_opt
+ req_header = create_request_header
+ req_body = SOAPBody.new(
+ op_info.request_body(params, @mapping_registry,
+ @literal_mapping_registry, mapping_opt)
+ )
+ reqopt = create_encoding_opt(
+ :soapaction => op_info.soapaction || @soapaction,
+ :envelopenamespace => @options["soap.envelope.requestnamespace"],
+ :default_encodingstyle =>
+ @default_encodingstyle || op_info.request_default_encodingstyle,
+ :elementformdefault => op_info.elementformdefault,
+ :attributeformdefault => op_info.attributeformdefault
+ )
+ resopt = create_encoding_opt(
+ :envelopenamespace => @options["soap.envelope.responsenamespace"],
+ :default_encodingstyle =>
+ @default_encodingstyle || op_info.response_default_encodingstyle,
+ :elementformdefault => op_info.elementformdefault,
+ :attributeformdefault => op_info.attributeformdefault
+ )
+ env = route(req_header, req_body, reqopt, resopt)
+ raise EmptyResponseError unless env
+ receive_headers(env.header)
+ begin
+ check_fault(env.body)
+ rescue ::SOAP::FaultError => e
+ op_info.raise_fault(e, @mapping_registry, @literal_mapping_registry)
+ end
+ op_info.response_obj(env.body, @mapping_registry,
+ @literal_mapping_registry, mapping_opt)
+ end
+
+ def route(req_header, req_body, reqopt, resopt)
+ req_env = ::SOAP::SOAPEnvelope.new(req_header, req_body)
+ unless reqopt[:envelopenamespace].nil?
+ set_envelopenamespace(req_env, reqopt[:envelopenamespace])
+ end
+ reqopt[:external_content] = nil
+ conn_data = marshal(req_env, reqopt)
+ if ext = reqopt[:external_content]
+ mime = MIMEMessage.new
+ ext.each do |k, v|
+ mime.add_attachment(v.data)
+ end
+ mime.add_part(conn_data.send_string + "\r\n")
+ mime.close
+ conn_data.send_string = mime.content_str
+ conn_data.send_contenttype = mime.headers['content-type'].str
+ end
+ conn_data = @streamhandler.send(@endpoint_url, conn_data,
+ reqopt[:soapaction])
+ if conn_data.receive_string.empty?
+ return nil
+ end
+ unmarshal(conn_data, resopt)
+ end
+
+ def check_fault(body)
+ if body.fault
+ raise SOAP::FaultError.new(body.fault)
+ end
+ end
+
+private
+
+ def set_envelopenamespace(env, namespace)
+ env.elename = XSD::QName.new(namespace, env.elename.name)
+ if env.header
+ env.header.elename = XSD::QName.new(namespace, env.header.elename.name)
+ end
+ if env.body
+ env.body.elename = XSD::QName.new(namespace, env.body.elename.name)
+ end
+ end
+
+ def create_request_header
+ headers = @headerhandler.on_outbound
+ if headers.empty?
+ nil
+ else
+ h = ::SOAP::SOAPHeader.new
+ headers.each do |header|
+ h.add(header.elename.name, header)
+ end
+ h
+ end
+ end
+
+ def receive_headers(headers)
+ @headerhandler.on_inbound(headers) if headers
+ end
+
+ def marshal(env, opt)
+ send_string = Processor.marshal(env, opt)
+ StreamHandler::ConnectionData.new(send_string)
+ end
+
+ def unmarshal(conn_data, opt)
+ contenttype = conn_data.receive_contenttype
+ if /#{MIMEMessage::MultipartContentType}/i =~ contenttype
+ opt[:external_content] = {}
+ mime = MIMEMessage.parse("Content-Type: " + contenttype,
+ conn_data.receive_string)
+ mime.parts.each do |part|
+ value = Attachment.new(part.content)
+ value.contentid = part.contentid
+ obj = SOAPAttachment.new(value)
+ opt[:external_content][value.contentid] = obj if value.contentid
+ end
+ opt[:charset] = @mandatorycharset ||
+ StreamHandler.parse_media_type(mime.root.headers['content-type'].str)
+ env = Processor.unmarshal(mime.root.content, opt)
+ else
+ opt[:charset] = @mandatorycharset ||
+ ::SOAP::StreamHandler.parse_media_type(contenttype)
+ env = Processor.unmarshal(conn_data.receive_string, opt)
+ end
+ unless env.is_a?(::SOAP::SOAPEnvelope)
+ raise ResponseFormatError.new(
+ "response is not a SOAP envelope: #{conn_data.receive_string}")
+ end
+ env
+ end
+
+ def create_header(headers)
+ header = SOAPHeader.new()
+ headers.each do |content, mustunderstand, encodingstyle|
+ header.add(SOAPHeaderItem.new(content, mustunderstand, encodingstyle))
+ end
+ header
+ end
+
+ def create_encoding_opt(hash = nil)
+ opt = {}
+ opt[:default_encodingstyle] = @default_encodingstyle
+ opt[:allow_unqualified_element] = @allow_unqualified_element
+ opt[:generate_explicit_type] = @generate_explicit_type
+ opt[:no_indent] = @options["soap.envelope.no_indent"]
+ opt[:use_numeric_character_reference] =
+ @options["soap.envelope.use_numeric_character_reference"]
+ opt.update(hash) if hash
+ opt
+ end
+
+ def create_mapping_opt(hash = nil)
+ opt = {
+ :external_ces => @options["soap.mapping.external_ces"]
+ }
+ opt.update(hash) if hash
+ opt
+ end
+
+ class Operation
+ attr_reader :soapaction
+ attr_reader :request_style
+ attr_reader :response_style
+ attr_reader :request_use
+ attr_reader :response_use
+ attr_reader :elementformdefault
+ attr_reader :attributeformdefault
+
+ def initialize(soapaction, param_def, opt)
+ @soapaction = soapaction
+ @request_style = opt[:request_style]
+ @response_style = opt[:response_style]
+ @request_use = opt[:request_use]
+ @response_use = opt[:response_use]
+ # set nil(unqualified) by default
+ @elementformdefault = opt[:elementformdefault]
+ @attributeformdefault = opt[:attributeformdefault]
+ check_style(@request_style)
+ check_style(@response_style)
+ check_use(@request_use)
+ check_use(@response_use)
+ if @request_style == :rpc
+ @rpc_request_qname = opt[:request_qname]
+ if @rpc_request_qname.nil?
+ raise MethodDefinitionError.new("rpc_request_qname must be given")
+ end
+ @rpc_method_factory =
+ RPC::SOAPMethodRequest.new(@rpc_request_qname, param_def, @soapaction)
+ else
+ @doc_request_qnames = []
+ @doc_request_qualified = []
+ @doc_response_qnames = []
+ @doc_response_qualified = []
+ param_def.each do |inout, paramname, typeinfo, eleinfo|
+ klass_not_used, nsdef, namedef = typeinfo
+ qualified = eleinfo
+ if namedef.nil?
+ raise MethodDefinitionError.new("qname must be given")
+ end
+ case inout
+ when SOAPMethod::IN
+ @doc_request_qnames << XSD::QName.new(nsdef, namedef)
+ @doc_request_qualified << qualified
+ when SOAPMethod::OUT
+ @doc_response_qnames << XSD::QName.new(nsdef, namedef)
+ @doc_response_qualified << qualified
+ else
+ raise MethodDefinitionError.new(
+ "illegal inout definition for document style: #{inout}")
+ end
+ end
+ end
+ end
+
+ def request_default_encodingstyle
+ (@request_use == :encoded) ? EncodingNamespace : LiteralNamespace
+ end
+
+ def response_default_encodingstyle
+ (@response_use == :encoded) ? EncodingNamespace : LiteralNamespace
+ end
+
+ def request_body(values, mapping_registry, literal_mapping_registry, opt)
+ if @request_style == :rpc
+ request_rpc(values, mapping_registry, literal_mapping_registry, opt)
+ else
+ request_doc(values, mapping_registry, literal_mapping_registry, opt)
+ end
+ end
+
+ def response_obj(body, mapping_registry, literal_mapping_registry, opt)
+ if @response_style == :rpc
+ response_rpc(body, mapping_registry, literal_mapping_registry, opt)
+ else
+ response_doc(body, mapping_registry, literal_mapping_registry, opt)
+ end
+ end
+
+ def raise_fault(e, mapping_registry, literal_mapping_registry)
+ if @response_style == :rpc
+ Mapping.fault2exception(e, mapping_registry)
+ else
+ Mapping.fault2exception(e, literal_mapping_registry)
+ end
+ end
+
+ private
+
+ def check_style(style)
+ unless [:rpc, :document].include?(style)
+ raise MethodDefinitionError.new("unknown style: #{style}")
+ end
+ end
+
+ def check_use(use)
+ unless [:encoded, :literal].include?(use)
+ raise MethodDefinitionError.new("unknown use: #{use}")
+ end
+ end
+
+ def request_rpc(values, mapping_registry, literal_mapping_registry, opt)
+ if @request_use == :encoded
+ request_rpc_enc(values, mapping_registry, opt)
+ else
+ request_rpc_lit(values, literal_mapping_registry, opt)
+ end
+ end
+
+ def request_doc(values, mapping_registry, literal_mapping_registry, opt)
+ if @request_use == :encoded
+ request_doc_enc(values, mapping_registry, opt)
+ else
+ request_doc_lit(values, literal_mapping_registry, opt)
+ end
+ end
+
+ def request_rpc_enc(values, mapping_registry, opt)
+ method = @rpc_method_factory.dup
+ names = method.input_params
+ obj = create_request_obj(names, values)
+ soap = Mapping.obj2soap(obj, mapping_registry, @rpc_request_qname, opt)
+ method.set_param(soap)
+ method
+ end
+
+ def request_rpc_lit(values, mapping_registry, opt)
+ method = @rpc_method_factory.dup
+ params = {}
+ idx = 0
+ method.input_params.each do |name|
+ params[name] = Mapping.obj2soap(values[idx], mapping_registry,
+ XSD::QName.new(nil, name), opt)
+ idx += 1
+ end
+ method.set_param(params)
+ method
+ end
+
+ def request_doc_enc(values, mapping_registry, opt)
+ (0...values.size).collect { |idx|
+ ele = Mapping.obj2soap(values[idx], mapping_registry, nil, opt)
+ ele.elename = @doc_request_qnames[idx]
+ ele
+ }
+ end
+
+ def request_doc_lit(values, mapping_registry, opt)
+ (0...values.size).collect { |idx|
+ ele = Mapping.obj2soap(values[idx], mapping_registry,
+ @doc_request_qnames[idx], opt)
+ ele.encodingstyle = LiteralNamespace
+ if ele.respond_to?(:qualified)
+ ele.qualified = @doc_request_qualified[idx]
+ end
+ ele
+ }
+ end
+
+ def response_rpc(body, mapping_registry, literal_mapping_registry, opt)
+ if @response_use == :encoded
+ response_rpc_enc(body, mapping_registry, opt)
+ else
+ response_rpc_lit(body, literal_mapping_registry, opt)
+ end
+ end
+
+ def response_doc(body, mapping_registry, literal_mapping_registry, opt)
+ if @response_use == :encoded
+ return *response_doc_enc(body, mapping_registry, opt)
+ else
+ return *response_doc_lit(body, literal_mapping_registry, opt)
+ end
+ end
+
+ def response_rpc_enc(body, mapping_registry, opt)
+ ret = nil
+ if body.response
+ ret = Mapping.soap2obj(body.response, mapping_registry,
+ @rpc_method_factory.retval_class_name, opt)
+ end
+ if body.outparams
+ outparams = body.outparams.collect { |outparam|
+ Mapping.soap2obj(outparam, mapping_registry, nil, opt)
+ }
+ [ret].concat(outparams)
+ else
+ ret
+ end
+ end
+
+ def response_rpc_lit(body, mapping_registry, opt)
+ body.root_node.collect { |key, value|
+ Mapping.soap2obj(value, mapping_registry,
+ @rpc_method_factory.retval_class_name, opt)
+ }
+ end
+
+ def response_doc_enc(body, mapping_registry, opt)
+ body.collect { |key, value|
+ Mapping.soap2obj(value, mapping_registry, nil, opt)
+ }
+ end
+
+ def response_doc_lit(body, mapping_registry, opt)
+ body.collect { |key, value|
+ Mapping.soap2obj(value, mapping_registry)
+ }
+ end
+
+ def create_request_obj(names, params)
+ o = Object.new
+ idx = 0
+ while idx < params.length
+ o.instance_variable_set('@' + names[idx], params[idx])
+ idx += 1
+ end
+ o
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/soap/rpc/router.rb b/ruby_1_8_5/lib/soap/rpc/router.rb
new file mode 100644
index 0000000000..71c6eb625b
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/rpc/router.rb
@@ -0,0 +1,594 @@
+# SOAP4R - RPC Routing library
+# Copyright (C) 2001, 2002, 2004, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'soap/soap'
+require 'soap/processor'
+require 'soap/mapping'
+require 'soap/mapping/wsdlliteralregistry'
+require 'soap/rpc/rpc'
+require 'soap/rpc/element'
+require 'soap/streamHandler'
+require 'soap/mimemessage'
+require 'soap/header/handlerset'
+
+
+module SOAP
+module RPC
+
+
+class Router
+ include SOAP
+
+ attr_reader :actor
+ attr_accessor :mapping_registry
+ attr_accessor :literal_mapping_registry
+ attr_accessor :generate_explicit_type
+ attr_accessor :external_ces
+
+ def initialize(actor)
+ @actor = actor
+ @mapping_registry = nil
+ @headerhandler = Header::HandlerSet.new
+ @literal_mapping_registry = ::SOAP::Mapping::WSDLLiteralRegistry.new
+ @generate_explicit_type = true
+ @external_ces = nil
+ @operation_by_soapaction = {}
+ @operation_by_qname = {}
+ @headerhandlerfactory = []
+ end
+
+ ###
+ ## header handler interface
+ #
+ def add_request_headerhandler(factory)
+ unless factory.respond_to?(:create)
+ raise TypeError.new("factory must respond to 'create'")
+ end
+ @headerhandlerfactory << factory
+ end
+
+ def add_headerhandler(handler)
+ @headerhandler.add(handler)
+ end
+
+ ###
+ ## servant definition interface
+ #
+ def add_rpc_request_servant(factory, namespace)
+ unless factory.respond_to?(:create)
+ raise TypeError.new("factory must respond to 'create'")
+ end
+ obj = factory.create # a dummy instance for introspection
+ ::SOAP::RPC.defined_methods(obj).each do |name|
+ begin
+ qname = XSD::QName.new(namespace, name)
+ param_def = ::SOAP::RPC::SOAPMethod.derive_rpc_param_def(obj, name)
+ opt = create_styleuse_option(:rpc, :encoded)
+ add_rpc_request_operation(factory, qname, nil, name, param_def, opt)
+ rescue SOAP::RPC::MethodDefinitionError => e
+ p e if $DEBUG
+ end
+ end
+ end
+
+ def add_rpc_servant(obj, namespace)
+ ::SOAP::RPC.defined_methods(obj).each do |name|
+ begin
+ qname = XSD::QName.new(namespace, name)
+ param_def = ::SOAP::RPC::SOAPMethod.derive_rpc_param_def(obj, name)
+ opt = create_styleuse_option(:rpc, :encoded)
+ add_rpc_operation(obj, qname, nil, name, param_def, opt)
+ rescue SOAP::RPC::MethodDefinitionError => e
+ p e if $DEBUG
+ end
+ end
+ end
+ alias add_servant add_rpc_servant
+
+ ###
+ ## operation definition interface
+ #
+ def add_rpc_operation(receiver, qname, soapaction, name, param_def, opt = {})
+ ensure_styleuse_option(opt, :rpc, :encoded)
+ opt[:request_qname] = qname
+ op = ApplicationScopeOperation.new(soapaction, receiver, name, param_def,
+ opt)
+ if opt[:request_style] != :rpc
+ raise RPCRoutingError.new("illegal request_style given")
+ end
+ assign_operation(soapaction, qname, op)
+ end
+ alias add_method add_rpc_operation
+ alias add_rpc_method add_rpc_operation
+
+ def add_rpc_request_operation(factory, qname, soapaction, name, param_def, opt = {})
+ ensure_styleuse_option(opt, :rpc, :encoded)
+ opt[:request_qname] = qname
+ op = RequestScopeOperation.new(soapaction, factory, name, param_def, opt)
+ if opt[:request_style] != :rpc
+ raise RPCRoutingError.new("illegal request_style given")
+ end
+ assign_operation(soapaction, qname, op)
+ end
+
+ def add_document_operation(receiver, soapaction, name, param_def, opt = {})
+ #
+ # adopt workaround for doc/lit wrapper method
+ # (you should consider to simply use rpc/lit service)
+ #
+ #unless soapaction
+ # raise RPCRoutingError.new("soapaction is a must for document method")
+ #end
+ ensure_styleuse_option(opt, :document, :literal)
+ op = ApplicationScopeOperation.new(soapaction, receiver, name, param_def,
+ opt)
+ if opt[:request_style] != :document
+ raise RPCRoutingError.new("illegal request_style given")
+ end
+ assign_operation(soapaction, first_input_part_qname(param_def), op)
+ end
+ alias add_document_method add_document_operation
+
+ def add_document_request_operation(factory, soapaction, name, param_def, opt = {})
+ #
+ # adopt workaround for doc/lit wrapper method
+ # (you should consider to simply use rpc/lit service)
+ #
+ #unless soapaction
+ # raise RPCRoutingError.new("soapaction is a must for document method")
+ #end
+ ensure_styleuse_option(opt, :document, :literal)
+ op = RequestScopeOperation.new(soapaction, receiver, name, param_def, opt)
+ if opt[:request_style] != :document
+ raise RPCRoutingError.new("illegal request_style given")
+ end
+ assign_operation(soapaction, first_input_part_qname(param_def), op)
+ end
+
+ def route(conn_data)
+ # we cannot set request_default_encodingsyle before parsing the content.
+ env = unmarshal(conn_data)
+ if env.nil?
+ raise ArgumentError.new("illegal SOAP marshal format")
+ end
+ op = lookup_operation(conn_data.soapaction, env.body)
+ headerhandler = @headerhandler.dup
+ @headerhandlerfactory.each do |f|
+ headerhandler.add(f.create)
+ end
+ receive_headers(headerhandler, env.header)
+ soap_response = default_encodingstyle = nil
+ begin
+ soap_response =
+ op.call(env.body, @mapping_registry, @literal_mapping_registry,
+ create_mapping_opt)
+ default_encodingstyle = op.response_default_encodingstyle
+ rescue Exception
+ soap_response = fault($!)
+ default_encodingstyle = nil
+ end
+ conn_data.is_fault = true if soap_response.is_a?(SOAPFault)
+ header = call_headers(headerhandler)
+ body = SOAPBody.new(soap_response)
+ env = SOAPEnvelope.new(header, body)
+ marshal(conn_data, env, default_encodingstyle)
+ end
+
+ # Create fault response string.
+ def create_fault_response(e)
+ env = SOAPEnvelope.new(SOAPHeader.new, SOAPBody.new(fault(e)))
+ opt = {}
+ opt[:external_content] = nil
+ response_string = Processor.marshal(env, opt)
+ conn_data = StreamHandler::ConnectionData.new(response_string)
+ conn_data.is_fault = true
+ if ext = opt[:external_content]
+ mimeize(conn_data, ext)
+ end
+ conn_data
+ end
+
+private
+
+ def first_input_part_qname(param_def)
+ param_def.each do |inout, paramname, typeinfo|
+ if inout == SOAPMethod::IN
+ klass, nsdef, namedef = typeinfo
+ return XSD::QName.new(nsdef, namedef)
+ end
+ end
+ nil
+ end
+
+ def create_styleuse_option(style, use)
+ opt = {}
+ opt[:request_style] = opt[:response_style] = style
+ opt[:request_use] = opt[:response_use] = use
+ opt
+ end
+
+ def ensure_styleuse_option(opt, style, use)
+ opt[:request_style] ||= style
+ opt[:response_style] ||= style
+ opt[:request_use] ||= use
+ opt[:response_use] ||= use
+ end
+
+ def assign_operation(soapaction, qname, op)
+ assigned = false
+ if soapaction and !soapaction.empty?
+ @operation_by_soapaction[soapaction] = op
+ assigned = true
+ end
+ if qname
+ @operation_by_qname[qname] = op
+ assigned = true
+ end
+ unless assigned
+ raise RPCRoutingError.new("cannot assign operation")
+ end
+ end
+
+ def lookup_operation(soapaction, body)
+ if op = @operation_by_soapaction[soapaction]
+ return op
+ end
+ qname = body.root_node.elename
+ if op = @operation_by_qname[qname]
+ return op
+ end
+ if soapaction
+ raise RPCRoutingError.new(
+ "operation: #{soapaction} #{qname} not supported")
+ else
+ raise RPCRoutingError.new("operation: #{qname} not supported")
+ end
+ end
+
+ def call_headers(headerhandler)
+ headers = headerhandler.on_outbound
+ if headers.empty?
+ nil
+ else
+ h = ::SOAP::SOAPHeader.new
+ headers.each do |header|
+ h.add(header.elename.name, header)
+ end
+ h
+ end
+ end
+
+ def receive_headers(headerhandler, headers)
+ headerhandler.on_inbound(headers) if headers
+ end
+
+ def unmarshal(conn_data)
+ opt = {}
+ contenttype = conn_data.receive_contenttype
+ if /#{MIMEMessage::MultipartContentType}/i =~ contenttype
+ opt[:external_content] = {}
+ mime = MIMEMessage.parse("Content-Type: " + contenttype,
+ conn_data.receive_string)
+ mime.parts.each do |part|
+ value = Attachment.new(part.content)
+ value.contentid = part.contentid
+ obj = SOAPAttachment.new(value)
+ opt[:external_content][value.contentid] = obj if value.contentid
+ end
+ opt[:charset] =
+ StreamHandler.parse_media_type(mime.root.headers['content-type'].str)
+ env = Processor.unmarshal(mime.root.content, opt)
+ else
+ opt[:charset] = ::SOAP::StreamHandler.parse_media_type(contenttype)
+ env = Processor.unmarshal(conn_data.receive_string, opt)
+ end
+ charset = opt[:charset]
+ conn_data.send_contenttype = "text/xml; charset=\"#{charset}\""
+ env
+ end
+
+ def marshal(conn_data, env, default_encodingstyle = nil)
+ opt = {}
+ opt[:external_content] = nil
+ opt[:default_encodingstyle] = default_encodingstyle
+ opt[:generate_explicit_type] = @generate_explicit_type
+ response_string = Processor.marshal(env, opt)
+ conn_data.send_string = response_string
+ if ext = opt[:external_content]
+ mimeize(conn_data, ext)
+ end
+ conn_data
+ end
+
+ def mimeize(conn_data, ext)
+ mime = MIMEMessage.new
+ ext.each do |k, v|
+ mime.add_attachment(v.data)
+ end
+ mime.add_part(conn_data.send_string + "\r\n")
+ mime.close
+ conn_data.send_string = mime.content_str
+ conn_data.send_contenttype = mime.headers['content-type'].str
+ conn_data
+ end
+
+ # Create fault response.
+ def fault(e)
+ detail = Mapping::SOAPException.new(e)
+ SOAPFault.new(
+ SOAPString.new('Server'),
+ SOAPString.new(e.to_s),
+ SOAPString.new(@actor),
+ Mapping.obj2soap(detail, @mapping_registry))
+ end
+
+ def create_mapping_opt
+ { :external_ces => @external_ces }
+ end
+
+ class Operation
+ attr_reader :name
+ attr_reader :soapaction
+ attr_reader :request_style
+ attr_reader :response_style
+ attr_reader :request_use
+ attr_reader :response_use
+
+ def initialize(soapaction, name, param_def, opt)
+ @soapaction = soapaction
+ @name = name
+ @request_style = opt[:request_style]
+ @response_style = opt[:response_style]
+ @request_use = opt[:request_use]
+ @response_use = opt[:response_use]
+ check_style(@request_style)
+ check_style(@response_style)
+ check_use(@request_use)
+ check_use(@response_use)
+ if @response_style == :rpc
+ request_qname = opt[:request_qname] or raise
+ @rpc_method_factory =
+ RPC::SOAPMethodRequest.new(request_qname, param_def, @soapaction)
+ @rpc_response_qname = opt[:response_qname]
+ else
+ @doc_request_qnames = []
+ @doc_request_qualified = []
+ @doc_response_qnames = []
+ @doc_response_qualified = []
+ param_def.each do |inout, paramname, typeinfo, eleinfo|
+ klass, nsdef, namedef = typeinfo
+ qualified = eleinfo
+ case inout
+ when SOAPMethod::IN
+ @doc_request_qnames << XSD::QName.new(nsdef, namedef)
+ @doc_request_qualified << qualified
+ when SOAPMethod::OUT
+ @doc_response_qnames << XSD::QName.new(nsdef, namedef)
+ @doc_response_qualified << qualified
+ else
+ raise ArgumentError.new(
+ "illegal inout definition for document style: #{inout}")
+ end
+ end
+ end
+ end
+
+ def request_default_encodingstyle
+ (@request_use == :encoded) ? EncodingNamespace : LiteralNamespace
+ end
+
+ def response_default_encodingstyle
+ (@response_use == :encoded) ? EncodingNamespace : LiteralNamespace
+ end
+
+ def call(body, mapping_registry, literal_mapping_registry, opt)
+ if @request_style == :rpc
+ values = request_rpc(body, mapping_registry, literal_mapping_registry,
+ opt)
+ else
+ values = request_document(body, mapping_registry,
+ literal_mapping_registry, opt)
+ end
+ result = receiver.method(@name.intern).call(*values)
+ return result if result.is_a?(SOAPFault)
+ if @response_style == :rpc
+ response_rpc(result, mapping_registry, literal_mapping_registry, opt)
+ else
+ response_doc(result, mapping_registry, literal_mapping_registry, opt)
+ end
+ end
+
+ private
+
+ def receiver
+ raise NotImplementedError.new('must be defined in derived class')
+ end
+
+ def request_rpc(body, mapping_registry, literal_mapping_registry, opt)
+ request = body.request
+ unless request.is_a?(SOAPStruct)
+ raise RPCRoutingError.new("not an RPC style")
+ end
+ if @request_use == :encoded
+ request_rpc_enc(request, mapping_registry, opt)
+ else
+ request_rpc_lit(request, literal_mapping_registry, opt)
+ end
+ end
+
+ def request_document(body, mapping_registry, literal_mapping_registry, opt)
+ # ToDo: compare names with @doc_request_qnames
+ if @request_use == :encoded
+ request_doc_enc(body, mapping_registry, opt)
+ else
+ request_doc_lit(body, literal_mapping_registry, opt)
+ end
+ end
+
+ def request_rpc_enc(request, mapping_registry, opt)
+ param = Mapping.soap2obj(request, mapping_registry, nil, opt)
+ request.collect { |key, value|
+ param[key]
+ }
+ end
+
+ def request_rpc_lit(request, mapping_registry, opt)
+ request.collect { |key, value|
+ Mapping.soap2obj(value, mapping_registry, nil, opt)
+ }
+ end
+
+ def request_doc_enc(body, mapping_registry, opt)
+ body.collect { |key, value|
+ Mapping.soap2obj(value, mapping_registry, nil, opt)
+ }
+ end
+
+ def request_doc_lit(body, mapping_registry, opt)
+ body.collect { |key, value|
+ Mapping.soap2obj(value, mapping_registry, nil, opt)
+ }
+ end
+
+ def response_rpc(result, mapping_registry, literal_mapping_registry, opt)
+ if @response_use == :encoded
+ response_rpc_enc(result, mapping_registry, opt)
+ else
+ response_rpc_lit(result, literal_mapping_registry, opt)
+ end
+ end
+
+ def response_doc(result, mapping_registry, literal_mapping_registry, opt)
+ if @doc_response_qnames.size == 1 and !result.is_a?(Array)
+ result = [result]
+ end
+ if result.size != @doc_response_qnames.size
+ raise "required #{@doc_response_qnames.size} responses " +
+ "but #{result.size} given"
+ end
+ if @response_use == :encoded
+ response_doc_enc(result, mapping_registry, opt)
+ else
+ response_doc_lit(result, literal_mapping_registry, opt)
+ end
+ end
+
+ def response_rpc_enc(result, mapping_registry, opt)
+ soap_response =
+ @rpc_method_factory.create_method_response(@rpc_response_qname)
+ if soap_response.have_outparam?
+ unless result.is_a?(Array)
+ raise RPCRoutingError.new("out parameter was not returned")
+ end
+ outparams = {}
+ i = 1
+ soap_response.output_params.each do |outparam|
+ outparams[outparam] = Mapping.obj2soap(result[i], mapping_registry,
+ nil, opt)
+ i += 1
+ end
+ soap_response.set_outparam(outparams)
+ soap_response.retval = Mapping.obj2soap(result[0], mapping_registry,
+ nil, opt)
+ else
+ soap_response.retval = Mapping.obj2soap(result, mapping_registry, nil,
+ opt)
+ end
+ soap_response
+ end
+
+ def response_rpc_lit(result, mapping_registry, opt)
+ soap_response =
+ @rpc_method_factory.create_method_response(@rpc_response_qname)
+ if soap_response.have_outparam?
+ unless result.is_a?(Array)
+ raise RPCRoutingError.new("out parameter was not returned")
+ end
+ outparams = {}
+ i = 1
+ soap_response.output_params.each do |outparam|
+ outparams[outparam] = Mapping.obj2soap(result[i], mapping_registry,
+ XSD::QName.new(nil, outparam), opt)
+ i += 1
+ end
+ soap_response.set_outparam(outparams)
+ soap_response.retval = Mapping.obj2soap(result[0], mapping_registry,
+ XSD::QName.new(nil, soap_response.elename), opt)
+ else
+ soap_response.retval = Mapping.obj2soap(result, mapping_registry,
+ XSD::QName.new(nil, soap_response.elename), opt)
+ end
+ soap_response
+ end
+
+ def response_doc_enc(result, mapping_registry, opt)
+ (0...result.size).collect { |idx|
+ ele = Mapping.obj2soap(result[idx], mapping_registry, nil, opt)
+ ele.elename = @doc_response_qnames[idx]
+ ele
+ }
+ end
+
+ def response_doc_lit(result, mapping_registry, opt)
+ (0...result.size).collect { |idx|
+ ele = Mapping.obj2soap(result[idx], mapping_registry,
+ @doc_response_qnames[idx])
+ ele.encodingstyle = LiteralNamespace
+ if ele.respond_to?(:qualified)
+ ele.qualified = @doc_response_qualified[idx]
+ end
+ ele
+ }
+ end
+
+ def check_style(style)
+ unless [:rpc, :document].include?(style)
+ raise ArgumentError.new("unknown style: #{style}")
+ end
+ end
+
+ def check_use(use)
+ unless [:encoded, :literal].include?(use)
+ raise ArgumentError.new("unknown use: #{use}")
+ end
+ end
+ end
+
+ class ApplicationScopeOperation < Operation
+ def initialize(soapaction, receiver, name, param_def, opt)
+ super(soapaction, name, param_def, opt)
+ @receiver = receiver
+ end
+
+ private
+
+ def receiver
+ @receiver
+ end
+ end
+
+ class RequestScopeOperation < Operation
+ def initialize(soapaction, receiver_factory, name, param_def, opt)
+ super(soapaction, name, param_def, opt)
+ unless receiver_factory.respond_to?(:create)
+ raise TypeError.new("factory must respond to 'create'")
+ end
+ @receiver_factory = receiver_factory
+ end
+
+ private
+
+ def receiver
+ @receiver_factory.create
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/soap/rpc/rpc.rb b/ruby_1_8_5/lib/soap/rpc/rpc.rb
new file mode 100644
index 0000000000..a48b525dbb
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/rpc/rpc.rb
@@ -0,0 +1,25 @@
+# SOAP4R - RPC utility.
+# Copyright (C) 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+module SOAP
+
+
+module RPC
+ ServerException = Mapping::MappedException
+
+ def self.defined_methods(obj)
+ if obj.is_a?(Module)
+ obj.methods - Module.methods
+ else
+ obj.methods - Object.instance_methods(true)
+ end
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/soap/rpc/soaplet.rb b/ruby_1_8_5/lib/soap/rpc/soaplet.rb
new file mode 100644
index 0000000000..7cccdd3e31
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/rpc/soaplet.rb
@@ -0,0 +1,162 @@
+# SOAP4R - SOAP handler servlet for WEBrick
+# Copyright (C) 2001-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'webrick/httpservlet/abstract'
+require 'webrick/httpstatus'
+require 'soap/rpc/router'
+require 'soap/streamHandler'
+begin
+ require 'stringio'
+ require 'zlib'
+rescue LoadError
+ warn("Loading stringio or zlib failed. No gzipped response supported.") if $DEBUG
+end
+
+
+warn("Overriding WEBrick::Log#debug") if $DEBUG
+require 'webrick/log'
+module WEBrick
+ class Log < BasicLog
+ alias __debug debug
+ def debug(msg = nil)
+ if block_given? and msg.nil?
+ __debug(yield)
+ else
+ __debug(msg)
+ end
+ end
+ end
+end
+
+
+module SOAP
+module RPC
+
+
+class SOAPlet < WEBrick::HTTPServlet::AbstractServlet
+public
+ attr_reader :options
+
+ def initialize(router = nil)
+ @router = router || ::SOAP::RPC::Router.new(self.class.name)
+ @options = {}
+ @config = {}
+ end
+
+ # for backward compatibility
+ def app_scope_router
+ @router
+ end
+
+ # for backward compatibility
+ def add_servant(obj, namespace)
+ @router.add_rpc_servant(obj, namespace)
+ end
+
+ def allow_content_encoding_gzip=(allow)
+ @options[:allow_content_encoding_gzip] = allow
+ end
+
+ ###
+ ## Servlet interfaces for WEBrick.
+ #
+ def get_instance(config, *options)
+ @config = config
+ self
+ end
+
+ def require_path_info?
+ false
+ end
+
+ def do_GET(req, res)
+ res.header['Allow'] = 'POST'
+ raise WEBrick::HTTPStatus::MethodNotAllowed, "GET request not allowed"
+ end
+
+ def do_POST(req, res)
+ logger.debug { "SOAP request: " + req.body } if logger
+ begin
+ conn_data = ::SOAP::StreamHandler::ConnectionData.new
+ setup_req(conn_data, req)
+ @router.external_ces = @options[:external_ces]
+ conn_data = @router.route(conn_data)
+ setup_res(conn_data, req, res)
+ rescue Exception => e
+ conn_data = @router.create_fault_response(e)
+ res.status = WEBrick::HTTPStatus::RC_INTERNAL_SERVER_ERROR
+ res.body = conn_data.send_string
+ res['content-type'] = conn_data.send_contenttype || "text/xml"
+ end
+ if res.body.is_a?(IO)
+ res.chunked = true
+ logger.debug { "SOAP response: (chunked response not logged)" } if logger
+ else
+ logger.debug { "SOAP response: " + res.body } if logger
+ end
+ end
+
+private
+
+ def logger
+ @config[:Logger]
+ end
+
+ def setup_req(conn_data, req)
+ conn_data.receive_string = req.body
+ conn_data.receive_contenttype = req['content-type']
+ conn_data.soapaction = parse_soapaction(req.meta_vars['HTTP_SOAPACTION'])
+ end
+
+ def setup_res(conn_data, req, res)
+ res['content-type'] = conn_data.send_contenttype
+ if conn_data.is_fault
+ res.status = WEBrick::HTTPStatus::RC_INTERNAL_SERVER_ERROR
+ end
+ if outstring = encode_gzip(req, conn_data.send_string)
+ res['content-encoding'] = 'gzip'
+ res['content-length'] = outstring.size
+ res.body = outstring
+ else
+ res.body = conn_data.send_string
+ end
+ end
+
+ def parse_soapaction(soapaction)
+ if !soapaction.nil? and !soapaction.empty?
+ if /^"(.+)"$/ =~ soapaction
+ return $1
+ end
+ end
+ nil
+ end
+
+ def encode_gzip(req, outstring)
+ unless encode_gzip?(req)
+ return nil
+ end
+ begin
+ ostream = StringIO.new
+ gz = Zlib::GzipWriter.new(ostream)
+ gz.write(outstring)
+ ostream.string
+ ensure
+ gz.close
+ end
+ end
+
+ def encode_gzip?(req)
+ @options[:allow_content_encoding_gzip] and defined?(::Zlib) and
+ req['accept-encoding'] and
+ req['accept-encoding'].split(/,\s*/).include?('gzip')
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/soap/rpc/standaloneServer.rb b/ruby_1_8_5/lib/soap/rpc/standaloneServer.rb
new file mode 100644
index 0000000000..080343ba33
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/rpc/standaloneServer.rb
@@ -0,0 +1,43 @@
+# SOAP4R - WEBrick Server
+# Copyright (C) 2003 by NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'soap/rpc/httpserver'
+
+
+module SOAP
+module RPC
+
+
+class StandaloneServer < HTTPServer
+ def initialize(appname, default_namespace, host = "0.0.0.0", port = 8080)
+ @appname = appname
+ @default_namespace = default_namespace
+ @host = host
+ @port = port
+ super(create_config)
+ end
+
+ alias add_servant add_rpc_servant
+ alias add_headerhandler add_rpc_headerhandler
+
+private
+
+ def create_config
+ {
+ :BindAddress => @host,
+ :Port => @port,
+ :AccessLog => [],
+ :SOAPDefaultNamespace => @default_namespace,
+ :SOAPHTTPServerApplicationName => @appname,
+ }
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/soap/soap.rb b/ruby_1_8_5/lib/soap/soap.rb
new file mode 100644
index 0000000000..12e09eccfe
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/soap.rb
@@ -0,0 +1,140 @@
+# soap/soap.rb: SOAP4R - Base definitions.
+# Copyright (C) 2000-2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/qname'
+require 'xsd/charset'
+
+
+module SOAP
+
+
+VERSION = Version = '1.5.5'
+PropertyName = 'soap/property'
+
+EnvelopeNamespace = 'http://schemas.xmlsoap.org/soap/envelope/'
+EncodingNamespace = 'http://schemas.xmlsoap.org/soap/encoding/'
+LiteralNamespace = 'http://xml.apache.org/xml-soap/literalxml'
+
+NextActor = 'http://schemas.xmlsoap.org/soap/actor/next'
+
+EleEnvelope = 'Envelope'
+EleHeader = 'Header'
+EleBody = 'Body'
+EleFault = 'Fault'
+EleFaultString = 'faultstring'
+EleFaultActor = 'faultactor'
+EleFaultCode = 'faultcode'
+EleFaultDetail = 'detail'
+
+AttrMustUnderstand = 'mustUnderstand'
+AttrEncodingStyle = 'encodingStyle'
+AttrActor = 'actor'
+AttrRoot = 'root'
+AttrArrayType = 'arrayType'
+AttrOffset = 'offset'
+AttrPosition = 'position'
+ValueArray = 'Array'
+
+EleEnvelopeName = XSD::QName.new(EnvelopeNamespace, EleEnvelope).freeze
+EleHeaderName = XSD::QName.new(EnvelopeNamespace, EleHeader).freeze
+EleBodyName = XSD::QName.new(EnvelopeNamespace, EleBody).freeze
+EleFaultName = XSD::QName.new(EnvelopeNamespace, EleFault).freeze
+EleFaultStringName = XSD::QName.new(nil, EleFaultString).freeze
+EleFaultActorName = XSD::QName.new(nil, EleFaultActor).freeze
+EleFaultCodeName = XSD::QName.new(nil, EleFaultCode).freeze
+EleFaultDetailName = XSD::QName.new(nil, EleFaultDetail).freeze
+AttrMustUnderstandName = XSD::QName.new(EnvelopeNamespace, AttrMustUnderstand).freeze
+AttrEncodingStyleName = XSD::QName.new(EnvelopeNamespace, AttrEncodingStyle).freeze
+AttrRootName = XSD::QName.new(EncodingNamespace, AttrRoot).freeze
+AttrArrayTypeName = XSD::QName.new(EncodingNamespace, AttrArrayType).freeze
+AttrOffsetName = XSD::QName.new(EncodingNamespace, AttrOffset).freeze
+AttrPositionName = XSD::QName.new(EncodingNamespace, AttrPosition).freeze
+ValueArrayName = XSD::QName.new(EncodingNamespace, ValueArray).freeze
+
+Base64Literal = 'base64'
+
+SOAPNamespaceTag = 'env'
+XSDNamespaceTag = 'xsd'
+XSINamespaceTag = 'xsi'
+
+MediaType = 'text/xml'
+
+class Error < StandardError; end
+
+class StreamError < Error; end
+class HTTPStreamError < StreamError; end
+class PostUnavailableError < HTTPStreamError; end
+class MPostUnavailableError < HTTPStreamError; end
+
+class ArrayIndexOutOfBoundsError < Error; end
+class ArrayStoreError < Error; end
+
+class RPCRoutingError < Error; end
+class EmptyResponseError < Error; end
+class ResponseFormatError < Error; end
+
+class UnhandledMustUnderstandHeaderError < Error; end
+
+class FaultError < Error
+ attr_reader :faultcode
+ attr_reader :faultstring
+ attr_reader :faultactor
+ attr_accessor :detail
+
+ def initialize(fault)
+ @faultcode = fault.faultcode
+ @faultstring = fault.faultstring
+ @faultactor = fault.faultactor
+ @detail = fault.detail
+ super(self.to_s)
+ end
+
+ def to_s
+ str = nil
+ if @faultstring and @faultstring.respond_to?('data')
+ str = @faultstring.data
+ end
+ str || '(No faultstring)'
+ end
+end
+
+
+module Env
+ def self.getenv(name)
+ ENV[name.downcase] || ENV[name.upcase]
+ end
+
+ use_proxy = getenv('soap_use_proxy') == 'on'
+ HTTP_PROXY = use_proxy ? getenv('http_proxy') : nil
+ NO_PROXY = use_proxy ? getenv('no_proxy') : nil
+end
+
+
+end
+
+
+unless Object.respond_to?(:instance_variable_get)
+ class Object
+ def instance_variable_get(ivarname)
+ instance_eval(ivarname)
+ end
+
+ def instance_variable_set(ivarname, value)
+ instance_eval("#{ivarname} = value")
+ end
+ end
+end
+
+
+unless Kernel.respond_to?(:warn)
+ module Kernel
+ def warn(msg)
+ STDERR.puts(msg + "\n") unless $VERBOSE.nil?
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/soap/streamHandler.rb b/ruby_1_8_5/lib/soap/streamHandler.rb
new file mode 100644
index 0000000000..672396ecce
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/streamHandler.rb
@@ -0,0 +1,229 @@
+# SOAP4R - Stream handler.
+# Copyright (C) 2000, 2001, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'soap/soap'
+require 'soap/httpconfigloader'
+begin
+ require 'stringio'
+ require 'zlib'
+rescue LoadError
+ warn("Loading stringio or zlib failed. No gzipped response support.") if $DEBUG
+end
+
+
+module SOAP
+
+
+class StreamHandler
+ RUBY_VERSION_STRING = "ruby #{ RUBY_VERSION } (#{ RUBY_RELEASE_DATE }) [#{ RUBY_PLATFORM }]"
+
+ class ConnectionData
+ attr_accessor :send_string
+ attr_accessor :send_contenttype
+ attr_accessor :receive_string
+ attr_accessor :receive_contenttype
+ attr_accessor :is_fault
+ attr_accessor :soapaction
+
+ def initialize(send_string = nil)
+ @send_string = send_string
+ @send_contenttype = nil
+ @receive_string = nil
+ @receive_contenttype = nil
+ @is_fault = false
+ @soapaction = nil
+ end
+ end
+
+ def self.parse_media_type(str)
+ if /^#{ MediaType }(?:\s*;\s*charset=([^"]+|"[^"]+"))?$/i !~ str
+ return nil
+ end
+ charset = $1
+ charset.gsub!(/"/, '') if charset
+ charset || 'us-ascii'
+ end
+
+ def self.create_media_type(charset)
+ "#{ MediaType }; charset=#{ charset }"
+ end
+end
+
+
+class HTTPStreamHandler < StreamHandler
+ include SOAP
+
+ begin
+ require 'http-access2'
+ if HTTPAccess2::VERSION < "2.0"
+ raise LoadError.new("http-access/2.0 or later is required.")
+ end
+ Client = HTTPAccess2::Client
+ RETRYABLE = true
+ rescue LoadError
+ warn("Loading http-access2 failed. Net/http is used.") if $DEBUG
+ require 'soap/netHttpClient'
+ Client = SOAP::NetHttpClient
+ RETRYABLE = false
+ end
+
+
+public
+
+ attr_reader :client
+ attr_accessor :wiredump_file_base
+
+ MAX_RETRY_COUNT = 10 # [times]
+
+ def initialize(options)
+ super()
+ @client = Client.new(nil, "SOAP4R/#{ Version }")
+ @wiredump_file_base = nil
+ @charset = @wiredump_dev = nil
+ @options = options
+ set_options
+ @client.debug_dev = @wiredump_dev
+ @cookie_store = nil
+ @accept_encoding_gzip = false
+ end
+
+ def test_loopback_response
+ @client.test_loopback_response
+ end
+
+ def accept_encoding_gzip=(allow)
+ @accept_encoding_gzip = allow
+ end
+
+ def inspect
+ "#<#{self.class}>"
+ end
+
+ def send(endpoint_url, conn_data, soapaction = nil, charset = @charset)
+ conn_data.soapaction ||= soapaction # for backward conpatibility
+ send_post(endpoint_url, conn_data, charset)
+ end
+
+ def reset(endpoint_url = nil)
+ if endpoint_url.nil?
+ @client.reset_all
+ else
+ @client.reset(endpoint_url)
+ end
+ @client.save_cookie_store if @cookie_store
+ end
+
+private
+
+ def set_options
+ HTTPConfigLoader.set_options(@client, @options)
+ @charset = @options["charset"] || XSD::Charset.xml_encoding_label
+ @options.add_hook("charset") do |key, value|
+ @charset = value
+ end
+ @wiredump_dev = @options["wiredump_dev"]
+ @options.add_hook("wiredump_dev") do |key, value|
+ @wiredump_dev = value
+ @client.debug_dev = @wiredump_dev
+ end
+ set_cookie_store_file(@options["cookie_store_file"])
+ @options.add_hook("cookie_store_file") do |key, value|
+ set_cookie_store_file(value)
+ end
+ ssl_config = @options["ssl_config"]
+ basic_auth = @options["basic_auth"]
+ @options.lock(true)
+ ssl_config.unlock
+ basic_auth.unlock
+ end
+
+ def set_cookie_store_file(value)
+ value = nil if value and value.empty?
+ @cookie_store = value
+ @client.set_cookie_store(@cookie_store) if @cookie_store
+ end
+
+ def send_post(endpoint_url, conn_data, charset)
+ conn_data.send_contenttype ||= StreamHandler.create_media_type(charset)
+
+ if @wiredump_file_base
+ filename = @wiredump_file_base + '_request.xml'
+ f = File.open(filename, "w")
+ f << conn_data.send_string
+ f.close
+ end
+
+ extra = {}
+ extra['Content-Type'] = conn_data.send_contenttype
+ extra['SOAPAction'] = "\"#{ conn_data.soapaction }\""
+ extra['Accept-Encoding'] = 'gzip' if send_accept_encoding_gzip?
+ send_string = conn_data.send_string
+ @wiredump_dev << "Wire dump:\n\n" if @wiredump_dev
+ begin
+ retry_count = 0
+ while true
+ res = @client.post(endpoint_url, send_string, extra)
+ if RETRYABLE and HTTP::Status.redirect?(res.status)
+ retry_count += 1
+ if retry_count >= MAX_RETRY_COUNT
+ raise HTTPStreamError.new("redirect count exceeded")
+ end
+ endpoint_url = res.header["location"][0]
+ puts "redirected to #{endpoint_url}" if $DEBUG
+ else
+ break
+ end
+ end
+ rescue
+ @client.reset(endpoint_url)
+ raise
+ end
+ @wiredump_dev << "\n\n" if @wiredump_dev
+ receive_string = res.content
+ if @wiredump_file_base
+ filename = @wiredump_file_base + '_response.xml'
+ f = File.open(filename, "w")
+ f << receive_string
+ f.close
+ end
+ case res.status
+ when 405
+ raise PostUnavailableError.new("#{ res.status }: #{ res.reason }")
+ when 200, 500
+ # Nothing to do.
+ else
+ raise HTTPStreamError.new("#{ res.status }: #{ res.reason }")
+ end
+ if res.respond_to?(:header) and !res.header['content-encoding'].empty? and
+ res.header['content-encoding'][0].downcase == 'gzip'
+ receive_string = decode_gzip(receive_string)
+ end
+ conn_data.receive_string = receive_string
+ conn_data.receive_contenttype = res.contenttype
+ conn_data
+ end
+
+ def send_accept_encoding_gzip?
+ @accept_encoding_gzip and defined?(::Zlib)
+ end
+
+ def decode_gzip(instring)
+ unless send_accept_encoding_gzip?
+ raise HTTPStreamError.new("Gzipped response content.")
+ end
+ begin
+ gz = Zlib::GzipReader.new(StringIO.new(instring))
+ gz.read
+ ensure
+ gz.close
+ end
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/soap/wsdlDriver.rb b/ruby_1_8_5/lib/soap/wsdlDriver.rb
new file mode 100644
index 0000000000..eba9608df5
--- /dev/null
+++ b/ruby_1_8_5/lib/soap/wsdlDriver.rb
@@ -0,0 +1,575 @@
+# SOAP4R - SOAP WSDL driver
+# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/parser'
+require 'wsdl/importer'
+require 'xsd/qname'
+require 'xsd/codegen/gensupport'
+require 'soap/mapping/wsdlencodedregistry'
+require 'soap/mapping/wsdlliteralregistry'
+require 'soap/rpc/driver'
+require 'wsdl/soap/methodDefCreator'
+
+
+module SOAP
+
+
+class WSDLDriverFactory
+ class FactoryError < StandardError; end
+
+ attr_reader :wsdl
+
+ def initialize(wsdl)
+ @wsdl = import(wsdl)
+ @methoddefcreator = WSDL::SOAP::MethodDefCreator.new(@wsdl)
+ end
+
+ def inspect
+ "#<#{self.class}:#{@wsdl.name}>"
+ end
+
+ def create_rpc_driver(servicename = nil, portname = nil)
+ port = find_port(servicename, portname)
+ drv = SOAP::RPC::Driver.new(port.soap_address.location)
+ init_driver(drv, port)
+ add_operation(drv, port)
+ drv
+ end
+
+ # depricated old interface
+ def create_driver(servicename = nil, portname = nil)
+ warn("WSDLDriverFactory#create_driver is depricated. Use create_rpc_driver instead.")
+ port = find_port(servicename, portname)
+ WSDLDriver.new(@wsdl, port, nil)
+ end
+
+ # Backward compatibility.
+ alias createDriver create_driver
+
+private
+
+ def find_port(servicename = nil, portname = nil)
+ service = port = nil
+ if servicename
+ service = @wsdl.service(
+ XSD::QName.new(@wsdl.targetnamespace, servicename))
+ else
+ service = @wsdl.services[0]
+ end
+ if service.nil?
+ raise FactoryError.new("service #{servicename} not found in WSDL")
+ end
+ if portname
+ port = service.ports[XSD::QName.new(@wsdl.targetnamespace, portname)]
+ if port.nil?
+ raise FactoryError.new("port #{portname} not found in WSDL")
+ end
+ else
+ port = service.ports.find { |port| !port.soap_address.nil? }
+ if port.nil?
+ raise FactoryError.new("no ports have soap:address")
+ end
+ end
+ if port.soap_address.nil?
+ raise FactoryError.new("soap:address element not found in WSDL")
+ end
+ port
+ end
+
+ def init_driver(drv, port)
+ wsdl_elements = @wsdl.collect_elements
+ wsdl_types = @wsdl.collect_complextypes + @wsdl.collect_simpletypes
+ rpc_decode_typemap = wsdl_types +
+ @wsdl.soap_rpc_complextypes(port.find_binding)
+ drv.proxy.mapping_registry =
+ Mapping::WSDLEncodedRegistry.new(rpc_decode_typemap)
+ drv.proxy.literal_mapping_registry =
+ Mapping::WSDLLiteralRegistry.new(wsdl_types, wsdl_elements)
+ end
+
+ def add_operation(drv, port)
+ port.find_binding.operations.each do |op_bind|
+ op_name = op_bind.soapoperation_name
+ soapaction = op_bind.soapaction || ''
+ orgname = op_name.name
+ name = XSD::CodeGen::GenSupport.safemethodname(orgname)
+ param_def = create_param_def(op_bind)
+ opt = {
+ :request_style => op_bind.soapoperation_style,
+ :response_style => op_bind.soapoperation_style,
+ :request_use => op_bind.input.soapbody_use,
+ :response_use => op_bind.output.soapbody_use,
+ :elementformdefault => false,
+ :attributeformdefault => false
+ }
+ if op_bind.soapoperation_style == :rpc
+ drv.add_rpc_operation(op_name, soapaction, name, param_def, opt)
+ else
+ drv.add_document_operation(soapaction, name, param_def, opt)
+ end
+ if orgname != name and orgname.capitalize == name.capitalize
+ ::SOAP::Mapping.define_singleton_method(drv, orgname) do |*arg|
+ __send__(name, *arg)
+ end
+ end
+ end
+ end
+
+ def import(location)
+ WSDL::Importer.import(location)
+ end
+
+ def create_param_def(op_bind)
+ op = op_bind.find_operation
+ if op_bind.soapoperation_style == :rpc
+ param_def = @methoddefcreator.collect_rpcparameter(op)
+ else
+ param_def = @methoddefcreator.collect_documentparameter(op)
+ end
+ # the first element of typedef in param_def is a String like
+ # "::SOAP::SOAPStruct". turn this String to a class.
+ param_def.collect { |io, name, typedef|
+ typedef[0] = Mapping.class_from_name(typedef[0])
+ [io, name, typedef]
+ }
+ end
+
+ def partqname(part)
+ if part.type
+ part.type
+ else
+ part.element
+ end
+ end
+
+ def param_def(type, name, klass, partqname)
+ [type, name, [klass, partqname.namespace, partqname.name]]
+ end
+
+ def filter_parts(partsdef, partssource)
+ parts = partsdef.split(/\s+/)
+ partssource.find_all { |part| parts.include?(part.name) }
+ end
+end
+
+
+class WSDLDriver
+ class << self
+ if RUBY_VERSION >= "1.7.0"
+ def __attr_proxy(symbol, assignable = false)
+ name = symbol.to_s
+ define_method(name) {
+ @servant.__send__(name)
+ }
+ if assignable
+ aname = name + '='
+ define_method(aname) { |rhs|
+ @servant.__send__(aname, rhs)
+ }
+ end
+ end
+ else
+ def __attr_proxy(symbol, assignable = false)
+ name = symbol.to_s
+ module_eval <<-EOS
+ def #{name}
+ @servant.#{name}
+ end
+ EOS
+ if assignable
+ module_eval <<-EOS
+ def #{name}=(value)
+ @servant.#{name} = value
+ end
+ EOS
+ end
+ end
+ end
+ end
+
+ __attr_proxy :options
+ __attr_proxy :headerhandler
+ __attr_proxy :streamhandler
+ __attr_proxy :test_loopback_response
+ __attr_proxy :endpoint_url, true
+ __attr_proxy :mapping_registry, true # for RPC unmarshal
+ __attr_proxy :wsdl_mapping_registry, true # for RPC marshal
+ __attr_proxy :default_encodingstyle, true
+ __attr_proxy :generate_explicit_type, true
+ __attr_proxy :allow_unqualified_element, true
+
+ def httpproxy
+ @servant.options["protocol.http.proxy"]
+ end
+
+ def httpproxy=(httpproxy)
+ @servant.options["protocol.http.proxy"] = httpproxy
+ end
+
+ def wiredump_dev
+ @servant.options["protocol.http.wiredump_dev"]
+ end
+
+ def wiredump_dev=(wiredump_dev)
+ @servant.options["protocol.http.wiredump_dev"] = wiredump_dev
+ end
+
+ def mandatorycharset
+ @servant.options["protocol.mandatorycharset"]
+ end
+
+ def mandatorycharset=(mandatorycharset)
+ @servant.options["protocol.mandatorycharset"] = mandatorycharset
+ end
+
+ def wiredump_file_base
+ @servant.options["protocol.wiredump_file_base"]
+ end
+
+ def wiredump_file_base=(wiredump_file_base)
+ @servant.options["protocol.wiredump_file_base"] = wiredump_file_base
+ end
+
+ def initialize(wsdl, port, logdev)
+ @servant = Servant__.new(self, wsdl, port, logdev)
+ end
+
+ def inspect
+ "#<#{self.class}:#{@servant.port.name}>"
+ end
+
+ def reset_stream
+ @servant.reset_stream
+ end
+
+ # Backward compatibility.
+ alias generateEncodeType= generate_explicit_type=
+
+ class Servant__
+ include SOAP
+
+ attr_reader :options
+ attr_reader :port
+
+ attr_accessor :soapaction
+ attr_accessor :default_encodingstyle
+ attr_accessor :allow_unqualified_element
+ attr_accessor :generate_explicit_type
+ attr_accessor :mapping_registry
+ attr_accessor :wsdl_mapping_registry
+
+ def initialize(host, wsdl, port, logdev)
+ @host = host
+ @wsdl = wsdl
+ @port = port
+ @logdev = logdev
+ @soapaction = nil
+ @options = setup_options
+ @default_encodingstyle = nil
+ @allow_unqualified_element = nil
+ @generate_explicit_type = false
+ @mapping_registry = nil # for rpc unmarshal
+ @wsdl_mapping_registry = nil # for rpc marshal
+ @wiredump_file_base = nil
+ @mandatorycharset = nil
+ @wsdl_elements = @wsdl.collect_elements
+ @wsdl_types = @wsdl.collect_complextypes + @wsdl.collect_simpletypes
+ @rpc_decode_typemap = @wsdl_types +
+ @wsdl.soap_rpc_complextypes(port.find_binding)
+ @wsdl_mapping_registry = Mapping::WSDLEncodedRegistry.new(
+ @rpc_decode_typemap)
+ @doc_mapper = Mapping::WSDLLiteralRegistry.new(
+ @wsdl_types, @wsdl_elements)
+ endpoint_url = @port.soap_address.location
+ # Convert a map which key is QName, to a Hash which key is String.
+ @operation = {}
+ @port.inputoperation_map.each do |op_name, op_info|
+ orgname = op_name.name
+ name = XSD::CodeGen::GenSupport.safemethodname(orgname)
+ @operation[name] = @operation[orgname] = op_info
+ add_method_interface(op_info)
+ end
+ @proxy = ::SOAP::RPC::Proxy.new(endpoint_url, @soapaction, @options)
+ end
+
+ def inspect
+ "#<#{self.class}:#{@proxy.inspect}>"
+ end
+
+ def endpoint_url
+ @proxy.endpoint_url
+ end
+
+ def endpoint_url=(endpoint_url)
+ @proxy.endpoint_url = endpoint_url
+ end
+
+ def headerhandler
+ @proxy.headerhandler
+ end
+
+ def streamhandler
+ @proxy.streamhandler
+ end
+
+ def test_loopback_response
+ @proxy.test_loopback_response
+ end
+
+ def reset_stream
+ @proxy.reset_stream
+ end
+
+ def rpc_call(name, *values)
+ set_wiredump_file_base(name)
+ unless op_info = @operation[name]
+ raise RuntimeError, "method: #{name} not defined"
+ end
+ req_header = create_request_header
+ req_body = create_request_body(op_info, *values)
+ reqopt = create_options({
+ :soapaction => op_info.soapaction || @soapaction})
+ resopt = create_options({
+ :decode_typemap => @rpc_decode_typemap})
+ env = @proxy.route(req_header, req_body, reqopt, resopt)
+ raise EmptyResponseError unless env
+ receive_headers(env.header)
+ begin
+ @proxy.check_fault(env.body)
+ rescue ::SOAP::FaultError => e
+ Mapping.fault2exception(e)
+ end
+ ret = env.body.response ?
+ Mapping.soap2obj(env.body.response, @mapping_registry) : nil
+ if env.body.outparams
+ outparams = env.body.outparams.collect { |outparam|
+ Mapping.soap2obj(outparam)
+ }
+ return [ret].concat(outparams)
+ else
+ return ret
+ end
+ end
+
+ # req_header: [[element, mustunderstand, encodingstyle(QName/String)], ...]
+ # req_body: SOAPBasetype/SOAPCompoundtype
+ def document_send(name, header_obj, body_obj)
+ set_wiredump_file_base(name)
+ unless op_info = @operation[name]
+ raise RuntimeError, "method: #{name} not defined"
+ end
+ req_header = header_obj ? header_from_obj(header_obj, op_info) : nil
+ req_body = body_from_obj(body_obj, op_info)
+ opt = create_options({
+ :soapaction => op_info.soapaction || @soapaction,
+ :decode_typemap => @wsdl_types})
+ env = @proxy.invoke(req_header, req_body, opt)
+ raise EmptyResponseError unless env
+ if env.body.fault
+ raise ::SOAP::FaultError.new(env.body.fault)
+ end
+ res_body_obj = env.body.response ?
+ Mapping.soap2obj(env.body.response, @mapping_registry) : nil
+ return env.header, res_body_obj
+ end
+
+ private
+
+ def create_options(hash = nil)
+ opt = {}
+ opt[:default_encodingstyle] = @default_encodingstyle
+ opt[:allow_unqualified_element] = @allow_unqualified_element
+ opt[:generate_explicit_type] = @generate_explicit_type
+ opt.update(hash) if hash
+ opt
+ end
+
+ def set_wiredump_file_base(name)
+ if @wiredump_file_base
+ @proxy.set_wiredump_file_base(@wiredump_file_base + "_#{name}")
+ end
+ end
+
+ def create_request_header
+ headers = @proxy.headerhandler.on_outbound
+ if headers.empty?
+ nil
+ else
+ h = SOAPHeader.new
+ headers.each do |header|
+ h.add(header.elename.name, header)
+ end
+ h
+ end
+ end
+
+ def receive_headers(headers)
+ @proxy.headerhandler.on_inbound(headers) if headers
+ end
+
+ def create_request_body(op_info, *values)
+ method = create_method_struct(op_info, *values)
+ SOAPBody.new(method)
+ end
+
+ def create_method_struct(op_info, *params)
+ parts_names = op_info.bodyparts.collect { |part| part.name }
+ obj = create_method_obj(parts_names, params)
+ method = Mapping.obj2soap(obj, @wsdl_mapping_registry, op_info.op_name)
+ if method.members.size != parts_names.size
+ new_method = SOAPStruct.new
+ method.each do |key, value|
+ if parts_names.include?(key)
+ new_method.add(key, value)
+ end
+ end
+ method = new_method
+ end
+ method.elename = op_info.op_name
+ method.type = XSD::QName.new # Request should not be typed.
+ method
+ end
+
+ def create_method_obj(names, params)
+ o = Object.new
+ idx = 0
+ while idx < params.length
+ o.instance_variable_set('@' + names[idx], params[idx])
+ idx += 1
+ end
+ o
+ end
+
+ def header_from_obj(obj, op_info)
+ if obj.is_a?(SOAPHeader)
+ obj
+ elsif op_info.headerparts.empty?
+ if obj.nil?
+ nil
+ else
+ raise RuntimeError.new("no header definition in schema: #{obj}")
+ end
+ elsif op_info.headerparts.size == 1
+ part = op_info.headerparts[0]
+ header = SOAPHeader.new()
+ header.add(headeritem_from_obj(obj, part.element || part.eletype))
+ header
+ else
+ header = SOAPHeader.new()
+ op_info.headerparts.each do |part|
+ child = Mapping.get_attribute(obj, part.name)
+ ele = headeritem_from_obj(child, part.element || part.eletype)
+ header.add(part.name, ele)
+ end
+ header
+ end
+ end
+
+ def headeritem_from_obj(obj, name)
+ if obj.nil?
+ SOAPElement.new(name)
+ elsif obj.is_a?(SOAPHeaderItem)
+ obj
+ else
+ Mapping.obj2soap(obj, @doc_mapper, name)
+ end
+ end
+
+ def body_from_obj(obj, op_info)
+ if obj.is_a?(SOAPBody)
+ obj
+ elsif op_info.bodyparts.empty?
+ if obj.nil?
+ nil
+ else
+ raise RuntimeError.new("no body found in schema")
+ end
+ elsif op_info.bodyparts.size == 1
+ part = op_info.bodyparts[0]
+ ele = bodyitem_from_obj(obj, part.element || part.type)
+ SOAPBody.new(ele)
+ else
+ body = SOAPBody.new
+ op_info.bodyparts.each do |part|
+ child = Mapping.get_attribute(obj, part.name)
+ ele = bodyitem_from_obj(child, part.element || part.type)
+ body.add(ele.elename.name, ele)
+ end
+ body
+ end
+ end
+
+ def bodyitem_from_obj(obj, name)
+ if obj.nil?
+ SOAPElement.new(name)
+ elsif obj.is_a?(SOAPElement)
+ obj
+ else
+ Mapping.obj2soap(obj, @doc_mapper, name)
+ end
+ end
+
+ def add_method_interface(op_info)
+ name = XSD::CodeGen::GenSupport.safemethodname(op_info.op_name.name)
+ orgname = op_info.op_name.name
+ parts_names = op_info.bodyparts.collect { |part| part.name }
+ case op_info.style
+ when :document
+ if orgname != name and orgname.capitalize == name.capitalize
+ add_document_method_interface(orgname, parts_names)
+ end
+ add_document_method_interface(name, parts_names)
+ when :rpc
+ if orgname != name and orgname.capitalize == name.capitalize
+ add_rpc_method_interface(orgname, parts_names)
+ end
+ add_rpc_method_interface(name, parts_names)
+ else
+ raise RuntimeError.new("unknown style: #{op_info.style}")
+ end
+ end
+
+ def add_rpc_method_interface(name, parts_names)
+ ::SOAP::Mapping.define_singleton_method(@host, name) do |*arg|
+ unless arg.size == parts_names.size
+ raise ArgumentError.new(
+ "wrong number of arguments (#{arg.size} for #{parts_names.size})")
+ end
+ @servant.rpc_call(name, *arg)
+ end
+ @host.method(name)
+ end
+
+ def add_document_method_interface(name, parts_names)
+ ::SOAP::Mapping.define_singleton_method(@host, name) do |h, b|
+ @servant.document_send(name, h, b)
+ end
+ @host.method(name)
+ end
+
+ def setup_options
+ if opt = Property.loadproperty(::SOAP::PropertyName)
+ opt = opt["client"]
+ end
+ opt ||= Property.new
+ opt.add_hook("protocol.mandatorycharset") do |key, value|
+ @mandatorycharset = value
+ end
+ opt.add_hook("protocol.wiredump_file_base") do |key, value|
+ @wiredump_file_base = value
+ end
+ opt["protocol.http.charset"] ||= XSD::Charset.xml_encoding_label
+ opt["protocol.http.proxy"] ||= Env::HTTP_PROXY
+ opt["protocol.http.no_proxy"] ||= Env::NO_PROXY
+ opt
+ end
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/sync.rb b/ruby_1_8_5/lib/sync.rb
new file mode 100644
index 0000000000..03b161c274
--- /dev/null
+++ b/ruby_1_8_5/lib/sync.rb
@@ -0,0 +1,311 @@
+#
+# sync.rb - 2 phase lock with counter
+# $Release Version: 1.0$
+# $Revision: 1.4 $
+# $Date: 2001/06/06 14:19:33 $
+# by Keiju ISHITSUKA(keiju@ishitsuka.com)
+#
+# --
+# Sync_m, Synchronizer_m
+# Usage:
+# obj.extend(Sync_m)
+# or
+# class Foo
+# include Sync_m
+# :
+# end
+#
+# Sync_m#sync_mode
+# Sync_m#sync_locked?, locked?
+# Sync_m#sync_shared?, shared?
+# Sync_m#sync_exclusive?, sync_exclusive?
+# Sync_m#sync_try_lock, try_lock
+# Sync_m#sync_lock, lock
+# Sync_m#sync_unlock, unlock
+#
+# Sync, Synchronicer:
+# include Sync_m
+# Usage:
+# sync = Sync.new
+#
+# Sync#mode
+# Sync#locked?
+# Sync#shared?
+# Sync#exclusive?
+# Sync#try_lock(mode) -- mode = :EX, :SH, :UN
+# Sync#lock(mode) -- mode = :EX, :SH, :UN
+# Sync#unlock
+# Sync#synchronize(mode) {...}
+#
+#
+
+unless defined? Thread
+ fail "Thread not available for this ruby interpreter"
+end
+
+module Sync_m
+ RCS_ID='-$Header: /src/ruby/lib/sync.rb,v 1.4 2001/06/06 14:19:33 keiju Exp $-'
+
+ # lock mode
+ UN = :UN
+ SH = :SH
+ EX = :EX
+
+ # exceptions
+ class Err < StandardError
+ def Err.Fail(*opt)
+ fail self, sprintf(self::Message, *opt)
+ end
+
+ class UnknownLocker < Err
+ Message = "Thread(%s) not locked."
+ def UnknownLocker.Fail(th)
+ super(th.inspect)
+ end
+ end
+
+ class LockModeFailer < Err
+ Message = "Unknown lock mode(%s)"
+ def LockModeFailer.Fail(mode)
+ if mode.id2name
+ mode = id2name
+ end
+ super(mode)
+ end
+ end
+ end
+
+ def Sync_m.define_aliases(cl)
+ cl.module_eval %q{
+ alias locked? sync_locked?
+ alias shared? sync_shared?
+ alias exclusive? sync_exclusive?
+ alias lock sync_lock
+ alias unlock sync_unlock
+ alias try_lock sync_try_lock
+ alias synchronize sync_synchronize
+ }
+ end
+
+ def Sync_m.append_features(cl)
+ super
+ unless cl.instance_of?(Module)
+ # do nothing for Modules
+ # make aliases and include the proper module.
+ define_aliases(cl)
+ end
+ end
+
+ def Sync_m.extend_object(obj)
+ super
+ obj.sync_extended
+ end
+
+ def sync_extended
+ unless (defined? locked? and
+ defined? shared? and
+ defined? exclusive? and
+ defined? lock and
+ defined? unlock and
+ defined? try_lock and
+ defined? synchronize)
+ Sync_m.define_aliases(class<<self;self;end)
+ end
+ sync_initialize
+ end
+
+ # accessing
+ def sync_locked?
+ sync_mode != UN
+ end
+
+ def sync_shared?
+ sync_mode == SH
+ end
+
+ def sync_exclusive?
+ sync_mode == EX
+ end
+
+ # locking methods.
+ def sync_try_lock(mode = EX)
+ return unlock if sync_mode == UN
+
+ Thread.critical = true
+ ret = sync_try_lock_sub(sync_mode)
+ Thread.critical = false
+ ret
+ end
+
+ def sync_lock(m = EX)
+ return unlock if m == UN
+
+ until (Thread.critical = true; sync_try_lock_sub(m))
+ if sync_sh_locker[Thread.current]
+ sync_upgrade_waiting.push [Thread.current, sync_sh_locker[Thread.current]]
+ sync_sh_locker.delete(Thread.current)
+ else
+ sync_waiting.push Thread.current
+ end
+ Thread.stop
+ end
+ Thread.critical = false
+ self
+ end
+
+ def sync_unlock(m = EX)
+ Thread.critical = true
+ if sync_mode == UN
+ Thread.critical = false
+ Err::UnknownLocker.Fail(Thread.current)
+ end
+
+ m = sync_mode if m == EX and sync_mode == SH
+
+ runnable = false
+ case m
+ when UN
+ Thread.critical = false
+ Err::UnknownLocker.Fail(Thread.current)
+
+ when EX
+ if sync_ex_locker == Thread.current
+ if (self.sync_ex_count = sync_ex_count - 1) == 0
+ self.sync_ex_locker = nil
+ if sync_sh_locker.include?(Thread.current)
+ self.sync_mode = SH
+ else
+ self.sync_mode = UN
+ end
+ runnable = true
+ end
+ else
+ Err::UnknownLocker.Fail(Thread.current)
+ end
+
+ when SH
+ if (count = sync_sh_locker[Thread.current]).nil?
+ Err::UnknownLocker.Fail(Thread.current)
+ else
+ if (sync_sh_locker[Thread.current] = count - 1) == 0
+ sync_sh_locker.delete(Thread.current)
+ if sync_sh_locker.empty? and sync_ex_count == 0
+ self.sync_mode = UN
+ runnable = true
+ end
+ end
+ end
+ end
+
+ if runnable
+ if sync_upgrade_waiting.size > 0
+ for k, v in sync_upgrade_waiting
+ sync_sh_locker[k] = v
+ end
+ wait = sync_upgrade_waiting
+ self.sync_upgrade_waiting = []
+ Thread.critical = false
+
+ for w, v in wait
+ w.run
+ end
+ else
+ wait = sync_waiting
+ self.sync_waiting = []
+ Thread.critical = false
+ for w in wait
+ w.run
+ end
+ end
+ end
+
+ Thread.critical = false
+ self
+ end
+
+ def sync_synchronize(mode = EX)
+ begin
+ sync_lock(mode)
+ yield
+ ensure
+ sync_unlock
+ end
+ end
+
+ attr :sync_mode, true
+
+ attr :sync_waiting, true
+ attr :sync_upgrade_waiting, true
+ attr :sync_sh_locker, true
+ attr :sync_ex_locker, true
+ attr :sync_ex_count, true
+
+ private
+
+ def sync_initialize
+ @sync_mode = UN
+ @sync_waiting = []
+ @sync_upgrade_waiting = []
+ @sync_sh_locker = Hash.new
+ @sync_ex_locker = nil
+ @sync_ex_count = 0
+ end
+
+ def initialize(*args)
+ sync_initialize
+ super
+ end
+
+ def sync_try_lock_sub(m)
+ case m
+ when SH
+ case sync_mode
+ when UN
+ self.sync_mode = m
+ sync_sh_locker[Thread.current] = 1
+ ret = true
+ when SH
+ count = 0 unless count = sync_sh_locker[Thread.current]
+ sync_sh_locker[Thread.current] = count + 1
+ ret = true
+ when EX
+ # in EX mode, lock will upgrade to EX lock
+ if sync_ex_locker == Thread.current
+ self.sync_ex_count = sync_ex_count + 1
+ ret = true
+ else
+ ret = false
+ end
+ end
+ when EX
+ if sync_mode == UN or
+ sync_mode == SH && sync_sh_locker.size == 1 && sync_sh_locker.include?(Thread.current)
+ self.sync_mode = m
+ self.sync_ex_locker = Thread.current
+ self.sync_ex_count = 1
+ ret = true
+ elsif sync_mode == EX && sync_ex_locker == Thread.current
+ self.sync_ex_count = sync_ex_count + 1
+ ret = true
+ else
+ ret = false
+ end
+ else
+ Thread.critical = false
+ Err::LockModeFailer.Fail mode
+ end
+ return ret
+ end
+end
+Synchronizer_m = Sync_m
+
+class Sync
+ #Sync_m.extend_class self
+ include Sync_m
+
+ def initialize
+ super
+ end
+
+end
+Synchronizer = Sync
diff --git a/ruby_1_8_5/lib/tempfile.rb b/ruby_1_8_5/lib/tempfile.rb
new file mode 100644
index 0000000000..a18c19446a
--- /dev/null
+++ b/ruby_1_8_5/lib/tempfile.rb
@@ -0,0 +1,193 @@
+#
+# tempfile - manipulates temporary files
+#
+# $Id: tempfile.rb,v 1.19.2.5 2006/06/10 23:27:38 matz Exp $
+#
+
+require 'delegate'
+require 'tmpdir'
+
+# A class for managing temporary files. This library is written to be
+# thread safe.
+class Tempfile < DelegateClass(File)
+ MAX_TRY = 10
+ @@cleanlist = []
+
+ # Creates a temporary file of mode 0600 in the temporary directory
+ # whose name is basename.pid.n and opens with mode "w+". A Tempfile
+ # object works just like a File object.
+ #
+ # If tmpdir is omitted, the temporary directory is determined by
+ # Dir::tmpdir provided by 'tmpdir.rb'.
+ # When $SAFE > 0 and the given tmpdir is tainted, it uses
+ # /tmp. (Note that ENV values are tainted by default)
+ def initialize(basename, tmpdir=Dir::tmpdir)
+ if $SAFE > 0 and tmpdir.tainted?
+ tmpdir = '/tmp'
+ end
+
+ lock = nil
+ n = failure = 0
+
+ begin
+ Thread.critical = true
+
+ begin
+ tmpname = File.join(tmpdir, make_tmpname(basename, n))
+ lock = tmpname + '.lock'
+ n += 1
+ end while @@cleanlist.include?(tmpname) or
+ File.exist?(lock) or File.exist?(tmpname)
+
+ Dir.mkdir(lock)
+ rescue
+ failure += 1
+ retry if failure < MAX_TRY
+ raise "cannot generate tempfile `%s'" % tmpname
+ ensure
+ Thread.critical = false
+ end
+
+ @data = [tmpname]
+ @clean_proc = Tempfile.callback(@data)
+ ObjectSpace.define_finalizer(self, @clean_proc)
+
+ @tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600)
+ @tmpname = tmpname
+ @@cleanlist << @tmpname
+ @data[1] = @tmpfile
+ @data[2] = @@cleanlist
+
+ super(@tmpfile)
+
+ # Now we have all the File/IO methods defined, you must not
+ # carelessly put bare puts(), etc. after this.
+
+ Dir.rmdir(lock)
+ end
+
+ def make_tmpname(basename, n)
+ sprintf('%s.%d.%d', basename, $$, n)
+ end
+ private :make_tmpname
+
+ # Opens or reopens the file with mode "r+".
+ def open
+ @tmpfile.close if @tmpfile
+ @tmpfile = File.open(@tmpname, 'r+')
+ @data[1] = @tmpfile
+ __setobj__(@tmpfile)
+ end
+
+ def _close # :nodoc:
+ @tmpfile.close if @tmpfile
+ @data[1] = @tmpfile = nil
+ end
+ protected :_close
+
+ # Closes the file. If the optional flag is true, unlinks the file
+ # after closing.
+ #
+ # If you don't explicitly unlink the temporary file, the removal
+ # will be delayed until the object is finalized.
+ def close(unlink_now=false)
+ if unlink_now
+ close!
+ else
+ _close
+ end
+ end
+
+ # Closes and unlinks the file.
+ def close!
+ _close
+ @clean_proc.call
+ ObjectSpace.undefine_finalizer(self)
+ end
+
+ # Unlinks the file. On UNIX-like systems, it is often a good idea
+ # to unlink a temporary file immediately after creating and opening
+ # it, because it leaves other programs zero chance to access the
+ # file.
+ def unlink
+ # keep this order for thread safeness
+ begin
+ File.unlink(@tmpname) if File.exist?(@tmpname)
+ @@cleanlist.delete(@tmpname)
+ @data = @tmpname = nil
+ ObjectSpace.undefine_finalizer(self)
+ rescue Errno::EACCES
+ # may not be able to unlink on Windows; just ignore
+ end
+ end
+ alias delete unlink
+
+ # Returns the full path name of the temporary file.
+ def path
+ @tmpname
+ end
+
+ # Returns the size of the temporary file. As a side effect, the IO
+ # buffer is flushed before determining the size.
+ def size
+ if @tmpfile
+ @tmpfile.flush
+ @tmpfile.stat.size
+ else
+ 0
+ end
+ end
+ alias length size
+
+ class << self
+ def callback(data) # :nodoc:
+ pid = $$
+ lambda{
+ if pid == $$
+ path, tmpfile, cleanlist = *data
+
+ print "removing ", path, "..." if $DEBUG
+
+ tmpfile.close if tmpfile
+
+ # keep this order for thread safeness
+ File.unlink(path) if File.exist?(path)
+ cleanlist.delete(path) if cleanlist
+
+ print "done\n" if $DEBUG
+ end
+ }
+ end
+
+ # If no block is given, this is a synonym for new().
+ #
+ # If a block is given, it will be passed tempfile as an argument,
+ # and the tempfile will automatically be closed when the block
+ # terminates. In this case, open() returns nil.
+ def open(*args)
+ tempfile = new(*args)
+
+ if block_given?
+ begin
+ yield(tempfile)
+ ensure
+ tempfile.close
+ end
+
+ nil
+ else
+ tempfile
+ end
+ end
+ end
+end
+
+if __FILE__ == $0
+# $DEBUG = true
+ f = Tempfile.new("foo")
+ f.print("foo\n")
+ f.close
+ f.open
+ p f.gets # => "foo\n"
+ f.close!
+end
diff --git a/ruby_1_8_5/lib/test/unit.rb b/ruby_1_8_5/lib/test/unit.rb
new file mode 100644
index 0000000000..b71f644566
--- /dev/null
+++ b/ruby_1_8_5/lib/test/unit.rb
@@ -0,0 +1,280 @@
+require 'test/unit/testcase'
+require 'test/unit/autorunner'
+
+module Test # :nodoc:
+ #
+ # = Test::Unit - Ruby Unit Testing Framework
+ #
+ # == Introduction
+ #
+ # Unit testing is making waves all over the place, largely due to the
+ # fact that it is a core practice of XP. While XP is great, unit testing
+ # has been around for a long time and has always been a good idea. One
+ # of the keys to good unit testing, though, is not just writing tests,
+ # but having tests. What's the difference? Well, if you just _write_ a
+ # test and throw it away, you have no guarantee that something won't
+ # change later which breaks your code. If, on the other hand, you _have_
+ # tests (obviously you have to write them first), and run them as often
+ # as possible, you slowly build up a wall of things that cannot break
+ # without you immediately knowing about it. This is when unit testing
+ # hits its peak usefulness.
+ #
+ # Enter Test::Unit, a framework for unit testing in Ruby, helping you to
+ # design, debug and evaluate your code by making it easy to write and
+ # have tests for it.
+ #
+ #
+ # == Notes
+ #
+ # Test::Unit has grown out of and superceded Lapidary.
+ #
+ #
+ # == Feedback
+ #
+ # I like (and do my best to practice) XP, so I value early releases,
+ # user feedback, and clean, simple, expressive code. There is always
+ # room for improvement in everything I do, and Test::Unit is no
+ # exception. Please, let me know what you think of Test::Unit as it
+ # stands, and what you'd like to see expanded/changed/improved/etc. If
+ # you find a bug, let me know ASAP; one good way to let me know what the
+ # bug is is to submit a new test that catches it :-) Also, I'd love to
+ # hear about any successes you have with Test::Unit, and any
+ # documentation you might add will be greatly appreciated. My contact
+ # info is below.
+ #
+ #
+ # == Contact Information
+ #
+ # A lot of discussion happens about Ruby in general on the ruby-talk
+ # mailing list (http://www.ruby-lang.org/en/ml.html), and you can ask
+ # any questions you might have there. I monitor the list, as do many
+ # other helpful Rubyists, and you're sure to get a quick answer. Of
+ # course, you're also welcome to email me (Nathaniel Talbott) directly
+ # at mailto:testunit@talbott.ws, and I'll do my best to help you out.
+ #
+ #
+ # == Credits
+ #
+ # I'd like to thank...
+ #
+ # Matz, for a great language!
+ #
+ # Masaki Suketa, for his work on RubyUnit, which filled a vital need in
+ # the Ruby world for a very long time. I'm also grateful for his help in
+ # polishing Test::Unit and getting the RubyUnit compatibility layer
+ # right. His graciousness in allowing Test::Unit to supercede RubyUnit
+ # continues to be a challenge to me to be more willing to defer my own
+ # rights.
+ #
+ # Ken McKinlay, for his interest and work on unit testing, and for his
+ # willingness to dialog about it. He was also a great help in pointing
+ # out some of the holes in the RubyUnit compatibility layer.
+ #
+ # Dave Thomas, for the original idea that led to the extremely simple
+ # "require 'test/unit'", plus his code to improve it even more by
+ # allowing the selection of tests from the command-line. Also, without
+ # RDoc, the documentation for Test::Unit would stink a lot more than it
+ # does now.
+ #
+ # Everyone who's helped out with bug reports, feature ideas,
+ # encouragement to continue, etc. It's a real privilege to be a part of
+ # the Ruby community.
+ #
+ # The guys at RoleModel Software, for putting up with me repeating, "But
+ # this would be so much easier in Ruby!" whenever we're coding in Java.
+ #
+ # My Creator, for giving me life, and giving it more abundantly.
+ #
+ #
+ # == License
+ #
+ # Test::Unit is copyright (c) 2000-2003 Nathaniel Talbott. It is free
+ # software, and is distributed under the Ruby license. See the COPYING
+ # file in the standard Ruby distribution for details.
+ #
+ #
+ # == Warranty
+ #
+ # This software is provided "as is" and without any express or
+ # implied warranties, including, without limitation, the implied
+ # warranties of merchantibility and fitness for a particular
+ # purpose.
+ #
+ #
+ # == Author
+ #
+ # Nathaniel Talbott.
+ # Copyright (c) 2000-2003, Nathaniel Talbott
+ #
+ # ----
+ #
+ # = Usage
+ #
+ # The general idea behind unit testing is that you write a _test_
+ # _method_ that makes certain _assertions_ about your code, working
+ # against a _test_ _fixture_. A bunch of these _test_ _methods_ are
+ # bundled up into a _test_ _suite_ and can be run any time the
+ # developer wants. The results of a run are gathered in a _test_
+ # _result_ and displayed to the user through some UI. So, lets break
+ # this down and see how Test::Unit provides each of these necessary
+ # pieces.
+ #
+ #
+ # == Assertions
+ #
+ # These are the heart of the framework. Think of an assertion as a
+ # statement of expected outcome, i.e. "I assert that x should be equal
+ # to y". If, when the assertion is executed, it turns out to be
+ # correct, nothing happens, and life is good. If, on the other hand,
+ # your assertion turns out to be false, an error is propagated with
+ # pertinent information so that you can go back and make your
+ # assertion succeed, and, once again, life is good. For an explanation
+ # of the current assertions, see Test::Unit::Assertions.
+ #
+ #
+ # == Test Method & Test Fixture
+ #
+ # Obviously, these assertions have to be called within a context that
+ # knows about them and can do something meaningful with their
+ # pass/fail value. Also, it's handy to collect a bunch of related
+ # tests, each test represented by a method, into a common test class
+ # that knows how to run them. The tests will be in a separate class
+ # from the code they're testing for a couple of reasons. First of all,
+ # it allows your code to stay uncluttered with test code, making it
+ # easier to maintain. Second, it allows the tests to be stripped out
+ # for deployment, since they're really there for you, the developer,
+ # and your users don't need them. Third, and most importantly, it
+ # allows you to set up a common test fixture for your tests to run
+ # against.
+ #
+ # What's a test fixture? Well, tests do not live in a vacuum; rather,
+ # they're run against the code they are testing. Often, a collection
+ # of tests will run against a common set of data, also called a
+ # fixture. If they're all bundled into the same test class, they can
+ # all share the setting up and tearing down of that data, eliminating
+ # unnecessary duplication and making it much easier to add related
+ # tests.
+ #
+ # Test::Unit::TestCase wraps up a collection of test methods together
+ # and allows you to easily set up and tear down the same test fixture
+ # for each test. This is done by overriding #setup and/or #teardown,
+ # which will be called before and after each test method that is
+ # run. The TestCase also knows how to collect the results of your
+ # assertions into a Test::Unit::TestResult, which can then be reported
+ # back to you... but I'm getting ahead of myself. To write a test,
+ # follow these steps:
+ #
+ # * Make sure Test::Unit is in your library path.
+ # * require 'test/unit' in your test script.
+ # * Create a class that subclasses Test::Unit::TestCase.
+ # * Add a method that begins with "test" to your class.
+ # * Make assertions in your test method.
+ # * Optionally define #setup and/or #teardown to set up and/or tear
+ # down your common test fixture.
+ # * You can now run your test as you would any other Ruby
+ # script... try it and see!
+ #
+ # A really simple test might look like this (#setup and #teardown are
+ # commented out to indicate that they are completely optional):
+ #
+ # require 'test/unit'
+ #
+ # class TC_MyTest < Test::Unit::TestCase
+ # # def setup
+ # # end
+ #
+ # # def teardown
+ # # end
+ #
+ # def test_fail
+ # assert(false, 'Assertion was false.')
+ # end
+ # end
+ #
+ #
+ # == Test Runners
+ #
+ # So, now you have this great test class, but you still need a way to
+ # run it and view any failures that occur during the run. This is
+ # where Test::Unit::UI::Console::TestRunner (and others, such as
+ # Test::Unit::UI::GTK::TestRunner) comes into play. The console test
+ # runner is automatically invoked for you if you require 'test/unit'
+ # and simply run the file. To use another runner, or to manually
+ # invoke a runner, simply call its run class method and pass in an
+ # object that responds to the suite message with a
+ # Test::Unit::TestSuite. This can be as simple as passing in your
+ # TestCase class (which has a class suite method). It might look
+ # something like this:
+ #
+ # require 'test/unit/ui/console/testrunner'
+ # Test::Unit::UI::Console::TestRunner.run(TC_MyTest)
+ #
+ #
+ # == Test Suite
+ #
+ # As more and more unit tests accumulate for a given project, it
+ # becomes a real drag running them one at a time, and it also
+ # introduces the potential to overlook a failing test because you
+ # forget to run it. Suddenly it becomes very handy that the
+ # TestRunners can take any object that returns a Test::Unit::TestSuite
+ # in response to a suite method. The TestSuite can, in turn, contain
+ # other TestSuites or individual tests (typically created by a
+ # TestCase). In other words, you can easily wrap up a group of
+ # TestCases and TestSuites like this:
+ #
+ # require 'test/unit/testsuite'
+ # require 'tc_myfirsttests'
+ # require 'tc_moretestsbyme'
+ # require 'ts_anothersetoftests'
+ #
+ # class TS_MyTests
+ # def self.suite
+ # suite = Test::Unit::TestSuite.new
+ # suite << TC_MyFirstTests.suite
+ # suite << TC_MoreTestsByMe.suite
+ # suite << TS_AnotherSetOfTests.suite
+ # return suite
+ # end
+ # end
+ # Test::Unit::UI::Console::TestRunner.run(TS_MyTests)
+ #
+ # Now, this is a bit cumbersome, so Test::Unit does a little bit more
+ # for you, by wrapping these up automatically when you require
+ # 'test/unit'. What does this mean? It means you could write the above
+ # test case like this instead:
+ #
+ # require 'test/unit'
+ # require 'tc_myfirsttests'
+ # require 'tc_moretestsbyme'
+ # require 'ts_anothersetoftests'
+ #
+ # Test::Unit is smart enough to find all the test cases existing in
+ # the ObjectSpace and wrap them up into a suite for you. It then runs
+ # the dynamic suite using the console TestRunner.
+ #
+ #
+ # == Questions?
+ #
+ # I'd really like to get feedback from all levels of Ruby
+ # practitioners about typos, grammatical errors, unclear statements,
+ # missing points, etc., in this document (or any other).
+ #
+
+ module Unit
+ # If set to false Test::Unit will not automatically run at exit.
+ def self.run=(flag)
+ @run = flag
+ end
+
+ # Automatically run tests at exit?
+ def self.run?
+ @run ||= false
+ end
+ end
+end
+
+at_exit do
+ unless $! || Test::Unit.run?
+ exit Test::Unit::AutoRunner.run
+ end
+end
diff --git a/ruby_1_8_5/lib/test/unit/assertionfailederror.rb b/ruby_1_8_5/lib/test/unit/assertionfailederror.rb
new file mode 100644
index 0000000000..a21e4b5870
--- /dev/null
+++ b/ruby_1_8_5/lib/test/unit/assertionfailederror.rb
@@ -0,0 +1,14 @@
+#--
+#
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+module Test
+ module Unit
+
+ # Thrown by Test::Unit::Assertions when an assertion fails.
+ class AssertionFailedError < StandardError
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/test/unit/assertions.rb b/ruby_1_8_5/lib/test/unit/assertions.rb
new file mode 100644
index 0000000000..aa97799de8
--- /dev/null
+++ b/ruby_1_8_5/lib/test/unit/assertions.rb
@@ -0,0 +1,622 @@
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+require 'test/unit/assertionfailederror'
+require 'test/unit/util/backtracefilter'
+
+module Test
+ module Unit
+
+ ##
+ # Test::Unit::Assertions contains the standard Test::Unit assertions.
+ # Assertions is included in Test::Unit::TestCase.
+ #
+ # To include it in your own code and use its functionality, you simply
+ # need to rescue Test::Unit::AssertionFailedError. Additionally you may
+ # override add_assertion to get notified whenever an assertion is made.
+ #
+ # Notes:
+ # * The message to each assertion, if given, will be propagated with the
+ # failure.
+ # * It is easy to add your own assertions based on assert_block().
+ #
+ # = Example Custom Assertion
+ #
+ # def deny(boolean, message = nil)
+ # message = build_message message, '<?> is not false or nil.', boolean
+ # assert_block message do
+ # not boolean
+ # end
+ # end
+
+ module Assertions
+
+ ##
+ # The assertion upon which all other assertions are based. Passes if the
+ # block yields true.
+ #
+ # Example:
+ # assert_block "Couldn't do the thing" do
+ # do_the_thing
+ # end
+
+ public
+ def assert_block(message="assert_block failed.") # :yields:
+ _wrap_assertion do
+ if (! yield)
+ raise AssertionFailedError.new(message.to_s)
+ end
+ end
+ end
+
+ ##
+ # Asserts that +boolean+ is not false or nil.
+ #
+ # Example:
+ # assert [1, 2].include?(5)
+
+ public
+ def assert(boolean, message=nil)
+ _wrap_assertion do
+ assert_block("assert should not be called with a block.") { !block_given? }
+ assert_block(build_message(message, "<?> is not true.", boolean)) { boolean }
+ end
+ end
+
+ ##
+ # Passes if +expected+ == +actual.
+ #
+ # Note that the ordering of arguments is important, since a helpful
+ # error message is generated when this one fails that tells you the
+ # values of expected and actual.
+ #
+ # Example:
+ # assert_equal 'MY STRING', 'my string'.upcase
+
+ public
+ def assert_equal(expected, actual, message=nil)
+ full_message = build_message(message, <<EOT, expected, actual)
+<?> expected but was
+<?>.
+EOT
+ assert_block(full_message) { expected == actual }
+ end
+
+ private
+ def _check_exception_class(args) # :nodoc:
+ args.partition do |klass|
+ next if klass.instance_of?(Module)
+ assert(Exception >= klass, "Should expect a class of exception, #{klass}")
+ true
+ end
+ end
+
+ private
+ def _expected_exception?(actual_exception, exceptions, modules) # :nodoc:
+ exceptions.include?(actual_exception.class) or
+ modules.any? {|mod| actual_exception.is_a?(mod)}
+ end
+
+ ##
+ # Passes if the block raises one of the given exceptions.
+ #
+ # Example:
+ # assert_raise RuntimeError, LoadError do
+ # raise 'Boom!!!'
+ # end
+
+ public
+ def assert_raise(*args)
+ _wrap_assertion do
+ if Module === args.last
+ message = ""
+ else
+ message = args.pop
+ end
+ exceptions, modules = _check_exception_class(args)
+ expected = args.size == 1 ? args.first : args
+ actual_exception = nil
+ full_message = build_message(message, "<?> exception expected but none was thrown.", expected)
+ assert_block(full_message) do
+ begin
+ yield
+ rescue Exception => actual_exception
+ break
+ end
+ false
+ end
+ full_message = build_message(message, "<?> exception expected but was\n?", expected, actual_exception)
+ assert_block(full_message) {_expected_exception?(actual_exception, exceptions, modules)}
+ actual_exception
+ end
+ end
+
+ ##
+ # Alias of assert_raise.
+ #
+ # Will be deprecated in 1.9, and removed in 2.0.
+
+ public
+ def assert_raises(*args, &block)
+ assert_raise(*args, &block)
+ end
+
+ ##
+ # Passes if +object+ .instance_of? +klass+
+ #
+ # Example:
+ # assert_instance_of String, 'foo'
+
+ public
+ def assert_instance_of(klass, object, message="")
+ _wrap_assertion do
+ assert_equal(Class, klass.class, "assert_instance_of takes a Class as its first argument")
+ full_message = build_message(message, <<EOT, object, klass, object.class)
+<?> expected to be an instance of
+<?> but was
+<?>.
+EOT
+ assert_block(full_message){object.instance_of?(klass)}
+ end
+ end
+
+ ##
+ # Passes if +object+ is nil.
+ #
+ # Example:
+ # assert_nil [1, 2].uniq!
+
+ public
+ def assert_nil(object, message="")
+ assert_equal(nil, object, message)
+ end
+
+ ##
+ # Passes if +object+ .kind_of? +klass+
+ #
+ # Example:
+ # assert_kind_of Object, 'foo'
+
+ public
+ def assert_kind_of(klass, object, message="")
+ _wrap_assertion do
+ assert(klass.kind_of?(Module), "The first parameter to assert_kind_of should be a kind_of Module.")
+ full_message = build_message(message, "<?>\nexpected to be kind_of\\?\n<?> but was\n<?>.", object, klass, object.class)
+ assert_block(full_message){object.kind_of?(klass)}
+ end
+ end
+
+ ##
+ # Passes if +object+ .respond_to? +method+
+ #
+ # Example:
+ # assert_respond_to 'bugbear', :slice
+
+ public
+ def assert_respond_to(object, method, message="")
+ _wrap_assertion do
+ full_message = build_message(nil, "<?>\ngiven as the method name argument to #assert_respond_to must be a Symbol or #respond_to\\?(:to_str).", method)
+
+ assert_block(full_message) do
+ method.kind_of?(Symbol) || method.respond_to?(:to_str)
+ end
+ full_message = build_message(message, <<EOT, object, object.class, method)
+<?>
+of type <?>
+expected to respond_to\\?<?>.
+EOT
+ assert_block(full_message) { object.respond_to?(method) }
+ end
+ end
+
+ ##
+ # Passes if +string+ =~ +pattern+.
+ #
+ # Example:
+ # assert_match(/\d+/, 'five, 6, seven')
+
+ public
+ def assert_match(pattern, string, message="")
+ _wrap_assertion do
+ pattern = case(pattern)
+ when String
+ Regexp.new(Regexp.escape(pattern))
+ else
+ pattern
+ end
+ full_message = build_message(message, "<?> expected to be =~\n<?>.", string, pattern)
+ assert_block(full_message) { string =~ pattern }
+ end
+ end
+
+ ##
+ # Passes if +actual+ .equal? +expected+ (i.e. they are the same
+ # instance).
+ #
+ # Example:
+ # o = Object.new
+ # assert_same o, o
+
+ public
+ def assert_same(expected, actual, message="")
+ full_message = build_message(message, <<EOT, expected, expected.__id__, actual, actual.__id__)
+<?>
+with id <?> expected to be equal\\? to
+<?>
+with id <?>.
+EOT
+ assert_block(full_message) { actual.equal?(expected) }
+ end
+
+ ##
+ # Compares the +object1+ with +object2+ using +operator+.
+ #
+ # Passes if object1.__send__(operator, object2) is true.
+ #
+ # Example:
+ # assert_operator 5, :>=, 4
+
+ public
+ def assert_operator(object1, operator, object2, message="")
+ _wrap_assertion do
+ full_message = build_message(nil, "<?>\ngiven as the operator for #assert_operator must be a Symbol or #respond_to\\?(:to_str).", operator)
+ assert_block(full_message){operator.kind_of?(Symbol) || operator.respond_to?(:to_str)}
+ full_message = build_message(message, <<EOT, object1, AssertionMessage.literal(operator), object2)
+<?> expected to be
+?
+<?>.
+EOT
+ assert_block(full_message) { object1.__send__(operator, object2) }
+ end
+ end
+
+ ##
+ # Passes if block does not raise an exception.
+ #
+ # Example:
+ # assert_nothing_raised do
+ # [1, 2].uniq
+ # end
+
+ public
+ def assert_nothing_raised(*args)
+ _wrap_assertion do
+ if Module === args.last
+ message = ""
+ else
+ message = args.pop
+ end
+ exceptions, modules = _check_exception_class(args)
+ begin
+ yield
+ rescue Exception => e
+ if ((args.empty? && !e.instance_of?(AssertionFailedError)) ||
+ _expected_exception?(e, exceptions, modules))
+ assert_block(build_message(message, "Exception raised:\n?", e)){false}
+ else
+ raise
+ end
+ end
+ nil
+ end
+ end
+
+ ##
+ # Flunk always fails.
+ #
+ # Example:
+ # flunk 'Not done testing yet.'
+
+ public
+ def flunk(message="Flunked")
+ assert_block(build_message(message)){false}
+ end
+
+ ##
+ # Passes if ! +actual+ .equal? +expected+
+ #
+ # Example:
+ # assert_not_same Object.new, Object.new
+
+ public
+ def assert_not_same(expected, actual, message="")
+ full_message = build_message(message, <<EOT, expected, expected.__id__, actual, actual.__id__)
+<?>
+with id <?> expected to not be equal\\? to
+<?>
+with id <?>.
+EOT
+ assert_block(full_message) { !actual.equal?(expected) }
+ end
+
+ ##
+ # Passes if +expected+ != +actual+
+ #
+ # Example:
+ # assert_not_equal 'some string', 5
+
+ public
+ def assert_not_equal(expected, actual, message="")
+ full_message = build_message(message, "<?> expected to be != to\n<?>.", expected, actual)
+ assert_block(full_message) { expected != actual }
+ end
+
+ ##
+ # Passes if ! +object+ .nil?
+ #
+ # Example:
+ # assert_not_nil '1 two 3'.sub!(/two/, '2')
+
+ public
+ def assert_not_nil(object, message="")
+ full_message = build_message(message, "<?> expected to not be nil.", object)
+ assert_block(full_message){!object.nil?}
+ end
+
+ ##
+ # Passes if +regexp+ !~ +string+
+ #
+ # Example:
+ # assert_no_match(/two/, 'one 2 three')
+
+ public
+ def assert_no_match(regexp, string, message="")
+ _wrap_assertion do
+ assert_instance_of(Regexp, regexp, "The first argument to assert_no_match should be a Regexp.")
+ full_message = build_message(message, "<?> expected to not match\n<?>.", regexp, string)
+ assert_block(full_message) { regexp !~ string }
+ end
+ end
+
+ UncaughtThrow = {NameError => /^uncaught throw \`(.+)\'$/,
+ ThreadError => /^uncaught throw \`(.+)\' in thread /} #`
+
+ ##
+ # Passes if the block throws +expected_symbol+
+ #
+ # Example:
+ # assert_throws :done do
+ # throw :done
+ # end
+
+ public
+ def assert_throws(expected_symbol, message="", &proc)
+ _wrap_assertion do
+ assert_instance_of(Symbol, expected_symbol, "assert_throws expects the symbol that should be thrown for its first argument")
+ assert_block("Should have passed a block to assert_throws."){block_given?}
+ caught = true
+ begin
+ catch(expected_symbol) do
+ proc.call
+ caught = false
+ end
+ full_message = build_message(message, "<?> should have been thrown.", expected_symbol)
+ assert_block(full_message){caught}
+ rescue NameError, ThreadError => error
+ if UncaughtThrow[error.class] !~ error.message
+ raise error
+ end
+ full_message = build_message(message, "<?> expected to be thrown but\n<?> was thrown.", expected_symbol, $1.intern)
+ flunk(full_message)
+ end
+ end
+ end
+
+ ##
+ # Passes if block does not throw anything.
+ #
+ # Example:
+ # assert_nothing_thrown do
+ # [1, 2].uniq
+ # end
+
+ public
+ def assert_nothing_thrown(message="", &proc)
+ _wrap_assertion do
+ assert(block_given?, "Should have passed a block to assert_nothing_thrown")
+ begin
+ proc.call
+ rescue NameError, ThreadError => error
+ if UncaughtThrow[error.class] !~ error.message
+ raise error
+ end
+ full_message = build_message(message, "<?> was thrown when nothing was expected", $1.intern)
+ flunk(full_message)
+ end
+ assert(true, "Expected nothing to be thrown")
+ end
+ end
+
+ ##
+ # Passes if +expected_float+ and +actual_float+ are equal
+ # within +delta+ tolerance.
+ #
+ # Example:
+ # assert_in_delta 0.05, (50000.0 / 10**6), 0.00001
+
+ public
+ def assert_in_delta(expected_float, actual_float, delta, message="")
+ _wrap_assertion do
+ {expected_float => "first float", actual_float => "second float", delta => "delta"}.each do |float, name|
+ assert_respond_to(float, :to_f, "The arguments must respond to to_f; the #{name} did not")
+ end
+ assert_operator(delta, :>=, 0.0, "The delta should not be negative")
+ full_message = build_message(message, <<EOT, expected_float, actual_float, delta)
+<?> and
+<?> expected to be within
+<?> of each other.
+EOT
+ assert_block(full_message) { (expected_float.to_f - actual_float.to_f).abs <= delta.to_f }
+ end
+ end
+
+ ##
+ # Passes if the method send returns a true value.
+ #
+ # +send_array+ is composed of:
+ # * A receiver
+ # * A method
+ # * Arguments to the method
+ #
+ # Example:
+ # assert_send [[1, 2], :include?, 4]
+
+ public
+ def assert_send(send_array, message="")
+ _wrap_assertion do
+ assert_instance_of(Array, send_array, "assert_send requires an array of send information")
+ assert(send_array.size >= 2, "assert_send requires at least a receiver and a message name")
+ full_message = build_message(message, <<EOT, send_array[0], AssertionMessage.literal(send_array[1].to_s), send_array[2..-1])
+<?> expected to respond to
+<?(?)> with a true value.
+EOT
+ assert_block(full_message) { send_array[0].__send__(send_array[1], *send_array[2..-1]) }
+ end
+ end
+
+ ##
+ # Builds a failure message. +head+ is added before the +template+ and
+ # +arguments+ replaces the '?'s positionally in the template.
+
+ public
+ def build_message(head, template=nil, *arguments)
+ template &&= template.chomp
+ return AssertionMessage.new(head, template, arguments)
+ end
+
+ private
+ def _wrap_assertion
+ @_assertion_wrapped ||= false
+ unless (@_assertion_wrapped)
+ @_assertion_wrapped = true
+ begin
+ add_assertion
+ return yield
+ ensure
+ @_assertion_wrapped = false
+ end
+ else
+ return yield
+ end
+ end
+
+ ##
+ # Called whenever an assertion is made. Define this in classes that
+ # include Test::Unit::Assertions to record assertion counts.
+
+ private
+ def add_assertion
+ end
+
+ ##
+ # Select whether or not to use the pretty-printer. If this option is set
+ # to false before any assertions are made, pp.rb will not be required.
+
+ public
+ def self.use_pp=(value)
+ AssertionMessage.use_pp = value
+ end
+
+ # :stopdoc:
+
+ class AssertionMessage
+ @use_pp = true
+ class << self
+ attr_accessor :use_pp
+ end
+
+ class Literal
+ def initialize(value)
+ @value = value
+ end
+
+ def inspect
+ @value.to_s
+ end
+ end
+
+ class Template
+ def self.create(string)
+ parts = (string ? string.scan(/(?=[^\\])\?|(?:\\\?|[^\?])+/m) : [])
+ self.new(parts)
+ end
+
+ attr_reader :count
+
+ def initialize(parts)
+ @parts = parts
+ @count = parts.find_all{|e| e == '?'}.size
+ end
+
+ def result(parameters)
+ raise "The number of parameters does not match the number of substitutions." if(parameters.size != count)
+ params = parameters.dup
+ @parts.collect{|e| e == '?' ? params.shift : e.gsub(/\\\?/m, '?')}.join('')
+ end
+ end
+
+ def self.literal(value)
+ Literal.new(value)
+ end
+
+ include Util::BacktraceFilter
+
+ def initialize(head, template_string, parameters)
+ @head = head
+ @template_string = template_string
+ @parameters = parameters
+ end
+
+ def convert(object)
+ case object
+ when Exception
+ <<EOM.chop
+Class: <#{convert(object.class)}>
+Message: <#{convert(object.message)}>
+---Backtrace---
+#{filter_backtrace(object.backtrace).join("\n")}
+---------------
+EOM
+ else
+ if(self.class.use_pp)
+ begin
+ require 'pp'
+ rescue LoadError
+ self.class.use_pp = false
+ return object.inspect
+ end unless(defined?(PP))
+ PP.pp(object, '').chomp
+ else
+ object.inspect
+ end
+ end
+ end
+
+ def template
+ @template ||= Template.create(@template_string)
+ end
+
+ def add_period(string)
+ (string =~ /\.\Z/ ? string : string + '.')
+ end
+
+ def to_s
+ message_parts = []
+ if (@head)
+ head = @head.to_s
+ unless(head.empty?)
+ message_parts << add_period(head)
+ end
+ end
+ tail = template.result(@parameters.collect{|e| convert(e)})
+ message_parts << tail unless(tail.empty?)
+ message_parts.join("\n")
+ end
+ end
+
+ # :startdoc:
+
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/test/unit/autorunner.rb b/ruby_1_8_5/lib/test/unit/autorunner.rb
new file mode 100644
index 0000000000..d9720daa49
--- /dev/null
+++ b/ruby_1_8_5/lib/test/unit/autorunner.rb
@@ -0,0 +1,204 @@
+require 'test/unit'
+require 'test/unit/ui/testrunnerutilities'
+require 'optparse'
+
+module Test
+ module Unit
+ class AutoRunner
+ def self.run(force_standalone=false, default_dir=nil, argv=ARGV, &block)
+ r = new(force_standalone || standalone?, &block)
+ if((!r.process_args(argv)) && default_dir)
+ r.to_run << default_dir
+ end
+ r.run
+ end
+
+ def self.standalone?
+ return false unless("-e" == $0)
+ ObjectSpace.each_object(Class) do |klass|
+ return false if(klass < TestCase)
+ end
+ true
+ end
+
+ RUNNERS = {
+ :console => proc do |r|
+ require 'test/unit/ui/console/testrunner'
+ Test::Unit::UI::Console::TestRunner
+ end,
+ :gtk => proc do |r|
+ require 'test/unit/ui/gtk/testrunner'
+ Test::Unit::UI::GTK::TestRunner
+ end,
+ :gtk2 => proc do |r|
+ require 'test/unit/ui/gtk2/testrunner'
+ Test::Unit::UI::GTK2::TestRunner
+ end,
+ :fox => proc do |r|
+ require 'test/unit/ui/fox/testrunner'
+ Test::Unit::UI::Fox::TestRunner
+ end,
+ :tk => proc do |r|
+ require 'test/unit/ui/tk/testrunner'
+ Test::Unit::UI::Tk::TestRunner
+ end,
+ }
+
+ OUTPUT_LEVELS = [
+ [:silent, UI::SILENT],
+ [:progress, UI::PROGRESS_ONLY],
+ [:normal, UI::NORMAL],
+ [:verbose, UI::VERBOSE],
+ ]
+
+ COLLECTORS = {
+ :objectspace => proc do |r|
+ require 'test/unit/collector/objectspace'
+ c = Collector::ObjectSpace.new
+ c.filter = r.filters
+ c.collect($0.sub(/\.rb\Z/, ''))
+ end,
+ :dir => proc do |r|
+ require 'test/unit/collector/dir'
+ c = Collector::Dir.new
+ c.filter = r.filters
+ c.pattern.concat(r.pattern) if(r.pattern)
+ c.exclude.concat(r.exclude) if(r.exclude)
+ c.collect(*(r.to_run.empty? ? ['.'] : r.to_run))
+ end,
+ }
+
+ attr_reader :suite
+ attr_accessor :output_level, :filters, :to_run, :pattern, :exclude
+ attr_writer :runner, :collector
+
+ def initialize(standalone)
+ Unit.run = true
+ @standalone = standalone
+ @runner = RUNNERS[:console]
+ @collector = COLLECTORS[(standalone ? :dir : :objectspace)]
+ @filters = []
+ @to_run = []
+ @output_level = UI::NORMAL
+ yield(self) if(block_given?)
+ end
+
+ def process_args(args = ARGV)
+ begin
+ options.order!(args) {|arg| @to_run << arg}
+ rescue OptionParser::ParseError => e
+ puts e
+ puts options
+ $! = nil
+ abort
+ else
+ @filters << proc{false} unless(@filters.empty?)
+ end
+ not @to_run.empty?
+ end
+
+ def options
+ @options ||= OptionParser.new do |o|
+ o.banner = "Test::Unit automatic runner."
+ o.banner << "\nUsage: #{$0} [options] [-- untouched arguments]"
+
+ o.on
+ o.on('-r', '--runner=RUNNER', RUNNERS,
+ "Use the given RUNNER.",
+ "(" + keyword_display(RUNNERS) + ")") do |r|
+ @runner = r
+ end
+
+ if(@standalone)
+ o.on('-a', '--add=TORUN', Array,
+ "Add TORUN to the list of things to run;",
+ "can be a file or a directory.") do |a|
+ @to_run.concat(a)
+ end
+
+ @pattern = []
+ o.on('-p', '--pattern=PATTERN', Regexp,
+ "Match files to collect against PATTERN.") do |e|
+ @pattern << e
+ end
+
+ @exclude = []
+ o.on('-x', '--exclude=PATTERN', Regexp,
+ "Ignore files to collect against PATTERN.") do |e|
+ @exclude << e
+ end
+ end
+
+ o.on('-n', '--name=NAME', String,
+ "Runs tests matching NAME.",
+ "(patterns may be used).") do |n|
+ n = (%r{\A/(.*)/\Z} =~ n ? Regexp.new($1) : n)
+ case n
+ when Regexp
+ @filters << proc{|t| n =~ t.method_name ? true : nil}
+ else
+ @filters << proc{|t| n == t.method_name ? true : nil}
+ end
+ end
+
+ o.on('-t', '--testcase=TESTCASE', String,
+ "Runs tests in TestCases matching TESTCASE.",
+ "(patterns may be used).") do |n|
+ n = (%r{\A/(.*)/\Z} =~ n ? Regexp.new($1) : n)
+ case n
+ when Regexp
+ @filters << proc{|t| n =~ t.class.name ? true : nil}
+ else
+ @filters << proc{|t| n == t.class.name ? true : nil}
+ end
+ end
+
+ o.on('-v', '--verbose=[LEVEL]', OUTPUT_LEVELS,
+ "Set the output level (default is verbose).",
+ "(" + keyword_display(OUTPUT_LEVELS) + ")") do |l|
+ @output_level = l || UI::VERBOSE
+ end
+
+ o.on('--',
+ "Stop processing options so that the",
+ "remaining options will be passed to the",
+ "test."){o.terminate}
+
+ o.on('-h', '--help', 'Display this help.'){puts o; exit}
+
+ o.on_tail
+ o.on_tail('Deprecated options:')
+
+ o.on_tail('--console', 'Console runner (use --runner).') do
+ warn("Deprecated option (--console).")
+ @runner = RUNNERS[:console]
+ end
+
+ o.on_tail('--gtk', 'GTK runner (use --runner).') do
+ warn("Deprecated option (--gtk).")
+ @runner = RUNNERS[:gtk]
+ end
+
+ o.on_tail('--fox', 'Fox runner (use --runner).') do
+ warn("Deprecated option (--fox).")
+ @runner = RUNNERS[:fox]
+ end
+
+ o.on_tail
+ end
+ end
+
+ def keyword_display(array)
+ list = array.collect {|e, *| e.to_s}
+ Array === array or list.sort!
+ list.collect {|e| e.sub(/^(.)([A-Za-z]+)(?=\w*$)/, '\\1[\\2]')}.join(", ")
+ end
+
+ def run
+ @suite = @collector[self]
+ result = @runner[self] or return false
+ result.run(@suite, @output_level).passed?
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/test/unit/collector.rb b/ruby_1_8_5/lib/test/unit/collector.rb
new file mode 100644
index 0000000000..9e9e654147
--- /dev/null
+++ b/ruby_1_8_5/lib/test/unit/collector.rb
@@ -0,0 +1,43 @@
+module Test
+ module Unit
+ module Collector
+ def initialize
+ @filters = []
+ end
+
+ def filter=(filters)
+ @filters = case(filters)
+ when Proc
+ [filters]
+ when Array
+ filters
+ end
+ end
+
+ def add_suite(destination, suite)
+ to_delete = suite.tests.find_all{|t| !include?(t)}
+ to_delete.each{|t| suite.delete(t)}
+ destination << suite unless(suite.size == 0)
+ end
+
+ def include?(test)
+ return true if(@filters.empty?)
+ @filters.each do |filter|
+ result = filter[test]
+ if(result.nil?)
+ next
+ elsif(!result)
+ return false
+ else
+ return true
+ end
+ end
+ true
+ end
+
+ def sort(suites)
+ suites.sort_by{|s| s.name}
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/test/unit/collector/dir.rb b/ruby_1_8_5/lib/test/unit/collector/dir.rb
new file mode 100644
index 0000000000..1395cdf4e5
--- /dev/null
+++ b/ruby_1_8_5/lib/test/unit/collector/dir.rb
@@ -0,0 +1,92 @@
+require 'test/unit/testsuite'
+require 'test/unit/collector'
+
+module Test
+ module Unit
+ module Collector
+ class Dir
+ include Collector
+
+ attr_reader :pattern, :exclude
+
+ def initialize(dir=::Dir, file=::File, object_space=::ObjectSpace, req=nil)
+ super()
+ @dir = dir
+ @file = file
+ @object_space = object_space
+ @req = req
+ @pattern = [/\btest_.*\.rb\Z/m]
+ @exclude = []
+ end
+
+ def collect(*from)
+ if(from.empty?)
+ recursive_collect('.', find_test_cases)
+ elsif(from.size == 1)
+ recursive_collect(from.first, find_test_cases)
+ else
+ suites = []
+ from.each do |f|
+ suite = recursive_collect(f, find_test_cases)
+ suites << suite unless(suite.tests.empty?)
+ end
+ suite = TestSuite.new("[#{from.join(', ')}]")
+ sort(suites).each{|s| suite << s}
+ suite
+ end
+ end
+
+ def find_test_cases(ignore=[])
+ cases = []
+ @object_space.each_object(Class) do |c|
+ cases << c if(c < TestCase && !ignore.include?(c))
+ end
+ ignore.concat(cases)
+ cases
+ end
+
+ def recursive_collect(name, already_gathered)
+ sub_suites = []
+ if(@file.directory?(name))
+ @dir.entries(name).each do |e|
+ next if(e == '.' || e == '..')
+ e_name = @file.join(name, e)
+ if(@file.directory?(e_name))
+ next if /\ACVS\z/ =~ e
+ sub_suite = recursive_collect(e_name, already_gathered)
+ sub_suites << sub_suite unless(sub_suite.empty?)
+ else
+ next if /~\z/ =~ e_name or /\A\.\#/ =~ e
+ if @pattern and !@pattern.empty?
+ next unless @pattern.any? {|pat| pat =~ e_name}
+ end
+ if @exclude and !@exclude.empty?
+ next if @exclude.any? {|pat| pat =~ e_name}
+ end
+ collect_file(e_name, sub_suites, already_gathered)
+ end
+ end
+ else
+ collect_file(name, sub_suites, already_gathered)
+ end
+ suite = TestSuite.new(@file.basename(name))
+ sort(sub_suites).each{|s| suite << s}
+ suite
+ end
+
+ def collect_file(name, suites, already_gathered)
+ dir = File.dirname(File.expand_path(name))
+ $:.unshift(dir)
+ if(@req)
+ @req.require(name)
+ else
+ require(name)
+ end
+ find_test_cases(already_gathered).each{|t| add_suite(suites, t.suite)}
+ ensure
+ $:.delete_at($:.rindex(dir)) if(dir)
+ end
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/test/unit/collector/objectspace.rb b/ruby_1_8_5/lib/test/unit/collector/objectspace.rb
new file mode 100644
index 0000000000..d1127a981f
--- /dev/null
+++ b/ruby_1_8_5/lib/test/unit/collector/objectspace.rb
@@ -0,0 +1,34 @@
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+require 'test/unit/collector'
+
+module Test
+ module Unit
+ module Collector
+ class ObjectSpace
+ include Collector
+
+ NAME = 'collected from the ObjectSpace'
+
+ def initialize(source=::ObjectSpace)
+ super()
+ @source = source
+ end
+
+ def collect(name=NAME)
+ suite = TestSuite.new(name)
+ sub_suites = []
+ @source.each_object(Class) do |klass|
+ if(Test::Unit::TestCase > klass)
+ add_suite(sub_suites, klass.suite)
+ end
+ end
+ sort(sub_suites).each{|s| suite << s}
+ suite
+ end
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/test/unit/error.rb b/ruby_1_8_5/lib/test/unit/error.rb
new file mode 100644
index 0000000000..43a813f7d1
--- /dev/null
+++ b/ruby_1_8_5/lib/test/unit/error.rb
@@ -0,0 +1,56 @@
+#--
+#
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+require 'test/unit/util/backtracefilter'
+
+module Test
+ module Unit
+
+ # Encapsulates an error in a test. Created by
+ # Test::Unit::TestCase when it rescues an exception thrown
+ # during the processing of a test.
+ class Error
+ include Util::BacktraceFilter
+
+ attr_reader(:test_name, :exception)
+
+ SINGLE_CHARACTER = 'E'
+
+ # Creates a new Error with the given test_name and
+ # exception.
+ def initialize(test_name, exception)
+ @test_name = test_name
+ @exception = exception
+ end
+
+ # Returns a single character representation of an error.
+ def single_character_display
+ SINGLE_CHARACTER
+ end
+
+ # Returns the message associated with the error.
+ def message
+ "#{@exception.class.name}: #{@exception.message}"
+ end
+
+ # Returns a brief version of the error description.
+ def short_display
+ "#@test_name: #{message.split("\n")[0]}"
+ end
+
+ # Returns a verbose version of the error description.
+ def long_display
+ backtrace = filter_backtrace(@exception.backtrace).join("\n ")
+ "Error:\n#@test_name:\n#{message}\n #{backtrace}"
+ end
+
+ # Overridden to return long_display.
+ def to_s
+ long_display
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/test/unit/failure.rb b/ruby_1_8_5/lib/test/unit/failure.rb
new file mode 100644
index 0000000000..832c99857c
--- /dev/null
+++ b/ruby_1_8_5/lib/test/unit/failure.rb
@@ -0,0 +1,51 @@
+#--
+#
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+module Test
+ module Unit
+
+ # Encapsulates a test failure. Created by Test::Unit::TestCase
+ # when an assertion fails.
+ class Failure
+ attr_reader :test_name, :location, :message
+
+ SINGLE_CHARACTER = 'F'
+
+ # Creates a new Failure with the given location and
+ # message.
+ def initialize(test_name, location, message)
+ @test_name = test_name
+ @location = location
+ @message = message
+ end
+
+ # Returns a single character representation of a failure.
+ def single_character_display
+ SINGLE_CHARACTER
+ end
+
+ # Returns a brief version of the error description.
+ def short_display
+ "#@test_name: #{@message.split("\n")[0]}"
+ end
+
+ # Returns a verbose version of the error description.
+ def long_display
+ location_display = if(location.size == 1)
+ location[0].sub(/\A(.+:\d+).*/, ' [\\1]')
+ else
+ "\n [#{location.join("\n ")}]"
+ end
+ "Failure:\n#@test_name#{location_display}:\n#@message"
+ end
+
+ # Overridden to return long_display.
+ def to_s
+ long_display
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/test/unit/testcase.rb b/ruby_1_8_5/lib/test/unit/testcase.rb
new file mode 100644
index 0000000000..b6bb420f96
--- /dev/null
+++ b/ruby_1_8_5/lib/test/unit/testcase.rb
@@ -0,0 +1,152 @@
+#--
+#
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+require 'test/unit/assertions'
+require 'test/unit/failure'
+require 'test/unit/error'
+require 'test/unit/testsuite'
+require 'test/unit/assertionfailederror'
+require 'test/unit/util/backtracefilter'
+
+module Test
+ module Unit
+
+ # Ties everything together. If you subclass and add your own
+ # test methods, it takes care of making them into tests and
+ # wrapping those tests into a suite. It also does the
+ # nitty-gritty of actually running an individual test and
+ # collecting its results into a Test::Unit::TestResult object.
+ class TestCase
+ include Assertions
+ include Util::BacktraceFilter
+
+ attr_reader :method_name
+
+ STARTED = name + "::STARTED"
+ FINISHED = name + "::FINISHED"
+
+ # Creates a new instance of the fixture for running the
+ # test represented by test_method_name.
+ def initialize(test_method_name)
+ unless(respond_to?(test_method_name) and
+ (method(test_method_name).arity == 0 ||
+ method(test_method_name).arity == -1))
+ throw :invalid_test
+ end
+ @method_name = test_method_name
+ @test_passed = true
+ end
+
+ # Rolls up all of the test* methods in the fixture into
+ # one suite, creating a new instance of the fixture for
+ # each method.
+ def self.suite
+ method_names = public_instance_methods(true)
+ tests = method_names.delete_if {|method_name| method_name !~ /^test./}
+ suite = TestSuite.new(name)
+ tests.sort.each do
+ |test|
+ catch(:invalid_test) do
+ suite << new(test)
+ end
+ end
+ if (suite.empty?)
+ catch(:invalid_test) do
+ suite << new(:default_test)
+ end
+ end
+ return suite
+ end
+
+ # Runs the individual test method represented by this
+ # instance of the fixture, collecting statistics, failures
+ # and errors in result.
+ def run(result)
+ yield(STARTED, name)
+ @_result = result
+ begin
+ setup
+ __send__(@method_name)
+ rescue AssertionFailedError => e
+ add_failure(e.message, e.backtrace)
+ rescue StandardError, ScriptError
+ add_error($!)
+ ensure
+ begin
+ teardown
+ rescue AssertionFailedError => e
+ add_failure(e.message, e.backtrace)
+ rescue StandardError, ScriptError
+ add_error($!)
+ end
+ end
+ result.add_run
+ yield(FINISHED, name)
+ end
+
+ # Called before every test method runs. Can be used
+ # to set up fixture information.
+ def setup
+ end
+
+ # Called after every test method runs. Can be used to tear
+ # down fixture information.
+ def teardown
+ end
+
+ def default_test
+ flunk("No tests were specified")
+ end
+
+ # Returns whether this individual test passed or
+ # not. Primarily for use in teardown so that artifacts
+ # can be left behind if the test fails.
+ def passed?
+ return @test_passed
+ end
+ private :passed?
+
+ def size
+ 1
+ end
+
+ def add_assertion
+ @_result.add_assertion
+ end
+ private :add_assertion
+
+ def add_failure(message, all_locations=caller())
+ @test_passed = false
+ @_result.add_failure(Failure.new(name, filter_backtrace(all_locations), message))
+ end
+ private :add_failure
+
+ def add_error(exception)
+ @test_passed = false
+ @_result.add_error(Error.new(name, exception))
+ end
+ private :add_error
+
+ # Returns a human-readable name for the specific test that
+ # this instance of TestCase represents.
+ def name
+ "#{@method_name}(#{self.class.name})"
+ end
+
+ # Overridden to return #name.
+ def to_s
+ name
+ end
+
+ # It's handy to be able to compare TestCase instances.
+ def ==(other)
+ return false unless(other.kind_of?(self.class))
+ return false unless(@method_name == other.method_name)
+ self.class == other.class
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/test/unit/testresult.rb b/ruby_1_8_5/lib/test/unit/testresult.rb
new file mode 100644
index 0000000000..e3a472ef7f
--- /dev/null
+++ b/ruby_1_8_5/lib/test/unit/testresult.rb
@@ -0,0 +1,80 @@
+#--
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+require 'test/unit/util/observable'
+
+module Test
+ module Unit
+
+ # Collects Test::Unit::Failure and Test::Unit::Error so that
+ # they can be displayed to the user. To this end, observers
+ # can be added to it, allowing the dynamic updating of, say, a
+ # UI.
+ class TestResult
+ include Util::Observable
+
+ CHANGED = "CHANGED"
+ FAULT = "FAULT"
+
+ attr_reader(:run_count, :assertion_count)
+
+ # Constructs a new, empty TestResult.
+ def initialize
+ @run_count, @assertion_count = 0, 0
+ @failures, @errors = Array.new, Array.new
+ end
+
+ # Records a test run.
+ def add_run
+ @run_count += 1
+ notify_listeners(CHANGED, self)
+ end
+
+ # Records a Test::Unit::Failure.
+ def add_failure(failure)
+ @failures << failure
+ notify_listeners(FAULT, failure)
+ notify_listeners(CHANGED, self)
+ end
+
+ # Records a Test::Unit::Error.
+ def add_error(error)
+ @errors << error
+ notify_listeners(FAULT, error)
+ notify_listeners(CHANGED, self)
+ end
+
+ # Records an individual assertion.
+ def add_assertion
+ @assertion_count += 1
+ notify_listeners(CHANGED, self)
+ end
+
+ # Returns a string contain the recorded runs, assertions,
+ # failures and errors in this TestResult.
+ def to_s
+ "#{run_count} tests, #{assertion_count} assertions, #{failure_count} failures, #{error_count} errors"
+ end
+
+ # Returns whether or not this TestResult represents
+ # successful completion.
+ def passed?
+ return @failures.empty? && @errors.empty?
+ end
+
+ # Returns the number of failures this TestResult has
+ # recorded.
+ def failure_count
+ return @failures.size
+ end
+
+ # Returns the number of errors this TestResult has
+ # recorded.
+ def error_count
+ return @errors.size
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/test/unit/testsuite.rb b/ruby_1_8_5/lib/test/unit/testsuite.rb
new file mode 100644
index 0000000000..6fea976c50
--- /dev/null
+++ b/ruby_1_8_5/lib/test/unit/testsuite.rb
@@ -0,0 +1,76 @@
+#--
+#
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+module Test
+ module Unit
+
+ # A collection of tests which can be #run.
+ #
+ # Note: It is easy to confuse a TestSuite instance with
+ # something that has a static suite method; I know because _I_
+ # have trouble keeping them straight. Think of something that
+ # has a suite method as simply providing a way to get a
+ # meaningful TestSuite instance.
+ class TestSuite
+ attr_reader :name, :tests
+
+ STARTED = name + "::STARTED"
+ FINISHED = name + "::FINISHED"
+
+ # Creates a new TestSuite with the given name.
+ def initialize(name="Unnamed TestSuite")
+ @name = name
+ @tests = []
+ end
+
+ # Runs the tests and/or suites contained in this
+ # TestSuite.
+ def run(result, &progress_block)
+ yield(STARTED, name)
+ @tests.each do |test|
+ test.run(result, &progress_block)
+ end
+ yield(FINISHED, name)
+ end
+
+ # Adds the test to the suite.
+ def <<(test)
+ @tests << test
+ self
+ end
+
+ def delete(test)
+ @tests.delete(test)
+ end
+
+ # Retuns the rolled up number of tests in this suite;
+ # i.e. if the suite contains other suites, it counts the
+ # tests within those suites, not the suites themselves.
+ def size
+ total_size = 0
+ @tests.each { |test| total_size += test.size }
+ total_size
+ end
+
+ def empty?
+ tests.empty?
+ end
+
+ # Overridden to return the name given the suite at
+ # creation.
+ def to_s
+ @name
+ end
+
+ # It's handy to be able to compare TestSuite instances.
+ def ==(other)
+ return false unless(other.kind_of?(self.class))
+ return false unless(@name == other.name)
+ @tests == other.tests
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/test/unit/ui/console/testrunner.rb b/ruby_1_8_5/lib/test/unit/ui/console/testrunner.rb
new file mode 100644
index 0000000000..6b600e319a
--- /dev/null
+++ b/ruby_1_8_5/lib/test/unit/ui/console/testrunner.rb
@@ -0,0 +1,127 @@
+#--
+#
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2003 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+require 'test/unit/ui/testrunnermediator'
+require 'test/unit/ui/testrunnerutilities'
+
+module Test
+ module Unit
+ module UI
+ module Console
+
+ # Runs a Test::Unit::TestSuite on the console.
+ class TestRunner
+ extend TestRunnerUtilities
+
+ # Creates a new TestRunner for running the passed
+ # suite. If quiet_mode is true, the output while
+ # running is limited to progress dots, errors and
+ # failures, and the final result. io specifies
+ # where runner output should go to; defaults to
+ # STDOUT.
+ def initialize(suite, output_level=NORMAL, io=STDOUT)
+ if (suite.respond_to?(:suite))
+ @suite = suite.suite
+ else
+ @suite = suite
+ end
+ @output_level = output_level
+ @io = io
+ @already_outputted = false
+ @faults = []
+ end
+
+ # Begins the test run.
+ def start
+ setup_mediator
+ attach_to_mediator
+ return start_mediator
+ end
+
+ private
+ def setup_mediator
+ @mediator = create_mediator(@suite)
+ suite_name = @suite.to_s
+ if ( @suite.kind_of?(Module) )
+ suite_name = @suite.name
+ end
+ output("Loaded suite #{suite_name}")
+ end
+
+ def create_mediator(suite)
+ return TestRunnerMediator.new(suite)
+ end
+
+ def attach_to_mediator
+ @mediator.add_listener(TestResult::FAULT, &method(:add_fault))
+ @mediator.add_listener(TestRunnerMediator::STARTED, &method(:started))
+ @mediator.add_listener(TestRunnerMediator::FINISHED, &method(:finished))
+ @mediator.add_listener(TestCase::STARTED, &method(:test_started))
+ @mediator.add_listener(TestCase::FINISHED, &method(:test_finished))
+ end
+
+ def start_mediator
+ return @mediator.run_suite
+ end
+
+ def add_fault(fault)
+ @faults << fault
+ output_single(fault.single_character_display, PROGRESS_ONLY)
+ @already_outputted = true
+ end
+
+ def started(result)
+ @result = result
+ output("Started")
+ end
+
+ def finished(elapsed_time)
+ nl
+ output("Finished in #{elapsed_time} seconds.")
+ @faults.each_with_index do |fault, index|
+ nl
+ output("%3d) %s" % [index + 1, fault.long_display])
+ end
+ nl
+ output(@result)
+ end
+
+ def test_started(name)
+ output_single(name + ": ", VERBOSE)
+ end
+
+ def test_finished(name)
+ output_single(".", PROGRESS_ONLY) unless (@already_outputted)
+ nl(VERBOSE)
+ @already_outputted = false
+ end
+
+ def nl(level=NORMAL)
+ output("", level)
+ end
+
+ def output(something, level=NORMAL)
+ @io.puts(something) if (output?(level))
+ @io.flush
+ end
+
+ def output_single(something, level=NORMAL)
+ @io.write(something) if (output?(level))
+ @io.flush
+ end
+
+ def output?(level)
+ level <= @output_level
+ end
+ end
+ end
+ end
+ end
+end
+
+if __FILE__ == $0
+ Test::Unit::UI::Console::TestRunner.start_command_line_test
+end
diff --git a/ruby_1_8_5/lib/test/unit/ui/fox/testrunner.rb b/ruby_1_8_5/lib/test/unit/ui/fox/testrunner.rb
new file mode 100644
index 0000000000..a23a450567
--- /dev/null
+++ b/ruby_1_8_5/lib/test/unit/ui/fox/testrunner.rb
@@ -0,0 +1,268 @@
+#--
+#
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+require 'fox'
+require 'test/unit/ui/testrunnermediator'
+require 'test/unit/ui/testrunnerutilities'
+
+include Fox
+
+module Test
+ module Unit
+ module UI
+ module Fox
+
+ # Runs a Test::Unit::TestSuite in a Fox UI. Obviously,
+ # this one requires you to have Fox
+ # (http://www.fox-toolkit.org/fox.html) and the Ruby
+ # Fox extension (http://fxruby.sourceforge.net/)
+ # installed.
+ class TestRunner
+
+ extend TestRunnerUtilities
+
+ RED_STYLE = FXRGBA(0xFF,0,0,0xFF) #0xFF000000
+ GREEN_STYLE = FXRGBA(0,0xFF,0,0xFF) #0x00FF0000
+
+ # Creates a new TestRunner for running the passed
+ # suite.
+ def initialize(suite, output_level = NORMAL)
+ if (suite.respond_to?(:suite))
+ @suite = suite.suite
+ else
+ @suite = suite
+ end
+
+ @result = nil
+ @red = false
+ end
+
+ # Begins the test run.
+ def start
+ setup_ui
+ setup_mediator
+ attach_to_mediator
+ start_ui
+ @result
+ end
+
+ def setup_mediator
+ @mediator = TestRunnerMediator.new(@suite)
+ suite_name = @suite.to_s
+ if ( @suite.kind_of?(Module) )
+ suite_name = @suite.name
+ end
+ @suite_name_entry.text = suite_name
+ end
+
+ def attach_to_mediator
+ @mediator.add_listener(TestRunnerMediator::RESET, &method(:reset_ui))
+ @mediator.add_listener(TestResult::FAULT, &method(:add_fault))
+ @mediator.add_listener(TestResult::CHANGED, &method(:result_changed))
+ @mediator.add_listener(TestRunnerMediator::STARTED, &method(:started))
+ @mediator.add_listener(TestCase::STARTED, &method(:test_started))
+ @mediator.add_listener(TestRunnerMediator::FINISHED, &method(:finished))
+ end
+
+ def start_ui
+ @application.create
+ @window.show(PLACEMENT_SCREEN)
+ @application.addTimeout(1) do
+ @mediator.run_suite
+ end
+ @application.run
+ end
+
+ def stop
+ @application.exit(0)
+ end
+
+ def reset_ui(count)
+ @test_progress_bar.barColor = GREEN_STYLE
+ @test_progress_bar.total = count
+ @test_progress_bar.progress = 0
+ @red = false
+
+ @test_count_label.text = "0"
+ @assertion_count_label.text = "0"
+ @failure_count_label.text = "0"
+ @error_count_label.text = "0"
+
+ @fault_list.clearItems
+ end
+
+ def add_fault(fault)
+ if ( ! @red )
+ @test_progress_bar.barColor = RED_STYLE
+ @red = true
+ end
+ item = FaultListItem.new(fault)
+ @fault_list.appendItem(item)
+ end
+
+ def show_fault(fault)
+ raw_show_fault(fault.long_display)
+ end
+
+ def raw_show_fault(string)
+ @detail_text.setText(string)
+ end
+
+ def clear_fault
+ raw_show_fault("")
+ end
+
+ def result_changed(result)
+ @test_progress_bar.progress = result.run_count
+
+ @test_count_label.text = result.run_count.to_s
+ @assertion_count_label.text = result.assertion_count.to_s
+ @failure_count_label.text = result.failure_count.to_s
+ @error_count_label.text = result.error_count.to_s
+
+ # repaint now!
+ @info_panel.repaint
+ @application.flush
+ end
+
+ def started(result)
+ @result = result
+ output_status("Started...")
+ end
+
+ def test_started(test_name)
+ output_status("Running #{test_name}...")
+ end
+
+ def finished(elapsed_time)
+ output_status("Finished in #{elapsed_time} seconds")
+ end
+
+ def output_status(string)
+ @status_entry.text = string
+ @status_entry.repaint
+ end
+
+ def setup_ui
+ @application = create_application
+ create_tooltip(@application)
+
+ @window = create_window(@application)
+
+ @status_entry = create_entry(@window)
+
+ main_panel = create_main_panel(@window)
+
+ suite_panel = create_suite_panel(main_panel)
+ create_label(suite_panel, "Suite:")
+ @suite_name_entry = create_entry(suite_panel)
+ create_button(suite_panel, "&Run\tRun the current suite", proc { @mediator.run_suite })
+
+ @test_progress_bar = create_progress_bar(main_panel)
+
+ @info_panel = create_info_panel(main_panel)
+ create_label(@info_panel, "Tests:")
+ @test_count_label = create_label(@info_panel, "0")
+ create_label(@info_panel, "Assertions:")
+ @assertion_count_label = create_label(@info_panel, "0")
+ create_label(@info_panel, "Failures:")
+ @failure_count_label = create_label(@info_panel, "0")
+ create_label(@info_panel, "Errors:")
+ @error_count_label = create_label(@info_panel, "0")
+
+ list_panel = create_list_panel(main_panel)
+ @fault_list = create_fault_list(list_panel)
+
+ detail_panel = create_detail_panel(main_panel)
+ @detail_text = create_text(detail_panel)
+ end
+
+ def create_application
+ app = FXApp.new("TestRunner", "Test::Unit")
+ app.init([])
+ app
+ end
+
+ def create_window(app)
+ FXMainWindow.new(app, "Test::Unit TestRunner", nil, nil, DECOR_ALL, 0, 0, 450)
+ end
+
+ def create_tooltip(app)
+ FXTooltip.new(app)
+ end
+
+ def create_main_panel(parent)
+ panel = FXVerticalFrame.new(parent, LAYOUT_FILL_X | LAYOUT_FILL_Y)
+ panel.vSpacing = 10
+ panel
+ end
+
+ def create_suite_panel(parent)
+ FXHorizontalFrame.new(parent, LAYOUT_SIDE_LEFT | LAYOUT_FILL_X)
+ end
+
+ def create_button(parent, text, action)
+ FXButton.new(parent, text).connect(SEL_COMMAND, &action)
+ end
+
+ def create_progress_bar(parent)
+ FXProgressBar.new(parent, nil, 0, PROGRESSBAR_NORMAL | LAYOUT_FILL_X)
+ end
+
+ def create_info_panel(parent)
+ FXMatrix.new(parent, 1, MATRIX_BY_ROWS | LAYOUT_FILL_X)
+ end
+
+ def create_label(parent, text)
+ FXLabel.new(parent, text, nil, JUSTIFY_CENTER_X | LAYOUT_FILL_COLUMN)
+ end
+
+ def create_list_panel(parent)
+ FXHorizontalFrame.new(parent, LAYOUT_FILL_X | FRAME_SUNKEN | FRAME_THICK)
+ end
+
+ def create_fault_list(parent)
+ list = FXList.new(parent, 10, nil, 0, LIST_SINGLESELECT | LAYOUT_FILL_X) #, 0, 0, 0, 150)
+ list.connect(SEL_COMMAND) do |sender, sel, ptr|
+ if sender.retrieveItem(sender.currentItem).selected?
+ show_fault(sender.retrieveItem(sender.currentItem).fault)
+ else
+ clear_fault
+ end
+ end
+ list
+ end
+
+ def create_detail_panel(parent)
+ FXHorizontalFrame.new(parent, LAYOUT_FILL_X | LAYOUT_FILL_Y | FRAME_SUNKEN | FRAME_THICK)
+ end
+
+ def create_text(parent)
+ FXText.new(parent, nil, 0, TEXT_READONLY | LAYOUT_FILL_X | LAYOUT_FILL_Y)
+ end
+
+ def create_entry(parent)
+ entry = FXTextField.new(parent, 30, nil, 0, TEXTFIELD_NORMAL | LAYOUT_SIDE_BOTTOM | LAYOUT_FILL_X)
+ entry.disable
+ entry
+ end
+ end
+
+ class FaultListItem < FXListItem
+ attr_reader(:fault)
+ def initialize(fault)
+ super(fault.short_display)
+ @fault = fault
+ end
+ end
+ end
+ end
+ end
+end
+
+if __FILE__ == $0
+ Test::Unit::UI::Fox::TestRunner.start_command_line_test
+end
diff --git a/ruby_1_8_5/lib/test/unit/ui/gtk/testrunner.rb b/ruby_1_8_5/lib/test/unit/ui/gtk/testrunner.rb
new file mode 100644
index 0000000000..994328dc9b
--- /dev/null
+++ b/ruby_1_8_5/lib/test/unit/ui/gtk/testrunner.rb
@@ -0,0 +1,416 @@
+#--
+#
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+require 'gtk'
+require 'test/unit/ui/testrunnermediator'
+require 'test/unit/ui/testrunnerutilities'
+
+module Test
+ module Unit
+ module UI
+ module GTK
+
+ # Runs a Test::Unit::TestSuite in a Gtk UI. Obviously,
+ # this one requires you to have Gtk
+ # (http://www.gtk.org/) and the Ruby Gtk extension
+ # (http://ruby-gnome.sourceforge.net/) installed.
+ class TestRunner
+ extend TestRunnerUtilities
+
+ # Creates a new TestRunner for running the passed
+ # suite.
+ def initialize(suite, output_level = NORMAL)
+ if (suite.respond_to?(:suite))
+ @suite = suite.suite
+ else
+ @suite = suite
+ end
+ @result = nil
+
+ @runner = Thread.current
+ @restart_signal = Class.new(Exception)
+ @viewer = Thread.start do
+ @runner.join rescue @runner.run
+ Gtk.main
+ end
+ @viewer.join rescue nil # wait deadlock to handshake
+ end
+
+ # Begins the test run.
+ def start
+ setup_mediator
+ setup_ui
+ attach_to_mediator
+ start_ui
+ @result
+ end
+
+ private
+ def setup_mediator
+ @mediator = TestRunnerMediator.new(@suite)
+ suite_name = @suite.to_s
+ if ( @suite.kind_of?(Module) )
+ suite_name = @suite.name
+ end
+ suite_name_entry.set_text(suite_name)
+ end
+
+ def attach_to_mediator
+ run_button.signal_connect("clicked", nil, &method(:run_test))
+ @mediator.add_listener(TestRunnerMediator::RESET, &method(:reset_ui))
+ @mediator.add_listener(TestResult::FAULT, &method(:add_fault))
+ @mediator.add_listener(TestResult::CHANGED, &method(:result_changed))
+ @mediator.add_listener(TestRunnerMediator::STARTED, &method(:started))
+ @mediator.add_listener(TestCase::STARTED, &method(:test_started))
+ @mediator.add_listener(TestCase::FINISHED, &method(:test_finished))
+ @mediator.add_listener(TestRunnerMediator::FINISHED, &method(:finished))
+ end
+
+ def run_test(*)
+ @runner.raise(@restart_signal)
+ end
+
+ def start_ui
+ @viewer.run
+ running = false
+ begin
+ loop do
+ if (running ^= true)
+ run_button.child.text = "Stop"
+ @mediator.run_suite
+ else
+ run_button.child.text = "Run"
+ @viewer.join
+ break
+ end
+ end
+ rescue @restart_signal
+ retry
+ rescue
+ end
+ end
+
+ def stop(*)
+ Gtk.main_quit
+ end
+
+ def reset_ui(count)
+ test_progress_bar.set_style(green_style)
+ test_progress_bar.configure(0, 0, count)
+ @red = false
+
+ run_count_label.set_text("0")
+ assertion_count_label.set_text("0")
+ failure_count_label.set_text("0")
+ error_count_label.set_text("0")
+
+ fault_list.remove_items(fault_list.children)
+ end
+
+ def add_fault(fault)
+ if ( ! @red )
+ test_progress_bar.set_style(red_style)
+ @red = true
+ end
+ item = FaultListItem.new(fault)
+ item.show
+ fault_list.append_items([item])
+ end
+
+ def show_fault(fault)
+ raw_show_fault(fault.long_display)
+ end
+
+ def raw_show_fault(string)
+ fault_detail_label.set_text(string)
+ outer_detail_sub_panel.queue_resize
+ end
+
+ def clear_fault
+ raw_show_fault("")
+ end
+
+ def result_changed(result)
+ run_count_label.set_text(result.run_count.to_s)
+ assertion_count_label.set_text(result.assertion_count.to_s)
+ failure_count_label.set_text(result.failure_count.to_s)
+ error_count_label.set_text(result.error_count.to_s)
+ end
+
+ def started(result)
+ @result = result
+ output_status("Started...")
+ end
+
+ def test_started(test_name)
+ output_status("Running #{test_name}...")
+ end
+
+ def test_finished(test_name)
+ test_progress_bar.set_value(test_progress_bar.get_value + 1)
+ end
+
+ def finished(elapsed_time)
+ output_status("Finished in #{elapsed_time} seconds")
+ end
+
+ def output_status(string)
+ status_entry.set_text(string)
+ end
+
+ def setup_ui
+ main_window.signal_connect("destroy", nil, &method(:stop))
+ main_window.show_all
+ fault_list.signal_connect("select-child", nil) {
+ | list, item, data |
+ show_fault(item.fault)
+ }
+ fault_list.signal_connect("unselect-child", nil) {
+ clear_fault
+ }
+ @red = false
+ end
+
+ def main_window
+ lazy_initialize(:main_window) {
+ @main_window = Gtk::Window.new(Gtk::WINDOW_TOPLEVEL)
+ @main_window.set_title("Test::Unit TestRunner")
+ @main_window.set_usize(800, 600)
+ @main_window.set_uposition(20, 20)
+ @main_window.set_policy(true, true, false)
+ @main_window.add(main_panel)
+ }
+ end
+
+ def main_panel
+ lazy_initialize(:main_panel) {
+ @main_panel = Gtk::VBox.new(false, 0)
+ @main_panel.pack_start(suite_panel, false, false, 0)
+ @main_panel.pack_start(progress_panel, false, false, 0)
+ @main_panel.pack_start(info_panel, false, false, 0)
+ @main_panel.pack_start(list_panel, false, false, 0)
+ @main_panel.pack_start(detail_panel, true, true, 0)
+ @main_panel.pack_start(status_panel, false, false, 0)
+ }
+ end
+
+ def suite_panel
+ lazy_initialize(:suite_panel) {
+ @suite_panel = Gtk::HBox.new(false, 10)
+ @suite_panel.border_width(10)
+ @suite_panel.pack_start(Gtk::Label.new("Suite:"), false, false, 0)
+ @suite_panel.pack_start(suite_name_entry, true, true, 0)
+ @suite_panel.pack_start(run_button, false, false, 0)
+ }
+ end
+
+ def suite_name_entry
+ lazy_initialize(:suite_name_entry) {
+ @suite_name_entry = Gtk::Entry.new
+ @suite_name_entry.set_editable(false)
+ }
+ end
+
+ def run_button
+ lazy_initialize(:run_button) {
+ @run_button = Gtk::Button.new("Run")
+ }
+ end
+
+ def progress_panel
+ lazy_initialize(:progress_panel) {
+ @progress_panel = Gtk::HBox.new(false, 10)
+ @progress_panel.border_width(10)
+ @progress_panel.pack_start(test_progress_bar, true, true, 0)
+ }
+ end
+
+ def test_progress_bar
+ lazy_initialize(:test_progress_bar) {
+ @test_progress_bar = EnhancedProgressBar.new
+ @test_progress_bar.set_usize(@test_progress_bar.allocation.width,
+ info_panel.size_request.height)
+ @test_progress_bar.set_style(green_style)
+ }
+ end
+
+ def green_style
+ lazy_initialize(:green_style) {
+ @green_style = Gtk::Style.new
+ @green_style.set_bg(Gtk::STATE_PRELIGHT, 0x0000, 0xFFFF, 0x0000)
+ }
+ end
+
+ def red_style
+ lazy_initialize(:red_style) {
+ @red_style = Gtk::Style.new
+ @red_style.set_bg(Gtk::STATE_PRELIGHT, 0xFFFF, 0x0000, 0x0000)
+ }
+ end
+
+ def info_panel
+ lazy_initialize(:info_panel) {
+ @info_panel = Gtk::HBox.new(false, 0)
+ @info_panel.border_width(10)
+ @info_panel.pack_start(Gtk::Label.new("Runs:"), false, false, 0)
+ @info_panel.pack_start(run_count_label, true, false, 0)
+ @info_panel.pack_start(Gtk::Label.new("Assertions:"), false, false, 0)
+ @info_panel.pack_start(assertion_count_label, true, false, 0)
+ @info_panel.pack_start(Gtk::Label.new("Failures:"), false, false, 0)
+ @info_panel.pack_start(failure_count_label, true, false, 0)
+ @info_panel.pack_start(Gtk::Label.new("Errors:"), false, false, 0)
+ @info_panel.pack_start(error_count_label, true, false, 0)
+ }
+ end
+
+ def run_count_label
+ lazy_initialize(:run_count_label) {
+ @run_count_label = Gtk::Label.new("0")
+ @run_count_label.set_justify(Gtk::JUSTIFY_LEFT)
+ }
+ end
+
+ def assertion_count_label
+ lazy_initialize(:assertion_count_label) {
+ @assertion_count_label = Gtk::Label.new("0")
+ @assertion_count_label.set_justify(Gtk::JUSTIFY_LEFT)
+ }
+ end
+
+ def failure_count_label
+ lazy_initialize(:failure_count_label) {
+ @failure_count_label = Gtk::Label.new("0")
+ @failure_count_label.set_justify(Gtk::JUSTIFY_LEFT)
+ }
+ end
+
+ def error_count_label
+ lazy_initialize(:error_count_label) {
+ @error_count_label = Gtk::Label.new("0")
+ @error_count_label.set_justify(Gtk::JUSTIFY_LEFT)
+ }
+ end
+
+ def list_panel
+ lazy_initialize(:list_panel) {
+ @list_panel = Gtk::HBox.new
+ @list_panel.border_width(10)
+ @list_panel.pack_start(list_scrolled_window, true, true, 0)
+ }
+ end
+
+ def list_scrolled_window
+ lazy_initialize(:list_scrolled_window) {
+ @list_scrolled_window = Gtk::ScrolledWindow.new
+ @list_scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
+ @list_scrolled_window.set_usize(@list_scrolled_window.allocation.width, 150)
+ @list_scrolled_window.add_with_viewport(fault_list)
+ }
+ end
+
+ def fault_list
+ lazy_initialize(:fault_list) {
+ @fault_list = Gtk::List.new
+ }
+ end
+
+ def detail_panel
+ lazy_initialize(:detail_panel) {
+ @detail_panel = Gtk::HBox.new
+ @detail_panel.border_width(10)
+ @detail_panel.pack_start(detail_scrolled_window, true, true, 0)
+ }
+ end
+
+ def detail_scrolled_window
+ lazy_initialize(:detail_scrolled_window) {
+ @detail_scrolled_window = Gtk::ScrolledWindow.new
+ @detail_scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
+ @detail_scrolled_window.set_usize(400, @detail_scrolled_window.allocation.height)
+ @detail_scrolled_window.add_with_viewport(outer_detail_sub_panel)
+ }
+ end
+
+ def outer_detail_sub_panel
+ lazy_initialize(:outer_detail_sub_panel) {
+ @outer_detail_sub_panel = Gtk::VBox.new
+ @outer_detail_sub_panel.pack_start(inner_detail_sub_panel, false, false, 0)
+ }
+ end
+
+ def inner_detail_sub_panel
+ lazy_initialize(:inner_detail_sub_panel) {
+ @inner_detail_sub_panel = Gtk::HBox.new
+ @inner_detail_sub_panel.pack_start(fault_detail_label, false, false, 0)
+ }
+ end
+
+ def fault_detail_label
+ lazy_initialize(:fault_detail_label) {
+ @fault_detail_label = EnhancedLabel.new("")
+ style = Gtk::Style.new
+ font = Gdk::Font.font_load("-*-Courier New-medium-r-normal--*-120-*-*-*-*-*-*")
+ begin
+ style.set_font(font)
+ rescue ArgumentError; end
+ @fault_detail_label.set_style(style)
+ @fault_detail_label.set_justify(Gtk::JUSTIFY_LEFT)
+ @fault_detail_label.set_line_wrap(false)
+ }
+ end
+
+ def status_panel
+ lazy_initialize(:status_panel) {
+ @status_panel = Gtk::HBox.new
+ @status_panel.border_width(10)
+ @status_panel.pack_start(status_entry, true, true, 0)
+ }
+ end
+
+ def status_entry
+ lazy_initialize(:status_entry) {
+ @status_entry = Gtk::Entry.new
+ @status_entry.set_editable(false)
+ }
+ end
+
+ def lazy_initialize(symbol)
+ if (!instance_eval("defined?(@#{symbol.to_s})"))
+ yield
+ end
+ return instance_eval("@" + symbol.to_s)
+ end
+ end
+
+ class EnhancedProgressBar < Gtk::ProgressBar
+ def set_style(style)
+ super
+ hide
+ show
+ end
+ end
+
+ class EnhancedLabel < Gtk::Label
+ def set_text(text)
+ super(text.gsub(/\n\t/, "\n" + (" " * 4)))
+ end
+ end
+
+ class FaultListItem < Gtk::ListItem
+ attr_reader(:fault)
+ def initialize(fault)
+ super(fault.short_display)
+ @fault = fault
+ end
+ end
+ end
+ end
+ end
+end
+
+if __FILE__ == $0
+ Test::Unit::UI::GTK::TestRunner.start_command_line_test
+end
diff --git a/ruby_1_8_5/lib/test/unit/ui/gtk2/testrunner.rb b/ruby_1_8_5/lib/test/unit/ui/gtk2/testrunner.rb
new file mode 100644
index 0000000000..b05549c0e8
--- /dev/null
+++ b/ruby_1_8_5/lib/test/unit/ui/gtk2/testrunner.rb
@@ -0,0 +1,465 @@
+#--
+#
+# Author:: Kenta MURATA.
+# Copyright:: Copyright (c) 2000-2002 Kenta MURATA. All rights reserved.
+# License:: Ruby license.
+
+require "gtk2"
+require "test/unit/ui/testrunnermediator"
+require "test/unit/ui/testrunnerutilities"
+
+module Test
+ module Unit
+ module UI
+ module GTK2
+
+ Gtk.init
+
+ class EnhancedLabel < Gtk::Label
+ def set_text(text)
+ super(text.gsub(/\n\t/, "\n "))
+ end
+ end
+
+ class FaultList < Gtk::TreeView
+ def initialize
+ @faults = []
+ @model = Gtk::ListStore.new(String, String)
+ super(@model)
+ column = Gtk::TreeViewColumn.new
+ column.visible = false
+ append_column(column)
+ renderer = Gtk::CellRendererText.new
+ column = Gtk::TreeViewColumn.new("Failures", renderer, {:text => 1})
+ append_column(column)
+ selection.mode = Gtk::SELECTION_SINGLE
+ set_rules_hint(true)
+ set_headers_visible(false)
+ end # def initialize
+
+ def add_fault(fault)
+ @faults.push(fault)
+ iter = @model.append
+ iter.set_value(0, (@faults.length - 1).to_s)
+ iter.set_value(1, fault.short_display)
+ end # def add_fault(fault)
+
+ def get_fault(iter)
+ @faults[iter.get_value(0).to_i]
+ end # def get_fault
+
+ def clear
+ model.clear
+ end # def clear
+ end
+
+ class TestRunner
+ extend TestRunnerUtilities
+
+ def lazy_initialize(symbol)
+ if !instance_eval("defined?(@#{symbol})") then
+ yield
+ end
+ return instance_eval("@#{symbol}")
+ end
+ private :lazy_initialize
+
+ def status_entry
+ lazy_initialize(:status_entry) do
+ @status_entry = Gtk::Entry.new
+ @status_entry.editable = false
+ end
+ end
+ private :status_entry
+
+ def status_panel
+ lazy_initialize(:status_panel) do
+ @status_panel = Gtk::HBox.new
+ @status_panel.border_width = 10
+ @status_panel.pack_start(status_entry, true, true, 0)
+ end
+ end
+ private :status_panel
+
+ def fault_detail_label
+ lazy_initialize(:fault_detail_label) do
+ @fault_detail_label = EnhancedLabel.new("")
+# style = Gtk::Style.new
+# font = Gdk::Font.
+# font_load("-*-Courier 10 Pitch-medium-r-normal--*-120-*-*-*-*-*-*")
+# style.set_font(font)
+# @fault_detail_label.style = style
+ @fault_detail_label.justify = Gtk::JUSTIFY_LEFT
+ @fault_detail_label.wrap = false
+ end
+ end
+ private :fault_detail_label
+
+ def inner_detail_sub_panel
+ lazy_initialize(:inner_detail_sub_panel) do
+ @inner_detail_sub_panel = Gtk::HBox.new
+ @inner_detail_sub_panel.pack_start(fault_detail_label, false, false, 0)
+ end
+ end
+ private :inner_detail_sub_panel
+
+ def outer_detail_sub_panel
+ lazy_initialize(:outer_detail_sub_panel) do
+ @outer_detail_sub_panel = Gtk::VBox.new
+ @outer_detail_sub_panel.pack_start(inner_detail_sub_panel, false, false, 0)
+ end
+ end
+ private :outer_detail_sub_panel
+
+ def detail_scrolled_window
+ lazy_initialize(:detail_scrolled_window) do
+ @detail_scrolled_window = Gtk::ScrolledWindow.new
+ @detail_scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
+ @detail_scrolled_window.
+ set_size_request(400, @detail_scrolled_window.allocation.height)
+ @detail_scrolled_window.add_with_viewport(outer_detail_sub_panel)
+ end
+ end
+ private :detail_scrolled_window
+
+ def detail_panel
+ lazy_initialize(:detail_panel) do
+ @detail_panel = Gtk::HBox.new
+ @detail_panel.border_width = 10
+ @detail_panel.pack_start(detail_scrolled_window, true, true, 0)
+ end
+ end
+ private :detail_panel
+
+ def fault_list
+ lazy_initialize(:fault_list) do
+ @fault_list = FaultList.new
+ end
+ end
+ private :fault_list
+
+ def list_scrolled_window
+ lazy_initialize(:list_scrolled_window) do
+ @list_scrolled_window = Gtk::ScrolledWindow.new
+ @list_scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
+ @list_scrolled_window.
+ set_size_request(@list_scrolled_window.allocation.width, 150)
+ @list_scrolled_window.add_with_viewport(fault_list)
+ end
+ end
+ private :list_scrolled_window
+
+ def list_panel
+ lazy_initialize(:list_panel) do
+ @list_panel = Gtk::HBox.new
+ @list_panel.border_width = 10
+ @list_panel.pack_start(list_scrolled_window, true, true, 0)
+ end
+ end
+ private :list_panel
+
+ def error_count_label
+ lazy_initialize(:error_count_label) do
+ @error_count_label = Gtk::Label.new("0")
+ @error_count_label.justify = Gtk::JUSTIFY_LEFT
+ end
+ end
+ private :error_count_label
+
+ def failure_count_label
+ lazy_initialize(:failure_count_label) do
+ @failure_count_label = Gtk::Label.new("0")
+ @failure_count_label.justify = Gtk::JUSTIFY_LEFT
+ end
+ end
+ private :failure_count_label
+
+ def assertion_count_label
+ lazy_initialize(:assertion_count_label) do
+ @assertion_count_label = Gtk::Label.new("0")
+ @assertion_count_label.justify = Gtk::JUSTIFY_LEFT
+ end
+ end
+ private :assertion_count_label
+
+ def run_count_label
+ lazy_initialize(:run_count_label) do
+ @run_count_label = Gtk::Label.new("0")
+ @run_count_label.justify = Gtk::JUSTIFY_LEFT
+ end
+ end
+ private :run_count_label
+
+ def info_panel
+ lazy_initialize(:info_panel) do
+ @info_panel = Gtk::HBox.new(false, 0)
+ @info_panel.border_width = 10
+ @info_panel.pack_start(Gtk::Label.new("Runs:"), false, false, 0)
+ @info_panel.pack_start(run_count_label, true, false, 0)
+ @info_panel.pack_start(Gtk::Label.new("Assertions:"), false, false, 0)
+ @info_panel.pack_start(assertion_count_label, true, false, 0)
+ @info_panel.pack_start(Gtk::Label.new("Failures:"), false, false, 0)
+ @info_panel.pack_start(failure_count_label, true, false, 0)
+ @info_panel.pack_start(Gtk::Label.new("Errors:"), false, false, 0)
+ @info_panel.pack_start(error_count_label, true, false, 0)
+ end
+ end # def info_panel
+ private :info_panel
+
+ def green_style
+ lazy_initialize(:green_style) do
+ @green_style = Gtk::Style.new
+ @green_style.set_bg(Gtk::STATE_PRELIGHT, 0x0000, 0xFFFF, 0x0000)
+ end
+ end # def green_style
+ private :green_style
+
+ def red_style
+ lazy_initialize(:red_style) do
+ @red_style = Gtk::Style.new
+ @red_style.set_bg(Gtk::STATE_PRELIGHT, 0xFFFF, 0x0000, 0x0000)
+ end
+ end # def red_style
+ private :red_style
+
+ def test_progress_bar
+ lazy_initialize(:test_progress_bar) {
+ @test_progress_bar = Gtk::ProgressBar.new
+ @test_progress_bar.fraction = 0.0
+ @test_progress_bar.
+ set_size_request(@test_progress_bar.allocation.width,
+ info_panel.size_request[1])
+ @test_progress_bar.style = green_style
+ }
+ end # def test_progress_bar
+ private :test_progress_bar
+
+ def progress_panel
+ lazy_initialize(:progress_panel) do
+ @progress_panel = Gtk::HBox.new(false, 10)
+ @progress_panel.border_width = 10
+ @progress_panel.pack_start(test_progress_bar, true, true, 0)
+ end
+ end # def progress_panel
+
+ def run_button
+ lazy_initialize(:run_button) do
+ @run_button = Gtk::Button.new("Run")
+ end
+ end # def run_button
+
+ def suite_name_entry
+ lazy_initialize(:suite_name_entry) do
+ @suite_name_entry = Gtk::Entry.new
+ @suite_name_entry.editable = false
+ end
+ end # def suite_name_entry
+ private :suite_name_entry
+
+ def suite_panel
+ lazy_initialize(:suite_panel) do
+ @suite_panel = Gtk::HBox.new(false, 10)
+ @suite_panel.border_width = 10
+ @suite_panel.pack_start(Gtk::Label.new("Suite:"), false, false, 0)
+ @suite_panel.pack_start(suite_name_entry, true, true, 0)
+ @suite_panel.pack_start(run_button, false, false, 0)
+ end
+ end # def suite_panel
+ private :suite_panel
+
+ def main_panel
+ lazy_initialize(:main_panel) do
+ @main_panel = Gtk::VBox.new(false, 0)
+ @main_panel.pack_start(suite_panel, false, false, 0)
+ @main_panel.pack_start(progress_panel, false, false, 0)
+ @main_panel.pack_start(info_panel, false, false, 0)
+ @main_panel.pack_start(list_panel, false, false, 0)
+ @main_panel.pack_start(detail_panel, true, true, 0)
+ @main_panel.pack_start(status_panel, false, false, 0)
+ end
+ end # def main_panel
+ private :main_panel
+
+ def main_window
+ lazy_initialize(:main_window) do
+ @main_window = Gtk::Window.new(Gtk::Window::TOPLEVEL)
+ @main_window.set_title("Test::Unit TestRunner")
+ @main_window.set_default_size(800, 600)
+ @main_window.set_resizable(true)
+ @main_window.add(main_panel)
+ end
+ end # def main_window
+ private :main_window
+
+ def setup_ui
+ main_window.signal_connect("destroy", nil) { stop }
+ main_window.show_all
+ fault_list.selection.signal_connect("changed", nil) do
+ |selection, data|
+ if selection.selected then
+ show_fault(fault_list.get_fault(selection.selected))
+ else
+ clear_fault
+ end
+ end
+ end # def setup_ui
+ private :setup_ui
+
+ def output_status(string)
+ status_entry.set_text(string)
+ end # def output_status(string)
+ private :output_status
+
+ def finished(elapsed_time)
+ test_progress_bar.fraction = 1.0
+ output_status("Finished in #{elapsed_time} seconds")
+ end # def finished(elapsed_time)
+ private :finished
+
+ def test_started(test_name)
+ output_status("Running #{test_name}...")
+ end # def test_started(test_name)
+ private :test_started
+
+ def started(result)
+ @result = result
+ output_status("Started...")
+ end # def started(result)
+ private :started
+
+ def test_finished(result)
+ test_progress_bar.fraction += 1.0 / @count
+ end # def test_finished(result)
+
+ def result_changed(result)
+ run_count_label.label = result.run_count.to_s
+ assertion_count_label.label = result.assertion_count.to_s
+ failure_count_label.label = result.failure_count.to_s
+ error_count_label.label = result.error_count.to_s
+ end # def result_changed(result)
+ private :result_changed
+
+ def clear_fault
+ raw_show_fault("")
+ end # def clear_fault
+ private :clear_fault
+
+ def raw_show_fault(string)
+ fault_detail_label.set_text(string)
+ outer_detail_sub_panel.queue_resize
+ end # def raw_show_fault(string)
+ private :raw_show_fault
+
+ def show_fault(fault)
+ raw_show_fault(fault.long_display)
+ end # def show_fault(fault)
+ private :show_fault
+
+ def add_fault(fault)
+ if not @red then
+ test_progress_bar.style = red_style
+ @red = true
+ end
+ fault_list.add_fault(fault)
+ end # def add_fault(fault)
+ private :add_fault
+
+ def reset_ui(count)
+ test_progress_bar.style = green_style
+ test_progress_bar.fraction = 0.0
+ @count = count + 1
+ @red = false
+
+ run_count_label.set_text("0")
+ assertion_count_label.set_text("0")
+ failure_count_label.set_text("0")
+ error_count_label.set_text("0")
+
+ fault_list.clear
+ end # def reset_ui(count)
+ private :reset_ui
+
+ def stop
+ Gtk.main_quit
+ end # def stop
+ private :stop
+
+ def run_test
+ @runner.raise(@restart_signal)
+ end
+ private :run_test
+
+ def start_ui
+ @viewer.run
+ running = false
+ begin
+ loop do
+ if (running ^= true)
+ run_button.child.text = "Stop"
+ @mediator.run_suite
+ else
+ run_button.child.text = "Run"
+ @viewer.join
+ break
+ end
+ end
+ rescue @restart_signal
+ retry
+ rescue
+ end
+ end # def start_ui
+ private :start_ui
+
+ def attach_to_mediator
+ run_button.signal_connect("clicked", nil) { run_test }
+ @mediator.add_listener(TestRunnerMediator::RESET, &method(:reset_ui))
+ @mediator.add_listener(TestRunnerMediator::STARTED, &method(:started))
+ @mediator.add_listener(TestRunnerMediator::FINISHED, &method(:finished))
+ @mediator.add_listener(TestResult::FAULT, &method(:add_fault))
+ @mediator.add_listener(TestResult::CHANGED, &method(:result_changed))
+ @mediator.add_listener(TestCase::STARTED, &method(:test_started))
+ @mediator.add_listener(TestCase::FINISHED, &method(:test_finished))
+ end # def attach_to_mediator
+ private :attach_to_mediator
+
+ def setup_mediator
+ @mediator = TestRunnerMediator.new(@suite)
+ suite_name = @suite.to_s
+ if @suite.kind_of?(Module) then
+ suite_name = @suite.name
+ end
+ suite_name_entry.set_text(suite_name)
+ end # def setup_mediator
+ private :setup_mediator
+
+ def start
+ setup_mediator
+ setup_ui
+ attach_to_mediator
+ start_ui
+ @result
+ end # def start
+
+ def initialize(suite, output_level = NORMAL)
+ if suite.respond_to?(:suite) then
+ @suite = suite.suite
+ else
+ @suite = suite
+ end
+ @result = nil
+
+ @runner = Thread.current
+ @restart_signal = Class.new(Exception)
+ @viewer = Thread.start do
+ @runner.join rescue @runner.run
+ Gtk.main
+ end
+ @viewer.join rescue nil # wait deadlock to handshake
+ end # def initialize(suite)
+
+ end # class TestRunner
+
+ end # module GTK2
+ end # module UI
+ end # module Unit
+end # module Test
diff --git a/ruby_1_8_5/lib/test/unit/ui/testrunnermediator.rb b/ruby_1_8_5/lib/test/unit/ui/testrunnermediator.rb
new file mode 100644
index 0000000000..d34510d1c6
--- /dev/null
+++ b/ruby_1_8_5/lib/test/unit/ui/testrunnermediator.rb
@@ -0,0 +1,68 @@
+#--
+#
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+require 'test/unit'
+require 'test/unit/util/observable'
+require 'test/unit/testresult'
+
+module Test
+ module Unit
+ module UI
+
+ # Provides an interface to write any given UI against,
+ # hopefully making it easy to write new UIs.
+ class TestRunnerMediator
+ RESET = name + "::RESET"
+ STARTED = name + "::STARTED"
+ FINISHED = name + "::FINISHED"
+
+ include Util::Observable
+
+ # Creates a new TestRunnerMediator initialized to run
+ # the passed suite.
+ def initialize(suite)
+ @suite = suite
+ end
+
+ # Runs the suite the TestRunnerMediator was created
+ # with.
+ def run_suite
+ Unit.run = true
+ begin_time = Time.now
+ notify_listeners(RESET, @suite.size)
+ result = create_result
+ notify_listeners(STARTED, result)
+ result_listener = result.add_listener(TestResult::CHANGED) do |updated_result|
+ notify_listeners(TestResult::CHANGED, updated_result)
+ end
+
+ fault_listener = result.add_listener(TestResult::FAULT) do |fault|
+ notify_listeners(TestResult::FAULT, fault)
+ end
+
+ @suite.run(result) do |channel, value|
+ notify_listeners(channel, value)
+ end
+
+ result.remove_listener(TestResult::FAULT, fault_listener)
+ result.remove_listener(TestResult::CHANGED, result_listener)
+ end_time = Time.now
+ elapsed_time = end_time - begin_time
+ notify_listeners(FINISHED, elapsed_time) #"Finished in #{elapsed_time} seconds.")
+ return result
+ end
+
+ private
+ # A factory method to create the result the mediator
+ # should run with. Can be overridden by subclasses if
+ # one wants to use a different result.
+ def create_result
+ return TestResult.new
+ end
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/test/unit/ui/testrunnerutilities.rb b/ruby_1_8_5/lib/test/unit/ui/testrunnerutilities.rb
new file mode 100644
index 0000000000..70b885bd6c
--- /dev/null
+++ b/ruby_1_8_5/lib/test/unit/ui/testrunnerutilities.rb
@@ -0,0 +1,46 @@
+#--
+#
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+module Test
+ module Unit
+ module UI
+
+ SILENT = 0
+ PROGRESS_ONLY = 1
+ NORMAL = 2
+ VERBOSE = 3
+
+ # Provides some utilities common to most, if not all,
+ # TestRunners.
+ #
+ #--
+ #
+ # Perhaps there ought to be a TestRunner superclass? There
+ # seems to be a decent amount of shared code between test
+ # runners.
+
+ module TestRunnerUtilities
+
+ # Creates a new TestRunner and runs the suite.
+ def run(suite, output_level=NORMAL)
+ return new(suite, output_level).start
+ end
+
+ # Takes care of the ARGV parsing and suite
+ # determination necessary for running one of the
+ # TestRunners from the command line.
+ def start_command_line_test
+ if ARGV.empty?
+ puts "You should supply the name of a test suite file to the runner"
+ exit
+ end
+ require ARGV[0].gsub(/.+::/, '')
+ new(eval(ARGV[0])).start
+ end
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/test/unit/ui/tk/testrunner.rb b/ruby_1_8_5/lib/test/unit/ui/tk/testrunner.rb
new file mode 100644
index 0000000000..4d05f2fb22
--- /dev/null
+++ b/ruby_1_8_5/lib/test/unit/ui/tk/testrunner.rb
@@ -0,0 +1,260 @@
+#--
+#
+# Original Author:: Nathaniel Talbott.
+# Author:: Kazuhiro NISHIYAMA.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# Copyright:: Copyright (c) 2003 Kazuhiro NISHIYAMA. All rights reserved.
+# License:: Ruby license.
+
+require 'tk'
+require 'test/unit/ui/testrunnermediator'
+require 'test/unit/ui/testrunnerutilities'
+
+module Test
+ module Unit
+ module UI
+ module Tk
+
+ # Runs a Test::Unit::TestSuite in a Tk UI. Obviously,
+ # this one requires you to have Tk
+ # and the Ruby Tk extension installed.
+ class TestRunner
+ extend TestRunnerUtilities
+
+ # Creates a new TestRunner for running the passed
+ # suite.
+ def initialize(suite, output_level = NORMAL)
+ if (suite.respond_to?(:suite))
+ @suite = suite.suite
+ else
+ @suite = suite
+ end
+ @result = nil
+
+ @red = false
+ @fault_detail_list = []
+ @runner = Thread.current
+ @restart_signal = Class.new(Exception)
+ @viewer = Thread.start do
+ @runner.join rescue @runner.run
+ ::Tk.mainloop
+ end
+ @viewer.join rescue nil # wait deadlock to handshake
+ end
+
+ # Begins the test run.
+ def start
+ setup_ui
+ setup_mediator
+ attach_to_mediator
+ start_ui
+ @result
+ end
+
+ private
+ def setup_mediator
+ @mediator = TestRunnerMediator.new(@suite)
+ suite_name = @suite.to_s
+ if ( @suite.kind_of?(Module) )
+ suite_name = @suite.name
+ end
+ @suite_name_entry.value = suite_name
+ end
+
+ def attach_to_mediator
+ @run_button.command(method(:run_test))
+ @fault_list.bind('ButtonPress-1', proc{|y|
+ fault = @fault_detail_list[@fault_list.nearest(y)]
+ if fault
+ show_fault(fault)
+ end
+ }, '%y')
+ @mediator.add_listener(TestRunnerMediator::RESET, &method(:reset_ui))
+ @mediator.add_listener(TestResult::FAULT, &method(:add_fault))
+ @mediator.add_listener(TestResult::CHANGED, &method(:result_changed))
+ @mediator.add_listener(TestRunnerMediator::STARTED, &method(:started))
+ @mediator.add_listener(TestCase::STARTED, &method(:test_started))
+ @mediator.add_listener(TestRunnerMediator::FINISHED, &method(:finished))
+ end
+
+ def run_test
+ @runner.raise(@restart_signal)
+ end
+
+ def start_ui
+ @viewer.run
+ running = false
+ begin
+ loop do
+ if (running ^= true)
+ @run_button.configure('text'=>'Stop')
+ @mediator.run_suite
+ else
+ @run_button.configure('text'=>'Run')
+ @viewer.join
+ break
+ end
+ end
+ rescue @restart_signal
+ retry
+ rescue
+ end
+ end
+
+ def stop
+ ::Tk.exit
+ end
+
+ def reset_ui(count)
+ @test_total_count = count.to_f
+ @test_progress_bar.configure('background'=>'green')
+ @test_progress_bar.place('relwidth'=>(count.zero? ? 0 : 0/count))
+ @red = false
+
+ @test_count_label.value = 0
+ @assertion_count_label.value = 0
+ @failure_count_label.value = 0
+ @error_count_label.value = 0
+
+ @fault_list.delete(0, 'end')
+ @fault_detail_list = []
+ clear_fault
+ end
+
+ def add_fault(fault)
+ if ( ! @red )
+ @test_progress_bar.configure('background'=>'red')
+ @red = true
+ end
+ @fault_detail_list.push fault
+ @fault_list.insert('end', fault.short_display)
+ end
+
+ def show_fault(fault)
+ raw_show_fault(fault.long_display)
+ end
+
+ def raw_show_fault(string)
+ @detail_text.value = string
+ end
+
+ def clear_fault
+ raw_show_fault("")
+ end
+
+ def result_changed(result)
+ @test_count_label.value = result.run_count
+ @test_progress_bar.place('relwidth'=>result.run_count/@test_total_count)
+ @assertion_count_label.value = result.assertion_count
+ @failure_count_label.value = result.failure_count
+ @error_count_label.value = result.error_count
+ end
+
+ def started(result)
+ @result = result
+ output_status("Started...")
+ end
+
+ def test_started(test_name)
+ output_status("Running #{test_name}...")
+ end
+
+ def finished(elapsed_time)
+ output_status("Finished in #{elapsed_time} seconds")
+ end
+
+ def output_status(string)
+ @status_entry.value = string
+ end
+
+ def setup_ui
+ @status_entry = TkVariable.new
+ l = TkLabel.new(nil, 'textvariable'=>@status_entry, 'relief'=>'sunken')
+ l.pack('side'=>'bottom', 'fill'=>'x')
+
+ suite_frame = TkFrame.new.pack('fill'=>'x')
+
+ @run_button = TkButton.new(suite_frame, 'text'=>'Run')
+ @run_button.pack('side'=>'right')
+
+ TkLabel.new(suite_frame, 'text'=>'Suite:').pack('side'=>'left')
+ @suite_name_entry = TkVariable.new
+ l = TkLabel.new(suite_frame, 'textvariable'=>@suite_name_entry, 'relief'=>'sunken')
+ l.pack('side'=>'left', 'fill'=>'x', 'expand'=>true)
+
+ f = TkFrame.new(nil, 'relief'=>'sunken', 'borderwidth'=>3, 'height'=>20).pack('fill'=>'x', 'padx'=>1)
+ @test_progress_bar = TkFrame.new(f, 'background'=>'green').place('anchor'=>'nw', 'relwidth'=>0.0, 'relheight'=>1.0)
+
+ info_frame = TkFrame.new.pack('fill'=>'x')
+ @test_count_label = create_count_label(info_frame, 'Tests:')
+ @assertion_count_label = create_count_label(info_frame, 'Assertions:')
+ @failure_count_label = create_count_label(info_frame, 'Failures:')
+ @error_count_label = create_count_label(info_frame, 'Errors:')
+
+ if (::Tk.info('command', TkPanedWindow::TkCommandNames[0]) != "")
+ # use panedwindow
+ paned_frame = TkPanedWindow.new("orient"=>"vertical").pack('fill'=>'both', 'expand'=>true)
+
+ fault_list_frame = TkFrame.new(paned_frame)
+ detail_frame = TkFrame.new(paned_frame)
+
+ paned_frame.add(fault_list_frame, detail_frame)
+ else
+ # no panedwindow
+ paned_frame = nil
+ fault_list_frame = TkFrame.new.pack('fill'=>'both', 'expand'=>true)
+ detail_frame = TkFrame.new.pack('fill'=>'both', 'expand'=>true)
+ end
+
+ TkGrid.rowconfigure(fault_list_frame, 0, 'weight'=>1, 'minsize'=>0)
+ TkGrid.columnconfigure(fault_list_frame, 0, 'weight'=>1, 'minsize'=>0)
+
+ fault_scrollbar_y = TkScrollbar.new(fault_list_frame)
+ fault_scrollbar_x = TkScrollbar.new(fault_list_frame)
+ @fault_list = TkListbox.new(fault_list_frame)
+ @fault_list.yscrollbar(fault_scrollbar_y)
+ @fault_list.xscrollbar(fault_scrollbar_x)
+
+ TkGrid.rowconfigure(detail_frame, 0, 'weight'=>1, 'minsize'=>0)
+ TkGrid.columnconfigure(detail_frame, 0, 'weight'=>1, 'minsize'=>0)
+
+ ::Tk.grid(@fault_list, fault_scrollbar_y, 'sticky'=>'news')
+ ::Tk.grid(fault_scrollbar_x, 'sticky'=>'news')
+
+ detail_scrollbar_y = TkScrollbar.new(detail_frame)
+ detail_scrollbar_x = TkScrollbar.new(detail_frame)
+ @detail_text = TkText.new(detail_frame, 'height'=>10, 'wrap'=>'none') {
+ bindtags(bindtags - [TkText])
+ }
+ @detail_text.yscrollbar(detail_scrollbar_y)
+ @detail_text.xscrollbar(detail_scrollbar_x)
+
+ ::Tk.grid(@detail_text, detail_scrollbar_y, 'sticky'=>'news')
+ ::Tk.grid(detail_scrollbar_x, 'sticky'=>'news')
+
+ # rubber-style pane
+ if paned_frame
+ ::Tk.update
+ @height = paned_frame.winfo_height
+ paned_frame.bind('Configure', proc{|h|
+ paned_frame.sash_place(0, 0, paned_frame.sash_coord(0)[1] * h / @height)
+ @height = h
+ }, '%h')
+ end
+ end
+
+ def create_count_label(parent, label)
+ TkLabel.new(parent, 'text'=>label).pack('side'=>'left', 'expand'=>true)
+ v = TkVariable.new(0)
+ TkLabel.new(parent, 'textvariable'=>v).pack('side'=>'left', 'expand'=>true)
+ v
+ end
+ end
+ end
+ end
+ end
+end
+
+if __FILE__ == $0
+ Test::Unit::UI::Tk::TestRunner.start_command_line_test
+end
diff --git a/ruby_1_8_5/lib/test/unit/util/backtracefilter.rb b/ruby_1_8_5/lib/test/unit/util/backtracefilter.rb
new file mode 100644
index 0000000000..7ebec2dfef
--- /dev/null
+++ b/ruby_1_8_5/lib/test/unit/util/backtracefilter.rb
@@ -0,0 +1,40 @@
+module Test
+ module Unit
+ module Util
+ module BacktraceFilter
+ TESTUNIT_FILE_SEPARATORS = %r{[\\/:]}
+ TESTUNIT_PREFIX = __FILE__.split(TESTUNIT_FILE_SEPARATORS)[0..-3]
+ TESTUNIT_RB_FILE = /\.rb\Z/
+
+ def filter_backtrace(backtrace, prefix=nil)
+ return ["No backtrace"] unless(backtrace)
+ split_p = if(prefix)
+ prefix.split(TESTUNIT_FILE_SEPARATORS)
+ else
+ TESTUNIT_PREFIX
+ end
+ match = proc do |e|
+ split_e = e.split(TESTUNIT_FILE_SEPARATORS)[0, split_p.size]
+ next false unless(split_e[0..-2] == split_p[0..-2])
+ split_e[-1].sub(TESTUNIT_RB_FILE, '') == split_p[-1]
+ end
+ return backtrace unless(backtrace.detect(&match))
+ found_prefix = false
+ new_backtrace = backtrace.reverse.reject do |e|
+ if(match[e])
+ found_prefix = true
+ true
+ elsif(found_prefix)
+ false
+ else
+ true
+ end
+ end.reverse
+ new_backtrace = (new_backtrace.empty? ? backtrace : new_backtrace)
+ new_backtrace = new_backtrace.reject(&match)
+ new_backtrace.empty? ? backtrace : new_backtrace
+ end
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/test/unit/util/observable.rb b/ruby_1_8_5/lib/test/unit/util/observable.rb
new file mode 100644
index 0000000000..3567d34271
--- /dev/null
+++ b/ruby_1_8_5/lib/test/unit/util/observable.rb
@@ -0,0 +1,90 @@
+#--
+#
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+require 'test/unit/util/procwrapper'
+
+module Test
+ module Unit
+ module Util
+
+ # This is a utility class that allows anything mixing
+ # it in to notify a set of listeners about interesting
+ # events.
+ module Observable
+ # We use this for defaults since nil might mean something
+ NOTHING = "NOTHING/#{__id__}"
+
+ # Adds the passed proc as a listener on the
+ # channel indicated by channel_name. listener_key
+ # is used to remove the listener later; if none is
+ # specified, the proc itself is used.
+ #
+ # Whatever is used as the listener_key is
+ # returned, making it very easy to use the proc
+ # itself as the listener_key:
+ #
+ # listener = add_listener("Channel") { ... }
+ # remove_listener("Channel", listener)
+ def add_listener(channel_name, listener_key=NOTHING, &listener) # :yields: value
+ unless(block_given?)
+ raise ArgumentError.new("No callback was passed as a listener")
+ end
+
+ key = listener_key
+ if (listener_key == NOTHING)
+ listener_key = listener
+ key = ProcWrapper.new(listener)
+ end
+
+ channels[channel_name] ||= {}
+ channels[channel_name][key] = listener
+ return listener_key
+ end
+
+ # Removes the listener indicated by listener_key
+ # from the channel indicated by
+ # channel_name. Returns the registered proc, or
+ # nil if none was found.
+ def remove_listener(channel_name, listener_key)
+ channel = channels[channel_name]
+ return nil unless (channel)
+ key = listener_key
+ if (listener_key.instance_of?(Proc))
+ key = ProcWrapper.new(listener_key)
+ end
+ if (channel.has_key?(key))
+ return channel.delete(key)
+ end
+ return nil
+ end
+
+ # Calls all the procs registered on the channel
+ # indicated by channel_name. If value is
+ # specified, it is passed in to the procs,
+ # otherwise they are called with no arguments.
+ #
+ #--
+ #
+ # Perhaps this should be private? Would it ever
+ # make sense for an external class to call this
+ # method directly?
+ def notify_listeners(channel_name, *arguments)
+ channel = channels[channel_name]
+ return 0 unless (channel)
+ listeners = channel.values
+ listeners.each { |listener| listener.call(*arguments) }
+ return listeners.size
+ end
+
+ private
+ def channels
+ @channels ||= {}
+ return @channels
+ end
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/test/unit/util/procwrapper.rb b/ruby_1_8_5/lib/test/unit/util/procwrapper.rb
new file mode 100644
index 0000000000..ad7252186d
--- /dev/null
+++ b/ruby_1_8_5/lib/test/unit/util/procwrapper.rb
@@ -0,0 +1,48 @@
+#--
+#
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+module Test
+ module Unit
+ module Util
+
+ # Allows the storage of a Proc passed through '&' in a
+ # hash.
+ #
+ # Note: this may be inefficient, since the hash being
+ # used is not necessarily very good. In Observable,
+ # efficiency is not too important, since the hash is
+ # only accessed when adding and removing listeners,
+ # not when notifying.
+
+ class ProcWrapper
+
+ # Creates a new wrapper for a_proc.
+ def initialize(a_proc)
+ @a_proc = a_proc
+ @hash = a_proc.inspect.sub(/^(#<#{a_proc.class}:)/){''}.sub(/(>)$/){''}.hex
+ end
+
+ def hash
+ return @hash
+ end
+
+ def ==(other)
+ case(other)
+ when ProcWrapper
+ return @a_proc == other.to_proc
+ else
+ return super
+ end
+ end
+ alias :eql? :==
+
+ def to_proc
+ return @a_proc
+ end
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/thread.rb b/ruby_1_8_5/lib/thread.rb
new file mode 100644
index 0000000000..640bea5111
--- /dev/null
+++ b/ruby_1_8_5/lib/thread.rb
@@ -0,0 +1,484 @@
+#
+# thread.rb - thread support classes
+# $Date: 2005/06/07 09:41:17 $
+# by Yukihiro Matsumoto <matz@netlab.co.jp>
+#
+# Copyright (C) 2001 Yukihiro Matsumoto
+# Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+# Copyright (C) 2000 Information-technology Promotion Agency, Japan
+#
+
+unless defined? Thread
+ fail "Thread not available for this ruby interpreter"
+end
+
+unless defined? ThreadError
+ class ThreadError<StandardError
+ end
+end
+
+if $DEBUG
+ Thread.abort_on_exception = true
+end
+
+class Thread
+ #
+ # Wraps a block in Thread.critical, restoring the original value upon exit
+ # from the critical section.
+ #
+ def Thread.exclusive
+ _old = Thread.critical
+ begin
+ Thread.critical = true
+ return yield
+ ensure
+ Thread.critical = _old
+ end
+ end
+end
+
+#
+# Mutex implements a simple semaphore that can be used to coordinate access to
+# shared data from multiple concurrent threads.
+#
+# Example:
+#
+# require 'thread'
+# semaphore = Mutex.new
+#
+# a = Thread.new {
+# semaphore.synchronize {
+# # access shared resource
+# }
+# }
+#
+# b = Thread.new {
+# semaphore.synchronize {
+# # access shared resource
+# }
+# }
+#
+class Mutex
+ #
+ # Creates a new Mutex
+ #
+ def initialize
+ @waiting = []
+ @locked = false;
+ @waiting.taint # enable tainted comunication
+ self.taint
+ end
+
+ #
+ # Returns +true+ if this lock is currently held by some thread.
+ #
+ def locked?
+ @locked
+ end
+
+ #
+ # Attempts to obtain the lock and returns immediately. Returns +true+ if the
+ # lock was granted.
+ #
+ def try_lock
+ result = false
+ Thread.critical = true
+ unless @locked
+ @locked = true
+ result = true
+ end
+ Thread.critical = false
+ result
+ end
+
+ #
+ # Attempts to grab the lock and waits if it isn't available.
+ #
+ def lock
+ while (Thread.critical = true; @locked)
+ @waiting.push Thread.current
+ Thread.stop
+ end
+ @locked = true
+ Thread.critical = false
+ self
+ end
+
+ #
+ # Releases the lock. Returns +nil+ if ref wasn't locked.
+ #
+ def unlock
+ return unless @locked
+ Thread.critical = true
+ @locked = false
+ begin
+ t = @waiting.shift
+ t.wakeup if t
+ rescue ThreadError
+ retry
+ end
+ Thread.critical = false
+ begin
+ t.run if t
+ rescue ThreadError
+ end
+ self
+ end
+
+ #
+ # Obtains a lock, runs the block, and releases the lock when the block
+ # completes. See the example under Mutex.
+ #
+ def synchronize
+ lock
+ begin
+ yield
+ ensure
+ unlock
+ end
+ end
+
+ #
+ # If the mutex is locked, unlocks the mutex, wakes one waiting thread, and
+ # yields in a critical section.
+ #
+ def exclusive_unlock
+ return unless @locked
+ Thread.exclusive do
+ @locked = false
+ begin
+ t = @waiting.shift
+ t.wakeup if t
+ rescue ThreadError
+ retry
+ end
+ yield
+ end
+ self
+ end
+end
+
+#
+# ConditionVariable objects augment class Mutex. Using condition variables,
+# it is possible to suspend while in the middle of a critical section until a
+# resource becomes available.
+#
+# Example:
+#
+# require 'thread'
+#
+# mutex = Mutex.new
+# resource = ConditionVariable.new
+#
+# a = Thread.new {
+# mutex.synchronize {
+# # Thread 'a' now needs the resource
+# resource.wait(mutex)
+# # 'a' can now have the resource
+# }
+# }
+#
+# b = Thread.new {
+# mutex.synchronize {
+# # Thread 'b' has finished using the resource
+# resource.signal
+# }
+# }
+#
+class ConditionVariable
+ #
+ # Creates a new ConditionVariable
+ #
+ def initialize
+ @waiters = []
+ end
+
+ #
+ # Releases the lock held in +mutex+ and waits; reacquires the lock on wakeup.
+ #
+ def wait(mutex)
+ begin
+ mutex.exclusive_unlock do
+ @waiters.push(Thread.current)
+ Thread.stop
+ end
+ ensure
+ mutex.lock
+ end
+ end
+
+ #
+ # Wakes up the first thread in line waiting for this lock.
+ #
+ def signal
+ begin
+ t = @waiters.shift
+ t.run if t
+ rescue ThreadError
+ retry
+ end
+ end
+
+ #
+ # Wakes up all threads waiting for this lock.
+ #
+ def broadcast
+ waiters0 = nil
+ Thread.exclusive do
+ waiters0 = @waiters.dup
+ @waiters.clear
+ end
+ for t in waiters0
+ begin
+ t.run
+ rescue ThreadError
+ end
+ end
+ end
+end
+
+#
+# This class provides a way to synchronize communication between threads.
+#
+# Example:
+#
+# require 'thread'
+#
+# queue = Queue.new
+#
+# producer = Thread.new do
+# 5.times do |i|
+# sleep rand(i) # simulate expense
+# queue << i
+# puts "#{i} produced"
+# end
+# end
+#
+# consumer = Thread.new do
+# 5.times do |i|
+# value = queue.pop
+# sleep rand(i/2) # simulate expense
+# puts "consumed #{value}"
+# end
+# end
+#
+# consumer.join
+#
+class Queue
+ #
+ # Creates a new queue.
+ #
+ def initialize
+ @que = []
+ @waiting = []
+ @que.taint # enable tainted comunication
+ @waiting.taint
+ self.taint
+ end
+
+ #
+ # Pushes +obj+ to the queue.
+ #
+ def push(obj)
+ Thread.critical = true
+ @que.push obj
+ begin
+ t = @waiting.shift
+ t.wakeup if t
+ rescue ThreadError
+ retry
+ ensure
+ Thread.critical = false
+ end
+ begin
+ t.run if t
+ rescue ThreadError
+ end
+ end
+
+ #
+ # Alias of push
+ #
+ alias << push
+
+ #
+ # Alias of push
+ #
+ alias enq push
+
+ #
+ # Retrieves data from the queue. If the queue is empty, the calling thread is
+ # suspended until data is pushed onto the queue. If +non_block+ is true, the
+ # thread isn't suspended, and an exception is raised.
+ #
+ def pop(non_block=false)
+ while (Thread.critical = true; @que.empty?)
+ raise ThreadError, "queue empty" if non_block
+ @waiting.push Thread.current
+ Thread.stop
+ end
+ @que.shift
+ ensure
+ Thread.critical = false
+ end
+
+ #
+ # Alias of pop
+ #
+ alias shift pop
+
+ #
+ # Alias of pop
+ #
+ alias deq pop
+
+ #
+ # Returns +true+ is the queue is empty.
+ #
+ def empty?
+ @que.empty?
+ end
+
+ #
+ # Removes all objects from the queue.
+ #
+ def clear
+ @que.clear
+ end
+
+ #
+ # Returns the length of the queue.
+ #
+ def length
+ @que.length
+ end
+
+ #
+ # Alias of length.
+ #
+ alias size length
+
+ #
+ # Returns the number of threads waiting on the queue.
+ #
+ def num_waiting
+ @waiting.size
+ end
+end
+
+#
+# This class represents queues of specified size capacity. The push operation
+# may be blocked if the capacity is full.
+#
+# See Queue for an example of how a SizedQueue works.
+#
+class SizedQueue<Queue
+ #
+ # Creates a fixed-length queue with a maximum size of +max+.
+ #
+ def initialize(max)
+ raise ArgumentError, "queue size must be positive" unless max > 0
+ @max = max
+ @queue_wait = []
+ @queue_wait.taint # enable tainted comunication
+ super()
+ end
+
+ #
+ # Returns the maximum size of the queue.
+ #
+ def max
+ @max
+ end
+
+ #
+ # Sets the maximum size of the queue.
+ #
+ def max=(max)
+ Thread.critical = true
+ if max <= @max
+ @max = max
+ Thread.critical = false
+ else
+ diff = max - @max
+ @max = max
+ Thread.critical = false
+ diff.times do
+ begin
+ t = @queue_wait.shift
+ t.run if t
+ rescue ThreadError
+ retry
+ end
+ end
+ end
+ max
+ end
+
+ #
+ # Pushes +obj+ to the queue. If there is no space left in the queue, waits
+ # until space becomes available.
+ #
+ def push(obj)
+ Thread.critical = true
+ while @que.length >= @max
+ @queue_wait.push Thread.current
+ Thread.stop
+ Thread.critical = true
+ end
+ super
+ end
+
+ #
+ # Alias of push
+ #
+ alias << push
+
+ #
+ # Alias of push
+ #
+ alias enq push
+
+ #
+ # Retrieves data from the queue and runs a waiting thread, if any.
+ #
+ def pop(*args)
+ retval = super
+ Thread.critical = true
+ if @que.length < @max
+ begin
+ t = @queue_wait.shift
+ t.wakeup if t
+ rescue ThreadError
+ retry
+ ensure
+ Thread.critical = false
+ end
+ begin
+ t.run if t
+ rescue ThreadError
+ end
+ end
+ retval
+ end
+
+ #
+ # Alias of pop
+ #
+ alias shift pop
+
+ #
+ # Alias of pop
+ #
+ alias deq pop
+
+ #
+ # Returns the number of threads waiting on the queue.
+ #
+ def num_waiting
+ @waiting.size + @queue_wait.size
+ end
+end
+
+# Documentation comments:
+# - How do you make RDoc inherit documentation from superclass?
diff --git a/ruby_1_8_5/lib/thwait.rb b/ruby_1_8_5/lib/thwait.rb
new file mode 100644
index 0000000000..95d294a85d
--- /dev/null
+++ b/ruby_1_8_5/lib/thwait.rb
@@ -0,0 +1,169 @@
+#
+# thwait.rb - thread synchronization class
+# $Release Version: 0.9 $
+# $Revision: 1.3 $
+# $Date: 1998/06/26 03:19:34 $
+# by Keiju ISHITSUKA(Nihpon Rational Software Co.,Ltd.)
+#
+# --
+# feature:
+# provides synchronization for multiple threads.
+#
+# class methods:
+# * ThreadsWait.all_waits(thread1,...)
+# waits until all of specified threads are terminated.
+# if a block is supplied for the method, evaluates it for
+# each thread termination.
+# * th = ThreadsWait.new(thread1,...)
+# creates synchronization object, specifying thread(s) to wait.
+#
+# methods:
+# * th.threads
+# list threads to be synchronized
+# * th.empty?
+# is there any thread to be synchronized.
+# * th.finished?
+# is there already terminated thread.
+# * th.join(thread1,...)
+# wait for specified thread(s).
+# * th.join_nowait(threa1,...)
+# specifies thread(s) to wait. non-blocking.
+# * th.next_wait
+# waits until any of specified threads is terminated.
+# * th.all_waits
+# waits until all of specified threads are terminated.
+# if a block is supplied for the method, evaluates it for
+# each thread termination.
+#
+
+require "thread.rb"
+require "e2mmap.rb"
+
+#
+# This class watches for termination of multiple threads. Basic functionality
+# (wait until specified threads have terminated) can be accessed through the
+# class method ThreadsWait::all_waits. Finer control can be gained using
+# instance methods.
+#
+# Example:
+#
+# ThreadsWait.all_wait(thr1, thr2, ...) do |t|
+# STDERR.puts "Thread #{t} has terminated."
+# end
+#
+class ThreadsWait
+ RCS_ID='-$Id: thwait.rb,v 1.3 1998/06/26 03:19:34 keiju Exp keiju $-'
+
+ Exception2MessageMapper.extend_to(binding)
+ def_exception("ErrNoWaitingThread", "No threads for waiting.")
+ def_exception("ErrNoFinishedThread", "No finished threads.")
+
+ #
+ # Waits until all specified threads have terminated. If a block is provided,
+ # it is executed for each thread termination.
+ #
+ def ThreadsWait.all_waits(*threads) # :yield: thread
+ tw = ThreadsWait.new(*threads)
+ if block_given?
+ tw.all_waits do |th|
+ yield th
+ end
+ else
+ tw.all_waits
+ end
+ end
+
+ #
+ # Creates a ThreadsWait object, specifying the threads to wait on.
+ # Non-blocking.
+ #
+ def initialize(*threads)
+ @threads = []
+ @wait_queue = Queue.new
+ join_nowait(*threads) unless threads.empty?
+ end
+
+ # Returns the array of threads in the wait queue.
+ attr :threads
+
+ #
+ # Returns +true+ if there are no threads to be synchronized.
+ #
+ def empty?
+ @threads.empty?
+ end
+
+ #
+ # Returns +true+ if any thread has terminated.
+ #
+ def finished?
+ !@wait_queue.empty?
+ end
+
+ #
+ # Waits for specified threads to terminate.
+ #
+ def join(*threads)
+ join_nowait(*threads)
+ next_wait
+ end
+
+ #
+ # Specifies the threads that this object will wait for, but does not actually
+ # wait.
+ #
+ def join_nowait(*threads)
+ threads.flatten!
+ @threads.concat threads
+ for th in threads
+ Thread.start(th) do |t|
+ begin
+ t.join
+ ensure
+ @wait_queue.push t
+ end
+ end
+ end
+ end
+
+ #
+ # Waits until any of the specified threads has terminated, and returns the one
+ # that does.
+ #
+ # If there is no thread to wait, raises +ErrNoWaitingThread+. If +nonblock+
+ # is true, and there is no terminated thread, raises +ErrNoFinishedThread+.
+ #
+ def next_wait(nonblock = nil)
+ ThreadsWait.fail ErrNoWaitingThread if @threads.empty?
+ begin
+ @threads.delete(th = @wait_queue.pop(nonblock))
+ th
+ rescue ThreadError
+ ThreadsWait.fail ErrNoFinishedThread
+ end
+ end
+
+ #
+ # Waits until all of the specified threads are terminated. If a block is
+ # supplied for the method, it is executed for each thread termination.
+ #
+ # Raises exceptions in the same manner as +next_wait+.
+ #
+ def all_waits
+ until @threads.empty?
+ th = next_wait
+ yield th if block_given?
+ end
+ end
+end
+
+ThWait = ThreadsWait
+
+
+# Documentation comments:
+# - Source of documentation is evenly split between Nutshell, existing
+# comments, and my own rephrasing.
+# - I'm not particularly confident that the comments are all exactly correct.
+# - The history, etc., up the top appears in the RDoc output. Perhaps it would
+# be better to direct that not to appear, and put something else there
+# instead.
diff --git a/ruby_1_8_5/lib/time.rb b/ruby_1_8_5/lib/time.rb
new file mode 100644
index 0000000000..3b4ee9e72a
--- /dev/null
+++ b/ruby_1_8_5/lib/time.rb
@@ -0,0 +1,797 @@
+
+#
+# == Introduction
+#
+# This library extends the Time class:
+# * conversion between date string and time object.
+# * date-time defined by RFC 2822
+# * HTTP-date defined by RFC 2616
+# * dateTime defined by XML Schema Part 2: Datatypes (ISO 8601)
+# * various formats handled by Date._parse (string to time only)
+#
+# == Design Issues
+#
+# === Specialized interface
+#
+# This library provides methods dedicated to special purposes:
+# * RFC 2822, RFC 2616 and XML Schema.
+# * They makes usual life easier.
+#
+# === Doesn't depend on strftime
+#
+# This library doesn't use +strftime+. Especially #rfc2822 doesn't depend
+# on +strftime+ because:
+#
+# * %a and %b are locale sensitive
+#
+# Since they are locale sensitive, they may be replaced to
+# invalid weekday/month name in some locales.
+# Since ruby-1.6 doesn't invoke setlocale by default,
+# the problem doesn't arise until some external library invokes setlocale.
+# Ruby/GTK is the example of such library.
+#
+# * %z is not portable
+#
+# %z is required to generate zone in date-time of RFC 2822
+# but it is not portable.
+#
+# == Revision Information
+#
+# $Id$
+#
+
+require 'parsedate'
+
+#
+# Implements the extensions to the Time class that are described in the
+# documentation for the time.rb library.
+#
+class Time
+ class << Time
+
+ ZoneOffset = {
+ 'UTC' => 0,
+ # ISO 8601
+ 'Z' => 0,
+ # RFC 822
+ 'UT' => 0, 'GMT' => 0,
+ 'EST' => -5, 'EDT' => -4,
+ 'CST' => -6, 'CDT' => -5,
+ 'MST' => -7, 'MDT' => -6,
+ 'PST' => -8, 'PDT' => -7,
+ # Following definition of military zones is original one.
+ # See RFC 1123 and RFC 2822 for the error in RFC 822.
+ 'A' => +1, 'B' => +2, 'C' => +3, 'D' => +4, 'E' => +5, 'F' => +6,
+ 'G' => +7, 'H' => +8, 'I' => +9, 'K' => +10, 'L' => +11, 'M' => +12,
+ 'N' => -1, 'O' => -2, 'P' => -3, 'Q' => -4, 'R' => -5, 'S' => -6,
+ 'T' => -7, 'U' => -8, 'V' => -9, 'W' => -10, 'X' => -11, 'Y' => -12,
+ }
+ def zone_offset(zone, year=Time.now.year)
+ off = nil
+ zone = zone.upcase
+ if /\A([+-])(\d\d):?(\d\d)\z/ =~ zone
+ off = ($1 == '-' ? -1 : 1) * ($2.to_i * 60 + $3.to_i) * 60
+ elsif /\A[+-]\d\d\z/ =~ zone
+ off = zone.to_i * 3600
+ elsif ZoneOffset.include?(zone)
+ off = ZoneOffset[zone] * 3600
+ elsif ((t = Time.local(year, 1, 1)).zone.upcase == zone rescue false)
+ off = t.utc_offset
+ elsif ((t = Time.local(year, 7, 1)).zone.upcase == zone rescue false)
+ off = t.utc_offset
+ end
+ off
+ end
+
+ def zone_utc?(zone)
+ # * +0000 means localtime. [RFC 2822]
+ # * GMT is a localtime abbreviation in Europe/London, etc.
+ if /\A(?:-00:00|-0000|-00|UTC|Z|UT)\z/i =~ zone
+ true
+ else
+ false
+ end
+ end
+ private :zone_utc?
+
+ LeapYearMonthDays = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
+ CommonYearMonthDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
+ def month_days(y, m)
+ if ((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0)
+ LeapYearMonthDays[m-1]
+ else
+ CommonYearMonthDays[m-1]
+ end
+ end
+ private :month_days
+
+ def apply_offset(year, mon, day, hour, min, sec, off)
+ if off < 0
+ off = -off
+ off, o = off.divmod(60)
+ if o != 0 then sec += o; o, sec = sec.divmod(60); off += o end
+ off, o = off.divmod(60)
+ if o != 0 then min += o; o, min = min.divmod(60); off += o end
+ off, o = off.divmod(24)
+ if o != 0 then hour += o; o, hour = hour.divmod(24); off += o end
+ if off != 0
+ day += off
+ if month_days(year, mon) < day
+ mon += 1
+ if 12 < mon
+ mon = 1
+ year += 1
+ end
+ day = 1
+ end
+ end
+ elsif 0 < off
+ off, o = off.divmod(60)
+ if o != 0 then sec -= o; o, sec = sec.divmod(60); off -= o end
+ off, o = off.divmod(60)
+ if o != 0 then min -= o; o, min = min.divmod(60); off -= o end
+ off, o = off.divmod(24)
+ if o != 0 then hour -= o; o, hour = hour.divmod(24); off -= o end
+ if off != 0 then
+ day -= off
+ if day < 1
+ mon -= 1
+ if mon < 1
+ year -= 1
+ mon = 12
+ end
+ day = month_days(year, mon)
+ end
+ end
+ end
+ return year, mon, day, hour, min, sec
+ end
+ private :apply_offset
+
+ def make_time(year, mon, day, hour, min, sec, sec_fraction, zone, now)
+ usec = nil
+ usec = (sec_fraction * 1000000).to_i if sec_fraction
+ if now
+ begin
+ break if year; year = now.year
+ break if mon; mon = now.mon
+ break if day; day = now.day
+ break if hour; hour = now.hour
+ break if min; min = now.min
+ break if sec; sec = now.sec
+ break if sec_fraction; usec = now.tv_usec
+ end until true
+ end
+
+ year ||= 1970
+ mon ||= 1
+ day ||= 1
+ hour ||= 0
+ min ||= 0
+ sec ||= 0
+ usec ||= 0
+
+ off = nil
+ off = zone_offset(zone, year) if zone
+
+ if off
+ year, mon, day, hour, min, sec =
+ apply_offset(year, mon, day, hour, min, sec, off)
+ t = Time.utc(year, mon, day, hour, min, sec, usec)
+ t.localtime if !zone_utc?(zone)
+ t
+ else
+ Time.local(year, mon, day, hour, min, sec, usec)
+ end
+ end
+ private :make_time
+
+ #
+ # Parses +date+ using Date._parse and converts it to a Time object.
+ #
+ # If a block is given, the year described in +date+ is converted by the
+ # block. For example:
+ #
+ # Time.parse(...) {|y| y < 100 ? (y >= 69 ? y + 1900 : y + 2000) : y}
+ #
+ # If the upper components of the given time are broken or missing, they are
+ # supplied with those of +now+. For the lower components, the minimum
+ # values (1 or 0) are assumed if broken or missing. For example:
+ #
+ # # Suppose it is "Thu Nov 29 14:33:20 GMT 2001" now and
+ # # your timezone is GMT:
+ # Time.parse("16:30") #=> Thu Nov 29 16:30:00 GMT 2001
+ # Time.parse("7/23") #=> Mon Jul 23 00:00:00 GMT 2001
+ # Time.parse("Aug 31") #=> Fri Aug 31 00:00:00 GMT 2001
+ #
+ # Since there are numerous conflicts among locally defined timezone
+ # abbreviations all over the world, this method is not made to
+ # understand all of them. For example, the abbreviation "CST" is
+ # used variously as:
+ #
+ # -06:00 in America/Chicago,
+ # -05:00 in America/Havana,
+ # +08:00 in Asia/Harbin,
+ # +09:30 in Australia/Darwin,
+ # +10:30 in Australia/Adelaide,
+ # etc.
+ #
+ # Based on the fact, this method only understands the timezone
+ # abbreviations described in RFC 822 and the system timezone, in the
+ # order named. (i.e. a definition in RFC 822 overrides the system
+ # timezone definition.) The system timezone is taken from
+ # <tt>Time.local(year, 1, 1).zone</tt> and
+ # <tt>Time.local(year, 7, 1).zone</tt>.
+ # If the extracted timezone abbreviation does not match any of them,
+ # it is ignored and the given time is regarded as a local time.
+ #
+ # ArgumentError is raised if Date._parse cannot extract information from
+ # +date+ or Time class cannot represent specified date.
+ #
+ # This method can be used as fail-safe for other parsing methods as:
+ #
+ # Time.rfc2822(date) rescue Time.parse(date)
+ # Time.httpdate(date) rescue Time.parse(date)
+ # Time.xmlschema(date) rescue Time.parse(date)
+ #
+ # A failure for Time.parse should be checked, though.
+ #
+ def parse(date, now=Time.now)
+ d = Date._parse(date, false)
+ year = d[:year]
+ year = yield(year) if year && block_given?
+ make_time(year, d[:mon], d[:mday], d[:hour], d[:min], d[:sec], d[:sec_fraction], d[:zone], now)
+ end
+
+ MonthValue = {
+ 'JAN' => 1, 'FEB' => 2, 'MAR' => 3, 'APR' => 4, 'MAY' => 5, 'JUN' => 6,
+ 'JUL' => 7, 'AUG' => 8, 'SEP' => 9, 'OCT' =>10, 'NOV' =>11, 'DEC' =>12
+ }
+
+ #
+ # Parses +date+ as date-time defined by RFC 2822 and converts it to a Time
+ # object. The format is identical to the date format defined by RFC 822 and
+ # updated by RFC 1123.
+ #
+ # ArgumentError is raised if +date+ is not compliant with RFC 2822
+ # or Time class cannot represent specified date.
+ #
+ # See #rfc2822 for more information on this format.
+ #
+ def rfc2822(date)
+ if /\A\s*
+ (?:(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s*,\s*)?
+ (\d{1,2})\s+
+ (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+
+ (\d{2,})\s+
+ (\d{2})\s*
+ :\s*(\d{2})\s*
+ (?::\s*(\d{2}))?\s+
+ ([+-]\d{4}|
+ UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT|[A-IK-Z])/ix =~ date
+ # Since RFC 2822 permit comments, the regexp has no right anchor.
+ day = $1.to_i
+ mon = MonthValue[$2.upcase]
+ year = $3.to_i
+ hour = $4.to_i
+ min = $5.to_i
+ sec = $6 ? $6.to_i : 0
+ zone = $7
+
+ # following year completion is compliant with RFC 2822.
+ year = if year < 50
+ 2000 + year
+ elsif year < 1000
+ 1900 + year
+ else
+ year
+ end
+
+ year, mon, day, hour, min, sec =
+ apply_offset(year, mon, day, hour, min, sec, zone_offset(zone))
+ t = Time.utc(year, mon, day, hour, min, sec)
+ t.localtime if !zone_utc?(zone)
+ t
+ else
+ raise ArgumentError.new("not RFC 2822 compliant date: #{date.inspect}")
+ end
+ end
+ alias rfc822 rfc2822
+
+ #
+ # Parses +date+ as HTTP-date defined by RFC 2616 and converts it to a Time
+ # object.
+ #
+ # ArgumentError is raised if +date+ is not compliant with RFC 2616 or Time
+ # class cannot represent specified date.
+ #
+ # See #httpdate for more information on this format.
+ #
+ def httpdate(date)
+ if /\A\s*
+ (?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),\x20
+ (\d{2})\x20
+ (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\x20
+ (\d{4})\x20
+ (\d{2}):(\d{2}):(\d{2})\x20
+ GMT
+ \s*\z/ix =~ date
+ Time.rfc2822(date)
+ elsif /\A\s*
+ (?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday),\x20
+ (\d\d)-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(\d\d)\x20
+ (\d\d):(\d\d):(\d\d)\x20
+ GMT
+ \s*\z/ix =~ date
+ Time.parse(date)
+ elsif /\A\s*
+ (?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\x20
+ (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\x20
+ (\d\d|\x20\d)\x20
+ (\d\d):(\d\d):(\d\d)\x20
+ (\d{4})
+ \s*\z/ix =~ date
+ Time.utc($6.to_i, MonthValue[$1.upcase], $2.to_i,
+ $3.to_i, $4.to_i, $5.to_i)
+ else
+ raise ArgumentError.new("not RFC 2616 compliant date: #{date.inspect}")
+ end
+ end
+
+ #
+ # Parses +date+ as dateTime defined by XML Schema and converts it to a Time
+ # object. The format is restricted version of the format defined by ISO
+ # 8601.
+ #
+ # ArgumentError is raised if +date+ is not compliant with the format or Time
+ # class cannot represent specified date.
+ #
+ # See #xmlschema for more information on this format.
+ #
+ def xmlschema(date)
+ if /\A\s*
+ (-?\d+)-(\d\d)-(\d\d)
+ T
+ (\d\d):(\d\d):(\d\d)
+ (\.\d*)?
+ (Z|[+-]\d\d:\d\d)?
+ \s*\z/ix =~ date
+ year = $1.to_i
+ mon = $2.to_i
+ day = $3.to_i
+ hour = $4.to_i
+ min = $5.to_i
+ sec = $6.to_i
+ usec = 0
+ usec = $7.to_f * 1000000 if $7
+ if $8
+ zone = $8
+ year, mon, day, hour, min, sec =
+ apply_offset(year, mon, day, hour, min, sec, zone_offset(zone))
+ Time.utc(year, mon, day, hour, min, sec, usec)
+ else
+ Time.local(year, mon, day, hour, min, sec, usec)
+ end
+ else
+ raise ArgumentError.new("invalid date: #{date.inspect}")
+ end
+ end
+ alias iso8601 xmlschema
+ end # class << self
+
+ #
+ # Returns a string which represents the time as date-time defined by RFC 2822:
+ #
+ # day-of-week, DD month-name CCYY hh:mm:ss zone
+ #
+ # where zone is [+-]hhmm.
+ #
+ # If +self+ is a UTC time, -0000 is used as zone.
+ #
+ def rfc2822
+ sprintf('%s, %02d %s %d %02d:%02d:%02d ',
+ RFC2822_DAY_NAME[wday],
+ day, RFC2822_MONTH_NAME[mon-1], year,
+ hour, min, sec) +
+ if utc?
+ '-0000'
+ else
+ off = utc_offset
+ sign = off < 0 ? '-' : '+'
+ sprintf('%s%02d%02d', sign, *(off.abs / 60).divmod(60))
+ end
+ end
+ alias rfc822 rfc2822
+
+ RFC2822_DAY_NAME = [
+ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'
+ ]
+ RFC2822_MONTH_NAME = [
+ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
+ 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
+ ]
+
+ #
+ # Returns a string which represents the time as rfc1123-date of HTTP-date
+ # defined by RFC 2616:
+ #
+ # day-of-week, DD month-name CCYY hh:mm:ss GMT
+ #
+ # Note that the result is always UTC (GMT).
+ #
+ def httpdate
+ t = dup.utc
+ sprintf('%s, %02d %s %d %02d:%02d:%02d GMT',
+ RFC2822_DAY_NAME[t.wday],
+ t.day, RFC2822_MONTH_NAME[t.mon-1], t.year,
+ t.hour, t.min, t.sec)
+ end
+
+ #
+ # Returns a string which represents the time as dateTime defined by XML
+ # Schema:
+ #
+ # CCYY-MM-DDThh:mm:ssTZD
+ # CCYY-MM-DDThh:mm:ss.sssTZD
+ #
+ # where TZD is Z or [+-]hh:mm.
+ #
+ # If self is a UTC time, Z is used as TZD. [+-]hh:mm is used otherwise.
+ #
+ # +fractional_seconds+ specifies a number of digits of fractional seconds.
+ # Its default value is 0.
+ #
+ def xmlschema(fraction_digits=0)
+ sprintf('%d-%02d-%02dT%02d:%02d:%02d',
+ year, mon, day, hour, min, sec) +
+ if fraction_digits == 0
+ ''
+ elsif fraction_digits <= 6
+ '.' + sprintf('%06d', usec)[0, fraction_digits]
+ else
+ '.' + sprintf('%06d', usec) + '0' * (fraction_digits - 6)
+ end +
+ if utc?
+ 'Z'
+ else
+ off = utc_offset
+ sign = off < 0 ? '-' : '+'
+ sprintf('%s%02d:%02d', sign, *(off.abs / 60).divmod(60))
+ end
+ end
+ alias iso8601 xmlschema
+end
+
+if __FILE__ == $0
+ require 'test/unit'
+
+ class TimeExtentionTest < Test::Unit::TestCase # :nodoc:
+ def test_rfc822
+ assert_equal(Time.utc(1976, 8, 26, 14, 30) + 4 * 3600,
+ Time.rfc2822("26 Aug 76 14:30 EDT"))
+ assert_equal(Time.utc(1976, 8, 27, 9, 32) + 7 * 3600,
+ Time.rfc2822("27 Aug 76 09:32 PDT"))
+ end
+
+ def test_rfc2822
+ assert_equal(Time.utc(1997, 11, 21, 9, 55, 6) + 6 * 3600,
+ Time.rfc2822("Fri, 21 Nov 1997 09:55:06 -0600"))
+ assert_equal(Time.utc(2003, 7, 1, 10, 52, 37) - 2 * 3600,
+ Time.rfc2822("Tue, 1 Jul 2003 10:52:37 +0200"))
+ assert_equal(Time.utc(1997, 11, 21, 10, 1, 10) + 6 * 3600,
+ Time.rfc2822("Fri, 21 Nov 1997 10:01:10 -0600"))
+ assert_equal(Time.utc(1997, 11, 21, 11, 0, 0) + 6 * 3600,
+ Time.rfc2822("Fri, 21 Nov 1997 11:00:00 -0600"))
+ assert_equal(Time.utc(1997, 11, 24, 14, 22, 1) + 8 * 3600,
+ Time.rfc2822("Mon, 24 Nov 1997 14:22:01 -0800"))
+ begin
+ Time.at(-1)
+ rescue ArgumentError
+ # ignore
+ else
+ assert_equal(Time.utc(1969, 2, 13, 23, 32, 54) + 3 * 3600 + 30 * 60,
+ Time.rfc2822("Thu, 13 Feb 1969 23:32:54 -0330"))
+ assert_equal(Time.utc(1969, 2, 13, 23, 32, 0) + 3 * 3600 + 30 * 60,
+ Time.rfc2822(" Thu,
+ 13
+ Feb
+ 1969
+ 23:32
+ -0330 (Newfoundland Time)"))
+ end
+ assert_equal(Time.utc(1997, 11, 21, 9, 55, 6),
+ Time.rfc2822("21 Nov 97 09:55:06 GMT"))
+ assert_equal(Time.utc(1997, 11, 21, 9, 55, 6) + 6 * 3600,
+ Time.rfc2822("Fri, 21 Nov 1997 09 : 55 : 06 -0600"))
+ assert_raise(ArgumentError) {
+ # inner comment is not supported.
+ Time.rfc2822("Fri, 21 Nov 1997 09(comment): 55 : 06 -0600")
+ }
+ end
+
+ def test_rfc2616
+ t = Time.utc(1994, 11, 6, 8, 49, 37)
+ assert_equal(t, Time.httpdate("Sun, 06 Nov 1994 08:49:37 GMT"))
+ assert_equal(t, Time.httpdate("Sunday, 06-Nov-94 08:49:37 GMT"))
+ assert_equal(t, Time.httpdate("Sun Nov 6 08:49:37 1994"))
+ assert_equal(Time.utc(1995, 11, 15, 6, 25, 24),
+ Time.httpdate("Wed, 15 Nov 1995 06:25:24 GMT"))
+ assert_equal(Time.utc(1995, 11, 15, 4, 58, 8),
+ Time.httpdate("Wed, 15 Nov 1995 04:58:08 GMT"))
+ assert_equal(Time.utc(1994, 11, 15, 8, 12, 31),
+ Time.httpdate("Tue, 15 Nov 1994 08:12:31 GMT"))
+ assert_equal(Time.utc(1994, 12, 1, 16, 0, 0),
+ Time.httpdate("Thu, 01 Dec 1994 16:00:00 GMT"))
+ assert_equal(Time.utc(1994, 10, 29, 19, 43, 31),
+ Time.httpdate("Sat, 29 Oct 1994 19:43:31 GMT"))
+ assert_equal(Time.utc(1994, 11, 15, 12, 45, 26),
+ Time.httpdate("Tue, 15 Nov 1994 12:45:26 GMT"))
+ assert_equal(Time.utc(1999, 12, 31, 23, 59, 59),
+ Time.httpdate("Fri, 31 Dec 1999 23:59:59 GMT"))
+ end
+
+ def test_rfc3339
+ t = Time.utc(1985, 4, 12, 23, 20, 50, 520000)
+ s = "1985-04-12T23:20:50.52Z"
+ assert_equal(t, Time.iso8601(s))
+ assert_equal(s, t.iso8601(2))
+
+ t = Time.utc(1996, 12, 20, 0, 39, 57)
+ s = "1996-12-19T16:39:57-08:00"
+ assert_equal(t, Time.iso8601(s))
+ # There is no way to generate time string with arbitrary timezone.
+ s = "1996-12-20T00:39:57Z"
+ assert_equal(t, Time.iso8601(s))
+ assert_equal(s, t.iso8601)
+
+ t = Time.utc(1990, 12, 31, 23, 59, 60)
+ s = "1990-12-31T23:59:60Z"
+ assert_equal(t, Time.iso8601(s))
+ # leap second is representable only if timezone file has it.
+ s = "1990-12-31T15:59:60-08:00"
+ assert_equal(t, Time.iso8601(s))
+
+ begin
+ Time.at(-1)
+ rescue ArgumentError
+ # ignore
+ else
+ t = Time.utc(1937, 1, 1, 11, 40, 27, 870000)
+ s = "1937-01-01T12:00:27.87+00:20"
+ assert_equal(t, Time.iso8601(s))
+ end
+ end
+
+ # http://www.w3.org/TR/xmlschema-2/
+ def test_xmlschema
+ assert_equal(Time.utc(1999, 5, 31, 13, 20, 0) + 5 * 3600,
+ Time.xmlschema("1999-05-31T13:20:00-05:00"))
+ assert_equal(Time.local(2000, 1, 20, 12, 0, 0),
+ Time.xmlschema("2000-01-20T12:00:00"))
+ assert_equal(Time.utc(2000, 1, 20, 12, 0, 0),
+ Time.xmlschema("2000-01-20T12:00:00Z"))
+ assert_equal(Time.utc(2000, 1, 20, 12, 0, 0) - 12 * 3600,
+ Time.xmlschema("2000-01-20T12:00:00+12:00"))
+ assert_equal(Time.utc(2000, 1, 20, 12, 0, 0) + 13 * 3600,
+ Time.xmlschema("2000-01-20T12:00:00-13:00"))
+ assert_equal(Time.utc(2000, 3, 4, 23, 0, 0) - 3 * 3600,
+ Time.xmlschema("2000-03-04T23:00:00+03:00"))
+ assert_equal(Time.utc(2000, 3, 4, 20, 0, 0),
+ Time.xmlschema("2000-03-04T20:00:00Z"))
+ assert_equal(Time.local(2000, 1, 15, 0, 0, 0),
+ Time.xmlschema("2000-01-15T00:00:00"))
+ assert_equal(Time.local(2000, 2, 15, 0, 0, 0),
+ Time.xmlschema("2000-02-15T00:00:00"))
+ assert_equal(Time.local(2000, 1, 15, 12, 0, 0),
+ Time.xmlschema("2000-01-15T12:00:00"))
+ assert_equal(Time.utc(2000, 1, 16, 12, 0, 0),
+ Time.xmlschema("2000-01-16T12:00:00Z"))
+ assert_equal(Time.local(2000, 1, 1, 12, 0, 0),
+ Time.xmlschema("2000-01-01T12:00:00"))
+ assert_equal(Time.utc(1999, 12, 31, 23, 0, 0),
+ Time.xmlschema("1999-12-31T23:00:00Z"))
+ assert_equal(Time.local(2000, 1, 16, 12, 0, 0),
+ Time.xmlschema("2000-01-16T12:00:00"))
+ assert_equal(Time.local(2000, 1, 16, 0, 0, 0),
+ Time.xmlschema("2000-01-16T00:00:00"))
+ assert_equal(Time.utc(2000, 1, 12, 12, 13, 14),
+ Time.xmlschema("2000-01-12T12:13:14Z"))
+ assert_equal(Time.utc(2001, 4, 17, 19, 23, 17, 300000),
+ Time.xmlschema("2001-04-17T19:23:17.3Z"))
+ end
+
+ def test_encode_xmlschema
+ t = Time.utc(2001, 4, 17, 19, 23, 17, 300000)
+ assert_equal("2001-04-17T19:23:17Z", t.xmlschema)
+ assert_equal("2001-04-17T19:23:17.3Z", t.xmlschema(1))
+ assert_equal("2001-04-17T19:23:17.300000Z", t.xmlschema(6))
+ assert_equal("2001-04-17T19:23:17.3000000Z", t.xmlschema(7))
+
+ t = Time.utc(2001, 4, 17, 19, 23, 17, 123456)
+ assert_equal("2001-04-17T19:23:17.1234560Z", t.xmlschema(7))
+ assert_equal("2001-04-17T19:23:17.123456Z", t.xmlschema(6))
+ assert_equal("2001-04-17T19:23:17.12345Z", t.xmlschema(5))
+ assert_equal("2001-04-17T19:23:17.1Z", t.xmlschema(1))
+
+ begin
+ Time.at(-1)
+ rescue ArgumentError
+ # ignore
+ else
+ t = Time.utc(1960, 12, 31, 23, 0, 0, 123456)
+ assert_equal("1960-12-31T23:00:00.123456Z", t.xmlschema(6))
+ end
+ end
+
+ def test_completion
+ now = Time.local(2001,11,29,21,26,35)
+ assert_equal(Time.local( 2001,11,29,21,12),
+ Time.parse("2001/11/29 21:12", now))
+ assert_equal(Time.local( 2001,11,29),
+ Time.parse("2001/11/29", now))
+ assert_equal(Time.local( 2001,11,29),
+ Time.parse( "11/29", now))
+ #assert_equal(Time.local(2001,11,1), Time.parse("Nov", now))
+ assert_equal(Time.local( 2001,11,29,10,22),
+ Time.parse( "10:22", now))
+ end
+
+ def test_invalid
+ # They were actually used in some web sites.
+ assert_raise(ArgumentError) { Time.httpdate("1 Dec 2001 10:23:57 GMT") }
+ assert_raise(ArgumentError) { Time.httpdate("Sat, 1 Dec 2001 10:25:42 GMT") }
+ assert_raise(ArgumentError) { Time.httpdate("Sat, 1-Dec-2001 10:53:55 GMT") }
+ assert_raise(ArgumentError) { Time.httpdate("Saturday, 01-Dec-2001 10:15:34 GMT") }
+ assert_raise(ArgumentError) { Time.httpdate("Saturday, 01-Dec-101 11:10:07 GMT") }
+ assert_raise(ArgumentError) { Time.httpdate("Fri, 30 Nov 2001 21:30:00 JST") }
+
+ # They were actually used in some mails.
+ assert_raise(ArgumentError) { Time.rfc2822("01-5-20") }
+ assert_raise(ArgumentError) { Time.rfc2822("7/21/00") }
+ assert_raise(ArgumentError) { Time.rfc2822("2001-8-28") }
+ assert_raise(ArgumentError) { Time.rfc2822("00-5-6 1:13:06") }
+ assert_raise(ArgumentError) { Time.rfc2822("2001-9-27 9:36:49") }
+ assert_raise(ArgumentError) { Time.rfc2822("2000-12-13 11:01:11") }
+ assert_raise(ArgumentError) { Time.rfc2822("2001/10/17 04:29:55") }
+ assert_raise(ArgumentError) { Time.rfc2822("9/4/2001 9:23:19 PM") }
+ assert_raise(ArgumentError) { Time.rfc2822("01 Nov 2001 09:04:31") }
+ assert_raise(ArgumentError) { Time.rfc2822("13 Feb 2001 16:4 GMT") }
+ assert_raise(ArgumentError) { Time.rfc2822("01 Oct 00 5:41:19 PM") }
+ assert_raise(ArgumentError) { Time.rfc2822("2 Jul 00 00:51:37 JST") }
+ assert_raise(ArgumentError) { Time.rfc2822("01 11 2001 06:55:57 -0500") }
+ assert_raise(ArgumentError) { Time.rfc2822("18 \343\366\356\341\370 2000") }
+ assert_raise(ArgumentError) { Time.rfc2822("Fri, Oct 2001 18:53:32") }
+ assert_raise(ArgumentError) { Time.rfc2822("Fri, 2 Nov 2001 03:47:54") }
+ assert_raise(ArgumentError) { Time.rfc2822("Fri, 27 Jul 2001 11.14.14 +0200") }
+ assert_raise(ArgumentError) { Time.rfc2822("Thu, 2 Nov 2000 04:13:53 -600") }
+ assert_raise(ArgumentError) { Time.rfc2822("Wed, 5 Apr 2000 22:57:09 JST") }
+ assert_raise(ArgumentError) { Time.rfc2822("Mon, 11 Sep 2000 19:47:33 00000") }
+ assert_raise(ArgumentError) { Time.rfc2822("Fri, 28 Apr 2000 20:40:47 +-900") }
+ assert_raise(ArgumentError) { Time.rfc2822("Fri, 19 Jan 2001 8:15:36 AM -0500") }
+ assert_raise(ArgumentError) { Time.rfc2822("Thursday, Sep 27 2001 7:42:35 AM EST") }
+ assert_raise(ArgumentError) { Time.rfc2822("3/11/2001 1:31:57 PM Pacific Daylight Time") }
+ assert_raise(ArgumentError) { Time.rfc2822("Mi, 28 Mrz 2001 11:51:36") }
+ assert_raise(ArgumentError) { Time.rfc2822("P, 30 sept 2001 23:03:14") }
+ assert_raise(ArgumentError) { Time.rfc2822("fr, 11 aug 2000 18:39:22") }
+ assert_raise(ArgumentError) { Time.rfc2822("Fr, 21 Sep 2001 17:44:03 -1000") }
+ assert_raise(ArgumentError) { Time.rfc2822("Mo, 18 Jun 2001 19:21:40 -1000") }
+ assert_raise(ArgumentError) { Time.rfc2822("l\366, 12 aug 2000 18:53:20") }
+ assert_raise(ArgumentError) { Time.rfc2822("l\366, 26 maj 2001 00:15:58") }
+ assert_raise(ArgumentError) { Time.rfc2822("Dom, 30 Sep 2001 17:36:30") }
+ assert_raise(ArgumentError) { Time.rfc2822("%&, 31 %2/ 2000 15:44:47 -0500") }
+ assert_raise(ArgumentError) { Time.rfc2822("dom, 26 ago 2001 03:57:07 -0300") }
+ assert_raise(ArgumentError) { Time.rfc2822("ter, 04 set 2001 16:27:58 -0300") }
+ assert_raise(ArgumentError) { Time.rfc2822("Wen, 3 oct 2001 23:17:49 -0400") }
+ assert_raise(ArgumentError) { Time.rfc2822("Wen, 3 oct 2001 23:17:49 -0400") }
+ assert_raise(ArgumentError) { Time.rfc2822("ele, 11 h: 2000 12:42:15 -0500") }
+ assert_raise(ArgumentError) { Time.rfc2822("Tue, 14 Aug 2001 3:55:3 +0200") }
+ assert_raise(ArgumentError) { Time.rfc2822("Fri, 25 Aug 2000 9:3:48 +0800") }
+ assert_raise(ArgumentError) { Time.rfc2822("Fri, 1 Dec 2000 0:57:50 EST") }
+ assert_raise(ArgumentError) { Time.rfc2822("Mon, 7 May 2001 9:39:51 +0200") }
+ assert_raise(ArgumentError) { Time.rfc2822("Wed, 1 Aug 2001 16:9:15 +0200") }
+ assert_raise(ArgumentError) { Time.rfc2822("Wed, 23 Aug 2000 9:17:36 +0800") }
+ assert_raise(ArgumentError) { Time.rfc2822("Fri, 11 Aug 2000 10:4:42 +0800") }
+ assert_raise(ArgumentError) { Time.rfc2822("Sat, 15 Sep 2001 13:22:2 +0300") }
+ assert_raise(ArgumentError) { Time.rfc2822("Wed,16 \276\305\324\302 2001 20:06:25 +0800") }
+ assert_raise(ArgumentError) { Time.rfc2822("Wed,7 \312\256\322\273\324\302 2001 23:47:22 +0800") }
+ assert_raise(ArgumentError) { Time.rfc2822("=?iso-8859-1?Q?(=C5=DA),?= 10 2 2001 23:32:26 +0900 (JST)") }
+ assert_raise(ArgumentError) { Time.rfc2822("\307\341\314\343\332\311, 30 \344\346\335\343\310\321 2001 10:01:06") }
+ assert_raise(ArgumentError) { Time.rfc2822("=?iso-8859-1?Q?(=BF=E5),?= 12 =?iso-8859-1?Q?9=B7=EE?= 2001 14:52:41\n+0900 (JST)") }
+ end
+
+ def test_zone_0000
+ assert_equal(true, Time.parse("2000-01-01T00:00:00Z").utc?)
+ assert_equal(true, Time.parse("2000-01-01T00:00:00-00:00").utc?)
+ assert_equal(false, Time.parse("2000-01-01T00:00:00+00:00").utc?)
+ assert_equal(false, Time.parse("Sat, 01 Jan 2000 00:00:00 GMT").utc?)
+ assert_equal(true, Time.parse("Sat, 01 Jan 2000 00:00:00 -0000").utc?)
+ assert_equal(false, Time.parse("Sat, 01 Jan 2000 00:00:00 +0000").utc?)
+ assert_equal(false, Time.rfc2822("Sat, 01 Jan 2000 00:00:00 GMT").utc?)
+ assert_equal(true, Time.rfc2822("Sat, 01 Jan 2000 00:00:00 -0000").utc?)
+ assert_equal(false, Time.rfc2822("Sat, 01 Jan 2000 00:00:00 +0000").utc?)
+ assert_equal(true, Time.rfc2822("Sat, 01 Jan 2000 00:00:00 UTC").utc?)
+ end
+
+ def test_parse_leap_second
+ t = Time.utc(1998,12,31,23,59,59)
+ assert_equal(t, Time.parse("Thu Dec 31 23:59:59 UTC 1998"))
+ assert_equal(t, Time.parse("Fri Dec 31 23:59:59 -0000 1998"));t.localtime
+ assert_equal(t, Time.parse("Fri Jan 1 08:59:59 +0900 1999"))
+ assert_equal(t, Time.parse("Fri Jan 1 00:59:59 +0100 1999"))
+ assert_equal(t, Time.parse("Fri Dec 31 23:59:59 +0000 1998"))
+ assert_equal(t, Time.parse("Fri Dec 31 22:59:59 -0100 1998"));t.utc
+ t += 1
+ assert_equal(t, Time.parse("Thu Dec 31 23:59:60 UTC 1998"))
+ assert_equal(t, Time.parse("Fri Dec 31 23:59:60 -0000 1998"));t.localtime
+ assert_equal(t, Time.parse("Fri Jan 1 08:59:60 +0900 1999"))
+ assert_equal(t, Time.parse("Fri Jan 1 00:59:60 +0100 1999"))
+ assert_equal(t, Time.parse("Fri Dec 31 23:59:60 +0000 1998"))
+ assert_equal(t, Time.parse("Fri Dec 31 22:59:60 -0100 1998"));t.utc
+ t += 1 if t.sec == 60
+ assert_equal(t, Time.parse("Thu Jan 1 00:00:00 UTC 1999"))
+ assert_equal(t, Time.parse("Fri Jan 1 00:00:00 -0000 1999"));t.localtime
+ assert_equal(t, Time.parse("Fri Jan 1 09:00:00 +0900 1999"))
+ assert_equal(t, Time.parse("Fri Jan 1 01:00:00 +0100 1999"))
+ assert_equal(t, Time.parse("Fri Jan 1 00:00:00 +0000 1999"))
+ assert_equal(t, Time.parse("Fri Dec 31 23:00:00 -0100 1998"))
+ end
+
+ def test_rfc2822_leap_second
+ t = Time.utc(1998,12,31,23,59,59)
+ assert_equal(t, Time.rfc2822("Thu, 31 Dec 1998 23:59:59 UTC"))
+ assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:59 -0000"));t.localtime
+ assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 08:59:59 +0900"))
+ assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 00:59:59 +0100"))
+ assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:59 +0000"))
+ assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 22:59:59 -0100"));t.utc
+ t += 1
+ assert_equal(t, Time.rfc2822("Thu, 31 Dec 1998 23:59:60 UTC"))
+ assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:60 -0000"));t.localtime
+ assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 08:59:60 +0900"))
+ assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 00:59:60 +0100"))
+ assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:59:60 +0000"))
+ assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 22:59:60 -0100"));t.utc
+ t += 1 if t.sec == 60
+ assert_equal(t, Time.rfc2822("Thu, 1 Jan 1999 00:00:00 UTC"))
+ assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 00:00:00 -0000"));t.localtime
+ assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 09:00:00 +0900"))
+ assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 01:00:00 +0100"))
+ assert_equal(t, Time.rfc2822("Fri, 1 Jan 1999 00:00:00 +0000"))
+ assert_equal(t, Time.rfc2822("Fri, 31 Dec 1998 23:00:00 -0100"))
+ end
+
+ def test_xmlschema_leap_second
+ t = Time.utc(1998,12,31,23,59,59)
+ assert_equal(t, Time.xmlschema("1998-12-31T23:59:59Z"))
+ assert_equal(t, Time.xmlschema("1998-12-31T23:59:59-00:00"));t.localtime
+ assert_equal(t, Time.xmlschema("1999-01-01T08:59:59+09:00"))
+ assert_equal(t, Time.xmlschema("1999-01-01T00:59:59+01:00"))
+ assert_equal(t, Time.xmlschema("1998-12-31T23:59:59+00:00"))
+ assert_equal(t, Time.xmlschema("1998-12-31T22:59:59-01:00"));t.utc
+ t += 1
+ assert_equal(t, Time.xmlschema("1998-12-31T23:59:60Z"))
+ assert_equal(t, Time.xmlschema("1998-12-31T23:59:60-00:00"));t.localtime
+ assert_equal(t, Time.xmlschema("1999-01-01T08:59:60+09:00"))
+ assert_equal(t, Time.xmlschema("1999-01-01T00:59:60+01:00"))
+ assert_equal(t, Time.xmlschema("1998-12-31T23:59:60+00:00"))
+ assert_equal(t, Time.xmlschema("1998-12-31T22:59:60-01:00"));t.utc
+ t += 1 if t.sec == 60
+ assert_equal(t, Time.xmlschema("1999-01-01T00:00:00Z"))
+ assert_equal(t, Time.xmlschema("1999-01-01T00:00:00-00:00"));t.localtime
+ assert_equal(t, Time.xmlschema("1999-01-01T09:00:00+09:00"))
+ assert_equal(t, Time.xmlschema("1999-01-01T01:00:00+01:00"))
+ assert_equal(t, Time.xmlschema("1999-01-01T00:00:00+00:00"))
+ assert_equal(t, Time.xmlschema("1998-12-31T23:00:00-01:00"))
+ end
+
+ def test_ruby_talk_152866
+ t = Time::xmlschema('2005-08-30T22:48:00-07:00')
+ assert_equal(31, t.day)
+ assert_equal(8, t.mon)
+ end
+
+ def test_parse_fraction
+ assert_equal(500000, Time.parse("2000-01-01T00:00:00.5+00:00").tv_usec)
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/timeout.rb b/ruby_1_8_5/lib/timeout.rb
new file mode 100644
index 0000000000..dc92964c0b
--- /dev/null
+++ b/ruby_1_8_5/lib/timeout.rb
@@ -0,0 +1,105 @@
+#--
+# = timeout.rb
+#
+# execution timeout
+#
+# = Copyright
+#
+# Copyright:: (C) 2000 Network Applied Communication Laboratory, Inc.
+# Copyright:: (C) 2000 Information-technology Promotion Agency, Japan
+#
+#++
+#
+# = Description
+#
+# A way of performing a potentially long-running operation in a thread, and
+# terminating it's execution if it hasn't finished within fixed amount of
+# time.
+#
+# Previous versions of timeout didn't use a module for namespace. This version
+# provides both Timeout.timeout, and a backwards-compatible #timeout.
+#
+# = Synopsis
+#
+# require 'timeout'
+# status = Timeout::timeout(5) {
+# # Something that should be interrupted if it takes too much time...
+# }
+#
+
+module Timeout
+
+ ##
+ # Raised by Timeout#timeout when the block times out.
+
+ class Error<Interrupt
+ end
+
+ ##
+ # Executes the method's block. If the block execution terminates before +sec+
+ # seconds has passed, it returns true. If not, it terminates the execution
+ # and raises +exception+ (which defaults to Timeout::Error).
+ #
+ # Note that this is both a method of module Timeout, so you can 'include
+ # Timeout' into your classes so they have a #timeout method, as well as a
+ # module method, so you can call it directly as Timeout.timeout().
+
+ def timeout(sec, exception=Error)
+ return yield if sec == nil or sec.zero?
+ raise ThreadError, "timeout within critical session" if Thread.critical
+ begin
+ x = Thread.current
+ y = Thread.start {
+ sleep sec
+ x.raise exception, "execution expired" if x.alive?
+ }
+ yield sec
+ # return true
+ ensure
+ y.kill if y and y.alive?
+ end
+ end
+
+ module_function :timeout
+
+end
+
+##
+# Identical to:
+#
+# Timeout::timeout(n, e, &block).
+#
+# Defined for backwards compatibility with earlier versions of timeout.rb, see
+# Timeout#timeout.
+
+def timeout(n, e=Timeout::Error, &block) # :nodoc:
+ Timeout::timeout(n, e, &block)
+end
+
+##
+# Another name for Timeout::Error, defined for backwards compatibility with
+# earlier versions of timeout.rb.
+
+TimeoutError = Timeout::Error # :nodoc:
+
+if __FILE__ == $0
+ p timeout(5) {
+ 45
+ }
+ p timeout(5, TimeoutError) {
+ 45
+ }
+ p timeout(nil) {
+ 54
+ }
+ p timeout(0) {
+ 54
+ }
+ p timeout(5) {
+ loop {
+ p 10
+ sleep 1
+ }
+ }
+end
+
diff --git a/ruby_1_8_5/lib/tmpdir.rb b/ruby_1_8_5/lib/tmpdir.rb
new file mode 100644
index 0000000000..bfc214b6b9
--- /dev/null
+++ b/ruby_1_8_5/lib/tmpdir.rb
@@ -0,0 +1,45 @@
+#
+# tmpdir - retrieve temporary directory path
+#
+# $Id: tmpdir.rb,v 1.5.2.1 2005/12/15 15:57:05 matz Exp $
+#
+
+class Dir
+
+ @@systmpdir = '/tmp'
+
+ begin
+ require 'Win32API'
+ max_pathlen = 260
+ windir = ' '*(max_pathlen+1)
+ begin
+ getdir = Win32API.new('kernel32', 'GetSystemWindowsDirectory', 'PL', 'L')
+ rescue RuntimeError
+ getdir = Win32API.new('kernel32', 'GetWindowsDirectory', 'PL', 'L')
+ end
+ getdir.call(windir, windir.size)
+ windir = File.expand_path(windir.rstrip.untaint)
+ temp = File.join(windir, 'temp')
+ @@systmpdir = temp if File.directory?(temp) and File.writable?(temp)
+ rescue LoadError
+ end
+
+ ##
+ # Returns the operating system's temporary file path.
+
+ def Dir::tmpdir
+ tmp = '.'
+ if $SAFE > 0
+ tmp = @@systmpdir
+ else
+ for dir in [ENV['TMPDIR'], ENV['TMP'], ENV['TEMP'],
+ ENV['USERPROFILE'], @@systmpdir, '/tmp']
+ if dir and File.directory?(dir) and File.writable?(dir)
+ tmp = dir
+ break
+ end
+ end
+ end
+ File.expand_path(tmp)
+ end
+end
diff --git a/ruby_1_8_5/lib/tracer.rb b/ruby_1_8_5/lib/tracer.rb
new file mode 100644
index 0000000000..71aa49c306
--- /dev/null
+++ b/ruby_1_8_5/lib/tracer.rb
@@ -0,0 +1,167 @@
+#
+# tracer.rb -
+# $Release Version: 0.2$
+# $Revision: 1.8 $
+# $Date: 1998/05/19 03:42:49 $
+# by Keiju ISHITSUKA(Nippon Rational Inc.)
+#
+# --
+#
+#
+#
+
+#
+# tracer main class
+#
+class Tracer
+ @RCS_ID='-$Id: tracer.rb,v 1.8 1998/05/19 03:42:49 keiju Exp keiju $-'
+
+ @stdout = STDOUT
+ @verbose = false
+ class << self
+ attr :verbose, true
+ alias verbose? verbose
+ attr :stdout, true
+ end
+
+ EVENT_SYMBOL = {
+ "line" => "-",
+ "call" => ">",
+ "return" => "<",
+ "class" => "C",
+ "end" => "E",
+ "c-call" => ">",
+ "c-return" => "<",
+ }
+
+ def initialize
+ @threads = Hash.new
+ if defined? Thread.main
+ @threads[Thread.main.object_id] = 0
+ else
+ @threads[Thread.current.object_id] = 0
+ end
+
+ @get_line_procs = {}
+
+ @filters = []
+ end
+
+ def stdout
+ Tracer.stdout
+ end
+
+ def on
+ if block_given?
+ on
+ begin
+ yield
+ ensure
+ off
+ end
+ else
+ set_trace_func method(:trace_func).to_proc
+ stdout.print "Trace on\n" if Tracer.verbose?
+ end
+ end
+
+ def off
+ set_trace_func nil
+ stdout.print "Trace off\n" if Tracer.verbose?
+ end
+
+ def add_filter(p = proc)
+ @filters.push p
+ end
+
+ def set_get_line_procs(file, p = proc)
+ @get_line_procs[file] = p
+ end
+
+ def get_line(file, line)
+ if p = @get_line_procs[file]
+ return p.call(line)
+ end
+
+ unless list = SCRIPT_LINES__[file]
+ begin
+ f = open(file)
+ begin
+ SCRIPT_LINES__[file] = list = f.readlines
+ ensure
+ f.close
+ end
+ rescue
+ SCRIPT_LINES__[file] = list = []
+ end
+ end
+
+ if l = list[line - 1]
+ l
+ else
+ "-\n"
+ end
+ end
+
+ def get_thread_no
+ if no = @threads[Thread.current.object_id]
+ no
+ else
+ @threads[Thread.current.object_id] = @threads.size
+ end
+ end
+
+ def trace_func(event, file, line, id, binding, klass, *)
+ return if file == __FILE__
+
+ for p in @filters
+ return unless p.call event, file, line, id, binding, klass
+ end
+
+ saved_crit = Thread.critical
+ Thread.critical = true
+ stdout.printf("#%d:%s:%d:%s:%s: %s",
+ get_thread_no,
+ file,
+ line,
+ klass || '',
+ EVENT_SYMBOL[event],
+ get_line(file, line))
+ Thread.critical = saved_crit
+ end
+
+ Single = new
+ def Tracer.on
+ if block_given?
+ Single.on{yield}
+ else
+ Single.on
+ end
+ end
+
+ def Tracer.off
+ Single.off
+ end
+
+ def Tracer.set_get_line_procs(file_name, p = proc)
+ Single.set_get_line_procs(file_name, p)
+ end
+
+ def Tracer.add_filter(p = proc)
+ Single.add_filter(p)
+ end
+
+end
+
+SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
+
+if $0 == __FILE__
+ # direct call
+
+ $0 = ARGV[0]
+ ARGV.shift
+ Tracer.on
+ require $0
+elsif caller(0).size == 1
+ Tracer.on
+end
diff --git a/ruby_1_8_5/lib/tsort.rb b/ruby_1_8_5/lib/tsort.rb
new file mode 100644
index 0000000000..a014e7f6c2
--- /dev/null
+++ b/ruby_1_8_5/lib/tsort.rb
@@ -0,0 +1,290 @@
+#!/usr/bin/env ruby
+#--
+# tsort.rb - provides a module for topological sorting and strongly connected components.
+#++
+#
+
+#
+# TSort implements topological sorting using Tarjan's algorithm for
+# strongly connected components.
+#
+# TSort is designed to be able to be used with any object which can be
+# interpreted as a directed graph.
+#
+# TSort requires two methods to interpret an object as a graph,
+# tsort_each_node and tsort_each_child.
+#
+# * tsort_each_node is used to iterate for all nodes over a graph.
+# * tsort_each_child is used to iterate for child nodes of a given node.
+#
+# The equality of nodes are defined by eql? and hash since
+# TSort uses Hash internally.
+#
+# == A Simple Example
+#
+# The following example demonstrates how to mix the TSort module into an
+# existing class (in this case, Hash). Here, we're treating each key in
+# the hash as a node in the graph, and so we simply alias the required
+# #tsort_each_node method to Hash's #each_key method. For each key in the
+# hash, the associated value is an array of the node's child nodes. This
+# choice in turn leads to our implementation of the required #tsort_each_child
+# method, which fetches the array of child nodes and then iterates over that
+# array using the user-supplied block.
+#
+# require 'tsort'
+#
+# class Hash
+# include TSort
+# alias tsort_each_node each_key
+# def tsort_each_child(node, &block)
+# fetch(node).each(&block)
+# end
+# end
+#
+# {1=>[2, 3], 2=>[3], 3=>[], 4=>[]}.tsort
+# #=> [3, 2, 1, 4]
+#
+# {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}.strongly_connected_components
+# #=> [[4], [2, 3], [1]]
+#
+# == A More Realistic Example
+#
+# A very simple `make' like tool can be implemented as follows:
+#
+# require 'tsort'
+#
+# class Make
+# def initialize
+# @dep = {}
+# @dep.default = []
+# end
+#
+# def rule(outputs, inputs=[], &block)
+# triple = [outputs, inputs, block]
+# outputs.each {|f| @dep[f] = [triple]}
+# @dep[triple] = inputs
+# end
+#
+# def build(target)
+# each_strongly_connected_component_from(target) {|ns|
+# if ns.length != 1
+# fs = ns.delete_if {|n| Array === n}
+# raise TSort::Cyclic.new("cyclic dependencies: #{fs.join ', '}")
+# end
+# n = ns.first
+# if Array === n
+# outputs, inputs, block = n
+# inputs_time = inputs.map {|f| File.mtime f}.max
+# begin
+# outputs_time = outputs.map {|f| File.mtime f}.min
+# rescue Errno::ENOENT
+# outputs_time = nil
+# end
+# if outputs_time == nil ||
+# inputs_time != nil && outputs_time <= inputs_time
+# sleep 1 if inputs_time != nil && inputs_time.to_i == Time.now.to_i
+# block.call
+# end
+# end
+# }
+# end
+#
+# def tsort_each_child(node, &block)
+# @dep[node].each(&block)
+# end
+# include TSort
+# end
+#
+# def command(arg)
+# print arg, "\n"
+# system arg
+# end
+#
+# m = Make.new
+# m.rule(%w[t1]) { command 'date > t1' }
+# m.rule(%w[t2]) { command 'date > t2' }
+# m.rule(%w[t3]) { command 'date > t3' }
+# m.rule(%w[t4], %w[t1 t3]) { command 'cat t1 t3 > t4' }
+# m.rule(%w[t5], %w[t4 t2]) { command 'cat t4 t2 > t5' }
+# m.build('t5')
+#
+# == Bugs
+#
+# * 'tsort.rb' is wrong name because this library uses
+# Tarjan's algorithm for strongly connected components.
+# Although 'strongly_connected_components.rb' is correct but too long.
+#
+# == References
+#
+# R. E. Tarjan, "Depth First Search and Linear Graph Algorithms",
+# <em>SIAM Journal on Computing</em>, Vol. 1, No. 2, pp. 146-160, June 1972.
+#
+
+module TSort
+ class Cyclic < StandardError
+ end
+
+ #
+ # Returns a topologically sorted array of nodes.
+ # The array is sorted from children to parents, i.e.
+ # the first element has no child and the last node has no parent.
+ #
+ # If there is a cycle, TSort::Cyclic is raised.
+ #
+ def tsort
+ result = []
+ tsort_each {|element| result << element}
+ result
+ end
+
+ #
+ # The iterator version of the #tsort method.
+ # <tt><em>obj</em>.tsort_each</tt> is similar to <tt><em>obj</em>.tsort.each</tt>, but
+ # modification of _obj_ during the iteration may lead to unexpected results.
+ #
+ # #tsort_each returns +nil+.
+ # If there is a cycle, TSort::Cyclic is raised.
+ #
+ def tsort_each # :yields: node
+ each_strongly_connected_component {|component|
+ if component.size == 1
+ yield component.first
+ else
+ raise Cyclic.new("topological sort failed: #{component.inspect}")
+ end
+ }
+ end
+
+ #
+ # Returns strongly connected components as an array of arrays of nodes.
+ # The array is sorted from children to parents.
+ # Each elements of the array represents a strongly connected component.
+ #
+ def strongly_connected_components
+ result = []
+ each_strongly_connected_component {|component| result << component}
+ result
+ end
+
+ #
+ # The iterator version of the #strongly_connected_components method.
+ # <tt><em>obj</em>.each_strongly_connected_component</tt> is similar to
+ # <tt><em>obj</em>.strongly_connected_components.each</tt>, but
+ # modification of _obj_ during the iteration may lead to unexpected results.
+ #
+ #
+ # #each_strongly_connected_component returns +nil+.
+ #
+ def each_strongly_connected_component # :yields: nodes
+ id_map = {}
+ stack = []
+ tsort_each_node {|node|
+ unless id_map.include? node
+ each_strongly_connected_component_from(node, id_map, stack) {|c|
+ yield c
+ }
+ end
+ }
+ nil
+ end
+
+ #
+ # Iterates over strongly connected component in the subgraph reachable from
+ # _node_.
+ #
+ # Return value is unspecified.
+ #
+ # #each_strongly_connected_component_from doesn't call #tsort_each_node.
+ #
+ def each_strongly_connected_component_from(node, id_map={}, stack=[]) # :yields: nodes
+ minimum_id = node_id = id_map[node] = id_map.size
+ stack_length = stack.length
+ stack << node
+
+ tsort_each_child(node) {|child|
+ if id_map.include? child
+ child_id = id_map[child]
+ minimum_id = child_id if child_id && child_id < minimum_id
+ else
+ sub_minimum_id =
+ each_strongly_connected_component_from(child, id_map, stack) {|c|
+ yield c
+ }
+ minimum_id = sub_minimum_id if sub_minimum_id < minimum_id
+ end
+ }
+
+ if node_id == minimum_id
+ component = stack.slice!(stack_length .. -1)
+ component.each {|n| id_map[n] = nil}
+ yield component
+ end
+
+ minimum_id
+ end
+
+ #
+ # Should be implemented by a extended class.
+ #
+ # #tsort_each_node is used to iterate for all nodes over a graph.
+ #
+ def tsort_each_node # :yields: node
+ raise NotImplementedError.new
+ end
+
+ #
+ # Should be implemented by a extended class.
+ #
+ # #tsort_each_child is used to iterate for child nodes of _node_.
+ #
+ def tsort_each_child(node) # :yields: child
+ raise NotImplementedError.new
+ end
+end
+
+if __FILE__ == $0
+ require 'test/unit'
+
+ class TSortHash < Hash # :nodoc:
+ include TSort
+ alias tsort_each_node each_key
+ def tsort_each_child(node, &block)
+ fetch(node).each(&block)
+ end
+ end
+
+ class TSortArray < Array # :nodoc:
+ include TSort
+ alias tsort_each_node each_index
+ def tsort_each_child(node, &block)
+ fetch(node).each(&block)
+ end
+ end
+
+ class TSortTest < Test::Unit::TestCase # :nodoc:
+ def test_dag
+ h = TSortHash[{1=>[2, 3], 2=>[3], 3=>[]}]
+ assert_equal([3, 2, 1], h.tsort)
+ assert_equal([[3], [2], [1]], h.strongly_connected_components)
+ end
+
+ def test_cycle
+ h = TSortHash[{1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}]
+ assert_equal([[4], [2, 3], [1]],
+ h.strongly_connected_components.map {|nodes| nodes.sort})
+ assert_raise(TSort::Cyclic) { h.tsort }
+ end
+
+ def test_array
+ a = TSortArray[[1], [0], [0], [2]]
+ assert_equal([[0, 1], [2], [3]],
+ a.strongly_connected_components.map {|nodes| nodes.sort})
+
+ a = TSortArray[[], [0]]
+ assert_equal([[0], [1]],
+ a.strongly_connected_components.map {|nodes| nodes.sort})
+ end
+ end
+
+end
+
diff --git a/ruby_1_8_5/lib/un.rb b/ruby_1_8_5/lib/un.rb
new file mode 100644
index 0000000000..9bf6c13146
--- /dev/null
+++ b/ruby_1_8_5/lib/un.rb
@@ -0,0 +1,235 @@
+#
+# = un.rb
+#
+# Copyright (c) 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+#
+# This program is free software.
+# You can distribute/modify this program under the same terms of Ruby.
+#
+# == Utilities to replace common UNIX commands in Makefiles etc
+#
+# == SYNOPSIS
+#
+# ruby -run -e cp -- [OPTION] SOURCE DEST
+# ruby -run -e ln -- [OPTION] TARGET LINK_NAME
+# ruby -run -e mv -- [OPTION] SOURCE DEST
+# ruby -run -e rm -- [OPTION] FILE
+# ruby -run -e mkdir -- [OPTION] DIRS
+# ruby -run -e rmdir -- [OPTION] DIRS
+# ruby -run -e install -- [OPTION] SOURCE DEST
+# ruby -run -e chmod -- [OPTION] OCTAL-MODE FILE
+# ruby -run -e touch -- [OPTION] FILE
+# ruby -run -e help [COMMAND]
+
+require "fileutils"
+require "optparse"
+
+module FileUtils
+# @fileutils_label = ""
+ @fileutils_output = $stdout
+end
+
+def setup(options = "")
+ ARGV.map! do |x|
+ case x
+ when /^-/
+ x.delete "^-#{options}v"
+ when /[*?\[{]/
+ Dir[x]
+ else
+ x
+ end
+ end
+ ARGV.flatten!
+ ARGV.delete_if{|x| x == "-"}
+ opt_hash = {}
+ OptionParser.new do |o|
+ options.scan(/.:?/) do |s|
+ o.on("-" + s.tr(":", " ")) do |val|
+ opt_hash[s.delete(":").intern] = val
+ end
+ end
+ o.on("-v") do opt_hash[:verbose] = true end
+ o.parse!
+ end
+ yield ARGV, opt_hash
+end
+
+##
+# Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY
+#
+# ruby -run -e cp -- [OPTION] SOURCE DEST
+#
+# -p preserve file attributes if possible
+# -r copy recursively
+# -v verbose
+#
+
+def cp
+ setup("pr") do |argv, options|
+ cmd = "cp"
+ cmd += "_r" if options.delete :r
+ options[:preserve] = true if options.delete :p
+ dest = argv.pop
+ argv = argv[0] if argv.size == 1
+ FileUtils.send cmd, argv, dest, options
+ end
+end
+
+##
+# Create a link to the specified TARGET with LINK_NAME.
+#
+# ruby -run -e ln -- [OPTION] TARGET LINK_NAME
+#
+# -s make symbolic links instead of hard links
+# -f remove existing destination files
+# -v verbose
+#
+
+def ln
+ setup("sf") do |argv, options|
+ cmd = "ln"
+ cmd += "_s" if options.delete :s
+ options[:force] = true if options.delete :f
+ dest = argv.pop
+ argv = argv[0] if argv.size == 1
+ FileUtils.send cmd, argv, dest, options
+ end
+end
+
+##
+# Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.
+#
+# ruby -run -e mv -- [OPTION] SOURCE DEST
+#
+# -v verbose
+#
+
+def mv
+ setup do |argv, options|
+ dest = argv.pop
+ argv = argv[0] if argv.size == 1
+ FileUtils.mv argv, dest, options
+ end
+end
+
+##
+# Remove the FILE
+#
+# ruby -run -e rm -- [OPTION] FILE
+#
+# -f ignore nonexistent files
+# -r remove the contents of directories recursively
+# -v verbose
+#
+
+def rm
+ setup("fr") do |argv, options|
+ cmd = "rm"
+ cmd += "_r" if options.delete :r
+ options[:force] = true if options.delete :f
+ FileUtils.send cmd, argv, options
+ end
+end
+
+##
+# Create the DIR, if they do not already exist.
+#
+# ruby -run -e mkdir -- [OPTION] DIR
+#
+# -p no error if existing, make parent directories as needed
+# -v verbose
+#
+
+def mkdir
+ setup("p") do |argv, options|
+ cmd = "mkdir"
+ cmd += "_p" if options.delete :p
+ FileUtils.send cmd, argv, options
+ end
+end
+
+##
+# Remove the DIR.
+#
+# ruby -run -e rmdir -- [OPTION] DIR
+#
+# -v verbose
+#
+
+def rmdir
+ setup do |argv, options|
+ FileUtils.rmdir argv, options
+ end
+end
+
+##
+# Copy SOURCE to DEST.
+#
+# ruby -run -e install -- [OPTION] SOURCE DEST
+#
+# -p apply access/modification times of SOURCE files to
+# corresponding destination files
+# -m set permission mode (as in chmod), instead of 0755
+# -v verbose
+#
+
+def install
+ setup("pm:") do |argv, options|
+ options[:mode] = (mode = options.delete :m) ? mode.oct : 0755
+ options[:preserve] = true if options.delete :p
+ dest = argv.pop
+ argv = argv[0] if argv.size == 1
+ FileUtils.install argv, dest, options
+ end
+end
+
+##
+# Change the mode of each FILE to OCTAL-MODE.
+#
+# ruby -run -e chmod -- [OPTION] OCTAL-MODE FILE
+#
+# -v verbose
+#
+
+def chmod
+ setup do |argv, options|
+ mode = argv.shift.oct
+ FileUtils.chmod mode, argv, options
+ end
+end
+
+##
+# Update the access and modification times of each FILE to the current time.
+#
+# ruby -run -e touch -- [OPTION] FILE
+#
+# -v verbose
+#
+
+def touch
+ setup do |argv, options|
+ FileUtils.touch argv, options
+ end
+end
+
+##
+# Display help message.
+#
+# ruby -run -e help [COMMAND]
+#
+
+def help
+ setup do |argv,|
+ all = argv.empty?
+ open(__FILE__) do |me|
+ while me.gets("##\n")
+ if help = me.gets("\n\n")
+ if all or argv.delete help[/-e \w+/].sub(/-e /, "")
+ print help.gsub(/^# ?/, "")
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/uri.rb b/ruby_1_8_5/lib/uri.rb
new file mode 100644
index 0000000000..fab3b94d16
--- /dev/null
+++ b/ruby_1_8_5/lib/uri.rb
@@ -0,0 +1,28 @@
+#
+# URI support for Ruby
+#
+# Author:: Akira Yamada <akira@ruby-lang.org>
+# Documentation:: Akira Yamada <akira@ruby-lang.org>, Dmitry V. Sabanin <sdmitry@lrn.ru>
+# License::
+# Copyright (c) 2001 akira yamada <akira@ruby-lang.org>
+# You can redistribute it and/or modify it under the same term as Ruby.
+# Revision:: $Id: uri.rb,v 1.8.2.2 2004/07/17 13:07:46 akira Exp $
+#
+# See URI for documentation
+#
+
+module URI
+ # :stopdoc:
+ VERSION_CODE = '000911'.freeze
+ VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze
+ # :startdoc:
+
+end
+
+require 'uri/common'
+require 'uri/generic'
+require 'uri/ftp'
+require 'uri/http'
+require 'uri/https'
+require 'uri/ldap'
+require 'uri/mailto'
diff --git a/ruby_1_8_5/lib/uri/common.rb b/ruby_1_8_5/lib/uri/common.rb
new file mode 100644
index 0000000000..7a5207a315
--- /dev/null
+++ b/ruby_1_8_5/lib/uri/common.rb
@@ -0,0 +1,607 @@
+# = uri/common.rb
+#
+# Author:: Akira Yamada <akira@ruby-lang.org>
+# Revision:: $Id: common.rb,v 1.11.2.7 2005/06/24 04:15:20 akira Exp $
+# License::
+# You can redistribute it and/or modify it under the same term as Ruby.
+#
+
+module URI
+ module REGEXP
+ #
+ # Patterns used to parse URI's
+ #
+ module PATTERN
+ # :stopdoc:
+
+ # RFC 2396 (URI Generic Syntax)
+ # RFC 2732 (IPv6 Literal Addresses in URL's)
+ # RFC 2373 (IPv6 Addressing Architecture)
+
+ # alpha = lowalpha | upalpha
+ ALPHA = "a-zA-Z"
+ # alphanum = alpha | digit
+ ALNUM = "#{ALPHA}\\d"
+
+ # hex = digit | "A" | "B" | "C" | "D" | "E" | "F" |
+ # "a" | "b" | "c" | "d" | "e" | "f"
+ HEX = "a-fA-F\\d"
+ # escaped = "%" hex hex
+ ESCAPED = "%[#{HEX}]{2}"
+ # mark = "-" | "_" | "." | "!" | "~" | "*" | "'" |
+ # "(" | ")"
+ # unreserved = alphanum | mark
+ UNRESERVED = "-_.!~*'()#{ALNUM}"
+ # reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
+ # "$" | ","
+ # reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
+ # "$" | "," | "[" | "]" (RFC 2732)
+ RESERVED = ";/?:@&=+$,\\[\\]"
+
+ # uric = reserved | unreserved | escaped
+ URIC = "(?:[#{UNRESERVED}#{RESERVED}]|#{ESCAPED})"
+ # uric_no_slash = unreserved | escaped | ";" | "?" | ":" | "@" |
+ # "&" | "=" | "+" | "$" | ","
+ URIC_NO_SLASH = "(?:[#{UNRESERVED};?:@&=+$,]|#{ESCAPED})"
+ # query = *uric
+ QUERY = "#{URIC}*"
+ # fragment = *uric
+ FRAGMENT = "#{URIC}*"
+
+ # domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
+ DOMLABEL = "(?:[#{ALNUM}](?:[-#{ALNUM}]*[#{ALNUM}])?)"
+ # toplabel = alpha | alpha *( alphanum | "-" ) alphanum
+ TOPLABEL = "(?:[#{ALPHA}](?:[-#{ALNUM}]*[#{ALNUM}])?)"
+ # hostname = *( domainlabel "." ) toplabel [ "." ]
+ HOSTNAME = "(?:#{DOMLABEL}\\.)*#{TOPLABEL}\\.?"
+
+ # RFC 2373, APPENDIX B:
+ # IPv6address = hexpart [ ":" IPv4address ]
+ # IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
+ # hexpart = hexseq | hexseq "::" [ hexseq ] | "::" [ hexseq ]
+ # hexseq = hex4 *( ":" hex4)
+ # hex4 = 1*4HEXDIG
+ #
+ # XXX: This definition has a flaw. "::" + IPv4address must be
+ # allowed too. Here is a replacement.
+ #
+ # IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
+ IPV4ADDR = "\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"
+ # hex4 = 1*4HEXDIG
+ HEX4 = "[#{HEX}]{1,4}"
+ # lastpart = hex4 | IPv4address
+ LASTPART = "(?:#{HEX4}|#{IPV4ADDR})"
+ # hexseq1 = *( hex4 ":" ) hex4
+ HEXSEQ1 = "(?:#{HEX4}:)*#{HEX4}"
+ # hexseq2 = *( hex4 ":" ) lastpart
+ HEXSEQ2 = "(?:#{HEX4}:)*#{LASTPART}"
+ # IPv6address = hexseq2 | [ hexseq1 ] "::" [ hexseq2 ]
+ IPV6ADDR = "(?:#{HEXSEQ2}|(?:#{HEXSEQ1})?::(?:#{HEXSEQ2})?)"
+
+ # IPv6prefix = ( hexseq1 | [ hexseq1 ] "::" [ hexseq1 ] ) "/" 1*2DIGIT
+ # unused
+
+ # ipv6reference = "[" IPv6address "]" (RFC 2732)
+ IPV6REF = "\\[#{IPV6ADDR}\\]"
+
+ # host = hostname | IPv4address
+ # host = hostname | IPv4address | IPv6reference (RFC 2732)
+ HOST = "(?:#{HOSTNAME}|#{IPV4ADDR}|#{IPV6REF})"
+ # port = *digit
+ PORT = '\d*'
+ # hostport = host [ ":" port ]
+ HOSTPORT = "#{HOST}(?::#{PORT})?"
+
+ # userinfo = *( unreserved | escaped |
+ # ";" | ":" | "&" | "=" | "+" | "$" | "," )
+ USERINFO = "(?:[#{UNRESERVED};:&=+$,]|#{ESCAPED})*"
+
+ # pchar = unreserved | escaped |
+ # ":" | "@" | "&" | "=" | "+" | "$" | ","
+ PCHAR = "(?:[#{UNRESERVED}:@&=+$,]|#{ESCAPED})"
+ # param = *pchar
+ PARAM = "#{PCHAR}*"
+ # segment = *pchar *( ";" param )
+ SEGMENT = "#{PCHAR}*(?:;#{PARAM})*"
+ # path_segments = segment *( "/" segment )
+ PATH_SEGMENTS = "#{SEGMENT}(?:/#{SEGMENT})*"
+
+ # server = [ [ userinfo "@" ] hostport ]
+ SERVER = "(?:#{USERINFO}@)?#{HOSTPORT}"
+ # reg_name = 1*( unreserved | escaped | "$" | "," |
+ # ";" | ":" | "@" | "&" | "=" | "+" )
+ REG_NAME = "(?:[#{UNRESERVED}$,;+@&=+]|#{ESCAPED})+"
+ # authority = server | reg_name
+ AUTHORITY = "(?:#{SERVER}|#{REG_NAME})"
+
+ # rel_segment = 1*( unreserved | escaped |
+ # ";" | "@" | "&" | "=" | "+" | "$" | "," )
+ REL_SEGMENT = "(?:[#{UNRESERVED};@&=+$,]|#{ESCAPED})+"
+
+ # scheme = alpha *( alpha | digit | "+" | "-" | "." )
+ SCHEME = "[#{ALPHA}][-+.#{ALPHA}\\d]*"
+
+ # abs_path = "/" path_segments
+ ABS_PATH = "/#{PATH_SEGMENTS}"
+ # rel_path = rel_segment [ abs_path ]
+ REL_PATH = "#{REL_SEGMENT}(?:#{ABS_PATH})?"
+ # net_path = "//" authority [ abs_path ]
+ NET_PATH = "//#{AUTHORITY}(?:#{ABS_PATH})?"
+
+ # hier_part = ( net_path | abs_path ) [ "?" query ]
+ HIER_PART = "(?:#{NET_PATH}|#{ABS_PATH})(?:\\?(?:#{QUERY}))?"
+ # opaque_part = uric_no_slash *uric
+ OPAQUE_PART = "#{URIC_NO_SLASH}#{URIC}*"
+
+ # absoluteURI = scheme ":" ( hier_part | opaque_part )
+ ABS_URI = "#{SCHEME}:(?:#{HIER_PART}|#{OPAQUE_PART})"
+ # relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ]
+ REL_URI = "(?:#{NET_PATH}|#{ABS_PATH}|#{REL_PATH})(?:\\?#{QUERY})?"
+
+ # URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
+ URI_REF = "(?:#{ABS_URI}|#{REL_URI})?(?:##{FRAGMENT})?"
+
+ # XXX:
+ X_ABS_URI = "
+ (#{PATTERN::SCHEME}): (?# 1: scheme)
+ (?:
+ (#{PATTERN::OPAQUE_PART}) (?# 2: opaque)
+ |
+ (?:(?:
+ //(?:
+ (?:(?:(#{PATTERN::USERINFO})@)? (?# 3: userinfo)
+ (?:(#{PATTERN::HOST})(?::(\\d*))?))?(?# 4: host, 5: port)
+ |
+ (#{PATTERN::REG_NAME}) (?# 6: registry)
+ )
+ |
+ (?!//)) (?# XXX: '//' is the mark for hostport)
+ (#{PATTERN::ABS_PATH})? (?# 7: path)
+ )(?:\\?(#{PATTERN::QUERY}))? (?# 8: query)
+ )
+ (?:\\#(#{PATTERN::FRAGMENT}))? (?# 9: fragment)
+ "
+ X_REL_URI = "
+ (?:
+ (?:
+ //
+ (?:
+ (?:(#{PATTERN::USERINFO})@)? (?# 1: userinfo)
+ (#{PATTERN::HOST})?(?::(\\d*))? (?# 2: host, 3: port)
+ |
+ (#{PATTERN::REG_NAME}) (?# 4: registry)
+ )
+ )
+ |
+ (#{PATTERN::REL_SEGMENT}) (?# 5: rel_segment)
+ )?
+ (#{PATTERN::ABS_PATH})? (?# 6: abs_path)
+ (?:\\?(#{PATTERN::QUERY}))? (?# 7: query)
+ (?:\\#(#{PATTERN::FRAGMENT}))? (?# 8: fragment)
+ "
+ # :startdoc:
+ end # PATTERN
+
+ # :stopdoc:
+
+ # for URI::split
+ ABS_URI = Regexp.new('^' + PATTERN::X_ABS_URI + '$', #'
+ Regexp::EXTENDED, 'N').freeze
+ REL_URI = Regexp.new('^' + PATTERN::X_REL_URI + '$', #'
+ Regexp::EXTENDED, 'N').freeze
+
+ # for URI::extract
+ URI_REF = Regexp.new(PATTERN::URI_REF, false, 'N').freeze
+ ABS_URI_REF = Regexp.new(PATTERN::X_ABS_URI, Regexp::EXTENDED, 'N').freeze
+ REL_URI_REF = Regexp.new(PATTERN::X_REL_URI, Regexp::EXTENDED, 'N').freeze
+
+ # for URI::escape/unescape
+ ESCAPED = Regexp.new(PATTERN::ESCAPED, false, 'N').freeze
+ UNSAFE = Regexp.new("[^#{PATTERN::UNRESERVED}#{PATTERN::RESERVED}]",
+ false, 'N').freeze
+
+ # for Generic#initialize
+ SCHEME = Regexp.new("^#{PATTERN::SCHEME}$", false, 'N').freeze #"
+ USERINFO = Regexp.new("^#{PATTERN::USERINFO}$", false, 'N').freeze #"
+ HOST = Regexp.new("^#{PATTERN::HOST}$", false, 'N').freeze #"
+ PORT = Regexp.new("^#{PATTERN::PORT}$", false, 'N').freeze #"
+ OPAQUE = Regexp.new("^#{PATTERN::OPAQUE_PART}$", false, 'N').freeze #"
+ REGISTRY = Regexp.new("^#{PATTERN::REG_NAME}$", false, 'N').freeze #"
+ ABS_PATH = Regexp.new("^#{PATTERN::ABS_PATH}$", false, 'N').freeze #"
+ REL_PATH = Regexp.new("^#{PATTERN::REL_PATH}$", false, 'N').freeze #"
+ QUERY = Regexp.new("^#{PATTERN::QUERY}$", false, 'N').freeze #"
+ FRAGMENT = Regexp.new("^#{PATTERN::FRAGMENT}$", false, 'N').freeze #"
+ # :startdoc:
+ end # REGEXP
+
+ module Util # :nodoc:
+ def make_components_hash(klass, array_hash)
+ tmp = {}
+ if array_hash.kind_of?(Array) &&
+ array_hash.size == klass.component.size - 1
+ klass.component[1..-1].each_index do |i|
+ begin
+ tmp[klass.component[i + 1]] = array_hash[i].clone
+ rescue TypeError
+ tmp[klass.component[i + 1]] = array_hash[i]
+ end
+ end
+
+ elsif array_hash.kind_of?(Hash)
+ array_hash.each do |key, value|
+ begin
+ tmp[key] = value.clone
+ rescue TypeError
+ tmp[key] = value
+ end
+ end
+ else
+ raise ArgumentError,
+ "expected Array of or Hash of components of #{klass.to_s} (#{klass.component[1..-1].join(', ')})"
+ end
+ tmp[:scheme] = klass.to_s.sub(/\A.*::/, '').downcase
+
+ return tmp
+ end
+ module_function :make_components_hash
+ end
+
+ module Escape
+ include REGEXP
+
+ #
+ # == Synopsis
+ #
+ # URI.escape(str [, unsafe])
+ #
+ # == Args
+ #
+ # +str+::
+ # String to replaces in.
+ # +unsafe+::
+ # Regexp that matches all symbols that must be replaced with codes.
+ # By default uses <tt>REGEXP::UNSAFE</tt>.
+ #
+ # == Description
+ #
+ # Escapes the string, replacing all unsafe characters with codes.
+ #
+ # == Usage
+ #
+ # require 'uri'
+ #
+ # enc_uri = URI.escape("http://example.com/?a=\11\15")
+ # p enc_uri
+ # # => "http://example.com/?a=%09%0D"
+ #
+ # p URI.unescape(enc_uri)
+ # # => "http://example.com/?a=\t\r"
+ #
+ def escape(str, unsafe = UNSAFE)
+ unless unsafe.kind_of?(Regexp)
+ # perhaps unsafe is String object
+ unsafe = Regexp.new(Regexp.quote(unsafe), false, 'N')
+ end
+ str.gsub(unsafe) do |us|
+ tmp = ''
+ us.each_byte do |uc|
+ tmp << sprintf('%%%02X', uc)
+ end
+ tmp
+ end
+ end
+ alias encode escape
+ #
+ # == Synopsis
+ #
+ # URI.unescape(str)
+ #
+ # == Args
+ #
+ # +str+::
+ # Unescapes the string.
+ #
+ # == Usage
+ #
+ # require 'uri'
+ #
+ # enc_uri = URI.escape("http://example.com/?a=\11\15")
+ # p enc_uri
+ # # => "http://example.com/?a=%09%0D"
+ #
+ # p URI.unescape(enc_uri)
+ # # => "http://example.com/?a=\t\r"
+ #
+ def unescape(str)
+ str.gsub(ESCAPED) do
+ $&[1,2].hex.chr
+ end
+ end
+ alias decode unescape
+ end
+
+ include REGEXP
+ extend Escape
+
+ @@schemes = {}
+
+ #
+ # Base class for all URI exceptions.
+ #
+ class Error < StandardError; end
+ #
+ # Not a URI.
+ #
+ class InvalidURIError < Error; end
+ #
+ # Not a URI component.
+ #
+ class InvalidComponentError < Error; end
+ #
+ # URI is valid, bad usage is not.
+ #
+ class BadURIError < Error; end
+
+ #
+ # == Synopsis
+ #
+ # URI::split(uri)
+ #
+ # == Args
+ #
+ # +uri+::
+ # String with URI.
+ #
+ # == Description
+ #
+ # Splits the string on following parts and returns array with result:
+ #
+ # * Scheme
+ # * Userinfo
+ # * Host
+ # * Port
+ # * Registry
+ # * Path
+ # * Opaque
+ # * Query
+ # * Fragment
+ #
+ # == Usage
+ #
+ # require 'uri'
+ #
+ # p URI.split("http://www.ruby-lang.org/")
+ # # => ["http", nil, "www.ruby-lang.org", nil, nil, "/", nil, nil, nil]
+ #
+ def self.split(uri)
+ case uri
+ when ''
+ # null uri
+
+ when ABS_URI
+ scheme, opaque, userinfo, host, port,
+ registry, path, query, fragment = $~[1..-1]
+
+ # URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
+
+ # absoluteURI = scheme ":" ( hier_part | opaque_part )
+ # hier_part = ( net_path | abs_path ) [ "?" query ]
+ # opaque_part = uric_no_slash *uric
+
+ # abs_path = "/" path_segments
+ # net_path = "//" authority [ abs_path ]
+
+ # authority = server | reg_name
+ # server = [ [ userinfo "@" ] hostport ]
+
+ if !scheme
+ raise InvalidURIError,
+ "bad URI(absolute but no scheme): #{uri}"
+ end
+ if !opaque && (!path && (!host && !registry))
+ raise InvalidURIError,
+ "bad URI(absolute but no path): #{uri}"
+ end
+
+ when REL_URI
+ scheme = nil
+ opaque = nil
+
+ userinfo, host, port, registry,
+ rel_segment, abs_path, query, fragment = $~[1..-1]
+ if rel_segment && abs_path
+ path = rel_segment + abs_path
+ elsif rel_segment
+ path = rel_segment
+ elsif abs_path
+ path = abs_path
+ end
+
+ # URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
+
+ # relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ]
+
+ # net_path = "//" authority [ abs_path ]
+ # abs_path = "/" path_segments
+ # rel_path = rel_segment [ abs_path ]
+
+ # authority = server | reg_name
+ # server = [ [ userinfo "@" ] hostport ]
+
+ else
+ raise InvalidURIError, "bad URI(is not URI?): #{uri}"
+ end
+
+ path = '' if !path && !opaque # (see RFC2396 Section 5.2)
+ ret = [
+ scheme,
+ userinfo, host, port, # X
+ registry, # X
+ path, # Y
+ opaque, # Y
+ query,
+ fragment
+ ]
+ return ret
+ end
+
+ #
+ # == Synopsis
+ #
+ # URI::parse(uri_str)
+ #
+ # == Args
+ #
+ # +uri_str+::
+ # String with URI.
+ #
+ # == Description
+ #
+ # Creates one of the URI's subclasses instance from the string.
+ #
+ # == Raises
+ #
+ # URI::InvalidURIError
+ # Raised if URI given is not a correct one.
+ #
+ # == Usage
+ #
+ # require 'uri'
+ #
+ # uri = URI.parse("http://www.ruby-lang.org/")
+ # p uri
+ # # => #<URI::HTTP:0x202281be URL:http://www.ruby-lang.org/>
+ # p uri.scheme
+ # # => "http"
+ # p uri.host
+ # # => "www.ruby-lang.org"
+ #
+ def self.parse(uri)
+ scheme, userinfo, host, port,
+ registry, path, opaque, query, fragment = self.split(uri)
+
+ if scheme && @@schemes.include?(scheme.upcase)
+ @@schemes[scheme.upcase].new(scheme, userinfo, host, port,
+ registry, path, opaque, query,
+ fragment)
+ else
+ Generic.new(scheme, userinfo, host, port,
+ registry, path, opaque, query,
+ fragment)
+ end
+ end
+
+ #
+ # == Synopsis
+ #
+ # URI::join(str[, str, ...])
+ #
+ # == Args
+ #
+ # +str+::
+ # String(s) to work with
+ #
+ # == Description
+ #
+ # Joins URIs.
+ #
+ # == Usage
+ #
+ # require 'uri'
+ #
+ # p URI.join("http://localhost/","main.rbx")
+ # # => #<URI::HTTP:0x2022ac02 URL:http://localhost/main.rbx>
+ #
+ def self.join(*str)
+ u = self.parse(str[0])
+ str[1 .. -1].each do |x|
+ u = u.merge(x)
+ end
+ u
+ end
+
+ #
+ # == Synopsis
+ #
+ # URI::extract(str[, schemes][,&blk])
+ #
+ # == Args
+ #
+ # +str+::
+ # String to extract URIs from.
+ # +schemes+::
+ # Limit URI matching to a specific schemes.
+ #
+ # == Description
+ #
+ # Extracts URIs from a string. If block given, iterates through all matched URIs.
+ # Returns nil if block given or array with matches.
+ #
+ # == Usage
+ #
+ # require "uri"
+ #
+ # URI.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.")
+ # # => ["http://foo.example.org/bla", "mailto:test@example.com"]
+ #
+ def self.extract(str, schemes = nil, &block)
+ if block_given?
+ str.scan(regexp(schemes)) { yield $& }
+ nil
+ else
+ result = []
+ str.scan(regexp(schemes)) { result.push $& }
+ result
+ end
+ end
+
+ #
+ # == Synopsis
+ #
+ # URI::regexp([match_schemes])
+ #
+ # == Args
+ #
+ # +match_schemes+::
+ # Array of schemes. If given, resulting regexp matches to URIs
+ # whose scheme is one of the match_schemes.
+ #
+ # == Description
+ # Returns a Regexp object which matches to URI-like strings.
+ # The Regexp object returned by this method includes arbitrary
+ # number of capture group (parentheses). Never rely on it's number.
+ #
+ # == Usage
+ #
+ # require 'uri'
+ #
+ # # extract first URI from html_string
+ # html_string.slice(URI.regexp)
+ #
+ # # remove ftp URIs
+ # html_string.sub(URI.regexp(['ftp'])
+ #
+ # # You should not rely on the number of parentheses
+ # html_string.scan(URI.regexp) do |*matches|
+ # p $&
+ # end
+ #
+ def self.regexp(schemes = nil)
+ unless schemes
+ ABS_URI_REF
+ else
+ /(?=#{Regexp.union(*schemes)}:)#{PATTERN::X_ABS_URI}/xn
+ end
+ end
+
+end
+
+module Kernel
+ # alias for URI.parse.
+ #
+ # This method is introduced at 1.8.2.
+ def URI(uri_str) # :doc:
+ URI.parse(uri_str)
+ end
+ module_function :URI
+end
diff --git a/ruby_1_8_5/lib/uri/ftp.rb b/ruby_1_8_5/lib/uri/ftp.rb
new file mode 100644
index 0000000000..08f128f23f
--- /dev/null
+++ b/ruby_1_8_5/lib/uri/ftp.rb
@@ -0,0 +1,148 @@
+#
+# = uri/ftp.rb
+#
+# Author:: Akira Yamada <akira@ruby-lang.org>
+# License:: You can redistribute it and/or modify it under the same term as Ruby.
+# Revision:: $Id: ftp.rb,v 1.3.2.1 2004/03/24 12:20:32 gsinclair Exp $
+#
+
+require 'uri/generic'
+
+module URI
+
+ #
+ # RFC1738 section 3.2.
+ #
+ class FTP < Generic
+ DEFAULT_PORT = 21
+
+ COMPONENT = [
+ :scheme,
+ :userinfo, :host, :port,
+ :path, :typecode
+ ].freeze
+ #
+ # Typecode is, "a", "i" or "d".
+ # As for "a" the text, as for "i" binary,
+ # as for "d" the directory is displayed.
+ # "A" with the text, as for "i" being binary,
+ # is because the respective data type was called ASCII and
+ # IMAGE with the protocol of FTP.
+ #
+ TYPECODE = ['a', 'i', 'd'].freeze
+ TYPECODE_PREFIX = ';type='.freeze
+
+ def self.new2(user, password, host, port, path,
+ typecode = nil, arg_check = true)
+ typecode = nil if typecode.size == 0
+ if typecode && !TYPECODE.include?(typecode)
+ raise ArgumentError,
+ "bad typecode is specified: #{typecode}"
+ end
+
+ # do escape
+
+ self.new('ftp',
+ [user, password],
+ host, port, nil,
+ typecode ? path + TYPECODE_PREFIX + typecode : path,
+ nil, nil, nil, arg_check)
+ end
+
+ #
+ # == Description
+ #
+ # Creates a new URI::FTP object from components of URI::FTP with
+ # check. It is scheme, userinfo, host, port, path and typecode. It
+ # provided by an Array or a Hash. typecode is "a", "i" or "d".
+ #
+ def self.build(args)
+ tmp = Util::make_components_hash(self, args)
+
+ if tmp[:typecode]
+ if tmp[:typecode].size == 1
+ tmp[:typecode] = TYPECODE_PREFIX + tmp[:typecode]
+ end
+ tmp[:path] << tmp[:typecode]
+ end
+
+ return super(tmp)
+ end
+
+ #
+ # == Description
+ #
+ # Create a new URI::FTP object from ``generic'' components with no
+ # check.
+ #
+ # == Usage
+ #
+ # require 'uri'
+ # p ftp = URI.parse("ftp://ftp.ruby-lang.org/pub/ruby/;type=d")
+ # # => #<URI::FTP:0x201fad08 URL:ftp://ftp.ruby-lang.org/pub/ruby/;type=d>
+ # p ftp.typecode
+ # # => "d"
+ #
+ def initialize(*arg)
+ super(*arg)
+ @typecode = nil
+ tmp = @path.index(TYPECODE_PREFIX)
+ if tmp
+ typecode = @path[tmp + TYPECODE_PREFIX.size..-1]
+ self.set_path(@path[0..tmp - 1])
+
+ if arg[-1]
+ self.typecode = typecode
+ else
+ self.set_typecode(typecode)
+ end
+ end
+ end
+ attr_reader :typecode
+
+ def check_typecode(v)
+ if TYPECODE.include?(v)
+ return true
+ else
+ raise InvalidComponentError,
+ "bad typecode(expected #{TYPECODE.join(', ')}): #{v}"
+ end
+ end
+ private :check_typecode
+
+ def set_typecode(v)
+ @typecode = v
+ end
+ protected :set_typecode
+
+ def typecode=(typecode)
+ check_typecode(typecode)
+ set_typecode(typecode)
+ typecode
+ end
+
+ def merge(oth) # :nodoc:
+ tmp = super(oth)
+ if self != tmp
+ tmp.set_typecode(oth.typecode)
+ end
+
+ return tmp
+ end
+
+ def to_s
+ save_path = nil
+ if @typecode
+ save_path = @path
+ @path = @path + TYPECODE_PREFIX + @typecode
+ end
+ str = super
+ if @typecode
+ @path = save_path
+ end
+
+ return str
+ end
+ end
+ @@schemes['FTP'] = FTP
+end
diff --git a/ruby_1_8_5/lib/uri/generic.rb b/ruby_1_8_5/lib/uri/generic.rb
new file mode 100644
index 0000000000..b7a4cf6ef0
--- /dev/null
+++ b/ruby_1_8_5/lib/uri/generic.rb
@@ -0,0 +1,1118 @@
+#
+# = uri/generic.rb
+#
+# Author:: Akira Yamada <akira@ruby-lang.org>
+# License:: You can redistribute it and/or modify it under the same term as Ruby.
+# Revision:: $Id: generic.rb,v 1.13.2.5 2005/06/24 04:15:20 akira Exp $
+#
+
+require 'uri/common'
+
+module URI
+
+ #
+ # Base class for all URI classes.
+ #
+ class Generic
+ include URI
+ include REGEXP
+
+ DEFAULT_PORT = nil
+
+ #
+ # Returns default port
+ #
+ def self.default_port
+ self::DEFAULT_PORT
+ end
+
+ def default_port
+ self.class.default_port
+ end
+
+ COMPONENT = [
+ :scheme,
+ :userinfo, :host, :port, :registry,
+ :path, :opaque,
+ :query,
+ :fragment
+ ].freeze
+
+ #
+ # Components of the URI in the order.
+ #
+ def self.component
+ self::COMPONENT
+ end
+
+ USE_REGISTRY = false
+
+ #
+ # DOC: FIXME!
+ #
+ def self.use_registry
+ self::USE_REGISTRY
+ end
+
+ #
+ # == Synopsis
+ #
+ # See #new
+ #
+ # == Description
+ #
+ # At first, tries to create a new URI::Generic instance using
+ # URI::Generic::build. But, if exception URI::InvalidComponentError is raised,
+ # then it URI::Escape.escape all URI components and tries again.
+ #
+ #
+ def self.build2(args)
+ begin
+ return self.build(args)
+ rescue InvalidComponentError
+ if args.kind_of?(Array)
+ return self.build(args.collect{|x|
+ if x
+ URI.escape(x)
+ else
+ x
+ end
+ })
+ elsif args.kind_of?(Hash)
+ tmp = {}
+ args.each do |key, value|
+ tmp[key] = if value
+ URI.escape(value)
+ else
+ value
+ end
+ end
+ return self.build(tmp)
+ end
+ end
+ end
+
+ #
+ # == Synopsis
+ #
+ # See #new
+ #
+ # == Description
+ #
+ # Creates a new URI::Generic instance from components of URI::Generic
+ # with check. Components are: scheme, userinfo, host, port, registry, path,
+ # opaque, query and fragment. You can provide arguments either by an Array or a Hash.
+ # See #new for hash keys to use or for order of array items.
+ #
+ def self.build(args)
+ if args.kind_of?(Array) &&
+ args.size == ::URI::Generic::COMPONENT.size
+ tmp = args
+ elsif args.kind_of?(Hash)
+ tmp = ::URI::Generic::COMPONENT.collect do |c|
+ if args.include?(c)
+ args[c]
+ else
+ nil
+ end
+ end
+ else
+ raise ArgumentError,
+ "expected Array of or Hash of components of #{self.class} (#{self.class.component.join(', ')})"
+ end
+
+ tmp << true
+ return self.new(*tmp)
+ end
+ #
+ # == Args
+ #
+ # +scheme+::
+ # Protocol scheme, i.e. 'http','ftp','mailto' and so on.
+ # +userinfo+::
+ # User name and password, i.e. 'sdmitry:bla'
+ # +host+::
+ # Server host name
+ # +port+::
+ # Server port
+ # +registry+::
+ # DOC: FIXME!
+ # +path+::
+ # Path on server
+ # +opaque+::
+ # DOC: FIXME!
+ # +query+::
+ # Query data
+ # +fragment+::
+ # A part of URI after '#' sign
+ # +arg_check+::
+ # Check arguments [false by default]
+ #
+ # == Description
+ #
+ # Creates a new URI::Generic instance from ``generic'' components without check.
+ #
+ def initialize(scheme,
+ userinfo, host, port, registry,
+ path, opaque,
+ query,
+ fragment,
+ arg_check = false)
+ @scheme = nil
+ @user = nil
+ @password = nil
+ @host = nil
+ @port = nil
+ @path = nil
+ @query = nil
+ @opaque = nil
+ @registry = nil
+ @fragment = nil
+
+ if arg_check
+ self.scheme = scheme
+ self.userinfo = userinfo
+ self.host = host
+ self.port = port
+ self.path = path
+ self.query = query
+ self.opaque = opaque
+ self.registry = registry
+ self.fragment = fragment
+ else
+ self.set_scheme(scheme)
+ self.set_userinfo(userinfo)
+ self.set_host(host)
+ self.set_port(port)
+ self.set_path(path)
+ self.set_query(query)
+ self.set_opaque(opaque)
+ self.set_registry(registry)
+ self.set_fragment(fragment)
+ end
+ if @registry && !self.class.use_registry
+ raise InvalidURIError,
+ "the scheme #{@scheme} does not accept registry part: #{@registry} (or bad hostname?)"
+ end
+
+ @scheme.freeze if @scheme
+ self.set_path('') if !@path && !@opaque # (see RFC2396 Section 5.2)
+ self.set_port(self.default_port) if self.default_port && !@port
+ end
+ attr_reader :scheme
+ attr_reader :host
+ attr_reader :port
+ attr_reader :registry
+ attr_reader :path
+ attr_reader :query
+ attr_reader :opaque
+ attr_reader :fragment
+
+ # replace self by other URI object
+ def replace!(oth)
+ if self.class != oth.class
+ raise ArgumentError, "expected #{self.class} object"
+ end
+
+ component.each do |c|
+ self.__send__("#{c}=", oth.__send__(c))
+ end
+ end
+ private :replace!
+
+ def component
+ self.class.component
+ end
+
+ def check_scheme(v)
+ if v && SCHEME !~ v
+ raise InvalidComponentError,
+ "bad component(expected scheme component): #{v}"
+ end
+
+ return true
+ end
+ private :check_scheme
+
+ def set_scheme(v)
+ @scheme = v
+ end
+ protected :set_scheme
+
+ def scheme=(v)
+ check_scheme(v)
+ set_scheme(v)
+ v
+ end
+
+ def check_userinfo(user, password = nil)
+ if !password
+ user, password = split_userinfo(user)
+ end
+ check_user(user)
+ check_password(password, user)
+
+ return true
+ end
+ private :check_userinfo
+
+ def check_user(v)
+ if @registry || @opaque
+ raise InvalidURIError,
+ "can not set user with registry or opaque"
+ end
+
+ return v unless v
+
+ if USERINFO !~ v
+ raise InvalidComponentError,
+ "bad component(expected userinfo component or user component): #{v}"
+ end
+
+ return true
+ end
+ private :check_user
+
+ def check_password(v, user = @user)
+ if @registry || @opaque
+ raise InvalidURIError,
+ "can not set password with registry or opaque"
+ end
+ return v unless v
+
+ if !user
+ raise InvalidURIError,
+ "password component depends user component"
+ end
+
+ if USERINFO !~ v
+ raise InvalidComponentError,
+ "bad component(expected user component): #{v}"
+ end
+
+ return true
+ end
+ private :check_password
+
+ #
+ # Sets userinfo, argument is string like 'name:pass'
+ #
+ def userinfo=(userinfo)
+ if userinfo.nil?
+ return nil
+ end
+ check_userinfo(*userinfo)
+ set_userinfo(*userinfo)
+ userinfo
+ end
+
+ def user=(user)
+ check_user(user)
+ set_user(user)
+ user
+ end
+
+ def password=(password)
+ check_password(password)
+ set_password(password)
+ password
+ end
+
+ def set_userinfo(user, password = nil)
+ unless password
+ user, password = split_userinfo(user)
+ end
+ @user = user
+ @password = password if password
+
+ [@user, @password]
+ end
+ protected :set_userinfo
+
+ def set_user(v)
+ set_userinfo(v, @password)
+ v
+ end
+ protected :set_user
+
+ def set_password(v)
+ set_userinfo(@user, v)
+ v
+ end
+ protected :set_password
+
+ def split_userinfo(ui)
+ return nil, nil unless ui
+ user, password = ui.split(/:/, 2)
+
+ return user, password
+ end
+ private :split_userinfo
+
+ def escape_userpass(v)
+ v = URI.escape(v, /[@:\/]/o) # RFC 1738 section 3.1 #/
+ end
+ private :escape_userpass
+
+ def userinfo
+ if !@password
+ @user
+ else
+ @user + ':' + @password
+ end
+ end
+
+ def user
+ @user
+ end
+
+ def password
+ @password
+ end
+
+ def check_host(v)
+ return v unless v
+
+ if @registry || @opaque
+ raise InvalidURIError,
+ "can not set host with registry or opaque"
+ elsif HOST !~ v
+ raise InvalidComponentError,
+ "bad component(expected host component): #{v}"
+ end
+
+ return true
+ end
+ private :check_host
+
+ def set_host(v)
+ @host = v
+ end
+ protected :set_host
+
+ def host=(v)
+ check_host(v)
+ set_host(v)
+ v
+ end
+
+ def check_port(v)
+ return v unless v
+
+ if @registry || @opaque
+ raise InvalidURIError,
+ "can not set port with registry or opaque"
+ elsif !v.kind_of?(Fixnum) && PORT !~ v
+ raise InvalidComponentError,
+ "bad component(expected port component): #{v}"
+ end
+
+ return true
+ end
+ private :check_port
+
+ def set_port(v)
+ unless !v || v.kind_of?(Fixnum)
+ if v.empty?
+ v = nil
+ else
+ v = v.to_i
+ end
+ end
+ @port = v
+ end
+ protected :set_port
+
+ def port=(v)
+ check_port(v)
+ set_port(v)
+ port
+ end
+
+ def check_registry(v)
+ return v unless v
+
+ # raise if both server and registry are not nil, because:
+ # authority = server | reg_name
+ # server = [ [ userinfo "@" ] hostport ]
+ if @host || @port || @user # userinfo = @user + ':' + @password
+ raise InvalidURIError,
+ "can not set registry with host, port, or userinfo"
+ elsif v && REGISTRY !~ v
+ raise InvalidComponentError,
+ "bad component(expected registry component): #{v}"
+ end
+
+ return true
+ end
+ private :check_registry
+
+ def set_registry(v)
+ @registry = v
+ end
+ protected :set_registry
+
+ def registry=(v)
+ check_registry(v)
+ set_registry(v)
+ v
+ end
+
+ def check_path(v)
+ # raise if both hier and opaque are not nil, because:
+ # absoluteURI = scheme ":" ( hier_part | opaque_part )
+ # hier_part = ( net_path | abs_path ) [ "?" query ]
+ if v && @opaque
+ raise InvalidURIError,
+ "path conflicts with opaque"
+ end
+
+ if @scheme
+ if v && v != '' && ABS_PATH !~ v
+ raise InvalidComponentError,
+ "bad component(expected absolute path component): #{v}"
+ end
+ else
+ if v && v != '' && ABS_PATH !~ v && REL_PATH !~ v
+ raise InvalidComponentError,
+ "bad component(expected relative path component): #{v}"
+ end
+ end
+
+ return true
+ end
+ private :check_path
+
+ def set_path(v)
+ @path = v
+ end
+ protected :set_path
+
+ def path=(v)
+ check_path(v)
+ set_path(v)
+ v
+ end
+
+ def check_query(v)
+ return v unless v
+
+ # raise if both hier and opaque are not nil, because:
+ # absoluteURI = scheme ":" ( hier_part | opaque_part )
+ # hier_part = ( net_path | abs_path ) [ "?" query ]
+ if @opaque
+ raise InvalidURIError,
+ "query conflicts with opaque"
+ end
+
+ if v && v != '' && QUERY !~ v
+ raise InvalidComponentError,
+ "bad component(expected query component): #{v}"
+ end
+
+ return true
+ end
+ private :check_query
+
+ def set_query(v)
+ @query = v
+ end
+ protected :set_query
+
+ def query=(v)
+ check_query(v)
+ set_query(v)
+ v
+ end
+
+ def check_opaque(v)
+ return v unless v
+
+ # raise if both hier and opaque are not nil, because:
+ # absoluteURI = scheme ":" ( hier_part | opaque_part )
+ # hier_part = ( net_path | abs_path ) [ "?" query ]
+ if @host || @port || @user || @path # userinfo = @user + ':' + @password
+ raise InvalidURIError,
+ "can not set opaque with host, port, userinfo or path"
+ elsif v && OPAQUE !~ v
+ raise InvalidComponentError,
+ "bad component(expected opaque component): #{v}"
+ end
+
+ return true
+ end
+ private :check_opaque
+
+ def set_opaque(v)
+ @opaque = v
+ end
+ protected :set_opaque
+
+ def opaque=(v)
+ check_opaque(v)
+ set_opaque(v)
+ v
+ end
+
+ def check_fragment(v)
+ return v unless v
+
+ if v && v != '' && FRAGMENT !~ v
+ raise InvalidComponentError,
+ "bad component(expected fragment component): #{v}"
+ end
+
+ return true
+ end
+ private :check_fragment
+
+ def set_fragment(v)
+ @fragment = v
+ end
+ protected :set_fragment
+
+ def fragment=(v)
+ check_fragment(v)
+ set_fragment(v)
+ v
+ end
+
+ #
+ # Checks if URI has a path
+ #
+ def hierarchical?
+ if @path
+ true
+ else
+ false
+ end
+ end
+
+ #
+ # Checks if URI is an absolute one
+ #
+ def absolute?
+ if @scheme
+ true
+ else
+ false
+ end
+ end
+ alias absolute absolute?
+
+ #
+ # Checks if URI is relative
+ #
+ def relative?
+ !absolute?
+ end
+
+ def split_path(path)
+ path.split(%r{/+}, -1)
+ end
+ private :split_path
+
+ def merge_path(base, rel)
+ # RFC2396, Section 5.2, 5)
+ if rel[0] == ?/ #/
+ # RFC2396, Section 5.2, 5)
+ return rel
+
+ else
+ # RFC2396, Section 5.2, 6)
+ base_path = split_path(base)
+ rel_path = split_path(rel)
+
+ # RFC2396, Section 5.2, 6), a)
+ base_path << '' if base_path.last == '..'
+ while i = base_path.index('..')
+ base_path.slice!(i - 1, 2)
+ end
+ if base_path.empty?
+ base_path = [''] # keep '/' for root directory
+ else
+ base_path.pop
+ end
+
+ # RFC2396, Section 5.2, 6), c)
+ # RFC2396, Section 5.2, 6), d)
+ rel_path.push('') if rel_path.last == '.' || rel_path.last == '..'
+ rel_path.delete('.')
+
+ # RFC2396, Section 5.2, 6), e)
+ tmp = []
+ rel_path.each do |x|
+ if x == '..' &&
+ !(tmp.empty? || tmp.last == '..')
+ tmp.pop
+ else
+ tmp << x
+ end
+ end
+
+ add_trailer_slash = true
+ while x = tmp.shift
+ if x == '..' && base_path.size > 1
+ # RFC2396, Section 4
+ # a .. or . in an absolute path has no special meaning
+ base_path.pop
+ else
+ # if x == '..'
+ # valid absolute (but abnormal) path "/../..."
+ # else
+ # valid absolute path
+ # end
+ base_path << x
+ tmp.each {|t| base_path << t}
+ add_trailer_slash = false
+ break
+ end
+ end
+ base_path.push('') if add_trailer_slash
+
+ return base_path.join('/')
+ end
+ end
+ private :merge_path
+
+ #
+ # == Args
+ #
+ # +oth+::
+ # URI or String
+ #
+ # == Description
+ #
+ # Destructive form of #merge
+ #
+ # == Usage
+ #
+ # require 'uri'
+ #
+ # uri = URI.parse("http://my.example.com")
+ # uri.merge!("/main.rbx?page=1")
+ # p uri
+ # # => #<URI::HTTP:0x2021f3b0 URL:http://my.example.com/main.rbx?page=1>
+ #
+ def merge!(oth)
+ t = merge(oth)
+ if self == t
+ nil
+ else
+ replace!(t)
+ self
+ end
+ end
+
+ #
+ # == Args
+ #
+ # +oth+::
+ # URI or String
+ #
+ # == Description
+ #
+ # Merges two URI's.
+ #
+ # == Usage
+ #
+ # require 'uri'
+ #
+ # uri = URI.parse("http://my.example.com")
+ # p uri.merge("/main.rbx?page=1")
+ # # => #<URI::HTTP:0x2021f3b0 URL:http://my.example.com/main.rbx?page=1>
+ #
+ def merge(oth)
+ begin
+ base, rel = merge0(oth)
+ rescue
+ raise $!.class, $!.message
+ end
+
+ if base == rel
+ return base
+ end
+
+ authority = rel.userinfo || rel.host || rel.port
+
+ # RFC2396, Section 5.2, 2)
+ if (rel.path.nil? || rel.path.empty?) && !authority && !rel.query
+ base.set_fragment(rel.fragment) if rel.fragment
+ return base
+ end
+
+ base.set_query(nil)
+ base.set_fragment(nil)
+
+ # RFC2396, Section 5.2, 4)
+ if !authority
+ base.set_path(merge_path(base.path, rel.path)) if base.path && rel.path
+ else
+ # RFC2396, Section 5.2, 4)
+ base.set_path(rel.path) if rel.path
+ end
+
+ # RFC2396, Section 5.2, 7)
+ base.set_userinfo(rel.userinfo) if rel.userinfo
+ base.set_host(rel.host) if rel.host
+ base.set_port(rel.port) if rel.port
+ base.set_query(rel.query) if rel.query
+ base.set_fragment(rel.fragment) if rel.fragment
+
+ return base
+ end # merge
+ alias + merge
+
+ # return base and rel.
+ # you can modify `base', but can not `rel'.
+ def merge0(oth)
+ case oth
+ when Generic
+ when String
+ oth = URI.parse(oth)
+ else
+ raise ArgumentError,
+ "bad argument(expected URI object or URI string)"
+ end
+
+ if self.relative? && oth.relative?
+ raise BadURIError,
+ "both URI are relative"
+ end
+
+ if self.absolute? && oth.absolute?
+ #raise BadURIError,
+ # "both URI are absolute"
+ # hmm... should return oth for usability?
+ return oth, oth
+ end
+
+ if self.absolute?
+ return self.dup, oth
+ else
+ return oth, oth
+ end
+ end
+ private :merge0
+
+ def route_from_path(src, dst)
+ # RFC2396, Section 4.2
+ return '' if src == dst
+
+ src_path = split_path(src)
+ dst_path = split_path(dst)
+
+ # hmm... dst has abnormal absolute path,
+ # like "/./", "/../", "/x/../", ...
+ if dst_path.include?('..') ||
+ dst_path.include?('.')
+ return dst.dup
+ end
+
+ src_path.pop
+
+ # discard same parts
+ while dst_path.first == src_path.first
+ break if dst_path.empty?
+
+ src_path.shift
+ dst_path.shift
+ end
+
+ tmp = dst_path.join('/')
+
+ # calculate
+ if src_path.empty?
+ if tmp.empty?
+ return './'
+ elsif dst_path.first.include?(':') # (see RFC2396 Section 5)
+ return './' + tmp
+ else
+ return tmp
+ end
+ end
+
+ return '../' * src_path.size + tmp
+ end
+ private :route_from_path
+
+ def route_from0(oth)
+ case oth
+ when Generic
+ when String
+ oth = URI.parse(oth)
+ else
+ raise ArgumentError,
+ "bad argument(expected URI object or URI string)"
+ end
+
+ if self.relative?
+ raise BadURIError,
+ "relative URI: #{self}"
+ end
+ if oth.relative?
+ raise BadURIError,
+ "relative URI: #{oth}"
+ end
+
+ if self.scheme != oth.scheme
+ return self, self.dup
+ end
+ rel = URI::Generic.new(nil, # it is relative URI
+ self.userinfo, self.host, self.port,
+ self.registry, self.path, self.opaque,
+ self.query, self.fragment)
+
+ if rel.userinfo != oth.userinfo ||
+ rel.host.to_s.downcase != oth.host.to_s.downcase ||
+ rel.port != oth.port
+ if self.userinfo.nil? && self.host.nil?
+ return self, self.dup
+ end
+ rel.set_port(nil) if rel.port == oth.default_port
+ return rel, rel
+ end
+ rel.set_userinfo(nil)
+ rel.set_host(nil)
+ rel.set_port(nil)
+
+ if rel.path && rel.path == oth.path
+ rel.set_path('')
+ rel.set_query(nil) if rel.query == oth.query
+ return rel, rel
+ elsif rel.opaque && rel.opaque == oth.opaque
+ rel.set_opaque('')
+ rel.set_query(nil) if rel.query == oth.query
+ return rel, rel
+ end
+
+ # you can modify `rel', but can not `oth'.
+ return oth, rel
+ end
+ private :route_from0
+ #
+ # == Args
+ #
+ # +oth+::
+ # URI or String
+ #
+ # == Description
+ #
+ # Calculates relative path from oth to self
+ #
+ # == Usage
+ #
+ # require 'uri'
+ #
+ # uri = URI.parse('http://my.example.com/main.rbx?page=1')
+ # p uri.route_from('http://my.example.com')
+ # #=> #<URI::Generic:0x20218858 URL:/main.rbx?page=1>
+ #
+ def route_from(oth)
+ # you can modify `rel', but can not `oth'.
+ begin
+ oth, rel = route_from0(oth)
+ rescue
+ raise $!.class, $!.message
+ end
+ if oth == rel
+ return rel
+ end
+
+ rel.set_path(route_from_path(oth.path, self.path))
+ if rel.path == './' && self.query
+ # "./?foo" -> "?foo"
+ rel.set_path('')
+ end
+
+ return rel
+ end
+
+ alias - route_from
+
+ #
+ # == Args
+ #
+ # +oth+::
+ # URI or String
+ #
+ # == Description
+ #
+ # Calculates relative path to oth from self
+ #
+ # == Usage
+ #
+ # require 'uri'
+ #
+ # uri = URI.parse('http://my.example.com')
+ # p uri.route_to('http://my.example.com/main.rbx?page=1')
+ # #=> #<URI::Generic:0x2020c2f6 URL:/main.rbx?page=1>
+ #
+ def route_to(oth)
+ case oth
+ when Generic
+ when String
+ oth = URI.parse(oth)
+ else
+ raise ArgumentError,
+ "bad argument(expected URI object or URI string)"
+ end
+
+ oth.route_from(self)
+ end
+
+ #
+ # Returns normalized URI
+ #
+ def normalize
+ uri = dup
+ uri.normalize!
+ uri
+ end
+
+ #
+ # Destructive version of #normalize
+ #
+ def normalize!
+ if path && path == ''
+ set_path('/')
+ end
+ if host && host != host.downcase
+ set_host(self.host.downcase)
+ end
+ end
+
+ def path_query
+ str = @path
+ if @query
+ str += '?' + @query
+ end
+ str
+ end
+ private :path_query
+
+ #
+ # Constructs String from URI
+ #
+ def to_s
+ str = ''
+ if @scheme
+ str << @scheme
+ str << ':'
+ end
+
+ if @opaque
+ str << @opaque
+
+ else
+ if @registry
+ str << @registry
+ else
+ if @host
+ str << '//'
+ end
+ if self.userinfo
+ str << self.userinfo
+ str << '@'
+ end
+ if @host
+ str << @host
+ end
+ if @port && @port != self.default_port
+ str << ':'
+ str << @port.to_s
+ end
+ end
+
+ str << path_query
+ end
+
+ if @fragment
+ str << '#'
+ str << @fragment
+ end
+
+ str
+ end
+
+ #
+ # Compares to URI's
+ #
+ def ==(oth)
+ if self.class == oth.class
+ self.normalize.component_ary == oth.normalize.component_ary
+ else
+ false
+ end
+ end
+
+ def hash
+ self.component_ary.hash
+ end
+
+ def eql?(oth)
+ self.component_ary.eql?(oth.component_ary)
+ end
+
+=begin
+
+--- URI::Generic#===(oth)
+
+=end
+# def ===(oth)
+# raise NotImplementedError
+# end
+
+=begin
+=end
+ def component_ary
+ component.collect do |x|
+ self.send(x)
+ end
+ end
+ protected :component_ary
+
+ # == Args
+ #
+ # +components+::
+ # Multiple Symbol arguments defined in URI::HTTP
+ #
+ # == Description
+ #
+ # Selects specified components from URI
+ #
+ # == Usage
+ #
+ # require 'uri'
+ #
+ # uri = URI.parse('http://myuser:mypass@my.example.com/test.rbx')
+ # p uri.select(:userinfo, :host, :path)
+ # # => ["myuser:mypass", "my.example.com", "/test.rbx"]
+ #
+ def select(*components)
+ components.collect do |c|
+ if component.include?(c)
+ self.send(c)
+ else
+ raise ArgumentError,
+ "expected of components of #{self.class} (#{self.class.component.join(', ')})"
+ end
+ end
+ end
+
+ def inspect
+ sprintf("#<%s:%#0x URL:%s>", self.class.to_s, self.object_id, self.to_s)
+ end
+
+ def coerce(oth)
+ case oth
+ when String
+ oth = URI.parse(oth)
+ else
+ super
+ end
+
+ return oth, self
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/uri/http.rb b/ruby_1_8_5/lib/uri/http.rb
new file mode 100644
index 0000000000..651658b918
--- /dev/null
+++ b/ruby_1_8_5/lib/uri/http.rb
@@ -0,0 +1,65 @@
+#
+# = uri/http.rb
+#
+# Author:: Akira Yamada <akira@ruby-lang.org>
+# License:: You can redistribute it and/or modify it under the same term as Ruby.
+# Revision:: $Id: http.rb,v 1.2.2.1 2004/03/24 12:20:32 gsinclair Exp $
+#
+
+require 'uri/generic'
+
+module URI
+
+ #
+ # RFC1738 section 3.3.
+ #
+ class HTTP < Generic
+ DEFAULT_PORT = 80
+
+ COMPONENT = [
+ :scheme,
+ :userinfo, :host, :port,
+ :path,
+ :query,
+ :fragment
+ ].freeze
+
+ #
+ # == Description
+ #
+ # Create a new URI::HTTP object from components of URI::HTTP with
+ # check. It is scheme, userinfo, host, port, path, query and
+ # fragment. It provided by an Array of a Hash.
+ #
+ def self.build(args)
+ tmp = Util::make_components_hash(self, args)
+ return super(tmp)
+ end
+
+ #
+ # == Description
+ #
+ # Create a new URI::HTTP object from ``generic'' components with no
+ # check.
+ #
+ def initialize(*arg)
+ super(*arg)
+ end
+
+ #
+ # == Description
+ #
+ # Returns: path + '?' + query
+ #
+ def request_uri
+ r = path_query
+ if r[0] != ?/
+ r = '/' + r
+ end
+
+ r
+ end
+ end
+
+ @@schemes['HTTP'] = HTTP
+end
diff --git a/ruby_1_8_5/lib/uri/https.rb b/ruby_1_8_5/lib/uri/https.rb
new file mode 100644
index 0000000000..1ee1e67a24
--- /dev/null
+++ b/ruby_1_8_5/lib/uri/https.rb
@@ -0,0 +1,16 @@
+#
+# = uri/https.rb
+#
+# Author:: Akira Yamada <akira@ruby-lang.org>
+# License:: You can redistribute it and/or modify it under the same term as Ruby.
+# Revision:: $Id: https.rb,v 1.2.2.1 2004/03/24 12:20:32 gsinclair Exp $
+#
+
+require 'uri/http'
+
+module URI
+ class HTTPS < HTTP
+ DEFAULT_PORT = 443
+ end
+ @@schemes['HTTPS'] = HTTPS
+end
diff --git a/ruby_1_8_5/lib/uri/ldap.rb b/ruby_1_8_5/lib/uri/ldap.rb
new file mode 100644
index 0000000000..f033b2c307
--- /dev/null
+++ b/ruby_1_8_5/lib/uri/ldap.rb
@@ -0,0 +1,190 @@
+#
+# = uri/ldap.rb
+#
+# Author::
+# Takaaki Tateishi <ttate@jaist.ac.jp>
+# Akira Yamada <akira@ruby-lang.org>
+# License::
+# URI::LDAP is copyrighted free software by Takaaki Tateishi and Akira Yamada.
+# You can redistribute it and/or modify it under the same term as Ruby.
+# Revision:: $Id: ldap.rb,v 1.3.2.2 2004/07/17 13:07:46 akira Exp $
+#
+
+require 'uri/generic'
+
+module URI
+
+ #
+ # LDAP URI SCHEMA (described in RFC2255)
+ # ldap://<host>/<dn>[?<attrs>[?<scope>[?<filter>[?<extensions>]]]]
+ #
+ class LDAP < Generic
+
+ DEFAULT_PORT = 389
+
+ COMPONENT = [
+ :scheme,
+ :host, :port,
+ :dn,
+ :attributes,
+ :scope,
+ :filter,
+ :extensions,
+ ].freeze
+
+ SCOPE = [
+ SCOPE_ONE = 'one',
+ SCOPE_SUB = 'sub',
+ SCOPE_BASE = 'base',
+ ].freeze
+
+ def self.build(args)
+ tmp = Util::make_components_hash(self, args)
+
+ if tmp[:dn]
+ tmp[:path] = tmp[:dn]
+ end
+
+ query = []
+ [:extensions, :filter, :scope, :attributes].collect do |x|
+ next if !tmp[x] && query.size == 0
+ query.unshift(tmp[x])
+ end
+
+ tmp[:query] = query.join('?')
+
+ return super(tmp)
+ end
+
+ def initialize(*arg)
+ super(*arg)
+
+ if @fragment
+ raise InvalidURIError, 'bad LDAP URL'
+ end
+
+ parse_dn
+ parse_query
+ end
+
+ def parse_dn
+ @dn = @path[1..-1]
+ end
+ private :parse_dn
+
+ def parse_query
+ @attributes = nil
+ @scope = nil
+ @filter = nil
+ @extensions = nil
+
+ if @query
+ attrs, scope, filter, extensions = @query.split('?')
+
+ @attributes = attrs if attrs && attrs.size > 0
+ @scope = scope if scope && scope.size > 0
+ @filter = filter if filter && filter.size > 0
+ @extensions = extensions if extensions && extensions.size > 0
+ end
+ end
+ private :parse_query
+
+ def build_path_query
+ @path = '/' + @dn
+
+ query = []
+ [@extensions, @filter, @scope, @attributes].each do |x|
+ next if !x && query.size == 0
+ query.unshift(x)
+ end
+ @query = query.join('?')
+ end
+ private :build_path_query
+
+ def dn
+ @dn
+ end
+
+ def set_dn(val)
+ @dn = val
+ build_path_query
+ @dn
+ end
+ protected :set_dn
+
+ def dn=(val)
+ set_dn(val)
+ val
+ end
+
+ def attributes
+ @attributes
+ end
+
+ def set_attributes(val)
+ @attributes = val
+ build_path_query
+ @attributes
+ end
+ protected :set_attributes
+
+ def attributes=(val)
+ set_attributes(val)
+ val
+ end
+
+ def scope
+ @scope
+ end
+
+ def set_scope(val)
+ @scope = val
+ build_path_query
+ @scope
+ end
+ protected :set_scope
+
+ def scope=(val)
+ set_scope(val)
+ val
+ end
+
+ def filter
+ @filter
+ end
+
+ def set_filter(val)
+ @filter = val
+ build_path_query
+ @filter
+ end
+ protected :set_filter
+
+ def filter=(val)
+ set_filter(val)
+ val
+ end
+
+ def extensions
+ @extensions
+ end
+
+ def set_extensions(val)
+ @extensions = val
+ build_path_query
+ @extensions
+ end
+ protected :set_extensions
+
+ def extensions=(val)
+ set_extensions(val)
+ val
+ end
+
+ def hierarchical?
+ false
+ end
+ end
+
+ @@schemes['LDAP'] = LDAP
+end
diff --git a/ruby_1_8_5/lib/uri/mailto.rb b/ruby_1_8_5/lib/uri/mailto.rb
new file mode 100644
index 0000000000..f4f9d02736
--- /dev/null
+++ b/ruby_1_8_5/lib/uri/mailto.rb
@@ -0,0 +1,239 @@
+#
+# = uri/mailto.rb
+#
+# Author:: Akira Yamada <akira@ruby-lang.org>
+# License:: You can redistribute it and/or modify it under the same term as Ruby.
+# Revision:: $Id: mailto.rb,v 1.6.2.2 2004/05/13 04:03:33 akira Exp $
+#
+
+require 'uri/generic'
+
+module URI
+
+ #
+ # RFC2368, The mailto URL scheme
+ #
+ class MailTo < Generic
+ include REGEXP
+
+ DEFAULT_PORT = nil
+
+ COMPONENT = [ :scheme, :to, :headers ].freeze
+
+ # :stopdoc:
+ # "hname" and "hvalue" are encodings of an RFC 822 header name and
+ # value, respectively. As with "to", all URL reserved characters must
+ # be encoded.
+ #
+ # "#mailbox" is as specified in RFC 822 [RFC822]. This means that it
+ # consists of zero or more comma-separated mail addresses, possibly
+ # including "phrase" and "comment" components. Note that all URL
+ # reserved characters in "to" must be encoded: in particular,
+ # parentheses, commas, and the percent sign ("%"), which commonly occur
+ # in the "mailbox" syntax.
+ #
+ # Within mailto URLs, the characters "?", "=", "&" are reserved.
+
+ # hname = *urlc
+ # hvalue = *urlc
+ # header = hname "=" hvalue
+ HEADER_PATTERN = "(?:[^?=&]*=[^?=&]*)".freeze
+ HEADER_REGEXP = Regexp.new(HEADER_PATTERN, 'N').freeze
+ # headers = "?" header *( "&" header )
+ # to = #mailbox
+ # mailtoURL = "mailto:" [ to ] [ headers ]
+ MAILBOX_PATTERN = "(?:#{PATTERN::ESCAPED}|[^(),%?=&])".freeze
+ MAILTO_REGEXP = Regexp.new(" # :nodoc:
+ \\A
+ (#{MAILBOX_PATTERN}*?) (?# 1: to)
+ (?:
+ \\?
+ (#{HEADER_PATTERN}(?:\\&#{HEADER_PATTERN})*) (?# 2: headers)
+ )?
+ (?:
+ \\#
+ (#{PATTERN::FRAGMENT}) (?# 3: fragment)
+ )?
+ \\z
+ ", Regexp::EXTENDED, 'N').freeze
+ # :startdoc:
+
+ #
+ # == Description
+ #
+ # Creates a new URI::MailTo object from components of URI::MailTo
+ # with check. It is to and headers. It provided by an Array of a
+ # Hash. You can provide headers as String like
+ # "subject=subscribe&cc=addr" or Array like [["subject",
+ # "subscribe"], ["cc", "addr"]]
+ #
+ def self.build(args)
+ tmp = Util::make_components_hash(self, args)
+
+ if tmp[:to]
+ tmp[:opaque] = tmp[:to]
+ else
+ tmp[:opaque] = ''
+ end
+
+ if tmp[:headers]
+ tmp[:opaque] << '?'
+
+ if tmp[:headers].kind_of?(Array)
+ tmp[:opaque] << tmp[:headers].collect { |x|
+ if x.kind_of?(Array)
+ x[0] + '=' + x[1..-1].to_s
+ else
+ x.to_s
+ end
+ }.join('&')
+
+ elsif tmp[:headers].kind_of?(Hash)
+ tmp[:opaque] << tmp[:headers].collect { |h,v|
+ h + '=' + v
+ }.join('&')
+
+ else
+ tmp[:opaque] << tmp[:headers].to_s
+ end
+ end
+
+ return super(tmp)
+ end
+
+ #
+ # == Description
+ #
+ # Creates a new URI::MailTo object from ``generic'' components with
+ # no check. Because, this method is usually called from URI::parse
+ # and the method checks validity of each components.
+ #
+ def initialize(*arg)
+ super(*arg)
+
+ @to = nil
+ @headers = []
+
+ if MAILTO_REGEXP =~ @opaque
+ if arg[-1]
+ self.to = $1
+ self.headers = $2
+ else
+ set_to($1)
+ set_headers($2)
+ end
+
+ else
+ raise InvalidComponentError,
+ "unrecognised opaque part for mailtoURL: #{@opaque}"
+ end
+ end
+ attr_reader :to
+ attr_reader :headers
+
+ def check_to(v)
+ return true unless v
+ return true if v.size == 0
+
+ if OPAQUE !~ v || /\A#{MAILBOX_PATTERN}*\z/o !~ v
+ raise InvalidComponentError,
+ "bad component(expected opaque component): #{v}"
+ end
+
+ return true
+ end
+ private :check_to
+
+ def set_to(v)
+ @to = v
+ end
+ protected :set_to
+
+ def to=(v)
+ check_to(v)
+ set_to(v)
+ v
+ end
+
+ def check_headers(v)
+ return true unless v
+ return true if v.size == 0
+
+ if OPAQUE !~ v ||
+ /\A(#{HEADER_PATTERN}(?:\&#{HEADER_PATTERN})*)\z/o !~ v
+ raise InvalidComponentError,
+ "bad component(expected opaque component): #{v}"
+ end
+
+ return true
+ end
+ private :check_headers
+
+ def set_headers(v)
+ @headers = []
+ if v
+ v.scan(HEADER_REGEXP) do |x|
+ @headers << x.split(/=/o, 2)
+ end
+ end
+ end
+ protected :set_headers
+
+ def headers=(v)
+ check_headers(v)
+ set_headers(v)
+ v
+ end
+
+ def to_s
+ @scheme + ':' +
+ if @to
+ @to
+ else
+ ''
+ end +
+ if @headers.size > 0
+ '?' + @headers.collect{|x| x.join('=')}.join('&')
+ else
+ ''
+ end +
+ if @fragment
+ '#' + @fragment
+ else
+ ''
+ end
+ end
+ #
+ # == Usage
+ # require 'uri'
+ #
+ # uri = URI.parse("mailto:ruby-list@ruby-lang.org?Subject=subscribe&cc=myaddr")
+ # uri.to_mailtext
+ # # => "To: ruby-list@ruby-lang.org\nSubject: subscribe\nCc: myaddr\n\n\n"
+ #
+ def to_mailtext
+ to = URI::unescape(@to)
+ head = ''
+ body = ''
+ @headers.each do |x|
+ case x[0]
+ when 'body'
+ body = URI::unescape(x[1])
+ when 'to'
+ to << ', ' + URI::unescape(x[1])
+ else
+ head << URI::unescape(x[0]).capitalize + ': ' +
+ URI::unescape(x[1]) + "\n"
+ end
+ end
+
+ return "To: #{to}
+#{head}
+#{body}
+"
+ end
+ alias to_rfc822text to_mailtext
+ end
+
+ @@schemes['MAILTO'] = MailTo
+end
diff --git a/ruby_1_8_5/lib/weakref.rb b/ruby_1_8_5/lib/weakref.rb
new file mode 100644
index 0000000000..7d43f71264
--- /dev/null
+++ b/ruby_1_8_5/lib/weakref.rb
@@ -0,0 +1,94 @@
+require "delegate"
+
+# WeakRef is a class to represent a reference to an object that is not seen by
+# the tracing phase of the garbage collector. This allows the referenced
+# object to be garbage collected as if nothing is referring to it. Because
+# WeakRef delegates method calls to the referenced object, it may be used in
+# place of that object, i.e. it is of the same duck type.
+#
+# Usage:
+#
+# foo = Object.new
+# foo = Object.new
+# p foo.to_s # original's class
+# foo = WeakRef.new(foo)
+# p foo.to_s # should be same class
+# ObjectSpace.garbage_collect
+# p foo.to_s # should raise exception (recycled)
+class WeakRef<Delegator
+
+ # RefError is raised if an object cannot be referenced by a WeakRef.
+ class RefError<StandardError
+ end
+
+ @@id_map = {} # obj -> [ref,...]
+ @@id_rev_map = {} # ref -> obj
+ @@final = lambda{|id|
+ __old_status = Thread.critical
+ Thread.critical = true
+ begin
+ rids = @@id_map[id]
+ if rids
+ for rid in rids
+ @@id_rev_map.delete(rid)
+ end
+ @@id_map.delete(id)
+ end
+ rid = @@id_rev_map[id]
+ if rid
+ @@id_rev_map.delete(id)
+ @@id_map[rid].delete(id)
+ @@id_map.delete(rid) if @@id_map[rid].empty?
+ end
+ ensure
+ Thread.critical = __old_status
+ end
+ }
+
+ # Create a new WeakRef from +orig+.
+ def initialize(orig)
+ super
+ @__id = orig.__id__
+ ObjectSpace.define_finalizer orig, @@final
+ ObjectSpace.define_finalizer self, @@final
+ __old_status = Thread.critical
+ begin
+ Thread.critical = true
+ @@id_map[@__id] = [] unless @@id_map[@__id]
+ ensure
+ Thread.critical = __old_status
+ end
+ @@id_map[@__id].push self.__id__
+ @@id_rev_map[self.__id__] = @__id
+ end
+
+ # Return the object this WeakRef references. Raises RefError if the object
+ # has been garbage collected. The object returned is the object to which
+ # method calls are delegated (see Delegator).
+ def __getobj__
+ unless @@id_rev_map[self.__id__] == @__id
+ raise RefError, "Illegal Reference - probably recycled", caller(2)
+ end
+ begin
+ ObjectSpace._id2ref(@__id)
+ rescue RangeError
+ raise RefError, "Illegal Reference - probably recycled", caller(2)
+ end
+ end
+
+ # Returns true if the referenced object still exists, and false if it has
+ # been garbage collected.
+ def weakref_alive?
+ @@id_rev_map[self.__id__] == @__id
+ end
+end
+
+if __FILE__ == $0
+ require 'thread'
+ foo = Object.new
+ p foo.to_s # original's class
+ foo = WeakRef.new(foo)
+ p foo.to_s # should be same class
+ ObjectSpace.garbage_collect
+ p foo.to_s # should raise exception (recycled)
+end
diff --git a/ruby_1_8_5/lib/webrick.rb b/ruby_1_8_5/lib/webrick.rb
new file mode 100644
index 0000000000..8fca81bafb
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick.rb
@@ -0,0 +1,29 @@
+#
+# WEBrick -- WEB server toolkit.
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2000 TAKAHASHI Masayoshi, GOTOU YUUZOU
+# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: webrick.rb,v 1.12 2002/10/01 17:16:31 gotoyuzo Exp $
+
+require 'webrick/compat.rb'
+
+require 'webrick/version.rb'
+require 'webrick/config.rb'
+require 'webrick/log.rb'
+require 'webrick/server.rb'
+require 'webrick/utils.rb'
+require 'webrick/accesslog'
+
+require 'webrick/htmlutils.rb'
+require 'webrick/httputils.rb'
+require 'webrick/cookie.rb'
+require 'webrick/httpversion.rb'
+require 'webrick/httpstatus.rb'
+require 'webrick/httprequest.rb'
+require 'webrick/httpresponse.rb'
+require 'webrick/httpserver.rb'
+require 'webrick/httpservlet.rb'
+require 'webrick/httpauth.rb'
diff --git a/ruby_1_8_5/lib/webrick/accesslog.rb b/ruby_1_8_5/lib/webrick/accesslog.rb
new file mode 100644
index 0000000000..f97769545e
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/accesslog.rb
@@ -0,0 +1,67 @@
+#
+# accesslog.rb -- Access log handling utilities
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2002 keita yamaguchi
+# Copyright (c) 2002 Internet Programming with Ruby writers
+#
+# $IPR: accesslog.rb,v 1.1 2002/10/01 17:16:32 gotoyuzo Exp $
+
+module WEBrick
+ module AccessLog
+ class AccessLogError < StandardError; end
+
+ CLF_TIME_FORMAT = "[%d/%b/%Y:%H:%M:%S %Z]"
+ COMMON_LOG_FORMAT = "%h %l %u %t \"%r\" %s %b"
+ CLF = COMMON_LOG_FORMAT
+ REFERER_LOG_FORMAT = "%{Referer}i -> %U"
+ AGENT_LOG_FORMAT = "%{User-Agent}i"
+ COMBINED_LOG_FORMAT = "#{CLF} \"%{Referer}i\" \"%{User-agent}i\""
+
+ module_function
+
+ # This format specification is a subset of mod_log_config of Apache.
+ # http://httpd.apache.org/docs/mod/mod_log_config.html#formats
+ def setup_params(config, req, res)
+ params = Hash.new("")
+ params["a"] = req.peeraddr[3]
+ params["b"] = res.sent_size
+ params["e"] = ENV
+ params["f"] = res.filename || ""
+ params["h"] = req.peeraddr[2]
+ params["i"] = req
+ params["l"] = "-"
+ params["m"] = req.request_method
+ params["n"] = req.attributes
+ params["o"] = res
+ params["p"] = req.port
+ params["q"] = req.query_string
+ params["r"] = req.request_line.sub(/\x0d?\x0a\z/o, '')
+ params["s"] = res.status # won't support "%>s"
+ params["t"] = req.request_time
+ params["T"] = Time.now - req.request_time
+ params["u"] = req.user || "-"
+ params["U"] = req.unparsed_uri
+ params["v"] = config[:ServerName]
+ params
+ end
+
+ def format(format_string, params)
+ format_string.gsub(/\%(?:\{(.*?)\})?>?([a-zA-Z%])/){
+ param, spec = $1, $2
+ case spec[0]
+ when ?e, ?i, ?n, ?o
+ raise AccessLogError,
+ "parameter is required for \"#{spec}\"" unless param
+ params[spec][param] || "-"
+ when ?t
+ params[spec].strftime(param || CLF_TIME_FORMAT)
+ when ?%
+ "%"
+ else
+ params[spec]
+ end
+ }
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/cgi.rb b/ruby_1_8_5/lib/webrick/cgi.rb
new file mode 100644
index 0000000000..c6d5acea31
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/cgi.rb
@@ -0,0 +1,257 @@
+#
+# cgi.rb -- Yet another CGI library
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $Id: cgi.rb,v 1.4.2.9 2005/09/28 06:16:59 gotoyuzo Exp $
+
+require "webrick/httprequest"
+require "webrick/httpresponse"
+require "webrick/config"
+require "stringio"
+
+module WEBrick
+ class CGI
+ CGIError = Class.new(StandardError)
+
+ attr_reader :config, :logger
+
+ def initialize(*args)
+ if defined?(MOD_RUBY)
+ unless ENV.has_key?("GATEWAY_INTERFACE")
+ Apache.request.setup_cgi_env
+ end
+ end
+ if %r{HTTP/(\d+\.\d+)} =~ ENV["SERVER_PROTOCOL"]
+ httpv = $1
+ end
+ @config = WEBrick::Config::HTTP.dup.update(
+ :ServerSoftware => ENV["SERVER_SOFTWARE"] || "null",
+ :HTTPVersion => HTTPVersion.new(httpv || "1.0"),
+ :RunOnCGI => true, # to detect if it runs on CGI.
+ :NPH => false # set true to run as NPH script.
+ )
+ if config = args.shift
+ @config.update(config)
+ end
+ @config[:Logger] ||= WEBrick::BasicLog.new($stderr)
+ @logger = @config[:Logger]
+ @options = args
+ end
+
+ def [](key)
+ @config[key]
+ end
+
+ def start(env=ENV, stdin=$stdin, stdout=$stdout)
+ sock = WEBrick::CGI::Socket.new(@config, env, stdin, stdout)
+ req = HTTPRequest.new(@config)
+ res = HTTPResponse.new(@config)
+ unless @config[:NPH] or defined?(MOD_RUBY)
+ def res.setup_header
+ unless @header["status"]
+ phrase = HTTPStatus::reason_phrase(@status)
+ @header["status"] = "#{@status} #{phrase}"
+ end
+ super
+ end
+ def res.status_line
+ ""
+ end
+ end
+
+ begin
+ req.parse(sock)
+ req.script_name = (env["SCRIPT_NAME"] || File.expand_path($0)).dup
+ req.path_info = (env["PATH_INFO"] || "").dup
+ req.query_string = env["QUERY_STRING"]
+ req.user = env["REMOTE_USER"]
+ res.request_method = req.request_method
+ res.request_uri = req.request_uri
+ res.request_http_version = req.http_version
+ res.keep_alive = req.keep_alive?
+ self.service(req, res)
+ rescue HTTPStatus::Error => ex
+ res.set_error(ex)
+ rescue HTTPStatus::Status => ex
+ res.status = ex.code
+ rescue Exception => ex
+ @logger.error(ex)
+ res.set_error(ex, true)
+ ensure
+ req.fixup
+ if defined?(MOD_RUBY)
+ res.setup_header
+ Apache.request.status_line = "#{res.status} #{res.reason_phrase}"
+ Apache.request.status = res.status
+ table = Apache.request.headers_out
+ res.header.each{|key, val|
+ case key
+ when /^content-encoding$/i
+ Apache::request.content_encoding = val
+ when /^content-type$/i
+ Apache::request.content_type = val
+ else
+ table[key] = val.to_s
+ end
+ }
+ res.cookies.each{|cookie|
+ table.add("Set-Cookie", cookie.to_s)
+ }
+ Apache.request.send_http_header
+ res.send_body(sock)
+ else
+ res.send_response(sock)
+ end
+ end
+ end
+
+ def service(req, res)
+ method_name = "do_" + req.request_method.gsub(/-/, "_")
+ if respond_to?(method_name)
+ __send__(method_name, req, res)
+ else
+ raise HTTPStatus::MethodNotAllowed,
+ "unsupported method `#{req.request_method}'."
+ end
+ end
+
+ class Socket
+ include Enumerable
+
+ private
+
+ def initialize(config, env, stdin, stdout)
+ @config = config
+ @env = env
+ @header_part = StringIO.new
+ @body_part = stdin
+ @out_port = stdout
+ @out_port.binmode
+
+ @server_addr = @env["SERVER_ADDR"] || "0.0.0.0"
+ @server_name = @env["SERVER_NAME"]
+ @server_port = @env["SERVER_PORT"]
+ @remote_addr = @env["REMOTE_ADDR"]
+ @remote_host = @env["REMOTE_HOST"] || @remote_addr
+ @remote_port = @env["REMOTE_PORT"] || 0
+
+ begin
+ @header_part << request_line << CRLF
+ setup_header
+ @header_part << CRLF
+ @header_part.rewind
+ rescue Exception => ex
+ raise CGIError, "invalid CGI environment"
+ end
+ end
+
+ def request_line
+ meth = @env["REQUEST_METHOD"] || "GET"
+ unless url = @env["REQUEST_URI"]
+ url = (@env["SCRIPT_NAME"] || File.expand_path($0)).dup
+ url << @env["PATH_INFO"].to_s
+ url = WEBrick::HTTPUtils.escape_path(url)
+ if query_string = @env["QUERY_STRING"]
+ unless query_string.empty?
+ url << "?" << query_string
+ end
+ end
+ end
+ # we cannot get real HTTP version of client ;)
+ httpv = @config[:HTTPVersion]
+ return "#{meth} #{url} HTTP/#{httpv}"
+ end
+
+ def setup_header
+ add_header("CONTENT_TYPE", "Content-Type")
+ add_header("CONTENT_LENGTH", "Content-length")
+ @env.each_key{|name|
+ if /^HTTP_(.*)/ =~ name
+ add_header(name, $1.gsub(/_/, "-"))
+ end
+ }
+ end
+
+ def add_header(envname, hdrname)
+ if value = @env[envname]
+ unless value.empty?
+ @header_part << hdrname << ": " << value << CRLF
+ end
+ end
+ end
+
+ def input
+ @header_part.eof? ? @body_part : @header_part
+ end
+
+ public
+
+ def peeraddr
+ [nil, @remote_port, @remote_host, @remote_addr]
+ end
+
+ def addr
+ [nil, @server_port, @server_name, @server_addr]
+ end
+
+ def gets(eol=LF)
+ input.gets(eol)
+ end
+
+ def read(size=nil)
+ input.read(size)
+ end
+
+ def each
+ input.each{|line| yield(line) }
+ end
+
+ def <<(data)
+ @out_port << data
+ end
+
+ def cert
+ return nil unless defined?(OpenSSL)
+ if pem = @env["SSL_SERVER_CERT"]
+ OpenSSL::X509::Certificate.new(pem) unless pem.empty?
+ end
+ end
+
+ def peer_cert
+ return nil unless defined?(OpenSSL)
+ if pem = @env["SSL_CLIENT_CERT"]
+ OpenSSL::X509::Certificate.new(pem) unless pem.empty?
+ end
+ end
+
+ def peer_cert_chain
+ return nil unless defined?(OpenSSL)
+ if @env["SSL_CLIENT_CERT_CHAIN_0"]
+ keys = @env.keys
+ certs = keys.sort.collect{|k|
+ if /^SSL_CLIENT_CERT_CHAIN_\d+$/ =~ k
+ if pem = @env[k]
+ OpenSSL::X509::Certificate.new(pem) unless pem.empty?
+ end
+ end
+ }
+ certs.compact
+ end
+ end
+
+ def cipher
+ return nil unless defined?(OpenSSL)
+ if cipher = @env["SSL_CIPHER"]
+ ret = [ cipher ]
+ ret << @env["SSL_PROTOCOL"]
+ ret << @env["SSL_CIPHER_USEKEYSIZE"]
+ ret << @env["SSL_CIPHER_ALGKEYSIZE"]
+ ret
+ end
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/compat.rb b/ruby_1_8_5/lib/webrick/compat.rb
new file mode 100644
index 0000000000..ad7760b640
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/compat.rb
@@ -0,0 +1,15 @@
+#
+# compat.rb -- cross platform compatibility
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2002 GOTOU Yuuzou
+# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: compat.rb,v 1.6 2002/10/01 17:16:32 gotoyuzo Exp $
+
+module Errno
+ class EPROTO < SystemCallError; end
+ class ECONNRESET < SystemCallError; end
+ class ECONNABORTED < SystemCallError; end
+end
diff --git a/ruby_1_8_5/lib/webrick/config.rb b/ruby_1_8_5/lib/webrick/config.rb
new file mode 100644
index 0000000000..5897b977d8
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/config.rb
@@ -0,0 +1,97 @@
+#
+# config.rb -- Default configurations.
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
+# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: config.rb,v 1.52 2003/07/22 19:20:42 gotoyuzo Exp $
+
+require 'webrick/version'
+require 'webrick/httpversion'
+require 'webrick/httputils'
+require 'webrick/utils'
+require 'webrick/log'
+
+module WEBrick
+ module Config
+ LIBDIR = File::dirname(__FILE__)
+
+ # for GenericServer
+ General = {
+ :ServerName => Utils::getservername,
+ :BindAddress => nil, # "0.0.0.0" or "::" or nil
+ :Port => nil, # users MUST specifiy this!!
+ :MaxClients => 100, # maximum number of the concurrent connections
+ :ServerType => nil, # default: WEBrick::SimpleServer
+ :Logger => nil, # default: WEBrick::Log.new
+ :ServerSoftware => "WEBrick/#{WEBrick::VERSION} " +
+ "(Ruby/#{RUBY_VERSION}/#{RUBY_RELEASE_DATE})",
+ :TempDir => ENV['TMPDIR']||ENV['TMP']||ENV['TEMP']||'/tmp',
+ :DoNotListen => false,
+ :StartCallback => nil,
+ :StopCallback => nil,
+ :AcceptCallback => nil,
+ }
+
+ # for HTTPServer, HTTPRequest, HTTPResponse ...
+ HTTP = General.dup.update(
+ :Port => 80,
+ :RequestTimeout => 30,
+ :HTTPVersion => HTTPVersion.new("1.1"),
+ :AccessLog => nil,
+ :MimeTypes => HTTPUtils::DefaultMimeTypes,
+ :DirectoryIndex => ["index.html","index.htm","index.cgi","index.rhtml"],
+ :DocumentRoot => nil,
+ :DocumentRootOptions => { :FancyIndexing => true },
+ :RequestHandler => nil,
+ :RequestCallback => nil, # alias of :RequestHandler
+ :ServerAlias => nil,
+
+ # for HTTPProxyServer
+ :ProxyAuthProc => nil,
+ :ProxyContentHandler => nil,
+ :ProxyVia => true,
+ :ProxyTimeout => true,
+ :ProxyURI => nil,
+
+ :CGIInterpreter => nil,
+ :CGIPathEnv => nil,
+
+ # workaround: if Request-URIs contain 8bit chars,
+ # they should be escaped before calling of URI::parse().
+ :Escape8bitURI => false
+ )
+
+ FileHandler = {
+ :NondisclosureName => [".ht*", "*~"],
+ :FancyIndexing => false,
+ :HandlerTable => {},
+ :HandlerCallback => nil,
+ :DirectoryCallback => nil,
+ :FileCallback => nil,
+ :UserDir => nil, # e.g. "public_html"
+ :AcceptableLanguages => [] # ["en", "ja", ... ]
+ }
+
+ BasicAuth = {
+ :AutoReloadUserDB => true,
+ }
+
+ DigestAuth = {
+ :Algorithm => 'MD5-sess', # or 'MD5'
+ :Domain => nil, # an array includes domain names.
+ :Qop => [ 'auth' ], # 'auth' or 'auth-int' or both.
+ :UseOpaque => true,
+ :UseNextNonce => false,
+ :CheckNc => false,
+ :UseAuthenticationInfoHeader => true,
+ :AutoReloadUserDB => true,
+ :NonceExpirePeriod => 30*60,
+ :NonceExpireDelta => 60,
+ :InternetExplorerHack => true,
+ :OperaHack => true,
+ }
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/cookie.rb b/ruby_1_8_5/lib/webrick/cookie.rb
new file mode 100644
index 0000000000..b9663dc791
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/cookie.rb
@@ -0,0 +1,104 @@
+#
+# cookie.rb -- Cookie class
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
+# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: cookie.rb,v 1.16 2002/09/21 12:23:35 gotoyuzo Exp $
+
+require 'time'
+require 'webrick/httputils'
+
+module WEBrick
+ class Cookie
+
+ attr_reader :name
+ attr_accessor :value, :version
+ attr_accessor :domain, :path, :secure
+ attr_accessor :comment, :max_age
+ #attr_accessor :comment_url, :discard, :port
+
+ def initialize(name, value)
+ @name = name
+ @value = value
+ @version = 0 # Netscape Cookie
+
+ @domain = @path = @secure = @comment = @max_age =
+ @expires = @comment_url = @discard = @port = nil
+ end
+
+ def expires=(t)
+ @expires = t && (t.is_a?(Time) ? t.httpdate : t.to_s)
+ end
+
+ def expires
+ @expires && Time.parse(@expires)
+ end
+
+ def to_s
+ ret = ""
+ ret << @name << "=" << @value
+ ret << "; " << "Version=" << @version.to_s if @version > 0
+ ret << "; " << "Domain=" << @domain if @domain
+ ret << "; " << "Expires=" << @expires if @expires
+ ret << "; " << "Max-Age=" << @max_age.to_s if @max_age
+ ret << "; " << "Comment=" << @comment if @comment
+ ret << "; " << "Path=" << @path if @path
+ ret << "; " << "Secure" if @secure
+ ret
+ end
+
+ # Cookie::parse()
+ # It parses Cookie field sent from the user agent.
+ def self.parse(str)
+ if str
+ ret = []
+ cookie = nil
+ ver = 0
+ str.split(/[;,]\s+/).each{|x|
+ key, val = x.split(/=/,2)
+ val = val ? HTTPUtils::dequote(val) : ""
+ case key
+ when "$Version"; ver = val.to_i
+ when "$Path"; cookie.path = val
+ when "$Domain"; cookie.domain = val
+ when "$Port"; cookie.port = val
+ else
+ ret << cookie if cookie
+ cookie = self.new(key, val)
+ cookie.version = ver
+ end
+ }
+ ret << cookie if cookie
+ ret
+ end
+ end
+
+ def self.parse_set_cookie(str)
+ cookie_elem = str.split(/;/)
+ first_elem = cookie_elem.shift
+ first_elem.strip!
+ key, value = first_elem.split(/=/, 2)
+ cookie = new(key, HTTPUtils.dequote(value))
+ cookie_elem.each{|pair|
+ pair.strip!
+ key, value = pair.split(/=/, 2)
+ if value
+ value = HTTPUtils.dequote(value.strip)
+ end
+ case key.downcase
+ when "domain" then cookie.domain = value
+ when "path" then cookie.path = value
+ when "expires" then cookie.expires = value
+ when "max-age" then cookie.max_age = Integer(value)
+ when "comment" then cookie.comment = value
+ when "version" then cookie.version = Integer(value)
+ when "secure" then cookie.secure = true
+ end
+ }
+ return cookie
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/htmlutils.rb b/ruby_1_8_5/lib/webrick/htmlutils.rb
new file mode 100644
index 0000000000..cf8d542c09
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/htmlutils.rb
@@ -0,0 +1,25 @@
+#
+# htmlutils.rb -- HTMLUtils Module
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
+# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: htmlutils.rb,v 1.7 2002/09/21 12:23:35 gotoyuzo Exp $
+
+module WEBrick
+ module HTMLUtils
+
+ def escape(string)
+ str = string ? string.dup : ""
+ str.gsub!(/&/n, '&amp;')
+ str.gsub!(/\"/n, '&quot;')
+ str.gsub!(/>/n, '&gt;')
+ str.gsub!(/</n, '&lt;')
+ str
+ end
+ module_function :escape
+
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/httpauth.rb b/ruby_1_8_5/lib/webrick/httpauth.rb
new file mode 100644
index 0000000000..147c04021c
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/httpauth.rb
@@ -0,0 +1,45 @@
+#
+# httpauth.rb -- HTTP access authentication
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
+# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: httpauth.rb,v 1.14 2003/07/22 19:20:42 gotoyuzo Exp $
+
+require 'webrick/httpauth/basicauth'
+require 'webrick/httpauth/digestauth'
+require 'webrick/httpauth/htpasswd'
+require 'webrick/httpauth/htdigest'
+require 'webrick/httpauth/htgroup'
+
+module WEBrick
+ module HTTPAuth
+ module_function
+
+ def _basic_auth(req, res, realm, req_field, res_field, err_type, block)
+ user = pass = nil
+ if /^Basic\s+(.*)/o =~ req[req_field]
+ userpass = $1
+ user, pass = userpass.unpack("m*")[0].split(":", 2)
+ end
+ if block.call(user, pass)
+ req.user = user
+ return
+ end
+ res[res_field] = "Basic realm=\"#{realm}\""
+ raise err_type
+ end
+
+ def basic_auth(req, res, realm, &block)
+ _basic_auth(req, res, realm, "Authorization", "WWW-Authenticate",
+ HTTPStatus::Unauthorized, block)
+ end
+
+ def proxy_basic_auth(req, res, realm, &block)
+ _basic_auth(req, res, realm, "Proxy-Authorization", "Proxy-Authenticate",
+ HTTPStatus::ProxyAuthenticationRequired, block)
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/httpauth/authenticator.rb b/ruby_1_8_5/lib/webrick/httpauth/authenticator.rb
new file mode 100644
index 0000000000..fe2dbf4e0c
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/httpauth/authenticator.rb
@@ -0,0 +1,79 @@
+#
+# httpauth/authenticator.rb -- Authenticator mix-in module.
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: authenticator.rb,v 1.3 2003/02/20 07:15:47 gotoyuzo Exp $
+
+module WEBrick
+ module HTTPAuth
+ module Authenticator
+ RequestField = "Authorization"
+ ResponseField = "WWW-Authenticate"
+ ResponseInfoField = "Authentication-Info"
+ AuthException = HTTPStatus::Unauthorized
+ AuthScheme = nil # must override by the derived class
+
+ attr_reader :realm, :userdb, :logger
+
+ private
+
+ def check_init(config)
+ [:UserDB, :Realm].each{|sym|
+ unless config[sym]
+ raise ArgumentError, "Argument #{sym.inspect} missing."
+ end
+ }
+ @realm = config[:Realm]
+ @userdb = config[:UserDB]
+ @logger = config[:Logger] || Log::new($stderr)
+ @reload_db = config[:AutoReloadUserDB]
+ @request_field = self::class::RequestField
+ @response_field = self::class::ResponseField
+ @resp_info_field = self::class::ResponseInfoField
+ @auth_exception = self::class::AuthException
+ @auth_scheme = self::class::AuthScheme
+ end
+
+ def check_scheme(req)
+ unless credentials = req[@request_field]
+ error("no credentials in the request.")
+ return nil
+ end
+ unless match = /^#{@auth_scheme}\s+/.match(credentials)
+ error("invalid scheme in %s.", credentials)
+ info("%s: %s", @request_field, credentials) if $DEBUG
+ return nil
+ end
+ return match.post_match
+ end
+
+ def log(meth, fmt, *args)
+ msg = format("%s %s: ", @auth_scheme, @realm)
+ msg << fmt % args
+ @logger.send(meth, msg)
+ end
+
+ def error(fmt, *args)
+ if @logger.error?
+ log(:error, fmt, *args)
+ end
+ end
+
+ def info(fmt, *args)
+ if @logger.info?
+ log(:info, fmt, *args)
+ end
+ end
+ end
+
+ module ProxyAuthenticator
+ RequestField = "Proxy-Authorization"
+ ResponseField = "Proxy-Authenticate"
+ InfoField = "Proxy-Authentication-Info"
+ AuthException = HTTPStatus::ProxyAuthenticationRequired
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/httpauth/basicauth.rb b/ruby_1_8_5/lib/webrick/httpauth/basicauth.rb
new file mode 100644
index 0000000000..e835361dc2
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/httpauth/basicauth.rb
@@ -0,0 +1,65 @@
+#
+# httpauth/basicauth.rb -- HTTP basic access authentication
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: basicauth.rb,v 1.5 2003/02/20 07:15:47 gotoyuzo Exp $
+
+require 'webrick/config'
+require 'webrick/httpstatus'
+require 'webrick/httpauth/authenticator'
+
+module WEBrick
+ module HTTPAuth
+ class BasicAuth
+ include Authenticator
+
+ AuthScheme = "Basic"
+
+ def self.make_passwd(realm, user, pass)
+ pass ||= ""
+ pass.crypt(Utils::random_string(2))
+ end
+
+ attr_reader :realm, :userdb, :logger
+
+ def initialize(config, default=Config::BasicAuth)
+ check_init(config)
+ @config = default.dup.update(config)
+ end
+
+ def authenticate(req, res)
+ unless basic_credentials = check_scheme(req)
+ challenge(req, res)
+ end
+ userid, password = basic_credentials.unpack("m*")[0].split(":", 2)
+ password ||= ""
+ if userid.empty?
+ error("user id was not given.")
+ challenge(req, res)
+ end
+ unless encpass = @userdb.get_passwd(@realm, userid, @reload_db)
+ error("%s: the user is not allowed.", userid)
+ challenge(req, res)
+ end
+ if password.crypt(encpass) != encpass
+ error("%s: password unmatch.", userid)
+ challenge(req, res)
+ end
+ info("%s: authentication succeeded.", userid)
+ req.user = userid
+ end
+
+ def challenge(req, res)
+ res[@response_field] = "#{@auth_scheme} realm=\"#{@realm}\""
+ raise @auth_exception
+ end
+ end
+
+ class ProxyBasicAuth < BasicAuth
+ include ProxyAuthenticator
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/httpauth/digestauth.rb b/ruby_1_8_5/lib/webrick/httpauth/digestauth.rb
new file mode 100644
index 0000000000..318e0bf17f
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/httpauth/digestauth.rb
@@ -0,0 +1,343 @@
+#
+# httpauth/digestauth.rb -- HTTP digest access authentication
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2003 Internet Programming with Ruby writers.
+# Copyright (c) 2003 H.M.
+#
+# The original implementation is provided by H.M.
+# URL: http://rwiki.jin.gr.jp/cgi-bin/rw-cgi.rb?cmd=view;name=
+# %C7%A7%BE%DA%B5%A1%C7%BD%A4%F2%B2%FE%C2%A4%A4%B7%A4%C6%A4%DF%A4%EB
+#
+# $IPR: digestauth.rb,v 1.5 2003/02/20 07:15:47 gotoyuzo Exp $
+
+require 'webrick/config'
+require 'webrick/httpstatus'
+require 'webrick/httpauth/authenticator'
+require 'digest/md5'
+require 'digest/sha1'
+
+module WEBrick
+ module HTTPAuth
+ class DigestAuth
+ include Authenticator
+
+ AuthScheme = "Digest"
+ OpaqueInfo = Struct.new(:time, :nonce, :nc)
+ attr_reader :algorithm, :qop
+
+ def self.make_passwd(realm, user, pass)
+ pass ||= ""
+ Digest::MD5::hexdigest([user, realm, pass].join(":"))
+ end
+
+ def initialize(config, default=Config::DigestAuth)
+ check_init(config)
+ @config = default.dup.update(config)
+ @algorithm = @config[:Algorithm]
+ @domain = @config[:Domain]
+ @qop = @config[:Qop]
+ @use_opaque = @config[:UseOpaque]
+ @use_next_nonce = @config[:UseNextNonce]
+ @check_nc = @config[:CheckNc]
+ @use_auth_info_header = @config[:UseAuthenticationInfoHeader]
+ @nonce_expire_period = @config[:NonceExpirePeriod]
+ @nonce_expire_delta = @config[:NonceExpireDelta]
+ @internet_explorer_hack = @config[:InternetExplorerHack]
+ @opera_hack = @config[:OperaHack]
+
+ case @algorithm
+ when 'MD5','MD5-sess'
+ @h = Digest::MD5
+ when 'SHA1','SHA1-sess' # it is a bonus feature :-)
+ @h = Digest::SHA1
+ else
+ msg = format('Alogrithm "%s" is not supported.', @algorithm)
+ raise ArgumentError.new(msg)
+ end
+
+ @instance_key = hexdigest(self.__id__, Time.now.to_i, Process.pid)
+ @opaques = {}
+ @last_nonce_expire = Time.now
+ @mutex = Mutex.new
+ end
+
+ def authenticate(req, res)
+ unless result = @mutex.synchronize{ _authenticate(req, res) }
+ challenge(req, res)
+ end
+ if result == :nonce_is_stale
+ challenge(req, res, true)
+ end
+ return true
+ end
+
+ def challenge(req, res, stale=false)
+ nonce = generate_next_nonce(req)
+ if @use_opaque
+ opaque = generate_opaque(req)
+ @opaques[opaque].nonce = nonce
+ end
+
+ param = Hash.new
+ param["realm"] = HTTPUtils::quote(@realm)
+ param["domain"] = HTTPUtils::quote(@domain.to_a.join(" ")) if @domain
+ param["nonce"] = HTTPUtils::quote(nonce)
+ param["opaque"] = HTTPUtils::quote(opaque) if opaque
+ param["stale"] = stale.to_s
+ param["algorithm"] = @algorithm
+ param["qop"] = HTTPUtils::quote(@qop.to_a.join(",")) if @qop
+
+ res[@response_field] =
+ "#{@auth_scheme} " + param.map{|k,v| "#{k}=#{v}" }.join(", ")
+ info("%s: %s", @response_field, res[@response_field]) if $DEBUG
+ raise @auth_exception
+ end
+
+ private
+
+ MustParams = ['username','realm','nonce','uri','response']
+ MustParamsAuth = ['cnonce','nc']
+
+ def _authenticate(req, res)
+ unless digest_credentials = check_scheme(req)
+ return false
+ end
+
+ auth_req = split_param_value(digest_credentials)
+ if auth_req['qop'] == "auth" || auth_req['qop'] == "auth-int"
+ req_params = MustParams + MustParamsAuth
+ else
+ req_params = MustParams
+ end
+ req_params.each{|key|
+ unless auth_req.has_key?(key)
+ error('%s: parameter missing. "%s"', auth_req['username'], key)
+ raise HTTPStatus::BadRequest
+ end
+ }
+
+ if !check_uri(req, auth_req)
+ raise HTTPStatus::BadRequest
+ end
+
+ if auth_req['realm'] != @realm
+ error('%s: realm unmatch. "%s" for "%s"',
+ auth_req['username'], auth_req['realm'], @realm)
+ return false
+ end
+
+ auth_req['algorithm'] ||= 'MD5'
+ if auth_req['algorithm'] != @algorithm &&
+ (@opera_hack && auth_req['algorithm'] != @algorithm.upcase)
+ error('%s: algorithm unmatch. "%s" for "%s"',
+ auth_req['username'], auth_req['algorithm'], @algorithm)
+ return false
+ end
+
+ if (@qop.nil? && auth_req.has_key?('qop')) ||
+ (@qop && (! @qop.member?(auth_req['qop'])))
+ error('%s: the qop is not allowed. "%s"',
+ auth_req['username'], auth_req['qop'])
+ return false
+ end
+
+ password = @userdb.get_passwd(@realm, auth_req['username'], @reload_db)
+ unless password
+ error('%s: the user is not allowd.', auth_req['username'])
+ return false
+ end
+
+ nonce_is_invalid = false
+ if @use_opaque
+ info("@opaque = %s", @opaque.inspect) if $DEBUG
+ if !(opaque = auth_req['opaque'])
+ error('%s: opaque is not given.', auth_req['username'])
+ nonce_is_invalid = true
+ elsif !(opaque_struct = @opaques[opaque])
+ error('%s: invalid opaque is given.', auth_req['username'])
+ nonce_is_invalid = true
+ elsif !check_opaque(opaque_struct, req, auth_req)
+ @opaques.delete(auth_req['opaque'])
+ nonce_is_invalid = true
+ end
+ elsif !check_nonce(req, auth_req)
+ nonce_is_invalid = true
+ end
+
+ if /-sess$/ =~ auth_req['algorithm'] ||
+ (@opera_hack && /-SESS$/ =~ auth_req['algorithm'])
+ ha1 = hexdigest(password, auth_req['nonce'], auth_req['cnonce'])
+ else
+ ha1 = password
+ end
+
+ if auth_req['qop'] == "auth" || auth_req['qop'] == nil
+ ha2 = hexdigest(req.request_method, auth_req['uri'])
+ ha2_res = hexdigest("", auth_req['uri'])
+ elsif auth_req['qop'] == "auth-int"
+ ha2 = hexdigest(req.request_method, auth_req['uri'],
+ hexdigest(req.body))
+ ha2_res = hexdigest("", auth_req['uri'], hexdigest(res.body))
+ end
+
+ if auth_req['qop'] == "auth" || auth_req['qop'] == "auth-int"
+ param2 = ['nonce', 'nc', 'cnonce', 'qop'].map{|key|
+ auth_req[key]
+ }.join(':')
+ digest = hexdigest(ha1, param2, ha2)
+ digest_res = hexdigest(ha1, param2, ha2_res)
+ else
+ digest = hexdigest(ha1, auth_req['nonce'], ha2)
+ digest_res = hexdigest(ha1, auth_req['nonce'], ha2_res)
+ end
+
+ if digest != auth_req['response']
+ error("%s: digest unmatch.", auth_req['username'])
+ return false
+ elsif nonce_is_invalid
+ error('%s: digest is valid, but nonce is not valid.',
+ auth_req['username'])
+ return :nonce_is_stale
+ elsif @use_auth_info_header
+ auth_info = {
+ 'nextnonce' => generate_next_nonce(req),
+ 'rspauth' => digest_res
+ }
+ if @use_opaque
+ opaque_struct.time = req.request_time
+ opaque_struct.nonce = auth_info['nextnonce']
+ opaque_struct.nc = "%08x" % (auth_req['nc'].hex + 1)
+ end
+ if auth_req['qop'] == "auth" || auth_req['qop'] == "auth-int"
+ ['qop','cnonce','nc'].each{|key|
+ auth_info[key] = auth_req[key]
+ }
+ end
+ res[@resp_info_field] = auth_info.keys.map{|key|
+ if key == 'nc'
+ key + '=' + auth_info[key]
+ else
+ key + "=" + HTTPUtils::quote(auth_info[key])
+ end
+ }.join(', ')
+ end
+ info('%s: authentication scceeded.', auth_req['username'])
+ req.user = auth_req['username']
+ return true
+ end
+
+ def split_param_value(string)
+ ret = {}
+ while string.size != 0
+ case string
+ when /^\s*([\w\-\.\*\%\!]+)=\s*\"((\\.|[^\"])*)\"\s*,?/
+ key = $1
+ matched = $2
+ string = $'
+ ret[key] = matched.gsub(/\\(.)/, "\\1")
+ when /^\s*([\w\-\.\*\%\!]+)=\s*([^,\"]*),?/
+ key = $1
+ matched = $2
+ string = $'
+ ret[key] = matched.clone
+ when /^s*^,/
+ string = $'
+ else
+ break
+ end
+ end
+ ret
+ end
+
+ def generate_next_nonce(req)
+ now = "%012d" % req.request_time.to_i
+ pk = hexdigest(now, @instance_key)[0,32]
+ nonce = [now + ":" + pk].pack("m*").chop # it has 60 length of chars.
+ nonce
+ end
+
+ def check_nonce(req, auth_req)
+ username = auth_req['username']
+ nonce = auth_req['nonce']
+
+ pub_time, pk = nonce.unpack("m*")[0].split(":", 2)
+ if (!pub_time || !pk)
+ error("%s: empty nonce is given", username)
+ return false
+ elsif (hexdigest(pub_time, @instance_key)[0,32] != pk)
+ error("%s: invalid private-key: %s for %s",
+ username, hexdigest(pub_time, @instance_key)[0,32], pk)
+ return false
+ end
+
+ diff_time = req.request_time.to_i - pub_time.to_i
+ if (diff_time < 0)
+ error("%s: difference of time-stamp is negative.", username)
+ return false
+ elsif diff_time > @nonce_expire_period
+ error("%s: nonce is expired.", username)
+ return false
+ end
+
+ return true
+ end
+
+ def generate_opaque(req)
+ @mutex.synchronize{
+ now = req.request_time
+ if now - @last_nonce_expire > @nonce_expire_delta
+ @opaques.delete_if{|key,val|
+ (now - val.time) > @nonce_expire_period
+ }
+ @last_nonce_expire = now
+ end
+ begin
+ opaque = Utils::random_string(16)
+ end while @opaques[opaque]
+ @opaques[opaque] = OpaqueInfo.new(now, nil, '00000001')
+ opaque
+ }
+ end
+
+ def check_opaque(opaque_struct, req, auth_req)
+ if (@use_next_nonce && auth_req['nonce'] != opaque_struct.nonce)
+ error('%s: nonce unmatched. "%s" for "%s"',
+ auth_req['username'], auth_req['nonce'], opaque_struct.nonce)
+ return false
+ elsif !check_nonce(req, auth_req)
+ return false
+ end
+ if (@check_nc && auth_req['nc'] != opaque_struct.nc)
+ error('%s: nc unmatched."%s" for "%s"',
+ auth_req['username'], auth_req['nc'], opaque_struct.nc)
+ return false
+ end
+ true
+ end
+
+ def check_uri(req, auth_req)
+ uri = auth_req['uri']
+ if uri != req.request_uri.to_s && uri != req.unparsed_uri &&
+ (@internet_explorer_hack && uri != req.path)
+ error('%s: uri unmatch. "%s" for "%s"', auth_req['username'],
+ auth_req['uri'], req.request_uri.to_s)
+ return false
+ end
+ true
+ end
+
+ def hexdigest(*args)
+ @h.hexdigest(args.join(":"))
+ end
+ end
+
+ class ProxyDigestAuth < DigestAuth
+ include ProxyAuthenticator
+
+ def check_uri(req, auth_req)
+ return true
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/httpauth/htdigest.rb b/ruby_1_8_5/lib/webrick/httpauth/htdigest.rb
new file mode 100644
index 0000000000..3949756f2b
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/httpauth/htdigest.rb
@@ -0,0 +1,91 @@
+#
+# httpauth/htdigest.rb -- Apache compatible htdigest file
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: htdigest.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $
+
+require 'webrick/httpauth/userdb'
+require 'webrick/httpauth/digestauth'
+require 'tempfile'
+
+module WEBrick
+ module HTTPAuth
+ class Htdigest
+ include UserDB
+
+ def initialize(path)
+ @path = path
+ @mtime = Time.at(0)
+ @digest = Hash.new
+ @mutex = Mutex::new
+ @auth_type = DigestAuth
+ open(@path,"a").close unless File::exist?(@path)
+ reload
+ end
+
+ def reload
+ mtime = File::mtime(@path)
+ if mtime > @mtime
+ @digest.clear
+ open(@path){|io|
+ while line = io.gets
+ line.chomp!
+ user, realm, pass = line.split(/:/, 3)
+ unless @digest[realm]
+ @digest[realm] = Hash.new
+ end
+ @digest[realm][user] = pass
+ end
+ }
+ @mtime = mtime
+ end
+ end
+
+ def flush(output=nil)
+ output ||= @path
+ tmp = Tempfile.new("htpasswd", File::dirname(output))
+ begin
+ each{|item| tmp.puts(item.join(":")) }
+ tmp.close
+ File::rename(tmp.path, output)
+ rescue
+ tmp.close(true)
+ end
+ end
+
+ def get_passwd(realm, user, reload_db)
+ reload() if reload_db
+ if hash = @digest[realm]
+ hash[user]
+ end
+ end
+
+ def set_passwd(realm, user, pass)
+ @mutex.synchronize{
+ unless @digest[realm]
+ @digest[realm] = Hash.new
+ end
+ @digest[realm][user] = make_passwd(realm, user, pass)
+ }
+ end
+
+ def delete_passwd(realm, user)
+ if hash = @digest[realm]
+ hash.delete(user)
+ end
+ end
+
+ def each
+ @digest.keys.sort.each{|realm|
+ hash = @digest[realm]
+ hash.keys.sort.each{|user|
+ yield([user, realm, hash[user]])
+ }
+ }
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/httpauth/htgroup.rb b/ruby_1_8_5/lib/webrick/httpauth/htgroup.rb
new file mode 100644
index 0000000000..c9270c61cc
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/httpauth/htgroup.rb
@@ -0,0 +1,61 @@
+#
+# httpauth/htgroup.rb -- Apache compatible htgroup file
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: htgroup.rb,v 1.1 2003/02/16 22:22:56 gotoyuzo Exp $
+
+require 'tempfile'
+
+module WEBrick
+ module HTTPAuth
+ class Htgroup
+ def initialize(path)
+ @path = path
+ @mtime = Time.at(0)
+ @group = Hash.new
+ open(@path,"a").close unless File::exist?(@path)
+ reload
+ end
+
+ def reload
+ if (mtime = File::mtime(@path)) > @mtime
+ @group.clear
+ open(@path){|io|
+ while line = io.gets
+ line.chomp!
+ group, members = line.split(/:\s*/)
+ @group[group] = members.split(/\s+/)
+ end
+ }
+ @mtime = mtime
+ end
+ end
+
+ def flush(output=nil)
+ output ||= @path
+ tmp = Tempfile.new("htgroup", File::dirname(output))
+ begin
+ @group.keys.sort.each{|group|
+ tmp.puts(format("%s: %s", group, self.members(group).join(" ")))
+ }
+ tmp.close
+ File::rename(tmp.path, output)
+ rescue
+ tmp.close(true)
+ end
+ end
+
+ def members(group)
+ reload
+ @group[group] || []
+ end
+
+ def add(group, members)
+ @group[group] = members(group) | members
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/httpauth/htpasswd.rb b/ruby_1_8_5/lib/webrick/httpauth/htpasswd.rb
new file mode 100644
index 0000000000..40f9297b05
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/httpauth/htpasswd.rb
@@ -0,0 +1,83 @@
+#
+# httpauth/htpasswd -- Apache compatible htpasswd file
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: htpasswd.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $
+
+require 'webrick/httpauth/userdb'
+require 'webrick/httpauth/basicauth'
+require 'tempfile'
+
+module WEBrick
+ module HTTPAuth
+ class Htpasswd
+ include UserDB
+
+ def initialize(path)
+ @path = path
+ @mtime = Time.at(0)
+ @passwd = Hash.new
+ @auth_type = BasicAuth
+ open(@path,"a").close unless File::exist?(@path)
+ reload
+ end
+
+ def reload
+ mtime = File::mtime(@path)
+ if mtime > @mtime
+ @passwd.clear
+ open(@path){|io|
+ while line = io.gets
+ line.chomp!
+ case line
+ when %r!\A[^:]+:[a-zA-Z0-9./]{13}\z!
+ user, pass = line.split(":")
+ when /:\$/, /:\{SHA\}/
+ raise NotImplementedError,
+ 'MD5, SHA1 .htpasswd file not supported'
+ else
+ raise StandardError, 'bad .htpasswd file'
+ end
+ @passwd[user] = pass
+ end
+ }
+ @mtime = mtime
+ end
+ end
+
+ def flush(output=nil)
+ output ||= @path
+ tmp = Tempfile.new("htpasswd", File::dirname(output))
+ begin
+ each{|item| tmp.puts(item.join(":")) }
+ tmp.close
+ File::rename(tmp.path, output)
+ rescue
+ tmp.close(true)
+ end
+ end
+
+ def get_passwd(realm, user, reload_db)
+ reload() if reload_db
+ @passwd[user]
+ end
+
+ def set_passwd(realm, user, pass)
+ @passwd[user] = make_passwd(realm, user, pass)
+ end
+
+ def delete_passwd(realm, user)
+ @passwd.delete(user)
+ end
+
+ def each
+ @passwd.keys.sort.each{|user|
+ yield([user, @passwd[user]])
+ }
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/httpauth/userdb.rb b/ruby_1_8_5/lib/webrick/httpauth/userdb.rb
new file mode 100644
index 0000000000..33e01405f4
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/httpauth/userdb.rb
@@ -0,0 +1,29 @@
+#
+# httpauth/userdb.rb -- UserDB mix-in module.
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: userdb.rb,v 1.2 2003/02/20 07:15:48 gotoyuzo Exp $
+
+module WEBrick
+ module HTTPAuth
+ module UserDB
+ attr_accessor :auth_type # BasicAuth or DigestAuth
+
+ def make_passwd(realm, user, pass)
+ @auth_type::make_passwd(realm, user, pass)
+ end
+
+ def set_passwd(realm, user, pass)
+ self[user] = pass
+ end
+
+ def get_passwd(realm, user, reload_db=false)
+ # reload_db is dummy
+ make_passwd(realm, user, self[user])
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/httpproxy.rb b/ruby_1_8_5/lib/webrick/httpproxy.rb
new file mode 100644
index 0000000000..14e3499775
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/httpproxy.rb
@@ -0,0 +1,254 @@
+#
+# httpproxy.rb -- HTTPProxy Class
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2002 GOTO Kentaro
+# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: httpproxy.rb,v 1.18 2003/03/08 18:58:10 gotoyuzo Exp $
+# $kNotwork: straw.rb,v 1.3 2002/02/12 15:13:07 gotoken Exp $
+
+require "webrick/httpserver"
+require "net/http"
+
+Net::HTTP::version_1_2 if RUBY_VERSION < "1.7"
+
+module WEBrick
+ NullReader = Object.new
+ class << NullReader
+ def read(*args)
+ nil
+ end
+ alias gets read
+ end
+
+ class HTTPProxyServer < HTTPServer
+ def initialize(config)
+ super
+ c = @config
+ @via = "#{c[:HTTPVersion]} #{c[:ServerName]}:#{c[:Port]}"
+ end
+
+ def service(req, res)
+ if req.request_method == "CONNECT"
+ proxy_connect(req, res)
+ elsif req.unparsed_uri =~ %r!^http://!
+ proxy_service(req, res)
+ else
+ super(req, res)
+ end
+ end
+
+ def proxy_auth(req, res)
+ if proc = @config[:ProxyAuthProc]
+ proc.call(req, res)
+ end
+ req.header.delete("proxy-authorization")
+ end
+
+ # Some header fields shuold not be transfered.
+ HopByHop = %w( connection keep-alive proxy-authenticate upgrade
+ proxy-authorization te trailers transfer-encoding )
+ ShouldNotTransfer = %w( set-cookie proxy-connection )
+ def split_field(f) f ? f.split(/,\s+/).collect{|i| i.downcase } : [] end
+
+ def choose_header(src, dst)
+ connections = split_field(src['connection'])
+ src.each{|key, value|
+ key = key.downcase
+ if HopByHop.member?(key) || # RFC2616: 13.5.1
+ connections.member?(key) || # RFC2616: 14.10
+ ShouldNotTransfer.member?(key) # pragmatics
+ @logger.debug("choose_header: `#{key}: #{value}'")
+ next
+ end
+ dst[key] = value
+ }
+ end
+
+ # Net::HTTP is stupid about the multiple header fields.
+ # Here is workaround:
+ def set_cookie(src, dst)
+ if str = src['set-cookie']
+ cookies = []
+ str.split(/,\s*/).each{|token|
+ if /^[^=]+;/o =~ token
+ cookies[-1] << ", " << token
+ elsif /=/o =~ token
+ cookies << token
+ else
+ cookies[-1] << ", " << token
+ end
+ }
+ dst.cookies.replace(cookies)
+ end
+ end
+
+ def set_via(h)
+ if @config[:ProxyVia]
+ if h['via']
+ h['via'] << ", " << @via
+ else
+ h['via'] = @via
+ end
+ end
+ end
+
+ def proxy_uri(req, res)
+ @config[:ProxyURI]
+ end
+
+ def proxy_service(req, res)
+ # Proxy Authentication
+ proxy_auth(req, res)
+
+ # Create Request-URI to send to the origin server
+ uri = req.request_uri
+ path = uri.path.dup
+ path << "?" << uri.query if uri.query
+
+ # Choose header fields to transfer
+ header = Hash.new
+ choose_header(req, header)
+ set_via(header)
+
+ # select upstream proxy server
+ if proxy = proxy_uri(req, res)
+ proxy_host = proxy.host
+ proxy_port = proxy.port
+ if proxy.userinfo
+ credentials = "Basic " + [proxy.userinfo].pack("m*")
+ credentials.chomp!
+ header['proxy-authorization'] = credentials
+ end
+ end
+
+ response = nil
+ begin
+ http = Net::HTTP.new(uri.host, uri.port, proxy_host, proxy_port)
+ http.start{
+ if @config[:ProxyTimeout]
+ ################################## these issues are
+ http.open_timeout = 30 # secs # necessary (maybe bacause
+ http.read_timeout = 60 # secs # Ruby's bug, but why?)
+ ##################################
+ end
+ case req.request_method
+ when "GET" then response = http.get(path, header)
+ when "POST" then response = http.post(path, req.body || "", header)
+ when "HEAD" then response = http.head(path, header)
+ else
+ raise HTTPStatus::MethodNotAllowed,
+ "unsupported method `#{req.request_method}'."
+ end
+ }
+ rescue => err
+ logger.debug("#{err.class}: #{err.message}")
+ raise HTTPStatus::ServiceUnavailable, err.message
+ end
+
+ # Persistent connction requirements are mysterious for me.
+ # So I will close the connection in every response.
+ res['proxy-connection'] = "close"
+ res['connection'] = "close"
+
+ # Convert Net::HTTP::HTTPResponse to WEBrick::HTTPProxy
+ res.status = response.code.to_i
+ choose_header(response, res)
+ set_cookie(response, res)
+ set_via(res)
+ res.body = response.body
+
+ # Process contents
+ if handler = @config[:ProxyContentHandler]
+ handler.call(req, res)
+ end
+ end
+
+ def proxy_connect(req, res)
+ # Proxy Authentication
+ proxy_auth(req, res)
+
+ ua = Thread.current[:WEBrickSocket] # User-Agent
+ raise HTTPStatus::InternalServerError,
+ "[BUG] cannot get socket" unless ua
+
+ host, port = req.unparsed_uri.split(":", 2)
+ # Proxy authentication for upstream proxy server
+ if proxy = proxy_uri(req, res)
+ proxy_request_line = "CONNECT #{host}:#{port} HTTP/1.0"
+ if proxy.userinfo
+ credentials = "Basic " + [proxy.userinfo].pack("m*")
+ credentials.chomp!
+ end
+ host, port = proxy.host, proxy.port
+ end
+
+ begin
+ @logger.debug("CONNECT: upstream proxy is `#{host}:#{port}'.")
+ os = TCPSocket.new(host, port) # origin server
+
+ if proxy
+ @logger.debug("CONNECT: sending a Request-Line")
+ os << proxy_request_line << CRLF
+ @logger.debug("CONNECT: > #{proxy_request_line}")
+ if credentials
+ @logger.debug("CONNECT: sending a credentials")
+ os << "Proxy-Authorization: " << credentials << CRLF
+ end
+ os << CRLF
+ proxy_status_line = os.gets(LF)
+ @logger.debug("CONNECT: read a Status-Line form the upstream server")
+ @logger.debug("CONNECT: < #{proxy_status_line}")
+ if %r{^HTTP/\d+\.\d+\s+200\s*} =~ proxy_status_line
+ while line = os.gets(LF)
+ break if /\A(#{CRLF}|#{LF})\z/om =~ line
+ end
+ else
+ raise HTTPStatus::BadGateway
+ end
+ end
+ @logger.debug("CONNECT #{host}:#{port}: succeeded")
+ res.status = HTTPStatus::RC_OK
+ rescue => ex
+ @logger.debug("CONNECT #{host}:#{port}: failed `#{ex.message}'")
+ res.set_error(ex)
+ raise HTTPStatus::EOFError
+ ensure
+ if handler = @config[:ProxyContentHandler]
+ handler.call(req, res)
+ end
+ res.send_response(ua)
+ access_log(@config, req, res)
+
+ # Should clear request-line not to send the sesponse twice.
+ # see: HTTPServer#run
+ req.parse(NullReader) rescue nil
+ end
+
+ begin
+ while fds = IO::select([ua, os])
+ if fds[0].member?(ua)
+ buf = ua.sysread(1024);
+ @logger.debug("CONNECT: #{buf.size} byte from User-Agent")
+ os.syswrite(buf)
+ elsif fds[0].member?(os)
+ buf = os.sysread(1024);
+ @logger.debug("CONNECT: #{buf.size} byte from #{host}:#{port}")
+ ua.syswrite(buf)
+ end
+ end
+ rescue => ex
+ os.close
+ @logger.debug("CONNECT #{host}:#{port}: closed")
+ end
+
+ raise HTTPStatus::EOFError
+ end
+
+ def do_OPTIONS(req, res)
+ res['allow'] = "GET,HEAD,POST,OPTIONS,CONNECT"
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/httprequest.rb b/ruby_1_8_5/lib/webrick/httprequest.rb
new file mode 100644
index 0000000000..1d32293a27
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/httprequest.rb
@@ -0,0 +1,365 @@
+#
+# httprequest.rb -- HTTPRequest Class
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
+# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: httprequest.rb,v 1.64 2003/07/13 17:18:22 gotoyuzo Exp $
+
+require 'timeout'
+require 'uri'
+
+require 'webrick/httpversion'
+require 'webrick/httpstatus'
+require 'webrick/httputils'
+require 'webrick/cookie'
+
+module WEBrick
+
+ class HTTPRequest
+ BODY_CONTAINABLE_METHODS = [ "POST", "PUT" ]
+ BUFSIZE = 1024*4
+
+ # Request line
+ attr_reader :request_line
+ attr_reader :request_method, :unparsed_uri, :http_version
+
+ # Request-URI
+ attr_reader :request_uri, :host, :port, :path
+ attr_accessor :script_name, :path_info, :query_string
+
+ # Header and entity body
+ attr_reader :raw_header, :header, :cookies
+ attr_reader :accept, :accept_charset
+ attr_reader :accept_encoding, :accept_language
+
+ # Misc
+ attr_accessor :user
+ attr_reader :addr, :peeraddr
+ attr_reader :attributes
+ attr_reader :keep_alive
+ attr_reader :request_time
+
+ def initialize(config)
+ @config = config
+ @logger = config[:Logger]
+
+ @request_line = @request_method =
+ @unparsed_uri = @http_version = nil
+
+ @request_uri = @host = @port = @path = nil
+ @script_name = @path_info = nil
+ @query_string = nil
+ @query = nil
+ @form_data = nil
+
+ @raw_header = Array.new
+ @header = nil
+ @cookies = []
+ @accept = []
+ @accept_charset = []
+ @accept_encoding = []
+ @accept_language = []
+ @body = ""
+
+ @addr = @peeraddr = nil
+ @attributes = {}
+ @user = nil
+ @keep_alive = false
+ @request_time = nil
+
+ @remaining_size = nil
+ @socket = nil
+ end
+
+ def parse(socket=nil)
+ @socket = socket
+ begin
+ @peeraddr = socket.respond_to?(:peeraddr) ? socket.peeraddr : []
+ @addr = socket.respond_to?(:addr) ? socket.addr : []
+ rescue Errno::ENOTCONN
+ raise HTTPStatus::EOFError
+ end
+
+ read_request_line(socket)
+ if @http_version.major > 0
+ read_header(socket)
+ @header['cookie'].each{|cookie|
+ @cookies += Cookie::parse(cookie)
+ }
+ @accept = HTTPUtils.parse_qvalues(self['accept'])
+ @accept_charset = HTTPUtils.parse_qvalues(self['accept-charset'])
+ @accept_encoding = HTTPUtils.parse_qvalues(self['accept-encoding'])
+ @accept_language = HTTPUtils.parse_qvalues(self['accept-language'])
+ end
+ return if @request_method == "CONNECT"
+ return if @unparsed_uri == "*"
+
+ begin
+ @request_uri = parse_uri(@unparsed_uri)
+ @path = HTTPUtils::unescape(@request_uri.path)
+ @path = HTTPUtils::normalize_path(@path)
+ @host = @request_uri.host
+ @port = @request_uri.port
+ @query_string = @request_uri.query
+ @script_name = ""
+ @path_info = @path.dup
+ rescue
+ raise HTTPStatus::BadRequest, "bad URI `#{@unparsed_uri}'."
+ end
+
+ if /close/io =~ self["connection"]
+ @keep_alive = false
+ elsif /keep-alive/io =~ self["connection"]
+ @keep_alive = true
+ elsif @http_version < "1.1"
+ @keep_alive = false
+ else
+ @keep_alive = true
+ end
+ end
+
+ def body(&block)
+ block ||= Proc.new{|chunk| @body << chunk }
+ read_body(@socket, block)
+ @body.empty? ? nil : @body
+ end
+
+ def query
+ unless @query
+ parse_query()
+ end
+ @query
+ end
+
+ def content_length
+ return Integer(self['content-length'])
+ end
+
+ def content_type
+ return self['content-type']
+ end
+
+ def [](header_name)
+ if @header
+ value = @header[header_name.downcase]
+ value.empty? ? nil : value.join(", ")
+ end
+ end
+
+ def each
+ @header.each{|k, v|
+ value = @header[k]
+ yield(k, value.empty? ? nil : value.join(", "))
+ }
+ end
+
+ def keep_alive?
+ @keep_alive
+ end
+
+ def to_s
+ ret = @request_line.dup
+ @raw_header.each{|line| ret << line }
+ ret << CRLF
+ ret << body if body
+ ret
+ end
+
+ def fixup()
+ begin
+ body{|chunk| } # read remaining body
+ rescue HTTPStatus::Error => ex
+ @logger.error("HTTPRequest#fixup: #{ex.class} occured.")
+ @keep_alive = false
+ rescue => ex
+ @logger.error(ex)
+ @keep_alive = false
+ end
+ end
+
+ def meta_vars
+ # This method provides the metavariables defined by the revision 3
+ # of ``The WWW Common Gateway Interface Version 1.1''.
+ # (http://Web.Golux.Com/coar/cgi/)
+
+ meta = Hash.new
+
+ cl = self["Content-Length"]
+ ct = self["Content-Type"]
+ meta["CONTENT_LENGTH"] = cl if cl.to_i > 0
+ meta["CONTENT_TYPE"] = ct.dup if ct
+ meta["GATEWAY_INTERFACE"] = "CGI/1.1"
+ meta["PATH_INFO"] = @path_info ? @path_info.dup : ""
+ #meta["PATH_TRANSLATED"] = nil # no plan to be provided
+ meta["QUERY_STRING"] = @query_string ? @query_string.dup : ""
+ meta["REMOTE_ADDR"] = @peeraddr[3]
+ meta["REMOTE_HOST"] = @peeraddr[2]
+ #meta["REMOTE_IDENT"] = nil # no plan to be provided
+ meta["REMOTE_USER"] = @user
+ meta["REQUEST_METHOD"] = @request_method.dup
+ meta["REQUEST_URI"] = @request_uri.to_s
+ meta["SCRIPT_NAME"] = @script_name.dup
+ meta["SERVER_NAME"] = @host
+ meta["SERVER_PORT"] = @port.to_s
+ meta["SERVER_PROTOCOL"] = "HTTP/" + @config[:HTTPVersion].to_s
+ meta["SERVER_SOFTWARE"] = @config[:ServerSoftware].dup
+
+ self.each{|key, val|
+ next if /^content-type$/i =~ key
+ next if /^content-length$/i =~ key
+ name = "HTTP_" + key
+ name.gsub!(/-/o, "_")
+ name.upcase!
+ meta[name] = val
+ }
+
+ meta
+ end
+
+ private
+
+ def read_request_line(socket)
+ @request_line = read_line(socket) if socket
+ @request_time = Time.now
+ raise HTTPStatus::EOFError unless @request_line
+ if /^(\S+)\s+(\S+)(?:\s+HTTP\/(\d+\.\d+))?\r?\n/mo =~ @request_line
+ @request_method = $1
+ @unparsed_uri = $2
+ @http_version = HTTPVersion.new($3 ? $3 : "0.9")
+ else
+ rl = @request_line.sub(/\x0d?\x0a\z/o, '')
+ raise HTTPStatus::BadRequest, "bad Request-Line `#{rl}'."
+ end
+ end
+
+ def read_header(socket)
+ if socket
+ while line = read_line(socket)
+ break if /\A(#{CRLF}|#{LF})\z/om =~ line
+ @raw_header << line
+ end
+ end
+ begin
+ @header = HTTPUtils::parse_header(@raw_header)
+ rescue => ex
+ raise HTTPStatus::BadRequest, ex.message
+ end
+ end
+
+ def parse_uri(str, scheme="http")
+ if @config[:Escape8bitURI]
+ str = HTTPUtils::escape8bit(str)
+ end
+ uri = URI::parse(str)
+ return uri if uri.absolute?
+ if self["host"]
+ pattern = /\A(#{URI::REGEXP::PATTERN::HOST})(?::(\d+))?\z/n
+ host, port = *self['host'].scan(pattern)[0]
+ elsif @addr.size > 0
+ host, port = @addr[2], @addr[1]
+ else
+ host, port = @config[:ServerName], @config[:Port]
+ end
+ uri.scheme = scheme
+ uri.host = host
+ uri.port = port ? port.to_i : nil
+ return URI::parse(uri.to_s)
+ end
+
+ def read_body(socket, block)
+ return unless socket
+ if tc = self['transfer-encoding']
+ case tc
+ when /chunked/io then read_chunked(socket, block)
+ else raise HTTPStatus::NotImplemented, "Transfer-Encoding: #{tc}."
+ end
+ elsif self['content-length'] || @remaining_size
+ @remaining_size ||= self['content-length'].to_i
+ while @remaining_size > 0
+ sz = BUFSIZE < @remaining_size ? BUFSIZE : @remaining_size
+ break unless buf = read_data(socket, sz)
+ @remaining_size -= buf.size
+ block.call(buf)
+ end
+ if @remaining_size > 0 && @socket.eof?
+ raise HTTPStatus::BadRequest, "invalid body size."
+ end
+ elsif BODY_CONTAINABLE_METHODS.member?(@request_method)
+ raise HTTPStatus::LengthRequired
+ end
+ return @body
+ end
+
+ def read_chunk_size(socket)
+ line = read_line(socket)
+ if /^([0-9a-fA-F]+)(?:;(\S+))?/ =~ line
+ chunk_size = $1.hex
+ chunk_ext = $2
+ [ chunk_size, chunk_ext ]
+ else
+ raise HTTPStatus::BadRequest, "bad chunk `#{line}'."
+ end
+ end
+
+ def read_chunked(socket, block)
+ chunk_size, = read_chunk_size(socket)
+ while chunk_size > 0
+ data = ""
+ while data.size < chunk_size
+ tmp = read_data(socket, chunk_size-data.size) # read chunk-data
+ break unless tmp
+ data << tmp
+ end
+ if data.nil? || data.size != chunk_size
+ raise BadRequest, "bad chunk data size."
+ end
+ read_line(socket) # skip CRLF
+ block.call(data)
+ chunk_size, = read_chunk_size(socket)
+ end
+ read_header(socket) # trailer + CRLF
+ @header.delete("transfer-encoding")
+ @remaining_size = 0
+ end
+
+ def _read_data(io, method, arg)
+ begin
+ timeout(@config[:RequestTimeout]){
+ return io.__send__(method, arg)
+ }
+ rescue Errno::ECONNRESET
+ return nil
+ rescue TimeoutError
+ raise HTTPStatus::RequestTimeout
+ end
+ end
+
+ def read_line(io)
+ _read_data(io, :gets, LF)
+ end
+
+ def read_data(io, size)
+ _read_data(io, :read, size)
+ end
+
+ def parse_query()
+ begin
+ if @request_method == "GET" || @request_method == "HEAD"
+ @query = HTTPUtils::parse_query(@query_string)
+ elsif self['content-type'] =~ /^application\/x-www-form-urlencoded/
+ @query = HTTPUtils::parse_query(body)
+ elsif self['content-type'] =~ /^multipart\/form-data; boundary=(.+)/
+ boundary = HTTPUtils::dequote($1)
+ @query = HTTPUtils::parse_form_data(body, boundary)
+ else
+ @query = Hash.new
+ end
+ rescue => ex
+ raise HTTPStatus::BadRequest, ex.message
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/httpresponse.rb b/ruby_1_8_5/lib/webrick/httpresponse.rb
new file mode 100644
index 0000000000..d0f232d1e1
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/httpresponse.rb
@@ -0,0 +1,327 @@
+#
+# httpresponse.rb -- HTTPResponse Class
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
+# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: httpresponse.rb,v 1.45 2003/07/11 11:02:25 gotoyuzo Exp $
+
+require 'time'
+require 'webrick/httpversion'
+require 'webrick/htmlutils'
+require 'webrick/httputils'
+require 'webrick/httpstatus'
+
+module WEBrick
+ class HTTPResponse
+ BUFSIZE = 1024*4
+
+ attr_reader :http_version, :status, :header
+ attr_reader :cookies
+ attr_accessor :reason_phrase
+ attr_accessor :body
+
+ attr_accessor :request_method, :request_uri, :request_http_version
+ attr_accessor :filename
+ attr_accessor :keep_alive
+ attr_reader :config, :sent_size
+
+ def initialize(config)
+ @config = config
+ @logger = config[:Logger]
+ @header = Hash.new
+ @status = HTTPStatus::RC_OK
+ @reason_phrase = nil
+ @http_version = HTTPVersion::convert(@config[:HTTPVersion])
+ @body = ''
+ @keep_alive = true
+ @cookies = []
+ @request_method = nil
+ @request_uri = nil
+ @request_http_version = @http_version # temporary
+ @chunked = false
+ @filename = nil
+ @sent_size = 0
+ end
+
+ def status_line
+ "HTTP/#@http_version #@status #@reason_phrase #{CRLF}"
+ end
+
+ def status=(status)
+ @status = status
+ @reason_phrase = HTTPStatus::reason_phrase(status)
+ end
+
+ def [](field)
+ @header[field.downcase]
+ end
+
+ def []=(field, value)
+ @header[field.downcase] = value.to_s
+ end
+
+ def content_length
+ if len = self['content-length']
+ return Integer(len)
+ end
+ end
+
+ def content_length=(len)
+ self['content-length'] = len.to_s
+ end
+
+ def content_type
+ self['content-type']
+ end
+
+ def content_type=(type)
+ self['content-type'] = type
+ end
+
+ def each
+ @header.each{|k, v| yield(k, v) }
+ end
+
+ def chunked?
+ @chunked
+ end
+
+ def chunked=(val)
+ @chunked = val ? true : false
+ end
+
+ def keep_alive?
+ @keep_alive
+ end
+
+ def send_response(socket)
+ begin
+ setup_header()
+ send_header(socket)
+ send_body(socket)
+ rescue Errno::EPIPE, Errno::ECONNRESET, Errno::ENOTCONN => ex
+ @logger.debug(ex)
+ @keep_alive = false
+ rescue Exception => ex
+ @logger.error(ex)
+ @keep_alive = false
+ end
+ end
+
+ def setup_header()
+ @reason_phrase ||= HTTPStatus::reason_phrase(@status)
+ @header['server'] ||= @config[:ServerSoftware]
+ @header['date'] ||= Time.now.httpdate
+
+ # HTTP/0.9 features
+ if @request_http_version < "1.0"
+ @http_version = HTTPVersion.new("0.9")
+ @keep_alive = false
+ end
+
+ # HTTP/1.0 features
+ if @request_http_version < "1.1"
+ if chunked?
+ @chunked = false
+ ver = @request_http_version.to_s
+ msg = "chunked is set for an HTTP/#{ver} request. (ignored)"
+ @logger.warn(msg)
+ end
+ end
+
+ # Determin the message length (RFC2616 -- 4.4 Message Length)
+ if @status == 304 || @status == 204 || HTTPStatus::info?(@status)
+ @header.delete('content-length')
+ @body = ""
+ elsif chunked?
+ @header["transfer-encoding"] = "chunked"
+ @header.delete('content-length')
+ elsif %r{^multipart/byteranges} =~ @header['content-type']
+ @header.delete('content-length')
+ elsif @header['content-length'].nil?
+ unless @body.is_a?(IO)
+ @header['content-length'] = @body ? @body.size : 0
+ end
+ end
+
+ # Keep-Alive connection.
+ if @header['connection'] == "close"
+ @keep_alive = false
+ elsif keep_alive?
+ if chunked? || @header['content-length']
+ @header['connection'] = "Keep-Alive"
+ end
+ else
+ @header['connection'] = "close"
+ end
+
+ # Location is a single absoluteURI.
+ if location = @header['location']
+ if @request_uri
+ @header['location'] = @request_uri.merge(location)
+ end
+ end
+ end
+
+ def send_header(socket)
+ if @http_version.major > 0
+ data = status_line()
+ @header.each{|key, value|
+ tmp = key.gsub(/\bwww|^te$|\b\w/){|s| s.upcase }
+ data << "#{tmp}: #{value}" << CRLF
+ }
+ @cookies.each{|cookie|
+ data << "Set-Cookie: " << cookie.to_s << CRLF
+ }
+ data << CRLF
+ _write_data(socket, data)
+ end
+ end
+
+ def send_body(socket)
+ case @body
+ when IO then send_body_io(socket)
+ else send_body_string(socket)
+ end
+ end
+
+ def to_s
+ ret = ""
+ send_response(ret)
+ ret
+ end
+
+ def set_redirect(status, url)
+ @body = "<HTML><A HREF=\"#{url.to_s}\">#{url.to_s}</A>.</HTML>\n"
+ @header['location'] = url.to_s
+ raise status
+ end
+
+ def set_error(ex, backtrace=false)
+ case ex
+ when HTTPStatus::Status
+ @keep_alive = false if HTTPStatus::error?(ex.code)
+ self.status = ex.code
+ else
+ @keep_alive = false
+ self.status = HTTPStatus::RC_INTERNAL_SERVER_ERROR
+ end
+ @header['content-type'] = "text/html"
+
+ if respond_to?(:create_error_page)
+ create_error_page()
+ return
+ end
+
+ if @request_uri
+ host, port = @request_uri.host, @request_uri.port
+ else
+ host, port = @config[:ServerName], @config[:Port]
+ end
+
+ @body = ''
+ @body << <<-_end_of_html_
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
+<HTML>
+ <HEAD><TITLE>#{HTMLUtils::escape(@reason_phrase)}</TITLE></HEAD>
+ <BODY>
+ <H1>#{HTMLUtils::escape(@reason_phrase)}</H1>
+ #{HTMLUtils::escape(ex.message)}
+ <HR>
+ _end_of_html_
+
+ if backtrace && $DEBUG
+ @body << "backtrace of `#{HTMLUtils::escape(ex.class.to_s)}' "
+ @body << "#{HTMLUtils::escape(ex.message)}"
+ @body << "<PRE>"
+ ex.backtrace.each{|line| @body << "\t#{line}\n"}
+ @body << "</PRE><HR>"
+ end
+
+ @body << <<-_end_of_html_
+ <ADDRESS>
+ #{HTMLUtils::escape(@config[:ServerSoftware])} at
+ #{host}:#{port}
+ </ADDRESS>
+ </BODY>
+</HTML>
+ _end_of_html_
+ end
+
+ private
+
+ def send_body_io(socket)
+ begin
+ if @request_method == "HEAD"
+ # do nothing
+ elsif chunked?
+ while buf = @body.read(BUFSIZE)
+ next if buf.empty?
+ data = ""
+ data << format("%x", buf.size) << CRLF
+ data << buf << CRLF
+ _write_data(socket, data)
+ @sent_size += buf.size
+ end
+ _write_data(socket, "0#{CRLF}#{CRLF}")
+ else
+ size = @header['content-length'].to_i
+ _send_file(socket, @body, 0, size)
+ @sent_size = size
+ end
+ ensure
+ @body.close
+ end
+ end
+
+ def send_body_string(socket)
+ if @request_method == "HEAD"
+ # do nothing
+ elsif chunked?
+ remain = body ? @body.size : 0
+ while buf = @body[@sent_size, BUFSIZE]
+ break if buf.empty?
+ data = ""
+ data << format("%x", buf.size) << CRLF
+ data << buf << CRLF
+ _write_data(socket, data)
+ @sent_size += buf.size
+ end
+ _write_data(socket, "0#{CRLF}#{CRLF}")
+ else
+ if @body && @body.size > 0
+ _write_data(socket, @body)
+ @sent_size = @body.size
+ end
+ end
+ end
+
+ def _send_file(output, input, offset, size)
+ while offset > 0
+ sz = BUFSIZE < offset ? BUFSIZE : offset
+ buf = input.read(sz)
+ offset -= buf.size
+ end
+
+ if size == 0
+ while buf = input.read(BUFSIZE)
+ _write_data(output, buf)
+ end
+ else
+ while size > 0
+ sz = BUFSIZE < size ? BUFSIZE : size
+ buf = input.read(sz)
+ _write_data(output, buf)
+ size -= buf.size
+ end
+ end
+ end
+
+ def _write_data(socket, data)
+ socket << data
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/https.rb b/ruby_1_8_5/lib/webrick/https.rb
new file mode 100644
index 0000000000..81b65ce803
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/https.rb
@@ -0,0 +1,63 @@
+#
+# https.rb -- SSL/TLS enhancement for HTTPServer
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2001 GOTOU Yuuzou
+# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: https.rb,v 1.15 2003/07/22 19:20:42 gotoyuzo Exp $
+
+require 'webrick/ssl'
+
+module WEBrick
+ module Config
+ HTTP.update(SSL)
+ end
+
+ class HTTPRequest
+ attr_reader :cipher, :server_cert, :client_cert
+
+ alias orig_parse parse
+
+ def parse(socket=nil)
+ if socket.respond_to?(:cert)
+ @server_cert = socket.cert || @config[:SSLCertificate]
+ @client_cert = socket.peer_cert
+ @client_cert_chain = socket.peer_cert_chain
+ @cipher = socket.cipher
+ end
+ orig_parse(socket)
+ end
+
+ alias orig_parse_uri parse_uri
+
+ def parse_uri(str, scheme="https")
+ if @server_cert
+ return orig_parse_uri(str, scheme)
+ end
+ return orig_parse_uri(str)
+ end
+
+ alias orig_meta_vars meta_vars
+
+ def meta_vars
+ meta = orig_meta_vars
+ if @server_cert
+ meta["HTTPS"] = "on"
+ meta["SSL_SERVER_CERT"] = @server_cert.to_pem
+ meta["SSL_CLIENT_CERT"] = @client_cert ? @client_cert.to_pem : ""
+ if @client_cert_chain
+ @client_cert_chain.each_with_index{|cert, i|
+ meta["SSL_CLIENT_CERT_CHAIN_#{i}"] = cert.to_pem
+ }
+ end
+ meta["SSL_CIPHER"] = @cipher[0]
+ meta["SSL_PROTOCOL"] = @cipher[1]
+ meta["SSL_CIPHER_USEKEYSIZE"] = @cipher[2].to_s
+ meta["SSL_CIPHER_ALGKEYSIZE"] = @cipher[3].to_s
+ end
+ meta
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/httpserver.rb b/ruby_1_8_5/lib/webrick/httpserver.rb
new file mode 100644
index 0000000000..9edb0018f8
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/httpserver.rb
@@ -0,0 +1,210 @@
+#
+# httpserver.rb -- HTTPServer Class
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
+# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: httpserver.rb,v 1.63 2002/10/01 17:16:32 gotoyuzo Exp $
+
+require 'webrick/server'
+require 'webrick/httputils'
+require 'webrick/httpstatus'
+require 'webrick/httprequest'
+require 'webrick/httpresponse'
+require 'webrick/httpservlet'
+require 'webrick/accesslog'
+
+module WEBrick
+ class HTTPServerError < ServerError; end
+
+ class HTTPServer < ::WEBrick::GenericServer
+ def initialize(config={}, default=Config::HTTP)
+ super
+ @http_version = HTTPVersion::convert(@config[:HTTPVersion])
+
+ @mount_tab = MountTable.new
+ if @config[:DocumentRoot]
+ mount("/", HTTPServlet::FileHandler, @config[:DocumentRoot],
+ @config[:DocumentRootOptions])
+ end
+
+ unless @config[:AccessLog]
+ @config[:AccessLog] = [
+ [ $stderr, AccessLog::COMMON_LOG_FORMAT ],
+ [ $stderr, AccessLog::REFERER_LOG_FORMAT ]
+ ]
+ end
+
+ @virtual_hosts = Array.new
+ end
+
+ def run(sock)
+ while true
+ res = HTTPResponse.new(@config)
+ req = HTTPRequest.new(@config)
+ server = self
+ begin
+ timeout = @config[:RequestTimeout]
+ while timeout > 0
+ break if IO.select([sock], nil, nil, 0.5)
+ timeout = 0 if @status != :Running
+ timeout -= 0.5
+ end
+ raise HTTPStatus::EOFError if timeout <= 0 || sock.eof?
+ req.parse(sock)
+ res.request_method = req.request_method
+ res.request_uri = req.request_uri
+ res.request_http_version = req.http_version
+ res.keep_alive = req.keep_alive?
+ server = lookup_server(req) || self
+ if callback = server[:RequestCallback] || server[:RequestHandler]
+ callback.call(req, res)
+ end
+ server.service(req, res)
+ rescue HTTPStatus::EOFError, HTTPStatus::RequestTimeout => ex
+ res.set_error(ex)
+ rescue HTTPStatus::Error => ex
+ @logger.error(ex.message)
+ res.set_error(ex)
+ rescue HTTPStatus::Status => ex
+ res.status = ex.code
+ rescue StandardError => ex
+ @logger.error(ex)
+ res.set_error(ex, true)
+ ensure
+ if req.request_line
+ req.fixup()
+ res.send_response(sock)
+ server.access_log(@config, req, res)
+ end
+ end
+ break if @http_version < "1.1"
+ break unless req.keep_alive?
+ break unless res.keep_alive?
+ end
+ end
+
+ def service(req, res)
+ if req.unparsed_uri == "*"
+ if req.request_method == "OPTIONS"
+ do_OPTIONS(req, res)
+ raise HTTPStatus::OK
+ end
+ raise HTTPStatus::NotFound, "`#{req.unparsed_uri}' not found."
+ end
+
+ servlet, options, script_name, path_info = search_servlet(req.path)
+ raise HTTPStatus::NotFound, "`#{req.path}' not found." unless servlet
+ req.script_name = script_name
+ req.path_info = path_info
+ si = servlet.get_instance(self, *options)
+ @logger.debug(format("%s is invoked.", si.class.name))
+ si.service(req, res)
+ end
+
+ def do_OPTIONS(req, res)
+ res["allow"] = "GET,HEAD,POST,OPTIONS"
+ end
+
+ def mount(dir, servlet, *options)
+ @logger.debug(sprintf("%s is mounted on %s.", servlet.inspect, dir))
+ @mount_tab[dir] = [ servlet, options ]
+ end
+
+ def mount_proc(dir, proc=nil, &block)
+ proc ||= block
+ raise HTTPServerError, "must pass a proc or block" unless proc
+ mount(dir, HTTPServlet::ProcHandler.new(proc))
+ end
+
+ def unmount(dir)
+ @logger.debug(sprintf("unmount %s.", dir))
+ @mount_tab.delete(dir)
+ end
+ alias umount unmount
+
+ def search_servlet(path)
+ script_name, path_info = @mount_tab.scan(path)
+ servlet, options = @mount_tab[script_name]
+ if servlet
+ [ servlet, options, script_name, path_info ]
+ end
+ end
+
+ def virtual_host(server)
+ @virtual_hosts << server
+ @virtual_hosts = @virtual_hosts.sort_by{|s|
+ num = 0
+ num -= 4 if s[:BindAddress]
+ num -= 2 if s[:Port]
+ num -= 1 if s[:ServerName]
+ num
+ }
+ end
+
+ def lookup_server(req)
+ @virtual_hosts.find{|s|
+ (s[:BindAddress].nil? || req.addr[3] == s[:BindAddress]) &&
+ (s[:Port].nil? || req.port == s[:Port]) &&
+ ((s[:ServerName].nil? || req.host == s[:ServerName]) ||
+ (!s[:ServerAlias].nil? && s[:ServerAlias].find{|h| h === req.host}))
+ }
+ end
+
+ def access_log(config, req, res)
+ param = AccessLog::setup_params(config, req, res)
+ @config[:AccessLog].each{|logger, fmt|
+ logger << AccessLog::format(fmt+"\n", param)
+ }
+ end
+
+ class MountTable
+ def initialize
+ @tab = Hash.new
+ compile
+ end
+
+ def [](dir)
+ dir = normalize(dir)
+ @tab[dir]
+ end
+
+ def []=(dir, val)
+ dir = normalize(dir)
+ @tab[dir] = val
+ compile
+ val
+ end
+
+ def delete(dir)
+ dir = normalize(dir)
+ res = @tab.delete(dir)
+ compile
+ res
+ end
+
+ def scan(path)
+ @scanner =~ path
+ [ $&, $' ]
+ end
+
+ private
+
+ def compile
+ k = @tab.keys
+ k.sort!
+ k.reverse!
+ k.collect!{|path| Regexp.escape(path) }
+ @scanner = Regexp.new("^(" + k.join("|") +")(?=/|$)")
+ end
+
+ def normalize(dir)
+ ret = dir ? dir.dup : ""
+ ret.sub!(%r|/+$|, "")
+ ret
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/httpservlet.rb b/ruby_1_8_5/lib/webrick/httpservlet.rb
new file mode 100644
index 0000000000..ac7c022bd7
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/httpservlet.rb
@@ -0,0 +1,22 @@
+#
+# httpservlet.rb -- HTTPServlet Utility File
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
+# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: httpservlet.rb,v 1.21 2003/02/23 12:24:46 gotoyuzo Exp $
+
+require 'webrick/httpservlet/abstract'
+require 'webrick/httpservlet/filehandler'
+require 'webrick/httpservlet/cgihandler'
+require 'webrick/httpservlet/erbhandler'
+require 'webrick/httpservlet/prochandler'
+
+module WEBrick
+ module HTTPServlet
+ FileHandler.add_handler("cgi", CGIHandler)
+ FileHandler.add_handler("rhtml", ERBHandler)
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/httpservlet/abstract.rb b/ruby_1_8_5/lib/webrick/httpservlet/abstract.rb
new file mode 100644
index 0000000000..03861e8fc7
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/httpservlet/abstract.rb
@@ -0,0 +1,71 @@
+#
+# httpservlet.rb -- HTTPServlet Module
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2000 TAKAHASHI Masayoshi, GOTOU Yuuzou
+# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: abstract.rb,v 1.24 2003/07/11 11:16:46 gotoyuzo Exp $
+
+require 'thread'
+
+require 'webrick/htmlutils'
+require 'webrick/httputils'
+require 'webrick/httpstatus'
+
+module WEBrick
+ module HTTPServlet
+ class HTTPServletError < StandardError; end
+
+ class AbstractServlet
+ def self.get_instance(config, *options)
+ self.new(config, *options)
+ end
+
+ def initialize(server, *options)
+ @server = @config = server
+ @logger = @server[:Logger]
+ @options = options
+ end
+
+ def service(req, res)
+ method_name = "do_" + req.request_method.gsub(/-/, "_")
+ if respond_to?(method_name)
+ __send__(method_name, req, res)
+ else
+ raise HTTPStatus::MethodNotAllowed,
+ "unsupported method `#{req.request_method}'."
+ end
+ end
+
+ def do_GET(req, res)
+ raise HTTPStatus::NotFound, "not found."
+ end
+
+ def do_HEAD(req, res)
+ do_GET(req, res)
+ end
+
+ def do_OPTIONS(req, res)
+ m = self.methods.grep(/^do_[A-Z]+$/)
+ m.collect!{|i| i.sub(/do_/, "") }
+ m.sort!
+ res["allow"] = m.join(",")
+ end
+
+ private
+
+ def redirect_to_directory_uri(req, res)
+ if req.path[-1] != ?/
+ location = req.path + "/"
+ if req.query_string && req.query_string.size > 0
+ location << "?" << req.query_string
+ end
+ res.set_redirect(HTTPStatus::MovedPermanently, location)
+ end
+ end
+ end
+
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/httpservlet/cgi_runner.rb b/ruby_1_8_5/lib/webrick/httpservlet/cgi_runner.rb
new file mode 100644
index 0000000000..1069a68d58
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/httpservlet/cgi_runner.rb
@@ -0,0 +1,45 @@
+#
+# cgi_runner.rb -- CGI launcher.
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2000 TAKAHASHI Masayoshi, GOTOU YUUZOU
+# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: cgi_runner.rb,v 1.9 2002/09/25 11:33:15 gotoyuzo Exp $
+
+def sysread(io, size)
+ buf = ""
+ while size > 0
+ tmp = io.sysread(size)
+ buf << tmp
+ size -= tmp.size
+ end
+ return buf
+end
+
+STDIN.binmode
+
+buf = ""
+len = sysread(STDIN, 8).to_i
+out = sysread(STDIN, len)
+STDOUT.reopen(open(out, "w"))
+
+len = sysread(STDIN, 8).to_i
+err = sysread(STDIN, len)
+STDERR.reopen(open(err, "w"))
+
+len = sysread(STDIN, 8).to_i
+dump = sysread(STDIN, len)
+hash = Marshal.restore(dump)
+ENV.keys.each{|name| ENV.delete(name) }
+hash.each{|k, v| ENV[k] = v if v }
+
+dir = File::dirname(ENV["SCRIPT_FILENAME"])
+Dir::chdir dir
+
+if interpreter = ARGV[0]
+ exec(interpreter, ENV["SCRIPT_FILENAME"])
+ # NOTREACHED
+end
+exec ENV["SCRIPT_FILENAME"]
diff --git a/ruby_1_8_5/lib/webrick/httpservlet/cgihandler.rb b/ruby_1_8_5/lib/webrick/httpservlet/cgihandler.rb
new file mode 100644
index 0000000000..a35b59edb8
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/httpservlet/cgihandler.rb
@@ -0,0 +1,104 @@
+#
+# cgihandler.rb -- CGIHandler Class
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
+# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: cgihandler.rb,v 1.27 2003/03/21 19:56:01 gotoyuzo Exp $
+
+require 'rbconfig'
+require 'tempfile'
+require 'webrick/config'
+require 'webrick/httpservlet/abstract'
+
+module WEBrick
+ module HTTPServlet
+
+ class CGIHandler < AbstractServlet
+ Ruby = File::join(::Config::CONFIG['bindir'],
+ ::Config::CONFIG['ruby_install_name'])
+ Ruby << ::Config::CONFIG['EXEEXT']
+ CGIRunner = "\"#{Ruby}\" \"#{Config::LIBDIR}/httpservlet/cgi_runner.rb\""
+
+ def initialize(server, name)
+ super
+ @script_filename = name
+ @tempdir = server[:TempDir]
+ @cgicmd = "#{CGIRunner} #{server[:CGIInterpreter]}"
+ end
+
+ def do_GET(req, res)
+ data = nil
+ status = -1
+
+ cgi_in = IO::popen(@cgicmd, "wb")
+ cgi_out = Tempfile.new("webrick.cgiout.", @tempdir)
+ cgi_err = Tempfile.new("webrick.cgierr.", @tempdir)
+ begin
+ cgi_in.sync = true
+ meta = req.meta_vars
+ meta["SCRIPT_FILENAME"] = @script_filename
+ meta["PATH"] = @config[:CGIPathEnv]
+ if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
+ meta["SystemRoot"] = ENV["SystemRoot"]
+ end
+ dump = Marshal.dump(meta)
+
+ cgi_in.write("%8d" % cgi_out.path.size)
+ cgi_in.write(cgi_out.path)
+ cgi_in.write("%8d" % cgi_err.path.size)
+ cgi_in.write(cgi_err.path)
+ cgi_in.write("%8d" % dump.size)
+ cgi_in.write(dump)
+
+ if req.body and req.body.size > 0
+ cgi_in.write(req.body)
+ end
+ ensure
+ cgi_in.close
+ status = $?.exitstatus
+ sleep 0.1 if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
+ data = cgi_out.read
+ cgi_out.close(true)
+ if errmsg = cgi_err.read
+ if errmsg.size > 0
+ @logger.error("CGIHandler: #{@script_filename}:\n" + errmsg)
+ end
+ end
+ cgi_err.close(true)
+ end
+
+ if status != 0
+ @logger.error("CGIHandler: #{@script_filename} exit with #{status}")
+ end
+
+ data = "" unless data
+ raw_header, body = data.split(/^[\xd\xa]+/on, 2)
+ raise HTTPStatus::InternalServerError,
+ "Premature end of script headers: #{@script_filename}" if body.nil?
+
+ begin
+ header = HTTPUtils::parse_header(raw_header)
+ if /^(\d+)/ =~ header['status'][0]
+ res.status = $1.to_i
+ header.delete('status')
+ end
+ if header.has_key?('set-cookie')
+ header['set-cookie'].each{|k|
+ res.cookies << Cookie.parse_set_cookie(k)
+ }
+ header.delete('set-cookie')
+ end
+ header.each{|key, val| res[key] = val.join(", ") }
+ rescue => ex
+ raise HTTPStatus::InternalServerError, ex.message
+ end
+ res.body = body
+ end
+ alias do_POST do_GET
+ end
+
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/httpservlet/erbhandler.rb b/ruby_1_8_5/lib/webrick/httpservlet/erbhandler.rb
new file mode 100644
index 0000000000..b9d5e65b65
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/httpservlet/erbhandler.rb
@@ -0,0 +1,54 @@
+#
+# erbhandler.rb -- ERBHandler Class
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
+# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: erbhandler.rb,v 1.25 2003/02/24 19:25:31 gotoyuzo Exp $
+
+require 'webrick/httpservlet/abstract.rb'
+
+require 'erb'
+
+module WEBrick
+ module HTTPServlet
+
+ class ERBHandler < AbstractServlet
+ def initialize(server, name)
+ super
+ @script_filename = name
+ end
+
+ def do_GET(req, res)
+ unless defined?(ERB)
+ @logger.warn "#{self.class}: ERB not defined."
+ raise HTTPStatus::Forbidden, "ERBHandler cannot work."
+ end
+ begin
+ data = open(@script_filename){|io| io.read }
+ res.body = evaluate(ERB.new(data), req, res)
+ res['content-type'] =
+ HTTPUtils::mime_type(@script_filename, @config[:MimeTypes])
+ rescue StandardError => ex
+ raise
+ rescue Exception => ex
+ @logger.error(ex)
+ raise HTTPStatus::InternalServerError, ex.message
+ end
+ end
+
+ alias do_POST do_GET
+
+ private
+ def evaluate(erb, servlet_request, servlet_response)
+ Module.new.module_eval{
+ meta_vars = servlet_request.meta_vars
+ query = servlet_request.query
+ erb.result(binding)
+ }
+ end
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/httpservlet/filehandler.rb b/ruby_1_8_5/lib/webrick/httpservlet/filehandler.rb
new file mode 100644
index 0000000000..410cc6f9a9
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/httpservlet/filehandler.rb
@@ -0,0 +1,398 @@
+#
+# filehandler.rb -- FileHandler Module
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
+# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: filehandler.rb,v 1.44 2003/06/07 01:34:51 gotoyuzo Exp $
+
+require 'thread'
+require 'time'
+
+require 'webrick/htmlutils'
+require 'webrick/httputils'
+require 'webrick/httpstatus'
+
+module WEBrick
+ module HTTPServlet
+
+ class DefaultFileHandler < AbstractServlet
+ def initialize(server, local_path)
+ super
+ @local_path = local_path
+ end
+
+ def do_GET(req, res)
+ st = File::stat(@local_path)
+ mtime = st.mtime
+ res['etag'] = sprintf("%x-%x-%x", st.ino, st.size, st.mtime.to_i)
+
+ if not_modified?(req, res, mtime, res['etag'])
+ res.body = ''
+ raise HTTPStatus::NotModified
+ elsif req['range']
+ make_partial_content(req, res, @local_path, st.size)
+ raise HTTPStatus::PartialContent
+ else
+ mtype = HTTPUtils::mime_type(@local_path, @config[:MimeTypes])
+ res['content-type'] = mtype
+ res['content-length'] = st.size
+ res['last-modified'] = mtime.httpdate
+ res.body = open(@local_path, "rb")
+ end
+ end
+
+ def not_modified?(req, res, mtime, etag)
+ if ir = req['if-range']
+ begin
+ if Time.httpdate(ir) >= mtime
+ return true
+ end
+ rescue
+ if HTTPUtils::split_header_value(ir).member?(res['etag'])
+ return true
+ end
+ end
+ end
+
+ if (ims = req['if-modified-since']) && Time.parse(ims) >= mtime
+ return true
+ end
+
+ if (inm = req['if-none-match']) &&
+ HTTPUtils::split_header_value(inm).member?(res['etag'])
+ return true
+ end
+
+ return false
+ end
+
+ def make_partial_content(req, res, filename, filesize)
+ mtype = HTTPUtils::mime_type(filename, @config[:MimeTypes])
+ unless ranges = HTTPUtils::parse_range_header(req['range'])
+ raise HTTPStatus::BadRequest,
+ "Unrecognized range-spec: \"#{req['range']}\""
+ end
+ open(filename, "rb"){|io|
+ if ranges.size > 1
+ time = Time.now
+ boundary = "#{time.sec}_#{time.usec}_#{Process::pid}"
+ body = ''
+ ranges.each{|range|
+ first, last = prepare_range(range, filesize)
+ next if first < 0
+ io.pos = first
+ content = io.read(last-first+1)
+ body << "--" << boundary << CRLF
+ body << "Content-Type: #{mtype}" << CRLF
+ body << "Content-Range: #{first}-#{last}/#{filesize}" << CRLF
+ body << CRLF
+ body << content
+ body << CRLF
+ }
+ raise HTTPStatus::RequestRangeNotSatisfiable if body.empty?
+ body << "--" << boundary << "--" << CRLF
+ res["content-type"] = "multipart/byteranges; boundary=#{boundary}"
+ res.body = body
+ elsif range = ranges[0]
+ first, last = prepare_range(range, filesize)
+ raise HTTPStatus::RequestRangeNotSatisfiable if first < 0
+ if last == filesize - 1
+ content = io.dup
+ content.pos = first
+ else
+ io.pos = first
+ content = io.read(last-first+1)
+ end
+ res['content-type'] = mtype
+ res['content-range'] = "#{first}-#{last}/#{filesize}"
+ res['content-length'] = last - first + 1
+ res.body = content
+ else
+ raise HTTPStatus::BadRequest
+ end
+ }
+ end
+
+ def prepare_range(range, filesize)
+ first = range.first < 0 ? filesize + range.first : range.first
+ return -1, -1 if first < 0 || first >= filesize
+ last = range.last < 0 ? filesize + range.last : range.last
+ last = filesize - 1 if last >= filesize
+ return first, last
+ end
+ end
+
+ class FileHandler < AbstractServlet
+ HandlerTable = Hash.new
+
+ def self.add_handler(suffix, handler)
+ HandlerTable[suffix] = handler
+ end
+
+ def self.remove_handler(suffix)
+ HandlerTable.delete(suffix)
+ end
+
+ def initialize(server, root, options={}, default=Config::FileHandler)
+ @config = server.config
+ @logger = @config[:Logger]
+ @root = File.expand_path(root)
+ if options == true || options == false
+ options = { :FancyIndexing => options }
+ end
+ @options = default.dup.update(options)
+ end
+
+ def service(req, res)
+ # if this class is mounted on "/" and /~username is requested.
+ # we're going to override path informations before invoking service.
+ if defined?(Etc) && @options[:UserDir] && req.script_name.empty?
+ if %r|^(/~([^/]+))| =~ req.path_info
+ script_name, user = $1, $2
+ path_info = $'
+ begin
+ passwd = Etc::getpwnam(user)
+ @root = File::join(passwd.dir, @options[:UserDir])
+ req.script_name = script_name
+ req.path_info = path_info
+ rescue
+ @logger.debug "#{self.class}#do_GET: getpwnam(#{user}) failed"
+ end
+ end
+ end
+ super(req, res)
+ end
+
+ def do_GET(req, res)
+ unless exec_handler(req, res)
+ set_dir_list(req, res)
+ end
+ end
+
+ def do_POST(req, res)
+ unless exec_handler(req, res)
+ raise HTTPStatus::NotFound, "`#{req.path}' not found."
+ end
+ end
+
+ def do_OPTIONS(req, res)
+ unless exec_handler(req, res)
+ super(req, res)
+ end
+ end
+
+ # ToDo
+ # RFC2518: HTTP Extensions for Distributed Authoring -- WEBDAV
+ #
+ # PROPFIND PROPPATCH MKCOL DELETE PUT COPY MOVE
+ # LOCK UNLOCK
+
+ # RFC3253: Versioning Extensions to WebDAV
+ # (Web Distributed Authoring and Versioning)
+ #
+ # VERSION-CONTROL REPORT CHECKOUT CHECK_IN UNCHECKOUT
+ # MKWORKSPACE UPDATE LABEL MERGE ACTIVITY
+
+ private
+
+ def exec_handler(req, res)
+ raise HTTPStatus::NotFound, "`#{req.path}' not found" unless @root
+ if set_filename(req, res)
+ handler = get_handler(req)
+ call_callback(:HandlerCallback, req, res)
+ h = handler.get_instance(@config, res.filename)
+ h.service(req, res)
+ return true
+ end
+ call_callback(:HandlerCallback, req, res)
+ return false
+ end
+
+ def get_handler(req)
+ suffix1 = (/\.(\w+)$/ =~ req.script_name) && $1.downcase
+ suffix2 = (/\.(\w+)\.[\w\-]+$/ =~ req.script_name) && $1.downcase
+ handler_table = @options[:HandlerTable]
+ return handler_table[suffix1] || handler_table[suffix2] ||
+ HandlerTable[suffix1] || HandlerTable[suffix2] ||
+ DefaultFileHandler
+ end
+
+ def set_filename(req, res)
+ res.filename = @root.dup
+ path_info = req.path_info.scan(%r|/[^/]*|)
+
+ path_info.unshift("") # dummy for checking @root dir
+ while base = path_info.first
+ check_filename(req, res, base)
+ break if base == "/"
+ break unless File.directory?(res.filename + base)
+ shift_path_info(req, res, path_info)
+ call_callback(:DirectoryCallback, req, res)
+ end
+
+ if base = path_info.first
+ check_filename(req, res, base)
+ if base == "/"
+ if file = search_index_file(req, res)
+ shift_path_info(req, res, path_info, file)
+ call_callback(:FileCallback, req, res)
+ return true
+ end
+ shift_path_info(req, res, path_info)
+ elsif file = search_file(req, res, base)
+ shift_path_info(req, res, path_info, file)
+ call_callback(:FileCallback, req, res)
+ return true
+ else
+ raise HTTPStatus::NotFound, "`#{req.path}' not found."
+ end
+ end
+
+ return false
+ end
+
+ def check_filename(req, res, name)
+ @options[:NondisclosureName].each{|pattern|
+ if File.fnmatch("/#{pattern}", name)
+ @logger.warn("the request refers nondisclosure name `#{name}'.")
+ raise HTTPStatus::NotFound, "`#{req.path}' not found."
+ end
+ }
+ end
+
+ def shift_path_info(req, res, path_info, base=nil)
+ tmp = path_info.shift
+ base = base || tmp
+ req.path_info = path_info.join
+ req.script_name << base
+ res.filename << base
+ end
+
+ def search_index_file(req, res)
+ @config[:DirectoryIndex].each{|index|
+ if file = search_file(req, res, "/"+index)
+ return file
+ end
+ }
+ return nil
+ end
+
+ def search_file(req, res, basename)
+ langs = @options[:AcceptableLanguages]
+ path = res.filename + basename
+ if File.file?(path)
+ return basename
+ elsif langs.size > 0
+ req.accept_language.each{|lang|
+ path_with_lang = path + ".#{lang}"
+ if langs.member?(lang) && File.file?(path_with_lang)
+ return basename + ".#{lang}"
+ end
+ }
+ (langs - req.accept_language).each{|lang|
+ path_with_lang = path + ".#{lang}"
+ if File.file?(path_with_lang)
+ return basename + ".#{lang}"
+ end
+ }
+ end
+ return nil
+ end
+
+ def call_callback(callback_name, req, res)
+ if cb = @options[callback_name]
+ cb.call(req, res)
+ end
+ end
+
+ def nondisclosure_name?(name)
+ @options[:NondisclosureName].each{|pattern|
+ if File.fnmatch(pattern, name)
+ return true
+ end
+ }
+ return false
+ end
+
+ def set_dir_list(req, res)
+ redirect_to_directory_uri(req, res)
+ unless @options[:FancyIndexing]
+ raise HTTPStatus::Forbidden, "no access permission to `#{req.path}'"
+ end
+ local_path = res.filename
+ list = Dir::entries(local_path).collect{|name|
+ next if name == "." || name == ".."
+ next if nondisclosure_name?(name)
+ st = (File::stat(local_path + name) rescue nil)
+ if st.nil?
+ [ name, nil, -1 ]
+ elsif st.directory?
+ [ name + "/", st.mtime, -1 ]
+ else
+ [ name, st.mtime, st.size ]
+ end
+ }
+ list.compact!
+
+ if d0 = req.query["N"]; idx = 0
+ elsif d0 = req.query["M"]; idx = 1
+ elsif d0 = req.query["S"]; idx = 2
+ else d0 = "A" ; idx = 0
+ end
+ d1 = (d0 == "A") ? "D" : "A"
+
+ if d0 == "A"
+ list.sort!{|a,b| a[idx] <=> b[idx] }
+ else
+ list.sort!{|a,b| b[idx] <=> a[idx] }
+ end
+
+ res['content-type'] = "text/html"
+
+ res.body = <<-_end_of_html_
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<HTML>
+ <HEAD><TITLE>Index of #{HTMLUtils::escape(req.path)}</TITLE></HEAD>
+ <BODY>
+ <H1>Index of #{HTMLUtils::escape(req.path)}</H1>
+ _end_of_html_
+
+ res.body << "<PRE>\n"
+ res.body << " <A HREF=\"?N=#{d1}\">Name</A> "
+ res.body << "<A HREF=\"?M=#{d1}\">Last modified</A> "
+ res.body << "<A HREF=\"?S=#{d1}\">Size</A>\n"
+ res.body << "<HR>\n"
+
+ list.unshift [ "..", File::mtime(local_path+".."), -1 ]
+ list.each{ |name, time, size|
+ if name == ".."
+ dname = "Parent Directory"
+ elsif name.size > 25
+ dname = name.sub(/^(.{23})(.*)/){ $1 + ".." }
+ else
+ dname = name
+ end
+ s = " <A HREF=\"#{HTTPUtils::escape(name)}\">#{dname}</A>"
+ s << " " * (30 - dname.size)
+ s << (time ? time.strftime("%Y/%m/%d %H:%M ") : " " * 22)
+ s << (size >= 0 ? size.to_s : "-") << "\n"
+ res.body << s
+ }
+ res.body << "</PRE><HR>"
+
+ res.body << <<-_end_of_html_
+ <ADDRESS>
+ #{HTMLUtils::escape(@config[:ServerSoftware])}<BR>
+ at #{req.host}:#{req.port}
+ </ADDRESS>
+ </BODY>
+</HTML>
+ _end_of_html_
+ end
+
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/httpservlet/prochandler.rb b/ruby_1_8_5/lib/webrick/httpservlet/prochandler.rb
new file mode 100644
index 0000000000..783cb27896
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/httpservlet/prochandler.rb
@@ -0,0 +1,33 @@
+#
+# prochandler.rb -- ProcHandler Class
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
+# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: prochandler.rb,v 1.7 2002/09/21 12:23:42 gotoyuzo Exp $
+
+require 'webrick/httpservlet/abstract.rb'
+
+module WEBrick
+ module HTTPServlet
+
+ class ProcHandler < AbstractServlet
+ def get_instance(server, *options)
+ self
+ end
+
+ def initialize(proc)
+ @proc = proc
+ end
+
+ def do_GET(request, response)
+ @proc.call(request, response)
+ end
+
+ alias do_POST do_GET
+ end
+
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/httpstatus.rb b/ruby_1_8_5/lib/webrick/httpstatus.rb
new file mode 100644
index 0000000000..0b22c992b3
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/httpstatus.rb
@@ -0,0 +1,126 @@
+#
+# httpstatus.rb -- HTTPStatus Class
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
+# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: httpstatus.rb,v 1.11 2003/03/24 20:18:55 gotoyuzo Exp $
+
+module WEBrick
+
+ module HTTPStatus
+
+ class Status < StandardError; end
+ class Info < Status; end
+ class Success < Status; end
+ class Redirect < Status; end
+ class Error < Status; end
+ class ClientError < Error; end
+ class ServerError < Error; end
+
+ class EOFError < StandardError; end
+
+ StatusMessage = {
+ 100, 'Continue',
+ 101, 'Switching Protocols',
+ 200, 'OK',
+ 201, 'Created',
+ 202, 'Accepted',
+ 203, 'Non-Authoritative Information',
+ 204, 'No Content',
+ 205, 'Reset Content',
+ 206, 'Partial Content',
+ 300, 'Multiple Choices',
+ 301, 'Moved Permanently',
+ 302, 'Found',
+ 303, 'See Other',
+ 304, 'Not Modified',
+ 305, 'Use Proxy',
+ 307, 'Temporary Redirect',
+ 400, 'Bad Request',
+ 401, 'Unauthorized',
+ 402, 'Payment Required',
+ 403, 'Forbidden',
+ 404, 'Not Found',
+ 405, 'Method Not Allowed',
+ 406, 'Not Acceptable',
+ 407, 'Proxy Authentication Required',
+ 408, 'Request Timeout',
+ 409, 'Conflict',
+ 410, 'Gone',
+ 411, 'Length Required',
+ 412, 'Precondition Failed',
+ 413, 'Request Entity Too Large',
+ 414, 'Request-URI Too Large',
+ 415, 'Unsupported Media Type',
+ 416, 'Request Range Not Satisfiable',
+ 417, 'Expectation Failed',
+ 500, 'Internal Server Error',
+ 501, 'Not Implemented',
+ 502, 'Bad Gateway',
+ 503, 'Service Unavailable',
+ 504, 'Gateway Timeout',
+ 505, 'HTTP Version Not Supported'
+ }
+
+ CodeToError = {}
+
+ StatusMessage.each{|code, message|
+ var_name = message.gsub(/[ \-]/,'_').upcase
+ err_name = message.gsub(/[ \-]/,'')
+
+ case code
+ when 100...200; parent = Info
+ when 200...300; parent = Success
+ when 300...400; parent = Redirect
+ when 400...500; parent = ClientError
+ when 500...600; parent = ServerError
+ end
+
+ eval %-
+ RC_#{var_name} = #{code}
+ class #{err_name} < #{parent}
+ def self.code() RC_#{var_name} end
+ def self.reason_phrase() StatusMessage[code] end
+ def code() self::class::code end
+ def reason_phrase() self::class::reason_phrase end
+ alias to_i code
+ end
+ -
+
+ CodeToError[code] = const_get(err_name)
+ }
+
+ def reason_phrase(code)
+ StatusMessage[code.to_i]
+ end
+ def info?(code)
+ code.to_i >= 100 and code.to_i < 200
+ end
+ def success?(code)
+ code.to_i >= 200 and code.to_i < 300
+ end
+ def redirect?(code)
+ code.to_i >= 300 and code.to_i < 400
+ end
+ def error?(code)
+ code.to_i >= 400 and code.to_i < 600
+ end
+ def client_error?(code)
+ code.to_i >= 400 and code.to_i < 500
+ end
+ def server_error?(code)
+ code.to_i >= 500 and code.to_i < 600
+ end
+
+ def self.[](code)
+ CodeToError[code]
+ end
+
+ module_function :reason_phrase
+ module_function :info?, :success?, :redirect?, :error?
+ module_function :client_error?, :server_error?
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/httputils.rb b/ruby_1_8_5/lib/webrick/httputils.rb
new file mode 100644
index 0000000000..c57af2c860
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/httputils.rb
@@ -0,0 +1,399 @@
+#
+# httputils.rb -- HTTPUtils Module
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
+# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: httputils.rb,v 1.34 2003/06/05 21:34:08 gotoyuzo Exp $
+
+require 'socket'
+require 'tempfile'
+
+module WEBrick
+ CR = "\x0d"
+ LF = "\x0a"
+ CRLF = "\x0d\x0a"
+
+ module HTTPUtils
+
+ def normalize_path(path)
+ raise "abnormal path `#{path}'" if path[0] != ?/
+ ret = path.dup
+
+ ret.gsub!(%r{/+}o, '/') # // => /
+ while ret.sub!(%r{/\.(/|\Z)}o, '/'); end # /. => /
+ begin # /foo/.. => /foo
+ match = ret.sub!(%r{/([^/]+)/\.\.(/|\Z)}o){
+ if $1 == ".."
+ raise "abnormal path `#{path}'"
+ else
+ "/"
+ end
+ }
+ end while match
+
+ raise "abnormal path `#{path}'" if %r{/\.\.(/|\Z)} =~ ret
+ ret
+ end
+ module_function :normalize_path
+
+ #####
+
+ DefaultMimeTypes = {
+ "ai" => "application/postscript",
+ "asc" => "text/plain",
+ "avi" => "video/x-msvideo",
+ "bin" => "application/octet-stream",
+ "bmp" => "image/bmp",
+ "class" => "application/octet-stream",
+ "cer" => "application/pkix-cert",
+ "crl" => "application/pkix-crl",
+ "crt" => "application/x-x509-ca-cert",
+ #"crl" => "application/x-pkcs7-crl",
+ "css" => "text/css",
+ "dms" => "application/octet-stream",
+ "doc" => "application/msword",
+ "dvi" => "application/x-dvi",
+ "eps" => "application/postscript",
+ "etx" => "text/x-setext",
+ "exe" => "application/octet-stream",
+ "gif" => "image/gif",
+ "htm" => "text/html",
+ "html" => "text/html",
+ "jpe" => "image/jpeg",
+ "jpeg" => "image/jpeg",
+ "jpg" => "image/jpeg",
+ "lha" => "application/octet-stream",
+ "lzh" => "application/octet-stream",
+ "mov" => "video/quicktime",
+ "mpe" => "video/mpeg",
+ "mpeg" => "video/mpeg",
+ "mpg" => "video/mpeg",
+ "pbm" => "image/x-portable-bitmap",
+ "pdf" => "application/pdf",
+ "pgm" => "image/x-portable-graymap",
+ "png" => "image/png",
+ "pnm" => "image/x-portable-anymap",
+ "ppm" => "image/x-portable-pixmap",
+ "ppt" => "application/vnd.ms-powerpoint",
+ "ps" => "application/postscript",
+ "qt" => "video/quicktime",
+ "ras" => "image/x-cmu-raster",
+ "rb" => "text/plain",
+ "rd" => "text/plain",
+ "rtf" => "application/rtf",
+ "sgm" => "text/sgml",
+ "sgml" => "text/sgml",
+ "tif" => "image/tiff",
+ "tiff" => "image/tiff",
+ "txt" => "text/plain",
+ "xbm" => "image/x-xbitmap",
+ "xls" => "application/vnd.ms-excel",
+ "xml" => "text/xml",
+ "xpm" => "image/x-xpixmap",
+ "xwd" => "image/x-xwindowdump",
+ "zip" => "application/zip",
+ }
+
+ # Load Apache compatible mime.types file.
+ def load_mime_types(file)
+ open(file){ |io|
+ hash = Hash.new
+ io.each{ |line|
+ next if /^#/ =~ line
+ line.chomp!
+ mimetype, ext0 = line.split(/\s+/, 2)
+ next unless ext0
+ next if ext0.empty?
+ ext0.split(/\s+/).each{ |ext| hash[ext] = mimetype }
+ }
+ hash
+ }
+ end
+ module_function :load_mime_types
+
+ def mime_type(filename, mime_tab)
+ suffix1 = (/\.(\w+)$/ =~ filename && $1.downcase)
+ suffix2 = (/\.(\w+)\.[\w\-]+$/ =~ filename && $1.downcase)
+ mime_tab[suffix1] || mime_tab[suffix2] || "application/octet-stream"
+ end
+ module_function :mime_type
+
+ #####
+
+ def parse_header(raw)
+ header = Hash.new([].freeze)
+ field = nil
+ raw.each{|line|
+ case line
+ when /^([A-Za-z0-9!\#$%&'*+\-.^_`|~]+):\s*(.*?)\s*\z/om
+ field, value = $1, $2
+ field.downcase!
+ header[field] = [] unless header.has_key?(field)
+ header[field] << value
+ when /^\s+(.*?)\s*\z/om
+ value = $1
+ unless field
+ raise "bad header '#{line.inspect}'."
+ end
+ header[field][-1] << " " << value
+ else
+ raise "bad header '#{line.inspect}'."
+ end
+ }
+ header.each{|key, values|
+ values.each{|value|
+ value.strip!
+ value.gsub!(/\s+/, " ")
+ }
+ }
+ header
+ end
+ module_function :parse_header
+
+ def split_header_value(str)
+ str.scan(/((?:"(?:\\.|[^"])+?"|[^",]+)+)
+ (?:,\s*|\Z)/xn).collect{|v| v[0] }
+ end
+ module_function :split_header_value
+
+ def parse_range_header(ranges_specifier)
+ if /^bytes=(.*)/ =~ ranges_specifier
+ byte_range_set = split_header_value($1)
+ byte_range_set.collect{|range_spec|
+ case range_spec
+ when /^(\d+)-(\d+)/ then $1.to_i .. $2.to_i
+ when /^(\d+)-/ then $1.to_i .. -1
+ when /^-(\d+)/ then -($1.to_i) .. -1
+ else return nil
+ end
+ }
+ end
+ end
+ module_function :parse_range_header
+
+ def parse_qvalues(value)
+ tmp = []
+ if value
+ parts = value.split(/,\s*/)
+ parts.each {|part|
+ if m = %r{^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$}.match(part)
+ val = m[1]
+ q = (m[2] or 1).to_f
+ tmp.push([val, q])
+ end
+ }
+ tmp = tmp.sort_by{|val, q| -q}
+ tmp.collect!{|val, q| val}
+ end
+ return tmp
+ end
+ module_function :parse_qvalues
+
+ #####
+
+ def dequote(str)
+ ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup
+ ret.gsub!(/\\(.)/, "\\1")
+ ret
+ end
+ module_function :dequote
+
+ def quote(str)
+ '"' << str.gsub(/[\\\"]/o, "\\\1") << '"'
+ end
+ module_function :quote
+
+ #####
+
+ class FormData < String
+ EmptyRawHeader = [].freeze
+ EmptyHeader = {}.freeze
+
+ attr_accessor :name, :filename, :next_data
+ protected :next_data
+
+ def initialize(*args)
+ @name = @filename = @next_data = nil
+ if args.empty?
+ @raw_header = []
+ @header = nil
+ super("")
+ else
+ @raw_header = EmptyRawHeader
+ @header = EmptyHeader
+ super(args.shift)
+ unless args.empty?
+ @next_data = self.class.new(*args)
+ end
+ end
+ end
+
+ def [](*key)
+ begin
+ @header[key[0].downcase].join(", ")
+ rescue StandardError, NameError
+ super
+ end
+ end
+
+ def <<(str)
+ if @header
+ super
+ elsif str == CRLF
+ @header = HTTPUtils::parse_header(@raw_header)
+ if cd = self['content-disposition']
+ if /\s+name="(.*?)"/ =~ cd then @name = $1 end
+ if /\s+filename="(.*?)"/ =~ cd then @filename = $1 end
+ end
+ else
+ @raw_header << str
+ end
+ self
+ end
+
+ def append_data(data)
+ tmp = self
+ while tmp
+ unless tmp.next_data
+ tmp.next_data = data
+ break
+ end
+ tmp = tmp.next_data
+ end
+ self
+ end
+
+ def each_data
+ tmp = self
+ while tmp
+ next_data = tmp.next_data
+ yield(tmp)
+ tmp = next_data
+ end
+ end
+
+ def list
+ ret = []
+ each_data{|data|
+ ret << data.to_s
+ }
+ ret
+ end
+
+ alias :to_ary :list
+
+ def to_s
+ String.new(self)
+ end
+ end
+
+ def parse_query(str)
+ query = Hash.new
+ if str
+ str.split(/[&;]/).each{|x|
+ next if x.empty?
+ key, val = x.split(/=/,2)
+ key = unescape_form(key)
+ val = unescape_form(val.to_s)
+ val = FormData.new(val)
+ val.name = key
+ if query.has_key?(key)
+ query[key].append_data(val)
+ next
+ end
+ query[key] = val
+ }
+ end
+ query
+ end
+ module_function :parse_query
+
+ def parse_form_data(io, boundary)
+ boundary_regexp = /\A--#{boundary}(--)?#{CRLF}\z/
+ form_data = Hash.new
+ return form_data unless io
+ data = nil
+ io.each{|line|
+ if boundary_regexp =~ line
+ if data
+ data.chop!
+ key = data.name
+ if form_data.has_key?(key)
+ form_data[key].append_data(data)
+ else
+ form_data[key] = data
+ end
+ end
+ data = FormData.new
+ next
+ else
+ if data
+ data << line
+ end
+ end
+ }
+ return form_data
+ end
+ module_function :parse_form_data
+
+ #####
+
+ reserved = ';/?:@&=+$,'
+ num = '0123456789'
+ lowalpha = 'abcdefghijklmnopqrstuvwxyz'
+ upalpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ mark = '-_.!~*\'()'
+ unreserved = num + lowalpha + upalpha + mark
+ control = (0x0..0x1f).collect{|c| c.chr }.join + "\x7f"
+ space = " "
+ delims = '<>#%"'
+ unwise = '{}|\\^[]`'
+ nonascii = (0x80..0xff).collect{|c| c.chr }.join
+
+ module_function
+
+ def _make_regex(str) /([#{Regexp.escape(str)}])/n end
+ def _make_regex!(str) /([^#{Regexp.escape(str)}])/n end
+ def _escape(str, regex) str.gsub(regex){ "%%%02X" % $1[0] } end
+ def _unescape(str, regex) str.gsub(regex){ $1.hex.chr } end
+
+ UNESCAPED = _make_regex(control+space+delims+unwise+nonascii)
+ UNESCAPED_FORM = _make_regex(reserved+control+delims+unwise+nonascii)
+ NONASCII = _make_regex(nonascii)
+ ESCAPED = /%([0-9a-fA-F]{2})/
+ UNESCAPED_PCHAR = _make_regex!(unreserved+":@&=+$,")
+
+ def escape(str)
+ _escape(str, UNESCAPED)
+ end
+
+ def unescape(str)
+ _unescape(str, ESCAPED)
+ end
+
+ def escape_form(str)
+ ret = _escape(str, UNESCAPED_FORM)
+ ret.gsub!(/ /, "+")
+ ret
+ end
+
+ def unescape_form(str)
+ _unescape(str.gsub(/\+/, " "), ESCAPED)
+ end
+
+ def escape_path(str)
+ result = ""
+ str.scan(%r{/([^/]*)}).each{|i|
+ result << "/" << _escape(i[0], UNESCAPED_PCHAR)
+ }
+ return result
+ end
+
+ def escape8bit(str)
+ _escape(str, NONASCII)
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/httpversion.rb b/ruby_1_8_5/lib/webrick/httpversion.rb
new file mode 100644
index 0000000000..86907a26bd
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/httpversion.rb
@@ -0,0 +1,49 @@
+#
+# HTTPVersion.rb -- presentation of HTTP version
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: httpversion.rb,v 1.5 2002/09/21 12:23:37 gotoyuzo Exp $
+
+module WEBrick
+ class HTTPVersion
+ include Comparable
+
+ attr_accessor :major, :minor
+
+ def self.convert(version)
+ version.is_a?(self) ? version : new(version)
+ end
+
+ def initialize(version)
+ case version
+ when HTTPVersion
+ @major, @minor = version.major, version.minor
+ when String
+ if /^(\d+)\.(\d+)$/ =~ version
+ @major, @minor = $1.to_i, $2.to_i
+ end
+ end
+ if @major.nil? || @minor.nil?
+ raise ArgumentError,
+ format("cannot convert %s into %s", version.class, self.class)
+ end
+ end
+
+ def <=>(other)
+ unless other.is_a?(self.class)
+ other = self.class.new(other)
+ end
+ if (ret = @major <=> other.major) == 0
+ return @minor <=> other.minor
+ end
+ return ret
+ end
+
+ def to_s
+ format("%d.%d", @major, @minor)
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/log.rb b/ruby_1_8_5/lib/webrick/log.rb
new file mode 100644
index 0000000000..5d4fd0a174
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/log.rb
@@ -0,0 +1,88 @@
+#
+# log.rb -- Log Class
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
+# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: log.rb,v 1.26 2002/10/06 17:06:10 gotoyuzo Exp $
+
+module WEBrick
+ class BasicLog
+ # log-level constant
+ FATAL, ERROR, WARN, INFO, DEBUG = 1, 2, 3, 4, 5
+
+ attr_accessor :level
+
+ def initialize(log_file=nil, level=nil)
+ @level = level || INFO
+ case log_file
+ when String
+ @log = open(log_file, "a+")
+ @log.sync = true
+ @opened = true
+ when NilClass
+ @log = $stderr
+ else
+ @log = log_file # requires "<<". (see BasicLog#log)
+ end
+ end
+
+ def close
+ @log.close if @opened
+ @log = nil
+ end
+
+ def log(level, data)
+ if @log && level <= @level
+ data += "\n" if /\n\Z/ !~ data
+ @log << data
+ end
+ end
+
+ def <<(obj)
+ log(INFO, obj.to_s)
+ end
+
+ def fatal(msg) log(FATAL, "FATAL " << format(msg)); end
+ def error(msg) log(ERROR, "ERROR " << format(msg)); end
+ def warn(msg) log(WARN, "WARN " << format(msg)); end
+ def info(msg) log(INFO, "INFO " << format(msg)); end
+ def debug(msg) log(DEBUG, "DEBUG " << format(msg)); end
+
+ def fatal?; @level >= FATAL; end
+ def error?; @level >= ERROR; end
+ def warn?; @level >= WARN; end
+ def info?; @level >= INFO; end
+ def debug?; @level >= DEBUG; end
+
+ private
+
+ def format(arg)
+ str = if arg.is_a?(Exception)
+ "#{arg.class}: #{arg.message}\n\t" <<
+ arg.backtrace.join("\n\t") << "\n"
+ elsif arg.respond_to?(:to_str)
+ arg.to_str
+ else
+ arg.inspect
+ end
+ end
+ end
+
+ class Log < BasicLog
+ attr_accessor :time_format
+
+ def initialize(log_file=nil, level=nil)
+ super(log_file, level)
+ @time_format = "[%Y-%m-%d %H:%M:%S]"
+ end
+
+ def log(level, data)
+ tmp = Time.now.strftime(@time_format)
+ tmp << " " << data
+ super(level, tmp)
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/server.rb b/ruby_1_8_5/lib/webrick/server.rb
new file mode 100644
index 0000000000..16dbd46f98
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/server.rb
@@ -0,0 +1,200 @@
+#
+# server.rb -- GenericServer Class
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
+# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: server.rb,v 1.62 2003/07/22 19:20:43 gotoyuzo Exp $
+
+require 'thread'
+require 'socket'
+require 'timeout'
+require 'webrick/config'
+require 'webrick/log'
+
+module WEBrick
+
+ class ServerError < StandardError; end
+
+ class SimpleServer
+ def SimpleServer.start
+ yield
+ end
+ end
+
+ class Daemon
+ def Daemon.start
+ exit!(0) if fork
+ Process::setsid
+ exit!(0) if fork
+ Dir::chdir("/")
+ File::umask(0)
+ STDIN.reopen("/dev/null")
+ STDOUT.reopen("/dev/null", "w")
+ STDERR.reopen("/dev/null", "w")
+ yield if block_given?
+ end
+ end
+
+ class GenericServer
+ attr_reader :status, :config, :logger, :tokens, :listeners
+
+ def initialize(config={}, default=Config::General)
+ @config = default.dup.update(config)
+ @status = :Stop
+ @config[:Logger] ||= Log::new
+ @logger = @config[:Logger]
+
+ @tokens = SizedQueue.new(@config[:MaxClients])
+ @config[:MaxClients].times{ @tokens.push(nil) }
+
+ webrickv = WEBrick::VERSION
+ rubyv = "#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
+ @logger.info("WEBrick #{webrickv}")
+ @logger.info("ruby #{rubyv}")
+
+ @listeners = []
+ unless @config[:DoNotListen]
+ if @config[:Listen]
+ warn(":Listen option is deprecated; use GenericServer#listen")
+ end
+ listen(@config[:BindAddress], @config[:Port])
+ if @config[:Port] == 0
+ @config[:Port] = @listeners[0].addr[1]
+ end
+ end
+ end
+
+ def [](key)
+ @config[key]
+ end
+
+ def listen(address, port)
+ @listeners += Utils::create_listeners(address, port, @logger)
+ end
+
+ def start(&block)
+ raise ServerError, "already started." if @status != :Stop
+ server_type = @config[:ServerType] || SimpleServer
+
+ server_type.start{
+ @logger.info \
+ "#{self.class}#start: pid=#{$$} port=#{@config[:Port]}"
+ call_callback(:StartCallback)
+
+ thgroup = ThreadGroup.new
+ @status = :Running
+ while @status == :Running
+ begin
+ if svrs = IO.select(@listeners, nil, nil, 2.0)
+ svrs[0].each{|svr|
+ @tokens.pop # blocks while no token is there.
+ if sock = accept_client(svr)
+ th = start_thread(sock, &block)
+ th[:WEBrickThread] = true
+ thgroup.add(th)
+ else
+ @tokens.push(nil)
+ end
+ }
+ end
+ rescue Errno::EBADF, IOError => ex
+ # if the listening socket was closed in GenericServer#shutdown,
+ # IO::select raise it.
+ rescue Exception => ex
+ msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
+ @logger.error msg
+ end
+ end
+
+ @logger.info "going to shutdown ..."
+ thgroup.list.each{|th| th.join if th[:WEBrickThread] }
+ call_callback(:StopCallback)
+ @logger.info "#{self.class}#start done."
+ @status = :Stop
+ }
+ end
+
+ def stop
+ if @status == :Running
+ @status = :Shutdown
+ end
+ end
+
+ def shutdown
+ stop
+ @listeners.each{|s|
+ if @logger.debug?
+ addr = s.addr
+ @logger.debug("close TCPSocket(#{addr[2]}, #{addr[1]})")
+ end
+ s.close
+ }
+ @listeners.clear
+ end
+
+ def run(sock)
+ @logger.fatal "run() must be provided by user."
+ end
+
+ private
+
+ def accept_client(svr)
+ sock = nil
+ begin
+ sock = svr.accept
+ sock.sync = true
+ Utils::set_non_blocking(sock)
+ Utils::set_close_on_exec(sock)
+ rescue Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPROTO => ex
+ # TCP connection was established but RST segment was sent
+ # from peer before calling TCPServer#accept.
+ rescue Exception => ex
+ msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
+ @logger.error msg
+ end
+ return sock
+ end
+
+ def start_thread(sock, &block)
+ Thread.start{
+ begin
+ Thread.current[:WEBrickSocket] = sock
+ begin
+ addr = sock.peeraddr
+ @logger.debug "accept: #{addr[3]}:#{addr[1]}"
+ rescue SocketError
+ @logger.debug "accept: <address unknown>"
+ raise
+ end
+ call_callback(:AcceptCallback, sock)
+ block ? block.call(sock) : run(sock)
+ rescue Errno::ENOTCONN
+ @logger.debug "Errno::ENOTCONN raised"
+ rescue ServerError => ex
+ msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
+ @logger.error msg
+ rescue Exception => ex
+ @logger.error ex
+ ensure
+ @tokens.push(nil)
+ Thread.current[:WEBrickSocket] = nil
+ if addr
+ @logger.debug "close: #{addr[3]}:#{addr[1]}"
+ else
+ @logger.debug "close: <address unknown>"
+ end
+ sock.close
+ end
+ }
+ end
+
+ def call_callback(callback_name, *args)
+ if cb = @config[callback_name]
+ cb.call(*args)
+ end
+ end
+ end # end of GenericServer
+end
diff --git a/ruby_1_8_5/lib/webrick/ssl.rb b/ruby_1_8_5/lib/webrick/ssl.rb
new file mode 100644
index 0000000000..d9c032f6c9
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/ssl.rb
@@ -0,0 +1,126 @@
+#
+# ssl.rb -- SSL/TLS enhancement for GenericServer
+#
+# Copyright (c) 2003 GOTOU Yuuzou All rights reserved.
+#
+# $Id: ssl.rb,v 1.2.2.1 2005/01/18 06:03:43 gotoyuzo Exp $
+
+require 'webrick'
+require 'openssl'
+
+module WEBrick
+ module Config
+ svrsoft = General[:ServerSoftware]
+ osslv = ::OpenSSL::OPENSSL_VERSION.split[1]
+ SSL = {
+ :ServerSoftware => "#{svrsoft} OpenSSL/#{osslv}",
+ :SSLEnable => false,
+ :SSLCertificate => nil,
+ :SSLPrivateKey => nil,
+ :SSLClientCA => nil,
+ :SSLExtraChainCert => nil,
+ :SSLCACertificateFile => nil,
+ :SSLCACertificatePath => nil,
+ :SSLCertificateStore => nil,
+ :SSLVerifyClient => ::OpenSSL::SSL::VERIFY_NONE,
+ :SSLVerifyDepth => nil,
+ :SSLVerifyCallback => nil, # custom verification
+ :SSLTimeout => nil,
+ :SSLOptions => nil,
+ :SSLStartImmediately => true,
+ # Must specify if you use auto generated certificate.
+ :SSLCertName => nil,
+ :SSLCertComment => "Generated by Ruby/OpenSSL"
+ }
+ General.update(SSL)
+ end
+
+ module Utils
+ def create_self_signed_cert(bits, cn, comment)
+ rsa = OpenSSL::PKey::RSA.new(bits){|p, n|
+ case p
+ when 0; $stderr.putc "." # BN_generate_prime
+ when 1; $stderr.putc "+" # BN_generate_prime
+ when 2; $stderr.putc "*" # searching good prime,
+ # n = #of try,
+ # but also data from BN_generate_prime
+ when 3; $stderr.putc "\n" # found good prime, n==0 - p, n==1 - q,
+ # but also data from BN_generate_prime
+ else; $stderr.putc "*" # BN_generate_prime
+ end
+ }
+ cert = OpenSSL::X509::Certificate.new
+ cert.version = 3
+ cert.serial = 0
+ name = OpenSSL::X509::Name.new(cn)
+ cert.subject = name
+ cert.issuer = name
+ cert.not_before = Time.now
+ cert.not_after = Time.now + (365*24*60*60)
+ cert.public_key = rsa.public_key
+
+ ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
+ ef.issuer_certificate = cert
+ cert.extensions = [
+ ef.create_extension("basicConstraints","CA:FALSE"),
+ ef.create_extension("keyUsage", "keyEncipherment"),
+ ef.create_extension("subjectKeyIdentifier", "hash"),
+ ef.create_extension("extendedKeyUsage", "serverAuth"),
+ ef.create_extension("nsComment", comment),
+ ]
+ aki = ef.create_extension("authorityKeyIdentifier",
+ "keyid:always,issuer:always")
+ cert.add_extension(aki)
+ cert.sign(rsa, OpenSSL::Digest::SHA1.new)
+
+ return [ cert, rsa ]
+ end
+ module_function :create_self_signed_cert
+ end
+
+ class GenericServer
+ def ssl_context
+ @ssl_context ||= nil
+ end
+
+ def listen(address, port)
+ listeners = Utils::create_listeners(address, port, @logger)
+ if @config[:SSLEnable]
+ unless ssl_context
+ @ssl_context = setup_ssl_context(@config)
+ @logger.info("\n" + @config[:SSLCertificate].to_text)
+ end
+ listeners.collect!{|svr|
+ ssvr = ::OpenSSL::SSL::SSLServer.new(svr, ssl_context)
+ ssvr.start_immediately = @config[:SSLStartImmediately]
+ ssvr
+ }
+ end
+ @listeners += listeners
+ end
+
+ def setup_ssl_context(config)
+ unless config[:SSLCertificate]
+ cn = config[:SSLCertName]
+ comment = config[:SSLCertComment]
+ cert, key = Utils::create_self_signed_cert(1024, cn, comment)
+ config[:SSLCertificate] = cert
+ config[:SSLPrivateKey] = key
+ end
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.key = config[:SSLPrivateKey]
+ ctx.cert = config[:SSLCertificate]
+ ctx.client_ca = config[:SSLClientCA]
+ ctx.extra_chain_cert = config[:SSLExtraChainCert]
+ ctx.ca_file = config[:SSLCACertificateFile]
+ ctx.ca_path = config[:SSLCACertificatePath]
+ ctx.cert_store = config[:SSLCertificateStore]
+ ctx.verify_mode = config[:SSLVerifyClient]
+ ctx.verify_depth = config[:SSLVerifyDepth]
+ ctx.verify_callback = config[:SSLVerifyCallback]
+ ctx.timeout = config[:SSLTimeout]
+ ctx.options = config[:SSLOptions]
+ ctx
+ end
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/utils.rb b/ruby_1_8_5/lib/webrick/utils.rb
new file mode 100644
index 0000000000..cf9da6f2ce
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/utils.rb
@@ -0,0 +1,100 @@
+#
+# utils.rb -- Miscellaneous utilities
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
+# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: utils.rb,v 1.10 2003/02/16 22:22:54 gotoyuzo Exp $
+
+require 'socket'
+require 'fcntl'
+begin
+ require 'etc'
+rescue LoadError
+ nil
+end
+
+module WEBrick
+ module Utils
+ def set_non_blocking(io)
+ flag = File::NONBLOCK
+ if defined?(Fcntl::F_GETFL)
+ flag |= io.fcntl(Fcntl::F_GETFL)
+ end
+ io.fcntl(Fcntl::F_SETFL, flag)
+ end
+ module_function :set_non_blocking
+
+ def set_close_on_exec(io)
+ if defined?(Fcntl::FD_CLOEXEC)
+ io.fcntl(Fcntl::FD_CLOEXEC, 1)
+ end
+ end
+ module_function :set_close_on_exec
+
+ def su(user)
+ if defined?(Etc)
+ pw = Etc.getpwnam(user)
+ Process::initgroups(user, pw.gid)
+ Process::Sys::setgid(pw.gid)
+ Process::Sys::setuid(pw.uid)
+ else
+ warn("WEBrick::Utils::su doesn't work on this platform")
+ end
+ end
+ module_function :su
+
+ def getservername
+ host = Socket::gethostname
+ begin
+ Socket::gethostbyname(host)[0]
+ rescue
+ host
+ end
+ end
+ module_function :getservername
+
+ def create_listeners(address, port, logger=nil)
+ unless port
+ raise ArgumentError, "must specify port"
+ end
+ res = Socket::getaddrinfo(address, port,
+ Socket::AF_UNSPEC, # address family
+ Socket::SOCK_STREAM, # socket type
+ 0, # protocol
+ Socket::AI_PASSIVE) # flag
+ last_error = nil
+ sockets = []
+ res.each{|ai|
+ begin
+ logger.debug("TCPServer.new(#{ai[3]}, #{port})") if logger
+ sock = TCPServer.new(ai[3], port)
+ port = sock.addr[1] if port == 0
+ Utils::set_close_on_exec(sock)
+ sockets << sock
+ rescue => ex
+ logger.warn("TCPServer Error: #{ex}") if logger
+ last_error = ex
+ end
+ }
+ raise last_error if sockets.empty?
+ return sockets
+ end
+ module_function :create_listeners
+
+ RAND_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
+ "0123456789" +
+ "abcdefghijklmnopqrstuvwxyz"
+
+ def random_string(len)
+ rand_max = RAND_CHARS.size
+ ret = ""
+ len.times{ ret << RAND_CHARS[rand(rand_max)] }
+ ret
+ end
+ module_function :random_string
+
+ end
+end
diff --git a/ruby_1_8_5/lib/webrick/version.rb b/ruby_1_8_5/lib/webrick/version.rb
new file mode 100644
index 0000000000..b2b9fd3b78
--- /dev/null
+++ b/ruby_1_8_5/lib/webrick/version.rb
@@ -0,0 +1,13 @@
+#
+# version.rb -- version and release date
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2000 TAKAHASHI Masayoshi, GOTOU YUUZOU
+# Copyright (c) 2003 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: version.rb,v 1.74 2003/07/22 19:20:43 gotoyuzo Exp $
+
+module WEBrick
+ VERSION = "1.3.1"
+end
diff --git a/ruby_1_8_5/lib/wsdl/binding.rb b/ruby_1_8_5/lib/wsdl/binding.rb
new file mode 100644
index 0000000000..58a21d820d
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/binding.rb
@@ -0,0 +1,65 @@
+# WSDL4R - WSDL binding definition.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+require 'xsd/namedelements'
+
+
+module WSDL
+
+
+class Binding < Info
+ attr_reader :name # required
+ attr_reader :type # required
+ attr_reader :operations
+ attr_reader :soapbinding
+
+ def initialize
+ super
+ @name = nil
+ @type = nil
+ @operations = XSD::NamedElements.new
+ @soapbinding = nil
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def parse_element(element)
+ case element
+ when OperationName
+ o = OperationBinding.new
+ @operations << o
+ o
+ when SOAPBindingName
+ o = WSDL::SOAP::Binding.new
+ @soapbinding = o
+ o
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NameAttrName
+ @name = XSD::QName.new(targetnamespace, value.source)
+ when TypeAttrName
+ @type = value
+ else
+ nil
+ end
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/wsdl/data.rb b/ruby_1_8_5/lib/wsdl/data.rb
new file mode 100644
index 0000000000..45eaad8526
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/data.rb
@@ -0,0 +1,64 @@
+# WSDL4R - WSDL data definitions.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/qname'
+require 'wsdl/documentation'
+require 'wsdl/definitions'
+require 'wsdl/types'
+require 'wsdl/message'
+require 'wsdl/part'
+require 'wsdl/portType'
+require 'wsdl/operation'
+require 'wsdl/param'
+require 'wsdl/binding'
+require 'wsdl/operationBinding'
+require 'wsdl/service'
+require 'wsdl/port'
+require 'wsdl/import'
+
+
+module WSDL
+
+
+ArrayTypeAttrName = XSD::QName.new(Namespace, 'arrayType')
+BindingName = XSD::QName.new(Namespace, 'binding')
+DefinitionsName = XSD::QName.new(Namespace, 'definitions')
+DocumentationName = XSD::QName.new(Namespace, 'documentation')
+FaultName = XSD::QName.new(Namespace, 'fault')
+ImportName = XSD::QName.new(Namespace, 'import')
+InputName = XSD::QName.new(Namespace, 'input')
+MessageName = XSD::QName.new(Namespace, 'message')
+OperationName = XSD::QName.new(Namespace, 'operation')
+OutputName = XSD::QName.new(Namespace, 'output')
+PartName = XSD::QName.new(Namespace, 'part')
+PortName = XSD::QName.new(Namespace, 'port')
+PortTypeName = XSD::QName.new(Namespace, 'portType')
+ServiceName = XSD::QName.new(Namespace, 'service')
+TypesName = XSD::QName.new(Namespace, 'types')
+
+SchemaName = XSD::QName.new(XSD::Namespace, 'schema')
+
+SOAPAddressName = XSD::QName.new(SOAPBindingNamespace, 'address')
+SOAPBindingName = XSD::QName.new(SOAPBindingNamespace, 'binding')
+SOAPHeaderName = XSD::QName.new(SOAPBindingNamespace, 'header')
+SOAPBodyName = XSD::QName.new(SOAPBindingNamespace, 'body')
+SOAPFaultName = XSD::QName.new(SOAPBindingNamespace, 'fault')
+SOAPOperationName = XSD::QName.new(SOAPBindingNamespace, 'operation')
+
+BindingAttrName = XSD::QName.new(nil, 'binding')
+ElementAttrName = XSD::QName.new(nil, 'element')
+LocationAttrName = XSD::QName.new(nil, 'location')
+MessageAttrName = XSD::QName.new(nil, 'message')
+NameAttrName = XSD::QName.new(nil, 'name')
+NamespaceAttrName = XSD::QName.new(nil, 'namespace')
+ParameterOrderAttrName = XSD::QName.new(nil, 'parameterOrder')
+TargetNamespaceAttrName = XSD::QName.new(nil, 'targetNamespace')
+TypeAttrName = XSD::QName.new(nil, 'type')
+
+
+end
diff --git a/ruby_1_8_5/lib/wsdl/definitions.rb b/ruby_1_8_5/lib/wsdl/definitions.rb
new file mode 100644
index 0000000000..5235037cfe
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/definitions.rb
@@ -0,0 +1,250 @@
+# WSDL4R - WSDL definitions.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+require 'xsd/namedelements'
+
+
+module WSDL
+
+
+class Definitions < Info
+ attr_reader :name
+ attr_reader :targetnamespace
+ attr_reader :imports
+
+ attr_accessor :location
+ attr_reader :importedschema
+
+ def initialize
+ super
+ @name = nil
+ @targetnamespace = nil
+ @location = nil
+ @importedschema = {}
+
+ @types = nil
+ @imports = []
+ @messages = XSD::NamedElements.new
+ @porttypes = XSD::NamedElements.new
+ @bindings = XSD::NamedElements.new
+ @services = XSD::NamedElements.new
+
+ @anontypes = XSD::NamedElements.new
+ @root = self
+ end
+
+ def inspect
+ sprintf("#<%s:0x%x %s>", self.class.name, __id__, @name || '(unnamed)')
+ end
+
+ def targetnamespace=(targetnamespace)
+ @targetnamespace = targetnamespace
+ if @name
+ @name = XSD::QName.new(@targetnamespace, @name.name)
+ end
+ end
+
+ def collect_attributes
+ result = XSD::NamedElements.new
+ if @types
+ @types.schemas.each do |schema|
+ result.concat(schema.collect_attributes)
+ end
+ end
+ @imports.each do |import|
+ result.concat(import.content.collect_attributes)
+ end
+ result
+ end
+
+ def collect_elements
+ result = XSD::NamedElements.new
+ if @types
+ @types.schemas.each do |schema|
+ result.concat(schema.collect_elements)
+ end
+ end
+ @imports.each do |import|
+ result.concat(import.content.collect_elements)
+ end
+ result
+ end
+
+ def collect_complextypes
+ result = @anontypes.dup
+ if @types
+ @types.schemas.each do |schema|
+ result.concat(schema.collect_complextypes)
+ end
+ end
+ @imports.each do |import|
+ result.concat(import.content.collect_complextypes)
+ end
+ result
+ end
+
+ def collect_simpletypes
+ result = XSD::NamedElements.new
+ if @types
+ @types.schemas.each do |schema|
+ result.concat(schema.collect_simpletypes)
+ end
+ end
+ @imports.each do |import|
+ result.concat(import.content.collect_simpletypes)
+ end
+ result
+ end
+
+ # ToDo: simpletype must be accepted...
+ def add_type(complextype)
+ @anontypes << complextype
+ end
+
+ def messages
+ result = @messages.dup
+ @imports.each do |import|
+ result.concat(import.content.messages) if self.class === import.content
+ end
+ result
+ end
+
+ def porttypes
+ result = @porttypes.dup
+ @imports.each do |import|
+ result.concat(import.content.porttypes) if self.class === import.content
+ end
+ result
+ end
+
+ def bindings
+ result = @bindings.dup
+ @imports.each do |import|
+ result.concat(import.content.bindings) if self.class === import.content
+ end
+ result
+ end
+
+ def services
+ result = @services.dup
+ @imports.each do |import|
+ result.concat(import.content.services) if self.class === import.content
+ end
+ result
+ end
+
+ def message(name)
+ message = @messages[name]
+ return message if message
+ @imports.each do |import|
+ message = import.content.message(name) if self.class === import.content
+ return message if message
+ end
+ nil
+ end
+
+ def porttype(name)
+ porttype = @porttypes[name]
+ return porttype if porttype
+ @imports.each do |import|
+ porttype = import.content.porttype(name) if self.class === import.content
+ return porttype if porttype
+ end
+ nil
+ end
+
+ def binding(name)
+ binding = @bindings[name]
+ return binding if binding
+ @imports.each do |import|
+ binding = import.content.binding(name) if self.class === import.content
+ return binding if binding
+ end
+ nil
+ end
+
+ def service(name)
+ service = @services[name]
+ return service if service
+ @imports.each do |import|
+ service = import.content.service(name) if self.class === import.content
+ return service if service
+ end
+ nil
+ end
+
+ def porttype_binding(name)
+ binding = @bindings.find { |item| item.type == name }
+ return binding if binding
+ @imports.each do |import|
+ binding = import.content.porttype_binding(name) if self.class === import.content
+ return binding if binding
+ end
+ nil
+ end
+
+ def parse_element(element)
+ case element
+ when ImportName
+ o = Import.new
+ @imports << o
+ o
+ when TypesName
+ o = Types.new
+ @types = o
+ o
+ when MessageName
+ o = Message.new
+ @messages << o
+ o
+ when PortTypeName
+ o = PortType.new
+ @porttypes << o
+ o
+ when BindingName
+ o = Binding.new
+ @bindings << o
+ o
+ when ServiceName
+ o = Service.new
+ @services << o
+ o
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NameAttrName
+ @name = XSD::QName.new(targetnamespace, value.source)
+ when TargetNamespaceAttrName
+ self.targetnamespace = value.source
+ else
+ nil
+ end
+ end
+
+ def self.parse_element(element)
+ if element == DefinitionsName
+ Definitions.new
+ else
+ nil
+ end
+ end
+
+private
+
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/wsdl/documentation.rb b/ruby_1_8_5/lib/wsdl/documentation.rb
new file mode 100644
index 0000000000..3a7fd7d23e
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/documentation.rb
@@ -0,0 +1,32 @@
+# WSDL4R - WSDL SOAP documentation element.
+# Copyright (C) 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+
+
+class Documentation < Info
+ def initialize
+ super
+ end
+
+ def parse_element(element)
+ # Accepts any element.
+ self
+ end
+
+ def parse_attr(attr, value)
+ # Accepts any attribute.
+ true
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/wsdl/import.rb b/ruby_1_8_5/lib/wsdl/import.rb
new file mode 100644
index 0000000000..faf60871a5
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/import.rb
@@ -0,0 +1,80 @@
+# WSDL4R - WSDL import definition.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+require 'wsdl/importer'
+
+
+module WSDL
+
+
+class Import < Info
+ attr_reader :namespace
+ attr_reader :location
+ attr_reader :content
+
+ def initialize
+ super
+ @namespace = nil
+ @location = nil
+ @content = nil
+ @web_client = nil
+ end
+
+ def parse_element(element)
+ case element
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NamespaceAttrName
+ @namespace = value.source
+ if @content
+ @content.targetnamespace = @namespace
+ end
+ @namespace
+ when LocationAttrName
+ @location = URI.parse(value.source)
+ if @location.relative? and !parent.location.nil? and
+ !parent.location.relative?
+ @location = parent.location + @location
+ end
+ if root.importedschema.key?(@location)
+ @content = root.importedschema[@location]
+ else
+ root.importedschema[@location] = nil # placeholder
+ @content = import(@location)
+ if @content.is_a?(Definitions)
+ @content.root = root
+ if @namespace
+ @content.targetnamespace = @namespace
+ end
+ end
+ root.importedschema[@location] = @content
+ end
+ @location
+ else
+ nil
+ end
+ end
+
+private
+
+ def import(location)
+ Importer.import(location, root)
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/wsdl/importer.rb b/ruby_1_8_5/lib/wsdl/importer.rb
new file mode 100644
index 0000000000..481bd81b25
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/importer.rb
@@ -0,0 +1,38 @@
+# WSDL4R - WSDL importer library.
+# Copyright (C) 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/xmlSchema/importer'
+require 'wsdl/parser'
+
+
+module WSDL
+
+
+class Importer < WSDL::XMLSchema::Importer
+ def self.import(location, originalroot = nil)
+ new.import(location, originalroot)
+ end
+
+private
+
+ def parse(content, location, originalroot)
+ opt = {
+ :location => location,
+ :originalroot => originalroot
+ }
+ begin
+ WSDL::Parser.new(opt).parse(content)
+ rescue WSDL::Parser::ParseError
+ super(content, location, originalroot)
+ end
+ end
+
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/wsdl/info.rb b/ruby_1_8_5/lib/wsdl/info.rb
new file mode 100644
index 0000000000..ffd90390a4
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/info.rb
@@ -0,0 +1,39 @@
+# WSDL4R - WSDL information base.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+module WSDL
+
+
+class Info
+ attr_accessor :root
+ attr_accessor :parent
+ attr_accessor :id
+
+ def initialize
+ @root = nil
+ @parent = nil
+ @id = nil
+ end
+
+ def inspect
+ if self.respond_to?(:name)
+ sprintf("#<%s:0x%x %s>", self.class.name, __id__, self.name)
+ else
+ sprintf("#<%s:0x%x>", self.class.name, __id__)
+ end
+ end
+
+ def parse_element(element); end # abstract
+
+ def parse_attr(attr, value); end # abstract
+
+ def parse_epilogue; end # abstract
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/wsdl/message.rb b/ruby_1_8_5/lib/wsdl/message.rb
new file mode 100644
index 0000000000..cecc602da3
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/message.rb
@@ -0,0 +1,54 @@
+# WSDL4R - WSDL message definition.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+
+
+class Message < Info
+ attr_reader :name # required
+ attr_reader :parts
+
+ def initialize
+ super
+ @name = nil
+ @parts = []
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def parse_element(element)
+ case element
+ when PartName
+ o = Part.new
+ @parts << o
+ o
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NameAttrName
+ @name = XSD::QName.new(parent.targetnamespace, value.source)
+ else
+ nil
+ end
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/wsdl/operation.rb b/ruby_1_8_5/lib/wsdl/operation.rb
new file mode 100644
index 0000000000..fb7f4a80f4
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/operation.rb
@@ -0,0 +1,130 @@
+# WSDL4R - WSDL operation definition.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+
+
+class Operation < Info
+ class NameInfo
+ attr_reader :op_name
+ attr_reader :optype_name
+ attr_reader :parts
+ def initialize(op_name, optype_name, parts)
+ @op_name = op_name
+ @optype_name = optype_name
+ @parts = parts
+ end
+ end
+
+ attr_reader :name # required
+ attr_reader :parameter_order # optional
+ attr_reader :input
+ attr_reader :output
+ attr_reader :fault
+ attr_reader :type # required
+
+ def initialize
+ super
+ @name = nil
+ @type = nil
+ @parameter_order = nil
+ @input = nil
+ @output = nil
+ @fault = []
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def input_info
+ typename = input.find_message.name
+ NameInfo.new(@name, typename, inputparts)
+ end
+
+ def output_info
+ typename = output.find_message.name
+ NameInfo.new(@name, typename, outputparts)
+ end
+
+ def inputparts
+ sort_parts(input.find_message.parts)
+ end
+
+ def inputname
+ XSD::QName.new(targetnamespace, input.name ? input.name.name : @name.name)
+ end
+
+ def outputparts
+ sort_parts(output.find_message.parts)
+ end
+
+ def outputname
+ XSD::QName.new(targetnamespace,
+ output.name ? output.name.name : @name.name + 'Response')
+ end
+
+ def parse_element(element)
+ case element
+ when InputName
+ o = Param.new
+ @input = o
+ o
+ when OutputName
+ o = Param.new
+ @output = o
+ o
+ when FaultName
+ o = Param.new
+ @fault << o
+ o
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NameAttrName
+ @name = XSD::QName.new(targetnamespace, value.source)
+ when TypeAttrName
+ @type = value
+ when ParameterOrderAttrName
+ @parameter_order = value.source.split(/\s+/)
+ else
+ nil
+ end
+ end
+
+private
+
+ def sort_parts(parts)
+ return parts.dup unless parameter_order
+ result = []
+ parameter_order.each do |orderitem|
+ if (ele = parts.find { |part| part.name == orderitem })
+ result << ele
+ end
+ end
+ if result.length == 0
+ return parts.dup
+ end
+ # result length can be shorter than parts's.
+ # return part must not be a part of the parameterOrder.
+ result
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/wsdl/operationBinding.rb b/ruby_1_8_5/lib/wsdl/operationBinding.rb
new file mode 100644
index 0000000000..c2b8cd6591
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/operationBinding.rb
@@ -0,0 +1,108 @@
+# WSDL4R - WSDL bound operation definition.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+
+
+class OperationBinding < Info
+ attr_reader :name # required
+ attr_reader :input
+ attr_reader :output
+ attr_reader :fault
+ attr_reader :soapoperation
+
+ def initialize
+ super
+ @name = nil
+ @input = nil
+ @output = nil
+ @fault = []
+ @soapoperation = nil
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def porttype
+ root.porttype(parent.type)
+ end
+
+ def find_operation
+ porttype.operations[@name] or raise RuntimeError.new("#{@name} not found")
+ end
+
+ def soapoperation_name
+ if @soapoperation
+ @soapoperation.input_info.op_name
+ else
+ find_operation.name
+ end
+ end
+
+ def soapoperation_style
+ style = nil
+ if @soapoperation
+ style = @soapoperation.operation_style
+ elsif parent.soapbinding
+ style = parent.soapbinding.style
+ else
+ raise TypeError.new("operation style definition not found")
+ end
+ style || :document
+ end
+
+ def soapaction
+ if @soapoperation
+ @soapoperation.soapaction
+ else
+ nil
+ end
+ end
+
+ def parse_element(element)
+ case element
+ when InputName
+ o = Param.new
+ @input = o
+ o
+ when OutputName
+ o = Param.new
+ @output = o
+ o
+ when FaultName
+ o = Param.new
+ @fault << o
+ o
+ when SOAPOperationName
+ o = WSDL::SOAP::Operation.new
+ @soapoperation = o
+ o
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NameAttrName
+ @name = XSD::QName.new(targetnamespace, value.source)
+ else
+ nil
+ end
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/wsdl/param.rb b/ruby_1_8_5/lib/wsdl/param.rb
new file mode 100644
index 0000000000..b6836b7def
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/param.rb
@@ -0,0 +1,85 @@
+# WSDL4R - WSDL param definition.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+
+
+class Param < Info
+ attr_reader :message # required
+ attr_reader :name # optional but required for fault.
+ attr_reader :soapbody
+ attr_reader :soapheader
+ attr_reader :soapfault
+
+ def initialize
+ super
+ @message = nil
+ @name = nil
+ @soapbody = nil
+ @soapheader = []
+ @soapfault = nil
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def find_message
+ root.message(@message) or raise RuntimeError.new("#{@message} not found")
+ end
+
+ def soapbody_use
+ if @soapbody
+ @soapbody.use || :literal
+ else
+ raise RuntimeError.new("soap:body not found")
+ end
+ end
+
+ def parse_element(element)
+ case element
+ when SOAPBodyName
+ o = WSDL::SOAP::Body.new
+ @soapbody = o
+ o
+ when SOAPHeaderName
+ o = WSDL::SOAP::Header.new
+ @soapheader << o
+ o
+ when SOAPFaultName
+ o = WSDL::SOAP::Fault.new
+ @soap_fault = o
+ o
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when MessageAttrName
+ if value.namespace.nil?
+ value = XSD::QName.new(targetnamespace, value.source)
+ end
+ @message = value
+ when NameAttrName
+ @name = XSD::QName.new(targetnamespace, value.source)
+ else
+ nil
+ end
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/wsdl/parser.rb b/ruby_1_8_5/lib/wsdl/parser.rb
new file mode 100644
index 0000000000..f96e96ee2a
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/parser.rb
@@ -0,0 +1,163 @@
+# WSDL4R - WSDL XML Instance parser library.
+# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/qname'
+require 'xsd/ns'
+require 'xsd/charset'
+require 'xsd/datatypes'
+require 'xsd/xmlparser'
+require 'wsdl/wsdl'
+require 'wsdl/data'
+require 'wsdl/xmlSchema/data'
+require 'wsdl/soap/data'
+
+
+module WSDL
+
+
+class Parser
+ include WSDL
+
+ class ParseError < Error; end
+ class FormatDecodeError < ParseError; end
+ class UnknownElementError < FormatDecodeError; end
+ class UnknownAttributeError < FormatDecodeError; end
+ class UnexpectedElementError < FormatDecodeError; end
+ class ElementConstraintError < FormatDecodeError; end
+ class AttributeConstraintError < FormatDecodeError; end
+
+private
+
+ class ParseFrame
+ attr_reader :ns
+ attr_reader :name
+ attr_accessor :node
+
+ private
+
+ def initialize(ns, name, node)
+ @ns = ns
+ @name = name
+ @node = node
+ end
+ end
+
+public
+
+ def initialize(opt = {})
+ @parser = XSD::XMLParser.create_parser(self, opt)
+ @parsestack = nil
+ @lastnode = nil
+ @ignored = {}
+ @location = opt[:location]
+ @originalroot = opt[:originalroot]
+ end
+
+ def parse(string_or_readable)
+ @parsestack = []
+ @lastnode = nil
+ @textbuf = ''
+ @parser.do_parse(string_or_readable)
+ @lastnode
+ end
+
+ def charset
+ @parser.charset
+ end
+
+ def start_element(name, attrs)
+ lastframe = @parsestack.last
+ ns = parent = nil
+ if lastframe
+ ns = lastframe.ns.clone_ns
+ parent = lastframe.node
+ else
+ ns = XSD::NS.new
+ parent = nil
+ end
+ attrs = XSD::XMLParser.filter_ns(ns, attrs)
+ node = decode_tag(ns, name, attrs, parent)
+ @parsestack << ParseFrame.new(ns, name, node)
+ end
+
+ def characters(text)
+ lastframe = @parsestack.last
+ if lastframe
+ # Need not to be cloned because character does not have attr.
+ ns = lastframe.ns
+ decode_text(ns, text)
+ else
+ p text if $DEBUG
+ end
+ end
+
+ def end_element(name)
+ lastframe = @parsestack.pop
+ unless name == lastframe.name
+ raise UnexpectedElementError.new("closing element name '#{name}' does not match with opening element '#{lastframe.name}'")
+ end
+ decode_tag_end(lastframe.ns, lastframe.node)
+ @lastnode = lastframe.node
+ end
+
+private
+
+ def decode_tag(ns, name, attrs, parent)
+ o = nil
+ elename = ns.parse(name)
+ if !parent
+ if elename == DefinitionsName
+ o = Definitions.parse_element(elename)
+ o.location = @location
+ else
+ raise UnknownElementError.new("unknown element: #{elename}")
+ end
+ o.root = @originalroot if @originalroot # o.root = o otherwise
+ else
+ if elename == XMLSchema::AnnotationName
+ # only the first annotation element is allowed for each xsd element.
+ o = XMLSchema::Annotation.new
+ else
+ o = parent.parse_element(elename)
+ end
+ unless o
+ unless @ignored.key?(elename)
+ warn("ignored element: #{elename}")
+ @ignored[elename] = elename
+ end
+ o = Documentation.new # which accepts any element.
+ end
+ # node could be a pseudo element. pseudo element has its own parent.
+ o.root = parent.root
+ o.parent = parent if o.parent.nil?
+ end
+ attrs.each do |key, value|
+ attr_ele = ns.parse(key, true)
+ value_ele = ns.parse(value, true)
+ value_ele.source = value # for recovery; value may not be a QName
+ unless o.parse_attr(attr_ele, value_ele)
+ unless @ignored.key?(attr_ele)
+ warn("ignored attr: #{attr_ele}")
+ @ignored[attr_ele] = attr_ele
+ end
+ end
+ end
+ o
+ end
+
+ def decode_tag_end(ns, node)
+ node.parse_epilogue
+ end
+
+ def decode_text(ns, text)
+ @textbuf << text
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/wsdl/part.rb b/ruby_1_8_5/lib/wsdl/part.rb
new file mode 100644
index 0000000000..5dafd4ee73
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/part.rb
@@ -0,0 +1,52 @@
+# WSDL4R - WSDL part definition.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+
+
+class Part < Info
+ attr_reader :name # required
+ attr_reader :element # optional
+ attr_reader :type # optional
+
+ def initialize
+ super
+ @name = nil
+ @element = nil
+ @type = nil
+ end
+
+ def parse_element(element)
+ case element
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NameAttrName
+ @name = value.source
+ when ElementAttrName
+ @element = value
+ when TypeAttrName
+ @type = value
+ else
+ nil
+ end
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/wsdl/port.rb b/ruby_1_8_5/lib/wsdl/port.rb
new file mode 100644
index 0000000000..883cc3232d
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/port.rb
@@ -0,0 +1,84 @@
+# WSDL4R - WSDL port definition.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+
+
+class Port < Info
+ attr_reader :name # required
+ attr_reader :binding # required
+ attr_reader :soap_address
+
+ def initialize
+ super
+ @name = nil
+ @binding = nil
+ @soap_address = nil
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def porttype
+ root.porttype(find_binding.type)
+ end
+
+ def find_binding
+ root.binding(@binding) or raise RuntimeError.new("#{@binding} not found")
+ end
+
+ def inputoperation_map
+ result = {}
+ find_binding.operations.each do |op_bind|
+ op_info = op_bind.soapoperation.input_info
+ result[op_info.op_name] = op_info
+ end
+ result
+ end
+
+ def outputoperation_map
+ result = {}
+ find_binding.operations.each do |op_bind|
+ op_info = op_bind.soapoperation.output_info
+ result[op_info.op_name] = op_info
+ end
+ result
+ end
+
+ def parse_element(element)
+ case element
+ when SOAPAddressName
+ o = WSDL::SOAP::Address.new
+ @soap_address = o
+ o
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NameAttrName
+ @name = XSD::QName.new(targetnamespace, value.source)
+ when BindingAttrName
+ @binding = value
+ else
+ nil
+ end
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/wsdl/portType.rb b/ruby_1_8_5/lib/wsdl/portType.rb
new file mode 100644
index 0000000000..03e37690a8
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/portType.rb
@@ -0,0 +1,73 @@
+# WSDL4R - WSDL portType definition.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+require 'xsd/namedelements'
+
+
+module WSDL
+
+
+class PortType < Info
+ attr_reader :name # required
+ attr_reader :operations
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def initialize
+ super
+ @name = nil
+ @operations = XSD::NamedElements.new
+ end
+
+ def find_binding
+ root.bindings.find { |item| item.type == @name } or
+ raise RuntimeError.new("#{@name} not found")
+ end
+
+ def locations
+ bind_name = find_binding.name
+ result = []
+ root.services.each do |service|
+ service.ports.each do |port|
+ if port.binding == bind_name
+ result << port.soap_address.location if port.soap_address
+ end
+ end
+ end
+ result
+ end
+
+ def parse_element(element)
+ case element
+ when OperationName
+ o = Operation.new
+ @operations << o
+ o
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NameAttrName
+ @name = XSD::QName.new(targetnamespace, value.source)
+ else
+ nil
+ end
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/wsdl/service.rb b/ruby_1_8_5/lib/wsdl/service.rb
new file mode 100644
index 0000000000..652b127331
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/service.rb
@@ -0,0 +1,61 @@
+# WSDL4R - WSDL service definition.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+require 'xsd/namedelements'
+
+
+module WSDL
+
+
+class Service < Info
+ attr_reader :name # required
+ attr_reader :ports
+ attr_reader :soap_address
+
+ def initialize
+ super
+ @name = nil
+ @ports = XSD::NamedElements.new
+ @soap_address = nil
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def parse_element(element)
+ case element
+ when PortName
+ o = Port.new
+ @ports << o
+ o
+ when SOAPAddressName
+ o = WSDL::SOAP::Address.new
+ @soap_address = o
+ o
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NameAttrName
+ @name = XSD::QName.new(targetnamespace, value.source)
+ else
+ nil
+ end
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/wsdl/soap/address.rb b/ruby_1_8_5/lib/wsdl/soap/address.rb
new file mode 100644
index 0000000000..0b2b59caf0
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/soap/address.rb
@@ -0,0 +1,40 @@
+# WSDL4R - WSDL SOAP address definition.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+module SOAP
+
+
+class Address < Info
+ attr_reader :location
+
+ def initialize
+ super
+ @location = nil
+ end
+
+ def parse_element(element)
+ nil
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when LocationAttrName
+ @location = value.source
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/soap/binding.rb b/ruby_1_8_5/lib/wsdl/soap/binding.rb
new file mode 100644
index 0000000000..7e15a99701
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/soap/binding.rb
@@ -0,0 +1,49 @@
+# WSDL4R - WSDL SOAP binding definition.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+module SOAP
+
+
+class Binding < Info
+ attr_reader :style
+ attr_reader :transport
+
+ def initialize
+ super
+ @style = nil
+ @transport = nil
+ end
+
+ def parse_element(element)
+ nil
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when StyleAttrName
+ if ["document", "rpc"].include?(value.source)
+ @style = value.source.intern
+ else
+ raise Parser::AttributeConstraintError.new(
+ "Unexpected value #{ value }.")
+ end
+ when TransportAttrName
+ @transport = value.source
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/soap/body.rb b/ruby_1_8_5/lib/wsdl/soap/body.rb
new file mode 100644
index 0000000000..824f8121a2
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/soap/body.rb
@@ -0,0 +1,56 @@
+# WSDL4R - WSDL SOAP body definition.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+module SOAP
+
+
+class Body < Info
+ attr_reader :parts
+ attr_reader :use # required
+ attr_reader :encodingstyle
+ attr_reader :namespace
+
+ def initialize
+ super
+ @parts = nil
+ @use = nil
+ @encodingstyle = nil
+ @namespace = nil
+ end
+
+ def parse_element(element)
+ nil
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when PartsAttrName
+ @parts = value.source
+ when UseAttrName
+ if ['literal', 'encoded'].include?(value.source)
+ @use = value.source.intern
+ else
+ raise RuntimeError.new("unknown use of soap:body: #{value.source}")
+ end
+ when EncodingStyleAttrName
+ @encodingstyle = value.source
+ when NamespaceAttrName
+ @namespace = value.source
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/soap/cgiStubCreator.rb b/ruby_1_8_5/lib/wsdl/soap/cgiStubCreator.rb
new file mode 100644
index 0000000000..2c4dff2f62
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/soap/cgiStubCreator.rb
@@ -0,0 +1,76 @@
+# WSDL4R - Creating CGI stub code from WSDL.
+# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+require 'wsdl/soap/mappingRegistryCreator'
+require 'wsdl/soap/methodDefCreator'
+require 'wsdl/soap/classDefCreatorSupport'
+
+
+module WSDL
+module SOAP
+
+
+class CGIStubCreator
+ include ClassDefCreatorSupport
+
+ attr_reader :definitions
+
+ def initialize(definitions)
+ @definitions = definitions
+ end
+
+ def dump(service_name)
+ warn("CGI stub can have only 1 port. Creating stub for the first port... Rests are ignored.")
+ port = @definitions.service(service_name).ports[0]
+ dump_porttype(port.porttype.name)
+ end
+
+private
+
+ def dump_porttype(name)
+ class_name = create_class_name(name)
+ methoddef, types = MethodDefCreator.new(@definitions).dump(name)
+ mr_creator = MappingRegistryCreator.new(@definitions)
+ c1 = XSD::CodeGen::ClassDef.new(class_name)
+ c1.def_require("soap/rpc/cgistub")
+ c1.def_require("soap/mapping/registry")
+ c1.def_const("MappingRegistry", "::SOAP::Mapping::Registry.new")
+ c1.def_code(mr_creator.dump(types))
+ c1.def_code <<-EOD
+Methods = [
+#{methoddef.gsub(/^/, " ")}
+]
+ EOD
+ c2 = XSD::CodeGen::ClassDef.new(class_name + "App",
+ "::SOAP::RPC::CGIStub")
+ c2.def_method("initialize", "*arg") do
+ <<-EOD
+ super(*arg)
+ servant = #{class_name}.new
+ #{class_name}::Methods.each do |definitions|
+ opt = definitions.last
+ if opt[:request_style] == :document
+ @router.add_document_operation(servant, *definitions)
+ else
+ @router.add_rpc_operation(servant, *definitions)
+ end
+ end
+ self.mapping_registry = #{class_name}::MappingRegistry
+ self.level = Logger::Severity::ERROR
+ EOD
+ end
+ c1.dump + "\n" + c2.dump + format(<<-EOD)
+ #{class_name}App.new('app', nil).start
+ EOD
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/soap/classDefCreator.rb b/ruby_1_8_5/lib/wsdl/soap/classDefCreator.rb
new file mode 100644
index 0000000000..aeb67c0613
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/soap/classDefCreator.rb
@@ -0,0 +1,314 @@
+# WSDL4R - Creating class definition from WSDL
+# Copyright (C) 2002, 2003, 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/data'
+require 'wsdl/soap/classDefCreatorSupport'
+require 'xsd/codegen'
+
+
+module WSDL
+module SOAP
+
+
+class ClassDefCreator
+ include ClassDefCreatorSupport
+
+ def initialize(definitions)
+ @elements = definitions.collect_elements
+ @simpletypes = definitions.collect_simpletypes
+ @complextypes = definitions.collect_complextypes
+ @faulttypes = nil
+ if definitions.respond_to?(:collect_faulttypes)
+ @faulttypes = definitions.collect_faulttypes
+ end
+ end
+
+ def dump(type = nil)
+ result = "require 'xsd/qname'\n"
+ if type
+ result = dump_classdef(type.name, type)
+ else
+ str = dump_element
+ unless str.empty?
+ result << "\n" unless result.empty?
+ result << str
+ end
+ str = dump_complextype
+ unless str.empty?
+ result << "\n" unless result.empty?
+ result << str
+ end
+ str = dump_simpletype
+ unless str.empty?
+ result << "\n" unless result.empty?
+ result << str
+ end
+ end
+ result
+ end
+
+private
+
+ def dump_element
+ @elements.collect { |ele|
+ if ele.local_complextype
+ dump_classdef(ele.name, ele.local_complextype,
+ ele.elementform == 'qualified')
+ elsif ele.local_simpletype
+ dump_simpletypedef(ele.name, ele.local_simpletype)
+ else
+ nil
+ end
+ }.compact.join("\n")
+ end
+
+ def dump_simpletype
+ @simpletypes.collect { |type|
+ dump_simpletypedef(type.name, type)
+ }.compact.join("\n")
+ end
+
+ def dump_complextype
+ @complextypes.collect { |type|
+ case type.compoundtype
+ when :TYPE_STRUCT, :TYPE_EMPTY
+ dump_classdef(type.name, type)
+ when :TYPE_ARRAY
+ dump_arraydef(type)
+ when :TYPE_SIMPLE
+ dump_simpleclassdef(type)
+ when :TYPE_MAP
+ # mapped as a general Hash
+ nil
+ else
+ raise RuntimeError.new(
+ "unknown kind of complexContent: #{type.compoundtype}")
+ end
+ }.compact.join("\n")
+ end
+
+ def dump_simpletypedef(qname, simpletype)
+ if !simpletype.restriction or simpletype.restriction.enumeration.empty?
+ return nil
+ end
+ c = XSD::CodeGen::ModuleDef.new(create_class_name(qname))
+ c.comment = "#{qname}"
+ const = {}
+ simpletype.restriction.enumeration.each do |value|
+ constname = safeconstname(value)
+ const[constname] ||= 0
+ if (const[constname] += 1) > 1
+ constname += "_#{const[constname]}"
+ end
+ c.def_const(constname, ndq(value))
+ end
+ c.dump
+ end
+
+ def dump_simpleclassdef(type_or_element)
+ qname = type_or_element.name
+ base = create_class_name(type_or_element.simplecontent.base)
+ c = XSD::CodeGen::ClassDef.new(create_class_name(qname), base)
+ c.comment = "#{qname}"
+ c.dump
+ end
+
+ def dump_classdef(qname, typedef, qualified = false)
+ if @faulttypes and @faulttypes.index(qname)
+ c = XSD::CodeGen::ClassDef.new(create_class_name(qname),
+ '::StandardError')
+ else
+ c = XSD::CodeGen::ClassDef.new(create_class_name(qname))
+ end
+ c.comment = "#{qname}"
+ c.def_classvar('schema_type', ndq(qname.name))
+ c.def_classvar('schema_ns', ndq(qname.namespace))
+ c.def_classvar('schema_qualified', dq('true')) if qualified
+ schema_element = []
+ init_lines = ''
+ params = []
+ typedef.each_element do |element|
+ if element.type == XSD::AnyTypeName
+ type = nil
+ elsif klass = element_basetype(element)
+ type = klass.name
+ elsif element.type
+ type = create_class_name(element.type)
+ else
+ type = nil # means anyType.
+ # do we define a class for local complexType from it's name?
+ # type = create_class_name(element.name)
+ # <element>
+ # <complexType>
+ # <seq...>
+ # </complexType>
+ # </element>
+ end
+ name = name_element(element).name
+ attrname = safemethodname?(name) ? name : safemethodname(name)
+ varname = safevarname(name)
+ c.def_attr(attrname, true, varname)
+ init_lines << "@#{varname} = #{varname}\n"
+ if element.map_as_array?
+ params << "#{varname} = []"
+ type << '[]' if type
+ else
+ params << "#{varname} = nil"
+ end
+ # nil means @@schema_ns + varname
+ eleqname =
+ (varname == name && element.name.namespace == qname.namespace) ?
+ nil : element.name
+ schema_element << [varname, eleqname, type]
+ end
+ unless typedef.attributes.empty?
+ define_attribute(c, typedef.attributes)
+ init_lines << "@__xmlattr = {}\n"
+ end
+ c.def_classvar('schema_element',
+ '[' +
+ schema_element.collect { |varname, name, type|
+ '[' +
+ (
+ if name
+ varname.dump + ', [' + ndq(type) + ', ' + dqname(name) + ']'
+ else
+ varname.dump + ', ' + ndq(type)
+ end
+ ) +
+ ']'
+ }.join(', ') +
+ ']'
+ )
+ c.def_method('initialize', *params) do
+ init_lines
+ end
+ c.dump
+ end
+
+ def element_basetype(ele)
+ if klass = basetype_class(ele.type)
+ klass
+ elsif ele.local_simpletype
+ basetype_class(ele.local_simpletype.base)
+ else
+ nil
+ end
+ end
+
+ def attribute_basetype(attr)
+ if klass = basetype_class(attr.type)
+ klass
+ elsif attr.local_simpletype
+ basetype_class(attr.local_simpletype.base)
+ else
+ nil
+ end
+ end
+
+ def basetype_class(type)
+ return nil if type.nil?
+ if simpletype = @simpletypes[type]
+ basetype_mapped_class(simpletype.base)
+ else
+ basetype_mapped_class(type)
+ end
+ end
+
+ def define_attribute(c, attributes)
+ schema_attribute = []
+ attributes.each do |attribute|
+ name = name_attribute(attribute)
+ if klass = attribute_basetype(attribute)
+ type = klass.name
+ else
+ type = nil
+ end
+ methodname = safemethodname('xmlattr_' + name.name)
+ c.def_method(methodname) do <<-__EOD__
+ (@__xmlattr ||= {})[#{dqname(name)}]
+ __EOD__
+ end
+ c.def_method(methodname + '=', 'value') do <<-__EOD__
+ (@__xmlattr ||= {})[#{dqname(name)}] = value
+ __EOD__
+ end
+ schema_attribute << [name, type]
+ end
+ c.def_classvar('schema_attribute',
+ '{' +
+ schema_attribute.collect { |name, type|
+ dqname(name) + ' => ' + ndq(type)
+ }.join(', ') +
+ '}'
+ )
+ end
+
+ def name_element(element)
+ return element.name if element.name
+ return element.ref if element.ref
+ raise RuntimeError.new("cannot define name of #{element}")
+ end
+
+ def name_attribute(attribute)
+ return attribute.name if attribute.name
+ return attribute.ref if attribute.ref
+ raise RuntimeError.new("cannot define name of #{attribute}")
+ end
+
+ DEFAULT_ITEM_NAME = XSD::QName.new(nil, 'item')
+
+ def dump_arraydef(complextype)
+ qname = complextype.name
+ c = XSD::CodeGen::ClassDef.new(create_class_name(qname), '::Array')
+ c.comment = "#{qname}"
+ child_type = complextype.child_type
+ c.def_classvar('schema_type', ndq(child_type.name))
+ c.def_classvar('schema_ns', ndq(child_type.namespace))
+ child_element = complextype.find_aryelement
+ schema_element = []
+ if child_type == XSD::AnyTypeName
+ type = nil
+ elsif child_element and (klass = element_basetype(child_element))
+ type = klass.name
+ elsif child_type
+ type = create_class_name(child_type)
+ else
+ type = nil
+ end
+ if child_element
+ if child_element.map_as_array?
+ type << '[]' if type
+ end
+ child_element_name = child_element.name
+ else
+ child_element_name = DEFAULT_ITEM_NAME
+ end
+ schema_element << [child_element_name.name, child_element_name, type]
+ c.def_classvar('schema_element',
+ '[' +
+ schema_element.collect { |varname, name, type|
+ '[' +
+ (
+ if name
+ varname.dump + ', [' + ndq(type) + ', ' + dqname(name) + ']'
+ else
+ varname.dump + ', ' + ndq(type)
+ end
+ ) +
+ ']'
+ }.join(', ') +
+ ']'
+ )
+ c.dump
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/soap/classDefCreatorSupport.rb b/ruby_1_8_5/lib/wsdl/soap/classDefCreatorSupport.rb
new file mode 100644
index 0000000000..8f335653c8
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/soap/classDefCreatorSupport.rb
@@ -0,0 +1,126 @@
+# WSDL4R - Creating class code support from WSDL.
+# Copyright (C) 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+require 'soap/mapping'
+require 'soap/mapping/typeMap'
+require 'xsd/codegen/gensupport'
+
+
+module WSDL
+module SOAP
+
+
+module ClassDefCreatorSupport
+ include XSD::CodeGen::GenSupport
+
+ def create_class_name(qname)
+ if klass = basetype_mapped_class(qname)
+ ::SOAP::Mapping::DefaultRegistry.find_mapped_obj_class(klass).name
+ else
+ safeconstname(qname.name)
+ end
+ end
+
+ def basetype_mapped_class(name)
+ ::SOAP::TypeMap[name]
+ end
+
+ def dump_method_signature(operation)
+ name = operation.name.name
+ input = operation.input
+ output = operation.output
+ fault = operation.fault
+ signature = "#{ name }#{ dump_inputparam(input) }"
+ str = <<__EOD__
+# SYNOPSIS
+# #{name}#{dump_inputparam(input)}
+#
+# ARGS
+#{dump_inout_type(input).chomp}
+#
+# RETURNS
+#{dump_inout_type(output).chomp}
+#
+__EOD__
+ unless fault.empty?
+ faultstr = (fault.collect { |f| dump_inout_type(f).chomp }).join(', ')
+ str <<<<__EOD__
+# RAISES
+# #{faultstr}
+#
+__EOD__
+ end
+ str
+ end
+
+ def dq(ele)
+ ele.dump
+ end
+
+ def ndq(ele)
+ ele.nil? ? 'nil' : dq(ele)
+ end
+
+ def sym(ele)
+ ':' + ele
+ end
+
+ def dqname(qname)
+ qname.dump
+ end
+
+private
+
+ def dump_inout_type(param)
+ if param
+ message = param.find_message
+ params = ""
+ message.parts.each do |part|
+ name = safevarname(part.name)
+ if part.type
+ typename = safeconstname(part.type.name)
+ params << add_at("# #{name}", "#{typename} - #{part.type}\n", 20)
+ elsif part.element
+ typename = safeconstname(part.element.name)
+ params << add_at("# #{name}", "#{typename} - #{part.element}\n", 20)
+ end
+ end
+ unless params.empty?
+ return params
+ end
+ end
+ "# N/A\n"
+ end
+
+ def dump_inputparam(input)
+ message = input.find_message
+ params = ""
+ message.parts.each do |part|
+ params << ", " unless params.empty?
+ params << safevarname(part.name)
+ end
+ if params.empty?
+ ""
+ else
+ "(#{ params })"
+ end
+ end
+
+ def add_at(base, str, pos)
+ if base.size >= pos
+ base + ' ' + str
+ else
+ base + ' ' * (pos - base.size) + str
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/soap/clientSkeltonCreator.rb b/ruby_1_8_5/lib/wsdl/soap/clientSkeltonCreator.rb
new file mode 100644
index 0000000000..916f0d4dc0
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/soap/clientSkeltonCreator.rb
@@ -0,0 +1,78 @@
+# WSDL4R - Creating client skelton code from WSDL.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+require 'wsdl/soap/classDefCreatorSupport'
+
+
+module WSDL
+module SOAP
+
+
+class ClientSkeltonCreator
+ include ClassDefCreatorSupport
+
+ attr_reader :definitions
+
+ def initialize(definitions)
+ @definitions = definitions
+ end
+
+ def dump(service_name)
+ result = ""
+ @definitions.service(service_name).ports.each do |port|
+ result << dump_porttype(port.porttype.name)
+ result << "\n"
+ end
+ result
+ end
+
+private
+
+ def dump_porttype(name)
+ drv_name = create_class_name(name)
+
+ result = ""
+ result << <<__EOD__
+endpoint_url = ARGV.shift
+obj = #{ drv_name }.new(endpoint_url)
+
+# run ruby with -d to see SOAP wiredumps.
+obj.wiredump_dev = STDERR if $DEBUG
+
+__EOD__
+ @definitions.porttype(name).operations.each do |operation|
+ result << dump_method_signature(operation)
+ result << dump_input_init(operation.input) << "\n"
+ result << dump_operation(operation) << "\n\n"
+ end
+ result
+ end
+
+ def dump_operation(operation)
+ name = operation.name
+ input = operation.input
+ "puts obj.#{ safemethodname(name.name) }#{ dump_inputparam(input) }"
+ end
+
+ def dump_input_init(input)
+ result = input.find_message.parts.collect { |part|
+ safevarname(part.name)
+ }.join(" = ")
+ if result.empty?
+ ""
+ else
+ result << " = nil"
+ end
+ result
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/soap/complexType.rb b/ruby_1_8_5/lib/wsdl/soap/complexType.rb
new file mode 100644
index 0000000000..b2e13d0564
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/soap/complexType.rb
@@ -0,0 +1,161 @@
+# WSDL4R - SOAP complexType definition for WSDL.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/xmlSchema/complexType'
+require 'soap/mapping'
+
+
+module WSDL
+module XMLSchema
+
+
+class ComplexType < Info
+ def compoundtype
+ @compoundtype ||= check_type
+ end
+
+ def check_type
+ if content
+ if attributes.empty? and
+ content.elements.size == 1 and content.elements[0].maxoccurs != '1'
+ if name == ::SOAP::Mapping::MapQName
+ :TYPE_MAP
+ else
+ :TYPE_ARRAY
+ end
+ else
+ :TYPE_STRUCT
+ end
+ elsif complexcontent
+ if complexcontent.base == ::SOAP::ValueArrayName
+ :TYPE_ARRAY
+ else
+ complexcontent.basetype.check_type
+ end
+ elsif simplecontent
+ :TYPE_SIMPLE
+ elsif !attributes.empty?
+ :TYPE_STRUCT
+ else # empty complexType definition (seen in partner.wsdl of salesforce)
+ :TYPE_EMPTY
+ end
+ end
+
+ def child_type(name = nil)
+ case compoundtype
+ when :TYPE_STRUCT
+ if ele = find_element(name)
+ ele.type
+ elsif ele = find_element_by_name(name.name)
+ ele.type
+ end
+ when :TYPE_ARRAY
+ @contenttype ||= content_arytype
+ when :TYPE_MAP
+ item_ele = find_element_by_name("item") or
+ raise RuntimeError.new("'item' element not found in Map definition.")
+ content = item_ele.local_complextype or
+ raise RuntimeError.new("No complexType definition for 'item'.")
+ if ele = content.find_element(name)
+ ele.type
+ elsif ele = content.find_element_by_name(name.name)
+ ele.type
+ end
+ else
+ raise NotImplementedError.new("Unknown kind of complexType.")
+ end
+ end
+
+ def child_defined_complextype(name)
+ ele = nil
+ case compoundtype
+ when :TYPE_STRUCT, :TYPE_MAP
+ unless ele = find_element(name)
+ if name.namespace.nil?
+ ele = find_element_by_name(name.name)
+ end
+ end
+ when :TYPE_ARRAY
+ if content.elements.size == 1
+ ele = content.elements[0]
+ else
+ raise RuntimeError.new("Assert: must not reach.")
+ end
+ else
+ raise RuntimeError.new("Assert: Not implemented.")
+ end
+ unless ele
+ raise RuntimeError.new("Cannot find #{name} as a children of #{@name}.")
+ end
+ ele.local_complextype
+ end
+
+ def find_arytype
+ unless compoundtype == :TYPE_ARRAY
+ raise RuntimeError.new("Assert: not for array")
+ end
+ if complexcontent
+ complexcontent.attributes.each do |attribute|
+ if attribute.ref == ::SOAP::AttrArrayTypeName
+ return attribute.arytype
+ end
+ end
+ if check_array_content(complexcontent.content)
+ return element_simpletype(complexcontent.content.elements[0])
+ end
+ elsif check_array_content(content)
+ return element_simpletype(content.elements[0])
+ end
+ raise RuntimeError.new("Assert: Unknown array definition.")
+ end
+
+ def find_aryelement
+ unless compoundtype == :TYPE_ARRAY
+ raise RuntimeError.new("Assert: not for array")
+ end
+ if complexcontent
+ if check_array_content(complexcontent.content)
+ return complexcontent.content.elements[0]
+ end
+ elsif check_array_content(content)
+ return content.elements[0]
+ end
+ nil # use default item name
+ end
+
+private
+
+ def element_simpletype(element)
+ if element.type
+ element.type
+ elsif element.local_simpletype
+ element.local_simpletype.base
+ else
+ nil
+ end
+ end
+
+ def check_array_content(content)
+ content and content.elements.size == 1 and
+ content.elements[0].maxoccurs != '1'
+ end
+
+ def content_arytype
+ if arytype = find_arytype
+ ns = arytype.namespace
+ name = arytype.name.sub(/\[(?:,)*\]$/, '')
+ XSD::QName.new(ns, name)
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/soap/data.rb b/ruby_1_8_5/lib/wsdl/soap/data.rb
new file mode 100644
index 0000000000..48512d3751
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/soap/data.rb
@@ -0,0 +1,42 @@
+# WSDL4R - WSDL SOAP binding data definitions.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/qname'
+require 'wsdl/soap/definitions'
+require 'wsdl/soap/binding'
+require 'wsdl/soap/operation'
+require 'wsdl/soap/body'
+require 'wsdl/soap/element'
+require 'wsdl/soap/header'
+require 'wsdl/soap/headerfault'
+require 'wsdl/soap/fault'
+require 'wsdl/soap/address'
+require 'wsdl/soap/complexType'
+
+
+module WSDL
+module SOAP
+
+
+HeaderFaultName = XSD::QName.new(SOAPBindingNamespace, 'headerfault')
+
+LocationAttrName = XSD::QName.new(nil, 'location')
+StyleAttrName = XSD::QName.new(nil, 'style')
+TransportAttrName = XSD::QName.new(nil, 'transport')
+UseAttrName = XSD::QName.new(nil, 'use')
+PartsAttrName = XSD::QName.new(nil, 'parts')
+PartAttrName = XSD::QName.new(nil, 'part')
+NameAttrName = XSD::QName.new(nil, 'name')
+MessageAttrName = XSD::QName.new(nil, 'message')
+EncodingStyleAttrName = XSD::QName.new(nil, 'encodingStyle')
+NamespaceAttrName = XSD::QName.new(nil, 'namespace')
+SOAPActionAttrName = XSD::QName.new(nil, 'soapAction')
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/soap/definitions.rb b/ruby_1_8_5/lib/wsdl/soap/definitions.rb
new file mode 100644
index 0000000000..b014d5af6b
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/soap/definitions.rb
@@ -0,0 +1,149 @@
+# WSDL4R - WSDL additional definitions for SOAP.
+# Copyright (C) 2002-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+require 'xsd/namedelements'
+require 'soap/mapping'
+
+
+module WSDL
+
+
+class Definitions < Info
+ def self.soap_rpc_complextypes
+ types = XSD::NamedElements.new
+ types << array_complextype
+ types << fault_complextype
+ types << exception_complextype
+ types
+ end
+
+ def self.array_complextype
+ type = XMLSchema::ComplexType.new(::SOAP::ValueArrayName)
+ type.complexcontent = XMLSchema::ComplexContent.new
+ type.complexcontent.base = ::SOAP::ValueArrayName
+ attr = XMLSchema::Attribute.new
+ attr.ref = ::SOAP::AttrArrayTypeName
+ anytype = XSD::AnyTypeName.dup
+ anytype.name += '[]'
+ attr.arytype = anytype
+ type.complexcontent.attributes << attr
+ type
+ end
+
+=begin
+<xs:complexType name="Fault" final="extension">
+ <xs:sequence>
+ <xs:element name="faultcode" type="xs:QName" />
+ <xs:element name="faultstring" type="xs:string" />
+ <xs:element name="faultactor" type="xs:anyURI" minOccurs="0" />
+ <xs:element name="detail" type="tns:detail" minOccurs="0" />
+ </xs:sequence>
+</xs:complexType>
+=end
+ def self.fault_complextype
+ type = XMLSchema::ComplexType.new(::SOAP::EleFaultName)
+ faultcode = XMLSchema::Element.new(::SOAP::EleFaultCodeName, XSD::XSDQName::Type)
+ faultstring = XMLSchema::Element.new(::SOAP::EleFaultStringName, XSD::XSDString::Type)
+ faultactor = XMLSchema::Element.new(::SOAP::EleFaultActorName, XSD::XSDAnyURI::Type)
+ faultactor.minoccurs = 0
+ detail = XMLSchema::Element.new(::SOAP::EleFaultDetailName, XSD::AnyTypeName)
+ detail.minoccurs = 0
+ type.all_elements = [faultcode, faultstring, faultactor, detail]
+ type.final = 'extension'
+ type
+ end
+
+ def self.exception_complextype
+ type = XMLSchema::ComplexType.new(XSD::QName.new(
+ ::SOAP::Mapping::RubyCustomTypeNamespace, 'SOAPException'))
+ excn_name = XMLSchema::Element.new(XSD::QName.new(nil, 'excn_type_name'), XSD::XSDString::Type)
+ cause = XMLSchema::Element.new(XSD::QName.new(nil, 'cause'), XSD::AnyTypeName)
+ backtrace = XMLSchema::Element.new(XSD::QName.new(nil, 'backtrace'), ::SOAP::ValueArrayName)
+ message = XMLSchema::Element.new(XSD::QName.new(nil, 'message'), XSD::XSDString::Type)
+ type.all_elements = [excn_name, cause, backtrace, message]
+ type
+ end
+
+ def soap_rpc_complextypes(binding)
+ types = rpc_operation_complextypes(binding)
+ types + self.class.soap_rpc_complextypes
+ end
+
+ def collect_faulttypes
+ result = []
+ collect_fault_messages.each do |name|
+ faultparts = message(name).parts
+ if faultparts.size != 1
+ raise RuntimeError.new("expecting fault message to have only 1 part")
+ end
+ if result.index(faultparts[0].type).nil?
+ result << faultparts[0].type
+ end
+ end
+ result
+ end
+
+private
+
+ def collect_fault_messages
+ result = []
+ porttypes.each do |porttype|
+ porttype.operations.each do |operation|
+ operation.fault.each do |fault|
+ if result.index(fault.message).nil?
+ result << fault.message
+ end
+ end
+ end
+ end
+ result
+ end
+
+ def rpc_operation_complextypes(binding)
+ types = XSD::NamedElements.new
+ binding.operations.each do |op_bind|
+ if op_bind_rpc?(op_bind)
+ operation = op_bind.find_operation
+ if op_bind.input
+ type = XMLSchema::ComplexType.new(op_bind.soapoperation_name)
+ message = messages[operation.input.message]
+ type.sequence_elements = elements_from_message(message)
+ types << type
+ end
+ if op_bind.output
+ type = XMLSchema::ComplexType.new(operation.outputname)
+ message = messages[operation.output.message]
+ type.sequence_elements = elements_from_message(message)
+ types << type
+ end
+ end
+ end
+ types
+ end
+
+ def op_bind_rpc?(op_bind)
+ op_bind.soapoperation_style == :rpc
+ end
+
+ def elements_from_message(message)
+ message.parts.collect { |part|
+ if part.element
+ collect_elements[part.element]
+ elsif part.name.nil? or part.type.nil?
+ raise RuntimeError.new("part of a message must be an element or typed")
+ else
+ qname = XSD::QName.new(nil, part.name)
+ XMLSchema::Element.new(qname, part.type)
+ end
+ }
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/wsdl/soap/driverCreator.rb b/ruby_1_8_5/lib/wsdl/soap/driverCreator.rb
new file mode 100644
index 0000000000..eba7c158a2
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/soap/driverCreator.rb
@@ -0,0 +1,95 @@
+# WSDL4R - Creating driver code from WSDL.
+# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+require 'wsdl/soap/mappingRegistryCreator'
+require 'wsdl/soap/methodDefCreator'
+require 'wsdl/soap/classDefCreatorSupport'
+require 'xsd/codegen'
+
+
+module WSDL
+module SOAP
+
+
+class DriverCreator
+ include ClassDefCreatorSupport
+
+ attr_reader :definitions
+
+ def initialize(definitions)
+ @definitions = definitions
+ end
+
+ def dump(porttype = nil)
+ if porttype.nil?
+ result = ""
+ @definitions.porttypes.each do |type|
+ result << dump_porttype(type.name)
+ result << "\n"
+ end
+ else
+ result = dump_porttype(porttype)
+ end
+ result
+ end
+
+private
+
+ def dump_porttype(name)
+ class_name = create_class_name(name)
+ methoddef, types = MethodDefCreator.new(@definitions).dump(name)
+ mr_creator = MappingRegistryCreator.new(@definitions)
+ binding = @definitions.bindings.find { |item| item.type == name }
+ return '' unless binding.soapbinding # not a SOAP binding
+ address = @definitions.porttype(name).locations[0]
+
+ c = XSD::CodeGen::ClassDef.new(class_name, "::SOAP::RPC::Driver")
+ c.def_require("soap/rpc/driver")
+ c.def_const("MappingRegistry", "::SOAP::Mapping::Registry.new")
+ c.def_const("DefaultEndpointUrl", ndq(address))
+ c.def_code(mr_creator.dump(types))
+ c.def_code <<-EOD
+Methods = [
+#{methoddef.gsub(/^/, " ")}
+]
+ EOD
+ c.def_method("initialize", "endpoint_url = nil") do
+ <<-EOD
+ endpoint_url ||= DefaultEndpointUrl
+ super(endpoint_url, nil)
+ self.mapping_registry = MappingRegistry
+ init_methods
+ EOD
+ end
+ c.def_privatemethod("init_methods") do
+ <<-EOD
+ Methods.each do |definitions|
+ opt = definitions.last
+ if opt[:request_style] == :document
+ add_document_operation(*definitions)
+ else
+ add_rpc_operation(*definitions)
+ qname = definitions[0]
+ name = definitions[2]
+ if qname.name != name and qname.name.capitalize == name.capitalize
+ ::SOAP::Mapping.define_singleton_method(self, qname.name) do |*arg|
+ __send__(name, *arg)
+ end
+ end
+ end
+ end
+ EOD
+ end
+ c.dump
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/soap/element.rb b/ruby_1_8_5/lib/wsdl/soap/element.rb
new file mode 100644
index 0000000000..0fa6017c5b
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/soap/element.rb
@@ -0,0 +1,28 @@
+# WSDL4R - XMLSchema element definition for WSDL.
+# Copyright (C) 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/xmlSchema/element'
+
+
+module WSDL
+module XMLSchema
+
+
+class Element < Info
+ def map_as_array?
+ maxoccurs != '1'
+ end
+
+ def attributes
+ @local_complextype.attributes
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/soap/fault.rb b/ruby_1_8_5/lib/wsdl/soap/fault.rb
new file mode 100644
index 0000000000..2862b659ab
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/soap/fault.rb
@@ -0,0 +1,56 @@
+# WSDL4R - WSDL SOAP body definition.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+module SOAP
+
+
+class Fault < Info
+ attr_reader :name # required
+ attr_reader :use # required
+ attr_reader :encodingstyle
+ attr_reader :namespace
+
+ def initialize
+ super
+ @name = nil
+ @use = nil
+ @encodingstyle = nil
+ @namespace = nil
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def parse_element(element)
+ nil
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NameAttrName
+ @name = XSD::QName.new(targetnamespace, value.source)
+ when UseAttrName
+ @use = value.source
+ when EncodingStyleAttrName
+ @encodingstyle = value.source
+ when NamespaceAttrName
+ @namespace = value.source
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/soap/header.rb b/ruby_1_8_5/lib/wsdl/soap/header.rb
new file mode 100644
index 0000000000..8d7c4e9d70
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/soap/header.rb
@@ -0,0 +1,86 @@
+# WSDL4R - WSDL SOAP body definition.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+module SOAP
+
+
+class Header < Info
+ attr_reader :headerfault
+
+ attr_reader :message # required
+ attr_reader :part # required
+ attr_reader :use # required
+ attr_reader :encodingstyle
+ attr_reader :namespace
+
+ def initialize
+ super
+ @message = nil
+ @part = nil
+ @use = nil
+ @encodingstyle = nil
+ @namespace = nil
+ @headerfault = nil
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def find_message
+ root.message(@message) or raise RuntimeError.new("#{@message} not found")
+ end
+
+ def find_part
+ find_message.parts.each do |part|
+ if part.name == @part
+ return part
+ end
+ end
+ raise RuntimeError.new("#{@part} not found")
+ end
+
+ def parse_element(element)
+ case element
+ when HeaderFaultName
+ o = WSDL::SOAP::HeaderFault.new
+ @headerfault = o
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when MessageAttrName
+ if value.namespace.nil?
+ value = XSD::QName.new(targetnamespace, value.source)
+ end
+ @message = value
+ when PartAttrName
+ @part = value.source
+ when UseAttrName
+ @use = value.source
+ when EncodingStyleAttrName
+ @encodingstyle = value.source
+ when NamespaceAttrName
+ @namespace = value.source
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/soap/headerfault.rb b/ruby_1_8_5/lib/wsdl/soap/headerfault.rb
new file mode 100644
index 0000000000..d6b14f2646
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/soap/headerfault.rb
@@ -0,0 +1,56 @@
+# WSDL4R - WSDL SOAP body definition.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+module SOAP
+
+
+class HeaderFault < Info
+ attr_reader :message # required
+ attr_reader :part # required
+ attr_reader :use # required
+ attr_reader :encodingstyle
+ attr_reader :namespace
+
+ def initialize
+ super
+ @message = nil
+ @part = nil
+ @use = nil
+ @encodingstyle = nil
+ @namespace = nil
+ end
+
+ def parse_element(element)
+ nil
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when MessageAttrName
+ @message = value
+ when PartAttrName
+ @part = value.source
+ when UseAttrName
+ @use = value.source
+ when EncodingStyleAttrName
+ @encodingstyle = value.source
+ when NamespaceAttrName
+ @namespace = value.source
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/soap/mappingRegistryCreator.rb b/ruby_1_8_5/lib/wsdl/soap/mappingRegistryCreator.rb
new file mode 100644
index 0000000000..8669339ce4
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/soap/mappingRegistryCreator.rb
@@ -0,0 +1,92 @@
+# WSDL4R - Creating MappingRegistry code from WSDL.
+# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+require 'wsdl/soap/classDefCreatorSupport'
+
+
+module WSDL
+module SOAP
+
+
+class MappingRegistryCreator
+ include ClassDefCreatorSupport
+
+ attr_reader :definitions
+
+ def initialize(definitions)
+ @definitions = definitions
+ @complextypes = @definitions.collect_complextypes
+ @types = nil
+ end
+
+ def dump(types)
+ @types = types
+ map_cache = []
+ map = ""
+ @types.each do |type|
+ if map_cache.index(type).nil?
+ map_cache << type
+ if type.namespace != XSD::Namespace
+ if typemap = dump_typemap(type)
+ map << typemap
+ end
+ end
+ end
+ end
+ return map
+ end
+
+private
+
+ def dump_typemap(type)
+ if definedtype = @complextypes[type]
+ case definedtype.compoundtype
+ when :TYPE_STRUCT
+ dump_struct_typemap(definedtype)
+ when :TYPE_ARRAY
+ dump_array_typemap(definedtype)
+ when :TYPE_MAP, :TYPE_EMPTY
+ nil
+ else
+ raise NotImplementedError.new("must not reach here")
+ end
+ end
+ end
+
+ def dump_struct_typemap(definedtype)
+ ele = definedtype.name
+ return <<__EOD__
+MappingRegistry.set(
+ #{create_class_name(ele)},
+ ::SOAP::SOAPStruct,
+ ::SOAP::Mapping::Registry::TypedStructFactory,
+ { :type => #{dqname(ele)} }
+)
+__EOD__
+ end
+
+ def dump_array_typemap(definedtype)
+ ele = definedtype.name
+ arytype = definedtype.find_arytype || XSD::AnyTypeName
+ type = XSD::QName.new(arytype.namespace, arytype.name.sub(/\[(?:,)*\]$/, ''))
+ @types << type
+ return <<__EOD__
+MappingRegistry.set(
+ #{create_class_name(ele)},
+ ::SOAP::SOAPArray,
+ ::SOAP::Mapping::Registry::TypedArrayFactory,
+ { :type => #{dqname(type)} }
+)
+__EOD__
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/soap/methodDefCreator.rb b/ruby_1_8_5/lib/wsdl/soap/methodDefCreator.rb
new file mode 100644
index 0000000000..f3ffadbe69
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/soap/methodDefCreator.rb
@@ -0,0 +1,228 @@
+# WSDL4R - Creating driver code from WSDL.
+# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+require 'wsdl/soap/classDefCreatorSupport'
+require 'soap/rpc/element'
+
+
+module WSDL
+module SOAP
+
+
+class MethodDefCreator
+ include ClassDefCreatorSupport
+
+ attr_reader :definitions
+
+ def initialize(definitions)
+ @definitions = definitions
+ @simpletypes = @definitions.collect_simpletypes
+ @complextypes = @definitions.collect_complextypes
+ @elements = @definitions.collect_elements
+ @types = []
+ end
+
+ def dump(porttype)
+ @types.clear
+ result = ""
+ operations = @definitions.porttype(porttype).operations
+ binding = @definitions.porttype_binding(porttype)
+ operations.each do |operation|
+ op_bind = binding.operations[operation.name]
+ next unless op_bind # no binding is defined
+ next unless op_bind.soapoperation # not a SOAP operation binding
+ result << ",\n" unless result.empty?
+ result << dump_method(operation, op_bind).chomp
+ end
+ return result, @types
+ end
+
+ def collect_rpcparameter(operation)
+ result = operation.inputparts.collect { |part|
+ collect_type(part.type)
+ param_set(::SOAP::RPC::SOAPMethod::IN, part.name, rpcdefinedtype(part))
+ }
+ outparts = operation.outputparts
+ if outparts.size > 0
+ retval = outparts[0]
+ collect_type(retval.type)
+ result << param_set(::SOAP::RPC::SOAPMethod::RETVAL, retval.name,
+ rpcdefinedtype(retval))
+ cdr(outparts).each { |part|
+ collect_type(part.type)
+ result << param_set(::SOAP::RPC::SOAPMethod::OUT, part.name,
+ rpcdefinedtype(part))
+ }
+ end
+ result
+ end
+
+ def collect_documentparameter(operation)
+ param = []
+ operation.inputparts.each do |input|
+ param << param_set(::SOAP::RPC::SOAPMethod::IN, input.name,
+ documentdefinedtype(input), elementqualified(input))
+ end
+ operation.outputparts.each do |output|
+ param << param_set(::SOAP::RPC::SOAPMethod::OUT, output.name,
+ documentdefinedtype(output), elementqualified(output))
+ end
+ param
+ end
+
+private
+
+ def dump_method(operation, binding)
+ name = safemethodname(operation.name.name)
+ name_as = operation.name.name
+ style = binding.soapoperation_style
+ inputuse = binding.input.soapbody_use
+ outputuse = binding.output.soapbody_use
+ namespace = binding.input.soapbody.namespace
+ if style == :rpc
+ qname = XSD::QName.new(namespace, name_as)
+ paramstr = param2str(collect_rpcparameter(operation))
+ else
+ qname = nil
+ paramstr = param2str(collect_documentparameter(operation))
+ end
+ if paramstr.empty?
+ paramstr = '[]'
+ else
+ paramstr = "[ " << paramstr.split(/\r?\n/).join("\n ") << " ]"
+ end
+ definitions = <<__EOD__
+#{ndq(binding.soapaction)},
+ #{dq(name)},
+ #{paramstr},
+ { :request_style => #{sym(style.id2name)}, :request_use => #{sym(inputuse.id2name)},
+ :response_style => #{sym(style.id2name)}, :response_use => #{sym(outputuse.id2name)} }
+__EOD__
+ if style == :rpc
+ return <<__EOD__
+[ #{qname.dump},
+ #{definitions}]
+__EOD__
+ else
+ return <<__EOD__
+[ #{definitions}]
+__EOD__
+ end
+ end
+
+ def rpcdefinedtype(part)
+ if mapped = basetype_mapped_class(part.type)
+ ['::' + mapped.name]
+ elsif definedtype = @simpletypes[part.type]
+ ['::' + basetype_mapped_class(definedtype.base).name]
+ elsif definedtype = @elements[part.element]
+ #['::SOAP::SOAPStruct', part.element.namespace, part.element.name]
+ ['nil', part.element.namespace, part.element.name]
+ elsif definedtype = @complextypes[part.type]
+ case definedtype.compoundtype
+ when :TYPE_STRUCT, :TYPE_EMPTY # ToDo: empty should be treated as void.
+ type = create_class_name(part.type)
+ [type, part.type.namespace, part.type.name]
+ when :TYPE_MAP
+ [Hash.name, part.type.namespace, part.type.name]
+ when :TYPE_ARRAY
+ arytype = definedtype.find_arytype || XSD::AnyTypeName
+ ns = arytype.namespace
+ name = arytype.name.sub(/\[(?:,)*\]$/, '')
+ type = create_class_name(XSD::QName.new(ns, name))
+ [type + '[]', ns, name]
+ else
+ raise NotImplementedError.new("must not reach here")
+ end
+ else
+ raise RuntimeError.new("part: #{part.name} cannot be resolved")
+ end
+ end
+
+ def documentdefinedtype(part)
+ if mapped = basetype_mapped_class(part.type)
+ ['::' + mapped.name, nil, part.name]
+ elsif definedtype = @simpletypes[part.type]
+ ['::' + basetype_mapped_class(definedtype.base).name, nil, part.name]
+ elsif definedtype = @elements[part.element]
+ ['::SOAP::SOAPElement', part.element.namespace, part.element.name]
+ elsif definedtype = @complextypes[part.type]
+ ['::SOAP::SOAPElement', part.type.namespace, part.type.name]
+ else
+ raise RuntimeError.new("part: #{part.name} cannot be resolved")
+ end
+ end
+
+ def elementqualified(part)
+ if mapped = basetype_mapped_class(part.type)
+ false
+ elsif definedtype = @simpletypes[part.type]
+ false
+ elsif definedtype = @elements[part.element]
+ definedtype.elementform == 'qualified'
+ elsif definedtype = @complextypes[part.type]
+ false
+ else
+ raise RuntimeError.new("part: #{part.name} cannot be resolved")
+ end
+ end
+
+ def param_set(io_type, name, type, ele = nil)
+ [io_type, name, type, ele]
+ end
+
+ def collect_type(type)
+ # ignore inline type definition.
+ return if type.nil?
+ return if @types.include?(type)
+ @types << type
+ return unless @complextypes[type]
+ @complextypes[type].each_element do |element|
+ collect_type(element.type)
+ end
+ end
+
+ def param2str(params)
+ params.collect { |param|
+ io, name, type, ele = param
+ unless ele.nil?
+ "[#{dq(io)}, #{dq(name)}, #{type2str(type)}, #{ele2str(ele)}]"
+ else
+ "[#{dq(io)}, #{dq(name)}, #{type2str(type)}]"
+ end
+ }.join(",\n")
+ end
+
+ def type2str(type)
+ if type.size == 1
+ "[#{dq(type[0])}]"
+ else
+ "[#{dq(type[0])}, #{ndq(type[1])}, #{dq(type[2])}]"
+ end
+ end
+
+ def ele2str(ele)
+ qualified = ele
+ if qualified
+ "true"
+ else
+ "false"
+ end
+ end
+
+ def cdr(ary)
+ result = ary.dup
+ result.shift
+ result
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/soap/operation.rb b/ruby_1_8_5/lib/wsdl/soap/operation.rb
new file mode 100644
index 0000000000..502d34a07d
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/soap/operation.rb
@@ -0,0 +1,122 @@
+# WSDL4R - WSDL SOAP operation definition.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+module SOAP
+
+
+class Operation < Info
+ class OperationInfo
+ attr_reader :style
+ attr_reader :op_name
+ attr_reader :optype_name
+ attr_reader :headerparts
+ attr_reader :bodyparts
+ attr_reader :faultpart
+ attr_reader :soapaction
+
+ def initialize(style, op_name, optype_name, headerparts, bodyparts, faultpart, soapaction)
+ @style = style
+ @op_name = op_name
+ @optype_name = optype_name
+ @headerparts = headerparts
+ @bodyparts = bodyparts
+ @faultpart = faultpart
+ @soapaction = soapaction
+ end
+ end
+
+ attr_reader :soapaction
+ attr_reader :style
+
+ def initialize
+ super
+ @soapaction = nil
+ @style = nil
+ end
+
+ def parse_element(element)
+ nil
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when StyleAttrName
+ if ["document", "rpc"].include?(value.source)
+ @style = value.source.intern
+ else
+ raise Parser::AttributeConstraintError.new(
+ "Unexpected value #{ value }.")
+ end
+ when SOAPActionAttrName
+ @soapaction = value.source
+ else
+ nil
+ end
+ end
+
+ def input_info
+ name_info = parent.find_operation.input_info
+ param_info(name_info, parent.input)
+ end
+
+ def output_info
+ name_info = parent.find_operation.output_info
+ param_info(name_info, parent.output)
+ end
+
+ def operation_style
+ return @style if @style
+ if parent_binding.soapbinding
+ return parent_binding.soapbinding.style
+ end
+ nil
+ end
+
+private
+
+ def parent_binding
+ parent.parent
+ end
+
+ def param_info(name_info, param)
+ op_name = name_info.op_name
+ optype_name = name_info.optype_name
+
+ soapheader = param.soapheader
+ headerparts = soapheader.collect { |item| item.find_part }
+
+ soapbody = param.soapbody
+ if soapbody.encodingstyle and
+ soapbody.encodingstyle != ::SOAP::EncodingNamespace
+ raise NotImplementedError.new(
+ "EncodingStyle '#{ soapbody.encodingstyle }' not supported.")
+ end
+ if soapbody.namespace
+ op_name = XSD::QName.new(soapbody.namespace, op_name.name)
+ end
+ if soapbody.parts
+ target = soapbody.parts.split(/\s+/)
+ bodyparts = name_info.parts.find_all { |part|
+ target.include?(part.name)
+ }
+ else
+ bodyparts = name_info.parts
+ end
+
+ faultpart = nil
+ OperationInfo.new(operation_style, op_name, optype_name, headerparts, bodyparts, faultpart, parent.soapaction)
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/soap/servantSkeltonCreator.rb b/ruby_1_8_5/lib/wsdl/soap/servantSkeltonCreator.rb
new file mode 100644
index 0000000000..88294ffed8
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/soap/servantSkeltonCreator.rb
@@ -0,0 +1,67 @@
+# WSDL4R - Creating servant skelton code from WSDL.
+# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+require 'wsdl/soap/classDefCreatorSupport'
+require 'xsd/codegen'
+
+
+module WSDL
+module SOAP
+
+
+class ServantSkeltonCreator
+ include ClassDefCreatorSupport
+ include XSD::CodeGen::GenSupport
+
+ attr_reader :definitions
+
+ def initialize(definitions)
+ @definitions = definitions
+ end
+
+ def dump(porttype = nil)
+ if porttype.nil?
+ result = ""
+ @definitions.porttypes.each do |type|
+ result << dump_porttype(type.name)
+ result << "\n"
+ end
+ else
+ result = dump_porttype(porttype)
+ end
+ result
+ end
+
+private
+
+ def dump_porttype(name)
+ class_name = create_class_name(name)
+ c = XSD::CodeGen::ClassDef.new(class_name)
+ operations = @definitions.porttype(name).operations
+ operations.each do |operation|
+ name = safemethodname(operation.name.name)
+ input = operation.input
+ params = input.find_message.parts.collect { |part|
+ safevarname(part.name)
+ }
+ m = XSD::CodeGen::MethodDef.new(name, params) do <<-EOD
+ p [#{params.join(", ")}]
+ raise NotImplementedError.new
+ EOD
+ end
+ m.comment = dump_method_signature(operation)
+ c.add_method(m)
+ end
+ c.dump
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/soap/standaloneServerStubCreator.rb b/ruby_1_8_5/lib/wsdl/soap/standaloneServerStubCreator.rb
new file mode 100644
index 0000000000..0b751b5153
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/soap/standaloneServerStubCreator.rb
@@ -0,0 +1,85 @@
+# WSDL4R - Creating standalone server stub code from WSDL.
+# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+require 'wsdl/soap/mappingRegistryCreator'
+require 'wsdl/soap/methodDefCreator'
+require 'wsdl/soap/classDefCreatorSupport'
+
+
+module WSDL
+module SOAP
+
+
+class StandaloneServerStubCreator
+ include ClassDefCreatorSupport
+
+ attr_reader :definitions
+
+ def initialize(definitions)
+ @definitions = definitions
+ end
+
+ def dump(service_name)
+ warn("- Standalone stub can have only 1 port for now. So creating stub for the first port and rests are ignored.")
+ warn("- Standalone server stub ignores port location defined in WSDL. Location is http://localhost:10080/ by default. Generated client from WSDL must be configured to point this endpoint manually.")
+ port = @definitions.service(service_name).ports[0]
+ dump_porttype(port.porttype.name)
+ end
+
+private
+
+ def dump_porttype(name)
+ class_name = create_class_name(name)
+ methoddef, types = MethodDefCreator.new(@definitions).dump(name)
+ mr_creator = MappingRegistryCreator.new(@definitions)
+
+ c1 = XSD::CodeGen::ClassDef.new(class_name)
+ c1.def_require("soap/rpc/standaloneServer")
+ c1.def_require("soap/mapping/registry")
+ c1.def_const("MappingRegistry", "::SOAP::Mapping::Registry.new")
+ c1.def_code(mr_creator.dump(types))
+ c1.def_code <<-EOD
+Methods = [
+#{methoddef.gsub(/^/, " ")}
+]
+ EOD
+ c2 = XSD::CodeGen::ClassDef.new(class_name + "App",
+ "::SOAP::RPC::StandaloneServer")
+ c2.def_method("initialize", "*arg") do
+ <<-EOD
+ super(*arg)
+ servant = #{class_name}.new
+ #{class_name}::Methods.each do |definitions|
+ opt = definitions.last
+ if opt[:request_style] == :document
+ @router.add_document_operation(servant, *definitions)
+ else
+ @router.add_rpc_operation(servant, *definitions)
+ end
+ end
+ self.mapping_registry = #{class_name}::MappingRegistry
+ EOD
+ end
+ c1.dump + "\n" + c2.dump + format(<<-EOD)
+
+ if $0 == __FILE__
+ # Change listen port.
+ server = #{class_name}App.new('app', nil, '0.0.0.0', 10080)
+ trap(:INT) do
+ server.shutdown
+ end
+ server.start
+ end
+ EOD
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/soap/wsdl2ruby.rb b/ruby_1_8_5/lib/wsdl/soap/wsdl2ruby.rb
new file mode 100644
index 0000000000..16b05fb032
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/soap/wsdl2ruby.rb
@@ -0,0 +1,176 @@
+# WSDL4R - WSDL to ruby mapping library.
+# Copyright (C) 2002-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'logger'
+require 'xsd/qname'
+require 'wsdl/importer'
+require 'wsdl/soap/classDefCreator'
+require 'wsdl/soap/servantSkeltonCreator'
+require 'wsdl/soap/driverCreator'
+require 'wsdl/soap/clientSkeltonCreator'
+require 'wsdl/soap/standaloneServerStubCreator'
+require 'wsdl/soap/cgiStubCreator'
+
+
+module WSDL
+module SOAP
+
+
+class WSDL2Ruby
+ attr_accessor :location
+ attr_reader :opt
+ attr_accessor :logger
+ attr_accessor :basedir
+
+ def run
+ unless @location
+ raise RuntimeError, "WSDL location not given"
+ end
+ @wsdl = import(@location)
+ @name = @wsdl.name ? @wsdl.name.name : 'default'
+ create_file
+ end
+
+private
+
+ def initialize
+ @location = nil
+ @opt = {}
+ @logger = Logger.new(STDERR)
+ @basedir = nil
+ @wsdl = nil
+ @name = nil
+ end
+
+ def create_file
+ create_classdef if @opt.key?('classdef')
+ create_servant_skelton(@opt['servant_skelton']) if @opt.key?('servant_skelton')
+ create_cgi_stub(@opt['cgi_stub']) if @opt.key?('cgi_stub')
+ create_standalone_server_stub(@opt['standalone_server_stub']) if @opt.key?('standalone_server_stub')
+ create_driver(@opt['driver']) if @opt.key?('driver')
+ create_client_skelton(@opt['client_skelton']) if @opt.key?('client_skelton')
+ end
+
+ def create_classdef
+ @logger.info { "Creating class definition." }
+ @classdef_filename = @name + '.rb'
+ check_file(@classdef_filename) or return
+ write_file(@classdef_filename) do |f|
+ f << WSDL::SOAP::ClassDefCreator.new(@wsdl).dump
+ end
+ end
+
+ def create_client_skelton(servicename)
+ @logger.info { "Creating client skelton." }
+ servicename ||= @wsdl.services[0].name.name
+ @client_skelton_filename = servicename + 'Client.rb'
+ check_file(@client_skelton_filename) or return
+ write_file(@client_skelton_filename) do |f|
+ f << shbang << "\n"
+ f << "require '#{@driver_filename}'\n\n" if @driver_filename
+ f << WSDL::SOAP::ClientSkeltonCreator.new(@wsdl).dump(
+ create_name(servicename))
+ end
+ end
+
+ def create_servant_skelton(porttypename)
+ @logger.info { "Creating servant skelton." }
+ @servant_skelton_filename = (porttypename || @name + 'Servant') + '.rb'
+ check_file(@servant_skelton_filename) or return
+ write_file(@servant_skelton_filename) do |f|
+ f << "require '#{@classdef_filename}'\n\n" if @classdef_filename
+ f << WSDL::SOAP::ServantSkeltonCreator.new(@wsdl).dump(
+ create_name(porttypename))
+ end
+ end
+
+ def create_cgi_stub(servicename)
+ @logger.info { "Creating CGI stub." }
+ servicename ||= @wsdl.services[0].name.name
+ @cgi_stubFilename = servicename + '.cgi'
+ check_file(@cgi_stubFilename) or return
+ write_file(@cgi_stubFilename) do |f|
+ f << shbang << "\n"
+ if @servant_skelton_filename
+ f << "require '#{@servant_skelton_filename}'\n\n"
+ end
+ f << WSDL::SOAP::CGIStubCreator.new(@wsdl).dump(create_name(servicename))
+ end
+ end
+
+ def create_standalone_server_stub(servicename)
+ @logger.info { "Creating standalone stub." }
+ servicename ||= @wsdl.services[0].name.name
+ @standalone_server_stub_filename = servicename + '.rb'
+ check_file(@standalone_server_stub_filename) or return
+ write_file(@standalone_server_stub_filename) do |f|
+ f << shbang << "\n"
+ f << "require '#{@servant_skelton_filename}'\n\n" if @servant_skelton_filename
+ f << WSDL::SOAP::StandaloneServerStubCreator.new(@wsdl).dump(
+ create_name(servicename))
+ end
+ end
+
+ def create_driver(porttypename)
+ @logger.info { "Creating driver." }
+ @driver_filename = (porttypename || @name) + 'Driver.rb'
+ check_file(@driver_filename) or return
+ write_file(@driver_filename) do |f|
+ f << "require '#{@classdef_filename}'\n\n" if @classdef_filename
+ f << WSDL::SOAP::DriverCreator.new(@wsdl).dump(
+ create_name(porttypename))
+ end
+ end
+
+ def write_file(filename)
+ if @basedir
+ filename = File.join(basedir, filename)
+ end
+ File.open(filename, "w") do |f|
+ yield f
+ end
+ end
+
+ def check_file(filename)
+ if @basedir
+ filename = File.join(basedir, filename)
+ end
+ if FileTest.exist?(filename)
+ if @opt.key?('force')
+ @logger.warn {
+ "File '#{filename}' exists but overrides it."
+ }
+ true
+ else
+ @logger.warn {
+ "File '#{filename}' exists. #{$0} did not override it."
+ }
+ false
+ end
+ else
+ @logger.info { "Creates file '#{filename}'." }
+ true
+ end
+ end
+
+ def shbang
+ "#!/usr/bin/env ruby"
+ end
+
+ def create_name(name)
+ name ? XSD::QName.new(@wsdl.targetnamespace, name) : nil
+ end
+
+ def import(location)
+ WSDL::Importer.import(location)
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/types.rb b/ruby_1_8_5/lib/wsdl/types.rb
new file mode 100644
index 0000000000..96ae5a4988
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/types.rb
@@ -0,0 +1,43 @@
+# WSDL4R - WSDL types definition.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+
+
+class Types < Info
+ attr_reader :schemas
+
+ def initialize
+ super
+ @schemas = []
+ end
+
+ def parse_element(element)
+ case element
+ when SchemaName
+ o = XMLSchema::Schema.new
+ @schemas << o
+ o
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ nil
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/wsdl/wsdl.rb b/ruby_1_8_5/lib/wsdl/wsdl.rb
new file mode 100644
index 0000000000..eb13c18806
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/wsdl.rb
@@ -0,0 +1,23 @@
+# WSDL4R - Base definitions.
+# Copyright (C) 2000, 2001, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/qname'
+
+
+module WSDL
+
+
+Version = '0.0.2'
+
+Namespace = 'http://schemas.xmlsoap.org/wsdl/'
+SOAPBindingNamespace ='http://schemas.xmlsoap.org/wsdl/soap/'
+
+class Error < StandardError; end
+
+
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/all.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/all.rb
new file mode 100644
index 0000000000..bb9566feac
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/all.rb
@@ -0,0 +1,69 @@
+# WSDL4R - XMLSchema complexType definition for WSDL.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+module XMLSchema
+
+
+class All < Info
+ attr_reader :minoccurs
+ attr_reader :maxoccurs
+ attr_reader :elements
+
+ def initialize
+ super()
+ @minoccurs = '1'
+ @maxoccurs = '1'
+ @elements = []
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def elementformdefault
+ parent.elementformdefault
+ end
+
+ def <<(element)
+ @elements << element
+ end
+
+ def parse_element(element)
+ case element
+ when AnyName
+ o = Any.new
+ @elements << o
+ o
+ when ElementName
+ o = Element.new
+ @elements << o
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when MaxOccursAttrName
+ @maxoccurs = value.source
+ when MinOccursAttrName
+ @minoccurs = value.source
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/annotation.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/annotation.rb
new file mode 100644
index 0000000000..633bd196f1
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/annotation.rb
@@ -0,0 +1,34 @@
+# WSDL4R - WSDL SOAP documentation element.
+# Copyright (C) 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+module XMLSchema
+
+
+class Annotation < Info
+ def initialize
+ super
+ end
+
+ def parse_element(element)
+ # Accepts any element.
+ self
+ end
+
+ def parse_attr(attr, value)
+ # Accepts any attribute.
+ true
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/any.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/any.rb
new file mode 100644
index 0000000000..72d25e8dde
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/any.rb
@@ -0,0 +1,56 @@
+# WSDL4R - XMLSchema any definition for WSDL.
+# Copyright (C) 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+module XMLSchema
+
+
+class Any < Info
+ attr_accessor :maxoccurs
+ attr_accessor :minoccurs
+ attr_accessor :namespace
+ attr_accessor :process_contents
+
+ def initialize
+ super()
+ @maxoccurs = '1'
+ @minoccurs = '1'
+ @namespace = '##any'
+ @process_contents = 'strict'
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def parse_element(element)
+ nil
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when MaxOccursAttrName
+ @maxoccurs = value.source
+ when MinOccursAttrName
+ @minoccurs = value.source
+ when NamespaceAttrName
+ @namespace = value.source
+ when ProcessContentsAttrName
+ @process_contents = value.source
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/attribute.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/attribute.rb
new file mode 100644
index 0000000000..f9048661a2
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/attribute.rb
@@ -0,0 +1,127 @@
+# WSDL4R - XMLSchema attribute definition for WSDL.
+# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+module XMLSchema
+
+
+class Attribute < Info
+ class << self
+ if RUBY_VERSION > "1.7.0"
+ def attr_reader_ref(symbol)
+ name = symbol.to_s
+ define_method(name) {
+ instance_variable_get("@#{name}") ||
+ (refelement ? refelement.__send__(name) : nil)
+ }
+ end
+ else
+ def attr_reader_ref(symbol)
+ name = symbol.to_s
+ module_eval <<-EOS
+ def #{name}
+ @#{name} || (refelement ? refelement.#{name} : nil)
+ end
+ EOS
+ end
+ end
+ end
+
+ attr_writer :use
+ attr_writer :form
+ attr_writer :name
+ attr_writer :type
+ attr_writer :local_simpletype
+ attr_writer :default
+ attr_writer :fixed
+
+ attr_reader_ref :use
+ attr_reader_ref :form
+ attr_reader_ref :name
+ attr_reader_ref :type
+ attr_reader_ref :local_simpletype
+ attr_reader_ref :default
+ attr_reader_ref :fixed
+
+ attr_accessor :ref
+ attr_accessor :arytype
+
+ def initialize
+ super
+ @use = nil
+ @form = nil
+ @name = nil
+ @type = nil
+ @local_simpletype = nil
+ @default = nil
+ @fixed = nil
+ @ref = nil
+ @refelement = nil
+ @arytype = nil
+ end
+
+ def refelement
+ @refelement ||= root.collect_attributes[@ref]
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def parse_element(element)
+ case element
+ when SimpleTypeName
+ @local_simpletype = SimpleType.new
+ @local_simpletype
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when RefAttrName
+ @ref = value
+ when UseAttrName
+ @use = value.source
+ when FormAttrName
+ @form = value.source
+ when NameAttrName
+ if directelement?
+ @name = XSD::QName.new(targetnamespace, value.source)
+ else
+ @name = XSD::QName.new(nil, value.source)
+ end
+ when TypeAttrName
+ @type = value
+ when DefaultAttrName
+ @default = value.source
+ when FixedAttrName
+ @fixed = value.source
+ when ArrayTypeAttrName
+ @arytype = if value.namespace.nil?
+ XSD::QName.new(XSD::Namespace, value.source)
+ else
+ value
+ end
+ else
+ nil
+ end
+ end
+
+private
+
+ def directelement?
+ parent.is_a?(Schema)
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/choice.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/choice.rb
new file mode 100644
index 0000000000..f6d27fa38c
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/choice.rb
@@ -0,0 +1,69 @@
+# WSDL4R - XMLSchema complexType definition for WSDL.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+module XMLSchema
+
+
+class Choice < Info
+ attr_reader :minoccurs
+ attr_reader :maxoccurs
+ attr_reader :elements
+
+ def initialize
+ super()
+ @minoccurs = '1'
+ @maxoccurs = '1'
+ @elements = []
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def elementformdefault
+ parent.elementformdefault
+ end
+
+ def <<(element)
+ @elements << element
+ end
+
+ def parse_element(element)
+ case element
+ when AnyName
+ o = Any.new
+ @elements << o
+ o
+ when ElementName
+ o = Element.new
+ @elements << o
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when MaxOccursAttrName
+ @maxoccurs = value.source
+ when MinOccursAttrName
+ @minoccurs = value.source
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/complexContent.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/complexContent.rb
new file mode 100644
index 0000000000..f7fb6c16b4
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/complexContent.rb
@@ -0,0 +1,92 @@
+# WSDL4R - XMLSchema complexContent definition for WSDL.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+require 'xsd/namedelements'
+
+
+module WSDL
+module XMLSchema
+
+
+class ComplexContent < Info
+ attr_accessor :base
+ attr_reader :derivetype
+ attr_reader :content
+ attr_reader :attributes
+
+ def initialize
+ super
+ @base = nil
+ @derivetype = nil
+ @content = nil
+ @attributes = XSD::NamedElements.new
+ @basetype = nil
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def elementformdefault
+ parent.elementformdefault
+ end
+
+ def basetype
+ @basetype ||= root.collect_complextypes[@base]
+ end
+
+ def parse_element(element)
+ case element
+ when RestrictionName, ExtensionName
+ @derivetype = element.name
+ self
+ when AllName
+ if @derivetype.nil?
+ raise Parser::ElementConstraintError.new("base attr not found.")
+ end
+ @content = All.new
+ @content
+ when SequenceName
+ if @derivetype.nil?
+ raise Parser::ElementConstraintError.new("base attr not found.")
+ end
+ @content = Sequence.new
+ @content
+ when ChoiceName
+ if @derivetype.nil?
+ raise Parser::ElementConstraintError.new("base attr not found.")
+ end
+ @content = Choice.new
+ @content
+ when AttributeName
+ if @derivetype.nil?
+ raise Parser::ElementConstraintError.new("base attr not found.")
+ end
+ o = Attribute.new
+ @attributes << o
+ o
+ end
+ end
+
+ def parse_attr(attr, value)
+ if @derivetype.nil?
+ return nil
+ end
+ case attr
+ when BaseAttrName
+ @base = value
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/complexType.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/complexType.rb
new file mode 100644
index 0000000000..dc9ec954fc
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/complexType.rb
@@ -0,0 +1,139 @@
+# WSDL4R - XMLSchema complexType definition for WSDL.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+require 'wsdl/xmlSchema/content'
+require 'wsdl/xmlSchema/element'
+require 'xsd/namedelements'
+
+
+module WSDL
+module XMLSchema
+
+
+class ComplexType < Info
+ attr_accessor :name
+ attr_accessor :complexcontent
+ attr_accessor :simplecontent
+ attr_reader :content
+ attr_accessor :final
+ attr_accessor :mixed
+ attr_reader :attributes
+
+ def initialize(name = nil)
+ super()
+ @name = name
+ @complexcontent = nil
+ @simplecontent = nil
+ @content = nil
+ @final = nil
+ @mixed = false
+ @attributes = XSD::NamedElements.new
+ end
+
+ def targetnamespace
+ # inner elements can be qualified
+ # parent.is_a?(WSDL::XMLSchema::Element) ? nil : parent.targetnamespace
+ parent.targetnamespace
+ end
+
+ def elementformdefault
+ parent.elementformdefault
+ end
+
+ AnyAsElement = Element.new(XSD::QName.new(nil, 'any'), XSD::AnyTypeName)
+ def each_element
+ if content
+ content.elements.each do |element|
+ if element.is_a?(Any)
+ yield(AnyAsElement)
+ else
+ yield(element)
+ end
+ end
+ end
+ end
+
+ def find_element(name)
+ if content
+ content.elements.each do |element|
+ if element.is_a?(Any)
+ return AnyAsElement if name == AnyAsElement.name
+ else
+ return element if name == element.name
+ end
+ end
+ end
+ nil
+ end
+
+ def find_element_by_name(name)
+ if content
+ content.elements.each do |element|
+ if element.is_a?(Any)
+ return AnyAsElement if name == AnyAsElement.name.name
+ else
+ return element if name == element.name.name
+ end
+ end
+ end
+ nil
+ end
+
+ def sequence_elements=(elements)
+ @content = Sequence.new
+ elements.each do |element|
+ @content << element
+ end
+ end
+
+ def all_elements=(elements)
+ @content = All.new
+ elements.each do |element|
+ @content << element
+ end
+ end
+
+ def parse_element(element)
+ case element
+ when AllName
+ @content = All.new
+ when SequenceName
+ @content = Sequence.new
+ when ChoiceName
+ @content = Choice.new
+ when ComplexContentName
+ @complexcontent = ComplexContent.new
+ when SimpleContentName
+ @simplecontent = SimpleContent.new
+ when AttributeName
+ o = Attribute.new
+ @attributes << o
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when FinalAttrName
+ @final = value.source
+ when MixedAttrName
+ @mixed = (value.source == 'true')
+ when NameAttrName
+ @name = XSD::QName.new(targetnamespace, value.source)
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/content.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/content.rb
new file mode 100644
index 0000000000..2f1dfb4b6c
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/content.rb
@@ -0,0 +1,96 @@
+# WSDL4R - XMLSchema complexType definition for WSDL.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+module XMLSchema
+
+
+class Content < Info
+ attr_accessor :final
+ attr_accessor :mixed
+ attr_accessor :type
+ attr_reader :contents
+ attr_reader :elements
+
+ def initialize
+ super()
+ @final = nil
+ @mixed = false
+ @type = nil
+ @contents = []
+ @elements = []
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def <<(content)
+ @contents << content
+ update_elements
+ end
+
+ def each
+ @contents.each do |content|
+ yield content
+ end
+ end
+
+ def parse_element(element)
+ case element
+ when AllName, SequenceName, ChoiceName
+ o = Content.new
+ o.type = element.name
+ @contents << o
+ o
+ when AnyName
+ o = Any.new
+ @contents << o
+ o
+ when ElementName
+ o = Element.new
+ @contents << o
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when FinalAttrName
+ @final = value.source
+ when MixedAttrName
+ @mixed = (value.source == 'true')
+ else
+ nil
+ end
+ end
+
+ def parse_epilogue
+ update_elements
+ end
+
+private
+
+ def update_elements
+ @elements = []
+ @contents.each do |content|
+ if content.is_a?(Element)
+ @elements << [content.name, content]
+ end
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/data.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/data.rb
new file mode 100644
index 0000000000..23ab1adf0b
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/data.rb
@@ -0,0 +1,80 @@
+# WSDL4R - XMLSchema data definitions.
+# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/datatypes'
+require 'wsdl/xmlSchema/annotation'
+require 'wsdl/xmlSchema/schema'
+require 'wsdl/xmlSchema/import'
+require 'wsdl/xmlSchema/include'
+require 'wsdl/xmlSchema/simpleType'
+require 'wsdl/xmlSchema/simpleRestriction'
+require 'wsdl/xmlSchema/simpleExtension'
+require 'wsdl/xmlSchema/complexType'
+require 'wsdl/xmlSchema/complexContent'
+require 'wsdl/xmlSchema/simpleContent'
+require 'wsdl/xmlSchema/any'
+require 'wsdl/xmlSchema/element'
+require 'wsdl/xmlSchema/all'
+require 'wsdl/xmlSchema/choice'
+require 'wsdl/xmlSchema/sequence'
+require 'wsdl/xmlSchema/attribute'
+require 'wsdl/xmlSchema/unique'
+require 'wsdl/xmlSchema/enumeration'
+require 'wsdl/xmlSchema/length'
+require 'wsdl/xmlSchema/pattern'
+
+module WSDL
+module XMLSchema
+
+
+AllName = XSD::QName.new(XSD::Namespace, 'all')
+AnnotationName = XSD::QName.new(XSD::Namespace, 'annotation')
+AnyName = XSD::QName.new(XSD::Namespace, 'any')
+AttributeName = XSD::QName.new(XSD::Namespace, 'attribute')
+ChoiceName = XSD::QName.new(XSD::Namespace, 'choice')
+ComplexContentName = XSD::QName.new(XSD::Namespace, 'complexContent')
+ComplexTypeName = XSD::QName.new(XSD::Namespace, 'complexType')
+ElementName = XSD::QName.new(XSD::Namespace, 'element')
+EnumerationName = XSD::QName.new(XSD::Namespace, 'enumeration')
+ExtensionName = XSD::QName.new(XSD::Namespace, 'extension')
+ImportName = XSD::QName.new(XSD::Namespace, 'import')
+IncludeName = XSD::QName.new(XSD::Namespace, 'include')
+LengthName = XSD::QName.new(XSD::Namespace, 'length')
+PatternName = XSD::QName.new(XSD::Namespace, 'pattern')
+RestrictionName = XSD::QName.new(XSD::Namespace, 'restriction')
+SequenceName = XSD::QName.new(XSD::Namespace, 'sequence')
+SchemaName = XSD::QName.new(XSD::Namespace, 'schema')
+SimpleContentName = XSD::QName.new(XSD::Namespace, 'simpleContent')
+SimpleTypeName = XSD::QName.new(XSD::Namespace, 'simpleType')
+UniqueName = XSD::QName.new(XSD::Namespace, 'unique')
+
+AttributeFormDefaultAttrName = XSD::QName.new(nil, 'attributeFormDefault')
+BaseAttrName = XSD::QName.new(nil, 'base')
+DefaultAttrName = XSD::QName.new(nil, 'default')
+ElementFormDefaultAttrName = XSD::QName.new(nil, 'elementFormDefault')
+FinalAttrName = XSD::QName.new(nil, 'final')
+FixedAttrName = XSD::QName.new(nil, 'fixed')
+FormAttrName = XSD::QName.new(nil, 'form')
+IdAttrName = XSD::QName.new(nil, 'id')
+MaxOccursAttrName = XSD::QName.new(nil, 'maxOccurs')
+MinOccursAttrName = XSD::QName.new(nil, 'minOccurs')
+MixedAttrName = XSD::QName.new(nil, 'mixed')
+NameAttrName = XSD::QName.new(nil, 'name')
+NamespaceAttrName = XSD::QName.new(nil, 'namespace')
+NillableAttrName = XSD::QName.new(nil, 'nillable')
+ProcessContentsAttrName = XSD::QName.new(nil, 'processContents')
+RefAttrName = XSD::QName.new(nil, 'ref')
+SchemaLocationAttrName = XSD::QName.new(nil, 'schemaLocation')
+TargetNamespaceAttrName = XSD::QName.new(nil, 'targetNamespace')
+TypeAttrName = XSD::QName.new(nil, 'type')
+UseAttrName = XSD::QName.new(nil, 'use')
+ValueAttrName = XSD::QName.new(nil, 'value')
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/element.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/element.rb
new file mode 100644
index 0000000000..fffb6485d0
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/element.rb
@@ -0,0 +1,154 @@
+# WSDL4R - XMLSchema element definition for WSDL.
+# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+module XMLSchema
+
+
+class Element < Info
+ class << self
+ if RUBY_VERSION > "1.7.0"
+ def attr_reader_ref(symbol)
+ name = symbol.to_s
+ define_method(name) {
+ instance_variable_get("@#{name}") ||
+ (refelement ? refelement.__send__(name) : nil)
+ }
+ end
+ else
+ def attr_reader_ref(symbol)
+ name = symbol.to_s
+ module_eval <<-EOS
+ def #{name}
+ @#{name} || (refelement ? refelement.#{name} : nil)
+ end
+ EOS
+ end
+ end
+ end
+
+ attr_writer :name # required
+ attr_writer :form
+ attr_writer :type
+ attr_writer :local_simpletype
+ attr_writer :local_complextype
+ attr_writer :constraint
+ attr_writer :maxoccurs
+ attr_writer :minoccurs
+ attr_writer :nillable
+
+ attr_reader_ref :name
+ attr_reader_ref :form
+ attr_reader_ref :type
+ attr_reader_ref :local_simpletype
+ attr_reader_ref :local_complextype
+ attr_reader_ref :constraint
+ attr_reader_ref :maxoccurs
+ attr_reader_ref :minoccurs
+ attr_reader_ref :nillable
+
+ attr_accessor :ref
+
+ def initialize(name = nil, type = nil)
+ super()
+ @name = name
+ @form = nil
+ @type = type
+ @local_simpletype = @local_complextype = nil
+ @constraint = nil
+ @maxoccurs = '1'
+ @minoccurs = '1'
+ @nillable = nil
+ @ref = nil
+ @refelement = nil
+ end
+
+ def refelement
+ @refelement ||= (@ref ? root.collect_elements[@ref] : nil)
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def elementformdefault
+ parent.elementformdefault
+ end
+
+ def elementform
+ self.form.nil? ? parent.elementformdefault : self.form
+ end
+
+ def parse_element(element)
+ case element
+ when SimpleTypeName
+ @local_simpletype = SimpleType.new
+ @local_simpletype
+ when ComplexTypeName
+ @type = nil
+ @local_complextype = ComplexType.new
+ @local_complextype
+ when UniqueName
+ @constraint = Unique.new
+ @constraint
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NameAttrName
+ # namespace may be nil
+ if directelement? or elementform == 'qualified'
+ @name = XSD::QName.new(targetnamespace, value.source)
+ else
+ @name = XSD::QName.new(nil, value.source)
+ end
+ when FormAttrName
+ @form = value.source
+ when TypeAttrName
+ @type = value
+ when RefAttrName
+ @ref = value
+ when MaxOccursAttrName
+ if parent.is_a?(All)
+ if value.source != '1'
+ raise Parser::AttrConstraintError.new(
+ "cannot parse #{value} for #{attr}")
+ end
+ end
+ @maxoccurs = value.source
+ when MinOccursAttrName
+ if parent.is_a?(All)
+ unless ['0', '1'].include?(value.source)
+ raise Parser::AttrConstraintError.new(
+ "cannot parse #{value} for #{attr}")
+ end
+ end
+ @minoccurs = value.source
+ when NillableAttrName
+ @nillable = (value.source == 'true')
+ else
+ nil
+ end
+ end
+
+private
+
+ def directelement?
+ parent.is_a?(Schema)
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/enumeration.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/enumeration.rb
new file mode 100644
index 0000000000..5a16476032
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/enumeration.rb
@@ -0,0 +1,36 @@
+# WSDL4R - XMLSchema enumeration definition for WSDL.
+# Copyright (C) 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+module XMLSchema
+
+
+class Enumeration < Info
+ def initialize
+ super
+ end
+
+ def parse_element(element)
+ nil
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when ValueAttrName
+ parent.enumeration << value.source
+ value.source
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/import.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/import.rb
new file mode 100644
index 0000000000..d3487af934
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/import.rb
@@ -0,0 +1,65 @@
+# WSDL4R - XMLSchema import definition.
+# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+require 'wsdl/xmlSchema/importer'
+
+
+module WSDL
+module XMLSchema
+
+
+class Import < Info
+ attr_reader :namespace
+ attr_reader :schemalocation
+ attr_reader :content
+
+ def initialize
+ super
+ @namespace = nil
+ @schemalocation = nil
+ @content = nil
+ end
+
+ def parse_element(element)
+ nil
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NamespaceAttrName
+ @namespace = value.source
+ when SchemaLocationAttrName
+ @schemalocation = URI.parse(value.source)
+ if @schemalocation.relative? and !parent.location.nil? and
+ !parent.location.relative?
+ @schemalocation = parent.location + @schemalocation
+ end
+ if root.importedschema.key?(@schemalocation)
+ @content = root.importedschema[@schemalocation]
+ else
+ root.importedschema[@schemalocation] = nil # placeholder
+ @content = import(@schemalocation)
+ root.importedschema[@schemalocation] = @content
+ end
+ @schemalocation
+ else
+ nil
+ end
+ end
+
+private
+
+ def import(location)
+ Importer.import(location, root)
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/importer.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/importer.rb
new file mode 100644
index 0000000000..f57bfd972a
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/importer.rb
@@ -0,0 +1,87 @@
+# WSDL4R - XSD importer library.
+# Copyright (C) 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'soap/httpconfigloader'
+require 'wsdl/xmlSchema/parser'
+
+
+module WSDL
+module XMLSchema
+
+
+class Importer
+ def self.import(location, originalroot = nil)
+ new.import(location, originalroot)
+ end
+
+ def initialize
+ @web_client = nil
+ end
+
+ def import(location, originalroot = nil)
+ unless location.is_a?(URI)
+ location = URI.parse(location)
+ end
+ content = parse(fetch(location), location, originalroot)
+ content.location = location
+ content
+ end
+
+private
+
+ def parse(content, location, originalroot)
+ opt = {
+ :location => location,
+ :originalroot => originalroot
+ }
+ WSDL::XMLSchema::Parser.new(opt).parse(content)
+ end
+
+ def fetch(location)
+ warn("importing: #{location}") if $DEBUG
+ content = nil
+ if location.scheme == 'file' or
+ (location.relative? and FileTest.exist?(location.path))
+ content = File.open(location.path).read
+ elsif location.scheme and location.scheme.size == 1 and
+ FileTest.exist?(location.to_s)
+ # ToDo: remove this ugly workaround for a path with drive letter
+ # (D://foo/bar)
+ content = File.open(location.to_s).read
+ else
+ client = web_client.new(nil, "WSDL4R")
+ client.proxy = ::SOAP::Env::HTTP_PROXY
+ client.no_proxy = ::SOAP::Env::NO_PROXY
+ if opt = ::SOAP::Property.loadproperty(::SOAP::PropertyName)
+ ::SOAP::HTTPConfigLoader.set_options(client,
+ opt["client.protocol.http"])
+ end
+ content = client.get_content(location)
+ end
+ content
+ end
+
+ def web_client
+ @web_client ||= begin
+ require 'http-access2'
+ if HTTPAccess2::VERSION < "2.0"
+ raise LoadError.new("http-access/2.0 or later is required.")
+ end
+ HTTPAccess2::Client
+ rescue LoadError
+ warn("Loading http-access2 failed. Net/http is used.") if $DEBUG
+ require 'soap/netHttpClient'
+ ::SOAP::NetHttpClient
+ end
+ @web_client
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/include.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/include.rb
new file mode 100644
index 0000000000..af1ef942bb
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/include.rb
@@ -0,0 +1,54 @@
+# WSDL4R - XMLSchema include definition.
+# Copyright (C) 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+require 'wsdl/xmlSchema/importer'
+
+
+module WSDL
+module XMLSchema
+
+
+class Include < Info
+ attr_reader :schemalocation
+ attr_reader :content
+
+ def initialize
+ super
+ @schemalocation = nil
+ @content = nil
+ end
+
+ def parse_element(element)
+ nil
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when SchemaLocationAttrName
+ @schemalocation = URI.parse(value.source)
+ if @schemalocation.relative?
+ @schemalocation = parent.location + @schemalocation
+ end
+ @content = import(@schemalocation)
+ @schemalocation
+ else
+ nil
+ end
+ end
+
+private
+
+ def import(location)
+ Importer.import(location)
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/length.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/length.rb
new file mode 100644
index 0000000000..7f61602da9
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/length.rb
@@ -0,0 +1,35 @@
+# WSDL4R - XMLSchema length definition for WSDL.
+# Copyright (C) 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+module XMLSchema
+
+
+class Length < Info
+ def initialize
+ super
+ end
+
+ def parse_element(element)
+ nil
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when ValueAttrName
+ value.source
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/parser.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/parser.rb
new file mode 100644
index 0000000000..057d9d9b70
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/parser.rb
@@ -0,0 +1,166 @@
+# WSDL4R - WSDL XML Instance parser library.
+# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/qname'
+require 'xsd/ns'
+require 'xsd/charset'
+require 'xsd/datatypes'
+require 'xsd/xmlparser'
+require 'wsdl/xmlSchema/data'
+
+
+module WSDL
+module XMLSchema
+
+
+class Parser
+ include XSD
+
+ class ParseError < Error; end
+ class FormatDecodeError < ParseError; end
+ class UnknownElementError < FormatDecodeError; end
+ class UnknownAttributeError < FormatDecodeError; end
+ class UnexpectedElementError < FormatDecodeError; end
+ class ElementConstraintError < FormatDecodeError; end
+ class AttributeConstraintError < FormatDecodeError; end
+
+private
+
+ class ParseFrame
+ attr_reader :ns
+ attr_reader :name
+ attr_accessor :node
+
+ private
+
+ def initialize(ns, name, node)
+ @ns = ns
+ @name = name
+ @node = node
+ end
+ end
+
+public
+
+ def initialize(opt = {})
+ @parser = XSD::XMLParser.create_parser(self, opt)
+ @parsestack = nil
+ @lastnode = nil
+ @ignored = {}
+ @location = opt[:location]
+ @originalroot = opt[:originalroot]
+ end
+
+ def parse(string_or_readable)
+ @parsestack = []
+ @lastnode = nil
+ @textbuf = ''
+ @parser.do_parse(string_or_readable)
+ @lastnode
+ end
+
+ def charset
+ @parser.charset
+ end
+
+ def start_element(name, attrs)
+ lastframe = @parsestack.last
+ ns = parent = nil
+ if lastframe
+ ns = lastframe.ns.clone_ns
+ parent = lastframe.node
+ else
+ ns = XSD::NS.new
+ parent = nil
+ end
+ attrs = XSD::XMLParser.filter_ns(ns, attrs)
+ node = decode_tag(ns, name, attrs, parent)
+ @parsestack << ParseFrame.new(ns, name, node)
+ end
+
+ def characters(text)
+ lastframe = @parsestack.last
+ if lastframe
+ # Need not to be cloned because character does not have attr.
+ ns = lastframe.ns
+ decode_text(ns, text)
+ else
+ p text if $DEBUG
+ end
+ end
+
+ def end_element(name)
+ lastframe = @parsestack.pop
+ unless name == lastframe.name
+ raise UnexpectedElementError.new("closing element name '#{name}' does not match with opening element '#{lastframe.name}'")
+ end
+ decode_tag_end(lastframe.ns, lastframe.node)
+ @lastnode = lastframe.node
+ end
+
+private
+
+ def decode_tag(ns, name, attrs, parent)
+ o = nil
+ elename = ns.parse(name)
+ if !parent
+ if elename == SchemaName
+ o = Schema.parse_element(elename)
+ o.location = @location
+ else
+ raise UnknownElementError.new("unknown element: #{elename}")
+ end
+ o.root = @originalroot if @originalroot # o.root = o otherwise
+ else
+ if elename == AnnotationName
+ # only the first annotation element is allowed for each element.
+ o = Annotation.new
+ else
+ o = parent.parse_element(elename)
+ end
+ unless o
+ unless @ignored.key?(elename)
+ warn("ignored element: #{elename} of #{parent.class}")
+ @ignored[elename] = elename
+ end
+ o = Documentation.new # which accepts any element.
+ end
+ # node could be a pseudo element. pseudo element has its own parent.
+ o.root = parent.root
+ o.parent = parent if o.parent.nil?
+ end
+ attrs.each do |key, value|
+ attr_ele = ns.parse(key, true)
+ value_ele = ns.parse(value, true)
+ value_ele.source = value # for recovery; value may not be a QName
+ if attr_ele == IdAttrName
+ o.id = value_ele
+ else
+ unless o.parse_attr(attr_ele, value_ele)
+ unless @ignored.key?(attr_ele)
+ warn("ignored attr: #{attr_ele}")
+ @ignored[attr_ele] = attr_ele
+ end
+ end
+ end
+ end
+ o
+ end
+
+ def decode_tag_end(ns, node)
+ node.parse_epilogue
+ end
+
+ def decode_text(ns, text)
+ @textbuf << text
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/pattern.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/pattern.rb
new file mode 100644
index 0000000000..f826be4578
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/pattern.rb
@@ -0,0 +1,36 @@
+# WSDL4R - XMLSchema pattern definition for WSDL.
+# Copyright (C) 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+module XMLSchema
+
+
+class Pattern < Info
+ def initialize
+ super
+ end
+
+ def parse_element(element)
+ nil
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when ValueAttrName
+ parent.pattern = /\A#{value.source}\z/n
+ value.source
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/schema.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/schema.rb
new file mode 100644
index 0000000000..ec97d07aa5
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/schema.rb
@@ -0,0 +1,143 @@
+# WSDL4R - XMLSchema schema definition for WSDL.
+# Copyright (C) 2002, 2003-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+require 'xsd/namedelements'
+
+
+module WSDL
+module XMLSchema
+
+
+class Schema < Info
+ attr_reader :targetnamespace # required
+ attr_reader :complextypes
+ attr_reader :simpletypes
+ attr_reader :elements
+ attr_reader :attributes
+ attr_reader :imports
+ attr_accessor :attributeformdefault
+ attr_accessor :elementformdefault
+
+ attr_reader :importedschema
+
+ def initialize
+ super
+ @targetnamespace = nil
+ @complextypes = XSD::NamedElements.new
+ @simpletypes = XSD::NamedElements.new
+ @elements = XSD::NamedElements.new
+ @attributes = XSD::NamedElements.new
+ @imports = []
+ @attributeformdefault = "unqualified"
+ @elementformdefault = "unqualified"
+ @importedschema = {}
+ @location = nil
+ @root = self
+ end
+
+ def location
+ @location || (root.nil? ? nil : root.location)
+ end
+
+ def location=(location)
+ @location = location
+ end
+
+ def parse_element(element)
+ case element
+ when ImportName
+ o = Import.new
+ @imports << o
+ o
+ when IncludeName
+ o = Include.new
+ @imports << o
+ o
+ when ComplexTypeName
+ o = ComplexType.new
+ @complextypes << o
+ o
+ when SimpleTypeName
+ o = SimpleType.new
+ @simpletypes << o
+ o
+ when ElementName
+ o = Element.new
+ @elements << o
+ o
+ when AttributeName
+ o = Attribute.new
+ @attributes << o
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when TargetNamespaceAttrName
+ @targetnamespace = value.source
+ when AttributeFormDefaultAttrName
+ @attributeformdefault = value.source
+ when ElementFormDefaultAttrName
+ @elementformdefault = value.source
+ else
+ nil
+ end
+ end
+
+ def collect_attributes
+ result = XSD::NamedElements.new
+ result.concat(@attributes)
+ @imports.each do |import|
+ result.concat(import.content.collect_attributes) if import.content
+ end
+ result
+ end
+
+ def collect_elements
+ result = XSD::NamedElements.new
+ result.concat(@elements)
+ @imports.each do |import|
+ result.concat(import.content.collect_elements) if import.content
+ end
+ result
+ end
+
+ def collect_complextypes
+ result = XSD::NamedElements.new
+ result.concat(@complextypes)
+ @imports.each do |import|
+ result.concat(import.content.collect_complextypes) if import.content
+ end
+ result
+ end
+
+ def collect_simpletypes
+ result = XSD::NamedElements.new
+ result.concat(@simpletypes)
+ @imports.each do |import|
+ result.concat(import.content.collect_simpletypes) if import.content
+ end
+ result
+ end
+
+ def self.parse_element(element)
+ if element == SchemaName
+ Schema.new
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/sequence.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/sequence.rb
new file mode 100644
index 0000000000..823fa3b7f9
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/sequence.rb
@@ -0,0 +1,69 @@
+# WSDL4R - XMLSchema complexType definition for WSDL.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+module XMLSchema
+
+
+class Sequence < Info
+ attr_reader :minoccurs
+ attr_reader :maxoccurs
+ attr_reader :elements
+
+ def initialize
+ super()
+ @minoccurs = '1'
+ @maxoccurs = '1'
+ @elements = []
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def elementformdefault
+ parent.elementformdefault
+ end
+
+ def <<(element)
+ @elements << element
+ end
+
+ def parse_element(element)
+ case element
+ when AnyName
+ o = Any.new
+ @elements << o
+ o
+ when ElementName
+ o = Element.new
+ @elements << o
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when MaxOccursAttrName
+ @maxoccurs = value.source
+ when MinOccursAttrName
+ @minoccurs = value.source
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/simpleContent.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/simpleContent.rb
new file mode 100644
index 0000000000..e1f35c88b8
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/simpleContent.rb
@@ -0,0 +1,65 @@
+# WSDL4R - XMLSchema simpleContent definition for WSDL.
+# Copyright (C) 2004, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+require 'xsd/namedelements'
+
+
+module WSDL
+module XMLSchema
+
+
+class SimpleContent < Info
+ attr_reader :restriction
+ attr_reader :extension
+
+ def check_lexical_format(value)
+ check(value)
+ end
+
+ def initialize
+ super
+ @restriction = nil
+ @extension = nil
+ end
+
+ def base
+ content.base
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def parse_element(element)
+ case element
+ when RestrictionName
+ @restriction = SimpleRestriction.new
+ @restriction
+ when ExtensionName
+ @extension = SimpleExtension.new
+ @extension
+ end
+ end
+
+private
+
+ def content
+ @restriction || @extension
+ end
+
+ def check(value)
+ unless content.valid?(value)
+ raise XSD::ValueSpaceError.new("#{@name}: cannot accept '#{value}'")
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/simpleExtension.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/simpleExtension.rb
new file mode 100644
index 0000000000..3c53a7328c
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/simpleExtension.rb
@@ -0,0 +1,54 @@
+# WSDL4R - XMLSchema simpleType extension definition for WSDL.
+# Copyright (C) 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+require 'xsd/namedelements'
+
+
+module WSDL
+module XMLSchema
+
+
+class SimpleExtension < Info
+ attr_reader :base
+ attr_reader :attributes
+
+ def initialize
+ super
+ @base = nil
+ @attributes = XSD::NamedElements.new
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def valid?(value)
+ true
+ end
+
+ def parse_element(element)
+ case element
+ when AttributeName
+ o = Attribute.new
+ @attributes << o
+ o
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when BaseAttrName
+ @base = value
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/simpleRestriction.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/simpleRestriction.rb
new file mode 100644
index 0000000000..e8bf3ebfa5
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/simpleRestriction.rb
@@ -0,0 +1,73 @@
+# WSDL4R - XMLSchema simpleContent restriction definition for WSDL.
+# Copyright (C) 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+require 'xsd/namedelements'
+
+
+module WSDL
+module XMLSchema
+
+
+class SimpleRestriction < Info
+ attr_reader :base
+ attr_reader :enumeration
+ attr_accessor :length
+ attr_accessor :pattern
+
+ def initialize
+ super
+ @base = nil
+ @enumeration = [] # NamedElements?
+ @length = nil
+ @pattern = nil
+ end
+
+ def valid?(value)
+ return false unless check_restriction(value)
+ return false unless check_length(value)
+ return false unless check_pattern(value)
+ true
+ end
+
+ def parse_element(element)
+ case element
+ when EnumerationName
+ Enumeration.new # just a parsing handler
+ when LengthName
+ Length.new # just a parsing handler
+ when PatternName
+ Pattern.new # just a parsing handler
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when BaseAttrName
+ @base = value
+ end
+ end
+
+private
+
+ def check_restriction(value)
+ @enumeration.empty? or @enumeration.include?(value)
+ end
+
+ def check_length(value)
+ @length.nil? or value.size == @length
+ end
+
+ def check_pattern(value)
+ @pattern.nil? or @pattern =~ value
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/simpleType.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/simpleType.rb
new file mode 100644
index 0000000000..e808c318c4
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/simpleType.rb
@@ -0,0 +1,73 @@
+# WSDL4R - XMLSchema simpleType definition for WSDL.
+# Copyright (C) 2004, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+require 'xsd/namedelements'
+
+
+module WSDL
+module XMLSchema
+
+
+class SimpleType < Info
+ attr_accessor :name
+ attr_reader :restriction
+
+ def check_lexical_format(value)
+ if @restriction
+ check_restriction(value)
+ else
+ raise ArgumentError.new("incomplete simpleType")
+ end
+ end
+
+ def base
+ if @restriction
+ @restriction.base
+ else
+ raise ArgumentError.new("incomplete simpleType")
+ end
+ end
+
+ def initialize(name = nil)
+ super()
+ @name = name
+ @restriction = nil
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def parse_element(element)
+ case element
+ when RestrictionName
+ @restriction = SimpleRestriction.new
+ @restriction
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NameAttrName
+ @name = XSD::QName.new(targetnamespace, value.source)
+ end
+ end
+
+private
+
+ def check_restriction(value)
+ unless @restriction.valid?(value)
+ raise XSD::ValueSpaceError.new("#{@name}: cannot accept '#{value}'")
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/unique.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/unique.rb
new file mode 100644
index 0000000000..837ff22b4a
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/unique.rb
@@ -0,0 +1,34 @@
+# WSDL4R - XMLSchema unique element.
+# Copyright (C) 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'wsdl/info'
+
+
+module WSDL
+module XMLSchema
+
+
+class Unique < Info
+ def initialize
+ super
+ end
+
+ def parse_element(element)
+ # Accepts any element.
+ self
+ end
+
+ def parse_attr(attr, value)
+ # Accepts any attribute.
+ true
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/wsdl/xmlSchema/xsd2ruby.rb b/ruby_1_8_5/lib/wsdl/xmlSchema/xsd2ruby.rb
new file mode 100644
index 0000000000..afe5fc5ada
--- /dev/null
+++ b/ruby_1_8_5/lib/wsdl/xmlSchema/xsd2ruby.rb
@@ -0,0 +1,107 @@
+# XSD4R - XSD to ruby mapping library.
+# Copyright (C) 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/codegen/gensupport'
+require 'wsdl/xmlSchema/importer'
+require 'wsdl/soap/classDefCreator'
+
+
+module WSDL
+module XMLSchema
+
+
+class XSD2Ruby
+ attr_accessor :location
+ attr_reader :opt
+ attr_accessor :logger
+ attr_accessor :basedir
+
+ def run
+ unless @location
+ raise RuntimeError, "XML Schema location not given"
+ end
+ @xsd = import(@location)
+ @name = create_classname(@xsd)
+ create_file
+ end
+
+private
+
+ def initialize
+ @location = nil
+ @opt = {}
+ @logger = Logger.new(STDERR)
+ @basedir = nil
+ @xsd = nil
+ @name = nil
+ end
+
+ def create_file
+ create_classdef
+ end
+
+ def create_classdef
+ @logger.info { "Creating class definition." }
+ @classdef_filename = @name + '.rb'
+ check_file(@classdef_filename) or return
+ write_file(@classdef_filename) do |f|
+ f << WSDL::SOAP::ClassDefCreator.new(@xsd).dump
+ end
+ end
+
+ def write_file(filename)
+ if @basedir
+ filename = File.join(basedir, filename)
+ end
+ File.open(filename, "w") do |f|
+ yield f
+ end
+ end
+
+ def check_file(filename)
+ if @basedir
+ filename = File.join(basedir, filename)
+ end
+ if FileTest.exist?(filename)
+ if @opt.key?('force')
+ @logger.warn {
+ "File '#{filename}' exists but overrides it."
+ }
+ true
+ else
+ @logger.warn {
+ "File '#{filename}' exists. #{$0} did not override it."
+ }
+ false
+ end
+ else
+ @logger.info { "Creates file '#{filename}'." }
+ true
+ end
+ end
+
+ def create_classname(xsd)
+ name = nil
+ if xsd.targetnamespace
+ name = xsd.targetnamespace.scan(/[a-zA-Z0-9]+$/)[0]
+ end
+ if name.nil?
+ 'default'
+ else
+ XSD::CodeGen::GenSupport.safevarname(name)
+ end
+ end
+
+ def import(location)
+ WSDL::XMLSchema::Importer.import(location)
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/xmlrpc/.document b/ruby_1_8_5/lib/xmlrpc/.document
new file mode 100644
index 0000000000..e475c53ed0
--- /dev/null
+++ b/ruby_1_8_5/lib/xmlrpc/.document
@@ -0,0 +1 @@
+README.rdoc
diff --git a/ruby_1_8_5/lib/xmlrpc/README.txt b/ruby_1_8_5/lib/xmlrpc/README.txt
new file mode 100644
index 0000000000..ade842d8b1
--- /dev/null
+++ b/ruby_1_8_5/lib/xmlrpc/README.txt
@@ -0,0 +1,31 @@
+= XMLRPC for Ruby, Standard Library Documentation
+
+== Overview
+
+XMLRPC is a lightweight protocol that enables remote procedure calls over
+HTTP. It is defined at http://www.xmlrpc.com.
+
+XMLRPC allows you to create simple distributed computing solutions that span
+computer languages. Its distinctive feature is its simplicity compared to
+other approaches like SOAP and CORBA.
+
+The Ruby standard library package 'xmlrpc' enables you to create a server that
+implements remote procedures and a client that calls them. Very little code
+is required to achieve either of these.
+
+== Example
+
+Try the following code. It calls a standard demonstration remote procedure.
+
+ require 'xmlrpc/client'
+ require 'pp'
+
+ server = XMLRPC::Client.new2("http://xmlrpc-c.sourceforge.net/api/sample.php")
+ result = server.call("sample.sumAndDifference", 5, 3)
+ pp result
+
+== Documentation
+
+See http://www.ntecs.de/projects/xmlrpc4r. There is plenty of detail there to
+use the client and implement a server.
+
diff --git a/ruby_1_8_5/lib/xmlrpc/base64.rb b/ruby_1_8_5/lib/xmlrpc/base64.rb
new file mode 100644
index 0000000000..a0050d895c
--- /dev/null
+++ b/ruby_1_8_5/lib/xmlrpc/base64.rb
@@ -0,0 +1,81 @@
+=begin
+= xmlrpc/base64.rb
+Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
+
+Released under the same term of license as Ruby.
+
+= Classes
+* ((<XMLRPC::Base64>))
+
+= XMLRPC::Base64
+== Description
+This class is necessary for (('xmlrpc4r')) to determine that a string should
+be transmitted base64-encoded and not as a raw-string.
+You can use (({XMLRPC::Base64})) on the client and server-side as a
+parameter and/or return-value.
+
+== Class Methods
+--- XMLRPC::Base64.new( str, state = :dec )
+ Creates a new (({XMLRPC::Base64})) instance with string ((|str|)) as the
+ internal string. When ((|state|)) is (({:dec})) it assumes that the
+ string ((|str|)) is not in base64 format (perhaps already decoded),
+ otherwise if ((|state|)) is (({:enc})) it decodes ((|str|))
+ and stores it as the internal string.
+
+--- XMLRPC::Base64.decode( str )
+ Decodes string ((|str|)) with base64 and returns that value.
+
+--- XMLRPC::Base64.encode( str )
+ Encodes string ((|str|)) with base64 and returns that value.
+
+== Instance Methods
+--- XMLRPC::Base64#decoded
+ Returns the internal string decoded.
+
+--- XMLRPC::Base64#encoded
+ Returns the internal string encoded with base64.
+
+=end
+
+module XMLRPC
+
+class Base64
+
+ def initialize(str, state = :dec)
+ case state
+ when :enc
+ @str = Base64.decode(str)
+ when :dec
+ @str = str
+ else
+ raise ArgumentError, "wrong argument; either :enc or :dec"
+ end
+ end
+
+ def decoded
+ @str
+ end
+
+ def encoded
+ Base64.encode(@str)
+ end
+
+
+ def Base64.decode(str)
+ str.gsub(/\s+/, "").unpack("m")[0]
+ end
+
+ def Base64.encode(str)
+ [str].pack("m")
+ end
+
+end
+
+
+end # module XMLRPC
+
+
+=begin
+= History
+ $Id: base64.rb,v 1.1 2003/07/19 10:05:54 matz Exp $
+=end
diff --git a/ruby_1_8_5/lib/xmlrpc/client.rb b/ruby_1_8_5/lib/xmlrpc/client.rb
new file mode 100644
index 0000000000..4d23e31e3e
--- /dev/null
+++ b/ruby_1_8_5/lib/xmlrpc/client.rb
@@ -0,0 +1,616 @@
+=begin
+= xmlrpc/client.rb
+Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
+
+Released under the same term of license as Ruby.
+
+= Classes
+* ((<XMLRPC::Client>))
+* ((<XMLRPC::Client::Proxy>))
+
+
+= XMLRPC::Client
+== Synopsis
+ require "xmlrpc/client"
+
+ server = XMLRPC::Client.new("www.ruby-lang.org", "/RPC2", 80)
+ begin
+ param = server.call("michael.add", 4, 5)
+ puts "4 + 5 = #{param}"
+ rescue XMLRPC::FaultException => e
+ puts "Error:"
+ puts e.faultCode
+ puts e.faultString
+ end
+
+or
+
+ require "xmlrpc/client"
+
+ server = XMLRPC::Client.new("www.ruby-lang.org", "/RPC2", 80)
+ ok, param = server.call2("michael.add", 4, 5)
+ if ok then
+ puts "4 + 5 = #{param}"
+ else
+ puts "Error:"
+ puts param.faultCode
+ puts param.faultString
+ end
+
+== Description
+Class (({XMLRPC::Client})) provides remote procedure calls to a XML-RPC server.
+After setting the connection-parameters with ((<XMLRPC::Client.new>)) which
+creates a new (({XMLRPC::Client})) instance, you can execute a remote procedure
+by sending the ((<call|XMLRPC::Client#call>)) or ((<call2|XMLRPC::Client#call2>))
+message to this new instance. The given parameters indicate which method to
+call on the remote-side and of course the parameters for the remote procedure.
+
+== Class Methods
+--- XMLRPC::Client.new( host=nil, path=nil, port=nil, proxy_host=nil, proxy_port=nil, user=nil, password=nil, use_ssl=false, timeout =nil)
+ Creates an object which represents the remote XML-RPC server on the
+ given host ((|host|)). If the server is CGI-based, ((|path|)) is the
+ path to the CGI-script, which will be called, otherwise (in the
+ case of a standalone server) ((|path|)) should be (({"/RPC2"})).
+ ((|port|)) is the port on which the XML-RPC server listens.
+ If ((|proxy_host|)) is given, then a proxy server listening at
+ ((|proxy_host|)) is used. ((|proxy_port|)) is the port of the
+ proxy server.
+
+ Default values for ((|host|)), ((|path|)) and ((|port|)) are 'localhost', '/RPC2' and
+ '80' respectively using SSL '443'.
+
+ If ((|user|)) and ((|password|)) are given, each time a request is send,
+ a Authorization header is send. Currently only Basic Authentification is
+ implemented no Digest.
+
+ If ((|use_ssl|)) is set to (({true})), comunication over SSL is enabled.
+ Note, that you need the SSL package from RAA installed.
+
+ Parameter ((|timeout|)) is the time to wait for a XML-RPC response, defaults to 30.
+
+--- XMLRPC::Client.new2( uri, proxy=nil, timeout=nil)
+--- XMLRPC::Client.new_from_uri( uri, proxy=nil, timeout=nil)
+: uri
+ URI specifying protocol (http or https), host, port, path, user and password.
+ Example: https://user:password@host:port/path
+
+: proxy
+ Is of the form "host:port".
+
+: timeout
+ Defaults to 30.
+
+--- XMLRPC::Client.new3( hash={} )
+--- XMLRPC::Client.new_from_hash( hash={} )
+ Parameter ((|hash|)) has following case-insensitive keys:
+ * host
+ * path
+ * port
+ * proxy_host
+ * proxy_port
+ * user
+ * password
+ * use_ssl
+ * timeout
+
+ Calls ((<XMLRPC::Client.new>)) with the corresponding values.
+
+== Instance Methods
+--- XMLRPC::Client#call( method, *args )
+ Invokes the method named ((|method|)) with the parameters given by
+ ((|args|)) on the XML-RPC server.
+ The parameter ((|method|)) is converted into a (({String})) and should
+ be a valid XML-RPC method-name.
+ Each parameter of ((|args|)) must be of one of the following types,
+ where (({Hash})), (({Struct})) and (({Array})) can contain any of these listed ((:types:)):
+ * (({Fixnum})), (({Bignum}))
+ * (({TrueClass})), (({FalseClass})) ((({true})), (({false})))
+ * (({String})), (({Symbol}))
+ * (({Float}))
+ * (({Hash})), (({Struct}))
+ * (({Array}))
+ * (({Date})), (({Time})), (({XMLRPC::DateTime}))
+ * (({XMLRPC::Base64}))
+ * A Ruby object which class includes XMLRPC::Marshallable (only if Config::ENABLE_MARSHALLABLE is (({true}))).
+ That object is converted into a hash, with one additional key/value pair "___class___" which contains the class name
+ for restoring later that object.
+
+ The method returns the return-value from the RPC
+ ((-stands for Remote Procedure Call-)).
+ The type of the return-value is one of the above shown,
+ only that a (({Bignum})) is only allowed when it fits in 32-bit and
+ that a XML-RPC (('dateTime.iso8601')) type is always returned as
+ a ((<(({XMLRPC::DateTime}))|URL:datetime.html>)) object and
+ a (({Struct})) is never returned, only a (({Hash})), the same for a (({Symbol})), where
+ always a (({String})) is returned.
+ A (({XMLRPC::Base64})) is returned as a (({String})) from xmlrpc4r version 1.6.1 on.
+
+ If the remote procedure returned a fault-structure, then a
+ (({XMLRPC::FaultException})) exception is raised, which has two accessor-methods
+ (({faultCode})) and (({faultString})) of type (({Integer})) and (({String})).
+
+--- XMLRPC::Client#call2( method, *args )
+ The difference between this method and ((<call|XMLRPC::Client#call>)) is, that
+ this method do ((*not*)) raise a (({XMLRPC::FaultException})) exception.
+ The method returns an array of two values. The first value indicates if
+ the second value is a return-value ((({true}))) or an object of type
+ (({XMLRPC::FaultException})).
+ Both are explained in ((<call|XMLRPC::Client#call>)).
+
+ Simple to remember: The "2" in "call2" denotes the number of values it returns.
+
+--- XMLRPC::Client#multicall( *methods )
+ You can use this method to execute several methods on a XMLRPC server which supports
+ the multi-call extension.
+ Example:
+
+ s.multicall(
+ ['michael.add', 3, 4],
+ ['michael.sub', 4, 5]
+ )
+ # => [7, -1]
+
+--- XMLRPC::Client#multicall2( *methods )
+ Same as ((<XMLRPC::Client#multicall>)), but returns like ((<XMLRPC::Client#call2>)) two parameters
+ instead of raising an (({XMLRPC::FaultException})).
+
+--- XMLRPC::Client#proxy( prefix, *args )
+ Returns an object of class (({XMLRPC::Client::Proxy})), initialized with
+ ((|prefix|)) and ((|args|)). A proxy object returned by this method behaves
+ like ((<XMLRPC::Client#call>)), i.e. a call on that object will raise a
+ (({XMLRPC::FaultException})) when a fault-structure is returned by that call.
+
+--- XMLRPC::Client#proxy2( prefix, *args )
+ Almost the same like ((<XMLRPC::Client#proxy>)) only that a call on the returned
+ (({XMLRPC::Client::Proxy})) object behaves like ((<XMLRPC::Client#call2>)), i.e.
+ a call on that object will return two parameters.
+
+
+
+
+--- XMLRPC::Client#call_async(...)
+--- XMLRPC::Client#call2_async(...)
+--- XMLRPC::Client#multicall_async(...)
+--- XMLRPC::Client#multicall2_async(...)
+--- XMLRPC::Client#proxy_async(...)
+--- XMLRPC::Client#proxy2_async(...)
+ In contrast to corresponding methods without "_async", these can be
+ called concurrently and use for each request a new connection, where the
+ non-asynchronous counterparts use connection-alive (one connection for all requests)
+ if possible.
+
+ Note, that you have to use Threads to call these methods concurrently.
+ The following example calls two methods concurrently:
+
+ Thread.new {
+ p client.call_async("michael.add", 4, 5)
+ }
+
+ Thread.new {
+ p client.call_async("michael.div", 7, 9)
+ }
+
+
+--- XMLRPC::Client#timeout
+--- XMLRPC::Client#user
+--- XMLRPC::Client#password
+ Return the corresponding attributes.
+
+--- XMLRPC::Client#timeout= (new_timeout)
+--- XMLRPC::Client#user= (new_user)
+--- XMLRPC::Client#password= (new_password)
+ Set the corresponding attributes.
+
+
+--- XMLRPC::Client#set_writer( writer )
+ Sets the XML writer to use for generating XML output.
+ Should be an instance of a class from module (({XMLRPC::XMLWriter})).
+ If this method is not called, then (({XMLRPC::Config::DEFAULT_WRITER})) is used.
+
+--- XMLRPC::Client#set_parser( parser )
+ Sets the XML parser to use for parsing XML documents.
+ Should be an instance of a class from module (({XMLRPC::XMLParser})).
+ If this method is not called, then (({XMLRPC::Config::DEFAULT_PARSER})) is used.
+
+--- XMLRPC::Client#cookie
+--- XMLRPC::Client#cookie= (cookieString)
+ Get and set the HTTP Cookie header.
+
+--- XMLRPC::Client#http_header_extra= (additionalHeaders)
+ Set extra HTTP headers that are included in the request.
+
+--- XMLRPC::Client#http_header_extra
+ Access the via ((<XMLRPC::Client#http_header_extra=>)) assigned header.
+
+--- XMLRPC::Client#http_last_response
+ Returns the (({Net::HTTPResponse})) object of the last RPC.
+
+= XMLRPC::Client::Proxy
+== Synopsis
+ require "xmlrpc/client"
+
+ server = XMLRPC::Client.new("www.ruby-lang.org", "/RPC2", 80)
+
+ michael = server.proxy("michael")
+ michael2 = server.proxy("michael", 4)
+
+ # both calls should return the same value '9'.
+ p michael.add(4,5)
+ p michael2.add(5)
+
+== Description
+Class (({XMLRPC::Client::Proxy})) makes XML-RPC calls look nicer!
+You can call any method onto objects of that class - the object handles
+(({method_missing})) and will forward the method call to a XML-RPC server.
+Don't use this class directly, but use instead method ((<XMLRPC::Client#proxy>)) or
+((<XMLRPC::Client#proxy2>)).
+
+== Class Methods
+--- XMLRPC::Client::Proxy.new( server, prefix, args=[], meth=:call, delim="." )
+ Creates an object which provides (({method_missing})).
+
+ ((|server|)) must be of type (({XMLRPC::Client})), which is the XML-RPC server to be used
+ for a XML-RPC call. ((|prefix|)) and ((|delim|)) will be prepended to the methodname
+ called onto this object.
+
+ Parameter ((|meth|)) is the method (call, call2, call_async, call2_async) to use for
+ a RPC.
+
+ ((|args|)) are arguments which are automatically given
+ to every XML-RPC call before the arguments provides through (({method_missing})).
+
+== Instance Methods
+Every method call is forwarded to the XML-RPC server defined in ((<new|XMLRPC::Client::Proxy#new>)).
+
+Note: Inherited methods from class (({Object})) cannot be used as XML-RPC names, because they get around
+(({method_missing})).
+
+
+
+= History
+ $Id: client.rb,v 1.2.2.2 2005/09/20 08:51:02 matz Exp $
+
+=end
+
+
+
+require "xmlrpc/parser"
+require "xmlrpc/create"
+require "xmlrpc/config"
+require "xmlrpc/utils" # ParserWriterChooseMixin
+require "net/http"
+
+module XMLRPC
+
+ class Client
+
+ USER_AGENT = "XMLRPC::Client (Ruby #{RUBY_VERSION})"
+
+ include ParserWriterChooseMixin
+ include ParseContentType
+
+
+ # Constructors -------------------------------------------------------------------
+
+ def initialize(host=nil, path=nil, port=nil, proxy_host=nil, proxy_port=nil,
+ user=nil, password=nil, use_ssl=nil, timeout=nil)
+
+ @http_header_extra = nil
+ @http_last_response = nil
+ @cookie = nil
+
+ @host = host || "localhost"
+ @path = path || "/RPC2"
+ @proxy_host = proxy_host
+ @proxy_port = proxy_port
+ @proxy_host ||= 'localhost' if @proxy_port != nil
+ @proxy_port ||= 8080 if @proxy_host != nil
+ @use_ssl = use_ssl || false
+ @timeout = timeout || 30
+
+ if use_ssl
+ require "net/https"
+ @port = port || 443
+ else
+ @port = port || 80
+ end
+
+ @user, @password = user, password
+
+ set_auth
+
+ # convert ports to integers
+ @port = @port.to_i if @port != nil
+ @proxy_port = @proxy_port.to_i if @proxy_port != nil
+
+ # HTTP object for synchronous calls
+ Net::HTTP.version_1_2
+ @http = Net::HTTP.new(@host, @port, @proxy_host, @proxy_port)
+ @http.use_ssl = @use_ssl if @use_ssl
+ @http.read_timeout = @timeout
+ @http.open_timeout = @timeout
+
+ @parser = nil
+ @create = nil
+ end
+
+
+ class << self
+
+ def new2(uri, proxy=nil, timeout=nil)
+ if match = /^([^:]+):\/\/(([^@]+)@)?([^\/]+)(\/.*)?$/.match(uri)
+ proto = match[1]
+ user, passwd = (match[3] || "").split(":")
+ host, port = match[4].split(":")
+ path = match[5]
+
+ if proto != "http" and proto != "https"
+ raise "Wrong protocol specified. Only http or https allowed!"
+ end
+
+ else
+ raise "Wrong URI as parameter!"
+ end
+
+ proxy_host, proxy_port = (proxy || "").split(":")
+
+ self.new(host, path, port, proxy_host, proxy_port, user, passwd, (proto == "https"), timeout)
+ end
+
+ alias new_from_uri new2
+
+ def new3(hash={})
+
+ # convert all keys into lowercase strings
+ h = {}
+ hash.each { |k,v| h[k.to_s.downcase] = v }
+
+ self.new(h['host'], h['path'], h['port'], h['proxy_host'], h['proxy_port'], h['user'], h['password'],
+ h['use_ssl'], h['timeout'])
+ end
+
+ alias new_from_hash new3
+
+ end
+
+
+ # Attribute Accessors -------------------------------------------------------------------
+
+ # add additional HTTP headers to the request
+ attr_accessor :http_header_extra
+
+ # makes last HTTP response accessible
+ attr_reader :http_last_response
+
+ # Cookie support
+ attr_accessor :cookie
+
+
+ attr_reader :timeout, :user, :password
+
+ def timeout=(new_timeout)
+ @timeout = new_timeout
+ @http.read_timeout = @timeout
+ @http.open_timeout = @timeout
+ end
+
+ def user=(new_user)
+ @user = new_user
+ set_auth
+ end
+
+ def password=(new_password)
+ @password = new_password
+ set_auth
+ end
+
+ # Call methods --------------------------------------------------------------
+
+ def call(method, *args)
+ ok, param = call2(method, *args)
+ if ok
+ param
+ else
+ raise param
+ end
+ end
+
+ def call2(method, *args)
+ request = create().methodCall(method, *args)
+ data = do_rpc(request, false)
+ parser().parseMethodResponse(data)
+ end
+
+ def call_async(method, *args)
+ ok, param = call2_async(method, *args)
+ if ok
+ param
+ else
+ raise param
+ end
+ end
+
+ def call2_async(method, *args)
+ request = create().methodCall(method, *args)
+ data = do_rpc(request, true)
+ parser().parseMethodResponse(data)
+ end
+
+
+ # Multicall methods --------------------------------------------------------------
+
+ def multicall(*methods)
+ ok, params = multicall2(*methods)
+ if ok
+ params
+ else
+ raise params
+ end
+ end
+
+ def multicall2(*methods)
+ gen_multicall(methods, false)
+ end
+
+ def multicall_async(*methods)
+ ok, params = multicall2_async(*methods)
+ if ok
+ params
+ else
+ raise params
+ end
+ end
+
+ def multicall2_async(*methods)
+ gen_multicall(methods, true)
+ end
+
+
+ # Proxy generating methods ------------------------------------------
+
+ def proxy(prefix=nil, *args)
+ Proxy.new(self, prefix, args, :call)
+ end
+
+ def proxy2(prefix=nil, *args)
+ Proxy.new(self, prefix, args, :call2)
+ end
+
+ def proxy_async(prefix=nil, *args)
+ Proxy.new(self, prefix, args, :call_async)
+ end
+
+ def proxy2_async(prefix=nil, *args)
+ Proxy.new(self, prefix, args, :call2_async)
+ end
+
+
+ private # ----------------------------------------------------------
+
+ def set_auth
+ if @user.nil?
+ @auth = nil
+ else
+ a = "#@user"
+ a << ":#@password" if @password != nil
+ @auth = ("Basic " + [a].pack("m")).chomp
+ end
+ end
+
+ def do_rpc(request, async=false)
+ header = {
+ "User-Agent" => USER_AGENT,
+ "Content-Type" => "text/xml; charset=utf-8",
+ "Content-Length" => request.size.to_s,
+ "Connection" => (async ? "close" : "keep-alive")
+ }
+
+ header["Cookie"] = @cookie if @cookie
+ header.update(@http_header_extra) if @http_header_extra
+
+ if @auth != nil
+ # add authorization header
+ header["Authorization"] = @auth
+ end
+
+ resp = nil
+ @http_last_response = nil
+
+ if async
+ # use a new HTTP object for each call
+ Net::HTTP.version_1_2
+ http = Net::HTTP.new(@host, @port, @proxy_host, @proxy_port)
+ http.use_ssl = @use_ssl if @use_ssl
+ http.read_timeout = @timeout
+ http.open_timeout = @timeout
+
+ # post request
+ http.start {
+ resp = http.post2(@path, request, header)
+ }
+ else
+ # reuse the HTTP object for each call => connection alive is possible
+
+ # post request
+ resp = @http.post2(@path, request, header)
+ end
+
+ @http_last_response = resp
+
+ data = resp.body
+
+ if resp.code == "401"
+ # Authorization Required
+ raise "Authorization failed.\nHTTP-Error: #{resp.code} #{resp.message}"
+ elsif resp.code[0,1] != "2"
+ raise "HTTP-Error: #{resp.code} #{resp.message}"
+ end
+
+ ct = parse_content_type(resp["Content-Type"]).first
+ if ct != "text/xml"
+ if ct == "text/html"
+ raise "Wrong content-type: \n#{data}"
+ else
+ raise "Wrong content-type"
+ end
+ end
+
+ expected = resp["Content-Length"] || "<unknown>"
+ if data.nil? or data.size == 0
+ raise "Wrong size. Was #{data.size}, should be #{expected}"
+ elsif expected.to_i != data.size and resp["Transfer-Encoding"].nil?
+ raise "Wrong size. Was #{data.size}, should be #{expected}"
+ end
+
+ c = resp["Set-Cookie"]
+ @cookie = c if c
+
+ return data
+ end
+
+ def gen_multicall(methods=[], async=false)
+ meth = :call2
+ meth = :call2_async if async
+
+ ok, params = self.send(meth, "system.multicall",
+ methods.collect {|m| {'methodName' => m[0], 'params' => m[1..-1]} }
+ )
+
+ if ok
+ params = params.collect do |param|
+ if param.is_a? Array
+ param[0]
+ elsif param.is_a? Hash
+ XMLRPC::FaultException.new(param["faultCode"], param["faultString"])
+ else
+ raise "Wrong multicall return value"
+ end
+ end
+ end
+
+ return ok, params
+ end
+
+
+
+ class Proxy
+
+ def initialize(server, prefix, args=[], meth=:call, delim=".")
+ @server = server
+ @prefix = prefix ? prefix + delim : ""
+ @args = args
+ @meth = meth
+ end
+
+ def method_missing(mid, *args)
+ pre = @prefix + mid.to_s
+ arg = @args + args
+ @server.send(@meth, pre, *arg)
+ end
+
+ end # class Proxy
+
+ end # class Client
+
+end # module XMLRPC
+
diff --git a/ruby_1_8_5/lib/xmlrpc/config.rb b/ruby_1_8_5/lib/xmlrpc/config.rb
new file mode 100644
index 0000000000..230fe14a7c
--- /dev/null
+++ b/ruby_1_8_5/lib/xmlrpc/config.rb
@@ -0,0 +1,40 @@
+#
+# $Id: config.rb,v 1.1 2003/07/19 10:05:54 matz Exp $
+# Configuration file for XML-RPC for Ruby
+#
+
+module XMLRPC
+
+ module Config
+
+ DEFAULT_WRITER = XMLWriter::Simple # or XMLWriter::XMLParser
+
+ # available parser:
+ # * XMLParser::NQXMLTreeParser
+ # * XMLParser::NQXMLStreamParser
+ # * XMLParser::XMLTreeParser
+ # * XMLParser::XMLStreamParser (fastest)
+ # * XMLParser::REXMLStreamParser
+ # * XMLParser::XMLScanStreamParser
+ DEFAULT_PARSER = XMLParser::REXMLStreamParser
+
+ # enable <nil/> tag
+ ENABLE_NIL_CREATE = false
+ ENABLE_NIL_PARSER = false
+
+ # allows integers greater than 32-bit if true
+ ENABLE_BIGINT = false
+
+ # enable marshalling ruby objects which include XMLRPC::Marshallable
+ ENABLE_MARSHALLING = true
+
+ # enable multiCall extension by default
+ ENABLE_MULTICALL = false
+
+ # enable Introspection extension by default
+ ENABLE_INTROSPECTION = false
+
+ end
+
+end
+
diff --git a/ruby_1_8_5/lib/xmlrpc/create.rb b/ruby_1_8_5/lib/xmlrpc/create.rb
new file mode 100644
index 0000000000..ce7a401fa5
--- /dev/null
+++ b/ruby_1_8_5/lib/xmlrpc/create.rb
@@ -0,0 +1,290 @@
+#
+# Creates XML-RPC call/response documents
+#
+# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
+#
+# $Id: create.rb,v 1.1.2.2 2006/06/20 23:43:42 matz Exp $
+#
+
+require "date"
+require "xmlrpc/base64"
+
+module XMLRPC
+
+ module XMLWriter
+
+ class Abstract
+ def ele(name, *children)
+ element(name, nil, *children)
+ end
+
+ def tag(name, txt)
+ element(name, nil, text(txt))
+ end
+ end
+
+
+ class Simple < Abstract
+
+ def document_to_str(doc)
+ doc
+ end
+
+ def document(*params)
+ params.join("")
+ end
+
+ def pi(name, *params)
+ "<?#{name} " + params.join(" ") + " ?>"
+ end
+
+ def element(name, attrs, *children)
+ raise "attributes not yet implemented" unless attrs.nil?
+ if children.empty?
+ "<#{name}/>"
+ else
+ "<#{name}>" + children.join("") + "</#{name}>"
+ end
+ end
+
+ def text(txt)
+ cleaned = txt.dup
+ cleaned.gsub!(/&/, '&amp;')
+ cleaned.gsub!(/</, '&lt;')
+ cleaned.gsub!(/>/, '&gt;')
+ cleaned
+ end
+
+ end # class Simple
+
+
+ class XMLParser < Abstract
+
+ def initialize
+ require "xmltreebuilder"
+ end
+
+ def document_to_str(doc)
+ doc.to_s
+ end
+
+ def document(*params)
+ XML::SimpleTree::Document.new(*params)
+ end
+
+ def pi(name, *params)
+ XML::SimpleTree::ProcessingInstruction.new(name, *params)
+ end
+
+ def element(name, attrs, *children)
+ XML::SimpleTree::Element.new(name, attrs, *children)
+ end
+
+ def text(txt)
+ XML::SimpleTree::Text.new(txt)
+ end
+
+ end # class XMLParser
+
+ Classes = [Simple, XMLParser]
+
+ # yields an instance of each installed XML writer
+ def self.each_installed_writer
+ XMLRPC::XMLWriter::Classes.each do |klass|
+ begin
+ yield klass.new
+ rescue LoadError
+ end
+ end
+ end
+
+ end # module XMLWriter
+
+ class Create
+
+ def initialize(xml_writer = nil)
+ @writer = xml_writer || Config::DEFAULT_WRITER.new
+ end
+
+
+ def methodCall(name, *params)
+ name = name.to_s
+
+ if name !~ /[a-zA-Z0-9_.:\/]+/
+ raise ArgumentError, "Wrong XML-RPC method-name"
+ end
+
+ parameter = params.collect do |param|
+ @writer.ele("param", conv2value(param))
+ end
+
+ tree = @writer.document(
+ @writer.pi("xml", 'version="1.0"'),
+ @writer.ele("methodCall",
+ @writer.tag("methodName", name),
+ @writer.ele("params", *parameter)
+ )
+ )
+
+ @writer.document_to_str(tree) + "\n"
+ end
+
+
+
+ #
+ # generates a XML-RPC methodResponse document
+ #
+ # if is_ret == false then the params array must
+ # contain only one element, which is a structure
+ # of a fault return-value.
+ #
+ # if is_ret == true then a normal
+ # return-value of all the given params is created.
+ #
+ def methodResponse(is_ret, *params)
+
+ if is_ret
+ resp = params.collect do |param|
+ @writer.ele("param", conv2value(param))
+ end
+
+ resp = [@writer.ele("params", *resp)]
+ else
+ if params.size != 1 or params[0] === XMLRPC::FaultException
+ raise ArgumentError, "no valid fault-structure given"
+ end
+ resp = @writer.ele("fault", conv2value(params[0].to_h))
+ end
+
+
+ tree = @writer.document(
+ @writer.pi("xml", 'version="1.0"'),
+ @writer.ele("methodResponse", resp)
+ )
+
+ @writer.document_to_str(tree) + "\n"
+ end
+
+
+
+ #####################################
+ private
+ #####################################
+
+ #
+ # converts a Ruby object into
+ # a XML-RPC <value> tag
+ #
+ def conv2value(param)
+
+ val = case param
+ when Fixnum
+ @writer.tag("i4", param.to_s)
+
+ when Bignum
+ if Config::ENABLE_BIGINT
+ @writer.tag("i4", param.to_s)
+ else
+ if param >= -(2**31) and param <= (2**31-1)
+ @writer.tag("i4", param.to_s)
+ else
+ raise "Bignum is too big! Must be signed 32-bit integer!"
+ end
+ end
+ when TrueClass, FalseClass
+ @writer.tag("boolean", param ? "1" : "0")
+
+ when String
+ @writer.tag("string", param)
+
+ when Symbol
+ @writer.tag("string", param.to_s)
+
+ when NilClass
+ if Config::ENABLE_NIL_CREATE
+ @writer.ele("nil")
+ else
+ raise "Wrong type NilClass. Not allowed!"
+ end
+
+ when Float
+ @writer.tag("double", param.to_s)
+
+ when Struct
+ h = param.members.collect do |key|
+ value = param[key]
+ @writer.ele("member",
+ @writer.tag("name", key.to_s),
+ conv2value(value)
+ )
+ end
+
+ @writer.ele("struct", *h)
+
+ when Hash
+ # TODO: can a Hash be empty?
+
+ h = param.collect do |key, value|
+ @writer.ele("member",
+ @writer.tag("name", key.to_s),
+ conv2value(value)
+ )
+ end
+
+ @writer.ele("struct", *h)
+
+ when Array
+ # TODO: can an Array be empty?
+ a = param.collect {|v| conv2value(v) }
+
+ @writer.ele("array",
+ @writer.ele("data", *a)
+ )
+
+ when Time, Date
+ @writer.tag("dateTime.iso8601", param.strftime("%Y%m%dT%H:%M:%S"))
+
+ when XMLRPC::DateTime
+ @writer.tag("dateTime.iso8601",
+ format("%.4d%02d%02dT%02d:%02d:%02d", *param.to_a))
+
+ when XMLRPC::Base64
+ @writer.tag("base64", param.encoded)
+
+ else
+ if Config::ENABLE_MARSHALLING and param.class.included_modules.include? XMLRPC::Marshallable
+ # convert Ruby object into Hash
+ ret = {"___class___" => param.class.name}
+ param.instance_variables.each {|v|
+ name = v[1..-1]
+ val = param.instance_variable_get(v)
+
+ if val.nil?
+ ret[name] = val if Config::ENABLE_NIL_CREATE
+ else
+ ret[name] = val
+ end
+ }
+ return conv2value(ret)
+ else
+ ok, pa = wrong_type(param)
+ if ok
+ return conv2value(pa)
+ else
+ raise "Wrong type!"
+ end
+ end
+ end
+
+ @writer.ele("value", val)
+ end
+
+ def wrong_type(value)
+ false
+ end
+
+
+ end # class Create
+
+end # module XMLRPC
+
diff --git a/ruby_1_8_5/lib/xmlrpc/datetime.rb b/ruby_1_8_5/lib/xmlrpc/datetime.rb
new file mode 100644
index 0000000000..198b2cda1e
--- /dev/null
+++ b/ruby_1_8_5/lib/xmlrpc/datetime.rb
@@ -0,0 +1,142 @@
+=begin
+= xmlrpc/datetime.rb
+Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
+
+Released under the same term of license as Ruby.
+
+= Classes
+* ((<XMLRPC::DateTime>))
+
+= XMLRPC::DateTime
+== Description
+This class is important to handle XMLRPC (('dateTime.iso8601')) values,
+correcly, because normal UNIX-dates (class (({Date}))) only handle dates
+from year 1970 on, and class (({Time})) handles dates without the time
+component. (({XMLRPC::DateTime})) is able to store a XMLRPC
+(('dateTime.iso8601')) value correctly.
+
+== Class Methods
+--- XMLRPC::DateTime.new( year, month, day, hour, min, sec )
+ Creates a new (({XMLRPC::DateTime})) instance with the
+ parameters ((|year|)), ((|month|)), ((|day|)) as date and
+ ((|hour|)), ((|min|)), ((|sec|)) as time.
+ Raises (({ArgumentError})) if a parameter is out of range, or ((|year|)) is not
+ of type (({Integer})).
+
+== Instance Methods
+--- XMLRPC::DateTime#year
+--- XMLRPC::DateTime#month
+--- XMLRPC::DateTime#day
+--- XMLRPC::DateTime#hour
+--- XMLRPC::DateTime#min
+--- XMLRPC::DateTime#sec
+ Return the value of the specified date/time component.
+
+--- XMLRPC::DateTime#mon
+ Alias for ((<XMLRPC::DateTime#month>)).
+
+--- XMLRPC::DateTime#year=( value )
+--- XMLRPC::DateTime#month=( value )
+--- XMLRPC::DateTime#day=( value )
+--- XMLRPC::DateTime#hour=( value )
+--- XMLRPC::DateTime#min=( value )
+--- XMLRPC::DateTime#sec=( value )
+ Set ((|value|)) as the new date/time component.
+ Raises (({ArgumentError})) if ((|value|)) is out of range, or in the case
+ of (({XMLRPC::DateTime#year=})) if ((|value|)) is not of type (({Integer})).
+
+--- XMLRPC::DateTime#mon=( value )
+ Alias for ((<XMLRPC::DateTime#month=>)).
+
+--- XMLRPC::DateTime#to_time
+ Return a (({Time})) object of the date/time which (({self})) represents.
+ If the (('year')) is below 1970, this method returns (({nil})),
+ because (({Time})) cannot handle years below 1970.
+ The used timezone is GMT.
+
+--- XMLRPC::DateTime#to_date
+ Return a (({Date})) object of the date which (({self})) represents.
+ The (({Date})) object do ((*not*)) contain the time component (only date).
+
+--- XMLRPC::DateTime#to_a
+ Returns all date/time components in an array.
+ Returns (({[year, month, day, hour, min, sec]})).
+=end
+
+require "date"
+
+module XMLRPC
+
+class DateTime
+
+ attr_reader :year, :month, :day, :hour, :min, :sec
+
+ def year= (value)
+ raise ArgumentError, "date/time out of range" unless value.is_a? Integer
+ @year = value
+ end
+
+ def month= (value)
+ raise ArgumentError, "date/time out of range" unless (1..12).include? value
+ @month = value
+ end
+
+ def day= (value)
+ raise ArgumentError, "date/time out of range" unless (1..31).include? value
+ @day = value
+ end
+
+ def hour= (value)
+ raise ArgumentError, "date/time out of range" unless (0..24).include? value
+ @hour = value
+ end
+
+ def min= (value)
+ raise ArgumentError, "date/time out of range" unless (0..59).include? value
+ @min = value
+ end
+
+ def sec= (value)
+ raise ArgumentError, "date/time out of range" unless (0..59).include? value
+ @sec = value
+ end
+
+ alias mon month
+ alias mon= month=
+
+
+ def initialize(year, month, day, hour, min, sec)
+ self.year, self.month, self.day = year, month, day
+ self.hour, self.min, self.sec = hour, min, sec
+ end
+
+ def to_time
+ if @year >= 1970
+ Time.gm(*to_a)
+ else
+ nil
+ end
+ end
+
+ def to_date
+ Date.new(*to_a[0,3])
+ end
+
+ def to_a
+ [@year, @month, @day, @hour, @min, @sec]
+ end
+
+ def ==(o)
+ Array(self) == Array(o)
+ end
+
+end
+
+
+end # module XMLRPC
+
+
+=begin
+= History
+ $Id: datetime.rb,v 1.1.2.1 2005/06/24 20:27:42 mneumann Exp $
+=end
diff --git a/ruby_1_8_5/lib/xmlrpc/httpserver.rb b/ruby_1_8_5/lib/xmlrpc/httpserver.rb
new file mode 100644
index 0000000000..e4aa99f46a
--- /dev/null
+++ b/ruby_1_8_5/lib/xmlrpc/httpserver.rb
@@ -0,0 +1,178 @@
+#
+# Implements a simple HTTP-server by using John W. Small's (jsmall@laser.net)
+# ruby-generic-server.
+#
+# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
+#
+# $Id: httpserver.rb,v 1.1 2003/07/19 10:05:54 matz Exp $
+#
+
+
+require "gserver"
+
+class HttpServer < GServer
+
+ ##
+ # handle_obj specifies the object, that receives calls to request_handler
+ # and ip_auth_handler
+ def initialize(handle_obj, port = 8080, host = DEFAULT_HOST, maxConnections = 4,
+ stdlog = $stdout, audit = true, debug = true)
+ @handler = handle_obj
+ super(port, host, maxConnections, stdlog, audit, debug)
+ end
+
+private
+
+ # Constants -----------------------------------------------
+
+ CRLF = "\r\n"
+ HTTP_PROTO = "HTTP/1.0"
+ SERVER_NAME = "HttpServer (Ruby #{RUBY_VERSION})"
+
+ DEFAULT_HEADER = {
+ "Server" => SERVER_NAME
+ }
+
+ ##
+ # Mapping of status code and error message
+ #
+ StatusCodeMapping = {
+ 200 => "OK",
+ 400 => "Bad Request",
+ 403 => "Forbidden",
+ 405 => "Method Not Allowed",
+ 411 => "Length Required",
+ 500 => "Internal Server Error"
+ }
+
+ # Classes -------------------------------------------------
+
+ class Request
+ attr_reader :data, :header, :method, :path, :proto
+
+ def initialize(data, method=nil, path=nil, proto=nil)
+ @header, @data = Table.new, data
+ @method, @path, @proto = method, path, proto
+ end
+
+ def content_length
+ len = @header['Content-Length']
+ return nil if len.nil?
+ return len.to_i
+ end
+
+ end
+
+ class Response
+ attr_reader :header
+ attr_accessor :body, :status, :status_message
+
+ def initialize(status=200)
+ @status = status
+ @status_message = nil
+ @header = Table.new
+ end
+ end
+
+
+ ##
+ # a case-insensitive Hash class for HTTP header
+ #
+ class Table
+ include Enumerable
+
+ def initialize(hash={})
+ @hash = hash
+ update(hash)
+ end
+
+ def [](key)
+ @hash[key.to_s.capitalize]
+ end
+
+ def []=(key, value)
+ @hash[key.to_s.capitalize] = value
+ end
+
+ def update(hash)
+ hash.each {|k,v| self[k] = v}
+ self
+ end
+
+ def each
+ @hash.each {|k,v| yield k.capitalize, v }
+ end
+
+ def writeTo(port)
+ each { |k,v| port << "#{k}: #{v}" << CRLF }
+ end
+ end # class Table
+
+
+ # Helper Methods ------------------------------------------
+
+ def http_header(header=nil)
+ new_header = Table.new(DEFAULT_HEADER)
+ new_header.update(header) unless header.nil?
+
+ new_header["Connection"] = "close"
+ new_header["Date"] = http_date(Time.now)
+
+ new_header
+ end
+
+ def http_date( aTime )
+ aTime.gmtime.strftime( "%a, %d %b %Y %H:%M:%S GMT" )
+ end
+
+ def http_resp(status_code, status_message=nil, header=nil, body=nil)
+ status_message ||= StatusCodeMapping[status_code]
+
+ str = ""
+ str << "#{HTTP_PROTO} #{status_code} #{status_message}" << CRLF
+ http_header(header).writeTo(str)
+ str << CRLF
+ str << body unless body.nil?
+ str
+ end
+
+ # Main Serve Loop -----------------------------------------
+
+ def serve(io)
+ # perform IP authentification
+ unless @handler.ip_auth_handler(io)
+ io << http_resp(403, "Forbidden")
+ return
+ end
+
+ # parse first line
+ if io.gets =~ /^(\S+)\s+(\S+)\s+(\S+)/
+ request = Request.new(io, $1, $2, $3)
+ else
+ io << http_resp(400, "Bad Request")
+ return
+ end
+
+ # parse HTTP headers
+ while (line=io.gets) !~ /^(\n|\r)/
+ if line =~ /^([\w-]+):\s*(.*)$/
+ request.header[$1] = $2.strip
+ end
+ end
+
+ io.binmode
+ response = Response.new
+
+ # execute request handler
+ @handler.request_handler(request, response)
+
+ # write response back to the client
+ io << http_resp(response.status, response.status_message,
+ response.header, response.body)
+
+ rescue Exception => e
+ io << http_resp(500, "Internal Server Error")
+ end
+
+end # class HttpServer
+
diff --git a/ruby_1_8_5/lib/xmlrpc/marshal.rb b/ruby_1_8_5/lib/xmlrpc/marshal.rb
new file mode 100644
index 0000000000..a4aa620da1
--- /dev/null
+++ b/ruby_1_8_5/lib/xmlrpc/marshal.rb
@@ -0,0 +1,76 @@
+#
+# Marshalling of XML-RPC methodCall and methodResponse
+#
+# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
+#
+# $Id: marshal.rb,v 1.1 2003/07/19 10:05:54 matz Exp $
+#
+
+require "xmlrpc/parser"
+require "xmlrpc/create"
+require "xmlrpc/config"
+require "xmlrpc/utils"
+
+module XMLRPC
+
+ class Marshal
+ include ParserWriterChooseMixin
+
+ # class methods -------------------------------
+
+ class << self
+
+ def dump_call( methodName, *params )
+ new.dump_call( methodName, *params )
+ end
+
+ def dump_response( param )
+ new.dump_response( param )
+ end
+
+ def load_call( stringOrReadable )
+ new.load_call( stringOrReadable )
+ end
+
+ def load_response( stringOrReadable )
+ new.load_response( stringOrReadable )
+ end
+
+ alias dump dump_response
+ alias load load_response
+
+ end # class self
+
+ # instance methods ----------------------------
+
+ def initialize( parser = nil, writer = nil )
+ set_parser( parser )
+ set_writer( writer )
+ end
+
+ def dump_call( methodName, *params )
+ create.methodCall( methodName, *params )
+ end
+
+ def dump_response( param )
+ create.methodResponse( ! param.kind_of?( XMLRPC::FaultException ) , param )
+ end
+
+ ##
+ # returns [ methodname, params ]
+ #
+ def load_call( stringOrReadable )
+ parser.parseMethodCall( stringOrReadable )
+ end
+
+ ##
+ # returns paramOrFault
+ #
+ def load_response( stringOrReadable )
+ parser.parseMethodResponse( stringOrReadable )[1]
+ end
+
+ end # class Marshal
+
+end
+
diff --git a/ruby_1_8_5/lib/xmlrpc/parser.rb b/ruby_1_8_5/lib/xmlrpc/parser.rb
new file mode 100644
index 0000000000..e4f21edec6
--- /dev/null
+++ b/ruby_1_8_5/lib/xmlrpc/parser.rb
@@ -0,0 +1,813 @@
+#
+# Parser for XML-RPC call and response
+#
+# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
+#
+# $Id: parser.rb,v 1.3.2.3 2005/06/24 20:27:42 mneumann Exp $
+#
+
+
+require "date"
+require "xmlrpc/base64"
+require "xmlrpc/datetime"
+
+
+# add some methods to NQXML::Node
+module NQXML
+ class Node
+
+ def removeChild(node)
+ @children.delete(node)
+ end
+ def childNodes
+ @children
+ end
+ def hasChildNodes
+ not @children.empty?
+ end
+ def [] (index)
+ @children[index]
+ end
+
+ def nodeType
+ if @entity.instance_of? NQXML::Text then :TEXT
+ elsif @entity.instance_of? NQXML::Comment then :COMMENT
+ #elsif @entity.instance_of? NQXML::Element then :ELEMENT
+ elsif @entity.instance_of? NQXML::Tag then :ELEMENT
+ else :ELSE
+ end
+ end
+
+ def nodeValue
+ #TODO: error when wrong Entity-type
+ @entity.text
+ end
+ def nodeName
+ #TODO: error when wrong Entity-type
+ @entity.name
+ end
+ end # class Node
+end # module NQXML
+
+module XMLRPC
+
+ class FaultException < StandardError
+ attr_reader :faultCode, :faultString
+
+ alias message faultString
+
+ def initialize(faultCode, faultString)
+ @faultCode = faultCode
+ @faultString = faultString
+ end
+
+ # returns a hash
+ def to_h
+ {"faultCode" => @faultCode, "faultString" => @faultString}
+ end
+ end
+
+ module Convert
+ def self.int(str)
+ str.to_i
+ end
+
+ def self.boolean(str)
+ case str
+ when "0" then false
+ when "1" then true
+ else
+ raise "RPC-value of type boolean is wrong"
+ end
+ end
+
+ def self.double(str)
+ str.to_f
+ end
+
+ def self.dateTime(str)
+ case str
+ when /^(-?\d\d\d\d)-?(\d\d)-?(\d\d)T(\d\d):(\d\d):(\d\d)(?:Z|([+-])(\d\d):?(\d\d))?$/
+ a = [$1, $2, $3, $4, $5, $6].collect{|i| i.to_i}
+ if $7
+ ofs = $8.to_i*3600 + $9.to_i*60
+ ofs = -ofs if $7=='+'
+ utc = Time.utc(a.reverse) + ofs
+ a = [ utc.year, utc.month, utc.day, utc.hour, utc.min, utc.sec ]
+ end
+ XMLRPC::DateTime.new(*a)
+ when /^(-?\d\d)-?(\d\d)-?(\d\d)T(\d\d):(\d\d):(\d\d)(Z|([+-]\d\d):(\d\d))?$/
+ a = [$1, $2, $3, $4, $5, $6].collect{|i| i.to_i}
+ if a[0] < 70
+ a[0] += 2000
+ else
+ a[0] += 1900
+ end
+ if $7
+ ofs = $8.to_i*3600 + $9.to_i*60
+ ofs = -ofs if $7=='+'
+ utc = Time.utc(a.reverse) + ofs
+ a = [ utc.year, utc.month, utc.day, utc.hour, utc.min, utc.sec ]
+ end
+ XMLRPC::DateTime.new(*a)
+ else
+ raise "wrong dateTime.iso8601 format " + str
+ end
+ end
+
+ def self.base64(str)
+ XMLRPC::Base64.decode(str)
+ end
+
+ def self.struct(hash)
+ # convert to marhalled object
+ klass = hash["___class___"]
+ if klass.nil? or Config::ENABLE_MARSHALLING == false
+ hash
+ else
+ begin
+ mod = Module
+ klass.split("::").each {|const| mod = mod.const_get(const.strip)}
+
+ obj = mod.allocate
+
+ hash.delete "___class___"
+ hash.each {|key, value|
+ obj.instance_variable_set("@#{ key }", value) if key =~ /^([\w_][\w_0-9]*)$/
+ }
+ obj
+ rescue
+ hash
+ end
+ end
+ end
+
+ def self.fault(hash)
+ if hash.kind_of? Hash and hash.size == 2 and
+ hash.has_key? "faultCode" and hash.has_key? "faultString" and
+ hash["faultCode"].kind_of? Integer and hash["faultString"].kind_of? String
+
+ XMLRPC::FaultException.new(hash["faultCode"], hash["faultString"])
+ else
+ raise "wrong fault-structure: #{hash.inspect}"
+ end
+ end
+
+ end # module Convert
+
+ module XMLParser
+
+ class AbstractTreeParser
+
+ def parseMethodResponse(str)
+ methodResponse_document(createCleanedTree(str))
+ end
+
+ def parseMethodCall(str)
+ methodCall_document(createCleanedTree(str))
+ end
+
+ private
+
+ #
+ # remove all whitespaces but in the tags i4, int, boolean....
+ # and all comments
+ #
+ def removeWhitespacesAndComments(node)
+ remove = []
+ childs = node.childNodes.to_a
+ childs.each do |nd|
+ case _nodeType(nd)
+ when :TEXT
+ # TODO: add nil?
+ unless %w(i4 int boolean string double dateTime.iso8601 base64).include? node.nodeName
+
+ if node.nodeName == "value"
+ if not node.childNodes.to_a.detect {|n| _nodeType(n) == :ELEMENT}.nil?
+ remove << nd if nd.nodeValue.strip == ""
+ end
+ else
+ remove << nd if nd.nodeValue.strip == ""
+ end
+ end
+ when :COMMENT
+ remove << nd
+ else
+ removeWhitespacesAndComments(nd)
+ end
+ end
+
+ remove.each { |i| node.removeChild(i) }
+ end
+
+
+ def nodeMustBe(node, name)
+ cmp = case name
+ when Array
+ name.include?(node.nodeName)
+ when String
+ name == node.nodeName
+ else
+ raise "error"
+ end
+
+ if not cmp then
+ raise "wrong xml-rpc (name)"
+ end
+
+ node
+ end
+
+ #
+ # returns, when successfully the only child-node
+ #
+ def hasOnlyOneChild(node, name=nil)
+ if node.childNodes.to_a.size != 1
+ raise "wrong xml-rpc (size)"
+ end
+ if name != nil then
+ nodeMustBe(node.firstChild, name)
+ end
+ end
+
+
+ def assert(b)
+ if not b then
+ raise "assert-fail"
+ end
+ end
+
+ # the node `node` has empty string or string
+ def text_zero_one(node)
+ nodes = node.childNodes.to_a.size
+
+ if nodes == 1
+ text(node.firstChild)
+ elsif nodes == 0
+ ""
+ else
+ raise "wrong xml-rpc (size)"
+ end
+ end
+
+
+ def integer(node)
+ #TODO: check string for float because to_i returnsa
+ # 0 when wrong string
+ nodeMustBe(node, %w(i4 int))
+ hasOnlyOneChild(node)
+
+ Convert.int(text(node.firstChild))
+ end
+
+ def boolean(node)
+ nodeMustBe(node, "boolean")
+ hasOnlyOneChild(node)
+
+ Convert.boolean(text(node.firstChild))
+ end
+
+ def v_nil(node)
+ nodeMustBe(node, "nil")
+ assert( node.childNodes.to_a.size == 0 )
+ nil
+ end
+
+ def string(node)
+ nodeMustBe(node, "string")
+ text_zero_one(node)
+ end
+
+ def double(node)
+ #TODO: check string for float because to_f returnsa
+ # 0.0 when wrong string
+ nodeMustBe(node, "double")
+ hasOnlyOneChild(node)
+
+ Convert.double(text(node.firstChild))
+ end
+
+ def dateTime(node)
+ nodeMustBe(node, "dateTime.iso8601")
+ hasOnlyOneChild(node)
+
+ Convert.dateTime( text(node.firstChild) )
+ end
+
+ def base64(node)
+ nodeMustBe(node, "base64")
+ #hasOnlyOneChild(node)
+
+ Convert.base64(text_zero_one(node))
+ end
+
+ def member(node)
+ nodeMustBe(node, "member")
+ assert( node.childNodes.to_a.size == 2 )
+
+ [ name(node[0]), value(node[1]) ]
+ end
+
+ def name(node)
+ nodeMustBe(node, "name")
+ #hasOnlyOneChild(node)
+ text_zero_one(node)
+ end
+
+ def array(node)
+ nodeMustBe(node, "array")
+ hasOnlyOneChild(node, "data")
+ data(node.firstChild)
+ end
+
+ def data(node)
+ nodeMustBe(node, "data")
+
+ node.childNodes.to_a.collect do |val|
+ value(val)
+ end
+ end
+
+ def param(node)
+ nodeMustBe(node, "param")
+ hasOnlyOneChild(node, "value")
+ value(node.firstChild)
+ end
+
+ def methodResponse(node)
+ nodeMustBe(node, "methodResponse")
+ hasOnlyOneChild(node, %w(params fault))
+ child = node.firstChild
+
+ case child.nodeName
+ when "params"
+ [ true, params(child,false) ]
+ when "fault"
+ [ false, fault(child) ]
+ else
+ raise "unexpected error"
+ end
+
+ end
+
+ def methodName(node)
+ nodeMustBe(node, "methodName")
+ hasOnlyOneChild(node)
+ text(node.firstChild)
+ end
+
+ def params(node, call=true)
+ nodeMustBe(node, "params")
+
+ if call
+ node.childNodes.to_a.collect do |n|
+ param(n)
+ end
+ else # response (only one param)
+ hasOnlyOneChild(node)
+ param(node.firstChild)
+ end
+ end
+
+ def fault(node)
+ nodeMustBe(node, "fault")
+ hasOnlyOneChild(node, "value")
+ f = value(node.firstChild)
+ Convert.fault(f)
+ end
+
+
+
+ # _nodeType is defined in the subclass
+ def text(node)
+ assert( _nodeType(node) == :TEXT )
+ assert( node.hasChildNodes == false )
+ assert( node.nodeValue != nil )
+
+ node.nodeValue.to_s
+ end
+
+ def struct(node)
+ nodeMustBe(node, "struct")
+
+ hash = {}
+ node.childNodes.to_a.each do |me|
+ n, v = member(me)
+ hash[n] = v
+ end
+
+ Convert.struct(hash)
+ end
+
+
+ def value(node)
+ nodeMustBe(node, "value")
+ nodes = node.childNodes.to_a.size
+ if nodes == 0
+ return ""
+ elsif nodes > 1
+ raise "wrong xml-rpc (size)"
+ end
+
+ child = node.firstChild
+
+ case _nodeType(child)
+ when :TEXT
+ text_zero_one(node)
+ when :ELEMENT
+ case child.nodeName
+ when "i4", "int" then integer(child)
+ when "boolean" then boolean(child)
+ when "string" then string(child)
+ when "double" then double(child)
+ when "dateTime.iso8601" then dateTime(child)
+ when "base64" then base64(child)
+ when "struct" then struct(child)
+ when "array" then array(child)
+ when "nil"
+ if Config::ENABLE_NIL_PARSER
+ v_nil(child)
+ else
+ raise "wrong/unknown XML-RPC type 'nil'"
+ end
+ else
+ raise "wrong/unknown XML-RPC type"
+ end
+ else
+ raise "wrong type of node"
+ end
+
+ end
+
+ def methodCall(node)
+ nodeMustBe(node, "methodCall")
+ assert( (1..2).include?( node.childNodes.to_a.size ) )
+ name = methodName(node[0])
+
+ if node.childNodes.to_a.size == 2 then
+ pa = params(node[1])
+ else # no parameters given
+ pa = []
+ end
+ [name, pa]
+ end
+
+ end # module TreeParserMixin
+
+ class AbstractStreamParser
+ def parseMethodResponse(str)
+ parser = @parser_class.new
+ parser.parse(str)
+ raise "No valid method response!" if parser.method_name != nil
+ if parser.fault != nil
+ # is a fault structure
+ [false, parser.fault]
+ else
+ # is a normal return value
+ raise "Missing return value!" if parser.params.size == 0
+ raise "Too many return values. Only one allowed!" if parser.params.size > 1
+ [true, parser.params[0]]
+ end
+ end
+
+ def parseMethodCall(str)
+ parser = @parser_class.new
+ parser.parse(str)
+ raise "No valid method call - missing method name!" if parser.method_name.nil?
+ [parser.method_name, parser.params]
+ end
+ end
+
+ module StreamParserMixin
+ attr_reader :params
+ attr_reader :method_name
+ attr_reader :fault
+
+ def initialize(*a)
+ super(*a)
+ @params = []
+ @values = []
+ @val_stack = []
+
+ @names = []
+ @name = []
+
+ @structs = []
+ @struct = {}
+
+ @method_name = nil
+ @fault = nil
+
+ @data = nil
+ end
+
+ def startElement(name, attrs=[])
+ @data = nil
+ case name
+ when "value"
+ @value = nil
+ when "nil"
+ raise "wrong/unknown XML-RPC type 'nil'" unless Config::ENABLE_NIL_PARSER
+ @value = :nil
+ when "array"
+ @val_stack << @values
+ @values = []
+ when "struct"
+ @names << @name
+ @name = []
+
+ @structs << @struct
+ @struct = {}
+ end
+ end
+
+ def endElement(name)
+ @data ||= ""
+ case name
+ when "string"
+ @value = @data
+ when "i4", "int"
+ @value = Convert.int(@data)
+ when "boolean"
+ @value = Convert.boolean(@data)
+ when "double"
+ @value = Convert.double(@data)
+ when "dateTime.iso8601"
+ @value = Convert.dateTime(@data)
+ when "base64"
+ @value = Convert.base64(@data)
+ when "value"
+ @value = @data if @value.nil?
+ @values << (@value == :nil ? nil : @value)
+ when "array"
+ @value = @values
+ @values = @val_stack.pop
+ when "struct"
+ @value = Convert.struct(@struct)
+
+ @name = @names.pop
+ @struct = @structs.pop
+ when "name"
+ @name[0] = @data
+ when "member"
+ @struct[@name[0]] = @values.pop
+
+ when "param"
+ @params << @values[0]
+ @values = []
+
+ when "fault"
+ @fault = Convert.fault(@values[0])
+
+ when "methodName"
+ @method_name = @data
+ end
+
+ @data = nil
+ end
+
+ def character(data)
+ if @data
+ @data << data
+ else
+ @data = data
+ end
+ end
+
+ end # module StreamParserMixin
+
+ # ---------------------------------------------------------------------------
+ class XMLStreamParser < AbstractStreamParser
+ def initialize
+ require "xmlparser"
+ @parser_class = Class.new(::XMLParser) {
+ include StreamParserMixin
+ }
+ end
+ end # class XMLStreamParser
+ # ---------------------------------------------------------------------------
+ class NQXMLStreamParser < AbstractStreamParser
+ def initialize
+ require "nqxml/streamingparser"
+ @parser_class = XMLRPCParser
+ end
+
+ class XMLRPCParser
+ include StreamParserMixin
+
+ def parse(str)
+ parser = NQXML::StreamingParser.new(str)
+ parser.each do |ele|
+ case ele
+ when NQXML::Text
+ @data = ele.text
+ #character(ele.text)
+ when NQXML::Tag
+ if ele.isTagEnd
+ endElement(ele.name)
+ else
+ startElement(ele.name, ele.attrs)
+ end
+ end
+ end # do
+ end # method parse
+ end # class XMLRPCParser
+
+ end # class NQXMLStreamParser
+ # ---------------------------------------------------------------------------
+ class XMLTreeParser < AbstractTreeParser
+
+ def initialize
+ require "xmltreebuilder"
+
+ # The new XMLParser library (0.6.2+) uses a slightly different DOM implementation.
+ # The following code removes the differences between both versions.
+ if defined? XML::DOM::Builder
+ return if defined? XML::DOM::Node::DOCUMENT # code below has been already executed
+ klass = XML::DOM::Node
+ klass.const_set("DOCUMENT", klass::DOCUMENT_NODE)
+ klass.const_set("TEXT", klass::TEXT_NODE)
+ klass.const_set("COMMENT", klass::COMMENT_NODE)
+ klass.const_set("ELEMENT", klass::ELEMENT_NODE)
+ end
+ end
+
+ private
+
+ def _nodeType(node)
+ tp = node.nodeType
+ if tp == XML::SimpleTree::Node::TEXT then :TEXT
+ elsif tp == XML::SimpleTree::Node::COMMENT then :COMMENT
+ elsif tp == XML::SimpleTree::Node::ELEMENT then :ELEMENT
+ else :ELSE
+ end
+ end
+
+
+ def methodResponse_document(node)
+ assert( node.nodeType == XML::SimpleTree::Node::DOCUMENT )
+ hasOnlyOneChild(node, "methodResponse")
+
+ methodResponse(node.firstChild)
+ end
+
+ def methodCall_document(node)
+ assert( node.nodeType == XML::SimpleTree::Node::DOCUMENT )
+ hasOnlyOneChild(node, "methodCall")
+
+ methodCall(node.firstChild)
+ end
+
+ def createCleanedTree(str)
+ doc = XML::SimpleTreeBuilder.new.parse(str)
+ doc.documentElement.normalize
+ removeWhitespacesAndComments(doc)
+ doc
+ end
+
+ end # class XMLParser
+ # ---------------------------------------------------------------------------
+ class NQXMLTreeParser < AbstractTreeParser
+
+ def initialize
+ require "nqxml/treeparser"
+ end
+
+ private
+
+ def _nodeType(node)
+ node.nodeType
+ end
+
+ def methodResponse_document(node)
+ methodResponse(node)
+ end
+
+ def methodCall_document(node)
+ methodCall(node)
+ end
+
+ def createCleanedTree(str)
+ doc = ::NQXML::TreeParser.new(str).document.rootNode
+ removeWhitespacesAndComments(doc)
+ doc
+ end
+
+ end # class NQXMLTreeParser
+ # ---------------------------------------------------------------------------
+ class REXMLStreamParser < AbstractStreamParser
+ def initialize
+ require "rexml/document"
+ @parser_class = StreamListener
+ end
+
+ class StreamListener
+ include StreamParserMixin
+
+ alias :tag_start :startElement
+ alias :tag_end :endElement
+ alias :text :character
+ alias :cdata :character
+
+ def method_missing(*a)
+ # ignore
+ end
+
+ def parse(str)
+ parser = REXML::Document.parse_stream(str, self)
+ end
+ end
+
+ end
+ # ---------------------------------------------------------------------------
+ class XMLScanStreamParser < AbstractStreamParser
+ def initialize
+ require "xmlscan/parser"
+ @parser_class = XMLScanParser
+ end
+
+ class XMLScanParser
+ include StreamParserMixin
+
+ Entities = {
+ "lt" => "<",
+ "gt" => ">",
+ "amp" => "&",
+ "quot" => '"',
+ "apos" => "'"
+ }
+
+ def parse(str)
+ parser = XMLScan::XMLParser.new(self)
+ parser.parse(str)
+ end
+
+ alias :on_stag :startElement
+ alias :on_etag :endElement
+
+ def on_stag_end(name); end
+
+ def on_stag_end_empty(name)
+ startElement(name)
+ endElement(name)
+ end
+
+ def on_chardata(str)
+ character(str)
+ end
+
+ def on_cdata(str)
+ character(str)
+ end
+
+ def on_entityref(ent)
+ str = Entities[ent]
+ if str
+ character(str)
+ else
+ raise "unknown entity"
+ end
+ end
+
+ def on_charref(code)
+ character(code.chr)
+ end
+
+ def on_charref_hex(code)
+ character(code.chr)
+ end
+
+ def method_missing(*a)
+ end
+
+ # TODO: call/implement?
+ # valid_name?
+ # valid_chardata?
+ # valid_char?
+ # parse_error
+
+ end
+ end
+ # ---------------------------------------------------------------------------
+ XMLParser = XMLTreeParser
+ NQXMLParser = NQXMLTreeParser
+
+ Classes = [XMLStreamParser, XMLTreeParser,
+ NQXMLStreamParser, NQXMLTreeParser,
+ REXMLStreamParser, XMLScanStreamParser]
+
+ # yields an instance of each installed parser
+ def self.each_installed_parser
+ XMLRPC::XMLParser::Classes.each do |klass|
+ begin
+ yield klass.new
+ rescue LoadError
+ end
+ end
+ end
+
+ end # module XMLParser
+
+
+end # module XMLRPC
+
diff --git a/ruby_1_8_5/lib/xmlrpc/server.rb b/ruby_1_8_5/lib/xmlrpc/server.rb
new file mode 100644
index 0000000000..7900a1d920
--- /dev/null
+++ b/ruby_1_8_5/lib/xmlrpc/server.rb
@@ -0,0 +1,780 @@
+=begin
+= xmlrpc/server.rb
+Copyright (C) 2001, 2002, 2003, 2005 by Michael Neumann (mneumann@ntecs.de)
+
+Released under the same term of license as Ruby.
+
+= Classes
+* ((<XMLRPC::BasicServer>))
+* ((<XMLRPC::CGIServer>))
+* ((<XMLRPC::ModRubyServer>))
+* ((<XMLRPC::Server>))
+* ((<XMLRPC::WEBrickServlet>))
+
+= XMLRPC::BasicServer
+== Description
+Is the base class for all XML-RPC server-types (CGI, standalone).
+You can add handler and set a default handler.
+Do not use this server, as this is/should be an abstract class.
+
+=== How the method to call is found
+The arity (number of accepted arguments) of a handler (method or (({Proc})) object) is
+compared to the given arguments submitted by the client for a RPC ((-Remote Procedure Call-)).
+A handler is only called if it accepts the number of arguments, otherwise the search
+for another handler will go on. When at the end no handler was found,
+the ((<default_handler|XMLRPC::BasicServer#set_default_handler>)) will be called.
+With this technique it is possible to do overloading by number of parameters, but
+only for (({Proc})) handler, because you cannot define two methods of the same name in
+the same class.
+
+
+== Class Methods
+--- XMLRPC::BasicServer.new( class_delim="." )
+ Creates a new (({XMLRPC::BasicServer})) instance, which should not be
+ done, because (({XMLRPC::BasicServer})) is an abstract class. This
+ method should be called from a subclass indirectly by a (({super})) call
+ in the method (({initialize})). The paramter ((|class_delim|)) is used
+ in ((<add_handler|XMLRPC::BasicServer#add_handler>)) when an object is
+ added as handler, to delimit the object-prefix and the method-name.
+
+== Instance Methods
+--- XMLRPC::BasicServer#add_handler( name, signature=nil, help=nil ) { aBlock }
+ Adds ((|aBlock|)) to the list of handlers, with ((|name|)) as the name of the method.
+ Parameters ((|signature|)) and ((|help|)) are used by the Introspection method if specified,
+ where ((|signature|)) is either an Array containing strings each representing a type of it's
+ signature (the first is the return value) or an Array of Arrays if the method has multiple
+ signatures. Value type-names are "int, boolean, double, string, dateTime.iso8601, base64, array, struct".
+
+ Parameter ((|help|)) is a String with informations about how to call this method etc.
+
+ A handler method or code-block can return the types listed at
+ ((<XMLRPC::Client#call|URL:client.html#index:0>)).
+ When a method fails, it can tell it the client by throwing an
+ (({XMLRPC::FaultException})) like in this example:
+ s.add_handler("michael.div") do |a,b|
+ if b == 0
+ raise XMLRPC::FaultException.new(1, "division by zero")
+ else
+ a / b
+ end
+ end
+ The client gets in the case of (({b==0})) an object back of type
+ (({XMLRPC::FaultException})) that has a ((|faultCode|)) and ((|faultString|))
+ field.
+
+--- XMLRPC::BasicServer#add_handler( prefix, obj )
+ This is the second form of ((<add_handler|XMLRPC::BasicServer#add_handler>)).
+ To add an object write:
+ server.add_handler("michael", MyHandlerClass.new)
+ All public methods of (({MyHandlerClass})) are accessible to
+ the XML-RPC clients by (('michael."name of method"')). This is
+ where the ((|class_delim|)) in ((<new|XMLRPC::BasicServer.new>))
+ has it's role, a XML-RPC method-name is defined by
+ ((|prefix|)) + ((|class_delim|)) + (('"name of method"')).
+
+--- XMLRPC::BasicServer#add_handler( interface, obj )
+ This is the third form of ((<add_handler|XMLRPC::BasicServer#add_handler>)).
+
+ Use (({XMLRPC::interface})) to generate an ServiceInterface object, which
+ represents an interface (with signature and help text) for a handler class.
+
+ Parameter ((|interface|)) must be of type (({XMLRPC::ServiceInterface})).
+ Adds all methods of ((|obj|)) which are defined in ((|interface|)) to the
+ server.
+
+ This is the recommended way of adding services to a server!
+
+
+--- XMLRPC::BasicServer#get_default_handler
+ Returns the default-handler, which is called when no handler for
+ a method-name is found.
+ It is a (({Proc})) object or (({nil})).
+
+--- XMLRPC::BasicServer#set_default_handler ( &handler )
+ Sets ((|handler|)) as the default-handler, which is called when
+ no handler for a method-name is found. ((|handler|)) is a code-block.
+ The default-handler is called with the (XML-RPC) method-name as first argument, and
+ the other arguments are the parameters given by the client-call.
+
+ If no block is specified the default of (({XMLRPC::BasicServer})) is used, which raises a
+ XMLRPC::FaultException saying "method missing".
+
+
+--- XMLRPC::BasicServer#set_writer( writer )
+ Sets the XML writer to use for generating XML output.
+ Should be an instance of a class from module (({XMLRPC::XMLWriter})).
+ If this method is not called, then (({XMLRPC::Config::DEFAULT_WRITER})) is used.
+
+--- XMLRPC::BasicServer#set_parser( parser )
+ Sets the XML parser to use for parsing XML documents.
+ Should be an instance of a class from module (({XMLRPC::XMLParser})).
+ If this method is not called, then (({XMLRPC::Config::DEFAULT_PARSER})) is used.
+
+--- XMLRPC::BasicServer#add_introspection
+ Adds the introspection handlers "system.listMethods", "system.methodSignature" and "system.methodHelp",
+ where only the first one works.
+
+--- XMLRPC::BasicServer#add_multicall
+ Adds the multi-call handler "system.multicall".
+
+--- XMLRPC::BasicServer#get_service_hook
+ Returns the service-hook, which is called on each service request (RPC) unless it's (({nil})).
+
+--- XMLRPC::BasicServer#set_service_hook ( &handler )
+ A service-hook is called for each service request (RPC).
+ You can use a service-hook for example to wrap existing methods and catch exceptions of them or
+ convert values to values recognized by XMLRPC. You can disable it by passing (({nil})) as parameter
+ ((|handler|)) .
+
+ The service-hook is called with a (({Proc})) object and with the parameters for this (({Proc})).
+ An example:
+
+ server.set_service_hook {|obj, *args|
+ begin
+ ret = obj.call(*args) # call the original service-method
+ # could convert the return value
+ resuce
+ # rescue exceptions
+ end
+ }
+
+=end
+
+
+
+require "xmlrpc/parser"
+require "xmlrpc/create"
+require "xmlrpc/config"
+require "xmlrpc/utils" # ParserWriterChooseMixin
+
+
+
+module XMLRPC
+
+
+class BasicServer
+
+ include ParserWriterChooseMixin
+ include ParseContentType
+
+ ERR_METHOD_MISSING = 1
+ ERR_UNCAUGHT_EXCEPTION = 2
+ ERR_MC_WRONG_PARAM = 3
+ ERR_MC_MISSING_PARAMS = 4
+ ERR_MC_MISSING_METHNAME = 5
+ ERR_MC_RECURSIVE_CALL = 6
+ ERR_MC_WRONG_PARAM_PARAMS = 7
+ ERR_MC_EXPECTED_STRUCT = 8
+
+
+ def initialize(class_delim=".")
+ @handler = []
+ @default_handler = nil
+ @service_hook = nil
+
+ @class_delim = class_delim
+ @create = nil
+ @parser = nil
+
+ add_multicall if Config::ENABLE_MULTICALL
+ add_introspection if Config::ENABLE_INTROSPECTION
+ end
+
+ def add_handler(prefix, obj_or_signature=nil, help=nil, &block)
+ if block_given?
+ # proc-handler
+ @handler << [prefix, block, obj_or_signature, help]
+ else
+ if prefix.kind_of? String
+ # class-handler
+ raise ArgumentError, "Expected non-nil value" if obj_or_signature.nil?
+ @handler << [prefix + @class_delim, obj_or_signature]
+ elsif prefix.kind_of? XMLRPC::Service::BasicInterface
+ # class-handler with interface
+ # add all methods
+ @handler += prefix.get_methods(obj_or_signature, @class_delim)
+ else
+ raise ArgumentError, "Wrong type for parameter 'prefix'"
+ end
+ end
+ self
+ end
+
+ def get_service_hook
+ @service_hook
+ end
+
+ def set_service_hook(&handler)
+ @service_hook = handler
+ self
+ end
+
+ def get_default_handler
+ @default_handler
+ end
+
+ def set_default_handler (&handler)
+ @default_handler = handler
+ self
+ end
+
+ def add_multicall
+ add_handler("system.multicall", %w(array array), "Multicall Extension") do |arrStructs|
+ unless arrStructs.is_a? Array
+ raise XMLRPC::FaultException.new(ERR_MC_WRONG_PARAM, "system.multicall expects an array")
+ end
+
+ arrStructs.collect {|call|
+ if call.is_a? Hash
+ methodName = call["methodName"]
+ params = call["params"]
+
+ if params.nil?
+ multicall_fault(ERR_MC_MISSING_PARAMS, "Missing params")
+ elsif methodName.nil?
+ multicall_fault(ERR_MC_MISSING_METHNAME, "Missing methodName")
+ else
+ if methodName == "system.multicall"
+ multicall_fault(ERR_MC_RECURSIVE_CALL, "Recursive system.multicall forbidden")
+ else
+ unless params.is_a? Array
+ multicall_fault(ERR_MC_WRONG_PARAM_PARAMS, "Parameter params have to be an Array")
+ else
+ ok, val = call_method(methodName, *params)
+ if ok
+ # correct return value
+ [val]
+ else
+ # exception
+ multicall_fault(val.faultCode, val.faultString)
+ end
+ end
+ end
+ end
+
+ else
+ multicall_fault(ERR_MC_EXPECTED_STRUCT, "system.multicall expected struct")
+ end
+ }
+ end # end add_handler
+ self
+ end
+
+ def add_introspection
+ add_handler("system.listMethods",%w(array), "List methods available on this XML-RPC server") do
+ methods = []
+ @handler.each do |name, obj|
+ if obj.kind_of? Proc
+ methods << name
+ else
+ obj.methods.each {|meth| methods << name + meth}
+ end
+ end
+ methods
+ end
+
+ add_handler("system.methodSignature", %w(array string), "Returns method signature") do |meth|
+ sigs = []
+ @handler.each do |name, obj, sig|
+ if obj.kind_of? Proc and sig != nil and name == meth
+ if sig[0].kind_of? Array
+ # sig contains multiple signatures, e.g. [["array"], ["array", "string"]]
+ sig.each {|s| sigs << s}
+ else
+ # sig is a single signature, e.g. ["array"]
+ sigs << sig
+ end
+ end
+ end
+ sigs.uniq! || sigs # remove eventually duplicated signatures
+ end
+
+ add_handler("system.methodHelp", %w(string string), "Returns help on using this method") do |meth|
+ help = nil
+ @handler.each do |name, obj, sig, hlp|
+ if obj.kind_of? Proc and name == meth
+ help = hlp
+ break
+ end
+ end
+ help || ""
+ end
+
+ self
+ end
+
+
+
+ def process(data)
+ method, params = parser().parseMethodCall(data)
+ handle(method, *params)
+ end
+
+ private # --------------------------------------------------------------
+
+ def multicall_fault(nr, str)
+ {"faultCode" => nr, "faultString" => str}
+ end
+
+ #
+ # method dispatch
+ #
+ def dispatch(methodname, *args)
+ for name, obj in @handler
+ if obj.kind_of? Proc
+ next unless methodname == name
+ else
+ next unless methodname =~ /^#{name}(.+)$/
+ next unless obj.respond_to? $1
+ obj = obj.method($1)
+ end
+
+ if check_arity(obj, args.size)
+ if @service_hook.nil?
+ return obj.call(*args)
+ else
+ return @service_hook.call(obj, *args)
+ end
+ end
+ end
+
+ if @default_handler.nil?
+ raise XMLRPC::FaultException.new(ERR_METHOD_MISSING, "Method #{methodname} missing or wrong number of parameters!")
+ else
+ @default_handler.call(methodname, *args)
+ end
+ end
+
+
+ #
+ # returns true, if the arity of "obj" matches
+ #
+ def check_arity(obj, n_args)
+ ary = obj.arity
+
+ if ary >= 0
+ n_args == ary
+ else
+ n_args >= (ary+1).abs
+ end
+ end
+
+
+
+ def call_method(methodname, *args)
+ begin
+ [true, dispatch(methodname, *args)]
+ rescue XMLRPC::FaultException => e
+ [false, e]
+ rescue Exception => e
+ [false, XMLRPC::FaultException.new(ERR_UNCAUGHT_EXCEPTION, "Uncaught exception #{e.message} in method #{methodname}")]
+ end
+ end
+
+ #
+ #
+ #
+ def handle(methodname, *args)
+ create().methodResponse(*call_method(methodname, *args))
+ end
+
+
+end
+
+
+=begin
+= XMLRPC::CGIServer
+== Synopsis
+ require "xmlrpc/server"
+
+ s = XMLRPC::CGIServer.new
+
+ s.add_handler("michael.add") do |a,b|
+ a + b
+ end
+
+ s.add_handler("michael.div") do |a,b|
+ if b == 0
+ raise XMLRPC::FaultException.new(1, "division by zero")
+ else
+ a / b
+ end
+ end
+
+ s.set_default_handler do |name, *args|
+ raise XMLRPC::FaultException.new(-99, "Method #{name} missing" +
+ " or wrong number of parameters!")
+ end
+
+ s.serve
+
+== Description
+Implements a CGI-based XML-RPC server.
+
+== Superclass
+((<XMLRPC::BasicServer>))
+
+== Class Methods
+--- XMLRPC::CGIServer.new( *a )
+ Creates a new (({XMLRPC::CGIServer})) instance. All parameters given
+ are by-passed to ((<XMLRPC::BasicServer.new>)). You can only create
+ ((*one*)) (({XMLRPC::CGIServer})) instance, because more than one makes
+ no sense.
+
+== Instance Methods
+--- XMLRPC::CGIServer#serve
+ Call this after you have added all you handlers to the server.
+ This method processes a XML-RPC methodCall and sends the answer
+ back to the client.
+ Make sure that you don't write to standard-output in a handler, or in
+ any other part of your program, this would case a CGI-based server to fail!
+=end
+
+class CGIServer < BasicServer
+ @@obj = nil
+
+ def CGIServer.new(*a)
+ @@obj = super(*a) if @@obj.nil?
+ @@obj
+ end
+
+ def initialize(*a)
+ super(*a)
+ end
+
+ def serve
+ catch(:exit_serve) {
+ length = ENV['CONTENT_LENGTH'].to_i
+
+ http_error(405, "Method Not Allowed") unless ENV['REQUEST_METHOD'] == "POST"
+ http_error(400, "Bad Request") unless parse_content_type(ENV['CONTENT_TYPE']).first == "text/xml"
+ http_error(411, "Length Required") unless length > 0
+
+ # TODO: do we need a call to binmode?
+ $stdin.binmode if $stdin.respond_to? :binmode
+ data = $stdin.read(length)
+
+ http_error(400, "Bad Request") if data.nil? or data.size != length
+
+ http_write(process(data), "Content-type" => "text/xml; charset=utf-8")
+ }
+ end
+
+
+ private
+
+ def http_error(status, message)
+ err = "#{status} #{message}"
+ msg = <<-"MSGEND"
+ <html>
+ <head>
+ <title>#{err}</title>
+ </head>
+ <body>
+ <h1>#{err}</h1>
+ <p>Unexpected error occured while processing XML-RPC request!</p>
+ </body>
+ </html>
+ MSGEND
+
+ http_write(msg, "Status" => err, "Content-type" => "text/html")
+ throw :exit_serve # exit from the #serve method
+ end
+
+ def http_write(body, header)
+ h = {}
+ header.each {|key, value| h[key.to_s.capitalize] = value}
+ h['Status'] ||= "200 OK"
+ h['Content-length'] ||= body.size.to_s
+
+ str = ""
+ h.each {|key, value| str << "#{key}: #{value}\r\n"}
+ str << "\r\n#{body}"
+
+ print str
+ end
+
+end
+
+=begin
+= XMLRPC::ModRubyServer
+== Description
+Implements a XML-RPC server, which works with Apache mod_ruby.
+
+Use it in the same way as CGIServer!
+
+== Superclass
+((<XMLRPC::BasicServer>))
+=end
+
+class ModRubyServer < BasicServer
+
+ def initialize(*a)
+ @ap = Apache::request
+ super(*a)
+ end
+
+ def serve
+ catch(:exit_serve) {
+ header = {}
+ @ap.headers_in.each {|key, value| header[key.capitalize] = value}
+
+ length = header['Content-length'].to_i
+
+ http_error(405, "Method Not Allowed") unless @ap.request_method == "POST"
+ http_error(400, "Bad Request") unless parse_content_type(header['Content-type']).first == "text/xml"
+ http_error(411, "Length Required") unless length > 0
+
+ # TODO: do we need a call to binmode?
+ @ap.binmode
+ data = @ap.read(length)
+
+ http_error(400, "Bad Request") if data.nil? or data.size != length
+
+ http_write(process(data), 200, "Content-type" => "text/xml; charset=utf-8")
+ }
+ end
+
+
+ private
+
+ def http_error(status, message)
+ err = "#{status} #{message}"
+ msg = <<-"MSGEND"
+ <html>
+ <head>
+ <title>#{err}</title>
+ </head>
+ <body>
+ <h1>#{err}</h1>
+ <p>Unexpected error occured while processing XML-RPC request!</p>
+ </body>
+ </html>
+ MSGEND
+
+ http_write(msg, status, "Status" => err, "Content-type" => "text/html")
+ throw :exit_serve # exit from the #serve method
+ end
+
+ def http_write(body, status, header)
+ h = {}
+ header.each {|key, value| h[key.to_s.capitalize] = value}
+ h['Status'] ||= "200 OK"
+ h['Content-length'] ||= body.size.to_s
+
+ h.each {|key, value| @ap.headers_out[key] = value }
+ @ap.content_type = h["Content-type"]
+ @ap.status = status.to_i
+ @ap.send_http_header
+
+ @ap.print body
+ end
+
+end
+
+=begin
+= XMLRPC::Server
+== Synopsis
+ require "xmlrpc/server"
+
+ s = XMLRPC::Server.new(8080)
+
+ s.add_handler("michael.add") do |a,b|
+ a + b
+ end
+
+ s.add_handler("michael.div") do |a,b|
+ if b == 0
+ raise XMLRPC::FaultException.new(1, "division by zero")
+ else
+ a / b
+ end
+ end
+
+ s.set_default_handler do |name, *args|
+ raise XMLRPC::FaultException.new(-99, "Method #{name} missing" +
+ " or wrong number of parameters!")
+ end
+
+ s.serve
+
+== Description
+Implements a standalone XML-RPC server. The method (({serve}))) is left if a SIGHUP is sent to the
+program.
+
+== Superclass
+((<XMLRPC::WEBrickServlet>))
+
+== Class Methods
+--- XMLRPC::Server.new( port=8080, host="127.0.0.1", maxConnections=4, stdlog=$stdout, audit=true, debug=true, *a )
+ Creates a new (({XMLRPC::Server})) instance, which is a XML-RPC server listening on
+ port ((|port|)) and accepts requests for the host ((|host|)), which is by default only the localhost.
+ The server is not started, to start it you have to call ((<serve|XMLRPC::Server#serve>)).
+
+ Parameters ((|audit|)) and ((|debug|)) are obsolete!
+
+ All additionally given parameters in ((|*a|)) are by-passed to ((<XMLRPC::BasicServer.new>)).
+
+== Instance Methods
+--- XMLRPC::Server#serve
+ Call this after you have added all you handlers to the server.
+ This method starts the server to listen for XML-RPC requests and answer them.
+
+--- XMLRPC::Server#shutdown
+ Stops and shuts the server down.
+
+=end
+
+class WEBrickServlet < BasicServer; end # forward declaration
+
+class Server < WEBrickServlet
+
+ def initialize(port=8080, host="127.0.0.1", maxConnections=4, stdlog=$stdout, audit=true, debug=true, *a)
+ super(*a)
+ require 'webrick'
+ @server = WEBrick::HTTPServer.new(:Port => port, :BindAddress => host, :MaxClients => maxConnections,
+ :Logger => WEBrick::Log.new(stdlog))
+ @server.mount("/", self)
+ end
+
+ def serve
+ if RUBY_PLATFORM =~ /mingw|mswin32/
+ signal = 1
+ else
+ signal = "HUP"
+ end
+ trap(signal) { @server.shutdown }
+
+ @server.start
+ end
+
+ def shutdown
+ @server.shutdown
+ end
+
+end
+
+=begin
+= XMLRPC::WEBrickServlet
+== Synopsis
+
+ require "webrick"
+ require "xmlrpc/server"
+
+ s = XMLRPC::WEBrickServlet.new
+ s.add_handler("michael.add") do |a,b|
+ a + b
+ end
+
+ s.add_handler("michael.div") do |a,b|
+ if b == 0
+ raise XMLRPC::FaultException.new(1, "division by zero")
+ else
+ a / b
+ end
+ end
+
+ s.set_default_handler do |name, *args|
+ raise XMLRPC::FaultException.new(-99, "Method #{name} missing" +
+ " or wrong number of parameters!")
+ end
+
+ httpserver = WEBrick::HTTPServer.new(:Port => 8080)
+ httpserver.mount("/RPC2", s)
+ trap("HUP") { httpserver.shutdown } # use 1 instead of "HUP" on Windows
+ httpserver.start
+
+== Instance Methods
+
+--- XMLRPC::WEBrickServlet#set_valid_ip( *ip_addr )
+ Specifies the valid IP addresses that are allowed to connect to the server.
+ Each IP is either a (({String})) or a (({Regexp})).
+
+--- XMLRPC::WEBrickServlet#get_valid_ip
+ Return the via method ((<set_valid_ip|XMLRPC::Server#set_valid_ip>)) specified
+ valid IP addresses.
+
+== Description
+Implements a servlet for use with WEBrick, a pure Ruby (HTTP-) server framework.
+
+== Superclass
+((<XMLRPC::BasicServer>))
+
+=end
+
+class WEBrickServlet < BasicServer
+ def initialize(*a)
+ super
+ require "webrick/httpstatus"
+ @valid_ip = nil
+ end
+
+ # deprecated from WEBrick/1.2.2.
+ # but does not break anything.
+ def require_path_info?
+ false
+ end
+
+ def get_instance(config, *options)
+ # TODO: set config & options
+ self
+ end
+
+ def set_valid_ip(*ip_addr)
+ if ip_addr.size == 1 and ip_addr[0].nil?
+ @valid_ip = nil
+ else
+ @valid_ip = ip_addr
+ end
+ end
+
+ def get_valid_ip
+ @valid_ip
+ end
+
+ def service(request, response)
+
+ if @valid_ip
+ raise WEBrick::HTTPStatus::Forbidden unless @valid_ip.any? { |ip| request.peeraddr[3] =~ ip }
+ end
+
+ if request.request_method != "POST"
+ raise WEBrick::HTTPStatus::MethodNotAllowed,
+ "unsupported method `#{request.request_method}'."
+ end
+
+ if parse_content_type(request['Content-type']).first != "text/xml"
+ raise WEBrick::HTTPStatus::BadRequest
+ end
+
+ length = (request['Content-length'] || 0).to_i
+
+ raise WEBrick::HTTPStatus::LengthRequired unless length > 0
+
+ data = request.body
+
+ if data.nil? or data.size != length
+ raise WEBrick::HTTPStatus::BadRequest
+ end
+
+ resp = process(data)
+ if resp.nil? or resp.size <= 0
+ raise WEBrick::HTTPStatus::InternalServerError
+ end
+
+ response.status = 200
+ response['Content-Length'] = resp.size
+ response['Content-Type'] = "text/xml; charset=utf-8"
+ response.body = resp
+ end
+end
+
+
+end # module XMLRPC
+
+
+=begin
+= History
+ $Id: server.rb,v 1.2.2.6 2005/10/04 19:46:35 gotoyuzo Exp $
+=end
+
diff --git a/ruby_1_8_5/lib/xmlrpc/utils.rb b/ruby_1_8_5/lib/xmlrpc/utils.rb
new file mode 100644
index 0000000000..b0a4a0d987
--- /dev/null
+++ b/ruby_1_8_5/lib/xmlrpc/utils.rb
@@ -0,0 +1,165 @@
+#
+# Defines ParserWriterChooseMixin, which makes it possible to choose a
+# different XML writer and/or XML parser then the default one.
+# The Mixin is used in client.rb (class Client) and server.rb (class
+# BasicServer)
+#
+# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
+#
+# $Id: utils.rb,v 1.2.2.1 2005/06/24 20:27:42 mneumann Exp $
+#
+
+module XMLRPC
+
+ #
+ # This module enables a user-class to be marshalled
+ # by XML-RPC for Ruby into a Hash, with one additional
+ # key/value pair "___class___" => ClassName
+ #
+ module Marshallable
+ end
+
+
+ module ParserWriterChooseMixin
+
+ def set_writer(writer)
+ @create = Create.new(writer)
+ self
+ end
+
+ def set_parser(parser)
+ @parser = parser
+ self
+ end
+
+ private
+
+ def create
+ # if set_writer was not already called then call it now
+ if @create.nil? then
+ set_writer(Config::DEFAULT_WRITER.new)
+ end
+ @create
+ end
+
+ def parser
+ # if set_parser was not already called then call it now
+ if @parser.nil? then
+ set_parser(Config::DEFAULT_PARSER.new)
+ end
+ @parser
+ end
+
+ end # module ParserWriterChooseMixin
+
+
+ module Service
+
+ #
+ # base class for Service Interface definitions, used
+ # by BasicServer#add_handler
+ #
+
+ class BasicInterface
+ attr_reader :prefix, :methods
+
+ def initialize(prefix)
+ @prefix = prefix
+ @methods = []
+ end
+
+ def add_method(sig, help=nil, meth_name=nil)
+ mname = nil
+ sig = [sig] if sig.kind_of? String
+
+ sig = sig.collect do |s|
+ name, si = parse_sig(s)
+ raise "Wrong signatures!" if mname != nil and name != mname
+ mname = name
+ si
+ end
+
+ @methods << [mname, meth_name || mname, sig, help]
+ end
+
+ private # ---------------------------------
+
+ def parse_sig(sig)
+ # sig is a String
+ if sig =~ /^\s*(\w+)\s+([^(]+)(\(([^)]*)\))?\s*$/
+ params = [$1]
+ name = $2.strip
+ $4.split(",").each {|i| params << i.strip} if $4 != nil
+ return name, params
+ else
+ raise "Syntax error in signature"
+ end
+ end
+
+ end # class BasicInterface
+
+ #
+ # class which wraps a Service Interface definition, used
+ # by BasicServer#add_handler
+ #
+ class Interface < BasicInterface
+ def initialize(prefix, &p)
+ raise "No interface specified" if p.nil?
+ super(prefix)
+ instance_eval(&p)
+ end
+
+ def get_methods(obj, delim=".")
+ prefix = @prefix + delim
+ @methods.collect { |name, meth, sig, help|
+ [prefix + name, obj.method(meth).to_proc, sig, help]
+ }
+ end
+
+ private # ---------------------------------
+
+ def meth(*a)
+ add_method(*a)
+ end
+
+ end # class Interface
+
+ class PublicInstanceMethodsInterface < BasicInterface
+ def initialize(prefix)
+ super(prefix)
+ end
+
+ def get_methods(obj, delim=".")
+ prefix = @prefix + delim
+ obj.class.public_instance_methods(false).collect { |name|
+ [prefix + name, obj.method(name).to_proc, nil, nil]
+ }
+ end
+ end
+
+
+ end # module Service
+
+
+ #
+ # short-form to create a Service::Interface
+ #
+ def self.interface(prefix, &p)
+ Service::Interface.new(prefix, &p)
+ end
+
+ # short-cut for creating a PublicInstanceMethodsInterface
+ def self.iPIMethods(prefix)
+ Service::PublicInstanceMethodsInterface.new(prefix)
+ end
+
+
+ module ParseContentType
+ def parse_content_type(str)
+ a, *b = str.split(";")
+ return a.strip, *b
+ end
+ end
+
+end # module XMLRPC
+
diff --git a/ruby_1_8_5/lib/xsd/charset.rb b/ruby_1_8_5/lib/xsd/charset.rb
new file mode 100644
index 0000000000..15d5500fce
--- /dev/null
+++ b/ruby_1_8_5/lib/xsd/charset.rb
@@ -0,0 +1,187 @@
+# XSD4R - Charset handling library.
+# Copyright (C) 2001, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+module XSD
+
+
+module Charset
+ @internal_encoding = $KCODE
+
+ class XSDError < StandardError; end
+ class CharsetError < XSDError; end
+ class UnknownCharsetError < CharsetError; end
+ class CharsetConversionError < CharsetError; end
+
+public
+
+ ###
+ ## Maps
+ #
+ EncodingConvertMap = {}
+ def Charset.init
+ EncodingConvertMap[['UTF8', 'X_ISO8859_1']] =
+ Proc.new { |str| str.unpack('U*').pack('C*') }
+ EncodingConvertMap[['X_ISO8859_1', 'UTF8']] =
+ Proc.new { |str| str.unpack('C*').pack('U*') }
+ begin
+ require 'xsd/iconvcharset'
+ @internal_encoding = 'UTF8'
+ sjtag = (/(mswin|bccwin|mingw|cygwin|emx)/ =~ RUBY_PLATFORM) ? 'cp932' :
+ 'shift_jis'
+ EncodingConvertMap[['UTF8', 'EUC' ]] =
+ Proc.new { |str| IconvCharset.safe_iconv("euc-jp", "utf-8", str) }
+ EncodingConvertMap[['EUC' , 'UTF8']] =
+ Proc.new { |str| IconvCharset.safe_iconv("utf-8", "euc-jp", str) }
+ EncodingConvertMap[['EUC' , 'SJIS']] =
+ Proc.new { |str| IconvCharset.safe_iconv(sjtag, "euc-jp", str) }
+ EncodingConvertMap[['UTF8', 'SJIS']] =
+ Proc.new { |str| IconvCharset.safe_iconv(sjtag, "utf-8", str) }
+ EncodingConvertMap[['SJIS', 'UTF8']] =
+ Proc.new { |str| IconvCharset.safe_iconv("utf-8", sjtag, str) }
+ EncodingConvertMap[['SJIS', 'EUC' ]] =
+ Proc.new { |str| IconvCharset.safe_iconv("euc-jp", sjtag, str) }
+ rescue LoadError
+ begin
+ require 'nkf'
+ EncodingConvertMap[['EUC' , 'SJIS']] =
+ Proc.new { |str| NKF.nkf('-sXm0', str) }
+ EncodingConvertMap[['SJIS', 'EUC' ]] =
+ Proc.new { |str| NKF.nkf('-eXm0', str) }
+ rescue LoadError
+ end
+
+ begin
+ require 'uconv'
+ @internal_encoding = 'UTF8'
+ EncodingConvertMap[['UTF8', 'EUC' ]] = Uconv.method(:u8toeuc)
+ EncodingConvertMap[['UTF8', 'SJIS']] = Uconv.method(:u8tosjis)
+ EncodingConvertMap[['EUC' , 'UTF8']] = Uconv.method(:euctou8)
+ EncodingConvertMap[['SJIS', 'UTF8']] = Uconv.method(:sjistou8)
+ rescue LoadError
+ end
+ end
+ end
+ self.init
+
+ CharsetMap = {
+ 'NONE' => 'us-ascii',
+ 'EUC' => 'euc-jp',
+ 'SJIS' => 'shift_jis',
+ 'UTF8' => 'utf-8',
+ 'X_ISO_8859_1' => 'iso-8859-1',
+ 'X_UNKNOWN' => nil,
+ }
+
+
+ ###
+ ## handlers
+ #
+ def Charset.encoding
+ @internal_encoding
+ end
+
+ def Charset.encoding=(encoding)
+ warn("xsd charset is set to #{encoding}") if $DEBUG
+ @internal_encoding = encoding
+ end
+
+ def Charset.xml_encoding_label
+ charset_label(@internal_encoding)
+ end
+
+ def Charset.encoding_to_xml(str, charset)
+ encoding_conv(str, @internal_encoding, charset_str(charset))
+ end
+
+ def Charset.encoding_from_xml(str, charset)
+ encoding_conv(str, charset_str(charset), @internal_encoding)
+ end
+
+ def Charset.encoding_conv(str, enc_from, enc_to)
+ if enc_from == enc_to or enc_from == 'NONE' or enc_to == 'NONE'
+ str
+ elsif converter = EncodingConvertMap[[enc_from, enc_to]]
+ converter.call(str)
+ else
+ raise CharsetConversionError.new(
+ "Converter not found: #{enc_from} -> #{enc_to}")
+ end
+ end
+
+ def Charset.charset_label(encoding)
+ CharsetMap[encoding.upcase]
+ end
+
+ def Charset.charset_str(label)
+ if CharsetMap.respond_to?(:key)
+ CharsetMap.key(label.downcase) || 'X_UNKNOWN'
+ else
+ CharsetMap.index(label.downcase) || 'X_UNKNOWN'
+ end
+ end
+
+ # us_ascii = '[\x00-\x7F]'
+ us_ascii = '[\x9\xa\xd\x20-\x7F]' # XML 1.0 restricted.
+ USASCIIRegexp = Regexp.new("\\A#{us_ascii}*\\z", nil, "NONE")
+
+ twobytes_euc = '(?:[\x8E\xA1-\xFE][\xA1-\xFE])'
+ threebytes_euc = '(?:\x8F[\xA1-\xFE][\xA1-\xFE])'
+ character_euc = "(?:#{us_ascii}|#{twobytes_euc}|#{threebytes_euc})"
+ EUCRegexp = Regexp.new("\\A#{character_euc}*\\z", nil, "NONE")
+
+ # onebyte_sjis = '[\x00-\x7F\xA1-\xDF]'
+ onebyte_sjis = '[\x9\xa\xd\x20-\x7F\xA1-\xDF]' # XML 1.0 restricted.
+ twobytes_sjis = '(?:[\x81-\x9F\xE0-\xFC][\x40-\x7E\x80-\xFC])'
+ character_sjis = "(?:#{onebyte_sjis}|#{twobytes_sjis})"
+ SJISRegexp = Regexp.new("\\A#{character_sjis}*\\z", nil, "NONE")
+
+ # 0xxxxxxx
+ # 110yyyyy 10xxxxxx
+ twobytes_utf8 = '(?:[\xC0-\xDF][\x80-\xBF])'
+ # 1110zzzz 10yyyyyy 10xxxxxx
+ threebytes_utf8 = '(?:[\xE0-\xEF][\x80-\xBF][\x80-\xBF])'
+ # 11110uuu 10uuuzzz 10yyyyyy 10xxxxxx
+ fourbytes_utf8 = '(?:[\xF0-\xF7][\x80-\xBF][\x80-\xBF][\x80-\xBF])'
+ character_utf8 =
+ "(?:#{us_ascii}|#{twobytes_utf8}|#{threebytes_utf8}|#{fourbytes_utf8})"
+ UTF8Regexp = Regexp.new("\\A#{character_utf8}*\\z", nil, "NONE")
+
+ def Charset.is_us_ascii(str)
+ USASCIIRegexp =~ str
+ end
+
+ def Charset.is_utf8(str)
+ UTF8Regexp =~ str
+ end
+
+ def Charset.is_euc(str)
+ EUCRegexp =~ str
+ end
+
+ def Charset.is_sjis(str)
+ SJISRegexp =~ str
+ end
+
+ def Charset.is_ces(str, code = $KCODE)
+ case code
+ when 'NONE'
+ is_us_ascii(str)
+ when 'UTF8'
+ is_utf8(str)
+ when 'EUC'
+ is_euc(str)
+ when 'SJIS'
+ is_sjis(str)
+ else
+ raise UnknownCharsetError.new("Unknown charset: #{code}")
+ end
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/xsd/codegen.rb b/ruby_1_8_5/lib/xsd/codegen.rb
new file mode 100644
index 0000000000..d820ebf1f2
--- /dev/null
+++ b/ruby_1_8_5/lib/xsd/codegen.rb
@@ -0,0 +1,12 @@
+# XSD4R - Generating code library
+# Copyright (C) 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/codegen/gensupport'
+require 'xsd/codegen/moduledef'
+require 'xsd/codegen/classdef'
+require 'xsd/codegen/methoddef'
diff --git a/ruby_1_8_5/lib/xsd/codegen/classdef.rb b/ruby_1_8_5/lib/xsd/codegen/classdef.rb
new file mode 100644
index 0000000000..9eb1ce6607
--- /dev/null
+++ b/ruby_1_8_5/lib/xsd/codegen/classdef.rb
@@ -0,0 +1,203 @@
+# XSD4R - Generating class definition code
+# Copyright (C) 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/codegen/gensupport'
+require 'xsd/codegen/moduledef'
+require 'xsd/codegen/methoddef'
+
+
+module XSD
+module CodeGen
+
+
+class ClassDef < ModuleDef
+ include GenSupport
+
+ def initialize(name, baseclass = nil)
+ super(name)
+ @baseclass = baseclass
+ @classvar = []
+ @attrdef = []
+ end
+
+ def def_classvar(var, value)
+ var = var.sub(/\A@@/, "")
+ unless safevarname?(var)
+ raise ArgumentError.new("#{var} seems to be unsafe")
+ end
+ @classvar << [var, value]
+ end
+
+ def def_attr(attrname, writable = true, varname = nil)
+ unless safevarname?(varname || attrname)
+ raise ArgumentError.new("#{varname || attrname} seems to be unsafe")
+ end
+ @attrdef << [attrname, writable, varname]
+ end
+
+ def dump
+ buf = ""
+ unless @requirepath.empty?
+ buf << dump_requirepath
+ end
+ buf << dump_emptyline unless buf.empty?
+ package = @name.split(/::/)[0..-2]
+ buf << dump_package_def(package) unless package.empty?
+ buf << dump_comment if @comment
+ buf << dump_class_def
+ spacer = false
+ unless @classvar.empty?
+ spacer = true
+ buf << dump_classvar
+ end
+ unless @const.empty?
+ buf << dump_emptyline if spacer
+ spacer = true
+ buf << dump_const
+ end
+ unless @code.empty?
+ buf << dump_emptyline if spacer
+ spacer = true
+ buf << dump_code
+ end
+ unless @attrdef.empty?
+ buf << dump_emptyline if spacer
+ spacer = true
+ buf << dump_attributes
+ end
+ unless @methoddef.empty?
+ buf << dump_emptyline if spacer
+ spacer = true
+ buf << dump_methods
+ end
+ buf << dump_class_def_end
+ buf << dump_package_def_end(package) unless package.empty?
+ buf.gsub(/^\s+$/, '')
+ end
+
+private
+
+ def dump_class_def
+ name = @name.to_s.split(/::/)
+ if @baseclass
+ format("class #{name.last} < #{@baseclass}")
+ else
+ format("class #{name.last}")
+ end
+ end
+
+ def dump_class_def_end
+ str = format("end")
+ end
+
+ def dump_classvar
+ dump_static(
+ @classvar.collect { |var, value|
+ %Q(@@#{var.sub(/^@@/, "")} = #{dump_value(value)})
+ }.join("\n")
+ )
+ end
+
+ def dump_attributes
+ str = ""
+ @attrdef.each do |attrname, writable, varname|
+ varname ||= attrname
+ if attrname == varname
+ str << format(dump_accessor(attrname, writable), 2)
+ end
+ end
+ @attrdef.each do |attrname, writable, varname|
+ varname ||= attrname
+ if attrname != varname
+ str << "\n" unless str.empty?
+ str << format(dump_attribute(attrname, writable, varname), 2)
+ end
+ end
+ str
+ end
+
+ def dump_accessor(attrname, writable)
+ if writable
+ "attr_accessor :#{attrname}"
+ else
+ "attr_reader :#{attrname}"
+ end
+ end
+
+ def dump_attribute(attrname, writable, varname)
+ str = nil
+ mr = MethodDef.new(attrname)
+ mr.definition = "@#{varname}"
+ str = mr.dump
+ if writable
+ mw = MethodDef.new(attrname + "=", 'value')
+ mw.definition = "@#{varname} = value"
+ str << "\n" + mw.dump
+ end
+ str
+ end
+end
+
+
+end
+end
+
+
+if __FILE__ == $0
+ require 'xsd/codegen/classdef'
+ include XSD::CodeGen
+ c = ClassDef.new("Foo::Bar::HobbitName", String)
+ c.def_require("foo/bar")
+ c.comment = <<-EOD
+ foo
+ bar
+ baz
+ EOD
+ c.def_const("FOO", 1)
+ c.def_classvar("@@foo", "var".dump)
+ c.def_classvar("baz", "1".dump)
+ c.def_attr("Foo", true, "foo")
+ c.def_attr("bar")
+ c.def_attr("baz", true)
+ c.def_attr("Foo2", true, "foo2")
+ c.def_attr("foo3", false, "foo3")
+ c.def_method("foo") do
+ <<-EOD
+ foo.bar = 1
+\tbaz.each do |ele|
+\t ele
+ end
+ EOD
+ end
+ c.def_method("baz", "qux") do
+ <<-EOD
+ [1, 2, 3].each do |i|
+ p i
+ end
+ EOD
+ end
+
+ m = MethodDef.new("qux", "quxx", "quxxx") do
+ <<-EOD
+ p quxx + quxxx
+ EOD
+ end
+ m.comment = "hello world\n123"
+ c.add_method(m)
+ c.def_code <<-EOD
+ Foo.new
+ Bar.z
+ EOD
+ c.def_code <<-EOD
+ Foo.new
+ Bar.z
+ EOD
+ c.def_privatemethod("foo", "baz", "*arg", "&block")
+
+ puts c.dump
+end
diff --git a/ruby_1_8_5/lib/xsd/codegen/commentdef.rb b/ruby_1_8_5/lib/xsd/codegen/commentdef.rb
new file mode 100644
index 0000000000..c9493a1363
--- /dev/null
+++ b/ruby_1_8_5/lib/xsd/codegen/commentdef.rb
@@ -0,0 +1,34 @@
+# XSD4R - Generating comment definition code
+# Copyright (C) 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/codegen/gensupport'
+
+
+module XSD
+module CodeGen
+
+
+module CommentDef
+ include GenSupport
+
+ attr_accessor :comment
+
+private
+
+ def dump_comment
+ if /\A#/ =~ @comment
+ format(@comment)
+ else
+ format(@comment).gsub(/^/, '# ')
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/xsd/codegen/gensupport.rb b/ruby_1_8_5/lib/xsd/codegen/gensupport.rb
new file mode 100644
index 0000000000..1e85d3668f
--- /dev/null
+++ b/ruby_1_8_5/lib/xsd/codegen/gensupport.rb
@@ -0,0 +1,166 @@
+# XSD4R - Code generation support
+# Copyright (C) 2004, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+module XSD
+module CodeGen
+
+# from the file 'keywords' in 1.9.
+KEYWORD = {}
+%w(
+__LINE__
+__FILE__
+BEGIN
+END
+alias
+and
+begin
+break
+case
+class
+def
+defined?
+do
+else
+elsif
+end
+ensure
+false
+for
+if
+in
+module
+next
+nil
+not
+or
+redo
+rescue
+retry
+return
+self
+super
+then
+true
+undef
+unless
+until
+when
+while
+yield
+).each { |k| KEYWORD[k] = nil }
+
+module GenSupport
+ def capitalize(target)
+ target.sub(/^([a-z])/) { $1.tr!('[a-z]', '[A-Z]') }
+ end
+ module_function :capitalize
+
+ def uncapitalize(target)
+ target.sub(/^([A-Z])/) { $1.tr!('[A-Z]', '[a-z]') }
+ end
+ module_function :uncapitalize
+
+ def safeconstname(name)
+ safename = name.scan(/[a-zA-Z0-9_]+/).collect { |ele|
+ GenSupport.capitalize(ele)
+ }.join
+ if /^[A-Z]/ !~ safename or keyword?(safename)
+ safename = "C_#{safename}"
+ end
+ safename
+ end
+ module_function :safeconstname
+
+ def safeconstname?(name)
+ /\A[A-Z][a-zA-Z0-9_]*\z/ =~ name and !keyword?(name)
+ end
+ module_function :safeconstname?
+
+ def safemethodname(name)
+ safename = name.scan(/[a-zA-Z0-9_]+/).join('_')
+ safename = uncapitalize(safename)
+ if /^[a-z]/ !~ safename
+ safename = "m_#{safename}"
+ end
+ safename
+ end
+ module_function :safemethodname
+
+ def safemethodname?(name)
+ /\A[a-zA-Z_][a-zA-Z0-9_]*[=!?]?\z/ =~ name
+ end
+ module_function :safemethodname?
+
+ def safevarname(name)
+ safename = uncapitalize(name.scan(/[a-zA-Z0-9_]+/).join('_'))
+ if /^[a-z]/ !~ safename or keyword?(safename)
+ "v_#{safename}"
+ else
+ safename
+ end
+ end
+ module_function :safevarname
+
+ def safevarname?(name)
+ /\A[a-z_][a-zA-Z0-9_]*\z/ =~ name and !keyword?(name)
+ end
+ module_function :safevarname?
+
+ def keyword?(word)
+ KEYWORD.key?(word)
+ end
+ module_function :keyword?
+
+ def format(str, indent = nil)
+ str = trim_eol(str)
+ str = trim_indent(str)
+ if indent
+ str.gsub(/^/, " " * indent)
+ else
+ str
+ end
+ end
+
+private
+
+ def trim_eol(str)
+ str.collect { |line|
+ line.sub(/\r?\n\z/, "") + "\n"
+ }.join
+ end
+
+ def trim_indent(str)
+ indent = nil
+ str = str.collect { |line| untab(line) }.join
+ str.each do |line|
+ head = line.index(/\S/)
+ if !head.nil? and (indent.nil? or head < indent)
+ indent = head
+ end
+ end
+ return str unless indent
+ str.collect { |line|
+ line.sub(/^ {0,#{indent}}/, "")
+ }.join
+ end
+
+ def untab(line, ts = 8)
+ while pos = line.index(/\t/)
+ line = line.sub(/\t/, " " * (ts - (pos % ts)))
+ end
+ line
+ end
+
+ def dump_emptyline
+ "\n"
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/xsd/codegen/methoddef.rb b/ruby_1_8_5/lib/xsd/codegen/methoddef.rb
new file mode 100644
index 0000000000..15892fc5bf
--- /dev/null
+++ b/ruby_1_8_5/lib/xsd/codegen/methoddef.rb
@@ -0,0 +1,63 @@
+# XSD4R - Generating method definition code
+# Copyright (C) 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/codegen/gensupport'
+require 'xsd/codegen/commentdef'
+
+
+module XSD
+module CodeGen
+
+
+class MethodDef
+ include GenSupport
+ include CommentDef
+
+ attr_accessor :definition
+
+ def initialize(name, *params)
+ unless safemethodname?(name)
+ raise ArgumentError.new("name '#{name}' seems to be unsafe")
+ end
+ @name = name
+ @params = params
+ @comment = nil
+ @definition = yield if block_given?
+ end
+
+ def dump
+ buf = ""
+ buf << dump_comment if @comment
+ buf << dump_method_def
+ buf << dump_definition if @definition and !@definition.empty?
+ buf << dump_method_def_end
+ buf
+ end
+
+private
+
+ def dump_method_def
+ if @params.empty?
+ format("def #{@name}")
+ else
+ format("def #{@name}(#{@params.join(", ")})")
+ end
+ end
+
+ def dump_method_def_end
+ format("end")
+ end
+
+ def dump_definition
+ format(@definition, 2)
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/xsd/codegen/moduledef.rb b/ruby_1_8_5/lib/xsd/codegen/moduledef.rb
new file mode 100644
index 0000000000..744af2ff97
--- /dev/null
+++ b/ruby_1_8_5/lib/xsd/codegen/moduledef.rb
@@ -0,0 +1,191 @@
+# XSD4R - Generating module definition code
+# Copyright (C) 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/codegen/gensupport'
+require 'xsd/codegen/methoddef'
+require 'xsd/codegen/commentdef'
+
+
+module XSD
+module CodeGen
+
+
+class ModuleDef
+ include GenSupport
+ include CommentDef
+
+ def initialize(name)
+ @name = name
+ @comment = nil
+ @const = []
+ @code = []
+ @requirepath = []
+ @methoddef = []
+ end
+
+ def def_require(path)
+ @requirepath << path
+ end
+
+ def def_const(const, value)
+ unless safeconstname?(const)
+ raise ArgumentError.new("#{const} seems to be unsafe")
+ end
+ @const << [const, value]
+ end
+
+ def def_code(code)
+ @code << code
+ end
+
+ def def_method(name, *params)
+ add_method(MethodDef.new(name, *params) { yield if block_given? }, :public)
+ end
+ alias def_publicmethod def_method
+
+ def def_protectedmethod(name, *params)
+ add_method(MethodDef.new(name, *params) { yield if block_given? },
+ :protected)
+ end
+
+ def def_privatemethod(name, *params)
+ add_method(MethodDef.new(name, *params) { yield if block_given? }, :private)
+ end
+
+ def add_method(m, visibility = :public)
+ @methoddef << [visibility, m]
+ end
+
+ def dump
+ buf = ""
+ unless @requirepath.empty?
+ buf << dump_requirepath
+ end
+ buf << dump_emptyline unless buf.empty?
+ package = @name.split(/::/)[0..-2]
+ buf << dump_package_def(package) unless package.empty?
+ buf << dump_comment if @comment
+ buf << dump_module_def
+ spacer = false
+ unless @const.empty?
+ buf << dump_emptyline if spacer
+ spacer = true
+ buf << dump_const
+ end
+ unless @code.empty?
+ buf << dump_emptyline if spacer
+ spacer = true
+ buf << dump_code
+ end
+ unless @methoddef.empty?
+ buf << dump_emptyline if spacer
+ spacer = true
+ buf << dump_methods
+ end
+ buf << dump_module_def_end
+ buf << dump_package_def_end(package) unless package.empty?
+ buf.gsub(/^\s+$/, '')
+ end
+
+private
+
+ def dump_requirepath
+ format(
+ @requirepath.collect { |path|
+ %Q(require '#{path}')
+ }.join("\n")
+ )
+ end
+
+ def dump_const
+ dump_static(
+ @const.sort.collect { |var, value|
+ %Q(#{var} = #{dump_value(value)})
+ }.join("\n")
+ )
+ end
+
+ def dump_code
+ dump_static(@code.join("\n"))
+ end
+
+ def dump_static(str)
+ format(str, 2)
+ end
+
+ def dump_methods
+ methods = {}
+ @methoddef.each do |visibility, method|
+ (methods[visibility] ||= []) << method
+ end
+ str = ""
+ [:public, :protected, :private].each do |visibility|
+ if methods[visibility]
+ str << "\n" unless str.empty?
+ str << visibility.to_s << "\n\n" unless visibility == :public
+ str << methods[visibility].collect { |m| format(m.dump, 2) }.join("\n")
+ end
+ end
+ str
+ end
+
+ def dump_value(value)
+ if value.respond_to?(:to_src)
+ value.to_src
+ else
+ value
+ end
+ end
+
+ def dump_package_def(package)
+ format(package.collect { |ele| "module #{ele}" }.join("; ")) + "\n\n"
+ end
+
+ def dump_package_def_end(package)
+ "\n\n" + format(package.collect { |ele| "end" }.join("; "))
+ end
+
+ def dump_module_def
+ name = @name.to_s.split(/::/)
+ format("module #{name.last}")
+ end
+
+ def dump_module_def_end
+ format("end")
+ end
+end
+
+
+end
+end
+
+
+if __FILE__ == $0
+ require 'xsd/codegen/moduledef'
+ include XSD::CodeGen
+ m = ModuleDef.new("Foo::Bar::HobbitName")
+ m.def_require("foo/bar")
+ m.def_require("baz")
+ m.comment = <<-EOD
+ foo
+ bar
+ baz
+ EOD
+ m.def_method("foo") do
+ <<-EOD
+ foo.bar = 1
+ baz.each do |ele|
+ ele + 1
+ end
+ EOD
+ end
+ m.def_method("baz", "qux")
+ #m.def_protectedmethod("aaa")
+ m.def_privatemethod("bbb")
+ puts m.dump
+end
diff --git a/ruby_1_8_5/lib/xsd/datatypes.rb b/ruby_1_8_5/lib/xsd/datatypes.rb
new file mode 100644
index 0000000000..bbe6c8578f
--- /dev/null
+++ b/ruby_1_8_5/lib/xsd/datatypes.rb
@@ -0,0 +1,1269 @@
+# XSD4R - XML Schema Datatype implementation.
+# Copyright (C) 2000, 2001, 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/qname'
+require 'xsd/charset'
+require 'uri'
+
+
+###
+## XMLSchamaDatatypes general definitions.
+#
+module XSD
+
+
+Namespace = 'http://www.w3.org/2001/XMLSchema'
+InstanceNamespace = 'http://www.w3.org/2001/XMLSchema-instance'
+
+AttrType = 'type'
+NilValue = 'true'
+
+AnyTypeLiteral = 'anyType'
+AnySimpleTypeLiteral = 'anySimpleType'
+NilLiteral = 'nil'
+StringLiteral = 'string'
+BooleanLiteral = 'boolean'
+DecimalLiteral = 'decimal'
+FloatLiteral = 'float'
+DoubleLiteral = 'double'
+DurationLiteral = 'duration'
+DateTimeLiteral = 'dateTime'
+TimeLiteral = 'time'
+DateLiteral = 'date'
+GYearMonthLiteral = 'gYearMonth'
+GYearLiteral = 'gYear'
+GMonthDayLiteral = 'gMonthDay'
+GDayLiteral = 'gDay'
+GMonthLiteral = 'gMonth'
+HexBinaryLiteral = 'hexBinary'
+Base64BinaryLiteral = 'base64Binary'
+AnyURILiteral = 'anyURI'
+QNameLiteral = 'QName'
+
+NormalizedStringLiteral = 'normalizedString'
+#3.3.2 token
+#3.3.3 language
+#3.3.4 NMTOKEN
+#3.3.5 NMTOKENS
+#3.3.6 Name
+#3.3.7 NCName
+#3.3.8 ID
+#3.3.9 IDREF
+#3.3.10 IDREFS
+#3.3.11 ENTITY
+#3.3.12 ENTITIES
+IntegerLiteral = 'integer'
+NonPositiveIntegerLiteral = 'nonPositiveInteger'
+NegativeIntegerLiteral = 'negativeInteger'
+LongLiteral = 'long'
+IntLiteral = 'int'
+ShortLiteral = 'short'
+ByteLiteral = 'byte'
+NonNegativeIntegerLiteral = 'nonNegativeInteger'
+UnsignedLongLiteral = 'unsignedLong'
+UnsignedIntLiteral = 'unsignedInt'
+UnsignedShortLiteral = 'unsignedShort'
+UnsignedByteLiteral = 'unsignedByte'
+PositiveIntegerLiteral = 'positiveInteger'
+
+AttrTypeName = QName.new(InstanceNamespace, AttrType)
+AttrNilName = QName.new(InstanceNamespace, NilLiteral)
+
+AnyTypeName = QName.new(Namespace, AnyTypeLiteral)
+AnySimpleTypeName = QName.new(Namespace, AnySimpleTypeLiteral)
+
+class Error < StandardError; end
+class ValueSpaceError < Error; end
+
+
+###
+## The base class of all datatypes with Namespace.
+#
+class NSDBase
+ @@types = []
+
+ attr_accessor :type
+
+ def self.inherited(klass)
+ @@types << klass
+ end
+
+ def self.types
+ @@types
+ end
+
+ def initialize
+ end
+
+ def init(type)
+ @type = type
+ end
+end
+
+
+###
+## The base class of XSD datatypes.
+#
+class XSDAnySimpleType < NSDBase
+ include XSD
+ Type = QName.new(Namespace, AnySimpleTypeLiteral)
+
+ # @data represents canonical space (ex. Integer: 123).
+ attr_reader :data
+ # @is_nil represents this data is nil or not.
+ attr_accessor :is_nil
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+ # true or raise
+ def check_lexical_format(value)
+ screen_data(value)
+ true
+ end
+
+ # set accepts a string which follows lexical space (ex. String: "+123"), or
+ # an object which follows canonical space (ex. Integer: 123).
+ def set(value)
+ if value.nil?
+ @is_nil = true
+ @data = nil
+ _set(nil)
+ else
+ @is_nil = false
+ _set(screen_data(value))
+ end
+ end
+
+ # to_s creates a string which follows lexical space (ex. String: "123").
+ def to_s()
+ if @is_nil
+ ""
+ else
+ _to_s
+ end
+ end
+
+private
+
+ def init(type, value)
+ super(type)
+ set(value)
+ end
+
+ # raises ValueSpaceError if check failed
+ def screen_data(value)
+ value
+ end
+
+ def _set(value)
+ @data = value
+ end
+
+ def _to_s
+ @data.to_s
+ end
+end
+
+class XSDNil < XSDAnySimpleType
+ Type = QName.new(Namespace, NilLiteral)
+ Value = 'true'
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+end
+
+
+###
+## Primitive datatypes.
+#
+class XSDString < XSDAnySimpleType
+ Type = QName.new(Namespace, StringLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def screen_data(value)
+ unless XSD::Charset.is_ces(value, XSD::Charset.encoding)
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
+ end
+ value
+ end
+end
+
+class XSDBoolean < XSDAnySimpleType
+ Type = QName.new(Namespace, BooleanLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def screen_data(value)
+ if value.is_a?(String)
+ str = value.strip
+ if str == 'true' || str == '1'
+ true
+ elsif str == 'false' || str == '0'
+ false
+ else
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
+ end
+ else
+ value ? true : false
+ end
+ end
+end
+
+class XSDDecimal < XSDAnySimpleType
+ Type = QName.new(Namespace, DecimalLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+ def nonzero?
+ (@number != '0')
+ end
+
+private
+
+ def screen_data(d)
+ if d.is_a?(String)
+ # Integer("00012") => 10 in Ruby.
+ d.sub!(/^([+\-]?)0*(?=\d)/, "\\1")
+ end
+ screen_data_str(d)
+ end
+
+ def screen_data_str(str)
+ /^([+\-]?)(\d*)(?:\.(\d*)?)?$/ =~ str.to_s.strip
+ unless Regexp.last_match
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
+ end
+ sign = $1 || '+'
+ int_part = $2
+ frac_part = $3
+ int_part = '0' if int_part.empty?
+ frac_part = frac_part ? frac_part.sub(/0+$/, '') : ''
+ point = - frac_part.size
+ number = int_part + frac_part
+ # normalize
+ if sign == '+'
+ sign = ''
+ elsif sign == '-'
+ if number == '0'
+ sign = ''
+ end
+ end
+ [sign, point, number]
+ end
+
+ def _set(data)
+ if data.nil?
+ @sign = @point = @number = @data = nil
+ return
+ end
+ @sign, @point, @number = data
+ @data = _to_s
+ @data.freeze
+ end
+
+ # 0.0 -> 0; right?
+ def _to_s
+ str = @number.dup
+ if @point.nonzero?
+ str[@number.size + @point, 0] = '.'
+ end
+ @sign + str
+ end
+end
+
+module FloatConstants
+ NaN = 0.0/0.0
+ POSITIVE_INF = +1.0/0.0
+ NEGATIVE_INF = -1.0/0.0
+ POSITIVE_ZERO = +1.0/POSITIVE_INF
+ NEGATIVE_ZERO = -1.0/POSITIVE_INF
+ MIN_POSITIVE_SINGLE = 2.0 ** -149
+end
+
+class XSDFloat < XSDAnySimpleType
+ include FloatConstants
+ Type = QName.new(Namespace, FloatLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def screen_data(value)
+ # "NaN".to_f => 0 in some environment. libc?
+ if value.is_a?(Float)
+ return narrow32bit(value)
+ end
+ str = value.to_s.strip
+ if str == 'NaN'
+ NaN
+ elsif str == 'INF'
+ POSITIVE_INF
+ elsif str == '-INF'
+ NEGATIVE_INF
+ else
+ if /^[+\-\.\deE]+$/ !~ str
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
+ end
+ # Float("-1.4E") might fail on some system.
+ str << '0' if /e$/i =~ str
+ begin
+ return narrow32bit(Float(str))
+ rescue ArgumentError
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
+ end
+ end
+ end
+
+ def _to_s
+ if @data.nan?
+ 'NaN'
+ elsif @data.infinite? == 1
+ 'INF'
+ elsif @data.infinite? == -1
+ '-INF'
+ else
+ sign = XSDFloat.positive?(@data) ? '+' : '-'
+ sign + sprintf("%.10g", @data.abs).sub(/[eE]([+-])?0+/) { 'e' + $1 }
+ end
+ end
+
+ # Convert to single-precision 32-bit floating point value.
+ def narrow32bit(f)
+ if f.nan? || f.infinite?
+ f
+ elsif f.abs < MIN_POSITIVE_SINGLE
+ XSDFloat.positive?(f) ? POSITIVE_ZERO : NEGATIVE_ZERO
+ else
+ f
+ end
+ end
+
+ def self.positive?(value)
+ (1 / value) > 0.0
+ end
+end
+
+# Ruby's Float is double-precision 64-bit floating point value.
+class XSDDouble < XSDAnySimpleType
+ include FloatConstants
+ Type = QName.new(Namespace, DoubleLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def screen_data(value)
+ # "NaN".to_f => 0 in some environment. libc?
+ if value.is_a?(Float)
+ return value
+ end
+ str = value.to_s.strip
+ if str == 'NaN'
+ NaN
+ elsif str == 'INF'
+ POSITIVE_INF
+ elsif str == '-INF'
+ NEGATIVE_INF
+ else
+ begin
+ return Float(str)
+ rescue ArgumentError
+ # '1.4e' cannot be parsed on some architecture.
+ if /e\z/i =~ str
+ begin
+ return Float(str + '0')
+ rescue ArgumentError
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
+ end
+ else
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
+ end
+ end
+ end
+ end
+
+ def _to_s
+ if @data.nan?
+ 'NaN'
+ elsif @data.infinite? == 1
+ 'INF'
+ elsif @data.infinite? == -1
+ '-INF'
+ else
+ sign = (1 / @data > 0.0) ? '+' : '-'
+ sign + sprintf("%.16g", @data.abs).sub(/[eE]([+-])?0+/) { 'e' + $1 }
+ end
+ end
+end
+
+class XSDDuration < XSDAnySimpleType
+ Type = QName.new(Namespace, DurationLiteral)
+
+ attr_accessor :sign
+ attr_accessor :year
+ attr_accessor :month
+ attr_accessor :day
+ attr_accessor :hour
+ attr_accessor :min
+ attr_accessor :sec
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def screen_data(value)
+ /^([+\-]?)P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?)?$/ =~ value.to_s.strip
+ unless Regexp.last_match
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
+ end
+ if ($5 and ((!$2 and !$3 and !$4) or (!$6 and !$7 and !$8)))
+ # Should we allow 'PT5S' here?
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
+ end
+ sign = $1
+ year = $2.to_i
+ month = $3.to_i
+ day = $4.to_i
+ hour = $6.to_i
+ min = $7.to_i
+ sec = $8 ? XSDDecimal.new($8) : 0
+ [sign, year, month, day, hour, min, sec]
+ end
+
+ def _set(data)
+ if data.nil?
+ @sign = @year = @month = @day = @hour = @min = @sec = @data = nil
+ return
+ end
+ @sign, @year, @month, @day, @hour, @min, @sec = data
+ @data = _to_s
+ @data.freeze
+ end
+
+ def _to_s
+ str = ''
+ str << @sign if @sign
+ str << 'P'
+ l = ''
+ l << "#{ @year }Y" if @year.nonzero?
+ l << "#{ @month }M" if @month.nonzero?
+ l << "#{ @day }D" if @day.nonzero?
+ r = ''
+ r << "#{ @hour }H" if @hour.nonzero?
+ r << "#{ @min }M" if @min.nonzero?
+ r << "#{ @sec }S" if @sec.nonzero?
+ str << l
+ if l.empty?
+ str << "0D"
+ end
+ unless r.empty?
+ str << "T" << r
+ end
+ str
+ end
+end
+
+
+require 'rational'
+require 'date'
+
+module XSDDateTimeImpl
+ SecInDay = 86400 # 24 * 60 * 60
+
+ def to_obj(klass)
+ if klass == Time
+ to_time
+ elsif klass == Date
+ to_date
+ elsif klass == DateTime
+ to_datetime
+ else
+ nil
+ end
+ end
+
+ def to_time
+ begin
+ if @data.offset * SecInDay == Time.now.utc_offset
+ d = @data
+ usec = (d.sec_fraction * SecInDay * 1000000).round
+ Time.local(d.year, d.month, d.mday, d.hour, d.min, d.sec, usec)
+ else
+ d = @data.newof
+ usec = (d.sec_fraction * SecInDay * 1000000).round
+ Time.gm(d.year, d.month, d.mday, d.hour, d.min, d.sec, usec)
+ end
+ rescue ArgumentError
+ nil
+ end
+ end
+
+ def to_date
+ Date.new0(@data.class.jd_to_ajd(@data.jd, 0, 0), 0, @data.start)
+ end
+
+ def to_datetime
+ data
+ end
+
+ def tz2of(str)
+ /^(?:Z|(?:([+\-])(\d\d):(\d\d))?)$/ =~ str
+ sign = $1
+ hour = $2.to_i
+ min = $3.to_i
+
+ of = case sign
+ when '+'
+ of = +(hour.to_r * 60 + min) / 1440 # 24 * 60
+ when '-'
+ of = -(hour.to_r * 60 + min) / 1440 # 24 * 60
+ else
+ 0
+ end
+ of
+ end
+
+ def of2tz(offset)
+ diffmin = offset * 24 * 60
+ if diffmin.zero?
+ 'Z'
+ else
+ ((diffmin < 0) ? '-' : '+') << format('%02d:%02d',
+ (diffmin.abs / 60.0).to_i, (diffmin.abs % 60.0).to_i)
+ end
+ end
+
+ def screen_data(t)
+ # convert t to a DateTime as an internal representation.
+ if t.respond_to?(:to_datetime) # 1.9 or later
+ t.to_datetime
+ elsif t.is_a?(DateTime)
+ t
+ elsif t.is_a?(Date)
+ t = screen_data_str(t)
+ t <<= 12 if t.year < 0
+ t
+ elsif t.is_a?(Time)
+ jd = DateTime.civil_to_jd(t.year, t.mon, t.mday, DateTime::ITALY)
+ fr = DateTime.time_to_day_fraction(t.hour, t.min, [t.sec, 59].min) +
+ t.usec.to_r / 1000000 / SecInDay
+ of = t.utc_offset.to_r / SecInDay
+ DateTime.new0(DateTime.jd_to_ajd(jd, fr, of), of, DateTime::ITALY)
+ else
+ screen_data_str(t)
+ end
+ end
+
+ def add_tz(s)
+ s + of2tz(@data.offset)
+ end
+end
+
+class XSDDateTime < XSDAnySimpleType
+ include XSDDateTimeImpl
+ Type = QName.new(Namespace, DateTimeLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def screen_data_str(t)
+ /^([+\-]?\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d(?:\.(\d*))?)(Z|(?:[+\-]\d\d:\d\d)?)?$/ =~ t.to_s.strip
+ unless Regexp.last_match
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
+ end
+ if $1 == '0000'
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
+ end
+ year = $1.to_i
+ if year < 0
+ year += 1
+ end
+ mon = $2.to_i
+ mday = $3.to_i
+ hour = $4.to_i
+ min = $5.to_i
+ sec = $6.to_i
+ secfrac = $7
+ zonestr = $8
+ data = DateTime.civil(year, mon, mday, hour, min, sec, tz2of(zonestr))
+ if secfrac
+ diffday = secfrac.to_i.to_r / (10 ** secfrac.size) / SecInDay
+ data += diffday
+ # FYI: new0 and jd_to_rjd are not necessary to use if you don't have
+ # exceptional reason.
+ end
+ [data, secfrac]
+ end
+
+ def _set(data)
+ if data.nil?
+ @data = @secfrac = nil
+ return
+ end
+ @data, @secfrac = data
+ end
+
+ def _to_s
+ year = (@data.year > 0) ? @data.year : @data.year - 1
+ s = format('%.4d-%02d-%02dT%02d:%02d:%02d',
+ year, @data.mon, @data.mday, @data.hour, @data.min, @data.sec)
+ if @data.sec_fraction.nonzero?
+ if @secfrac
+ s << ".#{ @secfrac }"
+ else
+ s << sprintf("%.16f",
+ (@data.sec_fraction * SecInDay).to_f).sub(/^0/, '').sub(/0*$/, '')
+ end
+ end
+ add_tz(s)
+ end
+end
+
+class XSDTime < XSDAnySimpleType
+ include XSDDateTimeImpl
+ Type = QName.new(Namespace, TimeLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def screen_data_str(t)
+ /^(\d\d):(\d\d):(\d\d(?:\.(\d*))?)(Z|(?:([+\-])(\d\d):(\d\d))?)?$/ =~ t.to_s.strip
+ unless Regexp.last_match
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
+ end
+ hour = $1.to_i
+ min = $2.to_i
+ sec = $3.to_i
+ secfrac = $4
+ zonestr = $5
+ data = DateTime.civil(1, 1, 1, hour, min, sec, tz2of(zonestr))
+ if secfrac
+ diffday = secfrac.to_i.to_r / (10 ** secfrac.size) / SecInDay
+ data += diffday
+ end
+ [data, secfrac]
+ end
+
+ def _set(data)
+ if data.nil?
+ @data = @secfrac = nil
+ return
+ end
+ @data, @secfrac = data
+ end
+
+ def _to_s
+ s = format('%02d:%02d:%02d', @data.hour, @data.min, @data.sec)
+ if @data.sec_fraction.nonzero?
+ if @secfrac
+ s << ".#{ @secfrac }"
+ else
+ s << sprintf("%.16f",
+ (@data.sec_fraction * SecInDay).to_f).sub(/^0/, '').sub(/0*$/, '')
+ end
+ end
+ add_tz(s)
+ end
+end
+
+class XSDDate < XSDAnySimpleType
+ include XSDDateTimeImpl
+ Type = QName.new(Namespace, DateLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def screen_data_str(t)
+ /^([+\-]?\d{4,})-(\d\d)-(\d\d)(Z|(?:([+\-])(\d\d):(\d\d))?)?$/ =~ t.to_s.strip
+ unless Regexp.last_match
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
+ end
+ year = $1.to_i
+ if year < 0
+ year += 1
+ end
+ mon = $2.to_i
+ mday = $3.to_i
+ zonestr = $4
+ DateTime.civil(year, mon, mday, 0, 0, 0, tz2of(zonestr))
+ end
+
+ def _to_s
+ year = (@data.year > 0) ? @data.year : @data.year - 1
+ s = format('%.4d-%02d-%02d', year, @data.mon, @data.mday)
+ add_tz(s)
+ end
+end
+
+class XSDGYearMonth < XSDAnySimpleType
+ include XSDDateTimeImpl
+ Type = QName.new(Namespace, GYearMonthLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def screen_data_str(t)
+ /^([+\-]?\d{4,})-(\d\d)(Z|(?:([+\-])(\d\d):(\d\d))?)?$/ =~ t.to_s.strip
+ unless Regexp.last_match
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
+ end
+ year = $1.to_i
+ if year < 0
+ year += 1
+ end
+ mon = $2.to_i
+ zonestr = $3
+ DateTime.civil(year, mon, 1, 0, 0, 0, tz2of(zonestr))
+ end
+
+ def _to_s
+ year = (@data.year > 0) ? @data.year : @data.year - 1
+ s = format('%.4d-%02d', year, @data.mon)
+ add_tz(s)
+ end
+end
+
+class XSDGYear < XSDAnySimpleType
+ include XSDDateTimeImpl
+ Type = QName.new(Namespace, GYearLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def screen_data_str(t)
+ /^([+\-]?\d{4,})(Z|(?:([+\-])(\d\d):(\d\d))?)?$/ =~ t.to_s.strip
+ unless Regexp.last_match
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
+ end
+ year = $1.to_i
+ if year < 0
+ year += 1
+ end
+ zonestr = $2
+ DateTime.civil(year, 1, 1, 0, 0, 0, tz2of(zonestr))
+ end
+
+ def _to_s
+ year = (@data.year > 0) ? @data.year : @data.year - 1
+ s = format('%.4d', year)
+ add_tz(s)
+ end
+end
+
+class XSDGMonthDay < XSDAnySimpleType
+ include XSDDateTimeImpl
+ Type = QName.new(Namespace, GMonthDayLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def screen_data_str(t)
+ /^(\d\d)-(\d\d)(Z|(?:[+\-]\d\d:\d\d)?)?$/ =~ t.to_s.strip
+ unless Regexp.last_match
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
+ end
+ mon = $1.to_i
+ mday = $2.to_i
+ zonestr = $3
+ DateTime.civil(1, mon, mday, 0, 0, 0, tz2of(zonestr))
+ end
+
+ def _to_s
+ s = format('%02d-%02d', @data.mon, @data.mday)
+ add_tz(s)
+ end
+end
+
+class XSDGDay < XSDAnySimpleType
+ include XSDDateTimeImpl
+ Type = QName.new(Namespace, GDayLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def screen_data_str(t)
+ /^(\d\d)(Z|(?:[+\-]\d\d:\d\d)?)?$/ =~ t.to_s.strip
+ unless Regexp.last_match
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
+ end
+ mday = $1.to_i
+ zonestr = $2
+ DateTime.civil(1, 1, mday, 0, 0, 0, tz2of(zonestr))
+ end
+
+ def _to_s
+ s = format('%02d', @data.mday)
+ add_tz(s)
+ end
+end
+
+class XSDGMonth < XSDAnySimpleType
+ include XSDDateTimeImpl
+ Type = QName.new(Namespace, GMonthLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def screen_data_str(t)
+ /^(\d\d)(Z|(?:[+\-]\d\d:\d\d)?)?$/ =~ t.to_s.strip
+ unless Regexp.last_match
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
+ end
+ mon = $1.to_i
+ zonestr = $2
+ DateTime.civil(1, mon, 1, 0, 0, 0, tz2of(zonestr))
+ end
+
+ def _to_s
+ s = format('%02d', @data.mon)
+ add_tz(s)
+ end
+end
+
+class XSDHexBinary < XSDAnySimpleType
+ Type = QName.new(Namespace, HexBinaryLiteral)
+
+ # String in Ruby could be a binary.
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+ def set_encoded(value)
+ if /^[0-9a-fA-F]*$/ !~ value
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
+ end
+ @data = String.new(value).strip
+ @is_nil = false
+ end
+
+ def string
+ [@data].pack("H*")
+ end
+
+private
+
+ def screen_data(value)
+ value.unpack("H*")[0].tr('a-f', 'A-F')
+ end
+end
+
+class XSDBase64Binary < XSDAnySimpleType
+ Type = QName.new(Namespace, Base64BinaryLiteral)
+
+ # String in Ruby could be a binary.
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+ def set_encoded(value)
+ if /^[A-Za-z0-9+\/=]*$/ !~ value
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
+ end
+ @data = String.new(value).strip
+ @is_nil = false
+ end
+
+ def string
+ @data.unpack("m")[0]
+ end
+
+private
+
+ def screen_data(value)
+ [value].pack("m").strip
+ end
+end
+
+class XSDAnyURI < XSDAnySimpleType
+ Type = QName.new(Namespace, AnyURILiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def screen_data(value)
+ begin
+ URI.parse(value.to_s.strip)
+ rescue URI::InvalidURIError
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
+ end
+ end
+end
+
+class XSDQName < XSDAnySimpleType
+ Type = QName.new(Namespace, QNameLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def screen_data(value)
+ /^(?:([^:]+):)?([^:]+)$/ =~ value.to_s.strip
+ unless Regexp.last_match
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
+ end
+ prefix = $1
+ localpart = $2
+ [prefix, localpart]
+ end
+
+ def _set(data)
+ if data.nil?
+ @prefix = @localpart = @data = nil
+ return
+ end
+ @prefix, @localpart = data
+ @data = _to_s
+ @data.freeze
+ end
+
+ def _to_s
+ if @prefix
+ "#{ @prefix }:#{ @localpart }"
+ else
+ "#{ @localpart }"
+ end
+ end
+end
+
+
+###
+## Derived types
+#
+class XSDNormalizedString < XSDString
+ Type = QName.new(Namespace, NormalizedStringLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def screen_data(value)
+ if /[\t\r\n]/ =~ value
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
+ end
+ super
+ end
+end
+
+class XSDInteger < XSDDecimal
+ Type = QName.new(Namespace, IntegerLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def screen_data_str(str)
+ begin
+ data = Integer(str)
+ rescue ArgumentError
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
+ end
+ unless validate(data)
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
+ end
+ data
+ end
+
+ def _set(value)
+ @data = value
+ end
+
+ def _to_s()
+ @data.to_s
+ end
+
+ def validate(v)
+ max = maxinclusive
+ min = mininclusive
+ (max.nil? or v <= max) and (min.nil? or v >= min)
+ end
+
+ def maxinclusive
+ nil
+ end
+
+ def mininclusive
+ nil
+ end
+
+ PositiveMinInclusive = 1
+ def positive(v)
+ PositiveMinInclusive <= v
+ end
+end
+
+class XSDNonPositiveInteger < XSDInteger
+ Type = QName.new(Namespace, NonPositiveIntegerLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def maxinclusive
+ 0
+ end
+
+ def mininclusive
+ nil
+ end
+end
+
+class XSDNegativeInteger < XSDNonPositiveInteger
+ Type = QName.new(Namespace, NegativeIntegerLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def maxinclusive
+ -1
+ end
+
+ def mininclusive
+ nil
+ end
+end
+
+class XSDLong < XSDInteger
+ Type = QName.new(Namespace, LongLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def maxinclusive
+ +9223372036854775807
+ end
+
+ def mininclusive
+ -9223372036854775808
+ end
+end
+
+class XSDInt < XSDLong
+ Type = QName.new(Namespace, IntLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def maxinclusive
+ +2147483647
+ end
+
+ def mininclusive
+ -2147483648
+ end
+end
+
+class XSDShort < XSDInt
+ Type = QName.new(Namespace, ShortLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def maxinclusive
+ +32767
+ end
+
+ def mininclusive
+ -32768
+ end
+end
+
+class XSDByte < XSDShort
+ Type = QName.new(Namespace, ByteLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def maxinclusive
+ +127
+ end
+
+ def mininclusive
+ -128
+ end
+end
+
+class XSDNonNegativeInteger < XSDInteger
+ Type = QName.new(Namespace, NonNegativeIntegerLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def maxinclusive
+ nil
+ end
+
+ def mininclusive
+ 0
+ end
+end
+
+class XSDUnsignedLong < XSDNonNegativeInteger
+ Type = QName.new(Namespace, UnsignedLongLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def maxinclusive
+ +18446744073709551615
+ end
+
+ def mininclusive
+ 0
+ end
+end
+
+class XSDUnsignedInt < XSDUnsignedLong
+ Type = QName.new(Namespace, UnsignedIntLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def maxinclusive
+ +4294967295
+ end
+
+ def mininclusive
+ 0
+ end
+end
+
+class XSDUnsignedShort < XSDUnsignedInt
+ Type = QName.new(Namespace, UnsignedShortLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def maxinclusive
+ +65535
+ end
+
+ def mininclusive
+ 0
+ end
+end
+
+class XSDUnsignedByte < XSDUnsignedShort
+ Type = QName.new(Namespace, UnsignedByteLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def maxinclusive
+ +255
+ end
+
+ def mininclusive
+ 0
+ end
+end
+
+class XSDPositiveInteger < XSDNonNegativeInteger
+ Type = QName.new(Namespace, PositiveIntegerLiteral)
+
+ def initialize(value = nil)
+ init(Type, value)
+ end
+
+private
+
+ def maxinclusive
+ nil
+ end
+
+ def mininclusive
+ 1
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/xsd/datatypes1999.rb b/ruby_1_8_5/lib/xsd/datatypes1999.rb
new file mode 100644
index 0000000000..c7d6479e54
--- /dev/null
+++ b/ruby_1_8_5/lib/xsd/datatypes1999.rb
@@ -0,0 +1,20 @@
+# XSD4R - XML Schema Datatype 1999 support
+# Copyright (C) 2001, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/datatypes'
+
+
+module XSD
+ Namespace.replace('http://www.w3.org/1999/XMLSchema')
+ InstanceNamespace.replace('http://www.w3.org/1999/XMLSchema-instance')
+ AnyTypeLiteral.replace('ur-type')
+ AnySimpleTypeLiteral.replace('ur-type')
+ NilLiteral.replace('null')
+ NilValue.replace('1')
+ DateTimeLiteral.replace('timeInstant')
+end
diff --git a/ruby_1_8_5/lib/xsd/iconvcharset.rb b/ruby_1_8_5/lib/xsd/iconvcharset.rb
new file mode 100644
index 0000000000..7e629d569b
--- /dev/null
+++ b/ruby_1_8_5/lib/xsd/iconvcharset.rb
@@ -0,0 +1,33 @@
+# XSD4R - Charset handling with iconv.
+# Copyright (C) 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'iconv'
+
+
+module XSD
+
+
+class IconvCharset
+ def self.safe_iconv(to, from, str)
+ iconv = Iconv.new(to, from)
+ out = ""
+ begin
+ out << iconv.iconv(str)
+ rescue Iconv::IllegalSequence => e
+ out << e.success
+ ch, str = e.failed.split(//, 2)
+ out << '?'
+ warn("Failed to convert #{ch}")
+ retry
+ end
+ return out
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/xsd/mapping.rb b/ruby_1_8_5/lib/xsd/mapping.rb
new file mode 100644
index 0000000000..06d30d4cbf
--- /dev/null
+++ b/ruby_1_8_5/lib/xsd/mapping.rb
@@ -0,0 +1,42 @@
+# XSD4R - XML Mapping for Ruby
+# Copyright (C) 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require "soap/parser"
+require 'soap/encodingstyle/literalHandler'
+require "soap/generator"
+require "soap/mapping"
+require "soap/mapping/wsdlliteralregistry"
+
+
+module XSD
+
+
+module Mapping
+ MappingRegistry = SOAP::Mapping::WSDLLiteralRegistry.new
+ MappingOpt = {:default_encodingstyle => SOAP::LiteralNamespace}
+
+ def self.obj2xml(obj, elename = nil, io = nil)
+ if !elename.nil? and !elename.is_a?(XSD::QName)
+ elename = XSD::QName.new(nil, elename)
+ end
+ elename ||= XSD::QName.new(nil, SOAP::Mapping.name2elename(obj.class.to_s))
+ soap = SOAP::Mapping.obj2soap(obj, MappingRegistry)
+ soap.elename = elename
+ generator = SOAP::SOAPGenerator.new(MappingOpt)
+ generator.generate(soap, io)
+ end
+
+ def self.xml2obj(stream)
+ parser = SOAP::Parser.new(MappingOpt)
+ soap = parser.parse(stream)
+ SOAP::Mapping.soap2obj(soap, MappingRegistry)
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/xsd/namedelements.rb b/ruby_1_8_5/lib/xsd/namedelements.rb
new file mode 100644
index 0000000000..a13396bb71
--- /dev/null
+++ b/ruby_1_8_5/lib/xsd/namedelements.rb
@@ -0,0 +1,95 @@
+# XSD4R - WSDL named element collection.
+# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+module XSD
+
+
+class NamedElements
+ include Enumerable
+
+ def initialize
+ @elements = []
+ @cache = {}
+ end
+
+ def dup
+ o = NamedElements.new
+ o.elements = @elements.dup
+ o
+ end
+
+ def freeze
+ super
+ @elements.freeze
+ self
+ end
+
+ def empty?
+ size == 0
+ end
+
+ def size
+ @elements.size
+ end
+
+ def [](idx)
+ if idx.is_a?(Numeric)
+ @elements[idx]
+ else
+ @cache[idx] ||= @elements.find { |item| item.name == idx }
+ end
+ end
+
+ def find_name(name)
+ @elements.find { |item| item.name.name == name }
+ end
+
+ def keys
+ collect { |element| element.name }
+ end
+
+ def each
+ @elements.each do |element|
+ yield(element)
+ end
+ end
+
+ def <<(rhs)
+ @elements << rhs
+ self
+ end
+
+ def delete(rhs)
+ @elements.delete(rhs)
+ end
+
+ def +(rhs)
+ o = NamedElements.new
+ o.elements = @elements + rhs.elements
+ o
+ end
+
+ def concat(rhs)
+ @elements.concat(rhs.elements)
+ self
+ end
+
+ Empty = NamedElements.new.freeze
+
+protected
+
+ def elements=(rhs)
+ @elements = rhs
+ end
+
+ def elements
+ @elements
+ end
+end
+
+end
diff --git a/ruby_1_8_5/lib/xsd/ns.rb b/ruby_1_8_5/lib/xsd/ns.rb
new file mode 100644
index 0000000000..53eeae7130
--- /dev/null
+++ b/ruby_1_8_5/lib/xsd/ns.rb
@@ -0,0 +1,140 @@
+# XSD4R - XML Schema Namespace library
+# Copyright (C) 2000-2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/datatypes'
+
+
+module XSD
+
+
+class NS
+ class Assigner
+ def initialize
+ @count = 0
+ end
+
+ def assign(ns)
+ @count += 1
+ "n#{@count}"
+ end
+ end
+
+ attr_reader :default_namespace
+
+ class FormatError < Error; end
+
+public
+
+ def initialize(tag2ns = {})
+ @tag2ns = tag2ns
+ @assigner = nil
+ @ns2tag = {}
+ @tag2ns.each do |tag, ns|
+ @ns2tag[ns] = tag
+ end
+ @default_namespace = nil
+ end
+
+ def assign(ns, tag = nil)
+ if (tag == '')
+ @default_namespace = ns
+ tag
+ else
+ @assigner ||= Assigner.new
+ tag ||= @assigner.assign(ns)
+ @ns2tag[ns] = tag
+ @tag2ns[tag] = ns
+ tag
+ end
+ end
+
+ def assigned?(ns)
+ @default_namespace == ns or @ns2tag.key?(ns)
+ end
+
+ def assigned_tag?(tag)
+ @tag2ns.key?(tag)
+ end
+
+ def clone_ns
+ cloned = NS.new(@tag2ns.dup)
+ cloned.assigner = @assigner
+ cloned.assign(@default_namespace, '') if @default_namespace
+ cloned
+ end
+
+ def name(name)
+ if (name.namespace == @default_namespace)
+ name.name
+ elsif @ns2tag.key?(name.namespace)
+ "#{@ns2tag[name.namespace]}:#{name.name}"
+ else
+ raise FormatError.new("namespace: #{name.namespace} not defined yet")
+ end
+ end
+
+ def compare(ns, name, rhs)
+ if (ns == @default_namespace)
+ return true if (name == rhs)
+ end
+ @tag2ns.each do |assigned_tag, assigned_ns|
+ if assigned_ns == ns && "#{assigned_tag}:#{name}" == rhs
+ return true
+ end
+ end
+ false
+ end
+
+ # $1 and $2 are necessary.
+ ParseRegexp = Regexp.new('^([^:]+)(?::(.+))?$')
+
+ def parse(str, local = false)
+ if ParseRegexp =~ str
+ if (name = $2) and (ns = @tag2ns[$1])
+ return XSD::QName.new(ns, name)
+ end
+ end
+ XSD::QName.new(local ? nil : @default_namespace, str)
+ end
+
+ # For local attribute key parsing
+ # <foo xmlns="urn:a" xmlns:n1="urn:a" bar="1" n1:baz="2" />
+ # =>
+ # {}bar, {urn:a}baz
+ def parse_local(elem)
+ ParseRegexp =~ elem
+ if $2
+ ns = @tag2ns[$1]
+ name = $2
+ if !ns
+ raise FormatError.new("unknown namespace qualifier: #{$1}")
+ end
+ elsif $1
+ ns = nil
+ name = $1
+ else
+ raise FormatError.new("illegal element format: #{elem}")
+ end
+ XSD::QName.new(ns, name)
+ end
+
+ def each_ns
+ @ns2tag.each do |ns, tag|
+ yield(ns, tag)
+ end
+ end
+
+protected
+
+ def assigner=(assigner)
+ @assigner = assigner
+ end
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/xsd/qname.rb b/ruby_1_8_5/lib/xsd/qname.rb
new file mode 100644
index 0000000000..bb9763a69a
--- /dev/null
+++ b/ruby_1_8_5/lib/xsd/qname.rb
@@ -0,0 +1,78 @@
+# XSD4R - XML QName definition.
+# Copyright (C) 2002, 2003, 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+module XSD
+
+
+class QName
+ attr_accessor :namespace
+ attr_accessor :name
+ attr_accessor :source
+
+ def initialize(namespace = nil, name = nil)
+ @namespace = namespace
+ @name = name
+ @source = nil
+ end
+
+ def dup_name(name)
+ XSD::QName.new(@namespace, name)
+ end
+
+ def dump
+ ns = @namespace.nil? ? 'nil' : @namespace.dump
+ name = @name.nil? ? 'nil' : @name.dump
+ "XSD::QName.new(#{ns}, #{name})"
+ end
+
+ def match(rhs)
+ if rhs.namespace and (rhs.namespace != @namespace)
+ return false
+ end
+ if rhs.name and (rhs.name != @name)
+ return false
+ end
+ true
+ end
+
+ def ==(rhs)
+ !rhs.nil? and @namespace == rhs.namespace and @name == rhs.name
+ end
+
+ def ===(rhs)
+ (self == rhs)
+ end
+
+ def eql?(rhs)
+ (self == rhs)
+ end
+
+ def hash
+ @namespace.hash ^ @name.hash
+ end
+
+ def to_s
+ "{#{ namespace }}#{ name }"
+ end
+
+ def inspect
+ sprintf("#<%s:0x%x %s>", self.class.name, __id__,
+ "{#{ namespace }}#{ name }")
+ end
+
+ NormalizedNameRegexp = /^\{([^}]*)\}(.*)$/
+ def parse(str)
+ NormalizedNameRegexp =~ str
+ self.new($1, $2)
+ end
+
+ EMPTY = QName.new.freeze
+end
+
+
+end
diff --git a/ruby_1_8_5/lib/xsd/xmlparser.rb b/ruby_1_8_5/lib/xsd/xmlparser.rb
new file mode 100644
index 0000000000..e79e36c58c
--- /dev/null
+++ b/ruby_1_8_5/lib/xsd/xmlparser.rb
@@ -0,0 +1,61 @@
+# XSD4R - XML Instance parser library.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/xmlparser/parser'
+
+
+module XSD
+
+
+module XMLParser
+ def create_parser(host, opt)
+ XSD::XMLParser::Parser.create_parser(host, opt)
+ end
+ module_function :create_parser
+
+ # $1 is necessary.
+ NSParseRegexp = Regexp.new('^xmlns:?(.*)$')
+
+ def filter_ns(ns, attrs)
+ return attrs if attrs.nil? or attrs.empty?
+ newattrs = {}
+ attrs.each do |key, value|
+ if (NSParseRegexp =~ key)
+ # '' means 'default namespace'.
+ tag = $1 || ''
+ ns.assign(value, tag)
+ else
+ newattrs[key] = value
+ end
+ end
+ newattrs
+ end
+ module_function :filter_ns
+end
+
+
+end
+
+
+# Try to load XML processor.
+loaded = false
+[
+ 'xsd/xmlparser/xmlparser',
+ 'xsd/xmlparser/xmlscanner',
+ 'xsd/xmlparser/rexmlparser',
+].each do |lib|
+ begin
+ require lib
+ loaded = true
+ break
+ rescue LoadError
+ end
+end
+unless loaded
+ raise RuntimeError.new("XML processor module not found.")
+end
diff --git a/ruby_1_8_5/lib/xsd/xmlparser/parser.rb b/ruby_1_8_5/lib/xsd/xmlparser/parser.rb
new file mode 100644
index 0000000000..ad01d55aa0
--- /dev/null
+++ b/ruby_1_8_5/lib/xsd/xmlparser/parser.rb
@@ -0,0 +1,96 @@
+# XSD4R - XML Instance parser library.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/qname'
+require 'xsd/ns'
+require 'xsd/charset'
+
+
+module XSD
+module XMLParser
+
+
+class Parser
+ class ParseError < Error; end
+ class FormatDecodeError < ParseError; end
+ class UnknownElementError < FormatDecodeError; end
+ class UnknownAttributeError < FormatDecodeError; end
+ class UnexpectedElementError < FormatDecodeError; end
+ class ElementConstraintError < FormatDecodeError; end
+
+ @@parser_factory = nil
+
+ def self.factory
+ @@parser_factory
+ end
+
+ def self.create_parser(host, opt = {})
+ @@parser_factory.new(host, opt)
+ end
+
+ def self.add_factory(factory)
+ if $DEBUG
+ puts "Set #{ factory } as XML processor."
+ end
+ @@parser_factory = factory
+ end
+
+public
+
+ attr_accessor :charset
+
+ def initialize(host, opt = {})
+ @host = host
+ @charset = opt[:charset] || nil
+ end
+
+ def parse(string_or_readable)
+ @textbuf = ''
+ prologue
+ do_parse(string_or_readable)
+ epilogue
+ end
+
+private
+
+ def do_parse(string_or_readable)
+ raise NotImplementError.new(
+ 'Method do_parse must be defined in derived class.')
+ end
+
+ def start_element(name, attrs)
+ @host.start_element(name, attrs)
+ end
+
+ def characters(text)
+ @host.characters(text)
+ end
+
+ def end_element(name)
+ @host.end_element(name)
+ end
+
+ def prologue
+ end
+
+ def epilogue
+ end
+
+ def xmldecl_encoding=(charset)
+ if @charset.nil?
+ @charset = charset
+ else
+ # Definition in a stream (like HTTP) has a priority.
+ p "encoding definition: #{ charset } is ignored." if $DEBUG
+ end
+ end
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/xsd/xmlparser/rexmlparser.rb b/ruby_1_8_5/lib/xsd/xmlparser/rexmlparser.rb
new file mode 100644
index 0000000000..61da9aafc7
--- /dev/null
+++ b/ruby_1_8_5/lib/xsd/xmlparser/rexmlparser.rb
@@ -0,0 +1,54 @@
+# XSD4R - REXMLParser XML parser library.
+# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/xmlparser'
+require 'rexml/streamlistener'
+require 'rexml/document'
+
+
+module XSD
+module XMLParser
+
+
+class REXMLParser < XSD::XMLParser::Parser
+ include REXML::StreamListener
+
+ def do_parse(string_or_readable)
+ source = nil
+ source = REXML::SourceFactory.create_from(string_or_readable)
+ source.encoding = charset if charset
+ # Listener passes a String in utf-8.
+ @charset = 'utf-8'
+ REXML::Document.parse_stream(source, self)
+ end
+
+ def epilogue
+ end
+
+ def tag_start(name, attrs)
+ start_element(name, attrs)
+ end
+
+ def tag_end(name)
+ end_element(name)
+ end
+
+ def text(text)
+ characters(text)
+ end
+
+ def xmldecl(version, encoding, standalone)
+ # Version should be checked.
+ end
+
+ add_factory(self)
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/xsd/xmlparser/xmlparser.rb b/ruby_1_8_5/lib/xsd/xmlparser/xmlparser.rb
new file mode 100644
index 0000000000..6db914cd37
--- /dev/null
+++ b/ruby_1_8_5/lib/xsd/xmlparser/xmlparser.rb
@@ -0,0 +1,50 @@
+# XSD4R - XMLParser XML parser library.
+# Copyright (C) 2001, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/xmlparser'
+require 'xml/parser'
+
+
+module XSD
+module XMLParser
+
+
+class XMLParser < XSD::XMLParser::Parser
+ class Listener < XML::Parser
+ begin
+ require 'xml/encoding-ja'
+ include XML::Encoding_ja
+ rescue LoadError
+ # uconv may not be installed.
+ end
+ end
+
+ def do_parse(string_or_readable)
+ # XMLParser passes a String in utf-8.
+ @charset = 'utf-8'
+ @parser = Listener.new
+ @parser.parse(string_or_readable) do |type, name, data|
+ case type
+ when XML::Parser::START_ELEM
+ start_element(name, data)
+ when XML::Parser::END_ELEM
+ end_element(name)
+ when XML::Parser::CDATA
+ characters(data)
+ else
+ raise FormatDecodeError.new("Unexpected XML: #{ type }/#{ name }/#{ data }.")
+ end
+ end
+ end
+
+ add_factory(self)
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/xsd/xmlparser/xmlscanner.rb b/ruby_1_8_5/lib/xsd/xmlparser/xmlscanner.rb
new file mode 100644
index 0000000000..c80dc23910
--- /dev/null
+++ b/ruby_1_8_5/lib/xsd/xmlparser/xmlscanner.rb
@@ -0,0 +1,147 @@
+# XSD4R - XMLScan XML parser library.
+# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+
+# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
+# redistribute it and/or modify it under the same terms of Ruby's license;
+# either the dual license version in 2003, or any later version.
+
+
+require 'xsd/xmlparser'
+require 'xmlscan/scanner'
+
+
+module XSD
+module XMLParser
+
+
+class XMLScanner < XSD::XMLParser::Parser
+ include XMLScan::Visitor
+
+ def do_parse(string_or_readable)
+ @attrs = {}
+ @curattr = nil
+ @scanner = XMLScan::XMLScanner.new(self)
+ @scanner.kcode = XSD::Charset.charset_str(charset) if charset
+ @scanner.parse(string_or_readable)
+ end
+
+ def scanner_kcode=(charset)
+ @scanner.kcode = XSD::Charset.charset_str(charset) if charset
+ self.xmldecl_encoding = charset
+ end
+
+ ENTITY_REF_MAP = {
+ 'lt' => '<',
+ 'gt' => '>',
+ 'amp' => '&',
+ 'quot' => '"',
+ 'apos' => '\''
+ }
+
+ def parse_error(msg)
+ raise ParseError.new(msg)
+ end
+
+ def wellformed_error(msg)
+ raise NotWellFormedError.new(msg)
+ end
+
+ def valid_error(msg)
+ raise NotValidError.new(msg)
+ end
+
+ def warning(msg)
+ p msg if $DEBUG
+ end
+
+ # def on_xmldecl; end
+
+ def on_xmldecl_version(str)
+ # 1.0 expected.
+ end
+
+ def on_xmldecl_encoding(str)
+ self.scanner_kcode = str
+ end
+
+ # def on_xmldecl_standalone(str); end
+
+ # def on_xmldecl_other(name, value); end
+
+ # def on_xmldecl_end; end
+
+ # def on_doctype(root, pubid, sysid); end
+
+ # def on_prolog_space(str); end
+
+ # def on_comment(str); end
+
+ # def on_pi(target, pi); end
+
+ def on_chardata(str)
+ characters(str)
+ end
+
+ # def on_cdata(str); end
+
+ def on_etag(name)
+ end_element(name)
+ end
+
+ def on_entityref(ref)
+ characters(ENTITY_REF_MAP[ref])
+ end
+
+ def on_charref(code)
+ characters([code].pack('U'))
+ end
+
+ def on_charref_hex(code)
+ on_charref(code)
+ end
+
+ # def on_start_document; end
+
+ # def on_end_document; end
+
+ def on_stag(name)
+ @attrs = {}
+ end
+
+ def on_attribute(name)
+ @attrs[name] = @curattr = ''
+ end
+
+ def on_attr_value(str)
+ @curattr << str
+ end
+
+ def on_attr_entityref(ref)
+ @curattr << ENTITY_REF_MAP[ref]
+ end
+
+ def on_attr_charref(code)
+ @curattr << [code].pack('U')
+ end
+
+ def on_attr_charref_hex(code)
+ on_attr_charref(code)
+ end
+
+ # def on_attribute_end(name); end
+
+ def on_stag_end_empty(name)
+ on_stag_end(name)
+ on_etag(name)
+ end
+
+ def on_stag_end(name)
+ start_element(name, @attrs)
+ end
+
+ add_factory(self)
+end
+
+
+end
+end
diff --git a/ruby_1_8_5/lib/yaml.rb b/ruby_1_8_5/lib/yaml.rb
new file mode 100644
index 0000000000..ce3aced1e6
--- /dev/null
+++ b/ruby_1_8_5/lib/yaml.rb
@@ -0,0 +1,436 @@
+# -*- mode: ruby; ruby-indent-level: 4; tab-width: 4 -*- vim: sw=4 ts=4
+# $Id: yaml.rb,v 1.9.2.12 2005/12/26 08:18:31 matz Exp $
+#
+# = yaml.rb: top-level module with methods for loading and parsing YAML documents
+#
+# Author:: why the lucky stiff
+#
+
+require 'stringio'
+require 'yaml/error'
+require 'yaml/syck'
+require 'yaml/tag'
+require 'yaml/stream'
+require 'yaml/constants'
+
+# == YAML
+#
+# YAML(tm) (rhymes with 'camel') is a
+# straightforward machine parsable data serialization format designed for
+# human readability and interaction with scripting languages such as Perl
+# and Python. YAML is optimized for data serialization, formatted
+# dumping, configuration files, log files, Internet messaging and
+# filtering. This specification describes the YAML information model and
+# serialization format. Together with the Unicode standard for characters, it
+# provides all the information necessary to understand YAML Version 1.0
+# and construct computer programs to process it.
+#
+# See http://yaml.org/ for more information. For a quick tutorial, please
+# visit YAML In Five Minutes (http://yaml.kwiki.org/?YamlInFiveMinutes).
+#
+# == About This Library
+#
+# The YAML 1.0 specification outlines four stages of YAML loading and dumping.
+# This library honors all four of those stages, although data is really only
+# available to you in three stages.
+#
+# The four stages are: native, representation, serialization, and presentation.
+#
+# The native stage refers to data which has been loaded completely into Ruby's
+# own types. (See +YAML::load+.)
+#
+# The representation stage means data which has been composed into
+# +YAML::BaseNode+ objects. In this stage, the document is available as a
+# tree of node objects. You can perform YPath queries and transformations
+# at this level. (See +YAML::parse+.)
+#
+# The serialization stage happens inside the parser. The YAML parser used in
+# Ruby is called Syck. Serialized nodes are available in the extension as
+# SyckNode structs.
+#
+# The presentation stage is the YAML document itself. This is accessible
+# to you as a string. (See +YAML::dump+.)
+#
+# For more information about the various information models, see Chapter
+# 3 of the YAML 1.0 Specification (http://yaml.org/spec/#id2491269).
+#
+# The YAML module provides quick access to the most common loading (YAML::load)
+# and dumping (YAML::dump) tasks. This module also provides an API for registering
+# global types (YAML::add_domain_type).
+#
+# == Example
+#
+# A simple round-trip (load and dump) of an object.
+#
+# require "yaml"
+#
+# test_obj = ["dogs", "cats", "badgers"]
+#
+# yaml_obj = YAML::dump( test_obj )
+# # -> ---
+# - dogs
+# - cats
+# - badgers
+# ruby_obj = YAML::load( yaml_obj )
+# # => ["dogs", "cats", "badgers"]
+# ruby_obj == test_obj
+# # => true
+#
+# To register your custom types with the global resolver, use +add_domain_type+.
+#
+# YAML::add_domain_type( "your-site.com,2004", "widget" ) do |type, val|
+# Widget.new( val )
+# end
+#
+module YAML
+
+ Resolver = YAML::Syck::Resolver
+ DefaultResolver = YAML::Syck::DefaultResolver
+ DefaultResolver.use_types_at( @@tagged_classes )
+ GenericResolver = YAML::Syck::GenericResolver
+ Parser = YAML::Syck::Parser
+ Emitter = YAML::Syck::Emitter
+
+ # Returns a new default parser
+ def YAML.parser; Parser.new.set_resolver( YAML.resolver ); end
+ # Returns a new generic parser
+ def YAML.generic_parser; Parser.new.set_resolver( GenericResolver ); end
+ # Returns the default resolver
+ def YAML.resolver; DefaultResolver; end
+ # Returns a new default emitter
+ def YAML.emitter; Emitter.new.set_resolver( YAML.resolver ); end
+
+ #
+ # Converts _obj_ to YAML and writes the YAML result to _io_.
+ #
+ # File.open( 'animals.yaml', 'w' ) do |out|
+ # YAML.dump( ['badger', 'elephant', 'tiger'], out )
+ # end
+ #
+ # If no _io_ is provided, a string containing the dumped YAML
+ # is returned.
+ #
+ # YAML.dump( :locked )
+ # #=> "--- :locked"
+ #
+ def YAML.dump( obj, io = nil )
+ obj.to_yaml( io || io2 = StringIO.new )
+ io || ( io2.rewind; io2.read )
+ end
+
+ #
+ # Load a document from the current _io_ stream.
+ #
+ # File.open( 'animals.yaml' ) { |yf| YAML::load( yf ) }
+ # #=> ['badger', 'elephant', 'tiger']
+ #
+ # Can also load from a string.
+ #
+ # YAML.load( "--- :locked" )
+ # #=> :locked
+ #
+ def YAML.load( io )
+ yp = parser.load( io )
+ end
+
+ #
+ # Load a document from the file located at _filepath_.
+ #
+ # YAML.load_file( 'animals.yaml' )
+ # #=> ['badger', 'elephant', 'tiger']
+ #
+ def YAML.load_file( filepath )
+ File.open( filepath ) do |f|
+ load( f )
+ end
+ end
+
+ #
+ # Parse the first document from the current _io_ stream
+ #
+ # File.open( 'animals.yaml' ) { |yf| YAML::load( yf ) }
+ # #=> #<YAML::Syck::Node:0x82ccce0
+ # @kind=:seq,
+ # @value=
+ # [#<YAML::Syck::Node:0x82ccd94
+ # @kind=:scalar,
+ # @type_id="str",
+ # @value="badger">,
+ # #<YAML::Syck::Node:0x82ccd58
+ # @kind=:scalar,
+ # @type_id="str",
+ # @value="elephant">,
+ # #<YAML::Syck::Node:0x82ccd1c
+ # @kind=:scalar,
+ # @type_id="str",
+ # @value="tiger">]>
+ #
+ # Can also load from a string.
+ #
+ # YAML.parse( "--- :locked" )
+ # #=> #<YAML::Syck::Node:0x82edddc
+ # @type_id="tag:ruby.yaml.org,2002:sym",
+ # @value=":locked", @kind=:scalar>
+ #
+ def YAML.parse( io )
+ yp = generic_parser.load( io )
+ end
+
+ #
+ # Parse a document from the file located at _filepath_.
+ #
+ # YAML.parse_file( 'animals.yaml' )
+ # #=> #<YAML::Syck::Node:0x82ccce0
+ # @kind=:seq,
+ # @value=
+ # [#<YAML::Syck::Node:0x82ccd94
+ # @kind=:scalar,
+ # @type_id="str",
+ # @value="badger">,
+ # #<YAML::Syck::Node:0x82ccd58
+ # @kind=:scalar,
+ # @type_id="str",
+ # @value="elephant">,
+ # #<YAML::Syck::Node:0x82ccd1c
+ # @kind=:scalar,
+ # @type_id="str",
+ # @value="tiger">]>
+ #
+ def YAML.parse_file( filepath )
+ File.open( filepath ) do |f|
+ parse( f )
+ end
+ end
+
+ #
+ # Calls _block_ with each consecutive document in the YAML
+ # stream contained in _io_.
+ #
+ # File.open( 'many-docs.yaml' ) do |yf|
+ # YAML.each_document( yf ) do |ydoc|
+ # ## ydoc contains the single object
+ # ## from the YAML document
+ # end
+ # end
+ #
+ def YAML.each_document( io, &block )
+ yp = parser.load_documents( io, &block )
+ end
+
+ #
+ # Calls _block_ with each consecutive document in the YAML
+ # stream contained in _io_.
+ #
+ # File.open( 'many-docs.yaml' ) do |yf|
+ # YAML.load_documents( yf ) do |ydoc|
+ # ## ydoc contains the single object
+ # ## from the YAML document
+ # end
+ # end
+ #
+ def YAML.load_documents( io, &doc_proc )
+ YAML.each_document( io, &doc_proc )
+ end
+
+ #
+ # Calls _block_ with a tree of +YAML::BaseNodes+, one tree for
+ # each consecutive document in the YAML stream contained in _io_.
+ #
+ # File.open( 'many-docs.yaml' ) do |yf|
+ # YAML.each_node( yf ) do |ydoc|
+ # ## ydoc contains a tree of nodes
+ # ## from the YAML document
+ # end
+ # end
+ #
+ def YAML.each_node( io, &doc_proc )
+ yp = generic_parser.load_documents( io, &doc_proc )
+ end
+
+ #
+ # Calls _block_ with a tree of +YAML::BaseNodes+, one tree for
+ # each consecutive document in the YAML stream contained in _io_.
+ #
+ # File.open( 'many-docs.yaml' ) do |yf|
+ # YAML.parse_documents( yf ) do |ydoc|
+ # ## ydoc contains a tree of nodes
+ # ## from the YAML document
+ # end
+ # end
+ #
+ def YAML.parse_documents( io, &doc_proc )
+ YAML.each_node( io, &doc_proc )
+ end
+
+ #
+ # Loads all documents from the current _io_ stream,
+ # returning a +YAML::Stream+ object containing all
+ # loaded documents.
+ #
+ def YAML.load_stream( io )
+ d = nil
+ parser.load_documents( io ) do |doc|
+ d = YAML::Stream.new if not d
+ d.add( doc )
+ end
+ return d
+ end
+
+ #
+ # Returns a YAML stream containing each of the items in +objs+,
+ # each having their own document.
+ #
+ # YAML.dump_stream( 0, [], {} )
+ # #=> --- 0
+ # --- []
+ # --- {}
+ #
+ def YAML.dump_stream( *objs )
+ d = YAML::Stream.new
+ objs.each do |doc|
+ d.add( doc )
+ end
+ d.emit
+ end
+
+ #
+ # Add a global handler for a YAML domain type.
+ #
+ def YAML.add_domain_type( domain, type_tag, &transfer_proc )
+ resolver.add_type( "tag:#{ domain }:#{ type_tag }", transfer_proc )
+ end
+
+ #
+ # Add a transfer method for a builtin type
+ #
+ def YAML.add_builtin_type( type_tag, &transfer_proc )
+ resolver.add_type( "tag:yaml.org,2002:#{ type_tag }", transfer_proc )
+ end
+
+ #
+ # Add a transfer method for a builtin type
+ #
+ def YAML.add_ruby_type( type_tag, &transfer_proc )
+ resolver.add_type( "tag:ruby.yaml.org,2002:#{ type_tag }", transfer_proc )
+ end
+
+ #
+ # Add a private document type
+ #
+ def YAML.add_private_type( type_re, &transfer_proc )
+ resolver.add_type( "x-private:" + type_re, transfer_proc )
+ end
+
+ #
+ # Detect typing of a string
+ #
+ def YAML.detect_implicit( val )
+ resolver.detect_implicit( val )
+ end
+
+ #
+ # Convert a type_id to a taguri
+ #
+ def YAML.tagurize( val )
+ resolver.tagurize( val )
+ end
+
+ #
+ # Apply a transfer method to a Ruby object
+ #
+ def YAML.transfer( type_id, obj )
+ resolver.transfer( YAML.tagurize( type_id ), obj )
+ end
+
+ #
+ # Apply any implicit a node may qualify for
+ #
+ def YAML.try_implicit( obj )
+ YAML.transfer( YAML.detect_implicit( obj ), obj )
+ end
+
+ #
+ # Method to extract colon-seperated type and class, returning
+ # the type and the constant of the class
+ #
+ def YAML.read_type_class( type, obj_class )
+ scheme, domain, type, tclass = type.split( ':', 4 )
+ tclass.split( "::" ).each { |c| obj_class = obj_class.const_get( c ) } if tclass
+ return [ type, obj_class ]
+ end
+
+ #
+ # Allocate blank object
+ #
+ def YAML.object_maker( obj_class, val )
+ if Hash === val
+ o = obj_class.allocate
+ val.each_pair { |k,v|
+ o.instance_variable_set("@#{k}", v)
+ }
+ o
+ else
+ raise YAML::Error, "Invalid object explicitly tagged !ruby/Object: " + val.inspect
+ end
+ end
+
+ #
+ # Allocate an Emitter if needed
+ #
+ def YAML.quick_emit( oid, opts = {}, &e )
+ out =
+ if opts.is_a? YAML::Emitter
+ opts
+ else
+ emitter.reset( opts )
+ end
+ out.emit( oid, &e )
+ end
+
+end
+
+require 'yaml/rubytypes'
+require 'yaml/types'
+
+module Kernel
+ #
+ # ryan:: You know how Kernel.p is a really convenient way to dump ruby
+ # structures? The only downside is that it's not as legible as
+ # YAML.
+ #
+ # _why:: (listening)
+ #
+ # ryan:: I know you don't want to urinate all over your users' namespaces.
+ # But, on the other hand, convenience of dumping for debugging is,
+ # IMO, a big YAML use case.
+ #
+ # _why:: Go nuts! Have a pony parade!
+ #
+ # ryan:: Either way, I certainly will have a pony parade.
+ #
+
+ # Prints any supplied _objects_ out in YAML. Intended as
+ # a variation on +Kernel::p+.
+ #
+ # S = Struct.new(:name, :state)
+ # s = S['dave', 'TX']
+ # y s
+ #
+ # _produces:_
+ #
+ # --- !ruby/struct:S
+ # name: dave
+ # state: TX
+ #
+ def y( object, *objects )
+ objects.unshift object
+ puts( if objects.length == 1
+ YAML::dump( *objects )
+ else
+ YAML::dump_stream( *objects )
+ end )
+ end
+ private :y
+end
+
+
diff --git a/ruby_1_8_5/lib/yaml/baseemitter.rb b/ruby_1_8_5/lib/yaml/baseemitter.rb
new file mode 100644
index 0000000000..1aef152749
--- /dev/null
+++ b/ruby_1_8_5/lib/yaml/baseemitter.rb
@@ -0,0 +1,247 @@
+#
+# BaseEmitter
+#
+
+require 'yaml/constants'
+require 'yaml/encoding'
+require 'yaml/error'
+
+module YAML
+
+ module BaseEmitter
+
+ def options( opt = nil )
+ if opt
+ @options[opt] || YAML::DEFAULTS[opt]
+ else
+ @options
+ end
+ end
+
+ def options=( opt )
+ @options = opt
+ end
+
+ #
+ # Emit binary data
+ #
+ def binary_base64( value )
+ self << "!binary "
+ self.node_text( [value].pack("m"), '|' )
+ end
+
+ #
+ # Emit plain, normal flowing text
+ #
+ def node_text( value, block = nil )
+ @seq_map = false
+ valx = value.dup
+ unless block
+ block =
+ if options(:UseBlock)
+ '|'
+ elsif not options(:UseFold) and valx =~ /\n[ \t]/ and not valx =~ /#{YAML::ESCAPE_CHAR}/
+ '|'
+ else
+ '>'
+ end
+
+ indt = $&.to_i if block =~ /\d+/
+ if valx =~ /(\A\n*[ \t#]|^---\s+)/
+ indt = options(:Indent) unless indt.to_i > 0
+ block += indt.to_s
+ end
+
+ block +=
+ if valx =~ /\n\Z\n/
+ "+"
+ elsif valx =~ /\Z\n/
+ ""
+ else
+ "-"
+ end
+ end
+ block += "\n"
+ if block[0] == ?"
+ esc_skip = ( "\t\n" unless valx =~ /^[ \t]/ ) || ""
+ valx = fold( YAML::escape( valx, esc_skip ) + "\"" ).chomp
+ self << '"' + indent_text( valx, indt, false )
+ else
+ if block[0] == ?>
+ valx = fold( valx )
+ end
+ #p [block, indt]
+ self << block + indent_text( valx, indt )
+ end
+ end
+
+ #
+ # Emit a simple, unqouted string
+ #
+ def simple( value )
+ @seq_map = false
+ self << value.to_s
+ end
+
+ #
+ # Emit double-quoted string
+ #
+ def double( value )
+ "\"#{YAML.escape( value )}\""
+ end
+
+ #
+ # Emit single-quoted string
+ #
+ def single( value )
+ "'#{value}'"
+ end
+
+ #
+ # Write a text block with the current indent
+ #
+ def indent_text( text, mod, first_line = true )
+ return "" if text.to_s.empty?
+ spacing = indent( mod )
+ text = text.gsub( /\A([^\n])/, "#{ spacing }\\1" ) if first_line
+ return text.gsub( /\n^([^\n])/, "\n#{spacing}\\1" )
+ end
+
+ #
+ # Write a current indent
+ #
+ def indent( mod = nil )
+ #p [ self.id, level, mod, :INDENT ]
+ if level <= 0
+ mod ||= 0
+ else
+ mod ||= options(:Indent)
+ mod += ( level - 1 ) * options(:Indent)
+ end
+ return " " * mod
+ end
+
+ #
+ # Add indent to the buffer
+ #
+ def indent!
+ self << indent
+ end
+
+ #
+ # Folding paragraphs within a column
+ #
+ def fold( value )
+ value.gsub( /(^[ \t]+.*$)|(\S.{0,#{options(:BestWidth) - 1}})(?:[ \t]+|(\n+(?=[ \t]|\Z))|$)/ ) do |s|
+ $1 || $2 + ( $3 || "\n" )
+ end
+ end
+
+ #
+ # Quick mapping
+ #
+ def map( type, &e )
+ val = Mapping.new
+ e.call( val )
+ self << "#{type} " if type.length.nonzero?
+
+ #
+ # Empty hashes
+ #
+ if val.length.zero?
+ self << "{}"
+ @seq_map = false
+ else
+ # FIXME
+ # if @buffer.length == 1 and options(:UseHeader) == false and type.length.zero?
+ # @headless = 1
+ # end
+
+ defkey = @options.delete( :DefaultKey )
+ if defkey
+ seq_map_shortcut
+ self << "= : "
+ defkey.to_yaml( :Emitter => self )
+ end
+
+ #
+ # Emit the key and value
+ #
+ val.each { |v|
+ seq_map_shortcut
+ if v[0].is_complex_yaml?
+ self << "? "
+ end
+ v[0].to_yaml( :Emitter => self )
+ if v[0].is_complex_yaml?
+ self << "\n"
+ indent!
+ end
+ self << ": "
+ v[1].to_yaml( :Emitter => self )
+ }
+ end
+ end
+
+ def seq_map_shortcut
+ # FIXME: seq_map needs to work with the new anchoring system
+ # if @seq_map
+ # @anchor_extras[@buffer.length - 1] = "\n" + indent
+ # @seq_map = false
+ # else
+ self << "\n"
+ indent!
+ # end
+ end
+
+ #
+ # Quick sequence
+ #
+ def seq( type, &e )
+ @seq_map = false
+ val = Sequence.new
+ e.call( val )
+ self << "#{type} " if type.length.nonzero?
+
+ #
+ # Empty arrays
+ #
+ if val.length.zero?
+ self << "[]"
+ else
+ # FIXME
+ # if @buffer.length == 1 and options(:UseHeader) == false and type.length.zero?
+ # @headless = 1
+ # end
+
+ #
+ # Emit the key and value
+ #
+ val.each { |v|
+ self << "\n"
+ indent!
+ self << "- "
+ @seq_map = true if v.class == Hash
+ v.to_yaml( :Emitter => self )
+ }
+ end
+ end
+
+ end
+
+ #
+ # Emitter helper classes
+ #
+ class Mapping < Array
+ def add( k, v )
+ push [k, v]
+ end
+ end
+
+ class Sequence < Array
+ def add( v )
+ push v
+ end
+ end
+
+end
diff --git a/ruby_1_8_5/lib/yaml/basenode.rb b/ruby_1_8_5/lib/yaml/basenode.rb
new file mode 100644
index 0000000000..d24f6172e9
--- /dev/null
+++ b/ruby_1_8_5/lib/yaml/basenode.rb
@@ -0,0 +1,216 @@
+#
+# YAML::BaseNode class
+#
+require 'yaml/ypath'
+
+module YAML
+
+ #
+ # YAML Generic Model container
+ #
+ module BaseNode
+
+ #
+ # Search for YPath entry and return
+ # qualified nodes.
+ #
+ def select( ypath_str )
+ matches = match_path( ypath_str )
+
+ #
+ # Create a new generic view of the elements selected
+ #
+ if matches
+ result = []
+ matches.each { |m|
+ result.push m.last
+ }
+ YAML.transfer( 'seq', result )
+ end
+ end
+
+ #
+ # Search for YPath entry and return
+ # transformed nodes.
+ #
+ def select!( ypath_str )
+ matches = match_path( ypath_str )
+
+ #
+ # Create a new generic view of the elements selected
+ #
+ if matches
+ result = []
+ matches.each { |m|
+ result.push m.last.transform
+ }
+ result
+ end
+ end
+
+ #
+ # Search for YPath entry and return a list of
+ # qualified paths.
+ #
+ def search( ypath_str )
+ matches = match_path( ypath_str )
+
+ if matches
+ matches.collect { |m|
+ path = []
+ m.each_index { |i|
+ path.push m[i] if ( i % 2 ).zero?
+ }
+ "/" + path.compact.join( "/" )
+ }
+ end
+ end
+
+ def at( seg )
+ if Hash === @value
+ self[seg]
+ elsif Array === @value and seg =~ /\A\d+\Z/ and @value[seg.to_i]
+ @value[seg.to_i]
+ end
+ end
+
+ #
+ # YPath search returning a complete depth array
+ #
+ def match_path( ypath_str )
+ depth = 0
+ matches = []
+ YPath.each_path( ypath_str ) do |ypath|
+ seg = match_segment( ypath, 0 )
+ matches += seg if seg
+ end
+ matches.uniq
+ end
+
+ #
+ # Search a node for a single YPath segment
+ #
+ def match_segment( ypath, depth )
+ deep_nodes = []
+ seg = ypath.segments[ depth ]
+ if seg == "/"
+ unless String === @value
+ idx = -1
+ @value.collect { |v|
+ idx += 1
+ if Hash === @value
+ match_init = [v[0].transform, v[1]]
+ match_deep = v[1].match_segment( ypath, depth )
+ else
+ match_init = [idx, v]
+ match_deep = v.match_segment( ypath, depth )
+ end
+ if match_deep
+ match_deep.each { |m|
+ deep_nodes.push( match_init + m )
+ }
+ end
+ }
+ end
+ depth += 1
+ seg = ypath.segments[ depth ]
+ end
+ match_nodes =
+ case seg
+ when "."
+ [[nil, self]]
+ when ".."
+ [["..", nil]]
+ when "*"
+ if @value.is_a? Enumerable
+ idx = -1
+ @value.collect { |h|
+ idx += 1
+ if Hash === @value
+ [h[0].transform, h[1]]
+ else
+ [idx, h]
+ end
+ }
+ end
+ else
+ if seg =~ /^"(.*)"$/
+ seg = $1
+ elsif seg =~ /^'(.*)'$/
+ seg = $1
+ end
+ if ( v = at( seg ) )
+ [[ seg, v ]]
+ end
+ end
+ return deep_nodes unless match_nodes
+ pred = ypath.predicates[ depth ]
+ if pred
+ case pred
+ when /^\.=/
+ pred = $' # '
+ match_nodes.reject! { |n|
+ n.last.value != pred
+ }
+ else
+ match_nodes.reject! { |n|
+ n.last.at( pred ).nil?
+ }
+ end
+ end
+ return match_nodes + deep_nodes unless ypath.segments.length > depth + 1
+
+ #puts "DEPTH: #{depth + 1}"
+ deep_nodes = []
+ match_nodes.each { |n|
+ if n[1].is_a? BaseNode
+ match_deep = n[1].match_segment( ypath, depth + 1 )
+ if match_deep
+ match_deep.each { |m|
+ deep_nodes.push( n + m )
+ }
+ end
+ else
+ deep_nodes = []
+ end
+ }
+ deep_nodes = nil if deep_nodes.length == 0
+ deep_nodes
+ end
+
+ #
+ # We want the node to act like as Hash
+ # if it is.
+ #
+ def []( *key )
+ if Hash === @value
+ v = @value.detect { |k,v| k.transform == key.first }
+ v[1] if v
+ elsif Array === @value
+ @value.[]( *key )
+ end
+ end
+
+ def children
+ if Hash === @value
+ @value.values.collect { |c| c[1] }
+ elsif Array === @value
+ @value
+ end
+ end
+
+ def children_with_index
+ if Hash === @value
+ @value.keys.collect { |i| [self[i], i] }
+ elsif Array === @value
+ i = -1; @value.collect { |v| i += 1; [v, i] }
+ end
+ end
+
+ def emit
+ transform.to_yaml
+ end
+ end
+
+end
+
diff --git a/ruby_1_8_5/lib/yaml/constants.rb b/ruby_1_8_5/lib/yaml/constants.rb
new file mode 100644
index 0000000000..fb833d3077
--- /dev/null
+++ b/ruby_1_8_5/lib/yaml/constants.rb
@@ -0,0 +1,45 @@
+#
+# Constants used throughout the library
+#
+module YAML
+
+ #
+ # Constants
+ #
+ VERSION = '0.60'
+ SUPPORTED_YAML_VERSIONS = ['1.0']
+
+ #
+ # Parser tokens
+ #
+ WORD_CHAR = 'A-Za-z0-9'
+ PRINTABLE_CHAR = '-_A-Za-z0-9!?/()$\'". '
+ NOT_PLAIN_CHAR = '\x7f\x0-\x1f\x80-\x9f'
+ ESCAPE_CHAR = '[\\x00-\\x09\\x0b-\\x1f]'
+ INDICATOR_CHAR = '*&!|\\\\^@%{}[]='
+ SPACE_INDICATORS = '-#:,?'
+ RESTRICTED_INDICATORS = '#:,}]'
+ DNS_COMP_RE = "\\w(?:[-\\w]*\\w)?"
+ DNS_NAME_RE = "(?:(?:#{DNS_COMP_RE}\\.)+#{DNS_COMP_RE}|#{DNS_COMP_RE})"
+ ESCAPES = %w{\x00 \x01 \x02 \x03 \x04 \x05 \x06 \a
+ \x08 \t \n \v \f \r \x0e \x0f
+ \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17
+ \x18 \x19 \x1a \e \x1c \x1d \x1e \x1f
+ }
+ UNESCAPES = {
+ 'a' => "\x07", 'b' => "\x08", 't' => "\x09",
+ 'n' => "\x0a", 'v' => "\x0b", 'f' => "\x0c",
+ 'r' => "\x0d", 'e' => "\x1b", '\\' => '\\',
+ }
+
+ #
+ # Default settings
+ #
+ DEFAULTS = {
+ :Indent => 2, :UseHeader => false, :UseVersion => false, :Version => '1.0',
+ :SortKeys => false, :AnchorFormat => 'id%03d', :ExplicitTypes => false,
+ :WidthType => 'absolute', :BestWidth => 80,
+ :UseBlock => false, :UseFold => false, :Encoding => :None
+ }
+
+end
diff --git a/ruby_1_8_5/lib/yaml/dbm.rb b/ruby_1_8_5/lib/yaml/dbm.rb
new file mode 100644
index 0000000000..87d6009250
--- /dev/null
+++ b/ruby_1_8_5/lib/yaml/dbm.rb
@@ -0,0 +1,111 @@
+require 'yaml'
+require 'dbm'
+#
+# YAML + DBM = YDBM
+# - Same interface as DBM class
+#
+module YAML
+
+class DBM < ::DBM
+ VERSION = "0.1"
+ def []( key )
+ fetch( key )
+ end
+ def []=( key, val )
+ store( key, val )
+ end
+ def fetch( keystr, ifnone = nil )
+ begin
+ val = super( keystr )
+ return YAML::load( val ) if String === val
+ rescue IndexError
+ end
+ if block_given?
+ yield keystr
+ else
+ ifnone
+ end
+ end
+ def index( keystr )
+ super( keystr.to_yaml )
+ end
+ def values_at( *keys )
+ keys.collect { |k| fetch( k ) }
+ end
+ def delete( key )
+ v = super( key )
+ if String === v
+ v = YAML::load( v )
+ end
+ v
+ end
+ def delete_if
+ del_keys = keys.dup
+ del_keys.delete_if { |k| yield( k, fetch( k ) ) == false }
+ del_keys.each { |k| delete( k ) }
+ self
+ end
+ def reject
+ hsh = self.to_hash
+ hsh.reject { |k,v| yield k, v }
+ end
+ def each_pair
+ keys.each { |k| yield k, fetch( k ) }
+ self
+ end
+ def each_value
+ super { |v| yield YAML::load( v ) }
+ self
+ end
+ def values
+ super.collect { |v| YAML::load( v ) }
+ end
+ def has_value?( val )
+ each_value { |v| return true if v == val }
+ return false
+ end
+ def invert
+ h = {}
+ keys.each { |k| h[ self.fetch( k ) ] = k }
+ h
+ end
+ def replace( hsh )
+ clear
+ update( hsh )
+ end
+ def shift
+ a = super
+ a[1] = YAML::load( a[1] ) if a
+ a
+ end
+ def select( *keys )
+ if block_given?
+ self.keys.collect { |k| v = self[k]; [k, v] if yield k, v }.compact
+ else
+ values_at( *keys )
+ end
+ end
+ def store( key, val )
+ super( key, val.to_yaml )
+ val
+ end
+ def update( hsh )
+ hsh.keys.each do |k|
+ self.store( k, hsh.fetch( k ) )
+ end
+ self
+ end
+ def to_a
+ a = []
+ keys.each { |k| a.push [ k, self.fetch( k ) ] }
+ a
+ end
+ def to_hash
+ h = {}
+ keys.each { |k| h[ k ] = self.fetch( k ) }
+ h
+ end
+ alias :each :each_pair
+end
+
+end
diff --git a/ruby_1_8_5/lib/yaml/encoding.rb b/ruby_1_8_5/lib/yaml/encoding.rb
new file mode 100644
index 0000000000..37f5cfda64
--- /dev/null
+++ b/ruby_1_8_5/lib/yaml/encoding.rb
@@ -0,0 +1,33 @@
+#
+# Handle Unicode-to-Internal conversion
+#
+
+module YAML
+
+ #
+ # Escape the string, condensing common escapes
+ #
+ def YAML.escape( value, skip = "" )
+ value.gsub( /\\/, "\\\\\\" ).
+ gsub( /"/, "\\\"" ).
+ gsub( /([\x00-\x1f])/ ) do |x|
+ skip[x] || ESCAPES[ x.unpack("C")[0] ]
+ end
+ end
+
+ #
+ # Unescape the condenses escapes
+ #
+ def YAML.unescape( value )
+ value.gsub( /\\(?:([nevfbart\\])|0?x([0-9a-fA-F]{2})|u([0-9a-fA-F]{4}))/ ) { |x|
+ if $3
+ ["#$3".hex ].pack('U*')
+ elsif $2
+ [$2].pack( "H2" )
+ else
+ UNESCAPES[$1]
+ end
+ }
+ end
+
+end
diff --git a/ruby_1_8_5/lib/yaml/error.rb b/ruby_1_8_5/lib/yaml/error.rb
new file mode 100644
index 0000000000..15865a9aa9
--- /dev/null
+++ b/ruby_1_8_5/lib/yaml/error.rb
@@ -0,0 +1,34 @@
+#
+# Error messages and exception class
+#
+
+module YAML
+
+ #
+ # Error messages
+ #
+
+ ERROR_NO_HEADER_NODE = "With UseHeader=false, the node Array or Hash must have elements"
+ ERROR_NEED_HEADER = "With UseHeader=false, the node must be an Array or Hash"
+ ERROR_BAD_EXPLICIT = "Unsupported explicit transfer: '%s'"
+ ERROR_MANY_EXPLICIT = "More than one explicit transfer"
+ ERROR_MANY_IMPLICIT = "More than one implicit request"
+ ERROR_NO_ANCHOR = "No anchor for alias '%s'"
+ ERROR_BAD_ANCHOR = "Invalid anchor: %s"
+ ERROR_MANY_ANCHOR = "More than one anchor"
+ ERROR_ANCHOR_ALIAS = "Can't define both an anchor and an alias"
+ ERROR_BAD_ALIAS = "Invalid alias: %s"
+ ERROR_MANY_ALIAS = "More than one alias"
+ ERROR_ZERO_INDENT = "Can't use zero as an indentation width"
+ ERROR_UNSUPPORTED_VERSION = "This release of YAML.rb does not support YAML version %s"
+ ERROR_UNSUPPORTED_ENCODING = "Attempt to use unsupported encoding: %s"
+
+ #
+ # YAML Error classes
+ #
+
+ class Error < StandardError; end
+ class ParseError < Error; end
+ class TypeError < StandardError; end
+
+end
diff --git a/ruby_1_8_5/lib/yaml/loader.rb b/ruby_1_8_5/lib/yaml/loader.rb
new file mode 100644
index 0000000000..eb0709e103
--- /dev/null
+++ b/ruby_1_8_5/lib/yaml/loader.rb
@@ -0,0 +1,14 @@
+#
+# YAML::Loader class
+# .. type handling ..
+#
+module YAML
+ class Loader
+ TRANSFER_DOMAINS = {
+ 'yaml.org,2002' => {},
+ 'ruby.yaml.org,2002' => {}
+ }
+ PRIVATE_TYPES = {}
+ IMPLICIT_TYPES = [ 'null', 'bool', 'time', 'int', 'float' ]
+ end
+end
diff --git a/ruby_1_8_5/lib/yaml/rubytypes.rb b/ruby_1_8_5/lib/yaml/rubytypes.rb
new file mode 100644
index 0000000000..eebf027135
--- /dev/null
+++ b/ruby_1_8_5/lib/yaml/rubytypes.rb
@@ -0,0 +1,408 @@
+# -*- mode: ruby; ruby-indent-level: 4; tab-width: 4 -*- vim: sw=4 ts=4
+require 'date'
+
+class Class
+ def to_yaml( opts = {} )
+ raise TypeError, "can't dump anonymous class %s" % self.class
+ end
+end
+
+class Object
+ yaml_as "tag:ruby.yaml.org,2002:object"
+ def to_yaml_style; end
+ def to_yaml_properties; instance_variables.sort; end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( object_id, opts ) do |out|
+ out.map( taguri, to_yaml_style ) do |map|
+ to_yaml_properties.each do |m|
+ map.add( m[1..-1], instance_variable_get( m ) )
+ end
+ end
+ end
+ end
+end
+
+class Hash
+ yaml_as "tag:ruby.yaml.org,2002:hash"
+ yaml_as "tag:yaml.org,2002:map"
+ def yaml_initialize( tag, val )
+ if Array === val
+ update Hash.[]( *val ) # Convert the map to a sequence
+ elsif Hash === val
+ update val
+ else
+ raise YAML::TypeError, "Invalid map explicitly tagged #{ tag }: " + val.inspect
+ end
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( object_id, opts ) do |out|
+ out.map( taguri, to_yaml_style ) do |map|
+ each do |k, v|
+ map.add( k, v )
+ end
+ end
+ end
+ end
+end
+
+class Struct
+ yaml_as "tag:ruby.yaml.org,2002:struct"
+ def self.yaml_tag_class_name; self.name.gsub( "Struct::", "" ); end
+ def self.yaml_tag_read_class( name ); "Struct::#{ name }"; end
+ def self.yaml_new( klass, tag, val )
+ if Hash === val
+ struct_type = nil
+
+ #
+ # Use existing Struct if it exists
+ #
+ props = {}
+ val.delete_if { |k,v| props[k] = v if k =~ /^@/ }
+ begin
+ struct_name, struct_type = YAML.read_type_class( tag, Struct )
+ rescue NameError
+ end
+ if not struct_type
+ struct_def = [ tag.split( ':', 4 ).last ]
+ struct_type = Struct.new( *struct_def.concat( val.keys.collect { |k| k.intern } ) )
+ end
+
+ #
+ # Set the Struct properties
+ #
+ st = YAML::object_maker( struct_type, {} )
+ st.members.each do |m|
+ st.send( "#{m}=", val[m] )
+ end
+ props.each do |k,v|
+ st.instance_variable_set(k, v)
+ end
+ st
+ else
+ raise YAML::TypeError, "Invalid Ruby Struct: " + val.inspect
+ end
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( object_id, opts ) do |out|
+ #
+ # Basic struct is passed as a YAML map
+ #
+ out.map( taguri, to_yaml_style ) do |map|
+ self.members.each do |m|
+ map.add( m, self[m] )
+ end
+ self.to_yaml_properties.each do |m|
+ map.add( m, instance_variable_get( m ) )
+ end
+ end
+ end
+ end
+end
+
+class Array
+ yaml_as "tag:ruby.yaml.org,2002:array"
+ yaml_as "tag:yaml.org,2002:seq"
+ def yaml_initialize( tag, val ); concat( val.to_a ); end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( object_id, opts ) do |out|
+ out.seq( taguri, to_yaml_style ) do |seq|
+ each do |x|
+ seq.add( x )
+ end
+ end
+ end
+ end
+end
+
+class Exception
+ yaml_as "tag:ruby.yaml.org,2002:exception"
+ def Exception.yaml_new( klass, tag, val )
+ o = YAML.object_maker( klass, { 'mesg' => val.delete( 'message' ) } )
+ val.each_pair do |k,v|
+ o.instance_variable_set("@#{k}", v)
+ end
+ o
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( object_id, opts ) do |out|
+ out.map( taguri, to_yaml_style ) do |map|
+ map.add( 'message', message )
+ to_yaml_properties.each do |m|
+ map.add( m[1..-1], instance_variable_get( m ) )
+ end
+ end
+ end
+ end
+end
+
+class String
+ yaml_as "tag:ruby.yaml.org,2002:string"
+ yaml_as "tag:yaml.org,2002:binary"
+ yaml_as "tag:yaml.org,2002:str"
+ def is_complex_yaml?
+ to_yaml_style or not to_yaml_properties.empty? or self =~ /\n.+/
+ end
+ def is_binary_data?
+ ( self.count( "^ -~", "^\r\n" ) / self.size > 0.3 || self.count( "\x00" ) > 0 ) unless empty?
+ end
+ def String.yaml_new( klass, tag, val )
+ val = val.unpack("m")[0] if tag == "tag:yaml.org,2002:binary"
+ val = { 'str' => val } if String === val
+ if Hash === val
+ s = klass.allocate
+ # Thank you, NaHi
+ String.instance_method(:initialize).
+ bind(s).
+ call( val.delete( 'str' ) )
+ val.each { |k,v| s.instance_variable_set( k, v ) }
+ s
+ else
+ raise YAML::TypeError, "Invalid String: " + val.inspect
+ end
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( is_complex_yaml? ? object_id : nil, opts ) do |out|
+ if is_binary_data?
+ out.scalar( "tag:yaml.org,2002:binary", [self].pack("m"), :literal )
+ elsif to_yaml_properties.empty?
+ out.scalar( taguri, self, self =~ /^:/ ? :quote2 : to_yaml_style )
+ else
+ out.map( taguri, to_yaml_style ) do |map|
+ map.add( 'str', "#{self}" )
+ to_yaml_properties.each do |m|
+ map.add( m, instance_variable_get( m ) )
+ end
+ end
+ end
+ end
+ end
+end
+
+class Symbol
+ yaml_as "tag:ruby.yaml.org,2002:symbol"
+ yaml_as "tag:ruby.yaml.org,2002:sym"
+ def Symbol.yaml_new( klass, tag, val )
+ if String === val
+ val = YAML::load( val ) if val =~ /\A(["']).*\1\z/
+ val.intern
+ else
+ raise YAML::TypeError, "Invalid Symbol: " + val.inspect
+ end
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( nil, opts ) do |out|
+ out.scalar( "tag:yaml.org,2002:str", self.inspect, :plain )
+ end
+ end
+end
+
+class Range
+ yaml_as "tag:ruby.yaml.org,2002:range"
+ def Range.yaml_new( klass, tag, val )
+ inr = %r'(\w+|[+-]?\d+(?:\.\d+)?(?:e[+-]\d+)?|"(?:[^\\"]|\\.)*")'
+ opts = {}
+ if String === val and val =~ /^#{inr}(\.{2,3})#{inr}$/o
+ r1, rdots, r2 = $1, $2, $3
+ opts = {
+ 'begin' => YAML.load( "--- #{r1}" ),
+ 'end' => YAML.load( "--- #{r2}" ),
+ 'excl' => rdots.length == 3
+ }
+ val = {}
+ elsif Hash === val
+ opts['begin'] = val.delete('begin')
+ opts['end'] = val.delete('end')
+ opts['excl'] = val.delete('excl')
+ end
+ if Hash === opts
+ r = YAML::object_maker( klass, {} )
+ # Thank you, NaHi
+ Range.instance_method(:initialize).
+ bind(r).
+ call( opts['begin'], opts['end'], opts['excl'] )
+ val.each { |k,v| r.instance_variable_set( k, v ) }
+ r
+ else
+ raise YAML::TypeError, "Invalid Range: " + val.inspect
+ end
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( object_id, opts ) do |out|
+ # if self.begin.is_complex_yaml? or self.begin.respond_to? :to_str or
+ # self.end.is_complex_yaml? or self.end.respond_to? :to_str or
+ # not to_yaml_properties.empty?
+ out.map( taguri, to_yaml_style ) do |map|
+ map.add( 'begin', self.begin )
+ map.add( 'end', self.end )
+ map.add( 'excl', self.exclude_end? )
+ to_yaml_properties.each do |m|
+ map.add( m, instance_variable_get( m ) )
+ end
+ end
+ # else
+ # out.scalar( taguri ) do |sc|
+ # sc.embed( self.begin )
+ # sc.concat( self.exclude_end? ? "..." : ".." )
+ # sc.embed( self.end )
+ # end
+ # end
+ end
+ end
+end
+
+class Regexp
+ yaml_as "tag:ruby.yaml.org,2002:regexp"
+ def Regexp.yaml_new( klass, tag, val )
+ if String === val and val =~ /^\/(.*)\/([mix]*)$/
+ val = { 'regexp' => $1, 'mods' => $2 }
+ end
+ if Hash === val
+ mods = nil
+ unless val['mods'].to_s.empty?
+ mods = 0x00
+ mods |= Regexp::EXTENDED if val['mods'].include?( 'x' )
+ mods |= Regexp::IGNORECASE if val['mods'].include?( 'i' )
+ mods |= Regexp::MULTILINE if val['mods'].include?( 'm' )
+ end
+ val.delete( 'mods' )
+ r = YAML::object_maker( klass, {} )
+ Regexp.instance_method(:initialize).
+ bind(r).
+ call( val.delete( 'regexp' ), mods )
+ val.each { |k,v| r.instance_variable_set( k, v ) }
+ r
+ else
+ raise YAML::TypeError, "Invalid Regular expression: " + val.inspect
+ end
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( nil, opts ) do |out|
+ if to_yaml_properties.empty?
+ out.scalar( taguri, self.inspect, :plain )
+ else
+ out.map( taguri, to_yaml_style ) do |map|
+ src = self.inspect
+ if src =~ /\A\/(.*)\/([a-z]*)\Z/
+ map.add( 'regexp', $1 )
+ map.add( 'mods', $2 )
+ else
+ raise YAML::TypeError, "Invalid Regular expression: " + src
+ end
+ to_yaml_properties.each do |m|
+ map.add( m, instance_variable_get( m ) )
+ end
+ end
+ end
+ end
+ end
+end
+
+class Time
+ yaml_as "tag:ruby.yaml.org,2002:time"
+ yaml_as "tag:yaml.org,2002:timestamp"
+ def Time.yaml_new( klass, tag, val )
+ if Hash === val
+ t = val.delete( 'at' )
+ val.each { |k,v| t.instance_variable_set( k, v ) }
+ t
+ else
+ raise YAML::TypeError, "Invalid Time: " + val.inspect
+ end
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( object_id, opts ) do |out|
+ tz = "Z"
+ # from the tidy Tobias Peters <t-peters@gmx.de> Thanks!
+ unless self.utc?
+ utc_same_instant = self.dup.utc
+ utc_same_writing = Time.utc(year,month,day,hour,min,sec,usec)
+ difference_to_utc = utc_same_writing - utc_same_instant
+ if (difference_to_utc < 0)
+ difference_sign = '-'
+ absolute_difference = -difference_to_utc
+ else
+ difference_sign = '+'
+ absolute_difference = difference_to_utc
+ end
+ difference_minutes = (absolute_difference/60).round
+ tz = "%s%02d:%02d" % [ difference_sign, difference_minutes / 60, difference_minutes % 60]
+ end
+ standard = self.strftime( "%Y-%m-%d %H:%M:%S" )
+ standard += ".%06d" % [usec] if usec.nonzero?
+ standard += " %s" % [tz]
+ if to_yaml_properties.empty?
+ out.scalar( taguri, standard, :plain )
+ else
+ out.map( taguri, to_yaml_style ) do |map|
+ map.add( 'at', standard )
+ to_yaml_properties.each do |m|
+ map.add( m, instance_variable_get( m ) )
+ end
+ end
+ end
+ end
+ end
+end
+
+class Date
+ yaml_as "tag:yaml.org,2002:timestamp#ymd"
+ def to_yaml( opts = {} )
+ YAML::quick_emit( object_id, opts ) do |out|
+ out.scalar( "tag:yaml.org,2002:timestamp", self.to_s, :plain )
+ end
+ end
+end
+
+class Integer
+ yaml_as "tag:yaml.org,2002:int"
+ def to_yaml( opts = {} )
+ YAML::quick_emit( nil, opts ) do |out|
+ out.scalar( "tag:yaml.org,2002:int", self.to_s, :plain )
+ end
+ end
+end
+
+class Float
+ yaml_as "tag:yaml.org,2002:float"
+ def to_yaml( opts = {} )
+ YAML::quick_emit( nil, opts ) do |out|
+ str = self.to_s
+ if str == "Infinity"
+ str = ".Inf"
+ elsif str == "-Infinity"
+ str = "-.Inf"
+ elsif str == "NaN"
+ str = ".NaN"
+ end
+ out.scalar( "tag:yaml.org,2002:float", str, :plain )
+ end
+ end
+end
+
+class TrueClass
+ yaml_as "tag:yaml.org,2002:bool#yes"
+ def to_yaml( opts = {} )
+ YAML::quick_emit( nil, opts ) do |out|
+ out.scalar( taguri, "true", :plain )
+ end
+ end
+end
+
+class FalseClass
+ yaml_as "tag:yaml.org,2002:bool#no"
+ def to_yaml( opts = {} )
+ YAML::quick_emit( nil, opts ) do |out|
+ out.scalar( taguri, "false", :plain )
+ end
+ end
+end
+
+class NilClass
+ yaml_as "tag:yaml.org,2002:null"
+ def to_yaml( opts = {} )
+ YAML::quick_emit( nil, opts ) do |out|
+ out.scalar( taguri, "", :plain )
+ end
+ end
+end
+
diff --git a/ruby_1_8_5/lib/yaml/store.rb b/ruby_1_8_5/lib/yaml/store.rb
new file mode 100644
index 0000000000..2ffa9554b9
--- /dev/null
+++ b/ruby_1_8_5/lib/yaml/store.rb
@@ -0,0 +1,29 @@
+#
+# YAML::Store
+#
+require 'yaml'
+require 'pstore'
+
+class YAML::Store < PStore
+ def initialize( *o )
+ @opt = YAML::DEFAULTS.dup
+ if String === o.first
+ super(o.shift)
+ end
+ if o.last.is_a? Hash
+ @opt.update(o.pop)
+ end
+ end
+
+ def dump(table)
+ @table.to_yaml(@opt)
+ end
+
+ def load(content)
+ YAML::load(content)
+ end
+
+ def load_file(file)
+ YAML::load(file)
+ end
+end
diff --git a/ruby_1_8_5/lib/yaml/stream.rb b/ruby_1_8_5/lib/yaml/stream.rb
new file mode 100644
index 0000000000..060fbc4200
--- /dev/null
+++ b/ruby_1_8_5/lib/yaml/stream.rb
@@ -0,0 +1,40 @@
+module YAML
+
+ #
+ # YAML::Stream -- for emitting many documents
+ #
+ class Stream
+
+ attr_accessor :documents, :options
+
+ def initialize( opts = {} )
+ @options = opts
+ @documents = []
+ end
+
+ def []( i )
+ @documents[ i ]
+ end
+
+ def add( doc )
+ @documents << doc
+ end
+
+ def edit( doc_num, doc )
+ @documents[ doc_num ] = doc
+ end
+
+ def emit( io = nil )
+ # opts = @options.dup
+ # opts[:UseHeader] = true if @documents.length > 1
+ out = YAML.emitter
+ out.reset( io || io2 = StringIO.new )
+ @documents.each { |v|
+ v.to_yaml( out )
+ }
+ io || ( io2.rewind; io2.read )
+ end
+
+ end
+
+end
diff --git a/ruby_1_8_5/lib/yaml/stringio.rb b/ruby_1_8_5/lib/yaml/stringio.rb
new file mode 100644
index 0000000000..8ad949fa2b
--- /dev/null
+++ b/ruby_1_8_5/lib/yaml/stringio.rb
@@ -0,0 +1,83 @@
+#
+# Limited StringIO if no core lib is available
+#
+begin
+require 'stringio'
+rescue LoadError
+ # StringIO based on code by MoonWolf
+ class StringIO
+ def initialize(string="")
+ @string=string
+ @pos=0
+ @eof=(string.size==0)
+ end
+ def pos
+ @pos
+ end
+ def eof
+ @eof
+ end
+ alias eof? eof
+ def readline(rs=$/)
+ if @eof
+ raise EOFError
+ else
+ if p = @string[@pos..-1]=~rs
+ line = @string[@pos,p+1]
+ else
+ line = @string[@pos..-1]
+ end
+ @pos+=line.size
+ @eof =true if @pos==@string.size
+ $_ = line
+ end
+ end
+ def rewind
+ seek(0,0)
+ end
+ def seek(offset,whence)
+ case whence
+ when 0
+ @pos=offset
+ when 1
+ @pos+=offset
+ when 2
+ @pos=@string.size+offset
+ end
+ @eof=(@pos>=@string.size)
+ 0
+ end
+ end
+
+ #
+ # Class method for creating streams
+ #
+ def YAML.make_stream( io )
+ if String === io
+ io = StringIO.new( io )
+ elsif not IO === io
+ raise YAML::Error, "YAML stream must be an IO or String object."
+ end
+ if YAML::unicode
+ def io.readline
+ YAML.utf_to_internal( readline( @ln_sep ), @utf_encoding )
+ end
+ def io.check_unicode
+ @utf_encoding = YAML.sniff_encoding( read( 4 ) )
+ @ln_sep = YAML.enc_separator( @utf_encoding )
+ seek( -4, IO::SEEK_CUR )
+ end
+ def io.utf_encoding
+ @utf_encoding
+ end
+ io.check_unicode
+ else
+ def io.utf_encoding
+ :None
+ end
+ end
+ io
+ end
+
+end
+
diff --git a/ruby_1_8_5/lib/yaml/syck.rb b/ruby_1_8_5/lib/yaml/syck.rb
new file mode 100644
index 0000000000..faf57e8036
--- /dev/null
+++ b/ruby_1_8_5/lib/yaml/syck.rb
@@ -0,0 +1,19 @@
+#
+# YAML::Syck module
+# .. glues syck and yaml.rb together ..
+#
+require 'syck'
+require 'yaml/basenode'
+
+module YAML
+ module Syck
+
+ #
+ # Mixin BaseNode functionality
+ #
+ class Node
+ include YAML::BaseNode
+ end
+
+ end
+end
diff --git a/ruby_1_8_5/lib/yaml/tag.rb b/ruby_1_8_5/lib/yaml/tag.rb
new file mode 100644
index 0000000000..098aca5cfd
--- /dev/null
+++ b/ruby_1_8_5/lib/yaml/tag.rb
@@ -0,0 +1,91 @@
+# -*- mode: ruby; ruby-indent-level: 4; tab-width: 4 -*- vim: sw=4 ts=4
+# $Id: tag.rb,v 1.1.2.3 2006/08/22 09:43:47 matz Exp $
+#
+# = yaml/tag.rb: methods for associating a taguri to a class.
+#
+# Author:: why the lucky stiff
+#
+module YAML
+ # A dictionary of taguris which map to
+ # Ruby classes.
+ @@tagged_classes = {}
+
+ #
+ # Associates a taguri _tag_ with a Ruby class _cls_. The taguri is used to give types
+ # to classes when loading YAML. Taguris are of the form:
+ #
+ # tag:authorityName,date:specific
+ #
+ # The +authorityName+ is a domain name or email address. The +date+ is the date the type
+ # was issued in YYYY or YYYY-MM or YYYY-MM-DD format. The +specific+ is a name for
+ # the type being added.
+ #
+ # For example, built-in YAML types have 'yaml.org' as the +authorityName+ and '2002' as the
+ # +date+. The +specific+ is simply the name of the type:
+ #
+ # tag:yaml.org,2002:int
+ # tag:yaml.org,2002:float
+ # tag:yaml.org,2002:timestamp
+ #
+ # The domain must be owned by you on the +date+ declared. If you don't own any domains on the
+ # date you declare the type, you can simply use an e-mail address.
+ #
+ # tag:why@ruby-lang.org,2004:notes/personal
+ #
+ def YAML.tag_class( tag, cls )
+ if @@tagged_classes.has_key? tag
+ warn "class #{ @@tagged_classes[tag] } held ownership of the #{ tag } tag"
+ end
+ @@tagged_classes[tag] = cls
+ end
+
+ # Returns the complete dictionary of taguris, paired with classes. The key for
+ # the dictionary is the full taguri. The value for each key is the class constant
+ # associated to that taguri.
+ #
+ # YAML.tagged_classes["tag:yaml.org,2002:int"] => Integer
+ #
+ def YAML.tagged_classes
+ @@tagged_classes
+ end
+end
+
+class Module
+ # :stopdoc:
+
+ # Adds a taguri _tag_ to a class, used when dumping or loading the class
+ # in YAML. See YAML::tag_class for detailed information on typing and
+ # taguris.
+ def yaml_as( tag, sc = true )
+ verbose, $VERBOSE = $VERBOSE, nil
+ class_eval <<-"end;", __FILE__, __LINE__+1
+ attr_writer :taguri
+ def taguri
+ if respond_to? :to_yaml_type
+ YAML::tagurize( to_yaml_type[1..-1] )
+ else
+ return @taguri if defined?(@taguri) and @taguri
+ tag = #{ tag.dump }
+ if self.class.yaml_tag_subclasses? and self.class != YAML::tagged_classes[tag]
+ tag = "\#{ tag }:\#{ self.class.yaml_tag_class_name }"
+ end
+ tag
+ end
+ end
+ def self.yaml_tag_subclasses?; #{ sc ? 'true' : 'false' }; end
+ end;
+ YAML::tag_class tag, self
+ ensure
+ $VERBOSE = verbose
+ end
+ # Transforms the subclass name into a name suitable for display
+ # in a subclassed tag.
+ def yaml_tag_class_name
+ self.name
+ end
+ # Transforms the subclass name found in the tag into a Ruby
+ # constant name.
+ def yaml_tag_read_class( name )
+ name
+ end
+end
diff --git a/ruby_1_8_5/lib/yaml/types.rb b/ruby_1_8_5/lib/yaml/types.rb
new file mode 100644
index 0000000000..05113f216d
--- /dev/null
+++ b/ruby_1_8_5/lib/yaml/types.rb
@@ -0,0 +1,194 @@
+# -*- mode: ruby; ruby-indent-level: 4 -*- vim: sw=4
+#
+# Classes required by the full core typeset
+#
+
+module YAML
+
+ #
+ # Default private type
+ #
+ class PrivateType
+ def self.tag_subclasses?; false; end
+ attr_accessor :type_id, :value
+ verbose, $VERBOSE = $VERBOSE, nil
+ def initialize( type, val )
+ @type_id = type; @value = val
+ @value.taguri = "x-private:#{ @type_id }"
+ end
+ def to_yaml( opts = {} )
+ @value.to_yaml( opts )
+ end
+ ensure
+ $VERBOSE = verbose
+ end
+
+ #
+ # Default domain type
+ #
+ class DomainType
+ def self.tag_subclasses?; false; end
+ attr_accessor :domain, :type_id, :value
+ verbose, $VERBOSE = $VERBOSE, nil
+ def initialize( domain, type, val )
+ @domain = domain; @type_id = type; @value = val
+ @value.taguri = "tag:#{ @domain }:#{ @type_id }"
+ end
+ def to_yaml( opts = {} )
+ @value.to_yaml( opts )
+ end
+ ensure
+ $VERBOSE = verbose
+ end
+
+ #
+ # Unresolved objects
+ #
+ class Object
+ def self.tag_subclasses?; false; end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( object_id, opts ) do |out|
+ out.map( "tag:ruby.yaml.org,2002:object:#{ @class }", to_yaml_style ) do |map|
+ @ivars.each do |k,v|
+ map.add( k, v )
+ end
+ end
+ end
+ end
+ end
+
+ #
+ # YAML Hash class to support comments and defaults
+ #
+ class SpecialHash < ::Hash
+ attr_accessor :default
+ def inspect
+ self.default.to_s
+ end
+ def to_s
+ self.default.to_s
+ end
+ def update( h )
+ if YAML::SpecialHash === h
+ @default = h.default if h.default
+ end
+ super( h )
+ end
+ def to_yaml( opts = {} )
+ opts[:DefaultKey] = self.default
+ super( opts )
+ end
+ end
+
+ #
+ # Builtin collection: !omap
+ #
+ class Omap < ::Array
+ yaml_as "tag:yaml.org,2002:omap"
+ def yaml_initialize( tag, val )
+ if Array === val
+ val.each do |v|
+ if Hash === v
+ concat( v.to_a ) # Convert the map to a sequence
+ else
+ raise YAML::Error, "Invalid !omap entry: " + val.inspect
+ end
+ end
+ else
+ raise YAML::Error, "Invalid !omap: " + val.inspect
+ end
+ self
+ end
+ def self.[]( *vals )
+ o = Omap.new
+ 0.step( vals.length - 1, 2 ) do |i|
+ o[vals[i]] = vals[i+1]
+ end
+ o
+ end
+ def []( k )
+ self.assoc( k ).to_a[1]
+ end
+ def []=( k, *rest )
+ val, set = rest.reverse
+ if ( tmp = self.assoc( k ) ) and not set
+ tmp[1] = val
+ else
+ self << [ k, val ]
+ end
+ val
+ end
+ def has_key?( k )
+ self.assoc( k ) ? true : false
+ end
+ def is_complex_yaml?
+ true
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( self.object_id, opts ) do |out|
+ out.seq( taguri, to_yaml_style ) do |seq|
+ self.each do |v|
+ seq.add( Hash[ *v ] )
+ end
+ end
+ end
+ end
+ end
+
+ #
+ # Builtin collection: !pairs
+ #
+ class Pairs < ::Array
+ yaml_as "tag:yaml.org,2002:pairs"
+ def yaml_initialize( tag, val )
+ if Array === val
+ val.each do |v|
+ if Hash === v
+ concat( v.to_a ) # Convert the map to a sequence
+ else
+ raise YAML::Error, "Invalid !pairs entry: " + val.inspect
+ end
+ end
+ else
+ raise YAML::Error, "Invalid !pairs: " + val.inspect
+ end
+ self
+ end
+ def self.[]( *vals )
+ p = Pairs.new
+ 0.step( vals.length - 1, 2 ) { |i|
+ p[vals[i]] = vals[i+1]
+ }
+ p
+ end
+ def []( k )
+ self.assoc( k ).to_a
+ end
+ def []=( k, val )
+ self << [ k, val ]
+ val
+ end
+ def has_key?( k )
+ self.assoc( k ) ? true : false
+ end
+ def is_complex_yaml?
+ true
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( self.object_id, opts ) do |out|
+ out.seq( taguri, to_yaml_style ) do |seq|
+ self.each do |v|
+ seq.add( Hash[ *v ] )
+ end
+ end
+ end
+ end
+ end
+
+ #
+ # Builtin collection: !set
+ #
+ class Set < ::Hash
+ yaml_as "tag:yaml.org,2002:set"
+ end
+end
diff --git a/ruby_1_8_5/lib/yaml/yamlnode.rb b/ruby_1_8_5/lib/yaml/yamlnode.rb
new file mode 100644
index 0000000000..e36a18e694
--- /dev/null
+++ b/ruby_1_8_5/lib/yaml/yamlnode.rb
@@ -0,0 +1,54 @@
+#
+# YAML::YamlNode class
+#
+require 'yaml/basenode'
+
+module YAML
+
+ #
+ # YAML Generic Model container
+ #
+ class YamlNode
+ include BaseNode
+ attr_accessor :kind, :type_id, :value, :anchor
+ def initialize( t, v )
+ @type_id = t
+ if Hash === v
+ @kind = 'map'
+ @value = {}
+ v.each { |k,v|
+ @value[ k.transform ] = [ k, v ]
+ }
+ elsif Array === v
+ @kind = 'seq'
+ @value = v
+ elsif String === v
+ @kind = 'scalar'
+ @value = v
+ end
+ end
+
+ #
+ # Transform this node fully into a native type
+ #
+ def transform
+ t = nil
+ if @value.is_a? Hash
+ t = {}
+ @value.each { |k,v|
+ t[ k ] = v[1].transform
+ }
+ elsif @value.is_a? Array
+ t = []
+ @value.each { |v|
+ t.push v.transform
+ }
+ else
+ t = @value
+ end
+ YAML.transfer_method( @type_id, t )
+ end
+
+ end
+
+end
diff --git a/ruby_1_8_5/lib/yaml/ypath.rb b/ruby_1_8_5/lib/yaml/ypath.rb
new file mode 100644
index 0000000000..81348ca043
--- /dev/null
+++ b/ruby_1_8_5/lib/yaml/ypath.rb
@@ -0,0 +1,52 @@
+#
+# YAML::YPath
+#
+
+module YAML
+
+ class YPath
+ attr_accessor :segments, :predicates, :flags
+ def initialize( str )
+ @segments = []
+ @predicates = []
+ @flags = nil
+ while str =~ /^\/?(\/|[^\/\[]+)(?:\[([^\]]+)\])?/
+ @segments.push $1
+ @predicates.push $2
+ str = $'
+ end
+ unless str.to_s.empty?
+ @segments += str.split( "/" )
+ end
+ if @segments.length == 0
+ @segments.push "."
+ end
+ end
+ def YPath.each_path( str )
+ #
+ # Find choices
+ #
+ paths = []
+ str = "(#{ str })"
+ while str.sub!( /\(([^()]+)\)/, "\n#{ paths.length }\n" )
+ paths.push $1.split( '|' )
+ end
+
+ #
+ # Construct all possible paths
+ #
+ all = [ str ]
+ ( paths.length - 1 ).downto( 0 ) do |i|
+ all = all.collect do |a|
+ paths[i].collect do |p|
+ a.gsub( /\n#{ i }\n/, p )
+ end
+ end.flatten.uniq
+ end
+ all.collect do |path|
+ yield YPath.new( path )
+ end
+ end
+ end
+
+end