summaryrefslogtreecommitdiff
path: root/spec/ruby/core/enumerable/slice_after_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core/enumerable/slice_after_spec.rb')
0 files changed, 0 insertions, 0 deletions
mode'>-rw-r--r--MANIFEST193
-rw-r--r--Makefile.in43
-rw-r--r--README.EXT46
-rw-r--r--README.EXT.ja20
-rw-r--r--ToDo13
-rw-r--r--array.c358
-rw-r--r--bcc32/Makefile.sub129
-rw-r--r--bcc32/setup.mak14
-rw-r--r--bignum.c195
-rwxr-xr-xbin/erb8
-rw-r--r--class.c276
-rw-r--r--compar.c52
-rw-r--r--config.guess645
-rw-r--r--config.sub351
-rw-r--r--configure.in211
-rw-r--r--cygwin/GNUmakefile.in24
-rw-r--r--defines.h49
-rw-r--r--dir.c22
-rw-r--r--djgpp/GNUmakefile.in2
-rw-r--r--djgpp/config.status77
-rw-r--r--dln.c35
-rw-r--r--dln.h2
-rw-r--r--doc/NEWS131
-rw-r--r--doc/irb/irb.rd2
-rw-r--r--doc/irb/irb.rd.ja6
-rw-r--r--doc/net/http.rd.ja502
-rw-r--r--doc/net/smtp.rd.ja205
-rw-r--r--enum.c66
-rw-r--r--env.h2
-rw-r--r--error.c199
-rw-r--r--eval.c1834
-rw-r--r--ext/Setup3
-rw-r--r--ext/Setup.atheos7
-rw-r--r--ext/Setup.dj3
-rw-r--r--ext/Setup.emx3
-rw-r--r--ext/Setup.nt3
-rw-r--r--ext/Setup.x683
-rw-r--r--ext/Win32API/MANIFEST2
-rw-r--r--ext/Win32API/Win32API.c243
-rw-r--r--ext/Win32API/extconf.rb3
-rw-r--r--ext/Win32API/lib/win32/registry.rb831
-rw-r--r--ext/Win32API/lib/win32/resolv.rb366
-rw-r--r--ext/aix_mksym.rb33
-rw-r--r--ext/bigdecimal/.cvsignore3
-rw-r--r--ext/bigdecimal/MANIFEST17
-rw-r--r--ext/bigdecimal/README60
-rw-r--r--ext/bigdecimal/bigdecimal.c4313
-rw-r--r--ext/bigdecimal/bigdecimal.def2
-rw-r--r--ext/bigdecimal/bigdecimal.h218
-rw-r--r--ext/bigdecimal/bigdecimal_en.html796
-rw-r--r--ext/bigdecimal/bigdecimal_ja.html801
-rw-r--r--ext/bigdecimal/depend1
-rw-r--r--ext/bigdecimal/extconf.rb2
-rw-r--r--ext/bigdecimal/lib/bigdecimal/jacobian.rb63
-rw-r--r--ext/bigdecimal/lib/bigdecimal/ludcmp.rb75
-rw-r--r--ext/bigdecimal/lib/bigdecimal/newton.rb75
-rw-r--r--ext/bigdecimal/lib/bigdecimal/nlsolve.rb38
-rw-r--r--ext/bigdecimal/lib/bigdecimal/util.rb73
-rw-r--r--ext/bigdecimal/sample/linear.rb46
-rw-r--r--ext/bigdecimal/sample/nlsolve.rb38
-rw-r--r--ext/bigdecimal/sample/pi.rb50
-rw-r--r--ext/configsub.rb32
-rw-r--r--ext/curses/curses.c16
-rw-r--r--ext/curses/extconf.rb2
-rw-r--r--ext/dbm/dbm.c37
-rw-r--r--ext/dbm/extconf.rb7
-rw-r--r--ext/dbm/testdbm.rb13
-rw-r--r--ext/digest/defs.h2
-rw-r--r--ext/digest/digest.c5
-rw-r--r--ext/digest/md5/md5ossl.c2
-rw-r--r--ext/digest/sha2/sha2.c4
-rw-r--r--ext/dl/MANIFEST1
-rw-r--r--ext/dl/dl.c106
-rw-r--r--ext/dl/doc/dl.txt36
-rw-r--r--ext/dl/handle.c35
-rw-r--r--ext/dl/lib/dl/win32.rb27
-rw-r--r--ext/dl/ptr.c172
-rw-r--r--ext/dl/sym.c25
-rw-r--r--ext/etc/etc.c8
-rw-r--r--ext/extmk.rb112
-rw-r--r--ext/gdbm/gdbm.c25
-rw-r--r--ext/gdbm/testgdbm.rb4
-rw-r--r--ext/iconv/MANIFEST1
-rw-r--r--ext/iconv/charset_alias.rb36
-rw-r--r--ext/iconv/extconf.rb35
-rw-r--r--ext/iconv/iconv.c172
-rw-r--r--ext/io/wait/MANIFEST4
-rw-r--r--ext/io/wait/extconf.rb12
-rw-r--r--ext/io/wait/lib/nonblock.rb23
-rw-r--r--ext/io/wait/wait.c106
-rw-r--r--ext/openssl/MANIFEST66
-rw-r--r--ext/openssl/extconf.rb128
-rw-r--r--ext/openssl/lib/net/ftptls.rb43
-rw-r--r--ext/openssl/lib/net/https.rb188
-rw-r--r--ext/openssl/lib/net/protocols.rb57
-rw-r--r--ext/openssl/lib/net/telnets.rb250
-rw-r--r--ext/openssl/lib/openssl.rb24
-rw-r--r--ext/openssl/lib/openssl/bn.rb35
-rw-r--r--ext/openssl/lib/openssl/buffering.rb189
-rw-r--r--ext/openssl/lib/openssl/cipher.rb52
-rw-r--r--ext/openssl/lib/openssl/digest.rb44
-rw-r--r--ext/openssl/lib/openssl/ssl.rb38
-rw-r--r--ext/openssl/lib/openssl/x509.rb132
-rw-r--r--ext/openssl/openssl_missing.c279
-rw-r--r--ext/openssl/openssl_missing.h105
-rw-r--r--ext/openssl/ossl.c568
-rw-r--r--ext/openssl/ossl.h197
-rw-r--r--ext/openssl/ossl_bn.c751
-rw-r--r--ext/openssl/ossl_bn.h22
-rw-r--r--ext/openssl/ossl_cipher.c377
-rw-r--r--ext/openssl/ossl_cipher.h22
-rw-r--r--ext/openssl/ossl_config.c152
-rw-r--r--ext/openssl/ossl_config.h20
-rw-r--r--ext/openssl/ossl_digest.c289
-rw-r--r--ext/openssl/ossl_digest.h22
-rw-r--r--ext/openssl/ossl_hmac.c222
-rw-r--r--ext/openssl/ossl_hmac.h19
-rw-r--r--ext/openssl/ossl_ns_spki.c232
-rw-r--r--ext/openssl/ossl_ns_spki.h21
-rw-r--r--ext/openssl/ossl_ocsp.c765
-rw-r--r--ext/openssl/ossl_ocsp.h24
-rw-r--r--ext/openssl/ossl_pkcs7.c775
-rw-r--r--ext/openssl/ossl_pkcs7.h22
-rw-r--r--ext/openssl/ossl_pkey.c238
-rw-r--r--ext/openssl/ossl_pkey.h111
-rw-r--r--ext/openssl/ossl_pkey_dh.c386
-rw-r--r--ext/openssl/ossl_pkey_dsa.c404
-rw-r--r--ext/openssl/ossl_pkey_rsa.c515
-rw-r--r--ext/openssl/ossl_rand.c142
-rw-r--r--ext/openssl/ossl_rand.h20
-rw-r--r--ext/openssl/ossl_ssl.c689
-rw-r--r--ext/openssl/ossl_ssl.h21
-rw-r--r--ext/openssl/ossl_version.h16
-rw-r--r--ext/openssl/ossl_x509.c95
-rw-r--r--ext/openssl/ossl_x509.h113
-rw-r--r--ext/openssl/ossl_x509attr.c152
-rw-r--r--ext/openssl/ossl_x509cert.c691
-rw-r--r--ext/openssl/ossl_x509crl.c551
-rw-r--r--ext/openssl/ossl_x509ext.c345
-rw-r--r--ext/openssl/ossl_x509name.c234
-rw-r--r--ext/openssl/ossl_x509req.c449
-rw-r--r--ext/openssl/ossl_x509revoked.c230
-rw-r--r--ext/openssl/ossl_x509store.c561
-rw-r--r--ext/openssl/ruby_missing.h70
-rw-r--r--ext/openssl/sample/c_rehash.rb174
-rw-r--r--ext/openssl/sample/cert2text.rb23
-rw-r--r--ext/openssl/sample/cert_store_view.rb911
-rw-r--r--ext/openssl/sample/certstore.rb161
-rw-r--r--ext/openssl/sample/cipher.rb29
-rw-r--r--ext/openssl/sample/crlstore.rb122
-rw-r--r--ext/openssl/sample/echo_cli.rb36
-rw-r--r--ext/openssl/sample/echo_svr.rb64
-rw-r--r--ext/openssl/sample/gen_csr.rb52
-rw-r--r--ext/openssl/sample/smime_read.rb23
-rw-r--r--ext/openssl/sample/smime_write.rb23
-rw-r--r--ext/openssl/sample/wget.rb33
-rw-r--r--ext/pty/README40
-rw-r--r--ext/pty/pty.c93
-rw-r--r--ext/pty/script.rb3
-rw-r--r--ext/racc/cparse/cparse.c2
-rw-r--r--ext/readline/readline.c21
-rw-r--r--ext/sdbm/init.c31
-rw-r--r--ext/sdbm/testsdbm.rb6
-rw-r--r--ext/socket/addrinfo.h2
-rw-r--r--ext/socket/extconf.rb34
-rw-r--r--ext/socket/socket.c87
-rw-r--r--ext/stringio/stringio.c29
-rw-r--r--ext/strscan/strscan.c40
-rw-r--r--ext/syck/.cvsignore3
-rw-r--r--ext/syck/MANIFEST12
-rw-r--r--ext/syck/emitter.c434
-rw-r--r--ext/syck/extconf.rb5
-rw-r--r--ext/syck/gram.c1702
-rw-r--r--ext/syck/gram.h79
-rw-r--r--ext/syck/handler.c146
-rw-r--r--ext/syck/implicit.c2826
-rw-r--r--ext/syck/node.c324
-rw-r--r--ext/syck/rubyext.c1288
-rw-r--r--ext/syck/syck.c493
-rw-r--r--ext/syck/syck.h378
-rw-r--r--ext/syck/token.c2345
-rw-r--r--ext/syslog/syslog.c1
-rw-r--r--ext/tcltklib/MANIFEST5
-rw-r--r--ext/tcltklib/MANUAL.euc233
-rw-r--r--ext/tcltklib/README.euc26
-rw-r--r--ext/tcltklib/demo/safeTk.rb22
-rw-r--r--ext/tcltklib/extconf.rb40
-rw-r--r--ext/tcltklib/lib/tcltk.rb6
-rw-r--r--ext/tcltklib/tcltklib.c543
-rw-r--r--ext/tk/MANIFEST150
-rw-r--r--ext/tk/lib/README4
-rw-r--r--ext/tk/lib/multi-tk.rb1195
-rw-r--r--ext/tk/lib/tk.rb2209
-rw-r--r--ext/tk/lib/tkafter.rb81
-rw-r--r--ext/tk/lib/tkbgerror.rb14
-rw-r--r--ext/tk/lib/tkcanvas.rb132
-rw-r--r--ext/tk/lib/tkconsole.rb28
-rw-r--r--ext/tk/lib/tkdialog.rb194
-rw-r--r--ext/tk/lib/tkentry.rb89
-rw-r--r--ext/tk/lib/tkfont.rb28
-rw-r--r--ext/tk/lib/tkmacpkg.rb56
-rw-r--r--ext/tk/lib/tkmngfocus.rb12
-rw-r--r--ext/tk/lib/tkpalette.rb6
-rw-r--r--ext/tk/lib/tktext.rb545
-rw-r--r--ext/tk/lib/tkvirtevent.rb12
-rw-r--r--ext/tk/lib/tkwinpkg.rb84
-rw-r--r--ext/tk/sample/demos-en/ChangeLog64
-rw-r--r--ext/tk/sample/demos-en/ChangeLog.prev9
-rw-r--r--ext/tk/sample/demos-en/README138
-rw-r--r--ext/tk/sample/demos-en/README.tkencoding24
-rw-r--r--ext/tk/sample/demos-en/arrow.rb239
-rw-r--r--ext/tk/sample/demos-en/bind.rb110
-rw-r--r--ext/tk/sample/demos-en/bitmap.rb73
-rw-r--r--ext/tk/sample/demos-en/browse163
-rw-r--r--ext/tk/sample/demos-en/browse282
-rw-r--r--ext/tk/sample/demos-en/button.rb84
-rw-r--r--ext/tk/sample/demos-en/check.rb70
-rw-r--r--ext/tk/sample/demos-en/clrpick.rb77
-rw-r--r--ext/tk/sample/demos-en/colors.rb148
-rw-r--r--ext/tk/sample/demos-en/cscroll.rb134
-rw-r--r--ext/tk/sample/demos-en/ctext.rb186
-rw-r--r--ext/tk/sample/demos-en/dialog1.rb35
-rw-r--r--ext/tk/sample/demos-en/dialog2.rb38
-rw-r--r--ext/tk/sample/demos-en/doc.org/README7
-rw-r--r--ext/tk/sample/demos-en/doc.org/README.JP14
-rw-r--r--ext/tk/sample/demos-en/doc.org/README.tk8046
-rw-r--r--ext/tk/sample/demos-en/doc.org/license.terms39
-rw-r--r--ext/tk/sample/demos-en/doc.org/license.terms.tk8039
-rw-r--r--ext/tk/sample/demos-en/entry1.rb56
-rw-r--r--ext/tk/sample/demos-en/entry2.rb91
-rw-r--r--ext/tk/sample/demos-en/filebox.rb97
-rw-r--r--ext/tk/sample/demos-en/floor.rb1721
-rw-r--r--ext/tk/sample/demos-en/form.rb62
-rw-r--r--ext/tk/sample/demos-en/hello14
-rw-r--r--ext/tk/sample/demos-en/hscale.rb74
-rw-r--r--ext/tk/sample/demos-en/icon.rb95
-rw-r--r--ext/tk/sample/demos-en/image1.rb60
-rw-r--r--ext/tk/sample/demos-en/image2.rb105
-rw-r--r--ext/tk/sample/demos-en/images/earth.gifbin0 -> 51709 bytes-rw-r--r--ext/tk/sample/demos-en/images/earthris.gifbin0 -> 6343 bytes-rw-r--r--ext/tk/sample/demos-en/images/face.xbm173
-rw-r--r--ext/tk/sample/demos-en/images/flagdown.xbm27
-rw-r--r--ext/tk/sample/demos-en/images/flagup.xbm27
-rw-r--r--ext/tk/sample/demos-en/images/gray25.xbm6
-rw-r--r--ext/tk/sample/demos-en/images/grey.256
-rw-r--r--ext/tk/sample/demos-en/images/grey.56
-rw-r--r--ext/tk/sample/demos-en/images/letters.xbm27
-rw-r--r--ext/tk/sample/demos-en/images/noletter.xbm27
-rw-r--r--ext/tk/sample/demos-en/images/pattern.xbm6
-rw-r--r--ext/tk/sample/demos-en/images/tcllogo.gifbin0 -> 2341 bytes-rw-r--r--ext/tk/sample/demos-en/images/teapot.ppm56
-rw-r--r--ext/tk/sample/demos-en/items.rb374
-rw-r--r--ext/tk/sample/demos-en/ixset333
-rw-r--r--ext/tk/sample/demos-en/label.rb69
-rw-r--r--ext/tk/sample/demos-en/menu.rb185
-rw-r--r--ext/tk/sample/demos-en/menubu.rb225
-rw-r--r--ext/tk/sample/demos-en/msgbox.rb88
-rw-r--r--ext/tk/sample/demos-en/patch_1.1c193
-rw-r--r--ext/tk/sample/demos-en/plot.rb122
-rw-r--r--ext/tk/sample/demos-en/puzzle.rb111
-rw-r--r--ext/tk/sample/demos-en/radio.rb84
-rw-r--r--ext/tk/sample/demos-en/rmt265
-rw-r--r--ext/tk/sample/demos-en/rolodex320
-rw-r--r--ext/tk/sample/demos-en/rolodex-j324
-rw-r--r--ext/tk/sample/demos-en/ruler.rb201
-rw-r--r--ext/tk/sample/demos-en/sayings.rb104
-rw-r--r--ext/tk/sample/demos-en/search.rb192
-rw-r--r--ext/tk/sample/demos-en/square74
-rw-r--r--ext/tk/sample/demos-en/states.rb78
-rw-r--r--ext/tk/sample/demos-en/style.rb211
-rw-r--r--ext/tk/sample/demos-en/tcolor513
-rw-r--r--ext/tk/sample/demos-en/tcolor.bak513
-rw-r--r--ext/tk/sample/demos-en/text.rb102
-rw-r--r--ext/tk/sample/demos-en/timer120
-rw-r--r--ext/tk/sample/demos-en/tkencoding.rb42
-rw-r--r--ext/tk/sample/demos-en/twind.rb285
-rw-r--r--ext/tk/sample/demos-en/vscale.rb78
-rw-r--r--ext/tk/sample/demos-en/widget499
-rw-r--r--ext/tk/sample/demos-jp/README54
-rw-r--r--ext/tk/sample/demos-jp/arrow.rb235
-rw-r--r--ext/tk/sample/demos-jp/bind.rb106
-rw-r--r--ext/tk/sample/demos-jp/bitmap.rb70
-rw-r--r--ext/tk/sample/demos-jp/browse163
-rw-r--r--ext/tk/sample/demos-jp/browse282
-rw-r--r--ext/tk/sample/demos-jp/button.rb80
-rw-r--r--ext/tk/sample/demos-jp/check.rb66
-rw-r--r--ext/tk/sample/demos-jp/clrpick.rb74
-rw-r--r--ext/tk/sample/demos-jp/colors.rb143
-rw-r--r--ext/tk/sample/demos-jp/cscroll.rb130
-rw-r--r--ext/tk/sample/demos-jp/ctext.rb181
-rw-r--r--ext/tk/sample/demos-jp/dialog1.rb35
-rw-r--r--ext/tk/sample/demos-jp/dialog2.rb39
-rw-r--r--ext/tk/sample/demos-jp/doc.org/README7
-rw-r--r--ext/tk/sample/demos-jp/doc.org/README.JP14
-rw-r--r--ext/tk/sample/demos-jp/doc.org/README.tk8046
-rw-r--r--ext/tk/sample/demos-jp/doc.org/license.terms39
-rw-r--r--ext/tk/sample/demos-jp/doc.org/license.terms.tk8039
-rw-r--r--ext/tk/sample/demos-jp/entry1.rb56
-rw-r--r--ext/tk/sample/demos-jp/entry2.rb87
-rw-r--r--ext/tk/sample/demos-jp/filebox.rb95
-rw-r--r--ext/tk/sample/demos-jp/floor.rb1717
-rw-r--r--ext/tk/sample/demos-jp/form.rb62
-rw-r--r--ext/tk/sample/demos-jp/hello9
-rw-r--r--ext/tk/sample/demos-jp/hscale.rb76
-rw-r--r--ext/tk/sample/demos-jp/icon.rb91
-rw-r--r--ext/tk/sample/demos-jp/image1.rb57
-rw-r--r--ext/tk/sample/demos-jp/image2.rb101
-rw-r--r--ext/tk/sample/demos-jp/images/earth.gifbin0 -> 51709 bytes-rw-r--r--ext/tk/sample/demos-jp/images/earthris.gifbin0 -> 6343 bytes-rw-r--r--ext/tk/sample/demos-jp/images/face.bmp173
-rw-r--r--ext/tk/sample/demos-jp/images/flagdown.bmp27
-rw-r--r--ext/tk/sample/demos-jp/images/flagup.bmp27
-rw-r--r--ext/tk/sample/demos-jp/images/gray25.bmp6
-rw-r--r--ext/tk/sample/demos-jp/images/grey.256
-rw-r--r--ext/tk/sample/demos-jp/images/grey.56
-rw-r--r--ext/tk/sample/demos-jp/images/letters.bmp27
-rw-r--r--ext/tk/sample/demos-jp/images/noletter.bmp27
-rw-r--r--ext/tk/sample/demos-jp/images/pattern.bmp6
-rw-r--r--ext/tk/sample/demos-jp/images/tcllogo.gifbin0 -> 2341 bytes-rw-r--r--ext/tk/sample/demos-jp/images/teapot.ppm56
-rw-r--r--ext/tk/sample/demos-jp/items.rb372
-rw-r--r--ext/tk/sample/demos-jp/ixset333
-rw-r--r--ext/tk/sample/demos-jp/label.rb64
-rw-r--r--ext/tk/sample/demos-jp/menu.rb186
-rw-r--r--ext/tk/sample/demos-jp/menu8x.rb219
-rw-r--r--ext/tk/sample/demos-jp/menubu.rb223
-rw-r--r--ext/tk/sample/demos-jp/msgbox.rb85
-rw-r--r--ext/tk/sample/demos-jp/plot.rb118
-rw-r--r--ext/tk/sample/demos-jp/puzzle.rb105
-rw-r--r--ext/tk/sample/demos-jp/radio.rb80
-rw-r--r--ext/tk/sample/demos-jp/rmt265
-rw-r--r--ext/tk/sample/demos-jp/rolodex320
-rw-r--r--ext/tk/sample/demos-jp/rolodex-j311
-rw-r--r--ext/tk/sample/demos-jp/ruler.rb197
-rw-r--r--ext/tk/sample/demos-jp/sayings.rb99
-rw-r--r--ext/tk/sample/demos-jp/search.rb187
-rw-r--r--ext/tk/sample/demos-jp/square74
-rw-r--r--ext/tk/sample/demos-jp/states.rb70
-rw-r--r--ext/tk/sample/demos-jp/style.rb247
-rw-r--r--ext/tk/sample/demos-jp/tcolor513
-rw-r--r--ext/tk/sample/demos-jp/text.rb94
-rw-r--r--ext/tk/sample/demos-jp/timer120
-rw-r--r--ext/tk/sample/demos-jp/twind.rb284
-rw-r--r--ext/tk/sample/demos-jp/vscale.rb77
-rw-r--r--ext/tk/sample/demos-jp/widget504
-rw-r--r--ext/tk/sample/resource.en12
-rw-r--r--ext/tk/sample/resource.ja12
-rw-r--r--ext/tk/sample/safe-tk.rb71
-rw-r--r--ext/tk/sample/tkbiff.rb12
-rw-r--r--ext/tk/sample/tkdialog.rb1
-rw-r--r--ext/tk/sample/tkfrom.rb8
-rw-r--r--ext/tk/sample/tkmenubutton.rb136
-rw-r--r--ext/tk/sample/tkoptdb-safeTk.rb18
-rw-r--r--ext/tk/sample/tkoptdb.rb84
-rw-r--r--ext/tk/sample/tktimer2.rb47
-rw-r--r--ext/win32ole/tests/testOLEMETHOD.rb4
-rw-r--r--ext/win32ole/tests/testOLEPARAM.rb7
-rw-r--r--ext/win32ole/tests/testOLETYPE.rb13
-rw-r--r--ext/win32ole/tests/testOLEVARIABLE.rb7
-rw-r--r--ext/win32ole/tests/testall.rb4
-rw-r--r--ext/win32ole/win32ole.c50
-rw-r--r--ext/zlib/.cvsignore3
-rw-r--r--ext/zlib/MANIFEST4
-rw-r--r--ext/zlib/doc/zlib.rd911
-rw-r--r--ext/zlib/extconf.rb66
-rw-r--r--ext/zlib/zlib.c2773
-rw-r--r--file.c571
-rw-r--r--gc.c288
-rw-r--r--hash.c434
-rw-r--r--inits.c2
-rw-r--r--instruby.rb210
-rw-r--r--intern.h63
-rw-r--r--io.c830
-rw-r--r--lib/README16
-rw-r--r--lib/benchmark.rb21
-rw-r--r--lib/cgi-lib.rb2
-rw-r--r--lib/cgi.rb66
-rw-r--r--lib/cgi/session.rb12
-rw-r--r--lib/cgi/session/pstore.rb78
-rw-r--r--lib/complex.rb451
-rw-r--r--lib/csv.rb1322
-rw-r--r--lib/date.rb20
-rw-r--r--lib/date/format.rb28
-rw-r--r--lib/debug.rb80
-rw-r--r--lib/delegate.rb12
-rw-r--r--lib/drb.rb2
-rw-r--r--lib/drb/drb.rb807
-rw-r--r--lib/drb/eq.rb16
-rw-r--r--lib/drb/extserv.rb67
-rw-r--r--lib/drb/extservm.rb94
-rw-r--r--lib/drb/gw.rb60
-rw-r--r--lib/drb/invokemethod.rb33
-rw-r--r--lib/drb/observer.rb22
-rw-r--r--lib/drb/timeridconv.rb91
-rw-r--r--lib/drb/unix.rb106
-rw-r--r--lib/erb.rb449
-rw-r--r--lib/fileutils.rb1021
-rw-r--r--lib/final.rb4
-rw-r--r--lib/find.rb53
-rw-r--r--lib/ftools.rb16
-rw-r--r--lib/ftplib.rb14
-rw-r--r--lib/getoptlong.rb4
-rw-r--r--lib/gserver.rb175
-rw-r--r--lib/ipaddr.rb700
-rw-r--r--lib/irb.rb8
-rw-r--r--lib/irb/completion.rb4
-rw-r--r--lib/irb/context.rb7
-rw-r--r--lib/irb/ext/math-mode.rb2
-rw-r--r--lib/irb/ext/multi-irb.rb4
-rw-r--r--lib/irb/input-method.rb2
-rw-r--r--lib/irb/lc/error.rb10
-rw-r--r--lib/irb/lc/help-message32
-rw-r--r--lib/irb/lc/ja/error.rb10
-rw-r--r--lib/irb/locale.rb23
-rw-r--r--lib/irb/ruby-lex.rb17
-rw-r--r--lib/irb/slex.rb12
-rw-r--r--lib/irb/workspace.rb2
-rw-r--r--lib/jcode.rb25
-rw-r--r--lib/mathn.rb35
-rw-r--r--lib/matrix.rb425
-rw-r--r--lib/mkmf.rb252
-rw-r--r--lib/monitor.rb76
-rw-r--r--lib/net/ftp.rb325
-rw-r--r--lib/net/http.rb185
-rw-r--r--lib/net/imap.rb232
-rw-r--r--lib/net/pop.rb821
-rw-r--r--lib/net/protocol.rb650
-rw-r--r--lib/net/smtp.rb622
-rw-r--r--lib/net/telnet.rb24
-rw-r--r--lib/observer.rb152
-rw-r--r--lib/open-uri.rb106
-rw-r--r--lib/optparse.rb167
-rw-r--r--lib/optparse/date.rb17
-rw-r--r--lib/optparse/shellwords.rb2
-rw-r--r--lib/optparse/time.rb2
-rw-r--r--lib/optparse/uri.rb2
-rw-r--r--lib/parsedate.rb2
-rw-r--r--lib/pathname.rb523
-rw-r--r--lib/pp.rb195
-rw-r--r--lib/prettyprint.rb21
-rw-r--r--lib/profile.rb61
-rw-r--r--lib/profiler.rb59
-rw-r--r--lib/pstore.rb5
-rw-r--r--lib/racc/parser.rb5
-rw-r--r--lib/rational.rb94
-rw-r--r--lib/resolv.rb98
-rw-r--r--lib/rexml/attlistdecl.rb62
-rw-r--r--lib/rexml/attribute.rb151
-rw-r--r--lib/rexml/cdata.rb68
-rw-r--r--lib/rexml/child.rb96
-rw-r--r--lib/rexml/comment.rb79
-rw-r--r--lib/rexml/doctype.rb182
-rw-r--r--lib/rexml/document.rb237
-rw-r--r--lib/rexml/dtd/attlistdecl.rb10
-rw-r--r--lib/rexml/dtd/dtd.rb51
-rw-r--r--lib/rexml/dtd/elementdecl.rb17
-rw-r--r--lib/rexml/dtd/entitydecl.rb56
-rw-r--r--lib/rexml/dtd/notationdecl.rb39
-rw-r--r--lib/rexml/element.rb1147
-rw-r--r--lib/rexml/encoding.rb62
-rw-r--r--lib/rexml/encodings/EUC-JP.rb32
-rw-r--r--lib/rexml/encodings/EUC-JP_decl.rb6
-rw-r--r--lib/rexml/encodings/ISO-8859-1.rb23
-rw-r--r--lib/rexml/encodings/ISO-8859-1_decl.rb6
-rw-r--r--lib/rexml/encodings/SHIFT-JIS.rb1
-rw-r--r--lib/rexml/encodings/SHIFT_JIS.rb33
-rw-r--r--lib/rexml/encodings/Shift-JIS_decl.rb6
-rw-r--r--lib/rexml/encodings/UNILE.rb27
-rw-r--r--lib/rexml/encodings/UNILE_decl.rb6
-rw-r--r--lib/rexml/encodings/US-ASCII.rb23
-rw-r--r--lib/rexml/encodings/US-ASCII_decl.rb6
-rw-r--r--lib/rexml/encodings/UTF-16.rb27
-rw-r--r--lib/rexml/encodings/UTF-16_decl.rb6
-rw-r--r--lib/rexml/entity.rb159
-rw-r--r--lib/rexml/functions.rb360
-rw-r--r--lib/rexml/instruction.rb62
-rw-r--r--lib/rexml/light/node.rb231
-rw-r--r--lib/rexml/namespace.rb47
-rw-r--r--lib/rexml/node.rb35
-rw-r--r--lib/rexml/output.rb22
-rw-r--r--lib/rexml/parent.rb165
-rw-r--r--lib/rexml/parseexception.rb44
-rw-r--r--lib/rexml/parsers/baseparser.rb394
-rw-r--r--lib/rexml/parsers/lightparser.rb56
-rw-r--r--lib/rexml/parsers/pullparser.rb143
-rw-r--r--lib/rexml/parsers/sax2parser.rb204
-rw-r--r--lib/rexml/parsers/streamparser.rb33
-rw-r--r--lib/rexml/parsers/ultralightparser.rb52
-rw-r--r--lib/rexml/parsers/xpathparser.rb598
-rw-r--r--lib/rexml/quickpath.rb266
-rw-r--r--lib/rexml/rexml.rb26
-rw-r--r--lib/rexml/sax2listener.rb94
-rw-r--r--lib/rexml/source.rb191
-rw-r--r--lib/rexml/streamlistener.rb89
-rw-r--r--lib/rexml/text.rb279
-rw-r--r--lib/rexml/xmldecl.rb72
-rw-r--r--lib/rexml/xmltokens.rb18
-rw-r--r--lib/rexml/xpath.rb62
-rw-r--r--lib/rexml/xpath_parser.rb530
-rw-r--r--lib/rubyunit.rb8
-rw-r--r--lib/runit/assert.rb71
-rw-r--r--lib/runit/cui/testrunner.rb51
-rw-r--r--lib/runit/error.rb9
-rw-r--r--lib/runit/testcase.rb45
-rw-r--r--lib/runit/testresult.rb44
-rw-r--r--lib/runit/testsuite.rb26
-rw-r--r--lib/runit/topublic.rb8
-rw-r--r--lib/scanf.rb697
-rw-r--r--lib/set.rb338
-rw-r--r--lib/shell.rb1
-rw-r--r--lib/shell/command-processor.rb6
-rw-r--r--lib/shell/error.rb4
-rw-r--r--lib/shell/filter.rb8
-rw-r--r--lib/shellwords.rb61
-rw-r--r--lib/singleton.rb4
-rw-r--r--lib/telnet.rb9
-rw-r--r--lib/tempfile.rb143
-rw-r--r--lib/test/unit.rb214
-rw-r--r--lib/test/unit/assertionfailederror.rb14
-rw-r--r--lib/test/unit/assertions.rb394
-rw-r--r--lib/test/unit/error.rb68
-rw-r--r--lib/test/unit/failure.rb45
-rw-r--r--lib/test/unit/testcase.rb152
-rw-r--r--lib/test/unit/testresult.rb81
-rw-r--r--lib/test/unit/testsuite.rb64
-rw-r--r--lib/test/unit/ui/console/testrunner.rb135
-rw-r--r--lib/test/unit/ui/fox/testrunner.rb270
-rw-r--r--lib/test/unit/ui/gtk/testrunner.rb389
-rw-r--r--lib/test/unit/ui/testrunnermediator.rb75
-rw-r--r--lib/test/unit/ui/testrunnerutilities.rb36
-rw-r--r--lib/test/unit/util/observable.rb90
-rw-r--r--lib/test/unit/util/procwrapper.rb48
-rw-r--r--lib/thread.rb136
-rw-r--r--lib/thwait.rb81
-rw-r--r--lib/time.rb467
-rw-r--r--lib/timeout.rb50
-rw-r--r--lib/tmpdir.rb42
-rw-r--r--lib/tracer.rb3
-rw-r--r--lib/tsort.rb34
-rw-r--r--lib/un.rb183
-rw-r--r--lib/uri.rb2
-rw-r--r--lib/uri/ftp.rb1
-rw-r--r--lib/uri/generic.rb21
-rw-r--r--lib/uri/ldap.rb5
-rw-r--r--lib/uri/mailto.rb2
-rw-r--r--lib/webrick.rb29
-rw-r--r--lib/webrick/accesslog.rb64
-rw-r--r--lib/webrick/compat.rb30
-rw-r--r--lib/webrick/config.rb96
-rw-r--r--lib/webrick/cookie.rb80
-rw-r--r--lib/webrick/htmlutils.rb25
-rw-r--r--lib/webrick/httpauth.rb46
-rw-r--r--lib/webrick/httpauth/authenticator.rb79
-rw-r--r--lib/webrick/httpauth/basicauth.rb66
-rw-r--r--lib/webrick/httpauth/digestauth.rb348
-rw-r--r--lib/webrick/httpauth/htdigest.rb91
-rw-r--r--lib/webrick/httpauth/htgroup.rb61
-rw-r--r--lib/webrick/httpauth/htpasswd.rb75
-rw-r--r--lib/webrick/httpauth/userdb.rb29
-rw-r--r--lib/webrick/httpproxy.rb237
-rw-r--r--lib/webrick/httprequest.rb339
-rw-r--r--lib/webrick/httpresponse.rb304
-rw-r--r--lib/webrick/https.rb158
-rw-r--r--lib/webrick/httpserver.rb179
-rw-r--r--lib/webrick/httpservlet.rb22
-rw-r--r--lib/webrick/httpservlet/abstract.rb71
-rw-r--r--lib/webrick/httpservlet/cgi_runner.rb45
-rw-r--r--lib/webrick/httpservlet/cgihandler.rb93
-rw-r--r--lib/webrick/httpservlet/erbhandler.rb53
-rw-r--r--lib/webrick/httpservlet/filehandler.rb330
-rw-r--r--lib/webrick/httpservlet/prochandler.rb33
-rw-r--r--lib/webrick/httpstatus.rb126
-rw-r--r--lib/webrick/httputils.rb374
-rw-r--r--lib/webrick/httpversion.rb49
-rw-r--r--lib/webrick/log.rb83
-rw-r--r--lib/webrick/server.rb189
-rw-r--r--lib/webrick/utils.rb64
-rw-r--r--lib/webrick/version.rb13
-rw-r--r--lib/xmlrpc/base64.rb81
-rw-r--r--lib/xmlrpc/client.rb570
-rw-r--r--lib/xmlrpc/config.rb40
-rw-r--r--lib/xmlrpc/create.rb280
-rw-r--r--lib/xmlrpc/datetime.rb138
-rw-r--r--lib/xmlrpc/httpserver.rb178
-rw-r--r--lib/xmlrpc/marshal.rb76
-rw-r--r--lib/xmlrpc/parser.rb803
-rw-r--r--lib/xmlrpc/server.rb839
-rw-r--r--lib/xmlrpc/utils.rb172
-rw-r--r--lib/yaml.rb222
-rw-r--r--lib/yaml/baseemitter.rb241
-rw-r--r--lib/yaml/basenode.rb216
-rw-r--r--lib/yaml/constants.rb45
-rw-r--r--lib/yaml/dbm.rb111
-rw-r--r--lib/yaml/emitter.rb107
-rw-r--r--lib/yaml/encoding.rb29
-rw-r--r--lib/yaml/error.rb33
-rw-r--r--lib/yaml/loader.rb14
-rw-r--r--lib/yaml/rubytypes.rb583
-rw-r--r--lib/yaml/store.rb75
-rw-r--r--lib/yaml/stream.rb44
-rw-r--r--lib/yaml/stringio.rb83
-rw-r--r--lib/yaml/syck.rb27
-rw-r--r--lib/yaml/types.rb196
-rw-r--r--lib/yaml/yamlnode.rb54
-rw-r--r--lib/yaml/ypath.rb52
-rw-r--r--main.c2
-rw-r--r--marshal.c309
-rw-r--r--math.c29
-rwxr-xr-xmdoc2man.rb465
-rw-r--r--misc/ruby-mode.el404
-rw-r--r--missing.h12
-rw-r--r--missing/erf.c91
-rw-r--r--missing/strftime.c14
-rw-r--r--mkconfig.rb10
-rw-r--r--node.h211
-rw-r--r--numeric.c366
-rw-r--r--object.c299
-rw-r--r--pack.c62
-rw-r--r--parse.y780
-rw-r--r--prec.c4
-rw-r--r--process.c1057
-rw-r--r--random.c7
-rw-r--r--range.c185
-rw-r--r--re.c313
-rw-r--r--re.h4
-rw-r--r--regex.c171
-rw-r--r--ruby.1524
-rw-r--r--ruby.c98
-rw-r--r--ruby.h157
-rw-r--r--rubyio.h5
-rw-r--r--rubysig.h14
-rw-r--r--rubytest.rb2
-rw-r--r--sample/README2
-rw-r--r--sample/biorhythm.rb26
-rw-r--r--sample/cal.rb4
-rw-r--r--sample/clnt.rb10
-rw-r--r--sample/dir.rb6
-rw-r--r--sample/eval.rb15
-rw-r--r--sample/exyacc.rb4
-rw-r--r--sample/fact.rb5
-rw-r--r--sample/fullpath.rb2
-rw-r--r--sample/occur.rb2
-rw-r--r--sample/occur2.rb4
-rw-r--r--sample/pi.rb2
-rw-r--r--sample/rcs.rb20
-rw-r--r--sample/svr.rb4
-rw-r--r--sample/test.rb702
-rw-r--r--sample/time.rb2
-rw-r--r--sample/trojan.rb2
-rw-r--r--sample/tsvr.rb6
-rw-r--r--signal.c26
-rw-r--r--sprintf.c90
-rw-r--r--st.c59
-rw-r--r--st.h30
-rw-r--r--string.c446
-rw-r--r--struct.c113
-rw-r--r--time.c146
-rw-r--r--util.c146
-rw-r--r--util.h2
-rw-r--r--variable.c556
-rw-r--r--version.c4
-rw-r--r--version.h15
-rw-r--r--win32/Makefile.sub74
-rw-r--r--win32/resource.rb2
-rw-r--r--win32/setup.mak13
-rw-r--r--win32/win32.c1161
-rw-r--r--win32/win32.h196
-rw-r--r--wince/Makefile.sub707
-rw-r--r--wince/README.wince101
-rw-r--r--wince/assert.c11
-rw-r--r--wince/assert.h2
-rw-r--r--wince/config141
-rw-r--r--wince/configure.bat192
-rw-r--r--wince/direct.c4
-rw-r--r--wince/direct.h3
-rw-r--r--wince/dll.mak1531
-rw-r--r--wince/exe.mak353
-rw-r--r--wince/io.h22
-rw-r--r--wince/io_wce.c (renamed from wince/io.c)8
-rw-r--r--wince/mkexports.rb35
-rw-r--r--wince/mswince-ruby17.def813
-rw-r--r--wince/process.h2
-rw-r--r--wince/process_wce.c (renamed from wince/process.c)0
-rw-r--r--wince/resource.rb96
-rw-r--r--wince/setup.mak222
-rw-r--r--wince/signal_wce.c (renamed from wince/signal.c)0
-rw-r--r--wince/stdio.c2
-rw-r--r--wince/stdlib.c35
-rw-r--r--wince/string_wce.c (renamed from wince/string.c)32
-rw-r--r--wince/sys/stat.c3
-rw-r--r--wince/sys/utime.c2
-rw-r--r--wince/sys/utime.h4
-rw-r--r--wince/time.h4
-rw-r--r--wince/time_wce.c (renamed from wince/time.c)0
-rw-r--r--wince/wince.c62
-rw-r--r--wince/wince.h49
-rw-r--r--wince/wincemain.c3
699 files changed, 107693 insertions, 14465 deletions
diff --git a/ChangeLog b/ChangeLog
index 1c276212c4..faeb0d127a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,4939 @@
+Fri Aug 1 19:48:56 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * ext/syck/rubyext.c (syck_emitter_write_m): forgot to declare
+ "self", making it default to "int".
+
+ * ext/syck/rubyext.c (syck_emitter_simple_write): ditto.
+
+ * gc.c (rb_gc): should mark backing store region on IA64.
+
+Fri Aug 1 17:13:23 2003 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * ext/openssl/extconf.rb: should replace literally.
+
+Fri Aug 1 16:22:57 2003 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * io.c (rb_io_check_readable, rb_io_check_writable): ensure not
+ closed at first.
+
+ * io.c (rb_io_getline): check readable always. (ruby-bugs:PR#1069)
+
+ * io.c (rb_io_each_byte): ditto.
+
+Fri Aug 1 16:02:46 2003 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * io.c (READ_DATA_PENDING_PTR): cast to get rid of warnings.
+
+ * ext/socket/socket.c (unix_send_io, unix_recv_io): ditto.
+
+Fri Aug 1 15:53:24 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * win32/win32.c (isInternalCmd): shouldn't return if find end of str.
+ [ruby-talk:77678]
+
+Fri Aug 1 13:45:14 2003 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * eval.c (rb_call_super): propagate previous block if a block is
+ given. [ruby-talk:77577]
+
+Fri Aug 1 09:54:38 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (BEGIN_CALLARGS): should not always reset ruby_iter,
+ need to restore previous value. [ruby-talk:77577]
+
+ * array.c (rb_ary_fill): array length may be changed during the
+ block execution. [ruby-talk:77579]
+
+ * array.c (rb_ary_zip): ditto.
+
+ * array.c (rb_ary_fill): ditto.
+
+ * hash.c (env_reject_bang): length may be changed during the block
+ execution.
+
+ * hash.c (env_clear): ditto.
+
+Fri Aug 1 00:52:58 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * gc.c (Init_stack): IA64 requires STACK_LEVEL_MAX to be less than
+ magic number when optimizer turned on, regardless of rlimit
+ values.
+
+Thu Jul 31 23:44:00 2003 Masatoshi SEKI <m_seki@mva.biglobe.ne.jp>
+
+ * lib/erb.rb: import erb-2.0.4b4.
+
+Thu Jul 31 23:02:47 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * ext/etc/etc.c: revert getenv()'s prototype. use it only when _WIN32
+ is not defined.
+
+Thu Jul 31 15:25:12 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * array.c (rb_ary_collect): must get length of array for each
+ iteration. reported on [ruby-talk:77500], and fixed by
+ K.Sasada <ko1@namikilab.tuat.ac.jp> on [ruby-talk:77504]
+
+Thu Jul 31 14:11:54 2003 GOTOU Yuuzou <gotoyuzo@notwork.org>
+
+ * ext/openssl/extconf.rb: move gmake specific features
+ into GNUmakefile.
+
+Thu Jul 31 12:36:11 2003 Masatoshi SEKI <m_seki@mva.biglobe.ne.jp>
+
+ * bin/erb, lib/erb.rb: add explicit trim mode.
+
+Thu Jul 31 04:59:10 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * numeric.c (rb_num_coerce_relop): export function.
+
+Thu Jul 31 00:17:19 2003 Shugo Maeda <shugo@ruby-lang.org>
+
+ * lib/net/ftp.rb (return_code): obsolete.
+
+ * lib/net/ftp.rb (last_response_code): new method. lastresp is now
+ alias to last_response_code.
+
+ * lib/net/ftp.rb (last_response): new method.
+
+Wed Jul 30 23:55:44 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * marshal.c (w_object): check has been dropped. "_dump must return
+ string." [ruby-dev:21024]
+
+Wed Jul 30 22:35:19 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * lib/mkmf.rb (dir_config): allow multiple directories separated
+ by File::PATH_SEPARATOR.
+
+ * lib/mkmf.rb (create_makefile): DLDFLAGS include $LDFLAGS again.
+ [ruby-talk:76894]
+
+ * lib/mkmf.rb (init_mkmf): not default $LDFLAGS to LDFLAGS for
+ ruby itself, but default $DLDFLAGS to DLDFLAGS.
+
+Wed Jul 30 16:17:06 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * marshal.c (w_object): marshal_dump should not take any
+ argument.
+
+Wed Jul 30 15:54:04 2003 GOTOU Yuuzou <gotoyuzo@notwork.org>
+
+ * ext/openssl/ossl_ssl.c (ossl_sslctx_initialize): should initialize
+ instance variables. [ruby-talk:77362]
+
+Wed Jul 30 15:39:54 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * ruby.c (proc_options): -F set compiled regular expression to $;.
+ [ruby-talk:77381]
+
+ * string.c (Init_String): no setter type check for $;
+
+Wed Jul 30 15:10:02 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * error.c (rb_raise): snprintf() termination moved to
+ win32/win32.c.
+
+ * win32/win32.c (valid_filename, str_grow): unused.
+
+ * win32/win32.c (NTLoginName, ChildRecord): make static.
+
+ * win32/win32.c (CreateChild): argument check.
+
+ * win32/win32.c (kill): should not call CloseHandle() when
+ OpenProcess() failed.
+
+ * win32/win32.c (rb_w32_vsnprintf, rb_w32_snprintf): ensure buffer
+ terminated. [ruby-talk:69672]
+
+Wed Jul 30 10:54:10 2003 Shugo Maeda <shugo@ruby-lang.org>
+
+ * lib/net/ftp.rb (get): fix wrong argument name. Thanks to William
+ Webber.
+
+Wed Jul 30 10:31:37 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * ext/iconv/iconv.c (iconv_convert): append unchanged portion
+ after overflow. [ruby-dev:21006]
+
+ * ext/iconv/extconf.rb: check if iconv() 2nd argument is const.
+
+Wed Jul 30 09:31:55 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * configure.in (os2-emx): renamed from os2_emx, add flags to
+ CFLAGS and LDFLAGS, and remove lib prefix. [ruby-dev:20993]
+
+ * file.c (rb_file_s_rename): retry with removing new file on
+ DOSISH. [ruby-dev:21007]
+
+ * ext/socket/extconf.rb (sendmsg, recvmsg): check functions.
+
+ * ext/socket/socket.c (unix_send_io, unix_recv_io): raise
+ NotImplementedError unless system calls are available.
+
+ * ext/socket/socket.c (sock_initialize): rename from sock_init()
+ to get rid of conflict with OS/2 socket library.
+
+Wed Jul 30 02:37:12 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * marshal.c (w_object): if object responds to 'marshal_dump',
+ Marshal.dump uses it to dump object. unlike '_dump',
+ marshal_dump returns any kind of object.
+
+ * marshal.c (r_object0): restore instance by calling
+ 'marshal_load' method. unlike '_load', it's an instance
+ method, to handle cyclic reference.
+
+ * marshal.c (marshal_load): all objects read from file should be
+ tainted. [ruby-core:01325]
+
+Wed Jul 30 01:47:51 2003 Hugh Sasse <hgs@dmu.ac.uk>
+
+ * lib/timeout.rb (Timeout::timeout): execute immediately if sec is
+ zero.
+
+Wed Jul 30 01:36:18 2003 Aron Griffis <ruby-talk@griffis1.net>
+
+ * ext/socket/socket.c (socks_init): typo fixed. [ruby-talk:77232]
+
+Wed Jul 30 00:48:43 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * ext/socket/extconf.rb: the default value for --enable-socks is
+ taken from ENV["SOCKS_SERVER"]. [ruby-talk:77232]
+
+ * ruby.c (proc_options): add -W option. -W0 to shut up all warning
+ messages. [ruby-talk:77227]
+
+ * error.c (rb_warn): no message will be printed if the value of
+ $VERBOSE is "nil", i.e. perfect silence.
+
+ * ruby.c (verbose_setter): $VERBOSE value is either true, false,
+ or nil.
+
+ * io.c (Init_IO): no "read" check for $stdin. in addition some
+ function names has been changed.
+
+Tue Jul 29 23:10:19 2003 Yoshida Masato <yoshidam@yoshidam.net>
+
+ * regex.c (re_match_exec): incorrect multibyte match.
+
+Tue Jul 29 22:36:50 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/net/smtp.rb (send0): do taint check only when $SAFE > 0
+
+Tue Jul 29 19:20:34 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * lib/fileutils.rb (install): support preserve timestamp.
+
+ * instruby.rb (install): use FileUtils::install preserve mode.
+
+ * lib/un.rb: new. % ruby -run -e cp -- -p foo bar
+
+ * lib/mkmf.rb: use un.rb instead of ftools.rb.
+
+ * MANIFEST: add lib/un.rb.
+
+ * ext/extmk.rb (INSTALL_PROG, INSTALL_DATA): modify verbose messages.
+
+Tue Jul 29 18:55:22 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/net/smtp.rb: unify coding style.
+
+ * lib/net/http.rb: ditto.
+
+Tue Jul 29 17:27:59 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * ruby.h (LLONG_MIN): fix typo.
+
+Tue Jul 29 16:38:44 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * lib/net/smtp.rb (Net::SMTP::send0): add taint check.
+
+Tue Jul 29 15:41:02 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * instruby.rb (install): preserve the timestamp for Mac OS X ranlib
+ problem.
+
+Tue Jul 29 01:14:51 2003 Rick Ohnemus <rick_ohnemus@acm.org>
+
+ * ruby.h (LLONG_MIN): wrong value.
+
+Mon Jul 28 22:57:52 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * io.c (rb_f_getc): $stdin may not be IO. [ruby-dev:20973]
+
+Tue Jul 29 12:22:28 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/token.c: prefixed many constants and definitions
+ with YAML_ to avoid name clash.
+
+ * ext/syck/gram.c: ditto.
+
+ * ext/syck/gram.h: ditto.
+
+Tue Jul 29 12:15:37 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * ext/etc/etc.c: add real prototype to getenv().
+
+ * win32/win32.h: add arguments to definitions of functions if possible.
+
+Tue Jul 29 04:22:08 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/syck.h: Added 'syck' yacc prefixes.
+
+ * ext/syck/gram.c: ditto.
+
+ * ext/syck/token.c: ditto.
+
+ * ext/syck: Added ruby.h reference to source files.
+
+Tue Jul 29 03:53:28 2003 GOTOU Yuuzou <gotoyuzo@notwork.org>
+
+ * ext/openssl/lib/net/https.rb (use_ssl=): raise ProtocolError if
+ connection is set up already.
+
+Mon Jul 28 23:23:08 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * file.c (Init_File): IO should include File::Const.
+ [ruby-dev:20964]
+
+Mon Jul 28 18:53:03 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * ext/openssl/extconf.rb: check again after pkg-config for MinGW on
+ Cygwin.
+
+Mon Jul 28 15:32:04 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * ext/stringio/stringio.c (strio_gets): only "gets" should set $_.
+
+ * ext/stringio/stringio.c (strio_getline): should not set $_ here.
+
+ * io.c (argf_to_s): argf.to_s returns "ARGF".
+
+ * io.c (set_defout_var, set_deferr_var): make $defout and $deferr
+ obsolete.
+
+ * io.c (set_input_var, set_output_var): allow $stdin, $stdout,
+ $stderr not to be instance of IO.
+
+ * io.c (rb_f_readline): forward method to current_file. gets,
+ readline, readlines, getc, readchar, tell, seek, pos=, rewind,
+ fileno, to_io, eof, each_line, each_byte, binmode, and closed?
+ as well.
+
+ * io.c (argf_forward): utility function to forward method to
+ current_file.
+
+Mon Jul 28 03:08:47 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * lib/set.rb: each() should return self.
+
+Mon Jul 28 01:35:32 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * string.c (rb_str_chomp_bang): defer rb_str_modify() to actual
+ modify point. other methods, replace, tr, delete, squeeze,
+ lstrip, and rstrip as well.
+
+ * string.c (rb_str_rstrip_bang): remove trailing '\0' at the end
+ of string.
+
+ * string.c (rb_str_lstrip_bang): do not strip '\0' from the left.
+
+Sun Jul 27 21:16:30 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * ext/openssl/extconf.rb: better support MinGW. add
+ dir_config("kerberos") and with_config("pkg-config").
+
+ * mkconfig.rb: initialize global variables to avoid warnings.
+
+Sun Jul 27 14:43:37 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>
+
+ * lib/debug.rb: fix breakpoint parameter parsing/checking.
+ (?:(file|class):)(line_number|method)
+
+Sun Jul 27 10:21:28 2003 Masatoshi SEKI <m_seki@mva.biglobe.ne.jp>
+
+ * lib/drb/unix.rb: add UNIXFileOwner, UNIXFileGroup.
+
+Sun Jul 27 03:10:43 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * io.c (io_reopen): avoid dup2() equal handles not to close itself and
+ to get rid of a msvcrt bug. [ruby-dev:20919]
+
+Sun Jul 27 00:37:16 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * lib/tmpdir.rb: use GetWindowsDirectory, not GetSystemDirectory.
+ [ruby-talk:77073]
+
+Sat Jul 26 21:25:21 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * io.c (rb_fdopen): set errno if it's zero on win32 platforms.
+
+ * ext/openssl/ossl_ssl.c (TO_SOCKET): define special version when
+ _WIN32 is defined. this is ruby's problem, not OpenSSL.
+
+ * win32/win32.c: remove some old comments.
+
+Sat Jul 26 14:26:57 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * ext/tk/lib/tk.rb (TkCore::chooseDirectory): back up wrongly
+ removed method.
+
+Sat Jul 26 14:14:12 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * ext/stringio/stringio.c: includes Enumerable as well as IO.
+ [ruby-talk:77058]
+
+Sat Jul 26 07:00:53 2003 Masatoshi SEKI <m_seki@mva.biglobe.ne.jp>
+
+ * lib/erb.rb: fix % line.
+
+Sat Jul 26 05:31:09 2003 GOTOU Yuuzou <gotoyuzo@notwork.org>
+
+ * ext/openssl/ossl.h: fix comment.
+
+ * ext/openssl/ossl.c (ossl_debug): should enable if no va-args
+ macro supplied.
+
+Sat Jul 26 04:04:36 2003 GOTOU Yuuzou <gotoyuzo@notwork.org>
+
+ * ext/openssl/extconf.rb: refine va-args macro detection.
+ [ruby-talk:76983]
+
+Sat Jul 26 01:33:51 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * ext/openssl/ossl_ssl.c (ossl_ssl_setup): need to pass the real
+ socket to SSL_get_fd on native win32 platforms.
+
+Sat Jul 26 01:20:29 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * variable.c (rb_mod_const_missing): "const_missing" should not
+ appear in the caller(); add call frame adjustment.
+
+ * eval.c (rb_method_missing): simplify call frame adjustment.
+
+Fri Jul 26 00:04:25 2003 NAKAMURA, Hiroshi <nakahiro@sarion.co.jp>
+
+ * ext/openssl/sample: add samples.
+ - cert2text.rb: dump certificate file as text.
+ - crlstore.rb: CRL store implementation. Fetch CRL via HTTP when
+ http-access2 is installed.
+ - certstore.rb: certificate store implementation.
+ - cert_store_view.rb: certificate store viewer with FXRuby. Uses
+ c_rehash.rb, crlstore.rb and certstore.rb.
+
+Fri Jul 25 15:47:39 2003 GOTOU Yuuzou <gotoyuzo@notwork.org>
+
+ * ext/openssl/extconf.rb: add check for BN_rand_range() and
+ BN_pseudo_rand_range().
+
+ * ext/openssl/ossl_bn.c (ossl_bn_s_rand_range): should raise
+ NotImplementedError if BN_rand_range() wan not defined.
+
+ * ext/openssl/ossl_bn.c (ossl_bn_s_pseudo_rand_range): should raise
+ NotImplementedError if BN_pseudo_rand_range() wan not defined.
+
+ * ext/openssl/ossl_pkcs7.c (ossl_pkcs7_s_encrypt): avoid compiler
+ warning for OpenSSL-0.9.6.
+
+ * ext/openssl/ossl_pkcs7.c (ossl_pkcs7si_initialize): ditto.
+
+Fri Jul 25 14:34:55 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * ext/socket/socket.c (tcp_s_gethostbyname): was using
+ uninitialized size_t value. [ruby-talk:76946]
+
+Fri Jul 25 13:38:38 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * re.c (rb_reg_options_m): use rb_reg_options() to mask internal
+ flags.
+
+ * re.c (rb_reg_initialize_m): allow nil as third argument and
+ ignore, and mask code flags if the argument is given.
+ [ruby-dev:20885]
+
+ * re.c (rb_reg_options): get common flags directly.
+
+Fri Jul 25 03:52:21 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * lib/yaml/dbm.rb: replace indexes with values_at.
+
+Fri Jul 25 02:55:59 2003 GOTOU Yuuzou <gotoyuzo@notwork.org>
+
+ * ext/openssl/extconf.rb: add check for libsocket and libnsl.
+
+ * ext/openssl/extconf.rb: use pkg-config to build CFLAGS and LDFLAGS.
+
+Fri Jul 25 01:27:59 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/emitter.c (syck_emitter_flush): accepts count
+ of bytes to flush. anchor offsets now functional.
+
+ * ext/syck/syck.h (syck_emitter_flush): ditto.
+
+ * ext/syck/rubyext.c: ditto.
+
+ * ext/syck/token.c: URI escaping now supported.
+
+Thu Jul 24 16:41:31 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * lib/mkmf.rb (have_type): check if a type is defined.
+
+ * lib/mkmf.rb (check_sizeof): check size of a type.
+
+ * ext/dbm/extconf.rb: check if type DBM is defined.
+ [ruby-talk:76693]
+
+Thu Jul 24 16:18:40 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * ChangeLog (add-log-time-format): "%c" contains timezone on
+ XEmacs.
+
+Thu Jul 24 16:05:22 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * configure.in (AC_C_VOLATILE): check if volatile works.
+
+ * defines.h (volatile): removed.
+
+ * eval.c (rb_thread_group): Thread#group. [new]
+
+Thu Jul 24 15:50:42 2003 GOTOU Yuuzou <gotoyuzo@notwork.org>
+
+ * ext/openssl/extconf.rb: add check for win32 OpenSSL libraries.
+
+ * ext/openssl/extconf.rb: add check for __VA_ARGS__.
+
+ * ext/openssl/ossl.h: avoid non C99 compiler errors.
+
+Thu Jul 24 13:32:56 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (thgroup_add): no warning for terminated threads.
+
+Thu Jul 24 13:09:26 2003 Tanaka Akira <akr@m17n.org>
+
+ * lib/pathname.rb: added.
+
+Thu Jul 24 11:21:10 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * ext/io/wait/extconf.rb: removed unnecessary backward
+ compatibility stuff.
+
+Thu Jul 24 11:09:10 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * ext/openssl/extconf.rb: revert use of dir_config.
+
+Thu Jul 24 09:58:32 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * ext/Win32API/lib/win32/resolv.rb: added.
+
+ * lib/resolv.rb: support Win32 platforms. based on Tietew's work
+ [ruby-dev:15573].
+
+Thu Jul 24 04:05:46 2003 GOTOU Yuuzou <gotoyuzo@notwork.org>
+
+ * ext/openssl/ssl.h: undef X509_NAME and PKCS7_SIGNER_INFO to
+ avoid name confliction on mswin32.
+
+ * ext/openssl/ssl.c (ossl_protect_obj2bio): avoid VC++ warnings
+ in function prototype.
+
+ * ext/openssl/ssl.c (ossl_protect_membio2str): ditto.
+
+ * ext/openssl/ssl.c (ossl_protect_x509_ary2sk): ditto.
+
+Thu Jul 24 03:44:04 2003 Michal Rokos <m.rokos@sh.cvut.cz>
+
+ * ext/openssl/extconf.rb: cut check for OpenSSL version
+
+Thu Jul 24 03:41:30 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * ext/tcltklib/tcltklib.c (ip_init): need at least one statement after
+ label.
+
+Thu Jul 24 01:48:03 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * lib/cgi.rb (CGI::QueryExtension::[]): should return StringIO (or
+ Tempfile) for multipart/form.
+
+ * variable.c (rb_define_const): give warning for non constant
+ name. [ruby-core:01287]
+
+Thu Jul 24 01:51:08 2003 GOTOU Yuuzou <gotoyuzo@notwork.org>
+
+ * lib/webrick: imported.
+
+ * MANIFEST: added webrick files.
+
+Thu Jul 24 01:32:04 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * lib/tmpdir.rb (tmpdir): new method. remove TMPDIR.
+ use GetSystemWindowsDirectory(GetSystemDirectory), not GetTempPath.
+
+Thu Jul 24 01:08:43 2003 GOTOU Yuuzou <gotoyuzo@notwork.org>
+
+ * ext/openssl: imported.
+
+Wed Jul 23 23:06:59 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * file.c (DOSISH): better Cygwin support.
+
+Wed Jul 23 19:13:21 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * string.c (rb_str_split_m): the receiver may be empty string.
+
+Wed Jul 23 18:43:00 2003 Masatoshi SEKI <m_seki@mva.biglobe.ne.jp>
+
+ * lib/erb.rb: import erb-2.0.4b1.
+
+Wed Jul 23 18:21:52 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * ext/io/wait: imported.
+
+Wed Jul 23 15:49:01 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * string.c (rb_str_lstrip_bang): strip NUL along with white
+ spaces. [ruby-talk:76659]
+
+ * string.c (rb_str_rstrip_bang): ditto.
+
+Wed Jul 23 14:19:17 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * lib/mkmf.rb (log_src, checking_for, create_header):
+ Logging.message is printf like format.
+
+Wed Jul 23 10:11:15 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * ext/iconv/iconv.c (check_iconv): check if Iconv instance.
+
+ * ext/iconv/iconv.c (iconv_convert): stringify argument.
+
+Tue Jul 22 19:16:40 2003 Tanaka Akira <akr@m17n.org>
+
+ * ext/iconv/iconv.c (iconv_failure_initialize): limit
+ inspect message. [ruby-dev:20785]
+
+ * ext/iconv/iconv.c (rb_str_derive): share with original
+ string if possible. [ruby-dev:20785]
+
+Tue Jul 22 17:22:34 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * variable.c (rb_mod_const_missing): new method. [ruby-core:00441]
+
+ * variable.c (rb_const_get_at): allow "const_missing" hook.
+
+ * variable.c (rb_const_get_0): ditto.
+
+ * eval.c (method_missing): rename from rb_undefined to clarify.
+
+ * eval.c (ruby_finalize_0): update exit status if any of END proc
+ raises SystemExit. [ruby-core:01256]
+
+ * signal.c (rb_trap_exit): wrap rb_eval_cmd
+
+ * eval.c (rb_exec_end_proc): reduce rb_protect().
+
+Tue Jul 22 17:15:57 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * MANIFEST (lib/cgi/session/pstore.rb, lib/yaml/baseemitter.rb):
+ added.
+
+Tue Jul 22 10:52:19 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * lib/tmpdir.rb: remove charcters after "\000" and regularize path.
+
+Tue Jul 22 02:22:45 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * numeric.c (num_equal): should not use rb_equal().
+
+ * string.c (rb_str_equal): should return nil for non string
+ operand to conform comparable convention. [ruby-dev:20759]
+
+Tue Jul 22 00:19:19 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * lib/tmpdir.rb: new library to get temporary directory path,
+ using GetTempPath on Win32 environment.
+
+ * lib/tempfile.rb: now uses tmpdir.rb.
+
+ * lib/cgi/session.rb, ib/drb/unix.rb: ditto.
+
+Mon Jul 21 01:53:43 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * string.c (rb_string_value_cstr): check null byte in the string
+ before retrieving C ptr. accessed via macro StringValueCStr.
+
+ * file.c: use StringValueCStr to retrieve paths to system calls.
+
+ * file.c (sys_fail2): raise error for two operand system calls
+ such as rename, link, symlink. (ruby-bugs PR#1047)
+
+Sun Jul 20 11:03:25 2003 UENO Katsuhiro <katsu@blue.sky.or.jp>
+
+ * ext/zlib/zlib.c (gzfile_read_header): gz->z.input may be nil after
+ finishing reading a gzip header.
+
+Sat Jul 19 22:25:47 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * string.c (rb_str_match2): add warning to "~string".
+ [ruby-list:37751]
+
+ * lib/net/ftp.rb (Net::FTP::open): takes block. suggested by Gavin
+ Sinclair in [ruby-core:01237].
+
+Sat Jul 19 19:03:24 2003 Takaaki Uematsu <uema2x@jcom.home.ne.jp>
+
+ * wince/stdlib.c: add bsearch().
+
+Sat Jul 19 11:27:25 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * lib/xmlrpc: import.
+
+ * eval.c (thgroup_add): should return group for terminated thread
+ case.
+
+ * eval.c (thgroup_add): do not raise ThreadError on terminated
+ thread addition for compatibility. just warning.
+
+Sat Jul 19 04:50:56 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * ext/iconv/charset_alias.rb, ext/iconv/extconf.rb: make wrapper
+ script which maps charset names. [ruby-dev:20625]
+
+ * ext/iconv/iconv.c (charset_map): charset name map.
+
+ * ext/iconv/iconv.c (iconv_dfree): no exception while
+ finalization.
+
+ * ext/iconv/iconv.c (iconv_s_conv): new method Iconv.conv.
+ [ruby-dev:20588]
+
+Sat Jul 19 03:09:18 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * ext/Win32API/lib/win32/registry.rb (Win32::Registry::Error):
+ inherit StandardError instead of SystemCallError.
+
+Sat Jul 19 02:00:39 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (rb_attr): extra calls of method_added. [ruby-talk:76361]
+
+Fri Jul 18 18:44:22 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * lib/mkmf.rb (init_mkmf): clear $INSTALLFILES. [ruby-dev:20727]
+
+Fri Jul 18 17:34:39 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * lib/mkmf.rb (rm_f): use FileUtils.
+
+ * lib/mkmf.rb (modified?): return mtime of the target if
+ it exists and newer than times.
+
+ * lib/mkmf.rb (install_files): add a current directory
+ file even if it does not exist yet.
+
+ * lib/mkmf.rb (configuration): do not add $LDFLAGS to
+ DLDFLAGS.
+
+ * ext/extmk.rb (extmake): check whether Makefile is newer
+ than depend and MANIFEST.
+
+Fri Jul 18 14:57:19 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * win32/win32.c (make_cmdvector): recognize quote within string.
+ based on Nobu's patch ([ruby-win32:450]). [ruby-talk:75853]
+
+Fri Jul 18 13:04:36 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_f_missing): VCALL is called only for LOCAL_ID. no
+ check required.
+
+ * parse.y (primary): pritmary:tFID generates NODE_FCALL.
+ [ruby-dev:20641]
+
+Thu Jul 17 18:50:26 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * re.c (match_captures): rename from "groups".
+
+Thu Jul 17 17:57:32 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_clear_cache_by_class): check both klass and origin.
+
+Thu Jul 17 13:46:25 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (ruby_init): set ruby_running to true after
+ initialization.
+
+Thu Jul 17 13:42:53 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * lib/ftools.rb (File::makedirs): do not handle "//" as a directory.
+
+Wed Jul 16 16:23:58 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_proc_new): call svalue_to_avalue for yield argument.
+
+Wed Jul 16 00:31:00 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_disable_super, rb_enable_super): deprecate.
+
+ * eval.c (thgroup_s_alloc): re-implement group struct.
+
+ * eval.c (thgroup_add): add check for enclose and frozen status.
+
+Tue Jul 15 19:50:49 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (rb_add_method, rb_alias): need to clear cache by
+ ID when method defined in parent class is cached for
+ grand child classes. [ruby-dev:20672]
+
+Tue Jul 15 14:38:21 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * lib/matrix.rb: remove elements conversion to_f, to_i, to_r.
+
+ * lib/cgi/session/pstore.rb: add new file.
+
+Tue Jul 15 03:30:41 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/rubyext.c (syck_mark_emitter): forgot to rb_gc_mark the
+ outgoing IO object.
+
+Sun Jul 13 14:55:36 2003 Koji Arai <jca02266@nifty.ne.jp>
+
+ * process.c (proc_getgroups, proc_setmaxgroups): fix typo.
+
+Sat Jul 12 17:01:28 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * struct.c (struct_entry): add prototype to avoid VC++ warnings.
+
+Sat Jul 12 04:43:57 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/emitter.c: new emitter code.
+
+ * ext/syck/rubyext.c: Emitter class.
+
+ * lib/yaml.rb: Load Syck emitter, if available.
+
+ * lib/yaml/stream.rb: ditto.
+
+ * lib/yaml/baseemitter.rb: underlying class for all emitters.
+
+ * lib/yaml/rubytypes.rb: use BaseEmitter abstraction.
+
+ * lib/yaml/emitter.rb: ditto.
+
+Sat Jul 12 04:23:13 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (rb_undef): need to clear cache for inherited class.
+ (rubicon/builtin/TestModulePrivate.rb:test_undef_method)
+
+Sat Jul 12 01:21:54 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (avalue_to_svalue): typo.
+
+ * eval.c (rb_load): rb_prohibit_interrupt must not underflow.
+
+ * parse.y (NODE_STRTERM, tokadd_string, parse_string): moved
+ string nest level from a static variable to NODE_STRTERM, to
+ preserve it from word to word in %W/%w.
+
+Fri Jul 11 22:37:18 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * configure.in (aix): needs ruby.imp even with gcc.
+ (ruby-bugs:PR#1007)
+
+Fri Jul 11 18:37:37 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * instruby.rb: do not handle directories. [ruby-dev:20613]
+
+Fri Jul 11 16:09:09 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * util.c (ruby_strtod): exp should be less than MDMAXEXPT.
+
+Thu Jul 10 14:42:02 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * math.c (math_log): nan takes a dummy argument on Cygwin 1.5.0.
+
+Wed Jul 9 23:50:46 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * regex.c (mbctab_sjis): 0x80 is not shift jis first byte.
+ [ruby-dev:20516]
+
+Wed Jul 9 15:38:28 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * instruby.rb: do not install shared libraries as man pages.
+
+ * mkconfig.rb: support text-mount on Cygwin.
+
+Wed Jul 9 11:09:57 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * re.c (match_entry): add prototype to avoid VC++ warnings.
+
+Wed Jul 9 03:48:27 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_load): put rb_load_file() in a thread critical
+ section. [ruby-dev:20490]
+
+ * eval.c (compile): put rb_compile_string() in a thread critical
+ section.
+
+Tue Jul 8 02:35:41 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * variable.c (rb_const_get_0): should not warn if constant is not
+ defined. (ruby-bugs-ja PR#509)
+
+ * bignum.c (rb_big2dbl): give a warning on overflow.
+ (ruby-bugs-ja PR#510)
+
+ * util.c (ruby_strtod): change MDMAXEXPT from 511 to 308.
+
+ * pack.c (utf8_to_uv): long is sufficient. LONG_LONG is not
+ required.
+
+Tue Jul 8 01:43:16 2003 Koji Arai <jca02266@nifty.ne.jp>
+
+ * bignum.c (rb_big2str): support 32 bit (without `long long' type)
+ machines. (ruby-bugs-ja PR#512)
+
+Mon Jul 7 10:22:46 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * ext/dbm/extconf.rb (gdbm_compat, qdbm): add check for gdbm_compat
+ and qdbm.
+
+Mon Jul 7 01:34:49 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_call_super): k->super maybe NULL if klass is Kernel.
+ [ruby-dev:20519]
+
+ * gc.c (obj_free): clear method cache when freeing class/module.
+
+Sat Jul 5 23:32:06 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_mod_remove_method): allow "remove_method" to accept
+ multiple arguments.
+
+Sat Jul 5 00:22:59 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * node.h (NEW_NODE): cast arguments to rb_node_newnode().
+
+Fri Jul 4 21:48:44 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * ext/syck/rubyext.c, ext/syck/syck.c, ext/syck/syck.h,
+ ext/syck/token.c: C++ style comments are not allowed.
+ (ruby-bugs:PR#1008)
+
+Thu Jul 3 23:41:30 2003 Tanaka Akira <akr@m17n.org>
+
+ * lib/timeout.rb: add optional exception argument for compatibility
+ function.
+
+Thu Jul 3 14:22:46 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * array.c (rb_values_at): extract common procedure from
+ rb_ary_values_at. follow DRY principle.
+
+ * re.c (match_values_at): values_at should understand ranges.
+
+ * struct.c (rb_struct_values_at): ditto.
+
+ * struct.c (inspect_struct): inspect format changed; add "struct "
+ at the top.
+
+ * sprintf.c (rb_f_sprintf): "%p" specifier for inspect output.
+ (RCR#69)
+
+ * eval.c (rb_mod_undef_method): allow "undef_method" to accept
+ multiple arguments. (RCR#146)
+
+ * lib/timeout.rb: put timeout in Timeout module. (RCR#121)
+ [ruby-talk:61028]
+
+ * re.c (match_groups): new method added. (RCR#139)
+
+ * variable.c (rb_mod_const_of): should exclude constant defined
+ in Object, unless retrieving constants of Object.
+
+Thu Jul 3 12:13:05 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * lib/mkmf.rb (VPATH): convert from Windows form to Unix form on
+ MinGW. This fixes the build with GNU make 3.80-1 for Cygwin.
+
+Wed Jul 2 23:27:34 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * string.c (rb_str_new4): do not allocate new string if original
+ is frozen or already have copy-on-write entry. [ruby-talk:74940]
+
+Wed Jul 2 13:22:39 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * string.c (rb_str_shared_replace): clear flags before copy.
+
+ * string.c (rb_str_replace): ditto.
+
+ * eval.c (rb_yield_0): override visibility mode for module_eval
+ etc. (ruby-bugs-ja PR#505)
+
+Wed Jul 2 11:45:34 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/net/smtp.rb: synchronize document with source code.
+
+ * lib/net/pop.rb: ditto.
+
+Wed Jul 2 11:39:50 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/net/smtp.rb: unify SMTP and SMTPCommand.
+
+ * lib/net/smtp.rb: new exception class SMTPError.
+
+ * lib/net/smtp.rb: new exception class SMTPAuthenticationError.
+
+ * lib/net/smtp.rb: new exception class SMTPServerBusy.
+
+ * lib/net/smtp.rb: new exception class SMTPSyntaxError.
+
+ * lib/net/smtp.rb: new exception class SMTPFatalError.
+
+ * lib/net/smtp.rb: new exception class SMTPUnknownError.
+
+ * lib/net/smtp.rb: change critical section protect algorithm.
+
+ * lib/net/smtp.rb (SMTP#do_start): check authentication args
+ before all.
+
+ * lib/net/smtp.rb: new method send_message (alias send_mail).
+
+ * lib/net/smtp.rb: new method open_message_stream (alias ready).
+
+ * lib/net/pop.rb: POPBadResponse is a POPError.
+
+ * lib/net/pop.rb (POPMail#pop): ban ReadAdapter.
+
+ * lib/net/pop.rb (POPMail#top): ditto.
+
+ * lib/net/pop.rb (POP3Command): change critical section protect
+ algorithm.
+
+ * lib/net/pop.rb (POP3Command#auth): USER and PASS should be one
+ critical block.
+
+ * lib/net/pop.rb (POP3Command#retr): ban `dest' argument using
+ iterator.
+
+ * lib/net/pop.rb (POP3Command#top): ditto.
+
+ * lib/net/protocol.rb: #read_message_to -> #each_message_chunk
+
+ * lib/net/protocol.rb: #D -> #LOG
+
+ * lib/net/protocol.rb: #D_off -> #LOG_off
+
+ * lib/net/protocol.rb: #D_on -> #LOG_on
+
+Wed Jul 2 11:10:47 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/net/http.rb: set old class aliases for backward
+ compatibility. [ruby-talk:74863]
+
+ * lib/net/protocol.rb: ditto.
+
+Wed Jul 2 01:32:40 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * lib/net/pop.rb (Net::POP3#start): typofix.
+
+Tue Jul 1 19:02:12 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * parse.y (rb_intern): should use mbclen instead of mblen.
+
+Tue Jul 1 10:36:19 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * class.c (rb_define_class, rb_define_module): also set constant under
+ Object. [ruby-dev:20445]
+
+ * object.c (boot_defclass): ditto.
+
+ * variable.c (rb_const_get_at, rb_const_get_0, rb_mod_const_at,
+ rb_const_defined, mod_av_set, rb_const_assign): toplevel constants
+ are now under Object, rb_class_tbl remains for GC.
+
+Mon Jun 30 17:53:06 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (mnew): ignore metaclasses have no influence, for rklass.
+ [ruby-talk:74706]
+
+Sun Jun 29 06:59:07 2003 Masatoshi SEKI <m_seki@mva.biglobe.ne.jp>
+
+ * lib/drb/drb.rb, lib/drb/invokemethod.rb: import drb-2.0.4
+ (use LocalJumpError#reason)
+
+Sat Jun 28 12:28:54 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * configure.in (rb_cv_stack_grow_dir): check stack growing direction.
+
+ * eval.c (rb_thread_restore_context): prior configuration macro.
+
+ * gc.c (ruby_stack_length): always return the address of lower edge.
+
+ * gc.c (rb_gc_mark_locations): remove margin. [ruby-dev:20462]
+
+ * gc.c (rb_gc, Init_stack): prior configuration macro.
+
+ * gc.c (Init_stack): add safety margin.
+
+Fri Jun 27 14:41:22 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * string.c (rb_str_split_m): remove white spaces on the head of
+ the last element, when limit is specified. [ruby-talk:74506]
+
+Fri Jun 27 03:24:54 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * io.c (io_fflush): need to check if closed after thread switch.
+ [ruby-dev:20351]
+
+ * io.c (fptr_finalize): ditto.
+
+ * string.c (rb_str_rindex_m): fixed wrong fix. should move backward
+ first only when matching from the end.
+
+Thu Jun 26 21:34:49 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * class.c (class_instance_method_list): get rid of warning about
+ arguement type mismatch, and inline method_list().
+ [ruby-core:01198]
+
+Wed Jun 25 12:52:58 2003 Matthew Dempsky <jivera@flame.org>
+
+ * class.c (rb_generic_class_instance_methods): merge argument
+ check (and warning) into one function; following DRY principle.
+ [ruby-core:01193]
+
+Wed Jun 25 00:14:30 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * variable.c (autoload_delete): should delete Qundef from iv_tbl.
+ (ruby-bugs-ja PR#504)
+
+Tue Jun 24 14:22:41 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * lib/yaml/types.rb: replaced Kernel::Hash reference with Object::Hash
+ from [ruby-talk:74270]
+
+Tue Jun 24 17:59:30 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (rb_yield_0): show yielded block position not only yielding
+ point. [ruby-dev:20441]
+
+Tue Jun 24 16:47:07 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/net/http.rb (HTTPHeader#proxy_basic_auth): missing `@'.
+ Thanks Douglas Koszerek. (ruby-bugs:PR975)
+
+Tue Jun 24 14:31:17 2003 Minero Aoki <aamine@loveruby.net>
+
+ * config.guess: have wrongly returned "alphaev56-unknown-linux-"
+ on Linux/Alpha. [ruby-dev:20434]
+
+Tue Jun 24 04:54:46 2003 Minero Aoki <aamine@loveruby.net>
+
+ * configure.in: always add -mieee for gcc/alpha. [ruby-dev:20429]
+
+Tue Jun 24 02:40:09 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * array.c (rb_ary_unshift_m): need to check number of arguments.
+ [ruby-talk:74189]
+
+Mon Jun 23 23:59:56 2003 Minero Aoki <aamine@loveruby.net>
+
+ * io.c (io_close): missing prototype. [ruby-dev:20422]
+
+ * ext/socket/socket.c (bsock_do_not_rev_lookup_set): ditto.
+
+ * ext/win32ole/win32ole.c (foletype_guid, foletype_progid): ditto.
+
+ * error.c (syserr_initialize): length argument of sprintf() is an
+ int.
+
+Mon Jun 23 23:28:14 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * MANIFEST: add wince files.
+
+ * ext/tk/MANIFEST: add sample/tkmenubutton.rb.
+
+Mon Jun 23 17:40:58 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * dir.c (find_dirsep): get rid of warnings.
+
+ * eval.c (error_print): temporary value might be disposed by GC.
+
+ * hash.c (env_has_value, env_index): should not increment NULL.
+
+ * io.c (io_read, rb_io_sysread): not read when length is 0.
+
+ * io.c (rb_io_reopen): ensure initialized IO.
+
+ * io.c (rb_io_init_copy): sychronize file pointer.
+
+ * io.c (rb_io_s_pipe): make exception proof.
+
+ * string.c (rb_str_rindex_m): Fixnum 0 matched end of string.
+
+Mon Jun 23 16:18:12 2003 Tanaka Akira <akr@m17n.org>
+
+ * io.c (rb_open_file): initialize flags.
+
+ * time.c (time_arg): initialize v[6] even when argc is 10 to
+ avoid valgrind error.
+
+Mon Jun 23 08:24:01 2003 Florian Frank <flori@nixe.ping.de>
+
+ * string.c (rb_str_upto): generate sequence according to "succ"
+ order. formerly check was done by dictionary order.
+ [ruby-talk:74138]
+
+Mon Jun 23 00:27:32 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * string.c (rb_string_value): fill constant empty string along
+ with setting ELTS_SHARED if str->ptr is NULL. [ruby-core:01179]
+
+ * string.c (rb_string_value_ptr): ditto.
+
+ * string.c (rb_check_string_type): ditto.
+
+Sun Jun 22 23:42:20 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * string.c (str_gsub): move END(0) check before mbclen2().
+
+ * string.c (scan_once): reduce END(0) check.
+
+ * io.c (rb_io_initialize): accept fixnum mode.
+
+ * eval.c (error_print): replace strchr() by memchr(), einfo may
+ contain "\0".
+
+ * pack.c (pack_unpack): range check for "@" move; initialize check
+ for "m".
+
+ * error.c (syserr_initialize): avoid buffer overflow.
+
+ * file.c (rb_file_s_readlink): expand buffer until readlink
+ succeed.
+
+Sat Jun 21 23:15:08 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (proc_invoke): should not propagate distination tag if
+ tag is already handled in this level. (ruby-bugs-ja PR#501)
+
+ * object.c (str_to_id): check for empty string before intern.
+ [ruby-talk:74006]
+
+Sat Jun 21 13:56:09 2003 Takaaki Uematsu <uema2x@jcom.home.ne.jp>
+
+ * wince/Makefile.sub: undefine HAVE__SETJMP.
+
+ * wince/resource.rb: include winver.h in wince3.0.
+
+Fri Jun 20 23:28:27 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (proc_invoke): should not propagate TAG_BREAK and
+ TAG_RETURN from orphan Proc object. [ruby-core:01148]
+
+Fri Jun 20 15:04:28 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * defines.h (PATH_ENV): name of PATH environment. [new].
+
+ * defines.h (ENV_IGNORECASE): define for case insensitive platforms
+ to access environment variables.
+
+ * dln.c (dln_find_exe): use PATH_ENV instead of "PATH".
+
+ * hash.c (env_delete, rb_f_getenv, env_fetch, rb_env_path_tainted,
+ env_aset): ditto.
+
+ * ruby.c (proc_options): ditto.
+
+Fri Jun 20 03:09:21 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * parse.y (new_yield): distinguish "yield 1,2" and "yield [1,2]".
+ [ruby-dev:20360]
+
+ * eval.c (rb_eval): support new_yield() change.
+
+ * variable.c (rb_const_get_0): warn for Foo::BAR when BAR is a
+ toplevel constant (i.e. a constant defined under Object).
+ [ruby-list:36935]
+
+ * parse.y (no_blockarg): separate no block argument check and
+ ret_args argument processing.
+
+Fri Jun 20 00:45:19 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>
+
+ * lib/csv.rb: import csv module.
+
+Thu Jun 19 22:51:41 2003 Masatoshi SEKI <m_seki@mva.biglobe.ne.jp>
+
+ * lib/drb.rb, lib/drb/drb.rb, lib/drb/eq.rb,
+ lib/drb/extserv.rb, lib/drb/extservm.rb, lib/drb/gw.rb,
+ lib/drb/invokemethod.rb, lib/drb/observer.rb,
+ lib/drb/timeridconv.rb, lib/drb/unix.rb: import drb-2.0.4b3
+
+Thu Jun 19 16:13:54 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * rubytest.rb: add library path to include standard libraries.
+
+Thu Jun 19 13:13:10 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * hash.c (env_delete, rb_f_getenv, env_fetch): case insensitive to
+ access environment variables on DOSISH platforms.
+
+Thu Jun 19 00:51:47 2003 NAKAMURA, Hiroshi <nakahiro@sarion.co.jp>
+
+ * range.c (rb_range_beg_len): out_of_range check after adjusting
+ end point. [ruby-dev:20370]
+
+Wed Jun 18 23:59:11 2003 Guy Decoux <ts@moulon.inra.fr>
+
+ * parse.y (call_args): the first argument to arg_cancat() should
+ be NODE_LIST. [ruby-core:01151]
+
+Wed Jun 18 23:41:27 2003 Marc Cartright <marc@isri.unlv.edu>
+
+ * ext/zlib/zlib.c (zstream_run): In a particular situation,
+ deflate/inflate will return Z_BUF_ERROR, even though another call
+ is required by the zlib library.
+
+Wed Jun 18 13:50:06 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_eval): should dispatch based on ID type.
+
+Wed Jun 18 12:53:42 2003 Minero Aoki <aamine@loveruby.net>
+
+ * eval.c (rb_yield_0): should restore scope_vmode during yield.
+ [ruby-dev:20361]
+
+Wed Jun 18 01:13:36 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/rubyext.c (rb_syck_load_handler): merge key implemented.
+
+ * ext/syck/rubyext.c (transfer_find_i): removed use of String#=~ in favor
+ of Regexp#match.
+
+ * lib/yaml.rb: YAML::try_implicit returns.
+
+ * lib/yaml/rubytypes.rb: Regexps added for type matching.
+
+ * lib/yaml/emitter.rb: fix String + nil error.
+
+Tue Jun 17 17:01:08 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/gram.c: added grammar for certain empty sequence entries.
+
+ * ext/syck/handler.c, ext/syck/syck.c, ext/syck/syck.h: track bad anchors.
+
+ * ext/syck/token.c: added pause token, tag possible circular references.
+
+ * lib/yaml/rubytypes.rb: parsing YMD time as Date instance.
+
+ * ext/syck/rubyext.c: ditto. DomainType, PrivateType, BadAlias classes.
+
+Tue Jun 17 21:28:27 2003 Ariff Abdullah <skywizard@time.net.my>
+
+ * win32/win32.c (rb_w32_opendir): need to set errno. [ruby-talk:73761]
+
+Mon Jun 16 19:01:25 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c: remove rb_cBlock.
+
+Mon Jun 16 18:06:33 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * numeric.c (rb_fix2uint): renamed from rb_fix2int on IA64.
+
+Mon Jun 16 17:02:57 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (proc_invoke): format the message for localjump_error().
+
+Mon Jun 16 16:23:56 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * ext/dl/dl.c (rb_dl_callback): use rb_block_proc() instead of
+ rb_block_new().
+
+ * ext/win32ole/win32ole.c (ev_on_event): ditto.
+
+Mon Jun 16 16:06:47 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (proc_alloc): re-unification of Block and Proc. Block
+ class is no longer available.
+
+Mon Jun 16 14:43:14 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * bcc32/Makefile.sub: undefine HAVE_GETGROUPS.
+
+Sat Jun 14 16:58:41 2003 Guy Decoux <ts@moulon.inra.fr>
+
+ * regex.c (calculate_must_string): should handle option_set
+ properly. [ruby-talk:73481]
+
+ * regex.c (re_compile_fastmap): a bug in flag manipulation.
+ [ruby-talk:73549]
+
+Sat Jun 14 17:59:59 2003 Guy Decoux <ts@moulon.inra.fr>
+
+ * eval.c (method_arity): should handle NODE_BMETHOD and
+ NODE_DMETHOD. [ruby-core:01138]
+
+Fri Jun 13 09:24:39 2003 Shugo Maeda <shugo@ruby-lang.org>
+
+ * lib/net/ftp.rb (storebinary): seek correctly. Thanks, William Webber.
+
+ * lib/net/ftp.rb (putbinaryfile): rescue FTPPermError.
+
+Tue Jun 10 14:26:30 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/token.c: preserve newlines prepended to a block.
+
+ * ext/syck/implicit.c (syck_match_implicit): added !merge and !default.
+
+ * lib/yaml/constants.rb: remove '\z' escape.
+
+ * lib/yaml/emitter.rb: ensure reset of @seq_map shortcut flag.
+
+ * lib/yaml/encoding.rb: remove Unicode translation methods.
+
+ * lib/yaml/rubytypes.rb: improved round-tripping of Strings.
+ [ruby-core:1134]
+
+Tue Jun 10 01:07:54 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * lib/irb.rb (IRB::Irb::eval_input): warn and exit if $SAFE >=3
+ after input evaluation.
+
+ * lib/irb.rb (IRB::Irb::eval_input): untaint input string. now
+ irb works for levels 1 and 2.
+
+Mon Jun 9 19:02:33 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * configure.in: checks presence of grp.h and setgroups().
+
+ * process.c (proc_getgroups, proc_setgroups): raise
+ NotImplementedError unless available. [ruby-talk:73014]
+
+Sun Jun 8 13:37:21 2003 Takaaki Uematsu <uema2x@jcom.home.ne.jp>
+
+ * wince/setup.mak: set SUBSYSTEM in each platform.
+
+ * wince/stdlib.c: fix mblen() bug.
+
+Sat Jun 7 22:22:03 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * ext/syck/rubyext.c (syck_loader_transfer): should not use
+ rb_cProc directly, since type_proc may be Proc, Block, or
+ Method.
+
+ * parse.y (value_expr0): class and module statements should not be
+ warned for "void value expression". [ruby-talk:72989]
+
+Sat Jun 7 01:46:41 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * gc.c (add_final): should determine type by respond_to?
+
+ * gc.c (define_final): ditto.
+
+ * io.c (rb_io_ctl): should not depend on respond_to?
+
+ * range.c (range_step): rb_check_string_type().
+
+Fri Jun 6 20:29:14 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (error_print): needs to be exception proof.
+
+ * eval.c (error_handle, rb_longjmp): bails out when exception
+ reentered. (ruby-bugs-ja:PR#487), [ruby-core:01119],
+ [ruby-core:01122]
+
+ * eval.c (Init_Proc): pre-allocates critical error objects.
+
+Fri Jun 6 20:29:14 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * parse.y (cmd_brace_block, do_block, brace_block): initialize block
+ variables at the beginning of the block. [ruby-talk:72521]
+
+Fri Jun 6 18:49:11 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * process.c (proc_setgroups): new functions.
+
+Fri Jun 6 18:33:27 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * gc.c (define_final): eliminate rb_f_lambda() call.
+
+ * class.c (rb_scan_args): ditto.
+
+ * signal.c (sig_trap): ditto.
+
+ * hash.c (rb_hash_initialize): ditto.
+
+ * variable.c (rb_f_trace_var): ditto.
+
+ * ext/dl/dl.c (rb_dl_callback): ditto.
+
+ * ext/win32ole/win32ole.c (ev_on_event): ditto.
+
+Fri Jun 6 16:10:01 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/net/http.rb: define Net::HTTPResponse#to_ary for backward
+ compatibility. [ruby-talk:72927]
+
+ * lib/net/protocol.rb: add warning.
+
+Fri Jun 6 13:30:57 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (ruby_cleanup): $SAFE is turned off in the finalization.
+ Each END proc should preserve its own $SAFE level. [ruby-core:01119]
+
+ * marshal.c (marshal_load): remove unused variable "hash".
+ [ruby-core:01120]
+
+ * hash.c (env_str_new): freeze strings from ENV. [ruby-talk:72860]
+
+ * array.c (rb_ary_first): optional argument to retrieve first n
+ elements.
+
+ * array.c (rb_ary_last): optional argument to retrieve last n
+ elements.
+
+Thu Jun 5 21:31:55 2003 Takaaki Uematsu <uema2x@jcom.home.ne.jp>
+
+ * wince/stdlib.c: add mblen().
+
+Thu Jun 5 18:33:46 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * ext/curses/curses.c (window_s_allocate,curses_finalize):
+ avoid VC++ warnings.
+
+Thu Jun 5 17:44:11 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/rubyext.c (syck_parser_mark): was a bit heavy on the GC.
+
+ * lib/yaml.rb (YAML::transfer): added.
+
+Thu Jun 5 16:11:50 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * bcc32/Makefile.sub, win32/Makefile.sub, wince/Makefile.sub
+ (MISSING): link with missing/erf.c.
+
+ * missing.h (erf, erfc): fix prototype.
+
+ * missing/erf.c: new. [ruby-list:37753]
+
+Thu Jun 5 15:09:06 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * math.c (math_erf,math_erfc): new function. [ruby-list:37753]
+
+Thu Jun 5 14:49:43 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/rubyext.c: using GC nodes caused segfault. [ruby-core:1071]
+
+Thu Jun 5 13:48:57 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/token.c: directives choked on a period.
+
+ * ext/syck/gram.y: anchors work above a collection. [ruby-core:1071]
+
+ * ext/syck/handler.c, ext/syck/syck.c: ensure a fresh strtable between
+ parser iterations.
+
+Wed Jun 4 12:06:59 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (ruby_finalize): no longer need to turn off $DEBUG in the
+ finalizer. (ruby-bugs-ja PR#473)
+
+Tue Jun 3 22:20:49 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_call_super): should search superclass method based on
+ orig_func, not last_func.
+
+Tue Jun 3 09:59:27 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_call_super): inheritance line adjustment moved from
+ rb_call(). [ruby-core:01113]
+
+ * eval.c (rb_eval): use rb_call_super() to follow DRY principle.
+
+Mon Jun 2 02:20:52 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * array.c (push_values_at): Array#values_at should work with
+ ranges too.
+
+ * range.c (rb_range_beg_len): length calculation was wrong.
+
+ * eval.c (rb_call): should set T_ICLASS in the frame->last_class.
+ [ruby-core:01110]
+
+Sun Jun 1 21:50:01 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * configure.in: should not use def file, use ld with
+ --export-all-symbols option on Cygwin/MinGW.
+
+ * defines.h: ditto.
+
+ * cygwin/GNUmakefile.in: ditto.
+
+ * ext/digest/defs.h: avoid warnings on Cygwin.
+
+Sun Jun 01 13:33:49 2003 Takaaki Uematsu <uema2x@jcom.home.ne.jp>
+
+ * wince/string_wce.c: add strpbrk() for hpcpro support.
+
+ * wince/setup.mak: add hpcpro(CE2.11) & armv4t(CE.NET) support.
+
+ * wince/resource.rb: ditto.
+
+ * wince/Makefile.sub: ditto.
+
+Sun Jun 1 10:38:28 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * variable.c (rb_autoload_load): autoloaded constants under a module
+ belong to the module. [ruby-core:01094], [ruby-dev:20309]
+
+Sat May 31 04:36:54 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * parse.y (rb_intern): should handle multibyte name.
+
+Fri May 30 23:18:01 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/rubyext.c (rb_syck_mktime): seconds calculated wrong.
+
+ * ext/syck/gram.c: flexibility to anchors and transfer methods on
+ collections.
+
+ * ext/syck/token.c: hex escapes.
+
+ * lib/yaml/basenode.rb: YamlNode references changed to YAML::BaseNode.
+
+Fri May 30 22:28:04 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * numeric.c (rb_num2uint, rb_fix2int): new function to convert
+ values over INT_MAX. [ruby-core:01099]
+
+ * ruby.h (NUM2UINT, FIX2INT): ditto.
+
+Fri May 30 15:01:05 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/token.c: preserve any indentation past an explicit
+ indentation.
+
+Fri May 30 14:55:44 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (rb_Array): exclude Kernel#to_a instead of Object#to_a.
+ (ruby-bugs-ja:PR#483)
+
+ * lib/optparse.rb (OptionParser::Switch#parse_arg): not splat.
+
+ * lib/optparse.rb (OptionParser::Switch#conv_arg): splat if no
+ conversion supplied.
+
+ * lib/optparse.rb (OptionParser::Switch::PlacedArgument#parse):
+ override next switch after argument conversion.
+
+Fri May 30 14:41:34 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/handler.c, ext/syck/syck.h: removed syck_fold_format().
+
+ * ext/syck/gram.c: flexibility for aliases and anchors.
+
+ * ext/syck/token.c: folding now handled in the tokenizer.
+
+Fri May 30 06:21:18 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * variable.c (rb_autoload_load): should delete autoloaded
+ symbol itself before load. [ruby-core:01097]
+
+ * variable.c (rb_mod_remove_const): must not return Qundef.
+
+Thu May 29 14:59:10 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * win32/win32.c (_CRTIMP): redefine _CRTIMP on MinGW.
+
+ * configure.in: remove '-D__USE_CRTIMP' from XCFLAGS on MinGW.
+
+ * win32/win32.c (NtMakeCmdVector): handle quotes only if not instring.
+
+Thu May 29 09:11:01 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (ev_const_defined, ev_const_get), variable.c
+ (rb_const_get_at, rb_const_get, rb_mod_remove_const): use Qundef
+ as autoload marker. [ruby-dev:18103], [ruby-dev:18184]
+
+ * eval.c (rb_mod_autoload, rb_mod_autoload_p): new method;
+ Module#autoload, Module#autoload?.
+
+ * variable.c (rb_autoload, rb_autoload_load, rb_autoload_p):
+ manage autoload constants per classes/modules.
+
+ * variable.c (rb_const_defined_at, rb_const_defined): return false
+ for autoloading constants.
+
+ * class.c (rb_define_class, rb_define_module), eval.c (rb_eval),
+ variable.c (rb_mod_const_at, rb_const_assign): removed autoload
+ stuff.
+
+ * intern.h: prototypes; rb_autoload, rb_autoload_load,
+ rb_autoload_p.
+
+ * lib/optparse.rb (OptionParser::Switch::PlacedArgument::parse):
+ do not treat unmatched argument as an option.
+
+Wed May 28 08:44:26 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * io.c (rb_f_syscall): type dispatch should be based on
+ rb_check_string_type(), not FIXNUM_P(), because values may be a
+ bignum. [ruby-talk:72257]
+
+Tue May 27 20:33:18 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c, util.c: removed duplicated includes/defines.
+
+ * ext/socket/socket.c (sock_addrinfo): get rid of SEGV at NULL ptr
+ String. increase buffer size for 64bit platforms.
+
+Tue May 27 02:34:14 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_call0): should pass the current klass value to
+ block_invoke, which may be called via "super". [ruby-core:01077]
+
+ * eval.c (block_invoke): now takes 4th argument "klass".
+
+ * eval.c (block_alloc): should propagate BLOCK_PROC to
+ ruby_block.
+
+Mon May 26 23:51:38 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * marshal.c (r_object0): should not use "yield" method, use "call"
+ instead. (ruby-bugs-ja PR#476)
+
+Mon May 26 21:39:46 2003 MoonWolf <moonwolf@moonwolf.com>
+
+ * lib/mkmf.rb, lib/optparse.rb, lib/tracer.rb: use Method#to_block
+ instead of deprecated Method#to_proc. (ruby-bugs-ja:PR#477)
+
+Mon May 26 21:21:20 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * lib/optparse.rb (OptionParser::Switch::parse,
+ OptionParser::order): use {Block,Proc}#call instead of deprecated
+ #yield.
+
+Mon May 26 16:39:10 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (Init_Proc): Block/Proc separation. [huge change]
+
+ * eval.c (block_arity): returns exact arity number for Procs out
+ of methods. also gives 1 for {|a|..}.
+
+ * string.c (rb_str_match): revert use of String#index for
+ invocation like string =~ string.
+
+ * eval.c (rb_Array): move Object#to_a exclusion hack from
+ splat_value(). need to be in eval.c for a while.
+
+Sun May 25 23:48:21 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * bignum.c (rb_quad_pack): should negate negative bignum.
+ (ruby-bugs-ja:PR#474)
+
+Sun May 25 03:27:25 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/net/smtp.rb: support LOGIN authentication, based on
+ the patch by Kazuhiko Izawa. [ruby-talk:78981]
+
+Sat May 24 18:19:51 2003 Takaaki Uematsu <uema2x@jcom.home.ne.jp>
+
+ * wince/Makefile.sub: add eMbedded Visual C++ 4.0 support.
+
+ * wince/resource.rb: ditto.
+
+ * wince/setup.mak: ditto.
+
+ * wince/configure.bat: ditto.
+
+ * wince/mkexports.rb: delete japanese comments.
+
+Fri May 23 18:34:05 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (rb_longjmp): get rid of reentering while debug warning.
+ (ruby-bugs-ja:PR473)
+
+Fri May 23 15:16:16 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * pack.c (pack_unpack): sign-extend if sizeof long is bigger than
+ 32. (ruby-bugs-ja:PR#472)
+
+Fri May 23 14:19:29 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (ruby_finalize): turn off ruby_debug flag before calling
+ at_exit procs and finalizers. (ruby-bugs-ja:PR473)
+
+ * ext/tcltklib/tcltklib.c (lib_mainloop_core): OK to block if
+ there's no other thread. (ruby-bugs:PR#861)
+
+Thu May 22 18:07:46 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/token.c: single- and double-quoted root-level fix.
+
+ * lib/yaml.rb (YAML::object_maker): can create object attributes (such as
+ found in Exception class)
+
+ * lib/yaml/rubytypes.rb: roundtripping of Exception and subclasses.
+
+Fri May 23 01:26:26 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * object.c (rb_obj_clone): defer copying freezing state after
+ calling initialize_copy(). [ruby-dev:20276]
+
+Thu May 22 17:12:10 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * gc.c (run_final): use rb_thread_critical instead of DEFER_INTS.
+ [ruby-dev:20272]
+
+ * marshal.c: try to make ArgumentError and TypeError consistent.
+ [ruby-core:01068]
+
+Thu May 22 15:46:37 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_define_alloc_func): need not to disable
+ rb_call_super() for allocation functions. [ruby-core:1065]
+
+Thu May 22 06:21:33 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/rubyext.c (rb_syck_err_handler): raise ArgumentError on
+ malformed YAML.
+
+ * lib/yaml/rubytypes.rb: String#to_yaml was missing space indicators at
+ the end of a line.
+
+Thu May 22 05:43:24 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/rubyext.c (syck_parser_load): root-level false was returning
+ nil.
+
+ * ext/syck/token.c: root-level transfer method bug.
+
+ * ext/syck/gram.c: root-level empty gave a parse error.
+
+ * lib/yaml/rubytypes.rb: Symbol#to_yaml generating method call error.
+
+Thu May 22 02:46:38 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (rb_eval): splat NODE_RESTARY. [ruby-dev:20268]
+
+ * eval.c (rb_thread_fd_close): raise for writing threads.
+ [ruby-dev:20269]
+
+ * io.c (rb_io_close, io_reopen): ditto.
+
+ * io.c (io_reopen): keep stdio objects for stdin, stdout,
+ and stderr. [ruby-dev:19442]
+
+Thu May 22 01:11:15 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * parse.y (strings, word_list): must create new instance always.
+ http://yowaken.dip.jp/tdiary/20030521.html#p02
+
+ * parse.y (yylex): slight optimization.
+
+Wed May 21 23:07:08 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * error.c (rb_sys_fail): should not specify errno explicitly.
+ [ruby-dev:20264]
+
+Wed May 21 20:51:47 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * Makefile.in, bcc32/Makefile.sub, win32/Makefile.sub,
+ wince/Makefile.sub: update dependencies.
+
+Wed May 21 17:44:16 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * error.c (syserr_initialize): prohibit specifying errno for
+ subclasses of SystemCallError. in addition, if initialize is
+ called for SystenCallError instance, its class be changed.
+ [ruby-dev:20257]
+
+ * gc.c (run_final): to protect thread context switch, finalizers
+ are wrapped in DEFER_INTS/ENABLE_INTS.
+
+Wed May 21 13:26:08 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * lib/optparse.rb: get rid of warnings.
+
+Tue May 20 18:59:54 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_thread_save_context): prohibit rb_gc_force_recycle()
+ on thread saved ruby_dyna_vars. [ruby-dev:20236]
+
+Tue May 20 17:39:15 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * object.c (init_copy): call initialize_copy at the end of copy
+ process.
+
+Tue May 20 17:15:55 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * error.c (syserr_initialize): use Errno constants as default
+ errno for subclasses. [ruby-dev:20241]
+
+Tue May 20 15:26:25 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * st.h: define ST_DATA_T_DEFINED for portability.
+
+ * ext/syck/syck.h: add typedef, st_data_t for Ruby 1.6.
+
+ * ext/syck/syck.c (syck_st_free_nodes): return int.
+
+ * ext/syck/syck.c (syck_add_sym): cast the data to st_data_t
+ to avoid error on bcc32.
+
+ * ext/syck/syck.c (syck_lookup_sym): ditto.
+
+ * ext/syck/syck.c (syck_free_parser): NULL is not integer.
+
+Tue May 20 13:29:04 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * win32/win32.c (kill): set errno after calling raise().
+
+Tue May 20 10:51:26 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (rb_f_missing): create exception instance by ordinal
+ method. [ruby-dev:20033]
+
+ * error.c (rb_name_error, rb_sys_fail): ditto.
+
+ * error.c (exc_to_s, exit_status, name_err_name,
+ nometh_err_args, syserr_errno, syserr_eqq): access
+ attributes.
+
+ * error.c (name_err_initialize, nometh_err_initialize,
+ syserr_initialize): initialize attributes.
+
+Tue May 20 10:26:56 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_yield_0): give warning for multiple values for a
+ block parameter.
+
+ * eval.c (rb_yield_values): a function to yield multiple values.
+
+ * array.c (sort_1): use rb_yield_values.
+
+ * enum.c (min_ii, max_ii): ditto.
+
+ * hash.c (rb_hash_update_block_i, delete_if_i, select_i,
+ each_pair_i, env_each, env_reject_bang, env_select,
+ env_update_i): ditto.
+
+ * struct.c (rb_struct_each_pair): ditto.
+
+ * eval.c (top_include): should include module in the current self,
+ not ruby_top_self. [ruby-dev:20198]
+
+ * eval.c (top_include): stop inclusion to ruby_wrapper; give
+ warning.
+
+Mon May 19 18:54:30 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/token.c, ext/syck/implicit.c: expanded character set to
+ allow UTF-8, other Ruby encodings.
+
+Mon May 19 16:47:00 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/syck.c, ext/syck/syck.h, ext/syck/token.c, ext/syck/gram.c:
+ count line numbers only if line pointer has increased.
+
+Tue May 20 00:45:40 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * dir.c (push_braces): do not push_braces() unless rbrace is found.
+ (ruby-bugs-ja:PR#469)
+
+Tue May 20 00:09:41 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * ext/pty/pty.c (pty_finalize_syswait): join (using Thread#value)
+ before detach pid. [ruby-talk:71519]
+
+Mon May 19 23:02:10 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (PUSH_FRAME): save outer ruby_block. [ruby-list:37677],
+ [ruby-dev:20202]
+
+ * eval.c (BEGIN_CALLARGS): restore outer block by using
+ ruby_block->outer.
+
+ * eval.c (block_pass): do not alter block->prev, but block->outer.
+
+ * array.c (get_inspect_tbl): warning on wrong condition.
+
+Mon May 19 16:13:57 2003 Minero Aoki <aamine@loveruby.net>
+
+ * class.c: add #include "version.h".
+
+ * hash.c: ditto.
+
+ * string.c: ditto.
+
+Mon May 19 15:33:27 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (localjump_xvalue): renamed exitstatus to exit_value
+ since it's not exit "status" after all.
+
+ * eval.c (localjump_error): add reason to LocalJumpError.
+
+ * compar.c (rb_cmpint): raise error via rb_cmperr(), if cmp value
+ is nil. now take new 2 arguments.
+
+ * time.c (time_cmp): 2003-05-16 fix was incomplete.
+ (ruby-bugs-ja:PR#458)
+
+Mon May 19 14:42:50 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * object.c (rb_mod_cmp): stupid comparison fixed.
+
+ * io.c (Init_IO): ARGF.path added (alias to ARGF.filename).
+ [ruby-dev:20197]
+
+Mon May 19 13:58:03 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * object.c (init_copy): rename copy_object as initialize_copy,
+ since it works as copy constructor.
+
+ * eval.c (rb_add_method): initialize_copy should always be
+ private, like initialize.
+
+Mon May 19 13:51:50 2003 Minero Aoki <aamine@loveruby.net>
+
+ * re.c (rb_reg_quote): \n \r \f \v quoting was wrong.
+ [ruby-dev:20203]
+
+ * re.c (rb_reg_quote): rb_reg_quote(" ") should be "\\ ", not
+ "\\s".
+
+Mon May 19 08:08:51 2003 Tadayoshi Funaba <tadf@dotrb.org>
+
+ * lib/date.rb: use warn() instead of $stderr.puts().
+
+ * sample/cal.rb: ditto.
+
+Sat May 17 12:02:25 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * array.c (get_inspect_tbl): check whether inspect_tbl value is a
+ valid array. (ruby-bugs-ja PR#65)
+
+ * array.c (inspect_ensure,rb_protect_inspect,rb_inspecting_p):
+ use get_inspect_tbl().
+
+Sat May 17 11:50:26 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_f_abort): call exit(1) if exception is raised. This
+ patch was made by Nobuyoshi Nakada <nobu.nokada@softhome.net> on
+ 2002-05-30. (ruby-bugs-ja PR#236)
+
+ * signal.c: disable Ruby's interrupt handler at the beginning.
+ (ruby-bugs-ja PR#236)
+
+Sat May 17 02:17:42 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * lib/rational.rb (Integer::denominator): fixed typo.
+ (ruby-bugs-ja:PR#466)
+
+Sat May 17 00:18:11 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * ext/socket/socket.c (ruby_connect): connect() after EINPROGRESS
+ returns EINVAL on some platforms, need to check true error
+ status. [ruby-core:01037]
+
+Sat May 17 00:21:51 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * object.c (rb_class_allocate_instance): singleton class check
+ moved to rb_obj_alloc(). (ruby-bugs-ja PR#345)
+
+Fri May 16 23:55:50 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * re.c (rb_reg_quote): should escape white space characters,
+ \t, \f, \n, \r. (ruby-bugs-ja PR#231)
+
+Fri May 16 12:40:40 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (block_pass): chain previous block to the pushing block.
+ [ruby-list:37677]
+
+ * time.c (time_cmp): does not compare with numbers for
+ interchangeability. (ruby-bugs-ja:PR#458)
+
+Thu May 15 21:55:54 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/gram.c: fixes to one-line documents and end of stream
+ documents.
+
+ * ext/syck/syck.c, ext/syck/syck.h: add root_on_error to parser
+ struct, specifying the symbol to be returned on a parse error.
+
+Thu May 15 18:44:31 2003 Tanaka Akira <akr@m17n.org>
+
+ * lib/open-uri.rb (OpenURI::Redirect#initialize): call super to
+ initialize mesg.
+
+ * lib/open-uri.rb (OpenURI::Meta#charset): call block to guess charset
+ if block is given and charset is not given.
+
+Thu May 15 16:55:16 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * object.c (rb_mod_le): returns nil if two classes/modules are not
+ in class-superclass relationship.
+
+ * object.c (rb_mod_cmp): uses new rb_mod_le() behavior.
+
+Thu May 15 07:45:30 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/rubyext.c, ext/syck/implicit.c: timestamp repairs to
+ timezone and milliseconds.
+
+ * ext/syck/syck.c (syck_parser_reset_levels): duplicate string literal
+ to avoid warning.
+
+Thu May 15 13:26:48 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * class.c (rb_class_instance_methods): default will be changed in
+ 1.8.1.
+
+ * io.c (set_stdio): better message.
+
+Thu May 15 13:18:11 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * io.c (set_stdio): $stdin, $stdout, $stderr now became read-only.
+
+ * variable.c (readonly_setter): message changed.
+
+Thu May 15 09:50:51 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * ext/syck/syck.c (syck_parser_pop_level): add prototype.
+
+ * ext/syck/syck.c (syck_strndup): should return value.
+
+Thu May 15 09:32:25 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * win32/win32.c (kill): fix typo and add signal 0 support.
+
+Wed May 14 20:09:26 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/gram.c: sequence-in-map shortcut, transfer methods on
+ sequence-in-sequence, memory leak in mapping merge.
+
+ * ext/syck/syck.c: memory leak in domain anchoring.
+
+ * lib/yaml/rubytypes.rb, lib/yaml/types.rb: eliminated 1.6.x code.
+
+Wed May 14 19:56:43 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * ext/syck/rubyext.c: add prototypes to avoid VC++ warnings.
+
+Wed May 14 12:23:46 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/net/http.rb (Net::HTTP#start): should check whether HTTP
+ session is opened before finishing. (ruby-bugs-ja:PR#463)
+
+Wed May 14 09:12:55 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/net/http.rb: reduce warning. (ruby-bugs-ja:PR#462)
+
+Tue May 13 22:31:04 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * lib/yaml/rubytypes.rb, lib/yaml/types.rb: using Object#object_id
+ rather than deprecated Object#id.
+
+ * ext/syck/token.c: changed ASCII escapes to octal notation.
+
+ * ext/Setup*: added entries for static linking of Syck extension.
+
+Tue May 13 20:31:58 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * configure.in: add '--Wl,--enable-auto-import' to DLDFLAGS
+ on Cygwin/MinGW.
+
+ * configure.in: add '-D__USE_CRTIMP' to XCFLAGS on MinGW.
+
+ * ext/syck/handler.c: add proper casts.
+
+ * ext/syck/syck.c: ditto.
+
+Tue May 13 17:58:08 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * configure.in, bcc32/Makefile.sub, win32/Makefile.sub: define
+ HAVE_FSYNC.
+
+ * win32/win32.h (fsync): define as _commit().
+
+Tue May 13 15:35:35 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * regex.c (re_match_exec): \Z changed to be consistent with new $
+ (endbuf) behavior.
+
+Tue May 13 14:48:07 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (error_pos): use $deferr for output instead of stderr
+ directly.
+
+ * eval.c (error_print,error_handle,rb_longjmp,rb_thread_schedule):
+ ditto.
+
+Tue May 13 06:34:19 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * lib/yaml/rubytypes.rb: object and struct loading
+
+ * lib/yaml.rb: YAML::detect_implicit will discover typing for a Ruby
+ string
+
+ * ext/syck/: Fixed portable comments, misuse of NULL and methods without
+ return VALUEs.
+
+Mon May 12 18:08:21 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * io.c (Init_IO): new variable $deferr which is default output
+ port of error messages.
+
+ * io.c (rb_warn_m): new method "warn". [new]
+
+ * error.c (warn_print): use $deferr.
+
+ * error.c (rb_bug): ditto.
+
+ * error.c (err_append): ditto.
+
+Sun May 11 13:50:12 2003 Tanaka Akira <akr@m17n.org>
+
+ * lib/pp.rb: refine to_s test.
+
+ * lib/pp.rb (PP::ObjectMixin#pretty_print): refine to_s handling.
+
+Sun May 11 06:32:13 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/implicit.c, ext/syck/rubyext.c: transfer methods applied to
+ native loading
+
+ * ext/syck/token.c: fix for transfer methods on same indentation as nested
+ mapping
+
+ * lib/yaml/rubytypes.rb: all type names in lowercase
+
+Sat May 10 19:55:18 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ext/syck/gram.c ext/syck/handler.c ext/syck/implicit.c
+ ext/syck/node.c ext/syck/rubyext.c ext/syck/syck.c
+ ext/syck/syck.h ext/syck/token.c: updated to Syck 0.27
+
+ * lib/yaml/loader.rb: new YAML::Loader class
+
+ * lib/yaml.rb: loading of type families leverages YAML::DefaultLoader
+
+Sat May 10 19:00:08 2003 Takaaki Uematsu <uema2x@jcom.home.ne.jp>
+
+ * wince/string.c: file removed.
+
+ * wince/stdlib.c: file added.
+
+Sat May 10 16:17:02 2003 Shugo Maeda <shugo@ruby-lang.org>
+
+ * lib/net/imap.rb (decode_utf7): new method.
+
+ * lib/net/imap.rb (encode_utf7): new method.
+
+Fri May 9 21:25:50 2003 why the lucky stiff <ruby-cvs@whytheluckystiff.net>
+
+ * ruby/ext/syck, ruby/lib/yaml: Initial checkin of YAML substances.
+
+Fri May 9 16:38:30 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * io.c (rb_io_reopen): It should be possible to reopen closed IO.
+ [ruby-talk:70941]
+
+ * io.c (rb_io_reopen): inherit original file mode unless specified.
+
+Thu May 8 18:44:09 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * gc.c (rb_gc): check odd alignment stack on m68k machines.
+
+Thu May 8 12:56:04 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * compar.c (rb_cmperr): raise comparison failure.
+
+ * intern.h: prototype; rb_cmperr
+
+ * numeric.c (flo_gt, flo_ge, flo_lt, flo_le, fix_gt, fix_ge,
+ fix_lt, fix_le): should fail unless the argument is comparable.
+ (ruby-bugs-ja:PR#456)
+
+ * numeric.c (int_upto, int_downto): should fail unless the
+ argument is comparable. (ruby-bugs-ja:PR#454)
+
+Wed May 7 13:30:11 2003 Masahiro TANAKA <masa@ir.isas.ac.jp>
+
+ * numeric.c (num_step): better error treatment of float values.
+
+Tue May 6 17:51:54 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/net/pop.rb: rename method: POP3#mail_size -> n_mails
+
+ * lib/net/pop.rb: rename method: POP3#bytes -> n_bytes
+
+Tue May 6 17:21:01 2003 Minero Aoki <aamine@loveruby.net>
+
+ * ext/bigdecimal/.cvsignore: new file.
+
+ * ext/zlib/.cvsignore: new file.
+
+Tue May 6 14:39:36 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * object.c (rb_obj_methods): list singleton methods if recur
+ argument is false; list all methods otherwise.
+
+Mon May 5 21:19:25 2003 Koji Arai <jca02266@nifty.ne.jp>
+
+ * ext/gdbm/gdbm.c (fgdbm_values_at): new method to replace
+ select(index..).
+
+ * ext/sdbm/init.c (fsdbm_values_at): ditto.
+
+ * ext/dbm/dbm.c (fdbm_values_at): ditto.
+
+ * ext/dbm/dbm.c (DBM::VERSION): defined.
+
+ * ext/gdbm/testgdbm.rb: replace select with values_at.
+
+ * ext/sdbm/testsdbm.rb: ditto.
+
+ * ext/dbm/testdbm.rb: ditto.
+
+ * ext/dbm/testdbm.rb (setup): DBM.open(path, 0400) cause EACCESS
+ on Berkeley DB[234].
+
+Mon May 5 22:57:07 2003 Tadayoshi Funaba <tadf@dotrb.org>
+
+ * sample/cal.rb: use values_at instead of select.
+
+ * sample/biorhythm.rb: ditto.
+
+Mon May 5 18:59:45 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * sample/test.rb: substitute 'select' with 'values_at'.
+
+ * lib/date.rb: ditto.
+
+ * lib/parsedate.rb: ditto.
+
+Mon May 5 00:46:10 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * array.c (rb_ary_values_at): new method to replace select(index..).
+
+ * hash.c (rb_hash_values_at,env_values_at): ditto.
+
+ * re.c (match_values_at): ditto.
+
+ * struct.c (rb_struct_values_at): ditto.
+
+ * re.c (match_select): add iterator behavior.
+
+Sun May 4 19:08:53 2003 Tadayoshi Funaba <tadf@dotrb.org>
+
+ * lib/date/format.rb: synchronized with date2 3.3.2.
+
+Sun May 4 15:21:18 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/net/smtp.rb: ESMTP -> SMTP transition wrongly fails.
+
+Sun May 4 15:06:37 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/net/pop.rb: APOP did not work. [ruby-dev:20149]
+
+Sat May 3 21:14:29 2003 Johan Holmberg <holmberg@iar.se>
+
+ * ext/curses/curses.c, ext/digest/sha2/sha2.c, ext/iconv/iconv.c,
+ ext/racc/cparse/cparse.c: include "ruby.h" at the top to shut up
+ "_FILE_OFFSET_BITS redefined" warning on Solaris.
+
+Sat May 3 11:00:12 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * class.c (rb_class_protected_instance_methods): now gives
+ warnings to show migration path. The default will be reversed
+ on Jan 2004.
+
+Sat May 3 00:58:53 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * object.c (rb_obj_methods): now accepts recurse parameter.
+
+ * lib/delegate.rb (Delegator::initialize): instance_methods
+ etc. now recurse by default. need to specify false.
+
+Sat May 3 00:22:00 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/net/protocol.rb: reintroduce Protocol.protocol_param.
+
+ * lib/net/http.rb: ditto.
+
+ * lib/net/pop.rb: ditto.
+
+ * lib/net/smtp.rb: ditto.
+
+Fri May 2 23:29:53 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/net/protocol.rb: remove Protocol class.
+
+ * lib/net/smtp.rb (SMTP): ditto.
+
+ * lib/net/pop.rb (POP3): ditto.
+
+ * lib/net/http.rb (HTTP): ditto.
+
+ * lib/net/protocol.rb: remove Command class.
+
+ * lib/net/smtp.rb (SMTPCommand): ditto.
+
+ * lib/net/pop.rb (POP3Command): ditto.
+
+ * lib/net/pop.rb: remove APOPCommand class.
+
+ * lib/net/protocol.rb: remove Code class and its all subclasses.
+
+ * lib/net/protocol.rb: remove Response class and its all
+ subclasses.
+
+ * lib/net/pop.rb (POPMail): new method unique_id (alias uidl).
+
+Fri May 2 18:17:37 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * compar.c (cmp_gt): raises ArgumentError when "<=>" give nil.
+ inspired by discussion on comp.lang.python.
+
+Fri May 2 17:37:01 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * lib/cgi/session.rb (CGI::Session::initialize): updated to
+ support 2003-04-23 change in cgi.rb [ruby-core:1002]
+
+Fri May 2 17:21:02 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * class.c (method_list): classify methods based on nearest
+ visibility. [ruby-dev:20127]
+
+ * class.c (rb_class_instance_methods): recurse by default. other
+ method listing methods as well.
+
+Fri May 2 09:38:06 2003 Warren Brown <wkb@airmail.net>
+
+ * string.c (rb_str_ljust): now takes optional argument to specify
+ pad string. [ruby-talk:70482]
+
+ * string.c (rb_str_rjust): ditto.
+
+ * string.c (rb_str_center): ditto.
+
+ * string.c (rb_str_justify): utility function.
+
+Fri May 2 04:10:59 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_add_method): call singleton_method_added or
+ method_added for every method definition (after ruby_running).
+ [ruby-talk:70471]
+
+ * array.c (rb_ary_reverse_bang): Array#reverse! should not return
+ nil even for arrays sized less than 2.
+
+Thu May 1 23:18:01 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * io.c (argf_eof): should not block after reading all argument
+ files. (ruby-bugs-ja PR#449)
+
+Fri May 2 15:10:41 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/fileutils.rb: use hashes to pass options.
+
+ * lib/fileutils.rb: new option mkdir(:mode), mkdir_p(:mode).
+
+ * instruby.rb: follow fileutils.rb feature change.
+
+Thu May 1 08:24:00 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * regex.c (re_match_exec): $ _always_ matches at the end of string.
+
+Wed Apr 30 14:12:00 2003 wanowa.kimura@nifty.ne.jp (kimura wataru)
+
+ * net/imap.rb: support THREAD extension.
+
+Sun Apr 27 23:13:20 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * string.c (rb_str_to_i): disallow negative radix.
+ [ruby-dev:20087]
+
+Sat Apr 26 23:34:42 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * parse.y (open_args): warning message changed to "don't put space
+ before argument parentheses".
+
+Sat Apr 26 14:25:00 2003 Takaaki Uematsu <uema2x@jcom.home.ne.jp>
+
+ * wince/ : files removed.
+ (config, dll.mak, exe.mak, mswince-ruby17.def,
+ io.c, process.c, signal.c, string.c, time.c)
+
+ * wince/ : files added.
+ (assert.c, Makefile.sub, mkexports.rb, io_wce.c,
+ process_wce.c, signal_wce.c, string_wce.c,
+ time_wce.c)
+
+ * wince/configure.bat : like mswin32 style.
+
+ * wince/direct.c : remove "static" at _currentdir.
+
+ * wince/io.h : change definition.
+
+ * wince/stdio.c : _fdopen -> fdopen.
+
+ * wince/process.h : add _P_OVERLAY.
+
+ * wince/time.h : change definition.
+
+ * wince/wincemain.c : add wce_SetCurrentDir.
+
+ * wince/wince.c : add wce_SetCurrentDir and wce_fopen.
+ fix GetModuleFileNameA to return correct "lpFileName".
+
+ * wince/wince.h : remove #ifdef.
+
+ * wince/sys/utime.h, utime.c : rename _utime to utime.
+
+ * wince/sys/stat.c : expand relative directory in stat.
+
+Sat Apr 26 06:33:04 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * io.c (argf_read): ARGF.read() should read all argument files.
+
+Fri Apr 25 18:46:00 2003 Takaaki Uematsu <uema2x@jcom.home.ne.jp>
+
+ * gc.c: STACK_LEVEL_MAX=65535 on mswince.
+
+Fri Apr 25 18:40:07 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * io.c (argf_read): read should not span two files. [ruby-dev:20073]
+
+Fri Apr 25 18:19:03 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (splat_value): split splat_value() and avalue_splat().
+
+ * io.c: there's no way to set non-IO value to current_file, thus
+ no need for argf_forward().
+
+Fri Apr 25 02:03:25 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (proc_invoke): Proc#yield should pass through retry and
+ break like keyword yield. [ruby-talk:70034]
+
+ * eval.c (proc_invoke): orphan Proc now raises LocalJumpError for
+ break and retry again.
+
+ * eval.c (rb_eval): ARGSCAT should splat the argument.
+
+ * eval.c (splat_value): splat operation function.
+
+Thu Apr 24 23:37:02 2003 Dave Thomas <dave@thomases.com>
+
+ * lib/matrix.rb (Matrix#minor): Used Range#size, which no longer
+ exists.
+
+ * lib/complex.rb (new!): Complex.new had been made private, but
+ Kernel#Complex called it. Re-exposed as new!.
+
+ * lib/matrix.rb (Matrix.row_vector): Fix method name typo
+
+Thu Apr 24 19:40:02 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * ext/extmk.rb: add -Wl,--no-undefined to LDSHARED only
+ if GNU ld is 2.11 or later.
+
+Wed Apr 23 14:05:40 2003 Dave Thomas <dave@pragprog.com>
+
+ * lib/ipaddr.rb (include?): Support non-IPAddr parameters.
+ [ruby-core:00980]
+
+Wed Apr 23 13:31:10 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * lib/cgi.rb (CGI::QueryExtension::[]): always return Value
+ object.
+
+Wed Apr 23 08:39:27 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * ext/zlib/extconf.rb: bccwin32 is win32 too.
+
+Tue Apr 22 20:58:00 2003 Takaaki Uematsu <uema2x@jcom.home.ne.jp>
+
+ * ruby.c: don't call VirtualQuery in ruby_init_loadpath()
+ on mswince.
+
+Tue Apr 22 19:08:53 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * marshal.c (save_mantissa, load_mantissa): for interoperability
+ should count cut-down bit from topmost.
+
+Tue Apr 22 09:20:40 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * parse.y (arg_ambiguous): hopefully better message.
+
+ * lib/cgi.rb (CGI::QueryExtension::initialize_query): to_ary
+ removed.
+
+Tue Apr 22 06:06:22 2003 Tanaka Akira <akr@m17n.org>
+
+ * lib/resolv.rb (Resolv::DNS::Resource#hash): use XOR to accumulate
+ hash value.
+
+ * lib/tsort.rb (TSort#each_strongly_connected_component): don't use
+ block argument.
+ (each_strongly_connected_component_from): ditto.
+
+Mon Apr 21 21:59:48 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * marshal.c: one more digit for decimal point. [ruby-talk:69808]
+
+Mon Apr 21 21:25:59 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * numeric.c (flo_is_finite_p): use finite() if available.
+
+ * win32/win32.h (isinf, isnan): define as macro.
+ [ruby-win32:00533]
+
+ * bcc32/Makefile.sub, win32/Makefile.sub: no longer use
+ missing/isinf.c, missing/isnan.c.
+
+Mon Apr 21 18:36:28 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * bignum.c (rb_cstr_to_inum): unnecessarily long buffer was used
+ for radix 9. [ruby-dev:20057]
+
+Mon Apr 21 17:44:34 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * parse.y (block_append, value_expr0, assign_in_cond,
+ warn_unless_e_option, warning_unless_e_option, range_op,
+ cond0): adjust line number in warning.
+
+Mon Apr 21 00:47:42 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * sample/test.rb: avoid the MSVCRT *printf problem(float).
+ [ruby-dev:20037]
+
+Mon Apr 21 00:11:15 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * marshal.c (w_float): append least mantissa bits to get rid
+ of roundoff problem. [ruby-talk:69518]
+
+ * marshal.c (r_object0): load least mantissa bits.
+
+Sun Apr 20 23:24:25 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * win32/win32.c (NtInitialize): set the floating-point control word
+ on bcc32.
+
+ * win32/win32.h, bcc32/Makefile.sub: use missing/isinf.c, should not
+ use _finite() because it returns 0 if NaN.
+
+Sun Apr 20 03:09:30 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * parse.y (void_expr0): node might become NULL after calling
+ remove_begin().
+
+Sat Apr 19 21:55:10 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * ext/Setup*: Add zlib and remove bogus and obsolete entries.
+
+Sat Apr 19 14:47:07 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * gc.c (rb_gc): use rb_gc_mark_maybe() to mark registered C
+ addresses. C variables may not hold valid reference to Ruby
+ objects. [ruby-core:00975]
+
+Sat Apr 19 00:56:13 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * struct.c (rb_struct_eql): should compare values with "eql?".
+
+Fri Apr 18 23:29:08 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * range.c (range_check): <=> returns nil for invalid values;
+ should check.
+
+Fri Apr 18 15:26:50 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * error.c (rb_raise): workaround for some implementations of
+ vsnprintf.
+
+Fri Apr 18 02:23:42 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * regex.c (re_compile_pattern): should not set RE_OPTIMIZE_ANCHOR,
+ if anychar_repeat is enclosed by parentheses.
+
+Fri Apr 18 01:49:18 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * util.c (ruby_strtod): improved conversion accuracy.
+
+Thu Apr 17 14:39:23 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * ext/dbm/dbm.c (each_pair): add prototype to avoid VC++ warnings.
+
+ * ext/readline/readline.c (Init_readline): follow readline 4.2
+ prototype.
+
+Thu Apr 17 14:22:36 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * parse.y (cond0): warn only range literals whose both side are
+ literals. [ruby-core:00964]
+
+Thu Apr 17 11:10:59 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * ext/readline/readline.c: add the defined operator for bcc32.
+
+Wed Apr 16 00:14:06 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * misc/ruby-mode.el (ruby-special-char-p): should test at the
+ point if no argument. fixed by Michael Scholz
+ <scholz-micha@gmx.de>.
+
+Tue Apr 15 19:35:08 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/fileutils.rb: rm_r should raise Errno::ENOENT if file
+ does not exist ([ruby-core:958]). Thanks Johan Holmberg.
+
+Tue Apr 15 19:12:21 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * struct.c (rb_struct_hash): new methods Struct#hash, Struct#eql?.
+ (ruby-bugs:PR#758)
+
+Tue Apr 15 16:05:11 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * numeric.c (rb_fix2str): buffer was insufficient.
+ (ruby-bugs-ja:PR#431)
+
+Mon Apr 14 19:45:56 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * file.c (file_expand_path): root must follow buf when
+ reallocated. [ruby-talk:69339], [ruby-dev:20025]
+
+Mon Apr 14 03:22:33 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * rubyio.h (struct OpenFile): add noraise flag to finalizer.
+
+ * io.c (Init_IO): define $/, $-0, and $\ as string-only
+ variables.
+
+ * string.c (rb_str_split_m): does not generate empty string if
+ the receiver is empty.
+
+ * io.c (fptr_finalize): should raise error on EBADF for readable
+ IOs as well.
+
+Mon Apr 14 15:54:18 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * bignum.c (rb_cstr_to_inum, rb_big2str): allow 2-36 as radix.
+
+ * numeric.c (rb_fix2str): ditto.
+
+ * string.c (rb_str_to_i): ditto.
+
+Sun Apr 13 03:20:31 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * lib/mkmf.rb (try_func): remove COMMON_HEADERS at first for
+ performance.
+
+Sat Apr 12 20:59:40 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * misc/ruby-mode.el (ruby-beginning-of-arg): substitute
+ ruby-backward-arg.
+
+ * misc/ruby-mode.el (ruby-calculate-indent): fixed wrong
+ indentation in brace block and parentheses.
+
+ * misc/ruby-mode.el (ruby-forward-sexp, ruby-backward-sexp):
+ support special char literal, and negative arguments.
+
+Sat Apr 12 17:52:47 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * file.c (rb_stat): use rb_check_convert_type() to retrieve IO.
+
+Fri Apr 11 19:00:14 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * win32/win32.c (rb_w32_stat): check arguments. [ruby-dev:20007]
+ [ruby-win32:535]
+
+Fri Apr 11 15:56:08 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * numeric.c (coerce_rescue): prevent inspected String from GC.
+
+ * numeric.c (flo_eq, rb_dbl_cmp, flo_gt, flo_ge, flo_lt, flo_le,
+ flo_eql): correct NaN comparison. (ruby-bugs:PR#744)
+
+ * sample/test.rb: NaN comparison test.
+
+Fri Apr 11 14:48:47 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * file.c (rb_stat): dereference using StringValuePtr().
+
+ * file.c (rb_file_s_stat): use rb_stat(). [ruby-dev:20007]
+
+Fri Apr 11 10:51:08 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * lib/benchmark.rb (Benchmark::bm): get rid of warning.
+ [ruby-talk:69124]
+
+Fri Apr 11 02:41:35 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * io.c (set_stdin): assigned value must respond to "read" and
+ "getc".
+
+ * io.c (set_outfile): assigned value must respond to "write".
+ (ruby-bugs-ja:PR#425)
+
+Thu Apr 10 21:12:19 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/net/pop.rb: Exception line was accidentaly removed.
+ [ruby-dev:19989]
+
+Thu Apr 10 18:42:13 2003 Tadayoshi Funaba <tadf@dotrb.org>
+
+ * array.c (rb_ary_times): added some checks for request size.
+
+Thu Apr 10 03:22:38 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * variable.c (rb_mod_name): always return empty string for
+ anonymous class/module. (ruby-bugs-ja PR#424)
+
+ * config.sub: stop forcing addition of -gnu to -linux.
+
+ * variable.c (classname): refactoring.
+
+ * variable.c (rb_class_path): __tmp__classpath__ handling moved
+ from classname().
+
+Thu Apr 10 01:52:24 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (rb_obj_is_method): indefinite return value.
+
+Thu Apr 10 00:39:32 2003 Tanaka Akira <akr@m17n.org>
+
+ * regex.c (re_compile_pattern): /[\--\-]/ was warned. warn /]/.
+
+ * mkconfig.rb: escape `]' in regexp.
+
+Thu Apr 10 00:27:07 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * time.c (time_strftime): RSTRING(format)->ptr might become NULL.
+
+Wed Apr 9 23:54:50 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * variable.c (rb_obj_remove_instance_variable): better message.
+ [ruby-talk:68987]
+
+ * variable.c (rb_mod_remove_const): ditto.
+
+ * object.c (rb_obj_ivar_get): ditto.
+
+ * object.c (rb_obj_ivar_set): ditto.
+
+ * parse.y (yylex): ditto.
+
+Wed Apr 9 21:51:20 2003 Dave Thomas <Dave@Thomases.com>
+
+ * eval.c (rb_mod_define_method): Allow UnboundMethod as
+ parameter.
+
+Wed Apr 9 18:30:58 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (top_include): include module to wrapper module if
+ wrapper is present. experimental. [ruby-list:37539]
+
+Wed Apr 9 17:24:21 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * gc.c (rb_gc_mark_children): introduce this function again; this
+ is required when stack was very tight. [ruby-talk:68916]
+
+Wed Apr 9 15:49:30 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * bignum.c (bigdivmod): small typo.
+
+Wed Apr 9 15:35:04 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * ext/readline/readline.c: include <unistd.h> only when
+ HAVE_UNISTD_H is defined.
+
+Wed Apr 9 14:05:00 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * marshal.c (w_object): preserve extended module on struct.
+ (ruby-bugs-ja:PR#422)
+
+Wed Apr 9 03:43:14 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * bignum.c (BIGZEROP): macro to determine if x is a bignum zero.
+
+Tue Apr 8 11:49:31 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (Init_Proc): make Method and UnboundMethod independent.
+ They are like instance and its class. [ruby-core:00941]
+
+ * parse.y (yylex): disallow global variables like "$1ve".
+ [ruby-core:00945]
+
+ * marshal.c (marshal_dump): Marshal.dump(0, false) should cause an
+ error. (ruby-bugs-ja PR#421)
+
+ * regex.c (re_compile_pattern): warn if '-' is the edge of
+ character range.
+
+Mon Apr 7 15:49:09 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * ext/socket/socket.c (sock_s_unpack_sockaddr_in): remove struct
+ size check. getnameinfo(3) can handle. [ruby-dev:19967]
+
+Mon Apr 7 01:33:31 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * io.c (io_read): do not call rb_sys_fail() when required data
+ length is zero. (ruby-bugs-ja PR#420)
+
+ * eval.c (umethod_proc): should raise TypeError, instead of
+ returning error causing Proc. Following the principle of "fail
+ early". [ruby-core:00927]
+
+Sun Apr 6 18:29:21 2003 UENO Katsuhiro <katsu@blue.sky.or.jp>
+
+ * ext/zlib/zlib.c: the return value of GzipReader#getc must be
+ unsigned.
+
+Sun Apr 6 00:35:37 2003 Tanaka Akira <akr@m17n.org>
+
+ * sample/exyacc.rb: use Regexp in gsub!.
+
+Sat Apr 5 23:41:28 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * pack.c (pack_pack): small but serious typo.
+
+Sat Apr 5 04:23:05 2003 Warren Brown <wkb@airmail.net>
+
+ * sprintf.c (rb_f_sprintf): was decrementing width even if there
+ is no sign character.
+
+Sat Apr 5 01:41:28 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (backtrace): skip internal allocator frame.
+ (ruby-bugs-ja PR#416)
+
+Fri Apr 4 10:53:22 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (assign): should prepare mrhs by svalue_to_mrhs().
+
+Wed Apr 2 15:11:23 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * README.EXT, README.EXT.ja (3.3): clarified -1 as free for
+ Data_Wrap_Struct(). [ruby-dev:19881]
+
+Mon Mar 31 11:11:36 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_f_missing): use "inspect" for T_OBJECT as well.
+
+Mon Mar 31 10:50:48 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * hash.c (env_reject_bang): untaint key string.
+
+ * hash.c (env_delete_m): execute block only if deleting key does
+ not exist.
+
+Sat Mar 29 17:54:46 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * pack.c (pack_pack): do not call rb_str_buf_cat() with NULL ptr,
+ which causes SEGV; jump to grow instead. [ruby-dev:19944]
+
+Sat Mar 29 15:19:48 2003 Tanaka Akira <akr@m17n.org>
+
+ * instruby.rb, ext/extmk.rb, lib/benchmark.rb, lib/cgi.rb,
+ lib/debug.rb, lib/getoptlong.rb, lib/optparse.rb, lib/time.rb,
+ lib/date/format.rb, lib/irb/ruby-lex.rb lib/uri/common.rb: revert
+ escape for `-' in character class.
+
+Sat Mar 29 09:48:35 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (avalue_to_svalue): use rb_check_array_type() again.
+ Clarify how "to_ary" and "to_a" work. [ruby-talk:68155]
+
+ * eval.c (svalue_to_avalue): ditto.
+
+ * eval.c (svalue_to_mrhs): ditto.
+
+ * eval.c (rb_eval): unary splat to use to_a, but we need a hack to
+ exclude Object#to_a until it's removed.
+
+ * object.c (rb_Array): check obj.respond_to?("to_a"). Currently
+ all object respond_to "to_a", but Object#to_a will be removed.
+
+ * range.c (Init_Range): undefine to_ary.
+
+ * re.c (Init_Regexp): ditto.
+
+Sat Mar 29 09:47:52 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * MANIFEST (ext/aix_mksym.rb): remove obsolete file.
+
+Fri Mar 29 06:21:24 2003 UENO Katsuhiro <katsu@blue.sky.or.jp>
+
+ * ext/zlib: merge from rough.
+
+Fri Mar 28 19:33:39 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * variable.c (rb_class_path): hold temporary class path in a
+ instance variable to get rid of GC. [ruby-dev:19932]
+
+ * variable.c (classname): remove temporary class path when exact
+ name found.
+
+Fri Mar 28 18:29:23 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * regex.c (re_compile_pattern): do not warn if "-" is at the top
+ or last of character class.
+
+Thu Mar 27 12:10:15 2003 Tanaka Akira <akr@m17n.org>
+
+ * regex.c (re_compile_pattern): fix [:name:] handling.
+ /[\[:digit:]]/ was treated as /[[:digit:]]/.
+ /[[:-@]/ was treated as /[\[:\-@]/.
+ /[%-[:digit:]]/ was treated as /[%-\[:digit:]\]/.
+
+Thu Mar 27 03:26:40 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * string.c (rb_str_capitalize_bang): check length before upcase
+ first character. (ruby-bugs:PR#697)
+
+Wed Mar 26 20:25:10 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * dln.c (dln_find_1): break if path list end, even for too long
+ path names. (ruby-bugs-ja:PR#412)
+
+Wed Mar 26 13:19:32 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (avalue_splat): new function to do unary * (splat)
+ operator.
+
+ * eval.c (avalue_to_svalue,svalue_to_avalue,svalue_to_mrhs): do
+ not use implicit "to_ary" conversion.
+
+ * ext/curses/curses.c (GetWINDOW,GetMOUSE): add taint check.
+
+ * ext/curses/curses.c (curses_init_screen): ditto.
+
+ * ext/curses/curses.c (window_initialize): ditto.
+
+ * gc.c (os_each_obj): prohibit ObjectSpace#each_object in safe
+ mode ($SAFE >= 4).
+
+Tue Mar 25 23:26:02 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * signal.c (trap): return "DEFAULT" and "IGNORE" respectively for
+ previous sighandler SIG_DFL and SIG_IGN. [ruby-talk:67860]
+
+Tue Mar 25 12:24:15 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_yield_0): call avalue_to_mrhs() to assign block
+ parameter |a|. [ruby-dev:19897]
+
+ * ruby.c (ruby_set_argv): freeze argument strings.
+
+Tue Mar 25 12:01:54 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * io.c (rb_io_initialize): should check rb_secure(4).
+
+ * dir.c (dir_s_getwd): should check rb_secure(4).
+
+ * object.c (rb_obj_infect): function version of OBJ_INFECT().
+
+ * eval.c (rb_secure_update): new function to check object update.
+
+Tue Mar 25 10:18:05 2003 Minero Aoki <aamine@loveruby.net>
+
+ * ext/strscan/strscan.c: should infect also return values of
+ #inspect.
+
+ * ext/strscan/strscan.c: use snprintf() instead of sprintf().
+
+Mon Mar 24 16:55:04 2003 Takaaki Tateishi <ttate@ttsky.net>
+
+ * ext/dl/dl.c: added rb_secure(4). (Thanks to Minero Aoki)
+
+ * ext/dl/sym.c: ditto.
+
+ * ext/dl/ptr.c: ditto.
+
+Mon Mar 24 00:09:02 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * parse.y (block_append): warn unused literal.
+
+Sun Mar 23 22:22:04 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * lib/jcode.rb (tr!, delete!, szueeze!): add empty string checking.
+
+Sun Mar 23 19:54:53 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * gc.c (rb_gc_call_finalizer_at_exit): use free() if dfree is -1.
+
+Sat Mar 22 15:50:29 2003 Tanaka Akira <akr@m17n.org>
+
+ * time.c (make_time_t): try search_time_t if mktime/timegm is failed.
+
+Sat Mar 22 13:26:33 2003 Tanaka Akira <akr@m17n.org>
+
+ * lib/optparse.rb, lib/jcode.rb, ext/tk/lib/tk.rb: reorder character
+ class /[\]\[]/ to /[\[\]]/ for readability.
+
+Sat Mar 22 12:44:15 2003 Tanaka Akira <akr@m17n.org>
+
+ * lib/date/format.rb, lib/uri/common.rb: escape `[', `]', `-' in
+ character class in regexp to avoid warning.
+
+Sat Mar 22 07:39:32 2003 Ulf Betlehem <flu@iki.fi>
+
+ * io.c (rb_io_fread): may lose data on nonblocking read.
+
+Fri Mar 21 23:40:41 2003 Tanaka Akira <akr@m17n.org>
+
+ * regex.c (re_compile_pattern): fix previous change.
+
+ * instruby.rb, ext/extmk.rb, ext/tk/lib/tk.rb, lib/benchmark.rb,
+ lib/cgi.rb, lib/debug.rb, lib/getoptlong.rb, lib/jcode.rb,
+ lib/optparse.rb, lib/time.rb, lib/date/format.rb,
+ lib/irb/ruby-lex.rb: escape `[', `]', `-' in character class in
+ regexp to avoid warning.
+
+Fri Mar 21 23:23:45 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * regex.c (re_compile_pattern): give warning for unescaped square
+ brackets and minus in character class. [ruby-dev:19868]
+
+Fri Mar 21 18:12:20 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (bmcall): missing type.
+
+Fri Mar 21 01:29:35 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * sprintf.c (rb_f_sprintf): copy sign bits only if value is
+ negative.
+
+ * missing.h: include <stdarg.h> or <varargs.h> if HAVE_VSNPRINTF
+ is not defined.
+
+Thu Mar 20 18:31:37 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * lib/optparse.rb (OptionParser#order!): follow recent change
+ of proc argument.
+
+Thu Mar 20 16:12:53 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * numeric.c (flo_to_s): change format specifier to "%.15g" to
+ avoid unnecessary 9s (e.g. 99.59999999999999). (ruby-bugs-ja PR#406)
+
+Thu Mar 20 16:03:18 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * parse.y (stmt, primary): get rid of SEGV at empty or invalid
+ condition. (ruby-bugs-ja:PR#410)
+
+ * parse.y (cond_negative): negate condition node when NODE_NOT.
+
+Thu Mar 20 10:45:29 2003 Tanaka Akira <akr@m17n.org>
+
+ * eval.c (bmcall): add volatile to avoid GC problem.
+
+Thu Mar 20 10:10:49 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (load_dyna): clear ruby_errinfo. (ruby-bugs-ja PR#409)
+
+Wed Mar 19 23:05:30 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>
+
+ * lib/tracer.rb (trace_func): save and recover Thread.critical state.
+ Fixed by Fukumoto Atsushi <fukumoto@imasy.or.jp> [ruby-dev:19830]
+
+Wed Mar 19 02:55:46 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * io.c (read_all): make str empty if given. (ruby-bugs-ja PR#408)
+
+ * io.c (io_read): ditto.
+
+ * io.c (rb_io_sysread): ditto.
+
+Tue Mar 18 18:24:03 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * range.c: do not override min and max.
+
+Sun Mar 16 12:29:55 2003 Tanaka Akira <akr@m17n.org>
+
+ * lib/pp.rb (object_address_group): use to_s instead of name
+ to get name of class.
+
+Fri Mar 14 08:53:29 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * sprintf.c (remove_sign_bits): octal left most digit for negative
+ numbers may be '3'. (ruby-bugs-ja PR#407)
+
+ * sprintf.c (rb_f_sprintf): should prefix sign bits if bignum is
+ negative, using sign_bits().
+
+Wed Mar 12 16:48:19 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * io.c (prep_stdio): set binmode only if the file descriptor
+ is not connected to a terminal on Cygwin.
+
+Wed Mar 12 11:23:49 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (avalue_to_mrhs): split argument passing and assignment
+ conversion.
+
+ * eval.c (svalue_to_mrhs): ditto.
+
+ * eval.c (avalue_to_svalue): avalue_to_svalue([[1,2]]) should be
+ [[1,2]], not [1,2] to wrap-around.
+
+Tue Mar 11 21:00:59 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/net/smtp.rb: Digest string wrongly included '\n' when user
+ name is too long (ruby-bugs-ja:PR#404).
+
+Tue Mar 11 20:07:01 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/net/http.rb: speeding up by avoiding extra flush.
+ (suggested by Brian Candler <B.Candler@pobox.com> [ruby-talk:66516])
+
+Tue Mar 11 04:30:12 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (massign): remove unnecessary array unpacking; it should
+ be handled before massign() by svalue_to_mrhs().
+
+ * eval.c (svalue_to_mrhs): '*a = v' value conversion to avalue
+ (i.e. [1] => [[1]], [1,2] => [1,2]).
+
+ * eval.c (rb_eval): use svalue_to_mrhs.
+
+ * eval.c (rb_yield_0): ditto.
+
+ * eval.c (proc_invoke): break from "Proc#yield" is legal.
+
+Mon Mar 10 23:19:29 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * file.c (rb_find_file): need world writable directory check for
+ relative paths too.
+
+Mon Mar 10 11:23:00 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * file.c (rb_find_file): world writable directory check if
+ $SAFE >= 1 (was $SAFE >= 2).
+
+Mon Mar 10 01:59:47 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/net/pop.rb: do not dispatch LIST when a mailbox is empty.
+
+ * lib/net/pop.rb: merge the 'STAT' patch from Frank S.Fejes
+ <frank@oopdreams.com>, with modifications (listed below).
+
+ * lib/net/pop.rb: new method Net::POP#mail_size.
+
+ * lib/net/pop.rb: new method Net::POP#bytes.
+
+ * lib/net/pop.rb: new method Net::POPCommand#stat.
+
+Sun Mar 9 19:30:25 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * lib/fileutils.rb (mkdir, mkdir_p): revert.
+
+ * instruby.rb (umask): umask 0022, not 0.
+
+Sun Mar 9 17:09:40 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * lib/fileutils.rb (mkdir, mkdir_p): set mode to 0755.
+
+ * Makefile.in (fake.rb): set ALT_SEPARATOR to the default value.
+
+Sat Mar 8 11:30:59 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (massign): fix a bug not to expand in assignment to sole
+ lhs. [ruby-dev:19766]
+
+Fri Mar 7 21:57:25 2003 Tanaka Akira <akr@m17n.org>
+
+ * lib/pp.rb (Kernel.pp): module function.
+ (MatchData#pretty_print): new method.
+
+Fri Mar 7 20:27:19 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * ext/tcltklib/extconf.rb (find_tcl, find_tk): return true if
+ non-versioned found. [ruby-dev:19759]
+
+Fri Mar 7 15:05:35 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * ext/dbm/extconf.rb: add QDBM support.
+
+Fri Mar 7 12:59:39 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (massign): deal with sole lhs, assign rest args from
+ converted array. [ruby-dev:19751]
+
+Fri Mar 7 03:31:36 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * parse.y (dsym): :"symbol string" style should not contain `\0'.
+
+ * process.c (proc_detach): new method Process.detach(pid) which
+ create background watcher thread to issue waitpid. [new]
+
+ * process.c (rb_detach_process): utility function to detach
+ process from C code.
+
+ * ext/pty/pty.c (pty_finalize_syswait): terminate watcher thread,
+ and detach child process (by creating new idle waitpid watcher
+ thread).
+
+ * ext/pty/pty.c (pty_syswait): may lost signal stopped child.
+
+Fri Mar 7 00:30:33 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * ext/Win32API/Win32API.c: no longer use inline-asms.
+
+ * ext/Win32API/extconf.rb: no need to add gcc options.
+
+Thu Mar 6 13:02:10 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * parse.y (reswords): fix reswords list.
+
+Wed Mar 5 12:13:21 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * configure.in: better YACC support on HP-UX.
+
+Wed Mar 5 05:55:20 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * string.c (rb_str_cat): remove ptr NULL check and MEMZERO(). ptr
+ must be non NULL.
+
+Tue Mar 4 23:12:07 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * configure.in, bcc32/Makefile.sub, win32/Makefile.sub: define
+ RUBY_EXPORT to export symbols.
+
+ * defines.h: use RUBY_EXTERN instead of EXTERN.
+
+ * intern.h, re.h, ruby.h, rubysig.h: ditto.
+
+ * win32/win32.h: remove EXTERN definition.
+
+Tue Mar 4 17:54:30 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * array.c (rb_ary_aref): raise TypeError if index is a symbol.
+ [ruby-list:37217]
+
+ * array.c (rb_ary_aset): ditto.
+
+Tue Nov 13 14:39:11 2001 WATANABE Tetsuya <tetsu@jpn.hp.com>
+
+ * missing/strftime.c: HP-UX support.
+
+Tue Mar 4 15:08:08 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * configure.in: better HP-UX support.
+
+ * missing/strftime.c: ditto.
+
+Tue Mar 4 10:11:32 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * io.c (rb_io_popen): do not call rb_io_close() directly, call
+ "close" method instead. [ruby-dev:19717]
+
+ * io.c (rb_io_s_open): ditto.
+
+ * hash.c (rb_any_hash): remove DEFER_INTS. all do_hash() calls in
+ st.c are at the top of functions. No reentrant problem.
+
+Tue Mar 4 01:19:21 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * ext/dl/MANIFEST: Exclude .cvsignore. [found by: eban]
+
+Tue Mar 4 01:17:08 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * ext/Win32API/MANIFEST: Belatedly add lib/win32/registry.rb.
+ [found by: eban]
+
+Tue Mar 4 00:33:04 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * MANIFEST: Belatedly add Test::Unit files. D'oh!
+
+Sun Mar 2 09:51:47 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * marshal.c (w_nbyte): should output always via rb_io_write().
+
+ * marshal.c (dump_ensure): ditto.
+
+ * marshal.c (marshal_dump): should call "binmode" method, if it
+ responds to.
+
+ * marshal.c (r_byte): should input always via "getc" method.
+
+ * marshal.c (r_bytes0): should input always via "read" method.
+
+ * marshal.c (marshal_load): need not to set up FILE* fp;
+
+Mon Mar 3 11:29:04 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * parse.y (arg): parse 'lhs = a rescue b' as 'lhs=(a rescue b)'.
+
+Mon Mar 3 02:53:52 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * io.c (rb_io_fread): should not clearerr() if there's no filled
+ buffer (i.e. rb_io_fread() returning zero).
+
+Mon Mar 3 01:42:35 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * misc/ruby-mode.el (ruby-expr-beg): escaped char syntax.
+
+ * misc/ruby-mode.el (ruby-parse-partial): ditto.
+
+ * misc/ruby-mode.el (ruby-parse-partial): no deep indent for
+ block.
+
+ * misc/ruby-mode.el (ruby-backward-arg): skip arguments backward.
+
+ * misc/ruby-mode.el (ruby-calculate-indent): too deep indentation.
+
+Fri Feb 28 23:50:32 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * win32/win32.c (map_errno): map OS error to errno. [new]
+
+ * win32/win32.c (pipe_exec, CreateChild, poll_child_status, waitpid,
+ kill, link, rb_w32_rename, unixtime_to_filetime, rb_w32_utime): use
+ map_errno() instead of using GetLastError() directly.
+
+ * win32/win32.c (rb_w32_select, rb_w32_accept, rb_w32_bind,
+ rb_w32_connect, rb_w32_getpeername, rb_w32_getsockname,
+ rb_w32_getsockopt, rb_w32_ioctlsocket, rb_w32_listen, rb_w32_recv,
+ rb_w32_recvfrom, rb_w32_send, rb_w32_sendto, rb_w32_setsockopt,
+ rb_w32_shutdown, rb_w32_socket, rb_w32_gethostbyaddr,
+ rb_w32_gethostbyname, rb_w32_gethostname, rb_w32_getprotobyname,
+ rb_w32_getprotobynumber, rb_w32_getservbyname, rb_w32_getservbyport,
+ rb_w32_fclose, rb_w32_close): map winsock error to errno.
+
+Fri Feb 28 22:54:10 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * win32/win32.c (flock): supports larger files, and maps error
+ code.
+
+ * win32/win32.c (rb_w32_asynchronize): returns errno from child
+ thread.
+
+ * win32/win32.c (rb_w32_fclose, rb_w32_close): ensures unlocked.
+
+Wed Feb 26 17:38:16 2003 Tanaka Akira <akr@m17n.org>
+
+ * lib/open-uri.rb: replace Kernel.open as well.
+
+Tue Feb 25 23:03:08 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>
+
+ * lib/debug.rb (DEBUGGER__::Context#debug_command): bp filename must
+ be the basename of it. [ruby-talk:65644]
+
+Mon Feb 24 17:49:35 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * parse.y (yycompile): zero clear ruby_eval_tree_begin if
+ compilation failed.
+
+Mon Feb 24 08:06:29 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * string.c (str_new): need no MEMZERO().
+
+Sun Feb 23 17:57:06 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * lib/fileutils (fu_stream_blksize): wrong logical condition.
+ (and -> or).
+
+Sat Feb 22 03:12:56 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * numeric.c (fix_gt): use rb_num_coerce_cmp() instead of
+ rb_num_coerce_bin.
+
+ * numeric.c (fix_ge, fix_lt, fix_le): ditto.
+
+ * numeric.c (flo_gt, flo_ge, flo_lt, flo_le): ditto.
+
+Sat Feb 22 02:45:20 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_thread_create): may called from place higher than
+ rb_gc_stack_start.
+
+ * gc.c (Init_stack): update rb_gc_stack_start if it is lower (or
+ higher if stack grows down) than the previous value.
+
+Fri Feb 21 21:03:41 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/fileutils.rb: new method FileUtils#copy_stream.
+
+ * lib/fileutils.rb: new method FileUtils#compare_file.
+
+ * lib/fileutils.rb: new method FileUtils#compare_stream.
+
+ * lib/fileutils.rb: new method FileUtils#rmtree (alias of rm_rf).
+
+Fri Feb 21 17:19:27 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * eval.c (rb_f_require): do not need to abort if a DLEXT file
+ is not found.
+
+Fri Feb 21 13:39:25 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * string.c (rb_str_cmp_m): should use LONG2NUM().
+
+Fri Feb 21 12:45:50 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * string.c (rb_str_cmp_m): two small bugs fixed.
+
+Fri Feb 21 08:03:09 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * gc.c (rb_gc_mark): inline rb_gc_mark_children().
+
+ * gc.c (gc_sweep): new tactics to increase malloc_limit mildly.
+
+Fri Feb 21 05:16:14 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * string.c (rb_str_cmp_m): return nil if str2 does not respond to
+ both "to_str" and "<=>".
+
+ * compar.c (cmp_gt): return nil if "<=>" returns nil (means
+ incomparable).
+
+ * compar.c (cmp_ge, cmp_lt, cmp_le): ditto.
+
+ * compar.c (cmp_between): use RTEST(), since cmp_lt and cmp_gt may
+ return nil.
+
+Thu Feb 20 19:05:51 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_thread_start_0): main thread swapped by fork() may
+ terminate rb_thread_start_0() successfully. call ruby_stop(0);
+ this change was suggested by Rudi Cilibrasi
+ <cilibrar@drachma.ugcs.caltech.edu>.
+
+Thu Feb 20 18:44:51 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * file.c (file_expand_path): fix wrong behavior for root file.
+ expand_path("..", "//machine/share") => "//machine/share"
+ expand_path("..", "c:/a") => "c:/"
+ expand_path("..", "/a") => "/"
+
+Thu Feb 20 18:11:01 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * file.c (file_expand_path): should not upward beyond share name.
+
+Thu Feb 20 15:45:33 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * missing.h (strtoul): fix prototype of strtoul.
+
+Thu Feb 20 10:11:30 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * parse.y (clhs): allow "Foo::Bar = x".
+
+Thu Feb 20 04:07:06 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * parse.y (primary): "self[n]=x" can be legal even when "[]=" is
+ private. changes submitted in [ruby-talk:63982]
+
+ * parse.y (aryset): ditto.
+
+ * parse.y (attrset): "self.foo=x" can be legal even when "foo="
+ is private.
+
+ * eval.c (is_defined): private "[]=" and "foo=" support.
+
+ * eval.c (rb_eval, assign): ditto.
+
+Thu Feb 20 03:58:34 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_eval): "foo=" should not always be public.
+
+Thu Feb 20 01:23:59 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (rb_thread_restore_context): inhibit interrupts in
+ critical section while context switching. [ruby-talk:64785]
+
+Wed Feb 19 18:27:42 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * node.h (nd_cpath): nested class/module declaration.
+ [EXPERIMENTAL]
+
+ * eval.c (rb_eval): ditto.
+
+ * gc.c (rb_gc_mark_children): ditto.
+
+ * parse.y (cpath): ditto.
+
+Tue Feb 18 21:39:27 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_call0): should not report uninitialized warning by
+ attribute reader method.
+
+ * variable.c (rb_attr_get): new function to get instance variable
+ without uninitialized warning.
+
+ * io.c (argf_to_io): should prefetch argv.
+
+Tue Feb 18 00:13:50 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * misc/ruby-mode.el (ruby-comment-column): customize comment
+ column. [new]
+
+ * misc/ruby-mode.el (ruby-deep-indent-paren): deep indentation
+ parentheses. [new]
+
+ * misc/ruby-mode.el (ruby-expr-beg): fix for / after $?.
+
+ * misc/ruby-mode.el (ruby-parse-partial, ruby-calculate-indent):
+ deep indentation support.
+
+ * misc/ruby-mode.el (ruby-forward-sexp, ruby-backward-sexp):
+ move forward/backward across one balanced expression. [new]
+
+ * misc/ruby-mode.el (ruby-indent-exp): indent balanced
+ expression. [new]
+
+ * misc/ruby-mode.el (ruby-electric-brace): indent before
+ show matching parenthesis. (contributed by NABEYA Kenichi)
+
+Mon Feb 17 14:36:56 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * win32/win32.c (rb_w32_opendir, rb_w32_utime): need parens.
+
+Mon Feb 17 14:13:25 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * win32/win32.c (link): implement with CreateHardLink().
+
+ * win32/win32.c, win32/win32.h (rb_w32_utime): enable utime() to
+ directory if on NT. [new] (ruby-bugs-ja:PR#393)
+
+Mon Feb 17 13:28:51 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * file.c (file_expand_path): strip last slash when path is
+ root.
+
+Sun Feb 16 19:22:31 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * file.c (file_expand_path): buffer might be reallocated while
+ expanding default directory.
+
+ * file.c (file_expand_path): default directory was being
+ ignored if path was full path with no drive letter, under
+ DOSISH.
+
+Sun Feb 16 03:14:33 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * io.c (prep_stdio, Init_io): always set binmode on Cygwin.
+
+Sat Feb 15 01:01:45 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * file.c (file_expand_path): fix surplus path separators while
+ expanding at root directory. [ruby-dev:19572]
+
+Fri Feb 14 14:25:24 2003 akira yamada <akira@arika.org>
+
+ * lib/uri/generic.rb, lib/uri/ldap.rb, lib/uri/mailto.ldap: all foo=()
+ returns arguments passed by caller.
+
+ * lib/uri/generic.rb (Generic#to_str, Generic#to_s): removed to_str.
+ Suggested by Tanaka Akira <akr@m17n.org> at [ruby-dev:19475].
+
+ * lib/uri/generic.rb (Generic#==): should not generate an URI object
+ from argument. Suggested by Tanaka Akira <akr@m17n.org> at
+ [ruby-dev:19475].
+
+Thu Feb 13 11:54:50 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * ruby.c (ruby_init_loadpath): ensures buffer terminated
+ before use strncpy().
+
+ * ruby.c (proc_options): avoid SEGV at -S with no arguments.
+ script argument is in effect only when -e is not given.
+ (ruby-bugs-ja:PR#391)
+
+Thu Feb 13 01:30:10 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (rb_thread_schedule): current thread may be dead when
+ deadlock. (ruby-bugs:PR#588)
+
+Thu Feb 13 00:28:52 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * range.c (range_step): step might be float 0 < x < 1.
+
+ * eval.c (rb_thread_schedule): pause if no runnable thread when
+ there's only one thread.
+
+Thu Feb 13 00:09:47 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * file.c (strrdirsep): ignore trailing directory separators.
+
+ * file.c (rb_file_s_expand_path): File.expand_path(".","/") should
+ return "/". (ruby-bugs-ja:PR#389)
+
+ * file.c (rb_file_s_basename): also ignore trailing directory
+ separators, in compliance with SUSv3. (ruby-bugs-ja:PR#390)
+
+ * file.c (rb_file_s_dirname, rb_file_s_extname): ditto.
+
+ * file.c (rb_file_s_dirname): append "." if drive only.
+
+ * file.c (rb_file_s_split): get rid of converting twice.
+
+Mon Feb 10 20:55:15 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * ext/extmk.rb (parse_args): add '-n' to $mflags BEFORE "--".
+ do not add DESTDIR if already included in $mflags.
+
+Mon Feb 10 19:54:30 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/fileutils.rb (FileUtils#uptodate?): use mtime for
+ comparison.
+
+Mon Feb 10 10:14:26 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * array.c (rb_ary_to_a): return value should be an Array if the
+ receiver is an instance of subclass of Array.
+
+ * string.c (rb_str_to_s): return value should be a String if the
+ receiver is an instance of subclass of String.
+
+Mon Feb 10 03:33:42 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * io.c (rb_file_sysopen): rb_file_sysopen_internal() needs four
+ arguments.
+
+Sun Feb 9 15:16:04 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * intern.h (HAVE_RB_DEFINE_ALLOC_FUNC, RB_CVAR_SET_4ARGS):
+ define to 1.
+
+ * ruby.h (NORETURN_STYLE_NEW): ditto.
+
+Sun Feb 9 12:28:18 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * lib/mkmf.rb (init_mkmf): add libdir to LIBPATH unless cross
+ compiling.
+
+Sun Feb 9 08:34:45 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/net/http.rb: 4xx raises Net::ProtoServerError, 5xx raises
+ Net::ProtoFatalError (for backward compatibility).
+
+Sun Feb 9 07:07:26 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/fileutils.rb: new method FileUtils.pwd (really).
+
+ * lib/fileutils.rb: FileUtils.pwd, cmp, identical?, uptodate? does
+ not accept any option.
+
+Sat Feb 8 18:35:30 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * misc/ruby-mode.el (ruby-forward-string): fixed void variable
+ error.
+
+Sat Feb 8 16:23:11 2003 NABEYA Kenichi <kenichi@nabeya.com>
+
+ * misc/ruby-mode.el (ruby-font-lock-keywords): method name can
+ be delimited by tab.
+
+Sat Feb 8 03:57:32 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * lib/irb/workspace.rb, lib/irb/ext/math-mode.rb,
+ lib/irb/ext/multi-irb.rb, lib/irb/lc/error.rb,
+ lib/irb/lc/help-message, lib/irb/lc/ja/error.rb,
+ lib/shell/command-processor.rb, lib/shell/error.rb,
+ lib/shell/filter.rb: Fix typos and grammos. [approved by: keiju]
+
+Sat Feb 8 03:34:28 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * intern.h (HAVE_RB_DEFINE_ALLOC_FUNC): New boolean macro to make
+ it easier to write extensions that work with both ~1.6 and 1.8~.
+
+ * intern.h (RB_CVAR_SET_4ARGS): Ditto.
+
+ * ruby.h (NORETURN_STYLE_NEW): Ditto.
+
+Sat Feb 8 00:47:24 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_call): calls method_missing when superclass method
+ does not exist.
+
+ * eval.c (rb_f_missing): now handles "no super" case.
+
+ * object.c (rb_obj_ivar_get): Object#instance_variable_get: new
+ method to get instance variable value without eval(). [new]
+
+ * object.c (rb_obj_ivar_set): Object#instance_variable_set: new
+ method to set instance variable value without eval(). [new]
+
+Fri Feb 7 15:35:21 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * intern.h, re.c (rb_memsearch): returns long.
+
+ * string.c (rb_str_index): should return offset position.
+
+Fri Feb 7 15:30:15 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (proc_invoke): should propagate self to super
+ methods. [ruby-dev:19510]
+
+Thu Feb 6 19:04:32 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * re.c (rb_reg_initialize_m): should not preset "kcode" unless
+ encoding is explicitly specified.
+
+Thu Feb 6 19:01:32 2003 Minero Aoki <aamine@loveruby.net>
+
+ * lib/fileutils.rb: new method FileUtils.pwd.
+
+ * lib/fileutils.rb: default label is ''.
+
+ * lib/fileutils.rb: using module_eval again, to avoid ruby's bug.
+
+ * lib/fileutils.rb: fix wrong examples in rdoc.
+
+Thu Feb 6 17:43:56 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * lib/complex.rb (Complex#==): should not raise error by type
+ mismatch.
+
+ * lib/rational.rb (Rational#==): ditto.
+
+Thu Feb 6 11:44:40 2003 MoonWolf <moonwolf@moonwolf.com>
+
+ * re.c (rb_reg_initialize_m): 3rd argument was ignored.
+
+Thu Feb 6 01:09:05 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * string.c (rb_str_count): return 0 for empty string (was
+ returning nil).
+
+Wed Feb 5 19:41:37 2003 Tanaka Akira <akr@m17n.org>
+
+ * lib/open-uri.rb: dispatch code restructured to make it openable
+ that has `open' method.
+
+ * lib/open-uri.rb: Location: field may has a relative URI.
+ pointed out by erik eriksson <ee@opera.com>.
+
+Wed Feb 5 17:11:02 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * parse.y (yylex): no .<digit> float literal anymore.
+
+Tue Feb 4 16:11:30 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * array.c (rb_ary_equal): a == b is true when b is non T_ARRAY
+ object, if b has "to_ary" and b == a.
+
+ * hash.c (rb_hash_equal): a == b is true when b is non T_HASH
+ object, if b has "to_hash" and b == a.
+
+ * string.c (rb_str_equal): a == b is true when b is non T_STRING
+ object, if b has "to_str" and b == a.
+
+Mon Feb 3 23:46:48 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * io.c (argf_getline): should not increment lineno at EOF.
+
+Mon Feb 3 16:49:19 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * object.c (Init_Object): default Object#=== now calls "=="
+ internally.
+
+ * re.c (rb_reg_initialize_m): should honor option status of
+ original regexp.
+
+ * array.c (rb_ary_equal): ary2 should be T_ARRAY (no to_ary
+ conversion).
+
+ * array.c (rb_ary_eql): ditto.
+
+ * string.c (rb_str_equal): str2 should be T_STRING (no to_str
+ conversion).
+
+Mon Feb 3 16:32:52 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * re.c (rb_memsearch): a little improvement.
+
+Mon Feb 3 13:18:05 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * re.c (rb_memsearch): algorithm body of String#index.
+
+ * error.c (Init_Exception): "to_str" removed.
+
+ * eval.c (eval): should not rely on Exception#to_str
+
+ * eval.c (compile_error): ditto.
+
+ * error.c (err_append): ditto.
+
+Sat Feb 1 23:56:29 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * hash.c (rb_hash_merge): Hash#merge, non destructive "update".
+ now there's also Hash#merge! which is an alias to "update".
+
+Fri Jan 31 14:16:59 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * string.c (rb_str_index): search using Karp-Rabin algorithm.
+
+Fri Jan 31 12:45:11 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * variable.c (rb_obj_classname): new function.
+
+ * string.c (rb_str_dup): should preserve original's class (but not
+ hidden singleton class).
+
+ * string.c (rb_str_substr): ditto.
+
+ * parse.y: backout EXPR_CMDARG removal.
+
+Fri Jan 31 09:40:07 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * lib/optparse.rb (OptionParser::List::accept): default
+ pattern must not be nil.
+
+ * lib/optparse.rb (OptionParser::make_switch): NoArgument doesn't
+ override other styles.
+
+Thu Jan 30 16:46:43 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * lib/optparse.rb (OptionParser::Switch::PlacedArgument): added.
+ if the next argument doesn't start with '-', use it as the
+ value.
+
+ * lib/optparse.rb (OptionParser::make_switch): fixed a bug of
+ pattern.
+
+ * lib/optparse.rb (Array): no need to guard.
+
+Thu Jan 30 08:27:19 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * file.c (rb_file_s_expand_path): removed a sludge.
+
+Wed Jan 29 03:24:39 2003 Michal Rokos <michal@rokos.homeip.net>
+
+ * dir.c (glob_helper): memory leak fixed.
+
+Tue Jan 28 04:45:03 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * instruby.rb (parse_args), ext/extmk.rb (parse_args): Prepend a
+ hyphen to the first argument of MAKEFLAGS only if appropriate.
+ Remove wrong comments.
+
+Mon Jan 27 03:30:06 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * error.c (get_syserror): use snprintf() instead of sprintf(). pointed
+ out by knu.
+
+Mon Jan 27 02:06:38 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * error.c (get_syserror): some Windows' errno have 5 digits. pointed
+ out by znz.
+
+Sun Jan 26 19:23:10 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * instruby.rb ($mflags.set?): Check $make instead of $nmake, since
+ there is no such a variable.
+
+ * instruby.rb ($mflags.set?), ext/extmk.rb ($mflags.set?): Return
+ false if unmatched.
+
+Sun Jan 26 19:08:30 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * lib/shellwords.rb: Embed rdoc style comments.
+
+ * lib/shellwords.rb (shellwords): Use String#lstrip!.
+
+ * lib/shellwords.rb (shellwords): Recognize an object that
+ responds to to_str() by using String.new().
+
+Sun Jan 26 17:53:04 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * instruby.rb (parse_args), ext/extmk.rb (parse_args): Detect -n
+ and emulate a dry run. Use 'make' in case no --make argument is
+ given.
+
+Sun Jan 26 07:18:42 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * instruby.rb: re-define individual methods verbosely rather than
+ including FileUtils::Verbose, in order to suppress messages from
+ FileUtils#cmp.
+
+ * instruby.rb (makedirs): make same directory only once even if
+ dryrun.
+
+ * lib/fileutils.rb (FileUtils::Verbose, FileUtils::NoWrite):
+ re-define methods with define_method instead of module_eval.
+
+Sun Jan 26 03:37:18 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * instruby.rb, ext/extmk.rb, Makefile.in, win32/Makefile.sub,
+ bcc32/Makefile.sub: Replace the complicated MFLAGS/MAKEFLAGS
+ parser with something plain and comprehensible. This fixes a
+ bug where make flags were wrongly reordered and the resulted
+ command line often did not make sense especially when BSD make
+ is used with extra arguments given. Tested with FreeBSD and
+ Linux by me and mswin32, bccwin32 and mingw by usa.
+
+Fri Jan 24 18:15:33 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * parse.y: tMINUS should have lower precedence than tPOW.
+
+Fri Jan 24 05:12:55 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * misc/ruby-mode.el (ruby-font-lock-syntactic-keywords): deal
+ with escaped $ and ? at the end of strings. [ruby-talk:62297]
+
+ * misc/ruby-mode.el (ruby-font-lock-keywords): added defined?.
+
+Thu Jan 23 17:25:04 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_eval): do not warn discarding already undefined
+ method.
+
+ * lib/rational.rb: undef quo before replacing.
+
+Thu Jan 23 15:49:57 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * parse.y (arg): missing arguments.
+
+Thu Jan 23 14:56:52 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * lib/rational.rb: modified to support "quo".
+
+ * numeric.c (num_quo): should return most exact quotient value,
+ i.e. float by default, rational if available.
+
+ * numeric.c (num_div): "div" should return x.divmod(x)[0].
+
+Thu Jan 23 13:24:18 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * time.c (time_arg): was accessing garbage argv value.
+
+Thu Jan 23 06:37:01 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * instruby.rb: should not contain destdir in shebang line.
+
+Wed Jan 22 23:19:57 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * win32/win32.c (pipe_exec): remove unnecessary SetStdHandle().
+
+Wed Jan 22 20:20:59 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * parse.y (arg): syntaxify tPOW negative number hack.
+
+ * parse.y (negate_lit): new function to negate literal numeric
+ values in compile time.
+
+Wed Jan 22 15:36:54 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * regex.c (re_match_exec): charset info may be stored in MBC
+ region when $KCODE != NONE.
+
+Wed Jan 22 14:22:53 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * error.c (set_syserr): should preserve duplicated error names.
+
+Tue Jan 21 20:29:31 2003 Michal Rokos <michal@rokos.homeip.net>
+
+ * mkmf.rb: make possible to add files to clean and distclean targets
+
+Tue Jan 21 18:05:25 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * bcc32/Makefile.sub (LIBRUBY_A): link dmyext.
+
+Tue Jan 21 16:59:18 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * instruby.rb: use real interpreter pathname at shebang line.
+ [ruby-dev:19370]
+
+Tue Jan 21 16:22:32 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * parse.y (arg): put back old ** behavior for negative number
+ right operand.
+
+Tue Jan 21 14:46:12 2003 Tanaka Akira <akr@m17n.org>
+
+ * lib/pp.rb: Use Test::Unit.
+
+ * lib/prettyprint.rb: Ditto
+
+ * lib/time.rb: Ditto
+
+ * lib/tsort.rb: Ditto
+
+Tue Jan 21 04:15:50 2003 Tanaka Akira <akr@m17n.org>
+
+ * lib/pp.rb: Use redefined `to_s' as well as `inspect'.
+ Useless `pretty_print' methods removed.
+ (PP::ObjectMixin#pretty_print_inspect): new method.
+
+Mon Jan 20 21:48:43 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * configure.in (MANTYPE): Detect if the system's nroff(1) groks
+ mdoc. Provide a new option --with-mantype={doc|man} in case the
+ check does not work as expected.
+
+ * Makefile.in (MANTYPE): Define MANTYPE and pass it to
+ instruby.rb.
+
+ * instruby.rb: Convert mdoc manpages to man for systems which
+ nroff(1) does not grok mdoc.
+
+Mon Jan 20 21:25:18 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * lib/tempfile.rb (self.open): If a block is given, call it with
+ tempfile as an argument and automatically close the tempfile
+ when the block terminates.
+
+Mon Jan 20 21:02:50 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * mdoc2man.rb: Properly put nested braces, parentheses and angles.
+
+ * mdoc2man.rb: Add support for .An and .Aq/.Ao/.Ac.
+
+ * mdoc2man.rb: Add support for .Dl.
+
+ * mdoc2man.rb: Make .Pf macro actually work.
+
+ * mdoc2man.rb: Properly handle .Os.
+
+ * mdoc2man.rb: Correctly omit spaces around punctuation
+ characters.
+
+Mon Jan 20 19:43:41 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * mdoc2man.rb: Make this work as a library.
+
+Mon Jan 20 18:22:40 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (rb_f_require): purge too many goto's.
+
+Mon Jan 20 17:50:05 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * mdoc2man.rb (parse_macro): Understand .Ux.
+
+Mon Jan 20 17:32:56 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * mdoc2man.rb: New file. A mdoc to man converter ported from
+ Perl.
+
+Mon Jan 20 15:40:15 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * ruby.1: Properly close .Bl with .El.
+
+Mon Jan 20 04:14:17 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * lib/mkmf.rb (egrep_cpp): use inspect to show options.
+
+ * lib/mkmf.rb (dir_config): prior configured directories to
+ defaults.
+
+ * lib/mkmf.rb (dir_config): extract first word to determine
+ make command type.
+
+Mon Jan 20 02:15:53 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * ext/aix_mksym.rb: no longer used.
+
+Mon Jan 20 00:17:16 2003 Matt Armstrong <matt@lickey.com>
+
+ * file.c (eaccess): under windows, make eaccess() just call
+ access(). [ruby-core:716], [ruby-bugs:PR#556]
+
+Sun Jan 19 23:08:18 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * lib/shellwords.rb (shellwords): A backslash ('\') in single
+ quotes should not be regarded as meta character. This bug or
+ maybe feature was inherited from Perl's shellwords.pl.
+
+Sun Jan 19 14:01:12 2003 UENO Katsuhiro <unnie@blue.sky.or.jp>
+
+ * regex.c (is_in_list): should work well with UTF-8.
+
+ * regex.c (re_match_exec): ditto.
+
+Sat Jan 18 14:53:49 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * bignum.c (rb_cstr_to_inum): should not erase all 0s, but
+ squeeze into one. [ruby-dev:19377]
+
+Fri Jan 17 03:33:42 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * sprintf.c (rb_f_sprintf): Fix a bug caused by an uninitialized
+ variable v, that a bignum unexpectedly gets converted into a
+ string with its higher figures all filled with ./f/7/1,
+ depending on the base. This bug seems to have been introduced
+ in rev.1.27.
+
+ * sprintf.c (rb_f_sprintf): Use switch instead of a sequence of
+ else-if's.
+
+Wed Jan 15 15:18:38 2003 moumar <moumar@netcourrier.com>
+
+ * configure.in (ARCHFILE): set even unless --enable-shared on
+ AIX. [ruby-talk:61466]
+
+ * marshal.c (math.h): should be included after ruby.h on AIX.
+ [ruby-talk:61366]
+
+Tue Jan 14 21:47:56 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (rb_f_require): do not search adding .rb/.so suffixes if
+ the suffix specified. [ruby-dev:18702]
+ http://moonrock.jp/~don/d/200211.html#d08_t1
+
+Tue Jan 14 18:36:41 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * enum.c (enum_all): now works without block.
+
+ * enum.c (enum_any): ditto.
+
+Tue Jan 14 01:21:32 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * io.c (next_argv): not always set binmode.
+
+Mon Jan 13 20:45:19 2003 Guy Decoux <ts@moulon.inra.fr>
+
+ * parse.y (list_append): avoid O(n) search using node->nd_next->nd_end.
+
+ * parse.y (list_concat): ditto.
+
+ * eval.c (rb_eval): NODE_ARRY nd_end adoption.
+
+Mon Jan 13 02:22:11 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * ext/dl/lib/dl/win32.rb: eliminate unnecessary "A" adding.
+
+Sun Jan 12 16:07:17 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * io.c (next_argv): inherit binmode from $defout.
+
+Sat Jan 11 22:50:47 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * ext/dl/lib/dl/win32.rb: compatibility improvement.
+
+Sat Jan 11 01:44:16 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * configure.in (RUBY_CHECK_IO_NEED): added more tests.
+
+ * io.c (rb_io_check_readable): seek after synchronized write.
+
+Fri Jan 10 01:23:45 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * misc/ruby-mode.el (ruby-font-lock-syntactic-keywords): syntax
+ classes are not allowed inside character classes.
+ [ruby-talk:60996]
+
+Thu Jan 9 23:28:01 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * configure.in: AC_MSG_FAILURE is a new macro in 2.54b or later.
+
+Thu Jan 9 17:05:24 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * configure.in (RUBY_CHECK_IO_NEED): check whether fseek() and
+ fflush() are needed.
+
+ * io.c (flush_before_seek): flush write stream only.
+
+ * io.c (rb_io_check_readable): seek instead of flush if the last
+ operation was write.
+
+ * io.c (rb_io_check_writable): seek instead of flush if the last
+ operation was read.
+
+ * bcc32/Makefile.sub, win32/Makefile.sub: needs to seek between
+ R/W.
+
+Thu Jan 9 16:31:51 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_eval): should not discard nested NODE_BLOCK.
+
+Thu Jan 9 15:12:30 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * parse.y (stmt): NODE_NOT elimination for if/unless/while/until node.
+
+ * parse.y (primary): ditto.
+
+Thu Jan 9 13:26:18 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * st.h, st.c: Back out the introduction of st_*_func_t. Some
+ compilers complain about function type mismatch.
+
+Thu Jan 9 02:10:44 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_eval): reduce recursive rb_eval() call by using sort
+ of continuation passing style.
+
+Wed Jan 8 17:10:32 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * ext/Win32API/lib/win32/registry.rb: added. [new]
+
+Wed Jan 8 15:54:05 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c: remove ruby_last_node and assignments seems to be
+ unnecessary
+
+ * intern.h: debug does not run if ID_ALLOCATOR is zero.
+
+Wed Jan 8 15:04:11 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * range.c (range_each): treat fixnums specially to boost.
+
+ * numeric.c (num_step): remove rb_scan_args() for small speedup.
+
+Tue Jan 7 17:56:08 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (svalue_to_avalue): should return converted array.
+
+Tue Jan 7 07:48:01 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (rb_f_local_variables): skip $_, $~ and flip states in
+ dynamic variables. [ruby-core:00681]
+
+Tue Jan 7 02:46:29 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * hash.c (env_clear): new Hash compatible method.
+
+ * hash.c (env_shift, env_invert, env_replace, env_update): ditto.
+
+Mon Jan 6 23:36:29 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * st.h, st.c: Introduce new conventional typedef's, st_data_t,
+ st_compare_func_t, st_hash_func_t and st_each_func_t.
+
+ * st.h, st.c: Do explicit function declarations and do not rely on
+ implicit declarations.
+
+ * class.c, eval.c, gc.c, hash.c, marshal.c, parse.y, variable.c:
+ Add proper casts to avoid warnings.
+
+Mon Jan 6 20:44:43 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * intern.h (rb_check_array_type): Declare rb_check_array_type().
+
+ * ext/digest/md5/md5ossl.c: Include stdio.h for sprintf() and
+ string.h for memcmp().
+
+ * ext/dl/ptr.c: Include ctype.h for isdigit().
+
+Mon Jan 6 18:43:17 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * file.c: improve DOSISH drive letter support.
+
+Mon Jan 6 18:31:45 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * lib/fileutils.rb (ln): add ' -f' in the verbose message.
+
+ * lib/fileutils.rb (cp_r): add 'p' in the verbose message.
+
+Mon Jan 6 16:44:52 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * array.c (rb_ary_join): dispatch based on "to_str".
+
+ * array.c (rb_ary_times, rb_ary_equal): ditto.
+
+Mon Jan 6 13:26:35 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * process.c (proc_exec_v): follow to proc_spawn_v(). call do_aspawn()
+ on Win32.
+
+ * process.c (rb_proc_exec): call do_spawn() on Win32.
+
+ * win32/win32.c, win32/win32.h (do_spawn, do_aspawn): add mode flag.
+
+ * process.c (proc_spawn_v, rb_f_system): follow above change.
+
+Mon Jan 6 05:11:15 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * ext/extmk.rb: make $0 normal variable.
+
+Mon Jan 6 02:32:46 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * struct.c (make_struct): needs meta class.
+
+Sun Jan 5 22:54:05 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * lib/fileutils.rb (ln): `argv' is not a argument.
+
+Sun Jan 5 17:44:37 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * ext/extmk.rb (extmake): set $0 temporarily while loading
+ extconf.rb.
+
+Sun Jan 5 14:46:46 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * instruby.rb: need paren in regexp(make -n install).
+
+ * ext/extmk.rb (sysquote): do not need to quote on mswin/bccwin/mingw.
+
+ * ext/extmk.rb ($mflags): uniq items and remove '-' and '--'.
+ move options to the lead.
+
+ * lib/fileutils.rb (install): model on the real install
+ command(message).
+
+Sun Jan 5 09:36:46 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * ruby.c (ruby_init_loadpath): under Windows, get the module
+ path from an internal address instead of hard coded library
+ name.
+
+ * cygwin/GNUmakefile.in, bcc32/Makefile.sub,
+ win32/Makefile.sub (CPPFLAGS): removed LIBRUBY_SO macro.
+
+ * bcc32/Makefile.sub, win32/Makefile.sub (config.h): no longer
+ depends on makefiles.
+
+Sun Jan 5 04:17:05 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * gc.c (SET_STACK_END): Issue a FLUSH_REGISTER_WINDOWS here too.
+ This fixes make test on FreeBSD/sparc64.
+
+Sun Jan 5 03:43:47 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * defines.h (FLUSH_REGISTER_WINDOWS): Make the flushw call an
+ inline function so it can be used as an expression.
+
+ * eval.c (EXEC_TAG, THREAD_SAVE_CONTEXT): Consistently call
+ FLUSH_REGISTER_WINDOWS before calling setjmp(). (I suspect that
+ every setjmp() implementation should take care of register
+ windows, though)
+
+Sun Jan 5 03:12:32 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * file.c (utimbuf): use utimbuf instead of _utimbuf if defined _WIN32.
+
+ * win32/Makefile.sub (LIBS): use oldnames.lib.
+
+ * win32/win32.c (rb_w32_getcwd): follow above change.
+
+ * win32/win32.h: ditto.
+
+ * wince/direct.c, wince/direct.h (getcwd): ditto.
+
+ * wince/io.h: ditto.
+
+ * wince/string.c, wince/wince.h (stricmp, strnicmp): ditto.
+
+Sat Jan 4 15:18:50 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * process.c (rb_proc_exec): use same logic as DJGPP on win32 ports.
+
+ * process.c (rb_f_system): ditto.
+
+ * win32/win32.c, win32/win32.h (do_aspawn): [new]. for arrayed
+ arguments.
+
+ * win32/win32.c (CreateChild): add new argument for real filename of
+ executing process.
+
+ * win32/win32.c (NtHasRedirection, pipe_exec): follow above change.
+
+Sat Jan 4 14:29:52 2003 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * configure.in: set rb_cv_need_io_flush_between_seek=yes.
+
+ * win32/Makefile.sub (config.h): define NEED_IO_FLUSH_BETWEE_SEEK.
+ (pointed out by moriq [ruby-dev:19299])
+
+Sat Jan 4 03:12:14 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (umethod_bind): exact class match is not required. relax
+ the restriction to subclasses.
+
+Sat Jan 4 01:33:40 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * file.c (rb_file_s_lchmod): get rid of gcc-3 -O3 warning.
+
+Fri Jan 3 22:26:07 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * process.c (rb_proc_times): need to initialize first.
+
+Fri Jan 3 01:10:17 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_eval): call "inherited" before executing class body.
+
+ * class.c (rb_define_class): call "inherited" after defining the
+ constant.
+
+ * class.c (rb_define_class_under): ditto.
+
+Thu Jan 2 19:37:30 2003 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (massign): expand first element if RHS is an array and
+ its size is 1, and LHS has concrete assignment target (i.e. LHS
+ has target(s) other than *var).
+
+ * eval.c (massign): avoid unnecessary avalue/svalue conversion.
+
+ * eval.c (rb_yield_0): ditto
+
+ * array.c (rb_ary_update): do not allocate unused array if rpl is
+ nil (i.e. merely removing elements).
+
+Thu Jan 2 13:55:08 2003 Mathieu Bouchard <matju@sympatico.ca>
+
+ * io.c (io_read): should resize supplied string if it's shorter
+ than expected.
+
+Thu Jan 2 11:01:20 2003 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (bmcall): arguments should be an array.
+
+Wed Jan 1 18:18:45 2003 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * configure.in: better DJGPP support. add GNUmakefile.
+
+ * djgpp/GNUmakefile: new.
+
+Wed Jan 1 04:16:18 2003 Akinori MUSHA <knu@iDaemons.org>
+
+ * node.h (struct RNode): Change argc from int to long. Otherwise
+ NEW_CFUNC() sets argc to a wrong value on platforms where
+ sizeof(int) != sizeof(long) and the byte order is big-endian.
+ This fixes breakage on FreeBSD/sparc64.
+
+Tue Dec 31 23:22:50 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (massign): removed awkward conversion between yvalue,
+ mvalue, etc.
+
+ * eval.c (rb_yield_0): new parameter added to tell whether val is
+ an array value or not.
+
+ * parse.y (yield_args): restructuring: new nodes: NODE_RESTARY2,
+ NODE_SVALUE; removed node: NODE_RESTARGS.
+
+Tue Dec 31 21:13:51 2002 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * Makefile.in, {win32,bcc32}/Makefile.sub: add new target:
+ what-where, no-install.
+
+ * mkconfig.rb: add const: CROSS_COMPILING.
+
+ * ext/extmk.rb: no-install support. add MAKEDIRS macro.
+
+ * lib/mkmf.rb: add !ifdef .. !endif for Borland make.
+
+ * process.c: improve DJGPP support. system "ls", "-l".
+
+Tue Dec 31 20:16:37 2002 Akinori MUSHA <knu@iDaemons.org>
+
+ * ext/socket/addrinfo.h (NI_MAXHOST): Define NI_MAXHOST and
+ NI_MAXSERV only if they are not defined yet. This fixes build
+ on such platforms as OpenBSD.
+
+Tue Dec 31 20:07:49 2002 Akinori MUSHA <knu@iDaemons.org>
+
+ * ext/tcltklib/extconf.rb (find_tcl, find_tk): Look for both
+ lib{tcl,tk}M.N and lib{tcl,tk}MN on all platforms. *BSD have
+ Tcl/Tk libraries named this way.
+
+Tue Dec 31 19:48:21 2002 Akinori MUSHA <knu@iDaemons.org>
+
+ * configure.in: Improve OpenBSD support. [obtained from: OpenBSD
+ ports]
+
+ * dln.c (FUNCNAME_PATTERN): Ditto.
+
+Tue Dec 31 19:21:02 2002 Akinori MUSHA <knu@iDaemons.org>
+
+ * array.c (rb_ary_transpose): Properly declare ary as a VALUE.
+
+ * file.c (rb_file_s_chmod): Do not directly cast an int to void *
+ to avoid a warning.
+
+ * defines.h (FLUSH_REGISTER_WINDOWS): Add support for
+ FreeBSD/sparc64. miniruby still coredumps in a different place,
+ though.
+
+Tue Dec 31 07:47:15 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * parse.y (parse_string): readjusted.
+
+ * parse.y (heredoc_identifier): readjusted.
+
+ * parse.y (here_document): make EOL codes of single-quoted
+ here-documents consistent.
+
+ * parse.y (yylex): reduced unnecessary conditionals.
+
+Tue Dec 31 04:49:51 2002 Akinori MUSHA <knu@iDaemons.org>
+
+ * ruby.1: mdoc'ify.
+
+Tue Dec 31 01:30:29 2002 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * parse.y (yylex): do not accept " __END__\n". ([ruby-dev:19245])
+
+Mon Dec 30 21:10:59 2002 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * parse.y (yylex): use strncmp instead of strcmp.
+ accept "__END__\r\n". ([ruby-dev:19241])
+
+Mon Dec 30 20:32:14 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * gc.c (rb_gc_mark_frame): should mark frame->node.
+
+Mon Dec 30 19:10:30 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * ext/extmk.rb: split --make argument contains options, assume
+ the first word of --make-flags is always options even unless
+ preceded by -, and ignore letter-case of options if nmake.
+
+ * instruby.rb: extract -n option also from --make and
+ --make-flags.
+
+ * bcc32/Makefile.sub, win32/Makefile.sub: not prepend - to
+ $(MFLAGS)
+
+Mon Dec 30 16:44:14 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * string.c (rb_str_substr): should share the shared string if
+ present, instead of the original string. (ruby-bugs:PR#528)
+
+Mon Dec 30 05:10:00 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * ext/socket/socket.c (tcp_svr_init): local host to
+ init_inetsock() is VALUE but not pointer.
+
+ * ext/socket/socket.c (sock_s_unpack_sockaddr_in): get rid of
+ gcc-3 -O3 warning.
+
+Sun Dec 29 23:45:53 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * gc.c (gc_sweep): adjust GC trigger.
+
+ * dln.c (init_funcname_len): get rid of gcc-3 -O3 warning.
+
+ * eval.c (copy_node_scope): ditto.
+
+ * hash.c (rb_hash_foreach, delete_if_i, select_i, each_value_i,
+ each_key_i, each_pair_i, envix): ditto.
+
+ * range.c (range_each_func): ditto.
+
+ * file.c (rb_file_s_chmod): ditto.
+
+Sun Dec 29 15:30:37 2002 Minero Aoki <aamine@loveruby.net>
+
+ * lib/fileutils.rb (fu_parseargs): should not inherit ftools.rb's
+ misfeature.
+
+Sun Dec 29 05:08:13 2002 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * lib/fileutils.rb (cmp): return false if file size differs.
+
+Sat Dec 28 19:21:24 2002 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * instruby.rb: remove junk args.
+
+ * lib/mkmf.rb (create_makefile): remove a trouble library
+ before making a shared library.
+
+ * win32/Makefile.sub: invoke instruby.rb with the --make-flags option.
+
+Sat Dec 28 03:09:58 2002 Wakou Aoyama <wakou@ruby-lang.org>
+
+ * lib/cgi.rb (CGI#[]): improvement. thanks to Kazuhiro NISHIYAMA
+ <zn@mbf.nifty.com>
+
+Sat Dec 28 00:34:03 2002 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * {win32,bcc32}/Makefile.sub: remove `=' from --make-flags options.
+ nmake quotes args if included `=' in args.
+
+ * instruby.rb: use getopts.rb.
+
+ * ext/dbm/extconf.rb (-DDBM_HDR): substitute ' with " to avoid
+ a error on Win32.
+
+ * ext/gdbm/gdbm.c: add prototypes to avoid VC++ warnings.
+
+Fri Dec 27 21:41:57 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * bcc32/setup.mak, win32/setup.mak(-prologue-): move srcdir from
+ CPP input or UNC path will be removed as a comment.
+
+Fri Dec 27 17:55:00 2002 Takaaki Uematsu <mail@uema2.cjb.net>
+
+ * wince/config, wince/configure.bat: replace 1.7 with 1.8
+ in macros.
+
+Fri Dec 27 13:28:14 2002 Minero Aoki <aamine@loveruby.net>
+
+ * instruby.rb: fileutils.rb accepts only one argument.
+
+Fri Dec 27 13:23:29 2002 Minero Aoki <aamine@loveruby.net>
+
+ * lib/fileutils.rb (fu_parseargs): reject illegal options
+ correctly.
+
+ * lib/fileutils.rb (uptodate?): parameter declaration was wrong.
+
+ * lib/fileutils.rb: change coding styles.
+
+Fri Dec 27 09:25:22 2002 ABE Shigeru <shiger-a@nifty.com>
+
+ * process.c (rb_proc_times): avoid WindowsXP crash using volatile
+ variables.
+
+Fri Dec 27 02:56:58 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * instruby.rb: check only `-' option, and use fileutils instead of
+ ftools.
+
+Fri Dec 27 02:45:17 2002 Wakou Aoyama <wakou@ruby-lang.org>
+
+ * lib/net/telnet.rb: Telnet#print not add "\n".
+
+ * lib/cgi.rb: cgi['key'] is equal cgi['key'][0]
+
+Thu Dec 26 22:33:18 2002 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * ext/extmk.rb (create_makefile): check only `-' option.
+
+ * configure.in: cleanups for MinGW. remove -D__NO_ISOCEXT in $CFLAGS.
+
+ * win32/win32.h: prototypes for isinf, isnan are not needed on MinGW.
+
+Thu Dec 26 19:22:00 2002 YOSHIDA Kazuhiro <moriq@moriq.com>
+
+ * win32/setup.mak (-prologue-): moved srcdir macro definition.
+ [ruby-win32:420].
+
+Wed Dec 25 18:26:44 2002 K.Kosako <kosako@sofnec.co.jp>
+
+ * regex.c (re_match): fixed wrong \G behavior. (ruby-bugs-ja:PR#377)
+
+Wed Dec 25 16:41:16 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * regex.c (re_match_exec): fix odd \G behavior based on the patch
+ from Nobu.
+
+Wed Dec 25 11:05:11 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * bcc32/setup.mak (-generic-): removed garbages.
+
+Wed Dec 25 10:36:20 2002 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * bcc32/Makefile.sub, win32/Makefile.sub (RUBY_SO_NAME, config.h):
+ use $(MAJOR) and $(MINOR). based on Nobu's patch. [ruby-win32:413]
+
+ * bcc32/setup.mak, win32/setup.mak (-prologue-): define MAJOR, MINOR
+ and TEENY from version.h. based on Nobu's patch. [ruby-win32:413]
+
+ * win32/Makefile.sub (config.h): add HAVE_FLOAT_H.
+
+ * win32/Makefile.sub (parse.obj): depend on win32/win32.h.
+
+Tue Dec 24 23:49:16 2002 Akinori MUSHA <knu@iDaemons.org>
+
+ * lib/irb/completion.rb: Use Object#class rather than Object#type.
+
+Tue Dec 24 23:37:40 2002 TADA Tadashi <sho@spc.gr.jp>
+
+ * lib/cgi.rb (Cookie::parse), lib/cgi-lib.rb (initialize): Do not
+ pass to split() a bare string longer than 2 characters as
+ separator.
+
+Tue Dec 24 19:19:24 2002 Tietew <tietew@tietew.net>
+
+ * numeric.c (DBL_MAX_10_EXP): fix typo. [ruby-dev:19175]
+
+Tue Dec 24 17:02:46 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (rb_undefined): use NoMethodError instead of fatal.
+
+Tue Dec 24 02:12:45 2002 Akinori MUSHA <knu@iDaemons.org>
+
+ * lib/README: Synchronize with reality.
+
+Tue Dec 24 02:05:51 2002 Akinori MUSHA <knu@iDaemons.org>
+
+ * MANIFEST, lib/README, lib/ipaddr.rb: Add ipaddr.rb from rough.
+
+Sun Dec 22 04:07:47 2002 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * ext/dbm/dbm.c (fdbm_alloc): allocator takes only one argument.
+
+Sun Dec 22 02:49:25 2002 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * array.c (ary_alloc), dir.c (dir_s_alloc), eval.c (thgroup_s_alloc),
+ file.c (rb_stat_s_alloc), hash.c (hash_alloc), io.c (io_alloc),
+ object.c (rb_module_s_alloc, rb_class_allocate_instance),
+ re.c (match_alloc, rb_reg_s_alloc), string.c (str_alloc),
+ time.c (time_s_alloc), ext/digest/digest.c (rb_digest_base_alloc),
+ ext/tcltklib/tcltklib.c (ip_alloc),
+ ext/win32ole/win32ole.c (fole_s_allocate, fev_s_allocate)
+ : add prototype to get rid of VC++ warnings.
+
+ * ext/sdbm/init.c (fsdbm_alloc): allocator takes only one argument.
+
+Sun Dec 22 00:36:43 2002 WATANABE Hirofumi <eban@ruby-lang.org>
+
+ * lib/mkmf.rb (create_makefile): accept pure ruby libraries.
+
+Sat Dec 21 23:59:42 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * class.c (ins_methods_i): should not show ID_ALLOCATOR.
+
+ * class.c (ins_methods_prot_i): ditto.
+
+ * class.c (ins_methods_priv_i): ditto.
+
+ * class.c (ins_methods_pub_i): ditto.
+
+ * eval.c (call_trace_func): ditto.
+
+ * eval.c (rb_undefined): ditto.
+
+Sat Dec 21 07:27:24 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * misc/ruby-mode.el (ruby-parse-partial): keywords must not be
+ preceded by @ or $.
+
+Fri Dec 20 20:29:04 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * ext/curses/curses.c, ext/dbm/dbm.c, ext/digest/digest.c,
+ ext/dl/handle.c, ext/dl/ptr.c, ext/dl/sym.c, ext/gdbm/gdbm.c,
+ ext/iconv/iconv.c, ext/sdbm/init.c, ext/stringio/stringio.c,
+ ext/strscan/strscan.c, ext/tcltklib/tcltklib.c,
+ ext/win32ole/win32ole.c: use rb_define_alloc_func().
+
+Fri Dec 20 18:29:04 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * io.c (rb_io_fwrite): separated from io_write().
+
+ * marshal.c (w_byten): use rb_io_fwrite() to support non-blocking
+ IO, and added error check.
+
+ * rubyio.h: prototypes; rb_io_fwrite
+
+Fri Dec 20 17:40:59 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * object.c (Init_Object): should not remove Class#allocate.
+
+ * lib/profiler.rb: separate profiling functions, without
+ trace_func and at_exit setting.
+
+Fri Dec 20 16:20:04 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * parse.y (do_block): split "do" block and tLBRACE_ARG block.
+
+ * parse.y (cmd_brace_block): new tLBRACE_ARG block rule
+
+ * parse.y (command): can take optional cmd_brace_block; use %prec
+ to resolve shift/reduce conflict. (ruby-bugs-ja PR#372)
+
+ * eval.c (ruby_finalize): trace_func should be cleared here (after
+ executing exit procs and finalizers).
+
+ * eval.c (rb_define_alloc_func): new allocation framework, based
+ on Nobu's work [ruby-dev:19116]. "allocate" method is no longer
+ used for object allocation.
+
+Fri Dec 20 05:06:49 2002 Akinori MUSHA <knu@iDaemons.org>
+
+ * lib/README, lib/cgi/ftplib.rb, lib/telnet.rb: Delete ftplib.rb
+ and telnet.rb. It has been quite some time sinc they were
+ obsoleted and made to emit warnings.
+
+Fri Dec 20 04:58:22 2002 Akinori MUSHA <knu@iDaemons.org>
+
+ * lib/tempfile.rb: Embed Rdoc style comments.
+
+ * lib/tempfile.rb: Add length as an alias for size.
+
+Fri Dec 20 03:57:32 2002 Akinori MUSHA <knu@iDaemons.org>
+
+ * lib/tempfile.rb: Add Tempfile#close!() as a shorthand for
+ Tempfile#close(true).
+
+ * lib/tempfile.rb: Add Tempfile#{unlink,delete}().
+
+Fri Dec 20 03:53:01 2002 Akinori MUSHA <knu@iDaemons.org>
+
+ * lib/README, lib/cgi/final.rb, lib/cgi/session.rb: Delete
+ final.rb, which was obsoleted long ago.
+
+Fri Dec 20 00:16:06 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * re.c (rb_reg_match_pre, rb_reg_match_post, match_to_a,
+ match_select): return instances of same class as the original
+ string. [ruby-dev:19119]
+
+Thu Dec 19 22:55:49 2002 NAKAMURA Usaku <usa@ruby-lang.org>
+
+ * numeric.c (DBL_EPSILON): fix typo.
+
+Thu Dec 19 22:35:20 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * eval.c (assign): avoid [BUG] at multiple attribute assignment.
+
+Thu Dec 19 01:00:09 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * numeric.c (num_step): use DBL_EPSILON.
+
+ * array.c (rb_check_array_type): new function: return an array
+ (convert if possible), or nil.
+
+ * string.c (rb_check_string_type): new function: return a string
+ (convert if possible), or nil.
+
+ * numeric.c (rb_dbl_cmp): returns nil if values are not
+ comparable.
+
+ * numeric.c (fix_cmp,flo_cmp): use rb_num_coerce_cmp()
+
+ * bignum.c (rb_big_cmp): ditto.
+
+ * numeric.c (rb_num_coerce_cmp): new coercing function for "<=>",
+ which does not raise TypeError.
+
+ * numeric.c (do_coerce): can be suppress exception now.
+
+ * object.c (rb_mod_cmp): should return nil for non class/module
+ objects.
+
+Thu Dec 19 04:21:10 2002 Akinori MUSHA <knu@iDaemons.org>
+
+ * lib/open-uri.rb: add a missing ||. (found by: ruby -wc)
+
+Wed Dec 18 17:53:05 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * re.c (rb_reg_eqq): return false if the argument is not a
+ string. now returns boolean value.
+
+ * class.c (rb_include_module): argument should be T_MODULE, not
+ T_class, nor T_ICLASS.
+
+Wed Dec 18 03:52:55 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * string.c (rb_str_new4): handle tail shared string.
+ (ruby-bugs-ja:PR#370)
+
+ * string.c (rb_str_dup_frozen): ditto.
+
+Tue Dec 17 21:08:29 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
+
+ * node.h (NODE_ATTRASGN): new node, assignment to attribute.
+ [ruby-core:00637].
+
+ * eval.c (is_defined, rb_eval): ditto.
+
+ * parse.y (attrset, node_assign): ditto.
+
+ * string.c (rb_str_substr): tail sharing. [ruby-core:00650]
+
+ * re.c (rb_reg_nth_match): ditto.
+
+Tue Dec 17 16:52:38 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * eval.c (is_defined): "defined?" should return "assignment" for
+ attribute assignment (e.g. a.foo=b) and indexed assignment
+ (e.g. a[2] = 44).
+
+ * parse.y (aryset): use NODE_ATTRASGN.
+
Tue Dec 17 04:03:45 2002 Tanaka Akira <akr@m17n.org>
- * lib/open-uri.rb: new file.
+ * lib/open-uri.rb: new file.
Tue Dec 17 00:28:19 2002 NAKAMURA Usaku <usa@ruby-lang.org>
@@ -19,10 +4952,10 @@ Sun Dec 15 21:16:44 2002 WATANABE Hirofumi <eban@ruby-lang.org>
Sat Dec 15 12:15:00 2002 Takaaki Uematsu <mail@uema2.cjb.net>
* configure.in, defines.h, dir.c, dir.h, dln.c, error.c,
- eval.c, file.c, hash.c, io.c, main.c, missing.c,
+ eval.c, file.c, hash.c, io.c, main.c, missing.c,
process.c, ruby.c, rubysig.h, signal.c, st.c, util.c, util.h,
bcc/Makefile.sub, win32/Makefile.sub, win32/win32.h,
- ext/Win32API/Win32API.c, ext/socket/getaddrinfo.c,
+ ext/Win32API/Win32API.c, ext/socket/getaddrinfo.c,
ext/socket/getnameinfo.c, ext/socket/socket.c,
ext/tcltklib/stubs.c
: replace "NT" with "_WIN32", add DOSISH_DRIVE_LETTER
@@ -54,6 +4987,11 @@ Fri Dec 13 23:42:16 2002 WATANABE Hirofumi <eban@ruby-lang.org>
* ext/dbm/extconf.rb (db_check): check existence of the function
in the specified library before checking it in libc.
+Fri Dec 13 17:15:49 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * variable.c (generic_ivar_get): should always warn uninitialized
+ instance variables.
+
Fri Dec 13 12:33:22 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
* parse.y (expr): rescue clause was ignored.
@@ -108,13 +5046,13 @@ Wed Dec 11 00:45:00 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
* lib/getoptlong.rb (GetoptLong::Error): provide a common ancestor
for GetoptLong error classes (RCR#129).
-Tue Dec 10 17:42:39 2002 2002 K.Kosako <kosako@sofnec.co.jp>
+Tue Dec 10 17:42:39 2002 K.Kosako <kosako@sofnec.co.jp>
* re.c (rb_reg_copy_object): fixed memory leak.
Tue Dec 10 17:30:35 2002 Tanaka Akira <akr@m17n.org>
- * pack.c (utf8_limits): fix the limit of 4 bytes UTF-8 sequence.
+ * pack.c (utf8_limits): fix the limit of 4 bytes UTF-8 sequence.
Tue Dec 10 12:01:15 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
@@ -286,7 +5224,7 @@ Thu Nov 21 20:04:06 2002 Minero Aoki <aamine@loveruby.net>
Thu Nov 21 20:01:33 2002 Minero Aoki <aamine@loveruby.net>
* lib/net/http.rb: support Proxy-Authorization.
- (This patch is contributed by Alexander Bokovoy)
+ (This patch is contributed by Alexander Bokovoy)
Thu Nov 21 11:03:39 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
@@ -358,7 +5296,7 @@ Tue Nov 19 01:53:35 2002 Akinori MUSHA <knu@iDaemons.org>
Mon Nov 18 02:13:36 2002 Akinori MUSHA <knu@iDaemons.org>
- * lib/tempfile.rb: Make this libary thread safe.
+ * lib/tempfile.rb: Make this library thread safe.
* lib/tempfile.rb: Do not pick a name which was once used and is
still scheduled for removal.
@@ -375,7 +5313,7 @@ Sun Nov 17 22:57:31 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
* parse.y (dsym): garbage returned. (ruby-bugs-ja:PR#358)
-Fri Nov 15 07:40:08 2002 NAKAMURA Hiroshi <nakahiro@sarion.co.jp>
+Fri Nov 15 07:40:08 2002 NAKAMURA, Hiroshi <nakahiro@sarion.co.jp>
* observer.rb: raise NoMethodError instead of NameError.
[ruby-dev:18788]
@@ -440,7 +5378,7 @@ Sun Nov 10 03:46:18 2002 Akinori MUSHA <knu@iDaemons.org>
[obtained from: Jason Voegele's set.rb]
* lib/set.rb: define several aliases: union() for |(),
- difference() for -(), ande intersection() for &().
+ difference() for -(), and intersection() for &().
[obtained from: Jason Voegele's set.rb]
* lib/set.rb: deal with a s/id/object_id/ leftover.
@@ -588,7 +5526,7 @@ Mon Oct 28 01:27:17 2002 WATANABE Hirofumi <eban@ruby-lang.org>
Sun Oct 27 22:59:50 2002 KONISHI Hiromasa <konishih@fd6.so-net.ne.jp>
* ext/extmk.rb(78) : The unnecessary error when installing by bccwin32
- is controlled.
+ is controlled.
* lib/mkmf.rb(773) : Also in the case of bccwin32, the path was added.
@@ -650,7 +5588,7 @@ Thu Oct 24 03:38:07 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
* configure.in (LIBPATHFLAG): should escape $. [ruby-dev:18572]
- * mkconfig.rb: never substute escaped $$.
+ * mkconfig.rb: never substitute escaped $$.
* instruby.rb: not install LIBRUBY_SO unless enable-shared.
[ruby-dev:18569]
@@ -682,8 +5620,8 @@ Tue Oct 22 23:56:41 2002 WATANABE Hirofumi <eban@ruby-lang.org>
Tue Oct 22 19:44:03 2002 KONISHI Hiromasa <konishih@fd6.so-net.ne.jp>
- * bcc32/configure.bat : The command line when calling setup.mak is
- corrected.
+ * bcc32/configure.bat : The command line when calling setup.mak is
+ corrected.
* bcc32/readme.bcc32 : It follows up about the option of configure.bat.
@@ -719,7 +5657,7 @@ Tue Oct 22 00:59:59 2002 WATANABE Hirofumi <eban@ruby-lang.org>
Mon Oct 21 22:53:02 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
- * configure.in (XCFLAGS): CFLAGS to comile ruby itself.
+ * configure.in (XCFLAGS): CFLAGS to compile ruby itself.
* configure.in (LIBEXT): suffix for static libraries.
@@ -738,7 +5676,7 @@ Mon Oct 21 22:53:02 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
libraries, macros and headers used in common.
* configure.in (RUBYW_INSTALL_NAME, rubyw_install_name): GUI mode
- excutable name.
+ executable name.
* Makefile.in (CFLAGS): append XCFLAGS.
@@ -763,11 +5701,11 @@ Mon Oct 21 22:53:02 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
* lib/mkmf.rb (older): accept multiple file names and Time
objects.
- * lib/mkmf.rb (xsystem): split and qoute.
+ * lib/mkmf.rb (xsystem): split and quote.
* lib/mkmf.rb (cpp_include): make include directives.
- * lib/mkmf.rb (try_func): try wheather specified function is
+ * lib/mkmf.rb (try_func): try whether specified function is
available.
* lib/mkmf.rb (install_files): default to site-install.
@@ -822,7 +5760,7 @@ Thu Oct 17 19:17:56 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
Thu Oct 17 12:58:24 2002 Minero Aoki <aamine@loveruby.net>
- * lib/fileutils.rb: stat.blksize might become 0/nil.
+ * lib/fileutils.rb: stat.blksize might be 0/nil.
* lib/fileutils.rb: change coding style.
@@ -887,7 +5825,7 @@ Thu Oct 10 15:20:18 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
* lib/weakref.rb (WeakRef::@@final): use Hash#delete.
- * lib/weakref.rb (WeakRef::__getobj__): examin if alive or not by
+ * lib/weakref.rb (WeakRef::__getobj__): examine if alive or not by
ID_REV_MAP to deal with recycled object. [ruby-dev:18472]
* lib/weakref.rb (WeakRef::weakref_alive?): ditto.
@@ -1146,7 +6084,8 @@ Wed Sep 25 23:51:29 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
Wed Sep 25 17:46:46 2002 NAKAMURA Usaku <usa@ruby-lang.org>
- * lib/mkmf.rb (libpathflag): restore ENV['LIB'] when some error occured.
+ * lib/mkmf.rb (libpathflag): restore ENV['LIB'] when some error
+ occurred.
Wed Sep 25 16:14:51 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
@@ -1155,7 +6094,7 @@ Wed Sep 25 16:14:51 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
Mon Sep 23 23:22:43 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
* eval.c (rb_call0): must not clear ruby_current_node, or
- backtrace cannot be genetated.
+ backtrace cannot be generated.
* intern.h (ruby_yyparse): rather than yyparse().
@@ -1170,7 +6109,7 @@ Mon Sep 23 23:22:43 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
Mon Sep 23 19:57:52 2002 WATANABE Hirofumi <eban@ruby-lang.org>
* configure.in (RUBY_MINGW32): new macro. check for the MinGW
- compiler envionment.
+ compiler environment.
* lib/mkmf.rb: refactoring.
@@ -1180,7 +6119,7 @@ Mon Sep 23 08:27:11 2002 Tanaka Akira <akr@m17n.org>
Mon Sep 23 02:46:29 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
- * eval.c (ruby_run): should set toplevel visibility again here.
+ * eval.c (ruby_run): should set toplevel visibility again here.
* eval.c (rb_eval): should not rely on ruby_class == rb_cObject
check. Besides allow implicit publicity for attribute set
@@ -1202,7 +6141,7 @@ Sun Sep 22 21:49:42 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
require.
Sun Sep 22 17:08:11 2002 Tanaka Akira <akr@m17n.org>
-
+
* string.c (rb_str_each_line): p might be at the top of the
string.
@@ -1225,7 +6164,7 @@ Fri Sep 20 23:02:01 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
* parse.y (block_append): eliminate unused literal nodes.
- * parse.y (literal_concat): refined literal concatination.
+ * parse.y (literal_concat): refined literal concatenation.
Fri Sep 20 19:43:40 2002 Akinori MUSHA <knu@iDaemons.org>
@@ -1339,7 +6278,7 @@ Wed Sep 11 12:58:57 2002 Akinori MUSHA <knu@iDaemons.org>
Wed Sep 11 11:33:40 2002 NAKAMURA Usaku <usa@ruby-lang.org>
- * bcc32/Makefile.sub: remove unnecessary `.dll' from filename of
+ * bcc32/Makefile.sub: remove unnecessary `.dll' from filename of
dll's resource file.
* cygwin/GNUmakefile.in: ditto. [ruby-dev:17103]
@@ -1558,7 +6497,7 @@ Fri Sep 6 12:11:22 2002 Minero Aoki <aamine@loveruby.net>
Fri Sep 6 01:15:23 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
* gc.c (ruby_xmalloc): remove MALLOC_LIMIT to avoid frequent
- garabage collection.
+ garbage collection.
Fri Sep 6 11:47:37 2002 Minero Aoki <aamine@loveruby.net>
@@ -1592,7 +6531,7 @@ Thu Sep 5 18:32:32 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
Thu Sep 5 17:18:22 2002 Michal Rokos <michal@ruby-lang.org>
- * dln.c: fix memory leak in dln_load (ruby-core:405) and
+ * dln.c: fix memory leak in dln_load (ruby-core:405) and
in load_1 (ruby-core:407)
Thu Sep 5 15:43:54 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
@@ -1636,7 +6575,7 @@ Wed Sep 4 15:23:23 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
Wed Sep 4 05:10:16 2002 Koji Arai <jca02266@nifty.ne.jp>
* parse.y (yylex): the warning message "invalid
- character syntax" was never issued (ruby-bugs-ja:PR#323).
+ character syntax" was never issued (ruby-bugs-ja:PR#323).
Wed Sep 4 01:08:45 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
@@ -1668,7 +6607,7 @@ Mon Sep 2 21:21:46 2002 Minero Aoki <aamine@loveruby.net>
Mon Sep 2 18:45:07 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
* variable.c (rb_copy_generic_ivar): remove old generic instance
- variable table if it existes.
+ variable table if it exists.
Sun Sep 1 15:54:33 2002 WATANABE Hirofumi <eban@ruby-lang.org>
@@ -1813,7 +6752,7 @@ Wed Aug 28 11:37:35 2002 NAKAMURA Usaku <usa@ruby-lang.org>
Tue Aug 27 19:50:27 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
- * ruby.c (require_libraries): prevent ruby_sorcefile from GC.
+ * ruby.c (require_libraries): prevent ruby_sourcefile from GC.
Tue Aug 27 15:03:35 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
@@ -1850,7 +6789,7 @@ Sat Aug 24 15:32:16 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
Sat Aug 24 14:59:02 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
- * marshal.c (w_class): integrate singleton check into a funciton
+ * marshal.c (w_class): integrate singleton check into a function
to follow DRY principle.
* marshal.c (w_uclass): should check singleton method.
@@ -2056,7 +6995,7 @@ Tue Aug 13 00:37:11 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
* array.c (rb_ary_aref): no need for Bignum check.
- * array.c (rb_ary_aset): explicit Bignum check removd.
+ * array.c (rb_ary_aset): explicit Bignum check removed.
* numeric.c (fix_aref): normalize bignum before bit-op.
@@ -2229,7 +7168,7 @@ Mon Jul 29 16:00:54 2002 WATANABE Hirofumi <eban@ruby-lang.org>
Sat Jul 27 23:07:52 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
- * numeric.c (num_to_int): default to_int implementaion for every
+ * numeric.c (num_to_int): default to_int implementation for every
numeric class.
Sat Jul 27 08:09:03 2002 Booker C. Bense <bbense@slac.stanford.edu>
@@ -2352,7 +7291,7 @@ Thu Jul 11 17:59:20 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
* re.c (rb_reg_quote): avoid unnecessary string allocation.
- * string.c (get_pat): quote metachracters before compiling a
+ * string.c (get_pat): quote metacharacters before compiling a
string into a regex.
* string.c (rb_str_split_m): special treatment of strings of size
@@ -2365,7 +7304,7 @@ Thu Jul 11 17:59:20 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
Thu Jul 11 12:59:23 2002 Shugo Maeda <shugo@ruby-lang.org>
* lib/resolv.rb: untaint strings read from /etc/hosts and
- /etc/resolv.conf to prevent SecurityError when $SAFE==1.
+ /etc/resolv.conf to prevent SecurityError when $SAFE==1.
Thu Jul 11 09:00:43 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
@@ -2375,7 +7314,7 @@ Thu Jul 11 09:00:43 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
Tue Jul 9 20:03:55 2002 Keiju Ishitsuka <keiju@ishitsuka.com>
* irb 0.9
-
+
Sat Jul 6 07:35:02 2002 Jamie Herre <jfh@gettysgroup.com>
* array.c (rb_ary_insert): type fixed.
@@ -2391,8 +7330,8 @@ Fri Jul 5 08:59:15 2002 Michal Rokos <michal@ruby-lang.org>
Fri Jul 5 05:00:40 2002 Wakou Aoyama <wakou@ruby-lang.org>
* lib/cgi.rb (CGI#initialize): improvement for mod_ruby.
- thanks to Sean Chittenden <sean@ruby-lang.org>, Shugo Maeda
- <shugo@modruby.net>
+ thanks to Sean Chittenden <sean@ruby-lang.org>, Shugo Maeda
+ <shugo@modruby.net>
Fri Jul 5 00:10:09 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
@@ -2415,7 +7354,7 @@ Wed Jul 3 14:26:40 2002 Sean Chittenden <sean@ruby-lang.org>
Wed Jul 3 13:57:53 2002 Sean Chittenden <sean@ruby-lang.org>
* lib/net/ftp.rb (getbinaryfile): the second argument (localfile)
- is now optional.
+ is now optional.
* lib/net/ftp.rb (gettextfile): ditto.
@@ -2477,7 +7416,7 @@ Thu Jun 27 20:57:45 2002 Tanaka Akira <akr@m17n.org>
Thu Jun 27 15:22:18 2002 Tanaka Akira <akr@m17n.org>
* lib/prettyprint.rb: re-implemented for incremental output to handle
- huge data. API is changed a bit.
+ huge data. API is changed a bit.
* lib/pp.rb: adapt new pretty printing API.
@@ -2544,7 +7483,7 @@ Mon Jun 24 16:32:31 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
* parse.y (here_document): splitted into three phases.
* parse.y (literall_append, literal_concat): added.
- append/concatinate string literals.
+ append/concatenate string literals.
* sample/test.rb (valid_syntax): adjust line number for BEGIN.
@@ -2593,7 +7532,7 @@ Wed Jun 19 01:01:13 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
Wed Jun 19 00:50:50 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
- * parse.y (yylex): ? followed by successive word charaters is
+ * parse.y (yylex): ? followed by successive word characters is
ternary operator not numeric literal.
* parse.y (yylex): commands after break/next/rescue can take
@@ -2630,7 +7569,7 @@ Sat Jun 15 22:56:37 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
Sat Jun 15 18:51:13 2002 Akinori MUSHA <knu@iDaemons.org>
* dir.c (glob_helper): Use lstat() instead of stat() so it catches
- a dead symlink. Given a dead symlink named "a", Dir.glob("?")
+ a dead symlink. Given a dead symlink named "a", Dir.glob("?")
did catch it but Dir.glob("a") somehow didn't.
Sat Jun 15 01:59:05 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
@@ -2646,7 +7585,7 @@ Fri Jun 14 21:01:48 2002 KONISHI Hiromasa <konishih@fd6.so-net.ne.jp>
* bcc32/mkexports.rb: insert sleep(1) for win9x.
- * bcc32/configure.bat: change return code LF -> CRLF fo win9x.
+ * bcc32/configure.bat: change return code LF -> CRLF for win9x.
* win32/win32.c: fix rb_w32_open_osfhandle()
@@ -2746,7 +7685,7 @@ Tue Jun 11 10:18:23 2002 KONISHI Hiromasa <konishih@fd6.so-net.ne.jp>
* new platform [bccwin32] merged.
- create new folder bcc32
- modify any files for bccwin32
- error.c, file.c, hash.c, io.c, instruby.rb,
+ error.c, file.c, hash.c, io.c, instruby.rb,
ext/extmk.rb.in,
lib/mkmf.rb, lib/ftools.rb,
ext/digest/defs.h,
@@ -2884,7 +7823,7 @@ Thu May 30 09:16:36 2002 Wakou Aoyama <wakou@ruby-lang.org>
Wed May 29 18:55:47 2002 KONISHI Hiromasa <H_Konishi@ruby-lang.org>
* function renames my* and win32_* to rb_w32_* in win32/win32.c
- fixed files win32/win32.c, win32/win32.h, win32/dir.h,
+ fixed files win32/win32.c, win32/win32.h, win32/dir.h,
hash.c, rubysig.h, signal.c, ext/socket/socket.c
Wed May 29 17:32:55 2002 WATANABE Hirofumi <eban@ruby-lang.org>
@@ -2902,7 +7841,7 @@ Tue May 28 17:56:02 2002 Sean Chittenden <sean@ruby-lang.org>
* ruby.c (proc_options): access prefixed "ruby_yydebug".
* applied modifies to pacify some of gcc -Wall warnings.
-
+
Tue May 28 14:07:00 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
* parse.y (arg): no more ugly hack for "**", so that "-2**2" to be
@@ -2971,7 +7910,7 @@ Tue May 21 04:48:37 2002 Sean Chittenden <sean@chittenden.org>
Tue May 21 01:16:46 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
- * parse.y (bodystmt): ensure clause was excuted on else clause
+ * parse.y (bodystmt): ensure clause was executed on else clause
without rescue clause.
Tue May 21 00:20:25 2002 Takaaki Tateishi <ttate@kt.jaist.ac.jp>
@@ -3010,7 +7949,7 @@ Sat May 18 02:16:41 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
Fri May 17 16:16:19 2002 WATANABE Hirofumi <eban@ruby-lang.org>
- * sampl/test.rb: use eval instead of './miniruby -c',
+ * sample/test.rb: use eval instead of './miniruby -c',
in order to check a syntax error.
Thu May 16 14:46:34 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
@@ -3151,14 +8090,14 @@ Tue Apr 30 09:23:05 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
Tue Apr 30 05:59:42 2002 Michal Rokos <m.rokos@sh.cvut.cz>
* range.c (range_step): step (for Range#step method) <= 0 makes no
- sence, thus ArgError will be raised.
+ sense, thus ArgError will be raised.
* range.c (range_each): Range#each method is special case for
Range#step(1)
Mon Apr 29 18:46:42 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
- * file.c (rb_find_file): load must be done from an abolute path if
+ * file.c (rb_find_file): load must be done from an absolute path if
$SAFE >= 4.
Sun Apr 28 17:01:56 2002 WATANABE Hirofumi <eban@ruby-lang.org>
@@ -3329,7 +8268,7 @@ Wed Apr 17 08:16:41 2002 Michal Rokos <m.rokos@sh.cvut.cz>
Wed Apr 17 00:01:59 2002 Michal Rokos <m.rokos@sh.cvut.cz>
- * dir.c: wrap multi-statment macro by do { } while (0)
+ * dir.c: wrap multi-statement macro by do { } while (0)
* eval.c, numeric,c, sprintf.c, util.c: ditto.
@@ -3550,7 +8489,7 @@ Mon Apr 1 23:48:12 2002 Takaaki Tateishi <ttate@kt.jaist.ac.jp>
Mon Apr 1 17:25:50 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
* io.c (rb_io_fptr_cleanup): need flush even when io will not be
- closed.
+ closed.
* io.c (rb_io_initialize): was calling wrong function
rb_io_mode_flags().
@@ -3667,7 +8606,7 @@ Tue Mar 26 18:45:15 2002 WATANABE Hirofumi <eban@ruby-lang.org>
* configure.in (FILE_READPTR): check bufread instead of bufend
for uClibc.
-
+
* ext/extmk.rb.in (arg_config): should use Shellwords::shellwords.
Tue Mar 26 01:56:33 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
@@ -3739,7 +8678,7 @@ Mon Mar 25 23:39:25 2002 Nobuyoshi Nakada <nobu.nakada@nifty.ne.jp>
* io.c (remain_size): separated from read_all().
- * io.c (read_all): argument chagend.
+ * io.c (read_all): argument changed.
* io.c (appendline): new. get a line and append to string.
@@ -3771,7 +8710,7 @@ Mon Mar 25 13:24:20 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
into THREAD_RUNNABLE on wakeup.
* eval.c (rb_thread_list): THREAD_TO_KILL threads should be in the
- list.
+ list.
* eval.c (thgroup_list): ditto; by moving gid clearance from
rb_thread_cleanup().
@@ -3960,9 +8899,9 @@ Mon Mar 18 10:31:20 2002 Nobuyoshi Nakada <nobu.nakada@nifty.ne.jp>
Sun Mar 17 20:08:04 2002 Nobuyoshi Nakada <nobu.nakada@nifty.ne.jp>
- * ext/iconv/depend: added.
+ * ext/iconv/depend: added.
- * ext/stringio/depend: added.
+ * ext/stringio/depend: added.
Sat Mar 16 22:43:53 2002 WATANABE Hirofumi <eban@ruby-lang.org>
@@ -4105,7 +9044,7 @@ Sun Mar 10 02:18:22 2002 Koji Arai <jca02266@nifty.ne.jp>
Sat Mar 9 08:45:58 2002 Tanaka Akira <akr@m17n.org>
* ext/socket/extconf.rb (have_struct_member): don't print checked
- result.
+ result.
Fri Mar 8 12:19:15 2002 Tanaka Akira <akr@m17n.org>
@@ -4213,7 +9152,7 @@ Fri Mar 1 06:25:49 2002 Tanaka Akira <akr@m17n.org>
(unix_send_io): ditto.
(unix_recv_io): ditto.
(unix_s_socketpair): ditto.
- (Init_socket): define UNIXSocket#send_io, UNIXSocket#recv_io,
+ (Init_socket): define UNIXSocket#send_io, UNIXSocket#recv_io,
UNIXSocket.socketpair and UNIXSocket.pair.
* dln.c (dln_load): fix typo.
@@ -4277,11 +9216,11 @@ Tue Feb 26 15:41:30 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
Tue Feb 26 11:25:50 2002 akira yamada <akira@arika.org>
- * lib/uri/generic.rb: merge0 shuld return [oth, oth] if oth is
- absolute URI.
+ * lib/uri/generic.rb: merge0 should return [oth, oth] if oth is
+ absolute URI.
* lib/uri/generic.rb: registry part must not be allowed for any
- schemes for the Internet. (RFC2396, section 3.2.2 and 3.2.1.)
+ schemes for the Internet. (RFC2396, section 3.2.2 and 3.2.1.)
Mon Feb 25 21:22:41 2002 Akinori MUSHA <knu@iDaemons.org>
@@ -4435,7 +9374,7 @@ Tue Feb 19 21:43:32 2002 Minero Aoki <aamine@loveruby.net>
* lib/net/http.rb: HTTPResponse class does not inherit from
Net::Response.
- * lib/net/http.rb: devide HTTP#connecting into
+ * lib/net/http.rb: divide HTTP#connecting into
{begin,end}_transport.
* lib/net/http.rb: unused class Accumulator removed.
@@ -4469,7 +9408,7 @@ Tue Feb 19 14:45:32 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
* process.c (security): need not to warn twice.
* marshal.c (r_object): complete restoration before calling
- r_regist().
+ r_regist().
Tue Feb 19 14:24:36 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
@@ -4573,7 +9512,7 @@ Fri Feb 15 14:40:38 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
Fri Feb 15 13:36:58 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
- * bignum.c (rb_big_rshift): should properly convert the nagative
+ * bignum.c (rb_big_rshift): should properly convert the negative
value to 2's compliment.
Thu Feb 14 17:38:35 2002 Nobuyoshi Nakada <nobu.nakada@nifty.ne.jp>
@@ -4631,7 +9570,7 @@ Sun Feb 10 16:52:53 2002 Nobuyoshi Nakada <nobu.nakada@nifty.ne.jp>
Fri Feb 8 23:07:23 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
- * eval.c (rb_eval): singleton chech should be moved from yycompile
+ * eval.c (rb_eval): singleton check should be moved from yycompile
to here.
* eval.c (is_defined): check should be added here too.
@@ -4650,16 +9589,16 @@ Fri Feb 8 01:27:33 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
* eval.c (rb_eval): class variables from singleton methods defined
within singleton class statement should work like ones defined
- by sington def statements.
+ by singleton def statements.
-Thu Feb 07 13:44:08 2002 akira yamada <akira@arika.org>
+Thu Feb 7 13:44:08 2002 akira yamada <akira@arika.org>
* uri/common.rb (URI::join): new method.
* uri/generic.rb (Generic#merge): URI.parse("http://a/")+"b" should
return "http://a/b" but it returned "http://a//b".
- * uri/generic.rb (Generic#check_path): corrected error message,
+ * uri/generic.rb (Generic#check_path): corrected error message,
@path -> v
Thu Feb 7 00:18:43 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
@@ -4678,7 +9617,7 @@ Wed Feb 6 13:28:53 2002 Amos Gouaux <amos+ruby@utdallas.edu>
* lib/net/imap.rb: OpenSSL support.
* lib/net/imap.rb (setquota): unset quota if the second argument
- is nil.
+ is nil.
Wed Feb 6 13:05:11 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
@@ -4724,7 +9663,7 @@ Fri Feb 1 19:10:04 2002 Nobuyoshi Nakada <nobu.nakada@nifty.ne.jp>
* bignum.c (rb_str_to_inum): ditto.
- * bignum.c (rb_cstr2inum): wapper of rb_cstr_to_inum() now.
+ * bignum.c (rb_cstr2inum): wrapper of rb_cstr_to_inum() now.
* bignum.c (rb_str2inum): ditto.
@@ -4777,7 +9716,7 @@ Thu Jan 31 13:22:36 2002 Tanaka Akira <akr@m17n.org>
Wed Jan 30 15:58:04 2002 K.Kosako <kosako@sofnec.co.jp>
- * regex.c (re_adjust_startpos): fix for SJIS and UTF-8.
+ * regex.c (re_adjust_startpos): fix for SJIS and UTF-8.
* regex.c (mbc_startpos): ditto.
@@ -4868,7 +9807,7 @@ Thu Jan 24 11:49:05 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
Thu Jan 24 05:42:01 2002 Koji Arai <jca02266@nifty.ne.jp>
* string.c (rb_str_split_m): no need to consider KANJI
- characters, if the length of separator is 1 (byte).
+ characters, if the length of separator is 1 (byte).
Wed Jan 23 16:07:31 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
@@ -4908,7 +9847,7 @@ Mon Jan 21 08:25:30 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
Sat Jan 19 02:31:45 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
- * eval.c (rb_eval): need not to clar method cache for NODE_CLASS,
+ * eval.c (rb_eval): need not to clear method cache for NODE_CLASS,
NODE_SCLASS.
* gc.c (obj_free): need not to clear method cache on class/module
@@ -5089,8 +10028,8 @@ Tue Jan 8 15:54:02 2002 Nobuyoshi Nakada <nobu.nakada@nifty.ne.jp>
Mon Jan 7 12:38:47 2002 Tanaka Akira <akr@m17n.org>
* lib/time.rb (Time#xmlschema): new optional argument
- fractional_seconds to specify a number of digits of
- fractional part of the time.
+ fractional_seconds to specify a number of digits of
+ fractional part of the time.
Sat Jan 5 13:18:11 2002 Nobuyoshi Nakada <nobu.nakada@nifty.ne.jp>
@@ -5207,7 +10146,7 @@ Tue Dec 25 02:37:49 2001 Tanaka Akira <akr@m17n.org>
Tue Dec 25 02:11:17 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
- * object.c (rb_convert_type): check method responce check before
+ * object.c (rb_convert_type): check method response check before
invoking rb_rescue().
* object.c (rb_check_convert_type): ditto.
@@ -5274,16 +10213,16 @@ Thu Dec 20 01:01:50 2001 takuma ozawa <metal@mine.ne.jp>
Wed Dec 19 16:58:29 2001 Shugo Maeda <shugo@ruby-lang.org>
* ext/readline/readline.c: new methods
- Readline::basic_word_break_characters,
- Readline::basic_word_break_characters=,
- Readline::completer_word_break_characters,
- Readline::completer_word_break_characters=,
- Readline::basic_quote_characters,
- Readline::basic_quote_characters=,
- Readline::completer_quote_characters,
- Readline::completer_quote_characters=,
- Readline::filename_quote_characters,
- Readline::filename_quote_characters=.
+ Readline::basic_word_break_characters,
+ Readline::basic_word_break_characters=,
+ Readline::completer_word_break_characters,
+ Readline::completer_word_break_characters=,
+ Readline::basic_quote_characters,
+ Readline::basic_quote_characters=,
+ Readline::completer_quote_characters,
+ Readline::completer_quote_characters=,
+ Readline::filename_quote_characters,
+ Readline::filename_quote_characters=.
Wed Dec 19 14:05:00 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
@@ -5291,7 +10230,7 @@ Wed Dec 19 14:05:00 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
default method visibility.
* eval.c (rb_attr): should warn if the default method visibility
- is "module_function" (can be error).
+ is "module_function" (can be error).
* eval.c (rb_mod_define_method): should define class/module method
also if the visibility is "module_function".
@@ -5360,27 +10299,27 @@ Mon Dec 17 15:41:24 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
Mon Dec 17 15:19:32 2001 Tanaka Akira <akr@m17n.org>
* time.c: new method `gmtoff', `gmt_offset' and `utc_offset'.
- (time_utc_offset): new function.
- (Init_Time): bind above methods to `time_utc_offset'.
+ (time_utc_offset): new function.
+ (Init_Time): bind above methods to `time_utc_offset'.
* time.c: 64bit time_t support.
- (time_s_at): use NUM2LONG instead of NUM2INT for tv_sec.
- (time_arg): initialize tm_isdst correctly.
- use long to initialize tm_year.
- (search_time_t): renamed from `make_time_t'.
- (make_time_t): call `timegm' and `mktime' instead of `search_time_t'
- if available.
- (time_to_i): use LONG2NUM instead of INT2NUM.
- (time_localtime): check localtime failure.
- (time_gmtime): check gmtime failure.
- (time_year): use LONG2NUM instead of INT2FIX.
- (time_to_a): use long for tm_year.
- (time_dump): check tm_year which is not representable with 17bit.
- (time_load): initialize tm_isdst.
+ (time_s_at): use NUM2LONG instead of NUM2INT for tv_sec.
+ (time_arg): initialize tm_isdst correctly.
+ use long to initialize tm_year.
+ (search_time_t): renamed from `make_time_t'.
+ (make_time_t): call `timegm' and `mktime' instead of `search_time_t'
+ if available.
+ (time_to_i): use LONG2NUM instead of INT2NUM.
+ (time_localtime): check localtime failure.
+ (time_gmtime): check gmtime failure.
+ (time_year): use LONG2NUM instead of INT2FIX.
+ (time_to_a): use long for tm_year.
+ (time_dump): check tm_year which is not representable with 17bit.
+ (time_load): initialize tm_isdst.
* configure.in: check existence of `mktime' and `timegm'.
- check existence of tm_gmtoff field of struct tm.
- fix negative time_t for 64bit time_t.
+ check existence of tm_gmtoff field of struct tm.
+ fix negative time_t for 64bit time_t.
* missing/strftime.c: fix overflow by tm_year + 1900.
@@ -5409,7 +10348,7 @@ Fri Dec 14 04:23:36 2001 Minero Aoki <aamine@loveruby.net>
Fri Dec 14 00:16:06 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
* class.c (rb_define_class): should return the existing class if
- the class is already defined and its superclass is ideintical to
+ the class is already defined and its superclass is identical to
the specified superclass.
* class.c (rb_define_class_under): ditto.
@@ -5487,7 +10426,7 @@ Sun Dec 9 18:06:26 2001 Minero Aoki <aamine@loveruby.net>
Sat Dec 8 23:27:44 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
- * io.c (rb_io_puts): old behavoir restored. rationale: a) if you
+ * io.c (rb_io_puts): old behavior restored. rationale: a) if you
want to call to_s for arrays, you can just call print a, "\n".
b) to_s wastes memory if array (and sum of its contents) is
huge. c) now any object that has to_ary is treated as an array,
@@ -5499,10 +10438,10 @@ Sat Dec 8 22:40:38 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
the default value. [new]
* hash.c (rb_hash_aref): call "default" method to get the value
- corrensponding to the non existing key.
+ corresponding to the non existing key.
* hash.c (rb_hash_default): get the default value based on the
- block given to 'new'. Now it takes an optinal "key" argument.
+ block given to 'new'. Now it takes an optional "key" argument.
"default" became the method to get the value for non existing
key. Users may override "default" method to change the hash
behavior.
@@ -5524,7 +10463,7 @@ Fri Dec 7 19:12:14 2001 Minero Aoki <aamine@loveruby.net>
* lib/net/http.rb: HTTP.new requires at least one arg.
- * lib/net/http.rb: changes implicit start algolithm.
+ * lib/net/http.rb: changes implicit start algorithm.
Fri Dec 7 15:49:39 2001 Usaku Nakamura <usa@ruby-lang.org>
@@ -5613,8 +10552,8 @@ Tue Dec 4 03:49:06 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
comparison.
Sun Dec 9 23:00:54 2001 Keiju Ishitsuka <keiju@ishitsuka.com>
* matrix.rb: Vector#* bug. reported from Massimiliano Mirra
- <info@chromatic-harp.com>.
-
+ <info@chromatic-harp.com>.
+
Sun Dec 9 22:15:59 2001 Nobuyoshi Nakada <nobu.nakada@nifty.ne.jp>
* enum.c (enum_sort_by): should replace with last elements.
@@ -5643,7 +10582,7 @@ Mon Dec 3 09:59:08 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
* time.c (time_minus): ditto.
* time.c (time_new_internal): round usec overflow and underflow
- here.
+ here.
* time.c (time_plus): move operand overflow/underflow check to
time_new_internal().
@@ -5672,7 +10611,7 @@ Sat Dec 1 12:13:20 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
Fri Nov 30 17:02:55 2001 WATANABE Hirofumi <eban@ruby-lang.org>
* configure.in: set target_cpu to i386 on cygwin and mingw32.
-
+
* configure.in: default --enable-shared to yes on cygwin and mingw32.
Fri Nov 30 00:25:28 2001 Usaku Nakamura <usa@ruby-lang.org>
@@ -5740,7 +10679,7 @@ Thu Nov 22 20:15:28 2001 TAMURA Takashi <sheepman@tcn.zaq.ne.jp>
* gc.c (gc_mark_rest): should call gc_mark_children(), not gc_mark().
- * gc.c (rb_gc_mark): may cause infinite looop.
+ * gc.c (rb_gc_mark): may cause infinite loop.
Thu Nov 22 00:28:13 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
@@ -5749,7 +10688,7 @@ Thu Nov 22 00:28:13 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
Wed Nov 21 12:22:52 2001 Shugo Maeda <shugo@ruby-lang.org>
* lib/cgi.rb: CGI#header: do not set Apache.request.status for
- Location: if Apache.request.status is already set.
+ Location: if Apache.request.status is already set.
Wed Nov 21 02:24:18 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
@@ -5843,7 +10782,7 @@ Thu Nov 15 03:37:17 2001 Usaku Nakamura <usa@ruby-lang.org>
Thu Nov 15 00:07:12 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
* array.c (rb_ary_to_s): if rb_output_fs is nil, insert newlines
- between array elements (use rb_default_rs as newline litral)
+ between array elements (use rb_default_rs as newline literal)
[experimental].
Wed Nov 14 15:16:23 2001 K.Kosako <kosako@sofnec.co.jp>
@@ -5966,7 +10905,7 @@ Mon Nov 12 01:30:37 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
level higher than 3.
* variable.c (rb_f_trace_var): should not allow tainted trace
- closure.
+ closure.
Sun Nov 11 00:12:23 2001 TAMURA Takashi <sheepman@tcn.zaq.ne.jp>
@@ -6045,7 +10984,7 @@ Tue Nov 6 14:38:48 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
Tue Nov 6 14:17:14 2001 Amos Gouaux <amos+ruby@utdallas.edu>
* lib/net/imap.rb (getquota_response): use astring for mailbox
- names.
+ names.
* lib/net/imap.rb (getacl_response): ditto.
@@ -6059,11 +10998,11 @@ Sat Nov 3 23:33:18 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
Sat Nov 3 22:28:51 2001 Keiju Ishitsuka <keiju@ishitsuka.com>
- * matrix.rb (Matrix#column_vectors, Matrix#row_vectors): ditto bug.
+ * matrix.rb (Matrix#column_vectors, Matrix#row_vectors): ditto bug.
this bug report and fix by tsutomu@nucba.ac.jp.
-
+
* forwardable.rb: change raise to Kernel::raise
-
+
Sat Nov 3 10:11:57 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
* eval.c (rb_yield_0): better error message.
@@ -6338,7 +11277,7 @@ Thu Oct 4 14:11:03 2001 WATANABE Hirofumi <eban@ruby-lang.org>
Wed Oct 3 20:11:06 2001 Nobuyoshi Nakada <nobu.nakada@nifty.ne.jp>
- * re.c (rb_reg_s_alloc): avoid inifinte recursion.
+ * re.c (rb_reg_s_alloc): avoid infinite recursion.
Wed Oct 3 16:49:49 2001 Nobuyoshi Nakada <nobu.nakada@nifty.ne.jp>
@@ -6352,7 +11291,7 @@ Wed Oct 3 13:32:06 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
* variable.c (rb_class_path): Module may have subclass.
* string.c (rb_str_update): should maintain original negative
- offset.
+ offset.
* string.c (rb_str_subpat_set): ditto
@@ -6381,7 +11320,7 @@ Tue Oct 2 08:04:52 2001 Nobuyoshi Nakada <nobu.nakada@nifty.ne.jp>
Mon Oct 1 19:18:54 2001 Tanaka Akira <akr@m17n.org>
- * ext/socket/socket.c (unix_addr): getsockname(2) may result len = 0.
+ * ext/socket/socket.c (unix_addr): getsockname(2) may result len = 0.
* ext/socket/socket.c (unix_peeraddr): getpeername(2) may result
len = 0.
@@ -6641,7 +11580,7 @@ Tue Aug 28 00:40:48 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
Fri Aug 24 15:17:40 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
- * array.c (rb_ary_equal): check identiry equality first.
+ * array.c (rb_ary_equal): check identity equality first.
* string.c (rb_str_equal): ditto.
@@ -6679,7 +11618,7 @@ Thu Aug 23 10:10:59 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
Wed Aug 22 23:20:03 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
- * hash.c (rb_hash_equal): check identiry equality first.
+ * hash.c (rb_hash_equal): check identity equality first.
Wed Aug 22 19:58:59 2001 Nobuyoshi Nakada <nobu.nakada@nifty.ne.jp>
@@ -6764,9 +11703,9 @@ Fri Aug 17 00:49:51 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
Thu Aug 16 23:03:40 2001 Koji Arai <JCA02266@nifty.ne.jp>
- * io.c: prevent recursive malloc calls on NEC UX/4800.
+ * io.c: prevent recursive malloc calls on NEC UX/4800.
- * ext/socket/socket.c: ditto.
+ * ext/socket/socket.c: ditto.
Thu Aug 16 13:54:04 2001 Usaku Nakamura <usa@ruby-lang.org>
@@ -6831,12 +11770,12 @@ Sun Aug 12 15:01:58 2001 Nobuyoshi Nakada <nobu.nakada@nifty.ne.jp>
Sat Aug 11 14:43:47 2001 Tanaka Akira <akr@m17n.org>
* array.c (rb_inspecting_p): initialize inspect_key if it is
- not initialized yet.
+ not initialized yet.
Fri Aug 10 22:14:37 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
* parse.y (cond0): operands of logical operators are not treated
- as conditional expresion anymore, but propagate conditional
+ as conditional expression anymore, but propagate conditional
status if used in conditionals.
Tue Aug 7 09:10:32 2001 Usaku Nakamura <usa@ruby-lang.org>
@@ -7012,8 +11951,8 @@ Wed Jul 25 12:15:32 2001 WATANABE Hirofumi <eban@ruby-lang.org>
Tue Jul 24 23:10:47 2001 Nobuyoshi Nakada <nobu.nakada@nifty.ne.jp>
- * file.c (strrdirsep): multi-byte pathname and DOSish separater
- supprot. originally comes from Patrick Cheng. [new]
+ * file.c (strrdirsep): multi-byte pathname and DOSish separator
+ support. originally comes from Patrick Cheng. [new]
* file.c (rb_file_s_basename, rb_file_s_dirname): use
strrdirsep(). comes from Patrick Cheng.
@@ -7191,7 +12130,7 @@ Fri Jul 6 02:15:06 2001 Akinori MUSHA <knu@iDaemons.org>
Thu Jul 5 20:28:53 2001 Tietew <tietew@tietew.net>
- * string.c (rb_str_each_line): should propagate taint mark.
+ * string.c (rb_str_each_line): should propagate taint mark.
* ext/nkf/nkf.c (rb_nkf_kconv): ditto.
@@ -7318,12 +12257,12 @@ Thu Jun 28 00:21:28 2001 Keiju Ishitsuka <keiju@ishitsuka.com>
* lib/irb/multi-irb.rb: resolve 'ruby -w' warnings.
* lib/irb/ruby-lex.rb: fix problem for "\\M-\\..." and "\\C-\\..."
- and resolve 'ruby -w' warnings.
+ and resolve 'ruby -w' warnings.
* lib/irb/ruby-token.rb: fix typo
* lib/shell/command-processor.rb: resolve 'ruby -w' warnings.
-
+
Wed Jun 27 08:53:26 2001 Minero Aoki <aamine@loveruby.net>
* lib/net/pop.rb: new methods POP3.auth_only, POP3#auth_only
@@ -7372,9 +12311,9 @@ Fri Jun 22 23:17:28 2001 WATANABE Hirofumi <eban@ruby-lang.org>
Cygwin socket.
Fri Jun 22 23:11:17 2001 Keiju Ishitsuka <keiju@ishitsuka.com>
-
+
* lib/irb/locale.rb: fix for require "kconv" problem
-
+
Fri Jun 22 18:08:45 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
* eval.c (rb_yield_0): no mvalue_to_svalue conversion here.
@@ -7525,8 +12464,8 @@ Wed Jun 6 23:02:36 2001 Keiju Ishitsuka <keiju@ishitsuka.com>
* lib/sync.rb: bug fix if obj.initialize has parameters when
obj.extend(Sync_m)
- * lib/mutex_m.rb: modified bit
-
+ * lib/mutex_m.rb: modified bit
+
Wed Jun 6 16:11:06 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
* eval.c (rb_load): should check if tainted even when wrap is
@@ -7615,7 +12554,7 @@ Sat Jun 2 23:05:20 2001 Shugo Maeda <shugo@ruby-lang.org>
Sat Jun 2 00:02:22 2001 Keiju Ishitsuka <keiju@ishitsuka.com>
* irb messages: fix typos.
-
+
Fri Jun 1 17:26:24 2001 K.Kosako <kosako@sofnec.co.jp>
* hash.c (replace_i): ignore when key == Qundef.
@@ -7696,7 +12635,7 @@ Mon May 28 23:20:43 2001 WATANABE Hirofumi <eban@ruby-lang.org>
* configure.in: remove unnecessary AC_CANONICAL_BUILD
- * defins.h: #define HAVE_SETITIMER on Cygwin(bug fixed).
+ * defines.h: #define HAVE_SETITIMER on Cygwin(bug fixed).
* ruby.c: use relative path from LIBRUBY_SO.
@@ -7980,7 +12919,7 @@ Fri May 11 03:34:20 2001 Akinori MUSHA <knu@iDaemons.org>
* README.EXT.jp: Remove the description of find_header() because
such a function does not actually exist.
-
+
* README.EXT.jp: Update the description of dir_config().
Fri May 11 02:42:05 2001 Kazuhiro NISHIYAMA <zn@mbf.nifty.com>
@@ -8132,7 +13071,7 @@ Thu Apr 26 22:30:43 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
Thu Apr 26 10:36:09 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
- * eval.c (rb_thread_schedule): infinite sleep should not cause
+ * eval.c (rb_thread_schedule): infinite sleep should not cause
dead lock.
Wed Apr 25 16:40:44 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
@@ -8141,7 +13080,7 @@ Wed Apr 25 16:40:44 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
Wed Apr 25 15:36:15 2001 K.Kosako <kosako@sofnec.co.jp>
- * eval.c (yield_under): need not to prohibit at safe level 4.
+ * eval.c (yield_under): need not to prohibit at safe level 4.
Wed Apr 25 15:22:20 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
@@ -8240,7 +13179,7 @@ Fri Apr 13 16:41:18 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
* parse.y (none): should clear cmdarg_stack too.
-Fri Apr 13 06:19:29 2001 GOTOU YUUZOU <gotoyuzo@notwork.org>
+Fri Apr 13 06:19:29 2001 GOTOU Yuuzou <gotoyuzo@notwork.org>
* io.c (rb_fopen): use setvbuf() to avoid recursive malloc() on
some platforms.
@@ -8289,7 +13228,7 @@ Mon Apr 9 15:20:21 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
Mon Apr 9 12:05:44 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
- * file.c (Init_File): should redifine "new" class method.
+ * file.c (Init_File): should redefine "new" class method.
Mon Apr 9 11:56:52 2001 Shugo Maeda <shugo@ruby-lang.org>
@@ -8328,9 +13267,9 @@ Thu Apr 5 02:19:03 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
Wed Apr 4 03:47:03 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
- * eval.c (proc_eq): class check aded.
+ * eval.c (proc_eq): class check added.
- * eval.c (proc_eq): typo fixed ("return" was ommitted).
+ * eval.c (proc_eq): typo fixed ("return" was omitted).
* error.c (Init_Exception): move NameError under StandardError.
@@ -8359,7 +13298,7 @@ Tue Apr 3 00:05:07 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
Mon Apr 2 16:52:48 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
* process.c (proc_waitall): should push Process::Status instead of
- Finuxm status.
+ Fixnum status.
* process.c (waitall_each): should add all entries in pid_tbl.
these changes are inspired by Koji Arai. Thanks.
@@ -8389,7 +13328,7 @@ Mon Apr 2 01:32:38 2001 Akinori MUSHA <knu@iDaemons.org>
* configure.in: Link libc_r against the ruby executable on
FreeBSD, which is the first attempt to work around a certain
problem regarding pthread on FreeBSD. It should make ruby/libruby
- happy when it loads an extention to a library compiled and linked
+ happy when it loads an extension to a library compiled and linked
with -pthread. Note, however, that libruby is _not_ linked with
libc_r so as not to mess up pthread unfriendly stuff including
apache+mod_ruby and vim6+ruby_interp.
@@ -8427,7 +13366,7 @@ Sat Mar 31 03:24:10 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
Fri Mar 30 23:37:49 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
- * eval.c (rb_load): should not copy topleve local variables. It
+ * eval.c (rb_load): should not copy toplevel local variables. It
cause variable/method ambiguity. Thanks to L. Peter Deutsch.
Fri Mar 30 22:56:56 2001 Shugo Maeda <shugo@ruby-lang.org>
@@ -8586,13 +13525,13 @@ Tue Mar 20 23:09:33 2001 WATANABE Hirofumi <eban@ruby-lang.org>
Tue Mar 20 15:10:00 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
- * dir.c (glob_helper): breaks loop after calling recusive
+ * dir.c (glob_helper): breaks loop after calling recursive
glob_helper; all wild cards should be consumed; no need for
further match.
* dir.c (dir_s_glob): gives warning if no match found.
-Tue Mar 20 14:13:45 Koji Arai <JCA02266@nifty.ne.jp>
+Tue Mar 20 14:13:45 2001 Koji Arai <JCA02266@nifty.ne.jp>
* object.c (sym_inspect): did allocate extra byte space.
@@ -8703,7 +13642,7 @@ Sun Mar 11 18:13:34 2001 Masahiro Tanaka <masa@stars.gsfc.nasa.gov>
* math.c: add acos, asin, atan, conh, sinh, tanh and hypot to Math.
- * configure.in: check hypot availablility.
+ * configure.in: check hypot availability.
* missing/hypot.c: public domain rewrite of hypot.
@@ -8787,7 +13726,7 @@ Sat Mar 3 16:15:16 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
Wed Feb 28 11:02:41 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
* string.c (rb_str_delete_bang): delete! should take at least 1
- argument.
+ argument.
* ruby.c (load_file): add rb_gc() after loading to avoid
extraordinary memory growth.
@@ -8804,7 +13743,7 @@ Tue Feb 27 16:38:15 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
* eval.c (ev_const_defined): check Object's constant if no current
class is available (e.g. defining singleton class for Fixnums).
- * time.c (time_timeval): negative time interval shoule not be
+ * time.c (time_timeval): negative time interval should not be
allowed.
* eval.c (proc_call): ignore block to `call' always, despite of
@@ -9019,7 +13958,7 @@ Tue Feb 13 08:43:10 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
Tue Feb 13 01:13:43 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
- * eval.c (rb_load): raise LocaJumpError if unexpected local jumps
+ * eval.c (rb_load): raise LocalJumpError if unexpected local jumps
appear during load.
* ext/socket/socket.c (bsock_close_read): don't call rb_thread_fd_close();
@@ -9219,7 +14158,7 @@ Wed Jan 24 01:45:49 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
not been objectified.
* eval.c (rb_callcc): should nail down block->tag history to avoid
- rb_gc_force_recycle().
+ rb_gc_force_recycle().
Tue Jan 23 18:51:57 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
@@ -9417,7 +14356,7 @@ Sat Dec 30 03:14:22 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
* eval.c (rb_iterate): NODE_CFUNC does not protect its data
(nd_tval), so create new node NODE_IFUNC for iteration C
- function.
+ function.
* eval.c (rb_yield_0): use NODE_IFUNC.
@@ -9440,13 +14379,13 @@ Fri Dec 29 11:05:41 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
* eval.c (rb_thread_select): ditto.
* eval.c (rb_thread_join): join during critical section causes
- deadlock.
+ deadlock.
Fri Dec 29 00:38:46 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
* m17n.c: new file - core functions of M17N.
-Tue Dec 26 18:46:41 2000 NAKAMURA Hiroshi <nakahiro@sarion.co.jp>
+Tue Dec 26 18:46:41 2000 NAKAMURA, Hiroshi <nakahiro@sarion.co.jp>
* lib/debug.rb: Avoid thread deadlock in debugging stopped thread.
@@ -9464,7 +14403,7 @@ Tue Dec 26 16:53:55 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
Tue Dec 26 15:45:35 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
- * string.c (rb_str_inspect): should treat multibyte chracters
+ * string.c (rb_str_inspect): should treat multibyte characters
properly.
Mon Dec 25 17:49:08 2000 K.Kosako <kosako@sofnec.co.jp>
@@ -9517,7 +14456,7 @@ Sat Dec 23 23:55:57 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
Sat Dec 23 11:55:57 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
- * array.c (rb_ary_and): Array#& should preverve original order.
+ * array.c (rb_ary_and): Array#& should preserve original order.
Sat Dec 23 03:44:16 2000 Minero Aoki <aamine@dp.u-netsurf.ne.jp>
@@ -9551,7 +14490,7 @@ Wed Dec 20 12:00:15 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
* bignum.c (rb_big_lshift): should cast up to BDIGIT_DBL.
- * parse.y (yylex): disallow trailing '_' for numeric litrals.
+ * parse.y (yylex): disallow trailing '_' for numeric literals.
* bignum.c (rb_cstr2inum): allow `_' within converting string.
@@ -9592,7 +14531,7 @@ Sat Dec 16 02:58:26 2000 Minero Aoki <aamine@dp.u-netsurf.ne.jp>
Thu Dec 14 13:06:18 2000 Nobuyoshi Nakada <nobu.nakada@nifty.ne.jp>
- * class.c (rb_include_module): prohibit fronzen class/module.
+ * class.c (rb_include_module): prohibit frozen class/module.
* eval.c (rb_frozen_class_p): make external.
@@ -9775,7 +14714,7 @@ Sat Nov 25 23:12:22 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
Fri Nov 24 22:03:48 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
- * range.c (EXCL): exclusive infomation is now stored in an
+ * range.c (EXCL): exclusive information is now stored in an
instance variable. this enables proper marshal dump.
* process.c (proc_waitpid): should clear rb_last_status ($?) if
@@ -9802,7 +14741,7 @@ Tue Nov 21 23:24:14 2000 Mitsuteru S Nakao <nakao@kuicr.kyoto-u.ac.jp>
Tue Nov 21 03:39:41 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
* marshal.c (marshal_load): marshal format compatibility check
- revised. greater minor revision is UPWARD compatibile;
+ revised. greater minor revision is UPWARD compatible;
downward compatibility is not assured.
* eval.c (is_defined): clarify class variable behavior for
@@ -9897,18 +14836,18 @@ Thu Nov 16 16:32:45 2000 Masahiro Tanaka <masa@stars.gsfc.nasa.gov>
Thu Nov 16 14:58:00 2000 Nobuyoshi Nakada <nobu.nakada@nifty.ne.jp>
- * ext/socket/socket.c (sock_new): duplicates file descriptor
+ * ext/socket/socket.c (sock_new): duplicates file descriptor
with myfddup() on mswin32/mingw32.
* win32/win32.h: uses system original fdopen().
* win32/win32.c (myfddup): newly added instead of myfdopen().
- * win32/win32.c (mybind, myconnect, mygetsockname, mygetsockopt,
- mylisten, mysetsockopt): now accept file descriptor only, not
+ * win32/win32.c (mybind, myconnect, mygetsockname, mygetsockopt,
+ mylisten, mysetsockopt): now accept file descriptor only, not
SOCKET.
- * win32/win32.c (myaccept, mysocket): return file descriptor,
+ * win32/win32.c (myaccept, mysocket): return file descriptor,
instead of SOCKET.
Thu Nov 16 10:23:24 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
@@ -9998,7 +14937,7 @@ Thu Nov 9 14:22:13 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
Wed Nov 8 03:08:53 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
* io.c (io_fflush): ensure fflush(3) would not block by calling
- rb_thread_fd_writable().
+ rb_thread_fd_writable().
Tue Nov 7 20:29:56 2000 Minero Aoki <aamine@dp.u-netsurf.ne.jp>
@@ -10312,7 +15251,7 @@ Sat Sep 23 07:33:20 2000 Aleksi Niemela <aleksi.niemela@cinnober.com>
Sat Sep 23 03:06:25 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
* variable.c (rb_autoload_load): should not require already
- provided features.
+ provided features.
Fri Sep 22 15:46:21 2000 Minero Aoki <aamine@dp.u-netsurf.ne.jp>
@@ -10378,11 +15317,11 @@ Wed Sep 20 14:01:45 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
* eval.c (rb_provided): too weak filename comparison.
* eval.c (rb_thread_alloc): avoid recycling still referenced
- dvar structures.
+ dvar structures.
* eval.c (rb_callcc): ditto.
- * eval.c (THREAD_ALLOC): fiil dyna_vars field by ruby_dyna_vars.
+ * eval.c (THREAD_ALLOC): fill dyna_vars field by ruby_dyna_vars.
Tue Sep 19 17:47:03 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
@@ -10401,7 +15340,7 @@ Tue Sep 19 13:07:47 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
* parse.y (yylex): was confusing $~ and $_.
-Tue Sep 19 13:06:53 2000 GOTOU YUUZOU <gotoyuzo@notwork.org>
+Tue Sep 19 13:06:53 2000 GOTOU Yuuzou <gotoyuzo@notwork.org>
* signal.c (rb_f_kill): signum may be a negative number, should be
treated by signed number.
@@ -10434,7 +15373,7 @@ Sat Sep 16 03:29:59 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
Thu Sep 14 02:46:54 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
* eval.c (rb_thread_yield): array strip should be done in this
- function.
+ function.
Wed Sep 13 17:01:03 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
@@ -10484,7 +15423,7 @@ Mon Sep 11 14:24:47 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
origenvironment.
* parse.y (command_call): kYIELD moved to this rule to allow
- 'a = yield b'. (ruby-bugs-ja:#PR15)
+ 'a = yield b'. (ruby-bugs-ja:#PR15)
Mon Sep 11 01:27:54 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
@@ -10677,7 +15616,7 @@ Sat Aug 19 01:34:02 2000 WATANABE Hirofumi <eban@os.rim.or.jp>
Fri Aug 18 13:23:59 2000 Yukihiro Matsumoto <matz@ruby-lang.org>
* eval.c (rb_eval): should preserve and clear $! value before
- compilation.
+ compilation.
* eval.c (eval): ditto.
@@ -11023,7 +15962,7 @@ Fri Jul 7 03:30:00 2000 Yukihiro Matsumoto <matz@netlab.co.jp>
* parse.y (aref_args): should allow Hash[:a=>2] etc.
- * numeric.c (fix_aref): convert index by NUM2INT, not FIX2INT.
+ * numeric.c (fix_aref): convert index by NUM2INT, not FIX2INT.
(ruby-bugs:#PR37)
* time.c (time_localtime): should prohibit for frozen time.
@@ -11038,7 +15977,7 @@ Thu Jul 6 19:12:12 2000 Yukihiro Matsumoto <matz@netlab.co.jp>
twice.
* ruby.c (require_libraries): clear req_list_head.next after
- execution.
+ execution.
Thu Jul 6 13:51:57 2000 Nobuyoshi Nakada <nobu.nakada@nifty.ne.jp>
@@ -11121,7 +16060,7 @@ Tue Jul 4 09:30:35 2000 Yukihiro Matsumoto <matz@netlab.co.jp>
* parse.y (here_document): supports EOF right after terminator.
- * random.c (rb_f_rand): argument is now optional (rand(max=0)).
+ * random.c (rb_f_rand): argument is now optional (rand(max=0)).
Tue Jul 4 01:50:49 2000 WATANABE Hirofumi <eban@os.rim.or.jp>
@@ -11205,7 +16144,7 @@ Sun Jul 2 03:37:50 2000 Minero Aoki <aamine@dp.u-netsurf.ne.jp>
Sat Jul 1 15:22:35 2000 Yukihiro Matsumoto <matz@netlab.co.jp>
* numeric.c (fix_rshift): should handle shift value more than
- sizeof(long).
+ sizeof(long).
Sat Jul 1 15:22:35 2000 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -11324,7 +16263,7 @@ Fri Jun 23 22:34:51 2000 Katsuyuki Komatsu <komatsu@sarion.co.jp>
Fri Jun 23 01:11:27 2000 Yukihiro Matsumoto <matz@netlab.co.jp>
- * string.c (rb_str_substr): should return empty string (""),
+ * string.c (rb_str_substr): should return empty string (""),
if beg == str.size and len == zero, mostly for convenience and
backward compatibility.
@@ -11365,7 +16304,7 @@ Wed Jun 21 01:18:03 2000 Yukihiro Matsumoto <matz@netlab.co.jp>
* string.c (rb_str_dup): ditto.
* error.c (Init_Exception): renamed NotImplementError to
- NotImplementedError.
+ NotImplementedError.
Tue Jun 20 16:22:38 2000 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -11445,7 +16384,7 @@ Fri Jun 16 21:23:59 2000 WATANABE Hirofumi <eban@os.rim.or.jp>
Fri Jun 16 18:41:58 2000 Koji Arai <JCA02266@nifty.ne.jp>
- * process.c (proc_setsid): BSD-style setpgrp() don't return
+ * process.c (proc_setsid): BSD-style setpgrp() don't return
process group ID, but 0 or -1.
Fri Jun 16 16:23:35 2000 Koji Arai <JCA02266@nifty.ne.jp>
@@ -11472,7 +16411,7 @@ Fri Jun 16 01:57:31 2000 Yukihiro Matsumoto <matz@netlab.co.jp>
Thu Jun 15 10:46:36 2000 Yukihiro Matsumoto <matz@netlab.co.jp>
- * string.c (rb_str_sub_bang): should probagate taintness from
+ * string.c (rb_str_sub_bang): should propagate taintness from
replacement string.
Wed Jun 14 17:01:41 2000 Katsuyuki Komatsu <komatsu@sarion.co.jp>
@@ -11544,7 +16483,7 @@ Tue Jun 13 11:46:17 2000 Yukihiro Matsumoto <matz@netlab.co.jp>
ioctl(fd, TIOCNOTTY, NULL).
* re.c (rb_reg_prepare_re): magic variable $= should affect regex
- pattern match.
+ pattern match.
* time.c (make_time_t): use tm.tm_gmtoff if possible.
@@ -11627,7 +16566,7 @@ Sun Jun 4 02:01:10 2000 WATANABE Hirofumi <eban@os.rim.or.jp>
Sat Jun 3 13:50:06 2000 Yukihiro Matsumoto <matz@netlab.co.jp>
* parse.y (rb_id2name): should support constant attrset
- identifiers.
+ identifiers.
* bignum.c (rb_big_eq): Bignum#== should not raise exception.
@@ -11642,7 +16581,7 @@ Fri Jun 2 00:42:31 2000 Yukihiro Matsumoto <matz@netlab.co.jp>
* eval.c (rb_thread_cleanup): should clear priority for thread
termination.
-Thu Jun 01 22:39:41 2000 Minero Aoki <aamine@dp.u-netsurf.ne.jp>
+Thu Jun 1 22:39:41 2000 Minero Aoki <aamine@dp.u-netsurf.ne.jp>
* lib/net/protocol.rb, smtp.rb, pop.rb, http.rb: 1.1.20.
@@ -11758,7 +16697,7 @@ Wed May 24 13:12:31 2000 Yukihiro Matsumoto <matz@netlab.co.jp>
* misc/ruby-mode.el (ruby-parse-region): support `while .. do'
etc. But corresponding keywords must be at the beginning of
- line.
+ line.
Tue May 23 23:50:12 2000 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -12573,7 +17512,7 @@ Fri Feb 25 12:50:20 2000 Yukihiro Matsumoto <matz@netlab.co.jp>
* eval.c (rb_thread_start_timer): interval changed to 10ms from 50ms.
-Fri Feb 25 06:42:26 2000 GOTOU YUUZOU <gotoyuzo@notwork.org>
+Fri Feb 25 06:42:26 2000 GOTOU Yuuzou <gotoyuzo@notwork.org>
* ext/socket/socket.c (ip_addrsetup): hostp should remain NULL if
host is nil.
@@ -12700,7 +17639,7 @@ Tue Feb 8 02:07:33 2000 Yukihiro Matsumoto <matz@netlab.co.jp>
returns nil if string not changed.
* regex.c (re_compile_pattern): support independent subexpression
- `(?>pattern)'.
+ `(?>pattern)'.
* regex.c (re_match): ditto.
@@ -12741,7 +17680,7 @@ Wed Feb 2 22:14:40 2000 Yukihiro Matsumoto <matz@netlab.co.jp>
* re.c (rb_reg_regsub): should check regs->num_regs.
* re.c (rb_reg_search): remove matchcache, use static struct
- re_register instead.
+ re_register instead.
* re.c (match_getter): avoid cloning match data.
@@ -12850,7 +17789,7 @@ Sat Jan 15 15:03:46 2000 Yukihiro Matsumoto <matz@netlab.co.jp>
* enum.c (enum_index): remove this method.
* enum.c: remove use of pointers to local variables. find,
- find_all, min, max, index, member?, each_with_index,
+ find_all, min, max, index, member?, each_with_index,
* eval.c (massign): multiple assignment does not use to_a anymore.
experimental.
@@ -12906,7 +17845,7 @@ Tue Jan 4 22:25:54 2000 Yukihiro Matsumoto <matz@netlab.co.jp>
Tue Jan 4 06:04:14 2000 WATANABE Hirofumi <eban@os.rim.or.jp>
* configure.in: modify for cross-compiling.
- use target_* instead of host_*.
+ use target_* instead of host_*.
use AC_CANONICAL_TARGET.
* Makefile.in: ditto.
@@ -12940,7 +17879,7 @@ Sat Jan 1 02:04:18 2000 Yukihiro Matsumoto <matz@netlab.co.jp>
* eval.c (rb_thread_safe_level): new method.
* eval.c (rb_yield_0): recycle dyna_var_map to reduce object
- allocation.
+ allocation.
Fri Dec 31 00:52:48 1999 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -13040,7 +17979,7 @@ Sun Dec 19 22:56:31 1999 KANEKO Naoshi <wbs01621@mail.wbs.ne.jp>
Sat Dec 18 03:00:01 1999 Yukihiro Matsumoto <matz@netlab.co.jp>
- * file.c (path_check_1): check should be done by absolute path.
+ * file.c (path_check_1): check should be done by absolute path.
* marshal.c (r_ivar): should restore generic_ivar too.
@@ -13109,7 +18048,7 @@ Wed Dec 8 11:48:23 1999 Yukihiro Matsumoto <matz@netlab.co.jp>
variables of untainted objects.
* variable.c (rb_mod_remove_const): should prohibit constant
- removals too.
+ removals too.
Wed Dec 8 09:23:01 1999 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -13219,7 +18158,7 @@ Fri Nov 26 18:12:49 1999 Yukihiro Matsumoto <matz@netlab.co.jp>
* eval.c (rb_load): save and restore ruby_wrapper around loading.
* eval.c (rb_mark_end_proc): mark end procs registered by END{} or
- at_exit{}.
+ at_exit{}.
* eval.c (rb_set_end_proc): should not call rb_global_variable()
on heap address; it crashed mod_ruby.
@@ -13303,7 +18242,7 @@ Wed Nov 10 21:54:11 1999 EGUCHI Osamu <eguchi@shizuokanet.ne.jp>
Wed Nov 10 17:57:06 1999 Yukihiro Matsumoto <matz@netlab.co.jp>
* sprintf.c: incorporate <yasuf@big.or.jp>'s sprintf patch at
- [ruby-dev:7754].
+ [ruby-dev:7754].
Wed Nov 10 08:28:53 1999 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -13345,7 +18284,7 @@ Thu Nov 4 17:41:18 1999 Yukihiro Matsumoto <matz@netlab.co.jp>
Wed Nov 3 08:52:57 1999 Masaki Fukushima <fukusima@goto.info.waseda.ac.jp>
- * io.c (Init_IO): forgot to use INT2FIX() around SEEK_SET, etc.
+ * io.c (Init_IO): forgot to use INT2FIX() around SEEK_SET, etc.
Wed Nov 3 00:25:20 1999 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -13536,7 +18475,7 @@ Mon Oct 4 00:08:34 1999 Yukihiro Matsumoto <matz@netlab.co.jp>
* pack.c (OFF16): need to adjust pointer address to pack/unpack on
64bit machines.
-Sun Oct 03 03:05:59 1999 WATANABE Hirofumi <eban@os.rim.or.jp>
+Sun Oct 3 03:05:59 1999 WATANABE Hirofumi <eban@os.rim.or.jp>
* time.c (time_arg): mktime y2k problem.
@@ -13574,7 +18513,7 @@ Wed Sep 22 00:06:07 1999 Katsuyuki Komatsu <komatsu@sarion.co.jp>
Tue Sep 21 23:57:54 1999 Yukihiro Matsumoto <matz@netlab.co.jp>
* eval.c (call_trace_func): should not propagate retval in
- trace_func.
+ trace_func.
Mon Sep 20 21:35:39 1999 Katsuyuki Komatsu <komatsu@sarion.co.jp>
@@ -13912,7 +18851,7 @@ Wed Jul 28 18:24:45 1999 Yukihiro Matsumoto <matz@netlab.co.jp>
Tue Jul 27 09:38:08 1999 EGUCHI Osamu <eguchi@shizuokanet.ne.jp>
* eval.c (rb_eval): reduce recursive rb_eval() calls by
- NODE_BLOCKs.
+ NODE_BLOCKs.
Tue Jul 27 01:20:40 1999 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp>
@@ -14001,7 +18940,7 @@ Fri Jul 16 01:37:50 1999 Koji Arai <JCA02266@nifty.ne.jp>
* string.c (rb_str_reverse): should always return copy.
-Thu Jul 15 23:25:57 1999 NAKAMURA Hiroshi <nakahiro@sarion.co.jp>
+Thu Jul 15 23:25:57 1999 NAKAMURA, Hiroshi <nakahiro@sarion.co.jp>
* lib/debug.rb: better display & frame treatment.
@@ -15365,7 +20304,7 @@ Fri Feb 12 16:16:47 1999 Yasuhiro Fukuma <yasuf@big.or.jp>
Fri Feb 12 16:21:17 1999 Yukihiro Matsumoto <matz@netlab.co.jp>
- * eval.c (rb_thread_fd_close):
+ * eval.c (rb_thread_fd_close):
* io.c (rb_io_fptr_close): tell scheduler that fd is closed.
@@ -16122,7 +21061,7 @@ Fri Oct 16 15:31:45 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
Thu Oct 15 13:54:48 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* parse.y (arg): local variables can be accessed within right side
- expression in assignment, notably in blocks.
+ expression in assignment, notably in blocks.
Wed Oct 14 00:18:33 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -16171,7 +21110,7 @@ Thu Oct 8 13:32:13 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* time.c (time_dump): marshal can dump Time object now.
* marshal.c (Init_marshal): rename marshal methods `_dump_to' to
- `_dump', `_load_from' to `_load'.
+ `_dump', `_load_from' to `_load'.
* parse.y (rb_intern): "+=".intern generates proper symbol.
@@ -16216,7 +21155,7 @@ Thu Sep 17 17:09:05 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
Tue Sep 15 05:28:11 1998 Koji Arai <JCA02266@nifty.ne.jp>
* regex.c (re_compile_pattern): forgot to fixup for the pattern
- like (?=(A)|(B)).
+ like (?=(A)|(B)).
Tue Sep 15 01:06:08 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -16254,7 +21193,7 @@ Tue Sep 8 18:05:33 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
Tue Sep 8 10:03:39 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* string.c (str_each_line): wrong line splitting with newline at
- top of the string.
+ top of the string.
* string.c: non bang methods return copied string.
@@ -16293,7 +21232,7 @@ Mon Aug 31 15:32:41 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
Sat Aug 29 16:31:40 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
- * eval.c (rb_check_safe_str): avoid calling rb_id2name() in normal
+ * eval.c (rb_check_safe_str): avoid calling rb_id2name() in normal
cases to speed-up.
* eval.c (thread_raise): do not save context of terminated thread.
@@ -16375,8 +21314,8 @@ Wed Aug 19 00:31:09 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
Fri Aug 14 11:01:47 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* eval.c (call_trace_func): save __FILE__, __LINE__ before
- executing trace_func, since trace function should not corrupt
- line number information.
+ executing trace_func, since trace function should not corrupt
+ line number information.
Thu Aug 13 15:09:02 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -16393,7 +21332,7 @@ Mon Aug 10 14:05:30 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
Fri Aug 7 17:44:44 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* error.c (err_snprintf): replace sprintf for fixed sized buffer,
- with snprintf to avoid buffer over-run. For systems which does
+ with snprintf to avoid buffer over-run. For systems which does
dot provide snprintf, missing/snprintf.c added.
Wed Aug 5 00:47:35 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -16488,7 +21427,7 @@ Fri Jul 17 08:01:49 1998 Tadayoshi Funaba <tadf@kt.rim.or.jp>
Thu Jul 16 22:58:48 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* string.c (scan_once): substrings to the block should not be
- tainted. use reg_nth_match(), not str_substr().
+ tainted. use reg_nth_match(), not str_substr().
* string.c (str_substr): needed to transfer taint.
@@ -16506,7 +21445,7 @@ Wed Jul 15 15:11:57 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
Wed Jul 15 15:05:27 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* eval.c (thread_create): exit() and abort() in threads now
- forwarded to main_thread.
+ forwarded to main_thread.
Tue Jul 14 14:03:47 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -16570,7 +21509,7 @@ Fri Jul 3 11:20:46 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
Thu Jul 2 12:49:21 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* marshal.c (w_object): remove `write_bignum' label for 64bit
- architectures.
+ architectures.
* marshal.c (r_bytes): needs int, not long.
@@ -16586,7 +21525,7 @@ Tue Jun 30 10:13:44 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* io.c (f_p): accepts arbitrary number of arguments.
- * eval.c (rb_yield_0): there's some case that iterator_p() returns
+ * eval.c (rb_yield_0): there's some case that iterator_p() returns
true even if the_block was not set. check added.
Tue Jun 30 01:05:20 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -16632,7 +21571,7 @@ Tue Jun 23 11:46:16 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
Sat Jun 20 02:53:50 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* parse.y (assignable): nesting local variables should have higher
- priority than normal local variables for assignment too.
+ priority than normal local variables for assignment too.
Fri Jun 19 18:28:19 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -16666,7 +21605,7 @@ Thu Jun 18 13:37:19 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
Wed Jun 17 11:20:00 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* parse.y (gettable): nesting local variables should have higher
- priority than normal local variables.
+ priority than normal local variables.
Tue Jun 16 12:30:46 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -16748,8 +21687,8 @@ Sat Jun 6 22:50:52 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
{start,stop}_nowidth.
* regex.c (re_match): various features imported from GNU regex.c
- 0.12, such as nested grouping, avoiding infinite loop with empty
- match, etc.
+ 0.12, such as nested grouping, avoiding infinite loop with empty
+ match, etc.
* regex.c (register_info_type): now use union.
@@ -16773,7 +21712,7 @@ Tue Jun 2 16:00:12 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
Sat May 30 07:10:02 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* re.c (reg_prepare_re): no more needless regular expression
- recompile on casefold conditions.
+ recompile on casefold conditions.
Thu May 28 18:02:55 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -16837,10 +21776,10 @@ Tue May 19 16:36:26 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
Tue May 19 16:31:57 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* parse.y (assignable): specification changed for in-block
- variable definition.
+ variable definition.
* eval.c (dyna_var_asgn): error in in-block variables' compile
- time definition.
+ time definition.
* parse.y (str_extend): wrong nesting detection.
@@ -16884,7 +21823,7 @@ Thu May 14 14:44:21 1998 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp>
Thu May 14 14:03:16 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* random.c (RANDOM_MAX): guessing proper maximum value for random
- numbers.
+ numbers.
* random.c (f_rand): use drand48 if possible.
@@ -16954,7 +21893,7 @@ Wed May 6 01:37:39 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* eval.c: remove global variable `errat'.
* eval.c (rb_longjmp): embed error position information in the
- exception object.
+ exception object.
Sat May 2 12:20:02 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -16992,7 +21931,7 @@ Thu Apr 30 01:08:35 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* lib/tk.rb: call 'unknown', if proc not defined.
- * eval.c (handle_rescue): default rescue handles `Exceptional' not
+ * eval.c (handle_rescue): default rescue handles `Exceptional' not
only the instance of the `Exception's.
* eval.c (f_raise): exception can be any object.
@@ -17051,7 +21990,7 @@ Fri Apr 24 14:35:45 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* random.c (f_srand): use tv_usec as the default seed.
* eval.c (rb_eval): values of nested local variables should be
- independent.
+ independent.
* eval.c (rb_yield_0): local variables wrong nested conditions.
@@ -17060,7 +21999,7 @@ Wed Apr 22 23:27:17 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* io.c (select_get_io): get IO object by `to_io'.
* io.c (io_to_io): method to retrieve IO object, from delegating
- object for example.
+ object for example.
Wed Apr 22 16:52:37 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -17112,7 +22051,7 @@ Sat Apr 18 03:53:27 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
each_with_index instead.
* class.c (rb_include_module): check for super modules, since
- module's included modules may be changed.
+ module's included modules may be changed.
Fri Apr 17 21:50:47 1998 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp>
@@ -17145,7 +22084,7 @@ Thu Apr 16 16:52:01 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* lib/tk.rb: thread support (experimental - maybe slow).
* eval.c (rb_longjmp): trace event on exception in raising
- context, just before raising exception.
+ context, just before raising exception.
* struct.c (struct_s_members): forgot to check singletons.
@@ -17162,8 +22101,8 @@ Thu Apr 16 01:38:02 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* file.c (thread_flock): do not block other threads.
* eval.c (thread_trap_eval): signals are now delivered to the
- current thread again. In case that the current thread is dead,
- signals are forwarded to the main thread.
+ current thread again. In case that the current thread is dead,
+ signals are forwarded to the main thread.
* string.c (str_new4): need not to duplicate frozen strings.
@@ -17185,7 +22124,7 @@ Wed Apr 15 01:22:56 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
receiver.
* io.c (io_puts): avoid generating "\n" each time. use RS_default
- instead.
+ instead.
* io.c (f_p): ditto.
@@ -17250,7 +22189,7 @@ Thu Apr 9 18:24:58 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* experimental release 1.1b9_09.
* string.c (str_cmp): do not depend on sentinel at the end of the
- strings.
+ strings.
* string.c (str_chomp_bang): forgot to set the sentinel.
@@ -17303,7 +22242,7 @@ Tue Apr 7 01:16:45 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* class.c (ins_methods_i): exclude protected methods.
* eval.c (PUSH_BLOCK): dynamic variables can be accessed from
- eval() with bindings.
+ eval() with bindings.
Mon Apr 6 14:49:06 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -17315,7 +22254,7 @@ Fri Apr 3 13:07:29 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
conditions.
* variable.c (rb_name_class): set classname by id before String
- class is initialized (1.0 behavior restored).
+ class is initialized (1.0 behavior restored).
Fri Apr 3 11:25:45 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -17326,7 +22265,7 @@ Fri Apr 3 11:25:45 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* numeric.c (num_zero_p): new method.
* numeric.c (num_nonzero_p): new method. returns the receiver if
- it's not zero.
+ it's not zero.
* eval.c (obj_instance_eval): the_class should be the object's
singleton class.
@@ -17355,7 +22294,7 @@ Tue Mar 31 13:23:58 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* eval.c (bind_s_new): new method.
* numeric.c (num2int): raise exception if Fixnums too big to
- convert into `int' in case that sizeof(int) < sizeof(INT).
+ convert into `int' in case that sizeof(int) < sizeof(INT).
* string.c (str_center): SEGV on negative width.
@@ -17383,7 +22322,7 @@ Mon Mar 30 01:44:13 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* eval.c (mod_module_eval): ditto
* file.c (file_s_umask): umask did not return old values, if no
- argument given.
+ argument given.
Sun Mar 29 00:54:23 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -17402,7 +22341,7 @@ Sat Mar 28 16:07:11 1998 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp>
Sat Mar 28 00:47:19 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* eval.c (f_local_variables): new method to return an array of
- local variable names.
+ local variable names.
* variable.c (obj_instance_variables): now returns an array of
variable names, as described in the reference.
@@ -17417,8 +22356,8 @@ Fri Mar 27 13:49:27 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* ruby.c (ruby_prog_init): `site_ruby' added to load_path.
* ruby.c (ruby_prog_init): load-path order changed. Paths in
- the RUBYLIB environment variable comes first in non-tainted
- mode.
+ the RUBYLIB environment variable comes first in non-tainted
+ mode.
Thu Mar 26 11:51:09 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -17436,14 +22375,14 @@ Wed Mar 25 21:20:13 1998 Tadayoshi Funaba <tadf@kt.rim.or.jp>
Wed Mar 25 08:12:07 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* numeric.c (flo_modulo): caused SEGV if left operand is not a
- float value.
+ float value.
* eval.c (f_eval): optional third and fourth argument to specify
file-name and line-number.
* eval.c (eval): file-name and line-number set properly.
- * parse.y (assign_in_cond): literal assignment is now warning, not
+ * parse.y (assign_in_cond): literal assignment is now warning, not
compile error.
* error.c (Warn): Warn() always print message, OTOH Waring()
@@ -17464,10 +22403,10 @@ Mon Mar 23 12:44:12 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
Fri Mar 20 16:40:34 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* string.c (str_chomp_bang): chomp! (and other ! methods) returns
- nil if it does not modify the string.
+ nil if it does not modify the string.
* string.c (str_sub_iter_s): should check last pattern since it
- may be matched to null.
+ may be matched to null.
Thu Mar 19 13:48:55 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -17480,7 +22419,7 @@ Wed Mar 18 17:46:31 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* ruby.c (load_file): new file object constant DATA. Only
available for the script from the file.
- * regex.c (re_match): forwarding failure point popped too much.
+ * regex.c (re_match): forwarding failure point popped too much.
Tue Mar 17 18:23:06 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -17491,7 +22430,7 @@ Tue Mar 17 18:23:06 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* bignum.c (bigdivmod): calculates modulo.
* numeric.c (fix_remainder): returns reminder, formerly introduced
- as modulo.
+ as modulo.
* numeric.c (fix_modulo): calculates proper `modulo'.
@@ -17523,7 +22462,7 @@ Mon Mar 16 11:49:25 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* range.c (range_length): returns zero, if the first is greater
than the last.
- * signal.c (trap_restore_mask): restore signal mask before raising
+ * signal.c (trap_restore_mask): restore signal mask before raising
exceptions and throws.
Fri Mar 13 13:49:24 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -17562,7 +22501,7 @@ Wed Mar 11 02:14:17 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
NOEX_UNDEF-ness, if exists.
* class.c (rb_define_method): disables superclass's overriding
- method by default.
+ method by default.
Wed Mar 11 01:40:48 1998 MAEDA shugo <shugo@po.aianet.ne.jp>
@@ -17594,7 +22533,7 @@ Fri Mar 6 17:23:07 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* object.c (obj_alloc): check for allocating instance for the
primitive classes (mostly perfect).
- * ext/curses/curses.c (curses_finalize): restore original state at
+ * ext/curses/curses.c (curses_finalize): restore original state at
interpreter termination.
* ext/curses/curses.c (curses_addstr): forgot to check argument
@@ -17616,7 +22555,7 @@ Wed Mar 4 01:39:52 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* eval.c (rb_eval): block arg support.
* parse.y (f_block_arg): new syntax - block argument in the
- formal arglist.
+ formal arglist.
Tue Mar 3 14:20:15 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -17640,7 +22579,7 @@ Tue Mar 3 11:21:28 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
Tue Mar 3 08:04:56 1998 Tadayoshi Funaba <tadf@kt.rim.or.jp>
* struct.c (struct_alloc): incomplete struct initialization made
- GC to access unallocated addresses.
+ GC to access unallocated addresses.
Mon Mar 2 16:28:27 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -17664,7 +22603,7 @@ Thu Feb 26 17:22:13 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* parse.y (fname): convert reswords into symbols.
* parse.y (reswords): reserved words are now embedded in the
- syntax (sigh).
+ syntax (sigh).
* parse.y: now reserved words can be method names safely.
@@ -17673,7 +22612,7 @@ Wed Feb 25 15:50:07 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* eval.c (mod_module_eval): clear the_scope's PRIVATE flag before
calling eval().
- * gc.c (gc_call_finalizer_at_exit): run finalizers before any data
+ * gc.c (gc_call_finalizer_at_exit): run finalizers before any data
object being freed.
* eval.c (rb_eval): needed to keep prot_tag->retval before
@@ -17682,10 +22621,10 @@ Wed Feb 25 15:50:07 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
Tue Feb 24 11:16:32 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* parse.y (yylex): reserved words can be appear as method names at
- right after 'def' and `.'(dot), like foo.next.
+ right after 'def' and `.'(dot), like foo.next.
* eval.c (return_check): checks for return out of thread (formerly
- done in return_value).
+ done in return_value).
* eval.c (POP_TAG): copy retval to outer level.
@@ -17697,7 +22636,7 @@ Tue Feb 24 11:16:32 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
closing parentheses.
* parse.y (assignable): should not assign dyna_var to true, if it
- is already defined.
+ is already defined.
Mon Feb 23 14:35:03 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -17722,9 +22661,9 @@ Fri Feb 20 10:17:51 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* ext/kconv/kconv.c (kconv_kconv): default output code now be
determined according to the value of $KCODE.
- * re.c (rb_get_kcode): can retrieve $KCODE from C code.
+ * re.c (rb_get_kcode): can retrieve $KCODE from C code.
- * parse.y (stmt): if/unless modifiers returns nil, if condition is
+ * parse.y (stmt): if/unless modifiers returns nil, if condition is
not established.
Thu Feb 19 11:06:47 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -17748,7 +22687,7 @@ Wed Feb 18 18:45:10 1998 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp>
Wed Feb 18 00:41:31 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* string.c (str_sub_s): needed to be mbchar aware to increment one
- character.
+ character.
* regex.c (re_match): \Z matches newline just before the end of
the string.
@@ -17756,11 +22695,11 @@ Wed Feb 18 00:41:31 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
Tue Feb 17 00:04:32 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* time.c (time_arg): Time.gm and Time.local now understands
- Time#to_a format.
+ Time#to_a format.
* string.c (str_sub_s): replace happened twice for null pattern.
- * regex.c (re_search): null pattern should not match after newline
+ * regex.c (re_search): null pattern should not match after newline
at the end of string.
* time.c (time_isdst): now returns boolean value.
@@ -17778,7 +22717,7 @@ Mon Feb 16 23:55:40 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* eval.c (blk_free): release duplicated block informations.
* eval.c (blk_copy_prev): duplicate outer block information into
- the heap, when proc/binding created.
+ the heap, when proc/binding created.
Mon Feb 16 14:38:25 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -17789,7 +22728,7 @@ Mon Feb 16 14:38:25 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
Mon Feb 16 13:28:33 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* regex.c (re_compile_pattern): need to fetch mbchar's second byte
- without translation.
+ without translation.
Mon Feb 16 12:29:27 1998 MAEDA shugo <shugo@po.aianet.ne.jp>
@@ -17809,8 +22748,8 @@ Tue Feb 10 17:29:08 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* string.c (str_oct): does recognize `0x'.
- * sprintf.c (f_sprintf): use base 10 for conversion from string to
- integer.
+ * sprintf.c (f_sprintf): use base 10 for conversion from string to
+ integer.
Mon Feb 9 14:51:56 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -17837,7 +22776,7 @@ Thu Feb 5 18:58:46 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
Thu Feb 5 14:10:35 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* parse.y (primary): singleton class def can be appeared inside
- method bodies.
+ method bodies.
* hash.c (hash_replace): replace content.
@@ -17918,7 +22857,7 @@ Thu Jan 22 16:21:08 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* eval.c (rb_eval): some singleton class def cause SEGV.
* eval.c (TMP_ALLOC): replace ALLOCA_N, where thread context
- switch may happen.
+ switch may happen.
Wed Jan 21 01:43:42 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -17928,7 +22867,7 @@ Wed Jan 21 01:43:42 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* regex.c (re_compile_pattern): too many pops for non register
subexpr.
- * parse.y (yylex): open parentheses after identifiers are argument
+ * parse.y (yylex): open parentheses after identifiers are argument
list, even if whitespaces have seen.
Tue Jan 20 15:19:59 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -17938,14 +22877,14 @@ Tue Jan 20 15:19:59 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* ext/tcltklib/extconf.rb: more accurate check for tcl/tk libs.
* file.c (rb_stat): most of the FileTest methods (and function
- `test') accept File objects as the argument.
+ `test') accept File objects as the argument.
Tue Jan 19 18:19:24 1998 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp>
* ext/extmk.rb.in (install): there should be no newline after install:
* re.c (MIN): renamed from min(). there's a local variable named
- min in the file, so that some cpp will raise an error.
+ min in the file, so that some cpp will raise an error.
Mon Jan 19 16:30:05 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -18006,7 +22945,7 @@ Tue Jan 6 00:27:43 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* parse.y: syntax modified for `while expr do .. end' etc.
* process.c (f_exec,f_system): can supply arbitrary name for the
- new process.
+ new process.
Mon Jan 5 16:59:13 1998 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp>
@@ -18023,7 +22962,7 @@ Sat Jan 3 19:14:14 1998 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp>
Fri Jan 2 20:38:59 1998 Yukihiro Matsumoto <matz@netlab.co.jp>
* ext/curses/curses.c (NUM2CHAR): uses the first character for
- string arguments.
+ string arguments.
* array.c (ary_fill): did not extend array for ranges.
@@ -18041,15 +22980,15 @@ Tue Dec 30 11:46:23 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
environment variable are not world writable.
* ruby.c (load_file): invoke specified interpreter if the #! line
- does not contain the word `ruby'.
+ does not contain the word `ruby'.
Fri Dec 26 03:26:41 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* string.c (uscore_get): type information included in the error
- message.
+ message.
* variable.c (f_untrace_var): does not free trace-data within
- trace procedure.
+ trace procedure.
Thu Dec 25 02:50:29 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -18059,11 +22998,11 @@ Thu Dec 25 02:50:29 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
Tue Dec 23 02:47:33 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
- * eval.c (rb_eval): public/private information kept in the current
+ * eval.c (rb_eval): public/private information kept in the current
scope, to remove undesired state from the class/module.
* time.c (time_strftime): remove hidden limit of 100 bytes of
- result string, using malloc'ed buffer.
+ result string, using malloc'ed buffer.
* hash.c (hash_update): merges the contents of another hash,
overriding existing keys.
@@ -18081,7 +23020,7 @@ Fri Dec 19 01:18:29 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* version 1.1b2 released.
- * eval.c (check_errat): check and convert (if necessary) traceback
+ * eval.c (check_errat): check and convert (if necessary) traceback
information before assigning to the variable $@.
* eval.c (f_raise): optional third argument to specify traceback
@@ -18119,7 +23058,7 @@ Thu Dec 18 00:25:03 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
Tue Dec 16 14:57:43 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
- * string.c (scan_once): wrong exception for regexp that match with
+ * string.c (scan_once): wrong exception for regexp that match with
null string (use substr instead of subseq).
Sat Dec 13 00:13:32 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -18131,10 +23070,10 @@ Sat Dec 13 00:13:32 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
Fri Dec 12 00:50:25 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* parse.y (expr): warns if BEGIN or END appear in the method
- bodies.
+ bodies.
* string.c (str_match): calls y =~ x if y is neither String nor
- Regexp so that eregex.rb works.
+ Regexp so that eregex.rb works.
* eval.c (f_at_exit): to register end proc.
@@ -18148,7 +23087,7 @@ Fri Dec 12 00:50:25 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* eval.c (rb_eval): new visibility status `function'.
- * parse.y (yycompile): do not clear eval_tree. thus enable multiple
+ * parse.y (yycompile): do not clear eval_tree. thus enable multiple
command line script by option `-e'.
* eval.c (rb_eval): END execute just once.
@@ -18159,7 +23098,7 @@ Thu Dec 11 13:14:35 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* object.c (mod_le): Module (or Class) comparison.
- * eval.c (rb_remove_method): raises NameError if named method does
+ * eval.c (rb_remove_method): raises NameError if named method does
not exist.
* ext/curses/curses.c: remove CHECK macro for BSD curses.
@@ -18186,7 +23125,7 @@ Tue Dec 9 10:05:17 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* eval.c (ruby_run): call finalizers at process termination.
* gc.c (gc_call_finalizer_at_exit): call free proc for every Data
- Wrapper, and finalizer for specified objects at termination.
+ Wrapper, and finalizer for specified objects at termination.
* version.c (show_version): version format changed.
@@ -18200,7 +23139,7 @@ Mon Dec 8 19:00:15 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* io.c (io_puts): just put a newline if no argument given.
* ext/tcltklib/tcltklib.c (lib_mainloop): thread-aware tk handle
- when $tk_thread_safe is set.
+ when $tk_thread_safe is set.
* ext/tcltklib/tcltklib.c (lib_mainloop): use Tcl_DoOneEvent()
instead of Tk_MainLoop().
@@ -18211,7 +23150,7 @@ Mon Dec 6 07:11:16 1997 MAEDA shugo <shugo@po.aianet.ne.jp>
Fri Dec 5 18:17:17 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
- * eval.c (mod_remove_method): remove (not undef) a method from the
+ * eval.c (mod_remove_method): remove (not undef) a method from the
class/module.
* variable.c (obj_remove_instance_variable): method to remove
@@ -18229,7 +23168,7 @@ Mon Dec 1 15:24:41 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
Wed Nov 26 18:18:05 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
- * lib/mkmf.rb: generate Makefile for extension modules out of ruby
+ * lib/mkmf.rb: generate Makefile for extension modules out of ruby
source tree. use like `ruby -r mkmf extconf.rb'.
* numeric.c (fix2str): enlarge buffer to prevent overflow on some
@@ -18251,7 +23190,7 @@ Tue Nov 18 10:13:08 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* array.c (Init_Array): Array#=== works as Array#include?
- * regex.c (re_compile_pattern): insert initialize code for jump_n,
+ * regex.c (re_compile_pattern): insert initialize code for jump_n,
before entering loops.
* re.c (reg_search): does not save registers unless $& etc appear
@@ -18260,7 +23199,7 @@ Tue Nov 18 10:13:08 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
Mon Nov 17 13:01:43 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* eval.c (is_defined): add defined? check for receivers and
- arguments for calls.
+ arguments for calls.
* re.c (reg_search): cache last match object.
@@ -18277,7 +23216,7 @@ Fri Nov 14 18:28:40 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
Thu Nov 13 14:39:06 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
- * string.c (str_sub): returns copy of the receiver string, even if
+ * string.c (str_sub): returns copy of the receiver string, even if
any substitution occurred.
* regex.c (re_compile_pattern): no-width match by (?=..), (?!..).
@@ -18300,7 +23239,7 @@ Mon Nov 10 11:24:51 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
Fri Nov 7 16:58:24 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
- * regex.c (re_compile_pattern): perl5 regxp \A and \Z available.
+ * regex.c (re_compile_pattern): perl5 regexp \A and \Z available.
* regex.c (re_compile_pattern): can expand compile stack dynamically.
@@ -18338,7 +23277,7 @@ Fri Oct 24 10:58:53 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
Thu Oct 23 11:17:44 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* io.c (io_errset): value of $stderr can be changed (to any IO
- object).
+ object).
* io.c (next_argv): $< can be anything that responds to `write'.
@@ -18355,7 +23294,7 @@ Wed Oct 22 12:52:30 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* file.c: File::Constants added for inclusion.
* array.c (ary_join): call ary_join() recursively for the 1st
- array element.
+ array element.
Mon Oct 20 12:18:29 1997 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp>
@@ -18376,7 +23315,7 @@ Wed Oct 15 17:49:24 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
Mon Oct 13 16:54:18 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* class.c (rb_define_class_id): call superclass's `inherited'
- method when making subclasses.
+ method when making subclasses.
* parse.y (nextc): clear lex_lastline at the end of file.
@@ -18386,13 +23325,13 @@ Mon Oct 13 16:54:18 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
Thu Oct 9 11:17:50 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
- * eval.c (error_print): the exception name follows after the error
+ * eval.c (error_print): the exception name follows after the error
message.
* eval.c (compile_error): error message slightly changed.
* parse.y (nextc): script parsing will be terminated by __END__ at
- beginning of line.
+ beginning of line.
* eval.c (compile_error): `__END__' is no longer a keyword.
@@ -18434,7 +23373,7 @@ Thu Oct 2 11:38:31 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* version 1.1 alpha8 released.
* ext/marshal/marshal.c (r_object): remove temporal regist for
- structs. (caused problem if structs form cycles.)
+ structs. (caused problem if structs form cycles.)
* parse.y (match_gen): static binding for match(=~) calls
with regexp literals.
@@ -18458,11 +23397,11 @@ Wed Oct 1 10:30:22 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* ext/marshal/marshal.c (marshal_dump): try to set binmode.
- * ext/marshal/marshal.c (r_object): forgot to re-regist structs in
+ * ext/marshal/marshal.c (r_object): forgot to re-regist structs in
the object table.
* eval.c (ruby_options): call Init_ext() before any require()
- calls by `-r'.
+ calls by `-r'.
Fri Sep 30 14:29:22 1997 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp>
@@ -18619,7 +23558,7 @@ Mon Sep 1 13:42:48 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* version 1.1 alpha5 released.
* object.c (mod_attr_reader): create methods to define attribute
- reader/write/accessor.
+ reader/write/accessor.
* class.c (rb_define_attr): always defines accessors.
@@ -18634,7 +23573,7 @@ Mon Sep 1 11:43:57 1997 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp>
Fri Aug 29 11:10:21 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* class.c (class_instance_methods): same method names should not
- appear more than once.
+ appear more than once.
* parse.y (yylex): spaces can follow =begin/=end.
@@ -18642,27 +23581,27 @@ Fri Aug 29 11:10:21 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
unnamed fundamental classes, such as Object, String, etc.
* variable.c (rb_name_class): can't name class before String class
- is initialized.
+ is initialized.
* inits.c (rb_call_inits): unrecognized dependency from GC to
Array.
* variable.c (find_class_path): could not find class if Object's
- iv_tbl is NULL.
+ iv_tbl is NULL.
Thu Aug 28 13:12:05 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* version 1.1 alpha4 released.
* variable.c (mod_constants): wrong condition for singleton
- class.
+ class.
* parse.y (yylex): revised `=begin' skip code.
* parse.y (here_document): forgot to free(eos).
* parse.y (yylex): spaces after `<<' prohibited for here
- documents to avoid confusing with operator `<<'.
+ documents to avoid confusing with operator `<<'.
* eval.c (is_defined): separated from rb_eval().
@@ -18688,12 +23627,12 @@ Tue Aug 26 13:43:47 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* eval.c (error_print): no class name print for anonymous class.
* eval.c (rb_longjmp): proper exception raised if raise() called
- without arguments, with $! or $@ set.
+ without arguments, with $! or $@ set.
* object.c (Init_Object): superclass()'s method argument setting
- was wrong again.
+ was wrong again.
- * class.c (mod_ancestors): list superclasses and included modules
+ * class.c (mod_ancestors): list superclasses and included modules
in priority order.
Mon Aug 25 11:53:11 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
@@ -18701,7 +23640,7 @@ Mon Aug 25 11:53:11 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* version 1.1 alpha2 released.
* sample/ruby-mode.el (ruby-parse-region): auto-indent now
- supports "\\" in the strings.
+ supports "\\" in the strings.
* struct.c (struct_getmember): new API to get member value from C
language side.
@@ -18727,7 +23666,7 @@ Thu Aug 21 11:36:58 1997 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp>
Wed Aug 20 17:28:50 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* ruby.c (ruby_process_options): require() all modules after
- processing all options
+ processing all options
* process.c (rb_proc_exec): more security checks added.
@@ -18740,10 +23679,10 @@ Tue Aug 19 00:15:38 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* version 1.1 alpha1 released.
* eval.c (mod_eval): work as normal eval() if second binding
- argument given.
+ argument given.
* eval.c (rb_call): did not raise ArgumentError if too many
- arguments more than optional arguments (without rest arg).
+ arguments more than optional arguments (without rest arg).
* eval.c (rb_eval): did not work well for op_asgn2 (attribute
self assignment).
@@ -18757,14 +23696,14 @@ Mon Aug 18 09:25:56 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* parse.y: provides more accurate line number information.
* eval.c (thread_value): include value's backtrace information in
- the variable `$@'.
+ the variable `$@'.
* eval.c (f_abort): print backtrace and exit.
Sat Aug 16 00:17:44 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* eval.c (class_new_instance): do not make instance from virtual
- classes.
+ classes.
* object.c (class_s_new): do not make subclass of singleton class.
@@ -18785,3 +23724,14 @@ Fri Aug 15 19:40:43 1997 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp>
Wed Aug 13 17:51:46 1997 Yukihiro Matsumoto <matz@netlab.co.jp>
* version 1.1 alpha0 released.
+
+Local variables:
+add-log-time-format: (lambda ()
+ (let* ((time (current-time))
+ (diff (+ (cadr time) 32400))
+ (lo (% diff 65536))
+ (hi (+ (car time) (/ diff 65536))))
+ (format-time-string "%a %b %e %H:%M:%S %Y" (list hi lo) t)))
+indent-tabs-mode: t
+tab-width: 8
+end:
diff --git a/LEGAL b/LEGAL
index 8a87bfa668..dce7c1acbf 100644
--- a/LEGAL
+++ b/LEGAL
@@ -134,6 +134,7 @@ st.[ch]:
x68/*:
missing/alloca.c:
missing/dup2.c:
+missing/erf.c:
missing/finite.c:
missing/hypot.c:
missing/isinf.c:
diff --git a/MANIFEST b/MANIFEST
index 670adab510..e6270afa56 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -41,6 +41,7 @@ lex.c
main.c
marshal.c
math.c
+mdoc2man.rb
missing.h
mkconfig.rb
node.h
@@ -83,6 +84,7 @@ bcc32/setup.mak
bin/erb
bin/irb
cygwin/GNUmakefile.in
+djgpp/GNUmakefile.in
djgpp/README.djgpp
djgpp/config.hin
djgpp/config.sed
@@ -94,18 +96,14 @@ doc/forwardable.rd.ja
doc/irb/irb-tools.rd.ja
doc/irb/irb.rd
doc/irb/irb.rd.ja
-doc/net/http.rd.ja
-doc/net/pop.rd.ja
-doc/net/smtp.rd.ja
doc/shell.rd
doc/shell.rd.ja
ext/Setup
+ext/Setup.atheos
ext/Setup.dj
ext/Setup.emx
ext/Setup.nt
ext/Setup.x68
-ext/aix_mksym.rb
-ext/configsub.rb
ext/extmk.rb
lib/English.rb
lib/Env.rb
@@ -115,25 +113,37 @@ lib/benchmark.rb
lib/cgi-lib.rb
lib/cgi.rb
lib/cgi/session.rb
+lib/cgi/session/pstore.rb
lib/complex.rb
+lib/csv.rb
lib/date.rb
lib/date/format.rb
lib/date2.rb
lib/debug.rb
lib/delegate.rb
+lib/drb.rb
+lib/drb/drb.rb
+lib/drb/eq.rb
+lib/drb/extserv.rb
+lib/drb/extservm.rb
+lib/drb/gw.rb
+lib/drb/invokemethod.rb
+lib/drb/observer.rb
+lib/drb/timeridconv.rb
+lib/drb/unix.rb
lib/e2mmap.rb
lib/erb.rb
lib/eregex.rb
lib/fileutils.rb
-lib/final.rb
lib/finalize.rb
lib/find.rb
lib/forwardable.rb
lib/ftools.rb
-lib/ftplib.rb
lib/getoptlong.rb
lib/getopts.rb
+lib/gserver.rb
lib/importenv.rb
+lib/ipaddr.rb
lib/irb.rb
lib/irb/cmd/chws.rb
lib/irb/cmd/fork.rb
@@ -160,9 +170,7 @@ lib/irb/lc/error.rb
lib/irb/lc/help-message
lib/irb/lc/ja/error.rb
lib/irb/lc/ja/help-message
-lib/irb/loader.rb
lib/irb/locale.rb
-lib/irb/multi-irb.rb
lib/irb/ruby-lex.rb
lib/irb/ruby-token.rb
lib/irb/slex.rb
@@ -188,22 +196,34 @@ lib/observer.rb
lib/open-uri.rb
lib/open3.rb
lib/optparse.rb
+lib/optparse/date.rb
lib/optparse/shellwords.rb
lib/optparse/time.rb
lib/optparse/uri.rb
lib/ostruct.rb
lib/parsearg.rb
lib/parsedate.rb
+lib/pathname.rb
lib/ping.rb
lib/pp.rb
lib/prettyprint.rb
lib/profile.rb
+lib/profiler.rb
lib/pstore.rb
lib/racc/parser.rb
lib/rational.rb
lib/readbytes.rb
lib/resolv-replace.rb
lib/resolv.rb
+lib/rubyunit.rb
+lib/runit/assert.rb
+lib/runit/cui/testrunner.rb
+lib/runit/error.rb
+lib/runit/testcase.rb
+lib/runit/testresult.rb
+lib/runit/testsuite.rb
+lib/runit/topublic.rb
+lib/scanf.rb
lib/set.rb
lib/shell.rb
lib/shell/builtin-command.rb
@@ -216,14 +236,30 @@ lib/shell/version.rb
lib/shellwords.rb
lib/singleton.rb
lib/sync.rb
-lib/telnet.rb
lib/tempfile.rb
+lib/test/unit.rb
+lib/test/unit/assertionfailederror.rb
+lib/test/unit/assertions.rb
+lib/test/unit/error.rb
+lib/test/unit/failure.rb
+lib/test/unit/testcase.rb
+lib/test/unit/testresult.rb
+lib/test/unit/testsuite.rb
+lib/test/unit/ui/console/testrunner.rb
+lib/test/unit/ui/fox/testrunner.rb
+lib/test/unit/ui/gtk/testrunner.rb
+lib/test/unit/ui/testrunnermediator.rb
+lib/test/unit/ui/testrunnerutilities.rb
+lib/test/unit/util/observable.rb
+lib/test/unit/util/procwrapper.rb
lib/thread.rb
lib/thwait.rb
lib/time.rb
lib/timeout.rb
+lib/tmpdir.rb
lib/tracer.rb
lib/tsort.rb
+lib/un.rb
lib/uri.rb
lib/uri/common.rb
lib/uri/ftp.rb
@@ -233,6 +269,119 @@ lib/uri/https.rb
lib/uri/ldap.rb
lib/uri/mailto.rb
lib/weakref.rb
+lib/rexml/dtd/attlistdecl.rb
+lib/rexml/dtd/dtd.rb
+lib/rexml/dtd/elementdecl.rb
+lib/rexml/dtd/entitydecl.rb
+lib/rexml/dtd/notationdecl.rb
+lib/rexml/attlistdecl.rb
+lib/rexml/attribute.rb
+lib/rexml/cdata.rb
+lib/rexml/child.rb
+lib/rexml/comment.rb
+lib/rexml/doctype.rb
+lib/rexml/document.rb
+lib/rexml/element.rb
+lib/rexml/encoding.rb
+lib/rexml/entity.rb
+lib/rexml/functions.rb
+lib/rexml/instruction.rb
+lib/rexml/namespace.rb
+lib/rexml/node.rb
+lib/rexml/output.rb
+lib/rexml/parent.rb
+lib/rexml/parseexception.rb
+lib/rexml/quickpath.rb
+lib/rexml/rexml.rb
+lib/rexml/sax2listener.rb
+lib/rexml/source.rb
+lib/rexml/streamlistener.rb
+lib/rexml/text.rb
+lib/rexml/xmldecl.rb
+lib/rexml/xmltokens.rb
+lib/rexml/xpath.rb
+lib/rexml/xpath_parser.rb
+lib/rexml/encodings/EUC-JP.rb
+lib/rexml/encodings/EUC-JP_decl.rb
+lib/rexml/encodings/ISO-8859-1.rb
+lib/rexml/encodings/ISO-8859-1_decl.rb
+lib/rexml/encodings/SHIFT-JIS.rb
+lib/rexml/encodings/SHIFT_JIS.rb
+lib/rexml/encodings/Shift-JIS_decl.rb
+lib/rexml/encodings/UNILE.rb
+lib/rexml/encodings/UNILE_decl.rb
+lib/rexml/encodings/US-ASCII.rb
+lib/rexml/encodings/US-ASCII_decl.rb
+lib/rexml/encodings/UTF-16.rb
+lib/rexml/encodings/UTF-16_decl.rb
+lib/rexml/light/node.rb
+lib/rexml/parsers/baseparser.rb
+lib/rexml/parsers/lightparser.rb
+lib/rexml/parsers/pullparser.rb
+lib/rexml/parsers/sax2parser.rb
+lib/rexml/parsers/streamparser.rb
+lib/rexml/parsers/ultralightparser.rb
+lib/rexml/parsers/xpathparser.rb
+lib/webrick.rb
+lib/webrick/httpauth/authenticator.rb
+lib/webrick/httpauth/basicauth.rb
+lib/webrick/httpauth/digestauth.rb
+lib/webrick/httpauth/htdigest.rb
+lib/webrick/httpauth/htgroup.rb
+lib/webrick/httpauth/htpasswd.rb
+lib/webrick/httpauth/userdb.rb
+lib/webrick/httpservlet/abstract.rb
+lib/webrick/httpservlet/cgi_runner.rb
+lib/webrick/httpservlet/erbhandler.rb
+lib/webrick/httpservlet/filehandler.rb
+lib/webrick/httpservlet/prochandler.rb
+lib/webrick/httpservlet/cgihandler.rb
+lib/webrick/accesslog.rb
+lib/webrick/cookie.rb
+lib/webrick/htmlutils.rb
+lib/webrick/httpauth.rb
+lib/webrick/httpproxy.rb
+lib/webrick/httprequest.rb
+lib/webrick/httpresponse.rb
+lib/webrick/https.rb
+lib/webrick/httpserver.rb
+lib/webrick/httpservlet.rb
+lib/webrick/httpstatus.rb
+lib/webrick/httputils.rb
+lib/webrick/httpversion.rb
+lib/webrick/log.rb
+lib/webrick/server.rb
+lib/webrick/utils.rb
+lib/webrick/version.rb
+lib/webrick/compat.rb
+lib/webrick/config.rb
+lib/xmlrpc/base64.rb
+lib/xmlrpc/client.rb
+lib/xmlrpc/config.rb
+lib/xmlrpc/create.rb
+lib/xmlrpc/datetime.rb
+lib/xmlrpc/httpserver.rb
+lib/xmlrpc/marshal.rb
+lib/xmlrpc/parser.rb
+lib/xmlrpc/server.rb
+lib/xmlrpc/utils.rb
+lib/yaml.rb
+lib/yaml/baseemitter.rb
+lib/yaml/basenode.rb
+lib/yaml/constants.rb
+lib/yaml/dbm.rb
+lib/yaml/emitter.rb
+lib/yaml/encoding.rb
+lib/yaml/error.rb
+lib/yaml/loader.rb
+lib/yaml/rubytypes.rb
+lib/yaml/store.rb
+lib/yaml/stream.rb
+lib/yaml/stringio.rb
+lib/yaml/syck.rb
+lib/yaml/types.rb
+lib/yaml/yamlnode.rb
+lib/yaml/ypath.rb
misc/README
misc/inf-ruby.el
misc/ruby-mode.el
@@ -242,6 +391,7 @@ missing/acosh.c
missing/alloca.c
missing/crypt.c
missing/dup2.c
+missing/erf.c
missing/file.h
missing/fileblocks.c
missing/finite.c
@@ -259,6 +409,7 @@ missing/strerror.c
missing/strftime.c
missing/strncasecmp.c
missing/strstr.c
+missing/strtod.c
missing/strtol.c
missing/strtoul.c
missing/vsnprintf.c
@@ -304,7 +455,6 @@ sample/rcs.awk
sample/rcs.dat
sample/rcs.rb
sample/regx.rb
-sample/rename.rb
sample/sieve.rb
sample/svr.rb
sample/test.rb
@@ -316,8 +466,6 @@ vms/config.h_in
vms/vms.h
win32/Makefile.sub
win32/README.win32
-win32/config.h.in
-win32/config.status.in
win32/configure.bat
win32/dir.h
win32/mkexports.rb
@@ -330,28 +478,29 @@ x68/_dtos18.c
x68/_round.c
x68/fconvert.c
x68/select.c
+wince/Makefile.sub
wince/README.wince
+wince/assert.c
wince/assert.h
-wince/config
wince/configure.bat
wince/direct.c
wince/direct.h
-wince/dll.mak
wince/errno.c
wince/errno.h
-wince/exe.mak
wince/fcntl.h
-wince/io.c
wince/io.h
-wince/mswince-ruby17.def
-wince/process.c
+wince/io_wce.c
+wince/mkexports.rb
wince/process.h
-wince/signal.c
+wince/process_wce.c
+wince/resource.rb
+wince/setup.mak
wince/signal.h
+wince/signal_wce.c
wince/stddef.h
wince/stdio.c
wince/stdlib.c
-wince/string.c
+wince/string_wce.c
wince/sys/stat.c
wince/sys/stat.h
wince/sys/timeb.c
@@ -359,8 +508,8 @@ wince/sys/timeb.h
wince/sys/types.h
wince/sys/utime.c
wince/sys/utime.h
-wince/time.c
wince/time.h
+wince/time_wce.c
wince/varargs.h
wince/wince.c
wince/wince.h
diff --git a/Makefile.in b/Makefile.in
index d36f649f95..8414aded98 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -100,8 +100,15 @@ OBJS = array.@OBJEXT@ \
version.@OBJEXT@ \
$(MISSING)
+MANTYPE = @MANTYPE@
+
+SCRIPT_ARGS = --dest-dir="$(DESTDIR)" \
+ --make="$(MAKE)" \
+ --mflags="$(MFLAGS)" \
+ --make-flags="$(MAKEFLAGS)"
+
all: @MAKEFILES@ miniruby$(EXEEXT) rbconfig.rb $(LIBRUBY)
- @$(MINIRUBY) $(srcdir)/ext/extmk.rb --extstatic="@EXTSTATIC@" --make="$(MAKE)" --make-flags="$(MFLAGS)$(MAKEFLAGS)"
+ @$(MINIRUBY) $(srcdir)/ext/extmk.rb --extstatic="@EXTSTATIC@" $(SCRIPT_ARGS)
miniruby$(EXEEXT): config.status $(LIBRUBY_A) $(MAINOBJ) dmyext.@OBJEXT@
@rm -f $@
@@ -126,11 +133,15 @@ ruby.imp: $(LIBRUBY_A)
# $(MINIRUBY) $< $@
install: rbconfig.rb
- $(MINIRUBY) $(srcdir)/instruby.rb --make="$(MAKE)" --make-flags="$(MFLAGS)$(MAKEFLAGS)" $(DESTDIR)
- $(MINIRUBY) $(srcdir)/ext/extmk.rb --make="$(MAKE)" --make-flags="$(MFLAGS)$(MAKEFLAGS) DESTDIR=$(DESTDIR)" install
+ $(MINIRUBY) $(srcdir)/instruby.rb $(SCRIPT_ARGS) --mantype="$(MANTYPE)"
+ $(MINIRUBY) $(srcdir)/ext/extmk.rb $(SCRIPT_ARGS) install
+
+what-where no-install: rbconfig.rb
+ $(MINIRUBY) $(srcdir)/instruby.rb -n $(SCRIPT_ARGS) --mantype="$(MANTYPE)"
+ $(MINIRUBY) $(srcdir)/ext/extmk.rb -n $(SCRIPT_ARGS) install
clean-ext:
- @-$(MINIRUBY) $(srcdir)/ext/extmk.rb --make="$(MAKE)" --make-flags="$(MFLAGS)$(MAKEFLAGS)" clean 2> /dev/null || true
+ @-$(MINIRUBY) $(srcdir)/ext/extmk.rb $(SCRIPT_ARGS) clean 2> /dev/null || true
clean-local:
@rm -f $(OBJS) $(MAINOBJ) $(LIBRUBY_A) $(LIBRUBY_SO) $(LIBRUBY_ALIASES)
@@ -140,7 +151,7 @@ clean-local:
clean: clean-ext clean-local
distclean-ext:
- @-$(MINIRUBY) $(srcdir)/ext/extmk.rb --make="$(MAKE)" --make-flags="$(MFLAGS)$(MAKEFLAGS)" distclean 2> /dev/null || true
+ @-$(MINIRUBY) $(srcdir)/ext/extmk.rb $(SCRIPT_ARGS) distclean 2> /dev/null || true
distclean-local: clean-local
@rm -f @MAKEFILES@ config.h rbconfig.rb
@@ -167,7 +178,13 @@ fake.rb: miniruby$(EXEEXT) Makefile
remove_const :RUBY_VERSION; \
RUBY_PLATFORM = "@arch@"; \
RUBY_VERSION = "@MAJOR@.@MINOR@.@TEENY@"; \
- end \
+ end; \
+ if RUBY_PLATFORM =~ /mswin|bccwin|mingw/; \
+ class File; \
+ remove_const :ALT_SEPARATOR; \
+ ALT_SEPARATOR = "\\"; \
+ end; \
+ end; \
' > $@
Makefile: $(srcdir)/Makefile.in
@@ -289,15 +306,15 @@ array.@OBJEXT@: array.c ruby.h config.h defines.h intern.h missing.h \
util.h st.h
bignum.@OBJEXT@: bignum.c ruby.h config.h defines.h intern.h missing.h
class.@OBJEXT@: class.c ruby.h config.h defines.h intern.h missing.h \
- rubysig.h node.h st.h
+ rubysig.h node.h st.h version.h
compar.@OBJEXT@: compar.c ruby.h config.h defines.h intern.h missing.h
dir.@OBJEXT@: dir.c ruby.h config.h defines.h intern.h missing.h util.h
-dln.@OBJEXT@: dln.c config.h defines.h dln.h
+dln.@OBJEXT@: dln.c ruby.h config.h defines.h intern.h missing.h dln.h
dmyext.@OBJEXT@: dmyext.c
enum.@OBJEXT@: enum.c ruby.h config.h defines.h intern.h missing.h node.h \
util.h
error.@OBJEXT@: error.c ruby.h config.h defines.h intern.h missing.h \
- env.h version.h
+ env.h version.h st.h
eval.@OBJEXT@: eval.c ruby.h config.h defines.h intern.h missing.h node.h \
env.h util.h rubysig.h st.h dln.h
file.@OBJEXT@: file.c ruby.h config.h defines.h intern.h missing.h \
@@ -305,17 +322,17 @@ file.@OBJEXT@: file.c ruby.h config.h defines.h intern.h missing.h \
gc.@OBJEXT@: gc.c ruby.h config.h defines.h intern.h missing.h rubysig.h \
st.h node.h env.h re.h regex.h
hash.@OBJEXT@: hash.c ruby.h config.h defines.h intern.h missing.h st.h \
- util.h rubysig.h
+ util.h rubysig.h version.h
inits.@OBJEXT@: inits.c ruby.h config.h defines.h intern.h missing.h
io.@OBJEXT@: io.c ruby.h config.h defines.h intern.h missing.h rubyio.h \
rubysig.h env.h util.h
main.@OBJEXT@: main.c ruby.h config.h defines.h intern.h missing.h
marshal.@OBJEXT@: marshal.c ruby.h config.h defines.h intern.h missing.h \
- rubyio.h st.h
+ rubyio.h st.h util.h
math.@OBJEXT@: math.c ruby.h config.h defines.h intern.h missing.h
numeric.@OBJEXT@: numeric.c ruby.h config.h defines.h intern.h missing.h
object.@OBJEXT@: object.c ruby.h config.h defines.h intern.h missing.h \
- st.h
+ st.h util.h
pack.@OBJEXT@: pack.c ruby.h config.h defines.h intern.h missing.h
parse.@OBJEXT@: parse.c ruby.h config.h defines.h intern.h missing.h \
env.h node.h st.h regex.h util.h lex.c
@@ -334,7 +351,7 @@ signal.@OBJEXT@: signal.c ruby.h config.h defines.h intern.h missing.h \
sprintf.@OBJEXT@: sprintf.c ruby.h config.h defines.h intern.h missing.h
st.@OBJEXT@: st.c config.h st.h
string.@OBJEXT@: string.c ruby.h config.h defines.h intern.h missing.h \
- re.h regex.h
+ re.h regex.h version.h
struct.@OBJEXT@: struct.c ruby.h config.h defines.h intern.h missing.h
time.@OBJEXT@: time.c ruby.h config.h defines.h intern.h missing.h
util.@OBJEXT@: util.c ruby.h config.h defines.h intern.h missing.h util.h
diff --git a/README.EXT b/README.EXT
index 95c5ddea61..3a37db4e11 100644
--- a/README.EXT
+++ b/README.EXT
@@ -35,6 +35,7 @@ The Ruby interpreter has the following data types:
T_HASH associative array
T_STRUCT (Ruby) structure
T_BIGNUM multi precision integer
+ T_FILE IO
T_TRUE true
T_FALSE false
T_DATA data
@@ -93,7 +94,20 @@ The T_FIXNUM data is a 31bit length fixed integer (63bit length on
some machines), which can be convert to a C integer by using the
FIX2INT() macro. There is also NUM2INT() which converts any Ruby
numbers into C integers. The NUM2INT() macro includes a type check, so
-an exception will be raised if the conversion failed.
+an exception will be raised if the conversion failed. NUM2DBL() can
+be used to retrieve the double float value in same way.
+
+To get char* from a VALUE, version 1.7 recommend to use new macros
+StringValue() and StringValuePtr(). StringValue(var) replaces var's
+value to the result of "var.to_str()". StringValuePtr(var) does same
+replacement and returns char* representation of var. These macros
+will skip the replacement if var is a String. Notice that the macros
+requires to take only lvalue as their argument, to change the value
+of var in the replacement.
+
+In version 1.6 or earlier, STR2CSTR() was used to do same thing
+but now it is obsoleted in version 1.7 because of STR2CSTR() has
+a risk of dangling pointer problem in to_str() impliclit conversion.
Other data types have corresponding C structures, e.g. struct RArray
for T_ARRAY etc. The VALUE of the type which has corresponding structure
@@ -408,8 +422,9 @@ DATA), use Data_Wrap_Struct().
Data_Wrap_Struct() returns a created DATA object. The klass argument
is the class for the DATA object. The mark argument is the function
to mark Ruby objects pointed by this data. The free argument is the
-function to free the pointer allocation. The functions mark and
-free will be called from garbage collector.
+function to free the pointer allocation. If this is -1, the pointer
+will be just freed. The functions mark and free will be called from
+garbage collector.
You can allocate and wrap the structure in one step.
@@ -488,6 +503,8 @@ Init_dbm()
rb_define_method(cDBM, "[]", fdbm_fetch, 1);
:
+ /* ID for a instance variable to store DBM data */
+ id_dbm = rb_intern("dbm");
}
--
@@ -756,6 +773,25 @@ sval, and returns the DATA encapsulating the pointer to memory region.
This macro retrieves the pointer value from DATA, and assigns it to
the variable sval.
+** Checking data types
+
+TYPE(value)
+FIXNUM_P(value)
+NIL_P(value)
+void Check_Type(VALUE value, int type)
+void Check_SafeStr(VALUE value)
+
+** Data type conversion
+
+FIX2INT(value)
+INT2FIX(i)
+NUM2INT(value)
+INT2NUM(i)
+NUM2DBL(value)
+rb_float_new(f)
+STR2CSTR(value)
+rb_str_new2(s)
+
** defining class/module
VALUE rb_define_class(const char *name, VALUE super)
@@ -944,6 +980,10 @@ Prints a warning message according to a printf-like format.
Prints a warning message according to a printf-like format, if
$VERBOSE is true.
+void rb_raise(rb_eRuntimeError, const char *fmt, ...)
+
+Raises RuntimeError. The fmt is a format string just like printf().
+
void rb_raise(VALUE exception, const char *fmt, ...)
Raises a class exception. The fmt is a format string just like printf().
diff --git a/README.EXT.ja b/README.EXT.ja
index 0ef67497ce..f49083e60e 100644
--- a/README.EXT.ja
+++ b/README.EXT.ja
@@ -104,10 +104,19 @@ FIXNUM¤ÈNIL¤Ë´Ø¤·¤Æ¤Ï¤è¤ê¹â®¤ÊȽÊÌ¥Þ¥¯¥í¤¬ÍѰդµ¤ì¤Æ¤¤¤Þ¤¹¡¥
¤¤¤Þ¤¹¡¥¤½¤ì¤«¤é¡¤FIXNUM¤Ë¸Â¤é¤ºRuby¤Î¥Ç¡¼¥¿¤òÀ°¿ô¤ËÊÑ´¹¤¹¤ë
¡ÖNUM2INT()¡×¤È¤¤¤¦¥Þ¥¯¥í¤¬¤¢¤ê¤Þ¤¹¡¥¤³¤Î¥Þ¥¯¥í¤Ï¥Ç¡¼¥¿¥¿¥¤
¥×¤Î¥Á¥§¥Ã¥¯Ìµ¤·¤Ç»È¤¨¤Þ¤¹(À°¿ô¤ËÊÑ´¹¤Ç¤­¤Ê¤¤¾ì¹ç¤Ë¤ÏÎã³°¤¬
-ȯÀ¸¤¹¤ë)¡¥
-
-ƱÍͤ˥Á¥§¥Ã¥¯Ìµ¤·¤Ç»È¤¨¤ëÊÑ´¹¥Þ¥¯¥í¤Ïdouble¤ò¼è¤ê½Ð¤¹
-¡ÖNUM2DBL()¡×¤Èchar*¤ò¼è¤ê½Ð¤¹¡ÖSTR2CSTR()¡×¤¬¤¢¤ê¤Þ¤¹¡¥
+ȯÀ¸¤¹¤ë)¡¥Æ±Íͤ˥Á¥§¥Ã¥¯Ìµ¤·¤Ç»È¤¨¤ëÊÑ´¹¥Þ¥¯¥í¤Ïdouble¤ò
+¼è¤ê½Ð¤¹¡ÖNUM2DBL()¡×¤¬¤¢¤ê¤Þ¤¹¡£
+
+char* ¤ò¼è¤ê½Ð¤¹¾ì¹ç¡¢version 1.6 °ÊÁ°¤Ç¤Ï¡ÖSTR2CSTR()¡×¤È
+¤¤¤¦¥Þ¥¯¥í¤ò»È¤Ã¤Æ¤¤¤Þ¤·¤¿¤¬¡¢¤³¤ì¤Ï to_str() ¤Ë¤è¤ë°ÅÌÛ¤Î
+·¿ÊÑ´¹·ë²Ì¤¬ GC ¤µ¤ì¤ë²ÄǽÀ­¤¬¤¢¤ë¤¿¤á¡¢version 1.7 °Ê¹ß¤Ç¤Ï
+obsolete ¤È¤Ê¤ê¡¢Âå¤ï¤ê¤Ë StringValue() ¤È StringValuePtr()
+¤ò»È¤¦»ö¤ò¿ä¾©¤·¤Æ¤¤¤Þ¤¹¡£StringValue(var) ¤Ï var ¤¬ String
+ ¤Ç¤¢¤ì¤Ð²¿¤â¤»¤º¡¢¤½¤¦¤Ç¤Ê¤±¤ì¤Ð var ¤ò var.to_str() ¤Î·ë²Ì¤Ë
+ÃÖ¤­´¹¤¨¤ë¥Þ¥¯¥í¡¢StringValuePtr(var) ¤ÏƱÍÍ¤Ë var ¤òÃÖ¤­´¹¤¨
+¤Æ¤«¤é var ¤Îʸ»úÎóɽ¸½¤ËÂФ¹¤ë char* ¤òÊÖ¤¹¥Þ¥¯¥í¤Ç¤¹¡£var ¤Î
+ÆâÍÆ¤òľÀÜÃÖ¤­´¹¤¨¤ë½èÍý¤¬Æþ¤ë¤Î¤Ç¡¢var ¤Ï lvalue ¤Ç¤¢¤ëɬÍפ¬
+¤¢¤ê¤Þ¤¹¡£
¤½¤ì°Ê³°¤Î¥Ç¡¼¥¿¥¿¥¤¥×¤ÏÂбþ¤¹¤ëC¤Î¹½Â¤ÂΤ¬¤¢¤ê¤Þ¤¹¡¥Âбþ¤¹
¤ë¹½Â¤ÂΤΤ¢¤ëVALUE¤Ï¤½¤Î¤Þ¤Þ¥­¥ã¥¹¥È(·¿ÊÑ´¹)¤¹¤ì¤Ð¹½Â¤ÂΤÎ
@@ -493,7 +502,8 @@ C¤Î¹½Â¤ÂΤؤΥݥ¤¥ó¥¿¤Ç¤¹¡¥mark¤Ï¤³¤Î¹½Â¤ÂΤ¬Ruby¤Î¥ª¥Ö¥¸¥§
# ¤½¤Î¤è¤¦¤Ê»²¾È¤Ï´«¤á¤é¤ì¤Þ¤»¤ó¡¥
free¤Ï¤³¤Î¹½Â¤ÂΤ¬¤â¤¦ÉÔÍפˤʤä¿»þ¤Ë¸Æ¤Ð¤ì¤ë´Ø¿ô¤Ç¤¹¡¥¤³¤Î
-´Ø¿ô¤¬¥¬¡¼¥Ù¡¼¥¸¥³¥ì¥¯¥¿¤«¤é¸Æ¤Ð¤ì¤Þ¤¹¡¥
+´Ø¿ô¤¬¥¬¡¼¥Ù¡¼¥¸¥³¥ì¥¯¥¿¤«¤é¸Æ¤Ð¤ì¤Þ¤¹¡¥¤³¤ì¤¬-1¤Î¾ì¹ç¤Ï¡¤Ã±
+½ã¤Ë³«Êü¤µ¤ì¤Þ¤¹¡¥
C¤Î¹½Â¤ÂΤγäÅö¤ÈData¥ª¥Ö¥¸¥§¥¯¥È¤ÎÀ¸À®¤òƱ»þ¤Ë¹Ô¤¦¥Þ¥¯¥í¤È
¤·¤Æ°Ê²¼¤Î¤â¤Î¤¬Ä󶡤µ¤ì¤Æ¤¤¤Þ¤¹¡¥
diff --git a/ToDo b/ToDo
index fa7f32daed..b55e399edf 100644
--- a/ToDo
+++ b/ToDo
@@ -1,6 +1,7 @@
Language Spec.
- Class#allocate - basicNew
+- class Foo::Bar<Baz .. end, module Boo::Bar .. end
* operator !! for rescue. ???
* objectify characters
* ../... outside condition invokes operator method too.
@@ -11,7 +12,6 @@ Language Spec.
* multiple return values, yield values. maybe incompatible ???
* cascading method invocation ???
* def Class#method .. end ??
-* class Foo::Bar<Baz .. end, module Boo::Bar .. end
* def Foo::Bar::baz() .. end ??
* I18N (or M17N) script/string/regexp
* Fixnum 0 as false ????
@@ -81,6 +81,8 @@ Standard Libraries
- hash etc. should handle self referenceing array/hash
- Array#select(n1,n2...) works like Array#indexes(n1,n2...)
- use Mersenne Twister RNG for random.
+- deprecate Array#indexes, and Array#indices.
+- remove dependency on MAXPATHLEN.
* String#scanf(?)
* Object#fmt(?)
* Time::strptime
@@ -101,11 +103,10 @@ Standard Libraries
* marshal should not depend on sprintf (works bad with locale).
* ternary arg pow: a.pow(b,c) == a**b%c
* new caller(), e.g. call_stack; needs better name.
-* remove dependency on MAXPATHLEN.
* pointer share mechanism similar to one in String for Array.
-* deprecate Array#indexes, and Array#indices.
* require "1.6" etc. by /usr/lib/ruby/1.6/1.6.rb ;-)
* save both "feature names" and "normalized path" in $"
+* implement Mutex_m (or MutexMixin) using Mutex.
Extension Libraries
@@ -116,7 +117,6 @@ Extension Libraries
Ruby Libraries
-- add uri.rb
* urllib.rb, nttplib.rb, etc.
* format like perl's
@@ -124,8 +124,3 @@ Tools
* freeze or undump to bundle everything
* bundle using zlib
-
-Misc
-
-- publish Ruby books
-- publish Ruby books in English
diff --git a/array.c b/array.c
index e6c605db14..887ba4db3e 100644
--- a/array.c
+++ b/array.c
@@ -6,7 +6,7 @@
$Date$
created at: Fri Aug 6 09:46:12 JST 1993
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
Copyright (C) 2000 Information-technology Promotion Agency, Japan
@@ -44,13 +44,13 @@ memfill(mem, size, val)
#define ARY_TMPLOCK FL_USER1
-static void
+static inline void
rb_ary_modify_check(ary)
VALUE ary;
{
if (OBJ_FROZEN(ary)) rb_error_frozen("array");
if (FL_TEST(ary, ARY_TMPLOCK))
- rb_raise(rb_eTypeError, "can't modify array during sort");
+ rb_raise(rb_eTypeError, "can't modify array during iteration");
if (!OBJ_TAINTED(ary) && rb_safe_level() >= 4)
rb_raise(rb_eSecurityError, "Insecure: can't modify array");
}
@@ -86,8 +86,9 @@ rb_ary_frozen_p(ary)
return Qfalse;
}
+static VALUE ary_alloc _((VALUE));
static VALUE
-rb_ary_s_alloc(klass)
+ary_alloc(klass)
VALUE klass;
{
NEWOBJ(ary, struct RArray);
@@ -105,7 +106,7 @@ ary_new(klass, len)
VALUE klass;
long len;
{
- VALUE ary = rb_obj_alloc(klass);
+ VALUE ary = ary_alloc(klass);
if (len < 0) {
rb_raise(rb_eArgError, "negative array size (or size too big)");
@@ -204,6 +205,13 @@ to_ary(ary)
return rb_convert_type(ary, T_ARRAY, "Array", "to_ary");
}
+VALUE
+rb_check_array_type(ary)
+ VALUE ary;
+{
+ return rb_check_convert_type(ary, T_ARRAY, "Array", "to_ary");
+}
+
static VALUE rb_ary_replace _((VALUE, VALUE));
static VALUE
@@ -225,7 +233,7 @@ rb_ary_initialize(argc, argv, ary)
}
if (argc == 1 && !FIXNUM_P(size)) {
- val = rb_check_convert_type(size, T_ARRAY, "Array", "to_ary");
+ val = rb_check_array_type(size);
if (!NIL_P(val)) {
rb_ary_replace(ary, val);
return ary;
@@ -268,7 +276,7 @@ rb_ary_s_create(argc, argv, klass)
VALUE *argv;
VALUE klass;
{
- VALUE ary = rb_obj_alloc(klass);
+ VALUE ary = ary_alloc(klass);
if (argc < 0) {
rb_raise(rb_eArgError, "negative number of arguments");
@@ -336,9 +344,6 @@ rb_ary_push_m(argc, argv, ary)
VALUE *argv;
VALUE ary;
{
- if (argc <= 0) {
- rb_raise(rb_eArgError, "wrong number arguments (at least 1)");
- }
while (argc--) {
rb_ary_push(ary, *argv++);
}
@@ -422,10 +427,11 @@ rb_ary_unshift_m(argc, argv, ary)
VALUE ary;
{
long len = RARRAY(ary)->len;
-
- if (argc <= 0) {
- rb_raise(rb_eArgError, "wrong number of arguments (at least 1)");
+
+ if (argc < 0) {
+ rb_raise(rb_eArgError, "negative number of arguments");
}
+ if (argc == 0) return ary;
/* make rooms by setting the last item */
rb_ary_store(ary, len + argc - 1, Qnil);
@@ -473,7 +479,7 @@ rb_ary_subseq(ary, beg, len)
if (len == 0) return ary_new(klass, 0);
ary_make_shared(ary);
- ary2 = rb_obj_alloc(klass);
+ ary2 = ary_alloc(klass);
RARRAY(ary2)->ptr = RARRAY(ary)->ptr + beg;
RARRAY(ary2)->len = len;
RARRAY(ary2)->aux.shared = RARRAY(ary)->aux.shared;
@@ -492,6 +498,9 @@ rb_ary_aref(argc, argv, ary)
long beg, len;
if (argc == 2) {
+ if (SYMBOL_P(argv[0])) {
+ rb_raise(rb_eTypeError, "Symbol as array index");
+ }
beg = NUM2LONG(argv[0]);
len = NUM2LONG(argv[1]);
if (beg < 0) {
@@ -507,6 +516,9 @@ rb_ary_aref(argc, argv, ary)
if (FIXNUM_P(arg)) {
return rb_ary_entry(ary, FIX2LONG(arg));
}
+ if (SYMBOL_P(arg)) {
+ rb_raise(rb_eTypeError, "Symbol as array index");
+ }
/* check if idx is Range */
switch (rb_range_beg_len(arg, &beg, &len, RARRAY(ary)->len, 0)) {
case Qfalse:
@@ -527,19 +539,53 @@ rb_ary_at(ary, pos)
}
static VALUE
-rb_ary_first(ary)
+rb_ary_first(argc, argv, ary)
+ int argc;
+ VALUE *argv;
VALUE ary;
{
- if (RARRAY(ary)->len == 0) return Qnil;
- return RARRAY(ary)->ptr[0];
+ if (argc == 0) {
+ if (RARRAY(ary)->len == 0) return Qnil;
+ return RARRAY(ary)->ptr[0];
+ }
+ else {
+ VALUE nv, result;
+ long n, i;
+
+ rb_scan_args(argc, argv, "01", &nv);
+ n = NUM2LONG(nv);
+ if (n > RARRAY(ary)->len) n = RARRAY(ary)->len;
+ result = rb_ary_new2(n);
+ for (i=0; i<n; i++) {
+ rb_ary_push(result, RARRAY(ary)->ptr[i]);
+ }
+ return result;
+ }
}
static VALUE
-rb_ary_last(ary)
+rb_ary_last(argc, argv, ary)
+ int argc;
+ VALUE *argv;
VALUE ary;
{
- if (RARRAY(ary)->len == 0) return Qnil;
- return RARRAY(ary)->ptr[RARRAY(ary)->len-1];
+ if (argc == 0) {
+ if (RARRAY(ary)->len == 0) return Qnil;
+ return RARRAY(ary)->ptr[RARRAY(ary)->len-1];
+ }
+ else {
+ VALUE nv, result;
+ long n, i;
+
+ rb_scan_args(argc, argv, "01", &nv);
+ n = NUM2LONG(nv);
+ if (n > RARRAY(ary)->len) n = RARRAY(ary)->len;
+ result = rb_ary_new2(n);
+ for (i=RARRAY(ary)->len-n; n--; i++) {
+ rb_ary_push(result, RARRAY(ary)->ptr[i]);
+ }
+ return result;
+ }
}
static VALUE
@@ -609,7 +655,7 @@ rb_ary_indexes(argc, argv, ary)
VALUE new_ary;
long i;
- rb_warn("Array#%s is deprecated; use Array#select",
+ rb_warn("Array#%s is deprecated; use Array#values_at",
rb_id2name(rb_frame_last_func()));
new_ary = rb_ary_new2(argc);
for (i=0; i<argc; i++) {
@@ -623,7 +669,6 @@ VALUE
rb_ary_to_ary(obj)
VALUE obj;
{
- if (NIL_P(obj)) return rb_ary_new2(0);
if (TYPE(obj) == T_ARRAY) {
return obj;
}
@@ -641,9 +686,6 @@ rb_ary_update(ary, beg, len, rpl)
{
long rlen;
- rpl = rb_ary_to_ary(rpl);
- rlen = RARRAY(rpl)->len;
-
if (len < 0) rb_raise(rb_eIndexError, "negative length (%ld)", len);
if (beg < 0) {
beg += RARRAY(ary)->len;
@@ -657,6 +699,14 @@ rb_ary_update(ary, beg, len, rpl)
}
rb_ary_modify(ary);
+ if (NIL_P(rpl)) {
+ rlen = 0;
+ }
+ else {
+ rpl = rb_ary_to_ary(rpl);
+ rlen = RARRAY(rpl)->len;
+ }
+
if (beg >= RARRAY(ary)->len) {
len = beg + rlen;
if (len >= RARRAY(ary)->aux.capa) {
@@ -664,7 +714,9 @@ rb_ary_update(ary, beg, len, rpl)
RARRAY(ary)->aux.capa = len;
}
rb_mem_clear(RARRAY(ary)->ptr + RARRAY(ary)->len, beg - RARRAY(ary)->len);
- MEMCPY(RARRAY(ary)->ptr + beg, RARRAY(rpl)->ptr, VALUE, rlen);
+ if (rlen > 0) {
+ MEMCPY(RARRAY(ary)->ptr + beg, RARRAY(rpl)->ptr, VALUE, rlen);
+ }
RARRAY(ary)->len = len;
}
else {
@@ -685,7 +737,9 @@ rb_ary_update(ary, beg, len, rpl)
VALUE, RARRAY(ary)->len - (beg + len));
RARRAY(ary)->len = alen;
}
- MEMMOVE(RARRAY(ary)->ptr + beg, RARRAY(rpl)->ptr, VALUE, rlen);
+ if (rlen > 0) {
+ MEMMOVE(RARRAY(ary)->ptr + beg, RARRAY(rpl)->ptr, VALUE, rlen);
+ }
}
}
@@ -698,6 +752,12 @@ rb_ary_aset(argc, argv, ary)
long offset, beg, len;
if (argc == 3) {
+ if (SYMBOL_P(argv[0])) {
+ rb_raise(rb_eTypeError, "Symbol as array index");
+ }
+ if (SYMBOL_P(argv[1])) {
+ rb_raise(rb_eTypeError, "Symbol as subarray length");
+ }
rb_ary_update(ary, NUM2LONG(argv[0]), NUM2LONG(argv[1]), argv[2]);
return argv[2];
}
@@ -708,6 +768,9 @@ rb_ary_aset(argc, argv, ary)
offset = FIX2LONG(argv[0]);
goto fixnum;
}
+ if (SYMBOL_P(argv[0])) {
+ rb_raise(rb_eTypeError, "Symbol as array index");
+ }
if (rb_range_beg_len(argv[0], &beg, &len, RARRAY(ary)->len, 1)) {
/* check if idx is Range */
rb_ary_update(ary, beg, len, argv[1]);
@@ -728,8 +791,8 @@ rb_ary_insert(argc, argv, ary)
{
long pos;
- if (argc < 2) {
- rb_raise(rb_eArgError, "wrong number of arguments (at least 2)");
+ if (argc < 1) {
+ rb_raise(rb_eArgError, "wrong number of arguments (at least 1)");
}
pos = NUM2LONG(argv[0]);
if (pos == -1) {
@@ -739,6 +802,7 @@ rb_ary_insert(argc, argv, ary)
pos++;
}
+ if (argc == 1) return ary;
rb_ary_update(ary, pos, 0, rb_ary_new4(argc - 1, argv + 1));
return ary;
}
@@ -775,6 +839,9 @@ rb_ary_reverse_each(ary)
while (len--) {
rb_yield(RARRAY(ary)->ptr[len]);
+ if (RARRAY(ary)->len < len) {
+ len = RARRAY(ary)->len;
+ }
}
return ary;
}
@@ -829,12 +896,8 @@ rb_ary_join(ary, sep)
if (OBJ_TAINTED(ary) || OBJ_TAINTED(sep)) taint = Qtrue;
for (i=0; i<RARRAY(ary)->len; i++) {
- if (TYPE(RARRAY(ary)->ptr[i]) == T_STRING) {
- len += RSTRING(RARRAY(ary)->ptr[i])->len;
- }
- else {
- len += 10;
- }
+ tmp = rb_check_string_type(RARRAY(ary)->ptr[i]);
+ len += NIL_P(tmp) ? 10 : RSTRING(tmp)->len;
}
if (!NIL_P(sep)) {
StringValue(sep);
@@ -909,13 +972,37 @@ inspect_call(arg)
}
static VALUE
+get_inspect_tbl(create)
+ int create;
+{
+ VALUE inspect_tbl = rb_thread_local_aref(rb_thread_current(), inspect_key);
+
+ if (NIL_P(inspect_tbl)) {
+ if (create) {
+ tbl_init:
+ inspect_tbl = rb_ary_new();
+ rb_thread_local_aset(rb_thread_current(), inspect_key, inspect_tbl);
+ }
+ }
+ else if (TYPE(inspect_tbl) != T_ARRAY) {
+ rb_warn("invalid inspect_tbl value");
+ if (create) goto tbl_init;
+ rb_thread_local_aset(rb_thread_current(), inspect_key, Qnil);
+ return Qnil;
+ }
+ return inspect_tbl;
+}
+
+static VALUE
inspect_ensure(obj)
VALUE obj;
{
VALUE inspect_tbl;
- inspect_tbl = rb_thread_local_aref(rb_thread_current(), inspect_key);
- rb_ary_pop(inspect_tbl);
+ inspect_tbl = get_inspect_tbl(Qfalse);
+ if (!NIL_P(inspect_tbl)) {
+ rb_ary_pop(inspect_tbl);
+ }
return 0;
}
@@ -928,11 +1015,7 @@ rb_protect_inspect(func, obj, arg)
VALUE inspect_tbl;
VALUE id;
- inspect_tbl = rb_thread_local_aref(rb_thread_current(), inspect_key);
- if (NIL_P(inspect_tbl)) {
- inspect_tbl = rb_ary_new();
- rb_thread_local_aset(rb_thread_current(), inspect_key, inspect_tbl);
- }
+ inspect_tbl = get_inspect_tbl(Qtrue);
id = rb_obj_id(obj);
if (rb_ary_includes(inspect_tbl, id)) {
return (*func)(obj, arg);
@@ -951,7 +1034,7 @@ rb_inspecting_p(obj)
{
VALUE inspect_tbl;
- inspect_tbl = rb_thread_local_aref(rb_thread_current(), inspect_key);
+ inspect_tbl = get_inspect_tbl(Qfalse);
if (NIL_P(inspect_tbl)) return Qfalse;
return rb_ary_includes(inspect_tbl, rb_obj_id(obj));
}
@@ -989,6 +1072,18 @@ static VALUE
rb_ary_to_a(ary)
VALUE ary;
{
+ if (rb_obj_class(ary) != rb_cArray) {
+ VALUE dup = rb_ary_new2(RARRAY(ary)->len);
+ rb_ary_replace(dup, ary);
+ return dup;
+ }
+ return ary;
+}
+
+static VALUE
+rb_ary_to_ary_m(ary)
+ VALUE ary;
+{
return ary;
}
@@ -1018,7 +1113,6 @@ static VALUE
rb_ary_reverse_bang(ary)
VALUE ary;
{
- if (RARRAY(ary)->len <= 1) return Qnil;
return rb_ary_reverse(ary);
}
@@ -1033,8 +1127,8 @@ static int
sort_1(a, b)
VALUE *a, *b;
{
- VALUE retval = rb_yield(rb_assoc_new(*a, *b));
- return rb_cmpint(retval);
+ VALUE retval = rb_yield_values(2, *a, *b);
+ return rb_cmpint(retval, *a, *b);
}
static int
@@ -1054,7 +1148,7 @@ sort_2(ap, bp)
}
retval = rb_funcall(a, id_cmp, 1, b);
- return rb_cmpint(retval);
+ return rb_cmpint(retval, a, b);
}
static VALUE
@@ -1099,16 +1193,15 @@ static VALUE
rb_ary_collect(ary)
VALUE ary;
{
- long len, i;
+ long i;
VALUE collect;
if (!rb_block_given_p()) {
return rb_ary_new4(RARRAY(ary)->len, RARRAY(ary)->ptr);
}
- len = RARRAY(ary)->len;
- collect = rb_ary_new2(len);
- for (i=0; i<len; i++) {
+ collect = rb_ary_new2(RARRAY(ary)->len);
+ for (i = 0; i < RARRAY(ary)->len; i++) {
rb_ary_push(collect, rb_yield(RARRAY(ary)->ptr[i]));
}
return collect;
@@ -1127,6 +1220,48 @@ rb_ary_collect_bang(ary)
return ary;
}
+VALUE
+rb_values_at(obj, olen, argc, argv, func)
+ VALUE obj;
+ long olen;
+ int argc;
+ VALUE *argv;
+ VALUE (*func) _((VALUE,long));
+{
+ VALUE result = rb_ary_new2(argc);
+ long beg, len, i, j;
+
+ for (i=0; i<argc; i++) {
+ if (FIXNUM_P(argv[i])) {
+ rb_ary_push(result, (*func)(obj, FIX2LONG(argv[i])));
+ continue;
+ }
+ /* check if idx is Range */
+ switch (rb_range_beg_len(argv[i], &beg, &len, olen, 0)) {
+ case Qfalse:
+ break;
+ case Qnil:
+ continue;
+ default:
+ for (j=0; j<len; j++) {
+ rb_ary_push(result, (*func)(obj, j+beg));
+ }
+ continue;
+ }
+ rb_ary_push(result, (*func)(obj, NUM2LONG(argv[i])));
+ }
+ return result;
+}
+
+static VALUE
+rb_ary_values_at(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ return rb_values_at(ary, RARRAY(ary)->len, argc, argv, rb_ary_entry);
+}
+
static VALUE
rb_ary_select(argc, argv, ary)
int argc;
@@ -1136,21 +1271,17 @@ rb_ary_select(argc, argv, ary)
VALUE result;
long i;
- if (rb_block_given_p()) {
- if (argc > 0) {
- rb_raise(rb_eArgError, "wrong number arguments (%d for 0)", argc);
- }
- result = rb_ary_new2(RARRAY(ary)->len);
- for (i = 0; i < RARRAY(ary)->len; i++) {
- if (RTEST(rb_yield(RARRAY(ary)->ptr[i]))) {
- rb_ary_push(result, RARRAY(ary)->ptr[i]);
- }
- }
+ if (!rb_block_given_p()) {
+ rb_warn("Array#select(index..) is deprecated; use Array#values_at");
+ return rb_ary_values_at(argc, argv, ary);
}
- else {
- result = rb_ary_new2(argc);
- for (i=0; i<argc; i++) {
- rb_ary_push(result, rb_ary_entry(ary, NUM2LONG(argv[i])));
+ if (argc > 0) {
+ rb_raise(rb_eArgError, "wrong number arguments (%d for 0)", argc);
+ }
+ result = rb_ary_new2(RARRAY(ary)->len);
+ for (i = 0; i < RARRAY(ary)->len; i++) {
+ if (RTEST(rb_yield(RARRAY(ary)->ptr[i]))) {
+ rb_ary_push(result, RARRAY(ary)->ptr[i]);
}
}
return result;
@@ -1295,12 +1426,11 @@ rb_ary_zip(argc, argv, ary)
long len;
VALUE result;
- len = RARRAY(ary)->len;
for (i=0; i<argc; i++) {
argv[i] = to_ary(argv[i]);
}
if (rb_block_given_p()) {
- for (i=0; i<len; i++) {
+ for (i=0; i<RARRAY(ary)->len; i++) {
VALUE tmp = rb_ary_new2(argc+1);
rb_ary_push(tmp, rb_ary_entry(ary, i));
@@ -1311,6 +1441,7 @@ rb_ary_zip(argc, argv, ary)
}
return Qnil;
}
+ len = RARRAY(ary)->len;
result = rb_ary_new2(len);
for (i=0; i<len; i++) {
VALUE tmp = rb_ary_new2(argc+1);
@@ -1326,9 +1457,10 @@ rb_ary_zip(argc, argv, ary)
static VALUE
rb_ary_transpose(ary)
+ VALUE ary;
{
long elen = -1, alen, i, j;
- VALUE tmp, result;
+ VALUE tmp, result = 0;
alen = RARRAY(ary)->len;
if (alen == 0) return rb_ary_dup(ary);
@@ -1433,15 +1565,21 @@ rb_ary_fill(argc, argv, ary)
}
RARRAY(ary)->len = end;
}
- p = RARRAY(ary)->ptr + beg;
- pend = p + len;
if (block_p) {
- while (p < pend) {
- *p++ = rb_yield(LONG2NUM(beg++));
+ VALUE v;
+ long i;
+
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ beg++;
+ v = rb_yield(LONG2NUM(beg++));
+ if (i>=RARRAY(ary)->len) break;
+ RARRAY(ary)->ptr[i] = v;
}
}
else {
+ p = RARRAY(ary)->ptr + beg;
+ pend = p + len;
while (p < pend) {
*p++ = item;
}
@@ -1480,17 +1618,22 @@ static VALUE
rb_ary_times(ary, times)
VALUE ary, times;
{
- VALUE ary2;
+ VALUE ary2, tmp;
long i, len;
- if (TYPE(times) == T_STRING) {
- return rb_ary_join(ary, times);
+ tmp = rb_check_string_type(times);
+ if (!NIL_P(tmp)) {
+ return rb_ary_join(ary, tmp);
}
len = NUM2LONG(times);
+ if (len == 0) return rb_ary_new2(0);
if (len < 0) {
rb_raise(rb_eArgError, "negative argument");
}
+ if (LONG_MAX/len < RARRAY(ary)->len) {
+ rb_raise(rb_eArgError, "argument too big");
+ }
len *= RARRAY(ary)->len;
ary2 = ary_new(rb_obj_class(ary), len);
@@ -1549,7 +1692,12 @@ rb_ary_equal(ary1, ary2)
long i;
if (ary1 == ary2) return Qtrue;
- if (TYPE(ary2) != T_ARRAY) return Qfalse;
+ if (TYPE(ary2) != T_ARRAY) {
+ if (!rb_respond_to(ary2, rb_intern("to_ary"))) {
+ return Qfalse;
+ }
+ return rb_equal(ary2, ary1);
+ }
if (RARRAY(ary1)->len != RARRAY(ary2)->len) return Qfalse;
for (i=0; i<RARRAY(ary1)->len; i++) {
if (!rb_equal(RARRAY(ary1)->ptr[i], RARRAY(ary2)->ptr[i]))
@@ -1564,6 +1712,7 @@ rb_ary_eql(ary1, ary2)
{
long i;
+ if (ary1 == ary2) return Qtrue;
if (TYPE(ary2) != T_ARRAY) return Qfalse;
if (RARRAY(ary1)->len != RARRAY(ary2)->len) return Qfalse;
for (i=0; i<RARRAY(ary1)->len; i++) {
@@ -1628,23 +1777,6 @@ rb_ary_cmp(ary1, ary2)
}
static VALUE
-rb_ary_diff(ary1, ary2)
- VALUE ary1, ary2;
-{
- VALUE ary3;
- long i;
-
- ary2 = to_ary(ary2);
- ary3 = rb_ary_new();
- for (i=0; i<RARRAY(ary1)->len; i++) {
- if (rb_ary_includes(ary2, RARRAY(ary1)->ptr[i])) continue;
- if (rb_ary_includes(ary3, RARRAY(ary1)->ptr[i])) continue;
- rb_ary_push(ary3, RARRAY(ary1)->ptr[i]);
- }
- return ary3;
-}
-
-static VALUE
ary_make_hash(ary1, ary2)
VALUE ary1, ary2;
{
@@ -1663,6 +1795,23 @@ ary_make_hash(ary1, ary2)
}
static VALUE
+rb_ary_diff(ary1, ary2)
+ VALUE ary1, ary2;
+{
+ VALUE ary3, hash;
+ long i;
+
+ hash = ary_make_hash(to_ary(ary2), 0);
+ ary3 = rb_ary_new();
+
+ for (i=0; i<RARRAY(ary1)->len; i++) {
+ if (st_lookup(RHASH(hash)->tbl, RARRAY(ary1)->ptr[i], 0)) continue;
+ rb_ary_push(ary3, RARRAY(ary1)->ptr[i]);
+ }
+ return ary3;
+}
+
+static VALUE
rb_ary_and(ary1, ary2)
VALUE ary1, ary2;
{
@@ -1676,7 +1825,7 @@ rb_ary_and(ary1, ary2)
for (i=0; i<RARRAY(ary1)->len; i++) {
VALUE v = RARRAY(ary1)->ptr[i];
- if (st_delete(RHASH(hash)->tbl, &v, 0)) {
+ if (st_delete(RHASH(hash)->tbl, (st_data_t*)&v, 0)) {
rb_ary_push(ary3, RARRAY(ary1)->ptr[i]);
}
}
@@ -1698,13 +1847,13 @@ rb_ary_or(ary1, ary2)
for (i=0; i<RARRAY(ary1)->len; i++) {
v = RARRAY(ary1)->ptr[i];
- if (st_delete(RHASH(hash)->tbl, &v, 0)) {
+ if (st_delete(RHASH(hash)->tbl, (st_data_t*)&v, 0)) {
rb_ary_push(ary3, RARRAY(ary1)->ptr[i]);
}
}
for (i=0; i<RARRAY(ary2)->len; i++) {
v = RARRAY(ary2)->ptr[i];
- if (st_delete(RHASH(hash)->tbl, &v, 0)) {
+ if (st_delete(RHASH(hash)->tbl, (st_data_t*)&v, 0)) {
rb_ary_push(ary3, RARRAY(ary2)->ptr[i]);
}
}
@@ -1729,7 +1878,7 @@ rb_ary_uniq_bang(ary)
end = p + RARRAY(ary)->len;
while (p < end) {
VALUE v = *p;
- if (st_delete(RHASH(hash)->tbl, &v, 0)) {
+ if (st_delete(RHASH(hash)->tbl, (st_data_t*)&v, 0)) {
*q++ = *p;
}
p++;
@@ -1865,26 +2014,27 @@ Init_Array()
rb_cArray = rb_define_class("Array", rb_cObject);
rb_include_module(rb_cArray, rb_mEnumerable);
- rb_define_singleton_method(rb_cArray, "allocate", rb_ary_s_alloc, 0);
+ rb_define_alloc_func(rb_cArray, ary_alloc);
rb_define_singleton_method(rb_cArray, "[]", rb_ary_s_create, -1);
rb_define_method(rb_cArray, "initialize", rb_ary_initialize, -1);
+ rb_define_method(rb_cArray, "initialize_copy", rb_ary_replace, 1);
+
rb_define_method(rb_cArray, "to_s", rb_ary_to_s, 0);
rb_define_method(rb_cArray, "inspect", rb_ary_inspect, 0);
rb_define_method(rb_cArray, "to_a", rb_ary_to_a, 0);
- rb_define_method(rb_cArray, "to_ary", rb_ary_to_a, 0);
+ rb_define_method(rb_cArray, "to_ary", rb_ary_to_ary_m, 0);
rb_define_method(rb_cArray, "frozen?", rb_ary_frozen_p, 0);
rb_define_method(rb_cArray, "==", rb_ary_equal, 1);
rb_define_method(rb_cArray, "eql?", rb_ary_eql, 1);
rb_define_method(rb_cArray, "hash", rb_ary_hash, 0);
- rb_define_method(rb_cArray, "===", rb_ary_equal, 1);
rb_define_method(rb_cArray, "[]", rb_ary_aref, -1);
rb_define_method(rb_cArray, "[]=", rb_ary_aset, -1);
rb_define_method(rb_cArray, "at", rb_ary_at, 1);
rb_define_method(rb_cArray, "fetch", rb_ary_fetch, -1);
- rb_define_method(rb_cArray, "first", rb_ary_first, 0);
- rb_define_method(rb_cArray, "last", rb_ary_last, 0);
+ rb_define_method(rb_cArray, "first", rb_ary_first, -1);
+ rb_define_method(rb_cArray, "last", rb_ary_last, -1);
rb_define_method(rb_cArray, "concat", rb_ary_concat, 1);
rb_define_method(rb_cArray, "<<", rb_ary_push, 1);
rb_define_method(rb_cArray, "push", rb_ary_push_m, -1);
@@ -1902,7 +2052,6 @@ Init_Array()
rb_define_method(rb_cArray, "rindex", rb_ary_rindex, 1);
rb_define_method(rb_cArray, "indexes", rb_ary_indexes, -1);
rb_define_method(rb_cArray, "indices", rb_ary_indexes, -1);
- rb_define_method(rb_cArray, "copy_object", rb_ary_replace, 1);
rb_define_method(rb_cArray, "join", rb_ary_join_m, -1);
rb_define_method(rb_cArray, "reverse", rb_ary_reverse_m, 0);
rb_define_method(rb_cArray, "reverse!", rb_ary_reverse_bang, 0);
@@ -1910,9 +2059,10 @@ Init_Array()
rb_define_method(rb_cArray, "sort!", rb_ary_sort_bang, 0);
rb_define_method(rb_cArray, "collect", rb_ary_collect, 0);
rb_define_method(rb_cArray, "collect!", rb_ary_collect_bang, 0);
- rb_define_method(rb_cArray, "select", rb_ary_select, -1);
rb_define_method(rb_cArray, "map", rb_ary_collect, 0);
rb_define_method(rb_cArray, "map!", rb_ary_collect_bang, 0);
+ rb_define_method(rb_cArray, "select", rb_ary_select, -1);
+ rb_define_method(rb_cArray, "values_at", rb_ary_values_at, -1);
rb_define_method(rb_cArray, "delete", rb_ary_delete, 1);
rb_define_method(rb_cArray, "delete_at", rb_ary_delete_at_m, 1);
rb_define_method(rb_cArray, "delete_if", rb_ary_delete_if, 0);
diff --git a/bcc32/Makefile.sub b/bcc32/Makefile.sub
index 7e787b18ff..c95f884f1b 100644
--- a/bcc32/Makefile.sub
+++ b/bcc32/Makefile.sub
@@ -20,7 +20,7 @@ RUBYW_INSTALL_NAME = $(RUBY_INSTALL_NAME:ruby=rubyw)
RUBYW_INSTALL_NAME = $(RUBY_INSTALL_NAME)w
!endif
!ifndef RUBY_SO_NAME
-RUBY_SO_NAME = $(RT)-$(RUBY_INSTALL_NAME)17
+RUBY_SO_NAME = $(RT)-$(RUBY_INSTALL_NAME)$(MAJOR)$(MINOR)
!endif
!ifndef icondirs
!ifdef ICONDIRS
@@ -90,7 +90,7 @@ DESTDIR = $(prefix)
CFLAGS = -q $(DEBUGFLAGS) $(OPTFLAGS) $(PROCESSOR_FLAG) -w-
!endif
!ifndef CPPFLAGS
-CPPFLAGS = -I. -I$(srcdir) -I$(srcdir)missing -DLIBRUBY_SO=\"$(LIBRUBY_SO)\"
+CPPFLAGS = -I. -I$(srcdir) -I$(srcdir)missing
!endif
!ifndef LDFLAGS
LDFLAGS = -S:$(STACK)
@@ -102,13 +102,13 @@ RFLAGS = $(iconinc)
EXTLIBS =
!endif
LIBS = cw32.lib import32.lib ws2_32.lib $(EXTLIBS)
-MISSING = acosh.obj crypt.obj win32.obj
+MISSING = acosh.obj crypt.obj erf.obj win32.obj
!ifndef STACK
STACK = 0x2000000
!endif
-XCFLAGS =
+XCFLAGS = -DRUBY_EXPORT
ARFLAGS = /a
LD = ilink32 -q -Gn
@@ -182,9 +182,14 @@ OBJS = array.obj \
version.obj \
$(MISSING)
+SCRIPT_ARGS = "--dest-dir=$(DESTDIR)" \
+ "--make=$(MAKE)" \
+ "--mflags=$(MFLAGS)" \
+ "--make-flags=$(MAKEFLAGS)"
+
all: miniruby$(EXEEXT) rbconfig.rb \
$(LIBRUBY) $(MISCLIBS)
- @.\miniruby$(EXEEXT) $(srcdir)ext/extmk.rb --extstatic=$(EXTSTATIC) --make="$(MAKE)" --make-flags="-$(MFLAGS)$(MAKEFLAGS)"
+ .\miniruby$(EXEEXT) $(srcdir)ext/extmk.rb --extstatic=$(EXTSTATIC) $(SCRIPT_ARGS)
ruby: $(PROGRAM)
rubyw: $(WPROGRAM)
@@ -193,7 +198,7 @@ dll: $(LIBRUBY_SO)
config: config.h config.status
-config.h: Makefile $(srcdir)bcc32/Makefile.sub
+config.h:
@echo Creating $(@:.\=)
@type > $@ &&|
\#define HAVE_PROTOTYPES 1
@@ -227,13 +232,14 @@ config.h: Makefile $(srcdir)bcc32/Makefile.sub
/* \#define HAVE_RANDOM 1 */
\#define HAVE_WAITPID 1
\#define HAVE_GETCWD 1
+\#define HAVE_FSYNC 1
/* \#define HAVE_TRUNCATE 1 */
\#define HAVE_CHSIZE 1
\#define HAVE_TIMES 1
/* \#define HAVE_UTIMES 1 */
/* \#define HAVE_FCNTL 1 */
/* \#define HAVE_SETITIMER 1 */
-\#define HAVE_GETGROUPS 1
+/* \#define HAVE_GETGROUPS 1 */
/* \#define HAVE_SIGPROCMASK 1 */
\#define HAVE_GETLOGIN 1
\#define HAVE_TELLDIR 1
@@ -242,18 +248,19 @@ config.h: Makefile $(srcdir)bcc32/Makefile.sub
\#define HAVE_SINH 1
\#define HAVE_TANH 1
+\#define NEED_IO_SEEK_BETWEEN_RW 1
\#define RSHIFT(x,y) ((x)>>y)
\#define FILE_COUNT level
\#define FILE_READPTR curp
\#define DEFAULT_KCODE KCODE_NONE
\#define DLEXT ".so"
\#define DLEXT2 ".dll"
-\#define RUBY_LIB "/lib/ruby/1.7"
+\#define RUBY_LIB "/lib/ruby/$(MAJOR).$(MINOR)"
\#define RUBY_SITE_LIB "/lib/ruby/site_ruby"
-\#define RUBY_SITE_LIB2 "/lib/ruby/site_ruby/1.7"
+\#define RUBY_SITE_LIB2 "/lib/ruby/site_ruby/$(MAJOR).$(MINOR)"
\#define RUBY_PLATFORM "$(ARCH)-$(OS)"
-\#define RUBY_ARCHLIB "/lib/ruby/1.7/$(ARCH)-$(OS)"
-\#define RUBY_SITE_ARCHLIB "/lib/ruby/site_ruby/1.7/$(ARCH)-$(OS)"
+\#define RUBY_ARCHLIB "/lib/ruby/$(MAJOR).$(MINOR)/$(ARCH)-$(OS)"
+\#define RUBY_SITE_ARCHLIB "/lib/ruby/site_ruby/$(MAJOR).$(MINOR)/$(ARCH)-$(OS)"
\#define SIZEOF_INT 4
\#define SIZEOF_SHORT 2
@@ -327,7 +334,7 @@ s,@AR@,$(AR),;t t
s,@ARFLAGS@,$(ARFLAGS) ,;t t
s,@LN_S@,$(LN_S),;t t
s,@SET_MAKE@,$(SET_MAKE),;t t
-s,@LIBOBJS@, acosh.obj crypt.obj win32.obj,;t t
+s,@LIBOBJS@, acosh.obj crypt.obj erf.obj win32.obj,;t t
s,@ALLOCA@,$(ALLOCA),;t t
s,@DEFAULT_KCODE@,$(DEFAULT_KCODE),;t t
s,@EXEEXT@,.exe,;t t
@@ -335,7 +342,7 @@ s,@OBJEXT@,obj,;t t
s,@XLDFLAGS@,$(XLDFLAGS),;t t
s,@DLDFLAGS@,$(DLDFLAGS),;t t
s,@STATIC@,$(STATIC),;t t
-s,@CCDLFLAGS@,-DIMPORT,;t t
+s,@CCDLFLAGS@,,;t t
s,@LDSHARED@,$(LDSHARED),;t t
s,@DLEXT@,so,;t t
s,@DLEXT2@,dll,;t t
@@ -392,9 +399,9 @@ $(PROGRAM): $(MAINOBJ) $(LIBRUBY_SO) $(RUBY_INSTALL_NAME).res
$(WPROGRAM): $(MAINOBJ) $(WINMAINOBJ) $(LIBRUBY_SO) $(RUBYW_INSTALL_NAME).res
$(LD) $(LDFLAGS) $(WLDFLAGS) $(MAINOBJ) $(WINMAINOBJ),$@,nul,$(LIBRUBYARG) $(LIBS),,$(RUBYW_INSTALL_NAME).res
-$(LIBRUBY_A): $(OBJS)
+$(LIBRUBY_A): $(OBJS) dmyext.obj
@-if exist $@ del $@
- $(AR) $(ARFLAGS) "$@" $(OBJS)
+ $(AR) $(ARFLAGS) "$@" $(OBJS) dmyext.obj
# $(LIBRUBY): $(LIBRUBY_SO)
# implib $@ $(LIBRUBY_SO)
@@ -407,8 +414,12 @@ $(RUBYDEF): $(LIBRUBY_A) miniruby$(EXEEXT)
$(MINIRUBY) $(srcdir)bcc32/mkexports.rb -output=$@ $(LIBRUBY_A)
install: rbconfig.rb
- $(MINIRUBY) $(srcdir)instruby.rb --make="$(MAKE)" --make-flags="-$(MFLAGS)$(MAKEFLAGS)" $(DESTDIR)
- $(MINIRUBY) $(srcdir)ext/extmk.rb --make="$(MAKE)" --make-flags="-$(MFLAGS)$(MAKEFLAGS) DESTDIR=$(DESTDIR)" install
+ $(MINIRUBY) $(srcdir)instruby.rb $(SCRIPT_ARGS)
+ $(MINIRUBY) $(srcdir)ext/extmk.rb $(SCRIPT_ARGS) install
+
+what-where no-install: rbconfig.rb
+ $(MINIRUBY) $(srcdir)instruby.rb -n $(SCRIPT_ARGS)
+ $(MINIRUBY) $(srcdir)ext/extmk.rb -n $(SCRIPT_ARGS) install
clean: clean-ext clean-local
@@ -425,7 +436,7 @@ clean-local:
@if exist *.il? del *.il?
clean-ext:
- @-$(MINIRUBY) $(srcdir)ext/extmk.rb --make="$(MAKE)" --make-flags="-$(MFLAGS)$(MAKEFLAGS)" clean
+ @-$(MINIRUBY) $(srcdir)ext/extmk.rb $(SCRIPT_ARGS) clean
distclean: distclean-ext distclean-local
@@ -459,7 +470,7 @@ distclean-local: clean-local
@if exist miniruby$(EXEEXT) del miniruby$(EXEEXT)
distclean-ext:
- @-$(MINIRUBY) $(srcdir)ext/extmk.rb --make="$(MAKE)" --make-flags="-$(MFLAGS)$(MAKEFLAGS)" distclean
+ @-$(MINIRUBY) $(srcdir)ext/extmk.rb $(SCRIPT_ARGS) distclean
realclean: distclean
@if exist parse.c del parse.c
@@ -507,10 +518,9 @@ acosh.obj: acosh.c win32.h
alloca.obj: alloca.c win32.h
crypt.obj: crypt.c win32.h
dup2.obj: dup2.c win32.h
+erf.obj: erf.c win32.h
finite.obj: finite.c win32.h
flock.obj: flock.c win32.h
-isinf.obj: isinf.c win32.h
-isnan.obj: isnan.c win32.h
memcmp.obj: memcmp.c win32.h
memmove.obj: memmove.c win32.h
mkdir.obj: mkdir.c win32.h
@@ -535,42 +545,41 @@ dl_os2.obj: dl_os2.c win32.h
win32.obj: win32.c win32.h
###
-parse.obj: parse.c ruby.h config.h defines.h intern.h env.h node.h st.h regex.h util.h lex.c win32.h
-###
-array.obj: array.c ruby.h config.h defines.h intern.h win32.h
-bignum.obj: bignum.c ruby.h config.h defines.h intern.h win32.h
-class.obj: class.c ruby.h config.h defines.h intern.h node.h st.h win32.h
-compar.obj: compar.c ruby.h config.h defines.h intern.h win32.h
-dir.obj: dir.c ruby.h config.h defines.h intern.h win32.h
-dln.obj: dln.c config.h defines.h dln.h win32.h
-dmyext.obj: dmyext.c win32.h
-enum.obj: enum.c ruby.h config.h defines.h intern.h win32.h
-error.obj: error.c ruby.h config.h defines.h intern.h env.h win32.h
-eval.obj: eval.c ruby.h config.h defines.h intern.h node.h env.h rubysig.h st.h dln.h win32.h
-file.obj: file.c ruby.h config.h defines.h intern.h rubyio.h rubysig.h win32.h
-gc.obj: gc.c ruby.h config.h defines.h intern.h rubysig.h st.h node.h env.h re.h regex.h win32.h
-hash.obj: hash.c ruby.h config.h defines.h intern.h st.h rubysig.h util.h win32.h
-inits.obj: inits.c ruby.h config.h defines.h intern.h win32.h
-io.obj: io.c ruby.h config.h defines.h intern.h rubyio.h rubysig.h env.h win32.h
-main.obj: main.c ruby.h config.h defines.h intern.h win32.h
-marshal.obj: marshal.c ruby.h config.h defines.h intern.h rubyio.h st.h win32.h
-prec.obj: prec.c ruby.h config.h defines.h intern.h win32.h
-math.obj: math.c ruby.h config.h defines.h intern.h win32.h
-numeric.obj: numeric.c ruby.h config.h defines.h intern.h win32.h
-object.obj: object.c ruby.h config.h defines.h intern.h st.h win32.h
-pack.obj: pack.c ruby.h config.h defines.h intern.h win32.h
-process.obj: process.c ruby.h config.h defines.h intern.h rubysig.h st.h win32.h
-random.obj: random.c ruby.h config.h defines.h intern.h win32.h
-range.obj: range.c ruby.h config.h defines.h intern.h win32.h
-re.obj: re.c ruby.h config.h defines.h intern.h re.h regex.h win32.h
-regex.obj: regex.c config.h regex.h util.h win32.h
-ruby.obj: ruby.c ruby.h config.h defines.h intern.h dln.h util.h win32.h
-signal.obj: signal.c ruby.h config.h defines.h intern.h rubysig.h win32.h
-sprintf.obj: sprintf.c ruby.h config.h defines.h intern.h win32.h
-st.obj: st.c config.h st.h win32.h
-string.obj: string.c ruby.h config.h defines.h intern.h re.h regex.h win32.h
-struct.obj: struct.c ruby.h config.h defines.h intern.h win32.h
-time.obj: time.c ruby.h config.h defines.h intern.h win32.h
-util.obj: util.c ruby.h config.h defines.h intern.h util.h win32.h
-variable.obj: variable.c ruby.h config.h defines.h intern.h env.h node.h st.h win32.h
-version.obj: version.c ruby.h config.h defines.h intern.h version.h win32.h
+array.obj: array.c ruby.h config.h defines.h intern.h missing.h util.h st.h win32.h
+bignum.obj: bignum.c ruby.h config.h defines.h intern.h missing.h win32.h
+class.obj: class.c ruby.h config.h defines.h intern.h missing.h rubysig.h node.h st.h version.h win32.h
+compar.obj: compar.c ruby.h config.h defines.h intern.h missing.h win32.h
+dir.obj: dir.c ruby.h config.h defines.h intern.h missing.h util.h win32.h
+dln.obj: dln.c ruby.h config.h defines.h intern.h missing.h dln.h win32.h
+dmyext.obj: dmyext.c
+enum.obj: enum.c ruby.h config.h defines.h intern.h missing.h node.h util.h win32.h
+error.obj: error.c ruby.h config.h defines.h intern.h missing.h env.h version.h st.h win32.h
+eval.obj: eval.c ruby.h config.h defines.h intern.h missing.h node.h env.h util.h rubysig.h st.h dln.h win32.h
+file.obj: file.c ruby.h config.h defines.h intern.h missing.h rubyio.h rubysig.h util.h dln.h win32.h
+gc.obj: gc.c ruby.h config.h defines.h intern.h missing.h rubysig.h st.h node.h env.h re.h regex.h win32.h
+hash.obj: hash.c ruby.h config.h defines.h intern.h missing.h st.h util.h rubysig.h version.h win32.h
+inits.obj: inits.c ruby.h config.h defines.h intern.h missing.h win32.h
+io.obj: io.c ruby.h config.h defines.h intern.h missing.h rubyio.h rubysig.h env.h util.h win32.h
+main.obj: main.c ruby.h config.h defines.h intern.h missing.h win32.h
+marshal.obj: marshal.c ruby.h config.h defines.h intern.h missing.h rubyio.h st.h util.h win32.h
+math.obj: math.c ruby.h config.h defines.h intern.h missing.h win32.h
+numeric.obj: numeric.c ruby.h config.h defines.h intern.h missing.h win32.h
+object.obj: object.c ruby.h config.h defines.h intern.h missing.h st.h util.h win32.h
+pack.obj: pack.c ruby.h config.h defines.h intern.h missing.h win32.h
+parse.obj: parse.c ruby.h config.h defines.h intern.h missing.h env.h node.h st.h regex.h util.h lex.c win32.h
+prec.obj: prec.c ruby.h config.h defines.h intern.h missing.h win32.h
+process.obj: process.c ruby.h config.h defines.h intern.h missing.h rubysig.h st.h win32.h
+random.obj: random.c ruby.h config.h defines.h intern.h missing.h win32.h
+range.obj: range.c ruby.h config.h defines.h intern.h missing.h win32.h
+re.obj: re.c ruby.h config.h defines.h intern.h missing.h re.h regex.h win32.h
+regex.obj: regex.c config.h regex.h win32.h
+ruby.obj: ruby.c ruby.h config.h defines.h intern.h missing.h dln.h node.h util.h win32.h
+signal.obj: signal.c ruby.h config.h defines.h intern.h missing.h rubysig.h win32.h
+sprintf.obj: sprintf.c ruby.h config.h defines.h intern.h missing.h win32.h
+st.obj: st.c config.h st.h
+string.obj: string.c ruby.h config.h defines.h intern.h missing.h re.h regex.h version.h win32.h
+struct.obj: struct.c ruby.h config.h defines.h intern.h missing.h win32.h
+time.obj: time.c ruby.h config.h defines.h intern.h missing.h win32.h
+util.obj: util.c ruby.h config.h defines.h intern.h missing.h util.h win32.h
+variable.obj: variable.c ruby.h config.h defines.h intern.h missing.h env.h node.h st.h util.h win32.h
+version.obj: version.c ruby.h config.h defines.h intern.h missing.h version.h win32.h
diff --git a/bcc32/setup.mak b/bcc32/setup.mak
index 27cf91164a..c81eaf2020 100644
--- a/bcc32/setup.mak
+++ b/bcc32/setup.mak
@@ -34,15 +34,23 @@ alpha-$(OS): -prologue- -alpha- -epilogue-
\#\#\# Makefile for ruby $(OS) \#\#\#
srcdir = $(srcdir:\=/)
|
+ @cpp32 -I$(srcdir) -P- -o$(MAKEFILE) > nul &&|
+\#include "version.h"
+MAJOR = RUBY_VERSION_MAJOR
+MINOR = RUBY_VERSION_MINOR
+TEENY = RUBY_VERSION_TEENY
+|
+ @type $(MAKEFILE).i >> $(MAKEFILE)
+ @del $(MAKEFILE).i
-generic-: nul
!if defined(PROCESSOR_ARCHITECTURE) || defined(PROCESSOR_LEVEL)
@type >> $(MAKEFILE) &&|
!if defined(PROCESSOR_ARCHITECTURE)
- @$(APPEND) PROCESSOR_ARCHITECTURE = $(PROCESSOR_ARCHITECTURE)
+PROCESSOR_ARCHITECTURE = $(PROCESSOR_ARCHITECTURE)
!endif
!if defined(PROCESSOR_LEVEL)
- @$(APPEND) PROCESSOR_LEVEL = $(PROCESSOR_LEVEL)
+PROCESSOR_LEVEL = $(PROCESSOR_LEVEL)
!endif
|
@@ -68,7 +76,7 @@ srcdir = $(srcdir:\=/)
\# OS = $(OS)
\# RT = $(RT)
\# RUBY_INSTALL_NAME = ruby
-\# RUBY_SO_NAME = $$(RT)-$$(RUBY_INSTALL_NAME)17
+\# RUBY_SO_NAME = $$(RT)-$$(RUBY_INSTALL_NAME)$$(MAJOR)$$(MINOR)
\# prefix = /usr
\# CFLAGS = -q $$(DEBUGFLAGS) $$(OPTFLAGS) $$(PROCESSOR_FLAG) -w-
\# CPPFLAGS = -I. -I$$(srcdir) -I$$(srcdir)missing -DLIBRUBY_SO=\"$$(LIBRUBY_SO)\"
diff --git a/bignum.c b/bignum.c
index f3ab04ab8e..550dcfcafe 100644
--- a/bignum.c
+++ b/bignum.c
@@ -6,7 +6,7 @@
$Date$
created at: Fri Jun 10 00:48:55 JST 1994
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
**********************************************************************/
@@ -31,6 +31,9 @@ VALUE rb_cBignum;
#define BIGUP(x) ((BDIGIT_DBL)(x) << BITSPERDIG)
#define BIGDN(x) RSHIFT(x,BITSPERDIG)
#define BIGLO(x) ((BDIGIT)((x) & (BIGRAD-1)))
+#define BDIGMAX ((BDIGIT)-1)
+
+#define BIGZEROP(x) (RBIGNUM(x)->len == 0 || (RBIGNUM(x)->len == 1 && BDIGITS(x)[0] == 0))
static VALUE
bignew_1(klass, len, sign)
@@ -203,6 +206,7 @@ rb_quad_pack(buf, val)
q = BIGUP(q);
q += ds[len];
}
+ if (!RBIGNUM(val)->sign) q = -q;
}
memcpy(buf, (char*)&q, SIZEOF_LONG_LONG);
}
@@ -310,7 +314,8 @@ rb_cstr_to_inum(str, base, badcheck)
{
const char *s = str;
char *end;
- char sign = 1, c, nondigit = 0;
+ char sign = 1, nondigit = 0;
+ int c;
BDIGIT_DBL num;
long len, blen = 1;
long i;
@@ -372,17 +377,22 @@ rb_cstr_to_inum(str, base, badcheck)
str += 2;
}
break;
+ case 3:
+ len = 2;
+ break;
case 8:
- len = 3;
if (str[0] == '0' && (str[1] == 'o'||str[1] == 'O')) {
str += 2;
}
+ case 4: case 5: case 6: case 7:
+ len = 3;
break;
case 10:
- len = 4;
if (str[0] == '0' && (str[1] == 'd'||str[1] == 'D')) {
str += 2;
}
+ case 9: case 11: case 12: case 13: case 14: case 15:
+ len = 4;
break;
case 16:
len = 4;
@@ -390,6 +400,17 @@ rb_cstr_to_inum(str, base, badcheck)
str += 2;
}
break;
+ default:
+ if (base < 2 || 36 < base) {
+ rb_raise(rb_eArgError, "illegal radix %d", base);
+ }
+ if (base <= 32) {
+ len = 5;
+ }
+ else {
+ len = 6;
+ }
+ break;
}
if (*str == '0') { /* squeeze preceeding 0s */
while (*++str == '0');
@@ -404,10 +425,7 @@ rb_cstr_to_inum(str, base, badcheck)
if (badcheck) {
if (end == str) goto bad; /* no number */
while (*end && ISSPACE(*end)) end++;
- if (*end) { /* trailing garbage */
- bad:
- rb_invalid_str(s, "Integer");
- }
+ if (*end) goto bad; /* trailing garbage */
}
if (POSFIXABLE(val)) {
@@ -431,42 +449,27 @@ rb_cstr_to_inum(str, base, badcheck)
zds = BDIGITS(z);
for (i=len;i--;) zds[i]=0;
while (c = *str++) {
- switch (c) {
- case '8': case '9':
- if (base == 8) {
- c = base;
- break;
- }
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7':
- c = c - '0';
- nondigit = 0;
- break;
- case 'a': case 'b': case 'c':
- case 'd': case 'e': case 'f':
- c -= 'a' - 'A';
- case 'A': case 'B': case 'C':
- case 'D': case 'E': case 'F':
- if (base != 16) {
- nondigit = c;
- c = base;
- }
- else {
- c = c - 'A' + 10;
- nondigit = 0;
- }
- break;
- case '_':
+ if (c == '_') {
if (badcheck) {
if (nondigit) goto bad;
nondigit = c;
}
continue;
- default:
- c = base;
+ }
+ else if (isdigit(c)) {
+ c -= '0';
+ }
+ else if (islower(c)) {
+ c -= 'a' - 10;
+ }
+ else if (isupper(c)) {
+ c -= 'A' - 10;
+ }
+ else {
break;
}
if (c >= base) break;
+ nondigit = 0;
i = 0;
num = c;
for (;;) {
@@ -486,7 +489,10 @@ rb_cstr_to_inum(str, base, badcheck)
str--;
if (s+1 < str && str[-1] == '_') goto bad;
while (*str && ISSPACE(*str)) str++;
- if (*str) goto bad;
+ if (*str) {
+ bad:
+ rb_invalid_str(s, "Integer");
+ }
}
return bignorm(z);
@@ -502,7 +508,12 @@ rb_str_to_inum(str, base, badcheck)
long len;
StringValue(str);
- s = RSTRING(str)->ptr;
+ if (badcheck) {
+ s = StringValueCStr(str);
+ }
+ else {
+ s = RSTRING(str)->ptr;
+ }
if (s) {
len = RSTRING(str)->len;
if (s[len]) { /* no sentinel somehow */
@@ -512,9 +523,6 @@ rb_str_to_inum(str, base, badcheck)
p[len] = '\0';
s = p;
}
- if (badcheck && len != strlen(s)) {
- rb_raise(rb_eArgError, "string for Integer contains null byte");
- }
}
return rb_cstr_to_inum(s, base, badcheck);
}
@@ -595,7 +603,7 @@ rb_str2inum(str, base)
return rb_str_to_inum(str, base, base==0);
}
-static char hexmap[] = "0123456789abcdef";
+const char ruby_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz";
VALUE
rb_big2str(x, base)
VALUE x;
@@ -611,28 +619,42 @@ rb_big2str(x, base)
return rb_fix2str(x, base);
}
i = RBIGNUM(x)->len;
- if (i == 0 || (i == 1 && BDIGITS(x)[0] == 0)) {
+ if (BIGZEROP(x)) {
return rb_str_new2("0");
}
- if (base == 10) {
- j = (SIZEOF_BDIGITS/sizeof(char)*CHAR_BIT*i*241L)/800+2;
- hbase = 10000;
- }
- else if (base == 16) {
- j = (SIZEOF_BDIGITS/sizeof(char)*CHAR_BIT*i)/4+2;
- hbase = 0x10000;
- }
- else if (base == 8) {
- j = (SIZEOF_BDIGITS/sizeof(char)*CHAR_BIT*i)+2;
- hbase = 010000;
- }
- else if (base == 2) {
- j = (SIZEOF_BDIGITS*CHAR_BIT*i)+2;
- hbase = 020;
- }
- else {
+ j = SIZEOF_BDIGITS*CHAR_BIT*i;
+ switch (base) {
+ case 2: break;
+ case 3:
+ j = j * 647L / 1024;
+ break;
+ case 4: case 5: case 6: case 7:
+ j /= 2;
+ break;
+ case 8: case 9:
+ j /= 3;
+ break;
+ case 10: case 11: case 12: case 13: case 14: case 15:
+ j = j * 241L / 800;
+ break;
+ case 16: case 17: case 18: case 19: case 20: case 21:
+ case 22: case 23: case 24: case 25: case 26: case 27:
+ case 28: case 29: case 30: case 31:
+ j /= 4;
+ break;
+ case 32: case 33: case 34: case 35: case 36:
+ j /= 5;
+ break;
+ default:
rb_raise(rb_eArgError, "illegal radix %d", base);
+ break;
}
+ j += 2;
+
+ hbase = base * base;
+#if SIZEOF_BDIGITS > 2
+ hbase *= hbase;
+#endif
t = rb_big_clone(x);
ds = BDIGITS(t);
@@ -650,10 +672,10 @@ rb_big2str(x, base)
num %= hbase;
}
if (ds[i-1] == 0) i--;
- k = 4;
+ k = SIZEOF_BDIGITS;
while (k--) {
c = (char)(num % base);
- s[--j] = hexmap[(int)c];
+ s[--j] = ruby_digitmap[(int)c];
num /= base;
if (i == 0 && num == 0) break;
}
@@ -823,7 +845,10 @@ rb_big2dbl(x)
while (i--) {
d = ds[i] + BIGRAD*d;
}
- if (isinf(d)) d = HUGE_VAL;
+ if (isinf(d)) {
+ rb_warn("Bignum out of Float range");
+ d = HUGE_VAL;
+ }
if (!RBIGNUM(x)->sign) d = -d;
return d;
}
@@ -853,7 +878,7 @@ rb_big_cmp(x, y)
return rb_dbl_cmp(rb_big2dbl(x), RFLOAT(y)->value);
default:
- return rb_num_coerce_bin(x, y);
+ return rb_num_coerce_cmp(x, y);
}
if (RBIGNUM(x)->sign > RBIGNUM(y)->sign) return INT2FIX(1);
@@ -1122,9 +1147,9 @@ bigdivrem(x, y, divp, modp)
BDIGIT_DBL_SIGNED num;
BDIGIT dd, q;
+ if (BIGZEROP(y)) rb_num_zerodiv();
yds = BDIGITS(y);
- if (ny == 0 && yds[0] == 0) rb_num_zerodiv();
- if (nx < ny || nx == ny && BDIGITS(x)[nx - 1] < BDIGITS(y)[ny - 1]) {
+ if (nx < ny || (nx == ny && BDIGITS(x)[nx - 1] < BDIGITS(y)[ny - 1])) {
if (divp) *divp = rb_int2big(0);
if (modp) *modp = x;
return;
@@ -1247,8 +1272,7 @@ bigdivmod(x, y, divp, modp)
VALUE mod;
bigdivrem(x, y, divp, &mod);
- if (RBIGNUM(x)->sign != RBIGNUM(y)->sign &&
- !(RBIGNUM(mod)->len == 1 && BDIGITS(mod)[0] == 0)) {
+ if (RBIGNUM(x)->sign != RBIGNUM(y)->sign && !BIGZEROP(mod)) {
if (divp) *divp = bigadd(*divp, rb_int2big(1), 0);
if (modp) *modp = bigadd(mod, y, 1);
}
@@ -1283,7 +1307,6 @@ rb_big_div(x, y)
return bignorm(z);
}
-
static VALUE
rb_big_modulo(x, y)
VALUE x, y;
@@ -1350,6 +1373,32 @@ rb_big_divmod(x, y)
return rb_assoc_new(bignorm(div), bignorm(mod));
}
+static VALUE
+rb_big_quo(x, y)
+ VALUE x, y;
+{
+ double dx = rb_big2dbl(x);
+ double dy;
+
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ dy = (double)FIX2LONG(y);
+ break;
+
+ case T_BIGNUM:
+ dy = rb_big2dbl(y);
+ break;
+
+ case T_FLOAT:
+ dy = RFLOAT(y)->value;
+ break;
+
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+ return rb_float_new(dx / dy);
+}
+
VALUE
rb_big_pow(x, y)
VALUE x, y;
@@ -1678,7 +1727,7 @@ rb_big_coerce(x, y)
}
else {
rb_raise(rb_eTypeError, "Can't coerce %s to Bignum",
- rb_class2name(CLASS_OF(y)));
+ rb_obj_classname(y));
}
/* not reached */
return Qnil;
@@ -1702,8 +1751,8 @@ rb_big_rand(max, rand_buf)
{
VALUE v;
long len = RBIGNUM(max)->len;
-
- if (len == 0 && BDIGITS(max)[0] == 0) {
+
+ if (BIGZEROP(max)) {
return rb_float_new(rand_buf[0]);
}
v = bignew(len,1);
@@ -1738,6 +1787,7 @@ Init_Bignum()
rb_define_method(rb_cBignum, "divmod", rb_big_divmod, 1);
rb_define_method(rb_cBignum, "modulo", rb_big_modulo, 1);
rb_define_method(rb_cBignum, "remainder", rb_big_remainder, 1);
+ rb_define_method(rb_cBignum, "quo", rb_big_quo, 1);
rb_define_method(rb_cBignum, "**", rb_big_pow, 1);
rb_define_method(rb_cBignum, "&", rb_big_and, 1);
rb_define_method(rb_cBignum, "|", rb_big_or, 1);
@@ -1749,7 +1799,6 @@ Init_Bignum()
rb_define_method(rb_cBignum, "<=>", rb_big_cmp, 1);
rb_define_method(rb_cBignum, "==", rb_big_eq, 1);
- rb_define_method(rb_cBignum, "===", rb_big_eq, 1);
rb_define_method(rb_cBignum, "eql?", rb_big_eql, 1);
rb_define_method(rb_cBignum, "hash", rb_big_hash, 0);
rb_define_method(rb_cBignum, "to_f", rb_big_to_f, 0);
diff --git a/bin/erb b/bin/erb
index bfc86f56c8..2459d2562e 100755
--- a/bin/erb
+++ b/bin/erb
@@ -35,6 +35,8 @@ class ERB
return '%>'
when 2
return '%<>'
+ when '-'
+ return '%-'
end
end
module_function :trim_mode_opt
@@ -64,6 +66,10 @@ class ERB
safe_level = arg.to_i
when '-T' # trim mode
arg = ARGV.req_arg
+ if arg == '-'
+ trim_mode = arg
+ next
+ end
raise "invalid trim mode #{arg.dump}" unless arg =~ /^[0-2]$/
trim_mode = arg.to_i
when '-K' # KCODE
@@ -100,7 +106,7 @@ class ERB
-r [library] load a library
-K [kcode] specify KANJI code-set
-S [safe_level] set $SAFE (0..4)
- -T [trim_mode] specify trim_mode (0..2)
+ -T [trim_mode] specify trim_mode (0..2, -)
-P disregard the lin which starts in "%"
EOU
exit 1
diff --git a/class.c b/class.c
index e680dc7a67..24b2289552 100644
--- a/class.c
+++ b/class.c
@@ -6,7 +6,7 @@
$Date$
created at: Tue Aug 10 15:05:44 JST 1993
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
**********************************************************************/
@@ -14,6 +14,7 @@
#include "rubysig.h"
#include "node.h"
#include "st.h"
+#include "version.h"
#include <ctype.h>
extern st_table *rb_class_tbl;
@@ -54,7 +55,7 @@ clone_method(mid, body, tbl)
NODE *body;
st_table *tbl;
{
- st_insert(tbl, mid, NEW_METHOD(body->nd_body, body->nd_noex));
+ st_insert(tbl, mid, (st_data_t)NEW_METHOD(body->nd_body, body->nd_noex));
return ST_CONTINUE;
}
@@ -71,13 +72,14 @@ rb_mod_clone(module)
RCLASS(clone)->iv_tbl = st_copy(RCLASS(module)->iv_tbl);
id = rb_intern("__classpath__");
- st_delete(RCLASS(clone)->iv_tbl, &id, 0);
+ st_delete(RCLASS(clone)->iv_tbl, (st_data_t*)&id, 0);
id = rb_intern("__classid__");
- st_delete(RCLASS(clone)->iv_tbl, &id, 0);
+ st_delete(RCLASS(clone)->iv_tbl, (st_data_t*)&id, 0);
}
if (RCLASS(module)->m_tbl) {
RCLASS(clone)->m_tbl = st_init_numtable();
- st_foreach(RCLASS(module)->m_tbl, clone_method, RCLASS(clone)->m_tbl);
+ st_foreach(RCLASS(module)->m_tbl, clone_method,
+ (st_data_t)RCLASS(clone)->m_tbl);
}
return (VALUE)clone;
@@ -120,7 +122,8 @@ rb_singleton_class_clone(obj)
clone->iv_tbl = st_copy(RCLASS(klass)->iv_tbl);
}
clone->m_tbl = st_init_numtable();
- st_foreach(RCLASS(klass)->m_tbl, clone_method, clone->m_tbl);
+ st_foreach(RCLASS(klass)->m_tbl, clone_method,
+ (st_data_t)clone->m_tbl);
rb_singleton_class_attached(RBASIC(clone)->klass, (VALUE)clone);
FL_SET(clone, FL_SINGLETON);
return (VALUE)clone;
@@ -195,9 +198,6 @@ rb_define_class(name, super)
ID id;
id = rb_intern(name);
- if (rb_autoload_defined(id)) {
- rb_autoload_load(id);
- }
if (rb_const_defined(rb_cObject, id)) {
klass = rb_const_get(rb_cObject, id);
if (TYPE(klass) != T_CLASS) {
@@ -212,8 +212,9 @@ rb_define_class(name, super)
rb_warn("no super class for `%s', Object assumed", name);
}
klass = rb_define_class_id(id, super);
- rb_class_inherited(super, klass);
st_add_direct(rb_class_tbl, id, klass);
+ rb_const_set(rb_cObject, id, klass);
+ rb_class_inherited(super, klass);
return klass;
}
@@ -229,7 +230,7 @@ rb_define_class_under(outer, name, super)
id = rb_intern(name);
if (rb_const_defined_at(outer, id)) {
- klass = rb_const_get(outer, id);
+ klass = rb_const_get_at(outer, id);
if (TYPE(klass) != T_CLASS) {
rb_raise(rb_eTypeError, "%s is not a class", name);
}
@@ -244,8 +245,8 @@ rb_define_class_under(outer, name, super)
}
klass = rb_define_class_id(id, super);
rb_set_class_path(klass, outer, name);
- rb_class_inherited(super, klass);
rb_const_set(outer, id, klass);
+ rb_class_inherited(super, klass);
return klass;
}
@@ -284,17 +285,15 @@ rb_define_module(name)
ID id;
id = rb_intern(name);
- if (rb_autoload_defined(id)) {
- rb_autoload_load(id);
- }
if (rb_const_defined(rb_cObject, id)) {
module = rb_const_get(rb_cObject, id);
if (TYPE(module) == T_MODULE)
return module;
- rb_raise(rb_eTypeError, "%s is not a module", rb_class2name(CLASS_OF(module)));
+ rb_raise(rb_eTypeError, "%s is not a module", rb_obj_classname(module));
}
module = rb_define_module_id(id);
st_add_direct(rb_class_tbl, id, module);
+ rb_const_set(rb_cObject, id, module);
return module;
}
@@ -309,11 +308,11 @@ rb_define_module_under(outer, name)
id = rb_intern(name);
if (rb_const_defined_at(outer, id)) {
- module = rb_const_get(outer, id);
+ module = rb_const_get_at(outer, id);
if (TYPE(module) == T_MODULE)
return module;
rb_raise(rb_eTypeError, "%s::%s is not a module",
- rb_class2name(outer), rb_class2name(CLASS_OF(module)));
+ rb_class2name(outer), rb_obj_classname(module));
}
module = rb_define_module_id(id);
rb_const_set(outer, id, module);
@@ -365,12 +364,7 @@ rb_include_module(klass, module)
if (NIL_P(module)) return;
if (klass == module) return;
- switch (TYPE(module)) {
- case T_MODULE:
- case T_CLASS:
- case T_ICLASS:
- break;
- default:
+ if (TYPE(module) != T_MODULE) {
Check_Type(module, T_MODULE);
}
@@ -456,128 +450,120 @@ rb_mod_ancestors(mod)
return ary;
}
-#define VISI_CHECK(x,f) (((x)&NOEX_MASK) == (f))
+#define VISI(x) ((x)&NOEX_MASK)
+#define VISI_CHECK(x,f) (VISI(x) == (f))
static int
-ins_methods_i(key, body, ary)
- ID key;
- NODE *body;
+ins_methods_push(name, type, ary, visi)
+ ID name;
+ long type;
VALUE ary;
-{
- if (!body->nd_body) {
- rb_ary_push(ary, Qnil);
- rb_ary_push(ary, rb_str_new2(rb_id2name(key)));
- }
- else if (!VISI_CHECK(body->nd_noex, NOEX_PRIVATE)) {
- VALUE name = rb_str_new2(rb_id2name(key));
-
- if (!rb_ary_includes(ary, name)) {
- rb_ary_push(ary, name);
- }
+ long visi;
+{
+ if (type == -1) return ST_CONTINUE;
+ switch (visi) {
+ case NOEX_PRIVATE:
+ case NOEX_PROTECTED:
+ case NOEX_PUBLIC:
+ visi = (type == visi);
+ break;
+ default:
+ visi = (type != NOEX_PRIVATE);
+ break;
}
- else if (nd_type(body->nd_body) == NODE_ZSUPER) {
- rb_ary_push(ary, Qnil);
- rb_ary_push(ary, rb_str_new2(rb_id2name(key)));
+ if (visi) {
+ rb_ary_push(ary, rb_str_new2(rb_id2name(name)));
}
return ST_CONTINUE;
}
static int
-ins_methods_prot_i(key, body, ary)
- ID key;
- NODE *body;
+ins_methods_i(name, type, ary)
+ ID name;
+ long type;
VALUE ary;
{
- if (!body->nd_body) {
- rb_ary_push(ary, Qnil);
- rb_ary_push(ary, rb_str_new2(rb_id2name(key)));
- }
- else if (VISI_CHECK(body->nd_noex, NOEX_PROTECTED)) {
- VALUE name = rb_str_new2(rb_id2name(key));
+ return ins_methods_push(name, type, ary, -1); /* everything but private */
+}
- if (!rb_ary_includes(ary, name)) {
- rb_ary_push(ary, name);
- }
- }
- else if (nd_type(body->nd_body) == NODE_ZSUPER) {
- rb_ary_push(ary, Qnil);
- rb_ary_push(ary, rb_str_new2(rb_id2name(key)));
- }
- return ST_CONTINUE;
+static int
+ins_methods_prot_i(name, type, ary)
+ ID name;
+ long type;
+ VALUE ary;
+{
+ return ins_methods_push(name, type, ary, NOEX_PROTECTED);
}
static int
-ins_methods_priv_i(key, body, ary)
- ID key;
- NODE *body;
+ins_methods_priv_i(name, type, ary)
+ ID name;
+ long type;
VALUE ary;
{
- if (!body->nd_body) {
- rb_ary_push(ary, Qnil);
- rb_ary_push(ary, rb_str_new2(rb_id2name(key)));
- }
- else if (VISI_CHECK(body->nd_noex, NOEX_PRIVATE)) {
- VALUE name = rb_str_new2(rb_id2name(key));
+ return ins_methods_push(name, type, ary, NOEX_PRIVATE);
+}
- if (!rb_ary_includes(ary, name)) {
- rb_ary_push(ary, name);
- }
- }
- else if (nd_type(body->nd_body) == NODE_ZSUPER) {
- rb_ary_push(ary, Qnil);
- rb_ary_push(ary, rb_str_new2(rb_id2name(key)));
- }
- return ST_CONTINUE;
+static int
+ins_methods_pub_i(name, type, ary)
+ ID name;
+ long type;
+ VALUE ary;
+{
+ return ins_methods_push(name, type, ary, NOEX_PUBLIC);
}
static int
-ins_methods_pub_i(key, body, ary)
+method_entry(key, body, list)
ID key;
NODE *body;
- VALUE ary;
+ st_table *list;
{
- if (!body->nd_body) {
- rb_ary_push(ary, Qnil);
- rb_ary_push(ary, rb_str_new2(rb_id2name(key)));
- }
- else if (VISI_CHECK(body->nd_noex, NOEX_PUBLIC)) {
- VALUE name = rb_str_new2(rb_id2name(key));
+ long type;
- if (!rb_ary_includes(ary, name)) {
- rb_ary_push(ary, name);
- }
- }
- else if (nd_type(body->nd_body) == NODE_ZSUPER) {
- rb_ary_push(ary, Qnil);
- rb_ary_push(ary, rb_str_new2(rb_id2name(key)));
+ if (key == ID_ALLOCATOR) return ST_CONTINUE;
+ if (!st_lookup(list, key, 0)) {
+ if (!body->nd_body) type = -1; /* none */
+ else type = VISI(body->nd_noex);
+ st_add_direct(list, key, type);
}
return ST_CONTINUE;
}
static VALUE
-method_list(mod, option, func)
+class_instance_method_list(argc, argv, mod, func)
+ int argc;
+ VALUE *argv;
VALUE mod;
- int option;
- int (*func)();
+ int (*func) _((ID, long, VALUE));
{
VALUE ary;
- VALUE klass;
- VALUE *p, *q, *pend;
+ int recur;
+ st_table *list;
- ary = rb_ary_new();
- for (klass = mod; klass; klass = RCLASS(klass)->super) {
- st_foreach(RCLASS(klass)->m_tbl, func, ary);
- if (!option) break;
- }
- p = q = RARRAY(ary)->ptr; pend = p + RARRAY(ary)->len;
- while (p < pend) {
- if (*p == Qnil) {
- p+=2;
- continue;
- }
- *q++ = *p++;
+ if (argc == 0) {
+#if RUBY_VERSION_CODE < 181
+ rb_warn("%s: parameter will default to 'true' as of 1.8.1", rb_id2name(rb_frame_last_func()));
+ recur = Qfalse;
+#else
+ recur = Qtrue;
+#endif
+ }
+ else {
+ VALUE r;
+ rb_scan_args(argc, argv, "01", &r);
+ recur = RTEST(r);
}
- RARRAY(ary)->len = q - RARRAY(ary)->ptr;
+
+ list = st_init_numtable();
+ for (; mod; mod = RCLASS(mod)->super) {
+ st_foreach(RCLASS(mod)->m_tbl, method_entry, (st_data_t)list);
+ if (!recur) break;
+ }
+ ary = rb_ary_new();
+ st_foreach(list, func, ary);
+ st_free_table(list);
+
return ary;
}
@@ -587,10 +573,7 @@ rb_class_instance_methods(argc, argv, mod)
VALUE *argv;
VALUE mod;
{
- VALUE option;
-
- rb_scan_args(argc, argv, "01", &option);
- return method_list(mod, RTEST(option), ins_methods_i);
+ return class_instance_method_list(argc, argv, mod, ins_methods_i);
}
VALUE
@@ -599,10 +582,7 @@ rb_class_protected_instance_methods(argc, argv, mod)
VALUE *argv;
VALUE mod;
{
- VALUE option;
-
- rb_scan_args(argc, argv, "01", &option);
- return method_list(mod, RTEST(option), ins_methods_prot_i);
+ return class_instance_method_list(argc, argv, mod, ins_methods_prot_i);
}
VALUE
@@ -611,10 +591,7 @@ rb_class_private_instance_methods(argc, argv, mod)
VALUE *argv;
VALUE mod;
{
- VALUE option;
-
- rb_scan_args(argc, argv, "01", &option);
- return method_list(mod, RTEST(option), ins_methods_priv_i);
+ return class_instance_method_list(argc, argv, mod, ins_methods_priv_i);
}
VALUE
@@ -623,10 +600,7 @@ rb_class_public_instance_methods(argc, argv, mod)
VALUE *argv;
VALUE mod;
{
- VALUE option;
-
- rb_scan_args(argc, argv, "01", &option);
- return method_list(mod, RTEST(option), ins_methods_pub_i);
+ return class_instance_method_list(argc, argv, mod, ins_methods_pub_i);
}
VALUE
@@ -635,33 +609,32 @@ rb_obj_singleton_methods(argc, argv, obj)
VALUE *argv;
VALUE obj;
{
- VALUE all;
- VALUE ary;
- VALUE klass;
- VALUE *p, *q, *pend;
+ VALUE recur, ary, klass;
+ st_table *list;
- rb_scan_args(argc, argv, "01", &all);
- ary = rb_ary_new();
+ rb_scan_args(argc, argv, "01", &recur);
+ if (argc == 0) {
+#if RUBY_VERSION_CODE < 181
+ rb_warn("singleton_methods: parameter will default to 'true' as of 1.8.1");
+#else
+ recur = Qtrue;
+#endif
+ }
klass = CLASS_OF(obj);
+ list = st_init_numtable();
while (klass && FL_TEST(klass, FL_SINGLETON)) {
- st_foreach(RCLASS(klass)->m_tbl, ins_methods_i, ary);
+ st_foreach(RCLASS(klass)->m_tbl, method_entry, (st_data_t)list);
klass = RCLASS(klass)->super;
}
- if (RTEST(all)) {
+ if (RTEST(recur)) {
while (klass && TYPE(klass) == T_ICLASS) {
- st_foreach(RCLASS(klass)->m_tbl, ins_methods_i, ary);
+ st_foreach(RCLASS(klass)->m_tbl, method_entry, (st_data_t)list);
klass = RCLASS(klass)->super;
}
}
- p = q = RARRAY(ary)->ptr; pend = p + RARRAY(ary)->len;
- while (p < pend) {
- if (*p == Qnil) {
- p+=2;
- continue;
- }
- *q++ = *p++;
- }
- RARRAY(ary)->len = q - RARRAY(ary)->ptr;
+ ary = rb_ary_new();
+ st_foreach(list, ins_methods_i, ary);
+ st_free_table(list);
return ary;
}
@@ -673,7 +646,7 @@ rb_define_method_id(klass, name, func, argc)
VALUE (*func)();
int argc;
{
- rb_add_method(klass, name, NEW_CFUNC(func,argc), NOEX_PUBLIC|NOEX_CFUNC);
+ rb_add_method(klass, name, NEW_CFUNC(func,argc), NOEX_PUBLIC);
}
void
@@ -687,9 +660,6 @@ rb_define_method(klass, name, func, argc)
int ex = NOEX_PUBLIC;
- if (BUILTIN_TYPE(klass) == T_CLASS) {
- ex |= NOEX_CFUNC;
- }
rb_add_method(klass, id, NEW_CFUNC(func, argc), ex);
}
@@ -700,8 +670,7 @@ rb_define_protected_method(klass, name, func, argc)
VALUE (*func)();
int argc;
{
- rb_add_method(klass, rb_intern(name), NEW_CFUNC(func, argc),
- NOEX_PROTECTED|NOEX_CFUNC);
+ rb_add_method(klass, rb_intern(name), NEW_CFUNC(func, argc), NOEX_PROTECTED);
}
void
@@ -711,8 +680,7 @@ rb_define_private_method(klass, name, func, argc)
VALUE (*func)();
int argc;
{
- rb_add_method(klass, rb_intern(name), NEW_CFUNC(func, argc),
- NOEX_PRIVATE|NOEX_CFUNC);
+ rb_add_method(klass, rb_intern(name), NEW_CFUNC(func, argc), NOEX_PRIVATE);
}
void
@@ -884,7 +852,7 @@ rb_scan_args(argc, argv, fmt, va_alist)
if (*p == '&') {
var = va_arg(vargs, VALUE*);
if (rb_block_given_p()) {
- *var = rb_f_lambda();
+ *var = rb_block_proc();
}
else {
*var = Qnil;
diff --git a/compar.c b/compar.c
index 65347e5f22..d6cca36137 100644
--- a/compar.c
+++ b/compar.c
@@ -6,7 +6,7 @@
$Date$
created at: Thu Aug 26 14:39:48 JST 1993
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
**********************************************************************/
@@ -17,9 +17,12 @@ VALUE rb_mComparable;
static ID cmp;
int
-rb_cmpint(val)
- VALUE val;
+rb_cmpint(val, a, b)
+ VALUE val, a, b;
{
+ if (NIL_P(val)) {
+ rb_cmperr(a, b);
+ }
if (FIXNUM_P(val)) return FIX2INT(val);
if (TYPE(val) == T_BIGNUM) {
if (RBIGNUM(val)->sign) return 1;
@@ -30,6 +33,25 @@ rb_cmpint(val)
return 0;
}
+void
+rb_cmperr(x, y)
+ VALUE x, y;
+{
+ const char *classname;
+
+ if (SPECIAL_CONST_P(y)) {
+ y = rb_inspect(y);
+ classname = StringValuePtr(y);
+ }
+ else {
+ classname = rb_obj_classname(y);
+ }
+ rb_raise(rb_eArgError, "comparison of %s with %s failed",
+ rb_obj_classname(x), classname);
+}
+
+#define cmperr() (rb_cmperr(x, y), Qnil)
+
static VALUE
cmp_equal(x, y)
VALUE x, y;
@@ -37,10 +59,11 @@ cmp_equal(x, y)
int c;
if (x == y) return Qtrue;
+
c = rb_funcall(x, cmp, 1, y);
- if (NIL_P(c)) return Qfalse;
+ if (NIL_P(c)) return Qnil;
if (c == INT2FIX(0)) return Qtrue;
- if (rb_cmpint(c) == 0) return Qtrue;
+ if (rb_cmpint(c, x, y) == 0) return Qtrue;
return Qfalse;
}
@@ -50,8 +73,8 @@ cmp_gt(x, y)
{
VALUE c = rb_funcall(x, cmp, 1, y);
- if (NIL_P(c)) return Qfalse;
- if (rb_cmpint(c) > 0) return Qtrue;
+ if (NIL_P(c)) return cmperr();
+ if (rb_cmpint(c, x, y) > 0) return Qtrue;
return Qfalse;
}
@@ -61,8 +84,8 @@ cmp_ge(x, y)
{
VALUE c = rb_funcall(x, cmp, 1, y);
- if (NIL_P(c)) return Qfalse;
- if (rb_cmpint(c) >= 0) return Qtrue;
+ if (NIL_P(c)) return cmperr();
+ if (rb_cmpint(c, x, y) >= 0) return Qtrue;
return Qfalse;
}
@@ -72,7 +95,8 @@ cmp_lt(x, y)
{
VALUE c = rb_funcall(x, cmp, 1, y);
- if (rb_cmpint(c) < 0) return Qtrue;
+ if (NIL_P(c)) return cmperr();
+ if (rb_cmpint(c, x, y) < 0) return Qtrue;
return Qfalse;
}
@@ -82,8 +106,8 @@ cmp_le(x, y)
{
VALUE c = rb_funcall(x, cmp, 1, y);
- if (NIL_P(c)) return Qfalse;
- if (rb_cmpint(c) <= 0) return Qtrue;
+ if (NIL_P(c)) return cmperr();
+ if (rb_cmpint(c, x, y) <= 0) return Qtrue;
return Qfalse;
}
@@ -91,8 +115,8 @@ static VALUE
cmp_between(x, min, max)
VALUE x, min, max;
{
- if (cmp_lt(x, min)) return Qfalse;
- if (cmp_gt(x, max)) return Qfalse;
+ if (RTEST(cmp_lt(x, min))) return Qfalse;
+ if (RTEST(cmp_gt(x, max))) return Qfalse;
return Qtrue;
}
diff --git a/config.guess b/config.guess
index d7e236f92c..844739cdaf 100644
--- a/config.guess
+++ b/config.guess
@@ -1,9 +1,9 @@
#! /bin/sh
# Attempt to guess a canonical system name.
-# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
-# Free Software Foundation, Inc.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
-timestamp='2001-07-13'
+timestamp='2003-01-10'
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
@@ -24,8 +24,9 @@ timestamp='2001-07-13'
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
-# Written by Per Bothner <bothner@cygnus.com>.
-# Please send patches to <config-patches@gnu.org>.
+# Originally written by Per Bothner <per@bothner.com>.
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
#
# This script attempts to guess a canonical system name similar to
# config.sub. If it succeeds, it prints the system name on stdout, and
@@ -52,7 +53,7 @@ version="\
GNU config.guess ($timestamp)
Originally written by Per Bothner.
-Copyright (C) 1992, 93, 94, 95, 96, 97, 98, 99, 2000
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
@@ -77,7 +78,7 @@ while test $# -gt 0 ; do
-* )
echo "$me: invalid option $1$help" >&2
exit 1 ;;
-*)
+ * )
break ;;
esac
done
@@ -87,80 +88,82 @@ if test $# != 0; then
exit 1
fi
+trap 'exit 1' 1 2 15
-dummy=dummy-$$
-trap 'rm -f $dummy.c $dummy.o $dummy.rel $dummy; exit 1' 1 2 15
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
-# CC_FOR_BUILD -- compiler used by this script.
# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
# use `HOST_CC' if defined, but it is deprecated.
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
case $CC_FOR_BUILD,$HOST_CC,$CC in
- ,,) echo "int dummy(){}" > $dummy.c
- for c in cc gcc c89 ; do
- ($c $dummy.c -c -o $dummy.o) >/dev/null 2>&1
- if test $? = 0 ; then
- CC_FOR_BUILD="$c"; break
- fi
- done
- rm -f $dummy.c $dummy.o $dummy.rel
+ ,,) echo "int x;" > $dummy.c ;
+ for c in cc gcc c89 c99 ; do
+ if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$c"; break ;
+ fi ;
+ done ;
if test x"$CC_FOR_BUILD" = x ; then
- CC_FOR_BUILD=no_compiler_found
+ CC_FOR_BUILD=no_compiler_found ;
fi
- ;;
+ ;;
,,*) CC_FOR_BUILD=$CC ;;
,*,*) CC_FOR_BUILD=$HOST_CC ;;
-esac
-
-# Modified for Human68k by K.Okabe 1997.07.09
-# Last change: 1997.07.09
-
-case "$KSH_VERSION" in
-*X6*)
- echo m68k-sharp-human
- exit 0 ;;
-*)
- ;;
-esac
+esac ;'
# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
-# (ghazi@noc.rutgers.edu 8/24/94.)
+# (ghazi@noc.rutgers.edu 1994-08-24)
if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
PATH=$PATH:/.attbin ; export PATH
fi
UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
-UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
# Note: order is significant - the case branches are not exclusive.
case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
*:NetBSD:*:*)
- # Netbsd (nbsd) targets should (where applicable) match one or
+ # NetBSD (nbsd) targets should (where applicable) match one or
# more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
# *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
# switched to ELF, *-*-netbsd* would select the old
# object file format. This provides both forward
# compatibility and a consistent mechanism for selecting the
# object file format.
- # Determine the machine/vendor (is the vendor relevant).
- case "${UNAME_MACHINE}" in
- amiga) machine=m68k-unknown ;;
- arm32) machine=arm-unknown ;;
- atari*) machine=m68k-atari ;;
- sun3*) machine=m68k-sun ;;
- mac68k) machine=m68k-apple ;;
- macppc) machine=powerpc-apple ;;
- hp3[0-9][05]) machine=m68k-hp ;;
- ibmrt|romp-ibm) machine=romp-ibm ;;
- *) machine=${UNAME_MACHINE}-unknown ;;
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+ /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+ case "${UNAME_MACHINE_ARCH}" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
esac
# The Operating System including object format, if it has switched
# to ELF recently, or will in the future.
- case "${UNAME_MACHINE}" in
- i386|sparc|amiga|arm*|hp300|mvme68k|vax|atari|luna68k|mac68k|news68k|next68k|pc532|sun3*|x68k)
+ case "${UNAME_MACHINE_ARCH}" in
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ eval $set_cc_for_build
if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
| grep __ELF__ >/dev/null
then
@@ -175,13 +178,53 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
os=netbsd
;;
esac
- # The OS release
- release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
# contains redundant information, the shorter form:
# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
- echo "${machine}-${os}${release}"
- exit 0;;
+ echo "${machine}-${os}"
+ exit 0 ;;
+ amiga:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ arc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ hp300:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ macppc:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme88k:OpenBSD:*:*)
+ echo m88k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvmeppc:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ pmax:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sgi:OpenBSD:*:*)
+ echo mipseb-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sun3:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ wgrisc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:OpenBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:MicroBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-microbsd${UNAME_RELEASE}
+ exit 0 ;;
alpha:OSF1:*:*)
if test $UNAME_RELEASE = "V4.0"; then
UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
@@ -190,6 +233,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# A Tn.n version is a released field test version.
# A Xn.n version is an unreleased experimental baselevel.
# 1.2 uses "1.2" for uname -r.
+ eval $set_cc_for_build
cat <<EOF >$dummy.s
.data
\$Lformat:
@@ -215,9 +259,9 @@ main:
jsr \$26,exit
.end main
EOF
- $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null
+ $CC_FOR_BUILD -o $dummy $dummy.s 2>/dev/null
if test "$?" = 0 ; then
- case `./$dummy` in
+ case `$dummy` in
0-0)
UNAME_MACHINE="alpha"
;;
@@ -236,9 +280,14 @@ EOF
2-307)
UNAME_MACHINE="alphaev67"
;;
+ 2-1307)
+ UNAME_MACHINE="alphaev68"
+ ;;
+ 3-1307)
+ UNAME_MACHINE="alphaev7"
+ ;;
esac
fi
- rm -f $dummy.s $dummy
echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
exit 0 ;;
Alpha\ *:Windows_NT*:*)
@@ -252,30 +301,12 @@ EOF
exit 0 ;;
Amiga*:UNIX_System_V:4.0:*)
echo m68k-unknown-sysv4
- exit 0 ;;
- amiga:OpenBSD:*:*)
- echo m68k-unknown-openbsd${UNAME_RELEASE}
- exit 0 ;;
+ exit 0;;
*:[Aa]miga[Oo][Ss]:*:*)
echo ${UNAME_MACHINE}-unknown-amigaos
exit 0 ;;
- arc64:OpenBSD:*:*)
- echo mips64el-unknown-openbsd${UNAME_RELEASE}
- exit 0 ;;
- arc:OpenBSD:*:*)
- echo mipsel-unknown-openbsd${UNAME_RELEASE}
- exit 0 ;;
- hkmips:OpenBSD:*:*)
- echo mips-unknown-openbsd${UNAME_RELEASE}
- exit 0 ;;
- pmax:OpenBSD:*:*)
- echo mipsel-unknown-openbsd${UNAME_RELEASE}
- exit 0 ;;
- sgi:OpenBSD:*:*)
- echo mips-unknown-openbsd${UNAME_RELEASE}
- exit 0 ;;
- wgrisc:OpenBSD:*:*)
- echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-morphos
exit 0 ;;
*:OS/390:*:*)
echo i370-ibm-openedition
@@ -297,6 +328,10 @@ EOF
NILE*:*:*:dcosx)
echo pyramid-pyramid-svr4
exit 0 ;;
+ DRS?6000:UNIX_SV:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7 && exit 0 ;;
+ esac ;;
sun4H:SunOS:5.*:*)
echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
exit 0 ;;
@@ -325,7 +360,7 @@ EOF
echo m68k-sun-sunos${UNAME_RELEASE}
exit 0 ;;
sun*:*:4.2BSD:*)
- UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
case "`/bin/arch`" in
sun3)
@@ -339,12 +374,9 @@ EOF
aushp:SunOS:*:*)
echo sparc-auspex-sunos${UNAME_RELEASE}
exit 0 ;;
- atari*:OpenBSD:*:*)
- echo m68k-unknown-openbsd${UNAME_RELEASE}
- exit 0 ;;
# The situation for MiNT is a little confusing. The machine name
# can be virtually everything (everything which is not
- # "atarist" or "atariste" at least should have a processor
+ # "atarist" or "atariste" at least should have a processor
# > m68000). The system name ranges from "MiNT" over "FreeMiNT"
# to the lowercase version "mint" (or "freemint"). Finally
# the system name "TOS" denotes a system which is actually not
@@ -368,30 +400,9 @@ EOF
*:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
echo m68k-unknown-mint${UNAME_RELEASE}
exit 0 ;;
- sun3*:NetBSD:*:*)
- echo m68k-sun-netbsd${UNAME_RELEASE}
- exit 0 ;;
- sun3*:OpenBSD:*:*)
- echo m68k-unknown-openbsd${UNAME_RELEASE}
- exit 0 ;;
- mac68k:NetBSD:*:*)
- echo m68k-apple-netbsd${UNAME_RELEASE}
- exit 0 ;;
- mac68k:OpenBSD:*:*)
- echo m68k-unknown-openbsd${UNAME_RELEASE}
- exit 0 ;;
- mvme68k:OpenBSD:*:*)
- echo m68k-unknown-openbsd${UNAME_RELEASE}
- exit 0 ;;
- mvme88k:OpenBSD:*:*)
- echo m88k-unknown-openbsd${UNAME_RELEASE}
- exit 0 ;;
powerpc:machten:*:*)
echo powerpc-apple-machten${UNAME_RELEASE}
exit 0 ;;
- macppc:NetBSD:*:*)
- echo powerpc-apple-netbsd${UNAME_RELEASE}
- exit 0 ;;
RISC*:Mach:*:*)
echo mips-dec-mach_bsd4.3
exit 0 ;;
@@ -405,6 +416,7 @@ EOF
echo clipper-intergraph-clix${UNAME_RELEASE}
exit 0 ;;
mips:*:*:UMIPS | mips:*:*:RISCos)
+ eval $set_cc_for_build
sed 's/^ //' << EOF >$dummy.c
#ifdef __cplusplus
#include <stdio.h> /* for printf() prototype */
@@ -426,15 +438,20 @@ EOF
exit (-1);
}
EOF
- $CC_FOR_BUILD $dummy.c -o $dummy \
- && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
- && rm -f $dummy.c $dummy && exit 0
- rm -f $dummy.c $dummy
+ $CC_FOR_BUILD -o $dummy $dummy.c \
+ && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+ && exit 0
echo mips-mips-riscos${UNAME_RELEASE}
exit 0 ;;
Motorola:PowerMAX_OS:*:*)
echo powerpc-motorola-powermax
exit 0 ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit 0 ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit 0 ;;
Night_Hawk:Power_UNIX:*:*)
echo powerpc-harris-powerunix
exit 0 ;;
@@ -456,12 +473,12 @@ EOF
[ ${TARGET_BINARY_INTERFACE}x = x ]
then
echo m88k-dg-dgux${UNAME_RELEASE}
- else
+ else
echo m88k-dg-dguxbcs${UNAME_RELEASE}
- fi
+ fi
else
echo i586-dg-dgux${UNAME_RELEASE}
- fi
+ fi
exit 0 ;;
M88*:DolphinOS:*:*) # DolphinOS (SVR3)
echo m88k-dolphin-sysv3
@@ -495,6 +512,7 @@ EOF
exit 0 ;;
*:AIX:2:3)
if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ eval $set_cc_for_build
sed 's/^ //' << EOF >$dummy.c
#include <sys/systemcfg.h>
@@ -506,8 +524,7 @@ EOF
exit(0);
}
EOF
- $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0
- rm -f $dummy.c $dummy
+ $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
echo rs6000-ibm-aix3.2.5
elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
echo rs6000-ibm-aix3.2.4
@@ -516,7 +533,7 @@ EOF
fi
exit 0 ;;
*:AIX:*:[45])
- IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'`
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
IBM_ARCH=rs6000
else
@@ -556,10 +573,8 @@ EOF
9000/31? ) HP_ARCH=m68000 ;;
9000/[34]?? ) HP_ARCH=m68k ;;
9000/[678][0-9][0-9])
- case "${HPUX_REV}" in
- 11.[0-9][0-9])
- if [ -x /usr/bin/getconf ]; then
- sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
case "${sc_cpu_version}" in
523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
@@ -568,12 +583,13 @@ EOF
case "${sc_kernel_bits}" in
32) HP_ARCH="hppa2.0n" ;;
64) HP_ARCH="hppa2.0w" ;;
+ '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
esac ;;
esac
- fi ;;
- esac
- if [ "${HP_ARCH}" = "" ]; then
- sed 's/^ //' << EOF >$dummy.c
+ fi
+ if [ "${HP_ARCH}" = "" ]; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
#define _HPUX_SOURCE
#include <stdlib.h>
@@ -606,11 +622,21 @@ EOF
exit (0);
}
EOF
- (CCOPTS= $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null ) && HP_ARCH=`./$dummy`
- if test -z "$HP_ARCH"; then HP_ARCH=hppa; fi
- rm -f $dummy.c $dummy
- fi ;;
+ (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
esac
+ if [ ${HP_ARCH} = "hppa2.0w" ]
+ then
+ # avoid double evaluation of $set_cc_for_build
+ test -n "$CC_FOR_BUILD" || eval $set_cc_for_build
+ if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null
+ then
+ HP_ARCH="hppa2.0w"
+ else
+ HP_ARCH="hppa64"
+ fi
+ fi
echo ${HP_ARCH}-hp-hpux${HPUX_REV}
exit 0 ;;
ia64:HP-UX:*:*)
@@ -618,6 +644,7 @@ EOF
echo ia64-hp-hpux${HPUX_REV}
exit 0 ;;
3050*:HI-UX:*:*)
+ eval $set_cc_for_build
sed 's/^ //' << EOF >$dummy.c
#include <unistd.h>
int
@@ -643,8 +670,7 @@ EOF
exit (0);
}
EOF
- $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0
- rm -f $dummy.c $dummy
+ $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
echo unknown-hitachi-hiuxwe2
exit 0 ;;
9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
@@ -653,7 +679,7 @@ EOF
9000/8??:4.3bsd:*:*)
echo hppa1.0-hp-bsd
exit 0 ;;
- *9??*:MPE/iX:*:*)
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
echo hppa1.0-hp-mpeix
exit 0 ;;
hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
@@ -672,9 +698,6 @@ EOF
parisc*:Lites*:*:*)
echo hppa1.1-hp-lites
exit 0 ;;
- hppa*:OpenBSD:*:*)
- echo hppa-unknown-openbsd
- exit 0 ;;
C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
echo c1-convex-bsd
exit 0 ;;
@@ -693,41 +716,33 @@ EOF
C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
echo c4-convex-bsd
exit 0 ;;
- CRAY*X-MP:*:*:*)
- echo xmp-cray-unicos
- exit 0 ;;
CRAY*Y-MP:*:*:*)
- echo ymp-cray-unicos${UNAME_RELEASE}
+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
exit 0 ;;
CRAY*[A-Z]90:*:*:*)
echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
| sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
- -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
exit 0 ;;
CRAY*TS:*:*:*)
echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
exit 0 ;;
- CRAY*T3D:*:*:*)
- echo alpha-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
- exit 0 ;;
CRAY*T3E:*:*:*)
echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
exit 0 ;;
CRAY*SV1:*:*:*)
echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
exit 0 ;;
- CRAY-2:*:*:*)
- echo cray2-cray-unicos
- exit 0 ;;
+ *:UNICOS/mp:*:*)
+ echo nv1-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
- exit 0 ;;
- hp300:OpenBSD:*:*)
- echo m68k-unknown-openbsd${UNAME_RELEASE}
- exit 0 ;;
+ exit 0 ;;
i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
exit 0 ;;
@@ -738,10 +753,18 @@ EOF
echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
exit 0 ;;
*:FreeBSD:*:*)
- echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
- exit 0 ;;
- *:OpenBSD:*:*)
- echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ # Determine whether the default compiler uses glibc.
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <features.h>
+ #if __GLIBC__ >= 2
+ LIBC=gnu
+ #else
+ LIBC=
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`${LIBC:+-$LIBC}
exit 0 ;;
i*:CYGWIN*:*)
echo ${UNAME_MACHINE}-pc-cygwin
@@ -752,11 +775,17 @@ EOF
i*:PW*:*)
echo ${UNAME_MACHINE}-pc-pw32
exit 0 ;;
+ x86:Interix*:3*)
+ echo i586-pc-interix3
+ exit 0 ;;
+ [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+ echo i${UNAME_MACHINE}-pc-mks
+ exit 0 ;;
i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
# How do we know it's Interix rather than the generic POSIX subsystem?
# It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
# UNAME_MACHINE based on the output of uname instead of i386?
- echo i386-pc-interix
+ echo i586-pc-interix
exit 0 ;;
i*:UWIN*:*)
echo ${UNAME_MACHINE}-pc-uwin
@@ -783,105 +812,69 @@ EOF
echo ${UNAME_MACHINE}-unknown-linux
exit 0 ;;
mips:Linux:*:*)
- cat >$dummy.c <<EOF
-#ifdef __cplusplus
-#include <stdio.h> /* for printf() prototype */
-int main (int argc, char *argv[]) {
-#else
-int main (argc, argv) int argc; char *argv[]; {
-#endif
-#ifdef __MIPSEB__
- printf ("%s-unknown-linux\n", argv[1]);
-#endif
-#ifdef __MIPSEL__
- printf ("%sel-unknown-linux\n", argv[1]);
-#endif
- return 0;
-}
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips
+ #undef mipsel
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mipsel
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips
+ #else
+ CPU=
+ #endif
+ #endif
EOF
- $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm -f $dummy.c $dummy && exit 0
- rm -f $dummy.c $dummy
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && echo "${CPU}-unknown-linux" && exit 0
;;
- ppc:Linux:*:*)
- # Determine Lib Version
- cat >$dummy.c <<EOF
-#include <features.h>
-#if defined(__GLIBC__)
-extern char __libc_version[];
-extern char __libc_release[];
-#endif
-main(argc, argv)
- int argc;
- char *argv[];
-{
-#if defined(__GLIBC__)
- printf("%s %s\n", __libc_version, __libc_release);
-#else
- printf("unknown\n");
-#endif
- return 0;
-}
+ mips64:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips64
+ #undef mips64el
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mips64el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips64
+ #else
+ CPU=
+ #endif
+ #endif
EOF
- LIBC=""
- $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null
- if test "$?" = 0 ; then
- ./$dummy | grep 1\.99 > /dev/null
- if test "$?" = 0 ; then LIBC="-libc1" ; fi
- fi
- rm -f $dummy.c $dummy
- echo powerpc-unknown-linux${LIBC}
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && echo "${CPU}-unknown-linux" && exit 0
+ ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux
+ exit 0 ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux
exit 0 ;;
alpha:Linux:*:*)
- cat <<EOF >$dummy.s
- .data
- \$Lformat:
- .byte 37,100,45,37,120,10,0 # "%d-%x\n"
- .text
- .globl main
- .align 4
- .ent main
- main:
- .frame \$30,16,\$26,0
- ldgp \$29,0(\$27)
- .prologue 1
- .long 0x47e03d80 # implver \$0
- lda \$2,-1
- .long 0x47e20c21 # amask \$2,\$1
- lda \$16,\$Lformat
- mov \$0,\$17
- not \$1,\$18
- jsr \$26,printf
- ldgp \$29,0(\$26)
- mov 0,\$16
- jsr \$26,exit
- .end main
-EOF
- LIBC=""
- $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null
- if test "$?" = 0 ; then
- case `./$dummy` in
- 0-0) UNAME_MACHINE="alpha" ;;
- 1-0) UNAME_MACHINE="alphaev5" ;;
- 1-1) UNAME_MACHINE="alphaev56" ;;
- 1-101) UNAME_MACHINE="alphapca56" ;;
- 2-303) UNAME_MACHINE="alphaev6" ;;
- 2-307) UNAME_MACHINE="alphaev67" ;;
- esac
- objdump --private-headers $dummy | \
- grep ld.so.1 > /dev/null
- if test "$?" = 0 ; then
- LIBC="-libc1"
- fi
- fi
- rm -f $dummy.s $dummy
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+ if test "$?" = 0 ; then LIBC="-libc1" ; else LIBC="" ; fi
echo ${UNAME_MACHINE}-unknown-linux${LIBC}
exit 0 ;;
parisc:Linux:*:* | hppa:Linux:*:*)
# Look for CPU level
case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
- PA7*) echo hppa1.1-unknown-linux ;;
- PA8*) echo hppa2.0-unknown-linux ;;
- *) echo hppa-unknown-linux ;;
+ PA7*) echo hppa1.1-unknown-linux;;
+ PA8*) echo hppa2.0-unknown-linux;;
+ *) echo hppa-unknown-linux;;
esac
exit 0 ;;
parisc64:Linux:*:* | hppa64:Linux:*:*)
@@ -903,71 +896,35 @@ EOF
# The BFD linker knows what the default object file format is, so
# first see if it will tell us. cd to the root directory to prevent
# problems with other programs or directories called `ld' in the path.
- ld_supported_emulations=`cd /; ld --help 2>&1 \
- | sed -ne '/supported emulations:/!d
+ # Set LC_ALL=C to ensure ld outputs messages in English.
+ ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
+ | sed -ne '/supported targets:/!d
s/[ ][ ]*/ /g
- s/.*supported emulations: *//
+ s/.*supported targets: *//
s/ .*//
p'`
- case "$ld_supported_emulations" in
- i*86linux)
- echo "${UNAME_MACHINE}-pc-linux-aout"
- exit 0
- ;;
- elf_i*86)
+ case "$ld_supported_targets" in
+ elf32-i386)
TENTATIVE="${UNAME_MACHINE}-pc-linux"
;;
- i*86coff)
+ a.out-i386-linux)
+ echo "${UNAME_MACHINE}-pc-linux-aout"
+ exit 0 ;;
+ coff-i386)
echo "${UNAME_MACHINE}-pc-linux-coff"
- exit 0
- ;;
+ exit 0 ;;
+ "")
+ # Either a pre-BFD a.out linker (linux-oldld) or
+ # one that does not give us useful --help.
+ echo "${UNAME_MACHINE}-pc-linux-oldld"
+ exit 0 ;;
esac
- # Either a pre-BFD a.out linker (linux-oldld)
- # or one that does not give us useful --help.
- # GCC wants to distinguish between linux-oldld and linux-aout.
- # If ld does not provide *any* "supported emulations:"
- # that means it is gnuoldld.
- test -z "$ld_supported_emulations" && echo "${UNAME_MACHINE}-pc-linux-oldld" && exit 0
- case "${UNAME_MACHINE}" in
- i*86)
- VENDOR=pc;
- ;;
- *)
- VENDOR=unknown;
- ;;
- esac
- # Determine whether the default compiler is a.out or elf
- cat >$dummy.c <<EOF
-#include <features.h>
-#ifdef __cplusplus
-#include <stdio.h> /* for printf() prototype */
- int main (int argc, char *argv[]) {
-#else
- int main (argc, argv) int argc; char *argv[]; {
-#endif
-#ifdef __ELF__
-# ifdef __GLIBC__
-# if __GLIBC__ >= 2
- printf ("%s-${VENDOR}-linux\n", argv[1]);
-# else
- printf ("%s-${VENDOR}-linux-libc1\n", argv[1]);
-# endif
-# else
- printf ("%s-${VENDOR}-linux-libc1\n", argv[1]);
-# endif
-#else
- printf ("%s-${VENDOR}-linux-aout\n", argv[1]);
-#endif
- return 0;
-}
-EOF
- $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm -f $dummy.c $dummy && exit 0
- rm -f $dummy.c $dummy
test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
;;
-# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. earlier versions
-# are messed up and put the nodename in both sysname and nodename.
i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
echo i386-sequent-sysv4
exit 0 ;;
i*86:UNIX_SV:4.2MP:2.*)
@@ -978,6 +935,23 @@ EOF
# Use sysv4.2uw... so that sysv4* matches it.
echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
exit 0 ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo ${UNAME_MACHINE}-pc-os2-emx
+ exit 0 ;;
+ i*86:XTS-300:*:STOP)
+ echo ${UNAME_MACHINE}-unknown-stop
+ exit 0 ;;
+ i*86:atheos:*:*)
+ echo ${UNAME_MACHINE}-unknown-atheos
+ exit 0 ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ i*86:*DOS:*:*)
+ echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ exit 0 ;;
i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
@@ -986,36 +960,32 @@ EOF
echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
fi
exit 0 ;;
- i*86:*:5:7*)
- # Fixed at (any) Pentium or better
- UNAME_MACHINE=i586
- if [ ${UNAME_SYSTEM} = "UnixWare" ] ; then
- echo ${UNAME_MACHINE}-sco-sysv${UNAME_RELEASE}uw${UNAME_VERSION}
- else
- echo ${UNAME_MACHINE}-pc-sysv${UNAME_RELEASE}
- fi
+ i*86:*:5:[78]*)
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
exit 0 ;;
i*86:*:3.2:*)
if test -f /usr/options/cb.name; then
UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
elif /bin/uname -X 2>/dev/null >/dev/null ; then
- UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')`
- (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486
- (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
&& UNAME_MACHINE=i586
- (/bin/uname -X|egrep '^Machine.*Pent ?II' >/dev/null) \
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
&& UNAME_MACHINE=i686
- (/bin/uname -X|egrep '^Machine.*Pentium Pro' >/dev/null) \
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
&& UNAME_MACHINE=i686
echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
else
echo ${UNAME_MACHINE}-pc-sysv32
fi
exit 0 ;;
- i*86:*DOS:*:*)
- echo ${UNAME_MACHINE}-pc-msdosdjgpp
- exit 0 ;;
pc:*:*:*)
# Left here for compatibility:
# uname -m prints for DJGPP always 'pc', but it prints nothing about
@@ -1039,9 +1009,15 @@ EOF
# "miniframe"
echo m68010-convergent-sysv
exit 0 ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit 0 ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit 0 ;;
M68*:*:R3V[567]*:*)
test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
- 3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0)
+ 3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0)
OS_REL=''
test -r /etc/.relid \
&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
@@ -1058,9 +1034,6 @@ EOF
mc68030:UNIX_System_V:4.*:*)
echo m68k-atari-sysv4
exit 0 ;;
- i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
- echo i386-unknown-lynxos${UNAME_RELEASE}
- exit 0 ;;
TSUNAMI:LynxOS:2.*:*)
echo sparc-unknown-lynxos${UNAME_RELEASE}
exit 0 ;;
@@ -1087,8 +1060,8 @@ EOF
echo ns32k-sni-sysv
fi
exit 0 ;;
- PENTIUM:CPunix:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
- # says <Richard.M.Bartel@ccMail.Census.GOV>
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
echo i586-unisys-sysv4
exit 0 ;;
*:UNIX_System_V:4*:FTX*)
@@ -1100,6 +1073,10 @@ EOF
# From seanf@swdc.stratus.com.
echo i860-stratus-sysv4
exit 0 ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit 0 ;;
mc68*:A/UX:*:*)
echo m68k-apple-aux${UNAME_RELEASE}
exit 0 ;;
@@ -1128,6 +1105,9 @@ EOF
SX-5:SUPER-UX:*:*)
echo sx5-nec-superux${UNAME_RELEASE}
exit 0 ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
Power*:Rhapsody:*:*)
echo powerpc-apple-rhapsody${UNAME_RELEASE}
exit 0 ;;
@@ -1135,18 +1115,24 @@ EOF
echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
exit 0 ;;
*:Darwin:*:*)
- echo `uname -p`-apple-darwin${UNAME_RELEASE}
+ case `uname -p` in
+ *86) UNAME_PROCESSOR=i686 ;;
+ powerpc) UNAME_PROCESSOR=powerpc ;;
+ esac
+ echo ${UNAME_PROCESSOR}-apple-darwin
exit 0 ;;
*:procnto*:*:* | *:QNX:[0123456789]*:*)
- if test "${UNAME_MACHINE}" = "x86pc"; then
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = "x86"; then
+ UNAME_PROCESSOR=i386
UNAME_MACHINE=pc
fi
- echo `uname -p`-${UNAME_MACHINE}-nto-qnx
+ echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
exit 0 ;;
*:QNX:*:4*)
echo i386-pc-qnx
exit 0 ;;
- NSR-[KW]:NONSTOP_KERNEL:*:*)
+ NSR-[DGKLNPTVW]:NONSTOP_KERNEL:*:*)
echo nsr-tandem-nsk${UNAME_RELEASE}
exit 0 ;;
*:NonStop-UX:*:*)
@@ -1169,11 +1155,6 @@ EOF
fi
echo ${UNAME_MACHINE}-unknown-plan9
exit 0 ;;
- i*86:OS/2:*:*)
- # If we were able to find `uname', then EMX Unix compatibility
- # is probably installed.
- echo ${UNAME_MACHINE}-pc-os2-emx
- exit 0 ;;
*:TOPS-10:*:*)
echo pdp10-unknown-tops10
exit 0 ;;
@@ -1197,6 +1178,7 @@ esac
#echo '(No uname command or uname output not recognized.)' 1>&2
#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+eval $set_cc_for_build
cat >$dummy.c <<EOF
#ifdef _SEQUENT_
# include <sys/types.h>
@@ -1283,7 +1265,7 @@ main ()
#endif
#if defined (vax)
-#if !defined (ultrix)
+# if !defined (ultrix)
# include <sys/param.h>
# if defined (BSD)
# if BSD == 43
@@ -1296,11 +1278,11 @@ main ()
# endif
# endif
# else
- printf ("vax-dec-bsd\n"); exit (0);
+ printf ("vax-dec-bsd\n"); exit (0);
# endif
-#else
- printf ("vax-dec-ultrix\n"); exit (0);
-#endif
+# else
+ printf ("vax-dec-ultrix\n"); exit (0);
+# endif
#endif
#if defined (alliant) && defined (i860)
@@ -1311,8 +1293,7 @@ main ()
}
EOF
-$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm -f $dummy.c $dummy && exit 0
-rm -f $dummy.c $dummy
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0
# Apollos put the system type in the environment.
diff --git a/config.sub b/config.sub
index 914903cfed..d2c7af03c5 100644
--- a/config.sub
+++ b/config.sub
@@ -1,9 +1,9 @@
#! /bin/sh
# Configuration validation subroutine script.
-# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
-# Free Software Foundation, Inc.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
-timestamp='2001-04-20'
+timestamp='2003-01-03'
# This file is (in principle) common to ALL GNU software.
# The presence of a machine in this file suggests that SOME GNU software
@@ -29,7 +29,8 @@ timestamp='2001-04-20'
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
-# Please send patches to <config-patches@gnu.org>.
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
#
# Configuration subroutine to validate and canonicalize a configuration type.
# Supply the specified configuration type as an argument.
@@ -80,7 +81,7 @@ Try \`$me --help' for more information."
# Parse command line
while test $# -gt 0 ; do
-case $1 in
+ case $1 in
--time-stamp | --time* | -t )
echo "$timestamp" ; exit 0 ;;
--version | -v )
@@ -95,12 +96,12 @@ case $1 in
echo "$me: invalid option $1$help"
exit 1 ;;
- *local*)
+ *local*)
# First pass through any local machine types.
- echo $1
+ echo $1
exit 0;;
- *)
+ * )
break ;;
esac
done
@@ -117,7 +118,7 @@ esac
# Here we must recognize all the valid KERNEL-OS combinations.
maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
case $maybe_os in
- nto-qnx* | linux-gnu* | linux-libc1 | storm-chaos* | os2-emx*)
+ nto-qnx* | linux-gnu* | freebsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*)
os=-$maybe_os
basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
;;
@@ -157,9 +158,14 @@ case $os in
os=-vxworks
basic_machine=$1
;;
- -hiuxmpp)
- os=-hiuxmpp
+ -chorusos*)
+ os=-chorusos
+ basic_machine=$1
;;
+ -chorusrdb)
+ os=-chorusrdb
+ basic_machine=$1
+ ;;
-hiux*)
os=-hiuxwe2
;;
@@ -218,26 +224,48 @@ esac
case $basic_machine in
# Recognize the basic CPU types without company name.
# Some are omitted here because they have special meanings below.
- tahoe | i860 | ia64 | m32r | m68k | m68000 | m88k | ns32k | arc \
- | arm | arme[lb] | arm[bl]e | armv[2345] | armv[345][lb] | strongarm | xscale \
- | pyramid | mn10200 | mn10300 | tron | a29k \
- | 580 | i960 | h8300 \
- | x86 | ppcbe | mipsbe | mipsle | shbe | shle \
- | hppa | hppa1.0 | hppa1.1 | hppa2.0 | hppa2.0w | hppa2.0n \
- | hppa64 \
- | alpha | alphaev[4-8] | alphaev56 | alphapca5[67] \
- | alphaev6[78] \
- | we32k | ns16k | clipper | i370 | sh | sh[34] \
- | powerpc | powerpcle \
- | 1750a | dsp16xx | pdp10 | pdp11 \
- | mips16 | mips64 | mipsel | mips64el \
- | mips64orion | mips64orionel | mipstx39 | mipstx39el \
- | mips64vr4300 | mips64vr4300el | mips64vr4100 | mips64vr4100el \
- | mips64vr5000 | miprs64vr5000el | mcore | s390 | s390x \
- | sparc | sparclet | sparclite | sparc64 | sparcv9 | sparcv9b \
- | v850 | c4x \
- | thumb | d10v | d30v | fr30 | avr | openrisc | tic80 \
- | pj | pjl | h8500)
+ 1750a | 580 \
+ | a29k \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+ | clipper \
+ | d10v | d30v | dlx | dsp16xx \
+ | fr30 | frv \
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | i370 | i860 | i960 | ia64 \
+ | ip2k \
+ | m32r | m68000 | m68k | m88k | mcore \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64el \
+ | mips64vr | mips64vrel \
+ | mips64orion | mips64orionel \
+ | mips64vr4100 | mips64vr4100el \
+ | mips64vr4300 | mips64vr4300el \
+ | mips64vr5000 | mips64vr5000el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
+ | mipstx39 | mipstx39el \
+ | mn10200 | mn10300 \
+ | msp430 \
+ | ns16k | ns32k \
+ | openrisc | or32 \
+ | pdp10 | pdp11 | pj | pjl \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | pyramid \
+ | sh | sh[1234] | sh3e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
+ | sh64 | sh64le \
+ | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv9 | sparcv9b \
+ | strongarm \
+ | tahoe | thumb | tic80 | tron \
+ | v850 | v850e \
+ | we32k \
+ | x86 | xscale | xstormy16 | xtensa \
+ | z8k)
basic_machine=$basic_machine-unknown
;;
m6811 | m68hc11 | m6812 | m68hc12)
@@ -245,7 +273,7 @@ case $basic_machine in
basic_machine=$basic_machine-unknown
os=-none
;;
- m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | z8k | v70 | w65)
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
;;
# We use `pc' rather than `unknown'
@@ -260,31 +288,58 @@ case $basic_machine in
exit 1
;;
# Recognize the basic CPU types with company name.
- # FIXME: clean up the formatting here.
- vax-* | tahoe-* | i*86-* | i860-* | ia64-* | m32r-* | m68k-* | m68000-* \
- | m88k-* | sparc-* | ns32k-* | fx80-* | arc-* | c[123]* \
- | arm-* | armbe-* | armle-* | armv*-* | strongarm-* | xscale-* \
- | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* \
- | power-* | none-* | 580-* | cray2-* | h8300-* | h8500-* | i960-* \
- | xmp-* | ymp-* \
- | x86-* | ppcbe-* | mipsbe-* | mipsle-* | shbe-* | shle-* \
- | hppa-* | hppa1.0-* | hppa1.1-* | hppa2.0-* | hppa2.0w-* \
- | hppa2.0n-* | hppa64-* \
- | alpha-* | alphaev[4-8]-* | alphaev56-* | alphapca5[67]-* \
- | alphaev6[78]-* \
- | we32k-* | cydra-* | ns16k-* | pn-* | np1-* | xps100-* \
- | clipper-* | orion-* \
- | sparclite-* | pdp10-* | pdp11-* | sh-* | powerpc-* | powerpcle-* \
- | sparc64-* | sparcv9-* | sparcv9b-* | sparc86x-* \
- | mips16-* | mips64-* | mipsel-* \
- | mips64el-* | mips64orion-* | mips64orionel-* \
- | mips64vr4100-* | mips64vr4100el-* | mips64vr4300-* | mips64vr4300el-* \
- | mipstx39-* | mipstx39el-* | mcore-* \
- | f30[01]-* | f700-* | s390-* | s390x-* | sv1-* | t3e-* \
- | [cjt]90-* \
- | m88110-* | m680[01234]0-* | m683?2-* | m68360-* | z8k-* | d10v-* \
- | thumb-* | v850-* | d30v-* | tic30-* | tic80-* | c30-* | fr30-* \
- | bs2000-* | tic54x-* | c54x-* | x86_64-* | pj-* | pjl-*)
+ 580-* \
+ | a29k-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
+ | avr-* \
+ | bs2000-* \
+ | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* \
+ | clipper-* | cydra-* \
+ | d10v-* | d30v-* | dlx-* \
+ | elxsi-* \
+ | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
+ | h8300-* | h8500-* \
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | ip2k-* \
+ | m32r-* \
+ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | mcore-* \
+ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+ | mips16-* \
+ | mips64-* | mips64el-* \
+ | mips64vr-* | mips64vrel-* \
+ | mips64orion-* | mips64orionel-* \
+ | mips64vr4100-* | mips64vr4100el-* \
+ | mips64vr4300-* | mips64vr4300el-* \
+ | mips64vr5000-* | mips64vr5000el-* \
+ | mipsisa32-* | mipsisa32el-* \
+ | mipsisa32r2-* | mipsisa32r2el-* \
+ | mipsisa64-* | mipsisa64el-* \
+ | mipsisa64sb1-* | mipsisa64sb1el-* \
+ | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+ | mipstx39-* | mipstx39el-* \
+ | msp430-* \
+ | none-* | np1-* | nv1-* | ns16k-* | ns32k-* \
+ | orion-* \
+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | pyramid-* \
+ | romp-* | rs6000-* \
+ | sh-* | sh[1234]-* | sh3e-* | sh[34]eb-* | shbe-* \
+ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+ | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \
+ | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
+ | tahoe-* | thumb-* | tic30-* | tic4x-* | tic54x-* | tic80-* | tron-* \
+ | v850-* | v850e-* | vax-* \
+ | we32k-* \
+ | x86-* | x86_64-* | xps100-* | xscale-* | xstormy16-* \
+ | xtensa-* \
+ | ymp-* \
+ | z8k-*)
;;
# Recognize the various machine names and aliases which stand
# for a CPU type and a company and sometimes even an OS.
@@ -347,6 +402,10 @@ case $basic_machine in
basic_machine=ns32k-sequent
os=-dynix
;;
+ c90)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
convex-c1)
basic_machine=c1-convex
os=-bsd
@@ -367,16 +426,8 @@ case $basic_machine in
basic_machine=c38-convex
os=-bsd
;;
- cray | ymp)
- basic_machine=ymp-cray
- os=-unicos
- ;;
- cray2)
- basic_machine=cray2-cray
- os=-unicos
- ;;
- [cjt]90)
- basic_machine=${basic_machine}-cray
+ cray | j90)
+ basic_machine=j90-cray
os=-unicos
;;
crds | unos)
@@ -391,6 +442,14 @@ case $basic_machine in
decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
basic_machine=mips-dec
;;
+ decsystem10* | dec10*)
+ basic_machine=pdp10-dec
+ os=-tops10
+ ;;
+ decsystem20* | dec20*)
+ basic_machine=pdp10-dec
+ os=-tops20
+ ;;
delta | 3300 | motorola-3300 | motorola-delta \
| 3300-motorola | delta-motorola)
basic_machine=m68k-motorola
@@ -571,14 +630,6 @@ case $basic_machine in
basic_machine=m68k-atari
os=-mint
;;
- mipsel*-linux*)
- basic_machine=mipsel-unknown
- os=-linux
- ;;
- mips*-linux*)
- basic_machine=mips-unknown
- os=-linux
- ;;
mips3*-*)
basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
;;
@@ -593,6 +644,10 @@ case $basic_machine in
basic_machine=m68k-rom68k
os=-coff
;;
+ morphos)
+ basic_machine=powerpc-unknown
+ os=-morphos
+ ;;
msdos)
basic_machine=i386-pc
os=-msdos
@@ -665,6 +720,10 @@ case $basic_machine in
np1)
basic_machine=np1-gould
;;
+ nv1)
+ basic_machine=nv1-cray
+ os=-unicosmp
+ ;;
nsr-tandem)
basic_machine=nsr-tandem
;;
@@ -672,6 +731,10 @@ case $basic_machine in
basic_machine=hppa1.1-oki
os=-proelf
;;
+ or32 | or32-*)
+ basic_machine=or32-unknown
+ os=-coff
+ ;;
OSE68000 | ose68000)
basic_machine=m68000-ericsson
os=-ose
@@ -694,19 +757,19 @@ case $basic_machine in
pbb)
basic_machine=m68k-tti
;;
- pc532 | pc532-*)
+ pc532 | pc532-*)
basic_machine=ns32k-pc532
;;
- pentium | p5 | k5 | k6 | nexgen)
+ pentium | p5 | k5 | k6 | nexgen | viac3)
basic_machine=i586-pc
;;
- pentiumpro | p6 | 6x86 | athlon)
+ pentiumpro | p6 | 6x86 | athlon | athlon_*)
basic_machine=i686-pc
;;
pentiumii | pentium2)
basic_machine=i686-pc
;;
- pentium-* | p5-* | k5-* | k6-* | nexgen-*)
+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
pentiumpro-* | p6-* | 6x86-* | athlon-*)
@@ -721,15 +784,25 @@ case $basic_machine in
power) basic_machine=power-ibm
;;
ppc) basic_machine=powerpc-unknown
- ;;
+ ;;
ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
ppcle | powerpclittle | ppc-le | powerpc-little)
basic_machine=powerpcle-unknown
- ;;
+ ;;
ppcle-* | powerpclittle-*)
basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
+ ppc64) basic_machine=powerpc64-unknown
+ ;;
+ ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+ basic_machine=powerpc64le-unknown
+ ;;
+ ppc64le-* | powerpc64little-*)
+ basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
ps2)
basic_machine=i386-ibm
;;
@@ -747,10 +820,22 @@ case $basic_machine in
rtpc | rtpc-*)
basic_machine=romp-ibm
;;
+ s390 | s390-*)
+ basic_machine=s390-ibm
+ ;;
+ s390x | s390x-*)
+ basic_machine=s390x-ibm
+ ;;
sa29200)
basic_machine=a29k-amd
os=-udi
;;
+ sb1)
+ basic_machine=mipsisa64sb1-unknown
+ ;;
+ sb1el)
+ basic_machine=mipsisa64sb1el-unknown
+ ;;
sequent)
basic_machine=i386-sequent
;;
@@ -758,7 +843,7 @@ case $basic_machine in
basic_machine=sh-hitachi
os=-hms
;;
- sparclite-wrs)
+ sparclite-wrs | simso-wrs)
basic_machine=sparclite-wrs
os=-vxworks
;;
@@ -825,9 +910,17 @@ case $basic_machine in
os=-dynix
;;
t3e)
- basic_machine=t3e-cray
+ basic_machine=alphaev5-cray
+ os=-unicos
+ ;;
+ t90)
+ basic_machine=t90-cray
os=-unicos
;;
+ tic4x | c4x*)
+ basic_machine=tic4x-unknown
+ os=-coff
+ ;;
tic54x | c54x*)
basic_machine=tic54x-unknown
os=-coff
@@ -838,6 +931,10 @@ case $basic_machine in
tx39el)
basic_machine=mipstx39el-unknown
;;
+ toad1)
+ basic_machine=pdp10-xkl
+ os=-tops20
+ ;;
tower | tower-32)
basic_machine=m68k-ncr
;;
@@ -862,8 +959,8 @@ case $basic_machine in
os=-vms
;;
vpp*|vx|vx-*)
- basic_machine=f301-fujitsu
- ;;
+ basic_machine=f301-fujitsu
+ ;;
vxworks960)
basic_machine=i960-wrs
os=-vxworks
@@ -884,13 +981,13 @@ case $basic_machine in
basic_machine=hppa1.1-winbond
os=-proelf
;;
- xmp)
- basic_machine=xmp-cray
- os=-unicos
- ;;
- xps | xps100)
+ xps | xps100)
basic_machine=xps100-honeywell
;;
+ ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
z8k-*-coff)
basic_machine=z8k-unknown
os=-sim
@@ -911,13 +1008,6 @@ case $basic_machine in
op60c)
basic_machine=hppa1.1-oki
;;
- mips)
- if [ x$os = x-linux ]; then
- basic_machine=mips-unknown
- else
- basic_machine=mips-mips
- fi
- ;;
romp)
basic_machine=romp-ibm
;;
@@ -937,13 +1027,16 @@ case $basic_machine in
we32k)
basic_machine=we32k-att
;;
- sh3 | sh4)
+ sh3 | sh4 | sh3eb | sh4eb | sh[1234]le | sh3ele)
basic_machine=sh-unknown
;;
+ sh64)
+ basic_machine=sh64-unknown
+ ;;
sparc | sparcv9 | sparcv9b)
basic_machine=sparc-sun
;;
- cydra)
+ cydra)
basic_machine=cydra-cydrome
;;
orion)
@@ -958,10 +1051,6 @@ case $basic_machine in
pmac | pmac-mpw)
basic_machine=powerpc-apple
;;
- c4x*)
- basic_machine=c4x-none
- os=-coff
- ;;
*-unknown)
# Make sure to match an already-canonicalized machine name.
;;
@@ -1021,11 +1110,15 @@ case $os in
| -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
| -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
| -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -chorusos* | -chorusrdb* \
| -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
- | -mingw32* | -linux* | -uxpv* | -beos* | -mpeix* | -udk* \
- | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \
+ | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
| -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
- | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* | -os2*)
+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+ | -powermax* | -dnix* | -microbsd*)
# Remember, each alternative MUST END IN *, to match a version number.
;;
-qnx*)
@@ -1037,8 +1130,10 @@ case $os in
;;
esac
;;
+ -nto-qnx*)
+ ;;
-nto*)
- os=-nto-qnx
+ os=`echo $os | sed -e 's|nto|nto-qnx|'`
;;
-sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
| -windows* | -osx | -abug | -netware* | -os9* | -beos* \
@@ -1047,6 +1142,9 @@ case $os in
-mac*)
os=`echo $os | sed -e 's|mac|macos|'`
;;
+ -linux*)
+ os=-linux
+ ;;
-sunos5*)
os=`echo $os | sed -e 's|sunos5|solaris2|'`
;;
@@ -1074,14 +1172,20 @@ case $os in
-acis*)
os=-aos
;;
+ -atheos*)
+ os=-atheos
+ ;;
-386bsd)
os=-bsd
;;
-ctix* | -uts*)
os=-sysv
;;
+ -nova*)
+ os=-rtmk-nova
+ ;;
-ns2 )
- os=-nextstep2
+ os=-nextstep2
;;
-nsk*)
os=-nsk
@@ -1120,16 +1224,8 @@ case $os in
-xenix)
os=-xenix
;;
- -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
- os=-mint
- ;;
- -uxpds)
- os=-uxpds
- ;;
- -human)
- ;;
- -beos)
- os=-beos
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ os=-mint
;;
-none)
;;
@@ -1162,10 +1258,11 @@ case $basic_machine in
arm*-semi)
os=-aout
;;
+ # This must come before the *-dec entry.
pdp10-*)
os=-tops20
;;
- pdp11-*)
+ pdp11-*)
os=-none
;;
*-dec | vax-*)
@@ -1192,6 +1289,9 @@ case $basic_machine in
mips*-*)
os=-elf
;;
+ or32-*)
+ os=-coff
+ ;;
*-tti) # must be before sparc entry or we get the wrong os.
os=-sysv3
;;
@@ -1255,19 +1355,19 @@ case $basic_machine in
*-next)
os=-nextstep3
;;
- *-gould)
+ *-gould)
os=-sysv
;;
- *-highlevel)
+ *-highlevel)
os=-bsd
;;
*-encore)
os=-bsd
;;
- *-sgi)
+ *-sgi)
os=-irix
;;
- *-siemens)
+ *-siemens)
os=-sysv4
;;
*-masscomp)
@@ -1339,7 +1439,7 @@ case $basic_machine in
-ptx*)
vendor=sequent
;;
- -vxsim* | -vxworks*)
+ -vxsim* | -vxworks* | -windiss*)
vendor=wrs
;;
-aux*)
@@ -1354,6 +1454,9 @@ case $basic_machine in
-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
vendor=atari
;;
+ -vos*)
+ vendor=stratus
+ ;;
esac
basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
;;
diff --git a/configure.in b/configure.in
index a00dc0d7cb..ad200de61f 100644
--- a/configure.in
+++ b/configure.in
@@ -35,7 +35,7 @@ AC_SUBST(CPPOUTFILE)])
AC_DEFUN(RUBY_PROG_GNU_LD,
[AC_CACHE_CHECK(whether the linker is GNU ld, rb_cv_prog_gnu_ld,
-[if `$CC $CFLAGS $CPPFLAGS $LDFLAGS --print-prog-name=ld` -v 2>&1 | grep "GNU ld" > /dev/null; then
+[if `$CC $CFLAGS $CPPFLAGS $LDFLAGS --print-prog-name=ld 2>&1` -v 2>&1 | grep "GNU ld" > /dev/null; then
rb_cv_prog_gnu_ld=yes
else
rb_cv_prog_gnu_ld=no
@@ -262,18 +262,16 @@ nextstep*) ;;
openstep*) ;;
rhapsody*) ;;
darwin*) LIBS="-lobjc $LIBS";;
+hpux*) LIBS="-lm $LIBS"
+ ac_cv_c_inline=no;;
human*) ac_cv_func_getpgrp_void=yes;;
beos*) ;;
cygwin*) rb_cv_have_daylight=no
- rb_cv_need_io_flush_between_rw=no
- rb_cv_need_io_flush_before_seek=no
ac_cv_var_tzname=no
ac_cv_func__setjmp=no
ac_cv_func_setitimer=no
;;
-mingw*) LIBS="-lwsock32 -lmsvcrt $LIBS"
- rb_cv_need_io_flush_between_rw=no
- rb_cv_need_io_flush_before_seek=no
+mingw*) LIBS="-lwsock32 $LIBS"
ac_cv_header_a_out_h=no
ac_cv_header_pwd_h=no
ac_cv_header_utime_h=no
@@ -284,12 +282,13 @@ mingw*) LIBS="-lwsock32 -lmsvcrt $LIBS"
ac_cv_header_sys_times_h=no
ac_cv_func_times=yes
ac_cv_func_waitpid=yes
+ ac_cv_func_fsync=yes
ac_cv_func_vsnprintf=yes
ac_cv_func_seekdir=yes
ac_cv_func_telldir=yes
- ac_cv_func_crypt=no
+ ac_cv_lib_crypt_crypt=no
;;
-os2_emx*) LIBS="-lm $LIBS"
+os2-emx*) LIBS="-lm $LIBS"
ac_cv_lib_dir_opendir=no;;
msdosdjgpp*) LIBS="-lm $LIBS"
ac_cv_func_getpgrp_void=yes;;
@@ -329,32 +328,27 @@ freebsd*) LIBS="-lm $LIBS"
fi
fi
;;
-linux*) LIBS="-lm $LIBS"
- case "$target_cpu" in
- alpha*)
- CFLAGS="-mieee $CFLAGS" ;;
- esac ;;
-osf*) LIBS="-lm $LIBS"
- case "$target_cpu"::"$GCC" in
- alpha*::yes)
- CFLAGS="-mieee $CFLAGS" ;;
- alpha*::no|alpha*::)
- CFLAGS="-ieee $CFLAGS" ;;
- esac ;;
*) LIBS="-lm $LIBS";;
esac
AC_CHECK_LIB(crypt, crypt)
AC_CHECK_LIB(dl, dlopen) # Dynamic linking for SunOS/Solaris and SYSV
AC_CHECK_LIB(dld, shl_load) # Dynamic linking for HP-UX
+case "$target_cpu" in
+alpha*) case "$target_os"::"$GCC" in
+ *::yes) CFLAGS="-mieee $CFLAGS" ;; # gcc
+ osf*) CFLAGS="-ieee $CFLAGS" ;; # ccc
+ esac ;;
+esac
+
dnl Checks for header files.
AC_HEADER_DIRENT
AC_HEADER_STDC
AC_HEADER_SYS_WAIT
AC_CHECK_HEADERS(stdlib.h string.h unistd.h limits.h sys/file.h sys/ioctl.h\
fcntl.h sys/fcntl.h sys/select.h sys/time.h sys/times.h sys/param.h\
- syscall.h pwd.h a.out.h utime.h memory.h direct.h sys/resource.h \
- sys/mkdev.h sys/utime.h)
+ syscall.h pwd.h grp.h a.out.h utime.h memory.h direct.h sys/resource.h \
+ sys/mkdev.h sys/utime.h float.h)
dnl Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_UID_T
@@ -372,14 +366,21 @@ AC_FUNC_FSEEKO
AC_CHECK_FUNCS(ftello)
AC_REPLACE_FUNCS(dup2 memmove mkdir strcasecmp strncasecmp strerror strftime\
strchr strstr strtoul crypt flock vsnprintf\
- isinf isnan finite hypot acosh)
+ isinf isnan finite hypot acosh erf)
AC_CHECK_FUNCS(fmod killpg wait4 waitpid syscall chroot fsync\
truncate chsize times utimes fcntl lockf lstat symlink readlink\
setitimer setruid seteuid setreuid setresuid setproctitle\
- setrgid setegid setregid setresgid pause lchown lchmod\
- getpgrp setpgrp getpgid setpgid getgroups getpriority getrlimit\
- dlopen sigprocmask sigaction _setjmp setsid telldir seekdir fchmod\
- mktime timegm cosh sinh tanh)
+ setrgid setegid setregid setresgid issetugid pause lchown lchmod\
+ getpgrp setpgrp getpgid setpgid initgroups getgroups setgroups\
+ getpriority getrlimit dlopen sigprocmask sigaction _setjmp\
+ setsid telldir seekdir fchmod mktime timegm cosh sinh tanh)
+AC_ARG_ENABLE(setreuid,
+ [ --enable-setreuid use setreuid()/setregid() according to need even if obsolete.],
+ [use_setreuid=$enableval])
+if test "$use_setreuid" = yes; then
+ AC_DEFINE(USE_SETREUID)
+ AC_DEFINE(USE_SETREGID)
+fi
AC_STRUCT_TIMEZONE
AC_CACHE_CHECK(for struct tm.tm_gmtoff, rb_cv_member_struct_tm_tm_gmtoff,
[AC_TRY_COMPILE([#include <time.h>],
@@ -474,6 +475,7 @@ AC_C_BIGENDIAN
AC_C_CONST
AC_C_CHAR_UNSIGNED
AC_C_INLINE
+AC_C_VOLATILE
AC_CACHE_CHECK(whether right shift preserve sign bit, rb_cv_rshift_sign,
[AC_TRY_RUN([
@@ -559,14 +561,24 @@ else
fi
fi
-AC_DEFUN(RUBY_CHECK_IO_NEED_FLUSH,
-[AC_CACHE_CHECK(whether need to flush [$1], [$2],
+AC_DEFUN(RUBY_CHECK_IO_NEED,
+[AC_CACHE_CHECK(whether need to [$1], [$2],
[AC_TRY_RUN([
#include <stdio.h>
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#endif
+#define before_seek(f) ]ifelse(index($2,flush_before_seek),-1,[fflush(f)],[(f,0)])[
+#define reset_rw(f) ]ifelse(index($2,seek_between_rw),-1,[do_seek(f,SEEK_CUR)],[(f,0)])[
+#define do_seek(f, w) (before_seek(f), fseek(f,0,w))
char *fn = "conftest.dat";
char *wombat = "wombat\n";
char *koara = "koara\n";
+char *kangaroo = "kangaroo\n";
int main()
{
@@ -576,33 +588,66 @@ int main()
if (!(f = fopen(fn, "w+"))) return 1;
fputs(wombat, f);
- fflush(f);
- fseek(f, 0, 0);
+ do_seek(f, SEEK_SET);
if (!fgets(buf, BUFSIZ, f) || strcmp(buf, wombat)) goto fail;
- ]ifelse(index($2,between_rw),-1,fflush(f);)[
+ reset_rw(f);
fputs(koara, f);
- ]ifelse(index($2,before_seek),-1,fflush(f);)[
- fseek(f, 0, 0);
- ]ifelse(index($2,between_rw),-1,fflush(f);)[
+ fputs(kangaroo, f);
+ do_seek(f, SEEK_SET);
if (!fgets(buf, BUFSIZ, f) || strcmp(buf, wombat)) goto fail;
if (!fgets(buf, BUFSIZ, f) || strcmp(buf, koara)) goto fail;
+ if (!fgets(buf, BUFSIZ, f) || strcmp(buf, kangaroo)) goto fail;
+ do_seek(f, SEEK_SET);
+ if (!fgets(buf, BUFSIZ, f) || strcmp(buf, wombat)) goto fail;
+ reset_rw(f);
+ fputc('X', f);
+ reset_rw(f);
+ if (!fgets(buf, BUFSIZ, f) || strcmp(buf, koara+1)) goto fail;
+ if (!fgets(buf, BUFSIZ, f) || strcmp(buf, kangaroo)) goto fail;
+ do_seek(f, SEEK_SET);
+ if (!fgets(buf, BUFSIZ, f) || strcmp(buf, wombat)) goto fail;
+ if (!fgets(buf, BUFSIZ, f) || buf[0] != 'X' || strcmp(buf+1, koara+1)) goto fail;
+ if (!fgets(buf, BUFSIZ, f) || strcmp(buf, kangaroo)) goto fail;
r = 0;
fail:
fclose(f);
unlink(fn);
return r;
}
-], [$2]=no, [$2]=yes, [$2]=yes)])])
-
-RUBY_CHECK_IO_NEED_FLUSH(between R/W, rb_cv_need_io_flush_between_rw)
-RUBY_CHECK_IO_NEED_FLUSH(before seek, rb_cv_need_io_flush_before_seek)
-if test "$rb_cv_need_io_flush_between_rw" = yes; then
- AC_DEFINE(NEED_IO_FLUSH_BETWEEN_RW, 1)
+], [$2]=no, [$2]=yes, [$2]=[$3])])])
+RUBY_CHECK_IO_NEED(seek between R/W, rb_cv_need_io_seek_between_rw, yes)
+if test "$rb_cv_need_io_seek_between_rw" = yes; then
+ AC_DEFINE(NEED_IO_SEEK_BETWEEN_RW, 1)
fi
+RUBY_CHECK_IO_NEED(flush before seek, rb_cv_need_io_flush_before_seek, no)
if test "$rb_cv_need_io_flush_before_seek" = yes; then
AC_DEFINE(NEED_IO_FLUSH_BEFORE_SEEK, 1)
fi
+case "$target_cpu" in
+m68*|i?86|sparc) rb_cv_stack_grow_dir=-1;;
+esac
+AC_CACHE_CHECK(stack growing direction, rb_cv_stack_grow_dir,
+ [AC_TRY_RUN([
+/* recurse to get rid of inlining */
+static int
+stack_growup_p(addr, n)
+ volatile int *addr, n;
+{
+ volatile int end;
+ if (n > 0)
+ return *addr = stack_growup_p(addr, n - 1);
+ else
+ return (&end > addr);
+}
+int main()
+{
+ int x;
+ return stack_growup_p(&x, 10);
+}
+], rb_cv_stack_grow_dir=-1, rb_cv_stack_grow_dir=+1, rb_cv_stack_grow_dir=0)])
+AC_DEFINE_UNQUOTED(STACK_GROW_DIRECTION, $rb_cv_stack_grow_dir)
+
dnl default value for $KANJI
DEFAULT_KCODE="KCODE_NONE"
@@ -662,7 +707,7 @@ linux*)
else
LDFLAGS="-rdynamic"
fi;;
-netbsd*)
+netbsd*|openbsd*)
if [[ "`$CC -dM -E - </dev/null | grep __ELF__`" != "" ]]
then
netbsd_elf=yes
@@ -699,12 +744,13 @@ if test "$with_dln_a_out" != yes; then
beos*) ;;
cygwin*) ;;
mingw*) ;;
+ aix*) ;;
netbsd*) CCDLFLAGS=-fPIC;;
*) CCDLFLAGS=-fPIC;;
esac
else
case "$target_os" in
- hpux*) CCDLFLAGS='+z';;
+ hpux*) CCDLFLAGS='+Z';;
solaris*|irix*) CCDLFLAGS='-KPIC' ;;
sunos*) CCDLFLAGS='-PIC' ;;
esix*|uxpds*) CCDLFLAGS='-KPIC' ;;
@@ -715,7 +761,7 @@ if test "$with_dln_a_out" != yes; then
case "$target_os" in
hpux*) DLDFLAGS="-E"
LDSHARED='ld -b'
- LDFLAGS="-Wl,-E"
+ XLDFLAGS="-Wl,-E"
rb_cv_dlopen=yes;;
solaris*) if test "$GCC" = yes; then
LDSHARED='$(CC) -Wl,-G'
@@ -737,7 +783,7 @@ if test "$with_dln_a_out" != yes; then
rb_cv_dlopen=yes ;;
esix*|uxpds*) LDSHARED="ld -G"
rb_cv_dlopen=yes ;;
- osf*) LDSHARED="$CC -shared"
+ osf*) LDSHARED="ld -shared -expect_unresolved \"*\""
rb_cv_dlopen=yes ;;
linux*) LDSHARED="$CC -shared"
rb_cv_dlopen=yes ;;
@@ -758,7 +804,10 @@ if test "$with_dln_a_out" != yes; then
LIBPATHFLAG=' -L%1$-s -Wl,-R%1$-s'
fi
rb_cv_dlopen=yes ;;
- openbsd*) LDSHARED="ld -Bforcearchive -Bshareable"
+ openbsd*) LDSHARED="\$(CC) -shared ${CCDLFLAGS}"
+ if test "$rb_cv_binary_elf" = yes; then
+ LDFLAGS="-Wl,-E"
+ fi
rb_cv_dlopen=yes ;;
bsdi3*) case "$CC" in
*shlicc*) LDSHARED="$CC -r"
@@ -787,6 +836,7 @@ if test "$with_dln_a_out" != yes; then
XLDFLAGS='-Wl,-bE:ruby.imp'
DLDFLAGS='-brtl -eInit_$(TARGET) -bI:$(topdir)/ruby.imp -bM:SRE -T512 -H512 -lc'
LDFLAGS="-brtl"
+ ARCHFILE="ruby.imp"
rb_cv_dlopen=yes ;;
human*) DLDFLAGS=''
@@ -811,11 +861,13 @@ if test "$with_dln_a_out" != yes; then
rb_cv_dlopen=yes;;
cygwin*|mingw*) : ${LDSHARED="${CC} -shared -s"}
XLDFLAGS='-Wl,--stack,0x02000000'
- DLDFLAGS="${DLDFLAGS} "'$(DEFFILE)'
+ DLDFLAGS="${DLDFLAGS} -Wl,--enable-auto-import,--export-all"
rb_cv_dlopen=yes ;;
hiuxmpp) LDSHARED='ld -r' ;;
atheos*) LDSHARED="$CC -shared"
rb_cv_dlopen=yes ;;
+ os2-emx*) LDFLAGS="$LDFLAGS -Zbsd-signals"
+ ;;
*) LDSHARED='ld' ;;
esac
AC_MSG_RESULT($rb_cv_dlopen)
@@ -863,7 +915,7 @@ else
AC_DEFINE(DLEXT, ".bundle");;
darwin*) DLEXT=bundle
AC_DEFINE(DLEXT, ".bundle");;
- os2_emx*) DLEXT=dll
+ os2-emx*) DLEXT=dll
AC_DEFINE(DLEXT, ".dll");;
cygwin*|mingw*) DLEXT=so
AC_DEFINE(DLEXT, ".so")
@@ -956,7 +1008,7 @@ rb_cv_missing_fconvert=yes, rb_cv_missing_fconvert=no, rb_cv_missing_fconvert=no
setup=Setup.x68
;;
dnl OS/2 environment w/ Autoconf 2.1x for EMX
- os2_emx)
+ os2-emx)
AC_LIBOBJ([os2])
setup=Setup.emx
;;
@@ -996,7 +1048,7 @@ LIBRUBYARG='$(LIBRUBYARG_STATIC)'
SOLIBS=
case "$target_os" in
- cygwin*|mingw*|beos*|openstep*|nextstep*|rhapsody*|darwin*|os2_emx*)
+ cygwin*|mingw*|beos*|openstep*|nextstep*|rhapsody*|darwin*|os2-emx*)
DLDLIBS=""
;;
*)
@@ -1056,7 +1108,8 @@ if test "$enable_shared" = 'yes'; then
;;
openbsd*)
SOLIBS='$(LIBS)'
- ;;
+ LIBRUBY_SO='lib$(RUBY_INSTALL_NAME).so.$(MAJOR).'`expr ${MINOR} \* 10 + ${TEENY}`
+ ;;
solaris*)
XLDFLAGS='-R${libdir}'
;;
@@ -1072,7 +1125,6 @@ if test "$enable_shared" = 'yes'; then
else
LIBRUBY_LDSHARED='/usr/ccs/bin/ld'
LIBRUBY_DLDFLAGS='-bE:ruby.imp -bM:SRE -bnoentry'
- ARCHFILE="ruby.imp"
fi
LIBRUBYARG_SHARED='-L${libdir} -Wl,lib$(RUBY_SO_NAME).so'
SOLIBS='-lm -lc'
@@ -1117,8 +1169,11 @@ case "$target_os" in
darwin*)
CFLAGS="$CFLAGS -pipe"
;;
- os2_emx)
- CFLAGS="$CFLAGS -DOS2"
+ os2-emx)
+ CFLAGS="$CFLAGS -DOS2 -Zmts"
+ LIBRUBY_A=`echo $LIBRUBY_A | sed 's/^lib//'`
+ LIBRUBY_SO=`echo $LIBRUBY_SO | sed 's/^lib//'`
+ LIBRUBY_ALIASES=`for i in $LIBRUBY_ALIASES; do echo "$i"; done | sed 's/^lib//'`
;;
osf*)
if test "$GCC" != "yes" ; then
@@ -1144,28 +1199,26 @@ case "$target_os" in
esac
;;
cygwin*|mingw*)
- EXPORT_PREFIX=' '
case "$target_os" in
cygwin*)
RUBY_SO_NAME=$target_os-'$(RUBY_INSTALL_NAME)'${MAJOR}${MINOR}
AC_LIBOBJ([strftime])
- CCDLFLAGS=-DUSEIMPORTLIB ;;
+ ;;
mingw*)
RUBY_SO_NAME=msvcrt-'$(RUBY_INSTALL_NAME)'${MAJOR}${MINOR}
AC_LIBOBJ([win32])
COMMON_LIBS=m
COMMON_MACROS="WIN32_LEAN_AND_MEAN="
COMMON_HEADERS="windows.h winsock.h"
- CFLAGS="-D__NO_ISOCEXT $CFLAGS"
- CCDLFLAGS=-DIMPORT ;;
+ ;;
esac
+ XCFLAGS="$XCFLAGS"
+ LIBRUBY_DLDFLAGS="${DLDFLAGS}"' -Wl,--out-implib=$(LIBRUBY)'
if test x"$enable_shared" = xyes; then
LIBRUBY_SO='$(RUBY_SO_NAME)'.dll
- LIBRUBY_DLDFLAGS='-Wl,--out-implib=$(LIBRUBY) $(RUBYDEF)'
LIBRUBY='lib$(LIBRUBY_SO).a'
else
LIBRUBY_SO=dummy
- LIBRUBY_DLDFLAGS=''
LIBRUBY='lib$(RUBY_SO_NAME).a'
LIBRUBYARG='-l$(RUBY_SO_NAME)'
fi
@@ -1173,10 +1226,22 @@ case "$target_os" in
FIRSTMAKEFILE=GNUmakefile:cygwin/GNUmakefile.in
SOLIBS='$(LIBS)'
;;
+ hpux*)
+ case "$YACC" in
+ *yacc*)
+ XCFLAGS="$XCFLAGS -DYYMAXDEPTH=300"
+ YACC="$YACC -Nl40000 -Nm40000"
+ ;;
+ esac
+ ;;
*)
;;
esac
+case "$build_os" in
+ *msdosdjgpp*) FIRSTMAKEFILE=GNUmakefile:djgpp/GNUmakefile.in;;
+esac
+
AC_SUBST(LIBRUBY_LDSHARED)
AC_SUBST(LIBRUBY_DLDFLAGS)
AC_SUBST(RUBY_INSTALL_NAME)
@@ -1219,7 +1284,7 @@ case "$target_os" in
;;
esac
case "$target_os" in
- cygwin*|mingw*|*djgpp*|os2_emx*)
+ cygwin*|mingw*|*djgpp*|os2-emx*)
RUBY_LIB_PREFIX="/lib/ruby"
;;
*)
@@ -1234,7 +1299,7 @@ AC_ARG_WITH(sitedir,
[sitedir='${prefix}/lib/ruby/site_ruby'])
SITE_DIR="`eval \"echo ${sitedir}\"`"
case "$target_os" in
- cygwin*|mingw*|*djgpp*|os2_emx*)
+ cygwin*|mingw*|*djgpp*|os2-emx*)
RUBY_SITE_LIB_PATH="`expr "$SITE_DIR" : "$prefix\(/.*\)"`" ||
RUBY_SITE_LIB_PATH="$SITE_DIR";;
*)
@@ -1282,6 +1347,28 @@ if test "$search_path" != ""; then
AC_DEFINE_UNQUOTED(RUBY_SEARCH_PATH,"$search_path")
fi
+AC_ARG_WITH(mantype,
+ [ --with-mantype=TYPE specify man page type; TYPE is one of man and doc],
+ [
+ case "$withval" in
+ man|doc)
+ MANTYPE=$withval
+ ;;
+ *)
+ AC_MSG_ERROR(invalid man type: $withval)
+ ;;
+ esac
+ ])
+if test -z "$MANTYPE"; then
+ AC_PATH_PROGS(NROFF, nroff awf, /bin/false, "/usr/bin:/usr/ucb")
+ if ${NROFF} -mdoc ${srcdir}/ruby.1 >/dev/null 2>&1; then
+ MANTYPE=doc
+ else
+ MANTYPE=man
+ fi
+fi
+AC_SUBST(MANTYPE)
+
if test -f config.h && tr -d '\015' < confdefs.h | cmp -s config.h -; then
echo "config.h unchanged"
else
diff --git a/cygwin/GNUmakefile.in b/cygwin/GNUmakefile.in
index af79698963..7e8672b9d3 100644
--- a/cygwin/GNUmakefile.in
+++ b/cygwin/GNUmakefile.in
@@ -2,12 +2,11 @@ include Makefile
ENABLE_SHARED=@ENABLE_SHARED@
-ifeq ($(ENABLE_SHARED),yes)
- CPPFLAGS += -DLIBRUBY_SO=\"$(LIBRUBY_SO)\"
-else
+ifneq ($(ENABLE_SHARED),yes)
RUBY_EXP = $(RUBY_INSTALL_NAME).exp
EXTOBJS = $(RUBY_EXP)
LIBRUBYARG = $(LIBRUBY_A)
+ LIBRUBY_SO =
endif
ifeq ($(RUBY_INSTALL_NAME),ruby)
@@ -16,12 +15,11 @@ else
RUBYW_INSTALL_NAME = $(subst ruby,rubyw,$(RUBY_INSTALL_NAME))
endif
WPROGRAM = $(RUBYW_INSTALL_NAME)$(EXEEXT)
-RUBYDEF = $(RUBY_INSTALL_NAME).def
SOLIBS := $(RUBY_SO_NAME).res.@OBJEXT@ $(SOLIBS)
EXTOBJS += $(@:$(EXEEXT)=.res.@OBJEXT@)
-$(LIBRUBY_SO): $(RUBYDEF) $(RUBY_SO_NAME).res.@OBJEXT@ $(RUBY_EXP)
-$(LIBRUBY): $(LIBRUBY_SO)
+$(LIBRUBY): $(RUBY_EXP) $(LIBRUBY_SO)
+$(RUBY_EXP) $(LIBRUBY_SO): $(RUBY_SO_NAME).res.@OBJEXT@
%.res.@OBJEXT@: %.rc
@WINDRES@ --include-dir . --include-dir $(<D) --include-dir $(srcdir)/win32 $< $@
@@ -38,14 +36,12 @@ $(WPROGRAM): $(RUBYW_INSTALL_NAME).res.@OBJEXT@
$(PURIFY) $(CC) -mwindows -e _mainCRTStartup $(LDFLAGS) $(XLDFLAGS) \
$(MAINOBJ) $(EXTOBJS) $(LIBRUBYARG) $(LIBS) -o $@
-$(RUBYDEF): $(LIBRUBY_A)
- echo EXPORTS > $(RUBYDEF)
- @NM@ --extern-only --defined-only $(LIBRUBY_A) | sed -n 's/.* [CDT] _//p' >> $(RUBYDEF)
-
-$(RUBY_EXP): $(RUBYDEF)
- @DLLWRAP@ --output-exp=$(RUBY_EXP) --output-lib=$(LIBRUBY) \
- --def=$(RUBYDEF) $(LIBRUBY_A) $(LIBS) -o $(PROGRAM)
- rm $(PROGRAM)
+$(RUBY_EXP): $(LIBRUBY_A)
+ @DLLWRAP@ --target=@target_os@ --driver-name=$(CC) \
+ --output-exp=$(RUBY_EXP) \
+ --export-all $(LIBRUBY_A) $(LIBS) -o $(PROGRAM)
+ $(LDSHARED) $(DLDFLAGS) $(OBJS) dmyext.o $(SOLIBS) -o $(PROGRAM)
+ @rm -f $(PROGRAM)
GNUmakefile: $(srcdir)/cygwin/GNUmakefile.in
diff --git a/defines.h b/defines.h
index 52d9856ab6..6fd17808ae 100644
--- a/defines.h
+++ b/defines.h
@@ -12,10 +12,6 @@
#define RUBY
-#if !defined(__STDC__) && !defined(_MSC_VER)
-# define volatile
-#endif
-
#ifdef __cplusplus
# ifndef HAVE_PROTOTYPES
# define HAVE_PROTOTYPES 1
@@ -124,27 +120,36 @@ void xfree _((void*));
#include "vms/vms.h"
#endif
-#if defined __CYGWIN__
-# undef EXTERN
-# if defined USEIMPORTLIB
-# define EXTERN extern __declspec(dllimport)
-# else
-# define EXTERN extern __declspec(dllexport)
+#undef RUBY_EXTERN
+#if defined _WIN32 && !defined __GNUC__
+# ifndef RUBY_EXPORT
+# define RUBY_EXTERN extern __declspec(dllimport)
# endif
#endif
+#ifndef RUBY_EXTERN
+#define RUBY_EXTERN extern
+#endif
+
#ifndef EXTERN
-#define EXTERN extern
+#define EXTERN RUBY_EXTERN /* deprecated */
#endif
#if defined(sparc) || defined(__sparc__)
-# if defined(linux) || defined(__linux__)
-#define FLUSH_REGISTER_WINDOWS asm("ta 0x83")
-# else /* Solaris, not sparc linux */
-#define FLUSH_REGISTER_WINDOWS asm("ta 0x03")
+static inline void
+flush_register_windows(void)
+{
+# if defined(__sparc_v9__) || defined(__arch64__)
+ asm volatile ("flushw" : :);
+# elif defined(linux) || defined(__linux__)
+ asm volatile ("ta 0x83");
+# else /* Solaris, OpenBSD, NetBSD, etc. */
+ asm volatile ("ta 0x03");
# endif /* trap always to flush register windows if we are on a Sparc system */
-#else /* Not a sparc, so */
-#define FLUSH_REGISTER_WINDOWS /* empty -- nothing to do here */
+}
+# define FLUSH_REGISTER_WINDOWS flush_register_windows()
+#else
+# define FLUSH_REGISTER_WINDOWS ((void)0)
#endif
#if defined(DOSISH)
@@ -157,6 +162,16 @@ void xfree _((void*));
#define PATH_SEP_CHAR PATH_SEP[0]
#if defined(__human68k__)
+#define PATH_ENV "path"
+#else
+#define PATH_ENV "PATH"
+#endif
+
+#if defined(DOSISH) || !defined(__human68k__)
+#define ENV_IGNORECASE
+#endif
+
+#if defined(__human68k__)
#undef HAVE_RANDOM
#undef HAVE_SETITIMER
#endif
diff --git a/dir.c b/dir.c
index 779407131e..7a188cacbb 100644
--- a/dir.c
+++ b/dir.c
@@ -6,7 +6,7 @@
$Date$
created at: Wed Jan 5 09:51:01 JST 1994
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
Copyright (C) 2000 Information-technology Promotion Agency, Japan
@@ -82,9 +82,9 @@ char *strchr _((char*,char));
#if defined DOSISH
#define isdirsep(c) ((c) == '/' || (c) == '\\')
-static char *
+static const char *
find_dirsep(s)
- char *s;
+ const char *s;
{
while (*s) {
if (isdirsep(*s))
@@ -248,6 +248,7 @@ free_dir(dir)
static VALUE dir_close _((VALUE));
+static VALUE dir_s_alloc _((VALUE));
static VALUE
dir_s_alloc(klass)
VALUE klass;
@@ -492,8 +493,12 @@ static VALUE
dir_s_getwd(dir)
VALUE dir;
{
- char *path = my_getcwd();
- VALUE cwd = rb_tainted_str_new2(path);
+ char *path;
+ VALUE cwd;
+
+ rb_secure(4);
+ path = my_getcwd();
+ cwd = rb_tainted_str_new2(path);
free(path);
return cwd;
@@ -762,7 +767,7 @@ glob_helper(path, sub, flags, func, arg)
free(magic);
break;
}
-
+
#if defined DOSISH_DRIVE_LETTER
#define BASE (*base && !((isdirsep(*base) && !base[1]) || (base[1] == ':' && isdirsep(base[2]) && !base[3])))
#else
@@ -777,6 +782,7 @@ glob_helper(path, sub, flags, func, arg)
sprintf(buf, "%s%s%s", base, (BASE) ? "/" : "", dp->d_name);
if (lstat(buf, &st) < 0) {
if (errno != ENOENT) rb_sys_warning(buf);
+ free(buf);
continue;
}
if (S_ISDIR(st.st_mode)) {
@@ -923,7 +929,7 @@ push_braces(ary, s, flags)
p++;
}
- if (lbrace) {
+ if (lbrace && rbrace) {
int len = strlen(s);
buf = xmalloc(len + 1);
memcpy(buf, s, lbrace-s);
@@ -1074,7 +1080,7 @@ Init_Dir()
rb_include_module(rb_cDir, rb_mEnumerable);
- rb_define_singleton_method(rb_cDir, "allocate", dir_s_alloc, 0);
+ rb_define_alloc_func(rb_cDir, dir_s_alloc);
rb_define_singleton_method(rb_cDir, "open", dir_s_open, 1);
rb_define_singleton_method(rb_cDir, "foreach", dir_foreach, 1);
rb_define_singleton_method(rb_cDir, "entries", dir_entries, 1);
diff --git a/djgpp/GNUmakefile.in b/djgpp/GNUmakefile.in
new file mode 100644
index 0000000000..0a7e1fb131
--- /dev/null
+++ b/djgpp/GNUmakefile.in
@@ -0,0 +1,2 @@
+include Makefile
+VPATH = $(srcdir) $(srcdir)/missing
diff --git a/djgpp/config.status b/djgpp/config.status
deleted file mode 100644
index 7a10754d1d..0000000000
--- a/djgpp/config.status
+++ /dev/null
@@ -1,77 +0,0 @@
-/^SHELL/s,/bin/sh,$(COMPSEC),
-s%@srcdir@%.%g
-s%@top_srcdir@%..%
-s%@CFLAGS@%-O2%g
-s%@CPPFLAGS@%%g
-s%@CXXFLAGS@%%g
-s%@LDFLAGS@%%g
-s%@LIBS@%-lm %g
-s%@exec_prefix@%${prefix}%g
-s%@prefix@%/usr/local%g
-s%@program_transform_name@%s,x,x,%g
-s%@bindir@%${exec_prefix}/bin%g
-s%@sbindir@%${exec_prefix}/sbin%g
-s%@libexecdir@%${exec_prefix}/libexec%g
-s%@datadir@%${prefix}/share%g
-s%@sysconfdir@%${prefix}/etc%g
-s%@sharedstatedir@%${prefix}/com%g
-s%@localstatedir@%${prefix}/var%g
-s%@libdir@%${exec_prefix}/lib%g
-s%@includedir@%${prefix}/include%g
-s%@oldincludedir@%/usr/include%g
-s%@infodir@%${prefix}/info%g
-s%@mandir@%${prefix}/man%g
-s%@host@%i386-pc-msdosdjgpp%g
-s%@host_alias@%i386-msdosdjgpp%g
-s%@host_cpu@%i386%g
-s%@host_vendor@%pc%g
-s%@host_os@%msdosdjgpp%g
-s%@CC@%gcc%g
-s%@CPP@%gcc -E%g
-s%@YACC@%bison -y%g
-s%@RANLIB@%ranlib%g
-s%@AR@%ar%g
-s%@INSTALL_PROGRAM@%${INSTALL}%g
-s%@INSTALL_DATA@%${INSTALL} -m 644%g
-s%@SET_MAKE@%%g
-s%@LIBOBJS@% crypt.o flock.o vsnprintf.o%g
-s%@ALLOCA@%%g
-s%@DEFAULT_KCODE@%%g
-s%@EXEEXT@%.exe%g
-s%@OBJEXT@%o%g
-s%@XLDFLAGS@%%g
-s%@DLDFLAGS@%%g
-s%@STATIC@%%g
-s%@CCDLFLAGS@%%g
-s%@LDSHARED@%ld%g
-s%@DLEXT@%o%g
-s%@STRIP@%strip%g
-s%@EXTSTATIC@%%g
-s%@binsuffix@%.exe%g
-s%@setup@%Setup.dj%g
-s%@LIBRUBY@%libruby.a%g
-s%@LIBRUBY_A@%libruby.a%g
-s%@LIBRUBYARG@%libruby.a%g
-s%@LIBRUBY_SO@%%g
-s%@SOLIBS@%%g
-s%@arch@%i386-msdosdjgpp%g
-;s%/bin/rm%rm%
-s%@DLDLIBS@%-lc%g
-s%@PREP@%%
-s%@RUBY_INSTALL_NAME@%ruby%g
-s%@RUBY_SO_NAME@%%g
-s%@arch@%i386-msdosdjgpp%g
-s%@sitedir@%${prefix}/lib/ruby/site_ruby%g
-s%@MINIRUBY@%./miniruby%
-s%@archlib@%/usr/local/lib/ruby/i386-msdosdjgpp%
-;s%|| true%%
-;/\/dev\/null/ {
-;s,/dev/null 2>&1, nul,
-;s,2> /dev/null,,
-;}
-;/^config.status/ {
-; N;N;N;N;N;d
-;}
-;s%y\.tab\.c%y_tab.c%
-/^,THIS_IS_DUMMY_PATTERN_/i\
-ac_given_srcdir=.
diff --git a/dln.c b/dln.c
index 55b751223b..eb907cbef2 100644
--- a/dln.c
+++ b/dln.c
@@ -6,7 +6,7 @@
$Date$
created at: Tue Jan 18 17:05:06 JST 1994
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
**********************************************************************/
@@ -31,7 +31,7 @@ char *dln_argv0;
#pragma alloca
#endif
-#if defined(HAVE_ALLOCA_H) && !defined(__GNUC__)
+#if defined(HAVE_ALLOCA_H)
#include <alloca.h>
#endif
@@ -97,7 +97,7 @@ int eaccess();
#endif
#ifndef FUNCNAME_PATTERN
-# if defined(__hp9000s300) || (defined(__NetBSD__) && !defined(__ELF__)) || defined(__BORLANDC__) || (defined(__FreeBSD__) && !defined(__ELF__)) || defined(__OpenBSD__) || defined(NeXT) || defined(__WATCOMC__) || defined(__APPLE__)
+# if defined(__hp9000s300) || (defined(__NetBSD__) && !defined(__ELF__)) || defined(__BORLANDC__) || (defined(__FreeBSD__) && !defined(__ELF__)) || (defined(__OpenBSD__) && !defined(__ELF__)) || defined(NeXT) || defined(__WATCOMC__) || defined(__APPLE__)
# define FUNCNAME_PATTERN "_Init_%s"
# else
# define FUNCNAME_PATTERN "Init_%s"
@@ -107,17 +107,18 @@ int eaccess();
static int
init_funcname_len(buf, file)
char **buf;
- char *file;
+ const char *file;
{
- char *p, *slash;
+ char *p;
+ const char *slash;
int len;
/* Load the file as an object one */
- for (p = file, slash = p-1; *p; p++) /* Find position of last '/' */
+ for (slash = file-1; *file; file++) /* Find position of last '/' */
#ifdef __MACOS__
- if (*p == ':') slash = p;
+ if (*file == ':') slash = file;
#else
- if (*p == '/') slash = p;
+ if (*file == '/') slash = file;
#endif
len = strlen(FUNCNAME_PATTERN) + strlen(slash + 1);
@@ -674,7 +675,7 @@ load_1(fd, disp, need_init)
char *key = sym->n_un.n_name;
if (st_lookup(sym_tbl, sym[1].n_un.n_name, &old_sym)) {
- if (st_delete(undef_tbl, &key, NULL)) {
+ if (st_delete(undef_tbl, (st_data_t*)&key, NULL)) {
unlink_undef(key, old_sym->n_value);
free(key);
}
@@ -687,7 +688,7 @@ load_1(fd, disp, need_init)
st_foreach(reloc_tbl, reloc_repl, &data);
st_insert(undef_tbl, strdup(sym[1].n_un.n_name), NULL);
- if (st_delete(undef_tbl, &key, NULL)) {
+ if (st_delete(undef_tbl, (st_data_t*)&key, NULL)) {
free(key);
}
}
@@ -755,7 +756,7 @@ load_1(fd, disp, need_init)
}
key = sym->n_un.n_name;
- if (st_delete(undef_tbl, &key, NULL) != 0) {
+ if (st_delete(undef_tbl, (st_data_t*)&key, NULL) != 0) {
unlink_undef(key, sym->n_value);
free(key);
}
@@ -1590,11 +1591,7 @@ dln_find_exe(fname, path)
const char *path;
{
if (!path) {
-#if defined(__human68k__)
- path = getenv("path");
-#else
- path = getenv("PATH");
-#endif
+ path = getenv(PATH_ENV);
}
if (!path) {
@@ -1665,6 +1662,7 @@ dln_find_1(fname, path, exe_flag)
const char* mac_fullpath;
#endif
+ if (!fname) return fname;
if (fname[0] == '/') return fname;
if (strncmp("./", fname, 2) == 0 || strncmp("../", fname, 3) == 0)
return fname;
@@ -1740,7 +1738,7 @@ dln_find_1(fname, path, exe_flag)
*bp = '\0';
fprintf(stderr, "\tDirectory \"%s\"\n", fbuf);
fprintf(stderr, "\tFile \"%s\"\n", fname);
- continue;
+ goto next;
}
memcpy(bp, fname, i + 1);
@@ -1794,10 +1792,13 @@ dln_find_1(fname, path, exe_flag)
#else
if (mac_fullpath = _macruby_exist_file_in_libdir_as_posix_name(fbuf))
return mac_fullpath;
+
#endif
}
}
#endif /* MSDOS or _WIN32 or __human68k__ or __EMX__ */
+
+ next:
/* if not, and no other alternatives, life is bleak */
if (*ep == '\0') {
return NULL;
diff --git a/dln.h b/dln.h
index adef813953..182cf9f9f4 100644
--- a/dln.h
+++ b/dln.h
@@ -6,7 +6,7 @@
$Date$
created at: Wed Jan 19 16:53:09 JST 1994
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
**********************************************************************/
diff --git a/doc/NEWS b/doc/NEWS
index 5aec22ac42..d45f4248d5 100644
--- a/doc/NEWS
+++ b/doc/NEWS
@@ -1,5 +1,93 @@
This file is not actively maintained. See ChangeLog for recent changes.
+: -W option
+
+ new option to specify warning level. -W0 to shut up warnings, -W1 for normal level,
+ -W2 for verbose level. -w equals to -W2.
+
+: Marshal to use marshal_dump and marshal_load
+
+ if a dumping object responds to 'marshal_dump', Marshal.dump calls
+ it, and dumps object returned. Marshal.load allocates a new instance
+ using "allocate", then calls its "marshal_load" with dumped data.
+
+: lib/un
+
+ Imported. Used like 'ruby -run -e cp -- -p foo bar'. Nest, isn't it?
+
+: lib/webrick
+
+ Imported. Generic Internet server kit.
+
+: $stdin, $stdout, $stderr
+
+ can be assignable again. the original stdio are preserved as STDIN,
+ STDOUT, STDERR.
+
+: multiple Tk interpreter
+
+ to allow safe Tk, etc.
+
+: ext/openssl
+
+ Imported.
+
+: ext/io/wait
+
+ Imported.
+
+: ext/bigdecimal
+
+ Imported.
+
+: Thread#group
+
+ new method to get belonging ThreadGroup.
+
+: Kernel#warn(message)
+
+ a method to give warnings.
+
+: Process::detach(pid)
+
+ new method to detach child process. child process will be "wait"ed
+ automagically.
+
+: Object#instance_variable_set, Object#instance_variable_get
+
+ added.
+
+: ext/Win32API/lib/win32/registry
+
+ added.
+
+: lib/open-uri
+
+ Imported. This is an easy-to-use wrapper for net/http and net/ftp.
+
+: lib/tmpdir
+
+ imported. add Dir::tmpdir() to determine the temporary directory.
+
+: lib/cgi.rb
+
+ cgi[name] returns CGI::QueryExtension::Value that wraps string
+ value, no longer array.
+
+: ext/syck
+: lib/yaml
+
+ Imported.
+
+: lib/rexml
+
+ Imported.
+
+: lib/xmlrpc
+: lib/gserver
+
+ Imported
+
: Class#inherited
Method is called when Class is inherited by another class.
@@ -12,6 +100,11 @@ This file is not actively maintained. See ChangeLog for recent changes.
Prints out "A inherited by B"
+: String#split
+
+ if "sep" argument is a string, regular expression meta characters
+ are escaped internally.
+
: String#to_i
Now accepts optional base argument.
@@ -125,11 +218,11 @@ This file is not actively maintained. See ChangeLog for recent changes.
Imported. Racc runtime library. (Racc is a parser generator for ruby)
-: tsort module
+: lib/tsort
Imported. Topological sorting library.
-: stringio module
+: ext/stringio
Imported. Pseudo (({IO})) class from/to (({String})).
@@ -377,20 +470,6 @@ This file is not actively maintained. See ChangeLog for recent changes.
Fixed with loading modules.
-: MatchData#to_ary
-
- Added for convenience of Regexp#match. [ruby-dev:12766]
-
- Previously we had to do:
-
- foo, bar, baz = /(\w+?)\s+(\w+?)\s+(\w+)/.match("foo bar baz").to_a[1..-1]
- p [foo, bar, baz]
-
- But now can do:
-
- _, foo, bar, baz = /(\w+?)\s+(\w+?)\s+(\w+)/.match("foo bar baz")
- p [foo, bar, baz]
-
: Math.acos(x)
: Math.asin(x)
: Math.atan(x)
@@ -452,11 +531,6 @@ This file is not actively maintained. See ChangeLog for recent changes.
Added.
-: Proc#yield
-
- Added. This is equivalent to Proc#call except it does not check the
- number of given arguments, which are thus passed to the proc as-is.
-
: Process.times
Moved from Time.times. (Time.times still remains but emits a
@@ -474,12 +548,6 @@ This file is not actively maintained. See ChangeLog for recent changes.
Added.
-: Range#to_ary
-
- Added. You can now do something like this:
-
- a, b, c = 1..3
-
: Regexp#options
Added.
@@ -582,12 +650,3 @@ This file is not actively maintained. See ChangeLog for recent changes.
Made to return "UTC" under gmtime. It used to return a platform
dependent value, typically "GMT", in 1.6 and prior.
-
-To be investigated:
-
- Sat Feb 24 03:15:49 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
-
- * io.c (set_stdin): preserve original stdin.
-
- * io.c (set_outfile): preserve original stdout/stderr.
-
diff --git a/doc/irb/irb.rd b/doc/irb/irb.rd
index c1ac367919..a42cd46680 100644
--- a/doc/irb/irb.rd
+++ b/doc/irb/irb.rd
@@ -221,7 +221,7 @@ For irb commands, both simple name and `irb_'-prefixed name are prepared.
Whether readline is used or not.
true: uses
false: doen't use
- nil: intends to use readline except for inf-reuby-mode (default)
+ nil: intends to use readline except for inf-ruby-mode (default)
#
#--- conf.verbose=T/F
# Whether verbose messages are display or not.
diff --git a/doc/irb/irb.rd.ja b/doc/irb/irb.rd.ja
index 7e80c7fc6f..bf8ac5d517 100644
--- a/doc/irb/irb.rd.ja
+++ b/doc/irb/irb.rd.ja
@@ -53,7 +53,7 @@ irb¤Î»È¤¤Êý¤Ï, Ruby¤µ¤¨ÃΤäƤ¤¤ì¤Ð¤¤¤¿¤Ã¤Æ´Êñ¤Ç¤¹. ´ðËÜŪ¤Ë¤Ï irb ¤È
--noinspect ·ë²Ì½ÐÎϤËinspect¤òÍѤ¤¤Ê¤¤.
--readline readline¥é¥¤¥Ö¥é¥ê¤òÍøÍѤ¹¤ë.
--noreadline readline¥é¥¤¥Ö¥é¥ê¤òÍøÍѤ·¤Ê¤¤. ¥Ç¥Õ¥©¥ë¥È¤Îưºî¤Ï,
- inf-reuby-mode°Ê³°¤Çreadline¥é¥¤¥Ö¥é¥ê¤òÍøÍѤ·¤è¤¦
+ inf-ruby-mode°Ê³°¤Çreadline¥é¥¤¥Ö¥é¥ê¤òÍøÍѤ·¤è¤¦
¤È¤¹¤ë.
--prompt prompt-mode
--prompt-mode prompt-mode
@@ -232,7 +232,7 @@ irb³ÈÄ¥¥³¥Þ¥ó¥É¤Ï, ´Êñ¤Ê̾Á°¤ÈƬ¤Ë`irb_'¤ò¤Ä¤±¤¿Ì¾Á°¤ÈξÊýÄêµÁ¤µ¤ì¤Æ
readline¤ò»È¤¦¤«¤É¤¦¤«?
true: readline¤ò»È¤¦.
false: readline¤ò»È¤ï¤Ê¤¤.
- nil: (¥Ç¥Õ¥©¥ë¥È)inf-reuby-mode°Ê³°¤Çreadline¥é¥¤¥Ö¥é¥ê¤òÍøÍѤ·¤è
+ nil: (¥Ç¥Õ¥©¥ë¥È)inf-ruby-mode°Ê³°¤Çreadline¥é¥¤¥Ö¥é¥ê¤òÍøÍѤ·¤è
¤¦¤È¤¹¤ë.
#
#--- conf.verbose=T/F
@@ -277,7 +277,7 @@ irb³ÈÄ¥¥³¥Þ¥ó¥É¤Ï, ´Êñ¤Ê̾Á°¤ÈƬ¤Ë`irb_'¤ò¤Ä¤±¤¿Ì¾Á°¤ÈξÊýÄêµÁ¤µ¤ì¤Æ
--- _
Á°¤Î·×»»¤Î¼Â¹Ô·ë²Ì¤ò³Ð¤¨¤Æ¤¤¤ë(¥í¡¼¥«¥ëÊÑ¿ô).
-___ __
+--- __
¼Â¹Ô·ë²Ì¤ÎÍúÎò¤ò³Ð¤¨¤Æ¤¤¤ë.
__[line_no]¤Ç¡¢¤½¤Î¹Ô¤Ç¼Â¹Ô¤·¤¿·ë²Ì¤òÆÀ¤ë¤³¤È¤¬¤Ç¤­¤ë. line_no¤¬Éé¤Î
»þ¤Ë¤Ï¡¢ºÇ¿·¤Î·ë²Ì¤«¤é-line_noÁ°¤Î·ë²Ì¤òÆÀ¤ë¤³¤È¤¬¤Ç¤­¤ë.
diff --git a/doc/net/http.rd.ja b/doc/net/http.rd.ja
deleted file mode 100644
index 0e39329197..0000000000
--- a/doc/net/http.rd.ja
+++ /dev/null
@@ -1,502 +0,0 @@
-=begin
-
-= net/http.rb
-
-== ¤³¤Î¥é¥¤¥Ö¥é¥ê¤Ë¤Ä¤¤¤Æ
-
-ÈÆÍѥǡ¼¥¿Å¾Á÷¥×¥í¥È¥³¥ë HTTP version 1.1 ¤ò°·¤¦¥é¥¤¥Ö¥é¥ê¤Ç¤¹¡£
-¼ÂÁõ¤Ï [RFC2616] ((<URL:http://www.ietf.org/rfc/rfc2616.txt>)) ¤Ë
-´ð¤¤¤Æ¤¤¤Þ¤¹¡£
-
-== »ÈÍÑÎã
-
-=== ¥¦¥§¥Ö¥µ¡¼¥Ð¤«¤é¥É¥­¥å¥á¥ó¥È¤òÆÀ¤ë (GET)
-
- require 'net/http'
- Net::HTTP.start( 'some.www.server', 80 ) {|http|
- response = http.get('/index.html')
- puts response.body
- }
-
-¤Þ¤¿°Ê²¼¤ÏƱ¤¸°ÕÌ£¤Çû¤¯½ñ¤¤¤¿¤â¤Î¤Ç¤¹¡£
-
- require 'net/http'
- Net::HTTP.get_print 'some.www.server', '/index.html'
- # or
- Net::HTTP.get_print URI.parse('http://www.example.com/index.html')
-
-=== ¥Õ¥©¡¼¥à¤Î¾ðÊó¤òÁ÷¿®¤¹¤ë (POST)
-
- require 'net/http'
- Net::HTTP.start( 'some.www.server', 80 ) {|http|
- response = http.post('/cgi-bin/any.rhtml',
- 'querytype=subject&target=ruby')
- }
-
-=== ¥×¥í¥¯¥··Ðͳ¤Î¥¢¥¯¥»¥¹
-
-Net::HTTP ¤Î¥¯¥é¥¹¥á¥½¥Ã¥É Net::HTTP.Proxy ¤Ï¡¢¾ï¤Ë¥×¥í¥¯¥··Ðͳ¤Ç
-Àܳ¤¹¤ë¤è¤¦¤Êưºî¤ò¤¹¤ë¡¢¿·¤·¤¤¥¯¥é¥¹¤òºîÀ®¤·¤ÆÊÖ¤·¤Þ¤¹¡£¤³¤Î¥¯¥é¥¹¤Ï
-Net::HTTP ¤ò·Ñ¾µ¤·¤Æ¤¤¤ë¤Î¤Ç Net::HTTP ¤ÈÁ´¤¯Æ±¤¸¤è¤¦¤Ë»È¤¨¤Þ¤¹¡£
-
- require 'net/http'
-
- $proxy_addr = 'your.proxy.addr'
- $proxy_port = 8080
- :
- Net::HTTP::Proxy($proxy_addr, $proxy_port).start('some.www.server') {|http|
- # always connect to your.proxy.addr:8080
- :
- }
-
-¤Þ¤¿ Net::HTTP.Proxy ¤ÏÂè°ì°ú¿ô¤¬ nil ¤À¤È Net::HTTP ¼«¿È¤òÊÖ¤¹¤Î¤Ç
-¾å¤Î¥³¡¼¥É¤Î¤è¤¦¤Ë½ñ¤¤¤Æ¤ª¤±¤Ð¥×¥í¥¯¥·¤Ê¤·¤Î¾ì¹ç¤Ë¤âÂбþ¤Ç¤­¤Þ¤¹¡£
-
-=== ¥ê¥À¥¤¥ì¥¯¥È¤ËÂбþ¤¹¤ë
-
- require 'net/http'
-
- def read_uri( uri_str )
- response = Net::HTTP.get_response(URI.parse(uri_str))
- case response
- when Net::HTTPSuccess then response
- when Net::HTTPRedirection then read_uri(response['location'])
- else
- response.error!
- end
- end
-
- print read_uri('http://www.ruby-lang.org')
-
-HTTPSuccess ¤ä HTTPRedirection ¤Ï HTTPResponse ¥¯¥é¥¹¤Î²¼°Ì¥¯¥é¥¹¤Ç¤¹¡£
-HTTPResponse ¥ª¥Ö¥¸¥§¥¯¥È¤Ï¤½¤ì¤¾¤ì HTTP ¥ì¥¹¥Ý¥ó¥¹¤Î¥¹¥Æ¡¼¥¿¥¹¤Ë¤è¤Ã¤Æ
-°Û¤ë¥¯¥é¥¹¤Ë°¤·¤Æ¤ª¤ê¡¢¤½¤Î¥¯¥é¥¹¤Ç·ë²Ì¤òʬÎà¤Ç¤­¤Þ¤¹¡£¤É¤Î¤è¤¦¤Ê¥¯¥é¥¹¤¬
-ÍѰդµ¤ì¤Æ¤¤¤ë¤Î¤«¤Ë¤Ä¤¤¤Æ¤Ï¡ÖHTTP ¥ì¥¹¥Ý¥ó¥¹¥¯¥é¥¹·²¡×¤ÎÀá¤ò¸«¤Æ¤¯¤À¤µ¤¤¡£
-
-=== Basic ǧ¾Ú
-
- require 'net/http'
-
- req = Net::HTTP::Get.new('/need-auth.cgi')
- req.basic_auth 'account', 'password'
- Net::HTTP.start( 'auth.some.domain' ) {|http|
- response = http.request(req)
- print response.body
- }
-
-=== HTTP ¥ì¥¹¥Ý¥ó¥¹¥¯¥é¥¹·²
-
-°Ê²¼¤Ë HTTP 1.1 ¤Î¥ê¥¶¥ë¥È¥³¡¼¥É¤È¤½¤ì¤ËÂбþ¤¹¤ë¥ì¥¹¥Ý¥ó¥¹¥¯¥é¥¹¤ò
-¼¨¤·¤Þ¤¹¡£¥¯¥é¥¹¤Ï¤¹¤Ù¤Æ Net ¥â¥¸¥å¡¼¥ë¤ÎÃæ¤ÇÄêµÁ¤µ¤ì¤Æ¤ª¤ê¡¢
-¥¤¥ó¥Ç¥ó¥È¤¬·Ñ¾µ´Ø·¸¤òɽ¤ï¤·¤Æ¤¤¤Þ¤¹¡£
-
- 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
-
-== ¿·¤·¤¤»ÅÍͤؤÎÊѹ¹¤È°Ü¹ÔÁ¼Ã֤ˤĤ¤¤Æ
-
-Ruby 1.6 ¤Ë¤Ï http.rb 1.1¡¢Ruby 1.7 ¤Ë¤Ï http.rb 1.2 ¤¬ÅºÉÕ
-¤µ¤ì¤Æ¤¤¤Þ¤¹¤¬¡¢¤³¤Î´Ö¤Ç¤Ï¤«¤Ê¤êÂ礭¤¯»ÅÍͤ¬ÊѤï¤Ã¤Æ¤¤¤Þ¤¹¡£
-¤½¤³¤ÇÆÍÁ³¤Ë»ÅÍͤòÊѹ¹¤¹¤ë¤Î¤Ç¤Ê¤¯¡¢Î¾Êý¤Î¼ÂÁõ¤òʸ¤µ¤»¤ë
-»þ´ü¤òÀߤ±¤ë¤³¤È¤Ë¤·¤Þ¤·¤¿¡£
-
-¥á¥½¥Ã¥É HTTP.version_1_2¡¢HTTP.version_1_1 ¤ò¸Æ¤Ö¤È
-¤½¤Î¤¢¤È¤ËÀ¸À®¤µ¤ì¤ë Net::HTTP ¥ª¥Ö¥¸¥§¥¯¥È¤Ï¤½¤ì¤¾¤ì¤Î
-¥Ð¡¼¥¸¥ç¥ó¤Î»ÅÍÍ¤ÇÆ°ºî¤¹¤ë¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹¡£°Ê²¼¤Ï»ÈÍÑÎã¤Ç¤¹¡£
-
- # 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)... }
-
-¤³¤Îµ¡Ç½¤Ï¥¹¥ì¥Ã¥É¥»¡¼¥Õ¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó¡£
-
-== class Net::HTTP
-
-=== ¥¯¥é¥¹¥á¥½¥Ã¥É
-
-: new( address, port = 80, proxy_addr = nil, proxy_port = nil )
- ¿·¤·¤¤ HTTP ¥ª¥Ö¥¸¥§¥¯¥È¤òÀ¸À®¤·¤Þ¤¹¡£address ¤Ï HTTP ¥µ¡¼¥Ð¡¼¤Î FQDN ¤Ç¡¢
- port ¤ÏÀܳ¤¹¤ë¥Ý¡¼¥ÈÈÖ¹æ¤Ç¤¹¡£¤³¤Î¥á¥½¥Ã¥É¤Ç¤Ï¤Þ¤ÀÀܳ¤Ï¤·¤Þ¤»¤ó¡£
-
- proxy_addr ¤òÍ¿¤¨¤ë¤È¥×¥í¥¯¥·¤ò²ð¤·¤ÆÀܳ¤¹¤ë¥ª¥Ö¥¸¥§¥¯¥È¤òÀ¸À®¤·¤Þ¤¹¡£
-
-: start( address, port = 80, proxy_addr = nil, proxy_port = nil )
- ¿·¤·¤¤ Net::HTTP ¥ª¥Ö¥¸¥§¥¯¥È¤òÀ¸À®¤· HTTP ¥»¥Ã¥·¥ç¥ó¤ò
- ³«»Ï¤·¤¿¤¦¤¨¤ÇÊÖ¤·¤Þ¤¹¡£
-
-: start( address, port = 80, proxy_addr = nil, proxy_port = nil ) {|http| .... }
- ¿·¤·¤¤ Net::HTTP ¥ª¥Ö¥¸¥§¥¯¥È¤òÀ¸À®¤·¥Ö¥í¥Ã¥¯¤ËÅϤ·¤Þ¤¹¡£
- ¥Ö¥í¥Ã¥¯¼Â¹ÔÃæ¤Î¤ß HTTP ¥»¥Ã¥·¥ç¥ó¤ò°Ý»ý¤·¤Þ¤¹¡£
-
- ¥Ö¥í¥Ã¥¯¤ÎÊÖ¤êÃͤò¤½¤Î¤Þ¤ÞÊÖ¤·¤Þ¤¹¡£
-
-: get_print( uri )
-: get_print( address, path, port = 80 )
- uri ¤Þ¤¿¤Ï address path port ¤Ç»ØÄꤵ¤ì¤¿¥¨¥ó¥Æ¥£¥Æ¥£¤ò
- ¼èÆÀ¤· stdout ¤Ë½ÐÎϤ·¤Þ¤¹¡£
-
- Net::HTTP.get_print URI.parse('http://www.example.com')
-
-: get( uri )
-: get( address, path, port = 80 )
- uri ¤Þ¤¿¤Ï address path port ¤Ç»ØÄꤵ¤ì¤¿¥¨¥ó¥Æ¥£¥Æ¥£¤ò
- ¼èÆÀ¤·Ê¸»úÎó¤ÇÊÖ¤·¤Þ¤¹¡£
-
- print Net::HTTP.get(URI.parse('http://www.example.com'))
-
-: get_response( uri )
-: get_response( address, path, port = 80 )
- uri ¤Þ¤¿¤Ï address path port ¤Ç»ØÄꤵ¤ì¤¿¥¨¥ó¥Æ¥£¥Æ¥£¤ò
- ¼èÆÀ¤· Net::HTTPResponse ¥ª¥Ö¥¸¥§¥¯¥È¤ÇÊÖ¤·¤Þ¤¹¡£
-
- res = Net::HTTP.get_response(URI.parse('http://www.example.com'))
- print res.body
-
-: Proxy( address, port = 80 )
- ¾ï¤Ë»ØÄꤵ¤ì¤¿¥×¥í¥¯¥·¤ËÀܳ¤¹¤ë¥¯¥é¥¹¤òºîÀ®¤·ÊÖ¤·¤Þ¤¹¡£
- ¤³¤Î¥¯¥é¥¹¤Ï Net::HTTP ¤ò·Ñ¾µ¤·¤Æ¤¤¤ë¤Î¤Ç Net::HTTP ¤ÈÁ´¤¯
- Ʊ¤¸¤è¤¦¤Ë»È¤¨¤Þ¤¹¡£
-
- address ¤¬ nil ¤Î¤È¤­¤Ï Net::HTTP ¥¯¥é¥¹¤ò¤½¤Î¤Þ¤ÞÊÖ¤·¤Þ¤¹¡£
-
- # example
- proxy_class = Net::HTTP::Proxy( 'proxy.foo.org', 8080 )
- :
- proxy_class.start( 'www.ruby-lang.org' ) {|http|
- # connecting proxy.foo.org:8080
- :
- }
-
-: proxy_class?
- ¼«¿È¤¬ (Proxy ¥á¥½¥Ã¥É¤Ë¤è¤Ã¤ÆºîÀ®¤µ¤ì¤¿) ¥×¥í¥¯¥·ÍѤΥ¯¥é¥¹¤Ê¤é¤Ð¿¿¡£
-
-: port
- HTTP ¤Î¥Ç¥Õ¥©¥ë¥È¥Ý¡¼¥È (80)¡£
-
-=== ¥á¥½¥Ã¥É
-
-: start
-: start {|http| .... }
- TCP ¥³¥Í¥¯¥·¥ç¥ó¤òÄ¥¤ê HTTP ¥»¥Ã¥·¥ç¥ó¤ò³«»Ï¤·¤Þ¤¹¡£
- ¤¹¤Ç¤Ë¥»¥Ã¥·¥ç¥ó¤¬³«»Ï¤·¤Æ¤¤¤¿¤éÎã³° IOError ¤òȯÀ¸¤·¤Þ¤¹¡£
-
- ¥¤¥Æ¥ì¡¼¥¿¤È¤·¤Æ¸Æ¤Ð¤ì¤¿»þ¤Ï¥Ö¥í¥Ã¥¯¤Î´Ö¤À¤±¥»¥Ã¥·¥ç¥ó¤ò
- °Ý»ý¤·¡¢¥Ö¥í¥Ã¥¯½ªÎ»¤È¤È¤â¤Ë¼«Æ°Åª¤Ë¥»¥Ã¥·¥ç¥ó¤ò½ªÎ»¤·¤Þ¤¹¡£
-
-: started?
- HTTP ¥»¥Ã¥·¥ç¥ó¤¬³«»Ï¤µ¤ì¤Æ¤¤¤¿¤é¿¿¡£
-
-: address
- Àܳ¤¹¤ë¥¢¥É¥ì¥¹
-
-: port
- Àܳ¤¹¤ë¥Ý¡¼¥ÈÈÖ¹æ
-
-: open_timeout
-: open_timeout=(n)
- Àܳ»þ¤ËÂԤĺÇÂçÉÿô¡£¤³¤ÎÉÿô¤¿¤Ã¤Æ¤â¥³¥Í¥¯¥·¥ç¥ó¤¬
- ³«¤«¤Ê¤±¤ì¤ÐÎã³° TimeoutError ¤òȯÀ¸¤·¤Þ¤¹¡£
-
-: read_timeout
-: read_timeout=(n)
- ÆÉ¤ß¤³¤ß (read(1) °ì²ó) ¤Ç¥Ö¥í¥Ã¥¯¤·¤Æ¤è¤¤ºÇÂçÉÿô¡£
- ¤³¤ÎÉÿô¤¿¤Ã¤Æ¤âÆÉ¤ß¤³¤á¤Ê¤±¤ì¤ÐÎã³° TimeoutError ¤òȯÀ¸¤·¤Þ¤¹¡£
-
-: finish
- HTTP ¥»¥Ã¥·¥ç¥ó¤ò½ªÎ»¤·¤Þ¤¹¡£¥»¥Ã¥·¥ç¥ó³«»ÏÁ°¤Ë¤³¤Î¥á¥½¥Ã¥É¤¬
- ¸Æ¤Ð¤ì¤¿¾ì¹ç¤ÏÎã³° IOError ¤òȯÀ¸¤·¤Þ¤¹¡£
-
-: proxy?
- ¥×¥í¥¯¥·¤ò²ð¤·¤ÆÀܳ¤¹¤ë¤Ê¤é¿¿¡£
-
-: proxy_address
- ¥×¥í¥¯¥··Ðͳ¤ÇÀܳ¤¹¤ë HTTP ¥ª¥Ö¥¸¥§¥¯¥È¤Ê¤é¥×¥í¥¯¥·¤Î¥¢¥É¥ì¥¹¡£
- ¤½¤¦¤Ç¤Ê¤¤¤Ê¤é nil¡£
-
-: proxy_port
- ¥×¥í¥¯¥··Ðͳ¤ÇÀܳ¤¹¤ë HTTP ¥ª¥Ö¥¸¥§¥¯¥È¤Ê¤é¥×¥í¥¯¥·¤Î¥Ý¡¼¥È¡£
- ¤½¤¦¤Ç¤Ê¤¤¤Ê¤é nil¡£
-
-: get( path, header = nil )
-: get( path, header = nil ) {|str| .... }
- ¥µ¡¼¥Ð¾å¤Î path ¤Ë¤¢¤ë¥¨¥ó¥Æ¥£¥Æ¥£¤ò¼èÆÀ¤·¤Þ¤¹¡£¤Þ¤¿ header ¤¬ nil
- ¤Ç¤Ê¤±¤ì¤Ð¡¢¥ê¥¯¥¨¥¹¥È¤òÁ÷¤ë¤È¤­¤Ë¤½¤ÎÆâÍÆ¤ò HTTP ¥Ø¥Ã¥À¤È¤·¤Æ½ñ¤­
- ¤³¤ß¤Þ¤¹¡£header ¤Ï¥Ï¥Ã¥·¥å¤Ç¡¢¡Ö¥Ø¥Ã¥À̾ => ÆâÍÆ¡×¤Î¤è¤¦¤Ê·Á¼°¤Ç
- ¤Ê¤±¤ì¤Ð¤¤¤±¤Þ¤»¤ó¡£
-
- ÊÖ¤êÃͤϡ¢¥Ð¡¼¥¸¥ç¥ó 1.1 ¤Ç¤Ï HTTPResponse ¤È¥¨¥ó¥Æ¥£¥Æ¥£¥Ü¥Ç¥£Ê¸»úÎó¤Î
- ÆóÍ×ÁǤÎÇÛÎó¤Ç¤¹¡£1.2 ¤Ç¤Ï HTTPResponse ¤¿¤À¤Ò¤È¤Ä¤Î¤ß¤Ç¤¹¡£¤³¤Î¾ì¹ç¡¢
- ¥¨¥ó¥Æ¥£¥Æ¥£¥Ü¥Ç¥£¤Ï response.body ¤ÇÆÀ¤é¤ì¤Þ¤¹¡£
-
- ¥Ö¥í¥Ã¥¯¤È¤È¤â¤Ë¸Æ¤Ð¤ì¤¿»þ¤Ï¥¨¥ó¥Æ¥£¥Æ¥£¥Ü¥Ç¥£¤ò¾¯¤·¤Å¤Ä¥Ö¥í¥Ã¥¯¤Ë
- Í¿¤¨¤Þ¤¹¡£
-
- 1.1 ¤Ç¤Ï 3xx (ºÆ»î¹Ô²Äǽ¤Ê¥¨¥é¡¼)¤ËÂФ·¤Æ¤âÎã³°¤òȯÀ¸¤·¤Þ¤¹¡£¤³¤Î¾ì¹ç
- HTTPResponse ¤ÏÎã³°¥ª¥Ö¥¸¥§¥¯¥È¤«¤é err.response ¤ÇÆÀ¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
- °ìÊý 1.2 ¤Ç¤ÏÁ´¤¯Îã³°¤òȯÀ¸¤·¤Þ¤»¤ó¡£
-
- # version 1.1 (bundled with Ruby 1.6)
- response, body = http.get( '/index.html' )
-
- # version 1.2 (bundled with Ruby 1.7 or later)
- response = http.get( '/index.html' )
-
- # compatible in both version
- response , = http.get( '/index.html' )
- response.body
-
- # using block
- File.open( 'save.txt', 'w' ) {|f|
- http.get( '/~foo/', nil ) do |str|
- f.write str
- end
- }
-
-: head( path, header = nil )
- ¥µ¡¼¥Ð¾å¤Î path ¤Ë¤¢¤ë¥¨¥ó¥Æ¥£¥Æ¥£¤Î¥Ø¥Ã¥À¤Î¤ß¤ò¼èÆÀ¤·¤Þ¤¹¡£
- ¤Þ¤¿ header ¤¬ nil ¤Ç¤Ê¤±¤ì¤Ð¥ê¥¯¥¨¥¹¥È¤òÁ÷¤ë¤È¤­¤Ë¤½¤ÎÆâÍÆ¤ò
- HTTP ¥Ø¥Ã¥À¤È¤·¤Æ½ñ¤­¤³¤ß¤Þ¤¹¡£header ¤Ï¥Ï¥Ã¥·¥å¤Ç¡¢
- ¡Ö¥Ø¥Ã¥À̾ => ÆâÍÆ¡×¤Î¤è¤¦¤Ê·Á¼°¤Ç¤Ê¤±¤ì¤Ð¤¤¤±¤Þ¤»¤ó¡£
-
- HTTPResponse ¥ª¥Ö¥¸¥§¥¯¥È¤òÊÖ¤·¤Þ¤¹¡£
-
- 1.1 ¤Ç¤Ï 3xx (ºÆ»î¹Ô²Äǽ¤Ê¥¨¥é¡¼)¤ËÂФ·¤Æ¤âÎã³°¤òȯÀ¸¤·¤Þ¤¹¡£¤³¤Î¾ì¹ç
- HTTPResponse ¤ÏÎã³°¥ª¥Ö¥¸¥§¥¯¥È¤«¤é err.response ¤ÇÆÀ¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
- °ìÊý 1.2 ¤Ç¤ÏÁ´¤¯Îã³°¤òȯÀ¸¤·¤Þ¤»¤ó¡£
-
- response = nil
- Net::HTTP.start( 'some.www.server', 80 ) {|http|
- response = http.head( '/index.html' )
- }
- p response['content-type']
-
-: post( path, data, header = nil )
-: post( path, data, header = nil ) {|str| .... }
- ¥µ¡¼¥Ð¾å¤Î path ¤Ë¤¢¤ë¥¨¥ó¥Æ¥£¥Æ¥£¤ËÂФ·Ê¸»úÎó data ¤ò
- Á÷¤ê¤Þ¤¹¡£¥ì¥¹¥Ý¥ó¥¹¤Ï << ¥á¥½¥Ã¥É¤ò»È¤Ã¤Æ dest ¤Ë½ñ¤­
- ¤³¤Þ¤ì¤Þ¤¹¡£header ¤Ï get ¥á¥½¥Ã¥É¤ÈƱ¤¸¤Ç¤¹¡£
- HTTPResponse ¥ª¥Ö¥¸¥§¥¯¥È¤È dest ¤ÎÇÛÎó¤òÊÖ¤·¤Þ¤¹¡£
-
- ¥¤¥Æ¥ì¡¼¥¿¤È¤·¤Æ¸Æ¤Ó¤À¤µ¤ì¤¿¤È¤­¤Ï¥¨¥ó¥Æ¥£¥Æ¥£¥Ü¥Ç¥£¤ò¾¯¤·¤Å¤Ä
- ¥Ö¥í¥Ã¥¯¤ËÍ¿¤¨¤Þ¤¹¡£
-
- 1.1 ¤Ç¤Ï 3xx (ºÆ»î¹Ô²Äǽ¤Ê¥¨¥é¡¼)¤ËÂФ·¤Æ¤âÎã³°¤òȯÀ¸¤·¤Þ¤¹¡£¤³¤Î¾ì¹ç
- HTTPResponse ¤ÏÎã³°¥ª¥Ö¥¸¥§¥¯¥È¤«¤é err.response ¤ÇÆÀ¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
- °ìÊý 1.2 ¤Ç¤ÏÁ´¤¯Îã³°¤òȯÀ¸¤·¤Þ¤»¤ó¡£
-
- # version 1.1
- response, body = http.post( '/cgi-bin/search.rb', 'query=subject&target=ruby' )
-
- # version 1.2
- response = http.post( '/cgi-bin/search.rb', 'query=subject&target=ruby' )
-
- # compatible in both version
- response , = http.post( '/cgi-bin/search.rb', 'query=subject&target=ruby' )
-
- # using block
- File.open( 'save.html', 'w' ) {|f|
- http.post( '/cgi-bin/search.rb',
- 'query=subject&target=ruby' ) do |str|
- f.write str
- end
- }
-
-: request_get( path, header = nil )
-: request_get( path, header = nil ) {|response| .... }
- path ¤Ë¤¢¤ë¥¨¥ó¥Æ¥£¥Æ¥£¤ò¼èÆÀ¤·¤Þ¤¹¡£HTTPResponse
- ¥ª¥Ö¥¸¥§¥¯¥È¤òÊÖ¤·¤Þ¤¹¡£
-
- ¥Ö¥í¥Ã¥¯¤È¤È¤â¤Ë¸Æ¤Ó½Ð¤µ¤ì¤¿¤È¤­¤Ï¡¢¥Ö¥í¥Ã¥¯¼Â¹ÔÃæ¤ÏÀܳ¤ò
- °Ý»ý¤·¤¿¤Þ¤Þ HTTPResponse ¥ª¥Ö¥¸¥§¥¯¥È¤ò¥Ö¥í¥Ã¥¯¤ËÅϤ·¤Þ¤¹¡£
-
- ¤³¤Î¥á¥½¥Ã¥É¤Ï HTTP ¥×¥í¥È¥³¥ë¤Ë´ØÏ¢¤·¤¿Îã³°¤ÏȯÀ¸¤µ¤»¤Þ¤»¤ó¡£
-
- # example
- response = http.request_get( '/index.html' )
- p response['content-type']
- puts response.body # body is already read
-
- # using block
- http.request_get( '/index.html' ) {|response|
- p response['content-type']
- response.read_body do |str| # read body now
- print str
- end
- }
-
-: request_post( path, data, header = nil )
-: request_post( path, data, header = nil ) {|response| .... }
- path ¤Ë¤¢¤ë¥¨¥ó¥Æ¥£¥Æ¥£¤ò¼èÆÀ¤·¤Þ¤¹¡£HTTPResponse
- ¥ª¥Ö¥¸¥§¥¯¥È¤òÊÖ¤·¤Þ¤¹¡£
-
- ¥Ö¥í¥Ã¥¯¤È¤È¤â¤Ë¸Æ¤Ó½Ð¤µ¤ì¤¿¤È¤­¤Ï¡¢¥Ü¥Ç¥£¤òÆÉ¤ß¤³¤àÁ°¤Ë
- HTTPResponse ¥ª¥Ö¥¸¥§¥¯¥È¤ò¥Ö¥í¥Ã¥¯¤ËÅϤ·¤Þ¤¹¡£
-
- ¤³¤Î¥á¥½¥Ã¥É¤Ï HTTP ¥×¥í¥È¥³¥ë¤Ë´ØÏ¢¤·¤¿Îã³°¤ÏȯÀ¸¤µ¤»¤Þ¤»¤ó¡£
-
- # example
- response = http.post2( '/cgi-bin/nice.rb', 'datadatadata...' )
- p response.status
- puts response.body # body is already read
-
- # using block
- http.post2( '/cgi-bin/nice.rb', 'datadatadata...' ) {|response|
- p response.status
- p response['content-type']
- response.read_body do |str| # read body now
- print str
- end
- }
-
-: request( request [, data] )
-: request( request [, data] ) {|response| .... }
- HTTPResquest ¥ª¥Ö¥¸¥§¥¯¥È request ¤òÁ÷¿®¤·¤Þ¤¹¡£POST/PUT ¤Î»þ¤Ï data ¤â
- Í¿¤¨¤é¤ì¤Þ¤¹ (POST/PUT °Ê³°¤Ç data ¤òÍ¿¤¨¤ë¤È ArgumentError ¤òȯÀ¸¤·¤Þ¤¹)¡£
-
- ¥Ö¥í¥Ã¥¯¤È¤È¤â¤Ë¸Æ¤Ó¤À¤µ¤ì¤¿¤È¤­¤Ï¥Ü¥Ç¥£¤òÆÉ¤ß¤³¤Þ¤º¤Ë HTTPResponse
- ¥ª¥Ö¥¸¥§¥¯¥È¤ò¥Ö¥í¥Ã¥¯¤ËÍ¿¤¨¤Þ¤¹¡£
-
- ¤³¤Î¥á¥½¥Ã¥É¤Ï HTTP ¥×¥í¥È¥³¥ë¤Ë´ØÏ¢¤·¤¿Îã³°¤ÏȯÀ¸¤µ¤»¤Þ¤»¤ó¡£
-
-== class Net::HTTPRequest
-
-HTTP ¥ê¥¯¥¨¥¹¥È¤òÃê¾Ý²½¤¹¤ë¥¯¥é¥¹¡£¼ÂºÝ¤Ë¤Ï²¼°Ì¥¯¥é¥¹¤Î
-Net::HTTP::Get, Post, Head ¤ò»È¤¤¤Þ¤¹¡£
-
-=== ¥¯¥é¥¹¥á¥½¥Ã¥É
-
-: new
- HTTP ¥ê¥¯¥¨¥¹¥È¥ª¥Ö¥¸¥§¥¯¥È¤òÀ¸À®¤·¤Þ¤¹¡£
-
-=== ¥á¥½¥Ã¥É
-
-: self[ key ]
- key ¥Ø¥Ã¥À¥Õ¥£¡¼¥ë¥É¤Îʸ»úÎó¡£
- key ¤ÏÂçʸ»ú¾®Ê¸»ú¤ò¶èÊ̤·¤Þ¤»¤ó¡£
-
-: self[ key ] = val
- key ¥Ø¥Ã¥À¥Õ¥£¡¼¥ë¥É¤Ë val ¤ò¥»¥Ã¥È¤·¤Þ¤¹¡£
- key ¤ÏÂçʸ»ú¾®Ê¸»ú¤ò¶èÊ̤·¤Þ¤»¤ó¡£
-
-: each {|name, val| .... }
- ¥Ø¥Ã¥À̾¤È¤½¤ÎÃͤËÂФ¹¤ë¤¯¤ê¤«¤¨¤·¡£¥Ø¥Ã¥À̾¤Ï¾®Ê¸»ú¤ÇÅý°ì¤µ¤ì¤Þ¤¹¡£
-
-: basic_auth( account, password )
- Authrization: ¥Ø¥Ã¥À¤ò basic auth ÍѤ˥»¥Ã¥È¤·¤Þ¤¹¡£
-
-: range
- Range: ¥Ø¥Ã¥À¤Î¼¨¤¹ÈϰϤò Range ¥ª¥Ö¥¸¥§¥¯¥È¤ÇÊÖ¤·¤Þ¤¹¡£
-
-: range = r
-: set_range( i, len )
- ÈϰϤò»ØÄꤷ¤Æ¥¨¥ó¥Æ¥£¥Æ¥£¤ò¼èÆÀ¤¹¤ë¤¿¤á¤Î¥Ø¥Ã¥À Range: ¤ò¥»¥Ã¥È¤·¤Þ¤¹¡£
- r ¤Ï Range ¥ª¥Ö¥¸¥§¥¯¥È¡¢i, len ¤Ï»ÏÅÀ¤ÈŤµ¤Ç¤¹¡£
-
-: content_length
- Content-Length: ¥Ø¥Ã¥À¤ÎÃÍ (À°¿ô)¡£
-
-: content_range
- Content-Range: ¥Ø¥Ã¥À¤ÎÃÍ (Range)¡£
-
-== class Net::HTTPResponse
-
-HTTP ¥ì¥¹¥Ý¥ó¥¹¤Î¥¯¥é¥¹¤Ç¤¹¡£
-°ú¿ô¤¬¥Ø¥Ã¥À¥Õ¥£¡¼¥ë¥É̾¤Ç¤¢¤ë¾ì¹ç¡¢Âçʸ»ú¾®Ê¸»ú¤ò¶èÊ̤·¤Þ¤»¤ó¡£
-
-=== ¥á¥½¥Ã¥É
-
-: self[ key ]
- key ¥Ø¥Ã¥À¥Õ¥£¡¼¥ë¥É(ʸ»úÎó)¤Ç¤¹¡£¤¿¤È¤¨¤Ð¥­¡¼ 'content-length'
- ¤ËÂФ·¤Æ¤Ï '2048' ¤Î¤è¤¦¤Êʸ»úÎ󤬯À¤é¤ì¤Þ¤¹¡£
- key ¤ÏÂçʸ»ú¾®Ê¸»ú¤ò¶èÊ̤·¤Þ¤»¤ó¡£
-
-: self[ key ] = val
- key ¥Ø¥Ã¥À¥Õ¥£¡¼¥ë¥É¤ò value ¤ËÀßÄꤷ¤Þ¤¹¡£
- key ¤ÏÂçʸ»ú¾®Ê¸»ú¤ò¶èÊ̤·¤Þ¤»¤ó¡£
-
-: key?( key )
- key ¤È¤¤¤¦¥Ø¥Ã¥À¥Õ¥£¡¼¥ë¥É¤¬¤¢¤ì¤Ð¿¿¡£
- key ¤ÏÂçʸ»ú¾®Ê¸»ú¤ò¶èÊ̤·¤Þ¤»¤ó¡£
-
-: each {|name,value| .... }
- ¤¹¤Ù¤Æ¤Î¥Ø¥Ã¥À¥Õ¥£¡¼¥ë¥É̾¤È¤½¤ÎÃͤΥڥ¢¤ËÂФ¹¤ë¤¯¤ê¤«¤¨¤·¡£
-
-: canonical_each {|name,value| .... }
- ¥Ø¥Ã¥À¥Õ¥£¡¼¥ë¥É¤ÎÀµ¼°Ì¾¤È¤½¤ÎÃͤΥڥ¢¤ËÂФ·¤Æ·«¤êÊÖ¤·¤Þ¤¹¡£
-
-: code
- HTTP ¤Î¥ê¥¶¥ë¥È¥³¡¼¥É¤Ç¤¹¡£Î㤨¤Ð '302' ¤Ê¤É¤Ç¤¹¡£
-
-: message
- HTTP ¥µ¡¼¥Ð¤¬¥ê¥¶¥ë¥È¥³¡¼¥É¤ËÉղä·¤ÆÊÖ¤¹¥á¥Ã¥»¡¼¥¸¤Ç¤¹¡£
- Î㤨¤Ð 'Not Found' ¤Ê¤É¤Ç¤¹¡£
-
-: read_body( dest = '' )
- ¥¨¥ó¥Æ¥£¥Æ¥£¥Ü¥Ç¥£¤ò¼èÆÀ¤· dest ¤Ë << ¥á¥½¥Ã¥É¤ò»È¤Ã¤Æ½ñ¤­¤³¤ß¤Þ¤¹¡£
- Ʊ¤¸ HTTPResponse ¥ª¥Ö¥¸¥§¥¯¥È¤ËÂФ·¤ÆÆó²ó°Ê¾å¸Æ¤Ð¤ì¤¿¾ì¹ç¡¢
- Æó²óÌܤ«¤é¤Ï¤Ê¤Ë¤â¤»¤º¤Ë°ì²óÌܤÎÊÖ¤êÃͤò¤½¤Î¤Þ¤ÞÊÖ¤·¤Þ¤¹¡£
-
-: read_body {|str| .... }
- ¥¨¥ó¥Æ¥£¥Æ¥£¥Ü¥Ç¥£¤ò¾¯¤·¤Å¤Ä¼èÆÀ¤·¤Æ½ç¼¡¥Ö¥í¥Ã¥¯¤ËÍ¿¤¨¤Þ¤¹¡£
-
-: body
- ¥¨¥ó¥Æ¥£¥Æ¥£¥Ü¥Ç¥£¤Ç¤¹¡£read_body ¤ò¸Æ¤ó¤Ç¤¤¤ì¤Ð¤½¤Î°ú¿ô dest¡¢
- ¸Æ¤ó¤Ç¤¤¤Ê¤±¤ì¤Ð¥¨¥ó¥Æ¥£¥Æ¥£¥Ü¥Ç¥£¤òʸ»úÎó¤È¤·¤ÆÆÉ¤ß¤³¤ó¤ÇÊÖ¤·¤Þ¤¹¡£
-
-=end
diff --git a/doc/net/smtp.rd.ja b/doc/net/smtp.rd.ja
deleted file mode 100644
index 7a748947d7..0000000000
--- a/doc/net/smtp.rd.ja
+++ /dev/null
@@ -1,205 +0,0 @@
-=begin
-
-= net/smtp.rb
-
-== ¤³¤Î¥é¥¤¥Ö¥é¥ê¤Ë¤Ä¤¤¤Æ
-
-¥á¡¼¥ë¤òÁ÷¿®¤¹¤ë¤¿¤á¤Î¥×¥í¥È¥³¥ë SMTP (Simple Mail Transfer Protocol)
-¤ò°·¤¦¥é¥¤¥Ö¥é¥ê¤Ç¤¹¡£¥Ø¥Ã¥À¤Ê¤É¥á¡¼¥ë¤Î¥Ç¡¼¥¿¤ò°·¤¦¤³¤È¤Ï¤Ç¤­¤Þ¤»¤ó¡£
-SMTP ¤Î¼ÂÁõ¤Ï RFC2821 ¤Ë´ð¤¤¤Æ¤¤¤Þ¤¹¡£
-
- * RFC2821 ((<URL:http://www.ietf.org/rfc/rfc2821.txt>))
-
-== ¥á¡¼¥ë¤ÎÊÔ½¸¤Ë¤Ä¤¤¤Æ
-
-¤³¤Î¥é¥¤¥Ö¥é¥ê¤¬¤Ç¤­¤ë¤Î¤ÏÁ÷¿®¤À¤±¤Ç¤¹¡£¥á¡¼¥ë¥Ø¥Ã¥À¤ÎÊÔ½¸µ¡Ç½¤Ê¤É¤Ï
-¤¢¤ê¤Þ¤»¤ó¡£¹âÅÙ¤ÊÊÔ½¸µ¡Ç½¤¬É¬Íפʤ顢TMail ¤ä RubyMail ¤Î¤è¤¦¤Ê
-¥é¥¤¥Ö¥é¥ê¤òÊ»ÍѤ·¤Æ¤¯¤À¤µ¤¤¡£¤¤¤º¤ì¤â RAA ¤«¤é¥À¥¦¥ó¥í¡¼¥É¤Ç¤­¤Þ¤¹¡£
-¤Þ¤¿¥¤¥ó¥¿¡¼¥Í¥Ã¥È¥á¡¼¥ë¥Õ¥©¡¼¥Þ¥Ã¥È¤ÎÀµ¼°¤Êµ¬³Ê½ñ¤Ï RFC2822 ¤Ç¤¹¡£
-
- * RAA ((<URL:http://www.ruby-lang.org/en/raa.html>))
- * RFC2822 ((<URL:http://www.ietf.org/rfc/rfc2822.txt>))
-
-== »ÈÍÑÎã
-
-=== ¤È¤Ë¤«¤¯¥á¡¼¥ë¤òÁ÷¤ë
-
-SMTP ¤ò»È¤Ã¤Æ¥á¡¼¥ë¤òÁ÷¤ë¤Ë¤Ï¤Þ¤º SMTP.start ¤Ç¥»¥Ã¥·¥ç¥ó¤ò³«¤­¤Þ¤¹¡£
-Âè°ì°ú¿ô¤¬¥µ¡¼¥Ð¤Î¥¢¥É¥ì¥¹¤ÇÂèÆó°ú¿ô¤¬¥Ý¡¼¥ÈÈÖ¹æ¤Ç¤¹¡£
-¥Ö¥í¥Ã¥¯¤ò»È¤¦¤È File.open ¤ÈƱ¤¸¤è¤¦¤Ë½ªÃ¼½èÍý¤ò¼«Æ°Åª¤Ë¤ä¤Ã¤Æ¤¯¤ì¤ë
-¤Î¤Ç¤ª¤¹¤¹¤á¤Ç¤¹¡£
-
- require 'net/smtp'
- Net::SMTP.start( 'your.smtp.server', 25 ) {|smtp|
- # use smtp object only in this block
- }
-
-your.smtp.server ¤ÏŬÀÚ¤Ê SMTP ¥µ¡¼¥Ð¤Î¥¢¥É¥ì¥¹¤ËÆÉ¤ß¤«¤¨¤Æ¤¯¤À¤µ¤¤¡£
-Ä̾ï¤Ï LAN ¤Î´ÉÍý¼Ô¤ä¥×¥í¥Ð¥¤¥À¤¬ SMTP ¥µ¡¼¥Ð¤òÍѰդ·¤Æ¤¯¤ì¤Æ¤¤¤ë¤Ï¤º¤Ç¤¹¡£
-
-¥»¥Ã¥·¥ç¥ó¤¬³«¤¤¤¿¤é¤¢¤È¤Ï send_mail ¤Ç¥á¡¼¥ë¤òή¤·¤³¤à¤À¤±¤Ç¤¹¡£
-
- require 'net/smtp'
-
- Net::SMTP.start( 'your.smtp.server', 25 ) {|smtp|
- smtp.send_mail <<EndOfMail, 'your@mail.address', 'to@some.domain'
- From: Your Name <your@mail.address>
- To: Dest Address <to@some.domain>
- Subject: test mail
- Date: Sat, 23 Jun 2001 16:26:43 +0900
- Message-Id: <unique.message.id.string@some.domain>
-
- This is test mail.
- EndOfMail
- }
-
-=== ¥»¥Ã¥·¥ç¥ó¤ò½ªÎ»¤¹¤ë
-
-¥á¡¼¥ë¤òÁ÷¤Ã¤¿¤é SMTP#finish ¤ò¸Æ¤ó¤Ç¥»¥Ã¥·¥ç¥ó¤ò½ªÎ»¤·¤Ê¤±¤ì¤Ð¤¤¤±
-¤Þ¤»¤ó¡£File ¤Î¤è¤¦¤Ë GC »þ¤Ë¾¡¼ê¤Ë close ¤µ¤ì¤ë¤³¤È¤â¤¢¤ê¤Þ¤»¤ó¡£
-¤¤¤í¤¤¤í¤Ê¤È¤³¤í¤Ç finish ¤¬¤Ê¤¤¥½¡¼¥¹¥³¡¼¥É¤ÎÎã¤ò¸«³Ý¤±¤Þ¤¹¤¬¡¢
-¤¹¤Ù¤Æ¸í¤ê¤Ç¤¹¡£finish ¤Ïɬ¤º¸Æ¤ó¤Ç¤¯¤À¤µ¤¤¡£
-
-¤Þ¤¿¥Ö¥í¥Ã¥¯ÉÕ¤­¤Î SMTP.start/SMTP#start ¤ò»È¤¦¤È¾¡¼ê¤Ë finish ¤ò
-¸Æ¤ó¤Ç¤¯¤ì¤ë¤Î¤ÇÊØÍø¤Ç¤¹¡£²Äǽ¤Ê¸Â¤ê¥Ö¥í¥Ã¥¯ÉÕ¤­¤Î start ¤ò»È¤¦¤Î¤¬
-¤è¤¤¤Ç¤·¤ç¤¦¡£
-
- # using SMTP#finish
- smtp = Net::SMTP.start( 'your.smtp.server', 25 )
- smtp.send_mail mail_string, 'from@address', 'to@address'
- smtp.finish
-
- # using block form of SMTP.start
- Net::SMTP.start( 'your.smtp.server', 25 ) {|smtp|
- smtp.send_mail mail_string, 'from@address', 'to@address'
- }
-
-=== ʸ»úÎó°Ê³°¤«¤é¤ÎÁ÷¿®
-
-¤Ò¤È¤Ä¾å¤ÎÎã¤Ç¤Ïʸ»úÎó¥ê¥Æ¥é¥ë(¥Ò¥¢¥É¥­¥å¥á¥ó¥È)¤ò»È¤Ã¤ÆÁ÷¿®¤·¤Þ¤·¤¿¤¬¡¢
-each ¥á¥½¥Ã¥É¤ò»ý¤Ã¤¿¥ª¥Ö¥¸¥§¥¯¥È¤«¤é¤Ê¤é¤Ê¤ó¤Ç¤âÁ÷¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
-°Ê²¼¤Ï File ¥ª¥Ö¥¸¥§¥¯¥È¤«¤éľÀÜÁ÷¿®¤¹¤ëÎã¤Ç¤¹¡£
-
- require 'net/smtp'
- Net::SMTP.start( 'your.smtp.server', 25 ) {|smtp|
- File.open( 'Mail/draft/1' ) {|f|
- smtp.send_mail f, 'your@mail.address', 'to@some.domain'
- }
- }
-
-=== HELO ¥É¥á¥¤¥ó
-
-SMTP ¤Ç¤Ï¥á¡¼¥ë¤òÁ÷¤ë¦¤Î¥Û¥¹¥È¤Î̾Á° (HELO ¥É¥á¥¤¥ó¤È¸Æ¤Ö) ¤òÍ×µá
-¤µ¤ì¤ë¤Î¤Ç¤¹¤¬¡¢Net::SMTP ¤Ç¤Ï¤È¤ê¤¢¤¨¤º localhost.localdomain ¤È
-¤¤¤¦Ì¾Á°¤òÁ÷¿®¤·¤Æ¤¤¤Þ¤¹¡£¤¿¤¤¤Æ¤¤¤Î SMTP ¥µ¡¼¥Ð¤Ï¤³¤Î HELO ¥É¥á¥¤¥ó
-¤Ë¤è¤ëǧ¾Ú¤Ï¤¢¤Þ¤ê¿¿ÌÌÌܤ˹Ԥï¤Ê¤¤¤Î¤Ç (´Êñ¤Ëµ¶Â¤¤Ç¤­¤ë¤«¤é¤Ç¤¹)
-ÌäÂê¤Ë¤Ê¤é¤Ê¤¤¤³¤È¤¬Â¿¤¤¤Î¤Ç¤¹¤¬¡¢¤Þ¤ì¤Ë¥á¡¼¥ë¥»¥Ã¥·¥ç¥ó¤òÀÚ¤é¤ì¤ë
-¤³¤È¤â¤¢¤ê¤Þ¤¹¡£¤½¤¦¤¤¤¦¤È¤­¤Ï¤È¤ê¤¢¤¨¤º HELO ¥É¥á¥¤¥ó¤òÍ¿¤¨¤Æ¤ß¤Æ
-¤¯¤À¤µ¤¤¡£¤â¤Á¤í¤ó¤½¤ì°Ê³°¤Î»þ¤â HELO ¥É¥á¥¤¥ó¤Ï¤Á¤ã¤ó¤ÈÅϤ¹¤Î¤¬
-¥Ù¥¹¥È¤Ç¤¹¡£
-
-HELO ¥É¥á¥¤¥ó¤Ï SMTP.start/SMTP#start ¤ÎÂè»°°ú¿ô helo_domain ¤Ë»ØÄê
-¤·¤Þ¤¹¡£
-
- Net::SMTP.start( 'your.smtp.server', 25,
- 'mail.from.domain' ) {|smtp|
-
-¤è¤¯¤¢¤ë¥À¥¤¥ä¥ë¥¢¥Ã¥×¥Û¥¹¥È¤Î¾ì¹ç¡¢HELO ¥É¥á¥¤¥ó¤Ë¤Ï ISP ¤Î¥á¡¼¥ë
-¥µ¡¼¥Ð¤Î¥É¥á¥¤¥ó¤ò»È¤Ã¤Æ¤ª¤±¤Ð¤¿¤¤¤Æ¤¤Ä̤ê¤Þ¤¹¡£
-
-== class Net::SMTP
-
-=== ¥¯¥é¥¹¥á¥½¥Ã¥É
-
-: new( address, port = 25 )
- ¿·¤·¤¤ SMTP ¥ª¥Ö¥¸¥§¥¯¥È¤òÀ¸À®¤·¤Þ¤¹¡£address ¤ÏSMTP¥µ¡¼¥Ð¡¼¤ÎFQDN¤Ç¡¢
- port ¤ÏÀܳ¤¹¤ë¥Ý¡¼¥ÈÈÖ¹æ¤Ç¤¹¡£¤¿¤À¤·¡¢¤³¤Î¥á¥½¥Ã¥É¤Ç¤Ï¤Þ¤ÀÀܳ¤Ï¤·¤Þ¤»¤ó¡£
-
-: start( address, port = 25, helo_domain = 'localhost.localdomain', account = nil, password = nil, authtype = nil )
-: start( address, port = 25, helo_domain = 'localhost.localdomain', account = nil, password = nil, authtype = nil ) {|smtp| .... }
- °Ê²¼¤ÈƱ¤¸¤Ç¤¹¡£
- Net::SMTP.new(address,port).start(helo_domain,account,password,authtype)
-
- # example
- Net::SMTP.start( 'your.smtp.server' ) {
- smtp.send_mail mail_string, 'from@mail.address', 'dest@mail.address'
- }
-
-=== ¥á¥½¥Ã¥É
-
-: start( helo_domain = <local host name>, account = nil, password = nil, authtype = nil )
-: start( helo_domain = <local host name>, account = nil, password = nil, authtype = nil ) {|smtp| .... }
- TCP ¥³¥Í¥¯¥·¥ç¥ó¤òÄ¥¤ê¡¢Æ±»þ¤Ë SMTP ¥»¥Ã¥·¥ç¥ó¤ò³«»Ï¤·¤Þ¤¹¡£¤½¤Î¤È¤­¡¢
- ¤³¤Á¤é¤Î¥Û¥¹¥È¤Î FQDN ¤ò helo_domain ¤Ë»ØÄꤷ¤Þ¤¹¡£
- ¤â¤·¤¹¤Ç¤Ë¥»¥Ã¥·¥ç¥ó¤¬³«»Ï¤·¤Æ¤¤¤¿¤é IOError ¤òȯÀ¸¤·¤Þ¤¹¡£
-
- account ¤È password ¤ÎξÊý¤¬Í¿¤¨¤é¤ì¤¿¾ì¹ç¡¢AUTH ¥³¥Þ¥ó¥É¤Ë¤è¤Ã¤Æ
- ǧ¾Ú¤ò¹Ô¤¤¤Þ¤¹¡£authtype ¤Ï»ÈÍѤ¹¤ëǧ¾Ú¤Î¥¿¥¤¥×¤Ç¡¢¥·¥ó¥Ü¥ë
- ¤Ç :plain ¤« :cram_md5 ¤ò»ØÄꤷ¤Þ¤¹¡£
-
-: active?
- SMTP ¥»¥Ã¥·¥ç¥ó¤¬³«»Ï¤µ¤ì¤Æ¤¤¤¿¤é¿¿¡£
-
-: address
- Àܳ¤¹¤ë¥¢¥É¥ì¥¹
-
-: port
- Àܳ¤¹¤ë¥Ý¡¼¥ÈÈÖ¹æ
-
-: open_timeout
-: open_timeout=(n)
- Àܳ»þ¤ËÂԤĺÇÂçÉÿô¡£¤³¤ÎÉÿô¤¿¤Ã¤Æ¤â¥³¥Í¥¯¥·¥ç¥ó¤¬
- ³«¤«¤Ê¤±¤ì¤ÐÎã³° TimeoutError ¤òȯÀ¸¤·¤Þ¤¹¡£
-
-: read_timeout
-: read_timeout=(n)
- ÆÉ¤ß¤³¤ß (read(1) °ì²ó) ¤Ç¥Ö¥í¥Ã¥¯¤·¤Æ¤è¤¤ºÇÂçÉÿô¡£
- ¤³¤ÎÉÿô¤¿¤Ã¤Æ¤âÆÉ¤ß¤³¤á¤Ê¤±¤ì¤ÐÎã³° TimeoutError ¤òȯÀ¸¤·¤Þ¤¹¡£
-
-: finish
- SMTP ¥»¥Ã¥·¥ç¥ó¤ò½ªÎ»¤·¤Þ¤¹¡£¥»¥Ã¥·¥ç¥ó³«»ÏÁ°¤Ë¤³¤Î¥á¥½¥Ã¥É¤¬
- ¸Æ¤Ð¤ì¤¿¾ì¹ç¤ÏÎã³° IOError ¤òȯÀ¸¤·¤Þ¤¹¡£
-
-: send_mail( mailsrc, from_addr, *to_addrs )
- mailsrc ¤ò¥á¡¼¥ë¤È¤·¤ÆÁ÷¿®¤·¤Þ¤¹¡£mailsrc ¤Ï each ¥¤¥Æ¥ì¡¼¥¿¤ò»ý¤Ä
- ¥ª¥Ö¥¸¥§¥¯¥È¤Ê¤é¤Ê¤ó¤Ç¤â¹½¤¤¤Þ¤»¤ó (¤¿¤È¤¨¤Ð String ¤ä File)¡£
- from_domain ¤ÏÁ÷¤ê¼ç¤Î¥á¡¼¥ë¥¢¥É¥ì¥¹ ('...@...'¤Î¤«¤¿¤Á¤Î¤â¤Î) ¤Ç¡¢
- to_addrs ¤Ë¤ÏÁ÷¿®Àè¥á¡¼¥ë¥¢¥É¥ì¥¹¤òʤ٤ޤ¹¡£
-
- # example
- Net::SMTP.start( 'your.smtp.server' ) {|smtp|
- smtp.send_mail mail_string,
- 'from@mail.address',
- 'dest@mail.address' 'dest2@mail.address'
- }
-
-: ready( from_addr, *to_addrs ) {|adapter| .... }
- ¥á¡¼¥ë½ñ¤­¤³¤ß¤Î½àÈ÷¤ò¤·¤¿¤¦¤¨¤Ç¡¢write ¥á¥½¥Ã¥É¤ò»ý¤Ä¥ª¥Ö¥¸¥§¥¯¥È¤ò
- ¥Ö¥í¥Ã¥¯¤Ë¤¢¤¿¤¨¤Þ¤¹¡£from_addr ¤ÏÁ÷¿®¸µ¥á¡¼¥ë¥¢¥É¥ì¥¹¤Ç to_addrs ¤Ï
- °¸Àè¤Î¥á¡¼¥ë¥Ü¥Ã¥¯¥¹¤Ç¤¹¡£
-
- # example
- Net::SMTP.start( 'your.smtp.server', 25 ) {|smtp|
- smtp.ready( 'from@mail.addr', 'dest@mail.addr' ) {|f|
- f.puts 'From: aamine@loveruby.net'
- f.puts 'To: someone@somedomain.org'
- f.puts 'Subject: test mail'
- f.puts
- f.puts 'This is test mail.'
- }
- }
-
-== ȯÀ¸¤¹¤ëÎã³°
-
-¥»¥Ã¥·¥ç¥óÃæ¤Ë (SMTP ¥ì¥Ù¥ë¤Î) ¥¨¥é¡¼¤¬¤ª¤³¤Ã¤¿¾ì¹ç¡¢
-°Ê²¼¤ÎÎã³°¤¬È¯À¸¤·¤Þ¤¹¡£
-: Net::ProtoSyntaxError
- SMTP ¥³¥Þ¥ó¥É¤Î¹½Ê¸¥ß¥¹(500ÈÖÂæ)
-: Net::ProtoFatalError
- ¹±µ×Ū¤Ê¥¨¥é¡¼(550ÈÖÂæ)
-: Net::ProtoUnknownError
- ͽ´ü¤·¤Ê¤¤¥¨¥é¡¼¡£¤ª¤½¤é¤¯¥Ð¥°
-: Net::ProtoServerBusy
- °ì»þŪ¤Ê¥¨¥é¡¼(420/450ÈÖÂæ)
-
-=end
diff --git a/enum.c b/enum.c
index 24b6e9e065..71b2e7e763 100644
--- a/enum.c
+++ b/enum.c
@@ -6,7 +6,7 @@
$Date$
created at: Fri Oct 1 15:15:19 JST 1993
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
**********************************************************************/
@@ -156,23 +156,23 @@ collect_all(i, ary)
}
static VALUE
-enum_to_a(obj)
+enum_collect(obj)
VALUE obj;
{
VALUE ary = rb_ary_new();
- rb_iterate(rb_each, obj, collect_all, ary);
+ rb_iterate(rb_each, obj, rb_block_given_p() ? collect_i : collect_all, ary);
return ary;
}
static VALUE
-enum_collect(obj)
+enum_to_a(obj)
VALUE obj;
{
VALUE ary = rb_ary_new();
- rb_iterate(rb_each, obj, rb_block_given_p() ? collect_i : collect_all, ary);
+ rb_iterate(rb_each, obj, collect_all, ary);
return ary;
}
@@ -264,7 +264,7 @@ sort_by_cmp(a, b)
VALUE retval;
retval = rb_funcall(RARRAY(*a)->ptr[0], id_cmp, 1, RARRAY(*b)->ptr[0]);
- return rb_cmpint(retval);
+ return rb_cmpint(retval, *a, *b);
}
static VALUE
@@ -274,7 +274,12 @@ enum_sort_by(obj)
VALUE ary;
long i;
- ary = rb_ary_new2((TYPE(obj) == T_ARRAY) ? RARRAY(obj)->len : 2000);
+ if (TYPE(obj) == T_ARRAY) {
+ ary = rb_ary_new2(RARRAY(obj)->len);
+ }
+ else {
+ ary = rb_ary_new();
+ }
rb_iterate(rb_each, obj, sort_by_i, ary);
if (RARRAY(ary)->len > 1) {
qsort(RARRAY(ary)->ptr, RARRAY(ary)->len, sizeof(VALUE), sort_by_cmp);
@@ -287,7 +292,7 @@ enum_sort_by(obj)
}
static VALUE
-all_i(i, memo)
+all_iter_i(i, memo)
VALUE i;
NODE *memo;
{
@@ -299,6 +304,18 @@ all_i(i, memo)
}
static VALUE
+all_i(i, memo)
+ VALUE i;
+ NODE *memo;
+{
+ if (!RTEST(i)) {
+ memo->u1.value = Qfalse;
+ rb_iter_break();
+ }
+ return Qnil;
+}
+
+static VALUE
enum_all(obj)
VALUE obj;
{
@@ -306,14 +323,14 @@ enum_all(obj)
NODE *memo = rb_node_newnode(NODE_MEMO, Qnil, 0, 0);
memo->u1.value = Qtrue;
- rb_iterate(rb_each, obj, all_i, (VALUE)memo);
+ rb_iterate(rb_each, obj, rb_block_given_p() ? all_iter_i : all_i, (VALUE)memo);
result = memo->u1.value;
rb_gc_force_recycle((VALUE)memo);
return result;
}
static VALUE
-any_i(i, memo)
+any_iter_i(i, memo)
VALUE i;
NODE *memo;
{
@@ -325,6 +342,18 @@ any_i(i, memo)
}
static VALUE
+any_i(i, memo)
+ VALUE i;
+ NODE *memo;
+{
+ if (RTEST(i)) {
+ memo->u1.value = Qtrue;
+ rb_iter_break();
+ }
+ return Qnil;
+}
+
+static VALUE
enum_any(obj)
VALUE obj;
{
@@ -332,7 +361,7 @@ enum_any(obj)
NODE *memo = rb_node_newnode(NODE_MEMO, Qnil, 0, 0);
memo->u1.value = Qfalse;
- rb_iterate(rb_each, obj, any_i, (VALUE)memo);
+ rb_iterate(rb_each, obj, rb_block_given_p() ? any_iter_i : any_i, (VALUE)memo);
result = memo->u1.value;
rb_gc_force_recycle((VALUE)memo);
return result;
@@ -350,7 +379,7 @@ min_i(i, memo)
}
else {
cmp = rb_funcall(i, id_cmp, 1, memo->u1.value);
- if (rb_cmpint(cmp) < 0) {
+ if (rb_cmpint(cmp, i, memo->u1.value) < 0) {
memo->u1.value = i;
}
}
@@ -368,8 +397,8 @@ min_ii(i, memo)
memo->u1.value = i;
}
else {
- cmp = rb_yield(rb_assoc_new(i, memo->u1.value));
- if (rb_cmpint(cmp) < 0) {
+ cmp = rb_yield_values(2, i, memo->u1.value);
+ if (rb_cmpint(cmp, i, memo->u1.value) < 0) {
memo->u1.value = i;
}
}
@@ -401,7 +430,7 @@ max_i(i, memo)
}
else {
cmp = rb_funcall(i, id_cmp, 1, memo->u1.value);
- if (rb_cmpint(cmp) > 0) {
+ if (rb_cmpint(cmp, i, memo->u1.value) > 0) {
memo->u1.value = i;
}
}
@@ -419,8 +448,8 @@ max_ii(i, memo)
memo->u1.value = i;
}
else {
- cmp = rb_yield(rb_assoc_new(i, memo->u1.value));
- if (rb_cmpint(cmp) > 0) {
+ cmp = rb_yield_values(2, i, memo->u1.value);
+ if (rb_cmpint(cmp, i, memo->u1.value) > 0) {
memo->u1.value = i;
}
}
@@ -517,11 +546,10 @@ enum_zip(argc, argv, obj)
VALUE *argv;
VALUE obj;
{
- int i, j, len;
+ int i;
VALUE result;
NODE *memo;
- len = 0;
for (i=0; i<argc; i++) {
argv[i] = rb_convert_type(argv[i], T_ARRAY, "Array", "to_ary");
}
diff --git a/env.h b/env.h
index 8290b3e481..a4802ed448 100644
--- a/env.h
+++ b/env.h
@@ -6,7 +6,7 @@
$Date$
created at: Mon Jul 11 11:53:03 JST 1994
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
**********************************************************************/
diff --git a/error.c b/error.c
index 38b0514197..f8fbdf7ade 100644
--- a/error.c
+++ b/error.c
@@ -6,7 +6,7 @@
$Date$
created at: Mon Aug 9 16:11:34 JST 1993
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
**********************************************************************/
@@ -26,26 +26,33 @@
int ruby_nerrs;
-static void
-err_snprintf(buf, len, fmt, args)
+static int
+err_position(buf, len)
char *buf;
long len;
- const char *fmt;
- va_list args;
{
- long n;
-
ruby_set_current_source();
if (!ruby_sourcefile) {
- vsnprintf(buf, len, fmt, args);
- return;
+ return 0;
}
else if (ruby_sourceline == 0) {
- n = snprintf(buf, len, "%s: ", ruby_sourcefile);
+ return snprintf(buf, len, "%s: ", ruby_sourcefile);
}
else {
- n = snprintf(buf, len, "%s:%d: ", ruby_sourcefile, ruby_sourceline);
+ return snprintf(buf, len, "%s:%d: ", ruby_sourcefile, ruby_sourceline);
}
+}
+
+static void
+err_snprintf(buf, len, fmt, args)
+ char *buf;
+ long len;
+ const char *fmt;
+ va_list args;
+{
+ long n;
+
+ n = err_position(buf, len);
if (len > n) {
vsnprintf((char*)buf+n, len-n, fmt, args);
}
@@ -106,9 +113,8 @@ warn_print(fmt, args)
char buf[BUFSIZ];
err_snprintf(buf, BUFSIZ, fmt, args);
- fputs(buf, stderr);
- fputs("\n", stderr);
- fflush(stderr);
+ rb_write_error(buf);
+ rb_write_error("\n");
}
void
@@ -123,6 +129,8 @@ rb_warn(fmt, va_alist)
char buf[BUFSIZ];
va_list args;
+ if (NIL_P(ruby_verbose)) return;
+
snprintf(buf, BUFSIZ, "warning: %s", fmt);
va_init_list(args, fmt);
@@ -152,6 +160,15 @@ rb_warning(fmt, va_alist)
va_end(args);
}
+static VALUE
+rb_warn_m(self, mesg)
+ VALUE self, mesg;
+{
+ rb_io_write(rb_stderr, mesg);
+ rb_io_write(rb_stderr, rb_default_rs);
+ return mesg;
+}
+
void
#ifdef HAVE_STDARG_PROTOTYPES
rb_bug(const char *fmt, ...)
@@ -170,7 +187,9 @@ rb_bug(fmt, va_alist)
va_init_list(args, fmt);
warn_print(buf, args);
va_end(args);
- fprintf(stderr, "ruby %s (%s) [%s]\n", RUBY_VERSION, RUBY_RELEASE_DATE, RUBY_PLATFORM);
+ snprintf(buf, BUFSIZ, "ruby %s (%s) [%s]\n", RUBY_VERSION, RUBY_RELEASE_DATE, RUBY_PLATFORM);
+ rb_write_error(buf);
+ rb_write_error("\n");
abort();
}
@@ -230,7 +249,7 @@ rb_check_type(x, t)
etype = RSTRING(rb_obj_as_string(x))->ptr;
}
else {
- etype = rb_class2name(CLASS_OF(x));
+ etype = rb_obj_classname(x);
}
rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)",
etype, type->name);
@@ -330,7 +349,7 @@ static VALUE
exc_to_s(exc)
VALUE exc;
{
- VALUE mesg = rb_iv_get(exc, "mesg");
+ VALUE mesg = rb_attr_get(exc, rb_intern("mesg"));
if (NIL_P(mesg)) return rb_class_path(CLASS_OF(exc));
if (OBJ_TAINTED(exc)) OBJ_TAINT(mesg);
@@ -420,7 +439,7 @@ static VALUE
exit_status(exc)
VALUE exc;
{
- return rb_iv_get(exc, "status");
+ return rb_attr_get(exc, rb_intern("status"));
}
void
@@ -433,31 +452,56 @@ rb_name_error(id, fmt, va_alist)
va_dcl
#endif
{
- VALUE exc;
-
+ VALUE exc, argv[2];
va_list args;
char buf[BUFSIZ];
va_init_list(args, fmt);
vsnprintf(buf, BUFSIZ, fmt, args);
va_end(args);
- exc = rb_exc_new2(rb_eNameError, buf);
- rb_iv_set(exc, "name", ID2SYM(id));
+
+ argv[0] = rb_str_new2(buf);
+ argv[1] = ID2SYM(id);
+ exc = rb_class_new_instance(2, argv, rb_eNameError);
rb_exc_raise(exc);
}
static VALUE
+name_err_initialize(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ VALUE name = (argc > 1) ? argv[--argc] : Qnil;
+ exc_initialize(argc, argv, self);
+ rb_iv_set(self, "name", name);
+ return self;
+}
+
+static VALUE
name_err_name(self)
VALUE self;
{
- return rb_iv_get(self, "name");
+ return rb_attr_get(self, rb_intern("name"));
+}
+
+static VALUE
+nometh_err_initialize(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ VALUE args = (argc > 2) ? argv[--argc] : Qnil;
+ name_err_initialize(argc, argv, self);
+ rb_iv_set(self, "args", args);
+ return self;
}
static VALUE
nometh_err_args(self)
VALUE self;
{
- return rb_iv_get(self, "args");
+ return rb_attr_get(self, rb_intern("args"));
}
void
@@ -479,32 +523,83 @@ set_syserr(n, name)
VALUE error;
if (!st_lookup(syserr_tbl, n, &error)) {
- error = rb_define_class_under(rb_mErrno, name, rb_eSystemCallError);;
+ error = rb_define_class_under(rb_mErrno, name, rb_eSystemCallError);
rb_define_const(error, "Errno", INT2NUM(n));
st_add_direct(syserr_tbl, n, error);
}
+ else {
+ rb_define_const(rb_mErrno, name, error);
+ }
return error;
}
static VALUE
-get_syserr(int n)
+get_syserr(n)
+ int n;
{
VALUE error;
if (!st_lookup(syserr_tbl, n, &error)) {
- char name[6];
-
- sprintf(name, "E%03d", n);
+ char name[8]; /* some Windows' errno have 5 digits. */
+
+ snprintf(name, sizeof(name), "E%03d", n);
error = set_syserr(n, name);
}
return error;
}
static VALUE
+syserr_initialize(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+#if !defined(_WIN32) && !defined(__VMS)
+ char *strerror();
+#endif
+ char *err;
+ char *buf;
+ VALUE mesg, error;
+ VALUE klass = rb_obj_class(self);
+
+ if (klass == rb_eSystemCallError) {
+ rb_scan_args(argc, argv, "11", &mesg, &error);
+ if (argc == 1 && FIXNUM_P(mesg)) {
+ error = mesg; mesg = Qnil;
+ }
+ if (!NIL_P(error) && st_lookup(syserr_tbl, NUM2LONG(error), &klass)) {
+ /* change class */
+ if (TYPE(self) != T_OBJECT) { /* insurance to avoid type crash */
+ rb_raise(rb_eTypeError, "invalid instance type");
+ }
+ RBASIC(self)->klass = klass;
+ }
+ }
+ else {
+ rb_scan_args(argc, argv, "01", &mesg);
+ error = rb_const_get_at(klass, rb_intern("Errno"));
+ }
+ if (!NIL_P(error)) err = strerror(NUM2LONG(error));
+ else err = "unknown error";
+ if (!NIL_P(mesg)) {
+ StringValue(mesg);
+ buf = ALLOCA_N(char, strlen(err)+RSTRING(mesg)->len+4);
+ sprintf(buf, "%s - %.*s", err, (int)RSTRING(mesg)->len, RSTRING(mesg)->ptr);
+ mesg = rb_str_new2(buf);
+ }
+ else {
+ mesg = rb_str_new2(err);
+ }
+ exc_initialize(1, &mesg, self);
+ rb_iv_set(self, "errno", error);
+ return self;
+}
+
+static VALUE
syserr_errno(self)
VALUE self;
{
- return rb_iv_get(self, "errno");
+ return rb_attr_get(self, rb_intern("errno"));
}
static VALUE
@@ -516,7 +611,7 @@ syserr_eqq(self, exc)
if (!rb_obj_is_kind_of(exc, rb_eSystemCallError)) return Qfalse;
if (self == rb_eSystemCallError) return Qtrue;
- num = rb_iv_get(exc, "errno");
+ num = rb_attr_get(exc, rb_intern("errno"));
if (NIL_P(num)) {
VALUE klass = CLASS_OF(exc);
@@ -560,8 +655,10 @@ Init_Exception()
rb_eIndexError = rb_define_class("IndexError", rb_eStandardError);
rb_eRangeError = rb_define_class("RangeError", rb_eStandardError);
rb_eNameError = rb_define_class("NameError", rb_eStandardError);
+ rb_define_method(rb_eNameError, "initialize", name_err_initialize, -1);
rb_define_method(rb_eNameError, "name", name_err_name, 0);
rb_eNoMethodError = rb_define_class("NoMethodError", rb_eNameError);
+ rb_define_method(rb_eNoMethodError, "initialize", nometh_err_initialize, -1);
rb_define_method(rb_eNoMethodError, "args", nometh_err_args, 0);
rb_eScriptError = rb_define_class("ScriptError", rb_eException);
@@ -574,6 +671,8 @@ Init_Exception()
rb_eNoMemError = rb_define_class("NoMemoryError", rb_eException);
init_syserr();
+
+ rb_define_global_function("warn", rb_warn_m, 1);
}
void
@@ -645,35 +744,17 @@ void
rb_sys_fail(mesg)
const char *mesg;
{
-#if !defined(_WIN32) && !defined(__VMS)
- char *strerror();
-#endif
- char *err;
- char *buf;
extern int errno;
int n = errno;
- VALUE ee;
+ VALUE arg;
- if (errno == 0) {
+ errno = 0;
+ if (n == 0) {
rb_bug("rb_sys_fail() - errno == 0");
}
- err = strerror(errno);
- if (mesg) {
- volatile VALUE tmp = rb_str_inspect(rb_str_new2(mesg));
-
- buf = ALLOCA_N(char, strlen(err)+RSTRING(tmp)->len+4);
- sprintf(buf, "%s - %s", err, RSTRING(tmp)->ptr);
- }
- else {
- buf = ALLOCA_N(char, strlen(err)+1);
- strcpy(buf, err);
- }
-
- errno = 0;
- ee = rb_exc_new2(get_syserr(n), buf);
- rb_iv_set(ee, "errno", INT2NUM(n));
- rb_exc_raise(ee);
+ arg = mesg ? rb_str_new2(mesg) : Qnil;
+ rb_exc_raise(rb_class_new_instance(1, &arg, get_syserr(n)));
}
void
@@ -720,7 +801,7 @@ void
rb_check_frozen(obj)
VALUE obj;
{
- if (OBJ_FROZEN(obj)) rb_error_frozen(rb_class2name(CLASS_OF(obj)));
+ if (OBJ_FROZEN(obj)) rb_error_frozen(rb_obj_classname(obj));
}
static void
@@ -728,6 +809,7 @@ init_syserr()
{
syserr_tbl = st_init_numtable();
rb_eSystemCallError = rb_define_class("SystemCallError", rb_eStandardError);
+ rb_define_method(rb_eSystemCallError, "initialize", syserr_initialize, -1);
rb_define_method(rb_eSystemCallError, "errno", syserr_errno, 0);
rb_define_singleton_method(rb_eSystemCallError, "===", syserr_eqq, 1);
@@ -1111,7 +1193,7 @@ err_append(s)
ruby_errinfo = rb_exc_new2(rb_eSyntaxError, s);
}
else {
- VALUE str = rb_str_to_str(ruby_errinfo);
+ VALUE str = rb_obj_as_string(ruby_errinfo);
rb_str_cat2(str, "\n");
rb_str_cat2(str, s);
@@ -1119,8 +1201,7 @@ err_append(s)
}
}
else {
- fputs(s, stderr);
- fputs("\n", stderr);
- fflush(stderr);
+ rb_write_error(s);
+ rb_write_error("\n");
}
}
diff --git a/eval.c b/eval.c
index bfa7330999..fd28c166fc 100644
--- a/eval.c
+++ b/eval.c
@@ -6,7 +6,7 @@
$Date$
created at: Thu Jun 10 14:22:17 JST 1993
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
Copyright (C) 2000 Information-technology Promotion Agency, Japan
@@ -95,7 +95,7 @@ char *strrchr _((const char*,const char));
VALUE rb_cProc;
static VALUE rb_cBinding;
-static VALUE proc_invoke _((VALUE,VALUE,int,VALUE));
+static VALUE proc_invoke _((VALUE,VALUE,VALUE,VALUE));
static VALUE rb_f_binding _((VALUE));
static void rb_f_END _((void));
static VALUE rb_f_block_given_p _((void));
@@ -115,7 +115,6 @@ static int scope_vmode;
#define SCOPE_SET(f) (scope_vmode=(f))
#define SCOPE_TEST(f) (scope_vmode&(f))
-static NODE* ruby_last_node;
NODE* ruby_current_node;
int ruby_safe_level = 0;
/* safe-level:
@@ -140,7 +139,14 @@ rb_secure(level)
}
void
-rb_check_safe_str(x)
+rb_secure_update(obj)
+ VALUE obj;
+{
+ if (!OBJ_TAINTED(obj)) rb_secure(4);
+}
+
+void
+rb_check_safe_obj(x)
VALUE x;
{
if (ruby_safe_level > 0 && OBJ_TAINTED(x)){
@@ -153,9 +159,16 @@ rb_check_safe_str(x)
}
}
rb_secure(4);
+}
+
+void
+rb_check_safe_str(x)
+ VALUE x;
+{
+ rb_check_safe_obj(x);
if (TYPE(x)!= T_STRING) {
rb_raise(rb_eTypeError, "wrong argument type %s (expected String)",
- rb_class2name(CLASS_OF(x)));
+ rb_obj_classname(x));
}
}
@@ -187,12 +200,14 @@ struct cache_entry { /* method hash table. */
};
static struct cache_entry cache[CACHE_SIZE];
+static int ruby_running = 0;
void
rb_clear_cache()
{
struct cache_entry *ent, *end;
+ if (!ruby_running) return;
ent = cache; end = ent + CACHE_SIZE;
while (ent < end) {
ent->mid = 0;
@@ -201,11 +216,28 @@ rb_clear_cache()
}
static void
+rb_clear_cache_for_undef(klass, id)
+ ID id;
+{
+ struct cache_entry *ent, *end;
+
+ if (!ruby_running) return;
+ ent = cache; end = ent + CACHE_SIZE;
+ while (ent < end) {
+ if (ent->origin == klass && ent->mid == id) {
+ ent->mid = 0;
+ }
+ ent++;
+ }
+}
+
+static void
rb_clear_cache_by_id(id)
ID id;
{
struct cache_entry *ent, *end;
+ if (!ruby_running) return;
ent = cache; end = ent + CACHE_SIZE;
while (ent < end) {
if (ent->mid == id) {
@@ -215,22 +247,23 @@ rb_clear_cache_by_id(id)
}
}
-static void
+void
rb_clear_cache_by_class(klass)
VALUE klass;
{
struct cache_entry *ent, *end;
+ if (!ruby_running) return;
ent = cache; end = ent + CACHE_SIZE;
while (ent < end) {
- if (ent->origin == klass) {
+ if (ent->klass == klass || ent->origin == klass) {
ent->mid = 0;
}
ent++;
}
}
-static ID init, alloc, eqq, each, aref, aset, match, missing;
+static ID init, eqq, each, aref, aset, match, missing;
static ID added, singleton_added;
static ID __id__, __send__;
@@ -247,13 +280,46 @@ rb_add_method(klass, mid, node, noex)
if (ruby_safe_level >= 4 && (klass == rb_cObject || !OBJ_TAINTED(klass))) {
rb_raise(rb_eSecurityError, "Insecure: can't define method");
}
- if (mid == init && !FL_TEST(klass, FL_SINGLETON) && node && nd_type(node) != NODE_ZSUPER) {
- noex = NOEX_PRIVATE | (noex & NOEX_NOSUPER);
+ if (!FL_TEST(klass, FL_SINGLETON) &&
+ node && nd_type(node) != NODE_ZSUPER &&
+ (mid == rb_intern("initialize" )|| mid == rb_intern("initialize_copy"))) {
+ noex = NOEX_PRIVATE | noex;
+ }
+ else if (FL_TEST(klass, FL_SINGLETON) && node && nd_type(node) == NODE_CFUNC &&
+ mid == rb_intern("allocate")) {
+ rb_warn("defining %s.allocate is deprecated; use rb_define_alloc_func()",
+ rb_class2name(rb_iv_get(klass, "__attached__")));
+ mid = ID_ALLOCATOR;
}
if (OBJ_FROZEN(klass)) rb_error_frozen("class/module");
rb_clear_cache_by_id(mid);
body = NEW_METHOD(node, noex);
- st_insert(RCLASS(klass)->m_tbl, mid, body);
+ st_insert(RCLASS(klass)->m_tbl, mid, (st_data_t)body);
+ if (node && mid != ID_ALLOCATOR && ruby_running) {
+ if (FL_TEST(klass, FL_SINGLETON)) {
+ rb_funcall(rb_iv_get(klass, "__attached__"), singleton_added, 1, ID2SYM(mid));
+ }
+ else {
+ rb_funcall(klass, added, 1, ID2SYM(mid));
+ }
+ }
+}
+
+void
+rb_define_alloc_func(klass, func)
+ VALUE klass;
+ VALUE (*func) _((VALUE));
+{
+ Check_Type(klass, T_CLASS);
+ rb_add_method(CLASS_OF(klass), ID_ALLOCATOR, NEW_CFUNC(func, 0), NOEX_PRIVATE);
+}
+
+void
+rb_undef_alloc_func(klass)
+ VALUE klass;
+{
+ Check_Type(klass, T_CLASS);
+ rb_add_method(CLASS_OF(klass), ID_ALLOCATOR, 0, NOEX_UNDEF);
}
static NODE*
@@ -264,7 +330,7 @@ search_method(klass, id, origin)
NODE *body;
if (!klass) return 0;
- while (!st_lookup(RCLASS(klass)->m_tbl, id, &body)) {
+ while (!st_lookup(RCLASS(klass)->m_tbl, id, (st_data_t *)&body)) {
klass = RCLASS(klass)->super;
if (!klass) return 0;
}
@@ -297,26 +363,41 @@ rb_get_method_body(klassp, idp, noexp)
return 0;
}
- /* store in cache */
- ent = cache + EXPR1(klass, id);
- ent->klass = klass;
- ent->noex = body->nd_noex;
- body = body->nd_body;
- if (nd_type(body) == NODE_FBODY) {
- ent->mid = id;
- *klassp = body->nd_orig;
- ent->origin = body->nd_orig;
- *idp = ent->mid0 = body->nd_mid;
- body = ent->method = body->nd_head;
+ if (ruby_running) {
+ /* store in cache */
+ if (BUILTIN_TYPE(origin) == T_ICLASS) origin = RBASIC(origin)->klass;
+ ent = cache + EXPR1(klass, id);
+ ent->klass = klass;
+ ent->noex = body->nd_noex;
+ if (noexp) *noexp = body->nd_noex;
+ body = body->nd_body;
+ if (nd_type(body) == NODE_FBODY) {
+ ent->mid = id;
+ *klassp = body->nd_orig;
+ ent->origin = body->nd_orig;
+ *idp = ent->mid0 = body->nd_mid;
+ body = ent->method = body->nd_head;
+ }
+ else {
+ *klassp = origin;
+ ent->origin = origin;
+ ent->mid = ent->mid0 = id;
+ ent->method = body;
+ }
}
else {
- *klassp = origin;
- ent->origin = origin;
- ent->mid = ent->mid0 = id;
- ent->method = body;
+ if (noexp) *noexp = body->nd_noex;
+ body = body->nd_body;
+ if (nd_type(body) == NODE_FBODY) {
+ *klassp = body->nd_orig;
+ *idp = body->nd_mid;
+ body = body->nd_head;
+ }
+ else {
+ *klassp = origin;
+ }
}
- if (noexp) *noexp = ent->noex;
return body;
}
@@ -337,18 +418,12 @@ remove_method(klass, mid)
if (mid == __id__ || mid == __send__ || mid == init) {
rb_warn("removing `%s' may cause serious problem", rb_id2name(mid));
}
- if (mid == alloc) {
- if (klass == rb_cClass ||
- (FL_TEST(klass, FL_SINGLETON) &&
- rb_obj_is_kind_of(rb_iv_get(klass, "__attached__"), rb_cClass))) {
- rb_name_error(mid, "removing `%s'", rb_id2name(mid));
- }
- }
- if (!st_delete(RCLASS(klass)->m_tbl, &mid, &body) || !body->nd_body) {
+ if (!st_delete(RCLASS(klass)->m_tbl, &mid, (st_data_t *)&body) ||
+ !body->nd_body) {
rb_name_error(mid, "method `%s' not defined in %s",
rb_id2name(mid), rb_class2name(klass));
}
- rb_clear_cache_by_id(mid);
+ rb_clear_cache_for_undef(klass, mid);
if (FL_TEST(klass, FL_SINGLETON)) {
rb_funcall(rb_iv_get(klass, "__attached__"), singleton_removed, 1, ID2SYM(mid));
}
@@ -366,10 +441,16 @@ rb_remove_method(klass, name)
}
static VALUE
-rb_mod_remove_method(mod, name)
- VALUE mod, name;
+rb_mod_remove_method(argc, argv, mod)
+ int argc;
+ VALUE *argv;
+ VALUE mod;
{
- remove_method(mod, rb_to_id(name));
+ int i;
+
+ for (i=0; i<argc; i++) {
+ remove_method(mod, rb_to_id(argv[i]));
+ }
return mod;
}
@@ -378,20 +459,7 @@ rb_disable_super(klass, name)
VALUE klass;
const char *name;
{
- VALUE origin;
- NODE *body;
- ID mid = rb_intern(name);
-
- body = search_method(klass, mid, &origin);
- if (!body || !body->nd_body) {
- print_undef(klass, mid);
- }
- if (origin == klass) {
- body->nd_noex |= NOEX_NOSUPER;
- }
- else {
- rb_add_method(klass, mid, 0, NOEX_UNDEF);
- }
+ /* obsolete - no use */
}
void
@@ -399,20 +467,7 @@ rb_enable_super(klass, name)
VALUE klass;
const char *name;
{
- VALUE origin;
- NODE *body;
- ID mid = rb_intern(name);
-
- body = search_method(klass, mid, &origin);
- if (!body) {
- print_undef(klass, mid);
- }
- if (!body->nd_body) {
- remove_method(klass, mid);
- }
- else {
- body->nd_noex &= ~NOEX_NOSUPER;
- }
+ rb_warning("rb_enable_super() is obsolete");
}
static void
@@ -505,13 +560,11 @@ rb_attr(klass, id, read, write, ex)
attriv = rb_intern(buf);
if (read) {
rb_add_method(klass, id, NEW_IVAR(attriv), noex);
- rb_funcall(klass, added, 1, ID2SYM(id));
}
if (write) {
sprintf(buf, "%s=", name);
id = rb_intern(buf);
rb_add_method(klass, id, NEW_ATTRSET(attriv), noex);
- rb_funcall(klass, added, 1, ID2SYM(id));
}
}
@@ -569,12 +622,15 @@ struct BLOCK {
struct RVarmap *dyna_vars;
VALUE orig_thread;
VALUE wrapper;
+ VALUE block_obj;
+ struct BLOCK *outer;
struct BLOCK *prev;
};
#define BLOCK_D_SCOPE 1
#define BLOCK_DYNAMIC 2
#define BLOCK_ORPHAN 4
+#define BLOCK_LAMBDA 8
static struct BLOCK *ruby_block;
@@ -599,11 +655,13 @@ new_blktag()
_block.frame.node = ruby_current_node;\
_block.scope = ruby_scope; \
_block.prev = ruby_block; \
+ _block.outer = ruby_block; \
_block.iter = ruby_iter->iter; \
_block.vmode = scope_vmode; \
_block.flags = BLOCK_D_SCOPE; \
_block.dyna_vars = ruby_dyna_vars; \
_block.wrapper = ruby_wrapper; \
+ _block.block_obj = 0; \
ruby_block = &_block
#define POP_BLOCK() \
@@ -806,7 +864,7 @@ static struct tag *prot_tag;
#define PROT_FUNC -1
#define PROT_THREAD -2
-#define EXEC_TAG() setjmp(prot_tag->buf)
+#define EXEC_TAG() (FLUSH_REGISTER_WINDOWS, setjmp(prot_tag->buf))
#define JUMP_TAG(st) do { \
ruby_frame = prot_tag->frame; \
@@ -845,7 +903,7 @@ static VALUE ruby_wrapper; /* security wrapper */
static NODE *ruby_cref = 0;
static NODE *top_cref;
-#define PUSH_CREF(c) ruby_cref = rb_node_newnode(NODE_CREF,(c),0,ruby_cref)
+#define PUSH_CREF(c) ruby_cref = NEW_NODE(NODE_CREF,(c),0,ruby_cref)
#define POP_CREF() ruby_cref = ruby_cref->nd_next
#define PUSH_SCOPE() do { \
@@ -886,7 +944,13 @@ static VALUE rb_eval _((VALUE,NODE*));
static VALUE eval _((VALUE,VALUE,VALUE,char*,int));
static NODE *compile _((VALUE, char*, int));
-static VALUE rb_yield_0 _((VALUE, VALUE, VALUE, int));
+static VALUE rb_yield_0 _((VALUE, VALUE, VALUE, int, int));
+
+#define YIELD_PROC_CALL 1
+#define YIELD_PUBLIC_DEF 2
+#define YIELD_FUNC_AVALUE 1
+#define YIELD_FUNC_SVALUE 2
+
static VALUE rb_call _((VALUE,VALUE,ID,int,const VALUE*,int));
static VALUE module_setup _((VALUE,NODE*));
@@ -897,31 +961,57 @@ static VALUE trace_func = 0;
static int tracing = 0;
static void call_trace_func _((char*,NODE*,VALUE,ID,VALUE));
+#if 0
#define SET_CURRENT_SOURCE() (ruby_sourcefile = ruby_current_node->nd_file, \
ruby_sourceline = nd_line(ruby_current_node))
+#else
+#define SET_CURRENT_SOURCE() ((void)0)
+#endif
void
ruby_set_current_source()
{
if (ruby_current_node) {
- SET_CURRENT_SOURCE();
+ ruby_sourcefile = ruby_current_node->nd_file;
+ ruby_sourceline = nd_line(ruby_current_node);
}
}
static void
+#ifdef HAVE_STDARG_PROTOTYPES
+warn_printf(const char *fmt, ...)
+#else
+warn_printf(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ char buf[BUFSIZ];
+ va_list args;
+
+ va_init_list(args, fmt);
+ vsnprintf(buf, BUFSIZ, fmt, args);
+ va_end(args);
+ rb_write_error(buf);
+}
+
+#define warn_print(x) rb_write_error(x)
+#define warn_print2(x,l) rb_write_error2(x,l)
+
+static void
error_pos()
{
ruby_set_current_source();
if (ruby_sourcefile) {
if (ruby_frame->last_func) {
- fprintf(stderr, "%s:%d:in `%s'", ruby_sourcefile, ruby_sourceline,
- rb_id2name(ruby_frame->last_func));
+ warn_printf("%s:%d:in `%s'", ruby_sourcefile, ruby_sourceline,
+ rb_id2name(ruby_frame->last_func));
}
else if (ruby_sourceline == 0) {
- fprintf(stderr, "%s", ruby_sourcefile);
+ warn_printf("%s", ruby_sourcefile);
}
else {
- fprintf(stderr, "%s:%d", ruby_sourcefile, ruby_sourceline);
+ warn_printf("%s:%d", ruby_sourcefile, ruby_sourceline);
}
}
}
@@ -945,7 +1035,7 @@ static void
error_print()
{
VALUE errat = Qnil; /* OK */
- volatile VALUE eclass;
+ volatile VALUE eclass, e;
char *einfo;
long elen;
@@ -958,13 +1048,13 @@ error_print()
else {
errat = Qnil;
}
- POP_TAG();
+ if (EXEC_TAG()) goto error;
if (NIL_P(errat)){
ruby_set_current_source();
if (ruby_sourcefile)
- fprintf(stderr, "%s:%d", ruby_sourcefile, ruby_sourceline);
+ warn_printf("%s:%d", ruby_sourcefile, ruby_sourceline);
else
- fprintf(stderr, "%d", ruby_sourceline);
+ warn_printf("%d", ruby_sourceline);
}
else if (RARRAY(errat)->len == 0) {
error_pos();
@@ -974,14 +1064,13 @@ error_print()
if (NIL_P(mesg)) error_pos();
else {
- fwrite(RSTRING(mesg)->ptr, 1, RSTRING(mesg)->len, stderr);
+ warn_print2(RSTRING(mesg)->ptr, RSTRING(mesg)->len);
}
}
eclass = CLASS_OF(ruby_errinfo);
- PUSH_TAG(PROT_NONE);
if (EXEC_TAG() == 0) {
- VALUE e = rb_obj_as_string(ruby_errinfo);
+ e = rb_obj_as_string(ruby_errinfo);
einfo = RSTRING(e)->ptr;
elen = RSTRING(e)->len;
}
@@ -989,38 +1078,36 @@ error_print()
einfo = "";
elen = 0;
}
- POP_TAG();
+ if (EXEC_TAG()) goto error;
if (eclass == rb_eRuntimeError && elen == 0) {
- fprintf(stderr, ": unhandled exception\n");
+ warn_print(": unhandled exception\n");
}
else {
VALUE epath;
epath = rb_class_path(eclass);
if (elen == 0) {
- fprintf(stderr, ": ");
- fwrite(RSTRING(epath)->ptr, 1, RSTRING(epath)->len, stderr);
- putc('\n', stderr);
+ warn_print(": ");
+ warn_print2(RSTRING(epath)->ptr, RSTRING(epath)->len);
}
else {
char *tail = 0;
long len = elen;
if (RSTRING(epath)->ptr[0] == '#') epath = 0;
- if (tail = strchr(einfo, '\n')) {
+ if (tail = memchr(einfo, '\n', elen)) {
len = tail - einfo;
tail++; /* skip newline */
}
- fprintf(stderr, ": ");
- fwrite(einfo, 1, len, stderr);
+ warn_print(": ");
+ warn_print2(einfo, len);
if (epath) {
- fprintf(stderr, " (");
- fwrite(RSTRING(epath)->ptr, 1, RSTRING(epath)->len, stderr);
- fprintf(stderr, ")\n");
+ warn_print(" (");
+ warn_print2(RSTRING(epath)->ptr, RSTRING(epath)->len);
+ warn_print(")\n");
}
if (tail) {
- fwrite(tail, 1, elen-len-1, stderr);
- putc('\n', stderr);
+ warn_print2(tail, elen-len-1);
}
}
}
@@ -1036,16 +1123,17 @@ error_print()
ep = RARRAY(errat);
for (i=1; i<ep->len; i++) {
if (TYPE(ep->ptr[i]) == T_STRING) {
- fprintf(stderr, "\tfrom %s\n", RSTRING(ep->ptr[i])->ptr);
+ warn_printf("\tfrom %s\n", RSTRING(ep->ptr[i])->ptr);
}
if (i == TRACE_HEAD && ep->len > TRACE_MAX) {
- fprintf(stderr, "\t ... %ld levels...\n",
+ warn_printf("\t ... %ld levels...\n",
ep->len - TRACE_HEAD - TRACE_TAIL);
i = ep->len - TRACE_TAIL;
}
}
}
- fflush(stderr);
+ error:
+ POP_TAG();
}
#if defined(__APPLE__)
@@ -1103,11 +1191,16 @@ ruby_init()
_macruby_init();
#endif
ruby_prog_init();
+ ALLOW_INTS;
}
POP_TAG();
- if (state) error_print();
+ if (state) {
+ error_print();
+ exit(1);
+ }
POP_SCOPE();
ruby_scope = top_scope;
+ ruby_running = 1;
}
static VALUE
@@ -1131,10 +1224,18 @@ int ruby_in_eval;
static void rb_thread_cleanup _((void));
static void rb_thread_wait_other_threads _((void));
+static int thread_set_raised();
+static void thread_reset_raised();
+
+static VALUE exception_error;
+static VALUE sysstack_error;
+
static int
error_handle(ex)
int ex;
{
+ if (thread_set_raised()) return 1;
+
switch (ex & TAG_MASK) {
case 0:
ex = 0;
@@ -1142,38 +1243,38 @@ error_handle(ex)
case TAG_RETURN:
error_pos();
- fprintf(stderr, ": unexpected return\n");
+ warn_print(": unexpected return\n");
ex = 1;
break;
case TAG_NEXT:
error_pos();
- fprintf(stderr, ": unexpected next\n");
+ warn_print(": unexpected next\n");
ex = 1;
break;
case TAG_BREAK:
error_pos();
- fprintf(stderr, ": unexpected break\n");
+ warn_print(": unexpected break\n");
ex = 1;
break;
case TAG_REDO:
error_pos();
- fprintf(stderr, ": unexpected redo\n");
+ warn_print(": unexpected redo\n");
ex = 1;
break;
case TAG_RETRY:
error_pos();
- fprintf(stderr, ": retry outside of rescue clause\n");
+ warn_print(": retry outside of rescue clause\n");
ex = 1;
break;
case TAG_THROW:
if (prot_tag && prot_tag->frame && prot_tag->frame->node) {
NODE *tag = prot_tag->frame->node;
- fprintf(stderr, "%s:%d: uncaught throw\n",
+ warn_printf("%s:%d: uncaught throw\n",
tag->nd_file, nd_line(tag));
}
else {
error_pos();
- fprintf(stderr, ": unexpected throw\n");
+ warn_printf(": unexpected throw\n");
}
ex = 1;
break;
@@ -1192,6 +1293,7 @@ error_handle(ex)
rb_bug("Unknown longjmp status %d", ex);
break;
}
+ thread_reset_raised();
return ex;
}
@@ -1217,18 +1319,30 @@ ruby_options(argc, argv)
void rb_exec_end_proc _((void));
-void
-ruby_finalize()
+static void
+ruby_finalize_0(exp)
+ int *exp;
{
- int state;
-
+ ruby_errinfo = 0;
PUSH_TAG(PROT_NONE);
- if ((state = EXEC_TAG()) == 0) {
+ if (EXEC_TAG() == 0) {
rb_trap_exit();
- rb_exec_end_proc();
- rb_gc_call_finalizer_at_exit();
}
POP_TAG();
+ rb_exec_end_proc();
+ rb_gc_call_finalizer_at_exit();
+ if (exp && ruby_errinfo && rb_obj_is_kind_of(ruby_errinfo, rb_eSystemExit)) {
+ VALUE st = rb_iv_get(ruby_errinfo, "status");
+ *exp = NUM2INT(st);
+ }
+ trace_func = 0;
+ tracing = 0;
+}
+
+void
+ruby_finalize()
+{
+ ruby_finalize_0(0);
}
int
@@ -1237,6 +1351,7 @@ ruby_cleanup(ex)
{
int state;
+ ruby_safe_level = 0;
PUSH_TAG(PROT_NONE);
PUSH_ITER(ITER_NOT);
if ((state = EXEC_TAG()) == 0) {
@@ -1248,11 +1363,9 @@ ruby_cleanup(ex)
}
POP_ITER();
- trace_func = 0;
- tracing = 0;
ex = error_handle(ex);
POP_TAG();
- ruby_finalize();
+ ruby_finalize_0(&ex);
return ex;
}
@@ -1287,6 +1400,7 @@ ruby_run()
{
int state;
static int ex;
+
if (ruby_nerrs > 0) exit(ruby_nerrs);
state = ruby_exec();
if (state && !ex) ex = state;
@@ -1307,7 +1421,7 @@ compile_error(at)
}
rb_str_buf_cat(str, "\n", 1);
if (!NIL_P(ruby_errinfo)) {
- rb_str_append(str, ruby_errinfo);
+ rb_str_append(str, rb_obj_as_string(ruby_errinfo));
}
rb_exc_raise(rb_exc_new3(rb_eSyntaxError, str));
}
@@ -1389,20 +1503,45 @@ rb_eval_string_wrap(str, state)
}
static void
-localjump_error(mesg, status)
+localjump_error(mesg, status, reason)
const char *mesg;
VALUE status;
+ int reason;
{
VALUE exc = rb_exc_new2(rb_eLocalJumpError, mesg);
- rb_iv_set(exc, "@status", status);
+ VALUE id;
+
+ rb_iv_set(exc, "@exit_value", status);
+ switch (reason) {
+ case TAG_BREAK:
+ id = rb_intern("break"); break;
+ case TAG_REDO:
+ id = rb_intern("redo"); break;
+ case TAG_RETRY:
+ id = rb_intern("retry"); break;
+ case TAG_NEXT:
+ id = rb_intern("next"); break;
+ case TAG_RETURN:
+ id = rb_intern("return"); break;
+ default:
+ id = rb_intern("noreason"); break;
+ }
+ rb_iv_set(exc, "@reason", ID2SYM(id));
rb_exc_raise(exc);
}
static VALUE
-localjump_exitstatus(exc)
+localjump_xvalue(exc)
+ VALUE exc;
+{
+ return rb_iv_get(exc, "@exit_value");
+}
+
+static VALUE
+localjump_reason(exc)
VALUE exc;
{
- return rb_iv_get(exc, "@status");
+ return rb_iv_get(exc, "@reason");
}
static void
@@ -1417,19 +1556,19 @@ jump_tag_but_local_jump(state)
case 0:
break;
case TAG_RETURN:
- localjump_error("unexpected return", val);
+ localjump_error("unexpected return", val, state);
break;
case TAG_NEXT:
- localjump_error("unexpected next", val);
+ localjump_error("unexpected next", val, state);
break;
case TAG_BREAK:
- localjump_error("unexpected break", val);
+ localjump_error("unexpected break", val, state);
break;
case TAG_REDO:
- localjump_error("unexpected redo", Qnil);
+ localjump_error("unexpected redo", Qnil, state);
break;
case TAG_RETRY:
- localjump_error("retry outside of rescue clause", Qnil);
+ localjump_error("retry outside of rescue clause", Qnil, state);
break;
default:
JUMP_TAG(state);
@@ -1511,7 +1650,7 @@ superclass(self, node)
}
if (TYPE(val) != T_CLASS) {
rb_raise(rb_eTypeError, "superclass must be a Class (%s given)",
- rb_class2name(CLASS_OF(val)));
+ rb_obj_classname(val));
}
if (FL_TEST(val, FL_SINGLETON)) {
rb_raise(rb_eTypeError, "can't make subclass of virtual class");
@@ -1529,12 +1668,16 @@ ev_const_defined(cref, id, self)
VALUE self;
{
NODE *cbase = cref;
+ VALUE result;
while (cbase && cbase->nd_next) {
struct RClass *klass = RCLASS(cbase->nd_clss);
if (NIL_P(klass)) return rb_const_defined(CLASS_OF(self), id);
- if (klass->iv_tbl && st_lookup(klass->iv_tbl, id, 0)) {
+ if (klass->iv_tbl && st_lookup(klass->iv_tbl, id, &result)) {
+ if (result == Qundef && NIL_P(rb_autoload_p((VALUE)klass, id))) {
+ return Qfalse;
+ }
return Qtrue;
}
cbase = cbase->nd_next;
@@ -1555,7 +1698,11 @@ ev_const_get(cref, id, self)
VALUE klass = cbase->nd_clss;
if (NIL_P(klass)) return rb_const_get(CLASS_OF(self), id);
- if (RCLASS(klass)->iv_tbl && st_lookup(RCLASS(klass)->iv_tbl, id, &result)) {
+ while (RCLASS(klass)->iv_tbl && st_lookup(RCLASS(klass)->iv_tbl, id, &result)) {
+ if (result == Qundef) {
+ rb_autoload_load(klass, id);
+ continue;
+ }
return result;
}
cbase = cbase->nd_next;
@@ -1649,7 +1796,7 @@ rb_undef(klass, id)
rb_raise(rb_eSecurityError, "Insecure: can't undef `%s'", rb_id2name(id));
}
rb_frozen_class_p(klass);
- if (id == __id__ || id == __send__ || id == init || id == alloc) {
+ if (id == __id__ || id == __send__ || id == init) {
rb_warn("undefining `%s' may cause serious problem", rb_id2name(id));
}
body = search_method(klass, id, &origin);
@@ -1684,10 +1831,16 @@ rb_undef(klass, id)
}
static VALUE
-rb_mod_undef_method(mod, name)
- VALUE mod, name;
+rb_mod_undef_method(argc, argv, mod)
+ int argc;
+ VALUE *argv;
+ VALUE mod;
{
- rb_undef(mod, rb_to_id(name));
+ int i;
+
+ for (i=0; i<argc; i++) {
+ rb_undef(mod, rb_to_id(argv[i]));
+ }
return mod;
}
@@ -1716,9 +1869,6 @@ rb_alias(klass, name, def)
}
if (FL_TEST(klass, FL_SINGLETON)) {
singleton = rb_iv_get(klass, "__attached__");
- if (name == alloc && TYPE(singleton) == T_CLASS) {
- rb_raise(rb_eNameError, "cannot make alias named `allocate'");
- }
}
body = orig->nd_body;
orig->nd_cnt++;
@@ -1730,7 +1880,7 @@ rb_alias(klass, name, def)
rb_clear_cache_by_id(name);
st_insert(RCLASS(klass)->m_tbl, name,
- NEW_METHOD(NEW_FBODY(body, def, origin), orig->nd_noex));
+ (st_data_t)NEW_METHOD(NEW_FBODY(body, def, origin), orig->nd_noex));
if (singleton) {
rb_funcall(singleton, singleton_added, 1, ID2SYM(name));
}
@@ -1750,9 +1900,9 @@ rb_mod_alias_method(mod, newname, oldname)
static NODE*
copy_node_scope(node, rval)
NODE *node;
- VALUE rval;
+ NODE *rval;
{
- NODE *copy = rb_node_newnode(NODE_SCOPE,0,rval,node->nd_next);
+ NODE *copy = NEW_NODE(NODE_SCOPE,0,rval,node->nd_next);
if (node->nd_tbl) {
copy->nd_tbl = ALLOC_N(ID, node->nd_tbl[0]+1);
@@ -1775,14 +1925,14 @@ copy_node_scope(node, rval)
# define TMP_ALLOC(n) ALLOCA_N(VALUE,n)
#endif
-#define SETUP_ARGS(anode) do {\
+#define SETUP_ARGS0(anode,alen) do {\
NODE *n = anode;\
if (!n) {\
argc = 0;\
argv = 0;\
}\
else if (nd_type(n) == NODE_ARRAY) {\
- argc=n->nd_alen;\
+ argc=alen;\
if (argc > 0) {\
int i;\
n = anode;\
@@ -1807,10 +1957,12 @@ copy_node_scope(node, rval)
}\
} while (0)
+#define SETUP_ARGS(anode) SETUP_ARGS0(anode, anode->nd_alen)
+
#define BEGIN_CALLARGS do {\
struct BLOCK *tmp_block = ruby_block;\
if (ruby_iter->iter == ITER_PRE) {\
- ruby_block = ruby_block->prev;\
+ ruby_block = ruby_block->outer;\
}\
PUSH_ITER(ITER_NOT)
@@ -1880,6 +2032,9 @@ is_defined(self, node, buf)
val = self;
goto check_bound;
+ case NODE_ATTRASGN:
+ val = self;
+ if (node->nd_recv == (NODE *)1) goto check_bound;
case NODE_CALL:
PUSH_TAG(PROT_NONE);
if ((state = EXEC_TAG()) == 0) {
@@ -1909,7 +2064,9 @@ is_defined(self, node, buf)
}
else if (!rb_method_boundp(val, node->nd_mid, call))
break;
- return arg_defined(self, node->nd_args, buf, "method");
+ return arg_defined(self, node->nd_args, buf,
+ nd_type(node) == NODE_ATTRASGN ?
+ "assignment" : "method");
}
break;
@@ -1991,7 +2148,7 @@ is_defined(self, node, buf)
switch (TYPE(val)) {
case T_CLASS:
case T_MODULE:
- if (rb_const_defined_at(val, node->nd_mid))
+ if (rb_const_defined_from(val, node->nd_mid))
return "constant";
break;
default:
@@ -2004,14 +2161,14 @@ is_defined(self, node, buf)
case NODE_NTH_REF:
if (RTEST(rb_reg_nth_defined(node->nd_nth, MATCH_DATA))) {
- sprintf(buf, "$%d", node->nd_nth);
+ sprintf(buf, "$%d", (int)node->nd_nth);
return buf;
}
break;
case NODE_BACK_REF:
if (RTEST(rb_reg_nth_defined(0, MATCH_DATA))) {
- sprintf(buf, "$%c", node->nd_nth);
+ sprintf(buf, "$%c", (char)node->nd_nth);
return buf;
}
break;
@@ -2040,20 +2197,10 @@ static int handle_rescue _((VALUE,NODE*));
static void blk_free();
static VALUE
-rb_obj_is_block(block)
- VALUE block;
-{
- if (TYPE(block) == T_DATA && RDATA(block)->dfree == (RUBY_DATA_FUNC)blk_free) {
- return Qtrue;
- }
- return Qfalse;
-}
-
-static VALUE
rb_obj_is_proc(proc)
VALUE proc;
{
- if (rb_obj_is_block(proc) && rb_obj_is_kind_of(proc, rb_cProc)) {
+ if (TYPE(proc) == T_DATA && RDATA(proc)->dfree == (RUBY_DATA_FUNC)blk_free) {
return Qtrue;
}
return Qfalse;
@@ -2083,16 +2230,16 @@ call_trace_func(event, node, self, id, klass)
{
int state;
struct FRAME *prev;
- NODE *node_save[2];
+ NODE *node_save;
VALUE srcfile;
if (!trace_func) return;
if (tracing) return;
if (ruby_in_compile) return;
+ if (id == ID_ALLOCATOR) return;
- node_save[0] = ruby_last_node;
- if (!(node_save[1] = ruby_current_node)) {
- node_save[1] = NEW_NEWLINE(0);
+ if (!(node_save = ruby_current_node)) {
+ node_save = NEW_NEWLINE(0);
}
tracing = 1;
prev = ruby_frame;
@@ -2124,57 +2271,87 @@ call_trace_func(event, node, self, id, klass)
id?ID2SYM(id):Qnil,
self?rb_f_binding(self):Qnil,
klass),
- Qtrue, Qundef);
+ Qundef, 0);
}
POP_TMPTAG(); /* do not propagate retval */
POP_FRAME();
tracing = 0;
- ruby_last_node = node_save[0];
- ruby_current_node = node_save[1];
+ ruby_current_node = node_save;
SET_CURRENT_SOURCE();
if (state) JUMP_TAG(state);
}
static VALUE
+avalue_to_svalue(v)
+ VALUE v;
+{
+ VALUE tmp, top;
+
+ tmp = rb_check_array_type(v);
+ if (NIL_P(tmp)) {
+ return v;
+ }
+ if (RARRAY(tmp)->len == 0) {
+ return Qundef;
+ }
+ if (RARRAY(tmp)->len == 1) {
+ top = rb_check_array_type(RARRAY(tmp)->ptr[0]);
+ if (NIL_P(top)) {
+ return RARRAY(tmp)->ptr[0];
+ }
+ if (RARRAY(top)->len > 1) {
+ return v;
+ }
+ return top;
+ }
+ return tmp;
+}
+
+static VALUE
svalue_to_avalue(v)
VALUE v;
{
- if (NIL_P(v)) return rb_ary_new2(0);
+ VALUE tmp, top;
+
if (v == Qundef) return rb_ary_new2(0);
- if (TYPE(v) == T_ARRAY) {
- if (RARRAY(v)->len > 1) return v;
+ tmp = rb_check_array_type(v);
+ if (NIL_P(tmp)) {
return rb_ary_new3(1, v);
}
- else {
- v = rb_ary_to_ary(v);
+ if (RARRAY(tmp)->len == 1) {
+ top = rb_check_array_type(RARRAY(tmp)->ptr[0]);
+ if (!NIL_P(top) && RARRAY(top)->len > 1) {
+ return v;
+ }
+ return rb_ary_new3(1, v);
}
- return v;
+ return tmp;
}
static VALUE
-avalue_to_svalue(v)
+svalue_to_mrhs(v, lhs)
VALUE v;
+ NODE *lhs;
{
- if (TYPE(v) != T_ARRAY) {
- v = rb_ary_to_ary(v);
- }
- if (RARRAY(v)->len == 0) {
- return Qnil;
+ VALUE tmp;
+
+ if (v == Qundef) return rb_ary_new2(0);
+ tmp = rb_check_array_type(v);
+ if (NIL_P(tmp)) {
+ return rb_ary_new3(1, v);
}
- if (RARRAY(v)->len == 1) {
- return RARRAY(v)->ptr[0];
+ /* no lhs means splat lhs only */
+ if (!lhs && RARRAY(tmp)->len <= 1) {
+ return rb_ary_new3(1, v);
}
- return v;
+ return tmp;
}
static VALUE
-avalue_to_yvalue(v)
+avalue_splat(v)
VALUE v;
{
- if (TYPE(v) != T_ARRAY) {
- v = rb_ary_to_ary(v);
- }
if (RARRAY(v)->len == 0) {
return Qundef;
}
@@ -2184,46 +2361,86 @@ avalue_to_yvalue(v)
return v;
}
-static VALUE
-svalue_to_mvalue(v)
- VALUE v;
+#if 1
+VALUE
+rb_Array(val)
+ VALUE val;
{
- if (v == Qnil || v == Qundef)
- return rb_ary_new2(0);
- if (TYPE(v) == T_ARRAY) {
- return v;
- }
- else {
- v = rb_ary_to_ary(v);
+ VALUE tmp = rb_check_array_type(val);
+
+ if (NIL_P(tmp)) {
+ /* hack to avoid invoke Object#to_a */
+ VALUE origin;
+ ID id = rb_intern("to_a");
+
+ if (search_method(CLASS_OF(val), id, &origin) &&
+ RCLASS(origin)->m_tbl != RCLASS(rb_mKernel)->m_tbl) { /* exclude Kernel#to_a */
+ val = rb_funcall(val, id, 0);
+ if (TYPE(val) != T_ARRAY) {
+ rb_raise(rb_eTypeError, "`to_a' did not return Array");
+ }
+ return val;
+ }
+ else {
+ return rb_ary_new3(1, val);
+ }
}
- return v;
+ return tmp;
}
+#endif
static VALUE
-mvalue_to_svalue(v)
+splat_value(v)
VALUE v;
{
- if (TYPE(v) != T_ARRAY) {
- v = rb_ary_to_ary(v);
+ if (NIL_P(v)) return rb_ary_new3(1, Qnil);
+ return rb_Array(v);
+}
+
+static VALUE
+class_prefix(self, cpath)
+ VALUE self;
+ NODE *cpath;
+{
+ if (!cpath) {
+ rb_bug("class path missing");
}
- if (RARRAY(v)->len == 0) {
- return Qnil;
+ if (cpath->nd_head) {
+ VALUE c = rb_eval(self, cpath->nd_head);
+ switch (TYPE(c)) {
+ case T_CLASS:
+ case T_MODULE:
+ break;
+ default:
+ rb_raise(rb_eTypeError, "%s is not a class/module",
+ RSTRING(rb_obj_as_string(c))->ptr);
+ }
+ return c;
}
- if (RARRAY(v)->len == 1 && TYPE(RARRAY(v)->ptr[0]) != T_ARRAY) {
- return RARRAY(v)->ptr[0];
+ else if (nd_type(cpath) == NODE_COLON2) {
+ return ruby_cbase;
+ }
+ else if (ruby_wrapper) {
+ return ruby_wrapper;
+ }
+ else {
+ return rb_cObject;
}
- return v;
}
static void return_check _((void));
-#define return_value(v) prot_tag->retval = (v)
+#define return_value(v) do {\
+ if ((prot_tag->retval = (v)) == Qundef) {\
+ prot_tag->retval = Qnil;\
+ }\
+} while (0)
static VALUE
rb_eval(self, n)
VALUE self;
NODE *n;
{
- NODE *nodesave = ruby_current_node;
+ NODE * volatile contnode = 0;
NODE * volatile node = n;
int state;
volatile VALUE result = Qnil;
@@ -2236,13 +2453,14 @@ rb_eval(self, n)
again:
if (!node) RETURN(Qnil);
- ruby_last_node = ruby_current_node = node;
+ ruby_current_node = node;
switch (nd_type(node)) {
case NODE_BLOCK:
- while (node->nd_next) {
- rb_eval(self, node->nd_head);
- node = node->nd_next;
+ if (contnode) {
+ result = rb_eval(self, node);
+ break;
}
+ contnode = node->nd_next;
node = node->nd_head;
goto again;
@@ -2537,23 +2755,13 @@ rb_eval(self, n)
break;
case NODE_BREAK:
- if (node->nd_stts) {
- return_value(avalue_to_svalue(rb_eval(self, node->nd_stts)));
- }
- else {
- return_value(Qnil);
- }
+ return_value(rb_eval(self, node->nd_stts));
JUMP_TAG(TAG_BREAK);
break;
case NODE_NEXT:
CHECK_INTS;
- if (node->nd_stts) {
- return_value(avalue_to_svalue(rb_eval(self, node->nd_stts)));
- }
- else {
- return_value(Qnil);
- }
+ return_value(rb_eval(self, node->nd_stts));
JUMP_TAG(TAG_NEXT);
break;
@@ -2567,24 +2775,29 @@ rb_eval(self, n)
JUMP_TAG(TAG_RETRY);
break;
- case NODE_RESTARGS:
case NODE_RESTARY:
- result = rb_ary_to_ary(rb_eval(self, node->nd_head));
+ case NODE_RESTARY2:
+ result = splat_value(rb_eval(self, node->nd_head));
+ break;
+
+ case NODE_SPLAT:
+ result = avalue_splat(splat_value(rb_eval(self, node->nd_head)));
break;
- case NODE_REXPAND:
- result = avalue_to_svalue(rb_eval(self, node->nd_head));
+ case NODE_SVALUE:
+ result = rb_eval(self, node->nd_head);
+ if (result == Qundef) result = Qnil;
break;
case NODE_YIELD:
- if (node->nd_stts) {
- result = avalue_to_yvalue(rb_eval(self, node->nd_stts));
+ if (node->nd_head) {
+ result = rb_eval(self, node->nd_head);
}
else {
result = Qundef; /* no arg */
}
SET_CURRENT_SOURCE();
- result = rb_yield_0(result, 0, 0, 0);
+ result = rb_yield_0(result, 0, 0, 0, node->nd_state);
break;
case NODE_RESCUE:
@@ -2722,19 +2935,14 @@ rb_eval(self, n)
break;
case NODE_RETURN:
- if (node->nd_stts) {
- return_value(avalue_to_svalue(rb_eval(self, node->nd_stts)));
- }
- else {
- return_value(Qnil);
- }
+ return_value(rb_eval(self, node->nd_stts));
return_check();
JUMP_TAG(TAG_RETURN);
break;
case NODE_ARGSCAT:
result = rb_ary_concat(rb_eval(self, node->nd_head),
- rb_eval(self, node->nd_body));
+ splat_value(rb_eval(self, node->nd_body)));
break;
case NODE_ARGSPUSH:
@@ -2742,6 +2950,31 @@ rb_eval(self, n)
rb_eval(self, node->nd_body));
break;
+ case NODE_ATTRASGN:
+ {
+ VALUE recv;
+ int argc; VALUE *argv; /* used in SETUP_ARGS */
+ int scope;
+ TMP_PROTECT;
+
+ BEGIN_CALLARGS;
+ if (node->nd_recv == (NODE *)1) {
+ recv = self;
+ scope = 1;
+ }
+ else {
+ recv = rb_eval(self, node->nd_recv);
+ scope = 0;
+ }
+ SETUP_ARGS(node->nd_args);
+ END_CALLARGS;
+
+ SET_CURRENT_SOURCE();
+ rb_call(CLASS_OF(recv),recv,node->nd_mid,argc,argv,scope);
+ result = argv[argc-1];
+ }
+ break;
+
case NODE_CALL:
{
VALUE recv;
@@ -2803,12 +3036,8 @@ rb_eval(self, n)
END_CALLARGS;
}
- PUSH_ITER(ruby_iter->iter?ITER_PRE:ITER_NOT);
SET_CURRENT_SOURCE();
- result = rb_call(RCLASS(ruby_frame->last_class)->super,
- ruby_frame->self, ruby_frame->orig_func,
- argc, argv, 3);
- POP_ITER();
+ result = rb_call_super(argc, argv);
}
break;
@@ -2860,7 +3089,7 @@ rb_eval(self, n)
recv = rb_eval(self, node->nd_recv);
rval = node->nd_args->nd_head;
- SETUP_ARGS(node->nd_args->nd_next);
+ SETUP_ARGS0(node->nd_args->nd_next, node->nd_args->nd_alen - 1);
val = rb_funcall2(recv, aref, argc-1, argv);
switch (node->nd_mid) {
case 0: /* OR */
@@ -2921,7 +3150,8 @@ rb_eval(self, n)
break;
case NODE_MASGN:
- result = massign(self, node, rb_eval(self, node->nd_value),0);
+ result = svalue_to_mrhs(rb_eval(self, node->nd_value), node->nd_head);
+ result = massign(self, node, result, 0);
break;
case NODE_LASGN:
@@ -2952,11 +3182,16 @@ rb_eval(self, n)
break;
case NODE_CDECL:
- if (NIL_P(ruby_cbase)) {
- rb_raise(rb_eTypeError, "no class/module to define constant");
- }
result = rb_eval(self, node->nd_value);
- rb_const_set(ruby_cbase, node->nd_vid, result);
+ if (node->nd_vid == 0) {
+ rb_const_set(class_prefix(self, node->nd_else), node->nd_else->nd_mid, result);
+ }
+ else {
+ if (NIL_P(ruby_cbase)) {
+ rb_raise(rb_eTypeError, "no class/module to define constant");
+ }
+ rb_const_set(ruby_cbase, node->nd_vid, result);
+ }
break;
case NODE_CVDECL:
@@ -3003,7 +3238,7 @@ rb_eval(self, n)
if (ruby_scope->local_vars == 0)
rb_bug("unexpected block argument");
if (rb_block_given_p()) {
- result = rb_f_lambda();
+ result = rb_block_proc();
ruby_scope->local_vars[node->nd_cnt] = result;
}
else {
@@ -3016,15 +3251,22 @@ rb_eval(self, n)
VALUE klass;
klass = rb_eval(self, node->nd_head);
- switch (TYPE(klass)) {
- case T_CLASS:
- case T_MODULE:
- result = rb_const_get(klass, node->nd_mid);
- break;
- default:
+ if (rb_is_const_id(node->nd_mid)) {
+ switch (TYPE(klass)) {
+ case T_CLASS:
+ case T_MODULE:
+ result = rb_const_get_from(klass, node->nd_mid);
+ break;
+ default:
+ rb_raise(rb_eTypeError, "%s is not a class/module",
+ RSTRING(rb_obj_as_string(klass))->ptr);
+ break;
+ }
+ }
+ else {
result = rb_funcall(klass, node->nd_mid, 0, 0);
- break;
}
+ break;
}
break;
@@ -3131,11 +3373,11 @@ rb_eval(self, n)
switch (nd_type(node)) {
case NODE_DREGX:
result = rb_reg_new(RSTRING(str)->ptr, RSTRING(str)->len,
- node->nd_cflag);
+ node->nd_cflag);
break;
case NODE_DREGX_ONCE: /* regexp expand once */
result = rb_reg_new(RSTRING(str)->ptr, RSTRING(str)->len,
- node->nd_cflag);
+ node->nd_cflag);
nd_set_type(node, NODE_LIT);
node->nd_lit = result;
break;
@@ -3176,9 +3418,6 @@ rb_eval(self, n)
if (NIL_P(ruby_class)) {
rb_raise(rb_eTypeError, "no class/module to add method");
}
- if (ruby_class == rb_cClass && node->nd_mid == alloc) {
- rb_raise(rb_eNameError, "redefining Class#allocate will cause infinite loop");
- }
if (ruby_class == rb_cObject && node->nd_mid == init) {
rb_warn("redefining Object#initialize may cause infinite loop");
}
@@ -3189,15 +3428,12 @@ rb_eval(self, n)
rb_frozen_class_p(ruby_class);
body = search_method(ruby_class, node->nd_mid, &origin);
if (body){
- if (RTEST(ruby_verbose) && ruby_class == origin && body->nd_cnt == 0) {
+ if (RTEST(ruby_verbose) && ruby_class == origin && body->nd_cnt == 0 && body->nd_body) {
rb_warning("method redefined; discarding old %s", rb_id2name(node->nd_mid));
}
}
- if (node->nd_noex == NOEX_PUBLIC) {
- noex = NOEX_PUBLIC; /* means is is an attrset */
- }
- else if (SCOPE_TEST(SCOPE_PRIVATE) || node->nd_mid == init) {
+ if (SCOPE_TEST(SCOPE_PRIVATE) || node->nd_mid == init) {
noex = NOEX_PRIVATE;
}
else if (SCOPE_TEST(SCOPE_PROTECTED)) {
@@ -3215,14 +3451,6 @@ rb_eval(self, n)
if (scope_vmode == SCOPE_MODFUNC) {
rb_add_method(rb_singleton_class(ruby_class),
node->nd_mid, defn, NOEX_PUBLIC);
- rb_funcall(ruby_class, singleton_added, 1, ID2SYM(node->nd_mid));
- }
- if (FL_TEST(ruby_class, FL_SINGLETON)) {
- rb_funcall(rb_iv_get(ruby_class, "__attached__"),
- singleton_added, 1, ID2SYM(node->nd_mid));
- }
- else {
- rb_funcall(ruby_class, added, 1, ID2SYM(node->nd_mid));
}
result = Qnil;
}
@@ -3241,12 +3469,12 @@ rb_eval(self, n)
rb_raise(rb_eTypeError,
"can't define singleton method \"%s\" for %s",
rb_id2name(node->nd_mid),
- rb_class2name(CLASS_OF(recv)));
+ rb_obj_classname(recv));
}
if (OBJ_FROZEN(recv)) rb_error_frozen("object");
klass = rb_singleton_class(recv);
- if (st_lookup(RCLASS(klass)->m_tbl, node->nd_mid, &body)) {
+ if (st_lookup(RCLASS(klass)->m_tbl, node->nd_mid, (st_data_t *)&body)) {
if (ruby_safe_level >= 4) {
rb_raise(rb_eSecurityError, "redefining method prohibited");
}
@@ -3258,7 +3486,6 @@ rb_eval(self, n)
defn->nd_rval = (VALUE)ruby_cref;
rb_add_method(klass, node->nd_mid, defn,
NOEX_PUBLIC|(body?body->nd_noex&NOEX_UNDEF:0));
- rb_funcall(recv, singleton_added, 1, ID2SYM(node->nd_mid));
result = Qnil;
}
break;
@@ -3286,7 +3513,8 @@ rb_eval(self, n)
case NODE_CLASS:
{
- VALUE super, klass, tmp;
+ VALUE super, klass, tmp, cbase;
+ ID cname;
if (NIL_P(ruby_cbase)) {
rb_raise(rb_eTypeError, "no outer class/module");
@@ -3298,14 +3526,13 @@ rb_eval(self, n)
super = 0;
}
- if ((ruby_cbase == rb_cObject) && rb_autoload_defined(node->nd_cname)) {
- rb_autoload_load(node->nd_cname);
- }
- if (rb_const_defined_at(ruby_cbase, node->nd_cname)) {
- klass = rb_const_get(ruby_cbase, node->nd_cname);
+ cbase = class_prefix(self, node->nd_cpath);
+ cname = node->nd_cpath->nd_mid;
+ if (rb_const_defined_at(cbase, cname)) {
+ klass = rb_const_get_at(cbase, cname);
if (TYPE(klass) != T_CLASS) {
rb_raise(rb_eTypeError, "%s is not a class",
- rb_id2name(node->nd_cname));
+ rb_id2name(cname));
}
if (super) {
tmp = rb_class_real(RCLASS(klass)->super);
@@ -3321,51 +3548,50 @@ rb_eval(self, n)
else {
override_class:
if (!super) super = rb_cObject;
- klass = rb_define_class_id(node->nd_cname, super);
- rb_set_class_path(klass,ruby_cbase,rb_id2name(node->nd_cname));
- rb_const_set(ruby_cbase, node->nd_cname, klass);
+ klass = rb_define_class_id(cname, super);
+ rb_set_class_path(klass, cbase, rb_id2name(cname));
+ rb_const_set(cbase, cname, klass);
}
if (ruby_wrapper) {
rb_extend_object(klass, ruby_wrapper);
rb_include_module(klass, ruby_wrapper);
}
-
- result = module_setup(klass, node->nd_body);
if (super) rb_class_inherited(super, klass);
+ result = module_setup(klass, node);
}
break;
case NODE_MODULE:
{
- VALUE module;
+ VALUE module, cbase;
+ ID cname;
if (NIL_P(ruby_cbase)) {
rb_raise(rb_eTypeError, "no outer class/module");
}
- if ((ruby_cbase == rb_cObject) && rb_autoload_defined(node->nd_cname)) {
- rb_autoload_load(node->nd_cname);
- }
- if (rb_const_defined_at(ruby_cbase, node->nd_cname)) {
- module = rb_const_get(ruby_cbase, node->nd_cname);
+ cbase = class_prefix(self, node->nd_cpath);
+ cname = node->nd_cpath->nd_mid;
+ if (rb_const_defined_at(cbase, cname)) {
+ module = rb_const_get_at(cbase, cname);
if (TYPE(module) != T_MODULE) {
rb_raise(rb_eTypeError, "%s is not a module",
- rb_id2name(node->nd_cname));
+ rb_id2name(cname));
}
if (ruby_safe_level >= 4) {
rb_raise(rb_eSecurityError, "extending module prohibited");
}
}
else {
- module = rb_define_module_id(node->nd_cname);
- rb_set_class_path(module,ruby_cbase,rb_id2name(node->nd_cname));
- rb_const_set(ruby_cbase, node->nd_cname, module);
+ module = rb_define_module_id(cname);
+ rb_set_class_path(module, cbase, rb_id2name(cname));
+ rb_const_set(cbase, cname, module);
}
if (ruby_wrapper) {
rb_extend_object(module, ruby_wrapper);
rb_include_module(module, ruby_wrapper);
}
- result = module_setup(module, node->nd_body);
+ result = module_setup(module, node);
}
break;
@@ -3376,7 +3602,7 @@ rb_eval(self, n)
result = rb_eval(self, node->nd_recv);
if (FIXNUM_P(result) || SYMBOL_P(result)) {
rb_raise(rb_eTypeError, "no virtual class for %s",
- rb_class2name(CLASS_OF(result)));
+ rb_obj_classname(result));
}
if (ruby_safe_level >= 4 && !OBJ_TAINTED(result))
rb_raise(rb_eSecurityError, "Insecure: can't extend object");
@@ -3387,7 +3613,7 @@ rb_eval(self, n)
rb_include_module(klass, ruby_wrapper);
}
- result = module_setup(klass, node->nd_body);
+ result = module_setup(klass, node);
}
break;
@@ -3402,8 +3628,6 @@ rb_eval(self, n)
break;
case NODE_NEWLINE:
- ruby_sourcefile = node->nd_file;
- ruby_sourceline = node->nd_nth;
if (trace_func) {
call_trace_func("line", node, self,
ruby_frame->last_func,
@@ -3417,7 +3641,11 @@ rb_eval(self, n)
}
finish:
CHECK_INTS;
- ruby_current_node = nodesave;
+ if (contnode) {
+ node = contnode;
+ contnode = 0;
+ goto again;
+ }
return result;
}
@@ -3426,7 +3654,7 @@ module_setup(module, n)
VALUE module;
NODE *n;
{
- NODE * volatile node = n;
+ NODE * volatile node = n->nd_body;
int state;
struct FRAME frame;
VALUE result; /* OK */
@@ -3458,9 +3686,7 @@ module_setup(module, n)
PUSH_TAG(PROT_NONE);
if ((state = EXEC_TAG()) == 0) {
if (trace_func) {
- call_trace_func("class", ruby_current_node, ruby_cbase,
- ruby_frame->last_func,
- ruby_frame->last_class);
+ call_trace_func("class", n, ruby_cbase, ruby_frame->last_func, ruby_frame->last_class);
}
result = rb_eval(ruby_cbase, node->nd_next);
}
@@ -3472,8 +3698,7 @@ module_setup(module, n)
ruby_frame = frame.tmp;
if (trace_func) {
- call_trace_func("end", ruby_last_node, 0,
- ruby_frame->last_func, ruby_frame->last_class);
+ call_trace_func("end", n, 0, ruby_frame->last_func, ruby_frame->last_class);
}
if (state) JUMP_TAG(state);
@@ -3521,7 +3746,6 @@ static VALUE
rb_mod_public_method_defined(mod, mid)
VALUE mod, mid;
{
- VALUE klass;
ID id = rb_to_id(mid);
int noex;
@@ -3536,7 +3760,6 @@ static VALUE
rb_mod_private_method_defined(mod, mid)
VALUE mod, mid;
{
- VALUE klass;
ID id = rb_to_id(mid);
int noex;
@@ -3551,7 +3774,6 @@ static VALUE
rb_mod_protected_method_defined(mod, mid)
VALUE mod, mid;
{
- VALUE klass;
ID id = rb_to_id(mid);
int noex;
@@ -3646,6 +3868,10 @@ rb_longjmp(tag, mesg)
{
VALUE at;
+ if (thread_set_raised()) {
+ ruby_errinfo = exception_error;
+ JUMP_TAG(TAG_FATAL);
+ }
if (NIL_P(mesg)) mesg = ruby_errinfo;
if (NIL_P(mesg)) {
mesg = rb_exc_new(rb_eRuntimeError, 0, 0);
@@ -3666,13 +3892,21 @@ rb_longjmp(tag, mesg)
if (RTEST(ruby_debug) && !NIL_P(ruby_errinfo)
&& !rb_obj_is_kind_of(ruby_errinfo, rb_eSystemExit)) {
VALUE e = ruby_errinfo;
+ int status;
- StringValue(e);
- fprintf(stderr, "Exception `%s' at %s:%d - %s\n",
- rb_class2name(CLASS_OF(ruby_errinfo)),
- ruby_sourcefile, ruby_sourceline,
- RSTRING(e)->ptr);
- fflush(stderr);
+ PUSH_TAG(PROT_NONE);
+ if ((status = EXEC_TAG()) == 0) {
+ StringValue(e);
+ warn_printf("Exception `%s' at %s:%d - %s\n",
+ rb_obj_classname(ruby_errinfo),
+ ruby_sourcefile, ruby_sourceline,
+ RSTRING(e)->ptr);
+ }
+ POP_TAG();
+ if (status) {
+ thread_reset_raised();
+ JUMP_TAG(status);
+ }
}
rb_trap_restore_mask();
@@ -3685,6 +3919,7 @@ rb_longjmp(tag, mesg)
if (!prot_tag) {
error_print();
}
+ thread_reset_raised();
JUMP_TAG(tag);
}
@@ -3792,9 +4027,9 @@ rb_f_block_given_p()
}
static VALUE
-rb_yield_0(val, self, klass, pcall)
+rb_yield_0(val, self, klass, flags, avalue)
VALUE val, self, klass; /* OK */
- int pcall;
+ int flags, avalue;
{
NODE *node;
volatile VALUE result = Qnil;
@@ -3802,13 +4037,14 @@ rb_yield_0(val, self, klass, pcall)
volatile VALUE old_wrapper;
struct BLOCK * volatile block;
struct SCOPE * volatile old_scope;
+ int old_vmode;
struct FRAME frame;
NODE *cnode = ruby_current_node;
int state;
static unsigned serial = 1;
if (!rb_block_given_p()) {
- localjump_error("no block given", Qnil);
+ localjump_error("no block given", Qnil, 0);
}
PUSH_VARS();
@@ -3823,6 +4059,8 @@ rb_yield_0(val, self, klass, pcall)
ruby_wrapper = block->wrapper;
old_scope = ruby_scope;
ruby_scope = block->scope;
+ old_vmode = scope_vmode;
+ scope_vmode = (flags & YIELD_PUBLIC_DEF) ? SCOPE_PUBLIC : block->vmode;
ruby_block = block->prev;
if (block->flags & BLOCK_D_SCOPE) {
/* put place holder for dynamic (in-block) local variables */
@@ -3832,15 +4070,15 @@ rb_yield_0(val, self, klass, pcall)
/* FOR does not introduce new scope */
ruby_dyna_vars = block->dyna_vars;
}
- ruby_class = klass?klass:block->klass;
+ ruby_class = klass ? klass : block->klass;
if (!klass) self = block->self;
node = block->body;
if (block->var) {
PUSH_TAG(PROT_NONE);
if ((state = EXEC_TAG()) == 0) {
- if (block->var == (NODE*)1) {
- if (pcall && RARRAY(val)->len != 0) {
+ if (block->var == (NODE*)1) { /* no parameter || */
+ if ((flags & YIELD_PROC_CALL) && RARRAY(val)->len != 0) {
rb_raise(rb_eArgError, "wrong number of arguments (%ld for 0)",
RARRAY(val)->len);
}
@@ -3852,21 +4090,39 @@ rb_yield_0(val, self, klass, pcall)
}
}
else if (nd_type(block->var) == NODE_MASGN) {
- massign(self, block->var, val, pcall);
+ if (!avalue) {
+ val = svalue_to_mrhs(val, block->var->nd_head);
+ }
+ massign(self, block->var, val, flags&YIELD_PROC_CALL);
}
else {
- if (pcall) {
- val = avalue_to_yvalue(val);
+ int len = 0;
+ if (avalue) {
+ len = RARRAY(val)->len;
+ if (len == 1) {
+ val = RARRAY(val)->ptr[0];
+ }
+ else {
+ goto mult_values;
+ }
}
- assign(self, block->var, val, pcall);
+ else if (val == Qundef) {
+ val = Qnil;
+ mult_values:
+ {
+ NODE *curr = ruby_current_node;
+ ruby_current_node = block->var;
+ rb_warn("multiple values for a block parameter (%d for 1)\n\tfrom %s:%d",
+ len, curr->nd_file, nd_line(curr));
+ ruby_current_node = curr;
+ }
+ }
+ assign(self, block->var, val, flags&YIELD_PROC_CALL);
}
}
POP_TAG();
if (state) goto pop_state;
}
- else if (pcall) {
- val = avalue_to_yvalue(val);
- }
PUSH_ITER(block->iter);
PUSH_TAG(PROT_NONE);
@@ -3876,6 +4132,18 @@ rb_yield_0(val, self, klass, pcall)
result = Qnil;
}
else if (nd_type(node) == NODE_CFUNC || nd_type(node) == NODE_IFUNC) {
+ if (node->nd_state == YIELD_FUNC_AVALUE) {
+ if (!avalue) {
+ val = svalue_to_avalue(val);
+ }
+ }
+ else {
+ if (avalue) {
+ val = avalue_to_svalue(val);
+ }
+ if (val == Qundef && node->nd_state != YIELD_FUNC_SVALUE)
+ val = Qnil;
+ }
result = (*node->nd_cfnc)(val, node->nd_tval, self);
}
else {
@@ -3928,6 +4196,7 @@ rb_yield_0(val, self, klass, pcall)
if (ruby_scope->flags & SCOPE_DONT_RECYCLE)
scope_dup(old_scope);
ruby_scope = old_scope;
+ scope_vmode = old_vmode;
ruby_current_node = cnode;
if (state) {
if (!block->tag) {
@@ -3947,14 +4216,38 @@ VALUE
rb_yield(val)
VALUE val;
{
- return rb_yield_0(val, 0, 0, 0);
+ return rb_yield_0(val, 0, 0, Qfalse, Qfalse);
+}
+
+VALUE
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_yield_values(int n, ...)
+#else
+rb_yield_values(int n, va_alist)
+ int n;
+ va_dcl
+#endif
+{
+ va_list args;
+ VALUE ary;
+
+ if (n == 0) {
+ return rb_yield_0(Qundef, 0, 0, Qfalse, Qfalse);
+ }
+ ary = rb_ary_new2(n);
+ va_init_list(args, n);
+ while (n--) {
+ rb_ary_push(ary, va_arg(args, VALUE));
+ }
+ va_end(args);
+ return rb_yield_0(ary, 0, 0, Qfalse, Qtrue);
}
static VALUE
rb_f_loop()
{
for (;;) {
- rb_yield_0(Qundef, 0, 0, 0);
+ rb_yield_0(Qundef, 0, 0, Qfalse, Qfalse);
CHECK_INTS;
}
return Qnil; /* dummy */
@@ -3970,12 +4263,9 @@ massign(self, node, val, pcall)
NODE *list;
long i = 0, len;
- if (!pcall) {
- val = svalue_to_mvalue(val);
- }
len = RARRAY(val)->len;
list = node->nd_head;
- for (i=0; list && i<len; i++) {
+ for (; list && i<len; i++) {
assign(self, list->nd_head, RARRAY(val)->ptr[i], pcall);
list = list->nd_next;
}
@@ -4046,7 +4336,12 @@ assign(self, lhs, val, pcall)
break;
case NODE_CDECL:
- rb_const_set(ruby_cbase, lhs->nd_vid, val);
+ if (lhs->nd_vid == 0) {
+ rb_const_set(class_prefix(self, lhs->nd_else), lhs->nd_else->nd_mid, val);
+ }
+ else {
+ rb_const_set(ruby_cbase, lhs->nd_vid, val);
+ }
break;
case NODE_CVDECL:
@@ -4061,18 +4356,27 @@ assign(self, lhs, val, pcall)
break;
case NODE_MASGN:
- massign(self, lhs, svalue_to_mvalue(val), pcall);
+ massign(self, lhs, svalue_to_mrhs(val, lhs->nd_head), pcall);
break;
case NODE_CALL:
+ case NODE_ATTRASGN:
{
VALUE recv;
- recv = rb_eval(self, lhs->nd_recv);
+ int scope;
+ if (lhs->nd_recv == (NODE *)1) {
+ recv = self;
+ scope = 1;
+ }
+ else {
+ recv = rb_eval(self, lhs->nd_recv);
+ scope = 0;
+ }
if (!lhs->nd_args) {
/* attr set */
ruby_current_node = lhs;
SET_CURRENT_SOURCE();
- rb_call(CLASS_OF(recv), recv, lhs->nd_mid, 1, &val, 0);
+ rb_call(CLASS_OF(recv), recv, lhs->nd_mid, 1, &val, scope);
}
else {
/* array set */
@@ -4083,7 +4387,7 @@ assign(self, lhs, val, pcall)
ruby_current_node = lhs;
SET_CURRENT_SOURCE();
rb_call(CLASS_OF(recv), recv, lhs->nd_mid,
- RARRAY(args)->len, RARRAY(args)->ptr, 0);
+ RARRAY(args)->len, RARRAY(args)->ptr, scope);
}
}
break;
@@ -4234,7 +4538,7 @@ rb_rescue(b_proc, data1, r_proc, data2)
VALUE (*b_proc)(), (*r_proc)();
VALUE data1, data2;
{
- return rb_rescue2(b_proc, data1, r_proc, data2, rb_eStandardError, 0);
+ return rb_rescue2(b_proc, data1, r_proc, data2, rb_eStandardError, (VALUE)0);
}
VALUE
@@ -4305,7 +4609,7 @@ rb_with_disable_interrupt(proc, data)
return result;
}
-static void
+static inline void
stack_check()
{
static int overflowing = 0;
@@ -4315,7 +4619,7 @@ stack_check()
overflowing = 1;
PUSH_TAG(PROT_NONE);
if ((state = EXEC_TAG()) == 0) {
- rb_raise(rb_eSysStackError, "stack level too deep");
+ rb_exc_raise(sysstack_error);
}
POP_TAG();
overflowing = 0;
@@ -4328,9 +4632,10 @@ static int last_call_status;
#define CSTAT_PRIV 1
#define CSTAT_PROT 2
#define CSTAT_VCALL 4
+#define CSTAT_SUPER 8
static VALUE
-rb_f_missing(argc, argv, obj)
+rb_method_missing(argc, argv, obj)
int argc;
VALUE *argv;
VALUE obj;
@@ -4360,11 +4665,11 @@ rb_f_missing(argc, argv, obj)
case T_FALSE:
desc = "false";
break;
- case T_OBJECT:
- d = rb_any_to_s(obj);
- break;
default:
- d = rb_inspect(obj);
+ if (rb_respond_to(obj, rb_intern("inspect")))
+ d = rb_inspect(obj);
+ else
+ d = rb_any_to_s(obj);
break;
}
if (d) {
@@ -4377,47 +4682,49 @@ rb_f_missing(argc, argv, obj)
if (last_call_status & CSTAT_PRIV) {
format = "private method `%s' called for %s%s%s";
}
- if (last_call_status & CSTAT_PROT) {
+ else if (last_call_status & CSTAT_PROT) {
format = "protected method `%s' called for %s%s%s";
}
else if (last_call_status & CSTAT_VCALL) {
- const char *mname = rb_id2name(id);
-
- if (('a' <= mname[0] && mname[0] <= 'z') || mname[0] == '_') {
- format = "undefined local variable or method `%s' for %s%s%s";
- exc = rb_eNameError;
- }
+ format = "undefined local variable or method `%s' for %s%s%s";
+ exc = rb_eNameError;
+ }
+ else if (last_call_status & CSTAT_SUPER) {
+ format = "super: no superclass method `%s'";
}
if (!format) {
format = "undefined method `%s' for %s%s%s";
}
ruby_current_node = cnode;
- PUSH_FRAME(); /* fake frame */
- *ruby_frame = *_frame.prev->prev;
{
char buf[BUFSIZ];
- int noclass = (!d || desc[0]=='#');
+ int noclass = (!desc || desc[0]=='#');
+ int n = 0;
+ VALUE args[3];
snprintf(buf, BUFSIZ, format, rb_id2name(id),
desc, noclass ? "" : ":",
- noclass ? "" : rb_class2name(CLASS_OF(obj)));
- exc = rb_exc_new2(exc, buf);
- rb_iv_set(exc, "name", argv[0]);
- rb_iv_set(exc, "args", rb_ary_new4(argc-1, argv+1));
+ noclass ? "" : rb_obj_classname(obj));
+ args[n++] = rb_str_new2(buf);
+ args[n++] = argv[0];
+ if (exc == rb_eNoMethodError) {
+ args[n++] = rb_ary_new4(argc-1, argv+1);
+ }
+ exc = rb_class_new_instance(n, args, exc);
+ ruby_frame = ruby_frame->prev; /* pop frame for "method_missing" */
rb_exc_raise(exc);
}
- POP_FRAME();
return Qnil; /* not reached */
}
static VALUE
-rb_undefined(obj, id, argc, argv, call_status)
+method_missing(obj, id, argc, argv, call_status)
VALUE obj;
ID id;
int argc;
- VALUE*argv;
+ const VALUE *argv;
int call_status;
{
VALUE *nargv;
@@ -4426,9 +4733,12 @@ rb_undefined(obj, id, argc, argv, call_status)
if (id == missing) {
PUSH_FRAME();
- rb_f_missing(argc, argv, obj);
+ rb_method_missing(argc, argv, obj);
POP_FRAME();
}
+ else if (id == ID_ALLOCATOR) {
+ rb_raise(rb_eNoMethodError, "allocator undefined for %s", rb_class2name(obj));
+ }
nargv = ALLOCA_N(VALUE, argc+1);
nargv[0] = ID2SYM(id);
@@ -4437,7 +4747,7 @@ rb_undefined(obj, id, argc, argv, call_status)
return rb_funcall2(obj, missing, argc+1, nargv);
}
-static VALUE
+static inline VALUE
call_cfunc(func, recv, len, argc, argv)
VALUE (*func)();
VALUE recv;
@@ -4598,6 +4908,9 @@ rb_call0(klass, recv, id, oid, argc, argv, body, nosuper)
if (argc != 0) {
rb_raise(rb_eArgError, "wrong number of arguments(%d for 0)", argc);
}
+ result = rb_attr_get(recv, body->nd_vid);
+ break;
+
case NODE_ATTRSET:
/* for re-scoped/renamed method */
case NODE_ZSUPER:
@@ -4609,7 +4922,7 @@ rb_call0(klass, recv, id, oid, argc, argv, body, nosuper)
break;
case NODE_BMETHOD:
- result = proc_invoke(body->nd_cval, rb_ary_new4(argc, argv), Qtrue, recv);
+ result = proc_invoke(body->nd_cval, rb_ary_new4(argc, argv), recv, klass);
break;
case NODE_SCOPE:
@@ -4713,7 +5026,6 @@ rb_call0(klass, recv, id, oid, argc, argv, body, nosuper)
if (trace_func) {
call_trace_func("call", b2, recv, id, klass);
}
- ruby_last_node = b2;
result = rb_eval(recv, body);
}
else if (state == TAG_RETURN) {
@@ -4725,7 +5037,7 @@ rb_call0(klass, recv, id, oid, argc, argv, body, nosuper)
POP_SCOPE();
ruby_cref = saved_cref;
if (trace_func) {
- call_trace_func("return", ruby_last_node, recv, id, klass);
+ call_trace_func("return", ruby_frame->prev->node, recv, id, klass);
}
switch (state) {
case 0:
@@ -4773,7 +5085,7 @@ rb_call(klass, recv, mid, argc, argv, scope)
ent = cache + EXPR1(klass, mid);
if (ent->mid == mid && ent->klass == klass) {
if (!ent->method)
- return rb_undefined(recv, mid, argc, argv, scope==2?CSTAT_VCALL:0);
+ return method_missing(recv, mid, argc, argv, scope==2?CSTAT_VCALL:0);
klass = ent->origin;
id = ent->mid0;
noex = ent->noex;
@@ -4781,18 +5093,17 @@ rb_call(klass, recv, mid, argc, argv, scope)
}
else if ((body = rb_get_method_body(&klass, &id, &noex)) == 0) {
if (scope == 3) {
- rb_name_error(mid, "super: no superclass method `%s'",
- rb_id2name(mid));
+ return method_missing(recv, mid, argc, argv, CSTAT_SUPER);
}
- return rb_undefined(recv, mid, argc, argv, scope==2?CSTAT_VCALL:0);
+ return method_missing(recv, mid, argc, argv, scope==2?CSTAT_VCALL:0);
}
if (mid != missing) {
/* receiver specified form for private method */
if ((noex & NOEX_PRIVATE) && scope == 0)
- return rb_undefined(recv, mid, argc, argv, CSTAT_PRIV);
+ return method_missing(recv, mid, argc, argv, CSTAT_PRIV);
- /* self must be kind of a specified form for private method */
+ /* self must be kind of a specified form for protected method */
if ((noex & NOEX_PROTECTED)) {
VALUE defined_class = klass;
@@ -4800,7 +5111,7 @@ rb_call(klass, recv, mid, argc, argv, scope)
defined_class = RBASIC(defined_class)->klass;
}
if (!rb_obj_is_kind_of(ruby_frame->self, rb_class_real(defined_class)))
- return rb_undefined(recv, mid, argc, argv, CSTAT_PROT);
+ return method_missing(recv, mid, argc, argv, CSTAT_PROT);
}
}
@@ -4840,14 +5151,6 @@ rb_f_send(argc, argv, recv)
return vid;
}
-#ifdef HAVE_STDARG_PROTOTYPES
-#include <stdarg.h>
-#define va_init_list(a,b) va_start(a,b)
-#else
-#include <varargs.h>
-#define va_init_list(a,b) va_start(a)
-#endif
-
VALUE
#ifdef HAVE_STDARG_PROTOTYPES
rb_funcall(VALUE recv, ID mid, int n, ...)
@@ -4905,17 +5208,35 @@ rb_call_super(argc, argv)
int argc;
const VALUE *argv;
{
- VALUE result;
+ VALUE result, self, klass, k;
if (ruby_frame->last_class == 0) {
- rb_name_error(ruby_frame->last_func, "superclass method `%s' must be enabled by rb_enable_super()",
+ rb_name_error(ruby_frame->last_func, "calling `super' from `%s' is prohibited",
rb_id2name(ruby_frame->last_func));
}
- PUSH_ITER(ruby_iter->iter?ITER_PRE:ITER_NOT);
- result = rb_call(RCLASS(ruby_frame->last_class)->super,
- ruby_frame->self, ruby_frame->last_func,
- argc, argv, 3);
+ self = ruby_frame->self;
+ klass = ruby_frame->last_class;
+ if (BUILTIN_TYPE(klass) == T_MODULE) {
+ k = CLASS_OF(self);
+ while (!(BUILTIN_TYPE(k) == T_ICLASS && RBASIC(k)->klass == klass)) {
+ k = RCLASS(k)->super;
+ if (!k) {
+ rb_raise(rb_eTypeError, "%s is not included in %s",
+ rb_class2name(klass),
+ rb_class2name(CLASS_OF(self)));
+ }
+ }
+ if (RCLASS(k)->super == 0) {
+ rb_name_error(ruby_frame->last_func,
+ "super: no superclass method `%s'",
+ rb_id2name(ruby_frame->last_func));
+ }
+ klass = k;
+ }
+
+ PUSH_ITER(rb_block_given_p()?ITER_PRE:ITER_NOT);
+ result = rb_call(RCLASS(klass)->super, self, ruby_frame->orig_func, argc, argv, 3);
POP_ITER();
return result;
@@ -4931,6 +5252,9 @@ backtrace(lev)
NODE *n;
ary = rb_ary_new();
+ if (frame->last_func == ID_ALLOCATOR) {
+ frame = frame->prev;
+ }
if (lev < 0) {
ruby_set_current_source();
if (frame->last_func) {
@@ -5019,10 +5343,15 @@ compile(src, file, line)
int line;
{
NODE *node;
+ int critical;
ruby_nerrs = 0;
- Check_Type(src, T_STRING);
+ StringValue(src);
+ critical = rb_thread_critical;
+ rb_thread_critical = Qtrue;
node = rb_compile_string(file, src, line);
+ rb_thread_critical = critical;
+
if (ruby_nerrs == 0) return node;
return 0;
@@ -5048,9 +5377,9 @@ eval(self, src, scope, file, line)
int state;
if (!NIL_P(scope)) {
- if (!rb_obj_is_block(scope)) {
+ if (!rb_obj_is_proc(scope)) {
rb_raise(rb_eTypeError, "wrong argument type %s (expected Proc/Binding)",
- rb_class2name(CLASS_OF(scope)));
+ rb_obj_classname(scope));
}
Data_Get_Struct(scope, struct BLOCK, data);
@@ -5070,9 +5399,9 @@ eval(self, src, scope, file, line)
ruby_cref = (NODE*)ruby_frame->cbase;
old_wrapper = ruby_wrapper;
ruby_wrapper = data->wrapper;
- if ((file == 0 || (line == 1 && strcmp(file, "(eval)") == 0)) &&
- data->body && data->body->nd_file) {
+ if ((file == 0 || (line == 1 && strcmp(file, "(eval)") == 0)) && data->body) {
file = data->body->nd_file;
+ if (!file) file = "__builtin__";
line = nd_line(data->body);
}
@@ -5150,18 +5479,18 @@ eval(self, src, scope, file, line)
ruby_set_current_source();
if (state) {
if (state == TAG_RAISE) {
- VALUE err;
- VALUE errat;
+ VALUE err, errat, mesg;
+ mesg = rb_obj_as_string(ruby_errinfo);
if (strcmp(file, "(eval)") == 0) {
if (ruby_sourceline > 1) {
errat = get_backtrace(ruby_errinfo);
err = rb_str_dup(RARRAY(errat)->ptr[0]);
rb_str_cat2(err, ": ");
- rb_str_append(err, ruby_errinfo);
+ rb_str_append(err, mesg);
}
else {
- err = rb_str_dup(ruby_errinfo);
+ err = mesg;
}
rb_exc_raise(rb_funcall(ruby_errinfo, rb_intern("exception"), 1, err));
}
@@ -5291,7 +5620,7 @@ static VALUE
yield_under_i(self)
VALUE self;
{
- return rb_yield_0(self, self, ruby_class, 0);
+ return rb_yield_0(self, self, ruby_class, YIELD_PUBLIC_DEF, Qfalse);
}
/* block eval under the class/module context */
@@ -5410,7 +5739,7 @@ rb_load(fname, wrap)
/* load in anonymous module as toplevel */
ruby_class = ruby_wrapper = rb_module_new();
self = rb_obj_clone(ruby_top_self);
- rb_extend_object(self, ruby_class);
+ rb_extend_object(self, ruby_wrapper);
PUSH_CREF(ruby_wrapper);
}
PUSH_ITER(ITER_NOT);
@@ -5427,17 +5756,21 @@ rb_load(fname, wrap)
last_func = ruby_frame->last_func;
if (state == 0) {
NODE *node;
+ volatile int critical;
DEFER_INTS;
ruby_in_eval++;
+ critical = rb_thread_critical;
+ rb_thread_critical = Qtrue;
rb_load_file(RSTRING(fname)->ptr);
ruby_in_eval--;
node = ruby_eval_tree;
- ALLOW_INTS;
+ rb_thread_critical = critical;
if (ruby_nerrs == 0) {
eval_node(self, node);
}
}
+ ALLOW_INTS;
ruby_frame->last_func = last_func;
if (ruby_scope->flags == SCOPE_ALLOCA && ruby_class == rb_cObject) {
if (ruby_scope->local_tbl) /* toplevel was empty */
@@ -5525,7 +5858,7 @@ rb_feature_p(feature, wait)
if (ext && strcmp(ext, ".rb") == 0) {
rb_thread_t th;
- while (st_lookup(loading_tbl, f, &th)) {
+ while (st_lookup(loading_tbl, (st_data_t)f, (st_data_t *)&th)) {
if (th == curr_thread) {
return Qtrue;
}
@@ -5573,60 +5906,55 @@ rb_provide(feature)
rb_provide_feature(rb_str_new2(feature));
}
+NORETURN(static void load_failed _((VALUE)));
+static VALUE load_dyna _((VALUE, VALUE));
+static VALUE load_rb _((VALUE, VALUE));
+
VALUE
rb_f_require(obj, fname)
VALUE obj, fname;
{
VALUE feature, tmp;
- char *ext, *ftptr; /* OK */
- int state;
- volatile int safe = ruby_safe_level;
+ char *ext; /* OK */
SafeStringValue(fname);
ext = strrchr(RSTRING(fname)->ptr, '.');
+ if (ext && strchr(ext, '/')) ext = 0;
if (ext) {
if (strcmp(".rb", ext) == 0) {
feature = rb_str_dup(fname);
tmp = rb_find_file(fname);
if (tmp) {
- fname = tmp;
- goto load_rb;
+ return load_rb(feature, tmp);
}
+ load_failed(fname);
}
else if (strcmp(".so", ext) == 0 || strcmp(".o", ext) == 0) {
- fname = rb_str_new(RSTRING(fname)->ptr, ext-RSTRING(fname)->ptr);
+ tmp = rb_str_new(RSTRING(fname)->ptr, ext-RSTRING(fname)->ptr);
#ifdef DLEXT2
- tmp = fname;
if (rb_find_file_ext(&tmp, loadable_ext+1)) {
- feature = tmp;
- fname = rb_find_file(tmp);
- goto load_dyna;
+ return load_dyna(tmp, rb_find_file(tmp));
}
#else
- feature = tmp = rb_str_dup(fname);
+ feature = tmp;
rb_str_cat2(tmp, DLEXT);
tmp = rb_find_file(tmp);
if (tmp) {
- fname = tmp;
- goto load_dyna;
+ return load_dyna(feature, tmp);
}
#endif
}
else if (strcmp(DLEXT, ext) == 0) {
tmp = rb_find_file(fname);
if (tmp) {
- feature = fname;
- fname = tmp;
- goto load_dyna;
+ return load_dyna(fname, tmp);
}
}
#ifdef DLEXT2
else if (strcmp(DLEXT2, ext) == 0) {
tmp = rb_find_file(fname);
if (tmp) {
- feature = fname;
- fname = tmp;
- goto load_dyna;
+ return load_dyna(fname, tmp);
}
}
#endif
@@ -5637,19 +5965,29 @@ rb_f_require(obj, fname)
break;
case 1:
- feature = fname = tmp;
- goto load_rb;
+ return load_rb(tmp, tmp);
default:
- feature = tmp;
- fname = rb_find_file(tmp);
- goto load_dyna;
+ return load_dyna(tmp, rb_find_file(tmp));
}
- if (rb_feature_p(RSTRING(fname)->ptr, Qfalse))
- return Qfalse;
+ if (!rb_feature_p(RSTRING(fname)->ptr, Qfalse))
+ load_failed(fname);
+ return Qfalse;
+}
+
+static void
+load_failed(fname)
+ VALUE fname;
+{
rb_raise(rb_eLoadError, "No such file to load -- %s", RSTRING(fname)->ptr);
+}
+
+static VALUE
+load_dyna(feature, fname)
+ VALUE feature, fname;
+{
+ int state;
- load_dyna:
if (rb_feature_p(RSTRING(feature)->ptr, Qfalse))
return Qfalse;
rb_provide_feature(feature);
@@ -5677,10 +6015,19 @@ rb_f_require(obj, fname)
SCOPE_SET(old_vmode);
}
if (state) JUMP_TAG(state);
+ ruby_errinfo = Qnil;
return Qtrue;
+}
+
+static VALUE
+load_rb(feature, fname)
+ VALUE feature, fname;
+{
+ int state;
+ char *ftptr;
+ volatile int safe = ruby_safe_level;
- load_rb:
if (rb_feature_p(RSTRING(feature)->ptr, Qtrue))
return Qfalse;
ruby_safe_level = 0;
@@ -5691,14 +6038,14 @@ rb_f_require(obj, fname)
}
/* partial state */
ftptr = ruby_strdup(RSTRING(feature)->ptr);
- st_insert(loading_tbl, ftptr, curr_thread);
+ st_insert(loading_tbl, (st_data_t)ftptr, (st_data_t)curr_thread);
PUSH_TAG(PROT_NONE);
if ((state = EXEC_TAG()) == 0) {
rb_load(fname, 0);
}
POP_TAG();
- st_delete(loading_tbl, &ftptr, 0); /* loading done */
+ st_delete(loading_tbl, (st_data_t *)&ftptr, 0); /* loading done */
free(ftptr);
ruby_safe_level = safe;
if (state) JUMP_TAG(state);
@@ -5858,7 +6205,6 @@ rb_mod_modfunc(argc, argv, module)
m = RCLASS(m)->super;
}
rb_add_method(rb_singleton_class(module), id, body->nd_body, NOEX_PUBLIC);
- rb_funcall(module, singleton_added, 1, ID2SYM(id));
}
return module;
}
@@ -5907,15 +6253,6 @@ rb_obj_call_init(obj, argc, argv)
POP_ITER();
}
-static VALUE
-top_include(argc, argv)
- int argc;
- VALUE *argv;
-{
- rb_secure(4);
- return rb_mod_include(argc, argv, rb_cObject);
-}
-
void
rb_extend_object(obj, module)
VALUE obj, module;
@@ -5949,6 +6286,22 @@ rb_obj_extend(argc, argv, obj)
return obj;
}
+static VALUE
+top_include(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ rb_secure(4);
+ if (ruby_wrapper) {
+ rb_warn("main#include in the wrapped load is effective only for toplevel");
+ return rb_obj_extend(argc, argv, self);
+ }
+ else {
+ return rb_mod_include(argc, argv, rb_cObject);
+ }
+}
+
VALUE rb_f_trace_var();
VALUE rb_f_untrace_var();
@@ -6002,7 +6355,7 @@ rb_f_local_variables()
vars = ruby_dyna_vars;
while (vars) {
- if (vars->id) {
+ if (vars->id && rb_is_local_id(vars->id)) { /* skip $_, $~ and flip states */
rb_ary_push(ary, rb_str_new2(rb_id2name(vars->id)));
}
vars = vars->next;
@@ -6066,7 +6419,7 @@ call_end_proc(data)
ruby_frame->self = ruby_frame->prev->self;
ruby_frame->last_func = 0;
ruby_frame->last_class = 0;
- proc_invoke(data, rb_ary_new2(0), Qfalse, Qundef);
+ proc_invoke(data, rb_ary_new2(0), Qundef, 0);
POP_FRAME();
POP_ITER();
}
@@ -6076,7 +6429,7 @@ rb_f_END()
{
PUSH_FRAME();
ruby_frame->argc = 0;
- rb_set_end_proc(call_end_proc, rb_f_lambda());
+ rb_set_end_proc(call_end_proc, rb_block_proc());
POP_FRAME();
}
@@ -6085,8 +6438,7 @@ rb_f_at_exit()
{
VALUE proc;
- proc = rb_f_lambda();
-
+ proc = rb_block_proc();
rb_set_end_proc(call_end_proc, proc);
return proc;
}
@@ -6099,7 +6451,11 @@ rb_exec_end_proc()
save = link = end_procs;
while (link) {
- rb_protect((VALUE(*)_((VALUE)))link->func, link->data, &status);
+ PUSH_TAG(PROT_NONE);
+ if ((status = EXEC_TAG()) == 0) {
+ (*link->func)(link->data);
+ }
+ POP_TAG();
if (status) {
error_handle(status);
}
@@ -6107,7 +6463,11 @@ rb_exec_end_proc()
}
link = end_procs;
while (link != save) {
- rb_protect((VALUE(*)_((VALUE)))link->func, link->data, &status);
+ PUSH_TAG(PROT_NONE);
+ if ((status = EXEC_TAG()) == 0) {
+ (*link->func)(link->data);
+ }
+ POP_TAG();
if (status) {
error_handle(status);
}
@@ -6116,7 +6476,11 @@ rb_exec_end_proc()
while (ephemeral_end_procs) {
link = ephemeral_end_procs;
ephemeral_end_procs = link->next;
- rb_protect((VALUE(*)_((VALUE)))link->func, link->data, &status);
+ PUSH_TAG(PROT_NONE);
+ if ((status = EXEC_TAG()) == 0) {
+ (*link->func)(link->data);
+ }
+ POP_TAG();
if (status) {
error_handle(status);
}
@@ -6128,7 +6492,6 @@ void
Init_eval()
{
init = rb_intern("initialize");
- alloc = rb_intern("allocate");
eqq = rb_intern("===");
each = rb_intern("each");
@@ -6158,7 +6521,7 @@ Init_eval()
rb_define_global_function("eval", rb_f_eval, -1);
rb_define_global_function("iterator?", rb_f_block_given_p, 0);
rb_define_global_function("block_given?", rb_f_block_given_p, 0);
- rb_define_global_function("method_missing", rb_f_missing, -1);
+ rb_define_global_function("method_missing", rb_method_missing, -1);
rb_define_global_function("loop", rb_f_loop, 0);
rb_define_method(rb_mKernel, "respond_to?", rb_obj_respond_to, -1);
@@ -6200,8 +6563,8 @@ Init_eval()
rb_undef_method(rb_cClass, "module_function");
- rb_define_private_method(rb_cModule, "remove_method", rb_mod_remove_method, 1);
- rb_define_private_method(rb_cModule, "undef_method", rb_mod_undef_method, 1);
+ rb_define_private_method(rb_cModule, "remove_method", rb_mod_remove_method, -1);
+ rb_define_private_method(rb_cModule, "undef_method", rb_mod_undef_method, -1);
rb_define_private_method(rb_cModule, "alias_method", rb_mod_alias_method, 2);
rb_define_private_method(rb_cModule, "define_method", rb_mod_define_method, -1);
@@ -6223,7 +6586,43 @@ Init_eval()
rb_define_virtual_variable("$SAFE", safe_getter, safe_setter);
}
-VALUE rb_f_autoload();
+static VALUE
+rb_mod_autoload(mod, sym, file)
+ VALUE mod;
+ VALUE sym;
+ VALUE file;
+{
+ ID id = rb_to_id(sym);
+
+ Check_SafeStr(file);
+ rb_autoload(mod, id, RSTRING(file)->ptr);
+ return Qnil;
+}
+
+static VALUE
+rb_mod_autoload_p(mod, sym)
+ VALUE mod, sym;
+{
+ return rb_autoload_p(mod, rb_to_id(sym));
+}
+
+static VALUE
+rb_f_autoload(obj, sym, file)
+ VALUE obj;
+ VALUE sym;
+ VALUE file;
+{
+ return rb_mod_autoload(ruby_class, sym, file);
+}
+
+static VALUE
+rb_f_autoload_p(obj, sym)
+ VALUE obj;
+ VALUE sym;
+{
+ /* use ruby_class as same as rb_f_autoload. */
+ return rb_mod_autoload_p(ruby_class, sym);
+}
void
Init_load()
@@ -6238,7 +6637,10 @@ Init_load()
rb_define_global_function("load", rb_f_load, -1);
rb_define_global_function("require", rb_f_require, 1);
- rb_define_global_function("autoload", rb_f_autoload, 2);
+ rb_define_method(rb_cModule, "autoload", rb_mod_autoload, 2);
+ rb_define_method(rb_cModule, "autoload?", rb_mod_autoload_p, 1);
+ rb_define_global_function("autoload", rb_f_autoload, 2);
+ rb_define_global_function("autoload?", rb_f_autoload_p, 1);
rb_global_variable(&ruby_wrapper);
ruby_dln_librefs = rb_ary_new();
@@ -6279,6 +6681,7 @@ blk_mark(data)
rb_gc_mark((VALUE)data->klass);
rb_gc_mark((VALUE)data->tag);
rb_gc_mark(data->wrapper);
+ rb_gc_mark(data->block_obj);
data = data->prev;
}
}
@@ -6359,14 +6762,14 @@ frame_dup(frame)
}
static VALUE
-bind_clone(self)
+proc_clone(self)
VALUE self;
{
struct BLOCK *orig, *data;
VALUE bind;
Data_Get_Struct(self, struct BLOCK, orig);
- bind = Data_Make_Struct(rb_cBinding,struct BLOCK,blk_mark,blk_free,data);
+ bind = Data_Make_Struct(rb_obj_class(self),struct BLOCK,blk_mark,blk_free,data);
CLONESETUP(bind, self);
MEMCPY(data, orig, struct BLOCK, 1);
frame_dup(&data->frame);
@@ -6477,18 +6880,25 @@ proc_set_safe_level(data)
}
static VALUE
-proc_new(klass)
+proc_alloc(klass, proc)
VALUE klass;
+ int proc;
{
- volatile VALUE proc;
+ volatile VALUE block;
struct BLOCK *data, *p;
struct RVarmap *vars;
if (!rb_block_given_p() && !rb_f_block_given_p()) {
rb_raise(rb_eArgError, "tried to create Proc object without a block");
}
+ if (proc && !rb_block_given_p()) {
+ rb_warn("tried to create Proc object without a block");
+ }
- proc = Data_Make_Struct(klass, struct BLOCK, blk_mark, blk_free, data);
+ if (!proc && ruby_block->block_obj) {
+ return ruby_block->block_obj;
+ }
+ block = Data_Make_Struct(klass, struct BLOCK, blk_mark, blk_free, data);
*data = *ruby_block;
data->orig_thread = rb_thread_current();
@@ -6511,9 +6921,15 @@ proc_new(klass)
}
}
scope_dup(data->scope);
- proc_save_safe_level(proc);
+ proc_save_safe_level(block);
+ if (proc) {
+ data->flags |= BLOCK_LAMBDA;
+ }
+ else {
+ ruby_block->block_obj = block;
+ }
- return proc;
+ return block;
}
static VALUE
@@ -6522,20 +6938,33 @@ proc_s_new(argc, argv, klass)
VALUE *argv;
VALUE klass;
{
- VALUE proc = proc_new(klass);
+ VALUE block = proc_alloc(klass, Qfalse);
- rb_obj_call_init(proc, argc, argv);
- return proc;
+ rb_obj_call_init(block, argc, argv);
+ return block;
+}
+
+VALUE
+rb_block_proc()
+{
+ return proc_alloc(rb_cProc, Qfalse);
}
VALUE
rb_f_lambda()
{
- return proc_new(rb_cProc);
+ rb_warn("rb_f_lambda() is deprecated; use rb_block_proc() instead");
+ return proc_alloc(rb_cProc, Qtrue);
+}
+
+static VALUE
+proc_lambda()
+{
+ return proc_alloc(rb_cProc, Qtrue);
}
static int
-blk_orphan(data)
+block_orphan(data)
struct BLOCK *data;
{
if ((data->tag->flags & BLOCK_ORPHAN) &&
@@ -6549,53 +6978,53 @@ blk_orphan(data)
}
static VALUE
-proc_invoke(proc, args, pcall, self)
+proc_invoke(proc, args, self, klass)
VALUE proc, args; /* OK */
- int pcall;
- VALUE self;
+ VALUE self, klass;
{
struct BLOCK * volatile old_block;
struct BLOCK _block;
struct BLOCK *data;
volatile VALUE result = Qnil;
- int state;
+ int state, incoming_state;
volatile int orphan;
volatile int safe = ruby_safe_level;
volatile VALUE old_wrapper = ruby_wrapper;
struct RVarmap * volatile old_dvars = ruby_dyna_vars;
+ volatile int pcall;
if (rb_block_given_p() && ruby_frame->last_func) {
rb_warning("block for %s#%s is useless",
- rb_class2name(CLASS_OF(proc)),
+ rb_obj_classname(proc),
rb_id2name(ruby_frame->last_func));
}
Data_Get_Struct(proc, struct BLOCK, data);
- orphan = blk_orphan(data);
+ orphan = block_orphan(data);
+ pcall = data->flags & BLOCK_LAMBDA ? YIELD_PROC_CALL : 0;
ruby_wrapper = data->wrapper;
ruby_dyna_vars = data->dyna_vars;
/* PUSH BLOCK from data */
old_block = ruby_block;
_block = *data;
+ if (self != Qundef) _block.frame.self = self;
+ if (klass) _block.frame.last_class = klass;
ruby_block = &_block;
PUSH_ITER(ITER_CUR);
ruby_frame->iter = ITER_CUR;
-
- if (!pcall) {
- args = avalue_to_yvalue(args);
- }
PUSH_TAG(PROT_NONE);
state = EXEC_TAG();
if (state == 0) {
proc_set_safe_level(proc);
- result = rb_yield_0(args, self, self!=Qundef?CLASS_OF(self):0, pcall);
+ result = rb_yield_0(args, self, self!=Qundef?CLASS_OF(self):0, pcall, Qtrue);
}
POP_TAG();
POP_ITER();
- if (ruby_block->tag->dst == state) {
+ incoming_state = state;
+ if (orphan || ruby_block->tag->dst == state) {
state &= TAG_MASK;
}
ruby_block = old_block;
@@ -6606,20 +7035,26 @@ proc_invoke(proc, args, pcall, self)
switch (state) {
case 0:
break;
- case TAG_BREAK:
- if (!pcall && orphan) {
- localjump_error("break from proc-closure", prot_tag->retval);
- }
- result = prot_tag->retval;
- break;
case TAG_RETRY:
- localjump_error("retry from proc-closure", Qnil);
- break;
- case TAG_RETURN:
- if (orphan) { /* orphan procedure */
- localjump_error("return from proc-closure", prot_tag->retval);
+ if (pcall || orphan) {
+ localjump_error("retry from proc-closure", Qnil, state);
}
/* fall through */
+ case TAG_BREAK:
+ case TAG_RETURN:
+ if (pcall) {
+ result = prot_tag->retval;
+ break;
+ }
+ else if (orphan) { /* orphan block */
+ char mesg[32];
+ snprintf(mesg, sizeof mesg, "%s from proc-closure",
+ state == TAG_BREAK ? "break" : "return");
+ localjump_error(mesg, prot_tag->retval, state);
+ }
+ else if (state == incoming_state) {
+ ruby_block->tag->dst = incoming_state;
+ }
default:
JUMP_TAG(state);
}
@@ -6630,15 +7065,11 @@ static VALUE
proc_call(proc, args)
VALUE proc, args; /* OK */
{
- return proc_invoke(proc, args, Qtrue, Qundef);
+ return proc_invoke(proc, args, Qundef, 0);
}
-static VALUE
-proc_yield(proc, args)
- VALUE proc, args; /* OK */
-{
- return proc_invoke(proc, args, Qfalse, Qundef);
-}
+static VALUE bmcall _((VALUE, VALUE));
+static VALUE method_arity _((VALUE));
static VALUE
proc_arity(proc)
@@ -6649,12 +7080,18 @@ proc_arity(proc)
int n;
Data_Get_Struct(proc, struct BLOCK, data);
- if (data->var == 0) return INT2FIX(-1);
+ if (data->var == 0) {
+ if (data->body && nd_type(data->body) == NODE_IFUNC &&
+ data->body->nd_cfnc == bmcall) {
+ return method_arity(data->body->nd_tval);
+ }
+ return INT2FIX(-1);
+ }
if (data->var == (NODE*)1) return INT2FIX(0);
if (data->var == (NODE*)2) return INT2FIX(0);
switch (nd_type(data->var)) {
default:
- return INT2FIX(-1);
+ return INT2FIX(1);
case NODE_MASGN:
list = data->var->nd_head;
n = 0;
@@ -6689,7 +7126,7 @@ proc_to_s(self, other)
{
struct BLOCK *data;
NODE *node;
- char *cname = rb_class2name(CLASS_OF(self));
+ char *cname = rb_obj_classname(self);
const int w = (SIZEOF_LONG * CHAR_BIT) / 4;
long len = strlen(cname)+6+w; /* 6:tags 16:addr */
VALUE str;
@@ -6712,10 +7149,10 @@ proc_to_s(self, other)
}
static VALUE
-proc_to_proc(proc)
- VALUE proc;
+proc_to_self(self)
+ VALUE self;
{
- return proc;
+ return self;
}
static VALUE
@@ -6745,7 +7182,7 @@ block_pass(self, node)
VALUE self;
NODE *node;
{
- VALUE block = rb_eval(self, node->nd_body); /* OK */
+ VALUE proc = rb_eval(self, node->nd_body); /* OK */
VALUE b;
struct BLOCK * volatile old_block;
struct BLOCK _block;
@@ -6755,34 +7192,35 @@ block_pass(self, node)
volatile int orphan;
volatile int safe = ruby_safe_level;
- if (NIL_P(block)) {
+ if (NIL_P(proc)) {
PUSH_ITER(ITER_NOT);
result = rb_eval(self, node->nd_iter);
POP_ITER();
return result;
}
- if (!rb_obj_is_proc(block)) {
- b = rb_check_convert_type(block, T_DATA, "Proc", "to_proc");
+ if (!rb_obj_is_proc(proc)) {
+ b = rb_check_convert_type(proc, T_DATA, "Proc", "to_proc");
if (!rb_obj_is_proc(b)) {
rb_raise(rb_eTypeError, "wrong argument type %s (expected Proc)",
- rb_class2name(CLASS_OF(block)));
+ rb_obj_classname(proc));
}
- block = b;
+ proc = b;
}
- if (ruby_safe_level >= 1 && OBJ_TAINTED(block)) {
- if (ruby_safe_level > proc_get_safe_level(block)) {
+ if (ruby_safe_level >= 1 && OBJ_TAINTED(proc)) {
+ if (ruby_safe_level > proc_get_safe_level(proc)) {
rb_raise(rb_eSecurityError, "Insecure: tainted block value");
}
}
- Data_Get_Struct(block, struct BLOCK, data);
- orphan = blk_orphan(data);
+ Data_Get_Struct(proc, struct BLOCK, data);
+ orphan = block_orphan(data);
retry:
/* PUSH BLOCK from data */
old_block = ruby_block;
_block = *data;
+ _block.outer = ruby_block;
ruby_block = &_block;
PUSH_ITER(ITER_PRE);
ruby_frame->iter = ITER_PRE;
@@ -6790,7 +7228,7 @@ block_pass(self, node)
PUSH_TAG(PROT_NONE);
state = EXEC_TAG();
if (state == 0) {
- proc_set_safe_level(block);
+ proc_set_safe_level(proc);
if (safe > ruby_safe_level)
ruby_safe_level = safe;
result = rb_eval(self, node->nd_iter);
@@ -6819,7 +7257,7 @@ block_pass(self, node)
ruby_block = old_block;
ruby_safe_level = safe;
- switch (state) {/* escape from orphan procedure */
+ switch (state) {/* escape from orphan block */
case 0:
break;
case TAG_BREAK:
@@ -6829,7 +7267,7 @@ block_pass(self, node)
goto retry;
case TAG_RETURN:
if (orphan) {
- localjump_error("return from proc-closure", prot_tag->retval);
+ localjump_error("return from proc-closure", prot_tag->retval, state);
}
default:
JUMP_TAG(state);
@@ -6877,6 +7315,10 @@ mnew(klass, obj, id, mklass)
goto again;
}
+ while (rklass != klass &&
+ (FL_TEST(rklass, FL_SINGLETON) || TYPE(rklass) == T_ICLASS)) {
+ rklass = RCLASS(rklass)->super;
+ }
if (TYPE(klass) == T_ICLASS) klass = RBASIC(klass)->klass;
method = Data_Make_Struct(mklass, struct METHOD, bm_mark, free, data);
data->klass = klass;
@@ -6932,13 +7374,6 @@ method_unbind(obj)
}
static VALUE
-umethod_unbind(obj)
- VALUE obj;
-{
- return obj;
-}
-
-static VALUE
rb_obj_method(obj, vid)
VALUE obj;
VALUE vid;
@@ -6975,7 +7410,7 @@ method_call(argc, argv, method)
VALUE *argv;
VALUE method;
{
- VALUE result;
+ VALUE result; /* OK */
struct METHOD *data;
int state;
volatile int safe = ruby_safe_level;
@@ -7014,9 +7449,7 @@ umethod_bind(method, recv)
st_lookup(RCLASS(CLASS_OF(recv))->m_tbl, data->oid, 0)) {
rb_raise(rb_eTypeError, "method `%s' overridden", rb_id2name(data->oid));
}
- if (!((TYPE(data->rklass) == T_MODULE) ?
- rb_obj_is_kind_of(recv, data->rklass) :
- rb_obj_is_instance_of(recv, data->rklass))) {
+ if(!rb_obj_is_kind_of(recv, data->rklass)) {
rb_raise(rb_eTypeError, "bind argument must be an instance of %s",
rb_class2name(data->rklass));
}
@@ -7051,6 +7484,9 @@ method_arity(method)
return INT2FIX(1);
case NODE_IVAR:
return INT2FIX(0);
+ case NODE_BMETHOD:
+ case NODE_DMETHOD:
+ return proc_arity(method);
default:
body = body->nd_next; /* skip NODE_SCOPE */
if (nd_type(body) == NODE_BLOCK)
@@ -7074,7 +7510,7 @@ method_inspect(method)
Data_Get_Struct(method, struct METHOD, data);
str = rb_str_buf_new2("#<");
- s = rb_class2name(CLASS_OF(method));
+ s = rb_obj_classname(method);
rb_str_buf_cat2(str, s);
rb_str_buf_cat2(str, ": ");
@@ -7112,14 +7548,15 @@ method_inspect(method)
}
static VALUE
-mproc()
+mproc(method)
+ VALUE method;
{
VALUE proc;
/* emulate ruby's method call */
PUSH_ITER(ITER_CUR);
PUSH_FRAME();
- proc = rb_f_lambda();
+ proc = rb_block_proc();
POP_FRAME();
POP_ITER();
@@ -7130,37 +7567,55 @@ static VALUE
bmcall(args, method)
VALUE args, method;
{
- args = svalue_to_avalue(args);
- return method_call(RARRAY(args)->len, RARRAY(args)->ptr, method);
-}
+ volatile VALUE a;
-static VALUE
-umcall(args, method)
- VALUE args, method;
-{
- return method_call(0, 0, method);
+ a = svalue_to_avalue(args);
+ return method_call(RARRAY(a)->len, RARRAY(a)->ptr, method);
}
+struct proc_funcall_data {
+ VALUE (*func)(ANYARGS);
+ VALUE val;
+};
+
VALUE
rb_proc_new(func, val)
VALUE (*func)(ANYARGS); /* VALUE yieldarg[, VALUE procarg] */
VALUE val;
{
- return rb_iterate((VALUE(*)_((VALUE)))mproc, 0, func, val);
+ struct BLOCK *data;
+ VALUE proc = rb_iterate((VALUE(*)_((VALUE)))mproc, 0, func, val);
+
+ Data_Get_Struct(proc, struct BLOCK, data);
+ data->body->nd_state = YIELD_FUNC_AVALUE;
+ return proc;
}
static VALUE
method_proc(method)
VALUE method;
{
- return rb_iterate((VALUE(*)_((VALUE)))mproc, 0, bmcall, method);
+ VALUE proc;
+ struct METHOD *mdata;
+ struct BLOCK *bdata;
+
+ proc = rb_iterate((VALUE(*)_((VALUE)))mproc, 0, bmcall, method);
+ Data_Get_Struct(method, struct METHOD, mdata);
+ Data_Get_Struct(proc, struct BLOCK, bdata);
+ bdata->body->nd_file = mdata->body->nd_file;
+ nd_set_line(bdata->body, nd_line(mdata->body));
+
+ return proc;
}
static VALUE
-umethod_proc(method)
- VALUE method;
+rb_obj_is_method(m)
+ VALUE m;
{
- return rb_iterate((VALUE(*)_((VALUE)))mproc, 0, umcall, method);
+ if (TYPE(m) == T_DATA && RDATA(m)->dmark == (RUBY_DATA_FUNC)bm_mark) {
+ return Qtrue;
+ }
+ return Qfalse;
}
static VALUE
@@ -7176,14 +7631,14 @@ rb_mod_define_method(argc, argv, mod)
if (argc == 1) {
id = rb_to_id(argv[0]);
- body = rb_f_lambda();
+ body = proc_lambda();
}
else if (argc == 2) {
id = rb_to_id(argv[0]);
body = argv[1];
- if (!rb_obj_is_kind_of(body, rb_cMethod) && !rb_obj_is_proc(body)) {
+ if (!rb_obj_is_method(body) && !rb_obj_is_proc(body)) {
rb_raise(rb_eTypeError, "wrong argument type %s (expected Proc/Method)",
- rb_class2name(CLASS_OF(body)));
+ rb_obj_classname(body));
}
}
else {
@@ -7195,7 +7650,7 @@ rb_mod_define_method(argc, argv, mod)
else if (RDATA(body)->dmark == (RUBY_DATA_FUNC)blk_mark) {
struct BLOCK *block;
- body = bind_clone(body);
+ body = proc_clone(body);
Data_Get_Struct(body, struct BLOCK, block);
block->frame.last_func = id;
block->frame.orig_func = id;
@@ -7217,16 +7672,6 @@ rb_mod_define_method(argc, argv, mod)
noex = NOEX_PUBLIC;
}
rb_add_method(mod, id, node, noex);
- if (scope_vmode == SCOPE_MODFUNC) {
- rb_add_method(rb_singleton_class(mod), id, node, NOEX_PUBLIC);
- rb_funcall(mod, singleton_added, 1, ID2SYM(id));
- }
- if (FL_TEST(mod, FL_SINGLETON)) {
- rb_funcall(rb_iv_get(mod, "__attached__"), singleton_added, 1, ID2SYM(id));
- }
- else {
- rb_funcall(mod, added, 1, ID2SYM(id));
- }
return body;
}
@@ -7234,32 +7679,40 @@ void
Init_Proc()
{
rb_eLocalJumpError = rb_define_class("LocalJumpError", rb_eStandardError);
- rb_define_method(rb_eLocalJumpError, "exitstatus", localjump_exitstatus, 0);
+ rb_define_method(rb_eLocalJumpError, "exit_value", localjump_xvalue, 0);
+ rb_define_method(rb_eLocalJumpError, "reason", localjump_reason, 0);
+
+ exception_error = rb_exc_new2(rb_eFatal, "exception reentered");
+ rb_global_variable(&exception_error);
rb_eSysStackError = rb_define_class("SystemStackError", rb_eStandardError);
+ sysstack_error = rb_exc_new2(rb_eSysStackError, "stack level too deep");
+ rb_global_variable(&sysstack_error);
rb_cProc = rb_define_class("Proc", rb_cObject);
- rb_undef_method(CLASS_OF(rb_cProc), "allocate");
+ rb_undef_alloc_func(rb_cProc);
rb_define_singleton_method(rb_cProc, "new", proc_s_new, -1);
+ rb_define_method(rb_cProc, "clone", proc_clone, 0);
rb_define_method(rb_cProc, "call", proc_call, -2);
- rb_define_method(rb_cProc, "yield", proc_yield, -2);
rb_define_method(rb_cProc, "arity", proc_arity, 0);
rb_define_method(rb_cProc, "[]", proc_call, -2);
rb_define_method(rb_cProc, "==", proc_eq, 1);
rb_define_method(rb_cProc, "to_s", proc_to_s, 0);
- rb_define_method(rb_cProc, "to_proc", proc_to_proc, 0);
+ rb_define_method(rb_cProc, "to_proc", proc_to_self, 0);
rb_define_method(rb_cProc, "binding", proc_binding, 0);
- rb_define_global_function("proc", rb_f_lambda, 0);
- rb_define_global_function("lambda", rb_f_lambda, 0);
- rb_define_global_function("binding", rb_f_binding, 0);
+
+ rb_define_global_function("proc", proc_lambda, 0);
+ rb_define_global_function("lambda", proc_lambda, 0);
+
rb_cBinding = rb_define_class("Binding", rb_cObject);
- rb_undef_method(CLASS_OF(rb_cBinding), "allocate");
+ rb_undef_alloc_func(rb_cBinding);
rb_undef_method(CLASS_OF(rb_cBinding), "new");
- rb_define_method(rb_cBinding, "clone", bind_clone, 0);
+ rb_define_method(rb_cBinding, "clone", proc_clone, 0);
+ rb_define_global_function("binding", rb_f_binding, 0);
rb_cMethod = rb_define_class("Method", rb_cObject);
- rb_undef_method(CLASS_OF(rb_cMethod), "allocate");
+ rb_undef_alloc_func(rb_cMethod);
rb_undef_method(CLASS_OF(rb_cMethod), "new");
rb_define_method(rb_cMethod, "==", method_eq, 1);
rb_define_method(rb_cMethod, "clone", method_clone, 0);
@@ -7272,10 +7725,15 @@ Init_Proc()
rb_define_method(rb_cMethod, "unbind", method_unbind, 0);
rb_define_method(rb_mKernel, "method", rb_obj_method, 1);
- rb_cUnboundMethod = rb_define_class("UnboundMethod", rb_cMethod);
- rb_define_method(rb_cUnboundMethod, "to_proc", umethod_proc, 0);
+ rb_cUnboundMethod = rb_define_class("UnboundMethod", rb_cObject);
+ rb_undef_alloc_func(rb_cUnboundMethod);
+ rb_undef_method(CLASS_OF(rb_cUnboundMethod), "new");
+ rb_define_method(rb_cUnboundMethod, "==", method_eq, 1);
+ rb_define_method(rb_cUnboundMethod, "clone", method_clone, 0);
+ rb_define_method(rb_cUnboundMethod, "arity", method_arity, 0);
+ rb_define_method(rb_cUnboundMethod, "inspect", method_inspect, 0);
+ rb_define_method(rb_cUnboundMethod, "to_s", method_inspect, 0);
rb_define_method(rb_cUnboundMethod, "bind", umethod_bind, 1);
- rb_define_method(rb_cUnboundMethod, "unbind", umethod_unbind, 0);
rb_define_method(rb_cModule, "instance_method", rb_mod_method, 1);
}
@@ -7345,7 +7803,7 @@ enum thread_status {
THREAD_TO_KILL,
THREAD_RUNNABLE,
THREAD_STOPPED,
- THREAD_KILLED
+ THREAD_KILLED,
};
#define WAIT_FD (1<<0)
@@ -7357,6 +7815,14 @@ enum thread_status {
/* +infty, for this purpose */
#define DELAY_INFTY 1E30
+#if !defined HAVE_PAUSE
+# if defined _WIN32 && !defined __CYGWIN__
+# define pause() Sleep(INFINITE)
+# else
+# define pause() sleep(0x7fffffff)
+# endif
+#endif
+
/* typedef struct thread * rb_thread_t; */
struct thread {
@@ -7407,7 +7873,7 @@ struct thread {
int abort;
int priority;
- int gid;
+ VALUE thgroup;
st_table *locals;
@@ -7468,6 +7934,20 @@ struct thread_status_t {
(dst)->join = (src)->join, \
0)
+static int
+thread_set_raised()
+{
+ if (curr_thread->flags & THREAD_RAISED) return 1;
+ curr_thread->flags |= THREAD_RAISED;
+ return 0;
+}
+
+static void
+thread_reset_raised()
+{
+ curr_thread->flags &= ~THREAD_RAISED;
+}
+
static void rb_thread_ready _((rb_thread_t));
static VALUE
@@ -7653,7 +8133,7 @@ rb_thread_check(data)
{
if (TYPE(data) != T_DATA || RDATA(data)->dmark != (RUBY_DATA_FUNC)thread_mark) {
rb_raise(rb_eTypeError, "wrong argument type %s (expected Thread)",
- rb_class2name(CLASS_OF(data)));
+ rb_obj_classname(data));
}
return (rb_thread_t)RDATA(data)->data;
}
@@ -7686,8 +8166,7 @@ rb_thread_save_context(th)
len = ruby_stack_length(&pos);
th->stk_len = 0;
- th->stk_pos = (rb_gc_stack_start<pos)?rb_gc_stack_start
- :rb_gc_stack_start - len;
+ th->stk_pos = pos;
if (len > th->stk_max) {
REALLOC_N(th->stk_ptr, VALUE, len);
th->stk_max = len;
@@ -7725,9 +8204,10 @@ rb_thread_save_context(th)
}
static int
-thread_switch(n)
+rb_thread_switch(n)
int n;
{
+ rb_trap_immediate = (curr_thread->flags&0x100)?1:0;
switch (n) {
case 0:
return 0;
@@ -7756,7 +8236,8 @@ thread_switch(n)
}
#define THREAD_SAVE_CONTEXT(th) \
- (rb_thread_save_context(th),thread_switch(setjmp((th)->context)))
+ (rb_thread_save_context(th),\
+ rb_thread_switch((FLUSH_REGISTER_WINDOWS, setjmp((th)->context))))
static void rb_thread_restore_context _((rb_thread_t,int));
@@ -7783,6 +8264,11 @@ rb_thread_restore_context(th, exit)
if (!th->stk_ptr) rb_bug("unsaved context");
+#if STACK_GROW_DIRECTION < 0
+ if (&v > th->stk_pos) stack_extend(th, exit);
+#elif STACK_GROW_DIRECTION > 0
+ if (&v < th->stk_pos + th->stk_len) stack_extend(th, exit);
+#else
if (&v < rb_gc_stack_start) {
/* Stack grows downward */
if (&v > th->stk_pos) stack_extend(th, exit);
@@ -7791,7 +8277,9 @@ rb_thread_restore_context(th, exit)
/* Stack grows upward */
if (&v < th->stk_pos + th->stk_len) stack_extend(th, exit);
}
+#endif
+ rb_trap_immediate = 0; /* inhibit interrupts from here */
ruby_frame = th->frame;
ruby_scope = th->scope;
ruby_class = th->klass;
@@ -7800,7 +8288,6 @@ rb_thread_restore_context(th, exit)
ruby_dyna_vars = th->dyna_vars;
ruby_block = th->block;
scope_vmode = th->flags&SCOPE_MASK;
- rb_trap_immediate = (th->flags&0x100)?1:0;
ruby_iter = th->iter;
prot_tag = th->tag;
tracing = th->tracing;
@@ -7842,7 +8329,7 @@ static void
rb_thread_die(th)
rb_thread_t th;
{
- th->gid = 0;
+ th->thgroup = 0;
th->status = THREAD_KILLED;
if (th->stk_ptr) free(th->stk_ptr);
th->stk_ptr = 0;
@@ -7874,7 +8361,11 @@ rb_thread_fd_close(fd)
rb_thread_t th;
FOREACH_THREAD(th) {
- if ((th->wait_for & WAIT_FD) && fd == th->fd) {
+ if (((th->wait_for & WAIT_FD) && fd == th->fd) ||
+ ((th->wait_for & WAIT_SELECT) && (fd < th->fd) &&
+ (FD_ISSET(fd, &th->readfds) ||
+ FD_ISSET(fd, &th->writefds) ||
+ FD_ISSET(fd, &th->exceptfds)))) {
VALUE exc = rb_exc_new2(rb_eIOError, "stream closed");
rb_thread_raise(1, &exc, th);
}
@@ -7986,6 +8477,8 @@ rb_thread_schedule()
int need_select = 0;
int select_timeout = 0;
+ if (ruby_in_compile) abort();
+
rb_thread_pending = 0;
if (curr_thread == curr_thread->next
&& curr_thread->status == THREAD_RUNNABLE)
@@ -8161,26 +8654,32 @@ rb_thread_schedule()
if (!next) {
/* raise fatal error to main thread */
curr_thread->node = ruby_current_node;
+ if (curr->next == curr) {
+ TRAP_BEG;
+ pause();
+ TRAP_END;
+ }
FOREACH_THREAD_FROM(curr, th) {
- fprintf(stderr, "deadlock 0x%lx: %s:",
- th->thread, thread_status_name(th->status));
- if (th->wait_for & WAIT_FD) fprintf(stderr, "F(%d)", th->fd);
- if (th->wait_for & WAIT_SELECT) fprintf(stderr, "S");
- if (th->wait_for & WAIT_TIME) fprintf(stderr, "T(%f)", th->delay);
+ warn_printf("deadlock 0x%lx: %s:",
+ th->thread, thread_status_name(th->status));
+ if (th->wait_for & WAIT_FD) warn_printf("F(%d)", th->fd);
+ if (th->wait_for & WAIT_SELECT) warn_printf("S");
+ if (th->wait_for & WAIT_TIME) warn_printf("T(%f)", th->delay);
if (th->wait_for & WAIT_JOIN)
- fprintf(stderr, "J(0x%lx)", th->join ? th->join->thread : 0);
- if (th->wait_for & WAIT_PID) fprintf(stderr, "P");
- if (!th->wait_for) fprintf(stderr, "-");
- fprintf(stderr, " %s - %s:%d\n",
- th==main_thread ? "(main)" : "",
- th->node->nd_file, nd_line(th->node));
+ warn_printf("J(0x%lx)", th->join ? th->join->thread : 0);
+ if (th->wait_for & WAIT_PID) warn_printf("P");
+ if (!th->wait_for) warn_printf("-");
+ warn_printf(" %s - %s:%d\n",
+ th==main_thread ? "(main)" : "",
+ th->node->nd_file, nd_line(th->node));
}
END_FOREACH_FROM(curr, th);
- fflush(stderr);
next = main_thread;
rb_thread_ready(next);
next->status = THREAD_TO_KILL;
- rb_thread_save_context(curr_thread);
+ if (!rb_thread_dead(curr_thread)) {
+ rb_thread_save_context(curr_thread);
+ }
rb_thread_deadlock();
}
next->wait_for = 0;
@@ -8211,6 +8710,7 @@ rb_thread_wait_fd(fd)
int fd;
{
if (rb_thread_critical) return;
+ if (ruby_in_compile) return;
if (curr_thread == curr_thread->next) return;
if (curr_thread->status == THREAD_TO_KILL) return;
@@ -8501,7 +9001,7 @@ rb_thread_run(thread)
return thread;
}
-static VALUE
+VALUE
rb_thread_kill(thread)
VALUE thread;
{
@@ -8515,7 +9015,6 @@ rb_thread_kill(thread)
if (th == th->next || th == main_thread) rb_exit(0);
rb_thread_ready(th);
- th->gid = 0;
th->status = THREAD_TO_KILL;
if (!rb_thread_critical) rb_thread_schedule();
return thread;
@@ -8585,14 +9084,6 @@ rb_thread_sleep(sec)
rb_thread_wait_for(rb_time_timeval(INT2FIX(sec)));
}
-#if !defined HAVE_PAUSE
-# if defined _WIN32 && !defined __CYGWIN__
-# define pause() Sleep(INFINITE)
-# else
-# define pause() sleep(0x7fffffff)
-# endif
-#endif
-
void
rb_thread_sleep_forever()
{
@@ -8644,12 +9135,13 @@ rb_thread_safe_level(thread)
return INT2NUM(th->safe);
}
-static int thread_abort;
+static int ruby_thread_abort;
+static VALUE thgroup_default;
static VALUE
rb_thread_s_abort_exc()
{
- return thread_abort?Qtrue:Qfalse;
+ return ruby_thread_abort?Qtrue:Qfalse;
}
static VALUE
@@ -8657,7 +9149,7 @@ rb_thread_s_abort_exc_set(self, val)
VALUE self, val;
{
rb_secure(4);
- thread_abort = RTEST(val);
+ ruby_thread_abort = RTEST(val);
return val;
}
@@ -8677,6 +9169,17 @@ rb_thread_abort_exc_set(thread, val)
return val;
}
+VALUE
+rb_thread_group(thread)
+ VALUE thread;
+{
+ VALUE group = rb_thread_check(thread)->thgroup;
+ if (!group) {
+ group = Qnil;
+ }
+ return group;
+}
+
#define THREAD_ALLOC(th) do {\
th = ALLOC(struct thread);\
\
@@ -8713,7 +9216,7 @@ rb_thread_abort_exc_set(thread, val)
th->last_match = Qnil;\
th->abort = 0;\
th->priority = 0;\
- th->gid = 1;\
+ th->thgroup = thgroup_default;\
th->locals = 0;\
} while (0)
@@ -8793,6 +9296,11 @@ rb_thread_start_0(fn, arg, th_arg)
enum thread_status status;
int state;
+ if (OBJ_FROZEN(curr_thread->thgroup)) {
+ rb_raise(rb_eThreadError,
+ "can't start a new thread (frozen ThreadGroup)");
+ }
+
#if defined(HAVE_SETITIMER)
if (!thread_init) {
#ifdef POSIX_SIGNAL
@@ -8826,7 +9334,7 @@ rb_thread_start_0(fn, arg, th_arg)
th->next = curr_thread->next;
curr_thread->next = th;
th->priority = curr_thread->priority;
- th->gid = curr_thread->gid;
+ th->thgroup = curr_thread->thgroup;
}
PUSH_TAG(PROT_THREAD);
@@ -8870,7 +9378,7 @@ rb_thread_start_0(fn, arg, th_arg)
rb_thread_raise(1, &ruby_errinfo, main_thread);
}
}
- else if (th->safe < 4 && (thread_abort || th->abort || RTEST(ruby_debug))) {
+ else if (th->safe < 4 && (ruby_thread_abort || th->abort || RTEST(ruby_debug))) {
VALUE err = system_exit(1, 0, 0);
error_print();
/* exit on main_thread */
@@ -8881,6 +9389,7 @@ rb_thread_start_0(fn, arg, th_arg)
}
}
rb_thread_schedule();
+ ruby_stop(0); /* last thread termination */
return 0; /* not reached */
}
@@ -8889,6 +9398,7 @@ rb_thread_create(fn, arg)
VALUE (*fn)();
void *arg;
{
+ Init_stack((VALUE*)&arg);
return rb_thread_start_0(fn, arg, rb_thread_alloc(rb_cThread));
}
@@ -8914,7 +9424,7 @@ rb_thread_yield(arg, th)
rb_dvar_push('~', Qnil);
ruby_block->dyna_vars = ruby_dyna_vars;
- return rb_yield_0(mvalue_to_svalue(arg), 0, 0, Qtrue);
+ return rb_yield_0(arg, 0, 0, Qtrue, Qtrue);
}
static VALUE
@@ -9037,7 +9547,7 @@ rb_thread_cleanup()
FOREACH_THREAD_FROM(curr, th) {
if (th->status != THREAD_KILLED) {
rb_thread_ready(th);
- th->gid = 0;
+ th->thgroup = 0;
th->priority = 0;
if (th != main_thread) {
th->status = THREAD_TO_KILL;
@@ -9221,7 +9731,7 @@ rb_thread_local_aset(thread, id, val)
th->locals = st_init_numtable();
}
if (NIL_P(val)) {
- st_delete(th->locals, &id, 0);
+ st_delete(th->locals, (st_data_t*)&id, 0);
return Qnil;
}
st_insert(th->locals, id, val);
@@ -9274,7 +9784,7 @@ static VALUE
rb_thread_inspect(thread)
VALUE thread;
{
- char *cname = rb_class2name(CLASS_OF(thread));
+ char *cname = rb_obj_classname(thread);
rb_thread_t th = rb_thread_check(thread);
const char *status = thread_status_name(th->status);
VALUE str;
@@ -9373,19 +9883,21 @@ rb_cont_call(argc, argv, cont)
}
struct thgroup {
- int gid;
+ int enclosed;
+ VALUE group;
};
+static VALUE thgroup_s_alloc _((VALUE));
static VALUE
thgroup_s_alloc(klass)
VALUE klass;
{
VALUE group;
struct thgroup *data;
- static int serial = 1;
group = Data_Make_Struct(klass, struct thgroup, 0, free, data);
- data->gid = serial++;
+ data->enclosed = 0;
+ data->group = group;
return group;
}
@@ -9402,7 +9914,7 @@ thgroup_list(group)
ary = rb_ary_new();
FOREACH_THREAD(th) {
- if (th->gid == data->gid) {
+ if (th->thgroup == data->group) {
rb_ary_push(ary, th->thread);
}
}
@@ -9411,6 +9923,29 @@ thgroup_list(group)
return ary;
}
+VALUE
+thgroup_enclose(group)
+ VALUE group;
+{
+ struct thgroup *data;
+
+ Data_Get_Struct(group, struct thgroup, data);
+ data->enclosed = 1;
+
+ return group;
+}
+
+static VALUE
+thgroup_enclosed_p(group)
+ VALUE group;
+{
+ struct thgroup *data;
+
+ Data_Get_Struct(group, struct thgroup, data);
+ if (data->enclosed) return Qtrue;
+ return Qfalse;
+}
+
static VALUE
thgroup_add(group, thread)
VALUE group, thread;
@@ -9420,9 +9955,27 @@ thgroup_add(group, thread)
rb_secure(4);
th = rb_thread_check(thread);
+
+ if (OBJ_FROZEN(group)) {
+ rb_raise(rb_eThreadError, "can't move to the frozen thread group");
+ }
Data_Get_Struct(group, struct thgroup, data);
+ if (data->enclosed) {
+ rb_raise(rb_eThreadError, "can't move to the enclosed thread group");
+ }
- th->gid = data->gid;
+ if (!th->thgroup) {
+ return Qnil;
+ }
+ if (OBJ_FROZEN(th->thgroup)) {
+ rb_raise(rb_eThreadError, "can't move from the frozen thread group");
+ }
+ Data_Get_Struct(th->thgroup, struct thgroup, data);
+ if (data->enclosed) {
+ rb_raise(rb_eThreadError, "can't move from the enclosed thread group");
+ }
+
+ th->thgroup = group;
return group;
}
@@ -9433,7 +9986,7 @@ Init_Thread()
rb_eThreadError = rb_define_class("ThreadError", rb_eStandardError);
rb_cThread = rb_define_class("Thread", rb_cObject);
- rb_undef_method(CLASS_OF(rb_cThread), "allocate");
+ rb_undef_alloc_func(rb_cThread);
rb_define_singleton_method(rb_cThread, "new", rb_thread_s_new, -1);
rb_define_method(rb_cThread, "initialize", rb_thread_initialize, -2);
@@ -9472,6 +10025,7 @@ Init_Thread()
rb_define_method(rb_cThread, "priority", rb_thread_priority, 0);
rb_define_method(rb_cThread, "priority=", rb_thread_priority_set, 1);
rb_define_method(rb_cThread, "safe_level", rb_thread_safe_level, 0);
+ rb_define_method(rb_cThread, "group", rb_thread_group, 0);
rb_define_method(rb_cThread, "[]", rb_thread_aref, 1);
rb_define_method(rb_cThread, "[]=", rb_thread_aset, 2);
@@ -9480,21 +10034,24 @@ Init_Thread()
rb_define_method(rb_cThread, "inspect", rb_thread_inspect, 0);
- /* allocate main thread */
- main_thread = rb_thread_alloc(rb_cThread);
- curr_thread = main_thread->prev = main_thread->next = main_thread;
-
rb_cCont = rb_define_class("Continuation", rb_cObject);
- rb_undef_method(CLASS_OF(rb_cCont), "allocate");
+ rb_undef_alloc_func(rb_cCont);
rb_undef_method(CLASS_OF(rb_cCont), "new");
rb_define_method(rb_cCont, "call", rb_cont_call, -1);
rb_define_global_function("callcc", rb_callcc, 0);
cThGroup = rb_define_class("ThreadGroup", rb_cObject);
- rb_define_singleton_method(cThGroup, "allocate", thgroup_s_alloc, 0);
+ rb_define_alloc_func(cThGroup, thgroup_s_alloc);
rb_define_method(cThGroup, "list", thgroup_list, 0);
+ rb_define_method(cThGroup, "enclose", thgroup_enclose, 0);
+ rb_define_method(cThGroup, "enclosed?", thgroup_enclosed_p, 0);
rb_define_method(cThGroup, "add", thgroup_add, 1);
- rb_define_const(cThGroup, "Default", rb_obj_alloc(cThGroup));
+ thgroup_default = rb_obj_alloc(cThGroup);
+ rb_define_const(cThGroup, "Default", thgroup_default);
+
+ /* allocate main thread */
+ main_thread = rb_thread_alloc(rb_cThread);
+ curr_thread = main_thread->prev = main_thread->next = main_thread;
}
static VALUE
@@ -9508,7 +10065,7 @@ rb_f_catch(dmy, tag)
t = rb_to_id(tag);
PUSH_TAG(t);
if ((state = EXEC_TAG()) == 0) {
- val = rb_yield_0(tag, 0, 0, 0);
+ val = rb_yield_0(tag, 0, 0, Qfalse, Qfalse);
}
else if (state == TAG_THROW && t == prot_tag->dst) {
val = prot_tag->retval;
@@ -9528,12 +10085,12 @@ catch_i(tag)
}
VALUE
-rb_catch(tag, proc, data)
+rb_catch(tag, func, data)
const char *tag;
- VALUE (*proc)();
+ VALUE (*func)();
VALUE data;
{
- return rb_iterate((VALUE(*)_((VALUE)))catch_i, rb_intern(tag), proc, data);
+ return rb_iterate((VALUE(*)_((VALUE)))catch_i, rb_intern(tag), func, data);
}
static VALUE
@@ -9566,6 +10123,9 @@ rb_f_throw(argc, argv)
return_value(value);
rb_trap_restore_mask();
JUMP_TAG(TAG_THROW);
+#ifndef __GNUC__
+ return Qnil; /* not reached */
+#endif
}
void
diff --git a/ext/Setup b/ext/Setup
index be5f73a3c9..157dd8a85d 100644
--- a/ext/Setup
+++ b/ext/Setup
@@ -1,6 +1,7 @@
#option nodynamic
#Win32API
+#bigdecimal
#curses
#dbm
#digest
@@ -21,7 +22,9 @@
#socket
#stringio
#strscan
+#syck
#syslog
#tcltklib
#tk
#win32ole
+#zlib
diff --git a/ext/Setup.atheos b/ext/Setup.atheos
index 8f04f06a4b..67c03b9e95 100644
--- a/ext/Setup.atheos
+++ b/ext/Setup.atheos
@@ -1,5 +1,6 @@
option nodynamic
+bigdecimal
curses
dbm
digest
@@ -12,7 +13,6 @@ etc
fcntl
gdbm
iconv
-marshal
nkf
pty
racc/parse
@@ -21,7 +21,8 @@ sdbm
socket
stringio
strscan
+syck
syslog
-#tk
#tcltklib
-#gtk
+#tk
+zlib
diff --git a/ext/Setup.dj b/ext/Setup.dj
index b6b5165bce..e5a10ffea1 100644
--- a/ext/Setup.dj
+++ b/ext/Setup.dj
@@ -1,6 +1,7 @@
option nodynamic
#Win32API
+bigdecimal
#curses
dbm
digest
@@ -21,7 +22,9 @@ sdbm
#socket
stringio
strscan
+syck
#syslog
#tcltklib
#tk
#win32ole
+#zlib
diff --git a/ext/Setup.emx b/ext/Setup.emx
index 90e1873702..de38b54f55 100644
--- a/ext/Setup.emx
+++ b/ext/Setup.emx
@@ -1,6 +1,7 @@
option nodynamic
#Win32API
+bigdecimal
curses
#dbm
digest
@@ -21,7 +22,9 @@ racc/cparse
socket
stringio
strscan
+#syck
#syslog
#tcltklib
#tk
#win32ole
+#zlib
diff --git a/ext/Setup.nt b/ext/Setup.nt
index 02638c8eab..d4e2e3adbe 100644
--- a/ext/Setup.nt
+++ b/ext/Setup.nt
@@ -1,6 +1,7 @@
#option nodynamic
Win32API
+bigdecimal
#curses
#dbm
digest
@@ -21,7 +22,9 @@ sdbm
socket
stringio
strscan
+#syck
#syslog
#tcltklib
#tk
win32ole
+#zlib
diff --git a/ext/Setup.x68 b/ext/Setup.x68
index a5e4bc64bb..dd70ea786d 100644
--- a/ext/Setup.x68
+++ b/ext/Setup.x68
@@ -1,6 +1,7 @@
option nodynamic
#Win32API
+bigdecimal
#curses
dbm
digest
@@ -21,7 +22,9 @@ racc/cparse
#socket
stringio
strscan
+#syck
#syslog
#tcltklib
#tk
#win32ole
+#zlib
diff --git a/ext/Win32API/MANIFEST b/ext/Win32API/MANIFEST
index 7cc9ac445e..ff3a399d26 100644
--- a/ext/Win32API/MANIFEST
+++ b/ext/Win32API/MANIFEST
@@ -5,3 +5,5 @@ Win32API.c
extconf.rb
getch.rb
point.rb
+lib/win32/registry.rb
+lib/win32/resolv.rb
diff --git a/ext/Win32API/Win32API.c b/ext/Win32API/Win32API.c
index ee9a1e0569..a33e989d27 100644
--- a/ext/Win32API/Win32API.c
+++ b/ext/Win32API/Win32API.c
@@ -8,27 +8,11 @@
#include <stdio.h>
#endif
-#if defined(_MSC_VER)
-#if defined(_M_ALPHA)
-#ifdef __cplusplus
-extern "C" { long __asm(char *,...); };
-#else
-long __asm(char *,...);
-#endif
-#pragma intrinsic(__asm)
-#endif
-#endif
-
#define _T_VOID 0
#define _T_NUMBER 1
#define _T_POINTER 2
#define _T_INTEGER 3
-typedef char *ApiPointer(void);
-typedef long ApiNumber(void);
-typedef void ApiVoid(void);
-typedef int ApiInteger(void);
-
#include "ruby.h"
typedef struct {
@@ -62,7 +46,7 @@ Win32API_initialize(self, dllname, proc, import, export)
char *s;
int i;
int len;
- int ex;
+ int ex = _T_VOID;
SafeStringValue(dllname);
SafeStringValue(proc);
@@ -85,43 +69,48 @@ Win32API_initialize(self, dllname, proc, import, export)
a_import = rb_ary_new();
switch (TYPE(import)) {
- case T_NIL:
+ case T_NIL:
break;
- case T_ARRAY:
+ case T_ARRAY:
ptr = RARRAY(import)->ptr;
for (i = 0, len = RARRAY(import)->len; i < len; i++) {
SafeStringValue(ptr[i]);
switch (*(char *)RSTRING(ptr[i])->ptr) {
- case 'N': case 'n': case 'L': case 'l':
+ case 'N': case 'n': case 'L': case 'l':
rb_ary_push(a_import, INT2FIX(_T_NUMBER));
break;
- case 'P': case 'p':
+ case 'P': case 'p':
rb_ary_push(a_import, INT2FIX(_T_POINTER));
break;
- case 'I': case 'i':
+ case 'I': case 'i':
rb_ary_push(a_import, INT2FIX(_T_INTEGER));
break;
}
}
break;
- default:
+ default:
SafeStringValue(import);
s = RSTRING(import)->ptr;
for (i = 0, len = RSTRING(import)->len; i < len; i++) {
switch (*s++) {
- case 'N': case 'n': case 'L': case 'l':
+ case 'N': case 'n': case 'L': case 'l':
rb_ary_push(a_import, INT2FIX(_T_NUMBER));
break;
- case 'P': case 'p':
+ case 'P': case 'p':
rb_ary_push(a_import, INT2FIX(_T_POINTER));
break;
- case 'I': case 'i':
+ case 'I': case 'i':
rb_ary_push(a_import, INT2FIX(_T_INTEGER));
break;
}
}
break;
}
+
+ if (16 < RARRAY(a_import)->len) {
+ rb_raise(rb_eRuntimeError, "too many parameters: %d\n", RARRAY(a_import)->len);
+ }
+
rb_iv_set(self, "__import__", a_import);
if (NIL_P(export)) {
@@ -129,16 +118,16 @@ Win32API_initialize(self, dllname, proc, import, export)
} else {
SafeStringValue(export);
switch (*RSTRING(export)->ptr) {
- case 'V': case 'v':
+ case 'V': case 'v':
ex = _T_VOID;
break;
- case 'N': case 'n': case 'L': case 'l':
+ case 'N': case 'n': case 'L': case 'l':
ex = _T_NUMBER;
break;
- case 'P': case 'p':
+ case 'P': case 'p':
ex = _T_POINTER;
break;
- case 'I': case 'i':
+ case 'I': case 'i':
ex = _T_INTEGER;
break;
}
@@ -148,15 +137,6 @@ Win32API_initialize(self, dllname, proc, import, export)
return Qnil;
}
-#ifdef __BORLANDC__
-int c_m( FARPROC api, long* p )
-{
- long pp[16];
- memcpy( pp, p, 16*sizeof(long) );
- return api();
-}
-#endif
-
static VALUE
Win32API_Call(argc, argv, obj)
int argc;
@@ -164,171 +144,62 @@ Win32API_Call(argc, argv, obj)
VALUE obj;
{
VALUE args;
+ unsigned long ret;
+ int i;
+ struct {
+ unsigned long params[16];
+ } param;
+#define params param.params
- FARPROC ApiFunction;
-
- ApiPointer *ApiFunctionPointer;
- ApiNumber *ApiFunctionNumber;
- ApiVoid *ApiFunctionVoid;
- ApiInteger *ApiFunctionInteger;
-
- long lParam;
- char *pParam;
-
- VALUE Return;
-
- VALUE obj_proc;
- VALUE obj_import;
- VALUE obj_export;
- VALUE import_type;
- int nimport, timport, texport, i;
- int items;
- int ret;
-#ifdef __BORLANDC__
- long* ptr;
- long p[16];
-#endif
-
- items = rb_scan_args(argc, argv, "0*", &args);
-
- obj_proc = rb_iv_get(obj, "__proc__");
-
- ApiFunction = (FARPROC)NUM2ULONG(obj_proc);
+ VALUE obj_proc = rb_iv_get(obj, "__proc__");
+ VALUE obj_import = rb_iv_get(obj, "__import__");
+ VALUE obj_export = rb_iv_get(obj, "__export__");
+ FARPROC ApiFunction = (FARPROC)NUM2ULONG(obj_proc);
+ int items = rb_scan_args(argc, argv, "0*", &args);
+ int nimport = RARRAY(obj_import)->len;
- obj_import = rb_iv_get(obj, "__import__");
- obj_export = rb_iv_get(obj, "__export__");
- nimport = RARRAY(obj_import)->len;
- texport = FIX2INT(obj_export);
if (items != nimport)
rb_raise(rb_eRuntimeError, "Wrong number of parameters: expected %d, got %d.\n",
nimport, items);
- if (0 < nimport) {
-#ifdef __BORLANDC__
- ptr = p + ( nimport - 1 );
-#endif
- for (i = nimport - 1; 0 <= i; i--) {
+ for (i = 0; i < nimport; i++) {
+ unsigned long lParam = 0;
+ switch (FIX2INT(rb_ary_entry(obj_import, i))) {
VALUE str;
- import_type = rb_ary_entry(obj_import, i);
- timport = FIX2INT(import_type);
- switch (timport) {
- case _T_NUMBER:
- case _T_INTEGER:
- lParam = NUM2ULONG(rb_ary_entry(args, i));
-#if defined(_MSC_VER) || defined(__LCC__)
-#if defined(_M_IX86)
- _asm {
- mov eax, lParam
- push eax
- }
-#elif defined(_M_ALPHA)
- __asm(
- "ldl r0, 0(%0);"
- "stq r0, -(sp);"
- , lParam
- );
-#else
-#error
-#endif
-#elif defined(__BORLANDC__)
- *ptr = lParam;
- --ptr;
-#elif defined __GNUC__
- asm volatile ("pushl %0" :: "g" (lParam));
-#else
-#error
-#endif
- break;
- case _T_POINTER:
- str = rb_ary_entry(args, i);
- if (NIL_P(str)) {
- pParam = 0;
- } else if (FIXNUM_P(str)){
- pParam = (char *)NUM2ULONG(str);
- } else {
- StringValue(str);
- rb_str_modify(str);
- pParam = StringValuePtr(str);
- }
-#if defined(_MSC_VER) || defined(__LCC__)
-#if defined(_M_IX86)
- _asm {
- mov eax, pParam
- push eax
- }
-#elif defined(_M_ALPHA)
- __asm(
- "ldl r0, 0(%0);"
- "stq r0, -(sp);"
- , pParam
- );
-#else
-#error
-#endif
-#elif defined(__BORLANDC__)
- *ptr = (long)pParam;
- --ptr;
-#elif defined __GNUC__
- asm volatile ("pushl %0" :: "g" (pParam));
-#else
-#error
-#endif
- break;
+ case _T_NUMBER:
+ case _T_INTEGER:
+ default:
+ lParam = NUM2ULONG(rb_ary_entry(args, i));
+ break;
+ case _T_POINTER:
+ str = rb_ary_entry(args, i);
+ if (NIL_P(str)) {
+ lParam = 0;
+ } else if (FIXNUM_P(str)) {
+ lParam = NUM2ULONG(str);
+ } else {
+ StringValue(str);
+ rb_str_modify(str);
+ lParam = (unsigned long)StringValuePtr(str);
}
+ break;
}
+ params[i] = lParam;
}
-#if defined __GNUC__
- asm volatile ("call *%1" : "=r" (ret) : "g" (ApiFunction));
- switch (texport) {
+ ret = ApiFunction(param);
+
+ switch (FIX2INT(obj_export)) {
case _T_NUMBER:
case _T_INTEGER:
- Return = INT2NUM(ret);
- break;
- case _T_POINTER:
- Return = rb_str_new2((char *)ret);
- break;
- case _T_VOID:
- default:
- Return = INT2NUM(0);
- break;
- }
-#else
- switch (texport) {
- case _T_NUMBER:
-#if defined(__BORLANDC__)
- Return = INT2NUM((long)c_m(ApiFunction, p));
-#else
- ApiFunctionNumber = (ApiNumber *) ApiFunction;
- Return = INT2NUM(ApiFunctionNumber());
-#endif
- break;
+ return INT2NUM(ret);
case _T_POINTER:
-#if defined(__BORLANDC__)
- Return = rb_str_new2((char *)c_m(ApiFunction, p));
-#else
- ApiFunctionPointer = (ApiPointer *) ApiFunction;
- Return = rb_str_new2((char *)ApiFunctionPointer());
-#endif
- break;
- case _T_INTEGER:
-#if defined(__BORLANDC__)
- Return = INT2NUM((int)c_m(ApiFunction, p));
-#else
- ApiFunctionInteger = (ApiInteger *) ApiFunction;
- Return = INT2NUM(ApiFunctionInteger());
-#endif
- break;
+ return rb_str_new2((char *)ret);
case _T_VOID:
default:
- ApiFunctionVoid = (ApiVoid *) ApiFunction;
- ApiFunctionVoid();
- Return = INT2NUM(0);
- break;
+ return INT2NUM(0);
}
-#endif
- return Return;
}
void
diff --git a/ext/Win32API/extconf.rb b/ext/Win32API/extconf.rb
index 8117a38720..134a6e5b92 100644
--- a/ext/Win32API/extconf.rb
+++ b/ext/Win32API/extconf.rb
@@ -1,8 +1,5 @@
require 'mkmf'
if have_header("windows.h") and have_library("kernel32")
- if Config::CONFIG["CC"] =~ /gcc/
- $CFLAGS += " -fno-defer-pop -fno-omit-frame-pointer"
- end
create_makefile("Win32API")
end
diff --git a/ext/Win32API/lib/win32/registry.rb b/ext/Win32API/lib/win32/registry.rb
new file mode 100644
index 0000000000..2671551a33
--- /dev/null
+++ b/ext/Win32API/lib/win32/registry.rb
@@ -0,0 +1,831 @@
+=begin
+= Win32 Registry I/F
+win32/registry is registry accessor library for Win32 platform.
+It uses Win32API to call Win32 Registry APIs.
+
+== example
+ Win32::Registry::HKEY_CURRENT_USER.open('SOFTWARE\foo') do |reg|
+ value = reg['foo'] # read a value
+ value = reg['foo', Win32::Registry::REG_SZ] # read a value with type
+ type, value = reg.read('foo') # read a value
+ reg['foo'] = 'bar' # write a value
+ reg['foo', Win32::Registry::REG_SZ] = 'bar' # write a value with type
+ reg.write('foo', Win32::Registry::REG_SZ, 'bar') # write a value
+
+ reg.each_value { |name, type, data| ... } # Enumerate values
+ reg.each_key { |key, wtime| ... } # Enumerate subkeys
+
+ reg.delete_value(name) # Delete a value
+ reg.delete_key(name) # Delete a subkey
+ reg.delete_key(name, true) # Delete a subkey recursively
+ end
+
+= Reference
+
+== Win32::Registry class
+
+=== including modules
+
+* Enumerable
+* Registry::Constants
+
+=== class methods
+--- Registry.open(key, subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED)
+--- Registry.open(key, subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED) { |reg| ... }
+ Open the registry key ((|subkey|)) under ((|key|)).
+ ((|key|)) is Win32::Registry object of parent key.
+ You can use predefined key HKEY_* (see ((<constants>)))
+
+ ((|desired|)) and ((|opt|)) is access mask and key option.
+ For detail, see ((<MSDN Library|URL:http://msdn.microsoft.com/library/en-us/sysinfo/base/regopenkeyex.asp>)).
+
+ If block is given, the key is closed automatically.
+
+--- Registry.create(key, subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED)
+--- Registry.create(key, subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED) { |reg| ... }
+ Create or open the registry key ((|subkey|)) under ((|key|)).
+ You can use predefined key HKEY_* (see ((<constants>)))
+
+ If subkey is already exists, key is opened and Registry#((<created?>))
+ method will return false.
+
+ If block is given, the key is closed automatically.
+
+--- Registry.expand_environ(str)
+ Replace (({%\w+%})) into the environment value of ((|str|)).
+ This method is used for REG_EXPAND_SZ.
+
+ For detail, see ((<ExpandEnvironmentStrings|URL:http://msdn.microsoft.com/library/en-us/sysinfo/base/expandenvironmentstrings.asp>)) Win32 API.
+
+--- Registry.type2name(type)
+ Convert registry type value to readable string.
+
+--- Registry.wtime2time(wtime)
+ Convert 64-bit FILETIME integer into Time object.
+
+--- Registry.time2wtime(time)
+ Convert Time object or Integer object into 64-bit FILETIME.
+
+=== instance methods
+--- open(subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED)
+ Same as (({Win32::((<Registry.open>))(self, subkey, desired, opt)}))
+
+--- create(subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED)
+ Same as (({Win32::((<Registry.create>))(self, subkey, desired, opt)}))
+
+--- close
+ Close key.
+
+ After closed, most method raises error.
+
+--- read(name, *rtype)
+ Read a registry value named ((|name|)) and return array of
+ [ ((|type|)), ((|data|)) ].
+ When name is nil, the `default' value is read.
+
+ ((|type|)) is value type. (see ((<Win32::Registry::Constants module>)))
+ ((|data|)) is value data, its class is:
+ :REG_SZ, REG_EXPAND_SZ
+ String
+ :REG_MULTI_SZ
+ Array of String
+ :REG_DWORD, REG_DWORD_BIG_ENDIAN, REG_QWORD
+ Integer
+ :REG_BINARY
+ String (contains binary data)
+
+ When ((|rtype|)) is specified, the value type must be included by
+ ((|rtype|)) array, or TypeError is raised.
+
+--- self[name, *rtype]
+ Read a registry value named ((|name|)) and return its value data.
+ The class of value is same as ((<read>)) method returns.
+
+ If the value type is REG_EXPAND_SZ, returns value data whose environment
+ variables are replaced.
+ If the value type is neither REG_SZ, REG_MULTI_SZ, REG_DWORD,
+ REG_DWORD_BIG_ENDIAN, nor REG_QWORD, TypeError is raised.
+
+ The meaning of ((|rtype|)) is same as ((<read>)) method.
+
+--- read_s(name)
+--- read_i(name)
+--- read_bin(name)
+ Read a REG_SZ(read_s), REG_DWORD(read_i), or REG_BINARY(read_bin)
+ registry value named ((|name|)).
+
+ If the values type does not match, TypeError is raised.
+
+--- read_s_expand(name)
+ Read a REG_SZ or REG_EXPAND_SZ registry value named ((|name|)).
+
+ If the value type is REG_EXPAND_SZ, environment variables are replaced.
+ Unless the value type is REG_SZ or REG_EXPAND_SZ, TypeError is raised.
+
+--- write(name, type, data)
+ Write ((|data|)) to a registry value named ((|name|)).
+ When name is nil, write to the `default' value.
+
+ ((|type|)) is type value. (see ((<Registry::Constants module>)))
+ Class of ((|data|)) must be same as which ((<read>))
+ method returns.
+
+--- self[name, wtype = nil] = value
+ Write ((|value|)) to a registry value named ((|name|)).
+
+ If ((|wtype|)) is specified, the value type is it.
+ Otherwise, the value type is depend on class of ((|value|)):
+ :Integer
+ REG_DWORD
+ :String
+ REG_SZ
+ :Array
+ REG_MULTI_SZ
+
+--- write_s(name, value)
+--- write_i(name, value)
+--- write_bin(name, value)
+ Write ((|value|)) to a registry value named ((|name|)).
+
+ The value type is REG_SZ(write_s), REG_DWORD(write_i), or
+ REG_BINARY(write_bin).
+
+--- each { |name, type, value| ... }
+--- each_value { |name, type, value| ... }
+ Enumerate values.
+
+--- each_key { |subkey, wtime| ... }
+ Enumerate subkeys.
+
+ ((|subkey|)) is String which contains name of subkey.
+ ((|wtime|)) is last write time as FILETIME (64-bit integer).
+ (see ((<Registry.wtime2time>)))
+
+--- delete(name)
+--- delete_value(name)
+ Delete a registry value named ((|name|)).
+ We can not delete the `default' value.
+
+--- delete_key(name, recursive = false)
+ Delete a subkey named ((|name|)) and all its values.
+
+ If ((|recursive|)) is false, the subkey must not have subkeys.
+ Otherwise, this method deletes all subkeys and values recursively.
+
+--- flush
+ Write all the attributes into the registry file.
+
+--- created?
+ Returns if key is created ((*newly*)).
+ (see ((<Registry.create>)))
+
+--- open?
+ Returns if key is not closed.
+
+--- hkey
+ Returns key handle value.
+
+--- parent
+ Win32::Registry object of parent key, or nil if predefeined key.
+
+--- keyname
+ Same as ((|subkey|)) value of ((<Registry.open>)) or
+ ((<Registry.create>)) method.
+
+--- disposition
+ Disposition value (REG_CREATED_NEW_KEY or REG_OPENED_EXISTING_KEY).
+
+--- name
+--- to_s
+ Full path of key such as (({'HKEY_CURRENT_USER\SOFTWARE\foo\bar'})).
+
+--- info
+ Returns key information as Array of:
+ :num_keys
+ The number of subkeys.
+ :max_key_length
+ Maximum length of name of subkeys.
+ :num_values
+ The number of values.
+ :max_value_name_length
+ Maximum length of name of values.
+ :max_value_length
+ Maximum length of value of values.
+ :descriptor_length
+ Length of security descriptor.
+ :wtime
+ Last write time as FILETIME(64-bit integer)
+
+ For detail, see ((<RegQueryInfoKey|URL:http://msdn.microsoft.com/library/en-us/sysinfo/base/regqueryinfokey.asp>)) Win32 API.
+
+--- num_keys
+--- max_key_length
+--- num_values
+--- max_value_name_length
+--- max_value_length
+--- descriptor_length
+--- wtime
+ Returns an item of key information.
+
+=== constants
+--- HKEY_CLASSES_ROOT
+--- HKEY_CURRENT_USER
+--- HKEY_LOCAL_MACHINE
+--- HKEY_PERFORMANCE_DATA
+--- HKEY_CURRENT_CONFIG
+--- HKEY_DYN_DATA
+ Win32::Registry object whose key is predefined key.
+ For detail, see ((<MSDN Library|URL:http://msdn.microsoft.com/library/en-us/sysinfo/base/predefined_keys.asp>)).
+
+== Win32::Registry::Constants module
+
+For detail, see ((<MSDN Library|URL:http://msdn.microsoft.com/library/en-us/sysinfo/base/registry.asp>)).
+
+--- HKEY_*
+ Predefined key ((*handle*)).
+ These are Integer, not Win32::Registry.
+
+--- REG_*
+ Registry value type.
+
+--- KEY_*
+ Security access mask.
+
+--- KEY_OPTIONS_*
+ Key options.
+
+--- REG_CREATED_NEW_KEY
+--- REG_OPENED_EXISTING_KEY
+ If the key is created newly or opened existing key.
+ See also Registry#((<disposition>)) method.
+
+=end
+
+require 'Win32API'
+
+module Win32
+ class Registry
+ module Constants
+ HKEY_CLASSES_ROOT = 0x80000000
+ HKEY_CURRENT_USER = 0x80000001
+ HKEY_LOCAL_MACHINE = 0x80000002
+ HKEY_USERS = 0x80000003
+ HKEY_PERFORMANCE_DATA = 0x80000004
+ HKEY_PERFORMANCE_TEXT = 0x80000050
+ HKEY_PERFORMANCE_NLSTEXT = 0x80000060
+ HKEY_CURRENT_CONFIG = 0x80000005
+ HKEY_DYN_DATA = 0x80000006
+
+ REG_NONE = 0
+ REG_SZ = 1
+ REG_EXPAND_SZ = 2
+ REG_BINARY = 3
+ REG_DWORD = 4
+ REG_DWORD_LITTLE_ENDIAN = 4
+ REG_DWORD_BIG_ENDIAN = 5
+ REG_LINK = 6
+ REG_MULTI_SZ = 7
+ REG_RESOURCE_LIST = 8
+ REG_FULL_RESOURCE_DESCRIPTOR = 9
+ REG_RESOURCE_REQUIREMENTS_LIST = 10
+ REG_QWORD = 11
+ REG_QWORD_LITTLE_ENDIAN = 11
+
+ STANDARD_RIGHTS_READ = 0x00020000
+ STANDARD_RIGHTS_WRITE = 0x00020000
+ KEY_QUERY_VALUE = 0x0001
+ KEY_SET_VALUE = 0x0002
+ KEY_CREATE_SUB_KEY = 0x0004
+ KEY_ENUMERATE_SUB_KEYS = 0x0008
+ KEY_NOTIFY = 0x0010
+ KEY_CREATE_LINK = 0x0020
+ KEY_READ = STANDARD_RIGHTS_READ |
+ KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY
+ KEY_WRITE = STANDARD_RIGHTS_WRITE |
+ KEY_SET_VALUE | KEY_CREATE_SUB_KEY
+ KEY_EXECUTE = KEY_READ
+ KEY_ALL_ACCESS = KEY_READ | KEY_WRITE | KEY_CREATE_LINK
+
+ REG_OPTION_RESERVED = 0x0000
+ REG_OPTION_NON_VOLATILE = 0x0000
+ REG_OPTION_VOLATILE = 0x0001
+ REG_OPTION_CREATE_LINK = 0x0002
+ REG_OPTION_BACKUP_RESTORE = 0x0004
+ REG_OPTION_OPEN_LINK = 0x0008
+ REG_LEGAL_OPTION = REG_OPTION_RESERVED |
+ REG_OPTION_NON_VOLATILE | REG_OPTION_CREATE_LINK |
+ REG_OPTION_BACKUP_RESTORE | REG_OPTION_OPEN_LINK
+
+ REG_CREATED_NEW_KEY = 1
+ REG_OPENED_EXISTING_KEY = 2
+
+ REG_WHOLE_HIVE_VOLATILE = 0x0001
+ REG_REFRESH_HIVE = 0x0002
+ REG_NO_LAZY_FLUSH = 0x0004
+ REG_FORCE_RESTORE = 0x0008
+
+ MAX_KEY_LENGTH = 514
+ MAX_VALUE_LENGTH = 32768
+ end
+ include Constants
+ include Enumerable
+
+ #
+ # Error
+ #
+ class Error < ::StandardError
+ FormatMessageA = Win32API.new('kernel32.dll', 'FormatMessageA', 'LPLLPLP', 'L')
+ def initialize(code)
+ @code = code
+ msg = "\0" * 1024
+ len = FormatMessageA.call(0x1200, 0, code, 0, msg, 1024, 0)
+ super msg[0, len].tr("\r", '').chomp
+ end
+ attr_reader :code
+ end
+
+ #
+ # Predefined Keys
+ #
+ class PredefinedKey < Registry
+ def initialize(hkey, keyname)
+ @hkey = hkey
+ @parent = nil
+ @keyname = keyname
+ @disposition = REG_OPENED_EXISTING_KEY
+ end
+
+ # Predefined keys cannot be closed
+ def close
+ raise Error.new(5) ## ERROR_ACCESS_DENIED
+ end
+
+ # Fake class for Registry#open, Registry#create
+ def class
+ Registry
+ end
+
+ # Make all
+ Constants.constants.grep(/^HKEY_/) do |c|
+ Registry.const_set c, new(Constants.const_get(c), c)
+ end
+ end
+
+ #
+ # Win32 APIs
+ #
+ module API
+ [
+ %w/RegOpenKeyExA LPLLP L/,
+ %w/RegCreateKeyExA LPLLLLPPP L/,
+ %w/RegEnumValueA LLPPPPPP L/,
+ %w/RegEnumKeyExA LLPPLLLP L/,
+ %w/RegQueryValueExA LPLPPP L/,
+ %w/RegSetValueExA LPLLPL L/,
+ %w/RegDeleteValue LP L/,
+ %w/RegDeleteKey LP L/,
+ %w/RegFlushKey L L/,
+ %w/RegCloseKey L L/,
+ %w/RegQueryInfoKey LPPPPPPPPPPP L/,
+ ].each do |fn|
+ const_set fn[0].intern, Win32API.new('advapi32.dll', *fn)
+ end
+
+ module_function
+
+ def check(result)
+ raise Error, result, caller(2) if result != 0
+ end
+
+ def packdw(dw)
+ [dw].pack('V')
+ end
+
+ def unpackdw(dw)
+ dw += [0].pack('V')
+ dw.unpack('V')[0]
+ end
+
+ def packqw(qw)
+ [ qw & 0xFFFFFFFF, qw >> 32 ].pack('VV')
+ end
+
+ def unpackqw(qw)
+ qw = qw.unpack('VV')
+ (qw[1] << 32) | qw[0]
+ end
+
+ def OpenKey(hkey, name, opt, desired)
+ result = packdw(0)
+ check RegOpenKeyExA.call(hkey, name, opt, desired, result)
+ unpackdw(result)
+ end
+
+ def CreateKey(hkey, name, opt, desired)
+ result = packdw(0)
+ disp = packdw(0)
+ check RegCreateKeyExA.call(hkey, name, 0, 0, opt, desired,
+ 0, result, disp)
+ [ unpackdw(result), unpackdw(disp) ]
+ end
+
+ def EnumValue(hkey, index)
+ name = ' ' * Constants::MAX_KEY_LENGTH
+ size = packdw(Constants::MAX_KEY_LENGTH)
+ check RegEnumValueA.call(hkey, index, name, size, 0, 0, 0, 0)
+ name[0, unpackdw(size)]
+ end
+
+ def EnumKey(hkey, index)
+ name = ' ' * Constants::MAX_KEY_LENGTH
+ size = packdw(Constants::MAX_KEY_LENGTH)
+ wtime = ' ' * 8
+ check RegEnumKeyExA.call(hkey, index, name, size, 0, 0, 0, wtime)
+ [ name[0, unpackdw(size)], unpackqw(wtime) ]
+ end
+
+ def QueryValue(hkey, name)
+ type = packdw(0)
+ size = packdw(0)
+ check RegQueryValueExA.call(hkey, name, 0, type, 0, size)
+ data = ' ' * unpackdw(size)
+ check RegQueryValueExA.call(hkey, name, 0, type, data, size)
+ [ unpackdw(type), data[0, unpackdw(size)] ]
+ end
+
+ def SetValue(hkey, name, type, data, size)
+ check RegSetValueExA.call(hkey, name, 0, type, data, size)
+ end
+
+ def DeleteValue(hkey, name)
+ check RegDeleteValue.call(hkey, name)
+ end
+
+ def DeleteKey(hkey, name)
+ check RegDeleteKey.call(hkey, name)
+ end
+
+ def FlushKey(hkey)
+ check RegFlushKey.call(hkey)
+ end
+
+ def CloseKey(hkey)
+ check RegCloseKey.call(hkey)
+ end
+
+ def QueryInfoKey(hkey)
+ subkeys = packdw(0)
+ maxsubkeylen = packdw(0)
+ values = packdw(0)
+ maxvaluenamelen = packdw(0)
+ maxvaluelen = packdw(0)
+ secdescs = packdw(0)
+ wtime = ' ' * 8
+ check RegQueryInfoKey.call(hkey, 0, 0, 0, subkeys, maxsubkeylen, 0,
+ values, maxvaluenamelen, maxvaluelen, secdescs, wtime)
+ [ unpackdw(subkeys), unpackdw(maxsubkeylen), unpackdw(values),
+ unpackdw(maxvaluenamelen), unpackdw(maxvaluelen),
+ unpackdw(secdescs), unpackqw(wtime) ]
+ end
+ end
+
+ #
+ # utility functions
+ #
+ def self.expand_environ(str)
+ str.gsub(/%([^%]+)%/) { ENV[$1] || $& }
+ end
+
+ @@type2name = { }
+ %w[
+ REG_NONE REG_SZ REG_EXPAND_SZ REG_BINARY REG_DWORD
+ REG_DWORD_BIG_ENDIAN REG_LINK REG_MULTI_SZ
+ REG_RESOURCE_LIST REG_FULL_RESOURCE_DESCRIPTOR
+ REG_RESOURCE_REQUIREMENTS_LIST REG_QWORD
+ ].each do |type|
+ @@type2name[Constants.const_get(type)] = type
+ end
+
+ def self.type2name(type)
+ @@type2name[type] || type.to_s
+ end
+
+ def self.wtime2time(wtime)
+ Time.at((wtime - 116444736000000000) / 10000000)
+ end
+
+ def self.time2wtime(time)
+ time.to_i * 10000000 + 116444736000000000
+ end
+
+ #
+ # constructors
+ #
+ private_class_method :new
+
+ def self.open(hkey, subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED)
+ subkey = subkey.chomp('\\')
+ newkey = API.OpenKey(hkey.hkey, subkey, opt, desired)
+ obj = new(newkey, hkey, subkey, REG_OPENED_EXISTING_KEY)
+ if block_given?
+ begin
+ yield obj
+ ensure
+ obj.close
+ end
+ else
+ obj
+ end
+ end
+
+ def self.create(hkey, subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED)
+ newkey, disp = API.CreateKey(hkey.hkey, subkey, opt, desired)
+ obj = new(newkey, hkey, subkey, disp)
+ if block_given?
+ begin
+ yield obj
+ ensure
+ obj.close
+ end
+ else
+ obj
+ end
+ end
+
+ #
+ # finalizer
+ #
+ @@final = proc { |hkey| proc { API.CloseKey(hkey[0]) if hkey[0] } }
+
+ #
+ # initialize
+ #
+ def initialize(hkey, parent, keyname, disposition)
+ @hkey = hkey
+ @parent = parent
+ @keyname = keyname
+ @disposition = disposition
+ @hkeyfinal = [ hkey ]
+ ObjectSpace.define_finalizer self, @@final.call(@hkeyfinal)
+ end
+ attr_reader :hkey, :parent, :keyname, :disposition
+
+ #
+ # attributes
+ #
+ def created?
+ @disposition == REG_CREATED_NEW_KEY
+ end
+
+ def open?
+ !@hkey.nil?
+ end
+
+ def name
+ parent = self
+ name = @keyname
+ while parent = parent.parent
+ name = parent.keyname + '\\' + name
+ end
+ name
+ end
+
+ def inspect
+ "\#<Win32::Registry key=#{name.inspect}>"
+ end
+
+ #
+ # marshalling
+ #
+ def _dump(depth)
+ raise TypeError, "can't dump Win32::Registry"
+ end
+
+ #
+ # open/close
+ #
+ def open(subkey, desired = KEY_READ, opt = REG_OPTION_RESERVED, &blk)
+ self.class.open(self, subkey, desired, opt, &blk)
+ end
+
+ def create(subkey, desired = KEY_ALL_ACCESS, opt = REG_OPTION_RESERVED, &blk)
+ self.class.create(self, subkey, desired, opt, &blk)
+ end
+
+ def close
+ API.CloseKey(@hkey)
+ @hkey = @parent = @keyname = nil
+ @hkeyfinal[0] = nil
+ end
+
+ #
+ # iterator
+ #
+ def each_value
+ index = 0
+ while true
+ begin
+ subkey = API.EnumValue(@hkey, index)
+ rescue Error
+ break
+ end
+ begin
+ type, data = read(subkey)
+ rescue Error
+ next
+ end
+ yield subkey, type, data
+ index += 1
+ end
+ index
+ end
+ alias each each_value
+
+ def each_key
+ index = 0
+ while true
+ begin
+ subkey, wtime = API.EnumKey(@hkey, index)
+ rescue Error
+ break
+ end
+ yield subkey, wtime
+ index += 1
+ end
+ index
+ end
+
+ def keys
+ keys_ary = []
+ each_key { |key,| keys_ary << key }
+ keys_ary
+ end
+
+ #
+ # reader
+ #
+ def read(name, *rtype)
+ type, data = API.QueryValue(@hkey, name)
+ unless rtype.empty? or rtype.include?(type)
+ raise TypeError, "Type mismatch (expect #{rtype.inspect} but #{type} present)"
+ end
+ case type
+ when REG_SZ, REG_EXPAND_SZ
+ [ type, data.chop ]
+ when REG_MULTI_SZ
+ [ type, data.split(/\0/) ]
+ when REG_BINARY
+ [ type, data ]
+ when REG_DWORD
+ [ type, API.unpackdw(data) ]
+ when REG_DWORD_BIG_ENDIAN
+ [ type, data.unpack('N')[0] ]
+ when REG_QWORD
+ [ type, API.unpackqw(data) ]
+ else
+ raise TypeError, "Type #{type} is not supported."
+ end
+ end
+
+ def [](name, *rtype)
+ type, data = read(name, *rtype)
+ case type
+ when REG_SZ, REG_DWORD, REG_QWORD, REG_MULTI_SZ
+ data
+ when REG_EXPAND_SZ
+ Registry.expand_environ(data)
+ else
+ raise TypeError, "Type #{type} is not supported."
+ end
+ end
+
+ def read_s(name)
+ read(name, REG_SZ)[1]
+ end
+
+ def read_s_expand(name)
+ type, data = read(name, REG_SZ, REG_EXPAND_SZ)
+ if type == REG_EXPAND_SZ
+ Registry.expand_environ(data)
+ else
+ data
+ end
+ end
+
+ def read_i(name)
+ read(name, REG_DWORD, REG_DWORD_BIG_ENDIAN, REG_QWORD)[1]
+ end
+
+ def read_bin(name)
+ read(name, REG_BINARY)[1]
+ end
+
+ #
+ # writer
+ #
+ def write(name, type, data)
+ case type
+ when REG_SZ, REG_EXPAND_SZ
+ data = data.to_s + "\0"
+ when REG_MULTI_SZ
+ data = data.to_a.join("\0") + "\0\0"
+ when REG_BINARY
+ data = data.to_s
+ when REG_DWORD
+ data = API.packdw(data.to_i)
+ when REG_DWORD_BIG_ENDIAN
+ data = [data.to_i].pack('N')
+ when REG_QWORD
+ data = API.packqw(data.to_i)
+ else
+ raise TypeError, "Unsupported type #{type}"
+ end
+ API.SetValue(@hkey, name, type, data, data.length)
+ end
+
+ def []=(name, rtype, value = nil)
+ if value
+ write name, rtype, value
+ else
+ case value = rtype
+ when Integer
+ write name, REG_DWORD, value
+ when String
+ write name, REG_SZ, value
+ when Array
+ write name, REG_MULTI_SZ, value
+ else
+ raise TypeError, "Unexpected type #{value.class}"
+ end
+ end
+ value
+ end
+
+ def write_s(name, value)
+ write name, REG_SZ, value.to_s
+ end
+
+ def write_i(name, value)
+ write name, REG_DWORD, value.to_i
+ end
+
+ def write_bin(name, value)
+ write name, REG_BINARY, value.to_s
+ end
+
+ #
+ # delete
+ #
+ def delete_value(name)
+ API.DeleteValue(@hkey, name)
+ end
+ alias delete delete_value
+
+ def delete_key(name, recursive = false)
+ if recursive
+ open(name, KEY_ALL_ACCESS) do |reg|
+ reg.keys.each do |key|
+ begin
+ reg.delete_key(key, true)
+ rescue Error
+ #
+ end
+ end
+ end
+ API.DeleteKey(@hkey, name)
+ else
+ begin
+ API.EnumKey @hkey, 0
+ rescue Error
+ return API.DeleteKey(@hkey, name)
+ end
+ raise Error.new(5) ## ERROR_ACCESS_DENIED
+ end
+ end
+
+ #
+ # flush
+ #
+ def flush
+ API.FlushKey @hkey
+ end
+
+ #
+ # key information
+ #
+ def info
+ API.QueryInfoKey(@hkey)
+ end
+ %w[
+ num_keys max_key_length
+ num_values max_value_name_length max_value_length
+ descriptor_length wtime
+ ].each_with_index do |s, i|
+ eval <<-__END__
+ def #{s}
+ info[#{i}]
+ end
+ __END__
+ end
+ end
+end
diff --git a/ext/Win32API/lib/win32/resolv.rb b/ext/Win32API/lib/win32/resolv.rb
new file mode 100644
index 0000000000..5a99d04d55
--- /dev/null
+++ b/ext/Win32API/lib/win32/resolv.rb
@@ -0,0 +1,366 @@
+=begin
+= Win32 DNS and DHCP I/F
+
+=end
+
+require 'win32/registry'
+
+module Win32
+ module Resolv
+ API = Registry::API
+
+ def self.get_hosts_path
+ path = get_hosts_dir
+ path = File.join(path.gsub(/\\/, File::SEPARATOR), 'hosts')
+ File.exist?(path) ? path : nil
+ end
+
+ def self.get_resolv_info
+ search, nameserver = get_info
+ if search.empty?
+ search = nil
+ else
+ search.delete("")
+ search.uniq!
+ end
+ if nameserver.empty?
+ nameserver = nil
+ else
+ nameserver.delete("")
+ nameserver.delete("0.0.0.0")
+ nameserver.uniq!
+ end
+ [ search, nameserver ]
+ end
+
+getv = Win32API.new('kernel32.dll', 'GetVersionExA', 'P', 'L')
+info = [ 148, 0, 0, 0, 0 ].pack('V5') + "\0" * 128
+getv.call(info)
+if info.unpack('V5')[4] == 2 # VER_PLATFORM_WIN32_NT
+#====================================================================
+# Windows NT
+#====================================================================
+ module_eval <<-'__EOS__', __FILE__, __LINE__+1
+ TCPIP_NT = 'SYSTEM\CurrentControlSet\Services\Tcpip\Parameters'
+
+ class << self
+ private
+ def get_hosts_dir
+ Registry::HKEY_LOCAL_MACHINE.open(TCPIP_NT) do |reg|
+ reg.read_s_expand('DataBasePath')
+ end
+ end
+
+ def get_info
+ search = nil
+ nameserver = []
+ Registry::HKEY_LOCAL_MACHINE.open(TCPIP_NT) do |reg|
+ begin
+ slist = reg.read_s('SearchList')
+ search = slist.split(/,\s*/) unless slist.empty?
+ rescue Registry::Error
+ end
+
+ if add_search = search.nil?
+ search = []
+ begin
+ nvdom = reg.read_s('NV Domain')
+ unless nvdom.empty?
+ @search = [ nvdom ]
+ if reg.read_i('UseDomainNameDevolution') != 0
+ if /^[\w\d]+\./ =~ nvdom
+ devo = $'
+ end
+ end
+ end
+ rescue Registry::Error
+ end
+ end
+
+ reg.open('Interfaces') do |reg|
+ reg.each_key do |iface,|
+ reg.open(iface) do |regif|
+ begin
+ [ 'NameServer', 'DhcpNameServer' ].each do |key|
+ ns = regif.read_s(key)
+ unless ns.empty?
+ nameserver.concat(ns.split(/\s+/))
+ break
+ end
+ end
+ rescue Registry::Error
+ end
+
+ if add_search
+ begin
+ [ 'Domain', 'DhcpDomain' ].each do |key|
+ dom = regif.read_s(key)
+ unless dom.empty?
+ search.concat(dom.split(/,\s*/))
+ break
+ end
+ end
+ rescue Registry::Error
+ end
+ end
+ end
+ end
+ end
+ search << devo if add_search and devo
+ end
+ [ search.uniq, nameserver.uniq ]
+ end
+ end
+ __EOS__
+else
+#====================================================================
+# Windows 9x
+#====================================================================
+ module_eval <<-'__EOS__', __FILE__, __LINE__+1
+ TCPIP_9X = 'SYSTEM\CurrentControlSet\Services\VxD\MSTCP'
+ DHCP_9X = 'SYSTEM\CurrentControlSet\Services\VxD\DHCP'
+ WINDOWS = 'Software\Microsoft\Windows\CurrentVersion'
+
+ class << self
+ # private
+
+ def get_hosts_dir
+ Registry::HKEY_LOCAL_MACHINE.open(WINDOWS) do |reg|
+ reg.read_s_expand('SystemRoot')
+ end
+ end
+
+ def get_info
+ search = []
+ nameserver = []
+ begin
+ Registry::HKEY_LOCAL_MACHINE.open(TCPIP_9X) do |reg|
+ if reg.read_s("EnableDNS") == "1"
+ domain = reg.read_s("Domain")
+ ns = reg.read_s("NameServer")
+ slist = reg.read_s("SearchList")
+ search << domain unless domain.empty?
+ search.concat(slist.split(/,\s*/))
+ nameserver.concat(ns.split(/,\s*/))
+ end
+ end
+ rescue Registry::Error
+ end
+
+ dhcpinfo = get_dhcpinfo
+ search.concat(dhcpinfo[0])
+ nameserver.concat(dhcpinfo[1])
+ [ search, nameserver ]
+ end
+
+ def get_dhcpinfo
+ macaddrs = {}
+ ipaddrs = {}
+ WsControl.get_iflist.each do |index, macaddr, *ipaddr|
+ macaddrs[macaddr] = 1
+ ipaddr.each { |ipaddr| ipaddrs[ipaddr] = 1 }
+ end
+ iflist = [ macaddrs, ipaddrs ]
+
+ search = []
+ nameserver = []
+ version = -1
+ Registry::HKEY_LOCAL_MACHINE.open(DHCP_9X) do |reg|
+ begin
+ version = API.unpackdw(reg.read_bin("Version"))
+ rescue Registry::Error
+ end
+
+ reg.each_key do |key,|
+ catch(:not_used) do
+ reg.open(key) do |regdi|
+ dom, ns = get_dhcpinfo_key(version, regdi, iflist)
+ search << dom if dom
+ nameserver.concat(ns) if ns
+ end
+ end
+ end
+ end
+ [ search, nameserver ]
+ end
+
+ def get_dhcpinfo_95(reg)
+ dhcp = reg.read_bin("DhcpInfo")
+ [
+ API.unpackdw(dhcp[4..7]),
+ API.unpackdw(dhcp[8..11]),
+ 1,
+ dhcp[45..50],
+ reg.read_bin("OptionInfo"),
+ ]
+ end
+
+ def get_dhcpinfo_98(reg)
+ [
+ API.unpackdw(reg.read_bin("DhcpIPAddress")),
+ API.unpackdw(reg.read_bin("DhcpSubnetMask")),
+ API.unpackdw(reg.read_bin("HardwareType")),
+ reg.read_bin("HardwareAddress"),
+ reg.read_bin("OptionInfo"),
+ ]
+ end
+
+ def get_dhcpinfo_key(version, reg, iflist)
+ info = case version
+ when 1
+ get_dhcpinfo_95(reg)
+ when 2
+ get_dhcpinfo_98(reg)
+ else
+ begin
+ get_dhcpinfo_98(reg)
+ rescue Registry::Error
+ get_dhcpinfo_95(reg)
+ end
+ end
+ ipaddr, netmask, hwtype, macaddr, opt = info
+ throw :not_used unless
+ ipaddr and ipaddr != 0 and
+ netmask and netmask != 0 and
+ macaddr and macaddr.size == 6 and
+ hwtype == 1 and
+ iflist[0][macaddr] and iflist[1][ipaddr]
+
+ size = opt.size
+ idx = 0
+ while idx <= size
+ opttype = opt[idx]
+ optsize = opt[idx + 1]
+ optval = opt[idx + 2, optsize]
+ case opttype
+ when 0xFF ## term
+ break
+ when 0x0F ## domain
+ domain = optval.chomp("\0")
+ when 0x06 ## dns
+ nameserver = optval.scan(/..../).collect { |addr|
+ "%d.%d.%d.%d" % addr.unpack('C4')
+ }
+ end
+ idx += optsize + 2
+ end
+ [ domain, nameserver ]
+ rescue Registry::Error
+ throw :not_used
+ end
+ end
+
+ module WsControl
+ WsControl = Win32API.new('wsock32.dll', 'WsControl', 'LLPPPP', 'L')
+ WSAGetLastError = Win32API.new('wsock32.dll', 'WSAGetLastError', 'V', 'L')
+
+ MAX_TDI_ENTITIES = 512
+ IPPROTO_TCP = 6
+ WSCTL_TCP_QUERY_INFORMATION = 0
+ INFO_CLASS_GENERIC = 0x100
+ INFO_CLASS_PROTOCOL = 0x200
+ INFO_TYPE_PROVIDER = 0x100
+ ENTITY_LIST_ID = 0
+ GENERIC_ENTITY = 0
+ CL_NL_ENTITY = 0x301
+ IF_ENTITY = 0x200
+ ENTITY_TYPE_ID = 1
+ CL_NL_IP = 0x303
+ IF_MIB = 0x202
+ IF_MIB_STATS_ID = 1
+ IP_MIB_ADDRTABLE_ENTRY_ID = 0x102
+
+ def self.wsctl(tei_entity, tei_instance,
+ toi_class, toi_type, toi_id,
+ buffsize)
+ reqinfo = [
+ ## TDIEntityID
+ tei_entity, tei_instance,
+ ## TDIObjectID
+ toi_class, toi_type, toi_id,
+ ## TCP_REQUEST_INFORMATION_EX
+ ""
+ ].pack('VVVVVa16')
+ reqsize = API.packdw(reqinfo.size)
+ buff = "\0" * buffsize
+ buffsize = API.packdw(buffsize)
+ result = WsControl.call(
+ IPPROTO_TCP,
+ WSCTL_TCP_QUERY_INFORMATION,
+ reqinfo, reqsize,
+ buff, buffsize)
+ if result != 0
+ raise RuntimeError, "WsControl failed.(#{result})"
+ end
+ [ buff, API.unpackdw(buffsize) ]
+ end
+ private_class_method :wsctl
+
+ def self.get_iflist
+ # Get TDI Entity List
+ entities, size =
+ wsctl(GENERIC_ENTITY, 0,
+ INFO_CLASS_GENERIC,
+ INFO_TYPE_PROVIDER,
+ ENTITY_LIST_ID,
+ MAX_TDI_ENTITIES * 8) # sizeof(TDIEntityID)
+ entities = entities[0, size].
+ scan(/.{8}/).
+ collect { |e| e.unpack('VV') }
+
+ # Get MIB Interface List
+ iflist = []
+ ifcount = 0
+ entities.each do |entity, instance|
+ if( (entity & IF_ENTITY)>0 )
+ ifcount += 1
+ etype, = wsctl(entity, instance,
+ INFO_CLASS_GENERIC,
+ INFO_TYPE_PROVIDER,
+ ENTITY_TYPE_ID,
+ 4)
+ if( (API.unpackdw(etype) & IF_MIB)==IF_MIB )
+ ifentry, = wsctl(entity, instance,
+ INFO_CLASS_PROTOCOL,
+ INFO_TYPE_PROVIDER,
+ IF_MIB_STATS_ID,
+ 21 * 4 + 8 + 130) # sizeof(IFEntry)
+ iflist << [
+ API.unpackdw(ifentry[0,4]),
+ ifentry[20, 6]
+ ]
+ end
+ end
+ end
+
+ # Get IP Addresses
+ entities.each do |entity, instance|
+ if entity == CL_NL_ENTITY
+ etype, = wsctl(entity, instance,
+ INFO_CLASS_GENERIC,
+ INFO_TYPE_PROVIDER,
+ ENTITY_TYPE_ID,
+ 4)
+ if API.unpackdw(etype) == CL_NL_IP
+ ipentries, = wsctl(entity, instance,
+ INFO_CLASS_PROTOCOL,
+ INFO_TYPE_PROVIDER,
+ IP_MIB_ADDRTABLE_ENTRY_ID,
+ 24 * (ifcount+1)) # sizeof(IPAddrEntry)
+ ipentries.scan(/.{24}/) do |ipentry|
+ ipaddr, index = ipentry.unpack('VV')
+ if ifitem = iflist.assoc(index)
+ ifitem << ipaddr
+ end
+ end
+ end
+ end
+ end
+ iflist
+ end
+ end
+ __EOS__
+end
+#====================================================================
+ end
+end
diff --git a/ext/aix_mksym.rb b/ext/aix_mksym.rb
deleted file mode 100644
index 7e1af283dc..0000000000
--- a/ext/aix_mksym.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-
-def uniq(data)
- last=nil
- data.delete_if do |name|
- if last == name
- TRUE
- else
- last = name
- FALSE
- end
- end
-end
-
-def extract(nm, out)
- data = nm.readlines.collect{|line|
- line = line.split
- case line[1]
- when "B", "D"
- line[0]
- else
- next
- end
- }.compact!.sort!
- uniq(data)
- exp = open(out, "w")
- exp.printf "#!\n"
- for line in data
- exp.printf "%s\n", line
- end
- exp.close
- nm.close
-end
-extract(open("|/usr/ccs/bin/nm -p ../libruby.a"), "../ruby.imp")
diff --git a/ext/bigdecimal/.cvsignore b/ext/bigdecimal/.cvsignore
new file mode 100644
index 0000000000..4088712231
--- /dev/null
+++ b/ext/bigdecimal/.cvsignore
@@ -0,0 +1,3 @@
+Makefile
+mkmf.log
+*.def
diff --git a/ext/bigdecimal/MANIFEST b/ext/bigdecimal/MANIFEST
new file mode 100644
index 0000000000..88fc3c2efb
--- /dev/null
+++ b/ext/bigdecimal/MANIFEST
@@ -0,0 +1,17 @@
+MANIFEST
+README
+bigdecimal.def
+bigdecimal.c
+bigdecimal.h
+depend
+extconf.rb
+bigdecimal_en.html
+bigdecimal_ja.html
+lib/bigdecimal/jacobian.rb
+lib/bigdecimal/newton.rb
+lib/bigdecimal/ludcmp.rb
+lib/bigdecimal/util.rb
+lib/bigdecimal/nlsolve.rb
+sample/linear.rb
+sample/nlsolve.rb
+sample/pi.rb
diff --git a/ext/bigdecimal/README b/ext/bigdecimal/README
new file mode 100644
index 0000000000..a233f47f64
--- /dev/null
+++ b/ext/bigdecimal/README
@@ -0,0 +1,60 @@
+
+ Ruby BIGDECIMAL(Variable Precision) extension library.
+ Copyright (C) 1999 by Shigeo Kobayashi(shigeo@tinyforest.gr.jp)
+
+BigDecimal is copyrighted free software by Shigeo Kobayashi <shigeo@tinyforest.gr.jp>.
+You can redistribute it and/or modify it under either the terms of the GPL
+(see COPYING file), or the conditions below:
+
+ 1. You may make and give away verbatim copies of the source form of the
+ software without restriction, provided that you duplicate all of the
+ original copyright notices and associated disclaimers.
+
+ 2. You may modify your copy of the software in any way, provided that
+ you do at least ONE of the following:
+
+ a) place your modifications in the Public Domain or otherwise
+ make them Freely Available, such as by posting said
+ modifications to Usenet or an equivalent medium, or by allowing
+ the author to include your modifications in the software.
+
+ b) use the modified software only within your corporation or
+ organization.
+
+ c) rename any non-standard executables so the names do not conflict
+ with standard executables, which must also be provided.
+
+ d) make other distribution arrangements with the author.
+
+ 3. You may distribute the software in object code or executable
+ form, provided that you do at least ONE of the following:
+
+ a) distribute the executables and library files of the software,
+ together with instructions (in the manual page or equivalent)
+ on where to get the original distribution.
+
+ b) accompany the distribution with the machine-readable source of
+ the software.
+
+ c) give non-standard executables non-standard names, with
+ instructions on where to get the original software distribution.
+
+ d) make other distribution arrangements with the author.
+
+ 4. You may modify and include the part of the software into any other
+ software (possibly commercial).
+
+ 5. 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.
+
+* The Author
+
+Feel free to send comments and bug reports to the author. Here is the
+author's latest mail address:
+
+ shigeo@tinyforest.gr.jp
+
+-------------------------------------------------------
+created at: Thu Dec 22 1999
diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c
new file mode 100644
index 0000000000..2acc0e67c4
--- /dev/null
+++ b/ext/bigdecimal/bigdecimal.c
@@ -0,0 +1,4313 @@
+/*
+ *
+ * Ruby BigDecimal(Variable decimal precision) extension library.
+ *
+ * Copyright(C) 2002 by Shigeo Kobayashi(shigeo@tinyforest.gr.jp)
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file
+ * of this BigDecimal distribution.
+ *
+ * NOTES:
+ * For the notes other than listed bellow,see ruby CVS log.
+ * 2003-04-17
+ * Bug in negative.exp(n) reported by Hitoshi Miyazaki fixed.
+ * 2003-03-28
+ * V1.0 checked in to CVS(ruby/ext/bigdecimal).
+ * use rb_str2cstr() instead of STR2CSTR().
+ * 2003-01-03
+ * assign instead of asign(by knu),use string.h functions(by t.saito).
+ * 2002-12-06
+ * The sqrt() bug reported by Bret Jolly fixed.
+ * 2002-5-6
+ * The bug reported by Sako Hiroshi (ruby-list:34988) in to_i fixed.
+ * 2002-4-17
+ * methods prec and double_fig(class method) added(S.K).
+ * 2002-04-04
+ * Copied from BigFloat 1.1.9 and
+ * hash method changed according to the suggestion from Akinori MUSHA <knu@iDaemons.org>.
+ * All ! class methods deactivated(but not actually removed).
+ * to_s and to_s2 merged to one to_s[(n)].
+ *
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <float.h>
+#include <math.h>
+#include "ruby.h"
+#include "math.h"
+#include "version.h"
+
+/* #define ENABLE_NUMERIC_STRING */
+/* #define ENABLE_TRIAL_METHOD */
+
+VALUE rb_cBigDecimal;
+
+#include "bigdecimal.h"
+
+/* MACRO's to guard objects from GC by keeping them in stack */
+#define ENTER(n) volatile VALUE vStack[n];int iStack=0
+#define PUSH(x) vStack[iStack++] = (unsigned long)(x);
+#define SAVE(p) PUSH(p->obj);
+#define GUARD_OBJ(p,y) {p=y;SAVE(p);}
+
+/*
+ * ================== Ruby Interface part ==========================
+ */
+static ID coerce;
+
+/*
+ * **** BigDecimal version ****
+ */
+static VALUE
+BigDecimal_version(VALUE self)
+{
+ return rb_str_new2("1.0.0");
+}
+
+/*
+ * VP routines used in BigDecimal part
+ */
+static unsigned short VpGetException(void);
+static void VpSetException(unsigned short f);
+static int VpInternalRound(Real *c,int ixDigit,U_LONG vPrev,U_LONG v);
+
+/*
+ * **** BigDecimal part ****
+ */
+/* Following functions borrowed from numeric.c */
+static VALUE
+coerce_body(VALUE *x)
+{
+ return rb_funcall(x[1], coerce, 1, x[0]);
+}
+
+static VALUE
+coerce_rescue(VALUE *x)
+{
+ rb_raise(rb_eTypeError, "%s can't be coerced into %s",
+ rb_special_const_p(x[1])?
+ rb_str2cstr(rb_inspect(x[1]),0):
+ rb_class2name(CLASS_OF(x[1])),
+ rb_class2name(CLASS_OF(x[0])));
+ return (VALUE)0;
+}
+
+static void
+do_coerce(VALUE *x, VALUE *y)
+{
+ VALUE ary;
+ VALUE a[2];
+ a[0] = *x; a[1] = *y;
+ ary = rb_rescue(coerce_body, (VALUE)a, coerce_rescue, (VALUE)a);
+ if (TYPE(ary) != T_ARRAY || RARRAY(ary)->len != 2) {
+ rb_raise(rb_eTypeError, "coerce must return [x, y]");
+ }
+ *x = RARRAY(ary)->ptr[0];
+ *y = RARRAY(ary)->ptr[1];
+}
+
+static VALUE
+DoSomeOne(VALUE x, VALUE y)
+{
+ do_coerce(&x, &y);
+ return rb_funcall(x, rb_frame_last_func(), 1, y);
+}
+
+static void
+BigDecimal_delete(Real *pv)
+{
+ VpFree(pv);
+}
+
+static VALUE
+ToValue(Real *p)
+{
+ if(VpIsNaN(p)) {
+ VpException(VP_EXCEPTION_NaN,"Computation results to 'NaN'(Not a Number)",0);
+ } else if(VpIsPosInf(p)) {
+ VpException(VP_EXCEPTION_INFINITY,"Computation results to 'Infinity'",0);
+ } else if(VpIsNegInf(p)) {
+ VpException(VP_EXCEPTION_INFINITY,"Computation results to '-Infinity'",0);
+ }
+ return p->obj;
+}
+
+static Real *
+GetVpValue(VALUE v, int must)
+{
+ Real *pv;
+ VALUE bg;
+ char szD[128];
+
+ switch(TYPE(v))
+ {
+ case T_DATA:
+ if(RDATA(v)->dfree ==(void *) BigDecimal_delete) {
+ Data_Get_Struct(v, Real, pv);
+ return pv;
+ } else {
+ goto SomeOneMayDoIt;
+ }
+ break;
+ case T_FIXNUM:
+ sprintf(szD, "%d", FIX2INT(v));
+ return VpCreateRbObject(VpBaseFig() * 2 + 1, szD);
+
+#ifdef ENABLE_NUMERIC_STRING
+ case T_STRING:
+ SafeStringValue(v);
+ return VpCreateRbObject(strlen(RSTRING(v)->ptr) + VpBaseFig() + 1,
+ RSTRING(v)->ptr);
+#endif /* ENABLE_NUMERIC_STRING */
+
+ case T_BIGNUM:
+ bg = rb_big2str(v, 10);
+ return VpCreateRbObject(strlen(RSTRING(bg)->ptr) + VpBaseFig() + 1,
+ RSTRING(bg)->ptr);
+ default:
+ goto SomeOneMayDoIt;
+ }
+
+SomeOneMayDoIt:
+ if(must) {
+ rb_raise(rb_eTypeError, "%s can't be coerced into BigDecimal",
+ rb_special_const_p(v)?
+ rb_str2cstr(rb_inspect(v),0):
+ rb_class2name(CLASS_OF(v))
+ );
+ }
+ return NULL; /* NULL means to coerce */
+}
+
+static VALUE
+BigDecimal_double_fig(VALUE self)
+{
+ return INT2FIX(VpDblFig());
+}
+
+static VALUE
+BigDecimal_prec(VALUE self)
+{
+ ENTER(1);
+ Real *p;
+ VALUE obj;
+
+ GUARD_OBJ(p,GetVpValue(self,1));
+ obj = rb_ary_new();
+ obj = rb_ary_push(obj,INT2NUM(p->Prec*VpBaseFig()));
+ obj = rb_ary_push(obj,INT2NUM(p->MaxPrec*VpBaseFig()));
+ return obj;
+}
+
+static VALUE
+BigDecimal_hash(VALUE self)
+{
+ ENTER(1);
+ Real *p;
+ U_LONG hash,i;
+
+ GUARD_OBJ(p,GetVpValue(self,1));
+ hash = (U_LONG)p->sign;
+ /* hash!=2: the case for 0(1),NaN(0) or +-Infinity(3) is sign itself */
+ if(hash==2) {
+ for(i = 0; i < p->Prec;i++) {
+ hash = 31 * hash + p->frac[i];
+ hash ^= p->frac[i];
+ }
+ hash += p->exponent;
+ }
+ return INT2FIX(hash);
+}
+
+static VALUE
+BigDecimal_dump(int argc, VALUE *argv, VALUE self)
+{
+ ENTER(5);
+ char sz[50];
+ Real *vp;
+ char *psz;
+ VALUE dummy;
+ rb_scan_args(argc, argv, "01", &dummy);
+ GUARD_OBJ(vp,GetVpValue(self,1));
+ sprintf(sz,"%lu:",VpMaxPrec(vp)*VpBaseFig());
+ psz = ALLOCA_N(char,(unsigned int)VpNumOfChars(vp)+strlen(sz));
+ sprintf(psz,"%s",sz);
+ VpToString(vp, psz+strlen(psz), 0);
+ return rb_str_new2(psz);
+}
+
+static VALUE
+BigDecimal_load(VALUE self, VALUE str)
+{
+ ENTER(2);
+ Real *pv;
+ unsigned char *pch;
+ unsigned char ch;
+ unsigned long m=0;
+
+ SafeStringValue(str);
+ pch = RSTRING(str)->ptr;
+ /* First get max prec */
+ while((*pch)!=(unsigned char)'\0' && (ch=*pch++)!=(unsigned char)':') {
+ if(!ISDIGIT(ch)) {
+ rb_raise(rb_eTypeError, "Load failed: invalid character in the marshaled string");
+ }
+ m = m*10 + (unsigned long)(ch-'0');
+ }
+ if(m>VpBaseFig()) m -= VpBaseFig();
+ GUARD_OBJ(pv,VpNewRbClass(m,pch,self));
+ m /= VpBaseFig();
+ if(m && pv->MaxPrec>m) pv->MaxPrec = m+1;
+ return ToValue(pv);
+}
+
+static VALUE
+BigDecimal_mode(VALUE self, VALUE which, VALUE val)
+{
+ unsigned long f,fo;
+
+ if(TYPE(which)!=T_FIXNUM) return Qnil;
+ f = (unsigned long)FIX2INT(which);
+
+ if(f&VP_EXCEPTION_ALL) {
+ /* Exception mode setting */
+ fo = VpGetException();
+ if(val!=Qfalse && val!=Qtrue) return Qnil;
+ if(f&VP_EXCEPTION_INFINITY) {
+ VpSetException((unsigned short)((val==Qtrue)?(fo|VP_EXCEPTION_INFINITY):
+ (fo&(~VP_EXCEPTION_INFINITY))));
+ }
+ if(f&VP_EXCEPTION_NaN) {
+ VpSetException((unsigned short)((val==Qtrue)?(fo|VP_EXCEPTION_NaN):
+ (fo&(~VP_EXCEPTION_NaN))));
+ }
+ return INT2FIX(fo);
+ }
+ if(VP_ROUND_MODE==f) {
+ /* Rounding mode setting */
+ if(TYPE(val)!=T_FIXNUM) return Qnil;
+ fo = VpSetRoundMode((unsigned long)FIX2INT(val));
+ return INT2FIX(fo);
+ }
+ return Qnil;
+}
+
+static U_LONG
+GetAddSubPrec(Real *a, Real *b)
+{
+ U_LONG mxs;
+ U_LONG mx = a->Prec;
+ S_INT d;
+
+ if(!VpIsDef(a) || !VpIsDef(b)) return (-1L);
+ if(mx < b->Prec) mx = b->Prec;
+ if(a->exponent!=b->exponent) {
+ mxs = mx;
+ d = a->exponent - b->exponent;
+ if(d<0) d = -d;
+ mx = mx+(U_LONG)d;
+ if(mx<mxs) {
+ return VpException(VP_EXCEPTION_INFINITY,"Exponent overflow",0);
+ }
+ }
+ return mx;
+}
+
+static S_INT
+GetPositiveInt(VALUE v)
+{
+ S_INT n;
+ Check_Type(v, T_FIXNUM);
+ n = FIX2INT(v);
+ if(n <= 0) {
+ rb_raise(rb_eArgError, "argument must be positive");
+ }
+ return n;
+}
+
+VP_EXPORT Real *
+VpNewRbClass(U_LONG mx, char *str, VALUE klass)
+{
+ Real *pv = VpAlloc(mx,str);
+ pv->obj = (VALUE)Data_Wrap_Struct(klass, 0, BigDecimal_delete, pv);
+ return pv;
+}
+
+VP_EXPORT Real *
+VpCreateRbObject(U_LONG mx, char *str)
+{
+ Real *pv = VpAlloc(mx,str);
+ pv->obj = (VALUE)Data_Wrap_Struct(rb_cBigDecimal, 0, BigDecimal_delete, pv);
+ return pv;
+}
+
+
+static VALUE
+BigDecimal_IsNaN(VALUE self)
+{
+ Real *p = GetVpValue(self,1);
+ if(VpIsNaN(p)) return Qtrue;
+ return Qfalse;
+}
+
+static VALUE
+BigDecimal_IsInfinite(VALUE self)
+{
+ Real *p = GetVpValue(self,1);
+ if(VpIsPosInf(p)) return INT2FIX(1);
+ if(VpIsNegInf(p)) return INT2FIX(-1);
+ return Qnil;
+}
+
+static VALUE
+BigDecimal_IsFinite(VALUE self)
+{
+ Real *p = GetVpValue(self,1);
+ if(VpIsNaN(p)) return Qfalse;
+ if(VpIsInf(p)) return Qfalse;
+ return Qtrue;
+}
+
+static VALUE
+BigDecimal_to_i(VALUE self)
+{
+ ENTER(5);
+ int e,n,i,nf;
+ U_LONG v,b,j;
+ char *psz,*pch;
+ Real *p;
+
+ GUARD_OBJ(p,GetVpValue(self,1));
+
+ /* Infinity or NaN not converted. */
+ if(VpIsNaN(p)) {
+ VpException(VP_EXCEPTION_NaN,"Computation results to 'NaN'(Not a Number)",0);
+ return Qnil;
+ } else if(VpIsPosInf(p)) {
+ VpException(VP_EXCEPTION_INFINITY,"Computation results to 'Infinity'",0);
+ return Qnil;
+ } else if(VpIsNegInf(p)) {
+ VpException(VP_EXCEPTION_INFINITY,"Computation results to '-Infinity'",0);
+ return Qnil;
+ }
+
+ e = VpExponent10(p);
+ if(e<=0) return INT2FIX(0);
+ nf = VpBaseFig();
+ if(e<=nf) {
+ e = VpGetSign(p)*p->frac[0];
+ return INT2FIX(e);
+ }
+ psz = ALLOCA_N(char,(unsigned int)(e+nf+2));
+
+ n = (e+nf-1)/nf;
+ pch = psz;
+ if(VpGetSign(p)<0) *pch++ = '-';
+ for(i=0;i<n;++i) {
+ b = VpBaseVal()/10;
+ if(i>=(int)p->Prec) {
+ while(b) {
+ *pch++ = '0';
+ b /= 10;
+ }
+ continue;
+ }
+ v = p->frac[i];
+ while(b) {
+ j = v/b;
+ *pch++ = (char)(j + '0');
+ v -= j*b;
+ b /= 10;
+ }
+ }
+ *pch++ = 0;
+ return rb_cstr2inum(psz,10);
+}
+
+static VALUE
+BigDecimal_induced_from(VALUE self, VALUE x)
+{
+ Real *p = GetVpValue(x,1);
+ return p->obj;
+}
+
+static VALUE
+BigDecimal_to_f(VALUE self)
+{
+ ENTER(1);
+ Real *p;
+ double d, d2;
+ S_LONG e;
+
+ GUARD_OBJ(p,GetVpValue(self,1));
+ if(VpVtoD(&d, &e, p)!=1) return rb_float_new(d);
+ errno = 0;
+ d2 = pow(10.0,(double)e);
+ if((errno == ERANGE && e>0) || (d2>1.0 && (fabs(d) > (DBL_MAX / d2)))) {
+ VpException(VP_EXCEPTION_OVERFLOW,"BigDecimal to Float conversion.",0);
+ if(d>0.0) return rb_float_new(DBL_MAX);
+ else return rb_float_new(-DBL_MAX);
+ }
+ return rb_float_new(d*d2);
+}
+
+static VALUE
+BigDecimal_coerce(VALUE self, VALUE other)
+{
+ ENTER(2);
+ VALUE obj;
+ Real *b;
+ if(TYPE(other) == T_FLOAT) {
+ obj = rb_ary_new();
+ obj = rb_ary_push(obj,other);
+ obj = rb_ary_push(obj,BigDecimal_to_f(self));
+ } else {
+ GUARD_OBJ(b,GetVpValue(other,1));
+ obj = rb_ary_new();
+ obj = rb_ary_push(obj, b->obj);
+ obj = rb_ary_push(obj, self);
+ }
+ return obj;
+}
+
+static VALUE
+BigDecimal_uplus(VALUE self)
+{
+ return self;
+}
+
+static VALUE
+BigDecimal_add(VALUE self, VALUE r)
+{
+ ENTER(5);
+ Real *c, *a, *b;
+ U_LONG mx;
+
+ GUARD_OBJ(a,GetVpValue(self,1));
+ b = GetVpValue(r,0);
+ if(!b) return DoSomeOne(self,r);
+ SAVE(b);
+
+ if(VpIsNaN(b)) return b->obj;
+ if(VpIsNaN(a)) return a->obj;
+ mx = GetAddSubPrec(a,b);
+ if(mx==(-1L)) {
+ GUARD_OBJ(c,VpCreateRbObject(VpBaseFig() + 1, "0"));
+ VpAddSub(c, a, b, 1);
+ } else {
+ GUARD_OBJ(c,VpCreateRbObject(mx *(VpBaseFig() + 1), "0"));
+ if(!mx) {
+ VpSetInf(c,VpGetSign(a));
+ } else {
+ VpAddSub(c, a, b, 1);
+ }
+ }
+ return ToValue(c);
+}
+
+static VALUE
+BigDecimal_sub(VALUE self, VALUE r)
+{
+ ENTER(5);
+ Real *c, *a, *b;
+ U_LONG mx;
+
+ GUARD_OBJ(a,GetVpValue(self,1));
+ b = GetVpValue(r,0);
+ if(!b) return DoSomeOne(self,r);
+ SAVE(b);
+
+ if(VpIsNaN(b)) return b->obj;
+ if(VpIsNaN(a)) return a->obj;
+
+ mx = GetAddSubPrec(a,b);
+ if(mx==(-1L)) {
+ GUARD_OBJ(c,VpCreateRbObject(VpBaseFig() + 1, "0"));
+ VpAddSub(c, a, b, -1);
+ } else {
+ GUARD_OBJ(c,VpCreateRbObject(mx *(VpBaseFig() + 1), "0"));
+ if(!mx) {
+ VpSetInf(c,VpGetSign(a));
+ } else {
+ VpAddSub(c, a, b, -1);
+ }
+ }
+ return ToValue(c);
+}
+
+static S_INT
+BigDecimalCmp(VALUE self, VALUE r)
+{
+ ENTER(5);
+ Real *a, *b;
+ GUARD_OBJ(a,GetVpValue(self,1));
+ b = GetVpValue(r,0);
+ if(!b) return DoSomeOne(self,r);
+ SAVE(b);
+ return VpComp(a, b);
+}
+
+static VALUE
+BigDecimal_zero(VALUE self)
+{
+ Real *a = GetVpValue(self,1);
+ return VpIsZero(a) ? Qtrue : Qfalse;
+}
+
+static VALUE
+BigDecimal_nonzero(VALUE self)
+{
+ Real *a = GetVpValue(self,1);
+ return VpIsZero(a) ? Qnil : self;
+}
+
+static VALUE
+BigDecimal_comp(VALUE self, VALUE r)
+{
+ S_INT e;
+ e = BigDecimalCmp(self, r);
+ if(e==999) return rb_float_new(VpGetDoubleNaN());
+ return INT2FIX(e);
+}
+
+static VALUE
+BigDecimal_eq(VALUE self, VALUE r)
+{
+ ENTER(5);
+ Real *a, *b;
+ GUARD_OBJ(a,GetVpValue(self,1));
+ b = GetVpValue(r,0);
+ if(!b) return Qfalse; /* Not comparable */
+ SAVE(b);
+ return VpComp(a, b)? Qfalse:Qtrue;
+}
+
+static VALUE
+BigDecimal_ne(VALUE self, VALUE r)
+{
+ ENTER(5);
+ Real *a, *b;
+ GUARD_OBJ(a,GetVpValue(self,1));
+ b = GetVpValue(r,0);
+ if(!b) return Qtrue; /* Not comparable */
+ SAVE(b);
+ return VpComp(a, b) ? Qtrue : Qfalse;
+}
+
+static VALUE
+BigDecimal_lt(VALUE self, VALUE r)
+{
+ S_INT e;
+ e = BigDecimalCmp(self, r);
+ if(e==999) return Qfalse;
+ return(e < 0) ? Qtrue : Qfalse;
+}
+
+static VALUE
+BigDecimal_le(VALUE self, VALUE r)
+{
+ S_INT e;
+ e = BigDecimalCmp(self, r);
+ if(e==999) return Qfalse;
+ return(e <= 0) ? Qtrue : Qfalse;
+}
+
+static VALUE
+BigDecimal_gt(VALUE self, VALUE r)
+{
+ S_INT e;
+ e = BigDecimalCmp(self, r);
+ if(e==999) return Qfalse;
+ return(e > 0) ? Qtrue : Qfalse;
+}
+
+static VALUE
+BigDecimal_ge(VALUE self, VALUE r)
+{
+ S_INT e;
+ e = BigDecimalCmp(self, r);
+ if(e==999) return Qfalse;
+ return(e >= 0) ? Qtrue : Qfalse;
+}
+
+static VALUE
+BigDecimal_neg(VALUE self, VALUE r)
+{
+ ENTER(5);
+ Real *c, *a;
+ GUARD_OBJ(a,GetVpValue(self,1));
+ GUARD_OBJ(c,VpCreateRbObject(a->Prec *(VpBaseFig() + 1), "0"));
+ VpAsgn(c, a, -1);
+ return ToValue(c);
+}
+
+static VALUE
+BigDecimal_mult(VALUE self, VALUE r)
+{
+ ENTER(5);
+ Real *c, *a, *b;
+ U_LONG mx;
+
+ GUARD_OBJ(a,GetVpValue(self,1));
+ b = GetVpValue(r,0);
+ if(!b) return DoSomeOne(self,r);
+ SAVE(b);
+
+ mx = a->Prec + b->Prec;
+ GUARD_OBJ(c,VpCreateRbObject(mx *(VpBaseFig() + 1), "0"));
+ VpMult(c, a, b);
+ return ToValue(c);
+}
+
+static VALUE
+BigDecimal_divide(Real **c, Real **res, Real **div, VALUE self, VALUE r)
+/* For c = self.div(r): with round operation */
+{
+ ENTER(5);
+ Real *a, *b;
+ U_LONG mx;
+
+ GUARD_OBJ(a,GetVpValue(self,1));
+ b = GetVpValue(r,0);
+ if(!b) return DoSomeOne(self,r);
+ SAVE(b);
+ *div = b;
+ mx =(a->MaxPrec + b->MaxPrec + 1) * VpBaseFig();
+ GUARD_OBJ((*c),VpCreateRbObject(mx, "0"));
+ GUARD_OBJ((*res),VpCreateRbObject((mx+1) * 2 +(VpBaseFig() + 1), "#0"));
+ VpDivd(*c, *res, a, b);
+ return (VALUE)0;
+}
+
+static VALUE
+BigDecimal_div(VALUE self, VALUE r)
+/* For c = self/r: with round operation */
+{
+ ENTER(5);
+ Real *c=NULL, *res=NULL, *div = NULL;
+ r = BigDecimal_divide(&c, &res, &div, self, r);
+ if(r!=(VALUE)0) return r; /* coerced by other */
+ SAVE(c);SAVE(res);SAVE(div);
+ /* a/b = c + r/b */
+ /* c xxxxx
+ r 00000yyyyy ==> (y/b)*BASE >= HALF_BASE
+ */
+ /* Round */
+ if(VpIsDef(c) && (!VpIsZero(c))) {
+ VpInternalRound(c,0,c->frac[c->Prec-1],(VpBaseVal()*res->frac[0])/div->frac[0]);
+ }
+ return ToValue(c);
+}
+
+/*
+ * %: mod = a%b = a - (a.to_f/b).floor * b
+ * div = (a.to_f/b).floor
+ */
+static VALUE
+BigDecimal_DoDivmod(VALUE self, VALUE r, Real **div, Real **mod)
+{
+ ENTER(8);
+ Real *c=NULL, *d=NULL, *res=NULL;
+ Real *a, *b;
+ U_LONG mx;
+
+ GUARD_OBJ(a,GetVpValue(self,1));
+ b = GetVpValue(r,0);
+ if(!b) return DoSomeOne(self,r);
+ SAVE(b);
+
+ if(VpIsNaN(a) || VpIsNaN(b)) goto NaN;
+ if(VpIsInf(a) || VpIsInf(b)) goto NaN;
+ if(VpIsZero(b)) goto NaN;
+ if(VpIsZero(a)) {
+ GUARD_OBJ(c,VpCreateRbObject(1, "0"));
+ GUARD_OBJ(d,VpCreateRbObject(1, "0"));
+ *div = d;
+ *mod = c;
+ return (VALUE)0;
+ }
+
+ mx = a->Prec;
+ if(mx<b->Prec) mx = b->Prec;
+ mx =(mx + 1) * VpBaseFig();
+ GUARD_OBJ(c,VpCreateRbObject(mx, "0"));
+ GUARD_OBJ(res,VpCreateRbObject((mx+1) * 2 +(VpBaseFig() + 1), "#0"));
+ VpDivd(c, res, a, b);
+ mx = c->Prec *(VpBaseFig() + 1);
+ GUARD_OBJ(d,VpCreateRbObject(mx, "0"));
+ VpActiveRound(d,c,VP_ROUND_DOWN,0);
+ VpMult(res,d,b);
+ VpAddSub(c,a,res,-1);
+ if(!VpIsZero(c) && (VpGetSign(a)*VpGetSign(b)<0)) {
+ VpAddSub(res,d,VpOne(),-1);
+ VpAddSub(d ,c,b, 1);
+ *div = res;
+ *mod = d;
+ } else {
+ *div = d;
+ *mod = c;
+ }
+ return (VALUE)0;
+
+NaN:
+ GUARD_OBJ(c,VpCreateRbObject(1, "NaN"));
+ GUARD_OBJ(d,VpCreateRbObject(1, "NaN"));
+ *div = d;
+ *mod = c;
+ return (VALUE)0;
+}
+
+static VALUE
+BigDecimal_mod(VALUE self, VALUE r) /* %: a%b = a - (a.to_f/b).floor * b */
+{
+ ENTER(3);
+ VALUE obj;
+ Real *div=NULL, *mod=NULL;
+
+ obj = BigDecimal_DoDivmod(self,r,&div,&mod);
+ if(obj!=(VALUE)0) return obj;
+ SAVE(div);SAVE(mod);
+ return ToValue(mod);
+}
+
+static VALUE
+BigDecimal_divremain(VALUE self, VALUE r, Real **dv, Real **rv)
+{
+ ENTER(10);
+ U_LONG mx;
+ Real *a=NULL, *b=NULL, *c=NULL, *res=NULL, *d=NULL, *rr=NULL, *ff=NULL;
+ Real *f=NULL;
+
+ GUARD_OBJ(a,GetVpValue(self,1));
+ b = GetVpValue(r,0);
+ if(!b) return DoSomeOne(self,r);
+ SAVE(b);
+
+ mx =(a->MaxPrec + b->MaxPrec) *VpBaseFig();
+ GUARD_OBJ(c ,VpCreateRbObject(mx, "0"));
+ GUARD_OBJ(res,VpCreateRbObject((mx+1) * 2 +(VpBaseFig() + 1), "#0"));
+ GUARD_OBJ(rr ,VpCreateRbObject((mx+1) * 2 +(VpBaseFig() + 1), "#0"));
+ GUARD_OBJ(ff ,VpCreateRbObject((mx+1) * 2 +(VpBaseFig() + 1), "#0"));
+
+ VpDivd(c, res, a, b);
+
+ mx = c->Prec *(VpBaseFig() + 1);
+
+ GUARD_OBJ(d,VpCreateRbObject(mx, "0"));
+ GUARD_OBJ(f,VpCreateRbObject(mx, "0"));
+
+ VpActiveRound(d,c,VP_ROUND_DOWN,0); /* 0: round off */
+
+ VpFrac(f, c);
+ VpMult(rr,f,b);
+ VpAddSub(ff,res,rr,1);
+
+ *dv = d;
+ *rv = ff;
+ return (VALUE)0;
+}
+
+static VALUE
+BigDecimal_remainder(VALUE self, VALUE r) /* remainder */
+{
+ VALUE f;
+ Real *d,*rv;
+ f = BigDecimal_divremain(self,r,&d,&rv);
+ if(f!=(VALUE)0) return f;
+ return ToValue(rv);
+}
+
+static VALUE
+BigDecimal_divmod(VALUE self, VALUE r)
+{
+ ENTER(5);
+ VALUE obj;
+ Real *div=NULL, *mod=NULL;
+
+ obj = BigDecimal_DoDivmod(self,r,&div,&mod);
+ if(obj!=(VALUE)0) return obj;
+ SAVE(div);SAVE(mod);
+ obj = rb_ary_new();
+ rb_ary_push(obj, ToValue(div));
+ rb_ary_push(obj, ToValue(mod));
+ return obj;
+}
+
+static VALUE
+BigDecimal_div2(int argc, VALUE *argv, VALUE self)
+{
+ ENTER(10);
+ VALUE obj;
+ VALUE b,n;
+ int na = rb_scan_args(argc,argv,"11",&b,&n);
+ if(na==1) { /* div in Float sense */
+ Real *div=NULL;
+ Real *mod;
+ obj = BigDecimal_DoDivmod(self,b,&div,&mod);
+ if(obj!=(VALUE)0) return obj;
+ return ToValue(div);
+ } else { /* div in BigDecimal sense */
+ Real *res=NULL;
+ Real *av=NULL, *bv=NULL, *cv=NULL;
+ U_LONG ix = (U_LONG)GetPositiveInt(n);
+ U_LONG mx = (ix+VpBaseFig()*2);
+ GUARD_OBJ(cv,VpCreateRbObject(mx,"0"));
+ GUARD_OBJ(av,GetVpValue(self,1));
+ GUARD_OBJ(bv,GetVpValue(b,1));
+ mx = cv->MaxPrec+1;
+ GUARD_OBJ(res,VpCreateRbObject((mx * 2 + 2)*VpBaseFig(), "#0"));
+ VpDivd(cv,res,av,bv);
+ VpLeftRound(cv,VpGetRoundMode(),ix);
+ return ToValue(cv);
+ }
+}
+
+static VALUE
+BigDecimal_add2(VALUE self, VALUE b, VALUE n)
+{
+ ENTER(2);
+ Real *cv;
+ U_LONG mx = (U_LONG)GetPositiveInt(n);
+ VALUE c = BigDecimal_add(self,b);
+ GUARD_OBJ(cv,GetVpValue(c,1));
+ VpLeftRound(cv,VpGetRoundMode(),mx);
+ return ToValue(cv);
+}
+
+static VALUE
+BigDecimal_sub2(VALUE self, VALUE b, VALUE n)
+{
+ ENTER(2);
+ Real *cv;
+ U_LONG mx = (U_LONG)GetPositiveInt(n);
+ VALUE c = BigDecimal_sub(self,b);
+ GUARD_OBJ(cv,GetVpValue(c,1));
+ VpLeftRound(cv,VpGetRoundMode(),mx);
+ return ToValue(cv);
+}
+
+static VALUE
+BigDecimal_mult2(VALUE self, VALUE b, VALUE n)
+{
+ ENTER(2);
+ Real *cv;
+ U_LONG mx = (U_LONG)GetPositiveInt(n);
+ VALUE c = BigDecimal_mult(self,b);
+ GUARD_OBJ(cv,GetVpValue(c,1));
+ VpLeftRound(cv,VpGetRoundMode(),mx);
+ return ToValue(cv);
+}
+
+static VALUE
+BigDecimal_abs(VALUE self)
+{
+ ENTER(5);
+ Real *c, *a;
+ U_LONG mx;
+
+ GUARD_OBJ(a,GetVpValue(self,1));
+ mx = a->Prec *(VpBaseFig() + 1);
+ GUARD_OBJ(c,VpCreateRbObject(mx, "0"));
+ VpAsgn(c, a, 1);
+ VpChangeSign(c,(S_INT)1);
+ return ToValue(c);
+}
+
+static VALUE
+BigDecimal_sqrt(VALUE self, VALUE nFig)
+{
+ ENTER(5);
+ Real *c, *a;
+ S_INT mx, n;
+
+ GUARD_OBJ(a,GetVpValue(self,1));
+ mx = a->Prec *(VpBaseFig() + 1);
+ mx *= 2;
+
+ n = GetPositiveInt(nFig) + VpBaseFig() + 1;
+ if(mx <= n) mx = n;
+ GUARD_OBJ(c,VpCreateRbObject(mx, "0"));
+ VpSqrt(c, a);
+ return ToValue(c);
+}
+
+static VALUE
+BigDecimal_fix(VALUE self)
+{
+ ENTER(5);
+ Real *c, *a;
+ U_LONG mx;
+
+ GUARD_OBJ(a,GetVpValue(self,1));
+ mx = a->Prec *(VpBaseFig() + 1);
+ GUARD_OBJ(c,VpCreateRbObject(mx, "0"));
+ VpActiveRound(c,a,VP_ROUND_DOWN,0); /* 0: round off */
+ return ToValue(c);
+}
+
+static VALUE
+BigDecimal_round(int argc, VALUE *argv, VALUE self)
+{
+ ENTER(5);
+ Real *c, *a;
+ int iLoc;
+ U_LONG mx;
+ VALUE vLoc;
+ VALUE vRound;
+
+ int sw = VpGetRoundMode();
+
+ int na = rb_scan_args(argc,argv,"02",&vLoc,&vRound);
+ switch(na) {
+ case 0:
+ iLoc = 0;
+ break;
+ case 1:
+ Check_Type(vLoc, T_FIXNUM);
+ iLoc = FIX2INT(vLoc);
+ break;
+ case 2:{
+ int sws = sw;
+ Check_Type(vLoc, T_FIXNUM);
+ iLoc = FIX2INT(vLoc);
+ Check_Type(vRound, T_FIXNUM);
+ sw = VpSetRoundMode(FIX2INT(vRound));
+ VpSetRoundMode(sws);
+ }
+ break;
+ }
+
+ GUARD_OBJ(a,GetVpValue(self,1));
+ mx = a->Prec *(VpBaseFig() + 1);
+ GUARD_OBJ(c,VpCreateRbObject(mx, "0"));
+ VpActiveRound(c,a,sw,iLoc);
+ return ToValue(c);
+}
+
+static VALUE
+BigDecimal_truncate(int argc, VALUE *argv, VALUE self)
+{
+ ENTER(5);
+ Real *c, *a;
+ int iLoc;
+ U_LONG mx;
+ VALUE vLoc;
+
+ if(rb_scan_args(argc,argv,"01",&vLoc)==0) {
+ iLoc = 0;
+ } else {
+ Check_Type(vLoc, T_FIXNUM);
+ iLoc = FIX2INT(vLoc);
+ }
+
+ GUARD_OBJ(a,GetVpValue(self,1));
+ mx = a->Prec *(VpBaseFig() + 1);
+ GUARD_OBJ(c,VpCreateRbObject(mx, "0"));
+ VpActiveRound(c,a,VP_ROUND_DOWN,iLoc); /* 0: truncate */
+ return ToValue(c);
+}
+
+static VALUE
+BigDecimal_frac(VALUE self)
+{
+ ENTER(5);
+ Real *c, *a;
+ U_LONG mx;
+
+ GUARD_OBJ(a,GetVpValue(self,1));
+ mx = a->Prec *(VpBaseFig() + 1);
+ GUARD_OBJ(c,VpCreateRbObject(mx, "0"));
+ VpFrac(c, a);
+ return ToValue(c);
+}
+
+static VALUE
+BigDecimal_floor(int argc, VALUE *argv, VALUE self)
+{
+ ENTER(5);
+ Real *c, *a;
+ U_LONG mx;
+ int iLoc;
+ VALUE vLoc;
+
+ if(rb_scan_args(argc,argv,"01",&vLoc)==0) {
+ iLoc = 0;
+ } else {
+ Check_Type(vLoc, T_FIXNUM);
+ iLoc = FIX2INT(vLoc);
+ }
+
+ GUARD_OBJ(a,GetVpValue(self,1));
+ mx = a->Prec *(VpBaseFig() + 1);
+ GUARD_OBJ(c,VpCreateRbObject(mx, "0"));
+ VpActiveRound(c,a,VP_ROUND_FLOOR,iLoc);
+ return ToValue(c);
+}
+
+static VALUE
+BigDecimal_ceil(int argc, VALUE *argv, VALUE self)
+{
+ ENTER(5);
+ Real *c, *a;
+ U_LONG mx;
+ int iLoc;
+ VALUE vLoc;
+
+ if(rb_scan_args(argc,argv,"01",&vLoc)==0) {
+ iLoc = 0;
+ } else {
+ Check_Type(vLoc, T_FIXNUM);
+ iLoc = FIX2INT(vLoc);
+ }
+
+ GUARD_OBJ(a,GetVpValue(self,1));
+ mx = a->Prec *(VpBaseFig() + 1);
+ GUARD_OBJ(c,VpCreateRbObject(mx, "0"));
+ VpActiveRound(c,a,VP_ROUND_CEIL,iLoc);
+ return ToValue(c);
+}
+
+static VALUE
+BigDecimal_to_s(int argc, VALUE *argv, VALUE self)
+{
+ ENTER(5);
+ Real *vp;
+ char *psz;
+ U_LONG nc;
+ S_INT mc = 0;
+ VALUE f;
+
+ GUARD_OBJ(vp,GetVpValue(self,1));
+ nc = VpNumOfChars(vp)+1;
+ if(rb_scan_args(argc,argv,"01",&f)==1) {
+ mc = GetPositiveInt(f);
+ nc += (nc + mc - 1) / mc + 1;
+ }
+ psz = ALLOCA_N(char,(unsigned int)nc);
+ VpToString(vp, psz, mc);
+ return rb_str_new2(psz);
+}
+
+static VALUE
+BigDecimal_split(VALUE self)
+{
+ ENTER(5);
+ Real *vp;
+ VALUE obj,obj1;
+ S_LONG e;
+ S_LONG s;
+ char *psz1;
+
+ GUARD_OBJ(vp,GetVpValue(self,1));
+ psz1 = ALLOCA_N(char,(unsigned int)VpNumOfChars(vp));
+ VpSzMantissa(vp,psz1);
+ s = 1;
+ if(psz1[0]=='-') {
+ s = -1; ++psz1;
+ }
+ if(psz1[0]=='N') s=0; /* NaN */
+ e = VpExponent10(vp);
+ obj1 = rb_str_new2(psz1);
+ obj = rb_ary_new();
+ rb_ary_push(obj, INT2FIX(s));
+ rb_ary_push(obj, obj1);
+ rb_ary_push(obj, INT2FIX(10));
+ rb_ary_push(obj, INT2NUM(e));
+ return obj;
+}
+
+static VALUE
+BigDecimal_exponent(VALUE self)
+{
+ S_LONG e = VpExponent10(GetVpValue(self,1));
+ return INT2NUM(e);
+}
+
+static VALUE
+BigDecimal_inspect(VALUE self)
+{
+ ENTER(5);
+ Real *vp;
+ VALUE obj;
+ unsigned int nc;
+ char *psz1;
+ char *pszAll;
+
+ GUARD_OBJ(vp,GetVpValue(self,1));
+ nc = VpNumOfChars(vp);
+ nc +=(nc + 9) / 10;
+
+ psz1 = ALLOCA_N(char,nc);
+ pszAll = ALLOCA_N(char,nc+256);
+ VpToString(vp, psz1, 10);
+ sprintf(pszAll,"#<BigDecimal:%lx,'%s',%lu(%lu)>",self,psz1,VpPrec(vp)*VpBaseFig(),VpMaxPrec(vp)*VpBaseFig());
+ obj = rb_str_new2(pszAll);
+ return obj;
+}
+
+static VALUE
+BigDecimal_power(VALUE self, VALUE p)
+{
+ ENTER(5);
+ Real *x, *y;
+ S_LONG mp, ma, n;
+
+ Check_Type(p, T_FIXNUM);
+ n = FIX2INT(p);
+ ma = n;
+ if(ma < 0) ma = -ma;
+ if(ma == 0) ma = 1;
+
+ GUARD_OBJ(x,GetVpValue(self,1));
+ if(VpIsDef(x)) {
+ mp = x->Prec *(VpBaseFig() + 1);
+ GUARD_OBJ(y,VpCreateRbObject(mp *(ma + 1), "0"));
+ } else {
+ GUARD_OBJ(y,VpCreateRbObject(1, "0"));
+ }
+ VpPower(y, x, n);
+ return ToValue(y);
+}
+
+static VALUE
+BigDecimal_global_new(int argc, VALUE *argv, VALUE self)
+{
+ ENTER(5);
+ Real *pv;
+ S_LONG mf;
+ VALUE nFig;
+ VALUE iniValue;
+
+ if(rb_scan_args(argc,argv,"11",&iniValue,&nFig)==1) {
+ mf = 0;
+ } else {
+ mf = GetPositiveInt(nFig);
+ }
+ SafeStringValue(iniValue);
+ GUARD_OBJ(pv,VpCreateRbObject(mf, RSTRING(iniValue)->ptr));
+ return ToValue(pv);
+}
+
+static VALUE
+BigDecimal_new(int argc, VALUE *argv, VALUE self)
+{
+ ENTER(5);
+ Real *pv;
+ S_LONG mf;
+ VALUE nFig;
+ VALUE iniValue;
+
+ if(rb_scan_args(argc,argv,"11",&iniValue,&nFig)==1) {
+ mf = 0;
+ } else {
+ mf = GetPositiveInt(nFig);
+ }
+ SafeStringValue(iniValue);
+ GUARD_OBJ(pv,VpNewRbClass(mf, RSTRING(iniValue)->ptr,self));
+ return ToValue(pv);
+}
+
+static VALUE
+BigDecimal_limit(int argc, VALUE *argv, VALUE self)
+{
+ VALUE nFig;
+ VALUE nCur = INT2NUM(VpGetPrecLimit());
+
+ if(rb_scan_args(argc,argv,"01",&nFig)==1) {
+ Check_Type(nFig, T_FIXNUM);
+ VpSetPrecLimit(FIX2INT(nFig));
+ }
+ return nCur;
+}
+
+static VALUE
+BigDecimal_sign(VALUE self)
+{ /* sign */
+ int s = GetVpValue(self,1)->sign;
+ return INT2FIX(s);
+}
+
+#ifdef ENABLE_TRIAL_METHOD
+static VALUE
+BigDecimal_e(VALUE self, VALUE nFig)
+{
+ ENTER(5);
+ Real *pv;
+ S_LONG mf;
+
+ mf = GetPositiveInt(nFig);
+ GUARD_OBJ(pv,VpCreateRbObject(mf, "0"));
+ VpExp1(pv);
+ return ToValue(pv);
+}
+
+static VALUE
+BigDecimal_pi(VALUE self, VALUE nFig)
+{
+ ENTER(5);
+ Real *pv;
+ S_LONG mf;
+
+ mf = GetPositiveInt(nFig)+VpBaseFig()-1;
+ GUARD_OBJ(pv,VpCreateRbObject(mf, "0"));
+ VpPi(pv);
+ return ToValue(pv);
+}
+
+static VALUE
+BigDecimal_exp(VALUE self, VALUE nFig)
+{
+ ENTER(5);
+ Real *c, *y;
+ S_LONG mf;
+
+ GUARD_OBJ(y,GetVpValue(self,1));
+ mf = GetPositiveInt(nFig);
+ GUARD_OBJ(c,VpCreateRbObject(mf, "0"));
+ VpExp(c, y);
+ return ToValue(c);
+}
+
+static VALUE
+BigDecimal_sincos(VALUE self, VALUE nFig)
+{
+ ENTER(5);
+ VALUE obj;
+ VALUE objSin;
+ VALUE objCos;
+ Real *pcos, *psin, *y;
+ S_LONG mf;
+
+ obj = rb_ary_new();
+ GUARD_OBJ(y,GetVpValue(self,1));
+ mf = GetPositiveInt(nFig);
+ GUARD_OBJ(pcos,VpCreateRbObject(mf, "0"));
+ GUARD_OBJ(psin,VpCreateRbObject(mf, "0"));
+ VpSinCos(psin, pcos, y);
+
+ objSin = ToValue(psin);
+ objCos = ToValue(pcos);
+ rb_ary_push(obj, objSin);
+ rb_ary_push(obj, objCos);
+ return obj;
+}
+#endif /* ENABLE_TRIAL_METHOD */
+
+void
+Init_bigdecimal(void)
+{
+ /* Initialize VP routines */
+ VpInit((U_LONG)0);
+ coerce = rb_intern("coerce");
+ /* Class and method registration */
+ rb_cBigDecimal = rb_define_class("BigDecimal",rb_cNumeric);
+
+ /* Global function */
+ rb_define_global_function("BigDecimal", BigDecimal_global_new, -1);
+
+ /* Class methods */
+ rb_define_singleton_method(rb_cBigDecimal, "new", BigDecimal_new, -1);
+ rb_define_singleton_method(rb_cBigDecimal, "mode", BigDecimal_mode, 2);
+ rb_define_singleton_method(rb_cBigDecimal, "limit", BigDecimal_limit, -1);
+ rb_define_singleton_method(rb_cBigDecimal, "double_fig", BigDecimal_double_fig, 0);
+ rb_define_singleton_method(rb_cBigDecimal, "induced_from",BigDecimal_induced_from, 1);
+ rb_define_singleton_method(rb_cBigDecimal, "_load", BigDecimal_load, 1);
+ rb_define_singleton_method(rb_cBigDecimal, "ver", BigDecimal_version, 0);
+
+ /* Constants definition */
+ rb_define_const(rb_cBigDecimal, "BASE", INT2FIX((S_INT)VpBaseVal()));
+
+ /* Exceptions */
+ rb_define_const(rb_cBigDecimal, "EXCEPTION_ALL",INT2FIX(VP_EXCEPTION_ALL));
+ rb_define_const(rb_cBigDecimal, "EXCEPTION_NaN",INT2FIX(VP_EXCEPTION_NaN));
+ rb_define_const(rb_cBigDecimal, "EXCEPTION_INFINITY",INT2FIX(VP_EXCEPTION_INFINITY));
+ rb_define_const(rb_cBigDecimal, "EXCEPTION_UNDERFLOW",INT2FIX(VP_EXCEPTION_UNDERFLOW));
+ rb_define_const(rb_cBigDecimal, "EXCEPTION_OVERFLOW",INT2FIX(VP_EXCEPTION_OVERFLOW));
+ rb_define_const(rb_cBigDecimal, "EXCEPTION_ZERODIVIDE",INT2FIX(VP_EXCEPTION_ZERODIVIDE));
+
+ /* Computation mode */
+ rb_define_const(rb_cBigDecimal, "ROUND_MODE",INT2FIX(VP_ROUND_MODE));
+ rb_define_const(rb_cBigDecimal, "ROUND_UP",INT2FIX(VP_ROUND_UP));
+ rb_define_const(rb_cBigDecimal, "ROUND_DOWN",INT2FIX(VP_ROUND_DOWN));
+ rb_define_const(rb_cBigDecimal, "ROUND_HALF_UP",INT2FIX(VP_ROUND_HALF_UP));
+ rb_define_const(rb_cBigDecimal, "ROUND_HALF_DOWN",INT2FIX(VP_ROUND_HALF_DOWN));
+ rb_define_const(rb_cBigDecimal, "ROUND_CEILING",INT2FIX(VP_ROUND_CEIL));
+ rb_define_const(rb_cBigDecimal, "ROUND_FLOOR",INT2FIX(VP_ROUND_FLOOR));
+ rb_define_const(rb_cBigDecimal, "ROUND_HALF_EVEN",INT2FIX(VP_ROUND_HALF_EVEN));
+
+ /* Constants for sign value */
+ rb_define_const(rb_cBigDecimal, "SIGN_NaN",INT2FIX(VP_SIGN_NaN));
+ rb_define_const(rb_cBigDecimal, "SIGN_POSITIVE_ZERO",INT2FIX(VP_SIGN_POSITIVE_ZERO));
+ rb_define_const(rb_cBigDecimal, "SIGN_NEGATIVE_ZERO",INT2FIX(VP_SIGN_NEGATIVE_ZERO));
+ rb_define_const(rb_cBigDecimal, "SIGN_POSITIVE_FINITE",INT2FIX(VP_SIGN_POSITIVE_FINITE));
+ rb_define_const(rb_cBigDecimal, "SIGN_NEGATIVE_FINITE",INT2FIX(VP_SIGN_NEGATIVE_FINITE));
+ rb_define_const(rb_cBigDecimal, "SIGN_POSITIVE_INFINITE",INT2FIX(VP_SIGN_POSITIVE_INFINITE));
+ rb_define_const(rb_cBigDecimal, "SIGN_NEGATIVE_INFINITE",INT2FIX(VP_SIGN_NEGATIVE_INFINITE));
+
+ /* instance methods */
+ rb_define_method(rb_cBigDecimal, "prec", BigDecimal_prec, 0);
+ rb_define_method(rb_cBigDecimal, "add", BigDecimal_add2, 2);
+ rb_define_method(rb_cBigDecimal, "sub", BigDecimal_sub2, 2);
+ rb_define_method(rb_cBigDecimal, "mult", BigDecimal_mult2, 2);
+ rb_define_method(rb_cBigDecimal, "div",BigDecimal_div2, -1);
+ rb_define_method(rb_cBigDecimal, "hash", BigDecimal_hash, 0);
+ rb_define_method(rb_cBigDecimal, "to_s", BigDecimal_to_s, -1);
+ rb_define_method(rb_cBigDecimal, "to_i", BigDecimal_to_i, 0);
+ rb_define_method(rb_cBigDecimal, "to_int", BigDecimal_to_i, 0);
+ rb_define_method(rb_cBigDecimal, "split", BigDecimal_split, 0);
+ rb_define_method(rb_cBigDecimal, "+", BigDecimal_add, 1);
+ rb_define_method(rb_cBigDecimal, "-", BigDecimal_sub, 1);
+ rb_define_method(rb_cBigDecimal, "+@", BigDecimal_uplus, 0);
+ rb_define_method(rb_cBigDecimal, "-@", BigDecimal_neg, 0);
+ rb_define_method(rb_cBigDecimal, "*", BigDecimal_mult, 1);
+ rb_define_method(rb_cBigDecimal, "/", BigDecimal_div, 1);
+ rb_define_method(rb_cBigDecimal, "quo", BigDecimal_div, 1);
+ rb_define_method(rb_cBigDecimal, "%", BigDecimal_mod, 1);
+ rb_define_method(rb_cBigDecimal, "modulo", BigDecimal_mod, 1);
+ rb_define_method(rb_cBigDecimal, "remainder", BigDecimal_remainder, 1);
+ rb_define_method(rb_cBigDecimal, "divmod", BigDecimal_divmod, 1);
+ /* rb_define_method(rb_cBigDecimal, "dup", BigDecimal_dup, 0); */
+ rb_define_method(rb_cBigDecimal, "to_f", BigDecimal_to_f, 0);
+ rb_define_method(rb_cBigDecimal, "abs", BigDecimal_abs, 0);
+ rb_define_method(rb_cBigDecimal, "sqrt", BigDecimal_sqrt, 1);
+ rb_define_method(rb_cBigDecimal, "fix", BigDecimal_fix, 0);
+ rb_define_method(rb_cBigDecimal, "round", BigDecimal_round, -1);
+ rb_define_method(rb_cBigDecimal, "frac", BigDecimal_frac, 0);
+ rb_define_method(rb_cBigDecimal, "floor", BigDecimal_floor, -1);
+ rb_define_method(rb_cBigDecimal, "ceil", BigDecimal_ceil, -1);
+ rb_define_method(rb_cBigDecimal, "power", BigDecimal_power, 1);
+ rb_define_method(rb_cBigDecimal, "**", BigDecimal_power, 1);
+ rb_define_method(rb_cBigDecimal, "<=>", BigDecimal_comp, 1);
+ rb_define_method(rb_cBigDecimal, "==", BigDecimal_eq, 1);
+ rb_define_method(rb_cBigDecimal, "===", BigDecimal_eq, 1);
+ rb_define_method(rb_cBigDecimal, "eql?", BigDecimal_eq, 1);
+ rb_define_method(rb_cBigDecimal, "!=", BigDecimal_ne, 1);
+ rb_define_method(rb_cBigDecimal, "<", BigDecimal_lt, 1);
+ rb_define_method(rb_cBigDecimal, "<=", BigDecimal_le, 1);
+ rb_define_method(rb_cBigDecimal, ">", BigDecimal_gt, 1);
+ rb_define_method(rb_cBigDecimal, ">=", BigDecimal_ge, 1);
+ rb_define_method(rb_cBigDecimal, "zero?", BigDecimal_zero, 0);
+ rb_define_method(rb_cBigDecimal, "nonzero?", BigDecimal_nonzero, 0);
+ rb_define_method(rb_cBigDecimal, "coerce", BigDecimal_coerce, 1);
+ rb_define_method(rb_cBigDecimal, "inspect", BigDecimal_inspect, 0);
+ rb_define_method(rb_cBigDecimal, "exponent", BigDecimal_exponent, 0);
+ rb_define_method(rb_cBigDecimal, "sign", BigDecimal_sign, 0);
+ rb_define_method(rb_cBigDecimal, "nan?", BigDecimal_IsNaN, 0);
+ rb_define_method(rb_cBigDecimal, "infinite?", BigDecimal_IsInfinite, 0);
+ rb_define_method(rb_cBigDecimal, "finite?", BigDecimal_IsFinite, 0);
+ rb_define_method(rb_cBigDecimal, "truncate", BigDecimal_truncate, -1);
+ rb_define_method(rb_cBigDecimal, "_dump", BigDecimal_dump, -1);
+
+#ifdef ENABLE_TRIAL_METHOD
+ rb_define_singleton_method(rb_cBigDecimal, "E", BigDecimal_e, 1);
+ rb_define_singleton_method(rb_cBigDecimal, "PI", BigDecimal_pi, 1);
+ rb_define_method(rb_cBigDecimal, "exp", BigDecimal_exp, 1);
+ rb_define_method(rb_cBigDecimal, "sincos", BigDecimal_sincos, 1);
+#endif /* ENABLE_TRIAL_METHOD */
+}
+
+/*
+ *
+ * ============================================================================
+ *
+ * vp_ routines begin from here.
+ *
+ * ============================================================================
+ *
+ */
+#ifdef _DEBUG
+static int gfDebug = 0; /* Debug switch */
+static int gfCheckVal = 1; /* Value checking flag in VpNmlz() */
+#endif /* _DEBUG */
+
+static U_LONG gnPrecLimit = 0; /* Global upper limit of the precision newly allocated */
+static short gfRoundMode = VP_ROUND_HALF_UP; /* Mode for general rounding operation */
+
+static U_LONG BASE_FIG = 4; /* =log10(BASE) */
+static U_LONG BASE = 10000L; /* Base value(value must be 10**BASE_FIG) */
+ /* The value of BASE**2 + BASE must be represented */
+ /* within one U_LONG. */
+static U_LONG HALF_BASE = 5000L;/* =BASE/2 */
+static S_LONG DBLE_FIG = 8; /* figure of double */
+static U_LONG BASE1 = 1000L; /* =BASE/10 */
+
+static Real *VpConstOne; /* constant 1.0 */
+static Real *VpPt5; /* constant 0.5 */
+static U_LONG maxnr = 100; /* Maximum iterations for calcurating sqrt. */
+ /* used in VpSqrt() */
+
+/* ETC */
+#define MemCmp(x,y,z) memcmp(x,y,z)
+#define StrCmp(x,y) strcmp(x,y)
+
+static int VpIsDefOP(Real *c,Real *a,Real *b,int sw);
+static int AddExponent(Real *a,S_INT n);
+static int VpAddAbs(Real *a,Real *b,Real *c);
+static int VpSubAbs(Real *a,Real *b,Real *c);
+static U_LONG VpSetPTR(Real *a,Real *b,Real *c,U_LONG *a_pos,U_LONG *b_pos,U_LONG *c_pos,U_LONG *av,U_LONG *bv);
+static int VpNmlz(Real *a);
+static void VpFormatSt(char *psz,S_INT fFmt);
+static int VpRdup(Real *m,U_LONG ind_m);
+static U_LONG SkipWhiteChar(char *szVal);
+
+#ifdef _DEBUG
+static int gnAlloc=0; /* Memory allocation counter */
+#endif /* _DEBUG */
+
+VP_EXPORT void *
+VpMemAlloc(U_LONG mb)
+{
+ void *p = xmalloc((unsigned int)mb);
+ if(!p) {
+ VpException(VP_EXCEPTION_MEMORY,"failed to allocate memory",1);
+ }
+ memset(p,0,mb);
+#ifdef _DEBUG
+ gnAlloc++; /* Count allocation call */
+#endif /* _DEBUG */
+ return p;
+}
+
+VP_EXPORT void
+VpFree(Real *pv)
+{
+ if(pv != NULL) {
+ xfree(pv);
+#ifdef _DEBUG
+ gnAlloc--; /* Decrement allocation count */
+ if(gnAlloc==0) {
+ printf(" *************** All memories allocated freed ****************");
+ getchar();
+ }
+ if(gnAlloc<0) {
+ printf(" ??????????? Too many memory free calls(%d) ?????????????\n",gnAlloc);
+ getchar();
+ }
+#endif /* _DEBUG */
+ }
+}
+
+/*
+ * EXCEPTION Handling.
+ */
+static unsigned short gfDoException = 0; /* Exception flag */
+
+static unsigned short
+VpGetException (void)
+{
+ return gfDoException;
+}
+
+static void
+VpSetException(unsigned short f)
+{
+ gfDoException = f;
+}
+
+/* These 2 functions added at v1.1.7 */
+VP_EXPORT U_LONG
+VpGetPrecLimit(void)
+{
+ return gnPrecLimit;
+}
+
+VP_EXPORT U_LONG
+VpSetPrecLimit(U_LONG n)
+{
+ U_LONG s = gnPrecLimit;
+ gnPrecLimit = n;
+ return s;
+}
+
+VP_EXPORT unsigned long
+VpGetRoundMode(void)
+{
+ return gfRoundMode;
+}
+
+VP_EXPORT unsigned long
+VpSetRoundMode(unsigned long n)
+{
+ if(n==VP_ROUND_UP || n!=VP_ROUND_DOWN ||
+ n==VP_ROUND_HALF_UP || n!=VP_ROUND_HALF_DOWN ||
+ n==VP_ROUND_CEIL || n!=VP_ROUND_FLOOR ||
+ n==VP_ROUND_HALF_EVEN
+ ) gfRoundMode = n;
+ return gfRoundMode;
+}
+
+/*
+ * 0.0 & 1.0 generator
+ * These gZero_..... and gOne_..... can be any name
+ * referenced from nowhere except Zero() and One().
+ * gZero_..... and gOne_..... must have global scope
+ * (to let the compiler know they may be changed in outside
+ * (... but not actually..)).
+ */
+volatile double gZero_ABCED9B1_CE73__00400511F31D = 0.0;
+volatile double gOne_ABCED9B4_CE73__00400511F31D = 1.0;
+static double
+Zero(void)
+{
+ return gZero_ABCED9B1_CE73__00400511F31D;
+}
+
+static double
+One(void)
+{
+ return gOne_ABCED9B4_CE73__00400511F31D;
+}
+
+VP_EXPORT U_LONG
+VpBaseFig(void)
+{
+ return BASE_FIG;
+}
+
+VP_EXPORT U_LONG
+VpDblFig(void)
+{
+ return DBLE_FIG;
+}
+
+VP_EXPORT U_LONG
+VpBaseVal(void)
+{
+ return BASE;
+}
+
+/*
+ ----------------------------------------------------------------
+ Value of sign in Real structure is reserved for future use.
+ short sign;
+ ==0 : NaN
+ 1 : Positive zero
+ -1 : Negative zero
+ 2 : Positive number
+ -2 : Negative number
+ 3 : Positive infinite number
+ -3 : Negative infinite number
+ ----------------------------------------------------------------
+*/
+
+VP_EXPORT double
+VpGetDoubleNaN(void) /* Returns the value of NaN */
+{
+ static double fNaN = 0.0;
+ if(fNaN==0.0) fNaN = Zero()/Zero();
+ return fNaN;
+}
+
+VP_EXPORT double
+VpGetDoublePosInf(void) /* Returns the value of +Infinity */
+{
+ static double fInf = 0.0;
+ if(fInf==0.0) fInf = One()/Zero();
+ return fInf;
+}
+
+VP_EXPORT double
+VpGetDoubleNegInf(void) /* Returns the value of -Infinity */
+{
+ static double fInf = 0.0;
+ if(fInf==0.0) fInf = -(One()/Zero());
+ return fInf;
+}
+
+VP_EXPORT double
+VpGetDoubleNegZero(void) /* Returns the value of -0 */
+{
+ static double nzero = 1000.0;
+ if(nzero!=0.0) nzero = (One()/VpGetDoubleNegInf());
+ return nzero;
+}
+
+VP_EXPORT int
+VpIsNegDoubleZero(double v)
+{
+ double z = VpGetDoubleNegZero();
+ return MemCmp(&v,&z,sizeof(v))==0;
+}
+
+VP_EXPORT int
+VpException(unsigned short f,char *str,int always)
+{
+ VALUE exc;
+ int fatal=0;
+
+ if(f==VP_EXCEPTION_OP || f==VP_EXCEPTION_MEMORY) always = 1;
+
+ if(always||(gfDoException&f)) {
+ switch(f)
+ {
+ /*
+ case VP_EXCEPTION_ZERODIVIDE:
+ case VP_EXCEPTION_OVERFLOW:
+ */
+ case VP_EXCEPTION_INFINITY:
+ exc = rb_eFloatDomainError;
+ goto raise;
+ case VP_EXCEPTION_NaN:
+ exc = rb_eFloatDomainError;
+ goto raise;
+ case VP_EXCEPTION_UNDERFLOW:
+ exc = rb_eFloatDomainError;
+ goto raise;
+ case VP_EXCEPTION_OP:
+ exc = rb_eFloatDomainError;
+ goto raise;
+ case VP_EXCEPTION_MEMORY:
+ fatal = 1;
+ goto raise;
+ default:
+ fatal = 1;
+ goto raise;
+ }
+ }
+ return 0; /* 0 Means VpException() raised no exception */
+
+raise:
+ if(fatal) rb_fatal(str);
+ else rb_raise(exc,str);
+ return 0;
+}
+
+/* Throw exception or returns 0,when resulting c is Inf or NaN */
+/* sw=1:+ 2:- 3:* 4:/ */
+static int
+VpIsDefOP(Real *c,Real *a,Real *b,int sw)
+{
+ if(VpIsNaN(a) || VpIsNaN(b)) {
+ /* at least a or b is NaN */
+ VpSetNaN(c);
+ goto NaN;
+ }
+
+ if(VpIsInf(a)) {
+ if(VpIsInf(b)) {
+ switch(sw)
+ {
+ case 1: /* + */
+ if(VpGetSign(a)==VpGetSign(b)) {
+ VpSetInf(c,VpGetSign(a));
+ goto Inf;
+ } else {
+ VpSetNaN(c);
+ goto NaN;
+ }
+ case 2: /* - */
+ if(VpGetSign(a)!=VpGetSign(b)) {
+ VpSetInf(c,VpGetSign(a));
+ goto Inf;
+ } else {
+ VpSetNaN(c);
+ goto NaN;
+ }
+ break;
+ case 3: /* * */
+ VpSetInf(c,VpGetSign(a)*VpGetSign(b));
+ goto Inf;
+ break;
+ case 4: /* / */
+ VpSetNaN(c);
+ goto NaN;
+ }
+ VpSetNaN(c);
+ goto NaN;
+ }
+ /* Inf op Finite */
+ switch(sw)
+ {
+ case 1: /* + */
+ case 2: /* - */
+ VpSetInf(c,VpGetSign(a));
+ break;
+ case 3: /* * */
+ if(VpIsZero(b)) {
+ VpSetNaN(c);
+ goto NaN;
+ }
+ VpSetInf(c,VpGetSign(a)*VpGetSign(b));
+ break;
+ case 4: /* / */
+ VpSetInf(c,VpGetSign(a)*VpGetSign(b));
+ }
+ goto Inf;
+ }
+
+ if(VpIsInf(b)) {
+ switch(sw)
+ {
+ case 1: /* + */
+ VpSetInf(c,VpGetSign(b));
+ break;
+ case 2: /* - */
+ VpSetInf(c,-VpGetSign(b));
+ break;
+ case 3: /* * */
+ if(VpIsZero(a)) {
+ VpSetNaN(c);
+ goto NaN;
+ }
+ VpSetInf(c,VpGetSign(a)*VpGetSign(b));
+ break;
+ case 4: /* / */
+ VpSetZero(c,VpGetSign(a)*VpGetSign(b));
+ }
+ goto Inf;
+ }
+ return 1; /* Results OK */
+
+Inf:
+ return VpException(VP_EXCEPTION_INFINITY,"Computation results to 'Infinity'",0);
+NaN:
+ return VpException(VP_EXCEPTION_NaN,"Computation results to 'NaN'",0);
+}
+
+/*
+ ----------------------------------------------------------------
+*/
+
+/*
+ * returns number of chars needed to represent vp.
+ */
+VP_EXPORT U_LONG
+VpNumOfChars(Real *vp)
+{
+ if(vp == NULL) return BASE_FIG*2+6;
+ if(!VpIsDef(vp)) return 32; /* not sure,may be OK */
+ return BASE_FIG *(vp->Prec + 2)+6; /* 3: sign + exponent chars */
+}
+
+/*
+ * Initializer for Vp routines and constants used.
+ * [Input]
+ * BaseVal: Base value(assigned to BASE) for Vp calculation.
+ * It must be the form BaseVal=10**n.(n=1,2,3,...)
+ * If Base <= 0L,then the BASE will be calcurated so
+ * that BASE is as large as possible satisfying the
+ * relation MaxVal <= BASE*(BASE+1). Where the value
+ * MaxVal is the largest value which can be represented
+ * by one U_LONG word(LONG) in the computer used.
+ *
+ * [Returns]
+ * DBLE_FIG ... OK
+ */
+VP_EXPORT U_LONG
+VpInit(U_LONG BaseVal)
+{
+ U_LONG w;
+ double v;
+
+ /* Setup +/- Inf NaN -0 */
+ VpGetDoubleNaN();
+ VpGetDoublePosInf();
+ VpGetDoubleNegInf();
+ VpGetDoubleNegZero();
+
+ if(BaseVal <= 0) {
+ /* Base <= 0, then determine Base by calcuration. */
+ BASE = 1;
+ while(
+ (BASE > 0) &&
+ ((w = BASE *(BASE + 1)) > BASE) &&((w / BASE) ==(BASE + 1))
+ ) {
+ BaseVal = BASE;
+ BASE = BaseVal * 10L;
+ }
+ }
+ /* Set Base Values */
+ BASE = BaseVal;
+ HALF_BASE = BASE / 2;
+ BASE1 = BASE / 10;
+ BASE_FIG = 0;
+ while(BaseVal /= 10) ++BASE_FIG;
+ /* Allocates Vp constants. */
+ VpConstOne = VpAlloc((U_LONG)1, "1");
+ VpPt5 = VpAlloc((U_LONG)1, ".5");
+
+#ifdef _DEBUG
+ gnAlloc = 0;
+#endif /* _DEBUG */
+
+ /* Determine # of digits available in one 'double'. */
+
+ v = 1.0;
+ DBLE_FIG = 0;
+ while(v + 1.0 > 1.0) {
+ ++DBLE_FIG;
+ v /= 10;
+ }
+
+#ifdef _DEBUG
+ if(gfDebug) {
+ printf("VpInit: BaseVal = %lu\n", BaseVal);
+ printf(" BASE = %lu\n", BASE);
+ printf(" HALF_BASE = %lu\n", HALF_BASE);
+ printf(" BASE1 = %lu\n", BASE1);
+ printf(" BASE_FIG = %lu\n", BASE_FIG);
+ printf(" DBLE_FIG = %lu\n", DBLE_FIG);
+ }
+#endif /* _DEBUG */
+
+ return DBLE_FIG;
+}
+
+VP_EXPORT Real *
+VpOne()
+{
+ return VpConstOne;
+}
+
+/* If exponent overflows,then raise exception or returns 0 */
+static int
+AddExponent(Real *a,S_INT n)
+{
+ S_INT e = a->exponent;
+ S_INT m = e+n;
+ S_INT eb,mb;
+ if(e>0) {
+ if(n>0) {
+ mb = m*BASE_FIG;
+ eb = e*BASE_FIG;
+ if(mb<eb) goto overflow;
+ }
+ } else if(n<0) {
+ mb = m*BASE_FIG;
+ eb = e*BASE_FIG;
+ if(mb>eb) goto underflow;
+ }
+ a->exponent = m;
+ return 1;
+
+/* Overflow/Underflow ==> Raise exception or returns 0 */
+underflow:
+ VpSetZero(a,VpGetSign(a));
+ return VpException(VP_EXCEPTION_UNDERFLOW,"Exponent underflow",0);
+
+overflow:
+ VpSetInf(a,VpGetSign(a));
+ return VpException(VP_EXCEPTION_OVERFLOW,"Exponent overflow",0);
+}
+
+/*
+ * Allocates variable.
+ * [Input]
+ * mx ... allocation unit, if zero then mx is determined by szVal.
+ * The mx is the number of effective digits can to be stored.
+ * szVal ... value assigned(char). If szVal==NULL,then zero is assumed.
+ * If szVal[0]=='#' then Max. Prec. will not be considered(1.1.7),
+ * full precision specified by szVal is allocated.
+ *
+ * [Returns]
+ * Pointer to the newly allocated variable, or
+ * NULL be returned if memory allocation is failed,or any error.
+ */
+VP_EXPORT Real *
+VpAlloc(U_LONG mx, char *szVal)
+{
+ U_LONG i, ni, ipn, ipf, nf, ipe, ne, nalloc;
+ char v;
+ int sign=1;
+ Real *vp = NULL;
+ U_LONG mf = VpGetPrecLimit();
+ mx = (mx + BASE_FIG - 1) / BASE_FIG + 1; /* Determine allocation unit. */
+ if(szVal) {
+ if(*szVal!='#') {
+ if(mf) {
+ mf = (mf + BASE_FIG - 1) / BASE_FIG + 2; /* Needs 1 more for div */
+ if(mx>mf) {
+ mx = mf;
+ }
+ }
+ } else {
+ ++szVal;
+ }
+ }
+
+ /* necessary to be able to store */
+ /* at least mx digits. */
+ if(szVal == NULL) {
+ /* szVal==NULL ==> allocate zero value. */
+ vp = (Real *) VpMemAlloc(sizeof(Real) + mx * sizeof(U_LONG));
+ /* xmalloc() alway returns(or throw interruption) */
+ vp->MaxPrec = mx; /* set max precision */
+ VpSetZero(vp,1); /* initialize vp to zero. */
+ return vp;
+ }
+ /* Check on Inf & NaN */
+ if(StrCmp(szVal,SZ_PINF)==0 ||
+ StrCmp(szVal,SZ_INF)==0 ) {
+ vp = (Real *) VpMemAlloc(sizeof(Real) + sizeof(U_LONG));
+ vp->MaxPrec = 1; /* set max precision */
+ VpSetPosInf(vp);
+ return vp;
+ }
+ if(StrCmp(szVal,SZ_NINF)==0) {
+ vp = (Real *) VpMemAlloc(sizeof(Real) + sizeof(U_LONG));
+ vp->MaxPrec = 1; /* set max precision */
+ VpSetNegInf(vp);
+ return vp;
+ }
+ if(StrCmp(szVal,SZ_NaN)==0) {
+ vp = (Real *) VpMemAlloc(sizeof(Real) + sizeof(U_LONG));
+ vp->MaxPrec = 1; /* set max precision */
+ VpSetNaN(vp);
+ return vp;
+ }
+
+ /* check on number szVal[] */
+ i = SkipWhiteChar(szVal);
+ ipn = i;
+ if (szVal[i] == '-') {sign=-1;++i;}
+ else if(szVal[i] == '+') ++i;
+ /* Skip digits */
+ ni = 0; /* digits in mantissa */
+ while(v = szVal[i]) {
+ if(!ISDIGIT(v)) break;
+ ++i;
+ ++ni;
+ }
+ nf = 0;
+ ipf = 0;
+ ipe = 0;
+ ne = 0;
+ if(v) {
+ /* other than digit nor \0 */
+ if(szVal[i] == '.') { /* xxx. */
+ ++i;
+ ipf = i;
+ while(v = szVal[i]) { /* get fraction part. */
+ if(!ISDIGIT(v)) break;
+ ++i;
+ ++nf;
+ }
+ }
+ ipe = 0; /* Exponent */
+
+ switch(szVal[i]) {
+ case '\0': break;
+ case 'e':
+ case 'E':
+ case 'd':
+ case 'D':
+ ++i;
+ ipe = i;
+ v = szVal[i];
+ if((v == '-') ||(v == '+')) ++i;
+ while(szVal[i]) {
+ ++i;
+ ++ne;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ nalloc =(ni + nf + BASE_FIG - 1) / BASE_FIG + 1; /* set effective allocation */
+ /* units for szVal[] */
+ if(mx <= 0) mx = 1;
+ nalloc = Max(nalloc, mx);
+ mx = nalloc;
+ vp =(Real *) VpMemAlloc(sizeof(Real) + mx * sizeof(U_LONG));
+ /* xmalloc() alway returns(or throw interruption) */
+ vp->MaxPrec = mx; /* set max precision */
+ VpSetZero(vp,sign);
+ VpCtoV(vp, &(szVal[ipn]), ni, &(szVal[ipf]), nf, &(szVal[ipe]), ne);
+ return vp;
+}
+
+/*
+ * Assignment(c=a).
+ * [Input]
+ * a ... RHSV
+ * isw ... switch for assignment.
+ * c = a when isw > 0
+ * c = -a when isw < 0
+ * if c->MaxPrec < a->Prec,then round operation
+ * will be performed.
+ * [Output]
+ * c ... LHSV
+ */
+VP_EXPORT int
+VpAsgn(Real *c, Real *a, int isw)
+{
+ U_LONG n;
+ if(VpIsNaN(a)) {
+ VpSetNaN(c);
+ return 0;
+ }
+ if(VpIsInf(a)) {
+ VpSetInf(c,isw);
+ return 0;
+ }
+
+ /* check if the RHS is zero */
+ if(!VpIsZero(a)) {
+ c->exponent = a->exponent; /* store exponent */
+ VpSetSign(c,(isw*VpGetSign(a))); /* set sign */
+ n =(a->Prec < c->MaxPrec) ?(a->Prec) :(c->MaxPrec);
+ c->Prec = n;
+ memcpy(c->frac, a->frac, n * sizeof(U_LONG));
+ /* Needs round ? */
+ if(c->Prec < a->Prec) {
+ VpInternalRound(c,n,(n>0)?a->frac[n-1]:0,a->frac[n]);
+ }
+ } else {
+ /* The value of 'a' is zero. */
+ VpSetZero(c,isw*VpGetSign(a));
+ return 1;
+ }
+ return c->Prec*BASE_FIG;
+}
+
+/*
+ * c = a + b when operation = 1 or 2
+ * = a - b when operation = -1 or -2.
+ * Returns number of significant digits of c
+ */
+VP_EXPORT int
+VpAddSub(Real *c, Real *a, Real *b, int operation)
+{
+ S_INT sw, isw;
+ Real *a_ptr, *b_ptr;
+ U_LONG n, na, nb, i;
+
+#ifdef _DEBUG
+ if(gfDebug) {
+ VPrint(stdout, "VpAddSub(enter) a=% \n", a);
+ VPrint(stdout, " b=% \n", b);
+ printf(" operation=%d\n", operation);
+ }
+#endif /* _DEBUG */
+
+ if(!VpIsDefOP(c,a,b,(operation>0)?1:2)) return 0; /* No significant digits */
+
+ /* check if a or b is zero */
+ if(VpIsZero(a)) {
+ /* a is zero,then assign b to c */
+ if(!VpIsZero(b)) {
+ VpAsgn(c, b, operation);
+ } else {
+ /* Both a and b are zero. */
+ if(VpGetSign(a)<0 && operation*VpGetSign(b)<0) {
+ /* -0 -0 */
+ VpSetZero(c,-1);
+ } else {
+ VpSetZero(c,1);
+ }
+ return 1; /* 0: 1 significant digits */
+ }
+ return c->Prec*BASE_FIG;
+ }
+ if(VpIsZero(b)) {
+ /* b is zero,then assign a to c. */
+ VpAsgn(c, a, 1);
+ return c->Prec*BASE_FIG;
+ }
+
+ if(operation < 0) sw = -1;
+ else sw = 1;
+
+ /* compare absolute value. As a result,|a_ptr|>=|b_ptr| */
+ if(a->exponent > b->exponent) {
+ a_ptr = a;
+ b_ptr = b;
+ } /* |a|>|b| */
+ else if(a->exponent < b->exponent) {
+ a_ptr = b;
+ b_ptr = a;
+ } /* |a|<|b| */
+ else {
+ /* Exponent part of a and b is the same,then compare fraction */
+ /* part */
+ na = a->Prec;
+ nb = b->Prec;
+ n = Min(na, nb);
+ for(i=0;i < n; ++i) {
+ if(a->frac[i] > b->frac[i]) {
+ a_ptr = a;
+ b_ptr = b;
+ goto end_if;
+ } else if(a->frac[i] < b->frac[i]) {
+ a_ptr = b;
+ b_ptr = a;
+ goto end_if;
+ }
+ }
+ if(na > nb) {
+ a_ptr = a;
+ b_ptr = b;
+ goto end_if;
+ } else if(na < nb) {
+ a_ptr = b;
+ b_ptr = a;
+ goto end_if;
+ }
+ /* |a| == |b| */
+ if(VpGetSign(a) + sw *VpGetSign(b) == 0) {
+ VpSetZero(c,1); /* abs(a)=abs(b) and operation = '-' */
+ return c->Prec*BASE_FIG;
+ }
+ a_ptr = a;
+ b_ptr = b;
+ }
+
+end_if:
+ isw = VpGetSign(a) + sw *VpGetSign(b);
+ /*
+ * isw = 0 ...( 1)+(-1),( 1)-( 1),(-1)+(1),(-1)-(-1)
+ * = 2 ...( 1)+( 1),( 1)-(-1)
+ * =-2 ...(-1)+(-1),(-1)-( 1)
+ * If isw==0, then c =(Sign a_ptr)(|a_ptr|-|b_ptr|)
+ * else c =(Sign of isw)(|a_ptr|+|b_ptr|)
+ */
+ if(isw) { /* addition */
+ VpSetSign(c,(S_INT)1);
+ VpAddAbs(a_ptr, b_ptr, c);
+ VpSetSign(c,isw / 2);
+ } else { /* subtraction */
+ VpSetSign(c,(S_INT)1);
+ VpSubAbs(a_ptr, b_ptr, c);
+ if(a_ptr == a) {
+ VpSetSign(c,VpGetSign(a));
+ } else {
+ VpSetSign(c,VpGetSign(a_ptr) * sw);
+ }
+ }
+
+#ifdef _DEBUG
+ if(gfDebug) {
+ VPrint(stdout, "VpAddSub(result) c=% \n", c);
+ VPrint(stdout, " a=% \n", a);
+ VPrint(stdout, " b=% \n", b);
+ printf(" operation=%d\n", operation);
+ }
+#endif /* _DEBUG */
+ return c->Prec*BASE_FIG;
+}
+
+/*
+ * Addition of two variable precisional variables
+ * a and b assuming abs(a)>abs(b).
+ * c = abs(a) + abs(b) ; where |a|>=|b|
+ */
+static int
+VpAddAbs(Real *a, Real *b, Real *c)
+{
+ U_LONG word_shift;
+ U_LONG carry;
+ U_LONG ap;
+ U_LONG bp;
+ U_LONG cp;
+ U_LONG a_pos;
+ U_LONG b_pos;
+ U_LONG c_pos;
+ U_LONG av, bv, mrv;
+
+#ifdef _DEBUG
+ if(gfDebug) {
+ VPrint(stdout, "VpAddAbs called: a = %\n", a);
+ VPrint(stdout, " b = %\n", b);
+ }
+#endif /* _DEBUG */
+
+ word_shift = VpSetPTR(a, b, c, &ap, &bp, &cp, &av, &bv);
+ a_pos = ap;
+ b_pos = bp;
+ c_pos = cp;
+ if(word_shift==-1L) return 0; /* Overflow */
+ if(b_pos == -1L) goto Assign_a;
+
+ mrv = av + bv; /* Most right val. Used for round. */
+
+ /* Just assign the last few digits of b to c because a has no */
+ /* corresponding digits to be added. */
+ while(b_pos + word_shift > a_pos) {
+ --c_pos;
+ if(b_pos > 0) {
+ --b_pos;
+ c->frac[c_pos] = b->frac[b_pos];
+ } else {
+ --word_shift;
+ c->frac[c_pos] = 0;
+ }
+ }
+
+ /* Just assign the last few digits of a to c because b has no */
+ /* corresponding digits to be added. */
+ bv = b_pos + word_shift;
+ while(a_pos > bv) {
+ --c_pos;
+ --a_pos;
+ c->frac[c_pos] = a->frac[a_pos];
+ }
+ carry = 0; /* set first carry be zero */
+
+ /* Now perform addition until every digits of b will be */
+ /* exhausted. */
+ while(b_pos > 0) {
+ --a_pos;
+ --b_pos;
+ --c_pos;
+ c->frac[c_pos] = a->frac[a_pos] + b->frac[b_pos] + carry;
+ if(c->frac[c_pos] >= BASE) {
+ c->frac[c_pos] -= BASE;
+ carry = 1;
+ } else {
+ carry = 0;
+ }
+ }
+
+ /* Just assign the first few digits of a with considering */
+ /* the carry obtained so far because b has been exhausted. */
+ while(a_pos > 0) {
+ --a_pos;
+ --c_pos;
+ c->frac[c_pos] = a->frac[a_pos] + carry;
+ if(c->frac[c_pos] >= BASE) {
+ c->frac[c_pos] -= BASE;
+ carry = 1;
+ } else {
+ carry = 0;
+ }
+ }
+ if(c_pos) c->frac[c_pos - 1] += carry;
+
+ if(!VpInternalRound(c,0,(c->Prec>0)?a->frac[c->Prec-1]:0,mrv)) VpNmlz(c);
+ goto Exit;
+
+Assign_a:
+ VpAsgn(c, a, 1);
+
+Exit:
+
+#ifdef _DEBUG
+ if(gfDebug) {
+ VPrint(stdout, "VpAddAbs exit: c=% \n", c);
+ }
+#endif /* _DEBUG */
+ return 1;
+}
+
+/*
+ * c = abs(a) - abs(b)
+ */
+static int
+VpSubAbs(Real *a, Real *b, Real *c)
+{
+ U_LONG word_shift;
+ U_LONG mrv;
+ U_LONG borrow;
+ U_LONG ap;
+ U_LONG bp;
+ U_LONG cp;
+ U_LONG a_pos;
+ U_LONG b_pos;
+ U_LONG c_pos;
+ U_LONG av, bv;
+
+#ifdef _DEBUG
+ if(gfDebug) {
+ VPrint(stdout, "VpSubAbs called: a = %\n", a);
+ VPrint(stdout, " b = %\n", b);
+ }
+#endif /* _DEBUG */
+
+ word_shift = VpSetPTR(a, b, c, &ap, &bp, &cp, &av, &bv);
+ a_pos = ap;
+ b_pos = bp;
+ c_pos = cp;
+ if(word_shift==-1L) return 0; /* Overflow */
+ if(b_pos == -1L) goto Assign_a;
+
+ if(av >= bv) {
+ mrv = av - bv;
+ borrow = 0;
+ } else {
+ mrv = 0;
+ borrow = 1;
+ }
+
+ /* Just assign the values which are the BASE subtracted by */
+ /* each of the last few digits of the b because the a has no */
+ /* corresponding digits to be subtracted. */
+ if(b_pos + word_shift > a_pos) {
+ borrow = 1;
+ --c_pos;
+ --b_pos;
+ c->frac[c_pos] = BASE - b->frac[b_pos];
+ while(b_pos + word_shift > a_pos) {
+ --c_pos;
+ if(b_pos > 0) {
+ --b_pos;
+ c->frac[c_pos] = BASE - b->frac[b_pos] - borrow;
+ } else {
+ --word_shift;
+ c->frac[c_pos] = BASE - borrow;
+ }
+ }
+ }
+ /* Just assign the last few digits of a to c because b has no */
+ /* corresponding digits to subtract. */
+
+ bv = b_pos + word_shift;
+ while(a_pos > bv) {
+ --c_pos;
+ --a_pos;
+ c->frac[c_pos] = a->frac[a_pos];
+ }
+
+ /* Now perform subtraction until every digits of b will be */
+ /* exhausted. */
+ while(b_pos > 0) {
+ --a_pos;
+ --b_pos;
+ --c_pos;
+ if(a->frac[a_pos] < b->frac[b_pos] + borrow) {
+ c->frac[c_pos] = BASE + a->frac[a_pos] - b->frac[b_pos] - borrow;
+ borrow = 1;
+ } else {
+ c->frac[c_pos] = a->frac[a_pos] - b->frac[b_pos] - borrow;
+ borrow = 0;
+ }
+ }
+
+ /* Just assign the first few digits of a with considering */
+ /* the borrow obtained so far because b has been exhausted. */
+ while(a_pos > 0) {
+ --c_pos;
+ --a_pos;
+ if(a->frac[a_pos] < borrow) {
+ c->frac[c_pos] = BASE + a->frac[a_pos] - borrow;
+ borrow = 1;
+ } else {
+ c->frac[c_pos] = a->frac[a_pos] - borrow;
+ borrow = 0;
+ }
+ }
+ if(c_pos) c->frac[c_pos - 1] -= borrow;
+
+ if(!VpInternalRound(c,0,(c->Prec>0)?a->frac[c->Prec-1]:0,mrv)) VpNmlz(c);
+ goto Exit;
+
+Assign_a:
+ VpAsgn(c, a, 1);
+
+Exit:
+#ifdef _DEBUG
+ if(gfDebug) {
+ VPrint(stdout, "VpSubAbs exit: c=% \n", c);
+ }
+#endif /* _DEBUG */
+ return 1;
+}
+
+/*
+ * Note: If(av+bv)>= HALF_BASE,then 1 will be added to the least significant
+ * digit of c(In case of addition).
+ * ------------------------- figure of output -----------------------------------
+ * a = xxxxxxxxxxx
+ * b = xxxxxxxxxx
+ * c =xxxxxxxxxxxxxxx
+ * word_shift = | |
+ * right_word = | | (Total digits in RHSV)
+ * left_word = | | (Total digits in LHSV)
+ * a_pos = |
+ * b_pos = |
+ * c_pos = |
+ */
+static U_LONG
+VpSetPTR(Real *a, Real *b, Real *c, U_LONG *a_pos, U_LONG *b_pos, U_LONG *c_pos, U_LONG *av, U_LONG *bv)
+{
+ U_LONG left_word, right_word, word_shift;
+ c->frac[0] = 0;
+ *av = *bv = 0;
+ word_shift =((a->exponent) -(b->exponent));
+ left_word = b->Prec + word_shift;
+ right_word = Max((a->Prec),left_word);
+ left_word =(c->MaxPrec) - 1; /* -1 ... prepare for round up */
+ /*
+ * check if 'round off' is needed.
+ */
+ if(right_word > left_word) { /* round off ? */
+ /*---------------------------------
+ * Actual size of a = xxxxxxAxx
+ * Actual size of b = xxxBxxxxx
+ * Max. size of c = xxxxxx
+ * Round off = |-----|
+ * c_pos = |
+ * right_word = |
+ * a_pos = |
+ */
+ *c_pos = right_word = left_word + 1; /* Set resulting precision */
+ /* be equal to that of c */
+ if((a->Prec) >=(c->MaxPrec)) {
+ /*
+ * a = xxxxxxAxxx
+ * c = xxxxxx
+ * a_pos = |
+ */
+ *a_pos = left_word;
+ *av = a->frac[*a_pos]; /* av is 'A' shown in above. */
+ } else {
+ /*
+ * a = xxxxxxx
+ * c = xxxxxxxxxx
+ * a_pos = |
+ */
+ *a_pos = a->Prec;
+ }
+ if((b->Prec + word_shift) >= c->MaxPrec) {
+ /*
+ * a = xxxxxxxxx
+ * b = xxxxxxxBxxx
+ * c = xxxxxxxxxxx
+ * b_pos = |
+ */
+ if(c->MaxPrec >=(word_shift + 1)) {
+ *b_pos = c->MaxPrec - word_shift - 1;
+ *bv = b->frac[*b_pos];
+ } else {
+ *b_pos = -1L;
+ }
+ } else {
+ /*
+ * a = xxxxxxxxxxxxxxxx
+ * b = xxxxxx
+ * c = xxxxxxxxxxxxx
+ * b_pos = |
+ */
+ *b_pos = b->Prec;
+ }
+ } else { /* The MaxPrec of c - 1 > The Prec of a + b */
+ /*
+ * a = xxxxxxx
+ * b = xxxxxx
+ * c = xxxxxxxxxxx
+ * c_pos = |
+ */
+ *b_pos = b->Prec;
+ *a_pos = a->Prec;
+ *c_pos = right_word + 1;
+ }
+ c->Prec = *c_pos;
+ c->exponent = a->exponent;
+ if(!AddExponent(c,(S_LONG)1)) return (-1L);
+ return word_shift;
+}
+
+/*
+ * Return number og significant digits
+ * c = a * b , Where a = a0a1a2 ... an
+ * b = b0b1b2 ... bm
+ * c = c0c1c2 ... cl
+ * a0 a1 ... an * bm
+ * a0 a1 ... an * bm-1
+ * . . .
+ * . . .
+ * a0 a1 .... an * b0
+ * +_____________________________
+ * c0 c1 c2 ...... cl
+ * nc <---|
+ * MaxAB |--------------------|
+ */
+VP_EXPORT int
+VpMult(Real *c, Real *a, Real *b)
+{
+ U_LONG MxIndA, MxIndB, MxIndAB, MxIndC;
+ U_LONG ind_c, i, nc;
+ U_LONG ind_as, ind_ae, ind_bs, ind_be;
+ U_LONG Carry, s;
+ Real *w;
+
+#ifdef _DEBUG
+ if(gfDebug) {
+ VPrint(stdout, "VpMult(Enter): a=% \n", a);
+ VPrint(stdout, " b=% \n", b);
+ }
+#endif /* _DEBUG */
+
+ if(!VpIsDefOP(c,a,b,3)) return 0; /* No significant digit */
+
+ if(VpIsZero(a) || VpIsZero(b)) {
+ /* at least a or b is zero */
+ VpSetZero(c,VpGetSign(a)*VpGetSign(b));
+ return 1; /* 0: 1 significant digit */
+ }
+
+ if(VpIsOne(a)) {
+ VpAsgn(c, b, VpGetSign(a));
+ goto Exit;
+ }
+ if(VpIsOne(b)) {
+ VpAsgn(c, a, VpGetSign(b));
+ goto Exit;
+ }
+ if((b->Prec) >(a->Prec)) {
+ /* Adjust so that digits(a)>digits(b) */
+ w = a;
+ a = b;
+ b = w;
+ }
+ w = NULL;
+ MxIndA = a->Prec - 1;
+ MxIndB = b->Prec - 1;
+ MxIndC = c->MaxPrec - 1;
+ MxIndAB = a->Prec + b->Prec - 1;
+
+ if(MxIndC < MxIndAB) { /* The Max. prec. of c < Prec(a)+Prec(b) */
+ w = c;
+ c = VpAlloc((U_LONG)((MxIndAB + 1) * BASE_FIG), "#0");
+ MxIndC = MxIndAB;
+ }
+
+ /* set LHSV c info */
+
+ c->exponent = a->exponent; /* set exponent */
+ if(!AddExponent(c,b->exponent)) return 0;
+ VpSetSign(c,VpGetSign(a)*VpGetSign(b)); /* set sign */
+ Carry = 0;
+ nc = ind_c = MxIndAB;
+ memset(c->frac, 0, (nc + 1) * sizeof(U_LONG)); /* Initialize c */
+ c->Prec = nc + 1; /* set precision */
+ for(nc = 0; nc < MxIndAB; ++nc, --ind_c) {
+ if(nc < MxIndB) { /* The left triangle of the Fig. */
+ ind_as = MxIndA - nc;
+ ind_ae = MxIndA;
+ ind_bs = MxIndB;
+ ind_be = MxIndB - nc;
+ } else if(nc <= MxIndA) { /* The middle rectangular of the Fig. */
+ ind_as = MxIndA - nc;
+ ind_ae = MxIndA -(nc - MxIndB);
+ ind_bs = MxIndB;
+ ind_be = 0;
+ } else if(nc > MxIndA) { /* The right triangle of the Fig. */
+ ind_as = 0;
+ ind_ae = MxIndAB - nc - 1;
+ ind_bs = MxIndB -(nc - MxIndA);
+ ind_be = 0;
+ }
+
+ s = 0L;
+ for(i = ind_as; i <= ind_ae; ++i) s +=((a->frac[i]) *(b->frac[ind_bs--]));
+ Carry = s / BASE;
+ s = s -(Carry * BASE);
+
+ c->frac[ind_c] += s;
+ if(c->frac[ind_c] >= BASE) {
+ s = c->frac[ind_c] / BASE;
+ Carry += s;
+ c->frac[ind_c] -=(s * BASE);
+ }
+ i = ind_c;
+ if(Carry) {
+ while((--i) >= 0) {
+ c->frac[i] += Carry;
+ if(c->frac[i] >= BASE) {
+ Carry = c->frac[i] / BASE;
+ c->frac[i] -=(Carry * BASE);
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ VpNmlz(c); /* normalize the result */
+ if(w != NULL) { /* free work variable */
+ VpAsgn(w, c, 1);
+ VpFree(c);
+ c = w;
+ }
+
+Exit:
+#ifdef _DEBUG
+ if(gfDebug) {
+ VPrint(stdout, "VpMult(c=a*b): c=% \n", c);
+ VPrint(stdout, " a=% \n", a);
+ VPrint(stdout, " b=% \n", b);
+ }
+#endif /*_DEBUG */
+ return c->Prec*BASE_FIG;
+}
+
+/*
+ * c = a / b, remainder = r
+ */
+VP_EXPORT int
+VpDivd(Real *c, Real *r, Real *a, Real *b)
+{
+ U_LONG word_a, word_b, word_c, word_r;
+ U_LONG i, n, ind_a, ind_b, ind_c, ind_r;
+ U_LONG nLoop;
+ U_LONG q, b1, b1p1, b1b2, b1b2p1, r1r2;
+ U_LONG borrow, borrow1, borrow2, qb;
+
+#ifdef _DEBUG
+ if(gfDebug) {
+ VPrint(stdout, " VpDivd(c=a/b) a=% \n", a);
+ VPrint(stdout, " b=% \n", b);
+ }
+#endif /*_DEBUG */
+
+ VpSetNaN(r);
+ if(!VpIsDefOP(c,a,b,4)) goto Exit;
+ if(VpIsZero(a)&&VpIsZero(b)) {
+ VpSetNaN(c);
+ return VpException(VP_EXCEPTION_NaN,"(VpDivd) 0/0 not defined(NaN)",0);
+ }
+ if(VpIsZero(b)) {
+ VpSetInf(c,VpGetSign(a)*VpGetSign(b));
+ return VpException(VP_EXCEPTION_ZERODIVIDE,"(VpDivd) Divide by zero",0);
+ }
+ if(VpIsZero(a)) {
+ /* numerator a is zero */
+ VpSetZero(c,VpGetSign(a)*VpGetSign(b));
+ VpSetZero(r,VpGetSign(a)*VpGetSign(b));
+ goto Exit;
+ }
+ if(VpIsOne(b)) {
+ /* divide by one */
+ VpAsgn(c, a, VpGetSign(b));
+ VpSetZero(r,VpGetSign(a));
+ goto Exit;
+ }
+
+ word_a = a->Prec;
+ word_b = b->Prec;
+ word_c = c->MaxPrec;
+ word_r = r->MaxPrec;
+
+ ind_c = 0;
+ ind_r = 1;
+
+ if(word_a >= word_r) goto space_error;
+
+ r->frac[0] = 0;
+ while(ind_r <= word_a) {
+ r->frac[ind_r] = a->frac[ind_r - 1];
+ ++ind_r;
+ }
+
+ while(ind_r < word_r) r->frac[ind_r++] = 0;
+ while(ind_c < word_c) c->frac[ind_c++] = 0;
+
+ /* initial procedure */
+ b1 = b1p1 = b->frac[0];
+ if(b->Prec <= 1) {
+ b1b2p1 = b1b2 = b1p1 * BASE;
+ } else {
+ b1p1 = b1 + 1;
+ b1b2p1 = b1b2 = b1 * BASE + b->frac[1];
+ if(b->Prec > 2) ++b1b2p1;
+ }
+
+ /* */
+ /* loop start */
+ ind_c = word_r - 1;
+ nLoop = Min(word_c,ind_c);
+ ind_c = 1;
+ while(ind_c < nLoop) {
+ if(r->frac[ind_c] == 0) {
+ ++ind_c;
+ continue;
+ }
+ r1r2 = r->frac[ind_c] * BASE + r->frac[ind_c + 1];
+ if(r1r2 == b1b2) {
+ /* The first two word digits is the same */
+ ind_b = 2;
+ ind_a = ind_c + 2;
+ while(ind_b < word_b) {
+ if(r->frac[ind_a] < b->frac[ind_b]) goto div_b1p1;
+ if(r->frac[ind_a] > b->frac[ind_b]) break;
+ ++ind_a;
+ ++ind_b;
+ }
+ /* The first few word digits of r and b is the same and */
+ /* the first different word digit of w is greater than that */
+ /* of b, so quotinet is 1 and just subtract b from r. */
+ borrow = 0; /* quotient=1, then just r-b */
+ ind_b = b->Prec - 1;
+ ind_r = ind_c + ind_b;
+ if(ind_r >= word_r) goto space_error;
+ n = ind_b;
+ for(i = 0; i <= n; ++i) {
+ if(r->frac[ind_r] < b->frac[ind_b] + borrow) {
+ r->frac[ind_r] +=(BASE -(b->frac[ind_b] + borrow));
+ borrow = 1;
+ } else {
+ r->frac[ind_r] = r->frac[ind_r] - b->frac[ind_b] - borrow;
+ borrow = 0;
+ }
+ --ind_r;
+ --ind_b;
+ }
+ ++(c->frac[ind_c]);
+ goto carry;
+ }
+ /* The first two word digits is not the same, */
+ /* then compare magnitude, and divide actually. */
+ if(r1r2 >= b1b2p1) {
+ q = r1r2 / b1b2p1;
+ c->frac[ind_c] += q;
+ ind_r = b->Prec + ind_c - 1;
+ goto sub_mult;
+ }
+
+div_b1p1:
+ if(ind_c + 1 >= word_c) goto out_side;
+ q = r1r2 / b1p1;
+ c->frac[ind_c + 1] += q;
+ ind_r = b->Prec + ind_c;
+
+sub_mult:
+ borrow1 = borrow2 = 0;
+ ind_b = word_b - 1;
+ if(ind_r >= word_r) goto space_error;
+ n = ind_b;
+ for(i = 0; i <= n; ++i) {
+ /* now, perform r = r - q * b */
+ qb = q *(b->frac[ind_b]);
+ if(qb < BASE) borrow1 = 0;
+ else {
+ borrow1 = qb / BASE;
+ qb = qb - borrow1 * BASE;
+ }
+ if(r->frac[ind_r] < qb) {
+ r->frac[ind_r] +=(BASE - qb);
+ borrow2 = borrow2 + borrow1 + 1;
+ } else {
+ r->frac[ind_r] -= qb;
+ borrow2 += borrow1;
+ }
+ if(borrow2) {
+ if(r->frac[ind_r - 1] < borrow2) {
+ r->frac[ind_r - 1] +=(BASE - borrow2);
+ borrow2 = 1;
+ } else {
+ r->frac[ind_r - 1] -= borrow2;
+ borrow2 = 0;
+ }
+ }
+ --ind_r;
+ --ind_b;
+ }
+
+ r->frac[ind_r] -= borrow2;
+carry:
+ ind_r = ind_c;
+ while(c->frac[ind_r] >= BASE) {
+ c->frac[ind_r] -= BASE;
+ --ind_r;
+ ++(c->frac[ind_r]);
+ }
+ }
+ /* End of operation, now final arrangement */
+out_side:
+ c->Prec = word_c;
+ c->exponent = a->exponent;
+ if(!AddExponent(c,(S_LONG)2)) return 0;
+ if(!AddExponent(c,-(b->exponent))) return 0;
+
+ VpSetSign(c,VpGetSign(a)*VpGetSign(b));
+ VpNmlz(c); /* normalize c */
+ r->Prec = word_r;
+ r->exponent = a->exponent;
+ if(!AddExponent(r,(S_LONG)1)) return 0;
+ VpSetSign(r,VpGetSign(a));
+ VpNmlz(r); /* normalize r(remainder) */
+ goto Exit;
+
+space_error:
+#ifdef _DEBUG
+ if(gfDebug) {
+ printf(" word_a=%lu\n", word_a);
+ printf(" word_b=%lu\n", word_b);
+ printf(" word_c=%lu\n", word_c);
+ printf(" word_r=%lu\n", word_r);
+ printf(" ind_r =%lu\n", ind_r);
+ }
+#endif /* _DEBUG */
+ rb_bug("ERROR(VpDivd): space for remainder too small.");
+
+Exit:
+#ifdef _DEBUG
+ if(gfDebug) {
+ VPrint(stdout, " VpDivd(c=a/b), c=% \n", c);
+ VPrint(stdout, " r=% \n", r);
+ }
+#endif /* _DEBUG */
+ return c->Prec*BASE_FIG;
+}
+
+/*
+ * Input a = 00000xxxxxxxx En(5 preceeding zeros)
+ * Output a = xxxxxxxx En-5
+ */
+static int
+VpNmlz(Real *a)
+{
+ U_LONG ind_a, i, j;
+
+ if(VpIsZero(a)) {
+ VpSetZero(a,VpGetSign(a));
+ return 1;
+ }
+ ind_a = a->Prec;
+ while(ind_a--) {
+ if(a->frac[ind_a]) {
+ a->Prec = ind_a + 1;
+ i = j = 0;
+ while(a->frac[i] == 0) ++i; /* skip the first few zeros */
+ if(i) {
+ a->Prec -= i;
+ if(!AddExponent(a,-((S_INT)i))) return 0;
+ while(i <= ind_a) {
+ a->frac[j] = a->frac[i];
+ ++i;
+ ++j;
+ }
+ }
+#ifdef _DEBUG
+ if(gfCheckVal) VpVarCheck(a);
+#endif /* _DEBUG */
+ return 1;
+ }
+ }
+ /* a is zero(no non-zero digit) */
+ VpSetZero(a,VpGetSign(a));
+ return 1;
+}
+
+/*
+ * VpComp = 0 ... if a=b,
+ * Pos ... a>b,
+ * Neg ... a<b.
+ * 999 ... result undefined(NaN)
+ */
+VP_EXPORT int
+VpComp(Real *a, Real *b)
+{
+ int val;
+ U_LONG mx, ind;
+ int e;
+ val = 0;
+ if(VpIsNaN(a)||VpIsNaN(b)) return 999;
+ if(!VpIsDef(a)) {
+ if(!VpIsDef(b)) e = a->sign - b->sign;
+ else e = a->sign;
+ if(e>0) return 1;
+ else if(e<0) return -1;
+ else return 0;
+ }
+ if(!VpIsDef(b)) {
+ e = -b->sign;
+ if(e>0) return 1;
+ else return -1;
+ }
+ /* Zero check */
+ if(VpIsZero(a)) {
+ if(VpIsZero(b)) return 0; /* both zero */
+ val = -VpGetSign(b);
+ goto Exit;
+ }
+ if(VpIsZero(b)) {
+ val = VpGetSign(a);
+ goto Exit;
+ }
+
+ /* compare sign */
+ if(VpGetSign(a) > VpGetSign(b)) {
+ val = 1; /* a>b */
+ goto Exit;
+ }
+ if(VpGetSign(a) < VpGetSign(b)) {
+ val = -1; /* a<b */
+ goto Exit;
+ }
+
+ /* a and b have same sign, && signe!=0,then compare exponent */
+ if((a->exponent) >(b->exponent)) {
+ val = VpGetSign(a);
+ goto Exit;
+ }
+ if((a->exponent) <(b->exponent)) {
+ val = -VpGetSign(b);
+ goto Exit;
+ }
+
+ /* a and b have same exponent, then compare significand. */
+ mx =((a->Prec) <(b->Prec)) ?(a->Prec) :(b->Prec);
+ ind = 0;
+ while(ind < mx) {
+ if((a->frac[ind]) >(b->frac[ind])) {
+ val = VpGetSign(a);
+ goto Exit;
+ }
+ if((a->frac[ind]) <(b->frac[ind])) {
+ val = -VpGetSign(b);
+ goto Exit;
+ }
+ ++ind;
+ }
+ if((a->Prec) >(b->Prec)) {
+ val = VpGetSign(a);
+ } else if((a->Prec) <(b->Prec)) {
+ val = -VpGetSign(b);
+ }
+
+Exit:
+ if (val> 1) val = 1;
+ else if(val<-1) val = -1;
+
+#ifdef _DEBUG
+ if(gfDebug) {
+ VPrint(stdout, " VpComp a=%\n", a);
+ VPrint(stdout, " b=%\n", b);
+ printf(" ans=%d\n", val);
+ }
+#endif /* _DEBUG */
+ return (int)val;
+}
+
+#ifdef _DEBUG
+/*
+ * cntl_chr ... ASCIIZ Character, print control characters
+ * Available control codes:
+ * % ... VP variable. To print '%', use '%%'.
+ * \n ... new line
+ * \b ... backspace
+ * ... tab
+ * Note: % must must not appear more than once
+ * a ... VP variable to be printed
+ */
+VP_EXPORT int
+VPrint(FILE *fp, char *cntl_chr, Real *a)
+{
+ U_LONG i, j, nc, nd, ZeroSup;
+ U_LONG n, m, e, nn;
+
+ /* Check if NaN & Inf. */
+ if(VpIsNaN(a)) {
+ fprintf(fp,SZ_NaN);
+ return 8;
+ }
+ if(VpIsPosInf(a)) {
+ fprintf(fp,SZ_INF);
+ return 8;
+ }
+ if(VpIsNegInf(a)) {
+ fprintf(fp,SZ_NINF);
+ return 9;
+ }
+ if(VpIsZero(a)) {
+ fprintf(fp,"0.0");
+ return 3;
+ }
+
+ j = 0;
+ nd = nc = 0; /* nd : number of digits in fraction part(every 10 digits, */
+ /* nd<=10). */
+ /* nc : number of caracters printed */
+ ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */
+ while(*(cntl_chr + j)) {
+ if((*(cntl_chr + j) == '%') &&(*(cntl_chr + j + 1) != '%')) {
+ nc = 0;
+ if(!VpIsZero(a)) {
+ if(VpGetSign(a) < 0) {
+ fprintf(fp, "-");
+ ++nc;
+ }
+ nc += fprintf(fp, "0.");
+ n = a->Prec;
+ for(i=0;i < n;++i) {
+ m = BASE1;
+ e = a->frac[i];
+ while(m) {
+ nn = e / m;
+ if((!ZeroSup) || nn) {
+ nc += fprintf(fp, "%lu", nn); /* The reading zero(s) */
+ /* as 0.00xx will not */
+ /* be printed. */
+ ++nd;
+ ZeroSup = 0; /* Set to print succeeding zeros */
+ }
+ if(nd >= 10) { /* print ' ' after every 10 digits */
+ nd = 0;
+ nc += fprintf(fp, " ");
+ }
+ e = e - nn * m;
+ m /= 10;
+ }
+ }
+ nc += fprintf(fp, "E%ld", VpExponent10(a));
+ } else {
+ nc += fprintf(fp, "0.0");
+ }
+ } else {
+ ++nc;
+ if(*(cntl_chr + j) == '\\') {
+ switch(*(cntl_chr + j + 1)) {
+ case 'n':
+ fprintf(fp, "\n");
+ ++j;
+ break;
+ case 't':
+ fprintf(fp, "\t");
+ ++j;
+ break;
+ case 'b':
+ fprintf(fp, "\n");
+ ++j;
+ break;
+ default:
+ fprintf(fp, "%c", *(cntl_chr + j));
+ break;
+ }
+ } else {
+ fprintf(fp, "%c", *(cntl_chr + j));
+ if(*(cntl_chr + j) == '%') ++j;
+ }
+ }
+ j++;
+ }
+ return (int)nc;
+}
+#endif /* _DEBUG */
+
+static void
+VpFormatSt(char *psz,S_INT fFmt)
+{
+ U_LONG ie;
+ U_LONG i;
+ S_INT nf = 0;
+ char ch;
+ int fDot = 0;
+
+ ie = strlen(psz);
+ for(i = 0; i < ie; ++i) {
+ ch = psz[i];
+ if(!ch) break;
+ if(ch == '.') {
+ nf = 0;
+ fDot = 1;
+ continue;
+ }
+ if(!fDot) continue;
+ if(ch == 'E') break;
+ nf++;
+ if(nf > fFmt) {
+ memmove(psz + i + 1, psz + i, ie - i + 1);
+ ++ie;
+ nf = 0;
+ psz[i] = ' ';
+ }
+ }
+}
+
+VP_EXPORT S_LONG
+VpExponent10(Real *a)
+{
+ S_LONG ex;
+ U_LONG n;
+
+ if(!VpIsDef(a)) return 0;
+ if(VpIsZero(a)) return 0;
+
+ ex =(a->exponent) * BASE_FIG;
+ n = BASE1;
+ while((a->frac[0] / n) == 0) {
+ --ex;
+ n /= 10;
+ }
+ return ex;
+}
+
+VP_EXPORT void
+VpSzMantissa(Real *a,char *psz)
+{
+ U_LONG i, ZeroSup;
+ U_LONG n, m, e, nn;
+
+ if(VpIsNaN(a)) {
+ sprintf(psz,SZ_NaN);
+ return;
+ }
+ if(VpIsPosInf(a)) {
+ sprintf(psz,SZ_INF);
+ return;
+ }
+ if(VpIsNegInf(a)) {
+ sprintf(psz,SZ_NINF);
+ return;
+ }
+
+ ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */
+ if(!VpIsZero(a)) {
+ if(VpGetSign(a) < 0) *psz++ = '-';
+ n = a->Prec;
+ for(i=0;i < n;++i) {
+ m = BASE1;
+ e = a->frac[i];
+ while(m) {
+ nn = e / m;
+ if((!ZeroSup) || nn) {
+ sprintf(psz, "%lu", nn); /* The reading zero(s) */
+ psz += strlen(psz);
+ /* as 0.00xx will be ignored. */
+ ZeroSup = 0; /* Set to print succeeding zeros */
+ }
+ e = e - nn * m;
+ m /= 10;
+ }
+ }
+ *psz = 0;
+ } else {
+ if(VpIsPosZero(a)) sprintf(psz, "0");
+ else sprintf(psz, "-0");
+ }
+}
+
+VP_EXPORT void
+VpToString(Real *a,char *psz,int fFmt)
+{
+ U_LONG i, ZeroSup;
+ U_LONG n, m, e, nn;
+ char *pszSav = psz;
+ S_LONG ex;
+
+ if(VpIsNaN(a)) {
+ sprintf(psz,SZ_NaN);
+ return;
+ }
+ if(VpIsPosInf(a)) {
+ sprintf(psz,SZ_INF);
+ return;
+ }
+ if(VpIsNegInf(a)) {
+ sprintf(psz,SZ_NINF);
+ return;
+ }
+
+ ZeroSup = 1; /* Flag not to print the leading zeros as 0.00xxxxEnn */
+ if(!VpIsZero(a)) {
+ if(VpGetSign(a) < 0) *psz++ = '-';
+ *psz++ = '0';
+ *psz++ = '.';
+ n = a->Prec;
+ for(i=0;i < n;++i) {
+ m = BASE1;
+ e = a->frac[i];
+ while(m) {
+ nn = e / m;
+ if((!ZeroSup) || nn) {
+ sprintf(psz, "%lu", nn); /* The reading zero(s) */
+ psz += strlen(psz);
+ /* as 0.00xx will be ignored. */
+ ZeroSup = 0; /* Set to print succeeding zeros */
+ }
+ e = e - nn * m;
+ m /= 10;
+ }
+ }
+ ex =(a->exponent) * BASE_FIG;
+ n = BASE1;
+ while((a->frac[0] / n) == 0) {
+ --ex;
+ n /= 10;
+ }
+ sprintf(psz, "E%ld", ex);
+ } else {
+ if(VpIsPosZero(a)) sprintf(psz, "0.0");
+ else sprintf(psz, "-0.0");
+ }
+ if(fFmt) VpFormatSt(pszSav, fFmt);
+}
+
+/*
+ * [Output]
+ * a[] ... variable to be assigned the value.
+ * [Input]
+ * int_chr[] ... integer part(may include '+/-').
+ * ni ... number of characters in int_chr[],not including '+/-'.
+ * frac[] ... fraction part.
+ * nf ... number of characters in frac[].
+ * exp_chr[] ... exponent part(including '+/-').
+ * ne ... number of characters in exp_chr[],not including '+/-'.
+ */
+VP_EXPORT int
+VpCtoV(Real *a, char *int_chr, U_LONG ni, char *frac, U_LONG nf, char *exp_chr, U_LONG ne)
+{
+ U_LONG i, j, ind_a, ma, mi, me;
+ U_LONG loc;
+ S_INT e,es, eb, ef;
+ S_INT sign, signe;
+ /* get exponent part */
+ e = 0;
+ ma = a->MaxPrec;
+ mi = ni;
+ me = ne;
+ signe = 1;
+ memset(a->frac, 0, ma * sizeof(U_LONG));
+ if(ne > 0) {
+ i = 0;
+ if(exp_chr[0] == '-') {
+ signe = -1;
+ ++i;
+ ++me;
+ } else if(exp_chr[0] == '+') {
+ ++i;
+ ++me;
+ }
+ while(i < me) {
+ es = e*((S_INT)BASE_FIG);
+ e = e * 10 + exp_chr[i] - '0';
+ if(es>e*((S_INT)BASE_FIG)) {
+ return VpException(VP_EXCEPTION_INFINITY,"Exponent overflow",0);
+ }
+ ++i;
+ }
+ }
+
+ /* get integer part */
+ i = 0;
+ sign = 1;
+ if(ni > 0) {
+ if(int_chr[0] == '-') {
+ sign = -1;
+ ++i;
+ ++mi;
+ } else if(int_chr[0] == '+') {
+ ++i;
+ ++mi;
+ }
+ }
+
+ e = signe * e; /* e: The value of exponent part. */
+ e = e + ni; /* set actual exponent size. */
+
+ if(e > 0) signe = 1;
+ else signe = -1;
+
+ /* Adjust the exponent so that it is the multiple of BASE_FIG. */
+ j = 0;
+ ef = 1;
+ while(ef) {
+ if(e>=0) eb = e;
+ else eb = -e;
+ ef = eb / ((S_INT)BASE_FIG);
+ ef = eb - ef * ((S_INT)BASE_FIG);
+ if(ef) {
+ ++j; /* Means to add one more preceeding zero */
+ ++e;
+ }
+ }
+
+ eb = e / ((S_INT)BASE_FIG);
+
+ ind_a = 0;
+ while(i < mi) {
+ a->frac[ind_a] = 0;
+ while((j < (U_LONG)BASE_FIG) &&(i < mi)) {
+ a->frac[ind_a] = a->frac[ind_a] * 10 + int_chr[i] - '0';
+ ++j;
+ ++i;
+ }
+ if(i < mi) {
+ ++ind_a;
+ if(ind_a >= ma) goto over_flow;
+ j = 0;
+ }
+ }
+ loc = 1;
+
+ /* get fraction part */
+
+ i = 0;
+ while(i < nf) {
+ while((j < (U_LONG)BASE_FIG) &&(i < nf)) {
+ a->frac[ind_a] = a->frac[ind_a] * 10 + frac[i] - '0';
+ ++j;
+ ++i;
+ }
+ if(i < nf) {
+ ++ind_a;
+ if(ind_a >= ma) goto over_flow;
+ j = 0;
+ }
+ }
+ goto Final;
+
+over_flow:
+ rb_warn("Conversion from String to BigDecimal overflow (last few digits discarded).");
+
+Final:
+ if(ind_a >= ma) ind_a = ma - 1;
+ while(j < (U_LONG)BASE_FIG) {
+ a->frac[ind_a] = a->frac[ind_a] * 10;
+ ++j;
+ }
+ a->Prec = ind_a + 1;
+ a->exponent = eb;
+ VpSetSign(a,sign);
+ VpNmlz(a);
+ return 1;
+}
+
+/*
+ * [Input]
+ * *m ... Real
+ * [Output]
+ * *d ... fraction part of m(d = 0.xxxxxxx). where # of 'x's is fig.
+ * *e ... U_LONG,exponent of m.
+ * DBLE_FIG ... Number of digits in a double variable.
+ *
+ * m -> d*10**e, 0<d<BASE
+ * [Returns]
+ * 0 ... Zero
+ * 1 ... Normal
+ * 2 ... Infinity
+ * -1 ... NaN
+ */
+VP_EXPORT int
+VpVtoD(double *d, S_LONG *e, Real *m)
+{
+ U_LONG ind_m, mm, fig;
+ double div;
+ int f = 1;
+
+ if(VpIsNaN(m)) {
+ *d = VpGetDoubleNaN();
+ *e = 0;
+ f = -1; /* NaN */
+ goto Exit;
+ } else
+ if(VpIsPosZero(m)) {
+ *d = 0.0;
+ *e = 0;
+ f = 0;
+ goto Exit;
+ } else
+ if(VpIsNegZero(m)) {
+ *d = VpGetDoubleNegZero();
+ *e = 0;
+ f = 0;
+ goto Exit;
+ } else
+ if(VpIsPosInf(m)) {
+ *d = VpGetDoublePosInf();
+ *e = 0;
+ f = 2;
+ goto Exit;
+ } else
+ if(VpIsNegInf(m)) {
+ *d = VpGetDoubleNegInf();
+ *e = 0;
+ f = 2;
+ goto Exit;
+ }
+ /* Normal number */
+ fig =(DBLE_FIG + BASE_FIG - 1) / BASE_FIG;
+ ind_m = 0;
+ mm = Min(fig,(m->Prec));
+ *d = 0.0;
+ div = 1.;
+ while(ind_m < mm) {
+ div /=(double)((S_INT)BASE);
+ *d = *d +((double) ((S_INT)m->frac[ind_m++])) * div;
+ }
+ *e = m->exponent * ((S_INT)BASE_FIG);
+ *d *= VpGetSign(m);
+
+Exit:
+#ifdef _DEBUG
+ if(gfDebug) {
+ VPrint(stdout, " VpVtoD: m=%\n", m);
+ printf(" d=%e * 10 **%ld\n", *d, *e);
+ printf(" DBLE_FIG = %ld\n", DBLE_FIG);
+ }
+#endif /*_DEBUG */
+ return f;
+}
+
+/*
+ * m <- d
+ */
+VP_EXPORT void
+VpDtoV(Real *m, double d)
+{
+ U_LONG i, ind_m, mm;
+ U_LONG ne;
+ double val, val2;
+
+ if(isnan(d)) {
+ VpSetNaN(m);
+ goto Exit;
+ }
+ if(isinf(d)) {
+ if(d>0.0) VpSetPosInf(m);
+ else VpSetNegInf(m);
+ goto Exit;
+ }
+
+ if(d == 0.0) {
+ VpSetZero(m,1);
+ goto Exit;
+ }
+ val =(d > 0.) ? d :(-d);
+ ne = 0;
+ if(val >= 1.0) {
+ while(val >= 1.0) {
+ val /=(double)((S_INT)BASE);
+ ++ne;
+ }
+ } else {
+ val2 = 1.0 /(double)((S_INT)BASE);
+ while(val < val2) {
+ val *=(double)((S_INT)BASE);
+ --ne;
+ }
+ }
+ /* Now val = 0.xxxxx*BASE**ne */
+
+ mm = m->MaxPrec;
+ memset(m->frac, 0, mm * sizeof(U_LONG));
+ for(ind_m = 0;val > 0.0 && ind_m < mm;ind_m++) {
+ val *=(double)((S_INT)BASE);
+ i =(U_LONG) val;
+ val -=(double)((S_INT)i);
+ m->frac[ind_m] = i;
+ }
+ if(ind_m >= mm) ind_m = mm - 1;
+ if(d > 0.0) {
+ VpSetSign(m, (S_INT)1);
+ } else {
+ VpSetSign(m,-(S_INT)1);
+ }
+ m->Prec = ind_m + 1;
+ m->exponent = ne;
+
+ if(!VpInternalRound(m,0,(m->Prec>0)?m->frac[m->Prec-1]:0,
+ (U_LONG)(val*((double)((S_INT)BASE))))) VpNmlz(m);
+
+Exit:
+#ifdef _DEBUG
+ if(gfDebug) {
+ printf("VpDtoV d=%30.30e\n", d);
+ VPrint(stdout, " m=%\n", m);
+ }
+#endif /* _DEBUG */
+ return;
+}
+
+/*
+ * m <- ival
+ */
+VP_EXPORT void
+VpItoV(Real *m, S_INT ival)
+{
+ U_LONG mm, ind_m;
+ U_LONG val, v1, v2, v;
+ int isign;
+ S_INT ne;
+
+ if(ival == 0) {
+ VpSetZero(m,1);
+ goto Exit;
+ }
+ isign = 1;
+ val = ival;
+ if(ival < 0) {
+ isign = -1;
+ val =(U_LONG)(-ival);
+ }
+ ne = 0;
+ ind_m = 0;
+ mm = m->MaxPrec;
+ while(ind_m < mm) {
+ m->frac[ind_m] = 0;
+ ++ind_m;
+ }
+ ind_m = 0;
+ while(val > 0) {
+ if(val) {
+ v1 = val;
+ v2 = 1;
+ while(v1 >= BASE) {
+ v1 /= BASE;
+ v2 *= BASE;
+ }
+ val = val - v2 * v1;
+ v = v1;
+ } else {
+ v = 0;
+ }
+ m->frac[ind_m] = v;
+ ++ind_m;
+ ++ne;
+ }
+ m->Prec = ind_m - 1;
+ m->exponent = ne;
+ VpSetSign(m,isign);
+ VpNmlz(m);
+
+Exit:
+#ifdef _DEBUG
+ if(gfDebug) {
+ printf(" VpItoV i=%d\n", ival);
+ VPrint(stdout, " m=%\n", m);
+ }
+#endif /* _DEBUG */
+ return;
+}
+
+/*
+ * y = SQRT(x), y*y - x =>0
+ */
+VP_EXPORT int
+VpSqrt(Real *y, Real *x)
+{
+ Real *f = NULL;
+ Real *r = NULL;
+ S_LONG y_prec, f_prec;
+ S_LONG n;
+ S_LONG e;
+ S_LONG prec;
+ S_LONG nr;
+ double val;
+
+ if(!VpIsDef(x)) {
+ VpAsgn(y,x,1);
+ goto Exit;
+ }
+
+ if(VpIsZero(x)) {
+ VpSetZero(y,VpGetSign(x));
+ goto Exit;
+ }
+
+ if(VpGetSign(x) < 0) {
+ VpSetZero(y,VpGetSign(x));
+ return VpException(VP_EXCEPTION_OP,"(VpSqrt) SQRT(negative valuw)",0);
+ }
+
+ n = (S_LONG)y->MaxPrec;
+ if((S_LONG)x->MaxPrec > n) n = (S_LONG)x->MaxPrec;
+ /* allocate temporally variables */
+ f = VpAlloc(y->MaxPrec *(BASE_FIG + 2), "#1");
+ r = VpAlloc((n + n) *(BASE_FIG + 2), "#1");
+
+ nr = 0;
+ y_prec = (S_LONG)y->MaxPrec;
+ f_prec = (S_LONG)f->MaxPrec;
+
+ VpAsgn(y, x, 1); /* assign initial guess. y <= x */
+ prec = x->exponent;
+ if(prec > 0) ++prec;
+ else --prec;
+ prec = prec / 2 - (S_LONG)y->MaxPrec;
+ /*
+ * y = 0.yyyy yyyy yyyy YYYY
+ * BASE_FIG = | |
+ * prec =(0.YYYY*BASE-4)
+ */
+ VpVtoD(&val, &e, y); /* val <- y */
+ e /= ((S_LONG)BASE_FIG);
+ n = e / 2;
+ if(e - n * 2 != 0) {
+ val /=(double)((S_INT)BASE);
+ n =(e + 1) / 2;
+ }
+ VpDtoV(y, sqrt(val)); /* y <- sqrt(val) */
+ y->exponent += n;
+ n = (DBLE_FIG + BASE_FIG - 1) / BASE_FIG;
+ y->MaxPrec = (U_LONG)Min(n , y_prec);
+ f->MaxPrec = y->MaxPrec + 1;
+ n = y_prec*((S_LONG)BASE_FIG);
+ if((U_LONG)n<maxnr) n = (U_LONG)maxnr;
+ do {
+ y->MaxPrec *= 2;
+ if(y->MaxPrec > (U_LONG)y_prec) y->MaxPrec = (U_LONG)y_prec;
+ f->MaxPrec = y->MaxPrec;
+ VpDivd(f, r, x, y); /* f = x/y */
+ VpAddSub(r, y, f, 1); /* r = y + x/y */
+ VpMult(f, VpPt5, r); /* f = 0.5*r */
+ VpAddSub(r, f, y, -1);
+ if(VpIsZero(r)) goto converge;
+ if(r->exponent <= prec) goto converge;
+ VpAsgn(y, f, 1);
+ } while(++nr < n);
+ /* */
+#ifdef _DEBUG
+ if(gfDebug) {
+ printf("ERROR(VpSqrt): did not converge within %ld iterations.\n",
+ nr);
+ }
+#endif /* _DEBUG */
+ y->MaxPrec = y_prec;
+ goto Exit;
+
+converge:
+ VpChangeSign(y,(S_INT)1);
+#ifdef _DEBUG
+ if(gfDebug) {
+ VpMult(r, y, y);
+ VpAddSub(f, x, r, -1);
+ printf("VpSqrt: iterations = %lu\n", nr);
+ VPrint(stdout, " y =% \n", y);
+ VPrint(stdout, " x =% \n", x);
+ VPrint(stdout, " x-y*y = % \n", f);
+ }
+#endif /* _DEBUG */
+ y->MaxPrec = y_prec;
+
+Exit:
+ VpFree(f);
+ VpFree(r);
+ return 1;
+}
+
+/*
+ *
+ * f = 0: Round off/Truncate, 1: round up, 2:ceil, 3: floor, 4: Banker's rounding
+ * nf: digit position for operation.
+ *
+ */
+VP_EXPORT void
+VpMidRound(Real *y, int f, int nf)
+/*
+ * Round reletively from the decimal point.
+ * f: rounding mode
+ * nf: digit location to round from the the decimal point.
+ */
+{
+ int n,i,ix,ioffset;
+ U_LONG v;
+ U_LONG div;
+
+ nf += y->exponent*((int)BASE_FIG);
+
+ /* ix: x->fraq[ix] contains round position */
+ ix = nf/(int)BASE_FIG;
+ if(ix<0 || ((U_LONG)ix)>=y->Prec) return; /* Unable to round */
+ ioffset = nf - ix*((int)BASE_FIG);
+ memset(y->frac+ix+1, 0, (y->Prec - (ix+1)) * sizeof(U_LONG));
+ /* VpNmlz(y); */
+ v = y->frac[ix];
+ /* drop digits after pointed digit */
+ n = BASE_FIG - ioffset - 1;
+ for(i=0;i<n;++i) v /= 10;
+ div = v/10;
+ v = v - div*10;
+ switch(f) {
+ case VP_ROUND_DOWN: /* Truncate */
+ break;
+ case VP_ROUND_UP: /* Roundup */
+ if(v) ++div;
+ break;
+ case VP_ROUND_HALF_UP: /* Round half up */
+ if(v>=5) ++div;
+ break;
+ case VP_ROUND_HALF_DOWN: /* Round half down */
+ if(v>=6) ++div;
+ break;
+ case VP_ROUND_CEIL: /* ceil */
+ if(v && (VpGetSign(y)>0)) ++div;
+ break;
+ case VP_ROUND_FLOOR: /* floor */
+ if(v && (VpGetSign(y)<0)) ++div;
+ break;
+ case VP_ROUND_HALF_EVEN: /* Banker's rounding */
+ if(v>5) ++div;
+ else if(v==5) {
+ if(i==(BASE_FIG-1)) {
+ if(ix && (y->frac[ix-1]%2)) ++div;
+ } else {
+ if(div%2) ++div;
+ }
+ }
+ break;
+ }
+ for(i=0;i<=n;++i) div *= 10;
+ if(div>=BASE) {
+ y->frac[ix] = 0;
+ if(ix) {
+ VpNmlz(y);
+ VpRdup(y,0);
+ } else {
+ VpSetOne(y);
+ VpSetSign(y,VpGetSign(y));
+ }
+ } else {
+ y->frac[ix] = div;
+ VpNmlz(y);
+ }
+}
+
+VP_EXPORT void
+VpLeftRound(Real *y, int f, int nf)
+/*
+ * Round from the left hand side of the digits.
+ */
+{
+ U_LONG v;
+
+ if(!VpIsDef(y)) return; /* Unable to round */
+ if(VpIsZero(y)) return;
+
+ v = y->frac[0];
+ nf -= VpExponent(y)*BASE_FIG;
+ while(v=v/10) nf--;
+ nf += (BASE_FIG-1);
+ VpMidRound(y,f,nf);
+}
+
+VP_EXPORT void
+VpActiveRound(Real *y, Real *x, int f, int nf)
+{
+ /* First,assign whole value in truncation mode */
+ VpAsgn(y, x, 1); /* 1 round off,2 round up */
+ if(!VpIsDef(y)) return; /* Unable to round */
+ if(VpIsZero(y)) return;
+ VpMidRound(y,f,nf);
+}
+
+static int
+VpInternalRound(Real *c,int ixDigit,U_LONG vPrev,U_LONG v)
+{
+ int f = 0;
+
+ if(!VpIsDef(c)) return f; /* Unable to round */
+ if(VpIsZero(c)) return f;
+
+ v /= BASE1;
+ switch(gfRoundMode) {
+ case VP_ROUND_DOWN:
+ break;
+ case VP_ROUND_UP:
+ if(v) f = 1;
+ break;
+ case VP_ROUND_HALF_UP:
+ if(v >= 5) f = 1;
+ break;
+ case VP_ROUND_HALF_DOWN:
+ if(v >= 6) f = 1;
+ break;
+ case VP_ROUND_CEIL: /* ceil */
+ if(v && (VpGetSign(c)>0)) f = 1;
+ break;
+ case VP_ROUND_FLOOR: /* floor */
+ if(v && (VpGetSign(c)<0)) f = 1;
+ break;
+ case VP_ROUND_HALF_EVEN: /* Banker's rounding */
+ if(v>5) f = 1;
+ else if(v==5 && vPrev%2) f = 1;
+ break;
+ }
+ if(f) VpRdup(c,ixDigit); /* round up */
+ return f;
+}
+
+/*
+ * Rounds up m(plus one to final digit of m).
+ */
+static int
+VpRdup(Real *m,U_LONG ind_m)
+{
+ U_LONG carry;
+
+ if(!ind_m) ind_m = m->Prec;
+
+ carry = 1;
+ while(carry > 0 && (ind_m--)) {
+ m->frac[ind_m] += carry;
+ if(m->frac[ind_m] >= BASE) m->frac[ind_m] -= BASE;
+ else carry = 0;
+ }
+ if(carry > 0) { /* Overflow,count exponent and set fraction part be 1 */
+ if(!AddExponent(m,(S_LONG)1)) return 0;
+ m->Prec = m->frac[0] = 1;
+ } else {
+ VpNmlz(m);
+ }
+ return 1;
+}
+
+/*
+ * y = x - fix(x)
+ */
+VP_EXPORT void
+VpFrac(Real *y, Real *x)
+{
+ U_LONG my, ind_y, ind_x;
+
+ if(!VpIsDef(x) || VpIsZero(x)) {
+ VpAsgn(y,x,1);
+ goto Exit;
+ }
+
+ if(x->exponent > 0 && (U_LONG)x->exponent >= x->Prec) {
+ VpSetZero(y,VpGetSign(x));
+ goto Exit;
+ } else if(x->exponent <= 0) {
+ VpAsgn(y, x, 1);
+ goto Exit;
+ }
+ y->Prec = x->Prec -(U_LONG) x->exponent;
+ y->Prec = Min(y->Prec, y->MaxPrec);
+ y->exponent = 0;
+ VpSetSign(y,VpGetSign(x));
+ ind_y = 0;
+ my = y->Prec;
+ ind_x = x->exponent;
+ while(ind_y <= my) {
+ y->frac[ind_y] = x->frac[ind_x];
+ ++ind_y;
+ ++ind_x;
+ }
+
+Exit:
+#ifdef _DEBUG
+ if(gfDebug) {
+ VPrint(stdout, "VpFrac y=%\n", y);
+ VPrint(stdout, " x=%\n", x);
+ }
+#endif /* _DEBUG */
+ return;
+}
+
+/*
+ * y = x ** n
+ */
+VP_EXPORT int
+VpPower(Real *y, Real *x, S_INT n)
+{
+ U_LONG s, ss;
+ S_LONG sign;
+ Real *w1 = NULL;
+ Real *w2 = NULL;
+
+ if(VpIsZero(x)) {
+ if(n==0) {
+ VpSetOne(y);
+ goto Exit;
+ }
+ sign = VpGetSign(x);
+ if(n<0) {
+ n = -n;
+ if(sign<0) sign = (n%2)?(-1):(1);
+ VpSetInf (y,sign);
+ } else {
+ if(sign<0) sign = (n%2)?(-1):(1);
+ VpSetZero(y,sign);
+ }
+ goto Exit;
+ }
+ if(!VpIsDef(x)) {
+ VpSetNaN(y); /* Not sure !!! */
+ goto Exit;
+ }
+
+ if((x->exponent == 1) &&(x->Prec == 1) &&(x->frac[0] == 1)) {
+ /* abs(x) = 1 */
+ VpSetOne(y);
+ if(VpGetSign(x) > 0) goto Exit;
+ if((n % 2) == 0) goto Exit;
+ VpSetSign(y,-(S_INT)1);
+ goto Exit;
+ }
+
+ if(n > 0) sign = 1;
+ else if(n < 0) {
+ sign = -1;
+ n = -n;
+ } else {
+ VpSetOne(y);
+ goto Exit;
+ }
+
+ /* Allocate working variables */
+
+ w1 = VpAlloc((y->MaxPrec + 2) * BASE_FIG, "#0");
+ w2 = VpAlloc((w1->MaxPrec * 2 + 1) * BASE_FIG, "#0");
+ /* calculation start */
+
+ VpAsgn(y, x, 1);
+ --n;
+ while(n > 0) {
+ VpAsgn(w1, x, 1);
+ s = 1;
+loop1: ss = s;
+ s += s;
+ if(s >(U_LONG) n) goto out_loop1;
+ VpMult(w2, w1, w1);
+ VpAsgn(w1, w2, 1);
+ goto loop1;
+out_loop1:
+ n -= ss;
+ VpMult(w2, y, w1);
+ VpAsgn(y, w2, 1);
+ }
+ if(sign < 0) {
+ VpDivd(w1, w2, VpConstOne, y);
+ VpAsgn(y, w1, 1);
+ }
+
+Exit:
+#ifdef _DEBUG
+ if(gfDebug) {
+ VPrint(stdout, "VpPower y=%\n", y);
+ VPrint(stdout, "VpPower x=%\n", x);
+ printf(" n=%d\n", n);
+ }
+#endif /* _DEBUG */
+ VpFree(w2);
+ VpFree(w1);
+ return 1;
+}
+
+#ifdef ENABLE_TRIAL_METHOD
+/*
+ * Calculates pi(=3.141592653589793238462........).
+ */
+VP_EXPORT void
+VpPi(Real *y)
+{
+ Real *n, *n25, *n956, *n57121;
+ Real *r, *f, *t;
+ U_LONG p;
+ U_LONG nc;
+ U_LONG i1,i2;
+
+ p = y->MaxPrec *(BASE_FIG + 2) + 2;
+ if(p<maxnr) nc = maxnr;
+ else nc = p;
+
+ /* allocate temporally variables */
+ r = VpAlloc(p * 2, "#0");
+ f = VpAlloc(p, "#0");
+ t = VpAlloc(p, "#-80");
+
+ n = VpAlloc((U_LONG)10, "1");
+ n25 = VpAlloc((U_LONG)2, "-0.04"); /*-25");*/
+ n956 = VpAlloc((U_LONG)3, "956");
+ n57121 = VpAlloc((U_LONG)5, "-57121");
+
+ VpSetZero(y,1); /* y = 0 */
+ i1 = 0;
+ do {
+ ++i1;
+ /* VpDivd(f, r, t, n25); */ /* f = t/(-25) */
+ VpMult(f,t,n25);
+ VpAsgn(t, f, 1); /* t = f */
+
+ VpDivd(f, r, t, n); /* f = t/n */
+
+ VpAddSub(r, y, f, 1); /* r = y + f */
+ VpAsgn(y, r, 1); /* y = r */
+
+ VpRdup(n,0); /* n = n + 1 */
+ VpRdup(n,0); /* n = n + 1 */
+ if(VpIsZero(f)) break;
+ } while((f->exponent > 0 || ((U_LONG)(-(f->exponent)) < y->MaxPrec)) &&
+ i1<nc
+ );
+
+ VpSetOne(n);
+ VpAsgn(t, n956,1);
+ i2 = 0;
+ do {
+ ++i2;
+ VpDivd(f, r, t, n57121); /* f = t/(-57121) */
+ VpAsgn(t, f, 1); /* t = f */
+
+ VpDivd(f, r, t, n); /* f = t/n */
+ VpAddSub(r, y, f, 1); /* r = y + f */
+
+ VpAsgn(y, r, 1); /* y = r */
+ VpRdup(n,0); /* n = n + 1 */
+ VpRdup(n,0); /* n = n + 1 */
+ if(VpIsZero(f)) break;
+ } while((f->exponent > 0 || ((U_LONG)(-(f->exponent)) < y->MaxPrec)) &&
+ i2<nc
+ );
+
+ VpFree(n);
+ VpFree(n25);
+ VpFree(n956);
+ VpFree(n57121);
+
+ VpFree(t);
+ VpFree(f);
+ VpFree(r);
+#ifdef _DEBUG
+ printf("VpPi: # of iterations=%lu+%lu\n",i1,i2);
+#endif /* _DEBUG */
+}
+
+/*
+ * Calculates the value of e(=2.18281828459........).
+ * [Output] *y ... Real , the value of e.
+ *
+ * y = e
+ */
+VP_EXPORT void
+VpExp1(Real *y)
+{
+ Real *n, *r, *f, *add;
+ U_LONG p;
+ U_LONG nc;
+ U_LONG i;
+
+ p = y->MaxPrec*(BASE_FIG + 2) + 2;
+ if(p<maxnr) nc = maxnr;
+ else nc = p;
+
+ /* allocate temporally variables */
+
+ r = VpAlloc(p *(BASE_FIG + 2), "#0");
+ f = VpAlloc(p, "#1");
+ n = VpAlloc(p, "#1"); /* n = 1 */
+ add = VpAlloc(p, "#1"); /* add = 1 */
+
+ VpSetOne(y); /* y = 1 */
+ VpRdup(y,0); /* y = y + 1 */
+ i = 0;
+ do {
+ ++i;
+ VpRdup(n,0); /* n = n + 1 */
+ VpDivd(f, r, add, n); /* f = add/n(=1/n!) */
+ VpAsgn(add, f, 1); /* add = 1/n! */
+ VpAddSub(r, y, f, 1);
+ VpAsgn(y, r, 1); /* y = y + 1/n! */
+ } while((f->exponent > 0 || ((U_LONG)(-(f->exponent)) <= y->MaxPrec)) &&
+ i<nc
+ );
+
+#ifdef _DEBUG
+ if(gfDebug) {
+ VPrint(stdout, "vpexp e=%\n", y);
+ printf(" r=%d\n", f[3]);
+ }
+#endif /* _DEBUG */
+ VpFree(add);
+ VpFree(n);
+ VpFree(f);
+ VpFree(r);
+}
+
+/*
+ * Calculates y=e**x where e(=2.18281828459........).
+ */
+VP_EXPORT void
+VpExp(Real *y, Real *x)
+{
+ Real *z=NULL, *div=NULL, *n=NULL, *r=NULL, *c=NULL;
+ U_LONG p;
+ U_LONG nc;
+ U_LONG i;
+ short fNeg=0;
+
+ if(!VpIsDef(x)) {
+ VpSetNaN(y); /* Not sure */
+ goto Exit;
+ }
+ if(VpIsZero(x)) {
+ VpSetOne(y);
+ goto Exit;
+ }
+ p = y->MaxPrec;
+ if(p < x->Prec) p = x->Prec;
+ p = p *(BASE_FIG + 2) + 2;
+ if(p<maxnr) nc = maxnr;
+ else nc = p;
+
+ fNeg = x->sign;
+ if(fNeg<0) x->sign = -fNeg;
+
+ /* allocate temporally variables */
+ z = VpAlloc(p, "#1");
+ div = VpAlloc(p, "#1");
+
+ r = VpAlloc(p * 2, "#0");
+ c = VpAlloc(p, "#0");
+ n = VpAlloc(p, "#1"); /* n = 1 */
+
+ VpSetOne(r); /* y = 1 */
+ VpAddSub(y, r, x, 1); /* y = 1 + x/1 */
+ VpAsgn(z, x, 1); /* z = x/1 */
+
+ i = 0;
+ do {
+ ++i;
+ VpRdup(n,0); /* n = n + 1 */
+ VpDivd(div, r, x, n); /* div = x/n */
+ VpMult(c, z, div); /* c = x/(n-1)! * x/n */
+ VpAsgn(z, c, 1); /* z = x*n/n! */
+ VpAsgn(r, y, 1); /* Save previous val. */
+ VpAddSub(div, y, z, 1); /* */
+ VpAddSub(c, div, r, -1); /* y = y(new) - y(prev) */
+ VpAsgn(y, div, 1); /* y = y(new) */
+ } while(((!VpIsZero(c)) &&(c->exponent >= 0 ||((U_LONG)(-c->exponent)) <= y->MaxPrec)) &&
+ i<nc
+ );
+
+ if(fNeg < 0) {
+ x->sign = fNeg;
+ VpDivd(div, r, VpConstOne, y);
+ VpAsgn(y, div, 1);
+ }
+
+Exit:
+
+#ifdef _DEBUG
+ if(gfDebug) {
+ VPrint(stdout, "vpexp e=%\n", y);
+ }
+#endif /* _DEBUG */
+ VpFree(div);
+ VpFree(n);
+ VpFree(c);
+ VpFree(r);
+ VpFree(z);
+}
+
+VP_EXPORT void
+VpSinCos(Real *psin,Real *pcos,Real *x)
+/*
+ * Calculates sin(x) & cos(x)
+ *(Assumes psin->MaxPrec==pcos->MaxPrec)
+ */
+{
+ Real *z=NULL, *div=NULL, *n=NULL, *r=NULL, *c=NULL;
+ U_LONG p;
+ int fcos;
+ int fsin;
+ int which;
+ U_LONG nc;
+ U_LONG i;
+
+ if(!VpIsDef(x)) {
+ VpSetNaN(psin);
+ VpSetNaN(pcos);
+ goto Exit;
+ }
+
+ p = pcos->MaxPrec;
+ if(p < x->Prec) p = x->Prec;
+ p = p *(BASE_FIG + 2) + 2;
+ if(p<maxnr) nc = maxnr;
+ else nc = p;
+
+ /* allocate temporally variables */
+ z = VpAlloc(p, "#1");
+ div = VpAlloc(p, "#1");
+
+ r = VpAlloc(p * 2, "#0");
+ c = VpAlloc(p , "#0");
+ n = VpAlloc(p, "#1"); /* n = 1 */
+
+ VpSetOne(pcos); /* cos = 1 */
+ VpAsgn(psin, x, 1); /* sin = x/1 */
+ VpAsgn(z, x, 1); /* z = x/1 */
+ fcos = 1;
+ fsin = 1;
+ which = 1;
+ i = 0;
+ do {
+ ++i;
+ VpRdup(n,0); /* n = n + 1 */
+ VpDivd(div, r, x, n); /* div = x/n */
+ VpMult(c, z, div); /* c = x/(n-1)! * x/n */
+ VpAsgn(z, c, 1); /* z = x*n/n! */
+ if(which) {
+ /* COS */
+ which = 0;
+ fcos *= -1;
+ VpAsgn(r, pcos, 1); /* Save previous val. */
+ VpAddSub(div, pcos, z, fcos); /* */
+ VpAddSub(c, div, r, -1); /* cos = cos(new) - cos(prev) */
+ VpAsgn(pcos, div, 1); /* cos = cos(new) */
+ } else {
+ /* SIN */
+ which = 1;
+ fsin *= -1;
+ VpAsgn(r, psin, 1); /* Save previous val. */
+ VpAddSub(div, psin, z, fsin); /* */
+ VpAddSub(c, div, r, -1); /* sin = sin(new) - sin(prev) */
+ VpAsgn(psin, div, 1); /* sin = sin(new) */
+ }
+ } while(((!VpIsZero(c)) &&(c->exponent >= 0 || ((U_LONG)(-c->exponent)) <= pcos->MaxPrec)) &&
+ i<nc
+ );
+
+Exit:
+#ifdef _DEBUG
+ if(gfDebug) {
+ VPrint(stdout, "cos=%\n", pcos);
+ VPrint(stdout, "sin=%\n", psin);
+ }
+#endif /* _DEBUG */
+ VpFree(div);
+ VpFree(n);
+ VpFree(c);
+ VpFree(r);
+ VpFree(z);
+}
+#endif /* ENABLE_TRIAL_METHOD */
+
+#ifdef _DEBUG
+int
+VpVarCheck(Real * v)
+/*
+ * Checks the validity of the Real variable v.
+ * [Input]
+ * v ... Real *, variable to be checked.
+ * [Returns]
+ * 0 ... correct v.
+ * other ... error
+ */
+{
+ U_LONG i;
+
+ if(v->MaxPrec <= 0) {
+ printf("ERROR(VpVarCheck): Illegal Max. Precision(=%lu)\n",
+ v->MaxPrec);
+ return 1;
+ }
+ if((v->Prec <= 0) ||((v->Prec) >(v->MaxPrec))) {
+ printf("ERROR(VpVarCheck): Illegal Precision(=%lu)\n", v->Prec);
+ printf(" Max. Prec.=%lu\n", v->MaxPrec);
+ return 2;
+ }
+ for(i = 0; i < v->Prec; ++i) {
+ if((v->frac[i] >= BASE)) {
+ printf("ERROR(VpVarCheck): Illegal fraction\n");
+ printf(" Frac[%ld]=%lu\n", i, v->frac[i]);
+ printf(" Prec. =%lu\n", v->Prec);
+ printf(" Exp. =%d\n", v->exponent);
+ printf(" BASE =%lu\n", BASE);
+ return 3;
+ }
+ }
+ return 0;
+}
+#endif /* _DEBUG */
+
+static U_LONG
+SkipWhiteChar(char *szVal)
+{
+ char ch;
+ U_LONG i = 0;
+ while(ch = szVal[i++]) {
+ if(ISSPACE(ch)) continue;
+ break;
+ }
+ return i - 1;
+}
diff --git a/ext/bigdecimal/bigdecimal.def b/ext/bigdecimal/bigdecimal.def
new file mode 100644
index 0000000000..8450e164e6
--- /dev/null
+++ b/ext/bigdecimal/bigdecimal.def
@@ -0,0 +1,2 @@
+EXPORTS
+Init_bigdecimal
diff --git a/ext/bigdecimal/bigdecimal.h b/ext/bigdecimal/bigdecimal.h
new file mode 100644
index 0000000000..0af0b1dfc5
--- /dev/null
+++ b/ext/bigdecimal/bigdecimal.h
@@ -0,0 +1,218 @@
+/*
+ *
+ * Ruby BigDecimal(Variable decimal precision) extension library.
+ *
+ * Copyright(C) 2002 by Shigeo Kobayashi(shigeo@tinyforest.gr.jp)
+ *
+ * You may distribute under the terms of either the GNU General Public
+ * License or the Artistic License, as specified in the README file
+ * of this BigDecimal distribution.
+ *
+ * NOTES:
+ * 2003-03-28 V1.0 checked in.
+ *
+ */
+
+#ifndef ____BIG_DECIMAL__H____
+#define ____BIG_DECIMAL__H____
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*
+ * NaN & Infinity
+ */
+#define SZ_NaN "NaN"
+#define SZ_INF "Infinity"
+#define SZ_PINF "+Infinity"
+#define SZ_NINF "+Infinity"
+
+/*
+ * #define VP_EXPORT other than static to let VP_ routines
+ * be called from outside of this module.
+ */
+#define VP_EXPORT static
+
+#define U_LONG unsigned long
+#define S_LONG long
+#define U_INT unsigned int
+#define S_INT int
+
+/* Exception codes */
+#define VP_EXCEPTION_ALL ((unsigned short)0x00FF)
+#define VP_EXCEPTION_INFINITY ((unsigned short)0x0001)
+#define VP_EXCEPTION_NaN ((unsigned short)0x0002)
+#define VP_EXCEPTION_UNDERFLOW ((unsigned short)0x0004)
+#define VP_EXCEPTION_OVERFLOW ((unsigned short)0x0001) /* 0x0008) */
+#define VP_EXCEPTION_ZERODIVIDE ((unsigned short)0x0001) /* 0x0010) */
+
+/* Following 2 exceptions cann't controlled by user */
+#define VP_EXCEPTION_OP ((unsigned short)0x0020)
+#define VP_EXCEPTION_MEMORY ((unsigned short)0x0040)
+
+/* Computation mode */
+#define VP_ROUND_MODE ((unsigned short)0x0100)
+#define VP_ROUND_UP 1
+#define VP_ROUND_DOWN 2
+#define VP_ROUND_HALF_UP 3
+#define VP_ROUND_HALF_DOWN 4
+#define VP_ROUND_CEIL 5
+#define VP_ROUND_FLOOR 6
+#define VP_ROUND_HALF_EVEN 7
+
+#define VP_SIGN_NaN 0 /* NaN */
+#define VP_SIGN_POSITIVE_ZERO 1 /* Positive zero */
+#define VP_SIGN_NEGATIVE_ZERO -1 /* Negative zero */
+#define VP_SIGN_POSITIVE_FINITE 2 /* Positive finite number */
+#define VP_SIGN_NEGATIVE_FINITE -2 /* Negative finite number */
+#define VP_SIGN_POSITIVE_INFINITE 3 /* Positive infinite number */
+#define VP_SIGN_NEGATIVE_INFINITE -3 /* Negative infinite number */
+
+/*
+ * VP representation
+ * r = 0.xxxxxxxxx *BASE**exponent
+ */
+typedef struct {
+ VALUE obj; /* Back pointer(VALUE) for Ruby object. */
+ U_LONG MaxPrec; /* Maximum precision size */
+ /* This is the actual size of pfrac[] */
+ /*(frac[0] to frac[MaxPrec] are available). */
+ U_LONG Prec; /* Current precision size. */
+ /* This indicates how much the. */
+ /* the array frac[] is actually used. */
+ S_INT exponent;/* Exponent part. */
+ short sign; /* Attributes of the value. */
+ /*
+ * ==0 : NaN
+ * 1 : Positive zero
+ * -1 : Negative zero
+ * 2 : Positive number
+ * -2 : Negative number
+ * 3 : Positive infinite number
+ * -3 : Negative infinite number
+ */
+ short flag; /* Not used in vp_routines,space for user. */
+ U_LONG frac[1]; /* Pointer to array of fraction part. */
+} Real;
+
+/*
+ * ------------------
+ * EXPORTables.
+ * ------------------
+ */
+
+VP_EXPORT Real *
+VpNewRbClass(U_LONG mx,char *str,VALUE klass);
+
+VP_EXPORT Real *VpCreateRbObject(U_LONG mx,char *str);
+
+VP_EXPORT U_LONG VpBaseFig(void);
+VP_EXPORT U_LONG VpDblFig(void);
+VP_EXPORT U_LONG VpBaseVal(void);
+
+/* Zero,Inf,NaN (isinf(),isnan() used to check) */
+VP_EXPORT double VpGetDoubleNaN(void);
+VP_EXPORT double VpGetDoublePosInf(void);
+VP_EXPORT double VpGetDoubleNegInf(void);
+VP_EXPORT double VpGetDoubleNegZero(void);
+
+/* These 2 functions added at v1.1.7 */
+VP_EXPORT U_LONG VpGetPrecLimit(void);
+VP_EXPORT U_LONG VpSetPrecLimit(U_LONG n);
+
+/* Round mode */
+VP_EXPORT unsigned long VpGetRoundMode(void);
+VP_EXPORT unsigned long VpSetRoundMode(unsigned long n);
+
+VP_EXPORT int VpException(unsigned short f,char *str,int always);
+VP_EXPORT int VpIsNegDoubleZero(double v);
+VP_EXPORT U_LONG VpNumOfChars(Real *vp);
+VP_EXPORT U_LONG VpInit(U_LONG BaseVal);
+VP_EXPORT void *VpMemAlloc(U_LONG mb);
+VP_EXPORT void VpFree(Real *pv);
+VP_EXPORT Real *VpAlloc(U_LONG mx, char *szVal);
+VP_EXPORT int VpAsgn(Real *c,Real *a,int isw);
+VP_EXPORT int VpAddSub(Real *c,Real *a,Real *b,int operation);
+VP_EXPORT int VpMult(Real *c,Real *a,Real *b);
+VP_EXPORT int VpDivd(Real *c,Real *r,Real *a,Real *b);
+VP_EXPORT int VpComp(Real *a,Real *b);
+VP_EXPORT S_LONG VpExponent10(Real *a);
+VP_EXPORT void VpSzMantissa(Real *a,char *psz);
+VP_EXPORT void VpToString(Real *a,char *psz,int fFmt);
+VP_EXPORT int VpCtoV(Real *a,char *int_chr,U_LONG ni,char *frac,U_LONG nf,char *exp_chr,U_LONG ne);
+VP_EXPORT int VpVtoD(double *d,S_LONG *e,Real *m);
+VP_EXPORT void VpDtoV(Real *m,double d);
+VP_EXPORT void VpItoV(Real *m,S_INT ival);
+VP_EXPORT int VpSqrt(Real *y,Real *x);
+VP_EXPORT void VpActiveRound(Real *y,Real *x,int f,int il);
+VP_EXPORT void VpMidRound(Real *y, int f, int nf);
+VP_EXPORT void VpLeftRound(Real *y, int f, int nf);
+VP_EXPORT void VpFrac(Real *y,Real *x);
+VP_EXPORT int VpPower(Real *y,Real *x,S_INT n);
+
+/* VP constants */
+VP_EXPORT Real *VpOne();
+
+#ifdef ENABLE_TRIAL_METHOD
+VP_EXPORT void VpPi(Real *y);
+VP_EXPORT void VpExp1(Real *y);
+VP_EXPORT void VpExp(Real *y,Real *x);
+VP_EXPORT void VpSinCos(Real *psin,Real *pcos,Real *x);
+#endif /* ENABLE_TRIAL_METHOD */
+/*
+ * ------------------
+ * MACRO definitions.
+ * ------------------
+ */
+#define Abs(a) (((a)>= 0)?(a):(-(a)))
+#define Max(a, b) (((a)>(b))?(a):(b))
+#define Min(a, b) (((a)>(b))?(b):(a))
+
+#define VpMaxPrec(a) ((a)->MaxPrec)
+#define VpPrec(a) ((a)->Prec)
+#define VpGetFlag(a) ((a)->flag)
+
+/* Sign */
+
+/* VpGetSign(a) returns 1,-1 if a>0,a<0 respectively */
+#define VpGetSign(a) (((a)->sign>0)?1:(-1))
+/* Change sign of a to a>0,a<0 if s = 1,-1 respectively */
+#define VpChangeSign(a,s) {if((s)>0) (a)->sign=(short)Abs((S_LONG)(a)->sign);else (a)->sign=-(short)Abs((S_LONG)(a)->sign);}
+/* Sets sign of a to a>0,a<0 if s = 1,-1 respectively */
+#define VpSetSign(a,s) {if((s)>0) (a)->sign=(short)VP_SIGN_POSITIVE_FINITE;else (a)->sign=(short)VP_SIGN_NEGATIVE_FINITE;}
+
+/* 1 */
+#define VpSetOne(a) {(a)->frac[0]=(a)->exponent=(a)->Prec=1;(a)->sign=VP_SIGN_POSITIVE_FINITE;}
+
+/* ZEROs */
+#define VpIsPosZero(a) ((a)->sign==VP_SIGN_POSITIVE_ZERO)
+#define VpIsNegZero(a) ((a)->sign==VP_SIGN_NEGATIVE_ZERO)
+#define VpIsZero(a) (VpIsPosZero(a) || VpIsNegZero(a))
+#define VpSetPosZero(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->sign=VP_SIGN_POSITIVE_ZERO)
+#define VpSetNegZero(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->sign=VP_SIGN_NEGATIVE_ZERO)
+#define VpSetZero(a,s) ( ((s)>0)?VpSetPosZero(a):VpSetNegZero(a) )
+
+/* NaN */
+#define VpIsNaN(a) ((a)->sign==VP_SIGN_NaN)
+#define VpSetNaN(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->sign=VP_SIGN_NaN)
+
+/* Infinity */
+#define VpIsPosInf(a) ((a)->sign==VP_SIGN_POSITIVE_INFINITE)
+#define VpIsNegInf(a) ((a)->sign==VP_SIGN_NEGATIVE_INFINITE)
+#define VpIsInf(a) (VpIsPosInf(a) || VpIsNegInf(a))
+#define VpIsDef(a) ( !(VpIsNaN(a)||VpIsInf(a)) )
+#define VpSetPosInf(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->sign=VP_SIGN_POSITIVE_INFINITE)
+#define VpSetNegInf(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->sign=VP_SIGN_NEGATIVE_INFINITE)
+#define VpSetInf(a,s) ( ((s)>0)?VpSetPosInf(a):VpSetNegInf(a) )
+#define VpIsOne(a) ((a->Prec==1)&&(a->frac[0]==1)&&(a->exponent==1))
+#define VpExponent(a) (a->exponent)
+#ifdef _DEBUG
+int VpVarCheck(Real * v);
+VP_EXPORT int VPrint(FILE *fp,char *cntl_chr,Real *a);
+#endif /* _DEBUG */
+
+#if defined(__cplusplus)
+} /* extern "C" { */
+#endif
+#endif //____BIG_DECIMAL__H____
diff --git a/ext/bigdecimal/bigdecimal_en.html b/ext/bigdecimal/bigdecimal_en.html
new file mode 100644
index 0000000000..a8ced21e01
--- /dev/null
+++ b/ext/bigdecimal/bigdecimal_en.html
@@ -0,0 +1,796 @@
+<HTML>
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html">
+<style type="text/css"><!--
+body { color: #3f0f0f; background: #fefeff; margin-left: 2em; margin-right: 2em;}
+h1 { color: #ffffff; background-color: #3939AD; border-color: #FF00FF; width: 100%; border-style: solid;
+ border-top-width: 0.1em; border-bottom-width: 0.1em; border-right: none; border-left: none;
+ padding: 0.1em; font-weight: bold; font-size: 160%; text-align: center;}
+h2 { color: #00007f; background-color: #e7e7ff; border-color: #000094; width: 100%; border-style: solid; border-le ft: none; border-right: none; border-top-width: 0.1em; border-bottom-width: 0.1em; padding: 0.1em;
+ font-weight: bold; font-size: 110%;
+}
+h3 { color: #00007f; padding: 0.2em; font-size: 110%;}
+h4, h5 { color: #000000; padding: 0.2em; font-size: 100%;}
+table { margin-top: 0.2em; margin-bottom: 0.2em; margin-left: 2em; margin-right: 2em;}
+caption { color: #7f0000; font-weight: bold;}
+th { background: #e7e7ff; padding-left: 0.2em; padding-right: 0.2em;}
+td { background: #f3f7ff; padding-left: 0.2em; padding-right: 0.2em;}
+code { color: #0000df;}
+dt { margin-top: 0.2em;}
+li { margin-top: 0.2em;}
+pre
+{ BACKGROUND-COLOR: #d0d0d0; BORDER-BOTTOM: medium none; BORDER-LEFT: medium none;
+ BORDER-RIGHT: medium none; BORDER-TOP: medium none; LINE-HEIGHT: 100%; MARGIN: 12px 12px 12px 12px;
+ PADDING-BOTTOM: 12px; PADDING-LEFT: 12px; PADDING-RIGHT: 12px; PADDING-TOP: 12px;
+ WHITE-SPACE: pre; WIDTH: 100%
+}
+--></style>
+
+<TITLE>BigDecimal:An extension library for Ruby</TITLE>
+</HEAD>
+<BODY BGCOLOR=#FFFFE0>
+<H1>BigDecimal(Variable Precision Floating Library for Ruby)</H1>
+<DIV align="right"><A HREF="./bigdecimal_ja.html">Japanese</A></DIV><BR>
+BigDecimal is an extension library for the Ruby interpreter.
+Using BigDecimal class, you can obtain any number of significant digits in computation.
+For the details about Ruby see:<BR>
+<UL>
+<LI><A HREF="http://www.ruby-lang.org/en/">http://www.ruby-lang.org/en/</A>:Official Ruby page(English).</LI>
+<LI><A HREF="http://kahori.com/ruby/ring/">http://kahori.com/ruby/ring/</A>:Mutually linked pages relating to Ruby(Japanese).
+</LI>
+</UL>
+NOTE:<BR>
+ 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. For the details,see COPYING and README included in this
+ distribution.
+<BR>
+<hr>
+
+<H2>Contents</H2>
+<UL>
+<LI><A HREF="#INTRO">Introduction</LI>
+<LI><A HREF="#SPEC">Usage and methods</A></LI>
+<LI><A HREF="#UNDEF">Infinity,NaN,Zero</A></LI>
+<LI><A HREF="#STRUCT">Internal structure</A></LI>
+<LI><A HREF="#BASE">Binary or decimal number representation</A></LI>
+<LI><A HREF="#PREC">Resulting number of significant digits</A></LI>
+</UL>
+<HR>
+
+<A NAME="#INTRO">
+<H2>Introduction</H2>
+Ruby already has builtin (variable length integer number) class Bignum. Using Bignum class,you can obtain
+ any integer value in magnitude. But, variable length decimal number class is not yet built in.
+This is why I made variable length floating class BigDecimal.
+Feel free to send any comments or bug reports to me.
+<A HREF="mailto:shigeo@tinyforest.gr.jp">shigeo@tinyforest.gr.jp</A>
+I will try(but can't promise) to fix bugs reported.
+<hr>
+<H2>Installation</H2>
+The Ruby latest version can be downloaded from <A HREF="http://www.ruby-lang.org/en/">Official Ruby page</A>.
+Once decompress the downloaded Ruby archive,follow the normal installation procedures according to the
+documents included.
+
+<A NAME="#SPEC">
+<H2>Usage and methods</H2>
+Suppose you already know Ruby programming,
+to create BigDecimal objects,the program would like:<BR>
+
+<CODE><PRE>
+ require 'bigdecimal'
+ a=BigDecimal::new("0.123456789123456789")
+ b=BigDecimal("123456.78912345678",40)
+ c=a+b
+</PRE></CODE>
+
+<H3>List of methods</H3>
+In 32 bits integer system,every 4 digits(in decimal) are computed simultaneously.
+This means the number of significant digits in BigDecimal is always a multiple of 4.
+<P>
+Some more methods are available in Ruby code (not C code).
+To use them,just require util.rb as:
+<CODE><PRE>
+require "bigdecimal/util.rb"
+</PRE></CODE>
+String to BigDecimal conversion, BigDecimal to String conversion
+(in "nnnnnn.mmmm" form not in "0.xxxxxEn" form) etc are defined.
+For details,see the util.rb code.
+
+<H4><U>Class methods</U></H4>
+<UL>
+<LI><B>new</B></LI><BLOCKQUOTE>
+"new" method creates a new BigDecimal object.<BR>
+a=BigDecimal::new(s[,n]) or<BR>
+a=BigDecimal(s[,n]) or<BR>
+where:<BR>
+s: Initial value string.<BR>
+n: Maximum number of significant digits of a. n must be a Fixnum object.
+If n is omitted or is equal to 0,then the maximum number of significant digits of a is determined from the length of s.
+Actual number of digits handled in computations are usually gretaer than n.<BR>
+n is useful when performing divisions like
+<CODE><PRE>
+BigDecimal("1") / BigDecimal("3") # => 0.3333333333 33E0
+BigDecimal("1",10) / BigDecimal("3",10) # => 0.3333333333 3333333333 33333333E0
+</PRE></CODE>
+but the resulting digits obtained may differ in future version.
+</BLOCKQUOTE>
+
+<LI><B>mode</B></LI><BLOCKQUOTE>
+mode method controls BigDecimal computation.Following usage are defined.<BR>
+<P><B>[EXCEPTION control]</B><P>
+Actions when computation results NaN or Infinity can be defined as follows.
+<P>
+<BLOCKQUOTE>
+f = BigDecimal::mode(BigDecimal::EXCEPTION_NaN,flag)<BR>
+f = BigDecimal::mode(BigDecimal::EXCEPTION_INFINITY,flag)<BR>
+f = BigDecimal::mode(BigDecimal::EXCEPTION_UNDERFLOW,flag)<BR>
+f = BigDecimal::mode(BigDecimal::EXCEPTION_OVERFLOW,flag)<BR>
+f = BigDecimal::mode(BigDecimal::EXCEPTION_ZERODIVIDE,flag)<BR>
+f = BigDecimal::mode(BigDecimal::EXCEPTION_ALL,flag)<BR>
+</BLOCKQUOTE>
+EXCEPTION_NaN controls the execution when computation results to NaN.<BR>
+EXCEPTION_INFINITY controls the execution when computation results to Infinity(}Infinity).<BR>
+EXCEPTION_UNDERFLOW controls the execution when computation underflows.<BR>
+EXCEPTION_OVERFLOW controls the execution when computation overflows.<BR>
+EXCEPTION_ZERODIVIDE controls the execution when zero-division occures.<BR>
+EXCEPTION_ALL controls the execution for any exception defined occures.<BR>
+If the flag is true,then the relating exception is thrown.<BR>
+No exception is thrown when the flag is false(default) and computation
+continues with the result:<BR>
+<BLOCKQUOTE>
+EXCEPTION_NaN results to NaN<BR>
+EXCEPTION_INFINITY results to +Infinity or -Infinity<BR>
+EXCEPTION_UNDERFLOW results to 0.<BR>
+EXCEPTION_OVERFLOW results to +Infinity or -Infinity<BR>
+EXCEPTION_ZERODIVIDE results to +Infinity or -Infinity<BR>
+</BLOCKQUOTE>
+EXCEPTION_INFINITY,EXCEPTION_OVERFLOW, and EXCEPTION_ZERODIVIDE are
+ currently the same.<BR>
+The return value of mode method is the previous value set.<BR>
+nil is returned if any argument is wrong.<BR>
+Suppose the return value of the mode method is f,then
+ f &amp; BigDecimal::EXCEPTION_NaN !=0 means EXCEPTION_NaN is set to on.
+<P>
+<B>[ROUND error control]</B><P>
+Rounding operation can be controlled as:
+<BLOCKQUOTE>
+f = BigDecimal::mode(BigDecimal::ROUND_MODE,flag)
+</BLOCKQUOTE>
+where flag must be one of:
+<TABLE>
+
+<TR><TD>ROUND_UP</TD><TD>round away from zero.</TD></TR>
+<TR><TD>ROUND_DOWN</TD><TD>round towards zero(truncate).</TD></TR>
+<TR><TD>ROUND_HALF_UP</TD><TD>round up if the digit &gt;= 5 otherwise truncated(default).</TD></TR>
+<TR><TD>ROUND_HALF_DOWN</TD><TD>round up if the digit &gt;= 6 otherwise truncated.</TD></TR>
+<TR><TD>ROUND_HALF_EVEN</TD><TD>round towards the even neighbor(Banker's rounding).
+<TR><TD>ROUND_CEILING</TD><TD>round towards positive infinity(ceil).</TD></TR>
+<TR><TD>ROUND_FLOOR</TD><TD>round towards negative infinity(floor).</TD></TR>
+</TABLE>
+New rounding mode is returned,nil is returned if any argument is not an integer.
+Bad specification is ignored.<BR>
+The digit location for rounding operation can not be specified by this mode method,
+use truncate/round/ceil/floor/add/sub/mult/div mthods for each instance instead.
+</BLOCKQUOTE>
+
+<LI><B>limit[(n)]</B></LI><BLOCKQUOTE>
+Limits the maximum digits that the newly created BigDecimal objects can hold
+never exceed n+? (Currently,? can not be determined beforehand,but not so big).
+This means the rounding operation specified by BigDecimal.mode is
+performed if necessary.
+limit returns maximum value before set.
+Zero,the default value,means no upper limit.<BR>
+Except for zero,the limit has more priority than instance methods such as truncate,round,ceil,floor,add,sub,mult,and div. <BR>
+mf = BigDecimal::limit(n)<BR>
+</BLOCKQUOTE>
+
+<LI><B>double_fig</B></LI><BLOCKQUOTE>
+double_fig is a class method which returns the number of digits
+the Float class can have.
+<CODE><PRE>
+ p BigDecimal::double_fig # ==> 20 (depends on the CPU etc.)
+</PRE></CODE>
+The equivalent C programs which calculates the value of
+double_fig is:
+<CODE><PRE>
+ double v = 1.0;
+ int double_fig = 0;
+ while(v + 1.0 > 1.0) {
+ ++double_fig;
+ v /= 10;
+ }
+</PRE></CODE>
+</BLOCKQUOTE>
+
+<LI><B>BASE</B></LI><BLOCKQUOTE>
+Base value used in the BigDecimal calculation.
+On 32 bits integer system,the value of BASE is 10000.<BR>
+b = BigDecimal::BASE<BR>
+</BLOCKQUOTE>
+</UL>
+
+<H4><U>Instance methods</U></H4>
+<UL>
+<LI><B>+</B></LI><BLOCKQUOTE>
+addition(c = a + b)<BR>
+For the resulting number of significant digits of c,see <A HREF="#PREC">Resulting number of significant digits</A>.
+
+</BLOCKQUOTE>
+<LI><B>-</B></LI><BLOCKQUOTE>
+subtraction (c = a - b) or negation (c = -a)<BR>
+For the resulting number of significant digits of c,see <A HREF="#PREC">Resulting number of significant digits</A>.
+
+</BLOCKQUOTE>
+<LI><B>*</B></LI><BLOCKQUOTE>
+multiplication(c = a * b)<BR>
+For the resulting number of significant digits of c,see <A HREF="#PREC">Resulting number of significant digits</A>.
+
+</BLOCKQUOTE>
+<LI><B>/</B></LI><BLOCKQUOTE>
+division(c = a / b)<BR>
+For the resulting number of significant digits of c,see <A HREF="#PREC">Resulting number of significant digits</A>.
+</BLOCKQUOTE>
+
+<LI><B>add(b,n)</B></LI><BLOCKQUOTE>
+c = a.add(b,n)<BR>
+c = a.add(b,n) performs c = a + b.
+If n is less than the actual significant digits of a + b,
+then c is rounded properly according to the BigDecimal.limit.
+
+</BLOCKQUOTE>
+<LI><B>sub(b,n)</B></LI><BLOCKQUOTE>
+c = a.sub(b,n)<BR>
+c = a.sub(b,n) performs c = a - b.
+If n is less than the actual significant digits of a - b,
+then c is rounded properly according to the BigDecimal.limit.
+
+</BLOCKQUOTE>
+<LI><B>mult(b,n)</B></LI><BLOCKQUOTE>
+c = a.mult(b,n)<BR>
+c = a.mult(b,n) performs c = a * b.
+If n is less than the actual significant digits of a * b,
+then c is rounded properly according to the BigDecimal.limit.
+
+</BLOCKQUOTE>
+<LI><B>div(b[,n])</B></LI><BLOCKQUOTE>
+c = a.div(b,n)<BR>
+c = a.div(b,n) performs c = a / b.
+If n is less than the actual significant digits of a / b,
+then c is rounded properly according to the BigDecimal.limit.<BR>
+If n is not given,then the result will be an integer(BigDecimal) like Float#div.
+</BLOCKQUOTE>
+
+<LI><B>fix</B></LI><BLOCKQUOTE>
+c = a.fix<BR>
+returns integer part of a.<BR>
+
+</BLOCKQUOTE>
+<LI><B>frac</B></LI><BLOCKQUOTE>
+c = a.frac<BR>
+returns fraction part of a.<BR>
+
+</BLOCKQUOTE>
+<LI><B>floor[(n)]</B></LI><BLOCKQUOTE>
+c = a.floor<BR>
+returns the maximum integer value (in BigDecimal) which is less than or equal to a.
+<CODE><PRE>
+ c = BigDecimal("1.23456").floor # ==> 1
+ c = BigDecimal("-1.23456").floor # ==> -2
+</PRE></CODE>
+
+As shown in the following example,an optional integer argument (n) specifying the position
+of the target digit can be given.<BR>
+If n> 0,then the (n+1)th digit counted from the decimal point in fraction part is processed(resulting number of fraction part digits is less than or equal to n).<BR>
+If n<0,then the n-th digit counted from the decimal point in integer part is processed(at least n 0's are placed from the decimal point to left).
+<CODE><PRE>
+ c = BigDecimal("1.23456").floor(4) # ==> 1.2345
+ c = BigDecimal("15.23456").floor(-1) # ==> 10.0
+</PRE></CODE>
+
+</BLOCKQUOTE>
+<LI><B>ceil[(n)]</B></LI><BLOCKQUOTE>
+c = a.ceil<BR>
+returns the minimum integer value (in BigDecimal) which is greater than or equal to a.
+<CODE><PRE>
+ c = BigDecimal("1.23456").ceil # ==> 2
+ c = BigDecimal("-1.23456").ceil # ==> -1
+</PRE></CODE>
+
+As shown in the following example,an optional integer argument (n) specifying the position
+of the target digit can be given.<BR>
+If n>0,then the (n+1)th digit counted from the decimal point in fraction part is processed(resulting number of fraction part digits is less than or equal to n).<BR>
+If n<0,then the n-th digit counted from the decimal point in integer part is processed(at least n 0's are placed from the decimal point to left).
+<CODE><PRE>
+ c = BigDecimal("1.23456").ceil(4) # ==> 1.2346
+ c = BigDecimal("15.23456").ceil(-1) # ==> 20.0
+</PRE></CODE>
+
+</BLOCKQUOTE>
+<LI><B>round[(n[,b])]</B></LI><BLOCKQUOTE>
+c = a.round<BR>
+round a to the nearest 1(default)D<BR>
+<CODE><PRE>
+ c = BigDecimal("1.23456").round # ==> 1
+ c = BigDecimal("-1.23456").round # ==> -1
+</PRE></CODE>
+The rounding operation changes according to BigDecimal::mode(BigDecimal::ROUND_MODE,flag) if specified.
+
+As shown in the following example,an optional integer argument (n) specifying the position
+of the target digit can be given.<BR>
+If n>0,then the (n+1)th digit counted from the decimal point in fraction part is processed(resulting number of fraction part digits is less than or equal to n).<BR>
+If n<0,then the n-th digit counted from the decimal point in integer part is processed(at least n 0's are placed from the decimal point to left).
+<CODE><PRE>
+c = BigDecimal::new("1.23456").round(4) # ==> 1.2346
+c = BigDecimal::new("15.23456").round(-1) # ==> 20.0
+</PRE></CODE>
+
+Rounding operation can be specified by setting the second optional argument b with the valid ROUND_MODE.<BR>
+<CODE><PRE>
+c = BigDecimal::new("1.23456").round(3,BigDecimal::ROUND_HALF_EVEN) # ==> 1.234
+c = BigDecimal::new("1.23356").round(3,BigDecimal::ROUND_HALF_EVEN) # ==> 1.234
+</PRE></CODE>
+
+</BLOCKQUOTE>
+<LI><B>truncate[(n)]</B></LI><BLOCKQUOTE>
+c = a.truncate<BR>
+truncate a to the nearest 1D<BR>
+As shown in the following example,an optional integer argument (n) specifying the position
+of the target digit can be given.<BR>
+If n>0,then the (n+1)th digit counted from the decimal point in fraction part is processed(resulting number of fraction part digits is less than or equal to n).<BR>
+If n<0,then the n-th digit counted from the decimal point in integer part is processed(at least n 0's are placed from the decimal point to left).
+
+<CODE><PRE>
+c = BigDecimal::new("1.23456").truncate(4) # ==> 1.2345
+c = BigDecimal::new("15.23456").truncate(-1) # ==> 10.0
+</PRE></CODE>
+</BLOCKQUOTE>
+<LI><B>abs</B></LI><BLOCKQUOTE>
+c = a.abs<BR>
+returns an absolute value of a.<BR>
+
+</BLOCKQUOTE>
+<LI><B>to_i</B></LI><BLOCKQUOTE>
+changes a to an integer.<BR>
+i = a.to_i<BR>
+i becomes to Fixnum or Bignum.
+If a is Infinity or NaN,then i becomes to nil.
+
+</BLOCKQUOTE>
+<LI><B>to_s[(n)]</B></LI><BLOCKQUOTE>
+converts to string(results look like "0.xxxxxEn").<BR>
+s = a.to_s<BR>
+If n is given,then a space is inserted after every n digits for readability.<BR>
+s = a.to_s(n)
+
+</BLOCKQUOTE>
+<LI><B>exponent</B></LI><BLOCKQUOTE>
+returns an integer holding exponent value of a.<BR>
+n = a.exponent <BR>
+means a = 0.xxxxxxx*10**n.
+</BLOCKQUOTE>
+
+<LI><B>prec</B></LI><BLOCKQUOTE>
+n,m = a.prec <BR>
+prec returns number of significant digits (n) and maximum number of
+significant digits (m) of a.
+</BLOCKQUOTE>
+
+<LI><B>to_f</B></LI><BLOCKQUOTE>
+Creates a new Float object having (nearly) the same value.
+Use split method if you want to convert by yourself.
+</BLOCKQUOTE>
+
+</BLOCKQUOTE>
+<LI><B>sign</B></LI><BLOCKQUOTE>
+n = a.sign <BR>
+returns positive value if a &gt; 0,negative value if a &lt; 0,
+otherwise zero if a == 0.<BR>
+where the value of n means that a is:<BR>
+n = BigDecimal::SIGN_NaN(0) : a is NaN<BR>
+n = BigDecimal::SIGN_POSITIVE_ZERO(1) : a is +0<BR>
+n = BigDecimal::SIGN_NEGATIVE_ZERO(-1) : a is -0<BR>
+n = BigDecimal::SIGN_POSITIVE_FINITE(2) : a is positive<BR>
+n = BigDecimal::SIGN_NEGATIVE_FINITE(-2) : a is negative<BR>
+n = BigDecimal::SIGN_POSITIVE_INFINITE(3) : a is +Infinity<BR>
+n = BigDecimal::SIGN_NEGATIVE_INFINITE(-3) : a is -Infinity<BR>
+The value in () is the actual value,see (<A HREF="#STRUCT">Internal structure</A>.<BR>
+
+</BLOCKQUOTE>
+<LI><B>nan?</B></LI><BLOCKQUOTE>
+a.nan? returns True when a is NaN.
+
+</BLOCKQUOTE>
+<LI><B>infinite?</B></LI><BLOCKQUOTE>
+a.infinite? returns 1 when a is +‡,-1 when a is -‡, nil otherwise.
+
+</BLOCKQUOTE>
+<LI><B>finite?</B></LI><BLOCKQUOTE>
+a.finite? returns true when a is neither ‡ nor NaN.
+</BLOCKQUOTE>
+
+<LI><B>zero?</B></LI><BLOCKQUOTE>
+c = a.zero?<BR>
+returns true if a is equal to 0,otherwise returns false<BR>
+</BLOCKQUOTE>
+<LI><B>nonzero?</B></LI><BLOCKQUOTE>
+c = a.nonzero?<BR>
+returns nil if a is 0,otherwise returns a itself.<BR>
+</BLOCKQUOTE>
+
+<LI><B>split</B></LI><BLOCKQUOTE>
+decomposes a BigDecimal value to 4 parts.
+All 4 parts are returned as an array.<BR>
+Parts consist of a sign(0 when the value is NaN,+1 for positive and
+ -1 for negative value), a string representing fraction part,base value(always 10 currently),and an integer(Fixnum) for exponent respectively.
+a=BigDecimal::new("3.14159265")<BR>
+f,x,y,z = a.split<BR>
+where f=+1,x="314159265",y=10 and z=1<BR>
+therefore,you can translate BigDecimal value to Float as:<BR>
+s = "0."+x<BR>
+b = f*(s.to_f)*(y**z)<BR>
+
+</BLOCKQUOTE>
+<LI><B>inspect</B></LI><BLOCKQUOTE>
+is used for debugging output.<BR>
+p a=BigDecimal::new("3.14",10)<BR>
+should produce output like "#&lt;0x112344:'0.314E1',4(12)%gt;".
+where "0x112344" is the address,
+'0.314E1' is the value,4 is the number of the significant digits,
+and 12 is the maximum number of the significant digits
+the object can hold.
+</BLOCKQUOTE>
+
+<LI><B>sqrt</B></LI><BLOCKQUOTE>
+c = a.sqrt(n)<BR>
+computes square root value of a with significant digit number n at least.<BR>
+</BLOCKQUOTE>
+
+<LI><B>**</B></LI><BLOCKQUOTE>
+c = a ** n<BR>
+returns the value of a powered by n.
+n must be an integer.<BR>
+
+</BLOCKQUOTE>
+<LI><B>power</B></LI><BLOCKQUOTE>
+The same as ** method.<BR>
+c = a.power(n)<BR>
+returns the value of a powered by n(c=a**n).
+n must be an integer.<BR>
+</BLOCKQUOTE>
+
+<LI><B>divmod,quo,modulo,%,remainder</B></LI><BLOCKQUOTE>
+See,corresponding methods in Float class.
+</BLOCKQUOTE>
+
+</BLOCKQUOTE>
+<LI><B>&lt;=&gt;</B></LI><BLOCKQUOTE>
+c = a &lt;=&gt; b <BR>
+returns 0 if a==b,1 if a &gt b,and returns -1 if a &lt b.<BR>
+</BLOCKQUOTE>
+</UL>
+
+Following methods need no explanation.<BR>
+<UL>
+<LI>==</LI>
+<LI>===</LI>
+same as ==,used in case statement.
+<LI>!=</LI>
+<LI>&lt;</LI>
+<LI>&lt;=</LI>
+<LI>&gt;</LI>
+<LI>&gt;=</LI>
+</UL>
+
+<H4><U>Class methods(trial version)</U></H4>
+Following class methods are in trial stage, and not usable in default.
+Uncomment /* #define ENABLE_TRIAL_METHOD */ in bigdecimal.c, compile and install
+again if you want to use.
+<UL>
+<LI><B>E</B></LI><BLOCKQUOTE>
+e = BigDecimal::E(n)<BR>
+where e(=2.718281828....) is the base value of natural logarithm.<BR>
+n specifies the length of significant digits of e.
+
+</BLOCKQUOTE>
+<LI><B>PI</B></LI><BLOCKQUOTE>
+e = BigDecimal::PI(n)<BR>
+returns at least n digits of the ratio of the circumference of a circle to its diameter
+(pi=3.14159265358979....) using J.Machin's formula.<BR>
+</BLOCKQUOTE>
+</UL>
+
+<H4><U>Instance methods(trial version)</U></H4>
+Following instance methods are in trial stage, and not usable in default.
+Uncomment /* #define ENABLE_TRIAL_METHOD */ in bigdecimal.c, compile and install
+again if you want to use.
+<UL>
+<LI><B>sincos</B></LI><BLOCKQUOTE>
+computes and returns sine and cosine value of a with significant digit number n at least.<BR>
+sin,cos = a.sincos(n)<BR>
+Computation may return bad results unless |a|<2*3.1415.....
+</BLOCKQUOTE>
+<LI><B>exp</B></LI><BLOCKQUOTE>
+c = a.exp(n)<BR>
+computes the base of natural logarithm value(e=2.718281828....) powered by a
+with significant digit number n at least.<BR>
+</BLOCKQUOTE>
+</UL>
+<HR>
+
+<H3>About 'coerce'</H3>
+<B>For the binary operation like A op B:</B>
+<DL>
+<DT> 1.Both A and B are BigDecimal objects</DT>
+<DD> A op B is normally performed.</DD>
+<DT> 2.A is the BigDecimal object but B is other than BigDecimal object</DT>
+<DD> Operation is performed,after B is translated to correcponding BigDecimal object(because BigDecimal supports coerce method).</DD>
+<DT> 3.A is not the BigDecimal object but B is BigDecimal object</DT>
+<DD>If A has coerce mthod,then B will translate A to corresponding
+BigDecimal object and the operation is performed,otherwise an error occures.</DD>
+</DL>
+
+String is not translated to BigDecimal in default.
+Uncomment /* #define ENABLE_NUMERIC_STRING */ in bigdecimal.c, compile and install
+again if you want to enable string to BigDecimal conversion.
+Translation stops without error at the character representing non digit.
+For instance,"10XX" is translated to 10,"XXXX" is translated to 0.<BR>
+String representing zero or infinity such as "Infinity","+Infinity","-Infinity",and "NaN" can also be translated to BigDecimal unless false is specified by mode method.<BR>
+
+BigDecimal class supports coerce method(for the details about coerce method,see Ruby documentations). This means the most binary operation can be performed if the BigDecimal object is at the left hand side of the operation.<BR><BR>
+
+ For example:
+<CODE><PRE>
+ a = BigDecimal.E(20)
+ c = a * "0.123456789123456789123456789" # A String is changed to BigDecimal object.
+</PRE></CODE>
+is performed normally.<BR>
+ But,because String does not have coerce method,the following example can not be performed.<BR>
+
+<CODE><PRE>
+ a = BigDecimal.E(20)
+ c = "0.123456789123456789123456789" * a # ERROR
+</PRE></CODE>
+
+If you actually have any inconvenience about the error above.
+You can define a new class derived from String class,
+and define coerce method within the new class.<BR>
+
+<hr>
+<A NAME="#UNDEF">
+<H2>Infinity,Not a Number(NaN),Zero</H2>
+Infinite numbers and NaN can be represented by string writing "+Infinity"(or "Infinity"),"-Infinity",and "NaN" respectively in your program.
+Infinite numbers can be obtained by 1.0/0.0(=Infinity) or -1.0/0.0(=-Infinity).
+<BR><BR>
+NaN(Not a number) can be obtained by undefined computation like 0.0/0.0
+or Infinity-Infinity.
+Any computation including NaN results to NaN.
+Comparisons with NaN never become true,including comparison with NaN itself.
+<BR><BR>
+Zero has two different variations as +0.0 and -0.0.
+But,still, +0.0==-0.0 is true.
+<BR><BR>
+Computation results including Infinity,NaN,+0.0 or -0.0 become complicated.
+Run following program and comfirm the results.
+Send me any incorrect result if you find.
+
+<CODE><PRE>
+ require "bigdecimal"
+ aa = %w(1 -1 +0.0 -0.0 +Infinity -Infinity NaN)
+ ba = %w(1 -1 +0.0 -0.0 +Infinity -Infinity NaN)
+ opa = %w(+ - * / <=> > >= < == != <=)
+ for a in aa
+ for b in ba
+ for op in opa
+ x = BigDecimal::new(a)
+ y = BigDecimal::new(b)
+ eval("ans= x #{op} y;print a,' ',op,' ',b,' ==> ',ans.to_s,\"\n\"")
+ end
+ end
+ end
+</PRE></CODE>
+<hr>
+
+<A NAME="#STRUCT">
+<H2>Internal structure</H2>
+BigDecimal number is defined by the structure Real in BigDecimal.h.
+Digits representing a float number are kept in the array frac[] defined in the structure.
+In the program,any floating number(BigDecimal number) is represented as:<BR>
+ <BigDecimal number> = 0.xxxxxxxxx*BASE**n<BR><BR>
+where 'x' is any digit representing mantissa(kept in the array frac[]),
+BASE is base value(=10000 in 32 bit integer system),
+and n is the exponent value.<BR>
+Larger BASE value enables smaller size of the array frac[],and increases computation speed.
+The value of BASE is defined ind VpInit(). In 32 bit integer system,this value is
+10000. In 64 bit integer system,the value becomes larger.
+BigDecimal has not yet been compiled and tested on 64 bit integer system.
+It will be very nice if anyone try to run BigDecimal on 64 bit system and
+ inform me the results.
+When BASE is 10000,an element of the array frac[] can have vale of from 0 to 9999.
+(up to 4 digits).<BR>
+The structure Real is defined in bigdecimal.h as:<BR>
+<CODE><PRE>
+ typedef struct {
+ VALUE obj; /* Back pointer(VALUE) for Ruby object. */
+ unsigned long MaxPrec; /* The size of the array frac[] */
+ unsigned long Prec; /* Current size of frac[] actually used. */
+ short sign; /* Attribute of the value. */
+ /* ==0 : NaN */
+ /* 1 : +0 */
+ /* -1 : -0 */
+ /* 2 : Positive number */
+ /* -2 : Negative number */
+ /* 3 : +Infinity */
+ /* -3 : -Infinity */
+ unsigned short flag; /* Control flag */
+ int exponent; /* Exponent value(0.xxxx*BASE**exponent) */
+ unsigned long frac[1]; /* An araay holding mantissa(Variable) */
+ } Real;
+</CODE></PRE>
+The decimal value 1234.56784321 is represented as(BASE=10000):<BR>
+<PRE>
+ 0.1234 5678 4321*(10000)**1
+</PRE>
+where frac[0]=1234,frac[1]=5678,frac[2]=4321,
+Prec=3,sign=2,exponent=1. MaxPrec can be any value greater than or equal to
+Prec.
+<hr>
+
+<A NAME="#BASE">
+<H2>Binary or decimal number representation</H2>
+I adopted decimal number representation for BigDecimal implementation.
+Of cource,binary number representation is common on the most computers.
+
+<H3>Advantages using decimal representation</H3>
+The reason why I adopted decimal number representation for BigDecimal is:<BR>
+<DL>
+<DT>Easy for debugging
+<DD>The floating number 1234.56784321 can be easily represented as:<BR>
+ frac[0]=1234,frac[1]=5678,frac[2]=4321,exponent=1,and sign=2.
+<DT>Exact representation
+<DD>Following program can add all numbers(in decimal) in a file
+ without any error(no round operation).<BR>
+
+<CODE><PRE>
+ file = File::open(....,"r")
+ s = BigDecimal::new("0")
+ while line = file.gets
+ s = s + line
+ end
+</PRE></CODE>
+
+If the internal representation is binary,translation from decimal to
+binary is required and the translation error is inevitable.
+For example, 0.1 can not exactly be represented in binary.<BR>
+0.1 => b1*2**(-1)+b1*2**(-2)+b3*2**(-3)+b4*2**(-4)....<BR>
+where b1=0,b2=0,b3=0,b4=1...<BR>
+bn(n=1,2,3,...) is infinite series of digit with value of 0 or 1,
+and rounding operation is necessary but where we should round the series ?
+Of cource,exact "0.1" is printed if the rouding operation is properly done,
+<DT>Significant digit we can have is automatically determined
+<DD>In binary representation,0.1 can not be represented in finite series of digit.
+
+But we only need one element(frac[0]=1) in decimal representation.
+This means that we can always determine the size of the array frac[] in Real
+structure.
+</DL>
+
+<H3>Disadvantage of decimal representation</H3>
+Advantages stated so far can also be disadvantages if the input from outside is
+ represented in binary.
+Translation error from decimal to binary or vice versa is inevitable.
+So,translation from Float(binary) to BigDecimal(decimal) is not alway done exactly.
+
+<H4>Which is the first input?</H4>
+Because most people uses decimal notatin for numeric data representation,
+BigDecimal can handle numeric data without loss of translation error.
+<hr>
+
+<A NAME="#PREC">
+<H2>Resulting number of significant digits</H2>
+For the fundamental arithmetics such as addition,subtraction,
+multiplication,and division,I prepared 2 group of methods<BR>
+
+<H3>1. +,-,*,/</H3>
+For the operation + - * /,you can not specify the resulting
+number of significant digits.<BR>
+Resulting number of significant digits are defined as:<BR>
+1.1 For *,resulting number of significant digits is the sum of the
+significant digits of both side of the operator. For / ,resulting number of significant digits is the sum of the
+maximum significant digits of both side of the operator.<BR>
+1.2 For + and -,resulting number of significant digits is determined so that
+ no round operation is needed. <br>
+For example, c has more than 100 siginificant digits if c is computed as:<BR>
+c = 0.1+0.1*10**(-100)<br>
+<BR>
+As +,-,and * are always exact(no round operation is performed unless BigDecimal.limit is specified),
+which means more momories are required to keep computation results.
+But,the division such as c=1.0/3.0 will always be rounded.<BR>
+
+<H3>2. add,sub,mult,div</H3>
+The length of the significant digits obtained from +,-,*,/
+is always defined by that of right and left side of the operator.
+To specify the length of the significant digits by your self,
+use methos add,sub,mult,div.
+<CODE><PRE>
+ BigDecimal("2").div(3,12) # 2.0/3.0 => 0.6666666666 67E0
+</PRE></CODE>
+</BLOCKQUOTE>
+
+<H3>3. truncate,round,ceil,floor</H3>
+Using these methods,you can specify rounding location relatively from
+decimal point.
+<CODE><PRE>
+ BigDecimal("6.66666666666666").round(12) # => 0.6666666666 667E1
+</PRE></CODE>
+</BLOCKQUOTE>
+
+
+<H3>4. Example</H3>
+Following example compute the ratio of the circumference of a circle to
+its dirmeter(pi=3.14159265358979....) using J.Machin's formula.
+<BR><BR>
+<CODE><PRE>
+#!/usr/local/bin/ruby
+
+require "bigdecimal"
+#
+# Calculates 3.1415.... (the number of times that a circle's diameter
+# will fit around the circle) using J. Machin's formula.
+#
+def big_pi(sig) # sig: Number of significant figures
+ exp = -sig
+ pi = BigDecimal::new("0")
+ two = BigDecimal::new("2")
+ m25 = BigDecimal::new("-0.04")
+ m57121 = BigDecimal::new("-57121")
+
+ u = BigDecimal::new("1")
+ k = BigDecimal::new("1")
+ w = BigDecimal::new("1")
+ t = BigDecimal::new("-80")
+ while (u.nonzero? && u.exponent >= exp)
+ t = t*m25
+ u = t.div(k,sig)
+ pi = pi + u
+ k = k+two
+ end
+
+ u = BigDecimal::new("1")
+ k = BigDecimal::new("1")
+ w = BigDecimal::new("1")
+ t = BigDecimal::new("956")
+ while (u.nonzero? && u.exponent >= exp )
+ t = t.div(m57121,sig)
+ u = t.div(k,sig)
+ pi = pi + u
+ k = k+two
+ end
+ pi
+end
+
+if $0 == __FILE__
+ if ARGV.size == 1
+ print "PI("+ARGV[0]+"):\n"
+ p big_pi(ARGV[0].to_i)
+ else
+ print "TRY: ruby pi.rb 1000 \n"
+ end
+end
+
+</PRE></CODE>
+<HR>
+<FONT size=2>
+<I>
+<A HREF="http://www.tinyforest.gr.jp">
+Shigeo Kobayashi
+</A>
+(E-Mail:<A HREF="mailto:shigeo@tinyforest.gr.jp">&lt;shigeo@tinyforest.gr.jp&gt;</U></A>)
+</I>
+</FONT>
+</TD>
+</TR>
+</TABLE>
+</BODY>
+</HTML>
diff --git a/ext/bigdecimal/bigdecimal_ja.html b/ext/bigdecimal/bigdecimal_ja.html
new file mode 100644
index 0000000000..4fc3a4eb84
--- /dev/null
+++ b/ext/bigdecimal/bigdecimal_ja.html
@@ -0,0 +1,801 @@
+<HTML>
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=Shift_JIS">
+<style type="text/css"><!--
+body { color: #3f0f0f; background: #fefeff; margin-left: 2em; margin-right: 2em;}
+h1 { color: #ffffff; background-color: #3939AD; border-color: #FF00FF; width: 100%;
+ border-style: solid; border-top-width: 0.1em; border-bottom-width: 0.1em; border-right: none;
+ border-left: none; padding: 0.1em; font-weight: bold; font-size: 160%; text-align: center;
+}
+h2 { color: #00007f; background-color: #e7e7ff; border-color: #000094; width: 100%;
+ border-style: solid; border-left: none; border-right: none; border-top-width: 0.1em; border-bottom-width: 0.1em;
+ padding: 0.1em;
+ font-weight: bold; font-size: 110%;
+}
+h3 { color: #00007f; padding: 0.2em; font-size: 110%;}
+h4, h5 { color: #000000; padding: 0.2em; font-size: 100%;}
+table { margin-top: 0.2em; margin-bottom: 0.2em; margin-left: 2em; margin-right: 2em;}
+caption { color: #7f0000; font-weight: bold;}
+th { background: #e7e7ff; padding-left: 0.2em; padding-right: 0.2em;}
+td { background: #f3f7ff; padding-left: 0.2em; padding-right: 0.2em;}
+code { color: #0000df;}
+dt { margin-top: 0.2em;}
+li { margin-top: 0.2em;}
+pre
+{ BACKGROUND-COLOR: #d0d0d0; BORDER-BOTTOM: medium none; BORDER-LEFT: medium none;
+ BORDER-RIGHT: medium none; BORDER-TOP: medium none; LINE-HEIGHT: 100%; MARGIN: 12px 12px 12px 12px;
+ PADDING-BOTTOM: 12px; PADDING-LEFT: 12px; PADDING-RIGHT: 12px; PADDING-TOP: 12px;
+ WHITE-SPACE: pre; WIDTH: 100%
+}
+--></style>
+
+<TITLE>BigDecimal:An extension library for Ruby</TITLE>
+</HEAD>
+<BODY BGCOLOR=#FFFFE0>
+<H1>BigDecimal(‰Â•Ï’·•‚“®­”“_‰‰ŽZ—pŠg’£ƒ‰ƒCƒuƒ‰ƒŠ)</H1>
+<DIV align="right"><A HREF="./bigdecimal_en.html">English</A></DIV><BR>
+BigDecimal ‚̓IƒuƒWƒFƒNƒgŽwŒü‚Ì‹­—͂ȃXƒNƒŠƒvƒgŒ¾Œê‚Å‚ ‚é Ruby ‚ɉ•ϒ·•‚“®¬”“_
+ŒvŽZ‹@”\‚ð’ljÁ‚·‚邽‚߂̊g’£ƒ‰ƒCƒuƒ‰ƒŠ‚Å‚·B
+Ruby ‚ɂ‚¢‚Ä‚ÌÚ‚µ‚¢“à—e‚͈ȉº‚ÌURL‚ðŽQÆ‚µ‚Ä‚­‚¾‚³‚¢B
+<UL>
+<LI><A HREF="http://www.ruby-lang.org/ja/">http://www.ruby-lang.org/ja/</A>FRubyŒöŽ®ƒy[ƒW</LI>
+<LI><A HREF="http://kahori.com/ruby/ring/">http://kahori.com/ruby/ring/</A>FRuby‚ÉŠÖ‚·‚éƒy[ƒW‚ð’H‚ê‚Ü‚·</LI>
+</UL>
+<hr>
+<H2>–ÚŽŸ</H2>
+<UL>
+<LI><A HREF="#INTRO">‚Í‚¶‚ß‚É</LI>
+<LI><A HREF="#SPEC">Žg—p•û–@‚ƃƒ\ƒbƒh‚̈ꗗ</A></LI>
+<LI><A HREF="#UNDEF">–³ŒÀA”ñ”Aƒ[ƒ‚̈µ‚¢</A></LI>
+<LI><A HREF="#STRUCT">“à•”\‘¢</A></LI>
+<LI><A HREF="#BASE">2i‚Æ10i</A></LI>
+<LI><A HREF="#PREC">ŒvŽZ¸“x‚ɂ‚¢‚Ä</A></LI>
+</UL>
+
+<HR>
+<A NAME="#INTRO">
+<H2>‚Í‚¶‚ß‚É</H2>
+Ruby ‚É‚Í Bignum ‚Æ‚¢‚¤ƒNƒ‰ƒX‚ª‚ ‚èA”•SŒ…‚Ì®”‚Å‚àŒvŽZ‚·‚邱‚Æ‚ª‚Å‚«‚Ü‚·B
+‚½‚¾A”CˆÓŒ…‚Ì•‚“®­”“_‰‰ŽZ—pƒNƒ‰ƒX‚ª–³‚¢‚悤‚Å‚·B‚»‚±‚ÅA
+”CˆÓŒ…‚Ì•‚“®­”“_‰‰ŽZ—pŠg’£ƒ‰ƒCƒuƒ‰ƒŠ BigDecimal ‚ð쬂µ‚Ü‚µ‚½B
+•s‹ï‡‚╌¾E’ñˆÄ‚ª‚ ‚éꇂǂµ‚Ç‚µA
+<A HREF="mailto:shigeo@tinyforest.gr.jp">shigeo@tinyforest.gr.jp</A>
+‚܂ł¨’m‚点‚­‚¾‚³‚¢B•s‹ï‡‚𒼂·‹C‚͑傢‚É‚ ‚è‚Ü‚·B‚½‚¾AŽžŠÔ‚Ȃǂ̊֌W‚Å–ñ‘©
+‚͂ł«‚Ü‚¹‚ñB‚Ü‚½AŒ‹‰Ê‚ɂ‚¢‚Ä‚à•ÛØ‚Å‚«‚é‚à‚̂ł͂ ‚è‚Ü‚¹‚ñB
+—\‚ßA‚²—¹³‚­‚¾‚³‚¢B
+<BR><BR>
+‚±‚̃vƒƒOƒ‰ƒ€‚ÍAŽ©—R‚É”z•zE‰ü•Ï‚µ‚Ä\‚¢‚Ü‚¹‚ñB‚½‚¾‚µA’˜ìŒ ‚Í•úŠü‚µ‚Ä‚¢‚Ü‚¹‚ñB
+”z•zE‰ü•Ï“™‚ÌŒ —˜‚Í Ruby ‚Ì‚»‚ê‚É€‚¶‚Ü‚·BÚ‚µ‚­‚Í README ‚ð“Ç‚ñ‚Å‚­‚¾‚³‚¢B
+
+<hr>
+<H2>ƒCƒ“ƒXƒg[ƒ‹‚ɂ‚¢‚Ä</H2>
+BigDecimal ‚ðŠÜ‚Þ Ruby ‚ÌÅV”Å‚Í<A HREF="http://www.ruby-lang.org/ja/">RubyŒöŽ®ƒy[ƒW</A>‚©‚çƒ_ƒEƒ“ƒ[ƒh‚Å‚«‚Ü‚·B
+ƒ_ƒEƒ“ƒ[ƒh‚µ‚½ÅV”Å‚ð‰ð“€‚µ‚½‚çA’Êí‚̃Cƒ“ƒXƒg[ƒ‹Žè‡‚ðŽÀs‚µ‚ĉº‚³‚¢B
+Ruby ‚ª³‚µ‚­ƒCƒ“ƒXƒg[ƒ‹‚³‚ê‚ê‚ÎA“¯Žž‚É BigDecimal ‚à—˜—p‚Å‚«‚邿‚¤‚ɂȂé‚Í‚¸‚Å‚·B
+ƒ\[ƒXƒtƒ@ƒCƒ‹‚Í
+bigdecimal.c,bigdecimal.h
+‚Ì‚QŒÂ‚݂̂ł·B<BR>
+
+<hr>
+<A NAME="#SPEC">
+<H2>Žg—p•û–@‚ƃƒ\ƒbƒh‚̈ꗗ</H2>
+uRuby‚ÍŠù‚É‘‚¯‚év‚Æ‚¢‚¤‘O’ñ‚ÅA
+<CODE><PRE>
+require 'bigdecimal'
+a=BigDecimal::new("0.123456789123456789")
+b=BigDecimal("123456.78912345678",40)
+c=a+b
+</PRE></CODE>
+<br>
+‚Æ‚¢‚¤‚悤‚ÈŠ´‚¶‚ÅŽg—p‚µ‚Ü‚·B
+
+<H3>ƒƒ\ƒbƒhˆê——</H3>
+ˆÈ‰º‚̃ƒ\ƒbƒh‚ª—˜—p‰Â”\‚Å‚·B
+u—LŒøŒ…”v‚Æ‚Í BigDecimal ‚ª¸“x‚ð•ÛØ‚·‚錅”‚Å‚·B
+‚Ò‚Á‚½‚è‚ł͂ ‚è‚Ü‚¹‚ñAŽáб‚Ì—]—T‚ðŽ‚Á‚ÄŒvŽZ‚³‚ê‚Ü‚·B
+‚Ü‚½A—Ⴆ‚΂R‚Qƒrƒbƒg‚̃VƒXƒeƒ€‚ł͂P‚Oi‚Å‚SŒ…–ˆ‚ÉŒvŽZ‚µ‚Ü‚·B]‚Á‚ÄAŒ»ó‚Å‚ÍA
+“à•”‚Ìu—LŒøŒ…”v‚Í‚S‚Ì”{”‚ƂȂÁ‚Ä‚¢‚Ü‚·B
+<P>
+ˆÈ‰º‚̃ƒ\ƒbƒhˆÈŠO‚É‚àA(C ‚ł͂Ȃ¢) Ruby ƒ\[ƒX‚ÌŒ`‚Å
+’ñ‹Ÿ‚³‚ê‚Ä‚¢‚é‚à‚Ì‚à‚ ‚è‚Ü‚·B—Ⴆ‚ÎA•¶Žš—ñ‚©‚ç BigDecimal ‚Ö‚Ì
+•ÏŠ·‚âA"0.xxxxxEn" ‚Æ‚¢‚¤Œ`Ž®‚ł͂Ȃ­ "nnnnn.mmmm" ‚ÌŒ`Ž®‚Ì•¶Žš—ñ
+‚Ö•ÏŠ·‚·‚郃\ƒbƒh“™‚ª‚ ‚è‚Ü‚·B—˜—p‚·‚é‚É‚Í
+<CODE><PRE>
+require "bigdecimal/util.rb"
+</PRE></CODE>
+‚̂悤‚É‚µ‚Ü‚·BÚ×‚Í util.rb ‚Ì“à—e‚ðŽQÆ‚µ‚ĉº‚³‚¢B
+
+<H4><U>ƒNƒ‰ƒXƒƒ\ƒbƒh</U></H4>
+<UL>
+<LI><B>new</B></LI><BLOCKQUOTE>
+V‚µ‚¢ BigDecimal ƒIƒuƒWƒFƒNƒg‚𶬂µ‚Ü‚·B<BR>
+a=BigDecimal::new(s[,n]) ‚Ü‚½‚Í<BR>
+a=BigDecimal(s[,n])<BR>
+s ‚͉Šú’l‚ð•¶Žš—ñ‚ÅŽw’肵‚Ü‚·D
+n ‚Í•K—v‚È—LŒøŒ…”ia ‚ÌÅ‘å—LŒøŒ…”j‚ð®”‚ÅŽw’肵‚Ü‚·B
+n ‚ª 0 ‚Ü‚½‚ÍÈ—ª‚³‚ꂽ‚Æ‚«‚ÍAn ‚Ì’l‚Í s ‚Ì—LŒøŒ…”‚Ƃ݂Ȃ³‚ê‚Ü‚·B
+s ‚Ì—LŒøŒ…”‚æ‚è n ‚ª¬‚³‚¢‚Æ‚«‚à n=0 ‚̂Ƃ«‚Æ“¯‚¶‚Å‚·B
+a ‚ÌÅ‘å—LŒøŒ…”‚Í n ‚æ‚èŽáб‘å‚¢’l‚ªÌ—p‚³‚ê‚Ü‚·B
+Å‘å—LŒøŒ…”‚͈ȉº‚̂悤‚ÈŠ„‚èŽZ‚ðŽÀs‚·‚邯‚«“™‚ɈӖ¡‚ðŽ‚¿‚Ü‚·B
+<CODE><PRE>
+BigDecimal("1") / BigDecimal("3") # => 0.3333333333 33E0
+BigDecimal("1",10) / BigDecimal("3",10) # => 0.3333333333 3333333333 33333333E0
+</PRE></CODE>
+‚½‚¾‚µAŒÂX‚̉‰ŽZ‚É‚¨‚¯‚éÅ‘å—LŒøŒ…” n ‚ÌŽæ‚舵‚¢‚Í«—ˆ‚̃o[ƒWƒ‡ƒ“‚Å
+Žáб•ÏX‚³‚ê‚é‰Â”\«‚ª‚ ‚è‚Ü‚·B
+</BLOCKQUOTE>
+
+<LI><B>mode</B></LI><BLOCKQUOTE>
+BigDecimal‚ÌŽÀsŒ‹‰Ê‚ð§Œä‚µ‚Ü‚·BˆÈ‰º‚ÌŽg—p•û–@‚ª’è‹`‚³‚ê‚Ä‚¢‚Ü‚·B
+<P>
+<B>[—áŠOˆ—]</B><P>
+ŒvŽZŒ‹‰Ê‚ª”ñ”(NaN)‚âƒ[ƒ‚É‚æ‚霎Z‚ɂȂÁ‚½‚Æ‚«‚̈—‚ð’è‹`‚·‚邱‚Æ‚ª‚Å‚«‚Ü‚·B
+<BLOCKQUOTE>
+f = BigDecimal::mode(BigDecimal::EXCEPTION_NaN,flag)<BR>
+f = BigDecimal::mode(BigDecimal::EXCEPTION_INFINITY,flag)<BR>
+f = BigDecimal::mode(BigDecimal::EXCEPTION_UNDERFLOW,flag)<BR>
+f = BigDecimal::mode(BigDecimal::EXCEPTION_OVERFLOW,flag)<BR>
+f = BigDecimal::mode(BigDecimal::EXCEPTION_ZERODIVIDE,flag)<BR>
+f = BigDecimal::mode(BigDecimal::EXCEPTION_ALL,flag)<BR>
+</BLOCKQUOTE>
+
+EXCEPTION_NaN ‚ÍŒ‹‰Ê‚ª NaN ‚ɂȂÁ‚½‚Æ‚«‚ÌŽw’è‚Å‚·B<BR>
+EXCEPTION_INFINITY ‚ÍŒ‹‰Ê‚ª–³ŒÀ‘å(}Infinity)‚ɂȂÁ‚½‚Æ‚«‚ÌŽw’è‚Å‚·B<BR>
+EXCEPTION_UNDERFLOW ‚ÍŽw”•”‚ªƒAƒ“ƒ_[ƒtƒ[‚·‚邯‚«‚ÌŽw’è‚Å‚·B<BR>
+EXCEPTION_OVERFLOW ‚ÍŽw”•”‚ªƒI[ƒo[ƒtƒ[‚·‚邯‚«‚ÌŽw’è‚Å‚·B<BR>
+EXCEPTION_ZERODIVIDE ‚̓[ƒ‚É‚æ‚銄‚èŽZ‚ðŽÀs‚µ‚½‚Æ‚«‚ÌŽw’è‚Å‚·B<BR>
+EXCEPTION_ALL ‚ÍA‰Â”\‚È‘S‚Ăɑ΂µ‚Ĉꊇ‚µ‚ÄÝ’è‚·‚邯‚«‚ÉŽg—p‚µ‚Ü‚·B<BR><BR>
+
+flag ‚ª true ‚̂Ƃ«‚ÍAŽw’肵‚½ó‘ԂɂȂÁ‚½‚Æ‚«‚É—áŠO‚ð”­s‚·‚邿‚¤‚ɂȂè‚Ü‚·B<BR>
+flag ‚ª falseiƒfƒtƒHƒ‹ƒgj‚È‚çA—áŠO‚Í”­s‚³‚ê‚Ü‚¹‚ñBŒvŽZŒ‹‰Ê‚͈ȉº‚̂悤‚ɂȂè‚Ü‚·B<BR>
+<BLOCKQUOTE>
+EXCEPTION_NaN ‚̂Ƃ«A”ñ”(NaN)<BR>
+EXCEPTION_INFINITY ‚̂Ƃ«A–³ŒÀ(+ or -Infinity)<BR>
+EXCEPTION_UNDERFLOW ‚̂Ƃ«Aƒ[ƒ<BR>
+EXCEPTION_OVERFLOW ‚̂Ƃ«A+Infinity ‚© -Infinity<BR>
+EXCEPTION_ZERODIVIDE ‚̂Ƃ«A+Infinity ‚© -Infinity<BR>
+</BLOCKQUOTE>
+EXCEPTION_INFINITYAEXCEPTION_OVERFLOWAEXCEPTION_ZERODIVIDE
+‚Í¡‚̂Ƃ±‚듯‚¶‚Å‚·B<BR>
+–ß‚è’l‚ÍAÝ’è‘O‚Ì’l‚Å‚·Bu’lv‚̈Ӗ¡‚ÍA—Ⴆ‚Î
+BigDecimal::EXCEPTION_NaN‚Æu’lv‚Ì & ‚ª ƒ[ƒˆÈŠO‚È‚ç‚Î
+EXCEPTION_NaN‚ªÝ’肳‚ê‚Ä‚¢‚邯‚¢‚¤ˆÓ–¡‚Å‚·B<BR>
+ˆø”‚ɳ‚µ‚­‚È‚¢‚à‚Ì‚ªŽw’肳‚ê‚½ê‡‚Í nil ‚ª•Ô‚è‚Ü‚·B
+
+<P>
+<B>[ŠÛ‚߈—Žw’è]</B><P>
+ŒvŽZ“r’†‚ÌŠÛ‚ß‘€ì‚ÌŽw’肪‚Å‚«‚Ü‚·B
+<BLOCKQUOTE>
+f = BigDecimal::mode(BigDecimal::ROUND_MODE,flag)
+</BLOCKQUOTE>
+‚ÌŒ`Ž®‚ÅŽw’肵‚Ü‚·B<BR>
+‚±‚±‚ÅAflag ‚͈ȉº(ЇŒÊ“à‚͑Ήž‚·‚éƒCƒ“ƒXƒ^ƒ“ƒXƒƒ\ƒbƒh)‚̈ê‚‚ðŽw’肵‚Ü‚·B
+<TABLE>
+<TR><TD>ROUND_UP</TD><TD>‘S‚ÄØ‚èã‚°‚Ü‚·B</TD></TR>
+<TR><TD>ROUND_DOWN</TD><TD>‘S‚ÄØ‚èŽÌ‚Ă܂·(truncate)B</TD></TR>
+<TR><TD>ROUND_HALF_UP</TD><TD>ŽlŽÌŒÜ“ü‚µ‚Ü‚·(ƒfƒtƒHƒ‹ƒg)B</TD></TR>
+<TR><TD>ROUND_HALF_DOWN</TD><TD>ŒÜŽÌ˜Z“ü‚µ‚Ü‚·B</TD></TR>
+<TR><TD>ROUND_HALF_EVEN</TD><TD>ŽlŽÌ˜Z“ü‚µ‚Ü‚·B‚T‚ÌŽž‚ÍãˆÊ‚PŒ…‚ªŠï”‚ÌŽž‚̂݌J‚èã‚°‚Ü‚·(Banker's rounding)B</TD></TR>
+<TR><TD>ROUND_CEILING</TD><TD>”’l‚̑傫‚¢•û‚ÉŒJ‚èã‚°‚Ü‚·(ceil)B</TD></TR>
+<TR><TD>ROUND_FLOOR</TD><TD>”’l‚̬‚³‚¢•û‚ÉŒJ‚艺‚°‚Ü‚·(floor)B</TD></TR>
+
+</TABLE>
+–ß‚è’l‚ÍŽw’èŒã‚Ì flag ‚Ì’l‚Å‚·B
+ˆø”‚É”’lˆÈŠO‚ªŽw’肳‚ê‚½ê‡‚Í nil ‚ª•Ô‚è‚Ü‚·B³‚µ‚­‚È‚¢ ROUND_MODE ‚ªŽw’肳‚ꂽ‚Æ‚«‚Í
+–³Ž‹‚³‚êAŒ»ó‚Ì ROUND_MODE ‚ª•Ô‚è‚Ü‚·B<BR>
+mode ƒƒ\ƒbƒh‚ł͊ۂߑ€ì‚̈ʒu‚ðƒ†[ƒU‚ªŽw’è‚·‚邱‚Ƃ͂ł«‚Ü‚¹‚ñB
+ŠÛ‚ß‘€ì‚ƈʒu‚ðŽ©•ª‚ŧŒä‚µ‚½‚¢ê‡‚Í truncate/round/ceil/floor ‚â
+add/sub/mult/div ‚Æ‚¢‚Á‚½ƒCƒ“ƒXƒ^ƒ“ƒXƒƒ\ƒbƒh‚ðŽg—p‚µ‚ĉº‚³‚¢B
+</BLOCKQUOTE>
+<LI><B>limit([n])</B></LI><BLOCKQUOTE>
+¶¬‚³‚ê‚éBigDecimalƒIƒuƒWƒFƒNƒg‚Ìő包”‚ðnŒ…‚ɧŒÀ‚µ‚Ü‚·B
+–ß‚è’l‚ÍÝ’è‚·‚é‘O‚Ì’l‚Å‚·BÝ’è’l‚̃fƒtƒHƒ‹ƒg’l‚Í‚O‚ÅAŒ…”–³§ŒÀ‚Æ‚¢‚¤ˆÓ–¡‚Å‚·B
+n ‚ðŽw’肵‚È‚¢ê‡‚ÍAŒ»ó‚Ìő包”‚ª•Ô‚è‚Ü‚·B<BR>
+ŒvŽZ‚ð‘±s‚·‚éŠÔ‚ÉA”Žš‚ÌŒ…”‚ª–³§ŒÀ‚É‘‚¦‚Ä‚µ‚Ü‚¤‚悤‚Èê‡
+ limit ‚Å—\‚ߌ…”‚ð§ŒÀ‚Å‚«‚Ü‚·B‚±‚Ìê‡ BigDecimal.mode ‚ÅŽw’肳‚ꂽ
+ŠÛ‚߈—‚ªŽÀs‚³‚ê‚Ü‚·B
+‚½‚¾‚µAŽÀÛ‚É‚Í n ‚æ‚èŽáб‘å‚«‚¢
+Œ…”‚ªŠm•Û‚³‚ê‚Ü‚·B‚Ü‚½Alimit ‚É‚æ‚錅”§ŒÀ‚Í(–³§ŒÀ‚𜂢‚Ä)A
+ƒCƒ“ƒXƒ^ƒ“ƒXƒƒ\ƒbƒh (truncate/round/ceil/floor/add/sub/mult/div) ‚æ‚è
+—D悳‚ê‚é‚̂ŒˆÓ‚ª•K—v‚Å‚·B<BR>
+mf = BigDecimal::limit(n)<BR>
+</BLOCKQUOTE>
+
+<LI><B>double_fig</B></LI><BLOCKQUOTE>
+Ruby ‚Ì Float ƒNƒ‰ƒX‚ª•ÛŽ‚Å‚«‚é—LŒø”Žš‚Ì”‚ð•Ô‚µ‚Ü‚·B
+<CODE><PRE>
+ p BigDecimal::double_fig # ==> 20 (depends on the CPU etc.)
+</PRE></CODE>
+double_fig‚͈ȉº‚Ì C ƒvƒƒOƒ‰ƒ€‚ÌŒ‹‰Ê‚Æ“¯‚¶‚Å‚·B
+<CODE><PRE>
+ double v = 1.0;
+ int double_fig = 0;
+ while(v + 1.0 > 1.0) {
+ ++double_fig;
+ v /= 10;
+ }
+</PRE></CODE>
+</BLOCKQUOTE>
+
+<LI><B>BASE</B></LI><BLOCKQUOTE>
+“à•”‚ÅŽg—p‚³‚ê‚éŠî”‚Ì’l‚Å‚·B®”‚ª 32 ƒrƒbƒg‚̈—Œn‚Å‚Í10000‚Å‚·B<BR>
+b = BigDecimal::BASE<BR>
+</BLOCKQUOTE>
+</UL>
+
+<H4><U>ƒCƒ“ƒXƒ^ƒ“ƒXƒƒ\ƒbƒh</U></H4>
+<UL>
+<LI><B>+</B></LI><BLOCKQUOTE>
+‰ÁŽZic = a + bj<BR>
+c ‚̸“x‚ɂ‚¢‚Ä‚Íu<A HREF="#PREC">ŒvŽZ¸“x‚ɂ‚¢‚Ä</A>v‚ðŽQÆ‚µ‚Ä‚­‚¾‚³‚¢B
+</BLOCKQUOTE>
+
+<LI><B>-</B></LI><BLOCKQUOTE>
+Œ¸ŽZic = a - bjA‚Ü‚½‚Í•„†”½“]ic = -aj<BR>
+c ‚̸“x‚ɂ‚¢‚Ä‚Íu<A HREF="#PREC">ŒvŽZ¸“x‚ɂ‚¢‚Ä</A>v‚ðŽQÆ‚µ‚Ä‚­‚¾‚³‚¢B
+
+</BLOCKQUOTE>
+<LI><B>*</B></LI><BLOCKQUOTE>
+æŽZ(c = a * b)<BR>
+c‚̸“x‚Í(a‚̸“x)+(b‚̸“x)’ö“x‚Å‚·B<br>
+Ú‚µ‚­‚Íu<A HREF="#PREC">ŒvŽZ¸“x‚ɂ‚¢‚Ä</A>v‚ðŽQÆ‚µ‚Ä‚­‚¾‚³‚¢B
+
+</BLOCKQUOTE>
+<LI><B>/</B></LI><BLOCKQUOTE>
+œŽZ(c = a / b)<BR>
+c ‚̸“x‚ɂ‚¢‚Ä‚Íu<A HREF="#PREC">ŒvŽZ¸“x‚ɂ‚¢‚Ä</A>v‚ðŽQÆ‚µ‚Ä‚­‚¾‚³‚¢B
+
+</BLOCKQUOTE>
+
+<LI><B>add(b,n)</B></LI><BLOCKQUOTE>
+ˆÈ‰º‚̂悤‚ÉŽg—p‚µ‚Ü‚·B<BR>
+c = a.add(b,n)<BR>
+c = a + b ‚ðÅ‘å‚Å n Œ…‚܂ŌvŽZ‚µ‚Ü‚·B
+a + b ‚̸“x‚ª n ‚æ‚è‘å‚«‚¢‚Æ‚«‚Í BigDecimal.mode ‚ÅŽw’肳‚ꂽ•û–@‚ÅŠÛ‚ß‚ç‚ê‚Ü‚·B
+
+</BLOCKQUOTE>
+<LI><B>sub(b,n)</B></LI><BLOCKQUOTE>
+ˆÈ‰º‚̂悤‚ÉŽg—p‚µ‚Ü‚·B<BR>
+c = a.sub(b,n)<BR>
+c = a - b ‚ðÅ‘å‚Å n Œ…‚܂ŌvŽZ‚µ‚Ü‚·B
+a - b ‚̸“x‚ª n ‚æ‚è‘å‚«‚¢‚Æ‚«‚Í BigDecimal.mode ‚ÅŽw’肳‚ꂽ•û–@‚ÅŠÛ‚ß‚ç‚ê‚Ü‚·B
+
+</BLOCKQUOTE>
+<LI><B>mult(b,n)</B></LI><BLOCKQUOTE>
+ˆÈ‰º‚̂悤‚ÉŽg—p‚µ‚Ü‚·B<BR>
+c = a.mult(b,n)<BR>
+c = a * b ‚ðÅ‘å‚Å n Œ…‚܂ŌvŽZ‚µ‚Ü‚·B
+a * b ‚̸“x‚ª n ‚æ‚è‘å‚«‚¢‚Æ‚«‚Í BigDecimal.mode ‚ÅŽw’肳‚ꂽ•û–@‚ÅŠÛ‚ß‚ç‚ê‚Ü‚·B
+
+</BLOCKQUOTE>
+<LI><B>div(b[,n])</B></LI><BLOCKQUOTE>
+ˆÈ‰º‚̂悤‚ÉŽg—p‚µ‚Ü‚·B<BR>
+c = a.div(b,n)<BR>
+c = a / b ‚ðÅ‘å‚Å n Œ…‚܂ŌvŽZ‚µ‚Ü‚·B
+a / b ‚̸“x‚ª n ‚æ‚è‘å‚«‚¢‚Æ‚«‚Í BigDecimal.mode ‚ÅŽw’肳‚ꂽ•û–@‚ÅŠÛ‚ß‚ç‚ê‚Ü‚·B<BR>
+n ‚ªÈ—ª‚³‚ꂽ‚Æ‚«‚Í Float#div ‚Æ“¯—l‚ÉŒ‹‰Ê‚ª®”(BigDecimal)‚ɂȂè‚Ü‚·B
+</BLOCKQUOTE>
+
+<LI><B>fix</B></LI><BLOCKQUOTE>
+a ‚̬”“_ˆÈ‰º‚ÌØ‚èŽÌ‚ÄB<BR>
+c = a.fix
+</BLOCKQUOTE>
+<LI><B>frac</B></LI><BLOCKQUOTE>
+a ‚Ì®”•”•ª‚ÌØ‚èŽÌ‚ÄB<BR>
+c = a.frac
+</BLOCKQUOTE>
+
+<LI><B>floor[(n)]</B></LI><BLOCKQUOTE>
+c = a.floor<BR>
+a ˆÈ‰º‚Ìő宔iBigDecimal ’lj‚ð•Ô‚µ‚Ü‚·B
+<CODE><PRE>
+c = BigDecimal("1.23456").floor # ==> 1
+c = BigDecimal("-1.23456").floor # ==> -2
+</PRE></CODE>
+ˆÈ‰º‚̂悤‚Ɉø” n ‚ð—^‚¦‚邱‚Æ‚à‚Å‚«‚Ü‚·B<BR>
+n>=0 ‚È‚çA¬”“_ˆÈ‰º n+1 ˆÊ‚Ì”Žš‚ð‘€ì‚µ‚Ü‚·(­”“_ˆÈ‰º‚ðAÅ‘å n Œ…‚É‚µ‚Ü‚·)B<BR>
+n ‚ª•‰‚̂Ƃ«‚ͬ”“_ˆÈã n Œ…–Ú‚ð‘€ì‚µ‚Ü‚·(¬”“_ˆÊ’u‚©‚ç¶‚É­‚È‚­‚Æ‚à n ŒÂ‚Ì 0 ‚ª•À‚т܂·)B<BR>
+<CODE><PRE>
+ c = BigDecimal("1.23456").floor(4) # ==> 1.2345
+ c = BigDecimal("15.23456").floor(-1) # ==> 10.0
+</PRE></CODE>
+
+</BLOCKQUOTE>
+<LI><B>ceil[(n)]</B></LI><BLOCKQUOTE>
+c = a.ceil<BR>
+a ˆÈã‚Ì®”‚Ì‚¤‚¿Ałଂ³‚¢®”‚ðŒvŽZ‚µA‚»‚Ì’liBigDecimal ’lj‚ð•Ô‚µ‚Ü‚·B
+<CODE><PRE>
+c = BigDecimal("1.23456").ceil # ==> 2
+c = BigDecimal("-1.23456").ceil # ==> -1
+</PRE></CODE>
+
+ˆÈ‰º‚̂悤‚Ɉø”‚ð—^‚¦‚ÄA¬”“_ˆÈ‰º n+1 ˆÊ‚Ì”Žš‚ð‘€ì‚·‚邱‚Æ‚à‚Å‚«‚Ü‚·B<BR>
+n>=0 ‚È‚çA¬”“_ˆÈ‰º n+1 ˆÊ‚Ì”Žš‚ð‘€ì‚µ‚Ü‚·(­”“_ˆÈ‰º‚ðAÅ‘å n Œ…‚É‚µ‚Ü‚·)B<BR>
+ n ‚ª•‰‚̂Ƃ«‚ͬ”“_ˆÈã n Œ…–Ú‚ð‚ð‘€ì‚µ‚Ü‚·(¬”“_ˆÊ’u‚©‚ç¶‚É­‚È‚­‚Æ‚à n ŒÂ‚Ì 0 ‚ª•À‚т܂·)B<BR>
+<CODE><PRE>
+ c = BigDecimal("1.23456").ceil(4) # ==> 1.2346
+ c = BigDecimal("15.23456").ceil(-1) # ==> 20.0
+</PRE></CODE>
+
+</BLOCKQUOTE>
+<LI><B>round[(n[,b])]</B></LI><BLOCKQUOTE>
+c = a.round<BR>
+
+ƒNƒ‰ƒXƒƒ\ƒbƒh BigDecimal::mode(BigDecimal::ROUND_MODE,flag) ‚ÅŽw’肵‚½
+ROUND_MODE ‚É]‚Á‚ÄŠÛ‚ß‘€ì‚ðŽÀs‚µ‚Ü‚·B
+BigDecimal::mode(BigDecimal::ROUND_MODE,flag) ‚ʼn½‚àŽw’肹‚¸A‚©‚ÂAˆø”
+‚ðŽw’肵‚È‚¢ê‡‚Íu¬”“_ˆÈ‰º‘æˆêˆÊ‚Ì”‚ðŽlŽÌŒÜ“ü‚µ‚Ä®”iBigDecimal ’ljv‚É‚µ‚Ü‚·B<BR>
+<CODE><PRE>
+ c = BigDecimal("1.23456").round # ==> 1
+ c = BigDecimal("-1.23456").round # ==> -1
+</PRE></CODE>
+
+ˆÈ‰º‚̂悤‚Ɉø”‚ð—^‚¦‚ÄA¬”“_ˆÈ‰º n+1 ˆÊ‚Ì”Žš‚ð‘€ì‚·‚邱‚Æ‚à‚Å‚«‚Ü‚·B<BR>
+n ‚ª³‚ÌŽž‚ÍA¬”“_ˆÈ‰º n+1 ˆÊ‚Ì”Žš‚ðŠÛ‚߂܂·(­”“_ˆÈ‰º‚ðAÅ‘å n Œ…‚É‚µ‚Ü‚·)B<BR>
+n ‚ª•‰‚̂Ƃ«‚ͬ”“_ˆÈã n Œ…–Ú‚ðŠÛ‚߂܂·(¬”“_ˆÊ’u‚©‚ç¶‚É­‚È‚­‚Æ‚à n ŒÂ‚Ì 0 ‚ª•À‚т܂·)B
+<CODE><PRE>
+c = BigDecimal("1.23456").round(4) # ==> 1.2346
+c = BigDecimal("15.23456").round(-1) # ==> 20.0
+</PRE></CODE>
+‚Q”Ԗڂ̈ø”‚ðŽw’è‚·‚邯ABigDecimal#mode ‚ÌŽw’è‚𖳎‹‚µ‚ÄAŽw’肳‚ꂽ•û–@‚Å
+ŠÛ‚ß‘€ì‚ðŽÀs‚µ‚Ü‚·B
+<CODE><PRE>
+c = BigDecimal("1.23456").round(3,BigDecimal::ROUND_HALF_EVEN) # ==> 1.234
+c = BigDecimal("1.23356").round(3,BigDecimal::ROUND_HALF_EVEN) # ==> 1.234
+</PRE></CODE>
+
+</BLOCKQUOTE>
+<LI><B>truncate</B></LI><BLOCKQUOTE>
+c = a.truncate<BR>
+¬”“_ˆÈ‰º‚Ì”‚ðØ‚èŽÌ‚ĂĮ”iBigDecimal ’lj‚É‚µ‚Ü‚·B<BR>
+ˆÈ‰º‚̂悤‚Ɉø”‚ð—^‚¦‚ÄA¬”“_ˆÈ‰º n+1 ˆÊ‚Ì”Žš‚ð‘€ì‚·‚邱‚Æ‚à‚Å‚«‚Ü‚·B<BR>
+n ‚ª³‚ÌŽž‚ÍA¬”“_ˆÈ‰º n+1 ˆÊ‚Ì”Žš‚ðØ‚èŽÌ‚Ă܂·(­”“_ˆÈ‰º‚ðAÅ‘å n Œ…‚É‚µ‚Ü‚·)B
+n ‚ª•‰‚̂Ƃ«‚ͬ”“_ˆÈã n Œ…–Ú‚ð‚ð‘€ì‚µ‚Ü‚·(¬”“_ˆÊ’u‚©‚ç¶‚É­‚È‚­‚Æ‚à n ŒÂ‚Ì 0 ‚ª•À‚т܂·)B<BR>
+<CODE><PRE>
+c = BigDecimal("1.23456").truncate(4) # ==> 1.2345
+c = BigDecimal("15.23456").truncate(-1) # ==> 10.0
+</PRE></CODE>
+</BLOCKQUOTE>
+
+</BLOCKQUOTE>
+<LI><B>abs</B></LI><BLOCKQUOTE>
+‚‚Ìâ‘Î’l<BR>
+c = a.abs<BR>
+
+</BLOCKQUOTE>
+<LI><B>to_i</B></LI><BLOCKQUOTE>
+­”“_ˆÈ‰º‚ðØ‚èŽÌ‚ĂĮ”‚ɕϊ·‚µ‚Ü‚·B<BR>
+i = a.to_i<BR>
+i ‚Í’l‚ɉž‚¶‚Ä Fixnum ‚© Bignum ‚ɂȂè‚Ü‚·B
+a ‚ª Infinity ‚â NaN ‚̂Ƃ«Ai ‚Í nil ‚ɂȂè‚Ü‚·B
+</BLOCKQUOTE>
+<LI><B>to_f</B></LI><BLOCKQUOTE>
+Float ƒIƒuƒWƒFƒNƒg‚ɕϊ·‚µ‚Ü‚·B
+‚æ‚è‚«‚ßׂ©‚¢’l‚ª•K—v‚È‚ç‚Î split ƒƒ\ƒbƒh‚ð—˜—p‚µ‚Ä
+‚­‚¾‚³‚¢B
+</BLOCKQUOTE>
+<LI><B>to_s[(n)]</B></LI><BLOCKQUOTE>
+•¶Žš—ñ‚ɕϊ·‚µ‚Ü‚·("0.xxxxxEn"‚ÌŒ`‚ɂȂè‚Ü‚·jB<BR>
+s = a.to_s<BR>
+n ‚ªŽw’肳‚ꂽ‚Æ‚«‚ÍA‰¼”•”•ª‚ð n Œ…–ˆ‚É‹ó”’‚Å‹æØ‚è‚Ü‚·B<BR>
+s = a.to_s(n)
+</BLOCKQUOTE>
+<LI><B>exponent</B></LI><BLOCKQUOTE>
+Žw”•”‚ð®”’l‚ŕԂµ‚Ü‚·B
+n = a.exponent <BR>
+‚Í a ‚Ì’l‚ª 0.xxxxxxx*10**n ‚ðˆÓ–¡‚µ‚Ü‚·B
+</BLOCKQUOTE>
+
+<LI><B>prec</B></LI><BLOCKQUOTE>
+n,m = a.prec<BR>
+a ‚Ì—LŒø”Žš (n) ‚ÆÅ‘å—LŒø”Žš (m) ‚Ì”z—ñ‚ð•Ô‚µ‚Ü‚·B
+
+</BLOCKQUOTE>
+
+<LI><B>sign</B></LI><BLOCKQUOTE>
+’l‚ª³(sign &gt; 0)A•‰(sign &lt; 0)A‚»‚Ì‘¼(sigh==0)‚Å‚ ‚é‚©‚Ìî•ñ‚ð•Ô‚µ‚Ü‚·B
+n = a.sign <BR>
+‚Æ‚µ‚½‚Æ‚« n ‚Ì’l‚Í a ‚ªˆÈ‰º‚̂Ƃ«‚ðˆÓ–¡‚µ‚Ü‚·B<BR>
+() ‚Ì’†‚Ì”Žš‚ÍAŽÀÛ‚Ì’l‚Å‚·(<A HREF="#STRUCT">u“à•”\‘¢v</A>‚ðŽQÆ)B<BR>
+n = BigDecimal::SIGN_NaN(0) : a ‚Í NaN<BR>
+n = BigDecimal::SIGN_POSITIVE_ZERO(1) : a ‚Í +0<BR>
+n = BigDecimal::SIGN_NEGATIVE_ZERO(-1) : a ‚Í -0<BR>
+n = BigDecimal::SIGN_POSITIVE_FINITE(2) : a ‚ͳ‚Ì’l<BR>
+n = BigDecimal::SIGN_NEGATIVE_FINITE(-2) : a ‚Í•‰‚Ì’l<BR>
+n = BigDecimal::SIGN_POSITIVE_INFINITE(3) : a ‚Í+Infinity<BR>
+n = BigDecimal::SIGN_NEGATIVE_INFINITE(-3) : a ‚Í-Infinity<BR>
+
+</BLOCKQUOTE>
+<LI><B>nan?</B></LI><BLOCKQUOTE>
+a.nan? ‚Í a ‚ªNaN‚̂Ƃ«^‚ð•Ô‚µ‚Ü‚·B
+</BLOCKQUOTE>
+<LI><B>infinite?</B></LI><BLOCKQUOTE>
+a.infinite? ‚Í a ‚ª+‡‚̂Ƃ« 1 A-‡‚̂Ƃ«‚Í -1A‚»‚êˆÈŠO‚̂Ƃ«‚Í nil ‚ð•Ô‚µ‚Ü‚·B
+</BLOCKQUOTE>
+<LI><B>finite?</B></LI><BLOCKQUOTE>
+a.finite? ‚Í a ‚ª‡‚Ü‚½‚Í NaN ‚łȂ¢‚Æ‚«^‚ð•Ô‚µ‚Ü‚·B
+</BLOCKQUOTE>
+
+<LI><B>zero?</B></LI><BLOCKQUOTE>
+a ‚ª 0 ‚È‚ç true ‚ɂȂè‚Ü‚·B<BR>
+c = a.zero?
+</BLOCKQUOTE>
+<LI><B>nonzero?</B></LI><BLOCKQUOTE>
+a ‚ª 0 ‚È‚ç nilA0 ˆÈŠO‚È‚ç a ‚»‚Ì‚à‚Ì‚ª•Ô‚è‚Ü‚·B<BR>
+c = a.nonzero?
+
+</BLOCKQUOTE>
+<LI><B>split</B></LI><BLOCKQUOTE>
+BigDecimal ’l‚ð 0.xxxxxxx*10**n ‚Æ•\Œ»‚µ‚½‚Æ‚«‚ÉA•„†iNaN‚̂Ƃ«‚Í
+0A‚»‚êˆÈŠO‚Í+1‚©-1‚ɂȂè‚Ü‚·jA
+‰¼”•”•ª‚Ì•¶Žš—ñi"xxxxxxx"j‚ÆAŠî”i10jAX‚ÉŽw” n ‚ð”z—ñ‚Å
+•Ô‚µ‚Ü‚·B<BR>
+a=BigDecimal::new("3.14159265")<BR>
+f,x,y,z = a.split<BR>
+‚Æ‚·‚邯Af=+1Ax="314159265"Ay=10Az=1‚ɂȂè‚Ü‚·B<BR>
+]‚Á‚ÄA<BR>
+s = "0."+x<BR>
+b = f*(s.to_f)*(y**z)<BR>
+‚Å Float ‚ɕϊ·‚·‚邱‚Æ‚ª‚Å‚«‚Ü‚·B
+</BLOCKQUOTE>
+<LI><B>inspect</B></LI><BLOCKQUOTE>
+ƒfƒoƒbƒOo—͂Ɏg—p‚³‚ê‚Ü‚·B<BR>
+p a=BigDecimal::new("3.14",10)<BR>
+‚Æ‚·‚邯A[0x112344:'0.314E1',4(12)]‚̂悤‚Éo—Í‚³‚ê‚Ü‚·B
+ʼn‚Ì16i”‚̓IƒuƒWƒFƒNƒg‚̃AƒhƒŒƒXAŽŸ‚Ì '0.314E1' ‚Í’lA
+ŽŸ‚Ì4‚ÍŒ»Ý‚Ì—LŒøŒ…”(•\ަ‚æ‚èŽáб‘å‚«‚¢‚±‚Æ‚ª‚ ‚è‚Ü‚·)A
+ÅŒã‚̓IƒuƒWƒFƒNƒg‚ªŽæ‚蓾‚éő包”‚ɂȂè‚Ü‚·B
+</BLOCKQUOTE>
+<LI><B>**</B></LI><BLOCKQUOTE>
+a ‚Ì n æ‚ðŒvŽZ‚µ‚Ü‚·B‚Ž‚Í®”B<BR>
+c = a ** n<BR>
+Œ‹‰Ê‚Æ‚µ‚Ä c ‚Ì—LŒøŒ…‚Í a ‚Ì n ”{ˆÈã‚ɂȂé‚̂ŒˆÓB
+</BLOCKQUOTE>
+<LI><B>power</B></LI><BLOCKQUOTE>
+** ‚Æ“¯‚¶‚ÅAa ‚Ì n æ‚ðŒvŽZ‚µ‚Ü‚·B‚Ž‚Í®”B<BR>
+c = a.power(n)<BR>
+Œ‹‰Ê‚Æ‚µ‚Ä c ‚Ì—LŒøŒ…‚Í a ‚Ì n ”{ˆÈã‚ɂȂé‚̂ŒˆÓB
+</BLOCKQUOTE>
+<LI><B>sqrt</B></LI><BLOCKQUOTE>
+a‚Ì—LŒøŒ… n Œ…‚Ì•½•ûªin ‚Ì•½•ûª‚ł͂ ‚è‚Ü‚¹‚ñj‚ð
+ƒjƒ…[ƒgƒ“–@‚ÅŒvŽZ‚µ‚Ü‚·B<BR>
+c = a.sqrt(n)<BR>
+</BLOCKQUOTE>
+
+<LI><B>divmod,quo,modulo,%,remainder</B></LI><BLOCKQUOTE>
+Úׂ͑Ήž‚·‚é Float ‚ÌŠeƒƒ\ƒbƒh‚ðŽQÆ‚µ‚ĉº‚³‚¢B
+</BLOCKQUOTE>
+
+<LI><B>&lt=&gt</B></LI><BLOCKQUOTE>
+a==b ‚È‚ç 0Aa &gt b ‚È‚ç 1Aa &lt b ‚È‚ç -1 ‚ɂȂè‚Ü‚·B<BR>
+c = a &lt=&gt b
+</BLOCKQUOTE>
+</UL>
+Œã‚ÍA“Ç‚ñ‚ÅŽš‚Ì”@‚­‚Å‚·B<BR>
+<UL>
+<LI><B>==</B></LI>
+<LI><B>===</B></LI>
+u==v‚Æ“¯‚¶‚Å‚·‚ª case •¶‚ÅŽg—p‚³‚ê‚Ü‚·B
+<LI><B>!=</B></LI>
+<LI><B>&lt</B></LI>
+<LI><B>&lt=</B></LI>
+<LI><B>&gt</B></LI>
+<LI><B>&gt=</B></LI>
+</UL>
+
+<H4><U>(•]‰¿’iŠK‚Ì)ƒNƒ‰ƒXƒƒ\ƒbƒh</U></H4>
+ˆÈ‰º‚̃Nƒ‰ƒXƒƒ\ƒbƒh‚ÍA‚Ü‚¾•]‰¿’iŠK‚Å‚·‚Ì‚ÅA’Êí‚Å‚Í
+Žg—p‚Å‚«‚Ü‚¹‚ñBŽg—p‚·‚é‚É‚Í bigdecimal.c ‚Ì
+u/* #define ENABLE_TRIAL_METHOD */v
+‚̃Rƒƒ“ƒg‚ðŠO‚µAăRƒ“ƒpƒCƒ‹EăCƒ“ƒXƒg[ƒ‹‚ª•K—v‚Å‚·B
+
+<UL>
+<LI><B>E</B></LI><BLOCKQUOTE>
+Ž©‘R‘Δ‚Ì’êe(=2.718281828....)‚ðŒvŽZ‚µ‚Ü‚·i³’¼‚ɃeƒCƒ‰[“WŠJ‚ÅjB<BR>
+e = BigDecimal::E(n)<BR>
+n‚Í•K—v‚È—LŒøŒ…”‚ð®”‚ÅŽw’肵‚Ü‚·B
+</BLOCKQUOTE>
+<LI><B>PI</B></LI><BLOCKQUOTE>
+‰~Žü—¦(=3.14159265358979....)‚ðŒvŽZ‚µ‚Ü‚·iJDMachin‚ÌŒöŽ®‚ð—p‚¢‚Ü‚·jB<BR>
+e = BigDecimal::PI(n)<BR>
+n ‚Í•K—v‚È—LŒøŒ…”‚ð®”‚ÅŽw’肵‚Ü‚·B
+</BLOCKQUOTE>
+</UL>
+
+<H4><U>(•]‰¿’iŠK‚Ì)ƒCƒ“ƒXƒ^ƒ“ƒXƒƒ\ƒbƒh</U></H4>
+ˆÈ‰º‚̃Cƒ“ƒXƒ^ƒ“ƒXƒƒ\ƒbƒh‚ÍA‚Ü‚¾•]‰¿’iŠK‚Å‚·‚Ì‚ÅA’Êí‚Å‚Í
+Žg—p‚Å‚«‚Ü‚¹‚ñBŽg—p‚·‚é‚É‚Í bigdecimal.c ‚Ì
+u/* #define ENABLE_TRIAL_METHOD */v
+‚̃Rƒƒ“ƒg‚ðŠO‚µ‚ÄAăRƒ“ƒpƒCƒ‹EăCƒ“ƒXƒg[ƒ‹‚ª•K—v‚Å‚·B
+<UL>
+<LI><B>sincos</B></LI><BLOCKQUOTE>
+a ‚Ì—LŒøŒ… n Œ…‚Ì sin ‚Æ cos ‚𓯎ž‚ÉiƒeƒCƒ‰[“WŠJ‚ÅjŒvŽZ‚µ‚ÄA
+ sin ‚Æ cos ‚Ì”z—ñ‚ð•Ô‚µ‚Ü‚·B
+n ‚Í•K—v‚È—LŒøŒ…”‚Å‚·i n ‚Ì sin ‚â cos ‚ðŒvŽZ‚·‚é‚킯‚ł͂ ‚è‚Ü‚¹‚ñjB
+<BR>
+sin,cos = a.sincos(n)<BR>
+|a| &lt; 2*3.1415....‚łȂ¢‚Ƴ‚µ‚¢“š‚¦‚ðŒvŽZ‚Å‚«‚È‚¢‚±‚Æ‚à‚ ‚è‚Ü‚·B
+</BLOCKQUOTE>
+<LI><B>exp</B></LI><BLOCKQUOTE>
+Ž©‘R‘Δ‚Ì’êe(=2.718281828....)‚Ì a æ‚ðŒvŽZ‚µ‚Ü‚·B<BR>
+c = a.exp(n)<BR>
+n ‚Í•K—v‚È—LŒøŒ…”‚Å‚·B
+</BLOCKQUOTE>
+</UL>
+
+<H3>coerce‚ɂ‚¢‚Ä</H3>
+BigDecimal ƒIƒuƒWƒFƒNƒg‚ªŽZp‰‰ŽZŽq‚̶‚É‚ ‚邯‚«‚ÍABigDecimal ƒIƒuƒWƒFƒNƒg‚ª
+‰E‚É‚ ‚éƒIƒuƒWƒFƒNƒg‚ð(•K—v‚È‚ç) BigDecimal ‚ɕϊ·‚µ‚Ä‚©‚çŒvŽZ‚µ‚Ü‚·B
+]‚Á‚ÄABigDecimal ƒIƒuƒWƒFƒNƒgˆÈŠO‚Å‚à”’l‚ðˆÓ–¡‚·‚é‚à‚̂Ȃç‰E‚É’u‚¯‚Î
+‰‰ŽZ‚͉”\‚Å‚·B<BR>
+‚½‚¾‚µA•¶Žš—ñ‚Íi’Êíj”’l‚ÉŽ©“®•ÏŠ·‚·‚邱‚Ƃ͂ł«‚Ü‚¹‚ñB
+•¶Žš—ñ‚ð”’l‚ÉŽ©“®•ÏŠ·‚ÉŽ©“®•ÏŠ·‚µ‚½‚¢ê‡‚Í bigfloat.c ‚Ì
+u/* #define ENABLE_NUMERIC_STRING */v‚̃Rƒƒ“ƒg‚ðŠO‚µ‚Ä‚©‚çA
+ăRƒ“ƒpƒCƒ‹AăCƒ“ƒXƒg[ƒ‹‚·‚é•K—v‚ª‚ ‚è‚Ü‚·B
+•¶Žš—ñ‚Å”’l‚ð—^‚¦‚éꇂ͒ˆÓ‚ª•K—v‚Å‚·B”’l‚ɕϊ·‚Å‚«‚È‚¢•¶Žš‚ª‚ ‚邯A
+’P‚ɕϊ·‚ðŽ~‚߂邾‚¯‚ŃGƒ‰[‚ɂ͂Ȃè‚Ü‚¹‚ñB"10XX"‚È‚ç‚P‚OA"XXXX"‚Í‚O
+‚ƈµ‚í‚ê‚Ü‚·B<BR>
+<CODE><PRE>
+ a = BigDecimal.E(20)
+ c = a * "0.123456789123456789123456789" # •¶Žš‚ð BigDecimal ‚ɕϊ·‚µ‚Ä‚©‚çŒvŽZ
+</PRE></CODE>
+–³ŒÀ‘å‚â”ñ”‚ð•\‚·•¶Žš‚Æ‚µ‚ÄA"Infinity"A"+Infinity"A"-Infinity"A"NaN"
+‚àŽg—p‚Å‚«‚Ü‚·(‘å•¶ŽšE¬•¶Žš‚ð‹æ•Ê‚µ‚Ü‚·)B‚½‚¾‚µAmode ƒƒ\ƒbƒh‚Å false ‚ð
+Žw’肵‚½ê‡‚Í—áŠO‚ª”­¶‚µ‚Ü‚·B
+<BR>
+‚Ü‚½ABigDecimalƒNƒ‰ƒX‚Í coerceiRuby–{ŽQÆj‚ðƒTƒ|[ƒg‚µ‚Ä‚¢‚Ü‚·B
+]‚Á‚ÄABigDecimal ƒIƒuƒWƒFƒNƒg‚ª‰E‚É‚ ‚éꇂà‘å’ï‚Í‘åä•v‚Å‚·B
+‚½‚¾AŒ»Ý‚Ì Ruby ƒCƒ“ƒ^ƒvƒŠƒ^‚ÌŽd—lãA•¶Žš—ñ‚ª¶‚É‚ ‚邯ŒvŽZ‚Å‚«‚Ü‚¹‚ñB<BR>
+<CODE><PRE>
+ a = BigDecimal.E(20)
+ c = "0.123456789123456789123456789" * a # ƒGƒ‰[
+</PRE></CODE>
+•K—v«‚ª‚ ‚邯‚ÍŽv‚¢‚Ü‚¹‚ñ‚ªA‚Ç‚¤‚µ‚Ä‚à‚ÆŒ¾‚¤l‚Í
+ String ƒIƒuƒWƒFƒNƒg‚ðŒp³‚µ‚½V‚½‚ȃNƒ‰ƒX‚ð쬂µ‚Ä‚©‚çA
+‚»‚̃Nƒ‰ƒX‚Å coerce ‚ðƒTƒ|[ƒg‚µ‚Ä‚­‚¾‚³‚¢B
+
+<hr>
+<A NAME="#UNDEF">
+<H2>–³ŒÀA”ñ”Aƒ[ƒ‚̈µ‚¢</H2>
+u–³ŒÀv‚Ƃ͕\Œ»‚Å‚«‚È‚¢‚­‚ç‚¢‘å‚«‚È”‚Å‚·B“Á•ʂɈµ‚¤‚½‚ß‚É
+ +Infinityi³‚Ì–³ŒÀ‘åj‚â -Infinityi•‰‚Ì–³ŒÀ‘åj‚Æ‚¢‚¤
+‚悤‚É•\‹L‚³‚ê‚Ü‚·B
+–³ŒÀ‚Í 1.0/0.0 ‚̂悤‚Ƀ[ƒ‚ÅŠ„‚邿‚¤‚ÈŒvŽZ‚ð‚µ‚½‚Æ‚«‚ɶ¬‚³‚ê‚Ü‚·B
+<BR><BR>
+u”ñ”v‚Í 0.0/0.0 ‚â Infinity-Infinity “™‚ÌŒ‹‰Ê‚ª’è‹`‚Å‚«‚È‚¢
+ŒvŽZ‚ð‚µ‚½‚Æ‚«‚ɶ¬‚³‚ê‚Ü‚·B”ñ”‚Í NaNiNot a Numberj‚Æ•\‹L‚³‚ê‚Ü‚·B
+NaN ‚ðŠÜ‚ÞŒvŽZ‚Í‘S‚Ä NaN ‚ɂȂè‚Ü‚·B‚Ü‚½ NaN ‚ÍŽ©•ª‚àŠÜ‚ß‚ÄA‚Ç‚ñ‚È”
+‚Æ‚àˆê’v‚µ‚Ü‚¹‚ñB
+<BR><BR>
+ƒ[ƒ‚Í +0.0 ‚Æ -0.0 ‚ª‘¶Ý‚µ‚Ü‚·B‚½‚¾‚µA+0.0==-0.0 ‚Í true ‚Å‚·B
+<BR><BR>
+InfinityANaNA +0.0 ‚Æ -0.0 “™‚ðŠÜ‚ñ‚¾ŒvŽZŒ‹‰Ê‚Í‘g‚݇‚킹‚É
+‚æ‚è•¡ŽG‚Å‚·B‹»–¡‚Ì‚ ‚él‚ÍAˆÈ‰º‚̃vƒƒOƒ‰ƒ€‚ðŽÀs‚µ‚ÄŒ‹‰Ê‚ð
+Šm”F‚µ‚Ä‚­‚¾‚³‚¢iŒ‹‰Ê‚ɂ‚¢‚ÄA‹^–â‚âŠÔˆá‚¢‚ð”­Œ©‚³‚ꂽ•û‚Í
+‚¨’m‚点Šè‚¢‚Ü‚·jB
+
+<PRE>
+<CODE>
+require "bigdecimal"
+
+aa = %w(1 -1 +0.0 -0.0 +Infinity -Infinity NaN)
+ba = %w(1 -1 +0.0 -0.0 +Infinity -Infinity NaN)
+opa = %w(+ - * / <=> > >= < == != <=)
+
+for a in aa
+ for b in ba
+ for op in opa
+ x = BigDecimal::new(a)
+ y = BigDecimal::new(b)
+ eval("ans= x #{op} y;print a,' ',op,' ',b,' ==> ',ans.to_s,\"\n\"")
+ end
+ end
+end
+</CODE>
+</PRE>
+
+<hr>
+<A NAME="#STRUCT">
+<H2>“à•”\‘¢</H2>
+BigDecimal“à•”‚Å•‚“®¬”“_‚Í\‘¢‘Ì(Real)‚Å•\Œ»‚³‚ê‚Ü‚·B
+‚»‚Ì‚¤‚¿‰¼”•”‚Í unsigned long ‚Ì”z—ñ(ˆÈ‰º‚Ì\‘¢‘Ì—v‘ffrac)‚ÅŠÇ—‚³‚ê‚Ü‚·B
+ŠT”O“I‚É‚ÍAˆÈ‰º‚̂悤‚ɂȂè‚Ü‚·B<BR><BR>
+ <•‚“®¬”“_”> = 0.xxxxxxxxx*BASE**n<BR><BR>
+‚±‚±‚ÅAx‚͉¼”•”‚ð•\‚·”ŽšABASE‚ÍŠî”i‚P‚Oi‚È‚ç‚P‚OjAn‚ÍŽw”•”‚ð•\‚·
+®”’l‚Å‚·BBASE‚ª‘å‚«‚¢‚Ù‚ÇA‘å‚«‚È”’l‚ª•\Œ»‚Å‚«‚Ü‚·B‚‚܂èA”z—ñ‚̃TƒCƒY‚ð
+­‚È‚­‚Å‚«‚Ü‚·BBASE‚͑傫‚¢‚Ù‚Ç“s‡‚ª‚æ‚¢‚킯‚Å‚·‚ªAƒfƒoƒbƒO‚Ì‚â‚è‚â‚·‚³‚Ȃǂð
+l—¶‚µ‚ÄA10000‚ɂȂÁ‚Ä‚¢‚Ü‚·iBASE‚ÍVpInit()ŠÖ”‚ÅŽ©“®“I‚ÉŒvŽZ‚µ‚Ü‚·jB
+‚±‚ê‚ÍA32ƒrƒbƒg®”‚Ìꇂł·B64ƒrƒbƒg®”‚Ìꇂ͂à‚Á‚Ƒ傫‚È’l‚ɂȂè‚Ü‚·B
+Žc”O‚È‚ª‚çA64ƒrƒbƒg®”‚ł̃eƒXƒg‚͂܂¾‚â‚Á‚Ä‚¢‚Ü‚¹‚ñi‚à‚µA‚â‚ç‚ꂽ•û‚ª‚¢‚ê‚Î
+Œ‹‰Ê‚ð‹³‚¦‚Ä‚¢‚½‚¾‚¯‚ê‚΂ ‚肪‚½‚¢‚Å‚·jB
+BASE‚ª10000‚̂Ƃ«‚ÍAˆÈ‰º‚̉¼”•”‚Ì”z—ñ(frac)‚ÌŠe—v‘f‚É‚ÍÅ‘å‚Å‚SŒ…‚Ì
+”Žš‚ªŠi”[‚³‚ê‚Ü‚·B<BR><BR>
+•‚“®¬”“_\‘¢‘Ì(Real)‚͈ȉº‚̂悤‚ɂȂÁ‚Ä‚¢‚Ü‚·B
+<BR>
+<CODE><PRE>
+ typedef struct {
+ unsigned long MaxPrec; // ő帓x(frac[]‚Ì”z—ñƒTƒCƒY)
+ unsigned long Prec; // ¸“x(frac[]‚ÌŽg—pƒTƒCƒY)
+ short sign; // ˆÈ‰º‚̂悤‚É•„†“™‚Ìó‘Ô‚ð’è‹`‚µ‚Ü‚·B
+ // ==0 : NaN
+ // 1 : +0
+ // -1 : -0
+ // 2 : ³‚Ì’l
+ // -2 : •‰‚Ì’l
+ // 3 : +Infinity
+ // -3 : -Infinity
+ unsigned short flag; // ŠeŽí‚̧Œäƒtƒ‰ƒbƒO
+ int exponent; // Žw”•”‚Ì’l(‰¼”•”*BASE**exponent)
+ unsigned long frac[1]; // ‰¼”•”‚Ì”z—ñ(‰Â•Ï)
+ } Real;
+</CODE></PRE>
+—Ⴆ‚Î 1234.56784321 ‚Æ‚¢‚¤”Žš‚Í(BASE=10000‚È‚ç)<BR>
+<PRE>
+ 0.1234 5678 4321*(10000)**1
+</PRE>
+‚Å‚·‚©‚ç frac[0]=1234Afrac[1]=5678Afrac[2]=4321A
+Prec=3Asign=2Aexponent=1 ‚ƂȂè‚Ü‚·BMaxPrec‚Í
+Prec ‚æ‚è‘å‚«‚¯‚ê‚΂¢‚­‚Â‚Å‚à‚©‚Ü‚¢‚Ü‚¹‚ñBflag ‚Ì
+Žg—p•û–@‚ÍŽÀ‘•‚Ɉˑ¶‚µ‚Ä“à•”‚ÅŽg—p‚³‚ê‚Ü‚·B
+
+<hr>
+<A NAME="#BASE">
+<H2>2i‚Æ10i</H2>
+BigDecimal ‚Í <•‚“®¬”“_”> = 0.xxxxxxxxx*10**n ‚Æ‚¢‚¤10iŒ`Ž®‚Å”’l‚ð•ÛŽ‚µ‚Ü‚·B
+‚µ‚©‚µAŒvŽZ‹@‚Ì•‚“®¬”“_”‚Ì“à•”•\Œ»‚ÍAŒ¾‚¤‚܂łà‚È‚­ <•‚“®¬”“_”> = 0.bbbbbbbb*2**n ‚Æ‚¢‚¤
+2iŒ`Ž®‚ª•’ʂł·(x ‚Í 0 ‚©‚ç 9 ‚Ü‚ÅAb ‚Í 0 ‚© 1 ‚Ì”Žš)B
+BigDecimal ‚ª‚È‚º10i‚Ì“à•”•\Œ»Œ`Ž®‚ðÌ—p‚µ‚½‚Ì‚©‚ðˆÈ‰º‚Éà–¾‚µ‚Ü‚·B
+<H4>10i‚̃ƒŠƒbƒg</H4>
+<DL>
+<DT>ƒfƒoƒbƒO‚Ì‚µ‚â‚·‚³
+<DD>‚Ü‚¸AƒvƒƒOƒ‰ƒ€ì¬‚ªŠy‚Å‚·Bfrac[0]=1234Afrac[1]=5678Afrac[2]=4321A
+exponent=1Asign=2 ‚Ȃ甒l‚ª 1234.56784321 ‚Å‚ ‚é‚̂͌©‚ê‚Î’¼‚®‚É•ª‚©‚è‚Ü‚·B
+
+<DT>10i•\‹L‚³‚ꂽ”’l‚È‚çŠmŽÀ‚É“à•”•\Œ»‚ɕϊ·‚Å‚«‚é
+<DD>—Ⴆ‚ÎAˆÈ‰º‚̂悤‚ȃvƒƒOƒ‰ƒ€‚Í‘S‚­Œë·–³‚µ‚Å
+ŒvŽZ‚·‚邱‚Æ‚ª‚Å‚«‚Ü‚·BˆÈ‰º‚Ì—á‚ÍAˆês‚Ɉê‚‚̔’l
+‚ª‘‚¢‚Ä‚ ‚éƒtƒ@ƒCƒ‹ file ‚̇Œv”’l‚ð‹‚ß‚é‚à‚̂ł·B
+<CODE><PRE>
+ file = File::open(....,"r")
+ s = BigDecimal::new("0")
+ while line = file.gets
+ s = s + line
+ end
+</PRE></CODE>
+‚±‚Ì—á‚ð2i”‚Å‚â‚邯Œë·‚ª“ü‚螂މ”\«‚ª‚ ‚è‚Ü‚·B
+—Ⴆ‚Î 0.1 ‚ð2i‚Å•\Œ»‚·‚邯 0.1 = b1*2**(-1)+b1*2**(-2)+b3*2**(-3)+b4*2**(-4)....
+‚Æ–³ŒÀ‚É‘±‚¢‚Ä‚µ‚Ü‚¢‚Ü‚·(b1=0,b2=0,b3=0,b4=1...)B‚±‚±‚Å bn(n=1,2,3,...) ‚Í
+2i‚ð•\Œ»‚·‚é 0 ‚© 1 ‚Ì”Žš—ñ‚Å‚·B]‚Á‚ÄA‚Ç‚±‚©‚őł¿Ø‚é•K—v‚ª‚ ‚è‚Ü‚·B
+‚±‚±‚ŕϊ·Œë·‚ª“ü‚è‚Ü‚·B‚à‚¿‚ë‚ñA‚±‚ê‚ðÄ“x10i•\‹L‚É‚µ‚Ĉóü‚·‚邿‚¤‚È
+ꇂ͓K؂Ȋۂߑ€ìiŽlŽÌŒÜ“üj‚É‚æ‚Á‚ÄÄ‚Ñ "0.1" ‚Æ•\ަ‚³‚ê‚Ü‚·B‚µ‚©‚µA
+“à•”‚łͳŠm‚È 0.1 ‚ł͂ ‚è‚Ü‚¹‚ñB
+
+<DT>—LŒøŒ…”‚Í—LŒÀ‚Å‚ ‚éi‚‚܂莩“®Œˆ’è‚Å‚«‚éj
+<DD>0.1 ‚ð•\Œ»‚·‚邽‚߂̗̈æ‚Í‚½‚Á‚½ˆê‚‚̔z—ñ—v‘fi frac[0]=1 j‚Åς݂܂·B
+”z—ñ—v‘f‚Ì”‚Í10i”’l‚©‚玩“®“I‚ÉŒˆ’è‚Å‚«‚Ü‚·B‚±‚ê‚ÍA‰Â•Ï’·•‚“®¬”“_‰‰ŽZ‚Å‚Í
+‘厖‚È‚±‚Ƃł·B‹t‚É 0.1 ‚ð2i•\Œ»‚µ‚½‚Æ‚«‚É‚Í2i‚Ì—LŒøŒ…‚ð‚¢‚­‚‚ɂ·‚é‚Ì‚© 0.1 ‚ð
+Œ©‚½‚¾‚¯‚ł͌ˆ’è‚Å‚«‚Ü‚¹‚ñB
+</DL>
+
+<H3>10i‚̃fƒƒŠƒbƒg</H3>
+ŽÀ‚Í¡‚܂ł̃ƒŠƒbƒg‚ÍA‚»‚̂܂܃fƒƒŠƒbƒg‚É‚à‚È‚è‚Ü‚·B
+‚»‚à‚»‚àA10i‚ð2iA2i‚ð10i‚ɕϊ·‚·‚邿‚¤‚È‘€ì‚͕ϊ·Œë·
+‚𔺂¤ê‡‚ð‰ñ”ð‚·‚é‚±‚Ƃ͂ł«‚Ü‚¹‚ñB
+Šù‚ÉŒvŽZ‹@“à•”‚ÉŽæ‚螂܂ꂽ2i”’l‚ð BigDecimal ‚Ì“à•”•\Œ»‚É
+•ÏŠ·‚·‚邯‚«‚ɂ͌뷂ª”ð‚¯‚ç‚ê‚È‚¢ê‡‚ª‚ ‚è‚Ü‚·B
+
+<H3>ʼn‚͉½‚©H</H3>
+Ž©•ª‚ÅŒvŽZ‚·‚邯‚«‚ɂ킴‚í‚´2i”‚ðŽg‚¤l‚͋ɂ߂Ă܂ê‚Å‚·B
+ŒvŽZ‹@‚Ƀf[ƒ^‚ð“ü—Í‚·‚邯‚«‚à‚Ù‚Æ‚ñ‚Ç‚Ìê‡A
+10i”‚Å“ü—Í‚µ‚Ü‚·B‚»‚ÌŒ‹‰ÊAdouble “™‚ÌŒvŽZ‹@“à•”
+•\Œ»‚Íʼn‚©‚çŒë·‚ª“ü‚Á‚Ä‚¢‚éꇂª‚ ‚è‚Ü‚·B
+BigDecimal ‚̓†[ƒU“ü—Í‚ðŒë·–³‚µ‚ÅŽæ‚螂ނ±‚Æ‚ª‚Å‚«‚Ü‚·B
+ƒfƒoƒbƒO‚ª‚µ‚â‚·‚¢‚Ì‚ÆAƒf[ƒ^“ǂ݂±‚ÝŽž‚Ɍ뷂ª“ü‚ç‚È‚¢
+‚Æ‚¢‚¤‚Ì‚ªŽÀۂ̃ƒŠƒbƒg‚Å‚·B
+
+<hr>
+<A NAME="#PREC">
+<H2>ŒvŽZ¸“x‚ɂ‚¢‚Ä</H2>
+c = a op b ‚Æ‚¢‚¤ŒvŽZ(op ‚Í + - * /)‚ð‚µ‚½‚Æ‚«‚Ì“®ì‚Í
+ˆÈ‰º‚̂悤‚ɂȂè‚Ü‚·B<BR><BR>
+‚PDæŽZ‚Í(a ‚Ì—LŒøŒ…”)+(b ‚Ì—LŒøŒ…”)A
+œŽZ‚Í(a ‚ÌÅ‘å—LŒøŒ…”)+(b ‚ÌÅ‘å—LŒøŒ…”)•ª‚Ìő包”iŽÀÛ‚ÍA—]—T‚ðŽ‚Á‚ÄA
+‚à‚¤­‚µ‘å‚«‚­‚È‚è‚Ü‚·j‚ðŽ‚Â•Ï” c ‚ðV‚½‚ɶ¬‚µ‚Ü‚·B
+‰ÁŒ¸ŽZ‚ÌꇂÍAŒë·‚ªo‚È‚¢‚¾‚¯‚̸“x‚ðŽ‚Â c ‚𶬂µ‚Ü‚·B—Ⴆ‚Î
+ c = 0.1+0.1*10**(-100) ‚̂悤‚Èê‡Ac ‚̸“x‚Í‚P‚O‚OŒ…ˆÈã‚̸“x‚ð
+Ž‚Â‚æ‚¤‚ɂȂè‚Ü‚·B
+<BR><BR>
+‚QDŽŸ‚É c = a op b ‚ÌŒvŽZ‚ðŽÀs‚µ‚Ü‚·B<BR><BR>
+‚±‚̂悤‚ÉA‰ÁŒ¸ŽZ‚ÆæŽZ‚Å‚Ì c ‚Í•K‚¸uŒë·‚ªo‚È‚¢v‚¾‚¯‚̸“x‚ð
+Ž‚Á‚ͬ‚³‚ê‚Ü‚·(BigDecimal.limit ‚ðŽw’肵‚È‚¢ê‡)B
+œŽZ‚Í(a ‚ÌÅ‘å—LŒøŒ…”)+(b ‚ÌÅ‘å—LŒøŒ…”)•ª‚Ìő包”
+‚ðŽ‚Â c ‚ª¶¬‚³‚ê‚Ü‚·‚ªAc = 1.0/3.0 ‚̂悤‚ÈŒvŽZ‚Å–¾‚ç‚©‚Ȃ悤‚ÉA
+ c ‚Ìő帓x‚ð’´‚¦‚邯‚±‚ë‚ÅŒvŽZ‚ª‘Å‚¿Ø‚ç‚ê‚éꇂª‚ ‚è‚Ü‚·B<BR><BR>
+‚¢‚¸‚ê‚É‚¹‚æAc ‚Ìő帓x‚Í a ‚â b ‚æ‚è‘å‚«‚­‚È‚è‚Ü‚·‚̂Šc ‚ª•K—v‚Æ‚·‚é
+ƒƒ‚ƒŠ[—̈æ‚͑傫‚­‚Ȃ邱‚ƂɒˆÓ‚µ‚ĉº‚³‚¢B
+<BR><BR>
+’ˆÓFu+,-,*,/v‚ł͌‹‰Ê‚̸“xi—LŒøŒ…”j‚ðŽ©•ª‚ÅŽw’è‚Å‚«‚Ü‚¹‚ñB
+¸“x‚ðƒRƒ“ƒgƒ[ƒ‹‚µ‚½‚¢ê‡‚ÍAˆÈ‰º‚̃Cƒ“ƒXƒ^ƒ“ƒXƒƒ\ƒbƒh‚ðŽg—p‚µ‚Ü‚·B<BR>
+<UL>
+<LI>add,sub,mult,div</LI><BLOCKQUOTE>
+‚±‚ê‚ç‚̃ƒ\ƒbƒh‚Íæ“ª(Ŷ)‚Ì”Žš‚©‚ç‚ÌŒ…”‚ðŽw’è‚Å‚«‚Ü‚·B
+<CODE><PRE>
+ BigDecimal("2").div(3,12) # 2.0/3.0 => 0.6666666666 67E0
+</PRE></CODE>
+</BLOCKQUOTE>
+<LI>truncate,round,ceil,floor</LI><BLOCKQUOTE>
+‚±‚ê‚ç‚̃ƒ\ƒbƒh‚ͬ”“_‚©‚ç‚Ì‘Š‘ÎˆÊ’u‚ðŽw’肵‚ÄŒ…”‚ðŒˆ’è‚µ‚Ü‚·B
+<CODE><PRE>
+ BigDecimal("6.66666666666666").round(12) # => 0.6666666666 667E1
+</PRE></CODE>
+</BLOCKQUOTE>
+</UL>
+<H3>Ž©•ª‚Ÿ“x‚ðƒRƒ“ƒgƒ[ƒ‹‚µ‚½‚¢ê‡</H3>
+Ž©•ª‚Ÿ“x(—LŒøŒ…”)‚ðƒRƒ“ƒgƒ[ƒ‹‚µ‚½‚¢ê‡‚Í addAsubAmultAdiv “™‚̃ƒ\ƒbƒh
+‚ªŽg—p‚Å‚«‚Ü‚·B
+ˆÈ‰º‚̉~Žü—¦‚ðŒvŽZ‚·‚éƒvƒƒOƒ‰ƒ€—á‚̂悤‚ÉA
+‹‚߂錅”‚ÍŽ©•ª‚ÅŽw’è‚·‚邱‚Æ‚ª‚Å‚«‚Ü‚·B
+<BR><BR>
+<CODE><PRE>
+#!/usr/local/bin/ruby
+
+require "bigdecimal"
+#
+# Calculates 3.1415.... (the number of times that a circle's diameter
+# will fit around the circle) using J. Machin's formula.
+#
+def big_pi(sig) # sig: Number of significant figures
+ exp = -sig
+ pi = BigDecimal::new("0")
+ two = BigDecimal::new("2")
+ m25 = BigDecimal::new("-0.04")
+ m57121 = BigDecimal::new("-57121")
+
+ u = BigDecimal::new("1")
+ k = BigDecimal::new("1")
+ w = BigDecimal::new("1")
+ t = BigDecimal::new("-80")
+ while (u.nonzero? && u.exponent >= exp)
+ t = t*m25
+ u = t.div(k,sig)
+ pi = pi + u
+ k = k+two
+ end
+
+ u = BigDecimal::new("1")
+ k = BigDecimal::new("1")
+ w = BigDecimal::new("1")
+ t = BigDecimal::new("956")
+ while (u.nonzero? && u.exponent >= exp )
+ t = t.div(m57121,sig)
+ u = t.div(k,sig)
+ pi = pi + u
+ k = k+two
+ end
+ pi
+end
+
+if $0 == __FILE__
+ if ARGV.size == 1
+ print "PI("+ARGV[0]+"):\n"
+ p big_pi(ARGV[0].to_i)
+ else
+ print "TRY: ruby pi.rb 1000 \n"
+ end
+end
+
+</PRE></CODE>
+<HR>
+<FONT size=2>
+<I>
+<A HREF="http://www.tinyforest.gr.jp">
+¬—Ñ –ΗY
+</A>
+(E-Mail:<A HREF="mailto:shigeo@tinyforest.gr.jp">&ltshigeo@tinyforest.gr.jp&gt</U></A>)
+</I>
+</FONT>
+</TD>
+</TR>
+</TABLE>
+</BODY>
+</HTML>
diff --git a/ext/bigdecimal/depend b/ext/bigdecimal/depend
new file mode 100644
index 0000000000..402cae95dd
--- /dev/null
+++ b/ext/bigdecimal/depend
@@ -0,0 +1 @@
+bigdecimal.o: bigdecimal.c bigdecimal.h $(hdrdir)/ruby.h
diff --git a/ext/bigdecimal/extconf.rb b/ext/bigdecimal/extconf.rb
new file mode 100644
index 0000000000..a68a656044
--- /dev/null
+++ b/ext/bigdecimal/extconf.rb
@@ -0,0 +1,2 @@
+require 'mkmf'
+create_makefile('bigdecimal')
diff --git a/ext/bigdecimal/lib/bigdecimal/jacobian.rb b/ext/bigdecimal/lib/bigdecimal/jacobian.rb
new file mode 100644
index 0000000000..34a60ae67a
--- /dev/null
+++ b/ext/bigdecimal/lib/bigdecimal/jacobian.rb
@@ -0,0 +1,63 @@
+#
+# jacobian.rb
+#
+# Computes Jacobian matrix of f at x
+#
+module Jacobian
+ def isEqual(a,b,zero=0.0,e=1.0e-8)
+ aa = a.abs
+ bb = b.abs
+ if aa == zero && bb == zero then
+ true
+ else
+ if ((a-b)/(aa+bb)).abs < e then
+ true
+ else
+ false
+ end
+ end
+ end
+
+ def dfdxi(f,fx,x,i)
+ nRetry = 0
+ n = x.size
+ xSave = x[i]
+ ok = 0
+ ratio = f.ten*f.ten*f.ten
+ dx = x[i].abs/ratio
+ dx = fx[i].abs/ratio if isEqual(dx,f.zero,f.zero,f.eps)
+ dx = f.one/f.ten if isEqual(dx,f.zero,f.zero,f.eps)
+ until ok>0 do
+ s = f.zero
+ deriv = []
+ if(nRetry>100) then
+ raize "Singular Jacobian matrix. No change at x[" + i.to_s + "]"
+ end
+ dx = dx*f.two
+ x[i] += dx
+ fxNew = f.values(x)
+ for j in 0...n do
+ if !isEqual(fxNew[j],fx[j],f.zero,f.eps) then
+ ok += 1
+ deriv <<= (fxNew[j]-fx[j])/dx
+ else
+ deriv <<= f.zero
+ end
+ end
+ x[i] = xSave
+ end
+ deriv
+ end
+
+ def jacobian(f,fx,x)
+ n = x.size
+ dfdx = Array::new(n*n)
+ for i in 0...n do
+ df = dfdxi(f,fx,x,i)
+ for j in 0...n do
+ dfdx[j*n+i] = df[j]
+ end
+ end
+ dfdx
+ end
+end
diff --git a/ext/bigdecimal/lib/bigdecimal/ludcmp.rb b/ext/bigdecimal/lib/bigdecimal/ludcmp.rb
new file mode 100644
index 0000000000..c36f0dea5b
--- /dev/null
+++ b/ext/bigdecimal/lib/bigdecimal/ludcmp.rb
@@ -0,0 +1,75 @@
+#
+# ludcmp.rb
+#
+module LUSolve
+ def ludecomp(a,n,zero=0.0,one=1.0)
+ ps = []
+ scales = []
+ for i in 0...n do # pick up largest(abs. val.) element in each row.
+ ps <<= i
+ nrmrow = zero
+ ixn = i*n
+ for j in 0...n do
+ biggst = a[ixn+j].abs
+ nrmrow = biggst if biggst>nrmrow
+ end
+ if nrmrow>zero then
+ scales <<= one/nrmrow
+ else
+ raise "Singular matrix"
+ end
+ end
+ n1 = n - 1
+ for k in 0...n1 do # Gaussian elimination with partial pivoting.
+ biggst = zero;
+ for i in k...n do
+ size = a[ps[i]*n+k].abs*scales[ps[i]]
+ if size>biggst then
+ biggst = size
+ pividx = i
+ end
+ end
+ raise "Singular matrix" if biggst<=zero
+ if pividx!=k then
+ j = ps[k]
+ ps[k] = ps[pividx]
+ ps[pividx] = j
+ end
+ pivot = a[ps[k]*n+k]
+ for i in (k+1)...n do
+ psin = ps[i]*n
+ a[psin+k] = mult = a[psin+k]/pivot
+ if mult!=zero then
+ pskn = ps[k]*n
+ for j in (k+1)...n do
+ a[psin+j] -= mult*a[pskn+j]
+ end
+ end
+ end
+ end
+ raise "Singular matrix" if a[ps[n1]*n+n1] == zero
+ ps
+ end
+
+ def lusolve(a,b,ps,zero=0.0)
+ n = ps.size
+ x = []
+ for i in 0...n do
+ dot = zero
+ psin = ps[i]*n
+ for j in 0...i do
+ dot = a[psin+j]*x[j] + dot
+ end
+ x <<= b[ps[i]] - dot
+ end
+ (n-1).downto(0) do |i|
+ dot = zero
+ psin = ps[i]*n
+ for j in (i+1)...n do
+ dot = a[psin+j]*x[j] + dot
+ end
+ x[i] = (x[i]-dot)/a[psin+i]
+ end
+ x
+ end
+end
diff --git a/ext/bigdecimal/lib/bigdecimal/newton.rb b/ext/bigdecimal/lib/bigdecimal/newton.rb
new file mode 100644
index 0000000000..67a92474ac
--- /dev/null
+++ b/ext/bigdecimal/lib/bigdecimal/newton.rb
@@ -0,0 +1,75 @@
+#
+# newton.rb
+#
+# Solves nonlinear algebraic equation system f = 0 by Newton's method.
+# (This program is not dependent on BigDecimal)
+#
+# To call:
+# n = nlsolve(f,x)
+# where n is the number of iterations required.
+# x is the solution vector.
+# f is the object to be solved which must have following methods.
+#
+# f ... Object to compute Jacobian matrix of the equation systems.
+# [Methods required for f]
+# f.values(x) returns values of all functions at x.
+# f.zero returns 0.0
+# f.one returns 1.0
+# f.two returns 1.0
+# f.ten returns 10.0
+# f.eps convergence criterion
+# x ... initial values
+#
+require "ludcmp"
+require "jacobian"
+
+module Newton
+ include LUSolve
+ include Jacobian
+
+ def norm(fv,zero=0.0)
+ s = zero
+ n = fv.size
+ for i in 0...n do
+ s += fv[i]*fv[i]
+ end
+ s
+ end
+
+ def nlsolve(f,x)
+ nRetry = 0
+ n = x.size
+
+ f0 = f.values(x)
+ zero = f.zero
+ one = f.one
+ two = f.two
+ p5 = one/two
+ d = norm(f0,zero)
+ minfact = f.ten*f.ten*f.ten
+ minfact = one/minfact
+ e = f.eps
+ while d >= e do
+ nRetry += 1
+ # Not yet converged. => Compute Jacobian matrix
+ dfdx = jacobian(f,f0,x)
+ # Solve dfdx*dx = -f0 to estimate dx
+ dx = lusolve(dfdx,f0,ludecomp(dfdx,n,zero,one),zero)
+ fact = two
+ xs = x.dup
+ begin
+ fact *= p5
+ if fact < minfact then
+ raize "Failed to reduce function values."
+ end
+ for i in 0...n do
+ x[i] = xs[i] - dx[i]*fact
+ end
+ f0 = f.values(x)
+ dn = norm(f0,zero)
+ end while(dn>=d)
+ d = dn
+ end
+ nRetry
+ end
+end
diff --git a/ext/bigdecimal/lib/bigdecimal/nlsolve.rb b/ext/bigdecimal/lib/bigdecimal/nlsolve.rb
new file mode 100644
index 0000000000..08f17f9ecd
--- /dev/null
+++ b/ext/bigdecimal/lib/bigdecimal/nlsolve.rb
@@ -0,0 +1,38 @@
+#!/usr/local/bin/ruby
+
+#
+# nlsolve.rb
+# An example for solving nonlinear algebraic equation system.
+#
+
+require "bigdecimal"
+require "newton"
+include Newton
+
+class Function
+ def initialize()
+ @zero = BigDecimal::new("0.0")
+ @one = BigDecimal::new("1.0")
+ @two = BigDecimal::new("2.0")
+ @ten = BigDecimal::new("10.0")
+ @eps = BigDecimal::new("1.0e-16")
+ end
+ def zero;@zero;end
+ def one ;@one ;end
+ def two ;@two ;end
+ def ten ;@ten ;end
+ def eps ;@eps ;end
+ def values(x) # <= defines functions solved
+ f = []
+ f1 = x[0]*x[0] + x[1]*x[1] - @two # f1 = x**2 + y**2 - 2 => 0
+ f2 = x[0] - x[1] # f2 = x - y => 0
+ f <<= f1
+ f <<= f2
+ f
+ end
+end
+ f = BigDecimal::limit(100)
+ f = Function.new
+ x = [f.zero,f.zero] # Initial values
+ n = nlsolve(f,x)
+ p x
diff --git a/ext/bigdecimal/lib/bigdecimal/util.rb b/ext/bigdecimal/lib/bigdecimal/util.rb
new file mode 100644
index 0000000000..369fe3fdd0
--- /dev/null
+++ b/ext/bigdecimal/lib/bigdecimal/util.rb
@@ -0,0 +1,73 @@
+#
+# BigDecimal utility library.
+# ----------------------------------------------------------------------
+# Contents:
+#
+# String#
+# to_d ... to BigDecimal
+#
+# Float#
+# to_d ... to BigDecimal
+#
+# BigDecimal#
+# to_digits ... to xxxxx.yyyy form digit string(not 0.zzzE?? form).
+# to_r ... to Rational
+#
+# Rational#
+# to_d ... to BigDecimal
+#
+# ----------------------------------------------------------------------
+#
+class Float < Numeric
+ def to_d
+ BigFloat::new(selt.to_s)
+ end
+end
+
+class String
+ def to_d
+ BigDecimal::new(self)
+ end
+end
+
+class BigDecimal < Numeric
+ # to "nnnnnn.mmm" form digit string
+ def to_digits
+ if self.nan? || self.infinite?
+ self.to_s
+ else
+ s,i,y,z = self.fix.split
+ s,f,y,z = self.frac.split
+ if s > 0
+ s = ""
+ else
+ s = "-"
+ end
+ s + i + "." + f
+ end
+ end
+
+ # Convert BigDecimal to Rational
+ def to_r
+ sign,digits,base,power = self.split
+ numerator = sign*digits.to_i
+ denomi_power = power - digits.size # base is always 10
+ if denomi_power < 0
+ denominator = base ** (-denomi_power)
+ else
+ denominator = base ** denomi_power
+ end
+ Rational(numerator,denominator)
+ end
+end
+
+class Rational < Numeric
+ # Convert Rational to BigDecimal
+ def to_d(nFig=0)
+ num = self.numerator.to_s
+ if nFig<=0
+ nFig = BigDecimal.double_fig*2+1
+ end
+ BigDecimal.new(num).div(self.denominator,nFig)
+ end
+end
diff --git a/ext/bigdecimal/sample/linear.rb b/ext/bigdecimal/sample/linear.rb
new file mode 100644
index 0000000000..f93404fb6f
--- /dev/null
+++ b/ext/bigdecimal/sample/linear.rb
@@ -0,0 +1,46 @@
+#!/usr/local/bin/ruby
+
+#
+# linear.rb
+#
+# Solves linear equation system(A*x = b) by LU decomposition method.
+# where A is a coefficient matrix,x is an answer vector,b is a constant vector.
+#
+require "bigdecimal"
+require "ludcmp"
+
+include LUSolve
+
+def rd_order
+ printf("Number of equations ?")
+ n = gets().chomp.to_i
+end
+
+zero = BigDecimal::new("0.0")
+one = BigDecimal::new("1.0")
+
+while (n=rd_order())>0
+ a = []
+ as= []
+ b = []
+ printf("\nEnter coefficient matrix element A[i,j]\n");
+ for i in 0...n do
+ for j in 0...n do
+ printf("A[%d,%d]? ",i,j); s = gets
+ a <<=BigDecimal::new(s);
+ as<<=BigDecimal::new(s);
+ end
+ printf("Contatant vector element b[%d] ? ",i);b<<=BigDecimal::new(gets);
+ end
+ printf "ANS="
+ x = lusolve(a,b,ludecomp(a,n,zero,one),zero)
+ p x
+ printf "A*x-b\n"
+ for i in 0...n do
+ s = zero
+ for j in 0...n do
+ s = s + as[i*n+j]*x[j]
+ end
+ p s-b[i]
+ end
+end
diff --git a/ext/bigdecimal/sample/nlsolve.rb b/ext/bigdecimal/sample/nlsolve.rb
new file mode 100644
index 0000000000..08f17f9ecd
--- /dev/null
+++ b/ext/bigdecimal/sample/nlsolve.rb
@@ -0,0 +1,38 @@
+#!/usr/local/bin/ruby
+
+#
+# nlsolve.rb
+# An example for solving nonlinear algebraic equation system.
+#
+
+require "bigdecimal"
+require "newton"
+include Newton
+
+class Function
+ def initialize()
+ @zero = BigDecimal::new("0.0")
+ @one = BigDecimal::new("1.0")
+ @two = BigDecimal::new("2.0")
+ @ten = BigDecimal::new("10.0")
+ @eps = BigDecimal::new("1.0e-16")
+ end
+ def zero;@zero;end
+ def one ;@one ;end
+ def two ;@two ;end
+ def ten ;@ten ;end
+ def eps ;@eps ;end
+ def values(x) # <= defines functions solved
+ f = []
+ f1 = x[0]*x[0] + x[1]*x[1] - @two # f1 = x**2 + y**2 - 2 => 0
+ f2 = x[0] - x[1] # f2 = x - y => 0
+ f <<= f1
+ f <<= f2
+ f
+ end
+end
+ f = BigDecimal::limit(100)
+ f = Function.new
+ x = [f.zero,f.zero] # Initial values
+ n = nlsolve(f,x)
+ p x
diff --git a/ext/bigdecimal/sample/pi.rb b/ext/bigdecimal/sample/pi.rb
new file mode 100644
index 0000000000..833967a583
--- /dev/null
+++ b/ext/bigdecimal/sample/pi.rb
@@ -0,0 +1,50 @@
+#!/usr/local/bin/ruby
+
+#
+# pi.rb
+#
+
+require "bigdecimal"
+#
+# Calculates 3.1415.... (the number of times that a circle's diameter
+# will fit around the circle) using J. Machin's formula.
+#
+def big_pi(sig) # sig: Number of significant figures
+ exp = -sig
+ pi = BigDecimal::new("0")
+ two = BigDecimal::new("2")
+ m25 = BigDecimal::new("-0.04")
+ m57121 = BigDecimal::new("-57121")
+
+ u = BigDecimal::new("1")
+ k = BigDecimal::new("1")
+ w = BigDecimal::new("1")
+ t = BigDecimal::new("-80")
+ while (u.nonzero? && u.exponent >= exp)
+ t = t*m25
+ u = t.div(k,sig)
+ pi = pi + u
+ k = k+two
+ end
+
+ u = BigDecimal::new("1")
+ k = BigDecimal::new("1")
+ w = BigDecimal::new("1")
+ t = BigDecimal::new("956")
+ while (u.nonzero? && u.exponent >= exp )
+ t = t.div(m57121,sig)
+ u = t.div(k,sig)
+ pi = pi + u
+ k = k+two
+ end
+ pi
+end
+
+if $0 == __FILE__
+ if ARGV.size == 1
+ print "PI("+ARGV[0]+"):\n"
+ p big_pi(ARGV[0].to_i)
+ else
+ print "TRY: ruby pi.rb 1000 \n"
+ end
+end
diff --git a/ext/configsub.rb b/ext/configsub.rb
deleted file mode 100644
index 47689d7ec0..0000000000
--- a/ext/configsub.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-#!./miniruby -ps
-
-BEGIN {
- CONFIG = {}
-
- RUBY_VERSION.scan(/(\d+)\.(\d+)\.(\d+)/) do
- # overridden if config.status has version
- CONFIG['MAJOR'] = $1
- CONFIG['MINOR'] = $2
- CONFIG['TEENY'] = $3
- end
-
- File.foreach($config || "config.status") do |line|
- next if /^#/ =~ line
- if /^s[%,]@(\w+)@[%,](.*)[%,][g;]/ =~ line
- name = $1
- val = $2 || ""
- next if /^(INSTALL|DEFS|configure_input|srcdir)$/ =~ name
- val.gsub!(/\$\{([^{}]+)\}/) { "$(#{$1})" }
- CONFIG[name] = val
- end
- end
-
- CONFIG['top_srcdir'] = File.expand_path($srcdir || ".")
- CONFIG['RUBY_INSTALL_NAME'] = $install_name if $install_name
- CONFIG['RUBY_SO_NAME'] = $so_name if $so_name
- $defout = open($output, 'w') if $output
-}
-
-gsub!(/@(\w+)@/) {CONFIG[$1] || $&}
-
-# vi:set sw=2:
diff --git a/ext/curses/curses.c b/ext/curses/curses.c
index aeda267dca..52c8ab1e7f 100644
--- a/ext/curses/curses.c
+++ b/ext/curses/curses.c
@@ -13,6 +13,8 @@
* - Takaaki Tateishi (ttate@kt.jaist.ac.jp)
*/
+#include "ruby.h"
+
#if defined(HAVE_NCURSES_H)
# include <ncurses.h>
#elif defined(HAVE_NCURSES_CURSES_H)
@@ -46,7 +48,6 @@
#endif
#include <stdio.h>
-#include "ruby.h"
#include "rubyio.h"
static VALUE mCurses;
@@ -75,6 +76,8 @@ no_window()
}
#define GetWINDOW(obj, winp) do {\
+ if (!OBJ_TAINTED(obj) && rb_safe_level() >= 4)\
+ rb_raise(rb_eSecurityError, "Insecure: operation on untainted window");\
Data_Get_Struct(obj, struct windata, winp);\
if (winp->window == 0) no_window();\
} while (0)
@@ -113,6 +116,7 @@ prep_window(class, window)
static VALUE
curses_init_screen()
{
+ rb_secure(4);
if (rb_stdscr) return rb_stdscr;
initscr();
if (stdscr == 0) {
@@ -139,7 +143,7 @@ curses_close_screen()
}
static void
-curses_finalize()
+curses_finalize(VALUE dummy)
{
if (stdscr
#ifdef HAVE_ISENDWIN
@@ -593,6 +597,8 @@ no_mevent()
}
#define GetMOUSE(obj, data) do {\
+ if (!OBJ_TAINTED(obj) && rb_safe_level() >= 4)\
+ rb_raise(rb_eSecurityError, "Insecure: operation on untainted mouse");\
Data_Get_Struct(obj, struct mousedata, data);\
if (data->mevent == 0) no_mevent();\
} while (0)
@@ -657,8 +663,7 @@ DEFINE_MOUSE_GET_MEMBER(curs_mouse_bstate, bstate)
/* def self.allocate */
static VALUE
-window_s_allocate(class)
- VALUE class;
+window_s_allocate(VALUE class)
{
struct windata *winp;
@@ -677,6 +682,7 @@ window_initialize(obj, h, w, top, left)
struct windata *winp;
WINDOW *window;
+ rb_secure(4);
curses_init_screen();
Data_Get_Struct(obj, struct windata, winp);
if (winp->window) delwin(winp->window);
@@ -1308,7 +1314,7 @@ Init_curses()
#endif /* USE_MOUSE */
cWindow = rb_define_class_under(mCurses, "Window", rb_cData);
- rb_define_singleton_method(cWindow, "allocate", window_s_allocate, 0);
+ rb_define_alloc_func(cWindow, window_s_allocate);
rb_define_method(cWindow, "initialize", window_initialize, 4);
rb_define_method(cWindow, "subwin", window_subwin, 4);
rb_define_method(cWindow, "close", window_close, 0);
diff --git a/ext/curses/extconf.rb b/ext/curses/extconf.rb
index 94443b45e6..94d1e2e549 100644
--- a/ext/curses/extconf.rb
+++ b/ext/curses/extconf.rb
@@ -6,6 +6,7 @@ dir_config('termcap')
make=false
have_library("mytinfo", "tgetent") if /bow/ =~ RUBY_PLATFORM
+have_library("tinfo", "tgetent") or have_library("termcap", "tgetent")
if have_header("ncurses.h") and have_library("ncurses", "initscr")
make=true
elsif have_header("ncurses/curses.h") and have_library("ncurses", "initscr")
@@ -13,7 +14,6 @@ elsif have_header("ncurses/curses.h") and have_library("ncurses", "initscr")
elsif have_header("curses_colr/curses.h") and have_library("cur_colr", "initscr")
make=true
else
- have_library("termcap", "tgetent")
if have_header("curses.h") and have_library("curses", "initscr")
make=true
end
diff --git a/ext/dbm/dbm.c b/ext/dbm/dbm.c
index 88c4233cae..c422394405 100644
--- a/ext/dbm/dbm.c
+++ b/ext/dbm/dbm.c
@@ -64,15 +64,12 @@ fdbm_close(obj)
return Qnil;
}
+static VALUE fdbm_alloc _((VALUE));
static VALUE
-fdbm_s_new(argc, argv, klass)
- int argc;
- VALUE *argv;
+fdbm_alloc(klass)
VALUE klass;
{
- VALUE obj = Data_Wrap_Struct(klass, 0, free_dbm, 0);
- rb_obj_call_init(obj, argc, argv);
- return obj;
+ return Data_Wrap_Struct(klass, 0, free_dbm, 0);
}
static VALUE
@@ -240,7 +237,6 @@ fdbm_select(argc, argv, obj)
datum key, val;
DBM *dbm;
struct dbmdata *dbmp;
- VALUE keystr, valstr;
if (argc > 0) {
rb_raise(rb_eArgError, "wrong number arguments(%d for 0)", argc);
@@ -258,6 +254,8 @@ fdbm_select(argc, argv, obj)
}
}
else {
+ rb_warn("DBM#select(index..) is deprecated; use DBM#values_at");
+
for (i=0; i<argc; i++) {
rb_ary_push(new, fdbm_fetch(obj, argv[i], Qnil));
}
@@ -267,6 +265,22 @@ fdbm_select(argc, argv, obj)
}
static VALUE
+fdbm_values_at(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE new = rb_ary_new2(argc);
+ int i;
+
+ for (i=0; i<argc; i++) {
+ rb_ary_push(new, fdbm_fetch(obj, argv[i], Qnil));
+ }
+
+ return new;
+}
+
+static VALUE
fdbm_delete(obj, keystr)
VALUE obj, keystr;
{
@@ -409,6 +423,8 @@ fdbm_invert(obj)
return hash;
}
+static VALUE each_pair _((VALUE));
+
static VALUE
each_pair(obj)
VALUE obj;
@@ -720,7 +736,7 @@ Init_dbm()
rb_eDBMError = rb_define_class("DBMError", rb_eStandardError);
rb_include_module(rb_cDBM, rb_mEnumerable);
- rb_define_singleton_method(rb_cDBM, "new", fdbm_s_new, -1);
+ rb_define_alloc_func(rb_cDBM, fdbm_alloc);
rb_define_singleton_method(rb_cDBM, "open", fdbm_s_open, -1);
rb_define_method(rb_cDBM, "initialize", fdbm_initialize, -1);
@@ -733,6 +749,7 @@ Init_dbm()
rb_define_method(rb_cDBM, "indexes", fdbm_indexes, -1);
rb_define_method(rb_cDBM, "indices", fdbm_indexes, -1);
rb_define_method(rb_cDBM, "select", fdbm_select, -1);
+ rb_define_method(rb_cDBM, "values_at", fdbm_values_at, -1);
rb_define_method(rb_cDBM, "length", fdbm_length, 0);
rb_define_method(rb_cDBM, "size", fdbm_length, 0);
rb_define_method(rb_cDBM, "empty?", fdbm_empty_p, 0);
@@ -761,4 +778,8 @@ Init_dbm()
rb_define_method(rb_cDBM, "to_a", fdbm_to_a, 0);
rb_define_method(rb_cDBM, "to_hash", fdbm_to_hash, 0);
+
+#ifdef DB_VERSION_STRING
+ rb_define_const(rb_cDBM, "VERSION", rb_str_new2(DB_VERSION_STRING));
+#endif
}
diff --git a/ext/dbm/extconf.rb b/ext/dbm/extconf.rb
index e67ede88f5..5a67603e69 100644
--- a/ext/dbm/extconf.rb
+++ b/ext/dbm/extconf.rb
@@ -11,6 +11,7 @@ $dbm_conf_headers = {
"dbm" => ["ndbm.h"],
"gdbm" => ["gdbm-ndbm.h", "ndbm.h"],
"gdbm_compat" => ["gdbm-ndbm.h", "ndbm.h"],
+ "qdbm" => ["relic.h"],
}
def db_check(db)
@@ -31,8 +32,8 @@ def db_check(db)
if have_library(db, db_prefix("dbm_open")) || have_func(db_prefix("dbm_open"))
for hdr in $dbm_conf_headers.fetch(db, ["ndbm.h"])
- if have_header(hdr.dup)
- $CFLAGS += " " + hsearch + "-DDBM_HDR='<"+hdr+">'"
+ if have_header(hdr.dup) and have_type("DBM", hdr.dup, hsearch)
+ $CFLAGS += " " + hsearch + '-DDBM_HDR="<'+hdr+'>"'
return true
end
end
@@ -47,7 +48,7 @@ end
if dblib
db_check(dblib)
else
- for dblib in %w(db db2 db1 dbm gdbm)
+ for dblib in %w(db db2 db1 dbm gdbm gdbm_compat qdbm)
db_check(dblib) and break
end
end
diff --git a/ext/dbm/testdbm.rb b/ext/dbm/testdbm.rb
index 0be627d346..7ccb3d7b23 100644
--- a/ext/dbm/testdbm.rb
+++ b/ext/dbm/testdbm.rb
@@ -32,9 +32,12 @@ class TestDBM < RUNIT::TestCase
assert_instance_of(DBM, @dbm = DBM.new(@path))
# prepare to make readonly DBM file
- DBM.open("tmptest_dbm_rdonly", 0400) {|dbm|
+ DBM.open("tmptest_dbm_rdonly") {|dbm|
dbm['foo'] = 'FOO'
}
+
+ File.chmod(0400, *Dir.glob("tmptest_dbm_rdonly.*"))
+
assert_instance_of(DBM, @dbm_rdonly = DBM.new("tmptest_dbm_rdonly", nil))
end
def teardown
@@ -83,7 +86,7 @@ class TestDBM < RUNIT::TestCase
}
begin
sleep 1
- assert_exception(Errno::EWOULDBLOCK) {
+ assert_exception(Errno::EWOULDBLOCK, "NEVER MIND IF YOU USE Berkeley DB3") {
begin
assert_instance_of(DBM, dbm2 = DBM.open("tmptest_dbm", 0644))
rescue Errno::EAGAIN, Errno::EACCES, Errno::EINVAL
@@ -154,7 +157,7 @@ class TestDBM < RUNIT::TestCase
def test_s_open_error
assert_instance_of(DBM, dbm = DBM.open("tmptest_dbm", 0))
- assert_exception(Errno::EACCES) {
+ assert_exception(Errno::EACCES, "NEVER MIND IF YOU USE Berkeley DB3") {
DBM.open("tmptest_dbm", 0)
}
dbm.close
@@ -245,11 +248,11 @@ class TestDBM < RUNIT::TestCase
assert_equals(values.reverse, @dbm.indexes(*keys.reverse))
end
- def test_select
+ def test_values_at
keys = %w(foo bar baz)
values = %w(FOO BAR BAZ)
@dbm[keys[0]], @dbm[keys[1]], @dbm[keys[2]] = values
- assert_equals(values.reverse, @dbm.select(*keys.reverse))
+ assert_equals(values.reverse, @dbm.values_at(*keys.reverse))
end
def test_select_with_block
diff --git a/ext/digest/defs.h b/ext/digest/defs.h
index 8e44cf6fa2..5cfc77dd24 100644
--- a/ext/digest/defs.h
+++ b/ext/digest/defs.h
@@ -18,7 +18,7 @@
#if defined(HAVE_INTTYPES_H)
# include <inttypes.h>
-#else
+#elif !defined __CYGWIN__ || !defined __uint8_t_defined
typedef unsigned char uint8_t;
typedef unsigned int uint32_t;
# if SIZEOF_LONG == 8
diff --git a/ext/digest/digest.c b/ext/digest/digest.c
index 63a3b92a5c..a170678cf0 100644
--- a/ext/digest/digest.c
+++ b/ext/digest/digest.c
@@ -57,7 +57,8 @@ get_digest_base_metadata(klass)
return algo;
}
-
+
+static VALUE rb_digest_base_alloc _((VALUE));
static VALUE
rb_digest_base_alloc(klass)
VALUE klass;
@@ -297,7 +298,7 @@ Init_digest()
cDigest_Base = rb_define_class_under(mDigest, "Base", rb_cObject);
- rb_define_singleton_method(cDigest_Base, "allocate", rb_digest_base_alloc, 0);
+ rb_define_alloc_func(cDigest_Base, rb_digest_base_alloc);
rb_define_singleton_method(cDigest_Base, "digest", rb_digest_base_s_digest, 1);
rb_define_singleton_method(cDigest_Base, "hexdigest", rb_digest_base_s_hexdigest, 1);
diff --git a/ext/digest/md5/md5ossl.c b/ext/digest/md5/md5ossl.c
index 3c9f4ba49f..d930c7ab51 100644
--- a/ext/digest/md5/md5ossl.c
+++ b/ext/digest/md5/md5ossl.c
@@ -2,6 +2,8 @@
#include "md5ossl.h"
#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
void
MD5_End(MD5_CTX *pctx, unsigned char *hexdigest)
diff --git a/ext/digest/sha2/sha2.c b/ext/digest/sha2/sha2.c
index a3c3258082..24a57ded0d 100644
--- a/ext/digest/sha2/sha2.c
+++ b/ext/digest/sha2/sha2.c
@@ -36,10 +36,10 @@
/* $RoughId: sha2.c,v 1.3 2002/02/26 22:03:36 knu Exp $ */
/* $Id$ */
+#include "sha2.h"
#include <stdio.h>
#include <string.h> /* memcpy()/memset() or bcopy()/bzero() */
#include <assert.h> /* assert() */
-#include "sha2.h"
/*
* ASSERT NOTE:
@@ -67,7 +67,7 @@ typedef uint8_t sha2_byte; /* Exactly 1 byte */
typedef uint32_t sha2_word32; /* Exactly 4 bytes */
typedef uint64_t sha2_word64; /* Exactly 8 bytes */
-#if defined(__GNUC__)
+#if defined(__GNUC__) || defined(_HPUX_SOURCE)
#define ULL(number) number##ULL
#else
#define ULL(number) (uint64_t)(number)
diff --git a/ext/dl/MANIFEST b/ext/dl/MANIFEST
index 261b4ad914..d44344421d 100644
--- a/ext/dl/MANIFEST
+++ b/ext/dl/MANIFEST
@@ -1,4 +1,3 @@
-.cvsignore
MANIFEST
depend
dl.c
diff --git a/ext/dl/dl.c b/ext/dl/dl.c
index c1e5311c0a..22abb754d2 100644
--- a/ext/dl/dl.c
+++ b/ext/dl/dl.c
@@ -24,8 +24,8 @@ rb_dl_scan_callback_args(long stack[], const char *proto,
VALUE val;
sp = stack;
- for( i=1; proto[i]; i++ ){
- switch( proto[i] ){
+ for (i=1; proto[i]; i++) {
+ switch (proto[i]) {
case 'C':
{
char v;
@@ -162,11 +162,11 @@ dlsizeof(const char *cstr)
len = strlen(cstr);
size = 0;
- for( i=0; i<len; i++ ){
+ for (i=0; i<len; i++) {
n = 1;
- if( isdigit(cstr[i+1]) ){
+ if (isdigit(cstr[i+1])) {
dlen = 1;
- while( isdigit(cstr[i+dlen]) ){ dlen ++; };
+ while (isdigit(cstr[i+dlen])) { dlen ++; };
dlen --;
d = ALLOCA_N(char, dlen + 1);
strncpy(d, cstr + i + 1, dlen);
@@ -177,7 +177,7 @@ dlsizeof(const char *cstr)
dlen = 0;
}
- switch( cstr[i] ){
+ switch (cstr[i]) {
case 'I':
DLALIGN(0,size,INT_ALIGN);
case 'i':
@@ -234,9 +234,9 @@ c_farray(VALUE v, long *size)
len = RARRAY(v)->len;
*size = sizeof(float) * len;
ary = dlmalloc(*size);
- for( i=0; i < len; i++ ){
+ for (i=0; i < len; i++) {
e = rb_ary_entry(v, i);
- switch( TYPE(e) ){
+ switch (TYPE(e)) {
case T_FLOAT:
ary[i] = (float)(RFLOAT(e)->value);
break;
@@ -262,9 +262,9 @@ c_darray(VALUE v, long *size)
len = RARRAY(v)->len;
*size = sizeof(double) * len;
ary = dlmalloc(*size);
- for( i=0; i < len; i++ ){
+ for (i=0; i < len; i++) {
e = rb_ary_entry(v, i);
- switch( TYPE(e) ){
+ switch (TYPE(e)) {
case T_FLOAT:
ary[i] = (double)(RFLOAT(e)->value);
break;
@@ -290,9 +290,9 @@ c_larray(VALUE v, long *size)
len = RARRAY(v)->len;
*size = sizeof(long) * len;
ary = dlmalloc(*size);
- for( i=0; i < len; i++ ){
+ for (i=0; i < len; i++) {
e = rb_ary_entry(v, i);
- switch( TYPE(e) ){
+ switch (TYPE(e)) {
case T_FIXNUM:
case T_BIGNUM:
ary[i] = (long)(NUM2INT(e));
@@ -319,9 +319,9 @@ c_iarray(VALUE v, long *size)
len = RARRAY(v)->len;
*size = sizeof(int) * len;
ary = dlmalloc(*size);
- for( i=0; i < len; i++ ){
+ for (i=0; i < len; i++) {
e = rb_ary_entry(v, i);
- switch( TYPE(e) ){
+ switch (TYPE(e)) {
case T_FIXNUM:
case T_BIGNUM:
ary[i] = (int)(NUM2INT(e));
@@ -348,9 +348,9 @@ c_harray(VALUE v, long *size)
len = RARRAY(v)->len;
*size = sizeof(short) * len;
ary = dlmalloc(*size);
- for( i=0; i < len; i++ ){
+ for (i=0; i < len; i++) {
e = rb_ary_entry(v, i);
- switch( TYPE(e) ){
+ switch (TYPE(e)) {
case T_FIXNUM:
case T_BIGNUM:
ary[i] = (short)(NUM2INT(e));
@@ -377,9 +377,9 @@ c_carray(VALUE v, long *size)
len = RARRAY(v)->len;
*size = sizeof(char) * len;
ary = dlmalloc(*size);
- for( i=0; i < len; i++ ){
+ for (i=0; i < len; i++) {
e = rb_ary_entry(v, i);
- switch( TYPE(e) ){
+ switch (TYPE(e)) {
case T_FIXNUM:
case T_BIGNUM:
ary[i] = (char)(NUM2INT(e));
@@ -401,15 +401,23 @@ c_parray(VALUE v, long *size)
{
int i, len;
void **ary;
- VALUE e;
+ VALUE e, tmp;
len = RARRAY(v)->len;
*size = sizeof(void*) * len;
ary = dlmalloc(*size);
- for( i=0; i < len; i++ ){
+ for (i=0; i < len; i++) {
e = rb_ary_entry(v, i);
- switch( TYPE(e) ){
+ switch (TYPE(e)) {
+ default:
+ tmp = rb_check_string_type(e);
+ if (NIL_P(tmp)) {
+ rb_raise(rb_eDLTypeError, "unexpected type of the element #%d", i);
+ }
+ e = tmp;
+ /* fall through */
case T_STRING:
+ rb_check_safe_str(e);
{
char *str, *src;
src = RSTRING(e)->ptr;
@@ -421,7 +429,7 @@ c_parray(VALUE v, long *size)
ary[i] = NULL;
break;
case T_DATA:
- if( rb_obj_is_kind_of(e, rb_cDLPtrData) ){
+ if (rb_obj_is_kind_of(e, rb_cDLPtrData)) {
struct ptr_data *pdata;
Data_Get_Struct(e, struct ptr_data, pdata);
ary[i] = (void*)(pdata->ptr);
@@ -430,9 +438,6 @@ c_parray(VALUE v, long *size)
rb_raise(rb_eDLTypeError, "unexpected type of the element #%d", i);
}
break;
- default:
- rb_raise(rb_eDLTypeError, "unexpected type of the element #%d", i);
- break;
}
}
@@ -445,24 +450,26 @@ rb_ary2cary(char t, VALUE v, long *size)
int len;
VALUE val0;
- if( TYPE(v) != T_ARRAY ){
+ val0 = rb_check_array_type(v);
+ if(NIL_P(TYPE(val0))) {
rb_raise(rb_eDLTypeError, "an array is expected.");
}
+ v = val0;
len = RARRAY(v)->len;
- if( len == 0 ){
+ if (len == 0) {
return NULL;
}
- if( !size ){
+ if (!size) {
size = ALLOCA_N(long,1);
}
val0 = rb_ary_entry(v,0);
- switch( TYPE(val0) ){
+ switch (TYPE(val0)) {
case T_FIXNUM:
case T_BIGNUM:
- switch( t ){
+ switch (t) {
case 'C': case 'c':
return (void*)c_carray(v,size);
case 'H': case 'h':
@@ -477,7 +484,7 @@ rb_ary2cary(char t, VALUE v, long *size)
case T_STRING:
return (void*)c_parray(v,size);
case T_FLOAT:
- switch( t ){
+ switch (t) {
case 'F': case 'f':
return (void*)c_farray(v,size);
case 'D': case 'd': case 0:
@@ -485,7 +492,7 @@ rb_ary2cary(char t, VALUE v, long *size)
}
rb_raise(rb_eDLTypeError, "type mismatch");
case T_DATA:
- if( rb_obj_is_kind_of(val0, rb_cDLPtrData) ){
+ if (rb_obj_is_kind_of(val0, rb_cDLPtrData)) {
return (void*)c_parray(v,size);
}
rb_raise(rb_eDLTypeError, "type mismatch");
@@ -516,7 +523,7 @@ rb_ary_to_ptr(int argc, VALUE argv[], VALUE self)
VALUE t;
long size;
- switch( rb_scan_args(argc, argv, "01", &t) ){
+ switch (rb_scan_args(argc, argv, "01", &t)) {
case 1:
ptr = rb_ary2cary(StringValuePtr(t)[0], self, &size);
break;
@@ -542,21 +549,21 @@ rb_io_to_ptr(VALUE self)
VALUE
rb_dl_dlopen(int argc, VALUE argv[], VALUE self)
{
+ rb_secure(4);
return rb_class_new_instance(argc, argv, rb_cDLHandle);
}
VALUE
rb_dl_malloc(VALUE self, VALUE size)
{
+ rb_secure(4);
return rb_dlptr_malloc(DLNUM2LONG(size), dlfree);
}
VALUE
rb_dl_strdup(VALUE self, VALUE str)
{
- void *p;
-
- str = rb_String(str);
+ SafeStringValue(str);
return rb_dlptr_new(strdup(RSTRING(str)->ptr), RSTRING(str)->len, dlfree);
}
@@ -573,11 +580,12 @@ rb_dl_callback(int argc, VALUE argv[], VALUE self)
int rettype, entry, i;
char fname[127];
+ rb_secure(4);
proc = Qnil;
- switch( rb_scan_args(argc, argv, "11", &type, &proc) ){
+ switch (rb_scan_args(argc, argv, "11", &type, &proc)) {
case 1:
- if( rb_block_given_p() ){
- proc = rb_f_lambda();
+ if (rb_block_given_p()) {
+ proc = rb_block_proc();
}
else{
proc = Qnil;
@@ -586,8 +594,8 @@ rb_dl_callback(int argc, VALUE argv[], VALUE self)
break;
}
- Check_Type(type, T_STRING);
- switch( RSTRING(type)->ptr[0] ){
+ StringValue(type);
+ switch (RSTRING(type)->ptr[0]) {
case '0':
rettype = 0x00;
break;
@@ -617,13 +625,13 @@ rb_dl_callback(int argc, VALUE argv[], VALUE self)
}
entry = -1;
- for( i=0; i < MAX_CALLBACK; i++ ){
- if( rb_hash_aref(DLFuncTable, rb_assoc_new(INT2NUM(rettype), INT2NUM(i))) == Qnil ){
+ for (i=0; i < MAX_CALLBACK; i++) {
+ if (rb_hash_aref(DLFuncTable, rb_assoc_new(INT2NUM(rettype), INT2NUM(i))) == Qnil) {
entry = i;
break;
}
}
- if( entry < 0 ){
+ if (entry < 0) {
rb_raise(rb_eDLError, "too many callbacks are defined.");
}
@@ -638,12 +646,14 @@ rb_dl_callback(int argc, VALUE argv[], VALUE self)
static VALUE
rb_dl_remove_callback(VALUE mod, VALUE sym)
{
- freefunc_t f = rb_dlsym2csym(sym);
+ freefunc_t f;
int i, j;
- for( i=0; i < CALLBACK_TYPES; i++ ){
- for( j=0; j < MAX_CALLBACK; j++ ){
- if( rb_dl_callback_table[i][j] == f ){
+ rb_secure(4);
+ f = rb_dlsym2csym(sym);
+ for (i=0; i < CALLBACK_TYPES; i++) {
+ for (j=0; j < MAX_CALLBACK; j++) {
+ if (rb_dl_callback_table[i][j] == f) {
rb_hash_aset(DLFuncTable, rb_assoc_new(INT2NUM(i),INT2NUM(j)),Qnil);
break;
}
diff --git a/ext/dl/doc/dl.txt b/ext/dl/doc/dl.txt
index 51da92c49d..893bd21d79 100644
--- a/ext/dl/doc/dl.txt
+++ b/ext/dl/doc/dl.txt
@@ -235,24 +235,24 @@ the prototype consists of the following type specifiers, first element of
prototype represents the type of return value, and remaining elements represent
the type of each argument.
- C : a character (char)
- c : a pointer to a character (char *)
- H : a short integer (short)
- h : a pointer to a short integer (short *)
- I : an integer (char, short, int)
- i : a pointer to an integer (char *, short *, int *)
- L : a long integer (long)
- l : a pointer to a long integer (long *)
- F : a real (float)
- f : a pointer to a real (float *)
- D : a real (double)
- d : a pointer to a real (double *)
- S : an immutable string (const char *)
- s : a mutable string (char *)
- A : an array (const type[])
- a : a mutable array (type[])
- P : a pointer (void *)
- p : a mutable object (void *)
+ C : char
+ c : char *
+ H : short
+ h : short *
+ I : int
+ i : int *
+ L : long
+ l : long *
+ F : float
+ f : float *
+ D : double
+ d : double *
+ S : const char *
+ s : char *
+ A : const type[]
+ a : type[] (allocates new memory space)
+ P : void * (same as 'p')
+ p : void * (same as 'P')
0 : void function (this must be a first character of the prototype)
the cbtype consists of type specifiers 0, C, I, H, L, F, D, S and P.
diff --git a/ext/dl/handle.c b/ext/dl/handle.c
index ea3b98b3c6..9ca2fa0a54 100644
--- a/ext/dl/handle.c
+++ b/ext/dl/handle.c
@@ -10,7 +10,7 @@ VALUE rb_cDLHandle;
void
dlhandle_free(struct dl_handle *dlhandle)
{
- if( dlhandle->ptr && dlhandle->open && dlhandle->enable_close ){
+ if (dlhandle->ptr && dlhandle->open && dlhandle->enable_close) {
dlclose(dlhandle->ptr);
}
}
@@ -50,7 +50,7 @@ rb_dlhandle_initialize(int argc, VALUE argv[], VALUE self)
int cflag;
const char *err;
- switch( rb_scan_args(argc, argv, "11", &lib, &flag) ){
+ switch (rb_scan_args(argc, argv, "11", &lib, &flag)) {
case 1:
clib = StringValuePtr(lib);
cflag = RTLD_LAZY | RTLD_GLOBAL;
@@ -65,24 +65,24 @@ rb_dlhandle_initialize(int argc, VALUE argv[], VALUE self)
ptr = dlopen(clib, cflag);
#if defined(HAVE_DLERROR)
- if( (err = dlerror()) ){
+ if (!ptr && (err = dlerror())) {
rb_raise(rb_eRuntimeError, err);
}
#else
- if( !ptr ){
+ if (!ptr) {
err = dlerror();
rb_raise(rb_eRuntimeError, err);
}
#endif
Data_Get_Struct(self, struct dl_handle, dlhandle);
- if( dlhandle->ptr && dlhandle->open && dlhandle->enable_close ){
+ if (dlhandle->ptr && dlhandle->open && dlhandle->enable_close) {
dlclose(dlhandle->ptr);
}
dlhandle->ptr = ptr;
dlhandle->open = 1;
dlhandle->enable_close = 0;
- if( rb_block_given_p() ){
+ if (rb_block_given_p()) {
rb_ensure(rb_yield, self, rb_dlhandle_close, self);
}
@@ -133,22 +133,21 @@ rb_dlhandle_sym(int argc, VALUE argv[], VALUE self)
VALUE sym, type;
void (*func)();
VALUE val;
- struct sym_data *data;
- int *ctypes;
- int i, ctypes_len;
struct dl_handle *dlhandle;
void *handle;
const char *name, *stype;
const char *err;
- if( rb_scan_args(argc, argv, "11", &sym, &type) == 2 ){
+ rb_secure(2);
+ if (rb_scan_args(argc, argv, "11", &sym, &type) == 2) {
+ SafeStringValue(type);
stype = StringValuePtr(type);
}
else{
stype = NULL;
}
- if( sym == Qnil ){
+ if (sym == Qnil) {
#if defined(RTLD_NEXT)
name = RTLD_NEXT;
#else
@@ -156,21 +155,21 @@ rb_dlhandle_sym(int argc, VALUE argv[], VALUE self)
#endif
}
else{
+ SafeStringValue(sym);
name = StringValuePtr(sym);
}
-
Data_Get_Struct(self, struct dl_handle, dlhandle);
- if( ! dlhandle->open ){
+ if (!dlhandle->open) {
rb_raise(rb_eRuntimeError, "Closed handle.");
}
handle = dlhandle->ptr;
func = dlsym(handle, name);
#if defined(HAVE_DLERROR)
- if( (err = dlerror()) && (!func) )
+ if (!func && (err = dlerror()))
#else
- if( !func )
+ if (!func)
#endif
{
#if defined(__CYGWIN__) || defined(WIN32) || defined(__MINGW32__)
@@ -183,9 +182,9 @@ rb_dlhandle_sym(int argc, VALUE argv[], VALUE self)
func = dlsym(handle, name_a);
dlfree(name_a);
#if defined(HAVE_DLERROR)
- if( (err = dlerror()) && (!func) )
+ if (!func && (err = dlerror()))
#else
- if( !func )
+ if (!func)
#endif
{
rb_raise(rb_eRuntimeError, "Unknown symbol \"%sA\".", name);
@@ -204,7 +203,7 @@ void
Init_dlhandle()
{
rb_cDLHandle = rb_define_class_under(rb_mDL, "Handle", rb_cObject);
- rb_define_singleton_method(rb_cDLHandle, "allocate", rb_dlhandle_s_allocate, 0);
+ rb_define_alloc_func(rb_cDLHandle, rb_dlhandle_s_allocate);
rb_define_method(rb_cDLHandle, "initialize", rb_dlhandle_initialize, -1);
rb_define_method(rb_cDLHandle, "to_i", rb_dlhandle_to_i, 0);
rb_define_method(rb_cDLHandle, "to_ptr", rb_dlhandle_to_ptr, 0);
diff --git a/ext/dl/lib/dl/win32.rb b/ext/dl/lib/dl/win32.rb
index b507be5fde..92f473d392 100644
--- a/ext/dl/lib/dl/win32.rb
+++ b/ext/dl/lib/dl/win32.rb
@@ -3,24 +3,23 @@
require 'dl'
class Win32API
- LIBRARY = {}
+ DLL = {}
- attr_reader :val, :args
-
- def initialize(lib, func, args, ret)
- LIBRARY[lib] ||= DL.dlopen(lib)
- ty = (ret + args).tr('V','0')
- @sym = LIBRARY[lib].sym(func, ty)
- @__dll__ = LIBRARY[lib].to_i
- @__dllname__ = lib
- @__proc__ = @sym.to_i
- @val = nil
- @args = []
+ def initialize(dllname, func, import, export = "0")
+ prototype = (export + import.to_s).tr("VPpNnLlIi", "0SSI")
+ handle = DLL[dllname] ||= DL::Handle.new(dllname)
+ @sym = handle.sym(func, prototype)
end
def call(*args)
- @val,@args = @sym.call(*args)
- return @val
+ import = @sym.proto.split("", 2)[1]
+ args.each_with_index do |x, i|
+ args[i] = nil if x == 0 and import[i] == ?S
+ args[i], = [x].pack("I").unpack("i") if import[i] == ?I
+ end
+ ret, = @sym.call(*args)
+ return ret || 0
end
+
alias Call call
end
diff --git a/ext/dl/ptr.c b/ext/dl/ptr.c
index d90054c9c0..1eb0b82730 100644
--- a/ext/dl/ptr.c
+++ b/ext/dl/ptr.c
@@ -3,6 +3,7 @@
*/
#include <ruby.h>
+#include <ctype.h>
#include <version.h> /* for ruby version code */
#include "dl.h"
@@ -25,13 +26,14 @@ rb_hash_delete(VALUE hash, VALUE key)
static void
rb_dlmem_delete(void *ptr)
{
+ rb_secure(4);
rb_hash_delete(DLMemoryTable, DLLONG2NUM(ptr));
}
static void
rb_dlmem_aset(void *ptr, VALUE obj)
{
- if( obj == Qnil ){
+ if (obj == Qnil) {
rb_dlmem_delete(ptr);
}
else{
@@ -51,22 +53,22 @@ rb_dlmem_aref(void *ptr)
void
dlptr_free(struct ptr_data *data)
{
- if( data->ptr ){
+ if (data->ptr) {
DEBUG_CODE({
printf("dlptr_free(): removing the pointer `0x%x' from the MemorySpace\n",
data->ptr);
});
rb_dlmem_delete(data->ptr);
- if( data->free ){
+ if (data->free) {
DEBUG_CODE({
printf("dlptr_free(): 0x%x(data->ptr:0x%x)\n",data->free,data->ptr);
});
(*(data->free))(data->ptr);
}
}
- if( data->stype ) dlfree(data->stype);
- if( data->ssize ) dlfree(data->ssize);
- if( data->ids ) dlfree(data->ids);
+ if (data->stype) dlfree(data->stype);
+ if (data->ssize) dlfree(data->ssize);
+ if (data->ids) dlfree(data->ids);
}
void
@@ -80,6 +82,7 @@ dlptr_init(VALUE val)
data->ptr);
});
rb_dlmem_aset(data->ptr, val);
+ OBJ_TAINT(val);
}
VALUE
@@ -88,9 +91,10 @@ rb_dlptr_new2(VALUE klass, void *ptr, long size, freefunc_t func)
struct ptr_data *data;
VALUE val;
- if( ptr ){
+ rb_secure(4);
+ if (ptr) {
val = rb_dlmem_aref(ptr);
- if( val == Qnil ){
+ if (val == Qnil) {
val = Data_Make_Struct(klass, struct ptr_data,
0, dlptr_free, data);
data->ptr = ptr;
@@ -105,7 +109,7 @@ rb_dlptr_new2(VALUE klass, void *ptr, long size, freefunc_t func)
dlptr_init(val);
}
else{
- if( func ){
+ if (func) {
Data_Get_Struct(val, struct ptr_data, data);
data->free = func;
}
@@ -129,6 +133,7 @@ rb_dlptr_malloc(long size, freefunc_t func)
{
void *ptr;
+ rb_secure(4);
ptr = dlmalloc((size_t)size);
memset(ptr,0,(size_t)size);
return rb_dlptr_new(ptr, size, func);
@@ -140,11 +145,11 @@ rb_dlptr2cptr(VALUE val)
struct ptr_data *data;
void *ptr;
- if( rb_obj_is_kind_of(val, rb_cDLPtrData) ){
+ if (rb_obj_is_kind_of(val, rb_cDLPtrData)) {
Data_Get_Struct(val, struct ptr_data, data);
ptr = data->ptr;
}
- else if( val == Qnil ){
+ else if (val == Qnil) {
ptr = NULL;
}
else{
@@ -160,6 +165,7 @@ rb_dlptr_s_allocate(VALUE klass)
VALUE obj;
struct ptr_data *data;
+ rb_secure(4);
obj = Data_Make_Struct(klass, struct ptr_data, 0, dlptr_free, data);
data->ptr = 0;
data->free = 0;
@@ -177,13 +183,13 @@ rb_dlptr_s_allocate(VALUE klass)
static VALUE
rb_dlptr_initialize(int argc, VALUE argv[], VALUE self)
{
- VALUE ptr, sym, obj, size;
+ VALUE ptr, sym, size;
struct ptr_data *data;
void *p = NULL;
freefunc_t f = NULL;
long s = 0;
- switch( rb_scan_args(argc, argv, "12", &ptr, &size, &sym) ){
+ switch (rb_scan_args(argc, argv, "12", &ptr, &size, &sym)) {
case 1:
p = (void*)(DLNUM2LONG(rb_Integer(ptr)));
break;
@@ -200,9 +206,9 @@ rb_dlptr_initialize(int argc, VALUE argv[], VALUE self)
rb_bug("rb_dlptr_initialize");
}
- if( p ){
+ if (p) {
Data_Get_Struct(self, struct ptr_data, data);
- if( data->ptr && data->free ){
+ if (data->ptr && data->free) {
/* Free previous memory. Use of inappropriate initialize may cause SEGV. */
(*(data->free))(data->ptr);
}
@@ -221,7 +227,7 @@ rb_dlptr_s_malloc(int argc, VALUE argv[], VALUE klass)
int s;
freefunc_t f = NULL;
- switch( rb_scan_args(argc, argv, "11", &size, &sym) ){
+ switch (rb_scan_args(argc, argv, "11", &size, &sym)) {
case 1:
s = NUM2INT(size);
break;
@@ -278,7 +284,6 @@ VALUE
rb_dlptr_free_set(VALUE self, VALUE val)
{
struct ptr_data *data;
- int i;
Data_Get_Struct(self, struct ptr_data, data);
@@ -309,14 +314,14 @@ rb_dlptr_to_array(int argc, VALUE argv[], VALUE self)
Data_Get_Struct(self, struct ptr_data, data);
- switch( rb_scan_args(argc, argv, "11", &type, &size) ){
+ switch (rb_scan_args(argc, argv, "11", &type, &size)) {
case 2:
t = StringValuePtr(type)[0];
n = NUM2INT(size);
break;
case 1:
t = StringValuePtr(type)[0];
- switch( t ){
+ switch (t) {
case 'C':
n = data->size;
break;
@@ -339,9 +344,9 @@ rb_dlptr_to_array(int argc, VALUE argv[], VALUE self)
n = data->size / sizeof(void*);
break;
default:
- if( t == 'p' || t == 's' ){
+ if (t == 'p' || t == 's') {
int i;
- for( i=0; ((void**)(data->ptr))[i]; i++ ){};
+ for (i=0; ((void**)(data->ptr))[i]; i++) {};
n = i;
}
else{
@@ -355,8 +360,8 @@ rb_dlptr_to_array(int argc, VALUE argv[], VALUE self)
ary = rb_ary_new();
- for( i=0; i < n; i++ ){
- switch( t ){
+ for (i=0; i < n; i++) {
+ switch (t) {
case 'C':
rb_ary_push(ary, INT2NUM(((char*)(data->ptr))[i]));
break;
@@ -377,7 +382,7 @@ rb_dlptr_to_array(int argc, VALUE argv[], VALUE self)
case 'S':
{
char *str = ((char**)(data->ptr))[i];
- if( str ){
+ if (str) {
rb_ary_push(ary, rb_tainted_str_new2(str));
}
else{
@@ -388,7 +393,7 @@ rb_dlptr_to_array(int argc, VALUE argv[], VALUE self)
case 's':
{
char *str = ((char**)(data->ptr))[i];
- if( str ){
+ if (str) {
rb_ary_push(ary, rb_tainted_str_new2(str));
xfree(str);
}
@@ -419,7 +424,7 @@ rb_dlptr_to_s(int argc, VALUE argv[], VALUE self)
int len;
Data_Get_Struct(self, struct ptr_data, data);
- switch( rb_scan_args(argc, argv, "01", &arg1) ){
+ switch (rb_scan_args(argc, argv, "01", &arg1)) {
case 0:
val = rb_tainted_str_new2((char*)(data->ptr));
break;
@@ -442,7 +447,7 @@ rb_dlptr_to_str(int argc, VALUE argv[], VALUE self)
int len;
Data_Get_Struct(self, struct ptr_data, data);
- switch( rb_scan_args(argc, argv, "01", &arg1) ){
+ switch (rb_scan_args(argc, argv, "01", &arg1)) {
case 0:
val = rb_tainted_str_new((char*)(data->ptr),data->size);
break;
@@ -462,10 +467,9 @@ rb_dlptr_inspect(VALUE self)
{
struct ptr_data *data;
char str[1024];
- VALUE name;
Data_Get_Struct(self, struct ptr_data, data);
- snprintf(str, 1023, "#<%s:0x%x ptr=0x%x size=%ld free=0x%x>",
+ snprintf(str, 1023, "#<%s:0x%p ptr=0x%p size=%ld free=0x%p>",
rb_class2name(CLASS_OF(self)), data, data->ptr, data->size, data->free);
return rb_str_new2(str);
}
@@ -518,23 +522,22 @@ rb_dlptr_define_data_type(int argc, VALUE argv[], VALUE self)
{
VALUE data_type, type, rest, vid;
struct ptr_data *data;
- int i, t, len, num;
+ int i, t, num;
char *ctype;
- long size;
rb_scan_args(argc, argv, "11*", &data_type, &type, &rest);
Data_Get_Struct(self, struct ptr_data, data);
- if( argc == 1 || (argc == 2 && type == Qnil) ){
- if( NUM2INT(data_type) == DLPTR_CTYPE_UNKNOWN ){
+ if (argc == 1 || (argc == 2 && type == Qnil)) {
+ if (NUM2INT(data_type) == DLPTR_CTYPE_UNKNOWN) {
data->ctype = DLPTR_CTYPE_UNKNOWN;
data->slen = 0;
data->ids_num = 0;
- if( data->stype ){
+ if (data->stype) {
dlfree(data->stype);
data->stype = NULL;
}
- if( data->ids ){
+ if (data->ids) {
dlfree(data->ids);
data->ids = NULL;
}
@@ -549,32 +552,29 @@ rb_dlptr_define_data_type(int argc, VALUE argv[], VALUE self)
StringValue(type);
Check_Type(rest, T_ARRAY);
num = RARRAY(rest)->len;
- for( i=0; i<num; i++ ){
- vid = rb_ary_entry(rest,i);
- if( !(TYPE(vid)==T_STRING || TYPE(vid)==T_SYMBOL) ){
- rb_raise(rb_eTypeError, "#%d must be a string or symbol", i + 2);
- }
+ for (i=0; i<num; i++) {
+ rb_to_id(rb_ary_entry(rest,i));
}
data->ctype = t;
data->slen = num;
data->ids_num = num;
- if( data->stype ) dlfree(data->stype);
+ if (data->stype) dlfree(data->stype);
data->stype = (char*)dlmalloc(sizeof(char) * num);
- if( data->ssize ) dlfree(data->ssize);
+ if (data->ssize) dlfree(data->ssize);
data->ssize = (int*)dlmalloc(sizeof(int) * num);
- if( data->ids ) dlfree(data->ids);
+ if (data->ids) dlfree(data->ids);
data->ids = (ID*)dlmalloc(sizeof(ID) * data->ids_num);
ctype = StringValuePtr(type);
- for( i=0; i<num; i++ ){
+ for (i=0; i<num; i++) {
vid = rb_ary_entry(rest,i);
data->ids[i] = rb_to_id(vid);
data->stype[i] = *ctype;
ctype ++;
- if( isdigit(*ctype) ){
+ if (isdigit(*ctype)) {
char *p, *d;
- for( p=ctype; isdigit(*p); p++ ) ;
+ for (p=ctype; isdigit(*p); p++) ;
d = ALLOCA_N(char, p - ctype + 1);
strncpy(d, ctype, p - ctype);
d[p - ctype] = '\0';
@@ -586,11 +586,11 @@ rb_dlptr_define_data_type(int argc, VALUE argv[], VALUE self)
}
}
- if( *ctype ){
+ if (*ctype) {
rb_raise(rb_eArgError, "too few/many arguments");
}
- if( !data->size )
+ if (!data->size)
data->size = dlsizeof(RSTRING(type)->ptr);
return Qnil;
@@ -605,7 +605,7 @@ rb_dlptr_define_struct(int argc, VALUE argv[], VALUE self)
pass_argc = argc + 1;
pass_argv = ALLOCA_N(VALUE, pass_argc);
pass_argv[0] = INT2FIX(DLPTR_CTYPE_STRUCT);
- for( i=1; i<pass_argc; i++ ){
+ for (i=1; i<pass_argc; i++) {
pass_argv[i] = argv[i-1];
}
return rb_dlptr_define_data_type(pass_argc, pass_argv, self);
@@ -620,7 +620,7 @@ rb_dlptr_define_union(int argc, VALUE argv[], VALUE self)
pass_argc = argc + 1;
pass_argv = ALLOCA_N(VALUE, pass_argc);
pass_argv[0] = INT2FIX(DLPTR_CTYPE_UNION);
- for( i=1; i<pass_argc; i++ ){
+ for (i=1; i<pass_argc; i++) {
pass_argv[i] = argv[i-1];
}
return rb_dlptr_define_data_type(pass_argc, pass_argv, self);
@@ -632,7 +632,7 @@ rb_dlptr_get_data_type(VALUE self)
struct ptr_data *data;
Data_Get_Struct(self, struct ptr_data, data);
- if( data->stype )
+ if (data->stype)
return rb_assoc_new(INT2FIX(data->ctype),
rb_tainted_str_new(data->stype, data->slen));
else
@@ -646,11 +646,11 @@ cary2ary(void *ptr, char t, int len)
VALUE elem;
int i;
- if( len < 1 )
+ if (len < 1)
return Qnil;
- if( len == 1 ){
- switch( t ){
+ if (len == 1) {
+ switch (t) {
case 'I':
elem = INT2NUM(*((int*)ptr));
ptr = (char *)ptr + sizeof(int);
@@ -687,8 +687,8 @@ cary2ary(void *ptr, char t, int len)
}
ary = rb_ary_new();
- for( i=0; i < len; i++ ){
- switch( t ){
+ for (i=0; i < len; i++) {
+ switch (t) {
case 'I':
elem = INT2NUM(*((int*)ptr));
ptr = (char *)ptr + sizeof(int);
@@ -730,35 +730,34 @@ cary2ary(void *ptr, char t, int len)
VALUE
rb_dlptr_aref(int argc, VALUE argv[], VALUE self)
{
- VALUE val, key = Qnil, num = Qnil;
+ VALUE key = Qnil, num = Qnil;
ID id;
- int idx;
struct ptr_data *data;
int i;
int offset;
- if( rb_scan_args(argc, argv, "11", &key, &num) == 1 ){
+ if (rb_scan_args(argc, argv, "11", &key, &num) == 1) {
num = INT2NUM(0);
}
- if( TYPE(key) == T_FIXNUM || TYPE(key) == T_BIGNUM ){
+ if (TYPE(key) == T_FIXNUM || TYPE(key) == T_BIGNUM) {
VALUE pass[1];
pass[0] = num;
return rb_dlptr_to_str(1, pass, rb_dlptr_plus(self, key));
}
-
- if( ! (TYPE(key) == T_STRING || TYPE(key) == T_SYMBOL ) ){
+ rb_to_id(key);
+ if (! (TYPE(key) == T_STRING || TYPE(key) == T_SYMBOL)) {
rb_raise(rb_eTypeError, "the key must be a string or symbol");
}
id = rb_to_id(key);
Data_Get_Struct(self, struct ptr_data, data);
offset = 0;
- switch( data->ctype ){
+ switch (data->ctype) {
case DLPTR_CTYPE_STRUCT:
- for( i=0; i < data->ids_num; i++ ){
- if( data->ids[i] == id ){
- switch( data->stype[i] ){
+ for (i=0; i < data->ids_num; i++) {
+ if (data->ids[i] == id) {
+ switch (data->stype[i]) {
case 'I':
DLALIGN(data->ptr,offset,INT_ALIGN);
break;
@@ -785,7 +784,7 @@ rb_dlptr_aref(int argc, VALUE argv[], VALUE self)
}
return cary2ary((char *)data->ptr + offset, data->stype[i], data->ssize[i]);
}
- switch( data->stype[i] ){
+ switch (data->stype[i]) {
case 'I':
offset += sizeof(int) * data->ssize[i];
break;
@@ -814,8 +813,8 @@ rb_dlptr_aref(int argc, VALUE argv[], VALUE self)
}
break;
case DLPTR_CTYPE_UNION:
- for( i=0; i < data->ids_num; i++ ){
- if( data->ids[i] == id ){
+ for (i=0; i < data->ids_num; i++) {
+ if (data->ids[i] == id) {
return cary2ary((char *)data->ptr + offset, data->stype[i], data->ssize[i]);
}
}
@@ -833,7 +832,7 @@ ary2cary(char t, VALUE val, long *size)
{
void *ptr;
- if( TYPE(val) == T_ARRAY ){
+ if (TYPE(val) == T_ARRAY) {
ptr = rb_ary2cary(t, val, size);
}
else{
@@ -853,14 +852,15 @@ rb_dlptr_aset(int argc, VALUE argv[], VALUE self)
long memsize;
void *memimg;
- switch( rb_scan_args(argc, argv, "21", &key, &num, &val) ){
+ rb_secure(4);
+ switch (rb_scan_args(argc, argv, "21", &key, &num, &val)) {
case 2:
val = num;
num = Qnil;
break;
}
- if( TYPE(key) == T_FIXNUM || TYPE(key) == T_BIGNUM ){
+ if (TYPE(key) == T_FIXNUM || TYPE(key) == T_BIGNUM) {
void *dst, *src;
long len;
@@ -869,29 +869,25 @@ rb_dlptr_aset(int argc, VALUE argv[], VALUE self)
dst = (void*)((long)(data->ptr) + DLNUM2LONG(key));
src = RSTRING(val)->ptr;
len = RSTRING(val)->len;
- if( num == Qnil ){
+ if (num == Qnil) {
memcpy(dst, src, len);
}
else{
long n = NUM2INT(num);
memcpy(dst, src, n < len ? n : len);
- if( n > len ) MEMZERO((char*)dst + len, char, n - len);
+ if (n > len) MEMZERO((char*)dst + len, char, n - len);
}
return val;
}
- if( ! (TYPE(key) == T_STRING || TYPE(key) == T_SYMBOL ) ){
- rb_raise(rb_eTypeError, "the key must be a string or symbol");
- }
-
id = rb_to_id(key);
Data_Get_Struct(self, struct ptr_data, data);
- switch( data->ctype ){
+ switch (data->ctype) {
case DLPTR_CTYPE_STRUCT:
offset = 0;
- for( i=0; i < data->ids_num; i++ ){
- if( data->ids[i] == id ){
- switch( data->stype[i] ){
+ for (i=0; i < data->ids_num; i++) {
+ if (data->ids[i] == id) {
+ switch (data->stype[i]) {
case 'I':
DLALIGN(data->ptr,offset,INT_ALIGN);
break;
@@ -920,7 +916,7 @@ rb_dlptr_aset(int argc, VALUE argv[], VALUE self)
memcpy((char *)data->ptr + offset, memimg, memsize);
return val;
}
- switch( data->stype[i] ){
+ switch (data->stype[i]) {
case 'I':
case 'i':
offset += sizeof(int) * data->ssize[i];
@@ -958,9 +954,9 @@ rb_dlptr_aset(int argc, VALUE argv[], VALUE self)
return val;
/* break; */
case DLPTR_CTYPE_UNION:
- for( i=0; i < data->ids_num; i++ ){
- if( data->ids[i] == id ){
- switch( data->stype[i] ){
+ for (i=0; i < data->ids_num; i++) {
+ if (data->ids[i] == id) {
+ switch (data->stype[i]) {
case 'I': case 'i':
memsize = sizeof(int) * data->ssize[i];
break;
@@ -1005,7 +1001,7 @@ rb_dlptr_size(int argc, VALUE argv[], VALUE self)
{
VALUE size;
- if( rb_scan_args(argc, argv, "01", &size) == 0){
+ if (rb_scan_args(argc, argv, "01", &size) == 0){
return DLLONG2NUM(RDLPTR(self)->size);
}
else{
@@ -1035,7 +1031,7 @@ void
Init_dlptr()
{
rb_cDLPtrData = rb_define_class_under(rb_mDL, "PtrData", rb_cObject);
- rb_define_singleton_method(rb_cDLPtrData, "allocate", rb_dlptr_s_allocate, 0);
+ rb_define_alloc_func(rb_cDLPtrData, rb_dlptr_s_allocate);
rb_define_singleton_method(rb_cDLPtrData, "malloc", rb_dlptr_s_malloc, -1);
rb_define_method(rb_cDLPtrData, "initialize", rb_dlptr_initialize, -1);
rb_define_method(rb_cDLPtrData, "free=", rb_dlptr_free_set, 1);
diff --git a/ext/dl/sym.c b/ext/dl/sym.c
index 7868f07411..4a2d736465 100644
--- a/ext/dl/sym.c
+++ b/ext/dl/sym.c
@@ -77,6 +77,7 @@ rb_dlsym_new(void (*func)(), const char *name, const char *type)
struct sym_data *data;
const char *ptype;
+ rb_secure(4);
if( !type || !type[0] ){
return rb_dlptr_new((void*)func, 0, 0);
}
@@ -145,7 +146,6 @@ VALUE
rb_dlsym_initialize(int argc, VALUE argv[], VALUE self)
{
VALUE addr, name, type;
- VALUE val;
struct sym_data *data;
void *saddr;
const char *sname, *stype;
@@ -266,7 +266,7 @@ rb_dlsym_inspect(VALUE self)
str_size = RSTRING(proto)->len + 100;
str = dlmalloc(str_size);
snprintf(str, str_size - 1,
- "#<DL::Symbol:0x%x func=0x%x '%s'>",
+ "#<DL::Symbol:0x%p func=0x%p '%s'>",
sym, sym->func, RSTRING(proto)->ptr);
val = rb_tainted_str_new2(str);
dlfree(str);
@@ -330,6 +330,7 @@ rb_dlsym_call(int argc, VALUE argv[], VALUE self)
long ftype;
void *func;
+ rb_secure_update(self);
Data_Get_Struct(self, struct sym_data, sym);
DEBUG_CODE({
printf("rb_dlsym_call(): type = '%s', func = 0x%x\n", sym->type, sym->func);
@@ -456,20 +457,20 @@ rb_dlsym_call(int argc, VALUE argv[], VALUE self)
ANY2S(args[i]) = DLSTR(0);
}
else{
- if( TYPE(argv[i]) != T_STRING ){
- rb_raise(rb_eDLError, "#%d must be a string",i);
- }
- ANY2S(args[i]) = DLSTR(RSTRING(argv[i])->ptr);
+ VALUE str = argv[i];
+ SafeStringValue(str);
+ ANY2S(args[i]) = DLSTR(RSTRING(str)->ptr);
}
PUSH_P(ftype);
break;
case 's':
- if( TYPE(argv[i]) != T_STRING ){
- rb_raise(rb_eDLError, "#%d must be a string",i);
+ {
+ VALUE str = argv[i];
+ SafeStringValue(str);
+ ANY2S(args[i]) = DLSTR(dlmalloc(RSTRING(str)->len + 1));
+ memcpy((char*)(ANY2S(args[i])), RSTRING(str)->ptr, RSTRING(str)->len + 1);
+ dtypes[i] = 's';
}
- ANY2S(args[i]) = DLSTR(dlmalloc(RSTRING(argv[i])->len + 1));
- memcpy((char*)(ANY2S(args[i])), RSTRING(argv[i])->ptr, RSTRING(argv[i])->len + 1);
- dtypes[i] = 's';
PUSH_P(ftype);
break;
default:
@@ -822,7 +823,7 @@ void
Init_dlsym()
{
rb_cDLSymbol = rb_define_class_under(rb_mDL, "Symbol", rb_cObject);
- rb_define_singleton_method(rb_cDLSymbol, "allocate", rb_dlsym_s_allocate, 0);
+ rb_define_alloc_func(rb_cDLSymbol, rb_dlsym_s_allocate);
rb_define_singleton_method(rb_cDLSymbol, "char2type", rb_s_dlsym_char2type, 1);
rb_define_method(rb_cDLSymbol, "initialize", rb_dlsym_initialize, -1);
rb_define_method(rb_cDLSymbol, "call", rb_dlsym_call, -1);
diff --git a/ext/etc/etc.c b/ext/etc/etc.c
index b10943458d..51668c400f 100644
--- a/ext/etc/etc.c
+++ b/ext/etc/etc.c
@@ -25,7 +25,9 @@
static VALUE sPasswd, sGroup;
+#ifndef _WIN32
char *getenv();
+#endif
char *getlogin();
static VALUE
@@ -93,7 +95,7 @@ etc_getpwuid(argc, argv, obj)
VALUE obj;
{
#if defined(HAVE_GETPWENT)
- VALUE id, ary;
+ VALUE id;
int uid;
struct passwd *pwd;
@@ -316,11 +318,11 @@ Init_etc()
#ifdef HAVE_ST_PW_EXPIRE
"expire",
#endif
- 0);
+ NULL);
rb_global_variable(&sPasswd);
#ifdef HAVE_GETGRENT
- sGroup = rb_struct_define("Group", "name", "passwd", "gid", "mem", 0);
+ sGroup = rb_struct_define("Group", "name", "passwd", "gid", "mem", NULL);
rb_global_variable(&sGroup);
#endif
}
diff --git a/ext/extmk.rb b/ext/extmk.rb
index 876433d280..3a6d499557 100644
--- a/ext/extmk.rb
+++ b/ext/extmk.rb
@@ -1,4 +1,4 @@
-#! /usr/local/bin/ruby -s
+#! /usr/local/bin/ruby
# -*- ruby -*-
$force_static = nil
@@ -8,6 +8,13 @@ $clean = nil
$nodynamic = nil
$extinit = nil
$extobjs = nil
+$ignore = nil
+$message = nil
+$use_no_undef = nil
+
+$progname = $0
+alias $PROGRAM_NAME $0
+alias $0 $progname
$extlist = []
@@ -21,11 +28,23 @@ $:.replace [srcdir, srcdir+"/lib", "."]
require 'mkmf'
require 'ftools'
require 'shellwords'
+require 'getopts'
$topdir = File.expand_path(".")
$top_srcdir = srcdir
$hdrdir = $top_srcdir
+if not $use_no_undef and /linux/ =~ RUBY_PLATFORM and
+ $configure_args['--enable-shared'] and
+ CONFIG["GNU_LD"] == "yes"
+ $use_no_undef = 0 <= (`ld -v`.scan(/\d+/).map{|x| x.to_i} <=> [2, 11])
+end
+
+def sysquote(x)
+ @quote ||= /human|os2|macos/ =~ (CROSS_COMPILING || RUBY_PLATFORM)
+ @quote ? x.quote : x
+end
+
def extmake(target)
print "#{$message} #{target}\n"
$stdout.flush
@@ -41,7 +60,7 @@ def extmake(target)
init_mkmf
- if /linux/ =~ RUBY_PLATFORM and $configure_args['--enable-shared'] and CONFIG["GNU_LD"] == "yes"
+ if $use_no_undef
$DLDFLAGS << " -Wl,--no-undefined"
end
@@ -54,26 +73,35 @@ def extmake(target)
$srcdir = File.join($top_srcdir, "ext", $mdir)
unless $ignore
if $static ||
- older("./Makefile", *MTIMES + %W"#{$srcdir}/makefile.rb #{$srcdir}/extconf.rb")
+ !(t = modified?("./Makefile", MTIMES)) ||
+ %W<#{$srcdir}/makefile.rb #{$srcdir}/extconf.rb
+ #{$srcdir}/depend #{$srcdir}/MANIFEST>.any? {|f| modified?(f, [t])}
then
$defs = []
Logging::logfile 'mkmf.log'
Config::CONFIG["srcdir"] = $srcdir
- if File.exist?("#{$srcdir}/makefile.rb")
- load "#{$srcdir}/makefile.rb"
- elsif File.exist?("#{$srcdir}/extconf.rb")
- load "#{$srcdir}/extconf.rb"
- else
- create_makefile(target)
+ begin
+ if File.exist?($0 = "#{$srcdir}/makefile.rb")
+ load $0
+ elsif File.exist?($0 = "#{$srcdir}/extconf.rb")
+ load $0
+ else
+ create_makefile(target)
+ end
+ rescue SystemExit
+ # ignore
+ ensure
+ rm_f "conftest*"
+ $0 = $PROGRAM_NAME
+ Config::CONFIG["srcdir"] = $top_srcdir
end
- Config::CONFIG["srcdir"] = $top_srcdir
end
end
if File.exist?("./Makefile")
if $static
$extlist.push [$static, $target, File.basename($target)]
end
- unless system($make, *$mflags)
+ unless system($make, *sysquote($mflags))
$ignore or $continue or return false
end
else
@@ -89,30 +117,47 @@ def extmake(target)
$extlibs += " " + $libs unless $libs == ""
$extlibs += " " + $LOCAL_LIBS unless $LOCAL_LIBS == ""
end
- rescue SystemExit
- # ignore
ensure
- rm_f "conftest*"
Dir.chdir dir
end
true
end
-require 'getopts'
+def parse_args()
+ getopts('n', 'extstatic:', 'dest-dir:',
+ 'make:', 'make-flags:', 'mflags:')
-getopts('', 'extstatic', 'make:', 'make-flags:')
+ $dryrun = $OPT['n']
+ $force_static = $OPT['extstatic'] == 'static'
+ $destdir = $OPT['dest-dir'] || ''
+ $make = $OPT['make'] || $make || 'make'
+ mflags = ($OPT['make-flags'] || '').strip
+ mflags = ($OPT['mflags'] || '').strip if mflags.empty?
-$force_static = $OPT['extstatic']
-$make = $OPT['make'] || $make
-$mflags = Shellwords.shellwords($OPT['make-flags'] || "")
+ $mflags = Shellwords.shellwords(mflags)
+ if arg = $mflags.first
+ arg.insert(0, '-') if /\A[^-][^=]*\Z/ =~ arg
+ end
-if mflags = ENV["MAKEFLAGS"]
- mflags, = mflags.split(nil, 2)
-else
- mflags = ENV["MFLAGS"] || ""
+ $make, *rest = Shellwords.shellwords($make)
+ $mflags.unshift(*rest) unless rest.empty?
+
+ def $mflags.set?(flag)
+ grep(/\A-(?!-).*#{'%c' % flag}/i) { return true }
+ false
+ end
+
+ if $mflags.set?(?n)
+ $dryrun = true
+ else
+ $mflags.unshift '-n' if $dryrun
+ end
+
+ $continue = $mflags.set?(?k)
+ $mflags |= ["DESTDIR=#{$destdir}"]
end
-$continue = mflags.include?(?k)
-$dryrun = mflags.include?(?n)
+
+parse_args()
unless $message
if $message = ARGV.shift and /^[a-z]+$/ =~ $message
@@ -123,8 +168,9 @@ unless $message
$ignore ||= true
when "install"
$ignore ||= true
- $mflags.unshift("INSTALL_PROG=install -m 0755",
- "INSTALL_DATA=install -m 0644") if $dryrun
+ $mflags.unshift("INSTALL_PROG=install -c -p -m 0755",
+ "INSTALL_DATA=install -c -p -m 0644",
+ "MAKEDIRS=mkdir -p") if $dryrun
end
$message.sub!(/e?$/, "ing")
else
@@ -133,7 +179,7 @@ unless $message
end
EXEEXT = CONFIG['EXEEXT']
-if defined? CROSS_COMPILING
+if CROSS_COMPILING
$ruby = CONFIG['MINIRUBY']
elsif $nmake
$ruby = '$(topdir:/=\\)\\miniruby' + EXEEXT
@@ -200,7 +246,7 @@ if $extlist.size > 0
end
src = "void Init_ext() {\n#$extinit}\n"
- if older("extinit.c", *MTIMES) || IO.read("extinit.c") != src
+ if !modified?("extinit.c", MTIMES) || IO.read("extinit.c") != src
open("extinit.c", "w") {|f| f.print src}
end
@@ -224,12 +270,14 @@ rubies = []
}
Dir.chdir ".."
+if $extlist.size > 0
+ rm_f(Config::CONFIG["LIBRUBY_SO"])
+end
puts "making #{rubies.join(', ')}"
$stdout.flush
$mflags.concat(rubies)
-host = (defined?(CROSS_COMPILING) ? CROSS_COMPILING : RUBY_PLATFORM)
-/mswin|bccwin|mingw|djgpp|human|os2|macos/ =~ host or exec($make, *$mflags)
-system($make, *$mflags.quote) or exit($?.exitstatus)
+
+system($make, *sysquote($mflags)) or exit($?.exitstatus)
#Local variables:
# mode: ruby
diff --git a/ext/gdbm/gdbm.c b/ext/gdbm/gdbm.c
index 6ee4d6d6a4..84996896ab 100644
--- a/ext/gdbm/gdbm.c
+++ b/ext/gdbm/gdbm.c
@@ -65,6 +65,8 @@ fgdbm_close(obj)
return Qnil;
}
+static VALUE fgdbm_s_alloc _((VALUE));
+
static VALUE
fgdbm_s_alloc(klass)
VALUE klass;
@@ -350,6 +352,8 @@ fgdbm_select(argc, argv, obj)
}
}
else {
+ rb_warn("GDBM#select(index..) is deprecated; use GDBM#values_at");
+
for (i=0; i<argc; i++) {
rb_ary_push(new, rb_gdbm_fetch3(obj, argv[i]));
}
@@ -359,6 +363,22 @@ fgdbm_select(argc, argv, obj)
}
static VALUE
+fgdbm_values_at(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE new = rb_ary_new2(argc);
+ int i;
+
+ for (i=0; i<argc; i++) {
+ rb_ary_push(new, rb_gdbm_fetch3(obj, argv[i]));
+ }
+
+ return new;
+}
+
+static VALUE
rb_gdbm_delete(obj, keystr)
VALUE obj, keystr;
{
@@ -511,6 +531,8 @@ fgdbm_invert(obj)
return hash;
}
+static VALUE each_pair _((VALUE));
+
static VALUE
each_pair(obj)
VALUE obj;
@@ -921,7 +943,7 @@ Init_gdbm()
rb_eGDBMFatalError = rb_define_class("GDBMFatalError", rb_eException);
rb_include_module(rb_cGDBM, rb_mEnumerable);
- rb_define_singleton_method(rb_cGDBM, "allocate", fgdbm_s_alloc, 0);
+ rb_define_alloc_func(rb_cGDBM, fgdbm_s_alloc);
rb_define_singleton_method(rb_cGDBM, "open", fgdbm_s_open, -1);
rb_define_method(rb_cGDBM, "initialize", fgdbm_initialize, -1);
@@ -934,6 +956,7 @@ Init_gdbm()
rb_define_method(rb_cGDBM, "indexes", fgdbm_indexes, -1);
rb_define_method(rb_cGDBM, "indices", fgdbm_indexes, -1);
rb_define_method(rb_cGDBM, "select", fgdbm_select, -1);
+ rb_define_method(rb_cGDBM, "values_at", fgdbm_values_at, -1);
rb_define_method(rb_cGDBM, "length", fgdbm_length, 0);
rb_define_method(rb_cGDBM, "size", fgdbm_length, 0);
rb_define_method(rb_cGDBM, "empty?", fgdbm_empty_p, 0);
diff --git a/ext/gdbm/testgdbm.rb b/ext/gdbm/testgdbm.rb
index 524d3f8ca3..529b0010a2 100644
--- a/ext/gdbm/testgdbm.rb
+++ b/ext/gdbm/testgdbm.rb
@@ -279,11 +279,11 @@ class TestGDBM < RUNIT::TestCase
assert_equals(values.reverse, @gdbm.indexes(*keys.reverse))
end
- def test_select
+ def test_values_at
keys = %w(foo bar baz)
values = %w(FOO BAR BAZ)
@gdbm[keys[0]], @gdbm[keys[1]], @gdbm[keys[2]] = values
- assert_equals(values.reverse, @gdbm.select(*keys.reverse))
+ assert_equals(values.reverse, @gdbm.values_at(*keys.reverse))
end
def test_select_with_block
diff --git a/ext/iconv/MANIFEST b/ext/iconv/MANIFEST
index 643f3b7f4d..fd7e22deda 100644
--- a/ext/iconv/MANIFEST
+++ b/ext/iconv/MANIFEST
@@ -2,3 +2,4 @@ MANIFEST
extconf.rb
iconv.c
depend
+charset_alias.rb
diff --git a/ext/iconv/charset_alias.rb b/ext/iconv/charset_alias.rb
new file mode 100644
index 0000000000..20a7f6406a
--- /dev/null
+++ b/ext/iconv/charset_alias.rb
@@ -0,0 +1,36 @@
+#! /usr/bin/ruby
+require 'rbconfig'
+
+# http://www.ctan.org/tex-archive/macros/texinfo/texinfo/intl/config.charset'
+# Fri, 30 May 2003 00:09:00 GMT'
+
+OS = Config::CONFIG["target"]
+SHELL = Config::CONFIG['SHELL']
+
+def charset_alias(config_charset, mapfile)
+ map = {}
+ comments = []
+ IO.foreach("|#{SHELL} #{config_charset} #{OS}") do |list|
+ next comments << list if /^\#/ =~ list
+ next unless /^(\S+)\s+(\S+)$/ =~ list
+ sys, can = $1, $2
+ next if sys == can
+ next if can.downcase! and sys == can
+ map[can] = sys
+ end
+ case OS
+ when /linux|-gnu/
+ map.delete('ascii')
+ end
+ open(mapfile, "w") do |f|
+ f.puts("require 'iconv.so'")
+ f.puts
+ f.puts(comments)
+ f.puts("class Iconv")
+ map.each {|can, sys| f.puts(" charset_map['#{can}'.freeze] = '#{sys}'.freeze")}
+ f.puts("end")
+ end
+end
+
+ARGV.size == 2 or abort "usage: #$0 config.status map.rb"
+charset_alias(*ARGV)
diff --git a/ext/iconv/extconf.rb b/ext/iconv/extconf.rb
index 55d9c5da5f..7176fa45e3 100644
--- a/ext/iconv/extconf.rb
+++ b/ext/iconv/extconf.rb
@@ -2,7 +2,42 @@ require 'mkmf'
dir_config("iconv")
+conf = File.exist?(File.join($srcdir, "config.charset"))
+conf = with_config("config-charset", enable_config("config-charset", conf))
+
if have_header("iconv.h")
+ if !try_compile("", "-Werror") or checking_for("iconv() 2nd argument is const") do
+ !try_compile('
+#include <iconv.h>
+size_t
+test(iconv_t cd, char **inptr, size_t *inlen, char **outptr, size_t *outlen)
+{
+ return iconv(cd, inptr, inlen, outptr, outlen);
+}
+', "-Werror")
+ end
+ $defs.push('-DICONV_INPTR_CAST=""')
+ else
+ $defs.push('-DICONV_INPTR_CAST="(char **)"')
+ end
have_library("iconv")
+ if conf
+ prefix = '$(srcdir)'
+ prefix = $nmake ? "{#{prefix}}" : "#{prefix}/"
+ $INSTALLFILES = [["./iconv.rb", "$(RUBYLIBDIR)"]]
+ if String === conf
+ require 'uri'
+ scheme = URI.parse(conf).scheme
+ else
+ conf = prefix + "config.charset"
+ end
+ end
create_makefile("iconv")
+ if conf
+ open("Makefile", "a") do |mf|
+ mf.print("\nall: iconv.rb\n\niconv.rb: ", prefix, "charset_alias.rb")
+ mf.print(" ", conf) unless scheme
+ mf.print("\n\t$(RUBY) ", prefix, "charset_alias.rb ", conf, " $@\n")
+ end
+ end
end
diff --git a/ext/iconv/iconv.c b/ext/iconv/iconv.c
index a7b1b4118e..cbb89dca48 100644
--- a/ext/iconv/iconv.c
+++ b/ext/iconv/iconv.c
@@ -32,10 +32,11 @@ Which coding systems are available, it depends on the platform.
=end
*/
+#include "ruby.h"
#include <errno.h>
#include <iconv.h>
#include <assert.h>
-#include "ruby.h"
+#include "st.h"
#include "intern.h"
/* Invalid value for iconv_t is -1 but 0 for VALUE, I hope VALUE is
@@ -63,6 +64,7 @@ static VALUE iconv_failure_success _((VALUE self));
static VALUE iconv_failure_failed _((VALUE self));
static iconv_t iconv_create _((VALUE to, VALUE from));
+static void iconv_dfree _((void *cd));
static VALUE iconv_free _((VALUE cd));
static VALUE iconv_try _((iconv_t cd, const char **inptr, size_t *inlen, char **outptr, size_t *outlen));
static VALUE rb_str_derive _((VALUE str, const char* ptr, int len));
@@ -88,18 +90,46 @@ static VALUE iconv_iconv _((int argc, VALUE *argv, VALUE self));
== Iconv
=end
*/
+static VALUE charset_map;
+
+static VALUE charset_map_get _((void))
+{
+ return charset_map;
+}
+
+static char *
+map_charset
+#ifdef HAVE_PROTOTYPES
+ (VALUE *code)
+#else /* HAVE_PROTOTYPES */
+ (code)
+ VALUE *code;
+#endif /* HAVE_PROTOTYPES */
+{
+ VALUE val = *code;
+
+ StringValuePtr(val);
+ if (RHASH(charset_map)->tbl && RHASH(charset_map)->tbl->num_entries) {
+ if (st_lookup(RHASH(charset_map)->tbl, val, &val)) {
+ StringValuePtr(val);
+ *code = val;
+ }
+ }
+ return RSTRING(val)->ptr;
+}
+
static iconv_t
iconv_create
#ifdef HAVE_PROTOTYPES
-(VALUE to, VALUE from)
+ (VALUE to, VALUE from)
#else /* HAVE_PROTOTYPES */
(to, from)
VALUE to;
VALUE from;
#endif /* HAVE_PROTOTYPES */
{
- const char* tocode = StringValuePtr(to);
- const char* fromcode = StringValuePtr(from);
+ const char* tocode = map_charset(&to);
+ const char* fromcode = map_charset(&from);
iconv_t cd = iconv_open(tocode, fromcode);
@@ -122,10 +152,24 @@ iconv_create
return cd;
}
+static void
+iconv_dfree
+#ifdef HAVE_PROTOTYPES
+ (void *cd)
+#else /* HAVE_PROTOTYPES */
+ (cd)
+ void *cd;
+#endif /* HAVE_PROTOTYPES */
+{
+ iconv_close(VALUE2ICONV(cd));
+}
+
+#define ICONV_FREE iconv_dfree
+
static VALUE
iconv_free
#ifdef HAVE_PROTOTYPES
-(VALUE cd)
+ (VALUE cd)
#else /* HAVE_PROTOTYPES */
(cd)
VALUE cd;
@@ -136,12 +180,26 @@ iconv_free
return Qnil;
}
-#define ICONV_FREE (RUBY_DATA_FUNC)iconv_free
+static VALUE
+check_iconv
+#ifdef HAVE_PROTOTYPES
+ (VALUE obj)
+#else /* HAVE_PROTOTYPES */
+ (obj)
+ VALUE obj;
+#endif /* HAVE_PROTOTYPES */
+{
+ Check_Type(obj, T_DATA);
+ if (RDATA(obj)->dfree != ICONV_FREE) {
+ rb_raise(rb_eArgError, "Iconv expected (%s)", rb_class2name(CLASS_OF(obj)));
+ }
+ return (VALUE)DATA_PTR(obj);
+}
static VALUE
iconv_try
#ifdef HAVE_PROTOTYPES
-(iconv_t cd, const char **inptr, size_t *inlen, char **outptr, size_t *outlen)
+ (iconv_t cd, const char **inptr, size_t *inlen, char **outptr, size_t *outlen)
#else /* HAVE_PROTOTYPES */
(cd, inptr, inlen, outptr, outlen)
iconv_t cd;
@@ -151,7 +209,7 @@ iconv_try
size_t *outlen;
#endif /* HAVE_PROTOTYPES */
{
- if (iconv(cd, (char **)inptr, inlen, outptr, outlen) == (size_t)-1) {
+ if (iconv(cd, ICONV_INPTR_CAST inptr, inlen, outptr, outlen) == (size_t)-1) {
if (!*inlen)
return Qfalse;
switch (errno) {
@@ -176,10 +234,12 @@ iconv_try
#define iconv_fail(error, success, failed, env) \
rb_exc_raise(iconv_failure_initialize(error, success, failed, env))
+#define FAILED_MAXLEN 16
+
static VALUE
iconv_failure_initialize
#ifdef HAVE_PROTOTYPES
-(VALUE error, VALUE success, VALUE failed, struct iconv_env_t* env)
+ (VALUE error, VALUE success, VALUE failed, struct iconv_env_t* env)
#else /* HAVE_PROTOTYPES */
(error, success, failed, env)
VALUE error;
@@ -188,8 +248,16 @@ iconv_failure_initialize
struct iconv_env_t *env;
#endif /* HAVE_PROTOTYPES */
{
- if (!rb_ivar_defined(error, rb_mesg) || NIL_P(rb_ivar_get(error, rb_mesg)))
- rb_ivar_set(error, rb_mesg, rb_inspect(failed));
+ if (NIL_P(rb_attr_get(error, rb_mesg))) {
+ if (TYPE(failed) != T_STRING || RSTRING(failed)->len < FAILED_MAXLEN) {
+ rb_ivar_set(error, rb_mesg, rb_inspect(failed));
+ }
+ else {
+ VALUE mesg = rb_inspect(rb_str_substr(failed, 0, FAILED_MAXLEN));
+ rb_str_cat2(mesg, "...");
+ rb_ivar_set(error, rb_mesg, mesg);
+ }
+ }
if (env) {
success = rb_funcall3(env->ret, rb_inserter, 1, &success);
if (env->argc > 0) {
@@ -205,7 +273,7 @@ iconv_failure_initialize
static VALUE
rb_str_derive
#ifdef HAVE_PROTOTYPES
-(VALUE str, const char* ptr, int len)
+ (VALUE str, const char* ptr, int len)
#else /* HAVE_PROTOTYPES */
(str, ptr, len)
VALUE str;
@@ -219,7 +287,10 @@ rb_str_derive
return rb_str_new(ptr, len);
if (RSTRING(str)->ptr == ptr && RSTRING(str)->len == len)
return str;
- ret = rb_str_new(ptr, len);
+ if (RSTRING(str)->ptr + RSTRING(str)->len == ptr + len)
+ ret = rb_str_substr(str, ptr - RSTRING(str)->ptr, len);
+ else
+ ret = rb_str_new(ptr, len);
OBJ_INFECT(ret, str);
return ret;
}
@@ -227,7 +298,7 @@ rb_str_derive
static VALUE
iconv_convert
#ifdef HAVE_PROTOTYPES
-(iconv_t cd, VALUE str, int start, int length, struct iconv_env_t* env)
+ (iconv_t cd, VALUE str, int start, int length, struct iconv_env_t* env)
#else /* HAVE_PROTOTYPES */
(cd, str, start, length, env)
iconv_t cd;
@@ -265,7 +336,7 @@ iconv_convert
else {
int slen;
- Check_Type(str, T_STRING);
+ StringValue(str);
slen = RSTRING(str)->len;
inptr = RSTRING(str)->ptr;
@@ -330,6 +401,8 @@ iconv_convert
if (!ret)
ret = rb_str_derive(str, instart, inptr - instart);
+ else if (inptr > instart)
+ rb_str_cat(ret, instart, inptr - instart);
return ret;
}
@@ -366,7 +439,7 @@ iconv_convert
static VALUE
iconv_s_allocate
#ifdef HAVE_PROTOTYPES
-(VALUE klass)
+ (VALUE klass)
#else /* HAVE_PROTOTYPES */
(klass)
VALUE klass;
@@ -378,7 +451,7 @@ iconv_s_allocate
static VALUE
iconv_initialize
#ifdef HAVE_PROTOTYPES
-(VALUE self, VALUE to, VALUE from)
+ (VALUE self, VALUE to, VALUE from)
#else /* HAVE_PROTOTYPES */
(self, to, from)
VALUE self;
@@ -386,7 +459,7 @@ iconv_initialize
VALUE from;
#endif /* HAVE_PROTOTYPES */
{
- iconv_free((VALUE)(DATA_PTR(self)));
+ iconv_free(check_iconv(self));
DATA_PTR(self) = NULL;
DATA_PTR(self) = (void *)ICONV2VALUE(iconv_create(to, from));
return self;
@@ -395,7 +468,7 @@ iconv_initialize
static VALUE
iconv_s_open
#ifdef HAVE_PROTOTYPES
-(VALUE self, VALUE to, VALUE from)
+ (VALUE self, VALUE to, VALUE from)
#else /* HAVE_PROTOTYPES */
(self, to, from)
VALUE self;
@@ -405,12 +478,12 @@ iconv_s_open
{
VALUE cd = ICONV2VALUE(iconv_create(to, from));
+ self = Data_Wrap_Struct(self, NULL, ICONV_FREE, (void *)cd);
if (rb_block_given_p()) {
- self = Data_Wrap_Struct(self, NULL, NULL, (void *)cd);
return rb_ensure(rb_yield, self, (VALUE(*)())iconv_finish, self);
}
else {
- return Data_Wrap_Struct(self, NULL, ICONV_FREE, (void *)cd);
+ return self;
}
}
@@ -433,7 +506,7 @@ iconv_s_open
static VALUE
iconv_s_convert
#ifdef HAVE_PROTOTYPES
-(struct iconv_env_t* env)
+ (struct iconv_env_t* env)
#else /* HAVE_PROTOTYPES */
(env)
struct iconv_env_t *env;
@@ -458,7 +531,7 @@ iconv_s_convert
static VALUE
iconv_s_iconv
#ifdef HAVE_PROTOTYPES
-(int argc, VALUE *argv, VALUE self)
+ (int argc, VALUE *argv, VALUE self)
#else /* HAVE_PROTOTYPES */
(argc, argv, self)
int argc;
@@ -478,6 +551,24 @@ iconv_s_iconv
return rb_ensure(iconv_s_convert, (VALUE)&arg, iconv_free, ICONV2VALUE(arg.cd));
}
+static VALUE
+iconv_s_conv
+#ifdef HAVE_PROTOTYPES
+ (VALUE self, VALUE to, VALUE from, VALUE str)
+#else /* HAVE_PROTOTYPES */
+ (self, to, from, str)
+ VALUE self, to, from, str;
+#endif /* HAVE_PROTOTYPES */
+{
+ struct iconv_env_t arg;
+
+ arg.argc = 1;
+ arg.argv = &str;
+ arg.ret = rb_str_new(0, 0);
+ arg.cd = iconv_create(to, from);
+ return rb_ensure(iconv_s_convert, (VALUE)&arg, iconv_free, ICONV2VALUE(arg.cd));
+}
+
/*
=begin
@@ -498,7 +589,7 @@ iconv_s_iconv
static VALUE
iconv_init_state
#ifdef HAVE_PROTOTYPES
-(VALUE cd)
+ (VALUE cd)
#else /* HAVE_PROTOTYPES */
(cd)
VALUE cd;
@@ -510,17 +601,14 @@ iconv_init_state
static VALUE
iconv_finish
#ifdef HAVE_PROTOTYPES
-(VALUE self)
+ (VALUE self)
#else /* HAVE_PROTOTYPES */
(self)
VALUE self;
#endif /* HAVE_PROTOTYPES */
{
- VALUE cd;
-
- Check_Type(self, T_DATA);
+ VALUE cd = check_iconv(self);
- cd = (VALUE)DATA_PTR(self);
if (!cd) return Qnil;
DATA_PTR(self) = NULL;
@@ -555,7 +643,7 @@ iconv_finish
static VALUE
iconv_iconv
#ifdef HAVE_PROTOTYPES
-(int argc, VALUE *argv, VALUE self)
+ (int argc, VALUE *argv, VALUE self)
#else /* HAVE_PROTOTYPES */
(argc, argv, self)
int argc;
@@ -564,13 +652,12 @@ iconv_iconv
#endif /* HAVE_PROTOTYPES */
{
VALUE str, n1, n2;
-
- Check_Type(self, T_DATA);
+ VALUE cd = check_iconv(self);
n1 = n2 = Qnil;
rb_scan_args(argc, argv, "12", &str, &n1, &n2);
- return iconv_convert(VALUE2ICONV(DATA_PTR(self)), str,
+ return iconv_convert(VALUE2ICONV(cd), str,
NIL_P(n1) ? 0 : NUM2INT(n1),
NIL_P(n2) ? -1 : NUM2INT(n1),
NULL);
@@ -608,7 +695,7 @@ iconv_failure_success
VALUE self;
#endif /* HAVE_PROTOTYPES */
{
- return rb_ivar_get(self, rb_success);
+ return rb_attr_get(self, rb_success);
}
/*
@@ -627,7 +714,7 @@ iconv_failure_failed
VALUE self;
#endif /* HAVE_PROTOTYPES */
{
- return rb_ivar_get(self, rb_failed);
+ return rb_attr_get(self, rb_failed);
}
/*
@@ -639,15 +726,15 @@ iconv_failure_failed
static VALUE
iconv_failure_inspect
#ifdef HAVE_PROTOTYPES
-(VALUE self)
+ (VALUE self)
#else /* HAVE_PROTOTYPES */
(self)
VALUE self;
#endif /* HAVE_PROTOTYPES */
{
char *cname = rb_class2name(CLASS_OF(self));
- VALUE success = iconv_failure_success(self);
- VALUE failed = iconv_failure_failed(self);
+ VALUE success = rb_attr_get(self, rb_success);
+ VALUE failed = rb_attr_get(self, rb_failed);
VALUE str = rb_str_buf_cat2(rb_str_new2("#<"), cname);
str = rb_str_buf_cat(str, ": ", 2);
str = rb_str_buf_append(str, rb_inspect(success));
@@ -690,9 +777,12 @@ void
Init_iconv _((void))
{
VALUE rb_cIconv = rb_define_class("Iconv", rb_cData);
- rb_define_singleton_method(rb_cIconv, "allocate", iconv_s_allocate, 0);
+ VALUE metaclass = RBASIC(rb_cIconv)->klass;
+
+ rb_define_alloc_func(rb_cIconv, iconv_s_allocate);
rb_define_singleton_method(rb_cIconv, "open", iconv_s_open, 2);
rb_define_singleton_method(rb_cIconv, "iconv", iconv_s_iconv, -1);
+ rb_define_singleton_method(rb_cIconv, "conv", iconv_s_conv, 3);
rb_define_method(rb_cIconv, "initialize", iconv_initialize, 2);
rb_define_method(rb_cIconv, "close", iconv_finish, 0);
rb_define_method(rb_cIconv, "iconv", iconv_iconv, -1);
@@ -713,6 +803,10 @@ Init_iconv _((void))
rb_success = rb_intern("success");
rb_failed = rb_intern("failed");
rb_mesg = rb_intern("mesg");
+
+ charset_map = rb_hash_new();
+ rb_gc_register_address(&charset_map);
+ rb_define_singleton_method(rb_cIconv, "charset_map", charset_map_get, 0);
}
diff --git a/ext/io/wait/MANIFEST b/ext/io/wait/MANIFEST
new file mode 100644
index 0000000000..a08526c13a
--- /dev/null
+++ b/ext/io/wait/MANIFEST
@@ -0,0 +1,4 @@
+MANIFEST
+extconf.rb
+wait.c
+lib/nonblock.rb
diff --git a/ext/io/wait/extconf.rb b/ext/io/wait/extconf.rb
new file mode 100644
index 0000000000..a766ccbe6f
--- /dev/null
+++ b/ext/io/wait/extconf.rb
@@ -0,0 +1,12 @@
+require 'mkmf'
+target = "io/wait"
+
+unless /djgpp|mswin|mingw|human/ =~ RUBY_PLATFORM
+ fionread = %w[sys/ioctl.h sys/filio.h].find do |h|
+ checking_for("FIONREAD") {macro_defined?("FIONREAD", "#include <#{h}>\n")}
+ end
+ if fionread
+ $defs << "-DFIONREAD_HEADER=\"<#{fionread}>\""
+ create_makefile(target)
+ end
+end
diff --git a/ext/io/wait/lib/nonblock.rb b/ext/io/wait/lib/nonblock.rb
new file mode 100644
index 0000000000..46511fb40c
--- /dev/null
+++ b/ext/io/wait/lib/nonblock.rb
@@ -0,0 +1,23 @@
+require "fcntl"
+class IO
+ def nonblock?
+ (fcntl(Fcntl::F_GETFL) & File::NONBLOCK) != 0
+ end
+
+ def nonblock=(nb)
+ f = fcntl(Fcntl::F_GETFL)
+ if nb
+ f |= File::NONBLOCK
+ else
+ f &= ~File::NONBLOCK
+ end
+ fcntl(Fcntl::F_SETFL, f)
+ end
+
+ def nonblock(nb = true)
+ nb, self.nonblock = nonblock?, nb
+ yield
+ ensure
+ self.nonblock = nb
+ end
+end
diff --git a/ext/io/wait/wait.c b/ext/io/wait/wait.c
new file mode 100644
index 0000000000..53d5bd7d18
--- /dev/null
+++ b/ext/io/wait/wait.c
@@ -0,0 +1,106 @@
+/**********************************************************************
+
+ io/wait.c -
+
+ $Author$
+ $Date$
+ created at: Tue Aug 28 09:08:06 JST 2001
+
+ All the files in this distribution are covered under the Ruby's
+ license (see the file COPYING).
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "rubyio.h"
+
+#include <sys/types.h>
+#include FIONREAD_HEADER
+
+static VALUE io_ready_p _((VALUE io));
+static VALUE io_wait _((int argc, VALUE *argv, VALUE io));
+void Init_wait _((void));
+
+EXTERN struct timeval rb_time_interval _((VALUE time));
+
+/*
+=begin
+= IO wait methods.
+=end
+ */
+
+/*
+=begin
+--- IO#ready?
+ returns non-nil if input available without blocking, or nil.
+=end
+*/
+static VALUE
+io_ready_p(io)
+ VALUE io;
+{
+ OpenFile *fptr;
+ FILE *fp;
+ int n;
+
+ GetOpenFile(io, fptr);
+ rb_io_check_readable(fptr);
+ fp = fptr->f;
+ if (feof(fp)) return Qfalse;
+ if (rb_read_pending(fp)) return Qtrue;
+ if (ioctl(fileno(fp), FIONREAD, &n)) rb_sys_fail(0);
+ if (n > 0) return INT2NUM(n);
+ return Qnil;
+}
+
+/*
+=begin
+--- IO#wait([timeout])
+ waits until input available or timed out and returns self, or nil
+ when EOF reached.
+=end
+*/
+static VALUE
+io_wait(argc, argv, io)
+ int argc;
+ VALUE *argv;
+ VALUE io;
+{
+ OpenFile *fptr;
+ fd_set rd;
+ FILE *fp;
+ int fd, n;
+ VALUE timeout;
+ struct timeval *tp, timerec;
+
+ GetOpenFile(io, fptr);
+ rb_io_check_readable(fptr);
+ rb_scan_args(argc, argv, "01", &timeout);
+ if (NIL_P(timeout)) {
+ tp = 0;
+ }
+ else {
+ timerec = rb_time_interval(timeout);
+ tp = &timerec;
+ }
+
+ fp = fptr->f;
+ if (feof(fp)) return Qfalse;
+ if (rb_read_pending(fp)) return Qtrue;
+ fd = fileno(fp);
+ FD_ZERO(&rd);
+ FD_SET(fd, &rd);
+ if (rb_thread_select(fd + 1, &rd, NULL, NULL, tp) < 0)
+ rb_sys_fail(0);
+ rb_io_check_closed(fptr);
+ if (ioctl(fileno(fp), FIONREAD, &n)) rb_sys_fail(0);
+ if (n > 0) return io;
+ return Qnil;
+}
+
+void
+Init_wait()
+{
+ rb_define_method(rb_cIO, "ready?", io_ready_p, 0);
+ rb_define_method(rb_cIO, "wait", io_wait, -1);
+}
diff --git a/ext/openssl/MANIFEST b/ext/openssl/MANIFEST
new file mode 100644
index 0000000000..50ce40bc66
--- /dev/null
+++ b/ext/openssl/MANIFEST
@@ -0,0 +1,66 @@
+MANIFEST
+extconf.rb
+lib/net/ftptls.rb
+lib/net/https.rb
+lib/net/protocols.rb
+lib/net/telnets.rb
+lib/openssl.rb
+lib/openssl/bn.rb
+lib/openssl/buffering.rb
+lib/openssl/cipher.rb
+lib/openssl/digest.rb
+lib/openssl/ssl.rb
+lib/openssl/x509.rb
+openssl_missing.c
+openssl_missing.h
+ossl.c
+ossl.h
+ossl_bn.c
+ossl_bn.h
+ossl_cipher.c
+ossl_cipher.h
+ossl_config.c
+ossl_config.h
+ossl_digest.c
+ossl_digest.h
+ossl_hmac.c
+ossl_hmac.h
+ossl_ns_spki.c
+ossl_ns_spki.h
+ossl_ocsp.c
+ossl_ocsp.h
+ossl_pkcs7.c
+ossl_pkcs7.h
+ossl_pkey.c
+ossl_pkey.h
+ossl_pkey_dh.c
+ossl_pkey_dsa.c
+ossl_pkey_rsa.c
+ossl_rand.c
+ossl_rand.h
+ossl_ssl.c
+ossl_ssl.h
+ossl_version.h
+ossl_x509.c
+ossl_x509.h
+ossl_x509attr.c
+ossl_x509cert.c
+ossl_x509crl.c
+ossl_x509ext.c
+ossl_x509name.c
+ossl_x509req.c
+ossl_x509revoked.c
+ossl_x509store.c
+ruby_missing.h
+sample/c_rehash.rb
+sample/cipher.rb
+sample/echo_cli.rb
+sample/echo_svr.rb
+sample/gen_csr.rb
+sample/smime_read.rb
+sample/smime_write.rb
+sample/wget.rb
+sample/cert2text.rb
+sample/cert_store_view.rb
+sample/certstore.rb
+sample/crlstore.rb
diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb
new file mode 100644
index 0000000000..76befa5a5f
--- /dev/null
+++ b/ext/openssl/extconf.rb
@@ -0,0 +1,128 @@
+=begin
+= $RCSfile$ -- Generator for Makefile
+
+= Info
+ 'OpenSSL for Ruby 2' project
+ Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ All rights reserved.
+
+= Licence
+ This program is licenced under the same licence as Ruby.
+ (See the file 'LICENCE'.)
+
+= Version
+ $Id$
+=end
+
+require "mkmf"
+
+dir_config("openssl")
+dir_config("kerberos")
+pkgconfig = with_config("pkg-config", !CROSS_COMPILING && "pkg-config")
+
+message "=== OpenSSL for Ruby configurator ===\n"
+
+##
+# Adds -Wall -DOSSL_DEBUG for compilation and some more targets when GCC is used
+# To turn it on, use: --with-debug or --enable-debug
+#
+if with_config("debug") or enable_config("debug")
+ $defs.push("-DOSSL_DEBUG") unless $defs.include? "-DOSSL_DEBUG"
+
+ if /gcc/ =~ CONFIG["CC"]
+ $CPPFLAGS += " -Wall" unless $CPPFLAGS.split.include? "-Wall"
+ end
+end
+
+
+
+message "=== Checking for system dependent stuff... ===\n"
+have_library("nsl", "t_open")
+have_library("socket", "socket")
+have_header("unistd.h")
+have_header("sys/time.h")
+
+message "=== Checking for required stuff... ===\n"
+if $mingw
+ have_library("wsock32")
+ have_library("gdi32")
+end
+result = have_header("openssl/ssl.h")
+result &&= %w[crypto libeay32].any? {|lib| have_library(lib, "OpenSSL_add_all_digests")}
+result &&= %w[ssl ssleay32].any? {|lib| have_library(lib, "SSL_library_init")}
+if !result
+ if find_executable(pkgconfig) and system(pkgconfig, "--exists", "openssl")
+ $CFLAGS += " " << `#{pkgconfig} --cflags openssl`.chomp
+ $DLDFLAGS += " " << `#{pkgconfig} --libs-only-L openssl`.chomp
+ $LIBS += " " << `#{pkgconfig} --libs-only-l openssl`.chomp
+ result = have_header("openssl/ssl.h")
+ end
+ if !result
+ message "=== Checking for required stuff failed. ===\n"
+ message "Makefile wasn't created. Fix the errors above.\n"
+ exit 1
+ end
+end
+
+message "=== Checking for OpenSSL features... ===\n"
+have_func("HMAC_CTX_copy")
+have_func("X509_STORE_get_ex_data")
+have_func("X509_STORE_set_ex_data")
+have_func("EVP_MD_CTX_create")
+have_func("EVP_MD_CTX_cleanup")
+have_func("EVP_MD_CTX_destroy")
+have_func("PEM_def_callback")
+have_func("EVP_MD_CTX_init")
+have_func("HMAC_CTX_init")
+have_func("HMAC_CTX_cleanup")
+have_func("X509_CRL_set_version")
+have_func("X509_CRL_set_issuer_name")
+have_func("X509_CRL_sort")
+have_func("X509_CRL_add0_revoked")
+have_func("CONF_get1_default_config_file")
+have_func("BN_mod_sqr")
+have_func("BN_mod_add")
+have_func("BN_mod_sub")
+have_func("BN_rand_range")
+have_func("BN_pseudo_rand_range")
+have_func("CONF_get1_default_config_file")
+if try_cpp("#define FOO(a, ...) foo(a, ##__VA_ARGS__)\n int x(){FOO(1,2);}\n")
+ $defs.push("-DHAVE_VA_ARGS_MACRO")
+end
+have_header("openssl/ocsp.h")
+have_struct_member("EVP_CIPHER_CTX", "flags", "openssl/evp.h")
+
+message "=== Checking for Ruby features... ===\n"
+have_func("rb_obj_init_copy", "ruby.h")
+
+message "=== Checking done. ===\n"
+$distcleanfiles << "GNUmakefile" << "dep"
+create_makefile("openssl")
+if /gcc/ =~ CONFIG["CC"]
+ File.open("GNUmakefile", "w") {|f|
+ f.print <<EOD
+include Makefile
+
+SRCS = $(OBJS:.o=.c)
+
+test-link: $(OBJS)
+ $(CC) $(DLDFLAGS) #{OUTFLAG}.testlink $(OBJS) $(LIBPATH) $(LIBS) $(LOCAL_LIBS)
+ @$(RM) .testlink
+ @echo "Done."
+
+dep: $(SRCS)
+ $(CC) $(CFLAGS) $(CPPFLAGS) -c $^ -MM | \\
+ $(RUBY) -p -e 'BEGIN{S = []' \\
+ -e 'while !ARGV.empty? and /^(\\w+)=(.*)/ =~ ARGV[0]' \\
+ -e 'S << [/\#{Regexp.quote($$2)}\\//, "$$(\#{$$1})/"]' \\
+ -e 'ARGV.shift' \\
+ -e 'end' \\
+ -e '}' -e 'S.each(&method(:gsub!))' -- \\
+ 'topdir=$(topdir)' 'srcdir=$(srcdir)' 'hdrdir=$(hdrdir)' \\
+ > dep
+
+include dep
+EOD
+ }
+end
+message "Done.\n"
diff --git a/ext/openssl/lib/net/ftptls.rb b/ext/openssl/lib/net/ftptls.rb
new file mode 100644
index 0000000000..f433457923
--- /dev/null
+++ b/ext/openssl/lib/net/ftptls.rb
@@ -0,0 +1,43 @@
+=begin
+= $RCSfile$ -- SSL/TLS enhancement for Net::HTTP.
+
+= Info
+ 'OpenSSL for Ruby 2' project
+ Copyright (C) 2003 Blaz Grilc <farmer@gmx.co.uk>
+ All rights reserved.
+
+= Licence
+ This program is licenced under the same licence as Ruby.
+ (See the file 'LICENCE'.)
+
+= Requirements
+
+= Version
+ $Id$
+
+= Notes
+ Tested on FreeBSD 5-CURRENT and 4-STABLE
+ - ruby 1.6.8 (2003-01-17) [i386-freebsd5]
+ - OpenSSL 0.9.7a Feb 19 2003
+ - ruby-openssl-0.2.0.p0
+ tested on ftp server: glftpd 1.30
+=end
+
+require 'socket'
+require 'openssl'
+require 'net/ftp'
+
+module Net
+ class FTPTLS < FTP
+ def login(user = "anonymous", passwd = nil, acct = nil)
+ ctx = OpenSSL::SSL::SSLContext.new('SSLv23')
+ ctx.key = nil
+ ctx.cert = nil
+ voidcmd("AUTH TLS")
+ @sock = OpenSSL::SSL::SSLSocket.new(@sock, ctx)
+ @sock.connect
+ super(user, passwd, acct)
+ voidcmd("PBSZ 0")
+ end
+ end
+end
diff --git a/ext/openssl/lib/net/https.rb b/ext/openssl/lib/net/https.rb
new file mode 100644
index 0000000000..fb7f53c555
--- /dev/null
+++ b/ext/openssl/lib/net/https.rb
@@ -0,0 +1,188 @@
+=begin
+= $RCSfile$ -- 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$
+
+ 2001/11/06: Contiributed to Ruby/OpenSSL project.
+
+== class Net::HTTP
+
+== Example
+
+Simple HTTP client is here:
+
+ require 'net/http'
+ host, port, path = "localhost", 80, "/"
+ if %r!http://(.*?)(?::(\d+))?(/.*)! =~ ARGV[0]
+ host = $1
+ port = $2.to_i if $2
+ path = $3
+ end
+ h = Net::HTTP.new(host, port)
+ h.get2(path){ |resp| print resp.body }
+
+It can be replaced by follow one:
+
+ require 'net/https'
+ host, port, path = "localhost", 80, "/"
+ if %r!(https?)://(.*?)(?::(\d+))?(/.*)! =~ ARGV[0]
+ scheme = $1
+ host = $2
+ port = $3 ? $3.to_i : ((scheme == "http") ? 80 : 443)
+ path = $4
+ end
+ h = Net::HTTP.new(host, port)
+ h.use_ssl = true if scheme == "https" # enable SSL/TLS
+ h.get2(path){ |resp| print resp.body }
+
+=== Instance Methods
+
+: use_ssl
+ returns ture 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|))
+ Sets an OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object.
+ (This method is appeared in Michal Rokos's OpenSSL extention.)
+
+: key_file=((|path|))
+ Sets a private key file to use in PEM format.
+
+: cert=((|cert|))
+ Sets an OpenSSL::X509::Certificate object as client certificate.
+ (This method is appeared in Michal Rokos's OpenSSL extention.)
+
+: cert_file=((|path|))
+ Sets pathname of a X.509 certification file in PEM format.
+
+: ca_file=((|path|))
+ Sets path of a CA certification file in PEM format.
+ The file can contrain several CA certificats.
+
+: ca_path=((|path|))
+ Sets path of a CA certification directory containing certifications
+ in PEM format.
+
+: 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=((|proc|))
+ Sets the verify callback for the server certification verification.
+
+: verify_depth=((|num|))
+ Sets the maximum depth for the certificate chain verification.
+
+: cert_store=((|store|))
+ Sets the X509::Store to verify peer certificate.
+
+=end
+
+require 'net/protocols'
+require 'net/http'
+
+module Net
+ class HTTP
+ class Conn < HTTPRequest
+ REQUEST_HAS_BODY=false
+ RESPONSE_HAS_BODY=false
+ METHOD="connect"
+
+ def initialize
+ super nil, nil
+ end
+
+ def exec( sock, addr, port, ver )
+ @socket = sock
+ request(addr, port, ver)
+ end
+
+ def request( addr, port, ver )
+ @socket.writeline sprintf('CONNECT %s:%s HTTP/%s', addr, port, ver)
+ @socket.writeline ''
+ end
+ end
+
+ module ProxyMod
+ def edit_path( path )
+ if use_ssl
+ 'https://' + addr_port + path
+ else
+ 'http://' + addr_port + path
+ end
+ end
+ end
+
+ def self.socket_type
+ SSLIO
+ end
+
+ attr_reader :use_ssl
+ attr_writer :key, :cert
+ attr_writer :ca_file, :ca_path
+ attr_writer :verify_mode, :verify_callback, :verify_depth
+ attr_writer :cert_store, :timeout
+ attr_reader :peer_cert
+
+ alias :default_initialize :initialize
+
+ def initialize(*args)
+ default_initialize(*args)
+ @key = @cert = @ca_file = @ca_path = @verify_mode =
+ @verify_callback = @verify_depth = @timeout = @cert_store = nil
+ @already_connected = false
+ end
+
+ def use_ssl=(flag)
+ if @already_connected && !@use_ssl
+ raise ProtocolError, "connection is alrady set up"
+ end
+ @use_ssl = flag
+ end
+
+ def on_connect
+ if use_ssl
+ if proxy?
+ Conn.new.exec(@socket, @address, @port, "1.0")
+ resp = HTTPResponse.read_new(@socket)
+ if resp.code != '200'
+ raise resp.message
+ end
+ end
+ @socket.key = @key if @key
+ @socket.cert = @cert if @cert
+ @socket.ca_file = @ca_file
+ @socket.ca_path = @ca_path
+ @socket.verify_mode = @verify_mode
+ @socket.verify_callback = @verify_callback
+ @socket.verify_depth = @verify_depth
+ @socket.timeout = @timeout
+ @socket.cert_store = @cert_store
+ @socket.ssl_connect
+ @peer_cert = @socket.peer_cert
+ end
+ @already_connected = true
+ end
+
+ end
+end
diff --git a/ext/openssl/lib/net/protocols.rb b/ext/openssl/lib/net/protocols.rb
new file mode 100644
index 0000000000..5897716f3d
--- /dev/null
+++ b/ext/openssl/lib/net/protocols.rb
@@ -0,0 +1,57 @@
+=begin
+= $RCSfile$ -- SSL/TLS enhancement for Net.
+
+= 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$
+
+ 2001/11/06: Contiributed to Ruby/OpenSSL project.
+=end
+
+require 'net/protocol'
+require 'forwardable'
+require 'openssl'
+
+module Net
+ class SSLIO < InternetMessageIO
+ extend Forwardable
+
+ def_delegators(:@ssl_context,
+ :key=, :cert=, :key_file=, :cert_file=,
+ :ca_file=, :ca_path=,
+ :verify_mode=, :verify_callback=, :verify_depth=,
+ :timeout=, :cert_store=)
+
+ def initialize(addr, port, otime = nil, rtime = nil, dout = nil)
+ super
+ @ssl_context = OpenSSL::SSL::SSLContext.new()
+ end
+
+ def ssl_connect()
+ @raw_socket = @socket
+ @socket = OpenSSL::SSL::SSLSocket.new(@raw_socket, @ssl_context)
+ @socket.connect
+ end
+
+ def close
+ super
+ @raw_socket.close if @raw_socket
+ end
+
+ def peer_cert
+ @socket.peer_cert
+ end
+ end
+end
diff --git a/ext/openssl/lib/net/telnets.rb b/ext/openssl/lib/net/telnets.rb
new file mode 100644
index 0000000000..c7ecbd717a
--- /dev/null
+++ b/ext/openssl/lib/net/telnets.rb
@@ -0,0 +1,250 @@
+=begin
+= $RCSfile$ -- SSL/TLS enhancement for Net::Telnet.
+
+= 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'.)
+
+= Version
+ $Id$
+
+ 2001/11/06: Contiributed to Ruby/OpenSSL project.
+
+== class Net::Telnet
+
+This class will initiate SSL/TLS session automaticaly if the server
+sent OPT_STARTTLS. Some options are added for SSL/TLS.
+
+ host = Net::Telnet::new({
+ "Host" => "localhost",
+ "Port" => "telnets",
+ ## follows are new options.
+ 'CertFile' => "user.crt",
+ 'KeyFile' => "user.key",
+ 'CAFile' => "/some/where/certs/casert.pem",
+ 'CAPath' => "/some/where/caserts",
+ 'VerifyMode' => SSL::VERIFY_PEER,
+ 'VerifyCallback' => verify_proc
+ })
+
+Or, the new options ('Cert', 'Key' and 'CACert') are available from
+Michal Rokos's OpenSSL module.
+
+ cert_data = File.open("user.crt"){|io| io.read }
+ pkey_data = File.open("user.key"){|io| io.read }
+ cacert_data = File.open("your_ca.pem"){|io| io.read }
+ host = Net::Telnet::new({
+ "Host" => "localhost",
+ "Port" => "telnets",
+ 'Cert' => OpenSSL::X509::Certificate.new(cert_data)
+ 'Key' => OpenSSL::PKey::RSA.new(pkey_data)
+ 'CACert' => OpenSSL::X509::Certificate.new(cacert_data)
+ 'CAFile' => "/some/where/certs/casert.pem",
+ 'CAPath' => "/some/where/caserts",
+ 'VerifyMode' => SSL::VERIFY_PEER,
+ 'VerifyCallback' => verify_proc
+ })
+
+This class is expected to be a superset of usual Net::Telnet.
+=end
+
+require "net/telnet"
+require "openssl"
+
+module Net
+ class Telnet
+ attr_reader :ssl
+
+ OPT_STARTTLS = 46.chr # "\056" # "\x2e" # Start TLS
+ TLS_FOLLOWS = 1.chr # "\001" # "\x01" # FOLLOWS (for STARTTLS)
+
+ alias preprocess_orig preprocess
+
+ def ssl?; @ssl; end
+
+ 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_EXOPL}]|
+ #{SB}[#{OPT_BINARY}-#{OPT_EXOPL}]
+ (#{IAC}#{IAC}|[^#{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)
+ elsif OPT_STARTTLS[0] == $1[1]
+ self.write(IAC + WILL + OPT_STARTTLS)
+ self.write(IAC + SB + OPT_STARTTLS + TLS_FOLLOWS + IAC + SE)
+ 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
+ ''
+ elsif SB[0] == $1[0] # respond to "IAC SB xxx IAC SE"
+ if OPT_STARTTLS[0] == $1[1] && TLS_FOLLOWS[0] == $2[0]
+ @sock = OpenSSL::SSL::SSLSocket.new(@sock)
+ @sock.cert_file = @options['CertFile']
+ @sock.cert = @options['Cert'] unless @sock.cert
+ @sock.key_file = @options['KeyFile']
+ @sock.key = @options['Key'] unless @sock.key
+ @sock.ca_cert = @options['CACert']
+ @sock.ca_file = @options['CAFile']
+ @sock.ca_path = @options['CAPath']
+ @sock.timeout = @options['Timeout']
+ @sock.verify_mode = @options['VerifyMode']
+ @sock.verify_callback = @options['VerifyCallback']
+ @sock.verify_depth = @options['VerifyDepth']
+ @sock.connect
+ @ssl = true
+ end
+ ''
+ else
+ ''
+ end
+ end
+ end # preprocess
+
+ alias waitfor_org waitfor
+
+ def waitfor(options)
+ 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 = '' unless @rest
+
+ until(prompt === line and not IO::select([@sock], nil, nil, waittime))
+ unless IO::select([@sock], nil, nil, time_out)
+ raise TimeoutError, "timed-out; wait for the next data"
+ end
+ begin
+ c = @rest + @sock.sysread(1024 * 1024)
+ @dumplog.log_dump('<', c) if @options.has_key?("Dump_log")
+ if @options["Telnetmode"]
+ pos = 0
+ catch(:next){
+ while true
+ case c[pos]
+ when IAC[0]
+ case c[pos+1]
+ when DO[0], DONT[0], WILL[0], WONT[0]
+ throw :next unless c[pos+2]
+ pos += 3
+ when SB[0]
+ ret = detect_sub_negotiation(c, pos)
+ throw :next unless ret
+ pos = ret
+ when nil
+ throw :next
+ else
+ pos += 2
+ end
+ when nil
+ throw :next
+ else
+ pos += 1
+ end
+ end
+ }
+
+ buf = preprocess(c[0...pos])
+ @rest = c[pos..-1]
+ end
+ @log.print(buf) if @options.has_key?("Output_log")
+ line.concat(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
+
+ private
+
+ def detect_sub_negotiation(data, pos)
+ return nil if data.length < pos+6 # IAC SB x param IAC SE
+ pos += 3
+ while true
+ case data[pos]
+ when IAC[0]
+ if data[pos+1] == SE[0]
+ pos += 2
+ return pos
+ else
+ pos += 2
+ end
+ when nil
+ return nil
+ else
+ pos += 1
+ end
+ end
+ end
+
+ end
+end
diff --git a/ext/openssl/lib/openssl.rb b/ext/openssl/lib/openssl.rb
new file mode 100644
index 0000000000..24a9eed136
--- /dev/null
+++ b/ext/openssl/lib/openssl.rb
@@ -0,0 +1,24 @@
+=begin
+= $RCSfile$ -- Loader for all OpenSSL C-space and Ruby-space definitions
+
+= Info
+ 'OpenSSL for Ruby 2' project
+ Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ All rights reserved.
+
+= Licence
+ This program is licenced under the same licence as Ruby.
+ (See the file 'LICENCE'.)
+
+= Version
+ $Id$
+=end
+
+require 'openssl.so'
+
+require 'openssl/bn'
+require 'openssl/cipher'
+require 'openssl/digest'
+require 'openssl/ssl'
+require 'openssl/x509'
+
diff --git a/ext/openssl/lib/openssl/bn.rb b/ext/openssl/lib/openssl/bn.rb
new file mode 100644
index 0000000000..e7cbf2cfaf
--- /dev/null
+++ b/ext/openssl/lib/openssl/bn.rb
@@ -0,0 +1,35 @@
+=begin
+= $RCSfile$ -- Ruby-space definitions that completes C-space funcs for BN
+
+= Info
+ 'OpenSSL for Ruby 2' project
+ Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ All rights reserved.
+
+= Licence
+ This program is licenced under the same licence as Ruby.
+ (See the file 'LICENCE'.)
+
+= Version
+ $Id$
+=end
+
+##
+# Should we care what if somebody require this file directly?
+#require 'openssl'
+
+module OpenSSL
+ class BN
+ include Comparable
+ end # BN
+end # OpenSSL
+
+##
+# Add double dispatch to Integer
+#
+class Integer
+ def to_bn
+ OpenSSL::BN::new(self)
+ end
+end # Integer
+
diff --git a/ext/openssl/lib/openssl/buffering.rb b/ext/openssl/lib/openssl/buffering.rb
new file mode 100644
index 0000000000..61eb9dcc04
--- /dev/null
+++ b/ext/openssl/lib/openssl/buffering.rb
@@ -0,0 +1,189 @@
+=begin
+= $RCSfile$ -- Buffering mix-in module.
+
+= 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'.)
+
+= Version
+ $Id$
+=end
+
+module Buffering
+ include Enumerable
+ attr_accessor :sync
+ BLOCK_SIZE = 1024
+
+ #
+ # for reading.
+ #
+ private
+
+ def fill_rbuff
+ @rbuffer = "" unless defined? @rbuffer
+ begin
+ if self.respond_to?(:to_io)
+ IO.select([self.to_io], nil, nil)
+ end
+ @rbuffer << self.sysread(BLOCK_SIZE)
+ rescue EOFError
+ @eof = true
+ end
+ end
+
+ def consume_rbuff(size=nil)
+ if @rbuffer.size == 0
+ @eof = nil
+ nil
+ else
+ size = @rbuffer.size unless size
+ ret = @rbuffer[0, size]
+ @rbuffer[0, size] = ""
+ ret
+ end
+ end
+
+ public
+
+ def read(size=nil)
+ fill_rbuff unless defined? @rbuffer
+ @eof ||= nil
+ until @eof
+ break if size && size <= @rbuffer.size
+ fill_rbuff
+ end
+ consume_rbuff(size)
+ end
+
+ def gets(eol=$/)
+ fill_rbuff unless defined? @rbuffer
+ idx = @rbuffer.index(eol)
+ @eof ||= nil
+ until @eof
+ break if idx
+ fill_rbuff
+ idx = @rbuffer.index(eol)
+ end
+ if eol.is_a?(Regexp)
+ size = idx ? idx+$&.size : nil
+ else
+ size = idx ? idx+eol.size : nil
+ end
+ consume_rbuff(size)
+ end
+
+ def each(eol=$/)
+ while line = self.gets(eol?)
+ yield line
+ end
+ end
+ alias each_line each
+
+ def readlines(eol=$/)
+ ary = []
+ while line = self.gets(eol)
+ ary << line
+ end
+ ary
+ end
+
+ def readline(eol=$/)
+ raise EOFErorr if eof?
+ gets(eol)
+ end
+
+ def getc
+ c = read(1)
+ c ? c.to_i : nil
+ end
+
+ def each_byte
+ while c = getc
+ yield(c)
+ end
+ end
+
+ def readchar
+ raise EOFErorr if eof?
+ getc
+ end
+
+ def ungetc(c)
+ @rbuffer[0,0] = c.chr
+ end
+
+ def eof?
+ @eof ||= nil
+ @eof && @rbuffer.size == 0
+ end
+ alias eof eof?
+
+ #
+ # for writing.
+ #
+ private
+
+ def do_write(s)
+ @wbuffer = "" unless defined? @wbuffer
+ @wbuffer << s
+ @sync ||= false
+ if @sync or @wbuffer.size > BLOCK_SIZE or idx = @wbuffer.rindex($/)
+ remain = idx ? idx + $/.size : @wbuffer.length
+ nwritten = 0
+ while remain > 0
+ nwrote = syswrite(@wbuffer[nwritten,remain])
+ remain -= nwrote
+ nwritten += nwrote
+ end
+ @wbuffer = ""
+ end
+ end
+
+ public
+
+ def write(s)
+ do_write(s)
+ s.length
+ end
+
+ def << (s)
+ do_write(s)
+ self
+ end
+
+ def puts(*args)
+ s = ""
+ args.each{ |arg| s << arg.to_s + $/ }
+ do_write(s)
+ nil
+ end
+
+ def print(*args)
+ s = ""
+ args.each{ |arg| s << arg.to_s }
+ do_write(s)
+ nil
+ end
+
+ def printf(s, *args)
+ do_write(s % args)
+ nil
+ end
+
+ def flush
+ osync = @sync
+ @sync = true
+ do_write ""
+ @sync = osync
+ end
+
+ def close
+ flush
+ sysclose
+ end
+end
diff --git a/ext/openssl/lib/openssl/cipher.rb b/ext/openssl/lib/openssl/cipher.rb
new file mode 100644
index 0000000000..11153104ee
--- /dev/null
+++ b/ext/openssl/lib/openssl/cipher.rb
@@ -0,0 +1,52 @@
+=begin
+= $RCSfile$ -- Ruby-space predefined Cipher subclasses
+
+= Info
+ 'OpenSSL for Ruby 2' project
+ Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ All rights reserved.
+
+= Licence
+ This program is licenced under the same licence as Ruby.
+ (See the file 'LICENCE'.)
+
+= Version
+ $Id$
+=end
+
+##
+# Should we care what if somebody require this file directly?
+#require 'openssl'
+
+module OpenSSL
+ module Cipher
+ %w(AES Cast5 BF DES Idea RC2 RC4 RC5).each{|cipher|
+ eval(<<-EOD)
+ class #{cipher} < Cipher
+ def initialize(*args)
+ args = args.join('-')
+ if args.size == 0
+ super(\"#{cipher}\")
+ else
+ super(\"#{cipher}-#\{args\}\")
+ end
+ end
+ end
+ EOD
+ }
+
+ class Cipher
+ def random_key
+ str = OpenSSL::Random.random_bytes(self.key_len)
+ self.key = str
+ return str
+ end
+
+ def random_iv
+ str = OpenSSL::Random.random_bytes(self.iv_len)
+ self.iv = str
+ return str
+ end
+ end
+ end # Cipher
+end # OpenSSL
diff --git a/ext/openssl/lib/openssl/digest.rb b/ext/openssl/lib/openssl/digest.rb
new file mode 100644
index 0000000000..58622f543e
--- /dev/null
+++ b/ext/openssl/lib/openssl/digest.rb
@@ -0,0 +1,44 @@
+=begin
+= $RCSfile$ -- Ruby-space predefined Digest subclasses
+
+= Info
+ 'OpenSSL for Ruby 2' project
+ Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ All rights reserved.
+
+= Licence
+ This program is licenced under the same licence as Ruby.
+ (See the file 'LICENCE'.)
+
+= Version
+ $Id$
+=end
+
+##
+# Should we care what if somebody require this file directly?
+#require 'openssl'
+
+module OpenSSL
+ module Digest
+
+ %w(DSS DSS1 MD2 MD4 MD5 MDC2 RIPEMD160 SHA SHA1).each{|digest|
+ eval(<<-EOD)
+ class #{digest} < Digest
+ def initialize(data=nil)
+ super(\"#{digest}\", data)
+ end
+
+ def #{digest}::digest(data)
+ Digest::digest(\"#{digest}\", data)
+ end
+
+ def #{digest}::hexdigest(data)
+ Digest::hexdigest(\"#{digest}\", data)
+ end
+ end
+ EOD
+ }
+
+ end # Digest
+end # OpenSSL
+
diff --git a/ext/openssl/lib/openssl/ssl.rb b/ext/openssl/lib/openssl/ssl.rb
new file mode 100644
index 0000000000..e434941913
--- /dev/null
+++ b/ext/openssl/lib/openssl/ssl.rb
@@ -0,0 +1,38 @@
+=begin
+= $RCSfile$ -- Ruby-space definitions that completes C-space funcs for SSL
+
+= 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'.)
+
+= Version
+ $Id$
+=end
+
+require 'openssl/buffering'
+
+module OpenSSL
+ module SSL
+ class SSLSocket
+ include Buffering
+
+ def addr
+ @io.addr
+ end
+
+ def peeraddr
+ @io.peeraddr
+ end
+
+ def closed?
+ @io.closed?
+ end
+ end
+ end
+end
+
diff --git a/ext/openssl/lib/openssl/x509.rb b/ext/openssl/lib/openssl/x509.rb
new file mode 100644
index 0000000000..f7df597acb
--- /dev/null
+++ b/ext/openssl/lib/openssl/x509.rb
@@ -0,0 +1,132 @@
+=begin
+= $RCSfile$ -- Ruby-space definitions that completes C-space funcs for X509 and subclasses
+
+= Info
+ 'OpenSSL for Ruby 2' project
+ Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ All rights reserved.
+
+= Licence
+ This program is licenced under the same licence as Ruby.
+ (See the file 'LICENCE'.)
+
+= Version
+ $Id$
+=end
+
+##
+# Should we care what if somebody require this file directly?
+#require 'openssl'
+
+module OpenSSL
+ module X509
+
+ class ExtensionFactory
+ def create_extension(*arg)
+ if arg.size == 1 then arg = arg[0] end
+ type = arg.class
+ while type
+ method = "create_ext_from_#{type.name.downcase}".intern
+ return send(method, arg) if respond_to? method
+ type = type.superclass
+ end
+ raise TypeError, "Don't how to create ext from #{arg.class}"
+ ###send("create_ext_from_#{arg.class.name.downcase}", arg)
+ end
+
+ #
+ # create_ext_from_array is built-in
+ #
+ def create_ext_from_string(str) # "oid = critical, value"
+ unless str =~ /\s*=\s*/
+ raise ArgumentError, "string in format \"oid = value\" expected"
+ end
+ ary = []
+ ary << $`.sub(/^\s*/,"") # delete whitespaces from the beginning
+ rest = $'.sub(/\s*$/,"") # delete them from the end
+ if rest =~ /^critical,\s*/ # handle 'critical' option
+ ary << $'
+ ary << true
+ else
+ ary << rest
+ end
+ create_ext_from_array(ary)
+ end
+
+ #
+ # Create an extention from Hash
+ # {"oid"=>sn|ln, "value"=>value, "critical"=>true|false}
+ #
+ def create_ext_from_hash(hash)
+ unless (hash.has_key? "oid" and hash.has_key? "value")
+ raise ArgumentError,
+ "hash in format {\"oid\"=>..., \"value\"=>...} expected"
+ end
+ ary = []
+ ary << hash["oid"]
+ ary << hash["value"]
+ ary << hash["critical"] if hash.has_key? "critical"
+ create_ext_from_array(ary)
+ end
+ end # ExtensionFactory
+
+ class Extension
+ def to_s # "oid = critical, value"
+ str = self.oid
+ str << " = "
+ str << "critical, " if self.critical?
+ str << self.value.gsub(/\n/, ", ")
+ end
+
+ def to_h # {"oid"=>sn|ln, "value"=>value, "critical"=>true|false}
+ {"oid"=>self.oid,"value"=>self.value,"critical"=>self.critical?}
+ end
+
+ def to_a
+ [ self.oid, self.value, self.critical? ]
+ end
+ end # Extension
+
+ class Attribute
+ def Attribute::new(arg)
+ type = arg.class
+ while type
+ method = "new_from_#{type.name.downcase}".intern
+ return Attribute::send(method, arg) if Attribute::respond_to? method
+ type = type.superclass
+ end
+ raise "Don't how to make new #{self} from #{arg.class}"
+ ###Attribute::send("new_from_#{arg.class.name.downcase}", arg)
+ end
+
+ #
+ # Attribute::new_from_array(ary) is built-in method
+ #
+ def Attribute::new_from_string(str) # "oid = value"
+ unless str =~ /\s*=\s*/
+ raise ArgumentError, "string in format \"oid = value\" expected"
+ end
+ ary = []
+ ary << $`.sub(/^\s*/,"") # delete whitespaces from the beginning
+ ary << $'.sub(/\s*$/,"") # delete them from the end
+ Attribute::new_from_array(ary)
+ end
+
+ #
+ # Create an attribute from Hash
+ # {"oid"=>sn|ln, "value"=>value, "critical"=>true|false}
+ #
+ def Attribute::new_from_hash(hash) # {"oid"=>"...", "value"=>"..."}
+ unless (hash.has_key? "oid" and hash.has_key? "value")
+ raise ArgumentError,
+ "hash in format {\"oid\"=>..., \"value\"=>...} expected"
+ end
+ ary = []
+ ary << hash["oid"]
+ ary << hash["value"]
+ Attribute::new_from_array(ary)
+ end
+ end # Attribute
+
+ end # X509
+end # OpenSSL
diff --git a/ext/openssl/openssl_missing.c b/ext/openssl/openssl_missing.c
new file mode 100644
index 0000000000..e201e750c9
--- /dev/null
+++ b/ext/openssl/openssl_missing.c
@@ -0,0 +1,279 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+
+#if !defined(OPENSSL_NO_HMAC)
+#include <string.h> /* memcpy() */
+#include <openssl/hmac.h>
+
+#if !defined(HAVE_HMAC_CTX_COPY)
+int
+HMAC_CTX_copy(HMAC_CTX *out, HMAC_CTX *in)
+{
+ if (!out || !in) {
+ /* HMACerr(HMAC_CTX_COPY,HMAC_R_INPUT_NOT_INITIALIZED); */
+ return 0;
+ }
+ memcpy(out, in, sizeof(HMAC_CTX));
+
+ if (!EVP_MD_CTX_copy(&out->md_ctx, &in->md_ctx)) {
+ return 0;
+ }
+ if (!EVP_MD_CTX_copy(&out->i_ctx, &in->i_ctx)) {
+ return 0;
+ }
+ if (!EVP_MD_CTX_copy(&out->o_ctx, &in->o_ctx)) {
+ return 0;
+ }
+ return 1;
+}
+#endif /* HAVE_HMAC_CTX_COPY */
+#endif /* NO_HMAC */
+
+#if !defined(HAVE_X509_STORE_SET_EX_DATA)
+#include <openssl/x509_vfy.h>
+
+int X509_STORE_set_ex_data(X509_STORE *str, int idx, void *data)
+{
+ return CRYPTO_set_ex_data(&str->ex_data,idx,data);
+}
+
+void *X509_STORE_get_ex_data(X509_STORE *str, int idx)
+{
+ return CRYPTO_get_ex_data(&str->ex_data,idx);
+}
+#endif
+
+#if !defined(HAVE_EVP_MD_CTX_CREATE)
+EVP_MD_CTX *
+EVP_MD_CTX_create(void)
+{
+ EVP_MD_CTX *ctx = OPENSSL_malloc(sizeof *ctx);
+
+ memset(ctx, '\0', sizeof *ctx);
+
+ return ctx;
+}
+#endif
+
+#if !defined(HAVE_EVP_MD_CTX_CLEANUP)
+int
+EVP_MD_CTX_cleanup(EVP_MD_CTX *ctx)
+{
+ /* FIXME!!! */
+ memset(ctx, '\0', sizeof *ctx);
+
+ return 1;
+}
+#endif
+
+#if !defined(HAVE_EVP_MD_CTX_DESTROY)
+void
+EVP_MD_CTX_destroy(EVP_MD_CTX *ctx)
+{
+ EVP_MD_CTX_cleanup(ctx);
+ OPENSSL_free(ctx);
+}
+#endif
+
+#if !defined(HAVE_EVP_MD_CTX_INIT)
+void
+EVP_MD_CTX_init(EVP_MD_CTX *ctx)
+{
+ memset(ctx,'\0',sizeof *ctx);
+}
+#endif
+
+#if !defined(HAVE_HMAC_CTX_INIT)
+void
+HMAC_CTX_init(HMAC_CTX *ctx)
+{
+ EVP_MD_CTX_init(&ctx->i_ctx);
+ EVP_MD_CTX_init(&ctx->o_ctx);
+ EVP_MD_CTX_init(&ctx->md_ctx);
+}
+#endif
+
+#if !defined(HAVE_HMAC_CTX_CLEANUP)
+void
+HMAC_CTX_cleanup(HMAC_CTX *ctx)
+{
+ EVP_MD_CTX_cleanup(&ctx->i_ctx);
+ EVP_MD_CTX_cleanup(&ctx->o_ctx);
+ EVP_MD_CTX_cleanup(&ctx->md_ctx);
+ memset(ctx,0,sizeof *ctx);
+}
+#endif
+
+#if !defined(HAVE_X509_CRL_SET_VERSION)
+int
+X509_CRL_set_version(X509_CRL *x, long version)
+{
+ if (x == NULL) return(0);
+ if (x->crl->version == NULL)
+ {
+ if ((x->crl->version=M_ASN1_INTEGER_new()) == NULL)
+ return(0);
+ }
+ return(ASN1_INTEGER_set(x->crl->version,version));
+}
+#endif
+
+#if !defined(HAVE_X509_CRL_SET_ISSUER_NAME)
+int
+X509_CRL_set_issuer_name(X509_CRL *x, X509_NAME *name)
+{
+ if ((x == NULL) || (x->crl == NULL)) return(0);
+ return(X509_NAME_set(&x->crl->issuer,name));
+}
+#endif
+
+#if !defined(HAVE_X509_CRL_SORT)
+int
+X509_CRL_sort(X509_CRL *c)
+{
+ int i;
+ X509_REVOKED *r;
+ /* sort the data so it will be written in serial
+ * number order */
+ sk_X509_REVOKED_sort(c->crl->revoked);
+ for (i=0; i<sk_X509_REVOKED_num(c->crl->revoked); i++){
+ r=sk_X509_REVOKED_value(c->crl->revoked,i);
+ r->sequence=i;
+ }
+ return 1;
+}
+#endif
+
+#if !defined(HAVE_X509_CRL_ADD0_REVOKED)
+static int
+OSSL_X509_REVOKED_cmp(const X509_REVOKED * const *a, const X509_REVOKED * const *b)
+{
+ return(ASN1_STRING_cmp(
+ (ASN1_STRING *)(*a)->serialNumber,
+ (ASN1_STRING *)(*b)->serialNumber));
+}
+
+int
+X509_CRL_add0_revoked(X509_CRL *crl, X509_REVOKED *rev)
+{
+ X509_CRL_INFO *inf;
+ inf = crl->crl;
+ if(!inf->revoked)
+ inf->revoked = sk_X509_REVOKED_new(OSSL_X509_REVOKED_cmp);
+ if(!inf->revoked || !sk_X509_REVOKED_push(inf->revoked, rev)) {
+ /* ASN1err(ASN1_F_X509_CRL_ADD0_REVOKED, ERR_R_MALLOC_FAILURE); */
+ return 0;
+ }
+ return 1;
+}
+#endif
+
+#if !defined(HAVE_BN_MOD_SQR)
+int
+BN_mod_sqr(BIGNUM *r, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx)
+{
+ if (!BN_sqr(r, (BIGNUM*)a, ctx)) return 0;
+ /* r->neg == 0, thus we don't need BN_nnmod */
+ return BN_mod(r, r, m, ctx);
+}
+#endif
+
+#if !defined(HAVE_BN_MOD_ADD) || !defined(HAVE_BN_MOD_SUB)
+int BN_nnmod(BIGNUM *r, const BIGNUM *m, const BIGNUM *d, BN_CTX *ctx)
+{
+ /* like BN_mod, but returns non-negative remainder
+ * (i.e., 0 <= r < |d| always holds) */
+ if (!(BN_mod(r,m,d,ctx))) return 0;
+ if (!r->neg) return 1;
+ /* now -|d| < r < 0, so we have to set r := r + |d| */
+ return (d->neg ? BN_sub : BN_add)(r, r, d);
+}
+#endif
+
+#if !defined(HAVE_BN_MOD_ADD)
+int
+BN_mod_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m, BN_CTX *ctx)
+{
+ if (!BN_add(r, a, b)) return 0;
+ return BN_nnmod(r, r, m, ctx);
+}
+#endif
+
+#if !defined(HAVE_BN_MOD_SUB)
+int
+BN_mod_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m, BN_CTX *ctx)
+{
+ if (!BN_sub(r, a, b)) return 0;
+ return BN_nnmod(r, r, m, ctx);
+}
+#endif
+
+#if !defined(HAVE_CONF_GET1_DEFAULT_CONFIG_FILE)
+#define OPENSSL_CONF "openssl.cnf"
+char *
+CONF_get1_default_config_file(void)
+{
+ char *file;
+ int len;
+
+ file = getenv("OPENSSL_CONF");
+ if (file) return BUF_strdup(file);
+ len = strlen(X509_get_default_cert_area());
+#ifndef OPENSSL_SYS_VMS
+ len++;
+#endif
+ len += strlen(OPENSSL_CONF);
+ file = OPENSSL_malloc(len + 1);
+ if (!file) return NULL;
+ strcpy(file,X509_get_default_cert_area());
+#ifndef OPENSSL_SYS_VMS
+ strcat(file,"/");
+#endif
+ strcat(file,OPENSSL_CONF);
+
+ return file;
+}
+#endif
+
+#if !defined(HAVE_PEM_DEF_CALLBACK)
+#define OSSL_PASS_MIN_LENGTH 4
+int
+PEM_def_callback(char *buf, int num, int w, void *key)
+{
+ int i,j;
+ const char *prompt;
+ if(key){
+ i = strlen(key);
+ i = (i > num) ? num : i;
+ memcpy(buf, key, i);
+ return(i);
+ }
+
+ prompt = EVP_get_pw_prompt();
+ if (prompt == NULL) prompt= "Enter PEM pass phrase:";
+ for(;;){
+ i = EVP_read_pw_string(buf, num, prompt, w);
+ if(i != 0){
+ memset(buf,0,(unsigned int)num);
+ return(-1);
+ }
+ j = strlen(buf);
+ if(j < OSSL_PASS_MIN_LENGTH){
+ fprintf(stderr,
+ "phrase is too short, needs to be at least %d chars\n",
+ OSSL_PASS_MIN_LENGTH);
+ }
+ else break;
+ }
+ return(j);
+}
+#endif
+
diff --git a/ext/openssl/openssl_missing.h b/ext/openssl/openssl_missing.h
new file mode 100644
index 0000000000..33b15f6aa6
--- /dev/null
+++ b/ext/openssl/openssl_missing.h
@@ -0,0 +1,105 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#if !defined(_OSSL_OPENSSL_MISSING_H_)
+#define _OSSL_OPENSSL_MISSING_H_
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*
+ * These functions are not included in headers of OPENSSL <= 0.9.6b
+ */
+
+#if !defined(PEM_read_bio_DSAPublicKey)
+# define PEM_read_bio_DSAPublicKey(bp,x,cb,u) (DSA *)PEM_ASN1_read_bio( \
+ (char *(*)())d2i_DSAPublicKey,PEM_STRING_DSA_PUBLIC,bp,(char **)x,cb,u)
+#endif
+
+#if !defined(PEM_write_bio_DSAPublicKey)
+# define PEM_write_bio_DSAPublicKey(bp,x) \
+ PEM_ASN1_write_bio((int (*)())i2d_DSAPublicKey,\
+ PEM_STRING_DSA_PUBLIC,\
+ bp,(char *)x, NULL, NULL, 0, NULL, NULL)
+#endif
+
+#if !defined(DSAPrivateKey_dup)
+# define DSAPrivateKey_dup(dsa) (DSA *)ASN1_dup((int (*)())i2d_DSAPrivateKey, \
+ (char *(*)())d2i_DSAPrivateKey,(char *)dsa)
+#endif
+
+#if !defined(DSAPublicKey_dup)
+# define DSAPublicKey_dup(dsa) (DSA *)ASN1_dup((int (*)())i2d_DSAPublicKey, \
+ (char *(*)())d2i_DSAPublicKey,(char *)dsa)
+#endif
+
+#if !defined(X509_REVOKED_dup)
+# define X509_REVOKED_dup(rev) (X509_REVOKED *)ASN1_dup((int (*)())i2d_X509_REVOKED, \
+ (char *(*)())d2i_X509_REVOKED, (char *)rev)
+#endif
+
+#if !defined(PKCS7_SIGNER_INFO_dup)
+# define PKCS7_SIGNER_INFO_dup(si) (PKCS7_SIGNER_INFO *)ASN1_dup((int (*)())i2d_PKCS7_SIGNER_INFO, \
+ (char *(*)())d2i_PKCS7_SIGNER_INFO, (char *)si)
+#endif
+
+#if !defined(PKCS7_RECIP_INFO_dup)
+# define PKCS7_RECIP_INFO_dup(ri) (PKCS7_RECIP_INFO *)ASN1_dup((int (*)())i2d_PKCS7_RECIP_INFO, \
+ (char *(*)())d2i_PKCS7_RECIP_INFO, (char *)ri)
+#endif
+
+int HMAC_CTX_copy(HMAC_CTX *out, HMAC_CTX *in);
+void *X509_STORE_get_ex_data(X509_STORE *str, int idx);
+int X509_STORE_set_ex_data(X509_STORE *str, int idx, void *data);
+EVP_MD_CTX *EVP_MD_CTX_create(void);
+int EVP_MD_CTX_cleanup(EVP_MD_CTX *ctx);
+void EVP_MD_CTX_destroy(EVP_MD_CTX *ctx);
+
+#if !defined(EVP_CIPHER_name)
+# define EVP_CIPHER_name(e) OBJ_nid2sn(EVP_CIPHER_nid(e))
+#endif
+
+#if !defined(EVP_MD_name)
+# define EVP_MD_name(e) OBJ_nid2sn(EVP_MD_type(e))
+#endif
+
+void EVP_MD_CTX_init(EVP_MD_CTX *ctx);
+void HMAC_CTX_init(HMAC_CTX *ctx);
+void HMAC_CTX_cleanup(HMAC_CTX *ctx);
+
+#if !defined(PKCS7_is_detached)
+# define PKCS7_is_detached(p7) (PKCS7_type_is_signed(p7) && PKCS7_get_detached(p7))
+#endif
+
+#if !defined(PKCS7_type_is_encrypted)
+# define PKCS7_type_is_encrypted(a) (OBJ_obj2nid((a)->type) == NID_pkcs7_encrypted)
+#endif
+
+int X509_CRL_set_version(X509_CRL *x, long version);
+int X509_CRL_set_issuer_name(X509_CRL *x, X509_NAME *name);
+int X509_CRL_sort(X509_CRL *c);
+int X509_CRL_add0_revoked(X509_CRL *crl, X509_REVOKED *rev);
+int BN_mod_sqr(BIGNUM *r, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx);
+int BN_mod_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m, BN_CTX *ctx);
+int BN_mod_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m, BN_CTX *ctx);
+char *CONF_get1_default_config_file(void);
+
+#if !defined(HAVE_PEM_DEF_CALLBACK)
+int PEM_def_callback(char *buf, int num, int w, void *key);
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+
+#endif /* _OSSL_OPENSSL_MISSING_H_ */
+
diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c
new file mode 100644
index 0000000000..9e476cb5af
--- /dev/null
+++ b/ext/openssl/ossl.c
@@ -0,0 +1,568 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#include "ossl.h"
+#include <stdarg.h> /* for ossl_raise */
+
+#if defined(HAVE_SYS_TIME_H)
+# include <sys/time.h>
+#elif !defined(NT) && !defined(_WIN32)
+struct timeval {
+ long tv_sec; /* seconds */
+ long tv_usec; /* and microseconds */
+};
+#endif
+
+/*
+ * DATE conversion
+ */
+VALUE
+asn1time_to_time(ASN1_TIME *time)
+{
+ struct tm tm;
+ VALUE argv[6];
+
+ if (!time) {
+ ossl_raise(rb_eTypeError, "ASN1_TIME is NULL!");
+ }
+ memset(&tm, 0, sizeof(struct tm));
+
+ switch (time->type) {
+ case V_ASN1_UTCTIME:
+ if (sscanf(time->data, "%2d%2d%2d%2d%2d%2dZ", &tm.tm_year, &tm.tm_mon,
+ &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
+ ossl_raise(rb_eTypeError, "bad UTCTIME format");
+ }
+ if (tm.tm_year < 69) {
+ tm.tm_year += 2000;
+ } else {
+ tm.tm_year += 1900;
+ }
+ tm.tm_mon -= 1;
+ break;
+ case V_ASN1_GENERALIZEDTIME:
+ if (sscanf(time->data, "%4d%2d%2d%2d%2d%2dZ", &tm.tm_year, &tm.tm_mon,
+ &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
+ ossl_raise(rb_eTypeError, "bad GENERALIZEDTIME format" );
+ }
+ tm.tm_mon -= 1;
+ break;
+ default:
+ rb_warning("unknown time format");
+ return Qnil;
+ }
+ argv[0] = INT2NUM(tm.tm_year);
+ argv[1] = INT2NUM(tm.tm_mon+1);
+ argv[2] = INT2NUM(tm.tm_mday);
+ argv[3] = INT2NUM(tm.tm_hour);
+ argv[4] = INT2NUM(tm.tm_min);
+ argv[5] = INT2NUM(tm.tm_sec);
+
+ return rb_funcall2(rb_cTime, rb_intern("utc"), 6, argv);
+}
+
+/*
+ * This function is not exported in Ruby's *.h
+ */
+extern struct timeval rb_time_timeval(VALUE);
+
+time_t
+time_to_time_t(VALUE time)
+{
+ struct timeval t = rb_time_timeval(time);
+ return t.tv_sec;
+}
+
+/*
+ * ASN1_INTEGER conversions
+ * TODO: Make a decision what's the right way to do this.
+ */
+#define DO_IT_VIA_RUBY 0
+VALUE
+asn1integer_to_num(ASN1_INTEGER *ai)
+{
+ BIGNUM *bn;
+#if DO_IT_VIA_RUBY
+ char *txt;
+#endif
+ VALUE num;
+
+ if (!ai) {
+ ossl_raise(rb_eTypeError, "ASN1_INTEGER is NULL!");
+ }
+ if (!(bn = ASN1_INTEGER_to_BN(ai, NULL))) {
+ ossl_raise(eOSSLError, NULL);
+ }
+#if DO_IT_VIA_RUBY
+ if (!(txt = BN_bn2dec(bn))) {
+ BN_free(bn);
+ ossl_raise(eOSSLError, NULL);
+ }
+ num = rb_cstr_to_inum(txt, 10, Qtrue);
+ OPENSSL_free(txt);
+#else
+ num = ossl_bn_new(bn);
+#endif
+ BN_free(bn);
+
+ return num;
+}
+
+#if DO_IT_VIA_RUBY
+ASN1_INTEGER *num_to_asn1integer(VALUE obj, ASN1_INTEGER *ai)
+{
+ BIGNUM *bn = NULL;
+
+ if (RTEST(rb_obj_is_kind_of(obj, cBN))) {
+ bn = GetBNPtr(obj);
+ } else {
+ obj = rb_String(obj);
+ if (!BN_dec2bn(&bn, StringValuePtr(obj))) {
+ ossl_raise(eOSSLError, NULL);
+ }
+ }
+ if (!(ai = BN_to_ASN1_INTEGER(bn, ai))) {
+ BN_free(bn);
+ ossl_raise(eOSSLError, NULL);
+ }
+ BN_free(bn);
+ return ai;
+}
+#else
+ASN1_INTEGER *num_to_asn1integer(VALUE obj, ASN1_INTEGER *ai)
+{
+ BIGNUM *bn = GetBNPtr(obj);
+
+ if (!(ai = BN_to_ASN1_INTEGER(bn, ai))) {
+ ossl_raise(eOSSLError, NULL);
+ }
+ return ai;
+}
+#endif
+
+/*
+ * String to HEXString conversion
+ */
+int
+string2hex(char *buf, int buf_len, char **hexbuf, int *hexbuf_len)
+{
+ static const char hex[]="0123456789abcdef";
+ int i, len = 2 * buf_len;
+
+ if (buf_len < 0 || len < buf_len) { /* PARANOIA? */
+ return -1;
+ }
+ if (!hexbuf) { /* if no buf, return calculated len */
+ if (hexbuf_len) {
+ *hexbuf_len = len;
+ }
+ return len;
+ }
+ if (!(*hexbuf = OPENSSL_malloc(len + 1))) {
+ return -1;
+ }
+ for (i = 0; i < buf_len; i++) {
+ (*hexbuf)[2 * i] = hex[((unsigned char)buf[i]) >> 4];
+ (*hexbuf)[2 * i + 1] = hex[buf[i] & 0x0f];
+ }
+ (*hexbuf)[2 * i] = '\0';
+
+ if (hexbuf_len) {
+ *hexbuf_len = len;
+ }
+ return len;
+}
+
+/*
+ * Data Conversion
+ */
+BIO *
+ossl_obj2bio(VALUE obj)
+{
+ BIO *bio;
+
+ if (TYPE(obj) == T_FILE) {
+ OpenFile *fptr;
+ GetOpenFile(obj, fptr);
+ rb_io_check_readable(fptr);
+ bio = BIO_new_fp(fptr->f, BIO_NOCLOSE);
+ }
+ else {
+ StringValue(obj);
+ bio = BIO_new_mem_buf(RSTRING(obj)->ptr, RSTRING(obj)->len);
+ }
+ if (!bio) ossl_raise(eOSSLError, NULL);
+
+ return bio;
+}
+
+BIO *
+ossl_protect_obj2bio(VALUE obj, int *status)
+{
+ BIO *ret = NULL;
+ ret = (BIO*)rb_protect((VALUE(*)_((VALUE)))ossl_obj2bio, obj, status);
+ return ret;
+}
+
+VALUE
+ossl_membio2str(BIO *bio)
+{
+ VALUE ret;
+ BUF_MEM *buf;
+
+ BIO_get_mem_ptr(bio, &buf);
+ ret = rb_str_new(buf->data, buf->length);
+
+ return ret;
+}
+
+VALUE
+ossl_protect_membio2str(BIO *bio, int *status)
+{
+ return rb_protect((VALUE(*)_((VALUE)))ossl_membio2str, (VALUE)bio, status);
+}
+
+STACK_OF(X509) *
+ossl_x509_ary2sk(VALUE ary)
+{
+ STACK_OF(X509) *sk;
+ VALUE val;
+ X509 *x509;
+ int i;
+
+ Check_Type(ary, T_ARRAY);
+ sk = sk_X509_new_null();
+ if (!sk) ossl_raise(eOSSLError, NULL);
+
+ for (i = 0; i < RARRAY(ary)->len; i++) {
+ val = rb_ary_entry(ary, i);
+ if (!rb_obj_is_kind_of(val, cX509Cert)) {
+ sk_X509_pop_free(sk, X509_free);
+ ossl_raise(eOSSLError, "object except X509 cert is in array");
+ }
+ x509 = DupX509CertPtr(val); /* NEED TO DUP */
+ sk_X509_push(sk, x509);
+ }
+ return sk;
+}
+
+STACK_OF(X509) *
+ossl_protect_x509_ary2sk(VALUE ary, int *status)
+{
+ return (STACK_OF(X509)*)rb_protect((VALUE(*)_((VALUE)))ossl_x509_ary2sk, ary, status);
+}
+
+#if 0
+#define OSSL_SK2ARY(name, type) \
+VALUE \
+ossl_##name##_sk2ary(STACK *sk) \
+{ \
+ type *t; \
+ int i, num; \
+ VALUE ary; \
+ \
+ if (!sk) { \
+ OSSL_Debug("empty sk!"); \
+ return rb_ary_new(); \
+ } \
+ num = sk_num(sk); \
+ if (num < 0) { \
+ OSSL_Debug("items in sk < -1???"); \
+ return rb_ary_new(); \
+ } \
+ ary = rb_ary_new2(num); \
+ \
+ for (i=0; i<num; i++) { \
+ t = (type *)sk_value(sk, i); \
+ rb_ary_push(ary, ossl_##name##_new(t)); \
+ } \
+ return ary; \
+}
+OSSL_SK2ARY(x509, X509)
+OSSL_SK2ARY(x509crl, X509_CRL)
+#endif
+
+/*
+ * our default PEM callback
+ */
+static VALUE
+ossl_pem_passwd_cb0(VALUE flag)
+{
+ VALUE pass;
+
+ pass = rb_yield(flag);
+ SafeStringValue(pass);
+
+ return pass;
+}
+
+int
+ossl_pem_passwd_cb(char *buf, int max_len, int flag, void *pwd)
+{
+ int len, status = 0;
+ VALUE rflag, pass;
+
+ if (pwd || !rb_block_given_p())
+ return PEM_def_callback(buf, max_len, flag, pwd);
+
+ while (1) {
+ /*
+ * when the flag is nonzero, this passphrase
+ * will be used to perform encryption; otherwise it will
+ * be used to perform decryption.
+ */
+ rflag = flag ? Qtrue : Qfalse;
+ pass = rb_protect(ossl_pem_passwd_cb0, rflag, &status);
+ if (status) return -1; /* exception was raised. */
+ len = RSTRING(pass)->len;
+ if (len < 4) { /* 4 is OpenSSL hardcoded limit */
+ rb_warning("password must be longer than 4 bytes");
+ continue;
+ }
+ if (len > max_len) {
+ rb_warning("password must be shorter then %d bytes", max_len-1);
+ continue;
+ }
+ memcpy(buf, RSTRING(pass)->ptr, len);
+ break;
+ }
+ return len;
+}
+
+/*
+ * Verify callback
+ */
+int ossl_verify_cb_idx;
+
+VALUE
+ossl_call_verify_cb_proc(struct ossl_verify_cb_args *args)
+{
+ return rb_funcall(args->proc, rb_intern("call"), 2,
+ args->preverify_ok, args->store_ctx);
+}
+
+int
+ossl_verify_cb(int ok, X509_STORE_CTX *ctx)
+{
+ VALUE proc, rctx, ret;
+ struct ossl_verify_cb_args args;
+ int state = 0;
+
+ proc = (VALUE)X509_STORE_CTX_get_ex_data(ctx, ossl_verify_cb_idx);
+ if ((void*)proc == 0)
+ proc = (VALUE)X509_STORE_get_ex_data(ctx->ctx, ossl_verify_cb_idx);
+ if ((void*)proc == 0)
+ return ok;
+ if (!NIL_P(proc)) {
+ rctx = rb_protect((VALUE(*)(VALUE))ossl_x509stctx_new,
+ (VALUE)ctx, &state);
+ ret = Qfalse;
+ if (!state) {
+ args.proc = proc;
+ args.preverify_ok = ok ? Qtrue : Qfalse;
+ args.store_ctx = rctx;
+ ret = rb_ensure(ossl_call_verify_cb_proc, (VALUE)&args,
+ ossl_x509stctx_clear_ptr, rctx);
+ }
+ if (ret == Qtrue) {
+ X509_STORE_CTX_set_error(ctx, X509_V_OK);
+ ok = 1;
+ }
+ else{
+ if (X509_STORE_CTX_get_error(ctx) == X509_V_OK) {
+ X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REJECTED);
+ }
+ ok = 0;
+ }
+ }
+
+ return ok;
+}
+
+/*
+ * main module
+ */
+VALUE mOSSL;
+
+/*
+ * OpenSSLError < StandardError
+ */
+VALUE eOSSLError;
+
+/*
+ * Errors
+ */
+void
+ossl_raise(VALUE exc, const char *fmt, ...)
+{
+ va_list args;
+ char buf[BUFSIZ];
+ const char *msg;
+ long e = ERR_get_error();
+ int len = 0;
+
+ if (fmt) {
+ va_start(args, fmt);
+ len = vsnprintf(buf, BUFSIZ, fmt, args);
+ va_end(args);
+ len += snprintf(buf+len, BUFSIZ-len, ": ");
+ }
+ if (e) {
+ if (dOSSL == Qtrue) /* FULL INFO */
+ msg = ERR_error_string(e, NULL);
+ else
+ msg = ERR_reason_error_string(e);
+ ERR_clear_error();
+ len += snprintf(buf+len, BUFSIZ-len, "%s", msg);
+ }
+
+ rb_exc_raise(rb_exc_new(exc, buf, len));
+}
+
+/*
+ * Debug
+ */
+VALUE dOSSL;
+
+#if !defined(HAVE_VA_ARGS_MACRO)
+void
+ossl_debug(const char *fmt, ...)
+{
+ va_list args;
+
+ if (dOSSL == Qtrue) {
+ fprintf(stderr, "OSSL_DEBUG: ");
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, " [CONTEXT N/A]\n");
+ }
+}
+#endif
+
+static VALUE
+ossl_debug_get(VALUE self)
+{
+ return dOSSL;
+}
+
+static VALUE
+ossl_debug_set(VALUE self, VALUE val)
+{
+ VALUE old = dOSSL;
+ dOSSL = val;
+
+ if (old != dOSSL) {
+ if (dOSSL == Qtrue) {
+ CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
+ fprintf(stderr, "OSSL_DEBUG: IS NOW ON!\n");
+ } else if (old == Qtrue) {
+ CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_OFF);
+ fprintf(stderr, "OSSL_DEBUG: IS NOW OFF!\n");
+ }
+ }
+ return val;
+}
+
+/*
+ * OSSL library init
+ */
+void
+Init_openssl()
+{
+ /*
+ * Init timezone info
+ */
+#if 0
+ tzset();
+#endif
+
+ /*
+ * Init all digests, ciphers
+ */
+ /* CRYPTO_malloc_init(); */
+ /* ENGINE_load_builtin_engines(); */
+ OpenSSL_add_all_algorithms();
+ ERR_load_crypto_strings();
+ SSL_load_error_strings();
+
+ /*
+ * FIXME:
+ * On unload do:
+ */
+#if 0
+ CONF_modules_unload(1);
+ destroy_ui_method();
+ EVP_cleanup();
+ ENGINE_cleanup();
+ CRYPTO_cleanup_all_ex_data();
+ ERR_remove_state(0);
+ ERR_free_strings();
+#endif
+
+ /*
+ * Init main module
+ */
+ mOSSL = rb_define_module("OpenSSL");
+
+ /*
+ * Constants
+ */
+ rb_define_const(mOSSL, "VERSION", rb_str_new2(OSSL_VERSION));
+ rb_define_const(mOSSL, "OPENSSL_VERSION", rb_str_new2(OPENSSL_VERSION_TEXT));
+ rb_define_const(mOSSL, "OPENSSL_VERSION_NUMBER", INT2NUM(OPENSSL_VERSION_NUMBER));
+
+ /*
+ * Generic error,
+ * common for all classes under OpenSSL module
+ */
+ eOSSLError = rb_define_class_under(mOSSL,"OpenSSLError",rb_eStandardError);
+
+ /*
+ * Verify callback Proc index for ext-data
+ */
+ ossl_verify_cb_idx =
+ X509_STORE_CTX_get_ex_new_index(0, "ossl_verify_cb_idx", 0, 0, 0);
+
+ /*
+ * Init debug core
+ */
+ dOSSL = Qfalse;
+ rb_define_module_function(mOSSL, "debug", ossl_debug_get, 0);
+ rb_define_module_function(mOSSL, "debug=", ossl_debug_set, 1);
+
+ /*
+ * Init components
+ */
+ Init_ossl_bn();
+ Init_ossl_cipher();
+ Init_ossl_config();
+ Init_ossl_digest();
+ Init_ossl_hmac();
+ Init_ossl_ns_spki();
+ Init_ossl_pkcs7();
+ Init_ossl_pkey();
+ Init_ossl_rand();
+ Init_ossl_ssl();
+ Init_ossl_x509();
+ Init_ossl_ocsp();
+}
+
+#if defined(OSSL_DEBUG)
+/*
+ * Check if all symbols are OK with 'make LDSHARED=gcc all'
+ */
+int
+main(int argc, char *argv[], char *env[])
+{
+ return 0;
+}
+#endif /* OSSL_DEBUG */
+
diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h
new file mode 100644
index 0000000000..72b183986e
--- /dev/null
+++ b/ext/openssl/ossl.h
@@ -0,0 +1,197 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#if !defined(_OSSL_H_)
+#define _OSSL_H_
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*
+ * Check the Ruby version and OpenSSL
+ * The only supported are:
+ * Ruby >= 1.7.2
+ * OpenSSL >= 0.9.7
+ */
+#include <version.h>
+#include <openssl/opensslv.h>
+
+#if defined(NT) || defined(_WIN32)
+# define OpenFile WINAPI_OpenFile
+#endif
+#include <errno.h>
+#include <openssl/err.h>
+#include <openssl/asn1_mac.h>
+#include <openssl/x509v3.h>
+#include <openssl/ssl.h>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
+#undef X509_NAME
+#undef PKCS7_SIGNER_INFO
+#if defined(HAVE_OPENSSL_OCSP_H)
+# define OSSL_OCSP_ENABLED
+# include <openssl/ocsp.h>
+#endif
+#if defined(NT) || defined(_WIN32)
+# undef OpenFile
+#endif
+
+/*
+ * OpenSSL has defined RFILE and Ruby has defined RFILE - so undef it!
+ */
+#if defined(RFILE) /*&& !defined(OSSL_DEBUG)*/
+# undef RFILE
+#endif
+#include <ruby.h>
+#include <rubyio.h>
+
+/*
+ * Common Module
+ */
+extern VALUE mOSSL;
+
+/*
+ * Common Error Class
+ */
+extern VALUE eOSSLError;
+
+/*
+ * CheckTypes
+ */
+#define OSSL_Check_Kind(obj, klass) do {\
+ if (!rb_obj_is_kind_of(obj, klass)) {\
+ ossl_raise(rb_eTypeError, "wrong argument (%s)! (Expected kind of %s)",\
+ rb_obj_classname(obj), rb_class2name(klass));\
+ }\
+} while (0)
+
+#define OSSL_Check_Instance(obj, klass) do {\
+ if (!rb_obj_is_instance_of(obj, klass)) {\
+ ossl_raise(rb_eTypeError, "wrong argument (%s)! (Expected instance of %s)",\
+ rb_obj_classname(obj), rb_class2name(klass));\
+ }\
+} while (0)
+
+#define OSSL_Check_Same_Class(obj1, obj2) do {\
+ if (!rb_obj_is_instance_of(obj1, rb_obj_class(obj2))) {\
+ ossl_raise(rb_eTypeError, "wrong argument type");\
+ }\
+} while (0)
+
+/*
+ * ASN1_DATE conversions
+ */
+VALUE asn1time_to_time(ASN1_TIME *);
+time_t time_to_time_t(VALUE);
+
+/*
+ * ASN1_INTEGER conversions
+ */
+VALUE asn1integer_to_num(ASN1_INTEGER *);
+ASN1_INTEGER *num_to_asn1integer(VALUE, ASN1_INTEGER *);
+
+/*
+ * String to HEXString conversion
+ */
+int string2hex(char *, int, char **, int *);
+
+/*
+ * Data Conversion
+ */
+BIO *ossl_obj2bio(VALUE);
+BIO *ossl_protect_obj2bio(VALUE,int*);
+VALUE ossl_membio2str(BIO*);
+VALUE ossl_protect_membio2str(BIO*,int*);
+STACK_OF(X509) *ossl_x509_ary2sk(VALUE);
+STACK_OF(X509) *ossl_protect_x509_ary2sk(VALUE,int*);
+
+/*
+ * our default PEM callback
+ */
+int ossl_pem_passwd_cb(char *, int, int, void *);
+
+/*
+ * ERRor messages
+ */
+#define OSSL_ErrMsg() ERR_reason_error_string(ERR_get_error())
+NORETURN(void ossl_raise(VALUE, const char *, ...));
+
+/*
+ * Verify callback
+ */
+extern int ossl_verify_cb_idx;
+
+struct ossl_verify_cb_args {
+ VALUE proc;
+ VALUE preverify_ok;
+ VALUE store_ctx;
+};
+
+VALUE ossl_call_verify_cb_proc(struct ossl_verify_cb_args *);
+int ossl_verify_cb(int, X509_STORE_CTX *);
+
+/*
+ * Debug
+ */
+extern VALUE dOSSL;
+
+#if defined(HAVE_VA_ARGS_MACRO)
+#define OSSL_Debug(fmt, ...) do { \
+ if (dOSSL == Qtrue) { \
+ fprintf(stderr, "OSSL_DEBUG: "); \
+ fprintf(stderr, fmt, ##__VA_ARGS__); \
+ fprintf(stderr, " [in %s (%s:%d)]\n", __func__, __FILE__, __LINE__); \
+ } \
+} while (0)
+
+#define OSSL_Warning(fmt, ...) do { \
+ OSSL_Debug(fmt, ##__VA_ARGS__); \
+ rb_warning(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define OSSL_Warn(fmt, ...) do { \
+ OSSL_Debug(fmt, ##__VA_ARGS__); \
+ rb_warn(fmt, ##__VA_ARGS__); \
+} while (0)
+#else
+void ossl_debug(const char *, ...);
+#define OSSL_Debug ossl_debug
+#define OSSL_Warning rb_warning
+#define OSSL_Warn rb_warn
+#endif
+
+/*
+ * Include all parts
+ */
+#include "openssl_missing.h"
+#include "ruby_missing.h"
+#include "ossl_bn.h"
+#include "ossl_cipher.h"
+#include "ossl_config.h"
+#include "ossl_digest.h"
+#include "ossl_hmac.h"
+#include "ossl_ns_spki.h"
+#include "ossl_pkcs7.h"
+#include "ossl_pkey.h"
+#include "ossl_rand.h"
+#include "ossl_ssl.h"
+#include "ossl_version.h"
+#include "ossl_x509.h"
+#include "ossl_ocsp.h"
+
+void Init_openssl(void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* _OSSL_H_ */
+
diff --git a/ext/openssl/ossl_bn.c b/ext/openssl/ossl_bn.c
new file mode 100644
index 0000000000..c59d809bae
--- /dev/null
+++ b/ext/openssl/ossl_bn.c
@@ -0,0 +1,751 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Technorama team <oss-ruby@technorama.net>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+/* modified by Michal Rokos <m.rokos@sh.cvut.cz> */
+#include "ossl.h"
+
+#define WrapBN(klass, obj, bn) do { \
+ if (!bn) { \
+ ossl_raise(rb_eRuntimeError, "BN wasn't initialized!"); \
+ } \
+ obj = Data_Wrap_Struct(klass, 0, BN_clear_free, bn); \
+} while (0)
+
+#define GetBN(obj, bn) do { \
+ Data_Get_Struct(obj, BIGNUM, bn); \
+ if (!bn) { \
+ ossl_raise(rb_eRuntimeError, "BN wasn't initialized!"); \
+ } \
+} while (0)
+
+#define SafeGetBN(obj, bn) do { \
+ OSSL_Check_Kind(obj, cBN); \
+ GetBN(obj, bn); \
+} while (0)
+
+/*
+ * Classes
+ */
+VALUE cBN;
+VALUE eBNError;
+
+/*
+ * Public
+ */
+VALUE
+ossl_bn_new(BIGNUM *bn)
+{
+ BIGNUM *newbn;
+ VALUE obj;
+
+ newbn = bn ? BN_dup(bn) : BN_new();
+ if (!newbn) {
+ ossl_raise(eBNError, NULL);
+ }
+ WrapBN(cBN, obj, newbn);
+
+ return obj;
+}
+
+BIGNUM *
+GetBNPtr(VALUE obj)
+{
+ BIGNUM *bn = NULL;
+
+ if (RTEST(rb_obj_is_kind_of(obj, cBN))) {
+ GetBN(obj, bn);
+ } else switch (TYPE(obj)) {
+ case T_FIXNUM:
+ case T_BIGNUM:
+ obj = rb_String(obj);
+ if (!BN_dec2bn(&bn, StringValuePtr(obj))) {
+ ossl_raise(eBNError, NULL);
+ }
+ WrapBN(cBN, obj, bn); /* Handle potencial mem leaks */
+ break;
+ default:
+ ossl_raise(rb_eTypeError, "Cannot convert into OpenSSL::BN");
+ }
+ return bn;
+}
+
+/*
+ * Private
+ */
+/*
+ * BN_CTX - is used in more difficult math. ops
+ * (Why just 1? Because Ruby itself isn't thread safe,
+ * we don't need to care about threads)
+ */
+BN_CTX *ossl_bn_ctx;
+
+static VALUE
+ossl_bn_alloc(VALUE klass)
+{
+ BIGNUM *bn;
+ VALUE obj;
+
+ if (!(bn = BN_new())) {
+ ossl_raise(eBNError, NULL);
+ }
+ WrapBN(klass, obj, bn);
+
+ return obj;
+}
+DEFINE_ALLOC_WRAPPER(ossl_bn_alloc)
+
+static VALUE
+ossl_bn_initialize(int argc, VALUE *argv, VALUE self)
+{
+ BIGNUM *bn;
+ VALUE str, bs;
+ int base = 10;
+
+ GetBN(self, bn);
+
+ if (rb_scan_args(argc, argv, "11", &str, &bs) == 2) {
+ base = NUM2INT(bs);
+ }
+ if (RTEST(rb_obj_is_kind_of(str, cBN))) {
+ BIGNUM *other;
+
+ GetBN(str, other); /* Safe - we checked kind_of? above */
+ if (!BN_copy(bn, other)) {
+ ossl_raise(eBNError, NULL);
+ }
+ return self;
+ }
+ str = rb_String(str);
+ StringValue(str);
+
+ switch (base) {
+ case 0:
+ if (!BN_mpi2bn(RSTRING(str)->ptr, RSTRING(str)->len, bn)) {
+ ossl_raise(eBNError, NULL);
+ }
+ break;
+ case 2:
+ if (!BN_bin2bn(RSTRING(str)->ptr, RSTRING(str)->len, bn)) {
+ ossl_raise(eBNError, NULL);
+ }
+ break;
+ case 10:
+ if (!BN_dec2bn(&bn, RSTRING(str)->ptr)) {
+ ossl_raise(eBNError, NULL);
+ }
+ break;
+ case 16:
+ if (!BN_hex2bn(&bn, RSTRING(str)->ptr)) {
+ ossl_raise(eBNError, NULL);
+ }
+ break;
+ default:
+ ossl_raise(rb_eArgError, "illegal radix %d", base);
+ }
+ return self;
+}
+
+static VALUE
+ossl_bn_to_s(int argc, VALUE *argv, VALUE self)
+{
+ BIGNUM *bn;
+ VALUE str, bs;
+ int base = 10, len;
+ char *buf;
+
+ GetBN(self, bn);
+
+ if (rb_scan_args(argc, argv, "01", &bs) == 1) {
+ base = NUM2INT(bs);
+ }
+ switch (base) {
+ case 0:
+ len = BN_bn2mpi(bn, NULL);
+ if (!(buf = OPENSSL_malloc(len))) {
+ ossl_raise(eBNError, "Cannot allocate mem for BN");
+ }
+ if (BN_bn2mpi(bn, buf) != len) {
+ OPENSSL_free(buf);
+ ossl_raise(eBNError, NULL);
+ }
+ break;
+ case 2:
+ len = BN_num_bytes(bn);
+ if (!(buf = OPENSSL_malloc(len))) {
+ ossl_raise(eBNError, "Cannot allocate mem for BN");
+ }
+ if (BN_bn2bin(bn, buf) != len) {
+ OPENSSL_free(buf);
+ ossl_raise(eBNError, NULL);
+ }
+ break;
+ case 10:
+ if (!(buf = BN_bn2dec(bn))) {
+ ossl_raise(eBNError, NULL);
+ }
+ len = strlen(buf);
+ break;
+ case 16:
+ if (!(buf = BN_bn2hex(bn))) {
+ ossl_raise(eBNError, NULL);
+ }
+ len = strlen(buf);
+ break;
+ default:
+ ossl_raise(rb_eArgError, "illegal radix %d", base);
+ }
+ str = rb_str_new(buf, len);
+ OPENSSL_free(buf);
+
+ return str;
+}
+
+static VALUE
+ossl_bn_to_i(VALUE self)
+{
+ BIGNUM *bn;
+ char *txt;
+ VALUE num;
+
+ GetBN(self, bn);
+
+ if (!(txt = BN_bn2dec(bn))) {
+ ossl_raise(eBNError, NULL);
+ }
+ num = rb_cstr_to_inum(txt, 10, Qtrue);
+ OPENSSL_free(txt);
+
+ return num;
+}
+
+static VALUE
+ossl_bn_to_bn(VALUE self)
+{
+ return self;
+}
+
+static VALUE
+ossl_bn_coerce(VALUE self, VALUE other)
+{
+ switch(TYPE(other)) {
+ case T_STRING:
+ self = ossl_bn_to_s(0, NULL, self);
+ break;
+ case T_FIXNUM:
+ case T_BIGNUM:
+ self = ossl_bn_to_i(self);
+ break;
+ default:
+ if (!RTEST(rb_obj_is_kind_of(other, cBN))) {
+ ossl_raise(rb_eTypeError, "Don't know how to coerce");
+ }
+ }
+ return rb_assoc_new(other, self);
+}
+
+#define BIGNUM_BOOL1(func) \
+ static VALUE \
+ ossl_bn_##func(VALUE self) \
+ { \
+ BIGNUM *bn; \
+ GetBN(self, bn); \
+ if (BN_##func(bn)) { \
+ return Qtrue; \
+ } \
+ return Qfalse; \
+ }
+BIGNUM_BOOL1(is_zero);
+BIGNUM_BOOL1(is_one);
+BIGNUM_BOOL1(is_odd);
+
+#define BIGNUM_1c(func) \
+ static VALUE \
+ ossl_bn_##func(VALUE self) \
+ { \
+ BIGNUM *bn, *result; \
+ VALUE obj; \
+ GetBN(self, bn); \
+ if (!(result = BN_new())) { \
+ ossl_raise(eBNError, NULL); \
+ } \
+ if (!BN_##func(result, bn, ossl_bn_ctx)) { \
+ BN_free(result); \
+ ossl_raise(eBNError, NULL); \
+ } \
+ WrapBN(CLASS_OF(self), obj, result); \
+ return obj; \
+ }
+BIGNUM_1c(sqr);
+
+#define BIGNUM_2(func) \
+ static VALUE \
+ ossl_bn_##func(VALUE self, VALUE other) \
+ { \
+ BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \
+ VALUE obj; \
+ GetBN(self, bn1); \
+ if (!(result = BN_new())) { \
+ ossl_raise(eBNError, NULL); \
+ } \
+ if (!BN_##func(result, bn1, bn2)) { \
+ BN_free(result); \
+ ossl_raise(eBNError, NULL); \
+ } \
+ WrapBN(CLASS_OF(self), obj, result); \
+ return obj; \
+ }
+BIGNUM_2(add);
+BIGNUM_2(sub);
+
+#define BIGNUM_2c(func) \
+ static VALUE \
+ ossl_bn_##func(VALUE self, VALUE other) \
+ { \
+ BIGNUM *bn1, *bn2 = GetBNPtr(other), *result; \
+ VALUE obj; \
+ GetBN(self, bn1); \
+ if (!(result = BN_new())) { \
+ ossl_raise(eBNError, NULL); \
+ } \
+ if (!BN_##func(result, bn1, bn2, ossl_bn_ctx)) { \
+ BN_free(result); \
+ ossl_raise(eBNError, NULL); \
+ } \
+ WrapBN(CLASS_OF(self), obj, result); \
+ return obj; \
+ }
+BIGNUM_2c(mul);
+BIGNUM_2c(mod);
+BIGNUM_2c(exp);
+BIGNUM_2c(gcd);
+BIGNUM_2c(mod_sqr);
+BIGNUM_2c(mod_inverse);
+
+static VALUE
+ossl_bn_div(VALUE self, VALUE other)
+{
+ BIGNUM *bn1, *bn2 = GetBNPtr(other), *r1, *r2;
+ VALUE obj1, obj2;
+
+ GetBN(self, bn1);
+
+ if (!(r1 = BN_new())) {
+ ossl_raise(eBNError, NULL);
+ }
+ if (!(r2 = BN_new())) {
+ BN_free(r1);
+ ossl_raise(eBNError, NULL);
+ }
+ if (!BN_div(r1, r2, bn1, bn2, ossl_bn_ctx)) {
+ BN_free(r1);
+ BN_free(r2);
+ ossl_raise(eBNError, NULL);
+ }
+ WrapBN(CLASS_OF(self), obj1, r1);
+ WrapBN(CLASS_OF(self), obj2, r2);
+
+ return rb_ary_new3(2, obj1, obj2);
+}
+
+#define BIGNUM_3c(func) \
+ static VALUE \
+ ossl_bn_##func(VALUE self, VALUE other1, VALUE other2) \
+ { \
+ BIGNUM *bn1, *bn2 = GetBNPtr(other1); \
+ BIGNUM *bn3 = GetBNPtr(other2), *result; \
+ VALUE obj; \
+ GetBN(self, bn1); \
+ if (!(result = BN_new())) { \
+ ossl_raise(eBNError, NULL); \
+ } \
+ if (!BN_##func(result, bn1, bn2, bn3, ossl_bn_ctx)) { \
+ BN_free(result); \
+ ossl_raise(eBNError, NULL); \
+ } \
+ WrapBN(CLASS_OF(self), obj, result); \
+ return obj; \
+ }
+BIGNUM_3c(mod_add);
+BIGNUM_3c(mod_sub);
+BIGNUM_3c(mod_mul);
+BIGNUM_3c(mod_exp);
+
+#define BIGNUM_BIT(func) \
+ static VALUE \
+ ossl_bn_##func(VALUE self, VALUE bit) \
+ { \
+ BIGNUM *bn; \
+ GetBN(self, bn); \
+ if (!BN_##func(bn, NUM2INT(bit))) { \
+ ossl_raise(eBNError, NULL); \
+ } \
+ return self; \
+ }
+BIGNUM_BIT(set_bit);
+BIGNUM_BIT(clear_bit);
+BIGNUM_BIT(mask_bits);
+
+static VALUE
+ossl_bn_is_bit_set(VALUE self, VALUE bit)
+{
+ BIGNUM *bn;
+
+ GetBN(self, bn);
+
+ if (BN_is_bit_set(bn, NUM2INT(bit))) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+#define BIGNUM_SHIFT(func) \
+ static VALUE \
+ ossl_bn_##func(VALUE self, VALUE bits) \
+ { \
+ BIGNUM *bn, *result; \
+ int b; \
+ VALUE obj; \
+ GetBN(self, bn); \
+ b = NUM2INT(bits); \
+ if (!(result = BN_new())) { \
+ ossl_raise(eBNError, NULL); \
+ } \
+ if (!BN_##func(result, bn, b)) { \
+ BN_free(result); \
+ ossl_raise(eBNError, NULL); \
+ } \
+ WrapBN(CLASS_OF(self), obj, result); \
+ return obj; \
+ }
+BIGNUM_SHIFT(lshift);
+BIGNUM_SHIFT(rshift);
+
+#define BIGNUM_RAND(func) \
+ static VALUE \
+ ossl_bn_s_##func(int argc, VALUE *argv, VALUE klass) \
+ { \
+ BIGNUM *result; \
+ int bottom = 0, top = 0, b; \
+ VALUE bits, fill, odd, obj; \
+ \
+ switch (rb_scan_args(argc, argv, "12", &bits, &fill, &odd)) { \
+ case 3: \
+ bottom = (odd == Qtrue) ? 1 : 0; \
+ /* FALLTHROUGH */ \
+ case 2: \
+ top = FIX2INT(fill); \
+ } \
+ b = NUM2INT(bits); \
+ if (!(result = BN_new())) { \
+ ossl_raise(eBNError, NULL); \
+ } \
+ if (!BN_##func(result, b, top, bottom)) { \
+ BN_free(result); \
+ ossl_raise(eBNError, NULL); \
+ } \
+ WrapBN(klass, obj, result); \
+ return obj; \
+ }
+BIGNUM_RAND(rand);
+BIGNUM_RAND(pseudo_rand);
+
+#define BIGNUM_RAND_RANGE(func) \
+ static VALUE \
+ ossl_bn_s_##func##_range(VALUE klass, VALUE range) \
+ { \
+ BIGNUM *bn = GetBNPtr(range), *result; \
+ VALUE obj; \
+ if (!(result = BN_new())) { \
+ ossl_raise(eBNError, NULL); \
+ } \
+ if (!BN_##func##_range(result, bn)) { \
+ BN_free(result); \
+ ossl_raise(eBNError, NULL); \
+ } \
+ WrapBN(klass, obj, result); \
+ return obj; \
+ }
+
+#define BIGNUM_RAND_RANGE_NOT_IMPL(func) \
+ static VALUE \
+ ossl_bn_s_##func##_range(VALUE klass, VALUE range) \
+ { \
+ rb_notimplement(); \
+ }
+
+#if defined(HAVE_BN_RAND_RANGE)
+BIGNUM_RAND_RANGE(rand);
+#else
+BIGNUM_RAND_RANGE_NOT_IMPL(rand);
+#endif
+
+#if defined(HAVE_BN_PSEUDO_RAND_RANGE)
+BIGNUM_RAND_RANGE(pseudo_rand);
+#else
+BIGNUM_RAND_RANGE_NOT_IMPL(pseudo_rand);
+#endif
+
+static VALUE
+ossl_bn_s_generate_prime(int argc, VALUE *argv, VALUE klass)
+{
+ BIGNUM *add = NULL, *rem = NULL, *result;
+ int safe = 1, num;
+ VALUE vnum, vsafe, vadd, vrem, obj;
+
+ rb_scan_args(argc, argv, "13", &vnum, &vsafe, &vadd, &vrem);
+
+ num = NUM2INT(vnum);
+
+ if (vsafe == Qfalse) {
+ safe = 0;
+ }
+ if (!NIL_P(vadd)) {
+ if (NIL_P(vrem)) {
+ ossl_raise(rb_eArgError,
+ "if ADD is specified, REM must be also given");
+ }
+ add = GetBNPtr(vadd);
+ rem = GetBNPtr(vrem);
+ }
+ if (!(result = BN_new())) {
+ ossl_raise(eBNError, NULL);
+ }
+ if (!BN_generate_prime(result, num, safe, add, rem, NULL, NULL)) {
+ BN_free(result);
+ ossl_raise(eBNError, NULL);
+ }
+ WrapBN(klass, obj, result);
+
+ return obj;
+}
+
+#define BIGNUM_NUM(func) \
+ static VALUE \
+ ossl_bn_##func(VALUE self) \
+ { \
+ BIGNUM *bn; \
+ GetBN(self, bn); \
+ return INT2FIX(BN_##func(bn)); \
+ }
+BIGNUM_NUM(num_bytes);
+BIGNUM_NUM(num_bits);
+
+static VALUE
+ossl_bn_copy(VALUE self, VALUE other)
+{
+ BIGNUM *bn1, *bn2;
+
+ rb_check_frozen(self);
+
+ if (self == other) return self;
+
+ GetBN(self, bn1);
+ bn2 = GetBNPtr(other);
+
+ if (!BN_copy(bn1, bn2)) {
+ ossl_raise(eBNError, NULL);
+ }
+ return self;
+}
+
+#define BIGNUM_CMP(func) \
+ static VALUE \
+ ossl_bn_##func(VALUE self, VALUE other) \
+ { \
+ BIGNUM *bn1, *bn2 = GetBNPtr(other); \
+ GetBN(self, bn1); \
+ return INT2FIX(BN_##func(bn1, bn2)); \
+ }
+BIGNUM_CMP(cmp);
+BIGNUM_CMP(ucmp);
+
+static VALUE
+ossl_bn_eql(VALUE self, VALUE other)
+{
+ if (ossl_bn_cmp(self, other) == INT2FIX(0)) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+static VALUE
+ossl_bn_is_prime(int argc, VALUE *argv, VALUE self)
+{
+ BIGNUM *bn;
+ VALUE vchecks;
+ int checks = BN_prime_checks;
+
+ GetBN(self, bn);
+
+ if (rb_scan_args(argc, argv, "01", &vchecks) == 0) {
+ checks = NUM2INT(vchecks);
+ }
+ switch (BN_is_prime(bn, checks, NULL, ossl_bn_ctx, NULL)) {
+ case 1:
+ return Qtrue;
+ case 0:
+ return Qfalse;
+ default:
+ ossl_raise(eBNError, NULL);
+ }
+ /* not reachable */
+ return Qnil;
+}
+
+static VALUE
+ossl_bn_is_prime_fasttest(int argc, VALUE *argv, VALUE self)
+{
+ BIGNUM *bn;
+ VALUE vchecks, vtrivdiv;
+ int checks = BN_prime_checks, do_trial_division = 1;
+
+ GetBN(self, bn);
+
+ rb_scan_args(argc, argv, "02", &vchecks, &vtrivdiv);
+
+ if (!NIL_P(vchecks)) {
+ checks = NUM2INT(vchecks);
+ }
+ /* handle true/false */
+ if (vtrivdiv == Qfalse) {
+ do_trial_division = 0;
+ }
+ switch (BN_is_prime_fasttest(bn, checks, NULL, ossl_bn_ctx, NULL, do_trial_division)) {
+ case 1:
+ return Qtrue;
+ case 0:
+ return Qfalse;
+ default:
+ ossl_raise(eBNError, NULL);
+ }
+ /* not reachable */
+ return Qnil;
+}
+
+/*
+ * INIT
+ * (NOTE: ordering of methods is the same as in 'man bn')
+ */
+void
+Init_ossl_bn()
+{
+ if (!(ossl_bn_ctx = BN_CTX_new())) {
+ ossl_raise(rb_eRuntimeError, "Cannot init BN_CTX");
+ }
+
+ eBNError = rb_define_class_under(mOSSL, "BNError", eOSSLError);
+
+ cBN = rb_define_class_under(mOSSL, "BN", rb_cObject);
+
+ rb_define_alloc_func(cBN, ossl_bn_alloc);
+ rb_define_method(cBN, "initialize", ossl_bn_initialize, -1);
+
+ rb_define_copy_func(cBN, ossl_bn_copy);
+ rb_define_method(cBN, "copy", ossl_bn_copy, 1);
+
+ /* swap (=coerce?) */
+
+ rb_define_method(cBN, "num_bytes", ossl_bn_num_bytes, 0);
+ rb_define_method(cBN, "num_bits", ossl_bn_num_bits, 0);
+ /* num_bits_word */
+
+ rb_define_method(cBN, "+", ossl_bn_add, 1);
+ rb_define_method(cBN, "-", ossl_bn_sub, 1);
+ rb_define_method(cBN, "*", ossl_bn_mul, 1);
+ rb_define_method(cBN, "sqr", ossl_bn_sqr, 0);
+ rb_define_method(cBN, "/", ossl_bn_div, 1);
+ rb_define_method(cBN, "%", ossl_bn_mod, 1);
+ /* nnmod */
+
+ rb_define_method(cBN, "mod_add", ossl_bn_mod_add, 2);
+ rb_define_method(cBN, "mod_sub", ossl_bn_mod_sub, 2);
+ rb_define_method(cBN, "mod_mul", ossl_bn_mod_mul, 2);
+ rb_define_method(cBN, "mod_sqr", ossl_bn_mod_sqr, 1);
+ rb_define_method(cBN, "**", ossl_bn_exp, 1);
+ rb_define_method(cBN, "mod_exp", ossl_bn_mod_exp, 2);
+ rb_define_method(cBN, "gcd", ossl_bn_gcd, 1);
+
+ /* add_word
+ * sub_word
+ * mul_word
+ * div_word
+ * mod_word */
+
+ rb_define_method(cBN, "cmp", ossl_bn_cmp, 1);
+ rb_define_alias(cBN, "<=>", "cmp");
+ rb_define_method(cBN, "ucmp", ossl_bn_ucmp, 1);
+ rb_define_method(cBN, "eql?", ossl_bn_eql, 1);
+ rb_define_alias(cBN, "==", "eql?");
+ rb_define_alias(cBN, "===", "eql?");
+ rb_define_method(cBN, "zero?", ossl_bn_is_zero, 0);
+ rb_define_method(cBN, "one?", ossl_bn_is_one, 0);
+ /* is_word */
+ rb_define_method(cBN, "odd?", ossl_bn_is_odd, 0);
+
+ /* zero
+ * one
+ * value_one - DON'T IMPL.
+ * set_word
+ * get_word */
+
+ rb_define_singleton_method(cBN, "rand", ossl_bn_s_rand, -1);
+ rb_define_singleton_method(cBN, "pseudo_rand", ossl_bn_s_pseudo_rand, -1);
+ rb_define_singleton_method(cBN, "rand_range", ossl_bn_s_rand_range, 1);
+ rb_define_singleton_method(cBN, "pseudo_rand_range", ossl_bn_s_pseudo_rand_range, 1);
+
+ rb_define_singleton_method(cBN, "generate_prime", ossl_bn_s_generate_prime, -1);
+ rb_define_method(cBN, "prime?", ossl_bn_is_prime, -1);
+
+ rb_define_method(cBN, "set_bit!", ossl_bn_set_bit, 1);
+ rb_define_method(cBN, "clear_bit!", ossl_bn_clear_bit, 1);
+ rb_define_method(cBN, "bit_set?", ossl_bn_is_bit_set, 1);
+ rb_define_method(cBN, "mask_bits!", ossl_bn_mask_bits, 1);
+ rb_define_method(cBN, "<<", ossl_bn_lshift, 1);
+ /* lshift1 - DON'T IMPL. */
+ rb_define_method(cBN, ">>", ossl_bn_rshift, 1);
+ /* rshift1 - DON'T IMPL. */
+
+ /*
+ * bn2bin
+ * bin2bn
+ * bn2hex
+ * bn2dec
+ * hex2bn
+ * dec2bn - all these are implemented in ossl_bn_initialize, and ossl_bn_to_s
+ * print - NOT IMPL.
+ * print_fp - NOT IMPL.
+ * bn2mpi
+ * mpi2bn
+ */
+ rb_define_method(cBN, "to_s", ossl_bn_to_s, -1);
+ rb_define_method(cBN, "to_i", ossl_bn_to_i, 0);
+ rb_define_alias(cBN, "to_int", "to_i");
+ rb_define_method(cBN, "to_bn", ossl_bn_to_bn, 0);
+ rb_define_method(cBN, "coerce", ossl_bn_coerce, 1);
+
+ /*
+ * TODO:
+ * But how to: from_bin, from_mpi? PACK?
+ * to_bin
+ * to_mpi
+ */
+
+ rb_define_method(cBN, "mod_inverse", ossl_bn_mod_inverse, 1);
+
+ /* RECiProcal
+ * MONTgomery */
+
+ /*
+ * TODO:
+ * Where to belong these?
+ */
+ rb_define_method(cBN, "prime_fasttest?", ossl_bn_is_prime_fasttest, -1);
+}
+
diff --git a/ext/openssl/ossl_bn.h b/ext/openssl/ossl_bn.h
new file mode 100644
index 0000000000..12aa484873
--- /dev/null
+++ b/ext/openssl/ossl_bn.h
@@ -0,0 +1,22 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#if !defined(_OSSL_BN_H_)
+#define _OSSL_BN_H_
+
+extern VALUE cBN;
+extern VALUE eBNError;
+
+VALUE ossl_bn_new(BIGNUM *);
+BIGNUM *GetBNPtr(VALUE);
+void Init_ossl_bn(void);
+
+#endif /* _OSS_BN_H_ */
+
diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c
new file mode 100644
index 0000000000..3d63a2a038
--- /dev/null
+++ b/ext/openssl/ossl_cipher.c
@@ -0,0 +1,377 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#include "ossl.h"
+
+#define MakeCipher(obj, klass, ctx) \
+ obj = Data_Make_Struct(klass, EVP_CIPHER_CTX, 0, ossl_cipher_free, ctx)
+#define GetCipher(obj, ctx) do { \
+ Data_Get_Struct(obj, EVP_CIPHER_CTX, ctx); \
+ if (!ctx) { \
+ ossl_raise(rb_eRuntimeError, "Cipher not inititalized!"); \
+ } \
+} while (0)
+#define SafeGetCipher(obj, ctx) do { \
+ OSSL_Check_Kind(obj, cCipher); \
+ GetCipher(obj, ctx); \
+} while (0)
+
+/*
+ * Classes
+ */
+VALUE mCipher;
+VALUE cCipher;
+VALUE eCipherError;
+
+/*
+ * PUBLIC
+ */
+const EVP_CIPHER *
+GetCipherPtr(VALUE obj)
+{
+ EVP_CIPHER_CTX *ctx;
+
+ SafeGetCipher(obj, ctx);
+
+ return EVP_CIPHER_CTX_cipher(ctx);
+}
+
+/*
+ * PRIVATE
+ */
+static void
+ossl_cipher_free(EVP_CIPHER_CTX *ctx)
+{
+ if (ctx) {
+ EVP_CIPHER_CTX_cleanup(ctx);
+ free(ctx);
+ }
+}
+
+static VALUE
+ossl_cipher_alloc(VALUE klass)
+{
+ EVP_CIPHER_CTX *ctx;
+ VALUE obj;
+
+ MakeCipher(obj, klass, ctx);
+
+ return obj;
+}
+DEFINE_ALLOC_WRAPPER(ossl_cipher_alloc)
+
+static VALUE
+ossl_cipher_initialize(VALUE self, VALUE str)
+{
+ EVP_CIPHER_CTX *ctx;
+ const EVP_CIPHER *cipher;
+ char *name;
+
+ GetCipher(self, ctx);
+
+ name = StringValuePtr(str);
+
+ if (!(cipher = EVP_get_cipherbyname(name))) {
+ ossl_raise(rb_eRuntimeError, "Unsupported cipher algorithm (%s).", name);
+ }
+ EVP_CIPHER_CTX_init(ctx);
+ if (EVP_CipherInit(ctx, cipher, NULL, NULL, -1) != 1)
+ ossl_raise(eCipherError, NULL);
+
+ return self;
+}
+static VALUE
+ossl_cipher_copy(VALUE self, VALUE other)
+{
+ EVP_CIPHER_CTX *ctx1, *ctx2;
+
+ rb_check_frozen(self);
+ if (self == other) return self;
+
+ GetCipher(self, ctx1);
+ SafeGetCipher(other, ctx2);
+
+ memcpy(ctx1, ctx2, sizeof(EVP_CIPHER_CTX));
+
+ return self;
+}
+
+static VALUE
+ossl_cipher_reset(VALUE self)
+{
+ EVP_CIPHER_CTX *ctx;
+
+ GetCipher(self, ctx);
+ if (EVP_CipherInit(ctx, NULL, NULL, NULL, -1) != 1)
+ ossl_raise(eCipherError, NULL);
+
+ return self;
+}
+
+static VALUE
+ossl_cipher_encrypt(int argc, VALUE *argv, VALUE self)
+{
+ EVP_CIPHER_CTX *ctx;
+ unsigned char iv[EVP_MAX_IV_LENGTH], key[EVP_MAX_KEY_LENGTH];
+ VALUE pass, init_v;
+
+ GetCipher(self, ctx);
+
+ rb_scan_args(argc, argv, "02", &pass, &init_v);
+
+ if (NIL_P(init_v)) {
+ /*
+ * TODO:
+ * random IV generation!
+ */
+ memcpy(iv, "OpenSSL for Ruby rulez!", sizeof(iv));
+ /*
+ RAND_add(data,i,0); where from take data?
+ if (RAND_pseudo_bytes(iv, 8) < 0) {
+ ossl_raise(eCipherError, NULL);
+ }
+ */
+ }
+ else {
+ init_v = rb_obj_as_string(init_v);
+ if (EVP_MAX_IV_LENGTH > RSTRING(init_v)->len) {
+ memset(iv, 0, EVP_MAX_IV_LENGTH);
+ memcpy(iv, RSTRING(init_v)->ptr, RSTRING(init_v)->len);
+ }
+ else {
+ memcpy(iv, RSTRING(init_v)->ptr, sizeof(iv));
+ }
+ }
+
+ if (EVP_CipherInit(ctx, NULL, NULL, NULL, 1) != 1) {
+ ossl_raise(eCipherError, NULL);
+ }
+
+ if (!NIL_P(pass)) {
+ StringValue(pass);
+
+ EVP_BytesToKey(EVP_CIPHER_CTX_cipher(ctx), EVP_md5(), iv,
+ RSTRING(pass)->ptr, RSTRING(pass)->len, 1, key, NULL);
+ if (EVP_CipherInit(ctx, NULL, key, iv, -1) != 1) {
+ ossl_raise(eCipherError, NULL);
+ }
+ }
+
+ return self;
+}
+
+static VALUE
+ossl_cipher_decrypt(int argc, VALUE *argv, VALUE self)
+{
+ EVP_CIPHER_CTX *ctx;
+ unsigned char iv[EVP_MAX_IV_LENGTH], key[EVP_MAX_KEY_LENGTH];
+ VALUE pass, init_v;
+
+ GetCipher(self, ctx);
+ rb_scan_args(argc, argv, "02", &pass, &init_v);
+
+ if (NIL_P(init_v)) {
+ /*
+ * TODO:
+ * random IV generation!
+ */
+ memcpy(iv, "OpenSSL for Ruby rulez!", EVP_MAX_IV_LENGTH);
+ }
+ else {
+ init_v = rb_obj_as_string(init_v);
+ if (EVP_MAX_IV_LENGTH > RSTRING(init_v)->len) {
+ memset(iv, 0, EVP_MAX_IV_LENGTH);
+ memcpy(iv, RSTRING(init_v)->ptr, RSTRING(init_v)->len);
+ }
+ else {
+ memcpy(iv, RSTRING(init_v)->ptr, EVP_MAX_IV_LENGTH);
+ }
+ }
+
+ if (EVP_CipherInit(ctx, NULL, NULL, NULL, 0) != 1) {
+ ossl_raise(eCipherError, NULL);
+ }
+
+ if (!NIL_P(pass)) {
+ StringValue(pass);
+
+ EVP_BytesToKey(EVP_CIPHER_CTX_cipher(ctx), EVP_md5(), iv,
+ RSTRING(pass)->ptr, RSTRING(pass)->len, 1, key, NULL);
+ if (EVP_CipherInit(ctx, NULL, key, iv, -1) != 1) {
+ ossl_raise(eCipherError, NULL);
+ }
+ }
+
+ return self;
+}
+
+static VALUE
+ossl_cipher_update(VALUE self, VALUE data)
+{
+ EVP_CIPHER_CTX *ctx;
+ char *in, *out;
+ int in_len, out_len;
+ VALUE str;
+
+ GetCipher(self, ctx);
+
+ StringValue(data);
+ in = RSTRING(data)->ptr;
+ in_len = RSTRING(data)->len;
+
+ if (!(out = OPENSSL_malloc(in_len+EVP_CIPHER_CTX_block_size(ctx)))) {
+ ossl_raise(eCipherError, NULL);
+ }
+ if (!EVP_CipherUpdate(ctx, out, &out_len, in, in_len)) {
+ OPENSSL_free(out);
+ ossl_raise(eCipherError, NULL);
+ }
+ str = rb_str_new(out, out_len);
+ OPENSSL_free(out);
+
+ return str;
+}
+
+static VALUE
+ossl_cipher_final(VALUE self)
+{
+ EVP_CIPHER_CTX *ctx;
+ char *out;
+ int out_len;
+ VALUE str;
+
+ GetCipher(self, ctx);
+
+ if (!(out = OPENSSL_malloc(EVP_CIPHER_CTX_block_size(ctx)))) {
+ ossl_raise(eCipherError, NULL);
+ }
+ if (!EVP_CipherFinal(ctx, out, &out_len)) {
+ OPENSSL_free(out);
+ ossl_raise(eCipherError, NULL);
+ }
+
+ str = rb_str_new(out, out_len);
+ OPENSSL_free(out);
+
+ return str;
+}
+
+static VALUE
+ossl_cipher_name(VALUE self)
+{
+ EVP_CIPHER_CTX *ctx;
+
+ GetCipher(self, ctx);
+
+ return rb_str_new2(EVP_CIPHER_name(EVP_CIPHER_CTX_cipher(ctx)));
+}
+
+static VALUE
+ossl_cipher_set_key(VALUE self, VALUE key)
+{
+ EVP_CIPHER_CTX *ctx;
+
+ StringValue(key);
+ GetCipher(self, ctx);
+
+ if (RSTRING(key)->len < EVP_CIPHER_CTX_key_length(ctx))
+ ossl_raise(eCipherError, "key length too short");
+
+ if (EVP_CipherInit(ctx, NULL, RSTRING(key)->ptr, NULL, -1) != 1)
+ ossl_raise(eCipherError, NULL);
+
+ return key;
+}
+
+static VALUE
+ossl_cipher_set_iv(VALUE self, VALUE iv)
+{
+ EVP_CIPHER_CTX *ctx;
+
+ StringValue(iv);
+ GetCipher(self, ctx);
+
+ if (RSTRING(iv)->len < EVP_CIPHER_CTX_iv_length(ctx))
+ ossl_raise(eCipherError, "iv length too short");
+
+ if (EVP_CipherInit(ctx, NULL, NULL, RSTRING(iv)->ptr, -1) != 1)
+ ossl_raise(eCipherError, NULL);
+
+ return iv;
+}
+
+static VALUE
+ossl_cipher_set_padding(VALUE self, VALUE padding)
+{
+#if defined(HAVE_ST_FLAGS)
+ EVP_CIPHER_CTX *ctx;
+
+ GetCipher(self, ctx);
+
+ if (EVP_CIPHER_CTX_set_padding(ctx, NUM2INT(padding)) != 1)
+ ossl_raise(eCipherError, NULL);
+#else
+ rb_notimplement();
+#endif
+ return padding;
+}
+
+#define CIPHER_0ARG_INT(func) \
+ static VALUE \
+ ossl_cipher_##func(VALUE self) \
+ { \
+ EVP_CIPHER_CTX *ctx; \
+ GetCipher(self, ctx); \
+ return INT2NUM(EVP_CIPHER_##func(EVP_CIPHER_CTX_cipher(ctx))); \
+ }
+CIPHER_0ARG_INT(key_length)
+CIPHER_0ARG_INT(iv_length)
+CIPHER_0ARG_INT(block_size)
+
+/*
+ * INIT
+ */
+void
+Init_ossl_cipher(void)
+{
+ mCipher = rb_define_module_under(mOSSL, "Cipher");
+ eCipherError = rb_define_class_under(mOSSL, "CipherError", eOSSLError);
+ cCipher = rb_define_class_under(mCipher, "Cipher", rb_cObject);
+
+ rb_define_alloc_func(cCipher, ossl_cipher_alloc);
+ rb_define_method(cCipher, "initialize", ossl_cipher_initialize, 1);
+
+ rb_define_copy_func(cCipher, ossl_cipher_copy);
+
+ rb_define_method(cCipher, "reset", ossl_cipher_reset, 0);
+
+ rb_define_method(cCipher, "encrypt", ossl_cipher_encrypt, -1);
+ rb_define_method(cCipher, "decrypt", ossl_cipher_decrypt, -1);
+ rb_define_method(cCipher, "update", ossl_cipher_update, 1);
+ rb_define_alias(cCipher, "<<", "update");
+ rb_define_method(cCipher, "final", ossl_cipher_final, 0);
+
+ rb_define_method(cCipher, "name", ossl_cipher_name, 0);
+
+ rb_define_method(cCipher, "key=", ossl_cipher_set_key, 1);
+ rb_define_method(cCipher, "key_len", ossl_cipher_key_length, 0);
+/*
+ * TODO
+ * int EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *x, int keylen);
+ */
+ rb_define_method(cCipher, "iv=", ossl_cipher_set_iv, 1);
+ rb_define_method(cCipher, "iv_len", ossl_cipher_iv_length, 0);
+
+ rb_define_method(cCipher, "block_size", ossl_cipher_block_size, 0);
+
+ rb_define_method(cCipher, "padding=", ossl_cipher_set_padding, 1);
+
+} /* Init_ossl_cipher */
+
diff --git a/ext/openssl/ossl_cipher.h b/ext/openssl/ossl_cipher.h
new file mode 100644
index 0000000000..4dcbd4fd89
--- /dev/null
+++ b/ext/openssl/ossl_cipher.h
@@ -0,0 +1,22 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#if !defined(_OSSL_CIPHER_H_)
+#define _OSSL_CIPHER_H_
+
+extern VALUE mCipher;
+extern VALUE cCipher;
+extern VALUE eCipherError;
+
+const EVP_CIPHER *GetCipherPtr(VALUE);
+void Init_ossl_cipher(void);
+
+#endif /* _OSSL_CIPHER_H_ */
+
diff --git a/ext/openssl/ossl_config.c b/ext/openssl/ossl_config.c
new file mode 100644
index 0000000000..f721bb90b1
--- /dev/null
+++ b/ext/openssl/ossl_config.c
@@ -0,0 +1,152 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#include "ossl.h"
+
+#define WrapConfig(klass, obj, conf) do { \
+ if (!conf) { \
+ ossl_raise(rb_eRuntimeError, "Config wasn't intitialized!"); \
+ } \
+ obj = Data_Wrap_Struct(klass, 0, NCONF_free, conf); \
+} while (0)
+
+#define GetConfig(obj, conf) do { \
+ Data_Get_Struct(obj, CONF, conf); \
+ if (!conf) { \
+ ossl_raise(rb_eRuntimeError, "Config wasn't intitialized!"); \
+ } \
+} while (0)
+
+/*
+ * Classes
+ */
+VALUE cConfig;
+VALUE eConfigError;
+
+/*
+ * Public
+ */
+
+/*
+ * Private
+ */
+static VALUE
+ossl_config_s_load(int argc, VALUE *argv, VALUE klass)
+{
+ CONF *conf;
+ long err_line = -1;
+ char *filename;
+ VALUE path, obj;
+
+ if (rb_scan_args(argc, argv, "01", &path) == 1) {
+ SafeStringValue(path);
+ filename = BUF_strdup(RSTRING(path)->ptr);
+ }
+ else {
+ if (!(filename = CONF_get1_default_config_file())) {
+ ossl_raise(eConfigError, NULL);
+ }
+ }
+ if (!(conf = NCONF_new(NULL))) {
+ OPENSSL_free(filename);
+ ossl_raise(eConfigError, NULL);
+ }
+ OSSL_Debug("Loading file: %s", filename);
+
+ if (!NCONF_load(conf, filename, &err_line)) {
+ char tmp[255];
+
+ memcpy(tmp, filename, strlen(filename)>=sizeof(tmp)?sizeof(tmp):strlen(filename));
+ tmp[sizeof(tmp)-1] = '\0';
+ OPENSSL_free(filename);
+
+ if (err_line <= 0) {
+ ossl_raise(eConfigError, "wrong config file (%s)", tmp);
+ } else {
+ ossl_raise(eConfigError, "error on line %ld in config file \"%s\"",
+ err_line, tmp);
+ }
+ }
+ OPENSSL_free(filename);
+ WrapConfig(klass, obj, conf);
+
+ return obj;
+}
+
+static VALUE
+ossl_config_get_value(int argc, VALUE *argv, VALUE self)
+{
+ CONF *conf;
+ VALUE section, item;
+ char *sect = NULL, *str;
+
+ GetConfig(self, conf);
+
+ if (rb_scan_args(argc, argv, "11", &section, &item) == 1) {
+ item = section;
+ } else if (!NIL_P(section)) {
+ sect = StringValuePtr(section);
+ }
+ if (!(str = NCONF_get_string(conf, sect, StringValuePtr(item)))) {
+ ossl_raise(eConfigError, NULL);
+ }
+ return rb_str_new2(str);
+}
+
+/*
+ * Get all numbers as strings - use str.to_i to convert
+ * long number = CONF_get_number(confp->config, sect, StringValuePtr(item));
+ */
+
+static VALUE
+ossl_config_get_section(VALUE self, VALUE section)
+{
+ CONF *conf;
+ STACK_OF(CONF_VALUE) *sk;
+ CONF_VALUE *entry;
+ int i, entries;
+ VALUE hash;
+
+ GetConfig(self, conf);
+
+ if (!(sk = NCONF_get_section(conf, StringValuePtr(section)))) {
+ ossl_raise(eConfigError, NULL);
+ }
+ hash = rb_hash_new();
+
+ if ((entries = sk_CONF_VALUE_num(sk)) < 0) {
+ OSSL_Debug("# of items in section is < 0?!?");
+ return hash;
+ }
+ for (i=0; i<entries; i++) {
+ entry = sk_CONF_VALUE_value(sk, i);
+ rb_hash_aset(hash, rb_str_new2(entry->name), rb_str_new2(entry->value));
+ }
+ return hash;
+}
+
+/*
+ * INIT
+ */
+void
+Init_ossl_config()
+{
+ eConfigError = rb_define_class_under(mOSSL, "ConfigError", eOSSLError);
+
+ cConfig = rb_define_class_under(mOSSL, "Config", rb_cObject);
+
+ rb_define_singleton_method(cConfig, "load", ossl_config_s_load, -1);
+ rb_define_alias(CLASS_OF(cConfig), "new", "load");
+
+ rb_define_method(cConfig, "value", ossl_config_get_value, -1);
+ rb_define_method(cConfig, "section", ossl_config_get_section, 1);
+ rb_define_alias(cConfig, "[]", "section");
+}
+
diff --git a/ext/openssl/ossl_config.h b/ext/openssl/ossl_config.h
new file mode 100644
index 0000000000..c45619dc1b
--- /dev/null
+++ b/ext/openssl/ossl_config.h
@@ -0,0 +1,20 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#if !defined(_OSSL_CONFIG_H_)
+#define _OSSL_CONFIG_H_
+
+extern VALUE cConfig;
+extern VALUE eConfigError;
+
+void Init_ossl_config(void);
+
+#endif /* _OSSL_CONFIG_H_ */
+
diff --git a/ext/openssl/ossl_digest.c b/ext/openssl/ossl_digest.c
new file mode 100644
index 0000000000..e04b145889
--- /dev/null
+++ b/ext/openssl/ossl_digest.c
@@ -0,0 +1,289 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#include "ossl.h"
+
+#define GetDigest(obj, ctx) do { \
+ Data_Get_Struct(obj, EVP_MD_CTX, ctx); \
+ if (!ctx) { \
+ ossl_raise(rb_eRuntimeError, "Digest CTX wasn't initialized!"); \
+ } \
+} while (0)
+#define SafeGetDigest(obj, ctx) do { \
+ OSSL_Check_Kind(obj, cDigest); \
+ GetDigest(obj, ctx); \
+} while (0)
+
+/*
+ * Classes
+ */
+VALUE mDigest;
+VALUE cDigest;
+VALUE eDigestError;
+
+/*
+ * Public
+ */
+const EVP_MD *
+GetDigestPtr(VALUE obj)
+{
+ EVP_MD_CTX *ctx;
+
+ SafeGetDigest(obj, ctx);
+
+ return EVP_MD_CTX_md(ctx); /*== ctx->digest*/
+}
+
+/*
+ * Private
+ */
+static VALUE
+ossl_digest_alloc(VALUE klass)
+{
+ EVP_MD_CTX *ctx;
+ VALUE obj;
+
+ ctx = EVP_MD_CTX_create();
+ if (ctx == NULL)
+ ossl_raise(rb_eRuntimeError, "EVP_MD_CTX_create() failed");
+ obj = Data_Wrap_Struct(klass, 0, EVP_MD_CTX_destroy, ctx);
+
+ return obj;
+}
+DEFINE_ALLOC_WRAPPER(ossl_digest_alloc)
+
+VALUE ossl_digest_update(VALUE, VALUE);
+
+static VALUE
+ossl_digest_initialize(int argc, VALUE *argv, VALUE self)
+{
+ EVP_MD_CTX *ctx;
+ const EVP_MD *md;
+ char *name;
+ VALUE type, data;
+
+ GetDigest(self, ctx);
+
+ rb_scan_args(argc, argv, "11", &type, &data);
+ name = StringValuePtr(type);
+ if (!NIL_P(data)) StringValue(data);
+
+ md = EVP_get_digestbyname(name);
+ if (!md) {
+ ossl_raise(rb_eRuntimeError, "Unsupported digest algorithm (%s).", name);
+ }
+ EVP_DigestInit(ctx, md);
+
+ if (!NIL_P(data)) return ossl_digest_update(self, data);
+ return self;
+}
+
+static VALUE
+ossl_digest_copy(VALUE self, VALUE other)
+{
+ EVP_MD_CTX *ctx1, *ctx2;
+
+ rb_check_frozen(self);
+ if (self == other) return self;
+
+ GetDigest(self, ctx1);
+ SafeGetDigest(other, ctx2);
+
+ if (!EVP_MD_CTX_copy(ctx1, ctx2)) {
+ ossl_raise(eDigestError, NULL);
+ }
+ return self;
+}
+
+static VALUE
+ossl_digest_reset(VALUE self)
+{
+ EVP_MD_CTX *ctx;
+
+ GetDigest(self, ctx);
+ EVP_DigestInit(ctx, EVP_MD_CTX_md(ctx));
+
+ return self;
+}
+
+VALUE
+ossl_digest_update(VALUE self, VALUE data)
+{
+ EVP_MD_CTX *ctx;
+
+ GetDigest(self, ctx);
+ StringValue(data);
+ EVP_DigestUpdate(ctx, RSTRING(data)->ptr, RSTRING(data)->len);
+
+ return self;
+}
+
+static void
+digest_final(EVP_MD_CTX *ctx, char **buf, int *buf_len)
+{
+ EVP_MD_CTX final;
+
+ if (!EVP_MD_CTX_copy(&final, ctx)) {
+ ossl_raise(eDigestError, NULL);
+ }
+ if (!(*buf = OPENSSL_malloc(EVP_MD_CTX_size(&final)))) {
+ ossl_raise(eDigestError, "Cannot allocate mem for digest");
+ }
+ EVP_DigestFinal(&final, *buf, buf_len);
+ EVP_MD_CTX_cleanup(&final);
+}
+
+static VALUE
+ossl_digest_digest(VALUE self)
+{
+ EVP_MD_CTX *ctx;
+ char *buf;
+ int buf_len;
+ VALUE digest;
+
+ GetDigest(self, ctx);
+ digest_final(ctx, &buf, &buf_len);
+ digest = rb_str_new(buf, buf_len);
+ OPENSSL_free(buf);
+
+ return digest;
+}
+
+static VALUE
+ossl_digest_hexdigest(VALUE self)
+{
+ EVP_MD_CTX *ctx;
+ char *buf, *hexbuf;
+ int buf_len;
+ VALUE hexdigest;
+
+ GetDigest(self, ctx);
+ digest_final(ctx, &buf, &buf_len);
+ if (string2hex(buf, buf_len, &hexbuf, NULL) != 2 * buf_len) {
+ OPENSSL_free(buf);
+ ossl_raise(eDigestError, "Memory alloc error");
+ }
+ hexdigest = rb_str_new(hexbuf, 2 * buf_len);
+ OPENSSL_free(buf);
+ OPENSSL_free(hexbuf);
+
+ return hexdigest;
+}
+
+static VALUE
+ossl_digest_s_digest(VALUE klass, VALUE str, VALUE data)
+{
+ VALUE obj =
+#if HAVE_RB_DEFINE_ALLOC_FUNC
+ rb_class_new_instance(1, &str, klass);
+#else
+ ossl_digest_alloc_wrapper(1, &str, klass);
+#endif
+
+ ossl_digest_update(obj, data);
+
+ return ossl_digest_digest(obj);
+}
+
+static VALUE
+ossl_digest_s_hexdigest(VALUE klass, VALUE str, VALUE data)
+{
+ VALUE obj =
+#if HAVE_RB_DEFINE_ALLOC_FUNC
+ rb_class_new_instance(1, &str, klass);
+#else
+ ossl_digest_alloc_wrapper(1, &str, klass);
+#endif
+
+ ossl_digest_update(obj, data);
+
+ return ossl_digest_hexdigest(obj);
+}
+
+static VALUE
+ossl_digest_equal(VALUE self, VALUE other)
+{
+ EVP_MD_CTX *ctx;
+ VALUE str1, str2;
+
+ GetDigest(self, ctx);
+ if (rb_obj_is_kind_of(other, cDigest) == Qtrue) {
+ str2 = ossl_digest_digest(other);
+ } else {
+ StringValue(other);
+ str2 = other;
+ }
+ if (RSTRING(str2)->len == EVP_MD_CTX_size(ctx)) {
+ str1 = ossl_digest_digest(self);
+ } else {
+ str1 = ossl_digest_hexdigest(self);
+ }
+ if (RSTRING(str1)->len == RSTRING(str2)->len
+ && rb_str_cmp(str1, str2) == 0) {
+ return Qtrue;
+ }
+
+ return Qfalse;
+}
+
+static VALUE
+ossl_digest_name(VALUE self)
+{
+ EVP_MD_CTX *ctx;
+
+ GetDigest(self, ctx);
+
+ return rb_str_new2(EVP_MD_name(EVP_MD_CTX_md(ctx)));
+}
+
+static VALUE
+ossl_digest_size(VALUE self)
+{
+ EVP_MD_CTX *ctx;
+
+ GetDigest(self, ctx);
+
+ return INT2NUM(EVP_MD_CTX_size(ctx));
+}
+
+/*
+ * INIT
+ */
+void
+Init_ossl_digest()
+{
+ mDigest = rb_define_module_under(mOSSL, "Digest");
+
+ eDigestError = rb_define_class_under(mDigest, "DigestError", eOSSLError);
+
+ cDigest = rb_define_class_under(mDigest, "Digest", rb_cObject);
+
+ rb_define_alloc_func(cDigest, ossl_digest_alloc);
+ rb_define_singleton_method(cDigest, "digest", ossl_digest_s_digest, 2);
+ rb_define_singleton_method(cDigest, "hexdigest", ossl_digest_s_hexdigest, 2);
+
+ rb_define_method(cDigest, "initialize", ossl_digest_initialize, -1);
+ rb_define_method(cDigest, "reset", ossl_digest_reset, 0);
+
+ rb_define_copy_func(cDigest, ossl_digest_copy);
+
+ rb_define_method(cDigest, "digest", ossl_digest_digest, 0);
+ rb_define_method(cDigest, "hexdigest", ossl_digest_hexdigest, 0);
+ rb_define_alias(cDigest, "inspect", "hexdigest");
+ rb_define_alias(cDigest, "to_s", "hexdigest");
+
+ rb_define_method(cDigest, "update", ossl_digest_update, 1);
+ rb_define_alias(cDigest, "<<", "update");
+
+ rb_define_method(cDigest, "==", ossl_digest_equal, 1);
+
+ rb_define_method(cDigest, "name", ossl_digest_name, 0);
+ rb_define_method(cDigest, "size", ossl_digest_size, 0);
+}
diff --git a/ext/openssl/ossl_digest.h b/ext/openssl/ossl_digest.h
new file mode 100644
index 0000000000..5aa1c42f05
--- /dev/null
+++ b/ext/openssl/ossl_digest.h
@@ -0,0 +1,22 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#if !defined(_OSSL_DIGEST_H_)
+#define _OSSL_DIGEST_H_
+
+extern VALUE mDigest;
+extern VALUE cDigest;
+extern VALUE eDigestError;
+
+const EVP_MD *GetDigestPtr(VALUE);
+void Init_ossl_digest(void);
+
+#endif /* _OSSL_DIGEST_H_ */
+
diff --git a/ext/openssl/ossl_hmac.c b/ext/openssl/ossl_hmac.c
new file mode 100644
index 0000000000..05b6e81202
--- /dev/null
+++ b/ext/openssl/ossl_hmac.c
@@ -0,0 +1,222 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#if !defined(OPENSSL_NO_HMAC)
+
+#include "ossl.h"
+
+#define MakeHMAC(obj, klass, ctx) \
+ obj = Data_Make_Struct(klass, HMAC_CTX, 0, ossl_hmac_free, ctx)
+#define GetHMAC(obj, ctx) do { \
+ Data_Get_Struct(obj, HMAC_CTX, ctx); \
+ if (!ctx) { \
+ ossl_raise(rb_eRuntimeError, "HMAC wasn't initialized"); \
+ } \
+} while (0)
+#define SafeGetHMAC(obj, ctx) do { \
+ OSSL_Check_Kind(obj, cHMAC); \
+ GetHMAC(obj, ctx); \
+} while (0)
+
+/*
+ * Classes
+ */
+VALUE cHMAC;
+VALUE eHMACError;
+
+/*
+ * Public
+ */
+
+/*
+ * Private
+ */
+static void
+ossl_hmac_free(HMAC_CTX *ctx)
+{
+ HMAC_CTX_cleanup(ctx);
+ free(ctx);
+}
+
+static VALUE
+ossl_hmac_alloc(VALUE klass)
+{
+ HMAC_CTX *ctx;
+ VALUE obj;
+
+ MakeHMAC(obj, klass, ctx);
+
+ return obj;
+}
+DEFINE_ALLOC_WRAPPER(ossl_hmac_alloc)
+
+static VALUE
+ossl_hmac_initialize(VALUE self, VALUE key, VALUE digest)
+{
+ HMAC_CTX *ctx;
+
+ GetHMAC(self, ctx);
+ StringValue(key);
+ HMAC_CTX_init(ctx);
+ HMAC_Init(ctx, RSTRING(key)->ptr, RSTRING(key)->len, GetDigestPtr(digest));
+
+ return self;
+}
+
+static VALUE
+ossl_hmac_copy(VALUE self, VALUE other)
+{
+ HMAC_CTX *ctx1, *ctx2;
+
+ rb_check_frozen(self);
+ if (self == other) return self;
+
+ GetHMAC(self, ctx1);
+ SafeGetHMAC(other, ctx2);
+
+ if (!HMAC_CTX_copy(ctx1, ctx2)) {
+ ossl_raise(eHMACError, NULL);
+ }
+ return self;
+}
+
+static VALUE
+ossl_hmac_update(VALUE self, VALUE data)
+{
+ HMAC_CTX *ctx;
+
+ GetHMAC(self, ctx);
+ StringValue(data);
+ HMAC_Update(ctx, RSTRING(data)->ptr, RSTRING(data)->len);
+
+ return self;
+}
+
+static void
+hmac_final(HMAC_CTX *ctx, char **buf, int *buf_len)
+{
+ HMAC_CTX final;
+
+ if (!HMAC_CTX_copy(&final, ctx)) {
+ ossl_raise(eHMACError, NULL);
+ }
+ if (!(*buf = OPENSSL_malloc(HMAC_size(&final)))) {
+ OSSL_Debug("Allocating %d mem", HMAC_size(&final));
+ ossl_raise(eHMACError, "Cannot allocate memory for hmac");
+ }
+ HMAC_Final(&final, *buf, buf_len);
+ HMAC_CTX_cleanup(&final);
+}
+
+static VALUE
+ossl_hmac_digest(VALUE self)
+{
+ HMAC_CTX *ctx;
+ char *buf;
+ int buf_len;
+ VALUE digest;
+
+ GetHMAC(self, ctx);
+ hmac_final(ctx, &buf, &buf_len);
+ digest = rb_str_new(buf, buf_len);
+ OPENSSL_free(buf);
+
+ return digest;
+}
+
+static VALUE
+ossl_hmac_hexdigest(VALUE self)
+{
+ HMAC_CTX *ctx;
+ char *buf, *hexbuf;
+ int buf_len;
+ VALUE hexdigest;
+
+ GetHMAC(self, ctx);
+ hmac_final(ctx, &buf, &buf_len);
+ if (string2hex(buf, buf_len, &hexbuf, NULL) != 2 * buf_len) {
+ OPENSSL_free(buf);
+ ossl_raise(eHMACError, "Memory alloc error");
+ }
+ hexdigest = rb_str_new(hexbuf, 2 * buf_len);
+ OPENSSL_free(buf);
+ OPENSSL_free(hexbuf);
+
+ return hexdigest;
+}
+
+static VALUE
+ossl_hmac_s_digest(VALUE klass, VALUE digest, VALUE key, VALUE data)
+{
+ char *buf;
+ int buf_len;
+
+ StringValue(key);
+ StringValue(data);
+ buf = HMAC(GetDigestPtr(digest), RSTRING(key)->ptr, RSTRING(key)->len,
+ RSTRING(data)->ptr, RSTRING(data)->len, NULL, &buf_len);
+
+ return rb_str_new(buf, buf_len);
+}
+
+static VALUE
+ossl_hmac_s_hexdigest(VALUE klass, VALUE digest, VALUE key, VALUE data)
+{
+ char *buf, *hexbuf;
+ int buf_len;
+ VALUE hexdigest;
+
+ StringValue(key);
+ StringValue(data);
+
+ buf = HMAC(GetDigestPtr(digest), RSTRING(key)->ptr, RSTRING(key)->len,
+ RSTRING(data)->ptr, RSTRING(data)->len, NULL, &buf_len);
+ if (string2hex(buf, buf_len, &hexbuf, NULL) != 2 * buf_len) {
+ ossl_raise(eHMACError, "Cannot convert buf to hexbuf");
+ }
+ hexdigest = rb_str_new(hexbuf, 2 * buf_len);
+ OPENSSL_free(hexbuf);
+
+ return hexdigest;
+}
+
+/*
+ * INIT
+ */
+void
+Init_ossl_hmac()
+{
+ eHMACError = rb_define_class_under(mOSSL, "HMACError", eOSSLError);
+
+ cHMAC = rb_define_class_under(mOSSL, "HMAC", rb_cObject);
+
+ rb_define_alloc_func(cHMAC, ossl_hmac_alloc);
+ rb_define_singleton_method(cHMAC, "digest", ossl_hmac_s_digest, 3);
+ rb_define_singleton_method(cHMAC, "hexdigest", ossl_hmac_s_hexdigest, 3);
+
+ rb_define_method(cHMAC, "initialize", ossl_hmac_initialize, 2);
+ rb_define_copy_func(cHMAC, ossl_hmac_copy);
+
+ rb_define_method(cHMAC, "update", ossl_hmac_update, 1);
+ rb_define_alias(cHMAC, "<<", "update");
+ rb_define_method(cHMAC, "digest", ossl_hmac_digest, 0);
+ rb_define_method(cHMAC, "hexdigest", ossl_hmac_hexdigest, 0);
+ rb_define_alias(cHMAC, "inspect", "hexdigest");
+ rb_define_alias(cHMAC, "to_s", "hexdigest");
+}
+
+#else /* NO_HMAC */
+# warning >>> OpenSSL is compiled without HMAC support <<<
+void
+Init_ossl_hmac()
+{
+ rb_warning("HMAC will NOT be avaible: OpenSSL is compiled without HMAC.");
+}
+#endif /* NO_HMAC */
diff --git a/ext/openssl/ossl_hmac.h b/ext/openssl/ossl_hmac.h
new file mode 100644
index 0000000000..1a2978b39a
--- /dev/null
+++ b/ext/openssl/ossl_hmac.h
@@ -0,0 +1,19 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#if !defined(_OSSL_HMAC_H_)
+#define _OSSL_HMAC_H_
+
+extern VALUE cHMAC;
+extern VALUE eHMACError;
+
+void Init_ossl_hmac(void);
+
+#endif /* _OSSL_HMAC_H_ */
diff --git a/ext/openssl/ossl_ns_spki.c b/ext/openssl/ossl_ns_spki.c
new file mode 100644
index 0000000000..bcefcbdfde
--- /dev/null
+++ b/ext/openssl/ossl_ns_spki.c
@@ -0,0 +1,232 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#include "ossl.h"
+
+#define WrapSPKI(klass, obj, spki) do { \
+ if (!spki) { \
+ ossl_raise(rb_eRuntimeError, "SPKI wasn't initialized!"); \
+ } \
+ obj = Data_Wrap_Struct(klass, 0, NETSCAPE_SPKI_free, spki); \
+} while (0)
+#define GetSPKI(obj, spki) do { \
+ Data_Get_Struct(obj, NETSCAPE_SPKI, spki); \
+ if (!spki) { \
+ ossl_raise(rb_eRuntimeError, "SPKI wasn't initialized!"); \
+ } \
+} while (0)
+
+/*
+ * Classes
+ */
+VALUE mNetscape;
+VALUE cSPKI;
+VALUE eSPKIError;
+
+/*
+ * Public functions
+ */
+
+/*
+ * Private functions
+ */
+static VALUE
+ossl_spki_alloc(VALUE klass)
+{
+ NETSCAPE_SPKI *spki;
+ VALUE obj;
+
+ if (!(spki = NETSCAPE_SPKI_new())) {
+ ossl_raise(eSPKIError, NULL);
+ }
+ WrapSPKI(klass, obj, spki);
+
+ return obj;
+}
+DEFINE_ALLOC_WRAPPER(ossl_spki_alloc)
+
+static VALUE
+ossl_spki_initialize(int argc, VALUE *argv, VALUE self)
+{
+ NETSCAPE_SPKI *spki;
+ VALUE buffer;
+
+ if (rb_scan_args(argc, argv, "01", &buffer) == 0) {
+ return self;
+ }
+ if (!(spki = NETSCAPE_SPKI_b64_decode(StringValuePtr(buffer), -1))) {
+ ossl_raise(eSPKIError, NULL);
+ }
+ NETSCAPE_SPKI_free(DATA_PTR(self));
+ DATA_PTR(self) = spki;
+
+ return self;
+}
+
+static VALUE
+ossl_spki_to_pem(VALUE self)
+{
+ NETSCAPE_SPKI *spki;
+ char *data;
+ VALUE str;
+
+ GetSPKI(self, spki);
+ if (!(data = NETSCAPE_SPKI_b64_encode(spki))) {
+ ossl_raise(eSPKIError, NULL);
+ }
+ str = rb_str_new2(data);
+ OPENSSL_free(data);
+
+ return str;
+}
+
+static VALUE
+ossl_spki_print(VALUE self)
+{
+ NETSCAPE_SPKI *spki;
+ BIO *out;
+ BUF_MEM *buf;
+ VALUE str;
+
+ GetSPKI(self, spki);
+ if (!(out = BIO_new(BIO_s_mem()))) {
+ ossl_raise(eSPKIError, NULL);
+ }
+ if (!NETSCAPE_SPKI_print(out, spki)) {
+ BIO_free(out);
+ ossl_raise(eSPKIError, NULL);
+ }
+ BIO_get_mem_ptr(out, &buf);
+ str = rb_str_new(buf->data, buf->length);
+ BIO_free(out);
+
+ return str;
+}
+
+static VALUE
+ossl_spki_get_public_key(VALUE self)
+{
+ NETSCAPE_SPKI *spki;
+ EVP_PKEY *pkey;
+
+ GetSPKI(self, spki);
+ if (!(pkey = NETSCAPE_SPKI_get_pubkey(spki))) { /* adds an reference */
+ ossl_raise(eSPKIError, NULL);
+ }
+
+ return ossl_pkey_new(pkey); /* NO DUP - OK */
+}
+
+static VALUE
+ossl_spki_set_public_key(VALUE self, VALUE key)
+{
+ NETSCAPE_SPKI *spki;
+
+ GetSPKI(self, spki);
+ if (!NETSCAPE_SPKI_set_pubkey(spki, GetPKeyPtr(key))) { /* NO NEED TO DUP */
+ ossl_raise(eSPKIError, NULL);
+ }
+
+ return key;
+}
+
+static VALUE
+ossl_spki_get_challenge(VALUE self)
+{
+ NETSCAPE_SPKI *spki;
+
+ GetSPKI(self, spki);
+ if (spki->spkac->challenge->length <= 0) {
+ OSSL_Debug("Challenge.length <= 0?");
+ return rb_str_new2(NULL);
+ }
+
+ return rb_str_new(spki->spkac->challenge->data,
+ spki->spkac->challenge->length);
+}
+
+static VALUE
+ossl_spki_set_challenge(VALUE self, VALUE str)
+{
+ NETSCAPE_SPKI *spki;
+
+ GetSPKI(self, spki);
+ StringValue(str);
+ if (!ASN1_STRING_set(spki->spkac->challenge, RSTRING(str)->ptr,
+ RSTRING(str)->len)) {
+ ossl_raise(eSPKIError, NULL);
+ }
+
+ return str;
+}
+
+static VALUE
+ossl_spki_sign(VALUE self, VALUE key, VALUE digest)
+{
+ NETSCAPE_SPKI *spki;
+ EVP_PKEY *pkey;
+ const EVP_MD *md;
+
+ GetSPKI(self, spki);
+ pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */
+ md = GetDigestPtr(digest);
+ if (!NETSCAPE_SPKI_sign(spki, pkey, md)) {
+ ossl_raise(eSPKIError, NULL);
+ }
+
+ return self;
+}
+
+/*
+ * Checks that cert signature is made with PRIVversion of this PUBLIC 'key'
+ */
+static VALUE
+ossl_spki_verify(VALUE self, VALUE key)
+{
+ NETSCAPE_SPKI *spki;
+
+ GetSPKI(self, spki);
+ switch (NETSCAPE_SPKI_verify(spki, GetPKeyPtr(key))) { /* NO NEED TO DUP */
+ case 0:
+ return Qfalse;
+ case 1:
+ return Qtrue;
+ default:
+ ossl_raise(eSPKIError, NULL);
+ }
+ return Qnil; /* dummy */
+}
+
+/*
+ * NETSCAPE_SPKI init
+ */
+void
+Init_ossl_ns_spki()
+{
+ mNetscape = rb_define_module_under(mOSSL, "Netscape");
+
+ eSPKIError = rb_define_class_under(mNetscape, "SPKIError", eOSSLError);
+
+ cSPKI = rb_define_class_under(mNetscape, "SPKI", rb_cObject);
+
+ rb_define_alloc_func(cSPKI, ossl_spki_alloc);
+ rb_define_method(cSPKI, "initialize", ossl_spki_initialize, -1);
+
+ rb_define_method(cSPKI, "to_pem", ossl_spki_to_pem, 0);
+ rb_define_alias(cSPKI, "to_s", "to_pem");
+ rb_define_method(cSPKI, "to_text", ossl_spki_print, 0);
+ rb_define_method(cSPKI, "public_key", ossl_spki_get_public_key, 0);
+ rb_define_method(cSPKI, "public_key=", ossl_spki_set_public_key, 1);
+ rb_define_method(cSPKI, "sign", ossl_spki_sign, 2);
+ rb_define_method(cSPKI, "verify", ossl_spki_verify, 1);
+ rb_define_method(cSPKI, "challenge", ossl_spki_get_challenge, 0);
+ rb_define_method(cSPKI, "challenge=", ossl_spki_set_challenge, 1);
+}
+
diff --git a/ext/openssl/ossl_ns_spki.h b/ext/openssl/ossl_ns_spki.h
new file mode 100644
index 0000000000..9977035a9c
--- /dev/null
+++ b/ext/openssl/ossl_ns_spki.h
@@ -0,0 +1,21 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#if !defined(_OSSL_NS_SPKI_H_)
+#define _OSSL_NS_SPKI_H_
+
+extern VALUE mNetscape;
+extern VALUE cSPKI;
+extern VALUE eSPKIError;
+
+void Init_ossl_ns_spki(void);
+
+#endif /* _OSSL_NS_SPKI_H_ */
+
diff --git a/ext/openssl/ossl_ocsp.c b/ext/openssl/ossl_ocsp.c
new file mode 100644
index 0000000000..3ec5c39472
--- /dev/null
+++ b/ext/openssl/ossl_ocsp.c
@@ -0,0 +1,765 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2003 Michal Rokos <m.rokos@sh.cvut.cz>
+ * Copyright (C) 2003 GOTOU Yuuzou <gotoyuzo@notwork.org>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#include "ossl.h"
+
+#if defined(OSSL_OCSP_ENABLED)
+
+#define WrapOCSPReq(klass, obj, req) do { \
+ if(!req) ossl_raise(rb_eRuntimeError, "Request wasn't initialized!"); \
+ obj = Data_Wrap_Struct(klass, 0, OCSP_REQUEST_free, req); \
+} while (0)
+#define GetOCSPReq(obj, req) do { \
+ Data_Get_Struct(obj, OCSP_REQUEST, req); \
+ if(!req) ossl_raise(rb_eRuntimeError, "Request wasn't initialized!"); \
+} while (0)
+#define SafeGetOCSPReq(obj, req) do { \
+ OSSL_Check_Kind(obj, cOCSPReq); \
+ GetOCSPReq(obj, req); \
+} while (0)
+
+#define WrapOCSPRes(klass, obj, res) do { \
+ if(!res) ossl_raise(rb_eRuntimeError, "Response wasn't initialized!"); \
+ obj = Data_Wrap_Struct(klass, 0, OCSP_RESPONSE_free, res); \
+} while (0)
+#define GetOCSPRes(obj, res) do { \
+ Data_Get_Struct(obj, OCSP_RESPONSE, res); \
+ if(!res) ossl_raise(rb_eRuntimeError, "Response wasn't initialized!"); \
+} while (0)
+#define SafeGetOCSPRes(obj, res) do { \
+ OSSL_Check_Kind(obj, cOCSPRes); \
+ GetOCSPRes(obj, res); \
+} while (0)
+
+#define WrapOCSPBasicRes(klass, obj, res) do { \
+ if(!res) ossl_raise(rb_eRuntimeError, "Response wasn't initialized!"); \
+ obj = Data_Wrap_Struct(klass, 0, OCSP_BASICRESP_free, res); \
+} while (0)
+#define GetOCSPBasicRes(obj, res) do { \
+ Data_Get_Struct(obj, OCSP_BASICRESP, res); \
+ if(!res) ossl_raise(rb_eRuntimeError, "Response wasn't initialized!"); \
+} while (0)
+#define SafeGetOCSPBasicRes(obj, res) do { \
+ OSSL_Check_Kind(obj, cOCSPBasicRes); \
+ GetOCSPBasicRes(obj, res); \
+} while (0)
+
+#define WrapOCSPCertId(klass, obj, cid) do { \
+ if(!cid) ossl_raise(rb_eRuntimeError, "Cert ID wasn't initialized!"); \
+ obj = Data_Wrap_Struct(klass, 0, OCSP_CERTID_free, cid); \
+} while (0)
+#define GetOCSPCertId(obj, cid) do { \
+ Data_Get_Struct(obj, OCSP_CERTID, cid); \
+ if(!cid) ossl_raise(rb_eRuntimeError, "Cert ID wasn't initialized!"); \
+} while (0)
+#define SafeGetOCSPCertId(obj, cid) do { \
+ OSSL_Check_Kind(obj, cOCSPCertId); \
+ GetOCSPCertId(obj, cid); \
+} while (0)
+
+VALUE mOCSP;
+VALUE eOCSPError;
+VALUE cOCSPReq;
+VALUE cOCSPRes;
+VALUE cOCSPBasicRes;
+VALUE cOCSPCertId;
+
+/*
+ * Public
+ */
+static VALUE
+ossl_ocspcertid_new(OCSP_CERTID *cid)
+{
+ VALUE obj;
+ WrapOCSPCertId(cOCSPCertId, obj, cid);
+ return obj;
+}
+
+/*
+ * OCSP::Resquest
+ */
+static VALUE
+ossl_ocspreq_alloc(VALUE klass)
+{
+ OCSP_REQUEST *req;
+ VALUE obj;
+
+ if (!(req = OCSP_REQUEST_new()))
+ ossl_raise(eOCSPError, NULL);
+ WrapOCSPReq(klass, obj, req);
+
+ return obj;
+}
+DEFINE_ALLOC_WRAPPER(ossl_ocspreq_alloc)
+
+static VALUE
+ossl_ocspreq_initialize(int argc, VALUE *argv, VALUE self)
+{
+ VALUE arg;
+ BIO *bio;
+
+ rb_scan_args(argc, argv, "01", &arg);
+ if(!NIL_P(arg)){
+ bio = ossl_obj2bio(arg);
+ if(!d2i_OCSP_REQUEST_bio(bio, (OCSP_REQUEST**)&DATA_PTR(self))){
+ BIO_free(bio);
+ ossl_raise(eOCSPError, "cannot load DER encoded request");
+ }
+ BIO_free(bio);
+ }
+
+ return self;
+}
+
+static VALUE
+ossl_ocspreq_add_nonce(int argc, VALUE *argv, VALUE self)
+{
+ OCSP_REQUEST *req;
+ VALUE val;
+ int ret;
+
+ rb_scan_args(argc, argv, "01", &val);
+ GetOCSPReq(self, req);
+ if(NIL_P(val))
+ ret = OCSP_request_add1_nonce(req, NULL, -1);
+ else{
+ StringValue(val);
+ ret = OCSP_request_add1_nonce(req, RSTRING(val)->ptr, RSTRING(val)->len);
+ }
+ if(!ret) ossl_raise(eOCSPError, NULL);
+
+ return self;
+}
+
+/* Check nonce validity in a request and response.
+ * Return value reflects result:
+ * 1: nonces present and equal.
+ * 2: nonces both absent.
+ * 3: nonce present in response only.
+ * 0: nonces both present and not equal.
+ * -1: nonce in request only.
+ *
+ * For most responders clients can check return > 0.
+ * If responder doesn't handle nonces return != 0 may be
+ * necessary. return == 0 is always an error.
+ */
+static VALUE
+ossl_ocspreq_check_nonce(VALUE self, VALUE basic_resp)
+{
+ OCSP_REQUEST *req;
+ OCSP_BASICRESP *bs;
+ int res;
+
+ GetOCSPReq(self, req);
+ SafeGetOCSPBasicRes(basic_resp, bs);
+ res = OCSP_check_nonce(req, bs);
+
+ return INT2NUM(res);
+}
+
+static VALUE
+ossl_ocspreq_add_certid(VALUE self, VALUE certid)
+{
+ OCSP_REQUEST *req;
+ OCSP_CERTID *id;
+
+ GetOCSPReq(self, req);
+ GetOCSPCertId(certid, id);
+ if(!OCSP_request_add0_id(req, OCSP_CERTID_dup(id)))
+ ossl_raise(eOCSPError, NULL);
+
+ return self;
+}
+
+static VALUE
+ossl_ocspreq_get_certid(VALUE self)
+{
+ OCSP_REQUEST *req;
+ OCSP_ONEREQ *one;
+ OCSP_CERTID *id;
+ VALUE ary, tmp;
+ int i, count;
+
+ GetOCSPReq(self, req);
+ count = OCSP_request_onereq_count(req);
+ ary = (count > 0) ? rb_ary_new() : Qnil;
+ for(i = 0; i < count; i++){
+ one = OCSP_request_onereq_get0(req, i);
+ if(!(id = OCSP_CERTID_dup(OCSP_onereq_get0_id(one))))
+ ossl_raise(eOCSPError, NULL);
+ WrapOCSPCertId(cOCSPCertId, tmp, id);
+ rb_ary_push(ary, tmp);
+ }
+
+ return ary;
+}
+
+static VALUE
+ossl_ocspreq_sign(int argc, VALUE *argv, VALUE self)
+{
+ VALUE signer_cert, signer_key, certs, flags;
+ OCSP_REQUEST *req;
+ X509 *signer;
+ EVP_PKEY *key;
+ STACK_OF(X509) *x509s;
+ unsigned long flg;
+ int ret, status = 0;
+
+ rb_scan_args(argc, argv, "22", &signer_cert, &signer_key, &certs, &flags);
+ GetOCSPReq(self, req);
+ signer = GetX509CertPtr(signer_cert);
+ key = GetPrivPKeyPtr(signer_key);
+ flg = NIL_P(flags) ? 0 : NUM2INT(flags);
+ if(NIL_P(certs)){
+ x509s = sk_X509_new_null();
+ flags |= OCSP_NOCERTS;
+ }
+ else x509s = ossl_protect_x509_ary2sk(certs, &status);
+ if(status){
+ sk_X509_pop_free(x509s, X509_free);
+ rb_jump_tag(status);
+ }
+ ret = OCSP_request_sign(req, signer, key, EVP_sha1(), x509s, flg);
+ sk_X509_pop_free(x509s, X509_free);
+ if(!ret) ossl_raise(eOCSPError, NULL);
+
+ return self;
+}
+
+static VALUE
+ossl_ocspreq_verify(int argc, VALUE *argv, VALUE self)
+{
+ VALUE certs, store, flags;
+ OCSP_REQUEST *req;
+ STACK_OF(X509) *x509s;
+ X509_STORE *x509st;
+ int flg, result;
+
+ rb_scan_args(argc, argv, "21", &certs, &store, &flags);
+ GetOCSPReq(self, req);
+ x509st = GetX509StorePtr(store);
+ flg = NIL_P(flags) ? 0 : INT2NUM(flags);
+ x509s = ossl_x509_ary2sk(certs);
+ result = OCSP_request_verify(req, x509s, x509st, flg);
+ sk_X509_pop_free(x509s, X509_free);
+ if(!result) rb_warn("%s", ERR_error_string(ERR_peek_error(), NULL));
+
+ return result ? Qtrue : Qfalse;
+}
+
+static VALUE
+ossl_ocspreq_to_der(VALUE self)
+{
+ OCSP_REQUEST *req;
+ BIO *bio;
+ VALUE str;
+ int status = 0;
+
+ GetOCSPReq(self, req);
+ if(!(bio = BIO_new(BIO_s_mem()))) rb_raise(eOCSPError, NULL);
+ i2d_OCSP_REQUEST_bio(bio, req);
+ str = ossl_protect_membio2str(bio, &status);
+ BIO_free(bio);
+ if(status) rb_jump_tag(status);
+
+ return str;
+}
+
+/*
+ * OCSP::Response
+ */
+static VALUE
+ossl_ocspres_s_create(VALUE klass, VALUE status, VALUE basic_resp)
+{
+ OCSP_BASICRESP *bs;
+ OCSP_RESPONSE *res;
+ VALUE obj;
+
+ if(NIL_P(basic_resp)) bs = NULL;
+ else GetOCSPBasicRes(basic_resp, bs); /* NO NEED TO DUP */
+ if(!(res = OCSP_response_create(NUM2INT(status), bs)))
+ ossl_raise(eOCSPError, NULL);
+ WrapOCSPRes(klass, obj, res);
+
+ return obj;
+}
+
+static VALUE
+ossl_ocspres_alloc(VALUE klass)
+{
+ OCSP_RESPONSE *res;
+ VALUE obj;
+
+ if(!(res = OCSP_RESPONSE_new()))
+ ossl_raise(eOCSPError, NULL);
+ WrapOCSPRes(klass, obj, res);
+
+ return obj;
+}
+DEFINE_ALLOC_WRAPPER(ossl_ocspreq_alloc)
+
+static VALUE
+ossl_ocspres_initialize(int argc, VALUE *argv, VALUE self)
+{
+ VALUE arg;
+ BIO *bio;
+
+ rb_scan_args(argc, argv, "01", &arg);
+ bio = ossl_obj2bio(arg);
+ if(!d2i_OCSP_RESPONSE_bio(bio, (OCSP_RESPONSE**)&DATA_PTR(self))){
+ BIO_free(bio);
+ ossl_raise(eOCSPError, "cannot load DER encoded response");
+ }
+ BIO_free(bio);
+
+ return self;
+}
+
+static VALUE
+ossl_ocspres_status(VALUE self)
+{
+ OCSP_RESPONSE *res;
+ int st;
+
+ GetOCSPRes(self, res);
+ st = OCSP_response_status(res);
+
+ return INT2NUM(st);
+}
+
+static VALUE
+ossl_ocspres_status_string(VALUE self)
+{
+ OCSP_RESPONSE *res;
+ int st;
+
+ GetOCSPRes(self, res);
+ st = OCSP_response_status(res);
+
+ return rb_str_new2(OCSP_response_status_str(st));
+}
+
+static VALUE
+ossl_ocspres_get_basic(VALUE self)
+{
+ OCSP_RESPONSE *res;
+ OCSP_BASICRESP *bs;
+ VALUE ret;
+
+ GetOCSPRes(self, res);
+ if(!(bs = OCSP_response_get1_basic(res)))
+ return Qnil;
+ WrapOCSPBasicRes(cOCSPBasicRes, ret, bs);
+
+ return ret;
+}
+
+static VALUE
+ossl_ocspres_to_der(VALUE self)
+{
+ OCSP_RESPONSE *res;
+ BIO *bio;
+ VALUE str;
+ int status = 0;
+
+ GetOCSPRes(self, res);
+ if(!(bio = BIO_new(BIO_s_mem()))) rb_raise(eOCSPError, NULL);
+ i2d_OCSP_RESPONSE_bio(bio, res);
+ str = ossl_protect_membio2str(bio, &status);
+ BIO_free(bio);
+ if(status) rb_jump_tag(status);
+
+ return str;
+}
+
+/*
+ * OCSP::BasicResponse
+ */
+static VALUE
+ossl_ocspbres_alloc(VALUE klass)
+{
+ OCSP_BASICRESP *bs;
+ VALUE obj;
+
+ if(!(bs = OCSP_BASICRESP_new()))
+ ossl_raise(eOCSPError, NULL);
+ WrapOCSPBasicRes(klass, obj, bs);
+
+ return obj;
+}
+DEFINE_ALLOC_WRAPPER(ossl_ocspbres_alloc)
+
+static VALUE
+ossl_ocspbres_initialize(int argc, VALUE *argv, VALUE self)
+{
+ return self;
+}
+
+static VALUE
+ossl_ocspbres_copy_nonce(VALUE self, VALUE request)
+{
+ OCSP_BASICRESP *bs;
+ OCSP_REQUEST *req;
+ int ret;
+
+ GetOCSPBasicRes(self, bs);
+ SafeGetOCSPReq(request, req);
+ ret = OCSP_copy_nonce(bs, req);
+
+ return INT2NUM(ret);
+}
+
+static VALUE
+ossl_ocspbres_add_nonce(int argc, VALUE *argv, VALUE self)
+{
+ OCSP_BASICRESP *bs;
+ VALUE val;
+ int ret;
+
+ GetOCSPBasicRes(self, bs);
+ rb_scan_args(argc, argv, "01", &val);
+ if(NIL_P(val))
+ ret = OCSP_basic_add1_nonce(bs, NULL, -1);
+ else{
+ StringValue(val);
+ ret = OCSP_basic_add1_nonce(bs, RSTRING(val)->ptr, RSTRING(val)->len);
+ }
+ if(!ret) ossl_raise(eOCSPError, NULL);
+
+ return self;
+}
+
+static VALUE
+ossl_ocspbres_add_status(VALUE self, VALUE cid, VALUE status,
+ VALUE reason, VALUE revtime,
+ VALUE thisupd, VALUE nextupd, VALUE ext)
+{
+ OCSP_BASICRESP *bs;
+ OCSP_SINGLERESP *single;
+ OCSP_CERTID *id;
+ int st, rsn;
+ ASN1_TIME *ths, *nxt, *rev;
+ int error, i, rstatus = 0;
+ VALUE tmp;
+
+ GetOCSPBasicRes(self, bs);
+ SafeGetOCSPCertId(cid, id);
+ st = NUM2INT(status);
+ rsn = NIL_P(status) ? 0 : NUM2INT(reason);
+ if(!NIL_P(ext)){
+ /* All ary's members should be X509Extension */
+ Check_Type(ext, T_ARRAY);
+ for (i = 0; i < RARRAY(ext)->len; i++)
+ OSSL_Check_Kind(RARRAY(ext)->ptr[i], cX509Ext);
+ }
+
+ error = 0;
+ ths = nxt = rev = NULL;
+ if(!NIL_P(revtime)){
+ tmp = rb_protect(rb_Integer, revtime, &rstatus);
+ if(rstatus) goto err;
+ rev = X509_gmtime_adj(NULL, NUM2INT(tmp));
+ }
+ tmp = rb_protect(rb_Integer, thisupd, &rstatus);
+ if(rstatus) goto err;
+ ths = X509_gmtime_adj(NULL, NUM2INT(tmp));
+ tmp = rb_protect(rb_Integer, nextupd, &rstatus);
+ if(rstatus) goto err;
+ nxt = X509_gmtime_adj(NULL, NUM2INT(tmp));
+
+ if(!(single = OCSP_basic_add1_status(bs, id, st, rsn, rev, ths, nxt))){
+ error = 1;
+ goto err;
+ }
+
+ if(!NIL_P(ext)){
+ X509_EXTENSION *x509ext;
+ sk_X509_EXTENSION_pop_free(single->singleExtensions, X509_EXTENSION_free);
+ single->singleExtensions = NULL;
+ for(i = 0; i < RARRAY(ext)->len; i++){
+ x509ext = DupX509ExtPtr(RARRAY(ext)->ptr[i]);
+ if(!OCSP_SINGLERESP_add_ext(single, x509ext, -1)){
+ X509_EXTENSION_free(x509ext);
+ error = 1;
+ goto err;
+ }
+ X509_EXTENSION_free(x509ext);
+ }
+ }
+
+ err:
+ ASN1_TIME_free(ths);
+ ASN1_TIME_free(nxt);
+ ASN1_TIME_free(rev);
+ if(error) ossl_raise(eOCSPError, NULL);
+ if(rstatus) rb_jump_tag(rstatus);
+
+ return self;
+}
+
+static VALUE
+ossl_ocspbres_get_status(VALUE self)
+{
+ OCSP_BASICRESP *bs;
+ OCSP_SINGLERESP *single;
+ OCSP_CERTID *cid;
+ ASN1_TIME *revtime, *thisupd, *nextupd;
+ int status, reason;
+ X509_EXTENSION *x509ext;
+ VALUE ret, ary, ext;
+ int count, ext_count, i, j;
+
+ GetOCSPBasicRes(self, bs);
+ ret = rb_ary_new();
+ count = OCSP_resp_count(bs);
+ for(i = 0; i < count; i++){
+ single = OCSP_resp_get0(bs, i);
+ if(!single) continue;
+
+ revtime = thisupd = nextupd = NULL;
+ status = OCSP_single_get0_status(single, &reason, &revtime,
+ &thisupd, &nextupd);
+ if(status < 0) continue;
+ if(!(cid = OCSP_CERTID_dup(single->certId)))
+ ossl_raise(eOCSPError, NULL);
+ ary = rb_ary_new();
+ rb_ary_push(ary, ossl_ocspcertid_new(cid));
+ rb_ary_push(ary, INT2NUM(status));
+ rb_ary_push(ary, INT2NUM(reason));
+ rb_ary_push(ary, revtime ? asn1time_to_time(revtime) : Qnil);
+ rb_ary_push(ary, thisupd ? asn1time_to_time(thisupd) : Qnil);
+ rb_ary_push(ary, nextupd ? asn1time_to_time(nextupd) : Qnil);
+ ext = rb_ary_new();
+ ext_count = OCSP_SINGLERESP_get_ext_count(single);
+ for(j = 0; j < ext_count; j++){
+ x509ext = OCSP_SINGLERESP_get_ext(single, j);
+ rb_ary_push(ext, ossl_x509ext_new(x509ext));
+ }
+ rb_ary_push(ary, ext);
+ rb_ary_push(ret, ary);
+ }
+
+ return ret;
+}
+
+static VALUE
+ossl_ocspbres_sign(int argc, VALUE *argv, VALUE self)
+{
+ VALUE signer_cert, signer_key, certs, flags;
+ OCSP_BASICRESP *bs;
+ X509 *signer;
+ EVP_PKEY *key;
+ STACK_OF(X509) *x509s;
+ unsigned long flg;
+ int ret, status = 0;
+
+ rb_scan_args(argc, argv, "22", &signer_cert, &signer_key, &certs, &flags);
+ GetOCSPBasicRes(self, bs);
+ signer = GetX509CertPtr(signer_cert);
+ key = GetPrivPKeyPtr(signer_key);
+ flg = NIL_P(flags) ? 0 : NUM2INT(flags);
+ if(NIL_P(certs)){
+ x509s = sk_X509_new_null();
+ flg |= OCSP_NOCERTS;
+ }
+ else{
+ x509s = ossl_protect_x509_ary2sk(certs, &status);
+ if(status) rb_jump_tag(status);
+ }
+ ret = OCSP_basic_sign(bs, signer, key, EVP_sha1(), x509s, flg);
+ sk_X509_pop_free(x509s, X509_free);
+ if(!ret) ossl_raise(eOCSPError, NULL);
+
+ return self;
+}
+
+static VALUE
+ossl_ocspbres_verify(int argc, VALUE *argv, VALUE self)
+{
+ VALUE certs, store, flags;
+ OCSP_BASICRESP *bs;
+ STACK_OF(X509) *x509s;
+ X509_STORE *x509st;
+ int flg, result;
+
+ rb_scan_args(argc, argv, "21", &certs, &store, &flags);
+ GetOCSPBasicRes(self, bs);
+ x509st = GetX509StorePtr(store);
+ flg = NIL_P(flags) ? 0 : INT2NUM(flags);
+ x509s = ossl_x509_ary2sk(certs);
+ result = OCSP_basic_verify(bs, x509s, x509st, flg);
+ sk_X509_pop_free(x509s, X509_free);
+ if(!result) rb_warn("%s", ERR_error_string(ERR_peek_error(), NULL));
+
+ return result ? Qtrue : Qfalse;
+}
+
+/*
+ * OCSP::CertificateId
+ */
+static VALUE
+ossl_ocspcid_alloc(VALUE klass)
+{
+ OCSP_CERTID *id;
+ VALUE obj;
+
+ if(!(id = OCSP_CERTID_new()))
+ ossl_raise(eOCSPError, NULL);
+ WrapOCSPCertId(klass, obj, id);
+
+ return obj;
+}
+DEFINE_ALLOC_WRAPPER(ossl_ocspcid_alloc)
+
+static VALUE
+ossl_ocspcid_initialize(VALUE self, VALUE subject, VALUE issuer)
+{
+ OCSP_CERTID *id, *newid;
+ X509 *x509s, *x509i;
+
+ GetOCSPCertId(self, id);
+ x509s = GetX509CertPtr(subject); /* NO NEED TO DUP */
+ x509i = GetX509CertPtr(issuer); /* NO NEED TO DUP */
+ if(!(newid = OCSP_cert_to_id(NULL, x509s, x509i)))
+ ossl_raise(eOCSPError, NULL);
+ OCSP_CERTID_free(id);
+ RDATA(self)->data = newid;
+
+ return self;
+}
+
+static VALUE
+ossl_ocspcid_cmp(VALUE self, VALUE other)
+{
+ OCSP_CERTID *id, *id2;
+ int result;
+
+ GetOCSPCertId(self, id);
+ SafeGetOCSPCertId(other, id2);
+ result = OCSP_id_cmp(id, id2);
+
+ return (result == 0) ? Qtrue : Qfalse;
+}
+
+static VALUE
+ossl_ocspcid_cmp_issuer(VALUE self, VALUE other)
+{
+ OCSP_CERTID *id, *id2;
+ int result;
+
+ GetOCSPCertId(self, id);
+ SafeGetOCSPCertId(other, id2);
+ result = OCSP_id_issuer_cmp(id, id2);
+
+ return (result == 0) ? Qtrue : Qfalse;
+}
+
+static VALUE
+ossl_ocspcid_get_serial(VALUE self)
+{
+ OCSP_CERTID *id;
+
+ GetOCSPCertId(self, id);
+
+ return asn1integer_to_num(id->serialNumber);
+}
+
+void
+Init_ossl_ocsp()
+{
+ mOCSP = rb_define_module_under(mOSSL, "OCSP");
+
+ eOCSPError = rb_define_class_under(mOCSP, "OCSPError", rb_cObject);
+
+ cOCSPReq = rb_define_class_under(mOCSP, "Request", rb_cObject);
+ rb_define_alloc_func(cOCSPReq, ossl_ocspreq_alloc);
+ rb_define_method(cOCSPReq, "initialize", ossl_ocspreq_initialize, -1);
+ rb_define_method(cOCSPReq, "add_nonce", ossl_ocspreq_add_nonce, -1);
+ rb_define_method(cOCSPReq, "check_nonce", ossl_ocspreq_check_nonce, 1);
+ rb_define_method(cOCSPReq, "add_certid", ossl_ocspreq_add_certid, 1);
+ rb_define_method(cOCSPReq, "certid", ossl_ocspreq_get_certid, 0);
+ rb_define_method(cOCSPReq, "sign", ossl_ocspreq_sign, -1);
+ rb_define_method(cOCSPReq, "verify", ossl_ocspreq_verify, -1);
+ rb_define_method(cOCSPReq, "to_der", ossl_ocspreq_to_der, 0);
+
+ cOCSPRes = rb_define_class_under(mOCSP, "Response", rb_cObject);
+ rb_define_singleton_method(cOCSPRes, "create", ossl_ocspres_s_create, 2);
+ rb_define_alloc_func(cOCSPRes, ossl_ocspres_alloc);
+ rb_define_method(cOCSPRes, "initialize", ossl_ocspres_initialize, -1);
+ rb_define_method(cOCSPRes, "status", ossl_ocspres_status, 0);
+ rb_define_method(cOCSPRes, "status_string", ossl_ocspres_status_string, 0);
+ rb_define_method(cOCSPRes, "basic", ossl_ocspres_get_basic, 0);
+ rb_define_method(cOCSPRes, "to_der", ossl_ocspres_to_der, 0);
+
+ cOCSPBasicRes = rb_define_class_under(mOCSP, "BasicResponse", rb_cObject);
+ rb_define_alloc_func(cOCSPBasicRes, ossl_ocspbres_alloc);
+ rb_define_method(cOCSPBasicRes, "initialize", ossl_ocspbres_initialize, -1);
+ rb_define_method(cOCSPBasicRes, "copy_nonce", ossl_ocspbres_copy_nonce, 1);
+ rb_define_method(cOCSPBasicRes, "add_nonce", ossl_ocspbres_add_nonce, -1);
+ rb_define_method(cOCSPBasicRes, "add_status", ossl_ocspbres_add_status, 7);
+ rb_define_method(cOCSPBasicRes, "status", ossl_ocspbres_get_status, 0);
+ rb_define_method(cOCSPBasicRes, "sign", ossl_ocspbres_sign, -1);
+ rb_define_method(cOCSPBasicRes, "verify", ossl_ocspbres_verify, -1);
+
+ cOCSPCertId = rb_define_class_under(mOCSP, "CertificateId", rb_cObject);
+ rb_define_alloc_func(cOCSPCertId, ossl_ocspcid_alloc);
+ rb_define_method(cOCSPCertId, "initialize", ossl_ocspcid_initialize, 2);
+ rb_define_method(cOCSPCertId, "cmp", ossl_ocspcid_cmp, 1);
+ rb_define_method(cOCSPCertId, "cmp_issuer", ossl_ocspcid_cmp_issuer, 1);
+ rb_define_method(cOCSPCertId, "serial", ossl_ocspcid_get_serial, 0);
+
+#define DefOCSPConst(x) rb_define_const(mOCSP, #x, INT2NUM(OCSP_##x))
+
+ DefOCSPConst(RESPONSE_STATUS_SUCCESSFUL);
+ DefOCSPConst(RESPONSE_STATUS_MALFORMEDREQUEST);
+ DefOCSPConst(RESPONSE_STATUS_INTERNALERROR);
+ DefOCSPConst(RESPONSE_STATUS_TRYLATER);
+ DefOCSPConst(RESPONSE_STATUS_SIGREQUIRED);
+ DefOCSPConst(RESPONSE_STATUS_UNAUTHORIZED);
+
+ DefOCSPConst(REVOKED_STATUS_NOSTATUS);
+ DefOCSPConst(REVOKED_STATUS_UNSPECIFIED);
+ DefOCSPConst(REVOKED_STATUS_KEYCOMPROMISE);
+ DefOCSPConst(REVOKED_STATUS_CACOMPROMISE);
+ DefOCSPConst(REVOKED_STATUS_AFFILIATIONCHANGED);
+ DefOCSPConst(REVOKED_STATUS_SUPERSEDED);
+ DefOCSPConst(REVOKED_STATUS_CESSATIONOFOPERATION);
+ DefOCSPConst(REVOKED_STATUS_CERTIFICATEHOLD);
+ DefOCSPConst(REVOKED_STATUS_REMOVEFROMCRL);
+
+ DefOCSPConst(NOCERTS);
+ DefOCSPConst(NOINTERN);
+ DefOCSPConst(NOSIGS);
+ DefOCSPConst(NOCHAIN);
+ DefOCSPConst(NOVERIFY);
+ DefOCSPConst(NOEXPLICIT);
+ DefOCSPConst(NOCASIGN);
+ DefOCSPConst(NODELEGATED);
+ DefOCSPConst(NOCHECKS);
+ DefOCSPConst(TRUSTOTHER);
+ DefOCSPConst(RESPID_KEY);
+ DefOCSPConst(NOTIME);
+
+#define DefOCSPVConst(x) rb_define_const(mOCSP, "V_" #x, INT2NUM(V_OCSP_##x))
+
+ DefOCSPVConst(CERTSTATUS_GOOD);
+ DefOCSPVConst(CERTSTATUS_REVOKED);
+ DefOCSPVConst(CERTSTATUS_UNKNOWN);
+ DefOCSPVConst(RESPID_NAME);
+ DefOCSPVConst(RESPID_KEY);
+}
+
+#else /* ! OSSL_OCSP_ENABLED */
+void
+Init_ossl_ocsp()
+{
+}
+#endif
diff --git a/ext/openssl/ossl_ocsp.h b/ext/openssl/ossl_ocsp.h
new file mode 100644
index 0000000000..65b4f2e23f
--- /dev/null
+++ b/ext/openssl/ossl_ocsp.h
@@ -0,0 +1,24 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2003 Michal Rokos <m.rokos@sh.cvut.cz>
+ * Copyright (C) 2003 GOTOU Yuuzou <gotoyuzo@notwork.org>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#if !defined(_OSSL_OCSP_H_)
+#define _OSSL_OCSP_H_
+
+#if defined(OSSL_OCSP_ENABLED)
+extern VALUE mOCSP;
+extern VALUE cOPCSReq;
+extern VALUE cOPCSRes;
+extern VALUE cOPCSBasicRes;
+#endif
+
+void Init_ossl_ocsp(void);
+
+#endif /* _OSSL_OCSP_H_ */
diff --git a/ext/openssl/ossl_pkcs7.c b/ext/openssl/ossl_pkcs7.c
new file mode 100644
index 0000000000..c612eb43ff
--- /dev/null
+++ b/ext/openssl/ossl_pkcs7.c
@@ -0,0 +1,775 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#include "ossl.h"
+
+#define WrapPKCS7(klass, obj, pkcs7) do { \
+ if (!pkcs7) { \
+ ossl_raise(rb_eRuntimeError, "PKCS7 wasn't initialized."); \
+ } \
+ obj = Data_Wrap_Struct(klass, 0, PKCS7_free, pkcs7); \
+} while (0)
+#define GetPKCS7(obj, pkcs7) do { \
+ Data_Get_Struct(obj, PKCS7, pkcs7); \
+ if (!pkcs7) { \
+ ossl_raise(rb_eRuntimeError, "PKCS7 wasn't initialized."); \
+ } \
+} while (0)
+#define SafeGetPKCS7(obj, pkcs7) do { \
+ OSSL_Check_Kind(obj, cPKCS7); \
+ GetPKCS7(obj, pkcs7); \
+} while (0)
+
+#define WrapPKCS7si(klass, obj, p7si) do { \
+ if (!p7si) { \
+ ossl_raise(rb_eRuntimeError, "PKCS7si wasn't initialized."); \
+ } \
+ obj = Data_Wrap_Struct(klass, 0, PKCS7_SIGNER_INFO_free, p7si); \
+} while (0)
+#define GetPKCS7si(obj, p7si) do { \
+ Data_Get_Struct(obj, PKCS7_SIGNER_INFO, p7si); \
+ if (!p7si) { \
+ ossl_raise(rb_eRuntimeError, "PKCS7si wasn't initialized."); \
+ } \
+} while (0)
+#define SafeGetPKCS7si(obj, p7si) do { \
+ OSSL_Check_Kind(obj, cPKCS7Signer); \
+ GetPKCS7si(obj, p7si); \
+} while (0)
+
+#define numberof(ary) (sizeof(ary)/sizeof(ary[0]))
+
+#define ossl_pkcs7_set_data(o,v) rb_iv_set((o), "@data", (v))
+#define ossl_pkcs7_get_data(o) rb_iv_get((o), "@data")
+#define ossl_pkcs7_set_err_string(o,v) rb_iv_set((o), "@error_string", (v))
+#define ossl_pkcs7_get_err_string(o) rb_iv_get((o), "@error_string")
+
+/*
+ * Classes
+ */
+VALUE mPKCS7;
+VALUE cPKCS7;
+VALUE cPKCS7Signer;
+VALUE ePKCS7Error;
+
+/*
+ * Public
+ * (MADE PRIVATE UNTIL SOMEBODY WILL NEED THEM)
+ */
+static VALUE
+ossl_pkcs7si_new(PKCS7_SIGNER_INFO *p7si)
+{
+ PKCS7_SIGNER_INFO *pkcs7;
+ VALUE obj;
+
+ pkcs7 = p7si ? PKCS7_SIGNER_INFO_dup(p7si) : PKCS7_SIGNER_INFO_new();
+ if (!pkcs7) ossl_raise(ePKCS7Error, NULL);
+ WrapPKCS7si(cPKCS7Signer, obj, pkcs7);
+
+ return obj;
+}
+
+static PKCS7_SIGNER_INFO *
+DupPKCS7SignerPtr(VALUE obj)
+{
+ PKCS7_SIGNER_INFO *p7si, *pkcs7;
+
+ SafeGetPKCS7si(obj, p7si);
+ if (!(pkcs7 = PKCS7_SIGNER_INFO_dup(p7si))) {
+ ossl_raise(ePKCS7Error, NULL);
+ }
+
+ return pkcs7;
+}
+
+/*
+ * Private
+ */
+static VALUE
+ossl_pkcs7_s_read_smime(VALUE klass, VALUE arg)
+{
+ BIO *in, *out;
+ PKCS7 *pkcs7;
+ VALUE ret, data;
+ int status = 0;
+
+ in = ossl_obj2bio(arg);
+ out = NULL;
+ if((pkcs7 = SMIME_read_PKCS7(in, &out)) == NULL){
+ BIO_free(in);
+ BIO_free(out);
+ ossl_raise(ePKCS7Error, NULL);
+ }
+ if(out) data = ossl_protect_membio2str(out, &status);
+ else data = Qnil;
+ BIO_free(in);
+ BIO_free(out);
+ if(status) rb_jump_tag(status);
+ WrapPKCS7(cPKCS7, ret, pkcs7);
+ ossl_pkcs7_set_data(ret, data);
+ ossl_pkcs7_set_err_string(ret, Qnil);
+
+ return ret;
+}
+
+static VALUE
+ossl_pkcs7_s_write_smime(int argc, VALUE *argv, VALUE klass)
+{
+ VALUE pkcs7, data, flags;
+ BIO *out;
+ BIO *in;
+ PKCS7 *p7;
+ VALUE str;
+ int flg, status = 0;
+
+ rb_scan_args(argc, argv, "12", &pkcs7, &data, &flags);
+ SafeGetPKCS7(pkcs7, p7);
+ flg = NIL_P(flags) ? 0 : NUM2INT(flags);
+ if(NIL_P(data)) data = ossl_pkcs7_get_data(pkcs7);
+ if(!NIL_P(data) && PKCS7_is_detached(p7))
+ flg |= PKCS7_DETACHED;
+ in = NIL_P(data) ? NULL : ossl_obj2bio(data);
+ if(!(out = BIO_new(BIO_s_mem()))){
+ BIO_free(in);
+ ossl_raise(ePKCS7Error, NULL);
+ }
+ if(!SMIME_write_PKCS7(out, p7, in, flg)){
+ BIO_free(out);
+ BIO_free(in);
+ ossl_raise(ePKCS7Error, NULL);
+ }
+ str = ossl_protect_membio2str(out, &status);
+ BIO_free(in);
+ BIO_free(out);
+ if(status) rb_jump_tag(status);
+
+ return str;
+}
+
+static VALUE
+ossl_pkcs7_s_sign(int argc, VALUE *argv, VALUE klass)
+{
+ VALUE cert, key, data, certs, flags;
+ X509 *x509;
+ EVP_PKEY *pkey;
+ BIO *in;
+ STACK_OF(X509) *x509s;
+ int flg, status = 0;
+ PKCS7 *pkcs7;
+ VALUE ret;
+
+ rb_scan_args(argc, argv, "32", &cert, &key, &data, &certs, &flags);
+ x509 = GetX509CertPtr(cert); /* NO NEED TO DUP */
+ pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */
+ flg = NIL_P(flags) ? 0 : NUM2INT(flags);
+ in = ossl_obj2bio(data);
+ if(NIL_P(certs)) x509s = NULL;
+ else{
+ x509s = ossl_protect_x509_ary2sk(certs, &status);
+ if(status){
+ BIO_free(in);
+ rb_jump_tag(status);
+ }
+ }
+ if(!(pkcs7 = PKCS7_sign(x509, pkey, x509s, in, flg))){
+ BIO_free(in);
+ sk_X509_pop_free(x509s, X509_free);
+ ossl_raise(ePKCS7Error, NULL);
+ }
+ WrapPKCS7(cPKCS7, ret, pkcs7);
+ ossl_pkcs7_set_data(ret, data);
+ ossl_pkcs7_set_err_string(ret, Qnil);
+ BIO_free(in);
+ sk_X509_pop_free(x509s, X509_free);
+
+ return ret;
+}
+
+static VALUE
+ossl_pkcs7_s_encrypt(int argc, VALUE *argv, VALUE klass)
+{
+ VALUE certs, data, cipher, flags;
+ STACK_OF(X509) *x509s;
+ BIO *in;
+ const EVP_CIPHER *ciph;
+ int flg, status = 0;
+ VALUE ret;
+ PKCS7 *p7;
+
+ rb_scan_args(argc, argv, "22", &certs, &data, &cipher, &flags);
+ if(NIL_P(cipher)){
+#if !defined(OPENSSL_NO_RC2)
+ ciph = EVP_rc2_40_cbc();
+#elif !defined(OPENSSL_NO_DES)
+ ciph = EVP_des_ede3_cbc();
+#elif !defined(OPENSSL_NO_RC2)
+ ciph = EVP_rc2_40_cbc();
+#elif !defined(OPENSSL_NO_AES)
+ ciph = EVP_EVP_aes_128_cbc();
+#else
+ ossl_raise(ePKCS7Error, "Must specify cipher");
+#endif
+
+ }
+ else ciph = GetCipherPtr(cipher); /* NO NEED TO DUP */
+ flg = NIL_P(flags) ? 0 : NUM2INT(flags);
+ in = ossl_obj2bio(data);
+ x509s = ossl_protect_x509_ary2sk(certs, &status);
+ if(status){
+ BIO_free(in);
+ rb_jump_tag(status);
+ }
+ if(!(p7 = PKCS7_encrypt(x509s, in, (EVP_CIPHER*)ciph, flg))){
+ BIO_free(in);
+ sk_X509_pop_free(x509s, X509_free);
+ ossl_raise(ePKCS7Error, NULL);
+ }
+ WrapPKCS7(cPKCS7, ret, p7);
+ ossl_pkcs7_set_data(ret, data);
+ BIO_free(in);
+ sk_X509_pop_free(x509s, X509_free);
+
+ return ret;
+}
+
+static VALUE
+ossl_pkcs7_alloc(VALUE klass)
+{
+ PKCS7 *pkcs7;
+ VALUE obj;
+
+ if (!(pkcs7 = PKCS7_new())) {
+ ossl_raise(ePKCS7Error, NULL);
+ }
+ WrapPKCS7(klass, obj, pkcs7);
+
+ return obj;
+}
+DEFINE_ALLOC_WRAPPER(ossl_pkcs7_alloc)
+
+static VALUE
+ossl_pkcs7_initialize(int argc, VALUE *argv, VALUE self)
+{
+ BIO *in;
+ VALUE s;
+
+ if(rb_scan_args(argc, argv, "01", &s) == 0)
+ return self;
+ in = ossl_obj2bio(s);
+
+ if (!PEM_read_bio_PKCS7(in, (PKCS7 **)&DATA_PTR(self), NULL, NULL)) {
+ BIO_free(in);
+ ossl_raise(ePKCS7Error, NULL);
+ }
+ BIO_free(in);
+ ossl_pkcs7_set_data(self, Qnil);
+ ossl_pkcs7_set_err_string(self, Qnil);
+
+ return self;
+}
+
+static VALUE
+ossl_pkcs7_copy(VALUE self, VALUE other)
+{
+ PKCS7 *a, *b, *pkcs7;
+
+ rb_check_frozen(self);
+ if (self == other) return self;
+
+ GetPKCS7(self, a);
+ SafeGetPKCS7(other, b);
+
+ pkcs7 = PKCS7_dup(b);
+ if (!pkcs7) {
+ ossl_raise(ePKCS7Error, NULL);
+ }
+ DATA_PTR(self) = pkcs7;
+ PKCS7_free(a);
+
+ return self;
+}
+
+static int
+ossl_pkcs7_sym2typeid(VALUE sym)
+{
+ int i, ret = Qnil;
+ char *s;
+
+ static struct {
+ const char *name;
+ int nid;
+ } p7_type_tab[] = {
+ { "signed", NID_pkcs7_signed },
+ { "data", NID_pkcs7_data },
+ { "signedAndEnveloped", NID_pkcs7_signedAndEnveloped },
+ { "enveloped", NID_pkcs7_enveloped },
+ { "encrypted", NID_pkcs7_encrypted },
+ { "digest", NID_pkcs7_digest },
+ { NULL, 0 },
+ };
+
+ if(TYPE(sym) == T_SYMBOL) s = rb_id2name(SYM2ID(sym));
+ else s = StringValuePtr(sym);
+ for(i = 0; i < numberof(p7_type_tab); i++){
+ if(p7_type_tab[i].name == NULL)
+ ossl_raise(ePKCS7Error, "unknown type \"%s\"", s);
+ if(strcmp(p7_type_tab[i].name, s) == 0){
+ ret = p7_type_tab[i].nid;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static VALUE
+ossl_pkcs7_set_type(VALUE self, VALUE type)
+{
+ PKCS7 *p7;
+
+ GetPKCS7(self, p7);
+ if(!PKCS7_set_type(p7, ossl_pkcs7_sym2typeid(type)))
+ ossl_raise(ePKCS7Error, NULL);
+
+ return type;
+}
+
+static VALUE
+ossl_pkcs7_get_type(VALUE self)
+{
+ PKCS7 *p7;
+
+ GetPKCS7(self, p7);
+ if(PKCS7_type_is_signed(p7))
+ return ID2SYM(rb_intern("signed"));
+ if(PKCS7_type_is_encrypted(p7))
+ return ID2SYM(rb_intern("encrypted"));
+ if(PKCS7_type_is_enveloped(p7))
+ return ID2SYM(rb_intern("enveloped"));
+ if(PKCS7_type_is_signedAndEnveloped(p7))
+ return ID2SYM(rb_intern("signedAndEnveloped"));
+ if(PKCS7_type_is_data(p7))
+ return ID2SYM(rb_intern("data"));
+ return Qnil;
+}
+
+static VALUE
+ossl_pkcs7_set_detached(VALUE self, VALUE flag)
+{
+ PKCS7 *p7;
+
+ GetPKCS7(self, p7);
+ if(flag != Qtrue && flag != Qfalse)
+ ossl_raise(ePKCS7Error, "must secify a boolean");
+ if(!PKCS7_set_detached(p7, flag == Qtrue ? 1 : 0))
+ ossl_raise(ePKCS7Error, NULL);
+
+ return flag;
+}
+
+static VALUE
+ossl_pkcs7_get_detached(VALUE self)
+{
+ PKCS7 *p7;
+ GetPKCS7(self, p7);
+ return PKCS7_get_detached(p7) ? Qtrue : Qfalse;
+}
+
+static VALUE
+ossl_pkcs7_detached_p(VALUE self)
+{
+ PKCS7 *p7;
+ GetPKCS7(self, p7);
+ return PKCS7_is_detached(p7) ? Qtrue : Qfalse;
+}
+
+static VALUE
+ossl_pkcs7_set_cipher(VALUE self, VALUE cipher)
+{
+ PKCS7 *pkcs7;
+
+ GetPKCS7(self, pkcs7);
+ if (!PKCS7_set_cipher(pkcs7, GetCipherPtr(cipher))) {
+ ossl_raise(ePKCS7Error, NULL);
+ }
+
+ return cipher;
+}
+
+static VALUE
+ossl_pkcs7_add_signer(VALUE self, VALUE signer)
+{
+ PKCS7 *pkcs7;
+ PKCS7_SIGNER_INFO *p7si;
+
+ GetPKCS7(self, pkcs7);
+ p7si = DupPKCS7SignerPtr(signer); /* NEED TO DUP */
+ if (!PKCS7_add_signer(pkcs7, p7si)) {
+ PKCS7_SIGNER_INFO_free(p7si);
+ ossl_raise(ePKCS7Error, "Could not add signer.");
+ }
+ if (PKCS7_type_is_signed(pkcs7)){
+ PKCS7_add_signed_attribute(p7si, NID_pkcs9_contentType,
+ V_ASN1_OBJECT, OBJ_nid2obj(NID_pkcs7_data));
+ }
+
+ return self;
+}
+
+static VALUE
+ossl_pkcs7_get_signer(VALUE self)
+{
+ PKCS7 *pkcs7;
+ STACK_OF(PKCS7_SIGNER_INFO) *sk;
+ PKCS7_SIGNER_INFO *si;
+ int num, i;
+ VALUE ary;
+
+ GetPKCS7(self, pkcs7);
+ if (!(sk = PKCS7_get_signer_info(pkcs7))) {
+ OSSL_Debug("OpenSSL::PKCS7#get_signer_info == NULL!");
+ return rb_ary_new();
+ }
+ if ((num = sk_PKCS7_SIGNER_INFO_num(sk)) < 0) {
+ ossl_raise(ePKCS7Error, "Negative number of signers!");
+ }
+ ary = rb_ary_new2(num);
+ for (i=0; i<num; i++) {
+ si = sk_PKCS7_SIGNER_INFO_value(sk, i);
+ rb_ary_push(ary, ossl_pkcs7si_new(si));
+ }
+
+ return ary;
+}
+
+static VALUE
+ossl_pkcs7_add_recipient(VALUE self, VALUE cert)
+{
+ PKCS7 *pkcs7;
+ PKCS7_RECIP_INFO *ri;
+ X509 *x509;
+
+ GetPKCS7(self, pkcs7);
+ x509 = GetX509CertPtr(cert); /* NO NEED TO DUP */
+ if (!(ri = PKCS7_RECIP_INFO_new())) {
+ ossl_raise(ePKCS7Error, NULL);
+ }
+ if (!PKCS7_RECIP_INFO_set(ri, x509)) {
+ PKCS7_RECIP_INFO_free(ri);
+ ossl_raise(ePKCS7Error, NULL);
+ }
+ if (!PKCS7_add_recipient_info(pkcs7, ri)) {
+ PKCS7_RECIP_INFO_free(ri);
+ ossl_raise(ePKCS7Error, NULL);
+ }
+
+ return self;
+}
+
+static VALUE
+ossl_pkcs7_add_certificate(VALUE self, VALUE cert)
+{
+ PKCS7 *pkcs7;
+ X509 *x509;
+
+ GetPKCS7(self, pkcs7);
+ x509 = GetX509CertPtr(cert); /* NO NEED TO DUP */
+ if (!PKCS7_add_certificate(pkcs7, x509)){
+ ossl_raise(ePKCS7Error, NULL);
+ }
+
+ return self;
+}
+
+static VALUE
+ossl_pkcs7_add_crl(VALUE self, VALUE crl)
+{
+ PKCS7 *pkcs7;
+ X509_CRL *x509crl;
+
+ GetPKCS7(self, pkcs7); /* NO DUP needed! */
+ x509crl = GetX509CRLPtr(crl);
+ if (!PKCS7_add_crl(pkcs7, x509crl)) {
+ ossl_raise(ePKCS7Error, NULL);
+ }
+
+ return self;
+}
+
+static VALUE
+ossl_pkcs7_verify(int argc, VALUE *argv, VALUE self)
+{
+ VALUE certs, store, indata, flags;
+ STACK_OF(X509) *x509s;
+ X509_STORE *x509st;
+ int flg, ok, status = 0;
+ BIO *in, *out;
+ PKCS7 *p7;
+ VALUE data;
+ const char *msg;
+
+ GetPKCS7(self, p7);
+ rb_scan_args(argc, argv, "22", &certs, &store, &indata, &flags);
+ x509st = GetX509StorePtr(store);
+ flg = NIL_P(flags) ? 0 : NUM2INT(flags);
+ if(NIL_P(indata)) indata = ossl_pkcs7_get_data(self);
+ in = NIL_P(indata) ? NULL : ossl_obj2bio(indata);
+ if(NIL_P(certs)) x509s = NULL;
+ else{
+ x509s = ossl_protect_x509_ary2sk(certs, &status);
+ if(status){
+ BIO_free(in);
+ rb_jump_tag(status);
+ }
+ }
+ if(!(out = BIO_new(BIO_s_mem()))){
+ BIO_free(in);
+ sk_X509_pop_free(x509s, X509_free);
+ ossl_raise(ePKCS7Error, NULL);
+ }
+ ok = PKCS7_verify(p7, x509s, x509st, in, out, flg);
+ msg = ERR_reason_error_string(ERR_get_error());
+ ossl_pkcs7_set_err_string(self, msg ? rb_str_new2(msg) : Qnil);
+ data = ossl_protect_membio2str(out, &status);
+ ossl_pkcs7_set_data(self, data);
+ BIO_free(in);
+ BIO_free(out);
+ sk_X509_pop_free(x509s, X509_free);
+ if(status) rb_jump_tag(status);
+
+ return (ok == 1) ? Qtrue : Qfalse;
+}
+
+static VALUE
+ossl_pkcs7_decrypt(int argc, VALUE *argv, VALUE self)
+{
+ VALUE pkey, cert, flags;
+ EVP_PKEY *key;
+ X509 *x509;
+ int flg;
+ PKCS7 *p7;
+ BIO *out;
+ VALUE str;
+ int status = 0;
+
+ rb_scan_args(argc, argv, "21", &pkey, &cert, &flags);
+ GetPKCS7(self, p7);
+ key = GetPrivPKeyPtr(pkey); /* NO NEED TO DUP */
+ x509 = GetX509CertPtr(cert); /* NO NEED TO DUP */
+ flg = NIL_P(flags) ? 0 : NUM2INT(flags);
+ if(!(out = BIO_new(BIO_s_mem())))
+ ossl_raise(ePKCS7Error, NULL);
+ if(!PKCS7_decrypt(p7, key, x509, out, flg)){
+ BIO_free(out);
+ ossl_raise(ePKCS7Error, NULL);
+ }
+ str = ossl_protect_membio2str(out, &status);
+ BIO_free(out);
+ if(status) rb_jump_tag(status);
+
+ return str;
+}
+
+static VALUE
+ossl_pkcs7_add_data(VALUE self, VALUE data)
+{
+ PKCS7 *pkcs7;
+ BIO *out, *in;
+ char buf[4096];
+ int len;
+
+ in = out = NULL;
+ GetPKCS7(self, pkcs7);
+ if(PKCS7_type_is_signed(pkcs7)){
+ if(!PKCS7_content_new(pkcs7, NID_pkcs7_data))
+ ossl_raise(ePKCS7Error, NULL);
+ }
+ in = ossl_obj2bio(data);
+ if(!(out = PKCS7_dataInit(pkcs7, NULL))) goto err;
+ for(;;){
+ if((len = BIO_read(in, buf, sizeof(buf))) <= 0)
+ break;
+ if(BIO_write(out, buf, len) != len)
+ goto err;
+ }
+ if(!PKCS7_dataFinal(pkcs7, out)) goto err;
+ ossl_pkcs7_set_data(self, Qnil);
+
+ err:
+ BIO_free(out);
+ BIO_free(in);
+ if(ERR_peek_error()){
+ ossl_raise(ePKCS7Error, NULL);
+ }
+
+ return data;
+}
+
+static VALUE
+ossl_pkcs7_to_pem(VALUE self)
+{
+ PKCS7 *pkcs7;
+ BIO *out;
+ VALUE str;
+ int status = 0;
+
+ GetPKCS7(self, pkcs7);
+ if (!(out = BIO_new(BIO_s_mem()))) {
+ ossl_raise(ePKCS7Error, NULL);
+ }
+ if (!PEM_write_bio_PKCS7(out, pkcs7)) {
+ BIO_free(out);
+ ossl_raise(ePKCS7Error, NULL);
+ }
+ str = ossl_protect_membio2str(out, &status);
+ BIO_free(out);
+ if(status) rb_jump_tag(status);
+
+ return str;
+}
+
+/*
+ * SIGNER INFO
+ */
+static VALUE
+ossl_pkcs7si_alloc(VALUE klass)
+{
+ PKCS7_SIGNER_INFO *p7si;
+ VALUE obj;
+
+ if (!(p7si = PKCS7_SIGNER_INFO_new())) {
+ ossl_raise(ePKCS7Error, NULL);
+ }
+ WrapPKCS7si(klass, obj, p7si);
+
+ return obj;
+}
+DEFINE_ALLOC_WRAPPER(ossl_pkcs7si_alloc)
+
+static VALUE
+ossl_pkcs7si_initialize(VALUE self, VALUE cert, VALUE key, VALUE digest)
+{
+ PKCS7_SIGNER_INFO *p7si;
+ EVP_PKEY *pkey;
+ X509 *x509;
+ const EVP_MD *md;
+
+ GetPKCS7si(self, p7si);
+ pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */
+ x509 = GetX509CertPtr(cert); /* NO NEED TO DUP */
+ md = GetDigestPtr(digest);
+ if (!(PKCS7_SIGNER_INFO_set(p7si, x509, pkey, (EVP_MD*)md))) {
+ ossl_raise(ePKCS7Error, NULL);
+ }
+
+ return self;
+}
+
+static VALUE
+ossl_pkcs7si_get_name(VALUE self)
+{
+ PKCS7_SIGNER_INFO *p7si;
+
+ GetPKCS7si(self, p7si);
+
+ return ossl_x509name_new(p7si->issuer_and_serial->issuer);
+}
+
+static VALUE
+ossl_pkcs7si_get_serial(VALUE self)
+{
+ PKCS7_SIGNER_INFO *p7si;
+
+ GetPKCS7si(self, p7si);
+
+ return asn1integer_to_num(p7si->issuer_and_serial->serial);
+}
+
+static VALUE
+ossl_pkcs7si_get_signed_time(VALUE self)
+{
+ PKCS7_SIGNER_INFO *p7si;
+ ASN1_TYPE *asn1obj;
+
+ GetPKCS7si(self, p7si);
+
+ if (!(asn1obj = PKCS7_get_signed_attribute(p7si, NID_pkcs9_signingTime))) {
+ ossl_raise(ePKCS7Error, NULL);
+ }
+ if (asn1obj->type == V_ASN1_UTCTIME) {
+ return asn1time_to_time(asn1obj->value.utctime);
+ }
+ /*
+ * OR
+ * ossl_raise(ePKCS7Error, "...");
+ * ?
+ */
+
+ return Qnil;
+}
+
+/*
+ * INIT
+ */
+void
+Init_ossl_pkcs7()
+{
+ mPKCS7 = rb_define_module_under(mOSSL, "PKCS7");
+
+ ePKCS7Error = rb_define_class_under(mPKCS7, "PKCS7Error", eOSSLError);
+
+ cPKCS7 = rb_define_class_under(mPKCS7, "PKCS7", rb_cObject);
+ rb_define_singleton_method(mPKCS7, "read_smime", ossl_pkcs7_s_read_smime, 1);
+ rb_define_singleton_method(mPKCS7, "write_smime", ossl_pkcs7_s_write_smime, -1);
+ rb_define_singleton_method(mPKCS7, "sign", ossl_pkcs7_s_sign, -1);
+ rb_define_singleton_method(mPKCS7, "encrypt", ossl_pkcs7_s_encrypt, -1);
+ rb_attr(cPKCS7, rb_intern("data"), 1, 0, Qfalse);
+ rb_attr(cPKCS7, rb_intern("error_string"), 1, 1, Qfalse);
+ rb_define_alloc_func(cPKCS7, ossl_pkcs7_alloc);
+ rb_define_copy_func(cPKCS7, ossl_pkcs7_copy);
+ rb_define_method(cPKCS7, "initialize", ossl_pkcs7_initialize, -1);
+ rb_define_method(cPKCS7, "type=", ossl_pkcs7_set_type, 1);
+ rb_define_method(cPKCS7, "type", ossl_pkcs7_get_type, 0);
+ rb_define_method(cPKCS7, "detached=", ossl_pkcs7_set_detached, 1);
+ rb_define_method(cPKCS7, "detached", ossl_pkcs7_get_detached, 0);
+ rb_define_method(cPKCS7, "detached?", ossl_pkcs7_detached_p, 0);
+ rb_define_method(cPKCS7, "cipher=", ossl_pkcs7_set_cipher, 1);
+ rb_define_method(cPKCS7, "add_signer", ossl_pkcs7_add_signer, 1);
+ rb_define_method(cPKCS7, "signers", ossl_pkcs7_get_signer, 0);
+ rb_define_method(cPKCS7, "add_recipient", ossl_pkcs7_add_recipient, 1);
+ rb_define_method(cPKCS7, "add_certificate", ossl_pkcs7_add_certificate, 1);
+ rb_define_method(cPKCS7, "add_crl", ossl_pkcs7_add_crl, 1);
+ rb_define_method(cPKCS7, "add_data", ossl_pkcs7_add_data, 1);
+ rb_define_alias(cPKCS7, "data=", "add_data");
+ rb_define_method(cPKCS7, "verify", ossl_pkcs7_verify, -1);
+ rb_define_method(cPKCS7, "decrypt", ossl_pkcs7_decrypt, -1);
+ rb_define_method(cPKCS7, "to_pem", ossl_pkcs7_to_pem, 0);
+ rb_define_alias(cPKCS7, "to_s", "to_pem");
+
+ cPKCS7Signer = rb_define_class_under(mPKCS7, "Signer", rb_cObject);
+ rb_define_alloc_func(cPKCS7Signer, ossl_pkcs7si_alloc);
+ rb_define_method(cPKCS7Signer, "initialize", ossl_pkcs7si_initialize,3);
+ rb_define_method(cPKCS7Signer, "name", ossl_pkcs7si_get_name,0);
+ rb_define_method(cPKCS7Signer, "serial", ossl_pkcs7si_get_serial,0);
+ rb_define_method(cPKCS7Signer, "signed_time", ossl_pkcs7si_get_signed_time,0);
+
+#define DefPKCS7Const(x) rb_define_const(mPKCS7, #x, INT2NUM(PKCS7_##x))
+
+ DefPKCS7Const(TEXT);
+ DefPKCS7Const(NOCERTS);
+ DefPKCS7Const(NOSIGS);
+ DefPKCS7Const(NOCHAIN);
+ DefPKCS7Const(NOINTERN);
+ DefPKCS7Const(NOVERIFY);
+ DefPKCS7Const(DETACHED);
+ DefPKCS7Const(BINARY);
+ DefPKCS7Const(NOATTR);
+ DefPKCS7Const(NOSMIMECAP);
+}
diff --git a/ext/openssl/ossl_pkcs7.h b/ext/openssl/ossl_pkcs7.h
new file mode 100644
index 0000000000..9378397f25
--- /dev/null
+++ b/ext/openssl/ossl_pkcs7.h
@@ -0,0 +1,22 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#if !defined(_OSSL_PKCS7_H_)
+#define _OSSL_PKCS7_H_
+
+extern VALUE mPKCS7;
+extern VALUE cPKCS7;
+extern VALUE cPKCS7SignerInfo;
+extern VALUE ePKCS7Error;
+
+void Init_ossl_pkcs7(void);
+
+#endif /* _OSSL_PKCS7_H_ */
+
diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c
new file mode 100644
index 0000000000..cd73355d66
--- /dev/null
+++ b/ext/openssl/ossl_pkey.c
@@ -0,0 +1,238 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#include "ossl.h"
+
+/*
+ * Classes
+ */
+VALUE mPKey;
+VALUE cPKey;
+VALUE ePKeyError;
+ID id_private_q;
+
+/*
+ * Public
+ */
+VALUE
+ossl_pkey_new(EVP_PKEY *pkey)
+{
+ if (!pkey) {
+ ossl_raise(ePKeyError, "Cannot make new key from NULL.");
+ }
+ switch (EVP_PKEY_type(pkey->type)) {
+#if !defined(OPENSSL_NO_RSA)
+ case EVP_PKEY_RSA:
+ return ossl_rsa_new(pkey);
+#endif
+#if !defined(OPENSSL_NO_DSA)
+ case EVP_PKEY_DSA:
+ return ossl_dsa_new(pkey);
+#endif
+#if !defined(OPENSSL_NO_DH)
+ case EVP_PKEY_DH:
+ return ossl_dh_new(pkey);
+#endif
+ default:
+ ossl_raise(ePKeyError, "unsupported key type");
+ }
+ return Qnil; /* not reached */
+}
+
+VALUE
+ossl_pkey_new_from_file(VALUE filename)
+{
+ FILE *fp;
+ EVP_PKEY *pkey;
+
+ SafeStringValue(filename);
+ if (!(fp = fopen(RSTRING(filename)->ptr, "r"))) {
+ ossl_raise(ePKeyError, "%s", strerror(errno));
+ }
+
+ pkey = PEM_read_PrivateKey(fp, NULL, ossl_pem_passwd_cb, NULL);
+ fclose(fp);
+ if (!pkey) {
+ ossl_raise(ePKeyError, NULL);
+ }
+
+ return ossl_pkey_new(pkey);
+}
+
+EVP_PKEY *
+GetPKeyPtr(VALUE obj)
+{
+ EVP_PKEY *pkey;
+
+ SafeGetPKey(obj, pkey);
+
+ return pkey;
+}
+
+EVP_PKEY *
+GetPrivPKeyPtr(VALUE obj)
+{
+ EVP_PKEY *pkey;
+
+ SafeGetPKey(obj, pkey);
+ if (rb_funcall(obj, id_private_q, 0, NULL) != Qtrue) { /* returns Qtrue */
+ ossl_raise(rb_eArgError, "Private key is needed.");
+ }
+
+ return pkey;
+}
+
+EVP_PKEY *
+DupPrivPKeyPtr(VALUE obj)
+{
+ EVP_PKEY *pkey;
+
+ SafeGetPKey(obj, pkey);
+ if (rb_funcall(obj, id_private_q, 0, NULL) != Qtrue) { /* returns Qtrue */
+ ossl_raise(rb_eArgError, "Private key is needed.");
+ }
+ CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY);
+
+ return pkey;
+}
+
+/*
+ * Private
+ */
+static VALUE
+ossl_pkey_alloc(VALUE klass)
+{
+ EVP_PKEY *pkey;
+ VALUE obj;
+
+ if (!(pkey = EVP_PKEY_new())) {
+ ossl_raise(ePKeyError, NULL);
+ }
+ WrapPKey(klass, obj, pkey);
+
+ return obj;
+}
+DEFINE_ALLOC_WRAPPER(ossl_pkey_alloc)
+
+static VALUE
+ossl_pkey_initialize(VALUE self)
+{
+ if (rb_obj_is_instance_of(self, cPKey)) {
+ ossl_raise(rb_eNotImpError, "OpenSSL::PKey::PKey is an abstract class.");
+ }
+ return self;
+}
+
+static VALUE
+ossl_pkey_to_der(VALUE self)
+{
+ EVP_PKEY *pkey;
+ VALUE str;
+ BIO *out;
+ BUF_MEM *buf;
+
+ GetPKey(self, pkey);
+
+ out = BIO_new(BIO_s_mem());
+ if (!out) ossl_raise(ePKeyError, NULL);
+
+ if (!i2d_PUBKEY_bio(out, pkey)) {
+ BIO_free(out);
+ ossl_raise(ePKeyError, NULL);
+ }
+
+ BIO_get_mem_ptr(out, &buf);
+ str = rb_str_new(buf->data, buf->length);
+
+ BIO_free(out);
+
+ return str;
+}
+
+static VALUE
+ossl_pkey_sign(VALUE self, VALUE digest, VALUE data)
+{
+ EVP_PKEY *pkey;
+ EVP_MD_CTX ctx;
+ char *buf;
+ int buf_len;
+ VALUE str;
+
+ GetPKey(self, pkey);
+ if (rb_funcall(self, id_private_q, 0, NULL) != Qtrue) {
+ ossl_raise(rb_eArgError, "Private key is needed.");
+ }
+ EVP_SignInit(&ctx, GetDigestPtr(digest));
+ StringValue(data);
+ EVP_SignUpdate(&ctx, RSTRING(data)->ptr, RSTRING(data)->len);
+ if (!(buf = OPENSSL_malloc(EVP_PKEY_size(pkey) + 16))) {
+ ossl_raise(ePKeyError, NULL);
+ }
+ if (!EVP_SignFinal(&ctx, buf, &buf_len, pkey)) {
+ OPENSSL_free(buf);
+ ossl_raise(ePKeyError, NULL);
+ }
+ str = rb_str_new(buf, buf_len);
+ OPENSSL_free(buf);
+
+ return str;
+}
+
+static VALUE
+ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data)
+{
+ EVP_PKEY *pkey;
+ EVP_MD_CTX ctx;
+
+ GetPKey(self, pkey);
+ EVP_VerifyInit(&ctx, GetDigestPtr(digest));
+ StringValue(sig);
+ StringValue(data);
+ EVP_VerifyUpdate(&ctx, RSTRING(data)->ptr, RSTRING(data)->len);
+ switch (EVP_VerifyFinal(&ctx, RSTRING(sig)->ptr, RSTRING(sig)->len, pkey)) {
+ case 0:
+ return Qfalse;
+ case 1:
+ return Qtrue;
+ default:
+ ossl_raise(ePKeyError, NULL);
+ }
+ return Qnil; /* dummy */
+}
+
+/*
+ * INIT
+ */
+void
+Init_ossl_pkey()
+{
+ mPKey = rb_define_module_under(mOSSL, "PKey");
+
+ ePKeyError = rb_define_class_under(mPKey, "PKeyError", eOSSLError);
+
+ cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject);
+
+ rb_define_alloc_func(cPKey, ossl_pkey_alloc);
+ rb_define_method(cPKey, "initialize", ossl_pkey_initialize, 0);
+
+ rb_define_method(cPKey, "to_der", ossl_pkey_to_der, 0);
+ rb_define_method(cPKey, "sign", ossl_pkey_sign, 2);
+ rb_define_method(cPKey, "verify", ossl_pkey_verify, 3);
+
+ id_private_q = rb_intern("private?");
+
+ /*
+ * INIT rsa, dsa
+ */
+ Init_ossl_rsa();
+ Init_ossl_dsa();
+ Init_ossl_dh();
+}
+
diff --git a/ext/openssl/ossl_pkey.h b/ext/openssl/ossl_pkey.h
new file mode 100644
index 0000000000..f3cf20c5d3
--- /dev/null
+++ b/ext/openssl/ossl_pkey.h
@@ -0,0 +1,111 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#if !defined(_OSSL_PKEY_H_)
+#define _OSSL_PKEY_H_
+
+extern VALUE mPKey;
+extern VALUE cPKey;
+extern VALUE ePKeyError;
+extern ID id_private_q;
+
+#define WrapPKey(klass, obj, pkey) do { \
+ if (!pkey) { \
+ rb_raise(rb_eRuntimeError, "PKEY wasn't initialized!"); \
+ } \
+ obj = Data_Wrap_Struct(klass, 0, EVP_PKEY_free, pkey); \
+} while (0)
+#define GetPKey(obj, pkey) do {\
+ Data_Get_Struct(obj, EVP_PKEY, pkey);\
+ if (!pkey) { \
+ rb_raise(rb_eRuntimeError, "PKEY wasn't initialized!");\
+ } \
+} while (0)
+#define SafeGetPKey(obj, pkey) do { \
+ OSSL_Check_Kind(obj, cPKey); \
+ GetPKey(obj, pkey); \
+} while (0)
+
+VALUE ossl_pkey_new(EVP_PKEY *);
+VALUE ossl_pkey_new_from_file(VALUE);
+EVP_PKEY *GetPKeyPtr(VALUE);
+/*EVP_PKEY *DupPKeyPtr(VALUE);*/
+EVP_PKEY *GetPrivPKeyPtr(VALUE);
+EVP_PKEY *DupPrivPKeyPtr(VALUE);
+void Init_ossl_pkey(void);
+
+/*
+ * RSA
+ */
+extern VALUE cRSA;
+extern VALUE eRSAError;
+
+VALUE ossl_rsa_new(EVP_PKEY *);
+void Init_ossl_rsa(void);
+
+/*
+ * DSA
+ */
+extern VALUE cDSA;
+extern VALUE eDSAError;
+
+VALUE ossl_dsa_new(EVP_PKEY *);
+void Init_ossl_dsa(void);
+
+/*
+ * DH
+ */
+extern VALUE cDH;
+extern VALUE eDHError;
+
+VALUE ossl_dh_new(EVP_PKEY *);
+void Init_ossl_dh(void);
+
+#define OSSL_PKEY_BN(keytype, name) \
+static VALUE ossl_##keytype##_get_##name(VALUE self) \
+{ \
+ EVP_PKEY *pkey; \
+ BIGNUM *bn; \
+ \
+ GetPKey(self, pkey); \
+ bn = pkey->pkey.keytype->name; \
+ if (bn == NULL) \
+ return Qnil; \
+ return ossl_bn_new(bn); \
+} \
+static VALUE ossl_##keytype##_set_##name(VALUE self, VALUE bignum) \
+{ \
+ EVP_PKEY *pkey; \
+ BIGNUM *bn; \
+ \
+ GetPKey(self, pkey); \
+ if (NIL_P(bignum)) { \
+ BN_clear_free(pkey->pkey.keytype->name); \
+ pkey->pkey.keytype->name = NULL; \
+ return Qnil; \
+ } \
+ \
+ bn = GetBNPtr(bignum); \
+ if (pkey->pkey.keytype->name == NULL) \
+ pkey->pkey.keytype->name = BN_new(); \
+ if (pkey->pkey.keytype->name == NULL) \
+ ossl_raise(eBNError, NULL); \
+ if (BN_copy(pkey->pkey.keytype->name, bn) == NULL) \
+ ossl_raise(eBNError, NULL); \
+ return bignum; \
+}
+
+#define DEF_OSSL_PKEY_BN(class, keytype, name) \
+do { \
+ rb_define_method(class, #name, ossl_##keytype##_get_##name, 0); \
+ rb_define_method(class, #name "=", ossl_##keytype##_set_##name, 1); \
+} while (0)
+
+#endif /* _OSSL_PKEY_H_ */
diff --git a/ext/openssl/ossl_pkey_dh.c b/ext/openssl/ossl_pkey_dh.c
new file mode 100644
index 0000000000..9a6bec15eb
--- /dev/null
+++ b/ext/openssl/ossl_pkey_dh.c
@@ -0,0 +1,386 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#if !defined(OPENSSL_NO_DH)
+
+#include "ossl.h"
+
+#define GetPKeyDH(obj, pkey) do { \
+ GetPKey(obj, pkey); \
+ if (EVP_PKEY_type(pkey->type) != EVP_PKEY_DH) { /* PARANOIA? */ \
+ ossl_raise(rb_eRuntimeError, "THIS IS NOT A DH!") ; \
+ } \
+} while (0)
+
+#define DH_PRIVATE(dh) ((dh)->priv_key)
+
+/*
+ * Classes
+ */
+VALUE cDH;
+VALUE eDHError;
+
+/*
+ * Public
+ */
+static VALUE
+dh_instance(VALUE klass, DH *dh)
+{
+ EVP_PKEY *pkey;
+ VALUE obj;
+
+ if (!dh) {
+ return Qfalse;
+ }
+ if (!(pkey = EVP_PKEY_new())) {
+ return Qfalse;
+ }
+ if (!EVP_PKEY_assign_DH(pkey, dh)) {
+ EVP_PKEY_free(pkey);
+ return Qfalse;
+ }
+ WrapPKey(klass, obj, pkey);
+
+ return obj;
+}
+
+VALUE
+ossl_dh_new(EVP_PKEY *pkey)
+{
+ VALUE obj;
+
+ if (!pkey) {
+ obj = dh_instance(cDH, DH_new());
+ } else {
+ if (EVP_PKEY_type(pkey->type) != EVP_PKEY_DH) {
+ ossl_raise(rb_eTypeError, "Not a DH key!");
+ }
+ WrapPKey(cDH, obj, pkey);
+ }
+ if (obj == Qfalse) {
+ ossl_raise(eDHError, NULL);
+ }
+
+ return obj;
+}
+
+/*
+ * Private
+ */
+/*
+ * CB for yielding when generating DH params
+ */
+static void
+ossl_dh_generate_cb(int p, int n, void *arg)
+{
+ VALUE ary;
+
+ ary = rb_ary_new2(2);
+ rb_ary_store(ary, 0, INT2NUM(p));
+ rb_ary_store(ary, 1, INT2NUM(n));
+
+ rb_yield(ary);
+}
+
+static DH *
+dh_generate(int size, int gen)
+{
+ DH *dh;
+ void (*cb)(int, int, void *) = NULL;
+
+ if (rb_block_given_p()) {
+ cb = ossl_dh_generate_cb;
+ }
+ /* arg to cb = NULL */
+ if (!(dh = DH_generate_parameters(size, gen, cb, NULL))) {
+ return 0;
+ }
+ if (!DH_generate_key(dh)) {
+ DH_free(dh);
+ return 0;
+ }
+
+ return dh;
+}
+
+static VALUE
+ossl_dh_s_generate(int argc, VALUE *argv, VALUE klass)
+{
+ DH *dh ;
+ int g = 2;
+ VALUE size, gen, obj;
+
+ if (rb_scan_args(argc, argv, "11", &size, &gen) == 2) {
+ g = FIX2INT(gen);
+ }
+ dh = dh_generate(FIX2INT(size), g);
+ obj = dh_instance(klass, dh);
+ if (obj == Qfalse) {
+ DH_free(dh);
+ ossl_raise(eDHError, NULL);
+ }
+
+ return obj;
+}
+
+static VALUE
+ossl_dh_initialize(int argc, VALUE *argv, VALUE self)
+{
+ EVP_PKEY *pkey;
+ DH *dh;
+ int g = 2;
+ BIO *in;
+ VALUE buffer, gen;
+
+ GetPKey(self, pkey);
+ rb_scan_args(argc, argv, "11", &buffer, &gen);
+ if (FIXNUM_P(buffer)) {
+ if (!NIL_P(gen)) {
+ g = FIX2INT(gen);
+ }
+ if (!(dh = dh_generate(FIX2INT(buffer), g))) {
+ ossl_raise(eDHError, NULL);
+ }
+ } else {
+ StringValue(buffer);
+ in = BIO_new_mem_buf(RSTRING(buffer)->ptr, RSTRING(buffer)->len);
+ if (!in){
+ ossl_raise(eDHError, NULL);
+ }
+ if (!(dh = PEM_read_bio_DHparams(in, NULL, NULL, NULL))) {
+ BIO_free(in);
+ ossl_raise(eDHError, NULL);
+ }
+ BIO_free(in);
+ }
+ if (!EVP_PKEY_assign_DH(pkey, dh)) {
+ DH_free(dh);
+ ossl_raise(eRSAError, NULL);
+ }
+ return self;
+}
+
+static VALUE
+ossl_dh_is_public(VALUE self)
+{
+ EVP_PKEY *pkey;
+
+ GetPKeyDH(self, pkey);
+ /*
+ * Do we need to check dhp->dh->public_pkey?
+ * return Qtrue;
+ */
+ return (pkey->pkey.dh->pub_key) ? Qtrue : Qfalse;
+}
+
+static VALUE
+ossl_dh_is_private(VALUE self)
+{
+ EVP_PKEY *pkey;
+
+ GetPKeyDH(self, pkey);
+
+ return (DH_PRIVATE(pkey->pkey.dh)) ? Qtrue : Qfalse;
+}
+
+static VALUE
+ossl_dh_export(VALUE self)
+{
+ EVP_PKEY *pkey;
+ BIO *out;
+ BUF_MEM *buf;
+ VALUE str;
+
+ GetPKeyDH(self, pkey);
+ if (!(out = BIO_new(BIO_s_mem()))) {
+ ossl_raise(eDHError, NULL);
+ }
+ if (!PEM_write_bio_DHparams(out, pkey->pkey.dh)) {
+ BIO_free(out);
+ ossl_raise(eDHError, NULL);
+ }
+ BIO_get_mem_ptr(out, &buf);
+ str = rb_str_new(buf->data, buf->length);
+ BIO_free(out);
+
+ return str;
+}
+
+/*
+ * Stores all parameters of key to the hash
+ * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!!
+ * Don't use :-)) (I's up to you)
+ */
+static VALUE
+ossl_dh_get_params(VALUE self)
+{
+ EVP_PKEY *pkey;
+ VALUE hash;
+
+ GetPKeyDH(self, pkey);
+
+ hash = rb_hash_new();
+
+ rb_hash_aset(hash, rb_str_new2("p"), ossl_bn_new(pkey->pkey.dh->p));
+ rb_hash_aset(hash, rb_str_new2("g"), ossl_bn_new(pkey->pkey.dh->g));
+ rb_hash_aset(hash, rb_str_new2("pub_key"), ossl_bn_new(pkey->pkey.dh->pub_key));
+ rb_hash_aset(hash, rb_str_new2("priv_key"), ossl_bn_new(pkey->pkey.dh->priv_key));
+
+ return hash;
+}
+
+/*
+ * Prints all parameters of key to buffer
+ * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!!
+ * Don't use :-)) (I's up to you)
+ */
+static VALUE
+ossl_dh_to_text(VALUE self)
+{
+ EVP_PKEY *pkey;
+ BIO *out;
+ BUF_MEM *buf;
+ VALUE str;
+
+ GetPKeyDH(self, pkey);
+ if (!(out = BIO_new(BIO_s_mem()))) {
+ ossl_raise(eDHError, NULL);
+ }
+ if (!DHparams_print(out, pkey->pkey.dh)) {
+ BIO_free(out);
+ ossl_raise(eDHError, NULL);
+ }
+ BIO_get_mem_ptr(out, &buf);
+ str = rb_str_new(buf->data, buf->length);
+ BIO_free(out);
+
+ return str;
+}
+
+/*
+ * Makes new instance DH PUBLIC_KEY from PRIVATE_KEY
+ */
+static VALUE
+ossl_dh_to_public_key(VALUE self)
+{
+ EVP_PKEY *pkey;
+ DH *dh;
+ VALUE obj;
+
+ GetPKeyDH(self, pkey);
+ dh = DHparams_dup(pkey->pkey.dh); /* err check perfomed by dh_instance */
+ obj = dh_instance(CLASS_OF(self), dh);
+ if (obj == Qfalse) {
+ DH_free(dh);
+ ossl_raise(eDHError, NULL);
+ }
+
+ return obj;
+}
+
+static VALUE
+ossl_dh_check_params(VALUE self)
+{
+ DH *dh;
+ EVP_PKEY *pkey;
+ int codes;
+
+ GetPKeyDH(self, pkey);
+ dh = pkey->pkey.dh;
+
+ if (!DH_check(dh, &codes)) {
+ return Qfalse;
+ }
+
+ return codes == 0 ? Qtrue : Qfalse;
+}
+
+static VALUE
+ossl_dh_generate_key(VALUE self)
+{
+ DH *dh;
+ EVP_PKEY *pkey;
+
+ GetPKeyDH(self, pkey);
+ dh = pkey->pkey.dh;
+
+ if (!DH_generate_key(dh))
+ ossl_raise(eDHError, "Failed to generate key");
+ return self;
+}
+
+static VALUE
+ossl_dh_compute_key(VALUE self, VALUE pub)
+{
+ DH *dh;
+ EVP_PKEY *pkey;
+ BIGNUM *pub_key;
+ VALUE str;
+ int len;
+ char *buf;
+
+ GetPKeyDH(self, pkey);
+ dh = pkey->pkey.dh;
+ pub_key = GetBNPtr(pub);
+
+ len = DH_size(dh);
+ if (!(buf = OPENSSL_malloc(len))) {
+ ossl_raise(eDHError, "Cannot allocate mem for shared secret");
+ }
+
+ if ((len = DH_compute_key(buf, pub_key, dh)) < 0) {
+ OPENSSL_free(buf);
+ ossl_raise(eDHError, NULL);
+ }
+
+ str = rb_str_new(buf, len);
+ OPENSSL_free(buf);
+
+ return str;
+}
+
+/*
+ * INIT
+ */
+void
+Init_ossl_dh()
+{
+ eDHError = rb_define_class_under(mPKey, "DHError", ePKeyError);
+
+ cDH = rb_define_class_under(mPKey, "DH", cPKey);
+
+ rb_define_singleton_method(cDH, "generate", ossl_dh_s_generate, -1);
+ rb_define_method(cDH, "initialize", ossl_dh_initialize, -1);
+
+ rb_define_method(cDH, "public?", ossl_dh_is_public, 0);
+ rb_define_method(cDH, "private?", ossl_dh_is_private, 0);
+ rb_define_method(cDH, "to_text", ossl_dh_to_text, 0);
+ rb_define_method(cDH, "export", ossl_dh_export, 0);
+ rb_define_alias(cDH, "to_pem", "export");
+ rb_define_alias(cDH, "to_s", "export");
+ rb_define_method(cDH, "public_key", ossl_dh_to_public_key, 0);
+
+ rb_define_method(cDH, "params_ok?", ossl_dh_check_params, 0);
+ rb_define_method(cDH, "generate_key!", ossl_dh_generate_key, 0);
+ rb_define_method(cDH, "compute_key", ossl_dh_compute_key, 1);
+
+ rb_define_method(cDH, "params", ossl_dh_get_params, 0);
+}
+
+#else /* defined NO_DH */
+# warning >>> OpenSSL is compiled without DH support <<<
+
+void
+Init_ossl_dh()
+{
+ rb_warning("OpenSSL is compiled without DH support");
+}
+#endif /* NO_DH */
+
diff --git a/ext/openssl/ossl_pkey_dsa.c b/ext/openssl/ossl_pkey_dsa.c
new file mode 100644
index 0000000000..73ab47b2c9
--- /dev/null
+++ b/ext/openssl/ossl_pkey_dsa.c
@@ -0,0 +1,404 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#if !defined(OPENSSL_NO_DSA)
+
+#include "ossl.h"
+
+#define GetPKeyDSA(obj, pkey) do { \
+ GetPKey(obj, pkey); \
+ if (EVP_PKEY_type(pkey->type) != EVP_PKEY_DSA) { /* PARANOIA? */ \
+ ossl_raise(rb_eRuntimeError, "THIS IS NOT A DSA!"); \
+ } \
+} while (0)
+
+#define DSA_PRIVATE(dsa) ((dsa)->priv_key)
+
+/*
+ * Classes
+ */
+VALUE cDSA;
+VALUE eDSAError;
+
+/*
+ * Public
+ */
+static VALUE
+dsa_instance(VALUE klass, DSA *dsa)
+{
+ EVP_PKEY *pkey;
+ VALUE obj;
+
+ if (!dsa) {
+ return Qfalse;
+ }
+ if (!(pkey = EVP_PKEY_new())) {
+ return Qfalse;
+ }
+ if (!EVP_PKEY_assign_DSA(pkey, dsa)) {
+ EVP_PKEY_free(pkey);
+ return Qfalse;
+ }
+ WrapPKey(klass, obj, pkey);
+
+ return obj;
+}
+
+VALUE
+ossl_dsa_new(EVP_PKEY *pkey)
+{
+ VALUE obj;
+
+ if (!pkey) {
+ obj = dsa_instance(cDSA, DSA_new());
+ } else {
+ if (EVP_PKEY_type(pkey->type) != EVP_PKEY_DSA) {
+ ossl_raise(rb_eTypeError, "Not a DSA key!");
+ }
+ WrapPKey(cDSA, obj, pkey);
+ }
+ if (obj == Qfalse) {
+ ossl_raise(eDSAError, NULL);
+ }
+
+ return obj;
+}
+
+/*
+ * Private
+ */
+/*
+ * CB for yielding when generating DSA params
+ */
+static void
+ossl_dsa_generate_cb(int p, int n, void *arg)
+{
+ VALUE ary;
+
+ ary = rb_ary_new2(2);
+ rb_ary_store(ary, 0, INT2NUM(p));
+ rb_ary_store(ary, 1, INT2NUM(n));
+
+ rb_yield(ary);
+}
+
+static DSA *
+dsa_generate(int size)
+{
+ DSA *dsa;
+ unsigned char seed[20];
+ int seed_len = 20, counter;
+ unsigned long h;
+ void (*cb)(int, int, void *) = NULL;
+
+ if (!RAND_bytes(seed, seed_len)) {
+ return 0;
+ }
+ if (rb_block_given_p()) {
+ cb = ossl_dsa_generate_cb;
+ }
+ dsa = DSA_generate_parameters(size, seed, seed_len, &counter, &h, cb, NULL);
+ if(!dsa) { /* arg to cb = NULL */
+ return 0;
+ }
+ if (!DSA_generate_key(dsa)) {
+ DSA_free(dsa);
+ return 0;
+ }
+
+ return dsa;
+}
+
+static VALUE
+ossl_dsa_s_generate(VALUE klass, VALUE size)
+{
+ DSA *dsa = dsa_generate(FIX2INT(size)); /* err handled by dsa_instance */
+ VALUE obj = dsa_instance(klass, dsa);
+
+ if (obj == Qfalse) {
+ DSA_free(dsa);
+ ossl_raise(eDSAError, NULL);
+ }
+
+ return obj;
+}
+
+static VALUE
+ossl_dsa_initialize(int argc, VALUE *argv, VALUE self)
+{
+ EVP_PKEY *pkey;
+ DSA *dsa;
+ BIO *in;
+ char *passwd = NULL;
+ VALUE buffer, pass;
+
+ GetPKey(self, pkey);
+ rb_scan_args(argc, argv, "11", &buffer, &pass);
+ if (FIXNUM_P(buffer)) {
+ if (!(dsa = dsa_generate(FIX2INT(buffer)))) {
+ ossl_raise(eDSAError, NULL);
+ }
+ } else {
+ StringValue(buffer);
+ if (!NIL_P(pass)) {
+ passwd = StringValuePtr(pass);
+ }
+ in = BIO_new_mem_buf(RSTRING(buffer)->ptr, RSTRING(buffer)->len);
+ if (!in){
+ ossl_raise(eDSAError, NULL);
+ }
+
+ dsa = PEM_read_bio_DSAPrivateKey(in, NULL, ossl_pem_passwd_cb, passwd);
+ if (!dsa) {
+ BIO_reset(in);
+
+ dsa = PEM_read_bio_DSAPublicKey(in, NULL, NULL, NULL);
+ }
+ if (!dsa) {
+ BIO_reset(in);
+
+ dsa = PEM_read_bio_DSA_PUBKEY(in, NULL, NULL, NULL);
+ }
+ if (!dsa) {
+ BIO_free(in);
+ ossl_raise(eDSAError, "Neither PUB key nor PRIV key:");
+ }
+ BIO_free(in);
+ }
+ if (!EVP_PKEY_assign_DSA(pkey, dsa)) {
+ DSA_free(dsa);
+ ossl_raise(eDSAError, NULL);
+ }
+
+ return self;
+}
+
+static VALUE
+ossl_dsa_is_public(VALUE self)
+{
+ EVP_PKEY *pkey;
+
+ GetPKeyDSA(self, pkey);
+
+ /*
+ * Do we need to check dsap->dsa->public_pkey?
+ * return Qtrue;
+ */
+ return (pkey->pkey.dsa->pub_key) ? Qtrue : Qfalse;
+}
+
+static VALUE
+ossl_dsa_is_private(VALUE self)
+{
+ EVP_PKEY *pkey;
+
+ GetPKeyDSA(self, pkey);
+
+ return (DSA_PRIVATE(pkey->pkey.dsa)) ? Qtrue : Qfalse;
+}
+
+static VALUE
+ossl_dsa_export(int argc, VALUE *argv, VALUE self)
+{
+ EVP_PKEY *pkey;
+ BIO *out;
+ BUF_MEM *buf;
+ const EVP_CIPHER *ciph = NULL;
+ char *passwd = NULL;
+ VALUE cipher, pass, str;
+
+ GetPKeyDSA(self, pkey);
+ rb_scan_args(argc, argv, "02", &cipher, &pass);
+ if (!NIL_P(cipher)) {
+ ciph = GetCipherPtr(cipher);
+ if (!NIL_P(pass)) {
+ passwd = StringValuePtr(pass);
+ }
+ }
+ if (!(out = BIO_new(BIO_s_mem()))) {
+ ossl_raise(eDSAError, NULL);
+ }
+ if (DSA_PRIVATE(pkey->pkey.dsa)) {
+ if (!PEM_write_bio_DSAPrivateKey(out, pkey->pkey.dsa, ciph,
+ NULL, 0, ossl_pem_passwd_cb, passwd)){
+ BIO_free(out);
+ ossl_raise(eDSAError, NULL);
+ }
+ } else {
+ if (!PEM_write_bio_DSAPublicKey(out, pkey->pkey.dsa)) {
+ BIO_free(out);
+ ossl_raise(eDSAError, NULL);
+ }
+ }
+ BIO_get_mem_ptr(out, &buf);
+ str = rb_str_new(buf->data, buf->length);
+ BIO_free(out);
+
+ return str;
+}
+
+/*
+ * Stores all parameters of key to the hash
+ * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!!
+ * Don't use :-)) (I's up to you)
+ */
+static VALUE
+ossl_dsa_get_params(VALUE self)
+{
+ EVP_PKEY *pkey;
+ VALUE hash;
+
+ GetPKeyDSA(self, pkey);
+
+ hash = rb_hash_new();
+
+ rb_hash_aset(hash, rb_str_new2("p"), ossl_bn_new(pkey->pkey.dsa->p));
+ rb_hash_aset(hash, rb_str_new2("q"), ossl_bn_new(pkey->pkey.dsa->q));
+ rb_hash_aset(hash, rb_str_new2("g"), ossl_bn_new(pkey->pkey.dsa->g));
+ rb_hash_aset(hash, rb_str_new2("pub_key"), ossl_bn_new(pkey->pkey.dsa->pub_key));
+ rb_hash_aset(hash, rb_str_new2("priv_key"), ossl_bn_new(pkey->pkey.dsa->priv_key));
+
+ return hash;
+}
+
+/*
+ * Prints all parameters of key to buffer
+ * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!!
+ * Don't use :-)) (I's up to you)
+ */
+static VALUE
+ossl_dsa_to_text(VALUE self)
+{
+ EVP_PKEY *pkey;
+ BIO *out;
+ BUF_MEM *buf;
+ VALUE str;
+
+ GetPKeyDSA(self, pkey);
+ if (!(out = BIO_new(BIO_s_mem()))) {
+ ossl_raise(eDSAError, NULL);
+ }
+ if (!DSA_print(out, pkey->pkey.dsa, 0)) { //offset = 0
+ BIO_free(out);
+ ossl_raise(eDSAError, NULL);
+ }
+ BIO_get_mem_ptr(out, &buf);
+ str = rb_str_new(buf->data, buf->length);
+ BIO_free(out);
+
+ return str;
+}
+
+/*
+ * Makes new instance DSA PUBLIC_KEY from PRIVATE_KEY
+ */
+static VALUE
+ossl_dsa_to_public_key(VALUE self)
+{
+ EVP_PKEY *pkey;
+ DSA *dsa;
+ VALUE obj;
+
+ GetPKeyDSA(self, pkey);
+ /* err check performed by dsa_instance */
+ dsa = DSAPublicKey_dup(pkey->pkey.dsa);
+ obj = dsa_instance(CLASS_OF(self), dsa);
+ if (obj == Qfalse) {
+ DSA_free(dsa);
+ ossl_raise(eDSAError, NULL);
+ }
+ return obj;
+}
+
+static VALUE
+ossl_dsa_sign(VALUE self, VALUE data)
+{
+ EVP_PKEY *pkey;
+ char *buf;
+ int buf_len;
+ VALUE str;
+
+ GetPKeyDSA(self, pkey);
+ StringValue(data);
+ if (!DSA_PRIVATE(pkey->pkey.dsa)) {
+ ossl_raise(eDSAError, "Private DSA key needed!");
+ }
+ if (!(buf = OPENSSL_malloc(DSA_size(pkey->pkey.dsa) + 16))) {
+ ossl_raise(eDSAError, NULL);
+ }
+ if (!DSA_sign(0, RSTRING(data)->ptr, RSTRING(data)->len, buf,
+ &buf_len, pkey->pkey.dsa)) { /* type is ignored (0) */
+ OPENSSL_free(buf);
+ ossl_raise(eDSAError, NULL);
+ }
+ str = rb_str_new(buf, buf_len);
+ OPENSSL_free(buf);
+
+ return str;
+}
+
+static VALUE
+ossl_dsa_verify(VALUE self, VALUE digest, VALUE sig)
+{
+ EVP_PKEY *pkey;
+ int ret;
+
+ GetPKeyDSA(self, pkey);
+ StringValue(digest);
+ StringValue(sig);
+ /* type is ignored (0) */
+ ret = DSA_verify(0, RSTRING(digest)->ptr, RSTRING(digest)->len,
+ RSTRING(sig)->ptr, RSTRING(sig)->len, pkey->pkey.dsa);
+ if (ret < 0) {
+ ossl_raise(eDSAError, NULL);
+ }
+ else if (ret == 1) {
+ return Qtrue;
+ }
+
+ return Qfalse;
+}
+
+/*
+ * INIT
+ */
+void
+Init_ossl_dsa()
+{
+ eDSAError = rb_define_class_under(mPKey, "DSAError", ePKeyError);
+
+ cDSA = rb_define_class_under(mPKey, "DSA", cPKey);
+
+ rb_define_singleton_method(cDSA, "generate", ossl_dsa_s_generate, 1);
+ rb_define_method(cDSA, "initialize", ossl_dsa_initialize, -1);
+
+ rb_define_method(cDSA, "public?", ossl_dsa_is_public, 0);
+ rb_define_method(cDSA, "private?", ossl_dsa_is_private, 0);
+ rb_define_method(cDSA, "to_text", ossl_dsa_to_text, 0);
+ rb_define_method(cDSA, "export", ossl_dsa_export, -1);
+ rb_define_alias(cDSA, "to_pem", "export");
+ rb_define_alias(cDSA, "to_s", "export");
+ rb_define_method(cDSA, "public_key", ossl_dsa_to_public_key, 0);
+ rb_define_method(cDSA, "syssign", ossl_dsa_sign, 1);
+ rb_define_method(cDSA, "sysverify", ossl_dsa_verify, 2);
+
+ rb_define_method(cDSA, "params", ossl_dsa_get_params, 0);
+}
+
+#else /* defined NO_DSA */
+# warning >>> OpenSSL is compiled without DSA support <<<
+
+void
+Init_ossl_dsa()
+{
+ rb_warning("OpenSSL is compiled without DSA support");
+}
+
+#endif /* NO_DSA */
diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c
new file mode 100644
index 0000000000..c3d23666c9
--- /dev/null
+++ b/ext/openssl/ossl_pkey_rsa.c
@@ -0,0 +1,515 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#if !defined(OPENSSL_NO_RSA)
+
+#include "ossl.h"
+
+#define GetPKeyRSA(obj, pkey) do { \
+ GetPKey(obj, pkey); \
+ if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) { /* PARANOIA? */ \
+ ossl_raise(rb_eRuntimeError, "THIS IS NOT A RSA!") ; \
+ } \
+} while (0)
+
+#define RSA_PRIVATE(rsa) ((rsa)->p && (rsa)->q)
+
+/*
+ * Classes
+ */
+VALUE cRSA;
+VALUE eRSAError;
+
+/*
+ * Public
+ */
+static VALUE
+rsa_instance(VALUE klass, RSA *rsa)
+{
+ EVP_PKEY *pkey;
+ VALUE obj;
+
+ if (!rsa) {
+ return Qfalse;
+ }
+ if (!(pkey = EVP_PKEY_new())) {
+ return Qfalse;
+ }
+ if (!EVP_PKEY_assign_RSA(pkey, rsa)) {
+ EVP_PKEY_free(pkey);
+ return Qfalse;
+ }
+ WrapPKey(klass, obj, pkey);
+
+ return obj;
+}
+
+VALUE
+ossl_rsa_new(EVP_PKEY *pkey)
+{
+ VALUE obj;
+
+ if (!pkey) {
+ obj = rsa_instance(cRSA, RSA_new());
+ }
+ else {
+ if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) {
+ ossl_raise(rb_eTypeError, "Not a RSA key!");
+ }
+ WrapPKey(cRSA, obj, pkey);
+ }
+ if (obj == Qfalse) {
+ ossl_raise(eRSAError, NULL);
+ }
+
+ return obj;
+}
+
+/*
+ * Private
+ */
+/*
+ * CB for yielding when generating RSA data
+ */
+static void
+ossl_rsa_generate_cb(int p, int n, void *arg)
+{
+ VALUE ary;
+
+ ary = rb_ary_new2(2);
+ rb_ary_store(ary, 0, INT2NUM(p));
+ rb_ary_store(ary, 1, INT2NUM(n));
+
+ rb_yield(ary);
+}
+
+static RSA *
+rsa_generate(int size, int exp)
+{
+ void (*cb)(int, int, void *) = NULL;
+
+ if (rb_block_given_p()) {
+ cb = ossl_rsa_generate_cb;
+ }
+ return RSA_generate_key(size, exp, cb, NULL);
+}
+
+static VALUE
+ossl_rsa_s_generate(int argc, VALUE *argv, VALUE klass)
+{
+ RSA *rsa;
+ VALUE size, exp;
+ VALUE obj;
+
+ rb_scan_args(argc, argv, "11", &size, &exp);
+
+ rsa = rsa_generate(NUM2INT(size), NIL_P(exp) ? RSA_F4 : NUM2INT(exp)); /* err handled by rsa_instance */
+ obj = rsa_instance(klass, rsa);
+
+ if (obj == Qfalse) {
+ RSA_free(rsa);
+ ossl_raise(eRSAError, NULL);
+ }
+
+ return obj;
+}
+
+static VALUE
+ossl_rsa_initialize(int argc, VALUE *argv, VALUE self)
+{
+ EVP_PKEY *pkey;
+ RSA *rsa;
+ BIO *in;
+ char *passwd = NULL;
+ VALUE buffer, pass;
+
+ GetPKey(self, pkey);
+
+ rb_scan_args(argc, argv, "11", &buffer, &pass);
+
+ if (FIXNUM_P(buffer)) {
+ rsa = rsa_generate(FIX2INT(buffer), NIL_P(pass) ? RSA_F4 : NUM2INT(pass));
+ if (!rsa) {
+ ossl_raise(eRSAError, NULL);
+ }
+ }
+ else {
+ StringValue(buffer);
+ if (!NIL_P(pass)) {
+ passwd = StringValuePtr(pass);
+ }
+ if (!(in = BIO_new_mem_buf(RSTRING(buffer)->ptr, RSTRING(buffer)->len))){
+ ossl_raise(eRSAError, NULL);
+ }
+
+ rsa = PEM_read_bio_RSAPrivateKey(in, NULL, ossl_pem_passwd_cb, passwd);
+ if (!rsa) {
+ BIO_reset(in);
+
+ rsa = PEM_read_bio_RSAPublicKey(in, NULL, NULL, NULL);
+ }
+ if (!rsa) {
+ BIO_reset(in);
+
+ rsa = PEM_read_bio_RSA_PUBKEY(in, NULL, NULL, NULL);
+ }
+ BIO_free(in);
+
+ if (!rsa) {
+ ossl_raise(eRSAError, "Neither PUB key nor PRIV key:");
+ }
+ }
+ if (!EVP_PKEY_assign_RSA(pkey, rsa)) {
+ RSA_free(rsa);
+ ossl_raise(eRSAError, NULL);
+ }
+
+ return self;
+}
+
+static VALUE
+ossl_rsa_is_public(VALUE self)
+{
+ EVP_PKEY *pkey;
+
+ GetPKeyRSA(self, pkey);
+ /*
+ * SURPRISE! :-))
+ * Every key is public at the same time!
+ */
+ return Qtrue;
+}
+
+static VALUE
+ossl_rsa_is_private(VALUE self)
+{
+ EVP_PKEY *pkey;
+
+ GetPKeyRSA(self, pkey);
+
+ return (RSA_PRIVATE(pkey->pkey.rsa)) ? Qtrue : Qfalse;
+}
+
+static VALUE
+ossl_rsa_export(int argc, VALUE *argv, VALUE self)
+{
+ EVP_PKEY *pkey;
+ BIO *out;
+ BUF_MEM *buf;
+ const EVP_CIPHER *ciph = NULL;
+ char *passwd = NULL;
+ VALUE cipher, pass, str;
+
+ GetPKeyRSA(self, pkey);
+
+ rb_scan_args(argc, argv, "02", &cipher, &pass);
+
+ if (!NIL_P(cipher)) {
+ ciph = GetCipherPtr(cipher);
+ if (!NIL_P(pass)) {
+ passwd = StringValuePtr(pass);
+ }
+ }
+ if (!(out = BIO_new(BIO_s_mem()))) {
+ ossl_raise(eRSAError, NULL);
+ }
+ if (RSA_PRIVATE(pkey->pkey.rsa)) {
+ if (!PEM_write_bio_RSAPrivateKey(out, pkey->pkey.rsa, ciph,
+ NULL, 0, ossl_pem_passwd_cb, passwd)) {
+ BIO_free(out);
+ ossl_raise(eRSAError, NULL);
+ }
+ } else {
+ if (!PEM_write_bio_RSAPublicKey(out, pkey->pkey.rsa)) {
+ BIO_free(out);
+ ossl_raise(eRSAError, NULL);
+ }
+ }
+ BIO_get_mem_ptr(out, &buf);
+ str = rb_str_new(buf->data, buf->length);
+ BIO_free(out);
+
+ return str;
+}
+
+static VALUE
+ossl_rsa_public_encrypt(VALUE self, VALUE buffer)
+{
+ EVP_PKEY *pkey;
+ char *buf;
+ int buf_len;
+ VALUE str;
+
+ GetPKeyRSA(self, pkey);
+
+ StringValue(buffer);
+
+ if (!(buf = OPENSSL_malloc(RSA_size(pkey->pkey.rsa) + 16))) {
+ ossl_raise(eRSAError, NULL);
+ }
+ buf_len = RSA_public_encrypt(RSTRING(buffer)->len, RSTRING(buffer)->ptr,
+ buf, pkey->pkey.rsa, RSA_PKCS1_PADDING);
+ if (buf_len < 0){
+ OPENSSL_free(buf);
+ ossl_raise(eRSAError, NULL);
+ }
+ str = rb_str_new(buf, buf_len);
+ OPENSSL_free(buf);
+
+ return str;
+}
+
+static VALUE
+ossl_rsa_public_decrypt(VALUE self, VALUE buffer)
+{
+ EVP_PKEY *pkey;
+ char *buf;
+ int buf_len;
+ VALUE str;
+
+ GetPKeyRSA(self, pkey);
+ StringValue(buffer);
+ if (!(buf = OPENSSL_malloc(RSA_size(pkey->pkey.rsa) + 16))) {
+ ossl_raise(eRSAError, NULL);
+ }
+ buf_len = RSA_public_decrypt(RSTRING(buffer)->len, RSTRING(buffer)->ptr,
+ buf, pkey->pkey.rsa, RSA_PKCS1_PADDING);
+ if(buf_len < 0) {
+ OPENSSL_free(buf);
+ ossl_raise(eRSAError, NULL);
+ }
+ str = rb_str_new(buf, buf_len);
+ OPENSSL_free(buf);
+
+ return str;
+}
+
+static VALUE
+ossl_rsa_private_encrypt(VALUE self, VALUE buffer)
+{
+ EVP_PKEY *pkey;
+ char *buf;
+ int buf_len;
+ VALUE str;
+
+ GetPKeyRSA(self, pkey);
+ if (!RSA_PRIVATE(pkey->pkey.rsa)) {
+ ossl_raise(eRSAError, "PRIVATE key needed for this operation!");
+ }
+ StringValue(buffer);
+ if (!(buf = OPENSSL_malloc(RSA_size(pkey->pkey.rsa) + 16))) {
+ ossl_raise(eRSAError, "Memory alloc error");
+ }
+ buf_len = RSA_private_encrypt(RSTRING(buffer)->len, RSTRING(buffer)->ptr,
+ buf, pkey->pkey.rsa, RSA_PKCS1_PADDING);
+ if (buf_len < 0){
+ OPENSSL_free(buf);
+ ossl_raise(eRSAError, NULL);
+ }
+ str = rb_str_new(buf, buf_len);
+ OPENSSL_free(buf);
+
+ return str;
+}
+
+static VALUE
+ossl_rsa_private_decrypt(VALUE self, VALUE buffer)
+{
+ EVP_PKEY *pkey;
+ char *buf;
+ int buf_len;
+ VALUE str;
+
+ GetPKeyRSA(self, pkey);
+ if (!RSA_PRIVATE(pkey->pkey.rsa)) {
+ ossl_raise(eRSAError, "Private RSA key needed!");
+ }
+ StringValue(buffer);
+ if (!(buf = OPENSSL_malloc(RSA_size(pkey->pkey.rsa) + 16))) {
+ ossl_raise(eRSAError, "Memory alloc error");
+ }
+ buf_len = RSA_private_decrypt(RSTRING(buffer)->len, RSTRING(buffer)->ptr,
+ buf, pkey->pkey.rsa, RSA_PKCS1_PADDING);
+ if(buf_len < 0) {
+ OPENSSL_free(buf);
+ ossl_raise(eRSAError, NULL);
+ }
+ str = rb_str_new(buf, buf_len);
+ OPENSSL_free(buf);
+
+ return str;
+}
+
+/*
+ * Stores all parameters of key to the hash
+ * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!!
+ * Don't use :-)) (I's up to you)
+ */
+static VALUE
+ossl_rsa_get_params(VALUE self)
+{
+ EVP_PKEY *pkey;
+ VALUE hash;
+
+ GetPKeyRSA(self, pkey);
+
+ hash = rb_hash_new();
+
+ rb_hash_aset(hash, rb_str_new2("n"), ossl_bn_new(pkey->pkey.rsa->n));
+ rb_hash_aset(hash, rb_str_new2("e"), ossl_bn_new(pkey->pkey.rsa->e));
+ rb_hash_aset(hash, rb_str_new2("d"), ossl_bn_new(pkey->pkey.rsa->d));
+ rb_hash_aset(hash, rb_str_new2("p"), ossl_bn_new(pkey->pkey.rsa->p));
+ rb_hash_aset(hash, rb_str_new2("q"), ossl_bn_new(pkey->pkey.rsa->q));
+ rb_hash_aset(hash, rb_str_new2("dmp1"), ossl_bn_new(pkey->pkey.rsa->dmp1));
+ rb_hash_aset(hash, rb_str_new2("dmq1"), ossl_bn_new(pkey->pkey.rsa->dmq1));
+ rb_hash_aset(hash, rb_str_new2("iqmp"), ossl_bn_new(pkey->pkey.rsa->iqmp));
+
+ return hash;
+}
+
+/*
+ * Prints all parameters of key to buffer
+ * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!!
+ * Don't use :-)) (I's up to you)
+ */
+static VALUE
+ossl_rsa_to_text(VALUE self)
+{
+ EVP_PKEY *pkey;
+ BIO *out;
+ BUF_MEM *buf;
+ VALUE str;
+
+ GetPKeyRSA(self, pkey);
+ if (!(out = BIO_new(BIO_s_mem()))) {
+ ossl_raise(eRSAError, NULL);
+ }
+ if (!RSA_print(out, pkey->pkey.rsa, 0)) { //offset = 0
+ BIO_free(out);
+ ossl_raise(eRSAError, NULL);
+ }
+ BIO_get_mem_ptr(out, &buf);
+ str = rb_str_new(buf->data, buf->length);
+ BIO_free(out);
+
+ return str;
+}
+
+/*
+ * Makes new instance RSA PUBLIC_KEY from PRIVATE_KEY
+ */
+static VALUE
+ossl_rsa_to_public_key(VALUE self)
+{
+ EVP_PKEY *pkey;
+ RSA *rsa;
+ VALUE obj;
+
+ GetPKeyRSA(self, pkey);
+ /* err check performed by rsa_instance */
+ rsa = RSAPublicKey_dup(pkey->pkey.rsa);
+ obj = rsa_instance(CLASS_OF(self), rsa);
+ if (obj == Qfalse) {
+ RSA_free(rsa);
+ ossl_raise(eRSAError, NULL);
+ }
+ return obj;
+}
+
+/*
+ * TODO: Test me
+extern BN_CTX *ossl_bn_ctx;
+
+static VALUE
+ossl_rsa_blinding_on(VALUE self)
+{
+ EVP_PKEY *pkey;
+
+ GetPKeyRSA(self, pkey);
+
+ if (RSA_blinding_on(pkey->pkey.rsa, ossl_bn_ctx) != 1) {
+ ossl_raise(eRSAError, NULL);
+ }
+ return self;
+}
+
+static VALUE
+ossl_rsa_blinding_off(VALUE self)
+{
+ EVP_PKEY *pkey;
+
+ GetPKeyRSA(self, pkey);
+ RSA_blinding_off(pkey->pkey.rsa);
+
+ return self;
+}
+ */
+
+OSSL_PKEY_BN(rsa, n);
+OSSL_PKEY_BN(rsa, e);
+OSSL_PKEY_BN(rsa, d);
+OSSL_PKEY_BN(rsa, p);
+OSSL_PKEY_BN(rsa, q);
+OSSL_PKEY_BN(rsa, dmp1);
+OSSL_PKEY_BN(rsa, dmq1);
+OSSL_PKEY_BN(rsa, iqmp);
+
+/*
+ * INIT
+ */
+void
+Init_ossl_rsa()
+{
+ eRSAError = rb_define_class_under(mPKey, "RSAError", ePKeyError);
+
+ cRSA = rb_define_class_under(mPKey, "RSA", cPKey);
+
+ rb_define_singleton_method(cRSA, "generate", ossl_rsa_s_generate, -1);
+ rb_define_method(cRSA, "initialize", ossl_rsa_initialize, -1);
+
+ rb_define_method(cRSA, "public?", ossl_rsa_is_public, 0);
+ rb_define_method(cRSA, "private?", ossl_rsa_is_private, 0);
+ rb_define_method(cRSA, "to_text", ossl_rsa_to_text, 0);
+ rb_define_method(cRSA, "export", ossl_rsa_export, -1);
+ rb_define_alias(cRSA, "to_pem", "export");
+ rb_define_alias(cRSA, "to_s", "export");
+ rb_define_method(cRSA, "public_key", ossl_rsa_to_public_key, 0);
+ rb_define_method(cRSA, "public_encrypt", ossl_rsa_public_encrypt, 1);
+ rb_define_method(cRSA, "public_decrypt", ossl_rsa_public_decrypt, 1);
+ rb_define_method(cRSA, "private_encrypt", ossl_rsa_private_encrypt, 1);
+ rb_define_method(cRSA, "private_decrypt", ossl_rsa_private_decrypt, 1);
+
+ DEF_OSSL_PKEY_BN(cRSA, rsa, n);
+ DEF_OSSL_PKEY_BN(cRSA, rsa, e);
+ DEF_OSSL_PKEY_BN(cRSA, rsa, d);
+ DEF_OSSL_PKEY_BN(cRSA, rsa, p);
+ DEF_OSSL_PKEY_BN(cRSA, rsa, q);
+ DEF_OSSL_PKEY_BN(cRSA, rsa, dmp1);
+ DEF_OSSL_PKEY_BN(cRSA, rsa, dmq1);
+ DEF_OSSL_PKEY_BN(cRSA, rsa, iqmp);
+
+ rb_define_method(cRSA, "params", ossl_rsa_get_params, 0);
+
+/*
+ * TODO: Test it
+ rb_define_method(cRSA, "blinding_on!", ossl_rsa_blinding_on, 0);
+ rb_define_method(cRSA, "blinding_off!", ossl_rsa_blinding_off, 0);
+ */
+}
+
+#else /* defined NO_RSA */
+# warning >>> OpenSSL is compiled without RSA support <<<
+void
+Init_ossl_rsa()
+{
+ rb_warning("OpenSSL is compiled without RSA support");
+}
+#endif /* NO_RSA */
+
diff --git a/ext/openssl/ossl_rand.c b/ext/openssl/ossl_rand.c
new file mode 100644
index 0000000000..c30889221e
--- /dev/null
+++ b/ext/openssl/ossl_rand.c
@@ -0,0 +1,142 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#include "ossl.h"
+
+/*
+ * Classes
+ */
+VALUE mRandom;
+VALUE eRandomError;
+
+/*
+ * Struct
+ */
+
+/*
+ * Public
+ */
+
+/*
+ * Private
+ */
+static VALUE
+ossl_rand_seed(VALUE self, VALUE str)
+{
+ StringValue(str);
+ RAND_seed(RSTRING(str)->ptr, RSTRING(str)->len);
+
+ return str;
+}
+
+static VALUE
+ossl_rand_load_file(VALUE self, VALUE filename)
+{
+ SafeStringValue(filename);
+
+ if(!RAND_load_file(RSTRING(filename)->ptr, -1)) {
+ ossl_raise(eRandomError, NULL);
+ }
+ return Qtrue;
+}
+
+static VALUE
+ossl_rand_write_file(VALUE self, VALUE filename)
+{
+ SafeStringValue(filename);
+ if (RAND_write_file(RSTRING(filename)->ptr) == -1) {
+ ossl_raise(eRandomError, NULL);
+ }
+ return Qtrue;
+}
+
+static VALUE
+ossl_rand_bytes(VALUE self, VALUE len)
+{
+ unsigned char *buffer = NULL;
+ VALUE str;
+
+ if (!(buffer = OPENSSL_malloc(FIX2INT(len) + 1))) {
+ ossl_raise(eRandomError, NULL);
+ }
+ if (!RAND_bytes(buffer, FIX2INT(len))) {
+ OPENSSL_free(buffer);
+ ossl_raise(eRandomError, NULL);
+ }
+ str = rb_str_new(buffer, FIX2INT(len));
+ OPENSSL_free(buffer);
+
+ return str;
+}
+
+static VALUE
+ossl_rand_pseudo_bytes(VALUE self, VALUE len)
+{
+ unsigned char *buffer = NULL;
+ VALUE str;
+
+ if (!(buffer = OPENSSL_malloc(FIX2INT(len) + 1))) {
+ ossl_raise(eRandomError, NULL);
+ }
+ if (!RAND_pseudo_bytes(buffer, FIX2INT(len))) {
+ OPENSSL_free(buffer);
+ ossl_raise(eRandomError, NULL);
+ }
+ str = rb_str_new(buffer, FIX2INT(len));
+ OPENSSL_free(buffer);
+
+ return str;
+}
+
+static VALUE
+ossl_rand_egd(VALUE self, VALUE filename)
+{
+ SafeStringValue(filename);
+
+ if(!RAND_egd(RSTRING(filename)->ptr)) {
+ ossl_raise(eRandomError, NULL);
+ }
+ return Qtrue;
+}
+
+static VALUE
+ossl_rand_egd_bytes(VALUE self, VALUE filename, VALUE len)
+{
+ SafeStringValue(filename);
+
+ if (!RAND_egd_bytes(RSTRING(filename)->ptr, FIX2INT(len))) {
+ ossl_raise(eRandomError, NULL);
+ }
+ return Qtrue;
+}
+
+#define DEFMETH(class, name, func, argc) \
+ rb_define_method(class, name, func, argc); \
+ rb_define_singleton_method(class, name, func, argc);
+
+/*
+ * INIT
+ */
+void
+Init_ossl_rand()
+{
+ mRandom = rb_define_module_under(mOSSL, "Random");
+
+ eRandomError = rb_define_class_under(mRandom, "RandomError", eOSSLError);
+
+ DEFMETH(mRandom, "seed", ossl_rand_seed, 1);
+ DEFMETH(mRandom, "load_random_file", ossl_rand_load_file, 1);
+ DEFMETH(mRandom, "write_random_file", ossl_rand_write_file, 1);
+ DEFMETH(mRandom, "random_bytes", ossl_rand_bytes, 1);
+ DEFMETH(mRandom, "pseudo_bytes", ossl_rand_pseudo_bytes, 1);
+ DEFMETH(mRandom, "egd", ossl_rand_egd, 1);
+ DEFMETH(mRandom, "egd_bytes", ossl_rand_egd_bytes, 2);
+}
+
diff --git a/ext/openssl/ossl_rand.h b/ext/openssl/ossl_rand.h
new file mode 100644
index 0000000000..ce2ae0d129
--- /dev/null
+++ b/ext/openssl/ossl_rand.h
@@ -0,0 +1,20 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#if !defined(_OSSL_RAND_H_)
+#define _OSSL_RAND_H_
+
+extern VALUE mRandom;
+extern VALUE eRandomError;
+
+void Init_ossl_rand(void);
+
+#endif /* _OSSL_RAND_H_ */
+
diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c
new file mode 100644
index 0000000000..db28ed3640
--- /dev/null
+++ b/ext/openssl/ossl_ssl.c
@@ -0,0 +1,689 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2000-2002 GOTOU Yuuzou <gotoyuzo@notwork.org>
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#include "ossl.h"
+#include <rubysig.h>
+#include <rubyio.h>
+
+#if defined(HAVE_UNISTD_H)
+# include <unistd.h> /* for read(), and write() */
+#endif
+
+#define numberof(ary) (sizeof(ary)/sizeof(ary[0]))
+
+#ifdef _WIN32
+# define TO_SOCKET(s) _get_osfhandle(s)
+#else
+# define TO_SOCKET(s) s
+#endif
+
+VALUE mSSL;
+VALUE eSSLError;
+VALUE cSSLContext;
+VALUE cSSLSocket;
+
+/*
+ * SSLContext class
+ */
+#define ossl_sslctx_set_cert(o,v) rb_iv_set((o),"@cert",(v))
+#define ossl_sslctx_set_key(o,v) rb_iv_set((o),"@key",(v))
+#define ossl_sslctx_set_client_ca(o,v) rb_iv_set((o),"@client_ca",(v))
+#define ossl_sslctx_set_ca_file(o,v) rb_iv_set((o),"@ca_file",(v))
+#define ossl_sslctx_set_ca_path(o,v) rb_iv_set((o),"@ca_path",(v))
+#define ossl_sslctx_set_timeout(o,v) rb_iv_set((o),"@timeout",(v))
+#define ossl_sslctx_set_verify_mode(o,v) rb_iv_set((o),"@verify_mode",(v))
+#define ossl_sslctx_set_verify_dep(o,v) rb_iv_set((o),"@verify_depth",(v))
+#define ossl_sslctx_set_verify_cb(o,v) rb_iv_set((o),"@verify_callback",(v))
+#define ossl_sslctx_set_options(o,v) rb_iv_set((o),"@options",(v))
+#define ossl_sslctx_set_cert_store(o,v) rb_iv_set((o),"@cert_store",(v))
+
+#define ossl_sslctx_get_cert(o) rb_iv_get((o),"@cert")
+#define ossl_sslctx_get_key(o) rb_iv_get((o),"@key")
+#define ossl_sslctx_get_client_ca(o) rb_iv_get((o),"@client_ca")
+#define ossl_sslctx_get_ca_file(o) rb_iv_get((o),"@ca_file")
+#define ossl_sslctx_get_ca_path(o) rb_iv_get((o),"@ca_path")
+#define ossl_sslctx_get_timeout(o) rb_iv_get((o),"@timeout")
+#define ossl_sslctx_get_verify_mode(o) rb_iv_get((o),"@verify_mode")
+#define ossl_sslctx_get_verify_dep(o) rb_iv_get((o),"@verify_depth")
+#define ossl_sslctx_get_verify_cb(o) rb_iv_get((o),"@verify_callback")
+#define ossl_sslctx_get_options(o) rb_iv_get((o),"@options")
+#define ossl_sslctx_get_cert_store(o) rb_iv_get((o),"@cert_store")
+
+static char *ossl_sslctx_attrs[] = {
+ "cert", "key", "client_ca", "ca_file", "ca_path",
+ "timeout", "verify_mode", "verify_depth",
+ "verify_callback", "options", "cert_store",
+};
+
+struct {
+ const char *name;
+ SSL_METHOD *(*func)(void);
+} ossl_ssl_method_tab[] = {
+#define OSSL_SSL_METHOD_ENTRY(name) { #name, name##_method }
+ OSSL_SSL_METHOD_ENTRY(TLSv1),
+ OSSL_SSL_METHOD_ENTRY(TLSv1_server),
+ OSSL_SSL_METHOD_ENTRY(TLSv1_client),
+ OSSL_SSL_METHOD_ENTRY(SSLv2),
+ OSSL_SSL_METHOD_ENTRY(SSLv2_server),
+ OSSL_SSL_METHOD_ENTRY(SSLv2_client),
+ OSSL_SSL_METHOD_ENTRY(SSLv3),
+ OSSL_SSL_METHOD_ENTRY(SSLv3_server),
+ OSSL_SSL_METHOD_ENTRY(SSLv3_client),
+ OSSL_SSL_METHOD_ENTRY(SSLv23),
+ OSSL_SSL_METHOD_ENTRY(SSLv23_server),
+ OSSL_SSL_METHOD_ENTRY(SSLv23_client),
+#undef OSSL_SSL_METHOD_ENTRY
+};
+
+int ossl_ssl_ex_vcb_idx;
+int ossl_ssl_ex_store_p;
+
+static void
+ossl_sslctx_free(SSL_CTX *ctx)
+{
+ if(ctx && SSL_CTX_get_ex_data(ctx, ossl_ssl_ex_store_p)== (void*)1)
+ ctx->cert_store = NULL;
+ SSL_CTX_free(ctx);
+}
+
+static VALUE
+ossl_sslctx_s_alloc(VALUE klass)
+{
+ SSL_CTX *ctx;
+
+ ctx = SSL_CTX_new(SSLv23_method());
+ if (!ctx) {
+ ossl_raise(eSSLError, "SSL_CTX_new:");
+ }
+ SSL_CTX_set_options(ctx, SSL_OP_ALL);
+ return Data_Wrap_Struct(klass, 0, ossl_sslctx_free, ctx);
+}
+DEFINE_ALLOC_WRAPPER(ossl_sslctx_s_alloc)
+
+static VALUE
+ossl_sslctx_initialize(int argc, VALUE *argv, VALUE self)
+{
+ VALUE ssl_method;
+ SSL_METHOD *method = NULL;
+ SSL_CTX *ctx;
+ int i;
+ char *s;
+
+ Data_Get_Struct(self, SSL_CTX, ctx);
+
+ for(i = 0; i < numberof(ossl_sslctx_attrs); i++){
+ char buf[32];
+ snprintf(buf, sizeof(buf), "@%s", ossl_sslctx_attrs[i]);
+ rb_iv_set(self, buf, Qnil);
+ }
+ if (rb_scan_args(argc, argv, "01", &ssl_method) == 0){
+ return self;
+ }
+ if(TYPE(ssl_method) == T_SYMBOL)
+ s = rb_id2name(SYM2ID(ssl_method));
+ else
+ s = StringValuePtr(ssl_method);
+ for (i = 0; i < numberof(ossl_ssl_method_tab); i++) {
+ if (strcmp(ossl_ssl_method_tab[i].name, s) == 0) {
+ method = ossl_ssl_method_tab[i].func();
+ break;
+ }
+ }
+ if (!method) {
+ ossl_raise(rb_eArgError, "unknown SSL method `%s'.", s);
+ }
+ if (SSL_CTX_set_ssl_version(ctx, method) != 1) {
+ ossl_raise(eSSLError, "SSL_CTX_set_ssl_version:");
+ }
+
+ return self;
+}
+
+static int
+ossl_ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
+{
+ VALUE cb;
+ SSL *ssl;
+
+ ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+ cb = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_vcb_idx);
+ X509_STORE_CTX_set_ex_data(ctx, ossl_verify_cb_idx, (void*)cb);
+ return ossl_verify_cb(preverify_ok, ctx);
+}
+
+static VALUE
+ossl_sslctx_setup(VALUE self)
+{
+ SSL_CTX *ctx;
+ X509 *cert = NULL, *client_ca = NULL;
+ X509_STORE *store;
+ EVP_PKEY *key = NULL;
+ char *ca_path = NULL, *ca_file = NULL;
+ int i, verify_mode;
+ VALUE val;
+
+ if(OBJ_FROZEN(self)) return Qnil;
+ Data_Get_Struct(self, SSL_CTX, ctx);
+
+ val = ossl_sslctx_get_cert_store(self);
+ if(!NIL_P(val)){
+ /*
+ * WORKAROUND:
+ * X509_STORE can count references, but
+ * X509_STORE_free() doesn't care it.
+ * So we won't increment it but mark it by ex_data.
+ */
+ store = GetX509StorePtr(val); /* NO NEED TO DUP */
+ SSL_CTX_set_cert_store(ctx, store);
+ SSL_CTX_set_ex_data(ctx, ossl_ssl_ex_store_p, (void*)1);
+ }
+
+ /* private key may be bundled in certificate file. */
+ val = ossl_sslctx_get_cert(self);
+ cert = NIL_P(val) ? NULL : GetX509CertPtr(val); /* NO DUP NEEDED */
+ val = ossl_sslctx_get_key(self);
+ key = NIL_P(val) ? NULL : GetPKeyPtr(val); /* NO DUP NEEDED */
+ if (cert && key) {
+ if (!SSL_CTX_use_certificate(ctx, cert)) {
+ /* Adds a ref => Safe to FREE */
+ ossl_raise(eSSLError, "SSL_CTX_use_certificate:");
+ }
+ if (!SSL_CTX_use_PrivateKey(ctx, key)) {
+ /* Adds a ref => Safe to FREE */
+ ossl_raise(eSSLError, "SSL_CTX_use_PrivateKey:");
+ }
+ if (!SSL_CTX_check_private_key(ctx)) {
+ ossl_raise(eSSLError, "SSL_CTX_check_private_key:");
+ }
+ }
+
+ val = ossl_sslctx_get_client_ca(self);
+ if(!NIL_P(val)){
+ if(TYPE(val) == T_ARRAY){
+ for(i = 0; i < RARRAY(val)->len; i++){
+ client_ca = GetX509CertPtr(RARRAY(val)->ptr[i]);
+ if (!SSL_CTX_add_client_CA(ctx, client_ca)){
+ /* Copies X509_NAME => FREE it. */
+ ossl_raise(eSSLError, "SSL_CTX_add_client_CA");
+ }
+ }
+ }
+ else{
+ client_ca = GetX509CertPtr(val); /* NO DUP NEEDED. */
+ if (!SSL_CTX_add_client_CA(ctx, client_ca)){
+ /* Copies X509_NAME => FREE it. */
+ ossl_raise(eSSLError, "SSL_CTX_add_client_CA");
+ }
+ }
+ }
+
+ val = ossl_sslctx_get_ca_file(self);
+ ca_file = NIL_P(val) ? NULL : StringValuePtr(val);
+ val = ossl_sslctx_get_ca_path(self);
+ ca_path = NIL_P(val) ? NULL : StringValuePtr(val);
+ if(ca_file || ca_path){
+ if (!SSL_CTX_load_verify_locations(ctx, ca_file, ca_path))
+ rb_warning("can't set verify locations");
+ }
+
+ val = ossl_sslctx_get_verify_mode(self);
+ verify_mode = NIL_P(val) ? SSL_VERIFY_NONE : NUM2INT(val);
+ SSL_CTX_set_verify(ctx, verify_mode, ossl_ssl_verify_callback);
+
+ val = ossl_sslctx_get_timeout(self);
+ if(!NIL_P(val)) SSL_CTX_set_timeout(ctx, NUM2LONG(val));
+
+ val = ossl_sslctx_get_verify_dep(self);
+ if(!NIL_P(val)) SSL_CTX_set_verify_depth(ctx, NUM2LONG(val));
+
+ val = ossl_sslctx_get_options(self);
+ if(!NIL_P(val)) SSL_CTX_set_options(ctx, NUM2LONG(val));
+ rb_obj_freeze(self);
+
+ return Qtrue;
+}
+
+static VALUE
+ossl_ssl_cipher_to_ary(SSL_CIPHER *cipher)
+{
+ VALUE ary;
+ int bits, alg_bits;
+
+ ary = rb_ary_new2(4);
+ rb_ary_push(ary, rb_str_new2(SSL_CIPHER_get_name(cipher)));
+ rb_ary_push(ary, rb_str_new2(SSL_CIPHER_get_version(cipher)));
+ bits = SSL_CIPHER_get_bits(cipher, &alg_bits);
+ rb_ary_push(ary, INT2FIX(bits));
+ rb_ary_push(ary, INT2FIX(alg_bits));
+
+ return ary;
+}
+
+static VALUE
+ossl_sslctx_get_ciphers(VALUE self)
+{
+ SSL_CTX *ctx;
+ STACK_OF(SSL_CIPHER) *ciphers;
+ SSL_CIPHER *cipher;
+ VALUE ary;
+ int i, num;
+
+ Data_Get_Struct(self, SSL_CTX, ctx);
+ if(!ctx){
+ rb_warning("SSL_CTX is not initialized.");
+ return Qnil;
+ }
+ ciphers = ctx->cipher_list;
+
+ if (!ciphers)
+ return rb_ary_new();
+
+ num = sk_num((STACK*)ciphers);
+ ary = rb_ary_new2(num);
+ for(i = 0; i < num; i++){
+ cipher = (SSL_CIPHER*)sk_value((STACK*)ciphers, i);
+ rb_ary_push(ary, ossl_ssl_cipher_to_ary(cipher));
+ }
+ return ary;
+}
+
+static VALUE
+ossl_sslctx_set_ciphers(VALUE self, VALUE v)
+{
+ SSL_CTX *ctx;
+ VALUE str, elem;
+ int i;
+
+ rb_check_frozen(self);
+ Data_Get_Struct(self, SSL_CTX, ctx);
+ if(!ctx){
+ ossl_raise(eSSLError, "SSL_CTX is not initialized.");
+ return Qnil;
+ }
+
+ if (TYPE(v) == T_ARRAY) {
+ str = rb_str_new2(NULL);
+ for (i = 0; i < RARRAY(v)->len; i++) {
+ elem = rb_ary_entry(v, i);
+ if (TYPE(elem) == T_ARRAY) elem = rb_ary_entry(elem, 0);
+ elem = rb_String(elem);
+ rb_str_append(str, elem);
+ if (i < RARRAY(v)->len-1) rb_str_cat2(str, ":");
+ }
+ } else {
+ str = v;
+ StringValue(str);
+ }
+
+ if (!SSL_CTX_set_cipher_list(ctx, RSTRING(str)->ptr)) {
+ ossl_raise(eSSLError, "SSL_CTX_set_ciphers:");
+ }
+ return Qnil;
+}
+
+/*
+ * SSLSocket class
+ */
+#define ossl_ssl_get_io(o) rb_iv_get((o),"@io")
+#define ossl_ssl_get_ctx(o) rb_iv_get((o),"@context")
+
+#define ossl_ssl_set_io(o,v) rb_iv_set((o),"@io",(v))
+#define ossl_ssl_set_ctx(o,v) rb_iv_set((o),"@context",(v))
+
+static char *ossl_ssl_attrs[] = { "io", "context", };
+
+static void
+ossl_ssl_shutdown(SSL *ssl)
+{
+ if (ssl) {
+ SSL_shutdown(ssl);
+ SSL_clear(ssl);
+ }
+}
+
+static void
+ossl_ssl_free(SSL *ssl)
+{
+ ossl_ssl_shutdown(ssl);
+ SSL_free(ssl);
+}
+
+static VALUE
+ossl_ssl_s_alloc(VALUE klass)
+{
+ return Data_Wrap_Struct(klass, 0, ossl_ssl_free, NULL);
+}
+DEFINE_ALLOC_WRAPPER(ossl_ssl_s_alloc)
+
+static VALUE
+ossl_ssl_initialize(int argc, VALUE *argv, VALUE self)
+{
+ VALUE io, ctx;
+
+ if (rb_scan_args(argc, argv, "11", &io, &ctx) == 1) {
+ ctx = rb_funcall(cSSLContext, rb_intern("new"), 0);
+ }
+ OSSL_Check_Kind(ctx, cSSLContext);
+ Check_Type(io, T_FILE);
+ ossl_ssl_set_io(self, io);
+ ossl_ssl_set_ctx(self, ctx);
+ ossl_sslctx_setup(ctx);
+
+ return self;
+}
+
+static VALUE
+ossl_ssl_setup(VALUE self)
+{
+ VALUE io, v_ctx;
+ SSL_CTX *ctx;
+ SSL *ssl;
+ OpenFile *fptr;
+
+ Data_Get_Struct(self, SSL, ssl);
+ if(!ssl){
+ v_ctx = ossl_ssl_get_ctx(self);
+ Data_Get_Struct(v_ctx, SSL_CTX, ctx);
+
+ ssl = SSL_new(ctx);
+ if (!ssl) {
+ ossl_raise(eSSLError, "SSL_new:");
+ }
+ DATA_PTR(self) = ssl;
+
+ io = ossl_ssl_get_io(self);
+ GetOpenFile(io, fptr);
+ rb_io_check_readable(fptr);
+ rb_io_check_writable(fptr);
+ SSL_set_fd(ssl, TO_SOCKET(fileno(fptr->f)));
+ }
+
+ return Qtrue;
+}
+
+static VALUE
+ossl_ssl_connect(VALUE self)
+{
+ SSL *ssl;
+ VALUE cb;
+
+ ossl_ssl_setup(self);
+ Data_Get_Struct(self, SSL, ssl);
+ cb = ossl_sslctx_get_verify_cb(ossl_ssl_get_ctx(self));
+ SSL_set_ex_data(ssl, ossl_ssl_ex_vcb_idx, (void *)cb);
+ if (SSL_connect(ssl) <= 0) {
+ ossl_raise(eSSLError, "SSL_connect:");
+ }
+
+ return self;
+}
+
+static VALUE
+ossl_ssl_accept(VALUE self)
+{
+ SSL *ssl;
+ VALUE cb;
+
+ ossl_ssl_setup(self);
+ Data_Get_Struct(self, SSL, ssl);
+ cb = ossl_sslctx_get_verify_cb(ossl_ssl_get_ctx(self));
+ SSL_set_ex_data(ssl, ossl_ssl_ex_vcb_idx, (void *)cb);
+ if (SSL_accept(ssl) <= 0) {
+ ossl_raise(eSSLError, "SSL_accept:");
+ }
+
+ return self;
+}
+
+static VALUE
+ossl_ssl_read(VALUE self, VALUE len)
+{
+ SSL *ssl;
+ int ilen, nread = 0;
+ VALUE str;
+ OpenFile *fptr;
+
+ Data_Get_Struct(self, SSL, ssl);
+ ilen = NUM2INT(len);
+ str = rb_str_new(0, ilen);
+
+ if (ssl) {
+ nread = SSL_read(ssl, RSTRING(str)->ptr, RSTRING(str)->len);
+ if (nread < 0) {
+ ossl_raise(eSSLError, "SSL_read:");
+ }
+ }
+ else {
+ rb_warning("SSL session is not started yet.");
+ GetOpenFile(ossl_ssl_get_io(self), fptr);
+ rb_io_check_readable(fptr);
+ TRAP_BEG;
+ nread = read(fileno(fptr->f), RSTRING(str)->ptr, RSTRING(str)->len);
+ TRAP_END;
+ if(nread < 0) {
+ ossl_raise(eSSLError, "read:%s", strerror(errno));
+ }
+ }
+
+ if (nread == 0) {
+ ossl_raise(rb_eEOFError, "End of file reached");
+ }
+
+ RSTRING(str)->len = nread;
+ RSTRING(str)->ptr[nread] = 0;
+ OBJ_TAINT(str);
+
+ return str;
+}
+
+static VALUE
+ossl_ssl_write(VALUE self, VALUE str)
+{
+ SSL *ssl;
+ int nwrite = 0;
+ OpenFile *fptr;
+ FILE *fp;
+
+ Data_Get_Struct(self, SSL, ssl);
+ StringValue(str);
+
+ if (ssl) {
+ nwrite = SSL_write(ssl, RSTRING(str)->ptr, RSTRING(str)->len);
+ if (nwrite <= 0) {
+ ossl_raise(eSSLError, "SSL_write:");
+ }
+ }
+ else {
+ rb_warning("SSL session is not started yet.");
+ GetOpenFile(ossl_ssl_get_io(self), fptr);
+ rb_io_check_writable(fptr);
+ fp = GetWriteFile(fptr);
+ nwrite = write(fileno(fp), RSTRING(str)->ptr, RSTRING(str)->len);
+ if (nwrite < 0) {
+ ossl_raise(eSSLError, "write:%s", strerror(errno));
+ }
+ }
+
+ return INT2NUM(nwrite);
+}
+
+static VALUE
+ossl_ssl_close(VALUE self)
+{
+ SSL *ssl;
+
+ Data_Get_Struct(self, SSL, ssl);
+
+ ossl_ssl_shutdown(ssl);
+
+ return Qnil;
+}
+
+static VALUE
+ossl_ssl_get_cert(VALUE self)
+{
+ SSL *ssl;
+ X509 *cert = NULL;
+
+ Data_Get_Struct(self, SSL, ssl);
+ if (ssl) {
+ rb_warning("SSL session is not started yet.");
+ return Qnil;
+ }
+
+ /*
+ * Is this OpenSSL bug? Should add a ref?
+ * TODO: Ask for.
+ */
+ cert = SSL_get_certificate(ssl); /* NO DUPs => DON'T FREE. */
+
+ if (!cert) {
+ return Qnil;
+ }
+ return ossl_x509_new(cert);
+}
+
+static VALUE
+ossl_ssl_get_peer_cert(VALUE self)
+{
+ SSL *ssl;
+ X509 *cert = NULL;
+ VALUE obj;
+
+ Data_Get_Struct(self, SSL, ssl);
+
+ if (!ssl){
+ rb_warning("SSL session is not started yet.");
+ return Qnil;
+ }
+
+ cert = SSL_get_peer_certificate(ssl); /* Adds a ref => Safe to FREE. */
+
+ if (!cert) {
+ return Qnil;
+ }
+ obj = ossl_x509_new(cert);
+ X509_free(cert);
+
+ return obj;
+}
+
+static VALUE
+ossl_ssl_get_cipher(VALUE self)
+{
+ SSL *ssl;
+ SSL_CIPHER *cipher;
+
+ Data_Get_Struct(self, SSL, ssl);
+ if (!ssl) {
+ rb_warning("SSL session is not started yet.");
+ return Qnil;
+ }
+ cipher = SSL_get_current_cipher(ssl);
+
+ return ossl_ssl_cipher_to_ary(cipher);
+}
+
+static VALUE
+ossl_ssl_get_state(VALUE self)
+{
+ SSL *ssl;
+ VALUE ret;
+
+ Data_Get_Struct(self, SSL, ssl);
+ if (!ssl) {
+ rb_warning("SSL session is not started yet.");
+ return Qnil;
+ }
+ ret = rb_str_new2(SSL_state_string(ssl));
+ if (ruby_verbose) {
+ rb_str_cat2(ret, ": ");
+ rb_str_cat2(ret, SSL_state_string_long(ssl));
+ }
+ return ret;
+}
+
+void
+Init_ossl_ssl()
+{
+ int i;
+
+ ossl_ssl_ex_vcb_idx = SSL_get_ex_new_index(0,"ossl_ssl_ex_vcb_idx",0,0,0);
+ ossl_ssl_ex_store_p = SSL_get_ex_new_index(0,"ossl_ssl_ex_store_p",0,0,0);
+
+ mSSL = rb_define_module_under(mOSSL, "SSL");
+ eSSLError = rb_define_class_under(mSSL, "SSLError", eOSSLError);
+
+ /* class SSLContext */
+ cSSLContext = rb_define_class_under(mSSL, "SSLContext", rb_cObject);
+ rb_define_alloc_func(cSSLContext, ossl_sslctx_s_alloc);
+ for(i = 0; i < numberof(ossl_sslctx_attrs); i++)
+ rb_attr(cSSLContext, rb_intern(ossl_sslctx_attrs[i]), 1, 1, Qfalse);
+ rb_define_method(cSSLContext, "initialize", ossl_sslctx_initialize, -1);
+ rb_define_method(cSSLContext, "ciphers", ossl_sslctx_get_ciphers, 0);
+ rb_define_method(cSSLContext, "ciphers=", ossl_sslctx_set_ciphers, 1);
+
+ /* class SSLSocket */
+ cSSLSocket = rb_define_class_under(mSSL, "SSLSocket", rb_cObject);
+ rb_define_alloc_func(cSSLSocket, ossl_ssl_s_alloc);
+ for(i = 0; i < numberof(ossl_ssl_attrs); i++)
+ rb_attr(cSSLSocket, rb_intern(ossl_ssl_attrs[i]), 1, 0, Qfalse);
+ rb_define_alias(cSSLSocket, "to_io", "io");
+ rb_define_method(cSSLSocket, "initialize", ossl_ssl_initialize, -1);
+ rb_define_method(cSSLSocket, "connect", ossl_ssl_connect, 0);
+ rb_define_method(cSSLSocket, "accept", ossl_ssl_accept, 0);
+ rb_define_method(cSSLSocket, "sysread", ossl_ssl_read, 1);
+ rb_define_method(cSSLSocket, "syswrite", ossl_ssl_write, 1);
+ rb_define_method(cSSLSocket, "sysclose", ossl_ssl_close, 0);
+ rb_define_method(cSSLSocket, "cert", ossl_ssl_get_cert, 0);
+ rb_define_method(cSSLSocket, "peer_cert", ossl_ssl_get_peer_cert, 0);
+ rb_define_method(cSSLSocket, "cipher", ossl_ssl_get_cipher, 0);
+ rb_define_method(cSSLSocket, "state", ossl_ssl_get_state, 0);
+
+#define ossl_ssl_def_const(x) rb_define_const(mSSL, #x, INT2FIX(SSL_##x))
+
+ ossl_ssl_def_const(VERIFY_NONE);
+ ossl_ssl_def_const(VERIFY_PEER);
+ ossl_ssl_def_const(VERIFY_FAIL_IF_NO_PEER_CERT);
+ ossl_ssl_def_const(VERIFY_CLIENT_ONCE);
+ /* Not introduce constants included in OP_ALL such as...
+ * ossl_ssl_def_const(OP_MICROSOFT_SESS_ID_BUG);
+ * ossl_ssl_def_const(OP_NETSCAPE_CHALLENGE_BUG);
+ * ossl_ssl_def_const(OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG);
+ * ossl_ssl_def_const(OP_SSLREF2_REUSE_CERT_TYPE_BUG);
+ * ossl_ssl_def_const(OP_MICROSOFT_BIG_SSLV3_BUFFER);
+ * ossl_ssl_def_const(OP_MSIE_SSLV2_RSA_PADDING);
+ * ossl_ssl_def_const(OP_SSLEAY_080_CLIENT_DH_BUG);
+ * ossl_ssl_def_const(OP_TLS_D5_BUG);
+ * ossl_ssl_def_const(OP_TLS_BLOCK_PADDING_BUG);
+ * ossl_ssl_def_const(OP_DONT_INSERT_EMPTY_FRAGMENTS);
+ */
+ ossl_ssl_def_const(OP_ALL);
+#if defined(SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION)
+ ossl_ssl_def_const(OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
+#endif
+#if defined(SSL_OP_SINGLE_ECDH_USE)
+ ossl_ssl_def_const(OP_SINGLE_ECDH_USE);
+#endif
+ ossl_ssl_def_const(OP_SINGLE_DH_USE);
+ ossl_ssl_def_const(OP_EPHEMERAL_RSA);
+#if defined(SSL_OP_CIPHER_SERVER_PREFERENCE)
+ ossl_ssl_def_const(OP_CIPHER_SERVER_PREFERENCE);
+#endif
+ ossl_ssl_def_const(OP_TLS_ROLLBACK_BUG);
+ ossl_ssl_def_const(OP_NO_SSLv2);
+ ossl_ssl_def_const(OP_NO_SSLv3);
+ ossl_ssl_def_const(OP_NO_TLSv1);
+ ossl_ssl_def_const(OP_PKCS1_CHECK_1);
+ ossl_ssl_def_const(OP_PKCS1_CHECK_2);
+ ossl_ssl_def_const(OP_NETSCAPE_CA_DN_BUG);
+ ossl_ssl_def_const(OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG);
+}
diff --git a/ext/openssl/ossl_ssl.h b/ext/openssl/ossl_ssl.h
new file mode 100644
index 0000000000..5929eef856
--- /dev/null
+++ b/ext/openssl/ossl_ssl.h
@@ -0,0 +1,21 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#if !defined(_OSSL_SSL_H_)
+#define _OSSL_SSL_H_
+
+extern VALUE mSSL;
+extern VALUE eSSLError;
+extern VALUE cSSLSocket;
+extern VALUE cSSLContext;
+
+void Init_ossl_ssl(void);
+
+#endif /* _OSSL_SSL_H_ */
diff --git a/ext/openssl/ossl_version.h b/ext/openssl/ossl_version.h
new file mode 100644
index 0000000000..63878e0d8e
--- /dev/null
+++ b/ext/openssl/ossl_version.h
@@ -0,0 +1,16 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#if !defined(_OSSL_VERSION_H_)
+#define _OSSL_VERSION_H_
+
+#define OSSL_VERSION "1.0.0"
+
+#endif /* _OSSL_VERSION_H_ */
diff --git a/ext/openssl/ossl_x509.c b/ext/openssl/ossl_x509.c
new file mode 100644
index 0000000000..1fe50abf2c
--- /dev/null
+++ b/ext/openssl/ossl_x509.c
@@ -0,0 +1,95 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#include "ossl.h"
+
+VALUE mX509;
+
+#define DefX509Const(x) rb_define_const(mX509, #x,INT2FIX(X509_##x))
+
+void
+Init_ossl_x509()
+{
+ mX509 = rb_define_module_under(mOSSL, "X509");
+
+ Init_ossl_x509attr();
+ Init_ossl_x509cert();
+ Init_ossl_x509crl();
+ Init_ossl_x509ext();
+ Init_ossl_x509name();
+ Init_ossl_x509req();
+ Init_ossl_x509revoked();
+ Init_ossl_x509store();
+
+ DefX509Const(V_OK);
+ DefX509Const(V_ERR_UNABLE_TO_GET_ISSUER_CERT);
+ DefX509Const(V_ERR_UNABLE_TO_GET_CRL);
+ DefX509Const(V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE);
+ DefX509Const(V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE);
+ DefX509Const(V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY);
+ DefX509Const(V_ERR_CERT_SIGNATURE_FAILURE);
+ DefX509Const(V_ERR_CRL_SIGNATURE_FAILURE);
+ DefX509Const(V_ERR_CERT_NOT_YET_VALID);
+ DefX509Const(V_ERR_CERT_HAS_EXPIRED);
+ DefX509Const(V_ERR_CRL_NOT_YET_VALID);
+ DefX509Const(V_ERR_CRL_HAS_EXPIRED);
+ DefX509Const(V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD);
+ DefX509Const(V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD);
+ DefX509Const(V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD);
+ DefX509Const(V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
+ DefX509Const(V_ERR_OUT_OF_MEM);
+ DefX509Const(V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT);
+ DefX509Const(V_ERR_SELF_SIGNED_CERT_IN_CHAIN);
+ DefX509Const(V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY);
+ DefX509Const(V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE);
+ DefX509Const(V_ERR_CERT_CHAIN_TOO_LONG);
+ DefX509Const(V_ERR_CERT_REVOKED);
+ DefX509Const(V_ERR_INVALID_CA);
+ DefX509Const(V_ERR_PATH_LENGTH_EXCEEDED);
+ DefX509Const(V_ERR_INVALID_PURPOSE);
+ DefX509Const(V_ERR_CERT_UNTRUSTED);
+ DefX509Const(V_ERR_CERT_REJECTED);
+ DefX509Const(V_ERR_SUBJECT_ISSUER_MISMATCH);
+ DefX509Const(V_ERR_AKID_SKID_MISMATCH);
+ DefX509Const(V_ERR_AKID_ISSUER_SERIAL_MISMATCH);
+ DefX509Const(V_ERR_KEYUSAGE_NO_CERTSIGN);
+ DefX509Const(V_ERR_APPLICATION_VERIFICATION);
+
+#if defined(X509_V_FLAG_CRL_CHECK)
+ DefX509Const(V_FLAG_CRL_CHECK);
+#endif
+#if defined(X509_V_FLAG_CRL_CHECK_ALL)
+ DefX509Const(V_FLAG_CRL_CHECK_ALL);
+#endif
+
+ DefX509Const(PURPOSE_SSL_CLIENT);
+ DefX509Const(PURPOSE_SSL_SERVER);
+ DefX509Const(PURPOSE_NS_SSL_SERVER);
+ DefX509Const(PURPOSE_SMIME_SIGN);
+ DefX509Const(PURPOSE_SMIME_ENCRYPT);
+ DefX509Const(PURPOSE_CRL_SIGN);
+ DefX509Const(PURPOSE_ANY);
+#if defined(X509_PURPOSE_OCSP_HELPER)
+ DefX509Const(PURPOSE_OCSP_HELPER);
+#endif
+
+ DefX509Const(TRUST_COMPAT);
+ DefX509Const(TRUST_SSL_CLIENT);
+ DefX509Const(TRUST_SSL_SERVER);
+ DefX509Const(TRUST_EMAIL);
+ DefX509Const(TRUST_OBJECT_SIGN);
+#if defined(X509_TRUST_OCSP_SIGN)
+ DefX509Const(TRUST_OCSP_SIGN);
+#endif
+#if defined(X509_TRUST_OCSP_REQUEST)
+ DefX509Const(TRUST_OCSP_REQUEST);
+#endif
+}
+
diff --git a/ext/openssl/ossl_x509.h b/ext/openssl/ossl_x509.h
new file mode 100644
index 0000000000..196ce06848
--- /dev/null
+++ b/ext/openssl/ossl_x509.h
@@ -0,0 +1,113 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#if !defined(_OSSL_X509_H_)
+#define _OSSL_X509_H_
+
+/*
+ * X509 main module
+ */
+extern VALUE mX509;
+
+void Init_ossl_x509(void);
+
+/*
+ * X509Attr
+ */
+extern VALUE cX509Attr;
+extern VALUE eX509AttrError;
+
+VALUE ossl_x509attr_new(X509_ATTRIBUTE *);
+X509_ATTRIBUTE *DupX509AttrPtr(VALUE);
+void Init_ossl_x509attr(void);
+
+/*
+ * X509Cert
+ */
+extern VALUE cX509Cert;
+extern VALUE eX509CertError;
+
+VALUE ossl_x509_new(X509 *);
+VALUE ossl_x509_new_from_file(VALUE);
+X509 *GetX509CertPtr(VALUE);
+X509 *DupX509CertPtr(VALUE);
+void Init_ossl_x509cert(void);
+
+/*
+ * X509CRL
+ */
+extern VALUE cX509CRL;
+extern VALUE eX509CRLError;
+
+VALUE ossl_x509crl_new(X509_CRL *);
+X509_CRL *GetX509CRLPtr(VALUE);
+X509_CRL *DupX509CRLPtr(VALUE);
+void Init_ossl_x509crl(void);
+
+/*
+ * X509Extension
+ */
+extern VALUE cX509Ext;
+extern VALUE cX509ExtFactory;
+extern VALUE eX509ExtError;
+
+VALUE ossl_x509ext_new(X509_EXTENSION *);
+X509_EXTENSION *GetX509ExtPtr(VALUE);
+X509_EXTENSION *DupX509ExtPtr(VALUE);
+void Init_ossl_x509ext(void);
+
+/*
+ * X509Name
+ */
+extern VALUE cX509Name;
+extern VALUE eX509NameError;
+
+VALUE ossl_x509name_new(X509_NAME *);
+X509_NAME *GetX509NamePtr(VALUE);
+void Init_ossl_x509name(void);
+
+/*
+ * X509Request
+ */
+extern VALUE cX509Req;
+extern VALUE eX509ReqError;
+
+VALUE ossl_x509req_new(X509_REQ *);
+X509_REQ *DupX509ReqPtr(VALUE);
+void Init_ossl_x509req(void);
+
+/*
+ * X509Revoked
+ */
+extern VALUE cX509Rev;
+extern VALUE eX509RevError;
+
+VALUE ossl_x509revoked_new(X509_REVOKED *);
+X509_REVOKED *DupX509RevokedPtr(VALUE);
+void Init_ossl_x509revoked(void);
+
+/*
+ * X509Store and X509StoreContext
+ */
+extern VALUE cX509Store;
+extern VALUE cX509StoreContext;
+extern VALUE eX509StoreError;
+
+VALUE ossl_x509store_new(X509_STORE *);
+X509_STORE *GetX509StorePtr(VALUE);
+X509_STORE *DupX509StorePtr(VALUE);
+
+VALUE ossl_x509stctx_new(X509_STORE_CTX *);
+VALUE ossl_x509stctx_clear_ptr(VALUE);
+X509_STORE_CTX *GetX509StCtxtPtr(VALUE);
+
+void Init_ossl_x509store(void);
+
+#endif /* _OSSL_X509_H_ */
diff --git a/ext/openssl/ossl_x509attr.c b/ext/openssl/ossl_x509attr.c
new file mode 100644
index 0000000000..6d5df51126
--- /dev/null
+++ b/ext/openssl/ossl_x509attr.c
@@ -0,0 +1,152 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#include "ossl.h"
+
+#define WrapX509Attr(klass, obj, attr) do { \
+ if (!attr) { \
+ ossl_raise(rb_eRuntimeError, "ATTR wasn't initialized!"); \
+ } \
+ obj = Data_Wrap_Struct(klass, 0, X509_ATTRIBUTE_free, attr); \
+} while (0)
+#define GetX509Attr(obj, attr) do { \
+ Data_Get_Struct(obj, X509_ATTRIBUTE, attr); \
+ if (!attr) { \
+ ossl_raise(rb_eRuntimeError, "ATTR wasn't initialized!"); \
+ } \
+} while (0)
+#define SafeGetX509Attr(obj, attr) do { \
+ OSSL_Check_Kind(obj, cX509Attr); \
+ GetX509Attr(obj, attr); \
+} while (0)
+
+/*
+ * Classes
+ */
+VALUE cX509Attr;
+VALUE eX509AttrError;
+
+/*
+ * Public
+ */
+VALUE
+ossl_x509attr_new(X509_ATTRIBUTE *attr)
+{
+ X509_ATTRIBUTE *new;
+ VALUE obj;
+
+ if (!attr) {
+ new = X509_ATTRIBUTE_new();
+ } else {
+ new = X509_ATTRIBUTE_dup(attr);
+ }
+ if (!new) {
+ ossl_raise(eX509AttrError, NULL);
+ }
+ WrapX509Attr(cX509Attr, obj, new);
+
+ return obj;
+}
+
+X509_ATTRIBUTE *
+DupX509AttrPtr(VALUE obj)
+{
+ X509_ATTRIBUTE *attr, *new;
+
+ SafeGetX509Attr(obj, attr);
+ if (!(new = X509_ATTRIBUTE_dup(attr))) {
+ ossl_raise(eX509AttrError, NULL);
+ }
+
+ return new;
+}
+
+/*
+ * Private
+ */
+static VALUE
+ossl_x509attr_s_new_from_array(VALUE klass, VALUE ary)
+{
+ X509_ATTRIBUTE *attr;
+ int nid = NID_undef;
+ VALUE item, obj;
+
+ Check_Type(ary, T_ARRAY);
+ if (RARRAY(ary)->len != 2) {
+ ossl_raise(eX509AttrError, "unsupported ary structure");
+ }
+ /* key [0] */
+ item = RARRAY(ary)->ptr[0];
+ StringValue(item);
+ if (!(nid = OBJ_ln2nid(RSTRING(item)->ptr))) {
+ if (!(nid = OBJ_sn2nid(RSTRING(item)->ptr))) {
+ ossl_raise(eX509AttrError, NULL);
+ }
+ }
+ /* data [1] */
+ item = RARRAY(ary)->ptr[1];
+ StringValuePtr(item);
+ if (!(attr = X509_ATTRIBUTE_create(nid, MBSTRING_ASC, RSTRING(item)->ptr))) {
+ ossl_raise(eX509AttrError, NULL);
+ }
+ WrapX509Attr(klass, obj, attr);
+
+ return obj;
+}
+
+#if 0
+/*
+ * is there any print for attribute?
+ * (NO, but check t_req.c in crypto/asn1)
+ */
+static VALUE
+ossl_x509attr_to_a(VALUE self)
+{
+ ossl_x509attr *attrp = NULL;
+ BIO *out = NULL;
+ BUF_MEM *buf = NULL;
+ int nid = NID_undef;
+ VALUE ary, value;
+
+ GetX509Attr(obj, attrp);
+ ary = rb_ary_new2(2);
+ nid = OBJ_obj2nid(X509_ATTRIBUTE_get0_object(attrp->attribute));
+ rb_ary_push(ary, rb_str_new2(OBJ_nid2sn(nid)));
+ if (!(out = BIO_new(BIO_s_mem())))
+ ossl_raise(eX509ExtensionError, NULL);
+ if (!X509V3_???_print(out, extp->extension, 0, 0)) {
+ BIO_free(out);
+ ossl_raise(eX509ExtensionError, NULL);
+ }
+ BIO_get_mem_ptr(out, &buf);
+ value = rb_str_new(buf->data, buf->length);
+ BIO_free(out);
+ rb_funcall(value, rb_intern("tr!"), 2, rb_str_new2("\n"), rb_str_new2(","));
+ rb_ary_push(ary, value);
+
+ return ary;
+}
+#endif
+
+/*
+ * X509_ATTRIBUTE init
+ */
+void
+Init_ossl_x509attr()
+{
+ eX509AttrError = rb_define_class_under(mX509, "AttributeError", eOSSLError);
+
+ cX509Attr = rb_define_class_under(mX509, "Attribute", rb_cObject);
+ rb_define_singleton_method(cX509Attr, "new_from_array", ossl_x509attr_s_new_from_array, 1);
+/*
+ * TODO:
+ rb_define_method(cX509Attr, "to_a", ossl_x509attr_to_a, 0);
+ */
+}
diff --git a/ext/openssl/ossl_x509cert.c b/ext/openssl/ossl_x509cert.c
new file mode 100644
index 0000000000..b6ed438e0f
--- /dev/null
+++ b/ext/openssl/ossl_x509cert.c
@@ -0,0 +1,691 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#include "ossl.h"
+
+#define WrapX509(klass, obj, x509) do { \
+ if (!x509) { \
+ ossl_raise(rb_eRuntimeError, "CERT wasn't initialized!"); \
+ } \
+ obj = Data_Wrap_Struct(klass, 0, X509_free, x509); \
+} while (0)
+#define GetX509(obj, x509) do { \
+ Data_Get_Struct(obj, X509, x509); \
+ if (!x509) { \
+ ossl_raise(rb_eRuntimeError, "CERT wasn't initialized!"); \
+ } \
+} while (0)
+#define SafeGetX509(obj, x509) do { \
+ OSSL_Check_Kind(obj, cX509Cert); \
+ GetX509(obj, x509); \
+} while (0)
+
+/*
+ * Classes
+ */
+VALUE cX509Cert;
+VALUE eX509CertError;
+
+/*
+ * Public
+ */
+VALUE
+ossl_x509_new(X509 *x509)
+{
+ X509 *new;
+ VALUE obj;
+
+ if (!x509) {
+ new = X509_new();
+ } else {
+ new = X509_dup(x509);
+ }
+ if (!new) {
+ ossl_raise(eX509CertError, NULL);
+ }
+ WrapX509(cX509Cert, obj, new);
+
+ return obj;
+}
+
+VALUE
+ossl_x509_new_from_file(VALUE filename)
+{
+ X509 *x509;
+ FILE *fp;
+ VALUE obj;
+
+ SafeStringValue(filename);
+ if (!(fp = fopen(RSTRING(filename)->ptr, "r"))) {
+ ossl_raise(eX509CertError, "%s", strerror(errno));
+ }
+ x509 = PEM_read_X509(fp, NULL, NULL, NULL);
+ /*
+ * prepare for DER...
+#if !defined(OPENSSL_NO_FP_API)
+ if (!x509) {
+ rewind(fp);
+
+ x509 = d2i_X509_fp(fp, NULL);
+ }
+#endif
+ */
+ fclose(fp);
+ if (!x509) {
+ ossl_raise(eX509CertError, NULL);
+ }
+ WrapX509(cX509Cert, obj, x509);
+
+ return obj;
+}
+
+X509 *
+GetX509CertPtr(VALUE obj)
+{
+ X509 *x509;
+
+ SafeGetX509(obj, x509);
+
+ return x509;
+}
+
+X509 *
+DupX509CertPtr(VALUE obj)
+{
+ X509 *x509;
+
+ SafeGetX509(obj, x509);
+
+ CRYPTO_add(&x509->references, 1, CRYPTO_LOCK_X509);
+
+ return x509;
+}
+
+/*
+ * Private
+ */
+static VALUE
+ossl_x509_alloc(VALUE klass)
+{
+ X509 *x509;
+ VALUE obj;
+
+ x509 = X509_new();
+ if (!x509) ossl_raise(eX509CertError, NULL);
+
+ WrapX509(klass, obj, x509);
+
+ return obj;
+}
+DEFINE_ALLOC_WRAPPER(ossl_x509_alloc)
+
+static VALUE
+ossl_x509_initialize(int argc, VALUE *argv, VALUE self)
+{
+ BIO *in;
+ X509 *x509;
+ VALUE arg;
+
+ if (rb_scan_args(argc, argv, "01", &arg) == 0) {
+ /* create just empty X509Cert */
+ return self;
+ }
+ in = ossl_obj2bio(arg);
+
+ /*
+ * TODO:
+ * Check if we could free old X509
+ X509_free(DATA_PTR(self));
+ */
+ x509 = PEM_read_bio_X509(in, (X509 **)&DATA_PTR(self), NULL, NULL);
+ if (!x509) {
+ BIO_reset(in);
+ x509 = d2i_X509_bio(in, (X509 **)&DATA_PTR(self));
+ }
+ BIO_free(in);
+ if (!x509) ossl_raise(eX509CertError, NULL);
+
+ return self;
+}
+
+static VALUE
+ossl_x509_copy(VALUE self, VALUE other)
+{
+ X509 *a, *b, *x509;
+
+ rb_check_frozen(self);
+ if (self == other) return self;
+
+ GetX509(self, a);
+ SafeGetX509(other, b);
+
+ x509 = X509_dup(b);
+ if (!x509) ossl_raise(eX509CertError, NULL);
+
+ DATA_PTR(self) = x509;
+ X509_free(a);
+
+ return self;
+}
+
+static VALUE
+ossl_x509_to_der(VALUE self)
+{
+ X509 *x509;
+ BIO *out;
+ VALUE str;
+ int status=0;
+
+ GetX509(self, x509);
+
+ out = BIO_new(BIO_s_mem());
+ if (!out) ossl_raise(eX509CertError, NULL);
+
+ if (!i2d_X509_bio(out, x509)) {
+ BIO_free(out);
+ ossl_raise(eX509CertError, NULL);
+ }
+ str = ossl_protect_membio2str(out, &status);
+ BIO_free(out);
+ if (status) rb_jump_tag(status);
+
+ return str;
+}
+
+static VALUE
+ossl_x509_to_pem(VALUE self)
+{
+ X509 *x509;
+ BIO *out;
+ VALUE str;
+ int status=0;
+
+ GetX509(self, x509);
+ out = BIO_new(BIO_s_mem());
+ if (!out) ossl_raise(eX509CertError, NULL);
+
+ if (!PEM_write_bio_X509(out, x509)) {
+ BIO_free(out);
+ ossl_raise(eX509CertError, NULL);
+ }
+ str = ossl_protect_membio2str(out, &status);
+ BIO_free(out);
+ if (status) rb_jump_tag(status);
+
+ return str;
+}
+
+static VALUE
+ossl_x509_to_text(VALUE self)
+{
+ X509 *x509;
+ BIO *out;
+ VALUE str;
+ int status=0;
+
+ GetX509(self, x509);
+
+ out = BIO_new(BIO_s_mem());
+ if (!out) ossl_raise(eX509CertError, NULL);
+
+ if (!X509_print(out, x509)) {
+ BIO_free(out);
+ ossl_raise(eX509CertError, NULL);
+ }
+ str = ossl_protect_membio2str(out, &status);
+ BIO_free(out);
+ if (status) rb_jump_tag(status);
+
+ return str;
+}
+
+#if 0
+/*
+ * Makes from X509 X509_REQuest
+ */
+static VALUE
+ossl_x509_to_req(VALUE self)
+{
+ X509 *x509;
+ X509_REQ *req;
+ VALUE obj;
+
+ GetX509(self, x509);
+ if (!(req = X509_to_X509_REQ(x509, NULL, EVP_md5()))) {
+ ossl_raise(eX509CertError, NULL);
+ }
+ obj = ossl_x509req_new(req);
+ X509_REQ_free(req);
+
+ return obj;
+}
+#endif
+
+static VALUE
+ossl_x509_get_version(VALUE self)
+{
+ X509 *x509;
+
+ GetX509(self, x509);
+
+ return LONG2NUM(X509_get_version(x509));
+}
+
+static VALUE
+ossl_x509_set_version(VALUE self, VALUE version)
+{
+ X509 *x509;
+ long ver;
+
+ GetX509(self, x509);
+ if ((ver = NUM2LONG(version)) < 0) {
+ ossl_raise(eX509CertError, "version must be >= 0!");
+ }
+ if (!X509_set_version(x509, ver)) {
+ ossl_raise(eX509CertError, NULL);
+ }
+
+ return version;
+}
+
+static VALUE
+ossl_x509_get_serial(VALUE self)
+{
+ X509 *x509;
+
+ GetX509(self, x509);
+
+ return asn1integer_to_num(X509_get_serialNumber(x509));
+}
+
+static VALUE
+ossl_x509_set_serial(VALUE self, VALUE num)
+{
+ X509 *x509;
+
+ GetX509(self, x509);
+
+ x509->cert_info->serialNumber =
+ num_to_asn1integer(num, X509_get_serialNumber(x509));
+
+ return num;
+}
+
+static VALUE
+ossl_x509_get_signature_algorithm(VALUE self)
+{
+ X509 *x509;
+ BIO *out;
+ VALUE str;
+ int status=0;
+
+ GetX509(self, x509);
+
+ out = BIO_new(BIO_s_mem());
+ if (!out) ossl_raise(eX509CertError, NULL);
+
+ if (!i2a_ASN1_OBJECT(out, x509->cert_info->signature->algorithm)) {
+ BIO_free(out);
+ ossl_raise(eX509CertError, NULL);
+ }
+ str = ossl_protect_membio2str(out, &status);
+ BIO_free(out);
+ if (status) rb_jump_tag(status);
+
+ return str;
+}
+
+static VALUE
+ossl_x509_get_subject(VALUE self)
+{
+ X509 *x509;
+ X509_NAME *name;
+
+ GetX509(self, x509);
+ if (!(name = X509_get_subject_name(x509))) { /* NO DUP - don't free! */
+ ossl_raise(eX509CertError, NULL);
+ }
+
+ return ossl_x509name_new(name);
+}
+
+static VALUE
+ossl_x509_set_subject(VALUE self, VALUE subject)
+{
+ X509 *x509;
+
+ GetX509(self, x509);
+ if (!X509_set_subject_name(x509, GetX509NamePtr(subject))) { /* DUPs name */
+ ossl_raise(eX509CertError, NULL);
+ }
+
+ return subject;
+}
+
+static VALUE
+ossl_x509_get_issuer(VALUE self)
+{
+ X509 *x509;
+ X509_NAME *name;
+
+ GetX509(self, x509);
+ if(!(name = X509_get_issuer_name(x509))) { /* NO DUP - don't free! */
+ ossl_raise(eX509CertError, NULL);
+ }
+
+ return ossl_x509name_new(name);
+}
+
+static VALUE
+ossl_x509_set_issuer(VALUE self, VALUE issuer)
+{
+ X509 *x509;
+
+ GetX509(self, x509);
+ if (!X509_set_issuer_name(x509, GetX509NamePtr(issuer))) { /* DUPs name */
+ ossl_raise(eX509CertError, NULL);
+ }
+
+ return issuer;
+}
+
+static VALUE
+ossl_x509_get_not_before(VALUE self)
+{
+ X509 *x509;
+ ASN1_UTCTIME *asn1time;
+
+ GetX509(self, x509);
+ if (!(asn1time = X509_get_notBefore(x509))) { /* NO DUP - don't free! */
+ ossl_raise(eX509CertError, NULL);
+ }
+
+ return asn1time_to_time(asn1time);
+}
+
+static VALUE
+ossl_x509_set_not_before(VALUE self, VALUE time)
+{
+ X509 *x509;
+ time_t sec;
+
+ GetX509(self, x509);
+ sec = time_to_time_t(time);
+ if (!X509_time_adj(X509_get_notBefore(x509), 0, &sec)) {
+ ossl_raise(eX509CertError, NULL);
+ }
+
+ return time;
+}
+
+static VALUE
+ossl_x509_get_not_after(VALUE self)
+{
+ X509 *x509;
+ ASN1_TIME *asn1time;
+
+ GetX509(self, x509);
+ if (!(asn1time = X509_get_notAfter(x509))) { /* NO DUP - don't free! */
+ ossl_raise(eX509CertError, NULL);
+ }
+
+ return asn1time_to_time(asn1time);
+}
+
+static VALUE
+ossl_x509_set_not_after(VALUE self, VALUE time)
+{
+ X509 *x509;
+ time_t sec;
+
+ GetX509(self, x509);
+ sec = time_to_time_t(time);
+ if (!X509_time_adj(X509_get_notAfter(x509), 0, &sec)) {
+ ossl_raise(eX509CertError, NULL);
+ }
+
+ return time;
+}
+
+static VALUE
+ossl_x509_get_public_key(VALUE self)
+{
+ X509 *x509;
+ EVP_PKEY *pkey;
+
+ GetX509(self, x509);
+ if (!(pkey = X509_get_pubkey(x509))) { /* adds an reference */
+ ossl_raise(eX509CertError, NULL);
+ }
+
+ return ossl_pkey_new(pkey); /* NO DUP - OK */
+}
+
+static VALUE
+ossl_x509_set_public_key(VALUE self, VALUE key)
+{
+ X509 *x509;
+
+ GetX509(self, x509);
+ if (!X509_set_pubkey(x509, GetPKeyPtr(key))) { /* DUPs pkey */
+ ossl_raise(eX509CertError, NULL);
+ }
+
+ return key;
+}
+
+static VALUE
+ossl_x509_sign(VALUE self, VALUE key, VALUE digest)
+{
+ X509 *x509;
+ EVP_PKEY *pkey;
+ const EVP_MD *md;
+
+ GetX509(self, x509);
+ pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */
+ md = GetDigestPtr(digest);
+ if (!X509_sign(x509, pkey, md)) {
+ ossl_raise(eX509CertError, NULL);
+ }
+
+ return self;
+}
+
+/*
+ * Checks that cert signature is made with PRIVversion of this PUBLIC 'key'
+ */
+static VALUE
+ossl_x509_verify(VALUE self, VALUE key)
+{
+ X509 *x509;
+ EVP_PKEY *pkey;
+ int i;
+
+ GetX509(self, x509);
+ pkey = GetPKeyPtr(key); /* NO NEED TO DUP */
+ if ((i = X509_verify(x509, pkey)) < 0) {
+ ossl_raise(eX509CertError, NULL);
+ }
+ if (i > 0) {
+ return Qtrue;
+ }
+
+ return Qfalse;
+}
+
+/*
+ * Checks if 'key' is PRIV key for this cert
+ */
+static VALUE
+ossl_x509_check_private_key(VALUE self, VALUE key)
+{
+ X509 *x509;
+ EVP_PKEY *pkey;
+
+ GetX509(self, x509);
+ /* not needed private key, but should be */
+ pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */
+ if (!X509_check_private_key(x509, pkey)) {
+ OSSL_Warning("Check private key:%s", OSSL_ErrMsg());
+ return Qfalse;
+ }
+
+ return Qtrue;
+}
+
+/*
+ * Gets X509v3 extensions as array of X509Ext objects
+ */
+static VALUE
+ossl_x509_get_extensions(VALUE self)
+{
+ X509 *x509;
+ int count, i;
+ X509_EXTENSION *ext;
+ VALUE ary;
+
+ GetX509(self, x509);
+ count = X509_get_ext_count(x509);
+ if (count < 0) {
+ return rb_ary_new();
+ }
+ ary = rb_ary_new2(count);
+ for (i=0; i<count; i++) {
+ ext = X509_get_ext(x509, i); /* NO DUP - don't free! */
+ rb_ary_push(ary, ossl_x509ext_new(ext));
+ }
+
+ return ary;
+}
+
+/*
+ * Sets X509_EXTENSIONs
+ */
+static VALUE
+ossl_x509_set_extensions(VALUE self, VALUE ary)
+{
+ X509 *x509;
+ X509_EXTENSION *ext;
+ int i;
+
+ GetX509(self, x509);
+ Check_Type(ary, T_ARRAY);
+ /* All ary's members should be X509Extension */
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ OSSL_Check_Kind(RARRAY(ary)->ptr[i], cX509Ext);
+ }
+ sk_X509_EXTENSION_pop_free(x509->cert_info->extensions, X509_EXTENSION_free);
+ x509->cert_info->extensions = NULL;
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ ext = DupX509ExtPtr(RARRAY(ary)->ptr[i]);
+
+ if (!X509_add_ext(x509, ext, -1)) { /* DUPs ext - FREE it */
+ X509_EXTENSION_free(ext);
+ ossl_raise(eX509CertError, NULL);
+ }
+ X509_EXTENSION_free(ext);
+ }
+
+ return ary;
+}
+
+static VALUE
+ossl_x509_add_extension(VALUE self, VALUE extension)
+{
+ X509 *x509;
+ X509_EXTENSION *ext;
+
+ GetX509(self, x509);
+ ext = DupX509ExtPtr(extension);
+ if (!X509_add_ext(x509, ext, -1)) { /* DUPs ext - FREE it */
+ X509_EXTENSION_free(ext);
+ ossl_raise(eX509CertError, NULL);
+ }
+ X509_EXTENSION_free(ext);
+
+ return extension;
+}
+
+static VALUE
+ossl_x509_inspect(VALUE self)
+{
+ VALUE str;
+ char *cname = rb_class2name(rb_obj_class(self));
+
+ str = rb_str_new2("#<");
+ rb_str_cat2(str, cname);
+ rb_str_cat2(str, " ");
+
+ rb_str_cat2(str, "subject=");
+ rb_str_append(str, rb_inspect(ossl_x509_get_subject(self)));
+ rb_str_cat2(str, ", ");
+
+ rb_str_cat2(str, "issuer=");
+ rb_str_append(str, rb_inspect(ossl_x509_get_issuer(self)));
+ rb_str_cat2(str, ", ");
+
+ rb_str_cat2(str, "serial=");
+ rb_str_append(str, rb_inspect(ossl_x509_get_serial(self)));
+ rb_str_cat2(str, ", ");
+
+ rb_str_cat2(str, "not_before=");
+ rb_str_append(str, rb_inspect(ossl_x509_get_not_before(self)));
+ rb_str_cat2(str, ", ");
+
+ rb_str_cat2(str, "not_after=");
+ rb_str_append(str, rb_inspect(ossl_x509_get_not_after(self)));
+
+ str = rb_str_cat2(str, ">");
+
+ return str;
+}
+
+/*
+ * INIT
+ */
+void
+Init_ossl_x509cert()
+{
+ eX509CertError = rb_define_class_under(mX509, "CertificateError", eOSSLError);
+
+ cX509Cert = rb_define_class_under(mX509, "Certificate", rb_cObject);
+
+ rb_define_alloc_func(cX509Cert, ossl_x509_alloc);
+ rb_define_method(cX509Cert, "initialize", ossl_x509_initialize, -1);
+ rb_define_copy_func(cX509Cert, ossl_x509_copy);
+
+ rb_define_method(cX509Cert, "to_der", ossl_x509_to_der, 0);
+ rb_define_method(cX509Cert, "to_pem", ossl_x509_to_pem, 0);
+ rb_define_alias(cX509Cert, "to_s", "to_pem");
+ rb_define_method(cX509Cert, "to_text", ossl_x509_to_text, 0);
+ rb_define_method(cX509Cert, "version", ossl_x509_get_version, 0);
+ rb_define_method(cX509Cert, "version=", ossl_x509_set_version, 1);
+ rb_define_method(cX509Cert, "signature_algorithm", ossl_x509_get_signature_algorithm, 0);
+ rb_define_method(cX509Cert, "serial", ossl_x509_get_serial, 0);
+ rb_define_method(cX509Cert, "serial=", ossl_x509_set_serial, 1);
+ rb_define_method(cX509Cert, "subject", ossl_x509_get_subject, 0);
+ rb_define_method(cX509Cert, "subject=", ossl_x509_set_subject, 1);
+ rb_define_method(cX509Cert, "issuer", ossl_x509_get_issuer, 0);
+ rb_define_method(cX509Cert, "issuer=", ossl_x509_set_issuer, 1);
+ rb_define_method(cX509Cert, "not_before", ossl_x509_get_not_before, 0);
+ rb_define_method(cX509Cert, "not_before=", ossl_x509_set_not_before, 1);
+ rb_define_method(cX509Cert, "not_after", ossl_x509_get_not_after, 0);
+ rb_define_method(cX509Cert, "not_after=", ossl_x509_set_not_after, 1);
+ rb_define_method(cX509Cert, "public_key", ossl_x509_get_public_key, 0);
+ rb_define_method(cX509Cert, "public_key=", ossl_x509_set_public_key, 1);
+ rb_define_method(cX509Cert, "sign", ossl_x509_sign, 2);
+ rb_define_method(cX509Cert, "verify", ossl_x509_verify, 1);
+ rb_define_method(cX509Cert, "check_private_key", ossl_x509_check_private_key, 1);
+ rb_define_method(cX509Cert, "extensions", ossl_x509_get_extensions, 0);
+ rb_define_method(cX509Cert, "extensions=", ossl_x509_set_extensions, 1);
+ rb_define_method(cX509Cert, "add_extension", ossl_x509_add_extension, 1);
+ rb_define_method(cX509Cert, "inspect", ossl_x509_inspect, 0);
+}
+
diff --git a/ext/openssl/ossl_x509crl.c b/ext/openssl/ossl_x509crl.c
new file mode 100644
index 0000000000..2c1f9e0cdb
--- /dev/null
+++ b/ext/openssl/ossl_x509crl.c
@@ -0,0 +1,551 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#include "ossl.h"
+
+#define WrapX509CRL(klass, obj, crl) do { \
+ if (!crl) { \
+ ossl_raise(rb_eRuntimeError, "CRL wasn't initialized!"); \
+ } \
+ obj = Data_Wrap_Struct(klass, 0, X509_CRL_free, crl); \
+} while (0)
+#define GetX509CRL(obj, crl) do { \
+ Data_Get_Struct(obj, X509_CRL, crl); \
+ if (!crl) { \
+ ossl_raise(rb_eRuntimeError, "CRL wasn't initialized!"); \
+ } \
+} while (0)
+#define SafeGetX509CRL(obj, crl) do { \
+ OSSL_Check_Kind(obj, cX509CRL); \
+ GetX509CRL(obj, crl); \
+} while (0)
+
+/*
+ * Classes
+ */
+VALUE cX509CRL;
+VALUE eX509CRLError;
+
+/*
+ * PUBLIC
+ */
+X509_CRL *
+GetX509CRLPtr(VALUE obj)
+{
+ X509_CRL *crl;
+
+ SafeGetX509CRL(obj, crl);
+
+ return crl;
+}
+
+X509_CRL *
+DupX509CRLPtr(VALUE obj)
+{
+ X509_CRL *crl;
+
+ SafeGetX509CRL(obj, crl);
+ CRYPTO_add(&crl->references, 1, CRYPTO_LOCK_X509_CRL);
+
+ return crl;
+}
+
+VALUE
+ossl_x509crl_new(X509_CRL *crl)
+{
+ X509_CRL *tmp;
+ VALUE obj;
+
+ tmp = crl ? X509_CRL_dup(crl) : X509_CRL_new();
+ if(!tmp) ossl_raise(eX509CRLError, NULL);
+ WrapX509CRL(cX509CRL, obj, tmp);
+
+ return obj;
+}
+
+/*
+ * PRIVATE
+ */
+static VALUE
+ossl_x509crl_alloc(VALUE klass)
+{
+ X509_CRL *crl;
+ VALUE obj;
+
+ if (!(crl = X509_CRL_new())) {
+ ossl_raise(eX509CRLError, NULL);
+ }
+ WrapX509CRL(klass, obj, crl);
+
+ return obj;
+}
+DEFINE_ALLOC_WRAPPER(ossl_x509crl_alloc)
+
+static VALUE
+ossl_x509crl_initialize(int argc, VALUE *argv, VALUE self)
+{
+ BIO *in;
+ X509_CRL *crl;
+ VALUE buffer;
+
+ if (rb_scan_args(argc, argv, "01", &buffer) == 0) {
+ return self;
+ }
+ StringValue(buffer);
+
+ in = BIO_new_mem_buf(RSTRING(buffer)->ptr, RSTRING(buffer)->len);
+ if (!in) {
+ ossl_raise(eX509CRLError, NULL);
+ }
+ /*
+ * TODO:
+ * Check if we should free CRL
+ X509_CRL_free(DATA_PTR(self));
+ */
+ crl = PEM_read_bio_X509_CRL(in, (X509_CRL **)&DATA_PTR(self), NULL, NULL);
+ if (!crl) {
+ BIO_reset(in);
+
+ crl = d2i_X509_CRL_bio(in, (X509_CRL **)&DATA_PTR(self));
+ }
+ if (!crl) {
+ BIO_free(in);
+ ossl_raise(eX509CRLError, NULL);
+ }
+ BIO_free(in);
+
+ return self;
+}
+
+static VALUE
+ossl_x509crl_copy(VALUE self, VALUE other)
+{
+ X509_CRL *a, *b, *crl;
+
+ rb_check_frozen(self);
+ if (self == other) return self;
+ GetX509CRL(self, a);
+ SafeGetX509CRL(other, b);
+ if (!(crl = X509_CRL_dup(b))) {
+ ossl_raise(eX509CRLError, NULL);
+ }
+ X509_CRL_free(a);
+ DATA_PTR(self) = crl;
+
+ return self;
+}
+
+static VALUE
+ossl_x509crl_get_version(VALUE self)
+{
+ X509_CRL *crl;
+ long ver;
+
+ GetX509CRL(self, crl);
+ ver = X509_CRL_get_version(crl);
+
+ return LONG2NUM(ver);
+}
+
+static VALUE
+ossl_x509crl_set_version(VALUE self, VALUE version)
+{
+ X509_CRL *crl;
+ long ver;
+
+ GetX509CRL(self, crl);
+
+ if ((ver = NUM2LONG(version)) < 0) {
+ ossl_raise(eX509CRLError, "version must be >= 0!");
+ }
+ if (!X509_CRL_set_version(crl, ver)) {
+ ossl_raise(eX509CRLError, NULL);
+ }
+
+ return version;
+}
+
+static VALUE
+ossl_x509crl_get_signature_algorithm(VALUE self)
+{
+ X509_CRL *crl;
+ BIO *out;
+ BUF_MEM *buf;
+ VALUE str;
+
+ GetX509CRL(self, crl);
+
+ if (!(out = BIO_new(BIO_s_mem()))) {
+ ossl_raise(eX509CRLError, NULL);
+ }
+ if (!i2a_ASN1_OBJECT(out, crl->sig_alg->algorithm)) {
+ BIO_free(out);
+ ossl_raise(eX509CRLError, NULL);
+ }
+ BIO_get_mem_ptr(out, &buf);
+ str = rb_str_new(buf->data, buf->length);
+ BIO_free(out);
+ return str;
+}
+
+static VALUE
+ossl_x509crl_get_issuer(VALUE self)
+{
+ X509_CRL *crl;
+
+ GetX509CRL(self, crl);
+
+ return ossl_x509name_new(X509_CRL_get_issuer(crl)); /* NO DUP - don't free */
+}
+
+static VALUE
+ossl_x509crl_set_issuer(VALUE self, VALUE issuer)
+{
+ X509_CRL *crl;
+
+ GetX509CRL(self, crl);
+
+ if (!X509_CRL_set_issuer_name(crl, GetX509NamePtr(issuer))) { /* DUPs name */
+ ossl_raise(eX509CRLError, NULL);
+ }
+ return issuer;
+}
+
+static VALUE
+ossl_x509crl_get_last_update(VALUE self)
+{
+ X509_CRL *crl;
+
+ GetX509CRL(self, crl);
+
+ return asn1time_to_time(X509_CRL_get_lastUpdate(crl));
+}
+
+static VALUE
+ossl_x509crl_set_last_update(VALUE self, VALUE time)
+{
+ X509_CRL *crl;
+ time_t sec;
+
+ GetX509CRL(self, crl);
+ sec = time_to_time_t(time);
+ if (!X509_time_adj(crl->crl->lastUpdate, 0, &sec)) {
+ ossl_raise(eX509CRLError, NULL);
+ }
+
+ return time;
+}
+
+static VALUE
+ossl_x509crl_get_next_update(VALUE self)
+{
+ X509_CRL *crl;
+
+ GetX509CRL(self, crl);
+
+ return asn1time_to_time(X509_CRL_get_nextUpdate(crl));
+}
+
+static VALUE
+ossl_x509crl_set_next_update(VALUE self, VALUE time)
+{
+ X509_CRL *crl;
+ time_t sec;
+
+ GetX509CRL(self, crl);
+ sec = time_to_time_t(time);
+ /* This must be some thinko in OpenSSL */
+ if (!(crl->crl->nextUpdate = X509_time_adj(crl->crl->nextUpdate, 0, &sec))){
+ ossl_raise(eX509CRLError, NULL);
+ }
+
+ return time;
+}
+
+static VALUE
+ossl_x509crl_get_revoked(VALUE self)
+{
+ X509_CRL *crl;
+ int i, num;
+ X509_REVOKED *rev;
+ VALUE ary, revoked;
+
+ GetX509CRL(self, crl);
+ num = sk_X509_CRL_num(X509_CRL_get_REVOKED(crl));
+ if (num < 0) {
+ OSSL_Debug("num < 0???");
+ return rb_ary_new();
+ }
+ ary = rb_ary_new2(num);
+ for(i=0; i<num; i++) {
+ /* NO DUP - don't free! */
+ rev = (X509_REVOKED *)sk_X509_CRL_value(X509_CRL_get_REVOKED(crl), i);
+ revoked = ossl_x509revoked_new(rev);
+ rb_ary_push(ary, revoked);
+ }
+
+ return ary;
+}
+
+static VALUE
+ossl_x509crl_set_revoked(VALUE self, VALUE ary)
+{
+ X509_CRL *crl;
+ X509_REVOKED *rev;
+ int i;
+
+ GetX509CRL(self, crl);
+ Check_Type(ary, T_ARRAY);
+ /* All ary members should be X509 Revoked */
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ OSSL_Check_Kind(RARRAY(ary)->ptr[i], cX509Rev);
+ }
+ sk_X509_REVOKED_pop_free(crl->crl->revoked, X509_REVOKED_free);
+ crl->crl->revoked = NULL;
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ rev = DupX509RevokedPtr(RARRAY(ary)->ptr[i]);
+ if (!X509_CRL_add0_revoked(crl, rev)) { /* NO DUP - don't free! */
+ ossl_raise(eX509CRLError, NULL);
+ }
+ }
+ X509_CRL_sort(crl);
+
+ return ary;
+}
+
+static VALUE
+ossl_x509crl_add_revoked(VALUE self, VALUE revoked)
+{
+ X509_CRL *crl;
+ X509_REVOKED *rev;
+
+ GetX509CRL(self, crl);
+ rev = DupX509RevokedPtr(revoked);
+ if (!X509_CRL_add0_revoked(crl, rev)) { /* NO DUP - don't free! */
+ ossl_raise(eX509CRLError, NULL);
+ }
+ X509_CRL_sort(crl);
+
+ return revoked;
+}
+
+static VALUE
+ossl_x509crl_sign(VALUE self, VALUE key, VALUE digest)
+{
+ X509_CRL *crl;
+ EVP_PKEY *pkey;
+ const EVP_MD *md;
+
+ GetX509CRL(self, crl);
+ pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */
+ md = GetDigestPtr(digest);
+ if (!X509_CRL_sign(crl, pkey, md)) {
+ ossl_raise(eX509CRLError, NULL);
+ }
+
+ return self;
+}
+
+static VALUE
+ossl_x509crl_verify(VALUE self, VALUE key)
+{
+ X509_CRL *crl;
+ int ret;
+
+ GetX509CRL(self, crl);
+ if ((ret = X509_CRL_verify(crl, GetPKeyPtr(key))) < 0) {
+ ossl_raise(eX509CRLError, NULL);
+ }
+ if (ret == 1) {
+ return Qtrue;
+ }
+
+ return Qfalse;
+}
+
+static VALUE
+ossl_x509crl_to_der(VALUE self)
+{
+ X509_CRL *crl;
+ BIO *out;
+ BUF_MEM *buf;
+ VALUE str;
+
+ GetX509CRL(self, crl);
+ if (!(out = BIO_new(BIO_s_mem()))) {
+ ossl_raise(eX509CRLError, NULL);
+ }
+ if (!i2d_X509_CRL_bio(out, crl)) {
+ BIO_free(out);
+ ossl_raise(eX509CRLError, NULL);
+ }
+ BIO_get_mem_ptr(out, &buf);
+ str = rb_str_new(buf->data, buf->length);
+ BIO_free(out);
+
+ return str;
+}
+
+static VALUE
+ossl_x509crl_to_pem(VALUE self)
+{
+ X509_CRL *crl;
+ BIO *out;
+ BUF_MEM *buf;
+ VALUE str;
+
+ GetX509CRL(self, crl);
+ if (!(out = BIO_new(BIO_s_mem()))) {
+ ossl_raise(eX509CRLError, NULL);
+ }
+ if (!PEM_write_bio_X509_CRL(out, crl)) {
+ BIO_free(out);
+ ossl_raise(eX509CRLError, NULL);
+ }
+ BIO_get_mem_ptr(out, &buf);
+ str = rb_str_new(buf->data, buf->length);
+ BIO_free(out);
+
+ return str;
+}
+
+static VALUE
+ossl_x509crl_to_text(VALUE self)
+{
+ X509_CRL *crl;
+ BIO *out;
+ BUF_MEM *buf;
+ VALUE str;
+
+ GetX509CRL(self, crl);
+ if (!(out = BIO_new(BIO_s_mem()))) {
+ ossl_raise(eX509CRLError, NULL);
+ }
+ if (!X509_CRL_print(out, crl)) {
+ BIO_free(out);
+ ossl_raise(eX509CRLError, NULL);
+ }
+ BIO_get_mem_ptr(out, &buf);
+ str = rb_str_new(buf->data, buf->length);
+ BIO_free(out);
+
+ return str;
+}
+
+/*
+ * Gets X509v3 extensions as array of X509Ext objects
+ */
+static VALUE
+ossl_x509crl_get_extensions(VALUE self)
+{
+ X509_CRL *crl;
+ int count, i;
+ X509_EXTENSION *ext;
+ VALUE ary;
+
+ GetX509CRL(self, crl);
+ count = X509_CRL_get_ext_count(crl);
+ if (count < 0) {
+ OSSL_Debug("count < 0???");
+ return rb_ary_new();
+ }
+ ary = rb_ary_new2(count);
+ for (i=0; i<count; i++) {
+ ext = X509_CRL_get_ext(crl, i); /* NO DUP - don't free! */
+ rb_ary_push(ary, ossl_x509ext_new(ext));
+ }
+
+ return ary;
+}
+
+/*
+ * Sets X509_EXTENSIONs
+ */
+static VALUE
+ossl_x509crl_set_extensions(VALUE self, VALUE ary)
+{
+ X509_CRL *crl;
+ X509_EXTENSION *ext;
+ int i;
+
+ GetX509CRL(self, crl);
+ Check_Type(ary, T_ARRAY);
+ /* All ary members should be X509 Extensions */
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ OSSL_Check_Kind(RARRAY(ary)->ptr[i], cX509Ext);
+ }
+ sk_X509_EXTENSION_pop_free(crl->crl->extensions, X509_EXTENSION_free);
+ crl->crl->extensions = NULL;
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ ext = DupX509ExtPtr(RARRAY(ary)->ptr[i]);
+ if(!X509_CRL_add_ext(crl, ext, -1)) { /* DUPs ext - FREE it */
+ X509_EXTENSION_free(ext);
+ ossl_raise(eX509CRLError, NULL);
+ }
+ X509_EXTENSION_free(ext);
+ }
+
+ return ary;
+}
+
+static VALUE
+ossl_x509crl_add_extension(VALUE self, VALUE extension)
+{
+ X509_CRL *crl;
+ X509_EXTENSION *ext;
+
+ GetX509CRL(self, crl);
+ ext = DupX509ExtPtr(extension);
+ if (!X509_CRL_add_ext(crl, ext, -1)) { /* DUPs ext - FREE it */
+ X509_EXTENSION_free(ext);
+ ossl_raise(eX509CRLError, NULL);
+ }
+ X509_EXTENSION_free(ext);
+
+ return extension;
+}
+
+/*
+ * INIT
+ */
+void
+Init_ossl_x509crl()
+{
+ eX509CRLError = rb_define_class_under(mX509, "CRLError", eOSSLError);
+
+ cX509CRL = rb_define_class_under(mX509, "CRL", rb_cObject);
+
+ rb_define_alloc_func(cX509CRL, ossl_x509crl_alloc);
+ rb_define_method(cX509CRL, "initialize", ossl_x509crl_initialize, -1);
+ rb_define_copy_func(cX509CRL, ossl_x509crl_copy);
+
+ rb_define_method(cX509CRL, "version", ossl_x509crl_get_version, 0);
+ rb_define_method(cX509CRL, "version=", ossl_x509crl_set_version, 1);
+ rb_define_method(cX509CRL, "signature_algorithm", ossl_x509crl_get_signature_algorithm, 0);
+ rb_define_method(cX509CRL, "issuer", ossl_x509crl_get_issuer, 0);
+ rb_define_method(cX509CRL, "issuer=", ossl_x509crl_set_issuer, 1);
+ rb_define_method(cX509CRL, "last_update", ossl_x509crl_get_last_update, 0);
+ rb_define_method(cX509CRL, "last_update=", ossl_x509crl_set_last_update, 1);
+ rb_define_method(cX509CRL, "next_update", ossl_x509crl_get_next_update, 0);
+ rb_define_method(cX509CRL, "next_update=", ossl_x509crl_set_next_update, 1);
+ rb_define_method(cX509CRL, "revoked", ossl_x509crl_get_revoked, 0);
+ rb_define_method(cX509CRL, "revoked=", ossl_x509crl_set_revoked, 1);
+ rb_define_method(cX509CRL, "add_revoked", ossl_x509crl_add_revoked, 1);
+ rb_define_method(cX509CRL, "sign", ossl_x509crl_sign, 2);
+ rb_define_method(cX509CRL, "verify", ossl_x509crl_verify, 1);
+ rb_define_method(cX509CRL, "to_der", ossl_x509crl_to_der, 0);
+ rb_define_method(cX509CRL, "to_pem", ossl_x509crl_to_pem, 0);
+ rb_define_alias(cX509CRL, "to_s", "to_pem");
+ rb_define_method(cX509CRL, "to_text", ossl_x509crl_to_text, 0);
+ rb_define_method(cX509CRL, "extensions", ossl_x509crl_get_extensions, 0);
+ rb_define_method(cX509CRL, "extensions=", ossl_x509crl_set_extensions, 1);
+ rb_define_method(cX509CRL, "add_extension", ossl_x509crl_add_extension, 1);
+}
+
diff --git a/ext/openssl/ossl_x509ext.c b/ext/openssl/ossl_x509ext.c
new file mode 100644
index 0000000000..4d981ed938
--- /dev/null
+++ b/ext/openssl/ossl_x509ext.c
@@ -0,0 +1,345 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#include "ossl.h"
+
+#define WrapX509Ext(klass, obj, ext) do { \
+ if (!ext) { \
+ ossl_raise(rb_eRuntimeError, "EXT wasn't initialized!"); \
+ } \
+ obj = Data_Wrap_Struct(klass, 0, X509_EXTENSION_free, ext); \
+} while (0)
+#define GetX509Ext(obj, ext) do { \
+ Data_Get_Struct(obj, X509_EXTENSION, ext); \
+ if (!ext) { \
+ ossl_raise(rb_eRuntimeError, "EXT wasn't initialized!"); \
+ } \
+} while (0)
+#define SafeGetX509Ext(obj, ext) do { \
+ OSSL_Check_Kind(obj, cX509Ext); \
+ GetX509Ext(obj, ext); \
+} while (0)
+
+#define MakeX509ExtFactory(klass, obj, ctx) \
+ obj = Data_Make_Struct(klass, X509V3_CTX, 0, ossl_x509extfactory_free, ctx)
+#define GetX509ExtFactory(obj, ctx) do { \
+ Data_Get_Struct(obj, X509V3_CTX, ctx); \
+ if (!ctx) { \
+ ossl_raise(rb_eRuntimeError, "CTX wasn't initialized!"); \
+ } \
+} while (0)
+
+/*
+ * Classes
+ */
+VALUE cX509Ext;
+VALUE cX509ExtFactory;
+VALUE eX509ExtError;
+
+/*
+ * Public
+ */
+VALUE
+ossl_x509ext_new(X509_EXTENSION *ext)
+{
+ X509_EXTENSION *new;
+ VALUE obj;
+
+ if (!ext) {
+ new = X509_EXTENSION_new();
+ } else {
+ new = X509_EXTENSION_dup(ext);
+ }
+ if (!new) {
+ ossl_raise(eX509ExtError, NULL);
+ }
+ WrapX509Ext(cX509Ext, obj, new);
+
+ return obj;
+}
+
+X509_EXTENSION *
+GetX509ExtPtr(VALUE obj)
+{
+ X509_EXTENSION *ext;
+
+ SafeGetX509Ext(obj, ext);
+
+ return ext;
+}
+
+X509_EXTENSION *
+DupX509ExtPtr(VALUE obj)
+{
+ X509_EXTENSION *ext, *new;
+
+ SafeGetX509Ext(obj, ext);
+ if (!(new = X509_EXTENSION_dup(ext))) {
+ ossl_raise(eX509ExtError, NULL);
+ }
+
+ return new;
+}
+
+/*
+ * Private
+ */
+/*
+ * Ext factory
+ */
+static void
+ossl_x509extfactory_free(X509V3_CTX *ctx)
+{
+ if (ctx) {
+ if (ctx->issuer_cert) X509_free(ctx->issuer_cert);
+ if (ctx->subject_cert) X509_free(ctx->subject_cert);
+ if (ctx->crl) X509_CRL_free(ctx->crl);
+ if (ctx->subject_req) X509_REQ_free(ctx->subject_req);
+ OPENSSL_free(ctx);
+ }
+}
+
+static VALUE
+ossl_x509extfactory_alloc(VALUE klass)
+{
+ X509V3_CTX *ctx;
+ VALUE obj;
+
+ MakeX509ExtFactory(klass, obj, ctx);
+
+ return obj;
+}
+DEFINE_ALLOC_WRAPPER(ossl_x509extfactory_alloc)
+
+static VALUE
+ossl_x509extfactory_set_issuer_cert(VALUE self, VALUE cert)
+{
+ X509V3_CTX *ctx;
+
+ GetX509ExtFactory(self, ctx);
+ ctx->issuer_cert = DupX509CertPtr(cert); /* DUP NEEDED */
+
+ return cert;
+}
+
+static VALUE
+ossl_x509extfactory_set_subject_cert(VALUE self, VALUE cert)
+{
+ X509V3_CTX *ctx;
+
+ GetX509ExtFactory(self, ctx);
+ ctx->subject_cert = DupX509CertPtr(cert); /* DUP NEEDED */
+
+ return cert;
+}
+
+static VALUE
+ossl_x509extfactory_set_subject_req(VALUE self, VALUE req)
+{
+ X509V3_CTX *ctx;
+
+ GetX509ExtFactory(self, ctx);
+ ctx->subject_req = DupX509ReqPtr(req);
+
+ return req;
+}
+
+static VALUE
+ossl_x509extfactory_set_crl(VALUE self, VALUE crl)
+{
+ X509V3_CTX *ctx;
+
+ GetX509ExtFactory(self, ctx);
+ ctx->crl = DupX509CRLPtr(crl);
+
+ return crl;
+}
+
+static VALUE
+ossl_x509extfactory_initialize(int argc, VALUE *argv, VALUE self)
+{
+ /*X509V3_CTX *ctx;*/
+ VALUE issuer_cert, subject_cert, subject_req, crl;
+
+ /*GetX509ExtFactory(self, ctx);*/
+
+ rb_scan_args(argc, argv, "04", &issuer_cert, &subject_cert, &subject_req, &crl);
+
+ if (!NIL_P(issuer_cert)) {
+ ossl_x509extfactory_set_issuer_cert(self, issuer_cert);
+ }
+ if (!NIL_P(subject_cert)) {
+ ossl_x509extfactory_set_subject_cert(self, subject_cert);
+ }
+ if (!NIL_P(subject_req)) {
+ ossl_x509extfactory_set_subject_req(self, subject_req);
+ }
+ if (!NIL_P(crl)) {
+ ossl_x509extfactory_set_crl(self, crl);
+ }
+ return self;
+}
+
+/*
+ * Array to X509_EXTENSION
+ * Structure:
+ * ["ln", "value", bool_critical] or
+ * ["sn", "value", bool_critical] or
+ * ["ln", "critical,value"] or the same for sn
+ * ["ln", "value"] => not critical
+ */
+static VALUE
+ossl_x509extfactory_create_ext_from_array(VALUE self, VALUE ary)
+{
+ X509V3_CTX *ctx;
+ X509_EXTENSION *ext;
+ int nid;
+ char *value;
+ VALUE item, obj;
+
+ GetX509ExtFactory(self, ctx);
+ Check_Type(ary, T_ARRAY);
+ if ((RARRAY(ary)->len) < 2 || (RARRAY(ary)->len > 3)) { /*2 or 3 allowed*/
+ ossl_raise(eX509ExtError, "unsupported structure");
+ }
+ /* key [0] */
+ item = RARRAY(ary)->ptr[0];
+ StringValue(item);
+ if (!(nid = OBJ_ln2nid(RSTRING(item)->ptr))) {
+ if (!(nid = OBJ_sn2nid(RSTRING(item)->ptr))) {
+ ossl_raise(eX509ExtError, NULL);
+ }
+ }
+ /* data [1] */
+ item = RARRAY(ary)->ptr[1];
+ StringValue(item);
+ /* (optional) critical [2] */
+ if (RARRAY(ary)->len == 3 && RARRAY(ary)->ptr[2] == Qtrue) {
+ if (!(value = OPENSSL_malloc(strlen("critical,") +
+ (RSTRING(item)->len) + 1))) {
+ ossl_raise(eX509ExtError, "malloc error");
+ }
+ strcpy(value, "critical,");
+ strncat(value, RSTRING(item)->ptr, RSTRING(item)->len);
+ } else {
+ value = strdup(StringValuePtr(item));
+ }
+ if (!(ext = X509V3_EXT_conf_nid(NULL, ctx, nid, value))) {
+ OPENSSL_free(value);
+ ossl_raise(eX509ExtError, NULL);
+ }
+ OPENSSL_free(value);
+ WrapX509Ext(cX509Ext, obj, ext);
+
+ return obj;
+}
+
+/*
+ * Ext
+ */
+static VALUE
+ossl_x509ext_get_oid(VALUE obj)
+{
+ X509_EXTENSION *ext;
+ ASN1_OBJECT *extobj;
+ BIO *out;
+ VALUE ret;
+ int nid, status = 0;
+
+ GetX509Ext(obj, ext);
+ extobj = X509_EXTENSION_get_object(ext);
+ if ((nid = OBJ_obj2nid(extobj)) != NID_undef)
+ ret = rb_str_new2(OBJ_nid2sn(nid));
+ else{
+ if (!(out = BIO_new(BIO_s_mem())))
+ ossl_raise(eX509ExtError, NULL);
+ i2a_ASN1_OBJECT(out, extobj);
+ ret = ossl_protect_membio2str(out, &status);
+ BIO_free(out);
+ if(status) rb_jump_tag(status);
+ }
+
+ return ret;
+}
+
+static VALUE
+ossl_x509ext_get_value(VALUE obj)
+{
+ X509_EXTENSION *ext;
+ BIO *out;
+ VALUE ret;
+ int status = 0;
+
+ GetX509Ext(obj, ext);
+ if (!(out = BIO_new(BIO_s_mem())))
+ ossl_raise(eX509ExtError, NULL);
+ if (!X509V3_EXT_print(out, ext, 0, 0))
+ M_ASN1_OCTET_STRING_print(out, ext->value);
+ ret = ossl_protect_membio2str(out, &status);
+ BIO_free(out);
+ if(status) rb_jump_tag(status);
+
+ return ret;
+}
+
+static VALUE
+ossl_x509ext_get_critical(VALUE obj)
+{
+ X509_EXTENSION *ext;
+ GetX509Ext(obj, ext);
+ return X509_EXTENSION_get_critical(ext) ? Qtrue : Qfalse;
+}
+
+static VALUE
+ossl_x509ext_to_der(VALUE obj)
+{
+ X509_EXTENSION *ext;
+ unsigned char *p;
+ int len;
+ VALUE str;
+
+ GetX509Ext(obj, ext);
+ p = NULL;
+ if((len = i2d_X509_EXTENSION(ext, &p)) < 0)
+ ossl_raise(eX509ExtError, NULL);
+ str = rb_str_new(p, len);
+ free(p);
+
+ return str;
+}
+
+/*
+ * INIT
+ */
+void
+Init_ossl_x509ext()
+{
+ eX509ExtError = rb_define_class_under(mX509, "ExtensionError", eOSSLError);
+
+ cX509ExtFactory = rb_define_class_under(mX509, "ExtensionFactory", rb_cObject);
+
+ rb_define_alloc_func(cX509ExtFactory, ossl_x509extfactory_alloc);
+ rb_define_method(cX509ExtFactory, "initialize", ossl_x509extfactory_initialize, -1);
+
+ rb_define_method(cX509ExtFactory, "issuer_certificate=", ossl_x509extfactory_set_issuer_cert, 1);
+ rb_define_method(cX509ExtFactory, "subject_certificate=", ossl_x509extfactory_set_subject_cert, 1);
+ rb_define_method(cX509ExtFactory, "subject_request=", ossl_x509extfactory_set_subject_req, 1);
+ rb_define_method(cX509ExtFactory, "crl=", ossl_x509extfactory_set_crl, 1);
+ rb_define_method(cX509ExtFactory, "create_ext_from_array", ossl_x509extfactory_create_ext_from_array, 1);
+
+ cX509Ext = rb_define_class_under(mX509, "Extension", rb_cObject);
+ rb_undef_method(CLASS_OF(cX509Ext), "new");
+/* rb_define_alloc_func(cX509Ext, ossl_x509ext_alloc); */
+/* rb_define_method(cX509Ext, "initialize", ossl_x509ext_initialize, -1); */
+ rb_define_method(cX509Ext, "oid", ossl_x509ext_get_oid, 0);
+ rb_define_method(cX509Ext, "value", ossl_x509ext_get_value, 0);
+ rb_define_method(cX509Ext, "critical?", ossl_x509ext_get_critical, 0);
+ rb_define_method(cX509Ext, "to_der", ossl_x509ext_to_der, 0);
+}
diff --git a/ext/openssl/ossl_x509name.c b/ext/openssl/ossl_x509name.c
new file mode 100644
index 0000000000..a1e5cba1b4
--- /dev/null
+++ b/ext/openssl/ossl_x509name.c
@@ -0,0 +1,234 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#include "ossl.h"
+#include "st.h" /* For st_foreach -- ST_CONTINUE */
+
+#define WrapX509Name(klass, obj, name) do { \
+ if (!name) { \
+ ossl_raise(rb_eRuntimeError, "Name wasn't initialized."); \
+ } \
+ obj = Data_Wrap_Struct(klass, 0, X509_NAME_free, name); \
+} while (0)
+#define GetX509Name(obj, name) do { \
+ Data_Get_Struct(obj, X509_NAME, name); \
+ if (!name) { \
+ ossl_raise(rb_eRuntimeError, "Name wasn't initialized."); \
+ } \
+} while (0)
+#define SafeGetX509Name(obj, name) do { \
+ OSSL_Check_Kind(obj, cX509Name); \
+ GetX509Name(obj, name); \
+} while (0)
+
+/*
+ * Classes
+ */
+VALUE cX509Name;
+VALUE eX509NameError;
+
+/*
+ * Public
+ */
+VALUE
+ossl_x509name_new(X509_NAME *name)
+{
+ X509_NAME *new;
+ VALUE obj;
+
+ if (!name) {
+ new = X509_NAME_new();
+ } else {
+ new = X509_NAME_dup(name);
+ }
+ if (!new) {
+ ossl_raise(eX509NameError, NULL);
+ }
+ WrapX509Name(cX509Name, obj, new);
+
+ return obj;
+}
+
+X509_NAME *
+GetX509NamePtr(VALUE obj)
+{
+ X509_NAME *name;
+
+ SafeGetX509Name(obj, name);
+
+ return name;
+}
+
+/*
+ * Private
+ */
+static VALUE
+ossl_x509name_alloc(VALUE klass)
+{
+ X509_NAME *name;
+ VALUE obj;
+
+ if (!(name = X509_NAME_new())) {
+ ossl_raise(eX509NameError, NULL);
+ }
+ WrapX509Name(klass, obj, name);
+
+ return obj;
+}
+DEFINE_ALLOC_WRAPPER(ossl_x509name_alloc)
+
+static VALUE
+ossl_x509name_initialize(int argc, VALUE *argv, VALUE self)
+{
+ X509_NAME *name;
+ int i, type;
+ VALUE arg, item, key, value;
+
+ GetX509Name(self, name);
+ if (rb_scan_args(argc, argv, "01", &arg) == 0) {
+ return self;
+ }
+ Check_Type(arg, T_ARRAY);
+ for (i=0; i<RARRAY(arg)->len; i++) {
+ item = RARRAY(arg)->ptr[i];
+ Check_Type(item, T_ARRAY);
+ if (RARRAY(item)->len != 2) {
+ ossl_raise(rb_eArgError, "Unsupported structure.");
+ }
+ key = RARRAY(item)->ptr[0];
+ value = RARRAY(item)->ptr[1];
+ StringValue(key);
+ StringValue(value);
+ type = ASN1_PRINTABLE_type(RSTRING(value)->ptr, -1);
+ if (!X509_NAME_add_entry_by_txt(name, RSTRING(key)->ptr, type,
+ RSTRING(value)->ptr, RSTRING(value)->len, -1, 0)) {
+ ossl_raise(eX509NameError, NULL);
+ }
+ }
+
+ return self;
+}
+
+static VALUE
+ossl_x509name_to_s(VALUE self)
+{
+ X509_NAME *name;
+ char *buf;
+ VALUE str;
+
+ GetX509Name(self, name);
+ buf = X509_NAME_oneline(name, NULL, 0);
+ str = rb_str_new2(buf);
+ OPENSSL_free(buf);
+
+ return str;
+}
+
+static VALUE
+ossl_x509name_to_a(VALUE self)
+{
+ X509_NAME *name;
+ X509_NAME_ENTRY *entry;
+ int i,entries;
+ char long_name[512];
+ const char *short_name;
+ VALUE ary;
+
+ GetX509Name(self, name);
+ entries = X509_NAME_entry_count(name);
+ if (entries < 0) {
+ OSSL_Debug("name entries < 0!");
+ return rb_ary_new();
+ }
+ ary = rb_ary_new2(entries);
+ for (i=0; i<entries; i++) {
+ if (!(entry = X509_NAME_get_entry(name, i))) {
+ ossl_raise(eX509NameError, NULL);
+ }
+ if (!i2t_ASN1_OBJECT(long_name, sizeof(long_name), entry->object)) {
+ ossl_raise(eX509NameError, NULL);
+ }
+ short_name = OBJ_nid2sn(OBJ_ln2nid(long_name));
+
+ rb_ary_push(ary, rb_assoc_new(rb_str_new2(short_name),
+ rb_str_new(entry->value->data, entry->value->length)));
+ }
+ return ary;
+}
+
+static int
+ossl_x509name_cmp0(VALUE self, VALUE other)
+{
+ X509_NAME *name1, *name2;
+
+ GetX509Name(self, name1);
+ SafeGetX509Name(other, name2);
+
+ return X509_NAME_cmp(name1, name2);
+}
+
+static VALUE
+ossl_x509name_cmp(VALUE self, VALUE other)
+{
+ int result;
+
+ result = ossl_x509name_cmp0(self, other);
+ if (result < 0) return INT2FIX(-1);
+ if (result > 1) return INT2FIX(1);
+
+ return INT2FIX(0);
+}
+
+static VALUE
+ossl_x509name_eql(VALUE self, VALUE other)
+{
+ int result;
+
+ if(CLASS_OF(other) != cX509Name) return Qfalse;
+ result = ossl_x509name_cmp0(self, other);
+
+ return (result == 0) ? Qtrue : Qfalse;
+}
+
+static VALUE
+ossl_x509name_hash(VALUE self)
+{
+ X509_NAME *name;
+ unsigned long hash;
+
+ GetX509Name(self, name);
+
+ hash = X509_NAME_hash(name);
+
+ return ULONG2NUM(hash);
+}
+
+/*
+ * INIT
+ */
+void
+Init_ossl_x509name()
+{
+ eX509NameError = rb_define_class_under(mX509, "NameError", eOSSLError);
+
+ cX509Name = rb_define_class_under(mX509, "Name", rb_cObject);
+
+ rb_define_alloc_func(cX509Name, ossl_x509name_alloc);
+ rb_define_method(cX509Name, "initialize", ossl_x509name_initialize, -1);
+
+ rb_define_method(cX509Name, "to_s", ossl_x509name_to_s, 0);
+ rb_define_method(cX509Name, "to_a", ossl_x509name_to_a, 0);
+
+ rb_define_method(cX509Name, "cmp", ossl_x509name_cmp, 1);
+ rb_define_alias(cX509Name, "<=>", "cmp");
+ rb_define_method(cX509Name, "eql?", ossl_x509name_eql, 1);
+
+ rb_define_method(cX509Name, "hash", ossl_x509name_hash, 0);
+}
diff --git a/ext/openssl/ossl_x509req.c b/ext/openssl/ossl_x509req.c
new file mode 100644
index 0000000000..e7329d158b
--- /dev/null
+++ b/ext/openssl/ossl_x509req.c
@@ -0,0 +1,449 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#include "ossl.h"
+
+#define WrapX509Req(klass, obj, req) do { \
+ if (!req) { \
+ ossl_raise(rb_eRuntimeError, "Req wasn't initialized!"); \
+ } \
+ obj = Data_Wrap_Struct(klass, 0, X509_REQ_free, req); \
+} while (0)
+#define GetX509Req(obj, req) do { \
+ Data_Get_Struct(obj, X509_REQ, req); \
+ if (!req) { \
+ ossl_raise(rb_eRuntimeError, "Req wasn't initialized!"); \
+ } \
+} while (0)
+#define SafeGetX509Req(obj, req) do { \
+ OSSL_Check_Kind(obj, cX509Req); \
+ GetX509Req(obj, req); \
+} while (0)
+
+/*
+ * Classes
+ */
+VALUE cX509Req;
+VALUE eX509ReqError;
+
+/*
+ * Public functions
+ */
+VALUE
+ossl_x509req_new(X509_REQ *req)
+{
+ X509_REQ *new;
+ VALUE obj;
+
+ if (!req) {
+ new = X509_REQ_new();
+ } else {
+ new = X509_REQ_dup(req);
+ }
+ if (!new) {
+ ossl_raise(eX509ReqError, NULL);
+ }
+ WrapX509Req(cX509Req, obj, new);
+
+ return obj;
+}
+
+X509_REQ *
+DupX509ReqPtr(VALUE obj)
+{
+ X509_REQ *req, *new;
+
+ SafeGetX509Req(obj, req);
+ if (!(new = X509_REQ_dup(req))) {
+ ossl_raise(eX509ReqError, NULL);
+ }
+
+ return new;
+}
+
+/*
+ * Private functions
+ */
+static VALUE
+ossl_x509req_alloc(VALUE klass)
+{
+ X509_REQ *req;
+ VALUE obj;
+
+ if (!(req = X509_REQ_new())) {
+ ossl_raise(eX509ReqError, NULL);
+ }
+ WrapX509Req(klass, obj, req);
+
+ return obj;
+}
+DEFINE_ALLOC_WRAPPER(ossl_x509req_alloc)
+
+static VALUE
+ossl_x509req_initialize(int argc, VALUE *argv, VALUE self)
+{
+ BIO *in;
+ X509_REQ *req;
+ VALUE buffer;
+
+ if (rb_scan_args(argc, argv, "01", &buffer) == 0) {
+ return self;
+ }
+ StringValue(buffer);
+
+ in = BIO_new_mem_buf(RSTRING(buffer)->ptr, RSTRING(buffer)->len);
+ if (!in) {
+ ossl_raise(eX509ReqError, NULL);
+ }
+ /*
+ * TODO:
+ * Check if we should
+ X509_REQ_free(DATA_PTR(self));
+ */
+ req = PEM_read_bio_X509_REQ(in, (X509_REQ **)&DATA_PTR(self), NULL, NULL);
+ if (!req) {
+ BIO_reset(in);
+
+ req = d2i_X509_REQ_bio(in, (X509_REQ **)&DATA_PTR(self));
+ }
+ if (!req) {
+ BIO_free(in);
+ ossl_raise(eX509ReqError, NULL);
+ }
+ BIO_free(in);
+
+ return self;
+}
+
+static VALUE
+ossl_x509req_copy(VALUE self, VALUE other)
+{
+ X509_REQ *a, *b, *req;
+
+ rb_check_frozen(self);
+ if (self == other) return self;
+ GetX509Req(self, a);
+ SafeGetX509Req(other, b);
+ if (!(req = X509_REQ_dup(b))) {
+ ossl_raise(eX509ReqError, NULL);
+ }
+ X509_REQ_free(a);
+ DATA_PTR(self) = req;
+
+ return self;
+}
+
+static VALUE
+ossl_x509req_to_pem(VALUE self)
+{
+ X509_REQ *req;
+ BIO *out;
+ BUF_MEM *buf;
+ VALUE str;
+
+ GetX509Req(self, req);
+ if (!(out = BIO_new(BIO_s_mem()))) {
+ ossl_raise(eX509ReqError, NULL);
+ }
+ if (!PEM_write_bio_X509_REQ(out, req)) {
+ BIO_free(out);
+ ossl_raise(eX509ReqError, NULL);
+ }
+ BIO_get_mem_ptr(out, &buf);
+ str = rb_str_new(buf->data, buf->length);
+ BIO_free(out);
+
+ return str;
+}
+
+static VALUE
+ossl_x509req_to_text(VALUE self)
+{
+ X509_REQ *req;
+ BIO *out;
+ BUF_MEM *buf;
+ VALUE str;
+
+ GetX509Req(self, req);
+ if (!(out = BIO_new(BIO_s_mem()))) {
+ ossl_raise(eX509ReqError, NULL);
+ }
+ if (!X509_REQ_print(out, req)) {
+ BIO_free(out);
+ ossl_raise(eX509ReqError, NULL);
+ }
+ BIO_get_mem_ptr(out, &buf);
+ str = rb_str_new(buf->data, buf->length);
+ BIO_free(out);
+
+ return str;
+}
+
+#if 0
+/*
+ * Makes X509 from X509_REQuest
+ */
+static VALUE
+ossl_x509req_to_x509(VALUE self, VALUE days, VALUE key)
+{
+ X509_REQ *req;
+ X509 *x509;
+
+ GetX509Req(self, req);
+ ...
+ if (!(x509 = X509_REQ_to_X509(req, d, pkey))) {
+ ossl_raise(eX509ReqError, NULL);
+ }
+
+ return ossl_x509_new(x509);
+}
+#endif
+
+static VALUE
+ossl_x509req_get_version(VALUE self)
+{
+ X509_REQ *req;
+ long version;
+
+ GetX509Req(self, req);
+ version = X509_REQ_get_version(req);
+
+ return LONG2FIX(version);
+}
+
+static VALUE
+ossl_x509req_set_version(VALUE self, VALUE version)
+{
+ X509_REQ *req;
+ long ver;
+
+ GetX509Req(self, req);
+ if ((ver = FIX2LONG(version)) < 0) {
+ ossl_raise(eX509ReqError, "version must be >= 0!");
+ }
+ if (!X509_REQ_set_version(req, ver)) {
+ ossl_raise(eX509ReqError, NULL);
+ }
+
+ return version;
+}
+
+static VALUE
+ossl_x509req_get_subject(VALUE self)
+{
+ X509_REQ *req;
+ X509_NAME *name;
+
+ GetX509Req(self, req);
+ if (!(name = X509_REQ_get_subject_name(req))) { /* NO DUP - don't free */
+ ossl_raise(eX509ReqError, NULL);
+ }
+
+ return ossl_x509name_new(name);
+}
+
+static VALUE
+ossl_x509req_set_subject(VALUE self, VALUE subject)
+{
+ X509_REQ *req;
+
+ GetX509Req(self, req);
+ /* DUPs name */
+ if (!X509_REQ_set_subject_name(req, GetX509NamePtr(subject))) {
+ ossl_raise(eX509ReqError, NULL);
+ }
+
+ return subject;
+}
+
+static VALUE
+ossl_x509req_get_signature_algorithm(VALUE self)
+{
+ X509_REQ *req;
+ BIO *out;
+ BUF_MEM *buf;
+ VALUE str;
+
+ GetX509Req(self, req);
+
+ if (!(out = BIO_new(BIO_s_mem()))) {
+ ossl_raise(eX509ReqError, NULL);
+ }
+ if (!i2a_ASN1_OBJECT(out, req->sig_alg->algorithm)) {
+ BIO_free(out);
+ ossl_raise(eX509ReqError, NULL);
+ }
+ BIO_get_mem_ptr(out, &buf);
+ str = rb_str_new(buf->data, buf->length);
+ BIO_free(out);
+ return str;
+}
+
+static VALUE
+ossl_x509req_get_public_key(VALUE self)
+{
+ X509_REQ *req;
+ EVP_PKEY *pkey;
+
+ GetX509Req(self, req);
+ if (!(pkey = X509_REQ_get_pubkey(req))) { /* adds reference */
+ ossl_raise(eX509ReqError, NULL);
+ }
+
+ return ossl_pkey_new(pkey); /* NO DUP - OK */
+}
+
+static VALUE
+ossl_x509req_set_public_key(VALUE self, VALUE key)
+{
+ X509_REQ *req;
+ EVP_PKEY *pkey;
+
+ GetX509Req(self, req);
+ pkey = GetPKeyPtr(key); /* NO NEED TO DUP */
+ if (!X509_REQ_set_pubkey(req, pkey)) {
+ ossl_raise(eX509ReqError, NULL);
+ }
+
+ return key;
+}
+
+static VALUE
+ossl_x509req_sign(VALUE self, VALUE key, VALUE digest)
+{
+ X509_REQ *req;
+ EVP_PKEY *pkey;
+ const EVP_MD *md;
+
+ GetX509Req(self, req);
+ pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */
+ md = GetDigestPtr(digest);
+ if (!X509_REQ_sign(req, pkey, md)) {
+ ossl_raise(eX509ReqError, NULL);
+ }
+
+ return self;
+}
+
+/*
+ * Checks that cert signature is made with PRIVversion of this PUBLIC 'key'
+ */
+static VALUE
+ossl_x509req_verify(VALUE self, VALUE key)
+{
+ X509_REQ *req;
+ EVP_PKEY *pkey;
+ int i;
+
+ GetX509Req(self, req);
+ pkey = GetPKeyPtr(key); /* NO NEED TO DUP */
+ if ((i = X509_REQ_verify(req, pkey)) < 0) {
+ ossl_raise(eX509ReqError, NULL);
+ }
+ if (i > 0) {
+ return Qtrue;
+ }
+
+ return Qfalse;
+}
+
+static VALUE
+ossl_x509req_get_attributes(VALUE self)
+{
+ X509_REQ *req;
+ int count, i;
+ X509_ATTRIBUTE *attr;
+ VALUE ary;
+
+ GetX509Req(self, req);
+
+ count = X509_REQ_get_attr_count(req);
+ if (count < 0) {
+ OSSL_Debug("count < 0???");
+ return rb_ary_new();
+ }
+ ary = rb_ary_new2(count);
+ for (i=0; i<count; i++) {
+ attr = X509_REQ_get_attr(req, i);
+ rb_ary_push(ary, ossl_x509attr_new(attr));
+ }
+
+ return ary;
+}
+
+static VALUE
+ossl_x509req_set_attributes(VALUE self, VALUE ary)
+{
+ X509_REQ *req;
+ X509_ATTRIBUTE *attr;
+ int i;
+ VALUE item;
+
+ GetX509Req(self, req);
+ Check_Type(ary, T_ARRAY);
+ for (i=0;i<RARRAY(ary)->len; i++) {
+ OSSL_Check_Kind(RARRAY(ary)->ptr[i], cX509Attr);
+ }
+ sk_X509_ATTRIBUTE_pop_free(req->req_info->attributes, X509_ATTRIBUTE_free);
+ req->req_info->attributes = NULL;
+ for (i=0;i<RARRAY(ary)->len; i++) {
+ item = RARRAY(ary)->ptr[i];
+ attr = DupX509AttrPtr(item);
+ if (!X509_REQ_add1_attr(req, attr)) {
+ ossl_raise(eX509ReqError, NULL);
+ }
+ }
+ return ary;
+}
+
+static VALUE
+ossl_x509req_add_attribute(VALUE self, VALUE attr)
+{
+ X509_REQ *req;
+
+ GetX509Req(self, req);
+ if (!X509_REQ_add1_attr(req, DupX509AttrPtr(attr))) {
+ ossl_raise(eX509ReqError, NULL);
+ }
+
+ return attr;
+}
+
+/*
+ * X509_REQUEST init
+ */
+void
+Init_ossl_x509req()
+{
+ eX509ReqError = rb_define_class_under(mX509, "RequestError", eOSSLError);
+
+ cX509Req = rb_define_class_under(mX509, "Request", rb_cObject);
+
+ rb_define_alloc_func(cX509Req, ossl_x509req_alloc);
+ rb_define_method(cX509Req, "initialize", ossl_x509req_initialize, -1);
+ rb_define_copy_func(cX509Req, ossl_x509req_copy);
+
+ rb_define_method(cX509Req, "to_pem", ossl_x509req_to_pem, 0);
+ rb_define_alias(cX509Req, "to_s", "to_pem");
+ rb_define_method(cX509Req, "to_text", ossl_x509req_to_text, 0);
+ rb_define_method(cX509Req, "version", ossl_x509req_get_version, 0);
+ rb_define_method(cX509Req, "version=", ossl_x509req_set_version, 1);
+ rb_define_method(cX509Req, "subject", ossl_x509req_get_subject, 0);
+ rb_define_method(cX509Req, "subject=", ossl_x509req_set_subject, 1);
+ rb_define_method(cX509Req, "signature_algorithm", ossl_x509req_get_signature_algorithm, 0);
+ rb_define_method(cX509Req, "public_key", ossl_x509req_get_public_key, 0);
+ rb_define_method(cX509Req, "public_key=", ossl_x509req_set_public_key, 1);
+ rb_define_method(cX509Req, "sign", ossl_x509req_sign, 2);
+ rb_define_method(cX509Req, "verify", ossl_x509req_verify, 1);
+ rb_define_method(cX509Req, "attributes", ossl_x509req_get_attributes, 0);
+ rb_define_method(cX509Req, "attributes=", ossl_x509req_set_attributes, 1);
+ rb_define_method(cX509Req, "add_attribute", ossl_x509req_add_attribute, 1);
+}
+
diff --git a/ext/openssl/ossl_x509revoked.c b/ext/openssl/ossl_x509revoked.c
new file mode 100644
index 0000000000..1cd04eaa25
--- /dev/null
+++ b/ext/openssl/ossl_x509revoked.c
@@ -0,0 +1,230 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#include "ossl.h"
+
+#define WrapX509Rev(klass, obj, rev) do { \
+ if (!rev) { \
+ ossl_raise(rb_eRuntimeError, "REV wasn't initialized!"); \
+ } \
+ obj = Data_Wrap_Struct(klass, 0, X509_REVOKED_free, rev); \
+} while (0)
+#define GetX509Rev(obj, rev) do { \
+ Data_Get_Struct(obj, X509_REVOKED, rev); \
+ if (!rev) { \
+ ossl_raise(rb_eRuntimeError, "REV wasn't initialized!"); \
+ } \
+} while (0)
+#define SafeGetX509Rev(obj, rev) do { \
+ OSSL_Check_Kind(obj, cX509Rev); \
+ GetX509Rev(obj, rev); \
+} while (0)
+
+/*
+ * Classes
+ */
+VALUE cX509Rev;
+VALUE eX509RevError;
+
+/*
+ * PUBLIC
+ */
+VALUE
+ossl_x509revoked_new(X509_REVOKED *rev)
+{
+ X509_REVOKED *new;
+ VALUE obj;
+
+ if (!rev) {
+ new = X509_REVOKED_new();
+ } else {
+ new = X509_REVOKED_dup(rev);
+ }
+ if (!new) {
+ ossl_raise(eX509RevError, NULL);
+ }
+ WrapX509Rev(cX509Rev, obj, new);
+
+ return obj;
+}
+
+X509_REVOKED *
+DupX509RevokedPtr(VALUE obj)
+{
+ X509_REVOKED *rev, *new;
+
+ SafeGetX509Rev(obj, rev);
+ if (!(new = X509_REVOKED_dup(rev))) {
+ ossl_raise(eX509RevError, NULL);
+ }
+
+ return new;
+}
+
+/*
+ * PRIVATE
+ */
+static VALUE
+ossl_x509revoked_alloc(VALUE klass)
+{
+ X509_REVOKED *rev;
+ VALUE obj;
+
+ if (!(rev = X509_REVOKED_new())) {
+ ossl_raise(eX509RevError, NULL);
+ }
+ WrapX509Rev(klass, obj, rev);
+
+ return obj;
+}
+DEFINE_ALLOC_WRAPPER(ossl_x509revoked_alloc)
+
+static VALUE
+ossl_x509revoked_initialize(int argc, VALUE *argv, VALUE self)
+{
+ /* EMPTY */
+ return self;
+}
+
+static VALUE
+ossl_x509revoked_get_serial(VALUE self)
+{
+ X509_REVOKED *rev;
+
+ GetX509Rev(self, rev);
+
+ return asn1integer_to_num(rev->serialNumber);
+}
+
+static VALUE
+ossl_x509revoked_set_serial(VALUE self, VALUE num)
+{
+ X509_REVOKED *rev;
+
+ GetX509Rev(self, rev);
+ rev->serialNumber = num_to_asn1integer(num, rev->serialNumber);
+
+ return num;
+}
+
+static VALUE
+ossl_x509revoked_get_time(VALUE self)
+{
+ X509_REVOKED *rev;
+
+ GetX509Rev(self, rev);
+
+ return asn1time_to_time(rev->revocationDate);
+}
+
+static VALUE
+ossl_x509revoked_set_time(VALUE self, VALUE time)
+{
+ X509_REVOKED *rev;
+ time_t sec;
+
+ GetX509Rev(self, rev);
+ sec = time_to_time_t(time);
+ if (!X509_time_adj(rev->revocationDate, 0, &sec)) {
+ ossl_raise(eX509RevError, NULL);
+ }
+
+ return time;
+}
+/*
+ * Gets X509v3 extensions as array of X509Ext objects
+ */
+static VALUE
+ossl_x509revoked_get_extensions(VALUE self)
+{
+ X509_REVOKED *rev;
+ int count, i;
+ X509_EXTENSION *ext;
+ VALUE ary;
+
+ GetX509Rev(self, rev);
+ count = X509_REVOKED_get_ext_count(rev);
+ if (count < 0) {
+ OSSL_Debug("count < 0???");
+ return rb_ary_new();
+ }
+ ary = rb_ary_new2(count);
+ for (i=0; i<count; i++) {
+ ext = X509_REVOKED_get_ext(rev, i);
+ rb_ary_push(ary, ossl_x509ext_new(ext));
+ }
+
+ return ary;
+}
+
+/*
+ * Sets X509_EXTENSIONs
+ */
+static VALUE
+ossl_x509revoked_set_extensions(VALUE self, VALUE ary)
+{
+ X509_REVOKED *rev;
+ X509_EXTENSION *ext;
+ int i;
+ VALUE item;
+
+ GetX509Rev(self, rev);
+ Check_Type(ary, T_ARRAY);
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ OSSL_Check_Kind(RARRAY(ary)->ptr[i], cX509Ext);
+ }
+ sk_X509_EXTENSION_pop_free(rev->extensions, X509_EXTENSION_free);
+ rev->extensions = NULL;
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ item = RARRAY(ary)->ptr[i];
+ ext = DupX509ExtPtr(item);
+ if(!X509_REVOKED_add_ext(rev, ext, -1)) {
+ ossl_raise(eX509RevError, NULL);
+ }
+ }
+
+ return ary;
+}
+
+static VALUE
+ossl_x509revoked_add_extension(VALUE self, VALUE ext)
+{
+ X509_REVOKED *rev;
+
+ GetX509Rev(self, rev);
+ if(!X509_REVOKED_add_ext(rev, DupX509ExtPtr(ext), -1)) {
+ ossl_raise(eX509RevError, NULL);
+ }
+
+ return ext;
+}
+
+/*
+ * INIT
+ */
+void
+Init_ossl_x509revoked()
+{
+ eX509RevError = rb_define_class_under(mX509, "RevokedError", eOSSLError);
+
+ cX509Rev = rb_define_class_under(mX509, "Revoked", rb_cObject);
+
+ rb_define_alloc_func(cX509Rev, ossl_x509revoked_alloc);
+ rb_define_method(cX509Rev, "initialize", ossl_x509revoked_initialize, -1);
+
+ rb_define_method(cX509Rev, "serial", ossl_x509revoked_get_serial, 0);
+ rb_define_method(cX509Rev, "serial=", ossl_x509revoked_set_serial, 1);
+ rb_define_method(cX509Rev, "time", ossl_x509revoked_get_time, 0);
+ rb_define_method(cX509Rev, "time=", ossl_x509revoked_set_time, 1);
+ rb_define_method(cX509Rev, "extensions", ossl_x509revoked_get_extensions, 0);
+ rb_define_method(cX509Rev, "extensions=", ossl_x509revoked_set_extensions, 1);
+ rb_define_method(cX509Rev, "add_extension", ossl_x509revoked_add_extension, 1);
+}
+
diff --git a/ext/openssl/ossl_x509store.c b/ext/openssl/ossl_x509store.c
new file mode 100644
index 0000000000..d001a02787
--- /dev/null
+++ b/ext/openssl/ossl_x509store.c
@@ -0,0 +1,561 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2002 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#include "ossl.h"
+#include <rubysig.h>
+
+#define WrapX509Store(klass, obj, st) do { \
+ if (!st) { \
+ ossl_raise(rb_eRuntimeError, "STORE wasn't initialized!"); \
+ } \
+ obj = Data_Wrap_Struct(klass, 0, X509_STORE_free, st); \
+} while (0)
+#define GetX509Store(obj, st) do { \
+ Data_Get_Struct(obj, X509_STORE, st); \
+ if (!st) { \
+ ossl_raise(rb_eRuntimeError, "STORE wasn't initialized!"); \
+ } \
+} while (0)
+#define SafeGetX509Store(obj, st) do { \
+ OSSL_Check_Kind(obj, cX509Store); \
+ GetX509Store(obj, st); \
+} while (0)
+
+#define WrapX509StCtx(klass, obj, ctx) do { \
+ if (!ctx) { \
+ ossl_raise(rb_eRuntimeError, "STORE_CTX wasn't initialized!"); \
+ } \
+ obj = Data_Wrap_Struct(klass, 0, ossl_x509stctx_free, ctx); \
+} while (0)
+#define GetX509StCtx(obj, ctx) do { \
+ Data_Get_Struct(obj, X509_STORE_CTX, ctx); \
+ if (!ctx) { \
+ ossl_raise(rb_eRuntimeError, "STORE_CTX is out of scope!"); \
+ } \
+} while (0)
+#define SafeGetX509StCtx(obj, storep) do { \
+ OSSL_Check_Kind(obj, cX509StoreContext); \
+ GetX509Store(obj, ctx); \
+} while (0)
+
+/*
+ * Classes
+ */
+VALUE cX509Store;
+VALUE cX509StoreContext;
+VALUE eX509StoreError;
+
+/*
+ * Public functions
+ */
+VALUE
+ossl_x509store_new(X509_STORE *store)
+{
+ VALUE obj;
+
+ WrapX509Store(cX509Store, obj, store);
+
+ return obj;
+}
+
+X509_STORE *
+GetX509StorePtr(VALUE obj)
+{
+ X509_STORE *store;
+
+ SafeGetX509Store(obj, store);
+
+ return store;
+}
+
+X509_STORE *
+DupX509StorePtr(VALUE obj)
+{
+ X509_STORE *store;
+
+ SafeGetX509Store(obj, store);
+ CRYPTO_add(&store->references, 1, CRYPTO_LOCK_X509_STORE);
+
+ return store;
+}
+
+/*
+ * Private functions
+ */
+static VALUE
+ossl_x509store_alloc(VALUE klass)
+{
+ X509_STORE *store;
+ VALUE obj;
+
+ if((store = X509_STORE_new()) == NULL){
+ ossl_raise(eX509StoreError, NULL);
+ }
+ WrapX509Store(klass, obj, store);
+
+ return obj;
+}
+DEFINE_ALLOC_WRAPPER(ossl_x509store_alloc)
+
+/*
+ * General callback for OpenSSL verify
+ */
+static VALUE
+ossl_x509store_set_vfy_cb(VALUE self, VALUE cb)
+{
+ X509_STORE *store;
+
+ GetX509Store(self, store);
+ X509_STORE_set_ex_data(store, ossl_verify_cb_idx, (void*)cb);
+ rb_iv_set(self, "@verify_callback", cb);
+
+ return cb;
+}
+
+static VALUE
+ossl_x509store_initialize(int argc, VALUE *argv, VALUE self)
+{
+ X509_STORE *store;
+
+ GetX509Store(self, store);
+ X509_STORE_set_verify_cb_func(store, ossl_verify_cb);
+ ossl_x509store_set_vfy_cb(self, Qnil);
+
+#if (OPENSSL_VERSION_NUMBER < 0x00907000L)
+ rb_iv_set(self, "@flags", INT2NUM(0));
+ rb_iv_set(self, "@purpose", INT2NUM(0));
+ rb_iv_set(self, "@trust", INT2NUM(0));
+#endif
+
+ /* last verification status */
+ rb_iv_set(self, "@error", Qnil);
+ rb_iv_set(self, "@error_string", Qnil);
+ rb_iv_set(self, "@chain", Qnil);
+
+ return self;
+}
+
+static VALUE
+ossl_x509store_set_flags(VALUE self, VALUE flags)
+{
+#if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
+ X509_STORE *store;
+
+ GetX509Store(self, store);
+ X509_STORE_set_flags(store, NUM2LONG(flags));
+#else
+ rb_iv_set(self, "@flags", flags);
+#endif
+
+ return flags;
+}
+
+static VALUE
+ossl_x509store_set_purpose(VALUE self, VALUE purpose)
+{
+#if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
+ X509_STORE *store;
+
+ GetX509Store(self, store);
+ X509_STORE_set_purpose(store, NUM2LONG(purpose));
+#else
+ rb_iv_set(self, "@purpose", purpose);
+#endif
+
+ return purpose;
+}
+
+static VALUE
+ossl_x509store_set_trust(VALUE self, VALUE trust)
+{
+#if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
+ X509_STORE *store;
+
+ GetX509Store(self, store);
+ X509_STORE_set_trust(store, NUM2LONG(trust));
+#else
+ rb_iv_set(self, "@trust", trust);
+#endif
+
+ return trust;
+}
+
+static VALUE
+ossl_x509store_add_file(VALUE self, VALUE file)
+{
+ X509_STORE *store;
+ X509_LOOKUP *lookup;
+ char *path = NULL;
+
+ if(file != Qnil){
+ Check_SafeStr(file);
+ path = RSTRING(file)->ptr;
+ }
+ GetX509Store(self, store);
+ lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
+ if(lookup == NULL) ossl_raise(eX509StoreError, NULL);
+ if(X509_LOOKUP_load_file(lookup, path, X509_FILETYPE_PEM) != 1){
+ ossl_raise(eX509StoreError, NULL);
+ }
+
+ return self;
+}
+
+static VALUE
+ossl_x509store_add_path(VALUE self, VALUE dir)
+{
+ X509_STORE *store;
+ X509_LOOKUP *lookup;
+ char *path = NULL;
+
+ if(dir != Qnil){
+ Check_SafeStr(dir);
+ path = RSTRING(dir)->ptr;
+ }
+ GetX509Store(self, store);
+ lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
+ if(lookup == NULL) ossl_raise(eX509StoreError, NULL);
+ if(X509_LOOKUP_add_dir(lookup, path, X509_FILETYPE_PEM) != 1){
+ ossl_raise(eX509StoreError, NULL);
+ }
+
+ return self;
+}
+
+static VALUE
+ossl_x509store_add_cert(VALUE self, VALUE arg)
+{
+ X509_STORE *store;
+ X509 *cert;
+
+ cert = GetX509CertPtr(arg); /* NO NEED TO DUP */
+ GetX509Store(self, store);
+ X509_STORE_add_cert(store, cert);
+
+ return self;
+}
+
+static VALUE
+ossl_x509store_add_crl(VALUE self, VALUE arg)
+{
+ X509_STORE *store;
+ X509_CRL *crl;
+
+ crl = GetX509CRLPtr(arg); /* NO NEED TO DUP */
+ GetX509Store(self, store);
+ X509_STORE_add_crl(store, crl);
+
+ return self;
+}
+
+static VALUE ossl_x509stctx_get_err(VALUE);
+static VALUE ossl_x509stctx_get_err_string(VALUE);
+static VALUE ossl_x509stctx_get_chain(VALUE);
+
+static VALUE
+ossl_x509store_verify(int argc, VALUE *argv, VALUE self)
+{
+ VALUE cert, chain;
+ VALUE ctx, proc, result;
+
+ rb_scan_args(argc, argv, "11", &cert, &chain);
+ ctx = rb_funcall(cX509StoreContext, rb_intern("new"), 3, self, cert, chain);
+ proc = rb_block_given_p() ? rb_block_proc() :
+ rb_iv_get(self, "@verify_callback");
+ rb_iv_set(ctx, "@verify_callback", proc);
+ result = rb_funcall(ctx, rb_intern("verify"), 0);
+
+ rb_iv_set(self, "@error", ossl_x509stctx_get_err(ctx));
+ rb_iv_set(self, "@error_string", ossl_x509stctx_get_err_string(ctx));
+ rb_iv_set(self, "@chain", ossl_x509stctx_get_chain(ctx));
+
+ return result;
+}
+
+/*
+ * Public Functions
+ */
+static void ossl_x509stctx_free(X509_STORE_CTX*);
+
+VALUE
+ossl_x509stctx_new(X509_STORE_CTX *ctx)
+{
+ VALUE obj;
+
+ WrapX509StCtx(cX509StoreContext, obj, ctx);
+
+ return obj;
+}
+
+VALUE
+ossl_x509stctx_clear_ptr(VALUE obj)
+{
+ OSSL_Check_Kind(obj, cX509StoreContext);
+ RDATA(obj)->data = NULL;
+
+ return obj;
+}
+
+/*
+ * Private functions
+ */
+static void
+ossl_x509stctx_free(X509_STORE_CTX *ctx)
+{
+ if(ctx->untrusted)
+ sk_X509_pop_free(ctx->untrusted, X509_free);
+ if(ctx->cert)
+ X509_free(ctx->cert);
+ X509_STORE_CTX_free(ctx);
+}
+
+static VALUE
+ossl_x509stctx_alloc(VALUE klass)
+{
+ X509_STORE_CTX *ctx;
+ VALUE obj;
+
+ if((ctx = X509_STORE_CTX_new()) == NULL){
+ ossl_raise(eX509StoreError, NULL);
+ }
+ WrapX509StCtx(klass, obj, ctx);
+
+ return obj;
+}
+DEFINE_ALLOC_WRAPPER(ossl_x509stctx_alloc)
+
+static VALUE
+ossl_x509stctx_initialize(int argc, VALUE *argv, VALUE self)
+{
+ VALUE store, cert, chain;
+ X509_STORE_CTX *ctx;
+ X509_STORE *x509st;
+ X509 *x509 = NULL;
+ STACK_OF(X509) *x509s = NULL;
+
+ GetX509StCtx(self, ctx);
+ rb_scan_args(argc, argv, "12", &store, &cert, &chain);
+ SafeGetX509Store(store, x509st);
+ if(!NIL_P(cert)) x509 = DupX509CertPtr(cert); /* NEED TO DUP */
+ if(!NIL_P(chain)) x509s = ossl_x509_ary2sk(chain);
+#if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
+ if(X509_STORE_CTX_init(ctx, x509st, x509, x509s) != 1){
+ sk_X509_pop_free(x509s, X509_free);
+ ossl_raise(eX509StoreError, NULL);
+ }
+#else
+ X509_STORE_CTX_init(ctx, x509st, x509, x509s);
+ X509_STORE_CTX_set_flags(ctx, NUM2INT(rb_iv_get(store, "@flags")));
+ X509_STORE_CTX_set_purpose(ctx, NUM2INT(rb_iv_get(store, "@purpose")));
+ X509_STORE_CTX_set_trust(ctx, NUM2INT(rb_iv_get(store, "@trust")));
+#endif
+ rb_iv_set(self, "@verify_callback", rb_iv_get(store, "@verify_callback"));
+ rb_iv_set(self, "@cert", cert);
+
+ return self;
+}
+
+static VALUE
+ossl_x509stctx_verify(VALUE self)
+{
+ X509_STORE_CTX *ctx;
+ int result;
+
+ GetX509StCtx(self, ctx);
+ X509_STORE_CTX_set_ex_data(ctx, ossl_verify_cb_idx,
+ (void*)rb_iv_get(self, "@verify_callback"));
+ result = X509_verify_cert(ctx);
+
+ return result ? Qtrue : Qfalse;
+}
+
+static VALUE
+ossl_x509stctx_get_chain(VALUE self)
+{
+ X509_STORE_CTX *ctx;
+ STACK_OF(X509) *chain;
+ X509 *x509;
+ int i, num;
+ VALUE ary;
+
+ GetX509StCtx(self, ctx);
+ if((chain = X509_STORE_CTX_get_chain(ctx)) == NULL){
+ return Qnil;
+ }
+ if((num = sk_X509_num(chain)) < 0){
+ OSSL_Debug("certs in chain < 0???");
+ return rb_ary_new();
+ }
+ ary = rb_ary_new2(num);
+ for(i = 0; i < num; i++) {
+ x509 = sk_X509_value(chain, i);
+ rb_ary_push(ary, ossl_x509_new(x509));
+ }
+
+ return ary;
+}
+
+static VALUE
+ossl_x509stctx_get_err(VALUE self)
+{
+ X509_STORE_CTX *ctx;
+
+ GetX509StCtx(self, ctx);
+
+ return INT2FIX(X509_STORE_CTX_get_error(ctx));
+}
+
+static VALUE
+ossl_x509stctx_set_error(VALUE self, VALUE err)
+{
+ X509_STORE_CTX *ctx;
+
+ GetX509StCtx(self, ctx);
+ X509_STORE_CTX_set_error(ctx, FIX2INT(err));
+
+ return err;
+}
+
+static VALUE
+ossl_x509stctx_get_err_string(VALUE self)
+{
+ X509_STORE_CTX *ctx;
+ long err;
+
+ GetX509StCtx(self, ctx);
+ err = X509_STORE_CTX_get_error(ctx);
+
+ return rb_str_new2(X509_verify_cert_error_string(err));
+}
+
+static VALUE
+ossl_x509stctx_get_err_depth(VALUE self)
+{
+ X509_STORE_CTX *ctx;
+
+ GetX509StCtx(self, ctx);
+
+ return INT2FIX(X509_STORE_CTX_get_error_depth(ctx));
+}
+
+static VALUE
+ossl_x509stctx_get_curr_cert(VALUE self)
+{
+ X509_STORE_CTX *ctx;
+
+ GetX509StCtx(self, ctx);
+
+ return ossl_x509_new(X509_STORE_CTX_get_current_cert(ctx));
+}
+
+static VALUE
+ossl_x509stctx_get_curr_crl(VALUE self)
+{
+#if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
+ X509_STORE_CTX *ctx;
+
+ GetX509StCtx(self, ctx);
+ if(!ctx->current_crl) return Qnil;
+
+ return ossl_x509crl_new(ctx->current_crl);
+#else
+ return Qnil;
+#endif
+}
+
+static VALUE
+ossl_x509stctx_cleanup(VALUE self)
+{
+ X509_STORE_CTX *ctx;
+
+ GetX509StCtx(self, ctx);
+ X509_STORE_CTX_cleanup(ctx);
+
+ return self;
+}
+
+static VALUE
+ossl_x509stctx_set_flags(VALUE self, VALUE flags)
+{
+ X509_STORE_CTX *store;
+
+ GetX509StCtx(self, store);
+ X509_STORE_CTX_set_flags(store, NUM2LONG(flags));
+
+ return flags;
+}
+
+static VALUE
+ossl_x509stctx_set_purpose(VALUE self, VALUE purpose)
+{
+ X509_STORE_CTX *store;
+
+ GetX509StCtx(self, store);
+ X509_STORE_CTX_set_purpose(store, NUM2LONG(purpose));
+
+ return purpose;
+}
+
+static VALUE
+ossl_x509stctx_set_trust(VALUE self, VALUE trust)
+{
+ X509_STORE_CTX *store;
+
+ GetX509StCtx(self, store);
+ X509_STORE_CTX_set_trust(store, NUM2LONG(trust));
+
+ return trust;
+}
+
+/*
+ * INIT
+ */
+void
+Init_ossl_x509store()
+{
+ VALUE x509stctx;
+
+ eX509StoreError = rb_define_class_under(mX509, "StoreError", eOSSLError);
+
+ cX509Store = rb_define_class_under(mX509, "Store", rb_cObject);
+ rb_attr(cX509Store, rb_intern("verify_callback"), 1, 0, Qfalse);
+ rb_attr(cX509Store, rb_intern("error"), 1, 0, Qfalse);
+ rb_attr(cX509Store, rb_intern("error_string"), 1, 0, Qfalse);
+ rb_attr(cX509Store, rb_intern("chain"), 1, 0, Qfalse);
+ rb_define_alloc_func(cX509Store, ossl_x509store_alloc);
+ rb_define_method(cX509Store, "initialize", ossl_x509store_initialize, -1);
+ rb_define_method(cX509Store, "verify_callback=", ossl_x509store_set_vfy_cb, 1);
+ rb_define_method(cX509Store, "flags=", ossl_x509store_set_flags, 1);
+ rb_define_method(cX509Store, "purpose=", ossl_x509store_set_purpose, 1);
+ rb_define_method(cX509Store, "trust=", ossl_x509store_set_trust, 1);
+ rb_define_method(cX509Store, "add_path", ossl_x509store_add_path, 1);
+ rb_define_method(cX509Store, "add_file", ossl_x509store_add_file, 1);
+ rb_define_method(cX509Store, "add_cert", ossl_x509store_add_cert, 1);
+ rb_define_method(cX509Store, "add_crl", ossl_x509store_add_crl, 1);
+ rb_define_method(cX509Store, "verify", ossl_x509store_verify, -1);
+
+ cX509StoreContext = rb_define_class_under(mX509,"StoreContext",rb_cObject);
+ x509stctx = cX509StoreContext;
+ rb_define_alloc_func(cX509StoreContext, ossl_x509stctx_alloc);
+ rb_define_method(x509stctx,"initialize", ossl_x509stctx_initialize, -1);
+ rb_define_method(x509stctx,"verify", ossl_x509stctx_verify, 0);
+ rb_define_method(x509stctx,"chain", ossl_x509stctx_get_chain,0);
+ rb_define_method(x509stctx,"error", ossl_x509stctx_get_err, 0);
+ rb_define_method(x509stctx,"error=", ossl_x509stctx_set_error, 1);
+ rb_define_method(x509stctx,"error_string",ossl_x509stctx_get_err_string,0);
+ rb_define_method(x509stctx,"error_depth", ossl_x509stctx_get_err_depth, 0);
+ rb_define_method(x509stctx,"current_cert",ossl_x509stctx_get_curr_cert, 0);
+ rb_define_method(x509stctx,"current_crl", ossl_x509stctx_get_curr_crl, 0);
+ rb_define_method(x509stctx,"cleanup", ossl_x509stctx_cleanup, 0);
+ rb_define_method(x509stctx,"flags=", ossl_x509stctx_set_flags, 1);
+ rb_define_method(x509stctx,"purpose=", ossl_x509stctx_set_purpose, 1);
+ rb_define_method(x509stctx,"trust=", ossl_x509stctx_set_trust, 1);
+
+}
diff --git a/ext/openssl/ruby_missing.h b/ext/openssl/ruby_missing.h
new file mode 100644
index 0000000000..bdb152b08e
--- /dev/null
+++ b/ext/openssl/ruby_missing.h
@@ -0,0 +1,70 @@
+/*
+ * $Id$
+ * 'OpenSSL for Ruby' project
+ * Copyright (C) 2001-2003 Michal Rokos <m.rokos@sh.cvut.cz>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#if !defined(_OSSL_RUBY_MISSING_H_)
+#define _OSS_RUBY_MISSING_H_
+
+#if !defined(StringValue)
+# define StringValue(v) \
+ if (TYPE(v) != T_STRING) v = rb_str_to_str(v)
+#endif
+
+#if !defined(StringValuePtr)
+# define StringValuePtr(v) \
+ RSTRING((TYPE(v) == T_STRING) ? (v) : rb_str_to_str(v))->ptr
+#endif
+
+#if !defined(SafeStringValue)
+# define SafeStringValue(v) do {\
+ StringValue(v);\
+ rb_check_safe_str(v);\
+} while (0)
+#endif
+
+#if RUBY_VERSION_CODE < 180
+# define rb_cstr_to_inum(a,b,c) \
+ rb_cstr2inum(a,b)
+# define rb_check_frozen(obj) \
+ if (OBJ_FROZEN(obj)) rb_error_frozen(rb_obj_classname(obj))
+# define rb_obj_classname(obj) \
+ rb_class2name(CLASS_OF(obj))
+#endif
+
+#if HAVE_RB_DEFINE_ALLOC_FUNC
+# define DEFINE_ALLOC_WRAPPER(func)
+#else
+# define DEFINE_ALLOC_WRAPPER(func) \
+ static VALUE \
+ func##_wrapper(int argc, VALUE *argv, VALUE klass) \
+ { \
+ VALUE obj; \
+ \
+ obj = func(klass); \
+ \
+ rb_obj_call_init(obj, argc, argv); \
+ \
+ return obj; \
+ }
+# define rb_define_alloc_func(klass, func) \
+ rb_define_singleton_method(klass, "new", func##_wrapper, -1)
+#endif
+
+#if RUBY_VERSION_CODE >= 180
+# if !defined(HAVE_RB_OBJ_INIT_COPY)
+# define rb_define_copy_func(klass, func) \
+ rb_define_method(klass, "copy_object", func, 1)
+# else
+# define rb_define_copy_func(klass, func) \
+ rb_define_method(klass, "initialize_copy", func, 1)
+# endif
+#endif
+
+#endif /* _OSS_RUBY_MISSING_H_ */
+
diff --git a/ext/openssl/sample/c_rehash.rb b/ext/openssl/sample/c_rehash.rb
new file mode 100644
index 0000000000..386eef5f24
--- /dev/null
+++ b/ext/openssl/sample/c_rehash.rb
@@ -0,0 +1,174 @@
+#!/usr/bin/env ruby
+
+require 'openssl'
+require 'md5'
+
+class CHashDir
+ include Enumerable
+
+ def initialize(dirpath)
+ @dirpath = dirpath
+ @fingerprint_cache = @cert_cache = @crl_cache = nil
+ end
+
+ def hash_dir(silent = false)
+ # ToDo: Should lock the directory...
+ @silent = silent
+ @fingerprint_cache = Hash.new
+ @cert_cache = Hash.new
+ @crl_cache = Hash.new
+ do_hash_dir
+ end
+
+ def get_certs(name = nil)
+ if name
+ @cert_cache[hash_name(name)]
+ else
+ @cert_cache.values.flatten
+ end
+ end
+
+ def get_crls(name = nil)
+ if name
+ @crl_cache[hash_name(name)]
+ else
+ @crl_cache.values.flatten
+ end
+ end
+
+ def delete_crl(crl)
+ File.unlink(crl_filename(crl))
+ hash_dir(true)
+ end
+
+ def add_crl(crl)
+ File.open(crl_filename(crl), "w") do |f|
+ f << crl.to_pem
+ end
+ hash_dir(true)
+ end
+
+ def load_pem_file(filepath)
+ str = File.read(filepath)
+ begin
+ OpenSSL::X509::Certificate.new(str)
+ rescue
+ begin
+ OpenSSL::X509::CRL.new(str)
+ rescue
+ begin
+ OpenSSL::X509::Request.new(str)
+ rescue
+ nil
+ end
+ end
+ end
+ end
+
+private
+
+ def crl_filename(crl)
+ path(hash_name(crl.issuer)) + '.pem'
+ end
+
+ def do_hash_dir
+ Dir.chdir(@dirpath) do
+ delete_symlink
+ Dir.glob('*.pem') do |pemfile|
+ cert = load_pem_file(pemfile)
+ case cert
+ when OpenSSL::X509::Certificate
+ link_hash_cert(pemfile, cert)
+ when OpenSSL::X509::CRL
+ link_hash_crl(pemfile, cert)
+ else
+ STDERR.puts("WARNING: #{pemfile} does not contain a certificate or CRL: skipping") unless @silent
+ end
+ end
+ end
+ end
+
+ def delete_symlink
+ Dir.entries(".").each do |entry|
+ next unless /^[\da-f]+\.r{0,1}\d+$/ =~ entry
+ File.unlink(entry) if FileTest.symlink?(entry)
+ end
+ end
+
+ def link_hash_cert(org_filename, cert)
+ name_hash = hash_name(cert.subject)
+ fingerprint = fingerprint(cert.to_der)
+ filepath = link_hash(org_filename, name_hash, fingerprint) { |idx|
+ "#{name_hash}.#{idx}"
+ }
+ unless filepath
+ unless @silent
+ STDERR.puts("WARNING: Skipping duplicate certificate #{org_filename}")
+ end
+ else
+ (@cert_cache[name_hash] ||= []) << path(filepath)
+ end
+ end
+
+ def link_hash_crl(org_filename, crl)
+ name_hash = hash_name(crl.issuer)
+ fingerprint = fingerprint(crl.to_der)
+ filepath = link_hash(org_filename, name_hash, fingerprint) { |idx|
+ "#{name_hash}.r#{idx}"
+ }
+ unless filepath
+ unless @silent
+ STDERR.puts("WARNING: Skipping duplicate CRL #{org_filename}")
+ end
+ else
+ (@crl_cache[name_hash] ||= []) << path(filepath)
+ end
+ end
+
+ def link_hash(org_filename, name, fingerprint)
+ idx = 0
+ filepath = nil
+ while true
+ filepath = yield(idx)
+ break unless FileTest.symlink?(filepath) or FileTest.exist?(filepath)
+ if @fingerprint_cache[filepath] == fingerprint
+ return false
+ end
+ idx += 1
+ end
+ STDOUT.puts("#{org_filename} => #{filepath}") unless @silent
+ symlink(org_filename, filepath)
+ @fingerprint_cache[filepath] = fingerprint
+ filepath
+ end
+
+ def symlink(from, to)
+ begin
+ File.symlink(from, to)
+ rescue
+ File.open(to, "w") do |f|
+ f << File.read(from)
+ end
+ end
+ end
+
+ def path(filename)
+ File.join(@dirpath, filename)
+ end
+
+ def hash_name(name)
+ sprintf("%x", name.hash)
+ end
+
+ def fingerprint(der)
+ MD5.hexdigest(der).upcase
+ end
+end
+
+if $0 == __FILE__
+ dirlist = ARGV
+ dirlist << '/usr/ssl/certs' if dirlist.empty?
+ dirlist.each do |dir|
+ CHashDir.new(dir).hash_dir
+ end
+end
diff --git a/ext/openssl/sample/cert2text.rb b/ext/openssl/sample/cert2text.rb
new file mode 100644
index 0000000000..50da224e76
--- /dev/null
+++ b/ext/openssl/sample/cert2text.rb
@@ -0,0 +1,23 @@
+#!/usr/bin/env ruby
+
+require 'openssl'
+include OpenSSL::X509
+
+def cert2text(cert_str)
+ [Certificate, CRL, Request].each do |klass|
+ begin
+ puts klass.new(cert_str).to_text
+ return
+ rescue
+ end
+ end
+ raise ArgumentError.new('Unknown format.')
+end
+
+if ARGV.empty?
+ cert2text(STDIN.read)
+else
+ ARGV.each do |file|
+ cert2text(File.read(file))
+ end
+end
diff --git a/ext/openssl/sample/cert_store_view.rb b/ext/openssl/sample/cert_store_view.rb
new file mode 100644
index 0000000000..26c4d527f7
--- /dev/null
+++ b/ext/openssl/sample/cert_store_view.rb
@@ -0,0 +1,911 @@
+#!/usr/bin/env ruby
+
+require 'fox'
+require 'openssl'
+require 'time'
+require 'certstore'
+require 'getopts'
+
+include Fox
+
+module CertDumpSupport
+ def cert_label(cert)
+ subject_alt_name =
+ cert.extensions.find { |ext| ext.oid == 'subjectAltName' }
+ if subject_alt_name
+ subject_alt_name.value.split(/\s*,\s/).each do |alt_name_pair|
+ alt_tag, alt_name = alt_name_pair.split(/:/)
+ return alt_name
+ end
+ end
+ name_label(cert.subject)
+ end
+
+ def name_label(name)
+ ary = name.to_a
+ if (cn = ary.find { |rdn| rdn[0] == 'CN' })
+ return cn[1]
+ end
+ if ary.last[0] == 'OU'
+ return ary.last[1]
+ end
+ name.to_s
+ end
+
+ def name_text(name)
+ name.to_a.collect { |tag, value|
+ "#{tag} = #{value}"
+ }.reverse.join("\n")
+ end
+
+ def bn_label(bn)
+ ("0" << sprintf("%X", bn)).scan(/../).join(" ")
+ end
+end
+
+class CertDump
+ include CertDumpSupport
+
+ def initialize(cert)
+ @cert = cert
+ end
+
+ def get_dump(tag)
+ case tag
+ when 'Version'
+ version
+ when 'Serial'
+ serial
+ when 'Signature Algorithm'
+ signature_algorithm
+ when 'Issuer'
+ issuer
+ when 'Validity'
+ validity
+ when 'Not before'
+ not_before
+ when 'Not after'
+ not_after
+ when 'Subject'
+ subject
+ when 'Public key'
+ public_key
+ else
+ ext(tag)
+ end
+ end
+
+ def get_dump_line(tag)
+ case tag
+ when 'Version'
+ version_line
+ when 'Serial'
+ serial_line
+ when 'Signature Algorithm'
+ signature_algorithm_line
+ when 'Subject'
+ subject_line
+ when 'Issuer'
+ issuer_line
+ when 'Validity'
+ validity_line
+ when 'Not before'
+ not_before_line
+ when 'Not after'
+ not_after_line
+ when 'Public key'
+ public_key_line
+ else
+ ext_line(tag)
+ end
+ end
+
+private
+
+ def version
+ "Version: #{@cert.version + 1}"
+ end
+
+ def version_line
+ version
+ end
+
+ def serial
+ bn_label(@cert.serial)
+ end
+
+ def serial_line
+ serial
+ end
+
+ def signature_algorithm
+ @cert.signature_algorithm
+ end
+
+ def signature_algorithm_line
+ signature_algorithm
+ end
+
+ def subject
+ name_text(@cert.subject)
+ end
+
+ def subject_line
+ @cert.subject.to_s
+ end
+
+ def issuer
+ name_text(@cert.issuer)
+ end
+
+ def issuer_line
+ @cert.issuer.to_s
+ end
+
+ def validity
+ <<EOS
+Not before: #{not_before}
+Not after: #{not_after}
+EOS
+ end
+
+ def validity_line
+ "from #{@cert.not_before.iso8601} to #{@cert.not_after.iso8601}"
+ end
+
+ def not_before
+ @cert.not_before.to_s
+ end
+
+ def not_before_line
+ not_before
+ end
+
+ def not_after
+ @cert.not_after.to_s
+ end
+
+ def not_after_line
+ not_after
+ end
+
+ def public_key
+ @cert.public_key.to_text
+ end
+
+ def public_key_line
+ "#{@cert.public_key.class} -- " << public_key.scan(/\A[^\n]*/)[0] << '...'
+ end
+
+ def ext(tag)
+ @cert.extensions.each do |ext|
+ if ext.oid == tag
+ return ext_detail(tag, ext.value)
+ end
+ end
+ "(unknown)"
+ end
+
+ def ext_line(tag)
+ ext(tag).tr("\r\n", '')
+ end
+
+ def ext_detail(tag, value)
+ value
+ end
+end
+
+class CrlDump
+ include CertDumpSupport
+
+ def initialize(crl)
+ @crl = crl
+ end
+
+ def get_dump(tag)
+ case tag
+ when 'Version'
+ version
+ when 'Signature Algorithm'
+ signature_algorithm
+ when 'Issuer'
+ issuer
+ when 'Last update'
+ last_update
+ when 'Next update'
+ next_update
+ else
+ ext(tag)
+ end
+ end
+
+ def get_dump_line(tag)
+ case tag
+ when 'Version'
+ version_line
+ when 'Signature Algorithm'
+ signature_algorithm_line
+ when 'Issuer'
+ issuer_line
+ when 'Last update'
+ last_update_line
+ when 'Next update'
+ next_update_line
+ else
+ ext_line(tag)
+ end
+ end
+
+private
+
+ def version
+ "Version: #{@crl.version + 1}"
+ end
+
+ def version_line
+ version
+ end
+
+ def signature_algorithm
+ @crl.signature_algorithm
+ end
+
+ def signature_algorithm_line
+ signature_algorithm
+ end
+
+ def issuer
+ name_text(@crl.issuer)
+ end
+
+ def issuer_line
+ @crl.issuer.to_s
+ end
+
+ def last_update
+ @crl.last_update.to_s
+ end
+
+ def last_update_line
+ last_update
+ end
+
+ def next_update
+ @crl.next_update.to_s
+ end
+
+ def next_update_line
+ next_update
+ end
+
+ def ext(tag)
+ @crl.extensions.each do |ext|
+ if ext.oid == tag
+ return ext_detail(tag, ext.value)
+ end
+ end
+ "(unknown)"
+ end
+
+ def ext_line(tag)
+ ext(tag).tr("\r\n", '')
+ end
+
+ def ext_detail(tag, value)
+ value
+ end
+end
+
+class RevokedDump
+ include CertDumpSupport
+
+ def initialize(revoked)
+ @revoked = revoked
+ end
+
+ def get_dump(tag)
+ case tag
+ when 'Serial'
+ serial
+ when 'Time'
+ time
+ else
+ ext(tag)
+ end
+ end
+
+ def get_dump_line(tag)
+ case tag
+ when 'Serial'
+ serial_line
+ when 'Time'
+ time_line
+ else
+ ext_line(tag)
+ end
+ end
+
+private
+
+ def serial
+ bn_label(@revoked.serial)
+ end
+
+ def serial_line
+ serial
+ end
+
+ def time
+ @revoked.time.to_s
+ end
+
+ def time_line
+ time
+ end
+
+ def ext(tag)
+ @revoked.extensions.each do |ext|
+ if ext.oid == tag
+ return ext_detail(tag, ext.value)
+ end
+ end
+ "(unknown)"
+ end
+
+ def ext_line(tag)
+ ext(tag).tr("\r\n", '')
+ end
+
+ def ext_detail(tag, value)
+ value
+ end
+end
+
+class RequestDump
+ include CertDumpSupport
+
+ def initialize(req)
+ @req = req
+ end
+
+ def get_dump(tag)
+ case tag
+ when 'Version'
+ version
+ when 'Signature Algorithm'
+ signature_algorithm
+ when 'Subject'
+ subject
+ when 'Public key'
+ public_key
+ else
+ attributes(tag)
+ end
+ end
+
+ def get_dump_line(tag)
+ case tag
+ when 'Version'
+ version_line
+ when 'Signature Algorithm'
+ signature_algorithm_line
+ when 'Subject'
+ subject_line
+ when 'Public key'
+ public_key_line
+ else
+ attributes_line(tag)
+ end
+ end
+
+private
+
+ def version
+ "Version: #{@req.version + 1}"
+ end
+
+ def version_line
+ version
+ end
+
+ def signature_algorithm
+ @req.signature_algorithm
+ end
+
+ def signature_algorithm_line
+ signature_algorithm
+ end
+
+ def subject
+ name_text(@req.subject)
+ end
+
+ def subject_line
+ @req.subject.to_s
+ end
+
+ def public_key
+ @req.public_key.to_text
+ end
+
+ def public_key_line
+ "#{@req.public_key.class} -- " << public_key.scan(/\A[^\n]*/)[0] << '...'
+ end
+
+ def attributes(tag)
+ "(unknown)"
+ end
+
+ def attributes_line(tag)
+ attributes(tag).tr("\r\n", '')
+ end
+end
+
+class CertStoreView < FXMainWindow
+ class CertTree
+ include CertDumpSupport
+
+ def initialize(observer, tree)
+ @observer = observer
+ @tree = tree
+ @tree.connect(SEL_COMMAND) do |sender, sel, item|
+ if item.data
+ @observer.getApp().beginWaitCursor do
+ @observer.show_item(item.data)
+ end
+ else
+ @observer.show_item(nil)
+ end
+ end
+ end
+
+ def show(cert_store)
+ @tree.clearItems
+ @self_signed_ca_node = add_item_last(nil, "Trusted root CA")
+ @other_ca_node = add_item_last(nil, "Intermediate CA")
+ @ee_node = add_item_last(nil, "Personal")
+ @crl_node = add_item_last(nil, "CRL")
+ @request_node = add_item_last(nil, "Request")
+ @verify_path_node = add_item_last(nil, "Certification path")
+ show_certs(cert_store)
+ end
+
+ def show_certs(cert_store)
+ remove_items(@self_signed_ca_node)
+ remove_items(@other_ca_node)
+ remove_items(@ee_node)
+ remove_items(@crl_node)
+ remove_items(@request_node)
+ import_certs(cert_store)
+ end
+
+ def show_request(req)
+ node = add_item_last(@request_node, name_label(req.subject), req)
+ @tree.selectItem(node)
+ @observer.show_item(req)
+ end
+
+ def show_verify_path(verify_path)
+ add_verify_path(verify_path)
+ end
+
+ private
+
+ def open_node(node)
+ node.expanded = node.opened = true
+ end
+
+ def close_node(node)
+ node.expanded = node.opened = false
+ end
+
+ def import_certs(cert_store)
+ cert_store.self_signed_ca.each do |cert|
+ add_item_last(@self_signed_ca_node, cert_label(cert), cert)
+ end
+ cert_store.other_ca.each do |cert|
+ add_item_last(@other_ca_node, cert_label(cert), cert)
+ end
+ cert_store.ee.each do |cert|
+ add_item_last(@ee_node, cert_label(cert), cert)
+ end
+ cert_store.crl.each do |crl|
+ node = add_item_last(@crl_node, name_label(crl.issuer), crl)
+ close_node(node)
+ crl.revoked.each do |revoked|
+ add_item_last(node, bn_label(revoked.serial), revoked)
+ end
+ end
+ cert_store.request.each do |req|
+ add_item_last(@requestnode, name_label(req.subject), req)
+ end
+ end
+
+ def add_verify_path(verify_path)
+ node = @verify_path_node
+ last_cert = nil
+ verify_path.reverse_each do |ok, cert, crl_check, error_string|
+ warn = []
+ if @observer.cert_store.is_ca?(cert)
+ warn << 'NO ARL' unless crl_check
+ else
+ warn << 'NO CRL' unless crl_check
+ end
+ warn_str = '(' << warn.join(", ") << ')'
+ warn_mark = warn.empty? ? '' : '!'
+ label = if ok
+ "OK#{warn_mark}..." + cert_label(cert)
+ else
+ "NG(#{error_string})..." + cert_label(cert)
+ end
+ label << warn_str unless warn.empty?
+ node = add_item_last(node, label, cert)
+ node.expanded = true
+ last_cert = cert
+ end
+ if last_cert
+ @tree.selectItem(node)
+ @observer.show_item(last_cert)
+ end
+ end
+
+ def add_item_last(parent, label, obj = nil)
+ node = @tree.addItemLast(parent, FXTreeItem.new(label))
+ node.data = obj if obj
+ open_node(node)
+ node
+ end
+
+ def remove_items(node)
+ while node.getNumChildren > 0
+ @tree.removeItem(node.getFirst)
+ end
+ end
+ end
+
+ class CertInfo
+ def initialize(observer, table)
+ @observer = observer
+ @table = table
+ @table.leadingRows = 0
+ @table.leadingCols = 0
+ @table.trailingRows = 0
+ @table.trailingCols = 0
+ @table.showVertGrid(false)
+ @table.showHorzGrid(false)
+ @table.setTableSize(1, 2)
+ @table.setColumnWidth(0, 125)
+ @table.setColumnWidth(1, 350)
+ end
+
+ def show(item)
+ @observer.show_detail(nil, nil)
+ if item.nil?
+ set_column_size(1)
+ return
+ end
+ case item
+ when OpenSSL::X509::Certificate
+ show_cert(item)
+ when OpenSSL::X509::CRL
+ show_crl(item)
+ when OpenSSL::X509::Revoked
+ show_revoked(item)
+ when OpenSSL::X509::Request
+ show_request(item)
+ else
+ raise NotImplementedError.new("Unknown item type #{item.class}.")
+ end
+ end
+
+ private
+
+ def show_cert(cert)
+ wrap = CertDump.new(cert)
+ items = []
+ items << ['Version', wrap.get_dump_line('Version')]
+ items << ['Signature Algorithm', wrap.get_dump_line('Signature Algorithm')]
+ items << ['Issuer', wrap.get_dump_line('Issuer')]
+ items << ['Serial', wrap.get_dump_line('Serial')]
+ #items << ['Not before', wrap.get_dump_line('Not before')]
+ #items << ['Not after', wrap.get_dump_line('Not after')]
+ items << ['Subject', wrap.get_dump_line('Subject')]
+ items << ['Public key', wrap.get_dump_line('Public key')]
+ items << ['Validity', wrap.get_dump_line('Validity')]
+ (cert.extensions.sort { |a, b| a.oid <=> b.oid }).each do |ext|
+ items << [ext.oid, wrap.get_dump_line(ext.oid)]
+ end
+ show_items(cert, items)
+ end
+
+ def show_crl(crl)
+ wrap = CrlDump.new(crl)
+ items = []
+ items << ['Version', wrap.get_dump_line('Version')]
+ items << ['Signature Algorithm', wrap.get_dump_line('Signature Algorithm')]
+ items << ['Issuer', wrap.get_dump_line('Issuer')]
+ items << ['Last update', wrap.get_dump_line('Last update')]
+ items << ['Next update', wrap.get_dump_line('Next update')]
+ crl.extensions.each do |ext|
+ items << [ext.oid, wrap.get_dump_line(ext.oid)]
+ end
+ show_items(crl, items)
+ end
+
+ def show_revoked(revoked)
+ wrap = RevokedDump.new(revoked)
+ items = []
+ items << ['Serial', wrap.get_dump_line('Serial')]
+ items << ['Time', wrap.get_dump_line('Time')]
+ revoked.extensions.each do |ext|
+ items << [ext.oid, wrap.get_dump_line(ext.oid)]
+ end
+ show_items(revoked, items)
+ end
+
+ def show_request(req)
+ wrap = RequestDump.new(req)
+ items = []
+ items << ['Version', wrap.get_dump_line('Version')]
+ items << ['Signature Algorithm', wrap.get_dump_line('Signature Algorithm')]
+ items << ['Subject', wrap.get_dump_line('Subject')]
+ items << ['Public key', wrap.get_dump_line('Public key')]
+ req.attributes.each do |attr|
+ items << [attr.attr, wrap.get_dump_line(attr.oid)]
+ end
+ show_items(req, items)
+ end
+
+ def show_items(obj, items)
+ set_column_size(items.size)
+ items.each_with_index do |ele, idx|
+ tag, value = ele
+ @table.setItemText(idx, 0, tag)
+ @table.getItem(idx, 0).data = tag
+ @table.setItemText(idx, 1, value.to_s)
+ @table.getItem(idx, 1).data = tag
+ end
+ @table.connect(SEL_COMMAND) do |sender, sel, loc|
+ item = @table.getItem(loc.row, loc.col)
+ @observer.show_detail(obj, item.data)
+ end
+ justify_table
+ end
+
+ def set_column_size(size)
+ col0_width = @table.getColumnWidth(0)
+ col1_width = @table.getColumnWidth(1)
+ @table.setTableSize(size, 2)
+ @table.setColumnWidth(0, col0_width)
+ @table.setColumnWidth(1, col1_width)
+ end
+
+ def justify_table
+ for col in 0..@table.numCols-1
+ for row in 0..@table.numRows-1
+ @table.getItem(row, col).justify = FXTableItem::LEFT
+ end
+ end
+ end
+ end
+
+ class CertDetail
+ def initialize(observer, detail)
+ @observer = observer
+ @detail = detail
+ end
+
+ def show(item, tag)
+ if item.nil?
+ @detail.text = ''
+ return
+ end
+ case item
+ when OpenSSL::X509::Certificate
+ show_cert(item, tag)
+ when OpenSSL::X509::CRL
+ show_crl(item, tag)
+ when OpenSSL::X509::Revoked
+ show_revoked(item, tag)
+ when OpenSSL::X509::Request
+ show_request(item, tag)
+ else
+ raise NotImplementedError.new("Unknown item type #{item.class}.")
+ end
+ end
+
+ private
+
+ def show_cert(cert, tag)
+ wrap = CertDump.new(cert)
+ @detail.text = wrap.get_dump(tag)
+ end
+
+ def show_crl(crl, tag)
+ wrap = CrlDump.new(crl)
+ @detail.text = wrap.get_dump(tag)
+ end
+
+ def show_revoked(revoked, tag)
+ wrap = RevokedDump.new(revoked)
+ @detail.text = wrap.get_dump(tag)
+ end
+
+ def show_request(request, tag)
+ wrap = RequestDump.new(request)
+ @detail.text = wrap.get_dump(tag)
+ end
+ end
+
+ attr_reader :cert_store
+
+ def initialize(app, cert_store)
+ @cert_store = cert_store
+ @verify_filter = 0
+ @verify_filename = nil
+ full_width = 800
+ full_height = 500
+ horz_pos = 300
+
+ super(app, "Certificate store", nil, nil, DECOR_ALL, 0, 0, full_width,
+ full_height)
+
+ FXTooltip.new(self.getApp())
+
+ menubar = FXMenubar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X)
+ file_menu = FXMenuPane.new(self)
+ FXMenuTitle.new(menubar, "&File", nil, file_menu)
+ file_open_menu = FXMenuPane.new(self)
+ FXMenuCommand.new(file_open_menu, "&Directory\tCtl-O").connect(SEL_COMMAND,
+ method(:on_cmd_file_open_dir))
+ FXMenuCascade.new(file_menu, "&Open\tCtl-O", nil, file_open_menu)
+ FXMenuCommand.new(file_menu, "&Quit\tCtl-Q", nil, getApp(), FXApp::ID_QUIT)
+
+ tool_menu = FXMenuPane.new(self)
+ FXMenuTitle.new(menubar, "&Tool", nil, tool_menu)
+ FXMenuCommand.new(tool_menu, "&Verify\tCtl-N").connect(SEL_COMMAND,
+ method(:on_cmd_tool_verify))
+ FXMenuCommand.new(tool_menu, "&Show Request\tCtl-R").connect(SEL_COMMAND,
+ method(:on_cmd_tool_request))
+
+ base_frame = FXHorizontalFrame.new(self, LAYOUT_FILL_X | LAYOUT_FILL_Y)
+ splitter_horz = FXSplitter.new(base_frame, LAYOUT_SIDE_TOP | LAYOUT_FILL_X |
+ LAYOUT_FILL_Y | SPLITTER_TRACKING | SPLITTER_HORIZONTAL)
+
+ # Cert tree
+ cert_tree_frame = FXHorizontalFrame.new(splitter_horz, LAYOUT_FILL_X |
+ LAYOUT_FILL_Y | FRAME_SUNKEN | FRAME_THICK)
+ cert_tree_frame.setWidth(horz_pos)
+ cert_tree = FXTreeList.new(cert_tree_frame, 0, nil, 0,
+ TREELIST_BROWSESELECT | TREELIST_SHOWS_LINES | TREELIST_SHOWS_BOXES |
+ TREELIST_ROOT_BOXES | LAYOUT_FILL_X | LAYOUT_FILL_Y)
+ @cert_tree = CertTree.new(self, cert_tree)
+
+ # Cert info
+ splitter_vert = FXSplitter.new(splitter_horz, LAYOUT_SIDE_TOP |
+ LAYOUT_FILL_X | LAYOUT_FILL_Y | SPLITTER_TRACKING | SPLITTER_VERTICAL |
+ SPLITTER_REVERSED)
+ cert_list_base = FXVerticalFrame.new(splitter_vert, LAYOUT_FILL_X |
+ LAYOUT_FILL_Y, 0,0,0,0, 0,0,0,0)
+ cert_list_frame = FXHorizontalFrame.new(cert_list_base, FRAME_SUNKEN |
+ FRAME_THICK | LAYOUT_FILL_X | LAYOUT_FILL_Y)
+ cert_info = FXTable.new(cert_list_frame, 2, 10, nil, 0, FRAME_SUNKEN |
+ TABLE_COL_SIZABLE | LAYOUT_FILL_X | LAYOUT_FILL_Y, 0, 0, 0, 0, 2, 2, 2, 2)
+ @cert_info = CertInfo.new(self, cert_info)
+
+ cert_detail_base = FXVerticalFrame.new(splitter_vert, LAYOUT_FILL_X |
+ LAYOUT_FILL_Y, 0,0,0,0, 0,0,0,0)
+ cert_detail_frame = FXHorizontalFrame.new(cert_detail_base, FRAME_SUNKEN |
+ FRAME_THICK | LAYOUT_FILL_X | LAYOUT_FILL_Y)
+ cert_detail = FXText.new(cert_detail_frame, nil, 0, TEXT_READONLY |
+ LAYOUT_FILL_X | LAYOUT_FILL_Y)
+ @cert_detail = CertDetail.new(self, cert_detail)
+
+ show_init
+ end
+
+ def create
+ super
+ show(PLACEMENT_SCREEN)
+ end
+
+ def show_init
+ @cert_tree.show(@cert_store)
+ show_item(nil)
+ end
+
+ def show_certs
+ @cert_tree.show_certs(@cert_store)
+ end
+
+ def show_request(req)
+ @cert_tree.show_request(req)
+ end
+
+ def show_verify_path(verify_path)
+ @cert_tree.show_verify_path(verify_path)
+ end
+
+ def show_item(item)
+ @cert_info.show(item) if @cert_info
+ end
+
+ def show_detail(item, tag)
+ @cert_detail.show(item, tag) if @cert_detail
+ end
+
+ def verify(certfile)
+ path = verify_certfile(certfile)
+ show_certs # CRL could be change.
+ show_verify_path(path)
+ end
+
+private
+
+ def on_cmd_file_open_dir(sender, sel, ptr)
+ dir = FXFileDialog.getOpenDirectory(self, "Open certificate directory", ".")
+ unless dir.empty?
+ begin
+ @cert_store = CertStore.new(dir)
+ rescue
+ show_error($!)
+ end
+ show_init
+ end
+ 1
+ end
+
+ def on_cmd_tool_verify(sender, sel, ptr)
+ dialog = FXFileDialog.new(self, "Verify certificate")
+ dialog.filename = ''
+ dialog.patternList = ["All Files (*)", "PEM formatted certificate (*.pem)"]
+ dialog.currentPattern = @verify_filter
+ if dialog.execute != 0
+ @verify_filename = dialog.filename
+ verify(@verify_filename)
+ end
+ @verify_filter = dialog.currentPattern
+ 1
+ end
+
+ def on_cmd_tool_request(sender, sel, ptr)
+ dialog = FXFileDialog.new(self, "Show request")
+ dialog.filename = ''
+ dialog.patternList = ["All Files (*)", "PEM formatted certificate (*.pem)"]
+ if dialog.execute != 0
+ req = @cert_store.generate_cert(dialog.filename)
+ show_request(req)
+ end
+ 1
+ end
+
+ def verify_certfile(filename)
+ begin
+ cert = @cert_store.generate_cert(filename)
+ result = @cert_store.verify(cert)
+ @cert_store.scan_certs
+ result
+ rescue
+ show_error($!)
+ []
+ end
+ end
+
+ def show_error(e)
+ msg = e.inspect + "\n" + e.backtrace.join("\n")
+ FXMessageBox.error(self, MBOX_OK, "Error", msg)
+ end
+end
+
+getopts nil, "cert:"
+
+certs_dir = ARGV.shift or raise "#{$0} cert_dir"
+certfile = $OPT_cert
+app = FXApp.new("CertStore", "FoxTest")
+cert_store = CertStore.new(certs_dir)
+w = CertStoreView.new(app, cert_store)
+app.create
+if certfile
+ w.verify(certfile)
+end
+app.run
diff --git a/ext/openssl/sample/certstore.rb b/ext/openssl/sample/certstore.rb
new file mode 100644
index 0000000000..bbc637f668
--- /dev/null
+++ b/ext/openssl/sample/certstore.rb
@@ -0,0 +1,161 @@
+require 'c_rehash'
+require 'crlstore'
+
+
+class CertStore
+ include OpenSSL
+ include X509
+
+ attr_reader :self_signed_ca
+ attr_reader :other_ca
+ attr_reader :ee
+ attr_reader :crl
+ attr_reader :request
+
+ def initialize(certs_dir)
+ @certs_dir = certs_dir
+ @c_store = CHashDir.new(@certs_dir)
+ @c_store.hash_dir(true)
+ @crl_store = CrlStore.new(@c_store)
+ @x509store = Store.new
+ @self_signed_ca = @other_ca = @ee = @crl = nil
+
+ # Uncomment this line to let OpenSSL to check CRL for each certs.
+ # @x509store.flags = V_FLAG_CRL_CHECK | V_FLAG_CRL_CHECK_ALL
+
+ add_path
+ scan_certs
+ end
+
+ def generate_cert(filename)
+ @c_store.load_pem_file(filename)
+ end
+
+ def verify(cert)
+ error, crl_map = do_verify(cert)
+ if error
+ [[false, cert, crl_map[cert.subject], error]]
+ else
+ @x509store.chain.collect { |c| [true, c, crl_map[c.subject], nil] }
+ end
+ end
+
+ def match_cert(cert1, cert2)
+ (cert1.issuer.cmp(cert2.issuer) == 0) and cert1.serial == cert2.serial
+ end
+
+ def is_ca?(cert)
+ case guess_cert_type(cert)
+ when CERT_TYPE_SELF_SIGNED
+ true
+ when CERT_TYPE_OTHER
+ true
+ else
+ false
+ end
+ end
+
+ def scan_certs
+ @self_signed_ca = []
+ @other_ca = []
+ @ee = []
+ @crl = []
+ @request = []
+ load_certs
+ end
+
+private
+
+ def add_path
+ @x509store.add_path(@certs_dir)
+ end
+
+ def do_verify(cert)
+ error_map = {}
+ crl_map = {}
+ result = @x509store.verify(cert) do |ok, ctx|
+ cert = ctx.current_cert
+ if ctx.current_crl
+ crl_map[cert.subject] = true
+ end
+ if ok
+ if !ctx.current_crl
+ if crl = @crl_store.find_crl(cert)
+ crl_map[cert.subject] = true
+ if crl.revoked.find { |revoked| revoked.serial == cert.serial }
+ ok = false
+ error_string = 'certification revoked'
+ end
+ end
+ end
+ end
+ error_map[cert.subject] = error_string if error_string
+ ok
+ end
+ error = if result
+ nil
+ else
+ error_map[cert.subject] || @x509store.error_string
+ end
+ return error, crl_map
+ end
+
+ def load_certs
+ @c_store.get_certs.each do |certfile|
+ cert = generate_cert(certfile)
+ case guess_cert_type(cert)
+ when CERT_TYPE_SELF_SIGNED
+ @self_signed_ca << cert
+ when CERT_TYPE_OTHER
+ @other_ca << cert
+ when CERT_TYPE_EE
+ @ee << cert
+ else
+ raise "Unknown cert type."
+ end
+ end
+ @c_store.get_crls.each do |crlfile|
+ @crl << generate_cert(crlfile)
+ end
+ end
+
+ CERT_TYPE_SELF_SIGNED = 0
+ CERT_TYPE_OTHER = 1
+ CERT_TYPE_EE = 2
+ def guess_cert_type(cert)
+ ca = self_signed = is_cert_self_signed(cert)
+ cert.extensions.each do |ext|
+ # Ignores criticality of extensions. It's 'guess'ing.
+ case ext.oid
+ when 'basicConstraints'
+ /CA:(TRUE|FALSE), pathlen:(\d+)/ =~ ext.value
+ ca = ($1 == 'TRUE') unless ca
+ when 'keyUsage'
+ usage = ext.value.split(/\s*,\s*/)
+ ca = usage.include?('Certificate Sign') unless ca
+ when 'nsCertType'
+ usage = ext.value.split(/\s*,\s*/)
+ ca = usage.include?('SSL CA') unless ca
+ end
+ end
+ if ca
+ if self_signed
+ CERT_TYPE_SELF_SIGNED
+ else
+ CERT_TYPE_OTHER
+ end
+ else
+ CERT_TYPE_EE
+ end
+ end
+
+ def is_cert_self_signed(cert)
+ # cert.subject.cmp(cert.issuer) == 0
+ cert.subject.to_s == cert.issuer.to_s
+ end
+end
+
+
+if $0 == __FILE__
+ c = CertStore.new("trust_certs")
+end
diff --git a/ext/openssl/sample/cipher.rb b/ext/openssl/sample/cipher.rb
new file mode 100644
index 0000000000..844b6eea4e
--- /dev/null
+++ b/ext/openssl/sample/cipher.rb
@@ -0,0 +1,29 @@
+#!/usr/bin/env ruby
+require 'openssl'
+
+text = "abcdefghijklmnopqrstuvwxyz"
+key = "key"
+alg = "DES-EDE3-CBC"
+#alg = "AES-128-CBC"
+
+puts "--Setup--"
+puts %(clear text: "#{text}")
+puts %(symmetric key: "#{key}")
+puts %(cipher alg: "#{alg}")
+puts
+
+puts "--Encrypting--"
+des = OpenSSL::Cipher::Cipher.new(alg)
+des.encrypt(key) #, "iv12345678")
+cipher = des.update(text)
+cipher << des.final
+puts %(encrypted text: #{cipher.inspect})
+puts
+
+puts "--Decrypting--"
+des = OpenSSL::Cipher::Cipher.new(alg)
+des.decrypt(key) #, "iv12345678")
+out = des.update(cipher)
+out << des.final
+puts %(decrypted text: "#{out}")
+puts
diff --git a/ext/openssl/sample/crlstore.rb b/ext/openssl/sample/crlstore.rb
new file mode 100644
index 0000000000..b305913eb0
--- /dev/null
+++ b/ext/openssl/sample/crlstore.rb
@@ -0,0 +1,122 @@
+begin
+ require 'http-access2'
+rescue LoadError
+ STDERR.puts("Cannot load http-access2. CRL might not be fetched.")
+end
+require 'c_rehash'
+
+
+class CrlStore
+ def initialize(c_store)
+ @c_store = c_store
+ @c_store.hash_dir(true)
+ end
+
+ def find_crl(cert)
+ do_find_crl(cert)
+ end
+
+private
+
+ def do_find_crl(cert)
+ unless ca = find_ca(cert)
+ return nil
+ end
+ unless crlfiles = @c_store.get_crls(ca.subject)
+ if crl = renew_crl(cert, ca)
+ @c_store.add_crl(crl)
+ return crl
+ end
+ return nil
+ end
+ crlfiles.each do |crlfile|
+ next unless crl = load_crl(crlfile)
+ if crl.next_update < Time.now
+ if new_crl = renew_crl(cert, ca)
+ @c_store.delete_crl(crl)
+ @c_store.add_crl(new_crl)
+ crl = new_crl
+ end
+ end
+ if check_valid(crl, ca)
+ return crl
+ end
+ end
+ nil
+ end
+
+ def find_ca(cert)
+ @c_store.get_certs(cert.issuer).each do |cafile|
+ ca = load_cert(cafile)
+ if cert.verify(ca.public_key)
+ return ca
+ end
+ end
+ nil
+ end
+
+ def fetch(location)
+ if /\AURI:(.*)\z/ =~ location
+ begin
+ c = HTTPAccess2::Client.new(ENV['http_proxy'] || ENV['HTTP_PROXY'])
+ c.get_content($1)
+ rescue NameError, StandardError
+ nil
+ end
+ else
+ nil
+ end
+ end
+
+ def load_cert(certfile)
+ load_cert_str(File.read(certfile))
+ end
+
+ def load_crl(crlfile)
+ load_crl_str(File.read(crlfile))
+ end
+
+ def load_cert_str(cert_str)
+ OpenSSL::X509::Certificate.new(cert_str)
+ end
+
+ def load_crl_str(crl_str)
+ OpenSSL::X509::CRL.new(crl_str)
+ end
+
+ def check_valid(crl, ca)
+ unless crl.verify(ca.public_key)
+ return false
+ end
+ crl.last_update <= Time.now
+ end
+
+ RE_CDP = /\AcrlDistributionPoints\z/
+ def get_cdp(cert)
+ if cdp_ext = cert.extensions.find { |ext| RE_CDP =~ ext.oid }
+ cdp_ext.value.chomp
+ else
+ false
+ end
+ end
+
+ def renew_crl(cert, ca)
+ if cdp = get_cdp(cert)
+ if new_crl_str = fetch(cdp)
+ new_crl = load_crl_str(new_crl_str)
+ if check_valid(new_crl, ca)
+ return new_crl
+ end
+ end
+ end
+ false
+ end
+end
+
+if $0 == __FILE__
+ dir = "trust_certs"
+ c_store = CHashDir.new(dir)
+ s = CrlStore.new(c_store)
+ c = OpenSSL::X509::Certificate.new(File.read("cert_store/google_codesign.pem"))
+ p s.find_crl(c)
+end
diff --git a/ext/openssl/sample/echo_cli.rb b/ext/openssl/sample/echo_cli.rb
new file mode 100644
index 0000000000..87dacaf545
--- /dev/null
+++ b/ext/openssl/sample/echo_cli.rb
@@ -0,0 +1,36 @@
+#!/usr/bin/env ruby
+
+require 'socket'
+require 'openssl'
+require 'getopts'
+
+getopts nil, "p:2000", "c:", "k:", "C:"
+
+host = ARGV[0] || "localhost"
+port = $OPT_p
+cert_file = $OPT_c
+key_file = $OPT_k
+ca_path = $OPT_C
+
+ctx = OpenSSL::SSL::SSLContext.new()
+if cert_file && key_file
+ ctx.cert = OpenSSL::X509::Certificate.new(File::read(cert_file))
+ ctx.key = OpenSSL::PKey::RSA.new(File::read(key_file))
+end
+if ca_path
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ ctx.ca_path = ca_path
+else
+ $stderr.puts "!!! WARNING: PEER CERTIFICATE WON'T BE VERIFIED !!!"
+end
+
+s = TCPSocket.new(host, port)
+ssl = OpenSSL::SSL::SSLSocket.new(s, ctx)
+ssl.connect
+while line = $stdin.gets
+ ssl.write line
+ print ssl.gets
+end
+
+ssl.close
+s.close
diff --git a/ext/openssl/sample/echo_svr.rb b/ext/openssl/sample/echo_svr.rb
new file mode 100644
index 0000000000..e35ad12a19
--- /dev/null
+++ b/ext/openssl/sample/echo_svr.rb
@@ -0,0 +1,64 @@
+#!/usr/bin/env ruby
+
+require 'socket'
+require 'openssl'
+require 'getopts'
+
+getopts nil, "p:2000", "c:", "k:", "C:"
+
+port = $OPT_p
+cert_file = $OPT_c
+key_file = $OPT_k
+ca_path = $OPT_C
+
+if cert_file && key_file
+ cert = OpenSSL::X509::Certificate.new(File::read(cert_file))
+ key = OpenSSL::PKey::RSA.new(File::read(key_file))
+else
+ key = OpenSSL::PKey::RSA.new(512){ print "." }
+ puts
+ cert = OpenSSL::X509::Certificate.new
+ cert.version = 2
+ cert.serial = 0
+ name = OpenSSL::X509::Name.new([["C","JP"],["O","TEST"],["CN","localhost"]])
+ cert.subject = name
+ cert.issuer = name
+ cert.not_before = Time.now
+ cert.not_after = Time.now + 3600
+ cert.public_key = key.public_key
+ ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
+ cert.extensions = [
+ ef.create_extension("basicConstraints","CA:FALSE"),
+ ef.create_extension("subjectKeyIdentifier","hash"),
+ ef.create_extension("extendedKeyUsage","serverAuth"),
+ ef.create_extension("keyUsage",
+ "keyEncipherment,dataEncipherment,digitalSignature")
+ ]
+ ef.issuer_certificate = cert
+ cert.add_extension ef.create_extension("authorityKeyIdentifier",
+ "keyid:always,issuer:always")
+ cert.sign(key, OpenSSL::Digest::SHA1.new)
+end
+
+ctx = OpenSSL::SSL::SSLContext.new()
+ctx.key = key
+ctx.cert = cert
+if ca_path
+ ctx.verify_mode =
+ OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
+ ctx.ca_path = ca_path
+else
+ $stderr.puts "!!! WARNING: PEER CERTIFICATE WON'T BE VERIFIED !!!"
+end
+
+svr = TCPServer.new(port)
+loop do
+ ns = svr.accept
+ ssl = OpenSSL::SSL::SSLSocket.new(ns, ctx)
+ ssl.accept
+ while line = ssl.gets
+ ssl.write line
+ end
+ ssl.close
+ ns.close
+end
diff --git a/ext/openssl/sample/gen_csr.rb b/ext/openssl/sample/gen_csr.rb
new file mode 100644
index 0000000000..c22073b9b9
--- /dev/null
+++ b/ext/openssl/sample/gen_csr.rb
@@ -0,0 +1,52 @@
+#!/usr/bin/env ruby
+
+require 'getopts'
+require 'openssl'
+
+include OpenSSL
+
+def usage
+ myname = File::basename($0)
+ $stderr.puts <<EOS
+Usage: #{myname} name [keypair_file]
+ name ... ex. /C=JP/O=RRR/OU=CA/CN=NaHi/emailAddress=nahi@example.org
+EOS
+ exit
+end
+
+getopts nil, "key:", "csrout:", "keyout:"
+keypair_file = $OPT_key
+csrout = $OPT_csrout || "csr.pem"
+keyout = $OPT_keyout || "keypair.pem"
+
+name_str = ARGV.shift or usage()
+
+$stdout.sync = true
+
+name_ary = name_str.scan(/\s*([^\/,]+)\s*/).collect { |i| i[0].split("=") }
+p name_ary
+name = X509::Name.new(name_ary)
+
+keypair = nil
+if keypair_file
+ keypair = PKey::RSA.new(File.read(keypair_file))
+else
+ keypair = PKey::RSA.new(1024) { putc "." }
+ puts
+ puts "Writing #{keyout}..."
+ File.open(keyout, "w", 0400) do |f|
+ f << keypair.to_pem
+ end
+end
+
+puts "Generating CSR for #{name_ary.inspect}"
+
+req = X509::Request.new
+req.subject = name
+req.public_key = keypair.public_key
+req.sign(keypair, Digest::SHA1.new)
+
+puts "Writing #{csrout}..."
+File.open(csrout, "w") do |f|
+ f << req.to_pem
+end
diff --git a/ext/openssl/sample/smime_read.rb b/ext/openssl/sample/smime_read.rb
new file mode 100644
index 0000000000..0f08f54f7e
--- /dev/null
+++ b/ext/openssl/sample/smime_read.rb
@@ -0,0 +1,23 @@
+require 'getopts'
+require 'openssl'
+include OpenSSL
+
+getopts nil, "c:", "k:", "C:"
+
+cert_file = $OPT_c
+key_file = $OPT_k
+ca_path = $OPT_C
+
+data = $stdin.read
+
+cert = X509::Certificate.new(File::read(cert_file))
+key = PKey::RSA.new(File::read(key_file))
+p7enc = PKCS7::read_smime(data)
+data = p7enc.decrypt(key, cert)
+
+store = X509::Store.new
+store.add_path(ca_path)
+p7sig = PKCS7::read_smime(data)
+if p7sig.verify([], store)
+ puts p7sig.data
+end
diff --git a/ext/openssl/sample/smime_write.rb b/ext/openssl/sample/smime_write.rb
new file mode 100644
index 0000000000..ce32cd8146
--- /dev/null
+++ b/ext/openssl/sample/smime_write.rb
@@ -0,0 +1,23 @@
+require 'openssl'
+require 'getopts'
+include OpenSSL
+
+getopts nil, "c:", "k:", "r:"
+
+cert_file = $OPT_c
+key_file = $OPT_k
+rcpt_file = $OPT_r
+
+cert = X509::Certificate.new(File::read(cert_file))
+key = PKey::RSA.new(File::read(key_file))
+
+data = "Content-Type: text/plain\r\n"
+data << "\r\n"
+data << "This is a clear-signed message.\r\n"
+
+p7sig = PKCS7::sign(cert, key, data, [], PKCS7::DETACHED)
+smime0 = PKCS7::write_smime(p7sig)
+
+rcpt = X509::Certificate.new(File::read(rcpt_file))
+p7enc = PKCS7::encrypt([rcpt], smime0)
+print PKCS7::write_smime(p7enc)
diff --git a/ext/openssl/sample/wget.rb b/ext/openssl/sample/wget.rb
new file mode 100644
index 0000000000..0362ab980d
--- /dev/null
+++ b/ext/openssl/sample/wget.rb
@@ -0,0 +1,33 @@
+#!/usr/bin/env ruby
+
+require 'net/https'
+require 'getopts'
+
+getopts nil, 'C:'
+
+ca_path = $OPT_C
+
+uri = URI.parse(ARGV[0])
+if proxy = ENV['HTTP_PROXY']
+ prx_uri = URI.parse(proxy)
+ prx_host = prx_uri.host
+ prx_port = prx_uri.port
+end
+
+h = Net::HTTP.new(uri.host, uri.port, prx_host, prx_port)
+h.set_debug_output($stderr) if $DEBUG
+if uri.scheme == "https"
+ h.use_ssl = true
+ if ca_path
+ h.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ h.ca_path = ca_path
+ else
+ $stderr.puts "!!! WARNING: PEER CERTIFICATE WON'T BE VERIFIED !!!"
+ end
+end
+
+path = uri.path.empty? ? "/" : uri.path
+h.get2(path){|resp|
+ STDERR.puts h.peer_cert.inspect if h.peer_cert
+ print resp.body
+}
diff --git a/ext/pty/README b/ext/pty/README
index a09469d39c..42c7d4f891 100644
--- a/ext/pty/README
+++ b/ext/pty/README
@@ -33,43 +33,15 @@ following module fungtions:
the array is passed to the block as block parameters, and the
function itself returns nil.
- While the process spawned by this function is active, SIGCHLD
- is captured to handle the change of the child process. When the
- child process is suspended or finished, an exception is raised.
- As all SIGCHLD signal is captured and processed by PTY module,
- you can't use other function or method which spawns subprosesses
- (including signal() and IO.popen()) while the PTY subprocesses
- are active. Otherwise, unexpected exception will occur. To avoid
- this problem, see protect_signal() below.
-
- If this function is called with an iterator block, SIGCHLD signal
- is captured only within the block. Therefore, it is risky to use
- File objects for PTY subprocess outside the iterator block.
-
+ When the child process is suspended or finished, an exception is
+ raised. If this function is called with an iterator block,
+ exception is raised only within the block. Child process
+ monitor is terminated on block exit.
protect_signal
+ reset_signal
- This function takes an iterator block. Within the iterator block,
- no exception is raised even if any subprocess is terminated.
- This function is used to enable functions like system() or IO.popen()
- while PTY subprocess is active. For example,
-
- PTY.spawn("command_foo") do |r,w|
- ...
- ...
- PTY.protect_signal do
- system "some other commands"
- end
- ...
- end
-
- disables to send exception when "some other commands" is
- terminated.
-
- reset_signal
-
- Disables to handle SIGCHLD while PTY subprocess is active.
-
+ These functions are obsolete in this version of pty.
4. License
diff --git a/ext/pty/pty.c b/ext/pty/pty.c
index b7c69fe400..cea0de50a2 100644
--- a/ext/pty/pty.c
+++ b/ext/pty/pty.c
@@ -133,44 +133,50 @@ struct pty_info {
VALUE thread;
};
+static void
+raise_from_wait(state, info)
+ struct pty_info *info;
+ char *state;
+{
+ extern VALUE rb_last_status;
+ char buf[1024];
+ VALUE exc;
+
+ snprintf(buf, sizeof(buf), "pty - %s: %d", state, info->child_pid);
+ exc = rb_exc_new2(eChildExited, buf);
+ rb_iv_set(exc, "status", rb_last_status);
+ rb_funcall(info->thread, rb_intern("raise"), 1, exc);
+}
+
static VALUE
pty_syswait(info)
struct pty_info *info;
{
- extern VALUE rb_last_status;
int cpid, status;
- char buf[1024];
- VALUE exc, st;
- char *state = "changed";
- cpid = rb_waitpid(info->child_pid, &status, WUNTRACED);
- st = rb_last_status;
-
- if (cpid == 0 || cpid == -1)
- return Qnil;
+ for (;;) {
+ cpid = rb_waitpid(info->child_pid, &status, WUNTRACED);
+ if (cpid == -1) return Qnil;
-#ifdef IF_STOPPED
- if (IF_STOPPED(status)) { /* suspend */
- state = "stopped";
- }
-#else
-#ifdef WIFSTOPPED
- if (WIFSTOPPED(status)) { /* suspend */
- state = "stopped";
- }
+#if defined(IF_STOPPED)
+ if (IF_STOPPED(status)) { /* suspend */
+ raise_from_wait("stopped", info);
+ }
+#elif defined(WIFSTOPPED)
+ if (WIFSTOPPED(status)) { /* suspend */
+ raise_from_wait("stopped", info);
+ }
#else
---->> Either IF_STOPPED or WIFSTOPPED is needed <<----
-#endif /* WIFSTOPPED */
-#endif /* IF_STOPPED */
- if (WIFEXITED(status)) {
- state = "exit";
+#endif /* WIFSTOPPED | IF_STOPPED */
+ else if (kill(info->child_pid, 0) == 0) {
+ raise_from_wait("changed", info);
+ }
+ else {
+ raise_from_wait("exited", info);
+ return Qnil;
+ }
}
-
- snprintf(buf, sizeof(buf), "pty - %s: %d", state, cpid);
- exc = rb_exc_new2(eChildExited, buf);
- rb_iv_set(exc, "status", st);
- rb_funcall(info->thread, rb_intern("raise"), 1, exc);
- return Qnil;
}
static void getDevice _((int*, int*));
@@ -290,27 +296,15 @@ establishShell(argc, argv, info)
}
static VALUE
-pty_kill_child(info)
+pty_finalize_syswait(info)
struct pty_info *info;
{
- if (rb_funcall(info->thread, rb_intern("alive?"), 0, 0) == Qtrue &&
- kill(info->child_pid, 0) == 0) {
- rb_thread_schedule();
- if (kill(info->child_pid, SIGTERM) == 0) {
- rb_thread_schedule();
- if (kill(info->child_pid, 0) == 0) {
- kill(info->child_pid, SIGINT);
- rb_thread_schedule();
- if (kill(info->child_pid, 0) == 0)
- kill(info->child_pid, SIGKILL);
- }
- }
- }
- rb_funcall(info->thread, rb_intern("join"), 0, 0);
+ rb_thread_kill(info->thread);
+ rb_funcall(info->thread, rb_intern("value"), 0);
+ rb_detach_process(info->child_pid);
return Qnil;
}
-
#ifdef HAVE_OPENPTY
/*
* Use openpty(3) of 4.3BSD Reno and later,
@@ -418,8 +412,9 @@ pty_getpty(argc, argv, self)
VALUE *argv;
VALUE self;
{
- VALUE res, th;
- struct pty_info info, thinfo;
+ VALUE res;
+ struct pty_info info;
+ struct pty_info thinfo;
OpenFile *wfptr,*rfptr;
VALUE rport = rb_obj_alloc(rb_cFile);
VALUE wport = rb_obj_alloc(rb_cFile);
@@ -442,12 +437,12 @@ pty_getpty(argc, argv, self)
rb_ary_store(res,1,(VALUE)wport);
rb_ary_store(res,2,INT2FIX(info.child_pid));
- th = rb_thread_create(pty_syswait, (void*)&info);
- thinfo.thread = th;
+ thinfo.thread = rb_thread_create(pty_syswait, (void*)&info);
thinfo.child_pid = info.child_pid;
+ rb_thread_schedule();
if (rb_block_given_p()) {
- rb_ensure(rb_yield, res, pty_kill_child, (VALUE)&thinfo);
+ rb_ensure(rb_yield, res, pty_finalize_syswait, (VALUE)&thinfo);
return Qnil;
}
return res;
diff --git a/ext/pty/script.rb b/ext/pty/script.rb
index 3084935637..dbb933171f 100644
--- a/ext/pty/script.rb
+++ b/ext/pty/script.rb
@@ -10,8 +10,7 @@ logfile = File.open(ofile,"a")
system "stty -echo raw lnext ^_"
-PTY.spawn("/bin/csh") do
- |r_pty,w_pty,pid|
+PTY.spawn("/bin/csh") do |r_pty,w_pty,pid|
Thread.new do
while true
diff --git a/ext/racc/cparse/cparse.c b/ext/racc/cparse/cparse.c
index 6ed6293dee..3d75cc9ca7 100644
--- a/ext/racc/cparse/cparse.c
+++ b/ext/racc/cparse/cparse.c
@@ -11,8 +11,8 @@
*/
-#include <stdio.h>
#include "ruby.h"
+#include <stdio.h>
/* -----------------------------------------------------------------------
diff --git a/ext/readline/readline.c b/ext/readline/readline.c
index 2f460ea3e0..cac0b1d11c 100644
--- a/ext/readline/readline.c
+++ b/ext/readline/readline.c
@@ -9,6 +9,10 @@
#include "ruby.h"
#include "rubysig.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
static VALUE mReadline;
#define TOLOWER(c) (isupper(c) ? tolower(c) : c)
@@ -50,10 +54,10 @@ readline_readline(argc, argv, self)
buff = (char*)rb_protect((VALUE(*)_((VALUE)))readline, (VALUE)prompt,
&status);
if (status) {
-#if READLINE_40_OR_LATER
+#if defined READLINE_40_OR_LATER
/* restore terminal mode and signal handler*/
rl_cleanup_after_signal();
-#elif READLINE_21_OR_LATER
+#elif defined READLINE_21_OR_LATER
/* restore terminal mode */
(*rl_deprep_term_function)();
#else
@@ -228,7 +232,6 @@ readline_s_set_basic_word_break_characters(self, str)
{
#ifdef READLINE_21_OR_LATER
static char *basic_word_break_characters = NULL;
- char *s;
StringValue(str);
if (basic_word_break_characters == NULL) {
@@ -268,7 +271,6 @@ readline_s_set_completer_word_break_characters(self, str)
{
#ifdef READLINE_21_OR_LATER
static char *completer_word_break_characters = NULL;
- char *s;
StringValue(str);
if (completer_word_break_characters == NULL) {
@@ -308,7 +310,6 @@ readline_s_set_basic_quote_characters(self, str)
{
#ifdef READLINE_21_OR_LATER
static char *basic_quote_characters = NULL;
- char *s;
StringValue(str);
if (basic_quote_characters == NULL) {
@@ -348,7 +349,6 @@ readline_s_set_completer_quote_characters(self, str)
{
#ifdef READLINE_21_OR_LATER
static char *completer_quote_characters = NULL;
- char *s;
StringValue(str);
if (completer_quote_characters == NULL) {
@@ -388,7 +388,6 @@ readline_s_set_filename_quote_characters(self, str)
{
#ifdef READLINE_21_OR_LATER
static char *filename_quote_characters = NULL;
- char *s;
StringValue(str);
if (filename_quote_characters == NULL) {
@@ -724,15 +723,21 @@ Init_readline()
rb_define_singleton_method(ucomp, "call",
username_completion_proc_call, 1);
rb_define_const(mReadline, "USERNAME_COMPLETION_PROC", ucomp);
-#if READLINE_21_OR_LATER
+#if defined READLINE_21_OR_LATER
rb_define_const(mReadline, "VERSION", rb_str_new2(rl_library_version));
#else
rb_define_const(mReadline, "VERSION",
rb_str_new2("2.0 or before version"));
#endif
+#if defined READLINE_42_OR_LATER
+ rl_attempted_completion_function
+ = (rl_completion_func_t *)readline_attempted_completion_function;
+ rl_event_hook = (rl_hook_func_t *)readline_event;
+#else
rl_attempted_completion_function
= (CPPFunction *) readline_attempted_completion_function;
rl_event_hook = readline_event;
+#endif
rl_clear_signals();
}
diff --git a/ext/sdbm/init.c b/ext/sdbm/init.c
index 99945d3a7d..f473555840 100644
--- a/ext/sdbm/init.c
+++ b/ext/sdbm/init.c
@@ -56,15 +56,12 @@ fsdbm_close(obj)
return Qnil;
}
+static VALUE fsdbm_alloc _((VALUE));
static VALUE
-fsdbm_s_new(argc, argv, klass)
- int argc;
- VALUE *argv;
+fsdbm_alloc(klass)
VALUE klass;
{
- VALUE obj = Data_Wrap_Struct(klass, 0, free_sdbm, 0);
- rb_obj_call_init(obj, argc, argv);
- return obj;
+ return Data_Wrap_Struct(klass, 0, free_sdbm, 0);
}
static VALUE
@@ -228,7 +225,6 @@ fsdbm_select(argc, argv, obj)
datum key, val;
DBM *dbm;
struct dbmdata *dbmp;
- VALUE keystr, valstr;
if (argc > 0) {
rb_raise(rb_eArgError, "wrong number arguments(%d for 0)", argc);
@@ -246,6 +242,8 @@ fsdbm_select(argc, argv, obj)
}
}
else {
+ rb_warn("SDBM#select(index..) is deprecated; use SDBM#values_at");
+
for (i=0; i<argc; i++) {
rb_ary_push(new, fsdbm_fetch(obj, argv[i], Qnil));
}
@@ -255,6 +253,22 @@ fsdbm_select(argc, argv, obj)
}
static VALUE
+fsdbm_values_at(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE new = rb_ary_new2(argc);
+ int i;
+
+ for (i=0; i<argc; i++) {
+ rb_ary_push(new, fsdbm_fetch(obj, argv[i], Qnil));
+ }
+
+ return new;
+}
+
+static VALUE
fsdbm_delete(obj, keystr)
VALUE obj, keystr;
{
@@ -719,7 +733,7 @@ Init_sdbm()
rb_eDBMError = rb_define_class("SDBMError", rb_eStandardError);
rb_include_module(rb_cDBM, rb_mEnumerable);
- rb_define_singleton_method(rb_cDBM, "new", fsdbm_s_new, -1);
+ rb_define_alloc_func(rb_cDBM, fsdbm_alloc);
rb_define_singleton_method(rb_cDBM, "open", fsdbm_s_open, -1);
rb_define_method(rb_cDBM, "initialize", fsdbm_initialize, -1);
@@ -732,6 +746,7 @@ Init_sdbm()
rb_define_method(rb_cDBM, "indexes", fsdbm_indexes, -1);
rb_define_method(rb_cDBM, "indices", fsdbm_indexes, -1);
rb_define_method(rb_cDBM, "select", fsdbm_select, -1);
+ rb_define_method(rb_cDBM, "values_at", fsdbm_values_at, -1);
rb_define_method(rb_cDBM, "length", fsdbm_length, 0);
rb_define_method(rb_cDBM, "size", fsdbm_length, 0);
rb_define_method(rb_cDBM, "empty?", fsdbm_empty_p, 0);
diff --git a/ext/sdbm/testsdbm.rb b/ext/sdbm/testsdbm.rb
index 550b47a008..3577d3606c 100644
--- a/ext/sdbm/testsdbm.rb
+++ b/ext/sdbm/testsdbm.rb
@@ -51,7 +51,7 @@ class TestSDBM < RUNIT::TestCase
end
def test_version
- STDERR.print SDBM::VERSION
+ assert(! SDBM.const_defined?(:VERSION))
end
def test_s_new_has_no_block
@@ -219,11 +219,11 @@ class TestSDBM < RUNIT::TestCase
assert_equals(values.reverse, @sdbm.indexes(*keys.reverse))
end
- def test_select
+ def test_values_at
keys = %w(foo bar baz)
values = %w(FOO BAR BAZ)
@sdbm[keys[0]], @sdbm[keys[1]], @sdbm[keys[2]] = values
- assert_equals(values.reverse, @sdbm.select(*keys.reverse))
+ assert_equals(values.reverse, @sdbm.values_at(*keys.reverse))
end
def test_select_with_block
diff --git a/ext/socket/addrinfo.h b/ext/socket/addrinfo.h
index 5beea2cec6..a22615ee00 100644
--- a/ext/socket/addrinfo.h
+++ b/ext/socket/addrinfo.h
@@ -115,8 +115,10 @@
/*
* Constants for getnameinfo()
*/
+#ifndef NI_MAXHOST
#define NI_MAXHOST 1025
#define NI_MAXSERV 32
+#endif
/*
* Flag values for getnameinfo()
diff --git a/ext/socket/extconf.rb b/ext/socket/extconf.rb
index 6b03307f97..45e596700c 100644
--- a/ext/socket/extconf.rb
+++ b/ext/socket/extconf.rb
@@ -1,7 +1,5 @@
require 'mkmf'
-$CPPFLAGS += " -Dss_family=__ss_family -Dss_len=__ss_len"
-
case RUBY_PLATFORM
when /bccwin32/
test_func = "WSACleanup"
@@ -150,6 +148,29 @@ main()
}
EOF
$CFLAGS="-DHAVE_SOCKADDR_STORAGE "+$CFLAGS
+else # doug's fix, NOW add -Dss_family... only if required!
+$CPPFLAGS += " -Dss_family=__ss_family -Dss_len=__ss_len"
+ if try_link(<<EOF)
+#ifdef _WIN32
+# include <windows.h>
+# include <winsock.h>
+#else
+# include <sys/types.h>
+# include <netdb.h>
+# include <string.h>
+# include <sys/socket.h>
+#endif
+int
+main()
+{
+ struct sockaddr_storage ss;
+
+ ss.ss_family;
+ return 0;
+}
+EOF
+ $CFLAGS="-DHAVE_SOCKADDR_STORAGE "+$CFLAGS
+end
end
if try_link(<<EOF)
@@ -173,8 +194,10 @@ end
have_header("netinet/tcp.h") if not /cygwin/ =~ RUBY_PLATFORM # for cygwin 1.1.5
have_header("netinet/udp.h")
-have_struct_member('struct msghdr', 'msg_control', header=['sys/types.h', 'sys/socket.h'])
-have_struct_member('struct msghdr', 'msg_accrights', header=['sys/types.h', 'sys/socket.h'])
+if have_func("sendmsg") or have_func("recvmsg")
+ have_struct_member('struct msghdr', 'msg_control', header=['sys/types.h', 'sys/socket.h'])
+ have_struct_member('struct msghdr', 'msg_accrights', header=['sys/types.h', 'sys/socket.h'])
+end
$getaddr_info_ok = false
if !enable_config("wide-getaddrinfo", false) and try_run(<<EOF)
@@ -197,6 +220,7 @@ main()
for (passive = 0; passive <= 1; passive++) {
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
+ hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = passive ? AI_PASSIVE : 0;
hints.ai_socktype = SOCK_STREAM;
if ((gaierr = getaddrinfo(NULL, "54321", &hints, &aitop)) != 0) {
@@ -337,7 +361,7 @@ if have_func(test_func)
unless have_func("gethostname")
have_func("uname")
end
- if ENV["SOCKS_SERVER"] or enable_config("socks", false)
+ if enable_config("socks", ENV["SOCKS_SERVER"])
if have_library("socks5", "SOCKSinit")
$CFLAGS+=" -DSOCKS5 -DSOCKS"
elsif have_library("socks", "Rconnect")
diff --git a/ext/socket/socket.c b/ext/socket/socket.c
index 8378368313..13329eedcb 100644
--- a/ext/socket/socket.c
+++ b/ext/socket/socket.c
@@ -324,7 +324,7 @@ bsock_getsockopt(sock, lev, optname)
static VALUE
bsock_getsockname(sock)
- VALUE sock;
+ VALUE sock;
{
char buf[1024];
socklen_t len = sizeof buf;
@@ -338,7 +338,7 @@ bsock_getsockname(sock)
static VALUE
bsock_getpeername(sock)
- VALUE sock;
+ VALUE sock;
{
char buf[1024];
socklen_t len = sizeof buf;
@@ -484,6 +484,7 @@ bsock_do_not_rev_lookup()
static VALUE
bsock_do_not_rev_lookup_set(self, val)
+ VALUE self, val;
{
rb_secure(4);
do_not_reverse_lookup = RTEST(val);
@@ -553,7 +554,7 @@ sock_addrinfo(host, port, socktype, flags)
struct addrinfo hints, *hintsp, *res;
char *hostp, *portp;
int error;
- char hbuf[1024], pbuf[16];
+ char hbuf[1024], pbuf[32];
if (NIL_P(host)) {
hostp = NULL;
@@ -569,7 +570,7 @@ sock_addrinfo(host, port, socktype, flags)
SafeStringValue(host);
name = RSTRING(host)->ptr;
- if (*name == 0 || (name[0] == '<' && strcmp(name, "<any>") == 0)) {
+ if (!name || *name == 0 || (name[0] == '<' && strcmp(name, "<any>") == 0)) {
mkinetaddr(INADDR_ANY, hbuf, sizeof(hbuf));
}
else if (name[0] == '<' && strcmp(name, "<broadcast>") == 0) {
@@ -724,6 +725,13 @@ thread_write_select(fd)
rb_thread_select(fd+1, 0, &fds, 0, 0);
}
+#ifdef __CYGWIN__
+#define WAIT_IN_PROGRESS 10
+#endif
+#ifdef __APPLE__
+#define WAIT_IN_PROGRESS 10
+#endif
+
static int
ruby_connect(fd, sockaddr, len, socks)
int fd;
@@ -733,7 +741,7 @@ ruby_connect(fd, sockaddr, len, socks)
{
int status;
int mode;
-#if defined __CYGWIN__
+#ifdef WAIT_IN_PROGRESS
int wait_in_progress = -1;
#endif
@@ -770,20 +778,34 @@ ruby_connect(fd, sockaddr, len, socks)
case EAGAIN:
#ifdef EINPROGRESS
case EINPROGRESS:
-#ifdef __CYGWIN__
+#endif
+#ifdef EALREADY
case EALREADY:
- wait_in_progress = 10;
#endif
+#ifdef WAIT_IN_PROGRESS
+ wait_in_progress = WAIT_IN_PROGRESS;
#endif
thread_write_select(fd);
continue;
-#if defined __CYGWIN__
+#ifdef WAIT_IN_PROGRESS
case EINVAL:
if (wait_in_progress-- > 0) {
- struct timeval tv = {0, 100000};
- rb_thread_wait_for(tv);
- continue;
+ int sockerr, sockerrlen = sizeof(sockerr);
+
+ /*
+ * connect() after EINPROGRESS returns EINVAL on
+ * some platforms, need to check true error
+ * status.
+ */
+ status = getsockopt(fd, SOL_SOCKET, SO_ERROR, &sockerr, &sockerrlen);
+ if (!status && !sockerr) {
+ struct timeval tv = {0, 100000};
+ rb_thread_wait_for(tv);
+ continue;
+ }
+ status = -1;
+ errno = sockerr;
}
break;
#endif
@@ -879,7 +901,7 @@ init_inetsock_internal(arg)
struct inetsock_arg *arg;
{
int type = arg->type;
- struct addrinfo hints, *res;
+ struct addrinfo *res;
int fd, status;
char *syscall;
@@ -972,12 +994,11 @@ tcp_init(argc, argv, sock)
VALUE remote_host, remote_serv;
VALUE local_host, local_serv;
- int pcount = rb_scan_args(argc, argv, "22",
- &remote_host, &remote_serv,
- &local_host, &local_serv);
+ rb_scan_args(argc, argv, "22", &remote_host, &remote_serv,
+ &local_host, &local_serv);
return init_inetsock(sock, remote_host, remote_serv,
- local_host, local_serv, INET_CLIENT);
+ local_host, local_serv, INET_CLIENT);
}
#ifdef SOCKS
@@ -992,7 +1013,7 @@ socks_init(sock, host, serv)
init = 1;
}
- return init_inetsock(class, host, serv, Qnil, Qnil, INET_SOCKS);
+ return init_inetsock(sock, host, serv, Qnil, Qnil, INET_SOCKS);
}
#ifdef SOCKS5
@@ -1071,7 +1092,6 @@ tcp_s_gethostbyname(obj, host)
struct hostent *h = sock_hostbyname(host);
VALUE ary, names;
char **pch;
- size_t size;
ary = rb_ary_new();
rb_ary_push(ary, rb_str_new2(h->h_name));
@@ -1103,7 +1123,7 @@ tcp_s_gethostbyname(obj, host)
#ifdef SIN6_LEN
sin6.sin6_len = sizeof(sin6);
#endif
- memcpy((char*)&sin6.sin6_addr, *pch, size);
+ memcpy((char*)&sin6.sin6_addr, *pch, h->h_length);
rb_ary_push(ary, mkipaddr((struct sockaddr*)&sin6));
break;
}
@@ -1129,9 +1149,9 @@ tcp_svr_init(argc, argv, sock)
VALUE arg1, arg2;
if (rb_scan_args(argc, argv, "11", &arg1, &arg2) == 2)
- return init_inetsock(sock, arg1, arg2, NULL, Qnil, INET_SERVER);
+ return init_inetsock(sock, arg1, arg2, Qnil, Qnil, INET_SERVER);
else
- return init_inetsock(sock, Qnil, arg1, NULL, Qnil, INET_SERVER);
+ return init_inetsock(sock, Qnil, arg1, Qnil, Qnil, INET_SERVER);
}
static VALUE
@@ -1362,7 +1382,6 @@ udp_connect(sock, host, port)
VALUE sock, host, port;
{
OpenFile *fptr;
- int fd;
struct udp_arg arg;
VALUE ret;
@@ -1489,7 +1508,7 @@ static VALUE
unix_send_io(sock, val)
VALUE sock, val;
{
-#if defined(HAVE_ST_MSG_CONTROL) || defined(HAVE_ST_MSG_ACCRIGHTS)
+#if defined(HAVE_SENDMSG) && (defined(HAVE_ST_MSG_CONTROL) || defined(HAVE_ST_MSG_ACCRIGHTS))
int fd;
OpenFile *fptr;
struct msghdr msg;
@@ -1546,6 +1565,7 @@ unix_send_io(sock, val)
return Qnil;
#else
rb_notimplement();
+ return Qnil; /* not reached */
#endif
}
@@ -1555,7 +1575,7 @@ unix_recv_io(argc, argv, sock)
VALUE *argv;
VALUE sock;
{
-#if defined(HAVE_ST_MSG_CONTROL) || defined(HAVE_ST_MSG_ACCRIGHTS)
+#if defined(HAVE_RECVMSG) && (defined(HAVE_ST_MSG_CONTROL) || defined(HAVE_ST_MSG_ACCRIGHTS))
VALUE klass, mode;
OpenFile *fptr;
struct msghdr msg;
@@ -1637,6 +1657,7 @@ unix_recv_io(argc, argv, sock)
}
#else
rb_notimplement();
+ return Qnil; /* not reached */
#endif
}
@@ -1798,7 +1819,7 @@ setup_domain_and_type(domain, dv, type, tv)
}
static VALUE
-sock_init(sock, domain, type, protocol)
+sock_initialize(sock, domain, type, protocol)
VALUE sock, domain, type, protocol;
{
int fd;
@@ -1893,7 +1914,7 @@ sock_bind(sock, addr)
static VALUE
sock_listen(sock, log)
- VALUE sock, log;
+ VALUE sock, log;
{
OpenFile *fptr;
@@ -1916,7 +1937,7 @@ sock_recvfrom(argc, argv, sock)
static VALUE
sock_accept(sock)
- VALUE sock;
+ VALUE sock;
{
OpenFile *fptr;
VALUE sock2;
@@ -1931,7 +1952,7 @@ sock_accept(sock)
static VALUE
sock_sysaccept(sock)
- VALUE sock;
+ VALUE sock;
{
OpenFile *fptr;
VALUE sock2;
@@ -2185,7 +2206,7 @@ sock_s_getnameinfo(argc, argv)
int error;
struct sockaddr_storage ss;
struct sockaddr *sap;
- char *ep, *ap;
+ char *ap;
sa = flags = Qnil;
rb_scan_args(argc, argv, "11", &sa, &flags);
@@ -2330,11 +2351,7 @@ sock_s_unpack_sockaddr_in(self, addr)
VALUE host;
sockaddr = (struct sockaddr_in*)StringValuePtr(addr);
- if (RSTRING(addr)->len != sizeof(struct sockaddr_in)) {
- rb_raise(rb_eTypeError, "sockaddr_in size differs - %ld required; %d given",
- RSTRING(addr)->len, sizeof(struct sockaddr_in));
- }
- host = mkipaddr(sockaddr);
+ host = mkipaddr((struct sockaddr*)sockaddr);
OBJ_INFECT(host, addr);
return rb_assoc_new(INT2NUM(ntohs(sockaddr->sin_port)), host);
}
@@ -2468,7 +2485,7 @@ Init_socket()
rb_cSocket = rb_define_class("Socket", rb_cBasicSocket);
- rb_define_method(rb_cSocket, "initialize", sock_init, 3);
+ rb_define_method(rb_cSocket, "initialize", sock_initialize, 3);
rb_define_method(rb_cSocket, "connect", sock_connect, 1);
rb_define_method(rb_cSocket, "bind", sock_bind, 1);
rb_define_method(rb_cSocket, "listen", sock_listen, 1);
diff --git a/ext/stringio/stringio.c b/ext/stringio/stringio.c
index f0c4af542c..7dfd8e6bd5 100644
--- a/ext/stringio/stringio.c
+++ b/ext/stringio/stringio.c
@@ -148,7 +148,7 @@ static VALUE strio_closed _((VALUE));
static VALUE strio_closed_read _((VALUE));
static VALUE strio_closed_write _((VALUE));
static VALUE strio_eof _((VALUE));
-static VALUE strio_become _((VALUE, VALUE));
+/* static VALUE strio_become _((VALUE, VALUE)); NOT USED */
static VALUE strio_get_lineno _((VALUE));
static VALUE strio_set_lineno _((VALUE, VALUE));
static VALUE strio_get_pos _((VALUE));
@@ -156,19 +156,19 @@ static VALUE strio_set_pos _((VALUE, VALUE));
static VALUE strio_rewind _((VALUE));
static VALUE strio_seek _((int, VALUE *, VALUE));
static VALUE strio_get_sync _((VALUE));
-static VALUE strio_set_sync _((VALUE, VALUE));
+/* static VALUE strio_set_sync _((VALUE, VALUE)); NOT USED */
static VALUE strio_each_byte _((VALUE));
static VALUE strio_getc _((VALUE));
static VALUE strio_ungetc _((VALUE, VALUE));
static VALUE strio_readchar _((VALUE));
-static VALUE strio_gets_internal _((int, VALUE *, struct StringIO *));
+static VALUE strio_getline _((int, VALUE *, struct StringIO *));
static VALUE strio_gets _((int, VALUE *, VALUE));
static VALUE strio_readline _((int, VALUE *, VALUE));
static VALUE strio_each _((int, VALUE *, VALUE));
static VALUE strio_readlines _((int, VALUE *, VALUE));
static VALUE strio_write _((VALUE, VALUE));
-static VALUE strio_print _((int, VALUE *, VALUE));
-static VALUE strio_printf _((int, VALUE *, VALUE));
+/* static VALUE strio_print _((int, VALUE *, VALUE)); NOT USED */
+/* static VALUE strio_printf _((int, VALUE *, VALUE)); NOT USED */
static VALUE strio_putc _((VALUE, VALUE));
static VALUE strio_read _((int, VALUE *, VALUE));
static VALUE strio_size _((VALUE));
@@ -633,7 +633,7 @@ bm_search(little, llen, big, blen, skip)
}
static VALUE
-strio_gets_internal(argc, argv, ptr)
+strio_getline(argc, argv, ptr)
int argc;
VALUE *argv;
struct StringIO *ptr;
@@ -701,7 +701,6 @@ strio_gets_internal(argc, argv, ptr)
}
ptr->pos = e - RSTRING(ptr->string)->ptr;
ptr->lineno++;
- rb_lastline_set(str);
return str;
}
@@ -711,7 +710,10 @@ strio_gets(argc, argv, self)
VALUE *argv;
VALUE self;
{
- return strio_gets_internal(argc, argv, readable(StringIO(self)));
+ VALUE str = strio_getline(argc, argv, readable(StringIO(self)));
+
+ rb_lastline_set(str);
+ return str;
}
static VALUE
@@ -720,7 +722,7 @@ strio_readline(argc, argv, self)
VALUE *argv;
VALUE self;
{
- VALUE line = strio_gets_internal(argc, argv, readable(StringIO(self)));
+ VALUE line = strio_getline(argc, argv, readable(StringIO(self)));
if (NIL_P(line)) rb_eof_error();
return line;
}
@@ -734,7 +736,7 @@ strio_each(argc, argv, self)
struct StringIO *ptr = StringIO(self);
VALUE line;
- while (!NIL_P(line = strio_gets_internal(argc, argv, readable(ptr)))) {
+ while (!NIL_P(line = strio_getline(argc, argv, readable(ptr)))) {
rb_yield(line);
}
return self;
@@ -748,7 +750,7 @@ strio_readlines(argc, argv, self)
{
struct StringIO *ptr = StringIO(self);
VALUE ary = rb_ary_new(), line;
- while (!NIL_P(line = strio_gets_internal(argc, argv, readable(ptr)))) {
+ while (!NIL_P(line = strio_getline(argc, argv, readable(ptr)))) {
rb_ary_push(ary, line);
}
return ary;
@@ -896,10 +898,11 @@ void
Init_stringio()
{
VALUE StringIO = rb_define_class("StringIO", rb_cData);
- rb_define_singleton_method(StringIO, "allocate", strio_s_allocate, 0);
+
+ rb_include_module(StringIO, rb_mEnumerable);
+ rb_define_alloc_func(StringIO, strio_s_allocate);
rb_define_singleton_method(StringIO, "open", strio_s_open, -1);
rb_define_method(StringIO, "initialize", strio_initialize, -1);
- rb_enable_super(StringIO, "initialize");
rb_define_method(StringIO, "copy_object", strio_copy, 1);
rb_define_method(StringIO, "reopen", strio_reopen, -1);
diff --git a/ext/strscan/strscan.c b/ext/strscan/strscan.c
index fb61ee07d2..7169c2e163 100644
--- a/ext/strscan/strscan.c
+++ b/ext/strscan/strscan.c
@@ -1,8 +1,8 @@
-/* vi:set sw=4:
+/*
strscan.c
- Copyright (c) 1999-2002 Minero Aoki <aamine@loveruby.net>
+ Copyright (c) 1999-2003 Minero Aoki <aamine@loveruby.net>
This program is free software.
You can distribute/modify this program under the terms of
@@ -644,34 +644,35 @@ strscan_rest_size(self)
#define INSPECT_LENGTH 5
+#define BUFSIZE 256
static VALUE
strscan_inspect(self)
VALUE self;
{
struct strscanner *p;
- char buf[256];
+ char buf[BUFSIZE];
char buf_before[16];
char buf_after[16];
long len;
Data_Get_Struct(self, struct strscanner, p);
if (NIL_P(p->str)) {
- len = sprintf(buf, "#<%s (uninitialized)>",
- rb_class2name(CLASS_OF(self)));
- return rb_str_new(buf, len);
+ len = snprintf(buf, BUFSIZE, "#<%s (uninitialized)>",
+ rb_class2name(CLASS_OF(self)));
+ return infect(rb_str_new(buf, len), p);
}
if (EOS_P(p)) {
- len = sprintf(buf, "#<%s fin>",
- rb_class2name(CLASS_OF(self)));
- return rb_str_new(buf, len);
+ len = snprintf(buf, BUFSIZE, "#<%s fin>",
+ rb_class2name(CLASS_OF(self)));
+ return infect(rb_str_new(buf, len), p);
}
- len = sprintf(buf, "#<%s %ld/%ld %s@%s>",
- rb_class2name(CLASS_OF(self)),
- p->curr, S_LEN(p),
- inspect_before(p, buf_before),
- inspect_after(p, buf_after));
- return rb_str_new(buf, len);
+ len = snprintf(buf, BUFSIZE, "#<%s %ld/%ld %s@%s>",
+ rb_class2name(CLASS_OF(self)),
+ p->curr, S_LEN(p),
+ inspect_before(p, buf_before),
+ inspect_after(p, buf_after));
+ return infect(rb_str_new(buf, len), p);
}
static char*
@@ -747,12 +748,9 @@ Init_strscan()
rb_obj_freeze(tmp);
rb_const_set(StringScanner, rb_intern("Id"), tmp);
- rb_define_singleton_method(StringScanner, "allocate",
- strscan_s_allocate, 0);
- rb_define_private_method(StringScanner, "initialize",
- strscan_initialize, -1);
- rb_define_singleton_method(StringScanner, "must_C_version",
- strscan_s_mustc, 0);
+ rb_define_alloc_func(StringScanner, strscan_s_allocate);
+ rb_define_private_method(StringScanner, "initialize", strscan_initialize, -1);
+ rb_define_singleton_method(StringScanner, "must_C_version", strscan_s_mustc, 0);
rb_define_method(StringScanner, "reset", strscan_reset, 0);
rb_define_method(StringScanner, "terminate", strscan_terminate, 0);
rb_define_method(StringScanner, "clear", strscan_terminate, 0);
diff --git a/ext/syck/.cvsignore b/ext/syck/.cvsignore
new file mode 100644
index 0000000000..4088712231
--- /dev/null
+++ b/ext/syck/.cvsignore
@@ -0,0 +1,3 @@
+Makefile
+mkmf.log
+*.def
diff --git a/ext/syck/MANIFEST b/ext/syck/MANIFEST
new file mode 100644
index 0000000000..666e9c0465
--- /dev/null
+++ b/ext/syck/MANIFEST
@@ -0,0 +1,12 @@
+MANIFEST
+extconf.rb
+gram.c
+gram.h
+handler.c
+implicit.c
+node.c
+rubyext.c
+syck.c
+syck.h
+token.c
+emitter.c
diff --git a/ext/syck/emitter.c b/ext/syck/emitter.c
new file mode 100644
index 0000000000..62f1b57f48
--- /dev/null
+++ b/ext/syck/emitter.c
@@ -0,0 +1,434 @@
+/*
+ * emitter.c
+ *
+ * $Author$
+ * $Date$
+ *
+ * Copyright (C) 2003 why the lucky stiff
+ *
+ * All Base64 code from Ruby's pack.c.
+ * Ruby is Copyright (C) 1993-2003 Yukihiro Matsumoto
+ */
+#include <stdio.h>
+#include <string.h>
+
+#include "syck.h"
+#include "ruby.h"
+
+#define DEFAULT_ANCHOR_FORMAT "id%03d"
+
+static char b64_table[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+struct adjust_arg {
+ /* Position to start adjusting */
+ long startpos;
+ /* Adjusting by an offset */
+ int offset;
+};
+
+/*
+ * Built-in base64 (from Ruby's pack.c)
+ */
+char *
+syck_base64enc( char *s, long len )
+{
+ long i = 0;
+ int padding = '=';
+ char *buff = S_ALLOCA_N(char, len * 4 / 3 + 6);
+
+ while (len >= 3) {
+ buff[i++] = b64_table[077 & (*s >> 2)];
+ buff[i++] = b64_table[077 & (((*s << 4) & 060) | ((s[1] >> 4) & 017))];
+ buff[i++] = b64_table[077 & (((s[1] << 2) & 074) | ((s[2] >> 6) & 03))];
+ buff[i++] = b64_table[077 & s[2]];
+ s += 3;
+ len -= 3;
+ }
+ if (len == 2) {
+ buff[i++] = b64_table[077 & (*s >> 2)];
+ buff[i++] = b64_table[077 & (((*s << 4) & 060) | ((s[1] >> 4) & 017))];
+ buff[i++] = b64_table[077 & (((s[1] << 2) & 074) | (('\0' >> 6) & 03))];
+ buff[i++] = padding;
+ }
+ else if (len == 1) {
+ buff[i++] = b64_table[077 & (*s >> 2)];
+ buff[i++] = b64_table[077 & (((*s << 4) & 060) | (('\0' >> 4) & 017))];
+ buff[i++] = padding;
+ buff[i++] = padding;
+ }
+ buff[i++] = '\n';
+ return buff;
+}
+
+char *
+syck_base64dec( char *s, long len )
+{
+ int a = -1,b = -1,c = 0,d;
+ static int first = 1;
+ static int b64_xtable[256];
+ char *ptr = syck_strndup( s, len );
+ char *end = ptr;
+ char *send = s + len;
+
+ if (first) {
+ int i;
+ first = 0;
+
+ for (i = 0; i < 256; i++) {
+ b64_xtable[i] = -1;
+ }
+ for (i = 0; i < 64; i++) {
+ b64_xtable[(int)b64_table[i]] = i;
+ }
+ }
+ while (s < send) {
+ while (s[0] == '\r' || s[0] == '\n') { s++; }
+ if ((a = b64_xtable[(int)s[0]]) == -1) break;
+ if ((b = b64_xtable[(int)s[1]]) == -1) break;
+ if ((c = b64_xtable[(int)s[2]]) == -1) break;
+ if ((d = b64_xtable[(int)s[3]]) == -1) break;
+ *end++ = a << 2 | b >> 4;
+ *end++ = b << 4 | c >> 2;
+ *end++ = c << 6 | d;
+ s += 4;
+ }
+ if (a != -1 && b != -1) {
+ if (s + 2 < send && s[2] == '=')
+ *end++ = a << 2 | b >> 4;
+ if (c != -1 && s + 3 < send && s[3] == '=') {
+ *end++ = a << 2 | b >> 4;
+ *end++ = b << 4 | c >> 2;
+ }
+ }
+ *end = '\0';
+ //RSTRING(buf)->len = ptr - RSTRING(buf)->ptr;
+ return ptr;
+}
+
+/*
+ * Allocate an emitter
+ */
+SyckEmitter *
+syck_new_emitter()
+{
+ SyckEmitter *e;
+ e = S_ALLOC( SyckEmitter );
+ e->headless = 0;
+ e->seq_map = 0;
+ e->use_header = 0;
+ e->use_version = 0;
+ e->sort_keys = 0;
+ e->anchor_format = NULL;
+ e->explicit_typing = 0;
+ e->best_width = 80;
+ e->block_style = block_arbitrary;
+ e->stage = doc_open;
+ e->indent = 2;
+ e->level = -1;
+ e->ignore_id = 0;
+ e->anchors = NULL;
+ e->markers = NULL;
+ e->bufsize = SYCK_BUFFERSIZE;
+ e->buffer = NULL;
+ e->marker = NULL;
+ e->bufpos = 0;
+ e->handler = NULL;
+ e->bonus = NULL;
+ return e;
+}
+
+int
+syck_st_free_anchors( char *key, char *name, char *arg )
+{
+ S_FREE( name );
+ return ST_CONTINUE;
+}
+
+int
+syck_st_free_markers( char *key, SyckEmitterNode *n, char *arg )
+{
+ S_FREE( n );
+ return ST_CONTINUE;
+}
+
+void
+syck_emitter_st_free( SyckEmitter *e )
+{
+ /*
+ * Free the anchor tables
+ */
+ if ( e->anchors != NULL )
+ {
+ st_foreach( e->anchors, syck_st_free_anchors, 0 );
+ st_free_table( e->anchors );
+ e->anchors = NULL;
+ }
+
+ /*
+ * Free the markers tables
+ */
+ if ( e->markers != NULL )
+ {
+ st_foreach( e->markers, syck_st_free_markers, 0 );
+ st_free_table( e->markers );
+ e->markers = NULL;
+ }
+}
+
+void
+syck_emitter_ignore_id( SyckEmitter *e, SYMID id )
+{
+ e->ignore_id = id;
+}
+
+void
+syck_emitter_handler( SyckEmitter *e, SyckOutputHandler hdlr )
+{
+ e->handler = hdlr;
+}
+
+void
+syck_free_emitter( SyckEmitter *e )
+{
+ /*
+ * Free tables
+ */
+ syck_emitter_st_free( e );
+ if ( e->buffer != NULL )
+ {
+ S_FREE( e->buffer );
+ }
+ S_FREE( e );
+}
+
+void
+syck_emitter_clear( SyckEmitter *e )
+{
+ if ( e->buffer == NULL )
+ {
+ e->buffer = S_ALLOC_N( char, e->bufsize );
+ S_MEMZERO( e->buffer, char, e->bufsize );
+ }
+ e->buffer[0] = '\0';
+ e->marker = e->buffer;
+ e->bufpos = 0;
+}
+
+/*
+ * Raw write to the emitter buffer.
+ */
+void
+syck_emitter_write( SyckEmitter *e, char *str, long len )
+{
+ long at;
+ ASSERT( str != NULL )
+ if ( e->buffer == NULL )
+ {
+ syck_emitter_clear( e );
+ }
+
+ /*
+ * Flush if at end of buffer
+ */
+ at = e->marker - e->buffer;
+ if ( len + at > e->bufsize )
+ {
+ syck_emitter_flush( e, 0 );
+ }
+
+ /*
+ * Write to buffer
+ */
+ S_MEMCPY( e->marker, str, char, len );
+ e->marker += len;
+}
+
+/*
+ * Write a chunk of data out.
+ */
+void
+syck_emitter_flush( SyckEmitter *e, long check_room )
+{
+ /*
+ * Check for enough space in the buffer for check_room length.
+ */
+ if ( check_room > 0 )
+ {
+ if ( e->bufsize > ( e->marker - e->buffer ) + check_room )
+ {
+ return;
+ }
+ }
+ else
+ {
+ check_room = e->bufsize;
+ }
+
+ /*
+ * Determine headers.
+ */
+ if ( ( e->stage == doc_open && ( e->headless == 0 || e->use_header == 1 ) ) ||
+ e->stage == doc_need_header )
+ {
+ if ( e->use_version == 1 )
+ {
+ char *header = S_ALLOC_N( char, 64 );
+ S_MEMZERO( header, char, 64 );
+ sprintf( header, "--- %%YAML:%d.%d ", SYCK_YAML_MAJOR, SYCK_YAML_MINOR );
+ (e->handler)( e, header, strlen( header ) );
+ S_FREE( header );
+ }
+ else
+ {
+ (e->handler)( e, "--- ", 4 );
+ }
+ e->stage = doc_processing;
+ }
+
+ /*
+ * Commit buffer.
+ */
+ if ( check_room > e->marker - e->buffer )
+ {
+ check_room = e->marker - e->buffer;
+ }
+ (e->handler)( e, e->buffer, check_room );
+ e->bufpos += check_room;
+ e->marker -= check_room;
+}
+
+/*
+ * Emit a simple, unquoted string.
+ */
+void
+syck_emitter_simple( SyckEmitter *e, char *str, long len )
+{
+ e->seq_map = 0;
+ syck_emitter_write( e, str, len );
+}
+
+/*
+ * Shift the offsets of all applicable anchors
+ */
+int
+syck_adjust_anchors( char *key, SyckEmitterNode *n, struct adjust_arg *arg )
+{
+ if ( arg->startpos < n->pos )
+ {
+ n->pos += arg->offset;
+ }
+ return ST_CONTINUE;
+}
+
+/*
+ * call on start of an object's marshalling
+ * (handles anchors, returns an alias)
+ */
+char *
+syck_emitter_start_obj( SyckEmitter *e, SYMID oid )
+{
+ SyckEmitterNode *n = NULL;
+ char *anchor_name = NULL;
+
+ e->level++;
+ if ( oid != e->ignore_id )
+ {
+ /*
+ * Look for anchors
+ */
+ if ( e->markers == NULL )
+ {
+ e->markers = st_init_numtable();
+ }
+
+ /*
+ * Markers table initially marks the string position of the
+ * object. Doesn't yet create an anchor, simply notes the
+ * position.
+ */
+ if ( ! st_lookup( e->markers, (st_data_t)oid, (st_data_t *)&n ) )
+ {
+ /*
+ * Store all markers
+ */
+ n = S_ALLOC( SyckEmitterNode );
+ n->is_shortcut = 0;
+ n->indent = e->level * e->indent;
+ n->pos = e->bufpos + ( e->marker - e->buffer );
+ st_insert( e->markers, (st_data_t)oid, (st_data_t)n );
+ }
+ else
+ {
+ if ( e->anchors == NULL )
+ {
+ e->anchors = st_init_numtable();
+ }
+
+ if ( ! st_lookup( e->anchors, (st_data_t)oid, (st_data_t *)&anchor_name ) )
+ {
+ int idx = 0;
+ /*
+ * Second time hitting this object, let's give it an anchor
+ */
+ idx = e->anchors->num_entries + 1;
+
+ /*
+ * Create the anchor tag
+ */
+ if ( n->pos >= e->bufpos )
+ {
+ int alen;
+ struct adjust_arg *args = S_ALLOC( struct adjust_arg );
+ char *start = e->buffer + ( n->pos - e->bufpos );
+
+ char *anc = ( e->anchor_format == NULL ? DEFAULT_ANCHOR_FORMAT : e->anchor_format );
+ anchor_name = S_ALLOC_N( char, strlen( anc ) + 10 );
+ S_MEMZERO( anchor_name, char, strlen( anc ) + 10 );
+ sprintf( anchor_name, anc, idx );
+
+ /*
+ * Need to flush the buffer some, if there is not room for the anchor.
+ */
+ alen = strlen( anchor_name ) + 2;
+ syck_emitter_flush( e, alen );
+
+ /*
+ * Write the anchor into the buffer
+ */
+ S_MEMMOVE( start + alen, start, char, e->marker - start );
+ S_MEMCPY( start + 1, anchor_name, char, strlen( anchor_name ) );
+ start[0] = '&';
+ start[alen - 1] = ' ';
+ e->marker += alen;
+
+ /*
+ * Cycle through anchors, modify for the size of the anchor.
+ */
+ args->startpos = n->pos;
+ args->offset = alen;
+ st_foreach( e->markers, syck_adjust_anchors, (st_data_t)args );
+ S_FREE( args );
+
+ /*
+ * Insert into anchors table
+ */
+ st_insert( e->anchors, (st_data_t)oid, (st_data_t)anchor_name );
+ }
+ }
+
+ }
+ }
+
+ return anchor_name;
+}
+
+/*
+ * call on completion of an object's marshalling
+ */
+void
+syck_emitter_end_obj( SyckEmitter *e )
+{
+ e->level--;
+}
+
diff --git a/ext/syck/extconf.rb b/ext/syck/extconf.rb
new file mode 100644
index 0000000000..6c10448c70
--- /dev/null
+++ b/ext/syck/extconf.rb
@@ -0,0 +1,5 @@
+require 'mkmf'
+
+have_header( "st.h" )
+create_makefile( "syck" )
+
diff --git a/ext/syck/gram.c b/ext/syck/gram.c
new file mode 100644
index 0000000000..edc6da3a45
--- /dev/null
+++ b/ext/syck/gram.c
@@ -0,0 +1,1702 @@
+/* A Bison parser, made from gram.y, by GNU bison 1.75. */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+/* Written by Richard Stallman by simplifying the original so called
+ ``semantic'' parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Pure parsers. */
+#define YYPURE 1
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+/* If NAME_PREFIX is specified substitute the variables and functions
+ names. */
+#define yyparse syckparse
+#define yylex sycklex
+#define yyerror syckerror
+#define yylval sycklval
+#define yychar syckchar
+#define yydebug syckdebug
+#define yynerrs sycknerrs
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ YAML_ANCHOR = 258,
+ YAML_ALIAS = 259,
+ YAML_TRANSFER = 260,
+ YAML_ITRANSFER = 261,
+ YAML_WORD = 262,
+ YAML_PLAIN = 263,
+ YAML_BLOCK = 264,
+ YAML_DOCSEP = 265,
+ YAML_IOPEN = 266,
+ YAML_INDENT = 267,
+ YAML_IEND = 268
+ };
+#endif
+#define YAML_ANCHOR 258
+#define YAML_ALIAS 259
+#define YAML_TRANSFER 260
+#define YAML_ITRANSFER 261
+#define YAML_WORD 262
+#define YAML_PLAIN 263
+#define YAML_BLOCK 264
+#define YAML_DOCSEP 265
+#define YAML_IOPEN 266
+#define YAML_INDENT 267
+#define YAML_IEND 268
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 14 "gram.y"
+
+
+#include "syck.h"
+
+#define YYPARSE_PARAM parser
+#define YYLEX_PARAM parser
+
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 1
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+#ifndef YYSTYPE
+#line 23 "gram.y"
+typedef union {
+ SYMID nodeId;
+ SyckNode *nodeData;
+ char *name;
+} yystype;
+/* Line 193 of /usr/local/share/bison/yacc.c. */
+#line 123 "y.tab.c"
+# define YYSTYPE yystype
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+#ifndef YYLTYPE
+typedef struct yyltype
+{
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+} yyltype;
+# define YYLTYPE yyltype
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+/* Copy the second part of user declarations. */
+int sycklex( YYSTYPE *, SyckParser * );
+
+
+/* Line 213 of /usr/local/share/bison/yacc.c. */
+#line 144 "y.tab.c"
+
+#if ! defined (yyoverflow) || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# if YYSTACK_USE_ALLOCA
+# define YYSTACK_ALLOC alloca
+# else
+# ifndef YYSTACK_USE_ALLOCA
+# if defined (alloca) || defined (_ALLOCA_H)
+# define YYSTACK_ALLOC alloca
+# else
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# else
+# if defined (__STDC__) || defined (__cplusplus)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# endif
+# define YYSTACK_ALLOC malloc
+# define YYSTACK_FREE free
+# endif
+#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */
+
+
+#if (! defined (yyoverflow) \
+ && (! defined (__cplusplus) \
+ || (YYLTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ short yyss;
+ YYSTYPE yyvs;
+ };
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAX (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (short) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAX)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ register YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (0)
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAX; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (0)
+
+#endif
+
+#if defined (__STDC__) || defined (__cplusplus)
+ typedef signed char yysigned_char;
+#else
+ typedef short yysigned_char;
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 35
+#define YYLAST 333
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 23
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 25
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 63
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 106
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 268
+
+#define YYTRANSLATE(X) \
+ ((unsigned)(X) <= YYMAXUTOK ? yytranslate[X] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const unsigned char yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 16, 21, 14, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 15, 2,
+ 2, 2, 2, 22, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 17, 2, 18, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 19, 2, 20, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const unsigned char yyprhs[] =
+{
+ 0, 0, 3, 5, 8, 9, 11, 13, 15, 18,
+ 22, 26, 28, 31, 32, 34, 37, 39, 41, 43,
+ 46, 49, 52, 55, 57, 59, 61, 64, 66, 68,
+ 70, 72, 74, 78, 81, 83, 87, 90, 94, 97,
+ 99, 103, 106, 110, 113, 115, 119, 123, 127, 131,
+ 134, 138, 141, 145, 147, 153, 155, 159, 163, 166,
+ 170, 174, 177, 179
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yysigned_char yyrhs[] =
+{
+ 24, 0, -1, 33, -1, 10, 27, -1, -1, 32,
+ -1, 26, -1, 33, -1, 3, 26, -1, 28, 32,
+ 31, -1, 28, 26, 31, -1, 25, -1, 28, 29,
+ -1, -1, 11, -1, 28, 12, -1, 13, -1, 12,
+ -1, 13, -1, 30, 31, -1, 5, 32, -1, 6,
+ 32, -1, 3, 32, -1, 4, -1, 7, -1, 8,
+ -1, 5, 33, -1, 9, -1, 34, -1, 38, -1,
+ 40, -1, 46, -1, 28, 36, 29, -1, 14, 27,
+ -1, 37, -1, 5, 30, 36, -1, 5, 36, -1,
+ 3, 30, 36, -1, 3, 36, -1, 35, -1, 37,
+ 30, 35, -1, 37, 30, -1, 17, 39, 18, -1,
+ 17, 18, -1, 25, -1, 39, 21, 25, -1, 28,
+ 41, 29, -1, 28, 44, 29, -1, 5, 30, 44,
+ -1, 5, 41, -1, 3, 30, 44, -1, 3, 41,
+ -1, 32, 15, 27, -1, 42, -1, 22, 25, 30,
+ 15, 27, -1, 43, -1, 44, 30, 35, -1, 44,
+ 30, 43, -1, 44, 30, -1, 25, 15, 27, -1,
+ 19, 47, 20, -1, 19, 20, -1, 45, -1, 47,
+ 21, 45, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const unsigned short yyrline[] =
+{
+ 0, 44, 44, 48, 52, 58, 59, 62, 63, 72,
+ 76, 82, 83, 96, 114, 115, 118, 121, 124, 125,
+ 133, 138, 146, 150, 158, 171, 178, 183, 184, 185,
+ 186, 187, 193, 199, 205, 206, 211, 216, 220, 226,
+ 230, 235, 244, 248, 254, 258, 268, 273, 280, 285,
+ 290, 294, 300, 315, 316, 324, 325, 337, 344, 353,
+ 361, 365, 371, 372
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE
+/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "YAML_ANCHOR", "YAML_ALIAS",
+ "YAML_TRANSFER", "YAML_ITRANSFER", "YAML_WORD", "YAML_PLAIN",
+ "YAML_BLOCK", "YAML_DOCSEP", "YAML_IOPEN", "YAML_INDENT", "YAML_IEND",
+ "'-'", "':'", "'+'", "'['", "']'", "'{'", "'}'", "','", "'?'",
+ "$accept", "doc", "atom", "ind_rep", "atom_or_empty", "indent_open",
+ "indent_end", "indent_sep", "indent_flex_end", "word_rep", "struct_rep",
+ "implicit_seq", "basic_seq", "top_imp_seq", "in_implicit_seq",
+ "inline_seq", "in_inline_seq", "implicit_map", "top_imp_map",
+ "basic_mapping", "complex_mapping", "in_implicit_map", "basic_mapping2",
+ "inline_map", "in_inline_map", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const unsigned short yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 45, 58, 43, 91, 93, 123,
+ 125, 44, 63
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const unsigned char yyr1[] =
+{
+ 0, 23, 24, 24, 24, 25, 25, 26, 26, 26,
+ 26, 27, 27, 27, 28, 28, 29, 30, 31, 31,
+ 32, 32, 32, 32, 32, 32, 33, 33, 33, 33,
+ 33, 33, 34, 35, 36, 36, 36, 36, 36, 37,
+ 37, 37, 38, 38, 39, 39, 40, 40, 41, 41,
+ 41, 41, 42, 43, 43, 44, 44, 44, 44, 45,
+ 46, 46, 47, 47
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const unsigned char yyr2[] =
+{
+ 0, 2, 1, 2, 0, 1, 1, 1, 2, 3,
+ 3, 1, 2, 0, 1, 2, 1, 1, 1, 2,
+ 2, 2, 2, 1, 1, 1, 2, 1, 1, 1,
+ 1, 1, 3, 2, 1, 3, 2, 3, 2, 1,
+ 3, 2, 3, 2, 1, 3, 3, 3, 3, 2,
+ 3, 2, 3, 1, 5, 1, 3, 3, 2, 3,
+ 3, 2, 1, 3
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const unsigned char yydefact[] =
+{
+ 4, 0, 27, 13, 14, 0, 0, 0, 0, 2,
+ 28, 29, 30, 31, 26, 0, 23, 0, 0, 24,
+ 25, 11, 6, 3, 0, 5, 7, 43, 44, 0,
+ 0, 61, 0, 62, 0, 1, 0, 0, 15, 13,
+ 0, 0, 39, 0, 34, 0, 53, 55, 0, 8,
+ 22, 0, 20, 0, 21, 0, 0, 16, 0, 12,
+ 0, 42, 0, 13, 60, 0, 17, 0, 38, 51,
+ 0, 36, 49, 33, 0, 13, 32, 41, 46, 47,
+ 58, 18, 0, 10, 9, 45, 59, 63, 0, 0,
+ 37, 50, 35, 48, 0, 52, 40, 56, 57, 19,
+ 0, 0, 13, 0, 0, 54
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yysigned_char yydefgoto[] =
+{
+ -1, 7, 21, 22, 23, 29, 59, 80, 83, 25,
+ 26, 10, 42, 68, 44, 11, 30, 12, 45, 46,
+ 47, 48, 33, 13, 34
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -54
+static const short yypact[] =
+{
+ 267, 278, -54, 245, -54, 228, 176, 8, 140, -54,
+ -54, -54, -54, -54, -54, 245, -54, 262, 325, -54,
+ -54, -54, -54, -54, 100, -54, -54, -54, -54, 120,
+ 48, -54, -5, -54, 52, -54, 295, 295, -54, 245,
+ 245, -3, -54, 13, 9, 13, -54, -54, 76, -54,
+ -54, 325, -54, 325, -54, 194, 211, -54, 108, -54,
+ 103, -54, 245, 245, -54, 245, -54, 152, -54, -54,
+ 152, -54, -54, -54, 9, 245, -54, 24, -54, -54,
+ 164, -54, 108, -54, -54, -54, -54, -54, 307, 307,
+ -54, 9, -54, 9, 32, -54, -54, -54, -54, -54,
+ 6, 6, 245, 313, 313, -54
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yysigned_char yypgoto[] =
+{
+ -54, -54, 31, -10, -35, 0, 12, -12, -53, -2,
+ 41, -54, -47, -6, -54, -54, -54, -54, 44, -54,
+ -28, 15, 14, -54, -54
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, parse error. */
+#define YYTABLE_NINF -1
+static const unsigned char yytable[] =
+{
+ 8, 8, 43, 24, 73, 49, 41, 84, 35, 103,
+ 63, 104, 75, 50, 58, 52, 54, 8, 43, 58,
+ 39, 66, 60, 43, 67, 70, 57, 60, 86, 99,
+ 96, 71, 77, 97, 50, 52, 28, 32, 39, 24,
+ 95, 9, 14, 67, 70, 49, 82, 102, 82, 50,
+ 71, 52, 98, 50, 52, 76, 8, 78, 14, 0,
+ 79, 90, 94, 24, 92, 41, 61, 105, 41, 62,
+ 82, 74, 64, 65, 0, 24, 100, 101, 41, 87,
+ 69, 72, 91, 71, 0, 93, 50, 52, 66, 57,
+ 0, 100, 101, 85, 90, 92, 32, 14, 71, 69,
+ 72, 0, 24, 55, 16, 56, 18, 19, 20, 2,
+ 0, 4, 38, 57, 39, 66, 81, 5, 75, 6,
+ 66, 81, 40, 55, 16, 56, 18, 19, 20, 2,
+ 0, 4, 38, 0, 39, 0, 0, 5, 0, 6,
+ 0, 0, 40, 36, 16, 37, 18, 19, 20, 0,
+ 0, 0, 38, 0, 39, 88, 16, 89, 18, 19,
+ 20, 0, 40, 0, 0, 0, 39, 51, 16, 53,
+ 18, 19, 20, 0, 40, 0, 0, 0, 39, 15,
+ 16, 17, 18, 19, 20, 2, 40, 4, 0, 0,
+ 0, 0, 0, 5, 0, 6, 31, 55, 16, 56,
+ 18, 19, 20, 2, 0, 4, 66, 0, 39, 0,
+ 0, 5, 0, 6, 36, 16, 56, 18, 19, 20,
+ 2, 0, 4, 66, 0, 39, 0, 0, 5, 0,
+ 6, 15, 16, 17, 18, 19, 20, 2, 0, 4,
+ 0, 0, 0, 0, 0, 5, 27, 6, 15, 16,
+ 17, 18, 19, 20, 2, 0, 4, 0, 0, 0,
+ 0, 0, 5, 0, 6, 51, 16, 17, 18, 19,
+ 20, 2, 1, 4, 0, 0, 2, 3, 4, 5,
+ 0, 6, 0, 1, 5, 0, 6, 2, 0, 4,
+ 0, 0, 0, 0, 0, 5, 0, 6, 36, 16,
+ 37, 18, 19, 20, 0, 0, 0, 66, 0, 39,
+ 88, 16, 89, 18, 19, 20, 103, 0, 104, 66,
+ 0, 39, 0, 0, 0, 66, 0, 39, 51, 16,
+ 53, 18, 19, 20
+};
+
+static const yysigned_char yycheck[] =
+{
+ 0, 1, 8, 3, 39, 15, 8, 60, 0, 3,
+ 15, 5, 15, 15, 24, 17, 18, 17, 24, 29,
+ 14, 12, 24, 29, 36, 37, 13, 29, 63, 82,
+ 77, 37, 44, 80, 36, 37, 5, 6, 14, 39,
+ 75, 0, 1, 55, 56, 55, 58, 15, 60, 51,
+ 56, 53, 80, 55, 56, 43, 56, 45, 17, -1,
+ 48, 67, 74, 63, 70, 67, 18, 102, 70, 21,
+ 82, 40, 20, 21, -1, 75, 88, 89, 80, 65,
+ 36, 37, 67, 89, -1, 70, 88, 89, 12, 13,
+ -1, 103, 104, 62, 100, 101, 65, 56, 104, 55,
+ 56, -1, 102, 3, 4, 5, 6, 7, 8, 9,
+ -1, 11, 12, 13, 14, 12, 13, 17, 15, 19,
+ 12, 13, 22, 3, 4, 5, 6, 7, 8, 9,
+ -1, 11, 12, -1, 14, -1, -1, 17, -1, 19,
+ -1, -1, 22, 3, 4, 5, 6, 7, 8, -1,
+ -1, -1, 12, -1, 14, 3, 4, 5, 6, 7,
+ 8, -1, 22, -1, -1, -1, 14, 3, 4, 5,
+ 6, 7, 8, -1, 22, -1, -1, -1, 14, 3,
+ 4, 5, 6, 7, 8, 9, 22, 11, -1, -1,
+ -1, -1, -1, 17, -1, 19, 20, 3, 4, 5,
+ 6, 7, 8, 9, -1, 11, 12, -1, 14, -1,
+ -1, 17, -1, 19, 3, 4, 5, 6, 7, 8,
+ 9, -1, 11, 12, -1, 14, -1, -1, 17, -1,
+ 19, 3, 4, 5, 6, 7, 8, 9, -1, 11,
+ -1, -1, -1, -1, -1, 17, 18, 19, 3, 4,
+ 5, 6, 7, 8, 9, -1, 11, -1, -1, -1,
+ -1, -1, 17, -1, 19, 3, 4, 5, 6, 7,
+ 8, 9, 5, 11, -1, -1, 9, 10, 11, 17,
+ -1, 19, -1, 5, 17, -1, 19, 9, -1, 11,
+ -1, -1, -1, -1, -1, 17, -1, 19, 3, 4,
+ 5, 6, 7, 8, -1, -1, -1, 12, -1, 14,
+ 3, 4, 5, 6, 7, 8, 3, -1, 5, 12,
+ -1, 14, -1, -1, -1, 12, -1, 14, 3, 4,
+ 5, 6, 7, 8
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const unsigned char yystos[] =
+{
+ 0, 5, 9, 10, 11, 17, 19, 24, 28, 33,
+ 34, 38, 40, 46, 33, 3, 4, 5, 6, 7,
+ 8, 25, 26, 27, 28, 32, 33, 18, 25, 28,
+ 39, 20, 25, 45, 47, 0, 3, 5, 12, 14,
+ 22, 32, 35, 36, 37, 41, 42, 43, 44, 26,
+ 32, 3, 32, 5, 32, 3, 5, 13, 26, 29,
+ 32, 18, 21, 15, 20, 21, 12, 30, 36, 41,
+ 30, 36, 41, 27, 25, 15, 29, 30, 29, 29,
+ 30, 13, 30, 31, 31, 25, 27, 45, 3, 5,
+ 36, 44, 36, 44, 30, 27, 35, 35, 43, 31,
+ 30, 30, 15, 3, 5, 27
+};
+
+#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__)
+# define YYSIZE_T __SIZE_TYPE__
+#endif
+#if ! defined (YYSIZE_T) && defined (size_t)
+# define YYSIZE_T size_t
+#endif
+#if ! defined (YYSIZE_T)
+# if defined (__STDC__) || defined (__cplusplus)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# endif
+#endif
+#if ! defined (YYSIZE_T)
+# define YYSIZE_T unsigned int
+#endif
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY -2
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrlab1
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yychar1 = YYTRANSLATE (yychar); \
+ YYPOPSTACK; \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror ("syntax error: cannot back up"); \
+ YYERROR; \
+ } \
+while (0)
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+/* YYLLOC_DEFAULT -- Compute the default location (before the actions
+ are run). */
+
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ Current.first_line = Rhs[1].first_line; \
+ Current.first_column = Rhs[1].first_column; \
+ Current.last_line = Rhs[N].last_line; \
+ Current.last_column = Rhs[N].last_column;
+#endif
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (&yylval, YYLEX_PARAM)
+#else
+# define YYLEX yylex (&yylval)
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (0)
+# define YYDSYMPRINT(Args) \
+do { \
+ if (yydebug) \
+ yysymprint Args; \
+} while (0)
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YYDSYMPRINT(Args)
+#endif /* !YYDEBUG */
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#if YYMAXDEPTH == 0
+# undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined (__GLIBC__) && defined (_STRING_H)
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+static YYSIZE_T
+# if defined (__STDC__) || defined (__cplusplus)
+yystrlen (const char *yystr)
+# else
+yystrlen (yystr)
+ const char *yystr;
+# endif
+{
+ register const char *yys = yystr;
+
+ while (*yys++ != '\0')
+ continue;
+
+ return yys - yystr - 1;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE)
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+static char *
+# if defined (__STDC__) || defined (__cplusplus)
+yystpcpy (char *yydest, const char *yysrc)
+# else
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+# endif
+{
+ register char *yyd = yydest;
+ register const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+#endif /* !YYERROR_VERBOSE */
+
+
+
+#if YYDEBUG
+/*-----------------------------.
+| Print this symbol on YYOUT. |
+`-----------------------------*/
+
+static void
+#if defined (__STDC__) || defined (__cplusplus)
+yysymprint (FILE* yyout, int yytype, YYSTYPE yyvalue)
+#else
+yysymprint (yyout, yytype, yyvalue)
+ FILE* yyout;
+ int yytype;
+ YYSTYPE yyvalue;
+#endif
+{
+ /* Pacify ``unused variable'' warnings. */
+ (void) yyvalue;
+
+ if (yytype < YYNTOKENS)
+ {
+ YYFPRINTF (yyout, "token %s (", yytname[yytype]);
+# ifdef YYPRINT
+ YYPRINT (yyout, yytoknum[yytype], yyvalue);
+# endif
+ }
+ else
+ YYFPRINTF (yyout, "nterm %s (", yytname[yytype]);
+
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+ YYFPRINTF (yyout, ")");
+}
+#endif /* YYDEBUG. */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+static void
+#if defined (__STDC__) || defined (__cplusplus)
+yydestruct (int yytype, YYSTYPE yyvalue)
+#else
+yydestruct (yytype, yyvalue)
+ int yytype;
+ YYSTYPE yyvalue;
+#endif
+{
+ /* Pacify ``unused variable'' warnings. */
+ (void) yyvalue;
+
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+
+/* The user can define YYPARSE_PARAM as the name of an argument to be passed
+ into yyparse. The argument should have type void *.
+ It should actually point to an object.
+ Grammar actions can access the variable by casting it
+ to the proper pointer type. */
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+# define YYPARSE_PARAM_ARG void *YYPARSE_PARAM
+# define YYPARSE_PARAM_DECL
+# else
+# define YYPARSE_PARAM_ARG YYPARSE_PARAM
+# define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
+# endif
+#else /* !YYPARSE_PARAM */
+# define YYPARSE_PARAM_ARG
+# define YYPARSE_PARAM_DECL
+#endif /* !YYPARSE_PARAM */
+
+/* Prevent warning if -Wstrict-prototypes. */
+#ifdef __GNUC__
+# ifdef YYPARSE_PARAM
+int yyparse (void *);
+# else
+int yyparse (void);
+# endif
+#endif
+
+
+
+
+int
+yyparse (YYPARSE_PARAM_ARG)
+ YYPARSE_PARAM_DECL
+{
+ /* The lookahead symbol. */
+int yychar;
+
+/* The semantic value of the lookahead symbol. */
+YYSTYPE yylval;
+
+/* Number of parse errors so far. */
+int yynerrs;
+
+ register int yystate;
+ register int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Lookahead token as an internal (translated) token number. */
+ int yychar1 = 0;
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ short yyssa[YYINITDEPTH];
+ short *yyss = yyssa;
+ register short *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ register YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK (yyvsp--, yyssp--)
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+
+ /* When reducing, the number of symbols on the RHS of the reduced
+ rule. */
+ int yylen;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks.
+ */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ short *yyss1 = yyss;
+
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow ("parser stack overflow",
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyoverflowlab;
+# else
+ /* Extend the stack our own way. */
+ if (yystacksize >= YYMAXDEPTH)
+ goto yyoverflowlab;
+ yystacksize *= 2;
+ if (yystacksize > YYMAXDEPTH)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ short *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyoverflowlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyssp >= yyss + yystacksize - 1)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+/* Do appropriate processing given the current state. */
+/* Read a lookahead token if we need one and don't already have one. */
+/* yyresume: */
+
+ /* First try to decide what to do without reference to lookahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* yychar is either YYEMPTY or YYEOF
+ or a valid token in external form. */
+
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ /* Convert token to internal form (in yychar1) for indexing tables with. */
+
+ if (yychar <= 0) /* This means end of input. */
+ {
+ yychar1 = 0;
+ yychar = YYEOF; /* Don't call YYLEX any more. */
+
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yychar1 = YYTRANSLATE (yychar);
+
+ /* We have to keep this `#if YYDEBUG', since we use variables
+ which are defined only if `YYDEBUG' is set. */
+ YYDPRINTF ((stderr, "Next token is "));
+ YYDSYMPRINT ((stderr, yychar1, yylval));
+ YYDPRINTF ((stderr, "\n"));
+ }
+
+ /* If the proper action on seeing token YYCHAR1 is to reduce or to
+ detect an error, take that action. */
+ yyn += yychar1;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yychar1)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the lookahead token. */
+ YYDPRINTF ((stderr, "Shifting token %d (%s), ",
+ yychar, yytname[yychar1]));
+
+ /* Discard the token being shifted unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ *++yyvsp = yylval;
+
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+
+#if YYDEBUG
+ /* We have to keep this `#if YYDEBUG', since we use variables which
+ are defined only if `YYDEBUG' is set. */
+ if (yydebug)
+ {
+ int yyi;
+
+ YYFPRINTF (stderr, "Reducing via rule %d (line %d), ",
+ yyn - 1, yyrline[yyn]);
+
+ /* Print the symbols being reduced, and their result. */
+ for (yyi = yyprhs[yyn]; yyrhs[yyi] >= 0; yyi++)
+ YYFPRINTF (stderr, "%s ", yytname[yyrhs[yyi]]);
+ YYFPRINTF (stderr, " -> %s\n", yytname[yyr1[yyn]]);
+ }
+#endif
+ switch (yyn)
+ {
+ case 2:
+#line 45 "gram.y"
+ {
+ ((SyckParser *)parser)->root = syck_hdlr_add_node( (SyckParser *)parser, yyvsp[0].nodeData );
+ }
+ break;
+
+ case 3:
+#line 49 "gram.y"
+ {
+ ((SyckParser *)parser)->root = syck_hdlr_add_node( (SyckParser *)parser, yyvsp[0].nodeData );
+ }
+ break;
+
+ case 4:
+#line 53 "gram.y"
+ {
+ ((SyckParser *)parser)->eof = 1;
+ }
+ break;
+
+ case 8:
+#line 64 "gram.y"
+ {
+ /*
+ * _Anchors_: The language binding must keep a separate symbol table
+ * for anchors. The actual ID in the symbol table is returned to the
+ * higher nodes, though.
+ */
+ yyval.nodeData = syck_hdlr_add_anchor( (SyckParser *)parser, yyvsp[-1].name, yyvsp[0].nodeData );
+ }
+ break;
+
+ case 9:
+#line 73 "gram.y"
+ {
+ yyval.nodeData = yyvsp[-1].nodeData;
+ }
+ break;
+
+ case 10:
+#line 77 "gram.y"
+ {
+ yyval.nodeData = yyvsp[-1].nodeData;
+ }
+ break;
+
+ case 12:
+#line 84 "gram.y"
+ {
+ SyckNode *n = syck_new_str( "" );
+ if ( ((SyckParser *)parser)->taguri_expansion == 1 )
+ {
+ n->type_id = syck_taguri( YAML_DOMAIN, "null", 4 );
+ }
+ else
+ {
+ n->type_id = syck_strndup( "null", 4 );
+ }
+ yyval.nodeData = n;
+ }
+ break;
+
+ case 13:
+#line 97 "gram.y"
+ {
+ SyckNode *n = syck_new_str( "" );
+ if ( ((SyckParser *)parser)->taguri_expansion == 1 )
+ {
+ n->type_id = syck_taguri( YAML_DOMAIN, "null", 4 );
+ }
+ else
+ {
+ n->type_id = syck_strndup( "null", 4 );
+ }
+ yyval.nodeData = n;
+ }
+ break;
+
+ case 20:
+#line 134 "gram.y"
+ {
+ syck_add_transfer( yyvsp[-1].name, yyvsp[0].nodeData, ((SyckParser *)parser)->taguri_expansion );
+ yyval.nodeData = yyvsp[0].nodeData;
+ }
+ break;
+
+ case 21:
+#line 139 "gram.y"
+ {
+ if ( ((SyckParser *)parser)->implicit_typing == 1 )
+ {
+ try_tag_implicit( yyvsp[0].nodeData, ((SyckParser *)parser)->taguri_expansion );
+ }
+ yyval.nodeData = yyvsp[0].nodeData;
+ }
+ break;
+
+ case 22:
+#line 147 "gram.y"
+ {
+ yyval.nodeData = syck_hdlr_add_anchor( (SyckParser *)parser, yyvsp[-1].name, yyvsp[0].nodeData );
+ }
+ break;
+
+ case 23:
+#line 151 "gram.y"
+ {
+ /*
+ * _Aliases_: The anchor symbol table is scanned for the anchor name.
+ * The anchor's ID in the language's symbol table is returned.
+ */
+ yyval.nodeData = syck_hdlr_get_anchor( (SyckParser *)parser, yyvsp[0].name );
+ }
+ break;
+
+ case 24:
+#line 159 "gram.y"
+ {
+ SyckNode *n = yyvsp[0].nodeData;
+ if ( ((SyckParser *)parser)->taguri_expansion == 1 )
+ {
+ n->type_id = syck_taguri( YAML_DOMAIN, "str", 3 );
+ }
+ else
+ {
+ n->type_id = syck_strndup( "str", 3 );
+ }
+ yyval.nodeData = n;
+ }
+ break;
+
+ case 26:
+#line 179 "gram.y"
+ {
+ syck_add_transfer( yyvsp[-1].name, yyvsp[0].nodeData, ((SyckParser *)parser)->taguri_expansion );
+ yyval.nodeData = yyvsp[0].nodeData;
+ }
+ break;
+
+ case 32:
+#line 194 "gram.y"
+ {
+ yyval.nodeData = yyvsp[-1].nodeData;
+ }
+ break;
+
+ case 33:
+#line 200 "gram.y"
+ {
+ yyval.nodeId = syck_hdlr_add_node( (SyckParser *)parser, yyvsp[0].nodeData );
+ }
+ break;
+
+ case 35:
+#line 207 "gram.y"
+ {
+ syck_add_transfer( yyvsp[-2].name, yyvsp[0].nodeData, ((SyckParser *)parser)->taguri_expansion );
+ yyval.nodeData = yyvsp[0].nodeData;
+ }
+ break;
+
+ case 36:
+#line 212 "gram.y"
+ {
+ syck_add_transfer( yyvsp[-1].name, yyvsp[0].nodeData, ((SyckParser *)parser)->taguri_expansion );
+ yyval.nodeData = yyvsp[0].nodeData;
+ }
+ break;
+
+ case 37:
+#line 217 "gram.y"
+ {
+ yyval.nodeData = syck_hdlr_add_anchor( (SyckParser *)parser, yyvsp[-2].name, yyvsp[0].nodeData );
+ }
+ break;
+
+ case 38:
+#line 221 "gram.y"
+ {
+ yyval.nodeData = syck_hdlr_add_anchor( (SyckParser *)parser, yyvsp[-1].name, yyvsp[0].nodeData );
+ }
+ break;
+
+ case 39:
+#line 227 "gram.y"
+ {
+ yyval.nodeData = syck_new_seq( yyvsp[0].nodeId );
+ }
+ break;
+
+ case 40:
+#line 231 "gram.y"
+ {
+ syck_seq_add( yyvsp[-2].nodeData, yyvsp[0].nodeId );
+ yyval.nodeData = yyvsp[-2].nodeData;
+ }
+ break;
+
+ case 41:
+#line 236 "gram.y"
+ {
+ yyval.nodeData = yyvsp[-1].nodeData;
+ }
+ break;
+
+ case 42:
+#line 245 "gram.y"
+ {
+ yyval.nodeData = yyvsp[-1].nodeData;
+ }
+ break;
+
+ case 43:
+#line 249 "gram.y"
+ {
+ yyval.nodeData = syck_alloc_seq();
+ }
+ break;
+
+ case 44:
+#line 255 "gram.y"
+ {
+ yyval.nodeData = syck_new_seq( syck_hdlr_add_node( (SyckParser *)parser, yyvsp[0].nodeData ) );
+ }
+ break;
+
+ case 45:
+#line 259 "gram.y"
+ {
+ syck_seq_add( yyvsp[-2].nodeData, syck_hdlr_add_node( (SyckParser *)parser, yyvsp[0].nodeData ) );
+ yyval.nodeData = yyvsp[-2].nodeData;
+ }
+ break;
+
+ case 46:
+#line 269 "gram.y"
+ {
+ apply_seq_in_map( (SyckParser *)parser, yyvsp[-1].nodeData );
+ yyval.nodeData = yyvsp[-1].nodeData;
+ }
+ break;
+
+ case 47:
+#line 274 "gram.y"
+ {
+ apply_seq_in_map( (SyckParser *)parser, yyvsp[-1].nodeData );
+ yyval.nodeData = yyvsp[-1].nodeData;
+ }
+ break;
+
+ case 48:
+#line 281 "gram.y"
+ {
+ syck_add_transfer( yyvsp[-2].name, yyvsp[0].nodeData, ((SyckParser *)parser)->taguri_expansion );
+ yyval.nodeData = yyvsp[0].nodeData;
+ }
+ break;
+
+ case 49:
+#line 286 "gram.y"
+ {
+ syck_add_transfer( yyvsp[-1].name, yyvsp[0].nodeData, ((SyckParser *)parser)->taguri_expansion );
+ yyval.nodeData = yyvsp[0].nodeData;
+ }
+ break;
+
+ case 50:
+#line 291 "gram.y"
+ {
+ yyval.nodeData = syck_hdlr_add_anchor( (SyckParser *)parser, yyvsp[-2].name, yyvsp[0].nodeData );
+ }
+ break;
+
+ case 51:
+#line 295 "gram.y"
+ {
+ yyval.nodeData = syck_hdlr_add_anchor( (SyckParser *)parser, yyvsp[-1].name, yyvsp[0].nodeData );
+ }
+ break;
+
+ case 52:
+#line 301 "gram.y"
+ {
+ yyval.nodeData = syck_new_map(
+ syck_hdlr_add_node( (SyckParser *)parser, yyvsp[-2].nodeData ),
+ syck_hdlr_add_node( (SyckParser *)parser, yyvsp[0].nodeData ) );
+ }
+ break;
+
+ case 54:
+#line 317 "gram.y"
+ {
+ yyval.nodeData = syck_new_map(
+ syck_hdlr_add_node( (SyckParser *)parser, yyvsp[-3].nodeData ),
+ syck_hdlr_add_node( (SyckParser *)parser, yyvsp[0].nodeData ) );
+ }
+ break;
+
+ case 56:
+#line 326 "gram.y"
+ {
+ if ( yyvsp[-2].nodeData->shortcut == NULL )
+ {
+ yyvsp[-2].nodeData->shortcut = syck_new_seq( yyvsp[0].nodeId );
+ }
+ else
+ {
+ syck_seq_add( yyvsp[-2].nodeData->shortcut, yyvsp[0].nodeId );
+ }
+ yyval.nodeData = yyvsp[-2].nodeData;
+ }
+ break;
+
+ case 57:
+#line 338 "gram.y"
+ {
+ apply_seq_in_map( (SyckParser *)parser, yyvsp[-2].nodeData );
+ syck_map_update( yyvsp[-2].nodeData, yyvsp[0].nodeData );
+ syck_free_node( yyvsp[0].nodeData );
+ yyval.nodeData = yyvsp[-2].nodeData;
+ }
+ break;
+
+ case 58:
+#line 345 "gram.y"
+ {
+ yyval.nodeData = yyvsp[-1].nodeData;
+ }
+ break;
+
+ case 59:
+#line 354 "gram.y"
+ {
+ yyval.nodeData = syck_new_map(
+ syck_hdlr_add_node( (SyckParser *)parser, yyvsp[-2].nodeData ),
+ syck_hdlr_add_node( (SyckParser *)parser, yyvsp[0].nodeData ) );
+ }
+ break;
+
+ case 60:
+#line 362 "gram.y"
+ {
+ yyval.nodeData = yyvsp[-1].nodeData;
+ }
+ break;
+
+ case 61:
+#line 366 "gram.y"
+ {
+ yyval.nodeData = syck_alloc_map();
+ }
+ break;
+
+ case 63:
+#line 373 "gram.y"
+ {
+ syck_map_update( yyvsp[-2].nodeData, yyvsp[0].nodeData );
+ syck_free_node( yyvsp[0].nodeData );
+ yyval.nodeData = yyvsp[-2].nodeData;
+ }
+ break;
+
+
+ }
+
+/* Line 1016 of /usr/local/share/bison/yacc.c. */
+#line 1464 "y.tab.c"
+
+ yyvsp -= yylen;
+ yyssp -= yylen;
+
+
+#if YYDEBUG
+ if (yydebug)
+ {
+ short *yyssp1 = yyss - 1;
+ YYFPRINTF (stderr, "state stack now");
+ while (yyssp1 != yyssp)
+ YYFPRINTF (stderr, " %d", *++yyssp1);
+ YYFPRINTF (stderr, "\n");
+ }
+#endif
+
+ *++yyvsp = yyval;
+
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if YYERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (YYPACT_NINF < yyn && yyn < YYLAST)
+ {
+ YYSIZE_T yysize = 0;
+ int yytype = YYTRANSLATE (yychar);
+ char *yymsg;
+ int yyx, yycount;
+
+ yycount = 0;
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ for (yyx = yyn < 0 ? -yyn : 0;
+ yyx < (int) (sizeof (yytname) / sizeof (char *)); yyx++)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ yysize += yystrlen (yytname[yyx]) + 15, yycount++;
+ yysize += yystrlen ("parse error, unexpected ") + 1;
+ yysize += yystrlen (yytname[yytype]);
+ yymsg = (char *) YYSTACK_ALLOC (yysize);
+ if (yymsg != 0)
+ {
+ char *yyp = yystpcpy (yymsg, "parse error, unexpected ");
+ yyp = yystpcpy (yyp, yytname[yytype]);
+
+ if (yycount < 5)
+ {
+ yycount = 0;
+ for (yyx = yyn < 0 ? -yyn : 0;
+ yyx < (int) (sizeof (yytname) / sizeof (char *));
+ yyx++)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ const char *yyq = ! yycount ? ", expecting " : " or ";
+ yyp = yystpcpy (yyp, yyq);
+ yyp = yystpcpy (yyp, yytname[yyx]);
+ yycount++;
+ }
+ }
+ yyerror (yymsg);
+ YYSTACK_FREE (yymsg);
+ }
+ else
+ yyerror ("parse error; also virtual memory exhausted");
+ }
+ else
+#endif /* YYERROR_VERBOSE */
+ yyerror ("parse error");
+ }
+ goto yyerrlab1;
+
+
+/*----------------------------------------------------.
+| yyerrlab1 -- error raised explicitly by an action. |
+`----------------------------------------------------*/
+yyerrlab1:
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ {
+ /* Pop the error token. */
+ YYPOPSTACK;
+ /* Pop the rest of the stack. */
+ while (yyssp > yyss)
+ {
+ YYDPRINTF ((stderr, "Error: popping "));
+ YYDSYMPRINT ((stderr,
+ yystos[*yyssp],
+ *yyvsp));
+ YYDPRINTF ((stderr, "\n"));
+ yydestruct (yystos[*yyssp], *yyvsp);
+ YYPOPSTACK;
+ }
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Discarding token %d (%s).\n",
+ yychar, yytname[yychar1]));
+ yydestruct (yychar1, yylval);
+ yychar = YYEMPTY;
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+ YYDPRINTF ((stderr, "Error: popping "));
+ YYDSYMPRINT ((stderr,
+ yystos[*yyssp], *yyvsp));
+ YYDPRINTF ((stderr, "\n"));
+
+ yydestruct (yystos[yystate], *yyvsp);
+ yyvsp--;
+ yystate = *--yyssp;
+
+
+#if YYDEBUG
+ if (yydebug)
+ {
+ short *yyssp1 = yyss - 1;
+ YYFPRINTF (stderr, "Error: state stack now");
+ while (yyssp1 != yyssp)
+ YYFPRINTF (stderr, " %d", *++yyssp1);
+ YYFPRINTF (stderr, "\n");
+ }
+#endif
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ YYDPRINTF ((stderr, "Shifting error token, "));
+
+ *++yyvsp = yylval;
+
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*----------------------------------------------.
+| yyoverflowlab -- parser overflow comes here. |
+`----------------------------------------------*/
+yyoverflowlab:
+ yyerror ("parser stack overflow");
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+ return yyresult;
+}
+
+
+#line 380 "gram.y"
+
+
+void
+apply_seq_in_map( SyckParser *parser, SyckNode *n )
+{
+ long map_len;
+ if ( n->shortcut == NULL )
+ {
+ return;
+ }
+
+ map_len = syck_map_count( n );
+ syck_map_assign( n, map_value, map_len - 1,
+ syck_hdlr_add_node( parser, n->shortcut ) );
+
+ n->shortcut = NULL;
+}
+
+
diff --git a/ext/syck/gram.h b/ext/syck/gram.h
new file mode 100644
index 0000000000..f21796a64d
--- /dev/null
+++ b/ext/syck/gram.h
@@ -0,0 +1,79 @@
+/* A Bison parser, made from gram.y, by GNU bison 1.75. */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+#ifndef BISON_Y_TAB_H
+# define BISON_Y_TAB_H
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ YAML_ANCHOR = 258,
+ YAML_ALIAS = 259,
+ YAML_TRANSFER = 260,
+ YAML_ITRANSFER = 261,
+ YAML_WORD = 262,
+ YAML_PLAIN = 263,
+ YAML_BLOCK = 264,
+ YAML_DOCSEP = 265,
+ YAML_IOPEN = 266,
+ YAML_INDENT = 267,
+ YAML_IEND = 268
+ };
+#endif
+#define YAML_ANCHOR 258
+#define YAML_ALIAS 259
+#define YAML_TRANSFER 260
+#define YAML_ITRANSFER 261
+#define YAML_WORD 262
+#define YAML_PLAIN 263
+#define YAML_BLOCK 264
+#define YAML_DOCSEP 265
+#define YAML_IOPEN 266
+#define YAML_INDENT 267
+#define YAML_IEND 268
+
+
+
+
+#ifndef YYSTYPE
+#line 23 "gram.y"
+typedef union {
+ SYMID nodeId;
+ SyckNode *nodeData;
+ char *name;
+} yystype;
+/* Line 1281 of /usr/local/share/bison/yacc.c. */
+#line 72 "y.tab.h"
+# define YYSTYPE yystype
+#endif
+
+
+
+
+#endif /* not BISON_Y_TAB_H */
+
diff --git a/ext/syck/handler.c b/ext/syck/handler.c
new file mode 100644
index 0000000000..0a7ee8c10f
--- /dev/null
+++ b/ext/syck/handler.c
@@ -0,0 +1,146 @@
+/*
+ * handler.h
+ *
+ * $Author$
+ * $Date$
+ *
+ * Copyright (C) 2003 why the lucky stiff
+ */
+
+#include "syck.h"
+#include "ruby.h"
+
+SYMID
+syck_hdlr_add_node( SyckParser *p, SyckNode *n )
+{
+ SYMID id;
+
+ if ( ! n->id )
+ {
+ n->id = (p->handler)( p, n );
+ }
+ id = n->id;
+
+ if ( n->anchor == NULL )
+ {
+ syck_free_node( n );
+ }
+ return id;
+}
+
+SyckNode *
+syck_hdlr_add_anchor( SyckParser *p, char *a, SyckNode *n )
+{
+ n->anchor = a;
+ if ( p->bad_anchors != NULL )
+ {
+ SyckNode *bad;
+ if ( st_lookup( p->bad_anchors, (st_data_t)a, (st_data_t *)&bad ) )
+ {
+ if ( n->kind != syck_str_kind )
+ {
+ n->id = bad->id;
+ (p->handler)( p, n );
+ }
+ }
+ }
+ if ( p->anchors == NULL )
+ {
+ p->anchors = st_init_strtable();
+ }
+ st_insert( p->anchors, (st_data_t)a, (st_data_t)n );
+ return n;
+}
+
+void
+syck_hdlr_remove_anchor( SyckParser *p, char *a )
+{
+ if ( p->anchors == NULL )
+ {
+ p->anchors = st_init_strtable();
+ }
+ st_insert( p->anchors, (st_data_t)a, (st_data_t)1 );
+}
+
+SyckNode *
+syck_hdlr_get_anchor( SyckParser *p, char *a )
+{
+ SyckNode *n = NULL;
+
+ if ( p->anchors != NULL )
+ {
+ if ( st_lookup( p->anchors, (st_data_t)a, (st_data_t *)&n ) )
+ {
+ if ( n != (void *)1 )
+ {
+ return n;
+ }
+ else
+ {
+ if ( p->bad_anchors == NULL )
+ {
+ p->bad_anchors = st_init_strtable();
+ }
+ if ( ! st_lookup( p->bad_anchors, (st_data_t)a, (st_data_t *)&n ) )
+ {
+ n = (p->bad_anchor_handler)( p, a );
+ st_insert( p->bad_anchors, (st_data_t)a, (st_data_t)n );
+ }
+ }
+ }
+ }
+
+ if ( n == NULL )
+ {
+ n = (p->bad_anchor_handler)( p, a );
+ }
+ n->anchor = a;
+ return n;
+}
+
+void
+syck_add_transfer( char *uri, SyckNode *n, int taguri )
+{
+ if ( n->type_id != NULL )
+ {
+ S_FREE( n->type_id );
+ }
+
+ if ( taguri == 0 )
+ {
+ n->type_id = uri;
+ return;
+ }
+
+ n->type_id = syck_type_id_to_uri( uri );
+ S_FREE( uri );
+}
+
+char *
+syck_xprivate( char *type_id, int type_len )
+{
+ char *uri = S_ALLOC_N( char, type_len + 14 );
+ uri[0] = '\0';
+ strcat( uri, "x-private:" );
+ strncat( uri, type_id, type_len );
+ return uri;
+}
+
+char *
+syck_taguri( char *domain, char *type_id, int type_len )
+{
+ char *uri = S_ALLOC_N( char, strlen( domain ) + type_len + 14 );
+ uri[0] = '\0';
+ strcat( uri, "taguri:" );
+ strcat( uri, domain );
+ strcat( uri, ":" );
+ strncat( uri, type_id, type_len );
+ return uri;
+}
+
+int
+syck_try_implicit( SyckNode *n )
+{
+ return 1;
+}
+
diff --git a/ext/syck/implicit.c b/ext/syck/implicit.c
new file mode 100644
index 0000000000..4ab61c5539
--- /dev/null
+++ b/ext/syck/implicit.c
@@ -0,0 +1,2826 @@
+/* Generated by re2c 0.5 on Mon Jul 28 11:21:48 2003 */
+#line 1 "implicit.re"
+/*
+ * implicit.re
+ *
+ * $Author$
+ * $Date$
+ *
+ * Copyright (C) 2003 why the lucky stiff
+ */
+
+#include "syck.h"
+#include "ruby.h"
+
+#define YYCTYPE char
+#define YYCURSOR cursor
+#define YYMARKER marker
+#define YYLIMIT limit
+#define YYFILL(n)
+
+void
+try_tag_implicit( SyckNode *n, int taguri )
+{
+ char *tid = "";
+ switch ( n->kind )
+ {
+ case syck_str_kind:
+ tid = syck_match_implicit( n->data.str->ptr, n->data.str->len );
+ break;
+
+ case syck_seq_kind:
+ tid = "seq";
+ break;
+
+ case syck_map_kind:
+ tid = "map";
+ break;
+ }
+ if ( taguri == 1 )
+ {
+ n->type_id = syck_taguri( YAML_DOMAIN, tid, strlen( tid ) );
+ } else {
+ n->type_id = syck_strndup( tid, strlen( tid ) );
+ }
+}
+
+char *syck_match_implicit( char *str, size_t len )
+{
+ char *cursor, *limit, *marker;
+ cursor = str;
+ limit = str + len;
+
+{
+ YYCTYPE yych;
+ unsigned int yyaccept;
+ goto yy0;
+yy1: ++YYCURSOR;
+yy0:
+ if((YYLIMIT - YYCURSOR) < 26) YYFILL(26);
+ yych = *YYCURSOR;
+ switch(yych){
+ case '\000': goto yy2;
+ case '+': goto yy15;
+ case '-': goto yy16;
+ case '.': goto yy19;
+ case '0': goto yy17;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy18;
+ case '<': goto yy21;
+ case '=': goto yy20;
+ case 'F': goto yy14;
+ case 'N': goto yy6;
+ case 'O': goto yy12;
+ case 'T': goto yy8;
+ case 'Y': goto yy10;
+ case 'f': goto yy13;
+ case 'n': goto yy5;
+ case 'o': goto yy11;
+ case 't': goto yy7;
+ case 'y': goto yy9;
+ case '~': goto yy3;
+ default: goto yy22;
+ }
+yy2: YYCURSOR = YYMARKER;
+ switch(yyaccept){
+ case 0: goto yy4;
+ }
+yy3: yych = *++YYCURSOR;
+ if(yych <= '\000') goto yy188;
+yy4:
+#line 116
+ { return "str"; }
+yy5: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case 'o': goto yy161;
+ case 'u': goto yy191;
+ default: goto yy4;
+ }
+yy6: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case 'O': case 'o': goto yy161;
+ case 'U': goto yy184;
+ case 'u': goto yy185;
+ default: goto yy4;
+ }
+yy7: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case 'r': goto yy182;
+ default: goto yy4;
+ }
+yy8: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case 'R': goto yy178;
+ case 'r': goto yy179;
+ default: goto yy4;
+ }
+yy9: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case 'e': goto yy177;
+ default: goto yy4;
+ }
+yy10: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case 'E': goto yy175;
+ case 'e': goto yy176;
+ default: goto yy4;
+ }
+yy11: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case 'f': goto yy174;
+ case 'n': goto yy171;
+ default: goto yy4;
+ }
+yy12: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case 'F': goto yy169;
+ case 'N': case 'n': goto yy171;
+ case 'f': goto yy170;
+ default: goto yy4;
+ }
+yy13: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case 'a': goto yy166;
+ default: goto yy4;
+ }
+yy14: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case 'A': goto yy157;
+ case 'a': goto yy158;
+ default: goto yy4;
+ }
+yy15: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case '.': goto yy156;
+ case '0': goto yy147;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy45;
+ default: goto yy4;
+ }
+yy16: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case '.': goto yy146;
+ case '0': goto yy147;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy45;
+ default: goto yy4;
+ }
+yy17: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case '\000': goto yy49;
+ case ',': goto yy131;
+ case '.': goto yy47;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7': goto yy129;
+ case '8':
+ case '9': goto yy130;
+ case 'x': goto yy133;
+ default: goto yy4;
+ }
+yy18: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case '\000': goto yy49;
+ case ',': goto yy45;
+ case '.': goto yy47;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy44;
+ default: goto yy4;
+ }
+yy19: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case 'I': goto yy31;
+ case 'N': goto yy29;
+ case 'i': goto yy30;
+ case 'n': goto yy28;
+ default: goto yy4;
+ }
+yy20: yych = *++YYCURSOR;
+ if(yych <= '\000') goto yy26;
+ goto yy4;
+yy21: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case '<': goto yy23;
+ default: goto yy4;
+ }
+yy22: yych = *++YYCURSOR;
+ goto yy4;
+yy23: yych = *++YYCURSOR;
+ if(yych >= '\001') goto yy2;
+yy24: yych = *++YYCURSOR;
+yy25:
+#line 114
+ { return "merge"; }
+yy26: yych = *++YYCURSOR;
+yy27:
+#line 112
+ { return "default"; }
+yy28: yych = *++YYCURSOR;
+ switch(yych){
+ case 'a': goto yy43;
+ default: goto yy2;
+ }
+yy29: yych = *++YYCURSOR;
+ switch(yych){
+ case 'A': goto yy38;
+ case 'a': goto yy39;
+ default: goto yy2;
+ }
+yy30: yych = *++YYCURSOR;
+ switch(yych){
+ case 'n': goto yy37;
+ default: goto yy2;
+ }
+yy31: yych = *++YYCURSOR;
+ switch(yych){
+ case 'N': goto yy32;
+ case 'n': goto yy33;
+ default: goto yy2;
+ }
+yy32: yych = *++YYCURSOR;
+ switch(yych){
+ case 'F': goto yy34;
+ default: goto yy2;
+ }
+yy33: yych = *++YYCURSOR;
+ switch(yych){
+ case 'f': goto yy34;
+ default: goto yy2;
+ }
+yy34: yych = *++YYCURSOR;
+ if(yych >= '\001') goto yy2;
+yy35: yych = *++YYCURSOR;
+yy36:
+#line 98
+ { return "float#inf"; }
+yy37: yych = *++YYCURSOR;
+ switch(yych){
+ case 'f': goto yy34;
+ default: goto yy2;
+ }
+yy38: yych = *++YYCURSOR;
+ switch(yych){
+ case 'N': goto yy40;
+ default: goto yy2;
+ }
+yy39: yych = *++YYCURSOR;
+ switch(yych){
+ case 'N': goto yy40;
+ default: goto yy2;
+ }
+yy40: yych = *++YYCURSOR;
+ if(yych >= '\001') goto yy2;
+yy41: yych = *++YYCURSOR;
+yy42:
+#line 102
+ { return "float#nan"; }
+yy43: yych = *++YYCURSOR;
+ switch(yych){
+ case 'n': goto yy40;
+ default: goto yy2;
+ }
+yy44: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy63;
+ default: goto yy46;
+ }
+yy45: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy46: switch(yych){
+ case '\000': goto yy49;
+ case ',': case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy45;
+ case '.': goto yy47;
+ default: goto yy2;
+ }
+yy47: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy48: switch(yych){
+ case '\000': goto yy53;
+ case ',': goto yy51;
+ case '.': goto yy55;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy47;
+ case 'E': case 'e': goto yy57;
+ default: goto yy2;
+ }
+yy49: yych = *++YYCURSOR;
+yy50:
+#line 92
+ { return "int"; }
+yy51: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy52: switch(yych){
+ case '\000': goto yy53;
+ case ',': case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy51;
+ default: goto yy2;
+ }
+yy53: yych = *++YYCURSOR;
+yy54:
+#line 94
+ { return "float#fix"; }
+yy55: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy56: switch(yych){
+ case '.': case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy55;
+ case 'E': case 'e': goto yy57;
+ default: goto yy2;
+ }
+yy57: yych = *++YYCURSOR;
+ switch(yych){
+ case '+': case '-': goto yy58;
+ default: goto yy2;
+ }
+yy58: yych = *++YYCURSOR;
+ if(yych <= '\000') goto yy2;
+ goto yy60;
+yy59: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy60: switch(yych){
+ case '\000': goto yy61;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy59;
+ default: goto yy2;
+ }
+yy61: yych = *++YYCURSOR;
+yy62:
+#line 96
+ { return "float#exp"; }
+yy63: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy64;
+ default: goto yy46;
+ }
+yy64: yych = *++YYCURSOR;
+ switch(yych){
+ case '-': goto yy65;
+ default: goto yy46;
+ }
+yy65: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy66;
+ default: goto yy2;
+ }
+yy66: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy67;
+ default: goto yy2;
+ }
+yy67: yych = *++YYCURSOR;
+ switch(yych){
+ case '-': goto yy68;
+ default: goto yy2;
+ }
+yy68: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy69;
+ default: goto yy2;
+ }
+yy69: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy70;
+ default: goto yy2;
+ }
+yy70: yych = *++YYCURSOR;
+ switch(yych){
+ case '\000': goto yy71;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy2;
+ case 'T': goto yy73;
+ case 't': goto yy74;
+ default: goto yy76;
+ }
+yy71: yych = *++YYCURSOR;
+yy72:
+#line 104
+ { return "timestamp#ymd"; }
+yy73: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy115;
+ default: goto yy2;
+ }
+yy74: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy97;
+ default: goto yy2;
+ }
+yy75: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy76: switch(yych){
+ case '\t': case ' ': goto yy75;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy77;
+ default: goto yy2;
+ }
+yy77: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy78;
+ default: goto yy2;
+ }
+yy78: yych = *++YYCURSOR;
+ switch(yych){
+ case ':': goto yy79;
+ default: goto yy2;
+ }
+yy79: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy80;
+ default: goto yy2;
+ }
+yy80: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy81;
+ default: goto yy2;
+ }
+yy81: yych = *++YYCURSOR;
+ switch(yych){
+ case ':': goto yy82;
+ default: goto yy2;
+ }
+yy82: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy83;
+ default: goto yy2;
+ }
+yy83: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy84;
+ default: goto yy2;
+ }
+yy84: yych = *++YYCURSOR;
+ switch(yych){
+ case '\t': case ' ': goto yy87;
+ case '.': goto yy85;
+ default: goto yy2;
+ }
+yy85: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy86: switch(yych){
+ case '\t': case ' ': goto yy87;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy85;
+ default: goto yy2;
+ }
+yy87: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy88: switch(yych){
+ case '\t': case ' ': goto yy87;
+ case '+': case '-': goto yy90;
+ case 'Z': goto yy89;
+ default: goto yy2;
+ }
+yy89: yych = *++YYCURSOR;
+ if(yych <= '\000') goto yy94;
+ goto yy2;
+yy90: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy91;
+ default: goto yy2;
+ }
+yy91: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy92;
+ default: goto yy2;
+ }
+yy92: yych = *++YYCURSOR;
+ switch(yych){
+ case '\000': goto yy94;
+ case ':': goto yy93;
+ default: goto yy2;
+ }
+yy93: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy96;
+ default: goto yy2;
+ }
+yy94: yych = *++YYCURSOR;
+yy95:
+#line 108
+ { return "timestamp#spaced"; }
+yy96: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy89;
+ default: goto yy2;
+ }
+yy97: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy98;
+ default: goto yy2;
+ }
+yy98: yych = *++YYCURSOR;
+ switch(yych){
+ case ':': goto yy99;
+ default: goto yy2;
+ }
+yy99: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy100;
+ default: goto yy2;
+ }
+yy100: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy101;
+ default: goto yy2;
+ }
+yy101: yych = *++YYCURSOR;
+ switch(yych){
+ case ':': goto yy102;
+ default: goto yy2;
+ }
+yy102: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy103;
+ default: goto yy2;
+ }
+yy103: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy104;
+ default: goto yy2;
+ }
+yy104: yych = *++YYCURSOR;
+ switch(yych){
+ case '.': goto yy105;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy2;
+ default: goto yy106;
+ }
+yy105: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy106: switch(yych){
+ case '+': case '-': goto yy108;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy105;
+ case 'Z': goto yy107;
+ default: goto yy2;
+ }
+yy107: yych = *++YYCURSOR;
+ if(yych <= '\000') goto yy112;
+ goto yy2;
+yy108: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy109;
+ default: goto yy2;
+ }
+yy109: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy110;
+ default: goto yy2;
+ }
+yy110: yych = *++YYCURSOR;
+ switch(yych){
+ case '\000': goto yy112;
+ case ':': goto yy111;
+ default: goto yy2;
+ }
+yy111: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy114;
+ default: goto yy2;
+ }
+yy112: yych = *++YYCURSOR;
+yy113:
+#line 106
+ { return "timestamp#iso8601"; }
+yy114: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy107;
+ default: goto yy2;
+ }
+yy115: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy116;
+ default: goto yy2;
+ }
+yy116: yych = *++YYCURSOR;
+ switch(yych){
+ case ':': goto yy117;
+ default: goto yy2;
+ }
+yy117: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy118;
+ default: goto yy2;
+ }
+yy118: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy119;
+ default: goto yy2;
+ }
+yy119: yych = *++YYCURSOR;
+ switch(yych){
+ case ':': goto yy120;
+ default: goto yy2;
+ }
+yy120: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy121;
+ default: goto yy2;
+ }
+yy121: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy122;
+ default: goto yy2;
+ }
+yy122: yych = *++YYCURSOR;
+ switch(yych){
+ case '.': goto yy123;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy2;
+ case 'Z': goto yy125;
+ default: goto yy124;
+ }
+yy123: ++YYCURSOR;
+ if((YYLIMIT - YYCURSOR) < 7) YYFILL(7);
+ yych = *YYCURSOR;
+yy124: switch(yych){
+ case '+': case '-': goto yy108;
+ case '0': goto yy123;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy127;
+ case 'Z': goto yy107;
+ default: goto yy2;
+ }
+yy125: yych = *++YYCURSOR;
+ if(yych >= '\001') goto yy2;
+yy126: yych = *++YYCURSOR;
+ goto yy113;
+yy127: ++YYCURSOR;
+ if((YYLIMIT - YYCURSOR) < 7) YYFILL(7);
+ yych = *YYCURSOR;
+yy128: switch(yych){
+ case '+': case '-': goto yy108;
+ case '0': goto yy123;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy127;
+ case 'Z': goto yy125;
+ default: goto yy2;
+ }
+yy129: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7': goto yy144;
+ case '8':
+ case '9': goto yy142;
+ default: goto yy132;
+ }
+yy130: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy142;
+ default: goto yy141;
+ }
+yy131: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy132: switch(yych){
+ case '\000': goto yy138;
+ case ',': case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7': goto yy131;
+ case '.': goto yy47;
+ case '8':
+ case '9': goto yy140;
+ default: goto yy2;
+ }
+yy133: yych = *++YYCURSOR;
+ if(yych <= '\000') goto yy2;
+ goto yy135;
+yy134: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy135: switch(yych){
+ case '\000': goto yy136;
+ case ',': case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f': goto yy134;
+ default: goto yy2;
+ }
+yy136: yych = *++YYCURSOR;
+yy137:
+#line 88
+ { return "int#hex"; }
+yy138: yych = *++YYCURSOR;
+yy139:
+#line 90
+ { return "int#oct"; }
+yy140: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy141: switch(yych){
+ case ',': case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy140;
+ case '.': goto yy47;
+ default: goto yy2;
+ }
+yy142: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy143;
+ default: goto yy141;
+ }
+yy143: yych = *++YYCURSOR;
+ switch(yych){
+ case '-': goto yy65;
+ default: goto yy141;
+ }
+yy144: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7': goto yy145;
+ case '8':
+ case '9': goto yy143;
+ default: goto yy132;
+ }
+yy145: yych = *++YYCURSOR;
+ switch(yych){
+ case '-': goto yy65;
+ default: goto yy132;
+ }
+yy146: yych = *++YYCURSOR;
+ switch(yych){
+ case 'I': goto yy149;
+ case 'i': goto yy148;
+ default: goto yy2;
+ }
+yy147: yych = *++YYCURSOR;
+ switch(yych){
+ case '\000': goto yy49;
+ case 'x': goto yy133;
+ default: goto yy132;
+ }
+yy148: yych = *++YYCURSOR;
+ switch(yych){
+ case 'n': goto yy155;
+ default: goto yy2;
+ }
+yy149: yych = *++YYCURSOR;
+ switch(yych){
+ case 'N': goto yy150;
+ case 'n': goto yy151;
+ default: goto yy2;
+ }
+yy150: yych = *++YYCURSOR;
+ switch(yych){
+ case 'F': goto yy152;
+ default: goto yy2;
+ }
+yy151: yych = *++YYCURSOR;
+ switch(yych){
+ case 'f': goto yy152;
+ default: goto yy2;
+ }
+yy152: yych = *++YYCURSOR;
+ if(yych >= '\001') goto yy2;
+yy153: yych = *++YYCURSOR;
+yy154:
+#line 100
+ { return "float#neginf"; }
+yy155: yych = *++YYCURSOR;
+ switch(yych){
+ case 'f': goto yy152;
+ default: goto yy2;
+ }
+yy156: yych = *++YYCURSOR;
+ switch(yych){
+ case 'I': goto yy31;
+ case 'i': goto yy30;
+ default: goto yy2;
+ }
+yy157: yych = *++YYCURSOR;
+ switch(yych){
+ case 'L': goto yy164;
+ default: goto yy2;
+ }
+yy158: yych = *++YYCURSOR;
+ switch(yych){
+ case 'l': goto yy159;
+ default: goto yy2;
+ }
+yy159: yych = *++YYCURSOR;
+ switch(yych){
+ case 's': goto yy160;
+ default: goto yy2;
+ }
+yy160: yych = *++YYCURSOR;
+ switch(yych){
+ case 'e': goto yy161;
+ default: goto yy2;
+ }
+yy161: yych = *++YYCURSOR;
+ if(yych >= '\001') goto yy2;
+yy162: yych = *++YYCURSOR;
+yy163:
+#line 86
+ { return "bool#no"; }
+yy164: yych = *++YYCURSOR;
+ switch(yych){
+ case 'S': goto yy165;
+ default: goto yy2;
+ }
+yy165: yych = *++YYCURSOR;
+ switch(yych){
+ case 'E': goto yy161;
+ default: goto yy2;
+ }
+yy166: yych = *++YYCURSOR;
+ switch(yych){
+ case 'l': goto yy167;
+ default: goto yy2;
+ }
+yy167: yych = *++YYCURSOR;
+ switch(yych){
+ case 's': goto yy168;
+ default: goto yy2;
+ }
+yy168: yych = *++YYCURSOR;
+ switch(yych){
+ case 'e': goto yy161;
+ default: goto yy2;
+ }
+yy169: yych = *++YYCURSOR;
+ switch(yych){
+ case 'F': goto yy161;
+ default: goto yy2;
+ }
+yy170: yych = *++YYCURSOR;
+ switch(yych){
+ case 'f': goto yy161;
+ default: goto yy2;
+ }
+yy171: yych = *++YYCURSOR;
+ if(yych >= '\001') goto yy2;
+yy172: yych = *++YYCURSOR;
+yy173:
+#line 84
+ { return "bool#yes"; }
+yy174: yych = *++YYCURSOR;
+ switch(yych){
+ case 'f': goto yy161;
+ default: goto yy2;
+ }
+yy175: yych = *++YYCURSOR;
+ switch(yych){
+ case 'S': goto yy171;
+ default: goto yy2;
+ }
+yy176: yych = *++YYCURSOR;
+ switch(yych){
+ case 's': goto yy171;
+ default: goto yy2;
+ }
+yy177: yych = *++YYCURSOR;
+ switch(yych){
+ case 's': goto yy171;
+ default: goto yy2;
+ }
+yy178: yych = *++YYCURSOR;
+ switch(yych){
+ case 'U': goto yy181;
+ default: goto yy2;
+ }
+yy179: yych = *++YYCURSOR;
+ switch(yych){
+ case 'u': goto yy180;
+ default: goto yy2;
+ }
+yy180: yych = *++YYCURSOR;
+ switch(yych){
+ case 'e': goto yy171;
+ default: goto yy2;
+ }
+yy181: yych = *++YYCURSOR;
+ switch(yych){
+ case 'E': goto yy171;
+ default: goto yy2;
+ }
+yy182: yych = *++YYCURSOR;
+ switch(yych){
+ case 'u': goto yy183;
+ default: goto yy2;
+ }
+yy183: yych = *++YYCURSOR;
+ switch(yych){
+ case 'e': goto yy171;
+ default: goto yy2;
+ }
+yy184: yych = *++YYCURSOR;
+ switch(yych){
+ case 'L': goto yy190;
+ default: goto yy2;
+ }
+yy185: yych = *++YYCURSOR;
+ switch(yych){
+ case 'l': goto yy186;
+ default: goto yy2;
+ }
+yy186: yych = *++YYCURSOR;
+ switch(yych){
+ case 'l': goto yy187;
+ default: goto yy2;
+ }
+yy187: yych = *++YYCURSOR;
+ if(yych >= '\001') goto yy2;
+yy188: yych = *++YYCURSOR;
+yy189:
+#line 82
+ { return "null"; }
+yy190: yych = *++YYCURSOR;
+ switch(yych){
+ case 'L': goto yy187;
+ default: goto yy2;
+ }
+yy191: yych = *++YYCURSOR;
+ switch(yych){
+ case 'l': goto yy192;
+ default: goto yy2;
+ }
+yy192: yych = *++YYCURSOR;
+ switch(yych){
+ case 'l': goto yy187;
+ default: goto yy2;
+ }
+}
+#line 118
+
+
+}
+
+char *
+syck_type_id_to_uri( char *type_id )
+{
+ char *cursor, *limit, *marker;
+
+ cursor = type_id;
+ limit = type_id + strlen( type_id );
+
+{
+ YYCTYPE yych;
+ unsigned int yyaccept;
+ goto yy193;
+yy194: ++YYCURSOR;
+yy193:
+ if((YYLIMIT - YYCURSOR) < 20) YYFILL(20);
+ yych = *YYCURSOR;
+ switch(yych){
+ case '\000': goto yy195;
+ case '!': goto yy199;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z': case '_': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's': case 'u':
+ case 'v':
+ case 'w': case 'y':
+ case 'z': goto yy201;
+ case 't': goto yy196;
+ case 'x': goto yy198;
+ default: goto yy202;
+ }
+yy195: YYCURSOR = YYMARKER;
+ switch(yyaccept){
+ case 0: goto yy197;
+ }
+yy196: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case ',': goto yy207;
+ case '-': goto yy203;
+ case '.': goto yy208;
+ case '/': goto yy209;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z': case '_': case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z': goto yy205;
+ case 'a': goto yy236;
+ default: goto yy197;
+ }
+yy197:
+#line 170
+ { return syck_taguri( YAML_DOMAIN, type_id, strlen( type_id ) ); }
+yy198: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case ',': goto yy207;
+ case '-': goto yy203;
+ case '.': goto yy208;
+ case '/': goto yy209;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z': case '_': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o': case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z': goto yy205;
+ case 'p': goto yy227;
+ default: goto yy197;
+ }
+yy199: yych = *++YYCURSOR;
+yy200:
+#line 144
+ { return syck_xprivate( type_id + 1, strlen( type_id ) - 1 ); }
+yy201: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case ',': goto yy207;
+ case '-': goto yy203;
+ case '.': goto yy208;
+ case '/': goto yy209;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z': case '_': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z': goto yy205;
+ default: goto yy197;
+ }
+yy202: yych = *++YYCURSOR;
+ goto yy197;
+yy203: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy204: switch(yych){
+ case '-': goto yy203;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z': case '_': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z': goto yy205;
+ default: goto yy195;
+ }
+yy205: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy206: switch(yych){
+ case ',': goto yy207;
+ case '-': goto yy203;
+ case '.': goto yy208;
+ case '/': goto yy209;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z': case '_': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z': goto yy205;
+ default: goto yy195;
+ }
+yy207: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy215;
+ default: goto yy195;
+ }
+yy208: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z': case '_': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z': goto yy211;
+ default: goto yy195;
+ }
+yy209: yych = *++YYCURSOR;
+yy210:
+#line 146
+ { char *domain = S_ALLOC_N( char, ( YYCURSOR - type_id ) + 15 );
+ char *uri;
+
+ domain[0] = '\0';
+ strncat( domain, type_id, ( YYCURSOR - type_id ) - 1 );
+ strcat( domain, "." );
+ strcat( domain, YAML_DOMAIN );
+ uri = syck_taguri( domain, YYCURSOR, YYLIMIT - YYCURSOR );
+
+ S_FREE( domain );
+ return uri;
+ }
+yy211: ++YYCURSOR;
+ if((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
+ yych = *YYCURSOR;
+yy212: switch(yych){
+ case ',': goto yy207;
+ case '-': goto yy213;
+ case '.': goto yy208;
+ case '/': goto yy209;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z': case '_': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z': goto yy211;
+ default: goto yy195;
+ }
+yy213: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy214: switch(yych){
+ case '-': goto yy213;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z': case '_': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z': goto yy211;
+ default: goto yy195;
+ }
+yy215: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy216;
+ default: goto yy195;
+ }
+yy216: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy217;
+ default: goto yy195;
+ }
+yy217: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy218;
+ default: goto yy195;
+ }
+yy218: yych = *++YYCURSOR;
+ switch(yych){
+ case '-': goto yy219;
+ case '/': goto yy220;
+ default: goto yy195;
+ }
+yy219: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy222;
+ default: goto yy195;
+ }
+yy220: yych = *++YYCURSOR;
+yy221:
+#line 159
+ { char *domain = S_ALLOC_N( char, YYCURSOR - type_id );
+ char *uri;
+
+ domain[0] = '\0';
+ strncat( domain, type_id, ( YYCURSOR - type_id ) - 1 );
+ uri = syck_taguri( domain, YYCURSOR, YYLIMIT - YYCURSOR );
+
+ S_FREE( domain );
+ return uri;
+ }
+yy222: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy223;
+ default: goto yy195;
+ }
+yy223: yych = *++YYCURSOR;
+ switch(yych){
+ case '-': goto yy224;
+ case '/': goto yy220;
+ default: goto yy195;
+ }
+yy224: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy225;
+ default: goto yy195;
+ }
+yy225: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy226;
+ default: goto yy195;
+ }
+yy226: yych = *++YYCURSOR;
+ switch(yych){
+ case '/': goto yy220;
+ default: goto yy195;
+ }
+yy227: yych = *++YYCURSOR;
+ switch(yych){
+ case ',': goto yy207;
+ case '.': goto yy208;
+ case '/': goto yy209;
+ case 'r': goto yy228;
+ default: goto yy204;
+ }
+yy228: yych = *++YYCURSOR;
+ switch(yych){
+ case ',': goto yy207;
+ case '.': goto yy208;
+ case '/': goto yy209;
+ case 'i': goto yy229;
+ default: goto yy204;
+ }
+yy229: yych = *++YYCURSOR;
+ switch(yych){
+ case ',': goto yy207;
+ case '.': goto yy208;
+ case '/': goto yy209;
+ case 'v': goto yy230;
+ default: goto yy204;
+ }
+yy230: yych = *++YYCURSOR;
+ switch(yych){
+ case ',': goto yy207;
+ case '.': goto yy208;
+ case '/': goto yy209;
+ case 'a': goto yy231;
+ default: goto yy204;
+ }
+yy231: yych = *++YYCURSOR;
+ switch(yych){
+ case ',': goto yy207;
+ case '.': goto yy208;
+ case '/': goto yy209;
+ case 't': goto yy232;
+ default: goto yy204;
+ }
+yy232: yych = *++YYCURSOR;
+ switch(yych){
+ case ',': goto yy207;
+ case '.': goto yy208;
+ case '/': goto yy209;
+ case 'e': goto yy233;
+ default: goto yy204;
+ }
+yy233: yych = *++YYCURSOR;
+ switch(yych){
+ case ',': goto yy207;
+ case '.': goto yy208;
+ case '/': goto yy209;
+ case ':': goto yy234;
+ default: goto yy204;
+ }
+yy234: yych = *++YYCURSOR;
+yy235:
+#line 142
+ { return type_id; }
+yy236: yych = *++YYCURSOR;
+ switch(yych){
+ case ',': goto yy207;
+ case '.': goto yy208;
+ case '/': goto yy209;
+ case 'g': goto yy237;
+ default: goto yy204;
+ }
+yy237: yych = *++YYCURSOR;
+ switch(yych){
+ case ',': goto yy207;
+ case '.': goto yy208;
+ case '/': goto yy209;
+ case 'u': goto yy238;
+ default: goto yy204;
+ }
+yy238: yych = *++YYCURSOR;
+ switch(yych){
+ case ',': goto yy207;
+ case '.': goto yy208;
+ case '/': goto yy209;
+ case 'r': goto yy239;
+ default: goto yy204;
+ }
+yy239: yych = *++YYCURSOR;
+ switch(yych){
+ case ',': goto yy207;
+ case '.': goto yy208;
+ case '/': goto yy209;
+ case 'i': goto yy240;
+ default: goto yy204;
+ }
+yy240: yych = *++YYCURSOR;
+ switch(yych){
+ case ',': goto yy207;
+ case '.': goto yy208;
+ case '/': goto yy209;
+ case ':': goto yy241;
+ default: goto yy204;
+ }
+yy241: yych = *++YYCURSOR;
+ switch(yych){
+ case ',':
+ case '-':
+ case '.': goto yy195;
+ default: goto yy243;
+ }
+yy242: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy243: switch(yych){
+ case ',': goto yy246;
+ case '-': goto yy244;
+ case '.': goto yy247;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z': case '_': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z': goto yy242;
+ default: goto yy195;
+ }
+yy244: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy245: switch(yych){
+ case '-': goto yy244;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z': case '_': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z': goto yy242;
+ default: goto yy195;
+ }
+yy246: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy252;
+ default: goto yy195;
+ }
+yy247: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z': case '_': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z': goto yy248;
+ default: goto yy195;
+ }
+yy248: ++YYCURSOR;
+ if((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
+ yych = *YYCURSOR;
+yy249: switch(yych){
+ case ',': goto yy246;
+ case '-': goto yy250;
+ case '.': goto yy247;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z': case '_': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z': goto yy248;
+ default: goto yy195;
+ }
+yy250: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy251: switch(yych){
+ case '-': goto yy250;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z': case '_': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z': goto yy248;
+ default: goto yy195;
+ }
+yy252: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy253;
+ default: goto yy195;
+ }
+yy253: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy254;
+ default: goto yy195;
+ }
+yy254: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy255;
+ default: goto yy195;
+ }
+yy255: yych = *++YYCURSOR;
+ switch(yych){
+ case '-': goto yy256;
+ case ':': goto yy257;
+ default: goto yy195;
+ }
+yy256: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy259;
+ default: goto yy195;
+ }
+yy257: yych = *++YYCURSOR;
+yy258:
+#line 140
+ { return type_id; }
+yy259: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy260;
+ default: goto yy195;
+ }
+yy260: yych = *++YYCURSOR;
+ switch(yych){
+ case '-': goto yy261;
+ case ':': goto yy257;
+ default: goto yy195;
+ }
+yy261: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy262;
+ default: goto yy195;
+ }
+yy262: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy263;
+ default: goto yy195;
+ }
+yy263: yych = *++YYCURSOR;
+ switch(yych){
+ case ':': goto yy257;
+ default: goto yy195;
+ }
+}
+#line 172
+
+
+}
+
diff --git a/ext/syck/node.c b/ext/syck/node.c
new file mode 100644
index 0000000000..f999700554
--- /dev/null
+++ b/ext/syck/node.c
@@ -0,0 +1,324 @@
+/*
+ * node.c
+ *
+ * $Author$
+ * $Date$
+ *
+ * Copyright (C) 2003 why the lucky stiff
+ */
+
+#include "syck.h"
+#include "ruby.h"
+
+/*
+ * Node allocation functions
+ */
+SyckNode *
+syck_alloc_node( enum syck_kind_tag type )
+{
+ SyckNode *s;
+
+ s = S_ALLOC( SyckNode );
+ s->kind = type;
+ s->id = 0;
+ s->type_id = NULL;
+ s->anchor = NULL;
+ s->shortcut = NULL;
+
+ return s;
+}
+
+void
+syck_free_node( SyckNode *n )
+{
+ syck_free_members( n );
+ if ( n->type_id != NULL )
+ S_FREE( n->type_id );
+ if ( n->anchor != NULL )
+ S_FREE( n->anchor );
+ S_FREE( n );
+}
+
+SyckNode *
+syck_alloc_map()
+{
+ SyckNode *n;
+ struct SyckMap *m;
+
+ m = S_ALLOC( struct SyckMap );
+ m->idx = 0;
+ m->capa = ALLOC_CT;
+ m->keys = S_ALLOC_N( SYMID, m->capa );
+ m->values = S_ALLOC_N( SYMID, m->capa );
+
+ n = syck_alloc_node( syck_map_kind );
+ n->data.pairs = m;
+
+ return n;
+}
+
+SyckNode *
+syck_alloc_seq()
+{
+ SyckNode *n;
+ struct SyckSeq *s;
+
+ s = S_ALLOC( struct SyckSeq );
+ s->idx = 0;
+ s->capa = ALLOC_CT;
+ s->items = S_ALLOC_N( SYMID, s->capa );
+
+ n = syck_alloc_node( syck_seq_kind );
+ n->data.list = s;
+
+ return n;
+}
+
+SyckNode *
+syck_alloc_str()
+{
+ SyckNode *n;
+ struct SyckStr *s;
+
+ s = S_ALLOC( struct SyckStr );
+ s->len = 0;
+ s->ptr = NULL;
+
+ n = syck_alloc_node( syck_str_kind );
+ n->data.str = s;
+
+ return n;
+}
+
+SyckNode *
+syck_new_str( char *str )
+{
+ return syck_new_str2( str, strlen( str ) );
+}
+
+SyckNode *
+syck_new_str2( char *str, long len )
+{
+ SyckNode *n;
+
+ n = syck_alloc_str();
+ n->data.str->ptr = S_ALLOC_N( char, len + 1 );
+ n->data.str->len = len;
+ memcpy( n->data.str->ptr, str, len );
+ n->data.str->ptr[len] = '\0';
+
+ return n;
+}
+
+void
+syck_str_blow_away_commas( SyckNode *n )
+{
+ char *go, *end;
+
+ go = n->data.str->ptr;
+ end = go + n->data.str->len;
+ while ( *(++go) != '\0' )
+ {
+ if ( *go == ',' )
+ {
+ n->data.str->len -= 1;
+ memmove( go, go + 1, end - go );
+ end -= 1;
+ }
+ }
+}
+
+char *
+syck_str_read( SyckNode *n )
+{
+ ASSERT( n != NULL );
+ return n->data.str->ptr;
+}
+
+SyckNode *
+syck_new_map( SYMID key, SYMID value )
+{
+ SyckNode *n;
+
+ n = syck_alloc_map();
+ syck_map_add( n, key, value );
+
+ return n;
+}
+
+void
+syck_map_add( SyckNode *map, SYMID key, SYMID value )
+{
+ struct SyckMap *m;
+ long idx;
+
+ ASSERT( map != NULL );
+ ASSERT( map->data.pairs != NULL );
+
+ m = map->data.pairs;
+ idx = m->idx;
+ m->idx += 1;
+ if ( m->idx > m->capa )
+ {
+ m->capa += ALLOC_CT;
+ S_REALLOC_N( m->keys, SYMID, m->capa );
+ S_REALLOC_N( m->values, SYMID, m->capa );
+ }
+ m->keys[idx] = key;
+ m->values[idx] = value;
+}
+
+void
+syck_map_update( SyckNode *map1, SyckNode *map2 )
+{
+ struct SyckMap *m1, *m2;
+ long new_idx, new_capa;
+ ASSERT( map1 != NULL );
+ ASSERT( map2 != NULL );
+
+ m1 = map1->data.pairs;
+ m2 = map2->data.pairs;
+ if ( m2->idx < 1 ) return;
+
+ new_idx = m1->idx;
+ new_idx += m2->idx;
+ new_capa = m1->capa;
+ while ( new_idx > new_capa )
+ {
+ new_capa += ALLOC_CT;
+ }
+ if ( new_capa > m1->capa )
+ {
+ m1->capa = new_capa;
+ S_REALLOC_N( m1->keys, SYMID, m1->capa );
+ S_REALLOC_N( m1->values, SYMID, m1->capa );
+ }
+ for ( new_idx = 0; new_idx < m2->idx; m1->idx++, new_idx++ )
+ {
+ m1->keys[m1->idx] = m2->keys[new_idx];
+ m1->values[m1->idx] = m2->values[new_idx];
+ }
+}
+
+long
+syck_map_count( SyckNode *map )
+{
+ ASSERT( map != NULL );
+ ASSERT( map->data.pairs != NULL );
+ return map->data.pairs->idx;
+}
+
+void
+syck_map_assign( SyckNode *map, enum map_part p, long idx, SYMID id )
+{
+ struct SyckMap *m;
+
+ ASSERT( map != NULL );
+ m = map->data.pairs;
+ ASSERT( m != NULL );
+ if ( p == map_key )
+ {
+ m->keys[idx] = id;
+ }
+ else
+ {
+ m->values[idx] = id;
+ }
+}
+
+SYMID
+syck_map_read( SyckNode *map, enum map_part p, long idx )
+{
+ struct SyckMap *m;
+
+ ASSERT( map != NULL );
+ m = map->data.pairs;
+ ASSERT( m != NULL );
+ if ( p == map_key )
+ {
+ return m->keys[idx];
+ }
+ else
+ {
+ return m->values[idx];
+ }
+}
+
+SyckNode *
+syck_new_seq( SYMID value )
+{
+ SyckNode *n;
+
+ n = syck_alloc_seq();
+ syck_seq_add( n, value );
+
+ return n;
+}
+
+void
+syck_seq_add( SyckNode *arr, SYMID value )
+{
+ struct SyckSeq *s;
+ long idx;
+
+ ASSERT( arr != NULL );
+ ASSERT( arr->data.list != NULL );
+
+ s = arr->data.list;
+ idx = s->idx;
+ s->idx += 1;
+ if ( s->idx > s->capa )
+ {
+ s->capa += ALLOC_CT;
+ S_REALLOC_N( s->items, SYMID, s->capa );
+ }
+ s->items[idx] = value;
+}
+
+long
+syck_seq_count( SyckNode *seq )
+{
+ ASSERT( seq != NULL );
+ ASSERT( seq->data.list != NULL );
+ return seq->data.list->idx;
+}
+
+SYMID
+syck_seq_read( SyckNode *seq, long idx )
+{
+ struct SyckSeq *s;
+
+ ASSERT( seq != NULL );
+ s = seq->data.list;
+ ASSERT( s != NULL );
+ return s->items[idx];
+}
+
+void
+syck_free_members( SyckNode *n )
+{
+ switch ( n->kind )
+ {
+ case syck_str_kind:
+ if ( n->data.str->ptr != NULL )
+ {
+ S_FREE( n->data.str->ptr );
+ n->data.str->ptr = NULL;
+ n->data.str->len = 0;
+ S_FREE( n->data.str );
+ }
+ break;
+
+ case syck_seq_kind:
+ S_FREE( n->data.list->items );
+ S_FREE( n->data.list );
+ break;
+
+ case syck_map_kind:
+ S_FREE( n->data.pairs->keys );
+ S_FREE( n->data.pairs->values );
+ S_FREE( n->data.pairs );
+ break;
+ }
+}
+
diff --git a/ext/syck/rubyext.c b/ext/syck/rubyext.c
new file mode 100644
index 0000000000..1e6030b4bd
--- /dev/null
+++ b/ext/syck/rubyext.c
@@ -0,0 +1,1288 @@
+/*
+ * rubyext.c
+ *
+ * $Author$
+ * $Date$
+ *
+ * Copyright (C) 2003 why the lucky stiff
+ */
+
+#include "ruby.h"
+#include "syck.h"
+#include <sys/types.h>
+#include <time.h>
+
+typedef struct RVALUE {
+ union {
+#if 0
+ struct {
+ unsigned long flags; /* always 0 for freed obj */
+ struct RVALUE *next;
+ } free;
+#endif
+ struct RBasic basic;
+ struct RObject object;
+ struct RClass klass;
+ /*struct RFloat flonum;*/
+ /*struct RString string;*/
+ struct RArray array;
+ /*struct RRegexp regexp;*/
+ struct RHash hash;
+ /*struct RData data;*/
+ struct RStruct rstruct;
+ /*struct RBignum bignum;*/
+ /*struct RFile file;*/
+ } as;
+} RVALUE;
+
+#define RUBY_DOMAIN "ruby.yaml.org,2002"
+
+/*
+ * symbols and constants
+ */
+static ID s_new, s_utc, s_at, s_to_f, s_read, s_binmode, s_call, s_transfer, s_update, s_dup, s_match;
+static VALUE sym_model, sym_generic;
+static VALUE sym_scalar, sym_seq, sym_map;
+VALUE cDate, cParser, cLoader, cNode, cPrivateType, cDomainType, cBadAlias, cMergeKey, cEmitter;
+VALUE oDefaultLoader;
+
+/*
+ * my private collection of numerical oddities.
+ */
+static double S_zero() { return 0.0; }
+static double S_one() { return 1.0; }
+static double S_inf() { return S_one() / S_zero(); }
+static double S_nan() { return S_zero() / S_zero(); }
+
+static VALUE syck_node_transform( VALUE );
+
+/*
+ * handler prototypes
+ */
+SYMID rb_syck_parse_handler _((SyckParser *, SyckNode *));
+SYMID rb_syck_load_handler _((SyckParser *, SyckNode *));
+void rb_syck_err_handler _((SyckParser *, char *));
+SyckNode * rb_syck_bad_anchor_handler _((SyckParser *, char *));
+void rb_syck_output_handler _((SyckEmitter *, char *, long));
+
+struct parser_xtra {
+ VALUE data; /* Borrowed this idea from marshal.c to fix [ruby-core:8067] problem */
+ VALUE proc;
+};
+
+/*
+ * read from io.
+ */
+long
+rb_syck_io_str_read( char *buf, SyckIoStr *str, long max_size, long skip )
+{
+ long len = 0;
+
+ ASSERT( str != NULL );
+ max_size -= skip;
+ if ( max_size < 0 ) max_size = 0;
+
+ if ( max_size > 0 )
+ {
+ /*
+ * call io#read.
+ */
+ VALUE src = (VALUE)str->ptr;
+ VALUE n = LONG2NUM(max_size);
+ VALUE str = rb_funcall2(src, s_read, 1, &n);
+ if (!NIL_P(str))
+ {
+ len = RSTRING(str)->len;
+ memcpy( buf + skip, RSTRING(str)->ptr, len );
+ }
+ }
+ len += skip;
+ buf[len] = '\0';
+ return len;
+}
+
+/*
+ * determine: are we reading from a string or io?
+ */
+void
+syck_parser_assign_io(parser, port)
+ SyckParser *parser;
+ VALUE port;
+{
+ if (rb_respond_to(port, rb_intern("to_str"))) {
+#if 0
+ arg.taint = OBJ_TAINTED(port); /* original taintedness */
+ StringValue(port); /* possible conversion */
+#endif
+ syck_parser_str( parser, RSTRING(port)->ptr, RSTRING(port)->len, NULL );
+ }
+ else if (rb_respond_to(port, s_read)) {
+ if (rb_respond_to(port, s_binmode)) {
+ rb_funcall2(port, s_binmode, 0, 0);
+ }
+#if 0
+ arg.taint = Qfalse;
+#endif
+ syck_parser_str( parser, (char *)port, 0, rb_syck_io_str_read );
+ }
+ else {
+ rb_raise(rb_eTypeError, "instance of IO needed");
+ }
+}
+
+/*
+ * Get value in hash by key, forcing an empty hash if nil.
+ */
+VALUE
+syck_get_hash_aref(hsh, key)
+ VALUE hsh, key;
+{
+ VALUE val = rb_hash_aref( hsh, key );
+ if ( NIL_P( val ) )
+ {
+ val = rb_hash_new();
+ rb_hash_aset(hsh, key, val);
+ }
+ return val;
+}
+
+/*
+ * creating timestamps
+ */
+SYMID
+rb_syck_mktime(str)
+ char *str;
+{
+ VALUE time;
+ char *ptr = str;
+ VALUE year, mon, day, hour, min, sec, usec;
+
+ /* Year*/
+ ptr[4] = '\0';
+ year = INT2FIX(strtol(ptr, NULL, 10));
+
+ /* Month*/
+ ptr += 4;
+ while ( !isdigit( *ptr ) ) ptr++;
+ mon = INT2FIX(strtol(ptr, NULL, 10));
+
+ /* Day*/
+ ptr += 2;
+ while ( !isdigit( *ptr ) ) ptr++;
+ day = INT2FIX(strtol(ptr, NULL, 10));
+
+ /* Hour*/
+ ptr += 2;
+ while ( !isdigit( *ptr ) ) ptr++;
+ hour = INT2FIX(strtol(ptr, NULL, 10));
+
+ /* Minute */
+ ptr += 2;
+ while ( !isdigit( *ptr ) ) ptr++;
+ min = INT2FIX(strtol(ptr, NULL, 10));
+
+ /* Second */
+ ptr += 2;
+ while ( !isdigit( *ptr ) ) ptr++;
+ sec = INT2FIX(strtol(ptr, NULL, 10));
+
+ /* Millisecond */
+ ptr += 2;
+ if ( *ptr == '.' )
+ {
+ usec = INT2FIX( strtod( ptr, NULL ) * 1000000 );
+ }
+ else
+ {
+ usec = INT2FIX( 0 );
+ }
+
+ /* Make UTC time*/
+ time = rb_funcall(rb_cTime, s_utc, 7, year, mon, day, hour, min, sec, usec);
+
+ /* Time Zone*/
+ while ( *ptr != 'Z' && *ptr != '+' && *ptr != '-' && *ptr != '\0' ) ptr++;
+ if ( *ptr == '-' || *ptr == '+' )
+ {
+ double tz_offset = 0;
+ double utc_time = 0;
+ tz_offset += strtod(ptr, NULL) * 3600;
+
+ while ( *ptr != ':' && *ptr != '\0' ) ptr++;
+ if ( *ptr == ':' )
+ {
+ ptr += 1;
+ if ( tz_offset < 0 )
+ {
+ tz_offset -= strtod(ptr, NULL) * 60;
+ }
+ else
+ {
+ tz_offset += strtod(ptr, NULL) * 60;
+ }
+ }
+
+ /* Make TZ time*/
+ utc_time = NUM2DBL(rb_funcall(time, s_to_f, 0));
+ utc_time -= tz_offset;
+ time = rb_funcall(rb_cTime, s_at, 1, rb_float_new(utc_time));
+ }
+
+ return time;
+}
+
+/*
+ * {generic mode} node handler
+ * - Loads data into Node classes
+ */
+SYMID
+rb_syck_parse_handler(p, n)
+ SyckParser *p;
+ SyckNode *n;
+{
+ VALUE t, obj, v = Qnil;
+ int i;
+ struct parser_xtra *bonus;
+
+ obj = rb_obj_alloc(cNode);
+ if ( n->type_id != NULL )
+ {
+ t = rb_str_new2(n->type_id);
+ rb_iv_set(obj, "@type_id", t);
+ }
+
+ switch (n->kind)
+ {
+ case syck_str_kind:
+ rb_iv_set(obj, "@kind", sym_scalar);
+ v = rb_str_new( n->data.str->ptr, n->data.str->len );
+ break;
+
+ case syck_seq_kind:
+ rb_iv_set(obj, "@kind", sym_seq);
+ v = rb_ary_new2( n->data.list->idx );
+ for ( i = 0; i < n->data.list->idx; i++ )
+ {
+ rb_ary_store( v, i, syck_seq_read( n, i ) );
+ }
+ break;
+
+ case syck_map_kind:
+ rb_iv_set(obj, "@kind", sym_map);
+ v = rb_hash_new();
+ for ( i = 0; i < n->data.pairs->idx; i++ )
+ {
+ VALUE key = syck_node_transform( syck_map_read( n, map_key, i ) );
+ VALUE val = rb_ary_new();
+ rb_ary_push(val, syck_map_read( n, map_key, i ));
+ rb_ary_push(val, syck_map_read( n, map_value, i ));
+
+ rb_hash_aset( v, key, val );
+ }
+ break;
+ }
+
+ bonus = (struct parser_xtra *)p->bonus;
+ if ( bonus->proc != 0 )
+ {
+ rb_funcall(bonus->proc, s_call, 1, v);
+ }
+
+ rb_iv_set(obj, "@value", v);
+ rb_hash_aset(bonus->data, INT2FIX(RHASH(bonus->data)->tbl->num_entries), obj);
+ return obj;
+}
+
+/*
+ * handles merging of an array of hashes
+ * (see http://www.yaml.org/type/merge/)
+ */
+VALUE
+syck_merge_i( entry, hsh )
+ VALUE entry, hsh;
+{
+ if ( rb_obj_is_kind_of( entry, rb_cHash ) )
+ {
+ rb_funcall( hsh, s_update, 1, entry );
+ }
+ return Qnil;
+}
+
+/*
+ * {native mode} node handler
+ * - Converts data into native Ruby types
+ */
+SYMID
+rb_syck_load_handler(p, n)
+ SyckParser *p;
+ SyckNode *n;
+{
+ VALUE obj = Qnil;
+ long i;
+ int check_transfers = 0;
+ struct parser_xtra *bonus;
+
+ switch (n->kind)
+ {
+ case syck_str_kind:
+ if ( n->type_id == NULL || strcmp( n->type_id, "str" ) == 0 )
+ {
+ obj = rb_str_new( n->data.str->ptr, n->data.str->len );
+ }
+ else if ( strcmp( n->type_id, "null" ) == 0 )
+ {
+ obj = Qnil;
+ }
+ else if ( strcmp( n->type_id, "bool#yes" ) == 0 )
+ {
+ obj = Qtrue;
+ }
+ else if ( strcmp( n->type_id, "bool#no" ) == 0 )
+ {
+ obj = Qfalse;
+ }
+ else if ( strcmp( n->type_id, "int#hex" ) == 0 )
+ {
+ obj = rb_cstr2inum( n->data.str->ptr, 16 );
+ }
+ else if ( strcmp( n->type_id, "int#oct" ) == 0 )
+ {
+ obj = rb_cstr2inum( n->data.str->ptr, 8 );
+ }
+ else if ( strncmp( n->type_id, "int", 3 ) == 0 )
+ {
+ syck_str_blow_away_commas( n );
+ obj = rb_cstr2inum( n->data.str->ptr, 10 );
+ }
+ else if ( strcmp( n->type_id, "float#nan" ) == 0 )
+ {
+ obj = rb_float_new( S_nan() );
+ }
+ else if ( strcmp( n->type_id, "float#inf" ) == 0 )
+ {
+ obj = rb_float_new( S_inf() );
+ }
+ else if ( strcmp( n->type_id, "float#neginf" ) == 0 )
+ {
+ obj = rb_float_new( -S_inf() );
+ }
+ else if ( strncmp( n->type_id, "float", 5 ) == 0 )
+ {
+ double f;
+ syck_str_blow_away_commas( n );
+ f = strtod( n->data.str->ptr, NULL );
+ obj = rb_float_new( f );
+ }
+ else if ( strcmp( n->type_id, "timestamp#iso8601" ) == 0 )
+ {
+ obj = rb_syck_mktime( n->data.str->ptr );
+ }
+ else if ( strcmp( n->type_id, "timestamp#spaced" ) == 0 )
+ {
+ obj = rb_syck_mktime( n->data.str->ptr );
+ }
+ else if ( strcmp( n->type_id, "timestamp#ymd" ) == 0 )
+ {
+ char *ptr = n->data.str->ptr;
+ VALUE year, mon, day;
+
+ /* Year*/
+ ptr[4] = '\0';
+ year = INT2FIX(strtol(ptr, NULL, 10));
+
+ /* Month*/
+ ptr += 4;
+ while ( !isdigit( *ptr ) ) ptr++;
+ mon = INT2FIX(strtol(ptr, NULL, 10));
+
+ /* Day*/
+ ptr += 2;
+ while ( !isdigit( *ptr ) ) ptr++;
+ day = INT2FIX(strtol(ptr, NULL, 10));
+
+ obj = rb_funcall( cDate, s_new, 3, year, mon, day );
+ }
+ else if ( strncmp( n->type_id, "timestamp", 9 ) == 0 )
+ {
+ obj = rb_syck_mktime( n->data.str->ptr );
+ }
+ else if ( strncmp( n->type_id, "merge", 5 ) == 0 )
+ {
+ obj = rb_funcall( cMergeKey, s_new, 0 );
+ }
+ else
+ {
+ check_transfers = 1;
+ obj = rb_str_new( n->data.str->ptr, n->data.str->len );
+ }
+ break;
+
+ case syck_seq_kind:
+ obj = rb_ary_new2( n->data.list->idx );
+ for ( i = 0; i < n->data.list->idx; i++ )
+ {
+ rb_ary_store( obj, i, syck_seq_read( n, i ) );
+ }
+ check_transfers = 1;
+ break;
+
+ case syck_map_kind:
+ obj = rb_hash_new();
+ for ( i = 0; i < n->data.pairs->idx; i++ )
+ {
+ VALUE k = syck_map_read( n, map_key, i );
+ VALUE v = syck_map_read( n, map_value, i );
+ int merge_key = 0;
+
+ /*
+ * Handle merge keys
+ */
+ if ( rb_obj_is_kind_of( k, cMergeKey ) )
+ {
+ if ( rb_obj_is_kind_of( v, rb_cHash ) )
+ {
+ VALUE dup = rb_funcall( v, s_dup, 0 );
+ rb_funcall( dup, s_update, 1, obj );
+ obj = dup;
+ merge_key = 1;
+ }
+ else if ( rb_obj_is_kind_of( v, rb_cArray ) )
+ {
+ VALUE end = rb_ary_pop( v );
+ if ( rb_obj_is_kind_of( end, rb_cHash ) )
+ {
+ VALUE dup = rb_funcall( end, s_dup, 0 );
+ v = rb_ary_reverse( v );
+ rb_ary_push( v, obj );
+ rb_iterate( rb_each, v, syck_merge_i, dup );
+ obj = dup;
+ merge_key = 1;
+ }
+ }
+ }
+
+ if ( ! merge_key )
+ {
+ rb_hash_aset( obj, k, v );
+ }
+ }
+ check_transfers = 1;
+ break;
+ }
+
+ /*
+ * ID already set, let's alter the symbol table to accept the new object
+ */
+ if (n->id > 0)
+ {
+ MEMCPY((void *)n->id, (void *)obj, RVALUE, 1);
+ MEMZERO((void *)obj, RVALUE, 1);
+ obj = n->id;
+ }
+
+ bonus = (struct parser_xtra *)p->bonus;
+ if ( bonus->proc != 0 )
+ {
+ rb_funcall(bonus->proc, s_call, 1, obj);
+ }
+
+ if ( check_transfers == 1 && n->type_id != NULL )
+ {
+ obj = rb_funcall( oDefaultLoader, s_transfer, 2, rb_str_new2( n->type_id ), obj );
+ }
+
+ rb_hash_aset(bonus->data, INT2FIX(RHASH(bonus->data)->tbl->num_entries), obj);
+ return obj;
+}
+
+/*
+ * friendly errors.
+ */
+void
+rb_syck_err_handler(p, msg)
+ SyckParser *p;
+ char *msg;
+{
+ char *endl = p->cursor;
+
+ while ( *endl != '\0' && *endl != '\n' )
+ endl++;
+
+ endl[0] = '\0';
+ rb_raise(rb_eArgError, "%s on line %d, col %d: `%s'",
+ msg,
+ p->linect,
+ p->cursor - p->lineptr,
+ p->lineptr);
+}
+
+/*
+ * provide bad anchor object to the parser.
+ */
+SyckNode *
+rb_syck_bad_anchor_handler(p, a)
+ SyckParser *p;
+ char *a;
+{
+ SyckNode *badanc = syck_new_map( rb_str_new2( "name" ), rb_str_new2( a ) );
+ badanc->type_id = syck_strndup( "taguri:ruby.yaml.org,2002:object:YAML::Syck::BadAlias", 53 );
+ return badanc;
+}
+
+/*
+ * data loaded based on the model requested.
+ */
+void
+syck_set_model( parser, model )
+ SyckParser *parser;
+ VALUE model;
+{
+ if ( model == sym_generic )
+ {
+ syck_parser_handler( parser, rb_syck_parse_handler );
+ syck_parser_implicit_typing( parser, 1 );
+ syck_parser_taguri_expansion( parser, 1 );
+ }
+ else
+ {
+ syck_parser_handler( parser, rb_syck_load_handler );
+ syck_parser_implicit_typing( parser, 1 );
+ syck_parser_taguri_expansion( parser, 0 );
+ }
+ syck_parser_error_handler( parser, rb_syck_err_handler );
+ syck_parser_bad_anchor_handler( parser, rb_syck_bad_anchor_handler );
+}
+
+/*
+ * mark parser nodes
+ */
+static void
+syck_mark_parser(parser)
+ SyckParser *parser;
+{
+ rb_gc_mark(parser->root);
+ rb_gc_mark(parser->root_on_error);
+}
+
+/*
+ * YAML::Syck::Parser.new
+ */
+VALUE
+syck_parser_new(argc, argv, class)
+ int argc;
+ VALUE *argv;
+ VALUE class;
+{
+ VALUE pobj, options, init_argv[1];
+ SyckParser *parser = syck_new_parser();
+
+ rb_scan_args(argc, argv, "01", &options);
+ pobj = Data_Wrap_Struct( class, syck_mark_parser, syck_free_parser, parser );
+
+ syck_parser_set_root_on_error( parser, Qnil );
+
+ if ( ! rb_obj_is_instance_of( options, rb_cHash ) )
+ {
+ options = rb_hash_new();
+ }
+ init_argv[0] = options;
+ rb_obj_call_init(pobj, 1, init_argv);
+ return pobj;
+}
+
+/*
+ * YAML::Syck::Parser.initialize( options )
+ */
+static VALUE
+syck_parser_initialize( self, options )
+ VALUE self, options;
+{
+ rb_iv_set(self, "@options", options);
+ return self;
+}
+
+/*
+ * YAML::Syck::Parser.load( IO or String )
+ */
+VALUE
+syck_parser_load(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ VALUE port, proc, model;
+ SyckParser *parser;
+ struct parser_xtra bonus;
+ volatile VALUE hash; /* protect from GC */
+
+ rb_scan_args(argc, argv, "11", &port, &proc);
+ Data_Get_Struct(self, SyckParser, parser);
+ syck_parser_assign_io(parser, port);
+
+ model = rb_hash_aref( rb_iv_get( self, "@options" ), sym_model );
+ syck_set_model( parser, model );
+
+ bonus.data = hash = rb_hash_new();
+ if ( NIL_P( proc ) ) bonus.proc = 0;
+ else bonus.proc = proc;
+
+ parser->bonus = (void *)&bonus;
+
+ return syck_parse( parser );
+}
+
+/*
+ * YAML::Syck::Parser.load_documents( IO or String ) { |doc| }
+ */
+VALUE
+syck_parser_load_documents(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ VALUE port, proc, v, model;
+ SyckParser *parser;
+ struct parser_xtra bonus;
+ volatile VALUE hash;
+
+ rb_scan_args(argc, argv, "1&", &port, &proc);
+ Data_Get_Struct(self, SyckParser, parser);
+ syck_parser_assign_io(parser, port);
+
+ model = rb_hash_aref( rb_iv_get( self, "@options" ), sym_model );
+ syck_set_model( parser, model );
+
+ while ( 1 )
+ {
+ /* Reset hash for tracking nodes */
+ bonus.data = hash = rb_hash_new();
+ bonus.proc = 0;
+ parser->bonus = (void *)&bonus;
+
+ /* Parse a document */
+ v = syck_parse( parser );
+ if ( parser->eof == 1 )
+ {
+ break;
+ }
+
+ /* Pass document to block */
+ rb_funcall( proc, s_call, 1, v );
+ }
+
+ return Qnil;
+}
+
+/*
+ * YAML::Syck::Loader.initialize
+ */
+static VALUE
+syck_loader_initialize( self )
+ VALUE self;
+{
+ VALUE families;
+
+ rb_iv_set(self, "@families", rb_hash_new() );
+ rb_iv_set(self, "@private_types", rb_hash_new() );
+ families = rb_iv_get(self, "@families");
+
+ rb_hash_aset(families, rb_str_new2( YAML_DOMAIN ), rb_hash_new());
+ rb_hash_aset(families, rb_str_new2( RUBY_DOMAIN ), rb_hash_new());
+
+ return self;
+}
+
+/*
+ * Add type family, used by add_*_type methods.
+ */
+VALUE
+syck_loader_add_type_family( self, domain, type_re, proc )
+ VALUE self, domain, type_re, proc;
+{
+ VALUE families, domain_types;
+
+ families = rb_iv_get(self, "@families");
+ domain_types = syck_get_hash_aref(families, domain);
+ rb_hash_aset( domain_types, type_re, proc );
+ return Qnil;
+}
+
+/*
+ * YAML::Syck::Loader.add_domain_type
+ */
+VALUE
+syck_loader_add_domain_type( argc, argv, self )
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ VALUE domain, type_re, proc;
+
+ rb_scan_args(argc, argv, "2&", &domain, &type_re, &proc);
+ syck_loader_add_type_family( self, domain, type_re, proc );
+ return Qnil;
+}
+
+
+/*
+ * YAML::Syck::Loader.add_builtin_type
+ */
+VALUE
+syck_loader_add_builtin_type( argc, argv, self )
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ VALUE type_re, proc;
+
+ rb_scan_args(argc, argv, "1&", &type_re, &proc);
+ syck_loader_add_type_family( self, rb_str_new2( YAML_DOMAIN ), type_re, proc );
+ return Qnil;
+}
+
+/*
+ * YAML::Syck::Loader.add_ruby_type
+ */
+VALUE
+syck_loader_add_ruby_type( argc, argv, self )
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ VALUE type_re, proc;
+
+ rb_scan_args(argc, argv, "1&", &type_re, &proc);
+ syck_loader_add_type_family( self, rb_str_new2( RUBY_DOMAIN ), type_re, proc );
+ return Qnil;
+}
+
+/*
+ * YAML::Syck::Loader.add_private_type
+ */
+VALUE
+syck_loader_add_private_type( argc, argv, self )
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ VALUE type_re, proc, priv_types;
+
+ rb_scan_args(argc, argv, "1&", &type_re, &proc);
+
+ priv_types = rb_iv_get(self, "@private_types");
+ rb_hash_aset( priv_types, type_re, proc );
+ return Qnil;
+}
+
+/*
+ * YAML::Syck::Loader#detect
+ */
+VALUE
+syck_loader_detect_implicit( self, val )
+ VALUE self, val;
+{
+ char *type_id;
+
+ if ( TYPE(val) == T_STRING )
+ {
+ type_id = syck_match_implicit( RSTRING(val)->ptr, RSTRING(val)->len );
+ return rb_str_new2( type_id );
+ }
+
+ return rb_str_new2( "" );
+}
+
+/*
+ * iterator to search a type hash for a match.
+ */
+static VALUE
+transfer_find_i(entry, col)
+ VALUE entry, col;
+{
+ VALUE key = rb_ary_entry( entry, 0 );
+ VALUE tid = rb_ary_entry( col, 0 );
+ if ( rb_respond_to( key, s_match ) )
+ {
+ VALUE match = rb_funcall( key, rb_intern("match"), 1, tid );
+ if ( ! NIL_P( match ) )
+ {
+ rb_ary_push( col, rb_ary_entry( entry, 1 ) );
+ rb_iter_break();
+ }
+ }
+ return Qnil;
+}
+
+/*
+ * YAML::Syck::Loader#transfer
+ */
+VALUE
+syck_loader_transfer( self, type, val )
+ VALUE self, type, val;
+{
+ char *taguri = NULL;
+
+#if 0
+ rb_p(rb_str_new2( "-- TYPE --" ));
+ rb_p(type);
+#endif
+ if (NIL_P(type) || !RSTRING(type)->ptr || RSTRING(type)->len == 0)
+ {
+ /*
+ * Empty transfer, detect type
+ */
+ if ( TYPE(val) == T_STRING )
+ {
+ taguri = syck_match_implicit( RSTRING(val)->ptr, RSTRING(val)->len );
+ taguri = syck_taguri( YAML_DOMAIN, taguri, strlen( taguri ) );
+ }
+ }
+ else
+ {
+ taguri = syck_type_id_to_uri( RSTRING(type)->ptr );
+ }
+
+ if ( taguri != NULL )
+ {
+ VALUE scheme, name, type_hash, domain = Qnil, type_proc = Qnil;
+ VALUE type_uri = rb_str_new2( taguri );
+ VALUE str_taguri = rb_str_new2("taguri");
+ VALUE str_xprivate = rb_str_new2("x-private");
+ VALUE parts = rb_str_split( type_uri, ":" );
+#if 0
+ rb_p(parts);
+#endif
+
+ scheme = rb_ary_shift( parts );
+
+ if ( rb_str_cmp( scheme, str_xprivate ) == 0 )
+ {
+ name = rb_ary_join( parts, rb_str_new2( ":" ) );
+ type_hash = rb_iv_get(self, "@private_types");
+ }
+ else if ( rb_str_cmp( scheme, str_taguri ) == 0 )
+ {
+ domain = rb_ary_shift( parts );
+ name = rb_ary_join( parts, rb_str_new2( ":" ) );
+ type_hash = rb_iv_get(self, "@families");
+ type_hash = rb_hash_aref(type_hash, domain);
+ }
+ else
+ {
+ rb_raise(rb_eTypeError, "invalid typing scheme: %s given",
+ scheme);
+ }
+
+ if ( rb_obj_is_instance_of( type_hash, rb_cHash ) )
+ {
+ type_proc = rb_hash_aref( type_hash, name );
+ if ( NIL_P( type_proc ) )
+ {
+ VALUE col = rb_ary_new();
+ rb_ary_push( col, name );
+ rb_iterate(rb_each, type_hash, transfer_find_i, col );
+ name = rb_ary_shift( col );
+ type_proc = rb_ary_shift( col );
+ }
+#if 0
+ rb_p(name);
+ rb_p(type_proc);
+#endif
+ }
+
+ if ( rb_respond_to( type_proc, s_call ) )
+ {
+ val = rb_funcall(type_proc, s_call, 2, type_uri, val);
+ }
+ else if ( rb_str_cmp( scheme, str_xprivate ) == 0 )
+ {
+ val = rb_funcall(cPrivateType, s_new, 2, name, val);
+ }
+ else
+ {
+ val = rb_funcall(cDomainType, s_new, 3, domain, name, val);
+ }
+ }
+
+ return val;
+}
+
+/*
+ * YAML::Syck::BadAlias.initialize
+ */
+VALUE
+syck_badalias_initialize( self, val )
+ VALUE self, val;
+{
+ rb_iv_set( self, "@name", val );
+ return self;
+}
+
+/*
+ * YAML::Syck::DomainType.initialize
+ */
+VALUE
+syck_domaintype_initialize( self, domain, type_id, val )
+ VALUE self, type_id, val;
+{
+ rb_iv_set( self, "@domain", domain );
+ rb_iv_set( self, "@type_id", type_id );
+ rb_iv_set( self, "@value", val );
+ return self;
+}
+
+/*
+ * YAML::Syck::PrivateType.initialize
+ */
+VALUE
+syck_privatetype_initialize( self, type_id, val )
+ VALUE self, type_id, val;
+{
+ rb_iv_set( self, "@type_id", type_id );
+ rb_iv_set( self, "@value", val );
+ return self;
+}
+
+/*
+ * YAML::Syck::Node.initialize
+ */
+VALUE
+syck_node_initialize( self, type_id, val )
+ VALUE self, type_id, val;
+{
+ rb_iv_set( self, "@type_id", type_id );
+ rb_iv_set( self, "@value", val );
+ return self;
+}
+
+VALUE
+syck_node_thash( entry, t )
+ VALUE entry, t;
+{
+ VALUE key, val;
+ key = rb_ary_entry( entry, 0 );
+ val = syck_node_transform( rb_ary_entry( rb_ary_entry( entry, 1 ), 1 ) );
+ rb_hash_aset( t, key, val );
+ return Qnil;
+}
+
+VALUE
+syck_node_ahash( entry, t )
+ VALUE entry, t;
+{
+ VALUE val = syck_node_transform( entry );
+ rb_ary_push( t, val );
+ return Qnil;
+}
+
+/*
+ * YAML::Syck::Node.transform
+ */
+VALUE
+syck_node_transform( self )
+ VALUE self;
+{
+ VALUE t = Qnil;
+ VALUE type_id = rb_iv_get( self, "@type_id" );
+ VALUE val = rb_iv_get( self, "@value" );
+ if ( rb_obj_is_instance_of( val, rb_cHash ) )
+ {
+ t = rb_hash_new();
+ rb_iterate( rb_each, val, syck_node_thash, t );
+ }
+ else if ( rb_obj_is_instance_of( val, rb_cArray ) )
+ {
+ t = rb_ary_new();
+ rb_iterate( rb_each, val, syck_node_ahash, t );
+ }
+ else
+ {
+ t = val;
+ }
+ return rb_funcall( oDefaultLoader, s_transfer, 2, type_id, t );
+}
+
+/*
+ * Handle output from the emitter
+ */
+void
+rb_syck_output_handler( emitter, str, len )
+ SyckEmitter *emitter;
+ char *str;
+ long len;
+{
+ VALUE dest = (VALUE)emitter->bonus;
+ if ( rb_respond_to( dest, rb_intern("to_str") ) ) {
+ rb_str_cat( dest, str, len );
+ } else {
+ rb_io_write( dest, rb_str_new( str, len ) );
+ }
+}
+
+/*
+ * Mark emitter values.
+ */
+static void
+syck_mark_emitter(emitter)
+ SyckEmitter *emitter;
+{
+ rb_gc_mark(emitter->ignore_id);
+ if ( emitter->bonus != NULL )
+ {
+ rb_gc_mark( (VALUE)emitter->bonus );
+ }
+}
+
+/*
+ * YAML::Syck::Emitter.new
+ */
+VALUE
+syck_emitter_new(argc, argv, class)
+ int argc;
+ VALUE *argv;
+ VALUE class;
+{
+ VALUE pobj, options, init_argv[1];
+ SyckEmitter *emitter = syck_new_emitter();
+ syck_emitter_ignore_id( emitter, Qnil );
+ syck_emitter_handler( emitter, rb_syck_output_handler );
+
+ emitter->bonus = (void *)rb_str_new2( "" );
+
+ rb_scan_args(argc, argv, "01", &options);
+ pobj = Data_Wrap_Struct( class, syck_mark_emitter, syck_free_emitter, emitter );
+
+ if ( ! rb_obj_is_instance_of( options, rb_cHash ) )
+ {
+ options = rb_hash_new();
+ }
+ init_argv[0] = options;
+ rb_obj_call_init(pobj, 1, init_argv);
+ return pobj;
+}
+
+/*
+ * YAML::Syck::Emitter.initialize( options )
+ */
+static VALUE
+syck_emitter_initialize( self, options )
+ VALUE self, options;
+{
+ rb_iv_set(self, "@options", options);
+ return self;
+}
+
+/*
+ * YAML::Syck::Emitter.level
+ */
+VALUE
+syck_emitter_level_m( self )
+ VALUE self;
+{
+ SyckEmitter *emitter;
+
+ Data_Get_Struct(self, SyckEmitter, emitter);
+ return LONG2NUM( emitter->level );
+}
+
+/*
+ * YAML::Syck::Emitter.flush
+ */
+VALUE
+syck_emitter_flush_m( self )
+ VALUE self;
+{
+ SyckEmitter *emitter;
+
+ Data_Get_Struct(self, SyckEmitter, emitter);
+ syck_emitter_flush( emitter, 0 );
+ return self;
+}
+
+/*
+ * YAML::Syck::Emitter.write( str )
+ */
+VALUE
+syck_emitter_write_m( self, str )
+ VALUE self, str;
+{
+ SyckEmitter *emitter;
+
+ Data_Get_Struct(self, SyckEmitter, emitter);
+ syck_emitter_write( emitter, RSTRING(str)->ptr, RSTRING(str)->len );
+ return self;
+}
+
+/*
+ * YAML::Syck::Emitter.simple( str )
+ */
+VALUE
+syck_emitter_simple_write( self, str )
+ VALUE self, str;
+{
+ SyckEmitter *emitter;
+
+ Data_Get_Struct(self, SyckEmitter, emitter);
+ syck_emitter_simple( emitter, RSTRING(str)->ptr, RSTRING(str)->len );
+ return self;
+}
+
+/*
+ * YAML::Syck::Emitter.start_object( object_id )
+ */
+VALUE
+syck_emitter_start_object( self, oid )
+ VALUE self, oid;
+{
+ char *anchor_name;
+ SyckEmitter *emitter;
+
+ Data_Get_Struct(self, SyckEmitter, emitter);
+ anchor_name = syck_emitter_start_obj( emitter, oid );
+
+ if ( anchor_name == NULL )
+ {
+ return Qnil;
+ }
+
+ return rb_str_new2( anchor_name );
+}
+
+/*
+ * YAML::Syck::Emitter.end_object( object_id )
+ */
+VALUE
+syck_emitter_end_object( self, oid )
+ VALUE self, oid;
+{
+ SyckEmitter *emitter;
+
+ Data_Get_Struct(self, SyckEmitter, emitter);
+ syck_emitter_end_obj( emitter );
+
+ if ( emitter->level < 0 )
+ {
+ syck_emitter_flush( emitter, 0 );
+ }
+ return (VALUE)emitter->bonus;
+}
+
+/*
+ * Initialize Syck extension
+ */
+void
+Init_syck()
+{
+ VALUE rb_yaml = rb_define_module( "YAML" );
+ VALUE rb_syck = rb_define_module_under( rb_yaml, "Syck" );
+ rb_define_const( rb_syck, "VERSION", rb_str_new2( SYCK_VERSION ) );
+
+ /*
+ * Global symbols
+ */
+ s_new = rb_intern("new");
+ s_utc = rb_intern("utc");
+ s_at = rb_intern("at");
+ s_to_f = rb_intern("to_f");
+ s_read = rb_intern("read");
+ s_binmode = rb_intern("binmode");
+ s_transfer = rb_intern("transfer");
+ s_call = rb_intern("call");
+ s_update = rb_intern("update");
+ s_dup = rb_intern("dup");
+ s_match = rb_intern("match");
+
+ sym_model = ID2SYM(rb_intern("Model"));
+ sym_generic = ID2SYM(rb_intern("Generic"));
+ sym_map = ID2SYM(rb_intern("map"));
+ sym_scalar = ID2SYM(rb_intern("scalar"));
+ sym_seq = ID2SYM(rb_intern("seq"));
+
+ /*
+ * Load Date module
+ */
+ rb_require( "date" );
+ cDate = rb_funcall( rb_cObject, rb_intern("const_get"), 1, rb_str_new2("Date") );
+
+ /*
+ * Define YAML::Syck::Loader class
+ */
+ cLoader = rb_define_class_under( rb_syck, "Loader", rb_cObject );
+ rb_define_attr( cLoader, "families", 1, 1 );
+ rb_define_attr( cLoader, "private_types", 1, 1 );
+ rb_define_method( cLoader, "initialize", syck_loader_initialize, 0 );
+ rb_define_method( cLoader, "add_domain_type", syck_loader_add_domain_type, -1 );
+ rb_define_method( cLoader, "add_builtin_type", syck_loader_add_builtin_type, -1 );
+ rb_define_method( cLoader, "add_ruby_type", syck_loader_add_ruby_type, -1 );
+ rb_define_method( cLoader, "add_private_type", syck_loader_add_private_type, -1 );
+ rb_define_method( cLoader, "detect_implicit", syck_loader_detect_implicit, 1 );
+ rb_define_method( cLoader, "transfer", syck_loader_transfer, 2 );
+
+ oDefaultLoader = rb_funcall( cLoader, rb_intern( "new" ), 0 );
+ rb_define_const( rb_syck, "DefaultLoader", oDefaultLoader );
+
+ /*
+ * Define YAML::Syck::Parser class
+ */
+ cParser = rb_define_class_under( rb_syck, "Parser", rb_cObject );
+ rb_define_attr( cParser, "options", 1, 1 );
+ rb_define_singleton_method( cParser, "new", syck_parser_new, -1 );
+ rb_define_method(cParser, "initialize", syck_parser_initialize, 1);
+ rb_define_method(cParser, "load", syck_parser_load, -1);
+ rb_define_method(cParser, "load_documents", syck_parser_load_documents, -1);
+
+ /*
+ * Define YAML::Syck::Node class
+ */
+ cNode = rb_define_class_under( rb_syck, "Node", rb_cObject );
+ rb_define_attr( cNode, "kind", 1, 1 );
+ rb_define_attr( cNode, "type_id", 1, 1 );
+ rb_define_attr( cNode, "value", 1, 1 );
+ rb_define_attr( cNode, "anchor", 1, 1 );
+ rb_define_method( cNode, "initialize", syck_node_initialize, 2);
+ rb_define_method( cNode, "transform", syck_node_transform, 0);
+
+ /*
+ * Define YAML::Syck::PrivateType class
+ */
+ cPrivateType = rb_define_class_under( rb_syck, "PrivateType", rb_cObject );
+ rb_define_attr( cPrivateType, "type_id", 1, 1 );
+ rb_define_attr( cPrivateType, "value", 1, 1 );
+ rb_define_method( cPrivateType, "initialize", syck_privatetype_initialize, 2);
+
+ /*
+ * Define YAML::Syck::DomainType class
+ */
+ cDomainType = rb_define_class_under( rb_syck, "DomainType", rb_cObject );
+ rb_define_attr( cDomainType, "domain", 1, 1 );
+ rb_define_attr( cDomainType, "type_id", 1, 1 );
+ rb_define_attr( cDomainType, "value", 1, 1 );
+ rb_define_method( cDomainType, "initialize", syck_domaintype_initialize, 3);
+
+ /*
+ * Define YAML::Syck::BadAlias class
+ */
+ cBadAlias = rb_define_class_under( rb_syck, "BadAlias", rb_cObject );
+ rb_define_attr( cBadAlias, "name", 1, 1 );
+ rb_define_method( cBadAlias, "initialize", syck_badalias_initialize, 1);
+
+ /*
+ * Define YAML::Syck::MergeKey class
+ */
+ cMergeKey = rb_define_class_under( rb_syck, "MergeKey", rb_cObject );
+
+ /*
+ * Define YAML::Syck::Emitter class
+ */
+ cEmitter = rb_define_class_under( rb_syck, "Emitter", rb_cObject );
+ rb_define_singleton_method( cEmitter, "new", syck_emitter_new, -1 );
+ rb_define_method( cEmitter, "initialize", syck_emitter_initialize, 1 );
+ rb_define_method( cEmitter, "level", syck_emitter_level_m, 0 );
+ rb_define_method( cEmitter, "write", syck_emitter_write_m, 1 );
+ rb_define_method( cEmitter, "<<", syck_emitter_write_m, 1 );
+ rb_define_method( cEmitter, "simple", syck_emitter_simple_write, 1 );
+ rb_define_method( cEmitter, "flush", syck_emitter_flush_m, 0 );
+ rb_define_method( cEmitter, "start_object", syck_emitter_start_object, 1 );
+ rb_define_method( cEmitter, "end_object", syck_emitter_end_object, 0 );
+}
+
diff --git a/ext/syck/syck.c b/ext/syck/syck.c
new file mode 100644
index 0000000000..42b70573bc
--- /dev/null
+++ b/ext/syck/syck.c
@@ -0,0 +1,493 @@
+/*
+ * syck.c
+ *
+ * $Author$
+ * $Date$
+ *
+ * Copyright (C) 2003 why the lucky stiff
+ */
+#include <stdio.h>
+#include <string.h>
+
+#include "syck.h"
+#include "ruby.h"
+
+void syck_parser_pop_level( SyckParser * );
+
+/*
+ * Custom assert
+ */
+void
+syck_assert( char *file_name, unsigned line_num )
+{
+ fflush( NULL );
+ fprintf( stderr, "\nAssertion failed: %s, line %u\n",
+ file_name, line_num );
+ fflush( stderr );
+ abort();
+}
+
+/*
+ * Allocates and copies a string
+ */
+char *
+syck_strndup( char *buf, long len )
+{
+ char *new = S_ALLOC_N( char, len + 1 );
+ S_MEMZERO( new, char, len + 1 );
+ S_MEMCPY( new, buf, char, len );
+ return new;
+}
+
+/*
+ * Default FILE IO function
+ */
+long
+syck_io_file_read( char *buf, SyckIoFile *file, long max_size, long skip )
+{
+ long len = 0;
+
+ ASSERT( file != NULL );
+
+ max_size -= skip;
+ len = fread( buf + skip, max_size, sizeof( char ), file->ptr );
+ len += skip;
+ buf[len] = '\0';
+
+ return len;
+}
+
+/*
+ * Default string IO function
+ */
+long
+syck_io_str_read( char *buf, SyckIoStr *str, long max_size, long skip )
+{
+ char *beg;
+ long len = 0;
+
+ ASSERT( str != NULL );
+ beg = str->ptr;
+ if ( max_size >= 0 )
+ {
+ max_size -= skip;
+ if ( max_size < 0 ) max_size = 0;
+
+ str->ptr += max_size;
+ if ( str->ptr > str->end )
+ {
+ str->ptr = str->end;
+ }
+ }
+ else
+ {
+ /* Use exact string length */
+ while ( str->ptr < str->end ) {
+ if (*(str->ptr++) == '\n') break;
+ }
+ }
+ if ( beg < str->ptr )
+ {
+ len = str->ptr - beg;
+ S_MEMCPY( buf + skip, beg, char, len );
+ }
+ len += skip;
+ buf[len] = '\0';
+
+ return len;
+}
+
+void
+syck_parser_reset_levels( SyckParser *p )
+{
+ while ( p->lvl_idx > 1 )
+ {
+ syck_parser_pop_level( p );
+ }
+
+ if ( p->lvl_idx < 1 )
+ {
+ p->lvl_idx = 1;
+ p->levels[0].spaces = -1;
+ p->levels[0].domain = syck_strndup( "", 0 );
+ }
+ p->levels[0].status = syck_lvl_header;
+}
+
+void
+syck_parser_reset_cursor( SyckParser *p )
+{
+ if ( p->buffer == NULL )
+ {
+ p->buffer = S_ALLOC_N( char, p->bufsize );
+ S_MEMZERO( p->buffer, char, p->bufsize );
+ }
+ p->buffer[0] = '\0';
+
+ p->cursor = NULL;
+ p->lineptr = NULL;
+ p->linectptr = NULL;
+ p->token = NULL;
+ p->toktmp = NULL;
+ p->marker = NULL;
+ p->limit = NULL;
+
+ p->root = 0;
+ p->root_on_error = 0;
+ p->linect = 0;
+ p->eof = 0;
+ p->last_token = 0;
+ p->force_token = 0;
+}
+
+/*
+ * Value to return on a parse error
+ */
+void
+syck_parser_set_root_on_error( SyckParser *p, SYMID roer )
+{
+ p->root_on_error = roer;
+}
+
+/*
+ * Allocate the parser
+ */
+SyckParser *
+syck_new_parser()
+{
+ SyckParser *p;
+ p = S_ALLOC( SyckParser );
+ p->lvl_capa = ALLOC_CT;
+ p->levels = S_ALLOC_N( SyckLevel, p->lvl_capa );
+ p->io_type = syck_io_str;
+ p->io.str = NULL;
+ p->syms = NULL;
+ p->anchors = NULL;
+ p->bad_anchors = NULL;
+ p->implicit_typing = 1;
+ p->taguri_expansion = 0;
+ p->bufsize = SYCK_BUFFERSIZE;
+ p->buffer = NULL;
+ p->lvl_idx = 0;
+ syck_parser_reset_levels( p );
+ return p;
+}
+
+int
+syck_add_sym( SyckParser *p, char *data )
+{
+ SYMID id = 0;
+ if ( p->syms == NULL )
+ {
+ p->syms = st_init_numtable();
+ }
+ id = p->syms->num_entries;
+ st_insert( p->syms, id, (st_data_t)data );
+ return id;
+}
+
+int
+syck_lookup_sym( SyckParser *p, SYMID id, char **data )
+{
+ if ( p->syms == NULL ) return 0;
+ return st_lookup( p->syms, id, (st_data_t *)data );
+}
+
+int
+syck_st_free_nodes( char *key, SyckNode *n, char *arg )
+{
+ syck_free_node( n );
+ return ST_CONTINUE;
+}
+
+void
+syck_st_free( SyckParser *p )
+{
+ /*
+ * Free the adhoc symbol table
+ */
+ if ( p->syms != NULL )
+ {
+ st_free_table( p->syms );
+ p->syms = NULL;
+ }
+
+ /*
+ * Free the anchor tables
+ */
+ if ( p->anchors != NULL )
+ {
+ st_foreach( p->anchors, syck_st_free_nodes, 0 );
+ st_free_table( p->anchors );
+ p->anchors = NULL;
+ }
+
+ if ( p->bad_anchors != NULL )
+ {
+ st_foreach( p->bad_anchors, syck_st_free_nodes, 0 );
+ st_free_table( p->bad_anchors );
+ p->bad_anchors = NULL;
+ }
+}
+
+void
+syck_free_parser( SyckParser *p )
+{
+ /*
+ * Free tables, levels
+ */
+ syck_st_free( p );
+ syck_parser_reset_levels( p );
+ S_FREE( p->levels[0].domain );
+ S_FREE( p->levels );
+
+ if ( p->buffer != NULL )
+ {
+ S_FREE( p->buffer );
+ }
+ free_any_io( p );
+ S_FREE( p );
+}
+
+void
+syck_parser_handler( SyckParser *p, SyckNodeHandler hdlr )
+{
+ ASSERT( p != NULL );
+ p->handler = hdlr;
+}
+
+void
+syck_parser_implicit_typing( SyckParser *p, int flag )
+{
+ p->implicit_typing = ( flag == 0 ? 0 : 1 );
+}
+
+void
+syck_parser_taguri_expansion( SyckParser *p, int flag )
+{
+ p->taguri_expansion = ( flag == 0 ? 0 : 1 );
+}
+
+void
+syck_parser_error_handler( SyckParser *p, SyckErrorHandler hdlr )
+{
+ ASSERT( p != NULL );
+ p->error_handler = hdlr;
+}
+
+void
+syck_parser_bad_anchor_handler( SyckParser *p, SyckBadAnchorHandler hdlr )
+{
+ ASSERT( p != NULL );
+ p->bad_anchor_handler = hdlr;
+}
+
+void
+syck_parser_file( SyckParser *p, FILE *fp, SyckIoFileRead read )
+{
+ ASSERT( p != NULL );
+ free_any_io( p );
+ syck_parser_reset_cursor( p );
+ p->io_type = syck_io_file;
+ p->io.file = S_ALLOC( SyckIoFile );
+ p->io.file->ptr = fp;
+ if ( read != NULL )
+ {
+ p->io.file->read = read;
+ }
+ else
+ {
+ p->io.file->read = syck_io_file_read;
+ }
+}
+
+void
+syck_parser_str( SyckParser *p, char *ptr, long len, SyckIoStrRead read )
+{
+ ASSERT( p != NULL );
+ free_any_io( p );
+ syck_parser_reset_cursor( p );
+ p->io_type = syck_io_str;
+ p->io.str = S_ALLOC( SyckIoStr );
+ p->io.str->beg = ptr;
+ p->io.str->ptr = ptr;
+ p->io.str->end = ptr + len;
+ if ( read != NULL )
+ {
+ p->io.str->read = read;
+ }
+ else
+ {
+ p->io.str->read = syck_io_str_read;
+ }
+}
+
+void
+syck_parser_str_auto( SyckParser *p, char *ptr, SyckIoStrRead read )
+{
+ syck_parser_str( p, ptr, strlen( ptr ), read );
+}
+
+SyckLevel *
+syck_parser_current_level( SyckParser *p )
+{
+ return &p->levels[p->lvl_idx-1];
+}
+
+void
+syck_parser_pop_level( SyckParser *p )
+{
+ ASSERT( p != NULL );
+
+ /* The root level should never be popped */
+ if ( p->lvl_idx <= 1 ) return;
+
+ p->lvl_idx -= 1;
+ free( p->levels[p->lvl_idx].domain );
+}
+
+void
+syck_parser_add_level( SyckParser *p, int len, enum syck_level_status status )
+{
+ ASSERT( p != NULL );
+ if ( p->lvl_idx + 1 > p->lvl_capa )
+ {
+ p->lvl_capa += ALLOC_CT;
+ S_REALLOC_N( p->levels, SyckLevel, p->lvl_capa );
+ }
+
+ ASSERT( len > p->levels[p->lvl_idx-1].spaces );
+ p->levels[p->lvl_idx].spaces = len;
+ p->levels[p->lvl_idx].domain = syck_strndup( p->levels[p->lvl_idx-1].domain, strlen( p->levels[p->lvl_idx-1].domain ) );
+ p->levels[p->lvl_idx].status = status;
+ p->lvl_idx += 1;
+}
+
+void
+free_any_io( SyckParser *p )
+{
+ ASSERT( p != NULL );
+ switch ( p->io_type )
+ {
+ case syck_io_str:
+ if ( p->io.str != NULL )
+ {
+ S_FREE( p->io.str );
+ p->io.str = NULL;
+ }
+ break;
+
+ case syck_io_file:
+ if ( p->io.file != NULL )
+ {
+ S_FREE( p->io.file );
+ p->io.file = NULL;
+ }
+ break;
+ }
+}
+
+long
+syck_move_tokens( SyckParser *p )
+{
+ long count, skip;
+ ASSERT( p->buffer != NULL );
+
+ if ( p->token == NULL )
+ return 0;
+
+ skip = p->limit - p->token;
+ if ( skip < 1 )
+ return 0;
+
+ if ( ( count = p->token - p->buffer ) )
+ {
+ S_MEMMOVE( p->buffer, p->token, char, skip );
+ p->token = p->buffer;
+ p->marker -= count;
+ p->cursor -= count;
+ p->toktmp -= count;
+ p->limit -= count;
+ p->lineptr -= count;
+ p->linectptr -= count;
+ }
+ return skip;
+}
+
+void
+syck_check_limit( SyckParser *p, long len )
+{
+ if ( p->cursor == NULL )
+ {
+ p->cursor = p->buffer;
+ p->lineptr = p->buffer;
+ p->linectptr = p->buffer;
+ p->marker = p->buffer;
+ }
+ p->limit = p->buffer + len;
+}
+
+long
+syck_parser_read( SyckParser *p )
+{
+ long len = 0;
+ long skip = 0;
+ ASSERT( p != NULL );
+ switch ( p->io_type )
+ {
+ case syck_io_str:
+ skip = syck_move_tokens( p );
+ len = (p->io.str->read)( p->buffer, p->io.str, SYCK_BUFFERSIZE - 1, skip );
+ break;
+
+ case syck_io_file:
+ skip = syck_move_tokens( p );
+ len = (p->io.file->read)( p->buffer, p->io.file, SYCK_BUFFERSIZE - 1, skip );
+ break;
+ }
+ syck_check_limit( p, len );
+ return len;
+}
+
+long
+syck_parser_readlen( SyckParser *p, long max_size )
+{
+ long len = 0;
+ long skip = 0;
+ ASSERT( p != NULL );
+ switch ( p->io_type )
+ {
+ case syck_io_str:
+ skip = syck_move_tokens( p );
+ len = (p->io.str->read)( p->buffer, p->io.str, max_size, skip );
+ break;
+
+ case syck_io_file:
+ skip = syck_move_tokens( p );
+ len = (p->io.file->read)( p->buffer, p->io.file, max_size, skip );
+ break;
+ }
+ syck_check_limit( p, len );
+ return len;
+}
+
+SYMID
+syck_parse( SyckParser *p )
+{
+ ASSERT( p != NULL );
+
+ syck_st_free( p );
+ syck_parser_reset_levels( p );
+ syckparse( p );
+ return p->root;
+}
+
+void
+syck_default_error_handler( SyckParser *p, char *msg )
+{
+ printf( "Error at [Line %d, Col %d]: %s\n",
+ p->linect,
+ p->cursor - p->lineptr,
+ msg );
+}
+
diff --git a/ext/syck/syck.h b/ext/syck/syck.h
new file mode 100644
index 0000000000..124e6c5d4e
--- /dev/null
+++ b/ext/syck/syck.h
@@ -0,0 +1,378 @@
+/*
+ * syck.h
+ *
+ * $Author$
+ * $Date$
+ *
+ * Copyright (C) 2003 why the lucky stiff
+ */
+
+#ifndef SYCK_H
+#define SYCK_H
+
+#define SYCK_YAML_MAJOR 1
+#define SYCK_YAML_MINOR 0
+
+#define SYCK_VERSION "0.38"
+#define YAML_DOMAIN "yaml.org,2002"
+
+#include <stdio.h>
+#include <ctype.h>
+#include "st.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*
+ * Memory Allocation
+ */
+#if defined(HAVE_ALLOCA_H) && !defined(__GNUC__)
+#include <alloca.h>
+#endif
+
+#if DEBUG
+ void syck_assert( char *, unsigned );
+# define ASSERT(f) \
+ if ( f ) \
+ {} \
+ else \
+ syck_assert( __FILE__, __LINE__ )
+#else
+# define ASSERT(f)
+#endif
+
+#ifndef NULL
+# define NULL (void *)0
+#endif
+
+#define ALLOC_CT 8
+#define SYCK_BUFFERSIZE 262144
+#define S_ALLOC_N(type,n) (type*)malloc(sizeof(type)*(n))
+#define S_ALLOC(type) (type*)malloc(sizeof(type))
+#define S_REALLOC_N(var,type,n) (var)=(type*)realloc((char*)(var),sizeof(type)*(n))
+#define S_FREE(n) free(n); n = NULL;
+
+#define S_ALLOCA_N(type,n) (type*)alloca(sizeof(type)*(n))
+
+#define S_MEMZERO(p,type,n) memset((p), 0, sizeof(type)*(n))
+#define S_MEMCPY(p1,p2,type,n) memcpy((p1), (p2), sizeof(type)*(n))
+#define S_MEMMOVE(p1,p2,type,n) memmove((p1), (p2), sizeof(type)*(n))
+#define S_MEMCMP(p1,p2,type,n) memcmp((p1), (p2), sizeof(type)*(n))
+
+#define BLOCK_FOLD 10
+#define BLOCK_LIT 20
+#define BLOCK_PLAIN 30
+#define NL_CHOMP 130
+#define NL_KEEP 140
+
+/*
+ * Node definitions
+ */
+#define SYMID unsigned long
+
+typedef struct _syck_node SyckNode;
+
+enum syck_kind_tag {
+ syck_map_kind,
+ syck_seq_kind,
+ syck_str_kind
+};
+
+enum map_part {
+ map_key,
+ map_value
+};
+
+/*
+ * Node metadata struct
+ */
+struct _syck_node {
+ /* Symbol table ID */
+ SYMID id;
+ /* Underlying kind */
+ enum syck_kind_tag kind;
+ /* Fully qualified tag-uri for type */
+ char *type_id;
+ /* Anchor name */
+ char *anchor;
+ union {
+ /* Storage for map data */
+ struct SyckMap {
+ SYMID *keys;
+ SYMID *values;
+ long capa;
+ long idx;
+ } *pairs;
+ /* Storage for sequence data */
+ struct SyckSeq {
+ SYMID *items;
+ long capa;
+ long idx;
+ } *list;
+ /* Storage for string data */
+ struct SyckStr {
+ char *ptr;
+ long len;
+ } *str;
+ } data;
+ /* Shortcut node */
+ void *shortcut;
+};
+
+/*
+ * Parser definitions
+ */
+typedef struct _syck_parser SyckParser;
+typedef struct _syck_file SyckIoFile;
+typedef struct _syck_str SyckIoStr;
+typedef struct _syck_level SyckLevel;
+
+typedef SYMID (*SyckNodeHandler)(SyckParser *, SyckNode *);
+typedef void (*SyckErrorHandler)(SyckParser *, char *);
+typedef SyckNode * (*SyckBadAnchorHandler)(SyckParser *, char *);
+typedef long (*SyckIoFileRead)(char *, SyckIoFile *, long, long);
+typedef long (*SyckIoStrRead)(char *, SyckIoStr *, long, long);
+
+enum syck_io_type {
+ syck_io_str,
+ syck_io_file
+};
+
+enum syck_level_status {
+ syck_lvl_header,
+ syck_lvl_doc,
+ syck_lvl_seq,
+ syck_lvl_map,
+ syck_lvl_block,
+ syck_lvl_str,
+ syck_lvl_inline,
+ syck_lvl_end,
+ syck_lvl_pause
+};
+
+/*
+ * Parser struct
+ */
+struct _syck_parser {
+ /* Root node */
+ SYMID root, root_on_error;
+ /* Implicit typing flag */
+ int implicit_typing, taguri_expansion;
+ /* Scripting language function to handle nodes */
+ SyckNodeHandler handler;
+ /* Error handler */
+ SyckErrorHandler error_handler;
+ /* InvalidAnchor handler */
+ SyckBadAnchorHandler bad_anchor_handler;
+ /* IO type */
+ enum syck_io_type io_type;
+ /* Custom buffer size */
+ size_t bufsize;
+ /* Buffer pointers */
+ char *buffer, *linectptr, *lineptr, *toktmp, *token, *cursor, *marker, *limit;
+ /* Line counter */
+ int linect;
+ /* Last token from yylex() */
+ int last_token;
+ /* Force a token upon next call to yylex() */
+ int force_token;
+ /* EOF flag */
+ int eof;
+ union {
+ struct _syck_file {
+ FILE *ptr;
+ SyckIoFileRead read;
+ } *file;
+ struct _syck_str {
+ char *beg, *ptr, *end;
+ SyckIoStrRead read;
+ } *str;
+ } io;
+ /* Symbol table for anchors */
+ st_table *anchors, *bad_anchors;
+ /* Optional symbol table for SYMIDs */
+ st_table *syms;
+ /* Levels of indentation */
+ struct _syck_level {
+ int spaces;
+ char *domain;
+ enum syck_level_status status;
+ } *levels;
+ int lvl_idx;
+ int lvl_capa;
+ void *bonus;
+};
+
+/*
+ * Emitter definitions
+ */
+typedef struct _syck_emitter SyckEmitter;
+typedef struct _syck_emitter_node SyckEmitterNode;
+
+typedef void (*SyckOutputHandler)(SyckEmitter *, char *, long);
+
+enum doc_stage {
+ doc_open,
+ doc_need_header,
+ doc_processing
+};
+
+enum block_styles {
+ block_arbitrary,
+ block_fold,
+ block_literal
+};
+
+/*
+ * Emitter struct
+ */
+struct _syck_emitter {
+ /* Headerless doc flag */
+ int headless;
+ /* Sequence map shortcut flag */
+ int seq_map;
+ /* Force header? */
+ int use_header;
+ /* Force version? */
+ int use_version;
+ /* Sort hash keys */
+ int sort_keys;
+ /* Anchor format */
+ char *anchor_format;
+ /* Explicit typing on all collections? */
+ int explicit_typing;
+ /* Best width on folded scalars */
+ int best_width;
+ /* Use literal[1] or folded[2] blocks on all text? */
+ enum block_styles block_style;
+ /* Stage of written document */
+ enum doc_stage stage;
+ /* Level counter */
+ int level;
+ /* Default indentation */
+ int indent;
+ /* Object ignore ID */
+ SYMID ignore_id;
+ /* Symbol table for anchors */
+ st_table *markers, *anchors;
+ /* Custom buffer size */
+ size_t bufsize;
+ /* Buffer */
+ char *buffer, *marker;
+ /* Absolute position of the buffer */
+ long bufpos;
+ /* Handler for output */
+ SyckOutputHandler handler;
+ /* Pointer for extension's use */
+ void *bonus;
+};
+
+/*
+ * Emitter node metadata struct
+ */
+struct _syck_emitter_node {
+ /* Node buffer position */
+ long pos;
+ /* Current indent */
+ long indent;
+ /* Collection? */
+ int is_shortcut;
+};
+
+/*
+ * Handler prototypes
+ */
+SYMID syck_hdlr_add_node( SyckParser *, SyckNode * );
+SyckNode *syck_hdlr_add_anchor( SyckParser *, char *, SyckNode * );
+void syck_hdlr_remove_anchor( SyckParser *, char * );
+SyckNode *syck_hdlr_get_anchor( SyckParser *, char * );
+void syck_add_transfer( char *, SyckNode *, int );
+char *syck_xprivate( char *, int );
+char *syck_taguri( char *, char *, int );
+int syck_add_sym( SyckParser *, char * );
+int syck_lookup_sym( SyckParser *, SYMID, char ** );
+int syck_try_implicit( SyckNode * );
+char *syck_type_id_to_uri( char * );
+void try_tag_implicit( SyckNode *, int );
+char *syck_match_implicit( char *, size_t );
+
+/*
+ * API prototypes
+ */
+char *syck_strndup( char *, long );
+long syck_io_file_read( char *, SyckIoFile *, long, long );
+long syck_io_str_read( char *, SyckIoStr *, long, long );
+char *syck_base64enc( char *, long );
+char *syck_base64dec( char *, long );
+SyckEmitter *syck_new_emitter();
+void syck_emitter_ignore_id( SyckEmitter *, SYMID );
+void syck_emitter_handler( SyckEmitter *, SyckOutputHandler );
+void syck_free_emitter( SyckEmitter * );
+void syck_emitter_clear( SyckEmitter * );
+void syck_emitter_simple( SyckEmitter *, char *, long );
+void syck_emitter_write( SyckEmitter *, char *, long );
+void syck_emitter_flush( SyckEmitter *, long );
+char *syck_emitter_start_obj( SyckEmitter *, SYMID );
+void syck_emitter_end_obj( SyckEmitter * );
+SyckParser *syck_new_parser();
+void syck_free_parser( SyckParser * );
+void syck_parser_set_root_on_error( SyckParser *, SYMID );
+void syck_parser_implicit_typing( SyckParser *, int );
+void syck_parser_taguri_expansion( SyckParser *, int );
+void syck_parser_handler( SyckParser *, SyckNodeHandler );
+void syck_parser_error_handler( SyckParser *, SyckErrorHandler );
+void syck_parser_bad_anchor_handler( SyckParser *, SyckBadAnchorHandler );
+void syck_parser_file( SyckParser *, FILE *, SyckIoFileRead );
+void syck_parser_str( SyckParser *, char *, long, SyckIoStrRead );
+void syck_parser_str_auto( SyckParser *, char *, SyckIoStrRead );
+SyckLevel *syck_parser_current_level( SyckParser * );
+void syck_parser_add_level( SyckParser *, int, enum syck_level_status );
+void syck_parser_pop_level( SyckParser * );
+void free_any_io( SyckParser * );
+long syck_parser_read( SyckParser * );
+long syck_parser_readlen( SyckParser *, long );
+void syck_parser_init( SyckParser *, int );
+SYMID syck_parse( SyckParser * );
+void syck_default_error_handler( SyckParser *, char * );
+
+/*
+ * Allocation prototypes
+ */
+SyckNode *syck_alloc_map();
+SyckNode *syck_alloc_seq();
+SyckNode *syck_alloc_str();
+void syck_free_node( SyckNode * );
+void syck_free_members( SyckNode * );
+SyckNode *syck_new_str( char * );
+SyckNode *syck_new_str2( char *, long );
+void syck_str_blow_away_commas( SyckNode * );
+char *syck_str_read( SyckNode * );
+SyckNode *syck_new_map( SYMID, SYMID );
+void syck_map_add( SyckNode *, SYMID, SYMID );
+SYMID syck_map_read( SyckNode *, enum map_part, long );
+void syck_map_assign( SyckNode *, enum map_part, long, SYMID );
+long syck_map_count( SyckNode * );
+void syck_map_update( SyckNode *, SyckNode * );
+SyckNode *syck_new_seq( SYMID );
+void syck_seq_add( SyckNode *, SYMID );
+SYMID syck_seq_read( SyckNode *, long );
+long syck_seq_count( SyckNode * );
+
+void apply_seq_in_map( SyckParser *, SyckNode * );
+
+/*
+ * Lexer prototypes
+ */
+int syckparse( void * );
+void syckerror( char *msg );
+
+#ifndef ST_DATA_T_DEFINED
+typedef long st_data_t;
+#endif
+
+#if defined(__cplusplus)
+} /* extern "C" { */
+#endif
+
+#endif /* ifndef SYCK_H */
diff --git a/ext/syck/token.c b/ext/syck/token.c
new file mode 100644
index 0000000000..1fc274c590
--- /dev/null
+++ b/ext/syck/token.c
@@ -0,0 +1,2345 @@
+/* Generated by re2c 0.5 on Mon Jul 28 21:10:39 2003 */
+#line 1 "token.re"
+/*
+ * token.re
+ *
+ * $Author$
+ * $Date$
+ *
+ * Copyright (C) 2003 why the lucky stiff
+ */
+#include "syck.h"
+#include "ruby.h"
+#include "gram.h"
+
+/*
+ * Allocate quoted strings in chunks
+ */
+#define QUOTELEN 1024
+
+/*
+ * They do my bidding...
+ */
+#define YYCTYPE char
+#define YYCURSOR parser->cursor
+#define YYMARKER parser->marker
+#define YYLIMIT parser->limit
+#define YYTOKEN parser->token
+#define YYTOKTMP parser->toktmp
+#define YYLINEPTR parser->lineptr
+#define YYLINECTPTR parser->linectptr
+#define YYLINE parser->linect
+#define YYFILL(n) syck_parser_read(parser)
+
+/*
+ * Repositions the cursor at `n' offset from the token start.
+ * Only works in `Header' and `Document' sections.
+ */
+#define YYPOS(n) YYCURSOR = YYTOKEN + n
+
+/*
+ * Track line numbers
+ */
+#define NEWLINE(ptr) YYLINEPTR = ptr + 1; if ( YYLINEPTR > YYLINECTPTR ) { YYLINE++; YYLINECTPTR = YYLINEPTR; }
+
+/*
+ * I like seeing the level operations as macros...
+ */
+#define ADD_LEVEL(len, status) syck_parser_add_level( parser, len, status )
+#define POP_LEVEL() syck_parser_pop_level( parser )
+#define CURRENT_LEVEL() syck_parser_current_level( parser )
+
+/*
+ * Force a token next time around sycklex()
+ */
+#define FORCE_NEXT_TOKEN(tok) parser->force_token = tok;
+
+/*
+ * Nice little macro to ensure we're YAML_IOPENed to the current level.
+ * * Only use this macro in the "Document" section *
+ */
+#define ENSURE_YAML_IOPEN(last_lvl, to_len, reset) \
+ if ( last_lvl->spaces < to_len ) \
+ { \
+ if ( last_lvl->status == syck_lvl_inline ) \
+ { \
+ goto Document; \
+ } \
+ else \
+ { \
+ ADD_LEVEL( to_len, syck_lvl_doc ); \
+ if ( reset == 1 ) YYPOS(0); \
+ return YAML_IOPEN; \
+ } \
+ }
+
+/*
+ * Nice little macro to ensure closure of levels.
+ * * Only use this macro in the "Document" section *
+ */
+#define ENSURE_YAML_IEND(last_lvl, to_len) \
+ if ( last_lvl->spaces > to_len ) \
+ { \
+ syck_parser_pop_level( parser ); \
+ YYPOS(0); \
+ return YAML_IEND; \
+ }
+
+/*
+ * Concatenates quoted string items and manages allocation
+ * to the quoted string
+ */
+#define QUOTECAT(s, c, i, l) \
+ { \
+ if ( i + 1 >= c ) \
+ { \
+ c += QUOTELEN; \
+ S_REALLOC_N( s, char, c ); \
+ } \
+ s[i++] = l; \
+ s[i] = '\0'; \
+ }
+
+#define QUOTECATS(s, c, i, cs, cl) \
+ { \
+ while ( i + cl >= c ) \
+ { \
+ c += QUOTELEN; \
+ S_REALLOC_N( s, char, c ); \
+ } \
+ S_MEMCPY( s + i, cs, char, cl ); \
+ i += cl; \
+ s[i] = '\0'; \
+ }
+
+/*
+ * Tags a plain scalar with a transfer method
+ * * Use only in "Plain" section *
+ */
+#define RETURN_IMPLICIT() \
+ { \
+ SyckNode *n = syck_alloc_str(); \
+ YYCURSOR = YYTOKTMP; \
+ n->data.str->ptr = qstr; \
+ n->data.str->len = qidx; \
+ sycklval->nodeData = n; \
+ if ( parser->implicit_typing == 1 ) \
+ { \
+ try_tag_implicit( sycklval->nodeData, parser->taguri_expansion ); \
+ } \
+ return YAML_PLAIN; \
+ }
+
+/*
+ * Keep or chomp block?
+ * * Use only in "ScalarBlock" section *
+ */
+#define RETURN_YAML_BLOCK() \
+ { \
+ SyckNode *n = syck_alloc_str(); \
+ n->data.str->ptr = qstr; \
+ n->data.str->len = qidx; \
+ if ( qidx > 0 ) \
+ { \
+ if ( nlDoWhat != NL_KEEP ) \
+ { \
+ char *fc = n->data.str->ptr + n->data.str->len - 1; \
+ while ( is_newline( fc ) ) fc--; \
+ if ( nlDoWhat != NL_CHOMP ) \
+ fc += 1; \
+ n->data.str->len = fc - n->data.str->ptr + 1; \
+ } \
+ } \
+ sycklval->nodeData = n; \
+ return YAML_BLOCK; \
+ }
+
+/*
+ * Handles newlines, calculates indent
+ */
+#define GOBBLE_UP_YAML_INDENT( ict, start ) \
+ char *indent = start; \
+ NEWLINE(indent); \
+ while ( indent < YYCURSOR ) \
+ { \
+ if ( is_newline( ++indent ) ) \
+ { \
+ NEWLINE(indent); \
+ } \
+ } \
+ ict = 0; \
+ if ( *YYCURSOR == '\0' ) \
+ { \
+ ict = -1; \
+ start = YYCURSOR - 1; \
+ } \
+ else if ( *YYLINEPTR == ' ' ) \
+ { \
+ ict = YYCURSOR - YYLINEPTR; \
+ }
+
+/*
+ * If an indent exists at the current level, back up.
+ */
+#define GET_TRUE_YAML_INDENT(indt_len) \
+ { \
+ SyckLevel *lvl_deep = CURRENT_LEVEL(); \
+ indt_len = lvl_deep->spaces; \
+ if ( indt_len == YYTOKEN - YYLINEPTR ) \
+ { \
+ SyckLevel *lvl_over; \
+ parser->lvl_idx--; \
+ lvl_over = CURRENT_LEVEL(); \
+ indt_len = lvl_over->spaces; \
+ parser->lvl_idx++; \
+ } \
+ }
+
+/*
+ * Argjh! I hate globals! Here for syckerror() only!
+ */
+SyckParser *syck_parser_ptr = NULL;
+
+/*
+ * Accessory funcs later in this file.
+ */
+void eat_comments( SyckParser * );
+int is_newline( char *ptr );
+int yywrap();
+
+/*
+ * My own re-entrant sycklex() using re2c.
+ * You really get used to the limited regexp.
+ * It's really nice to not rely on backtracking and such.
+ */
+int
+sycklex( YYSTYPE *sycklval, SyckParser *parser )
+{
+ syck_parser_ptr = parser;
+ if ( YYCURSOR == NULL )
+ {
+ syck_parser_read( parser );
+ }
+
+ if ( parser->force_token != 0 )
+ {
+ int t = parser->force_token;
+ parser->force_token = 0;
+ return t;
+ }
+
+#line 246
+
+
+ if ( YYLINEPTR != YYCURSOR )
+ {
+ goto Document;
+ }
+
+Header:
+
+ YYTOKEN = YYCURSOR;
+
+{
+ YYCTYPE yych;
+ unsigned int yyaccept;
+ goto yy0;
+yy1: ++YYCURSOR;
+yy0:
+ if((YYLIMIT - YYCURSOR) < 5) YYFILL(5);
+ yych = *YYCURSOR;
+ switch(yych){
+ case '\000': goto yy7;
+ case '\n': goto yy9;
+ case '\r': goto yy11;
+ case '#': goto yy5;
+ case '-': goto yy2;
+ case '.': goto yy4;
+ default: goto yy12;
+ }
+yy2: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case '-': goto yy24;
+ default: goto yy3;
+ }
+yy3:
+#line 302
+ { YYPOS(0);
+ goto Document;
+ }
+yy4: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case '.': goto yy17;
+ default: goto yy3;
+ }
+yy5: yych = *++YYCURSOR;
+yy6:
+#line 287
+ { eat_comments( parser );
+ goto Header;
+ }
+yy7: yych = *++YYCURSOR;
+yy8:
+#line 291
+ { SyckLevel *lvl = CURRENT_LEVEL();
+ ENSURE_YAML_IEND(lvl, -1);
+ YYPOS(0);
+ return 0;
+ }
+yy9: yyaccept = 1;
+ yych = *(YYMARKER = ++YYCURSOR);
+ goto yy14;
+yy10:
+#line 297
+ { int indt_len;
+ GOBBLE_UP_YAML_INDENT( indt_len, YYTOKEN );
+ goto Header;
+ }
+yy11: yych = *++YYCURSOR;
+ switch(yych){
+ case '\n': goto yy13;
+ default: goto yy3;
+ }
+yy12: yych = *++YYCURSOR;
+ goto yy3;
+yy13: yyaccept = 1;
+ YYMARKER = ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy14: switch(yych){
+ case '\n': case ' ': goto yy13;
+ case '\r': goto yy15;
+ default: goto yy10;
+ }
+yy15: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch(yych){
+ case '\n': goto yy13;
+ default: goto yy16;
+ }
+yy16: YYCURSOR = YYMARKER;
+ switch(yyaccept){
+ case 1: goto yy10;
+ case 0: goto yy3;
+ }
+yy17: yych = *++YYCURSOR;
+ switch(yych){
+ case '.': goto yy18;
+ default: goto yy16;
+ }
+yy18: yych = *++YYCURSOR;
+ switch(yych){
+ case '\n': goto yy19;
+ case '\r': goto yy23;
+ case ' ': goto yy21;
+ default: goto yy16;
+ }
+yy19: yych = *++YYCURSOR;
+yy20:
+#line 273
+ { SyckLevel *lvl = CURRENT_LEVEL();
+ if ( lvl->status == syck_lvl_header )
+ {
+ goto Header;
+ }
+ else
+ {
+ ENSURE_YAML_IEND(lvl, -1);
+ YYPOS(0);
+ return 0;
+ }
+ return 0;
+ }
+yy21: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy22: switch(yych){
+ case ' ': goto yy21;
+ default: goto yy20;
+ }
+yy23: yych = *++YYCURSOR;
+ switch(yych){
+ case '\n': goto yy19;
+ default: goto yy16;
+ }
+yy24: yych = *++YYCURSOR;
+ switch(yych){
+ case '-': goto yy25;
+ default: goto yy16;
+ }
+yy25: yych = *++YYCURSOR;
+ switch(yych){
+ case '\n': goto yy26;
+ case '\r': goto yy30;
+ case ' ': goto yy28;
+ default: goto yy16;
+ }
+yy26: yych = *++YYCURSOR;
+yy27:
+#line 259
+ { SyckLevel *lvl = CURRENT_LEVEL();
+ if ( lvl->status == syck_lvl_header )
+ {
+ YYPOS(3);
+ goto Directive;
+ }
+ else
+ {
+ ENSURE_YAML_IEND(lvl, -1);
+ YYPOS(0);
+ return 0;
+ }
+ }
+yy28: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy29: switch(yych){
+ case ' ': goto yy28;
+ default: goto yy27;
+ }
+yy30: yych = *++YYCURSOR;
+ switch(yych){
+ case '\n': goto yy26;
+ default: goto yy16;
+ }
+}
+#line 306
+
+
+Document:
+ {
+ SyckLevel *lvl = CURRENT_LEVEL();
+ if ( lvl->status == syck_lvl_header )
+ {
+ lvl->status = syck_lvl_doc;
+ }
+
+ YYTOKEN = YYCURSOR;
+
+{
+ YYCTYPE yych;
+ unsigned int yyaccept;
+ goto yy31;
+yy32: ++YYCURSOR;
+yy31:
+ if((YYLIMIT - YYCURSOR) < 3) YYFILL(3);
+ yych = *YYCURSOR;
+ switch(yych){
+ case '\000': goto yy56;
+ case '\n': goto yy33;
+ case '\r': goto yy35;
+ case ' ': goto yy54;
+ case '!': goto yy45;
+ case '"': goto yy49;
+ case '#': goto yy52;
+ case '&': goto yy43;
+ case '\'': goto yy47;
+ case '*': goto yy44;
+ case ',': case ':': goto yy41;
+ case '-': case '?': goto yy42;
+ case '>': case '|': goto yy51;
+ case '[': case '{': goto yy37;
+ case ']': case '}': goto yy39;
+ default: goto yy58;
+ }
+yy33: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ goto yy86;
+yy34:
+#line 320
+ { /* Isolate spaces */
+ int indt_len;
+ GOBBLE_UP_YAML_INDENT( indt_len, YYTOKEN );
+ lvl = CURRENT_LEVEL();
+
+ /* Check for open indent */
+ ENSURE_YAML_IEND(lvl, indt_len);
+ ENSURE_YAML_IOPEN(lvl, indt_len, 0);
+ if ( indt_len == -1 )
+ {
+ return 0;
+ }
+ return YAML_INDENT;
+ }
+yy35: yych = *++YYCURSOR;
+ switch(yych){
+ case '\n': goto yy85;
+ default: goto yy36;
+ }
+yy36:
+#line 407
+ { ENSURE_YAML_IOPEN(lvl, 0, 1);
+ goto Plain;
+ }
+yy37: yych = *++YYCURSOR;
+yy38:
+#line 335
+ { ENSURE_YAML_IOPEN(lvl, 0, 1);
+ lvl = CURRENT_LEVEL();
+ ADD_LEVEL(lvl->spaces + 1, syck_lvl_inline);
+ return YYTOKEN[0];
+ }
+yy39: yych = *++YYCURSOR;
+yy40:
+#line 341
+ { POP_LEVEL();
+ return YYTOKEN[0];
+ }
+yy41: yyaccept = 1;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case '\n': goto yy80;
+ case '\r': goto yy84;
+ case ' ': goto yy82;
+ default: goto yy36;
+ }
+yy42: yyaccept = 1;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case '\n': goto yy75;
+ case '\r': goto yy79;
+ case ' ': goto yy77;
+ default: goto yy36;
+ }
+yy43: yych = *++YYCURSOR;
+ switch(yych){
+ case '-': case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z': case '_': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z': goto yy72;
+ default: goto yy36;
+ }
+yy44: yych = *++YYCURSOR;
+ switch(yych){
+ case '-': case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z': case '_': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z': goto yy69;
+ default: goto yy36;
+ }
+yy45: yych = *++YYCURSOR;
+yy46:
+#line 380
+ { ENSURE_YAML_IOPEN(lvl, 0, 1);
+ goto TransferMethod; }
+yy47: yych = *++YYCURSOR;
+yy48:
+#line 383
+ { ENSURE_YAML_IOPEN(lvl, 0, 1);
+ goto SingleQuote; }
+yy49: yych = *++YYCURSOR;
+yy50:
+#line 386
+ { ENSURE_YAML_IOPEN(lvl, 0, 1);
+ goto DoubleQuote; }
+yy51: yyaccept = 1;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case '\n': goto yy64;
+ case '\r': goto yy68;
+ case ' ': goto yy66;
+ case '+': case '-': case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy61;
+ default: goto yy36;
+ }
+yy52: yych = *++YYCURSOR;
+yy53:
+#line 396
+ { eat_comments( parser );
+ goto Document;
+ }
+yy54: yych = *++YYCURSOR;
+ goto yy60;
+yy55:
+#line 400
+ { goto Document; }
+yy56: yych = *++YYCURSOR;
+yy57:
+#line 402
+ { ENSURE_YAML_IEND(lvl, -1);
+ YYPOS(0);
+ return 0;
+ }
+yy58: yych = *++YYCURSOR;
+ goto yy36;
+yy59: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy60: switch(yych){
+ case ' ': goto yy59;
+ default: goto yy55;
+ }
+yy61: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy62: switch(yych){
+ case '\n': goto yy64;
+ case '\r': goto yy68;
+ case ' ': goto yy66;
+ case '+': case '-': case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': goto yy61;
+ default: goto yy63;
+ }
+yy63: YYCURSOR = YYMARKER;
+ switch(yyaccept){
+ case 0: goto yy34;
+ case 1: goto yy36;
+ }
+yy64: yych = *++YYCURSOR;
+yy65:
+#line 389
+ { if ( is_newline( YYCURSOR - 1 ) )
+ {
+ YYCURSOR--;
+ }
+ goto ScalarBlock;
+ }
+yy66: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy67: switch(yych){
+ case ' ': goto yy66;
+ default: goto yy65;
+ }
+yy68: yych = *++YYCURSOR;
+ switch(yych){
+ case '\n': goto yy64;
+ default: goto yy63;
+ }
+yy69: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy70: switch(yych){
+ case '-': case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z': case '_': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z': goto yy69;
+ default: goto yy71;
+ }
+yy71:
+#line 375
+ { ENSURE_YAML_IOPEN(lvl, 0, 1);
+ sycklval->name = syck_strndup( YYTOKEN + 1, YYCURSOR - YYTOKEN - 1 );
+ return YAML_ALIAS;
+ }
+yy72: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy73: switch(yych){
+ case '-': case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z': case '_': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z': goto yy72;
+ default: goto yy74;
+ }
+yy74:
+#line 363
+ { ENSURE_YAML_IOPEN(lvl, 0, 1);
+ sycklval->name = syck_strndup( YYTOKEN + 1, YYCURSOR - YYTOKEN - 1 );
+
+ /*
+ * Remove previous anchors of the same name. Since the parser will likely
+ * construct deeper nodes first, we want those nodes to be placed in the
+ * queue for matching at a higher level of indentation.
+ */
+ syck_hdlr_remove_anchor(parser, sycklval->name);
+ return YAML_ANCHOR;
+ }
+yy75: yych = *++YYCURSOR;
+yy76:
+#line 349
+ { ENSURE_YAML_IOPEN(lvl, YYTOKEN - YYLINEPTR, 1);
+ FORCE_NEXT_TOKEN(YAML_IOPEN);
+ if ( is_newline( YYCURSOR ) || is_newline( YYCURSOR - 1 ) )
+ {
+ YYCURSOR--;
+ ADD_LEVEL((YYTOKEN + 1) - YYLINEPTR, syck_lvl_doc);
+ }
+ else
+ {
+ ADD_LEVEL(YYCURSOR - YYLINEPTR, syck_lvl_doc);
+ }
+ return YYTOKEN[0];
+ }
+yy77: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy78: switch(yych){
+ case ' ': goto yy77;
+ default: goto yy76;
+ }
+yy79: yych = *++YYCURSOR;
+ switch(yych){
+ case '\n': goto yy75;
+ default: goto yy63;
+ }
+yy80: yych = *++YYCURSOR;
+yy81:
+#line 345
+ { YYPOS(1);
+ return YYTOKEN[0];
+ }
+yy82: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy83: switch(yych){
+ case ' ': goto yy82;
+ default: goto yy81;
+ }
+yy84: yych = *++YYCURSOR;
+ switch(yych){
+ case '\n': goto yy80;
+ default: goto yy63;
+ }
+yy85: yyaccept = 0;
+ YYMARKER = ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy86: switch(yych){
+ case '\n': case ' ': goto yy85;
+ case '\r': goto yy87;
+ default: goto yy34;
+ }
+yy87: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch(yych){
+ case '\n': goto yy85;
+ default: goto yy63;
+ }
+}
+#line 411
+
+ }
+
+Directive:
+ {
+ YYTOKTMP = YYCURSOR;
+
+{
+ YYCTYPE yych;
+ unsigned int yyaccept;
+ goto yy88;
+yy89: ++YYCURSOR;
+yy88:
+ if((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
+ yych = *YYCURSOR;
+ switch(yych){
+ case '\000': goto yy90;
+ case ' ': goto yy93;
+ case '%': goto yy91;
+ default: goto yy95;
+ }
+yy90: YYCURSOR = YYMARKER;
+ switch(yyaccept){
+ case 0: goto yy92;
+ }
+yy91: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case '.':
+ case '/':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case ':':
+ case ';':
+ case '<':
+ case '=':
+ case '>':
+ case '?':
+ case '@':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ case '[':
+ case '\\':
+ case ']':
+ case '^':
+ case '_': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z': goto yy98;
+ default: goto yy92;
+ }
+yy92:
+#line 424
+ { YYCURSOR = YYTOKTMP;
+ return YAML_DOCSEP;
+ }
+yy93: yych = *++YYCURSOR;
+ goto yy97;
+yy94:
+#line 422
+ { goto Directive; }
+yy95: yych = *++YYCURSOR;
+ goto yy92;
+yy96: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy97: switch(yych){
+ case ' ': goto yy96;
+ default: goto yy94;
+ }
+yy98: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy99: switch(yych){
+ case '.':
+ case '/':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case ';':
+ case '<':
+ case '=':
+ case '>':
+ case '?':
+ case '@':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ case '[':
+ case '\\':
+ case ']':
+ case '^':
+ case '_': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z': goto yy98;
+ case ':': goto yy100;
+ default: goto yy90;
+ }
+yy100: yych = *++YYCURSOR;
+ switch(yych){
+ case '.':
+ case '/':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case ':':
+ case ';':
+ case '<':
+ case '=':
+ case '>':
+ case '?':
+ case '@':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ case '[':
+ case '\\':
+ case ']':
+ case '^':
+ case '_': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z': goto yy101;
+ default: goto yy90;
+ }
+yy101: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy102: switch(yych){
+ case '.':
+ case '/':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case ':':
+ case ';':
+ case '<':
+ case '=':
+ case '>':
+ case '?':
+ case '@':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ case '[':
+ case '\\':
+ case ']':
+ case '^':
+ case '_': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z': goto yy101;
+ default: goto yy103;
+ }
+yy103:
+#line 420
+ { goto Directive; }
+}
+#line 427
+
+
+ }
+
+Plain:
+ {
+ int qidx = 0;
+ int qcapa = 100;
+ char *qstr = S_ALLOC_N( char, qcapa );
+ SyckLevel *plvl;
+ int parentIndent;
+
+ YYCURSOR = YYTOKEN;
+ plvl = CURRENT_LEVEL();
+ GET_TRUE_YAML_INDENT(parentIndent);
+
+Plain2:
+ YYTOKTMP = YYCURSOR;
+
+Plain3:
+
+{
+ YYCTYPE yych;
+ unsigned int yyaccept;
+ goto yy104;
+yy105: ++YYCURSOR;
+yy104:
+ if((YYLIMIT - YYCURSOR) < 3) YYFILL(3);
+ yych = *YYCURSOR;
+ switch(yych){
+ case '\000': goto yy116;
+ case '\n': goto yy106;
+ case '\r': goto yy108;
+ case ' ': goto yy114;
+ case ',': goto yy113;
+ case ':': goto yy110;
+ case ']': case '}': goto yy111;
+ default: goto yy118;
+ }
+yy106: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ goto yy132;
+yy107:
+#line 450
+ { int indt_len, nl_count = 0;
+ SyckLevel *lvl;
+ char *tok = YYTOKTMP;
+ GOBBLE_UP_YAML_INDENT( indt_len, tok );
+ lvl = CURRENT_LEVEL();
+
+ if ( indt_len <= parentIndent )
+ {
+ RETURN_IMPLICIT();
+ }
+
+ while ( YYTOKTMP < YYCURSOR )
+ {
+ if ( is_newline( YYTOKTMP++ ) )
+ nl_count++;
+ }
+ if ( nl_count <= 1 )
+ {
+ QUOTECAT(qstr, qcapa, qidx, ' ');
+ }
+ else
+ {
+ int i;
+ for ( i = 0; i < nl_count - 1; i++ )
+ {
+ QUOTECAT(qstr, qcapa, qidx, '\n');
+ }
+ }
+
+ goto Plain2;
+ }
+yy108: yych = *++YYCURSOR;
+ switch(yych){
+ case '\n': goto yy131;
+ default: goto yy109;
+ }
+yy109:
+#line 504
+ { QUOTECATS(qstr, qcapa, qidx, YYTOKTMP, YYCURSOR - YYTOKTMP);
+ goto Plain2;
+ }
+yy110: yyaccept = 1;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case '\n': goto yy126;
+ case '\r': goto yy130;
+ case ' ': goto yy128;
+ default: goto yy109;
+ }
+yy111: yych = *++YYCURSOR;
+yy112:
+#line 484
+ { if ( plvl->status != syck_lvl_inline )
+ {
+ if ( *(YYCURSOR - 1) == ' ' || is_newline( YYCURSOR - 1 ) )
+ {
+ YYCURSOR--;
+ }
+ QUOTECATS(qstr, qcapa, qidx, YYTOKTMP, YYCURSOR - YYTOKTMP);
+ goto Plain2;
+ }
+ RETURN_IMPLICIT();
+ }
+yy113: yyaccept = 1;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case '\n': goto yy121;
+ case '\r': goto yy124;
+ case ' ': goto yy122;
+ default: goto yy109;
+ }
+yy114: yych = *++YYCURSOR;
+ switch(yych){
+ case '#': goto yy119;
+ default: goto yy115;
+ }
+yy115:
+#line 502
+ { goto Plain3; }
+yy116: yych = *++YYCURSOR;
+yy117:
+#line 500
+ { RETURN_IMPLICIT(); }
+yy118: yych = *++YYCURSOR;
+ goto yy109;
+yy119: yych = *++YYCURSOR;
+yy120:
+#line 496
+ { eat_comments( parser );
+ RETURN_IMPLICIT();
+ }
+yy121: yych = *++YYCURSOR;
+ goto yy112;
+yy122: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy123: switch(yych){
+ case ' ': goto yy122;
+ default: goto yy112;
+ }
+yy124: yych = *++YYCURSOR;
+ switch(yych){
+ case '\n': goto yy121;
+ default: goto yy125;
+ }
+yy125: YYCURSOR = YYMARKER;
+ switch(yyaccept){
+ case 0: goto yy107;
+ case 1: goto yy109;
+ }
+yy126: yych = *++YYCURSOR;
+yy127:
+#line 482
+ { RETURN_IMPLICIT(); }
+yy128: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy129: switch(yych){
+ case ' ': goto yy128;
+ default: goto yy127;
+ }
+yy130: yych = *++YYCURSOR;
+ switch(yych){
+ case '\n': goto yy126;
+ default: goto yy125;
+ }
+yy131: yyaccept = 0;
+ YYMARKER = ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy132: switch(yych){
+ case '\n': case ' ': goto yy131;
+ case '\r': goto yy133;
+ default: goto yy107;
+ }
+yy133: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch(yych){
+ case '\n': goto yy131;
+ default: goto yy125;
+ }
+}
+#line 508
+
+ }
+
+SingleQuote:
+ {
+ int qidx = 0;
+ int qcapa = 100;
+ char *qstr = S_ALLOC_N( char, qcapa );
+
+SingleQuote2:
+ YYTOKTMP = YYCURSOR;
+
+{
+ YYCTYPE yych;
+ unsigned int yyaccept;
+ goto yy134;
+yy135: ++YYCURSOR;
+yy134:
+ if((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
+ yych = *YYCURSOR;
+ switch(yych){
+ case '\000': goto yy142;
+ case '\n': goto yy136;
+ case '\r': goto yy138;
+ case '\'': goto yy140;
+ default: goto yy143;
+ }
+yy136: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ goto yy147;
+yy137:
+#line 522
+ { int indt_len;
+ int nl_count = 0;
+ SyckLevel *lvl;
+ GOBBLE_UP_YAML_INDENT( indt_len, YYTOKTMP );
+ lvl = CURRENT_LEVEL();
+
+ if ( lvl->status != syck_lvl_str )
+ {
+ ADD_LEVEL( indt_len, syck_lvl_str );
+ }
+ else if ( indt_len < lvl->spaces )
+ {
+ /* Error! */
+ }
+
+ while ( YYTOKTMP < YYCURSOR )
+ {
+ if ( is_newline( YYTOKTMP++ ) )
+ nl_count++;
+ }
+ if ( nl_count <= 1 )
+ {
+ QUOTECAT(qstr, qcapa, qidx, ' ');
+ }
+ else
+ {
+ int i;
+ for ( i = 0; i < nl_count - 1; i++ )
+ {
+ QUOTECAT(qstr, qcapa, qidx, '\n');
+ }
+ }
+
+ goto SingleQuote2;
+ }
+yy138: yych = *++YYCURSOR;
+ switch(yych){
+ case '\n': goto yy146;
+ default: goto yy139;
+ }
+yy139:
+#line 576
+ { QUOTECAT(qstr, qcapa, qidx, *(YYCURSOR - 1));
+ goto SingleQuote2;
+ }
+yy140: yych = *++YYCURSOR;
+ switch(yych){
+ case '\'': goto yy144;
+ default: goto yy141;
+ }
+yy141:
+#line 562
+ { SyckLevel *lvl;
+ SyckNode *n = syck_alloc_str();
+ lvl = CURRENT_LEVEL();
+
+ if ( lvl->status == syck_lvl_str )
+ {
+ POP_LEVEL();
+ }
+ n->data.str->ptr = qstr;
+ n->data.str->len = qidx;
+ sycklval->nodeData = n;
+ return YAML_PLAIN;
+ }
+yy142: yych = *++YYCURSOR;
+ goto yy141;
+yy143: yych = *++YYCURSOR;
+ goto yy139;
+yy144: yych = *++YYCURSOR;
+yy145:
+#line 558
+ { QUOTECAT(qstr, qcapa, qidx, '\'');
+ goto SingleQuote2;
+ }
+yy146: yyaccept = 0;
+ YYMARKER = ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy147: switch(yych){
+ case '\n': case ' ': goto yy146;
+ case '\r': goto yy148;
+ default: goto yy137;
+ }
+yy148: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch(yych){
+ case '\n': goto yy146;
+ default: goto yy149;
+ }
+yy149: YYCURSOR = YYMARKER;
+ switch(yyaccept){
+ case 0: goto yy137;
+ }
+}
+#line 580
+
+
+ }
+
+
+DoubleQuote:
+ {
+ int keep_nl = 1;
+ int qidx = 0;
+ int qcapa = 100;
+ char *qstr = S_ALLOC_N( char, qcapa );
+
+DoubleQuote2:
+ YYTOKTMP = YYCURSOR;
+
+
+{
+ YYCTYPE yych;
+ unsigned int yyaccept;
+ goto yy150;
+yy151: ++YYCURSOR;
+yy150:
+ if((YYLIMIT - YYCURSOR) < 4) YYFILL(4);
+ yych = *YYCURSOR;
+ switch(yych){
+ case '\000': goto yy157;
+ case '\n': goto yy152;
+ case '\r': goto yy154;
+ case '"': goto yy159;
+ case '\\': goto yy156;
+ default: goto yy160;
+ }
+yy152: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ goto yy174;
+yy153:
+#line 598
+ { int indt_len;
+ int nl_count = 0;
+ SyckLevel *lvl;
+ GOBBLE_UP_YAML_INDENT( indt_len, YYTOKTMP );
+ lvl = CURRENT_LEVEL();
+
+ if ( lvl->status != syck_lvl_str )
+ {
+ ADD_LEVEL( indt_len, syck_lvl_str );
+ }
+ else if ( indt_len < lvl->spaces )
+ {
+ /* FIXME */
+ }
+
+ if ( keep_nl == 1 )
+ {
+ while ( YYTOKTMP < YYCURSOR )
+ {
+ if ( is_newline( YYTOKTMP++ ) )
+ nl_count++;
+ }
+ if ( nl_count <= 1 )
+ {
+ QUOTECAT(qstr, qcapa, qidx, ' ');
+ }
+ else
+ {
+ int i;
+ for ( i = 0; i < nl_count - 1; i++ )
+ {
+ QUOTECAT(qstr, qcapa, qidx, '\n');
+ }
+ }
+ }
+
+ keep_nl = 1;
+ goto DoubleQuote2;
+ }
+yy154: yych = *++YYCURSOR;
+ switch(yych){
+ case '\n': goto yy173;
+ default: goto yy155;
+ }
+yy155:
+#line 682
+ { QUOTECAT(qstr, qcapa, qidx, *(YYCURSOR - 1));
+ goto DoubleQuote2;
+ }
+yy156: yyaccept = 1;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case '\n': goto yy164;
+ case '\r': goto yy166;
+ case ' ': goto yy161;
+ case '"': case '\\': case 'a':
+ case 'b': case 'e':
+ case 'f': case 'n': case 'r': case 't': case 'v': goto yy168;
+ case 'x': goto yy167;
+ default: goto yy155;
+ }
+yy157: yych = *++YYCURSOR;
+yy158:
+#line 668
+ { SyckLevel *lvl;
+ SyckNode *n = syck_alloc_str();
+ lvl = CURRENT_LEVEL();
+
+ if ( lvl->status == syck_lvl_str )
+ {
+ POP_LEVEL();
+ }
+ n->data.str->ptr = qstr;
+ n->data.str->len = qidx;
+ sycklval->nodeData = n;
+ return YAML_PLAIN;
+ }
+yy159: yych = *++YYCURSOR;
+ goto yy158;
+yy160: yych = *++YYCURSOR;
+ goto yy155;
+yy161: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy162: switch(yych){
+ case '\n': goto yy164;
+ case '\r': goto yy166;
+ case ' ': goto yy161;
+ default: goto yy163;
+ }
+yy163: YYCURSOR = YYMARKER;
+ switch(yyaccept){
+ case 0: goto yy153;
+ case 1: goto yy155;
+ }
+yy164: yych = *++YYCURSOR;
+yy165:
+#line 663
+ { keep_nl = 0;
+ YYCURSOR--;
+ goto DoubleQuote2;
+ }
+yy166: yych = *++YYCURSOR;
+ switch(yych){
+ case '\n': goto yy164;
+ default: goto yy163;
+ }
+yy167: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f': goto yy170;
+ default: goto yy163;
+ }
+yy168: yych = *++YYCURSOR;
+yy169:
+#line 638
+ { char ch = *( YYCURSOR - 1 );
+ switch ( ch )
+ {
+ case 'a': ch = 7; break;
+ case 'b': ch = '\010'; break;
+ case 'e': ch = '\033'; break;
+ case 'f': ch = '\014'; break;
+ case 'n': ch = '\n'; break;
+ case 'r': ch = '\015'; break;
+ case 't': ch = '\t'; break;
+ case 'v': ch = '\013'; break;
+ }
+ QUOTECAT(qstr, qcapa, qidx, ch);
+ goto DoubleQuote2;
+ }
+yy170: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f': goto yy171;
+ default: goto yy163;
+ }
+yy171: yych = *++YYCURSOR;
+yy172:
+#line 654
+ { long ch;
+ char *chr_text = syck_strndup( YYTOKTMP, 4 );
+ chr_text[0] = '0';
+ ch = strtol( chr_text, NULL, 16 );
+ free( chr_text );
+ QUOTECAT(qstr, qcapa, qidx, ch);
+ goto DoubleQuote2;
+ }
+yy173: yyaccept = 0;
+ YYMARKER = ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy174: switch(yych){
+ case '\n': case ' ': goto yy173;
+ case '\r': goto yy175;
+ default: goto yy153;
+ }
+yy175: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch(yych){
+ case '\n': goto yy173;
+ default: goto yy163;
+ }
+}
+#line 686
+
+ }
+
+TransferMethod:
+ {
+ int qidx = 0;
+ int qcapa = 100;
+ char *qstr = S_ALLOC_N( char, qcapa );
+
+TransferMethod2:
+ YYTOKTMP = YYCURSOR;
+
+{
+ YYCTYPE yych;
+ unsigned int yyaccept;
+ goto yy176;
+yy177: ++YYCURSOR;
+yy176:
+ if((YYLIMIT - YYCURSOR) < 4) YYFILL(4);
+ yych = *YYCURSOR;
+ switch(yych){
+ case '\000': goto yy178;
+ case '\n': goto yy179;
+ case '\r': goto yy182;
+ case ' ': goto yy181;
+ case '\\': goto yy184;
+ default: goto yy185;
+ }
+yy178: YYCURSOR = YYMARKER;
+ switch(yyaccept){
+ case 0: goto yy183;
+ }
+yy179: yych = *++YYCURSOR;
+yy180:
+#line 700
+ { SyckLevel *lvl;
+ YYCURSOR = YYTOKTMP;
+ if ( YYCURSOR == YYTOKEN + 1 )
+ {
+ free( qstr );
+ return YAML_ITRANSFER;
+ }
+
+ lvl = CURRENT_LEVEL();
+
+ /*
+ * URL Prefixing
+ */
+ if ( *qstr == '^' )
+ {
+ sycklval->name = S_ALLOC_N( char, qidx + strlen( lvl->domain ) );
+ sycklval->name[0] = '\0';
+ strcat( sycklval->name, lvl->domain );
+ strncat( sycklval->name, qstr + 1, qidx - 1 );
+ free( qstr );
+ }
+ else
+ {
+ char *carat = qstr;
+ char *qend = qstr + qidx;
+ while ( (++carat) < qend )
+ {
+ if ( *carat == '^' )
+ break;
+ }
+
+ if ( carat < qend )
+ {
+ free( lvl->domain );
+ lvl->domain = syck_strndup( qstr, carat - qstr );
+ sycklval->name = S_ALLOC_N( char, ( qend - carat ) + strlen( lvl->domain ) );
+ sycklval->name[0] = '\0';
+ strcat( sycklval->name, lvl->domain );
+ strncat( sycklval->name, carat + 1, ( qend - carat ) - 1 );
+ free( qstr );
+ }
+ else
+ {
+ sycklval->name = qstr;
+ }
+ }
+
+ return YAML_TRANSFER;
+ }
+yy181: yych = *++YYCURSOR;
+ goto yy192;
+yy182: yych = *++YYCURSOR;
+ switch(yych){
+ case '\n': goto yy190;
+ default: goto yy183;
+ }
+yy183:
+#line 762
+ { QUOTECAT(qstr, qcapa, qidx, *(YYCURSOR - 1));
+ goto TransferMethod2;
+ }
+yy184: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ switch(yych){
+ case 'x': goto yy186;
+ default: goto yy183;
+ }
+yy185: yych = *++YYCURSOR;
+ goto yy183;
+yy186: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f': goto yy187;
+ default: goto yy178;
+ }
+yy187: yych = *++YYCURSOR;
+ switch(yych){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F': case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f': goto yy188;
+ default: goto yy178;
+ }
+yy188: yych = *++YYCURSOR;
+yy189:
+#line 753
+ { long ch;
+ char *chr_text = syck_strndup( YYTOKTMP, 4 );
+ chr_text[0] = '0';
+ ch = strtol( chr_text, NULL, 16 );
+ free( chr_text );
+ QUOTECAT(qstr, qcapa, qidx, ch);
+ goto TransferMethod2;
+ }
+yy190: yych = *++YYCURSOR;
+ goto yy180;
+yy191: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy192: switch(yych){
+ case ' ': goto yy191;
+ default: goto yy180;
+ }
+}
+#line 767
+
+ }
+
+ScalarBlock:
+ {
+ int qidx = 0;
+ int qcapa = 100;
+ char *qstr = S_ALLOC_N( char, qcapa );
+ int blockType = 0;
+ int nlDoWhat = 0;
+ int lastIndent = 0;
+ int forceIndent = -1;
+ char *yyt = YYTOKEN;
+ SyckLevel *lvl = CURRENT_LEVEL();
+ int parentIndent;
+ GET_TRUE_YAML_INDENT(parentIndent);
+
+ switch ( *yyt )
+ {
+ case '|': blockType = BLOCK_LIT; break;
+ case '>': blockType = BLOCK_FOLD; break;
+ }
+
+ while ( ++yyt <= YYCURSOR )
+ {
+ if ( *yyt == '-' )
+ {
+ nlDoWhat = NL_CHOMP;
+ }
+ else if ( *yyt == '+' )
+ {
+ nlDoWhat = NL_KEEP;
+ }
+ else if ( isdigit( *yyt ) )
+ {
+ forceIndent = strtol( yyt, NULL, 10 ) + parentIndent;
+ }
+ }
+
+ qstr[0] = '\0';
+ YYTOKEN = YYCURSOR;
+
+ScalarBlock2:
+ YYTOKTMP = YYCURSOR;
+
+{
+ YYCTYPE yych;
+ unsigned int yyaccept;
+ goto yy193;
+yy194: ++YYCURSOR;
+yy193:
+ if((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
+ yych = *YYCURSOR;
+ switch(yych){
+ case '\000': goto yy201;
+ case '\n': goto yy195;
+ case '\r': goto yy197;
+ case '#': goto yy199;
+ default: goto yy203;
+ }
+yy195: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ goto yy205;
+yy196:
+#line 814
+ { char *pacer;
+ char *tok = YYTOKTMP;
+ int indt_len = 0, nl_count = 0, fold_nl = 0, nl_begin = 0;
+ GOBBLE_UP_YAML_INDENT( indt_len, tok );
+ lvl = CURRENT_LEVEL();
+
+ if ( indt_len > parentIndent && lvl->status != syck_lvl_block )
+ {
+ int new_spaces = forceIndent > 0 ? forceIndent : indt_len;
+ ADD_LEVEL( new_spaces, syck_lvl_block );
+ lastIndent = indt_len - new_spaces;
+ nl_begin = 1;
+ lvl = CURRENT_LEVEL();
+ }
+ else if ( lvl->status != syck_lvl_block )
+ {
+ YYCURSOR = YYTOKTMP;
+ RETURN_YAML_BLOCK();
+ }
+
+ /*
+ * Fold only in the event of two lines being on the leftmost
+ * indentation.
+ */
+ if ( blockType == BLOCK_FOLD && lastIndent == 0 && ( indt_len - lvl->spaces ) == 0 )
+ {
+ fold_nl = 1;
+ }
+
+ pacer = YYTOKTMP;
+ while ( pacer < YYCURSOR )
+ {
+ if ( is_newline( pacer++ ) )
+ nl_count++;
+ }
+
+ if ( fold_nl == 1 || nl_begin == 1 )
+ {
+ nl_count--;
+ }
+
+ if ( nl_count < 1 && nl_begin == 0 )
+ {
+ QUOTECAT(qstr, qcapa, qidx, ' ');
+ }
+ else
+ {
+ int i;
+ for ( i = 0; i < nl_count; i++ )
+ {
+ QUOTECAT(qstr, qcapa, qidx, '\n');
+ }
+ }
+
+ lastIndent = indt_len - lvl->spaces;
+ YYCURSOR -= lastIndent;
+
+ if ( indt_len < lvl->spaces )
+ {
+ POP_LEVEL();
+ YYCURSOR = YYTOKTMP;
+ RETURN_YAML_BLOCK();
+ }
+ goto ScalarBlock2;
+ }
+yy197: yych = *++YYCURSOR;
+ switch(yych){
+ case '\n': goto yy204;
+ default: goto yy198;
+ }
+yy198:
+#line 900
+ { QUOTECAT(qstr, qcapa, qidx, *YYTOKTMP);
+ goto ScalarBlock2;
+ }
+yy199: yych = *++YYCURSOR;
+yy200:
+#line 881
+ { lvl = CURRENT_LEVEL();
+ if ( lvl->status != syck_lvl_block )
+ {
+ eat_comments( parser );
+ YYTOKTMP = YYCURSOR;
+ }
+ else
+ {
+ QUOTECAT(qstr, qcapa, qidx, *YYTOKTMP);
+ }
+ goto ScalarBlock2;
+ }
+yy201: yych = *++YYCURSOR;
+yy202:
+#line 895
+ { YYCURSOR--;
+ POP_LEVEL();
+ RETURN_YAML_BLOCK();
+ }
+yy203: yych = *++YYCURSOR;
+ goto yy198;
+yy204: yyaccept = 0;
+ YYMARKER = ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy205: switch(yych){
+ case '\n': case ' ': goto yy204;
+ case '\r': goto yy206;
+ default: goto yy196;
+ }
+yy206: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch(yych){
+ case '\n': goto yy204;
+ default: goto yy207;
+ }
+yy207: YYCURSOR = YYMARKER;
+ switch(yyaccept){
+ case 0: goto yy196;
+ }
+}
+#line 905
+
+ }
+
+ return 0;
+
+}
+
+void
+eat_comments( SyckParser *parser )
+{
+ char *tok;
+
+Comment:
+ {
+ tok = YYCURSOR;
+
+{
+ YYCTYPE yych;
+ unsigned int yyaccept;
+ goto yy208;
+yy209: ++YYCURSOR;
+yy208:
+ if((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
+ yych = *YYCURSOR;
+ switch(yych){
+ case '\000': goto yy210;
+ case '\n': goto yy212;
+ case '\r': goto yy213;
+ default: goto yy215;
+ }
+yy210: yych = *++YYCURSOR;
+yy211:
+#line 923
+ { YYCURSOR = tok;
+ return;
+ }
+yy212: yyaccept = 0;
+ yych = *(YYMARKER = ++YYCURSOR);
+ goto yy217;
+yy213: yych = *++YYCURSOR;
+ switch(yych){
+ case '\n': goto yy216;
+ default: goto yy214;
+ }
+yy214:
+#line 927
+ { goto Comment;
+ }
+yy215: yych = *++YYCURSOR;
+ goto yy214;
+yy216: yyaccept = 0;
+ YYMARKER = ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+yy217: switch(yych){
+ case '\n': goto yy216;
+ case '\r': goto yy218;
+ default: goto yy211;
+ }
+yy218: ++YYCURSOR;
+ if(YYLIMIT == YYCURSOR) YYFILL(1);
+ yych = *YYCURSOR;
+ switch(yych){
+ case '\n': goto yy216;
+ default: goto yy219;
+ }
+yy219: YYCURSOR = YYMARKER;
+ switch(yyaccept){
+ case 0: goto yy211;
+ }
+}
+#line 930
+
+
+ }
+
+}
+
+int
+is_newline( char *ptr )
+{
+ if ( *ptr == '\n' )
+ return 1;
+
+ if ( *ptr == '\r' && *( ptr + 1 ) == '\n' )
+ return 1;
+
+ return 0;
+}
+
+int
+syckwrap()
+{
+ return 1;
+}
+
+void
+syckerror( char *msg )
+{
+ if ( syck_parser_ptr->error_handler == NULL )
+ syck_parser_ptr->error_handler = syck_default_error_handler;
+
+ syck_parser_ptr->root = syck_parser_ptr->root_on_error;
+ (syck_parser_ptr->error_handler)(syck_parser_ptr, msg);
+}
+
diff --git a/ext/syslog/syslog.c b/ext/syslog/syslog.c
index 3e86569f06..5ee2c6b89a 100644
--- a/ext/syslog/syslog.c
+++ b/ext/syslog/syslog.c
@@ -54,7 +54,6 @@ static VALUE mSyslog_close(VALUE self)
static VALUE mSyslog_open(int argc, VALUE *argv, VALUE self)
{
VALUE ident, opt, fac;
- int mask;
if (syslog_opened) {
rb_raise(rb_eRuntimeError, "syslog already open");
diff --git a/ext/tcltklib/MANIFEST b/ext/tcltklib/MANIFEST
index 4e37fb900f..7337bbeba5 100644
--- a/ext/tcltklib/MANIFEST
+++ b/ext/tcltklib/MANIFEST
@@ -6,11 +6,12 @@ stubs.c
depend
extconf.rb
lib/tcltk.rb
-demo/lines1.rb
demo/lines0.tcl
+demo/lines1.rb
demo/lines2.rb
+demo/safeTk.rb
+sample/sample0.rb
sample/sample1.rb
sample/sample2.rb
sample/maru.gif
sample/batsu.gif
-sample/sample0.rb
diff --git a/ext/tcltklib/MANUAL.euc b/ext/tcltklib/MANUAL.euc
index 789e85a9de..30cfd8c107 100644
--- a/ext/tcltklib/MANUAL.euc
+++ b/ext/tcltklib/MANUAL.euc
@@ -1,4 +1,20 @@
(tof)
+ 2003/07/25 Hidetoshi NAGAI
+
+Ëܥɥ­¥å¥á¥ó¥È¤Ë¤Ï¸Å¤¤ tcltk ¥é¥¤¥Ö¥é¥ê¡¤tcltklib ¥é¥¤¥Ö¥é¥ê¤ÎÀâÌÀ
+¤¬´Þ¤Þ¤ì¤Æ¤¤¤Þ¤¹¤¬¡¤¤½¤Îµ­½ÒÆâÍÆ¤Ï¸Å¤¤¤â¤Î¤È¤Ê¤Ã¤Æ¤¤¤Þ¤¹¡¥
+
+tcltk ¥é¥¤¥Ö¥é¥ê¡Êtcltk.rb¡Ë¤Ï¸½ºß¤Ç¤Ï¥á¥ó¥Æ¥Ê¥ó¥¹¤¬»ö¼Â¾å¹Ô¤ï¤ì¤Æ
+¤¤¤Ê¤¤¤¿¤á¡¤¸Å¤¤¥É¥­¥å¥á¥ó¥È¤ÎÀâÌÀ¤¬¤½¤Î¤Þ¤ÞÍ­¸ú¤Ç¤¹¡¥¤½¤ì¤ËÂФ·¡¤
+tcltklib ¥é¥¤¥Ö¥é¥ê¤Ë¤Ä¤¤¤Æ¤Ï¡¤¸½ºß¤Î Ruby/Tk¡Êtk.rb °Ê²¼¤Î¥é¥¤¥Ö¥é
+¥ê·²¡Ë¤ò²ÔƯ¤µ¤»¤ë¤¿¤á¤ÎÃæ¿´¤È¤·¤Æ¥á¥ó¥Æ¥Ê¥ó¥¹¤µ¤ì¤Æ¤¤¤ë¤¿¤á¡¤¾¯¡¹
+°ã¤¤¤¬À¸¤¸¤Æ¤¤¤Þ¤¹¡¥
+
+¤½¤³¤Ç¡¤¤Þ¤º¸Å¤¤ÀâÌÀʸ½ñ¤ò¼¨¤·¤¿¸å¡¤¸½ºß¤Î tcltklib ¥é¥¤¥Ö¥é¥ê¤Ë¤Ä
+¤¤¤Æ¤ÎÀâÌÀ¤ò²Ã¤¨¤Þ¤¹¡¥
+
+°Ê²¼¤¬¥é¥¤¥Ö¥é¥ê¤Î¸Å¤¤ÀâÌÀʸ½ñ¤Ç¤¹¡¥
+==============================================================
MANUAL.euc
Sep. 19, 1997 Y. Shigehiro
@@ -121,4 +137,221 @@ require "tcltklib" ¤¹¤ë¤È, °Ê²¼¤Î¥â¥¸¥å¡¼¥ë, ¥¯¥é¥¹¤¬ÍøÍѲÄǽ¤Ç¤¹.
°ú¿ô: ̵¤·
Ìá¤êÃÍ (Fixnum): ľÁ°¤Î Tcl_Eval() ¤¬ÊÖ¤·¤¿ÃÍ.
+==============================================================
+
+°Ê²¼¤¬Ëܥɥ­¥å¥á¥ó¥ÈºîÀ®»þÅÀ¤Ç¤Î tcltklib ¥é¥¤¥Ö¥é¥ê¤ÎÀâÌÀ¤Ç¤¹¡¥
+==============================================================
+¥â¥¸¥å¡¼¥ë TclTkLib
+ : ¸Ä¡¹¤Î Tcl/Tk ¥¤¥ó¥¿¡¼¥×¥ê¥¿¤Ë°Í¸¤·¤Ê¤¤½èÍý ( == ¥¤¥Ù¥ó¥È¥ë¡¼
+ : ¥×¤Ë´Ø¤¹¤ë½èÍý ) ¤ò¸Æ¤Ó½Ð¤¹¥á¥½¥Ã¥É¤òÄêµÁ¤·¤¿¥â¥¸¥å¡¼¥ë¡¥
+
+ ¥â¥¸¥å¡¼¥ë TclTkLib::EventFlag
+ : do_one_event ¤ò¸Æ¤Ó½Ð¤¹ºÝ¤Î½èÍýÂоݥ¤¥Ù¥ó¥È¤ò»ØÄꤹ¤ë¤¿¤á¤Î
+ : ¥Õ¥é¥° ( WINDOW|DONT_WAIT ¤È¤¤¤¦¤è¤¦¤Ë¥Ó¥Ã¥È±é»»»Ò¤ÇÏ¢·ë¤·¤Æ
+ : »ØÄê ) ¤òÄê¿ô¤È¤·¤ÆÄêµÁ¤·¤¿¥â¥¸¥å¡¼¥ë¡¥°Ê²¼¤ÎÄê¿ô¤¬´Þ¤Þ¤ì¤ë¡¥
+
+ Äê¿ô NONE
+ : ¤¤¤«¤Ê¤ë¼ïÎà¤Î¥¤¥Ù¥ó¥È¤â½èÍýÂоݤȤ·¤Ê¤¤ ( == 0 )
+
+ Äê¿ô WINDOW
+ : window ¥¤¥Ù¥ó¥È¤ò½èÍýÂоݤȤ¹¤ë
+
+ Äê¿ô FILE
+ : file ¥¤¥Ù¥ó¥È¤ò½èÍýÂоݤȤ¹¤ë
+
+ Äê¿ô TIMER
+ : timer ¥¤¥Ù¥ó¥È¤ò½èÍýÂоݤȤ¹¤ë
+
+ Äê¿ô IDLE
+ : ¥¢¥¤¥É¥ë¥ë¡¼¥×½èÍý ( ºÆÉÁ²è¤Ê¤É¡¤Â¾¤Î¼ïÎà¤Î¥¤¥Ù¥ó¥È¤¬È¯À¸
+ : ¤·¤Æ¤¤¤Ê¤¤¤È¤­¤Ë¹Ô¤ï¤ì¤ë½èÍý ) ¤ò½èÍýÂоݤȤ¹¤ë
+
+ Äê¿ô ALL
+ : ¤¹¤Ù¤Æ¤Î¼ïÎà¤Î¥¤¥Ù¥ó¥È¤ò½èÍýÂоݤȤ¹¤ë
+ : WINDOW|FILE|TIMER|IDLE ¤ÈƱ¤¸
+
+ Äê¿ô DONT_WAIT
+ : ½èÍýÂоݥ¤¥Ù¥ó¥È¤¬Â¸ºß¤·¤Ê¤¤¾ì¹ç¤Ë¡¤¥¤¥Ù¥ó¥ÈȯÀ¸¤òÂÔ¤¿¤º
+ : ¤Ë do_one_event ¤ò½ªÎ» ( false ¤òÊÖ¤¹ ) ¤¹¤ë
+
+ ¥â¥¸¥å¡¼¥ë¥á¥½¥Ã¥É
+ mainloop(check_root = true)
+ : ¥¤¥Ù¥ó¥È¥ë¡¼¥×¤òµ¯Æ°¤¹¤ë¡¥check_root ¤¬ true ¤Ç¤¢¤ì¤Ð¡¤
+ : root widget ¤¬Â¸ºß¤¹¤ë¸Â¤ê¡¤¤³¤Î¥á¥½¥Ã¥É¤Ï½ªÎ»¤·¤Ê¤¤¡¥
+ : check_root ¤¬ false ¤Î¾ì¹ç¤Ï¡¤root widget ¤¬¾ÃÌǤ·¤Æ¤â
+ : ¤³¤Î¥á¥½¥Ã¥É¤Ï½ªÎ»¤·¤Ê¤¤ ( root widget ¤¬¾ÃÌǤ·¤Æ¤â¡¤
+ : WINDOW °Ê³°¤Î¥¤¥Ù¥ó¥È¤ÏȯÀ¸¤·¤¦¤ë¤¿¤á )¡¥½ªÎ»¤Ë¤Ï¡¤³°Éô
+ : ¤«¤é¤ÎƯ¤­³Ý¤± ( ¥¹¥ì¥Ã¥É¤ò³èÍѤ¹¤ë¤Ê¤É ) ¤¬É¬Íס¥
+
+ mainloop_watchdog(check_root = true)
+ : Ä̾ï¤Î¥¤¥Ù¥ó¥È¥ë¡¼¥×¤Ç¤Ï¡¤¥¤¥Ù¥ó¥È½èÍý¤ÎÆâÍÆ¤Ë¤è¤Ã¤Æ¤Ï
+ : ¥Ç¥Ã¥É¥í¥Ã¥¯¤ò°ú¤­µ¯¤³¤¹²ÄǽÀ­¤¬¤¢¤ë (Î㤨¤Ð¥¤¥Ù¥ó¥È¤Ë
+ : ÂФ¹¤ë¥³¡¼¥ë¥Ð¥Ã¥¯½èÍýÃæ¤Ç widget Áàºî¤ò¤·¡¤¤½¤Î½ªÎ»¤ò
+ : ÂԤĤʤÉ)¡¥¤³¤Î¥á¥½¥Ã¥É¤Ï¡¤¤½¤¦¤·¤¿¥Ç¥Ã¥É¥í¥Ã¥¯¤ò²óÈò¤¹
+ : ¤ë¤¿¤á¤Î´Æ»ë¥¹¥ì¥Ã¥ÉÉÕ¤­¤Ç¥¤¥Ù¥ó¥È¥ë¡¼¥×¤òµ¯Æ°¤¹¤ë
+ : ( ´Æ»ë¥¹¥ì¥Ã¥É¤òÀ¸À®¤·¤¿¸å¤Ë¥¤¥Ù¥ó¥È¥ë¡¼¥×¤ò¼Â¹Ô¤¹¤ë )¡¥
+ : °ú¿ô¤Î°ÕÌ£¤Ï mainloop ¤ÈƱ¤¸¤Ç¤¢¤ë¡¥
+
+ do_one_event(flag = TclTkLib::EventFlag::ALL)
+ : ½èÍýÂÔ¤Á¤Î¥¤¥Ù¥ó¥È 1 ¸Ä¤ò¼Â¹Ô¤¹¤ë¡¥
+ : ¥¤¥Ù¥ó¥È¤ò½èÍý¤·¤¿¾ì¹ç¤Ï true ¤òÊÖ¤¹¡¥
+ : ¥Õ¥é¥°¤Ç DONT_WAIT ¤ò»ØÄꤷ¤Æ¤¤¤Ê¤¤¾ì¹ç¡¤¥Õ¥é¥°¤Ç½èÍýÂÐ
+ : ¾Ý¤È¤Ê¤Ã¤Æ¤¤¤ë¼ïÎà¤Î¥¤¥Ù¥ó¥È¤¬È¯À¸¤¹¤ë¤Þ¤ÇÂÔ¤Á³¤±¤ë¡¥
+ : DONT_WAIT ¤ò»ØÄꤷ¤Æ¤¤¤¿¾ì¹ç¡¤½èÍýÂоݥ¤¥Ù¥ó¥È¤¬¤Ê¤¯¤Æ¤â
+ : ¤¹¤°¤Ë½ªÎ»¤· false ¤òÊÖ¤¹¡¥
+
+ set_eventloop_tick(timer_tick)
+ : ¥¤¥Ù¥ó¥È¥ë¡¼¥×¤ÈƱ»þ¤ËÊÌ¥¹¥ì¥Ã¥É¤¬²ÔƯ¤·¤Æ¤¤¤ë¾ì¹ç¤Ë¡¤»þ
+ : ´Ö¤Ë´ð¤Å¤¤¤¿¶¯À©Åª¤Ê¥¹¥ì¥Ã¥É¥¹¥¤¥Ã¥Á¥ó¥°¤ò¤É¤ÎÄøÅÙ¤ÎÉÑÅÙ
+ : ( »þ´Ö´Ö³Ö ) ¤ÇȯÀ¸¤µ¤»¤ë¤«¤ò¥ß¥êÉÃñ°Ì¤ÎÀ°¿ôÃͤǻØÄꤹ¤ë¡¥
+ : 0 ¤ò»ØÄꤹ¤ë¤È¡¤¤³¤Î¶¯À©Åª¤Ê¥¹¥¤¥Ã¥Á¥ó¥°¤Ï¹Ô¤ï¤ì¤Ê¤¤¡¥
+ : ɸ½à¤Ç¤Ï 0 ¤ËÀßÄꤵ¤ì¤Æ¤ª¤ê¡¤¥¤¥Ù¥ó¥È½èÍý¿ô¤Ë´ð¤Å¤¯¥¹¥¤¥Ã
+ : ¥Á¥ó¥°¤À¤±¤¬¹Ô¤ï¤ì¤ë ( see set_eventloop_weight )¡¥
+ : ¤¿¤À¤·¡¤²ÔƯ¤·¤Æ¤¤¤ë¥¹¥ì¥Ã¥É¤¬¥¤¥Ù¥ó¥È¥ë¡¼¥×¤À¤±¤Î¾ì¹ç¡¤
+ : timer_tick ¤ò 0 ¤ËÀßÄꤹ¤ë¤³¤È¤Ï¤Ç¤­¤Ê¤¤¡¥¤â¤·ÀßÄꤵ¤ì¤Æ
+ : ¤¤¤¿¤é¡¤200 ms ( see NO_THREAD_INTERRUPT_TIME ) ¤Ë¼«Æ°Àß
+ : Äꤵ¤ì¤ë¡¥
+ : ¾ÜºÙ¤ÊÀâÌÀ¤Ïά¤¹¤¬¡¤¤³¤ì¤Ï CPU ¥Ñ¥ï¡¼¤òÀáÌ󤷤ĤİÂÁ´¤Ç
+ : °ÂÄꤷ¤¿Æ°ºî¤ò¼Â¸½¤¹¤ë¤¿¤á¤Ë¼ÂÁõ¤·¤¿»ÅÍͤǤ¢¤ë¡¥
+
+ get_eventloop_tick
+ : timer_tick ¤Î¸½ºßÃͤòÊÖ¤¹¡¥
+
+ set_no_event_wait(no_event_wait)
+ : Ê£¿ô¤Î¥¹¥ì¥Ã¥É¤¬²ÔƯ¤·¤Æ¤¤¤ë¾ì¹ç¤Ç¡¤½èÍýÂÔ¤Á¥¤¥Ù¥ó¥È¤¬Á´
+ : ¤¯Â¸ºß¤·¤Ê¤«¤Ã¤¿ºÝ¤Ë sleep ¾õÂÖ¤ËÆþ¤ë»þ´ÖŤò»ØÄꤹ¤ë¡¥
+ : ²ÔƯ¥¹¥ì¥Ã¥É¤¬¥¤¥Ù¥ó¥È¥ë¡¼¥×¤À¤±¤Î¾ì¹ç¤Ë¤Ï°ÕÌ£¤ò¤Ê¤µ¤Ê¤¤¡¥
+ : ¥Ç¥Õ¥©¥ë¥È¤ÎÃÍ¤Ï 20 (ms)
+
+ get_no_event_wait
+ : no_event_wait ¤Î¸½ºßÃͤòÊÖ¤¹¡¥
+
+ set_eventloop_weight(loop_max, no_event_tick)
+ : Ê£¿ô¤Î¥¹¥ì¥Ã¥É¤¬²ÔƯ¤·¤Æ¤¤¤ëºÝ¤Ë Ruby/Tk ¤Î¥¤¥Ù¥ó¥È¥ë¡¼
+ : ¥×¤Ë³ä¤êÅö¤Æ¤ëÈæ½Å¤òÄê¤á¤ë¤¿¤á¤Î¥Ñ¥é¥á¡¼¥¿¤òÀßÄꤹ¤ë¡¥
+ : ²ÔƯ¥¹¥ì¥Ã¥É¤¬¥¤¥Ù¥ó¥È¥ë¡¼¥×¤À¤±¤Î¾ì¹ç¤Ë¤Ï°ÕÌ£¤ò¤Ê¤µ¤Ê¤¤¡¥
+ : °ìÅ٤Υ¹¥ì¥Ã¥ÉÀÚ¤êÂØ¤¨¤Î´Ö¤Ë½èÍý¤¹¤ë¥¤¥Ù¥ó¥È¤ÎºÇÂç¿ô¤È¡¤
+ : ½èÍýÂÔ¤Á¤Î¥¤¥Ù¥ó¥È¤¬Â¸ºß¤·¤Ê¤¤ºÝ¤Î²Ã»»¿ô¤È¤òÀßÄꤹ¤ë¡¥
+ : ½èÍýÂÔ¤Á¥¤¥Ù¥ó¥È¤¬Â¸ºß¤·¤Ê¤¤¾ì¹ç¤Ï no_event_wait ( see
+ : set_no_event_wait ) ¤À¤±¤Î´Ö sleep ¾õÂÖ¤ËÆþ¤ë¡¥
+ : ¥Ç¥Õ¥©¥ë¥È¤Ç¤Ï¤½¤ì¤¾¤ì 800 ²ó¤È 10 ²ó¡¤¤Ä¤Þ¤ê¡¤800 ¸Ä¤Î¥¤
+ : ¥Ù¥ó¥È (¥¢¥¤¥É¥ë¥¤¥Ù¥ó¥È¤ò´Þ¤à) ¤ò½èÍý¤¹¤ë¤È¤«¡¤¥¤¥Ù¥ó¥È
+ : ¤¬Á´¤¯È¯À¸¤·¤Ê¤¤¤Þ¤Þ¤Ë 80 ²ó¤Î½èÍýÂÔ¤Á¥¤¥Ù¥ó¥È¸¡ºº¤¬´°Î»
+ : ¤¹¤ë¤È¤«¤Ç¥«¥¦¥ó¥È¤¬ 800 °Ê¾å¤Ë¤Ê¤ë¤È¥¹¥ì¥Ã¥É¥¹¥¤¥Ã¥Á¥ó¥°
+ : ¤¬È¯À¸¤¹¤ë¤³¤È¤Ë¤Ê¤ë¡¥
+
+ get_eventloop_weight
+ : ¸½ºß¤Î loop_max ¤È no_event_tick ¤È¤ÎÃͤòÊÖ¤¹¡¥
+ : ( see set_eventloop_wait )
+
+ mainloop_abort_on_exception=(bool)
+ : Tk ¥¤¥ó¥¿¡¼¥×¥ê¥¿¾å¤ÇÎã³°¤òȯÀ¸¤·¤¿ºÝ¤Ë¡¤¥¤¥Ù¥ó¥È¥ë¡¼¥×¤ò
+ : ¥¨¥é¡¼Ää»ß¤µ¤»¤ë¤«¤É¤¦¤«¤ò»ØÄꤹ¤ë¡¥true ¤ò»ØÄꤷ¤¿¾ì¹ç¤Ï
+ : ¥¨¥é¡¼Ää»ß¤¹¤ë¤¬¡¤false ¤Î¾ì¹ç¤ÏÎã³°¤ò̵»ë¤·¤Æ¥¤¥Ù¥ó¥È¥ë¡¼
+ : ¥×¤ò·Ñ³¤¹¤ë¡¥¤µ¤é¤Ë nil ¤Î¾ì¹ç¤Ï·Ù¹ð¥â¡¼¥É¤Ç¤Ê¤¤¸Â¤ê¤Ï¥¨
+ : ¥é¡¼¥á¥Ã¥»¡¼¥¸¤Î½ÐÎϤ¹¤é¾Êά¤·¤Æ¡¤Îã³°¤ò̵»ë¤¹¤ë¡¥
+ : ¥Ç¥Õ¥©¥ë¥È¤Ç¤Ï true ¤ËÀßÄꤵ¤ì¤Æ¤¤¤ë¡¥
+ : £±¸Ä¤Î¥¤¥ó¥¿¡¼¥×¥ê¥¿¤À¤±¤ò»È¤Ã¤Æ¤¤¤ë¾ì¹ç¤Ë¤Ï¥¨¥é¡¼»þ¤Ë¤½¤Î
+ : ¤Þ¤ÞÄä»ß¤·¤Æ¤âÄ̾ï¤ÏÌäÂê¤Ê¤¤¤¬¡¤Ê£¿ô¤Î¥¤¥ó¥¿¡¼¥×¥ê¥¿¤¬Æ±»þ
+ : ¤Ëưºî¤·¤Æ¤¤¤ë¾ì¹ç¤Ë¤Ï¡¤¤½¤ì¤é¤ò´ÉÍý¤¹¤ë¥¤¥Ù¥ó¥È¥ë¡¼¥×¤Ï£±
+ : ¸Ä¤À¤±¤Ç¤¢¤ë¤¿¤á¡¤¤¤¤º¤ì¤«¤Î¥¤¥ó¥¿¡¼¥×¥ê¥¿¤Î¥¨¥é¡¼¤¬¸¶°ø¤Ç¡¤
+ : ¾¤Î¥¤¥ó¥¿¡¼¥×¥ê¥¿¤Î½èÍý·Ñ³¤¬ÉÔ²Äǽ¤Ë¤Ê¤ë¤³¤È¤¬¤¢¤ë¡¥¤½¤Î
+ : ¤è¤¦¤Ê¾ì¹ç¤Ç¤â¥¨¥é¡¼¤ò̵»ë¤·¤Æ¥¤¥Ù¥ó¥È¥ë¡¼¥×¤¬²ÔƯ¤ò³¤±¤ë
+ : ¤³¤È¤Ç¡¤Â¾¤Î¥¤¥ó¥¿¡¼¥×¥ê¥¿¤¬Àµ¾ï¤Ëưºî¤·Â³¤±¤ë¤³¤È¤¬¤Ç¤­¤ë¡¥
+
+ mainloop_abort_on_exception
+ : Tk ¥¤¥ó¥¿¡¼¥×¥ê¥¿¾å¤ÇÎã³°¤òȯÀ¸¤·¤¿ºÝ¤Ë¡¤¥¤¥Ù¥ó¥È¥ë¡¼¥×¤ò¥¨
+ : ¥é¡¼Ää»ß¤µ¤»¤ë¤«¤É¤¦¤«¤ÎÀßÄê¾õÂÖ¤ò true/false ¤ÇÆÀ¤ë¡¥
+
+
+¥¯¥é¥¹ TclTkIp
+ ¥¯¥é¥¹¥á¥½¥Ã¥É
+ new(ip_name=nil, options='')
+ : TclTkIp ¥¯¥é¥¹¤Î¥¤¥ó¥¹¥¿¥ó¥¹¤òÀ¸À®¤¹¤ë¡¥
+ : ip_name ¤Ëʸ»úÎó¤òÍ¿¤¨¤¿¾ì¹ç¤Ï¡¤¤½¤ì¤¬ winfo interps ¤Ê¤É¤Ç
+ : ɽ¼¨¤µ¤ì¤ë̾Á°¤Ë¤Ê¤ë¡¥
+ : options ¤Ë¤Ï¡¤-geometry ¤ä -use ¤Ê¤É¡¤wish ¤Î¥³¥Þ¥ó¥É¥é¥¤¥ó
+ : °ú¿ô¤È¤·¤ÆÍ¿¤¨¤ë¥ª¥×¥·¥ç¥ó¤ÈƱÍͤξðÊó¤òʸ»úÎó¤È¤·¤ÆÍ¿¤¨¤ë¡¥
+ : Í¿¤¨¤é¤ì¤¿¾ðÊó¤Ï¡¤root widget À¸À®¤ÎºÝ¤ËÍѤ¤¤é¤ì¤ë¡¥
+ : ( e.g. TclTkIp.new('FOO', '-geometry 500x200 -use 0x2200009') )
+
+ ¥¤¥ó¥¹¥¿¥ó¥¹¥á¥½¥Ã¥É
+ create_slave(name, safe=false)
+ : ¥ì¥·¡¼¥Ð¤ò¿Æ¤È¤¹¤ë name ¤È¤¤¤¦Ì¾Á°¤Î¥¹¥ì¡¼¥Ö¥¤¥ó¥¿¡¼¥×¥ê¥¿¤ò
+ : À¸À®¤¹¤ë¡¥
+ : safe ¤Ë¤ÏÀ¸À®¤¹¤ë¥¤¥ó¥¿¡¼¥×¥ê¥¿¤ò safe ¥¤¥ó¥¿¡¼¥×¥ê¥¿¤È¤¹¤ë
+ : ¤«¤ò»ØÄꤹ¤ë¡¥¥Ç¥Õ¥©¥ë¥È¤Ï false ¤È¤¤¤¦¤³¤È¤Ë¤Ê¤Ã¤Æ¤¤¤ë¤¬¡¤
+ : ¤¿¤È¤¨ÌÀ³Î¤Ë false ¤ò»ØÄꤷ¤Æ¤¤¤¿¤È¤·¤Æ¤â¡¤¿Æ¤È¤Ê¤ë¥¤¥ó¥¿¡¼
+ : ¥×¥ê¥¿¤¬ safe ¥¤¥ó¥¿¡¼¥×¥ê¥¿¤Ç¤¢¤ì¤Ð¡¤¤½¤ÎÀßÄê¤ò°ú¤­·Ñ¤¤¤Ç
+ : safe ¥¤¥ó¥¿¡¼¥×¥ê¥¿¤È¤·¤ÆÀ¸À®¤µ¤ì¤ë¡¥
+
+ make_safe
+ : Tcl/Tk ¥¤¥ó¥¿¡¼¥×¥ê¥¿¤ò safe ¥¤¥ó¥¿¡¼¥×¥ê¥¿¤ËÊѹ¹¤¹¤ë¡¥
+ : Ìá¤êÃͤϥ쥷¡¼¥Ð¤Ç¤¢¤ë¥¤¥ó¥¿¡¼¥×¥ê¥¿¼«¿È¤Ç¤¢¤ë¡¥
+ : ¼ºÇÔ¤·¤¿¾ì¹ç¤Ï RuntimeError ¤ÎÎã³°¤òȯÀ¸¤¹¤ë¡¥
+
+ safe?
+ : Tcl/Tk ¥¤¥ó¥¿¡¼¥×¥ê¥¿¤ò safe ¥¤¥ó¥¿¡¼¥×¥ê¥¿¤Ç¤¢¤ë¤«¤òÄ´¤Ù¤ë¡¥
+ : safe ¥¤¥ó¥¿¡¼¥×¥ê¥¿¤Ç¤¢¤ì¤Ð true ¤òÊÖ¤¹¡¥
+
+ delete
+ : Tcl/Tk ¥¤¥ó¥¿¡¼¥×¥ê¥¿¤ò delete ¤¹¤ë¡¥
+ : delete ¤µ¤ì¤¿¥¤¥ó¥¿¡¼¥×¥ê¥¿¤Ï¡¤°Ê¸å°ìÀÚ¤ÎÁàºî¤¬¤Ç¤­¤Ê¤¯¤Ê¤ê¡¤
+ : ¥³¥Þ¥ó¥É¤òÁ÷¤Ã¤Æ¤âÎã³°¤òȯÀ¸¤¹¤ë¤è¤¦¤Ë¤Ê¤ë¡¥
+
+ deleted?
+ : Tcl/Tk ¥¤¥ó¥¿¡¼¥×¥ê¥¿¤¬¤¹¤Ç¤Ë delete ¤µ¤ì¤Æ¤¤¤ë¤«¤òÄ´¤Ù¤ë¡¥
+ : delete ºÑ¤ß¤Ç¥³¥Þ¥ó¥É¤ò¼õ¤±ÉÕ¤±¤Ê¤¤¾õÂ֤ˤʤäƤ¤¤ë¤Ê¤é¤Ð
+ : true ¤òÊÖ¤¹¡¥
+
+ restart
+ : Tcl/Tk ¥¤¥ó¥¿¡¼¥×¥ê¥¿¤Î Tk Éôʬ¤Î½é´ü²½¡¤ºÆµ¯Æ°¤ò¹Ô¤¦¡¥
+ : °ìö root widget ¤òÇ˲õ¤·¤¿¸å¤ËºÆÅÙ Tk ¤Îµ¡Ç½¤¬É¬ÍפÈ
+ : ¤Ê¤Ã¤¿¾ì¹ç¤ËÍѤ¤¤ë¡¥
+
+ _eval(str)
+ _invoke(*args)
+ : Tcl/Tk ¥¤¥ó¥¿¡¼¥×¥ê¥¿¾å¤Çɾ²Á¤ò¹Ô¤¦¡¥
+ : _eval ¤Ïɾ²Á¥¹¥¯¥ê¥×¥È¤¬°ì¤Ä¤Îʸ»úÎó¤Ç¤¢¤ë¤³¤È¤ËÂФ·¡¤
+ : _invoke ¤Ïɾ²Á¥¹¥¯¥ê¥×¥È¤Î token ¤´¤È¤Ë°ì¤Ä¤Î°ú¿ô¤È¤Ê
+ : ¤ë¤è¤¦¤ËÍ¿¤¨¤ë¡¥
+ : _invoke ¤ÎÊý¤Ï Tcl/Tk ¥¤¥ó¥¿¡¼¥×¥ê¥¿¤Î»ú¶ç²òÀÏ´ï¤òÍѤ¤
+ : ¤Ê¤¤¤¿¤á¡¤É¾²Á¤ÎÉé²Ù¤¬¤è¤ê¾¯¤Ê¤¯¤Æ¤¹¤à¡¥¤¿¤À¤·¡¤¤½¤ÎÂå
+ : ¤ï¤ê¤Ë auto_load ¤Î¤è¤¦¤Êµ¡¹½¤ÏƯ¤«¤º¡¤load Åù¤Ë¤è¤Ã¤Æ
+ : Tcl/Tk ¥¤¥ó¥¿¡¼¥×¥ê¥¿¾å¤Ë´û¤ËÅÐÏ¿ºÑ¤ß¤Î¥³¥Þ¥ó¥É¤·¤«¸Æ
+ : ¤Ó½Ð¤¹¤³¤È¤¬¤Ç¤­¤Ê¤¤¡¥
+ : _eval ¤Ç¤Ï auto_load µ¡¹½¤¬Æ¯¤¯¤¿¤á¡¤°ìÅÙ _eval ¤ò¼Â¹Ô
+ : ¤·¤ÆÅÐÏ¿¤ËÀ®¸ù¤·¤µ¤¨¤¹¤ì¤Ð¡¤°Ê¹ß¤Ï _invoke ¤Ç¤âÍøÍѤÇ
+ : ¤­¤ë¤è¤¦¤Ë¤Ê¤ë¡¥
+
+ _toUTF8(str, encoding)
+ _fromUTF8(str, encoding)
+ : Tcl/Tk ¤¬Æâ¢¤·¤Æ¤¤¤ë UTF8 ÊÑ´¹½èÍý¤ò¸Æ¤Ó½Ð¤¹¡¥
+
+ _return_value
+ : ľÁ°¤Î Tcl/Tk ¾å¤Ç¤Îɾ²Á¤Î¼Â¹Ô·ë²Ì¤È¤·¤Æ¤ÎÌá¤êÃͤòÊÖ¤¹¡¥
+
+ mainloop : °ú¿ô¤ò´Þ¤á¤Æ TclTkLib.mainloop ¤ËƱ¤¸
+ mainloop_watchdog : °ú¿ô¤ò´Þ¤á¤Æ TclTkLib.mainloop_watchdog ¤ËƱ¤¸
+ do_one_event : °ú¿ô¤ò´Þ¤á¤Æ TclTkLib.do_one_event ¤ËƱ¤¸
+ set_eventloop_tick : °ú¿ô¤ò´Þ¤á¤Æ TclTkLib.set_eventloop_tick ¤ËƱ¤¸
+ get_eventloop_tick : °ú¿ô¤ò´Þ¤á¤Æ TclTkLib.get_eventloop_tick ¤ËƱ¤¸
+ set_eventloop_weight : °ú¿ô¤ò´Þ¤á¤Æ TclTkLib.set_eventloop_weight ¤ËƱ¤¸
+ get_eventloop_weight : °ú¿ô¤ò´Þ¤á¤Æ TclTkLib.set_eventloop_weight ¤ËƱ¤¸
+ mainloop_abort_on_exception
+ : °ú¿ô¤ò´Þ¤á¤Æ TclTkLib.mainloop_abort_on_exception ¤ËƱ¤¸
+ mainloop_abort_on_exception=
+ : °ú¿ô¤ò´Þ¤á¤Æ TclTkLib.mainloop_abort_on_exception= ¤ËƱ¤¸
+
+¥¯¥é¥¹ TkCallbackBreak < StandardError
+¥¯¥é¥¹ TkCallbackContinue < StandardError
+ : ¤³¤ì¤é¤Ï¥¤¥Ù¥ó¥È¥³¡¼¥ë¥Ð¥Ã¥¯¤Ë¤ª¤¤¤Æ¡¤¥³¡¼¥ë¥Ð¥Ã¥¯½èÍý¤òŬÀÚ¤ËÃæ
+ : ÃǤ·¤¿¤ê¡¤¼¡¤Î¥Ð¥¤¥ó¥É¥¿¥°¤Î¥Ð¥¤¥ó¥Ç¥£¥ó¥°½èÍý¤Ë¿Ê¤á¤¿¤ê¤¹¤ë¤³¤È
+ : ¤ò²Äǽ¤Ë¤¹¤ë¤¿¤á¤ÎÎã³°¥¯¥é¥¹¤Ç¤¢¤ë¡¥
+ : ¥³¡¼¥ë¥Ð¥Ã¥¯¤Ç break ¤ä continue ¤ò¼Â¸½¤¹¤ë¤¿¤á¤Ë¤Ï¡¤¥³¡¼¥ë¥Ð¥Ã¥¯
+ : ¤Ç¤¢¤ë Ruby ¼ê³¤­¤¬ Tcl/Tk ¥¤¥ó¥¿¡¼¥×¥ê¥¿Â¦¤ËŬÀڤʥ꥿¡¼¥ó¥³¡¼
+ : ¥É¤òÊÖ¤¹É¬Íפ¬¤¢¤ë¡¥Ruby ¤Î¼ê³¤­¤¬ÉáÄ̤ËÃͤòÊÖ¤¹¤Î¤Ç¤Ï¡¤¤½¤ì¤¬Éá
+ : Ä̤ÎÌá¤êÃͤǤ¢¤ë¤Î¤«Èݤ«¤ò¶èÊ̤¬¤Ç¤­¤Ê¤¤¤¿¤á¡¤Î㳰ȯÀ¸¤òÍøÍѤ·¤¿
+ : ¼ÂÁõ¤ò¹Ô¤Ã¤Æ¤¤¤ë¡¥
+
(eof)
diff --git a/ext/tcltklib/README.euc b/ext/tcltklib/README.euc
index 290ffb0b60..fd75202c18 100644
--- a/ext/tcltklib/README.euc
+++ b/ext/tcltklib/README.euc
@@ -1,4 +1,30 @@
(tof)
+ 2003/06/19 Hidetoshi NAGAI
+
+Ëܥɥ­¥å¥á¥ó¥È¤Ë¤Ï¸Å¤¤ tcltk ¥é¥¤¥Ö¥é¥ê¡¤tcltklib ¥é¥¤¥Ö¥é¥ê¤ÎÀâÌÀ
+¤¬´Þ¤Þ¤ì¤Æ¤¤¤Þ¤¹¤¬¡¤¤½¤Îµ­½ÒÆâÍÆ¤Ï¸Å¤¤¤â¤Î¤È¤Ê¤Ã¤Æ¤¤¤Þ¤¹¡¥
+
+¤Þ¤º¡¤¸½ºß¤Î Ruby/Tk ¤ÎÃæ¿´¤Ç¤¢¤ë tk.rb ¤Ï wish ¤ò¸Æ¤Ó½Ð¤·¤¿¤ê¤Ï¤»
+¤º¡¤tcltklib ¥é¥¤¥Ö¥é¥ê¤ò wrap ¤·¤ÆÆ°ºî¤¹¤ë¤â¤Î¤È¤Ê¤Ã¤Æ¤¤¤Þ¤¹¡¥¤½¤Î
+¤¿¤á¡¤¸Å¤¤ÀâÌÀµ­½Ò¤Ç½Ò¤Ù¤é¤ì¤Æ¤¤¤ë¤è¤¦¤Ê¥×¥í¥»¥¹´ÖÄÌ¿®¤Ë¤è¤ë¥ª¡¼¥Ð
+¥Ø¥Ã¥É¤Ï¸ºß¤·¤Þ¤»¤ó¡¥
+
+¸½ºß¤Î tcltklib ¥é¥¤¥Ö¥é¥ê¤Ç¤â¡¤Tcl/Tk ¤Î C ¥é¥¤¥Ö¥é¥ê¤ò¥ê¥ó¥¯¤·¤Æ
+ľÀÜ¤ËÆ°¤«¤¹¤³¤È¤Ç¡¤¥ª¡¼¥Ð¥Ø¥Ã¥É¤ò²¡¤µ¤¨¤Ä¤Ä Tcl/Tk ¥¤¥ó¥¿¡¼¥×¥ê¥¿
+¤Î¤Û¤ÜÁ´µ¡Ç½¡Ê³ÈÄ¥¥é¥¤¥Ö¥é¥ê¤ò´Þ¤à¡Ë¤ò»È¤¨¤ëÅÀ¤ÏƱ¤¸¤Ç¤¹¡¥¤·¤«¤·¡¤
+¤½¤ÎÌò³ä¤Ï¤Û¤Ü¡Ötk.rb °Ê²¼¤Î¥é¥¤¥Ö¥é¥ê¤ò¸ú²ÌŪ¤ËƯ¤«¤»¤ë¤¿¤á¤Î¤â¤Î¡×
+¤È¸«¤Ê¤µ¤ì¤Æ¤ª¤ê¡¤¤½¤ÎÌÜŪ¤Ç¥á¥ó¥Æ¥Ê¥ó¥¹¤µ¤ì¤Æ¤¤¤Þ¤¹¡¥
+
+tk.rb ¤Î¹âµ¡Ç½²½¤Ëȼ¤Ã¤Æ¡¤Ãæ¿å½à¤Î¥é¥¤¥Ö¥é¥ê¤Ç¤¢¤ë tcltk ¥é¥¤¥Ö¥é¥ê
+¡Êtcltk.rb¡Ë¤Ï¤½¤Î¸ºß°ÕµÁ¤ò¸º¤¸¤Æ¤ª¤ê¡¤¸½ºß¤Ç¤Ï¥á¥ó¥Æ¥Ê¥ó¥¹¤Ï¹Ô¤ï
+¤ì¤Æ¤¤¤Þ¤»¤ó¡¥
+
+¤Ê¤ª¡¤¸Å¤¤ÀâÌÀ¤Ç¤Ï¥Ð¥¤¥ó¥Ç¥£¥ó¥°¤Ë¤ª¤±¤ë¥¹¥¯¥ê¥×¥È¤ÎÄɲäϤǤ­¤Ê¤¤¤³
+¤È¤È¤Ê¤Ã¤Æ¤¤¤Þ¤¹¤¬¡¤¸½ºß¤Î tk.rb ¤Ç¤Ï¤³¤ì¤â²Äǽ¤Ç¤¢¤ë¤³¤È¤òÊä­¤·¤Æ
+¤ª¤­¤Þ¤¹¡¥
+
+°Ê²¼¤¬¥é¥¤¥Ö¥é¥ê¤Î¸Å¤¤ÀâÌÀʸ½ñ¤Ç¤¹¡¥
+==============================================================
tcltk ¥é¥¤¥Ö¥é¥ê
tcltklib ¥é¥¤¥Ö¥é¥ê
Sep. 19, 1997 Y. Shigehiro
diff --git a/ext/tcltklib/demo/safeTk.rb b/ext/tcltklib/demo/safeTk.rb
new file mode 100644
index 0000000000..5d2c60e700
--- /dev/null
+++ b/ext/tcltklib/demo/safeTk.rb
@@ -0,0 +1,22 @@
+#!/usr/bin/env ruby
+require 'tcltklib'
+
+master = TclTkIp.new
+slave_name = 'slave0'
+slave = master.create_slave(slave_name, true)
+master._eval("::safe::interpInit #{slave_name}")
+master._eval("::safe::loadTk #{slave_name}")
+
+master._invoke('label', '.l1', '-text', 'master')
+master._invoke('pack', '.l1', '-padx', '30', '-pady', '50')
+master._eval('label .l2 -text {root widget of master-ip}')
+master._eval('pack .l2 -padx 30 -pady 50')
+
+slave._invoke('label', '.l1', '-text', 'slave')
+slave._invoke('pack', '.l1', '-padx', '30', '-pady', '50')
+slave._eval('label .l2 -text {root widget of slave-ip}')
+slave._eval('pack .l2 -padx 30 -pady 20')
+slave._eval('label .l3 -text {( container frame widget of master-ip )}')
+slave._eval('pack .l3 -padx 30 -pady 20')
+
+TclTkLib.mainloop
diff --git a/ext/tcltklib/extconf.rb b/ext/tcltklib/extconf.rb
index d58c8045a0..575bf78034 100644
--- a/ext/tcltklib/extconf.rb
+++ b/ext/tcltklib/extconf.rb
@@ -22,20 +22,13 @@ def find_tcl(tcllib, stubs)
func = stubs ? "Tcl_InitStubs" : "Tcl_FindExecutable"
if tcllib
find_library(tcllib, func, *paths)
- elsif RUBY_PLATFORM =~ /mswin32|mingw|cygwin|bccwin32/
- find_library("tcl", func, *paths) or
- find_library("tcl84", func, *paths) or
- find_library("tcl83", func, *paths) or
- find_library("tcl82", func, *paths) or
- find_library("tcl80", func, *paths) or
- find_library("tcl76", func, *paths)
+ elsif find_library("tcl", func, *paths)
+ true
else
- find_library("tcl", func, *paths) or
- find_library("tcl8.4", func, *paths) or
- find_library("tcl8.3", func, *paths) or
- find_library("tcl8.2", func, *paths) or
- find_library("tcl8.0", func, *paths) or
- find_library("tcl7.6", func, *paths)
+ %w[8.4 8.3 8.2 8.0 7.6].find { |ver|
+ find_library("tcl#{ver}", func, *paths) or
+ find_library("tcl#{ver.delete('.')}", func, *paths)
+ }
end
end
@@ -44,26 +37,19 @@ def find_tk(tklib, stubs)
func = stubs ? "Tk_InitStubs" : "Tk_Init"
if tklib
find_library(tklib, func, *paths)
- elsif RUBY_PLATFORM =~ /mswin32|mingw|cygwin|bccwin32/
- find_library("tk", func, *paths) or
- find_library("tk84", func, *paths) or
- find_library("tk83", func, *paths) or
- find_library("tk82", func, *paths) or
- find_library("tk80", func, *paths) or
- find_library("tk42", func, *paths)
+ elsif find_library("tk", func, *paths)
+ true
else
- find_library("tk", func, *paths) or
- find_library("tk8.4", func, *paths) or
- find_library("tk8.3", func, *paths) or
- find_library("tk8.2", func, *paths) or
- find_library("tk8.0", func, *paths) or
- find_library("tk4.2", func, *paths)
+ %w[8.4 8.3 8.2 8.0 4.2].find { |ver|
+ find_library("tk#{ver}", func, *paths) or
+ find_library("tk#{ver.delete('.')}", func, *paths)
+ }
end
end
if have_header("tcl.h") && have_header("tk.h") &&
(/mswin32|mingw|cygwin|bccwin32/ =~ RUBY_PLATFORM || find_library("X11", "XOpenDisplay",
- "/usr/X11/lib", "/usr/X11R6/lib", "/usr/openwin/lib")) &&
+ "/usr/X11/lib", "/usr/lib/X11", "/usr/X11R6/lib", "/usr/openwin/lib")) &&
find_tcl(tcllib, stubs) &&
find_tk(tklib, stubs)
$CPPFLAGS += ' -DUSE_TCL_STUBS -DUSE_TK_STUBS' if stubs
diff --git a/ext/tcltklib/lib/tcltk.rb b/ext/tcltklib/lib/tcltk.rb
index 54a00e8f3c..b7d0f11bec 100644
--- a/ext/tcltklib/lib/tcltk.rb
+++ b/ext/tcltklib/lib/tcltk.rb
@@ -93,9 +93,11 @@ class TclTkInterpreter
# ruby_fmt command format arguments by `format' and call `ruby' command
# (notice ruby command receives only one argument)
if $DEBUG
- @ip._eval("proc ruby_fmt {fmt args} { puts \"ruby_fmt: $fmt $args\" ; ruby [format $fmt $args] }")
+ @ip._eval("proc ruby_fmt {fmt args} { puts \"ruby_fmt: $fmt
+$args\" ; set cmd [list ruby [format $fmt $args]] ; uplevel $cmd }")
else
- @ip._eval("proc ruby_fmt {fmt args} { ruby [format $fmt $args] }")
+ @ip._eval("proc ruby_fmt {fmt args} { set cmd [list ruby [format
+$fmt $args]] ; uplevel $cmd }")
end
# @ip._get_eval_string(*args): generate string to evaluate in tcl interpreter
diff --git a/ext/tcltklib/tcltklib.c b/ext/tcltklib/tcltklib.c
index f4a307c9a9..ec00cea217 100644
--- a/ext/tcltklib/tcltklib.c
+++ b/ext/tcltklib/tcltklib.c
@@ -58,7 +58,6 @@ struct invoke_queue {
VALUE thread;
};
-static VALUE main_thread;
static VALUE eventloop_thread;
static VALUE watchdog_thread;
Tcl_Interp *current_interp;
@@ -70,13 +69,23 @@ Tcl_Interp *current_interp;
* 'timer_tick' is a limit of one term of thread scheduling.
* If 'timer_tick' == 0, then not use the timer for thread scheduling.
*/
-static int tick_counter;
-#define DEFAULT_EVENT_LOOP_MAX 800
-#define DEFAULT_NO_EVENT_TICK 10
-#define DEFAULT_TIMER_TICK 0
+#define DEFAULT_EVENT_LOOP_MAX 800/*counts*/
+#define DEFAULT_NO_EVENT_TICK 10/*counts*/
+#define DEFAULT_NO_EVENT_WAIT 20/*milliseconds ( 1 -- 999 ) */
+#define WATCHDOG_INTERVAL 10/*milliseconds ( 1 -- 999 ) */
+#define DEFAULT_TIMER_TICK 0/*milliseconds ( 0 -- 999 ) */
+#define NO_THREAD_INTERRUPT_TIME 200/*milliseconds ( 1 -- 999 ) */
+
static int event_loop_max = DEFAULT_EVENT_LOOP_MAX;
static int no_event_tick = DEFAULT_NO_EVENT_TICK;
+static int no_event_wait = DEFAULT_NO_EVENT_WAIT;
static int timer_tick = DEFAULT_TIMER_TICK;
+static int req_timer_tick = DEFAULT_TIMER_TICK;
+static int run_timer_flag = 0;
+
+static int event_loop_wait_event = 0;
+static int event_loop_abort_on_exc = 1;
+static int loop_counter = 0;
#if TCL_MAJOR_VERSION >= 8
static int ip_ruby _((ClientData, Tcl_Interp *, int, Tcl_Obj *CONST*));
@@ -93,19 +102,23 @@ static void
_timer_for_tcl(clientData)
ClientData clientData;
{
- struct invoke_queue *q, *tmp;
- VALUE thread;
+ /* struct invoke_queue *q, *tmp; */
+ /* VALUE thread; */
+ DUMP1("called timer_for_tcl");
Tk_DeleteTimerHandler(timer_token);
+
+ run_timer_flag = 1;
+
if (timer_tick > 0) {
- timer_token = Tk_CreateTimerHandler(timer_tick, _timer_for_tcl,
- (ClientData)0);
+ timer_token = Tk_CreateTimerHandler(timer_tick, _timer_for_tcl,
+ (ClientData)0);
} else {
- timer_token = (Tcl_TimerToken)NULL;
+ timer_token = (Tcl_TimerToken)NULL;
}
/* rb_thread_schedule(); */
- timer_tick += event_loop_max;
+ /* tick_counter += event_loop_max; */
}
static VALUE
@@ -116,19 +129,20 @@ set_eventloop_tick(self, tick)
int ttick = NUM2INT(tick);
if (ttick < 0) {
- rb_raise(rb_eArgError, "timer-tick parameter must be 0 or plus number");
+ rb_raise(rb_eArgError,
+ "timer-tick parameter must be 0 or positive number");
}
/* delete old timer callback */
Tk_DeleteTimerHandler(timer_token);
- timer_tick = ttick;
+ timer_tick = req_timer_tick = ttick;
if (timer_tick > 0) {
- /* start timer callback */
- timer_token = Tk_CreateTimerHandler(timer_tick, _timer_for_tcl,
- (ClientData)0);
+ /* start timer callback */
+ timer_token = Tk_CreateTimerHandler(timer_tick, _timer_for_tcl,
+ (ClientData)0);
} else {
- timer_token = (Tcl_TimerToken)NULL;
+ timer_token = (Tcl_TimerToken)NULL;
}
return tick;
@@ -142,6 +156,30 @@ get_eventloop_tick(self)
}
static VALUE
+set_no_event_wait(self, wait)
+ VALUE self;
+ VALUE wait;
+{
+ int t_wait = NUM2INT(wait);
+
+ if (t_wait <= 0) {
+ rb_raise(rb_eArgError,
+ "no_event_wait parameter must be positive number");
+ }
+
+ no_event_wait = t_wait;
+
+ return wait;
+}
+
+static VALUE
+get_no_event_wait(self)
+ VALUE self;
+{
+ return INT2NUM(no_event_wait);
+}
+
+static VALUE
set_eventloop_weight(self, loop_max, no_event)
VALUE self;
VALUE loop_max;
@@ -151,7 +189,7 @@ set_eventloop_weight(self, loop_max, no_event)
int no_ev = NUM2INT(no_event);
if (lpmax <= 0 || no_ev <= 0) {
- rb_raise(rb_eArgError, "weight parameters must be plus number");
+ rb_raise(rb_eArgError, "weight parameters must be positive numbers");
}
event_loop_max = lpmax;
@@ -167,37 +205,124 @@ get_eventloop_weight(self)
return rb_ary_new3(2, INT2NUM(event_loop_max), INT2NUM(no_event_tick));
}
+static VALUE
+rb_evloop_abort_on_exc(self)
+ VALUE self;
+{
+ if (event_loop_abort_on_exc > 0) {
+ return Qtrue;
+ } else if (event_loop_abort_on_exc == 0) {
+ return Qfalse;
+ } else {
+ return Qnil;
+ }
+}
+
+static VALUE
+rb_evloop_abort_on_exc_set(self, val)
+ VALUE self, val;
+{
+ rb_secure(4);
+ if (RTEST(val)) {
+ event_loop_abort_on_exc = 1;
+ } else if (val == Qnil) {
+ event_loop_abort_on_exc = -1;
+ } else {
+ event_loop_abort_on_exc = 0;
+ }
+ return rb_evloop_abort_on_exc(self);
+}
+
VALUE
lib_mainloop_core(check_root_widget)
VALUE check_root_widget;
{
VALUE current = eventloop_thread;
- int check = (check_root_widget == Qtrue);
+ int check = RTEST(check_root_widget);
+ int tick_counter;
+ struct timeval t;
+
+ t.tv_sec = (time_t)0;
+ t.tv_usec = (time_t)(no_event_wait*1000.0);
Tk_DeleteTimerHandler(timer_token);
+ run_timer_flag = 0;
if (timer_tick > 0) {
- timer_token = Tk_CreateTimerHandler(timer_tick, _timer_for_tcl,
- (ClientData)0);
+ timer_token = Tk_CreateTimerHandler(timer_tick, _timer_for_tcl,
+ (ClientData)0);
} else {
- timer_token = (Tcl_TimerToken)NULL;
+ timer_token = (Tcl_TimerToken)NULL;
}
for(;;) {
- tick_counter = 0;
- while(tick_counter < event_loop_max) {
- if (Tcl_DoOneEvent(TCL_ALL_EVENTS | TCL_DONT_WAIT)) {
- tick_counter++;
+ if (rb_thread_alone()) {
+ DUMP1("no other thread");
+ event_loop_wait_event = 0;
+
+ if (timer_tick == 0) {
+ timer_tick = NO_THREAD_INTERRUPT_TIME;
+ timer_token = Tk_CreateTimerHandler(timer_tick,
+ _timer_for_tcl,
+ (ClientData)0);
+ }
+
+ Tcl_DoOneEvent(TCL_ALL_EVENTS);
+
+ if (loop_counter++ > 30000) {
+ loop_counter = 0;
+ }
+
+ if (run_timer_flag) {
+ DUMP1("timer interrupt");
+ run_timer_flag = 0;
+ DUMP1("check Root Widget");
+ if (check && Tk_GetNumMainWindows() == 0) {
+ return Qnil;
+ }
+ }
+
} else {
- tick_counter += no_event_tick;
- }
- if (watchdog_thread != 0 && eventloop_thread != current) {
- return Qnil;
+ DUMP1("there are other threads");
+ event_loop_wait_event = 1;
+
+ timer_tick = req_timer_tick;
+ tick_counter = 0;
+ while(tick_counter < event_loop_max) {
+ if (Tcl_DoOneEvent(TCL_ALL_EVENTS | TCL_DONT_WAIT)) {
+ tick_counter++;
+ } else {
+ tick_counter += no_event_tick;
+
+ DUMP1("check Root Widget");
+ if (check && Tk_GetNumMainWindows() == 0) {
+ return Qnil;
+ }
+
+ rb_thread_wait_for(t);
+ }
+
+ if (loop_counter++ > 30000) {
+ loop_counter = 0;
+ }
+
+ if (watchdog_thread != 0 && eventloop_thread != current) {
+ return Qnil;
+ }
+
+ if (run_timer_flag) {
+ DUMP1("timer interrupt");
+ run_timer_flag = 0;
+ break; /* switch to other thread */
+ }
+ }
+
+ DUMP1("check Root Widget");
+ if (check && Tk_GetNumMainWindows() == 0) {
+ return Qnil;
+ }
}
- }
- if (check && Tk_GetNumMainWindows() == 0) {
- break;
- }
- rb_thread_schedule();
+
+ rb_thread_schedule();
}
return Qnil;
}
@@ -211,8 +336,8 @@ lib_mainloop_ensure(parent_evloop)
DUMP2("mainloop-ensure: current-thread : %lx\n", rb_thread_current());
DUMP2("mainloop-ensure: eventloop-thread : %lx\n", eventloop_thread);
if (eventloop_thread == rb_thread_current()) {
- DUMP2("tcltklib: eventloop-thread -> %lx\n", parent_evloop);
- eventloop_thread = parent_evloop;
+ DUMP2("eventloop-thread -> %lx\n", parent_evloop);
+ eventloop_thread = parent_evloop;
}
return Qnil;
}
@@ -226,8 +351,8 @@ lib_mainloop_launcher(check_rootwidget)
eventloop_thread = rb_thread_current();
if (ruby_debug) {
- fprintf(stderr, "tcltklib: eventloop-thread : %lx -> %lx\n",
- parent_evloop, eventloop_thread);
+ fprintf(stderr, "tcltklib: eventloop-thread : %lx -> %lx\n",
+ parent_evloop, eventloop_thread);
}
return rb_ensure(lib_mainloop_core, check_rootwidget,
@@ -244,11 +369,11 @@ lib_mainloop(argc, argv, self)
VALUE check_rootwidget;
if (rb_scan_args(argc, argv, "01", &check_rootwidget) == 0) {
- check_rootwidget = Qtrue;
+ check_rootwidget = Qtrue;
} else if (RTEST(check_rootwidget)) {
- check_rootwidget = Qtrue;
+ check_rootwidget = Qtrue;
} else {
- check_rootwidget = Qfalse;
+ check_rootwidget = Qfalse;
}
return lib_mainloop_launcher(check_rootwidget);
@@ -258,34 +383,53 @@ VALUE
lib_watchdog_core(check_rootwidget)
VALUE check_rootwidget;
{
- VALUE current = eventloop_thread;
VALUE evloop;
- int check = (check_rootwidget == Qtrue);
- ID stop = rb_intern("stop?");
+ int prev_val = -1;
+ int chance = 0;
+ int check = RTEST(check_rootwidget);
+ struct timeval t0, t1;
+
+ t0.tv_sec = (time_t)0;
+ t0.tv_usec = (time_t)((NO_THREAD_INTERRUPT_TIME)*1000.0);
+ t1.tv_sec = (time_t)0;
+ t1.tv_usec = (time_t)((WATCHDOG_INTERVAL)*1000.0);
/* check other watchdog thread */
if (watchdog_thread != 0) {
- if (rb_funcall(watchdog_thread, stop, 0) == Qtrue) {
- rb_funcall(watchdog_thread, rb_intern("kill"), 0);
- } else {
- return Qnil;
- }
+ if (RTEST(rb_funcall(watchdog_thread, rb_intern("stop?"), 0))) {
+ rb_funcall(watchdog_thread, rb_intern("kill"), 0);
+ } else {
+ return Qnil;
+ }
}
watchdog_thread = rb_thread_current();
/* watchdog start */
do {
- if (eventloop_thread == 0
- || rb_funcall(eventloop_thread, stop, 0) == Qtrue) {
- /* start new eventloop thread */
- DUMP2("eventloop thread %lx is sleeping or dead", eventloop_thread);
- evloop = rb_thread_create(lib_mainloop_launcher,
- (void*)&check_rootwidget);
- DUMP2("create new eventloop thread %lx", evloop);
- rb_thread_run(evloop);
- } else {
- rb_thread_schedule();
- }
+ if (eventloop_thread == 0
+ || (loop_counter == prev_val
+ && RTEST(rb_funcall(eventloop_thread, rb_intern("stop?"), 0))
+ && ++chance >= 3 )
+ ) {
+ /* start new eventloop thread */
+ DUMP2("eventloop thread %lx is sleeping or dead",
+ eventloop_thread);
+ evloop = rb_thread_create(lib_mainloop_launcher,
+ (void*)&check_rootwidget);
+ DUMP2("create new eventloop thread %lx", evloop);
+ loop_counter = -1;
+ chance = 0;
+ rb_thread_run(evloop);
+ } else {
+ loop_counter = prev_val;
+ chance = 0;
+ if (event_loop_wait_event) {
+ rb_thread_wait_for(t0);
+ } else {
+ rb_thread_wait_for(t1);
+ }
+ /* rb_thread_schedule(); */
+ }
} while(!check || Tk_GetNumMainWindows() != 0);
return Qnil;
@@ -308,11 +452,11 @@ lib_mainloop_watchdog(argc, argv, self)
VALUE check_rootwidget;
if (rb_scan_args(argc, argv, "01", &check_rootwidget) == 0) {
- check_rootwidget = Qtrue;
+ check_rootwidget = Qtrue;
} else if (RTEST(check_rootwidget)) {
- check_rootwidget = Qtrue;
+ check_rootwidget = Qtrue;
} else {
- check_rootwidget = Qfalse;
+ check_rootwidget = Qfalse;
}
return rb_ensure(lib_watchdog_core, check_rootwidget,
@@ -325,16 +469,22 @@ lib_do_one_event(argc, argv, self)
VALUE *argv;
VALUE self;
{
- VALUE obj, vflags;
+ VALUE vflags;
int flags;
+ int ret;
if (rb_scan_args(argc, argv, "01", &vflags) == 0) {
- flags = 0;
+ flags = TCL_ALL_EVENTS;
+ } else {
+ Check_Type(vflags, T_FIXNUM);
+ flags = FIX2INT(vflags);
+ }
+ ret = Tcl_DoOneEvent(flags);
+ if (ret) {
+ return Qtrue;
} else {
- Check_Type(vflags, T_FIXNUM);
- flags = FIX2INT(vflags);
+ return Qfalse;
}
- return INT2NUM(Tcl_DoOneEvent(flags));
}
/*---- class TclTkIp ----*/
@@ -378,11 +528,25 @@ lib_restart(self)
/* ignore ERROR */
DUMP2("(TCL_Eval result) %d", ptr->return_value);
- /* execute Tk_Init */
+ /* execute Tk_Init of Tk_SafeInit */
+#if TCL_MAJOR_VERSION >= 8
+ if (Tcl_IsSafe(ptr->ip)) {
+ DUMP1("Tk_SafeInit");
+ if (Tk_SafeInit(ptr->ip) == TCL_ERROR) {
+ rb_raise(rb_eRuntimeError, "%s", ptr->ip->result);
+ }
+ } else {
+ DUMP1("Tk_Init");
+ if (Tk_Init(ptr->ip) == TCL_ERROR) {
+ rb_raise(rb_eRuntimeError, "%s", ptr->ip->result);
+ }
+ }
+#else
DUMP1("Tk_Init");
if (Tk_Init(ptr->ip) == TCL_ERROR) {
rb_raise(rb_eRuntimeError, "%s", ptr->ip->result);
}
+#endif
return Qnil;
}
@@ -424,9 +588,9 @@ ip_ruby(clientData, interp, argc, argv)
DUMP2("rb_eval_string(%s)", arg);
old_trapflg = rb_trap_immediate;
rb_trap_immediate = 0;
- res = rb_rescue2(rb_eval_string, (VALUE)arg,
+ res = rb_rescue2(rb_eval_string, (VALUE)arg,
ip_eval_rescue, (VALUE)&failed,
- rb_eStandardError, rb_eScriptError, 0);
+ rb_eStandardError, rb_eScriptError, (VALUE)0);
rb_trap_immediate = old_trapflg;
Tcl_ResetResult(interp);
@@ -463,12 +627,14 @@ ip_free(ptr)
{
DUMP1("Tcl_DeleteInterp");
if (ptr) {
+ Tcl_Release((ClientData)ptr->ip);
Tcl_DeleteInterp(ptr->ip);
free(ptr);
}
}
/* create and initialize interpreter */
+static VALUE ip_alloc _((VALUE));
static VALUE
ip_alloc(self)
VALUE self;
@@ -477,10 +643,14 @@ ip_alloc(self)
}
static VALUE
-ip_init(self)
+ip_init(argc, argv, self)
+ int argc;
+ VALUE *argv;
VALUE self;
{
struct tcltkip *ptr; /* tcltkip data struct */
+ VALUE argv0, opts;
+ int cnt;
/* create object */
Data_Get_Struct(self, struct tcltkip, ptr);
@@ -491,6 +661,7 @@ ip_init(self)
/* from Tk_Main() */
DUMP1("Tcl_CreateInterp");
ptr->ip = Tcl_CreateInterp();
+ Tcl_Preserve((ClientData)ptr->ip);
current_interp = ptr->ip;
/* from Tcl_AppInit() */
@@ -498,13 +669,35 @@ ip_init(self)
if (Tcl_Init(ptr->ip) == TCL_ERROR) {
rb_raise(rb_eRuntimeError, "%s", ptr->ip->result);
}
+
+ /* set variables */
+ cnt = rb_scan_args(argc, argv, "02", &argv0, &opts);
+ switch(cnt) {
+ case 2:
+ /* options */
+ Tcl_SetVar(ptr->ip, "argv", StringValuePtr(opts), 0);
+ case 1:
+ /* argv0 */
+ if (argv0 != Qnil) {
+ Tcl_SetVar(ptr->ip, "argv0", StringValuePtr(argv0), 0);
+ }
+ case 0:
+ /* no args */
+ ;
+ }
+
+ /* from Tcl_AppInit() */
DUMP1("Tk_Init");
if (Tk_Init(ptr->ip) == TCL_ERROR) {
rb_raise(rb_eRuntimeError, "%s", ptr->ip->result);
}
DUMP1("Tcl_StaticPackage(\"Tk\")");
+#if TCL_MAJOR_VERSION >= 8
+ Tcl_StaticPackage(ptr->ip, "Tk", Tk_Init, Tk_SafeInit);
+#else
Tcl_StaticPackage(ptr->ip, "Tk", Tk_Init,
(Tcl_PackageInitProc *) NULL);
+#endif
/* add ruby command to the interpreter */
#if TCL_MAJOR_VERSION >= 8
@@ -520,6 +713,95 @@ ip_init(self)
return self;
}
+static VALUE
+ip_create_slave(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ struct tcltkip *master = get_ip(self);
+ struct tcltkip *slave = ALLOC(struct tcltkip);
+ VALUE name;
+ VALUE safemode;
+ int safe;
+
+ /* safe-mode check */
+ if (rb_scan_args(argc, argv, "11", &name, &safemode) == 1) {
+ safemode = Qfalse;
+ }
+ if (Tcl_IsSafe(master->ip) == 1) {
+ safe = 1;
+ } else if (safemode == Qfalse || safemode == Qnil) {
+ safe = 0;
+ } else {
+ safe = 1;
+ }
+
+ /* create slave-ip */
+ if ((slave->ip = Tcl_CreateSlave(master->ip, StringValuePtr(name), safe))
+ == NULL) {
+ rb_raise(rb_eRuntimeError, "fail to create the new slave interpreter");
+ }
+ Tcl_Preserve((ClientData)slave->ip);
+ slave->return_value = 0;
+
+ return Data_Wrap_Struct(CLASS_OF(self), 0, ip_free, slave);
+}
+
+/* make ip "safe" */
+static VALUE
+ip_make_safe(self)
+ VALUE self;
+{
+ struct tcltkip *ptr = get_ip(self);
+
+ if (Tcl_MakeSafe(ptr->ip) == TCL_ERROR) {
+ rb_raise(rb_eRuntimeError, "%s", ptr->ip->result);
+ }
+
+ return self;
+}
+
+/* is safe? */
+static VALUE
+ip_is_safe_p(self)
+ VALUE self;
+{
+ struct tcltkip *ptr = get_ip(self);
+
+ if (Tcl_IsSafe(ptr->ip)) {
+ return Qtrue;
+ } else {
+ return Qfalse;
+ }
+}
+
+/* delete interpreter */
+static VALUE
+ip_delete(self)
+ VALUE self;
+{
+ struct tcltkip *ptr = get_ip(self);
+
+ Tcl_DeleteInterp(ptr->ip);
+
+ return Qnil;
+}
+
+/* is deleted? */
+static VALUE
+ip_is_deleted_p(self)
+ VALUE self;
+{
+ struct tcltkip *ptr = get_ip(self);
+
+ if (Tcl_InterpDeleted(ptr->ip)) {
+ return Qtrue;
+ } else {
+ return Qfalse;
+ }
+}
+
/* eval string in tcl by Tcl_Eval() */
static VALUE
ip_eval(self, str)
@@ -527,7 +809,7 @@ ip_eval(self, str)
VALUE str;
{
char *s;
- char *buf; /* Tcl_Eval requires re-writable string region */
+ char *buf; /* Tcl_Eval requires re-writable string region */
struct tcltkip *ptr = get_ip(self);
/* call Tcl_Eval() */
@@ -542,10 +824,10 @@ ip_eval(self, str)
DUMP2("(TCL_Eval result) %d", ptr->return_value);
/* pass back the result (as string) */
- return(rb_str_new2(ptr->ip->result));
+ /* return(rb_str_new2(ptr->ip->result)); */
+ return(rb_tainted_str_new2(ptr->ip->result));
}
-
static VALUE
ip_toUTF8(self, str, encodename)
VALUE self;
@@ -572,7 +854,8 @@ ip_toUTF8(self, str, encodename)
Tcl_DStringInit(&dstr);
Tcl_DStringFree(&dstr);
Tcl_ExternalToUtfDString(encoding,buf,strlen(buf),&dstr);
- str = rb_str_new2(Tcl_DStringValue(&dstr));
+ /* str = rb_str_new2(Tcl_DStringValue(&dstr)); */
+ str = rb_tainted_str_new2(Tcl_DStringValue(&dstr));
Tcl_FreeEncoding(encoding);
Tcl_DStringFree(&dstr);
@@ -606,7 +889,8 @@ ip_fromUTF8(self, str, encodename)
Tcl_DStringInit(&dstr);
Tcl_DStringFree(&dstr);
Tcl_UtfToExternalDString(encoding,buf,strlen(buf),&dstr);
- str = rb_str_new2(Tcl_DStringValue(&dstr));
+ /* str = rb_str_new2(Tcl_DStringValue(&dstr)); */
+ str = rb_tainted_str_new2(Tcl_DStringValue(&dstr));
Tcl_FreeEncoding(encoding);
Tcl_DStringFree(&dstr);
@@ -640,9 +924,26 @@ ip_invoke_real(argc, argv, obj)
v = argv[0];
cmd = StringValuePtr(v);
+ /* ip is deleted? */
+ if (Tcl_InterpDeleted(ptr->ip)) {
+ Tcl_ResetResult(ptr->ip);
+ return rb_tainted_str_new2("");
+ }
+
/* map from the command name to a C procedure */
if (!Tcl_GetCommandInfo(ptr->ip, cmd, &info)) {
- rb_raise(rb_eNameError, "invalid command name `%s'", cmd);
+ /* if (event_loop_abort_on_exc || cmd[0] != '.') { */
+ if (event_loop_abort_on_exc > 0) {
+ rb_raise(rb_eNameError, "invalid command name `%s'", cmd);
+ } else {
+ if (event_loop_abort_on_exc < 0) {
+ rb_warning("invalid command name `%s' (ignore)", cmd);
+ } else {
+ rb_warn("invalid command name `%s' (ignore)", cmd);
+ }
+ Tcl_ResetResult(ptr->ip);
+ return rb_tainted_str_new2("");
+ }
}
/* memory allocation for arguments of this command */
@@ -661,7 +962,7 @@ ip_invoke_real(argc, argv, obj)
else
#endif
{
- /* string interface */
+ /* string interface */
av = (char **)ALLOCA_N(char *, argc+1);
for (i = 0; i < argc; ++i) {
v = argv[i];
@@ -694,16 +995,47 @@ ip_invoke_real(argc, argv, obj)
#endif
{
TRAP_BEG;
+#if TCL_MAJOR_VERSION >= 8
+# ifdef CONST84 /* Tcl8.4.x -- ?.?.? (current latest version is 8.4.4) */
+ ptr->return_value = (*info.proc)(info.clientData, ptr->ip,
+ argc, (CONST84 char **)av);
+# else
+# if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION <= 4 /* Tcl8.0.x -- 8.4b1 */
ptr->return_value = (*info.proc)(info.clientData, ptr->ip, argc, av);
+
+# else /* unknown (maybe TCL_VERSION >= 8.5) */
+# ifdef CONST
+ ptr->return_value = (*info.proc)(info.clientData, ptr->ip,
+ argc, (CONST char **)av);
+# else
+ ptr->return_value = (*info.proc)(info.clientData, ptr->ip, argc, av);
+# endif
+# endif
+# endif
+#else /* TCL_MAJOR_VERSION < 8 */
+ ptr->return_value = (*info.proc)(info.clientData, ptr->ip, argc, av);
+#endif
TRAP_END;
}
+ /* exception on mainloop */
if (ptr->return_value == TCL_ERROR) {
- rb_raise(rb_eRuntimeError, "%s", ptr->ip->result);
+ if (event_loop_abort_on_exc > 0 && !Tcl_InterpDeleted(ptr->ip)) {
+ rb_raise(rb_eRuntimeError, "%s", ptr->ip->result);
+ } else {
+ if (event_loop_abort_on_exc < 0) {
+ rb_warning("%s (ignore)", ptr->ip->result);
+ } else {
+ rb_warn("%s (ignore)", ptr->ip->result);
+ }
+ Tcl_ResetResult(ptr->ip);
+ return rb_tainted_str_new2("");
+ }
}
/* pass back the result (as string) */
- return rb_str_new2(ptr->ip->result);
+ /* return rb_str_new2(ptr->ip->result); */
+ return rb_tainted_str_new2(ptr->ip->result);
}
VALUE
@@ -725,15 +1057,15 @@ invoke_queue_handler(evPtr, flags)
Tcl_Event *evPtr;
int flags;
{
- struct invoke_queue *tmp, *q = (struct invoke_queue *)evPtr;
+ struct invoke_queue *q = (struct invoke_queue *)evPtr;
DUMP1("do_invoke_queue_handler");
DUMP2("invoke queue_thread : %lx", rb_thread_current());
DUMP2("added by thread : %lx", q->thread);
if (q->done) {
- /* processed by another event-loop */
- return 0;
+ /* processed by another event-loop */
+ return 0;
}
/* process it */
@@ -741,11 +1073,12 @@ invoke_queue_handler(evPtr, flags)
/* check safe-level */
if (rb_safe_level() != q->safe_level) {
- *(q->result) = rb_funcall(rb_proc_new(ivq_safelevel_handler,
- Data_Wrap_Struct(rb_cData,0,0,q)),
- rb_intern("call"), 0);
+ *(q->result)
+ = rb_funcall(rb_proc_new(ivq_safelevel_handler,
+ Data_Wrap_Struct(rb_cData,0,0,q)),
+ rb_intern("call"), 0);
} else {
- *(q->result) = ip_invoke_real(q->argc, q->argv, q->obj);
+ *(q->result) = ip_invoke_real(q->argc, q->argv, q->obj);
}
/* back to caller */
@@ -771,8 +1104,8 @@ ip_invoke(argc, argv, obj)
rb_raise(rb_eArgError, "command name missing");
}
if (eventloop_thread == 0 || current == eventloop_thread) {
- DUMP2("invoke from current eventloop %lx", current);
- return ip_invoke_real(argc, argv, obj);
+ DUMP2("invoke from current eventloop %lx", current);
+ return ip_invoke_real(argc, argv, obj);
}
DUMP2("invoke from thread %lx (NOT current eventloop)", current);
@@ -848,6 +1181,7 @@ Init_tcltklib()
rb_raise(rb_eLoadError, "tcltklib: tcltk_stubs init error(%d)", ret);
#endif
+ rb_define_const(ev_flag, "NONE", INT2FIX(0));
rb_define_const(ev_flag, "WINDOW", INT2FIX(TCL_WINDOW_EVENTS));
rb_define_const(ev_flag, "FILE", INT2FIX(TCL_FILE_EVENTS));
rb_define_const(ev_flag, "TIMER", INT2FIX(TCL_TIMER_EVENTS));
@@ -856,7 +1190,8 @@ Init_tcltklib()
rb_define_const(ev_flag, "DONT_WAIT", INT2FIX(TCL_DONT_WAIT));
eTkCallbackBreak = rb_define_class("TkCallbackBreak", rb_eStandardError);
- eTkCallbackContinue = rb_define_class("TkCallbackContinue",rb_eStandardError);
+ eTkCallbackContinue = rb_define_class("TkCallbackContinue",
+ rb_eStandardError);
rb_define_module_function(lib, "mainloop", lib_mainloop, -1);
rb_define_module_function(lib, "mainloop_watchdog",
@@ -864,13 +1199,24 @@ Init_tcltklib()
rb_define_module_function(lib, "do_one_event", lib_do_one_event, -1);
rb_define_module_function(lib, "set_eventloop_tick",set_eventloop_tick,1);
rb_define_module_function(lib, "get_eventloop_tick",get_eventloop_tick,0);
+ rb_define_module_function(lib, "set_no_event_wait", set_no_event_wait, 1);
+ rb_define_module_function(lib, "get_no_event_wait", get_no_event_wait, 0);
rb_define_module_function(lib, "set_eventloop_weight",
set_eventloop_weight, 2);
rb_define_module_function(lib, "get_eventloop_weight",
get_eventloop_weight, 0);
-
- rb_define_singleton_method(ip, "allocate", ip_alloc, 0);
- rb_define_method(ip, "initialize", ip_init, 0);
+ rb_define_module_function(lib, "mainloop_abort_on_exception",
+ rb_evloop_abort_on_exc, 0);
+ rb_define_module_function(lib, "mainloop_abort_on_exception=",
+ rb_evloop_abort_on_exc_set, 1);
+
+ rb_define_alloc_func(ip, ip_alloc);
+ rb_define_method(ip, "initialize", ip_init, -1);
+ rb_define_method(ip, "create_slave", ip_create_slave, -1);
+ rb_define_method(ip, "make_safe", ip_make_safe, 0);
+ rb_define_method(ip, "safe?", ip_is_safe_p, 0);
+ rb_define_method(ip, "delete", ip_delete, 0);
+ rb_define_method(ip, "deleted?", ip_is_deleted_p, 0);
rb_define_method(ip, "_eval", ip_eval, 1);
rb_define_method(ip, "_toUTF8",ip_toUTF8,2);
rb_define_method(ip, "_fromUTF8",ip_fromUTF8,2);
@@ -879,13 +1225,18 @@ Init_tcltklib()
rb_define_method(ip, "mainloop", lib_mainloop, -1);
rb_define_method(ip, "mainloop_watchdog", lib_mainloop_watchdog, -1);
rb_define_method(ip, "do_one_event", lib_do_one_event, -1);
+ rb_define_method(ip, "mainloop_abort_on_exception",
+ rb_evloop_abort_on_exc, 0);
+ rb_define_method(ip, "mainloop_abort_on_exception=",
+ rb_evloop_abort_on_exc_set, 1);
rb_define_method(ip, "set_eventloop_tick", set_eventloop_tick, 1);
rb_define_method(ip, "get_eventloop_tick", get_eventloop_tick, 0);
+ rb_define_method(ip, "set_no_event_wait", set_no_event_wait, 1);
+ rb_define_method(ip, "get_no_event_wait", get_no_event_wait, 0);
rb_define_method(ip, "set_eventloop_weight", set_eventloop_weight, 2);
rb_define_method(ip, "get_eventloop_weight", get_eventloop_weight, 0);
rb_define_method(ip, "restart", lib_restart, 0);
- main_thread = rb_thread_current();
eventloop_thread = 0;
watchdog_thread = 0;
diff --git a/ext/tk/MANIFEST b/ext/tk/MANIFEST
index bf03ac3841..c20ee7ffa3 100644
--- a/ext/tk/MANIFEST
+++ b/ext/tk/MANIFEST
@@ -3,24 +3,174 @@ extconf.rb
depend
tkutil.c
lib/README
+lib/multi-tk.rb
lib/tk.rb
lib/tkafter.rb
lib/tkbgerror.rb
lib/tkcanvas.rb
lib/tkclass.rb
+lib/tkconsole.rb
lib/tkdialog.rb
lib/tkentry.rb
lib/tkfont.rb
+lib/tkmacpkg.rb
lib/tkmenubar.rb
lib/tkmngfocus.rb
lib/tkpalette.rb
lib/tkscrollbox.rb
lib/tktext.rb
lib/tkvirtevent.rb
+lib/tkwinpkg.rb
+sample/safe-tk.rb
sample/tkbiff.rb
sample/tkbrowse.rb
sample/tkdialog.rb
sample/tkfrom.rb
sample/tkhello.rb
sample/tkline.rb
+sample/tkmenubutton.rb
+sample/tkoptdb-safeTk.rb
+sample/tkoptdb.rb
+sample/resource.ja
+sample/resource.en
sample/tktimer.rb
+sample/tktimer2.rb
+sample/demos-en/ChangeLog
+sample/demos-en/ChangeLog.prev
+sample/demos-en/README
+sample/demos-en/README.tkencoding
+sample/demos-en/arrow.rb
+sample/demos-en/bind.rb
+sample/demos-en/bitmap.rb
+sample/demos-en/browse1
+sample/demos-en/browse2
+sample/demos-en/button.rb
+sample/demos-en/check.rb
+sample/demos-en/clrpick.rb
+sample/demos-en/colors.rb
+sample/demos-en/cscroll.rb
+sample/demos-en/ctext.rb
+sample/demos-en/dialog1.rb
+sample/demos-en/dialog2.rb
+sample/demos-en/doc.org/README
+sample/demos-en/doc.org/README.JP
+sample/demos-en/doc.org/README.tk80
+sample/demos-en/doc.org/license.terms
+sample/demos-en/doc.org/license.terms.tk80
+sample/demos-en/entry1.rb
+sample/demos-en/entry2.rb
+sample/demos-en/filebox.rb
+sample/demos-en/floor.rb
+sample/demos-en/form.rb
+sample/demos-en/hello
+sample/demos-en/hscale.rb
+sample/demos-en/icon.rb
+sample/demos-en/image1.rb
+sample/demos-en/image2.rb
+sample/demos-en/images/earth.gif
+sample/demos-en/images/earthris.gif
+sample/demos-en/images/face.xbm
+sample/demos-en/images/flagdown.xbm
+sample/demos-en/images/flagup.xbm
+sample/demos-en/images/gray25.xbm
+sample/demos-en/images/grey.25
+sample/demos-en/images/grey.5
+sample/demos-en/images/letters.xbm
+sample/demos-en/images/noletter.xbm
+sample/demos-en/images/pattern.xbm
+sample/demos-en/images/tcllogo.gif
+sample/demos-en/images/teapot.ppm
+sample/demos-en/items.rb
+sample/demos-en/ixset
+sample/demos-en/label.rb
+sample/demos-en/menu.rb
+sample/demos-en/menubu.rb
+sample/demos-en/msgbox.rb
+sample/demos-en/patch_1.1c1
+sample/demos-en/plot.rb
+sample/demos-en/puzzle.rb
+sample/demos-en/radio.rb
+sample/demos-en/rmt
+sample/demos-en/rolodex
+sample/demos-en/rolodex-j
+sample/demos-en/ruler.rb
+sample/demos-en/sayings.rb
+sample/demos-en/search.rb
+sample/demos-en/square
+sample/demos-en/states.rb
+sample/demos-en/style.rb
+sample/demos-en/tcolor
+sample/demos-en/tcolor.bak
+sample/demos-en/text.rb
+sample/demos-en/timer
+sample/demos-en/tkencoding.rb
+sample/demos-en/twind.rb
+sample/demos-en/vscale.rb
+sample/demos-en/widget
+sample/demos-jp/README
+sample/demos-jp/arrow.rb
+sample/demos-jp/bind.rb
+sample/demos-jp/bitmap.rb
+sample/demos-jp/browse1
+sample/demos-jp/browse2
+sample/demos-jp/button.rb
+sample/demos-jp/check.rb
+sample/demos-jp/clrpick.rb
+sample/demos-jp/colors.rb
+sample/demos-jp/cscroll.rb
+sample/demos-jp/ctext.rb
+sample/demos-jp/dialog1.rb
+sample/demos-jp/dialog2.rb
+sample/demos-jp/doc.org/README
+sample/demos-jp/doc.org/README.JP
+sample/demos-jp/doc.org/README.tk80
+sample/demos-jp/doc.org/license.terms
+sample/demos-jp/doc.org/license.terms.tk80
+sample/demos-jp/entry1.rb
+sample/demos-jp/entry2.rb
+sample/demos-jp/filebox.rb
+sample/demos-jp/floor.rb
+sample/demos-jp/form.rb
+sample/demos-jp/hello
+sample/demos-jp/hscale.rb
+sample/demos-jp/icon.rb
+sample/demos-jp/image1.rb
+sample/demos-jp/image2.rb
+sample/demos-jp/images/earth.gif
+sample/demos-jp/images/earthris.gif
+sample/demos-jp/images/face.bmp
+sample/demos-jp/images/flagdown.bmp
+sample/demos-jp/images/flagup.bmp
+sample/demos-jp/images/gray25.bmp
+sample/demos-jp/images/grey.25
+sample/demos-jp/images/grey.5
+sample/demos-jp/images/letters.bmp
+sample/demos-jp/images/noletter.bmp
+sample/demos-jp/images/pattern.bmp
+sample/demos-jp/images/tcllogo.gif
+sample/demos-jp/images/teapot.ppm
+sample/demos-jp/items.rb
+sample/demos-jp/ixset
+sample/demos-jp/label.rb
+sample/demos-jp/menu.rb
+sample/demos-jp/menu8x.rb
+sample/demos-jp/menubu.rb
+sample/demos-jp/msgbox.rb
+sample/demos-jp/plot.rb
+sample/demos-jp/puzzle.rb
+sample/demos-jp/radio.rb
+sample/demos-jp/rmt
+sample/demos-jp/rolodex
+sample/demos-jp/rolodex-j
+sample/demos-jp/ruler.rb
+sample/demos-jp/sayings.rb
+sample/demos-jp/search.rb
+sample/demos-jp/square
+sample/demos-jp/states.rb
+sample/demos-jp/style.rb
+sample/demos-jp/tcolor
+sample/demos-jp/text.rb
+sample/demos-jp/timer
+sample/demos-jp/twind.rb
+sample/demos-jp/vscale.rb
+sample/demos-jp/widget
diff --git a/ext/tk/lib/README b/ext/tk/lib/README
index 87b59f4972..c003adb2c8 100644
--- a/ext/tk/lib/README
+++ b/ext/tk/lib/README
@@ -1,15 +1,19 @@
README this file
+multi-tk.rb multiple Tk interpreter (included safe-Tk) support
tk.rb Tk interface
tkafter.rb handles Tcl after
tkbgerror.rb Tk error module
tkcanvas.rb Tk canvas interface
tkclass.rb provides generic names for Tk classes
+tkconsole.rb console command support
tkdialog.rb Tk dialog class
tkentry.rb Tk entry class
tkfont.rb Tk font support
+tkmacpkg.rb Mac resource support
tkmenubar.rb TK menubar utility
tkmngfocus.rb focus manager
tkpalette.rb pallete support
tkscrollbox.rb scroll box, also example of compound widget
tktext.rb text classes
tkvirtevent.rb virtual event support
+tkwinpkg.rb Win DDE and registry support
diff --git a/ext/tk/lib/multi-tk.rb b/ext/tk/lib/multi-tk.rb
new file mode 100644
index 0000000000..c74b5714b8
--- /dev/null
+++ b/ext/tk/lib/multi-tk.rb
@@ -0,0 +1,1195 @@
+#
+# multi-tk.rb - supports multi Tk interpreters
+# by Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp>
+
+require 'tcltklib'
+require 'thread'
+
+################################################
+# ignore exception on the mainloop
+
+# TclTkLib.mainloop_abort_on_exception = false
+TclTkLib.mainloop_abort_on_exception = nil
+
+
+################################################
+# exceptiopn to treat the return value from IP
+class MultiTkIp_OK < Exception
+ def self.send(thred, ret=nil)
+ thread.raise self.new(ret)
+ end
+
+ def initialize(ret=nil)
+ super('succeed')
+ @return_value = ret
+ end
+
+ attr_reader :return_value
+ alias value return_value
+end
+MultiTkIp_OK.freeze
+
+
+################################################
+# methods for construction
+class MultiTkIp
+ SLAVE_IP_ID = ['slave'.freeze, '0']
+
+ @@IP_TABLE = {}
+
+ @@INIT_IP_ENV = [] # table of Procs
+ @@ADD_TK_PROCS = [] # table of [name, args, body]
+
+ @@TK_TABLE_LIST = []
+
+ @@TK_CMD_TBL = {}
+
+ ######################################
+
+ @@CB_ENTRY_CLASS = Class.new{|c|
+ def initialize(ip, cmd)
+ @ip = ip
+ @cmd = cmd
+ end
+ attr_reader :ip, :cmd
+ def call(*args)
+ begin
+ unless @ip.deleted?
+ @ip.cb_eval(@cmd, *args)
+ end
+ rescue TkCallbackBreak, TkCallbackContinue
+ fail
+ rescue Exception
+ end
+ end
+ }
+
+ ######################################
+
+ def _keys2opts(keys)
+ keys.collect{|k,v| "-#{k} #{v}"}.join(' ')
+ end
+ private :_keys2opts
+
+ def _check_and_return(thread, exception, wait=3)
+ # wait to stop the caller thread
+ return nil unless thread
+ wait.times{
+ if thread.stop?
+ # ready to send exception
+ thread.raise exception
+ return thread
+ end
+
+ # wait
+ Thread.pass
+ }
+
+ # unexpected error
+ thread.raise RuntimeError, "the thread may not wait for the return value"
+ return thread
+ end
+
+ ######################################
+
+ def set_safe_level(safe)
+ @cmd_queue.enq([@system, 'set_safe_level', safe])
+ self
+ end
+ def self.set_safe_level(safe)
+ __getip.set_safe_level(safe)
+ self
+ end
+
+ def _create_receiver_and_watchdog()
+ # command-procedures receiver
+ receiver = Thread.new{
+ loop do
+ thread, cmd, *args = @cmd_queue.deq
+ if thread == @system
+ case cmd
+ when 'set_safe_level'
+ begin
+ $SAFE = args[0]
+ rescue Exception
+ nil
+ end
+ else
+ # ignore
+ end
+ else
+ begin
+ ret = cmd.call(*args)
+ rescue Exception => e
+ # raise exception
+ _check_and_return(thread, e)
+ else
+ # no exception
+ _check_and_return(thread, MultiTkIp_OK.new(ret))
+ end
+ end
+ end
+ }
+
+ # watchdog of receiver
+ watchdog = Thread.new{
+ begin
+ receiver.join
+ rescue Exception
+ # ignore all kind of Exception
+ end
+ # receiver is dead
+ loop do
+ thread, cmd, *args = @cmd_queue.deq
+ next unless thread
+ if thread.alive?
+ if @interp.deleted?
+ thread.raise RuntimeError, 'the interpreter is already deleted'
+ else
+ thread.raise RuntimeError,
+ 'the interpreter no longer receives command procedures'
+ end
+ end
+ end
+ }
+
+ # return threads
+ [receiver, watchdog]
+ end
+ private :_check_and_return, :_create_receiver_and_watchdog
+
+ ######################################
+
+ if self.const_defined? :DEFAULT_MASTER_NAME
+ name = DEFAULT_MASTER_NAME.to_s
+ else
+ name = nil
+ end
+ if self.const_defined?(:DEFAULT_MASTER_OPTS) &&
+ DEFAULT_MASTER_OPTS.kind_of?(Hash)
+ keys = DEFAULT_MASTER_OPTS
+ else
+ keys = {}
+ end
+
+ @@DEFAULT_MASTER = self.allocate
+ @@DEFAULT_MASTER.instance_eval{
+ @encoding = []
+
+ @tk_windows = {}
+
+ @tk_table_list = []
+
+ @slave_ip_tbl = {}
+
+ unless keys.kind_of? Hash
+ fail ArgumentError, "expecting a Hash object for the 2nd argument"
+ end
+
+ @interp = TclTkIp.new(name, _keys2opts(keys))
+ @ip_name = nil
+
+ @system = Object.new
+
+ @threadgroup = Thread.current.group
+
+ @cmd_queue = Queue.new
+
+ @cmd_receiver, @receiver_watchdog = _create_receiver_and_watchdog()
+
+ @threadgroup.add @cmd_receiver
+ @threadgroup.add @receiver_watchdog
+
+ # NOT enclose @threadgroup for @@DEFAULT_MASTER
+
+ @@IP_TABLE[ThreadGroup::Default] = self
+ @@IP_TABLE[@threadgroup] = self
+ }
+ @@DEFAULT_MASTER.freeze # defend against modification
+
+ ######################################
+
+ def self.inherited(subclass)
+ # trust if on ThreadGroup::Default or @@DEFAULT_MASTER's ThreadGroup
+ if @@IP_TABLE[Thread.current.group] == @@DEFAULT_MASTER
+ begin
+ class << subclass
+ self.methods.each{|m|
+ begin
+ unless m == '__id__' || m == '__send__' || m == 'freeze'
+ undef_method(m)
+ end
+ rescue Exception
+ # ignore all exceptions
+ end
+ }
+ end
+ ensure
+ subclass.freeze
+ fail SecurityError,
+ "cannot create subclass of MultiTkIp on a untrusted ThreadGroup"
+ end
+ end
+ end
+
+ ######################################
+
+ SAFE_OPT_LIST = ['accessPath', 'statics', 'nested', 'deleteHook']
+ def _parse_slaveopts(keys)
+ name = nil
+ safe = false
+ safe_opts = {}
+ tk_opts = {}
+
+ keys.each{|k,v|
+ if k.to_s == 'name'
+ name = v
+ elsif k.to_s == 'safe'
+ safe = v
+ elsif SAFE_OPT_LIST.member?(k.to_s)
+ safe_opts[k] = v
+ else
+ tk_opts[k] = v
+ end
+ }
+
+ [name, safe, safe_opts, tk_opts]
+ end
+ private :_parse_slaveopts
+
+ def _create_slave_ip_name
+ name = SLAVE_IP_ID.join
+ SLAVE_IP_ID[1].succ!
+ name
+ end
+ private :_create_slave_ip_name
+
+ ######################################
+
+ def __check_safetk_optkeys(optkeys)
+ # based on 'safetk.tcl'
+ new_keys = {}
+ optkeys.each{|k,v| new_keys[k.to_s] = v}
+
+ # check 'display'
+ if !new_keys.key?('display')
+ begin
+ new_keys['display'] = @interp._eval('winfo screen .')
+ rescue
+ if ENV[DISPLAY]
+ new_keys['display'] = ENV[DISPLAY]
+ elsif !new_keys.key?('use')
+ warn "Warning: no screen info or ENV[DISPLAY], so use ':0.0'"
+ new_keys['display'] = ':0.0'
+ end
+ end
+ end
+
+ # check 'use'
+ if new_keys.key?('use')
+ # given 'use'
+ case new_keys['use']
+ when TkWindow
+ new_keys['use'] = TkWinfo.id(new_keys['use'])
+ assoc_display = @interp._eval('winfo screen .')
+ when /^\..*/
+ new_keys['use'] = @interp._invoke('winfo', 'id', new_keys['use'])
+ assoc_display = @interp._invoke('winfo', 'screen', new_keys['use'])
+ else
+ begin
+ pathname = @interp._invoke('winfo', 'pathname', new_keys['use'])
+ assco_display = @interp._invoke('winfo', 'screen', pathname)
+ rescue
+ assoc_display = new_keys['display']
+ end
+ end
+
+ # match display?
+ if assoc_display != new_keys['display']
+ if optkeys.keys?(:display) || optkeys.keys?('display')
+ fail RuntimeError,
+ "conflicting 'display'=>#{new_keys['display']} " +
+ "and display '#{assoc_display}' on 'use'=>#{new_keys['use']}"
+ else
+ new_keys['display'] = assoc_display
+ end
+ end
+ end
+
+ # return
+ new_keys
+ end
+ private :__check_safetk_optkeys
+
+ def __create_safetk_frame(slave_ip, slave_name, app_name, keys)
+ # display option is used by ::safe::loadTk
+ loadTk_keys = {}
+ loadTk_keys['display'] = keys['display']
+ dup_keys = keys.dup
+
+ # keys for toplevel : allow followings
+ toplevel_keys = {}
+ ['height', 'width', 'background', 'menu'].each{|k|
+ toplevel_keys[k] = dup_keys.delete(k) if dup_keys.key?(k)
+ }
+ toplevel_keys['classname'] = 'SafeTk'
+ toplevel_keys['screen'] = dup_keys.delete('display')
+
+ # other keys used by pack option of container frame
+
+ # create toplevel widget
+ begin
+ top = TkToplevel.new(toplevel_keys)
+ rescue NameError
+ fail unless @interp.safe?
+ fail SecurityError, "unable create toplevel on the safe interpreter"
+ end
+ msg = "Untrusted Ruby/Tk applet (#{slave_name})"
+ if app_name.kind_of?(String)
+ top.title "#{app_name} (#{slave_name})"
+ else
+ top.title msg
+ end
+
+ # procedure to delete slave interpreter
+ slave_delete_proc = proc{
+ unless slave_ip.deleted?
+ if slave_ip._invoke('info', 'command', '.') != ""
+ slave_ip._invoke('destroy', '.')
+ end
+ slave_ip.delete
+ end
+ }
+ tag = TkBindTag.new.bind('Destroy', slave_delete_proc)
+
+ # create control frame
+ TkFrame.new(top, :bg=>'red', :borderwidth=>3, :relief=>'ridge') {|fc|
+ fc.bindtags = fc.bindtags.unshift(tag)
+
+ TkFrame.new(fc, :bd=>0){|f|
+ TkButton.new(f,
+ :text=>'Delete', :bd=>1, :padx=>2, :pady=>0,
+ :highlightthickness=>0, :command=>slave_delete_proc
+ ).pack(:side=>:right, :fill=>:both)
+ f.pack(:side=>:right, :fill=>:both, :expand=>true)
+ }
+
+ TkLabel.new(fc, :text=>msg, :padx=>2, :pady=>0,
+ :anchor=>:w).pack(:side=>:left, :fill=>:both, :expand=>true)
+
+ fc.pack(:side=>:bottom, :fill=>:x)
+ }
+
+ # container frame for slave interpreter
+ dup_keys['fill'] = :both unless dup_keys.key?('fill')
+ dup_keys['expand'] = true unless dup_keys.key?('expand')
+ c = TkFrame.new(top, :container=>true).pack(dup_keys)
+
+ # return keys
+ loadTk_keys['use'] = TkWinfo.id(c)
+ loadTk_keys
+ end
+ private :__create_safetk_frame
+
+ def __create_safe_slave_obj(safe_opts, app_name, tk_opts)
+ # safe interpreter
+ # at present, not enough support for '-deleteHook' option
+ ip_name = _create_slave_ip_name
+ slave_ip = @interp.create_slave(ip_name, true)
+ @interp._eval("::safe::interpInit #{ip_name} "+_keys2opts(safe_opts))
+ tk_opts = __check_safetk_optkeys(tk_opts)
+ unless tk_opts.key?('use')
+ tk_opts = __create_safetk_frame(slave_ip, ip_name, app_name, tk_opts)
+ end
+ slave_ip._invoke('set', 'argv0', app_name) if app_name.kind_of?(String)
+ @interp._eval("::safe::loadTk #{ip_name} #{_keys2opts(tk_opts)}")
+ @slave_ip_tbl[ip_name] = slave_ip
+ [slave_ip, ip_name]
+ end
+
+ def __create_trusted_slave_obj(name, keys)
+ ip_name = _create_slave_ip_name
+ slave_ip = @interp.create_slave(ip_name, false)
+ slave_ip._invoke('set', 'argv0', name) if name.kind_of?(String)
+ slave_ip._invoke('set', 'argv', _keys2opts(keys))
+ @interp._invoke('load', '', 'Tk', ip_name)
+ @slave_ip_tbl[ip_name] = slave_ip
+ [slave_ip, ip_name]
+ end
+
+ ######################################
+
+ def _create_slave_object(keys={})
+ ip = MultiTkIp.new_slave(self, keys={})
+ @slave_ip_tbl[ip.name] = ip
+ end
+
+ ######################################
+
+ def initialize(master, safeip=true, keys={})
+ if safeip == nil && !master.master?
+ fail SecurityError, "slave-ip cannot create master-ip"
+ end
+
+ unless keys.kind_of? Hash
+ fail ArgumentError, "expecting a Hash object for the 2nd argument"
+ end
+
+ @encoding = []
+
+ @tk_windows = {}
+
+ @tk_table_list = []
+
+ @slave_ip_tbl = {}
+
+ name, safe, safe_opts, tk_opts = _parse_slaveopts(keys)
+
+ if safeip == nil
+ # create master-ip
+ @interp = TclTkIp.new(name, _keys2opts(tk_opts))
+ @ip_name = nil
+ else
+ # create slave-ip
+ if safeip || master.safe?
+ @interp, @ip_name = master.__create_safe_slave_obj(safe_opts,
+ name, tk_opts)
+ else
+ @interp, @ip_name = master.__create_trusted_slave_obj(name, tk_opts)
+ end
+ @set_alias_proc = proc{|name|
+ master._invoke('interp', 'alias', @ip_name, name, '', name)
+ }.freeze
+ end
+
+ @system = Object.new
+
+ @threadgroup = ThreadGroup.new
+
+ @cmd_queue = Queue.new
+
+ @cmd_receiver, @receiver_watchdog = _create_receiver_and_watchdog()
+
+ @threadgroup.add @cmd_receiver
+ @threadgroup.add @receiver_watchdog
+
+ @threadgroup.enclose
+
+ @@IP_TABLE[@threadgroup] = self
+ _init_ip_internal(@@INIT_IP_ENV, @@ADD_TK_PROCS)
+ @@TK_TABLE_LIST.size.times{ @tk_table_list << {} }
+
+ self.freeze # defend against modification
+ end
+end
+
+
+# get target IP
+class MultiTkIp
+ def self.__getip
+ if Thread.current.group == ThreadGroup::Default
+ @@DEFAULT_MASTER
+ else
+ ip = @@IP_TABLE[Thread.current.group]
+ unless ip
+ fail SecurityError,
+ "cannot call Tk methods on #{Thread.current.inspect}"
+ end
+ ip
+ end
+ end
+end
+
+
+# aliases of constructor
+class << MultiTkIp
+ alias __new new
+ private :__new
+
+ def new_master(keys={}, &b)
+ ip = __new(__getip, nil, keys)
+ ip.eval_proc(&b) if b
+ ip
+ end
+
+ alias new new_master
+
+ def new_slave(keys={}, &b)
+ ip = __new(__getip, false, keys)
+ ip.eval_proc(&b) if b
+ ip
+ end
+ alias new_trusted_slave new_master
+
+ def new_safe_slave(keys={},&b)
+ ip = __new(__getip, true, keys)
+ ip.eval_proc(&b) if b
+ ip
+ end
+ alias new_safeTk new_safe_slave
+end
+
+
+# get info
+class MultiTkIp
+ def inspect
+ s = self.to_s.chop!
+ if master?
+ s << ':master'
+ else
+ if @interp.safe?
+ s << ':safe-slave'
+ else
+ s << ':trusted-slave'
+ end
+ end
+ s << '>'
+ end
+
+ def master?
+ if @ip_name
+ false
+ else
+ true
+ end
+ end
+ def self.master?
+ __getip.master?
+ end
+
+ def slave?
+ not master?
+ end
+ def self.slave?
+ end
+
+ def alive?
+ begin
+ return false unless @cmd_receiver.alive?
+ return false if @interp.deleted?
+ return false if @interp._invoke('interp', 'exists', '') == '0'
+ rescue Exception
+ return false
+ end
+ true
+ end
+ def self.alive?
+ __getip.alive?
+ end
+
+ def path
+ @ip_name || ''
+ end
+ def self.path
+ __getip.path
+ end
+ def ip_name
+ @ip_name || ''
+ end
+ def self.ip_name
+ __getip.ip_name
+ end
+ def to_eval
+ @ip_name || ''
+ end
+ def self.to_eval
+ __getip.to_eval
+ end
+
+ def slaves(all = false)
+ @interp._invoke('interp','slaves').split.map!{|name|
+ if @slave_ip_tbl.key?(name)
+ @slave_ip_tbl[name]
+ elsif all
+ name
+ else
+ nil
+ end
+ }.compact!
+ end
+ def self.slaves(all = false)
+ __getip.slaves(all)
+ end
+end
+
+
+# instance methods to treat tables
+class MultiTkIp
+ def _tk_cmd_tbl
+ MultiTkIp.tk_cmd_tbl.collect{|ent| ent.ip == self }
+ end
+
+ def _tk_windows
+ @tk_windows
+ end
+
+ def _tk_table_list
+ @tk_table_list
+ end
+
+ def _init_ip_env(script)
+ script.call(self)
+ end
+
+ def _add_tk_procs(name, args, body)
+ return if slave?
+ @interp._invoke('proc', name, args, body) if args && body
+ @interp._invoke('interp', 'slaves').split.each{|slave|
+ @interp._invoke('interp', 'alias', slave, name, '', name)
+ }
+ end
+
+ def _init_ip_internal(init_ip_env, add_tk_procs)
+ init_ip_env.each{|script| script.call(self)}
+ add_tk_procs.each{|name, args, body|
+ if master?
+ @interp._invoke('proc', name, args, body) if args && body
+ else
+ @set_alias_proc.call(name)
+ end
+ }
+ end
+end
+
+
+# class methods to treat tables
+class MultiTkIp
+ def self.tk_cmd_tbl
+ @@TK_CMD_TBL
+ end
+ def self.tk_windows
+ __getip._tk_windows
+ end
+ def self.tk_object_table(id)
+ __getip._tk_table_list[id]
+ end
+ def self.create_table
+ id = @@TK_TABLE_LIST.size
+ @@IP_TABLE.each{|tg, ip|
+ ip.instance_eval{@tk_table_list << {}}
+ }
+ obj = Object.new
+ @@TK_TABLE_LIST << obj
+ obj.instance_eval <<-EOD
+ def self.method_missing(m, *args)
+ MultiTkIp.tk_object_table(#{id}).send(m, *args)
+ end
+ EOD
+ obj.freeze
+ return obj
+ end
+
+ def self.init_ip_env(script = Proc.new)
+ @@INIT_IP_ENV << script
+ @@IP_TABLE.each{|tg, ip|
+ ip._init_ip_env(script)
+ }
+ end
+
+ def self.add_tk_procs(name, args=nil, body=nil)
+ @@ADD_TK_PROCS << [name, args, body]
+ @@IP_TABLE.each{|tg, ip|
+ ip._add_tk_procs(name, args, body)
+ }
+ end
+
+ def self.init_ip_internal
+ __getip._init_ip_internal(@@INIT_IP_ENV, @@ADD_TK_PROCS)
+ end
+end
+
+
+# for callback operation
+class MultiTkIp
+ def self.get_cb_entry(cmd)
+ @@CB_ENTRY_CLASS.new(__getip, cmd).freeze
+ end
+
+ def cb_eval(cmd, *args)
+ self.eval_callback{ TkComm._get_eval_string(TkUtil.eval_cmd(cmd, *args)) }
+ end
+end
+
+
+# evaluate a procedure on the proper interpreter
+class MultiTkIp
+ # instance method
+ def eval_proc_core(req_val=true, cmd = Proc.new, *args)
+ # cmd string ==> proc
+ if cmd.kind_of?(String)
+ cmd = proc{ TkComm._get_eval_string(TkUtil.eval_cmd(cmd, *args)) }
+ args = []
+ end
+
+ # on IP thread
+ if (@cmd_receiver == Thread.current)
+ return cmd.call(*args)
+ end
+
+ # send cmd to the proc-queue
+ if req_val
+ @cmd_queue.enq([Thread.current, cmd, *args])
+ else
+ @cmd_queue.enq([nil, cmd, *args])
+ return nil
+ end
+
+ # wait and get return value by exception
+ begin
+ Thread.stop
+ rescue MultiTkIp_OK => ret
+ # return value
+ return ret.value
+ end
+ end
+ private :eval_proc_core
+
+ def eval_callback(cmd = Proc.new, *args)
+ eval_proc_core(false, cmd, *args)
+ end
+
+ def eval_proc(cmd = Proc.new, *args)
+ eval_proc_core(true, cmd, *args)
+ end
+ alias call eval_proc
+
+ # class method
+ def self.eval_proc(cmd = Proc.new, *args)
+ # class ==> interp object
+ __getip.eval_proc(cmd, *args)
+ end
+end
+
+
+# depend on TclTkLib
+# all master/slave IPs are controled by only one event-loop
+class << MultiTkIp
+ def mainloop(check_root = true)
+ TclTkLib.mainloop(check_root)
+ end
+ def mainloop_watchdog(check_root = true)
+ TclTkLib.mainloop_watchdog(check_root)
+ end
+ def do_one_event(flag = TclTkLib::EventFlag::ALL)
+ TclTkLib.do_one_event(flag)
+ end
+ def set_eventloop_tick(tick)
+ TclTkLib.set_eventloop_tick(tick)
+ end
+ def get_eventloop_tick
+ TclTkLib.get_eventloop_tick
+ end
+ def set_no_event_wait(tick)
+ TclTkLib.set_no_event_wait(tick)
+ end
+ def get_no_event_wait
+ TclTkLib.get_no_event_wait
+ end
+ def set_eventloop_weight(loop_max, no_event_tick)
+ TclTkLib.set_eventloop_weight(loop_max, no_event_tick)
+ end
+ def get_eventloop_weight
+ TclTkLib.get_eventloop_weight
+ end
+end
+
+
+# class methods to delegate to TclTkIp
+class << MultiTkIp
+ def method_missing(id, *args)
+ __getip.send(id, *args)
+ end
+
+ def make_safe
+ __getip.make_safe
+ end
+
+ def safe?
+ __getip.safe?
+ end
+
+ def restart
+ __getip.restart
+ end
+
+ def _eval(str)
+ __getip._eval(str)
+ end
+
+ def _invoke(*args)
+ __getip._invoke(*args)
+ end
+
+ def _toUTF8(str, encoding)
+ __getip._toUTF8(str, encoding)
+ end
+
+ def _fromUTF8(str, encoding)
+ __getip._fromUTF8(str, encoding)
+ end
+
+ def _return_value
+ __getip._return_value
+ end
+end
+
+
+# depend on TclTkIp
+class MultiTkIp
+ def mainloop(check_root = true, restart_on_dead = true)
+ unless restart_on_dead
+ @interp.mainloop(check_root)
+ else
+ begin
+ loop do
+ @interp.mainloop(check_root)
+ if check_root
+ begin
+ break if @interp._invoke('winfo', 'exists?', '.') == "1"
+ rescue Exception
+ break
+ end
+ end
+ end
+ rescue StandardError
+ if TclTkLib.mainloop_abort_on_exception != nil
+ STDERR.print("warning: Tk mainloop on ", @interp.inspect,
+ " receives ", $!.class.inspect,
+ " exception (ignore) : ", $!.message, "\n");
+ end
+ retry
+ end
+ end
+ end
+
+ def make_safe
+ @interp.make_safe
+ end
+
+ def safe?
+ @interp.safe?
+ end
+
+ def delete
+ @interp.delete
+ end
+
+ def deleted?
+ @interp.deleted?
+ end
+
+ def restart
+ @interp.restart
+ end
+
+ def _eval(str)
+ @interp._eval(str)
+ end
+
+ def _invoke(*args)
+ @interp._invoke(*args)
+ end
+
+ def _toUTF8(str, encoding)
+ @interp._toUTF8(str, encoding)
+ end
+
+ def _fromUTF8(str, encoding)
+ @interp._fromUTF8(str, encoding)
+ end
+
+ def _return_value
+ @interp._return_value
+ end
+end
+
+
+# interp command support
+class MultiTkIp
+ def _lst2ary(str)
+ return [] if str == ""
+ idx = str.index('{')
+ while idx and idx > 0 and str[idx-1] == ?\\
+ idx = str.index('{', idx+1)
+ end
+ return str.split unless idx
+
+ list = str[0,idx].split
+ str = str[idx+1..-1]
+ i = -1
+ brace = 1
+ str.each_byte {|c|
+ i += 1
+ brace += 1 if c == ?{
+ brace -= 1 if c == ?}
+ break if brace == 0
+ }
+ if i == 0
+ list.push ''
+ elsif str[0, i] == ' '
+ list.push ' '
+ else
+ list.push str[0..i-1]
+ end
+ list += tk_split_simplelist(str[i+1..-1])
+ list
+ end
+ private :_lst2ary
+
+ def _slavearg(slave)
+ if slave.kind_of?(MultiTkIp)
+ slave.path
+ elsif slave.kind_of?(String)
+ slave
+ else
+ cmd_name.to_s
+ end
+ end
+ private :_slavearg
+
+ def alias_info(slave, cmd_name)
+ _lst2ary(@interp._invoke('interp', 'alias', _slavearg(slave), cmd_name))
+ end
+ def self.alias_info(slave, cmd_name)
+ __getip.alias_info(slave, cmd_name)
+ end
+
+ def alias_delete(slave, cmd_name)
+ @interp._invoke('interp', 'alias', _slavearg(slave), cmd_name, '')
+ self
+ end
+ def self.alias_delete(slave, cmd_name)
+ __getip.alias_delete(slave, cmd_name)
+ self
+ end
+
+ def def_alias(slave, new_cmd, org_cmd, *args)
+ ret = @interp._invoke('interp', 'alias', _slavearg(slave), new_cmd,
+ '', org_cmd, *args)
+ (ret == new_cmd)? self: nil
+ end
+ def self.def_alias(slave, new_cmd, org_cmd, *args)
+ ret = __getip.def_alias(slave, new_cmd, org_cmd, *args)
+ (ret == new_cmd)? self: nil
+ end
+
+ def aliases(slave = '')
+ _lst2ary(@interp._invoke('interp', 'aliases', _slavearg(slave)))
+ end
+ def self.aliases(slave = '')
+ __getip.aliases(slave)
+ end
+
+ def delete_slaves(*args)
+ slaves = args.collect{|s| _slavearg(s)}
+ @interp._invoke('interp', 'delete', *slaves) if slaves.size > 0
+ self
+ end
+ def self.delete_slaves(*args)
+ __getip.delete_slaves(*args)
+ self
+ end
+
+ def exist?(slave = '')
+ ret = @interp._invoke('interp', 'exists', _slavearg(slave))
+ (ret == '1')? true: false
+ end
+ def self.exist?(slave = '')
+ __getip.exist?(slave)
+ end
+
+ def delete_cmd(slave, cmd)
+ slave_invoke = @interp._invoke('list', 'rename', cmd, '')
+ @interp._invoke('interp', 'eval', _slavearg(slave), slave_invoke)
+ self
+ end
+ def self.delete_cmd(slave, cmd)
+ __getip.delete_cmd(slave, cmd)
+ self
+ end
+
+ def expose_cmd(slave, cmd, aliasname = nil)
+ if aliasname
+ @interp._invoke('interp', 'expose', _slavearg(slave), cmd, aliasname)
+ else
+ @interp._invoke('interp', 'expose', _slavearg(slave), cmd)
+ end
+ self
+ end
+ def self.expose_cmd(slave, cmd, aliasname = nil)
+ __getip.expose_cmd(slave, cmd, aliasname)
+ self
+ end
+
+ def hide_cmd(slave, cmd, aliasname = nil)
+ if aliasname
+ @interp._invoke('interp', 'hide', _slavearg(slave), cmd, aliasname)
+ else
+ @interp._invoke('interp', 'hide', _slavearg(slave), cmd)
+ end
+ self
+ end
+ def self.hide_cmd(slave, cmd, aliasname = nil)
+ __getip.hide_cmd(slave, cmd, aliasname)
+ self
+ end
+
+ def hidden_cmds(slave = '')
+ _lst2ary(@interp._invoke('interp', 'hidden', _slavearg(slave)))
+ end
+ def self.hidden_cmds(slave = '')
+ __getip.hidden_cmds(slave)
+ end
+
+ def invoke_hidden(slave, cmd, *args)
+ @interp._invoke('interp', 'invokehidden', _slavearg(slave), cmd, *args)
+ end
+ def self.invoke_hidden(slave, cmd, *args)
+ __getip.invoke_hidden(slave, cmd, *args)
+ end
+
+ def invoke_hidden_on_global(slave, cmd, *args)
+ @interp._invoke('interp', 'invokehidden', _slavearg(slave),
+ '-global', cmd, *args)
+ end
+ def self.invoke_hidden_on_global(slave, cmd, *args)
+ __getip.invoke_hidden_on_global(slave, cmd, *args)
+ end
+
+ def mark_trusted(slave = '')
+ @interp._invoke('interp', 'marktrusted', _slavearg(slave))
+ self
+ end
+ def self.mark_trusted(slave = '')
+ __getip.mark_trusted(slave)
+ self
+ end
+
+ def alias_target(aliascmd, slave = '')
+ @interp._invoke('interp', 'target', _slavearg(slave), aliascmd)
+ end
+ def self.alias_target(aliascmd, slave = '')
+ __getip.alias_target(aliascmd, slave)
+ end
+
+ def share_stdin(dist, src = '')
+ @interp._invoke('interp', 'share', src, 'stdin', dist)
+ self
+ end
+ def self.share_stdin(dist, src = '')
+ __getip.share_stdin(dist, src)
+ self
+ end
+
+ def share_stdout(dist, src = '')
+ @interp._invoke('interp', 'share', src, 'stdout', dist)
+ self
+ end
+ def self.share_stdout(dist, src = '')
+ __getip.share_stdout(dist, src)
+ self
+ end
+
+ def share_stderr(dist, src = '')
+ @interp._invoke('interp', 'share', src, 'stderr', dist)
+ self
+ end
+ def self.share_stderr(dist, src = '')
+ __getip.share_stderr(dist, src)
+ self
+ end
+
+ def transfer_stdin(dist, src = '')
+ @interp._invoke('interp', 'transfer', src, 'stdin', dist)
+ self
+ end
+ def self.transfer_stdin(dist, src = '')
+ __getip.transfer_stdin(dist, src)
+ self
+ end
+
+ def transfer_stdout(dist, src = '')
+ @interp._invoke('interp', 'transfer', src, 'stdout', dist)
+ self
+ end
+ def self.transfer_stdout(dist, src = '')
+ __getip.transfer_stdout(dist, src)
+ self
+ end
+
+ def transfer_stderr(dist, src = '')
+ @interp._invoke('interp', 'transfer', src, 'stderr', dist)
+ self
+ end
+ def self.transfer_stderr(dist, src = '')
+ __getip.transfer_stderr(dist, src)
+ self
+ end
+
+ def share_stdio(dist, src = '')
+ @interp._invoke('interp', 'share', src, 'stdin', dist)
+ @interp._invoke('interp', 'share', src, 'stdout', dist)
+ @interp._invoke('interp', 'share', src, 'stderr', dist)
+ self
+ end
+ def self.share_stdio(dist, src = '')
+ __getip.share_stdio(dist, src)
+ self
+ end
+
+ def transfer_stdio(dist, src = '')
+ @interp._invoke('interp', 'transfer', src, 'stdin', dist)
+ @interp._invoke('interp', 'transfer', src, 'stdout', dist)
+ @interp._invoke('interp', 'transfer', src, 'stderr', dist)
+ self
+ end
+ def self.transfer_stdio(dist, src = '')
+ __getip.transfer_stdio(dist, src)
+ self
+ end
+end
+
+
+# encoding convert
+class MultiTkIp
+ # from tkencoding.rb by ttate@jaist.ac.jp
+ alias __eval _eval
+ alias __invoke _invoke
+
+ def encoding
+ @encoding[0]
+ end
+ def encoding=(enc)
+ @encoding[0] = enc
+ end
+
+ def _eval(cmd)
+ if @encoding[0] != nil
+ _fromUTF8(__eval(_toUTF8(cmd, @encoding[0])), @encoding[0])
+ else
+ __eval(cmd)
+ end
+ end
+
+ def _invoke(*cmds)
+ if defined?(@encoding[0]) && @encoding[0] != nil
+ cmds = cmds.collect{|cmd| _toUTF8(cmd, @encoding[0])}
+ _fromUTF8(__invoke(*cmds), @encoding[0])
+ else
+ __invoke(*cmds)
+ end
+ end
+end
+
+
+# end of MultiTkIp definition
+
+MultiTkIp.freeze # defend against modification
+
+
+########################################
+# start Tk which depends on MultiTkIp
+module TkCore
+ INTERP = MultiTkIp
+end
+require 'tk'
diff --git a/ext/tk/lib/tk.rb b/ext/tk/lib/tk.rb
index 9a15f09989..a2c4a7d61f 100644
--- a/ext/tk/lib/tk.rb
+++ b/ext/tk/lib/tk.rb
@@ -1,4 +1,4 @@
-#
+
# tk.rb - Tk interface module using tcltklib
# $Date$
# by Yukihiro Matsumoto <matz@netlab.jp>
@@ -15,8 +15,19 @@ module TkComm
'None'
end
- Tk_CMDTBL = {}
- Tk_WINDOWS = {}
+ #Tk_CMDTBL = {}
+ #Tk_WINDOWS = {}
+ Tk_IDs = ["00000", "00000"] # [0]-cmdid, [1]-winid
+
+ # for backward compatibility
+ Tk_CMDTBL = Object.new
+ def Tk_CMDTBL.method_missing(id, *args)
+ TkCore::INTERP.tk_cmd_tbl.send(id, *args)
+ end
+ Tk_WINDOWS = Object.new
+ def Tk_WINDOWS.method_missing(id, *args)
+ TkCore::INTERP.tk_windows.send(id, *args)
+ end
def error_at
frames = caller()
@@ -31,27 +42,43 @@ module TkComm
return TkRoot.new if path == '.'
begin
- tk_class = TkCore::INTERP._invoke('winfo', 'class', path)
+ #tk_class = TkCore::INTERP._invoke('winfo', 'class', path)
+ tk_class = Tk.ip_invoke('winfo', 'class', path)
rescue
return path
end
- ruby_class = WidgetClassNames[tk_class]
- gen_class_name = ruby_class.name + 'GeneratedOnTk'
+ if ruby_class = WidgetClassNames[tk_class]
+ ruby_class_name = ruby_class.name
+ # gen_class_name = ruby_class_name + 'GeneratedOnTk'
+ gen_class_name = ruby_class_name
+ classname_def = ''
+ elsif Object.const_defined?('Tk' + tk_class)
+ ruby_class_name = 'Tk' + tk_class
+ # gen_class_name = ruby_class_name + 'GeneratedOnTk'
+ gen_class_name = ruby_class_name
+ classname_def = ''
+ else
+ ruby_class_name = 'TkWindow'
+ # gen_class_name = ruby_class_name + tk_class + 'GeneratedOnTk'
+ gen_class_name = 'TkWidget_' + tk_class
+ classname_def = "WidgetClassName = '#{tk_class}'.freeze"
+ end
unless Object.const_defined? gen_class_name
- eval "class #{gen_class_name}<#{ruby_class.name}
- def initialize(path)
- @path=path
- Tk_WINDOWS[@path] = self
- end
- end"
+ Object.class_eval "class #{gen_class_name}<#{ruby_class_name}
+ #{classname_def}
+ end"
end
- eval "#{gen_class_name}.new('#{path}')"
+ Object.class_eval "#{gen_class_name}.new('widgetname'=>'#{path}',
+ 'without_creating'=>true)"
end
+ private :_genobj_for_tkwidget
+ module_function :_genobj_for_tkwidget
def tk_tcl2ruby(val)
if val =~ /^rb_out (c\d+)/
- return Tk_CMDTBL[$1]
+ #return Tk_CMDTBL[$1]
+ return TkCore::INTERP.tk_cmd_tbl[$1]
end
if val.include? ?\s
return val.split.collect{|v| tk_tcl2ruby(v)}
@@ -62,12 +89,16 @@ module TkComm
when /^-?\d+$/
val.to_i
when /^\./
- Tk_WINDOWS[val] ? Tk_WINDOWS[val] : _genobj_for_tkwidget(val)
+ #Tk_WINDOWS[val] ? Tk_WINDOWS[val] : _genobj_for_tkwidget(val)
+ TkCore::INTERP.tk_windows[val]?
+ TkCore::INTERP.tk_windows[val] : _genobj_for_tkwidget(val)
+ when /^i\d+$/
+ TkImage::Tk_IMGTBL[val]? TkImage::Tk_IMGTBL[val] : val
when / /
val.split.collect{|elt|
tk_tcl2ruby(elt)
}
- when /^-?\d+\.\d*$/
+ when /^-?\d+\.?\d*(e[-+]?\d+)?$/
val.to_f
else
val
@@ -80,7 +111,13 @@ module TkComm
while idx and idx > 0 and str[idx-1] == ?\\
idx = str.index('{', idx+1)
end
- return tk_tcl2ruby(str) unless idx
+ unless idx
+ list = tk_tcl2ruby(str)
+ unless Array === list
+ list = [list]
+ end
+ return list
+ end
list = tk_tcl2ruby(str[0,idx])
list = [] if list == ""
@@ -93,6 +130,9 @@ module TkComm
brace -= 1 if c == ?}
break if brace == 0
}
+ if str.size == i + 1
+ return tk_split_list(str[0, i])
+ end
if str[0, i] == ' '
list.push ' '
else
@@ -150,6 +190,7 @@ module TkComm
conf
end
private :hash_kv
+ module_function :hash_kv
def array2tk_list(ary)
ary.collect{|e|
@@ -164,23 +205,24 @@ module TkComm
}.join(" ")
end
private :array2tk_list
+ module_function :array2tk_list
def bool(val)
case val
when "1", 1, 'yes', 'true'
- TRUE
+ true
else
- FALSE
+ false
end
end
def number(val)
case val
when /^-?\d+$/
val.to_i
- when /^-?\d+\.\d*$/
+ when /^-?\d+\.?\d*(e[-+]?\d+)?$/
val.to_f
else
- val
+ fail ArgumentError, format('invalid value for Number:"%s"', val.to_s)
end
end
def string(val)
@@ -193,19 +235,33 @@ module TkComm
end
end
def list(val)
- tk_split_list(val).to_a
+ tk_split_list(val)
+ end
+ def simplelist(val)
+ tk_split_simplelist(val)
end
def window(val)
- Tk_WINDOWS[val]
+ if val =~ /^\./
+ #Tk_WINDOWS[val]? Tk_WINDOWS[val] : _genobj_for_tkwidget(val)
+ TkCore::INTERP.tk_windows[val]?
+ TkCore::INTERP.tk_windows[val] : _genobj_for_tkwidget(val)
+ else
+ nil
+ end
end
def procedure(val)
if val =~ /^rb_out (c\d+)/
- Tk_CMDTBL[$1]
+ #Tk_CMDTBL[$1]
+ #TkCore::INTERP.tk_cmd_tbl[$1]
+ TkCore::INTERP.tk_cmd_tbl[$1].cmd
else
- nil
+ #nil
+ val
end
end
- private :bool, :number, :string, :list, :window, :procedure
+ private :bool, :number, :string, :list, :simplelist, :window, :procedure
+ module_function :bool, :number, :string, :list, :simplelist
+ module_function :window, :procedure
def _get_eval_string(str)
return nil if str == None
@@ -228,11 +284,16 @@ module TkComm
elsif (str.respond_to?(:to_eval))
str = str.to_eval()
else
- str = str.to_s()
+ str = str.to_s() || ''
+ unless str.kind_of? String
+ fail RuntimeError, "fail to convert the object to a string"
+ end
+ str
end
return str
end
private :_get_eval_string
+ module_function :_get_eval_string
def ruby2tcl(v)
if v.kind_of?(Hash)
@@ -245,59 +306,152 @@ module TkComm
end
private :ruby2tcl
- Tk_IDs = [0, 0] # [0]-cmdid, [1]-winid
def _curr_cmd_id
- id = format("c%.4d", Tk_IDs[0])
+ #id = format("c%.4d", Tk_IDs[0])
+ id = "c" + TkComm::Tk_IDs[0]
end
def _next_cmd_id
id = _curr_cmd_id
- Tk_IDs[0] += 1
+ #Tk_IDs[0] += 1
+ TkComm::Tk_IDs[0].succ!
id
end
+ private :_curr_cmd_id, :_next_cmd_id
+ module_function :_curr_cmd_id, :_next_cmd_id
+
def install_cmd(cmd)
return '' if cmd == ''
id = _next_cmd_id
- Tk_CMDTBL[id] = cmd
- @cmdtbl = [] unless @cmdtbl
+ #Tk_CMDTBL[id] = cmd
+ TkCore::INTERP.tk_cmd_tbl[id] = TkCore::INTERP.get_cb_entry(cmd)
+ @cmdtbl = [] unless defined? @cmdtbl
@cmdtbl.push id
return format("rb_out %s", id);
end
def uninstall_cmd(id)
id = $1 if /rb_out (c\d+)/ =~ id
- Tk_CMDTBL.delete(id)
+ #Tk_CMDTBL.delete(id)
+ TkCore::INTERP.tk_cmd_tbl.delete(id)
end
private :install_cmd, :uninstall_cmd
+ module_function :install_cmd
def install_win(ppath,name=nil)
if !name or name == ''
- name = format("w%.4d", Tk_IDs[1])
- Tk_IDs[1] += 1
- end
- if !ppath or ppath == "."
+ #name = format("w%.4d", Tk_IDs[1])
+ #Tk_IDs[1] += 1
+ name = "w" + Tk_IDs[1]
+ Tk_IDs[1].succ!
+ end
+ if name[0] == ?.
+ @path = name.dup
+ elsif !ppath or ppath == "."
@path = format(".%s", name);
else
@path = format("%s.%s", ppath, name)
end
- Tk_WINDOWS[@path] = self
+ #Tk_WINDOWS[@path] = self
+ TkCore::INTERP.tk_windows[@path] = self
end
def uninstall_win()
- Tk_WINDOWS.delete(@path)
+ #Tk_WINDOWS.delete(@path)
+ TkCore::INTERP.tk_windows.delete(@path)
end
+ private :install_win, :uninstall_win
class Event
- def initialize(seq,a,b,c,d,f,h,k,m,o,p,s,t,w,x,y,
+ module TypeNum
+ KeyPress = 2
+ KeyRelease = 3
+ ButtonPress = 4
+ ButtonRelease = 5
+ MotionNotify = 6
+ EnterNotify = 7
+ LeaveNotify = 8
+ FocusIn = 9
+ FocusOut = 10
+ KeymapNotify = 11
+ Expose = 12
+ GraphicsExpose = 13
+ NoExpose = 14
+ VisibilityNotify = 15
+ CreateNotify = 16
+ DestroyNotify = 17
+ UnmapNotify = 18
+ MapNotify = 19
+ MapRequest = 20
+ ReparentNotify = 21
+ ConfigureNotify = 22
+ ConfigureRequest = 23
+ GravityNotify = 24
+ ResizeRequest = 25
+ CirculateNotify = 26
+ CirculateRequest = 27
+ PropertyNotify = 28
+ SelectionClear = 29
+ SelectionRequest = 30
+ SelectionNotify = 31
+ ColormapNotify = 32
+ ClientMessage = 33
+ MappingNotify = 34
+ end
+
+ EV_KEY = '#abcdfhikmopstwxyABDEKNRSTWXY'
+ EV_TYPE = 'nsnnsbnsnsbsxnnnnsnnbsnssnwnn'
+
+ def self.scan_args(arg_str, arg_val)
+ arg_cnv = []
+ arg_str.strip.split(/\s+/).each_with_index{|kwd,idx|
+ if kwd =~ /^%(.)$/
+ if num = EV_KEY.index($1)
+ case EV_TYPE[num]
+ when ?n
+ begin
+ val = TkComm::number(arg_val[idx])
+ rescue ArgumentError
+ # ignore --> no convert
+ val = TkComm::string(arg_val[idx])
+ end
+ arg_cnv << val
+ when ?s
+ arg_cnv << TkComm::string(arg_val[idx])
+ when ?b
+ arg_cnv << TkComm::bool(arg_val[idx])
+ when ?w
+ arg_cnv << TkComm::window(arg_val[idx])
+ when ?x
+ begin
+ arg_cnv << TkComm::number(arg_val[idx])
+ rescue ArgumentError
+ arg_cnv << arg_val[idx]
+ end
+ else
+ arg_cnv << arg_val[idx]
+ end
+ else
+ arg_cnv << arg_val[idx]
+ end
+ else
+ arg_cnv << arg_val[idx]
+ end
+ }
+ arg_cnv
+ end
+
+ def initialize(seq,a,b,c,d,f,h,i,k,m,o,p,s,t,w,x,y,
aa,bb,dd,ee,kk,nn,rr,ss,tt,ww,xx,yy)
@serial = seq
@above = a
@num = b
@count = c
@detail = d
- @focus = (f == 1)
+ @focus = f
@height = h
+ @win_hex = i
@keycode = k
@mode = m
- @override = (o == 1)
+ @override = o
@place = p
@state = s
@time = t
@@ -307,7 +461,7 @@ module TkComm
@char = aa
@borderwidth = bb
@wheel_delta = dd
- @send_event = (ee == 1)
+ @send_event = ee
@keysym = kk
@keysym_num = nn
@rootwin_id = rr
@@ -324,6 +478,7 @@ module TkComm
attr :detail
attr :focus
attr :height
+ attr :win_hex
attr :keycode
attr :mode
attr :override
@@ -350,15 +505,16 @@ module TkComm
def install_bind(cmd, args=nil)
if args
id = install_cmd(proc{|*arg|
- TkUtil.eval_cmd cmd, *arg
+ TkUtil.eval_cmd(cmd, *Event.scan_args(args, arg))
})
id + " " + args
else
- id = install_cmd(proc{|arg|
- TkUtil.eval_cmd cmd, Event.new(*arg)
+ args = ' %# %a %b %c %d %f %h %i %k %m %o %p %s %t %w %x %y' +
+ ' %A %B %D %E %K %N %R %S %T %W %X %Y'
+ id = install_cmd(proc{|*arg|
+ TkUtil.eval_cmd(cmd, Event.new(*Event.scan_args(args, arg)))
})
- id + ' %# %a %b %c %d %f %h %k %m %o %p %s %t %w %x %y' +
- ' %A %B %D %E %K %N %R %S %T %W %X %Y'
+ id + args
end
end
@@ -408,7 +564,8 @@ module TkComm
if context
tk_call(*what+["<#{tk_event_sequence(context)}>"]).collect {|cmdline|
if cmdline =~ /^rb_out (c\d+)\s+(.*)$/
- [Tk_CMDTBL[$1], $2]
+ #[Tk_CMDTBL[$1], $2]
+ [TkCore::INTERP.tk_cmd_tbl[$1], $2]
else
cmdline
end
@@ -434,14 +591,17 @@ module TkComm
def bind(tagOrClass, context, cmd=Proc.new, args=nil)
_bind(["bind", tagOrClass], context, cmd, args)
+ tagOrClass
end
def bind_append(tagOrClass, context, cmd=Proc.new, args=nil)
_bind_append(["bind", tagOrClass], context, cmd, args)
+ tagOrClass
end
def bind_remove(tagOrClass, context)
_bind_remove(['bind', tagOrClass], context)
+ tagOrClass
end
def bindinfo(tagOrClass, context=nil)
@@ -450,10 +610,17 @@ module TkComm
def bind_all(context, cmd=Proc.new, args=nil)
_bind(['bind', 'all'], context, cmd, args)
+ TkBindTag::ALL
end
def bind_append_all(context, cmd=Proc.new, args=nil)
_bind_append(['bind', 'all'], context, cmd, args)
+ TkBindTag::ALL
+ end
+
+ def bind_remove_all(context)
+ _bind_remove(['bind', 'all'], context)
+ TkBindTag::ALL
end
def bindinfo_all(context=nil)
@@ -482,9 +649,94 @@ module TkCore
include TkComm
extend TkComm
- INTERP = TclTkIp.new
+ unless self.const_defined? :INTERP
+ if self.const_defined? :IP_NAME
+ name = IP_NAME.to_s
+ else
+ name = nil
+ end
+ if self.const_defined? :IP_OPTS
+ if IP_OPTS.kind_of?(Hash)
+ opts = hash_kv(IP_OPTS).join(' ')
+ else
+ opts = IP_OPTS.to_s
+ end
+ else
+ opts = ''
+ end
+
+ INTERP = TclTkIp.new(name, opts)
+
+ def INTERP.__getip
+ self
+ end
+
+ INTERP.instance_eval{
+ @tk_cmd_tbl = {}
+ @tk_windows = {}
+
+ @tk_table_list = []
+
+ @init_ip_env = [] # table of Procs
+ @add_tk_procs = [] # table of [name, args, body]
+
+ @cb_entry_class = Class.new{|c|
+ def initialize(ip, cmd)
+ @ip = ip
+ @cmd = cmd
+ end
+ attr_reader :ip, :cmd
+ def call(*args)
+ @ip.cb_eval(@cmd, *args)
+ end
+ }
+ }
+
+ def INTERP.tk_cmd_tbl
+ @tk_cmd_tbl
+ end
+ def INTERP.tk_windows
+ @tk_windows
+ end
+
+ def INTERP.tk_object_table(id)
+ @tk_table_list[id]
+ end
+ def INTERP.create_table
+ id = @tk_table_list.size
+ @tk_table_list << {}
+ obj = Object.new
+ obj.instance_eval <<-EOD
+ def self.method_missing(m, *args)
+ TkCore::INTERP.tk_object_table(#{id}).send(m, *args)
+ end
+ EOD
+ return obj
+ end
+
+ def INTERP.get_cb_entry(cmd)
+ @cb_entry_class.new(__getip, cmd).freeze
+ end
+ def INTERP.cb_eval(cmd, *args)
+ TkComm._get_eval_string(TkUtil.eval_cmd(cmd, *args))
+ end
+
+ def INTERP.init_ip_env(script = Proc.new)
+ @init_ip_env << script
+ script.call(self)
+ end
+ def INTERP.add_tk_procs(name, args = nil, body = nil)
+ @add_tk_procs << [name, args, body]
+ self._invoke('proc', name, args, body) if args && body
+ end
+ def INTERP.init_ip_internal
+ ip = self
+ @init_ip_env.each{|script| script.call(ip)}
+ @add_tk_procs.each{|name,args,body| ip._invoke('proc',name,args,body)}
+ end
+ end
- INTERP._invoke("proc", "rb_out", "args", <<-'EOL')
+ INTERP.add_tk_procs('rb_out', 'args', <<-'EOL')
regsub -all {!} $args {\\!} args
regsub -all "{" $args "\\{" args
if {[set st [catch {ruby [format "TkCore.callback %%Q!%s!" $args]} ret]] != 0} {
@@ -504,6 +756,20 @@ module TkCore
fail TkCallbackContinue, "Tk callback returns 'continue' status"
end
+ def TkCore.callback(arg)
+ # arg = tk_split_list(arg)
+ arg = tk_split_simplelist(arg)
+ #_get_eval_string(TkUtil.eval_cmd(Tk_CMDTBL[arg.shift], *arg))
+ #_get_eval_string(TkUtil.eval_cmd(TkCore::INTERP.tk_cmd_tbl[arg.shift],
+ # *arg))
+ cb_obj = TkCore::INTERP.tk_cmd_tbl[arg.shift]
+ cb_obj.call(*arg)
+ end
+
+ def load_cmd_on_ip(tk_cmd)
+ bool(tk_call('auto_load', tk_cmd))
+ end
+
def after(ms, cmd=Proc.new)
myid = _curr_cmd_id
cmdid = install_cmd(cmd)
@@ -572,9 +838,8 @@ module TkCore
tk_call('clock','seconds').to_i
end
- def TkCore.callback(arg)
- arg = Array(tk_split_list(arg))
- _get_eval_string(TkUtil.eval_cmd(Tk_CMDTBL[arg.shift], *arg))
+ def windowingsystem
+ tk_call('tk', 'windowingsystem')
end
def scaling(scale=nil)
@@ -605,7 +870,7 @@ module TkCore
end
def rb_appsend(interp, async, *args)
- args = args.collect!{|c| _get_eval_string(c).gsub(/[][$"]/, '\\\\\&')}
+ args = args.collect!{|c| _get_eval_string(c).gsub(/[\[\]$"]/, '\\\\\&')}
args.push(').to_s"')
appsend(interp, async, 'ruby "(', *args)
end
@@ -620,7 +885,7 @@ module TkCore
end
def rb_appsend_displayof(interp, win, async, *args)
- args = args.collect!{|c| _get_eval_string(c).gsub(/[][$"]/, '\\\\\&')}
+ args = args.collect!{|c| _get_eval_string(c).gsub(/[\[\]$"]/, '\\\\\&')}
args.push(').to_s"')
appsend_displayof(interp, win, async, 'ruby "(', *args)
end
@@ -629,15 +894,38 @@ module TkCore
tk_call('info', *args)
end
- def mainloop(check_root = true)
- TclTkLib.mainloop(check_root)
+ def mainloop(check_root = true, restart_on_dead = true)
+ unless restart_on_dead
+ TclTkLib.mainloop(check_root)
+ else
+ begin
+ loop do
+ TclTkLib.mainloop(check_root)
+ if check_root
+ begin
+ break if TkWinfo.exist?('.')
+ rescue Exception
+ break
+ end
+ end
+ end
+ rescue StandardError
+ if TclTkLib.mainloop_abort_on_exception != nil
+ STDERR.print("warning: Tk mainloop on ", TkCore::INTERP.inspect,
+ " receives ", $!.class.inspect,
+ " exception (ignore) : ", $!.message, "\n");
+ end
+ retry
+ end
+ end
end
def mainloop_watchdog(check_root = true)
+ # watchdog restarts mainloop when mainloop is dead
TclTkLib.mainloop_watchdog(check_root)
end
- def do_one_event(flag = 0)
+ def do_one_event(flag = TclTkLib::EventFlag::ALL)
TclTkLib.do_one_event(flag)
end
@@ -649,6 +937,14 @@ module TkCore
TclTkLib.get_eventloop_tick
end
+ def set_no_event_wait(wait)
+ TclTkLib.set_no_even_wait(wait)
+ end
+
+ def get_no_event_wait()
+ TclTkLib.get_no_eventloop_wait
+ end
+
def set_eventloop_weight(loop_max, no_event_tick)
TclTkLib.set_eventloop_weight(loop_max, no_event_tick)
end
@@ -657,15 +953,16 @@ module TkCore
TclTkLib.get_eventloop_weight
end
- def restart(app_name = nil, use = nil)
+ def restart(app_name = nil, keys = {})
+ TkCore::INTERP.init_ip_internal
+
tk_call('set', 'argv0', app_name) if app_name
- if use
- tk_call('set', 'argc', 2)
- tk_call('set', 'argv', "-use #{use}")
+ if keys.kind_of?(Hash)
+ # tk_call('set', 'argc', keys.size * 2)
+ tk_call('set', 'argv', hash_kv(keys).join(' '))
end
- TkCore::INTERP.restart
- TkComm::Tk_CMDTBL.clear
- TkComm::Tk_WINDOWS.clear
+
+ INTERP.restart
nil
end
@@ -699,6 +996,22 @@ module TkCore
tk_call 'tk_chooseDirectory', *hash_kv(keys)
end
+ def ip_eval(cmd_string)
+ res = INTERP._eval(cmd_string)
+ if INTERP._return_value() != 0
+ fail RuntimeError, res, error_at
+ end
+ return res
+ end
+
+ def ip_invoke(*args)
+ res = INTERP._invoke(*args)
+ if INTERP._return_value() != 0
+ fail RuntimeError, res, error_at
+ end
+ return res
+ end
+
def tk_call(*args)
puts args.inspect if $DEBUG
args.collect! {|x|ruby2tcl(x)}
@@ -706,12 +1019,14 @@ module TkCore
args.flatten!
print "=> ", args.join(" ").inspect, "\n" if $DEBUG
begin
- res = INTERP._invoke(*args)
+ # res = INTERP._invoke(*args).taint
+ res = INTERP._invoke(*args) # _invoke returns a TAITED string
rescue NameError
err = $!
begin
args.unshift "unknown"
- res = INTERP._invoke(*args)
+ #res = INTERP._invoke(*args).taint
+ res = INTERP._invoke(*args) # _invoke returns a TAITED string
rescue
fail unless /^invalid command/ =~ $!
fail err
@@ -729,6 +1044,8 @@ module TkPackage
include TkCore
extend TkPackage
+ TkCommandNames = ['package'.freeze].freeze
+
def add_path(path)
Tk::AUTO_PATH.value = Tk::AUTO_PATH.to_a << path
end
@@ -784,39 +1101,87 @@ module Tk
include TkCore
extend Tk
- TCL_VERSION = INTERP._invoke("info", "tclversion")
- TK_VERSION = INTERP._invoke("set", "tk_version")
+ TCL_VERSION = INTERP._invoke("info", "tclversion").freeze
+ TK_VERSION = INTERP._invoke("set", "tk_version").freeze
- TCL_PATCHLEVEL = INTERP._invoke("info", "patchlevel")
- TK_PATCHLEVEL = INTERP._invoke("set", "tk_patchLevel")
+ TCL_PATCHLEVEL = INTERP._invoke("info", "patchlevel").freeze
+ TK_PATCHLEVEL = INTERP._invoke("set", "tk_patchLevel").freeze
- TCL_LIBRARY = INTERP._invoke("set", "tcl_library")
- TK_LIBRARY = INTERP._invoke("set", "tk_library")
- LIBRARY = INTERP._invoke("info", "library")
+ TCL_LIBRARY = INTERP._invoke("set", "tcl_library").freeze
+ TK_LIBRARY = INTERP._invoke("set", "tk_library").freeze
+ LIBRARY = INTERP._invoke("info", "library").freeze
- PLATFORM = Hash[*tk_split_simplelist(INTERP._eval('array get tcl_platform'))]
+ PLATFORM = Hash[*tk_split_simplelist(INTERP._invoke('array', 'get',
+ 'tcl_platform'))]
+ PLATFORM.each{|k, v| k.freeze; v.freeze}
+ PLATFORM.freeze
- JAPANIZED_TK = (INTERP._invoke("info", "commands", "kanji") != "")
+ TK_PREV = {}
+ Hash[*tk_split_simplelist(INTERP._invoke('array','get','tkPriv'))].each{|k,v|
+ k.freeze
+ case v
+ when /^-?\d+$/
+ TK_PREV[k] = v.to_i
+ when /^-?\d+\.?\d*(e[-+]?\d+)?$/
+ TK_PREV[k] = v.to_f
+ else
+ TK_PREV[k] = v.freeze
+ end
+ }
+ TK_PREV.freeze
+
+ JAPANIZED_TK = (INTERP._invoke("info", "commands", "kanji") != "").freeze
def root
TkRoot.new
end
- def bell
- tk_call 'bell'
+ def bell(nice = false)
+ if nice
+ tk_call 'bell', '-nice'
+ else
+ tk_call 'bell'
+ end
+ end
+
+ def bell_on_display(win, nice = false)
+ if nice
+ tk_call('bell', '-displayof', win, '-nice')
+ else
+ tk_call('bell', '-displayof', win)
+ end
+ end
+
+ def Tk.destroy(*wins)
+ tk_call('destroy', *wins)
+ end
+
+ def Tk.exit
+ tk_call('destroy', '.')
+ end
+
+ def Tk.current_grabs
+ tk_split_list(tk_call('grab', 'current'))
end
def Tk.focus(display=nil)
if display == nil
- r = tk_call('focus')
+ window(tk_call('focus'))
else
- r = tk_call('focus', '-displayof', display)
+ window(tk_call('focus', '-displayof', display))
end
- tk_tcl2ruby(r)
end
def Tk.focus_lastfor(win)
- tk_tcl2ruby(tk_call('focus', '-lastfor', win))
+ window(tk_call('focus', '-lastfor', win))
+ end
+
+ def Tk.focus_next(win)
+ TkManageFocus.next(win)
+ end
+
+ def Tk.focus_prev(win)
+ TkManageFocus.prev(win)
end
def Tk.strictMotif(bool=None)
@@ -880,8 +1245,8 @@ module Tk
if bar
@xscrollbar = bar
@xscrollbar.orient 'horizontal'
- self.xscrollcommand {|arg| @xscrollbar.set(*arg)}
- @xscrollbar.command {|arg| self.xview(*arg)}
+ self.xscrollcommand {|*arg| @xscrollbar.set(*arg)}
+ @xscrollbar.command {|*arg| self.xview(*arg)}
end
@xscrollbar
end
@@ -889,8 +1254,8 @@ module Tk
if bar
@yscrollbar = bar
@yscrollbar.orient 'vertical'
- self.yscrollcommand {|arg| @yscrollbar.set(*arg)}
- @yscrollbar.command {|arg| self.yview(*arg)}
+ self.yscrollcommand {|*arg| @yscrollbar.set(*arg)}
+ @yscrollbar.command {|*arg| self.yview(*arg)}
end
@yscrollbar
end
@@ -898,80 +1263,180 @@ module Tk
module Wm
include TkComm
+
+ TkCommandNames = ['wm'.freeze].freeze
+
def aspect(*args)
w = tk_call('wm', 'aspect', path, *args)
- list(w) if args.length == 0
+ if args.length == 0
+ list(w)
+ else
+ self
+ end
+ end
+ def attributes(slot=nil,value=None)
+ if slot == nil
+ lst = tk_split_list(tk_call('wm', 'attributes', path))
+ info = {}
+ while key = lst.shift
+ info[key[1..-1]] = lst.shift
+ end
+ info
+ elsif slot.kind_of? Hash
+ tk_call('wm', 'attributes', path, *hash_kv(slot))
+ self
+ elsif value == None
+ tk_call('wm', 'attributes', path, "-#{slot}")
+ else
+ tk_call('wm', 'attributes', path, "-#{slot}", value)
+ self
+ end
end
def client(name=None)
- tk_call 'wm', 'client', path, name
+ if name == None
+ tk_call 'wm', 'client', path
+ else
+ name = '' if name == nil
+ tk_call 'wm', 'client', path, name
+ self
+ end
end
def colormapwindows(*args)
- list(tk_call('wm', 'colormapwindows', path, *args))
+ r = tk_call('wm', 'colormapwindows', path, *args)
+ if args.size == 0
+ list(r)
+ else
+ self
+ end
end
- def wm_command(value=None)
- string(tk_call('wm', 'command', path, value))
+ def wm_command(value=nil)
+ if value
+ tk_call('wm', 'command', path, value)
+ self
+ else
+ procedure(tk_call('wm', 'command', path))
+ end
end
- def deiconify
- tk_call 'wm', 'deiconify', path
+ def deiconify(ex = true)
+ tk_call('wm', 'deiconify', path) if ex
+ self
end
- def focusmodel(*args)
- tk_call 'wm', 'focusmodel', path, *args
+ def focusmodel(mode = nil)
+ if mode
+ tk_call 'wm', 'focusmodel', path, mode
+ self
+ else
+ tk_call 'wm', 'focusmodel', path
+ end
end
def frame
tk_call('wm', 'frame', path)
end
- def geometry(*args)
- tk_call('wm', 'geometry', path, *args)
+ def geometry(geom)
+ if geom
+ tk_call('wm', 'geometry', path, geom)
+ self
+ else
+ tk_call('wm', 'geometry', path)
+ end
end
def grid(*args)
w = tk_call('wm', 'grid', path, *args)
- list(w) if args.size == 0
+ if args.size == 0
+ list(w)
+ else
+ self
+ end
end
def group(*args)
w = tk_call 'wm', 'group', path, *args
- window(w) if args.size == 0
+ if args.size == 0
+ window(w)
+ else
+ self
+ end
end
- def iconbitmap(*args)
- tk_call 'wm', 'iconbitmap', path, *args
+ def iconbitmap(bmp=nil)
+ if bmp
+ tk_call 'wm', 'iconbitmap', path, bmp
+ self
+ else
+ tk_call 'wm', 'iconbitmap', path
+ end
end
- def iconify
- tk_call 'wm', 'iconify', path
+ def iconify(ex = true)
+ tk_call('wm', 'iconify', path) if ex
+ self
end
- def iconmask(*args)
- tk_call 'wm', 'iconmask', path, *args
+ def iconmask(bmp=nil)
+ if bmp
+ tk_call 'wm', 'iconmask', path, bmp
+ self
+ else
+ tk_call 'wm', 'iconmask', path
+ end
end
- def iconname(*args)
- tk_call 'wm', 'iconname', path, *args
+ def iconname(name=nil)
+ if name
+ tk_call 'wm', 'iconname', path, name
+ self
+ else
+ tk_call 'wm', 'iconname', path
+ end
end
def iconposition(*args)
w = tk_call('wm', 'iconposition', path, *args)
- list(w) if args.size == 0
+ if args.size == 0
+ list(w)
+ else
+ self
+ end
end
def iconwindow(*args)
w = tk_call('wm', 'iconwindow', path, *args)
- window(w) if args.size == 0
+ if args.size == 0
+ window(w)
+ else
+ self
+ end
end
def maxsize(*args)
w = tk_call('wm', 'maxsize', path, *args)
- list(w) if args.size == 0
+ if args.size == 0
+ list(w)
+ else
+ self
+ end
end
def minsize(*args)
w = tk_call('wm', 'minsize', path, *args)
- list(w) if args.size == 0
+ if args.size == 0
+ list(w)
+ else
+ self
+ end
end
def overrideredirect(bool=None)
if bool == None
bool(tk_call('wm', 'overrideredirect', path))
else
tk_call 'wm', 'overrideredirect', path, bool
+ self
end
end
- def positionfrom(*args)
- tk_call 'wm', 'positionfrom', path, *args
+ def positionfrom(who=None)
+ if who == None
+ r = tk_call('wm', 'positionfrom', path)
+ (r == "")? nil: r
+ else
+ tk_call('wm', 'positionfrom', path, who)
+ self
+ end
end
def protocol(name=nil, cmd=nil)
if cmd
tk_call('wm', 'protocol', path, name, cmd)
+ self
elsif name
result = tk_call('wm', 'protocol', path, name)
(result == "")? nil : tk_tcl2ruby(result)
@@ -983,22 +1448,55 @@ module Tk
w = tk_call('wm', 'resizable', path, *args)
if args.length == 0
list(w).collect{|e| bool(e)}
+ else
+ self
+ end
+ end
+ def sizefrom(who=None)
+ if who == None
+ r = tk_call('wm', 'sizefrom', path)
+ (r == "")? nil: r
+ else
+ tk_call('wm', 'sizefrom', path, who)
+ self
end
end
- def sizefrom(*args)
- tk_call('wm', 'sizefrom', path, *args)
+ def stackorder
+ list(tk_call('wm', 'stackorder', path))
end
- def state(state=None)
- tk_call 'wm', 'state', path, state
+ def stackorder_isabove(win)
+ bool(tk_call('wm', 'stackorder', path, 'isabove', win))
end
- def title(*args)
- tk_call 'wm', 'title', path, *args
+ def stackorder_isbelow(win)
+ bool(tk_call('wm', 'stackorder', path, 'isbelow', win))
end
- def transient(*args)
- window(tk_call('wm', 'transient', path, *args))
+ def state(state=nil)
+ if state
+ tk_call 'wm', 'state', path, state
+ self
+ else
+ tk_call 'wm', 'state', path
+ end
+ end
+ def title(str=nil)
+ if str
+ tk_call('wm', 'title', path, str)
+ self
+ else
+ tk_call('wm', 'title', path)
+ end
+ end
+ def transient(master=nil)
+ if master
+ tk_call('wm', 'transient', path, master)
+ self
+ else
+ window(tk_call('wm', 'transient', path, master))
+ end
end
- def withdraw
- tk_call 'wm', 'withdraw', path
+ def withdraw(ex = true)
+ tk_call('wm', 'withdraw', path) if ex
+ self
end
end
end
@@ -1011,21 +1509,19 @@ if /^8\.[1-9]/ =~ Tk::TCL_VERSION && !Tk::JAPANIZED_TK
# from tkencoding.rb by ttate@jaist.ac.jp
alias __eval _eval
alias __invoke _invoke
- private :__eval
- private :__invoke
attr_accessor :encoding
def _eval(cmd)
- if @encoding
+ if defined? @encoding
_fromUTF8(__eval(_toUTF8(cmd, @encoding)), @encoding)
else
__eval(cmd)
end
end
-
+
def _invoke(*cmds)
- if @encoding
+ if defined? @encoding
cmds = cmds.collect{|cmd| _toUTF8(cmd, @encoding)}
_fromUTF8(__invoke(*cmds), @encoding)
else
@@ -1035,25 +1531,43 @@ if /^8\.[1-9]/ =~ Tk::TCL_VERSION && !Tk::JAPANIZED_TK
end
module Tk
- def encoding=(name)
- INTERP.encoding = name
- end
+ module Encoding
+ extend Encoding
- def encoding
- INTERP.encoding
- end
+ TkCommandNames = ['encoding'.freeze].freeze
- def encoding_names
- tk_split_simplelist(tk_call('encoding', 'names'))
- end
+ def encoding=(name)
+ TkCore::INTERP.encoding = name
+ end
- def encoding_system
- tk_call('encoding', 'system')
- end
+ def encoding
+ TkCore::INTERP.encoding
+ end
+
+ def encoding_names
+ tk_split_simplelist(tk_call('encoding', 'names'))
+ end
- def encoding_system=(enc)
- tk_call('encoding', 'system', enc)
+ def encoding_system
+ tk_call('encoding', 'system')
+ end
+
+ def encoding_system=(enc)
+ tk_call('encoding', 'system', enc)
+ end
+
+ def encoding_convertfrom(str, enc=None)
+ TkCore::INTERP.__invoke('encoding', 'convertfrom', enc, str)
+ end
+ alias encoding_convert_from encoding_convertfrom
+
+ def encoding_convertto(str, enc=None)
+ TkCore::INTERP.__invoke('encoding', 'convertto', enc, str)
+ end
+ alias encoding_convert_to encoding_convertto
end
+
+ extend Encoding
end
# estimate encoding
@@ -1074,64 +1588,97 @@ if /^8\.[1-9]/ =~ Tk::TCL_VERSION && !Tk::JAPANIZED_TK
else
# dummy methods
+ class TclTkIp
+ alias __eval _eval
+ alias __invoke _invoke
+ end
+
module Tk
- def encoding=(name)
- nil
- end
- def encoding
- nil
- end
- def encoding_names
- nil
- end
- def encoding_system
- nil
- end
- def encoding_system=(enc)
- nil
+ module Encoding
+ extend Encoding
+
+ def encoding=(name)
+ nil
+ end
+ def encoding
+ nil
+ end
+ def encoding_names
+ nil
+ end
+ def encoding_system
+ nil
+ end
+ def encoding_system=(enc)
+ nil
+ end
+
+ def encoding_convertfrom(str, enc=None)
+ str
+ end
+ alias encoding_convert_from encoding_convertfrom
+
+ def encoding_convertto(str, enc=None)
+ str
+ end
+ alias encoding_convert_to encoding_convertto
end
+
+ extend Encoding
end
end
module TkBindCore
def bind(context, cmd=Proc.new, args=nil)
- Tk.bind(to_eval, context, cmd, args)
+ Tk.bind(self, context, cmd, args)
end
def bind_append(context, cmd=Proc.new, args=nil)
- Tk.bind_append(to_eval, context, cmd, args)
+ Tk.bind_append(self, context, cmd, args)
end
def bind_remove(context)
- Tk.bind_remove(to_eval, context)
+ Tk.bind_remove(self, context)
end
def bindinfo(context=nil)
- Tk.bindinfo(to_eval, context)
+ Tk.bindinfo(self, context)
end
end
class TkBindTag
include TkBindCore
- BTagID_TBL = {}
- Tk_BINDTAG_ID = ["btag00000"]
+ #BTagID_TBL = {}
+ BTagID_TBL = TkCore::INTERP.create_table
+ Tk_BINDTAG_ID = ["btag".freeze, "00000"]
+
+ TkCore::INTERP.init_ip_env{ BTagID_TBL.clear }
def TkBindTag.id2obj(id)
BTagID_TBL[id]? BTagID_TBL[id]: id
end
- ALL = self.new
- ALL.instance_eval {
- @id = 'all'
- BTagID_TBL[@id] = self
- }
+ def TkBindTag.new_by_name(name, *args, &b)
+ return BTagID_TBL[name] if BTagID_TBL[name]
+ self.new(*args, &b).instance_eval{
+ BTagID_TBL.delete @id
+ @id = name
+ BTagID_TBL[@id] = self
+ }
+ end
- def initialize(*args)
- @id = Tk_BINDTAG_ID[0]
- Tk_BINDTAG_ID[0] = Tk_BINDTAG_ID[0].succ
+ def initialize(*args, &b)
+ @id = Tk_BINDTAG_ID.join
+ Tk_BINDTAG_ID[1].succ!
BTagID_TBL[@id] = self
- bind(*args) if args != []
+ bind(*args, &b) if args != []
+ end
+
+ ALL = self.new_by_name('all')
+
+ def name
+ @id
end
def to_eval
@@ -1144,22 +1691,45 @@ class TkBindTag
end
class TkBindTagAll<TkBindTag
- def TkBindTagAll.new(*args)
+ def TkBindTagAll.new(*args, &b)
$stderr.puts "Warning: TkBindTagALL is obsolete. Use TkBindTag::ALL\n"
- TkBindTag::ALL.bind(*args) if args != []
+ TkBindTag::ALL.bind(*args, &b) if args != []
TkBindTag::ALL
end
end
+class TkDatabaseClass<TkBindTag
+ def self.new(name, *args, &b)
+ return BTagID_TBL[name] if BTagID_TBL[name]
+ super(name, *args, &b)
+ end
+
+ def initialize(name, *args, &b)
+ @id = name
+ BTagID_TBL[@id] = self
+ bind(*args, &b) if args != []
+ end
+
+ def inspect
+ format "#<TkDatabaseClass: %s>", @id
+ end
+end
+
class TkVariable
include Tk
extend TkCore
- TkVar_CB_TBL = {}
- Tk_VARIABLE_ID = ["v00000"]
+ TkCommandNames = ['tkwait'.freeze].freeze
+
+ #TkVar_CB_TBL = {}
+ #TkVar_ID_TBL = {}
+ TkVar_CB_TBL = TkCore::INTERP.create_table
+ TkVar_ID_TBL = TkCore::INTERP.create_table
+ Tk_VARIABLE_ID = ["v".freeze, "00000"]
- INTERP._invoke("proc", "rb_var", "args", "ruby [format \"TkVariable.callback %%Q!%s!\" $args]")
+ TkCore::INTERP.add_tk_procs('rb_var', 'args',
+ "ruby [format \"TkVariable.callback %%Q!%s!\" $args]")
def TkVariable.callback(args)
name1,name2,op = tk_split_list(args)
@@ -1171,22 +1741,28 @@ class TkVariable
end
def initialize(val="")
- @id = Tk_VARIABLE_ID[0]
- Tk_VARIABLE_ID[0] = Tk_VARIABLE_ID[0].succ
+ @id = Tk_VARIABLE_ID.join
+ Tk_VARIABLE_ID[1].succ!
+ TkVar_ID_TBL[@id] = self
+
+ @trace_var = nil
+ @trace_elem = nil
+ @trace_opts = nil
+
if val == []
INTERP._eval(format('global %s; set %s(0) 0; unset %s(0)',
@id, @id, @id))
elsif val.kind_of?(Array)
a = []
val.each_with_index{|e,i| a.push(i); a.push(array2tk_list(e))}
- s = '"' + a.join(" ").gsub(/[][$"]/, '\\\\\&') + '"'
+ s = '"' + a.join(" ").gsub(/[\[\]$"]/, '\\\\\&') + '"'
INTERP._eval(format('global %s; array set %s %s', @id, @id, s))
elsif val.kind_of?(Hash)
s = '"' + val.to_a.collect{|e| array2tk_list(e)}.join(" ")\
- .gsub(/[][$"]/, '\\\\\&') + '"'
+ .gsub(/[\[\]$"]/, '\\\\\&') + '"'
INTERP._eval(format('global %s; array set %s %s', @id, @id, s))
else
- s = '"' + _get_eval_string(val).gsub(/[][$"]/, '\\\\\&') + '"'
+ s = '"' + _get_eval_string(val).gsub(/[\[\]$"]/, '\\\\\&') + '"'
INTERP._eval(format('global %s; set %s %s', @id, @id, s))
end
end
@@ -1214,7 +1790,7 @@ class TkVariable
def value=(val)
begin
- s = '"' + _get_eval_string(val).gsub(/[][$"]/, '\\\\\&') + '"'
+ s = '"' + _get_eval_string(val).gsub(/[\[\]$"]/, '\\\\\&') + '"'
INTERP._eval(format('global %s; set %s %s', @id, @id, s))
rescue
if INTERP._eval(format('global %s; array exists %s', @id, @id)) != "1"
@@ -1226,12 +1802,12 @@ class TkVariable
elsif val.kind_of?(Array)
a = []
val.each_with_index{|e,i| a.push(i); a.push(array2tk_list(e))}
- s = '"' + a.join(" ").gsub(/[][$"]/, '\\\\\&') + '"'
+ s = '"' + a.join(" ").gsub(/[\[\]$"]/, '\\\\\&') + '"'
INTERP._eval(format('global %s; unset %s; array set %s %s',
@id, @id, @id, s))
elsif val.kind_of?(Hash)
s = '"' + val.to_a.collect{|e| array2tk_list(e)}.join(" ")\
- .gsub(/[][$"]/, '\\\\\&') + '"'
+ .gsub(/[\[\]$"]/, '\\\\\&') + '"'
INTERP._eval(format('global %s; unset %s; array set %s %s',
@id, @id, @id, s))
else
@@ -1270,7 +1846,7 @@ class TkVariable
def ==(other)
case other
when TkVariable
- self.equal(self)
+ self.equal?(other)
when String
self.to_s == other
when Integer
@@ -1435,10 +2011,16 @@ class TkVariable
end
class TkVarAccess<TkVariable
+ def self.new(name, *args)
+ return TkVar_ID_TBL[name] if TkVar_ID_TBL[name]
+ super(name, *args)
+ end
+
def initialize(varname, val=nil)
@id = varname
+ TkVar_ID_TBL[@id] = self
if val
- s = '"' + _get_eval_string(val).gsub(/[][$"]/, '\\\\\&') + '"' #"
+ s = '"' + _get_eval_string(val).gsub(/[\[\]$"]/, '\\\\\&') + '"' #"
INTERP._eval(format('global %s; set %s %s', @id, @id, s))
end
end
@@ -1457,37 +2039,97 @@ module Tk
AUTO_PATH = TkVarAccess.new('auto_path', auto_path)
TCL_PACKAGE_PATH = TkVarAccess.new('tcl_pkgPath')
+
+ TCL_PRECISION = TkVarAccess.new('tcl_precision')
end
module TkSelection
include Tk
extend Tk
- def clear(win=Tk.root)
- tk_call 'selection', 'clear', win.path
+
+ TkCommandNames = ['selection'.freeze].freeze
+
+ def self.clear(sel=nil)
+ if sel
+ tk_call 'selection', 'clear', '-selection', sel
+ else
+ tk_call 'selection', 'clear'
+ end
+ end
+ def self.clear_on_display(win, sel=nil)
+ if sel
+ tk_call 'selection', 'clear', '-displayof', win, '-selection', sel
+ else
+ tk_call 'selection', 'clear', '-displayof', win
+ end
+ end
+ def clear(sel=nil)
+ TkSelection.clear_on_display(self, sel)
+ self
end
- def get(type=None)
- tk_call 'selection', 'get', type
+
+ def self.get(keys=nil)
+ tk_call 'selection', 'get', *hash_kv(keys)
+ end
+ def self.get_on_display(win, keys=nil)
+ tk_call 'selection', 'get', '-displayof', win, *hash_kv(keys)
+ end
+ def get(keys=nil)
+ TkSelection.get_on_display(self, sel)
end
- def TkSelection.handle(win, func, type=None, format=None)
- tk_call 'selection', 'handle', win.path, func, type, format
+
+ def self.handle(win, func=Proc.new, keys=nil, &b)
+ if func.kind_of?(Hash) && keys == nil
+ keys = func
+ func = Proc.new(&b)
+ end
+ args = ['selection', 'handle']
+ args += hash_kv(keys)
+ args += [win, func]
+ tk_call(*args)
end
- def handle(func, type=None, format=None)
- TkSelection.handle self, func, type, format
+ def handle(func=Proc.new, keys=nil, &b)
+ TkSelection.handle(self, func, keys, &b)
end
- def TkSelection.own(win=None, func=None)
- window(tk_call('selection', 'own', win, func))
+
+ def self.get_owner(sel=nil)
+ if sel
+ window(tk_call('selection', 'own', '-selection', sel))
+ else
+ window(tk_call('selection', 'own'))
+ end
+ end
+ def self.get_owner_on_display(win, sel=nil)
+ if sel
+ window(tk_call('selection', 'own', '-displayof', win, '-selection', sel))
+ else
+ window(tk_call('selection', 'own', '-displayof', win))
+ end
end
- def own(func=None)
- TkSelection.own self, func
+ def get_owner(sel=nil)
+ TkSelection.get_owner_on_display(self, sel)
+ self
end
- module_function :clear, :get
+ def self.set_owner(win, keys=nil)
+ tk_call('selection', 'own', *(hash_kv(keys) << win))
+ end
+ def set_owner(keys=nil)
+ TkSelection.set_owner(self, keys)
+ self
+ end
end
module TkKinput
include Tk
extend Tk
+ TkCommandNames = [
+ 'kinput_start'.freeze,
+ 'kinput_send_spot'.freeze,
+ 'kanjiInput'.freeze
+ ].freeze
+
def TkKinput.start(window, style=None)
tk_call 'kinput_start', window.path, style
end
@@ -1550,6 +2192,8 @@ module TkXIM
include Tk
extend Tk
+ TkCommandNames = ['imconfigure'.freeze].freeze
+
def TkXIM.useinputmethods(window=nil, value=nil)
if window
if value
@@ -1566,6 +2210,20 @@ module TkXIM
end
end
+ def TkXIM.caret(window, keys=nil)
+ if keys
+ tk_call('tk', 'caret', window, *hash_kv(keys))
+ self
+ else
+ lst = tk_split_list(tk_call('tk', 'caret', window))
+ info = {}
+ while key = lst.shift
+ info[key[1..-1]] = lst.shift
+ end
+ info
+ end
+ end
+
def TkXIM.configure(window, slot, value=None)
begin
if /^8\.*/ === Tk::TK_VERSION && JAPANIZED_TK
@@ -1604,36 +2262,54 @@ module TkXIM
TkXIM.useinputmethods(self, value)
end
- def imconfigure(window, slot, value=None)
- TkXIM.configinfo(window, slot, value)
+ def caret(keys=nil)
+ TkXIM.caret(self, keys=nil)
+ end
+
+ def imconfigure(slot, value=None)
+ TkXIM.configinfo(self, slot, value)
end
def imconfiginfo(slot=nil)
- TkXIM.configinfo(window, slot)
+ TkXIM.configinfo(self, slot)
end
end
module TkWinfo
include Tk
extend Tk
- def TkWinfo.atom(name)
- number(tk_call('winfo', 'atom', name))
+
+ TkCommandNames = ['winfo'.freeze].freeze
+
+ def TkWinfo.atom(name, win=nil)
+ if win
+ number(tk_call('winfo', 'atom', '-displayof', win, name))
+ else
+ number(tk_call('winfo', 'atom', name))
+ end
end
def winfo_atom(name)
- TkWinfo.atom name
+ TkWinfo.atom(name, self)
end
- def TkWinfo.atomname(id)
- tk_call 'winfo', 'atomname', id
+
+ def TkWinfo.atomname(id, win=nil)
+ if win
+ tk_call('winfo', 'atomname', '-displayof', win, id)
+ else
+ tk_call('winfo', 'atomname', id)
+ end
end
def winfo_atomname(id)
- TkWinfo.atomname id
+ TkWinfo.atomname(id, self)
end
+
def TkWinfo.cells(window)
number(tk_call('winfo', 'cells', window.path))
end
def winfo_cells
TkWinfo.cells self
end
+
def TkWinfo.children(window)
c = tk_call('winfo', 'children', window.path)
list(c)
@@ -1641,61 +2317,75 @@ module TkWinfo
def winfo_children
TkWinfo.children self
end
+
def TkWinfo.classname(window)
tk_call 'winfo', 'class', window.path
end
def winfo_classname
TkWinfo.classname self
end
+ alias winfo_class winfo_classname
+
def TkWinfo.colormapfull(window)
bool(tk_call('winfo', 'colormapfull', window.path))
end
def winfo_colormapfull
TkWinfo.colormapfull self
end
- def TkWinfo.containing(rootX, rootY)
- path = tk_call('winfo', 'containing', rootX, rootY)
- window(path)
+
+ def TkWinfo.containing(rootX, rootY, win=nil)
+ if win
+ window(tk_call('winfo', 'containing', '-displayof', win, rootX, rootY))
+ else
+ window(tk_call('winfo', 'containing', rootX, rootY))
+ end
end
def winfo_containing(x, y)
- TkWinfo.containing x, y
+ TkWinfo.containing(x, y, self)
end
+
def TkWinfo.depth(window)
number(tk_call('winfo', 'depth', window.path))
end
def winfo_depth
TkWinfo.depth self
end
+
def TkWinfo.exist?(window)
bool(tk_call('winfo', 'exists', window.path))
end
def winfo_exist?
TkWinfo.exist? self
end
- def TkWinfo.fpixels(window, number)
- number(tk_call('winfo', 'fpixels', window.path, number))
+
+ def TkWinfo.fpixels(window, dist)
+ number(tk_call('winfo', 'fpixels', window.path, dist))
end
- def winfo_fpixels(number)
- TkWinfo.fpixels self, number
+ def winfo_fpixels(dist)
+ TkWinfo.fpixels self, dist
end
+
def TkWinfo.geometry(window)
tk_call('winfo', 'geometry', window.path)
end
def winfo_geometry
TkWinfo.geometry self
end
+
def TkWinfo.height(window)
number(tk_call('winfo', 'height', window.path))
end
def winfo_height
TkWinfo.height self
end
+
def TkWinfo.id(window)
tk_call('winfo', 'id', window.path)
end
def winfo_id
TkWinfo.id self
end
+
def TkWinfo.interps(window=nil)
if window
tk_split_simplelist(tk_call('winfo', 'interps',
@@ -1707,215 +2397,253 @@ module TkWinfo
def winfo_interps
TkWinfo.interps self
end
+
def TkWinfo.mapped?(window)
bool(tk_call('winfo', 'ismapped', window.path))
end
def winfo_mapped?
TkWinfo.mapped? self
end
+
def TkWinfo.manager(window)
tk_call('winfo', 'manager', window.path)
end
def winfo_manager
TkWinfo.manager self
end
+
def TkWinfo.appname(window)
tk_call('winfo', 'name', window.path)
end
def winfo_appname
TkWinfo.appname self
end
+
def TkWinfo.parent(window)
window(tk_call('winfo', 'parent', window.path))
end
def winfo_parent
TkWinfo.parent self
end
- def TkWinfo.widget(id)
- window(tk_call('winfo', 'pathname', id))
+
+ def TkWinfo.widget(id, win=nil)
+ if win
+ window(tk_call('winfo', 'pathname', '-displayof', win, id))
+ else
+ window(tk_call('winfo', 'pathname', id))
+ end
end
def winfo_widget(id)
- TkWinfo.widget id
+ TkWinfo.widget id, self
end
- def TkWinfo.pixels(window, number)
- number(tk_call('winfo', 'pixels', window.path, number))
+
+ def TkWinfo.pixels(window, dist)
+ number(tk_call('winfo', 'pixels', window.path, dist))
end
- def winfo_pixels(number)
- TkWinfo.pixels self, number
+ def winfo_pixels(dist)
+ TkWinfo.pixels self, dist
end
+
def TkWinfo.reqheight(window)
number(tk_call('winfo', 'reqheight', window.path))
end
def winfo_reqheight
TkWinfo.reqheight self
end
+
def TkWinfo.reqwidth(window)
number(tk_call('winfo', 'reqwidth', window.path))
end
def winfo_reqwidth
TkWinfo.reqwidth self
end
+
def TkWinfo.rgb(window, color)
list(tk_call('winfo', 'rgb', window.path, color))
end
def winfo_rgb(color)
TkWinfo.rgb self, color
end
+
def TkWinfo.rootx(window)
number(tk_call('winfo', 'rootx', window.path))
end
def winfo_rootx
TkWinfo.rootx self
end
+
def TkWinfo.rooty(window)
number(tk_call('winfo', 'rooty', window.path))
end
def winfo_rooty
TkWinfo.rooty self
end
+
def TkWinfo.screen(window)
tk_call 'winfo', 'screen', window.path
end
def winfo_screen
TkWinfo.screen self
end
+
def TkWinfo.screencells(window)
number(tk_call('winfo', 'screencells', window.path))
end
def winfo_screencells
TkWinfo.screencells self
end
+
def TkWinfo.screendepth(window)
number(tk_call('winfo', 'screendepth', window.path))
end
def winfo_screendepth
TkWinfo.screendepth self
end
+
def TkWinfo.screenheight (window)
number(tk_call('winfo', 'screenheight', window.path))
end
def winfo_screenheight
TkWinfo.screenheight self
end
+
def TkWinfo.screenmmheight(window)
number(tk_call('winfo', 'screenmmheight', window.path))
end
def winfo_screenmmheight
TkWinfo.screenmmheight self
end
+
def TkWinfo.screenmmwidth(window)
number(tk_call('winfo', 'screenmmwidth', window.path))
end
def winfo_screenmmwidth
TkWinfo.screenmmwidth self
end
+
def TkWinfo.screenvisual(window)
- tk_call 'winfo', 'screenvisual', window.path
+ tk_call('winfo', 'screenvisual', window.path)
end
def winfo_screenvisual
TkWinfo.screenvisual self
end
+
def TkWinfo.screenwidth(window)
number(tk_call('winfo', 'screenwidth', window.path))
end
def winfo_screenwidth
TkWinfo.screenwidth self
end
+
def TkWinfo.server(window)
tk_call 'winfo', 'server', window.path
end
def winfo_server
TkWinfo.server self
end
+
def TkWinfo.toplevel(window)
window(tk_call('winfo', 'toplevel', window.path))
end
def winfo_toplevel
TkWinfo.toplevel self
end
+
def TkWinfo.visual(window)
- tk_call 'winfo', 'visual', window.path
+ tk_call('winfo', 'visual', window.path)
end
def winfo_visual
TkWinfo.visual self
end
+
def TkWinfo.visualid(window)
- tk_call 'winfo', 'visualid', window.path
+ tk_call('winfo', 'visualid', window.path)
end
def winfo_visualid
TkWinfo.visualid self
end
+
def TkWinfo.visualsavailable(window, includeids=false)
if includeids
- v = tk_call('winfo', 'visualsavailable', window.path, "includeids")
+ list(tk_call('winfo', 'visualsavailable', window.path, "includeids"))
else
- v = tk_call('winfo', 'visualsavailable', window.path)
+ list(tk_call('winfo', 'visualsavailable', window.path))
end
- list(v)
end
def winfo_visualsavailable(includeids=false)
TkWinfo.visualsavailable self, includeids
end
+
def TkWinfo.vrootheight(window)
number(tk_call('winfo', 'vrootheight', window.path))
end
def winfo_vrootheight
TkWinfo.vrootheight self
end
+
def TkWinfo.vrootwidth(window)
number(tk_call('winfo', 'vrootwidth', window.path))
end
def winfo_vrootwidth
TkWinfo.vrootwidth self
end
+
def TkWinfo.vrootx(window)
number(tk_call('winfo', 'vrootx', window.path))
end
def winfo_vrootx
TkWinfo.vrootx self
end
+
def TkWinfo.vrooty(window)
number(tk_call('winfo', 'vrooty', window.path))
end
def winfo_vrooty
TkWinfo.vrooty self
end
+
def TkWinfo.width(window)
number(tk_call('winfo', 'width', window.path))
end
def winfo_width
TkWinfo.width self
end
+
def TkWinfo.x(window)
number(tk_call('winfo', 'x', window.path))
end
def winfo_x
TkWinfo.x self
end
+
def TkWinfo.y(window)
number(tk_call('winfo', 'y', window.path))
end
def winfo_y
TkWinfo.y self
end
+
def TkWinfo.viewable(window)
bool(tk_call('winfo', 'viewable', window.path))
end
def winfo_viewable
TkWinfo.viewable self
end
+
def TkWinfo.pointerx(window)
number(tk_call('winfo', 'pointerx', window.path))
end
def winfo_pointerx
TkWinfo.pointerx self
end
+
def TkWinfo.pointery(window)
number(tk_call('winfo', 'pointery', window.path))
end
def winfo_pointery
TkWinfo.pointery self
end
+
def TkWinfo.pointerxy(window)
list(tk_call('winfo', 'pointerxy', window.path))
end
@@ -1927,6 +2655,9 @@ end
module TkPack
include Tk
extend Tk
+
+ TkCommandNames = ['pack'.freeze].freeze
+
def configure(win, *args)
if args[-1].kind_of?(Hash)
keys = args.pop
@@ -1970,6 +2701,8 @@ module TkGrid
include Tk
extend Tk
+ TkCommandNames = ['grid'.freeze].freeze
+
def bbox(*args)
list(tk_call('grid', 'bbox', *args))
end
@@ -1995,7 +2728,7 @@ module TkGrid
def columnconfiginfo(master, index, slot=nil)
if slot
- tk_call 'grid', 'columnconfigure', master, index, "-#{slot}"
+ tk_call('grid', 'columnconfigure', master, index, "-#{slot}").to_i
else
ilist = list(tk_call('grid', 'columnconfigure', master, index))
info = {}
@@ -2008,7 +2741,7 @@ module TkGrid
def rowconfiginfo(master, index, slot=nil)
if slot
- tk_call 'grid', 'rowconfigure', master, index, "-#{slot}"
+ tk_call('grid', 'rowconfigure', master, index, "-#{slot}").to_i
else
ilist = list(tk_call('grid', 'rowconfigure', master, index))
info = {}
@@ -2065,6 +2798,8 @@ module TkPlace
include Tk
extend Tk
+ TkCommandNames = ['place'.freeze].freeze
+
def configure(win, slot, value=None)
if slot.kind_of? Hash
tk_call 'place', 'configure', win.epath, *hash_kv(slot)
@@ -2110,47 +2845,111 @@ module TkPlace
module_function :configure, :configinfo, :forget, :info, :slaves
end
-module TkOption
+module TkOptionDB
include Tk
extend Tk
- def add pat, value, pri=None
+
+ TkCommandNames = ['option'.freeze].freeze
+
+ module Priority
+ WidgetDefault = 20
+ StartupFile = 40
+ UserDefault = 60
+ Interactive = 80
+ end
+
+ def add(pat, value, pri=None)
tk_call 'option', 'add', pat, value, pri
end
def clear
tk_call 'option', 'clear'
end
- def get win, name, klass
+ def get(win, name, klass)
tk_call('option', 'get', win ,name, klass).taint
end
- def readfile file, pri=None
+ def readfile(file, pri=None)
tk_call 'option', 'readfile', file, pri
end
module_function :add, :clear, :get, :readfile
+
+ def read_with_encoding(file, f_enc=nil, pri=None)
+ i_enc = Tk.encoding()
+
+ unless f_enc
+ f_enc = i_enc
+ end
+
+ cline = ''
+ open(file, 'r') {|f|
+ while line = f.gets
+ cline += line.chomp!
+ case cline
+ when /\\$/ # continue
+ cline.chop!
+ next
+ when /^!/ # coment
+ cline = ''
+ next
+ when /^([^:]+):\s(.*)$/
+ pat = $1
+ val = $2
+ p "ResourceDB: #{[pat, val].inspect}" if $DEBUG
+ pat = TkCore::INTERP._toUTF8(pat, f_enc)
+ pat = TkCore::INTERP._fromUTF8(pat, i_enc)
+ val = TkCore::INTERP._toUTF8(val, f_enc)
+ val = TkCore::INTERP._fromUTF8(val, i_enc)
+ add(pat, val, pri)
+ cline = ''
+ else # unknown --> ignore
+ cline = ''
+ next
+ end
+ end
+ }
+ end
+ module_function :read_with_encoding
# support procs on the resource database
@@resource_proc_class = Class.new
class << @@resource_proc_class
private :new
-
+
CARRIER = '.'.freeze
- METHOD_TBL = {}
+ METHOD_TBL = TkCore::INTERP.create_table
ADD_METHOD = false
SAFE_MODE = 4
+ def __closed_block_check__(str)
+ depth = 0
+ str.scan(/[{}]/){|x|
+ if x == "{"
+ depth += 1
+ elsif x == "}"
+ depth -= 1
+ end
+ if depth <= 0 && !($' =~ /\A\s*\Z/)
+ fail RuntimeError, "bad string for procedure : #{str.inspect}"
+ end
+ }
+ str
+ end
+
def __check_proc_string__(str)
# If you want to check the proc_string, do it in this method.
+ # Please define this in the block given to 'new_proc_class' method.
str
end
def method_missing(id, *args)
res_proc = self::METHOD_TBL[id]
unless res_proc.kind_of? Proc
- if id == :new || (!self::METHOD_TBL.has_key?(id) && !self::ADD_METHOD)
+ if id == :new || !(self::METHOD_TBL.has_key?(id) || self::ADD_METHOD)
raise NoMethodError,
"not support resource-proc '#{id.id2name}' for #{self.name}"
end
- proc_str = TkOption.get(self::CARRIER, id.id2name, '')
+ proc_str = TkOptionDB.get(self::CARRIER, id.id2name, '').strip
proc_str = '{' + proc_str + '}' unless /\A\{.*\}\Z/ =~ proc_str
+ proc_str = __closed_block_check__(proc_str)
proc_str = __check_proc_string__(proc_str)
res_proc = eval 'Proc.new' + proc_str
self::METHOD_TBL[id] = res_proc
@@ -2161,10 +2960,11 @@ module TkOption
}.call
end
- private :__check_proc_string__, :method_missing
+ private :__closed_block_check__, :__check_proc_string__, :method_missing
end
+ @@resource_proc_class.freeze
- def new_proc_class(klass, func, safe = 4, add = false, parent = nil)
+ def __create_new_class(klass, func, safe = 4, add = false, parent = nil)
klass = klass.to_s if klass.kind_of? Symbol
unless (?A..?Z) === klass[0]
fail ArgumentError, "bad string '#{klass}' for class name"
@@ -2183,25 +2983,91 @@ module TkOption
carrier = Tk.tk_call('frame', @path, '-class', klass)
body = <<-"EOD"
- class #{klass} < TkOption.module_eval('@@resource_proc_class')
+ class #{klass} < TkOptionDB.module_eval('@@resource_proc_class')
CARRIER = '#{carrier}'.freeze
- METHOD_TBL = {}
+ METHOD_TBL = TkCore::INTERP.create_table
ADD_METHOD = #{add}
SAFE_MODE = #{safe}
- %w(#{func_str}).each{|f| METHOD_TBL.delete(f.intern) }
+ %w(#{func_str}).each{|f| METHOD_TBL[f.intern] = nil }
end
EOD
if parent.kind_of?(Class) && parent <= @@resource_proc_class
- parent.class_eval body
- eval parent.name + '::' + klass
+ parent.class_eval(body)
+ eval(parent.name + '::' + klass)
else
- eval body
- eval 'TkOption::' + klass
- end
+ eval(body)
+ eval('TkOptionDB::' + klass)
+ end
+ end
+ module_function :__create_new_class
+ private_class_method :__create_new_class
+
+ def __remove_methods_of_proc_class(klass)
+ # for security, make these methods invalid
+ class << klass
+ attr_reader :class_eval, :name, :superclass,
+ :ancestors, :const_defined?, :const_get, :const_set,
+ :constants, :included_modules, :instance_methods,
+ :method_defined?, :module_eval, :private_instance_methods,
+ :protected_instance_methods, :public_instance_methods,
+ :remove_const, :remove_method, :undef_method,
+ :to_s, :inspect, :display, :method, :methods,
+ :instance_eval, :instance_variables, :kind_of?, :is_a?,
+ :private_methods, :protected_methods, :public_methods
+ end
+ end
+ module_function :__remove_methods_of_proc_class
+ private_class_method :__remove_methods_of_proc_class
+
+ RAND_BASE_CNT = [0]
+ RAND_BASE_HEAD = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ RAND_BASE_CHAR = RAND_BASE_HEAD + 'abcdefghijklmnopqrstuvwxyz0123456789_'
+ def __get_random_basename
+ name = '%s%03d' % [RAND_BASE_HEAD[rand(RAND_BASE_HEAD.size),1],
+ RAND_BASE_CNT[0]]
+ len = RAND_BASE_CHAR.size
+ (6+rand(10)).times{
+ name << RAND_BASE_CHAR[rand(len),1]
+ }
+ RAND_BASE_CNT[0] = RAND_BASE_CNT[0] + 1
+ name
+ end
+ module_function :__get_random_basename
+ private_class_method :__get_random_basename
+
+ # define new proc class :
+ # If you want to modify the new class or create a new subclass,
+ # you must do such operation in the block parameter.
+ # Because the created class is flozen after evaluating the block.
+ def new_proc_class(klass, func, safe = 4, add = false, parent = nil, &b)
+ new_klass = __create_new_class(klass, func, safe, add, parent)
+ new_klass.class_eval(&b) if block_given?
+ __remove_methods_of_proc_class(new_klass)
+ new_klass.freeze
+ new_klass
end
module_function :new_proc_class
+
+ def eval_under_random_base(parent = nil, &b)
+ new_klass = __create_new_class(__get_random_basename(),
+ [], 4, false, parent)
+ ret = new_klass.class_eval(&b) if block_given?
+ __remove_methods_of_proc_class(new_klass)
+ new_klass.freeze
+ ret
+ end
+ module_function :eval_under_random_base
+
+ def new_proc_class_random(klass, func, safe = 4, add = false, &b)
+ eval_under_random_base(){
+ TkOption.new_proc_class(klass, func, safe, add, self, &b)
+ }
+ end
+ module_function :new_proc_class_random
end
+TkOption = TkOptionDB
+TkResourceDB = TkOptionDB
module TkTreatFont
def font_configinfo
@@ -2249,6 +3115,7 @@ module TkTreatFont
else
fobj.latin_replace(ltn)
end
+ self
end
alias asciifont_configure latinfont_configure
@@ -2265,6 +3132,7 @@ module TkTreatFont
else
fobj.kanji_replace(knj)
end
+ self
end
def font_copy(window, tag=nil)
@@ -2280,6 +3148,7 @@ module TkTreatFont
}
fontobj.replace(window.fontobj.latin_font, window.fontobj.kanji_font)
end
+ self
end
def latinfont_copy(window, tag=nil)
@@ -2288,6 +3157,7 @@ module TkTreatFont
else
fontobj.latin_replace(window.fontobj.latin_font)
end
+ self
end
alias asciifont_copy latinfont_copy
@@ -2297,15 +3167,16 @@ module TkTreatFont
else
fontobj.kanji_replace(window.fontobj.kanji_font)
end
+ self
end
end
module TkTreatItemFont
def __conf_cmd(idx)
- raise NotImplementError, "need to define `__conf_cmd'"
+ raise NotImplementedError, "need to define `__conf_cmd'"
end
def __item_pathname(tagOrId)
- raise NotImplementError, "need to define `__item_pathname'"
+ raise NotImplementedError, "need to define `__item_pathname'"
end
private :__conf_cmd, :__item_pathname
@@ -2363,6 +3234,7 @@ module TkTreatItemFont
else
fobj.latin_replace(ltn)
end
+ self
end
alias asciitagfont_configure latintagfont_configure
@@ -2382,6 +3254,7 @@ module TkTreatItemFont
else
fobj.kanji_replace(knj)
end
+ self
end
def tagfont_copy(tagOrId, window, wintag=nil)
@@ -2398,6 +3271,7 @@ module TkTreatItemFont
tagfontobj(tagOrId).replace(window.fontobj.latin_font,
window.fontobj.kanji_font)
end
+ self
end
def latintagfont_copy(tagOrId, window, wintag=nil)
@@ -2406,6 +3280,7 @@ module TkTreatItemFont
else
tagfontobj(tagOrId).latin_replace(window.fontobj.latin_font)
end
+ self
end
alias asciitagfont_copy latintagfont_copy
@@ -2415,6 +3290,7 @@ module TkTreatItemFont
else
tagfontobj(tagOrId).kanji_replace(window.fontobj.kanji_font)
end
+ self
end
end
@@ -2449,7 +3325,9 @@ class TkObject<TkKernel
begin
cget name
rescue
- fail NameError, "undefined local variable or method `#{name}' for #{self.to_s}", error_at
+ fail NameError,
+ "undefined local variable or method `#{name}' for #{self.to_s}",
+ error_at
end
else
fail NameError, "undefined method `#{name}' for #{self.to_s}", error_at
@@ -2480,7 +3358,7 @@ class TkObject<TkKernel
slot['latinfont'] || slot[:latinfont] ||
slot['asciifont'] || slot[:asciifont] )
font_configure(slot)
- else
+ elsif slot.size > 0
tk_call path, 'configure', *hash_kv(slot)
end
@@ -2498,6 +3376,7 @@ class TkObject<TkKernel
tk_call path, 'configure', "-#{slot}", value
end
end
+ self
end
def configure_cmd(slot, value)
@@ -2565,20 +3444,27 @@ class TkObject<TkKernel
def tk_trace_variable(v)
unless v.kind_of?(TkVariable)
- fail ArgumentError, format("requires TkVariable given %s", v.type)
+ fail ArgumentError,
+ format("type error (%s); must be TkVariable object", v.class)
end
v
end
private :tk_trace_variable
def destroy
- tk_call 'trace', 'vdelete', @tk_vn, 'w', @var_id if @var_id
+ # tk_call 'trace', 'vdelete', @tk_vn, 'w', @var_id if @var_id
end
end
class TkWindow<TkObject
+ include TkWinfo
extend TkBindCore
+ WidgetClassName = ''.freeze
+ def self.to_eval
+ self::WidgetClassName
+ end
+
def initialize(parent=nil, keys=nil)
if parent.kind_of? Hash
keys = _symbolkey2str(parent)
@@ -2586,29 +3472,43 @@ class TkWindow<TkObject
parent = keys.delete('parent')
widgetname = keys.delete('widgetname')
install_win(if parent then parent.path end, widgetname)
+ without_creating = keys.delete('without_creating')
+ if without_creating && !widgetname
+ fail ArgumentError,
+ "if set 'without_creating' to true, need to define 'widgetname'"
+ end
elsif keys
keys = _symbolkey2str(keys)
widgetname = keys.delete('widgetname')
install_win(if parent then parent.path end, widgetname)
+ without_creating = keys.delete('without_creating')
+ if without_creating && !widgetname
+ fail ArgumentError,
+ "if set 'without_creating' to true, need to define 'widgetname'"
+ end
else
install_win(if parent then parent.path end)
end
if self.method(:create_self).arity == 0
p 'create_self has no arg' if $DEBUG
- create_self
+ create_self unless without_creating
if keys
- # tk_call @path, 'configure', *hash_kv(keys)
- configure(keys)
+ # tk_call @path, 'configure', *hash_kv(keys)
+ configure(keys)
end
else
p 'create_self has args' if $DEBUG
fontkeys = {}
if keys
- ['font', 'kanjifont', 'latinfont', 'asciifont'].each{|key|
- fontkeys[key] = keys.delete(key) if keys.key?(key)
- }
+ ['font', 'kanjifont', 'latinfont', 'asciifont'].each{|key|
+ fontkeys[key] = keys.delete(key) if keys.key?(key)
+ }
+ end
+ if without_creating && keys
+ configure(keys)
+ else
+ create_self(keys)
end
- create_self(keys)
font_configure(fontkeys) unless fontkeys.empty?
end
end
@@ -2617,11 +3517,44 @@ class TkWindow<TkObject
end
private :create_self
+ def bind_class
+ @db_class || self.class()
+ end
+
+ def database_classname
+ TkWinfo.classname(self)
+ end
+ def database_class
+ name = database_classname()
+ if WidgetClassNames[name]
+ WidgetClassNames[name]
+ else
+ TkDatabaseClass.new(name)
+ end
+ end
+ def self.database_classname
+ self::WidgetClassName
+ end
+ def self.database_class
+ WidgetClassNames[self::WidgetClassName]
+ end
+
def pack(keys = nil)
tk_call 'pack', epath, *hash_kv(keys)
self
end
+ def pack_in(target, keys = nil)
+ if keys
+ keys = keys.dup
+ keys['in'] = target
+ else
+ keys = {'in'=>target}
+ end
+ tk_call 'pack', epath, *hash_kv(keys)
+ self
+ end
+
def unpack
tk_call 'pack', 'forget', epath
self
@@ -2662,6 +3595,17 @@ class TkWindow<TkObject
self
end
+ def grid_in(target, keys = nil)
+ if keys
+ keys = keys.dup
+ keys['in'] = target
+ else
+ keys = {'in'=>target}
+ end
+ tk_call 'grid', epath, *hash_kv(keys)
+ self
+ end
+
def ungrid
tk_call 'grid', 'forget', epath
self
@@ -2690,7 +3634,7 @@ class TkWindow<TkObject
def grid_columnconfiginfo(index, slot=nil)
if slot
- tk_call('grid', 'columnconfigure', epath, index, "-#{slot}")
+ tk_call('grid', 'columnconfigure', epath, index, "-#{slot}").to_i
else
ilist = list(tk_call('grid', 'columnconfigure', epath, index))
info = {}
@@ -2703,7 +3647,7 @@ class TkWindow<TkObject
def grid_rowconfiginfo(index, slot=nil)
if slot
- tk_call('grid', 'rowconfigure', epath, index, "-#{slot}")
+ tk_call('grid', 'rowconfigure', epath, index, "-#{slot}").to_i
else
ilist = list(tk_call('grid', 'rowconfigure', epath, index))
info = {}
@@ -2747,6 +3691,17 @@ class TkWindow<TkObject
self
end
+ def place_in(target, keys = nil)
+ if keys
+ keys = keys.dup
+ keys['in'] = target
+ else
+ keys = {'in'=>target}
+ end
+ tk_call 'place', epath, *hash_kv(keys)
+ self
+ end
+
def unplace
tk_call 'place', 'forget', epath
self
@@ -2802,12 +3757,17 @@ class TkWindow<TkObject
def grab(*args)
if !args or args.length == 0
tk_call 'grab', 'set', path
+ self
elsif args.length == 1
case args[0]
when 'global', :global
- return(tk_call('grab', 'set', '-global', path))
+ #return(tk_call('grab', 'set', '-global', path))
+ tk_call('grab', 'set', '-global', path)
+ return self
when 'release', :release
- return tk_call('grab', 'release', path)
+ #return tk_call('grab', 'release', path)
+ tk_call('grab', 'release', path)
+ return self
else
val = tk_call('grab', args[0], path)
end
@@ -2817,11 +3777,25 @@ class TkWindow<TkObject
when 'status', :status
return val
end
+ self
else
fail ArgumentError, 'wrong # of args'
end
end
+ def grab_current
+ grab('current')
+ end
+ def grab_set
+ grab('set')
+ end
+ def grab_set_global
+ grab('global')
+ end
+ def grab_status
+ grab('status')
+ end
+
def lower(below=None)
tk_call 'lower', epath, below
self
@@ -2842,12 +3816,27 @@ class TkWindow<TkObject
def destroy
super
- tk_call 'destroy', epath
- if @cmdtbl
+ children = []
+ rexp = /^#{self.path}\.[^.]+$/
+ TkCore::INTERP.tk_windows.each{|path, obj|
+ children << [path, obj] if path =~ rexp
+ }
+ if defined?(@cmdtbl)
for id in @cmdtbl
uninstall_cmd id
end
end
+
+ children.each{|path, obj|
+ if defined?(@cmdtbl)
+ for id in @cmdtbl
+ uninstall_cmd id
+ end
+ end
+ TkCore::INTERP.tk_windows.delete(path)
+ }
+
+ tk_call 'destroy', epath
uninstall_win
end
@@ -2862,8 +3851,9 @@ class TkWindow<TkObject
def bindtags(taglist=nil)
if taglist
- fail ArgumentError unless taglist.kind_of? Array
+ fail ArgumentError, "taglist must be Array" unless taglist.kind_of? Array
tk_call('bindtags', path, taglist)
+ taglist
else
list(tk_call('bindtags', path)).collect{|tag|
if tag.kind_of?(String)
@@ -2880,23 +3870,68 @@ class TkWindow<TkObject
}
end
end
+
+ def bindtags=(taglist)
+ bindtags(taglist)
+ end
+
+ def bindtags_shift
+ taglist = bindtags
+ tag = taglist.shift
+ bindtags(taglist)
+ tag
+ end
+
+ def bindtags_unshift(tag)
+ bindtags(bindtags().unshift(tag))
+ end
end
class TkRoot<TkWindow
include Wm
+
+=begin
ROOT = []
- def TkRoot.new
- return ROOT[0] if ROOT[0]
- new = super
+ def TkRoot.new(keys=nil)
+ if ROOT[0]
+ Tk_WINDOWS["."] = ROOT[0]
+ return ROOT[0]
+ end
+ new = super(:without_creating=>true, :widgetname=>'.')
+ if keys # wm commands
+ keys.each{|k,v|
+ if v.kind_of? Array
+ new.send(k,*v)
+ else
+ new.send(k,v)
+ end
+ }
+ end
ROOT[0] = new
Tk_WINDOWS["."] = new
end
+=end
+ def TkRoot.new(keys=nil, &b)
+ unless TkCore::INTERP.tk_windows['.']
+ TkCore::INTERP.tk_windows['.'] =
+ super(:without_creating=>true, :widgetname=>'.')
+ end
+ root = TkCore::INTERP.tk_windows['.']
+ if keys # wm commands
+ keys.each{|k,v|
+ if v.kind_of? Array
+ root.send(k,*v)
+ else
+ root.send(k,v)
+ end
+ }
+ end
+ root.instance_eval(&b) if block_given?
+ root
+ end
WidgetClassName = 'Tk'.freeze
WidgetClassNames[WidgetClassName] = self
- def self.to_eval
- WidgetClassName
- end
def create_self
@path = '.'
@@ -2909,11 +3944,9 @@ end
class TkToplevel<TkWindow
include Wm
+ TkCommandNames = ['toplevel'.freeze].freeze
WidgetClassName = 'Toplevel'.freeze
WidgetClassNames[WidgetClassName] = self
- def self.to_eval
- WidgetClassName
- end
################# old version
# def initialize(parent=nil, screen=nil, classname=nil, keys=nil)
@@ -2947,38 +3980,117 @@ class TkToplevel<TkWindow
# end
#################
+ def _wm_command_option_chk(keys)
+ keys = {} unless keys
+ new_keys = {}
+ wm_cmds = {}
+ keys.each{|k,v|
+ if Wm.method_defined?(k)
+ case k
+ when 'screen','class','colormap','container','use','visual'
+ new_keys[k] = v
+ else
+ case self.method(k).arity
+ when -1,1
+ wm_cmds[k] = v
+ else
+ new_keys[k] = v
+ end
+ end
+ else
+ new_keys[k] = v
+ end
+ }
+ [new_keys, wm_cmds]
+ end
+ private :_wm_command_option_chk
+
def initialize(parent=nil, screen=nil, classname=nil, keys=nil)
+ my_class_name = nil
+ if self.class < WidgetClassNames[WidgetClassName]
+ my_class_name = self.class.name
+ my_class_name = nil if my_class_name == ''
+ end
if parent.kind_of? Hash
keys = _symbolkey2str(parent)
- @screen = keys['screen']
+ if keys.key?('classname')
+ keys['class'] = keys.delete('classname')
+ end
@classname = keys['class']
@colormap = keys['colormap']
@container = keys['container']
@screen = keys['screen']
@use = keys['use']
@visual = keys['visual']
+ if !@classname && my_class_name
+ keys['class'] = @classname = my_class_name
+ end
+ if @classname.kind_of? TkBindTag
+ @db_class = @classname
+ @classname = @classname.id
+ elsif @classname
+ @db_class = TkDatabaseClass.new(@classname)
+ else
+ @db_class = self.class
+ @classname = @db_class::WidgetClassName
+ end
+ keys, cmds = _wm_command_option_chk(keys)
super(keys)
+ cmds.each{|k,v|
+ if v.kind_of? Array
+ self.send(k,*v)
+ else
+ self.send(k,v)
+ end
+ }
return
end
+
if screen.kind_of? Hash
- keys = _symbolkey2str(screen)
+ keys = screen
else
@screen = screen
+ if classname.kind_of? Hash
+ keys = classname
+ else
+ @classname = classname
+ end
end
- @classname = classname
if keys.kind_of? Hash
keys = _symbolkey2str(keys)
- if keys.key?(:classname) || keys.key?('classname')
+ if keys.key?('classname')
keys['class'] = keys.delete('classname')
end
- @classname = keys['class']
+ @classname = keys['class'] unless @classname
@colormap = keys['colormap']
@container = keys['container']
- @screen = keys['screen']
+ @screen = keys['screen'] unless @screen
@use = keys['use']
@visual = keys['visual']
+ else
+ keys = {}
+ end
+ if !@classname && my_class_name
+ keys['class'] = @classname = my_class_name
end
+ if @classname.kind_of? TkBindTag
+ @db_class = @classname
+ @classname = @classname.id
+ elsif @classname
+ @db_class = TkDatabaseClass.new(@classname)
+ else
+ @db_class = self.class
+ @classname = @db_class::WidgetClassName
+ end
+ keys, cmds = _wm_command_option_chk(keys)
super(parent, keys)
+ cmds.each{|k,v|
+ if v.kind_of? Array
+ self.send(k,*v)
+ else
+ self.send(k,v)
+ end
+ }
end
def create_self(keys)
@@ -2992,14 +4104,52 @@ class TkToplevel<TkWindow
def specific_class
@classname
end
+
+ def self.database_class
+ if self == WidgetClassNames[WidgetClassName] || self.name == ''
+ self
+ else
+ TkDatabaseClass.new(self.name)
+ end
+ end
+ def self.database_classname
+ self.database_class.name
+ end
+
+ def self.bind(*args)
+ if self == WidgetClassNames[WidgetClassName] || self.name == ''
+ super(*args)
+ else
+ TkDatabaseClass.new(self.name).bind(*args)
+ end
+ end
+ def self.bind_append(*args)
+ if self == WidgetClassNames[WidgetClassName] || self.name == ''
+ super(*args)
+ else
+ TkDatabaseClass.new(self.name).bind_append(*args)
+ end
+ end
+ def self.bind_remove(*args)
+ if self == WidgetClassNames[WidgetClassName] || self.name == ''
+ super(*args)
+ else
+ TkDatabaseClass.new(self.name).bind_remove(*args)
+ end
+ end
+ def self.bindinfo(*args)
+ if self == WidgetClassNames[WidgetClassName] || self.name == ''
+ super(*args)
+ else
+ TkDatabaseClass.new(self.name).bindinfo(*args)
+ end
+ end
end
class TkFrame<TkWindow
+ TkCommandNames = ['frame'.freeze].freeze
WidgetClassName = 'Frame'.freeze
WidgetClassNames[WidgetClassName] = self
- def self.to_eval
- WidgetClassName
- end
################# old version
# def initialize(parent=nil, keys=nil)
@@ -3024,6 +4174,11 @@ class TkFrame<TkWindow
#################
def initialize(parent=nil, keys=nil)
+ my_class_name = nil
+ if self.class < WidgetClassNames[WidgetClassName]
+ my_class_name = self.class.name
+ my_class_name = nil if my_class_name == ''
+ end
if parent.kind_of? Hash
keys = _symbolkey2str(parent)
else
@@ -3041,6 +4196,18 @@ class TkFrame<TkWindow
@colormap = keys['colormap']
@container = keys['container']
@visual = keys['visual']
+ if !@classname && my_class_name
+ keys['class'] = @classname = my_class_name
+ end
+ if @classname.kind_of? TkBindTag
+ @db_class = @classname
+ @classname = @classname.id
+ elsif @classname
+ @db_class = TkDatabaseClass.new(@classname)
+ else
+ @db_class = self.class
+ @classname = @db_class::WidgetClassName
+ end
super(keys)
end
@@ -3051,14 +4218,178 @@ class TkFrame<TkWindow
tk_call 'frame', @path
end
end
+
+ def database_classname
+ @classname
+ end
+
+ def self.database_class
+ if self == WidgetClassNames[WidgetClassName] || self.name == ''
+ self
+ else
+ TkDatabaseClass.new(self.name)
+ end
+ end
+ def self.database_classname
+ self.database_class.name
+ end
+
+ def self.bind(*args)
+ if self == WidgetClassNames[WidgetClassName] || self.name == ''
+ super(*args)
+ else
+ TkDatabaseClass.new(self.name).bind(*args)
+ end
+ end
+ def self.bind_append(*args)
+ if self == WidgetClassNames[WidgetClassName] || self.name == ''
+ super(*args)
+ else
+ TkDatabaseClass.new(self.name).bind_append(*args)
+ end
+ end
+ def self.bind_remove(*args)
+ if self == WidgetClassNames[WidgetClassName] || self.name == ''
+ super(*args)
+ else
+ TkDatabaseClass.new(self.name).bind_remove(*args)
+ end
+ end
+ def self.bindinfo(*args)
+ if self == WidgetClassNames[WidgetClassName] || self.name == ''
+ super(*args)
+ else
+ TkDatabaseClass.new(self.name).bindinfo(*args)
+ end
+ end
end
+class TkLabelFrame<TkFrame
+ def create_self(keys)
+ if keys and keys != None
+ tk_call 'labelframe', @path, *hash_kv(keys)
+ else
+ tk_call 'labelframe', @path
+ end
+ end
+end
+
+class TkPanedWindow<TkWindow
+ TkCommandNames = ['panedwindow'.freeze].freeze
+ WidgetClassName = 'Panedwindow'.freeze
+ WidgetClassNames[WidgetClassName] = self
+ def create_self(keys)
+ if keys and keys != None
+ tk_call 'panedwindow', @path, *hash_kv(keys)
+ else
+ tk_call 'panedwindow', @path
+ end
+ end
+
+ def add(*args)
+ keys = args.pop
+ fail ArgumentError, "no window in arguments" unless keys
+ if keys && keys.kind_of?(Hash)
+ fail ArgumentError, "no window in arguments" if args == []
+ args += hash_kv(keys)
+ else
+ args.push(keys) if keys
+ end
+ tk_send('add', *args)
+ self
+ end
+
+ def forget(win, *wins)
+ tk_send('forget', win, *wins)
+ self
+ end
+ alias del forget
+ alias delete forget
+ alias remove forget
+
+ def identify(x, y)
+ list(tk_send('identify', x, y))
+ end
+
+ def proxy_coord
+ list(tk_send('proxy', 'coord'))
+ end
+ def proxy_forget
+ tk_send('proxy', 'forget')
+ self
+ end
+ def proxy_place(x, y)
+ tk_send('proxy', 'place', x, y)
+ self
+ end
+
+ def sash_coord(index)
+ list(tk_send('sash', 'coord', index))
+ end
+ def sash_dragto(index)
+ tk_send('sash', 'dragto', index, x, y)
+ self
+ end
+ def sash_mark(index, x, y)
+ tk_send('sash', 'mark', index, x, y)
+ self
+ end
+ def sash_place(index, x, y)
+ tk_send('sash', 'place', index, x, y)
+ self
+ end
+
+ def panecget(win, key)
+ tk_tcl2ruby(tk_send('panecget', win, "-#{key}"))
+ end
+
+ def paneconfigure(win, key, value=nil)
+ if key.kind_of? Hash
+ tk_send('paneconfigure', win, *hash_kv(key))
+ else
+ tk_send('paneconfigure', win, "-#{key}", value)
+ end
+ self
+ end
+
+ def paneconfiginfo(win, key=nil)
+ if key
+ conf = tk_split_list(tk_send('paneconfigure', win, "-#{key}"))
+ conf[0] = conf[0][1..-1]
+ conf
+ else
+ tk_split_simplelist(tk_send('paneconfigure', win)).collect{|conflist|
+ conf = tk_split_simplelist(conflist)
+ conf[0] = conf[0][1..-1]
+ if conf[3]
+ if conf[3].index('{')
+ conf[3] = tk_split_list(conf[3])
+ else
+ conf[3] = tk_tcl2ruby(conf[3])
+ end
+ end
+ if conf[4]
+ if conf[4].index('{')
+ conf[4] = tk_split_list(conf[4])
+ else
+ conf[4] = tk_tcl2ruby(conf[4])
+ end
+ end
+ conf
+ }
+ end
+ end
+
+ def panes
+ list(tk_send('panes'))
+ end
+end
+TkPanedwindow = TkPanedWindow
+
class TkLabel<TkWindow
+ TkCommandNames = ['label'.freeze].freeze
WidgetClassName = 'Label'.freeze
WidgetClassNames[WidgetClassName] = self
- def self.to_eval
- WidgetClassName
- end
def create_self(keys)
if keys and keys != None
tk_call 'label', @path, *hash_kv(keys)
@@ -3072,10 +4403,9 @@ class TkLabel<TkWindow
end
class TkButton<TkLabel
- WidgetClassNames['Button'] = self
- def TkButton.to_eval
- 'Button'
- end
+ TkCommandNames = ['button'.freeze].freeze
+ WidgetClassName = 'Button'.freeze
+ WidgetClassNames[WidgetClassName] = self
def create_self(keys)
if keys and keys != None
tk_call 'button', @path, *hash_kv(keys)
@@ -3088,14 +4418,14 @@ class TkButton<TkLabel
end
def flash
tk_send 'flash'
+ self
end
end
class TkRadioButton<TkButton
- WidgetClassNames['Radiobutton'] = self
- def TkRadioButton.to_eval
- 'Radiobutton'
- end
+ TkCommandNames = ['radiobutton'.freeze].freeze
+ WidgetClassName = 'Radiobutton'.freeze
+ WidgetClassNames[WidgetClassName] = self
def create_self(keys)
if keys and keys != None
tk_call 'radiobutton', @path, *hash_kv(keys)
@@ -3105,9 +4435,11 @@ class TkRadioButton<TkButton
end
def deselect
tk_send 'deselect'
+ self
end
def select
tk_send 'select'
+ self
end
def variable(v)
configure 'variable', tk_trace_variable(v)
@@ -3116,10 +4448,9 @@ end
TkRadiobutton = TkRadioButton
class TkCheckButton<TkRadioButton
- WidgetClassNames['Checkbutton'] = self
- def TkCheckButton.to_eval
- 'Checkbutton'
- end
+ TkCommandNames = ['checkbutton'.freeze].freeze
+ WidgetClassName = 'Checkbutton'.freeze
+ WidgetClassNames[WidgetClassName] = self
def create_self(keys)
if keys and keys != None
tk_call 'checkbutton', @path, *hash_kv(keys)
@@ -3129,15 +4460,15 @@ class TkCheckButton<TkRadioButton
end
def toggle
tk_send 'toggle'
+ self
end
end
TkCheckbutton = TkCheckButton
class TkMessage<TkLabel
- WidgetClassNames['Message'] = self
- def TkMessage.to_eval
- 'Message'
- end
+ TkCommandNames = ['message'.freeze].freeze
+ WidgetClassName = 'Message'.freeze
+ WidgetClassNames[WidgetClassName] = self
def create_self(keys)
if keys and keys != None
tk_call 'message', @path, *hash_kv(keys)
@@ -3148,20 +4479,52 @@ class TkMessage<TkLabel
end
class TkScale<TkWindow
+ TkCommandNames = ['scale'.freeze].freeze
WidgetClassName = 'Scale'.freeze
WidgetClassNames[WidgetClassName] = self
- def self.to_eval
- WidgetClassName
- end
def create_self(keys)
if keys and keys != None
+ if keys.key?('command')
+ cmd = keys.delete('command')
+ keys['command'] = proc{|val| cmd.call(val.to_f)}
+ end
tk_call 'scale', @path, *hash_kv(keys)
else
tk_call 'scale', @path
end
end
+ def _wrap_command_arg(cmd)
+ proc{|val|
+ if val.kind_of?(String)
+ cmd.call(number(val))
+ else
+ cmd.call(val)
+ end
+ }
+ end
+ private :_wrap_command_arg
+
+ def configure_cmd(slot, value)
+ configure(slot=>value)
+ end
+
+ def configure(slot, value=None)
+ if (slot == 'command' || slot == :command)
+ configure('command'=>value)
+ elsif slot.kind_of?(Hash) &&
+ (slot.key?('command') || slot.key?(:command))
+ slot = _symbolkey2str(slot)
+ slot['command'] = __wrap_command_arg(slot.delete('command'))
+ end
+ super(slot, value)
+ end
+
+ def command(cmd=Proc.new)
+ configure('command'=>cmd)
+ end
+
def get(x=None, y=None)
number(tk_send('get', x, y))
end
@@ -3188,11 +4551,9 @@ class TkScale<TkWindow
end
class TkScrollbar<TkWindow
+ TkCommandNames = ['scrollbar'.freeze].freeze
WidgetClassName = 'Scrollbar'.freeze
WidgetClassNames[WidgetClassName] = self
- def self.to_eval
- WidgetClassName
- end
def create_self(keys)
if keys and keys != None
@@ -3225,6 +4586,7 @@ class TkScrollbar<TkWindow
def set(first, last)
tk_send "set", first, last
+ self
end
def activate(element=None)
@@ -3238,28 +4600,30 @@ class TkTextWin<TkWindow
end
def bbox(index)
- tk_send 'bbox', index
+ list(tk_send('bbox', index))
end
def delete(first, last=None)
tk_send 'delete', first, last
+ self
end
def get(*index)
tk_send 'get', *index
end
- def index(index)
- tk_send 'index', index
- end
- def insert(index, chars, *args)
- tk_send 'insert', index, chars, *args
+ def insert(index, *args)
+ tk_send 'insert', index, *args
+ self
end
def scan_mark(x, y)
tk_send 'scan', 'mark', x, y
+ self
end
def scan_dragto(x, y)
tk_send 'scan', 'dragto', x, y
+ self
end
def see(index)
tk_send 'see', index
+ self
end
end
@@ -3280,10 +4644,10 @@ class TkListbox<TkTextWin
include TkTreatListItemFont
include Scrollable
- WidgetClassNames['Listbox'] = self
- def TkListbox.to_eval
- 'Listbox'
- end
+ TkCommandNames = ['listbox'.freeze].freeze
+ WidgetClassName = 'Listbox'.freeze
+ WidgetClassNames[WidgetClassName] = self
+
def create_self(keys)
if keys and keys != None
tk_call 'listbox', @path, *hash_kv(keys)
@@ -3294,6 +4658,7 @@ class TkListbox<TkTextWin
def activate(y)
tk_send 'activate', y
+ self
end
def curselection
list(tk_send('curselection'))
@@ -3314,23 +4679,30 @@ class TkListbox<TkTextWin
end
def selection_anchor(index)
tk_send 'selection', 'anchor', index
+ self
end
def selection_clear(first, last=None)
tk_send 'selection', 'clear', first, last
+ self
end
def selection_includes(index)
bool(tk_send('selection', 'includes', index))
end
def selection_set(first, last=None)
tk_send 'selection', 'set', first, last
+ self
+ end
+
+ def index(index)
+ tk_send('index', index).to_i
end
def itemcget(index, key)
case key.to_s
when 'text', 'label', 'show'
- tk_send 'itemcget', index, "-#{key}"
+ tk_send('itemcget', index, "-#{key}")
else
- tk_tcl2ruby tk_send('itemcget', index, "-#{key}")
+ tk_tcl2ruby(tk_send('itemcget', index, "-#{key}"))
end
end
def itemconfigure(index, key, val=None)
@@ -3354,6 +4726,7 @@ class TkListbox<TkTextWin
tk_call 'itemconfigure', index, "-#{key}", val
end
end
+ self
end
def itemconfiginfo(index, key=nil)
@@ -3410,11 +4783,10 @@ end
class TkMenu<TkWindow
include TkTreatMenuEntryFont
+ TkCommandNames = ['menu'.freeze].freeze
WidgetClassName = 'Menu'.freeze
WidgetClassNames[WidgetClassName] = self
- def self.to_eval
- WidgetClassName
- end
+
def create_self(keys)
if keys and keys != None
tk_call 'menu', @path, *hash_kv(keys)
@@ -3424,36 +4796,61 @@ class TkMenu<TkWindow
end
def activate(index)
tk_send 'activate', index
+ self
end
def add(type, keys=nil)
tk_send 'add', type, *hash_kv(keys)
+ self
+ end
+ def add_cascade(keys=nil)
+ add('cascade', keys)
+ end
+ def add_checkbutton(keys=nil)
+ add('checkbutton', keys)
+ end
+ def add_command(keys=nil)
+ add('command', keys)
+ end
+ def add_radiobutton(keys=nil)
+ add('radiobutton', keys)
+ end
+ def add_separator(keys=nil)
+ add('separator', keys)
end
def index(index)
- tk_send 'index', index
+ ret = tk_send('index', index)
+ (ret == 'none')? nil: number(ret)
end
def invoke(index)
tk_send 'invoke', index
end
def insert(index, type, keys=nil)
tk_send 'insert', index, type, *hash_kv(keys)
+ self
end
def delete(index, last=None)
tk_send 'delete', index, last
+ self
end
def popup(x, y, index=None)
- tk_call 'tk_popup', path, x, y, index
+ tk_call('tk_popup', path, x, y, index)
+ self
end
def post(x, y)
tk_send 'post', x, y
+ self
end
def postcascade(index)
tk_send 'postcascade', index
+ self
end
def postcommand(cmd=Proc.new)
configure_cmd 'postcommand', cmd
+ self
end
def tearoffcommand(cmd=Proc.new)
configure_cmd 'tearoffcommand', cmd
+ self
end
def menutype(index)
tk_send 'type', index
@@ -3493,6 +4890,7 @@ class TkMenu<TkWindow
tk_call 'entryconfigure', index, "-#{key}", val
end
end
+ self
end
def entryconfiginfo(index, key=nil)
@@ -3557,9 +4955,12 @@ module TkSystemMenu
keys = _symbolkey2str(parent)
parent = keys.delete('parent')
end
- fail unless parent.kind_of? TkMenu
- @path = format("%s.%s", parent.path, self.type::SYSMENU_NAME)
- TkComm::Tk_WINDOWS[@path] = self
+ unless parent.kind_of? TkMenu
+ fail ArgumentError, "parent must be a TkMenu object"
+ end
+ @path = format("%s.%s", parent.path, self.class::SYSMENU_NAME)
+ #TkComm::Tk_WINDOWS[@path] = self
+ TkCore::INTERP.tk_windows[@path] = self
if self.method(:create_self).arity == 0
p 'create_self has no arg' if $DEBUG
create_self
@@ -3590,10 +4991,9 @@ class TkSysMenu_Apple<TkMenu
end
class TkMenubutton<TkLabel
- WidgetClassNames['Menubutton'] = self
- def TkMenubutton.to_eval
- 'Menubutton'
- end
+ TkCommandNames = ['menubutton'.freeze].freeze
+ WidgetClassName = 'Menubutton'.freeze
+ WidgetClassNames[WidgetClassName] = self
def create_self(keys)
if keys and keys != None
tk_call 'menubutton', @path, *hash_kv(keys)
@@ -3604,27 +5004,30 @@ class TkMenubutton<TkLabel
end
class TkOptionMenubutton<TkMenubutton
+ TkCommandNames = ['tk_optionMenu'.freeze].freeze
+
class OptionMenu<TkMenu
- def initialize(parent)
- @path = parent.path + '.menu'
- TkComm::Tk_WINDOWS[@path] = self
+ def initialize(path) #==> return value of tk_optionMenu
+ @path = path
+ #TkComm::Tk_WINDOWS[@path] = self
+ TkCore::INTERP.tk_windows[@path] = self
end
end
def initialize(parent=nil, var=TkVariable.new, firstval=nil, *vals)
- if parent.kind_of Hash
+ if parent.kind_of? Hash
keys = _symbolkey2str(parent)
parent = keys['parent']
var = keys['variable'] if keys['variable']
firstval, *vals = keys['values']
end
- fail unless var.kind_of? TkVariable
+ fail 'variable option must be TkVariable' unless var.kind_of? TkVariable
@variable = var
firstval = @variable.value unless firstval
@variable.value = firstval
install_win(if parent then parent.path end)
- @menu = OptionMenu.new(self)
- tk_call 'tk_optionMenu', @path, @variable.id, firstval, *vals
+ @menu = OptionMenu.new(tk_call('tk_optionMenu', @path, @variable.id,
+ firstval, *vals))
end
def value
@@ -3633,10 +5036,12 @@ class TkOptionMenubutton<TkMenubutton
def activate(index)
@menu.activate(index)
+ self
end
def add(value)
@menu.add('radiobutton', 'variable'=>@variable,
'label'=>value, 'value'=>value)
+ self
end
def index(index)
@menu.index(index)
@@ -3647,27 +5052,34 @@ class TkOptionMenubutton<TkMenubutton
def insert(index, value)
@menu.add(index, 'radiobutton', 'variable'=>@variable,
'label'=>value, 'value'=>value)
+ self
end
def delete(index, last=None)
@menu.delete(index, last)
+ self
end
def yposition(index)
@menu.yposition(index)
end
- def menucget(index, key)
- @menu.cget(index, key)
+ def menu
+ @menu
+ end
+ def menucget(key)
+ @menu.cget(key)
end
- def menuconfigure(index, key, val=None)
- @menu.configure(index, key, val)
+ def menuconfigure(key, val=None)
+ @menu.configure(key, val)
+ self
end
- def menuconfiginfo(index, key=nil)
- @menu.configinfo(index, key)
+ def menuconfiginfo(key=nil)
+ @menu.configinfo(key)
end
def entrycget(index, key)
@menu.entrycget(index, key)
end
def entryconfigure(index, key, val=None)
@menu.entryconfigure(index, key, val)
+ self
end
def entryconfiginfo(index, key=nil)
@menu.entryconfiginfo(index, key)
@@ -3679,6 +5091,9 @@ module TkComposite
extend Tk
def initialize(parent=nil, *args)
+ @delegates = {}
+ @delegates['DEFAULT'] = @frame
+
if parent.kind_of? Hash
keys = _symbolkey2str(parent)
parent = keys['parent']
@@ -3700,10 +5115,6 @@ module TkComposite
private :initialize_composite
def delegate(option, *wins)
- unless @delegates
- @delegates = {}
- @delegates['DEFAULT'] = @frame
- end
if @delegates[option].kind_of?(Array)
for i in wins
@delegates[option].push(i)
@@ -3738,27 +5149,88 @@ module TkClipboard
include Tk
extend Tk
- def clear
- tk_call 'clipboard', 'clear'
+ TkCommandNames = ['clipboard'.freeze].freeze
+
+ def self.clear(win=nil)
+ if win
+ tk_call 'clipboard', 'clear', '-displayof', win
+ else
+ tk_call 'clipboard', 'clear'
+ end
end
- def get
- begin
- tk_call 'selection', 'get', '-selection', 'CLIPBOARD'
- rescue
- ''
+ def self.clear_on_display(win)
+ tk_call 'clipboard', 'clear', '-displayof', win
+ end
+
+ def self.get(type=nil)
+ if type
+ tk_call 'clipboard', 'get', '-type', type
+ else
+ tk_call 'clipboard', 'get'
end
end
- def set(data)
+ def self.get_on_display(win, type=nil)
+ if type
+ tk_call 'clipboard', 'get', '-displayof', win, '-type', type
+ else
+ tk_call 'clipboard', 'get', '-displayof', win
+ end
+ end
+
+ def self.set(data, keys=nil)
clear
- append(data)
+ append(data, keys)
end
- def append(data)
- tk_call 'clipboard', 'append', data
+ def self.set_on_display(win, data, keys=nil)
+ clear(win)
+ append_on_display(win, data, keys)
end
- module_function :clear, :set, :get, :append
+ def self.append(data, keys=nil)
+ args = ['clipboard', 'append']
+ args += hash_kv(keys)
+ args += ['--', data]
+ tk_call(*args)
+ end
+ def self.append_on_display(win, data, keys=nil)
+ args = ['clipboard', 'append', '-displayof', win]
+ args += hash_kv(keys)
+ args += ['--', data]
+ tk_call(*args)
+ end
+
+ def clear
+ TkClipboard.clear_on_display(self)
+ self
+ end
+ def get(type=nil)
+ TkClipboard.get_on_display(self, type)
+ end
+ def set(data, keys=nil)
+ TkClipboard.set_on_display(self, data, keys)
+ self
+ end
+ def append(data, keys=nil)
+ TkClipboard.append_on_display(self, data, keys)
+ self
+ end
end
+# widget_destroy_hook
+require 'tkvirtevent'
+TkBindTag::ALL.bind(TkVirtualEvent.new('Destroy'), proc{|xpath|
+ path = xpath[1..-1]
+ if (widget = TkCore::INTERP.tk_windows[path])
+ if widget.respond_to?(:__destroy_hook__)
+ begin
+ widget.__destroy_hook__
+ rescue Exception
+ end
+ end
+ end
+ }, 'x%W')
+
+# autoload
autoload :TkCanvas, 'tkcanvas'
autoload :TkImage, 'tkcanvas'
autoload :TkBitmapImage, 'tkcanvas'
@@ -3767,15 +5239,18 @@ autoload :TkEntry, 'tkentry'
autoload :TkSpinbox, 'tkentry'
autoload :TkText, 'tktext'
autoload :TkDialog, 'tkdialog'
+autoload :TkDialog2, 'tkdialog'
autoload :TkWarning, 'tkdialog'
+autoload :TkWarning2, 'tkdialog'
autoload :TkMenubar, 'tkmenubar'
autoload :TkAfter, 'tkafter'
+autoload :TkTimer, 'tkafter'
autoload :TkPalette, 'tkpalette'
autoload :TkFont, 'tkfont'
-autoload :TkVirtualEvent, 'tkvirtevent'
autoload :TkBgError, 'tkbgerror'
autoload :TkManageFocus, 'tkmngfocus'
autoload :TkPalette, 'tkpalette'
autoload :TkWinDDE, 'tkwinpkg'
autoload :TkWinRegistry, 'tkwinpkg'
autoload :TkMacResource, 'tkmacpkg'
+autoload :TkConsole, 'tkconsole'
diff --git a/ext/tk/lib/tkafter.rb b/ext/tk/lib/tkafter.rb
index b7c21e7529..529b1e6cfb 100644
--- a/ext/tk/lib/tkafter.rb
+++ b/ext/tk/lib/tkafter.rb
@@ -5,26 +5,41 @@
#
require 'tk'
-class TkAfter
+class TkTimer
include TkCore
extend TkCore
- Tk_CBID = [0]
+ TkCommandNames = ['after'.freeze].freeze
+
+ Tk_CBID = ['a'.freeze, '00000']
Tk_CBTBL = {}
- INTERP._invoke("proc", "rb_after", "id", "ruby [format \"TkAfter.callback %%Q!%s!\" $id]")
+ TkCore::INTERP.add_tk_procs('rb_after', 'id', <<-'EOL')
+ if {[set st [catch {ruby [format "TkTimer.callback %%Q!%s!" $id]} ret]] != 0} {
+ return -code $st $ret
+ } {
+ return $ret
+ }
+ EOL
+
###############################
# class methods
###############################
- def TkAfter.callback(obj_id)
+ def self.callback(obj_id)
@after_id = nil
ex_obj = Tk_CBTBL[obj_id]
- return nil if ex_obj == nil; # canceled
- _get_eval_string(ex_obj.do_callback)
+ return "" if ex_obj == nil; # canceled
+ #_get_eval_string(ex_obj.do_callback)
+ begin
+ ex_obj.cb_call
+ rescue Exception
+ ex_obj.cancel
+ ""
+ end
end
- def TkAfter.info
+ def self.info
tk_call('after', 'info').split(' ').collect!{|id|
ret = Tk_CBTBL.find{|key,val| val.after_id == id}
(ret == nil)? id: ret[1]
@@ -38,7 +53,7 @@ class TkAfter
@in_callback = true
begin
@return_value = @current_proc.call(self)
- rescue StandardError, NameError
+ rescue Exception
if @cancel_on_exception
cancel
return nil
@@ -60,6 +75,7 @@ class TkAfter
@after_id = tk_call('after', sleep, @after_script)
@current_args = args
@current_script = [sleep, @after_script]
+ self
end
def set_next_callback(args)
@@ -95,8 +111,17 @@ class TkAfter
end
def initialize(*args)
- @id = format("a%.4d", Tk_CBID[0])
- Tk_CBID[0] += 1
+ @id = Tk_CBID.join
+ Tk_CBID[1].succ!
+
+ # @cb_cmd = TkCore::INTERP.get_cb_entry(self.method(:do_callback))
+ @cb_cmd = TkCore::INTERP.get_cb_entry(proc{
+ begin
+ self.do_callback
+ rescue
+ self.cancel
+ end
+ })
@set_next = true
@@ -125,6 +150,7 @@ class TkAfter
set_procs(*args) if args != []
@running = false
+ @in_callback = false
end
attr :after_id
@@ -137,6 +163,10 @@ class TkAfter
attr_accessor :loop_exec
+ def cb_call
+ @cb_cmd.call
+ end
+
def get_procs
[@init_sleep, @init_proc, @init_args, @sleep_time, @loop_exec, @loop_proc]
end
@@ -215,6 +245,28 @@ class TkAfter
self
end
+ def delete_procs(*procs)
+ procs.each{|e|
+ if e.kind_of? Proc
+ @loop_proc.delete([e])
+ else
+ @loop_proc.delete(e)
+ end
+ }
+ @proc_max = @loop_proc.size
+
+ cancel if @proc_max == 0
+
+ self
+ end
+
+ def delete_at(n)
+ @loop_proc.delete_at(n)
+ @proc_max = @loop_proc.size
+ cancel if @proc_max == 0
+ self
+ end
+
def set_start_proc(sleep, init_proc, *init_args)
if !sleep == 'idle' && !sleep.kind_of?(Integer)
fail format("%s need to be Integer", sleep.inspect)
@@ -278,11 +330,12 @@ class TkAfter
alias stop cancel
def continue(wait=nil)
+ fail RuntimeError, "is already running" if @running
sleep, cmd = @current_script
- return nil if cmd == nil || @running == true
+ fail RuntimeError, "no procedure to continue" unless cmd
if wait
if not wait.kind_of? Integer
- fail format("%s need to be Integer", wait.inspect)
+ fail RuntimeError, format("%s need to be Integer", wait.inspect)
end
sleep = wait
end
@@ -293,7 +346,7 @@ class TkAfter
end
def skip
- return nil if @running == false
+ fail RuntimeError, "is not running now" unless @running
cancel
Tk_CBTBL[@id] = self
@running = true
@@ -310,3 +363,5 @@ class TkAfter
end
end
end
+
+TkAfter = TkTimer
diff --git a/ext/tk/lib/tkbgerror.rb b/ext/tk/lib/tkbgerror.rb
index 8022077a3f..760a3901b3 100644
--- a/ext/tk/lib/tkbgerror.rb
+++ b/ext/tk/lib/tkbgerror.rb
@@ -7,11 +7,23 @@ require 'tk'
module TkBgError
extend Tk
+ TkCommandNames = ['bgerror'.freeze].freeze
+
def bgerror(message)
tk_call 'bgerror', message
end
alias tkerror bgerror
alias show bgerror
-
module_function :bgerror, :tkerror, :show
+
+ def set_handler(hdlr = Proc.new) #==> handler :: proc{|msg| ...body... }
+ tk_call('proc', 'bgerror', 'msg', install_cmd(hdlr) + ' $msg')
+ end
+ def set_default
+ begin
+ tk_call('rename', 'bgerror', '')
+ rescue RuntimeError
+ end
+ end
+ module_function :set_handler, :set_default
end
diff --git a/ext/tk/lib/tkcanvas.rb b/ext/tk/lib/tkcanvas.rb
index 4a5e4b45bb..cbe4f89043 100644
--- a/ext/tk/lib/tkcanvas.rb
+++ b/ext/tk/lib/tkcanvas.rb
@@ -29,10 +29,12 @@ class TkCanvas<TkWindow
include TkTreatCItemFont
include Scrollable
+ TkCommandNames = ['canvas'.freeze].freeze
WidgetClassName = 'Canvas'.freeze
WidgetClassNames[WidgetClassName] = self
- def self.to_eval
- WidgetClassName
+
+ def __destroy_hook__
+ TkcItem::CItemID_TBL.delete(@path)
end
def create_self(keys)
@@ -54,6 +56,7 @@ class TkCanvas<TkWindow
def addtag(tag, mode, *args)
tk_send 'addtag', tagid(tag), mode, *args
+ self
end
def addtag_above(tagOrId, target)
addtag(tagOrId, 'above', tagid(target))
@@ -83,10 +86,17 @@ class TkCanvas<TkWindow
def itembind(tag, context, cmd=Proc.new, args=nil)
_bind([path, "bind", tagid(tag)], context, cmd, args)
+ self
end
def itembind_append(tag, context, cmd=Proc.new, args=nil)
_bind_append([path, "bind", tagid(tag)], context, cmd, args)
+ self
+ end
+
+ def itembind_remove(tag, context)
+ _bind_remove([path, "bind", tagid(tag)], context)
+ self
end
def itembindinfo(tag, context=nil)
@@ -104,21 +114,29 @@ class TkCanvas<TkWindow
if args == []
tk_split_list(tk_send('coords', tagid(tag)))
else
- tk_send('coords', tagid(tag), *args)
+ tk_send('coords', tagid(tag), *(args.flatten))
end
end
def dchars(tag, first, last=None)
tk_send 'dchars', tagid(tag), first, last
+ self
end
def delete(*args)
+ if TkcItem::CItemID_TBL[self.path]
+ find('withtag', *args).each{|item|
+ TkcItem::CItemID_TBL[self.path].delete(item.id)
+ }
+ end
tk_send 'delete', *args.collect{|t| tagid(t)}
+ self
end
alias remove delete
def dtag(tag, tag_to_del=None)
tk_send 'dtag', tagid(tag), tag_to_del
+ self
end
def find(mode, *args)
@@ -151,6 +169,7 @@ class TkCanvas<TkWindow
def itemfocus(tagOrId=nil)
if tagOrId
tk_send 'focus', tagid(tagOrId)
+ self
else
ret = tk_send('focus')
if ret == ""
@@ -169,14 +188,16 @@ class TkCanvas<TkWindow
def icursor(tagOrId, index)
tk_send 'icursor', tagid(tagOrId), index
+ self
end
def index(tagOrId, index)
- tk_send 'index', tagid(tagOrId), index
+ number(tk_send('index', tagid(tagOrId), index))
end
def insert(tagOrId, index, string)
tk_send 'insert', tagid(tagOrId), index, string
+ self
end
def itemcget(tagOrId, option)
@@ -215,6 +236,7 @@ class TkCanvas<TkWindow
tk_send 'itemconfigure', tagid(tagOrId), "-#{key}", value
end
end
+ self
end
# def itemconfigure(tagOrId, key, value=None)
# if key.kind_of? Hash
@@ -285,10 +307,12 @@ class TkCanvas<TkWindow
def lower(tag, below=None)
tk_send 'lower', tagid(tag), tagid(below)
+ self
end
def move(tag, x, y)
tk_send 'move', tagid(tag), x, y
+ self
end
def postscript(keys)
@@ -297,21 +321,26 @@ class TkCanvas<TkWindow
def raise(tag, above=None)
tk_send 'raise', tagid(tag), tagid(above)
+ self
end
def scale(tag, x, y, xs, ys)
tk_send 'scale', tagid(tag), x, y, xs, ys
+ self
end
def scan_mark(x, y)
tk_send 'scan', 'mark', x, y
+ self
end
def scan_dragto(x, y)
tk_send 'scan', 'dragto', x, y
+ self
end
def select(mode, *args)
- tk_send 'select', mode, *args
+ r = tk_send('select', mode, *args)
+ (mode == 'item')? TkcItem.id2obj(self, r): self
end
def select_adjust(tagOrId, index)
select('adjust', tagid(tagOrId), index)
@@ -340,6 +369,7 @@ module TkcTagAccess
def addtag(tag)
@c.addtag(tag, 'with', @id)
+ self
end
def bbox
@@ -348,6 +378,17 @@ module TkcTagAccess
def bind(seq, cmd=Proc.new, args=nil)
@c.itembind @id, seq, cmd, args
+ self
+ end
+
+ def bind_append(seq, cmd=Proc.new, args=nil)
+ @c.itembind_append @id, seq, cmd, args
+ self
+ end
+
+ def bind_remove(seq)
+ @c.itembind_remove @id, seq
+ self
end
def bindinfo(seq=nil)
@@ -360,6 +401,7 @@ module TkcTagAccess
def configure(key, value=None)
@c.itemconfigure @id, key, value
+ self
end
# def configure(keys)
# @c.itemconfigure @id, keys
@@ -375,10 +417,12 @@ module TkcTagAccess
def dchars(first, last=None)
@c.dchars @id, first, last
+ self
end
def dtag(tag_to_del=None)
@c.dtag @id, tag_to_del
+ self
end
def find
@@ -396,6 +440,7 @@ module TkcTagAccess
def icursor(index)
@c.icursor @id, index
+ self
end
def index(index)
@@ -404,32 +449,40 @@ module TkcTagAccess
def insert(beforethis, string)
@c.insert @id, beforethis, string
+ self
end
def lower(belowthis=None)
@c.lower @id, belowthis
+ self
end
def move(xamount, yamount)
@c.move @id, xamount, yamount
+ self
end
def raise(abovethis=None)
@c.raise @id, abovethis
+ self
end
def scale(xorigin, yorigin, xscale, yscale)
@c.scale @id, xorigin, yorigin, xscale, yscale
+ self
end
def select_adjust(index)
@c.select('adjust', @id, index)
+ self
end
def select_from(index)
@c.select('from', @id, index)
+ self
end
def select_to(index)
@c.select('to', @id, index)
+ self
end
def itemtype
@@ -475,8 +528,10 @@ end
class TkcTag<TkObject
include TkcTagAccess
- CTagID_TBL = {}
- Tk_CanvasTag_ID = ['ctag0000']
+ CTagID_TBL = TkCore::INTERP.create_table
+ Tk_CanvasTag_ID = ['ctag', '00000']
+
+ TkCore::INTERP.init_ip_env{ CTagID_TBL.clear }
def TkcTag.id2obj(canvas, id)
cpath = canvas.path
@@ -490,57 +545,65 @@ class TkcTag<TkObject
end
@c = parent
@cpath = parent.path
- @path = @id = Tk_CanvasTag_ID[0]
+ @path = @id = Tk_CanvasTag_ID.join
CTagID_TBL[@cpath] = {} unless CTagID_TBL[@cpath]
CTagID_TBL[@cpath][@id] = self
- Tk_CanvasTag_ID[0] = Tk_CanvasTag_ID[0].succ
+ Tk_CanvasTag_ID[1] = Tk_CanvasTag_ID[1].succ
if mode
tk_call @c.path, "addtag", @id, mode, *args
end
end
def id
- return @id
+ @id
end
def delete
@c.delete @id
CTagID_TBL[@cpath].delete(@id) if CTagID_TBL[@cpath]
+ self
end
alias remove delete
alias destroy delete
def set_to_above(target)
@c.addtag_above(@id, target)
+ self
end
alias above set_to_above
def set_to_all
@c.addtag_all(@id)
+ self
end
alias all set_to_all
def set_to_below(target)
@c.addtag_below(@id, target)
+ self
end
alias below set_to_below
def set_to_closest(x, y, halo=None, start=None)
@c.addtag_closest(@id, x, y, halo, start)
+ self
end
alias closest set_to_closest
def set_to_enclosed(x1, y1, x2, y2)
@c.addtag_enclosed(@id, x1, y1, x2, y2)
+ self
end
alias enclosed set_to_enclosed
def set_to_overlapping(x1, y1, x2, y2)
@c.addtag_overlapping(@id, x1, y1, x2, y2)
+ self
end
alias overlapping set_to_overlapping
def set_to_withtag(target)
@c.addtag_withtag(@id, target)
+ self
end
alias withtag set_to_withtag
end
@@ -597,17 +660,17 @@ class TkcTagCurrent<TkcTag
end
class TkcGroup<TkcTag
- Tk_cGroup_ID = ['tkcg00000']
+ Tk_cGroup_ID = ['tkcg', '00000']
def create_self(parent, *args)
if not parent.kind_of?(TkCanvas)
fail format("%s need to be TkCanvas", parent.inspect)
end
@c = parent
@cpath = parent.path
- @path = @id = Tk_cGroup_ID[0]
+ @path = @id = Tk_cGroup_ID.join
CTagID_TBL[@cpath] = {} unless CTagID_TBL[@cpath]
CTagID_TBL[@cpath][@id] = self
- Tk_cGroup_ID[0] = Tk_cGroup_ID[0].succ
+ Tk_cGroup_ID[1] = Tk_cGroup_ID[1].succ
add(*args) if args != []
end
@@ -615,12 +678,14 @@ class TkcGroup<TkcTag
for i in tags
i.addtag @id
end
+ self
end
def exclude(*tags)
for i in tags
i.delete @id
end
+ self
end
end
@@ -628,7 +693,9 @@ class TkcItem<TkObject
include TkcTagAccess
CItemTypeToClass = {}
- CItemID_TBL = {}
+ CItemID_TBL = TkCore::INTERP.create_table
+
+ TkCore::INTERP.init_ip_env{ CItemID_TBL.clear }
def TkcItem.type2class(type)
CItemTypeToClass[type]
@@ -683,12 +750,13 @@ class TkcItem<TkObject
def create_self(*args); end
private :create_self
def id
- return @id
+ @id
end
def delete
@c.delete @id
CItemID_TBL[@path].delete(@id) if CItemID_TBL[@path]
+ self
end
alias remove delete
alias destroy delete
@@ -752,12 +820,16 @@ end
class TkImage<TkObject
include Tk
- Tk_IMGTBL = {}
+ TkCommandNames = ['image'.freeze].freeze
+
+ Tk_IMGTBL = TkCore::INTERP.create_table
+ Tk_Image_ID = ['i', '00000']
+
+ TkCore::INTERP.init_ip_env{ Tk_IMGTBL.clear }
- Tk_Image_ID = ['i00000']
def initialize(keys=nil)
- @path = Tk_Image_ID[0]
- Tk_Image_ID[0] = Tk_Image_ID[0].succ
+ @path = Tk_Image_ID.join
+ Tk_Image_ID[1] = Tk_Image_ID[1].succ
tk_call 'image', 'create', @type, @path, *hash_kv(keys)
Tk_IMGTBL[@path] = self
end
@@ -765,6 +837,7 @@ class TkImage<TkObject
def delete
Tk_IMGTBL.delete(@id) if @id
tk_call('image', 'delete', @path)
+ self
end
def height
number(tk_call('image', 'height', @path))
@@ -805,6 +878,7 @@ class TkPhotoImage<TkImage
def blank
tk_send 'blank'
+ self
end
def cget(option)
@@ -826,14 +900,16 @@ class TkPhotoImage<TkImage
}.flatten
tk_send 'copy', source, *args
+
+ self
end
def data(keys=nil)
- tk_send 'data', *hash_kv(keys)
+ tk_send('data', *hash_kv(keys))
end
def get(x, y)
- tk_send 'get', x, y
+ tk_send('get', x, y).split.collect{|n| n.to_i}
end
def put(data, *to)
@@ -842,6 +918,7 @@ class TkPhotoImage<TkImage
else
tk_send 'put', data, '-to', *to
end
+ self
end
def read(file, *opts)
@@ -854,10 +931,21 @@ class TkPhotoImage<TkImage
}.flatten
tk_send 'read', file, *args
+
+ self
end
def redither
tk_send 'redither'
+ self
+ end
+
+ def get_transparency(x, y)
+ bool(tk_send('transparency', 'get', x, y))
+ end
+ def set_transparency(x, y, st)
+ tk_send('transparency', 'set', x, y, st)
+ self
end
def write(file, *opts)
@@ -870,5 +958,7 @@ class TkPhotoImage<TkImage
}.flatten
tk_send 'write', file, *args
+
+ self
end
end
diff --git a/ext/tk/lib/tkconsole.rb b/ext/tk/lib/tkconsole.rb
new file mode 100644
index 0000000000..d97df159fc
--- /dev/null
+++ b/ext/tk/lib/tkconsole.rb
@@ -0,0 +1,28 @@
+#
+# tkconsole.rb : control the console on system without a real console
+#
+require 'tk'
+
+module TkConsole
+ include Tk
+ extend Tk
+
+ TkCommandNames = ['console'.freeze].freeze
+
+ def self.title(str=None)
+ tk_call 'console', str
+ end
+ def self.hide
+ tk_call 'console', 'hide'
+ end
+ def self.show
+ tk_call 'console', 'show'
+ end
+ def self.eval(tcl_script)
+ #
+ # supports a Tcl script only
+ # I have no idea to support a Ruby script seamlessly.
+ #
+ tk_call 'console', 'eval', tcl_script
+ end
+end
diff --git a/ext/tk/lib/tkdialog.rb b/ext/tk/lib/tkdialog.rb
index 8b8ee69128..febe470376 100644
--- a/ext/tk/lib/tkdialog.rb
+++ b/ext/tk/lib/tkdialog.rb
@@ -1,18 +1,63 @@
require "tk"
-class TkDialog < TkWindow
+class TkDialog2 < TkWindow
extend Tk
+ TkCommandNames = ['tk_dialog'.freeze].freeze
+
+ def self.show(*args)
+ dlog = self.new(*args)
+ dlog.show
+ dlog
+ end
+
+ def _set_button_config(configs)
+ set_config = proc{|c,i|
+ if $VERBOSE && (c.has_key?('command') || c.has_key?(:command))
+ STDERR.print("Warning: cannot give a command option " +
+ "to the dialog button#{i}. It was removed.\n")
+ end
+ c.delete('command'); c.delete(:command)
+ @config << format("%s.button%s configure %s; ",
+ @path, i, hash_kv(c).join(' '))
+ }
+ case configs
+ when Proc
+ @buttons.each_index{|i|
+ if (c = configs.call(i)).kind_of? Hash
+ set_config.call(c,i)
+ end
+ }
+
+ when Array
+ @buttons.each_index{|i|
+ if (c = configs[i]).kind_of? Hash
+ set_config.call(c,i)
+ end
+ }
+
+ when Hash
+ @buttons.each_with_index{|s,i|
+ if (c = configs[s]).kind_of? Hash
+ set_config.call(c,i)
+ end
+ }
+ end
+ @config = 'after idle {' + @config + '};' if @config != ""
+ end
+ private :_set_button_config
+
# initialize tk_dialog
def initialize(keys = nil)
super()
+
@var = TkVariable.new
- id = @var.id
@title = title
@message = message
@message_config = message_config
+ @msgframe_config = msgframe_config
@bitmap = bitmap
@bitmap_config = message_config
@@ -20,28 +65,46 @@ class TkDialog < TkWindow
@default_button = default_button
@buttons = buttons
- @button_configs = proc{|num| button_configs num}
+ @button_configs = proc{|num| button_configs(num)}
+ @btnframe_config = btnframe_config
+
+ #@config = "puts [winfo children .w0000];"
+ @config = ""
+
+ @command = nil
if keys.kind_of? Hash
keys = _symbolkey2str(keys)
- @title = keys['title'] if keys['title']
- @message = keys['message'] if keys['message']
- @bitmap = keys['bitmap'] if keys['bitmap']
- @default_button = keys['default'] if keys['default']
- @buttons = keys['buttons'] if keys['buttons']
+ @title = keys['title'] if keys.key? 'title'
+ @message = keys['message'] if keys.key? 'message'
+ @bitmap = keys['bitmap'] if keys.key? 'bitmap'
+ @bitmap = '{}' if @bitmap == nil || @bitmap == ""
+ @default_button = keys['default'] if keys.key? 'default'
+ @buttons = keys['buttons'] if keys.key? 'buttons'
@command = keys['prev_command']
- @message_config = keys['message_config'] if keys['message_config']
- @bitmap_config = keys['bitmap_config'] if keys['bitmap_config']
- @button_configs = keys['button_configs'] if keys['button_configs']
+ @message_config = keys['message_config'] if keys.key? 'message_config'
+ @msgframe_config = keys['msgframe_config'] if keys.key? 'msgframe_config'
+ @bitmap_config = keys['bitmap_config'] if keys.key? 'bitmap_config'
+ @button_configs = keys['button_configs'] if keys.key? 'button_configs'
+ @btnframe_config = keys['btnframe_config'] if keys.key? 'btnframe_config'
end
if @title.include? ?\s
@title = '{' + @title + '}'
end
- @buttons = tk_split_list(@buttons) if @buttons.kind_of? String
+ if @buttons.kind_of? Array
+ _set_button_config(@buttons.collect{|cfg|
+ (cfg.kind_of? Array)? cfg[1]: nil})
+ @buttons = @buttons.collect{|cfg| (cfg.kind_of? Array)? cfg[0]: cfg}
+ end
+ if @buttons.kind_of? Hash
+ _set_button_config(@buttons)
+ @buttons = @buttons.keys
+ end
+ @buttons = tk_split_simplelist(@buttons) if @buttons.kind_of? String
@buttons = @buttons.collect{|s|
if s.kind_of? Array
s = s.join(' ')
@@ -53,34 +116,46 @@ class TkDialog < TkWindow
end
}
- config = ""
if @message_config.kind_of? Hash
- config << format("%s.msg configure %s\n",
+ @config << format("%s.msg configure %s;",
@path, hash_kv(@message_config).join(' '))
end
+
+ if @msgframe_config.kind_of? Hash
+ @config << format("%s.top configure %s;",
+ @path, hash_kv(@msgframe_config).join(' '))
+ end
+
+ if @btnframe_config.kind_of? Hash
+ @config << format("%s.bot configure %s;",
+ @path, hash_kv(@btnframe_config).join(' '))
+ end
+
if @bitmap_config.kind_of? Hash
- config << format("%s.msg configure %s\n",
+ @config << format("%s.bitmap configure %s;",
@path, hash_kv(@bitmap_config).join(' '))
end
- if @button_configs.kind_of? Proc
- @buttons.each_index{|i|
- if (c = @button_configs.call(i)).kind_of? Hash
- config << format("%s.button%s configure %s\n",
- @path, i, hash_kv(c).join(' '))
- end
- }
- end
- config = 'after idle {' + config + '};' if config != ""
+
+ _set_button_config(@button_configs) if @button_configs
if @command.kind_of? Proc
@command.call(self)
end
+ end
- INTERP._eval('eval {global '+id+';'+config+
- 'set '+id+' [tk_dialog '+
- @path+" "+@title+" {#{@message}} "+@bitmap+" "+
- String(@default_button)+" "+@buttons.join(' ')+']}')
+ def show
+ if @default_button.kind_of? String
+ default_button = @buttons.index(@default_button)
+ else
+ default_button = @default_button
+ end
+ default_button = '{}' if default_button == nil
+ Tk.ip_eval('eval {global '+@var.id+';'+@config+
+ 'set '+@var.id+' [tk_dialog '+
+ @path+" "+@title+" {#{@message}} "+@bitmap+" "+
+ String(default_button)+" "+@buttons.join(' ')+']}')
end
+
def value
return @var.value.to_i
end
@@ -89,22 +164,36 @@ class TkDialog < TkWindow
# these methods must be overridden for each dialog #
# #
######################################################
+ private
+
def title
+ # returns a title string of the dialog window
return "DIALOG"
end
def message
+ # returns a message text to display on the dialog
return "MESSAGE"
end
def message_config
+ # returns a Hash {option=>value, ...} for the message text
+ return nil
+ end
+ def msgframe_config
+ # returns a Hash {option=>value, ...} for the message text frame
return nil
end
def bitmap
+ # returns a bitmap name or a bitmap file path
+ # (@ + path ; e.g. '@/usr/share/bitmap/sample.xbm')
return "info"
end
def bitmap_config
+ # returns nil or a Hash {option=>value, ...} for the bitmap
return nil
end
def default_button
+ # returns a default button's number or name
+ # if nil or null string, set no-default
return 0
end
def buttons
@@ -112,21 +201,50 @@ class TkDialog < TkWindow
return ["BUTTON1", "BUTTON2"]
end
def button_configs(num)
+ # returns nil / Proc / Array or Hash (see _set_button_config)
+ return nil
+ end
+ def btnframe_config
+ # returns nil or a Hash {option=>value, ...} for the button frame
return nil
end
end
+
+#
+# TkDialog : with showing at initialize
+#
+class TkDialog < TkDialog2
+ def self.show(*args)
+ self.new(*args)
+ end
+
+ def initialize(*args)
+ super(*args)
+ show
+ end
+end
+
+
#
# dialog for warning
#
-class TkWarning < TkDialog
+class TkWarning2 < TkDialog2
def initialize(mes)
- @mes = mes
- super()
+ super(:message=>mes)
end
- def message
- return @mes
+
+ def show(mes = nil)
+ mes_bup = @message
+ @message = mes if mes
+ ret = super()
+ @message = mes_bup
+ ret
end
+
+ #######
+ private
+
def title
return "WARNING";
end
@@ -140,3 +258,13 @@ class TkWarning < TkDialog
return "OK";
end
end
+
+class TkWarning < TkWarning2
+ def self.show(*args)
+ self.new(*args)
+ end
+ def initialize(mes)
+ super(mes)
+ show
+ end
+end
diff --git a/ext/tk/lib/tkentry.rb b/ext/tk/lib/tkentry.rb
index 2772dfd676..ccfb7928ac 100644
--- a/ext/tk/lib/tkentry.rb
+++ b/ext/tk/lib/tkentry.rb
@@ -8,16 +8,42 @@ require 'tk.rb'
class TkEntry<TkLabel
include Scrollable
+ TkCommandNames = ['entry'.freeze].freeze
WidgetClassName = 'Entry'.freeze
WidgetClassNames[WidgetClassName] = self
- def self.to_eval
- WidgetClassName
- end
class ValidateCmd
include TkComm
class ValidateArgs
+ VARG_KEY = 'disvPSVW'
+ VARG_TYPE = 'nnsssssw'
+
+ def self.scan_args(arg_str, arg_val)
+ arg_cnv = []
+ arg_str.strip.split(/\s+/).each_with_index{|kwd,idx|
+ if kwd =~ /^%(.)$/
+ if num = VARG_KEY.index($1)
+ case VARG_TYPE[num]
+ when ?n
+ arg_cnv << TkComm::number(arg_val[idx])
+ when ?s
+ arg_cnv << TkComm::string(arg_val[idx])
+ when ?w
+ arg_cnv << TkComm::window(arg_val[idx])
+ else
+ arg_cnv << arg_val[idx]
+ end
+ else
+ arg_cnv << arg_val[idx]
+ end
+ else
+ arg_cnv << arg_val[idx]
+ end
+ }
+ arg_cnv
+ end
+
def initialize(d,i,s,v,pp,ss,vv,ww)
@action = d
@index = i
@@ -40,13 +66,19 @@ class TkEntry<TkLabel
def initialize(cmd = Proc.new, args=nil)
if args
- @id = install_cmd(proc{|*arg|
- TkUtil.eval_cmd cmd, *arg
- }) + " " + args
+ @id =
+ install_cmd(proc{|*arg|
+ TkUtil.eval_cmd(cmd, ValidateArgs.scan_args(args, arg))
+ }) + " " + args
else
- @id = install_cmd(proc{|arg|
- TkUtil.eval_cmd cmd, ValidateArgs.new(*arg)
- }) + ' %d %i %s %v %P %S %V %W'
+ args = ' %d %i %s %v %P %S %V %W'
+ @id =
+ install_cmd(proc{|*arg|
+ TkUtil.eval_cmd(
+ cmd,
+ ValidateArgs.new(ValidateArgs.scan_args(args, arg))
+ )
+ }) + args
end
end
@@ -63,14 +95,6 @@ class TkEntry<TkLabel
end
end
- def bbox(index)
- tk_send 'bbox', index
- end
-
- def delete(s, e=None)
- tk_send 'delete', s, e
- end
-
def configure(slot, value=None)
if slot.kind_of? Hash
slot = _symbolkey2str(slot)
@@ -113,54 +137,67 @@ class TkEntry<TkLabel
end
super(slot, value)
end
+ self
end
+ def bbox(index)
+ list(tk_send('bbox', index))
+ end
def cursor
- tk_send 'index', 'insert'
+ number(tk_send('index', 'insert'))
end
def cursor=(index)
tk_send 'icursor', index
+ self
end
def index(index)
number(tk_send('index', index))
end
def insert(pos,text)
tk_send 'insert', pos, text
+ self
+ end
+ def delete(first, last=None)
+ tk_send 'insert', first, last
+ self
end
def mark(pos)
tk_send 'scan', 'mark', pos
+ self
end
def dragto(pos)
tk_send 'scan', 'dragto', pos
+ self
end
def selection_adjust(index)
tk_send 'selection', 'adjust', index
+ self
end
def selection_clear
tk_send 'selection', 'clear'
+ self
end
def selection_from(index)
tk_send 'selection', 'from', index
+ self
end
def selection_present()
bool(tk_send('selection', 'present'))
end
def selection_range(s, e)
tk_send 'selection', 'range', s, e
+ self
end
def selection_to(index)
tk_send 'selection', 'to', index
+ self
end
def validate(mode = nil)
if mode
configure 'validate', mode
else
- if tk_send('validate') == '0'
- false
- else
- true
- end
+ bool(tk_send('validate'))
end
end
@@ -192,11 +229,9 @@ class TkEntry<TkLabel
end
class TkSpinbox<TkEntry
+ TkCommandNames = ['spinbox'.freeze].freeze
WidgetClassName = 'Spinbox'.freeze
WidgetClassNames[WidgetClassName] = self
- def self.to_eval
- WidgetClassName
- end
def create_self(keys)
if keys and keys != None
@@ -212,10 +247,12 @@ class TkSpinbox<TkEntry
def spinup
tk_send 'invoke', 'spinup'
+ self
end
def spindown
tk_send 'invoke', 'spindown'
+ self
end
def set(str)
diff --git a/ext/tk/lib/tkfont.rb b/ext/tk/lib/tkfont.rb
index d9eb8ecbed..68eb91039b 100644
--- a/ext/tk/lib/tkfont.rb
+++ b/ext/tk/lib/tkfont.rb
@@ -9,9 +9,16 @@ class TkFont
include Tk
extend TkCore
- Tk_FontID = [0]
- Tk_FontNameTBL = {}
- Tk_FontUseTBL = {}
+ TkCommandNames = ['font'.freeze].freeze
+
+ Tk_FontID = ["@font".freeze, "00000"]
+ Tk_FontNameTBL = TkCore::INTERP.create_table
+ Tk_FontUseTBL = TkCore::INTERP.create_table
+
+ TkCore::INTERP.init_ip_env{
+ Tk_FontNameTBL.clear
+ Tk_FontUseTBL.clear
+ }
# set default font
case Tk::TK_VERSION
@@ -214,8 +221,8 @@ class TkFont
private
###################################
def initialize(ltn=DEFAULT_LATIN_FONT_NAME, knj=nil, keys=nil)
- @id = format("@font%.4d", Tk_FontID[0])
- Tk_FontID[0] += 1
+ @id = Tk_FontID.join
+ Tk_FontID[1].succ!
Tk_FontNameTBL[@id] = self
knj = DEFAULT_KANJI_FONT_NAME if JAPANIZED_TK && !knj
create_compoundfont(ltn, knj, keys)
@@ -879,6 +886,7 @@ class TkFont
def configure(slot, value=None)
configure_core(@compoundfont, slot, value)
+ self
end
def configinfo(slot=nil)
@@ -895,6 +903,7 @@ class TkFont
else
configure(slot, value)
end
+ self
end
def latin_configinfo(slot=nil)
@@ -914,6 +923,7 @@ class TkFont
#""
configure(slot, value)
end
+ self
end
def kanji_configinfo(slot=nil)
@@ -935,11 +945,13 @@ class TkFont
def latin_replace(ltn)
latin_replace_core(ltn)
reset_pointadjust
+ self
end
def kanji_replace(knj)
kanji_replace_core(knj)
reset_pointadjust
+ self
end
def measure(text)
@@ -1019,27 +1031,33 @@ module TkTreatTagFont
def font_configure(slot)
@parent.tagfont_configure(@id, slot)
+ self
end
def latinfont_configure(ltn, keys=nil)
@parent.latintagfont_configure(@id, ltn, keys)
+ self
end
alias asciifont_configure latinfont_configure
def kanjifont_configure(knj, keys=nil)
@parent.kanjitagfont_configure(@id, ltn, keys)
+ self
end
def font_copy(window, wintag=nil)
@parent.tagfont_copy(@id, window, wintag)
+ self
end
def latinfont_copy(window, wintag=nil)
@parent.latintagfont_copy(@id, window, wintag)
+ self
end
alias asciifont_copy latinfont_copy
def kanjifont_copy(window, wintag=nil)
@parent.kanjitagfont_copy(@id, window, wintag)
+ self
end
end
diff --git a/ext/tk/lib/tkmacpkg.rb b/ext/tk/lib/tkmacpkg.rb
new file mode 100644
index 0000000000..3bac5e6c3e
--- /dev/null
+++ b/ext/tk/lib/tkmacpkg.rb
@@ -0,0 +1,56 @@
+#
+# tkmacpkg.rb : methods for Tcl/Tk packages for Macintosh
+# 2000/11/22 by Hidetoshi Nagai <nagai@ai.kyutech.ac.jp>
+#
+# ATTENTION !!
+# This is NOT TESTED. Because I have no test-environment.
+#
+#
+require 'tk'
+
+module TkMacResource
+ extend Tk
+ extend TkMacResource
+
+ TkCommandNames = ['resource'.freeze].freeze
+
+ tk_call('package', 'require', 'resource')
+
+ def close(rsrcRef)
+ tk_call('resource', 'close', rsrcRef)
+ end
+
+ def delete(rsrcType, opts=nil)
+ tk_call('resource', 'delete', *(hash_kv(opts) + rsrcType))
+ end
+
+ def files(rsrcRef=nil)
+ if rsrcRef
+ tk_call('resource', 'files', rsrcRef)
+ else
+ tk_split_simplelist(tk_call('resource', 'files'))
+ end
+ end
+
+ def list(rsrcType, rsrcRef=nil)
+ tk_split_simplelist(tk_call('resource', 'list', rsrcType, rsrcRef))
+ end
+
+ def open(fname, access=nil)
+ tk_call('resource', 'open', fname, access)
+ end
+
+ def read(rsrcType, rsrcID, rsrcRef=nil)
+ tk_call('resource', 'read', rsrcType, rsrcID, rsrcRef)
+ end
+
+ def types(rsrcRef=nil)
+ tk_split_simplelist(tk_call('resource', 'types', rsrcRef))
+ end
+
+ def write(rsrcType, data, opts=nil)
+ tk_call('resource', 'write', *(hash_kv(opts) + rsrcType + data))
+ end
+
+ module_function :close, :delete, :files, :list, :open, :read, :types, :write
+end
diff --git a/ext/tk/lib/tkmngfocus.rb b/ext/tk/lib/tkmngfocus.rb
index 921fb646e7..461525009f 100644
--- a/ext/tk/lib/tkmngfocus.rb
+++ b/ext/tk/lib/tkmngfocus.rb
@@ -1,25 +1,31 @@
#
# tkmngfocus.rb : methods for Tcl/Tk standard library 'focus.tcl'
-# 1998/07/16 by Hidetoshi Nagai <nagai@ai.kyutech.ac.jp>
+# by Hidetoshi Nagai <nagai@ai.kyutech.ac.jp>
#
require 'tk'
module TkManageFocus
extend Tk
+ TkCommandNames = [
+ 'tk_focusFollowMouse'.freeze,
+ 'tk_focusNext'.freeze,
+ 'tk_focusPrev'.freeze
+ ].freeze
+
def TkManageFocus.followsMouse
tk_call 'tk_focusFollowsMouse'
end
def TkManageFocus.next(window)
- tk_call 'tk_focusNext', window
+ tk_tcl2ruby(tk_call('tk_focusNext', window))
end
def focusNext
TkManageFocus.next(self)
end
def TkManageFocus.prev(window)
- tk_call 'tk_focusPrev', window
+ tk_tcl2ruby(tk_call('tk_focusPrev', window))
end
def focusPrev
TkManageFocus.prev(self)
diff --git a/ext/tk/lib/tkpalette.rb b/ext/tk/lib/tkpalette.rb
index a2dc7c87cb..dc2fd47e5c 100644
--- a/ext/tk/lib/tkpalette.rb
+++ b/ext/tk/lib/tkpalette.rb
@@ -8,6 +8,12 @@ module TkPalette
include Tk
extend Tk
+ TkCommandNames = [
+ 'tk_setPalette'.freeze,
+ 'tk_bisque'.freeze,
+ 'tkDarken'.freeze
+ ].freeze
+
def TkPalette.set(*args)
args = args.to_a.flatten if args.kind_of? Hash
tk_call 'tk_setPalette', *args
diff --git a/ext/tk/lib/tktext.rb b/ext/tk/lib/tktext.rb
index dd4d649042..a28c3d31fc 100644
--- a/ext/tk/lib/tktext.rb
+++ b/ext/tk/lib/tktext.rb
@@ -28,13 +28,10 @@ class TkText<TkTextWin
include TkTreatTextTagFont
include Scrollable
+ TkCommandNames = ['text'.freeze].freeze
WidgetClassName = 'Text'.freeze
WidgetClassNames[WidgetClassName] = self
- def self.to_eval
- WidgetClassName
- end
-
def self.new(*args, &block)
obj = super(*args){}
obj.init_instance_variable
@@ -43,9 +40,15 @@ class TkText<TkTextWin
end
def init_instance_variable
+ @cmdtbl = []
@tags = {}
end
+ def __destroy_hook__
+ TTagID_TBL.delete(@path)
+ TMarkID_TBL.delete(@path)
+ end
+
def create_self(keys)
if keys and keys != None
tk_call 'text', @path, *hash_kv(keys)
@@ -77,55 +80,143 @@ class TkText<TkTextWin
end
def tagid2obj(tagid)
- if not @tags[tagid]
- tagid
- else
+ if @tags[tagid]
@tags[tagid]
+ else
+ tagid
end
end
def tag_names(index=None)
- tk_split_list(tk_send('tag', 'names', index)).collect{|elt|
+ tk_split_simplelist(tk_send('tag', 'names', index)).collect{|elt|
tagid2obj(elt)
}
end
def mark_names
- tk_split_list(tk_send('mark', 'names')).collect{|elt|
+ tk_split_simplelist(tk_send('mark', 'names')).collect{|elt|
tagid2obj(elt)
}
end
+ def mark_gravity(mark, direction=nil)
+ if direction
+ tk_send 'mark', 'gravity', mark, direction
+ self
+ else
+ tk_send 'mark', 'gravity', mark
+ end
+ end
+
+ def mark_set(mark, index)
+ tk_send 'mark', 'set', mark, index
+ self
+ end
+ alias set_mark mark_set
+
+ def mark_unset(*marks)
+ tk_send 'mark', 'unset', *marks
+ self
+ end
+ alias unset_mark mark_unset
+
def mark_next(index)
tagid2obj(tk_send('mark', 'next', index))
end
+ alias next_mark mark_next
def mark_previous(index)
tagid2obj(tk_send('mark', 'previous', index))
end
+ alias previous_mark mark_previous
- def window_names
- tk_send('window', 'names').collect{|elt|
- tagid2obj(elt)
- }
+ def image_cget(index, slot)
+ case slot.to_s
+ when 'text', 'label', 'show', 'data', 'file'
+ tk_send('image', 'cget', index, "-#{slot}")
+ else
+ tk_tcl2ruby(tk_send('image', 'cget', index, "-#{slot}"))
+ end
+ end
+
+ def image_configure(index, slot, value=None)
+ if slot.kind_of? Hash
+ tk_send('image', 'configure', index, *hash_kv(slot))
+ else
+ tk_send('image', 'configure', index, "-#{slot}", value)
+ end
+ self
+ end
+
+ def image_configinfo(index, slot = nil)
+ if slot
+ case slot.to_s
+ when 'text', 'label', 'show', 'data', 'file'
+ conf = tk_split_simplelist(tk_send('image', 'configure',
+ index, "-#{slot}"))
+ else
+ conf = tk_split_list(tk_send('image', 'configure',
+ index, "-#{slot}"))
+ end
+ conf[0] = conf[0][1..-1]
+ conf
+ else
+ tk_split_simplelist(tk_send('image', 'configure',
+ index)).collect{|conflist|
+ conf = tk_split_simplelist(conflist)
+ conf[0] = conf[0][1..-1]
+ case conf[0]
+ when 'text', 'label', 'show', 'data', 'file'
+ else
+ if conf[3]
+ if conf[3].index('{')
+ conf[3] = tk_split_list(conf[3])
+ else
+ conf[3] = tk_tcl2ruby(conf[3])
+ end
+ end
+ if conf[4]
+ if conf[4].index('{')
+ conf[4] = tk_split_list(conf[4])
+ else
+ conf[4] = tk_tcl2ruby(conf[4])
+ end
+ end
+ end
+ conf
+ }
+ end
end
def image_names
- tk_send('image', 'names').collect{|elt|
+ tk_split_simplelist(tk_send('image', 'names')).collect{|elt|
tagid2obj(elt)
}
end
def set_insert(index)
tk_send 'mark', 'set', 'insert', index
+ self
end
def set_current(index)
tk_send 'mark', 'set', 'current', index
+ self
end
def insert(index, chars, *tags)
- super index, chars, tags.collect{|x|_get_eval_string(x)}.join(' ')
+ if tags[0].kind_of? Array
+ # multiple chars-taglist argument
+ args = [chars]
+ while tags.size > 0
+ tags.shift.collect{|x|_get_eval_string(x)}.join(' ') # taglist
+ args << tags.shift if tags.size > 0 # chars
+ end
+ super index, *args
+ else
+ # single chars-taglist argument
+ super index, chars, tags.collect{|x|_get_eval_string(x)}.join(' ')
+ end
end
def destroy
@@ -149,47 +240,98 @@ class TkText<TkTextWin
end
def debug=(boolean)
tk_send 'debug', boolean
+ self
end
-
def bbox(index)
- inf = tk_send('bbox', index)
- (inf == "")? [0,0,0,0]: inf
+ list(tk_send('bbox', index))
end
+
def dlineinfo(index)
- inf = tk_send('dlineinfo', index)
- (inf == "")? [0,0,0,0,0]: inf
+ list(tk_send('dlineinfo', index))
+ end
+
+ def modified?
+ bool(tk_send('edit', 'modified'))
+ end
+ def modified(mode)
+ tk_send('edit', 'modified', mode)
+ self
+ end
+ def edit_redo
+ tk_send('edit', 'redo')
+ self
+ end
+ def edit_reset
+ tk_send('edit', 'reset')
+ self
+ end
+ def edit_separator
+ tk_send('edit', 'separator')
+ self
+ end
+ def edit_undo
+ tk_send('edit', 'undo')
+ self
end
def yview_pickplace(*what)
tk_send 'yview', '-pickplace', *what
+ self
end
def xview_pickplace(*what)
tk_send 'xview', '-pickplace', *what
+ self
end
def tag_add(tag, index1, index2=None)
tk_send 'tag', 'add', tag, index1, index2
+ self
end
+ alias addtag tag_add
+ alias add_tag tag_add
+
+ def tag_delete(*tags)
+ tk_send 'tag', 'delete', *tags
+ if TkTextTag::TTagID_TBL[@path]
+ tags.each{|tag|
+ if tag.kind_of? TkTextTag
+ TTagID_TBL[@path].delete(tag.id)
+ else
+ TTagID_TBL[@path].delete(tag)
+ end
+ }
+ end
+ self
+ end
+ alias deltag tag_delete
+ alias delete_tag tag_delete
def tag_bind(tag, seq, cmd=Proc.new, args=nil)
- _bind(['tag', 'bind', tag], seq, cmd, args)
+ _bind([@path, 'tag', 'bind', tag], seq, cmd, args)
+ self
end
def tag_bind_append(tag, seq, cmd=Proc.new, args=nil)
- _bind_append(['tag', 'bind', tag], seq, cmd, args)
+ _bind_append([@path, 'tag', 'bind', tag], seq, cmd, args)
+ self
+ end
+
+ def tag_bind_remove(tag, seq)
+ _bind_remove([@path, 'tag', 'bind', tag], seq)
+ self
end
def tag_bindinfo(tag, context=nil)
- _bindinfo(['tag', 'bind', tag], context)
+ _bindinfo([@path, 'tag', 'bind', tag], context)
end
def tag_cget(tag, key)
case key.to_s
when 'text', 'label', 'show', 'data', 'file'
- tk_call @path, 'tag', 'cget', tag, "-#{key}"
+ tk_call(@path, 'tag', 'cget', tag, "-#{key}")
else
- tk_tcl2ruby tk_call(@path, 'tag', 'cget', tag, "-#{key}")
+ tk_tcl2ruby(tk_call(@path, 'tag', 'cget', tag, "-#{key}"))
end
end
@@ -213,6 +355,7 @@ class TkText<TkTextWin
tk_send 'tag', 'configure', tag, "-#{key}", val
end
end
+ self
end
def tag_configinfo(tag, key=nil)
@@ -254,14 +397,17 @@ class TkText<TkTextWin
def tag_raise(tag, above=None)
tk_send 'tag', 'raise', tag, above
+ self
end
def tag_lower(tag, below=None)
tk_send 'tag', 'lower', tag, below
+ self
end
def tag_remove(tag, *index)
tk_send 'tag', 'remove', tag, *index
+ self
end
def tag_ranges(tag)
@@ -274,11 +420,79 @@ class TkText<TkTextWin
end
def tag_nextrange(tag, first, last=None)
- tk_split_simplelist(tk_send('tag', 'nextrange', tag, first, last))
+ tk_split_list(tk_send('tag', 'nextrange', tag, first, last))
end
def tag_prevrange(tag, first, last=None)
- tk_split_simplelist(tk_send('tag', 'prevrange', tag, first, last))
+ tk_split_list(tk_send('tag', 'prevrange', tag, first, last))
+ end
+
+ def window_cget(index, slot)
+ case slot.to_s
+ when 'text', 'label', 'show', 'data', 'file'
+ tk_send('window', 'cget', index, "-#{slot}")
+ else
+ tk_tcl2ruby(tk_send('window', 'cget', index, "-#{slot}"))
+ end
+ end
+
+ def window_configure(index, slot, value=None)
+ if index.kind_of? TkTextWindow
+ index.configure(slot, value)
+ else
+ if slot.kind_of? Hash
+ tk_send('window', 'configure', index, *hash_kv(slot))
+ else
+ tk_send('window', 'configure', index, "-#{slot}", value)
+ end
+ end
+ self
+ end
+
+ def window_configinfo(win, slot = nil)
+ if slot
+ case slot.to_s
+ when 'text', 'label', 'show', 'data', 'file'
+ conf = tk_split_simplelist(tk_send('window', 'configure',
+ win, "-#{slot}"))
+ else
+ conf = tk_split_list(tk_send('window', 'configure',
+ win, "-#{slot}"))
+ end
+ conf[0] = conf[0][1..-1]
+ conf
+ else
+ tk_split_simplelist(tk_send('window', 'configure',
+ win)).collect{|conflist|
+ conf = tk_split_simplelist(conflist)
+ conf[0] = conf[0][1..-1]
+ case conf[0]
+ when 'text', 'label', 'show', 'data', 'file'
+ else
+ if conf[3]
+ if conf[3].index('{')
+ conf[3] = tk_split_list(conf[3])
+ else
+ conf[3] = tk_tcl2ruby(conf[3])
+ end
+ end
+ if conf[4]
+ if conf[4].index('{')
+ conf[4] = tk_split_list(conf[4])
+ else
+ conf[4] = tk_tcl2ruby(conf[4])
+ end
+ end
+ end
+ conf
+ }
+ end
+ end
+
+ def window_names
+ tk_split_simplelist(tk_send('window', 'names')).collect{|elt|
+ tagid2obj(elt)
+ }
end
def _ktext_length(txt)
@@ -419,7 +633,7 @@ class TkText<TkTextWin
def dump(type_info, *index, &block)
args = type_info.collect{|inf| '-' + inf}
- args << '-command' << Proc.new(&block) if iterator?
+ args << '-command' << block if block
str = tk_send('dump', *(args + index))
result = []
sel = nil
@@ -468,7 +682,7 @@ class TkText<TkTextWin
result.push tk_tcl2ruby(val)
end
when 'tagoff'
- result.push tk_tcl2ruby(sel)
+ result.push tk_tcl2ruby(val)
when 'window'
result.push tk_tcl2ruby(val)
end
@@ -552,8 +766,10 @@ end
class TkTextTag<TkObject
include TkTreatTagFont
- TTagID_TBL = {}
- Tk_TextTag_ID = ['tag0000']
+ TTagID_TBL = TkCore::INTERP.create_table
+ Tk_TextTag_ID = ['tag', '00000']
+
+ TkCore::INTERP.init_ip_env{ TTagID_TBL.clear }
def TkTextTag.id2obj(text, id)
tpath = text.path
@@ -567,11 +783,11 @@ class TkTextTag<TkObject
end
@parent = @t = parent
@tpath = parent.path
- @path = @id = Tk_TextTag_ID[0]
+ @path = @id = Tk_TextTag_ID.join
TTagID_TBL[@id] = self
TTagID_TBL[@tpath] = {} unless TTagID_TBL[@tpath]
TTagID_TBL[@tpath][@id] = self
- Tk_TextTag_ID[0] = Tk_TextTag_ID[0].succ
+ Tk_TextTag_ID[1].succ!
#tk_call @t.path, "tag", "configure", @id, *hash_kv(keys)
if args != [] then
keys = args.pop
@@ -587,7 +803,7 @@ class TkTextTag<TkObject
end
def id
- return @id
+ @id
end
def first
@@ -600,10 +816,12 @@ class TkTextTag<TkObject
def add(*index)
tk_call @t.path, 'tag', 'add', @id, *index
+ self
end
def remove(*index)
tk_call @t.path, 'tag', 'remove', @id, *index
+ self
end
def ranges
@@ -616,11 +834,11 @@ class TkTextTag<TkObject
end
def nextrange(first, last=None)
- tk_split_simplelist(tk_call(@t.path, 'tag', 'nextrange', @id, first, last))
+ tk_split_list(tk_call(@t.path, 'tag', 'nextrange', @id, first, last))
end
def prevrange(first, last=None)
- tk_split_simplelist(tk_call(@t.path, 'tag', 'prevrange', @id, first, last))
+ tk_split_list(tk_call(@t.path, 'tag', 'prevrange', @id, first, last))
end
def [](key)
@@ -665,10 +883,17 @@ class TkTextTag<TkObject
def bind(seq, cmd=Proc.new, args=nil)
_bind([@t.path, 'tag', 'bind', @id], seq, cmd, args)
+ self
end
def bind_append(seq, cmd=Proc.new, args=nil)
_bind_append([@t.path, 'tag', 'bind', @id], seq, cmd, args)
+ self
+ end
+
+ def bind_remove(seq)
+ _bind_remove([@t.path, 'tag', 'bind', @id], seq)
+ self
end
def bindinfo(context=nil)
@@ -677,22 +902,36 @@ class TkTextTag<TkObject
def raise(above=None)
tk_call @t.path, 'tag', 'raise', @id, above
+ self
end
def lower(below=None)
tk_call @t.path, 'tag', 'lower', @id, below
+ self
end
def destroy
tk_call @t.path, 'tag', 'delete', @id
- TTagID_TBL[@tpath].delete(@id) if CTagID_TBL[@tpath]
+ TTagID_TBL[@tpath].delete(@id) if TTagID_TBL[@tpath]
+ self
end
end
class TkTextNamedTag<TkTextTag
def self.new(parent, name, *args)
if TTagID_TBL[parent.path] && TTagID_TBL[parent.path][name]
- return TTagID_TBL[parent.path][name]
+ tagobj = TTagID_TBL[parent.path][name]
+ if args != [] then
+ keys = args.pop
+ if keys.kind_of? Hash then
+ tagobj.add(*args) if args != []
+ tagobj.configure(keys)
+ else
+ args.push keys
+ tagobj.add(*args)
+ end
+ end
+ return tagobj
else
super(parent, name, *args)
end
@@ -702,52 +941,80 @@ class TkTextNamedTag<TkTextTag
if not parent.kind_of?(TkText)
fail format("%s need to be TkText", parent.inspect)
end
- @t = parent
+ @parent = @t = parent
@tpath = parent.path
@path = @id = name
TTagID_TBL[@tpath] = {} unless TTagID_TBL[@tpath]
- TTagID_TBL[@tpath][@id] = self
- if mode
- tk_call @t.path, "addtag", @id, *args
+ TTagID_TBL[@tpath][@id] = self unless TTagID_TBL[@tpath][@id]
+ #if mode
+ # tk_call @t.path, "addtag", @id, *args
+ #end
+ if args != [] then
+ keys = args.pop
+ if keys.kind_of? Hash then
+ add(*args) if args != []
+ configure(keys)
+ else
+ args.push keys
+ add(*args)
+ end
end
+ @t._addtag id, self
end
end
-class TkTextTagSel<TkTextTag
- def initialize(parent, keys=nil)
- if not parent.kind_of?(TkText)
- fail format("%s need to be TkText", parent.inspect)
- end
- @t = parent
- @path = @id = 'sel'
- #tk_call @t.path, "tag", "configure", @id, *hash_kv(keys)
- configure(keys) if keys
- @t._addtag id, self
+class TkTextTagSel<TkTextNamedTag
+ def self.new(parent, *args)
+ super(parent, 'sel', *args)
end
end
class TkTextMark<TkObject
- Tk_TextMark_ID = ['mark0000']
+ TMarkID_TBL = TkCore::INTERP.create_table
+ Tk_TextMark_ID = ['mark', '00000']
+
+ TkCore::INTERP.init_ip_env{ TMarkID_TBL.clear }
+
+ def TkTextMark.id2obj(text, id)
+ tpath = text.path
+ return id unless TMarkID_TBL[tpath]
+ TMarkID_TBL[tpath][id]? TMarkID_TBL[tpath][id]: id
+ end
+
def initialize(parent, index)
if not parent.kind_of?(TkText)
fail format("%s need to be TkText", parent.inspect)
end
- @t = parent
- @path = @id = Tk_TextMark_ID[0]
+ @parent = @t = parent
+ @tpath = parent.path
+ @path = @id = Tk_TextMark_ID.join
+ TMarkID_TBL[@id] = self
+ TMarkID_TBL[@tpath] = {} unless TMarkID_TBL[@tpath]
+ TMarkID_TBL[@tpath][@id] = self
Tk_TextMark_ID[0] = Tk_TextMark_ID[0].succ
tk_call @t.path, 'mark', 'set', @id, index
@t._addtag id, self
end
+
def id
- return @id
+ @id
+ end
+
+ def +(mod)
+ @id + ' + ' + mod
+ end
+ def -(mod)
+ @id + ' - ' + mod
end
def set(where)
tk_call @t.path, 'mark', 'set', @id, where
+ self
end
def unset
tk_call @t.path, 'mark', 'unset', @id
+ self
end
alias destroy unset
@@ -757,6 +1024,7 @@ class TkTextMark<TkObject
def gravity=(direction)
tk_call @t.path, 'mark', 'gravity', @id, direction
+ self
end
def next(index = nil)
@@ -776,39 +1044,45 @@ class TkTextMark<TkObject
end
end
-class TkTextMarkInsert<TkTextMark
- def initialize(parent, index=nil)
+class TkTextNamedMark<TkTextMark
+ def self.new(parent, name, *args)
+ if TMarkID_TBL[parent.path] && TMarkID_TBL[parent.path][name]
+ return TMarkID_TBL[parent.path][name]
+ else
+ super(parent, name, *args)
+ end
+ end
+
+ def initialize(parent, name, index=nil)
if not parent.kind_of?(TkText)
fail format("%s need to be TkText", parent.inspect)
end
- @t = parent
- @path = @id = 'insert'
+ @parent = @t = parent
+ @tpath = parent.path
+ @path = @id = name
+ TMarkID_TBL[@id] = self
+ TMarkID_TBL[@tpath] = {} unless TMarkID_TBL[@tpath]
+ TMarkID_TBL[@tpath][@id] = self unless TMarkID_TBL[@tpath][@id]
tk_call @t.path, 'mark', 'set', @id, index if index
@t._addtag id, self
end
end
+class TkTextMarkInsert<TkTextNamedMark
+ def self.new(parent,*args)
+ super(parent, 'insert', *args)
+ end
+end
+
class TkTextMarkCurrent<TkTextMark
- def initialize(parent,index=nil)
- if not parent.kind_of?(TkText)
- fail format("%s need to be TkText", parent.inspect)
- end
- @t = parent
- @path = @id = 'current'
- tk_call @t.path, 'mark', 'set', @id, index if index
- @t._addtag id, self
+ def self.new(parent,*args)
+ super(parent, 'current', *args)
end
end
class TkTextMarkAnchor<TkTextMark
- def initialize(parent,index=nil)
- if not parent.kind_of?(TkText)
- fail format("%s need to be TkText", parent.inspect)
- end
- @t = parent
- @path = @id = 'anchor'
- tk_call @t.path, 'mark', 'set', @id, index if index
- @t._addtag id, self
+ def self.new(parent,*args)
+ super(parent, 'anchor', *args)
end
end
@@ -850,12 +1124,7 @@ class TkTextWindow<TkObject
end
def cget(slot)
- case slot.to_s
- when 'text', 'label', 'show', 'data', 'file'
- tk_call @t.path, 'window', 'cget', @index, "-#{slot}"
- else
- tk_tcl2ruby tk_call(@t.path, 'window', 'cget', @index, "-#{slot}")
- end
+ @t.window_cget(@index, slot)
end
def configure(slot, value=None)
@@ -867,16 +1136,21 @@ class TkTextWindow<TkObject
slot['create']=nil
end
if slot.size > 0
- tk_call @t.path, 'window', 'configure', @index, *hash_kv(slot)
+ tk_call(@t.path, 'window', 'configure', @index, *hash_kv(slot))
end
else
@id = value if slot == 'window' || slot == :window
if slot == 'create' || slot == :create
self.create=value
else
- tk_call @t.path, 'window', 'configure', @index, "-#{slot}", value
+ tk_call(@t.path, 'window', 'configure', @index, "-#{slot}", value)
end
end
+ self
+ end
+
+ def configinfo(slot = nil)
+ @t.window_configinfo(@index, slot)
end
def window
@@ -899,47 +1173,6 @@ class TkTextWindow<TkObject
end
tk_call @t.path, 'window', 'configure', @index, '-create', value
end
-
- def configinfo(slot = nil)
- if slot
- case slot.to_s
- when 'text', 'label', 'show', 'data', 'file'
- conf = tk_split_simplelist(tk_call(@t.path, 'window', 'configure',
- @index, "-#{slot}"))
- else
- conf = tk_split_list(tk_call(@t.path, 'window', 'configure',
- @index, "-#{slot}"))
- end
- conf[0] = conf[0][1..-1]
- conf
- else
- tk_split_simplelist(tk_call(@t.path, 'window', 'configure',
- @index)).collect{|conflist|
- conf = tk_split_simplelist(conflist)
- conf[0] = conf[0][1..-1]
- case conf[0]
- when 'text', 'label', 'show', 'data', 'file'
- else
- if conf[3]
- if conf[3].index('{')
- conf[3] = tk_split_list(conf[3])
- else
- conf[3] = tk_tcl2ruby(conf[3])
- end
- end
- if conf[4]
- if conf[4].index('{')
- conf[4] = tk_split_list(conf[4])
- else
- conf[4] = tk_tcl2ruby(conf[4])
- end
- end
- end
- conf
- }
- end
- end
-
end
class TkTextImage<TkObject
@@ -972,70 +1205,28 @@ class TkTextImage<TkObject
end
def cget(slot)
- case slot.to_s
- when 'text', 'label', 'show', 'data', 'file'
- tk_call @t.path, 'image', 'cget', @index, "-#{slot}"
- else
- tk_tcl2ruby tk_call(@t.path, 'image', 'cget', @index, "-#{slot}")
- end
+ @t.image_cget(@index, slot)
end
def configure(slot, value=None)
- if slot.kind_of? Hash
- tk_call @t.path, 'image', 'configure', @index, *hash_kv(slot)
- else
- tk_call @t.path, 'image', 'configure', @index, "-#{slot}", value
- end
+ @t.image_configure(@index, slot, value)
+ self
end
# def configure(slot, value)
# tk_call @t.path, 'image', 'configure', @index, "-#{slot}", value
# end
+ def configinfo(slot = nil)
+ @t.image_configinfo(@index, slot)
+ end
+
def image
- tk_call @t.path, 'image', 'configure', @index, '-image'
+ img = tk_call(@t.path, 'image', 'configure', @index, '-image')
+ TkImage::Tk_IMGTBL[img]? TkImage::Tk_IMGTBL[img] : img
end
def image=(value)
tk_call @t.path, 'image', 'configure', @index, '-image', value
- end
-
- def configinfo(slot = nil)
- if slot
- case slot.to_s
- when 'text', 'label', 'show', 'data', 'file'
- conf = tk_split_simplelist(tk_call(@t.path, 'image', 'configure',
- @index, "-#{slot}"))
- else
- conf = tk_split_list(tk_call(@t.path, 'image', 'configure',
- @index, "-#{slot}"))
- end
- conf[0] = conf[0][1..-1]
- conf
- else
- tk_split_simplelist(tk_call(@t.path, 'image', 'configure',
- @index)).collect{|conflist|
- conf = tk_split_simplelist(conflist)
- conf[0] = conf[0][1..-1]
- case conf[0]
- when 'text', 'label', 'show', 'data', 'file'
- else
- if conf[3]
- if conf[3].index('{')
- conf[3] = tk_split_list(conf[3])
- else
- conf[3] = tk_tcl2ruby(conf[3])
- end
- end
- if conf[4]
- if conf[4].index('{')
- conf[4] = tk_split_list(conf[4])
- else
- conf[4] = tk_tcl2ruby(conf[4])
- end
- end
- end
- conf
- }
- end
+ self
end
end
diff --git a/ext/tk/lib/tkvirtevent.rb b/ext/tk/lib/tkvirtevent.rb
index f5dcdc9429..c594d4716f 100644
--- a/ext/tk/lib/tkvirtevent.rb
+++ b/ext/tk/lib/tkvirtevent.rb
@@ -7,8 +7,12 @@ require 'tk'
class TkVirtualEvent<TkObject
extend Tk
- TkVirtualEventID = [0]
- TkVirtualEventTBL = {}
+ TkCommandNames = ['event'.freeze].freeze
+
+ TkVirtualEventID = ["<VirtEvent".freeze, "00000", ">".freeze]
+ TkVirtualEventTBL = TkCore::INTERP.create_table
+
+ TkCore::INTERP.init_ip_env{ TkVirtualEventTBL.clear }
class PreDefVirtEvent<self
def initialize(event)
@@ -37,8 +41,8 @@ class TkVirtualEvent<TkObject
end
def initialize(*sequences)
- @path = @id = format("<VirtEvent%.4d>", TkVirtualEventID[0])
- TkVirtualEventID[0] += 1
+ @path = @id = TkVirtualEventID.join
+ TkVirtualEventID[1].succ!
add(*sequences)
end
diff --git a/ext/tk/lib/tkwinpkg.rb b/ext/tk/lib/tkwinpkg.rb
new file mode 100644
index 0000000000..7762b3a596
--- /dev/null
+++ b/ext/tk/lib/tkwinpkg.rb
@@ -0,0 +1,84 @@
+#
+# tkwinpkg.rb : methods for Tcl/Tk packages for Microsoft Windows
+# 2000/11/22 by Hidetoshi Nagai <nagai@ai.kyutech.ac.jp>
+#
+# ATTENTION !!
+# This is NOT TESTED. Because I have no test-environment.
+#
+#
+require 'tk'
+
+module TkWinDDE
+ extend Tk
+ extend TkWinDDE
+
+ TkCommandNames = ['dde'.freeze].freeze
+
+ tk_call('package', 'require', 'dde')
+
+ def servername(topic=nil)
+ tk_call('dde', 'servername', topic)
+ end
+
+ def execute(service, topic, data)
+ tk_call('dde', 'execute', service, topic, data)
+ end
+
+ def async_execute(service, topic, data)
+ tk_call('dde', '-async', 'execute', service, topic, data)
+ end
+
+ def poke(service, topic, item, data)
+ tk_call('dde', 'poke', service, topic, item, data)
+ end
+
+ def request(service, topic, item)
+ tk_call('dde', 'request', service, topic, item)
+ end
+
+ def services(service, topic)
+ tk_call('dde', 'services', service, topic)
+ end
+
+ def eval(topic, cmd, *args)
+ tk_call('dde', 'eval', topic, cmd, *args)
+ end
+
+ module_function :servername, :execute, :async_execute,
+ :poke, :request, :services, :eval
+end
+
+module TkWinRegistry
+ extend Tk
+ extend TkWinRegistry
+
+ TkCommandNames = ['registry'.freeze].freeze
+
+ tk_call('package', 'require', 'registry')
+
+ def delete(keynam, valnam=nil)
+ tk_call('registry', 'delete', keynam, valnam)
+ end
+
+ def get(keynam, valnam)
+ tk_call('registry', 'get', keynam, valnam)
+ end
+
+ def keys(keynam)
+ tk_split_simplelist(tk_call('registry', 'keys', keynam))
+ end
+
+ def set(keynam, valnam=nil, data=nil, dattype=nil)
+ tk_call('registry', 'set', keynam, valnam, data, dattype)
+ end
+
+ def type(keynam, valnam)
+ tk_call('registry', 'type', keynam, valnam)
+ end
+
+ def values(keynam)
+ tk_split_simplelist(tk_call('registry', 'values', keynam))
+ end
+
+ module_function :delete, :get, :keys, :set, :type, :values
+end
diff --git a/ext/tk/sample/demos-en/ChangeLog b/ext/tk/sample/demos-en/ChangeLog
new file mode 100644
index 0000000000..c3f66cf409
--- /dev/null
+++ b/ext/tk/sample/demos-en/ChangeLog
@@ -0,0 +1,64 @@
+2002-08-29 16:30 matt
+
+ * ChangeLog: ChangeLog is auto-generated *from* CVS log.
+
+2002-08-29 16:27 matt
+
+ * ChangeLog.prev: [no log message]
+
+2002-08-28 18:07 matt
+
+ * browse1, browse2, hello, ixset, rmt, rolodex, rolodex-j, square,
+ tcolor, timer, widget: Changed #! lines to the slightly more
+ portable '#!/usr/bin/env ruby'.
+
+2002-08-28 17:56 matt
+
+ * icon.rb, items.rb, label.rb, menu.rb, ruler.rb: Changed bitmap
+ file extensions from .bmp to .xbm.
+
+2002-08-28 17:55 matt
+
+ * images/: face.bmp, face.xbm, flagdown.bmp, flagdown.xbm,
+ flagup.bmp, flagup.xbm, gray25.bmp, gray25.xbm, letters.bmp,
+ letters.xbm, noletter.bmp, noletter.xbm, pattern.bmp, pattern.xbm:
+ Changed X bitmap file extensions from .bmp to the more intuitive
+ .xbm.
+
+2002-08-28 17:35 matt
+
+ * bitmap.rb, colors.rb, cscroll.rb, ctext.rb, hello, ixset,
+ menubu.rb, patch_1.1c1, rmt, style.rb, timer, ChangeLog, README,
+ README.tkencoding, arrow.rb, bind.rb, browse1, browse2, button.rb,
+ check.rb, clrpick.rb, dialog1.rb, dialog2.rb, entry1.rb, entry2.rb,
+ filebox.rb, floor.rb, form.rb, hscale.rb, icon.rb, image1.rb,
+ image2.rb, items.rb, label.rb, menu.rb, msgbox.rb, plot.rb,
+ puzzle.rb, radio.rb, rolodex, rolodex-j, ruler.rb, sayings.rb,
+ search.rb, square, states.rb, tcolor, text.rb, tkencoding.rb,
+ twind.rb, vscale.rb, widget, doc.org/README, doc.org/README.JP,
+ doc.org/README.tk80, doc.org/license.terms,
+ doc.org/license.terms.tk80, images/earth.gif, images/earthris.gif,
+ images/face.bmp, images/flagdown.bmp, images/flagup.bmp,
+ images/gray25.bmp, images/grey.25, images/grey.5,
+ images/letters.bmp, images/noletter.bmp, images/pattern.bmp,
+ images/tcllogo.gif, images/teapot.ppm: Initial revision
+
+2002-08-28 17:35 matt
+
+ * bitmap.rb, colors.rb, cscroll.rb, ctext.rb, hello, ixset,
+ menubu.rb, patch_1.1c1, rmt, style.rb, timer, ChangeLog, README,
+ README.tkencoding, arrow.rb, bind.rb, browse1, browse2, button.rb,
+ check.rb, clrpick.rb, dialog1.rb, dialog2.rb, entry1.rb, entry2.rb,
+ filebox.rb, floor.rb, form.rb, hscale.rb, icon.rb, image1.rb,
+ image2.rb, items.rb, label.rb, menu.rb, msgbox.rb, plot.rb,
+ puzzle.rb, radio.rb, rolodex, rolodex-j, ruler.rb, sayings.rb,
+ search.rb, square, states.rb, tcolor, text.rb, tkencoding.rb,
+ twind.rb, vscale.rb, widget, doc.org/README, doc.org/README.JP,
+ doc.org/README.tk80, doc.org/license.terms,
+ doc.org/license.terms.tk80, images/earth.gif, images/earthris.gif,
+ images/face.bmp, images/flagdown.bmp, images/flagup.bmp,
+ images/gray25.bmp, images/grey.25, images/grey.5,
+ images/letters.bmp, images/noletter.bmp, images/pattern.bmp,
+ images/tcllogo.gif, images/teapot.ppm: Taking over demo package
+ from Jonathan Conway.
+
diff --git a/ext/tk/sample/demos-en/ChangeLog.prev b/ext/tk/sample/demos-en/ChangeLog.prev
new file mode 100644
index 0000000000..536ce30abe
--- /dev/null
+++ b/ext/tk/sample/demos-en/ChangeLog.prev
@@ -0,0 +1,9 @@
+2001-07-26 <rise@leannan.knavery.net>
+
+ * Moved files to directory ruby-tk81-demos-english in tarball.
+
+2001-07-26 <rise@knavery.net>
+
+ * Added test to widget and hello versus Tk::TCL_VERSION & Tk::JAPANIZED_TK (per Guy Decoux in [ruby-talk:18559]) before requiring tkencoding.rb.
+
+ \ No newline at end of file
diff --git a/ext/tk/sample/demos-en/README b/ext/tk/sample/demos-en/README
new file mode 100644
index 0000000000..2908aa38e4
--- /dev/null
+++ b/ext/tk/sample/demos-en/README
@@ -0,0 +1,138 @@
+Current Maintainer:
+ Jonathan Conway
+ rise@knavery.net
+
+ Please direct all bug reports/requests/suggestions to the above
+ address.
+
+
+Notes:
+
+* The files hello and widget have been changed to test Tk::TCL_VERSION
+ and Tk::JAPANIZED_TK before requiring tkencoding.rb to prevent an
+ infinite loop. This test was taken from a message in
+ [ruby-talk:18559] by Guy Decoux.
+
+* The .bmp files in the images directory are X bitmaps (i.e. XBM to many
+ graphics packages), not Windows bitmaps (.bmp). You will not be
+ able to use images exported by a graphics program as Windows
+ bitmaps with this demo collection nor will you be able to edit the
+ included images without setting the file type correctly.
+
+-- Jonathan Conway, 2001-07-26
+
+
+#------------------------------------------------------------------------------
+# ==== Introduction. ====
+#
+# To create this version of the Ruby/Tk widget demo, I took the
+# ruby-tk81-demos and removed all the Kanji strings and comments. I
+# have tried to restore the original English strings and comments
+# using the Tcl/Tk8.2.2 version of the widget demo.
+#
+# When I tried running the Kanji version, all I got was a mostly blank
+# panel with a non-functional "File" button. I disovered that if all
+# non-ASCII characters were replaced with blanks, then I could get the
+# gutted stuff running.
+#
+# Since English Ruby/Tk documentation is lacking and I needed this
+# code to see how it worked and to use as the basis of my try-it
+# prototype (The Ruby Yielding Interactive Toolkit), plus the fact
+# that no help was forthcoming for making the Kanji version work (plus
+# the fact that I can't read Kanji anyway), I decided to embark on
+# this English restoration project.
+#
+# Thanks to everyone who worked on the original Ruby/Tk widget demo
+# (and the preceding Tcl/Tk version for that matter). The
+# comparatively simple task of changing text strings and comments has
+# made me appreciate the great amount of original work that went into
+# this.
+#
+# -- Conrad Schneiker, 2000-07-23.
+#------------------------------------------------------------------------------
+
+#------------------------------------------------------------------------------
+# ==== Known bugs. ====
+#
+# ^C-ing the demo gives Ruby interrupt and stack message; clean exit needed.
+# Font settings don't work correctly.
+# Dismissing the embedded windows demo (tkwind.rb) kills the widget demo.
+# Rerunning the canvas item demo from the code widow get errors.
+# The "press me" button in the canvas item demo doesn't time out.
+# The simple 2 d plot starts up extremely slow compared to the Tcl version.
+# The first item message on the icon menu on the menu and cascades demo doesn't work.
+#------------------------------------------------------------------------------
+
+
+###################### Original README ########################################
+
+ Ruby/Tk81 widget-demo 1999/08/13
+
+ ËÌΦÀèüÂç¾ðÊó²Ê³Ø¸¦µæ²Ê
+ ΩÀÐ <ttate@jaist.ac.jp>
+
+Tcl/Tk8.1¤Ç¤Ïunicode(UTF8)¤¬ºÎÍѤµ¤ì¡¢unicode¤ËÊÑ´¹¤µ¤ì¤¿ÆüËܸì¤Ç¤¢¤ì¤Ð
+Widget¤Ëɽ¼¨¤µ¤»¤ë¤³¤È¤¬²Äǽ¤Ç¤¹¡£ËÜ¥¢¡¼¥«¥¤¥Ö¤Ë¤Ï°ÊÁ°¤Ë±Ê°æ¤µ¤ó¤¬Ãæ¿´¤È
+¤Ê¤é¤ì¤ÆºîÀ®¤µ¤ì¤¿Ruby/Tk¤Î¥µ¥ó¥×¥ë¤Ë½¤Àµ¤ò²Ã¤¨¡¢°Ê²¼¤Î´Ä¶­¤Ë¤ª¤¤¤Æ¤Çưºî
+¤ò³Îǧ¤·¤¿¤â¤Î¤Ç¤¹¡£
+
+* ruby-1.3.7,ruby-1.4.0
+* tcl8.1, tk8.1
+* linux-2.2
+
+Windows(Cygwin)¤Ë¤ª¤¤¤Æ¡¢¥µ¥ó¥×¥ë¤òư¤«¤¹¤Ë¤Ïruby¤ò-Ke¤È¤¤¤¦¥ª¥×¥·¥ç¥ó¤òÉÕ
+¤±¤Æµ¯Æ°¤µ¤»¤ë¤ÈÎɤ¤¤½¤¦¤Ç¤¹¡£
+ ¾ðÊóÄó¶¡¡§ÅÏÊÕ¤µ¤ó
+ <eban@os.rim.or.jp>
+
+----------------------------------------------------------------------------
+ Ruby/Tk widget-demo
+ version 1.1 ( 1998/07/24 )
+ ±Ê°æ¡÷ÃÎǽ¡¥¶å¹©Âç (nagai@ai.kyutech.ac.jp)
+
+ɸ½àÇÛÉۤΠTcl/Tk ³ÈÄ¥¥Ñ¥Ã¥±¡¼¥¸¤ò¼è¤ê¹þ¤ó¤À Ruby (°Ê²¼ Ruby/Tk ¤È¸Æ¤Ó¤Þ¤¹)
+¤Ç¤Ï¡¤Tk widget ¤òÍѤ¤¤¿ GUI ¤ÎºîÀ®¤ò¹Ô¤¦¤³¤È¤¬¤Ç¤­¤Þ¤¹¡¥¼ÂºÝ¤Ë GUI ¤òºîÀ®
+¤·¤Æ¤¤¤¯¾ì¹ç¤Ë¤ÏÍÍ¡¹¤Ê¼ÂÎ㤬¥µ¥ó¥×¥ë¤È¤·¤ÆÂ¸ºß¤¹¤ë¤ÈÊØÍø¤Ê¤Î¤Ç¤¹¤¬¡¤Ruby/Tk
+¤Ë¤Ï¤½¤Î¤è¤¦¤ÊŬÅö¤Ê¥µ¥ó¥×¥ë¥¹¥¯¥ê¥×¥È½¸¹ç¤Ï¸ºß¤·¤Þ¤»¤ó¤Ç¤·¤¿¡¥¤½¤ì¤ËÂФ·¡¤
+³ÈÄ¥¥Ñ¥Ã¥±¡¼¥¸¤Î¸µ¤Ç¤¢¤ë Tcl/Tk ¤Ë¤Ï¡¤Tk widget ¤òÍѤ¤¤Æ¤É¤Î¤è¤¦¤Ê¤³¤È¤¬¤Ç
+¤­¤ë¤«¤ò¼¨¤¹¤â¤Î¤È¤·¤Æ widget-demo ¤¬Â¸ºß¤·¤ª¤ê¡¤Tcl/Tk ¤òÍѤ¤¤¿ GUI ¤ÎºîÀ®
+¤ò½¬ÆÀ¤¹¤ëºÝ¤ÎÂåɽŪ¥µ¥ó¥×¥ë¤È¤Ê¤Ã¤Æ¤¤¤Þ¤¹¡¥ËÜ¥¢¡¼¥«¥¤¥Ö¤Ï¡¤Ruby/Tk ¤Î½¬ÆÀ¤Î
+¤¿¤á¤ÎÂåɽŪ¤Ê¥µ¥ó¥×¥ë¥¹¥¯¥ê¥×¥È¤È¤¹¤Ù¤¯¡¤Tcl/Tk ¤Î widget-demo ¤ò°Ü¿¢¤·¤¿¤â
+¤Î¤Ç¤¹¡¥
+
+ËÜ¥¢¡¼¥«¥¤¥Ö¤Ë´Þ¤Þ¤ì¤ë¥¹¥¯¥ê¥×¥È¤ò¼Â¹Ô¤¹¤ë¤¿¤á¤Ë¤Ï¡¤ruby-1.1c2 °Ê¾å¤Ç¤¢¤ë¤³
+¤È¤¬É¬ÍפǤ¹¡¥1.1c1 ¤Î¾ì¹ç¤Ï¡¤ËÜ¥¢¡¼¥«¥¤¥Ö¤Ë´Þ¤Þ¤ì¤ë patck_1.1c1 ¤ò Ruby ¤Î
+¥é¥¤¥Ö¥é¥ê¤Ë¤¢¤Æ¤Æ¤¯¤À¤µ¤¤¡¥ÁȤ߹þ¤à Tk ¤Î¥Ð¡¼¥¸¥ç¥ó¤Ï¡¤4.2 ¤Ç¤â 8.0 ¤Ç¤â½¤
+Àµ¤Ê¤¯Æ°¤«¤»¤ë¤Ï¤º¤Ç¤¹¡¥¤¿¤À¤·¡¤ÆüËܸìÈǤǤΰܿ¢¤È¤Ê¤Ã¤Æ¤¤¤ë¤¿¤á¡¤ÆüËܸ첽¤µ
+¤ì¤¿ Tk ¤ò¤´ÍøÍѤ¯¤À¤µ¤¤¡¥¥¹¥¯¥ê¥×¥È¤Î¥Æ¥¹¥È¤Ï¡¤Tk4.2jp ¤È Tk8.0jp ¤Î¾å¤Ç¹Ô
+¤¤¤Þ¤·¤¿ (´°àú¤Ë¤Ç¤Ï¤Ê¤¤¤Ç¤¹¤¬)¡¥
+
+ËÜ¥¢¡¼¥«¥¤¥Ö¤Ë´Þ¤Þ¤ì¤ë¥¹¥¯¥ê¥×¥È¤Î¿¤¯¤Ï¡¤¸µ¤È¤Ê¤Ã¤Æ¤¤¤ë Tcl/Tk ÈǤËÈæ³ÓŪ¶á
+¤¤¥¹¥¯¥ê¥×¥Èµ­½Ò¤È¤Ê¤ë¤è¤¦¤Ë¤·¤Æ¤¤¤Þ¤¹¡¥¤½¤Î¤¿¤á¡¤Ruby/Tk ¤Î¥µ¥ó¥×¥ë¤È¸À¤¦¤Ë
+¤Ï¡¤¤¢¤Þ¤ê Ruby ¤é¤·¤¯¤Ê¤¤¤È¤â¸À¤¨¤ë¤Ç¤·¤ç¤¦¡¥¤Ë¤â¤«¤«¤ï¤é¤º¡¤¤½¤Î¤è¤¦¤Êµ­½Ò
+¤ò¼è¤Ã¤Æ¤¤¤ëÍýͳ¤Ï¡¤Ruby/Tk ¤Î¥É¥­¥å¥á¥ó¥ÈÉÔ­¤Ë¤¢¤ê¤Þ¤¹¡¥
+
+Tcl/Tk ¤Ë¤ÏŬÅö¤Ê»²¹Í½ñ¤¬²¿ºý¤«Â¸ºß¤·¤Æ¤¤¤Þ¤¹¤«¤é¡¤Ruby/Tk ¥¹¥¯¥ê¥×¥È¤òºîÀ®
+¤¹¤ëºÝ¤Ï¡¤¤½¤Î¤è¤¦¤Ê Tcl/Tk ¤Î»²¹Í½ñ¤Ç¾ðÊó¤òÊ䤤¤Ê¤¬¤éºîÀ®¤¹¤ë¤³¤È¤Ë¤Ê¤ë¤È»×
+¤¤¤Þ¤¹¡¥³Æ widget ¤Î»ÈÍÑÎã¤È¤·¤Æ¡¤Tcl/Tk ¤Î widget-demo ¤ò»²¾È¤¹¤ë¤³¤È¤â¤¢¤ë
+¤Ç¤·¤ç¤¦¡¥Ruby/Tk ÈǤε­½Ò¤ò widget-demo ¤ò Tcl/Tk ÈǤε­½Ò¤Ë¶á¤¤¤â¤Î¤Ë¤·¤Æ
+¤ª¤±¤Ð¡¤¤½¤ÎÂÐÈæ¤Ë¤è¤Ã¤Æ¡¤Ruby/Tk ¤ÎÍý²ò¤òÁá¤á¤ë¤³¤È¤¬¤Ç¤­¤ë¤È¹Í¤¨¤é¤ì¤Þ¤¹¡¥
+°ìö Ruby/Tk ¤Ç¤Î ³Æ widget ¤Î»ÈÍÑÊýË¡¤ò½¬ÆÀ¤·¤Æ¤·¤Þ¤¨¤Ð¡¤Ruby ¤é¤·¤¤¥¹¥¯¥ê
+¥×¥È¤òºîÀ®¤¹¤ë¤³¤È¤ÏÆñ¤·¤¯¤Ê¤¤¤Ç¤·¤ç¤¦¡¥ËÜ¥¢¡¼¥«¥¤¥Ö¤Î¥¹¥¯¥ê¥×¥È¤Ï¡¤Ruby/Tk
+¤òºÇ½é¤Ë½¬ÆÀ¤¹¤ë¤Þ¤Ç¤ÎÆ§Âæ¤È¤·¤ÆÍøÍѤ·¤Æ¤¤¤¿¤À¤±¤ì¤Ð¹¬¤¤¤Ç¤¹¡¥
+
+widget-demo ¤Î°Ü¿¢¤Ë¤¢¤¿¤Ã¤Æ¤Ï¡¤¼¡¤ÎÊý¤Ë¤â°Ü¿¢¤·¤¿¥¹¥¯¥ê¥×¥È¤òÄ󶡤·¤Æ¤¤¤¿¤À
+¤­¤Þ¤·¤¿¡¥¤³¤³¤Ë´¶¼Õ¤Î°Õ¤òɽ¤·¤Þ¤¹¡¥
+
+ ΩÀС÷JAIST (ttate@jaist.ac.jp) ¤µ¤ó
+ Ê¿¾¾¾Í»Ë (hiramatu@cdrom.co.jp) ¤µ¤ó
+
+Ê¿¾¾¤µ¤ó¤Ë¤è¤ë Ruby/Tk ÆþÌç¤Î Web page (http://www.cdrom.co.jp/~hiramatu/)
+¤â Ruby/Tk ¤Î½¬ÆÀ¤ËÍ­ÍѤȻפ¨¤Þ¤¹¤Î¤Ç¡¤¤¼¤Ò¤´»²¾È¤¯¤À¤µ¤¤¡¥
+
+¤Þ¤¿¡¤Á°¶¶ (maebashi@iij.ad.jp) ¤µ¤ó¤ò¤Ï¤¸¤á¤È¤·¤Æ¡¤widget-demo ¤Î°Ü¿¢¤ËºÝ¤·
+¤ÆÉ¬ÍפȤʤä¿ Ruby ¤Î Tk ´ØÏ¢¥é¥¤¥Ö¥é¥ê½¤Àµ¤Ë¤Ä¤¤¤Æ¡¤ÌäÂêÅÀ¡¤¥Ð¥°¤Î»ØÅ¦¤ò¤·
+¤Æ¤¤¤¿¤À¤¤¤¿Êý¡¹¤Ë¤â´¶¼ÕÃפ·¤Þ¤¹¡¥
+
+¤½¤·¤ÆºÇ¸å¤ËºÇÂç¤Î´¶¼Õ¤ò Ruby À߷׼ԤΠ¤Þ¤Ä¤â¤È ¤æ¤­¤Ò¤í (matz@netlab.co.jp)
+¤µ¤ó¤ËÊû¤²¤¿¤¤¤È»×¤¤¤Þ¤¹¡¥
diff --git a/ext/tk/sample/demos-en/README.tkencoding b/ext/tk/sample/demos-en/README.tkencoding
new file mode 100644
index 0000000000..8fcb494c03
--- /dev/null
+++ b/ext/tk/sample/demos-en/README.tkencoding
@@ -0,0 +1,24 @@
+tkencoding.rb¤òÍѤ¤¤¿ÆüËܸì¤Îɽ¼¨¤Ë¤Ä¤¤¤Æ
+
+Copyright (C) 1999/07, Takaaki Tateishi <ttate@jaist.ac.jp>
+
+
+1. tkencoding.rb¤È¤Ï¡©
+
+tkencoding.rb¤ÏTcl/Tk8.1¤òÍøÍѤ·¤¿Ruby/Tk¤Î¤¿¤á¤Î¥é¥¤¥Ö¥é¥ê
+¤Ç¤¹¡£tkencoding.rb¤òrequire¤¹¤ë¤³¤È¤Ë¤è¤Ã¤ÆWedget¤Ëɽ¼¨¤µ
+¤ì¤ë¥Æ¥­¥¹¥È¤ÏÁ´¤Æunicode(UTF8)¤ØÊÑ´¹¤µ¤ì¤Þ¤¹¡£
+
+
+2. »È¤¤Êý
+
+tkencoding.rb¤òrequire¤·¤ÆTk.encoding¤Ç»ÈÍѤ·¤Æ¤¤¤ëʸ»ú¥³¡¼¥É
+¤ò»ØÄꤷ¤Æ²¼¤µ¤¤¡£Î㤨¤Ð°Ê²¼¤Î¤è¤¦¤Ê´¶¤¸¤Ë¤Ê¤ê¤Þ¤¹¡£
+
+----
+require 'tk'
+require 'tkencoding'
+
+Tk.encoding = "euc-jp"
+# Tk.encoding = "shiftjis"
+---
diff --git a/ext/tk/sample/demos-en/arrow.rb b/ext/tk/sample/demos-en/arrow.rb
new file mode 100644
index 0000000000..8ee13254ea
--- /dev/null
+++ b/ext/tk/sample/demos-en/arrow.rb
@@ -0,0 +1,239 @@
+# arrow.rb
+#
+# This demonstration script creates a canvas widget that displays a
+# large line with an arrowhead whose shape can be edited interactively.
+#
+# arrowhead widget demo (called by 'widget')
+#
+
+# arrowSetup --
+# This method regenerates all the text and graphics in the canvas
+# window. It's called when the canvas is initially created, and also
+# whenever any of the parameters of the arrow head are changed
+# interactively.
+#
+# Arguments:
+# c - Name of the canvas widget.
+
+def arrowSetup(c)
+ v = $demo_arrowInfo
+
+ # Remember the current box, if there is one.
+ tags = c.gettags('current')
+ if tags != []
+ cur = tags.find{|t| t.kind_of?(String) && t =~ /^box[1-3]$/ }
+ else
+ cur = nil
+ end
+
+ # Create the arrow and outline.
+ c.delete('all')
+ TkcLine.new(c, v.x1, v.y, v.x2, v.y,
+ { 'width'=>10 * v.width,
+ 'arrowshape'=>[10*v.a, 10*v.b, 10*v.c],
+ 'arrow'=>'last'
+ }.update(v.bigLineStyle) )
+ xtip = v.x2 - 10*v.b
+ deltaY = 10*v.c + 5*v.width
+ TkcLine.new(c, v.x2, v.y, xtip, v.y + deltaY,
+ v.x2 - 10*v.a, v.y, xtip, v.y - deltaY, v.x2, v.y,
+ 'width'=>2, 'capstyle'=>'round', 'joinstyle'=>'round')
+
+ # Create the boxes for reshaping the line and arrowhead.
+ TkcRectangle.new(c, v.x2-10*v.a-5, v.y-5, v.x2-10*v.a+5, v.y+5,
+ {'tags'=>['box1', $arrowTag_box]}.update(v.boxStyle) )
+ TkcRectangle.new(c, xtip-5, v.y-deltaY-5, xtip+5, v.y-deltaY+5,
+ {'tags'=>['box2', $arrowTag_box]}.update(v.boxStyle) )
+ TkcRectangle.new(c, v.x1-5, v.y-5*v.width-5, v.x1+5, v.y-5*v.width+5,
+ {'tags'=>['box3', $arrowTag_box]}.update(v.boxStyle) )
+ c.itemconfigure cur, v.activeStyle if cur
+
+ # Create three arrows in actual size with the same parameters
+ TkcLine.new(c, v.x2+50, 0, v.x2+50, 1000, 'width'=>2)
+ tmp = v.x2+100
+ TkcLine.new(c, tmp, v.y-125, tmp, v.y-75, 'width'=>v.width,
+ 'arrow'=>'both', 'arrowshape'=>[v.a, v.b, v.c])
+ TkcLine.new(c, tmp-25, v.y, tmp+25, v.y, 'width'=>v.width,
+ 'arrow'=>'both', 'arrowshape'=>[v.a, v.b, v.c])
+ TkcLine.new(c, tmp-25, v.y+75, tmp+25, v.y+125, 'width'=>v.width,
+ 'arrow'=>'both', 'arrowshape'=>[v.a, v.b, v.c])
+
+ # Create a bunch of other arrows and text items showing the
+ # current dimensions.
+ tmp = v.x2+10
+ TkcLine.new(c, tmp, v.y-5*v.width, tmp, v.y-deltaY,
+ 'arrow'=>'both', 'arrowshape'=>v.smallTips)
+ TkcText.new(c, v.x2+15, v.y-deltaY+5*v.c, 'text'=>v.c, 'anchor'=>'w')
+ tmp = v.x1-10
+ TkcLine.new(c, tmp, v.y-5*v.width, tmp, v.y+5*v.width,
+ 'arrow'=>'both', 'arrowshape'=>v.smallTips)
+ TkcText.new(c, v.x1-15, v.y, 'text'=>v.width, 'anchor'=>'e')
+ tmp = v.y+5*v.width+10*v.c+10
+ TkcLine.new(c, v.x2-10*v.a, tmp, v.x2, tmp,
+ 'arrow'=>'both', 'arrowshape'=>v.smallTips)
+ TkcText.new(c, v.x2-5*v.a, tmp+5, 'text'=>v.a, 'anchor'=>'n')
+ tmp = tmp+25
+ TkcLine.new(c, v.x2-10*v.b, tmp, v.x2, tmp,
+ 'arrow'=>'both', 'arrowshape'=>v.smallTips)
+ TkcText.new(c, v.x2-5*v.b, tmp+5, 'text'=>v.b, 'anchor'=>'n')
+
+ TkcText.new(c, v.x1, 310, 'text'=>"'width'=>#{v.width}", 'anchor'=>'w',
+ 'font'=>'-*-Helvetica-Medium-R-Normal--*-180-*-*-*-*-*-*')
+ TkcText.new(c, v.x1, 330,
+ 'text'=>"'arrowshape'=>[#{v.a}, #{v.b}, #{v.c}]", 'anchor'=>'w',
+ 'font'=>'-*-Helvetica-Medium-R-Normal--*-180-*-*-*-*-*-*')
+
+ v.count += 1
+end
+
+# toplevel widget
+if defined?($arrow_demo) && $arrow_demo
+ $arrow_demo.destroy
+ $arrow_demo = nil
+end
+
+# demo toplevel widget
+$arrow_demo = TkToplevel.new {|w|
+ title("Arrowhead Editor Demonstration")
+ iconname("arrow")
+ positionWindow(w)
+}
+
+# label
+TkLabel.new($arrow_demo, 'font'=>$font, 'wraplength'=>'5i', 'justify'=>'left',
+ 'text'=>"This widget allows you to experiment with different widths and arrowhead shapes for lines in canvases. To change the line width or the shape of the arrowhead, drag any of the three boxes attached to the oversized arrow. The arrows on the right give examples at normal scale. The text at the bottom shows the configuration options as you'd enter them for a canvas line item."){
+ pack('side'=>'top')
+}
+
+# frame
+$arrow_buttons = TkFrame.new($arrow_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $arrow_demo
+ $arrow_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'arrow'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}
+$arrow_buttons.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# canvas
+$arrow_canvas = TkCanvas.new($arrow_demo, 'width'=>500, 'height'=>350,
+ 'relief'=>'sunken', 'borderwidth'=>2)
+$arrow_canvas.pack('expand'=>'yes', 'fill'=>'both')
+
+#
+unless Struct.const_defined?("ArrowInfo")
+ $demo_arrowInfo = Struct.new("ArrowInfo", :a, :b, :c, :width, :motionProc,
+ :x1, :x2, :y, :smallTips, :count,
+ :bigLineStyle, :boxStyle, :activeStyle).new
+end
+$demo_arrowInfo.a = 8
+$demo_arrowInfo.b = 10
+$demo_arrowInfo.c = 3
+$demo_arrowInfo.width = 2
+$demo_arrowInfo.motionProc = proc{}
+$demo_arrowInfo.x1 = 40
+$demo_arrowInfo.x2 = 350
+$demo_arrowInfo.y = 150
+$demo_arrowInfo.smallTips = [5, 5, 2]
+$demo_arrowInfo.count = 0
+if TkWinfo.depth($arrow_canvas) > 1
+ $demo_arrowInfo.bigLineStyle = {'fill'=>'SkyBlue1'}
+ $demo_arrowInfo.boxStyle = {'fill'=>'', 'outline'=>'black', 'width'=>1}
+ $demo_arrowInfo.activeStyle = {'fill'=>'red', 'outline'=>'black', 'width'=>1}
+else
+ $demo_arrowInfo.bigLineStyle = {'fill'=>'black',
+ 'stipple'=>'@'+[$demo_dir, 'images', 'grey.25'].join(File::Separator)}
+ $demo_arrowInfo.boxStyle = {'fill'=>'', 'outline'=>'black', 'width'=>1}
+ $demo_arrowInfo.activeStyle = {'fill'=>'black','outline'=>'black','width'=>1}
+end
+$arrowTag_box = TkcTag.new($arrow_canvas)
+arrowSetup $arrow_canvas
+$arrowTag_box.bind('Enter', proc{$arrow_canvas.itemconfigure('current', $demo_arrowInfo.activeStyle)})
+$arrowTag_box.bind('Leave', proc{$arrow_canvas.itemconfigure('current', $demo_arrowInfo.boxStyle)})
+$arrowTag_box.bind('B1-Enter', proc{})
+$arrowTag_box.bind('B1-Leave', proc{})
+$arrow_canvas.itembind('box1', '1',
+ proc{$demo_arrowInfo.motionProc \
+ = proc{|x,y| arrowMove1 $arrow_canvas, x, y}})
+$arrow_canvas.itembind('box2', '1',
+ proc{$demo_arrowInfo.motionProc \
+ = proc{|x,y| arrowMove2 $arrow_canvas, x, y}})
+$arrow_canvas.itembind('box3', '1',
+ proc{$demo_arrowInfo.motionProc \
+ = proc{|x,y| arrowMove3 $arrow_canvas, x, y}})
+$arrowTag_box.bind('B1-Motion',
+ proc{|x,y| $demo_arrowInfo.motionProc.call(x,y)}, "%x %y")
+$arrow_canvas.bind('Any-ButtonRelease-1', proc{arrowSetup $arrow_canvas})
+
+# arrowMove1 --
+# This method is called for each mouse motion event on box1 (the
+# one at the vertex of the arrow). It updates the controlling parameters
+# for the line and arrowhead.
+#
+# Arguments:
+# c - The name of the canvas window.
+# x, y - The coordinates of the mouse.
+
+def arrowMove1(c,x,y)
+ v = $demo_arrowInfo
+ newA = (v.x2+5-c.canvasx(x).round)/10
+ newA = 0 if newA < 0
+ newA = 25 if newA > 25
+ if newA != v.a
+ c.move('box1', 10*(v.a-newA), 0)
+ v.a = newA
+ end
+end
+
+# arrowMove2 --
+# This method is called for each mouse motion event on box2 (the
+# one at the trailing tip of the arrowhead). It updates the controlling
+# parameters for the line and arrowhead.
+#
+# Arguments:
+# c - The name of the canvas window.
+# x, y - The coordinates of the mouse.
+
+def arrowMove2(c,x,y)
+ v = $demo_arrowInfo
+ newB = (v.x2+5-c.canvasx(x).round)/10
+ newB = 0 if newB < 0
+ newB = 25 if newB > 25
+ newC = (v.y+5-c.canvasy(y).round-5*v.width)/10
+ newC = 0 if newC < 0
+ newC = 20 if newC > 20
+ if newB != v.b || newC != v.c
+ c.move('box2', 10*(v.b-newB), 10*(v.c-newC))
+ v.b = newB
+ v.c = newC
+ end
+end
+
+# arrowMove3 --
+# This method is called for each mouse motion event on box3 (the
+# one that controls the thickness of the line). It updates the
+# controlling parameters for the line and arrowhead.
+#
+# Arguments:
+# c - The name of the canvas window.
+# x, y - The coordinates of the mouse.
+
+def arrowMove3(c,x,y)
+ v = $demo_arrowInfo
+ newWidth = (v.y+2-c.canvasy(y).round)/5
+ newWidth = 0 if newWidth < 0
+ newWidth = 20 if newWidth > 20
+ if newWidth != v.width
+ c.move('box3', 0, 5*(v.width-newWidth))
+ v.width = newWidth
+ end
+end
+
diff --git a/ext/tk/sample/demos-en/bind.rb b/ext/tk/sample/demos-en/bind.rb
new file mode 100644
index 0000000000..5d30d228f0
--- /dev/null
+++ b/ext/tk/sample/demos-en/bind.rb
@@ -0,0 +1,110 @@
+# bind.rb
+#
+# This demonstration script creates a text widget with bindings set
+# up for hypertext-like effects.
+#
+# text (tag bindings) widget demo (called by 'widget')
+#
+
+# toplevel widget
+if defined?($bind_demo) && $bind_demo
+ $bind_demo.destroy
+ $bind_demo = nil
+end
+
+# demo toplevel widget
+$bind_demo = TkToplevel.new {|w|
+ title("Text Demonstration - Tag Bindings")
+ iconname("bind")
+ positionWindow(w)
+}
+
+# frame
+TkFrame.new($bind_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $bind_demo
+ $bind_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'bind'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# bind
+def tag_binding_for_bind_demo(tag, enter_style, leave_style)
+ tag.bind('Any-Enter', proc{tag.configure enter_style})
+ tag.bind('Any-Leave', proc{tag.configure leave_style})
+end
+
+# text
+TkText.new($bind_demo){|t|
+ #
+ setgrid 'true'
+ width 60
+ height 24
+ font $font
+ wrap 'word'
+ TkScrollbar.new($bind_demo) {|s|
+ pack('side'=>'right', 'fill'=>'y')
+ command proc{|*args| t.yview(*args)}
+ t.yscrollcommand proc{|first,last| s.set first,last}
+ }
+ pack('expand'=>'yes', 'fill'=>'both')
+
+ #
+ if TkWinfo.depth($root).to_i > 1
+ tagstyle_bold = {'background'=>'#43ce80', 'relief'=>'raised',
+ 'borderwidth'=>1}
+ tagstyle_normal = {'background'=>'', 'relief'=>'flat'}
+ else
+ tagstyle_bold = {'foreground'=>'white', 'background'=>'black'}
+ tagstyle_normal = {'foreground'=>'', 'background'=>''}
+ end
+
+ # ¥Æ¥­¥¹¥ÈÁÞÆþ
+ insert 'insert', "The same tag mechanism that controls display styles in text widgets can also be used to associate Tcl commands with regions of text, so that mouse or keyboard actions on the text cause particular Tcl commands to be invoked. For example, in the text below the descriptions of the canvas demonstrations have been tagged. When you move the mouse over a demo description the description lights up, and when you press button 1 over a description then that particular demonstration is invoked.
+
+"
+ insert('end', '1. Samples of all the different types of items that can be created in canvas widgets.', (d1 = TkTextTag.new(t)) )
+ insert('end', "\n\n")
+ insert('end', '2. A simple two-dimensional plot that allows you to adjust the positions of the data points.', (d2 = TkTextTag.new(t)) )
+ insert('end', "\n\n")
+ insert('end', '3. Anchoring and justification modes for text items.',
+ (d3 = TkTextTag.new(t)) )
+ insert('end', "\n\n")
+ insert('end', '4. An editor for arrow-head shapes for line items.',
+ (d4 = TkTextTag.new(t)) )
+ insert('end', "\n\n")
+ insert('end', '5. A ruler with facilities for editing tab stops.',
+ (d5 = TkTextTag.new(t)) )
+ insert('end', "\n\n")
+ insert('end',
+ '6. A grid that demonstrates how canvases can be scrolled.',
+ (d6 = TkTextTag.new(t)) )
+
+ # binding
+ [d1, d2, d3, d4, d5, d6].each{|tag|
+ tag_binding_for_bind_demo(tag, tagstyle_bold, tagstyle_normal)
+ }
+ d1.bind('1',
+ proc{eval `cat #{[$demo_dir,'items.rb'].join(File::Separator)}`})
+ d2.bind('1',
+ proc{eval `cat #{[$demo_dir,'plot.rb'].join(File::Separator)}`})
+ d3.bind('1',
+ proc{eval `cat #{[$demo_dir,'ctext.rb'].join(File::Separator)}`})
+ d4.bind('1',
+ proc{eval `cat #{[$demo_dir,'arrow.rb'].join(File::Separator)}`})
+ d5.bind('1',
+ proc{eval `cat #{[$demo_dir,'ruler.rb'].join(File::Separator)}`})
+ d6.bind('1',
+ proc{eval `cat #{[$demo_dir,'cscroll.rb'].join(File::Separator)}`})
+
+ TkTextMarkInsert.new(t, '0.0')
+ configure('state','disabled')
+}
diff --git a/ext/tk/sample/demos-en/bitmap.rb b/ext/tk/sample/demos-en/bitmap.rb
new file mode 100644
index 0000000000..c81e4ac595
--- /dev/null
+++ b/ext/tk/sample/demos-en/bitmap.rb
@@ -0,0 +1,73 @@
+# bitmap.rb
+#
+# This demonstration script creates a toplevel window that displays
+# all of Tk's built-in bitmaps.#
+# bitmap widget demo (called by 'widget')
+#
+
+# bitmapRow --
+# Create a row of bitmap items in a window.
+#
+# Arguments:
+# w - The parent window that is to contain the row.
+# args - The names of one or more bitmaps, which will be displayed
+# in a new row across the bottom of w along with their
+# names.
+
+def bitmapRow(w,*args)
+ TkFrame.new(w){|row|
+ pack('side'=>'top', 'fill'=>'both')
+ for bitmap in args
+ TkFrame.new(row){|base|
+ pack('side'=>'left', 'fill'=>'both', 'pady'=>'.25c', 'padx'=>'.25c')
+ TkLabel.new(base, 'text'=>bitmap, 'width'=>9).pack('side'=>'bottom')
+ TkLabel.new(base, 'bitmap'=>bitmap).pack('side'=>'bottom')
+ }
+ end
+ }
+end
+
+# toplevel widget
+if defined?($bitmap_demo) && $bitmap_demo
+ $bitmap_demo.destroy
+ $bitmap_demo = nil
+end
+
+# demo toplevel widget
+$bitmap_demo = TkToplevel.new {|w|
+ title("Bitmap Demonstration")
+ iconname("bitmap")
+ positionWindow(w)
+}
+
+# label
+TkLabel.new($bitmap_demo,'font'=>$font,'wraplength'=>'4i','justify'=>'left',
+ 'text'=>"This window displays all of Tk's built-in bitmaps, along with the names you can use for them in Tcl scripts."){
+ pack('side'=>'top')
+}
+
+# frame
+$bitmap_buttons = TkFrame.new($bitmap_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $bitmap_demo
+ $bitmap_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'bitmap'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}
+$bitmap_buttons.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame
+TkFrame.new($bitmap_demo){|f|
+ bitmapRow(f,'error','gray25','gray50','hourglass')
+ bitmapRow(f,'info','question','questhead','warning')
+ pack('side'=>'top', 'expand'=>'yes', 'fill'=>'both')
+}
+
diff --git a/ext/tk/sample/demos-en/browse1 b/ext/tk/sample/demos-en/browse1
new file mode 100644
index 0000000000..03e251035a
--- /dev/null
+++ b/ext/tk/sample/demos-en/browse1
@@ -0,0 +1,63 @@
+#!/usr/bin/env ruby
+
+# browse --
+# This script generates a directory browser, which lists the working
+# directory and allow you to open files or subdirectories by
+# double-clicking.
+
+require 'tk'
+
+# Create a scrollbar on the right side of the main window and a listbox
+# on the left side.
+
+listbox = TkListbox.new(nil, 'relief'=>'sunken',
+ 'width'=>20, 'height'=>20, 'setgrid'=>'yes') {|l|
+ TkScrollbar.new(nil, 'command'=>proc{|*args| l.yview *args}) {|s|
+ pack('side'=>'right', 'fill'=>'y')
+ l.yscrollcommand(proc{|first,last| s.set(first,last)})
+ }
+
+ pack('side'=>'left', 'fill'=>'both', 'expand'=>'yes')
+}
+
+root = TkRoot.new
+root.minsize(1,1)
+
+# The procedure below is invoked to open a browser on a given file; if the
+# file is a directory then another instance of this program is invoked; if
+# the file is a regular file then the Mx editor is invoked to display
+# the file.
+
+def browse (dir, file)
+ file = dir + File::Separator + file if dir != '.'
+ type = File.ftype(file)
+ if type == 'directory'
+ system($0 + ' ' + file + ' &')
+ else
+ if type == 'file'
+ if ENV['EDITOR']
+ system(ENV['EDITOR'] + ' ' + file + ' &')
+ else
+ system('xedit ' + file + ' &')
+ end
+ else
+ STDOUT.print "\"#{file}\" isn't a directory or regular file"
+ end
+ end
+end
+
+# Fill the listbox with a list of all the files in the directory (run
+# the "ls" command to get that information).
+
+dir = ARGV[0] ? ARGV[0] : '.'
+open("|ls -a #{dir}", 'r'){|fid| fid.readlines}.each{|fname|
+ listbox.insert('end', fname.chomp)
+}
+
+# Set up bindings for the browser.
+
+Tk.bind_all('Control-c', proc{root.destroy})
+listbox.bind('Double-Button-1',
+ proc{TkSelection.get.each{|f| browse dir, f}})
+
+Tk.mainloop
diff --git a/ext/tk/sample/demos-en/browse2 b/ext/tk/sample/demos-en/browse2
new file mode 100644
index 0000000000..304a5f547e
--- /dev/null
+++ b/ext/tk/sample/demos-en/browse2
@@ -0,0 +1,82 @@
+#!/usr/bin/env ruby
+
+# browse --
+# This script generates a directory browser, which lists the working
+# directory and allow you to open files or subdirectories by
+# double-clicking.
+
+require 'tk'
+
+class Browse
+ BROWSE_WIN_COUNTER = TkVariable.new(0)
+
+ def initialize(dir)
+ BROWSE_WIN_COUNTER.value = BROWSE_WIN_COUNTER.to_i + 1
+
+ # create base frame
+ base = TkToplevel.new {
+ minsize(1,1)
+ title('Browse : ' + dir)
+ }
+
+ # Create a scrollbar on the right side of the main window and a listbox
+ # on the left side.
+ list = TkListbox.new(base, 'relief'=>'sunken',
+ 'width'=>20, 'height'=>20, 'setgrid'=>'yes') {|l|
+ TkScrollbar.new(base, 'command'=>proc{|*args| l.yview *args}) {|s|
+ pack('side'=>'right', 'fill'=>'y')
+ l.yscrollcommand(proc{|first,last| s.set(first,last)})
+ }
+
+ pack('side'=>'left', 'fill'=>'both', 'expand'=>'yes')
+
+ # Fill the listbox with a list of all the files in the directory (run
+ # the "ls" command to get that information).
+ open("|ls -a #{dir}", 'r'){|fid| fid.readlines}.each{|fname|
+ l.insert('end', fname.chomp)
+ }
+
+ }
+
+ # Set up bindings for the browser.
+ base.bind('Control-c',
+ proc{
+ base.destroy
+ Browse::BROWSE_WIN_COUNTER.value = \
+ Browse::BROWSE_WIN_COUNTER.to_i - 1})
+ list.bind('Double-Button-1',
+ proc{TkSelection.get.each{|f| self.browse dir, f}})
+ end
+
+ # The method below is invoked to open a browser on a given file; if the
+ # file is a directory then another instance of this program is invoked; if
+ # the file is a regular file then the Mx editor is invoked to display
+ # the file.
+ def browse (dir, file)
+ file = dir + File::Separator + file if dir != '.'
+ type = File.ftype(file)
+ if type == 'directory'
+ Browse.new(file)
+ else
+ if type == 'file'
+ if ENV['EDITOR']
+ system(ENV['EDITOR'] + ' ' + file + ' &')
+ else
+ system('xedit ' + file + ' &')
+ end
+ else
+ STDOUT.print "\"#{file}\" isn't a directory or regular file"
+ end
+ end
+ end
+
+end
+
+Browse.new(ARGV[0] ? ARGV[0] : '.')
+
+TkRoot.new {
+ withdraw
+ Browse::BROWSE_WIN_COUNTER.trace('w', proc{exit if Browse::BROWSE_WIN_COUNTER.to_i == 0})
+}
+
+Tk.mainloop
diff --git a/ext/tk/sample/demos-en/button.rb b/ext/tk/sample/demos-en/button.rb
new file mode 100644
index 0000000000..6614d99c92
--- /dev/null
+++ b/ext/tk/sample/demos-en/button.rb
@@ -0,0 +1,84 @@
+# button.rb
+#
+# This demonstration script creates a toplevel window containing
+# several button widgets.
+#
+# button widget demo (called by 'widget')
+#
+
+# toplevel widget
+if defined?($button_demo) && $button_demo
+ $button_demo.destroy
+ $button_demo = nil
+end
+
+# demo toplevel widget
+$button_demo = TkToplevel.new {|w|
+ title("Button Demonstration")
+ iconname("button")
+ positionWindow(w)
+}
+
+# label
+msg = TkLabel.new($button_demo) {
+ font $kanji_font
+ wraplength '4i'
+ justify 'left'
+ text "If you click on any of the four buttons below, the background of the button area will change to the color indicated in the button. You can press Tab to move among the buttons, then press Space to invoke the current button."
+}
+msg.pack('side'=>'top')
+
+# frame
+$button_buttons = TkFrame.new($button_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $button_demo
+ $button_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'See Code'
+ command proc{showCode 'button'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# button
+TkButton.new($button_demo){
+ text "Peach Puff"
+ width 10
+ command proc{
+ $button_demo.configure('bg','PeachPuff1')
+ $button_buttons.configure('bg','PeachPuff1')
+ }
+}.pack('side'=>'top', 'expand'=>'yes', 'pady'=>2)
+
+TkButton.new($button_demo){
+ text "Light Blue"
+ width 10
+ command proc{
+ $button_demo.configure('bg','LightBlue1')
+ $button_buttons.configure('bg','LightBlue1')
+ }
+}.pack('side'=>'top', 'expand'=>'yes', 'pady'=>2)
+
+TkButton.new($button_demo){
+ text "Sea Green"
+ width 10
+ command proc{
+ $button_demo.configure('bg','SeaGreen2')
+ $button_buttons.configure('bg','SeaGreen2')
+ }
+}.pack('side'=>'top', 'expand'=>'yes', 'pady'=>2)
+
+TkButton.new($button_demo){
+ text "Yellow"
+ width 10
+ command proc{
+ $button_demo.configure('bg','Yellow1')
+ $button_buttons.configure('bg','Yellow1')
+ }
+}.pack('side'=>'top', 'expand'=>'yes', 'pady'=>2)
diff --git a/ext/tk/sample/demos-en/check.rb b/ext/tk/sample/demos-en/check.rb
new file mode 100644
index 0000000000..b5def6a89b
--- /dev/null
+++ b/ext/tk/sample/demos-en/check.rb
@@ -0,0 +1,70 @@
+# check.rb
+#
+# This demonstration script creates a toplevel window containing
+# several checkbuttons.
+#
+# checkbutton widget demo (called by 'widget')
+#
+
+# toplevel widget
+if defined?($check_demo) && $check_demo
+ $check_demo.destroy
+ $check_demo = nil
+end
+
+# demo toplevel widget
+$check_demo = TkToplevel.new {|w|
+ title("Checkbutton Demonstration")
+ iconname("check")
+ positionWindow(w)
+}
+
+# label
+msg = TkLabel.new($check_demo) {
+ font $font
+ wraplength '4i'
+ justify 'left'
+ text "Three checkbuttons are displayed below. If you click on a button, it will toggle the button's selection state and set a Tcl variable to a value indicating the state of the checkbutton. Click the \"See Variables\" button to see the current values of the variables."
+}
+msg.pack('side'=>'top')
+
+#
+wipers = TkVariable.new(0)
+brakes = TkVariable.new(0)
+sober = TkVariable.new(0)
+
+# frame
+TkFrame.new($check_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $check_demo
+ $check_demo = nil
+ $showVarsWin[tmppath.path] = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'check'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+
+ TkButton.new(frame) {
+ text 'See Variables'
+ command proc{
+ showVars($check_demo,
+ ['wipers', wipers], ['brakes', brakes], ['sober', sober])
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+
+# checkbutton
+[ TkCheckButton.new($check_demo, 'text'=>'Wipers OK', 'variable'=>wipers),
+ TkCheckButton.new($check_demo, 'text'=>'Brakes OK', 'variable'=>brakes),
+ TkCheckButton.new($check_demo, 'text'=>'Driver Sober', 'variable'=>sober)
+].each{|w| w.relief('flat'); w.pack('side'=>'top', 'pady'=>2, 'anchor'=>'w')}
+
diff --git a/ext/tk/sample/demos-en/clrpick.rb b/ext/tk/sample/demos-en/clrpick.rb
new file mode 100644
index 0000000000..72c52a4394
--- /dev/null
+++ b/ext/tk/sample/demos-en/clrpick.rb
@@ -0,0 +1,77 @@
+# clrpick.rb
+#
+# This demonstration script prompts the user to select a color.
+#
+# widget demo prompts the user to select a color (called by 'widget')
+#
+
+# toplevel widget
+if defined?($clrpick_demo) && $clrpick_demo
+ $clrpick_demo.destroy
+ $clrpick_demo = nil
+end
+
+# demo toplevel widget
+$clrpick_demo = TkToplevel.new {|w|
+ title("Color Selection Dialogs")
+ iconname("colors")
+ positionWindow(w)
+}
+
+# label
+TkLabel.new($clrpick_demo,'font'=>$font,'wraplength'=>'4i','justify'=>'left',
+ 'text'=>"Press the buttons below to choose the foreground and background colors for the widgets in this window.").pack('side'=>'top')
+
+# frame
+TkFrame.new($clrpick_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $clrpick_demo
+ $clrpick_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'clrpick'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# button
+TkButton.new($clrpick_demo, 'text'=>'Set background color ...') {|b|
+ command(proc{setColor $clrpick_demo, b, 'background',
+ ['background', 'highlightbackground']})
+ pack('side'=>'top', 'anchor'=>'c', 'pady'=>'2m')
+}
+
+TkButton.new($clrpick_demo, 'text'=>'Set foreground color ...') {|b|
+ command(proc{setColor $clrpick_demo, b, 'foreground', ['foreground']})
+ pack('side'=>'top', 'anchor'=>'c', 'pady'=>'2m')
+}
+
+def setColor(w,button,name,options)
+ w.grab
+ initialColor = button[name]
+ color = Tk.chooseColor('title'=>"Choose a #{name} color", 'parent'=>w,
+ 'initialcolor'=>initialColor)
+ if color != ""
+ setColor_helper(w,options,color)
+ end
+
+ w.grab('release')
+end
+
+def setColor_helper(w, options, color)
+ options.each{|opt|
+ begin
+ w[opt] = color
+ rescue
+ end
+ }
+ TkWinfo.children(w).each{|child|
+ setColor_helper child, options, color
+ }
+end
+
diff --git a/ext/tk/sample/demos-en/colors.rb b/ext/tk/sample/demos-en/colors.rb
new file mode 100644
index 0000000000..66fb0afa36
--- /dev/null
+++ b/ext/tk/sample/demos-en/colors.rb
@@ -0,0 +1,148 @@
+# colors.rb
+#
+# This demonstration script creates a listbox widget that displays
+# many of the colors from the X color database. You can click on
+# a color to change the application's palette.
+#
+# listbox widget demo 'colors' (called by 'widget')
+#
+
+# toplevel widget
+if defined?($colors_demo) && $colors_demo
+ $colors_demo.destroy
+ $colors_demo = nil
+end
+
+# demo toplevel widget
+$colors_demo = TkToplevel.new {|w|
+ title("Listbox Demonstration (colors)")
+ iconname("colors")
+ positionWindow(w)
+}
+
+# label
+msg = TkLabel.new($colors_demo) {
+ font $font
+ wraplength '4i'
+ justify 'left'
+ text "A listbox containing several color names is displayed below, along with a scrollbar. You can scan the list either using the scrollbar or by dragging in the listbox window with button 2 pressed. If you double-click button 1 on a color, then the application's color palette will be set to match that color"
+}
+msg.pack('side'=>'top')
+
+# frame
+TkFrame.new($colors_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $colors_demo
+ $colors_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'colors'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame
+colors_lbox = nil
+TkFrame.new($colors_demo, 'borderwidth'=>10) {|w|
+ s = TkScrollbar.new(w)
+ colors_lbox = TkListbox.new(w) {
+ setgrid 1
+ width 10
+ height 12
+ yscrollcommand proc{|first,last| s.set first,last}
+ }
+ s.command(proc{|*args| colors_lbox.yview(*args)})
+ s.pack('side'=>'right', 'fill'=>'y')
+ colors_lbox.pack('side'=>'left', 'expand'=>1, 'fill'=>'both')
+}.pack('side'=>'top', 'expand'=>'yes', 'fill'=>'y')
+
+colors_lbox.bind('Double-1', proc{TkPalette.setPalette TkSelection.get})
+
+ins_data = [
+ 'gray60','gray70','gray80','gray85','gray90','gray95',
+ 'snow1','snow2','snow3','snow4','seashell1','seashell2',
+ 'seashell3','seashell4','AntiqueWhite1','AntiqueWhite2',
+ 'AntiqueWhite3','AntiqueWhite4','bisque1','bisque2',
+ 'bisque3','bisque4','PeachPuff1','PeachPuff2',
+ 'PeachPuff3','PeachPuff4','NavajoWhite1','NavajoWhite2',
+ 'NavajoWhite3','NavajoWhite4','LemonChiffon1',
+ 'LemonChiffon2','LemonChiffon3','LemonChiffon4',
+ 'cornsilk1','cornsilk2','cornsilk3','cornsilk4',
+ 'ivory1','ivory2','ivory3','ivory4','honeydew1',
+ 'honeydew2','honeydew3','honeydew4','LavenderBlush1',
+ 'LavenderBlush2','LavenderBlush3','LavenderBlush4',
+ 'MistyRose1','MistyRose2','MistyRose3','MistyRose4',
+ 'azure1','azure2','azure3','azure4','SlateBlue1',
+ 'SlateBlue2','SlateBlue3','SlateBlue4','RoyalBlue1',
+ 'RoyalBlue2','RoyalBlue3','RoyalBlue4','blue1','blue2',
+ 'blue3','blue4','DodgerBlue1','DodgerBlue2',
+ 'DodgerBlue3','DodgerBlue4','SteelBlue1','SteelBlue2',
+ 'SteelBlue3','SteelBlue4','DeepSkyBlue1','DeepSkyBlue2',
+ 'DeepSkyBlue3','DeepSkyBlue4','SkyBlue1','SkyBlue2',
+ 'SkyBlue3','SkyBlue4','LightSkyBlue1','LightSkyBlue2',
+ 'LightSkyBlue3','LightSkyBlue4','SlateGray1',
+ 'SlateGray2','SlateGray3','SlateGray4',
+ 'LightSteelBlue1','LightSteelBlue2','LightSteelBlue3',
+ 'LightSteelBlue4','LightBlue1','LightBlue2',
+ 'LightBlue3','LightBlue4','LightCyan1','LightCyan2',
+ 'LightCyan3','LightCyan4','PaleTurquoise1',
+ 'PaleTurquoise2','PaleTurquoise3','PaleTurquoise4',
+ 'CadetBlue1','CadetBlue2','CadetBlue3','CadetBlue4',
+ 'turquoise1','turquoise2','turquoise3','turquoise4',
+ 'cyan1','cyan2','cyan3','cyan4','DarkSlateGray1',
+ 'DarkSlateGray2','DarkSlateGray3','DarkSlateGray4',
+ 'aquamarine1','aquamarine2','aquamarine3','aquamarine4',
+ 'DarkSeaGreen1','DarkSeaGreen2','DarkSeaGreen3',
+ 'DarkSeaGreen4','SeaGreen1','SeaGreen2','SeaGreen3',
+ 'SeaGreen4','PaleGreen1','PaleGreen2','PaleGreen3',
+ 'PaleGreen4','SpringGreen1','SpringGreen2',
+ 'SpringGreen3','SpringGreen4','green1','green2',
+ 'green3','green4','chartreuse1','chartreuse2',
+ 'chartreuse3','chartreuse4','OliveDrab1','OliveDrab2',
+ 'OliveDrab3','OliveDrab4','DarkOliveGreen1',
+ 'DarkOliveGreen2','DarkOliveGreen3','DarkOliveGreen4',
+ 'khaki1','khaki2','khaki3','khaki4','LightGoldenrod1',
+ 'LightGoldenrod2','LightGoldenrod3','LightGoldenrod4',
+ 'LightYellow1','LightYellow2','LightYellow3',
+ 'LightYellow4','yellow1','yellow2','yellow3','yellow4',
+ 'gold1','gold2','gold3','gold4','goldenrod1',
+ 'goldenrod2','goldenrod3','goldenrod4','DarkGoldenrod1',
+ 'DarkGoldenrod2','DarkGoldenrod3','DarkGoldenrod4',
+ 'RosyBrown1','RosyBrown2','RosyBrown3','RosyBrown4',
+ 'IndianRed1','IndianRed2','IndianRed3','IndianRed4',
+ 'sienna1','sienna2','sienna3','sienna4','burlywood1',
+ 'burlywood2','burlywood3','burlywood4','wheat1',
+ 'wheat2','wheat3','wheat4','tan1','tan2','tan3','tan4',
+ 'chocolate1','chocolate2','chocolate3','chocolate4',
+ 'firebrick1','firebrick2','firebrick3','firebrick4',
+ 'brown1','brown2','brown3','brown4','salmon1','salmon2',
+ 'salmon3','salmon4','LightSalmon1','LightSalmon2',
+ 'LightSalmon3','LightSalmon4','orange1','orange2',
+ 'orange3','orange4','DarkOrange1','DarkOrange2',
+ 'DarkOrange3','DarkOrange4','coral1','coral2','coral3',
+ 'coral4','tomato1','tomato2','tomato3','tomato4',
+ 'OrangeRed1','OrangeRed2','OrangeRed3','OrangeRed4',
+ 'red1','red2','red3','red4','DeepPink1','DeepPink2',
+ 'DeepPink3','DeepPink4','HotPink1','HotPink2',
+ 'HotPink3','HotPink4','pink1','pink2','pink3','pink4',
+ 'LightPink1','LightPink2','LightPink3','LightPink4',
+ 'PaleVioletRed1','PaleVioletRed2','PaleVioletRed3',
+ 'PaleVioletRed4','maroon1','maroon2','maroon3',
+ 'maroon4','VioletRed1','VioletRed2','VioletRed3',
+ 'VioletRed4','magenta1','magenta2','magenta3',
+ 'magenta4','orchid1','orchid2','orchid3','orchid4',
+ 'plum1','plum2','plum3','plum4','MediumOrchid1',
+ 'MediumOrchid2','MediumOrchid3','MediumOrchid4',
+ 'DarkOrchid1','DarkOrchid2','DarkOrchid3',
+ 'DarkOrchid4','purple1','purple2','purple3','purple4',
+ 'MediumPurple1','MediumPurple2','MediumPurple3',
+ 'MediumPurple4','thistle1','thistle2','thistle3', 'thistle4'
+]
+
+colors_lbox.insert(0, *ins_data)
diff --git a/ext/tk/sample/demos-en/cscroll.rb b/ext/tk/sample/demos-en/cscroll.rb
new file mode 100644
index 0000000000..2f2be60da2
--- /dev/null
+++ b/ext/tk/sample/demos-en/cscroll.rb
@@ -0,0 +1,134 @@
+# cscroll.rb
+#
+# This demonstration script creates a simple canvas that can be
+# scrolled in two dimensions.
+#
+# simple scrollable canvas widget demo (called by 'widget')
+#
+
+# toplevel widget
+if defined?($cscroll_demo) && $cscroll_demo
+ $cscroll_demo.destroy
+ $cscroll_demo = nil
+end
+
+# demo toplevel widget
+$cscroll_demo = TkToplevel.new {|w|
+ title("Scrollable Canvas Demonstration")
+ iconname("cscroll")
+ positionWindow(w)
+}
+
+# label
+TkLabel.new($cscroll_demo, 'font'=>$font, 'wraplength'=>'4i',
+ 'justify'=>'left', 'text'=>"This window displays a canvas widget that can be scrolled either using the scrollbars or by dragging with button 2 in the canvas. If you click button 1 on one of the rectangles, its indices will be printed on stdout."){
+ pack('side'=>'top')
+}
+
+# frame
+$cscroll_buttons = TkFrame.new($cscroll_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $cscroll_demo
+ $cscroll_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'cscroll'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}
+$cscroll_buttons.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame
+unless $tk_version =~ /^4\.[01]/
+ $cscroll_grid = TkFrame.new($cscroll_demo) {
+ pack('expand'=>'yes', 'fill'=>'both', 'padx'=>1, 'pady'=>1)
+ }
+ TkGrid.rowconfigure($cscroll_grid, 0, 'weight'=>1, 'minsize'=>0)
+ TkGrid.columnconfigure($cscroll_grid, 0, 'weight'=>1, 'minsize'=>0)
+end
+
+# canvas ÀßÄê
+$cscroll_canvas = TkCanvas.new($cscroll_demo,
+ 'relief'=>'sunken', 'borderwidth'=>2,
+ 'scrollregion'=>['-11c', '-11c', '50c', '20c']
+ ) {|c|
+ if $tk_version =~ /^4\.[01]/
+ pack('expand'=>'yes', 'fill'=>'both')
+ else
+ grid('in'=>$cscroll_grid, 'padx'=>1, 'pady'=>1, 'row'=>0, 'column'=>0,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ end
+
+ TkScrollbar.new($cscroll_demo, 'command'=>proc{|*args| c.yview(*args)}) {|vs|
+ c.yscrollcommand(proc{|first,last| vs.set first,last})
+ if $tk_version =~ /^4\.[01]/
+ pack('side'=>'right', 'fill'=>'y')
+ else
+ grid('in'=>$cscroll_grid, 'padx'=>1, 'pady'=>1, 'row'=>0, 'column'=>1,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ end
+ }
+
+ TkScrollbar.new($cscroll_demo, 'orient'=>'horiz',
+ 'command'=>proc{|*args| c.xview(*args)}) {|hs|
+ c.xscrollcommand(proc{|first,last| hs.set first,last})
+ if $tk_version =~ /^4\.[01]/
+ pack('side'=>'bottom', 'fill'=>'x')
+ else
+ grid('in'=>$cscroll_grid, 'padx'=>1, 'pady'=>1, 'row'=>1, 'column'=>0,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ end
+ }
+}
+
+bg = $cscroll_canvas.configinfo('bg')[4]
+(0..19).each{|i|
+ x = -10+3*i
+ y = -10
+ (0..9).each{|j|
+ TkcRectangle.new($cscroll_canvas, "#{x}c", "#{y}c", "#{x+2}c", "#{y+2}c",
+ 'outline'=>'black', 'fill'=>bg, 'tags'=>'rect')
+ TkcText.new($cscroll_canvas, "#{x+1}c", "#{y+1}c",
+ 'text'=>"#{i},#{j}", 'anchor'=>'center', 'tags'=>'text')
+ y += 3
+ }
+}
+
+$cscroll_canvas.itembind('all', 'Any-Enter', proc{scrollEnter $cscroll_canvas})
+$cscroll_canvas.itembind('all', 'Any-Leave', proc{scrollLeave $cscroll_canvas})
+$cscroll_canvas.itembind('all', '1', proc{scrollButton $cscroll_canvas})
+$cscroll_canvas.itembind('all', 'Any-Enter', proc{scrollEnter $cscroll_canvas})
+$cscroll_canvas.bind('2', proc{|x,y| $cscroll_canvas.scan_mark(x,y)}, '%x %y')
+$cscroll_canvas.bind('B2-Motion',
+ proc{|x,y| $cscroll_canvas.scan_dragto(x,y)}, '%x %y')
+
+def scrollEnter(c)
+ id = c.find_withtag('current')[0].id
+ id -= 1 if c.gettags('current').include?('text')
+ $oldFill = c.itemconfiginfo(id, 'fill')[4]
+ if TkWinfo.depth(c) > 1
+ c.itemconfigure(id, 'fill'=>'SeaGreen1')
+ else
+ c.itemconfigure(id, 'fill'=>'black')
+ c.itemconfigure(id+1, 'fill'=>'white')
+ end
+end
+
+def scrollLeave(c)
+ id = c.find_withtag('current')[0].id
+ id -= 1 if c.gettags('current').include?('text')
+ c.itemconfigure(id, 'fill'=>$oldFill)
+ c.itemconfigure(id+1, 'fill'=>'black')
+end
+
+def scrollButton(c)
+ id = c.find_withtag('current')[0].id
+ id += 1 unless c.gettags('current').include?('text')
+ print "You buttoned at #{c.itemconfiginfo(id,'text')[4]}\n"
+end
+
diff --git a/ext/tk/sample/demos-en/ctext.rb b/ext/tk/sample/demos-en/ctext.rb
new file mode 100644
index 0000000000..13f8de7218
--- /dev/null
+++ b/ext/tk/sample/demos-en/ctext.rb
@@ -0,0 +1,186 @@
+# ctext.rb
+#
+# This demonstration script creates a canvas widget with a text
+# item that can be edited and reconfigured in various ways.
+#
+# Canvas Text widget demo (called by 'widget')
+#
+
+# toplevel widget
+if defined?($ctext_demo) && $ctext_demo
+ $ctext_demo.destroy
+ $ctext_demo = nil
+end
+
+# demo toplevel widget
+$ctext_demo = TkToplevel.new {|w|
+ title("Canvas Text Demonstration")
+ iconname("Text")
+ positionWindow(w)
+}
+
+# label
+TkLabel.new($ctext_demo, 'font'=>$font, 'wraplength'=>'5i', 'justify'=>'left',
+ 'text'=>"This window displays a string of text to demonstrate the text facilities of canvas widgets. You can click in the boxes to adjust the position of the text relative to its positioning point or change its justification. The text also supports the following simple bindings for editing:
+ 1. You can point, click, and type.
+ 2. You can also select with button 1.
+ 3. You can copy the selection to the mouse position with button 2.
+ 4. Backspace and Control+h delete the selection if there is one;
+ otherwise they delete the character just before the insertion cursor.
+ 5. Delete deletes the selection if there is one; otherwise it deletes
+ the character just after the insertion cursor."){
+ pack('side'=>'top')
+}
+
+# frame
+$ctext_buttons = TkFrame.new($ctext_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $ctext_demo
+ $ctext_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'ctext'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}
+$ctext_buttons.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# canvas
+$ctext_canvas = TkCanvas.new($ctext_demo, 'relief'=>'flat',
+ 'borderwidth'=>0, 'width'=>500, 'height'=>350)
+$ctext_canvas.pack('side'=>'top', 'expand'=>'yes', 'fill'=>'both')
+
+# font
+textFont = '-*-Helvetica-Medium-R-Normal--*-240-*-*-*-*-*-*'
+
+# canvas
+TkcRectangle.new($ctext_canvas, 245, 195, 255, 205,
+ 'outline'=>'black', 'fill'=>'red')
+
+$ctag_text = TkcTag.new($ctext_canvas)
+$ctag_text.withtag(TkcText.new($ctext_canvas, 250, 200,
+ 'text'=>"This is just a string of text to demonstrate the text facilities of canvas widgets. Bindings have been been defined to support editing (see above).",
+ 'width'=>440, 'anchor'=>'n',
+ 'font'=>'-*-Helvetica-Medium-R-Normal--*-240-*-*-*-*-*-*',
+ 'kanjifont'=>'-*--24-*-jisx0208.1983-0',
+ 'justify'=>'left') )
+
+$ctag_text.bind('1', proc{|x,y| textB1Press $ctext_canvas,x,y}, "%x %y")
+$ctag_text.bind('B1-Motion', proc{|x,y| textB1Move $ctext_canvas,x,y}, "%x %y")
+$ctag_text.bind('Shift-1',
+ proc{|x,y| $ctext_canvas.seleect_adjust 'current', "@#{x},#{y}"},
+ "%x %y")
+$ctag_text.bind('Shift-B1-Motion',
+ proc{|x,y| textB1Move $ctext_canvas,x,y}, "%x %y")
+$ctag_text.bind('KeyPress', proc{|a| textInsert $ctext_canvas,a}, "%A")
+$ctag_text.bind('Return', proc{textInsert $ctext_canvas,"\n"})
+$ctag_text.bind('Control-h', proc{textBs $ctext_canvas})
+$ctag_text.bind('BackSpace', proc{textBs $ctext_canvas})
+$ctag_text.bind('Delete', proc{textDel $ctext_canvas})
+$ctag_text.bind('2', proc{|x,y| textPaste $ctext_canvas, "@#{x},#{y}"},
+ "%x %y")
+
+# Next, create some items that allow the text's anchor position
+# to be edited.
+
+def mkTextConfig(w,x,y,option,value,color)
+ item = TkcRectangle.new(w, x, y, x+30, y+30,
+ 'outline'=>'black', 'fill'=>color, 'width'=>1)
+ item.bind('1', proc{$ctag_text.configure option, value})
+ w.addtag_withtag('config', item)
+end
+
+x = 50
+y = 50
+color = 'LightSkyBlue1'
+mkTextConfig $ctext_canvas, x, y, 'anchor', 'se', color
+mkTextConfig $ctext_canvas, x+30, y, 'anchor', 's', color
+mkTextConfig $ctext_canvas, x+60, y, 'anchor', 'sw', color
+mkTextConfig $ctext_canvas, x, y+30, 'anchor', 'e', color
+mkTextConfig $ctext_canvas, x+30, y+30, 'anchor', 'center', color
+mkTextConfig $ctext_canvas, x+60, y+30, 'anchor', 'w', color
+mkTextConfig $ctext_canvas, x, y+60, 'anchor', 'ne', color
+mkTextConfig $ctext_canvas, x+30, y+60, 'anchor', 'n', color
+mkTextConfig $ctext_canvas, x+60, y+60, 'anchor', 'nw', color
+item = TkcRectangle.new($ctext_canvas, x+40, y+40, x+50, y+50,
+ 'outline'=>'black', 'fill'=>'red')
+item.bind('1', proc{$ctag_text.configure 'anchor', 'center'})
+TkcText.new($ctext_canvas, x+45, y-5, 'text'=>'Text Position', 'anchor'=>'s',
+ 'font'=>'-*-times-medium-r-normal--*-240-*-*-*-*-*-*',
+ 'fill'=>'brown')
+
+# Lastly, create some items that allow the text's justification to be
+# changed.
+
+x = 350
+y = 50
+color = 'SeaGreen2'
+mkTextConfig $ctext_canvas, x, y, 'justify', 'left', color
+mkTextConfig $ctext_canvas, x+30, y, 'justify', 'center', color
+mkTextConfig $ctext_canvas, x+60, y, 'justify', 'right', color
+TkcText.new($ctext_canvas, x+45, y-5, 'text'=>'Justification', 'anchor'=>'s',
+ 'font'=>'-*-times-medium-r-normal--*-240-*-*-*-*-*-*',
+ 'fill'=>'brown')
+
+$ctext_canvas.itembind('config', 'Enter', proc{textEnter $ctext_canvas})
+$ctext_canvas.itembind('config', 'Leave',
+ proc{$ctext_canvas\
+ .itemconfigure('current',
+ 'fill'=>$textConfigFill)})
+
+$textConfigFill = ''
+
+def textEnter(w)
+ $textConfigFill = (w.itemconfiginfo 'current', 'fill')[4]
+ w.itemconfigure 'current', 'fill', 'black'
+end
+
+def textInsert(w, string)
+ return if string == ""
+ begin
+ $ctag_text.dchars 'sel.first', 'sel.last'
+ rescue
+ end
+ $ctag_text.insert 'insert', string
+end
+
+def textPaste(w, pos)
+ begin
+ $ctag_text.insert pos, TkSelection.get
+ rescue
+ end
+end
+
+def textB1Press(w,x,y)
+ w.icursor 'current', "@#{x},#{y}"
+ w.itemfocus 'current'
+ w.focus
+ w.select_from 'current', "@#{x},#{y}"
+end
+
+def textB1Move(w,x,y)
+ w.select_to 'current', "@#{x},#{y}"
+end
+
+def textBs(w)
+ begin
+ $ctag_text.dchars 'sel.first', 'sel.last'
+ rescue
+ char = $ctag_text.index('insert').to_i - 1
+ $ctag_text.dchars(char) if char >= 0
+ end
+end
+
+def textDel(w)
+ begin
+ $ctag_text.dchars 'sel.first', 'sel.last'
+ rescue
+ $ctag_text.dchars 'insert'
+ end
+end
+
diff --git a/ext/tk/sample/demos-en/dialog1.rb b/ext/tk/sample/demos-en/dialog1.rb
new file mode 100644
index 0000000000..d2b34e6b46
--- /dev/null
+++ b/ext/tk/sample/demos-en/dialog1.rb
@@ -0,0 +1,35 @@
+#
+# a dialog box with a local grab (called by 'widget')
+#
+class TkDialog_Demo1 < TkDialog
+ def title
+ "Dialog with local grab"
+ end
+
+ def message
+ 'This is a modal dialog box. It uses Tk\'s "grab" command to create a "local grab" on the dialog box. The grab prevents any pointer-related events from getting to any other windows in the application until you have answered the dialog by invoking one of the buttons below. However, you can still interact with other applications.'
+ end
+
+ def bitmap
+ 'info'
+ end
+
+ def default_button
+ 0
+ end
+
+ def buttons
+ ["Dismiss", "", "Show Code"]
+ ["OK", "Cancel", "Show Code"]
+ end
+end
+
+ret = TkDialog_Demo1.new('message_config'=>{'wraplength'=>'4i'}).value
+case ret
+when 0
+ print "You pressed OK\n"
+when 1
+ print "You pressed Cancel\n"
+when 2
+ showCode 'dialog1'
+end
diff --git a/ext/tk/sample/demos-en/dialog2.rb b/ext/tk/sample/demos-en/dialog2.rb
new file mode 100644
index 0000000000..c441dda62f
--- /dev/null
+++ b/ext/tk/sample/demos-en/dialog2.rb
@@ -0,0 +1,38 @@
+#
+# a dialog box with a global grab (called by 'widget')
+#
+class TkDialog_Demo2 < TkDialog
+ def title
+ "Dialog with global grab"
+ end
+
+ def message
+ "This dialog box uses a global grab, so it prevents you from interacting with anything on your display until you invoke one of the buttons below. Global grabs are almost always a bad idea; don't use them unless you're truly desperate."
+ end
+
+ def bitmap
+ 'info'
+ end
+
+ def default_button
+ 0
+ end
+
+ def buttons
+ ["OK", "Cancel", "Show Code"]
+ end
+end
+
+ret = TkDialog_Demo2.new('message_config'=>{'wraplength'=>'4i'},
+ 'prev_command'=>proc{|dialog|
+ Tk.after 100, proc{dialog.grab('global')}
+ }).value
+case ret
+when 0
+ print "\You pressed OK\n"
+when 1
+ print "You pressed Cancel\n"
+when 2
+ showCode 'dialog2'
+end
+
diff --git a/ext/tk/sample/demos-en/doc.org/README b/ext/tk/sample/demos-en/doc.org/README
new file mode 100644
index 0000000000..90677d3316
--- /dev/null
+++ b/ext/tk/sample/demos-en/doc.org/README
@@ -0,0 +1,7 @@
+This directory contains a collection of demonstration programs that
+are translated into Japanese. You need to use a Japanized "wish" to
+see these Japanese-translated demonstration programs. You also need
+to put this directory ("demos.jp") at the next to "demos" since some
+of the programs refer to the image files at "demos".
+
+Please refer to the README file at "demos" for more detail.
diff --git a/ext/tk/sample/demos-en/doc.org/README.JP b/ext/tk/sample/demos-en/doc.org/README.JP
new file mode 100644
index 0000000000..42b4929378
--- /dev/null
+++ b/ext/tk/sample/demos-en/doc.org/README.JP
@@ -0,0 +1,14 @@
+This directory contains "widget" demo for the Japanized Tcl7.6/Tk4.2.
+Most of the messages in the original are translated to Japanese.
+But other tools in this directory are not translated.
+
+Following 2 kanji fonts are defined at the beginning of the file "widget."
+
+ -*--24-*-jisx0208.1983-0
+ -*--16-*-jisx0208.1983-0
+
+These fonts are all part of the core distribution of X11R5, so
+if you are running X11R5, you don't have to modify the file.
+
+But if you don't have these fonts, replace them with appropriate ones.
+"-*--14-*-jisx0208.1983-0" will be a good choice.
diff --git a/ext/tk/sample/demos-en/doc.org/README.tk80 b/ext/tk/sample/demos-en/doc.org/README.tk80
new file mode 100644
index 0000000000..c71f977d74
--- /dev/null
+++ b/ext/tk/sample/demos-en/doc.org/README.tk80
@@ -0,0 +1,46 @@
+This directory contains a collection of programs to demonstrate
+the features of the Tk toolkit. The programs are all scripts for
+"wish", a windowing shell. If wish has been installed in /usr/local
+then you can invoke any of the programs in this directory just
+by typing its file name to your command shell. Otherwise invoke
+wish with the file as its first argument, e.g., "wish hello".
+The rest of this file contains a brief description of each program.
+Files with names ending in ".tcl" are procedure packages used by one
+or more of the demo programs; they can't be used as programs by
+themselves so they aren't described below.
+
+hello - Creates a single button; if you click on it, a message
+ is typed and the application terminates.
+
+widget - Contains a collection of demonstrations of the widgets
+ currently available in the Tk library. Most of the .tcl
+ files are scripts for individual demos available through
+ the "widget" program.
+
+ixset - A simple Tk-based wrapper for the "xset" program, which
+ allows you to interactively query and set various X options
+ such as mouse acceleration and bell volume. Thanks to
+ Pierre David for contributing this example.
+
+rolodex - A mock-up of a simple rolodex application. It has much of
+ the user interface for such an application but no back-end
+ database. This program was written in response to Tom
+ LaStrange's toolkit benchmark challenge.
+
+tcolor - A color editor. Allows you to edit colors in several
+ different ways, and will also perform automatic updates
+ using "send".
+
+rmt - Allows you to "hook-up" remotely to any Tk application
+ on the display. Select an application with the menu,
+ then just type commands: they'll go to that application.
+
+timer - Displays a seconds timer with start and stop buttons.
+ Control-c and control-q cause it to exit.
+
+browse - A simple directory browser. Invoke it with and argument
+ giving the name of the directory you'd like to browse.
+ Double-click on files or subdirectories to browse them.
+ Control-c and control-q cause the program to exit.
+
+sccs id = SCCS: @(#) README 1.3 96/02/16 10:49:14
diff --git a/ext/tk/sample/demos-en/doc.org/license.terms b/ext/tk/sample/demos-en/doc.org/license.terms
new file mode 100644
index 0000000000..03ca6fcb31
--- /dev/null
+++ b/ext/tk/sample/demos-en/doc.org/license.terms
@@ -0,0 +1,39 @@
+This software is copyrighted by the Regents of the University of
+California, Sun Microsystems, Inc., and other parties. The following
+terms apply to all files associated with the software unless explicitly
+disclaimed in individual files.
+
+The authors hereby grant permission to use, copy, modify, distribute,
+and license this software and its documentation for any purpose, provided
+that existing copyright notices are retained in all copies and that this
+notice is included verbatim in any distributions. No written agreement,
+license, or royalty fee is required for any of the authorized uses.
+Modifications to this software may be copyrighted by their authors
+and need not follow the licensing terms described here, provided that
+the new terms are clearly indicated on the first page of each file where
+they apply.
+
+IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
+FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
+DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
+IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
+NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+MODIFICATIONS.
+
+GOVERNMENT USE: If you are acquiring this software on behalf of the
+U.S. government, the Government shall have only "Restricted Rights"
+in the software and related documentation as defined in the Federal
+Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
+are acquiring the software on behalf of the Department of Defense, the
+software shall be classified as "Commercial Computer Software" and the
+Government shall have only "Restricted Rights" as defined in Clause
+252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the
+authors grant the U.S. Government and others acting in its behalf
+permission to use and distribute the software in accordance with the
+terms specified in this license.
diff --git a/ext/tk/sample/demos-en/doc.org/license.terms.tk80 b/ext/tk/sample/demos-en/doc.org/license.terms.tk80
new file mode 100644
index 0000000000..03ca6fcb31
--- /dev/null
+++ b/ext/tk/sample/demos-en/doc.org/license.terms.tk80
@@ -0,0 +1,39 @@
+This software is copyrighted by the Regents of the University of
+California, Sun Microsystems, Inc., and other parties. The following
+terms apply to all files associated with the software unless explicitly
+disclaimed in individual files.
+
+The authors hereby grant permission to use, copy, modify, distribute,
+and license this software and its documentation for any purpose, provided
+that existing copyright notices are retained in all copies and that this
+notice is included verbatim in any distributions. No written agreement,
+license, or royalty fee is required for any of the authorized uses.
+Modifications to this software may be copyrighted by their authors
+and need not follow the licensing terms described here, provided that
+the new terms are clearly indicated on the first page of each file where
+they apply.
+
+IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
+FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
+DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
+IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
+NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+MODIFICATIONS.
+
+GOVERNMENT USE: If you are acquiring this software on behalf of the
+U.S. government, the Government shall have only "Restricted Rights"
+in the software and related documentation as defined in the Federal
+Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
+are acquiring the software on behalf of the Department of Defense, the
+software shall be classified as "Commercial Computer Software" and the
+Government shall have only "Restricted Rights" as defined in Clause
+252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the
+authors grant the U.S. Government and others acting in its behalf
+permission to use and distribute the software in accordance with the
+terms specified in this license.
diff --git a/ext/tk/sample/demos-en/entry1.rb b/ext/tk/sample/demos-en/entry1.rb
new file mode 100644
index 0000000000..29bc693395
--- /dev/null
+++ b/ext/tk/sample/demos-en/entry1.rb
@@ -0,0 +1,56 @@
+#
+# entry (no scrollbars) widget demo (called by 'widget')
+#
+
+# toplevel widget
+if defined?($entry1_demo) && $entry1_demo
+ $entry1_demo.destroy
+ $entry1_demo = nil
+end
+
+# demo toplevel widget
+$entry1_demo = TkToplevel.new {|w|
+ title("Entry Demonstration (no scrollbars)")
+ iconname("entry1")
+ positionWindow(w)
+}
+
+# label
+msg = TkLabel.new($entry1_demo) {
+ font $font
+ wraplength '5i'
+ justify 'left'
+ text "Three different entries are displayed below. You can add characters by pointing, clicking and typing. The normal Motif editing characters are supported, along with many Emacs bindings. For example, Backspace and Control-h delete the character to the left of the insertion cursor and Delete and Control-d delete the chararacter to the right of the insertion cursor. For entries that are too large to fit in the window all at once, you can scan through the entries by dragging with mouse button2 pressed."
+}
+msg.pack('side'=>'top')
+
+# frame
+TkFrame.new($entry1_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $entry1_demo
+ $entry1_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'entry1'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+#
+e1 = TkEntry.new($entry1_demo, 'relief'=>'sunken')
+e2 = TkEntry.new($entry1_demo, 'relief'=>'sunken')
+e3 = TkEntry.new($entry1_demo, 'relief'=>'sunken')
+[e1,e2,e3].each{|w| w.pack('side'=>'top', 'padx'=>10, 'pady'=>5, 'fill'=>'x')}
+
+#
+e1.insert(0, 'Initial value')
+e2.insert('end', "This entry contains a long value, much too long ")
+e2.insert('end', "to fit in the window at one time, so long in fact ")
+e2.insert('end', "that you'll have to scan or scroll to see the end.")
+e2.insert('end', "")
+
diff --git a/ext/tk/sample/demos-en/entry2.rb b/ext/tk/sample/demos-en/entry2.rb
new file mode 100644
index 0000000000..d4e58d7dd5
--- /dev/null
+++ b/ext/tk/sample/demos-en/entry2.rb
@@ -0,0 +1,91 @@
+# entry2.rb
+#
+# This demonstration script is the same as the entry1.tcl script
+# except that it creates scrollbars for the entries.
+#
+# entry (with scrollbars) widget demo (called by 'widget')
+#
+
+# toplevel widget
+if defined?($entry2_demo) && $entry2_demo
+ $entry2_demo.destroy
+ $entry2_demo = nil
+end
+
+# demo toplevel widget
+$entry2_demo = TkToplevel.new {|w|
+ title("Entry Demonstration (with scrollbars)")
+ iconname("entry2")
+ positionWindow(w)
+}
+
+# label
+msg = TkLabel.new($entry2_demo) {
+ font $font
+ wraplength '5i'
+ justify 'left'
+ text "Three different entries are displayed below, with a scrollbar for each entry. You can add characters by pointing, clicking and typing. The normal Motif editing characters are supported, along with many Emacs bindings. For example, Backspace and Control-h delete the character to the left of the insertion cursor and Delete and Control-d delete the chararacter to the right of the insertion cursor. For entries that are too large to fit in the window all at once, you can scan through the entries with the scrollbars, or by dragging with mouse button2 pressed."
+}
+msg.pack('side'=>'top')
+
+# frame
+TkFrame.new($entry2_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $entry2_demo
+ $entry2_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'entry2'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame
+TkFrame.new($entry2_demo, 'borderwidth'=>10) {|w|
+ # entry 1
+ s1 = TkScrollbar.new(w, 'relief'=>'sunken', 'orient'=>'horiz')
+ e1 = TkEntry.new(w, 'relief'=>'sunken') {
+ xscrollcommand proc{|first,last| s1.set first,last}
+ }
+ s1.command(proc{|*args| e1.xview(*args)})
+ e1.pack('side'=>'top', 'fill'=>'x')
+ s1.pack('side'=>'top', 'fill'=>'x')
+
+ # spacer
+ TkFrame.new(w, 'width'=>20, 'height'=>10).pack('side'=>'top', 'fill'=>'x')
+
+ # entry 2
+ s2 = TkScrollbar.new(w, 'relief'=>'sunken', 'orient'=>'horiz')
+ e2 = TkEntry.new(w, 'relief'=>'sunken') {
+ xscrollcommand proc{|first,last| s2.set first,last}
+ }
+ s2.command(proc{|*args| e2.xview(*args)})
+ e2.pack('side'=>'top', 'fill'=>'x')
+ s2.pack('side'=>'top', 'fill'=>'x')
+
+ # spacer
+ TkFrame.new(w, 'width'=>20, 'height'=>10).pack('side'=>'top', 'fill'=>'x')
+
+ # entry 3
+ s3 = TkScrollbar.new(w, 'relief'=>'sunken', 'orient'=>'horiz')
+ e3 = TkEntry.new(w, 'relief'=>'sunken') {
+ xscrollcommand proc{|first,last| s3.set first,last}
+ }
+ s3.command(proc{|*args| e3.xview(*args)})
+ e3.pack('side'=>'top', 'fill'=>'x')
+ s3.pack('side'=>'top', 'fill'=>'x')
+
+ #
+ e1.insert(0, 'Initial value')
+ e2.insert('end', "This entry contains a long value, much too long ")
+ e2.insert('end', "to fit in the window at one time, so long in fact ")
+ e2.insert('end', "that you'll have to scan or scroll to see the end.")
+ e2.insert('end', "")
+
+}.pack('side'=>'top', 'fill'=>'x', 'expand'=>'yes')
+
diff --git a/ext/tk/sample/demos-en/filebox.rb b/ext/tk/sample/demos-en/filebox.rb
new file mode 100644
index 0000000000..7caaaf0ede
--- /dev/null
+++ b/ext/tk/sample/demos-en/filebox.rb
@@ -0,0 +1,97 @@
+# filebox.rb
+#
+# This demonstration script prompts the user to select a file.#
+# widget demo prompts the user to select a file (called by 'widget')
+#
+
+# toplevel widget
+if defined?($filebox_demo) && $filebox_demo
+ $filebox_demo.destroy
+ $filebox_demo = nil
+end
+
+# demo toplevel widget
+$filebox_demo = TkToplevel.new {|w|
+ title("File Selection Dialogs")
+ iconname("filebox")
+ positionWindow(w)
+}
+
+# label
+TkLabel.new($filebox_demo,'font'=>$font,'wraplength'=>'4i','justify'=>'left',
+ 'text'=>"Enter a file name in the entry box or click on the \"Browse\" buttons to select a file name using the file selection dialog.").pack('side'=>'top')
+
+# frame
+TkFrame.new($filebox_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $filebox_demo
+ $filebox_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'filebox'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame
+['open', 'save'].each{|type|
+ TkFrame.new($filebox_demo) {|f|
+ TkLabel.new(f, 'text'=>"Select a file to #{type}: ", 'anchor'=>'e')\
+ .pack('side'=>'left')
+
+ TkEntry.new(f, 'width'=>20) {|e|
+ pack('side'=>'left', 'expand'=>'yes', 'fill'=>'x')
+
+ TkButton.new(f, 'text'=>'Browse ...',
+ 'command'=>proc{fileDialog $filebox_demo,e,type})\
+ .pack('side'=>'left')
+ }
+
+ pack('fill'=>'x', 'padx'=>'1c', 'pady'=>3)
+ }
+}
+
+$tk_strictMotif = TkVarAccess.new('tk_strictMotif')
+if ($tk_platform['platform'] == 'unix')
+ TkCheckButton.new($filebox_demo,
+ 'text'=>'Use Motif Style Dialog',
+ 'variable'=>$tk_strictMotif,
+ 'onvalue'=>1, 'offvalue'=>0 ).pack('anchor'=>'c')
+end
+
+def fileDialog(w,ent,operation)
+ # Type names Extension(s) Mac File Type(s)
+ #
+ #--------------------------------------------------------
+ types = [
+ ['Text files', ['.txt','.doc'] ],
+ ['Text files', [], 'TEXT' ],
+ ['Ruby Scripts', ['.rb'], 'TEXT' ],
+ ['Tcl Scripts', ['.tcl'], 'TEXT' ],
+ ['C Source Files', ['.c','.h'] ],
+ ['All Source Files', ['.rb','.tcl','.c','.h'] ],
+ ['Image Files', ['.gif'] ],
+ ['Image Files', ['.jpeg','.jpg'] ],
+ ['Image Files', [], ['GIFF','JPEG']],
+ ['All files', '*' ]
+ ]
+
+ if operation == 'open'
+ file = Tk.getOpenFile('filetypes'=>types, 'parent'=>w)
+ else
+ file = Tk.getSaveFile('filetypes'=>types, 'parent'=>w,
+ 'initialfile'=>'Untitled',
+ 'defaultextension'=>'.txt')
+ end
+ if file != ""
+ ent.delete 0, 'end'
+ ent.insert 0, file
+ ent.xview 'end'
+ end
+end
+
diff --git a/ext/tk/sample/demos-en/floor.rb b/ext/tk/sample/demos-en/floor.rb
new file mode 100644
index 0000000000..db4d59fe73
--- /dev/null
+++ b/ext/tk/sample/demos-en/floor.rb
@@ -0,0 +1,1721 @@
+# floor.rb
+#
+# This demonstration script creates a canvas widet that displays the
+# floorplan for DEC's Western Research Laboratory.
+#
+# floorDisplay widget demo (called by 'widget')
+#
+
+# floorDisplay --
+# Recreate the floorplan display in the canvas given by "w". The
+# floor given by "active" is displayed on top with its office structure
+# visible.
+#
+# Arguments:
+# w - Name of the canvas window.
+# active - Number of active floor (1, 2, or 3).
+
+def floorDisplay(w,active)
+ return if $activeFloor == active
+
+ w.delete('all')
+ $activeFloor = active
+
+ # First go through the three floors, displaying the backgrounds for
+ # each floor.
+
+ floor_bg1(w,$floor_colors['bg1'],$floor_colors['outline1'])
+ floor_bg2(w,$floor_colors['bg2'],$floor_colors['outline2'])
+ floor_bg3(w,$floor_colors['bg3'],$floor_colors['outline3'])
+
+ # Raise the background for the active floor so that it's on top.
+
+ w.raise("floor#{active}")
+
+ # Create a dummy item just to mark this point in the display list,
+ # so we can insert highlights here.
+
+ TkcRectangle.new(w,0,100,1,101, 'fill'=>'', 'outline'=>'', 'tags'=>'marker')
+
+ # Add the walls and labels for the active floor, along with
+ # transparent polygons that define the rooms on the floor.
+ # Make sure that the room polygons are on top.
+
+ $floorLabels.clear
+ $floorItems.clear
+ send("floor_fg#{active}", w, $floor_colors['offices'])
+ w.raise('room')
+
+ # Offset the floors diagonally from each other.
+
+ w.move('floor1', '2c', '2c')
+ w.move('floor2', '1c', '1c')
+
+ # Create items for the room entry and its label.
+ TkcWindow.new(w, 600, 100, 'anchor'=>'w', 'window'=>$floor_entry)
+ TkcText.new(w, 600, 100, 'anchor'=>'e', 'text'=>"Room: ")
+ w['scrollregion'] = w.bbox('all')
+end
+
+# newRoom --
+# This method is invoked whenever the mouse enters a room
+# in the floorplan. It changes tags so that the current room is
+# highlighted.
+#
+# Arguments:
+# w - The name of the canvas window.
+
+def newRoom(w)
+ id = w.find_withtag('current')[0]
+ $currentRoom.value = $floorLabels[id.id] if id != ""
+ Tk.update(true)
+end
+
+# roomChanged --
+# This method is invoked whenever the currentRoom variable changes.
+# It highlights the current room and unhighlights any previous room.
+#
+# Arguments:
+# w - The canvas window displaying the floorplan.
+# args - Not used.
+
+def roomChanged(w,*args)
+ w.delete('highlight')
+ item = $floorItems[$currentRoom.value]
+ return if item == nil
+ new = TkcPolygon.new(w, *(w.coords(item)))
+ new.configure('fill'=>$floor_colors['active'], 'tags'=>'highlight')
+ w.raise(new, 'marker')
+end
+
+# floor_bg1 --
+# This method represents part of the floorplan database. When
+# invoked, it instantiates the background information for the first
+# floor.
+#
+# Arguments:
+# w - The canvas window.
+# fill - Fill color to use for the floor's background.
+# outline - Color to use for the floor's outline.
+
+def floor_bg1(w,fill,outline)
+ TkcPolygon.new(w,347,80,349,82,351,84,353,85,363,92,375,99,386,104,
+ 386,129,398,129,398,162,484,162,484,129,559,129,559,133,725,
+ 133,725,129,802,129,802,389,644,389,644,391,559,391,559,327,
+ 508,327,508,311,484,311,484,278,395,278,395,288,400,288,404,
+ 288,409,290,413,292,418,297,421,302,422,309,421,318,417,325,
+ 411,330,405,332,397,333,344,333,340,334,336,336,335,338,332,
+ 342,331,347,332,351,334,354,336,357,341,359,340,360,335,363,
+ 331,365,326,366,304,366,304,355,258,355,258,387,60,387,60,391,
+ 0,391,0,337,3,337,3,114,8,114,8,25,30,25,30,5,93,5,98,5,104,7,
+ 110,10,116,16,119,20,122,28,123,32,123,68,220,68,220,34,221,
+ 22,223,17,227,13,231,8,236,4,242,2,246,0,260,0,283,1,300,5,
+ 321,14,335,22,348,25,365,29,363,39,358,48,352,56,337,70,
+ 344,76,347,80, 'tags'=>['floor1','bg'], 'fill'=>fill)
+ TkcLine.new(w,386,129,398,129, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,258,355,258,387, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,60,387,60,391, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,0,337,0,391, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,60,391,0,391, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,3,114,3,337, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,258,387,60,387, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,484,162,398,162, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,398,162,398,129, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,484,278,484,311, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,484,311,508,311, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,508,327,508,311, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,559,327,508,327, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,644,391,559,391, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,644,389,644,391, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,559,129,484,129, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,484,162,484,129, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,725,133,559,133, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,559,129,559,133, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,725,129,802,129, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,802,389,802,129, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,3,337,0,337, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,559,391,559,327, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,802,389,644,389, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,725,133,725,129, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,8,25,8,114, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,8,114,3,114, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,30,25,8,25, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,484,278,395,278, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,30,25,30,5, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,93,5,30,5, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,98,5,93,5, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,104,7,98,5, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,110,10,104,7, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,116,16,110,10, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,119,20,116,16, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,122,28,119,20, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,123,32,122,28, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,123,68,123,32, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,220,68,123,68, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,386,129,386,104, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,386,104,375,99, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,375,99,363,92, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,353,85,363,92, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,220,68,220,34, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,337,70,352,56, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,352,56,358,48, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,358,48,363,39, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,363,39,365,29, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,365,29,348,25, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,348,25,335,22, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,335,22,321,14, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,321,14,300,5, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,300,5,283,1, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,283,1,260,0, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,260,0,246,0, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,246,0,242,2, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,242,2,236,4, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,236,4,231,8, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,231,8,227,13, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,223,17,227,13, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,221,22,223,17, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,220,34,221,22, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,340,360,335,363, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,335,363,331,365, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,331,365,326,366, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,326,366,304,366, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,304,355,304,366, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,395,288,400,288, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,404,288,400,288, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,409,290,404,288, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,413,292,409,290, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,418,297,413,292, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,421,302,418,297, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,422,309,421,302, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,421,318,422,309, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,421,318,417,325, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,417,325,411,330, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,411,330,405,332, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,405,332,397,333, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,397,333,344,333, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,344,333,340,334, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,340,334,336,336, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,336,336,335,338, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,335,338,332,342, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,331,347,332,342, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,332,351,331,347, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,334,354,332,351, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,336,357,334,354, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,341,359,336,357, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,341,359,340,360, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,395,288,395,278, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,304,355,258,355, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,347,80,344,76, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,344,76,337,70, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,349,82,347,80, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,351,84,349,82, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,353,85,351,84, 'fill'=>outline, 'tags'=>['floor1','bg'])
+end
+
+# floor_bg2 --
+# This method represents part of the floorplan database. When
+# invoked, it instantiates the background information for the first
+# floor.
+#
+# Arguments:
+# w - The canvas window.
+# fill - Fill color to use for the floor's background.
+# outline - Color to use for the floor's outline.
+
+def floor_bg2(w,fill,outline)
+ TkcPolygon.new(w,559,129,484,129,484,162,398,162,398,129,315,129,
+ 315,133,176,133,176,129,96,129,96,133,3,133,3,339,0,339,0,391,
+ 60,391,60,387,258,387,258,329,350,329,350,311,395,311,395,280,
+ 484,280,484,311,508,311,508,327,558,327,558,391,644,391,644,
+ 367,802,367,802,129,725,129,725,133,559,133,559,129,
+ 'tags'=>['floor2','bg'], 'fill'=>fill)
+ TkcLine.new(w,350,311,350,329, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,398,129,398,162, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,802,367,802,129, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,802,129,725,129, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,725,133,725,129, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,559,129,559,133, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,559,133,725,133, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,484,162,484,129, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,559,129,484,129, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,802,367,644,367, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,644,367,644,391, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,644,391,558,391, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,558,327,558,391, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,558,327,508,327, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,508,327,508,311, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,484,311,508,311, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,484,280,484,311, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,398,162,484,162, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,484,280,395,280, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,395,280,395,311, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,258,387,60,387, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,3,133,3,339, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,3,339,0,339, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,60,391,0,391, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,0,339,0,391, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,60,387,60,391, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,258,329,258,387, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,350,329,258,329, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,395,311,350,311, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,398,129,315,129, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,176,133,315,133, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,176,129,96,129, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,3,133,96,133, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,315,133,315,129, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,176,133,176,129, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,96,133,96,129, 'fill'=>outline, 'tags'=>['floor2','bg'])
+end
+
+# floor_bg3 --
+# This method represents part of the floorplan database. When
+# invoked, it instantiates the background information for the first
+# floor.
+#
+# Arguments:
+# w - The canvas window.
+# fill - Fill color to use for the floor's background.
+# outline - Color to use for the floor's outline.
+
+def floor_bg3(w,fill,outline)
+ TkcPolygon.new(w,159,300,107,300,107,248,159,248,159,129,96,129,96,
+ 133,21,133,21,331,0,331,0,391,60,391,60,370,159,370,159,300,
+ 'tags'=>['floor3','bg'], 'fill'=>fill)
+ TkcPolygon.new(w,258,370,258,329,350,329,350,311,399,311,399,129,
+ 315,129,315,133,176,133,176,129,159,129,159,370,258,370,
+ 'tags'=>['floor3','bg'], 'fill'=>fill)
+ TkcLine.new(w,96,133,96,129, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,176,129,96,129, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,176,129,176,133, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,315,133,176,133, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,315,133,315,129, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,399,129,315,129, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,399,311,399,129, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,399,311,350,311, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,350,329,350,311, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,350,329,258,329, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,258,370,258,329, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,60,370,258,370, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,60,370,60,391, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,60,391,0,391, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,0,391,0,331, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,21,331,0,331, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,21,331,21,133, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,96,133,21,133, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,107,300,159,300,159,248,107,248,107,300,
+ 'fill'=>outline, 'tags'=>['floor3','bg'])
+end
+
+# floor_fg1 --
+# This method represents part of the floorplan database. When
+# invoked, it instantiates the foreground information for the first
+# floor (office outlines and numbers).
+#
+# Arguments:
+# w - The canvas window.
+# color - Color to use for drawing foreground information.
+
+def floor_fg1(w,color)
+ i = TkcPolygon.new(w,375,246,375,172,341,172,341,246,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '101'
+ $floorItems['101'] = i
+ TkcText.new(w,358,209, 'text'=>'101', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,307,240,339,240,339,206,307,206,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = 'Pub Lift1'
+ $floorItems['Pub Lift1'] = i
+ TkcText.new(w,323,223, 'text'=>'Pub Lift1', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,339,205,307,205,307,171,339,171,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = 'Priv Lift1'
+ $floorItems['Priv Lift1'] = i
+ TkcText.new(w,323,188, 'text'=>'Priv Lift1', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,42,389,42,337,1,337,1,389,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '110'
+ $floorItems['110'] = i
+ TkcText.new(w,21.5,363, 'text'=>'110', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,59,389,59,385,90,385,90,337,44,337,44,389,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '109'
+ $floorItems['109'] = i
+ TkcText.new(w,67,363, 'text'=>'109', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,51,300,51,253,6,253,6,300,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '111'
+ $floorItems['111'] = i
+ TkcText.new(w,28.5,276.5, 'text'=>'111', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,98,248,98,309,79,309,79,248,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '117B'
+ $floorItems['117B'] = i
+ TkcText.new(w,88.5,278.5, 'text'=>'117B', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,51,251,51,204,6,204,6,251,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '112'
+ $floorItems['112'] = i
+ TkcText.new(w,28.5,227.5, 'text'=>'112', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,6,156,51,156,51,203,6,203,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '113'
+ $floorItems['113'] = i
+ TkcText.new(w,28.5,179.5, 'text'=>'113', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,85,169,79,169,79,192,85,192,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '117A'
+ $floorItems['117A'] = i
+ TkcText.new(w,82,180.5, 'text'=>'117A', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,77,302,77,168,53,168,53,302,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '117'
+ $floorItems['117'] = i
+ TkcText.new(w,65,235, 'text'=>'117', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,51,155,51,115,6,115,6,155,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '114'
+ $floorItems['114'] = i
+ TkcText.new(w,28.5,135, 'text'=>'114', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,95,115,53,115,53,168,95,168,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '115'
+ $floorItems['115'] = i
+ TkcText.new(w,74,141.5, 'text'=>'115', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,87,113,87,27,10,27,10,113,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '116'
+ $floorItems['116'] = i
+ TkcText.new(w,48.5,70, 'text'=>'116', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,89,91,128,91,128,113,89,131,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '118'
+ $floorItems['118'] = i
+ TkcText.new(w,108.5,102, 'text'=>'118', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,178,128,178,132,216,132,216,91,
+ 163,91,163,112,149,112,149,128,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '120'
+ $floorItems['120'] = i
+ TkcText.new(w,189.5,111.5, 'text'=>'120', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,79,193,87,193,87,169,136,169,136,192,
+ 156,192,156,169,175,169,175,246,79,246,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '122'
+ $floorItems['122'] = i
+ TkcText.new(w,131,207.5, 'text'=>'122', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,138,169,154,169,154,191,138,191,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '121'
+ $floorItems['121'] = i
+ TkcText.new(w,146,180, 'text'=>'121', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,99,300,126,300,126,309,99,309,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '106A'
+ $floorItems['106A'] = i
+ TkcText.new(w,112.5,304.5, 'text'=>'106A', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,128,299,128,309,150,309,150,248,99,248,99,299,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '105'
+ $floorItems['105'] = i
+ TkcText.new(w,124.5,278.5, 'text'=>'105', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,174,309,174,300,152,300,152,309,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '106B'
+ $floorItems['106B'] = i
+ TkcText.new(w,163,304.5, 'text'=>'106B', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,176,299,176,309,216,309,216,248,152,248,152,299,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '104'
+ $floorItems['104'] = i
+ TkcText.new(w,184,278.5, 'text'=>'104', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,138,385,138,337,91,337,91,385,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '108'
+ $floorItems['108'] = i
+ TkcText.new(w,114.5,361, 'text'=>'108', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,256,337,140,337,140,385,256,385,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '107'
+ $floorItems['107'] = i
+ TkcText.new(w,198,361, 'text'=>'107', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,300,353,300,329,260,329,260,353,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = 'Smoking'
+ $floorItems['Smoking'] = i
+ TkcText.new(w,280,341, 'text'=>'Smoking', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,314,135,314,170,306,170,306,246,177,246,177,135,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '123'
+ $floorItems['123'] = i
+ TkcText.new(w,245.5,190.5, 'text'=>'123', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,217,248,301,248,301,326,257,326,257,310,217,310,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '103'
+ $floorItems['103'] = i
+ TkcText.new(w,259,287, 'text'=>'103', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,396,188,377,188,377,169,316,169,316,131,396,131,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '124'
+ $floorItems['124'] = i
+ TkcText.new(w,356,150, 'text'=>'124', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,397,226,407,226,407,189,377,189,377,246,397,246,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '125'
+ $floorItems['125'] = i
+ TkcText.new(w,392,217.5, 'text'=>'125', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,399,187,409,187,409,207,474,207,474,164,399,164,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '126'
+ $floorItems['126'] = i
+ TkcText.new(w,436.5,185.5, 'text'=>'126', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,409,209,409,229,399,229,399,253,
+ 486,253,486,239,474,239,474,209,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '127'
+ $floorItems['127'] = i
+ TkcText.new(w,436.5,'231', 'text'=>'127', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,501,164,501,174,495,174,495,188,
+ 490,188,490,204,476,204,476,164,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = 'MShower'
+ $floorItems['MShower'] = i
+ TkcText.new(w,488.5,'184', 'text'=>'MShower', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,497,176,513,176,513,204,492,204,492,190,497,190,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = 'Closet'
+ $floorItems['Closet'] = i
+ TkcText.new(w,502.5,190, 'text'=>'Closet', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,476,237,476,206,513,206,513,254,488,254,488,237,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = 'WShower'
+ $floorItems['WShower'] = i
+ TkcText.new(w,494.5,230, 'text'=>'WShower', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,486,131,558,131,558,135,724,135,724,166,
+ 697,166,697,275,553,275,531,254,515,254,
+ 515,174,503,174,503,161,486,161,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '130'
+ $floorItems['130'] = i
+ TkcText.new(w,638.5,205, 'text'=>'130', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,308,242,339,242,339,248,342,248,
+ 342,246,397,246,397,276,393,276,
+ 393,309,300,309,300,248,308,248,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '102'
+ $floorItems['102'] = i
+ TkcText.new(w,367.5,278.5, 'text'=>'102', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,397,255,486,255,486,276,397,276,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '128'
+ $floorItems['128'] = i
+ TkcText.new(w,441.5,265.5, 'text'=>'128', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,510,309,486,309,486,255,530,255,
+ 552,277,561,277,561,325,510,325,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '129'
+ $floorItems['129'] = i
+ TkcText.new(w,535.5,293, 'text'=>'129', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,696,281,740,281,740,387,642,387,
+ 642,389,561,389,561,277,696,277,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '133'
+ $floorItems['133'] = i
+ TkcText.new(w,628.5,335, 'text'=>'133', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,742,387,742,281,800,281,800,387,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '132'
+ $floorItems['132'] = i
+ TkcText.new(w,771,334, 'text'=>'132', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,800,168,800,280,699,280,699,168,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '134'
+ $floorItems['134'] = i
+ TkcText.new(w,749.5,224, 'text'=>'134', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,726,131,726,166,800,166,800,131,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '135'
+ $floorItems['135'] = i
+ TkcText.new(w,763,148.5, 'text'=>'135', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,340,360,335,363,331,365,326,366,304,366,
+ 304,312,396,312,396,288,400,288,404,288,
+ 409,290,413,292,418,297,421,302,422,309,
+ 421,318,417,325,411,330,405,332,397,333,
+ 344,333,340,334,336,336,335,338,332,342,
+ 331,347,332,351,334,354,336,357,341,359,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = 'Ramona Stair'
+ $floorItems['Ramona Stair'] = i
+ TkcText.new(w,368,323, 'text'=>'Ramona Stair', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,30,23,30,5,93,5,98,5,104,7,110,10,116,16,119,20,
+ 122,28,123,32,123,68,220,68,220,87,90,87,90,23,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = 'University Stair'
+ $floorItems['University Stair'] = i
+ TkcText.new(w,155,77.5, 'text'=>'University Stair', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,282,37,295,40,312,49,323,56,337,70,352,56,
+ 358,48,363,39,365,29,348,25,335,22,321,14,
+ 300,5,283,1,260,0,246,0,242,2,236,4,231,8,
+ 227,13,223,17,221,22,220,34,260,34,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = 'Plaza Stair'
+ $floorItems['Plaza Stair'] = i
+ TkcText.new(w,317.5,28.5, 'text'=>'Plaza Stair', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,220,34,260,34,282,37,295,40,312,49,
+ 323,56,337,70,350,83,365,94,377,100,
+ 386,104,386,128,220,128,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = 'Plaza Deck'
+ $floorItems['Plaza Deck'] = i
+ TkcText.new(w,303,81, 'text'=>'Plaza Deck', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,257,336,77,336,6,336,6,301,77,301,77,310,257,310,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '106'
+ $floorItems['106'] = i
+ TkcText.new(w,131.5,318.5, 'text'=>'106', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,146,110,162,110,162,91,130,91,130,115,95,115,
+ 95,128,114,128,114,151,157,151,157,153,112,153,
+ 112,130,97,130,97,168,175,168,175,131,146,131,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '119'
+ $floorItems['119'] = i
+ TkcText.new(w,143.5,133, 'text'=>'119', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ TkcLine.new(w,155,191,155,189, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,155,177,155,169, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,96,129,96,169, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,78,169,176,169, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,176,247,176,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,340,206,307,206, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,340,187,340,170, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,340,210,340,201, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,340,247,340,224, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,340,241,307,241, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,376,246,376,170, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,307,247,307,170, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,376,170,307,170, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,315,129,315,170, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,147,129,176,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,202,133,176,133, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,398,129,315,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,258,352,258,387, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,60,387,60,391, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,0,337,0,391, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,60,391,0,391, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,3,114,3,337, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,258,387,60,387, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,52,237,52,273, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,52,189,52,225, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,52,140,52,177, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,395,306,395,311, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,531,254,398,254, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,475,178,475,238, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,502,162,398,162, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,398,129,398,188, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,383,188,376,188, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,408,188,408,194, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,398,227,398,254, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,408,227,398,227, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,408,222,408,227, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,408,206,408,210, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,408,208,475,208, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,484,278,484,311, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,484,311,508,311, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,508,327,508,311, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,559,327,508,327, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,644,391,559,391, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,644,389,644,391, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,514,205,475,205, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,496,189,496,187, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,559,129,484,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,484,162,484,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,725,133,559,133, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,559,129,559,133, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,725,149,725,167, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,725,129,802,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,802,389,802,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,739,167,802,167, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,396,188,408,188, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,0,337,9,337, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,58,337,21,337, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,43,391,43,337, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,105,337,75,337, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,91,387,91,337, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,154,337,117,337, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,139,387,139,337, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,227,337,166,337, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,258,337,251,337, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,258,328,302,328, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,302,355,302,311, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,395,311,302,311, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,484,278,395,278, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,395,294,395,278, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,473,278,473,275, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,473,256,473,254, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,533,257,531,254, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,553,276,551,274, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,698,276,553,276, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,559,391,559,327, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,802,389,644,389, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,741,314,741,389, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,698,280,698,167, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,707,280,698,280, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,802,280,731,280, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,741,280,741,302, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,698,167,727,167, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,725,137,725,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,514,254,514,175, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,496,175,514,175, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,502,175,502,162, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,475,166,475,162, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,496,176,496,175, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,491,189,496,189, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,491,205,491,189, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,487,238,475,238, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,487,240,487,238, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,487,252,487,254, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,315,133,304,133, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,256,133,280,133, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,78,247,270,247, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,307,247,294,247, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,214,133,232,133, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,217,247,217,266, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,217,309,217,291, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,217,309,172,309, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,154,309,148,309, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,175,300,175,309, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,151,300,175,300, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,151,247,151,309, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,78,237,78,265, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,78,286,78,309, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,106,309,78,309, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,130,309,125,309, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,99,309,99,247, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,127,299,99,299, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,127,309,127,299, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,155,191,137,191, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,137,169,137,191, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,78,171,78,169, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,78,190,78,218, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,86,192,86,169, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,86,192,78,192, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,52,301,3,301, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,52,286,52,301, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,52,252,3,252, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,52,203,3,203, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,3,156,52,156, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,8,25,8,114, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,63,114,3,114, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,75,114,97,114, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,108,114,129,114, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,129,114,129,89, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,52,114,52,128, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,132,89,88,89, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,88,25,88,89, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,88,114,88,89, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,218,89,144,89, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,147,111,147,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,162,111,147,111, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,162,109,162,111, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,162,96,162,89, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,218,89,218,94, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,218,89,218,119, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,8,25,88,25, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,258,337,258,328, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,113,129,96,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,302,355,258,355, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,386,104,386,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,377,100,386,104, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,365,94,377,100, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,350,83,365,94, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,337,70,350,83, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,337,70,323,56, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,312,49,323,56, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,295,40,312,49, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,282,37,295,40, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,260,34,282,37, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,253,34,260,34, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,386,128,386,104, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,113,152,156,152, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,113,152,156,152, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,113,152,113,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+end
+
+# floor_fg2 --
+# This method represents part of the floorplan database. When
+# invoked, it instantiates the foreground information for the second
+# floor (office outlines and numbers).
+#
+# Arguments:
+# w - The canvas window.
+# color - Color to use for drawing foreground information.
+
+def floor_fg2(w,color)
+ i = TkcPolygon.new(w,748,188,755,188,755,205,758,205,758,222,
+ 800,222,800,168,748,168,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '238'
+ $floorItems['238'] = i
+ TkcText.new(w,774,195, 'text'=>'238', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,726,188,746,188,746,166,800,166,800,131,726,131,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '237'
+ $floorItems['237'] = i
+ TkcText.new(w,763,148.5, 'text'=>'237', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,497,187,497,204,559,204,559,324,641,324,
+ 643,324,643,291,641,291,641,205,696,205,
+ 696,291,694,291,694,314,715,314,715,291,
+ 715,205,755,205,755,190,724,190,724,187,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '246'
+ $floorItems['246'] = i
+ TkcText.new(w,600,264, 'text'=>'246', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,694,279,643,279,643,314,694,314,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '247'
+ $floorItems['247'] = i
+ TkcText.new(w,668.5,296.5, 'text'=>'247', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,232,250,308,250,308,242,339,242,339,246,
+ 397,246,397,255,476,255,476,250,482,250,559,250,
+ 559,274,482,274,482,278,396,278,396,274,232,274,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '202'
+ $floorItems['202'] = i
+ TkcText.new(w,285.5,260, 'text'=>'202', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,53,228,53,338,176,338,233,338,233,196,
+ 306,196,306,180,175,180,175,169,156,169,
+ 156,196,176,196,176,228,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '206'
+ $floorItems['206'] = i
+ TkcText.new(w,143,267, 'text'=>'206', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,51,277,6,277,6,338,51,338,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '212'
+ $floorItems['212'] = i
+ TkcText.new(w,28.5,307.5, 'text'=>'212', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,557,276,486,276,486,309,510,309,510,325,557,325,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '245'
+ $floorItems['245'] = i
+ TkcText.new(w,521.5,300.5, 'text'=>'245', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,560,389,599,389,599,326,560,326,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '244'
+ $floorItems['244'] = i
+ TkcText.new(w,579.5,357.5, 'text'=>'244', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,601,389,601,326,643,326,643,389,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '243'
+ $floorItems['243'] = i
+ TkcText.new(w,622,357.5, 'text'=>'243', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,688,316,645,316,645,365,688,365,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '242'
+ $floorItems['242'] = i
+ TkcText.new(w,666.5,340.5, 'text'=>'242', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,802,367,759,367,759,226,802,226,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = 'Barbecue Deck'
+ $floorItems['Barbecue Deck'] = i
+ TkcText.new(w,780.5,296.5, 'text'=>'Barbecue Deck', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,755,262,755,314,717,314,717,262,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '240'
+ $floorItems['240'] = i
+ TkcText.new(w,736,288, 'text'=>'240', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,755,316,689,316,689,365,755,365,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '241'
+ $floorItems['241'] = i
+ TkcText.new(w,722,340.5, 'text'=>'241', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,755,206,717,206,717,261,755,261,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '239'
+ $floorItems['239'] = i
+ TkcText.new(w,736,233.5, 'text'=>'239', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,695,277,643,277,643,206,695,206,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '248'
+ $floorItems['248'] = i
+ TkcText.new(w,669,241.5, 'text'=>'248', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,676,135,676,185,724,185,724,135,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '236'
+ $floorItems['236'] = i
+ TkcText.new(w,700,160, 'text'=>'236', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,675,135,635,135,635,145,628,145,628,185,675,185,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '235'
+ $floorItems['235'] = i
+ TkcText.new(w,651.5,160, 'text'=>'235', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,626,143,633,143,633,135,572,135,
+ 572,143,579,143,579,185,626,185,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '234'
+ $floorItems['234'] = i
+ TkcText.new(w,606,160, 'text'=>'234', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,557,135,571,135,571,145,578,145,
+ 578,185,527,185,527,131,557,131,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '233'
+ $floorItems['233'] = i
+ TkcText.new(w,552.5,158, 'text'=>'233', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,476,249,557,249,557,205,476,205,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '230'
+ $floorItems['230'] = i
+ TkcText.new(w,516.5,227, 'text'=>'230', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,476,164,486,164,486,131,525,131,525,185,476,185,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '232'
+ $floorItems['232'] = i
+ TkcText.new(w,500.5,158, 'text'=>'232', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,476,186,495,186,495,204,476,204,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '229'
+ $floorItems['229'] = i
+ TkcText.new(w,485.5,195, 'text'=>'229', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,474,207,409,207,409,187,399,187,399,164,474,164,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '227'
+ $floorItems['227'] = i
+ TkcText.new(w,436.5,185.5, 'text'=>'227', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,399,228,399,253,474,253,474,209,409,209,409,228,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '228'
+ $floorItems['228'] = i
+ TkcText.new(w,436.5,231, 'text'=>'228', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,397,246,397,226,407,226,407,189,377,189,377,246,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '226'
+ $floorItems['226'] = i
+ TkcText.new(w,392,217.5, 'text'=>'226', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,377,169,316,169,316,131,397,131,397,188,377,188,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '225'
+ $floorItems['225'] = i
+ TkcText.new(w,356.5,150, 'text'=>'225', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,234,198,306,198,306,249,234,249,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '224'
+ $floorItems['224'] = i
+ TkcText.new(w,270,223.5, 'text'=>'224', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,270,179,306,179,306,170,314,170,314,135,270,135,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '223'
+ $floorItems['223'] = i
+ TkcText.new(w,292,157, 'text'=>'223', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,268,179,221,179,221,135,268,135,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '222'
+ $floorItems['222'] = i
+ TkcText.new(w,244.5,157, 'text'=>'222', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,177,179,219,179,219,135,177,135,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '221'
+ $floorItems['221'] = i
+ TkcText.new(w,198,157, 'text'=>'221', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,299,327,349,327,349,284,341,284,341,276,299,276,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '204'
+ $floorItems['204'] = i
+ TkcText.new(w,324,301.5, 'text'=>'204', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,234,276,297,276,297,327,257,327,257,338,234,338,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '205'
+ $floorItems['205'] = i
+ TkcText.new(w,265.5,307, 'text'=>'205', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,256,385,256,340,212,340,212,385,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '207'
+ $floorItems['207'] = i
+ TkcText.new(w,234,362.5, 'text'=>'207', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,210,340,164,340,164,385,210,385,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '208'
+ $floorItems['208'] = i
+ TkcText.new(w,187,362.5, 'text'=>'208', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,115,340,162,340,162,385,115,385,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '209'
+ $floorItems['209'] = i
+ TkcText.new(w,138.5,362.5, 'text'=>'209', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,89,228,89,156,53,156,53,228,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '217'
+ $floorItems['217'] = i
+ TkcText.new(w,71,192, 'text'=>'217', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,89,169,97,169,97,190,89,190,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '217A'
+ $floorItems['217A'] = i
+ TkcText.new(w,93,179.5, 'text'=>'217A', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,89,156,89,168,95,168,95,135,53,135,53,156,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '216'
+ $floorItems['216'] = i
+ TkcText.new(w,71,145.5, 'text'=>'216', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,51,179,51,135,6,135,6,179,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '215'
+ $floorItems['215'] = i
+ TkcText.new(w,28.5,157, 'text'=>'215', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,51,227,6,227,6,180,51,180,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '214'
+ $floorItems['214'] = i
+ TkcText.new(w,28.5,203.5, 'text'=>'214', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,51,275,6,275,6,229,51,229,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '213'
+ $floorItems['213'] = i
+ TkcText.new(w,28.5,252, 'text'=>'213', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,114,340,67,340,67,385,114,385,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '210'
+ $floorItems['210'] = i
+ TkcText.new(w,90.5,362.5, 'text'=>'210', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,59,389,59,385,65,385,65,340,1,340,1,389,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '211'
+ $floorItems['211'] = i
+ TkcText.new(w,33,364.5, 'text'=>'211', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,393,309,350,309,350,282,342,282,342,276,393,276,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '203'
+ $floorItems['203'] = i
+ TkcText.new(w,367.5,292.5, 'text'=>'203', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,99,191,91,191,91,226,174,226,174,198,
+ 154,198,154,192,109,192,109,169,99,169,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '220'
+ $floorItems['220'] = i
+ TkcText.new(w,132.5,208.5, 'text'=>'220', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,339,205,307,205,307,171,339,171,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = 'Priv Lift2'
+ $floorItems['Priv Lift2'] = i
+ TkcText.new(w,323,188, 'text'=>'Priv Lift2', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,307,240,339,240,339,206,307,206,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = 'Pub Lift 2'
+ $floorItems['Pub Lift 2'] = i
+ TkcText.new(w,323,223, 'text'=>'Pub Lift 2', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,175,168,97,168,97,131,175,131,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '218'
+ $floorItems['218'] = i
+ TkcText.new(w,136,149.5, 'text'=>'218', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,154,191,111,191,111,169,154,169,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '219'
+ $floorItems['219'] = i
+ TkcText.new(w,132.5,180, 'text'=>'219', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,375,246,375,172,341,172,341,246,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '201'
+ $floorItems['201'] = i
+ TkcText.new(w,358,209, 'text'=>'201', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ TkcLine.new(w,641,186,678,186, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,757,350,757,367, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,634,133,634,144, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,634,144,627,144, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,572,133,572,144, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,572,144,579,144, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,398,129,398,162, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,174,197,175,197, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,175,197,175,227, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,757,206,757,221, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,396,188,408,188, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,727,189,725,189, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,747,167,802,167, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,747,167,747,189, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,755,189,739,189, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,769,224,757,224, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,802,224,802,129, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,802,129,725,129, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,725,189,725,129, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,725,186,690,186, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,676,133,676,186, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,627,144,627,186, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,629,186,593,186, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,579,144,579,186, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,559,129,559,133, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,725,133,559,133, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,484,162,484,129, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,559,129,484,129, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,526,129,526,186, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,540,186,581,186, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,528,186,523,186, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,511,186,475,186, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,496,190,496,186, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,496,205,496,202, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,475,205,527,205, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,558,205,539,205, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,558,205,558,249, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,558,249,475,249, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,662,206,642,206, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,695,206,675,206, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,695,278,642,278, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,642,291,642,206, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,695,291,695,206, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,716,208,716,206, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,757,206,716,206, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,757,221,757,224, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,793,224,802,224, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,757,262,716,262, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,716,220,716,264, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,716,315,716,276, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,757,315,703,315, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,757,325,757,224, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,757,367,644,367, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,689,367,689,315, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,647,315,644,315, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,659,315,691,315, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,600,325,600,391, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,627,325,644,325, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,644,391,644,315, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,615,325,575,325, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,644,391,558,391, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,563,325,558,325, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,558,391,558,314, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,558,327,508,327, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,558,275,484,275, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,558,302,558,275, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,508,327,508,311, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,484,311,508,311, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,484,275,484,311, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,475,208,408,208, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,408,206,408,210, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,408,222,408,227, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,408,227,398,227, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,398,227,398,254, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,408,188,408,194, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,383,188,376,188, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,398,188,398,162, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,398,162,484,162, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,475,162,475,254, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,398,254,475,254, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,484,280,395,280, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,395,311,395,275, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,307,197,293,197, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,278,197,233,197, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,233,197,233,249, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,307,179,284,179, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,233,249,278,249, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,269,179,269,133, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,220,179,220,133, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,155,191,110,191, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,90,190,98,190, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,98,169,98,190, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,52,133,52,165, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,52,214,52,177, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,52,226,52,262, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,52,274,52,276, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,234,275,234,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,226,339,258,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,211,387,211,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,214,339,177,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,258,387,60,387, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,3,133,3,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,165,339,129,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,117,339,80,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,68,339,59,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,0,339,46,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,60,391,0,391, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,0,339,0,391, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,60,387,60,391, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,258,329,258,387, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,350,329,258,329, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,395,311,350,311, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,398,129,315,129, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,176,133,315,133, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,176,129,96,129, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,3,133,96,133, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,66,387,66,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,115,387,115,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,163,387,163,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,234,275,276,275, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,288,275,309,275, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,298,275,298,329, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,341,283,350,283, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,321,275,341,275, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,375,275,395,275, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,315,129,315,170, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,376,170,307,170, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,307,250,307,170, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,376,245,376,170, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,340,241,307,241, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,340,245,340,224, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,340,210,340,201, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,340,187,340,170, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,340,206,307,206, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,293,250,307,250, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,271,179,238,179, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,226,179,195,179, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,176,129,176,179, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,182,179,176,179, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,174,169,176,169, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,162,169,90,169, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,96,169,96,129, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,175,227,90,227, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,90,190,90,227, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,52,179,3,179, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,52,228,3,228, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,52,276,3,276, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,155,177,155,169, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,110,191,110,169, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,155,189,155,197, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,350,283,350,329, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,162,197,155,197, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,341,275,341,283, 'fill'=>color, 'tags'=>['floor2','wall'])
+end
+
+# floor_fg3 --
+# This method represents part of the floorplan database. When
+# invoked, it instantiates the foreground information for the third
+# floor (office outlines and numbers).
+#
+# Arguments:
+# w - The canvas window.
+# color - Color to use for drawing foreground information.
+
+def floor_fg3(w,color)
+ i = TkcPolygon.new(w,89,228,89,180,70,180,70,228,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '316'
+ $floorItems['316'] = i
+ TkcText.new(w,79.5,204, 'text'=>'316', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,115,368,162,368,162,323,115,323,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '309'
+ $floorItems['309'] = i
+ TkcText.new(w,138.5,345.5, 'text'=>'309', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,164,323,164,368,211,368,211,323,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '308'
+ $floorItems['308'] = i
+ TkcText.new(w,187.5,345.5, 'text'=>'308', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,256,368,212,368,212,323,256,323,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '307'
+ $floorItems['307'] = i
+ TkcText.new(w,234,345.5, 'text'=>'307', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,244,276,297,276,297,327,260,327,260,321,244,321,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '305'
+ $floorItems['305'] = i
+ TkcText.new(w,270.5,301.5, 'text'=>'305', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,251,219,251,203,244,203,244,219,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '324B'
+ $floorItems['324B'] = i
+ TkcText.new(w,247.5,211, 'text'=>'324B', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,251,249,244,249,244,232,251,232,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '324A'
+ $floorItems['324A'] = i
+ TkcText.new(w,247.5,240.5, 'text'=>'324A', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,223,135,223,179,177,179,177,135,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '320'
+ $floorItems['320'] = i
+ TkcText.new(w,200,157, 'text'=>'320', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,114,368,114,323,67,323,67,368,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '310'
+ $floorItems['310'] = i
+ TkcText.new(w,90.5,345.5, 'text'=>'310', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,23,277,23,321,68,321,68,277,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '312'
+ $floorItems['312'] = i
+ TkcText.new(w,45.5,299, 'text'=>'312', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,23,229,68,229,68,275,23,275,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '313'
+ $floorItems['313'] = i
+ TkcText.new(w,45.5,252, 'text'=>'313', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,68,227,23,227,23,180,68,180,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '314'
+ $floorItems['314'] = i
+ TkcText.new(w,40.5,203.5, 'text'=>'314', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,95,179,95,135,23,135,23,179,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '315'
+ $floorItems['315'] = i
+ TkcText.new(w,59,157, 'text'=>'315', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,99,226,99,204,91,204,91,226,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '316B'
+ $floorItems['316B'] = i
+ TkcText.new(w,95,215, 'text'=>'316B', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,91,202,99,202,99,180,91,180,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '316A'
+ $floorItems['316A'] = i
+ TkcText.new(w,95,191, 'text'=>'316A', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,97,169,109,169,109,192,154,192,154,198,
+ 174,198,174,226,101,226,101,179,97,179,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '319'
+ $floorItems['319'] = i
+ TkcText.new(w,141.5,209, 'text'=>'319', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,65,368,58,368,58,389,1,389,1,333,23,333,23,323,65,323,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '311'
+ $floorItems['311'] = i
+ TkcText.new(w,29.5,361, 'text'=>'311', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,154,191,111,191,111,169,154,169,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '318'
+ $floorItems['318'] = i
+ TkcText.new(w,132.5,180, 'text'=>'318', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,175,168,97,168,97,131,175,131,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '317'
+ $floorItems['317'] = i
+ TkcText.new(w,136,149.5, 'text'=>'317', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,274,194,274,221,306,221,306,194,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '323'
+ $floorItems['323'] = i
+ TkcText.new(w,290,207.5, 'text'=>'323', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,306,222,274,222,274,249,306,249,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '325'
+ $floorItems['325'] = i
+ TkcText.new(w,290,235.5, 'text'=>'325', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,263,179,224,179,224,135,263,135,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '321'
+ $floorItems['321'] = i
+ TkcText.new(w,243.5,157, 'text'=>'321', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,314,169,306,169,306,192,273,192,
+ 264,181,264,135,314,135,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '322'
+ $floorItems['322'] = i
+ TkcText.new(w,293.5,163.5, 'text'=>'322', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,307,240,339,240,339,206,307,206,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = 'Pub Lift3'
+ $floorItems['Pub Lift3'] = i
+ TkcText.new(w,323,223, 'text'=>'Pub Lift3', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,339,205,307,205,307,171,339,171,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = 'Priv Lift3'
+ $floorItems['Priv Lift3'] = i
+ TkcText.new(w,323,188, 'text'=>'Priv Lift3', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,350,284,376,284,376,276,397,276,397,309,350,309,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '303'
+ $floorItems['303'] = i
+ TkcText.new(w,373.5,292.5, 'text'=>'303', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,272,203,272,249,252,249,252,230,
+ 244,230,244,221,252,221,252,203,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '324'
+ $floorItems['324'] = i
+ TkcText.new(w,262,226, 'text'=>'324', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,299,276,299,327,349,327,349,284,341,284,341,276,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '304'
+ $floorItems['304'] = i
+ TkcText.new(w,324,301.5, 'text'=>'304', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,375,246,375,172,341,172,341,246,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '301'
+ $floorItems['301'] = i
+ TkcText.new(w,358,209, 'text'=>'301', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,397,246,377,246,377,185,397,185,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '327'
+ $floorItems['327'] = i
+ TkcText.new(w,387,215.5, 'text'=>'327', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,316,131,316,169,377,169,377,185,397,185,397,131,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '326'
+ $floorItems['326'] = i
+ TkcText.new(w,365.5,150, 'text'=>'326', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,308,251,242,251,242,274,342,274,342,282,375, 282,
+ 375,274,397,274,397,248,339,248,339,242,308,242,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '302'
+ $floorItems['302'] = i
+ TkcText.new(w,319.5,261, 'text'=>'302', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,70,321,242,321,242,200,259,200,259,203,272,203,
+ 272,193,263,180,242,180,175,180,175,169,156,169,
+ 156,196,177,196,177,228,107,228,70,228,70,275,107,275,
+ 107,248,160,248,160,301,107,301,107,275,70,275,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '306'
+ $floorItems['306'] = i
+ TkcText.new(w,200.5,284.5, 'text'=>'306', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ TkcLine.new(w,341,275,341,283, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,162,197,155,197, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,396,247,399,247, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,399,129,399,311, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,258,202,243,202, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,350,283,350,329, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,251,231,243,231, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,243,220,251,220, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,243,250,243,202, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,155,197,155,190, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,110,192,110,169, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,155,192,110,192, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,155,177,155,169, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,176,197,176,227, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,69,280,69,274, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,21,276,69,276, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,69,262,69,226, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,21,228,69,228, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,21,179,75,179, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,69,179,69,214, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,90,220,90,227, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,90,204,90,202, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,90,203,100,203, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,90,187,90,179, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,90,227,176,227, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,100,179,100,227, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,100,179,87,179, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,96,179,96,129, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,162,169,96,169, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,173,169,176,169, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,182,179,176,179, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,176,129,176,179, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,195,179,226,179, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,224,133,224,179, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,264,179,264,133, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,238,179,264,179, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,273,207,273,193, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,273,235,273,250, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,273,224,273,219, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,273,193,307,193, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,273,222,307,222, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,273,250,307,250, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,384,247,376,247, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,340,206,307,206, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,340,187,340,170, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,340,210,340,201, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,340,247,340,224, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,340,241,307,241, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,376,247,376,170, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,307,250,307,170, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,376,170,307,170, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,315,129,315,170, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,376,283,366,283, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,376,283,376,275, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,399,275,376,275, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,341,275,320,275, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,341,283,350,283, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,298,275,298,329, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,308,275,298,275, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,243,322,243,275, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,243,275,284,275, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,258,322,226,322, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,212,370,212,322, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,214,322,177,322, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,163,370,163,322, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,165,322,129,322, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,84,322,117,322, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,71,322,64,322, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,115,322,115,370, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,66,322,66,370, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,52,322,21,322, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,21,331,0,331, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,21,331,21,133, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,96,133,21,133, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,176,129,96,129, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,315,133,176,133, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,315,129,399,129, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,399,311,350,311, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,350,329,258,329, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,258,322,258,370, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,60,370,258,370, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,60,370,60,391, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,0,391,0,331, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,60,391,0,391, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,307,250,307,242, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,273,250,307,250, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,258,250,243,250, 'fill'=>color, 'tags'=>['floor3','wall'])
+end
+
+# Below is the "main program" that creates the floorplan demonstration.
+
+# toplevel widget
+if defined?($floor_demo) && $floor_demo
+ $floor_demo.destroy
+ $floor_demo = nil
+end
+
+# demo toplevel widget
+$floor_demo = TkToplevel.new {|w|
+ title("Floorplan Canvas Demonstration")
+ iconname("Floorplan")
+ positionWindow(w)
+ geometry('+20+20')
+ minsize(100,100)
+}
+
+# label
+TkLabel.new($floor_demo, 'font'=>$font, 'wraplength'=>'8i', 'justify'=>'left',
+ 'text'=>"This window contains a canvas widget showing the floorplan of Digital Equipment Corporation's Western Research Laboratory. It has three levels. At any given time one of the levels is active, meaning that you can see its room structure. To activate a level, click the left mouse button anywhere on it. As the mouse moves over the active level, the room under the mouse lights up and its room number appears in the \"Room:\" entry. You can also type a room number in the entry and the room will light up."){
+ pack('side'=>'top')
+}
+
+# frame
+$floor_buttons = TkFrame.new($floor_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $floor_demo
+ $floor_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'floor'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}
+$floor_buttons.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+#
+$floorLabels = {}
+$floorItems = {}
+
+# canvas
+if $tk_version =~ /^4\.[01]/
+ $floor_canvas_frame = TkFrame.new($floor_demo,'bd'=>2,'relief'=>'sunken',
+ 'highlightthickness'=>2)
+ $floor_canvas = TkCanvas.new($floor_canvas_frame,
+ 'width'=>900, 'height'=>500, 'borderwidth'=>0,
+ 'highlightthickness'=>0) {|c|
+ TkScrollbar.new($floor_demo, 'orient'=>'horiz',
+ 'command'=>proc{|*args| c.xview(*args)}){|hs|
+ c.xscrollcommand(proc{|first,last| hs.set first,last})
+ pack('side'=>'bottom', 'fill'=>'x')
+ }
+ TkScrollbar.new($floor_demo, 'command'=>proc{|*args| c.yview(*args)}){|vs|
+ c.yscrollcommand(proc{|first,last| vs.set first,last})
+ pack('side'=>'right', 'fill'=>'y')
+ }
+ }
+ $floor_canvas_frame.pack('side'=>'top','fill'=>'both', 'expand'=>'yes')
+ $floor_canvas.pack('expand'=>'yes', 'fill'=>'both')
+
+else
+ TkFrame.new($floor_demo) {|f|
+ pack('side'=>'top', 'fill'=>'both', 'expand'=>'yes')
+
+ h = TkScrollbar.new(f, 'highlightthickness'=>0, 'orient'=>'horizontal')
+ v = TkScrollbar.new(f, 'highlightthickness'=>0, 'orient'=>'vertical')
+
+ TkFrame.new(f, 'bd'=>2, 'relief'=>'sunken') {|f1|
+ $floor_canvas = TkCanvas.new(f1, 'width'=>900, 'height'=>500,
+ 'borderwidth'=>0,
+ 'highlightthickness'=>0) {
+ xscrollcommand(proc{|first,last| h.set first,last})
+ yscrollcommand(proc{|first,last| v.set first,last})
+ pack('expand'=>'yes', 'fill'=>'both')
+ }
+ grid('padx'=>1, 'pady'=>1, 'row'=>0, 'column'=>0,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ }
+
+ v.grid('padx'=>1, 'pady'=>1, 'row'=>0, 'column'=>1,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ h.grid('padx'=>1, 'pady'=>1, 'row'=>1, 'column'=>0,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+
+ TkGrid.rowconfigure(f, 0, 'weight'=>1, 'minsize'=>0)
+ TkGrid.columnconfigure(f, 0, 'weight'=>1, 'minsize'=>0)
+
+ pack('expand'=>'yes', 'fill'=>'both', 'padx'=>1, 'pady'=>1)
+
+ v.command(proc{|*args| c.yview(*args)})
+ h.command(proc{|*args| c.xview(*args)})
+ }
+end
+
+
+# Create an entry for displaying and typing in current room.
+
+$currentRoom = TkVariable.new
+$floor_entry = TkEntry.new($floor_canvas, 'width'=>10, 'relief'=>'sunken',
+ 'bd'=>2, 'textvariable'=>$currentRoom)
+
+# Choose colors, then fill in the floorplan.
+
+$floor_colors = {}
+if TkWinfo.depth($floor_canvas) > 1
+ $floor_colors['bg1'] = '#a9c1da'
+ $floor_colors['outline1'] = '#77889a'
+ $floor_colors['bg2'] = '#9ab0c6'
+ $floor_colors['outline2'] = '#687786'
+ $floor_colors['bg3'] = '#8ba0b3'
+ $floor_colors['outline3'] = '#596673'
+ $floor_colors['offices'] = 'Black'
+ $floor_colors['active'] = '#c4d1df'
+else
+ $floor_colors['bg1'] = 'white'
+ $floor_colors['outline1'] = 'black'
+ $floor_colors['bg2'] = 'white'
+ $floor_colors['outline2'] = 'black'
+ $floor_colors['bg3'] = 'white'
+ $floor_colors['outline3'] = 'black'
+ $floor_colors['offices'] = 'Black'
+ $floor_colors['active'] = 'black'
+end
+
+$activeFloor = ''
+floorDisplay $floor_canvas,3
+
+# Set up event bindings for canvas:
+
+$floor_canvas.itembind('floor1', '1', proc{floorDisplay $floor_canvas,1})
+$floor_canvas.itembind('floor2', '1', proc{floorDisplay $floor_canvas,2})
+$floor_canvas.itembind('floor3', '1', proc{floorDisplay $floor_canvas,3})
+$floor_canvas.itembind('room', 'Enter', proc{newRoom $floor_canvas})
+$floor_canvas.itembind('room', 'Leave', proc{$currentRoom.value = ''})
+$floor_canvas.bind('2', proc{|x,y| $floor_canvas.scan_mark x,y}, '%x %y')
+$floor_canvas.bind('B2-Motion',
+ proc{|x,y| $floor_canvas.scan_dragto x,y}, '%x %y')
+$floor_canvas.bind('Destroy', proc{$currentRoom.unset})
+$currentRoom.value = ''
+$currentRoom.trace('w',proc{roomChanged $floor_canvas})
+
diff --git a/ext/tk/sample/demos-en/form.rb b/ext/tk/sample/demos-en/form.rb
new file mode 100644
index 0000000000..dbb14302dc
--- /dev/null
+++ b/ext/tk/sample/demos-en/form.rb
@@ -0,0 +1,62 @@
+#
+# form widget demo (called by 'widget')
+#
+
+# toplevel widget
+if defined?($form_demo) && $form_demo
+ $form_demo.destroy
+ $form_demo = nil
+end
+
+# demo toplevel widget
+$form_demo = TkToplevel.new {|w|
+ title("Form Demonstration")
+ iconname("form")
+ positionWindow(w)
+}
+
+# label
+msg = TkLabel.new($form_demo) {
+ font $font
+ wraplength '4i'
+ justify 'left'
+ text "This window contains a simple form where you can type in the various entries and use tabs to move circularly between the entries."
+}
+msg.pack('side'=>'top', 'fill'=>'x')
+
+# frame
+TkFrame.new($form_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $form_demo
+ $form_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'form'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# entry
+form_data = []
+(1..5).each{|i|
+ f = TkFrame.new($form_demo, 'bd'=>2)
+ e = TkEntry.new(f, 'relief'=>'sunken', 'width'=>40)
+ l = TkLabel.new(f)
+ e.pack('side'=>'right')
+ l.pack('side'=>'left')
+ form_data[i] = {'frame'=>f, 'entry'=>e, 'label'=>l}
+}
+
+#
+form_data[1]['label'].text('Name:')
+form_data[2]['label'].text('Address:')
+form_data[5]['label'].text('Phone:')
+
+# pack
+(1..5).each{|i| form_data[i]['frame'].pack('side'=>'top', 'fill'=>'x')}
+
diff --git a/ext/tk/sample/demos-en/hello b/ext/tk/sample/demos-en/hello
new file mode 100644
index 0000000000..f06eabe518
--- /dev/null
+++ b/ext/tk/sample/demos-en/hello
@@ -0,0 +1,14 @@
+#!/usr/bin/env ruby
+require 'tk'
+
+unless /^8\.[1-9]/ =~ Tk::TCL_VERSION && !Tk::JAPANIZED_TK
+ require 'tkencoding'
+end
+
+TkButton.new(nil,
+ 'text'=>"Hello Ruby world!",
+ 'font'=>TkFont.new('k14'),
+ 'command'=>proc{print "Hello Ruby world!\n"; exit}
+).pack
+
+Tk.mainloop
diff --git a/ext/tk/sample/demos-en/hscale.rb b/ext/tk/sample/demos-en/hscale.rb
new file mode 100644
index 0000000000..14e395b61a
--- /dev/null
+++ b/ext/tk/sample/demos-en/hscale.rb
@@ -0,0 +1,74 @@
+require "tkcanvas"
+
+if defined?($hscale_demo) && $hscale_demo
+ $hscale_demo.destroy
+ $hscale_demo = nil
+end
+
+$hscale_demo = TkToplevel.new {|w|
+ title("Horizontal Scale Demonstration")
+ iconname("hscale")
+}
+positionWindow($hscale_demo)
+
+
+msg = TkLabel.new($hscale_demo) {
+ font $font
+ wraplength '3.5i'
+ justify 'left'
+ text "An arrow and a horizontal scale are displayed below. If you click or drag mouse button 1 in the scale, you can change the length of the arrow."
+}
+msg.pack('side'=>'top')
+
+TkFrame.new($hscale_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc {
+ tmppath = $hscale_demo
+ $hscale_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc { showCode 'hscale' }
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+TkFrame.new($hscale_demo) {|frame|
+ canvas = TkCanvas.new(frame) {|c|
+ width 50
+ height 50
+ bd 0
+ highlightthickness 0
+ TkcPolygon.new(c, '0', '0', '1', '1', '2', '2') {
+ fill 'DeepSkyBlue'
+ tags 'poly'
+ }
+ TkcLine.new(c, '0', '0', '1', '1', '2', '2', '0', '0') {
+ fill 'black'
+ tags 'line'
+ }
+ }.pack('side'=>'top', 'expand'=>'yes', 'anchor'=>'s', 'fill'=>'x', 'padx'=>'15')
+ scale = TkScale.new(frame) {
+ orient 'horizontal'
+ length 284
+ from 0
+ to 250
+ command proc{|value| setWidth(canvas, value)}
+ tickinterval 50
+ }.pack('side'=>'bottom', 'expand'=>'yes', 'anchor'=>'n')
+ scale.set 75
+}.pack('side'=>'top', 'fill'=>'x')
+
+
+def setWidth(w, width)
+ width = width + 21
+ x2 = width - 30
+ if x2 < 21
+ x2 = 21
+ end
+ w.coords 'poly',20,15,20,35,x2,35,x2,45,width,25,x2,5,x2,15,20,15
+ w.coords 'line',20,15,20,35,x2,35,x2,45,width,25,x2,5,x2,15,20,15
+end
diff --git a/ext/tk/sample/demos-en/icon.rb b/ext/tk/sample/demos-en/icon.rb
new file mode 100644
index 0000000000..7557257ef4
--- /dev/null
+++ b/ext/tk/sample/demos-en/icon.rb
@@ -0,0 +1,95 @@
+# icon.rb
+#
+# This demonstration script creates a toplevel window containing
+# buttons that display bitmaps instead of text.
+#
+# iconic button widget demo (called by 'widget')
+#
+
+# toplevel widget
+if defined?($icon_demo) && $icon_demo
+ $icon_demo.destroy
+ $icon_demo = nil
+end
+
+# demo toplevel widget
+$icon_demo = TkToplevel.new {|w|
+ title("Iconic Button Demonstration")
+ iconname("icon")
+ positionWindow(w)
+}
+
+# label
+msg = TkLabel.new($icon_demo) {
+ font $font
+ wraplength '5i'
+ justify 'left'
+ text "This window shows three ways of using bitmaps or images in radiobuttons and checkbuttons. On the left are two radiobuttons, each of which displays a bitmap and an indicator. In the middle is a checkbutton that displays a different image depending on whether it is selected or not. On the right is a checkbutton that displays a single bitmap but changes its background color to indicate whether or not it is selected. (This change is visible when the mouse pointer is not directy over the button.)"
+}
+msg.pack('side'=>'top')
+
+# frame
+TkFrame.new($icon_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $icon_demo
+ $icon_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'icon'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# image
+flagup = \
+TkBitmapImage.new('file'=>[$demo_dir,
+ 'images','flagup.xbm'].join(File::Separator),
+ 'maskfile'=>\
+ [$demo_dir,'images','flagup.xbm'].join(File::Separator))
+flagdown = \
+TkBitmapImage.new('file'=>[$demo_dir,
+ 'images','flagdown.xbm'].join(File::Separator),
+ 'maskfile'=>\
+ [$demo_dir,'images','flagdown.xbm'].join(File::Separator))
+
+# ÊÑ¿ôÀ¸À®
+letters = TkVariable.new
+
+# frame
+TkFrame.new($icon_demo, 'borderwidth'=>10){|w|
+ TkFrame.new(w) {|f|
+ TkRadioButton.new(f){
+ bitmap '@' + [$demo_dir,'images','letters.xbm'].join(File::Separator)
+ variable letters
+ value 'full'
+ }.pack('side'=>'top', 'expand'=>'yes')
+
+ TkRadioButton.new(f){
+ bitmap '@' + [$demo_dir,'images','noletter.xbm'].join(File::Separator)
+ variable letters
+ value 'empty'
+ }.pack('side'=>'top', 'expand'=>'yes')
+
+ }.pack('side'=>'left', 'expand'=>'yes', 'padx'=>'5m')
+
+ TkCheckButton.new(w) {
+ image flagdown
+ selectimage flagup
+ indicatoron 0
+ selectcolor self['background']
+ }.pack('side'=>'left', 'expand'=>'yes', 'padx'=>'5m')
+
+ TkCheckButton.new(w) {
+ bitmap '@' + [$demo_dir,'images','letters.xbm'].join(File::Separator)
+ indicatoron 0
+ selectcolor 'SeaGreen1'
+ }.pack('side'=>'left', 'expand'=>'yes', 'padx'=>'5m')
+
+}.pack('side'=>'top')
+
diff --git a/ext/tk/sample/demos-en/image1.rb b/ext/tk/sample/demos-en/image1.rb
new file mode 100644
index 0000000000..8d39b20049
--- /dev/null
+++ b/ext/tk/sample/demos-en/image1.rb
@@ -0,0 +1,60 @@
+## image1.rb
+#
+# This demonstration script displays two image widgets.
+#
+# two image widgets demo (called by 'widget')
+#
+
+# toplevel widget
+if defined?($image1_demo) && $image1_demo
+ $image1_demo.destroy
+ $image1_demo = nil
+end
+
+# demo toplevel widget
+$image1_demo = TkToplevel.new {|w|
+ title('Image Demonstration #1')
+ iconname("Image1")
+ positionWindow(w)
+}
+
+# label
+msg = TkLabel.new($image1_demo) {
+ font $font
+ wraplength '4i'
+ justify 'left'
+ text "This demonstration displays two images, each in a separate label widget."
+}
+msg.pack('side'=>'top')
+
+# frame
+TkFrame.new($image1_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $image1_demo
+ $image1_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'image1'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# image
+image1a = \
+TkPhotoImage.new('file'=>[$demo_dir,
+ 'images','earth.gif'].join(File::Separator))
+image1b = \
+TkPhotoImage.new('file'=>[$demo_dir,
+ 'images','earthris.gif'].join(File::Separator))
+
+# label
+[ TkLabel.new($image1_demo, 'image'=>image1a, 'bd'=>1, 'relief'=>'sunken'),
+ TkLabel.new($image1_demo, 'image'=>image1b, 'bd'=>1, 'relief'=>'sunken')
+].each{|w| w.pack('side'=>'top', 'padx'=>'.5m', 'pady'=>'.5m')}
+
diff --git a/ext/tk/sample/demos-en/image2.rb b/ext/tk/sample/demos-en/image2.rb
new file mode 100644
index 0000000000..9924195675
--- /dev/null
+++ b/ext/tk/sample/demos-en/image2.rb
@@ -0,0 +1,105 @@
+# image2.rb
+#
+# This demonstration script creates a simple collection of widgets
+# that allow you to select and view images in a Tk label.
+#
+# widget demo 'load image' (called by 'widget')
+#
+
+# toplevel widget
+if defined?($image2_demo) && $image2_demo
+ $image2_demo.destroy
+ $image2_demo = nil
+end
+
+# demo toplevel widget
+$image2_demo = TkToplevel.new {|w|
+ title('Image Demonstration #2')
+ iconname("Image2")
+ positionWindow(w)
+}
+
+# label
+msg = TkLabel.new($image2_demo) {
+ font $font
+ wraplength '4i'
+ justify 'left'
+ text "This demonstration allows you to view images using a Tk \"photo\" image. First type a directory name in the listbox, then press Enter to load the directory into the listbox. Then double-click on a file name in the listbox to see that image."
+}
+msg.pack('side'=>'top')
+
+# frame
+TkFrame.new($image2_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $image2_demo
+ $image2_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'image2'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# ÊÑ¿ôÀ¸À®
+$dirName = TkVariable.new([$demo_dir,'images'].join(File::Separator))
+
+# image
+$image2a = TkPhotoImage.new
+
+#
+TkLabel.new($image2_demo, 'text'=>'Directory:')\
+.pack('side'=>'top', 'anchor'=>'w')
+
+image2_e = TkEntry.new($image2_demo) {
+ width 30
+ textvariable $dirName
+}.pack('side'=>'top', 'anchor'=>'w')
+
+TkFrame.new($image2_demo, 'height'=>'3m', 'width'=>20)\
+.pack('side'=>'top', 'anchor'=>'w')
+
+TkLabel.new($image2_demo, 'text'=>'File:')\
+.pack('side'=>'top', 'anchor'=>'w')
+
+TkFrame.new($image2_demo){|w|
+ s = TkScrollbar.new(w)
+ l = TkListbox.new(w) {
+ width 20
+ height 10
+ yscrollcommand proc{|first,last| s.set first,last}
+ }
+ s.command(proc{|*args| l.yview(*args)})
+ l.pack('side'=>'left', 'expand'=>'yes', 'fill'=>'y')
+ s.pack('side'=>'left', 'expand'=>'yes', 'fill'=>'y')
+ #l.insert(0,'earth.gif', 'earthris.gif', 'mickey.gif', 'teapot.ppm')
+ l.insert(0,'earth.gif', 'earthris.gif', 'teapot.ppm')
+ l.bind('Double-1', proc{|x,y| loadImage $image2a,l,x,y}, '%x %y')
+
+ image2_e.bind 'Return', proc{loadDir l}
+
+}.pack('side'=>'top', 'anchor'=>'w')
+
+# image
+[ TkFrame.new($image2_demo, 'height'=>'3m', 'width'=>20),
+ TkLabel.new($image2_demo, 'text'=>'Image:'),
+ TkLabel.new($image2_demo, 'image'=>$image2a)
+].each{|w| w.pack('side'=>'top', 'anchor'=>'w')}
+
+#
+def loadDir(w)
+ w.delete(0,'end')
+ Dir.glob([$dirName,'*'].join(File::Separator)).sort.each{|f|
+ w.insert('end',File.basename(f))
+ }
+end
+
+def loadImage(img,w,x,y)
+ img.file([$dirName, w.get("@#{x},#{y}")].join(File::Separator))
+end
+
diff --git a/ext/tk/sample/demos-en/images/earth.gif b/ext/tk/sample/demos-en/images/earth.gif
new file mode 100644
index 0000000000..3ae4a9ce01
--- /dev/null
+++ b/ext/tk/sample/demos-en/images/earth.gif
Binary files differ
diff --git a/ext/tk/sample/demos-en/images/earthris.gif b/ext/tk/sample/demos-en/images/earthris.gif
new file mode 100644
index 0000000000..48f08c4421
--- /dev/null
+++ b/ext/tk/sample/demos-en/images/earthris.gif
Binary files differ
diff --git a/ext/tk/sample/demos-en/images/face.xbm b/ext/tk/sample/demos-en/images/face.xbm
new file mode 100644
index 0000000000..03d829f4d1
--- /dev/null
+++ b/ext/tk/sample/demos-en/images/face.xbm
@@ -0,0 +1,173 @@
+#define face_width 108
+#define face_height 144
+#define face_x_hot 48
+#define face_y_hot 80
+static char face_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x09,
+ 0x20, 0x80, 0x24, 0x05, 0x00, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x88,
+ 0x24, 0x20, 0x80, 0x24, 0x00, 0x00, 0x00, 0x10, 0x80, 0x04, 0x00, 0x01,
+ 0x00, 0x01, 0x40, 0x0a, 0x09, 0x00, 0x92, 0x04, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x40, 0x12, 0x00, 0x00, 0x10, 0x40, 0x00, 0x00, 0x84,
+ 0x24, 0x40, 0x22, 0xa8, 0x02, 0x14, 0x84, 0x92, 0x40, 0x42, 0x12, 0x04,
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x52, 0x11, 0x00, 0x12, 0x00,
+ 0x40, 0x02, 0x00, 0x20, 0x00, 0x08, 0x00, 0xaa, 0x02, 0x54, 0x85, 0x24,
+ 0x00, 0x10, 0x12, 0x00, 0x00, 0x81, 0x44, 0x00, 0x90, 0x5a, 0x00, 0xea,
+ 0x1b, 0x00, 0x80, 0x40, 0x40, 0x02, 0x00, 0x08, 0x00, 0x20, 0xa2, 0x05,
+ 0x8a, 0xb4, 0x6e, 0x45, 0x12, 0x04, 0x08, 0x00, 0x00, 0x00, 0x10, 0x02,
+ 0xa8, 0x92, 0x00, 0xda, 0x5f, 0x10, 0x00, 0x10, 0xa1, 0x04, 0x20, 0x41,
+ 0x02, 0x00, 0x5a, 0x25, 0xa0, 0xff, 0xfb, 0x05, 0x41, 0x02, 0x04, 0x00,
+ 0x00, 0x08, 0x40, 0x80, 0xec, 0x9b, 0xec, 0xfe, 0x7f, 0x01, 0x04, 0x20,
+ 0x90, 0x02, 0x04, 0x00, 0x08, 0x20, 0xfb, 0x2e, 0xf5, 0xff, 0xff, 0x57,
+ 0x00, 0x04, 0x02, 0x00, 0x00, 0x20, 0x01, 0xc1, 0x6e, 0xab, 0xfa, 0xff,
+ 0xff, 0x05, 0x90, 0x20, 0x48, 0x02, 0x00, 0x04, 0x20, 0xa8, 0xdf, 0xb5,
+ 0xfe, 0xff, 0xff, 0x0b, 0x01, 0x00, 0x01, 0x00, 0x80, 0x80, 0x04, 0xe0,
+ 0xbb, 0xef, 0xff, 0xff, 0x7f, 0x01, 0x00, 0x04, 0x48, 0x02, 0x00, 0x20,
+ 0x80, 0xf4, 0x6f, 0xfb, 0xff, 0xff, 0xff, 0x20, 0x90, 0x40, 0x02, 0x00,
+ 0x00, 0x04, 0x08, 0xb8, 0xf6, 0xff, 0xff, 0xdf, 0xbe, 0x12, 0x45, 0x10,
+ 0x90, 0x04, 0x90, 0x00, 0x22, 0xfa, 0xff, 0xff, 0xff, 0xbb, 0xd7, 0xe9,
+ 0x3a, 0x02, 0x02, 0x00, 0x04, 0x90, 0x80, 0xfe, 0xdf, 0xf6, 0xb7, 0xef,
+ 0xbe, 0x56, 0x57, 0x40, 0x48, 0x09, 0x00, 0x04, 0x00, 0xfa, 0xf5, 0xdf,
+ 0xed, 0x5a, 0xd5, 0xea, 0xbd, 0x09, 0x00, 0x00, 0x40, 0x00, 0x92, 0xfe,
+ 0xbf, 0x7d, 0xb7, 0x6a, 0x55, 0xbf, 0xf7, 0x02, 0x11, 0x01, 0x00, 0x91,
+ 0x00, 0xff, 0xff, 0xaf, 0x55, 0x55, 0x5b, 0xeb, 0xef, 0x22, 0x04, 0x04,
+ 0x04, 0x00, 0xa4, 0xff, 0xf7, 0xad, 0xaa, 0xaa, 0xaa, 0xbe, 0xfe, 0x03,
+ 0x20, 0x00, 0x10, 0x44, 0x80, 0xff, 0x7f, 0x55, 0x12, 0x91, 0x2a, 0xeb,
+ 0xbf, 0x0b, 0x82, 0x02, 0x00, 0x00, 0xd1, 0x7f, 0xdf, 0xa2, 0xa4, 0x54,
+ 0x55, 0xfd, 0xfd, 0x47, 0x08, 0x08, 0x00, 0x21, 0xe4, 0xff, 0x37, 0x11,
+ 0x09, 0xa5, 0xaa, 0xb6, 0xff, 0x0d, 0x80, 0x00, 0x00, 0x04, 0xd0, 0xff,
+ 0x4f, 0x44, 0x20, 0x48, 0x55, 0xfb, 0xff, 0x27, 0x11, 0x02, 0x40, 0x40,
+ 0xe2, 0xfb, 0x15, 0x11, 0x4a, 0x55, 0x4a, 0x7d, 0xf7, 0x0f, 0x00, 0x00,
+ 0x04, 0x08, 0xf8, 0xdf, 0x52, 0x44, 0x01, 0x52, 0xb5, 0xfa, 0xff, 0x0f,
+ 0x49, 0x02, 0x00, 0x02, 0xe9, 0xf6, 0x0a, 0x11, 0xa4, 0x88, 0x4a, 0x6d,
+ 0xff, 0x5f, 0x00, 0x00, 0x10, 0x20, 0xf0, 0x2f, 0x21, 0x44, 0x10, 0x52,
+ 0xb5, 0xfa, 0xff, 0x0f, 0x44, 0x04, 0x80, 0x08, 0xf8, 0xab, 0x8a, 0x00,
+ 0x81, 0xa4, 0xd4, 0xd6, 0xfe, 0x2f, 0x00, 0x00, 0x04, 0x40, 0xb5, 0x2d,
+ 0x21, 0x08, 0x04, 0x90, 0xaa, 0xfa, 0xff, 0x1f, 0x11, 0x01, 0x00, 0x04,
+ 0xf0, 0x57, 0x0a, 0x22, 0x40, 0x4a, 0xda, 0x5e, 0xfb, 0x1f, 0x40, 0x00,
+ 0x40, 0x20, 0xba, 0x95, 0x90, 0x00, 0x01, 0xa0, 0xaa, 0xea, 0xff, 0x5f,
+ 0x02, 0x02, 0x00, 0x01, 0xe8, 0x57, 0x05, 0x00, 0x00, 0x12, 0xd5, 0xfe,
+ 0xfd, 0x1f, 0x48, 0x00, 0x04, 0x48, 0x7a, 0x95, 0x08, 0x02, 0x10, 0x40,
+ 0xaa, 0x55, 0xf7, 0x1f, 0x00, 0x09, 0x20, 0x00, 0xf8, 0x57, 0x22, 0x10,
+ 0x00, 0x28, 0xa9, 0xfa, 0xff, 0x5f, 0x02, 0x00, 0x00, 0x49, 0xdd, 0x29,
+ 0x01, 0x00, 0x80, 0x80, 0xaa, 0xd7, 0xff, 0x0f, 0x10, 0x00, 0x08, 0x00,
+ 0xf8, 0x96, 0x08, 0x00, 0x00, 0x20, 0x54, 0xfa, 0xee, 0x3f, 0x81, 0x04,
+ 0x40, 0x24, 0xfe, 0x55, 0x82, 0x00, 0x00, 0x82, 0xd2, 0xad, 0xff, 0x0f,
+ 0x08, 0x00, 0x04, 0x80, 0x6c, 0x97, 0x00, 0x00, 0x02, 0x20, 0xa9, 0xf6,
+ 0xdf, 0x5f, 0x00, 0x02, 0x20, 0x09, 0xfa, 0x49, 0x12, 0x00, 0x20, 0x84,
+ 0x54, 0xdb, 0xfe, 0x1f, 0x91, 0x00, 0x00, 0x00, 0xf8, 0x2b, 0x00, 0x20,
+ 0x00, 0x40, 0xa4, 0xf6, 0xbb, 0x1f, 0x04, 0x00, 0x44, 0x92, 0x7e, 0x95,
+ 0x02, 0x00, 0x00, 0x89, 0xaa, 0xdd, 0xff, 0x1f, 0x20, 0x09, 0x10, 0x00,
+ 0xf4, 0x57, 0x20, 0x01, 0x08, 0x20, 0xa9, 0x76, 0xff, 0x5f, 0x02, 0x00,
+ 0x00, 0x21, 0xfc, 0x4a, 0x05, 0x00, 0x01, 0x80, 0x54, 0xdb, 0xff, 0x1e,
+ 0x08, 0x02, 0x04, 0x08, 0xf9, 0x2b, 0x00, 0x00, 0x40, 0x28, 0xd2, 0xf6,
+ 0xff, 0xbf, 0x80, 0x00, 0x90, 0x00, 0xbc, 0x92, 0x08, 0x10, 0x00, 0x82,
+ 0x54, 0xdb, 0xff, 0x1f, 0x20, 0x00, 0x00, 0x44, 0xf9, 0x55, 0x02, 0x01,
+ 0x00, 0x20, 0xaa, 0xbd, 0xfd, 0x3f, 0x08, 0x04, 0x04, 0x10, 0xf4, 0x2a,
+ 0x01, 0x00, 0x22, 0x80, 0xd4, 0xf6, 0xff, 0x5f, 0x82, 0x00, 0x40, 0x02,
+ 0xf8, 0x55, 0x20, 0x00, 0x00, 0x50, 0x6a, 0xdf, 0xfe, 0x3f, 0x00, 0x00,
+ 0x00, 0x48, 0xe9, 0x4a, 0x05, 0x08, 0x00, 0xa5, 0xd5, 0xf5, 0xff, 0x3f,
+ 0x10, 0x01, 0x10, 0x01, 0xb0, 0xab, 0x92, 0x02, 0x40, 0xf8, 0xbf, 0xde,
+ 0xfe, 0x5f, 0x02, 0x04, 0x04, 0x48, 0xfa, 0xd4, 0x6f, 0x20, 0x84, 0xef,
+ 0xff, 0xfb, 0xff, 0x1f, 0x20, 0x00, 0x00, 0x00, 0xe0, 0xed, 0xbf, 0x0b,
+ 0xa1, 0x7e, 0xff, 0xbf, 0xfd, 0x5f, 0x04, 0x01, 0x20, 0x49, 0xd2, 0xfb,
+ 0xfe, 0x55, 0xd4, 0xff, 0xff, 0xf6, 0xff, 0x07, 0x00, 0x04, 0x00, 0x00,
+ 0xc0, 0xaa, 0xfb, 0x2b, 0xa2, 0xfe, 0xff, 0xdf, 0xee, 0x1f, 0x91, 0x00,
+ 0x82, 0xa4, 0xa4, 0xf5, 0xff, 0x57, 0xd5, 0xff, 0xbf, 0xfd, 0xff, 0x4d,
+ 0x00, 0x00, 0x20, 0x00, 0x88, 0x5b, 0xff, 0x2f, 0x69, 0xff, 0xff, 0xdb,
+ 0xfe, 0x1f, 0x24, 0x02, 0x00, 0x49, 0xa2, 0xd6, 0xff, 0x5f, 0xea, 0xff,
+ 0x7f, 0x7f, 0x7f, 0x0d, 0x00, 0x00, 0x10, 0x00, 0x40, 0xab, 0xf7, 0xbb,
+ 0xf0, 0xdf, 0xff, 0xd5, 0xff, 0xbf, 0x82, 0x04, 0x42, 0x24, 0x91, 0xd5,
+ 0xaa, 0xae, 0xd4, 0xaa, 0x52, 0x7b, 0xff, 0x15, 0x08, 0x00, 0x00, 0x01,
+ 0x04, 0x55, 0xd5, 0x55, 0x70, 0x5b, 0x75, 0xdd, 0xdf, 0x1f, 0x40, 0x00,
+ 0x08, 0x48, 0xa0, 0x4a, 0xa9, 0x56, 0xea, 0x56, 0xad, 0x6a, 0x7d, 0x9b,
+ 0x04, 0x01, 0x00, 0x02, 0x42, 0x2a, 0xd5, 0xaa, 0xa8, 0xaa, 0xaa, 0xfa,
+ 0xdf, 0x2f, 0x10, 0x04, 0x22, 0x48, 0x08, 0x45, 0x2a, 0x15, 0x68, 0x55,
+ 0x55, 0xd7, 0x76, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x40, 0x2a, 0x80, 0xa0,
+ 0xb2, 0x09, 0x48, 0xb9, 0xdf, 0x17, 0x22, 0x01, 0x00, 0x24, 0x45, 0x8a,
+ 0x24, 0x4a, 0x54, 0x51, 0x91, 0xf6, 0x6e, 0x4b, 0x00, 0x04, 0x90, 0x00,
+ 0x80, 0x52, 0x00, 0x20, 0x69, 0x05, 0xa4, 0xaa, 0xff, 0x1e, 0x48, 0x00,
+ 0x02, 0x92, 0x08, 0x05, 0x81, 0x94, 0xd4, 0x92, 0x40, 0xfd, 0xb6, 0x8b,
+ 0x00, 0x01, 0x40, 0x00, 0x82, 0x54, 0x00, 0x48, 0x68, 0x05, 0x90, 0xa4,
+ 0xef, 0x06, 0x24, 0x00, 0x08, 0x12, 0x10, 0x05, 0x00, 0x10, 0xb5, 0x01,
+ 0x42, 0xfb, 0xbf, 0x43, 0x00, 0x09, 0x00, 0x40, 0x81, 0xa8, 0x08, 0x4a,
+ 0xaa, 0x96, 0x90, 0xac, 0x6d, 0x15, 0x22, 0x00, 0x20, 0x09, 0x04, 0x15,
+ 0x80, 0x28, 0xdc, 0x01, 0x24, 0xfb, 0xbf, 0x01, 0x80, 0x04, 0x09, 0x00,
+ 0x40, 0x48, 0x02, 0x45, 0xb2, 0x2e, 0x41, 0x6d, 0xef, 0x05, 0x11, 0x00,
+ 0x40, 0x52, 0x02, 0x15, 0x29, 0x2a, 0xac, 0x42, 0x54, 0xfb, 0x3b, 0x51,
+ 0x84, 0x00, 0x08, 0x00, 0x20, 0x54, 0x80, 0x05, 0xb5, 0x3d, 0xa2, 0xb6,
+ 0xdf, 0x00, 0x20, 0x04, 0x20, 0x49, 0x89, 0xa8, 0x6a, 0x29, 0xac, 0xd6,
+ 0x54, 0xff, 0x3f, 0x84, 0x00, 0x01, 0x04, 0x10, 0x00, 0x94, 0xa8, 0x56,
+ 0xda, 0x5f, 0xab, 0xd5, 0x1e, 0x10, 0x48, 0x00, 0x90, 0x82, 0x48, 0xa8,
+ 0xb2, 0xac, 0xfd, 0x55, 0xd5, 0xfe, 0x9f, 0x80, 0x00, 0x0a, 0x02, 0x08,
+ 0x02, 0x55, 0x5a, 0x75, 0xff, 0xaf, 0xb6, 0xf7, 0x2d, 0x12, 0x92, 0x00,
+ 0x10, 0x20, 0x10, 0xa8, 0x54, 0xd5, 0xbf, 0x5d, 0xad, 0xdd, 0x0f, 0x00,
+ 0x00, 0x04, 0x40, 0x09, 0x84, 0xa8, 0xaa, 0x5a, 0xed, 0xeb, 0x6a, 0xff,
+ 0x9f, 0xa4, 0x24, 0x01, 0x02, 0xa0, 0x20, 0x50, 0x55, 0xd5, 0xbe, 0xae,
+ 0xad, 0xfd, 0x16, 0x00, 0x10, 0x04, 0x20, 0x0a, 0x08, 0xb4, 0xaa, 0x95,
+ 0xaa, 0x7b, 0xb7, 0xdb, 0x5f, 0x92, 0x04, 0x01, 0x84, 0x20, 0x21, 0x51,
+ 0xd5, 0x2a, 0xa9, 0xee, 0xd5, 0xfe, 0x0d, 0x00, 0x20, 0x04, 0x10, 0x00,
+ 0x08, 0x50, 0xe9, 0xd7, 0xd4, 0xfb, 0xb5, 0xff, 0x9f, 0x24, 0x09, 0x01,
+ 0x42, 0x4a, 0xa2, 0x64, 0xd5, 0x55, 0x7b, 0x7f, 0xda, 0x7d, 0x4f, 0x00,
+ 0x20, 0x04, 0x00, 0x80, 0x00, 0xa0, 0x2a, 0x13, 0x84, 0x6a, 0x55, 0xff,
+ 0x1d, 0x48, 0x8a, 0x00, 0x94, 0x24, 0x8a, 0xc8, 0xaa, 0x42, 0x20, 0x5d,
+ 0xf5, 0xff, 0x5f, 0x01, 0x00, 0x02, 0x01, 0x00, 0x20, 0xa2, 0x4a, 0x1a,
+ 0x82, 0x56, 0xda, 0xbd, 0x3f, 0x92, 0x92, 0x00, 0x90, 0x92, 0x00, 0x40,
+ 0x95, 0x6a, 0xf4, 0x55, 0x6d, 0xff, 0xd6, 0x00, 0x00, 0x0a, 0x04, 0x20,
+ 0x14, 0x49, 0x4b, 0xaa, 0xaa, 0x56, 0xf5, 0xff, 0xbf, 0xab, 0xa4, 0x00,
+ 0x20, 0x89, 0x40, 0x80, 0xaa, 0xaa, 0xaa, 0xaa, 0xde, 0xbf, 0xeb, 0x03,
+ 0x00, 0x02, 0x04, 0x02, 0x0a, 0x10, 0x2b, 0x2a, 0x55, 0x5b, 0xf5, 0xff,
+ 0xd7, 0x2f, 0x92, 0x00, 0x10, 0x28, 0x21, 0x01, 0x56, 0x95, 0xa0, 0x56,
+ 0xdf, 0xef, 0xea, 0x87, 0x40, 0x0a, 0x42, 0x41, 0x00, 0x90, 0xaa, 0x52,
+ 0xb6, 0xad, 0xfa, 0xff, 0xd5, 0x2f, 0x14, 0x00, 0x00, 0x04, 0x95, 0x04,
+ 0xaa, 0xac, 0x55, 0x6b, 0xff, 0xb7, 0xea, 0x9f, 0x40, 0x02, 0x28, 0x51,
+ 0x00, 0x40, 0x58, 0xd5, 0xda, 0xd6, 0x6e, 0x7f, 0xf9, 0x3f, 0x12, 0x04,
+ 0x02, 0x04, 0x49, 0x25, 0x55, 0xaa, 0x77, 0xab, 0xff, 0x2b, 0xfd, 0x3f,
+ 0x48, 0x01, 0x20, 0x41, 0x00, 0x00, 0x58, 0xa9, 0xda, 0xea, 0xfd, 0xaf,
+ 0xfa, 0xff, 0x02, 0x04, 0x08, 0x14, 0x29, 0x49, 0x52, 0x55, 0x55, 0x55,
+ 0xff, 0x8d, 0xfe, 0x3f, 0xa8, 0x00, 0x02, 0x41, 0x00, 0x02, 0xa0, 0xa2,
+ 0xaa, 0xea, 0xff, 0x53, 0xfd, 0xff, 0x02, 0x04, 0x50, 0x04, 0x25, 0xa8,
+ 0x54, 0x49, 0x52, 0xb5, 0xbf, 0x8a, 0xfe, 0xff, 0xa9, 0x08, 0x04, 0x50,
+ 0x80, 0x02, 0xa1, 0x2a, 0x95, 0xea, 0xff, 0xa1, 0xff, 0xff, 0x03, 0x02,
+ 0x90, 0x02, 0x09, 0x08, 0x44, 0x49, 0x52, 0xbd, 0x7f, 0xca, 0xff, 0xff,
+ 0x2b, 0x09, 0x04, 0x48, 0x40, 0x82, 0x90, 0x56, 0xa9, 0xf6, 0xbf, 0xd0,
+ 0xff, 0xff, 0x47, 0x00, 0x50, 0x02, 0x15, 0x11, 0x40, 0x95, 0xaa, 0xfd,
+ 0x2f, 0xe9, 0xff, 0xff, 0x8f, 0x0a, 0x84, 0x50, 0x40, 0x84, 0x14, 0xaa,
+ 0x6a, 0xff, 0x5f, 0xf2, 0xff, 0xff, 0x7f, 0x00, 0x10, 0x02, 0x09, 0x10,
+ 0x40, 0x7d, 0xf7, 0xff, 0x0b, 0xfc, 0xff, 0xff, 0xaf, 0x02, 0x84, 0x50,
+ 0x42, 0x85, 0x12, 0xd0, 0xdd, 0xff, 0xa7, 0xf2, 0xff, 0xff, 0xff, 0x04,
+ 0x00, 0x0a, 0x08, 0x10, 0x48, 0xf8, 0xff, 0xff, 0x0a, 0xfe, 0xff, 0xff,
+ 0x7f, 0x03, 0xa4, 0x80, 0xa2, 0x8a, 0x02, 0x68, 0xff, 0xff, 0x52, 0xfd,
+ 0xff, 0xff, 0xff, 0x07, 0x00, 0x2a, 0x08, 0x20, 0x28, 0xdc, 0xff, 0x5f,
+ 0x05, 0xff, 0xff, 0xff, 0xff, 0x0d, 0x92, 0x40, 0x22, 0x09, 0x02, 0xea,
+ 0xfb, 0xaf, 0x48, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x12, 0x81, 0xa0,
+ 0x48, 0x9c, 0x6e, 0x93, 0xa2, 0xff, 0xff, 0xff, 0xff, 0x07, 0xa8, 0x40,
+ 0x28, 0x0a, 0x02, 0x74, 0xb5, 0x45, 0x81, 0xff, 0xff, 0xff, 0xff, 0x0f,
+ 0x02, 0x0a, 0x81, 0x20, 0x08, 0xae, 0xaa, 0x90, 0xe8, 0xff, 0xff, 0xff,
+ 0xff, 0x0f, 0x90, 0x40, 0x28, 0x88, 0x12, 0x58, 0x15, 0x50, 0xd0, 0xff,
+ 0xff, 0xff, 0xff, 0x0f, 0x44, 0x0a, 0x41, 0x21, 0x08, 0xae, 0x04, 0x14,
+ 0xf0, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x10, 0x40, 0x14, 0x88, 0x04, 0xba,
+ 0x02, 0x28, 0xe8, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x42, 0x15, 0x41, 0x21,
+ 0x05, 0xad, 0x00, 0x05, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x10, 0x40,
+ 0x24, 0x8a, 0x0e, 0x36, 0x00, 0x0a, 0xf4, 0xff, 0xff, 0xff, 0xff, 0x0f,
+ 0x42, 0x25, 0x90, 0xd0, 0x8b, 0xc2, 0x41, 0x05, 0xfc, 0xff, 0xff, 0xff,
+ 0xff, 0x0f, 0x10, 0x08, 0x05, 0xe8, 0x8e, 0x58, 0x80, 0x02, 0xfa, 0xff,
+ 0xff, 0xff, 0xff, 0x0f, 0x4a, 0x20, 0xa8, 0xba, 0x0b, 0x2b, 0x51, 0x01,
+ 0xfe, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x8a, 0x02, 0xe8, 0xaf, 0x84,
+ 0x90, 0x04, 0xfd, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x52, 0x21, 0x54, 0xbf,
+ 0x1f, 0x15, 0xa5, 0x02, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x08,
+ 0x01, 0xfa, 0xb6, 0xa4, 0x52, 0x40, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f,
+ 0x4a, 0xa2, 0x54, 0xef, 0x5f, 0x4b, 0xa4, 0x80, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x0f, 0x80, 0x10, 0x82, 0xfe, 0xbf, 0x92, 0x52, 0x42, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x0f, 0x12, 0x42, 0xa8, 0xbf, 0x1f, 0x24, 0x80, 0xa0,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x84, 0x28, 0x8a, 0xf7, 0x37, 0x80,
+ 0x52, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x10, 0x82, 0xe0, 0xff,
+ 0x1f, 0x00, 0x20, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x84, 0x28,
+ 0xca, 0xff, 0x1f, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f,
+ 0x10, 0x42, 0xf0, 0xfd, 0x1b, 0x00, 0x50, 0xf0, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x0f, 0xa4, 0x10, 0xc5, 0xff, 0x1f, 0x00, 0x00, 0xe0, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x0f, 0x00, 0x22, 0xf8, 0xff, 0x0e, 0x00, 0x00, 0xf0,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xaa, 0x88, 0xe2, 0xff, 0x0f, 0x10,
+ 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x25, 0xfa, 0xff,
+ 0x0f, 0x01, 0x11, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xfb,
+ 0xfb, 0xff, 0x7f, 0x5d, 0xd5, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f};
diff --git a/ext/tk/sample/demos-en/images/flagdown.xbm b/ext/tk/sample/demos-en/images/flagdown.xbm
new file mode 100644
index 0000000000..55abc51825
--- /dev/null
+++ b/ext/tk/sample/demos-en/images/flagdown.xbm
@@ -0,0 +1,27 @@
+#define flagdown_width 48
+#define flagdown_height 48
+static char flagdown_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xe1, 0x00, 0x00,
+ 0x00, 0x00, 0x70, 0x80, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x03, 0x00,
+ 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x04,
+ 0x00, 0x00, 0x03, 0x00, 0x06, 0x06, 0x00, 0x80, 0x01, 0x00, 0x06, 0x07,
+ 0x00, 0xc0, 0x1f, 0x00, 0x87, 0x07, 0x00, 0xe0, 0x7f, 0x80, 0xc7, 0x07,
+ 0x00, 0x70, 0xe0, 0xc0, 0xe5, 0x07, 0x00, 0x38, 0x80, 0xe1, 0x74, 0x07,
+ 0x00, 0x18, 0x80, 0x71, 0x3c, 0x07, 0x00, 0x0c, 0x00, 0x3b, 0x1e, 0x03,
+ 0x00, 0x0c, 0x00, 0x1f, 0x0f, 0x00, 0x00, 0x86, 0x1f, 0x8e, 0x07, 0x00,
+ 0x00, 0x06, 0x06, 0xc6, 0x05, 0x00, 0x00, 0x06, 0x00, 0xc6, 0x05, 0x00,
+ 0x00, 0x06, 0x00, 0xc6, 0x04, 0x00, 0x00, 0x06, 0x00, 0x06, 0x04, 0x00,
+ 0x7f, 0x06, 0x00, 0x06, 0xe4, 0xff, 0x00, 0x06, 0x00, 0x06, 0x04, 0x00,
+ 0x00, 0x06, 0x00, 0x06, 0x04, 0x00, 0x00, 0x06, 0x00, 0x06, 0x06, 0x00,
+ 0x00, 0x06, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x00, 0x86, 0x01, 0x00,
+ 0x00, 0x06, 0x00, 0xc6, 0x00, 0x00, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00,
+ 0x00, 0x06, 0x00, 0x36, 0x00, 0x00, 0x00, 0x06, 0x00, 0x3e, 0x00, 0x00,
+ 0x00, 0xfe, 0xff, 0x2f, 0x00, 0x00, 0x00, 0xfc, 0xff, 0x27, 0x00, 0x00,
+ 0x00, 0x00, 0x88, 0x20, 0x00, 0x00, 0x00, 0x00, 0x88, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x88, 0x20, 0x00, 0x00, 0x00, 0x00, 0x88, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x88, 0x20, 0x00, 0x00, 0x00, 0x00, 0x88, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x88, 0x20, 0x00, 0x00, 0x00, 0x00, 0x88, 0x20, 0x00, 0x00,
+ 0xf7, 0xbf, 0x8e, 0xfc, 0xdf, 0xf8, 0x9d, 0xeb, 0x9b, 0x76, 0xd2, 0x7a,
+ 0x46, 0x30, 0xe2, 0x0f, 0xe1, 0x47, 0x55, 0x84, 0x48, 0x11, 0x84, 0x19};
diff --git a/ext/tk/sample/demos-en/images/flagup.xbm b/ext/tk/sample/demos-en/images/flagup.xbm
new file mode 100644
index 0000000000..6eb0d846a3
--- /dev/null
+++ b/ext/tk/sample/demos-en/images/flagup.xbm
@@ -0,0 +1,27 @@
+#define flagup_width 48
+#define flagup_height 48
+static char flagup_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x7f, 0x00,
+ 0x00, 0x00, 0x00, 0xe0, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xef, 0x6a, 0x00,
+ 0x00, 0x00, 0xc0, 0x7b, 0x75, 0x00, 0x00, 0x00, 0xe0, 0xe0, 0x6a, 0x00,
+ 0x00, 0x00, 0x30, 0x60, 0x75, 0x00, 0x00, 0x00, 0x18, 0xe0, 0x7f, 0x00,
+ 0x00, 0x00, 0x0c, 0xe0, 0x7f, 0x00, 0x00, 0x00, 0x06, 0xe0, 0x04, 0x00,
+ 0x00, 0x00, 0x03, 0xe0, 0x04, 0x00, 0x00, 0x80, 0x01, 0xe0, 0x06, 0x00,
+ 0x00, 0xc0, 0x1f, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x7f, 0xe0, 0x07, 0x00,
+ 0x00, 0x70, 0xe0, 0xe0, 0x05, 0x00, 0x00, 0x38, 0x80, 0xe1, 0x04, 0x00,
+ 0x00, 0x18, 0x80, 0xf1, 0x04, 0x00, 0x00, 0x0c, 0x00, 0xfb, 0x04, 0x00,
+ 0x00, 0x0c, 0x00, 0xff, 0x04, 0x00, 0x00, 0x86, 0x1f, 0xee, 0x04, 0x00,
+ 0x00, 0x06, 0x06, 0xe6, 0x04, 0x00, 0x00, 0x06, 0x00, 0xe6, 0x04, 0x00,
+ 0x00, 0x06, 0x00, 0xe6, 0x04, 0x00, 0x00, 0x06, 0x00, 0x66, 0x04, 0x00,
+ 0x7f, 0x56, 0x52, 0x06, 0xe4, 0xff, 0x00, 0x76, 0x55, 0x06, 0x04, 0x00,
+ 0x00, 0x56, 0x57, 0x06, 0x04, 0x00, 0x00, 0x56, 0x55, 0x06, 0x06, 0x00,
+ 0x00, 0x56, 0xd5, 0x06, 0x03, 0x00, 0x00, 0x06, 0x00, 0x86, 0x01, 0x00,
+ 0x54, 0x06, 0x00, 0xc6, 0x54, 0x55, 0xaa, 0x06, 0x00, 0x66, 0xaa, 0x2a,
+ 0x54, 0x06, 0x00, 0x36, 0x55, 0x55, 0xaa, 0x06, 0x00, 0xbe, 0xaa, 0x2a,
+ 0x54, 0xfe, 0xff, 0x6f, 0x55, 0x55, 0xaa, 0xfc, 0xff, 0xa7, 0xaa, 0x2a,
+ 0x54, 0x01, 0x88, 0x60, 0x55, 0x55, 0xaa, 0xaa, 0x8a, 0xa0, 0xaa, 0x2a,
+ 0x54, 0x55, 0x8d, 0x60, 0x55, 0x55, 0xaa, 0xaa, 0x8a, 0xa0, 0xaa, 0x2a,
+ 0x54, 0x55, 0x8d, 0x60, 0x55, 0x55, 0xaa, 0xaa, 0x8a, 0xa0, 0xaa, 0x2a,
+ 0x54, 0x55, 0x8d, 0x50, 0x55, 0x55, 0xaa, 0xaa, 0x8a, 0xa8, 0xaa, 0x2a,
+ 0x54, 0x55, 0x95, 0x54, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x2a,
+ 0x54, 0x55, 0x55, 0x55, 0x55, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
diff --git a/ext/tk/sample/demos-en/images/gray25.xbm b/ext/tk/sample/demos-en/images/gray25.xbm
new file mode 100644
index 0000000000..b234b3cb0b
--- /dev/null
+++ b/ext/tk/sample/demos-en/images/gray25.xbm
@@ -0,0 +1,6 @@
+#define grey_width 16
+#define grey_height 16
+static char grey_bits[] = {
+ 0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44,
+ 0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44,
+ 0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44};
diff --git a/ext/tk/sample/demos-en/images/grey.25 b/ext/tk/sample/demos-en/images/grey.25
new file mode 100644
index 0000000000..b234b3cb0b
--- /dev/null
+++ b/ext/tk/sample/demos-en/images/grey.25
@@ -0,0 +1,6 @@
+#define grey_width 16
+#define grey_height 16
+static char grey_bits[] = {
+ 0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44,
+ 0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44,
+ 0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44};
diff --git a/ext/tk/sample/demos-en/images/grey.5 b/ext/tk/sample/demos-en/images/grey.5
new file mode 100644
index 0000000000..37688893f0
--- /dev/null
+++ b/ext/tk/sample/demos-en/images/grey.5
@@ -0,0 +1,6 @@
+#define grey_width 16
+#define grey_height 16
+static char grey_bits[] = {
+ 0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa,
+ 0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa,
+ 0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa};
diff --git a/ext/tk/sample/demos-en/images/letters.xbm b/ext/tk/sample/demos-en/images/letters.xbm
new file mode 100644
index 0000000000..0f12568d1a
--- /dev/null
+++ b/ext/tk/sample/demos-en/images/letters.xbm
@@ -0,0 +1,27 @@
+#define letters_width 48
+#define letters_height 48
+static char letters_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xfe, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20,
+ 0x00, 0xfa, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2a,
+ 0x00, 0x3a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2e,
+ 0xe0, 0xff, 0xff, 0xff, 0xff, 0x21, 0x20, 0x00, 0x00, 0x00, 0x00, 0x21,
+ 0xa0, 0x03, 0x00, 0x00, 0x70, 0x21, 0x20, 0x00, 0x00, 0x00, 0x50, 0x21,
+ 0xa0, 0x1f, 0x00, 0x00, 0x50, 0x21, 0x20, 0x00, 0x00, 0x00, 0x70, 0x21,
+ 0xfe, 0xff, 0xff, 0xff, 0x0f, 0x21, 0x02, 0x00, 0x00, 0x00, 0x08, 0x21,
+ 0xfa, 0x01, 0x00, 0x80, 0x0b, 0x21, 0x02, 0x00, 0x00, 0x80, 0x0a, 0x21,
+ 0xba, 0x01, 0x00, 0x80, 0x0a, 0x21, 0x02, 0x00, 0x00, 0x80, 0x0b, 0x21,
+ 0x3a, 0x00, 0x00, 0x00, 0x08, 0x21, 0x02, 0x00, 0x00, 0x00, 0x08, 0x21,
+ 0x02, 0xc0, 0xfb, 0x03, 0x08, 0x21, 0x02, 0x00, 0x00, 0x00, 0x08, 0x3f,
+ 0x02, 0xc0, 0xbd, 0x0f, 0x08, 0x01, 0x02, 0x00, 0x00, 0x00, 0x08, 0x01,
+ 0x02, 0xc0, 0x7f, 0x7b, 0x08, 0x01, 0x02, 0x00, 0x00, 0x00, 0x08, 0x01,
+ 0x02, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0xfe, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
diff --git a/ext/tk/sample/demos-en/images/noletter.xbm b/ext/tk/sample/demos-en/images/noletter.xbm
new file mode 100644
index 0000000000..5774124efe
--- /dev/null
+++ b/ext/tk/sample/demos-en/images/noletter.xbm
@@ -0,0 +1,27 @@
+#define noletters_width 48
+#define noletters_height 48
+static char noletters_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x1f, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x07, 0x00,
+ 0x00, 0xf0, 0x0f, 0xe0, 0x1f, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x7f, 0x00,
+ 0x00, 0x3e, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x1f, 0x00, 0x00, 0xf0, 0x01,
+ 0x80, 0x07, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0xe0, 0x07,
+ 0xe0, 0x01, 0x00, 0x00, 0xf0, 0x0f, 0xe0, 0x00, 0x00, 0x00, 0x78, 0x0e,
+ 0xf0, 0x00, 0x00, 0x00, 0x3c, 0x1e, 0x70, 0x00, 0x00, 0x00, 0x1e, 0x1c,
+ 0x38, 0x00, 0x00, 0x00, 0x0f, 0x38, 0x38, 0x00, 0x00, 0x80, 0x07, 0x38,
+ 0x3c, 0xfc, 0xff, 0xff, 0x7f, 0x78, 0x1c, 0x04, 0x00, 0xe0, 0x41, 0x70,
+ 0x1c, 0x04, 0x00, 0xf0, 0x40, 0x70, 0x1c, 0x74, 0x00, 0x78, 0x4e, 0x70,
+ 0x0e, 0x04, 0x00, 0x3c, 0x4a, 0xe0, 0x0e, 0x74, 0x03, 0x1e, 0x4a, 0xe0,
+ 0x0e, 0x04, 0x00, 0x0f, 0x4e, 0xe0, 0x0e, 0x04, 0x80, 0x07, 0x40, 0xe0,
+ 0x0e, 0x04, 0xf8, 0x0f, 0x40, 0xe0, 0x0e, 0x04, 0xe0, 0x01, 0x40, 0xe0,
+ 0x0e, 0x04, 0xf8, 0x00, 0x40, 0xe0, 0x0e, 0x04, 0x78, 0x00, 0x40, 0xe0,
+ 0x0e, 0x04, 0xfc, 0xf3, 0x40, 0xe0, 0x1c, 0x04, 0x1e, 0x00, 0x40, 0x70,
+ 0x1c, 0x04, 0x0f, 0x00, 0x40, 0x70, 0x1c, 0x84, 0x07, 0x00, 0x40, 0x70,
+ 0x3c, 0xfc, 0xff, 0xff, 0x7f, 0x78, 0x38, 0xe0, 0x01, 0x00, 0x00, 0x38,
+ 0x38, 0xf0, 0x00, 0x00, 0x00, 0x38, 0x70, 0x78, 0x00, 0x00, 0x00, 0x1c,
+ 0xf0, 0x3c, 0x00, 0x00, 0x00, 0x1e, 0xe0, 0x1e, 0x00, 0x00, 0x00, 0x0e,
+ 0xe0, 0x0f, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x07, 0x00, 0x00, 0x80, 0x07,
+ 0x80, 0x07, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x1f, 0x00, 0x00, 0xf0, 0x01,
+ 0x00, 0x3e, 0x00, 0x00, 0xf8, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x7f, 0x00,
+ 0x00, 0xf0, 0x0f, 0xe0, 0x1f, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x07, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xf0, 0x1f, 0x00, 0x00};
diff --git a/ext/tk/sample/demos-en/images/pattern.xbm b/ext/tk/sample/demos-en/images/pattern.xbm
new file mode 100644
index 0000000000..df31baf789
--- /dev/null
+++ b/ext/tk/sample/demos-en/images/pattern.xbm
@@ -0,0 +1,6 @@
+#define foo_width 16
+#define foo_height 16
+static char foo_bits[] = {
+ 0x60, 0x06, 0x90, 0x09, 0x90, 0x09, 0xb0, 0x0d, 0x4e, 0x72, 0x49, 0x92,
+ 0x71, 0x8e, 0x8e, 0x71, 0x8e, 0x71, 0x71, 0x8e, 0x49, 0x92, 0x4e, 0x72,
+ 0xb0, 0x0d, 0x90, 0x09, 0x90, 0x09, 0x60, 0x06};
diff --git a/ext/tk/sample/demos-en/images/tcllogo.gif b/ext/tk/sample/demos-en/images/tcllogo.gif
new file mode 100644
index 0000000000..3fc7720b17
--- /dev/null
+++ b/ext/tk/sample/demos-en/images/tcllogo.gif
Binary files differ
diff --git a/ext/tk/sample/demos-en/images/teapot.ppm b/ext/tk/sample/demos-en/images/teapot.ppm
new file mode 100644
index 0000000000..78afefbf82
--- /dev/null
+++ b/ext/tk/sample/demos-en/images/teapot.ppm
@@ -0,0 +1,56 @@
+P6
+256 256
+255
+\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À[7 eOLjQLmSMoTMnSMlRMhPL_9 \À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀnSMtVMzYN~[N~[N\N\O€\O€]O€]O€]O€]O€\O€\O}[NyYNtVM\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀG-wXN}[N€]O„^O†_O†`O‡`Oˆ`Oˆ`OˆaO‰aO‰aO‰aO‰aO‰aO‰aOˆaOˆ`O†_Oƒ^O\N \À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀaMLyYN…_O‰aP‹bPcPŽcPŽdPŽdPdPdPdPdPdPdPdPeP‘eP’eP’eP‘ePdPcP…_OpUM\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀwXN…_OdP“fP•gQ–hQ˜hQ˜iQ™iQ™iQšiQšiQšjQ›jQ›jQœjQœjQœjQœjQœjQ›jQœjQ™iQ“fP‡`O\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀNCJiQL‹bP—hQkQ¡mR¤nR¥oR¥oR¥oR¥oR¥oR¥oR¦oR¦oR¦pR¨pS©qSªqS«rS¬rS«rS©qS¤oRœjQ€]O\KK\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀfOLrUMcPŸlR©qS¯tS²uTµwT·xT¸xT¹yTºyT»zT»zU¼zU¼zU¼zU»zUºyT¸xT¶wT¯tS¡mR‰aOhPL\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\Àa0 cNLqUM€\O”fQ¦pS²wVºzV¿|VÂ}VÄVÆVÇ€VÉ‚WÌ…[Õeæ w÷³‹êª…Ĉg§qT“fQ{ZNYIK9\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀO1{G#‘JkRMqUMtVN–iS¨v\·€d¹bµzZ±vU°uT®sSªqS¤nRœjQ’eP„^OrUMHh>!T4\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀG-V5wE"~I#†M%U+¥e7²l:°g2®b*­a(­`(©^(¥])¡^-›]1ŠS,qC$`9 R3G-\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À@)J/i>!pA"tD"wF$yH&xH&tE$wE#yG%}M+ƒT4S5mE*Z7!K/B*;'\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À‰aO¦oR½{UÇ€VÏ…X<(F-a: e<!h>!j@#k@$h>"d<!c=$hD-fF2[<)K0@);'5$Ë‚VÇ€V¿|U_LKYIK\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À…_O·xTÉ‚Wó«€ûµ‹Ö’k¼|X×>µf-¨^(¡Z'šW&–T&œN>)F-J/b; g>#nD(jB&c<!b=%jH2_A/I0!<(8&5$”J¥Y’S%8&;'?)E,<:HA=HE?IJAISFJYIKXIK\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À£nRÁ}UܘqÊŠe±vU²e,™V&¥V†C
+€@ |> y< u: r9 o7 l6
+j5
+h4
+g3
+5$D,K/b; h>"wM1tK.e="a<#cA,U8&E-<(9&.!a0 b1 c1    
+
++3#@)46G<:HMCIXHK\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀU*´vT¿~X¸{YÃk+›W&‰N$|> u: p8 k5
+f3
+a0 _/ ]. [- I¡\*ª_(‘LkRMmSMmSMnSMnSMD,R3W5mA"|O0|P1j?"c<!a=%Y7"N1F,;'NCJNCJNDJODJODJODJh>!a: X/K%
+g3
+a0 Z- \/ T*Q(ŠHµm8kRMmSMnTMoTMpTMpUM15G15G05G04G04GpUMpTM5^9 d<!yF#O+€N,rC#qB"pB#k?"a: Z7 6ODJPDJPEJQEJQEJREJREJREJRFJSFJSFJSFJSFJe<!X/
+^/ V+Q(L&I$r9  TlRMnSM46G47G47G46G46G46G46G46G36G36G25G25G15G04G/4F.3F
+ˆ`O~[NqUM[- ‰HUGJUGJVGJVGJVHJWHJWHJWHKWHKXHKXHKXHKXHKXHKXIKXIKXIKXIKXIKh>!Y0
+
+L&C!:4
+X&pUMuWMwXNxXN<:H<:H<:H<:H<;H<;H<;H<;H=;H=;H=;H=;H>;H>;H?<H@<HA=HC>HG@ILBIREJ[JKcNLjQL§pR±uTºzUÃ~VÈWË‚XÖŽcäsÒŽe¼{V²vT¨pSžkR•gQŒbP†_O‚^O]O€\O€\O€\O€\O€]O]O]O]O]O]O]O]O]O]O]O€\O€\O~\N}[N|ZNxXN•T%H$
+›W&rVMvWNyYNzYN|ZN}[N}[N><H?<H?<H?<H?<H?<H@<H@<H@<HA=HA=HB=HC>HE?IG@IIAIKBIODJSFJWHK—hQŸlR§pR°b(¾i*Én+Ù|7Û|6Ïr,Íq+Êp-Ãl+»g)±b(®sS§pS lRšiQ•gQePcPŠaPˆaO‡`O‡`O†_O†_O…_O…_O…_O…_O…_O…_O…_O„_O„^O„^Oƒ^Oƒ^O‚]O]O€\O~[N{ZN•T%
+
+
+
+ 
+@%<-$G?@…pfdNLuWM\NdNL\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀTFJvWN‰aP./01„E}[N]O…_Oˆ`O‰aP‹bPŒbPcPcPŽcPdPdPdPeP‘eP’eP’eP“fP“fQ”fQ•gQ•gQ–gQ–hQ—hQ˜hQ™iQšiQ›jQœjQkQkRžlRŸlRžY&¤\'¨^'µ^½bÀcÃeÇi ÄgÀc½b¼a¹`µ^´]¯X¢[' Z'žY&¢mR¡mR¡mR lRŸlRŸlRžkRkQœkQœjQ›jQšjQšiQ™iQ™iQ˜iQ˜hQ—hQ—hQ—hQ–gQ–gQ•gQ•gQ•gQ”fQ”fQ“fQ“fP’eP‘ePdPcP‰aP—O
+ B\À\À\À\À\À\À\À\À\À\À%7!!C*F#P) {dYœze»p€\OgPL\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀSFJ`LKvWNŠaPm6
+ 
+\À\À\À\À\À\À\À\À\À B B
+$5 ¬`(¶e)£nRœjQƒ^OJAI\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀXIK^KKdNLhPLuWM‚]OŒbP”fQeP
+m6
+†`OŽcP“fQ—hQ˜hQ™iQšiQšjQ›jQ›jQ›jQœjQœjQœjQœkQkQkQkRžkRžkRžkRžlRŸlRŸlRŸlR lR lR lR¡mR¡mR¡mR¡mRºg)³c(²c(±b(­V¿cÂeÅi!Åi!Àd¼bº`¹`·_·_¶^¢Q§]'ª_(­`(¹f)£nR£nR£nR£nR£nR£nR£nR¢nR¢nR¢nR¢nR¢nR¢nR¢mR¢mR¢mR¢mR¢mR¢mR¢mR¢mR¢mR¢nR¢mR¢mR£nR¢mR¢mR¡mR mRkR—hQˆGa0 ŠbP mRœjQ“fQ‰aP}[NrUMmSM…L$\À\À\À\À\À\À\À\À B B
+#C, 8&H.Z7 §pR›jQ{ZN\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀQEJ[JK`LKdNLhQLqUM{ZN…_OŽcP–gQ—hQ
+‹bP‘eP–hQšiQ›jQœjQkQkQkRžkRžkRžlRžlRŸlRŸlRŸlRŸlRŸlR lR lR lR mR¡mR¡mR¡mR¡mR¡mR¢mR¢mR¢mR¢nR£nRÀj*ºg)·e)¶d)Âd°XÅgÅhÂe¿c½b½b¾bªU­`(®a(¯a(³c(¾i*¤oR¤oR¤nR¤nR¤nR¤nR¤nR¤nR¤nR¤nR¤nR¤nR¤nR¤nR¤nR¤nR¤nR¤oR¤oR¥oR¥oR¥oR¥oR¥oR¥oR¦oR¦oR¥oR¥oR¤nR¡mR›jQŽQ%Z- œjQ£nRŸlR—hQŽdP…_OuWMpTMnSMkRLa: \À\À\À\À\À\À\À B B&D2
+@*S6#G@IPDJ˜hQmSM\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀVGJ]KKbMLeOLiQLlRMvWN\OˆaO‘eP—hQœjQ•gQ
+\À\À\À\À\À B'D+E$(1 J/jH1NCJUGJYIKUGJ\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀXHK]KKbNLfOLiQLkRMmSMoTMqUMxXN\N†_OŒbP’fP˜hQkQ¡mR¥oR§pS¦pR˜hQ¢mR¥oR¨pSªqS«rS«rS«rS«rS«rS«rS«rS«rS«rS«rS«rSªrSªrSªrS«rS«rS«rS«rS«rS«rS«rS«rS«rS«rS«rS«rS«rS«rS«rS«rS¬rS¬rS¬rS¬rS¬rS¬rS¬rS¬sS¬sS­sS­sS­sS­sS­sS­sS®sS®sS®sS®sS®tS¯tS°tS°uS±uS±uT±uT²uT²uT²uT´vTµwT´vT³vT²uT¯tS¢mR¯tS±uT±uS®tS«rS§pR¢mRkQ—hQ‘ePŠaPƒ^O\N{ZNvXNqUMpTMnSMlRMP%\À\À\À\À B#C*E$.E- .!G$Y:%d<"SFJYIKZIKNCJ\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀPDJZIK_LKdNLgPLjQLlRMnSMpTMqUMuWMyYN€\O†`OcP’fP—hQœjQ¡mR¥oR¨qS«rS«rSªrS mR
+!C+E'0F.4F7%8%U/lG.SFJZIK]KKZIKB=H\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀREJZJK`LKdNLgPLjQLlRMnSMpTMqUMtWMxXN{ZN~[N]O„^O†`O‰aO‹bPdP•gQ™iQœkQ lR¤nR§pSªrS­sS¯tT²uT´vT¶wT·xT¹yT¹yTºyTºyT¹yT¶xT´vT¬rS¢nR—hQ¿|U¿|UÀ|UÀ|UÀ|UÀ|UÀ|UÀ|UÀ|UÀ|UÀ|UÀ|UÀ|UÀ|UÀ}UÀ}UÁ}UÁ}UÁ}UÁ}UÂ}UÂ~UÃ~UÃ~VÃ~VÄVÅ€WÆX®a(ŸlRªrS´vT¸yT¼zU¾|UÁ~VÃXÆ‚[Ɇ_΋dÓ‘jÔ“mÔ“nБlÊŒhĆd½_¶{[°vWªsU¦pS¢nRžkRšiQ˜hQ•gQ“fQ‘ePdPŒbP‰aO†_Oƒ^O€\O|ZNxXNsVMpTMnTMmSMjQL€C B)D&/F-3F47G6%>" Y7 kA$YIK]KK^KKSFJ\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀVGJ\KKbMLeOLhPLkRLmSMnTMpTMrUMuWNyYN|ZN\N‚]O„_O‡`OŠaPŒbPŽcPeP“fP—hQ›jQžlR¢nR¥oS©qT¬sT¯uU²vU´wV¶xV¸yV¹yUºzU»zU¼{U½{U¾{U¾|U¿|U¿|U¿|U¿|U¾{U½{U¼{U¼zU»zTºyT¹yT¸xTµwT³vT´vT´vT´vT´wT´wTµwT·xT¹yTºzT¼zU½{U¾{U¿|UÀ|UÂ}UÄVÅ€WÇ‚YÉ„\͈_ÑŒdÙ”láuç£|쩂ſt명æ¦ÞŸ{Õ—sËŽl†d¹^³yZ­uW¨qU¤oSŸlRžkRœjQšiQ˜hQ–gQ”fQ‘ePdPcPŠaP‡`O„^O]O}[NyYNuWMpTMoTMmSMkRLgPL&D#.E,3F46G;'<(D"iB(VGJ]KK`LK[JKB>H\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀNCJYIK^LKcNLfOLiQLkRMmSMoTMqUMsVMvXNzYN}[N€\O‚^O…_Oˆ`OŠaPŒcPdP‘eP“fQ•gQ—hQ™iQkR mS¤oT¨rU¬tW°wY´zZ¸}\»]¾€^À^Á‚^‚^Â\Á€ZÁYÁXÁ~WÁ~WÂ~VÂ~VÂ~VÃ~VÃ~UÃ~UÄ~UÄ~UÄUÄUÅVÅVÅVÅVÆVÆ€VÆ€VÇ€WÇWÈ‚XɃZË…[͇^ЊaÓdØ’iÜ—nâtè£zî©ó¯‡ø´û¸‘üº“û¹“÷¶ñ±Œé©…à¡~Ö˜vËmÇf»€`´z[®vX©rU¥pT£oS¢nS lRžkRœkRšjQ˜iQ–hQ”fQ’ePdPcP‹bPˆ`O…_O‚]O~[NzYNvWNpTMoTMnSMkRMhQLo7 ,2F36G99HC+@ ]8 nA"\JK`ML_LKSFJ\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀSFJ[JK`LKdNLgPLjQLlRMnSMpTMqUMtVMwXNzZN}[N€]Oƒ^O†_OˆaO‹bPcPdP‘eP“fQ•gQ—hQ™iQ›jRžlR mS£oU§rW¬vZ²{]¹€a¿…fÅŠjËnГqÓ•sÕ–sÕ–rÕ–qÕ”oÓ’mÑjÏgÍŠcˈaɆ^È„\Ç‚[ÆYÅ€XÅ€WÅWÅWÅVÅVÅWÅ€WÆ€WÇXÈ‚YɃ[Ê…\͇_ÏŠaÒeÕ‘hÙ•mÝ™qávä¡zç¤}꧀멃몄騃奀ߠ|Ù›wÓ•rÌmƉh¿„c¸~^²yZ®vX¬tWªsV¨qU¦pT¤oS¢nS mRžlRœkR›jQ™iQ—hQ•gQ“fPePŽcP‹bPˆaO…_O‚^O\N{ZNwXNsVMoTMnSMlRMiQL~I#26G99G?<HA*E$ i@$ZIKaMLbML[JK;:H\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀWHJ]KKbMLeOLhPLjRLlSMnTMpTMrUMuWMxXN{ZN~\N]O„^O†`O‰aO‹bPŽcPdP’eP”fQ–gQ˜hQšiQœkRžlS mT£oU¦rWªuZ¯y]´~aºƒfŠlË’sÔšzÜ¡€ã§†è«‰ë®‹í¯Œí®‹ë¬ˆè¨„ã£~ßžyÚ™tÖ•oÒjÎŒfˈbÈ…_ƃ\ÅZÄ€YÃXÂWÂ~WÂ~WÂ~WÃXÀXÄ€YÅZƃ\Ç…^Ɇ`ˈbÌŠdÍ‹fÎgÎŽiÎŽjÎŽjÍŽjËŒiljgÆd¿ƒaº^¸}]¶|\´{[²yZ°xY®vX¬tWªsV¨qU¦pT¤oS¢nS mRžlRkR›jQ™iQ—hQ•gQ“fP‘ePŽdPŒbP‰aO†_Oƒ^O€\O|ZNxXNtVMpTMnSMmSMjQLgPL99G?<HG-E&b;!YIK`MLdOM`LKNCJ\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀŸlRºyTÄ~UÊ‚XʃYÄXº{W­tUšW'¢[(—hQ lRcP€\OhQL\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀNCJYIK^LKcNLfOLiQLkRLmSMoTMqUMrVMvWNyYN|ZN\N‚]O„_O‡`O‰aPŒbPŽcPdP’fP”gQ–hQ˜iQšjRœkRžlS¡nT¤pU§sW«vZ°z]µb»„gŠlÉ‘sИyØžÞ¤…㩊è­ì±ï³‘ﳑ뭊穅⣀ݞzؘtÒ“nÎiɉdÆ…`Â]Á€[¿~Y¾}X½|W½|V¼{V¼{V¼{V¼{V¼{V¼|W¼|W½}X½}Y½~Z½~Z¼~Z»}[º}[º}[º~\º~\º~]º~]¹~]¸~]·}]¶|\´z[²yZ°wY®vX¬tWªsV¨rU¦pT¤oS¢nS mRŸlRkR›jQšiQ˜hQ–gQ“fQ‘ePdPŒcPŠaP‡`O„^O]O}[NyYNuWNpTMnTMmSMkRLhPL|H$D>IQ2P+XHK_LLfQOcNLXIK\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À©qSºyTÃ~VΈ`遲ޜv¾€]ªqS–LŽG|> g3
+S)?*%.—hQ—hQ‘eP‡`OuWM\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀSFJ[JK`LKdNLgPLjQLlRMnSMoTMqUMsVMwXNzYN}[N€\O‚^O…_O‡`OŠaPŒbPŽdP‘eP“fP•gQ—hQ˜iQšjRœkRŸlS¡nT¤pV§sX«vZ°z^¶b¼…gËmÊ’sјzØŸ€Þ¤…㩊è­ê¯ë°ê¯Žè¬‹å¨‡à¤‚Ûž|Ö™wÑ“qÌŽlljgÃ…bÀ‚_½\»}Zº{X¹zW¸yV·yU·xU·xU·xT·xT·xU·xU·xU·yV·yV·yW¸zW¸{X¹{Y¹|Zº}[º}[º}\º~\¹~]¹~]¸}]·|\µ{\´z[²yZ°wY®vX¬tWªsV¨rU¦pT¤oS¢nS¡mRŸlRkRœjQšiQ˜hQ–gQ”fQ’ePdPcPŠbP‡`O…_O‚]O~[NzZNvWNrUMoTMmSMlRMiQLeOLJAIJ(h>!]KKfQOgQN_LKD>I\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À™iQ°tS¸yT¼{UÂYÎŒeï­ˆô´Õ—u¶|\ Z'™LˆD
+|>
+
++,!.! "`E6†iYŒlZo\“q]•s^^J™va›wbycŸzd {e¤}foTMqUMsVMuWNwXNyYN{ZN|ZN~[N\O]O‚]Oƒ^O…_O†_O‡`Oˆ`O‰aOŠaP‹bPŒbPŒcPcPŽcPŽdPdPdPdPeP‘eP‘eP‘eP’eP’eP’eP’eP’fP’fP’fP“fP’fP’fP’fP’eP’eP’eP‘eP‘eP‘ePePdPdPdPŽdPŽcPcPŒcPŒbP‹bPŠaP‰aOˆ`O‡`O†_O…_Oƒ^O‚]O]O\O~[N|[N{ZNyYNwXN®ƒi¬ƒiª‚i¨i¦€hŒhR‰fQ†dQ‚bP•wfx]Oˆpdkbtd_m`]OEDG?A;:@.S….S….S….S….S…/S…/S…/S…/S…/S…/S…/S…/S…TxªTxªTxªTxªTxªTx«Tx«Tx«Ty«/S†GlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlž…ªÜ…ªÜ…ªÜHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlž…ªÜ…ªÜ£Ö£Ö£Ö£Ö¤Ö¤Ö¤Ö¤Ö¤ÖEi›€¤Ö€¤Ö€¤Ö€¤Ö€¤Ö€¤Ö€¤Ö€¤Ö€¤Ö€¤Ö€¤Ö€¤Ö€¤×€¤×€¤×€¤×€¥×€¥×€¥×Bg™Bg™Bg™Bg™Bg™&J|&J|&J|&J|&J|&J|&J|&J|&J|&J|&J|&J|&J|Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Af˜Af˜%J|%J|%J|%J|%J|%J|%J|%J|%J|%J|%I|%I|%I|%I|%I|%I|
++,YA5jPBpSD‹l[o]’q^–t`‚_Kšwbœycžze {f¡}g¤h¨i”lSrVMtWMvWNxXNyYN{ZN|[N~[N\O]O‚]Oƒ^O„_O…_O†`O‡`Oˆ`O‰aPŠaP‹bP‹bPŒbPcPcPŽcPŽcPdPdPdPdPdPdPdPdPePePePePePdPdPdPdPdPdPdPŽcPŽcPcPcPŒbP‹bP‹bPŠaP‰aOˆ`O‡`O†`O…_O„^Oƒ^O‚]O€]O\O~[N|[N{ZNyYNxXN°…j®„j¬„jªƒj¨‚j¦€jŒhSŠgS†eRƒcR|`QŒsf…oe}jcrd`k_]LCDC=@,,3(4F(4F.S….S…/S…/S…/S…/S…/S…/S…/S…TxªTxªTxªTxªTxªTxªTx«Tx«Tx«Ty«Ty«Ty«…ªÜHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlž†ªÜHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžChšChš¤Ö€¤Ö€¤Ö€¤Ö€¤ÖEi›Ei›Ei›€¤Ö€¤Ö€¤Ö€¤Ö€¤Ö€¤Ö€¤×€¤×€¤×€¤×Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Bg™Bg™&J|&J|&J|&J|&J|&J|&J|&J|&J|&J|Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜&J|%J|%J|%J|%J|%J|%J|%J|%J|%J|%J|%J|%I|%I|%I|%I|%I|#5H71O;3V?4iOBoSDsVFo]{[I^Kƒ`L…bN‡dOŸ{f }g¢~h¥€j’kT•mU˜oVšqWrWwXNxXNzYN{ZN}[N~[N\O€]O‚]Oƒ^O„^O…_O…_O†`O‡`Oˆ`O‰aO‰aPŠaP‹bP‹bPŒbPŒbPŒcPcPcPcPŽcPŽcPŽcPŽcPŽcPŽcPŽcPŽcPŽcPŽcPcPcPcPŒcPŒbP‹bP‹bP‹bPŠaP‰aP‰aOˆ`O‡`O†_O…_O„_O„^Oƒ^O]O€\O\N~[N|ZN{ZNyYN›oTšoT™oT—nT¬„lªƒl¨‚ljUŒiTŠhT†fT€cSvi‰rgnfyidqdah^^HBD?<@)+3OZkMYk(5F(5F(5F/S…/S…/S…/S…/S…TxªTxªTxªTxªTxªTxªTx«Tx«Ty«Ty«Ty«Ty«Uy«†ªÜ†ªÜ†ªÜ†ªÜHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlž†ªÜ†ªÜ†ªÜ†ªÜ†ªÜHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžDhšDhšDhšChš&K}&K}&K}&K}&K}&K}ChšChšCgšCgšCgšCgšCgšCgšCg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™&J|&J|&J|&J|&J|&J|Bg™Bg™Bg™Bf™Bf™Bf™Bf™Bf™Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜&J|&J|&J|&J|%J|%J|%J|%J|%J|%J|%J|%J|%J|%J|%J|%I|%I|A99N?;L:2T>4gNBlRD‡k\‹n^z[J~^LaN…cO‡dP‰fQŠgRŒhTjU’lV•nW˜pXšrXsY¶‹q¸qºŽr¼r½r¿s©z[©z[ªz[«{[¬{[¬{ZÅ“rÅ’qÅ’qÅ’pÅ’pÅ‘o­yV­xV¬xU¬wT¬wTŠaPŠbP‹bP‹bP‹bP‹bP‹bP‹bP‹bP‹bP‹bP‹bP‹bP‹bPŠaPŠaPŠaP‰aP‰aOˆaOˆ`O‡`O‡`O†_O…_O„^Oƒ^O‚^O‚]O]O€\O~\N}[N|ZNzYNpTœpU›pUšpU˜oV—oV•nV“mV‘lVkVŒjVˆhVƒfU~cUuj†qh~mfugdkaad\^E@D98?$(2minffm^blV^lMYk(5F(5F/S…TxªTxªTxªTxªTxªTxªTxªTx«Tx«Ty«Ty«Ty«Uy«Uy«†ªÜ†ªÜ†ªÜ†ªÜ†ªÜ†ªÜ†ªÜ†ªÜHlžHlžHlžHlžHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸ†ªÝ†ªÝ†ªÝ†ªÝ†ªÝ†ªÝ†ªÝ†ªÝ†ªÝ†ªÝHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸ'K}'K}'K}'K}'K}'K}'K}'K}'K}&K}&K}ChšChšChšChšChšChšChšCgšCgšCgšCgšCgšCgšCg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™&J|Cg™Cg™Cg™Cg™Cg™Bg™Bg™Bg™Bg™Bg™Bg™Bf™Bf™Bf™Bf™Bf™Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜&J|&J|&J|&J|&J|&J|&J|%J|%J|%J|%J|%J|%J|%J|Ae˜Ae˜;GY<68I=:I82Q=4XA6~fZ„j\‰m^p`|]L€`NƒcP†eQˆgS¡j£€l¦‚m©„n•oX˜qYšrZt[¶Œr¸sºs¼t½t¾‘t¨z]©{]ª{]«{\«{\¬{\¬{[Ä“sÄ“rÄ’rÄ’qÄ’pÄ‘p¬yWÄoÃnÃmÃlÂŽlÂŽkÁkˆaOˆaOˆaOˆaOˆaOˆaOˆaOˆ`Oˆ`O‡`O‡`O‡`O†`O†_O…_O…_O„_O„^Oƒ^O‚]O]O€]O\O~\N}[N|ZN¶‰l¶‰lµˆmœqV›qVšqV™pW˜pW–oW¬…nª…n§„n¤‚nŸ€n›~n€eW‘xlŠtk‚piykfodcf_`JDG@>C*,5$1MYktr~tstmolinadmX_lNZkMZkTxªTxªTxªTxªTx«Tx«Tx«Ty«Ty«Ty«Uy«Uy«Uy«†ªÝ†ªÝ†ªÝ†ªÝ†ªÝ†ªÝ†«Ý†«Ý†«Ý†«ÝHlŸHlŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸ†«Ý†«Ý†«Ý‡«Ý‡«Ý‡«Ý‡«Ý‡«Ý‡«Ý‡«Ý‡«Ý‡«Ý‡«ÝHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸ'K}'K}'K}'K}'K}'K}'K}'K}'K}'K}'K}'K}DhšDhšDhšDhšChšChšChšChšChšChšChšCgšCgšCgšCgšCgšCgšCg™Cg™Cg™Cg™Cg™&J}&J}&J}Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Bg™Bg™Bg™Bg™Bg™Bg™Bf™Bf™Bf™Bf™Bf˜Bf˜Bf˜Bf˜Bf˜&J|&J|&J|&J|&J|&J|&J|&J|&J|&J|Af˜Af˜Af˜Af˜;GY;GY;GY1'!D:9N?;N;3]I?zdY€h[†l^‹oasc“ue€bQ„dR‡fT l¢m¦ƒn©…o«‡p®ˆq±Šr³‹sžv] w]¹u»u¼‘u¾‘u¿’v¨{^©{^ª|^«|]«|]«{\¬{\¬{[¬{[¬zZ«zZ«yY«yX«xXÂoÂnÂnÁŽmÁŽm¨uT¨uS§tS§tS§tR¦sR¦sQ…_O…_O…_O„^O„^Oƒ^Oƒ^O‚^O‚]O]O€]O¢rS¡rS¡rS¸‰k·‰l·‰l¶‰m¶‰mµ‰m´‰n³‰n›qWšqX™qX®‡o­‡o«†p¨…p¤ƒp pœp—}o{cXv`Vp]U}nishfhaba\_DAF::B$)4
+., 7(8'A1&F4(L8*oXIw]Jpdasfcvhexkg{mi~oj€qll\Xn^Yp`Zpa[qa\rb]rc^sc^sd_ue`wf`xgayhayhayhbxy‘y‘y‘y‘yy~ywgbvfateasd`qd`pc`nb_la_€ut|ssxqrunpZUXVRWROUMMSHIRIC@967-/3'+0(*-ACF?AD;=@#%(
+.+>1(B3)B2&F4'E4)gTGlXJs^OzcTzaPqfethgvjhbVTcWUdXVeYWfZXg[Yh\Zi]Zi][j^\€us€ususts~tt~tt}tt|st{stut~tt|sszrsyqrwpquoqsmpqloXTXTQWPOULLSSJEA<:=99757335./2113)+.'),)+.8:="(
+"6*#5*">2)>0&A2'C3(I8-^OFbRHfUJjXMq^RwcVzfYfRDfQCdN@zdTqijrjksklrklrklrklqjmpjmpjmojmojmnimmimkhliflscYm`Xg\VbYT^VRE>;A<:>98:77645:873220/0,-/)+.*,/#%( &
+
+&3#.$-% .% .& /&!,#,#@70A71XNHXNHWNHWNHZRLYQLYQLXQLWQLWPLUOLSNLQMKOLJMJJ0//.-.,,-&(+"(!'
+
+ %' %$#" ! !$
+
+
+ 
+
+
+*  
+  ;?E7CU;HY=I[ 
diff --git a/ext/tk/sample/demos-en/items.rb b/ext/tk/sample/demos-en/items.rb
new file mode 100644
index 0000000000..e6bf57ee82
--- /dev/null
+++ b/ext/tk/sample/demos-en/items.rb
@@ -0,0 +1,374 @@
+# items.rb
+#
+# This demonstration script creates a canvas that displays the
+# canvas item types.
+#
+# canvas item types widget demo (called by 'widget')
+#
+
+# toplevel widget
+if defined?($items_demo) && $items_demo
+ $items_demo.destroy
+ $items_demo = nil
+end
+
+# demo toplevel widget
+$items_demo = TkToplevel.new {|w|
+ title("Canvas Item Demonstration")
+ iconname("Items")
+ positionWindow(w)
+}
+
+# label
+TkLabel.new($items_demo) {
+ font $font
+ wraplength '5i'
+ justify 'left'
+ text "This window contains a canvas widget with examples of the various kinds of items supported by canvases. The following operations are supported:\n Button-1 drag:\tmoves item under pointer.\n Button-2 drag:\trepositions view.\n Button-3 drag:\tstrokes out area.\n Ctrl+f:\t\tprints items under area."
+}.pack('side'=>'top')
+
+# frame
+TkFrame.new($items_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $items_demo
+ $items_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'items'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame
+cvs = nil
+TkFrame.new($items_demo) {|cf|
+ # canvas
+ cvs = TkCanvas.new(cf) {|c|
+ focus
+ scrollregion '0c 0c 30c 24c'
+ width '15c'
+ height '10c'
+ relief 'sunken'
+ borderwidth 2
+
+ hs = TkScrollbar.new(cf) {|s|
+ orient 'horizontal'
+ command proc{|*args| c.xview(*args)}
+ c.xscrollcommand proc{|first,last| s.set first,last}
+ }
+
+ vs = TkScrollbar.new(cf) {|s|
+ command proc{|*args| c.yview(*args)}
+ c.yscrollcommand proc{|first,last| s.set first,last}
+ }
+
+ if $tk_version =~ /^4\.[01]/
+ hs.pack('side'=>'bottom', 'fill'=>'x')
+ vs.pack('side'=>'right', 'fill'=>'y')
+ c.pack('in'=>cf, 'expand'=>'yes', 'fill'=>'both')
+
+ else
+ c.grid('in'=>cf, 'row'=>0, 'column'=>0,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ vs.grid('row'=>0, 'column'=>1,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ hs.grid('row'=>1, 'column'=>0,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ TkGrid.rowconfigure(cf, 0, 'weight'=>1, 'minsize'=>0)
+ TkGrid.columnconfigure(cf, 0, 'weight'=>1, 'minsize'=>0)
+
+ end
+
+ }
+}.pack('side'=>'top', 'fill'=>'both', 'expand'=>'yes')
+
+# Display a 3x3 rectangular grid
+TkcRectangle.new(cvs, '0c', '0c', '30c', '24c', 'width'=>2)
+TkcLine.new(cvs, '0c', '8c', '30c', '8c', 'width'=>2)
+TkcLine.new(cvs, '0c', '16c', '30c', '16c', 'width'=>2)
+TkcLine.new(cvs, '10c', '0c', '10c', '24c', 'width'=>2)
+TkcLine.new(cvs, '20c', '0c', '20c', '24c', 'width'=>2)
+
+font1 = '-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*'
+font2 = '-Adobe-Helvetica-Bold-R-Normal--*-240-*-*-*-*-*-*'
+if TkWinfo.depth($root).to_i > 1
+ blue = 'DeepSkyBlue3'
+ red = 'red'
+ bisque = 'bisque3'
+ green = 'SeaGreen3'
+else
+ blue = 'black'
+ red = 'black'
+ bisque = 'black'
+ green = 'black'
+end
+
+# tag
+$tag_item = TkcGroup.new(cvs)
+
+# Set up demos within each of the areas of the grid.
+TkcText.new(cvs, '5c', '.2c', 'text'=>'Lines', 'anchor'=>'n')
+TkcLine.new(cvs, '1c', '1c', '3c', '1c', '1c', '4c', '3c', '4c',
+ 'width'=>2, 'fill'=>blue, 'capstyle'=>'butt',
+ 'join'=>'miter', 'tags'=>$tag_item )
+TkcLine.new(cvs, '4.67c','1c','4.67c','4c', 'arrow'=>'last', 'tags'=>$tag_item)
+TkcLine.new(cvs, '6.33c','1c','6.33c','4c', 'arrow'=>'both', 'tags'=>$tag_item)
+TkcLine.new(cvs, '5c','6c','9c','6c','9c','1c','8c','1c','8c','4.8c','8.8c',
+ '4.8c','8.8c','1.2c','8.2c','1.2c','8.2c','4.6c','8.6c','4.6c',
+ '8.6c','1.4c','8.4c','1.4c','8.4c','4.4c',
+ 'width'=>3, 'fill'=>red, 'tags'=>$tag_item )
+TkcLine.new(cvs, '1c','5c','7c','5c','7c','7c','9c','7c', 'width'=>'.5c',
+ 'stipple'=>'@'+[$demo_dir,
+ 'images','gray25.xbm'].join(File::Separator),
+ 'arrow'=>'both', 'arrowshape'=>'15 15 7', 'tags'=>$tag_item )
+TkcLine.new(cvs, '1c','7c','1.75c','5.8c','2.5c','7c','3.25c','5.8c','4c','7c',
+ 'width'=>'.5c', 'capstyle'=>'round', 'join'=>'round',
+ 'tags'=>$tag_item )
+
+TkcText.new(cvs, '15c', '.2c',
+ 'text'=>'Curves (smoothed lines)', 'anchor'=>'n')
+TkcLine.new(cvs, '11c','4c','11.5c','1c','13.5c','1c','14c','4c',
+ 'smooth'=>'on', 'fill'=>blue, 'tags'=>$tag_item )
+TkcLine.new(cvs, '15.5c','1c','19.5c','1.5c','15.5c','4.5c','19.5c','4c',
+ 'smooth'=>'on', 'arrow'=>'both', 'width'=>3, 'tags'=>$tag_item )
+TkcLine.new(cvs, '12c','6c','13.5c','4.5c','16.5c','7.5c','18c','6c',
+ '16.5c','4.5c','13.5c','7.5c','12c','6c',
+ 'smooth'=>'on', 'width'=>'3m', 'capstyle'=>'round',
+ 'stipple'=>'@'+[$demo_dir,
+ 'images', 'gray25.xbm'].join(File::Separator),
+ 'fill'=>red, 'tags'=>$tag_item )
+
+TkcText.new(cvs, '25c', '.2c', 'text'=>'Polygons', 'anchor'=>'n')
+TkcPolygon.new(cvs, '21c','1.0c','22.5c','1.75c','24c','1.0c','23.25c','2.5c',
+ '24c','4.0c','22.5c','3.25c','21c','4.0c','21.75c','2.5c',
+ 'fill'=>'green', 'outline'=>'black', 'width'=>4,
+ 'tags'=>$tag_item )
+TkcPolygon.new(cvs, '25c','4c','25c','4c','25c','1c','26c','1c','27c','4c',
+ '28c','1c','29c','1c','29c','4c','29c','4c',
+ 'fill'=>red, 'smooth'=>'on', 'tags'=> $tag_item)
+TkcPolygon.new(cvs, '22c','4.5c','25c','4.5c','25c','6.75c','28c','6.75c',
+ '28c','5.25c','24c','5.25c','24c','6.0c','26c','6c','26c',
+ '7.5c','22c','7.5c',
+ 'stipple'=>'@' + [$demo_dir,
+ 'images', 'gray25.xbm'].join(File::Separator),
+ 'outline'=>'black', 'tags'=>$tag_item )
+
+TkcText.new(cvs, '5c', '8.2c', 'text'=>'Rectangles', 'anchor'=>'n')
+TkcRectangle.new(cvs, '1c','9.5c','4c','12.5c',
+ 'outline'=>red, 'width'=>'3m', 'tags'=>$tag_item)
+TkcRectangle.new(cvs, '0.5c','13.5c','4.5c','15.5c',
+ 'fill'=>green, 'tags'=>$tag_item )
+TkcRectangle.new(cvs, '6c','10c','9c','15c', 'outline'=>'',
+ 'stipple'=>'@'+[$demo_dir,
+ 'images','gray25.xbm'].join(File::Separator),
+ 'fill'=>blue, 'tags'=>$tag_item )
+
+TkcText.new(cvs, '15c', '8.2c', 'text'=>'Ovals', 'anchor'=>'n')
+TkcOval.new(cvs, '11c','9.5c','14c','12.5c',
+ 'outline'=>red, 'width'=>'3m', 'tags'=>$tag_item)
+TkcOval.new(cvs, '10.5c','13.5c','14.5c','15.5c',
+ 'fill'=>green, 'tags'=>$tag_item )
+TkcOval.new(cvs, '16c','10c','19c','15c', 'outline'=>'',
+ 'stipple'=>'@'+[$demo_dir,
+ 'images','gray25.xbm'].join(File::Separator),
+ 'fill'=>blue, 'tags'=>$tag_item )
+
+TkcText.new(cvs, '25c', '8.2c', 'text'=>'Text', 'anchor'=>'n')
+TkcRectangle.new(cvs, '22.4c','8.9c','22.6c','9.1c')
+TkcText.new(cvs, '22.5c', '9c', 'anchor'=>'n', 'font'=>font1, 'width'=>'4c',
+ 'text'=>'A short string of text, word-wrapped, justified left, and anchored north (at the top). The rectangles show the anchor points for each piece of text.', 'tags'=>$tag_item )
+TkcRectangle.new(cvs, '25.4c','10.9c','25.6c','11.1c')
+TkcText.new(cvs, '25.5c', '11c', 'anchor'=>'w', 'font'=>font1, 'fill'=>blue,
+ 'text'=>'Several lines,\n each centered\nindividually,\nand all anchored\nat the left edge.', 'justify'=>'center', 'tags'=>$tag_item )
+TkcRectangle.new(cvs, '24.9c','13.9c','25.1c','14.1c')
+if $tk_version =~ /^4\.[01]/
+ TkcText.new(cvs, '25c', '14c', 'anchor'=>'c', 'font'=>font2, 'fill'=>red,
+ 'stipple'=>'@' + [$demo_dir,
+ 'images', 'grey.5'].join(File::Separator),
+ 'text'=>'Stippled characters', 'tags'=>$tag_item )
+else
+ TkcText.new(cvs, '25c', '14c', 'anchor'=>'c', 'font'=>font2, 'fill'=>red,
+ 'stipple'=>'gray50', 'text'=>'Stippled characters',
+ 'tags'=>$tag_item )
+end
+
+TkcText.new(cvs, '5c', '16.2c', 'text'=>'Arcs', 'anchor'=>'n')
+TkcArc.new(cvs, '0.5c','17c','7c','20c', 'fill'=>green, 'outline'=>'black',
+ 'start'=>45, 'extent'=>270, 'style'=>'pieslice', 'tags'=>$tag_item)
+#TkcArc.new(cvs, '6.5c','17c','9.5c','20c', 'width'=>'4m', 'style'=>'arc',
+# 'outline'=>blue, 'start'=>135, 'extent'=>270,
+# 'outlinestipple'=>'@' + ['images', 'grey.25'].join(File::Separator),
+# 'tags'=>$tag_item)
+TkcArc.new(cvs, '6.5c','17c','9.5c','20c', 'width'=>'4m', 'style'=>'arc',
+ 'outline'=>blue, 'start'=>135, 'extent'=>270,
+ 'outlinestipple'=>'@'+[$demo_dir,
+ 'images','gray25.xbm'].join(File::Separator),
+ 'tags'=>$tag_item)
+TkcArc.new(cvs, '0.5c','20c','9.5c','24c', 'width'=>'4m', 'style'=>'pieslice',
+ 'fill'=>'', 'outline'=>red, 'start'=>225, 'extent'=>90,
+ 'tags'=>$tag_item)
+TkcArc.new(cvs, '5.5c','20.5c','9.5c','23.5c', 'width'=>'4m', 'style'=>'chord',
+ 'fill'=>blue, 'outline'=>'', 'start'=>45, 'extent'=>270,
+ 'tags'=>$tag_item)
+
+TkcText.new(cvs, '15c', '16.2c', 'text'=>'Bitmaps', 'anchor'=>'n')
+#TkcBitmap.new(cvs, '13c','20c',
+# 'bitmap'=>'@' + ['images', 'face'].join(File::Separator),
+# 'tags'=>$tag_item)
+TkcBitmap.new(cvs, '13c','20c',
+ 'bitmap'=>'@' + [$demo_dir,
+ 'images', 'face.xbm'].join(File::Separator),
+ 'tags'=>$tag_item)
+#TkcBitmap.new(cvs, '17c','18.5c',
+# 'bitmap'=>'@' + ['images', 'noletters'].join(File::Separator),
+# 'tags'=>$tag_item)
+TkcBitmap.new(cvs, '17c','18.5c',
+ 'bitmap'=>'@' + [$demo_dir,
+ 'images', 'noletter.xbm'].join(File::Separator),
+ 'tags'=>$tag_item)
+#TkcBitmap.new(cvs, '17c','21.5c',
+# 'bitmap'=>'@' + ['images', 'letters'].join(File::Separator),
+# 'tags'=>$tag_item)
+#
+TkcBitmap.new(cvs, '17c','21.5c') {
+ bitmap '@' + [$demo_dir, 'images', 'letters.xbm'].join(File::Separator)
+ tags $tag_item
+}
+#TkcBitmap.new(cvs, '17c','21.5c') {
+# bitmap '@' + ['images', 'letters'].join(File::Separator)
+# tags $tag_item
+#}
+
+TkcText.new(cvs, '25c', '16.2c', 'text'=>'Windows', 'anchor'=>'n')
+TkButton.new(cvs) {|b|
+ text 'Press Me'
+ command proc{butPress cvs, red}
+ TkcWindow.new(cvs, '21c','18c',
+ 'window'=>b, 'anchor'=>'nw', 'tags'=>$tag_item)
+}
+TkEntry.new(cvs, 'width'=>20, 'relief'=>'sunken') {|e|
+ insert 'end', 'Edit this text'
+ TkcWindow.new(cvs, '21c','21c',
+ 'window'=>e, 'anchor'=>'nw', 'tags'=>$tag_item)
+}
+TkScale.new(cvs, 'from'=>0, 'to'=>100, 'length'=>'6c', 'sliderlength'=>'.4c',
+ 'width'=>'.5c', 'tickinterval'=>0 ) {|s|
+ TkcWindow.new(cvs, '28.5c','17.5c',
+ 'window'=>s, 'anchor'=>'n', 'tags'=>$tag_item)
+}
+TkcText.new(cvs, '21c', '17.9c', 'text'=>'Button:', 'anchor'=>'sw')
+TkcText.new(cvs, '21c', '20.9c', 'text'=>'Entry:', 'anchor'=>'sw')
+TkcText.new(cvs, '28.5c', '17.4c', 'text'=>'Scale:', 'anchor'=>'s')
+
+# Set up event bindings for canvas:
+cvs.itembind($tag_item, 'Any-Enter', proc{itemEnter cvs})
+cvs.itembind($tag_item, 'Any-Leave', proc{itemLeave cvs})
+cvs.bind('2', proc{|x,y| cvs.scan_mark x,y}, '%x %y')
+cvs.bind('B2-Motion', proc{|x,y| cvs.scan_dragto x,y}, '%x %y')
+cvs.bind('3', proc{|x,y| itemMark cvs,x,y}, '%x %y')
+cvs.bind('B3-Motion', proc{|x,y| itemStroke cvs,x,y}, '%x %y')
+cvs.bind('Control-f', proc{itemsUnderArea cvs})
+cvs.bind('1', proc{|x,y| itemStartDrag cvs,x,y}, '%x %y')
+cvs.bind('B1-Motion', proc{|x,y| itemDrag cvs,x,y}, '%x %y')
+# Utility methods for highlighting the item under the pointer
+
+$restoreCmd = nil
+def itemEnter (c)
+ if TkWinfo.depth(c).to_i == 1
+ $restoreCmd = nil
+ return
+ end
+ type = c.itemtype('current')
+ if type == TkcWindow
+ $restoreCmd = nil
+ return
+ end
+ if type == TkcBitmap
+ bg = (c.itemconfiginfo('current', 'background'))[4]
+ $restoreCmd = proc{c.itemconfigure 'current', 'background', bg}
+ c.itemconfigure 'current', 'background', 'SteelBlue2'
+ return
+ end
+ fill = (c.itemconfiginfo('current', 'fill'))[4]
+ if (type == TkcRectangle || type == TkcOval || type == TkcArc) && fill == []
+ outline = (c.itemconfiginfo('current', 'outline'))[4]
+ $restoreCmd = proc{c.itemconfigure 'current', 'outline', outline}
+ c.itemconfigure 'current', 'outline', 'SteelBlue2'
+ else
+ $restoreCmd = proc{c.itemconfigure 'current', 'fill', fill}
+ c.itemconfigure 'current', 'fill', 'SteelBlue2'
+ end
+end
+
+def itemLeave(c)
+ $restoreCmd.call if $restoreCmd
+end
+
+# Utility methods for stroking out a rectangle and printing what's
+# underneath the rectangle's area.
+
+def itemMark(c,x,y)
+ $areaX1 = c.canvasx(x)
+ $areaY1 = c.canvasy(y)
+ c.delete 'area'
+end
+
+def itemStroke(c,x,y)
+ x = c.canvasx(x)
+ y = c.canvasy(y)
+ if $areaX1 != x && $areaY1 != y
+ c.delete 'area'
+ c.addtag_withtag 'area', TkcRectangle.new(c, $areaX1, $areaY1, x, y,
+ '-outline', 'black')
+ $areaX2 = x
+ $areaY2 = y
+ end
+end
+
+def itemsUnderArea(c)
+ area = c.find_withtag('area')
+ items = []
+ c.find_enclosed($areaX1,$areaY1,$areaX2,$areaY2).each{|i|
+ items.push(i) if i.gettags.include?($tag_item)
+ }
+ print "Items enclosed by area: #{items.inspect}\n"; STDOUT.flush
+ items.clear
+ c.find_overlapping($areaX1,$areaY1,$areaX2,$areaY2).each{|i|
+ items.push(i) if i.gettags.include?($tag_item)
+ }
+ print "Items overlapping area: #{items.inspect}\n"; STDOUT.flush
+end
+
+$areaX1 = 0
+$areaY1 = 0
+$areaX2 = 0
+$areaY2 = 0
+
+# Utility methods to support dragging of items.
+
+def itemStartDrag(c,x,y)
+ $lastX = c.canvasx(x)
+ $lastY = c.canvasy(y)
+end
+
+def itemDrag(c,x,y)
+ x = c.canvasx(x)
+ y = c.canvasy(y)
+ c.move 'current', x - $lastX, y - $lastY
+ $lastX = x
+ $lastY = y
+end
+
+# Method that's invoked when the button embedded in the canvas
+# is invoked.
+
+def butPress(w,color)
+ i = TkcText.new(w, '25c', '18.1c',
+ 'text'=>'Ouch!!', 'fill'=>color, 'anchor'=>'n')
+ Tk.after(500, proc{w.delete i})
+end
diff --git a/ext/tk/sample/demos-en/ixset b/ext/tk/sample/demos-en/ixset
new file mode 100644
index 0000000000..979894fcb8
--- /dev/null
+++ b/ext/tk/sample/demos-en/ixset
@@ -0,0 +1,333 @@
+#!/usr/bin/env ruby
+
+# ixset --
+# A nice interface to "xset" to change X server settings
+#
+
+require 'tk'
+
+class Xsettings
+ #
+ # Button actions
+ #
+ def quit
+ @root.destroy
+ end
+
+ def ok
+ writesettings
+ quit
+ end
+
+ def cancel
+ readsettings
+ dispsettings
+ end
+
+ # apply is just "writesettings"
+
+
+ #
+ # Read current settings
+ #
+ def readsettings
+ xfd = open("|xset q", 'r')
+ xfd.readlines.each{|line|
+ fields = line.chomp.strip.split(/\s+/)
+ case fields[0]
+ when "auto"
+ if fields[1] == 'repeat:'
+ @kbdrep = fields[2]
+ @w_kbdrep.set(@kbdrep)
+ @kbdcli = fields[6]
+ end
+
+ when "bell"
+ @bellvol = fields[2]
+ @bellpit = fields[5]
+ @belldur = fields[8]
+
+ when "acceleration:"
+ @mouseacc = fields[1]
+ @mousethr = fields[3]
+
+ when "prefer"
+ if fields[2] == 'yes'
+ @screenbla = 'blank'
+ else
+ @screenbla = 'noblank'
+ end
+ @w_screenbla.set(@screenbla)
+
+ when "timeout:"
+ @screentim = fields[1]
+ @screencyc = fields[3]
+
+ end
+ }
+
+ xfd.close
+ end
+
+ #
+ # Write settings into the X server
+ #
+ def writesettings
+ @bellvol = @w_bellvol.get
+ @bellpit = @w_bellpit.get
+ @belldur = @w_belldur.get
+
+ @kbdrep = @w_kbdrep.get
+ if @kbdrep == 'on'
+ @kbdcli = @w_kbdcli.get
+ else
+ @kbdcli = 'off'
+ end
+
+ @mouseacc = @w_mouseacc.get
+ @mousethr = @w_mousethr.get
+
+ @screentim = @w_screentim.get
+ @screencyc = @w_screencyc.get
+ @screenbla = @w_screenbla.get
+
+ system("xset \
+ b #{@bellvol} #{@bellpit} #{@belldur} \
+ c #{@kbdcli} \
+ r #{@kbdrep} \
+ m #{@mouseacc} #{@mousethr} \
+ s #{@screentim} #{@screencyc} \
+ s #{@screenbla}")
+ end
+
+ #
+ # Sends all settings to the window
+ #
+ def dispsettings
+ @w_bellvol.set(@bellvol)
+ @w_bellpit.set(@bellpit)
+ @w_belldur.set(@belldur)
+
+ @w_kbdonoff.set(@w_kbdrep.get)
+ @w_kbdcli.set(@kbdcli)
+
+ @w_mouseacc.set(@mouseacc)
+ @w_mousethr.set(@mousethr)
+
+ @w_screenblank.set(@w_screenbla.get)
+ @w_screenpat.set(@w_screenbla.get)
+
+ @w_screentim.set(@screentim)
+ @w_screencyc.set(@screencyc)
+ end
+
+ #
+ # Create all windows, and pack them
+ #
+ class LabelEntry
+ def initialize(parent, text, length)
+ @frame = TkFrame.new(parent)
+ TkLabel.new(@frame, 'text'=>text).pack('side'=>'left','expand'=>'y')
+ @entry = TkEntry.new(@frame, 'width'=>length, 'relief'=>'sunken') {
+ pack('side'=>'left','expand'=>'y')
+ }
+ end
+ def pack(keys)
+ @frame.pack(keys)
+ end
+ def get
+ @entry.value
+ end
+ def set(value)
+ @entry.delete(0,'end')
+ @entry.insert(0, value)
+ end
+ end
+
+ def createwindows
+ win = self
+
+ #
+ # Buttons
+ #
+ buttons = TkFrame.new(@root) {|f|
+ [ TkButton.new(f, 'command'=>proc{win.ok}, 'text'=>'Ok'),
+ TkButton.new(f, 'command'=>proc{win.writesettings}, 'text'=>'Apply'),
+ TkButton.new(f, 'command'=>proc{win.cancel}, 'text'=>'Cancel'),
+ TkButton.new(f, 'command'=>proc{win.quit}, 'text'=>'Quit') ].each{|b|
+ b.pack('side'=>'left', 'expand'=>'yes', 'pady'=>5)
+ }
+ }
+
+ #
+ # Bell settings
+ #
+ bell = TkFrame.new(@root, 'relief'=>'raised', 'borderwidth'=>2)
+ l = TkLabel.new(bell, 'text'=>'Bell Settings')
+ @w_bellvol = TkScale.new(bell, 'from'=>0, 'to'=>100, 'length'=>200,
+ 'tickinterval'=>20, 'orient'=>'horizontal',
+ 'label'=>"Volume (%)")
+
+ f = TkFrame.new(bell)
+ @w_bellpit = LabelEntry.new(f, "Pitch (Hz)", 6)
+ @w_bellpit.pack('side'=>'left', 'padx'=>5)
+ @w_belldur = LabelEntry.new(f, "Duration (ms)", 6)
+ @w_belldur.pack('side'=>'right', 'padx'=>5)
+
+ l.pack('side'=>'top', 'expand'=>'yes')
+ @w_bellvol.pack('side'=>'top', 'expand'=>'yes')
+ f.pack('side'=>'top', 'expand'=>'yes')
+
+ #
+ # Keyboard settings
+ #
+ kbdonoff = nil
+ kbdcli = nil
+ kbd = TkFrame.new(@root, 'relief'=>'raised', 'borderwidth'=>2)
+ l = TkLabel.new(kbd, 'text'=>'Keyboard Repeat Settings')
+ f = TkFrame.new(kbd)
+ @w_kbdonoff = TkCheckButton.new(f, 'text'=>'On', 'relief'=>'flat',
+ 'onvalue'=>'on', 'offvalue'=>'off',
+ 'variable'=>@w_kbdrep ) {
+ def self.set(value)
+ if value == 'on'
+ self.select
+ else
+ self.deselect
+ end
+ end
+ pack('side'=>'left', 'expand'=>'yes', 'fill'=>'both')
+ }
+ @w_kbdcli = TkScale.new(f, 'from'=>0, 'to'=>100, 'length'=>200,
+ 'tickinterval'=>20, 'orient'=>'horizontal',
+ 'label'=>'Click Volume (%)')
+ @w_kbdcli.pack('side'=>'left', 'expand'=>'yes')
+ l.pack('side'=>'top', 'expand'=>'yes')
+ f.pack('side'=>'top', 'expand'=>'yes', 'pady'=>2, 'fill'=>'x')
+
+ #
+ # Mouse settings
+ #
+ mouse = TkFrame.new(@root, 'relief'=>'raised', 'borderwidth'=>2)
+ l = TkLabel.new(mouse, 'text'=>'Mouse Settings')
+ f = TkFrame.new(mouse)
+ @w_mouseacc = LabelEntry.new(f, 'Acceleration', 3)
+ @w_mouseacc.pack('side'=>'left')
+ @w_mousethr = LabelEntry.new(f, 'Threshold (pixels)', 3)
+ @w_mousethr.pack('side'=>'right')
+ l.pack('side'=>'top')
+ f.pack('side'=>'top', 'expand'=>'yes')
+
+ #
+ # Screen Saver settings
+ #
+ screen = TkFrame.new(@root, 'relief'=>'raised', 'borderwidth'=>2)
+ l = TkLabel.new(screen, 'text'=>'Screen-saver Settings')
+ f = TkFrame.new(screen)
+ ff1 = TkFrame.new(f)
+ [ @w_screenblank = TkRadioButton.new(ff1, 'text'=>'Blank',
+ 'relief'=>'flat',
+ 'variable'=>@w_screenbla,
+ 'value'=>'blank') {
+ def self.set(value)
+ if value == 'blank'
+ self.select
+ else
+ self.deselect
+ end
+ end
+ },
+ @w_screenpat = TkRadioButton.new(ff1, 'text'=>'Pattern',
+ 'relief'=>'flat',
+ 'variable'=>@w_screenbla,
+ 'value'=>'noblank') {
+ def self.set(value)
+ if value != 'blank'
+ self.select
+ else
+ self.deselect
+ end
+ end
+ }
+ ].each {|w| w.pack('side'=>'top', 'pady'=>2, 'anchor'=>'w') }
+
+ ff2 = TkFrame.new(f)
+ [ @w_screentim = LabelEntry.new(ff2, 'Timeout (s)', 5),
+ @w_screencyc = LabelEntry.new(ff2, 'Cycle (s)', 5) ].each{|w|
+ w.pack('side'=>'top', 'pady'=>2, 'anchor'=>'e')
+ }
+
+ ff1.pack('side'=>'left')
+ ff2.pack('side'=>'left')
+
+ l.pack('side'=>'top')
+ f.pack('side'=>'top', 'expand'=>'yes')
+
+ #
+ # Main window
+ #
+ buttons.pack('side'=>'top', 'fill'=>'both')
+ bell.pack('side'=>'top', 'fill'=>'both', 'ipady'=>5, 'expand'=>'yes')
+ kbd.pack('side'=>'top', 'fill'=>'both', 'ipady'=>5, 'expand'=>'yes')
+ mouse.pack('side'=>'top', 'fill'=>'both', 'ipady'=>5, 'expand'=>'yes')
+ screen.pack('side'=>'top', 'fill'=>'both', 'ipady'=>5, 'expand'=>'yes')
+
+ #
+ # Let the user resize our window
+ #
+ @root.minsize(10,10)
+ end
+
+ def initialize
+ @root = TkRoot.new
+
+ @kbdrep = 'on'
+ @w_kbdrep = TkVariable.new(@kbdrep)
+ def @w_kbdrep.get
+ self.value
+ end
+ def @w_kbdrep.set(val)
+ self.value=val
+ end
+
+ @kbdcli = 0
+
+ @bellvol = 100
+ @bellpit = 440
+ @belldur = 100
+
+ @mouseacc = "3/1"
+ @mousethr = 4
+
+ @screenbla = "blank"
+ @w_screenbla = TkVariable.new(@screenbla)
+ def @w_screenbla.get
+ self.value
+ end
+ def @w_screenbla.set(val)
+ self.value=val
+ end
+
+ @screentim = 600
+ @screencyc = 600
+
+ #
+ # Listen what "xset" tells us...
+ #
+ readsettings
+
+ #
+ # Create all windows
+ #
+ createwindows
+
+ #
+ # Write xset parameters
+ #
+ dispsettings
+ end
+end
+
+Xsettings.new
+
+Tk.mainloop
diff --git a/ext/tk/sample/demos-en/label.rb b/ext/tk/sample/demos-en/label.rb
new file mode 100644
index 0000000000..ef1bfa6f8a
--- /dev/null
+++ b/ext/tk/sample/demos-en/label.rb
@@ -0,0 +1,69 @@
+# label.rb
+#
+# This demonstration script creates a toplevel window containing
+# several label widgets.
+#
+# label widget demo (called by 'widget')
+#
+
+# toplevel widget
+if defined?($label_demo) && $label_demo
+ $label_demo.destroy
+ $label_demo = nil
+end
+
+# demo toplevel widget
+$label_demo = TkToplevel.new {|w|
+ title("Label Demonstration")
+ iconname("label")
+ positionWindow(w)
+}
+
+# label
+msg = TkLabel.new($label_demo) {
+ font $font
+ wraplength '4i'
+ justify 'left'
+ text "Five labels are displayed below: three textual ones on the left, and a bitmap label and a text label on the right. Labels are pretty boring because you can't do anything with them."
+}
+msg.pack('side'=>'top')
+
+# frame
+TkFrame.new($label_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $label_demo
+ $label_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'See Code'
+ command proc{showCode 'label'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# label demo
+f_left = TkFrame.new($label_demo)
+f_right = TkFrame.new($label_demo)
+[f_left, f_right].each{|w| w.pack('side'=>'left', 'expand'=>'yes',
+ 'padx'=>10, 'pady'=>10, 'fill'=>'both')}
+
+# label
+[ TkLabel.new(f_left, 'text'=>'First label'),
+ TkLabel.new(f_left, 'text'=>'Second label, raised',
+ 'relief'=>'raised'),
+ TkLabel.new(f_left, 'text'=>'Third label, sunken', 'relief'=>'sunken')
+].each{|w| w.pack('side'=>'top', 'expand'=>'yes', 'pady'=>2, 'anchor'=>'w')}
+
+TkLabel.new(f_right) {
+ bitmap('@' + [$demo_dir,'images','face.xbm'].join(File::Separator))
+ borderwidth 2
+ relief 'sunken'
+}.pack('side'=>'top')
+
+TkLabel.new(f_right) { text 'Tcl/Tk Proprietor' }.pack('side'=>'top')
+
diff --git a/ext/tk/sample/demos-en/menu.rb b/ext/tk/sample/demos-en/menu.rb
new file mode 100644
index 0000000000..7ea3fd2ebb
--- /dev/null
+++ b/ext/tk/sample/demos-en/menu.rb
@@ -0,0 +1,185 @@
+#
+# menus widget demo (called by 'widget')
+#
+
+# toplevel widget
+if defined?($menu_demo) && $menu_demo
+ $menu_demo.destroy
+ $menu_demo = nil
+end
+
+# demo toplevel widget
+$menu_demo = TkToplevel.new {|w|
+ title("File Selection Dialogs")
+ iconname("menu")
+ positionWindow(w)
+}
+
+# menu frame À¸À®
+$menu_frame = TkFrame.new($menu_demo, 'relief'=>'raised', 'bd'=>2)
+$menu_frame.pack('side'=>'top', 'fill'=>'x')
+
+# label
+TkLabel.new($menu_demo,'font'=>$font,'wraplength'=>'4i','justify'=>'left') {
+ if $tk_platform['platform'] == 'macintosh'
+ text("This window contains a menubar with cascaded menus. You can invoke entries with an accelerator by typing Command+x, where \"x\" is the character next to the command key symbol. The rightmost menu can be torn off into a palette by dragging outside of its bounds and releasing the mouse.")
+ else
+ text("This window contains a menubar with cascaded menus. You can post a menu from the keyboard by typing Alt+x, where \"x\" is the character underlined on the menu. You can then traverse among the menus using the arrow keys. When a menu is posted, you can invoke the current entry by typing space, or you can invoke any entry by typing its underlined character. If a menu entry has an accelerator, you can invoke the entry without posting the menu just by typing the accelerator. The rightmost menu can be torn off into a palette by selecting the first item in the menu.")
+ end
+}.pack('side'=>'top')
+
+# frame
+TkFrame.new($menu_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $menu_demo
+ $menu_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'menu'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# menu
+TkMenubutton.new($menu_frame, 'text'=>'File', 'underline'=>0) {|m|
+ pack('side'=>'left')
+ TkMenu.new(m, 'tearoff'=>false) {|file_menu|
+ m.configure('menu'=>file_menu)
+ add('command', 'label'=>'Open...', 'command'=>proc{fail 'this is just a demo: no action has been defined for the "Open..." entry'})
+ add('command', 'label'=>'New', 'command'=>proc{fail 'this is just a demo: no action has been defined for the "New" entry'})
+ add('command', 'label'=>'Save', 'command'=>proc{fail 'this is just a demo: no action has been defined for the "Save" entry'})
+ add('command', 'label'=>'Save As...', 'command'=>proc{fail 'this is just a demo: no action has been defined for the "Save As..." entry'})
+ add('separator')
+ add('command', 'label'=>'Print Setup...', 'command'=>proc{fail 'this is just a demo: no action has been defined for the "Print Setup..." entry'})
+ add('command', 'label'=>'Print...', 'command'=>proc{fail 'this is just a demo: no action has been defined for the "Print..." entry'})
+ add('separator')
+ add('command', 'label'=>'Dismiss Menus Demo', 'command'=>proc{$menu_demo.destroy})
+ }
+}
+
+if $tk_platform['platform'] == 'macintosh'
+ modifier = 'Command'
+elsif $tk_platform['platform'] == 'windows'
+ modifier = 'Control'
+else
+ modifier = 'Meta'
+end
+
+TkMenubutton.new($menu_frame, 'text'=>'Basic', 'underline'=>0) {|m|
+ pack('side'=>'left')
+ TkMenu.new(m, 'tearoff'=>false) {|basic_menu|
+ m.configure('menu'=>basic_menu)
+ add('command', 'label'=>'Long entry that does nothing')
+ ['A','B','C','D','E','F','G'].each{|c|
+ add('command', 'label'=>"Print letter \"#{c}\"",
+ 'underline'=>14, 'accelerator'=>"Meta+#{c}",
+ 'command'=>proc{print c,"\n"}, 'accelerator'=>"#{modifier}+#{c}")
+ $menu_demo.bind("#{modifier}-#{c.downcase}", proc{print c,"\n"})
+ }
+ }
+}
+
+TkMenubutton.new($menu_frame, 'text'=>'Cascades', 'underline'=>0) {|m|
+ pack('side'=>'left')
+ TkMenu.new(m, 'tearoff'=>false) {|cascade_menu|
+ m.configure('menu'=>cascade_menu)
+ add('command', 'label'=>'Print hello',
+ 'command'=>proc{print "Hello\n"},
+ 'accelerator'=>"#{modifier}+H", 'underline'=>6)
+ $menu_demo.bind("#{modifier}-h", proc{print "Hello\n"})
+ add('command', 'label'=>'Print goodbye',
+ 'command'=>proc{print "Goodbye\n"},
+ 'accelerator'=>"#{modifier}+G", 'underline'=>6)
+ $menu_demo.bind("#{modifier}-g", proc{print "Goodbye\n"})
+
+ TkMenu.new(m, 'tearoff'=>false) {|cascade_check|
+ cascade_menu.add('cascade', 'label'=>'Check buttons',
+ 'menu'=>cascade_check, 'underline'=>0)
+ oil = TkVariable.new(0)
+ add('check', 'label'=>'Oil checked', 'variable'=>oil)
+ trans = TkVariable.new(0)
+ add('check', 'label'=>'Transmission checked', 'variable'=>trans)
+ brakes = TkVariable.new(0)
+ add('check', 'label'=>'Brakes checked', 'variable'=>brakes)
+ lights = TkVariable.new(0)
+ add('check', 'label'=>'Lights checked', 'variable'=>lights)
+ add('separator')
+ add('command', 'label'=>'Show current values',
+ 'command'=>proc{showVars($menu_demo,
+ ['oil', oil],
+ ['trans', trans],
+ ['brakes', brakes],
+ ['lights', lights])} )
+ invoke 1
+ invoke 3
+ }
+
+ TkMenu.new(m, 'tearoff'=>false) {|cascade_radio|
+ cascade_menu.add('cascade', 'label'=>'Radio buttons',
+ 'menu'=>cascade_radio, 'underline'=>0)
+ pointSize = TkVariable.new
+ add('radio', 'label'=>'10 point', 'variable'=>pointSize, 'value'=>10)
+ add('radio', 'label'=>'14 point', 'variable'=>pointSize, 'value'=>14)
+ add('radio', 'label'=>'18 point', 'variable'=>pointSize, 'value'=>18)
+ add('radio', 'label'=>'24 point', 'variable'=>pointSize, 'value'=>24)
+ add('radio', 'label'=>'32 point', 'variable'=>pointSize, 'value'=>32)
+ add('separator')
+ style = TkVariable.new
+ add('radio', 'label'=>'Roman', 'variable'=>style, 'value'=>'roman')
+ add('radio', 'label'=>'Bold', 'variable'=>style, 'value'=>'bold')
+ add('radio', 'label'=>'Italic', 'variable'=>style, 'value'=>'italic')
+ add('separator')
+ add('command', 'label'=>'Show current values',
+ 'command'=>proc{showVars($menu_demo,
+ ['pointSize', pointSize],
+ ['style', style])} )
+ invoke 1
+ invoke 7
+ }
+ }
+}
+
+TkMenubutton.new($menu_frame, 'text'=>'Icons', 'underline'=>0) {|m|
+ pack('side'=>'left')
+ TkMenu.new(m, 'tearoff'=>false) {|icon_menu|
+ m.configure('menu'=>icon_menu)
+ add('command',
+ 'bitmap'=>'@'+[$demo_dir,'images','pattern.xbm'].join(File::Separator),
+ 'command'=>proc{TkDialog.new('title'=>'Bitmap Menu Entry',
+ 'text'=>'The menu entry you invoked displays a bitmap rather than a text string. Other than this, it is just like any other menu entry.',
+ 'bitmap'=>'', 'default'=>0,
+ 'buttons'=>'Dismiss')} )
+ ['info', 'questhead', 'error'].each{|icon|
+ add('command', 'bitmap'=>icon,
+ 'command'=>proc{print "You invoked the #{icon} bitmap\n"})
+ }
+ }
+}
+
+TkMenubutton.new($menu_frame, 'text'=>'More', 'underline'=>0) {|m|
+ pack('side'=>'left')
+ TkMenu.new(m, 'tearoff'=>false) {|more_menu|
+ m.configure('menu'=>more_menu)
+ [ 'An entry','Another entry','Does nothing','Does almost nothing',
+ 'Make life meaningful' ].each{|i|
+ add('command', 'label'=>i,
+ 'command'=>proc{print "You invoked \"#{i}\"\n"})
+ }
+ }
+}
+
+TkMenubutton.new($menu_frame, 'text'=>'Colors', 'underline'=>0) {|m|
+ pack('side'=>'left')
+ TkMenu.new(m) {|colors_menu|
+ m.configure('menu'=>colors_menu)
+ ['red', 'orange', 'yellow', 'green', 'blue'].each{|c|
+ add('command', 'label'=>c, 'background'=>c,
+ 'command'=>proc{print "You invoked \"#{c}\"\n"})
+ }
+ }
+}
diff --git a/ext/tk/sample/demos-en/menubu.rb b/ext/tk/sample/demos-en/menubu.rb
new file mode 100644
index 0000000000..4c19aa12a8
--- /dev/null
+++ b/ext/tk/sample/demos-en/menubu.rb
@@ -0,0 +1,225 @@
+# menubutton.rb
+#
+# This demonstration script creates a window with a bunch of menus
+# and cascaded menus using menubuttons.
+
+require "tkcanvas"
+
+def optionMenu(menubutton, varName, firstValue, *rest)
+ varName.value = firstValue
+ configoptions = {'textvariable'=>varName,'indicatoron'=>'on',
+ 'relief'=>'raised','borderwidth'=>2,'highlightthickness'=>2,
+ 'anchor'=>'c','direction'=>'flush'}
+ configoptions.each {|key, value|
+ menubutton.configure(key, value)
+ }
+ menu = TkMenu.new(menubutton) {
+ tearoff 'off'
+ add 'radio', 'label'=>firstValue, 'variable'=>varName
+ }
+ menubutton.menu(menu)
+ for i in rest
+ menu.add 'radio', 'label'=>i, 'variable'=>varName
+ end
+
+ return menu
+end
+
+if defined?($menubu_demo) && $menubu_demo
+ $menubu_demo.destroy
+ $menubu_demo = nil
+end
+
+$menubu_demo = TkToplevel.new {|w|
+ title("Menu Button Demonstration")
+ iconname("menubutton")
+}
+
+positionWindow($menubu_demo)
+
+# version check
+if $tk_version.to_f < 8.0
+
+# label
+TkLabel.new($menubu_demo,'font'=>$font,'wraplength'=>'4i','justify'=>'left') {
+ text("This is a demonstration of menubuttons. The \"Below\" menubutton pops its menu below the button; the \"Right\" button pops to the right, etc. There are two option menus directly below this text; one is just a standard menu and the other is a 16-color palette.")
+}.pack('side'=>'top')
+
+# frame
+TkFrame.new($menubu_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $menubu_demo
+ $menubu_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'menu'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+else ; # Tk8.x
+
+body = TkFrame.new($menubu_demo)
+body.pack('expand'=>'yes', 'fill'=>'both')
+
+below = TkMenubutton.new(body) {
+ text "Below"
+ underline 0
+ direction 'below'
+ relief 'raised'
+}
+belowMenu = TkMenu.new(below) {
+ tearoff 0
+ add 'command', 'label'=>"Below menu: first item", 'command'=>proc {puts "\"You have selected the first item from the Below menu.\""}
+ add 'command', 'label'=>"Below menu: second item", 'command'=>proc {puts "\"You have selected the second item from the Below menu.\""}
+}
+below.menu(belowMenu)
+below.grid('row'=>0, 'column'=>1, 'sticky'=>'n')
+
+below = TkMenubutton.new(body) {
+ text "Below"
+ underline 0
+ direction 'below'
+ relief 'raised'
+}
+belowMenu = TkMenu.new(below) {
+ tearoff 0
+ add 'command', 'label'=>"Below menu: first item", 'command'=>proc {puts "\"You have selected the first item from the Below menu.\""}
+ add 'command', 'label'=>"Below menu: second item", 'command'=>proc {puts "\"You have selected the second item from the Below menu.\""}
+}
+below.menu(belowMenu)
+below.grid('row'=>0, 'column'=>1, 'sticky'=>'n')
+
+below = TkMenubutton.new(body) {
+ text "Below"
+ underline 0
+ direction 'below'
+ relief 'raised'
+}
+belowMenu = TkMenu.new(below) {
+ tearoff 0
+ add 'command', 'label'=>"Below menu: first item", 'command'=>proc {puts "\"You have selected the first item from the Below menu.\""}
+ add 'command', 'label'=>"Below menu: second item", 'command'=>proc {puts "\"You have selected the second item from the Below menu.\""}
+}
+below.menu(belowMenu)
+below.grid('row'=>0, 'column'=>1, 'sticky'=>'n')
+
+right = TkMenubutton.new(body) {
+ text "Right"
+ underline 0
+ direction 'right'
+ relief 'raised'
+}
+rightMenu = TkMenu.new(right) {
+ tearoff 0
+ add 'command', 'label'=>"Right menu: first item", 'command'=>proc {puts "\"You have selected the first item from the Left menu.\""}
+ add 'command', 'label'=>"Right menu: second item", 'command'=>proc {puts "\"You have selected the second item from the Right menu.\""}
+}
+right.menu(rightMenu)
+right.grid('row'=>1, 'column'=>0, 'sticky'=>'w')
+
+left = TkMenubutton.new(body) {
+ text "Left"
+ underline 0
+ direction 'left'
+ relief 'raised'
+}
+leftMenu = TkMenu.new(left) {
+ tearoff 0
+ add 'command', 'label'=>"Left menu: first item", 'command'=>proc {puts "\"You have selected the first item from the Left menu.\""}
+ add 'command', 'label'=>"Left menu: second item", 'command'=>proc {puts "\"You have selected the second item from the Left menu.\""}
+}
+left.menu(leftMenu)
+left.grid('row'=>1, 'column'=>2, 'sticky'=>'e')
+
+center = TkFrame.new(body) {
+ grid('row'=>1, 'column'=>1, 'sticky'=>'news')
+}
+
+above = TkMenubutton.new(body) {
+ text "Above"
+ underline 0
+ direction 'above'
+ relief 'raised'
+}
+aboveMenu = TkMenu.new(above) {
+ tearoff 0
+ add 'command', 'label'=>"Above menu: first item", 'command'=>proc {puts "\"You have selected the first item from the Above menu.\""}
+ add 'command', 'label'=>"Above menu: second item", 'command'=>proc {puts "\"You have selected the second item from the Above menu.\""}
+}
+above.menu(aboveMenu)
+above.grid('row'=>2, 'column'=>1, 'sticky'=>'s')
+
+center = TkFrame.new(body) {
+ grid('row'=>1, 'column'=>1, 'sticky'=>'news')
+}
+
+TkFrame.new($menubu_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc {
+ tmppath = $menubu_demo
+ $menubu_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc { showCode 'menubu' }
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'expand'=>'yes', 'fill'=>'x', 'pady'=>'2m')
+
+msg = TkLabel.new(center) {
+# font $font
+ wraplength '4i'
+ justify 'left'
+ text "This is a demonstration of menubuttons. The \"Below\" menubutton pops its menu below the button; the \"Right\" button pops to the right, etc. There are two option menus directly below this text; one is just a standard menu and the other is a 16-color palette."
+}
+msg.pack('side'=>'top', 'padx'=>25, 'pady'=>25)
+
+TkFrame.new(center) {|f|
+ menubuttonoptions = TkVariable.new
+ mbutton = TkMenubutton.new(f)
+ options = optionMenu(mbutton, menubuttonoptions,
+ 'one', 'two', 'three')
+ mbutton.pack('side'=>'left', 'padx'=>25, 'pady'=>25)
+ paletteColor = TkVariable.new
+ colors = ['Black','red4','DarkGreen','NavyBlue', 'gray75',
+ 'Red','Green','Blue','gray50','Yellow','Cyan','Magenta',
+ 'White','Brown','DarkSeaGreen','DarkViolet']
+ colorMenuButton = TkMenubutton.new(f)
+ m = optionMenu(colorMenuButton, paletteColor, *colors)
+ topBorderColor = 'gray50'
+ bottomBorderColor = 'gray75'
+ for i in 0..15
+ image = TkPhotoImage.new('height'=>16, 'width'=>16)
+ image.put(topBorderColor, 0, 0, 16, 1)
+ image.put(topBorderColor, 0, 1, 1, 16)
+ image.put(bottomBorderColor, 0, 15, 16, 16)
+ image.put(bottomBorderColor, 15, 1, 16, 16)
+ image.put(colors[i], 1, 1, 15, 15)
+
+ selectimage = TkPhotoImage.new('height'=>16, 'width'=>16)
+ selectimage.put('Black', 0, 0, 16, 2)
+ selectimage.put('Black', 0, 2, 2, 16)
+ selectimage.put('Black', 2, 14, 16, 16)
+ selectimage.put('Black', 14, 2, 16, 14)
+ selectimage.put(colors[i], 2, 2, 14, 14)
+
+ m.entryconfigure(i, 'image'=>image, 'selectimage'=>selectimage, 'hidemargin'=>'on')
+ end
+ m.configure('tearoff', 'on')
+ for c in ['Black', 'gray75', 'gray50', 'White']
+ m.entryconfigure(c, 'columnbreak'=>1)
+ end
+ colorMenuButton.pack('side'=>'left', 'padx'=>25, 'pady'=>25)
+ pack 'padx'=>25, 'pady'=>25
+}
+
+end ; # Tk8.x
diff --git a/ext/tk/sample/demos-en/msgbox.rb b/ext/tk/sample/demos-en/msgbox.rb
new file mode 100644
index 0000000000..4152477e67
--- /dev/null
+++ b/ext/tk/sample/demos-en/msgbox.rb
@@ -0,0 +1,88 @@
+# msgbox.rb
+#
+# This demonstration script creates message boxes of various type
+#
+# message boxes widget demo (called by 'widget')
+#
+
+# toplevel widget
+if defined?($msgbox_demo) && $msgbox_demo
+ $msgbox_demo.destroy
+ $msgbox_demo = nil
+end
+
+# demo toplevel widget
+$msgbox_demo = TkToplevel.new {|w|
+ title("Message Box Demonstration")
+ iconname("messagebox")
+ positionWindow(w)
+}
+
+# label
+TkLabel.new($msgbox_demo, 'font'=>$font, 'wraplength'=>'4i', 'justify'=>'left',
+ 'text'=>"Choose the icon and type option of the message box. Then press the \"Message Box\" button to see the message box.").pack('side'=>'top')
+
+# frame
+TkFrame.new($msgbox_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $msgbox_demo
+ $msgbox_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'msgbox'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Message Box'
+ command proc{showMessageBox $msgbox_demo}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame
+$msgbox_leftframe = TkFrame.new($msgbox_demo)
+$msgbox_rightframe = TkFrame.new($msgbox_demo)
+$msgbox_leftframe .pack('side'=>'left', 'expand'=>'yes', 'fill'=>'y',
+ 'pady'=>'.5c', 'padx'=>'.5c')
+$msgbox_rightframe.pack('side'=>'left', 'expand'=>'yes', 'fill'=>'y',
+ 'pady'=>'.5c', 'padx'=>'.5c')
+
+TkLabel.new($msgbox_leftframe, 'text'=>'Icon').pack('side'=>'top')
+TkFrame.new($msgbox_leftframe, 'relief'=>'ridge', 'bd'=>1, 'height'=>2)\
+.pack('side'=>'top', 'fill'=>'x', 'expand'=>'no')
+
+$msgboxIcon = TkVariable.new('info')
+['error', 'info', 'question', 'warning'].each {|icon|
+ TkRadioButton.new($msgbox_leftframe, 'text'=>icon, 'variable'=>$msgboxIcon,
+ 'relief'=>'flat', 'value'=>icon, 'width'=>16,
+ 'anchor'=>'w').pack('side'=>'top', 'pady'=>2,
+ 'anchor'=>'w', 'fill'=>'x')
+}
+
+TkLabel.new($msgbox_rightframe, 'text'=>'Type').pack('side'=>'top')
+TkFrame.new($msgbox_rightframe, 'relief'=>'ridge', 'bd'=>1, 'height'=>2)\
+.pack('side'=>'top', 'fill'=>'x', 'expand'=>'no')
+
+$msgboxType = TkVariable.new('ok')
+['abortretryignore', 'ok', 'okcancel',
+ 'retrycancel', 'yesno', 'yesnocancel'].each {|type|
+ TkRadioButton.new($msgbox_rightframe, 'text'=>type, 'variable'=>$msgboxType,
+ 'relief'=>'flat', 'value'=>type, 'width'=>16,
+ 'anchor'=>'w').pack('side'=>'top', 'pady'=>2,
+ 'anchor'=>'w', 'fill'=>'x')
+}
+
+def showMessageBox(w)
+ button = Tk.messageBox('icon'=>$msgboxIcon.value, 'type'=>$msgboxType.value,
+ 'title'=>'Message', 'parent'=>w,
+ 'message'=>"This is a \"#{$msgboxType.value}\" type messagebox with the \"#{$msgboxIcon.value}\" icon")
+
+ Tk.messageBox('icon'=>'info', 'type'=>'ok', 'parent'=>w,
+ 'message'=>"You have selected \"#{button}\"")
+end
+
diff --git a/ext/tk/sample/demos-en/patch_1.1c1 b/ext/tk/sample/demos-en/patch_1.1c1
new file mode 100644
index 0000000000..d3952e71eb
--- /dev/null
+++ b/ext/tk/sample/demos-en/patch_1.1c1
@@ -0,0 +1,93 @@
+--- /usr/src/ruby-1.1c1/lib/tkcanvas.rb Tue Jul 21 18:18:02 1998
++++ tkcanvas.rb Fri Jul 24 20:38:24 1998
+@@ -310,7 +310,7 @@
+ || key == 'latinfont' || key == 'asciifont' )
+ tagfont_configure(tagid(tagOrId), {key=>value})
+ else
+- tk_call 'itemconfigure', tagid(tagOrId), "-#{key}", value
++ tk_send 'itemconfigure', tagid(tagOrId), "-#{key}", value
+ end
+ end
+ end
+--- /usr/src/ruby-1.1c1/lib/tkfont.rb Fri Jul 17 23:43:28 1998
++++ tkfont.rb Fri Jul 24 17:46:22 1998
+@@ -42,7 +42,7 @@
+ r | []
+
+ when /^8\.*/
+- list(tk_call('font', 'names'))
++ tk_split_simplelist(tk_call('font', 'names'))
+
+ end
+ end
+@@ -89,10 +89,14 @@
+ if fnt == []
+ TkFont.new(nil, nil).call_font_configure(path, *(args + [{}]))
+ else
+- compound = Hash[*list(tk_call('font', 'configure',
+- fnt))].collect{|key,value|
+- [key[1..-1], value]
+- }.assoc('compound')[1]
++ begin
++ compound = Hash[*list(tk_call('font', 'configure',
++ fnt))].collect{|key,value|
++ [key[1..-1], value]
++ }.assoc('compound')[1]
++ rescue
++ compound = []
++ end
+ if compound == []
+ TkFont.new(fnt, DEFAULT_KANJI_FONT_NAME) \
+ .call_font_configure(path, *(args + [{}]))
+@@ -156,14 +160,19 @@
+ elsif font.kind_of? Array
+ finfo = {}
+ finfo['family'] = font[0].to_s
+- if font[1] && font[1] != '0' && font[1] =~ /^(|\+|-)([0-9]+)$/
+- if $1 == '-'
+- finfo['pixels'] = font[1].to_s
++ if font[1]
++ fsize = font[1].to_s
++ if fsize != '0' && fsize =~ /^(|\+|-)([0-9]+)$/
++ if $1 == '-'
++ finfo['pixels'] = $2
++ else
++ finfo['points'] = $2
++ end
+ else
+- finfo['points'] = font[1].to_s
++ finfo['points'] = '13'
+ end
+ end
+- finfo[2..-1].each{|style|
++ font[2..-1].each{|style|
+ case (style)
+ when 'normal'
+ finfo['weight'] = style
+@@ -199,16 +208,19 @@
+ elsif font.kind_of? Array
+ finfo = {}
+ finfo['family'] = font[0].to_s
+- if font[1] && font[1] != '0' && font[1] =~ /^(|\+|-)([0-9]+)$/
+- if $1 == '-'
+- finfo['pixels'] = $2
++ if font[1]
++ fsize = font[1].to_s
++ if fsize != '0' && fsize =~ /^(|\+|-)([0-9]+)$/
++ if $1 == '-'
++ finfo['pixels'] = $2
++ else
++ finfo['points'] = $2
++ end
+ else
+- finfo['points'] = $2
++ finfo['points'] = '13'
+ end
+- else
+- finfo['points'] = '13'
+ end
+- finfo[2..-1].each{|style|
++ font[2..-1].each{|style|
+ case (style)
+ when 'normal'
+ finfo['weight'] = style
diff --git a/ext/tk/sample/demos-en/plot.rb b/ext/tk/sample/demos-en/plot.rb
new file mode 100644
index 0000000000..fb887011a7
--- /dev/null
+++ b/ext/tk/sample/demos-en/plot.rb
@@ -0,0 +1,122 @@
+# plot.rb
+#
+# This demonstration script creates a canvas widget showing a 2-D
+# plot with data points that can be dragged with the mouse.
+#
+# 2-D plot widget demo (called by 'widget')
+#
+
+# toplevel widget
+if defined?($plot_demo) && $plot_demo
+ $plot_demo.destroy
+ $plot_demo = nil
+end
+
+# demo toplevel widget
+$plot_demo = TkToplevel.new {|w|
+ title("Plot Demonstration")
+ iconname("Plot")
+ positionWindow(w)
+}
+
+# label
+TkLabel.new($plot_demo, 'font'=>$font, 'wraplength'=>'4i', 'justify'=>'left',
+ 'text'=>"This window displays a canvas widget containing a simple 2-dimensional plot. You can doctor the data by dragging any of the points with mouse button 1."){
+ pack('side'=>'top')
+}
+
+# frame
+$plot_buttons = TkFrame.new($plot_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $plot_demo
+ $plot_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'plot'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}
+$plot_buttons.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# font
+plotFont = '-*-Helvetica-Medium-R-Normal--*-180-*-*-*-*-*-*'
+
+# canvas
+$plot_canvas = TkCanvas.new($plot_demo,'relief'=>'raised','width'=>450,'height'=>300)
+$plot_canvas.pack('side'=>'top', 'fill'=>'x')
+
+# plot
+TkcLine.new($plot_canvas, 100, 250, 400, 250, 'width'=>2)
+TkcLine.new($plot_canvas, 100, 250, 100, 50, 'width'=>2)
+TkcText.new($plot_canvas, 225, 20,
+ 'text'=>"A Simple Plot", 'font'=>plotFont, 'fill'=>'brown')
+
+(0..10).each {|i|
+ x = 100 + (i * 30)
+ TkcLine.new($plot_canvas, x, 250, x, 245, 'width'=>2)
+ TkcText.new($plot_canvas, x, 254,
+ 'text'=>10*i, 'font'=>plotFont, 'anchor'=>'n')
+}
+(0..5).each {|i|
+ y = 250 - (i * 40)
+ TkcLine.new($plot_canvas, 100, y, 105, y, 'width'=>2)
+ TkcText.new($plot_canvas, 96, y,
+ 'text'=>"#{i*50}.0", 'font'=>plotFont, 'anchor'=>'e')
+}
+
+for xx, yy in [[12,56],[20,94],[33,98],[32,120],[61,180],[75,160],[98,223]]
+ x = 100 + (3*xx)
+ y = 250 - (4*yy)/5
+ item = TkcOval.new($plot_canvas, x-6, y-6, x+6, y+6,
+ 'width'=>1, 'outline'=>'black', 'fill'=>'SkyBlue2')
+ item.addtag 'point'
+end
+
+$plot_canvas.itembind('point', 'Any-Enter',
+ proc{$plot_canvas.itemconfigure 'current','fill','red'})
+$plot_canvas.itembind('point', 'Any-Leave',
+ proc{$plot_canvas.itemconfigure 'current','fill','SkyBlue2'})
+$plot_canvas.itembind('point', '1',
+ proc{|x,y| plotDown $plot_canvas,x,y}, "%x %y")
+$plot_canvas.itembind('point', 'ButtonRelease-1',
+ proc{$plot_canvas.dtag 'selected'})
+$plot_canvas.bind('B1-Motion',
+ proc{|x,y| plotMove $plot_canvas,x,y}, "%x %y")
+
+$plot = {'lastX'=>0, 'lastY'=>0}
+
+# plotDown --
+# This method is invoked when the mouse is pressed over one of the
+# data points. It sets up state to allow the point to be dragged.
+#
+# Arguments:
+# w - The canvas window.
+# x, y - The coordinates of the mouse press.
+
+def plotDown (w, x, y)
+ w.dtag 'selected'
+ w.addtag_withtag 'selected', 'current'
+ w.raise 'current'
+ $plot['lastX'] = x
+ $plot['lastY'] = y
+end
+
+# plotMove --
+# This method is invoked during mouse motion events. It drags the
+# current item.
+#
+# Arguments:
+# w - The canvas window.
+# x, y - The coordinates of the mouse.
+
+def plotMove (w, x, y)
+ w.move 'selected', x - $plot['lastX'], y - $plot['lastY']
+ $plot['lastX'] = x
+ $plot['lastY'] = y
+end
+
diff --git a/ext/tk/sample/demos-en/puzzle.rb b/ext/tk/sample/demos-en/puzzle.rb
new file mode 100644
index 0000000000..91e4c8c1fb
--- /dev/null
+++ b/ext/tk/sample/demos-en/puzzle.rb
@@ -0,0 +1,111 @@
+# puzzle.rb
+#
+# This demonstration script creates a 15-puzzle game using a collection
+# of buttons.
+#
+# widet demo 'puzzle' (called by 'widget')
+#
+
+# toplevel widget
+if defined?($puzzle_demo) && $puzzle_demo
+ $puzzle_demo.destroy
+ $puzzle_demo = nil
+end
+
+# demo toplevel widget
+$puzzle_demo = TkToplevel.new {|w|
+ title("15-Puzzle Demonstration")
+ iconname("15-Puzzle")
+ positionWindow(w)
+}
+
+# label
+msg = TkLabel.new($puzzle_demo) {
+ font $font
+ wraplength '4i'
+ justify 'left'
+ text "A 15-puzzle appears below as a collection of buttons. Click on any of the pieces next to the space, and that piece will slide over the space. Continue this until the pieces are arranged in numerical order from upper-left to lower-right."
+}
+msg.pack('side'=>'top')
+
+# frame
+TkFrame.new($puzzle_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $puzzle_demo
+ $puzzle_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'puzzle'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame
+
+# Special trick: select a darker color for the space by creating a
+# scrollbar widget and using its trough color.
+
+s = TkScrollbar.new($puzzle_demo)
+base = TkFrame.new($puzzle_demo) {
+ width 120
+ height 120
+ borderwidth 2
+ relief 'sunken'
+ bg s['troughcolor']
+}
+s.destroy
+base.pack('side'=>'top', 'padx'=>'1c', 'pady'=>'1c')
+
+def def_puzzleswitch_proc(w, num)
+ proc{puzzleSwitch w, num}
+end
+
+$xpos = {}
+$ypos = {}
+order = [3,1,6,2,5,7,15,13,4,11,8,9,14,10,12]
+(0..14).each{|i|
+ num = order[i]
+ $xpos[num] = (i % 4) * 0.25
+ $ypos[num] = (i / 4) * 0.25
+ TkButton.new(base) {|w|
+ relief 'raised'
+ text num
+ highlightthickness 0
+ command def_puzzleswitch_proc(w, num)
+ }.place('relx'=>$xpos[num], 'rely'=>$ypos[num],
+ 'relwidth'=>0.25, 'relheight'=>0.25)
+}
+$xpos['space'] = 0.75
+$ypos['space'] = 0.75
+
+
+# puzzleSwitch --
+# This procedure is invoked when the user clicks on a particular button;
+# if the button is next to the empty space, it moves the button into the
+# empty space.
+
+def puzzleSwitch(w, num)
+ if ( ($ypos[num] >= ($ypos['space'] - 0.01)) \
+ && ($ypos[num] <= ($ypos['space'] + 0.01)) \
+ && ($xpos[num] >= ($xpos['space'] - 0.26)) \
+ && ($xpos[num] <= ($xpos['space'] + 0.26))) \
+ || (($xpos[num] >= ($xpos['space'] - 0.01)) \
+ && ($xpos[num] <= ($xpos['space'] + 0.01)) \
+ && ($ypos[num] >= ($ypos['space'] - 0.26)) \
+ && ($ypos[num] <= ($ypos['space'] + 0.26)))
+ tmp = $xpos['space']
+ $xpos['space'] = $xpos[num]
+ $xpos[num] = tmp
+ tmp = $ypos['space']
+ $ypos['space'] = $ypos[num]
+ $ypos[num] = tmp
+ w.place('relx'=>$xpos[num], 'rely'=>$ypos[num])
+ end
+end
+
diff --git a/ext/tk/sample/demos-en/radio.rb b/ext/tk/sample/demos-en/radio.rb
new file mode 100644
index 0000000000..96cdc4c54a
--- /dev/null
+++ b/ext/tk/sample/demos-en/radio.rb
@@ -0,0 +1,84 @@
+# radio.rb
+#
+# This demonstration script creates a toplevel window containing
+# several radiobutton widgets.
+#
+# radiobutton widget demo (called by 'widget')
+#
+
+# toplevel widget
+if defined?($radio_demo) && $radio_demo
+ $radio_demo.destroy
+ $radio_demo = nil
+end
+
+# demo toplevel widget
+$radio_demo = TkToplevel.new {|w|
+ title("Radiobutton Demonstration")
+ iconname("radio")
+ positionWindow(w)
+}
+
+# label
+msg = TkLabel.new($radio_demo) {
+ font $font
+ wraplength '4i'
+ justify 'left'
+ text "Two groups of radiobuttons are displayed below. If you click on a button then the button will become selected exclusively among all the buttons in its group. A Tcl variable is associated with each group to indicate which of the group's buttons is selected. Click the \"See Variables\" button to see the current values of the variables."
+}
+msg.pack('side'=>'top')
+
+#
+size = TkVariable.new
+color = TkVariable.new
+
+# frame
+TkFrame.new($radio_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $radio_demo
+ $radio_demo = nil
+ $showVarsWin[tmppath.path] = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'radio'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'See Variables'
+ command proc{
+ showVars($radio_demo, ['size', size], ['color', color])
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame
+f_left = TkFrame.new($radio_demo)
+f_right = TkFrame.new($radio_demo)
+f_left.pack('side'=>'left', 'expand'=>'yes', 'padx'=>'.5c', 'pady'=>'.5c')
+f_right.pack('side'=>'left', 'expand'=>'yes', 'padx'=>'.5c', 'pady'=>'.5c')
+
+# radiobutton
+[10, 12, 18, 24].each {|sz|
+ TkRadioButton.new(f_left) {
+ text "Point Size #{sz}"
+ variable size
+ relief 'flat'
+ value sz
+ }.pack('side'=>'top', 'pady'=>2, 'anchor'=>'w')
+}
+
+['Red', 'Green', 'Blue', 'Yellow', 'Orange', 'Purple'].each {|col|
+ TkRadioButton.new(f_right) {
+ text col
+ variable color
+ relief 'flat'
+ value col.downcase
+ }.pack('side'=>'top', 'pady'=>2, 'anchor'=>'w')
+}
+
diff --git a/ext/tk/sample/demos-en/rmt b/ext/tk/sample/demos-en/rmt
new file mode 100644
index 0000000000..d4b1d93760
--- /dev/null
+++ b/ext/tk/sample/demos-en/rmt
@@ -0,0 +1,265 @@
+#!/usr/bin/env ruby
+
+# rmt --
+# This script implements a simple remote-control mechanism for
+# Tk applications. It allows you to select an application and
+# then type commands to that application.
+
+require 'tk'
+
+class Rmt
+ def initialize(parent=nil)
+ win = self
+
+ unless parent
+ parent = TkRoot.new
+ end
+ root = TkWinfo.toplevel(parent)
+ root.minsize(1,1)
+
+ # The instance variable below keeps track of the remote application
+ # that we're sending to. If it's an empty string then we execute
+ # the commands locally.
+ @app = 'local'
+ @mode = 'Ruby'
+
+ # The instance variable below keeps track of whether we're in the
+ # middle of executing a command entered via the text.
+ @executing = 0
+
+ # The instance variable below keeps track of the last command executed,
+ # so it can be re-executed in response to !! commands.
+ @lastCommand = ""
+
+ # Create menu bar. Arrange to recreate all the information in the
+ # applications sub-menu whenever it is cascaded to.
+
+ TkFrame.new(root, 'relief'=>'raised', 'bd'=>2) {|f|
+ pack('side'=>'top', 'fill'=>'x')
+ TkMenubutton.new(f, 'text'=>'File', 'underline'=>0) {|mb|
+ TkMenu.new(mb) {|mf|
+ mb.menu(mf)
+ TkMenu.new(mf) {|ma|
+ postcommand proc{win.fillAppsMenu ma}
+ mf.add('cascade', 'label'=>'Select Application',
+ 'menu'=>ma, 'underline'=>0)
+ }
+ add('command', 'label'=>'Quit',
+ 'command'=>proc{root.destroy}, 'underline'=>0)
+ }
+ pack('side'=>'left')
+ }
+ }
+
+ # Create text window and scrollbar.
+
+ @txt = TkText.new(root, 'relief'=>'sunken', 'bd'=>2, 'setgrid'=>true) {|t|
+ TkScrollbar.new(root, 'command'=>proc{|*args| t.yview *args}) {
+ pack('side'=>'right', 'fill'=>'both')
+ }
+ pack('side'=>'left')
+ }
+
+ @promptEnd = TkTextMark.new(@txt, 'insert')
+
+ # Create a binding to forward commands to the target application,
+ # plus modify many of the built-in bindings so that only information
+ # in the current command can be deleted (can still set the cursor
+ # earlier in the text and select and insert; just can't delete).
+
+ @txt.bindtags([@txt, TkText, root, 'all'])
+ @txt.bind('Return', proc{
+ @txt.set_insert('end - 1c')
+ @txt.insert('insert', "\n")
+ win.invoke
+ Tk.callback_break
+ })
+ @txt.bind('Delete', proc{
+ begin
+ @txt.tag_remove('sel', 'sel.first', @promptEnd)
+ rescue
+ end
+ if @txt.tag_nextrange('sel', '1.0', 'end') == []
+ if @txt.compare('insert', '<', @promptEnd)
+ Tk.callback_break
+ end
+ end
+ })
+ @txt.bind('BackSpace', proc{
+ begin
+ @txt.tag_remove('sel', 'sel.first', @promptEnd)
+ rescue
+ end
+ if @txt.tag_nextrange('sel', '1.0', 'end') == []
+ if @txt.compare('insert', '<', @promptEnd)
+ Tk.callback_break
+ end
+ end
+ })
+ @txt.bind('Control-d', proc{
+ if @txt.compare('insert', '<', @promptEnd)
+ Tk.callback_break
+ end
+ })
+ @txt.bind('Control-k', proc{
+ if @txt.compare('insert', '<', @promptEnd)
+ @txt.set_insert(@promptEnd)
+ end
+ })
+ @txt.bind('Control-t', proc{
+ if @txt.compare('insert', '<', @promptEnd)
+ Tk.callback_break
+ end
+ })
+ @txt.bind('Meta-d', proc{
+ if @txt.compare('insert', '<', @promptEnd)
+ Tk.callback_break
+ end
+ })
+ @txt.bind('Meta-BackSpace', proc{
+ if @txt.compare('insert', '<=', @promptEnd)
+ Tk.callback_break
+ end
+ })
+ @txt.bind('Control-h', proc{
+ if @txt.compare('insert', '<=', @promptEnd)
+ Tk.callback_break
+ end
+ })
+
+ @txt.tag_configure('bold', 'font'=>['Courier', 12, 'bold'])
+
+ @app = Tk.appname('rmt')
+ if (@app =~ /^rmt(.*)$/)
+ root.title("Tk Remote Controller#{$1}")
+ root.iconname("Tk Remote#{$1}")
+ end
+ prompt
+ @txt.focus
+ #@app = TkWinfo.appname(TkRoot.new)
+ end
+
+ def tkTextInsert(w,s)
+ return if s == ""
+ begin
+ if w.compare('sel.first','<=','insert') \
+ && w.compare('sel.last','>=','insert')
+ w.tag_remove('sel', 'sel.first', @promptEnd)
+ w.delete('sel.first', 'sel.last')
+ end
+ rescue
+ end
+ w.insert('insert', s)
+ w.see('insert')
+ end
+
+ # The method below is used to print out a prompt at the
+ # insertion point (which should be at the beginning of a line
+ # right now).
+
+ def prompt
+ @txt.insert('insert', "#{@app}: ")
+ @promptEnd.set('insert')
+ @promptEnd.gravity = 'left'
+ @txt.tag_add('bold', "#{@promptEnd.path} linestart", @promptEnd)
+ end
+
+ # The method below executes a command (it takes everything on the
+ # current line after the prompt and either sends it to the remote
+ # application or executes it locally, depending on "app".
+
+ def invoke
+ cmd = @txt.get(@promptEnd, 'insert')
+ @executing += 1
+ case (@mode)
+ when 'Tcl'
+ if Tk.info('complete', cmd)
+ if (cmd == "!!\n")
+ cmd = @lastCommand
+ else
+ @lastCommand = cmd
+ end
+ begin
+ msg = Tk.appsend(@app, false, cmd)
+ rescue
+ msg = "Error: #{$!}"
+ end
+ @txt.insert('insert', msg + "\n") if msg != ""
+ prompt
+ @promptEnd.set('insert')
+ end
+
+ when 'Ruby'
+ if (cmd == "!!\n")
+ cmd = @lastCommand
+ end
+ complete = true
+ begin
+ eval("proc{#{cmd}}")
+ rescue
+ complete = false
+ end
+ if complete
+ @lastCommand = cmd
+ begin
+# msg = Tk.appsend(@app, false,
+# 'ruby',
+# '"(' + cmd.gsub(/[][$"]/, '\\\\\&') + ').to_s"')
+ msg = Tk.rb_appsend(@app, false, cmd)
+ rescue
+ msg = "Error: #{$!}"
+ end
+ @txt.insert('insert', msg + "\n") if msg != ""
+ prompt
+ @promptEnd.set('insert')
+ end
+ end
+
+ @executing -= 1
+ @txt.yview_pickplace('insert')
+ end
+
+ # The following method is invoked to change the application that
+ # we're talking to. It also updates the prompt for the current
+ # command, unless we're in the middle of executing a command from
+ # the text item (in which case a new prompt is about to be output
+ # so there's no need to change the old one).
+
+ def newApp(appName, mode)
+ @app = appName
+ @mode = mode
+ if @executing == 0
+ @promptEnd.gravity = 'right'
+ @txt.delete("#{@promptEnd.path} linestart", @promptEnd)
+ @txt.insert(@promptEnd, "#{appName}: ")
+ @txt.tag_add('bold', "#{@promptEnd.path} linestart", @promptEnd)
+ @promptEnd.gravity = 'left'
+ end
+ end
+
+ # The method below will fill in the applications sub-menu with a list
+ # of all the applications that currently exist.
+
+ def fillAppsMenu(menu)
+ win = self
+ begin
+ menu.delete(0,'last')
+ rescue
+ end
+ TkWinfo.interps.sort.each{|ip|
+ if Tk.appsend(ip, false, 'info commands ruby') == ""
+ mode = 'Tcl'
+ else
+ mode = 'Ruby'
+ end
+ menu.add('command', 'label'=>format("%s (#{mode}/Tk)", ip),
+ 'command'=>proc{win.newApp ip, mode})
+ }
+ menu.add('command', 'label'=>format("local (Ruby/Tk)"),
+ 'command'=>proc{win.newApp 'local', 'Ruby'})
+ end
+end
+
+Rmt.new
+
+Tk.mainloop
diff --git a/ext/tk/sample/demos-en/rolodex b/ext/tk/sample/demos-en/rolodex
new file mode 100644
index 0000000000..84254d93bf
--- /dev/null
+++ b/ext/tk/sample/demos-en/rolodex
@@ -0,0 +1,320 @@
+#!/usr/bin/env ruby
+#
+# rolodex --
+# This script is a part of Tom LaStrange's rolodex
+#
+# Copyright (C) 1998 by Takaaki Tateishi <ttate@jaist.ac.jp>
+# Time-stamp: "98/07/20 12:00:02 ttate"
+#
+
+require "tk"
+
+
+def show_help(topic,x=0,y=0)
+ if( topic.is_a?(TkWindow) )
+ w = TkWinfo.containing(x,y)
+ if( TkWinfo.exist?(w) )
+ topic = w
+ end
+ end
+
+ if( $helpTopics.include?(topic) )
+ msg = $helpTopics[topic]
+ else
+ msg = "Sorry, but no help is available for this topic"
+ end
+ TkDialog.new("title"=>"Rolodex Help",
+ "message"=>"Information on #{topic}:\n\n#{msg}",
+ "default_button"=>0,
+ "buttons"=>["OK"])
+end
+
+def fillCard
+ clearAction
+ $root.frame.entry[1].insert(0,"Takaaki Tateishi")
+ $root.frame.entry[2].insert(0,"Japan Advanced Institute of Science and Techonology")
+ $root.frame.entry[3].insert(0,"1-1 Asahidai, Tatsunokuchi")
+ $root.frame.entry[4].insert(0,"Ishikawa 923-1292, Japan")
+ $root.frame.entry[5].insert(0,"private")
+ $root.frame.entry[6].insert(0,"***-***-****")
+ $root.frame.entry[7].insert(0,"***-***-****")
+end
+
+def addAction
+ for i in 1..7
+ STDERR.print format("%-12s %s\n",
+ RolodexFrame::LABEL[i],
+ $root.frame.entry[i].value)
+ end
+end
+
+def clearAction
+ for i in 1..7
+ $root.frame.entry[i].delete(0,"end")
+ end
+end
+
+def fileAction
+ TkDialog.new("title"=>"File Selection",
+ "message"=>"This is a dummy file selection dialog box.\n",
+ "default_button"=>0,
+ "buttons"=>["OK"])
+ STDERR.print "dummy file name\n"
+end
+
+def deleteAction
+ result = TkDialog.new("title"=>"Confirm Action",
+ "message"=>"Are you sure?",
+ "default_button"=>0,
+ "buttons"=>["Cancel"])
+ if( result.value == 0 )
+ clearAction
+ end
+end
+
+
+class RolodexFrame < TkFrame
+ attr_reader :entry, :label
+
+ LABEL = ["","Name:","Address:","","","Home Phone:","Work Phone:","Fax:"]
+
+ def initialize(parent=nil,keys=nil)
+ super(parent,keys)
+ self["relief"] = "flat"
+
+ @i = []
+ @label = []
+ @entry = []
+ for i in 1..7
+ @i[i] = TkFrame.new(self)
+ @i[i].pack("side"=>"top",
+ "pady"=>2,
+ "anchor"=>"e")
+ @label[i] = TkLabel.new(@i[i],
+ "text"=>LABEL[i],
+ "anchor"=>"e")
+ @entry[i] = TkEntry.new(@i[i],
+ "width"=>30,
+ "relief"=>"sunken")
+ @entry[i].pack("side"=>"right")
+ @label[i].pack("side"=>"right")
+ end
+ end
+end
+
+class RolodexButtons < TkFrame
+ attr_reader :clear, :add, :search, :delete
+
+ def initialize(parent,keys=nil)
+ super(parent,keys)
+ @clear = TkButton.new(self,
+ "text" => "Clear")
+ @add = TkButton.new(self,
+ "text" => "Add")
+ @search = TkButton.new(self,
+ "text" => "Search")
+ @delete = TkButton.new(self,
+ "text" => "Delete")
+ for w in [@clear,@add,@search,@delete]
+ w.pack("side"=>"left", "padx"=>2)
+ end
+ end
+end
+
+class RolodexMenuFrame < TkFrame
+ attr_reader :file_menu, :help_menu, :file, :help
+
+ def initialize(parent,keys=nil)
+ super(parent,keys)
+ configure("relief"=>"raised",
+ "borderwidth"=>1)
+
+ @file = TkMenubutton.new(self,
+ "text"=>"File",
+ "underline"=>0)
+ @file_menu = TkMenu.new(@file)
+ @file_menu.add("command",
+ "label" => "Load ...",
+ "command" => proc{fileAction},
+ "underline" => 0)
+ @file_menu.add("command",
+ "label" => "Exit",
+ "command" => proc{$root.destroy},
+ "underline" => 0)
+ @file.menu(@file_menu)
+ @file.pack("side"=>"left")
+
+ @help = TkMenubutton.new(self,
+ "text"=>"Help",
+ "underline"=>0)
+ @help_menu = TkMenu.new(@help)
+ @help_menu.add("command",
+ "label"=>"On Context...",
+ "command"=>proc{show_help("context")},
+ "underline"=>3)
+ @help_menu.add("command",
+ "label"=>"On Help...",
+ "command"=>proc{show_help("help")},
+ "underline"=>3)
+ @help_menu.add("command",
+ "label"=>"On Window...",
+ "command"=>proc{show_help("window")},
+ "underline"=>3)
+ @help_menu.add("command",
+ "label"=>"On Keys...",
+ "command"=>proc{show_help("keys")},
+ "underline"=>3)
+ @help_menu.add("command",
+ "label"=>"On version...",
+ "command"=>proc{show_help("version")},
+ "underline"=>3)
+ @help.menu(@help_menu)
+ @help.pack("side"=>"right")
+ end
+end
+
+class Rolodex < TkRoot
+ attr_reader :frame, :buttons, :menu
+
+ def initialize
+ super
+ @frame = RolodexFrame.new(self)
+ @frame.pack("side"=>"top",
+ "fill"=>"y",
+ "anchor"=>"center")
+ @buttons = RolodexButtons.new(self)
+ @buttons.pack("side"=>"bottom",
+ "pady"=>2,
+ "anchor"=>"center")
+ @menu = RolodexMenuFrame.new(self)
+ @menu.pack("before"=>@frame,
+ "side"=>"top",
+ "fill"=>"x")
+ end
+end
+
+$root = Rolodex.new
+
+$root.buttons.delete.configure("command"=>proc{deleteAction})
+$root.buttons.add.configure("command"=>proc{addAction})
+$root.buttons.clear.configure("command"=>proc{clearAction})
+$root.buttons.search.configure("command"=>proc{addAction; fillCard})
+
+$root.buttons.clear.configure("text"=>"Clear Ctrl+C")
+$root.bind("Control-c",proc{clearAction})
+
+$root.buttons.add.configure("text"=>"Add Ctrl+A")
+$root.bind("Control-a",proc{addAction})
+
+$root.buttons.search.configure("text"=>"Search Ctrl+S")
+$root.bind("Control-s",proc{addAction; fillCard})
+
+$root.buttons.delete.configure("text"=>"Delete... Ctrl+D")
+$root.bind("Control-d",proc{deleteAction})
+
+$root.menu.file_menu.entryconfigure(1, "accel"=>"Ctrl+F")
+$root.bind("Control-f",proc{fileAction})
+
+$root.menu.file_menu.entryconfigure(2, "accel"=>"Ctrl+Q")
+$root.bind("Control-q",proc{$root.destroy})
+
+$root.frame.entry[1].focus
+
+$root.bind("Any-F1",
+ proc{|event| show_help(event.widget, event.x_root, event.y_root)})
+$root.bind("Any-Help",
+ proc{|event| show_help(event.widget, event.x_root, event.y_root)})
+
+
+$helpTopics = {}
+
+$helpTopics[$root.menu.file] = <<EOF
+This is the "file" menu. It can be used to invoke\
+some overall operations on the rolodex applications,\
+such as loading a file or exiting.
+EOF
+
+$helpTopics[$root.menu.file_menu.index(0)] = <<EOF
+The "Load" entry in the "File" menu posts a dialog box\
+that you can use to select a rolodex file
+EOF
+
+$helpTopics[$root.menu.file_menu.index(1)] = <<EOF
+The "Exit" entry in the "File" menu causes the rolodex\
+application to terminate
+EOF
+
+$helpTopics[$root.frame.entry[1]] = <<EOF
+In this field of the rolodex entry you should\
+type the person's name
+EOF
+
+$helpTopics[$root.frame.entry[2]] = <<EOF
+In this field of the rolodex entry you should\
+type the first line of the person's address
+EOF
+
+$helpTopics[$root.frame.entry[3]] = <<EOF
+In this field of the rolodex entry you should\
+type the second line of the person's address
+EOF
+
+$helpTopics[$root.frame.entry[4]] = <<EOF
+In this field of the rolodex entry you should\
+type the third line of the person's address
+EOF
+
+$helpTopics[$root.frame.entry[5]] = <<EOF
+In this field of the rolodex entry you should\
+type the person's home phone number, or "private"\
+if the person doesn't want his or he number publicized
+EOF
+
+$helpTopics[$root.frame.entry[6]] = <<EOF
+In this field of the rolodex entry you should\
+type the person's work phone number
+EOF
+
+$helpTopics[$root.frame.entry[7]] = <<EOF
+In this field of the rolodex entry you should\
+type the phone number for the person's FAX machine
+EOF
+
+$helpTopics["context"] = <<EOF
+Unfortunately, this application doesn't support context-sensitive\
+help in the usual way, because when this demo was written Ruby/Tk\
+didn't have a grab mechanism and this is needed for context-sensitive\
+help. Instead, you can achive much the same effect by simply moving\
+the mouse over the window you're curious about and pressing the\
+Help or F1 keys. You can do this anytime.
+EOF
+
+$helpTopics["help"] = <<EOF
+This application provides only very crude help. Besides the\
+entries in this menu, you can get help on individual windows\
+by moving the mouse cursor over the window and pressing the\
+Help or F1 keys.
+EOF
+
+$helpTopics["window"] = <<EOF
+This window is a dummy rolodex application created as part of\
+Tom LaStrange's toolkit benchmark. It doesn't really do anything\
+useful except to demonstrate a few features of the Ruby/Tk.
+EOF
+
+$helpTopics["keys"] = <<EOF
+The following accelerator keys are defined for this application\
+(in addition to those already available for the entry windows):
+Ctrl+A: Add
+Ctrl+C: Clear
+Ctrl+D: Delete
+Ctrl+F: Enter file name
+Ctrl+Q: Exit application (quit)
+Ctrl+S: Search (dummy operation)
+EOF
+
+$helpTopics["version"] = <<EOF
+This is version 1.0.
+EOF
+
+Tk.mainloop
diff --git a/ext/tk/sample/demos-en/rolodex-j b/ext/tk/sample/demos-en/rolodex-j
new file mode 100644
index 0000000000..b0fc1d2c76
--- /dev/null
+++ b/ext/tk/sample/demos-en/rolodex-j
@@ -0,0 +1,324 @@
+#!/usr/bin/env ruby
+#
+# rolodex --
+# ¤³¤Î¥¹¥¯¥ê¥×¥È¤Ï Tom LaStrange ¤Î rolodex ¤Î°ìÉô¤Ç¤¹¡£
+#
+# Copyright (C) 1998 by Takaaki Tateishi <ttate@jaist.ac.jp>
+# Time-stamp: "98/07/20 12:00:36 ttate"
+#
+
+require "tk"
+require 'tkencoding'
+
+Tk.encoding = "euc-jp"
+$font = TkFont.new('k14')
+
+def show_help(topic,x=0,y=0)
+ if( topic.is_a?(TkWindow) )
+ w = TkWinfo.containing(x,y)
+ if( w.is_a?(TkWindow) )
+ if( TkWinfo.exist?(w) )
+ topic = w
+ end
+ end
+ end
+
+ if( $helpTopics.include?(topic) )
+ msg = $helpTopics[topic]
+ else
+ msg = "¤³¤Î¥È¥Ô¥Ã¥¯¤Ë¤Ä¤¤¤Æ¤Î¥Ø¥ë¥×¤Ï¤Þ¤À»ÈÍѤǤ­¤Þ¤»¤ó"
+ end
+ TkDialog.new("title"=>"Rolodex Help",
+ "message"=>"¡Ö#{topic}¡×\n\n#{msg}",
+ "font"=>$font,
+ "default_button"=>0,
+ "buttons"=>["OK"])
+end
+
+def fillCard
+ clearAction
+ $root.frame.entry[1].insert(0, "ΩÀÐ ¹§¾´")
+ $root.frame.entry[2].insert(0, "923-1292 ÀÐÀ")
+ $root.frame.entry[3].insert(0, "ä¸ýÄ® °°Âæ 1-1")
+ $root.frame.entry[4].insert(0, "ËÌΦÀèü²Ê³Øµ»½ÑÂç³Ø±¡Âç³Ø")
+ $root.frame.entry[5].insert(0,"private")
+ $root.frame.entry[6].insert(0,"***-***-****")
+ $root.frame.entry[7].insert(0,"***-***-****")
+end
+
+def addAction
+ for i in 1..7
+ STDERR.print format("%-12s %s\n",
+ RolodexFrame::LABEL[i],
+ $root.frame.entry[i].value)
+ end
+end
+
+def clearAction
+ for i in 1..7
+ $root.frame.entry[i].delete(0,"end")
+ end
+end
+
+def fileAction
+ TkDialog.new("title"=>"File Selection",
+ "message"=>"¤³¤ì¤Ï¥Õ¥¡¥¤¥ëÁªÂò¥À¥¤¥¢¥í¥°¤Î¥À¥ß¡¼¤Ç¤¹¡£\n",
+ "font"=>$font,
+ "default_button"=>0,
+ "buttons"=>["OK"])
+ STDERR.print "dummy file name\n"
+end
+
+def deleteAction
+ result = TkDialog.new("title"=>"Confirm Action",
+ "message"=>"¤è¤í¤·¤¤¤Ç¤¹¤«¡©",
+ "font"=>$font,
+ "default_button"=>0,
+ "buttons"=>["¥­¥ã¥ó¥»¥ë"])
+ if( result.value == 0 )
+ clearAction
+ end
+end
+
+
+class RolodexFrame < TkFrame
+ attr_reader :entry, :label
+
+ LABEL = ["","̾Á°:","½»½ê","","","ÅÅÏÃ(¼«Âð):","ÅÅÏÃ(²ñ¼Ò):","Fax:"]
+
+ def initialize(parent=nil,keys=nil)
+ super(parent,keys)
+ self["relief"] = "flat"
+ @i = []
+ @label = []
+ @entry = []
+ for i in 1..7
+ @i[i] = TkFrame.new(self)
+ @i[i].pack("side"=>"top",
+ "pady"=>2,
+ "anchor"=>"e")
+ @label[i] = TkLabel.new(@i[i],
+ "text"=>LABEL[i],
+ "anchor"=>"e",
+ "font" => $font)
+ @entry[i] = TkEntry.new(@i[i],
+ "width"=>30,
+ "relief"=>"sunken",
+ "font" => $font)
+ @entry[i].pack("side"=>"right")
+ @label[i].pack("side"=>"right")
+ end
+ end
+end
+
+class RolodexButtons < TkFrame
+ attr_reader :clear, :add, :search, :delete
+
+ def initialize(parent,keys=nil)
+ super(parent,keys)
+ @clear = TkButton.new(self,
+ "text" => "¥¯¥ê¥¢¡¼",
+ "font" => $font)
+ @add = TkButton.new(self,
+ "text" => "ÄɲÃ",
+ "font" => $font)
+ @search = TkButton.new(self,
+ "text" => "¸¡º÷",
+ "font" => $font)
+ @delete = TkButton.new(self,
+ "text" => "¾Ãµî",
+ "font" => $font)
+ for w in [@clear,@add,@search,@delete]
+ w.pack("side"=>"left", "padx"=>2)
+ end
+ end
+end
+
+class RolodexMenuFrame < TkFrame
+ attr_reader :file_menu, :help_menu, :file, :help
+
+ def initialize(parent,keys=nil)
+ super(parent,keys)
+ configure("relief"=>"raised",
+ "borderwidth"=>1)
+
+ @file = TkMenubutton.new(self,
+ "text"=> "¥Õ¥¡¥¤¥ë",
+ "font"=> $font,
+ "underline"=>0)
+ @file_menu = TkMenu.new(@file)
+ @file_menu.add("command",
+ "label" => "ÆÉ¤ß¹þ¤ß ...",
+ "font" => $font,
+ "command" => proc{fileAction},
+ "underline" => 0)
+ @file_menu.add("command",
+ "label" => "½ªÎ»",
+ "font" => $font,
+ "command" => proc{$root.destroy},
+ "underline" => 0)
+ @file.menu(@file_menu)
+ @file.pack("side"=>"left")
+
+ @help = TkMenubutton.new(self,
+ "text"=> "¥Ø¥ë¥×",
+ "font"=> $font,
+ "underline"=>0)
+ @help_menu = TkMenu.new(@help)
+ @help_menu.add("command",
+ "label"=> "¥³¥ó¥Æ¥­¥¹¥È¤Ë¤Ä¤¤¤Æ",
+ "font" => $font,
+ "command"=>proc{show_help("¥³¥ó¥Æ¥­¥¹¥È")},
+ "underline"=>3)
+ @help_menu.add("command",
+ "label"=> "¥Ø¥ë¥×¤Ë¤Ä¤¤¤Æ",
+ "font" => $font,
+ "command"=>proc{show_help("¥Ø¥ë¥×")},
+ "underline"=>3)
+ @help_menu.add("command",
+ "label"=> "¥¦¥£¥ó¥É¥¦¤Ë¤Ä¤¤¤Æ",
+ "font" => $font,
+ "command"=>proc{show_help("¥¦¥£¥ó¥É¥¦")},
+ "underline"=>3)
+ @help_menu.add("command",
+ "label"=> "¥­¡¼Áàºî¤Ë¤Ä¤¤¤Æ",
+ "font" => $font,
+ "command"=>proc{show_help("¥­¡¼Áàºî")},
+ "underline"=>3)
+ @help_menu.add("command",
+ "label"=> "¥Ð¡¼¥¸¥ç¥ó¾ðÊó",
+ "font" => $font,
+ "command"=>proc{show_help("¥Ð¡¼¥¸¥ç¥ó¾ðÊó")},
+ "underline"=>3)
+ @help.menu(@help_menu)
+ @help.pack("side"=>"right")
+ end
+end
+
+class Rolodex < TkRoot
+ attr_reader :frame, :buttons, :menu
+
+ def initialize
+ super
+ @frame = RolodexFrame.new(self)
+ @frame.pack("side"=>"top",
+ "fill"=>"y",
+ "anchor"=>"center")
+ @buttons = RolodexButtons.new(self)
+ @buttons.pack("side"=>"bottom",
+ "pady"=>2,
+ "anchor"=>"center")
+ @menu = RolodexMenuFrame.new(self)
+ @menu.pack("before"=>@frame,
+ "side"=>"top",
+ "fill"=>"x")
+ end
+end
+
+$root = Rolodex.new
+
+$root.buttons.delete.configure("command"=>proc{deleteAction})
+$root.buttons.add.configure("command"=>proc{addAction})
+$root.buttons.clear.configure("command"=>proc{clearAction})
+$root.buttons.search.configure("command"=>proc{addAction; fillCard})
+
+$root.buttons.clear.configure("text"=> "¥¯¥ê¥¢¡¼ Ctrl+C", "font" => $font)
+$root.bind("Control-c",proc{clearAction})
+
+$root.buttons.add.configure("text"=> "Äɲà Ctrl+A", "font" => $font)
+$root.bind("Control-a",proc{addAction})
+
+$root.buttons.search.configure("text"=> "¸¡º÷ Ctrl+S", "font" => $font)
+$root.bind("Control-s",proc{addAction; fillCard})
+
+$root.buttons.delete.configure("text"=> "¾Ãµî Ctrl+D", "font" => $font)
+$root.bind("Control-d",proc{deleteAction})
+
+$root.menu.file_menu.entryconfigure(1, "accel"=>"Ctrl+F")
+$root.bind("Control-f",proc{fileAction})
+
+$root.menu.file_menu.entryconfigure(2, "accel"=>"Ctrl+Q")
+$root.bind("Control-q",proc{$root.destroy})
+
+$root.frame.entry[1].focus
+
+$root.bind("Any-F1",
+ proc{|event| show_help(event.widget, event.x_root, event.y_root)})
+$root.bind("Any-Help",
+ proc{|event| show_help(event.widget, event.x_root, event.y_root)})
+
+
+$helpTopics = {}
+
+$helpTopics[$root.menu.file] = <<EOF
+¤³¤ì¤Ï¡Ö¥Õ¥¡¥¤¥ë¡×¥á¥Ë¥å¡¼¤Ç¤¹¡£¡ÖÆÉ¤ß¹þ¤ß¡×¤ä¡Ö½ªÎ»¡×¤Ê¤É¤ò
+¹Ô¤Ê¤¦¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+EOF
+
+$helpTopics[$root.menu.file_menu.index(0)] = <<EOF
+¥Õ¥¡¥¤¥ë¤ÎÆÉ¤ß¹þ¤ß¤ò¹Ô¤Ê¤¦¤È¤­¤Ë»È¤¤¤Þ¤¹¡£
+EOF
+
+$helpTopics[$root.menu.file_menu.index(1)] = <<EOF
+¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¤ò½ªÎ»¤¹¤ë¤È¤­¤Ë»È¤¤¤Þ¤¹¡£
+EOF
+
+$helpTopics[$root.frame.entry[1]] = <<EOF
+̾Á°¤òµ­Æþ¤¹¤ë¥¨¥ó¥È¥ê¤Ç¤¹¡£
+EOF
+
+$helpTopics[$root.frame.entry[2]] = <<EOF
+½»½ê¤òµ­Æþ¤¹¤ë¥¨¥ó¥È¥ê¤Ç¤¹¡£
+EOF
+
+$helpTopics[$root.frame.entry[3]] = <<EOF
+½»½ê¤òµ­Æþ¤¹¤ë¥¨¥ó¥È¥ê¤Ç¤¹¡£
+EOF
+
+$helpTopics[$root.frame.entry[4]] = <<EOF
+½»½ê¤òµ­Æþ¤¹¤ë¥¨¥ó¥È¥ê¤Ç¤¹¡£
+EOF
+
+$helpTopics[$root.frame.entry[5]] = <<EOF
+¼«Âð¤ÎÅÅÏÃÈÖ¹æ¤òµ­Æþ¤¹¤ë¥¨¥ó¥È¥ê¤Ç¤¹¡£¸ø³«\
+¤·¤¿¤¯¤Ê¤¤¤È¤­¤Ï private ¤Èµ­Æþ¤·¤Þ¤¹¡£
+EOF
+
+$helpTopics[$root.frame.entry[6]] = <<EOF
+²ñ¼Ò¤ÎÅÅÏÃÈÖ¹æ¤òµ­Æþ¤¹¤ë¥¨¥ó¥È¥ê¤Ç¤¹¡£
+EOF
+
+$helpTopics[$root.frame.entry[7]] = <<EOF
+FAXÈÖ¹æ¤òµ­Æþ¤¹¤ë¥¨¥ó¥È¥ê¤Ç¤¹¡£
+EOF
+
+$helpTopics["¥³¥ó¥Æ¥­¥¹¥È"] = <<EOF
+Ruby/Tk¤Ç¤Ïgrab¤Îµ¡¹½¤¬¤Ê¤¤¤¿¤á¤³¤Î¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¤Ç¤Ï\
+¥³¥ó¥Æ¥­¥¹¥È¥Ø¥ë¥×¤Ï¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£
+¤·¤«¤·Æ±¤¸¤è¤¦¤Ê¸ú²Ì¤òbind¤È¥Þ¥¦¥¹¤Î°ÌÃÖ¤ÎWedget¤òÃΤë\
+¤³¤È¤ÇÆÀ¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+EOF
+
+$helpTopics["¥Ø¥ë¥×"] = <<EOF
+¥Þ¥¦¥¹¤ò¥¦¥£¥ó¥É¥¦¤Ë¤¢¤ï¤»¤ÆF1¥­¡¼¤ò²¡¤¹¤³¤È¤Ë¤è¤Ã¤Æ\
+¤½¤Î¥Ø¥ë¥×¤ò¸«¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+EOF
+
+$helpTopics["¥¦¥£¥ó¥É¥¦"] = <<EOF
+¤³¤Î¥¦¥£¥ó¥É¥¦¤Ï¥À¥ß¡¼¤Ç¤¹¡£
+EOF
+
+$helpTopics["¥­¡¼Áàºî"] = <<EOF
+Ctrl+A: ÄɲÃ
+Ctrl+C: ¥¯¥ê¥¢¡¼
+Ctrl+D: ¾Ãµî
+Ctrl+F: ¥Õ¥¡¥¤¥ëÁªÂò
+Ctrl+Q: ½ªÎ»
+Ctrl+S: ¸¡º÷
+EOF
+
+$helpTopics["¥Ð¡¼¥¸¥ç¥ó¾ðÊó"] = <<EOF
+¥Ð¡¼¥¸¥ç¥ó¤Ï 1.0 ¤Ç¤¹¡£
+EOF
+
+Tk.mainloop
diff --git a/ext/tk/sample/demos-en/ruler.rb b/ext/tk/sample/demos-en/ruler.rb
new file mode 100644
index 0000000000..f5e7add516
--- /dev/null
+++ b/ext/tk/sample/demos-en/ruler.rb
@@ -0,0 +1,201 @@
+# ruler.rb
+#
+# This demonstration script creates a canvas widget that displays a ruler
+# with tab stops that can be set, moved, and deleted.
+#
+# ruler widget demo (called by 'widget')
+#
+
+# rulerMkTab --
+# This method creates a new triangular polygon in a canvas to
+# represent a tab stop.
+#
+# Arguments:
+# c - The canvas window.
+# x, y - Coordinates at which to create the tab stop.
+
+def rulerMkTab(c,x,y)
+ v = $demo_rulerInfo
+ TkcPolygon.new(c, x, y, x+v.size, y+v.size, x-v.size, y+v.size)
+end
+
+# toplevel widget
+if defined?($ruler_demo) && $ruler_demo
+ $ruler_demo.destroy
+ $ruler_demo = nil
+end
+
+# demo toplevel widget
+$ruler_demo = TkToplevel.new {|w|
+ title("Ruler Demonstration")
+ iconname("ruler")
+ positionWindow(w)
+}
+
+# label
+TkLabel.new($ruler_demo, 'font'=>$font, 'wraplength'=>'5i', 'justify'=>'left',
+ 'text'=>"This canvas widget shows a mock-up of a ruler. You can create tab stops by dragging them out of the well to the right of the ruler. You can also drag existing tab stops. If you drag a tab stop far enough up or down so that it turns dim, it will be deleted when you release the mouse button."){
+ pack('side'=>'top')
+}
+
+# frame
+$ruler_buttons = TkFrame.new($ruler_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $ruler_demo
+ $ruler_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'ruler'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}
+$ruler_buttons.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# canvas
+$ruler_canvas = TkCanvas.new($ruler_demo, 'width'=>'14.8c', 'height'=>'2.5c')
+$ruler_canvas.pack('side'=>'top', 'fill'=>'x')
+
+#
+unless Struct.const_defined?("RulerInfo")
+ $demo_rulerInfo = Struct.new("RulerInfo", :grid, :left, :right, :x, :y,
+ :top, :bottom, :size, :normalStyle,
+ :activeStyle, :deleteStyle).new
+end
+$demo_rulerInfo.grid = '.25c'
+$demo_rulerInfo.left = TkWinfo.fpixels($ruler_canvas, '1c')
+$demo_rulerInfo.right = TkWinfo.fpixels($ruler_canvas, '13c')
+$demo_rulerInfo.top = TkWinfo.fpixels($ruler_canvas, '1c')
+$demo_rulerInfo.bottom = TkWinfo.fpixels($ruler_canvas, '1.5c')
+$demo_rulerInfo.size = TkWinfo.fpixels($ruler_canvas, '.2c')
+$demo_rulerInfo.normalStyle = {'fill'=>'black'}
+if TkWinfo.depth($ruler_canvas) > 1
+ $demo_rulerInfo.activeStyle = {'fill'=>'red', 'stipple'=>''}
+ $demo_rulerInfo.deleteStyle = {'fill'=>'red',
+ 'stipple'=>'@'+[$demo_dir, 'images', 'gray25.xbm'].join(File::Separator)}
+else
+ $demo_rulerInfo.activeStyle = {'fill'=>'black', 'stipple'=>''}
+ $demo_rulerInfo.deleteStyle = {'fill'=>'black',
+ 'stipple'=>'@'+[$demo_dir, 'images', 'gray25.xbm'].join(File::Separator)}
+end
+
+TkcLine.new($ruler_canvas,
+ '1c', '0.5c', '1c', '1c', '13c', '1c', '13c', '0.5c', 'width'=>1)
+(0..11).each{|i|
+ x = i+1
+ TkcLine.new($ruler_canvas, "#{x}c", '1c', "#{x}c", '0.6c', 'width'=>1)
+ TkcLine.new($ruler_canvas, "#{x}.25c", '1c', "#{x}.25c", '0.8c', 'width'=>1)
+ TkcLine.new($ruler_canvas, "#{x}.5c", '1c', "#{x}.5c", '0.7c', 'width'=>1)
+ TkcLine.new($ruler_canvas, "#{x}.75c", '1c', "#{x}.75c", '0.8c', 'width'=>1)
+ TkcText.new($ruler_canvas, "#{x}.15c", '0.75c', 'text'=>i, 'anchor'=>'sw')
+}
+
+$rulerTag_well = TkcTag.new($ruler_canvas)
+$ruler_canvas\
+.addtag_withtag($rulerTag_well,
+ TkcRectangle.new($ruler_canvas,
+ '13.2c', '1c', '13.8c', '0.5c',
+ 'outline'=>'black',
+ 'fill'=>($ruler_canvas\
+ .configinfo('background'))[4]) )
+$ruler_canvas\
+.addtag_withtag($rulerTag_well,
+ rulerMkTab($ruler_canvas,
+ TkWinfo.pixels($ruler_canvas, '13.5c'),
+ TkWinfo.pixels($ruler_canvas, '.65c') ) )
+
+$rulerTag_well.bind('1', proc{|x,y| rulerNewTab($ruler_canvas,x,y)}, '%x %y')
+$ruler_canvas.itembind('tab', '1',
+ proc{|x,y| rulerSelectTab($ruler_canvas,x,y)}, '%x %y')
+$ruler_canvas.bind('B1-Motion',
+ proc{|x,y| rulerMoveTab($ruler_canvas,x,y)}, '%x %y')
+$ruler_canvas.bind('Any-ButtonRelease-1', proc{rulerReleaseTab($ruler_canvas)})
+
+# rulerNewTab --
+# Does all the work of creating a tab stop, including creating the
+# triangle object and adding tags to it to give it tab behavior.
+#
+# Arguments:
+# c - The canvas window.
+# x, y - The coordinates of the tab stop.
+
+def rulerNewTab(c,x,y)
+ v = $demo_rulerInfo
+ c.addtag_withtag('active', rulerMkTab(c,x,y))
+ c.addtag_withtag('tab', 'active')
+ v.x = x
+ v.y = y
+ rulerMoveTab(c,x,y)
+end
+
+# rulerSelectTab --
+# This method is invoked when mouse button 1 is pressed over
+# a tab. It remembers information about the tab so that it can
+# be dragged interactively.
+#
+# Arguments:
+# c - The canvas widget.
+# x, y - The coordinates of the mouse (identifies the point by
+# which the tab was picked up for dragging).
+
+def rulerSelectTab(c,x,y)
+ v = $demo_rulerInfo
+ v.x = c.canvasx(x, v.grid)
+ v.y = v.top+2
+ c.addtag_withtag('active', 'current')
+ c.itemconfigure('active', v.activeStyle)
+ c.raise('active')
+end
+
+# rulerMoveTab --
+# This method is invoked during mouse motion events to drag a tab.
+# It adjusts the position of the tab, and changes its appearance if
+# it is about to be dragged out of the ruler.
+#
+# Arguments:
+# c - The canvas widget.
+# x, y - The coordinates of the mouse.
+
+def rulerMoveTab(c,x,y)
+ v = $demo_rulerInfo
+ return if c.find_withtag('active') == []
+ cx = c.canvasx(x,v.grid)
+ cy = c.canvasy(y)
+ cx = v.left if cx < v.left
+ cx = v.right if cx > v.right
+ if (cy >= v.top && cy <= v.bottom)
+ cy = v.top+2
+ c.itemconfigure('active', v.activeStyle)
+ else
+ cy = cy-v.size-2
+ c.itemconfigure('active', v.deleteStyle)
+ end
+ c.move('active', cx-v.x, cy-v.y)
+ v.x = cx
+ v.y = cy
+end
+
+# rulerReleaseTab --
+# This method is invoked during button release events that end
+# a tab drag operation. It deselects the tab and deletes the tab if
+# it was dragged out of the ruler.
+#
+# Arguments:
+# c - The canvas widget.
+# x, y - The coordinates of the mouse.
+
+def rulerReleaseTab(c)
+ v = $demo_rulerInfo
+ return if c.find_withtag('active') == []
+ if v.y != v.top+2
+ c.delete('active')
+ else
+ c.itemconfigure('active', v.normalStyle)
+ c.dtag('active')
+ end
+end
+
diff --git a/ext/tk/sample/demos-en/sayings.rb b/ext/tk/sample/demos-en/sayings.rb
new file mode 100644
index 0000000000..4ba3097bed
--- /dev/null
+++ b/ext/tk/sample/demos-en/sayings.rb
@@ -0,0 +1,104 @@
+# sayings.rb
+#
+# This demonstration script creates a listbox that can be scrolled
+# both horizontally and vertically. It displays a collection of
+# well-known sayings.
+#
+# listbox widget demo 'sayings' (called by 'widget')
+#
+
+# toplevel widget
+if defined?($sayings_demo) && $sayings_demo
+ $sayings_demo.destroy
+ $sayings_demo = nil
+end
+
+# demo toplevel widget
+$sayings_demo = TkToplevel.new {|w|
+ title("Listbox Demonstration (well-known sayings)")
+ iconname("sayings")
+ positionWindow(w)
+}
+
+# label
+msg = TkLabel.new($sayings_demo) {
+ font $font
+ wraplength '4i'
+ justify 'left'
+ text "The listbox below contains a collection of well-known sayings. You can scan the list using either of the scrollbars or by dragging in the listbox window with button 2 pressed."
+}
+msg.pack('side'=>'top')
+
+# frame
+TkFrame.new($sayings_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $sayings_demo
+ $sayings_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'sayings'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame
+sayings_lbox = nil
+TkFrame.new($sayings_demo, 'borderwidth'=>10) {|w|
+ sv = TkScrollbar.new(w)
+ sh = TkScrollbar.new(w, 'orient'=>'horizontal')
+ sayings_lbox = TkListbox.new(w) {
+ setgrid 1
+ width 20
+ height 10
+ yscrollcommand proc{|first,last| sv.set first,last}
+ xscrollcommand proc{|first,last| sh.set first,last}
+ }
+ sv.command(proc{|*args| sayings_lbox.yview(*args)})
+ sh.command(proc{|*args| sayings_lbox.xview(*args)})
+
+ if $tk_version =~ /^4\.[01]/
+ sv.pack('side'=>'right', 'fill'=>'y')
+ sh.pack('side'=>'bottom', 'fill'=>'x')
+ sayings_lbox.pack('expand'=>'yes', 'fill'=>'y')
+
+ else
+ sayings_lbox.grid('row'=>0, 'column'=>0,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ sv.grid('row'=>0, 'column'=>1,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ sh.grid('row'=>1, 'column'=>0,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ TkGrid.rowconfigure(w, 0, 'weight'=>1, 'minsize'=>0)
+ TkGrid.columnconfigure(w, 0, 'weight'=>1, 'minsize'=>0)
+ end
+
+}.pack('side'=>'top', 'expand'=>'yes', 'fill'=>'y')
+
+sayings_lbox.insert(0,
+"Waste not, want not",
+"Early to bed and early to rise makes a man healthy, wealthy, and wise",
+"Ask not what your country can do for you, ask what you can do for your country",
+"I shall return",
+"NOT",
+"A picture is worth a thousand words",
+"User interfaces are hard to build",
+"Thou shalt not steal",
+"A penny for your thoughts",
+"Fool me once, shame on you; fool me twice, shame on me",
+"Every cloud has a silver lining",
+"Where there's smoke there's fire",
+"It takes one to know one",
+"Curiosity killed the cat; but satisfaction brought it back",
+"Take this job and shove it",
+"Up a creek without a paddle",
+"I'm mad as hell and I'm not going to take it any more",
+"An apple a day keeps the doctor away",
+"Don't look a gift horse in the mouth"
+)
+
diff --git a/ext/tk/sample/demos-en/search.rb b/ext/tk/sample/demos-en/search.rb
new file mode 100644
index 0000000000..2b27cdbb42
--- /dev/null
+++ b/ext/tk/sample/demos-en/search.rb
@@ -0,0 +1,192 @@
+# search.rb
+#
+# This demonstration script creates a collection of widgets that
+# allow you to load a file into a text widget, then perform searches
+# on that file.
+#
+# Text Search widget demo (called by 'widget')
+#
+
+# textLoadFile --
+# This method below loads a file into a text widget, discarding
+# the previous contents of the widget. Tags for the old widget are
+# not affected, however.
+#
+# Arguments:
+# w - The window into which to load the file. Must be a
+# text widget.
+# file - The name of the file to load. Must be readable.
+
+def textLoadFile(w,file)
+ w.delete('1.0', 'end')
+ f = open(file, 'r')
+ while(!f.eof?)
+ w.insert('end', f.read(1000))
+ end
+ f.close
+end
+
+# textSearch --
+# Search for all instances of a given string in a text widget and
+# apply a given tag to each instance found.
+#
+# Arguments:
+# w - The window in which to search. Must be a text widget.
+# string - The string to search for. The search is done using
+# exact matching only; no special characters.
+# tag - Tag to apply to each instance of a matching string.
+
+def textSearch(w, string, tag)
+ tag.remove('0.0', 'end')
+ return if string == ""
+ cur = '1.0'
+ loop {
+ cur, len = w.search_with_length(string, cur, 'end')
+ break if cur == ""
+ tag.add(cur, "#{cur} + #{len} char")
+ cur = w.index("#{cur} + #{len} char")
+ }
+end
+
+# textToggle --
+# This method is invoked repeatedly to invoke two commands at
+# periodic intervals. It normally reschedules itself after each
+# execution but if an error occurs (e.g. because the window was
+# deleted) then it doesn't reschedule itself.
+#
+# Arguments:
+# cmd1 - Command to execute when method is called.
+# sleep1 - Ms to sleep after executing cmd1 before executing cmd2.
+# cmd2 - Command to execute in the *next* invocation of this method.
+# sleep2 - Ms to sleep after executing cmd2 before executing cmd1 again.
+
+def textToggle(cmd1,sleep1,cmd2,sleep2)
+ sleep_list = [sleep2, sleep1]
+ TkAfter.new(proc{sleep = sleep_list.shift; sleep_list.push(sleep); sleep},
+ -1, cmd1, cmd2).start(sleep1)
+end
+
+# toplevel widget
+if defined?($search_demo) && $search_demo
+ $search_demo.destroy
+ $search_demo = nil
+end
+
+# demo toplevel widget
+$search_demo = TkToplevel.new {|w|
+ title("Text Demonstration - Search and Highlight")
+ iconname("search")
+ positionWindow(w)
+}
+
+# frame
+$search_buttons = TkFrame.new($search_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $search_demo
+ $search_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'search'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}
+$search_buttons.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame
+TkFrame.new($search_demo) {|f|
+ TkLabel.new(f, 'text'=>'File name:',
+ 'width'=>13, 'anchor'=>'w').pack('side'=>'left')
+ $search_fileName = TkVariable.new
+ TkEntry.new(f, 'width'=>40,
+ 'textvariable'=>$search_fileName) {
+ pack('side'=>'left')
+ bind('Return', proc{textLoadFile($search_text, $search_fileName.value)
+ $search_string_entry.focus})
+ focus
+ }
+ TkButton.new(f, 'text'=>'Load File',
+ 'command'=>proc{textLoadFile($search_text,
+ $search_fileName.value)})\
+ .pack('side'=>'left', 'pady'=>5, 'padx'=>10)
+}.pack('side'=>'top', 'fill'=>'x')
+
+TkFrame.new($search_demo) {|f|
+ TkLabel.new(f, 'text'=>'Search string:',
+ 'width'=>13, 'anchor'=>'w').pack('side'=>'left')
+ $search_searchString = TkVariable.new
+ $search_string_entry = TkEntry.new(f, 'width'=>40,
+ 'textvariable'=>$search_searchString) {
+ pack('side'=>'left')
+ bind('Return', proc{textSearch($search_text, $search_searchString.value,
+ $search_Tag)})
+ }
+ TkButton.new(f, 'text'=>'Highlight',
+ 'command'=>proc{textSearch($search_text,
+ $search_searchString.value,
+ $search_Tag)}) {
+ pack('side'=>'left', 'pady'=>5, 'padx'=>10)
+ }
+}.pack('side'=>'top', 'fill'=>'x')
+
+$search_text = TkText.new($search_demo, 'setgrid'=>true) {|t|
+ $search_Tag = TkTextTag.new(t)
+ TkScrollbar.new($search_demo, 'command'=>proc{|*args| t.yview(*args)}) {|sc|
+ t.yscrollcommand(proc{|first,last| sc.set first,last})
+ pack('side'=>'right', 'fill'=>'y')
+ }
+ pack('expand'=>'yes', 'fill'=>'both')
+}
+
+# Set up display styles for text highlighting.
+
+if TkWinfo.depth($search_demo) > 1
+ textToggle(proc{
+ begin
+ $search_Tag.configure('background'=>'#ce5555',
+ 'foreground'=>'white')
+ rescue
+ end
+ },
+ 800,
+ proc{
+ begin
+ $search_Tag.configure('background'=>'', 'foreground'=>'')
+ rescue
+ end
+ },
+ 200 )
+else
+ textToggle(proc{
+ begin
+ $search_Tag.configure('background'=>'black',
+ 'foreground'=>'white')
+ rescue
+ end
+ },
+ 800,
+ proc{
+ begin
+ $search_Tag.configure('background'=>'', 'foreground'=>'')
+ rescue
+ end
+ },
+ 200 )
+end
+$search_text.insert('1.0', '\
+This window demonstrates how to use the tagging facilities in text
+widgets to implement a searching mechanism. First, type a file name
+in the top entry, then type <Return> or click on "Load File". Then
+type a string in the lower entry and type <Return> or click on
+"Load File". This will cause all of the instances of the string to
+be tagged with the tag "search", and it will arrange for the tag\'s
+display attributes to change to make all of the strings blink.')
+$search_text.set_insert '0.0'
+
+$search_fileName.value = ''
+$search_searchString.value = ''
+
diff --git a/ext/tk/sample/demos-en/square b/ext/tk/sample/demos-en/square
new file mode 100644
index 0000000000..821cd9927e
--- /dev/null
+++ b/ext/tk/sample/demos-en/square
@@ -0,0 +1,74 @@
+#!/usr/bin/env ruby
+
+# square --
+# This script generates a demo application containing only
+# a "square" widget. It's only usable if Tk has been compiled
+# with tkSquare.c and with the -DSQUARE_DEMO compiler switch.
+# This demo arranges the following bindings for the widget:
+#
+# Button-1 press/drag: moves square to mouse
+# "a": toggle size animation on/off
+#
+
+require 'tk'
+require 'tkafter'
+
+class TkSquare<TkWindow
+ def create_self
+ tk_call 'square', path
+ end
+ def size(amount=nil)
+ if amount
+ tk_send 'size', amount
+ else
+ number(tk_send 'size')
+ end
+ end
+ def position(x,y)
+ tk_send 'position', x, y
+ end
+end
+
+$s = TkSquare.new{
+ pack('expand'=>'yes', 'fill'=>'both')
+ bind('1', proc{|x,y| center(x,y)}, '%s %y')
+ bind('B1-Motion', proc{|x,y| center(x,y)}, '%s %y')
+ bind('a', proc{animate})
+ focus
+}
+TkRoot.new.minsize(1,1)
+
+# The procedure below centers the square on a given position.
+
+def center(x,y)
+ a = $s.size
+ $s.position(x-(a/2), y-(a/2))
+end
+
+# The procedures below provide a simple form of animation where
+# the box changes size in a pulsing pattern: larger, smaller, larger,
+# and so on.
+
+$inc = 0
+
+def timer_proc
+ a = $s.size
+ return if $inc == 0
+ $inc = -3 if a >= 40
+ $inc = 3 if a <= 10
+ $s.size(a+$inc)
+end
+
+$timer = TkAfter.new(30, -1, proc{timer_proc})
+
+def animate
+ if $inc == 0
+ $inc = 3
+ $timer.start
+ else
+ $inc = 0
+ $timer.stop
+ end
+end
+
+Tk.mainloop
diff --git a/ext/tk/sample/demos-en/states.rb b/ext/tk/sample/demos-en/states.rb
new file mode 100644
index 0000000000..d38c1245af
--- /dev/null
+++ b/ext/tk/sample/demos-en/states.rb
@@ -0,0 +1,78 @@
+# states.rb
+#
+# This demonstration script creates a listbox widget that displays
+# the names of the 50 states in the United States of America.
+#
+# listbox widget demo 'states' (called by 'widget')
+#
+
+# toplevel widget
+if defined?($states_demo) && $states_demo
+ $states_demo.destroy
+ $states_demo = nil
+end
+
+# demo toplevel widget
+$states_demo = TkToplevel.new {|w|
+ title("Listbox Demonstration (states)")
+ iconname("states")
+ positionWindow(w)
+}
+
+# label
+msg = TkLabel.new($states_demo) {
+ font $font
+ wraplength '4i'
+ justify 'left'
+ text "A listbox containing the 50 states is displayed below, along with a scrollbar. You can scan the list either using the scrollbar or by scanning. To scan, press button 2 in the widget and drag up or down."
+}
+msg.pack('side'=>'top')
+
+# frame
+TkFrame.new($states_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $states_demo
+ $states_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'states'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame
+states_lbox = nil
+TkFrame.new($states_demo, 'borderwidth'=>'.5c') {|w|
+ s = TkScrollbar.new(w)
+ states_lbox = TkListbox.new(w) {
+ setgrid 1
+ height 12
+ yscrollcommand proc{|first,last| s.set first,last}
+ }
+ s.command(proc{|*args| states_lbox.yview(*args)})
+ s.pack('side'=>'right', 'fill'=>'y')
+ states_lbox.pack('side'=>'left', 'expand'=>1, 'fill'=>'both')
+}.pack('side'=>'top', 'expand'=>'yes', 'fill'=>'y')
+
+ins_data = [
+ 'Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California',
+ 'Colorado', 'Connecticut', 'Delaware', 'Florida', 'Georgia',
+ 'Hawaii', 'Idaho', 'Illinois',
+ 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland',
+ 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri',
+ 'Montana', 'Nebraska', 'Nevada', 'New_Hampshire', 'New_Jersey', 'New_Mexico',
+ 'New_York', 'North_Carolina', 'North_Dakota',
+ 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode_Island',
+ 'South_Carolina', 'South_Dakota',
+ 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington',
+ 'West_Virginia', 'Wisconsin', 'Wyoming'
+]
+
+states_lbox.insert(0, *ins_data)
+
diff --git a/ext/tk/sample/demos-en/style.rb b/ext/tk/sample/demos-en/style.rb
new file mode 100644
index 0000000000..8bf9c09662
--- /dev/null
+++ b/ext/tk/sample/demos-en/style.rb
@@ -0,0 +1,211 @@
+# style.rb
+#
+# This demonstration script creates a text widget that illustrates the
+# various display styles that may be set for tags.
+#
+# text (display styles) widget demo (called by 'widget')
+#
+
+# toplevel widget
+if defined?($style_demo) && $style_demo
+ $style_demo.destroy
+ $style_demo = nil
+end
+
+# demo toplevel widget
+$style_demo = TkToplevel.new {|w|
+ title("Text Demonstration - Display Styles")
+ iconname("style")
+ positionWindow(w)
+}
+
+# frame
+TkFrame.new($style_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $style_demo
+ $style_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'style'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# text
+TkText.new($style_demo){|t|
+ #
+ setgrid 'true'
+ width 70
+ height 32
+ wrap 'word'
+ TkScrollbar.new($style_demo) {|s|
+ pack('side'=>'right', 'fill'=>'y')
+ command proc{|*args| t.yview(*args)}
+ t.yscrollcommand proc{|first,last| s.set first,last}
+ }
+ pack('expand'=>'yes', 'fill'=>'both')
+
+ #
+ style_tag_bold = TkTextTag.new(t, 'font'=>'-*-Courier-Bold-O-Normal--*-120-*-*-*-*-*-*')
+ style_tag_big = TkTextTag.new(t, 'font'=>'-*-Courier-Bold-R-Normal--*-140-*-*-*-*-*-*', 'kanjifont'=>$msg_kanji_font)
+ style_tag_verybig = TkTextTag.new(t, 'font'=>'-*-Helvetica-Bold-R-Normal--*-240-*-*-*-*-*-*')
+# style_tag_small = TkTextTag.new(t, 'font'=>'-Adobe-Helvetica-Bold-R-Normal-*-100-*', 'kanjifont'=>$kanji_font)
+ style_tag_small = TkTextTag.new(t, 'font'=>'-Adobe-Helvetica-Bold-R-Normal-*-100-*')
+###
+# case($tk_version)
+# when /^4.*/
+# style_tag_big = TkTextTag.new(t, 'font'=>'-*-Courier-Bold-R-Normal--*-140-*-*-*-*-*-*', 'kanjifont'=>$msg_kanji_font)
+# style_tag_small = TkTextTag.new(t, 'font'=>'-Adobe-Helvetica-Bold-R-Normal-*-100-*', 'kanjifont'=>$kanji_font)
+# when /^8.*/
+# unless $style_demo_do_first
+# $style_demo_do_first = true
+# Tk.tk_call('font', 'create', '@bigascii',
+# '-copy', '-*-Courier-Bold-R-Normal--*-140-*-*-*-*-*-*')
+# Tk.tk_call('font', 'create', '@smallascii',
+# '-copy', '-Adobe-Helvetica-Bold-R-Normal-*-100-*')
+# Tk.tk_call('font', 'create', '@cBigFont',
+# '-compound', '@bigascii @msg_knj')
+# Tk.tk_call('font', 'create', '@cSmallFont',
+# '-compound', '@smallascii @kanji')
+# end
+# style_tag_big = TkTextTag.new(t, 'font'=>'@cBigFont')
+# style_tag_small = TkTextTag.new(t, 'font'=>'@cSmallFont')
+# end
+
+ #
+ if TkWinfo.depth($root).to_i > 1
+ style_tag_color1 = TkTextTag.new(t, 'background'=>'#a0b7ce')
+ style_tag_color2 = TkTextTag.new(t, 'foreground'=>'red')
+ style_tag_raised = TkTextTag.new(t, 'relief'=>'raised', 'borderwidth'=>1)
+ style_tag_sunken = TkTextTag.new(t, 'relief'=>'sunken', 'borderwidth'=>1)
+ else
+ style_tag_color1 = TkTextTag.new(t, 'background'=>'black',
+ 'foreground'=>'white')
+ style_tag_color2 = TkTextTag.new(t, 'background'=>'black',
+ 'foreground'=>'white')
+ style_tag_raised = TkTextTag.new(t, 'background'=>'white',
+ 'relief'=>'raised', 'borderwidth'=>1)
+ style_tag_sunken = TkTextTag.new(t, 'background'=>'white',
+ 'relief'=>'sunken', 'borderwidth'=>1)
+ end
+
+ # ¥Æ¥­¥¹¥È¥¿¥°ÀßÄê (¤½¤Î¾)
+ if $tk_version =~ /^4\.[01]/
+ style_tag_bgstipple = TkTextTag.new(t, 'background'=>'black',
+ 'borderwidth'=>0,
+ 'bgstipple'=>'gray25')
+ else
+ style_tag_bgstipple = TkTextTag.new(t, 'background'=>'black',
+ 'borderwidth'=>0,
+ 'bgstipple'=>'gray12')
+ end
+ style_tag_fgstipple = TkTextTag.new(t, 'fgstipple'=>'gray50')
+ style_tag_underline = TkTextTag.new(t, 'underline'=>'on')
+ style_tag_overstrike = TkTextTag.new(t, 'overstrike'=>'on')
+ style_tag_right = TkTextTag.new(t, 'justify'=>'right')
+ style_tag_center = TkTextTag.new(t, 'justify'=>'center')
+ style_tag_super = TkTextTag.new(t, 'offset'=>'4p', 'font'=>'-Adobe-Courier-Medium-R-Normal--*-100-*-*-*-*-*-*')
+ style_tag_sub = TkTextTag.new(t, 'offset'=>'-2p', 'font'=>'-Adobe-Courier-Medium-R-Normal--*-100-*-*-*-*-*-*')
+ style_tag_margins = TkTextTag.new(t, 'lmargin1'=>'12m', 'lmargin2'=>'6m',
+ 'rmargin'=>'10m')
+ style_tag_spacing = TkTextTag.new(t, 'spacing1'=>'10p', 'spacing2'=>'2p',
+ 'lmargin1'=>'12m', 'lmargin2'=>'6m',
+ 'rmargin'=>'10m')
+
+ #
+ insert('end', 'Text widgets like this one allow you to display information in a
+variety of styles. Display styles are controlled using a mechanism
+called ')
+ insert('end', 'tags', style_tag_big)
+ insert('end', '. Tags are just textual names that you can apply to one
+or more ranges of characters within a text widget. You can configure
+tags with various display styles. If you do this, then the tagged
+characters will be displayed with the styles you chose. The
+available display styles are:
+')
+ insert('end', "\n1. Font.", style_tag_big)
+ insert('end', " You can choose any X font, ")
+ insert('end', "large", style_tag_verybig)
+ insert('end', " or ")
+ insert('end', "small.\n")
+ insert('end', "\n2. Color.", style_tag_big)
+ insert('end', " You can change either the ")
+ insert('end', "background", style_tag_color1)
+ insert('end', " or ")
+ insert('end', "foreground", style_tag_color2)
+ insert('end', "\ncolor, or ")
+ insert('end', "both", style_tag_color1, style_tag_color2)
+ insert('end', ".\n")
+ insert('end', "\n3. Stippling.", style_tag_big)
+ insert('end', " You can cause either the ")
+ insert('end', "background", style_tag_bgstipple)
+ insert('end', " or ")
+ insert('end', "foreground", style_tag_fgstipple)
+ insert('end', "\ninformation to be drawn with a stipple fill instead of a solid fill.\n")
+ insert('end', "\n4. Underlining.", style_tag_big)
+ insert('end', " You can ")
+ insert('end', "underline", style_tag_underline)
+ insert('end', " ranges of text.\n")
+ insert('end', "\n5. Overstrikes.", style_tag_big)
+ insert('end', " You can ")
+ insert('end', "draw lines through", style_tag_overstrike)
+ insert('end', " ranges of text.\n")
+ insert('end', "\n6. 3-D effects.", style_tag_big)
+ insert('end', " You can arrange for the background to be drawn\nwith a border that makes characters appear either\n")
+ insert('end', "raised", style_tag_raised)
+ insert('end', " or ")
+ insert('end', "sunken", style_tag_sunken)
+ insert('end', ".\n")
+ insert('end', "\n7. Justification.", style_tag_big)
+ insert('end', " You can arrange for lines to be displayed\n")
+ insert('end', "left-justified,\n")
+ insert('end', "right-justified, or\n", style_tag_right)
+ insert('end', "centered.\n", style_tag_center)
+ insert('end', "\n8. Superscripts and subscripts.", style_tag_big)
+ insert('end', " You can control the vertical\n")
+ insert('end', "position of text to generate superscript effects like 10")
+ insert('end', "n", style_tag_super)
+ insert('end', " or\nsubscript effects like X")
+ insert('end', "i", style_tag_sub)
+ insert('end', ".\n")
+ insert('end', "\n9. Margins.", style_tag_big)
+ insert('end', " You can control the amount of extra space left")
+ insert('end', " on\neach side of the text:\n")
+ insert('end', "This paragraph is an example of the use of ", style_tag_margins)
+ insert('end', "margins. It consists of a single line of text ", style_tag_margins)
+ insert('end', "that wraps around on the screen. There are two ", style_tag_margins)
+ insert('end', "separate left margin values, one for the first ", style_tag_margins)
+ insert('end', "display line associated with the text line, ", style_tag_margins)
+ insert('end', "and one for the subsequent display lines, which ", style_tag_margins)
+ insert('end', "occur because of wrapping. There is also a ", style_tag_margins)
+ insert('end', "separate specification for the right margin, ", style_tag_margins)
+ insert('end', "which is used to choose wrap points for lines.\n", style_tag_margins)
+ insert('end', "\n10. Spacing.", style_tag_big)
+ insert('end', " You can control the spacing of lines with three\n")
+ insert('end', "separate parameters. \"Spacing1\" tells how much ")
+ insert('end', "extra space to leave\nabove a line, \"spacing3\" ")
+ insert('end', "tells how much space to leave below a line,\nand ")
+ insert('end', "if a text line wraps, \"spacing2\" tells how much ")
+ insert('end', "space to leave\nbetween the display lines that ")
+ insert('end', "make up the text line.\n")
+ insert('end', "These indented paragraphs illustrate how spacing ", style_tag_spacing)
+ insert('end', "can be used. Each paragraph is actually a ", style_tag_spacing)
+ insert('end', "single line in the text widget, which is ", style_tag_spacing)
+ insert('end', "word-wrapped by the widget.\n", style_tag_spacing)
+ insert('end', "Spacing1 is set to 10 points for this text, ", style_tag_spacing)
+ insert('end', "which results in relatively large gaps between ", style_tag_spacing)
+ insert('end', "the paragraphs. Spacing2 is set to 2 points, ", style_tag_spacing)
+ insert('end', "which results in just a bit of extra space ", style_tag_spacing)
+ insert('end', "within a pararaph. Spacing3 isn't used ", style_tag_spacing)
+ insert('end', "in this example.\n", style_tag_spacing)
+ insert('end', "To see where the space is, select ranges of ", style_tag_spacing)
+ insert('end', "text within these paragraphs. The selection ", style_tag_spacing)
+ insert('end', "highlight will cover the extra space.", style_tag_spacing)
+
+}
+
diff --git a/ext/tk/sample/demos-en/tcolor b/ext/tk/sample/demos-en/tcolor
new file mode 100644
index 0000000000..8750d15c3e
--- /dev/null
+++ b/ext/tk/sample/demos-en/tcolor
@@ -0,0 +1,513 @@
+#!/usr/bin/env ruby
+#
+# tcolor --
+# ¤³¤Î¥¹¥¯¥ê¥×¥È¤ÏRGB,HSB,CYM·Á¼°¤ò¥µ¥Ý¡¼¥È¤¹¤ë
+# ´Ê°×¥«¥é¡¼¥¨¥Ç¥£¥¿¤Ç¤¹¡£
+#
+# Copyright (C) 1998 Takaaki Tateishi(ttate@jaist.ac.jp)
+# last update: Thu Jun 18 06:32:35 JST 1998
+#
+
+# ¤Þ¤º¤Ïtk.rb¤òÆÉ¤ß¹þ¤à¡£
+
+require "tk"
+
+
+# Tk¤Ë¤è¤Ã¤ÆÊѹ¹¤µ¤ì¤ëÊÑ¿ô¤ÏTkVariable¤Î¥¤¥ó¥¹¥¿¥ó¥¹¤ò»È¤¦¡£
+
+$colorSpace = TkVariable.new(:rgb)
+$red = 65535
+$green = 0
+$blue = 0
+$color = "#ffff00000000"
+$updating = TkVariable.new(0)
+$autoUpdate = TkVariable.new(1)
+$name = TkVariable.new("")
+# $command = TkVariable.new("print(%%,\"\n\")")
+$command = TkVariable.new("")
+$label1 = TkVariable.new("label1")
+$label2 = TkVariable.new("label2")
+$label3 = TkVariable.new("label3")
+
+
+# ³Æ¥¤¥Ù¥ó¥ÈÍѤΥ᥽¥Ã¥É
+
+def rgbToHsv(red,green,blue)
+
+ if ( red > green )
+ max = red
+ min = green
+ else
+ max = green
+ min = red
+ end
+
+ if ( blue > max )
+ max = blue
+ else
+ if ( blue < min )
+ min = blue
+ end
+ end
+
+ range = max - min
+
+ if ( max == 0 )
+ sat = 0.0
+ else
+ sat = (max-min)/max
+ end
+
+ if ( sat == 0 )
+ hue = 0.0
+ else
+ rc = (max-red)/range
+ gc = (max-green)/range
+ bc = (max-blue)/range
+ if ( red == max )
+ hue = 0.166667 * (bc - gc)
+ else
+ if ( green == max )
+ hue = 0.166667 * (2.0 + rc - bc)
+ else
+ hue = 0.166667 * (4.0 + gc - rc)
+ end
+ end
+ if ( hue < 0.0 )
+ hue = hue + 1.0
+ end
+ end
+
+ [hue,sat,max/65535]
+end
+
+
+def hsbToRgb(hue,sat,value)
+ v = 65535.0 * value
+ if( sat == 0 )
+ ans = [v,v,v]
+ else
+ hue = hue*6.0
+ if ( hue >= 6 )
+ hue = 0.0
+ end
+ i = hue.to_i
+ f = hue - i
+ p = 65535.0 * value * (1.0 - sat)
+ q = 65535.0 * value * (1.0 - (sat * f))
+ t = 65535.0 * value * (1.0 - (sat * (1.0 - f)))
+ case i
+ when 0
+ ans = [v,t,p]
+ when 1
+ ans = [q,v,p]
+ when 2
+ ans = [p,v,t]
+ when 3
+ ans = [p,q,v]
+ when 4
+ ans = [t,p,v]
+ when 5
+ ans = [v,p,q]
+ else
+ raise(eException,"i value #{i} is out of range")
+ end
+ end
+ return ans
+end
+
+
+def doUpdate
+ newCmd = $command.to_s.gsub("%%","\"#{$color}\"")
+ eval(newCmd)
+end
+
+
+def tc_scaleChanged
+ if( $updating.to_i == 1 )
+ return
+ end
+
+ scale1 = $root.middle.middle.scale1
+ scale2 = $root.middle.middle.scale2
+ scale3 = $root.middle.middle.scale3
+
+ case $colorSpace.to_i
+ when :rgb
+ $red = (scale1.get * 65.535).to_i
+ $green = (scale2.get * 65.535).to_i
+ $blue = (scale3.get * 65.535).to_i
+ when :cmy
+ $red = (65535 - scale1.get * 65.535).to_i
+ $green = (65535 - scale2.get * 65.535).to_i
+ $blue = (65535 - scale3.get * 65.535).to_i
+ when :hsb
+ list = hsbToRgb(scale1.get / 1000.0,
+ scale2.get / 1000.0,
+ scale3.get / 1000.0)
+ $red = list[0]
+ $green = list[1]
+ $blue = list[2]
+ else
+ raise(Exception,"unknown colorSpace")
+ end
+ $color = format("#%04x%04x%04x",$red.to_i,$green.to_i,$blue.to_i)
+ $root.middle.right.set_color($color)
+ if( $autoUpdate.to_i == 1 )
+ doUpdate
+ end
+ Tk.update(TRUE)
+end
+
+
+def tc_setScales
+ $updating.value = 1
+
+ scale1 = $root.middle.middle.scale1
+ scale2 = $root.middle.middle.scale2
+ scale3 = $root.middle.middle.scale3
+
+ case $colorSpace.to_i
+ when :rgb
+ scale1.set($red / 65.535)
+ scale2.set($green / 65.535)
+ scale3.set($blue / 65.535)
+ when :cmy
+ scale1.set((65535 - $red) / 65.535)
+ scale2.set((65535 - $green) / 65.535)
+ scale3.set((65535 - $blue) / 65.535)
+ when :hsb
+ list = rgbToHsv($red,$green,$blue)
+ scale1.set( list[0] * 1000.0 )
+ scale2.set( list[1] * 1000.0 )
+ scale3.set( list[2] * 1000.0 )
+ else
+ raise(Exception,"unknown colorSpace")
+ end
+
+ $updating.value = 0
+end
+
+
+def tc_loadNamedColor(name)
+ if name[0,1] != "#"
+ list = TkWinfo.rgb($root.middle.right.swatch,name)
+ $red = list[0]
+ $green = list[1]
+ $blue = list[2]
+ else
+ case name.length
+ when 4
+ format = /#(.{1})(.{1})(.{1})/
+ shift = 12
+ when 7
+ format = /#(.{2})(.{2})(.{2})/
+ shift = 8
+ when 10
+ format = /#(.{3})(.{3})(.{3})/
+ shift = 4
+ when 13
+ format = /#(.{4})(.{4})(.{4})/
+ shift = 0
+ else
+ raise(eException,"syntax error in color name \"#{name}\"")
+ end
+ name.scan(format){|strlist|
+ if strlist.length != 3
+ raise(eException,"syntax error in color name \"#{name}\"")
+ end
+ $red = strlist[0].to_i
+ $green = strlist[1].to_i
+ $blue = strlist[2].to_i
+ }
+ $red = $red << shift
+ $green = $green << shift
+ $blue = $blue << shift
+ end
+
+ tc_setScales
+ $color = format("#%04x%04x%04x",$red,$green,$blue)
+ $root.middle.right.set_color($color)
+ if $autoUpdate.to_i == 1
+ doUpdate
+ end
+end
+
+
+def changeColorSpace(space)
+ case space
+ when :rgb
+ $label1.value = "Red"
+ $label2.value = "Green"
+ $label3.value = "Blue"
+ when :cmy
+ $label1.value = "Cyan"
+ $label2.value = "Magenta"
+ $label3.value = "Yellow"
+ when :hsb
+ $label1.value = "Hue"
+ $label2.value = "Saturation"
+ $label3.value = "Brightness"
+ end
+ tc_setScales
+end
+
+
+
+
+
+# tcolorÍѤΥá¥Ë¥å¡¼
+
+class TkColorMenuFrame<TkFrame
+ def initialize(parent)
+ super(parent,
+ "relief"=>"raised",
+ "borderwidth"=>"2")
+
+ # File¥á¥Ë¥å¡¼¥Ü¥¿¥ó¤ÎÀ¸À®
+ @file = TkMenubutton.new(self){|button|
+
+ # File¥á¥Ë¥å¡¼¤ÎºîÀ®
+ @file_menu = TkMenu.new(button){
+ add "radio",
+ "label" => "RGB color space",
+ "variable" => $colorSpace,
+ "value" => :rgb,
+ "underline" => "0",
+ "command" => proc{changeColorSpace(:rgb)}
+ add "radio",
+ "label" => "CMY color space",
+ "variable" => $colorSpace,
+ "value" => :cmy,
+ "underline" => "0",
+ "command" => proc{changeColorSpace(:cmy)}
+ add "radio",
+ "label" => "HSB color space",
+ "variable" => $colorSpace,
+ "value" => :hsb,
+ "underline" => "0",
+ "command" => proc{changeColorSpace(:hsb)}
+ add "separator"
+ add "radio",
+ "label" => "Qutomatic updates",
+ "variable" => $autoUpdate,
+ "value" => "1",
+ "underline" => "0"
+ add "radio",
+ "label" => "Manual updates",
+ "variable" => $autoUpdate,
+ "value" => "0",
+ "underline" => "0"
+ add "separator"
+ add "command",
+ "label" => "Exit program",
+ "underline" => "0",
+ "command" => proc{exit}
+ }
+
+ # File¥á¥Ë¥å¡¼¤ÈFile¥Ü¥¿¥ó¤ò´ØÏ¢ÉÕ¤±¤ë
+ menu @file_menu
+
+ text "File"
+ underline "0"
+ }.pack("side"=>"left")
+
+ self
+ end
+end
+
+
+# ²¼Éô¤Î¥Õ¥ì¡¼¥à¤Î¤¿¤á¤Î¥¯¥é¥¹
+class TkColorBotFrame<TkFrame
+ def initialize(parent)
+ super(parent,
+ "relief"=> "raised",
+ "borderwidth"=> 2)
+
+ @commandLabel = TkLabel.new(self,
+ "text"=> "Command:")
+ @command = TkEntry.new(self,
+ "relief"=> "sunken",
+ "borderwidth"=> "2",
+ "textvariable"=> $command,
+ "font"=> "-Adobe-Courier-Medium-R-Normal--*-120-*-*-*-*-*-*")
+ @update = TkButton.new(self,
+ "text"=> "Update",
+ "command"=> proc{doUpdate})
+ @commandLabel.pack("side"=>"left")
+ @update.pack("side"=>"right","pady"=>".1c","padx"=>".25c")
+ @command.pack("expand"=>"yes","fill"=>"x","ipadx"=>".25c")
+
+ self
+ end
+end
+
+
+# ÃæÃʺ¸¤Î¥Õ¥ì¡¼¥à
+class TkColorMiddleLeftFrame<TkFrame
+ def initialize(parent)
+ super(parent)
+
+ for i in ["/usr/local/lib/X11rgb.txt","/usr/lib/X11/rgb.txt",
+ "/X11/R5/lib/X11/rgb.txt","/X11/R4/lib/rgb/rgb.txt",
+ "/usr/openwin/lib/X11/rgb.txt"]
+ if !File.readable?(i)
+ next
+ end
+ f = File.open(i)
+ @scroll = TkScrollbar.new(self,
+ "orient"=>"vertical",
+ "relief"=>"sunken",
+ "borderwidth"=>"2")
+ @scroll.pack("side"=>"right","fill"=>"y")
+ @names = TkListbox.new(self,
+ "width"=>"20",
+ "height"=>"12",
+ "yscrollcommand"=> proc{|first,last| @scroll.set first,last},
+ "relief"=>"sunken",
+ "borderwidth"=>"2",
+ "exportselection"=>"false")
+ @scroll.command(proc{|*args| @names.yview *args})
+ @names.bind("Double-1",proc{
+ tc_loadNamedColor(@names.get(@names.curselection))})
+ @names.pack("side"=>"left")
+ while (line = f.gets)
+ line.chop!
+ linelist = line.split(/[ \t]+/)
+ if linelist.length == 4
+ @names.insert("end",linelist[3])
+ end
+ end
+ f.close
+ break
+ end
+
+ self
+ end
+end
+
+
+# ÃæÃÊÃæ±û¤Î¥Õ¥ì¡¼¥à
+class TkColorMiddleMiddleFrame<TkFrame
+ # @scale1,@scale2,@scale3¤ò³°Éô¤«¤é»²¾È¤Î¤ßµö²Ä¤¹¤ë¡£(Êѹ¹ÉÔ²Ä)
+ attr_reader :scale1, :scale2, :scale3
+
+ def initialize(parent)
+ super(parent)
+
+ @f1 = TkFrame.new(self)
+ @f2 = TkFrame.new(self)
+ @f3 = TkFrame.new(self)
+ @f4 = TkFrame.new(self)
+
+ for f in [@f1,@f2,@f3]
+ f.pack("side"=>"top","expand"=>"yes")
+ end
+ @f4.pack("side"=>"top","expand"=>"yes","fill"=>"x")
+
+ @label1 = TkLabel.new(self,"textvariable"=>$label1)
+ @scale1 = TkScale.new(self,"from"=>"0","to"=>"1000","length"=>"6c",
+ "orient"=>"horizontal",
+ "command"=>proc{tc_scaleChanged})
+ @scale1.pack("side"=>"top","anchor"=>"w")
+ @label1.pack("side"=>"top","anchor"=>"w")
+
+ @label2 = TkLabel.new(self,"textvariable"=>$label2)
+ @scale2 = TkScale.new(self,"from"=>"0","to"=>"1000","length"=>"6c",
+ "orient"=>"horizontal",
+ "command"=>proc{tc_scaleChanged})
+ @scale2.pack("side"=>"top","anchor"=>"w")
+ @label2.pack("side"=>"top","anchor"=>"w")
+
+ @label3 = TkLabel.new(self,"textvariable"=>$label3)
+ @scale3 = TkScale.new(self,"from"=>"0","to"=>"1000","length"=>"6c",
+ "orient"=>"horizontal",
+ "command"=>proc{tc_scaleChanged})
+ @scale3.pack("side"=>"top","anchor"=>"w")
+ @label3.pack("side"=>"top","anchor"=>"w")
+
+ @nameLabel = TkLabel.new(self,"text"=>"Name:")
+ @name = TkEntry.new(self,"relief"=>"sunken","borderwidth"=>"2",
+ "textvariable"=>$name,"width"=>"10",
+ "font"=>"-Adobe-Courier-Medium-R-Normal--*-120-*-*-*-*-*-*")
+ @nameLabel.pack("side"=>"left")
+ @name.pack("side"=>"right", "expand"=>"1", "fill"=>"x")
+ @name.bind("Return",proc{tc_loadNamedColor $name.to_s})
+
+ self
+ end
+end
+
+
+class TkColorMiddleRightFrame<TkFrame
+ attr_reader :swatch
+
+ def initialize(parent)
+ super(parent)
+ @swatch = TkFrame.new(self, "width"=>"2c", "height"=>"5c",
+ "background"=>$color)
+ @value = TkLabel.new(self,
+ "text"=>$color,
+ "width"=>"13",
+ "font"=>"-Adobe-Courier-Medium-R-Normal--*-120-*-*-*-*-*-*")
+ @swatch.pack("side"=>"top","expand"=>"yes","fill"=>"both")
+ @value.pack("side"=>"bottom","pady"=>".25c")
+
+ self
+ end
+
+ def set_color(color)
+ @swatch["background"] = color
+ @value["text"] = color
+ end
+end
+
+
+
+# ÃæÃʤΥե졼¥à
+class TkColorMiddleFrame<TkFrame
+ attr_reader :left, :middle, :right
+
+ def initialize(parent)
+ super(parent,
+ "relief"=> "raised",
+ "borderwidth"=> "2")
+
+ @left = TkColorMiddleLeftFrame.new(self)
+ @left.pack("side"=>"left","padx"=>".25c","pady"=>".25c")
+
+ @middle = TkColorMiddleMiddleFrame.new(self)
+ @middle.pack("side"=>"left","expand"=>"yes","fill"=>"y")
+
+ @right = TkColorMiddleRightFrame.new(self)
+ @right.pack("side"=>"left","padx"=>".25c","pady"=>".25c","anchor"=>"s")
+
+ self
+ end
+end
+
+
+class TkColor<TkRoot
+ attr_reader :menu, :bottom, :middle
+
+ def initialize
+ super
+ @menu = TkColorMenuFrame.new(self)
+ @menu.pack("side"=>"top", "fill"=>"x")
+
+ @bottom = TkColorBotFrame.new(self)
+ @bottom.pack("side"=>"bottom","fill"=>"x")
+
+ @middle = TkColorMiddleFrame.new(self)
+ @middle.pack("side"=>"top","fill"=>"both")
+
+ self
+ end
+end
+
+
+$root = TkColor.new
+
+# ¥¤¥Ù¥ó¥È¤òÂԤİ٤˥롼¥×¤ËÆþ¤ë¡£
+changeColorSpace :rgb
+Tk.mainloop
diff --git a/ext/tk/sample/demos-en/tcolor.bak b/ext/tk/sample/demos-en/tcolor.bak
new file mode 100644
index 0000000000..5464aebae2
--- /dev/null
+++ b/ext/tk/sample/demos-en/tcolor.bak
@@ -0,0 +1,513 @@
+#!/usr/local/bin/ruby
+#
+# tcolor --
+# ¤³¤Î¥¹¥¯¥ê¥×¥È¤ÏRGB,HSB,CYM·Á¼°¤ò¥µ¥Ý¡¼¥È¤¹¤ë
+# ´Ê°×¥«¥é¡¼¥¨¥Ç¥£¥¿¤Ç¤¹¡£
+#
+# Copyright (C) 1998 Takaaki Tateishi(ttate@jaist.ac.jp)
+# last update: Thu Jun 18 06:32:35 JST 1998
+#
+
+# ¤Þ¤º¤Ïtk.rb¤òÆÉ¤ß¹þ¤à¡£
+
+require "tk"
+
+
+# Tk¤Ë¤è¤Ã¤ÆÊѹ¹¤µ¤ì¤ëÊÑ¿ô¤ÏTkVariable¤Î¥¤¥ó¥¹¥¿¥ó¥¹¤ò»È¤¦¡£
+
+$colorSpace = TkVariable.new(:rgb)
+$red = 65535
+$green = 0
+$blue = 0
+$color = "#ffff00000000"
+$updating = TkVariable.new(0)
+$autoUpdate = TkVariable.new(1)
+$name = TkVariable.new("")
+# $command = TkVariable.new("print(%%,\"\n\")")
+$command = TkVariable.new("")
+$label1 = TkVariable.new("label1")
+$label2 = TkVariable.new("label2")
+$label3 = TkVariable.new("label3")
+
+
+# ³Æ¥¤¥Ù¥ó¥ÈÍѤΥ᥽¥Ã¥É
+
+def rgbToHsv(red,green,blue)
+
+ if ( red > green )
+ max = red
+ min = green
+ else
+ max = green
+ min = red
+ end
+
+ if ( blue > max )
+ max = blue
+ else
+ if ( blue < min )
+ min = blue
+ end
+ end
+
+ range = max - min
+
+ if ( max == 0 )
+ sat = 0.0
+ else
+ sat = (max-min)/max
+ end
+
+ if ( sat == 0 )
+ hue = 0.0
+ else
+ rc = (max-red)/range
+ gc = (max-green)/range
+ bc = (max-blue)/range
+ if ( red == max )
+ hue = 0.166667 * (bc - gc)
+ else
+ if ( green == max )
+ hue = 0.166667 * (2.0 + rc - bc)
+ else
+ hue = 0.166667 * (4.0 + gc - rc)
+ end
+ end
+ if ( hue < 0.0 )
+ hue = hue + 1.0
+ end
+ end
+
+ [hue,sat,max/65535]
+end
+
+
+def hsbToRgb(hue,sat,value)
+ v = 65535.0 * value
+ if( sat == 0 )
+ ans = [v,v,v]
+ else
+ hue = hue*6.0
+ if ( hue >= 6 )
+ hue = 0.0
+ end
+ i = hue.to_i
+ f = hue - i
+ p = 65535.0 * value * (1.0 - sat)
+ q = 65535.0 * value * (1.0 - (sat * f))
+ t = 65535.0 * value * (1.0 - (sat * (1.0 - f)))
+ case i
+ when 0
+ ans = [v,t,p]
+ when 1
+ ans = [q,v,p]
+ when 2
+ ans = [p,v,t]
+ when 3
+ ans = [p,q,v]
+ when 4
+ ans = [t,p,v]
+ when 5
+ ans = [v,p,q]
+ else
+ raise(eException,"i value #{i} is out of range")
+ end
+ end
+ return ans
+end
+
+
+def doUpdate
+ newCmd = $command.to_s.gsub("%%","\"#{$color}\"")
+ eval(newCmd)
+end
+
+
+def tc_scaleChanged
+ if( $updating.to_i == 1 )
+ return
+ end
+
+ scale1 = $root.middle.middle.scale1
+ scale2 = $root.middle.middle.scale2
+ scale3 = $root.middle.middle.scale3
+
+ case $colorSpace.to_i
+ when :rgb
+ $red = (scale1.get * 65.535).to_i
+ $green = (scale2.get * 65.535).to_i
+ $blue = (scale3.get * 65.535).to_i
+ when :cmy
+ $red = (65535 - scale1.get * 65.535).to_i
+ $green = (65535 - scale2.get * 65.535).to_i
+ $blue = (65535 - scale3.get * 65.535).to_i
+ when :hsb
+ list = hsbToRgb(scale1.get / 1000.0,
+ scale2.get / 1000.0,
+ scale3.get / 1000.0)
+ $red = list[0]
+ $green = list[1]
+ $blue = list[2]
+ else
+ raise(Exception,"unknown colorSpace")
+ end
+ $color = format("#%04x%04x%04x",$red.to_i,$green.to_i,$blue.to_i)
+ $root.middle.right.set_color($color)
+ if( $autoUpdate.to_i == 1 )
+ doUpdate
+ end
+ Tk.update(TRUE)
+end
+
+
+def tc_setScales
+ $updating.value = 1
+
+ scale1 = $root.middle.middle.scale1
+ scale2 = $root.middle.middle.scale2
+ scale3 = $root.middle.middle.scale3
+
+ case $colorSpace.to_i
+ when :rgb
+ scale1.set($red / 65.535)
+ scale2.set($green / 65.535)
+ scale3.set($blue / 65.535)
+ when :cmy
+ scale1.set((65535 - $red) / 65.535)
+ scale2.set((65535 - $green) / 65.535)
+ scale3.set((65535 - $blue) / 65.535)
+ when :hsb
+ list = rgbToHsv($red,$green,$blue)
+ scale1.set( list[0] * 1000.0 )
+ scale2.set( list[1] * 1000.0 )
+ scale3.set( list[2] * 1000.0 )
+ else
+ raise(Exception,"unknown colorSpace")
+ end
+
+ $updating.value = 0
+end
+
+
+def tc_loadNamedColor(name)
+ if name[0,1] != "#"
+ list = TkWinfo.rgb($root.middle.right.swatch,name)
+ $red = list[0]
+ $green = list[1]
+ $blue = list[2]
+ else
+ case name.length
+ when 4
+ format = /#(.{1})(.{1})(.{1})/
+ shift = 12
+ when 7
+ format = /#(.{2})(.{2})(.{2})/
+ shift = 8
+ when 10
+ format = /#(.{3})(.{3})(.{3})/
+ shift = 4
+ when 13
+ format = /#(.{4})(.{4})(.{4})/
+ shift = 0
+ else
+ raise(eException,"syntax error in color name \"#{name}\"")
+ end
+ name.scan(format){|strlist|
+ if strlist.length != 3
+ raise(eException,"syntax error in color name \"#{name}\"")
+ end
+ $red = strlist[0].to_i
+ $green = strlist[1].to_i
+ $blue = strlist[2].to_i
+ }
+ $red = $red << shift
+ $green = $green << shift
+ $blue = $blue << shift
+ end
+
+ tc_setScales
+ $color = format("#%04x%04x%04x",$red,$green,$blue)
+ $root.middle.right.set_color($color)
+ if $autoUpdate.to_i == 1
+ doUpdate
+ end
+end
+
+
+def changeColorSpace(space)
+ case space
+ when :rgb
+ $label1.value = "Red"
+ $label2.value = "Green"
+ $label3.value = "Blue"
+ when :cmy
+ $label1.value = "Cyan"
+ $label2.value = "Magenta"
+ $label3.value = "Yellow"
+ when :hsb
+ $label1.value = "Hue"
+ $label2.value = "Saturation"
+ $label3.value = "Brightness"
+ end
+ tc_setScales
+end
+
+
+
+
+
+# tcolorÍѤΥá¥Ë¥å¡¼
+
+class TkColorMenuFrame<TkFrame
+ def initialize(parent)
+ super(parent,
+ "relief"=>"raised",
+ "borderwidth"=>"2")
+
+ # File¥á¥Ë¥å¡¼¥Ü¥¿¥ó¤ÎÀ¸À®
+ @file = TkMenubutton.new(self){|button|
+
+ # File¥á¥Ë¥å¡¼¤ÎºîÀ®
+ @file_menu = TkMenu.new(button){
+ add "radio",
+ "label" => "RGB color space",
+ "variable" => $colorSpace,
+ "value" => :rgb,
+ "underline" => "0",
+ "command" => proc{changeColorSpace(:rgb)}
+ add "radio",
+ "label" => "CMY color space",
+ "variable" => $colorSpace,
+ "value" => :cmy,
+ "underline" => "0",
+ "command" => proc{changeColorSpace(:cmy)}
+ add "radio",
+ "label" => "HSB color space",
+ "variable" => $colorSpace,
+ "value" => :hsb,
+ "underline" => "0",
+ "command" => proc{changeColorSpace(:hsb)}
+ add "separator"
+ add "radio",
+ "label" => "Qutomatic updates",
+ "variable" => $autoUpdate,
+ "value" => "1",
+ "underline" => "0"
+ add "radio",
+ "label" => "Manual updates",
+ "variable" => $autoUpdate,
+ "value" => "0",
+ "underline" => "0"
+ add "separator"
+ add "command",
+ "label" => "Exit program",
+ "underline" => "0",
+ "command" => proc{exit}
+ }
+
+ # File¥á¥Ë¥å¡¼¤ÈFile¥Ü¥¿¥ó¤ò´ØÏ¢ÉÕ¤±¤ë
+ menu @file_menu
+
+ text "File"
+ underline "0"
+ }.pack("side"=>"left")
+
+ self
+ end
+end
+
+
+# ²¼Éô¤Î¥Õ¥ì¡¼¥à¤Î¤¿¤á¤Î¥¯¥é¥¹
+class TkColorBotFrame<TkFrame
+ def initialize(parent)
+ super(parent,
+ "relief"=> "raised",
+ "borderwidth"=> 2)
+
+ @commandLabel = TkLabel.new(self,
+ "text"=> "Command:")
+ @command = TkEntry.new(self,
+ "relief"=> "sunken",
+ "borderwidth"=> "2",
+ "textvariable"=> $command,
+ "font"=> "-Adobe-Courier-Medium-R-Normal--*-120-*-*-*-*-*-*")
+ @update = TkButton.new(self,
+ "text"=> "Update",
+ "command"=> proc{doUpdate})
+ @commandLabel.pack("side"=>"left")
+ @update.pack("side"=>"right","pady"=>".1c","padx"=>".25c")
+ @command.pack("expand"=>"yes","fill"=>"x","ipadx"=>".25c")
+
+ self
+ end
+end
+
+
+# ÃæÃʺ¸¤Î¥Õ¥ì¡¼¥à
+class TkColorMiddleLeftFrame<TkFrame
+ def initialize(parent)
+ super(parent)
+
+ for i in ["/usr/local/lib/X11rgb.txt","/usr/lib/X11/rgb.txt",
+ "/X11/R5/lib/X11/rgb.txt","/X11/R4/lib/rgb/rgb.txt",
+ "/usr/openwin/lib/X11/rgb.txt"]
+ if !File.readable?(i)
+ next
+ end
+ f = File.open(i)
+ @scroll = TkScrollbar.new(self,
+ "orient"=>"vertical",
+ "relief"=>"sunken",
+ "borderwidth"=>"2")
+ @scroll.pack("side"=>"right","fill"=>"y")
+ @names = TkListbox.new(self,
+ "width"=>"20",
+ "height"=>"12",
+ "yscrollcommand"=> proc{|first,last| @scroll.set first,last},
+ "relief"=>"sunken",
+ "borderwidth"=>"2",
+ "exportselection"=>"false")
+ @scroll.command(proc{|*args| @names.yview *args})
+ @names.bind("Double-1",proc{
+ tc_loadNamedColor(@names.get(@names.curselection))})
+ @names.pack("side"=>"left")
+ while (line = f.gets)
+ line.chop!
+ linelist = line.split(/[ \t]+/)
+ if linelist.length == 4
+ @names.insert("end",linelist[3])
+ end
+ end
+ f.close
+ break
+ end
+
+ self
+ end
+end
+
+
+# ÃæÃÊÃæ±û¤Î¥Õ¥ì¡¼¥à
+class TkColorMiddleMiddleFrame<TkFrame
+ # @scale1,@scale2,@scale3¤ò³°Éô¤«¤é»²¾È¤Î¤ßµö²Ä¤¹¤ë¡£(Êѹ¹ÉÔ²Ä)
+ attr_reader :scale1, :scale2, :scale3
+
+ def initialize(parent)
+ super(parent)
+
+ @f1 = TkFrame.new(self)
+ @f2 = TkFrame.new(self)
+ @f3 = TkFrame.new(self)
+ @f4 = TkFrame.new(self)
+
+ for f in [@f1,@f2,@f3]
+ f.pack("side"=>"top","expand"=>"yes")
+ end
+ @f4.pack("side"=>"top","expand"=>"yes","fill"=>"x")
+
+ @label1 = TkLabel.new(self,"textvariable"=>$label1)
+ @scale1 = TkScale.new(self,"from"=>"0","to"=>"1000","length"=>"6c",
+ "orient"=>"horizontal",
+ "command"=>proc{tc_scaleChanged})
+ @scale1.pack("side"=>"top","anchor"=>"w")
+ @label1.pack("side"=>"top","anchor"=>"w")
+
+ @label2 = TkLabel.new(self,"textvariable"=>$label2)
+ @scale2 = TkScale.new(self,"from"=>"0","to"=>"1000","length"=>"6c",
+ "orient"=>"horizontal",
+ "command"=>proc{tc_scaleChanged})
+ @scale2.pack("side"=>"top","anchor"=>"w")
+ @label2.pack("side"=>"top","anchor"=>"w")
+
+ @label3 = TkLabel.new(self,"textvariable"=>$label3)
+ @scale3 = TkScale.new(self,"from"=>"0","to"=>"1000","length"=>"6c",
+ "orient"=>"horizontal",
+ "command"=>proc{tc_scaleChanged})
+ @scale3.pack("side"=>"top","anchor"=>"w")
+ @label3.pack("side"=>"top","anchor"=>"w")
+
+ @nameLabel = TkLabel.new(self,"text"=>"Name:")
+ @name = TkEntry.new(self,"relief"=>"sunken","borderwidth"=>"2",
+ "textvariable"=>$name,"width"=>"10",
+ "font"=>"-Adobe-Courier-Medium-R-Normal--*-120-*-*-*-*-*-*")
+ @nameLabel.pack("side"=>"left")
+ @name.pack("side"=>"right", "expand"=>"1", "fill"=>"x")
+ @name.bind("Return",proc{tc_loadNamedColor $name.to_s})
+
+ self
+ end
+end
+
+
+class TkColorMiddleRightFrame<TkFrame
+ attr_reader :swatch
+
+ def initialize(parent)
+ super(parent)
+ @swatch = TkFrame.new(self, "width"=>"2c", "height"=>"5c",
+ "background"=>$color)
+ @value = TkLabel.new(self,
+ "text"=>$color,
+ "width"=>"13",
+ "font"=>"-Adobe-Courier-Medium-R-Normal--*-120-*-*-*-*-*-*")
+ @swatch.pack("side"=>"top","expand"=>"yes","fill"=>"both")
+ @value.pack("side"=>"bottom","pady"=>".25c")
+
+ self
+ end
+
+ def set_color(color)
+ @swatch["background"] = color
+ @value["text"] = color
+ end
+end
+
+
+
+# ÃæÃʤΥե졼¥à
+class TkColorMiddleFrame<TkFrame
+ attr_reader :left, :middle, :right
+
+ def initialize(parent)
+ super(parent,
+ "relief"=> "raised",
+ "borderwidth"=> "2")
+
+ @left = TkColorMiddleLeftFrame.new(self)
+ @left.pack("side"=>"left","padx"=>".25c","pady"=>".25c")
+
+ @middle = TkColorMiddleMiddleFrame.new(self)
+ @middle.pack("side"=>"left","expand"=>"yes","fill"=>"y")
+
+ @right = TkColorMiddleRightFrame.new(self)
+ @right.pack("side"=>"left","padx"=>".25c","pady"=>".25c","anchor"=>"s")
+
+ self
+ end
+end
+
+
+class TkColor<TkRoot
+ attr_reader :menu, :bottom, :middle
+
+ def initialize
+ super
+ @menu = TkColorMenuFrame.new(self)
+ @menu.pack("side"=>"top", "fill"=>"x")
+
+ @bottom = TkColorBotFrame.new(self)
+ @bottom.pack("side"=>"bottom","fill"=>"x")
+
+ @middle = TkColorMiddleFrame.new(self)
+ @middle.pack("side"=>"top","fill"=>"both")
+
+ self
+ end
+end
+
+
+$root = TkColor.new
+
+# ¥¤¥Ù¥ó¥È¤òÂԤİ٤˥롼¥×¤ËÆþ¤ë¡£
+changeColorSpace :rgb
+Tk.mainloop
diff --git a/ext/tk/sample/demos-en/text.rb b/ext/tk/sample/demos-en/text.rb
new file mode 100644
index 0000000000..1dcaad1cf9
--- /dev/null
+++ b/ext/tk/sample/demos-en/text.rb
@@ -0,0 +1,102 @@
+# text.rb
+#
+# This demonstration script creates a text widget that describes
+# the basic editing functions.
+#
+# text (basic facilities) widget demo (called by 'widget')
+#
+
+# toplevel widget
+if defined?($text_demo) && $text_demo
+ $text_demo.destroy
+ $text_demo = nil
+end
+
+# demo toplevel widget
+$text_demo = TkToplevel.new {|w|
+ title("Text Demonstration - Basic Facilities")
+ iconname("text")
+ positionWindow(w)
+}
+
+# frame
+TkFrame.new($text_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $text_demo
+ $text_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'text'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# text À¸À®
+TkText.new($text_demo){|t|
+ # À¸À®
+ relief 'sunken'
+ bd 2
+ setgrid 1
+ height 30
+ TkScrollbar.new($text_demo) {|s|
+ pack('side'=>'right', 'fill'=>'y')
+ command proc{|*args| t.yview(*args)}
+ t.yscrollcommand proc{|first,last| s.set first,last}
+ }
+ pack('expand'=>'yes', 'fill'=>'both')
+
+ #
+ insert('0.0', %q|This window is a text widget. It displays one or more lines of text
+and allows you to edit the text. Here is a summary of the things you
+can do to a text widget:
+
+1. Scrolling. Use the scrollbar to adjust the view in the text window.
+
+2. Scanning. Press mouse button 2 in the text window and drag up or down.
+This will drag the text at high speed to allow you to scan its contents.
+
+3. Insert text. Press mouse button 1 to set the insertion cursor, then
+type text. What you type will be added to the widget.
+
+4. Select. Press mouse button 1 and drag to select a range of characters.
+Once you've released the button, you can adjust the selection by pressing
+button 1 with the shift key down. This will reset the end of the
+selection nearest the mouse cursor and you can drag that end of the
+selection by dragging the mouse before releasing the mouse button.
+You can double-click to select whole words or triple-click to select
+whole lines.
+
+5. Delete and replace. To delete text, select the characters you'd like
+to delete and type Backspace or Delete. Alternatively, you can type new
+text, in which case it will replace the selected text.
+
+6. Copy the selection. To copy the selection into this window, select
+what you want to copy (either here or in another application), then
+click button 2 to copy the selection to the point of the mouse cursor.
+
+7. Edit. Text widgets support the standard Motif editing characters
+plus many Emacs editing characters. Backspace and Control-h erase the
+character to the left of the insertion cursor. Delete and Control-d
+erase the character to the right of the insertion cursor. Meta-backspace
+deletes the word to the left of the insertion cursor, and Meta-d deletes
+the word to the right of the insertion cursor. Control-k deletes from
+the insertion cursor to the end of the line, or it deletes the newline
+character if that is the only thing left on the line. Control-o opens
+a new line by inserting a newline character to the right of the insertion
+cursor. Control-t transposes the two characters on either side of the
+insertion cursor.
+
+7. Resize the window. This widget has been configured with the "setGrid"
+option on, so that if you resize the window it will always resize to an
+even number of characters high and wide. Also, if you make the window
+narrow you can see that long lines automatically wrap around onto
+additional lines so that all the information is always visible.|)
+
+ set_insert('0.0')
+}
+
diff --git a/ext/tk/sample/demos-en/timer b/ext/tk/sample/demos-en/timer
new file mode 100644
index 0000000000..896568c0bc
--- /dev/null
+++ b/ext/tk/sample/demos-en/timer
@@ -0,0 +1,120 @@
+#!/usr/bin/env ruby
+#
+# timer --
+# This script generates a counter with start,stop and reset buttons.
+#
+# Copyright (C) 1998 Takaaki Tateishi (ttate@jaist.ac.jp)
+# last update: Sat Jun 27 12:24:14 JST 1998
+#
+
+require "tk"
+require "thread"
+require "tkafter"
+
+$time = "0.00"
+$m = Mutex.new
+$loop = false
+
+def timer_stop
+ $loop = false
+ $m.lock
+end
+
+def timer_start
+ $loop = true
+ $m.unlock
+end
+
+def timer_reset
+ $time = "0.00"
+ $root.countframe.counter['text'] = $time
+end
+
+def timer_loop
+ if $loop
+ $time = $time.succ
+ $root.countframe.counter['text'] = $time
+ end
+ Tk.after(10,proc{timer_loop})
+end
+
+
+#
+# thread version
+#
+def timer_loop2
+ while true
+ $m.lock
+ $time = $time.succ
+ $root.countframe.counter['text'] = $time
+ sleep(0.01)
+ $m.unlock
+ end
+end
+
+#
+# TkAfter
+#
+def timer_loop3
+ if $loop
+ $time = $time.succ
+ $root.countframe.counter['text'] = $time
+ end
+end
+
+
+class CountFrame < TkFrame
+ attr_reader :counter
+
+ def initialize(parent=nil,keys=nil)
+ super(parent,keys)
+ @counter = TkLabel.new(self,
+ 'text'=>$time,
+ 'relief'=>'raised')
+ @counter.pack('fill'=>'both')
+ self
+ end
+end
+
+
+class ButtonFrame < TkFrame
+ def initialize(parent=nil,keys=nil)
+ super(parent,keys)
+ @stop = TkButton.new(self,
+ 'text'=>'Stop',
+ 'command'=>proc{timer_stop})
+ @start = TkButton.new(self,
+ 'text'=>'Start',
+ 'command'=>proc{timer_start})
+ @reset = TkButton.new(self,
+ 'text'=>'Reset',
+ 'command'=>proc{timer_reset})
+ for b in [@stop,@start,@reset]
+ b.pack('side'=>'left', 'fill'=>'both', 'expand'=>'yes')
+ end
+ end
+end
+
+
+class Timer < TkRoot
+ attr_reader :countframe
+
+ def initialize
+ super
+ @countframe = CountFrame.new(self)
+ @buttonframe = ButtonFrame.new(self)
+ for f in [@buttonframe,@countframe]
+ f.pack('side'=>'top', 'fill'=>'both')
+ end
+ self
+ end
+end
+
+
+$root = Timer.new
+
+#$thread = Thread.start{timer_loop2}
+#timer_loop
+TkAfter.new(10,-1,proc{timer_loop3}).start
+
+Tk.mainloop
diff --git a/ext/tk/sample/demos-en/tkencoding.rb b/ext/tk/sample/demos-en/tkencoding.rb
new file mode 100644
index 0000000000..727491a6ad
--- /dev/null
+++ b/ext/tk/sample/demos-en/tkencoding.rb
@@ -0,0 +1,42 @@
+# -*- ruby -*-
+#
+# tkencoding.rb
+# written by ttate@jaist.ac.jp
+
+class TclTkIp
+ alias __eval _eval
+ alias __invoke _invoke
+ private :__eval
+ private :__invoke
+
+ attr_accessor :encoding
+
+ def _eval(cmd)
+ if( @encoding )
+ _fromUTF8(__eval(_toUTF8(cmd,@encoding)),@encoding)
+ else
+ __eval(cmd)
+ end
+ end
+
+ def _invoke(*cmds)
+ if( @encoding )
+ cmds = cmds.collect{|cmd| _toUTF8(cmd,@encoding)}
+ _fromUTF8(__invoke(*cmds),@encoding)
+ else
+ __invoke(*cmds)
+ end
+ end
+end
+
+module Tk
+ INTERP = TkCore::INTERP
+
+ def encoding=(name)
+ INTERP.encoding = name
+ end
+
+ def encoding
+ INTERP.encoding
+ end
+end
diff --git a/ext/tk/sample/demos-en/twind.rb b/ext/tk/sample/demos-en/twind.rb
new file mode 100644
index 0000000000..c841b7c317
--- /dev/null
+++ b/ext/tk/sample/demos-en/twind.rb
@@ -0,0 +1,285 @@
+# twind.rb
+#
+# This demonstration script creates a text widget with a bunch of
+# embedded windows.
+#
+# text (embedded windows) widget demo (called by 'widget')
+#
+
+# toplevel widget
+if defined?($twind_demo) && $twind_demo
+ $twind_demo.destroy
+ $twind_demo = nil
+end
+
+# demo toplevel widget
+$twind_demo = TkToplevel.new {|w|
+ title("Text Demonstration - Embedded Windows")
+ iconname("Embedded Windows")
+ positionWindow(w)
+}
+
+# frame
+$twind_buttons = TkFrame.new($twind_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc{
+ tmppath = $twind_demo
+ $twind_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc{showCode 'twind'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}
+$twind_buttons.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame
+$twind_text = nil
+TkFrame.new($twind_demo, 'highlightthickness'=>2, 'borderwidth'=>2,
+ 'relief'=>'sunken') {|f|
+ $twind_text = TkText.new(f, 'setgrid'=>'true', 'font'=>$font,
+ 'width'=>'70', 'height'=>35, 'wrap'=>'word',
+ 'highlightthickness'=>0, 'borderwidth'=>0 ){|t|
+ TkScrollbar.new(f) {|s|
+ command proc{|*args| t.yview(args)}
+ t.yscrollcommand proc{|first,last| s.set first,last}
+ }.pack('side'=>'right', 'fill'=>'y')
+ }.pack('expand'=>'yes', 'fill'=>'both')
+}.pack('expand'=>'yes', 'fill'=>'both')
+
+#
+$tag_center = TkTextTag.new($twind_text,
+ 'justify' =>'center',
+ 'spacing1'=>'5m',
+ 'spacing3'=>'5m' )
+$tag_buttons = TkTextTag.new($twind_text,
+ 'lmargin1'=>'1c',
+ 'lmargin2'=>'1c',
+ 'rmargin' =>'1c',
+ 'spacing1'=>'3m',
+ 'spacing2'=>0,
+ 'spacing3'=>0 )
+
+$twind_text.insert('end', "A text widget can contain other widgets embedded ")
+$twind_text.insert('end', "it. These are called \"embedded windows\", ")
+$twind_text.insert('end', "and they can consist of arbitrary widgets. ")
+$twind_text.insert('end', "For example, here are two embedded button ")
+$twind_text.insert('end', "widgets. You can click on the first button to ")
+TkTextWindow.new($twind_text, 'end',
+ 'window'=>TkButton.new($twind_text) {
+ #text 'ON'
+ text 'Turn On'
+ command proc{textWindOn $twind_text,$twind_buttons}
+ cursor 'top_left_arrow'
+ })
+$twind_text.insert('end', " horizontal scrolling, which also turns off ")
+$twind_text.insert('end', "word wrapping. Or, you can click on the second ")
+$twind_text.insert('end', "button to\n")
+TkTextWindow.new($twind_text, 'end',
+ 'window'=>TkButton.new($twind_text) {
+ #text 'OFF'
+ text 'Turn Off'
+ command proc{textWindOff $twind_text}
+ cursor 'top_left_arrow'
+ })
+
+$twind_text.insert('end', " horizontal scrolling and turn back on word wrapping.\n\n")
+$twind_text.insert('end', "Or, here is another example. If you ")
+TkTextWindow.new($twind_text, 'end',
+ 'window'=>TkButton.new($twind_text) {
+ text 'Click Here'
+ command proc{textWindPlot $twind_text}
+ cursor 'top_left_arrow'
+ })
+$twind_text.insert('end', " a canvas displaying an x-y plot will appear right here.")
+$mark_plot = TkTextMark.new($twind_text, 'insert')
+$mark_plot.gravity='left'
+$twind_text.insert('end', " You can drag the data points around with the mouse, ")
+$twind_text.insert('end', "or you can click here to ")
+TkTextWindow.new($twind_text, 'end',
+ 'window'=>TkButton.new($twind_text) {
+ text 'Delete'
+ command proc{textWindDel $twind_text}
+ cursor 'top_left_arrow'
+ })
+
+$twind_text.insert('end', " the plot again.\n\n")
+$twind_text.insert('end', "You may also find it useful to put embedded windows in ")
+$twind_text.insert('end', "a text without any actual text. In this case the ")
+$twind_text.insert('end', "text widget acts like a geometry manager. For ")
+$twind_text.insert('end', "example, here is a collection of buttons laid out ")
+$twind_text.insert('end', "neatly into rows by the text widget. These buttons ")
+$twind_text.insert('end', "can be used to change the background color of the ")
+$twind_text.insert('end', "text widget (\"Default\" restores the color to ")
+$twind_text.insert('end', "its default). If you click on the button labeled ")
+$twind_text.insert('end', "\"Short\", it changes to a longer string so that ")
+$twind_text.insert('end', "you can see how the text widget automatically ")
+$twind_text.insert('end', "changes the layout. Click on the button again ")
+$twind_text.insert('end', "to restore the short string.\n")
+
+
+TkTextWindow.new($twind_text, 'end',
+ 'window'=>TkButton.new($twind_text) {|b|
+ text 'Default'
+ command proc{embDefBg $twind_text}
+ cursor 'top_left_arrow'
+ $tag_buttons.add('end')
+ },
+ 'padx'=>3 )
+embToggle = TkVariable.new('Short')
+TkTextWindow.new($twind_text, 'end',
+ 'window'=>TkCheckButton.new($twind_text) {
+ textvariable embToggle
+ indicatoron 0
+ variable embToggle
+ onvalue 'A much longer string'
+ offvalue 'Short'
+ cursor 'top_left_arrow'
+ pady 5
+ padx 2
+ },
+ 'padx'=>3,
+ 'pady'=>2 )
+
+[ 'AntiqueWhite3', 'Bisque1', 'Bisque2', 'Bisque3', 'Bisque4',
+ 'SlateBlue3', 'RoyalBlue1', 'SteelBlue2', 'DeepSkyBlue3', 'LightBlue1',
+ 'DarkSlateGray1', 'Aquamarine2', 'DarkSeaGreen2', 'SeaGreen1',
+ 'Yellow1', 'IndianRed1', 'IndianRed2', 'Tan1', 'Tan4'
+].each{|twind_color|
+ TkTextWindow.new($twind_text, 'end',
+ 'window'=>TkButton.new($twind_text) {
+ text twind_color
+ cursor 'top_left_arrow'
+ command proc{$twind_text.bg twind_color}
+ },
+ 'padx'=>3,
+ 'pady'=>2 )
+}
+
+#
+def textWindOn (w,f)
+ if defined? $twind_scroll
+ begin
+ $twind_scroll.destroy
+ rescue
+ end
+ $twind_scroll = nil
+ end
+
+ base = TkWinfo.parent( TkWinfo.parent(w) )
+ $twind_scroll = TkScrollbar.new(base) {|s|
+ orient 'horizontal'
+ command proc{|*args| w.xview(*args)}
+ w.xscrollcommand proc{|first,last| s.set first,last}
+ w.wrap 'none'
+ pack('after'=>f, 'side'=>'bottom', 'fill'=>'x')
+ }
+
+ return nil
+end
+
+def textWindOff (w)
+ if defined? $twind_scroll
+ begin
+ $twind_scroll.destroy
+ rescue
+ end
+ $twind_scroll = nil
+ end
+ w.xscrollcommand ''
+ w.wrap 'word'
+end
+
+def textWindPlot (t)
+ if (defined? $twind_plot) && TkWinfo.exist?($twind_plot)
+ return
+ end
+
+ $twind_plot = TkCanvas.new(t) {
+ relief 'sunken'
+ width 450
+ height 300
+ cursor 'top_left_arrow'
+ }
+
+ font = '-Adobe-Helvetica-Medium-R-Normal--*-180-*-*-*-*-*-*'
+
+ TkcLine.new($twind_plot, 100, 250, 400, 250, 'width'=>2)
+ TkcLine.new($twind_plot, 100, 250, 100, 50, 'width'=>2)
+ TkcText.new($twind_plot, 225, 20,
+ 'text'=>"A Simple Plot", 'font'=>font, 'fill'=>'brown')
+
+ (0..10).each {|i|
+ x = 100 + (i * 30)
+ TkcLine.new($twind_plot, x, 250, x, 245, 'width'=>2)
+ TkcText.new($twind_plot, x, 254,
+ 'text'=>10*i, 'font'=>font, 'anchor'=>'n')
+ }
+ (0..5).each {|i|
+ y = 250 - (i * 40)
+ TkcLine.new($twind_plot, 100, y, 105, y, 'width'=>2)
+ TkcText.new($twind_plot, 96, y,
+ 'text'=>"#{i*50}.0", 'font'=>font, 'anchor'=>'e')
+ }
+
+ for xx, yy in [[12,56],[20,94],[33,98],[32,120],[61,180],[75,160],[98,223]]
+ x = 100 + (3*xx)
+ y = 250 - (4*yy)/5
+ item = TkcOval.new($twind_plot, x-6, y-6, x+6, y+6,
+ 'width'=>1, 'outline'=>'black', 'fill'=>'SkyBlue2')
+ item.addtag 'point'
+ end
+
+ $twind_plot.itembind('point', 'Any-Enter',
+ proc{$twind_plot.itemconfigure 'current', 'fill', 'red'})
+ $twind_plot.itembind('point', 'Any-Leave',
+ proc{$twind_plot.itemconfigure 'current', 'fill', 'SkyBlue2'})
+ $twind_plot.itembind('point', '1',
+ proc{|x,y| embPlotDown $twind_plot,x,y}, "%x %y")
+ $twind_plot.itembind('point', 'ButtonRelease-1',
+ proc{$twind_plot.dtag 'selected'})
+ $twind_plot.bind('B1-Motion',
+ proc{|x,y| embPlotMove $twind_plot,x,y}, "%x %y")
+ while ($twind_text.get($mark_plot) =~ /[ \t\n]/)
+ $twind_text.delete $mark_plot
+ end
+ $twind_text.insert $mark_plot,"\n"
+ TkTextWindow.new($twind_text, $mark_plot, 'window'=>$twind_plot)
+ $tag_center.add $mark_plot
+ $twind_text.insert $mark_plot,"\n"
+end
+
+$embPlot = {'lastX'=>0, 'lastY'=>0}
+
+def embPlotDown (w, x, y)
+ w.dtag 'selected'
+ w.addtag_withtag 'selected', 'current'
+ w.raise 'current'
+ $embPlot['lastX'] = x
+ $embPlot['lastY'] = y
+end
+
+def embPlotMove (w, x, y)
+ w.move 'selected', x - $embPlot['lastX'], y - $embPlot['lastY']
+ $embPlot['lastX'] = x
+ $embPlot['lastY'] = y
+end
+
+def textWindDel (w)
+ if (defined? $twind_text) && TkWinfo.exist?($twind_plot)
+ $twind_text.delete $twind_plot
+ $twind_plot = nil
+ while ($twind_text.get($mark_plot) =~ /[ \t\n]/)
+ $twind_text.delete $mark_plot
+ end
+ $twind_text.insert $mark_plot," "
+ end
+end
+
+def embDefBg (w)
+ w['background'] = w.configinfo('background')[3]
+end
diff --git a/ext/tk/sample/demos-en/vscale.rb b/ext/tk/sample/demos-en/vscale.rb
new file mode 100644
index 0000000000..636b85813b
--- /dev/null
+++ b/ext/tk/sample/demos-en/vscale.rb
@@ -0,0 +1,78 @@
+# vscale.rb
+#
+# This demonstration script shows an example with a vertical scale.
+
+require "tkcanvas"
+
+if defined?($vscale_demo) && $vscale_demo
+ $vscale_demo.destroy
+ $vscale_demo = nil
+end
+
+$vscale_demo = TkToplevel.new {|w|
+ title("Vertical Scale Demonstration")
+ iconname("vscale")
+}
+positionWindow($vscale_demo)
+
+msg = TkLabel.new($vscale_demo) {
+ font $font
+ wraplength '3.5i'
+ justify 'left'
+ text "An arrow and a vertical scale are displayed below. If you click or drag mouse button 1 in the scale, you can change the size of the arrow."
+}
+msg.pack('side'=>'top', 'padx'=>'.5c')
+
+TkFrame.new($vscale_demo) {|frame|
+ TkButton.new(frame) {
+ text 'Dismiss'
+ command proc {
+ tmppath = $vscale_demo
+ $vscale_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'Show Code'
+ command proc { showCode 'vscale' }
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+TkFrame.new($vscale_demo) {|frame|
+ borderwidth 10
+ canvas = TkCanvas.new(frame) {|c|
+ width 50
+ height 50
+ bd 0
+ highlightthickness 0
+ TkcPolygon.new(c, 0, 0, 1, 1, 2, 2) {
+ fill 'SeaGreen3'
+ tags 'poly'
+ }
+ TkcLine.new(c, 0, 0, 1, 1, 2, 2, 0, 0) {
+ fill 'black'
+ tags 'line'
+ }
+ }.pack('side'=>'left', 'anchor'=>'nw', 'fill'=>'y')
+ scale = TkScale.new(frame) {
+ orient 'vertical'
+ length 284
+ from 0
+ to 250
+ command proc{|value| setHeight(canvas, value)}
+ tickinterval 50
+ }.pack('side'=>'left', 'anchor'=>'ne')
+ scale.set 75
+}.pack
+
+
+def setHeight(w, height)
+ height = height + 21
+ y2 = height - 30
+ if y2 < 21
+ y2 = 21
+ end
+ w.coords 'poly',15,20,35,20,35,y2,45,y2,25,height,5,y2,15,y2,15,20
+ w.coords 'line',15,20,35,20,35,y2,45,y2,25,height,5,y2,15,y2,15,20
+end
diff --git a/ext/tk/sample/demos-en/widget b/ext/tk/sample/demos-en/widget
new file mode 100644
index 0000000000..96a24447e1
--- /dev/null
+++ b/ext/tk/sample/demos-en/widget
@@ -0,0 +1,499 @@
+#!/usr/bin/env ruby
+
+# widget --
+# This script demonstrates the various widgets provided by Tk,
+# along with many of the features of the Tk toolkit. This file
+# only contains code to generate the main window for the
+# application, which invokes individual demonstrations. The
+# code for the actual demonstrations is contained in separate
+# ".rb" files is this directory, which are sourced by this script
+# as needed.
+
+require 'tk'
+
+unless /^8\.[1-9]/ =~ Tk::TCL_VERSION && !Tk::JAPANIZED_TK
+ require 'tkencoding'
+end
+
+require 'tkafter'
+
+### $DEBUG=1 ##########
+
+#----------------------------------------------------------------
+# The code below create the main window, consisting of a menu bar
+# and a text widget that explains how to use the program, plus lists
+# all of the demos as hypertext items.
+#----------------------------------------------------------------
+
+# widget demo directory
+$demo_dir = File.dirname($0)
+
+# root
+$root = TkRoot.new{title "Ruby/Tk Widget Demonstration"}
+
+# tk
+$tk_version = Tk::TK_VERSION
+
+# tcl_platform
+$tk_platform = TkVarAccess.new('tcl_platform')
+
+#
+$font = TkFont.new('-*-Helvetica-Medium-R-Normal--*-140-*-*-*-*-*-*', nil)
+
+#
+TkMenubar.new($root,
+ [[['File', 0],
+ ['About ... ', proc{aboutBox}, 0, '<F1>'],
+ '---',
+ ['Quit', proc{exit}, 0, 'Meta-Q']
+ ]]).pack('side'=>'top', 'fill'=>'x')
+$root.bind('F1', proc{aboutBox})
+
+=begin
+TkFrame.new($root){|frame|
+ TkMenubutton.new(frame){|button|
+ m = TkMenu.new(button) {
+ add 'command', 'label'=>'Quit', 'command'=>proc{exit}, 'underline'=>0
+ }
+ menu m
+ text 'File'
+ underline 0
+ }.pack('side'=>'left')
+}.pack('side'=>'top', 'fill'=>'x')
+=end
+
+#
+if $tk_version =~ /^4\.[01]/
+ scr = TkScrollbar.new($root, 'orient'=>'vertical')
+ txt = TkText.new($root) {
+ wrap 'word'
+ width 60
+ height 30
+ font $font
+ setgrid 'yes'
+ yscrollcommand proc{|first,last| scr.set first,last}
+ }
+ scr.command(proc{|*args| txt.yview(*args)})
+ scr.pack('side'=>'right', 'fill'=>'y')
+ txt.pack('expand'=>'yes', 'fill'=>'both')
+else
+ textFrame = TkFrame.new($root)
+ scr = TkScrollbar.new($root, 'orient'=>'vertical',
+ 'highlightthickness'=>0, 'takefocus'=>1) {
+ pack('in'=>textFrame, 'side'=>'right', 'fill'=>'y', 'padx'=>1)
+ }
+ txt = TkText.new($root) {
+ wrap 'word'
+ width 60
+ height 30
+ font $font
+ setgrid 'yes'
+ highlightthickness 0
+ padx 4
+ pady 2
+ takefocus 0
+ yscrollcommand proc{|first,last| scr.set first,last}
+ }
+ scr.command(proc{|*args| txt.yview(*args)})
+# txt.pack('in'=>textFrame, 'expand'=>'yes', 'fill'=>'both', 'padx'=>1)
+ txt.pack('in'=>textFrame, 'expand'=>'yes', 'fill'=>'both')
+# textFrame.pack('expand'=>'yes', 'fill'=>'both', 'padx'=>1, 'pady'=>2)
+ textFrame.pack('expand'=>'yes', 'fill'=>'both')
+
+ statusBar = TkFrame.new($root) {|f|
+ $statusBarLabel = \
+ TkLabel.new(f, 'text'=>" ", 'relief'=>'sunken', 'bd'=>1, 'anchor'=>'w',
+ 'font'=>'-*-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*') \
+ .pack('side'=>'left', 'padx'=>2, 'expand'=>'yes', 'fill'=>'both')
+ TkLabel.new(f, 'width'=>8, 'relief'=>'sunken', 'bd'=>1, 'anchor'=>'w',
+ 'font'=>'-*-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*') \
+ .pack('side'=>'left', 'padx'=>2)
+ }.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>2)
+end
+
+# Create a bunch of tags to use in the text widget, such as those for
+# section titles and demo descriptions. Also define the bindings for
+# tags.
+
+tag_title = TkTextTag.new(txt, 'font'=>'-*-Helvetica-Bold-R-Normal--*-180-*-*-*-*-*-*')
+
+# We put some "space" characters to the left and right of each demo description
+# so that the descriptions are highlighted only when the mouse cursor
+# is right over them (but not when the cursor is to their left or right)
+
+tag_demospace = TkTextTag.new(txt, 'lmargin1'=>'1c', 'lmargin2'=>'1c')
+
+if TkWinfo.depth($root) == '1'
+ tag_demo = TkTextTag.new(txt, 'lmargin1'=>'1c', 'lmargin2'=>'1c',
+ 'underline'=>1)
+ $tag_visited = TkTextTag.new(txt, 'lmargin1'=>'1c', 'lmargin2'=>'1c',
+ 'underline'=>1)
+ tag_hot = TkTextTag.new(txt, 'background'=>'black', 'foreground'=>'white')
+else
+ tag_demo = TkTextTag.new(txt, 'lmargin1'=>'1c', 'lmargin2'=>'1c',
+ 'foreground'=>'blue', 'underline'=>1)
+ $tag_visited = TkTextTag.new(txt, 'lmargin1'=>'1c', 'lmargin2'=>'1c',
+ 'foreground'=>'#303080', 'underline'=>1)
+# tag_hot = TkTextTag.new(txt, 'relief'=>'raised', 'borderwidth'=>1,
+# 'background'=>'SeaGreen3')
+ tag_hot = TkTextTag.new(txt, 'borderwidth'=>1, 'foreground'=>'red')
+end
+
+#tag_demo.bind('Button-1', proc{invoke txt, txt.index('current')})
+tag_demo.bind('ButtonRelease-1',
+ proc{|x,y|invoke txt, txt.index("@#{x},#{y}")}, '%x %y')
+
+lastLine = TkVariable.new("")
+newLine = TkVariable.new("")
+tag_demo.bind('Enter', proc{|x,y|
+ lastLine.value = txt.index("@#{x},#{y} linestart")
+ tag_hot.add(lastLine.value, "#{lastLine.value} lineend")
+ showStatus txt, txt.index("@#{x},#{y}")
+ },
+ '%x %y')
+tag_demo.bind('Leave',
+ proc{
+ tag_hot.remove('1.0','end')
+ txt.configure('cursor','xterm')
+ $statusBarLabel.configure('text'=>"")
+ })
+tag_demo.bind('Motion', proc{|x, y|
+ newLine.value = txt.index("@#{x},#{y} linestart")
+ if newLine.value != lastLine.value
+ tag_hot.remove('1.0','end')
+ lastLine.value = newLine.value
+ if ( txt.tag_names("@#{x},#{y}").find{|t|
+ t.kind_of?(String) && t =~ /^demo-/
+ } )
+ tag_hot.add(lastLine.value,
+ "#{lastLine.value} lineend -1 chars")
+ end
+ end
+ showStatus txt, txt.index("@#{x},#{y}")
+ },
+ '%x %y')
+
+# Create the text for the text widget.
+
+txt.insert('end', "Ruby/Tk Widget Demonstrations\n\n", tag_title)
+txt.insert('end', <<EOT)
+This application provides a front end for several short scripts that \
+demonstrate what you can do with Tk widgets. Each of the numbered \
+lines below describes a demonstration; you can click on it to invoke \
+the demonstration. Once the demonstration window appears, you can \
+click the "See Code" button to see the Ruby/Tk code that created the \
+demonstration. If you wish, you can edit the code and click the \
+"Rerun Demo" button in the code window to reinvoke the demonstration \
+with the modified code.
+
+EOT
+
+txt.insert('end', "Labels, buttons, checkbuttons, and radiobuttons.\n", tag_title)
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "1. Labels (text and bitmaps).\n", tag_demo, "demo-label")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "2. Buttons.\n", tag_demo, "demo-button")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "3. Checkbuttons (select any of a group).\n", tag_demo, "demo-check")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "4. Radiobuttons (select one of a group).\n", tag_demo, "demo-radio")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "5. A 15-puzzle game made out of buttons.\n", tag_demo, "demo-puzzle")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "6. Iconic buttons that use bitmaps.\n", tag_demo, "demo-icon")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "7. Two labels displaying images.\n", tag_demo, "demo-image1")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "8. A simple user interface for viewing images.\n", tag_demo, "demo-image2")
+txt.insert('end', " \n ", tag_demospace)
+
+txt.insert('end', "\n")
+txt.insert('end', "Listboxes\n", tag_title)
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "1. 50 states.\n", tag_demo, "demo-states")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "2. Colors: change the color scheme for the application.\n", "#{tag_demo.id} demo-colors")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "3. A collection of famous sayings.\n", tag_demo, "demo-sayings")
+txt.insert('end', " \n ", tag_demospace)
+
+txt.insert('end', "\n")
+txt.insert('end', "Entries\n", tag_title)
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "1. Without scrollbars.\n", tag_demo, "demo-entry1")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "2. With scrollbars.\n", tag_demo, "demo-entry2")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "3. Simple Rolodex-like form.\n", tag_demo, "demo-form")
+txt.insert('end', " \n ", tag_demospace)
+
+txt.insert('end', "\n")
+txt.insert('end', "Text\n", tag_title)
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "1. Basic editable text.\n", tag_demo, "demo-text")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "2. Text display styles.\n", tag_demo, "demo-style")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "3. Hypertext (tag bindings).\n", tag_demo, "demo-bind")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "4. A text widget with embedded windows.\n", tag_demo, "demo-twind")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "5. A search tool built with a text widget.\n", tag_demo, "demo-search")
+txt.insert('end', " \n ", tag_demospace)
+
+txt.insert('end', "\n")
+txt.insert('end', "Canvases\n", tag_title)
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "1. The canvas item types.\n", tag_demo, "demo-items")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "2. A simple 2-D plot.\n", tag_demo, "demo-plot")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "3. Text items in canvases.\n", tag_demo, "demo-ctext")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "4. An editor for arrowheads on canvas lines.\n", tag_demo, "demo-arrow")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "5. A ruler with adjustable tab stops.\n", tag_demo, "demo-ruler")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "6. A building floor plan.\n", tag_demo, "demo-floor")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "7. A simple scrollable canvas.\n", tag_demo, "demo-cscroll")
+txt.insert('end', " \n ", tag_demospace)
+
+txt.insert('end', "\n")
+txt.insert('end', "Scales\n", tag_title)
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "1. Vertical scale.\n", tag_demo.id, "demo-vscale")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "2. Horizontal scale.\n", tag_demo.id, "demo-hscale")
+txt.insert('end', " \n ", tag_demospace)
+
+txt.insert('end', "\n")
+txt.insert('end', "Menus\n", tag_title)
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "1. Menus and cascades.\n", tag_demo, "demo-menu")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "2. Menubuttons\n", tag_demo, "demo-menubu")
+txt.insert('end', " \n ", tag_demospace)
+
+txt.insert('end', "\n")
+txt.insert('end', "Common Dialogs\n", tag_title)
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "1. Message boxes.\n", tag_demo, "demo-msgbox")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "2. File selection dialog.\n", tag_demo, "demo-filebox")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "3. Color picker.\n", tag_demo, "demo-clrpick")
+txt.insert('end', " \n ", tag_demospace)
+
+txt.insert('end', "\n")
+txt.insert('end', "Miscellaneous\n", tag_title)
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "1. The built-in bitmaps.\n", tag_demo, "demo-bitmap")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "2. A dialog box with a local grab.\n", tag_demo, "demo-dialog1")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "3. A dialog box with a global grab.\n", tag_demo, "demo-dialog2")
+txt.insert('end', " \n ", tag_demospace)
+
+txt.state('disabled')
+scr.focus
+
+# positionWindow --
+# This procedure is invoked by most of the demos to position a
+# new demo window.
+#
+# Arguments:
+# w - The name of the window to position.
+
+def positionWindow(w)
+ w.geometry('+300+300')
+end
+
+# showVars --
+# Displays the values of one or more variables in a window, and
+# updates the display whenever any of the variables changes.
+#
+# Arguments:
+# w - Name of new window to create for display.
+# args - Any number of names of variables.
+
+$showVarsWin = {}
+def showVars (parent, *args)
+ if $showVarsWin[parent.path]
+ begin
+ $showVarsWin[parent.path].destroy
+ rescue
+ end
+ end
+ w = TkToplevel.new(parent) {|w|
+ title "Variable values"
+ TkLabel.new(w) {
+ text "Variable values:"
+ width 20
+ anchor 'center'
+ font '-Adobe-helvetica-medium-r-normal--*-180-*-*-*-*-*-*'
+ }.pack('side'=>'top', 'fill'=>'x')
+ len = 1
+ args.each{|vnam,vbody|
+ len = vnam.to_s.length if vnam.to_s.length > len
+ }
+ args.each{|vnam,vbody|
+ TkFrame.new(w){|f|
+ #TkLabel.new(f, 'text'=>"#{vnam}: ").pack('side'=>'left')
+ TkLabel.new(f, 'text'=>"#{vnam}: ",'width'=>len+2).pack('side'=>'left')
+ TkLabel.new(f, 'textvariable'=>vbody, 'anchor'=>'w')\
+ .pack('side'=>'left', 'expand'=>'yes', 'fill'=>'x')
+ }.pack('side'=>'top', 'anchor'=>'w', 'fill'=>'x')
+ }
+ TkButton.new(w) {
+ text "OK"
+ command proc{w.destroy}
+ }.pack('side'=>'bottom', 'pady'=>2)
+ }
+ $showVarsWin[parent.path] = w
+end
+
+# invoke --
+# This procedure is called when the user clicks on a demo description.
+# It is responsible for invoking the demonstration.
+#
+# Arguments:
+# index - The index of the character that the user clicked on.
+
+def invoke (txt, index)
+ tag = txt.tag_names(index).find{|t| t.kind_of?(String) && t =~ /^demo-/}
+ return unless tag
+ cursor = txt.cget('cursor')
+ txt.cursor('watch')
+ Tk.update
+# eval `cat #{tag[5..-1]}.rb`
+ eval `cat #{[$demo_dir, tag[5..-1]].join(File::Separator)}.rb`
+ Tk.update
+# txt.cursor('xterm')
+ txt.cursor(cursor)
+
+ $tag_visited.add("#{index} linestart +1 chars", "#{index} lineend +1 chars")
+end
+
+# showStatus --
+#
+# Show the name of the demo program in the status bar. This procedure
+# is called when the user moves the cursor over a demo description.
+#
+
+def showStatus (txt, index)
+ tag = txt.tag_names(index).find{|t| t.kind_of?(String) && t =~ /^demo-/}
+ cursor = txt.cget('cursor')
+ unless tag
+ $statusBarLabel.configure('text', " ")
+ newcursor = 'xterm'
+ else
+ demoname = tag[5..-1]
+ $statusBarLabel.configure('text',
+ "Run the \"#{demoname}\" sample program")
+ newcursor = 'hand2'
+ end
+ txt.configure('cursor'=>newcursor) if cursor != newcursor
+end
+
+# showCode --
+# This procedure creates a toplevel window that displays the code for
+# a demonstration and allows it to be edited and reinvoked.
+#
+# Arguments:
+# w - The name of the demonstration's window, which can be
+# used to derive the name of the file containing its code.
+
+def showCode (demo)
+ file = "#{demo}.rb"
+ $code_window = nil unless defined? $code_window
+ if $code_window == nil || TkWinfo.exist?($code_window) == '0'
+ $code_window = TkToplevel.new(nil)
+ f = TkFrame.new($code_window)
+ TkButton.new(f) {
+ text "Dismiss"
+ command proc{
+ $code_window.destroy
+ $code_window = nil
+ }
+ }.pack('side'=>'left', 'expand'=>'yes', 'pady'=>2)
+ TkButton.new(f) {
+ text "Rerun Demo"
+ command proc{eval($code_text.get('1.0','end'))}
+ }.pack('side'=>'left', 'expand'=>'yes', 'pady'=>2)
+# f.pack('side'=>'bottom', 'expand'=>'yes', 'fill'=>'x')
+ f.pack('side'=>'bottom', 'fill'=>'x')
+
+ if $tk_version =~ /^4\.[01]/
+ s = TkScrollbar.new($code_window, 'orient'=>'vertical')
+ $code_text = TkText.new($code_window) {
+ height 40
+ setgrid 'yes'
+ yscrollcommand proc{|first,last| s.set first,last}
+ }
+ s.command(proc{|*args| $code_text.yview(*args)})
+ s.pack('side'=>'right', 'fill'=>'y')
+ $code_text.pack('side'=>'left', 'expand'=>'yes', 'fill'=>'both')
+ else
+ TkFrame.new($code_window) {|f|
+ pack('expand'=>'yes', 'fill'=>'both', 'padx'=>1, 'pady'=>1)
+
+ hs = TkScrollbar.new($code_window, 'highlightthickness'=>0,
+ 'orient'=>'horizontal')
+ vs = TkScrollbar.new($code_window, 'highlightthickness'=>0,
+ 'orient'=>'vertical')
+ $code_text = TkText.new($code_window) {|t|
+ height 40
+ wrap 'word'
+ xscrollcommand proc{|first,last| hs.set first,last}
+ yscrollcommand proc{|first,last| vs.set first,last}
+ setgrid 'yes'
+ highlightthickness 0
+ pady 2
+ padx 3
+ hs.command(proc{|*args| $code_text.xview(*args)})
+ vs.command(proc{|*args| $code_text.yview(*args)})
+ }
+
+ $code_text.grid('in'=>f, 'padx'=>1, 'pady'=>1, 'row'=>0, 'column'=>0,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ vs.grid('in'=>f, 'padx'=>1, 'pady'=>1, 'row'=>0, 'column'=>1,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+# xs.grid('in'=>f, 'padx'=>1, 'pady'=>1, 'row'=>1, 'column'=>0,
+# 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+
+# JKC 2001-07-26: segfaults under 1.7.1 (2001-06-19) [i686-linux]
+ TkGrid.rowconfigure(f, 0, 'weight'=>1, 'minsize'=>0)
+ TkGrid.columnconfigure(f, 0, 'weight'=>1, 'minsize'=>0)
+ }
+ end
+ else
+ $code_window.deiconify
+ $code_window.raise
+ end
+ $code_window.title("Demo code: #{file}")
+ $code_window.iconname(file)
+# fid = open(file, 'r')
+ fid = open([$demo_dir, file].join(File::Separator), 'r')
+ $code_text.delete('1.0', 'end')
+ #$code_text.insert('1.0', `cat #{file}`)
+ $code_text.insert('1.0', fid.read)
+ #$code_mark = TkTextMark.new($code_text, '1.0')
+ #$code_text.set_insert('1.0')
+ TkTextMarkInsert.new($code_text,'1.0')
+ fid.close
+end
+
+# aboutBox
+#
+# Pops up a message box with an "about" message
+#
+def aboutBox
+ Tk.messageBox('icon'=>'info', 'type'=>'ok', 'title'=>'About Widget Demo',
+ 'message'=>"Ruby/Tk widget demonstration Ver.1.0.2\n\n( based on Tk 8.1 Copyright (c) 1996-1997 Sun Microsystems, Inc. )\n\nRunning Version :: Ruby#{VERSION}/Tk#{$tk_version}")
+end
+
+################################
+
+Tk.mainloop
+
diff --git a/ext/tk/sample/demos-jp/README b/ext/tk/sample/demos-jp/README
new file mode 100644
index 0000000000..42bec16c60
--- /dev/null
+++ b/ext/tk/sample/demos-jp/README
@@ -0,0 +1,54 @@
+
+ Ruby/Tk widget-demo
+ version 1.2 ( 2000/04/08 )
+ ±Ê°æ¡÷ÃÎǽ¡¥¶å¹©Âç (nagai@ai.kyutech.ac.jp)
+
+ɸ½àÇÛÉۤΠTcl/Tk ³ÈÄ¥¥Ñ¥Ã¥±¡¼¥¸¤ò¼è¤ê¹þ¤ó¤À Ruby (°Ê²¼ Ruby/Tk ¤È¸Æ¤Ó¤Þ¤¹)
+¤Ç¤Ï¡¤Tk widget ¤òÍѤ¤¤¿ GUI ¤ÎºîÀ®¤ò¹Ô¤¦¤³¤È¤¬¤Ç¤­¤Þ¤¹¡¥¼ÂºÝ¤Ë GUI ¤òºîÀ®
+¤·¤Æ¤¤¤¯¾ì¹ç¤Ë¤ÏÍÍ¡¹¤Ê¼ÂÎ㤬¥µ¥ó¥×¥ë¤È¤·¤ÆÂ¸ºß¤¹¤ë¤ÈÊØÍø¤Ê¤Î¤Ç¤¹¤¬¡¤Ruby/Tk
+¤Ë¤Ï¤½¤Î¤è¤¦¤ÊŬÅö¤Ê¥µ¥ó¥×¥ë¥¹¥¯¥ê¥×¥È½¸¹ç¤Ï¸ºß¤·¤Þ¤»¤ó¤Ç¤·¤¿¡¥¤½¤ì¤ËÂФ·¡¤
+³ÈÄ¥¥Ñ¥Ã¥±¡¼¥¸¤Î¸µ¤Ç¤¢¤ë Tcl/Tk ¤Ë¤Ï¡¤Tk widget ¤òÍѤ¤¤Æ¤É¤Î¤è¤¦¤Ê¤³¤È¤¬¤Ç
+¤­¤ë¤«¤ò¼¨¤¹¤â¤Î¤È¤·¤Æ widget-demo ¤¬Â¸ºß¤·¤ª¤ê¡¤Tcl/Tk ¤òÍѤ¤¤¿ GUI ¤ÎºîÀ®
+¤ò½¬ÆÀ¤¹¤ëºÝ¤ÎÂåɽŪ¥µ¥ó¥×¥ë¤È¤Ê¤Ã¤Æ¤¤¤Þ¤¹¡¥ËÜ¥¢¡¼¥«¥¤¥Ö¤Ï¡¤Ruby/Tk ¤Î½¬ÆÀ¤Î
+¤¿¤á¤ÎÂåɽŪ¤Ê¥µ¥ó¥×¥ë¥¹¥¯¥ê¥×¥È¤È¤¹¤Ù¤¯¡¤Tcl/Tk ¤Î widget-demo ¤ò°Ü¿¢¤·¤¿¤â
+¤Î¤Ç¤¹¡¥
+
+º£²ó¤Î¥Ð¡¼¥¸¥ç¥ó¤Ï ruby-1.4.x ¤ËÂбþ¤È¤·¤Æ¤ª¤­¤Þ¤¹¤¬¡¤µì¥Ð¡¼¥¸¥ç¥ó¤ËÈæ¤Ù¤Æ¡¤
+¤Û¤È¤ó¤ÉÊѹ¹¤Ï¤¢¤ê¤Þ¤»¤ó¡¥ruby-1.1c2 °Ê¾å¤Ê¤éư¤¯¤È»×¤¤¤Þ¤¹¡¥ruby-1.5.x ¤Ë¤Ä
+¤¤¤Æ¤Ï¥Æ¥¹¥È¤·¤Æ¤¤¤Þ¤»¤ó¡¥¤½¤Î¤¿¤á¡¤Èó¸ß´¹¤Î±Æ¶Á¤¬½Ð¤ë¤³¤È¤¬¤¢¤ë¤«¤â¤·¤ì¤Þ¤»
+¤ó¤¬¡¤¤½¤Î¾ì¹ç¤Ç¤â¾¯¤·¤Î½¤Àµ¤Çư¤«¤»¤ë¤È»×¤¤¤Þ¤¹¡¥ÁȤ߹þ¤à Tk ¤Î¥Ð¡¼¥¸¥ç¥ó¤Ï¡¤
+4.2 ¤Ç¤â 8.0 ¤Ç¤â½¤Àµ¤Ê¤¯Æ°¤«¤»¤ë¤Ï¤º¤Ç¤¹¡¥¤¿¤À¤·¡¤ÆüËܸìÈǤǤΰܿ¢¤È¤Ê¤Ã¤Æ
+¤¤¤ë¤¿¤á¡¤ÆüËܸ첽¤µ¤ì¤¿ Tk ¤ò¤´ÍøÍѤ¯¤À¤µ¤¤¡¥¥¹¥¯¥ê¥×¥È¤Î¥Æ¥¹¥È¤Ï¡¤µì¥Ð¡¼¥¸¥ç
+¥ó¤ÎºÝ¤Ë Tk4.2jp ¤È Tk8.0jp ¤Î¾å¤Ç¹Ô¤Ã¤Æ¤¤¤Þ¤¹ (´°àú¤Ë¤Ç¤Ï¤Ê¤¤¤Ç¤¹¤¬)¡¥º£²ó
+¤Î¥Ð¡¼¥¸¥ç¥ó¤Ç¤Ï ruby-1.4.x + Tk8.0jp ¾å¤Ç¤Î´Êñ¤Ê¥Æ¥¹¥È¤·¤«¹Ô¤Ã¤Æ¤¤¤Þ¤»¤ó¤¬¡¤
+½¤Àµ¤È¤¤¤¦¤Û¤É¤Î½¤Àµ¤Ï¤·¤Æ¤¤¤Þ¤»¤ó¤Î¤ÇÌäÂê¤Ï¤Ê¤¤¤È¹Í¤¨¤Æ¤¤¤Þ¤¹¡¥
+
+ËÜ¥¢¡¼¥«¥¤¥Ö¤Ë´Þ¤Þ¤ì¤ë¥¹¥¯¥ê¥×¥È¤Î¿¤¯¤Ï¡¤¸µ¤È¤Ê¤Ã¤Æ¤¤¤ë Tcl/Tk ÈǤËÈæ³ÓŪ¶á
+¤¤¥¹¥¯¥ê¥×¥Èµ­½Ò¤È¤Ê¤ë¤è¤¦¤Ë¤·¤Æ¤¤¤Þ¤¹¡¥¤½¤Î¤¿¤á¡¤Ruby/Tk ¤Î¥µ¥ó¥×¥ë¤È¸À¤¦¤Ë
+¤Ï¡¤¤¢¤Þ¤ê Ruby ¤é¤·¤¯¤Ê¤¤¤È¤â¸À¤¨¤ë¤Ç¤·¤ç¤¦¡¥¤Ë¤â¤«¤«¤ï¤é¤º¡¤¤½¤Î¤è¤¦¤Êµ­½Ò
+¤ò¼è¤Ã¤Æ¤¤¤ëÍýͳ¤Ï¡¤Ruby/Tk ¤Î¥É¥­¥å¥á¥ó¥ÈÉÔ­¤Ë¤¢¤ê¤Þ¤¹¡¥
+
+Tcl/Tk ¤Ë¤ÏŬÅö¤Ê»²¹Í½ñ¤¬²¿ºý¤«Â¸ºß¤·¤Æ¤¤¤Þ¤¹¤«¤é¡¤Ruby/Tk ¥¹¥¯¥ê¥×¥È¤òºîÀ®
+¤¹¤ëºÝ¤Ï¡¤¤½¤Î¤è¤¦¤Ê Tcl/Tk ¤Î»²¹Í½ñ¤Ç¾ðÊó¤òÊ䤤¤Ê¤¬¤éºîÀ®¤¹¤ë¤³¤È¤Ë¤Ê¤ë¤È»×
+¤¤¤Þ¤¹¡¥³Æ widget ¤Î»ÈÍÑÎã¤È¤·¤Æ¡¤Tcl/Tk ¤Î widget-demo ¤ò»²¾È¤¹¤ë¤³¤È¤â¤¢¤ë
+¤Ç¤·¤ç¤¦¡¥Ruby/Tk ÈǤε­½Ò¤ò widget-demo ¤ò Tcl/Tk ÈǤε­½Ò¤Ë¶á¤¤¤â¤Î¤Ë¤·¤Æ
+¤ª¤±¤Ð¡¤¤½¤ÎÂÐÈæ¤Ë¤è¤Ã¤Æ¡¤Ruby/Tk ¤ÎÍý²ò¤òÁá¤á¤ë¤³¤È¤¬¤Ç¤­¤ë¤È¹Í¤¨¤é¤ì¤Þ¤¹¡¥
+°ìö Ruby/Tk ¤Ç¤Î ³Æ widget ¤Î»ÈÍÑÊýË¡¤ò½¬ÆÀ¤·¤Æ¤·¤Þ¤¨¤Ð¡¤Ruby ¤é¤·¤¤¥¹¥¯¥ê
+¥×¥È¤òºîÀ®¤¹¤ë¤³¤È¤ÏÆñ¤·¤¯¤Ê¤¤¤Ç¤·¤ç¤¦¡¥ËÜ¥¢¡¼¥«¥¤¥Ö¤Î¥¹¥¯¥ê¥×¥È¤Ï¡¤Ruby/Tk
+¤òºÇ½é¤Ë½¬ÆÀ¤¹¤ë¤Þ¤Ç¤ÎÆ§Âæ¤È¤·¤ÆÍøÍѤ·¤Æ¤¤¤¿¤À¤±¤ì¤Ð¹¬¤¤¤Ç¤¹¡¥
+
+widget-demo ¤Î°Ü¿¢¤Ë¤¢¤¿¤Ã¤Æ¤Ï¡¤¼¡¤ÎÊý¤Ë¤â°Ü¿¢¤·¤¿¥¹¥¯¥ê¥×¥È¤òÄ󶡤·¤Æ¤¤¤¿¤À
+¤­¤Þ¤·¤¿¡¥¤³¤³¤Ë´¶¼Õ¤Î°Õ¤òɽ¤·¤Þ¤¹¡¥
+
+ ΩÀС÷JAIST (ttate@jaist.ac.jp) ¤µ¤ó
+ Ê¿¾¾¾Í»Ë (hiramatu@cdrom.co.jp) ¤µ¤ó
+
+Ê¿¾¾¤µ¤ó¤Ë¤è¤ë Ruby/Tk ÆþÌç¤Î Web page (http://www.cdrom.co.jp/~hiramatu/)
+¤â Ruby/Tk ¤Î½¬ÆÀ¤ËÍ­ÍѤȻפ¨¤Þ¤¹¤Î¤Ç¡¤¤¼¤Ò¤´»²¾È¤¯¤À¤µ¤¤¡¥
+
+¤Þ¤¿¡¤Á°¶¶ (maebashi@iij.ad.jp) ¤µ¤ó¤ò¤Ï¤¸¤á¤È¤·¤Æ¡¤widget-demo ¤Î°Ü¿¢¤ËºÝ¤·
+¤ÆÉ¬ÍפȤʤä¿ Ruby ¤Î Tk ´ØÏ¢¥é¥¤¥Ö¥é¥ê½¤Àµ¤Ë¤Ä¤¤¤Æ¡¤ÌäÂêÅÀ¡¤¥Ð¥°¤Î»ØÅ¦¤ò¤·
+¤Æ¤¤¤¿¤À¤¤¤¿Êý¡¹¤Ë¤â´¶¼ÕÃפ·¤Þ¤¹¡¥
+
+¤½¤·¤ÆºÇ¸å¤ËºÇÂç¤Î´¶¼Õ¤ò Ruby À߷׼ԤΠ¤Þ¤Ä¤â¤È ¤æ¤­¤Ò¤í (matz@netlab.co.jp)
+¤µ¤ó¤ËÊû¤²¤¿¤¤¤È»×¤¤¤Þ¤¹¡¥
diff --git a/ext/tk/sample/demos-jp/arrow.rb b/ext/tk/sample/demos-jp/arrow.rb
new file mode 100644
index 0000000000..3640798db1
--- /dev/null
+++ b/ext/tk/sample/demos-jp/arrow.rb
@@ -0,0 +1,235 @@
+#
+# arrowhead widget demo (called by 'widget')
+#
+
+# arrowSetup --
+# This method regenerates all the text and graphics in the canvas
+# window. It's called when the canvas is initially created, and also
+# whenever any of the parameters of the arrow head are changed
+# interactively.
+#
+# Arguments:
+# c - Name of the canvas widget.
+
+def arrowSetup(c)
+ v = $demo_arrowInfo
+
+ # Remember the current box, if there is one.
+ tags = c.gettags('current')
+ if tags != []
+ cur = tags.find{|t| t.kind_of?(String) && t =~ /^box[1-3]$/ }
+ else
+ cur = nil
+ end
+
+ # Create the arrow and outline.
+ c.delete('all')
+ TkcLine.new(c, v.x1, v.y, v.x2, v.y,
+ { 'width'=>10 * v.width,
+ 'arrowshape'=>[10*v.a, 10*v.b, 10*v.c],
+ 'arrow'=>'last'
+ }.update(v.bigLineStyle) )
+ xtip = v.x2 - 10*v.b
+ deltaY = 10*v.c + 5*v.width
+ TkcLine.new(c, v.x2, v.y, xtip, v.y + deltaY,
+ v.x2 - 10*v.a, v.y, xtip, v.y - deltaY, v.x2, v.y,
+ 'width'=>2, 'capstyle'=>'round', 'joinstyle'=>'round')
+
+ # Create the boxes for reshaping the line and arrowhead.
+ TkcRectangle.new(c, v.x2-10*v.a-5, v.y-5, v.x2-10*v.a+5, v.y+5,
+ {'tags'=>['box1', $arrowTag_box]}.update(v.boxStyle) )
+ TkcRectangle.new(c, xtip-5, v.y-deltaY-5, xtip+5, v.y-deltaY+5,
+ {'tags'=>['box2', $arrowTag_box]}.update(v.boxStyle) )
+ TkcRectangle.new(c, v.x1-5, v.y-5*v.width-5, v.x1+5, v.y-5*v.width+5,
+ {'tags'=>['box3', $arrowTag_box]}.update(v.boxStyle) )
+ c.itemconfigure cur, v.activeStyle if cur
+
+ # Create three arrows in actual size with the same parameters
+ TkcLine.new(c, v.x2+50, 0, v.x2+50, 1000, 'width'=>2)
+ tmp = v.x2+100
+ TkcLine.new(c, tmp, v.y-125, tmp, v.y-75, 'width'=>v.width,
+ 'arrow'=>'both', 'arrowshape'=>[v.a, v.b, v.c])
+ TkcLine.new(c, tmp-25, v.y, tmp+25, v.y, 'width'=>v.width,
+ 'arrow'=>'both', 'arrowshape'=>[v.a, v.b, v.c])
+ TkcLine.new(c, tmp-25, v.y+75, tmp+25, v.y+125, 'width'=>v.width,
+ 'arrow'=>'both', 'arrowshape'=>[v.a, v.b, v.c])
+
+ # Create a bunch of other arrows and text items showing the
+ # current dimensions.
+ tmp = v.x2+10
+ TkcLine.new(c, tmp, v.y-5*v.width, tmp, v.y-deltaY,
+ 'arrow'=>'both', 'arrowshape'=>v.smallTips)
+ TkcText.new(c, v.x2+15, v.y-deltaY+5*v.c, 'text'=>v.c, 'anchor'=>'w')
+ tmp = v.x1-10
+ TkcLine.new(c, tmp, v.y-5*v.width, tmp, v.y+5*v.width,
+ 'arrow'=>'both', 'arrowshape'=>v.smallTips)
+ TkcText.new(c, v.x1-15, v.y, 'text'=>v.width, 'anchor'=>'e')
+ tmp = v.y+5*v.width+10*v.c+10
+ TkcLine.new(c, v.x2-10*v.a, tmp, v.x2, tmp,
+ 'arrow'=>'both', 'arrowshape'=>v.smallTips)
+ TkcText.new(c, v.x2-5*v.a, tmp+5, 'text'=>v.a, 'anchor'=>'n')
+ tmp = tmp+25
+ TkcLine.new(c, v.x2-10*v.b, tmp, v.x2, tmp,
+ 'arrow'=>'both', 'arrowshape'=>v.smallTips)
+ TkcText.new(c, v.x2-5*v.b, tmp+5, 'text'=>v.b, 'anchor'=>'n')
+
+ TkcText.new(c, v.x1, 310, 'text'=>"'width'=>#{v.width}", 'anchor'=>'w',
+ 'font'=>'-*-Helvetica-Medium-R-Normal--*-180-*-*-*-*-*-*')
+ TkcText.new(c, v.x1, 330,
+ 'text'=>"'arrowshape'=>[#{v.a}, #{v.b}, #{v.c}]", 'anchor'=>'w',
+ 'font'=>'-*-Helvetica-Medium-R-Normal--*-180-*-*-*-*-*-*')
+
+ v.count += 1
+end
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($arrow_demo) && $arrow_demo
+ $arrow_demo.destroy
+ $arrow_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$arrow_demo = TkToplevel.new {|w|
+ title("Arrowhead Editor Demonstration")
+ iconname("arrow")
+ positionWindow(w)
+}
+
+# label À¸À®
+TkLabel.new($arrow_demo, 'font'=>$font, 'wraplength'=>'5i', 'justify'=>'left',
+ 'text'=>"¤³¤Î widget ¤Ç¡¢¥­¥ã¥ó¥Ð¥¹¤Ç»È¤ï¤ì¤ë¥é¥¤¥ó¤Ë¤Ä¤¤¤ÆÍÍ¡¹¤ÊÉý¤äÌð°õ¤ÎƬ¤Î·Á¤ò»î¤·¤Æ¤ß¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£Àþ¤ÎÉý¤äÌð°õ¤Î·Á¤òÊѤ¨¤ë¤Ë¤Ï¡¢³ÈÂ礵¤ì¤¿Ìð°õ¤Ë¤Ä¤¤¤Æ¤¤¤ë 3¤Ä¤Î»Í³Ñ¤ò¥É¥é¥Ã¥°¤·¤Æ¤¯¤À¤µ¤¤¡£±¦Â¦¤ÎÌð°õ¤ÏÉáÄ̤ÎÂ礭¤µ¤Ç¤Î¥µ¥ó¥×¥ë¤ò¼¨¤·¤Æ¤¤¤Þ¤¹¡£²¼¤Î¥Æ¥­¥¹¥È¤Ï¥é¥¤¥ó¥¢¥¤¥Æ¥à¤ËÂФ¹¤ëÀßÄꥪ¥×¥·¥ç¥ó¤Ç¤¹¡£"){
+ pack('side'=>'top')
+}
+
+# frame À¸À®
+$arrow_buttons = TkFrame.new($arrow_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $arrow_demo
+ $arrow_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'arrow'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}
+$arrow_buttons.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# canvas ÀßÄê
+$arrow_canvas = TkCanvas.new($arrow_demo, 'width'=>500, 'height'=>350,
+ 'relief'=>'sunken', 'borderwidth'=>2)
+$arrow_canvas.pack('expand'=>'yes', 'fill'=>'both')
+
+# ÃÍÀßÄê
+unless Struct.const_defined?("ArrowInfo")
+ $demo_arrowInfo = Struct.new("ArrowInfo", :a, :b, :c, :width, :motionProc,
+ :x1, :x2, :y, :smallTips, :count,
+ :bigLineStyle, :boxStyle, :activeStyle).new
+end
+$demo_arrowInfo.a = 8
+$demo_arrowInfo.b = 10
+$demo_arrowInfo.c = 3
+$demo_arrowInfo.width = 2
+$demo_arrowInfo.motionProc = proc{}
+$demo_arrowInfo.x1 = 40
+$demo_arrowInfo.x2 = 350
+$demo_arrowInfo.y = 150
+$demo_arrowInfo.smallTips = [5, 5, 2]
+$demo_arrowInfo.count = 0
+if TkWinfo.depth($arrow_canvas) > 1
+ $demo_arrowInfo.bigLineStyle = {'fill'=>'SkyBlue1'}
+ $demo_arrowInfo.boxStyle = {'fill'=>'', 'outline'=>'black', 'width'=>1}
+ $demo_arrowInfo.activeStyle = {'fill'=>'red', 'outline'=>'black', 'width'=>1}
+else
+ $demo_arrowInfo.bigLineStyle = {'fill'=>'black',
+ 'stipple'=>'@'+[$demo_dir, 'images', 'grey.25'].join(File::Separator)}
+ $demo_arrowInfo.boxStyle = {'fill'=>'', 'outline'=>'black', 'width'=>1}
+ $demo_arrowInfo.activeStyle = {'fill'=>'black','outline'=>'black','width'=>1}
+end
+$arrowTag_box = TkcTag.new($arrow_canvas)
+arrowSetup $arrow_canvas
+$arrowTag_box.bind('Enter', proc{$arrow_canvas.itemconfigure('current', $demo_arrowInfo.activeStyle)})
+$arrowTag_box.bind('Leave', proc{$arrow_canvas.itemconfigure('current', $demo_arrowInfo.boxStyle)})
+$arrowTag_box.bind('B1-Enter', proc{})
+$arrowTag_box.bind('B1-Leave', proc{})
+$arrow_canvas.itembind('box1', '1',
+ proc{$demo_arrowInfo.motionProc \
+ = proc{|x,y| arrowMove1 $arrow_canvas, x, y}})
+$arrow_canvas.itembind('box2', '1',
+ proc{$demo_arrowInfo.motionProc \
+ = proc{|x,y| arrowMove2 $arrow_canvas, x, y}})
+$arrow_canvas.itembind('box3', '1',
+ proc{$demo_arrowInfo.motionProc \
+ = proc{|x,y| arrowMove3 $arrow_canvas, x, y}})
+$arrowTag_box.bind('B1-Motion',
+ proc{|x,y| $demo_arrowInfo.motionProc.call(x,y)}, "%x %y")
+$arrow_canvas.bind('Any-ButtonRelease-1', proc{arrowSetup $arrow_canvas})
+
+# arrowMove1 --
+# This method is called for each mouse motion event on box1 (the
+# one at the vertex of the arrow). It updates the controlling parameters
+# for the line and arrowhead.
+#
+# Arguments:
+# c - The name of the canvas window.
+# x, y - The coordinates of the mouse.
+
+def arrowMove1(c,x,y)
+ v = $demo_arrowInfo
+ newA = (v.x2+5-c.canvasx(x).round)/10
+ newA = 0 if newA < 0
+ newA = 25 if newA > 25
+ if newA != v.a
+ c.move('box1', 10*(v.a-newA), 0)
+ v.a = newA
+ end
+end
+
+# arrowMove2 --
+# This method is called for each mouse motion event on box2 (the
+# one at the trailing tip of the arrowhead). It updates the controlling
+# parameters for the line and arrowhead.
+#
+# Arguments:
+# c - The name of the canvas window.
+# x, y - The coordinates of the mouse.
+
+def arrowMove2(c,x,y)
+ v = $demo_arrowInfo
+ newB = (v.x2+5-c.canvasx(x).round)/10
+ newB = 0 if newB < 0
+ newB = 25 if newB > 25
+ newC = (v.y+5-c.canvasy(y).round-5*v.width)/10
+ newC = 0 if newC < 0
+ newC = 20 if newC > 20
+ if newB != v.b || newC != v.c
+ c.move('box2', 10*(v.b-newB), 10*(v.c-newC))
+ v.b = newB
+ v.c = newC
+ end
+end
+
+# arrowMove3 --
+# This method is called for each mouse motion event on box3 (the
+# one that controls the thickness of the line). It updates the
+# controlling parameters for the line and arrowhead.
+#
+# Arguments:
+# c - The name of the canvas window.
+# x, y - The coordinates of the mouse.
+
+def arrowMove3(c,x,y)
+ v = $demo_arrowInfo
+ newWidth = (v.y+2-c.canvasy(y).round)/5
+ newWidth = 0 if newWidth < 0
+ newWidth = 20 if newWidth > 20
+ if newWidth != v.width
+ c.move('box3', 0, 5*(v.width-newWidth))
+ v.width = newWidth
+ end
+end
+
diff --git a/ext/tk/sample/demos-jp/bind.rb b/ext/tk/sample/demos-jp/bind.rb
new file mode 100644
index 0000000000..1f221a6293
--- /dev/null
+++ b/ext/tk/sample/demos-jp/bind.rb
@@ -0,0 +1,106 @@
+#
+# text (tag bindings) widget demo (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($bind_demo) && $bind_demo
+ $bind_demo.destroy
+ $bind_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$bind_demo = TkToplevel.new {|w|
+ title("Text Demonstration - Tag Bindings")
+ iconname("bind")
+ positionWindow(w)
+}
+
+# frame À¸À®
+TkFrame.new($bind_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $bind_demo
+ $bind_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'bind'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# bind Íѥ᥽¥Ã¥É
+def tag_binding_for_bind_demo(tag, enter_style, leave_style)
+ tag.bind('Any-Enter', proc{tag.configure enter_style})
+ tag.bind('Any-Leave', proc{tag.configure leave_style})
+end
+
+# text À¸À®
+TkText.new($bind_demo){|t|
+ # À¸À®
+ setgrid 'true'
+ width 60
+ height 24
+ font $font
+ wrap 'word'
+ TkScrollbar.new($bind_demo) {|s|
+ pack('side'=>'right', 'fill'=>'y')
+ command proc{|*args| t.yview(*args)}
+ t.yscrollcommand proc{|first,last| s.set first,last}
+ }
+ pack('expand'=>'yes', 'fill'=>'both')
+
+ # ¥¹¥¿¥¤¥ëÀßÄê
+ if TkWinfo.depth($root).to_i > 1
+ tagstyle_bold = {'background'=>'#43ce80', 'relief'=>'raised',
+ 'borderwidth'=>1}
+ tagstyle_normal = {'background'=>'', 'relief'=>'flat'}
+ else
+ tagstyle_bold = {'foreground'=>'white', 'background'=>'black'}
+ tagstyle_normal = {'foreground'=>'', 'background'=>''}
+ end
+
+ # ¥Æ¥­¥¹¥ÈÁÞÆþ
+ insert 'insert', "¥Æ¥­¥¹¥Èwidget¤Îɽ¼¨¥¹¥¿¥¤¥ë¤òÀ©¸æ¤¹¤ë¤Î¤ÈƱ¤¸¥¿¥°¤Î¥á¥«¥Ë¥º¥à¤ò»È¤Ã¤Æ¡¢¥Æ¥­¥¹¥È¤ËTcl¤Î¥³¥Þ¥ó¥É¤ò³ä¤êÅö¤Æ¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£¤³¤ì¤Ë¤è¤ê¡¢¥Þ¥¦¥¹¤ä¥­¡¼¥Ü¡¼¥É¤Î¥¢¥¯¥·¥ç¥ó¤ÇÆÃÄê¤ÎTcl¤Î¥³¥Þ¥ó¥É¤¬¼Â¹Ô¤µ¤ì¤ë¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹¡£Î㤨¤Ð¡¢²¼¤Î¥­¥ã¥ó¥Ð¥¹¤Î¥Ç¥â¥×¥í¥°¥é¥à¤Ë¤Ä¤¤¤Æ¤ÎÀâÌÀʸ¤Ë¤Ï¤½¤Î¤è¤¦¤Ê¥¿¥°¤¬¤Ä¤¤¤Æ¤¤¤Þ¤¹¡£¥Þ¥¦¥¹¤òÀâÌÀʸ¤Î¾å¤Ë»ý¤Ã¤Æ¤¤¤¯¤ÈÀâÌÀʸ¤¬¸÷¤ê¡¢¥Ü¥¿¥ó1¤ò²¡¤¹¤È¤½¤ÎÀâÌÀ¤Î¥Ç¥â¤¬»Ï¤Þ¤ê¤Þ¤¹¡£
+
+"
+ insert('end', '1. ¥­¥ã¥ó¥Ð¥¹ widget ¤Ëºî¤ë¤³¤È¤Î¤Ç¤­¤ë¥¢¥¤¥Æ¥à¤Î¼ïÎàÁ´¤Æ¤Ë´Ø¤¹¤ë¥µ¥ó¥×¥ë¡£', (d1 = TkTextTag.new(t)) )
+ insert('end', "\n\n")
+ insert('end', '2. ´Êñ¤Ê 2¼¡¸µ¤Î¥×¥í¥Ã¥È¡£¥Ç¡¼¥¿¤òɽ¤¹ÅÀ¤òư¤«¤¹¤³¤È¤¬¤Ç¤­¤ë¡£', (d2 = TkTextTag.new(t)) )
+ insert('end', "\n\n")
+ insert('end', '3. ¥Æ¥­¥¹¥È¥¢¥¤¥Æ¥à¤Î¥¢¥ó¥«¡¼¤È¹Ô·¤¨¡£',
+ (d3 = TkTextTag.new(t)) )
+ insert('end', "\n\n")
+ insert('end', '4. ¥é¥¤¥ó¥¢¥¤¥Æ¥à¤Î¤¿¤á¤ÎÌð°õ¤ÎƬ¤Î·Á¤Î¥¨¥Ç¥£¥¿¡£',
+ (d4 = TkTextTag.new(t)) )
+ insert('end', "\n\n")
+ insert('end', '5. ¥¿¥Ö¥¹¥È¥Ã¥×¤òÊѹ¹¤¹¤ë¤¿¤á¤Îµ¡Ç½¤Ä¤­¤Î¥ë¡¼¥é¡¼¡£',
+ (d5 = TkTextTag.new(t)) )
+ insert('end', "\n\n")
+ insert('end',
+ '6. ¥­¥ã¥ó¥Ð¥¹¤¬¤É¤¦¤ä¤Ã¤Æ¥¹¥¯¥í¡¼¥ë¤¹¤ë¤Î¤«¤ò¼¨¤¹¥°¥ê¥Ã¥É¡£',
+ (d6 = TkTextTag.new(t)) )
+
+ # binding
+ [d1, d2, d3, d4, d5, d6].each{|tag|
+ tag_binding_for_bind_demo(tag, tagstyle_bold, tagstyle_normal)
+ }
+ d1.bind('1',
+ proc{eval `cat #{[$demo_dir,'items.rb'].join(File::Separator)}`})
+ d2.bind('1',
+ proc{eval `cat #{[$demo_dir,'plot.rb'].join(File::Separator)}`})
+ d3.bind('1',
+ proc{eval `cat #{[$demo_dir,'ctext.rb'].join(File::Separator)}`})
+ d4.bind('1',
+ proc{eval `cat #{[$demo_dir,'arrow.rb'].join(File::Separator)}`})
+ d5.bind('1',
+ proc{eval `cat #{[$demo_dir,'ruler.rb'].join(File::Separator)}`})
+ d6.bind('1',
+ proc{eval `cat #{[$demo_dir,'cscroll.rb'].join(File::Separator)}`})
+
+ TkTextMarkInsert.new(t, '0.0')
+ configure('state','disabled')
+}
diff --git a/ext/tk/sample/demos-jp/bitmap.rb b/ext/tk/sample/demos-jp/bitmap.rb
new file mode 100644
index 0000000000..a400862881
--- /dev/null
+++ b/ext/tk/sample/demos-jp/bitmap.rb
@@ -0,0 +1,70 @@
+#
+# bitmap widget demo (called by 'widget')
+#
+
+# bitmapRow --
+# Create a row of bitmap items in a window.
+#
+# Arguments:
+# w - The parent window that is to contain the row.
+# args - The names of one or more bitmaps, which will be displayed
+# in a new row across the bottom of w along with their
+# names.
+
+def bitmapRow(w,*args)
+ TkFrame.new(w){|row|
+ pack('side'=>'top', 'fill'=>'both')
+ for bitmap in args
+ TkFrame.new(row){|base|
+ pack('side'=>'left', 'fill'=>'both', 'pady'=>'.25c', 'padx'=>'.25c')
+ TkLabel.new(base, 'text'=>bitmap, 'width'=>9).pack('side'=>'bottom')
+ TkLabel.new(base, 'bitmap'=>bitmap).pack('side'=>'bottom')
+ }
+ end
+ }
+end
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($bitmap_demo) && $bitmap_demo
+ $bitmap_demo.destroy
+ $bitmap_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$bitmap_demo = TkToplevel.new {|w|
+ title("Bitmap Demonstration")
+ iconname("bitmap")
+ positionWindow(w)
+}
+
+# label À¸À®
+TkLabel.new($bitmap_demo,'font'=>$font,'wraplength'=>'4i','justify'=>'left',
+ 'text'=>"¤³¤Î¥¦¥£¥ó¥É¥¦¤Ë¤Ï¡¢Tk ¤ËÁȤ߹þ¤Þ¤ì¤¿¤¹¤Ù¤Æ¤Î¥Ó¥Ã¥È¥Þ¥Ã¥×¤¬¡¢¤½¤ì¤é¤Î̾Á°¤È¶¦¤Ëɽ¼¨¤µ¤ì¤Æ¤¤¤Þ¤¹¡£Tcl ¤Î¥¹¥¯¥ê¥×¥ÈÃæ¤Ç¤Ï¡¢¤½¤ì¤¾¤ì¤Î̾Á°¤òÍѤ¤¤Æ»²¾È¤·¤Þ¤¹¡£"){
+ pack('side'=>'top')
+}
+
+# frame À¸À®
+$bitmap_buttons = TkFrame.new($bitmap_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $bitmap_demo
+ $bitmap_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'bitmap'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}
+$bitmap_buttons.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame ÀßÄê
+TkFrame.new($bitmap_demo){|f|
+ bitmapRow(f,'error','gray25','gray50','hourglass')
+ bitmapRow(f,'info','question','questhead','warning')
+ pack('side'=>'top', 'expand'=>'yes', 'fill'=>'both')
+}
+
diff --git a/ext/tk/sample/demos-jp/browse1 b/ext/tk/sample/demos-jp/browse1
new file mode 100644
index 0000000000..6bb8fb35ca
--- /dev/null
+++ b/ext/tk/sample/demos-jp/browse1
@@ -0,0 +1,63 @@
+#!/usr/local/bin/ruby
+
+# browse --
+# This script generates a directory browser, which lists the working
+# directory and allow you to open files or subdirectories by
+# double-clicking.
+
+require 'tk'
+
+# Create a scrollbar on the right side of the main window and a listbox
+# on the left side.
+
+listbox = TkListbox.new(nil, 'relief'=>'sunken',
+ 'width'=>20, 'height'=>20, 'setgrid'=>'yes') {|l|
+ TkScrollbar.new(nil, 'command'=>proc{|*args| l.yview *args}) {|s|
+ pack('side'=>'right', 'fill'=>'y')
+ l.yscrollcommand(proc{|first,last| s.set(first,last)})
+ }
+
+ pack('side'=>'left', 'fill'=>'both', 'expand'=>'yes')
+}
+
+root = TkRoot.new
+root.minsize(1,1)
+
+# The procedure below is invoked to open a browser on a given file; if the
+# file is a directory then another instance of this program is invoked; if
+# the file is a regular file then the Mx editor is invoked to display
+# the file.
+
+def browse (dir, file)
+ file = dir + File::Separator + file if dir != '.'
+ type = File.ftype(file)
+ if type == 'directory'
+ system($0 + ' ' + file + ' &')
+ else
+ if type == 'file'
+ if ENV['EDITOR']
+ system(ENV['EDITOR'] + ' ' + file + ' &')
+ else
+ system('xedit ' + file + ' &')
+ end
+ else
+ STDOUT.print "\"#{file}\" isn't a directory or regular file"
+ end
+ end
+end
+
+# Fill the listbox with a list of all the files in the directory (run
+# the "ls" command to get that information).
+
+dir = ARGV[0] ? ARGV[0] : '.'
+open("|ls -a #{dir}", 'r'){|fid| fid.readlines}.each{|fname|
+ listbox.insert('end', fname.chomp)
+}
+
+# Set up bindings for the browser.
+
+Tk.bind_all('Control-c', proc{root.destroy})
+listbox.bind('Double-Button-1',
+ proc{TkSelection.get.each{|f| browse dir, f}})
+
+Tk.mainloop
diff --git a/ext/tk/sample/demos-jp/browse2 b/ext/tk/sample/demos-jp/browse2
new file mode 100644
index 0000000000..755afa682c
--- /dev/null
+++ b/ext/tk/sample/demos-jp/browse2
@@ -0,0 +1,82 @@
+#!/usr/local/bin/ruby
+
+# browse --
+# This script generates a directory browser, which lists the working
+# directory and allow you to open files or subdirectories by
+# double-clicking.
+
+require 'tk'
+
+class Browse
+ BROWSE_WIN_COUNTER = TkVariable.new(0)
+
+ def initialize(dir)
+ BROWSE_WIN_COUNTER.value = BROWSE_WIN_COUNTER.to_i + 1
+
+ # create base frame
+ base = TkToplevel.new {
+ minsize(1,1)
+ title('Browse : ' + dir)
+ }
+
+ # Create a scrollbar on the right side of the main window and a listbox
+ # on the left side.
+ list = TkListbox.new(base, 'relief'=>'sunken',
+ 'width'=>20, 'height'=>20, 'setgrid'=>'yes') {|l|
+ TkScrollbar.new(base, 'command'=>proc{|*args| l.yview *args}) {|s|
+ pack('side'=>'right', 'fill'=>'y')
+ l.yscrollcommand(proc{|first,last| s.set(first,last)})
+ }
+
+ pack('side'=>'left', 'fill'=>'both', 'expand'=>'yes')
+
+ # Fill the listbox with a list of all the files in the directory (run
+ # the "ls" command to get that information).
+ open("|ls -a #{dir}", 'r'){|fid| fid.readlines}.each{|fname|
+ l.insert('end', fname.chomp)
+ }
+
+ }
+
+ # Set up bindings for the browser.
+ base.bind('Control-c',
+ proc{
+ base.destroy
+ Browse::BROWSE_WIN_COUNTER.value = \
+ Browse::BROWSE_WIN_COUNTER.to_i - 1})
+ list.bind('Double-Button-1',
+ proc{TkSelection.get.each{|f| self.browse dir, f}})
+ end
+
+ # The method below is invoked to open a browser on a given file; if the
+ # file is a directory then another instance of this program is invoked; if
+ # the file is a regular file then the Mx editor is invoked to display
+ # the file.
+ def browse (dir, file)
+ file = dir + File::Separator + file if dir != '.'
+ type = File.ftype(file)
+ if type == 'directory'
+ Browse.new(file)
+ else
+ if type == 'file'
+ if ENV['EDITOR']
+ system(ENV['EDITOR'] + ' ' + file + ' &')
+ else
+ system('xedit ' + file + ' &')
+ end
+ else
+ STDOUT.print "\"#{file}\" isn't a directory or regular file"
+ end
+ end
+ end
+
+end
+
+Browse.new(ARGV[0] ? ARGV[0] : '.')
+
+TkRoot.new {
+ withdraw
+ Browse::BROWSE_WIN_COUNTER.trace('w', proc{exit if Browse::BROWSE_WIN_COUNTER.to_i == 0})
+}
+
+Tk.mainloop
diff --git a/ext/tk/sample/demos-jp/button.rb b/ext/tk/sample/demos-jp/button.rb
new file mode 100644
index 0000000000..33247261f2
--- /dev/null
+++ b/ext/tk/sample/demos-jp/button.rb
@@ -0,0 +1,80 @@
+#
+# button widget demo (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($button_demo) && $button_demo
+ $button_demo.destroy
+ $button_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$button_demo = TkToplevel.new {|w|
+ title("Button Demonstration")
+ iconname("button")
+ positionWindow(w)
+}
+
+# label À¸À®
+msg = TkLabel.new($button_demo) {
+ font $kanji_font
+ wraplength '4i'
+ justify 'left'
+ text "¥Ü¥¿¥ó¤ò¥¯¥ê¥Ã¥¯¤¹¤ë¤È¡¢¥Ü¥¿¥ó¤ÎÇØ·Ê¿§¤¬¤½¤Î¥Ü¥¿¥ó¤Ë½ñ¤«¤ì¤Æ¤¤¤ë¿§¤Ë¤Ê¤ê¤Þ¤¹¡£¥Ü¥¿¥ó¤«¤é¥Ü¥¿¥ó¤Ø¤Î°Üư¤Ï¥¿¥Ö¤ò²¡¤¹¤³¤È¤Ç¤â²Äǽ¤Ç¤¹¡£¤Þ¤¿¥¹¥Ú¡¼¥¹¤Ç¼Â¹Ô¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£"
+}
+msg.pack('side'=>'top')
+
+# frame À¸À®
+$button_buttons = TkFrame.new($button_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $button_demo
+ $button_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'button'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# button À¸À®
+TkButton.new($button_demo){
+ text "Peach Puff"
+ width 10
+ command proc{
+ $button_demo.configure('bg','PeachPuff1')
+ $button_buttons.configure('bg','PeachPuff1')
+ }
+}.pack('side'=>'top', 'expand'=>'yes', 'pady'=>2)
+
+TkButton.new($button_demo){
+ text "Light Blue"
+ width 10
+ command proc{
+ $button_demo.configure('bg','LightBlue1')
+ $button_buttons.configure('bg','LightBlue1')
+ }
+}.pack('side'=>'top', 'expand'=>'yes', 'pady'=>2)
+
+TkButton.new($button_demo){
+ text "Sea Green"
+ width 10
+ command proc{
+ $button_demo.configure('bg','SeaGreen2')
+ $button_buttons.configure('bg','SeaGreen2')
+ }
+}.pack('side'=>'top', 'expand'=>'yes', 'pady'=>2)
+
+TkButton.new($button_demo){
+ text "Yellow"
+ width 10
+ command proc{
+ $button_demo.configure('bg','Yellow1')
+ $button_buttons.configure('bg','Yellow1')
+ }
+}.pack('side'=>'top', 'expand'=>'yes', 'pady'=>2)
diff --git a/ext/tk/sample/demos-jp/check.rb b/ext/tk/sample/demos-jp/check.rb
new file mode 100644
index 0000000000..50c21fac0e
--- /dev/null
+++ b/ext/tk/sample/demos-jp/check.rb
@@ -0,0 +1,66 @@
+#
+# checkbutton widget demo (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($check_demo) && $check_demo
+ $check_demo.destroy
+ $check_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$check_demo = TkToplevel.new {|w|
+ title("Checkbutton Demonstration")
+ iconname("check")
+ positionWindow(w)
+}
+
+# label À¸À®
+msg = TkLabel.new($check_demo) {
+ font $font
+ wraplength '4i'
+ justify 'left'
+ text "²¼¤Ë¤Ï 3 ¤Ä¤Î¥Á¥§¥Ã¥¯¥Ü¥¿¥ó¤¬É½¼¨¤µ¤ì¤Æ¤¤¤Þ¤¹¡£¥¯¥ê¥Ã¥¯¤¹¤ë¤È¥Ü¥¿¥ó¤ÎÁªÂò¾õÂÖ¤¬ÊѤï¤ê¡¢Tcl ÊÑ¿ô¤Ë¤½¤Î¥Ü¥¿¥ó¤Î¾õÂÖ¤ò¼¨¤¹ÃͤòÀßÄꤷ¤Þ¤¹¡£¸½ºß¤ÎÊÑ¿ô¤ÎÃͤò¸«¤ë¤Ë¤Ï¡ÖÊÑ¿ô»²¾È¡×¥Ü¥¿¥ó¤ò¥¯¥ê¥Ã¥¯¤·¤Æ¤¯¤À¤µ¤¤¡£"
+}
+msg.pack('side'=>'top')
+
+# ÊÑ¿ôÀ¸À®
+wipers = TkVariable.new(0)
+brakes = TkVariable.new(0)
+sober = TkVariable.new(0)
+
+# frame À¸À®
+TkFrame.new($check_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $check_demo
+ $check_demo = nil
+ $showVarsWin[tmppath.path] = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'check'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+
+ TkButton.new(frame) {
+ text 'ÊÑ¿ô»²¾È'
+ command proc{
+ showVars($check_demo,
+ ['wipers', wipers], ['brakes', brakes], ['sober', sober])
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+
+# checkbutton À¸À®
+[ TkCheckButton.new($check_demo, 'text'=>'¥ï¥¤¥Ñ¡¼ OK', 'variable'=>wipers),
+ TkCheckButton.new($check_demo, 'text'=>'¥Ö¥ì¡¼¥­ OK', 'variable'=>brakes),
+ TkCheckButton.new($check_demo, 'text'=>'¥É¥é¥¤¥Ð¡¼ÁÇÌÌ', 'variable'=>sober)
+].each{|w| w.relief('flat'); w.pack('side'=>'top', 'pady'=>2, 'anchor'=>'w')}
+
diff --git a/ext/tk/sample/demos-jp/clrpick.rb b/ext/tk/sample/demos-jp/clrpick.rb
new file mode 100644
index 0000000000..55cfd9c7c6
--- /dev/null
+++ b/ext/tk/sample/demos-jp/clrpick.rb
@@ -0,0 +1,74 @@
+#
+# widget demo prompts the user to select a color (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($clrpick_demo) && $clrpick_demo
+ $clrpick_demo.destroy
+ $clrpick_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$clrpick_demo = TkToplevel.new {|w|
+ title("Color Selection Dialogs")
+ iconname("colors")
+ positionWindow(w)
+}
+
+# label À¸À®
+TkLabel.new($clrpick_demo,'font'=>$font,'wraplength'=>'4i','justify'=>'left',
+ 'text'=>"°Ê²¼¤Î¥Ü¥¿¥ó¤ò²¡¤·¤Æ¡¢¤³¤Î¥¦¥£¥ó¥É¥¦¾å¤Ë¤¢¤ë¥¦¥£¥¸¥§¥Ã¥È¤ÎÁ°·Ê¿§¤ÈÇØ·Ê¿§¤òÁªÂò¤·¤Æ²¼¤µ¤¤¡£").pack('side'=>'top')
+
+# frame À¸À®
+TkFrame.new($clrpick_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $clrpick_demo
+ $clrpick_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'clrpick'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# button À¸À®
+TkButton.new($clrpick_demo, 'text'=>'ÇØ·Ê¿§¤òÀßÄê ...') {|b|
+ command(proc{setColor $clrpick_demo, b, 'background',
+ ['background', 'highlightbackground']})
+ pack('side'=>'top', 'anchor'=>'c', 'pady'=>'2m')
+}
+
+TkButton.new($clrpick_demo, 'text'=>'Á°·Ê¿§¤òÀßÄê ...') {|b|
+ command(proc{setColor $clrpick_demo, b, 'foreground', ['foreground']})
+ pack('side'=>'top', 'anchor'=>'c', 'pady'=>'2m')
+}
+
+def setColor(w,button,name,options)
+ w.grab
+ initialColor = button[name]
+ color = Tk.chooseColor('title'=>"Choose a #{name} color", 'parent'=>w,
+ 'initialcolor'=>initialColor)
+ if color != ""
+ setColor_helper(w,options,color)
+ end
+
+ w.grab('release')
+end
+
+def setColor_helper(w, options, color)
+ options.each{|opt|
+ begin
+ w[opt] = color
+ rescue
+ end
+ }
+ TkWinfo.children(w).each{|child|
+ setColor_helper child, options, color
+ }
+end
+
diff --git a/ext/tk/sample/demos-jp/colors.rb b/ext/tk/sample/demos-jp/colors.rb
new file mode 100644
index 0000000000..f9a5983e75
--- /dev/null
+++ b/ext/tk/sample/demos-jp/colors.rb
@@ -0,0 +1,143 @@
+#
+# listbox widget demo 'colors' (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($colors_demo) && $colors_demo
+ $colors_demo.destroy
+ $colors_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$colors_demo = TkToplevel.new {|w|
+ title("Listbox Demonstration (colors)")
+ iconname("colors")
+ positionWindow(w)
+}
+
+# label À¸À®
+msg = TkLabel.new($colors_demo) {
+ font $font
+ wraplength '4i'
+ justify 'left'
+ text "²¼¤Ë¤Ï¿§¤Î̾Á°¤¬Æþ¤Ã¤¿¥¹¥¯¥í¡¼¥ë¥Ð¡¼ÉդΥꥹ¥È¥Ü¥Ã¥¯¥¹¤¬É½¼¨¤µ¤ì¤Æ¤¤¤Þ¤¹¡£¥ê¥¹¥È¤ò¥¹¥¯¥í¡¼¥ë¤µ¤»¤ë¤Î¤Ï¥¹¥¯¥í¡¼¥ë¥Ð¡¼¤Ç¤â¤Ç¤­¤Þ¤¹¤·¡¢¥ê¥¹¥È¥Ü¥Ã¥¯¥¹¤ÎÃæ¤Ç¥Þ¥¦¥¹¤Î¥Ü¥¿¥ó2(Ãæ¥Ü¥¿¥ó)¤ò²¡¤·¤¿¤Þ¤Þ¥É¥é¥Ã¥°¤·¤Æ¤â¤Ç¤­¤Þ¤¹¡£¤¢¤ë¿§¤ò¥Ü¥¿¥ó1(º¸¥Ü¥¿¥ó)¤Ç¥À¥Ö¥ë¥¯¥ê¥Ã¥¯¤¹¤ë¤È¥¢¥×¥ê¥±¡¼¥·¥ç¥óÁ´ÂΤ¬¤½¤Î¿§¤Ë¤Ê¤ê¤Þ¤¹¡£"
+}
+msg.pack('side'=>'top')
+
+# frame À¸À®
+TkFrame.new($colors_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $colors_demo
+ $colors_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'colors'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame À¸À®
+colors_lbox = nil
+TkFrame.new($colors_demo, 'borderwidth'=>10) {|w|
+ s = TkScrollbar.new(w)
+ colors_lbox = TkListbox.new(w) {
+ setgrid 1
+ width 10
+ height 12
+ yscrollcommand proc{|first,last| s.set first,last}
+ }
+ s.command(proc{|*args| colors_lbox.yview(*args)})
+ s.pack('side'=>'right', 'fill'=>'y')
+ colors_lbox.pack('side'=>'left', 'expand'=>1, 'fill'=>'both')
+}.pack('side'=>'top', 'expand'=>'yes', 'fill'=>'y')
+
+colors_lbox.bind('Double-1', proc{TkPalette.setPalette TkSelection.get})
+
+ins_data = [
+ 'gray60','gray70','gray80','gray85','gray90','gray95',
+ 'snow1','snow2','snow3','snow4','seashell1','seashell2',
+ 'seashell3','seashell4','AntiqueWhite1','AntiqueWhite2',
+ 'AntiqueWhite3','AntiqueWhite4','bisque1','bisque2',
+ 'bisque3','bisque4','PeachPuff1','PeachPuff2',
+ 'PeachPuff3','PeachPuff4','NavajoWhite1','NavajoWhite2',
+ 'NavajoWhite3','NavajoWhite4','LemonChiffon1',
+ 'LemonChiffon2','LemonChiffon3','LemonChiffon4',
+ 'cornsilk1','cornsilk2','cornsilk3','cornsilk4',
+ 'ivory1','ivory2','ivory3','ivory4','honeydew1',
+ 'honeydew2','honeydew3','honeydew4','LavenderBlush1',
+ 'LavenderBlush2','LavenderBlush3','LavenderBlush4',
+ 'MistyRose1','MistyRose2','MistyRose3','MistyRose4',
+ 'azure1','azure2','azure3','azure4','SlateBlue1',
+ 'SlateBlue2','SlateBlue3','SlateBlue4','RoyalBlue1',
+ 'RoyalBlue2','RoyalBlue3','RoyalBlue4','blue1','blue2',
+ 'blue3','blue4','DodgerBlue1','DodgerBlue2',
+ 'DodgerBlue3','DodgerBlue4','SteelBlue1','SteelBlue2',
+ 'SteelBlue3','SteelBlue4','DeepSkyBlue1','DeepSkyBlue2',
+ 'DeepSkyBlue3','DeepSkyBlue4','SkyBlue1','SkyBlue2',
+ 'SkyBlue3','SkyBlue4','LightSkyBlue1','LightSkyBlue2',
+ 'LightSkyBlue3','LightSkyBlue4','SlateGray1',
+ 'SlateGray2','SlateGray3','SlateGray4',
+ 'LightSteelBlue1','LightSteelBlue2','LightSteelBlue3',
+ 'LightSteelBlue4','LightBlue1','LightBlue2',
+ 'LightBlue3','LightBlue4','LightCyan1','LightCyan2',
+ 'LightCyan3','LightCyan4','PaleTurquoise1',
+ 'PaleTurquoise2','PaleTurquoise3','PaleTurquoise4',
+ 'CadetBlue1','CadetBlue2','CadetBlue3','CadetBlue4',
+ 'turquoise1','turquoise2','turquoise3','turquoise4',
+ 'cyan1','cyan2','cyan3','cyan4','DarkSlateGray1',
+ 'DarkSlateGray2','DarkSlateGray3','DarkSlateGray4',
+ 'aquamarine1','aquamarine2','aquamarine3','aquamarine4',
+ 'DarkSeaGreen1','DarkSeaGreen2','DarkSeaGreen3',
+ 'DarkSeaGreen4','SeaGreen1','SeaGreen2','SeaGreen3',
+ 'SeaGreen4','PaleGreen1','PaleGreen2','PaleGreen3',
+ 'PaleGreen4','SpringGreen1','SpringGreen2',
+ 'SpringGreen3','SpringGreen4','green1','green2',
+ 'green3','green4','chartreuse1','chartreuse2',
+ 'chartreuse3','chartreuse4','OliveDrab1','OliveDrab2',
+ 'OliveDrab3','OliveDrab4','DarkOliveGreen1',
+ 'DarkOliveGreen2','DarkOliveGreen3','DarkOliveGreen4',
+ 'khaki1','khaki2','khaki3','khaki4','LightGoldenrod1',
+ 'LightGoldenrod2','LightGoldenrod3','LightGoldenrod4',
+ 'LightYellow1','LightYellow2','LightYellow3',
+ 'LightYellow4','yellow1','yellow2','yellow3','yellow4',
+ 'gold1','gold2','gold3','gold4','goldenrod1',
+ 'goldenrod2','goldenrod3','goldenrod4','DarkGoldenrod1',
+ 'DarkGoldenrod2','DarkGoldenrod3','DarkGoldenrod4',
+ 'RosyBrown1','RosyBrown2','RosyBrown3','RosyBrown4',
+ 'IndianRed1','IndianRed2','IndianRed3','IndianRed4',
+ 'sienna1','sienna2','sienna3','sienna4','burlywood1',
+ 'burlywood2','burlywood3','burlywood4','wheat1',
+ 'wheat2','wheat3','wheat4','tan1','tan2','tan3','tan4',
+ 'chocolate1','chocolate2','chocolate3','chocolate4',
+ 'firebrick1','firebrick2','firebrick3','firebrick4',
+ 'brown1','brown2','brown3','brown4','salmon1','salmon2',
+ 'salmon3','salmon4','LightSalmon1','LightSalmon2',
+ 'LightSalmon3','LightSalmon4','orange1','orange2',
+ 'orange3','orange4','DarkOrange1','DarkOrange2',
+ 'DarkOrange3','DarkOrange4','coral1','coral2','coral3',
+ 'coral4','tomato1','tomato2','tomato3','tomato4',
+ 'OrangeRed1','OrangeRed2','OrangeRed3','OrangeRed4',
+ 'red1','red2','red3','red4','DeepPink1','DeepPink2',
+ 'DeepPink3','DeepPink4','HotPink1','HotPink2',
+ 'HotPink3','HotPink4','pink1','pink2','pink3','pink4',
+ 'LightPink1','LightPink2','LightPink3','LightPink4',
+ 'PaleVioletRed1','PaleVioletRed2','PaleVioletRed3',
+ 'PaleVioletRed4','maroon1','maroon2','maroon3',
+ 'maroon4','VioletRed1','VioletRed2','VioletRed3',
+ 'VioletRed4','magenta1','magenta2','magenta3',
+ 'magenta4','orchid1','orchid2','orchid3','orchid4',
+ 'plum1','plum2','plum3','plum4','MediumOrchid1',
+ 'MediumOrchid2','MediumOrchid3','MediumOrchid4',
+ 'DarkOrchid1','DarkOrchid2','DarkOrchid3',
+ 'DarkOrchid4','purple1','purple2','purple3','purple4',
+ 'MediumPurple1','MediumPurple2','MediumPurple3',
+ 'MediumPurple4','thistle1','thistle2','thistle3', 'thistle4'
+]
+
+colors_lbox.insert(0, *ins_data)
diff --git a/ext/tk/sample/demos-jp/cscroll.rb b/ext/tk/sample/demos-jp/cscroll.rb
new file mode 100644
index 0000000000..7f5ffa2f02
--- /dev/null
+++ b/ext/tk/sample/demos-jp/cscroll.rb
@@ -0,0 +1,130 @@
+#
+# simple scrollable canvas widget demo (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($cscroll_demo) && $cscroll_demo
+ $cscroll_demo.destroy
+ $cscroll_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$cscroll_demo = TkToplevel.new {|w|
+ title("Scrollable Canvas Demonstration")
+ iconname("cscroll")
+ positionWindow(w)
+}
+
+# label À¸À®
+TkLabel.new($cscroll_demo, 'font'=>$font, 'wraplength'=>'4i',
+ 'justify'=>'left', 'text'=>"¤³¤Î¥¦¥£¥ó¥É¥¦¤Ë¤Ï¥¹¥¯¥í¡¼¥ë¥Ð¡¼¤ä¥Þ¥¦¥¹¤Î¥Ü¥¿¥ó2 ¤Ç¥¹¥¯¥í¡¼¥ë¤Ç¤­¤ë¥­¥ã¥ó¥Ð¥¹ widget ¤¬É½¼¨¤µ¤ì¤Æ¤¤¤Þ¤¹¡£»Í³Ñ¤Î¾å¤Ç¥Ü¥¿¥ó1 ¤ò¥¯¥ê¥Ã¥¯¤¹¤ë¤È¡¢¤½¤Î¥¤¥ó¥Ç¥Ã¥¯¥¹¤¬É¸½à½ÐÎϤ˽ÐÎϤµ¤ì¤Þ¤¹¡£"){
+ pack('side'=>'top')
+}
+
+# frame À¸À®
+$cscroll_buttons = TkFrame.new($cscroll_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $cscroll_demo
+ $cscroll_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'cscroll'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}
+$cscroll_buttons.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame ÀßÄê
+unless $tk_version =~ /^4\.[01]/
+ $cscroll_grid = TkFrame.new($cscroll_demo) {
+ pack('expand'=>'yes', 'fill'=>'both', 'padx'=>1, 'pady'=>1)
+ }
+ TkGrid.rowconfigure($cscroll_grid, 0, 'weight'=>1, 'minsize'=>0)
+ TkGrid.columnconfigure($cscroll_grid, 0, 'weight'=>1, 'minsize'=>0)
+end
+
+# canvas ÀßÄê
+$cscroll_canvas = TkCanvas.new($cscroll_demo,
+ 'relief'=>'sunken', 'borderwidth'=>2,
+ 'scrollregion'=>['-11c', '-11c', '50c', '20c']
+ ) {|c|
+ if $tk_version =~ /^4\.[01]/
+ pack('expand'=>'yes', 'fill'=>'both')
+ else
+ grid('in'=>$cscroll_grid, 'padx'=>1, 'pady'=>1, 'row'=>0, 'column'=>0,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ end
+
+ TkScrollbar.new($cscroll_demo, 'command'=>proc{|*args| c.yview(*args)}) {|vs|
+ c.yscrollcommand(proc{|first,last| vs.set first,last})
+ if $tk_version =~ /^4\.[01]/
+ pack('side'=>'right', 'fill'=>'y')
+ else
+ grid('in'=>$cscroll_grid, 'padx'=>1, 'pady'=>1, 'row'=>0, 'column'=>1,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ end
+ }
+
+ TkScrollbar.new($cscroll_demo, 'orient'=>'horiz',
+ 'command'=>proc{|*args| c.xview(*args)}) {|hs|
+ c.xscrollcommand(proc{|first,last| hs.set first,last})
+ if $tk_version =~ /^4\.[01]/
+ pack('side'=>'bottom', 'fill'=>'x')
+ else
+ grid('in'=>$cscroll_grid, 'padx'=>1, 'pady'=>1, 'row'=>1, 'column'=>0,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ end
+ }
+}
+
+bg = $cscroll_canvas.configinfo('bg')[4]
+(0..19).each{|i|
+ x = -10+3*i
+ y = -10
+ (0..9).each{|j|
+ TkcRectangle.new($cscroll_canvas, "#{x}c", "#{y}c", "#{x+2}c", "#{y+2}c",
+ 'outline'=>'black', 'fill'=>bg, 'tags'=>'rect')
+ TkcText.new($cscroll_canvas, "#{x+1}c", "#{y+1}c",
+ 'text'=>"#{i},#{j}", 'anchor'=>'center', 'tags'=>'text')
+ y += 3
+ }
+}
+
+$cscroll_canvas.itembind('all', 'Any-Enter', proc{scrollEnter $cscroll_canvas})
+$cscroll_canvas.itembind('all', 'Any-Leave', proc{scrollLeave $cscroll_canvas})
+$cscroll_canvas.itembind('all', '1', proc{scrollButton $cscroll_canvas})
+$cscroll_canvas.itembind('all', 'Any-Enter', proc{scrollEnter $cscroll_canvas})
+$cscroll_canvas.bind('2', proc{|x,y| $cscroll_canvas.scan_mark(x,y)}, '%x %y')
+$cscroll_canvas.bind('B2-Motion',
+ proc{|x,y| $cscroll_canvas.scan_dragto(x,y)}, '%x %y')
+
+def scrollEnter(c)
+ id = c.find_withtag('current')[0].id
+ id -= 1 if c.gettags('current').include?('text')
+ $oldFill = c.itemconfiginfo(id, 'fill')[4]
+ if TkWinfo.depth(c) > 1
+ c.itemconfigure(id, 'fill'=>'SeaGreen1')
+ else
+ c.itemconfigure(id, 'fill'=>'black')
+ c.itemconfigure(id+1, 'fill'=>'white')
+ end
+end
+
+def scrollLeave(c)
+ id = c.find_withtag('current')[0].id
+ id -= 1 if c.gettags('current').include?('text')
+ c.itemconfigure(id, 'fill'=>$oldFill)
+ c.itemconfigure(id+1, 'fill'=>'black')
+end
+
+def scrollButton(c)
+ id = c.find_withtag('current')[0].id
+ id += 1 unless c.gettags('current').include?('text')
+ print "You buttoned at #{c.itemconfiginfo(id,'text')[4]}\n"
+end
+
diff --git a/ext/tk/sample/demos-jp/ctext.rb b/ext/tk/sample/demos-jp/ctext.rb
new file mode 100644
index 0000000000..62b02b3972
--- /dev/null
+++ b/ext/tk/sample/demos-jp/ctext.rb
@@ -0,0 +1,181 @@
+#
+# Canvas Text widget demo (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($ctext_demo) && $ctext_demo
+ $ctext_demo.destroy
+ $ctext_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$ctext_demo = TkToplevel.new {|w|
+ title("Canvas Text Demonstration")
+ iconname("Text")
+ positionWindow(w)
+}
+
+# label À¸À®
+TkLabel.new($ctext_demo, 'font'=>$font, 'wraplength'=>'5i', 'justify'=>'left',
+ 'text'=>"¤³¤Î¥¦¥£¥ó¥É¥¦¤Ë¤Ï¥­¥ã¥ó¥Ð¥¹widget¤Î¥Æ¥­¥¹¥Èµ¡Ç½¤ò¥Ç¥â¤¹¤ë¤¿¤á¤Î¥Æ¥­¥¹¥Èʸ»úÎó¤¬É½¼¨¤µ¤ì¤Æ¤¤¤Þ¤¹¡£¥Þ¥¦¥¹¤ò»Í³Ñ¤ÎÃæ¤Ë»ý¤Ã¤Æ¤¤¤­¡¢¥¯¥ê¥Ã¥¯¤¹¤ë¤È°ÌÃÖ¤®¤áÍѤÎÅÀ¤«¤é¤ÎÁêÂаÌÃÖ¤òÊѤ¨¤¿¤ê¡¢¹Ô·¤¨¤òÊѤ¨¤¿¤ê¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£¤Þ¤¿°Ê²¼¤Î¤è¤¦¤ÊÊÔ½¸¤Î¤¿¤á¤Î´Êñ¤Ê¥Ð¥¤¥ó¥Ç¥£¥ó¥°¤ò¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤Þ¤¹¡£
+
+ 1. ¥Þ¥¦¥¹¤ò»ý¤Ã¤Æ¤¤¤­¡¢¥¯¥ê¥Ã¥¯¤·¡¢ÆþÎϤǤ­¤Þ¤¹¡£
+ 2. ¥Ü¥¿¥ó1¤ÇÁªÂò¤Ç¤­¤Þ¤¹¡£
+ 3. ¥Þ¥¦¥¹¤Î°ÌÃ֤˥ܥ¿¥ó2¤ÇÁªÂò¤·¤¿¥Æ¥­¥¹¥È¤ò¥³¥Ô¡¼¤Ç¤­¤Þ¤¹¡£
+ 4.¥Ð¥Ã¥¯¥¹¥Ú¡¼¥¹¤ò¥³¥ó¥È¥í¡¼¥ë-H¤ÇÁÞÆþ¥«¡¼¥½¥ë¤ÎľÁ°¤Îʸ»ú¤òºï½ü¤·¤Þ¤¹¡£
+ 5. Delete¥­¡¼¤ÏÁÞÆþ¥«¡¼¥½¥ë¤Îľ¸å¤Îʸ»ú¤òºï½ü¤·¤Þ¤¹¡£"){
+ pack('side'=>'top')
+}
+
+# frame À¸À®
+$ctext_buttons = TkFrame.new($ctext_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $ctext_demo
+ $ctext_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'ctext'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}
+$ctext_buttons.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# canvas À¸À®
+$ctext_canvas = TkCanvas.new($ctext_demo, 'relief'=>'flat',
+ 'borderwidth'=>0, 'width'=>500, 'height'=>350)
+$ctext_canvas.pack('side'=>'top', 'expand'=>'yes', 'fill'=>'both')
+
+# font ÀßÄê
+textFont = '-*-Helvetica-Medium-R-Normal--*-240-*-*-*-*-*-*'
+
+# canvas ÀßÄê
+TkcRectangle.new($ctext_canvas, 245, 195, 255, 205,
+ 'outline'=>'black', 'fill'=>'red')
+
+$ctag_text = TkcTag.new($ctext_canvas)
+$ctag_text.withtag(TkcText.new($ctext_canvas, 250, 200,
+ 'text'=>"¤³¤ì¤Ï¥­¥ã¥ó¥Ð¥¹widget¤Î¥Æ¥­¥¹¥Èµ¡Ç½¤ò¥Ç¥â¤¹¤ë¤¿¤á¤Îʸ»úÎó¤Ç¤¹¡£¥Þ¥¦¥¹¤ò»ý¤Ã¤Æ¤¤¤­¡¢¥¯¥ê¥Ã¥¯¤·¤ÆÆþÎϤǤ­¤Þ¤¹¡£ÁªÂò¤·¤Æ¥³¥ó¥È¥í¡¼¥ë-D¤Ç¾Ãµî¤¹¤ë¤³¤È¤â¤Ç¤­¤Þ¤¹¡£",
+ 'width'=>440, 'anchor'=>'n',
+ 'font'=>'-*-Helvetica-Medium-R-Normal--*-240-*-*-*-*-*-*',
+ 'kanjifont'=>'-*--24-*-jisx0208.1983-0',
+ 'justify'=>'left') )
+
+$ctag_text.bind('1', proc{|x,y| textB1Press $ctext_canvas,x,y}, "%x %y")
+$ctag_text.bind('B1-Motion', proc{|x,y| textB1Move $ctext_canvas,x,y}, "%x %y")
+$ctag_text.bind('Shift-1',
+ proc{|x,y| $ctext_canvas.seleect_adjust 'current', "@#{x},#{y}"},
+ "%x %y")
+$ctag_text.bind('Shift-B1-Motion',
+ proc{|x,y| textB1Move $ctext_canvas,x,y}, "%x %y")
+$ctag_text.bind('KeyPress', proc{|a| textInsert $ctext_canvas,a}, "%A")
+$ctag_text.bind('Return', proc{textInsert $ctext_canvas,"\n"})
+$ctag_text.bind('Control-h', proc{textBs $ctext_canvas})
+$ctag_text.bind('BackSpace', proc{textBs $ctext_canvas})
+$ctag_text.bind('Delete', proc{textDel $ctext_canvas})
+$ctag_text.bind('2', proc{|x,y| textPaste $ctext_canvas, "@#{x},#{y}"},
+ "%x %y")
+
+# Next, create some items that allow the text's anchor position
+# to be edited.
+
+def mkTextConfig(w,x,y,option,value,color)
+ item = TkcRectangle.new(w, x, y, x+30, y+30,
+ 'outline'=>'black', 'fill'=>color, 'width'=>1)
+ item.bind('1', proc{$ctag_text.configure option, value})
+ w.addtag_withtag('config', item)
+end
+
+x = 50
+y = 50
+color = 'LightSkyBlue1'
+mkTextConfig $ctext_canvas, x, y, 'anchor', 'se', color
+mkTextConfig $ctext_canvas, x+30, y, 'anchor', 's', color
+mkTextConfig $ctext_canvas, x+60, y, 'anchor', 'sw', color
+mkTextConfig $ctext_canvas, x, y+30, 'anchor', 'e', color
+mkTextConfig $ctext_canvas, x+30, y+30, 'anchor', 'center', color
+mkTextConfig $ctext_canvas, x+60, y+30, 'anchor', 'w', color
+mkTextConfig $ctext_canvas, x, y+60, 'anchor', 'ne', color
+mkTextConfig $ctext_canvas, x+30, y+60, 'anchor', 'n', color
+mkTextConfig $ctext_canvas, x+60, y+60, 'anchor', 'nw', color
+item = TkcRectangle.new($ctext_canvas, x+40, y+40, x+50, y+50,
+ 'outline'=>'black', 'fill'=>'red')
+item.bind('1', proc{$ctag_text.configure 'anchor', 'center'})
+TkcText.new($ctext_canvas, x+45, y-5, 'text'=>'Text Position', 'anchor'=>'s',
+ 'font'=>'-*-times-medium-r-normal--*-240-*-*-*-*-*-*',
+ 'fill'=>'brown')
+
+# Lastly, create some items that allow the text's justification to be
+# changed.
+
+x = 350
+y = 50
+color = 'SeaGreen2'
+mkTextConfig $ctext_canvas, x, y, 'justify', 'left', color
+mkTextConfig $ctext_canvas, x+30, y, 'justify', 'center', color
+mkTextConfig $ctext_canvas, x+60, y, 'justify', 'right', color
+TkcText.new($ctext_canvas, x+45, y-5, 'text'=>'Justification', 'anchor'=>'s',
+ 'font'=>'-*-times-medium-r-normal--*-240-*-*-*-*-*-*',
+ 'fill'=>'brown')
+
+$ctext_canvas.itembind('config', 'Enter', proc{textEnter $ctext_canvas})
+$ctext_canvas.itembind('config', 'Leave',
+ proc{$ctext_canvas\
+ .itemconfigure('current',
+ 'fill'=>$textConfigFill)})
+
+$textConfigFill = ''
+
+def textEnter(w)
+ $textConfigFill = (w.itemconfiginfo 'current', 'fill')[4]
+ w.itemconfigure 'current', 'fill', 'black'
+end
+
+def textInsert(w, string)
+ return if string == ""
+ begin
+ $ctag_text.dchars 'sel.first', 'sel.last'
+ rescue
+ end
+ $ctag_text.insert 'insert', string
+end
+
+def textPaste(w, pos)
+ begin
+ $ctag_text.insert pos, TkSelection.get
+ rescue
+ end
+end
+
+def textB1Press(w,x,y)
+ w.icursor 'current', "@#{x},#{y}"
+ w.itemfocus 'current'
+ w.focus
+ w.select_from 'current', "@#{x},#{y}"
+end
+
+def textB1Move(w,x,y)
+ w.select_to 'current', "@#{x},#{y}"
+end
+
+def textBs(w)
+ begin
+ $ctag_text.dchars 'sel.first', 'sel.last'
+ rescue
+ char = $ctag_text.index('insert').to_i - 1
+ $ctag_text.dchars(char) if char >= 0
+ end
+end
+
+def textDel(w)
+ begin
+ $ctag_text.dchars 'sel.first', 'sel.last'
+ rescue
+ $ctag_text.dchars 'insert'
+ end
+end
+
diff --git a/ext/tk/sample/demos-jp/dialog1.rb b/ext/tk/sample/demos-jp/dialog1.rb
new file mode 100644
index 0000000000..317b6d9eb3
--- /dev/null
+++ b/ext/tk/sample/demos-jp/dialog1.rb
@@ -0,0 +1,35 @@
+#
+# a dialog box with a local grab (called by 'widget')
+#
+class TkDialog_Demo1 < TkDialog
+ def title
+ "Dialog with local grab"
+ end
+
+ def message
+ '¥â¡¼¥À¥ë¥À¥¤¥¢¥í¥°¥Ü¥Ã¥¯¥¹¤Ç¤¹¡£Tk ¤Î "grab" ¥³¥Þ¥ó¥É¤ò»ÈÍѤ·¤Æ¥À¥¤¥¢¥í¥°¥Ü¥Ã¥¯¥¹¤Ç¡Ö¥í¡¼¥«¥ë¥°¥é¥Ö¡×¤·¤Æ¤¤¤Þ¤¹¡£²¼¤Î¤¤¤º¤ì¤«¤Î¥Ü¥¿¥ó¤ò¼Â¹Ô¤¹¤ë¤³¤È¤Ë¤è¤Ã¤Æ¡¢¤³¤Î¥À¥¤¥¢¥í¥°¤ËÅú¤¨¤ë¤Þ¤Ç¡¢¤³¤Î¥°¥é¥Ö¤Ë¤è¤Ã¤Æ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¤Î¾¤Î¥¦¥£¥ó¥É¥¦¤Ç¤Ï¡¢¥Ý¥¤¥ó¥¿´Ø·¸¤Î¥¤¥Ù¥ó¥È¤ò¼õ¤±¼è¤ë¤³¤È¤¬¤Ç¤­¤Ê¤¯¤Ê¤Ã¤Æ¤¤¤Þ¤¹¡£'
+ end
+
+ def bitmap
+ 'info'
+ end
+
+ def default_button
+ 0
+ end
+
+ def buttons
+# "λ²ò ¥­¥ã¥ó¥»¥ë ¥³¡¼¥É»²¾È"
+ ["λ²ò", "¥­¥ã¥ó¥»¥ë", "¥³¡¼¥É»²¾È"]
+ end
+end
+
+ret = TkDialog_Demo1.new('message_config'=>{'wraplength'=>'4i'}).value
+case ret
+when 0
+ print "¤¢¤Ê¤¿¤Ï¡Öλ²ò¡×¤ò²¡¤·¤Þ¤·¤¿¤Í¡£\n"
+when 1
+ print "¤¢¤Ê¤¿¤Ï¡Ö¥­¥ã¥ó¥»¥ë¡×¤ò²¡¤·¤Þ¤·¤¿¤Í¡£\n"
+when 2
+ showCode dialog1
+end
diff --git a/ext/tk/sample/demos-jp/dialog2.rb b/ext/tk/sample/demos-jp/dialog2.rb
new file mode 100644
index 0000000000..295522e562
--- /dev/null
+++ b/ext/tk/sample/demos-jp/dialog2.rb
@@ -0,0 +1,39 @@
+#
+# a dialog box with a global grab (called by 'widget')
+#
+class TkDialog_Demo2 < TkDialog
+ def title
+ "Dialog with global grab"
+ end
+
+ def message
+ '¤³¤Î¥À¥¤¥¢¥í¥°¥Ü¥Ã¥¯¥¹¤Ï¥°¥í¡¼¥Ð¥ë¥°¥é¥Ö¤ò»ÈÍѤ·¤Æ¤¤¤Þ¤¹¡£²¼¤Î¥Ü¥¿¥ó¤ò¼Â¹Ô¤¹¤ë¤Þ¤Ç¡¢¥Ç¥£¥¹¥×¥ì¥¤¾å¤Î¤¤¤«¤Ê¤ë¤â¤Î¤È¤âÂÐÏäǤ­¤Þ¤»¤ó¡£¥°¥í¡¼¥Ð¥ë¥°¥é¥Ö¤ò»ÈÍѤ¹¤ë¤³¤È¤Ï¡¢¤Þ¤ºÎɤ¤¹Í¤¨¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó¡£¤É¤¦¤·¤Æ¤âɬÍפˤʤë¤Þ¤Ç»È¤ª¤¦¤È»×¤ï¤Ê¤¤¤Ç²¼¤µ¤¤¡£'
+ end
+
+ def bitmap
+ 'info'
+ end
+
+ def default_button
+ 0
+ end
+
+ def buttons
+# "λ²ò ¥­¥ã¥ó¥»¥ë ¥³¡¼¥É»²¾È"
+ ["λ²ò", "¥­¥ã¥ó¥»¥ë", "¥³¡¼¥É»²¾È"]
+ end
+end
+
+ret = TkDialog_Demo2.new('message_config'=>{'wraplength'=>'4i'},
+ 'prev_command'=>proc{|dialog|
+ Tk.after 100, proc{dialog.grab('global')}
+ }).value
+case ret
+when 0
+ print "¤¢¤Ê¤¿¤Ï¡Öλ²ò¡×¤ò²¡¤·¤Þ¤·¤¿¤Í¡£\n"
+when 1
+ print "¤¢¤Ê¤¿¤Ï¡Ö¥­¥ã¥ó¥»¥ë¡×¤ò²¡¤·¤Þ¤·¤¿¤Í¡£\n"
+when 2
+ showCode dialog2
+end
+
diff --git a/ext/tk/sample/demos-jp/doc.org/README b/ext/tk/sample/demos-jp/doc.org/README
new file mode 100644
index 0000000000..90677d3316
--- /dev/null
+++ b/ext/tk/sample/demos-jp/doc.org/README
@@ -0,0 +1,7 @@
+This directory contains a collection of demonstration programs that
+are translated into Japanese. You need to use a Japanized "wish" to
+see these Japanese-translated demonstration programs. You also need
+to put this directory ("demos.jp") at the next to "demos" since some
+of the programs refer to the image files at "demos".
+
+Please refer to the README file at "demos" for more detail.
diff --git a/ext/tk/sample/demos-jp/doc.org/README.JP b/ext/tk/sample/demos-jp/doc.org/README.JP
new file mode 100644
index 0000000000..42b4929378
--- /dev/null
+++ b/ext/tk/sample/demos-jp/doc.org/README.JP
@@ -0,0 +1,14 @@
+This directory contains "widget" demo for the Japanized Tcl7.6/Tk4.2.
+Most of the messages in the original are translated to Japanese.
+But other tools in this directory are not translated.
+
+Following 2 kanji fonts are defined at the beginning of the file "widget."
+
+ -*--24-*-jisx0208.1983-0
+ -*--16-*-jisx0208.1983-0
+
+These fonts are all part of the core distribution of X11R5, so
+if you are running X11R5, you don't have to modify the file.
+
+But if you don't have these fonts, replace them with appropriate ones.
+"-*--14-*-jisx0208.1983-0" will be a good choice.
diff --git a/ext/tk/sample/demos-jp/doc.org/README.tk80 b/ext/tk/sample/demos-jp/doc.org/README.tk80
new file mode 100644
index 0000000000..c71f977d74
--- /dev/null
+++ b/ext/tk/sample/demos-jp/doc.org/README.tk80
@@ -0,0 +1,46 @@
+This directory contains a collection of programs to demonstrate
+the features of the Tk toolkit. The programs are all scripts for
+"wish", a windowing shell. If wish has been installed in /usr/local
+then you can invoke any of the programs in this directory just
+by typing its file name to your command shell. Otherwise invoke
+wish with the file as its first argument, e.g., "wish hello".
+The rest of this file contains a brief description of each program.
+Files with names ending in ".tcl" are procedure packages used by one
+or more of the demo programs; they can't be used as programs by
+themselves so they aren't described below.
+
+hello - Creates a single button; if you click on it, a message
+ is typed and the application terminates.
+
+widget - Contains a collection of demonstrations of the widgets
+ currently available in the Tk library. Most of the .tcl
+ files are scripts for individual demos available through
+ the "widget" program.
+
+ixset - A simple Tk-based wrapper for the "xset" program, which
+ allows you to interactively query and set various X options
+ such as mouse acceleration and bell volume. Thanks to
+ Pierre David for contributing this example.
+
+rolodex - A mock-up of a simple rolodex application. It has much of
+ the user interface for such an application but no back-end
+ database. This program was written in response to Tom
+ LaStrange's toolkit benchmark challenge.
+
+tcolor - A color editor. Allows you to edit colors in several
+ different ways, and will also perform automatic updates
+ using "send".
+
+rmt - Allows you to "hook-up" remotely to any Tk application
+ on the display. Select an application with the menu,
+ then just type commands: they'll go to that application.
+
+timer - Displays a seconds timer with start and stop buttons.
+ Control-c and control-q cause it to exit.
+
+browse - A simple directory browser. Invoke it with and argument
+ giving the name of the directory you'd like to browse.
+ Double-click on files or subdirectories to browse them.
+ Control-c and control-q cause the program to exit.
+
+sccs id = SCCS: @(#) README 1.3 96/02/16 10:49:14
diff --git a/ext/tk/sample/demos-jp/doc.org/license.terms b/ext/tk/sample/demos-jp/doc.org/license.terms
new file mode 100644
index 0000000000..03ca6fcb31
--- /dev/null
+++ b/ext/tk/sample/demos-jp/doc.org/license.terms
@@ -0,0 +1,39 @@
+This software is copyrighted by the Regents of the University of
+California, Sun Microsystems, Inc., and other parties. The following
+terms apply to all files associated with the software unless explicitly
+disclaimed in individual files.
+
+The authors hereby grant permission to use, copy, modify, distribute,
+and license this software and its documentation for any purpose, provided
+that existing copyright notices are retained in all copies and that this
+notice is included verbatim in any distributions. No written agreement,
+license, or royalty fee is required for any of the authorized uses.
+Modifications to this software may be copyrighted by their authors
+and need not follow the licensing terms described here, provided that
+the new terms are clearly indicated on the first page of each file where
+they apply.
+
+IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
+FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
+DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
+IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
+NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+MODIFICATIONS.
+
+GOVERNMENT USE: If you are acquiring this software on behalf of the
+U.S. government, the Government shall have only "Restricted Rights"
+in the software and related documentation as defined in the Federal
+Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
+are acquiring the software on behalf of the Department of Defense, the
+software shall be classified as "Commercial Computer Software" and the
+Government shall have only "Restricted Rights" as defined in Clause
+252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the
+authors grant the U.S. Government and others acting in its behalf
+permission to use and distribute the software in accordance with the
+terms specified in this license.
diff --git a/ext/tk/sample/demos-jp/doc.org/license.terms.tk80 b/ext/tk/sample/demos-jp/doc.org/license.terms.tk80
new file mode 100644
index 0000000000..03ca6fcb31
--- /dev/null
+++ b/ext/tk/sample/demos-jp/doc.org/license.terms.tk80
@@ -0,0 +1,39 @@
+This software is copyrighted by the Regents of the University of
+California, Sun Microsystems, Inc., and other parties. The following
+terms apply to all files associated with the software unless explicitly
+disclaimed in individual files.
+
+The authors hereby grant permission to use, copy, modify, distribute,
+and license this software and its documentation for any purpose, provided
+that existing copyright notices are retained in all copies and that this
+notice is included verbatim in any distributions. No written agreement,
+license, or royalty fee is required for any of the authorized uses.
+Modifications to this software may be copyrighted by their authors
+and need not follow the licensing terms described here, provided that
+the new terms are clearly indicated on the first page of each file where
+they apply.
+
+IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
+FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
+DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
+IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
+NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+MODIFICATIONS.
+
+GOVERNMENT USE: If you are acquiring this software on behalf of the
+U.S. government, the Government shall have only "Restricted Rights"
+in the software and related documentation as defined in the Federal
+Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
+are acquiring the software on behalf of the Department of Defense, the
+software shall be classified as "Commercial Computer Software" and the
+Government shall have only "Restricted Rights" as defined in Clause
+252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the
+authors grant the U.S. Government and others acting in its behalf
+permission to use and distribute the software in accordance with the
+terms specified in this license.
diff --git a/ext/tk/sample/demos-jp/entry1.rb b/ext/tk/sample/demos-jp/entry1.rb
new file mode 100644
index 0000000000..273b6728df
--- /dev/null
+++ b/ext/tk/sample/demos-jp/entry1.rb
@@ -0,0 +1,56 @@
+#
+# entry (no scrollbars) widget demo (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($entry1_demo) && $entry1_demo
+ $entry1_demo.destroy
+ $entry1_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$entry1_demo = TkToplevel.new {|w|
+ title("Entry Demonstration (no scrollbars)")
+ iconname("entry1")
+ positionWindow(w)
+}
+
+# label À¸À®
+msg = TkLabel.new($entry1_demo) {
+ font $font
+ wraplength '5i'
+ justify 'left'
+ text "3¼ïÎà¤Î°Û¤Ê¤ë¥¨¥ó¥È¥ê¤¬É½¼¨¤µ¤ì¤Æ¤¤¤Þ¤¹¡£Ê¸»ú¤òÆþÎϤ¹¤ë¤Ë¤Ï¥Ý¥¤¥ó¥¿¤ò»ý¤Ã¤Æ¹Ô¤­¡¢¥¯¥ê¥Ã¥¯¤·¤Æ¤«¤é¥¿¥¤¥×¤·¤Æ¤¯¤À¤µ¤¤¡£É¸½àŪ¤ÊMotif¤ÎÊÔ½¸µ¡Ç½¤¬¡¢Emacs¤Î¥­¡¼¥Ð¥¤¥ó¥É¤È¤È¤â¤Ë¡¢¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤Þ¤¹¡£Î㤨¤Ð¡¢¥Ð¥Ã¥¯¥¹¥Ú¡¼¥¹¤È¥³¥ó¥È¥í¡¼¥ë-H¤Ï¥«¡¼¥½¥ë¤Îº¸¤Îʸ»ú¤òºï½ü¤·¡¢¥Ç¥ê¡¼¥È¥­¡¼¤È¥³¥ó¥È¥í¡¼¥ë-D¤Ï¥«¡¼¥½¥ë¤Î±¦Â¦¤Îʸ»ú¤òºï½ü¤·¤Þ¤¹¡£Ä¹²á¤®¤Æ¥¦¥£¥ó¥É¥¦¤ËÆþ¤êÀÚ¤é¤Ê¤¤¤â¤Î¤Ï¡¢¥Þ¥¦¥¹¤Î¥Ü¥¿¥ó2¤ò²¡¤·¤¿¤Þ¤Þ¥É¥é¥Ã¥°¤¹¤ë¤³¤È¤Ç¥¹¥¯¥í¡¼¥ë¤µ¤»¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ÆüËܸì¤òÆþÎϤ¹¤ë¤Î¤Ï¥³¥ó¥È¥í¡¼¥ë-¥Ð¥Ã¥¯¥¹¥é¥Ã¥·¥å¤Ç¤¹¡£kinput2¤¬Æ°¤¤¤Æ¤¤¤ì¤ÐÆþÎϤ¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£"
+}
+msg.pack('side'=>'top')
+
+# frame À¸À®
+TkFrame.new($entry1_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $entry1_demo
+ $entry1_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'entry1'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# entry À¸À®
+e1 = TkEntry.new($entry1_demo, 'relief'=>'sunken')
+e2 = TkEntry.new($entry1_demo, 'relief'=>'sunken')
+e3 = TkEntry.new($entry1_demo, 'relief'=>'sunken')
+[e1,e2,e3].each{|w| w.pack('side'=>'top', 'padx'=>10, 'pady'=>5, 'fill'=>'x')}
+
+# ½é´üÃÍÁÞÆþ
+e1.insert(0, '½é´üÃÍ')
+e2.insert('end', "¤³¤Î¥¨¥ó¥È¥ê¤Ë¤ÏŤ¤Ê¸»úÎ󤬯þ¤Ã¤Æ¤¤¤Æ¡¢")
+e2.insert('end', "Ť¹¤®¤Æ¥¦¥£¥ó¥É¥¦¤Ë¤ÏÆþ¤êÀÚ¤é¤Ê¤¤¤Î¤Ç¡¢")
+e2.insert('end', "¼ÂºÝ¤Î½ê½ª¤ê¤Þ¤Ç¸«¤ë¤Ë¤Ï¥¹¥¯¥í¡¼¥ë¤µ¤»¤Ê¤±¤ì¤Ð")
+e2.insert('end', "¤Ê¤é¤Ê¤¤¤Ç¤·¤ç¤¦¡£")
+
diff --git a/ext/tk/sample/demos-jp/entry2.rb b/ext/tk/sample/demos-jp/entry2.rb
new file mode 100644
index 0000000000..7efac005f0
--- /dev/null
+++ b/ext/tk/sample/demos-jp/entry2.rb
@@ -0,0 +1,87 @@
+#
+# entry (with scrollbars) widget demo (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($entry2_demo) && $entry2_demo
+ $entry2_demo.destroy
+ $entry2_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$entry2_demo = TkToplevel.new {|w|
+ title("Entry Demonstration (with scrollbars)")
+ iconname("entry2")
+ positionWindow(w)
+}
+
+# label À¸À®
+msg = TkLabel.new($entry2_demo) {
+ font $font
+ wraplength '5i'
+ justify 'left'
+ text "3¼ïÎà¤Î°Û¤Ê¤ë¥¨¥ó¥È¥ê¤¬³Æ¡¹¥¹¥¯¥í¡¼¥ë¥Ð¡¼ÉÕ¤Çɽ¼¨¤µ¤ì¤Æ¤¤¤Þ¤¹¡£Ê¸»ú¤òÆþÎϤ¹¤ë¤Ë¤Ï¥Ý¥¤¥ó¥¿¤ò»ý¤Ã¤Æ¹Ô¤­¡¢¥¯¥ê¥Ã¥¯¤·¤Æ¤«¤é¥¿¥¤¥×¤·¤Æ¤¯¤À¤µ¤¤¡£É¸½àŪ¤ÊMotif¤ÎÊÔ½¸µ¡Ç½¤¬¡¢Emacs¤Î¥­¡¼¥Ð¥¤¥ó¥É¤È¤È¤â¤Ë¡¢¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤Þ¤¹¡£Î㤨¤Ð¡¢¥Ð¥Ã¥¯¥¹¥Ú¡¼¥¹¤È¥³¥ó¥È¥í¡¼¥ë-H¤Ï¥«¡¼¥½¥ë¤Îº¸¤Îʸ»ú¤òºï½ü¤·¡¢¥Ç¥ê¡¼¥È¥­¡¼¤È¥³¥ó¥È¥í¡¼¥ë-D¤Ï¥«¡¼¥½¥ë¤Î±¦Â¦¤Îʸ»ú¤òºï½ü¤·¤Þ¤¹¡£Ä¹²á¤®¤Æ¥¦¥£¥ó¥É¥¦¤ËÆþ¤êÀÚ¤é¤Ê¤¤¤â¤Î¤Ï¡¢¥Þ¥¦¥¹¤Î¥Ü¥¿¥ó2¤ò²¡¤·¤¿¤Þ¤Þ¥É¥é¥Ã¥°¤¹¤ë¤³¤È¤Ç¥¹¥¯¥í¡¼¥ë¤µ¤»¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ÆüËܸì¤òÆþÎϤ¹¤ë¤Î¤Ï¥³¥ó¥È¥í¡¼¥ë-¥Ð¥Ã¥¯¥¹¥é¥Ã¥·¥å¤Ç¤¹¡£kinput2¤¬Æ°¤¤¤Æ¤¤¤ì¤ÐÆþÎϤ¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£"
+}
+msg.pack('side'=>'top')
+
+# frame À¸À®
+TkFrame.new($entry2_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $entry2_demo
+ $entry2_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'entry2'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame À¸À®
+TkFrame.new($entry2_demo, 'borderwidth'=>10) {|w|
+ # entry 1
+ s1 = TkScrollbar.new(w, 'relief'=>'sunken', 'orient'=>'horiz')
+ e1 = TkEntry.new(w, 'relief'=>'sunken') {
+ xscrollcommand proc{|first,last| s1.set first,last}
+ }
+ s1.command(proc{|*args| e1.xview(*args)})
+ e1.pack('side'=>'top', 'fill'=>'x')
+ s1.pack('side'=>'top', 'fill'=>'x')
+
+ # spacer
+ TkFrame.new(w, 'width'=>20, 'height'=>10).pack('side'=>'top', 'fill'=>'x')
+
+ # entry 2
+ s2 = TkScrollbar.new(w, 'relief'=>'sunken', 'orient'=>'horiz')
+ e2 = TkEntry.new(w, 'relief'=>'sunken') {
+ xscrollcommand proc{|first,last| s2.set first,last}
+ }
+ s2.command(proc{|*args| e2.xview(*args)})
+ e2.pack('side'=>'top', 'fill'=>'x')
+ s2.pack('side'=>'top', 'fill'=>'x')
+
+ # spacer
+ TkFrame.new(w, 'width'=>20, 'height'=>10).pack('side'=>'top', 'fill'=>'x')
+
+ # entry 3
+ s3 = TkScrollbar.new(w, 'relief'=>'sunken', 'orient'=>'horiz')
+ e3 = TkEntry.new(w, 'relief'=>'sunken') {
+ xscrollcommand proc{|first,last| s3.set first,last}
+ }
+ s3.command(proc{|*args| e3.xview(*args)})
+ e3.pack('side'=>'top', 'fill'=>'x')
+ s3.pack('side'=>'top', 'fill'=>'x')
+
+ # ½é´üÃÍÁÞÆþ
+ e1.insert(0, '½é´üÃÍ')
+ e2.insert('end', "¤³¤Î¥¨¥ó¥È¥ê¤Ë¤ÏŤ¤Ê¸»úÎ󤬯þ¤Ã¤Æ¤¤¤Æ¡¢")
+ e2.insert('end', "Ť¹¤®¤Æ¥¦¥£¥ó¥É¥¦¤Ë¤ÏÆþ¤êÀÚ¤é¤Ê¤¤¤Î¤Ç¡¢")
+ e2.insert('end', "¼ÂºÝ¤Î½ê½ª¤ê¤Þ¤Ç¸«¤ë¤Ë¤Ï¥¹¥¯¥í¡¼¥ë¤µ¤»¤Ê¤±¤ì¤Ð")
+ e2.insert('end', "¤Ê¤é¤Ê¤¤¤Ç¤·¤ç¤¦¡£")
+
+}.pack('side'=>'top', 'fill'=>'x', 'expand'=>'yes')
+
diff --git a/ext/tk/sample/demos-jp/filebox.rb b/ext/tk/sample/demos-jp/filebox.rb
new file mode 100644
index 0000000000..1754596706
--- /dev/null
+++ b/ext/tk/sample/demos-jp/filebox.rb
@@ -0,0 +1,95 @@
+#
+# widget demo prompts the user to select a file (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($filebox_demo) && $entry2_demo
+ $filebox_demo.destroy
+ $filebox_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$filebox_demo = TkToplevel.new {|w|
+ title("File Selection Dialogs")
+ iconname("filebox")
+ positionWindow(w)
+}
+
+# label À¸À®
+TkLabel.new($filebox_demo,'font'=>$font,'wraplength'=>'4i','justify'=>'left',
+ 'text'=>"¥¨¥ó¥È¥ê¤Ë¥Õ¥¡¥¤¥ë̾¤òľÀÜÆþÎϤ¹¤ë¤«¡¢\"Browse\" ¥Ü¥¿¥ó¤ò²¡¤·¤Æ¥Õ¥¡¥¤¥ëÁªÂò¥À¥¤¥¢¥í¥°¤«¤é¥Õ¥¡¥¤¥ë̾¤òÁª¤ó¤Ç²¼¤µ¤¤¡£").pack('side'=>'top')
+
+# frame À¸À®
+TkFrame.new($filebox_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $filebox_demo
+ $filebox_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'filebox'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame À¸À®
+['³«¤¯', 'Êݸ'].each{|type|
+ TkFrame.new($filebox_demo) {|f|
+ TkLabel.new(f, 'text'=>"¥Õ¥¡¥¤¥ë¤ò#{type}: ", 'anchor'=>'e')\
+ .pack('side'=>'left')
+
+ TkEntry.new(f, 'width'=>20) {|e|
+ pack('side'=>'left', 'expand'=>'yes', 'fill'=>'x')
+
+ TkButton.new(f, 'text'=>'Browse ...',
+ 'command'=>proc{fileDialog $filebox_demo,e,type})\
+ .pack('side'=>'left')
+ }
+
+ pack('fill'=>'x', 'padx'=>'1c', 'pady'=>3)
+ }
+}
+
+$tk_strictMotif = TkVarAccess.new('tk_strictMotif')
+if ($tk_platform['platform'] == 'unix')
+ TkCheckButton.new($filebox_demo,
+ 'text'=>'Motif¥¹¥¿¥¤¥ë¤Î¥À¥¤¥¢¥í¥°¤òÍѤ¤¤ë',
+ 'variable'=>$tk_strictMotif,
+ 'onvalue'=>1, 'offvalue'=>0 ).pack('anchor'=>'c')
+end
+
+def fileDialog(w,ent,operation)
+ # Type names Extension(s) Mac File Type(s)
+ #
+ #--------------------------------------------------------
+ types = [
+ ['Text files', ['.txt','.doc'] ],
+ ['Text files', [], 'TEXT' ],
+ ['Ruby Scripts', ['.rb'], 'TEXT' ],
+ ['Tcl Scripts', ['.tcl'], 'TEXT' ],
+ ['C Source Files', ['.c','.h'] ],
+ ['All Source Files', ['.rb','.tcl','.c','.h'] ],
+ ['Image Files', ['.gif'] ],
+ ['Image Files', ['.jpeg','.jpg'] ],
+ ['Image Files', [], ['GIFF','JPEG']],
+ ['All files', '*' ]
+ ]
+
+ if operation == '³«¤¯'
+ file = Tk.getOpenFile('filetypes'=>types, 'parent'=>w)
+ else
+ file = Tk.getSaveFile('filetypes'=>types, 'parent'=>w,
+ 'initialfile'=>'Untitled',
+ 'defaultextension'=>'.txt')
+ end
+ if file != ""
+ ent.delete 0, 'end'
+ ent.insert 0, file
+ ent.xview 'end'
+ end
+end
+
diff --git a/ext/tk/sample/demos-jp/floor.rb b/ext/tk/sample/demos-jp/floor.rb
new file mode 100644
index 0000000000..bb655d5b55
--- /dev/null
+++ b/ext/tk/sample/demos-jp/floor.rb
@@ -0,0 +1,1717 @@
+#
+# floorDisplay widget demo (called by 'widget')
+#
+
+# floorDisplay --
+# Recreate the floorplan display in the canvas given by "w". The
+# floor given by "active" is displayed on top with its office structure
+# visible.
+#
+# Arguments:
+# w - Name of the canvas window.
+# active - Number of active floor (1, 2, or 3).
+
+def floorDisplay(w,active)
+ return if $activeFloor == active
+
+ w.delete('all')
+ $activeFloor = active
+
+ # First go through the three floors, displaying the backgrounds for
+ # each floor.
+
+ floor_bg1(w,$floor_colors['bg1'],$floor_colors['outline1'])
+ floor_bg2(w,$floor_colors['bg2'],$floor_colors['outline2'])
+ floor_bg3(w,$floor_colors['bg3'],$floor_colors['outline3'])
+
+ # Raise the background for the active floor so that it's on top.
+
+ w.raise("floor#{active}")
+
+ # Create a dummy item just to mark this point in the display list,
+ # so we can insert highlights here.
+
+ TkcRectangle.new(w,0,100,1,101, 'fill'=>'', 'outline'=>'', 'tags'=>'marker')
+
+ # Add the walls and labels for the active floor, along with
+ # transparent polygons that define the rooms on the floor.
+ # Make sure that the room polygons are on top.
+
+ $floorLabels.clear
+ $floorItems.clear
+ send("floor_fg#{active}", w, $floor_colors['offices'])
+ w.raise('room')
+
+ # Offset the floors diagonally from each other.
+
+ w.move('floor1', '2c', '2c')
+ w.move('floor2', '1c', '1c')
+
+ # Create items for the room entry and its label.
+ TkcWindow.new(w, 600, 100, 'anchor'=>'w', 'window'=>$floor_entry)
+ TkcText.new(w, 600, 100, 'anchor'=>'e', 'text'=>"Éô²°ÈÖ¹æ: ")
+ w['scrollregion'] = w.bbox('all')
+end
+
+# newRoom --
+# This method is invoked whenever the mouse enters a room
+# in the floorplan. It changes tags so that the current room is
+# highlighted.
+#
+# Arguments:
+# w - The name of the canvas window.
+
+def newRoom(w)
+ id = w.find_withtag('current')[0]
+ $currentRoom.value = $floorLabels[id.id] if id != ""
+ Tk.update(true)
+end
+
+# roomChanged --
+# This method is invoked whenever the currentRoom variable changes.
+# It highlights the current room and unhighlights any previous room.
+#
+# Arguments:
+# w - The canvas window displaying the floorplan.
+# args - Not used.
+
+def roomChanged(w,*args)
+ w.delete('highlight')
+ item = $floorItems[$currentRoom.value]
+ return if item == nil
+ new = TkcPolygon.new(w, *(w.coords(item)))
+ new.configure('fill'=>$floor_colors['active'], 'tags'=>'highlight')
+ w.raise(new, 'marker')
+end
+
+# floor_bg1 --
+# This method represents part of the floorplan database. When
+# invoked, it instantiates the background information for the first
+# floor.
+#
+# Arguments:
+# w - The canvas window.
+# fill - Fill color to use for the floor's background.
+# outline - Color to use for the floor's outline.
+
+def floor_bg1(w,fill,outline)
+ TkcPolygon.new(w,347,80,349,82,351,84,353,85,363,92,375,99,386,104,
+ 386,129,398,129,398,162,484,162,484,129,559,129,559,133,725,
+ 133,725,129,802,129,802,389,644,389,644,391,559,391,559,327,
+ 508,327,508,311,484,311,484,278,395,278,395,288,400,288,404,
+ 288,409,290,413,292,418,297,421,302,422,309,421,318,417,325,
+ 411,330,405,332,397,333,344,333,340,334,336,336,335,338,332,
+ 342,331,347,332,351,334,354,336,357,341,359,340,360,335,363,
+ 331,365,326,366,304,366,304,355,258,355,258,387,60,387,60,391,
+ 0,391,0,337,3,337,3,114,8,114,8,25,30,25,30,5,93,5,98,5,104,7,
+ 110,10,116,16,119,20,122,28,123,32,123,68,220,68,220,34,221,
+ 22,223,17,227,13,231,8,236,4,242,2,246,0,260,0,283,1,300,5,
+ 321,14,335,22,348,25,365,29,363,39,358,48,352,56,337,70,
+ 344,76,347,80, 'tags'=>['floor1','bg'], 'fill'=>fill)
+ TkcLine.new(w,386,129,398,129, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,258,355,258,387, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,60,387,60,391, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,0,337,0,391, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,60,391,0,391, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,3,114,3,337, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,258,387,60,387, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,484,162,398,162, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,398,162,398,129, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,484,278,484,311, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,484,311,508,311, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,508,327,508,311, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,559,327,508,327, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,644,391,559,391, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,644,389,644,391, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,559,129,484,129, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,484,162,484,129, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,725,133,559,133, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,559,129,559,133, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,725,129,802,129, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,802,389,802,129, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,3,337,0,337, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,559,391,559,327, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,802,389,644,389, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,725,133,725,129, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,8,25,8,114, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,8,114,3,114, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,30,25,8,25, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,484,278,395,278, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,30,25,30,5, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,93,5,30,5, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,98,5,93,5, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,104,7,98,5, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,110,10,104,7, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,116,16,110,10, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,119,20,116,16, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,122,28,119,20, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,123,32,122,28, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,123,68,123,32, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,220,68,123,68, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,386,129,386,104, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,386,104,375,99, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,375,99,363,92, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,353,85,363,92, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,220,68,220,34, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,337,70,352,56, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,352,56,358,48, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,358,48,363,39, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,363,39,365,29, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,365,29,348,25, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,348,25,335,22, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,335,22,321,14, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,321,14,300,5, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,300,5,283,1, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,283,1,260,0, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,260,0,246,0, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,246,0,242,2, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,242,2,236,4, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,236,4,231,8, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,231,8,227,13, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,223,17,227,13, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,221,22,223,17, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,220,34,221,22, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,340,360,335,363, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,335,363,331,365, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,331,365,326,366, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,326,366,304,366, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,304,355,304,366, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,395,288,400,288, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,404,288,400,288, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,409,290,404,288, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,413,292,409,290, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,418,297,413,292, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,421,302,418,297, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,422,309,421,302, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,421,318,422,309, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,421,318,417,325, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,417,325,411,330, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,411,330,405,332, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,405,332,397,333, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,397,333,344,333, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,344,333,340,334, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,340,334,336,336, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,336,336,335,338, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,335,338,332,342, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,331,347,332,342, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,332,351,331,347, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,334,354,332,351, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,336,357,334,354, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,341,359,336,357, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,341,359,340,360, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,395,288,395,278, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,304,355,258,355, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,347,80,344,76, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,344,76,337,70, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,349,82,347,80, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,351,84,349,82, 'fill'=>outline, 'tags'=>['floor1','bg'])
+ TkcLine.new(w,353,85,351,84, 'fill'=>outline, 'tags'=>['floor1','bg'])
+end
+
+# floor_bg2 --
+# This method represents part of the floorplan database. When
+# invoked, it instantiates the background information for the first
+# floor.
+#
+# Arguments:
+# w - The canvas window.
+# fill - Fill color to use for the floor's background.
+# outline - Color to use for the floor's outline.
+
+def floor_bg2(w,fill,outline)
+ TkcPolygon.new(w,559,129,484,129,484,162,398,162,398,129,315,129,
+ 315,133,176,133,176,129,96,129,96,133,3,133,3,339,0,339,0,391,
+ 60,391,60,387,258,387,258,329,350,329,350,311,395,311,395,280,
+ 484,280,484,311,508,311,508,327,558,327,558,391,644,391,644,
+ 367,802,367,802,129,725,129,725,133,559,133,559,129,
+ 'tags'=>['floor2','bg'], 'fill'=>fill)
+ TkcLine.new(w,350,311,350,329, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,398,129,398,162, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,802,367,802,129, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,802,129,725,129, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,725,133,725,129, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,559,129,559,133, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,559,133,725,133, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,484,162,484,129, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,559,129,484,129, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,802,367,644,367, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,644,367,644,391, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,644,391,558,391, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,558,327,558,391, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,558,327,508,327, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,508,327,508,311, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,484,311,508,311, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,484,280,484,311, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,398,162,484,162, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,484,280,395,280, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,395,280,395,311, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,258,387,60,387, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,3,133,3,339, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,3,339,0,339, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,60,391,0,391, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,0,339,0,391, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,60,387,60,391, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,258,329,258,387, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,350,329,258,329, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,395,311,350,311, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,398,129,315,129, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,176,133,315,133, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,176,129,96,129, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,3,133,96,133, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,315,133,315,129, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,176,133,176,129, 'fill'=>outline, 'tags'=>['floor2','bg'])
+ TkcLine.new(w,96,133,96,129, 'fill'=>outline, 'tags'=>['floor2','bg'])
+end
+
+# floor_bg3 --
+# This method represents part of the floorplan database. When
+# invoked, it instantiates the background information for the first
+# floor.
+#
+# Arguments:
+# w - The canvas window.
+# fill - Fill color to use for the floor's background.
+# outline - Color to use for the floor's outline.
+
+def floor_bg3(w,fill,outline)
+ TkcPolygon.new(w,159,300,107,300,107,248,159,248,159,129,96,129,96,
+ 133,21,133,21,331,0,331,0,391,60,391,60,370,159,370,159,300,
+ 'tags'=>['floor3','bg'], 'fill'=>fill)
+ TkcPolygon.new(w,258,370,258,329,350,329,350,311,399,311,399,129,
+ 315,129,315,133,176,133,176,129,159,129,159,370,258,370,
+ 'tags'=>['floor3','bg'], 'fill'=>fill)
+ TkcLine.new(w,96,133,96,129, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,176,129,96,129, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,176,129,176,133, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,315,133,176,133, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,315,133,315,129, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,399,129,315,129, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,399,311,399,129, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,399,311,350,311, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,350,329,350,311, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,350,329,258,329, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,258,370,258,329, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,60,370,258,370, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,60,370,60,391, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,60,391,0,391, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,0,391,0,331, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,21,331,0,331, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,21,331,21,133, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,96,133,21,133, 'fill'=>outline, 'tags'=>['floor3','bg'])
+ TkcLine.new(w,107,300,159,300,159,248,107,248,107,300,
+ 'fill'=>outline, 'tags'=>['floor3','bg'])
+end
+
+# floor_fg1 --
+# This method represents part of the floorplan database. When
+# invoked, it instantiates the foreground information for the first
+# floor (office outlines and numbers).
+#
+# Arguments:
+# w - The canvas window.
+# color - Color to use for drawing foreground information.
+
+def floor_fg1(w,color)
+ i = TkcPolygon.new(w,375,246,375,172,341,172,341,246,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '101'
+ $floorItems['101'] = i
+ TkcText.new(w,358,209, 'text'=>'101', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,307,240,339,240,339,206,307,206,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = 'Pub Lift1'
+ $floorItems['Pub Lift1'] = i
+ TkcText.new(w,323,223, 'text'=>'Pub Lift1', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,339,205,307,205,307,171,339,171,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = 'Priv Lift1'
+ $floorItems['Priv Lift1'] = i
+ TkcText.new(w,323,188, 'text'=>'Priv Lift1', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,42,389,42,337,1,337,1,389,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '110'
+ $floorItems['110'] = i
+ TkcText.new(w,21.5,363, 'text'=>'110', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,59,389,59,385,90,385,90,337,44,337,44,389,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '109'
+ $floorItems['109'] = i
+ TkcText.new(w,67,363, 'text'=>'109', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,51,300,51,253,6,253,6,300,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '111'
+ $floorItems['111'] = i
+ TkcText.new(w,28.5,276.5, 'text'=>'111', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,98,248,98,309,79,309,79,248,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '117B'
+ $floorItems['117B'] = i
+ TkcText.new(w,88.5,278.5, 'text'=>'117B', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,51,251,51,204,6,204,6,251,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '112'
+ $floorItems['112'] = i
+ TkcText.new(w,28.5,227.5, 'text'=>'112', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,6,156,51,156,51,203,6,203,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '113'
+ $floorItems['113'] = i
+ TkcText.new(w,28.5,179.5, 'text'=>'113', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,85,169,79,169,79,192,85,192,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '117A'
+ $floorItems['117A'] = i
+ TkcText.new(w,82,180.5, 'text'=>'117A', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,77,302,77,168,53,168,53,302,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '117'
+ $floorItems['117'] = i
+ TkcText.new(w,65,235, 'text'=>'117', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,51,155,51,115,6,115,6,155,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '114'
+ $floorItems['114'] = i
+ TkcText.new(w,28.5,135, 'text'=>'114', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,95,115,53,115,53,168,95,168,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '115'
+ $floorItems['115'] = i
+ TkcText.new(w,74,141.5, 'text'=>'115', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,87,113,87,27,10,27,10,113,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '116'
+ $floorItems['116'] = i
+ TkcText.new(w,48.5,70, 'text'=>'116', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,89,91,128,91,128,113,89,131,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '118'
+ $floorItems['118'] = i
+ TkcText.new(w,108.5,102, 'text'=>'118', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,178,128,178,132,216,132,216,91,
+ 163,91,163,112,149,112,149,128,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '120'
+ $floorItems['120'] = i
+ TkcText.new(w,189.5,111.5, 'text'=>'120', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,79,193,87,193,87,169,136,169,136,192,
+ 156,192,156,169,175,169,175,246,79,246,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '122'
+ $floorItems['122'] = i
+ TkcText.new(w,131,207.5, 'text'=>'122', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,138,169,154,169,154,191,138,191,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '121'
+ $floorItems['121'] = i
+ TkcText.new(w,146,180, 'text'=>'121', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,99,300,126,300,126,309,99,309,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '106A'
+ $floorItems['106A'] = i
+ TkcText.new(w,112.5,304.5, 'text'=>'106A', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,128,299,128,309,150,309,150,248,99,248,99,299,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '105'
+ $floorItems['105'] = i
+ TkcText.new(w,124.5,278.5, 'text'=>'105', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,174,309,174,300,152,300,152,309,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '106B'
+ $floorItems['106B'] = i
+ TkcText.new(w,163,304.5, 'text'=>'106B', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,176,299,176,309,216,309,216,248,152,248,152,299,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '104'
+ $floorItems['104'] = i
+ TkcText.new(w,184,278.5, 'text'=>'104', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,138,385,138,337,91,337,91,385,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '108'
+ $floorItems['108'] = i
+ TkcText.new(w,114.5,361, 'text'=>'108', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,256,337,140,337,140,385,256,385,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '107'
+ $floorItems['107'] = i
+ TkcText.new(w,198,361, 'text'=>'107', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,300,353,300,329,260,329,260,353,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = 'Smoking'
+ $floorItems['Smoking'] = i
+ TkcText.new(w,280,341, 'text'=>'Smoking', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,314,135,314,170,306,170,306,246,177,246,177,135,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '123'
+ $floorItems['123'] = i
+ TkcText.new(w,245.5,190.5, 'text'=>'123', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,217,248,301,248,301,326,257,326,257,310,217,310,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '103'
+ $floorItems['103'] = i
+ TkcText.new(w,259,287, 'text'=>'103', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,396,188,377,188,377,169,316,169,316,131,396,131,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '124'
+ $floorItems['124'] = i
+ TkcText.new(w,356,150, 'text'=>'124', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,397,226,407,226,407,189,377,189,377,246,397,246,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '125'
+ $floorItems['125'] = i
+ TkcText.new(w,392,217.5, 'text'=>'125', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,399,187,409,187,409,207,474,207,474,164,399,164,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '126'
+ $floorItems['126'] = i
+ TkcText.new(w,436.5,185.5, 'text'=>'126', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,409,209,409,229,399,229,399,253,
+ 486,253,486,239,474,239,474,209,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '127'
+ $floorItems['127'] = i
+ TkcText.new(w,436.5,'231', 'text'=>'127', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,501,164,501,174,495,174,495,188,
+ 490,188,490,204,476,204,476,164,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = 'MShower'
+ $floorItems['MShower'] = i
+ TkcText.new(w,488.5,'184', 'text'=>'MShower', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,497,176,513,176,513,204,492,204,492,190,497,190,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = 'Closet'
+ $floorItems['Closet'] = i
+ TkcText.new(w,502.5,190, 'text'=>'Closet', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,476,237,476,206,513,206,513,254,488,254,488,237,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = 'WShower'
+ $floorItems['WShower'] = i
+ TkcText.new(w,494.5,230, 'text'=>'WShower', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,486,131,558,131,558,135,724,135,724,166,
+ 697,166,697,275,553,275,531,254,515,254,
+ 515,174,503,174,503,161,486,161,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '130'
+ $floorItems['130'] = i
+ TkcText.new(w,638.5,205, 'text'=>'130', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,308,242,339,242,339,248,342,248,
+ 342,246,397,246,397,276,393,276,
+ 393,309,300,309,300,248,308,248,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '102'
+ $floorItems['102'] = i
+ TkcText.new(w,367.5,278.5, 'text'=>'102', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,397,255,486,255,486,276,397,276,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '128'
+ $floorItems['128'] = i
+ TkcText.new(w,441.5,265.5, 'text'=>'128', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,510,309,486,309,486,255,530,255,
+ 552,277,561,277,561,325,510,325,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '129'
+ $floorItems['129'] = i
+ TkcText.new(w,535.5,293, 'text'=>'129', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,696,281,740,281,740,387,642,387,
+ 642,389,561,389,561,277,696,277,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '133'
+ $floorItems['133'] = i
+ TkcText.new(w,628.5,335, 'text'=>'133', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,742,387,742,281,800,281,800,387,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '132'
+ $floorItems['132'] = i
+ TkcText.new(w,771,334, 'text'=>'132', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,800,168,800,280,699,280,699,168,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '134'
+ $floorItems['134'] = i
+ TkcText.new(w,749.5,224, 'text'=>'134', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,726,131,726,166,800,166,800,131,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '135'
+ $floorItems['135'] = i
+ TkcText.new(w,763,148.5, 'text'=>'135', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,340,360,335,363,331,365,326,366,304,366,
+ 304,312,396,312,396,288,400,288,404,288,
+ 409,290,413,292,418,297,421,302,422,309,
+ 421,318,417,325,411,330,405,332,397,333,
+ 344,333,340,334,336,336,335,338,332,342,
+ 331,347,332,351,334,354,336,357,341,359,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = 'Ramona Stair'
+ $floorItems['Ramona Stair'] = i
+ TkcText.new(w,368,323, 'text'=>'Ramona Stair', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,30,23,30,5,93,5,98,5,104,7,110,10,116,16,119,20,
+ 122,28,123,32,123,68,220,68,220,87,90,87,90,23,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = 'University Stair'
+ $floorItems['University Stair'] = i
+ TkcText.new(w,155,77.5, 'text'=>'University Stair', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,282,37,295,40,312,49,323,56,337,70,352,56,
+ 358,48,363,39,365,29,348,25,335,22,321,14,
+ 300,5,283,1,260,0,246,0,242,2,236,4,231,8,
+ 227,13,223,17,221,22,220,34,260,34,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = 'Plaza Stair'
+ $floorItems['Plaza Stair'] = i
+ TkcText.new(w,317.5,28.5, 'text'=>'Plaza Stair', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,220,34,260,34,282,37,295,40,312,49,
+ 323,56,337,70,350,83,365,94,377,100,
+ 386,104,386,128,220,128,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = 'Plaza Deck'
+ $floorItems['Plaza Deck'] = i
+ TkcText.new(w,303,81, 'text'=>'Plaza Deck', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,257,336,77,336,6,336,6,301,77,301,77,310,257,310,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '106'
+ $floorItems['106'] = i
+ TkcText.new(w,131.5,318.5, 'text'=>'106', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ i = TkcPolygon.new(w,146,110,162,110,162,91,130,91,130,115,95,115,
+ 95,128,114,128,114,151,157,151,157,153,112,153,
+ 112,130,97,130,97,168,175,168,175,131,146,131,
+ 'fill'=>'', 'tags'=>['floor1','room'])
+ $floorLabels[i.id] = '119'
+ $floorItems['119'] = i
+ TkcText.new(w,143.5,133, 'text'=>'119', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor1','label'])
+ TkcLine.new(w,155,191,155,189, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,155,177,155,169, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,96,129,96,169, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,78,169,176,169, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,176,247,176,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,340,206,307,206, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,340,187,340,170, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,340,210,340,201, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,340,247,340,224, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,340,241,307,241, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,376,246,376,170, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,307,247,307,170, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,376,170,307,170, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,315,129,315,170, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,147,129,176,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,202,133,176,133, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,398,129,315,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,258,352,258,387, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,60,387,60,391, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,0,337,0,391, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,60,391,0,391, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,3,114,3,337, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,258,387,60,387, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,52,237,52,273, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,52,189,52,225, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,52,140,52,177, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,395,306,395,311, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,531,254,398,254, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,475,178,475,238, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,502,162,398,162, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,398,129,398,188, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,383,188,376,188, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,408,188,408,194, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,398,227,398,254, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,408,227,398,227, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,408,222,408,227, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,408,206,408,210, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,408,208,475,208, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,484,278,484,311, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,484,311,508,311, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,508,327,508,311, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,559,327,508,327, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,644,391,559,391, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,644,389,644,391, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,514,205,475,205, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,496,189,496,187, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,559,129,484,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,484,162,484,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,725,133,559,133, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,559,129,559,133, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,725,149,725,167, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,725,129,802,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,802,389,802,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,739,167,802,167, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,396,188,408,188, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,0,337,9,337, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,58,337,21,337, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,43,391,43,337, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,105,337,75,337, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,91,387,91,337, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,154,337,117,337, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,139,387,139,337, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,227,337,166,337, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,258,337,251,337, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,258,328,302,328, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,302,355,302,311, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,395,311,302,311, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,484,278,395,278, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,395,294,395,278, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,473,278,473,275, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,473,256,473,254, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,533,257,531,254, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,553,276,551,274, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,698,276,553,276, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,559,391,559,327, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,802,389,644,389, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,741,314,741,389, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,698,280,698,167, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,707,280,698,280, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,802,280,731,280, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,741,280,741,302, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,698,167,727,167, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,725,137,725,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,514,254,514,175, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,496,175,514,175, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,502,175,502,162, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,475,166,475,162, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,496,176,496,175, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,491,189,496,189, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,491,205,491,189, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,487,238,475,238, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,487,240,487,238, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,487,252,487,254, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,315,133,304,133, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,256,133,280,133, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,78,247,270,247, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,307,247,294,247, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,214,133,232,133, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,217,247,217,266, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,217,309,217,291, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,217,309,172,309, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,154,309,148,309, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,175,300,175,309, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,151,300,175,300, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,151,247,151,309, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,78,237,78,265, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,78,286,78,309, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,106,309,78,309, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,130,309,125,309, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,99,309,99,247, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,127,299,99,299, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,127,309,127,299, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,155,191,137,191, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,137,169,137,191, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,78,171,78,169, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,78,190,78,218, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,86,192,86,169, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,86,192,78,192, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,52,301,3,301, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,52,286,52,301, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,52,252,3,252, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,52,203,3,203, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,3,156,52,156, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,8,25,8,114, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,63,114,3,114, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,75,114,97,114, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,108,114,129,114, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,129,114,129,89, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,52,114,52,128, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,132,89,88,89, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,88,25,88,89, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,88,114,88,89, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,218,89,144,89, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,147,111,147,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,162,111,147,111, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,162,109,162,111, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,162,96,162,89, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,218,89,218,94, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,218,89,218,119, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,8,25,88,25, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,258,337,258,328, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,113,129,96,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,302,355,258,355, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,386,104,386,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,377,100,386,104, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,365,94,377,100, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,350,83,365,94, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,337,70,350,83, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,337,70,323,56, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,312,49,323,56, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,295,40,312,49, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,282,37,295,40, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,260,34,282,37, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,253,34,260,34, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,386,128,386,104, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,113,152,156,152, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,113,152,156,152, 'fill'=>color, 'tags'=>['floor1','wall'])
+ TkcLine.new(w,113,152,113,129, 'fill'=>color, 'tags'=>['floor1','wall'])
+end
+
+# floor_fg2 --
+# This method represents part of the floorplan database. When
+# invoked, it instantiates the foreground information for the second
+# floor (office outlines and numbers).
+#
+# Arguments:
+# w - The canvas window.
+# color - Color to use for drawing foreground information.
+
+def floor_fg2(w,color)
+ i = TkcPolygon.new(w,748,188,755,188,755,205,758,205,758,222,
+ 800,222,800,168,748,168,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '238'
+ $floorItems['238'] = i
+ TkcText.new(w,774,195, 'text'=>'238', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,726,188,746,188,746,166,800,166,800,131,726,131,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '237'
+ $floorItems['237'] = i
+ TkcText.new(w,763,148.5, 'text'=>'237', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,497,187,497,204,559,204,559,324,641,324,
+ 643,324,643,291,641,291,641,205,696,205,
+ 696,291,694,291,694,314,715,314,715,291,
+ 715,205,755,205,755,190,724,190,724,187,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '246'
+ $floorItems['246'] = i
+ TkcText.new(w,600,264, 'text'=>'246', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,694,279,643,279,643,314,694,314,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '247'
+ $floorItems['247'] = i
+ TkcText.new(w,668.5,296.5, 'text'=>'247', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,232,250,308,250,308,242,339,242,339,246,
+ 397,246,397,255,476,255,476,250,482,250,559,250,
+ 559,274,482,274,482,278,396,278,396,274,232,274,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '202'
+ $floorItems['202'] = i
+ TkcText.new(w,285.5,260, 'text'=>'202', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,53,228,53,338,176,338,233,338,233,196,
+ 306,196,306,180,175,180,175,169,156,169,
+ 156,196,176,196,176,228,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '206'
+ $floorItems['206'] = i
+ TkcText.new(w,143,267, 'text'=>'206', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,51,277,6,277,6,338,51,338,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '212'
+ $floorItems['212'] = i
+ TkcText.new(w,28.5,307.5, 'text'=>'212', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,557,276,486,276,486,309,510,309,510,325,557,325,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '245'
+ $floorItems['245'] = i
+ TkcText.new(w,521.5,300.5, 'text'=>'245', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,560,389,599,389,599,326,560,326,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '244'
+ $floorItems['244'] = i
+ TkcText.new(w,579.5,357.5, 'text'=>'244', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,601,389,601,326,643,326,643,389,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '243'
+ $floorItems['243'] = i
+ TkcText.new(w,622,357.5, 'text'=>'243', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,688,316,645,316,645,365,688,365,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '242'
+ $floorItems['242'] = i
+ TkcText.new(w,666.5,340.5, 'text'=>'242', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,802,367,759,367,759,226,802,226,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = 'Barbecue Deck'
+ $floorItems['Barbecue Deck'] = i
+ TkcText.new(w,780.5,296.5, 'text'=>'Barbecue Deck', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,755,262,755,314,717,314,717,262,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '240'
+ $floorItems['240'] = i
+ TkcText.new(w,736,288, 'text'=>'240', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,755,316,689,316,689,365,755,365,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '241'
+ $floorItems['241'] = i
+ TkcText.new(w,722,340.5, 'text'=>'241', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,755,206,717,206,717,261,755,261,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '239'
+ $floorItems['239'] = i
+ TkcText.new(w,736,233.5, 'text'=>'239', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,695,277,643,277,643,206,695,206,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '248'
+ $floorItems['248'] = i
+ TkcText.new(w,669,241.5, 'text'=>'248', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,676,135,676,185,724,185,724,135,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '236'
+ $floorItems['236'] = i
+ TkcText.new(w,700,160, 'text'=>'236', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,675,135,635,135,635,145,628,145,628,185,675,185,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '235'
+ $floorItems['235'] = i
+ TkcText.new(w,651.5,160, 'text'=>'235', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,626,143,633,143,633,135,572,135,
+ 572,143,579,143,579,185,626,185,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '234'
+ $floorItems['234'] = i
+ TkcText.new(w,606,160, 'text'=>'234', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,557,135,571,135,571,145,578,145,
+ 578,185,527,185,527,131,557,131,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '233'
+ $floorItems['233'] = i
+ TkcText.new(w,552.5,158, 'text'=>'233', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,476,249,557,249,557,205,476,205,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '230'
+ $floorItems['230'] = i
+ TkcText.new(w,516.5,227, 'text'=>'230', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,476,164,486,164,486,131,525,131,525,185,476,185,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '232'
+ $floorItems['232'] = i
+ TkcText.new(w,500.5,158, 'text'=>'232', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,476,186,495,186,495,204,476,204,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '229'
+ $floorItems['229'] = i
+ TkcText.new(w,485.5,195, 'text'=>'229', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,474,207,409,207,409,187,399,187,399,164,474,164,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '227'
+ $floorItems['227'] = i
+ TkcText.new(w,436.5,185.5, 'text'=>'227', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,399,228,399,253,474,253,474,209,409,209,409,228,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '228'
+ $floorItems['228'] = i
+ TkcText.new(w,436.5,231, 'text'=>'228', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,397,246,397,226,407,226,407,189,377,189,377,246,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '226'
+ $floorItems['226'] = i
+ TkcText.new(w,392,217.5, 'text'=>'226', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,377,169,316,169,316,131,397,131,397,188,377,188,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '225'
+ $floorItems['225'] = i
+ TkcText.new(w,356.5,150, 'text'=>'225', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,234,198,306,198,306,249,234,249,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '224'
+ $floorItems['224'] = i
+ TkcText.new(w,270,223.5, 'text'=>'224', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,270,179,306,179,306,170,314,170,314,135,270,135,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '223'
+ $floorItems['223'] = i
+ TkcText.new(w,292,157, 'text'=>'223', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,268,179,221,179,221,135,268,135,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '222'
+ $floorItems['222'] = i
+ TkcText.new(w,244.5,157, 'text'=>'222', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,177,179,219,179,219,135,177,135,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '221'
+ $floorItems['221'] = i
+ TkcText.new(w,198,157, 'text'=>'221', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,299,327,349,327,349,284,341,284,341,276,299,276,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '204'
+ $floorItems['204'] = i
+ TkcText.new(w,324,301.5, 'text'=>'204', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,234,276,297,276,297,327,257,327,257,338,234,338,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '205'
+ $floorItems['205'] = i
+ TkcText.new(w,265.5,307, 'text'=>'205', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,256,385,256,340,212,340,212,385,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '207'
+ $floorItems['207'] = i
+ TkcText.new(w,234,362.5, 'text'=>'207', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,210,340,164,340,164,385,210,385,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '208'
+ $floorItems['208'] = i
+ TkcText.new(w,187,362.5, 'text'=>'208', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,115,340,162,340,162,385,115,385,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '209'
+ $floorItems['209'] = i
+ TkcText.new(w,138.5,362.5, 'text'=>'209', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,89,228,89,156,53,156,53,228,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '217'
+ $floorItems['217'] = i
+ TkcText.new(w,71,192, 'text'=>'217', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,89,169,97,169,97,190,89,190,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '217A'
+ $floorItems['217A'] = i
+ TkcText.new(w,93,179.5, 'text'=>'217A', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,89,156,89,168,95,168,95,135,53,135,53,156,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '216'
+ $floorItems['216'] = i
+ TkcText.new(w,71,145.5, 'text'=>'216', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,51,179,51,135,6,135,6,179,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '215'
+ $floorItems['215'] = i
+ TkcText.new(w,28.5,157, 'text'=>'215', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,51,227,6,227,6,180,51,180,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '214'
+ $floorItems['214'] = i
+ TkcText.new(w,28.5,203.5, 'text'=>'214', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,51,275,6,275,6,229,51,229,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '213'
+ $floorItems['213'] = i
+ TkcText.new(w,28.5,252, 'text'=>'213', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,114,340,67,340,67,385,114,385,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '210'
+ $floorItems['210'] = i
+ TkcText.new(w,90.5,362.5, 'text'=>'210', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,59,389,59,385,65,385,65,340,1,340,1,389,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '211'
+ $floorItems['211'] = i
+ TkcText.new(w,33,364.5, 'text'=>'211', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,393,309,350,309,350,282,342,282,342,276,393,276,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '203'
+ $floorItems['203'] = i
+ TkcText.new(w,367.5,292.5, 'text'=>'203', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,99,191,91,191,91,226,174,226,174,198,
+ 154,198,154,192,109,192,109,169,99,169,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '220'
+ $floorItems['220'] = i
+ TkcText.new(w,132.5,208.5, 'text'=>'220', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,339,205,307,205,307,171,339,171,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = 'Priv Lift2'
+ $floorItems['Priv Lift2'] = i
+ TkcText.new(w,323,188, 'text'=>'Priv Lift2', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,307,240,339,240,339,206,307,206,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = 'Pub Lift 2'
+ $floorItems['Pub Lift 2'] = i
+ TkcText.new(w,323,223, 'text'=>'Pub Lift 2', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,175,168,97,168,97,131,175,131,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '218'
+ $floorItems['218'] = i
+ TkcText.new(w,136,149.5, 'text'=>'218', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,154,191,111,191,111,169,154,169,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '219'
+ $floorItems['219'] = i
+ TkcText.new(w,132.5,180, 'text'=>'219', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ i = TkcPolygon.new(w,375,246,375,172,341,172,341,246,
+ 'fill'=>'', 'tags'=>['floor2','room'])
+ $floorLabels[i.id] = '201'
+ $floorItems['201'] = i
+ TkcText.new(w,358,209, 'text'=>'201', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor2','label'])
+ TkcLine.new(w,641,186,678,186, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,757,350,757,367, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,634,133,634,144, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,634,144,627,144, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,572,133,572,144, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,572,144,579,144, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,398,129,398,162, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,174,197,175,197, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,175,197,175,227, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,757,206,757,221, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,396,188,408,188, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,727,189,725,189, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,747,167,802,167, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,747,167,747,189, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,755,189,739,189, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,769,224,757,224, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,802,224,802,129, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,802,129,725,129, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,725,189,725,129, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,725,186,690,186, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,676,133,676,186, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,627,144,627,186, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,629,186,593,186, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,579,144,579,186, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,559,129,559,133, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,725,133,559,133, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,484,162,484,129, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,559,129,484,129, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,526,129,526,186, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,540,186,581,186, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,528,186,523,186, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,511,186,475,186, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,496,190,496,186, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,496,205,496,202, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,475,205,527,205, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,558,205,539,205, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,558,205,558,249, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,558,249,475,249, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,662,206,642,206, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,695,206,675,206, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,695,278,642,278, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,642,291,642,206, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,695,291,695,206, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,716,208,716,206, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,757,206,716,206, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,757,221,757,224, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,793,224,802,224, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,757,262,716,262, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,716,220,716,264, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,716,315,716,276, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,757,315,703,315, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,757,325,757,224, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,757,367,644,367, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,689,367,689,315, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,647,315,644,315, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,659,315,691,315, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,600,325,600,391, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,627,325,644,325, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,644,391,644,315, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,615,325,575,325, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,644,391,558,391, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,563,325,558,325, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,558,391,558,314, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,558,327,508,327, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,558,275,484,275, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,558,302,558,275, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,508,327,508,311, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,484,311,508,311, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,484,275,484,311, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,475,208,408,208, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,408,206,408,210, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,408,222,408,227, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,408,227,398,227, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,398,227,398,254, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,408,188,408,194, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,383,188,376,188, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,398,188,398,162, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,398,162,484,162, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,475,162,475,254, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,398,254,475,254, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,484,280,395,280, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,395,311,395,275, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,307,197,293,197, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,278,197,233,197, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,233,197,233,249, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,307,179,284,179, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,233,249,278,249, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,269,179,269,133, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,220,179,220,133, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,155,191,110,191, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,90,190,98,190, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,98,169,98,190, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,52,133,52,165, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,52,214,52,177, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,52,226,52,262, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,52,274,52,276, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,234,275,234,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,226,339,258,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,211,387,211,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,214,339,177,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,258,387,60,387, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,3,133,3,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,165,339,129,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,117,339,80,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,68,339,59,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,0,339,46,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,60,391,0,391, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,0,339,0,391, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,60,387,60,391, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,258,329,258,387, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,350,329,258,329, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,395,311,350,311, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,398,129,315,129, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,176,133,315,133, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,176,129,96,129, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,3,133,96,133, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,66,387,66,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,115,387,115,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,163,387,163,339, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,234,275,276,275, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,288,275,309,275, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,298,275,298,329, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,341,283,350,283, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,321,275,341,275, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,375,275,395,275, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,315,129,315,170, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,376,170,307,170, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,307,250,307,170, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,376,245,376,170, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,340,241,307,241, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,340,245,340,224, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,340,210,340,201, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,340,187,340,170, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,340,206,307,206, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,293,250,307,250, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,271,179,238,179, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,226,179,195,179, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,176,129,176,179, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,182,179,176,179, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,174,169,176,169, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,162,169,90,169, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,96,169,96,129, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,175,227,90,227, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,90,190,90,227, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,52,179,3,179, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,52,228,3,228, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,52,276,3,276, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,155,177,155,169, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,110,191,110,169, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,155,189,155,197, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,350,283,350,329, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,162,197,155,197, 'fill'=>color, 'tags'=>['floor2','wall'])
+ TkcLine.new(w,341,275,341,283, 'fill'=>color, 'tags'=>['floor2','wall'])
+end
+
+# floor_fg3 --
+# This method represents part of the floorplan database. When
+# invoked, it instantiates the foreground information for the third
+# floor (office outlines and numbers).
+#
+# Arguments:
+# w - The canvas window.
+# color - Color to use for drawing foreground information.
+
+def floor_fg3(w,color)
+ i = TkcPolygon.new(w,89,228,89,180,70,180,70,228,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '316'
+ $floorItems['316'] = i
+ TkcText.new(w,79.5,204, 'text'=>'316', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,115,368,162,368,162,323,115,323,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '309'
+ $floorItems['309'] = i
+ TkcText.new(w,138.5,345.5, 'text'=>'309', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,164,323,164,368,211,368,211,323,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '308'
+ $floorItems['308'] = i
+ TkcText.new(w,187.5,345.5, 'text'=>'308', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,256,368,212,368,212,323,256,323,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '307'
+ $floorItems['307'] = i
+ TkcText.new(w,234,345.5, 'text'=>'307', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,244,276,297,276,297,327,260,327,260,321,244,321,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '305'
+ $floorItems['305'] = i
+ TkcText.new(w,270.5,301.5, 'text'=>'305', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,251,219,251,203,244,203,244,219,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '324B'
+ $floorItems['324B'] = i
+ TkcText.new(w,247.5,211, 'text'=>'324B', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,251,249,244,249,244,232,251,232,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '324A'
+ $floorItems['324A'] = i
+ TkcText.new(w,247.5,240.5, 'text'=>'324A', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,223,135,223,179,177,179,177,135,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '320'
+ $floorItems['320'] = i
+ TkcText.new(w,200,157, 'text'=>'320', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,114,368,114,323,67,323,67,368,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '310'
+ $floorItems['310'] = i
+ TkcText.new(w,90.5,345.5, 'text'=>'310', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,23,277,23,321,68,321,68,277,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '312'
+ $floorItems['312'] = i
+ TkcText.new(w,45.5,299, 'text'=>'312', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,23,229,68,229,68,275,23,275,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '313'
+ $floorItems['313'] = i
+ TkcText.new(w,45.5,252, 'text'=>'313', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,68,227,23,227,23,180,68,180,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '314'
+ $floorItems['314'] = i
+ TkcText.new(w,40.5,203.5, 'text'=>'314', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,95,179,95,135,23,135,23,179,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '315'
+ $floorItems['315'] = i
+ TkcText.new(w,59,157, 'text'=>'315', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,99,226,99,204,91,204,91,226,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '316B'
+ $floorItems['316B'] = i
+ TkcText.new(w,95,215, 'text'=>'316B', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,91,202,99,202,99,180,91,180,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '316A'
+ $floorItems['316A'] = i
+ TkcText.new(w,95,191, 'text'=>'316A', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,97,169,109,169,109,192,154,192,154,198,
+ 174,198,174,226,101,226,101,179,97,179,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '319'
+ $floorItems['319'] = i
+ TkcText.new(w,141.5,209, 'text'=>'319', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,65,368,58,368,58,389,1,389,1,333,23,333,23,323,65,323,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '311'
+ $floorItems['311'] = i
+ TkcText.new(w,29.5,361, 'text'=>'311', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,154,191,111,191,111,169,154,169,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '318'
+ $floorItems['318'] = i
+ TkcText.new(w,132.5,180, 'text'=>'318', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,175,168,97,168,97,131,175,131,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '317'
+ $floorItems['317'] = i
+ TkcText.new(w,136,149.5, 'text'=>'317', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,274,194,274,221,306,221,306,194,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '323'
+ $floorItems['323'] = i
+ TkcText.new(w,290,207.5, 'text'=>'323', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,306,222,274,222,274,249,306,249,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '325'
+ $floorItems['325'] = i
+ TkcText.new(w,290,235.5, 'text'=>'325', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,263,179,224,179,224,135,263,135,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '321'
+ $floorItems['321'] = i
+ TkcText.new(w,243.5,157, 'text'=>'321', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,314,169,306,169,306,192,273,192,
+ 264,181,264,135,314,135,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '322'
+ $floorItems['322'] = i
+ TkcText.new(w,293.5,163.5, 'text'=>'322', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,307,240,339,240,339,206,307,206,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = 'Pub Lift3'
+ $floorItems['Pub Lift3'] = i
+ TkcText.new(w,323,223, 'text'=>'Pub Lift3', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,339,205,307,205,307,171,339,171,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = 'Priv Lift3'
+ $floorItems['Priv Lift3'] = i
+ TkcText.new(w,323,188, 'text'=>'Priv Lift3', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,350,284,376,284,376,276,397,276,397,309,350,309,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '303'
+ $floorItems['303'] = i
+ TkcText.new(w,373.5,292.5, 'text'=>'303', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,272,203,272,249,252,249,252,230,
+ 244,230,244,221,252,221,252,203,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '324'
+ $floorItems['324'] = i
+ TkcText.new(w,262,226, 'text'=>'324', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,299,276,299,327,349,327,349,284,341,284,341,276,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '304'
+ $floorItems['304'] = i
+ TkcText.new(w,324,301.5, 'text'=>'304', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,375,246,375,172,341,172,341,246,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '301'
+ $floorItems['301'] = i
+ TkcText.new(w,358,209, 'text'=>'301', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,397,246,377,246,377,185,397,185,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '327'
+ $floorItems['327'] = i
+ TkcText.new(w,387,215.5, 'text'=>'327', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,316,131,316,169,377,169,377,185,397,185,397,131,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '326'
+ $floorItems['326'] = i
+ TkcText.new(w,365.5,150, 'text'=>'326', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,308,251,242,251,242,274,342,274,342,282,375, 282,
+ 375,274,397,274,397,248,339,248,339,242,308,242,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '302'
+ $floorItems['302'] = i
+ TkcText.new(w,319.5,261, 'text'=>'302', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ i = TkcPolygon.new(w,70,321,242,321,242,200,259,200,259,203,272,203,
+ 272,193,263,180,242,180,175,180,175,169,156,169,
+ 156,196,177,196,177,228,107,228,70,228,70,275,107,275,
+ 107,248,160,248,160,301,107,301,107,275,70,275,
+ 'fill'=>'', 'tags'=>['floor3','room'])
+ $floorLabels[i.id] = '306'
+ $floorItems['306'] = i
+ TkcText.new(w,200.5,284.5, 'text'=>'306', 'fill'=>color,
+ 'anchor'=>'c', 'tags'=>['floor3','label'])
+ TkcLine.new(w,341,275,341,283, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,162,197,155,197, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,396,247,399,247, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,399,129,399,311, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,258,202,243,202, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,350,283,350,329, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,251,231,243,231, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,243,220,251,220, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,243,250,243,202, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,155,197,155,190, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,110,192,110,169, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,155,192,110,192, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,155,177,155,169, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,176,197,176,227, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,69,280,69,274, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,21,276,69,276, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,69,262,69,226, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,21,228,69,228, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,21,179,75,179, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,69,179,69,214, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,90,220,90,227, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,90,204,90,202, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,90,203,100,203, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,90,187,90,179, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,90,227,176,227, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,100,179,100,227, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,100,179,87,179, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,96,179,96,129, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,162,169,96,169, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,173,169,176,169, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,182,179,176,179, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,176,129,176,179, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,195,179,226,179, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,224,133,224,179, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,264,179,264,133, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,238,179,264,179, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,273,207,273,193, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,273,235,273,250, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,273,224,273,219, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,273,193,307,193, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,273,222,307,222, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,273,250,307,250, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,384,247,376,247, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,340,206,307,206, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,340,187,340,170, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,340,210,340,201, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,340,247,340,224, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,340,241,307,241, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,376,247,376,170, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,307,250,307,170, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,376,170,307,170, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,315,129,315,170, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,376,283,366,283, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,376,283,376,275, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,399,275,376,275, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,341,275,320,275, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,341,283,350,283, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,298,275,298,329, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,308,275,298,275, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,243,322,243,275, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,243,275,284,275, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,258,322,226,322, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,212,370,212,322, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,214,322,177,322, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,163,370,163,322, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,165,322,129,322, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,84,322,117,322, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,71,322,64,322, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,115,322,115,370, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,66,322,66,370, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,52,322,21,322, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,21,331,0,331, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,21,331,21,133, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,96,133,21,133, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,176,129,96,129, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,315,133,176,133, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,315,129,399,129, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,399,311,350,311, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,350,329,258,329, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,258,322,258,370, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,60,370,258,370, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,60,370,60,391, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,0,391,0,331, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,60,391,0,391, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,307,250,307,242, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,273,250,307,250, 'fill'=>color, 'tags'=>['floor3','wall'])
+ TkcLine.new(w,258,250,243,250, 'fill'=>color, 'tags'=>['floor3','wall'])
+end
+
+# Below is the "main program" that creates the floorplan demonstration.
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($floor_demo) && $floor_demo
+ $floor_demo.destroy
+ $floor_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$floor_demo = TkToplevel.new {|w|
+ title("Floorplan Canvas Demonstration")
+ iconname("Floorplan")
+ positionWindow(w)
+ geometry('+20+20')
+ minsize(100,100)
+}
+
+# label À¸À®
+TkLabel.new($floor_demo, 'font'=>$font, 'wraplength'=>'8i', 'justify'=>'left',
+ 'text'=>"¤³¤Î¥¦¥£¥ó¥É¥¦¤Ë¤Ï¥Ç¥£¥¸¥¿¥ë¥¨¥¯¥¤¥Ã¥×¥á¥ó¥È¼Ò¤Î¥¦¥§¥¹¥¿¥ó¥ê¥µ¡¼¥Á¥é¥Ü¥é¥È¥ê (DECWRL) ¤Î´Ö¼è¤ê¤¬½ñ¤«¤ì¤¿¥­¥ã¥ó¥Ð¥¹ widget ¤¬Æþ¤Ã¤Æ¤¤¤Þ¤¹¡£¤³¤ì¤Ï 3³¬·ú¤Æ¤Ç¡¢¾ï¤Ë¤½¤Î¤¦¤Á¤Î1³¬Ê¬¤¬ÁªÂò¡¢¤Ä¤Þ¤ê¤½¤Î´Ö¼è¤ê¤¬É½¼¨¤µ¤ì¤ë¤è¤¦¤Ë¤Ê¤Ã¤Æ¤¤¤Þ¤¹¡£¤¢¤ë³¬¤òÁªÂò¤¹¤ë¤Ë¤Ï¡¢¤½¤Î¾å¤Ç¥Þ¥¦¥¹¤Îº¸¥Ü¥¿¥ó¤ò¥¯¥ê¥Ã¥¯¤·¤Æ¤¯¤À¤µ¤¤¡£¥Þ¥¦¥¹¤¬ÁªÂò¤µ¤ì¤Æ¤¤¤ë³¬¤Î¾å¤òư¤¯¤È¡¢¤½¤Î²¼¤Ë¤¢¤ëÉô²°¤Î¿§¤¬ÊѤï¤ê¡¢Éô²°Èֹ椬¡ÖÉô²°ÈÖ¹æ:¡×¥¨¥ó¥È¥ê¤Ëɽ¼¨¤µ¤ì¤Þ¤¹¡£¤Þ¤¿¡¢¥¨¥ó¥È¥ê¤ËÉô²°ÈÖ¹æ¤ò½ñ¤¯¤È¤½¤ÎÉô²°¤Î¿§¤¬ÊѤï¤ê¤Þ¤¹¡£"){
+ pack('side'=>'top')
+}
+
+# frame À¸À®
+$floor_buttons = TkFrame.new($floor_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $floor_demo
+ $floor_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'floor'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}
+$floor_buttons.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# ÊÑ¿ôÀßÄê
+$floorLabels = {}
+$floorItems = {}
+
+# canvas ÀßÄê
+if $tk_version =~ /^4\.[01]/
+ $floor_canvas_frame = TkFrame.new($floor_demo,'bd'=>2,'relief'=>'sunken',
+ 'highlightthickness'=>2)
+ $floor_canvas = TkCanvas.new($floor_canvas_frame,
+ 'width'=>900, 'height'=>500, 'borderwidth'=>0,
+ 'highlightthickness'=>0) {|c|
+ TkScrollbar.new($floor_demo, 'orient'=>'horiz',
+ 'command'=>proc{|*args| c.xview(*args)}){|hs|
+ c.xscrollcommand(proc{|first,last| hs.set first,last})
+ pack('side'=>'bottom', 'fill'=>'x')
+ }
+ TkScrollbar.new($floor_demo, 'command'=>proc{|*args| c.yview(*args)}){|vs|
+ c.yscrollcommand(proc{|first,last| vs.set first,last})
+ pack('side'=>'right', 'fill'=>'y')
+ }
+ }
+ $floor_canvas_frame.pack('side'=>'top','fill'=>'both', 'expand'=>'yes')
+ $floor_canvas.pack('expand'=>'yes', 'fill'=>'both')
+
+else
+ TkFrame.new($floor_demo) {|f|
+ pack('side'=>'top', 'fill'=>'both', 'expand'=>'yes')
+
+ h = TkScrollbar.new(f, 'highlightthickness'=>0, 'orient'=>'horizontal')
+ v = TkScrollbar.new(f, 'highlightthickness'=>0, 'orient'=>'vertical')
+
+ TkFrame.new(f, 'bd'=>2, 'relief'=>'sunken') {|f1|
+ $floor_canvas = TkCanvas.new(f1, 'width'=>900, 'height'=>500,
+ 'borderwidth'=>0,
+ 'highlightthickness'=>0) {
+ xscrollcommand(proc{|first,last| h.set first,last})
+ yscrollcommand(proc{|first,last| v.set first,last})
+ pack('expand'=>'yes', 'fill'=>'both')
+ }
+ grid('padx'=>1, 'pady'=>1, 'row'=>0, 'column'=>0,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ }
+
+ v.grid('padx'=>1, 'pady'=>1, 'row'=>0, 'column'=>1,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ h.grid('padx'=>1, 'pady'=>1, 'row'=>1, 'column'=>0,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+
+ TkGrid.rowconfigure(f, 0, 'weight'=>1, 'minsize'=>0)
+ TkGrid.columnconfigure(f, 0, 'weight'=>1, 'minsize'=>0)
+
+ pack('expand'=>'yes', 'fill'=>'both', 'padx'=>1, 'pady'=>1)
+
+ v.command(proc{|*args| c.yview(*args)})
+ h.command(proc{|*args| c.xview(*args)})
+ }
+end
+
+
+# Create an entry for displaying and typing in current room.
+
+$currentRoom = TkVariable.new
+$floor_entry = TkEntry.new($floor_canvas, 'width'=>10, 'relief'=>'sunken',
+ 'bd'=>2, 'textvariable'=>$currentRoom)
+
+# Choose colors, then fill in the floorplan.
+
+$floor_colors = {}
+if TkWinfo.depth($floor_canvas) > 1
+ $floor_colors['bg1'] = '#a9c1da'
+ $floor_colors['outline1'] = '#77889a'
+ $floor_colors['bg2'] = '#9ab0c6'
+ $floor_colors['outline2'] = '#687786'
+ $floor_colors['bg3'] = '#8ba0b3'
+ $floor_colors['outline3'] = '#596673'
+ $floor_colors['offices'] = 'Black'
+ $floor_colors['active'] = '#c4d1df'
+else
+ $floor_colors['bg1'] = 'white'
+ $floor_colors['outline1'] = 'black'
+ $floor_colors['bg2'] = 'white'
+ $floor_colors['outline2'] = 'black'
+ $floor_colors['bg3'] = 'white'
+ $floor_colors['outline3'] = 'black'
+ $floor_colors['offices'] = 'Black'
+ $floor_colors['active'] = 'black'
+end
+
+$activeFloor = ''
+floorDisplay $floor_canvas,3
+
+# Set up event bindings for canvas:
+
+$floor_canvas.itembind('floor1', '1', proc{floorDisplay $floor_canvas,1})
+$floor_canvas.itembind('floor2', '1', proc{floorDisplay $floor_canvas,2})
+$floor_canvas.itembind('floor3', '1', proc{floorDisplay $floor_canvas,3})
+$floor_canvas.itembind('room', 'Enter', proc{newRoom $floor_canvas})
+$floor_canvas.itembind('room', 'Leave', proc{$currentRoom.value = ''})
+$floor_canvas.bind('2', proc{|x,y| $floor_canvas.scan_mark x,y}, '%x %y')
+$floor_canvas.bind('B2-Motion',
+ proc{|x,y| $floor_canvas.scan_dragto x,y}, '%x %y')
+$floor_canvas.bind('Destroy', proc{$currentRoom.unset})
+$currentRoom.value = ''
+$currentRoom.trace('w',proc{roomChanged $floor_canvas})
+
diff --git a/ext/tk/sample/demos-jp/form.rb b/ext/tk/sample/demos-jp/form.rb
new file mode 100644
index 0000000000..35baeed46b
--- /dev/null
+++ b/ext/tk/sample/demos-jp/form.rb
@@ -0,0 +1,62 @@
+#
+# form widget demo (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($form_demo) && $form_demo
+ $form_demo.destroy
+ $form_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$form_demo = TkToplevel.new {|w|
+ title("Form Demonstration")
+ iconname("form")
+ positionWindow(w)
+}
+
+# label À¸À®
+msg = TkLabel.new($form_demo) {
+ font $font
+ wraplength '4i'
+ justify 'left'
+ text "¤³¤Î¥¦¥£¥ó¥É¥¦¤Ï´Êñ¤Ê¥Õ¥©¡¼¥àÆþÎÏÍѤˤʤäƤ¤¤Æ¡¢¤µ¤Þ¤¶¤Þ¤Ê¥¨¥ó¥È¥ê¤ËÆþÎϤ¬¤Ç¤­¤Þ¤¹¡£¥¿¥Ö¤Ç¥¨¥ó¥È¥ê¤ÎÀÚÂØ¤¨¤¬¤Ç¤­¤Þ¤¹¡£"
+}
+msg.pack('side'=>'top', 'fill'=>'x')
+
+# frame À¸À®
+TkFrame.new($form_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $form_demo
+ $form_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'form'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# entry À¸À®
+form_data = []
+(1..5).each{|i|
+ f = TkFrame.new($form_demo, 'bd'=>2)
+ e = TkEntry.new(f, 'relief'=>'sunken', 'width'=>40)
+ l = TkLabel.new(f)
+ e.pack('side'=>'right')
+ l.pack('side'=>'left')
+ form_data[i] = {'frame'=>f, 'entry'=>e, 'label'=>l}
+}
+
+# ʸ»úÎóÀßÄê
+form_data[1]['label'].text('̾Á°:')
+form_data[2]['label'].text('½»½ê:')
+form_data[5]['label'].text('ÅÅÏÃ:')
+
+# pack
+(1..5).each{|i| form_data[i]['frame'].pack('side'=>'top', 'fill'=>'x')}
+
diff --git a/ext/tk/sample/demos-jp/hello b/ext/tk/sample/demos-jp/hello
new file mode 100644
index 0000000000..94e450899c
--- /dev/null
+++ b/ext/tk/sample/demos-jp/hello
@@ -0,0 +1,9 @@
+#!/usr/local/bin/ruby
+require 'tk'
+
+TkButton.new(nil,
+ 'text'=>"¤³¤ó¤Ë¤Á¤Ï¡¢À¤³¦",
+ 'command'=>proc{print "¤³¤ó¤Ë¤Á¤Ï¡¢À¤³¦\n"; exit}
+).pack
+
+Tk.mainloop
diff --git a/ext/tk/sample/demos-jp/hscale.rb b/ext/tk/sample/demos-jp/hscale.rb
new file mode 100644
index 0000000000..4333239c73
--- /dev/null
+++ b/ext/tk/sample/demos-jp/hscale.rb
@@ -0,0 +1,76 @@
+require "tkcanvas"
+
+if defined?($hscale_demo) && $hscale_deom
+ $hscale_demo.destroy
+ $hscale_demo = nil
+end
+
+$hscale_demo = TkToplevel.new {|w|
+ title("Horizontal Scale Demonstration")
+ iconname("hscale")
+}
+positionWindow($hscale_demo)
+
+
+msg = TkLabel.new($hscale_demo) {
+ font $font
+ wraplength '3.5i'
+ justify 'left'
+ text "²¼¤Ë¤ÏÌð°õ¤¬1¤Ä¤È¿åÊ¿¤Ê¥¹¥±¡¼¥ë¤¬É½¼¨¤µ¤ì¤Æ¤¤¤Þ¤¹¡£\
+¥¹¥±¡¼¥ë¾å¤Ç¥Þ¥¦¥¹¥Ü¥¿¥ó1¤ò¥¯¥ê¥Ã¥¯¡¢¤Þ¤¿¤Ï¥É¥é¥Ã¥°¤¹¤ë¤È\
+Ìð°õ¤ÎŤµ¤òÊѤ¨¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£"
+}
+msg.pack('side'=>'top')
+
+TkFrame.new($hscale_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc {
+ tmppath = $hscale_demo
+ $hscale_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc { showCode 'hscale' }
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+TkFrame.new($hscale_demo) {|frame|
+ canvas = TkCanvas.new(frame) {|c|
+ width 50
+ height 50
+ bd 0
+ highlightthickness 0
+ TkcPolygon.new(c, '0', '0', '1', '1', '2', '2') {
+ fill 'DeepSkyBlue'
+ tags 'poly'
+ }
+ TkcLine.new(c, '0', '0', '1', '1', '2', '2', '0', '0') {
+ fill 'black'
+ tags 'line'
+ }
+ }.pack('side'=>'top', 'expand'=>'yes', 'anchor'=>'s', 'fill'=>'x', 'padx'=>'15')
+ scale = TkScale.new(frame) {
+ orient 'horizontal'
+ length 284
+ from 0
+ to 250
+ command proc{|value| setWidth(canvas, value)}
+ tickinterval 50
+ }.pack('side'=>'bottom', 'expand'=>'yes', 'anchor'=>'n')
+ scale.set 75
+}.pack('side'=>'top', 'fill'=>'x')
+
+
+def setWidth(w, width)
+ width = width + 21
+ x2 = width - 30
+ if x2 < 21
+ x2 = 21
+ end
+ w.coords 'poly',20,15,20,35,x2,35,x2,45,width,25,x2,5,x2,15,20,15
+ w.coords 'line',20,15,20,35,x2,35,x2,45,width,25,x2,5,x2,15,20,15
+end
diff --git a/ext/tk/sample/demos-jp/icon.rb b/ext/tk/sample/demos-jp/icon.rb
new file mode 100644
index 0000000000..9b39d33847
--- /dev/null
+++ b/ext/tk/sample/demos-jp/icon.rb
@@ -0,0 +1,91 @@
+#
+# iconic button widget demo (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($icon_demo) && $icon_demo
+ $icon_demo.destroy
+ $icon_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$icon_demo = TkToplevel.new {|w|
+ title("Iconic Button Demonstration")
+ iconname("icon")
+ positionWindow(w)
+}
+
+# label À¸À®
+msg = TkLabel.new($icon_demo) {
+ font $font
+ wraplength '5i'
+ justify 'left'
+ text "¤³¤Î¥¦¥£¥ó¥É¥¦¤Ë¤Ï¥é¥¸¥ª¥Ü¥¿¥ó¤È¥Á¥§¥Ã¥¯¥Ü¥¿¥ó¾å¤Ë¥Ó¥Ã¥È¥Þ¥Ã¥×¤ä²èÁü¤òɽ¼¨¤¹¤ë 3 ¤Ä¤ÎÊýË¡¤ò¼¨¤·¤Æ¤¤¤Þ¤¹¡£º¸¤Ë¤¢¤ë¤Î¤Ï2¤Ä¤Î¥é¥¸¥ª¥Ü¥¿¥ó¤Ç¡¢¤½¤ì¤¾¤ì¤¬¡¢¥Ó¥Ã¥È¥Þ¥Ã¥×¤ÈÁªÂò¤ò¼¨¤¹¥¤¥ó¥¸¥±¡¼¥¿¤Ç¤Ç¤­¤Æ¤¤¤Þ¤¹¡£Ãæ±û¤Ë¤¢¤ë¤Î¤Ï¡¢ÁªÂòºÑ¤ß¤«¤É¤¦¤«¤Ë¤è¤Ã¤Æ°Û¤Ê¤ë²èÁü¤òɽ¼¨¤¹¤ë¥Á¥§¥Ã¥¯¥Ü¥¿¥ó¤Ç¤¹¡£±¦Â¦¤Ë¤¢¤ë¤Î¤ÏÁªÂòºÑ¤ß¤«¤É¤¦¤«¤Ë¤è¤Ã¤ÆÇØ·Ê¿§¤¬ÊѤï¤ë¥Ó¥Ã¥È¥Þ¥Ã¥×¤òɽ¼¨¤¹¤ë¥Á¥§¥Ã¥¯¥Ü¥¿¥ó¤Ç¤¹¡£"
+}
+msg.pack('side'=>'top')
+
+# frame À¸À®
+TkFrame.new($icon_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $icon_demo
+ $icon_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'icon'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# image À¸À®
+flagup = \
+TkBitmapImage.new('file'=>[$demo_dir,
+ 'images','flagup.bmp'].join(File::Separator),
+ 'maskfile'=>\
+ [$demo_dir,'images','flagup.bmp'].join(File::Separator))
+flagdown = \
+TkBitmapImage.new('file'=>[$demo_dir,
+ 'images','flagdown.bmp'].join(File::Separator),
+ 'maskfile'=>\
+ [$demo_dir,'images','flagdown.bmp'].join(File::Separator))
+
+# ÊÑ¿ôÀ¸À®
+letters = TkVariable.new
+
+# frame À¸À®
+TkFrame.new($icon_demo, 'borderwidth'=>10){|w|
+ TkFrame.new(w) {|f|
+ TkRadioButton.new(f){
+ bitmap '@' + [$demo_dir,'images','letters.bmp'].join(File::Separator)
+ variable letters
+ value 'full'
+ }.pack('side'=>'top', 'expand'=>'yes')
+
+ TkRadioButton.new(f){
+ bitmap '@' + [$demo_dir,'images','noletter.bmp'].join(File::Separator)
+ variable letters
+ value 'empty'
+ }.pack('side'=>'top', 'expand'=>'yes')
+
+ }.pack('side'=>'left', 'expand'=>'yes', 'padx'=>'5m')
+
+ TkCheckButton.new(w) {
+ image flagdown
+ selectimage flagup
+ indicatoron 0
+ selectcolor self['background']
+ }.pack('side'=>'left', 'expand'=>'yes', 'padx'=>'5m')
+
+ TkCheckButton.new(w) {
+ bitmap '@' + [$demo_dir,'images','letters.bmp'].join(File::Separator)
+ indicatoron 0
+ selectcolor 'SeaGreen1'
+ }.pack('side'=>'left', 'expand'=>'yes', 'padx'=>'5m')
+
+}.pack('side'=>'top')
+
diff --git a/ext/tk/sample/demos-jp/image1.rb b/ext/tk/sample/demos-jp/image1.rb
new file mode 100644
index 0000000000..bfe47c47af
--- /dev/null
+++ b/ext/tk/sample/demos-jp/image1.rb
@@ -0,0 +1,57 @@
+#
+# two image widgets demo (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($image1_demo) && $image1_demo
+ $image1_demo.destroy
+ $image1_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$image1_demo = TkToplevel.new {|w|
+ title('Image Demonstration #1')
+ iconname("Image1")
+ positionWindow(w)
+}
+
+# label À¸À®
+msg = TkLabel.new($image1_demo) {
+ font $font
+ wraplength '4i'
+ justify 'left'
+ text "¤³¤Î¥Ç¥â¤Ç¤Ï2¤Ä¤Î¥é¥Ù¥ë¾å¤Ë²èÁü¤ò¤½¤ì¤¾¤ìɽ¼¨¤·¤Æ¤¤¤Þ¤¹¡£"
+}
+msg.pack('side'=>'top')
+
+# frame À¸À®
+TkFrame.new($image1_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $image1_demo
+ $image1_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'image1'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# image À¸À®
+image1a = \
+TkPhotoImage.new('file'=>[$demo_dir,
+ 'images','earth.gif'].join(File::Separator))
+image1b = \
+TkPhotoImage.new('file'=>[$demo_dir,
+ 'images','earthris.gif'].join(File::Separator))
+
+# label À¸À®
+[ TkLabel.new($image1_demo, 'image'=>image1a, 'bd'=>1, 'relief'=>'sunken'),
+ TkLabel.new($image1_demo, 'image'=>image1b, 'bd'=>1, 'relief'=>'sunken')
+].each{|w| w.pack('side'=>'top', 'padx'=>'.5m', 'pady'=>'.5m')}
+
diff --git a/ext/tk/sample/demos-jp/image2.rb b/ext/tk/sample/demos-jp/image2.rb
new file mode 100644
index 0000000000..07f9b17ebe
--- /dev/null
+++ b/ext/tk/sample/demos-jp/image2.rb
@@ -0,0 +1,101 @@
+#
+# widget demo 'load image' (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($image2_demo) && $image2_demo
+ $image2_demo.destroy
+ $image2_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$image2_demo = TkToplevel.new {|w|
+ title('Image Demonstration #2')
+ iconname("Image2")
+ positionWindow(w)
+}
+
+# label À¸À®
+msg = TkLabel.new($image2_demo) {
+ font $font
+ wraplength '4i'
+ justify 'left'
+ text "¤³¤Î¥Ç¥â¤Ç¤ÏTk¤Î photo image ¤ò»ÈÍѤ·¤Æ²èÁü¤ò¸«¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£ºÇ½é¤Ë¥¨¥ó¥È¥êÆâ¤Ë¤Ë¥Ç¥£¥ì¥¯¥È¥ê̾¤òÆþ¤ì¤Æ²¼¤µ¤¤¡£¼¡¤Ë²¼¤Î¥ê¥¹¥È¥Ü¥Ã¥¯¥¹¤Ë¤³¤Î¥Ç¥£¥ì¥¯¥È¥ê¤ò¥í¡¼¥É¤¹¤ë¤¿¤á¡¢¥ê¥¿¡¼¥ó¤ò²¡¤·¤Æ¤¯¤À¤µ¤¤¡£¤½¤Î¸å¡¢²èÁü¤òÁªÂò¤¹¤ë¤¿¤á¤Ë¥ê¥¹¥È¥Ü¥Ã¥¯¥¹¤ÎÃæ¤Î¥Õ¥¡¥¤¥ë̾¤ò¥À¥Ö¥ë¥¯¥ê¥Ã¥¯¤·¤Æ²¼¤µ¤¤¡£"
+}
+msg.pack('side'=>'top')
+
+# frame À¸À®
+TkFrame.new($image2_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $image2_demo
+ $image2_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'image2'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# ÊÑ¿ôÀ¸À®
+$dirName = TkVariable.new([$demo_dir,'images'].join(File::Separator))
+
+# image À¸À®
+$image2a = TkPhotoImage.new
+
+# ¥Õ¥¡¥¤¥ë̾ÆþÎÏÉô
+TkLabel.new($image2_demo, 'text'=>'¥Ç¥£¥ì¥¯¥È¥ê:')\
+.pack('side'=>'top', 'anchor'=>'w')
+
+image2_e = TkEntry.new($image2_demo) {
+ width 30
+ textvariable $dirName
+}.pack('side'=>'top', 'anchor'=>'w')
+
+TkFrame.new($image2_demo, 'height'=>'3m', 'width'=>20)\
+.pack('side'=>'top', 'anchor'=>'w')
+
+TkLabel.new($image2_demo, 'text'=>'¥Õ¥¡¥¤¥ë:')\
+.pack('side'=>'top', 'anchor'=>'w')
+
+TkFrame.new($image2_demo){|w|
+ s = TkScrollbar.new(w)
+ l = TkListbox.new(w) {
+ width 20
+ height 10
+ yscrollcommand proc{|first,last| s.set first,last}
+ }
+ s.command(proc{|*args| l.yview(*args)})
+ l.pack('side'=>'left', 'expand'=>'yes', 'fill'=>'y')
+ s.pack('side'=>'left', 'expand'=>'yes', 'fill'=>'y')
+ #l.insert(0,'earth.gif', 'earthris.gif', 'mickey.gif', 'teapot.ppm')
+ l.insert(0,'earth.gif', 'earthris.gif', 'teapot.ppm')
+ l.bind('Double-1', proc{|x,y| loadImage $image2a,l,x,y}, '%x %y')
+
+ image2_e.bind 'Return', proc{loadDir l}
+
+}.pack('side'=>'top', 'anchor'=>'w')
+
+# image ÇÛÃÖ
+[ TkFrame.new($image2_demo, 'height'=>'3m', 'width'=>20),
+ TkLabel.new($image2_demo, 'text'=>'²èÁü:'),
+ TkLabel.new($image2_demo, 'image'=>$image2a)
+].each{|w| w.pack('side'=>'top', 'anchor'=>'w')}
+
+# ¥á¥½¥Ã¥ÉÄêµÁ
+def loadDir(w)
+ w.delete(0,'end')
+ Dir.glob([$dirName,'*'].join(File::Separator)).sort.each{|f|
+ w.insert('end',File.basename(f))
+ }
+end
+
+def loadImage(img,w,x,y)
+ img.file([$dirName, w.get("@#{x},#{y}")].join(File::Separator))
+end
+
diff --git a/ext/tk/sample/demos-jp/images/earth.gif b/ext/tk/sample/demos-jp/images/earth.gif
new file mode 100644
index 0000000000..3ae4a9ce01
--- /dev/null
+++ b/ext/tk/sample/demos-jp/images/earth.gif
Binary files differ
diff --git a/ext/tk/sample/demos-jp/images/earthris.gif b/ext/tk/sample/demos-jp/images/earthris.gif
new file mode 100644
index 0000000000..48f08c4421
--- /dev/null
+++ b/ext/tk/sample/demos-jp/images/earthris.gif
Binary files differ
diff --git a/ext/tk/sample/demos-jp/images/face.bmp b/ext/tk/sample/demos-jp/images/face.bmp
new file mode 100644
index 0000000000..03d829f4d1
--- /dev/null
+++ b/ext/tk/sample/demos-jp/images/face.bmp
@@ -0,0 +1,173 @@
+#define face_width 108
+#define face_height 144
+#define face_x_hot 48
+#define face_y_hot 80
+static char face_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x09,
+ 0x20, 0x80, 0x24, 0x05, 0x00, 0x80, 0x08, 0x00, 0x00, 0x00, 0x00, 0x88,
+ 0x24, 0x20, 0x80, 0x24, 0x00, 0x00, 0x00, 0x10, 0x80, 0x04, 0x00, 0x01,
+ 0x00, 0x01, 0x40, 0x0a, 0x09, 0x00, 0x92, 0x04, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x40, 0x12, 0x00, 0x00, 0x10, 0x40, 0x00, 0x00, 0x84,
+ 0x24, 0x40, 0x22, 0xa8, 0x02, 0x14, 0x84, 0x92, 0x40, 0x42, 0x12, 0x04,
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x52, 0x11, 0x00, 0x12, 0x00,
+ 0x40, 0x02, 0x00, 0x20, 0x00, 0x08, 0x00, 0xaa, 0x02, 0x54, 0x85, 0x24,
+ 0x00, 0x10, 0x12, 0x00, 0x00, 0x81, 0x44, 0x00, 0x90, 0x5a, 0x00, 0xea,
+ 0x1b, 0x00, 0x80, 0x40, 0x40, 0x02, 0x00, 0x08, 0x00, 0x20, 0xa2, 0x05,
+ 0x8a, 0xb4, 0x6e, 0x45, 0x12, 0x04, 0x08, 0x00, 0x00, 0x00, 0x10, 0x02,
+ 0xa8, 0x92, 0x00, 0xda, 0x5f, 0x10, 0x00, 0x10, 0xa1, 0x04, 0x20, 0x41,
+ 0x02, 0x00, 0x5a, 0x25, 0xa0, 0xff, 0xfb, 0x05, 0x41, 0x02, 0x04, 0x00,
+ 0x00, 0x08, 0x40, 0x80, 0xec, 0x9b, 0xec, 0xfe, 0x7f, 0x01, 0x04, 0x20,
+ 0x90, 0x02, 0x04, 0x00, 0x08, 0x20, 0xfb, 0x2e, 0xf5, 0xff, 0xff, 0x57,
+ 0x00, 0x04, 0x02, 0x00, 0x00, 0x20, 0x01, 0xc1, 0x6e, 0xab, 0xfa, 0xff,
+ 0xff, 0x05, 0x90, 0x20, 0x48, 0x02, 0x00, 0x04, 0x20, 0xa8, 0xdf, 0xb5,
+ 0xfe, 0xff, 0xff, 0x0b, 0x01, 0x00, 0x01, 0x00, 0x80, 0x80, 0x04, 0xe0,
+ 0xbb, 0xef, 0xff, 0xff, 0x7f, 0x01, 0x00, 0x04, 0x48, 0x02, 0x00, 0x20,
+ 0x80, 0xf4, 0x6f, 0xfb, 0xff, 0xff, 0xff, 0x20, 0x90, 0x40, 0x02, 0x00,
+ 0x00, 0x04, 0x08, 0xb8, 0xf6, 0xff, 0xff, 0xdf, 0xbe, 0x12, 0x45, 0x10,
+ 0x90, 0x04, 0x90, 0x00, 0x22, 0xfa, 0xff, 0xff, 0xff, 0xbb, 0xd7, 0xe9,
+ 0x3a, 0x02, 0x02, 0x00, 0x04, 0x90, 0x80, 0xfe, 0xdf, 0xf6, 0xb7, 0xef,
+ 0xbe, 0x56, 0x57, 0x40, 0x48, 0x09, 0x00, 0x04, 0x00, 0xfa, 0xf5, 0xdf,
+ 0xed, 0x5a, 0xd5, 0xea, 0xbd, 0x09, 0x00, 0x00, 0x40, 0x00, 0x92, 0xfe,
+ 0xbf, 0x7d, 0xb7, 0x6a, 0x55, 0xbf, 0xf7, 0x02, 0x11, 0x01, 0x00, 0x91,
+ 0x00, 0xff, 0xff, 0xaf, 0x55, 0x55, 0x5b, 0xeb, 0xef, 0x22, 0x04, 0x04,
+ 0x04, 0x00, 0xa4, 0xff, 0xf7, 0xad, 0xaa, 0xaa, 0xaa, 0xbe, 0xfe, 0x03,
+ 0x20, 0x00, 0x10, 0x44, 0x80, 0xff, 0x7f, 0x55, 0x12, 0x91, 0x2a, 0xeb,
+ 0xbf, 0x0b, 0x82, 0x02, 0x00, 0x00, 0xd1, 0x7f, 0xdf, 0xa2, 0xa4, 0x54,
+ 0x55, 0xfd, 0xfd, 0x47, 0x08, 0x08, 0x00, 0x21, 0xe4, 0xff, 0x37, 0x11,
+ 0x09, 0xa5, 0xaa, 0xb6, 0xff, 0x0d, 0x80, 0x00, 0x00, 0x04, 0xd0, 0xff,
+ 0x4f, 0x44, 0x20, 0x48, 0x55, 0xfb, 0xff, 0x27, 0x11, 0x02, 0x40, 0x40,
+ 0xe2, 0xfb, 0x15, 0x11, 0x4a, 0x55, 0x4a, 0x7d, 0xf7, 0x0f, 0x00, 0x00,
+ 0x04, 0x08, 0xf8, 0xdf, 0x52, 0x44, 0x01, 0x52, 0xb5, 0xfa, 0xff, 0x0f,
+ 0x49, 0x02, 0x00, 0x02, 0xe9, 0xf6, 0x0a, 0x11, 0xa4, 0x88, 0x4a, 0x6d,
+ 0xff, 0x5f, 0x00, 0x00, 0x10, 0x20, 0xf0, 0x2f, 0x21, 0x44, 0x10, 0x52,
+ 0xb5, 0xfa, 0xff, 0x0f, 0x44, 0x04, 0x80, 0x08, 0xf8, 0xab, 0x8a, 0x00,
+ 0x81, 0xa4, 0xd4, 0xd6, 0xfe, 0x2f, 0x00, 0x00, 0x04, 0x40, 0xb5, 0x2d,
+ 0x21, 0x08, 0x04, 0x90, 0xaa, 0xfa, 0xff, 0x1f, 0x11, 0x01, 0x00, 0x04,
+ 0xf0, 0x57, 0x0a, 0x22, 0x40, 0x4a, 0xda, 0x5e, 0xfb, 0x1f, 0x40, 0x00,
+ 0x40, 0x20, 0xba, 0x95, 0x90, 0x00, 0x01, 0xa0, 0xaa, 0xea, 0xff, 0x5f,
+ 0x02, 0x02, 0x00, 0x01, 0xe8, 0x57, 0x05, 0x00, 0x00, 0x12, 0xd5, 0xfe,
+ 0xfd, 0x1f, 0x48, 0x00, 0x04, 0x48, 0x7a, 0x95, 0x08, 0x02, 0x10, 0x40,
+ 0xaa, 0x55, 0xf7, 0x1f, 0x00, 0x09, 0x20, 0x00, 0xf8, 0x57, 0x22, 0x10,
+ 0x00, 0x28, 0xa9, 0xfa, 0xff, 0x5f, 0x02, 0x00, 0x00, 0x49, 0xdd, 0x29,
+ 0x01, 0x00, 0x80, 0x80, 0xaa, 0xd7, 0xff, 0x0f, 0x10, 0x00, 0x08, 0x00,
+ 0xf8, 0x96, 0x08, 0x00, 0x00, 0x20, 0x54, 0xfa, 0xee, 0x3f, 0x81, 0x04,
+ 0x40, 0x24, 0xfe, 0x55, 0x82, 0x00, 0x00, 0x82, 0xd2, 0xad, 0xff, 0x0f,
+ 0x08, 0x00, 0x04, 0x80, 0x6c, 0x97, 0x00, 0x00, 0x02, 0x20, 0xa9, 0xf6,
+ 0xdf, 0x5f, 0x00, 0x02, 0x20, 0x09, 0xfa, 0x49, 0x12, 0x00, 0x20, 0x84,
+ 0x54, 0xdb, 0xfe, 0x1f, 0x91, 0x00, 0x00, 0x00, 0xf8, 0x2b, 0x00, 0x20,
+ 0x00, 0x40, 0xa4, 0xf6, 0xbb, 0x1f, 0x04, 0x00, 0x44, 0x92, 0x7e, 0x95,
+ 0x02, 0x00, 0x00, 0x89, 0xaa, 0xdd, 0xff, 0x1f, 0x20, 0x09, 0x10, 0x00,
+ 0xf4, 0x57, 0x20, 0x01, 0x08, 0x20, 0xa9, 0x76, 0xff, 0x5f, 0x02, 0x00,
+ 0x00, 0x21, 0xfc, 0x4a, 0x05, 0x00, 0x01, 0x80, 0x54, 0xdb, 0xff, 0x1e,
+ 0x08, 0x02, 0x04, 0x08, 0xf9, 0x2b, 0x00, 0x00, 0x40, 0x28, 0xd2, 0xf6,
+ 0xff, 0xbf, 0x80, 0x00, 0x90, 0x00, 0xbc, 0x92, 0x08, 0x10, 0x00, 0x82,
+ 0x54, 0xdb, 0xff, 0x1f, 0x20, 0x00, 0x00, 0x44, 0xf9, 0x55, 0x02, 0x01,
+ 0x00, 0x20, 0xaa, 0xbd, 0xfd, 0x3f, 0x08, 0x04, 0x04, 0x10, 0xf4, 0x2a,
+ 0x01, 0x00, 0x22, 0x80, 0xd4, 0xf6, 0xff, 0x5f, 0x82, 0x00, 0x40, 0x02,
+ 0xf8, 0x55, 0x20, 0x00, 0x00, 0x50, 0x6a, 0xdf, 0xfe, 0x3f, 0x00, 0x00,
+ 0x00, 0x48, 0xe9, 0x4a, 0x05, 0x08, 0x00, 0xa5, 0xd5, 0xf5, 0xff, 0x3f,
+ 0x10, 0x01, 0x10, 0x01, 0xb0, 0xab, 0x92, 0x02, 0x40, 0xf8, 0xbf, 0xde,
+ 0xfe, 0x5f, 0x02, 0x04, 0x04, 0x48, 0xfa, 0xd4, 0x6f, 0x20, 0x84, 0xef,
+ 0xff, 0xfb, 0xff, 0x1f, 0x20, 0x00, 0x00, 0x00, 0xe0, 0xed, 0xbf, 0x0b,
+ 0xa1, 0x7e, 0xff, 0xbf, 0xfd, 0x5f, 0x04, 0x01, 0x20, 0x49, 0xd2, 0xfb,
+ 0xfe, 0x55, 0xd4, 0xff, 0xff, 0xf6, 0xff, 0x07, 0x00, 0x04, 0x00, 0x00,
+ 0xc0, 0xaa, 0xfb, 0x2b, 0xa2, 0xfe, 0xff, 0xdf, 0xee, 0x1f, 0x91, 0x00,
+ 0x82, 0xa4, 0xa4, 0xf5, 0xff, 0x57, 0xd5, 0xff, 0xbf, 0xfd, 0xff, 0x4d,
+ 0x00, 0x00, 0x20, 0x00, 0x88, 0x5b, 0xff, 0x2f, 0x69, 0xff, 0xff, 0xdb,
+ 0xfe, 0x1f, 0x24, 0x02, 0x00, 0x49, 0xa2, 0xd6, 0xff, 0x5f, 0xea, 0xff,
+ 0x7f, 0x7f, 0x7f, 0x0d, 0x00, 0x00, 0x10, 0x00, 0x40, 0xab, 0xf7, 0xbb,
+ 0xf0, 0xdf, 0xff, 0xd5, 0xff, 0xbf, 0x82, 0x04, 0x42, 0x24, 0x91, 0xd5,
+ 0xaa, 0xae, 0xd4, 0xaa, 0x52, 0x7b, 0xff, 0x15, 0x08, 0x00, 0x00, 0x01,
+ 0x04, 0x55, 0xd5, 0x55, 0x70, 0x5b, 0x75, 0xdd, 0xdf, 0x1f, 0x40, 0x00,
+ 0x08, 0x48, 0xa0, 0x4a, 0xa9, 0x56, 0xea, 0x56, 0xad, 0x6a, 0x7d, 0x9b,
+ 0x04, 0x01, 0x00, 0x02, 0x42, 0x2a, 0xd5, 0xaa, 0xa8, 0xaa, 0xaa, 0xfa,
+ 0xdf, 0x2f, 0x10, 0x04, 0x22, 0x48, 0x08, 0x45, 0x2a, 0x15, 0x68, 0x55,
+ 0x55, 0xd7, 0x76, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x40, 0x2a, 0x80, 0xa0,
+ 0xb2, 0x09, 0x48, 0xb9, 0xdf, 0x17, 0x22, 0x01, 0x00, 0x24, 0x45, 0x8a,
+ 0x24, 0x4a, 0x54, 0x51, 0x91, 0xf6, 0x6e, 0x4b, 0x00, 0x04, 0x90, 0x00,
+ 0x80, 0x52, 0x00, 0x20, 0x69, 0x05, 0xa4, 0xaa, 0xff, 0x1e, 0x48, 0x00,
+ 0x02, 0x92, 0x08, 0x05, 0x81, 0x94, 0xd4, 0x92, 0x40, 0xfd, 0xb6, 0x8b,
+ 0x00, 0x01, 0x40, 0x00, 0x82, 0x54, 0x00, 0x48, 0x68, 0x05, 0x90, 0xa4,
+ 0xef, 0x06, 0x24, 0x00, 0x08, 0x12, 0x10, 0x05, 0x00, 0x10, 0xb5, 0x01,
+ 0x42, 0xfb, 0xbf, 0x43, 0x00, 0x09, 0x00, 0x40, 0x81, 0xa8, 0x08, 0x4a,
+ 0xaa, 0x96, 0x90, 0xac, 0x6d, 0x15, 0x22, 0x00, 0x20, 0x09, 0x04, 0x15,
+ 0x80, 0x28, 0xdc, 0x01, 0x24, 0xfb, 0xbf, 0x01, 0x80, 0x04, 0x09, 0x00,
+ 0x40, 0x48, 0x02, 0x45, 0xb2, 0x2e, 0x41, 0x6d, 0xef, 0x05, 0x11, 0x00,
+ 0x40, 0x52, 0x02, 0x15, 0x29, 0x2a, 0xac, 0x42, 0x54, 0xfb, 0x3b, 0x51,
+ 0x84, 0x00, 0x08, 0x00, 0x20, 0x54, 0x80, 0x05, 0xb5, 0x3d, 0xa2, 0xb6,
+ 0xdf, 0x00, 0x20, 0x04, 0x20, 0x49, 0x89, 0xa8, 0x6a, 0x29, 0xac, 0xd6,
+ 0x54, 0xff, 0x3f, 0x84, 0x00, 0x01, 0x04, 0x10, 0x00, 0x94, 0xa8, 0x56,
+ 0xda, 0x5f, 0xab, 0xd5, 0x1e, 0x10, 0x48, 0x00, 0x90, 0x82, 0x48, 0xa8,
+ 0xb2, 0xac, 0xfd, 0x55, 0xd5, 0xfe, 0x9f, 0x80, 0x00, 0x0a, 0x02, 0x08,
+ 0x02, 0x55, 0x5a, 0x75, 0xff, 0xaf, 0xb6, 0xf7, 0x2d, 0x12, 0x92, 0x00,
+ 0x10, 0x20, 0x10, 0xa8, 0x54, 0xd5, 0xbf, 0x5d, 0xad, 0xdd, 0x0f, 0x00,
+ 0x00, 0x04, 0x40, 0x09, 0x84, 0xa8, 0xaa, 0x5a, 0xed, 0xeb, 0x6a, 0xff,
+ 0x9f, 0xa4, 0x24, 0x01, 0x02, 0xa0, 0x20, 0x50, 0x55, 0xd5, 0xbe, 0xae,
+ 0xad, 0xfd, 0x16, 0x00, 0x10, 0x04, 0x20, 0x0a, 0x08, 0xb4, 0xaa, 0x95,
+ 0xaa, 0x7b, 0xb7, 0xdb, 0x5f, 0x92, 0x04, 0x01, 0x84, 0x20, 0x21, 0x51,
+ 0xd5, 0x2a, 0xa9, 0xee, 0xd5, 0xfe, 0x0d, 0x00, 0x20, 0x04, 0x10, 0x00,
+ 0x08, 0x50, 0xe9, 0xd7, 0xd4, 0xfb, 0xb5, 0xff, 0x9f, 0x24, 0x09, 0x01,
+ 0x42, 0x4a, 0xa2, 0x64, 0xd5, 0x55, 0x7b, 0x7f, 0xda, 0x7d, 0x4f, 0x00,
+ 0x20, 0x04, 0x00, 0x80, 0x00, 0xa0, 0x2a, 0x13, 0x84, 0x6a, 0x55, 0xff,
+ 0x1d, 0x48, 0x8a, 0x00, 0x94, 0x24, 0x8a, 0xc8, 0xaa, 0x42, 0x20, 0x5d,
+ 0xf5, 0xff, 0x5f, 0x01, 0x00, 0x02, 0x01, 0x00, 0x20, 0xa2, 0x4a, 0x1a,
+ 0x82, 0x56, 0xda, 0xbd, 0x3f, 0x92, 0x92, 0x00, 0x90, 0x92, 0x00, 0x40,
+ 0x95, 0x6a, 0xf4, 0x55, 0x6d, 0xff, 0xd6, 0x00, 0x00, 0x0a, 0x04, 0x20,
+ 0x14, 0x49, 0x4b, 0xaa, 0xaa, 0x56, 0xf5, 0xff, 0xbf, 0xab, 0xa4, 0x00,
+ 0x20, 0x89, 0x40, 0x80, 0xaa, 0xaa, 0xaa, 0xaa, 0xde, 0xbf, 0xeb, 0x03,
+ 0x00, 0x02, 0x04, 0x02, 0x0a, 0x10, 0x2b, 0x2a, 0x55, 0x5b, 0xf5, 0xff,
+ 0xd7, 0x2f, 0x92, 0x00, 0x10, 0x28, 0x21, 0x01, 0x56, 0x95, 0xa0, 0x56,
+ 0xdf, 0xef, 0xea, 0x87, 0x40, 0x0a, 0x42, 0x41, 0x00, 0x90, 0xaa, 0x52,
+ 0xb6, 0xad, 0xfa, 0xff, 0xd5, 0x2f, 0x14, 0x00, 0x00, 0x04, 0x95, 0x04,
+ 0xaa, 0xac, 0x55, 0x6b, 0xff, 0xb7, 0xea, 0x9f, 0x40, 0x02, 0x28, 0x51,
+ 0x00, 0x40, 0x58, 0xd5, 0xda, 0xd6, 0x6e, 0x7f, 0xf9, 0x3f, 0x12, 0x04,
+ 0x02, 0x04, 0x49, 0x25, 0x55, 0xaa, 0x77, 0xab, 0xff, 0x2b, 0xfd, 0x3f,
+ 0x48, 0x01, 0x20, 0x41, 0x00, 0x00, 0x58, 0xa9, 0xda, 0xea, 0xfd, 0xaf,
+ 0xfa, 0xff, 0x02, 0x04, 0x08, 0x14, 0x29, 0x49, 0x52, 0x55, 0x55, 0x55,
+ 0xff, 0x8d, 0xfe, 0x3f, 0xa8, 0x00, 0x02, 0x41, 0x00, 0x02, 0xa0, 0xa2,
+ 0xaa, 0xea, 0xff, 0x53, 0xfd, 0xff, 0x02, 0x04, 0x50, 0x04, 0x25, 0xa8,
+ 0x54, 0x49, 0x52, 0xb5, 0xbf, 0x8a, 0xfe, 0xff, 0xa9, 0x08, 0x04, 0x50,
+ 0x80, 0x02, 0xa1, 0x2a, 0x95, 0xea, 0xff, 0xa1, 0xff, 0xff, 0x03, 0x02,
+ 0x90, 0x02, 0x09, 0x08, 0x44, 0x49, 0x52, 0xbd, 0x7f, 0xca, 0xff, 0xff,
+ 0x2b, 0x09, 0x04, 0x48, 0x40, 0x82, 0x90, 0x56, 0xa9, 0xf6, 0xbf, 0xd0,
+ 0xff, 0xff, 0x47, 0x00, 0x50, 0x02, 0x15, 0x11, 0x40, 0x95, 0xaa, 0xfd,
+ 0x2f, 0xe9, 0xff, 0xff, 0x8f, 0x0a, 0x84, 0x50, 0x40, 0x84, 0x14, 0xaa,
+ 0x6a, 0xff, 0x5f, 0xf2, 0xff, 0xff, 0x7f, 0x00, 0x10, 0x02, 0x09, 0x10,
+ 0x40, 0x7d, 0xf7, 0xff, 0x0b, 0xfc, 0xff, 0xff, 0xaf, 0x02, 0x84, 0x50,
+ 0x42, 0x85, 0x12, 0xd0, 0xdd, 0xff, 0xa7, 0xf2, 0xff, 0xff, 0xff, 0x04,
+ 0x00, 0x0a, 0x08, 0x10, 0x48, 0xf8, 0xff, 0xff, 0x0a, 0xfe, 0xff, 0xff,
+ 0x7f, 0x03, 0xa4, 0x80, 0xa2, 0x8a, 0x02, 0x68, 0xff, 0xff, 0x52, 0xfd,
+ 0xff, 0xff, 0xff, 0x07, 0x00, 0x2a, 0x08, 0x20, 0x28, 0xdc, 0xff, 0x5f,
+ 0x05, 0xff, 0xff, 0xff, 0xff, 0x0d, 0x92, 0x40, 0x22, 0x09, 0x02, 0xea,
+ 0xfb, 0xaf, 0x48, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x12, 0x81, 0xa0,
+ 0x48, 0x9c, 0x6e, 0x93, 0xa2, 0xff, 0xff, 0xff, 0xff, 0x07, 0xa8, 0x40,
+ 0x28, 0x0a, 0x02, 0x74, 0xb5, 0x45, 0x81, 0xff, 0xff, 0xff, 0xff, 0x0f,
+ 0x02, 0x0a, 0x81, 0x20, 0x08, 0xae, 0xaa, 0x90, 0xe8, 0xff, 0xff, 0xff,
+ 0xff, 0x0f, 0x90, 0x40, 0x28, 0x88, 0x12, 0x58, 0x15, 0x50, 0xd0, 0xff,
+ 0xff, 0xff, 0xff, 0x0f, 0x44, 0x0a, 0x41, 0x21, 0x08, 0xae, 0x04, 0x14,
+ 0xf0, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x10, 0x40, 0x14, 0x88, 0x04, 0xba,
+ 0x02, 0x28, 0xe8, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x42, 0x15, 0x41, 0x21,
+ 0x05, 0xad, 0x00, 0x05, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x10, 0x40,
+ 0x24, 0x8a, 0x0e, 0x36, 0x00, 0x0a, 0xf4, 0xff, 0xff, 0xff, 0xff, 0x0f,
+ 0x42, 0x25, 0x90, 0xd0, 0x8b, 0xc2, 0x41, 0x05, 0xfc, 0xff, 0xff, 0xff,
+ 0xff, 0x0f, 0x10, 0x08, 0x05, 0xe8, 0x8e, 0x58, 0x80, 0x02, 0xfa, 0xff,
+ 0xff, 0xff, 0xff, 0x0f, 0x4a, 0x20, 0xa8, 0xba, 0x0b, 0x2b, 0x51, 0x01,
+ 0xfe, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x8a, 0x02, 0xe8, 0xaf, 0x84,
+ 0x90, 0x04, 0xfd, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x52, 0x21, 0x54, 0xbf,
+ 0x1f, 0x15, 0xa5, 0x02, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x08,
+ 0x01, 0xfa, 0xb6, 0xa4, 0x52, 0x40, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f,
+ 0x4a, 0xa2, 0x54, 0xef, 0x5f, 0x4b, 0xa4, 0x80, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x0f, 0x80, 0x10, 0x82, 0xfe, 0xbf, 0x92, 0x52, 0x42, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x0f, 0x12, 0x42, 0xa8, 0xbf, 0x1f, 0x24, 0x80, 0xa0,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x84, 0x28, 0x8a, 0xf7, 0x37, 0x80,
+ 0x52, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x10, 0x82, 0xe0, 0xff,
+ 0x1f, 0x00, 0x20, 0xe1, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x84, 0x28,
+ 0xca, 0xff, 0x1f, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f,
+ 0x10, 0x42, 0xf0, 0xfd, 0x1b, 0x00, 0x50, 0xf0, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x0f, 0xa4, 0x10, 0xc5, 0xff, 0x1f, 0x00, 0x00, 0xe0, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x0f, 0x00, 0x22, 0xf8, 0xff, 0x0e, 0x00, 0x00, 0xf0,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xaa, 0x88, 0xe2, 0xff, 0x0f, 0x10,
+ 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x25, 0xfa, 0xff,
+ 0x0f, 0x01, 0x11, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xfb,
+ 0xfb, 0xff, 0x7f, 0x5d, 0xd5, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f};
diff --git a/ext/tk/sample/demos-jp/images/flagdown.bmp b/ext/tk/sample/demos-jp/images/flagdown.bmp
new file mode 100644
index 0000000000..55abc51825
--- /dev/null
+++ b/ext/tk/sample/demos-jp/images/flagdown.bmp
@@ -0,0 +1,27 @@
+#define flagdown_width 48
+#define flagdown_height 48
+static char flagdown_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xe1, 0x00, 0x00,
+ 0x00, 0x00, 0x70, 0x80, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x03, 0x00,
+ 0x00, 0x00, 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x04,
+ 0x00, 0x00, 0x03, 0x00, 0x06, 0x06, 0x00, 0x80, 0x01, 0x00, 0x06, 0x07,
+ 0x00, 0xc0, 0x1f, 0x00, 0x87, 0x07, 0x00, 0xe0, 0x7f, 0x80, 0xc7, 0x07,
+ 0x00, 0x70, 0xe0, 0xc0, 0xe5, 0x07, 0x00, 0x38, 0x80, 0xe1, 0x74, 0x07,
+ 0x00, 0x18, 0x80, 0x71, 0x3c, 0x07, 0x00, 0x0c, 0x00, 0x3b, 0x1e, 0x03,
+ 0x00, 0x0c, 0x00, 0x1f, 0x0f, 0x00, 0x00, 0x86, 0x1f, 0x8e, 0x07, 0x00,
+ 0x00, 0x06, 0x06, 0xc6, 0x05, 0x00, 0x00, 0x06, 0x00, 0xc6, 0x05, 0x00,
+ 0x00, 0x06, 0x00, 0xc6, 0x04, 0x00, 0x00, 0x06, 0x00, 0x06, 0x04, 0x00,
+ 0x7f, 0x06, 0x00, 0x06, 0xe4, 0xff, 0x00, 0x06, 0x00, 0x06, 0x04, 0x00,
+ 0x00, 0x06, 0x00, 0x06, 0x04, 0x00, 0x00, 0x06, 0x00, 0x06, 0x06, 0x00,
+ 0x00, 0x06, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x00, 0x86, 0x01, 0x00,
+ 0x00, 0x06, 0x00, 0xc6, 0x00, 0x00, 0x00, 0x06, 0x00, 0x66, 0x00, 0x00,
+ 0x00, 0x06, 0x00, 0x36, 0x00, 0x00, 0x00, 0x06, 0x00, 0x3e, 0x00, 0x00,
+ 0x00, 0xfe, 0xff, 0x2f, 0x00, 0x00, 0x00, 0xfc, 0xff, 0x27, 0x00, 0x00,
+ 0x00, 0x00, 0x88, 0x20, 0x00, 0x00, 0x00, 0x00, 0x88, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x88, 0x20, 0x00, 0x00, 0x00, 0x00, 0x88, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x88, 0x20, 0x00, 0x00, 0x00, 0x00, 0x88, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x88, 0x20, 0x00, 0x00, 0x00, 0x00, 0x88, 0x20, 0x00, 0x00,
+ 0xf7, 0xbf, 0x8e, 0xfc, 0xdf, 0xf8, 0x9d, 0xeb, 0x9b, 0x76, 0xd2, 0x7a,
+ 0x46, 0x30, 0xe2, 0x0f, 0xe1, 0x47, 0x55, 0x84, 0x48, 0x11, 0x84, 0x19};
diff --git a/ext/tk/sample/demos-jp/images/flagup.bmp b/ext/tk/sample/demos-jp/images/flagup.bmp
new file mode 100644
index 0000000000..6eb0d846a3
--- /dev/null
+++ b/ext/tk/sample/demos-jp/images/flagup.bmp
@@ -0,0 +1,27 @@
+#define flagup_width 48
+#define flagup_height 48
+static char flagup_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x7f, 0x00,
+ 0x00, 0x00, 0x00, 0xe0, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xef, 0x6a, 0x00,
+ 0x00, 0x00, 0xc0, 0x7b, 0x75, 0x00, 0x00, 0x00, 0xe0, 0xe0, 0x6a, 0x00,
+ 0x00, 0x00, 0x30, 0x60, 0x75, 0x00, 0x00, 0x00, 0x18, 0xe0, 0x7f, 0x00,
+ 0x00, 0x00, 0x0c, 0xe0, 0x7f, 0x00, 0x00, 0x00, 0x06, 0xe0, 0x04, 0x00,
+ 0x00, 0x00, 0x03, 0xe0, 0x04, 0x00, 0x00, 0x80, 0x01, 0xe0, 0x06, 0x00,
+ 0x00, 0xc0, 0x1f, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x7f, 0xe0, 0x07, 0x00,
+ 0x00, 0x70, 0xe0, 0xe0, 0x05, 0x00, 0x00, 0x38, 0x80, 0xe1, 0x04, 0x00,
+ 0x00, 0x18, 0x80, 0xf1, 0x04, 0x00, 0x00, 0x0c, 0x00, 0xfb, 0x04, 0x00,
+ 0x00, 0x0c, 0x00, 0xff, 0x04, 0x00, 0x00, 0x86, 0x1f, 0xee, 0x04, 0x00,
+ 0x00, 0x06, 0x06, 0xe6, 0x04, 0x00, 0x00, 0x06, 0x00, 0xe6, 0x04, 0x00,
+ 0x00, 0x06, 0x00, 0xe6, 0x04, 0x00, 0x00, 0x06, 0x00, 0x66, 0x04, 0x00,
+ 0x7f, 0x56, 0x52, 0x06, 0xe4, 0xff, 0x00, 0x76, 0x55, 0x06, 0x04, 0x00,
+ 0x00, 0x56, 0x57, 0x06, 0x04, 0x00, 0x00, 0x56, 0x55, 0x06, 0x06, 0x00,
+ 0x00, 0x56, 0xd5, 0x06, 0x03, 0x00, 0x00, 0x06, 0x00, 0x86, 0x01, 0x00,
+ 0x54, 0x06, 0x00, 0xc6, 0x54, 0x55, 0xaa, 0x06, 0x00, 0x66, 0xaa, 0x2a,
+ 0x54, 0x06, 0x00, 0x36, 0x55, 0x55, 0xaa, 0x06, 0x00, 0xbe, 0xaa, 0x2a,
+ 0x54, 0xfe, 0xff, 0x6f, 0x55, 0x55, 0xaa, 0xfc, 0xff, 0xa7, 0xaa, 0x2a,
+ 0x54, 0x01, 0x88, 0x60, 0x55, 0x55, 0xaa, 0xaa, 0x8a, 0xa0, 0xaa, 0x2a,
+ 0x54, 0x55, 0x8d, 0x60, 0x55, 0x55, 0xaa, 0xaa, 0x8a, 0xa0, 0xaa, 0x2a,
+ 0x54, 0x55, 0x8d, 0x60, 0x55, 0x55, 0xaa, 0xaa, 0x8a, 0xa0, 0xaa, 0x2a,
+ 0x54, 0x55, 0x8d, 0x50, 0x55, 0x55, 0xaa, 0xaa, 0x8a, 0xa8, 0xaa, 0x2a,
+ 0x54, 0x55, 0x95, 0x54, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x2a,
+ 0x54, 0x55, 0x55, 0x55, 0x55, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
diff --git a/ext/tk/sample/demos-jp/images/gray25.bmp b/ext/tk/sample/demos-jp/images/gray25.bmp
new file mode 100644
index 0000000000..b234b3cb0b
--- /dev/null
+++ b/ext/tk/sample/demos-jp/images/gray25.bmp
@@ -0,0 +1,6 @@
+#define grey_width 16
+#define grey_height 16
+static char grey_bits[] = {
+ 0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44,
+ 0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44,
+ 0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44};
diff --git a/ext/tk/sample/demos-jp/images/grey.25 b/ext/tk/sample/demos-jp/images/grey.25
new file mode 100644
index 0000000000..b234b3cb0b
--- /dev/null
+++ b/ext/tk/sample/demos-jp/images/grey.25
@@ -0,0 +1,6 @@
+#define grey_width 16
+#define grey_height 16
+static char grey_bits[] = {
+ 0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44,
+ 0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44,
+ 0x11, 0x11, 0x44, 0x44, 0x11, 0x11, 0x44, 0x44};
diff --git a/ext/tk/sample/demos-jp/images/grey.5 b/ext/tk/sample/demos-jp/images/grey.5
new file mode 100644
index 0000000000..37688893f0
--- /dev/null
+++ b/ext/tk/sample/demos-jp/images/grey.5
@@ -0,0 +1,6 @@
+#define grey_width 16
+#define grey_height 16
+static char grey_bits[] = {
+ 0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa,
+ 0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa,
+ 0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa};
diff --git a/ext/tk/sample/demos-jp/images/letters.bmp b/ext/tk/sample/demos-jp/images/letters.bmp
new file mode 100644
index 0000000000..0f12568d1a
--- /dev/null
+++ b/ext/tk/sample/demos-jp/images/letters.bmp
@@ -0,0 +1,27 @@
+#define letters_width 48
+#define letters_height 48
+static char letters_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xfe, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20,
+ 0x00, 0xfa, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2a,
+ 0x00, 0x3a, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2e,
+ 0xe0, 0xff, 0xff, 0xff, 0xff, 0x21, 0x20, 0x00, 0x00, 0x00, 0x00, 0x21,
+ 0xa0, 0x03, 0x00, 0x00, 0x70, 0x21, 0x20, 0x00, 0x00, 0x00, 0x50, 0x21,
+ 0xa0, 0x1f, 0x00, 0x00, 0x50, 0x21, 0x20, 0x00, 0x00, 0x00, 0x70, 0x21,
+ 0xfe, 0xff, 0xff, 0xff, 0x0f, 0x21, 0x02, 0x00, 0x00, 0x00, 0x08, 0x21,
+ 0xfa, 0x01, 0x00, 0x80, 0x0b, 0x21, 0x02, 0x00, 0x00, 0x80, 0x0a, 0x21,
+ 0xba, 0x01, 0x00, 0x80, 0x0a, 0x21, 0x02, 0x00, 0x00, 0x80, 0x0b, 0x21,
+ 0x3a, 0x00, 0x00, 0x00, 0x08, 0x21, 0x02, 0x00, 0x00, 0x00, 0x08, 0x21,
+ 0x02, 0xc0, 0xfb, 0x03, 0x08, 0x21, 0x02, 0x00, 0x00, 0x00, 0x08, 0x3f,
+ 0x02, 0xc0, 0xbd, 0x0f, 0x08, 0x01, 0x02, 0x00, 0x00, 0x00, 0x08, 0x01,
+ 0x02, 0xc0, 0x7f, 0x7b, 0x08, 0x01, 0x02, 0x00, 0x00, 0x00, 0x08, 0x01,
+ 0x02, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0xfe, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
diff --git a/ext/tk/sample/demos-jp/images/noletter.bmp b/ext/tk/sample/demos-jp/images/noletter.bmp
new file mode 100644
index 0000000000..5774124efe
--- /dev/null
+++ b/ext/tk/sample/demos-jp/images/noletter.bmp
@@ -0,0 +1,27 @@
+#define noletters_width 48
+#define noletters_height 48
+static char noletters_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x1f, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x07, 0x00,
+ 0x00, 0xf0, 0x0f, 0xe0, 0x1f, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x7f, 0x00,
+ 0x00, 0x3e, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x1f, 0x00, 0x00, 0xf0, 0x01,
+ 0x80, 0x07, 0x00, 0x00, 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0xe0, 0x07,
+ 0xe0, 0x01, 0x00, 0x00, 0xf0, 0x0f, 0xe0, 0x00, 0x00, 0x00, 0x78, 0x0e,
+ 0xf0, 0x00, 0x00, 0x00, 0x3c, 0x1e, 0x70, 0x00, 0x00, 0x00, 0x1e, 0x1c,
+ 0x38, 0x00, 0x00, 0x00, 0x0f, 0x38, 0x38, 0x00, 0x00, 0x80, 0x07, 0x38,
+ 0x3c, 0xfc, 0xff, 0xff, 0x7f, 0x78, 0x1c, 0x04, 0x00, 0xe0, 0x41, 0x70,
+ 0x1c, 0x04, 0x00, 0xf0, 0x40, 0x70, 0x1c, 0x74, 0x00, 0x78, 0x4e, 0x70,
+ 0x0e, 0x04, 0x00, 0x3c, 0x4a, 0xe0, 0x0e, 0x74, 0x03, 0x1e, 0x4a, 0xe0,
+ 0x0e, 0x04, 0x00, 0x0f, 0x4e, 0xe0, 0x0e, 0x04, 0x80, 0x07, 0x40, 0xe0,
+ 0x0e, 0x04, 0xf8, 0x0f, 0x40, 0xe0, 0x0e, 0x04, 0xe0, 0x01, 0x40, 0xe0,
+ 0x0e, 0x04, 0xf8, 0x00, 0x40, 0xe0, 0x0e, 0x04, 0x78, 0x00, 0x40, 0xe0,
+ 0x0e, 0x04, 0xfc, 0xf3, 0x40, 0xe0, 0x1c, 0x04, 0x1e, 0x00, 0x40, 0x70,
+ 0x1c, 0x04, 0x0f, 0x00, 0x40, 0x70, 0x1c, 0x84, 0x07, 0x00, 0x40, 0x70,
+ 0x3c, 0xfc, 0xff, 0xff, 0x7f, 0x78, 0x38, 0xe0, 0x01, 0x00, 0x00, 0x38,
+ 0x38, 0xf0, 0x00, 0x00, 0x00, 0x38, 0x70, 0x78, 0x00, 0x00, 0x00, 0x1c,
+ 0xf0, 0x3c, 0x00, 0x00, 0x00, 0x1e, 0xe0, 0x1e, 0x00, 0x00, 0x00, 0x0e,
+ 0xe0, 0x0f, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x07, 0x00, 0x00, 0x80, 0x07,
+ 0x80, 0x07, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x1f, 0x00, 0x00, 0xf0, 0x01,
+ 0x00, 0x3e, 0x00, 0x00, 0xf8, 0x00, 0x00, 0xfc, 0x01, 0x00, 0x7f, 0x00,
+ 0x00, 0xf0, 0x0f, 0xe0, 0x1f, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x07, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xf0, 0x1f, 0x00, 0x00};
diff --git a/ext/tk/sample/demos-jp/images/pattern.bmp b/ext/tk/sample/demos-jp/images/pattern.bmp
new file mode 100644
index 0000000000..df31baf789
--- /dev/null
+++ b/ext/tk/sample/demos-jp/images/pattern.bmp
@@ -0,0 +1,6 @@
+#define foo_width 16
+#define foo_height 16
+static char foo_bits[] = {
+ 0x60, 0x06, 0x90, 0x09, 0x90, 0x09, 0xb0, 0x0d, 0x4e, 0x72, 0x49, 0x92,
+ 0x71, 0x8e, 0x8e, 0x71, 0x8e, 0x71, 0x71, 0x8e, 0x49, 0x92, 0x4e, 0x72,
+ 0xb0, 0x0d, 0x90, 0x09, 0x90, 0x09, 0x60, 0x06};
diff --git a/ext/tk/sample/demos-jp/images/tcllogo.gif b/ext/tk/sample/demos-jp/images/tcllogo.gif
new file mode 100644
index 0000000000..3fc7720b17
--- /dev/null
+++ b/ext/tk/sample/demos-jp/images/tcllogo.gif
Binary files differ
diff --git a/ext/tk/sample/demos-jp/images/teapot.ppm b/ext/tk/sample/demos-jp/images/teapot.ppm
new file mode 100644
index 0000000000..78afefbf82
--- /dev/null
+++ b/ext/tk/sample/demos-jp/images/teapot.ppm
@@ -0,0 +1,56 @@
+P6
+256 256
+255
+\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À[7 eOLjQLmSMoTMnSMlRMhPL_9 \À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀnSMtVMzYN~[N~[N\N\O€\O€]O€]O€]O€]O€\O€\O}[NyYNtVM\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀG-wXN}[N€]O„^O†_O†`O‡`Oˆ`Oˆ`OˆaO‰aO‰aO‰aO‰aO‰aO‰aOˆaOˆ`O†_Oƒ^O\N \À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀaMLyYN…_O‰aP‹bPcPŽcPŽdPŽdPdPdPdPdPdPdPdPeP‘eP’eP’eP‘ePdPcP…_OpUM\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀwXN…_OdP“fP•gQ–hQ˜hQ˜iQ™iQ™iQšiQšiQšjQ›jQ›jQœjQœjQœjQœjQœjQ›jQœjQ™iQ“fP‡`O\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀNCJiQL‹bP—hQkQ¡mR¤nR¥oR¥oR¥oR¥oR¥oR¥oR¦oR¦oR¦pR¨pS©qSªqS«rS¬rS«rS©qS¤oRœjQ€]O\KK\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀfOLrUMcPŸlR©qS¯tS²uTµwT·xT¸xT¹yTºyT»zT»zU¼zU¼zU¼zU»zUºyT¸xT¶wT¯tS¡mR‰aOhPL\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\Àa0 cNLqUM€\O”fQ¦pS²wVºzV¿|VÂ}VÄVÆVÇ€VÉ‚WÌ…[Õeæ w÷³‹êª…Ĉg§qT“fQ{ZNYIK9\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀO1{G#‘JkRMqUMtVN–iS¨v\·€d¹bµzZ±vU°uT®sSªqS¤nRœjQ’eP„^OrUMHh>!T4\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀG-V5wE"~I#†M%U+¥e7²l:°g2®b*­a(­`(©^(¥])¡^-›]1ŠS,qC$`9 R3G-\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À@)J/i>!pA"tD"wF$yH&xH&tE$wE#yG%}M+ƒT4S5mE*Z7!K/B*;'\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À‰aO¦oR½{UÇ€VÏ…X<(F-a: e<!h>!j@#k@$h>"d<!c=$hD-fF2[<)K0@);'5$Ë‚VÇ€V¿|U_LKYIK\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À…_O·xTÉ‚Wó«€ûµ‹Ö’k¼|X×>µf-¨^(¡Z'šW&–T&œN>)F-J/b; g>#nD(jB&c<!b=%jH2_A/I0!<(8&5$”J¥Y’S%8&;'?)E,<:HA=HE?IJAISFJYIKXIK\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À£nRÁ}UܘqÊŠe±vU²e,™V&¥V†C
+€@ |> y< u: r9 o7 l6
+j5
+h4
+g3
+5$D,K/b; h>"wM1tK.e="a<#cA,U8&E-<(9&.!a0 b1 c1    
+
++3#@)46G<:HMCIXHK\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀU*´vT¿~X¸{YÃk+›W&‰N$|> u: p8 k5
+f3
+a0 _/ ]. [- I¡\*ª_(‘LkRMmSMmSMnSMnSMD,R3W5mA"|O0|P1j?"c<!a=%Y7"N1F,;'NCJNCJNDJODJODJODJh>!a: X/K%
+g3
+a0 Z- \/ T*Q(ŠHµm8kRMmSMnTMoTMpTMpUM15G15G05G04G04GpUMpTM5^9 d<!yF#O+€N,rC#qB"pB#k?"a: Z7 6ODJPDJPEJQEJQEJREJREJREJRFJSFJSFJSFJSFJe<!X/
+^/ V+Q(L&I$r9  TlRMnSM46G47G47G46G46G46G46G46G36G36G25G25G15G04G/4F.3F
+ˆ`O~[NqUM[- ‰HUGJUGJVGJVGJVHJWHJWHJWHKWHKXHKXHKXHKXHKXHKXIKXIKXIKXIKXIKh>!Y0
+
+L&C!:4
+X&pUMuWMwXNxXN<:H<:H<:H<:H<;H<;H<;H<;H=;H=;H=;H=;H>;H>;H?<H@<HA=HC>HG@ILBIREJ[JKcNLjQL§pR±uTºzUÃ~VÈWË‚XÖŽcäsÒŽe¼{V²vT¨pSžkR•gQŒbP†_O‚^O]O€\O€\O€\O€\O€]O]O]O]O]O]O]O]O]O]O]O€\O€\O~\N}[N|ZNxXN•T%H$
+›W&rVMvWNyYNzYN|ZN}[N}[N><H?<H?<H?<H?<H?<H@<H@<H@<HA=HA=HB=HC>HE?IG@IIAIKBIODJSFJWHK—hQŸlR§pR°b(¾i*Én+Ù|7Û|6Ïr,Íq+Êp-Ãl+»g)±b(®sS§pS lRšiQ•gQePcPŠaPˆaO‡`O‡`O†_O†_O…_O…_O…_O…_O…_O…_O…_O„_O„^O„^Oƒ^Oƒ^O‚]O]O€\O~[N{ZN•T%
+
+
+
+ 
+@%<-$G?@…pfdNLuWM\NdNL\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀTFJvWN‰aP./01„E}[N]O…_Oˆ`O‰aP‹bPŒbPcPcPŽcPdPdPdPeP‘eP’eP’eP“fP“fQ”fQ•gQ•gQ–gQ–hQ—hQ˜hQ™iQšiQ›jQœjQkQkRžlRŸlRžY&¤\'¨^'µ^½bÀcÃeÇi ÄgÀc½b¼a¹`µ^´]¯X¢[' Z'žY&¢mR¡mR¡mR lRŸlRŸlRžkRkQœkQœjQ›jQšjQšiQ™iQ™iQ˜iQ˜hQ—hQ—hQ—hQ–gQ–gQ•gQ•gQ•gQ”fQ”fQ“fQ“fP’eP‘ePdPcP‰aP—O
+ B\À\À\À\À\À\À\À\À\À\À%7!!C*F#P) {dYœze»p€\OgPL\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀSFJ`LKvWNŠaPm6
+ 
+\À\À\À\À\À\À\À\À\À B B
+$5 ¬`(¶e)£nRœjQƒ^OJAI\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀXIK^KKdNLhPLuWM‚]OŒbP”fQeP
+m6
+†`OŽcP“fQ—hQ˜hQ™iQšiQšjQ›jQ›jQ›jQœjQœjQœjQœkQkQkQkRžkRžkRžkRžlRŸlRŸlRŸlR lR lR lR¡mR¡mR¡mR¡mRºg)³c(²c(±b(­V¿cÂeÅi!Åi!Àd¼bº`¹`·_·_¶^¢Q§]'ª_(­`(¹f)£nR£nR£nR£nR£nR£nR£nR¢nR¢nR¢nR¢nR¢nR¢nR¢mR¢mR¢mR¢mR¢mR¢mR¢mR¢mR¢mR¢nR¢mR¢mR£nR¢mR¢mR¡mR mRkR—hQˆGa0 ŠbP mRœjQ“fQ‰aP}[NrUMmSM…L$\À\À\À\À\À\À\À\À B B
+#C, 8&H.Z7 §pR›jQ{ZN\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀQEJ[JK`LKdNLhQLqUM{ZN…_OŽcP–gQ—hQ
+‹bP‘eP–hQšiQ›jQœjQkQkQkRžkRžkRžlRžlRŸlRŸlRŸlRŸlRŸlR lR lR lR mR¡mR¡mR¡mR¡mR¡mR¢mR¢mR¢mR¢nR£nRÀj*ºg)·e)¶d)Âd°XÅgÅhÂe¿c½b½b¾bªU­`(®a(¯a(³c(¾i*¤oR¤oR¤nR¤nR¤nR¤nR¤nR¤nR¤nR¤nR¤nR¤nR¤nR¤nR¤nR¤nR¤nR¤oR¤oR¥oR¥oR¥oR¥oR¥oR¥oR¦oR¦oR¥oR¥oR¤nR¡mR›jQŽQ%Z- œjQ£nRŸlR—hQŽdP…_OuWMpTMnSMkRLa: \À\À\À\À\À\À\À B B&D2
+@*S6#G@IPDJ˜hQmSM\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀVGJ]KKbMLeOLiQLlRMvWN\OˆaO‘eP—hQœjQ•gQ
+\À\À\À\À\À B'D+E$(1 J/jH1NCJUGJYIKUGJ\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀXHK]KKbNLfOLiQLkRMmSMoTMqUMxXN\N†_OŒbP’fP˜hQkQ¡mR¥oR§pS¦pR˜hQ¢mR¥oR¨pSªqS«rS«rS«rS«rS«rS«rS«rS«rS«rS«rS«rSªrSªrSªrS«rS«rS«rS«rS«rS«rS«rS«rS«rS«rS«rS«rS«rS«rS«rS«rS¬rS¬rS¬rS¬rS¬rS¬rS¬rS¬sS¬sS­sS­sS­sS­sS­sS­sS®sS®sS®sS®sS®tS¯tS°tS°uS±uS±uT±uT²uT²uT²uT´vTµwT´vT³vT²uT¯tS¢mR¯tS±uT±uS®tS«rS§pR¢mRkQ—hQ‘ePŠaPƒ^O\N{ZNvXNqUMpTMnSMlRMP%\À\À\À\À B#C*E$.E- .!G$Y:%d<"SFJYIKZIKNCJ\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀPDJZIK_LKdNLgPLjQLlRMnSMpTMqUMuWMyYN€\O†`OcP’fP—hQœjQ¡mR¥oR¨qS«rS«rSªrS mR
+!C+E'0F.4F7%8%U/lG.SFJZIK]KKZIKB=H\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀREJZJK`LKdNLgPLjQLlRMnSMpTMqUMtWMxXN{ZN~[N]O„^O†`O‰aO‹bPdP•gQ™iQœkQ lR¤nR§pSªrS­sS¯tT²uT´vT¶wT·xT¹yT¹yTºyTºyT¹yT¶xT´vT¬rS¢nR—hQ¿|U¿|UÀ|UÀ|UÀ|UÀ|UÀ|UÀ|UÀ|UÀ|UÀ|UÀ|UÀ|UÀ|UÀ}UÀ}UÁ}UÁ}UÁ}UÁ}UÂ}UÂ~UÃ~UÃ~VÃ~VÄVÅ€WÆX®a(ŸlRªrS´vT¸yT¼zU¾|UÁ~VÃXÆ‚[Ɇ_΋dÓ‘jÔ“mÔ“nБlÊŒhĆd½_¶{[°vWªsU¦pS¢nRžkRšiQ˜hQ•gQ“fQ‘ePdPŒbP‰aO†_Oƒ^O€\O|ZNxXNsVMpTMnTMmSMjQL€C B)D&/F-3F47G6%>" Y7 kA$YIK]KK^KKSFJ\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀVGJ\KKbMLeOLhPLkRLmSMnTMpTMrUMuWNyYN|ZN\N‚]O„_O‡`OŠaPŒbPŽcPeP“fP—hQ›jQžlR¢nR¥oS©qT¬sT¯uU²vU´wV¶xV¸yV¹yUºzU»zU¼{U½{U¾{U¾|U¿|U¿|U¿|U¿|U¾{U½{U¼{U¼zU»zTºyT¹yT¸xTµwT³vT´vT´vT´vT´wT´wTµwT·xT¹yTºzT¼zU½{U¾{U¿|UÀ|UÂ}UÄVÅ€WÇ‚YÉ„\͈_ÑŒdÙ”láuç£|쩂ſt명æ¦ÞŸ{Õ—sËŽl†d¹^³yZ­uW¨qU¤oSŸlRžkRœjQšiQ˜hQ–gQ”fQ‘ePdPcPŠaP‡`O„^O]O}[NyYNuWMpTMoTMmSMkRLgPL&D#.E,3F46G;'<(D"iB(VGJ]KK`LK[JKB>H\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀNCJYIK^LKcNLfOLiQLkRMmSMoTMqUMsVMvXNzYN}[N€\O‚^O…_Oˆ`OŠaPŒcPdP‘eP“fQ•gQ—hQ™iQkR mS¤oT¨rU¬tW°wY´zZ¸}\»]¾€^À^Á‚^‚^Â\Á€ZÁYÁXÁ~WÁ~WÂ~VÂ~VÂ~VÃ~VÃ~UÃ~UÄ~UÄ~UÄUÄUÅVÅVÅVÅVÆVÆ€VÆ€VÇ€WÇWÈ‚XɃZË…[͇^ЊaÓdØ’iÜ—nâtè£zî©ó¯‡ø´û¸‘üº“û¹“÷¶ñ±Œé©…à¡~Ö˜vËmÇf»€`´z[®vX©rU¥pT£oS¢nS lRžkRœkRšjQ˜iQ–hQ”fQ’ePdPcP‹bPˆ`O…_O‚]O~[NzYNvWNpTMoTMnSMkRMhQLo7 ,2F36G99HC+@ ]8 nA"\JK`ML_LKSFJ\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀSFJ[JK`LKdNLgPLjQLlRMnSMpTMqUMtVMwXNzZN}[N€]Oƒ^O†_OˆaO‹bPcPdP‘eP“fQ•gQ—hQ™iQ›jRžlR mS£oU§rW¬vZ²{]¹€a¿…fÅŠjËnГqÓ•sÕ–sÕ–rÕ–qÕ”oÓ’mÑjÏgÍŠcˈaɆ^È„\Ç‚[ÆYÅ€XÅ€WÅWÅWÅVÅVÅWÅ€WÆ€WÇXÈ‚YɃ[Ê…\͇_ÏŠaÒeÕ‘hÙ•mÝ™qávä¡zç¤}꧀멃몄騃奀ߠ|Ù›wÓ•rÌmƉh¿„c¸~^²yZ®vX¬tWªsV¨qU¦pT¤oS¢nS mRžlRœkR›jQ™iQ—hQ•gQ“fPePŽcP‹bPˆaO…_O‚^O\N{ZNwXNsVMoTMnSMlRMiQL~I#26G99G?<HA*E$ i@$ZIKaMLbML[JK;:H\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀWHJ]KKbMLeOLhPLjRLlSMnTMpTMrUMuWMxXN{ZN~\N]O„^O†`O‰aO‹bPŽcPdP’eP”fQ–gQ˜hQšiQœkRžlS mT£oU¦rWªuZ¯y]´~aºƒfŠlË’sÔšzÜ¡€ã§†è«‰ë®‹í¯Œí®‹ë¬ˆè¨„ã£~ßžyÚ™tÖ•oÒjÎŒfˈbÈ…_ƃ\ÅZÄ€YÃXÂWÂ~WÂ~WÂ~WÃXÀXÄ€YÅZƃ\Ç…^Ɇ`ˈbÌŠdÍ‹fÎgÎŽiÎŽjÎŽjÍŽjËŒiljgÆd¿ƒaº^¸}]¶|\´{[²yZ°xY®vX¬tWªsV¨qU¦pT¤oS¢nS mRžlRkR›jQ™iQ—hQ•gQ“fP‘ePŽdPŒbP‰aO†_Oƒ^O€\O|ZNxXNtVMpTMnSMmSMjQLgPL99G?<HG-E&b;!YIK`MLdOM`LKNCJ\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀŸlRºyTÄ~UÊ‚XʃYÄXº{W­tUšW'¢[(—hQ lRcP€\OhQL\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀNCJYIK^LKcNLfOLiQLkRLmSMoTMqUMrVMvWNyYN|ZN\N‚]O„_O‡`O‰aPŒbPŽcPdP’fP”gQ–hQ˜iQšjRœkRžlS¡nT¤pU§sW«vZ°z]µb»„gŠlÉ‘sИyØžÞ¤…㩊è­ì±ï³‘ﳑ뭊穅⣀ݞzؘtÒ“nÎiɉdÆ…`Â]Á€[¿~Y¾}X½|W½|V¼{V¼{V¼{V¼{V¼{V¼|W¼|W½}X½}Y½~Z½~Z¼~Z»}[º}[º}[º~\º~\º~]º~]¹~]¸~]·}]¶|\´z[²yZ°wY®vX¬tWªsV¨rU¦pT¤oS¢nS mRŸlRkR›jQšiQ˜hQ–gQ“fQ‘ePdPŒcPŠaP‡`O„^O]O}[NyYNuWNpTMnTMmSMkRLhPL|H$D>IQ2P+XHK_LLfQOcNLXIK\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À©qSºyTÃ~VΈ`遲ޜv¾€]ªqS–LŽG|> g3
+S)?*%.—hQ—hQ‘eP‡`OuWM\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\ÀSFJ[JK`LKdNLgPLjQLlRMnSMoTMqUMsVMwXNzYN}[N€\O‚^O…_O‡`OŠaPŒbPŽdP‘eP“fP•gQ—hQ˜iQšjRœkRŸlS¡nT¤pV§sX«vZ°z^¶b¼…gËmÊ’sјzØŸ€Þ¤…㩊è­ê¯ë°ê¯Žè¬‹å¨‡à¤‚Ûž|Ö™wÑ“qÌŽlljgÃ…bÀ‚_½\»}Zº{X¹zW¸yV·yU·xU·xU·xT·xT·xU·xU·xU·yV·yV·yW¸zW¸{X¹{Y¹|Zº}[º}[º}\º~\¹~]¹~]¸}]·|\µ{\´z[²yZ°wY®vX¬tWªsV¨rU¦pT¤oS¢nS¡mRŸlRkRœjQšiQ˜hQ–gQ”fQ’ePdPcPŠbP‡`O…_O‚]O~[NzZNvWNrUMoTMmSMlRMiQLeOLJAIJ(h>!]KKfQOgQN_LKD>I\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À\À™iQ°tS¸yT¼{UÂYÎŒeï­ˆô´Õ—u¶|\ Z'™LˆD
+|>
+
++,!.! "`E6†iYŒlZo\“q]•s^^J™va›wbycŸzd {e¤}foTMqUMsVMuWNwXNyYN{ZN|ZN~[N\O]O‚]Oƒ^O…_O†_O‡`Oˆ`O‰aOŠaP‹bPŒbPŒcPcPŽcPŽdPdPdPdPeP‘eP‘eP‘eP’eP’eP’eP’eP’fP’fP’fP“fP’fP’fP’fP’eP’eP’eP‘eP‘eP‘ePePdPdPdPŽdPŽcPcPŒcPŒbP‹bPŠaP‰aOˆ`O‡`O†_O…_Oƒ^O‚]O]O\O~[N|[N{ZNyYNwXN®ƒi¬ƒiª‚i¨i¦€hŒhR‰fQ†dQ‚bP•wfx]Oˆpdkbtd_m`]OEDG?A;:@.S….S….S….S….S…/S…/S…/S…/S…/S…/S…/S…/S…TxªTxªTxªTxªTxªTx«Tx«Tx«Ty«/S†GlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlž…ªÜ…ªÜ…ªÜHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlž…ªÜ…ªÜ£Ö£Ö£Ö£Ö¤Ö¤Ö¤Ö¤Ö¤ÖEi›€¤Ö€¤Ö€¤Ö€¤Ö€¤Ö€¤Ö€¤Ö€¤Ö€¤Ö€¤Ö€¤Ö€¤Ö€¤×€¤×€¤×€¤×€¥×€¥×€¥×Bg™Bg™Bg™Bg™Bg™&J|&J|&J|&J|&J|&J|&J|&J|&J|&J|&J|&J|&J|Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Af˜Af˜%J|%J|%J|%J|%J|%J|%J|%J|%J|%J|%I|%I|%I|%I|%I|%I|
++,YA5jPBpSD‹l[o]’q^–t`‚_Kšwbœycžze {f¡}g¤h¨i”lSrVMtWMvWNxXNyYN{ZN|[N~[N\O]O‚]Oƒ^O„_O…_O†`O‡`Oˆ`O‰aPŠaP‹bP‹bPŒbPcPcPŽcPŽcPdPdPdPdPdPdPdPdPePePePePePdPdPdPdPdPdPdPŽcPŽcPcPcPŒbP‹bP‹bPŠaP‰aOˆ`O‡`O†`O…_O„^Oƒ^O‚]O€]O\O~[N|[N{ZNyYNxXN°…j®„j¬„jªƒj¨‚j¦€jŒhSŠgS†eRƒcR|`QŒsf…oe}jcrd`k_]LCDC=@,,3(4F(4F.S….S…/S…/S…/S…/S…/S…/S…/S…TxªTxªTxªTxªTxªTxªTx«Tx«Tx«Ty«Ty«Ty«…ªÜHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlž†ªÜHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžChšChš¤Ö€¤Ö€¤Ö€¤Ö€¤ÖEi›Ei›Ei›€¤Ö€¤Ö€¤Ö€¤Ö€¤Ö€¤Ö€¤×€¤×€¤×€¤×Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Bg™Bg™&J|&J|&J|&J|&J|&J|&J|&J|&J|&J|Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜&J|%J|%J|%J|%J|%J|%J|%J|%J|%J|%J|%J|%I|%I|%I|%I|%I|#5H71O;3V?4iOBoSDsVFo]{[I^Kƒ`L…bN‡dOŸ{f }g¢~h¥€j’kT•mU˜oVšqWrWwXNxXNzYN{ZN}[N~[N\O€]O‚]Oƒ^O„^O…_O…_O†`O‡`Oˆ`O‰aO‰aPŠaP‹bP‹bPŒbPŒbPŒcPcPcPcPŽcPŽcPŽcPŽcPŽcPŽcPŽcPŽcPŽcPŽcPcPcPcPŒcPŒbP‹bP‹bP‹bPŠaP‰aP‰aOˆ`O‡`O†_O…_O„_O„^Oƒ^O]O€\O\N~[N|ZN{ZNyYN›oTšoT™oT—nT¬„lªƒl¨‚ljUŒiTŠhT†fT€cSvi‰rgnfyidqdah^^HBD?<@)+3OZkMYk(5F(5F(5F/S…/S…/S…/S…/S…TxªTxªTxªTxªTxªTxªTx«Tx«Ty«Ty«Ty«Ty«Uy«†ªÜ†ªÜ†ªÜ†ªÜHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlž†ªÜ†ªÜ†ªÜ†ªÜ†ªÜHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžHlžDhšDhšDhšChš&K}&K}&K}&K}&K}&K}ChšChšCgšCgšCgšCgšCgšCgšCg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™&J|&J|&J|&J|&J|&J|Bg™Bg™Bg™Bf™Bf™Bf™Bf™Bf™Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜&J|&J|&J|&J|%J|%J|%J|%J|%J|%J|%J|%J|%J|%J|%J|%I|%I|A99N?;L:2T>4gNBlRD‡k\‹n^z[J~^LaN…cO‡dP‰fQŠgRŒhTjU’lV•nW˜pXšrXsY¶‹q¸qºŽr¼r½r¿s©z[©z[ªz[«{[¬{[¬{ZÅ“rÅ’qÅ’qÅ’pÅ’pÅ‘o­yV­xV¬xU¬wT¬wTŠaPŠbP‹bP‹bP‹bP‹bP‹bP‹bP‹bP‹bP‹bP‹bP‹bP‹bPŠaPŠaPŠaP‰aP‰aOˆaOˆ`O‡`O‡`O†_O…_O„^Oƒ^O‚^O‚]O]O€\O~\N}[N|ZNzYNpTœpU›pUšpU˜oV—oV•nV“mV‘lVkVŒjVˆhVƒfU~cUuj†qh~mfugdkaad\^E@D98?$(2minffm^blV^lMYk(5F(5F/S…TxªTxªTxªTxªTxªTxªTxªTx«Tx«Ty«Ty«Ty«Uy«Uy«†ªÜ†ªÜ†ªÜ†ªÜ†ªÜ†ªÜ†ªÜ†ªÜHlžHlžHlžHlžHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸ†ªÝ†ªÝ†ªÝ†ªÝ†ªÝ†ªÝ†ªÝ†ªÝ†ªÝ†ªÝHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸHlŸ'K}'K}'K}'K}'K}'K}'K}'K}'K}&K}&K}ChšChšChšChšChšChšChšCgšCgšCgšCgšCgšCgšCg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™&J|Cg™Cg™Cg™Cg™Cg™Bg™Bg™Bg™Bg™Bg™Bg™Bf™Bf™Bf™Bf™Bf™Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜Bf˜&J|&J|&J|&J|&J|&J|&J|%J|%J|%J|%J|%J|%J|%J|Ae˜Ae˜;GY<68I=:I82Q=4XA6~fZ„j\‰m^p`|]L€`NƒcP†eQˆgS¡j£€l¦‚m©„n•oX˜qYšrZt[¶Œr¸sºs¼t½t¾‘t¨z]©{]ª{]«{\«{\¬{\¬{[Ä“sÄ“rÄ’rÄ’qÄ’pÄ‘p¬yWÄoÃnÃmÃlÂŽlÂŽkÁkˆaOˆaOˆaOˆaOˆaOˆaOˆaOˆ`Oˆ`O‡`O‡`O‡`O†`O†_O…_O…_O„_O„^Oƒ^O‚]O]O€]O\O~\N}[N|ZN¶‰l¶‰lµˆmœqV›qVšqV™pW˜pW–oW¬…nª…n§„n¤‚nŸ€n›~n€eW‘xlŠtk‚piykfodcf_`JDG@>C*,5$1MYktr~tstmolinadmX_lNZkMZkTxªTxªTxªTxªTx«Tx«Tx«Ty«Ty«Ty«Uy«Uy«Uy«†ªÝ†ªÝ†ªÝ†ªÝ†ªÝ†ªÝ†«Ý†«Ý†«Ý†«ÝHlŸHlŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸ†«Ý†«Ý†«Ý‡«Ý‡«Ý‡«Ý‡«Ý‡«Ý‡«Ý‡«Ý‡«Ý‡«Ý‡«ÝHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸHmŸ'K}'K}'K}'K}'K}'K}'K}'K}'K}'K}'K}'K}DhšDhšDhšDhšChšChšChšChšChšChšChšCgšCgšCgšCgšCgšCgšCg™Cg™Cg™Cg™Cg™&J}&J}&J}Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Cg™Bg™Bg™Bg™Bg™Bg™Bg™Bf™Bf™Bf™Bf™Bf˜Bf˜Bf˜Bf˜Bf˜&J|&J|&J|&J|&J|&J|&J|&J|&J|&J|Af˜Af˜Af˜Af˜;GY;GY;GY1'!D:9N?;N;3]I?zdY€h[†l^‹oasc“ue€bQ„dR‡fT l¢m¦ƒn©…o«‡p®ˆq±Šr³‹sžv] w]¹u»u¼‘u¾‘u¿’v¨{^©{^ª|^«|]«|]«{\¬{\¬{[¬{[¬zZ«zZ«yY«yX«xXÂoÂnÂnÁŽmÁŽm¨uT¨uS§tS§tS§tR¦sR¦sQ…_O…_O…_O„^O„^Oƒ^Oƒ^O‚^O‚]O]O€]O¢rS¡rS¡rS¸‰k·‰l·‰l¶‰m¶‰mµ‰m´‰n³‰n›qWšqX™qX®‡o­‡o«†p¨…p¤ƒp pœp—}o{cXv`Vp]U}nishfhaba\_DAF::B$)4
+., 7(8'A1&F4(L8*oXIw]Jpdasfcvhexkg{mi~oj€qll\Xn^Yp`Zpa[qa\rb]rc^sc^sd_ue`wf`xgayhayhayhbxy‘y‘y‘y‘yy~ywgbvfateasd`qd`pc`nb_la_€ut|ssxqrunpZUXVRWROUMMSHIRIC@967-/3'+0(*-ACF?AD;=@#%(
+.+>1(B3)B2&F4'E4)gTGlXJs^OzcTzaPqfethgvjhbVTcWUdXVeYWfZXg[Yh\Zi]Zi][j^\€us€ususts~tt~tt}tt|st{stut~tt|sszrsyqrwpquoqsmpqloXTXTQWPOULLSSJEA<:=99757335./2113)+.'),)+.8:="(
+"6*#5*">2)>0&A2'C3(I8-^OFbRHfUJjXMq^RwcVzfYfRDfQCdN@zdTqijrjksklrklrklrklqjmpjmpjmojmojmnimmimkhliflscYm`Xg\VbYT^VRE>;A<:>98:77645:873220/0,-/)+.*,/#%( &
+
+&3#.$-% .% .& /&!,#,#@70A71XNHXNHWNHWNHZRLYQLYQLXQLWQLWPLUOLSNLQMKOLJMJJ0//.-.,,-&(+"(!'
+
+ %' %$#" ! !$
+
+
+ 
+
+
+*  
+  ;?E7CU;HY=I[ 
diff --git a/ext/tk/sample/demos-jp/items.rb b/ext/tk/sample/demos-jp/items.rb
new file mode 100644
index 0000000000..84e561f27c
--- /dev/null
+++ b/ext/tk/sample/demos-jp/items.rb
@@ -0,0 +1,372 @@
+#
+# canvas item types widget demo (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($items_demo) && $items_demo
+ $items_demo.destroy
+ $items_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$items_demo = TkToplevel.new {|w|
+ title("Canvas Item Demonstration")
+ iconname("Items")
+ positionWindow(w)
+}
+
+# label À¸À®
+TkLabel.new($items_demo) {
+ font $font
+ wraplength '5i'
+ justify 'left'
+ text "¤³¤Î¥¦¥£¥ó¥É¥¦¤Ë¤Ï¥­¥ã¥ó¥Ð¥¹ widget ¤¬Æþ¤Ã¤Æ¤ª¤ê¡¢¤½¤ÎÃæ¤Ë¤Ï¥­¥ã¥ó¥Ð¥¹ widget ¤¬¥µ¥Ý¡¼¥È¤¹¤ëÍÍ¡¹¤Ê¥¿¥¤¥×¤Î¥¢¥¤¥Æ¥à¤ÎÎ㤬Æþ¤Ã¤Æ¤¤¤Þ¤¹¡£¼¡¤Î¤è¤¦¤ÊÁàºî¤¬¤Ç¤­¤Þ¤¹¡£\n ¥Ü¥¿¥ó-1 ¥É¥é¥Ã¥°:\t¥¢¥¤¥Æ¥à¤òư¤«¤¹¡£\n ¥Ü¥¿¥ó-2 ¥É¥é¥Ã¥°:\t¸«¤¨¤Æ¤¤¤ëÉôʬ¤ò¤º¤é¤¹¡£\n ¥Ü¥¿¥ó-3 ¥É¥é¥Ã¥°:\tÎΰè¤ò°Ï¤¦¡£\n ¥³¥ó¥È¥í¡¼¥ë-F:\tÎΰè¤Î²¼¤Î¥¢¥¤¥Æ¥à¤òɽ¼¨¤¹¤ë¡£"
+}.pack('side'=>'top')
+
+# frame À¸À®
+TkFrame.new($items_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $items_demo
+ $items_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'items'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame À¸À®
+cvs = nil
+TkFrame.new($items_demo) {|cf|
+ # canvas À¸À®
+ cvs = TkCanvas.new(cf) {|c|
+ focus
+ scrollregion '0c 0c 30c 24c'
+ width '15c'
+ height '10c'
+ relief 'sunken'
+ borderwidth 2
+
+ hs = TkScrollbar.new(cf) {|s|
+ orient 'horizontal'
+ command proc{|*args| c.xview(*args)}
+ c.xscrollcommand proc{|first,last| s.set first,last}
+ }
+
+ vs = TkScrollbar.new(cf) {|s|
+ command proc{|*args| c.yview(*args)}
+ c.yscrollcommand proc{|first,last| s.set first,last}
+ }
+
+ if $tk_version =~ /^4\.[01]/
+ hs.pack('side'=>'bottom', 'fill'=>'x')
+ vs.pack('side'=>'right', 'fill'=>'y')
+ c.pack('in'=>cf, 'expand'=>'yes', 'fill'=>'both')
+
+ else
+ c.grid('in'=>cf, 'row'=>0, 'column'=>0,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ vs.grid('row'=>0, 'column'=>1,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ hs.grid('row'=>1, 'column'=>0,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ TkGrid.rowconfigure(cf, 0, 'weight'=>1, 'minsize'=>0)
+ TkGrid.columnconfigure(cf, 0, 'weight'=>1, 'minsize'=>0)
+
+ end
+
+ }
+}.pack('side'=>'top', 'fill'=>'both', 'expand'=>'yes')
+
+# Display a 3x3 rectangular grid
+TkcRectangle.new(cvs, '0c', '0c', '30c', '24c', 'width'=>2)
+TkcLine.new(cvs, '0c', '8c', '30c', '8c', 'width'=>2)
+TkcLine.new(cvs, '0c', '16c', '30c', '16c', 'width'=>2)
+TkcLine.new(cvs, '10c', '0c', '10c', '24c', 'width'=>2)
+TkcLine.new(cvs, '20c', '0c', '20c', '24c', 'width'=>2)
+
+font1 = '-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*'
+font2 = '-Adobe-Helvetica-Bold-R-Normal--*-240-*-*-*-*-*-*'
+if TkWinfo.depth($root).to_i > 1
+ blue = 'DeepSkyBlue3'
+ red = 'red'
+ bisque = 'bisque3'
+ green = 'SeaGreen3'
+else
+ blue = 'black'
+ red = 'black'
+ bisque = 'black'
+ green = 'black'
+end
+
+# tag ¥ª¥Ö¥¸¥§¥¯¥È¤òÀ¸À®
+$tag_item = TkcGroup.new(cvs)
+
+# Set up demos within each of the areas of the grid.
+TkcText.new(cvs, '5c', '.2c', 'text'=>'¥é¥¤¥ó', 'anchor'=>'n')
+TkcLine.new(cvs, '1c', '1c', '3c', '1c', '1c', '4c', '3c', '4c',
+ 'width'=>2, 'fill'=>blue, 'capstyle'=>'butt',
+ 'join'=>'miter', 'tags'=>$tag_item )
+TkcLine.new(cvs, '4.67c','1c','4.67c','4c', 'arrow'=>'last', 'tags'=>$tag_item)
+TkcLine.new(cvs, '6.33c','1c','6.33c','4c', 'arrow'=>'both', 'tags'=>$tag_item)
+TkcLine.new(cvs, '5c','6c','9c','6c','9c','1c','8c','1c','8c','4.8c','8.8c',
+ '4.8c','8.8c','1.2c','8.2c','1.2c','8.2c','4.6c','8.6c','4.6c',
+ '8.6c','1.4c','8.4c','1.4c','8.4c','4.4c',
+ 'width'=>3, 'fill'=>red, 'tags'=>$tag_item )
+TkcLine.new(cvs, '1c','5c','7c','5c','7c','7c','9c','7c', 'width'=>'.5c',
+ 'stipple'=>'@'+[$demo_dir,
+ 'images','gray25.bmp'].join(File::Separator),
+ 'arrow'=>'both', 'arrowshape'=>'15 15 7', 'tags'=>$tag_item )
+TkcLine.new(cvs, '1c','7c','1.75c','5.8c','2.5c','7c','3.25c','5.8c','4c','7c',
+ 'width'=>'.5c', 'capstyle'=>'round', 'join'=>'round',
+ 'tags'=>$tag_item )
+
+TkcText.new(cvs, '15c', '.2c',
+ 'text'=>'¶ÊÀþ (³ê¤é¤«¤Ë¤Ä¤Ê¤¤¤ÀľÀþ)', 'anchor'=>'n')
+TkcLine.new(cvs, '11c','4c','11.5c','1c','13.5c','1c','14c','4c',
+ 'smooth'=>'on', 'fill'=>blue, 'tags'=>$tag_item )
+TkcLine.new(cvs, '15.5c','1c','19.5c','1.5c','15.5c','4.5c','19.5c','4c',
+ 'smooth'=>'on', 'arrow'=>'both', 'width'=>3, 'tags'=>$tag_item )
+TkcLine.new(cvs, '12c','6c','13.5c','4.5c','16.5c','7.5c','18c','6c',
+ '16.5c','4.5c','13.5c','7.5c','12c','6c',
+ 'smooth'=>'on', 'width'=>'3m', 'capstyle'=>'round',
+ 'stipple'=>'@'+[$demo_dir,
+ 'images', 'gray25.bmp'].join(File::Separator),
+ 'fill'=>red, 'tags'=>$tag_item )
+
+TkcText.new(cvs, '25c', '.2c', 'text'=>'¿³Ñ·Á', 'anchor'=>'n')
+TkcPolygon.new(cvs, '21c','1.0c','22.5c','1.75c','24c','1.0c','23.25c','2.5c',
+ '24c','4.0c','22.5c','3.25c','21c','4.0c','21.75c','2.5c',
+ 'fill'=>'green', 'outline'=>'black', 'width'=>4,
+ 'tags'=>$tag_item )
+TkcPolygon.new(cvs, '25c','4c','25c','4c','25c','1c','26c','1c','27c','4c',
+ '28c','1c','29c','1c','29c','4c','29c','4c',
+ 'fill'=>red, 'smooth'=>'on', 'tags'=> $tag_item)
+TkcPolygon.new(cvs, '22c','4.5c','25c','4.5c','25c','6.75c','28c','6.75c',
+ '28c','5.25c','24c','5.25c','24c','6.0c','26c','6c','26c',
+ '7.5c','22c','7.5c',
+ 'stipple'=>'@' + [$demo_dir,
+ 'images', 'gray25.bmp'].join(File::Separator),
+ 'outline'=>'black', 'tags'=>$tag_item )
+
+TkcText.new(cvs, '5c', '8.2c', 'text'=>'¶ë·Á', 'anchor'=>'n')
+TkcRectangle.new(cvs, '1c','9.5c','4c','12.5c',
+ 'outline'=>red, 'width'=>'3m', 'tags'=>$tag_item)
+TkcRectangle.new(cvs, '0.5c','13.5c','4.5c','15.5c',
+ 'fill'=>green, 'tags'=>$tag_item )
+TkcRectangle.new(cvs, '6c','10c','9c','15c', 'outline'=>'',
+ 'stipple'=>'@'+[$demo_dir,
+ 'images','gray25.bmp'].join(File::Separator),
+ 'fill'=>blue, 'tags'=>$tag_item )
+
+TkcText.new(cvs, '15c', '8.2c', 'text'=>'Âʱß', 'anchor'=>'n')
+TkcOval.new(cvs, '11c','9.5c','14c','12.5c',
+ 'outline'=>red, 'width'=>'3m', 'tags'=>$tag_item)
+TkcOval.new(cvs, '10.5c','13.5c','14.5c','15.5c',
+ 'fill'=>green, 'tags'=>$tag_item )
+TkcOval.new(cvs, '16c','10c','19c','15c', 'outline'=>'',
+ 'stipple'=>'@'+[$demo_dir,
+ 'images','gray25.bmp'].join(File::Separator),
+ 'fill'=>blue, 'tags'=>$tag_item )
+
+TkcText.new(cvs, '25c', '8.2c', 'text'=>'¥Æ¥­¥¹¥È', 'anchor'=>'n')
+TkcRectangle.new(cvs, '22.4c','8.9c','22.6c','9.1c')
+TkcText.new(cvs, '22.5c', '9c', 'anchor'=>'n', 'font'=>font1, 'width'=>'4c',
+ 'text'=>'û¤¤¥Æ¥­¥¹¥È¡£¥ï¡¼¥É¥é¥Ã¥×¡¢º¸Â·¤¨¡¢¥¢¥ó¥«¡¼¤ÏËÌ (¾å)¡£\
+¢¢¤Ï³Æ¥Æ¥­¥¹¥È¤Î¥¢¥ó¥«¡¼¥Ý¥¤¥ó¥È¤ò¼¨¤¹¡£', 'tags'=>$tag_item )
+TkcRectangle.new(cvs, '25.4c','10.9c','25.6c','11.1c')
+TkcText.new(cvs, '25.5c', '11c', 'anchor'=>'w', 'font'=>font1, 'fill'=>blue,
+ 'text'=>'¤¤¤¯¤Ä¤«¤Î¹Ô¡£\n¤½¤ì¤¾¤ìÆÈΩ¤Ë\n¹Ô·¤¨¡£\n\
+Á´¤Æº¸Ã¼¤¬¥¢¥ó¥«¡¼¤µ¤ì¤Æ¤¤¤ë¡£', 'justify'=>'center', 'tags'=>$tag_item )
+TkcRectangle.new(cvs, '24.9c','13.9c','25.1c','14.1c')
+if $tk_version =~ /^4\.[01]/
+ TkcText.new(cvs, '25c', '14c', 'anchor'=>'c', 'font'=>font2, 'fill'=>red,
+ 'stipple'=>'@' + [$demo_dir,
+ 'images', 'grey.5'].join(File::Separator),
+ 'text'=>'Stippled characters', 'tags'=>$tag_item )
+else
+ TkcText.new(cvs, '25c', '14c', 'anchor'=>'c', 'font'=>font2, 'fill'=>red,
+ 'stipple'=>'gray50', 'text'=>'Stippled characters',
+ 'tags'=>$tag_item )
+end
+
+TkcText.new(cvs, '5c', '16.2c', 'text'=>'¸Ì', 'anchor'=>'n')
+TkcArc.new(cvs, '0.5c','17c','7c','20c', 'fill'=>green, 'outline'=>'black',
+ 'start'=>45, 'extent'=>270, 'style'=>'pieslice', 'tags'=>$tag_item)
+#TkcArc.new(cvs, '6.5c','17c','9.5c','20c', 'width'=>'4m', 'style'=>'arc',
+# 'outline'=>blue, 'start'=>135, 'extent'=>270,
+# 'outlinestipple'=>'@' + ['images', 'grey.25'].join(File::Separator),
+# 'tags'=>$tag_item)
+TkcArc.new(cvs, '6.5c','17c','9.5c','20c', 'width'=>'4m', 'style'=>'arc',
+ 'outline'=>blue, 'start'=>135, 'extent'=>270,
+ 'outlinestipple'=>'@'+[$demo_dir,
+ 'images','gray25.bmp'].join(File::Separator),
+ 'tags'=>$tag_item)
+TkcArc.new(cvs, '0.5c','20c','9.5c','24c', 'width'=>'4m', 'style'=>'pieslice',
+ 'fill'=>'', 'outline'=>red, 'start'=>225, 'extent'=>90,
+ 'tags'=>$tag_item)
+TkcArc.new(cvs, '5.5c','20.5c','9.5c','23.5c', 'width'=>'4m', 'style'=>'chord',
+ 'fill'=>blue, 'outline'=>'', 'start'=>45, 'extent'=>270,
+ 'tags'=>$tag_item)
+
+TkcText.new(cvs, '15c', '16.2c', 'text'=>'¥Ó¥Ã¥È¥Þ¥Ã¥×', 'anchor'=>'n')
+#TkcBitmap.new(cvs, '13c','20c',
+# 'bitmap'=>'@' + ['images', 'face'].join(File::Separator),
+# 'tags'=>$tag_item)
+TkcBitmap.new(cvs, '13c','20c',
+ 'bitmap'=>'@' + [$demo_dir,
+ 'images', 'face.bmp'].join(File::Separator),
+ 'tags'=>$tag_item)
+#TkcBitmap.new(cvs, '17c','18.5c',
+# 'bitmap'=>'@' + ['images', 'noletters'].join(File::Separator),
+# 'tags'=>$tag_item)
+TkcBitmap.new(cvs, '17c','18.5c',
+ 'bitmap'=>'@' + [$demo_dir,
+ 'images', 'noletter.bmp'].join(File::Separator),
+ 'tags'=>$tag_item)
+#TkcBitmap.new(cvs, '17c','21.5c',
+# 'bitmap'=>'@' + ['images', 'letters'].join(File::Separator),
+# 'tags'=>$tag_item)
+# ¢­¤Î·Á¼°¤Ç¤â²Äǽ
+TkcBitmap.new(cvs, '17c','21.5c') {
+ bitmap '@' + [$demo_dir, 'images', 'letters.bmp'].join(File::Separator)
+ tags $tag_item
+}
+#TkcBitmap.new(cvs, '17c','21.5c') {
+# bitmap '@' + ['images', 'letters'].join(File::Separator)
+# tags $tag_item
+#}
+
+TkcText.new(cvs, '25c', '16.2c', 'text'=>'¥¦¥£¥ó¥É¥¦', 'anchor'=>'n')
+TkButton.new(cvs) {|b|
+ text '²¡¤·¤Æ¤Í'
+ command proc{butPress cvs, red}
+ TkcWindow.new(cvs, '21c','18c',
+ 'window'=>b, 'anchor'=>'nw', 'tags'=>$tag_item)
+}
+TkEntry.new(cvs, 'width'=>20, 'relief'=>'sunken') {|e|
+ insert 'end', 'ÊÔ½¸¤·¤Æ¤Í'
+ TkcWindow.new(cvs, '21c','21c',
+ 'window'=>e, 'anchor'=>'nw', 'tags'=>$tag_item)
+}
+TkScale.new(cvs, 'from'=>0, 'to'=>100, 'length'=>'6c', 'sliderlength'=>'.4c',
+ 'width'=>'.5c', 'tickinterval'=>0 ) {|s|
+ TkcWindow.new(cvs, '28.5c','17.5c',
+ 'window'=>s, 'anchor'=>'n', 'tags'=>$tag_item)
+}
+TkcText.new(cvs, '21c', '17.9c', 'text'=>'¥Ü¥¿¥ó:', 'anchor'=>'sw')
+TkcText.new(cvs, '21c', '20.9c', 'text'=>'¥¨¥ó¥È¥ê:', 'anchor'=>'sw')
+TkcText.new(cvs, '28.5c', '17.4c', 'text'=>'¥¹¥±¡¼¥ë:', 'anchor'=>'s')
+
+# Set up event bindings for canvas:
+cvs.itembind($tag_item, 'Any-Enter', proc{itemEnter cvs})
+cvs.itembind($tag_item, 'Any-Leave', proc{itemLeave cvs})
+cvs.bind('2', proc{|x,y| cvs.scan_mark x,y}, '%x %y')
+cvs.bind('B2-Motion', proc{|x,y| cvs.scan_dragto x,y}, '%x %y')
+cvs.bind('3', proc{|x,y| itemMark cvs,x,y}, '%x %y')
+cvs.bind('B3-Motion', proc{|x,y| itemStroke cvs,x,y}, '%x %y')
+cvs.bind('Control-f', proc{itemsUnderArea cvs})
+cvs.bind('1', proc{|x,y| itemStartDrag cvs,x,y}, '%x %y')
+cvs.bind('B1-Motion', proc{|x,y| itemDrag cvs,x,y}, '%x %y')
+# Utility methods for highlighting the item under the pointer
+
+$restoreCmd = nil
+def itemEnter (c)
+ if TkWinfo.depth(c).to_i == 1
+ $restoreCmd = nil
+ return
+ end
+ type = c.itemtype('current')
+ if type == TkcWindow
+ $restoreCmd = nil
+ return
+ end
+ if type == TkcBitmap
+ bg = (c.itemconfiginfo('current', 'background'))[4]
+ $restoreCmd = proc{c.itemconfigure 'current', 'background', bg}
+ c.itemconfigure 'current', 'background', 'SteelBlue2'
+ return
+ end
+ fill = (c.itemconfiginfo('current', 'fill'))[4]
+ if (type == TkcRectangle || type == TkcOval || type == TkcArc) && fill == []
+ outline = (c.itemconfiginfo('current', 'outline'))[4]
+ $restoreCmd = proc{c.itemconfigure 'current', 'outline', outline}
+ c.itemconfigure 'current', 'outline', 'SteelBlue2'
+ else
+ $restoreCmd = proc{c.itemconfigure 'current', 'fill', fill}
+ c.itemconfigure 'current', 'fill', 'SteelBlue2'
+ end
+end
+
+def itemLeave(c)
+ $restoreCmd.call if $restoreCmd
+end
+
+# Utility methods for stroking out a rectangle and printing what's
+# underneath the rectangle's area.
+
+def itemMark(c,x,y)
+ $areaX1 = c.canvasx(x)
+ $areaY1 = c.canvasy(y)
+ c.delete 'area'
+end
+
+def itemStroke(c,x,y)
+ x = c.canvasx(x)
+ y = c.canvasy(y)
+ if $areaX1 != x && $areaY1 != y
+ c.delete 'area'
+ c.addtag_withtag 'area', TkcRectangle.new(c, $areaX1, $areaY1, x, y,
+ '-outline', 'black')
+ $areaX2 = x
+ $areaY2 = y
+ end
+end
+
+def itemsUnderArea(c)
+ area = c.find_withtag('area')
+ items = []
+ c.find_enclosed($areaX1,$areaY1,$areaX2,$areaY2).each{|i|
+ items.push(i) if i.gettags.include?($tag_item)
+ }
+ print "Items enclosed by area: #{items.inspect}\n"; STDOUT.flush
+ items.clear
+ c.find_overlapping($areaX1,$areaY1,$areaX2,$areaY2).each{|i|
+ items.push(i) if i.gettags.include?($tag_item)
+ }
+ print "Items overlapping area: #{items.inspect}\n"; STDOUT.flush
+end
+
+$areaX1 = 0
+$areaY1 = 0
+$areaX2 = 0
+$areaY2 = 0
+
+# Utility methods to support dragging of items.
+
+def itemStartDrag(c,x,y)
+ $lastX = c.canvasx(x)
+ $lastY = c.canvasy(y)
+end
+
+def itemDrag(c,x,y)
+ x = c.canvasx(x)
+ y = c.canvasy(y)
+ c.move 'current', x - $lastX, y - $lastY
+ $lastX = x
+ $lastY = y
+end
+
+# Method that's invoked when the button embedded in the canvas
+# is invoked.
+
+def butPress(w,color)
+ i = TkcText.new(w, '25c', '18.1c',
+ 'text'=>'¤¤¤Æ¤Æ!!', 'fill'=>color, 'anchor'=>'n')
+ Tk.after(500, proc{w.delete i})
+end
diff --git a/ext/tk/sample/demos-jp/ixset b/ext/tk/sample/demos-jp/ixset
new file mode 100644
index 0000000000..5391012cfb
--- /dev/null
+++ b/ext/tk/sample/demos-jp/ixset
@@ -0,0 +1,333 @@
+#!/usr/local/bin/ruby
+
+# ixset --
+# A nice interface to "xset" to change X server settings
+#
+
+require 'tk'
+
+class Xsettings
+ #
+ # Button actions
+ #
+ def quit
+ @root.destroy
+ end
+
+ def ok
+ writesettings
+ quit
+ end
+
+ def cancel
+ readsettings
+ dispsettings
+ end
+
+ # apply is just "writesettings"
+
+
+ #
+ # Read current settings
+ #
+ def readsettings
+ xfd = open("|xset q", 'r')
+ xfd.readlines.each{|line|
+ fields = line.chomp.strip.split(/\s+/)
+ case fields[0]
+ when "auto"
+ if fields[1] == 'repeat:'
+ @kbdrep = fields[2]
+ @w_kbdrep.set(@kbdrep)
+ @kbdcli = fields[6]
+ end
+
+ when "bell"
+ @bellvol = fields[2]
+ @bellpit = fields[5]
+ @belldur = fields[8]
+
+ when "acceleration:"
+ @mouseacc = fields[1]
+ @mousethr = fields[3]
+
+ when "prefer"
+ if fields[2] == 'yes'
+ @screenbla = 'blank'
+ else
+ @screenbla = 'noblank'
+ end
+ @w_screenbla.set(@screenbla)
+
+ when "timeout:"
+ @screentim = fields[1]
+ @screencyc = fields[3]
+
+ end
+ }
+
+ xfd.close
+ end
+
+ #
+ # Write settings into the X server
+ #
+ def writesettings
+ @bellvol = @w_bellvol.get
+ @bellpit = @w_bellpit.get
+ @belldur = @w_belldur.get
+
+ @kbdrep = @w_kbdrep.get
+ if @kbdrep == 'on'
+ @kbdcli = @w_kbdcli.get
+ else
+ @kbdcli = 'off'
+ end
+
+ @mouseacc = @w_mouseacc.get
+ @mousethr = @w_mousethr.get
+
+ @screentim = @w_screentim.get
+ @screencyc = @w_screencyc.get
+ @screenbla = @w_screenbla.get
+
+ system("xset \
+ b #{@bellvol} #{@bellpit} #{@belldur} \
+ c #{@kbdcli} \
+ r #{@kbdrep} \
+ m #{@mouseacc} #{@mousethr} \
+ s #{@screentim} #{@screencyc} \
+ s #{@screenbla}")
+ end
+
+ #
+ # Sends all settings to the window
+ #
+ def dispsettings
+ @w_bellvol.set(@bellvol)
+ @w_bellpit.set(@bellpit)
+ @w_belldur.set(@belldur)
+
+ @w_kbdonoff.set(@w_kbdrep.get)
+ @w_kbdcli.set(@kbdcli)
+
+ @w_mouseacc.set(@mouseacc)
+ @w_mousethr.set(@mousethr)
+
+ @w_screenblank.set(@w_screenbla.get)
+ @w_screenpat.set(@w_screenbla.get)
+
+ @w_screentim.set(@screentim)
+ @w_screencyc.set(@screencyc)
+ end
+
+ #
+ # Create all windows, and pack them
+ #
+ class LabelEntry
+ def initialize(parent, text, length)
+ @frame = TkFrame.new(parent)
+ TkLabel.new(@frame, 'text'=>text).pack('side'=>'left','expand'=>'y')
+ @entry = TkEntry.new(@frame, 'width'=>length, 'relief'=>'sunken') {
+ pack('side'=>'left','expand'=>'y')
+ }
+ end
+ def pack(keys)
+ @frame.pack(keys)
+ end
+ def get
+ @entry.value
+ end
+ def set(value)
+ @entry.delete(0,'end')
+ @entry.insert(0, value)
+ end
+ end
+
+ def createwindows
+ win = self
+
+ #
+ # Buttons
+ #
+ buttons = TkFrame.new(@root) {|f|
+ [ TkButton.new(f, 'command'=>proc{win.ok}, 'text'=>'Ok'),
+ TkButton.new(f, 'command'=>proc{win.writesettings}, 'text'=>'Apply'),
+ TkButton.new(f, 'command'=>proc{win.cancel}, 'text'=>'Cancel'),
+ TkButton.new(f, 'command'=>proc{win.quit}, 'text'=>'Quit') ].each{|b|
+ b.pack('side'=>'left', 'expand'=>'yes', 'pady'=>5)
+ }
+ }
+
+ #
+ # Bell settings
+ #
+ bell = TkFrame.new(@root, 'relief'=>'raised', 'borderwidth'=>2)
+ l = TkLabel.new(bell, 'text'=>'Bell Settings')
+ @w_bellvol = TkScale.new(bell, 'from'=>0, 'to'=>100, 'length'=>200,
+ 'tickinterval'=>20, 'orient'=>'horizontal',
+ 'label'=>"Volume (%)")
+
+ f = TkFrame.new(bell)
+ @w_bellpit = LabelEntry.new(f, "Pitch (Hz)", 6)
+ @w_bellpit.pack('side'=>'left', 'padx'=>5)
+ @w_belldur = LabelEntry.new(f, "Duration (ms)", 6)
+ @w_belldur.pack('side'=>'right', 'padx'=>5)
+
+ l.pack('side'=>'top', 'expand'=>'yes')
+ @w_bellvol.pack('side'=>'top', 'expand'=>'yes')
+ f.pack('side'=>'top', 'expand'=>'yes')
+
+ #
+ # Keyboard settings
+ #
+ kbdonoff = nil
+ kbdcli = nil
+ kbd = TkFrame.new(@root, 'relief'=>'raised', 'borderwidth'=>2)
+ l = TkLabel.new(kbd, 'text'=>'Keyboard Repeat Settings')
+ f = TkFrame.new(kbd)
+ @w_kbdonoff = TkCheckButton.new(f, 'text'=>'On', 'relief'=>'flat',
+ 'onvalue'=>'on', 'offvalue'=>'off',
+ 'variable'=>@w_kbdrep ) {
+ def self.set(value)
+ if value == 'on'
+ self.select
+ else
+ self.deselect
+ end
+ end
+ pack('side'=>'left', 'expand'=>'yes', 'fill'=>'both')
+ }
+ @w_kbdcli = TkScale.new(f, 'from'=>0, 'to'=>100, 'length'=>200,
+ 'tickinterval'=>20, 'orient'=>'horizontal',
+ 'label'=>'Click Volume (%)')
+ @w_kbdcli.pack('side'=>'left', 'expand'=>'yes')
+ l.pack('side'=>'top', 'expand'=>'yes')
+ f.pack('side'=>'top', 'expand'=>'yes', 'pady'=>2, 'fill'=>'x')
+
+ #
+ # Mouse settings
+ #
+ mouse = TkFrame.new(@root, 'relief'=>'raised', 'borderwidth'=>2)
+ l = TkLabel.new(mouse, 'text'=>'Mouse Settings')
+ f = TkFrame.new(mouse)
+ @w_mouseacc = LabelEntry.new(f, 'Acceleration', 3)
+ @w_mouseacc.pack('side'=>'left')
+ @w_mousethr = LabelEntry.new(f, 'Threshold (pixels)', 3)
+ @w_mousethr.pack('side'=>'right')
+ l.pack('side'=>'top')
+ f.pack('side'=>'top', 'expand'=>'yes')
+
+ #
+ # Screen Saver settings
+ #
+ screen = TkFrame.new(@root, 'relief'=>'raised', 'borderwidth'=>2)
+ l = TkLabel.new(screen, 'text'=>'Screen-saver Settings')
+ f = TkFrame.new(screen)
+ ff1 = TkFrame.new(f)
+ [ @w_screenblank = TkRadioButton.new(ff1, 'text'=>'Blank',
+ 'relief'=>'flat',
+ 'variable'=>@w_screenbla,
+ 'value'=>'blank') {
+ def self.set(value)
+ if value == 'blank'
+ self.select
+ else
+ self.deselect
+ end
+ end
+ },
+ @w_screenpat = TkRadioButton.new(ff1, 'text'=>'Pattern',
+ 'relief'=>'flat',
+ 'variable'=>@w_screenbla,
+ 'value'=>'noblank') {
+ def self.set(value)
+ if value != 'blank'
+ self.select
+ else
+ self.deselect
+ end
+ end
+ }
+ ].each {|w| w.pack('side'=>'top', 'pady'=>2, 'anchor'=>'w') }
+
+ ff2 = TkFrame.new(f)
+ [ @w_screentim = LabelEntry.new(ff2, 'Timeout (s)', 5),
+ @w_screencyc = LabelEntry.new(ff2, 'Cycle (s)', 5) ].each{|w|
+ w.pack('side'=>'top', 'pady'=>2, 'anchor'=>'e')
+ }
+
+ ff1.pack('side'=>'left')
+ ff2.pack('side'=>'left')
+
+ l.pack('side'=>'top')
+ f.pack('side'=>'top', 'expand'=>'yes')
+
+ #
+ # Main window
+ #
+ buttons.pack('side'=>'top', 'fill'=>'both')
+ bell.pack('side'=>'top', 'fill'=>'both', 'ipady'=>5, 'expand'=>'yes')
+ kbd.pack('side'=>'top', 'fill'=>'both', 'ipady'=>5, 'expand'=>'yes')
+ mouse.pack('side'=>'top', 'fill'=>'both', 'ipady'=>5, 'expand'=>'yes')
+ screen.pack('side'=>'top', 'fill'=>'both', 'ipady'=>5, 'expand'=>'yes')
+
+ #
+ # Let the user resize our window
+ #
+ @root.minsize(10,10)
+ end
+
+ def initialize
+ @root = TkRoot.new
+
+ @kbdrep = 'on'
+ @w_kbdrep = TkVariable.new(@kbdrep)
+ def @w_kbdrep.get
+ self.value
+ end
+ def @w_kbdrep.set(val)
+ self.value=val
+ end
+
+ @kbdcli = 0
+
+ @bellvol = 100
+ @bellpit = 440
+ @belldur = 100
+
+ @mouseacc = "3/1"
+ @mousethr = 4
+
+ @screenbla = "blank"
+ @w_screenbla = TkVariable.new(@screenbla)
+ def @w_screenbla.get
+ self.value
+ end
+ def @w_screenbla.set(val)
+ self.value=val
+ end
+
+ @screentim = 600
+ @screencyc = 600
+
+ #
+ # Listen what "xset" tells us...
+ #
+ readsettings
+
+ #
+ # Create all windows
+ #
+ createwindows
+
+ #
+ # Write xset parameters
+ #
+ dispsettings
+ end
+end
+
+Xsettings.new
+
+Tk.mainloop
diff --git a/ext/tk/sample/demos-jp/label.rb b/ext/tk/sample/demos-jp/label.rb
new file mode 100644
index 0000000000..80ab5f3d8f
--- /dev/null
+++ b/ext/tk/sample/demos-jp/label.rb
@@ -0,0 +1,64 @@
+#
+# label widget demo (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($label_demo) && $label_demo
+ $label_demo.destroy
+ $label_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$label_demo = TkToplevel.new {|w|
+ title("Label Demonstration")
+ iconname("label")
+ positionWindow(w)
+}
+
+# label À¸À®
+msg = TkLabel.new($label_demo) {
+ font $font
+ wraplength '4i'
+ justify 'left'
+ text "²¼¤Ë¤Ï5¤Ä¤Î¥é¥Ù¥ë¤¬É½¼¨¤µ¤ì¤Æ¤¤¤Þ¤¹¡£º¸Â¦¤Ë¤Ï¥Æ¥­¥¹¥È¥é¥Ù¥ë¤¬3¤Ä¤¢¤ê¡¢±¦Â¦¤Ë¤Ï¥Ó¥Ã¥È¥Þ¥Ã¥×¥é¥Ù¥ë¤È¥Æ¥­¥¹¥È¥é¥Ù¥ë¤¬¤¢¤ê¤Þ¤¹¡£¥é¥Ù¥ë¤È¤¤¤¦¤Î¤Ï¤¢¤Þ¤êÌÌÇò¤¤¤â¤Î¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó¡£¤Ê¤¼¤Ê¤éį¤á¤ë°Ê³°²¿¤â¤Ç¤­¤Ê¤¤¤«¤é¤Ç¤¹¡£"
+}
+msg.pack('side'=>'top')
+
+# frame À¸À®
+TkFrame.new($label_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $label_demo
+ $label_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'label'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# label demo Íѥե졼¥àÀ¸À®
+f_left = TkFrame.new($label_demo)
+f_right = TkFrame.new($label_demo)
+[f_left, f_right].each{|w| w.pack('side'=>'left', 'expand'=>'yes',
+ 'padx'=>10, 'pady'=>10, 'fill'=>'both')}
+
+# label À¸À®
+[ TkLabel.new(f_left, 'text'=>'ºÇ½é¤Î¥é¥Ù¥ë'),
+ TkLabel.new(f_left, 'text'=>'2 ÈÖÌÜ¡£¤Á¤ç¤Ã¤ÈÉ⤭¾å¤¬¤é¤»¤Æ¤ß¤Þ¤·¤¿',
+ 'relief'=>'raised'),
+ TkLabel.new(f_left, 'text'=>'3 ÈÖÌÜ¡£ÄÀ¤ó¤Ç¤¤¤Þ¤¹ ', 'relief'=>'sunken')
+].each{|w| w.pack('side'=>'top', 'expand'=>'yes', 'pady'=>2, 'anchor'=>'w')}
+
+TkLabel.new(f_right) {
+ bitmap('@' + [$demo_dir,'images','face.bmp'].join(File::Separator))
+ borderwidth 2
+ relief 'sunken'
+}.pack('side'=>'top')
+
+TkLabel.new(f_right) { text 'Tcl/Tk ½êÍ­¼Ô' }.pack('side'=>'top')
diff --git a/ext/tk/sample/demos-jp/menu.rb b/ext/tk/sample/demos-jp/menu.rb
new file mode 100644
index 0000000000..fb32623cc8
--- /dev/null
+++ b/ext/tk/sample/demos-jp/menu.rb
@@ -0,0 +1,186 @@
+#
+# menus widget demo (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($menu_demo) && $menu_demo
+ $menu_demo.destroy
+ $menu_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$menu_demo = TkToplevel.new {|w|
+ title("File Selection Dialogs")
+ iconname("menu")
+ positionWindow(w)
+}
+
+# menu frame À¸À®
+$menu_frame = TkFrame.new($menu_demo, 'relief'=>'raised', 'bd'=>2)
+$menu_frame.pack('side'=>'top', 'fill'=>'x')
+
+# label À¸À®
+TkLabel.new($menu_demo,'font'=>$font,'wraplength'=>'4i','justify'=>'left') {
+ if $tk_platform['platform'] == 'macintosh'
+ text("¤³¤Î¥¦¥£¥ó¥É¥¦¤ÏÍÍ¡¹¤Ê¥á¥Ë¥å¡¼¤È¥«¥¹¥±¡¼¥É¥á¥Ë¥å¡¼¤«¤é¹½À®¤µ¤ì¤Æ¤¤¤Þ¤¹¡£Command-X ¤òÆþÎϤ¹¤ë¤È¡¢X¤¬¥³¥Þ¥ó¥É¥­¡¼µ­¹æ¤Ë³¤¤¤ÆÉ½¼¨¤µ¤ì¤Æ¤¤¤ëʸ»ú¤Ê¤é¤Ð¡¢¥¢¥¯¥»¥é¥ì¡¼¥¿¤ò»È¤Ã¤¿¹àÌܵ¯Æ°¤ò¹Ô¤¦¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£¥á¥Ë¥å¡¼Í×ÁÇÃæ¡¢ºÇ¸å¤Î¤â¤Î¤Ï¡¢¤½¤Î¥á¥Ë¥å¡¼¤ÎºÇ½é¤Î¹àÌܤòÁªÂò¤¹¤ë¤³¤È¤ÇÆÈΩ¤µ¤»¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£")
+ else
+ text("¤³¤Î¥¦¥£¥ó¥É¥¦¤ÏÍÍ¡¹¤Ê¥á¥Ë¥å¡¼¤È¥«¥¹¥±¡¼¥É¥á¥Ë¥å¡¼¤«¤é¹½À®¤µ¤ì¤Æ¤¤¤Þ¤¹¡£Alt-X ¤òÆþÎϤ¹¤ë¤È¡¢X¤¬¥á¥Ë¥å¡¼¤Ë¥¢¥ó¥À¡¼¥é¥¤¥óÉÕ¤­¤Çɽ¼¨¤µ¤ì¤Æ¤¤¤ëʸ»ú¤Ê¤é¤Ð¡¢¥­¡¼¥Ü¡¼¥É¤«¤é¤Î»ØÄ꤬¤Ç¤­¤Þ¤¹¡£Ìð°õ¥­¡¼¤Ç¥á¥Ë¥å¡¼¤Î¥È¥é¥Ð¡¼¥¹¤â²Äǽ¤Ç¤¹¡£¥á¥Ë¥å¡¼¤¬»ØÄꤵ¤ì¤¿ºÝ¤Ë¤Ï¡¢¥¹¥Ú¡¼¥¹¥­¡¼¤Ç¼Â¹Ô¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£¤¢¤ë¤¤¤Ï¡¢¥¢¥ó¥À¡¼¥é¥¤¥óÉÕ¤­¤Îʸ»ú¤òÆþÎϤ¹¤ë¤³¤È¤Ç¤â¼Â¹Ô¤Ç¤­¤Þ¤¹¡£¥á¥Ë¥å¡¼¤Î¥¨¥ó¥È¥ê¤¬¥¢¥¯¥»¥é¥ì¡¼¥¿¤ò»ý¤Ã¤Æ¤¤¤ë¾ì¹ç¤Ï¡¢¤½¤Î¥¢¥¯¥»¥é¥ì¡¼¥¿¤òÆþÎϤ¹¤ë¤³¤È¤Ç¥á¥Ë¥å¡¼¤ò»ØÄꤹ¤ë¤³¤È¤Ê¤·¤Ë¼Â¹Ô¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£¥á¥Ë¥å¡¼Í×ÁÇÃæ¡¢ºÇ¸å¤Î¤â¤Î¤Ï¡¢¤½¤Î¥á¥Ë¥å¡¼¤ÎºÇ½é¤Î¹àÌܤòÁªÂò¤¹¤ë¤³¤È¤ÇÆÈΩ¤µ¤»¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£")
+ end
+}.pack('side'=>'top')
+
+# frame À¸À®
+TkFrame.new($menu_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $menu_demo
+ $menu_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'menu'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# menu À¸À®
+TkMenubutton.new($menu_frame, 'text'=>'File', 'underline'=>0) {|m|
+ pack('side'=>'left')
+ TkMenu.new(m, 'tearoff'=>false) {|file_menu|
+ m.configure('menu'=>file_menu)
+ add('command', 'label'=>'³«¤¯ ...', 'command'=>proc{fail '¤³¤ì¤Ï¡¢¥Ç¥â¤Ç¤¹¤Î¤Ç"³«¤¯ ..."¤ËÂФ¹¤ë¥¢¥¯¥·¥ç¥ó¤ÏÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£'})
+ add('command', 'label'=>'¿·µ¬', 'command'=>proc{fail '¤³¤ì¤Ï¡¢¥Ç¥â¤Ç¤¹¤Î¤Ç"¿·µ¬"¤ËÂФ¹¤ë¥¢¥¯¥·¥ç¥ó¤ÏÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£'})
+ add('command', 'label'=>'Êݸ', 'command'=>proc{fail '¤³¤ì¤Ï¡¢¥Ç¥â¤Ç¤¹¤Î¤Ç"Êݸ"¤ËÂФ¹¤ë¥¢¥¯¥·¥ç¥ó¤ÏÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£'})
+ add('command', 'label'=>'Êݸ(»ØÄê) ...', 'command'=>proc{fail '¤³¤ì¤Ï¡¢¥Ç¥â¤Ç¤¹¤Î¤Ç"Êݸ(»ØÄê) ..."¤ËÂФ¹¤ë¥¢¥¯¥·¥ç¥ó¤ÏÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£'})
+ add('separator')
+ add('command', 'label'=>'¥×¥ê¥ó¥ÈÀßÄê ...', 'command'=>proc{fail '¤³¤ì¤Ï¡¢¥Ç¥â¤Ç¤¹¤Î¤Ç"¥×¥ê¥ó¥ÈÀßÄê ..."¤ËÂФ¹¤ë¥¢¥¯¥·¥ç¥ó¤ÏÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£'})
+ add('command', 'label'=>'¥×¥ê¥ó¥È ...', 'command'=>proc{fail '¤³¤ì¤Ï¡¢¥Ç¥â¤Ç¤¹¤Î¤Ç"¥×¥ê¥ó¥È ..."¤ËÂФ¹¤ë¥¢¥¯¥·¥ç¥ó¤ÏÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£'})
+ add('separator')
+ add('command', 'label'=>'½ªÎ»', 'command'=>proc{$menu_demo.destroy})
+ }
+}
+
+if $tk_platform['platform'] == 'macintosh'
+ modifier = 'Command'
+elsif $tk_platform['platform'] == 'windows'
+ modifier = 'Control'
+else
+ modifier = 'Meta'
+end
+
+TkMenubutton.new($menu_frame, 'text'=>'Basic', 'underline'=>0) {|m|
+ pack('side'=>'left')
+ TkMenu.new(m, 'tearoff'=>false) {|basic_menu|
+ m.configure('menu'=>basic_menu)
+ add('command', 'label'=>'²¿¤â¤·¤Ê¤¤Ä¹¤¤¥¨¥ó¥È¥ê')
+ ['A','B','C','D','E','F','G'].each{|c|
+ # add('command', 'label'=>"ʸ»ú \"#{c}\" ¤ò°õ»ú", 'underline'=>4,
+ add('command', 'label'=>"Print letter \"#{c}\" (ʸ»ú \"#{c}\" ¤ò°õ»ú)",
+ 'underline'=>14, 'accelerator'=>"Meta+#{c}",
+ 'command'=>proc{print c,"\n"}, 'accelerator'=>"#{modifier}+#{c}")
+ $menu_demo.bind("#{modifier}-#{c.downcase}", proc{print c,"\n"})
+ }
+ }
+}
+
+TkMenubutton.new($menu_frame, 'text'=>'Cascades', 'underline'=>0) {|m|
+ pack('side'=>'left')
+ TkMenu.new(m, 'tearoff'=>false) {|cascade_menu|
+ m.configure('menu'=>cascade_menu)
+ add('command', 'label'=>'Print hello(¤³¤ó¤Ë¤Á¤Ï)',
+ 'command'=>proc{print "Hello(¤³¤ó¤Ë¤Á¤Ï)\n"},
+ 'accelerator'=>"#{modifier}+H", 'underline'=>6)
+ $menu_demo.bind("#{modifier}-h", proc{print "Hello(¤³¤ó¤Ë¤Á¤Ï)\n"})
+ add('command', 'label'=>'Print goodbye(¤µ¤è¤¦¤Ê¤é)',
+ 'command'=>proc{print "Goodbye(¤µ¤è¤¦¤Ê¤é)\n"},
+ 'accelerator'=>"#{modifier}+G", 'underline'=>6)
+ $menu_demo.bind("#{modifier}-g", proc{print "Goodbye(¤µ¤è¤¦¤Ê¤é)\n"})
+
+ TkMenu.new(m, 'tearoff'=>false) {|cascade_check|
+ cascade_menu.add('cascade', 'label'=>'Check buttons(¥Á¥§¥Ã¥¯¥Ü¥¿¥ó)',
+ 'menu'=>cascade_check, 'underline'=>0)
+ oil = TkVariable.new(0)
+ add('check', 'label'=>'¥ª¥¤¥ëÅÀ¸¡', 'variable'=>oil)
+ trans = TkVariable.new(0)
+ add('check', 'label'=>'¥È¥é¥ó¥¹¥ß¥Ã¥·¥ç¥óÅÀ¸¡', 'variable'=>trans)
+ brakes = TkVariable.new(0)
+ add('check', 'label'=>'¥Ö¥ì¡¼¥­ÅÀ¸¡', 'variable'=>brakes)
+ lights = TkVariable.new(0)
+ add('check', 'label'=>'¥é¥¤¥ÈÅÀ¸¡', 'variable'=>lights)
+ add('separator')
+ add('command', 'label'=>'¸½ºß¤ÎÃͤòɽ¼¨',
+ 'command'=>proc{showVars($menu_demo,
+ ['¥ª¥¤¥ëÅÀ¸¡', oil],
+ ['¥È¥é¥ó¥¹¥ß¥Ã¥·¥ç¥óÅÀ¸¡', trans],
+ ['¥Ö¥ì¡¼¥­ÅÀ¸¡', brakes],
+ ['¥é¥¤¥ÈÅÀ¸¡', lights])} )
+ invoke 1
+ invoke 3
+ }
+
+ TkMenu.new(m, 'tearoff'=>false) {|cascade_radio|
+ cascade_menu.add('cascade', 'label'=>'Radio buttons(¥é¥¸¥ª¥Ü¥¿¥ó)',
+ 'menu'=>cascade_radio, 'underline'=>0)
+ pointSize = TkVariable.new
+ add('radio', 'label'=>'10 ¥Ý¥¤¥ó¥È', 'variable'=>pointSize, 'value'=>10)
+ add('radio', 'label'=>'14 ¥Ý¥¤¥ó¥È', 'variable'=>pointSize, 'value'=>14)
+ add('radio', 'label'=>'18 ¥Ý¥¤¥ó¥È', 'variable'=>pointSize, 'value'=>18)
+ add('radio', 'label'=>'24 ¥Ý¥¤¥ó¥È', 'variable'=>pointSize, 'value'=>24)
+ add('radio', 'label'=>'32 ¥Ý¥¤¥ó¥È', 'variable'=>pointSize, 'value'=>32)
+ add('separator')
+ style = TkVariable.new
+ add('radio', 'label'=>'¥í¡¼¥Þ¥ó', 'variable'=>style, 'value'=>'roman')
+ add('radio', 'label'=>'¥Ü¡¼¥ë¥É', 'variable'=>style, 'value'=>'bold')
+ add('radio', 'label'=>'¥¤¥¿¥ê¥Ã¥¯', 'variable'=>style, 'value'=>'italic')
+ add('separator')
+ add('command', 'label'=>'¸½ºß¤ÎÃͤòɽ¼¨',
+ 'command'=>proc{showVars($menu_demo,
+ ['¥Ý¥¤¥ó¥È¥µ¥¤¥º', pointSize],
+ ['¥¹¥¿¥¤¥ë', style])} )
+ invoke 1
+ invoke 7
+ }
+ }
+}
+
+TkMenubutton.new($menu_frame, 'text'=>'Icons', 'underline'=>0) {|m|
+ pack('side'=>'left')
+ TkMenu.new(m, 'tearoff'=>false) {|icon_menu|
+ m.configure('menu'=>icon_menu)
+ add('command',
+ 'bitmap'=>'@'+[$demo_dir,'images','pattern.bmp'].join(File::Separator),
+ 'command'=>proc{TkDialog.new('title'=>'Bitmap Menu Entry',
+ 'text'=>'º£¤¢¤Ê¤¿¤¬ÁªÂò¤·¤¿¥á¥Ë¥å¡¼¤Î¹àÌܤϥƥ­¥¹¥È¤Ç¤Ï¤Ê¤¯¥Ó¥Ã¥È¥Þ¥Ã¥×¤òɽ¼¨¤·¤Æ¤¤¤Þ¤·¤¿¡£¤½¤ì°Ê³°¤ÎÅÀ¤Ç¤Ï¾¤Î¥á¥Ë¥å¡¼¹àÌܤÈÊѤï¤ê¤Þ¤»¤ó¡£',
+ 'bitmap'=>'', 'default'=>0,
+ 'buttons'=>'λ²ò')} )
+ ['info', 'questhead', 'error'].each{|icon|
+ add('command', 'bitmap'=>icon,
+ 'command'=>proc{print "You invoked the #{icon} bitmap\n"})
+ }
+ }
+}
+
+TkMenubutton.new($menu_frame, 'text'=>'More', 'underline'=>0) {|m|
+ pack('side'=>'left')
+ TkMenu.new(m, 'tearoff'=>false) {|more_menu|
+ m.configure('menu'=>more_menu)
+ [ '¥¨¥ó¥È¥ê','Ê̤Υ¨¥ó¥È¥ê','²¿¤â¤·¤Ê¤¤','¤Û¤È¤ó¤É²¿¤â¤·¤Ê¤¤',
+ '¿ÍÀ¸¤ò°ÕµÁ¤¢¤ë¤â¤Î¤Ë' ].each{|i|
+ add('command', 'label'=>i,
+ 'command'=>proc{print "You invoked \"#{i}\"\n"})
+ }
+ }
+}
+
+TkMenubutton.new($menu_frame, 'text'=>'Colors', 'underline'=>0) {|m|
+ pack('side'=>'left')
+ TkMenu.new(m) {|colors_menu|
+ m.configure('menu'=>colors_menu)
+ ['red', 'orange', 'yellow', 'green', 'blue'].each{|c|
+ add('command', 'label'=>c, 'background'=>c,
+ 'command'=>proc{print "You invoked \"#{c}\"\n"})
+ }
+ }
+}
diff --git a/ext/tk/sample/demos-jp/menu8x.rb b/ext/tk/sample/demos-jp/menu8x.rb
new file mode 100644
index 0000000000..a6d3c5e09e
--- /dev/null
+++ b/ext/tk/sample/demos-jp/menu8x.rb
@@ -0,0 +1,219 @@
+#
+# menus widget demo (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($menu8x_demo) && $menu8x_demo
+ $menu8x_demo.destroy
+ $menu8x_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$menu8x_demo = TkToplevel.new {|w|
+ title("Menu Demonstration (Tk8.x)")
+ iconname("menu")
+ positionWindow(w)
+}
+
+# version check
+if $tk_version.to_f < 8.0
+
+# label À¸À®
+TkLabel.new($menu8x_demo,'font'=>$font,'wraplength'=>'4i','justify'=>'left') {
+ text("¼Â¹Ô¤·¤è¤¦¤È¤·¤¿¥¹¥¯¥ê¥×¥È¤Ï Tk8.0 °Ê¾å¤ÇÍøÍѤǤ­¤ëµ¡Ç½¤òÍøÍѤ·¤Æ¤¤¤ë¤¿¤á¡¢¤¢¤Ê¤¿¤Î Ruby#{VERSION}/Tk#{$tk_version}#{(Tk::JAPANIZED_TK)? 'jp': ''} ¤Ç¤ÏÀµ¾ï¤Ë¼Â¹Ô¤Ç¤­¤Þ¤»¤ó¡£¤è¤Ã¤Æ¥Ç¥â¤Î¼Â¹Ô¤òÃæ»ß¤·¤Þ¤·¤¿¡£¤¿¤À¤·¡¢²¼¤Î¥³¡¼¥É»²¾È¥Ü¥¿¥ó¤ò²¡¤¹¤³¤È¤Ç¡¢¼Â¹Ô¤¬Ãæ»ß¤µ¤ì¤¿¥¹¥¯¥ê¥×¥È¤Î¥½¡¼¥¹¤ò»²¾È¤¹¤ë¤³¤È¤Ï²Äǽ¤Ç¤¹¡£")
+}.pack('side'=>'top')
+
+# frame À¸À®
+TkFrame.new($menu8x_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $menu8x_demo
+ $menu8x_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'menu'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+else ; # Tk8.x
+
+# label À¸À®
+TkLabel.new($menu8x_demo,'font'=>$font,'wraplength'=>'4i','justify'=>'left') {
+ if $tk_platform['platform'] == 'macintosh'
+ text("¤³¤Î¥¦¥£¥ó¥É¥¦¤ÏÍÍ¡¹¤Ê¥á¥Ë¥å¡¼¤È¥«¥¹¥±¡¼¥É¥á¥Ë¥å¡¼¤«¤é¹½À®¤µ¤ì¤Æ¤¤¤Þ¤¹¡£Command-X ¤òÆþÎϤ¹¤ë¤È¡¢X¤¬¥³¥Þ¥ó¥É¥­¡¼µ­¹æ¤Ë³¤¤¤ÆÉ½¼¨¤µ¤ì¤Æ¤¤¤ëʸ»ú¤Ê¤é¤Ð¡¢¥¢¥¯¥»¥é¥ì¡¼¥¿¤ò»È¤Ã¤¿¹àÌܵ¯Æ°¤ò¹Ô¤¦¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£¥á¥Ë¥å¡¼Í×ÁÇÃæ¡¢ºÇ¸å¤Î¤â¤Î¤Ï¡¢¤½¤Î¥á¥Ë¥å¡¼¤ÎºÇ½é¤Î¹àÌܤòÁªÂò¤¹¤ë¤³¤È¤ÇÆÈΩ¤µ¤»¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£")
+ else
+ text("¤³¤Î¥¦¥£¥ó¥É¥¦¤ÏÍÍ¡¹¤Ê¥á¥Ë¥å¡¼¤È¥«¥¹¥±¡¼¥É¥á¥Ë¥å¡¼¤«¤é¹½À®¤µ¤ì¤Æ¤¤¤Þ¤¹¡£Alt-X ¤òÆþÎϤ¹¤ë¤È¡¢X¤¬¥á¥Ë¥å¡¼¤Ë¥¢¥ó¥À¡¼¥é¥¤¥óÉÕ¤­¤Çɽ¼¨¤µ¤ì¤Æ¤¤¤ëʸ»ú¤Ê¤é¤Ð¡¢¥­¡¼¥Ü¡¼¥É¤«¤é¤Î»ØÄ꤬¤Ç¤­¤Þ¤¹¡£Ìð°õ¥­¡¼¤Ç¥á¥Ë¥å¡¼¤Î¥È¥é¥Ð¡¼¥¹¤â²Äǽ¤Ç¤¹¡£¥á¥Ë¥å¡¼¤¬»ØÄꤵ¤ì¤¿ºÝ¤Ë¤Ï¡¢¥¹¥Ú¡¼¥¹¥­¡¼¤Ç¼Â¹Ô¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£¤¢¤ë¤¤¤Ï¡¢¥¢¥ó¥À¡¼¥é¥¤¥óÉÕ¤­¤Îʸ»ú¤òÆþÎϤ¹¤ë¤³¤È¤Ç¤â¼Â¹Ô¤Ç¤­¤Þ¤¹¡£¥á¥Ë¥å¡¼¤Î¥¨¥ó¥È¥ê¤¬¥¢¥¯¥»¥é¥ì¡¼¥¿¤ò»ý¤Ã¤Æ¤¤¤ë¾ì¹ç¤Ï¡¢¤½¤Î¥¢¥¯¥»¥é¥ì¡¼¥¿¤òÆþÎϤ¹¤ë¤³¤È¤Ç¥á¥Ë¥å¡¼¤ò»ØÄꤹ¤ë¤³¤È¤Ê¤·¤Ë¼Â¹Ô¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£¥á¥Ë¥å¡¼Í×ÁÇÃæ¡¢ºÇ¸å¤Î¤â¤Î¤Ï¡¢¤½¤Î¥á¥Ë¥å¡¼¤ÎºÇ½é¤Î¹àÌܤòÁªÂò¤¹¤ë¤³¤È¤ÇÆÈΩ¤µ¤»¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£")
+ end
+}.pack('side'=>'top')
+
+# ¾õÂÖɽ¼¨¤ÎÀ¸À®
+$menu8xstatus = TkVariable.new(" ")
+TkFrame.new($menu8x_demo) {|frame|
+ TkLabel.new(frame, 'textvariable'=>$menu8xstatus, 'relief'=>'sunken',
+ 'bd'=>1, 'font'=>['Helvetica', '10'], 'anchor'=>'w')\
+ .pack('side'=>'left', 'padx'=>2, 'expand'=>'yes', 'fill'=>'both')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>2)
+
+# frame À¸À®
+TkFrame.new($menu8x_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $menu8x_demo
+ $menu8x_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'menu'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# menu À¸À®
+TkMenu.new($menu8x_demo, 'tearoff'=>false) {|m|
+ TkMenu.new(m, 'tearoff'=>false) {|file_menu|
+ m.add('cascade', 'label'=>'File', 'menu'=>file_menu, 'underline'=>0)
+ add('command', 'label'=>'³«¤¯ ...', 'command'=>proc{fail '¤³¤ì¤Ï¡¢¥Ç¥â¤Ç¤¹¤Î¤Ç"³«¤¯ ..."¤ËÂФ¹¤ë¥¢¥¯¥·¥ç¥ó¤ÏÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£'})
+ add('command', 'label'=>'¿·µ¬', 'command'=>proc{fail '¤³¤ì¤Ï¡¢¥Ç¥â¤Ç¤¹¤Î¤Ç"¿·µ¬"¤ËÂФ¹¤ë¥¢¥¯¥·¥ç¥ó¤ÏÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£'})
+ add('command', 'label'=>'Êݸ', 'command'=>proc{fail '¤³¤ì¤Ï¡¢¥Ç¥â¤Ç¤¹¤Î¤Ç"Êݸ"¤ËÂФ¹¤ë¥¢¥¯¥·¥ç¥ó¤ÏÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£'})
+ add('command', 'label'=>'Êݸ(»ØÄê) ...', 'command'=>proc{fail '¤³¤ì¤Ï¡¢¥Ç¥â¤Ç¤¹¤Î¤Ç"Êݸ(»ØÄê) ..."¤ËÂФ¹¤ë¥¢¥¯¥·¥ç¥ó¤ÏÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£'})
+ add('separator')
+ add('command', 'label'=>'¥×¥ê¥ó¥ÈÀßÄê ...', 'command'=>proc{fail '¤³¤ì¤Ï¡¢¥Ç¥â¤Ç¤¹¤Î¤Ç"¥×¥ê¥ó¥ÈÀßÄê ..."¤ËÂФ¹¤ë¥¢¥¯¥·¥ç¥ó¤ÏÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£'})
+ add('command', 'label'=>'¥×¥ê¥ó¥È ...', 'command'=>proc{fail '¤³¤ì¤Ï¡¢¥Ç¥â¤Ç¤¹¤Î¤Ç"¥×¥ê¥ó¥È ..."¤ËÂФ¹¤ë¥¢¥¯¥·¥ç¥ó¤ÏÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£'})
+ add('separator')
+ add('command', 'label'=>'½ªÎ»', 'command'=>proc{$menu8x_demo.destroy})
+ }
+
+ if $tk_platform['platform'] == 'macintosh'
+ modifier = 'Command'
+ elsif $tk_platform['platform'] == 'windows'
+ modifier = 'Control'
+ else
+ modifier = 'Meta'
+ end
+
+ TkMenu.new(m, 'tearoff'=>false) {|basic_menu|
+ m.add('cascade', 'label'=>'Basic', 'menu'=>basic_menu, 'underline'=>0)
+ add('command', 'label'=>'²¿¤â¤·¤Ê¤¤Ä¹¤¤¥¨¥ó¥È¥ê')
+ ['A','B','C','D','E','F','G'].each{|c|
+ # add('command', 'label'=>"ʸ»ú \"#{c}\" ¤ò°õ»ú", 'underline'=>4,
+ add('command', 'label'=>"Print letter \"#{c}\" (ʸ»ú \"#{c}\" ¤ò°õ»ú)",
+ 'underline'=>14, 'accelerator'=>"Meta+#{c}",
+ 'command'=>proc{print c,"\n"}, 'accelerator'=>"#{modifier}+#{c}")
+ $menu8x_demo.bind("#{modifier}-#{c.downcase}", proc{print c,"\n"})
+ }
+ }
+
+ TkMenu.new(m, 'tearoff'=>false) {|cascade_menu|
+ m.add('cascade', 'label'=>'Cascades', 'menu'=>cascade_menu, 'underline'=>0)
+ add('command', 'label'=>'Print hello(¤³¤ó¤Ë¤Á¤Ï)',
+ 'command'=>proc{print "Hello(¤³¤ó¤Ë¤Á¤Ï)\n"},
+ 'accelerator'=>"#{modifier}+H", 'underline'=>6)
+ $menu8x_demo.bind("#{modifier}-h", proc{print "Hello(¤³¤ó¤Ë¤Á¤Ï)\n"})
+ add('command', 'label'=>'Print goodbye(¤µ¤è¤¦¤Ê¤é)',
+ 'command'=>proc{print "Goodbye(¤µ¤è¤¦¤Ê¤é)\n"},
+ 'accelerator'=>"#{modifier}+G", 'underline'=>6)
+ $menu8x_demo.bind("#{modifier}-g", proc{print "Goodbye(¤µ¤è¤¦¤Ê¤é)\n"})
+
+ TkMenu.new(m, 'tearoff'=>false) {|cascade_check|
+ cascade_menu.add('cascade', 'label'=>'Check buttons(¥Á¥§¥Ã¥¯¥Ü¥¿¥ó)',
+ 'menu'=>cascade_check, 'underline'=>0)
+ oil = TkVariable.new(0)
+ add('check', 'label'=>'¥ª¥¤¥ëÅÀ¸¡', 'variable'=>oil)
+ trans = TkVariable.new(0)
+ add('check', 'label'=>'¥È¥é¥ó¥¹¥ß¥Ã¥·¥ç¥óÅÀ¸¡', 'variable'=>trans)
+ brakes = TkVariable.new(0)
+ add('check', 'label'=>'¥Ö¥ì¡¼¥­ÅÀ¸¡', 'variable'=>brakes)
+ lights = TkVariable.new(0)
+ add('check', 'label'=>'¥é¥¤¥ÈÅÀ¸¡', 'variable'=>lights)
+ add('separator')
+ add('command', 'label'=>'¸½ºß¤ÎÃͤòɽ¼¨',
+ 'command'=>proc{showVars($menu8x_demo,
+ ['¥ª¥¤¥ëÅÀ¸¡', oil],
+ ['¥È¥é¥ó¥¹¥ß¥Ã¥·¥ç¥óÅÀ¸¡', trans],
+ ['¥Ö¥ì¡¼¥­ÅÀ¸¡', brakes],
+ ['¥é¥¤¥ÈÅÀ¸¡', lights])} )
+ invoke 1
+ invoke 3
+ }
+
+ TkMenu.new(m, 'tearoff'=>false) {|cascade_radio|
+ cascade_menu.add('cascade', 'label'=>'Radio buttons(¥é¥¸¥ª¥Ü¥¿¥ó)',
+ 'menu'=>cascade_radio, 'underline'=>0)
+ pointSize = TkVariable.new
+ add('radio', 'label'=>'10 ¥Ý¥¤¥ó¥È', 'variable'=>pointSize, 'value'=>10)
+ add('radio', 'label'=>'14 ¥Ý¥¤¥ó¥È', 'variable'=>pointSize, 'value'=>14)
+ add('radio', 'label'=>'18 ¥Ý¥¤¥ó¥È', 'variable'=>pointSize, 'value'=>18)
+ add('radio', 'label'=>'24 ¥Ý¥¤¥ó¥È', 'variable'=>pointSize, 'value'=>24)
+ add('radio', 'label'=>'32 ¥Ý¥¤¥ó¥È', 'variable'=>pointSize, 'value'=>32)
+ add('separator')
+ style = TkVariable.new
+ add('radio', 'label'=>'¥í¡¼¥Þ¥ó', 'variable'=>style, 'value'=>'roman')
+ add('radio', 'label'=>'¥Ü¡¼¥ë¥É', 'variable'=>style, 'value'=>'bold')
+ add('radio', 'label'=>'¥¤¥¿¥ê¥Ã¥¯', 'variable'=>style, 'value'=>'italic')
+ add('separator')
+ add('command', 'label'=>'¸½ºß¤ÎÃͤòɽ¼¨',
+ 'command'=>proc{showVars($menu8x_demo,
+ ['¥Ý¥¤¥ó¥È¥µ¥¤¥º', pointSize],
+ ['¥¹¥¿¥¤¥ë', style])} )
+ invoke 1
+ invoke 7
+ }
+ }
+
+ TkMenu.new(m, 'tearoff'=>false) {|icon_menu|
+ m.add('cascade', 'label'=>'Icons', 'menu'=>icon_menu, 'underline'=>0)
+ add('command',
+ 'bitmap'=>'@'+[$demo_dir,'images','pattern.bmp'].join(File::Separator),
+ 'hidemargin'=>1,
+ 'command'=>proc{TkDialog.new('title'=>'Bitmap Menu Entry',
+ 'text'=>'º£¤¢¤Ê¤¿¤¬ÁªÂò¤·¤¿¥á¥Ë¥å¡¼¤Î¹àÌܤϥƥ­¥¹¥È¤Ç¤Ï¤Ê¤¯¥Ó¥Ã¥È¥Þ¥Ã¥×¤òɽ¼¨¤·¤Æ¤¤¤Þ¤·¤¿¡£¤½¤ì°Ê³°¤ÎÅÀ¤Ç¤Ï¾¤Î¥á¥Ë¥å¡¼¹àÌܤÈÊѤï¤ê¤Þ¤»¤ó¡£',
+ 'bitmap'=>'', 'default'=>0,
+ 'buttons'=>'λ²ò')} )
+ ['info', 'questhead', 'error'].each{|icon|
+ add('command', 'bitmap'=>icon, 'hidemargin'=>1,
+ 'command'=>proc{print "You invoked the #{icon} bitmap\n"})
+ }
+
+ entryconfigure(2, 'columnbreak'=>1)
+ }
+
+ TkMenu.new(m, 'tearoff'=>false) {|more_menu|
+ m.add('cascade', 'label'=>'More', 'menu'=>more_menu, 'underline'=>0)
+ [ '¥¨¥ó¥È¥ê','Ê̤Υ¨¥ó¥È¥ê','²¿¤â¤·¤Ê¤¤','¤Û¤È¤ó¤É²¿¤â¤·¤Ê¤¤',
+ '¿ÍÀ¸¤ò°ÕµÁ¤¢¤ë¤â¤Î¤Ë' ].each{|i|
+ add('command', 'label'=>i,
+ 'command'=>proc{print "You invoked \"#{i}\"\n"})
+ }
+ }
+
+ TkMenu.new(m) {|colors_menu|
+ m.add('cascade', 'label'=>'Colors', 'menu'=>colors_menu, 'underline'=>1)
+ ['red', 'orange', 'yellow', 'green', 'blue'].each{|c|
+ add('command', 'label'=>c, 'background'=>c,
+ 'command'=>proc{print "You invoked \"#{c}\"\n"})
+ }
+ }
+
+ $menu8x_demo.configure('menu'=>m)
+}
+
+TkMenu.bind('<MenuSelect>',
+ proc{|w|
+ begin
+ label = w.entrycget('active', 'label')
+ rescue
+ label = ' '
+ end
+ $menu8xstatus.value = label
+ Tk.update('idletasks')
+ }, '%W')
+
+end ; # Tk 8.x
diff --git a/ext/tk/sample/demos-jp/menubu.rb b/ext/tk/sample/demos-jp/menubu.rb
new file mode 100644
index 0000000000..90056e02c6
--- /dev/null
+++ b/ext/tk/sample/demos-jp/menubu.rb
@@ -0,0 +1,223 @@
+require "tkcanvas"
+
+def optionMenu(menubutton, varName, firstValue, *rest)
+ varName.value = firstValue
+ configoptions = {'textvariable'=>varName,'indicatoron'=>'on',
+ 'relief'=>'raised','borderwidth'=>2,'highlightthickness'=>2,
+ 'anchor'=>'c','direction'=>'flush'}
+ configoptions.each {|key, value|
+ menubutton.configure(key, value)
+ }
+ menu = TkMenu.new(menubutton) {
+ tearoff 'off'
+ add 'radio', 'label'=>firstValue, 'variable'=>varName
+ }
+ menubutton.menu(menu)
+ for i in rest
+ menu.add 'radio', 'label'=>i, 'variable'=>varName
+ end
+
+ return menu
+end
+
+if defined?($menubu_demo) && $menubu_demo
+ $menubu_demo.destroy
+ $menubu_demo = nil
+end
+
+$menubu_demo = TkToplevel.new {|w|
+ title("Menu Button Demonstration")
+ iconname("menubutton")
+}
+
+positionWindow($menubu_demo)
+
+# version check
+if $tk_version.to_f < 8.0
+
+# label À¸À®
+TkLabel.new($menubu_demo,'font'=>$font,'wraplength'=>'4i','justify'=>'left') {
+ text("¼Â¹Ô¤·¤è¤¦¤È¤·¤¿¥¹¥¯¥ê¥×¥È¤Ï Tk8.0 °Ê¾å¤ÇÍøÍѤǤ­¤ëµ¡Ç½¤òÍøÍѤ·¤Æ¤¤¤ë¤¿¤á¡¢¤¢¤Ê¤¿¤Î Ruby#{VERSION}/Tk#{$tk_version}#{(Tk::JAPANIZED_TK)? 'jp': ''} ¤Ç¤ÏÀµ¾ï¤Ë¼Â¹Ô¤Ç¤­¤Þ¤»¤ó¡£¤è¤Ã¤Æ¥Ç¥â¤Î¼Â¹Ô¤òÃæ»ß¤·¤Þ¤·¤¿¡£¤¿¤À¤·¡¢²¼¤Î¥³¡¼¥É»²¾È¥Ü¥¿¥ó¤ò²¡¤¹¤³¤È¤Ç¡¢¼Â¹Ô¤¬Ãæ»ß¤µ¤ì¤¿¥¹¥¯¥ê¥×¥È¤Î¥½¡¼¥¹¤ò»²¾È¤¹¤ë¤³¤È¤Ï²Äǽ¤Ç¤¹¡£")
+}.pack('side'=>'top')
+
+# frame À¸À®
+TkFrame.new($menubu_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $menubu_demo
+ $menubu_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'menu'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+else ; # Tk8.x
+
+body = TkFrame.new($menubu_demo)
+body.pack('expand'=>'yes', 'fill'=>'both')
+
+below = TkMenubutton.new(body) {
+ text "Below"
+ underline 0
+ direction 'below'
+ relief 'raised'
+}
+belowMenu = TkMenu.new(below) {
+ tearoff 0
+ add 'command', 'label'=>"Below menu: first item", 'command'=>proc {puts "\"You have selected the first item from the Below menu.\""}
+ add 'command', 'label'=>"Below menu: second item", 'command'=>proc {puts "\"You have selected the second item from the Below menu.\""}
+}
+below.menu(belowMenu)
+below.grid('row'=>0, 'column'=>1, 'sticky'=>'n')
+
+below = TkMenubutton.new(body) {
+ text "Below"
+ underline 0
+ direction 'below'
+ relief 'raised'
+}
+belowMenu = TkMenu.new(below) {
+ tearoff 0
+ add 'command', 'label'=>"Below menu: first item", 'command'=>proc {puts "\"You have selected the first item from the Below menu.\""}
+ add 'command', 'label'=>"Below menu: second item", 'command'=>proc {puts "\"You have selected the second item from the Below menu.\""}
+}
+below.menu(belowMenu)
+below.grid('row'=>0, 'column'=>1, 'sticky'=>'n')
+
+below = TkMenubutton.new(body) {
+ text "Below"
+ underline 0
+ direction 'below'
+ relief 'raised'
+}
+belowMenu = TkMenu.new(below) {
+ tearoff 0
+ add 'command', 'label'=>"Below menu: first item", 'command'=>proc {puts "\"You have selected the first item from the Below menu.\""}
+ add 'command', 'label'=>"Below menu: second item", 'command'=>proc {puts "\"You have selected the second item from the Below menu.\""}
+}
+below.menu(belowMenu)
+below.grid('row'=>0, 'column'=>1, 'sticky'=>'n')
+
+right = TkMenubutton.new(body) {
+ text "Right"
+ underline 0
+ direction 'right'
+ relief 'raised'
+}
+rightMenu = TkMenu.new(right) {
+ tearoff 0
+ add 'command', 'label'=>"Right menu: first item", 'command'=>proc {puts "\"You have selected the first item from the Left menu.\""}
+ add 'command', 'label'=>"Right menu: second item", 'command'=>proc {puts "\"You have selected the second item from the Right menu.\""}
+}
+right.menu(rightMenu)
+right.grid('row'=>1, 'column'=>0, 'sticky'=>'w')
+
+left = TkMenubutton.new(body) {
+ text "Left"
+ underline 0
+ direction 'left'
+ relief 'raised'
+}
+leftMenu = TkMenu.new(left) {
+ tearoff 0
+ add 'command', 'label'=>"Left menu: first item", 'command'=>proc {puts "\"You have selected the first item from the Left menu.\""}
+ add 'command', 'label'=>"Left menu: second item", 'command'=>proc {puts "\"You have selected the second item from the Left menu.\""}
+}
+left.menu(leftMenu)
+left.grid('row'=>1, 'column'=>2, 'sticky'=>'e')
+
+center = TkFrame.new(body) {
+ grid('row'=>1, 'column'=>1, 'sticky'=>'news')
+}
+
+above = TkMenubutton.new(body) {
+ text "Above"
+ underline 0
+ direction 'above'
+ relief 'raised'
+}
+aboveMenu = TkMenu.new(above) {
+ tearoff 0
+ add 'command', 'label'=>"Above menu: first item", 'command'=>proc {puts "\"You have selected the first item from the Above menu.\""}
+ add 'command', 'label'=>"Above menu: second item", 'command'=>proc {puts "\"You have selected the second item from the Above menu.\""}
+}
+above.menu(aboveMenu)
+above.grid('row'=>2, 'column'=>1, 'sticky'=>'s')
+
+center = TkFrame.new(body) {
+ grid('row'=>1, 'column'=>1, 'sticky'=>'news')
+}
+
+TkFrame.new($menubu_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc {
+ tmppath = $menubu_demo
+ $menubu_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc { showCode 'menubu' }
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'expand'=>'yes', 'fill'=>'x', 'pady'=>'2m')
+
+msg = TkLabel.new(center) {
+# font $font
+ wraplength '4i'
+ justify 'left'
+ text "¤³¤ì¤Ï¥á¥Ë¥å¡¼¥Ü¥¿¥ó¤Î¥Ç¥â¤Ç¤¹¡£\"Below\"¤Î¥Ü¥¿¥ó¤Ï\
+²¼¤Ë¥á¥Ë¥å¡¼¤ò½Ð¤·¡¢\"Right\"¤Î¥Ü¥¿¥ó¤Ï±¦¤Ë¥á¥Ë¥å¡¼¤ò½Ð¤·¤Æ¡¢\
+¡Ä¡Ä¤È¤Ê¤ê¤Þ¤¹¡£¤³¤Îʸ¾Ï¤Î²¼¤Ë¤Ï2¤Ä¤Î¥ª¥×¥·¥ç¥ó¥á¥Ë¥å¡¼¤¬¤¢¤ê¤Þ¤¹¡£\
+1¤Ä¤ÏÉáÄ̤Υá¥Ë¥å¡¼¤Ç¡¢¤â¤¦1¤Ä¤Ï16¿§¤Î¥Ñ¥ì¥Ã¥È¤Ç¤¹¡£"
+}
+msg.pack('side'=>'top', 'padx'=>25, 'pady'=>25)
+
+TkFrame.new(center) {|f|
+ menubuttonoptions = TkVariable.new
+ mbutton = TkMenubutton.new(f)
+ options = optionMenu(mbutton, menubuttonoptions,
+ 'one', 'two', 'three')
+ mbutton.pack('side'=>'left', 'padx'=>25, 'pady'=>25)
+ paletteColor = TkVariable.new
+ colors = ['Black','red4','DarkGreen','NavyBlue', 'gray75',
+ 'Red','Green','Blue','gray50','Yellow','Cyan','Magenta',
+ 'White','Brown','DarkSeaGreen','DarkViolet']
+ colorMenuButton = TkMenubutton.new(f)
+ m = optionMenu(colorMenuButton, paletteColor, *colors)
+ topBorderColor = 'gray50'
+ bottomBorderColor = 'gray75'
+ for i in 0..15
+ image = TkPhotoImage.new('height'=>16, 'width'=>16)
+ image.put(topBorderColor, 0, 0, 16, 1)
+ image.put(topBorderColor, 0, 1, 1, 16)
+ image.put(bottomBorderColor, 0, 15, 16, 16)
+ image.put(bottomBorderColor, 15, 1, 16, 16)
+ image.put(colors[i], 1, 1, 15, 15)
+
+ selectimage = TkPhotoImage.new('height'=>16, 'width'=>16)
+ selectimage.put('Black', 0, 0, 16, 2)
+ selectimage.put('Black', 0, 2, 2, 16)
+ selectimage.put('Black', 2, 14, 16, 16)
+ selectimage.put('Black', 14, 2, 16, 14)
+ selectimage.put(colors[i], 2, 2, 14, 14)
+
+ m.entryconfigure(i, 'image'=>image, 'selectimage'=>selectimage, 'hidemargin'=>'on')
+ end
+ m.configure('tearoff', 'on')
+ for c in ['Black', 'gray75', 'gray50', 'White']
+ m.entryconfigure(c, 'columnbreak'=>1)
+ end
+ colorMenuButton.pack('side'=>'left', 'padx'=>25, 'pady'=>25)
+ pack 'padx'=>25, 'pady'=>25
+}
+
+end ; # Tk8.x
diff --git a/ext/tk/sample/demos-jp/msgbox.rb b/ext/tk/sample/demos-jp/msgbox.rb
new file mode 100644
index 0000000000..cf53d70487
--- /dev/null
+++ b/ext/tk/sample/demos-jp/msgbox.rb
@@ -0,0 +1,85 @@
+#
+# message boxes widget demo (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($msgbox_demo) && $msgbox_demo
+ $msgbox_demo.destroy
+ $msgbox_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$msgbox_demo = TkToplevel.new {|w|
+ title("Message Box Demonstration")
+ iconname("messagebox")
+ positionWindow(w)
+}
+
+# label À¸À®
+TkLabel.new($msgbox_demo, 'font'=>$font, 'wraplength'=>'4i', 'justify'=>'left',
+ 'text'=>"ɽ¼¨¤¹¤ë¥¢¥¤¥³¥ó¤È¥á¥Ã¥»¡¼¥¸¥Ü¥Ã¥¯¥¹¤Î¼ïÎà¤òÁª¤ó¤Ç²¼¤µ¤¤¡£¤½¤·¤Æ \"¥á¥Ã¥»¡¼¥¸¥Ü¥Ã¥¯¥¹\" ¥Ü¥¿¥ó¤ò²¡¤¹¤È¡¢»ØÄꤷ¤¿¥á¥Ã¥»¡¼¥¸¥Ü¥Ã¥¯¥¹¤¬É½¼¨¤µ¤ì¤Þ¤¹¡£").pack('side'=>'top')
+
+# frame À¸À®
+TkFrame.new($msgbox_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $msgbox_demo
+ $msgbox_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'msgbox'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥á¥Ã¥»¡¼¥¸¥Ü¥Ã¥¯¥¹'
+ command proc{showMessageBox $msgbox_demo}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame À¸À®
+$msgbox_leftframe = TkFrame.new($msgbox_demo)
+$msgbox_rightframe = TkFrame.new($msgbox_demo)
+$msgbox_leftframe .pack('side'=>'left', 'expand'=>'yes', 'fill'=>'y',
+ 'pady'=>'.5c', 'padx'=>'.5c')
+$msgbox_rightframe.pack('side'=>'left', 'expand'=>'yes', 'fill'=>'y',
+ 'pady'=>'.5c', 'padx'=>'.5c')
+
+TkLabel.new($msgbox_leftframe, 'text'=>'¥¢¥¤¥³¥ó').pack('side'=>'top')
+TkFrame.new($msgbox_leftframe, 'relief'=>'ridge', 'bd'=>1, 'height'=>2)\
+.pack('side'=>'top', 'fill'=>'x', 'expand'=>'no')
+
+$msgboxIcon = TkVariable.new('info')
+['error', 'info', 'question', 'warning'].each {|icon|
+ TkRadioButton.new($msgbox_leftframe, 'text'=>icon, 'variable'=>$msgboxIcon,
+ 'relief'=>'flat', 'value'=>icon, 'width'=>16,
+ 'anchor'=>'w').pack('side'=>'top', 'pady'=>2,
+ 'anchor'=>'w', 'fill'=>'x')
+}
+
+TkLabel.new($msgbox_rightframe, 'text'=>'¼ïÎà').pack('side'=>'top')
+TkFrame.new($msgbox_rightframe, 'relief'=>'ridge', 'bd'=>1, 'height'=>2)\
+.pack('side'=>'top', 'fill'=>'x', 'expand'=>'no')
+
+$msgboxType = TkVariable.new('ok')
+['abortretryignore', 'ok', 'okcancel',
+ 'retrycancel', 'yesno', 'yesnocancel'].each {|type|
+ TkRadioButton.new($msgbox_rightframe, 'text'=>type, 'variable'=>$msgboxType,
+ 'relief'=>'flat', 'value'=>type, 'width'=>16,
+ 'anchor'=>'w').pack('side'=>'top', 'pady'=>2,
+ 'anchor'=>'w', 'fill'=>'x')
+}
+
+def showMessageBox(w)
+ button = Tk.messageBox('icon'=>$msgboxIcon.value, 'type'=>$msgboxType.value,
+ 'title'=>'Message', 'parent'=>w,
+ 'message'=>"¤³¤ì¤Ï \"#{$msgboxType.value}\" ¤È¤¤¤¦¼ïÎà¤Î¥á¥Ã¥»¡¼¥¸¥Ü¥Ã¥¯¥¹¤Ç¡¢\"#{$msgboxIcon.value}\" ¤Î¥¢¥¤¥³¥ó¤¬É½¼¨¤µ¤ì¤Æ¤¤¤Þ¤¹¡£")
+
+ Tk.messageBox('icon'=>'info', 'type'=>'ok', 'parent'=>w,
+ 'message'=>"¤¢¤Ê¤¿¤Ï \"#{button}\" ¤ò²¡¤·¤Þ¤·¤¿¤Í¡£")
+end
+
diff --git a/ext/tk/sample/demos-jp/plot.rb b/ext/tk/sample/demos-jp/plot.rb
new file mode 100644
index 0000000000..464dd4441d
--- /dev/null
+++ b/ext/tk/sample/demos-jp/plot.rb
@@ -0,0 +1,118 @@
+#
+# 2-D plot widget demo (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($plot_demo) && $plot_demo
+ $plot_demo.destroy
+ $plot_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$plot_demo = TkToplevel.new {|w|
+ title("Plot Demonstration")
+ iconname("Plot")
+ positionWindow(w)
+}
+
+# label À¸À®
+TkLabel.new($plot_demo, 'font'=>$font, 'wraplength'=>'4i', 'justify'=>'left',
+ 'text'=>"¤³¤Î¥¦¥£¥ó¥É¥¦¤Ï´Êñ¤Ê2¼¡¸µ¤Î¥×¥í¥Ã¥È¤ò´Þ¤ó¤À¥­¥ã¥ó¥Ð¥¹ widget¤Ç¤¹¡£É½¼¨¤µ¤ì¤¿ÅÀ¤ò¥Þ¥¦¥¹¥Ü¥¿¥ó1¤Ç¥É¥é¥Ã¥°¤·¤Æ¥Ç¡¼¥¿¤ò¤¤¤¸¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£"){
+ pack('side'=>'top')
+}
+
+# frame À¸À®
+$plot_buttons = TkFrame.new($plot_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $plot_demo
+ $plot_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'plot'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}
+$plot_buttons.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# font ÀßÄê
+plotFont = '-*-Helvetica-Medium-R-Normal--*-180-*-*-*-*-*-*'
+
+# canvas ÀßÄê
+$plot_canvas = TkCanvas.new($plot_demo,'relief'=>'raised','width'=>450,'height'=>300)
+$plot_canvas.pack('side'=>'top', 'fill'=>'x')
+
+# plot À¸À®
+TkcLine.new($plot_canvas, 100, 250, 400, 250, 'width'=>2)
+TkcLine.new($plot_canvas, 100, 250, 100, 50, 'width'=>2)
+TkcText.new($plot_canvas, 225, 20,
+ 'text'=>"´Êñ¤Ê¥×¥í¥Ã¥È", 'font'=>plotFont, 'fill'=>'brown')
+
+(0..10).each {|i|
+ x = 100 + (i * 30)
+ TkcLine.new($plot_canvas, x, 250, x, 245, 'width'=>2)
+ TkcText.new($plot_canvas, x, 254,
+ 'text'=>10*i, 'font'=>plotFont, 'anchor'=>'n')
+}
+(0..5).each {|i|
+ y = 250 - (i * 40)
+ TkcLine.new($plot_canvas, 100, y, 105, y, 'width'=>2)
+ TkcText.new($plot_canvas, 96, y,
+ 'text'=>"#{i*50}.0", 'font'=>plotFont, 'anchor'=>'e')
+}
+
+for xx, yy in [[12,56],[20,94],[33,98],[32,120],[61,180],[75,160],[98,223]]
+ x = 100 + (3*xx)
+ y = 250 - (4*yy)/5
+ item = TkcOval.new($plot_canvas, x-6, y-6, x+6, y+6,
+ 'width'=>1, 'outline'=>'black', 'fill'=>'SkyBlue2')
+ item.addtag 'point'
+end
+
+$plot_canvas.itembind('point', 'Any-Enter',
+ proc{$plot_canvas.itemconfigure 'current','fill','red'})
+$plot_canvas.itembind('point', 'Any-Leave',
+ proc{$plot_canvas.itemconfigure 'current','fill','SkyBlue2'})
+$plot_canvas.itembind('point', '1',
+ proc{|x,y| plotDown $plot_canvas,x,y}, "%x %y")
+$plot_canvas.itembind('point', 'ButtonRelease-1',
+ proc{$plot_canvas.dtag 'selected'})
+$plot_canvas.bind('B1-Motion',
+ proc{|x,y| plotMove $plot_canvas,x,y}, "%x %y")
+
+$plot = {'lastX'=>0, 'lastY'=>0}
+
+# plotDown --
+# This method is invoked when the mouse is pressed over one of the
+# data points. It sets up state to allow the point to be dragged.
+#
+# Arguments:
+# w - The canvas window.
+# x, y - The coordinates of the mouse press.
+
+def plotDown (w, x, y)
+ w.dtag 'selected'
+ w.addtag_withtag 'selected', 'current'
+ w.raise 'current'
+ $plot['lastX'] = x
+ $plot['lastY'] = y
+end
+
+# plotMove --
+# This method is invoked during mouse motion events. It drags the
+# current item.
+#
+# Arguments:
+# w - The canvas window.
+# x, y - The coordinates of the mouse.
+
+def plotMove (w, x, y)
+ w.move 'selected', x - $plot['lastX'], y - $plot['lastY']
+ $plot['lastX'] = x
+ $plot['lastY'] = y
+end
+
diff --git a/ext/tk/sample/demos-jp/puzzle.rb b/ext/tk/sample/demos-jp/puzzle.rb
new file mode 100644
index 0000000000..7e7aafac2f
--- /dev/null
+++ b/ext/tk/sample/demos-jp/puzzle.rb
@@ -0,0 +1,105 @@
+#
+# widet demo 'puzzle' (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($puzzle_demo) && $puzzle_demo
+ $puzzle_demo.destroy
+ $puzzle_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$puzzle_demo = TkToplevel.new {|w|
+ title("15-Puzzle Demonstration")
+ iconname("15-Puzzle")
+ positionWindow(w)
+}
+
+# label À¸À®
+msg = TkLabel.new($puzzle_demo) {
+ font $font
+ wraplength '4i'
+ justify 'left'
+ text "²¼¤Î15-¥Ñ¥º¥ë¤Ï¥Ü¥¿¥ó¤ò½¸¤á¤Æ¤Ç¤­¤Æ¤¤¤Þ¤¹¡£¶õ¤¤¤Æ¤¤¤ë½ê¤ÎÎ٤Υԡ¼¥¹¤ò¥¯¥ê¥Ã¥¯¤¹¤ë¤È¡¢¤½¤Î¥Ô¡¼¥¹¤¬¤½¤Î¶õ¤¤¤Æ¤¤¤ë¾ì½ê¤Ë¥¹¥é¥¤¥É¤·¤Þ¤¹¡£¤³¤ÎÁàºî¤ò³¤±¡¢¥Ô¡¼¥¹¤¬¤½¤Î¿ô¤Î½ç¤Ë¾å¤«¤é²¼¡¢º¸¤«¤é±¦¤Ëʤ֤褦¤Ë¤·¤Æ¤¯¤À¤µ¤¤¡£"
+}
+msg.pack('side'=>'top')
+
+# frame À¸À®
+TkFrame.new($puzzle_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $puzzle_demo
+ $puzzle_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'puzzle'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame À¸À®
+#
+# Special trick: scrollbar widget ¤òÀ¸À®¤·¤Æ¤½¤Î trough color ¤òÍѤ¤¤ë¤³¤È¤Ç
+# ¶õÇòÉôʬ¤Î¤¿¤á¤Î°Å¿§¤òÁªÂò¤·¡¤ÀßÄꤹ¤ë
+#
+s = TkScrollbar.new($puzzle_demo)
+base = TkFrame.new($puzzle_demo) {
+ width 120
+ height 120
+ borderwidth 2
+ relief 'sunken'
+ bg s['troughcolor']
+}
+s.destroy
+base.pack('side'=>'top', 'padx'=>'1c', 'pady'=>'1c')
+
+# proc ¤Î¥¹¥³¡¼¥×¤òÊĤ¸¤ë¤¿¤á¡¤proc À¸À®¥á¥½¥Ã¥É¤òÍѰÕ
+# ¤³¤¦¤·¤Æ¤ª¤«¤Í¤Ð¡¤¥ë¡¼¥×Ãæ¤ÇÃͤ¬ÊѲ½¤¹¤ë num ¤Î±Æ¶Á¤ò¼õ¤±¤Æ
+# puzzleSwitch ¤ÎÂè 2 °ú¿ô¤¬ÊѲ½¤·¤Æ¤·¤Þ¤¤¡¤´üÂÔÄ̤ê¤Ë¤Ï¤Ê¤é¤Ê¤¤¡¥
+def def_puzzleswitch_proc(w, num)
+ proc{puzzleSwitch w, num}
+end
+
+$xpos = {}
+$ypos = {}
+order = [3,1,6,2,5,7,15,13,4,11,8,9,14,10,12]
+(0..14).each{|i|
+ num = order[i]
+ $xpos[num] = (i % 4) * 0.25
+ $ypos[num] = (i / 4) * 0.25
+ TkButton.new(base) {|w|
+ relief 'raised'
+ text num
+ highlightthickness 0
+ command def_puzzleswitch_proc(w, num)
+ }.place('relx'=>$xpos[num], 'rely'=>$ypos[num],
+ 'relwidth'=>0.25, 'relheight'=>0.25)
+}
+$xpos['space'] = 0.75
+$ypos['space'] = 0.75
+
+############
+def puzzleSwitch(w, num)
+ if ( ($ypos[num] >= ($ypos['space'] - 0.01)) \
+ && ($ypos[num] <= ($ypos['space'] + 0.01)) \
+ && ($xpos[num] >= ($xpos['space'] - 0.26)) \
+ && ($xpos[num] <= ($xpos['space'] + 0.26))) \
+ || (($xpos[num] >= ($xpos['space'] - 0.01)) \
+ && ($xpos[num] <= ($xpos['space'] + 0.01)) \
+ && ($ypos[num] >= ($ypos['space'] - 0.26)) \
+ && ($ypos[num] <= ($ypos['space'] + 0.26)))
+ tmp = $xpos['space']
+ $xpos['space'] = $xpos[num]
+ $xpos[num] = tmp
+ tmp = $ypos['space']
+ $ypos['space'] = $ypos[num]
+ $ypos[num] = tmp
+ w.place('relx'=>$xpos[num], 'rely'=>$ypos[num])
+ end
+end
+
diff --git a/ext/tk/sample/demos-jp/radio.rb b/ext/tk/sample/demos-jp/radio.rb
new file mode 100644
index 0000000000..871503029e
--- /dev/null
+++ b/ext/tk/sample/demos-jp/radio.rb
@@ -0,0 +1,80 @@
+#
+# radiobutton widget demo (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($radio_demo) && $radio_demo
+ $radio_demo.destroy
+ $radio_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$radio_demo = TkToplevel.new {|w|
+ title("Radiobutton Demonstration")
+ iconname("radio")
+ positionWindow(w)
+}
+
+# label À¸À®
+msg = TkLabel.new($radio_demo) {
+ font $font
+ wraplength '4i'
+ justify 'left'
+ text "²¼¤Ë¤Ï2¤Ä¤Î¥é¥¸¥ª¥Ü¥¿¥ó¥°¥ë¡¼¥×¤¬É½¼¨¤µ¤ì¤Æ¤¤¤Þ¤¹¡£¥Ü¥¿¥ó¤ò¥¯¥ê¥Ã¥¯¤¹¤ë¤È¡¢¤½¤Î¥Ü¥¿¥ó¤À¤±¤¬¤½¤Î¥°¥ë¡¼¥×¤ÎÃæ¤ÇÁªÂò¤µ¤ì¤Þ¤¹¡£³Æ¥°¥ë¡¼¥×¤ËÂФ·¤Æ¤½¤Î¥°¥ë¡¼¥×¤ÎÃæ¤Î¤É¤Î¥Ü¥¿¥ó¤¬ÁªÂò¤µ¤ì¤Æ¤¤¤ë¤«¤ò¼¨¤¹ÊÑ¿ô¤¬³ä¤êÅö¤Æ¤é¤ì¤Æ¤¤¤Þ¤¹¡£¸½ºß¤ÎÊÑ¿ô¤ÎÃͤò¸«¤ë¤Ë¤Ï¡ÖÊÑ¿ô»²¾È¡×¥Ü¥¿¥ó¤ò¥¯¥ê¥Ã¥¯¤·¤Æ¤¯¤À¤µ¤¤¡£"
+}
+msg.pack('side'=>'top')
+
+# ÊÑ¿ôÀ¸À®
+size = TkVariable.new
+color = TkVariable.new
+
+# frame À¸À®
+TkFrame.new($radio_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $radio_demo
+ $radio_demo = nil
+ $showVarsWin[tmppath.path] = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'radio'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text 'ÊÑ¿ô»²¾È'
+ command proc{
+ showVars($radio_demo, ['size', size], ['color', color])
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame À¸À®
+f_left = TkFrame.new($radio_demo)
+f_right = TkFrame.new($radio_demo)
+f_left.pack('side'=>'left', 'expand'=>'yes', 'padx'=>'.5c', 'pady'=>'.5c')
+f_right.pack('side'=>'left', 'expand'=>'yes', 'padx'=>'.5c', 'pady'=>'.5c')
+
+# radiobutton À¸À®
+[10, 12, 18, 24].each {|sz|
+ TkRadioButton.new(f_left) {
+ text "¥Ý¥¤¥ó¥È¥µ¥¤¥º #{sz}"
+ variable size
+ relief 'flat'
+ value sz
+ }.pack('side'=>'top', 'pady'=>2, 'anchor'=>'w')
+}
+
+['ÀÖ', 'ÎÐ', 'ÀÄ', '²«', 'Üô', '»ç'].each {|col|
+ TkRadioButton.new(f_right) {
+ text col
+ variable color
+ relief 'flat'
+ value col.downcase
+ }.pack('side'=>'top', 'pady'=>2, 'anchor'=>'w')
+}
+
diff --git a/ext/tk/sample/demos-jp/rmt b/ext/tk/sample/demos-jp/rmt
new file mode 100644
index 0000000000..64583d714e
--- /dev/null
+++ b/ext/tk/sample/demos-jp/rmt
@@ -0,0 +1,265 @@
+#!/usr/local/bin/ruby
+
+# rmt --
+# This script implements a simple remote-control mechanism for
+# Tk applications. It allows you to select an application and
+# then type commands to that application.
+
+require 'tk'
+
+class Rmt
+ def initialize(parent=nil)
+ win = self
+
+ unless parent
+ parent = TkRoot.new
+ end
+ root = TkWinfo.toplevel(parent)
+ root.minsize(1,1)
+
+ # The instance variable below keeps track of the remote application
+ # that we're sending to. If it's an empty string then we execute
+ # the commands locally.
+ @app = 'local'
+ @mode = 'Ruby'
+
+ # The instance variable below keeps track of whether we're in the
+ # middle of executing a command entered via the text.
+ @executing = 0
+
+ # The instance variable below keeps track of the last command executed,
+ # so it can be re-executed in response to !! commands.
+ @lastCommand = ""
+
+ # Create menu bar. Arrange to recreate all the information in the
+ # applications sub-menu whenever it is cascaded to.
+
+ TkFrame.new(root, 'relief'=>'raised', 'bd'=>2) {|f|
+ pack('side'=>'top', 'fill'=>'x')
+ TkMenubutton.new(f, 'text'=>'File', 'underline'=>0) {|mb|
+ TkMenu.new(mb) {|mf|
+ mb.menu(mf)
+ TkMenu.new(mf) {|ma|
+ postcommand proc{win.fillAppsMenu ma}
+ mf.add('cascade', 'label'=>'Select Application',
+ 'menu'=>ma, 'underline'=>0)
+ }
+ add('command', 'label'=>'Quit',
+ 'command'=>proc{root.destroy}, 'underline'=>0)
+ }
+ pack('side'=>'left')
+ }
+ }
+
+ # Create text window and scrollbar.
+
+ @txt = TkText.new(root, 'relief'=>'sunken', 'bd'=>2, 'setgrid'=>true) {|t|
+ TkScrollbar.new(root, 'command'=>proc{|*args| t.yview *args}) {
+ pack('side'=>'right', 'fill'=>'both')
+ }
+ pack('side'=>'left')
+ }
+
+ @promptEnd = TkTextMark.new(@txt, 'insert')
+
+ # Create a binding to forward commands to the target application,
+ # plus modify many of the built-in bindings so that only information
+ # in the current command can be deleted (can still set the cursor
+ # earlier in the text and select and insert; just can't delete).
+
+ @txt.bindtags([@txt, TkText, root, 'all'])
+ @txt.bind('Return', proc{
+ @txt.set_insert('end - 1c')
+ @txt.insert('insert', "\n")
+ win.invoke
+ Tk.callback_break
+ })
+ @txt.bind('Delete', proc{
+ begin
+ @txt.tag_remove('sel', 'sel.first', @promptEnd)
+ rescue
+ end
+ if @txt.tag_nextrange('sel', '1.0', 'end') == []
+ if @txt.compare('insert', '<', @promptEnd)
+ Tk.callback_break
+ end
+ end
+ })
+ @txt.bind('BackSpace', proc{
+ begin
+ @txt.tag_remove('sel', 'sel.first', @promptEnd)
+ rescue
+ end
+ if @txt.tag_nextrange('sel', '1.0', 'end') == []
+ if @txt.compare('insert', '<', @promptEnd)
+ Tk.callback_break
+ end
+ end
+ })
+ @txt.bind('Control-d', proc{
+ if @txt.compare('insert', '<', @promptEnd)
+ Tk.callback_break
+ end
+ })
+ @txt.bind('Control-k', proc{
+ if @txt.compare('insert', '<', @promptEnd)
+ @txt.set_insert(@promptEnd)
+ end
+ })
+ @txt.bind('Control-t', proc{
+ if @txt.compare('insert', '<', @promptEnd)
+ Tk.callback_break
+ end
+ })
+ @txt.bind('Meta-d', proc{
+ if @txt.compare('insert', '<', @promptEnd)
+ Tk.callback_break
+ end
+ })
+ @txt.bind('Meta-BackSpace', proc{
+ if @txt.compare('insert', '<=', @promptEnd)
+ Tk.callback_break
+ end
+ })
+ @txt.bind('Control-h', proc{
+ if @txt.compare('insert', '<=', @promptEnd)
+ Tk.callback_break
+ end
+ })
+
+ @txt.tag_configure('bold', 'font'=>['Courier', 12, 'bold'])
+
+ @app = Tk.appname('rmt')
+ if (@app =~ /^rmt(.*)$/)
+ root.title("Tk Remote Controller#{$1}")
+ root.iconname("Tk Remote#{$1}")
+ end
+ prompt
+ @txt.focus
+ #@app = TkWinfo.appname(TkRoot.new)
+ end
+
+ def tkTextInsert(w,s)
+ return if s == ""
+ begin
+ if w.compare('sel.first','<=','insert') \
+ && w.compare('sel.last','>=','insert')
+ w.tag_remove('sel', 'sel.first', @promptEnd)
+ w.delete('sel.first', 'sel.last')
+ end
+ rescue
+ end
+ w.insert('insert', s)
+ w.see('insert')
+ end
+
+ # The method below is used to print out a prompt at the
+ # insertion point (which should be at the beginning of a line
+ # right now).
+
+ def prompt
+ @txt.insert('insert', "#{@app}: ")
+ @promptEnd.set('insert')
+ @promptEnd.gravity = 'left'
+ @txt.tag_add('bold', "#{@promptEnd.path} linestart", @promptEnd)
+ end
+
+ # The method below executes a command (it takes everything on the
+ # current line after the prompt and either sends it to the remote
+ # application or executes it locally, depending on "app".
+
+ def invoke
+ cmd = @txt.get(@promptEnd, 'insert')
+ @executing += 1
+ case (@mode)
+ when 'Tcl'
+ if Tk.info('complete', cmd)
+ if (cmd == "!!\n")
+ cmd = @lastCommand
+ else
+ @lastCommand = cmd
+ end
+ begin
+ msg = Tk.appsend(@app, false, cmd)
+ rescue
+ msg = "Error: #{$!}"
+ end
+ @txt.insert('insert', msg + "\n") if msg != ""
+ prompt
+ @promptEnd.set('insert')
+ end
+
+ when 'Ruby'
+ if (cmd == "!!\n")
+ cmd = @lastCommand
+ end
+ complete = true
+ begin
+ eval("proc{#{cmd}}")
+ rescue
+ complete = false
+ end
+ if complete
+ @lastCommand = cmd
+ begin
+# msg = Tk.appsend(@app, false,
+# 'ruby',
+# '"(' + cmd.gsub(/[][$"]/, '\\\\\&') + ').to_s"')
+ msg = Tk.rb_appsend(@app, false, cmd)
+ rescue
+ msg = "Error: #{$!}"
+ end
+ @txt.insert('insert', msg + "\n") if msg != ""
+ prompt
+ @promptEnd.set('insert')
+ end
+ end
+
+ @executing -= 1
+ @txt.yview_pickplace('insert')
+ end
+
+ # The following method is invoked to change the application that
+ # we're talking to. It also updates the prompt for the current
+ # command, unless we're in the middle of executing a command from
+ # the text item (in which case a new prompt is about to be output
+ # so there's no need to change the old one).
+
+ def newApp(appName, mode)
+ @app = appName
+ @mode = mode
+ if @executing == 0
+ @promptEnd.gravity = 'right'
+ @txt.delete("#{@promptEnd.path} linestart", @promptEnd)
+ @txt.insert(@promptEnd, "#{appName}: ")
+ @txt.tag_add('bold', "#{@promptEnd.path} linestart", @promptEnd)
+ @promptEnd.gravity = 'left'
+ end
+ end
+
+ # The method below will fill in the applications sub-menu with a list
+ # of all the applications that currently exist.
+
+ def fillAppsMenu(menu)
+ win = self
+ begin
+ menu.delete(0,'last')
+ rescue
+ end
+ TkWinfo.interps.sort.each{|ip|
+ if Tk.appsend(ip, false, 'info commands ruby') == ""
+ mode = 'Tcl'
+ else
+ mode = 'Ruby'
+ end
+ menu.add('command', 'label'=>format("%s (#{mode}/Tk)", ip),
+ 'command'=>proc{win.newApp ip, mode})
+ }
+ menu.add('command', 'label'=>format("local (Ruby/Tk)"),
+ 'command'=>proc{win.newApp 'local', 'Ruby'})
+ end
+end
+
+Rmt.new
+
+Tk.mainloop
diff --git a/ext/tk/sample/demos-jp/rolodex b/ext/tk/sample/demos-jp/rolodex
new file mode 100644
index 0000000000..9ed1f6d7d1
--- /dev/null
+++ b/ext/tk/sample/demos-jp/rolodex
@@ -0,0 +1,320 @@
+#!/usr/local/bin/ruby
+#
+# rolodex --
+# This script is a part of Tom LaStrange's rolodex
+#
+# Copyright (C) 1998 by Takaaki Tateishi <ttate@jaist.ac.jp>
+# Time-stamp: "98/07/20 12:00:02 ttate"
+#
+
+require "tk"
+
+
+def show_help(topic,x=0,y=0)
+ if( topic.is_a?(TkWindow) )
+ w = TkWinfo.containing(x,y)
+ if( TkWinfo.exist?(w) )
+ topic = w
+ end
+ end
+
+ if( $helpTopics.include?(topic) )
+ msg = $helpTopics[topic]
+ else
+ msg = "Sorry, but no help is available for this topic"
+ end
+ TkDialog.new("title"=>"Rolodex Help",
+ "message"=>"Information on #{topic}:\n\n#{msg}",
+ "default_button"=>0,
+ "buttons"=>["OK"])
+end
+
+def fillCard
+ clearAction
+ $root.frame.entry[1].insert(0,"Takaaki Tateishi")
+ $root.frame.entry[2].insert(0,"Japan Advanced Institute of Science and Techonology")
+ $root.frame.entry[3].insert(0,"1-1 Asahidai, Tatsunokuchi")
+ $root.frame.entry[4].insert(0,"Ishikawa 923-1292, Japan")
+ $root.frame.entry[5].insert(0,"private")
+ $root.frame.entry[6].insert(0,"***-***-****")
+ $root.frame.entry[7].insert(0,"***-***-****")
+end
+
+def addAction
+ for i in 1..7
+ STDERR.print format("%-12s %s\n",
+ RolodexFrame::LABEL[i],
+ $root.frame.entry[i].value)
+ end
+end
+
+def clearAction
+ for i in 1..7
+ $root.frame.entry[i].delete(0,"end")
+ end
+end
+
+def fileAction
+ TkDialog.new("title"=>"File Selection",
+ "message"=>"This is a dummy file selection dialog box.\n",
+ "default_button"=>0,
+ "buttons"=>["OK"])
+ STDERR.print "dummy file name\n"
+end
+
+def deleteAction
+ result = TkDialog.new("title"=>"Confirm Action",
+ "message"=>"Are you sure?",
+ "default_button"=>0,
+ "buttons"=>["Cancel"])
+ if( result.value == 0 )
+ clearAction
+ end
+end
+
+
+class RolodexFrame < TkFrame
+ attr_reader :entry, :label
+
+ LABEL = ["","Name:","Address:","","","Home Phone:","Work Phone:","Fax:"]
+
+ def initialize(parent=nil,keys=nil)
+ super(parent,keys)
+ self["relief"] = "flat"
+
+ @i = []
+ @label = []
+ @entry = []
+ for i in 1..7
+ @i[i] = TkFrame.new(self)
+ @i[i].pack("side"=>"top",
+ "pady"=>2,
+ "anchor"=>"e")
+ @label[i] = TkLabel.new(@i[i],
+ "text"=>LABEL[i],
+ "anchor"=>"e")
+ @entry[i] = TkEntry.new(@i[i],
+ "width"=>30,
+ "relief"=>"sunken")
+ @entry[i].pack("side"=>"right")
+ @label[i].pack("side"=>"right")
+ end
+ end
+end
+
+class RolodexButtons < TkFrame
+ attr_reader :clear, :add, :search, :delete
+
+ def initialize(parent,keys=nil)
+ super(parent,keys)
+ @clear = TkButton.new(self,
+ "text" => "Clear")
+ @add = TkButton.new(self,
+ "text" => "Add")
+ @search = TkButton.new(self,
+ "text" => "Search")
+ @delete = TkButton.new(self,
+ "text" => "Delete")
+ for w in [@clear,@add,@search,@delete]
+ w.pack("side"=>"left", "padx"=>2)
+ end
+ end
+end
+
+class RolodexMenuFrame < TkFrame
+ attr_reader :file_menu, :help_menu, :file, :help
+
+ def initialize(parent,keys=nil)
+ super(parent,keys)
+ configure("relief"=>"raised",
+ "borderwidth"=>1)
+
+ @file = TkMenubutton.new(self,
+ "text"=>"File",
+ "underline"=>0)
+ @file_menu = TkMenu.new(@file)
+ @file_menu.add("command",
+ "label" => "Load ...",
+ "command" => proc{fileAction},
+ "underline" => 0)
+ @file_menu.add("command",
+ "label" => "Exit",
+ "command" => proc{$root.destroy},
+ "underline" => 0)
+ @file.menu(@file_menu)
+ @file.pack("side"=>"left")
+
+ @help = TkMenubutton.new(self,
+ "text"=>"Help",
+ "underline"=>0)
+ @help_menu = TkMenu.new(@help)
+ @help_menu.add("command",
+ "label"=>"On Context...",
+ "command"=>proc{show_help("context")},
+ "underline"=>3)
+ @help_menu.add("command",
+ "label"=>"On Help...",
+ "command"=>proc{show_help("help")},
+ "underline"=>3)
+ @help_menu.add("command",
+ "label"=>"On Window...",
+ "command"=>proc{show_help("window")},
+ "underline"=>3)
+ @help_menu.add("command",
+ "label"=>"On Keys...",
+ "command"=>proc{show_help("keys")},
+ "underline"=>3)
+ @help_menu.add("command",
+ "label"=>"On version...",
+ "command"=>proc{show_help("version")},
+ "underline"=>3)
+ @help.menu(@help_menu)
+ @help.pack("side"=>"right")
+ end
+end
+
+class Rolodex < TkRoot
+ attr_reader :frame, :buttons, :menu
+
+ def initialize
+ super
+ @frame = RolodexFrame.new(self)
+ @frame.pack("side"=>"top",
+ "fill"=>"y",
+ "anchor"=>"center")
+ @buttons = RolodexButtons.new(self)
+ @buttons.pack("side"=>"bottom",
+ "pady"=>2,
+ "anchor"=>"center")
+ @menu = RolodexMenuFrame.new(self)
+ @menu.pack("before"=>@frame,
+ "side"=>"top",
+ "fill"=>"x")
+ end
+end
+
+$root = Rolodex.new
+
+$root.buttons.delete.configure("command"=>proc{deleteAction})
+$root.buttons.add.configure("command"=>proc{addAction})
+$root.buttons.clear.configure("command"=>proc{clearAction})
+$root.buttons.search.configure("command"=>proc{addAction; fillCard})
+
+$root.buttons.clear.configure("text"=>"Clear Ctrl+C")
+$root.bind("Control-c",proc{clearAction})
+
+$root.buttons.add.configure("text"=>"Add Ctrl+A")
+$root.bind("Control-a",proc{addAction})
+
+$root.buttons.search.configure("text"=>"Search Ctrl+S")
+$root.bind("Control-s",proc{addAction; fillCard})
+
+$root.buttons.delete.configure("text"=>"Delete... Ctrl+D")
+$root.bind("Control-d",proc{deleteAction})
+
+$root.menu.file_menu.entryconfigure(1, "accel"=>"Ctrl+F")
+$root.bind("Control-f",proc{fileAction})
+
+$root.menu.file_menu.entryconfigure(2, "accel"=>"Ctrl+Q")
+$root.bind("Control-q",proc{$root.destroy})
+
+$root.frame.entry[1].focus
+
+$root.bind("Any-F1",
+ proc{|event| show_help(event.widget, event.x_root, event.y_root)})
+$root.bind("Any-Help",
+ proc{|event| show_help(event.widget, event.x_root, event.y_root)})
+
+
+$helpTopics = {}
+
+$helpTopics[$root.menu.file] = <<EOF
+This is the "file" menu. It can be used to invoke\
+some overall operations on the rolodex applications,\
+such as loading a file or exiting.
+EOF
+
+$helpTopics[$root.menu.file_menu.index(0)] = <<EOF
+The "Load" entry in the "File" menu posts a dialog box\
+that you can use to select a rolodex file
+EOF
+
+$helpTopics[$root.menu.file_menu.index(1)] = <<EOF
+The "Exit" entry in the "File" menu causes the rolodex\
+application to terminate
+EOF
+
+$helpTopics[$root.frame.entry[1]] = <<EOF
+In this field of the rolodex entry you should\
+type the person's name
+EOF
+
+$helpTopics[$root.frame.entry[2]] = <<EOF
+In this field of the rolodex entry you should\
+type the first line of the person's address
+EOF
+
+$helpTopics[$root.frame.entry[3]] = <<EOF
+In this field of the rolodex entry you should\
+type the second line of the person's address
+EOF
+
+$helpTopics[$root.frame.entry[4]] = <<EOF
+In this field of the rolodex entry you should\
+type the third line of the person's address
+EOF
+
+$helpTopics[$root.frame.entry[5]] = <<EOF
+In this field of the rolodex entry you should\
+type the person's home phone number, or "private"\
+if the person doesn't want his or he number publicized
+EOF
+
+$helpTopics[$root.frame.entry[6]] = <<EOF
+In this field of the rolodex entry you should\
+type the person's work phone number
+EOF
+
+$helpTopics[$root.frame.entry[7]] = <<EOF
+In this field of the rolodex entry you should\
+type the phone number for the person's FAX machine
+EOF
+
+$helpTopics["context"] = <<EOF
+Unfortunately, this application doesn't support context-sensitive\
+help in the usual way, because when this demo was written Ruby/Tk\
+didn't have a grab mechanism and this is needed for context-sensitive\
+help. Instead, you can achive much the same effect by simply moving\
+the mouse over the window you're curious about and pressing the\
+Help or F1 keys. You can do this anytime.
+EOF
+
+$helpTopics["help"] = <<EOF
+This application provides only very crude help. Besides the\
+entries in this menu, you can get help on individual windows\
+by moving the mouse cursor over the window and pressing the\
+Help or F1 keys.
+EOF
+
+$helpTopics["window"] = <<EOF
+This window is a dummy rolodex application created as part of\
+Tom LaStrange's toolkit benchmark. It doesn't really do anything\
+useful except to demonstrate a few features of the Ruby/Tk.
+EOF
+
+$helpTopics["keys"] = <<EOF
+The following accelerator keys are defined for this application\
+(in addition to those already available for the entry windows):
+Ctrl+A: Add
+Ctrl+C: Clear
+Ctrl+D: Delete
+Ctrl+F: Enter file name
+Ctrl+Q: Exit application (quit)
+Ctrl+S: Search (dummy operation)
+EOF
+
+$helpTopics["version"] = <<EOF
+This is version 1.0.
+EOF
+
+Tk.mainloop
diff --git a/ext/tk/sample/demos-jp/rolodex-j b/ext/tk/sample/demos-jp/rolodex-j
new file mode 100644
index 0000000000..170f7bb96b
--- /dev/null
+++ b/ext/tk/sample/demos-jp/rolodex-j
@@ -0,0 +1,311 @@
+#!/usr/local/bin/ruby
+#
+# rolodex --
+# ¤³¤Î¥¹¥¯¥ê¥×¥È¤Ï Tom LaStrange ¤Î rolodex ¤Î°ìÉô¤Ç¤¹¡£
+#
+# Copyright (C) 1998 by Takaaki Tateishi <ttate@jaist.ac.jp>
+# Time-stamp: "98/07/20 12:00:36 ttate"
+#
+
+require "tk"
+
+# UTF8¤Ø¤ÎÊÑ´¹¤ò¹Ô¤Ê¤¦¡£
+
+$encoding = "euc-jp"
+def j(str)
+ Tk.toUTF8(str,$encoding)
+end
+
+
+def show_help(topic,x=0,y=0)
+ if( topic.is_a?(TkWindow) )
+ w = TkWinfo.containing(x,y)
+ if( w.is_a?(TkWindow) )
+ if( TkWinfo.exist?(w) )
+ topic = w
+ end
+ end
+ end
+
+ if( $helpTopics.include?(topic) )
+ msg = $helpTopics[topic]
+ else
+ msg = "¤³¤Î¥È¥Ô¥Ã¥¯¤Ë¤Ä¤¤¤Æ¤Î¥Ø¥ë¥×¤Ï¤Þ¤À»ÈÍѤǤ­¤Þ¤»¤ó"
+ end
+ TkDialog.new("title"=>"Rolodex Help",
+ "message"=>j("¡Ö#{topic}¡×\n\n#{msg}"),
+ "default_button"=>0,
+ "buttons"=>["OK"])
+end
+
+def fillCard
+ clearAction
+ $root.frame.entry[1].insert(0,j("ΩÀÐ ¹§¾´"))
+ $root.frame.entry[2].insert(0,j("923-1292 ÀÐÀ"))
+ $root.frame.entry[3].insert(0,j("ä¸ýÄ® °°Âæ 1-1"))
+ $root.frame.entry[4].insert(0,j("ËÌΦÀèü²Ê³Øµ»½ÑÂç³Ø±¡Âç³Ø"))
+ $root.frame.entry[5].insert(0,"private")
+ $root.frame.entry[6].insert(0,"***-***-****")
+ $root.frame.entry[7].insert(0,"***-***-****")
+end
+
+def addAction
+ for i in 1..7
+ STDERR.print format("%-12s %s\n",
+ RolodexFrame::LABEL[i],
+ Tk.fromUTF8($root.frame.entry[i].value,$encoding))
+ end
+end
+
+def clearAction
+ for i in 1..7
+ $root.frame.entry[i].delete(0,"end")
+ end
+end
+
+def fileAction
+ TkDialog.new("title"=>"File Selection",
+ "message"=>j("¤³¤ì¤Ï¥Õ¥¡¥¤¥ëÁªÂò¥À¥¤¥¢¥í¥°¤Î¥À¥ß¡¼¤Ç¤¹¡£\n"),
+ "default_button"=>0,
+ "buttons"=>["OK"])
+ STDERR.print "dummy file name\n"
+end
+
+def deleteAction
+ result = TkDialog.new("title"=>"Confirm Action",
+ "message"=>j("¤è¤í¤·¤¤¤Ç¤¹¤«¡©"),
+ "default_button"=>0,
+ "buttons"=>[j("¥­¥ã¥ó¥»¥ë")])
+ if( result.value == 0 )
+ clearAction
+ end
+end
+
+
+class RolodexFrame < TkFrame
+ attr_reader :entry, :label
+
+ LABEL = ["","̾Á°:","½»½ê","","","ÅÅÏÃ(¼«Âð):","ÅÅÏÃ(²ñ¼Ò):","Fax:"]
+
+ def initialize(parent=nil,keys=nil)
+ super(parent,keys)
+ self["relief"] = "flat"
+
+ @i = []
+ @label = []
+ @entry = []
+ for i in 1..7
+ @i[i] = TkFrame.new(self)
+ @i[i].pack("side"=>"top",
+ "pady"=>2,
+ "anchor"=>"e")
+ @label[i] = TkLabel.new(@i[i],
+ "text"=>j(LABEL[i]),
+ "anchor"=>"e")
+ @entry[i] = TkEntry.new(@i[i],
+ "width"=>30,
+ "relief"=>"sunken")
+ @entry[i].pack("side"=>"right")
+ @label[i].pack("side"=>"right")
+ end
+ end
+end
+
+class RolodexButtons < TkFrame
+ attr_reader :clear, :add, :search, :delete
+
+ def initialize(parent,keys=nil)
+ super(parent,keys)
+ @clear = TkButton.new(self,
+ "text" => j("¥¯¥ê¥¢¡¼"))
+ @add = TkButton.new(self,
+ "text" => j("ÄɲÃ"))
+ @search = TkButton.new(self,
+ "text" => j("¸¡º÷"))
+ @delete = TkButton.new(self,
+ "text" => j("¾Ãµî"))
+ for w in [@clear,@add,@search,@delete]
+ w.pack("side"=>"left", "padx"=>2)
+ end
+ end
+end
+
+class RolodexMenuFrame < TkFrame
+ attr_reader :file_menu, :help_menu, :file, :help
+
+ def initialize(parent,keys=nil)
+ super(parent,keys)
+ configure("relief"=>"raised",
+ "borderwidth"=>1)
+
+ @file = TkMenubutton.new(self,
+ "text"=>j("¥Õ¥¡¥¤¥ë"),
+ "underline"=>0)
+ @file_menu = TkMenu.new(@file)
+ @file_menu.add("command",
+ "label" => j("ÆÉ¤ß¹þ¤ß ..."),
+ "command" => proc{fileAction},
+ "underline" => 0)
+ @file_menu.add("command",
+ "label" => j("½ªÎ»"),
+ "command" => proc{$root.destroy},
+ "underline" => 0)
+ @file.menu(@file_menu)
+ @file.pack("side"=>"left")
+
+ @help = TkMenubutton.new(self,
+ "text"=>j("¥Ø¥ë¥×"),
+ "underline"=>0)
+ @help_menu = TkMenu.new(@help)
+ @help_menu.add("command",
+ "label"=>j("¥³¥ó¥Æ¥­¥¹¥È¤Ë¤Ä¤¤¤Æ"),
+ "command"=>proc{show_help("¥³¥ó¥Æ¥­¥¹¥È")},
+ "underline"=>3)
+ @help_menu.add("command",
+ "label"=>j("¥Ø¥ë¥×¤Ë¤Ä¤¤¤Æ"),
+ "command"=>proc{show_help("¥Ø¥ë¥×")},
+ "underline"=>3)
+ @help_menu.add("command",
+ "label"=>j("¥¦¥£¥ó¥É¥¦¤Ë¤Ä¤¤¤Æ"),
+ "command"=>proc{show_help("¥¦¥£¥ó¥É¥¦")},
+ "underline"=>3)
+ @help_menu.add("command",
+ "label"=>j("¥­¡¼Áàºî¤Ë¤Ä¤¤¤Æ"),
+ "command"=>proc{show_help("¥­¡¼Áàºî")},
+ "underline"=>3)
+ @help_menu.add("command",
+ "label"=>j("¥Ð¡¼¥¸¥ç¥ó¾ðÊó"),
+ "command"=>proc{show_help("¥Ð¡¼¥¸¥ç¥ó¾ðÊó")},
+ "underline"=>3)
+ @help.menu(@help_menu)
+ @help.pack("side"=>"right")
+ end
+end
+
+class Rolodex < TkRoot
+ attr_reader :frame, :buttons, :menu
+
+ def initialize
+ super
+ @frame = RolodexFrame.new(self)
+ @frame.pack("side"=>"top",
+ "fill"=>"y",
+ "anchor"=>"center")
+ @buttons = RolodexButtons.new(self)
+ @buttons.pack("side"=>"bottom",
+ "pady"=>2,
+ "anchor"=>"center")
+ @menu = RolodexMenuFrame.new(self)
+ @menu.pack("before"=>@frame,
+ "side"=>"top",
+ "fill"=>"x")
+ end
+end
+
+$root = Rolodex.new
+
+$root.buttons.delete.configure("command"=>proc{deleteAction})
+$root.buttons.add.configure("command"=>proc{addAction})
+$root.buttons.clear.configure("command"=>proc{clearAction})
+$root.buttons.search.configure("command"=>proc{addAction; fillCard})
+
+$root.buttons.clear.configure("text"=>j("¥¯¥ê¥¢¡¼ Ctrl+C"))
+$root.bind("Control-c",proc{clearAction})
+
+$root.buttons.add.configure("text"=>j("Äɲà Ctrl+A"))
+$root.bind("Control-a",proc{addAction})
+
+$root.buttons.search.configure("text"=>j("¸¡º÷ Ctrl+S"))
+$root.bind("Control-s",proc{addAction; fillCard})
+
+$root.buttons.delete.configure("text"=>j("¾Ãµî Ctrl+D"))
+$root.bind("Control-d",proc{deleteAction})
+
+$root.menu.file_menu.entryconfigure(1, "accel"=>"Ctrl+F")
+$root.bind("Control-f",proc{fileAction})
+
+$root.menu.file_menu.entryconfigure(2, "accel"=>"Ctrl+Q")
+$root.bind("Control-q",proc{$root.destroy})
+
+$root.frame.entry[1].focus
+
+$root.bind("Any-F1",
+ proc{|event| show_help(event.widget, event.x_root, event.y_root)})
+$root.bind("Any-Help",
+ proc{|event| show_help(event.widget, event.x_root, event.y_root)})
+
+
+$helpTopics = {}
+
+$helpTopics[$root.menu.file] = <<EOF
+¤³¤ì¤Ï¡Ö¥Õ¥¡¥¤¥ë¡×¥á¥Ë¥å¡¼¤Ç¤¹¡£¡ÖÆÉ¤ß¹þ¤ß¡×¤ä¡Ö½ªÎ»¡×¤Ê¤É¤ò
+¹Ô¤Ê¤¦¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+EOF
+
+$helpTopics[$root.menu.file_menu.index(0)] = <<EOF
+¥Õ¥¡¥¤¥ë¤ÎÆÉ¤ß¹þ¤ß¤ò¹Ô¤Ê¤¦¤È¤­¤Ë»È¤¤¤Þ¤¹¡£
+EOF
+
+$helpTopics[$root.menu.file_menu.index(1)] = <<EOF
+¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¤ò½ªÎ»¤¹¤ë¤È¤­¤Ë»È¤¤¤Þ¤¹¡£
+EOF
+
+$helpTopics[$root.frame.entry[1]] = <<EOF
+̾Á°¤òµ­Æþ¤¹¤ë¥¨¥ó¥È¥ê¤Ç¤¹¡£
+EOF
+
+$helpTopics[$root.frame.entry[2]] = <<EOF
+½»½ê¤òµ­Æþ¤¹¤ë¥¨¥ó¥È¥ê¤Ç¤¹¡£
+EOF
+
+$helpTopics[$root.frame.entry[3]] = <<EOF
+½»½ê¤òµ­Æþ¤¹¤ë¥¨¥ó¥È¥ê¤Ç¤¹¡£
+EOF
+
+$helpTopics[$root.frame.entry[4]] = <<EOF
+½»½ê¤òµ­Æþ¤¹¤ë¥¨¥ó¥È¥ê¤Ç¤¹¡£
+EOF
+
+$helpTopics[$root.frame.entry[5]] = <<EOF
+¼«Âð¤ÎÅÅÏÃÈÖ¹æ¤òµ­Æþ¤¹¤ë¥¨¥ó¥È¥ê¤Ç¤¹¡£¸ø³«\
+¤·¤¿¤¯¤Ê¤¤¤È¤­¤Ï private ¤Èµ­Æþ¤·¤Þ¤¹¡£
+EOF
+
+$helpTopics[$root.frame.entry[6]] = <<EOF
+²ñ¼Ò¤ÎÅÅÏÃÈÖ¹æ¤òµ­Æþ¤¹¤ë¥¨¥ó¥È¥ê¤Ç¤¹¡£
+EOF
+
+$helpTopics[$root.frame.entry[7]] = <<EOF
+FAXÈÖ¹æ¤òµ­Æþ¤¹¤ë¥¨¥ó¥È¥ê¤Ç¤¹¡£
+EOF
+
+$helpTopics["¥³¥ó¥Æ¥­¥¹¥È"] = <<EOF
+Ruby/Tk¤Ç¤Ïgrab¤Îµ¡¹½¤¬¤Ê¤¤¤¿¤á¤³¤Î¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¤Ç¤Ï\
+¥³¥ó¥Æ¥­¥¹¥È¥Ø¥ë¥×¤Ï¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤Þ¤»¤ó¡£
+¤·¤«¤·Æ±¤¸¤è¤¦¤Ê¸ú²Ì¤òbind¤È¥Þ¥¦¥¹¤Î°ÌÃÖ¤ÎWedget¤òÃΤë\
+¤³¤È¤ÇÆÀ¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+EOF
+
+$helpTopics["¥Ø¥ë¥×"] = <<EOF
+¥Þ¥¦¥¹¤ò¥¦¥£¥ó¥É¥¦¤Ë¤¢¤ï¤»¤ÆF1¥­¡¼¤ò²¡¤¹¤³¤È¤Ë¤è¤Ã¤Æ\
+¤½¤Î¥Ø¥ë¥×¤ò¸«¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+EOF
+
+$helpTopics["¥¦¥£¥ó¥É¥¦"] = <<EOF
+¤³¤Î¥¦¥£¥ó¥É¥¦¤Ï¥À¥ß¡¼¤Ç¤¹¡£
+EOF
+
+$helpTopics["¥­¡¼Áàºî"] = <<EOF
+Ctrl+A: ÄɲÃ
+Ctrl+C: ¥¯¥ê¥¢¡¼
+Ctrl+D: ¾Ãµî
+Ctrl+F: ¥Õ¥¡¥¤¥ëÁªÂò
+Ctrl+Q: ½ªÎ»
+Ctrl+S: ¸¡º÷
+EOF
+
+$helpTopics["¥Ð¡¼¥¸¥ç¥ó¾ðÊó"] = <<EOF
+¥Ð¡¼¥¸¥ç¥ó¤Ï 1.0 ¤Ç¤¹¡£
+EOF
+
+Tk.mainloop
diff --git a/ext/tk/sample/demos-jp/ruler.rb b/ext/tk/sample/demos-jp/ruler.rb
new file mode 100644
index 0000000000..d1a7818be4
--- /dev/null
+++ b/ext/tk/sample/demos-jp/ruler.rb
@@ -0,0 +1,197 @@
+#
+# ruler widget demo (called by 'widget')
+#
+
+# rulerMkTab --
+# This method creates a new triangular polygon in a canvas to
+# represent a tab stop.
+#
+# Arguments:
+# c - The canvas window.
+# x, y - Coordinates at which to create the tab stop.
+
+def rulerMkTab(c,x,y)
+ v = $demo_rulerInfo
+ TkcPolygon.new(c, x, y, x+v.size, y+v.size, x-v.size, y+v.size)
+end
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($ruler_demo) && $ruler_demo
+ $ruler_demo.destroy
+ $ruler_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$ruler_demo = TkToplevel.new {|w|
+ title("Ruler Demonstration")
+ iconname("ruler")
+ positionWindow(w)
+}
+
+# label À¸À®
+TkLabel.new($ruler_demo, 'font'=>$font, 'wraplength'=>'5i', 'justify'=>'left',
+ 'text'=>"¤³¤Î¥­¥ã¥ó¥Ð¥¹widget¤Ï¥ë¡¼¥é¡¼¤ÎÌÏ·¿¤Ç¤¹¡£¥ë¡¼¥é¡¼¤Î±¦¤Ë¤¢¤ë¤Î¤Ï¥¿¥Ö¥¹¥È¥Ã¥×¤Î°æ¸Í¤Ç¡¢¤³¤³¤«¤é°ú¤ÃÄ¥¤Ã¤Æ¤¯¤ë¤³¤È¤Ë¤è¤Ã¤Æ¥¿¥Ö¥¹¥È¥Ã¥×¤òºî¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£¤Þ¤¿¡¢¤¹¤Ç¤Ë¤¢¤ë¥¿¥Ö¥¹¥È¥Ã¥×¤òư¤«¤¹¤³¤È¤â¤Ç¤­¤Þ¤¹¡£¥¿¥Ö¥¹¥È¥Ã¥×¤ò¾åÊý¤Þ¤¿¤Ï²¼Êý¤Ë¤«¤¹¤ì¤ÆÉ½¼¨¤µ¤ì¤ë¤Þ¤Ç¥É¥é¥Ã¥°¤¹¤ë¤È¡¢¥Þ¥¦¥¹¥Ü¥¿¥ó¤òÎ¥¤·¤¿»þ¤Ë¤½¤Î¥¿¥Ö¥¹¥È¥Ã¥×¤Ï¾Ã¤¨¤Þ¤¹¡£"){
+ pack('side'=>'top')
+}
+
+# frame À¸À®
+$ruler_buttons = TkFrame.new($ruler_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $ruler_demo
+ $ruler_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'ruler'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}
+$ruler_buttons.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# canvas ÀßÄê
+$ruler_canvas = TkCanvas.new($ruler_demo, 'width'=>'14.8c', 'height'=>'2.5c')
+$ruler_canvas.pack('side'=>'top', 'fill'=>'x')
+
+# ÃÍÀßÄê
+unless Struct.const_defined?("RulerInfo")
+ $demo_rulerInfo = Struct.new("RulerInfo", :grid, :left, :right, :x, :y,
+ :top, :bottom, :size, :normalStyle,
+ :activeStyle, :deleteStyle).new
+end
+$demo_rulerInfo.grid = '.25c'
+$demo_rulerInfo.left = TkWinfo.fpixels($ruler_canvas, '1c')
+$demo_rulerInfo.right = TkWinfo.fpixels($ruler_canvas, '13c')
+$demo_rulerInfo.top = TkWinfo.fpixels($ruler_canvas, '1c')
+$demo_rulerInfo.bottom = TkWinfo.fpixels($ruler_canvas, '1.5c')
+$demo_rulerInfo.size = TkWinfo.fpixels($ruler_canvas, '.2c')
+$demo_rulerInfo.normalStyle = {'fill'=>'black'}
+if TkWinfo.depth($ruler_canvas) > 1
+ $demo_rulerInfo.activeStyle = {'fill'=>'red', 'stipple'=>''}
+ $demo_rulerInfo.deleteStyle = {'fill'=>'red',
+ 'stipple'=>'@'+[$demo_dir, 'images', 'gray25.bmp'].join(File::Separator)}
+else
+ $demo_rulerInfo.activeStyle = {'fill'=>'black', 'stipple'=>''}
+ $demo_rulerInfo.deleteStyle = {'fill'=>'black',
+ 'stipple'=>'@'+[$demo_dir, 'images', 'gray25.bmp'].join(File::Separator)}
+end
+
+TkcLine.new($ruler_canvas,
+ '1c', '0.5c', '1c', '1c', '13c', '1c', '13c', '0.5c', 'width'=>1)
+(0..11).each{|i|
+ x = i+1
+ TkcLine.new($ruler_canvas, "#{x}c", '1c', "#{x}c", '0.6c', 'width'=>1)
+ TkcLine.new($ruler_canvas, "#{x}.25c", '1c', "#{x}.25c", '0.8c', 'width'=>1)
+ TkcLine.new($ruler_canvas, "#{x}.5c", '1c', "#{x}.5c", '0.7c', 'width'=>1)
+ TkcLine.new($ruler_canvas, "#{x}.75c", '1c', "#{x}.75c", '0.8c', 'width'=>1)
+ TkcText.new($ruler_canvas, "#{x}.15c", '0.75c', 'text'=>i, 'anchor'=>'sw')
+}
+
+$rulerTag_well = TkcTag.new($ruler_canvas)
+$ruler_canvas\
+.addtag_withtag($rulerTag_well,
+ TkcRectangle.new($ruler_canvas,
+ '13.2c', '1c', '13.8c', '0.5c',
+ 'outline'=>'black',
+ 'fill'=>($ruler_canvas\
+ .configinfo('background'))[4]) )
+$ruler_canvas\
+.addtag_withtag($rulerTag_well,
+ rulerMkTab($ruler_canvas,
+ TkWinfo.pixels($ruler_canvas, '13.5c'),
+ TkWinfo.pixels($ruler_canvas, '.65c') ) )
+
+$rulerTag_well.bind('1', proc{|x,y| rulerNewTab($ruler_canvas,x,y)}, '%x %y')
+$ruler_canvas.itembind('tab', '1',
+ proc{|x,y| rulerSelectTab($ruler_canvas,x,y)}, '%x %y')
+$ruler_canvas.bind('B1-Motion',
+ proc{|x,y| rulerMoveTab($ruler_canvas,x,y)}, '%x %y')
+$ruler_canvas.bind('Any-ButtonRelease-1', proc{rulerReleaseTab($ruler_canvas)})
+
+# rulerNewTab --
+# Does all the work of creating a tab stop, including creating the
+# triangle object and adding tags to it to give it tab behavior.
+#
+# Arguments:
+# c - The canvas window.
+# x, y - The coordinates of the tab stop.
+
+def rulerNewTab(c,x,y)
+ v = $demo_rulerInfo
+ c.addtag_withtag('active', rulerMkTab(c,x,y))
+ c.addtag_withtag('tab', 'active')
+ v.x = x
+ v.y = y
+ rulerMoveTab(c,x,y)
+end
+
+# rulerSelectTab --
+# This method is invoked when mouse button 1 is pressed over
+# a tab. It remembers information about the tab so that it can
+# be dragged interactively.
+#
+# Arguments:
+# c - The canvas widget.
+# x, y - The coordinates of the mouse (identifies the point by
+# which the tab was picked up for dragging).
+
+def rulerSelectTab(c,x,y)
+ v = $demo_rulerInfo
+ v.x = c.canvasx(x, v.grid)
+ v.y = v.top+2
+ c.addtag_withtag('active', 'current')
+ c.itemconfigure('active', v.activeStyle)
+ c.raise('active')
+end
+
+# rulerMoveTab --
+# This method is invoked during mouse motion events to drag a tab.
+# It adjusts the position of the tab, and changes its appearance if
+# it is about to be dragged out of the ruler.
+#
+# Arguments:
+# c - The canvas widget.
+# x, y - The coordinates of the mouse.
+
+def rulerMoveTab(c,x,y)
+ v = $demo_rulerInfo
+ return if c.find_withtag('active') == []
+ cx = c.canvasx(x,v.grid)
+ cy = c.canvasy(y)
+ cx = v.left if cx < v.left
+ cx = v.right if cx > v.right
+ if (cy >= v.top && cy <= v.bottom)
+ cy = v.top+2
+ c.itemconfigure('active', v.activeStyle)
+ else
+ cy = cy-v.size-2
+ c.itemconfigure('active', v.deleteStyle)
+ end
+ c.move('active', cx-v.x, cy-v.y)
+ v.x = cx
+ v.y = cy
+end
+
+# rulerReleaseTab --
+# This method is invoked during button release events that end
+# a tab drag operation. It deselects the tab and deletes the tab if
+# it was dragged out of the ruler.
+#
+# Arguments:
+# c - The canvas widget.
+# x, y - The coordinates of the mouse.
+
+def rulerReleaseTab(c)
+ v = $demo_rulerInfo
+ return if c.find_withtag('active') == []
+ if v.y != v.top+2
+ c.delete('active')
+ else
+ c.itemconfigure('active', v.normalStyle)
+ c.dtag('active')
+ end
+end
+
diff --git a/ext/tk/sample/demos-jp/sayings.rb b/ext/tk/sample/demos-jp/sayings.rb
new file mode 100644
index 0000000000..f627396e0f
--- /dev/null
+++ b/ext/tk/sample/demos-jp/sayings.rb
@@ -0,0 +1,99 @@
+#
+# listbox widget demo 'sayings' (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($sayings_demo) && $sayings_demo
+ $sayings_demo.destroy
+ $sayings_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$sayings_demo = TkToplevel.new {|w|
+ title("Listbox Demonstration (well-known sayings)")
+ iconname("sayings")
+ positionWindow(w)
+}
+
+# label À¸À®
+msg = TkLabel.new($sayings_demo) {
+ font $font
+ wraplength '4i'
+ justify 'left'
+ text "²¼¤Î¥ê¥¹¥È¥Ü¥Ã¥¯¥¹¤Ë¤Ï¤¤¤í¤¤¤í¤Ê³Ê¸À¤¬Æþ¤Ã¤Æ¤¤¤Þ¤¹¡£¥ê¥¹¥È¤ò¥¹¥¯¥í¡¼¥ë¤µ¤»¤ë¤Î¤Ï¥¹¥¯¥í¡¼¥ë¥Ð¡¼¤Ç¤â¤Ç¤­¤Þ¤¹¤·¡¢¥ê¥¹¥È¥Ü¥Ã¥¯¥¹¤ÎÃæ¤Ç¥Þ¥¦¥¹¤Î¥Ü¥¿¥ó2(Ãæ¥Ü¥¿¥ó)¤ò²¡¤·¤¿¤Þ¤Þ¥É¥é¥Ã¥°¤·¤Æ¤â¤Ç¤­¤Þ¤¹¡£"
+}
+msg.pack('side'=>'top')
+
+# frame À¸À®
+TkFrame.new($sayings_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $sayings_demo
+ $sayings_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'sayings'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame À¸À®
+sayings_lbox = nil
+TkFrame.new($sayings_demo, 'borderwidth'=>10) {|w|
+ sv = TkScrollbar.new(w)
+ sh = TkScrollbar.new(w, 'orient'=>'horizontal')
+ sayings_lbox = TkListbox.new(w) {
+ setgrid 1
+ width 20
+ height 10
+ yscrollcommand proc{|first,last| sv.set first,last}
+ xscrollcommand proc{|first,last| sh.set first,last}
+ }
+ sv.command(proc{|*args| sayings_lbox.yview(*args)})
+ sh.command(proc{|*args| sayings_lbox.xview(*args)})
+
+ if $tk_version =~ /^4\.[01]/
+ sv.pack('side'=>'right', 'fill'=>'y')
+ sh.pack('side'=>'bottom', 'fill'=>'x')
+ sayings_lbox.pack('expand'=>'yes', 'fill'=>'y')
+
+ else
+ sayings_lbox.grid('row'=>0, 'column'=>0,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ sv.grid('row'=>0, 'column'=>1,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ sh.grid('row'=>1, 'column'=>0,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ TkGrid.rowconfigure(w, 0, 'weight'=>1, 'minsize'=>0)
+ TkGrid.columnconfigure(w, 0, 'weight'=>1, 'minsize'=>0)
+ end
+
+}.pack('side'=>'top', 'expand'=>'yes', 'fill'=>'y')
+
+sayings_lbox.insert(0,
+"Waste not, want not",
+"Early to bed and early to rise makes a man healthy, wealthy, and wise",
+"Ask not what your country can do for you, ask what you can do for your country",
+"I shall return",
+"NOT",
+"A picture is worth a thousand words",
+"User interfaces are hard to build",
+"Thou shalt not steal",
+"A penny for your thoughts",
+"Fool me once, shame on you; fool me twice, shame on me",
+"Every cloud has a silver lining",
+"Where there's smoke there's fire",
+"It takes one to know one",
+"Curiosity killed the cat",
+"Take this job and shove it",
+"Up a creek without a paddle",
+"I'm mad as hell and I'm not going to take it any more",
+"An apple a day keeps the doctor away",
+"Don't look a gift horse in the mouth"
+)
+
diff --git a/ext/tk/sample/demos-jp/search.rb b/ext/tk/sample/demos-jp/search.rb
new file mode 100644
index 0000000000..538c607c01
--- /dev/null
+++ b/ext/tk/sample/demos-jp/search.rb
@@ -0,0 +1,187 @@
+#
+# Text Search widget demo (called by 'widget')
+#
+
+# textLoadFile --
+# This method below loads a file into a text widget, discarding
+# the previous contents of the widget. Tags for the old widget are
+# not affected, however.
+#
+# Arguments:
+# w - The window into which to load the file. Must be a
+# text widget.
+# file - The name of the file to load. Must be readable.
+
+def textLoadFile(w,file)
+ w.delete('1.0', 'end')
+ f = open(file, 'r')
+ while(!f.eof?)
+ w.insert('end', f.read(1000))
+ end
+ f.close
+end
+
+# textSearch --
+# Search for all instances of a given string in a text widget and
+# apply a given tag to each instance found.
+#
+# Arguments:
+# w - The window in which to search. Must be a text widget.
+# string - The string to search for. The search is done using
+# exact matching only; no special characters.
+# tag - Tag to apply to each instance of a matching string.
+
+def textSearch(w, string, tag)
+ tag.remove('0.0', 'end')
+ return if string == ""
+ cur = '1.0'
+ loop {
+ cur, len = w.search_with_length(string, cur, 'end')
+ break if cur == ""
+ tag.add(cur, "#{cur} + #{len} char")
+ cur = w.index("#{cur} + #{len} char")
+ }
+end
+
+# textToggle --
+# This method is invoked repeatedly to invoke two commands at
+# periodic intervals. It normally reschedules itself after each
+# execution but if an error occurs (e.g. because the window was
+# deleted) then it doesn't reschedule itself.
+#
+# Arguments:
+# cmd1 - Command to execute when method is called.
+# sleep1 - Ms to sleep after executing cmd1 before executing cmd2.
+# cmd2 - Command to execute in the *next* invocation of this method.
+# sleep2 - Ms to sleep after executing cmd2 before executing cmd1 again.
+
+def textToggle(cmd1,sleep1,cmd2,sleep2)
+ sleep_list = [sleep2, sleep1]
+ TkAfter.new(proc{sleep = sleep_list.shift; sleep_list.push(sleep); sleep},
+ -1, cmd1, cmd2).start(sleep1)
+end
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($search_demo) && $search_demo
+ $search_demo.destroy
+ $search_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$search_demo = TkToplevel.new {|w|
+ title("Text Demonstration - Search and Highlight")
+ iconname("search")
+ positionWindow(w)
+}
+
+# frame À¸À®
+$search_buttons = TkFrame.new($search_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $search_demo
+ $search_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'search'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}
+$search_buttons.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame À¸À®
+TkFrame.new($search_demo) {|f|
+ TkLabel.new(f, 'text'=>'¥Õ¥¡¥¤¥ë̾:',
+ 'width'=>13, 'anchor'=>'w').pack('side'=>'left')
+ $search_fileName = TkVariable.new
+ TkEntry.new(f, 'width'=>40,
+ 'textvariable'=>$search_fileName) {
+ pack('side'=>'left')
+ bind('Return', proc{textLoadFile($search_text, $search_fileName.value)
+ $search_string_entry.focus})
+ focus
+ }
+ TkButton.new(f, 'text'=>'ÆÉ¤ß¹þ¤ß',
+ 'command'=>proc{textLoadFile($search_text,
+ $search_fileName.value)})\
+ .pack('side'=>'left', 'pady'=>5, 'padx'=>10)
+}.pack('side'=>'top', 'fill'=>'x')
+
+TkFrame.new($search_demo) {|f|
+ TkLabel.new(f, 'text'=>'¸¡º÷ʸ»úÎó:',
+ 'width'=>13, 'anchor'=>'w').pack('side'=>'left')
+ $search_searchString = TkVariable.new
+ $search_string_entry = TkEntry.new(f, 'width'=>40,
+ 'textvariable'=>$search_searchString) {
+ pack('side'=>'left')
+ bind('Return', proc{textSearch($search_text, $search_searchString.value,
+ $search_Tag)})
+ }
+ TkButton.new(f, 'text'=>'ȿž',
+ 'command'=>proc{textSearch($search_text,
+ $search_searchString.value,
+ $search_Tag)}) {
+ pack('side'=>'left', 'pady'=>5, 'padx'=>10)
+ }
+}.pack('side'=>'top', 'fill'=>'x')
+
+$search_text = TkText.new($search_demo, 'setgrid'=>true) {|t|
+ $search_Tag = TkTextTag.new(t)
+ TkScrollbar.new($search_demo, 'command'=>proc{|*args| t.yview(*args)}) {|sc|
+ t.yscrollcommand(proc{|first,last| sc.set first,last})
+ pack('side'=>'right', 'fill'=>'y')
+ }
+ pack('expand'=>'yes', 'fill'=>'both')
+}
+
+# Set up display styles for text highlighting.
+
+if TkWinfo.depth($search_demo) > 1
+ textToggle(proc{
+ begin
+ $search_Tag.configure('background'=>'#ce5555',
+ 'foreground'=>'white')
+ rescue
+ end
+ },
+ 800,
+ proc{
+ begin
+ $search_Tag.configure('background'=>'', 'foreground'=>'')
+ rescue
+ end
+ },
+ 200 )
+else
+ textToggle(proc{
+ begin
+ $search_Tag.configure('background'=>'black',
+ 'foreground'=>'white')
+ rescue
+ end
+ },
+ 800,
+ proc{
+ begin
+ $search_Tag.configure('background'=>'', 'foreground'=>'')
+ rescue
+ end
+ },
+ 200 )
+end
+$search_text.insert('1.0', '\
+¤³¤Î¥¦¥£¥ó¥É¥¦¤Ï¸¡º÷µ¡¹½¤ò¼Â¸½¤¹¤ë¤Î¤Ë¥Æ¥­¥¹¥È widget ¤Î¥¿¥°µ¡Ç½¤¬¤É¤Î
+¤è¤¦¤Ë»È¤ï¤ì¤ë¤Î¤«¤ò¥Ç¥â¤¹¤ë¤â¤Î¤Ç¤¹¡£¤Þ¤º¾å¤Î¥¨¥ó¥È¥ê¤Ë¥Õ¥¡¥¤¥ë̾¤òÆþ
+¤ì¡¢<¥ê¥¿¡¼¥ó> ¤ò²¡¤¹¤«¡Ö¥í¡¼¥É¡×¥Ü¥¿¥ó¤ò²¡¤·¤Æ¤¯¤À¤µ¤¤¡£¼¡¤Ë¤½¤Î²¼¤Î
+¥¨¥ó¥È¥ê¤Ëʸ»úÎó¤òÆþÎϤ·¡¢<¥ê¥¿¡¼¥ó> ¤ò²¡¤¹¤«¡Öȿž¡×¥Ü¥¿¥ó¤ò²¡¤·¤Æ¤¯
+¤À¤µ¤¤¡£¤¹¤ë¤È¥Õ¥¡¥¤¥ëÃæ¤Î¡¢¸¡º÷ʸ»úÎó¤È°ìÃפ¹¤ëÉôʬ¤ËÁ´¤Æ "search_Tag"
+¤È¤¤¤¦¥¿¥°¤¬¤Ä¤±¤é¤ì¡¢¥¿¥°¤Îɽ¼¨Â°À­¤È¤·¤Æ¤½¤Îʸ»úÎó¤¬ÅÀÌǤ¹¤ë¤è¤¦¤Ë
+ÀßÄꤵ¤ì¤Þ¤¹¡£')
+$search_text.set_insert '0.0'
+
+$search_fileName.value = ''
+$search_searchString.value = ''
+
diff --git a/ext/tk/sample/demos-jp/square b/ext/tk/sample/demos-jp/square
new file mode 100644
index 0000000000..b914b735b2
--- /dev/null
+++ b/ext/tk/sample/demos-jp/square
@@ -0,0 +1,74 @@
+#!/usr/local/bin/ruby
+
+# square --
+# This script generates a demo application containing only
+# a "square" widget. It's only usable if Tk has been compiled
+# with tkSquare.c and with the -DSQUARE_DEMO compiler switch.
+# This demo arranges the following bindings for the widget:
+#
+# Button-1 press/drag: moves square to mouse
+# "a": toggle size animation on/off
+#
+
+require 'tk'
+require 'tkafter'
+
+class TkSquare<TkWindow
+ def create_self
+ tk_call 'square', path
+ end
+ def size(amount=nil)
+ if amount
+ tk_send 'size', amount
+ else
+ number(tk_send 'size')
+ end
+ end
+ def position(x,y)
+ tk_send 'position', x, y
+ end
+end
+
+$s = TkSquare.new{
+ pack('expand'=>'yes', 'fill'=>'both')
+ bind('1', proc{|x,y| center(x,y)}, '%s %y')
+ bind('B1-Motion', proc{|x,y| center(x,y)}, '%s %y')
+ bind('a', proc{animate})
+ focus
+}
+TkRoot.new.minsize(1,1)
+
+# The procedure below centers the square on a given position.
+
+def center(x,y)
+ a = $s.size
+ $s.position(x-(a/2), y-(a/2))
+end
+
+# The procedures below provide a simple form of animation where
+# the box changes size in a pulsing pattern: larger, smaller, larger,
+# and so on.
+
+$inc = 0
+
+def timer_proc
+ a = $s.size
+ return if $inc == 0
+ $inc = -3 if a >= 40
+ $inc = 3 if a <= 10
+ $s.size(a+$inc)
+end
+
+$timer = TkAfter.new(30, -1, proc{timer_proc})
+
+def animate
+ if $inc == 0
+ $inc = 3
+ $timer.start
+ else
+ $inc = 0
+ $timer.stop
+ end
+end
+
+Tk.mainloop
diff --git a/ext/tk/sample/demos-jp/states.rb b/ext/tk/sample/demos-jp/states.rb
new file mode 100644
index 0000000000..cf8a6768b6
--- /dev/null
+++ b/ext/tk/sample/demos-jp/states.rb
@@ -0,0 +1,70 @@
+#
+# listbox widget demo 'states' (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($states_demo) && $states_demo
+ $states_demo.destroy
+ $states_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$states_demo = TkToplevel.new {|w|
+ title("Listbox Demonstration (states)")
+ iconname("states")
+ positionWindow(w)
+}
+
+# label À¸À®
+msg = TkLabel.new($states_demo) {
+ font $font
+ wraplength '4i'
+ justify 'left'
+ text "²¼¤Ë¤¢¤ë¤Î¤ÏÅÔÆ»Éܸ©Ì¾¤¬Æþ¤Ã¤¿¥¹¥¯¥í¡¼¥ë¥Ð¡¼ÉդΥꥹ¥È¥Ü¥Ã¥¯¥¹¤Ç¤¹¡£¥ê¥¹¥È¤ò¥¹¥¯¥í¡¼¥ë¤µ¤»¤ë¤Î¤Ï¥¹¥¯¥í¡¼¥ë¥Ð¡¼¤Ç¤â¤Ç¤­¤Þ¤¹¤·¡¢¥ê¥¹¥È¥Ü¥Ã¥¯¥¹¤ÎÃæ¤Ç¥Þ¥¦¥¹¤Î¥Ü¥¿¥ó2(Ãæ¥Ü¥¿¥ó)¤ò²¡¤·¤¿¤Þ¤Þ¥É¥é¥Ã¥°¤·¤Æ¤â¤Ç¤­¤Þ¤¹¡£"
+}
+msg.pack('side'=>'top')
+
+# frame À¸À®
+TkFrame.new($states_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $states_demo
+ $states_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'states'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame À¸À®
+states_lbox = nil
+TkFrame.new($states_demo, 'borderwidth'=>'.5c') {|w|
+ s = TkScrollbar.new(w)
+ states_lbox = TkListbox.new(w) {
+ setgrid 1
+ height 12
+ yscrollcommand proc{|first,last| s.set first,last}
+ }
+ s.command(proc{|*args| states_lbox.yview(*args)})
+ s.pack('side'=>'right', 'fill'=>'y')
+ states_lbox.pack('side'=>'left', 'expand'=>1, 'fill'=>'both')
+}.pack('side'=>'top', 'expand'=>'yes', 'fill'=>'y')
+
+ins_data = [
+ '°¦ÃÎ','ÀÄ¿¹','½©ÅÄ','ÀÐÀî','°ñ¾ë','´ä¼ê','°¦É²',
+ 'Âçʬ','Âçºå','²¬»³','²­Æì','¹áÀî','¼¯»ùÅç','¿ÀÆàÀî',
+ '´ôÉì','µþÅÔ','·§ËÜ','·²ÇÏ','¹âÃÎ','ºë¶Ì','º´²ì',
+ '¼¢²ì','ÀŲ¬','Å纬','ÀéÍÕ','Åìµþ','ÆÁÅç','ÆÊÌÚ',
+ 'Ä»¼è','ÉÙ»³','Ĺºê','ĹÌî','ÆàÎÉ','¿·³ã','ʼ¸Ë',
+ '¹­Åç','Ê¡°æ','Ê¡²¬','Ê¡Åç','Ë̳¤Æ»','»°½Å','µÜ¾ë',
+ 'µÜºê','»³·Á','»³¸ý','»³Íü','ϲλ³'
+]
+
+states_lbox.insert(0, *ins_data)
+
diff --git a/ext/tk/sample/demos-jp/style.rb b/ext/tk/sample/demos-jp/style.rb
new file mode 100644
index 0000000000..d58c6115ae
--- /dev/null
+++ b/ext/tk/sample/demos-jp/style.rb
@@ -0,0 +1,247 @@
+#
+# text (display styles) widget demo (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($style_demo) && $style_demo
+ $style_demo.destroy
+ $style_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$style_demo = TkToplevel.new {|w|
+ title("Text Demonstration - Display Styles")
+ iconname("style")
+ positionWindow(w)
+}
+
+# frame À¸À®
+TkFrame.new($style_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $style_demo
+ $style_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'style'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# text À¸À®
+TkText.new($style_demo){|t|
+ # À¸À®
+ setgrid 'true'
+ width 70
+ height 32
+ wrap 'word'
+ TkScrollbar.new($style_demo) {|s|
+ pack('side'=>'right', 'fill'=>'y')
+ command proc{|*args| t.yview(*args)}
+ t.yscrollcommand proc{|first,last| s.set first,last}
+ }
+ pack('expand'=>'yes', 'fill'=>'both')
+
+ # ¥Æ¥­¥¹¥È¥¿¥°ÀßÄê (¥Õ¥©¥ó¥È´ØÏ¢)
+ style_tag_bold = TkTextTag.new(t, 'font'=>'-*-Courier-Bold-O-Normal--*-120-*-*-*-*-*-*')
+ style_tag_big = TkTextTag.new(t, 'font'=>'-*-Courier-Bold-R-Normal--*-140-*-*-*-*-*-*', 'kanjifont'=>$msg_kanji_font)
+ style_tag_verybig = TkTextTag.new(t, 'font'=>'-*-Helvetica-Bold-R-Normal--*-240-*-*-*-*-*-*')
+# style_tag_small = TkTextTag.new(t, 'font'=>'-Adobe-Helvetica-Bold-R-Normal-*-100-*', 'kanjifont'=>$kanji_font)
+ style_tag_small = TkTextTag.new(t, 'font'=>'-Adobe-Helvetica-Bold-R-Normal-*-100-*')
+###
+# case($tk_version)
+# when /^4.*/
+# style_tag_big = TkTextTag.new(t, 'font'=>'-*-Courier-Bold-R-Normal--*-140-*-*-*-*-*-*', 'kanjifont'=>$msg_kanji_font)
+# style_tag_small = TkTextTag.new(t, 'font'=>'-Adobe-Helvetica-Bold-R-Normal-*-100-*', 'kanjifont'=>$kanji_font)
+# when /^8.*/
+# unless $style_demo_do_first
+# $style_demo_do_first = true
+# Tk.tk_call('font', 'create', '@bigascii',
+# '-copy', '-*-Courier-Bold-R-Normal--*-140-*-*-*-*-*-*')
+# Tk.tk_call('font', 'create', '@smallascii',
+# '-copy', '-Adobe-Helvetica-Bold-R-Normal-*-100-*')
+# Tk.tk_call('font', 'create', '@cBigFont',
+# '-compound', '@bigascii @msg_knj')
+# Tk.tk_call('font', 'create', '@cSmallFont',
+# '-compound', '@smallascii @kanji')
+# end
+# style_tag_big = TkTextTag.new(t, 'font'=>'@cBigFont')
+# style_tag_small = TkTextTag.new(t, 'font'=>'@cSmallFont')
+# end
+
+ # ¥Æ¥­¥¹¥È¥¿¥°ÀßÄê (¿§¡¤¥ì¥ê¡¼¥Õ´ØÏ¢)
+ if TkWinfo.depth($root).to_i > 1
+ style_tag_color1 = TkTextTag.new(t, 'background'=>'#a0b7ce')
+ style_tag_color2 = TkTextTag.new(t, 'foreground'=>'red')
+ style_tag_raised = TkTextTag.new(t, 'relief'=>'raised', 'borderwidth'=>1)
+ style_tag_sunken = TkTextTag.new(t, 'relief'=>'sunken', 'borderwidth'=>1)
+ else
+ style_tag_color1 = TkTextTag.new(t, 'background'=>'black',
+ 'foreground'=>'white')
+ style_tag_color2 = TkTextTag.new(t, 'background'=>'black',
+ 'foreground'=>'white')
+ style_tag_raised = TkTextTag.new(t, 'background'=>'white',
+ 'relief'=>'raised', 'borderwidth'=>1)
+ style_tag_sunken = TkTextTag.new(t, 'background'=>'white',
+ 'relief'=>'sunken', 'borderwidth'=>1)
+ end
+
+ # ¥Æ¥­¥¹¥È¥¿¥°ÀßÄê (¤½¤Î¾)
+ if $tk_version =~ /^4\.[01]/
+ style_tag_bgstipple = TkTextTag.new(t, 'background'=>'black',
+ 'borderwidth'=>0,
+ 'bgstipple'=>'gray25')
+ else
+ style_tag_bgstipple = TkTextTag.new(t, 'background'=>'black',
+ 'borderwidth'=>0,
+ 'bgstipple'=>'gray12')
+ end
+ style_tag_fgstipple = TkTextTag.new(t, 'fgstipple'=>'gray50')
+ style_tag_underline = TkTextTag.new(t, 'underline'=>'on')
+ style_tag_overstrike = TkTextTag.new(t, 'overstrike'=>'on')
+ style_tag_right = TkTextTag.new(t, 'justify'=>'right')
+ style_tag_center = TkTextTag.new(t, 'justify'=>'center')
+ style_tag_super = TkTextTag.new(t, 'offset'=>'4p', 'font'=>'-Adobe-Courier-Medium-R-Normal--*-100-*-*-*-*-*-*')
+ style_tag_sub = TkTextTag.new(t, 'offset'=>'-2p', 'font'=>'-Adobe-Courier-Medium-R-Normal--*-100-*-*-*-*-*-*')
+ style_tag_margins = TkTextTag.new(t, 'lmargin1'=>'12m', 'lmargin2'=>'6m',
+ 'rmargin'=>'10m')
+ style_tag_spacing = TkTextTag.new(t, 'spacing1'=>'10p', 'spacing2'=>'2p',
+ 'lmargin1'=>'12m', 'lmargin2'=>'6m',
+ 'rmargin'=>'10m')
+
+ # ¥Æ¥­¥¹¥ÈÁÞÆþ
+ insert('end', '¤³¤Î¤è¤¦¤Ë¥Æ¥­¥¹¥È widget ¤Ï¾ðÊó¤òÍÍ¡¹¤Ê¥¹¥¿¥¤¥ë¤Çɽ¼¨¤¹¤ë¤³¤È
+¤¬¤Ç¤­¤Þ¤¹¡£')
+ insert('end', '¥¿¥°', style_tag_big)
+ insert('end', '¤È¤¤¤¦¥á¥«¥Ë¥º¥à¤Ç¥³¥ó¥È¥í¡¼¥ë¤µ¤ì¤Þ¤¹¡£
+¥¿¥°¤È¤Ï¥Æ¥­¥¹¥È widget Æâ¤Î¤¢¤ëʸ»ú (¤ÎÈϰÏ)¤ËÂФ·¤ÆÅ¬ÍѤǤ­¤ë
+ñ¤Ê¤ë̾Á°¤Î¤³¤È¤Ç¤¹¡£¥¿¥°¤ÏÍÍ¡¹¤Êɽ¼¨¥¹¥¿¥¤¥ë¤ËÀßÄê¤Ç¤­¤Þ¤¹¡£
+ÀßÄꤹ¤ë¤È¡¢¤½¤Î¥¿¥°¤Î¤Ä¤¤¤¿Ê¸»ú¤Ï»ØÄꤷ¤¿¥¹¥¿¥¤¥ë¤Çɽ¼¨¤µ¤ì¤ë
+¤è¤¦¤Ë¤Ê¤ê¤Þ¤¹¡£»ÈÍѤǤ­¤ëɽ¼¨¥¹¥¿¥¤¥ë¤Ï¼¡¤ÎÄ̤ê¤Ç¤¹¡£
+')
+ insert('end', '
+1. ¥Õ¥©¥ó¥È', style_tag_big)
+ insert('end', ' ¤É¤ó¤Ê X ¤Î¥Õ¥©¥ó¥È¤Ç¤â»È¤¨¤Þ¤¹¡£')
+ insert('end', 'large', style_tag_verybig)
+ insert('end', '
+¤È¤«')
+# insert('end', '¾®¤µ¤¤', style_tag_small)
+ insert('end', 'small', style_tag_small)
+ insert('end', '¤È¤«¡£
+')
+ insert('end', '
+2. ¿§', style_tag_big)
+ insert('end', ' ')
+ insert('end', 'ÇØ·Ê¿§', style_tag_color1)
+ insert('end', '¤â')
+ insert('end', 'Á°·Ê¿§', style_tag_color2)
+ insert('end', '¤â')
+ insert('end', 'ξÊý', style_tag_color1, style_tag_color2)
+ insert('end', '¤È¤âÊѤ¨¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+')
+ insert('end', '
+3. ÌÖ¤«¤±', style_tag_big)
+ insert('end', ' ¤³¤Î¤è¤¦¤ËÉÁ²è¤ÎºÝ¤Ë')
+ insert('end', 'ÇØ·Ê¤â', style_tag_bgstipple)
+ insert('end', 'ʸ»ú¤â', style_tag_fgstipple)
+ insert('end', 'ñ¤Ê¤ëÅɤê¤Ä¤Ö¤·
+¤Ç¤Ê¤¯¡¢ÌÖ¤«¤±¤ò»È¤¦¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+')
+ insert('end', '
+4. ²¼Àþ', style_tag_big)
+ insert('end', ' ¤³¤Î¤è¤¦¤Ë')
+ insert('end', 'ʸ»ú¤Ë²¼Àþ¤ò°ú¤¯', style_tag_underline)
+ insert('end', '¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+')
+ insert('end', '
+5. ÂǤÁ¾Ã¤·Àþ', style_tag_big)
+ insert('end', ' ¤³¤Î¤è¤¦¤Ë')
+ insert('end', 'ʸ»ú¤Ë½Å¤Í¤ÆÀþ¤ò°ú¤¯', style_tag_overstrike)
+ insert('end', '¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+')
+ insert('end', '
+6. 3D ¸ú²Ì', style_tag_big)
+ insert('end', ' ÇØ·Ê¤ËÏȤò¤Ä¤±¤Æ¡¢Ê¸»ú¤ò')
+ insert('end', 'Èô¤Ó½Ð¤¹', style_tag_raised)
+ insert('end', '¤è¤¦¤Ë¤·¤¿¤ê')
+ insert('end', 'ÄÀ¤à', style_tag_sunken)
+ insert('end', '
+¤è¤¦¤Ë¤Ç¤­¤Þ¤¹¡£
+')
+ insert('end', '
+7. ¹Ô·¤¨', style_tag_big)
+ insert('end', ' ¤³¤Î¤è¤¦¤Ë¹Ô¤ò
+')
+ insert('end', 'º¸¤Ë·¤¨¤¿¤ê
+')
+ insert('end', '±¦¤Ë·¤¨¤¿¤ê
+', style_tag_right)
+ insert('end', '¿¿Ãæ¤Ë·¤¨¤¿¤ê¤Ç¤­¤Þ¤¹¡£
+', style_tag_center)
+ insert('end', '
+8. ¸ªÉÕ¤­Ê¸»ú¤Èź»ú', style_tag_big)
+ insert('end', ' 10')
+ insert('end', 'n', style_tag_super)
+ insert('end', ' ¤Î¤è¤¦¤Ë¸ªÉÕ¤­Ê¸»ú¤Î¸ú²Ì¤ä¡¢')
+ insert('end', '
+X')
+ insert('end', 'i', style_tag_sub)
+ insert('end', '¤Î¤è¤¦¤Ëź»ú¤Î¸ú²Ì¤ò½Ð¤¹¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+')
+ insert('end', '
+9. ¥Þ¡¼¥¸¥ó', style_tag_big)
+ insert('end', '¥Æ¥­¥¹¥È¤Îº¸Â¦¤Ë;ʬ¤Ê¶õÇò¤òÃÖ¤¯¤³¤È¤¬¤Ç¤­¤Þ¤¹:
+')
+ insert('end', '¤³¤ÎÃÊÍî¤Ï¥Þ¡¼¥¸¥ó¤Î»ÈÍÑÎã¤Ç¤¹¡£¥¹¥¯¥ê¡¼¥ó',
+ style_tag_margins)
+ insert('end', '¾å¤ÇÀÞ¤êÊÖ¤µ¤ì¤ÆÉ½¼¨¤µ¤ì¤Æ¤¤¤ë1¹Ô¤Î¥Æ¥­¥¹¥È¤Ç¤¹¡£',
+ style_tag_margins)
+ insert('end', 'º¸Â¦¤Ë¤Ï2¼ïÎà¤Î¥Þ¡¼¥¸¥ó¤ò»ý¤Á¤Þ¤¹¡£', style_tag_margins)
+ insert('end', '1¹ÔÌܤËÂФ¹¤ë¤â¤Î¤È¡¢', style_tag_margins)
+ insert('end', '2¹ÔÌܰʹߤÎϢ³¤·¤¿¥Þ¡¼¥¸¥ó', style_tag_margins)
+ insert('end', '¤Ç¤¹¡£¤Þ¤¿±¦Â¦¤Ë¤â¥Þ¡¼¥¸¥ó¤¬¤¢¤ê¤Þ¤¹¡£', style_tag_margins)
+ insert('end', '¹Ô¤ÎÀÞ¤êÊÖ¤·°ÌÃÖ¤ò·è¤á¤ë¤¿¤á¤Ë»ÈÍѤ¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+', style_tag_margins)
+ insert('end', '
+10. ¥¹¥Ú¡¼¥·¥ó¥°', style_tag_big)
+ insert('end', '3¤Ä¤Î¥Ñ¥é¥á¡¼¥¿¤Ç¹Ô¤Î¥¹¥Ú¡¼¥·¥ó¥°¤ò')
+ insert('end', 'À©¸æ¤¹
+¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£Spacing1¤Ç¡¢¹Ô¤Î')
+ insert('end', '¾å¤Ë¤É¤Î¤¯¤é¤¤¤Î¶õ´Ö¤òÃÖ¤¯¤«¡¢
+spacing3')
+ insert('end', '¤Ç¹Ô¤Î²¼¤Ë¤É¤Î¤¯¤é¤¤¤Î¶õ´Ö¤òÃÖ¤¯¤«¡¢')
+ insert('end', '¹Ô¤¬ÀÞ¤êÊÖ¤µ¤ì¤Æ¤¤¤ë¤Ê¤é
+¤Ð¡¢spacing2¤Ç¡¢')
+ insert('end', '¥Æ¥­¥¹¥È¹Ô¤òÀ¸À®¤·¤Æ¤¤¤ë¹Ô¤Î´Ö¤Ë¤É¤Î¤¯¤é¤¤')
+ insert('end', '¤Î¶õ´Ö¤òÃÖ
+¤¯¤«¤ò¼¨¤·¤Þ¤¹¡£
+')
+ insert('end', '¤³¤ì¤é¤Î¥¤¥ó¥Ç¥ó¥È¤µ¤ì¤¿ÃÊÍî¤Ï¤É¤Î¤è¤¦¤Ë',
+ style_tag_spacing)
+ insert('end', '¥¹¥Ú¡¼¥·¥ó¥°¤¬¤¬¹Ô¤ï¤ì¤ë¤Î¤«¤ò¼¨¤·¤Þ¤¹¡£',
+ style_tag_spacing)
+ insert('end', '³ÆÃÊÍî¤Ï¼ÂºÝ¤Ï¥Æ¥­¥¹¥Èwidget', style_tag_spacing)
+ insert('end', '¤Î1¹Ô¤Ç¡¢widget¤Ë¤è¤Ã¤ÆÀÞ¤ê¾ö¤Þ¤ì¤Æ¤¤¤Þ¤¹¡£
+', style_tag_spacing)
+ insert('end', 'Spacing1¤Ï¤³¤Î¥Æ¥­¥¹¥È¤Ç¤Ï10point¤Ë', style_tag_spacing)
+ insert('end', 'ÀßÄꤵ¤ì¤Æ¤¤¤Þ¤¹¡£', style_tag_spacing)
+ insert('end', '¤³¤ì¤Ë¤è¤ê¡¢ÃÊÍî¤Î´Ö¤ËÂ礭¤Ê´Ö³Ö¤¬', style_tag_spacing)
+ insert('end', '¸ºß¤·¤Æ¤¤¤Þ¤¹¡£', style_tag_spacing)
+ insert('end', 'Spacing2¤Ï2point¤ËÀßÄꤵ¤ì¤Æ¤¤¤Þ¤¹¡£', style_tag_spacing)
+ insert('end', '¤³¤ì¤ÇÃÊÍî¤ÎÃæ¤Ë¤Û¤ó¤Î¾¯¤·´Ö³Ö¤¬Â¸ºß¤·¤Æ¤¤¤Þ¤¹¡£',
+ style_tag_spacing)
+ insert('end', 'Spacing3¤Ï¤³¤ÎÎã¤Ç¤Ï»ÈÍѤµ¤ì¤Æ¤¤¤Þ¤»¤ó¡£
+', style_tag_spacing)
+ insert('end', '´Ö³Ö¤¬¤É¤³¤Ë¤¢¤ë¤«¤ò¸«¤¿¤±¤ì¤Ð¡¢¤³¤ì¤é¤ÎÃÊÍî¤Î',
+ style_tag_spacing)
+ insert('end', '¤Ê¤«¤Ç¥Æ¥­¥¹¥È¤òÁªÂò¤·¤Æ¤¯¤À¤µ¤¤¡£ÁªÂò¤Î', style_tag_spacing)
+ insert('end', 'ȿž¤·¤¿Éôʬ¤Ë¤Ï;ʬ¤Ë¤È¤é¤ì¤¿´Ö³Ö¤¬', style_tag_spacing)
+ insert('end', '´Þ¤Þ¤ì¤Æ¤¤¤Þ¤¹¡£
+', style_tag_spacing)
+
+}
+
diff --git a/ext/tk/sample/demos-jp/tcolor b/ext/tk/sample/demos-jp/tcolor
new file mode 100644
index 0000000000..5464aebae2
--- /dev/null
+++ b/ext/tk/sample/demos-jp/tcolor
@@ -0,0 +1,513 @@
+#!/usr/local/bin/ruby
+#
+# tcolor --
+# ¤³¤Î¥¹¥¯¥ê¥×¥È¤ÏRGB,HSB,CYM·Á¼°¤ò¥µ¥Ý¡¼¥È¤¹¤ë
+# ´Ê°×¥«¥é¡¼¥¨¥Ç¥£¥¿¤Ç¤¹¡£
+#
+# Copyright (C) 1998 Takaaki Tateishi(ttate@jaist.ac.jp)
+# last update: Thu Jun 18 06:32:35 JST 1998
+#
+
+# ¤Þ¤º¤Ïtk.rb¤òÆÉ¤ß¹þ¤à¡£
+
+require "tk"
+
+
+# Tk¤Ë¤è¤Ã¤ÆÊѹ¹¤µ¤ì¤ëÊÑ¿ô¤ÏTkVariable¤Î¥¤¥ó¥¹¥¿¥ó¥¹¤ò»È¤¦¡£
+
+$colorSpace = TkVariable.new(:rgb)
+$red = 65535
+$green = 0
+$blue = 0
+$color = "#ffff00000000"
+$updating = TkVariable.new(0)
+$autoUpdate = TkVariable.new(1)
+$name = TkVariable.new("")
+# $command = TkVariable.new("print(%%,\"\n\")")
+$command = TkVariable.new("")
+$label1 = TkVariable.new("label1")
+$label2 = TkVariable.new("label2")
+$label3 = TkVariable.new("label3")
+
+
+# ³Æ¥¤¥Ù¥ó¥ÈÍѤΥ᥽¥Ã¥É
+
+def rgbToHsv(red,green,blue)
+
+ if ( red > green )
+ max = red
+ min = green
+ else
+ max = green
+ min = red
+ end
+
+ if ( blue > max )
+ max = blue
+ else
+ if ( blue < min )
+ min = blue
+ end
+ end
+
+ range = max - min
+
+ if ( max == 0 )
+ sat = 0.0
+ else
+ sat = (max-min)/max
+ end
+
+ if ( sat == 0 )
+ hue = 0.0
+ else
+ rc = (max-red)/range
+ gc = (max-green)/range
+ bc = (max-blue)/range
+ if ( red == max )
+ hue = 0.166667 * (bc - gc)
+ else
+ if ( green == max )
+ hue = 0.166667 * (2.0 + rc - bc)
+ else
+ hue = 0.166667 * (4.0 + gc - rc)
+ end
+ end
+ if ( hue < 0.0 )
+ hue = hue + 1.0
+ end
+ end
+
+ [hue,sat,max/65535]
+end
+
+
+def hsbToRgb(hue,sat,value)
+ v = 65535.0 * value
+ if( sat == 0 )
+ ans = [v,v,v]
+ else
+ hue = hue*6.0
+ if ( hue >= 6 )
+ hue = 0.0
+ end
+ i = hue.to_i
+ f = hue - i
+ p = 65535.0 * value * (1.0 - sat)
+ q = 65535.0 * value * (1.0 - (sat * f))
+ t = 65535.0 * value * (1.0 - (sat * (1.0 - f)))
+ case i
+ when 0
+ ans = [v,t,p]
+ when 1
+ ans = [q,v,p]
+ when 2
+ ans = [p,v,t]
+ when 3
+ ans = [p,q,v]
+ when 4
+ ans = [t,p,v]
+ when 5
+ ans = [v,p,q]
+ else
+ raise(eException,"i value #{i} is out of range")
+ end
+ end
+ return ans
+end
+
+
+def doUpdate
+ newCmd = $command.to_s.gsub("%%","\"#{$color}\"")
+ eval(newCmd)
+end
+
+
+def tc_scaleChanged
+ if( $updating.to_i == 1 )
+ return
+ end
+
+ scale1 = $root.middle.middle.scale1
+ scale2 = $root.middle.middle.scale2
+ scale3 = $root.middle.middle.scale3
+
+ case $colorSpace.to_i
+ when :rgb
+ $red = (scale1.get * 65.535).to_i
+ $green = (scale2.get * 65.535).to_i
+ $blue = (scale3.get * 65.535).to_i
+ when :cmy
+ $red = (65535 - scale1.get * 65.535).to_i
+ $green = (65535 - scale2.get * 65.535).to_i
+ $blue = (65535 - scale3.get * 65.535).to_i
+ when :hsb
+ list = hsbToRgb(scale1.get / 1000.0,
+ scale2.get / 1000.0,
+ scale3.get / 1000.0)
+ $red = list[0]
+ $green = list[1]
+ $blue = list[2]
+ else
+ raise(Exception,"unknown colorSpace")
+ end
+ $color = format("#%04x%04x%04x",$red.to_i,$green.to_i,$blue.to_i)
+ $root.middle.right.set_color($color)
+ if( $autoUpdate.to_i == 1 )
+ doUpdate
+ end
+ Tk.update(TRUE)
+end
+
+
+def tc_setScales
+ $updating.value = 1
+
+ scale1 = $root.middle.middle.scale1
+ scale2 = $root.middle.middle.scale2
+ scale3 = $root.middle.middle.scale3
+
+ case $colorSpace.to_i
+ when :rgb
+ scale1.set($red / 65.535)
+ scale2.set($green / 65.535)
+ scale3.set($blue / 65.535)
+ when :cmy
+ scale1.set((65535 - $red) / 65.535)
+ scale2.set((65535 - $green) / 65.535)
+ scale3.set((65535 - $blue) / 65.535)
+ when :hsb
+ list = rgbToHsv($red,$green,$blue)
+ scale1.set( list[0] * 1000.0 )
+ scale2.set( list[1] * 1000.0 )
+ scale3.set( list[2] * 1000.0 )
+ else
+ raise(Exception,"unknown colorSpace")
+ end
+
+ $updating.value = 0
+end
+
+
+def tc_loadNamedColor(name)
+ if name[0,1] != "#"
+ list = TkWinfo.rgb($root.middle.right.swatch,name)
+ $red = list[0]
+ $green = list[1]
+ $blue = list[2]
+ else
+ case name.length
+ when 4
+ format = /#(.{1})(.{1})(.{1})/
+ shift = 12
+ when 7
+ format = /#(.{2})(.{2})(.{2})/
+ shift = 8
+ when 10
+ format = /#(.{3})(.{3})(.{3})/
+ shift = 4
+ when 13
+ format = /#(.{4})(.{4})(.{4})/
+ shift = 0
+ else
+ raise(eException,"syntax error in color name \"#{name}\"")
+ end
+ name.scan(format){|strlist|
+ if strlist.length != 3
+ raise(eException,"syntax error in color name \"#{name}\"")
+ end
+ $red = strlist[0].to_i
+ $green = strlist[1].to_i
+ $blue = strlist[2].to_i
+ }
+ $red = $red << shift
+ $green = $green << shift
+ $blue = $blue << shift
+ end
+
+ tc_setScales
+ $color = format("#%04x%04x%04x",$red,$green,$blue)
+ $root.middle.right.set_color($color)
+ if $autoUpdate.to_i == 1
+ doUpdate
+ end
+end
+
+
+def changeColorSpace(space)
+ case space
+ when :rgb
+ $label1.value = "Red"
+ $label2.value = "Green"
+ $label3.value = "Blue"
+ when :cmy
+ $label1.value = "Cyan"
+ $label2.value = "Magenta"
+ $label3.value = "Yellow"
+ when :hsb
+ $label1.value = "Hue"
+ $label2.value = "Saturation"
+ $label3.value = "Brightness"
+ end
+ tc_setScales
+end
+
+
+
+
+
+# tcolorÍѤΥá¥Ë¥å¡¼
+
+class TkColorMenuFrame<TkFrame
+ def initialize(parent)
+ super(parent,
+ "relief"=>"raised",
+ "borderwidth"=>"2")
+
+ # File¥á¥Ë¥å¡¼¥Ü¥¿¥ó¤ÎÀ¸À®
+ @file = TkMenubutton.new(self){|button|
+
+ # File¥á¥Ë¥å¡¼¤ÎºîÀ®
+ @file_menu = TkMenu.new(button){
+ add "radio",
+ "label" => "RGB color space",
+ "variable" => $colorSpace,
+ "value" => :rgb,
+ "underline" => "0",
+ "command" => proc{changeColorSpace(:rgb)}
+ add "radio",
+ "label" => "CMY color space",
+ "variable" => $colorSpace,
+ "value" => :cmy,
+ "underline" => "0",
+ "command" => proc{changeColorSpace(:cmy)}
+ add "radio",
+ "label" => "HSB color space",
+ "variable" => $colorSpace,
+ "value" => :hsb,
+ "underline" => "0",
+ "command" => proc{changeColorSpace(:hsb)}
+ add "separator"
+ add "radio",
+ "label" => "Qutomatic updates",
+ "variable" => $autoUpdate,
+ "value" => "1",
+ "underline" => "0"
+ add "radio",
+ "label" => "Manual updates",
+ "variable" => $autoUpdate,
+ "value" => "0",
+ "underline" => "0"
+ add "separator"
+ add "command",
+ "label" => "Exit program",
+ "underline" => "0",
+ "command" => proc{exit}
+ }
+
+ # File¥á¥Ë¥å¡¼¤ÈFile¥Ü¥¿¥ó¤ò´ØÏ¢ÉÕ¤±¤ë
+ menu @file_menu
+
+ text "File"
+ underline "0"
+ }.pack("side"=>"left")
+
+ self
+ end
+end
+
+
+# ²¼Éô¤Î¥Õ¥ì¡¼¥à¤Î¤¿¤á¤Î¥¯¥é¥¹
+class TkColorBotFrame<TkFrame
+ def initialize(parent)
+ super(parent,
+ "relief"=> "raised",
+ "borderwidth"=> 2)
+
+ @commandLabel = TkLabel.new(self,
+ "text"=> "Command:")
+ @command = TkEntry.new(self,
+ "relief"=> "sunken",
+ "borderwidth"=> "2",
+ "textvariable"=> $command,
+ "font"=> "-Adobe-Courier-Medium-R-Normal--*-120-*-*-*-*-*-*")
+ @update = TkButton.new(self,
+ "text"=> "Update",
+ "command"=> proc{doUpdate})
+ @commandLabel.pack("side"=>"left")
+ @update.pack("side"=>"right","pady"=>".1c","padx"=>".25c")
+ @command.pack("expand"=>"yes","fill"=>"x","ipadx"=>".25c")
+
+ self
+ end
+end
+
+
+# ÃæÃʺ¸¤Î¥Õ¥ì¡¼¥à
+class TkColorMiddleLeftFrame<TkFrame
+ def initialize(parent)
+ super(parent)
+
+ for i in ["/usr/local/lib/X11rgb.txt","/usr/lib/X11/rgb.txt",
+ "/X11/R5/lib/X11/rgb.txt","/X11/R4/lib/rgb/rgb.txt",
+ "/usr/openwin/lib/X11/rgb.txt"]
+ if !File.readable?(i)
+ next
+ end
+ f = File.open(i)
+ @scroll = TkScrollbar.new(self,
+ "orient"=>"vertical",
+ "relief"=>"sunken",
+ "borderwidth"=>"2")
+ @scroll.pack("side"=>"right","fill"=>"y")
+ @names = TkListbox.new(self,
+ "width"=>"20",
+ "height"=>"12",
+ "yscrollcommand"=> proc{|first,last| @scroll.set first,last},
+ "relief"=>"sunken",
+ "borderwidth"=>"2",
+ "exportselection"=>"false")
+ @scroll.command(proc{|*args| @names.yview *args})
+ @names.bind("Double-1",proc{
+ tc_loadNamedColor(@names.get(@names.curselection))})
+ @names.pack("side"=>"left")
+ while (line = f.gets)
+ line.chop!
+ linelist = line.split(/[ \t]+/)
+ if linelist.length == 4
+ @names.insert("end",linelist[3])
+ end
+ end
+ f.close
+ break
+ end
+
+ self
+ end
+end
+
+
+# ÃæÃÊÃæ±û¤Î¥Õ¥ì¡¼¥à
+class TkColorMiddleMiddleFrame<TkFrame
+ # @scale1,@scale2,@scale3¤ò³°Éô¤«¤é»²¾È¤Î¤ßµö²Ä¤¹¤ë¡£(Êѹ¹ÉÔ²Ä)
+ attr_reader :scale1, :scale2, :scale3
+
+ def initialize(parent)
+ super(parent)
+
+ @f1 = TkFrame.new(self)
+ @f2 = TkFrame.new(self)
+ @f3 = TkFrame.new(self)
+ @f4 = TkFrame.new(self)
+
+ for f in [@f1,@f2,@f3]
+ f.pack("side"=>"top","expand"=>"yes")
+ end
+ @f4.pack("side"=>"top","expand"=>"yes","fill"=>"x")
+
+ @label1 = TkLabel.new(self,"textvariable"=>$label1)
+ @scale1 = TkScale.new(self,"from"=>"0","to"=>"1000","length"=>"6c",
+ "orient"=>"horizontal",
+ "command"=>proc{tc_scaleChanged})
+ @scale1.pack("side"=>"top","anchor"=>"w")
+ @label1.pack("side"=>"top","anchor"=>"w")
+
+ @label2 = TkLabel.new(self,"textvariable"=>$label2)
+ @scale2 = TkScale.new(self,"from"=>"0","to"=>"1000","length"=>"6c",
+ "orient"=>"horizontal",
+ "command"=>proc{tc_scaleChanged})
+ @scale2.pack("side"=>"top","anchor"=>"w")
+ @label2.pack("side"=>"top","anchor"=>"w")
+
+ @label3 = TkLabel.new(self,"textvariable"=>$label3)
+ @scale3 = TkScale.new(self,"from"=>"0","to"=>"1000","length"=>"6c",
+ "orient"=>"horizontal",
+ "command"=>proc{tc_scaleChanged})
+ @scale3.pack("side"=>"top","anchor"=>"w")
+ @label3.pack("side"=>"top","anchor"=>"w")
+
+ @nameLabel = TkLabel.new(self,"text"=>"Name:")
+ @name = TkEntry.new(self,"relief"=>"sunken","borderwidth"=>"2",
+ "textvariable"=>$name,"width"=>"10",
+ "font"=>"-Adobe-Courier-Medium-R-Normal--*-120-*-*-*-*-*-*")
+ @nameLabel.pack("side"=>"left")
+ @name.pack("side"=>"right", "expand"=>"1", "fill"=>"x")
+ @name.bind("Return",proc{tc_loadNamedColor $name.to_s})
+
+ self
+ end
+end
+
+
+class TkColorMiddleRightFrame<TkFrame
+ attr_reader :swatch
+
+ def initialize(parent)
+ super(parent)
+ @swatch = TkFrame.new(self, "width"=>"2c", "height"=>"5c",
+ "background"=>$color)
+ @value = TkLabel.new(self,
+ "text"=>$color,
+ "width"=>"13",
+ "font"=>"-Adobe-Courier-Medium-R-Normal--*-120-*-*-*-*-*-*")
+ @swatch.pack("side"=>"top","expand"=>"yes","fill"=>"both")
+ @value.pack("side"=>"bottom","pady"=>".25c")
+
+ self
+ end
+
+ def set_color(color)
+ @swatch["background"] = color
+ @value["text"] = color
+ end
+end
+
+
+
+# ÃæÃʤΥե졼¥à
+class TkColorMiddleFrame<TkFrame
+ attr_reader :left, :middle, :right
+
+ def initialize(parent)
+ super(parent,
+ "relief"=> "raised",
+ "borderwidth"=> "2")
+
+ @left = TkColorMiddleLeftFrame.new(self)
+ @left.pack("side"=>"left","padx"=>".25c","pady"=>".25c")
+
+ @middle = TkColorMiddleMiddleFrame.new(self)
+ @middle.pack("side"=>"left","expand"=>"yes","fill"=>"y")
+
+ @right = TkColorMiddleRightFrame.new(self)
+ @right.pack("side"=>"left","padx"=>".25c","pady"=>".25c","anchor"=>"s")
+
+ self
+ end
+end
+
+
+class TkColor<TkRoot
+ attr_reader :menu, :bottom, :middle
+
+ def initialize
+ super
+ @menu = TkColorMenuFrame.new(self)
+ @menu.pack("side"=>"top", "fill"=>"x")
+
+ @bottom = TkColorBotFrame.new(self)
+ @bottom.pack("side"=>"bottom","fill"=>"x")
+
+ @middle = TkColorMiddleFrame.new(self)
+ @middle.pack("side"=>"top","fill"=>"both")
+
+ self
+ end
+end
+
+
+$root = TkColor.new
+
+# ¥¤¥Ù¥ó¥È¤òÂԤİ٤˥롼¥×¤ËÆþ¤ë¡£
+changeColorSpace :rgb
+Tk.mainloop
diff --git a/ext/tk/sample/demos-jp/text.rb b/ext/tk/sample/demos-jp/text.rb
new file mode 100644
index 0000000000..a8232088a5
--- /dev/null
+++ b/ext/tk/sample/demos-jp/text.rb
@@ -0,0 +1,94 @@
+#
+# text (basic facilities) widget demo (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($text_demo) && $text_demo
+ $text_demo.destroy
+ $text_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$text_demo = TkToplevel.new {|w|
+ title("Text Demonstration - Basic Facilities")
+ iconname("text")
+ positionWindow(w)
+}
+
+# frame À¸À®
+TkFrame.new($text_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $text_demo
+ $text_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'text'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# text À¸À®
+TkText.new($text_demo){|t|
+ # À¸À®
+ relief 'sunken'
+ bd 2
+ setgrid 1
+ height 30
+ TkScrollbar.new($text_demo) {|s|
+ pack('side'=>'right', 'fill'=>'y')
+ command proc{|*args| t.yview(*args)}
+ t.yscrollcommand proc{|first,last| s.set first,last}
+ }
+ pack('expand'=>'yes', 'fill'=>'both')
+
+ # ¥Æ¥­¥¹¥ÈÁÞÆþ
+ insert('0.0', %q|
+¤³¤Î¥¦¥£¥ó¥É¥¦¤Ï¥Æ¥­¥¹¥È widget ¤Ç¤¹¡£1¹Ô¤Þ¤¿¤Ï¤½¤ì°Ê¾å¤Î¥Æ¥­¥¹¥È¤òɽ
+¼¨¡¦ÊÔ½¸¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£°Ê²¼¤Ï¥Æ¥­¥¹¥È widget ¤Ç¤Ç¤­¤ëÁàºî¤Ë¤Ä¤¤¤Æ
+¤Þ¤È¤á¤¿¤â¤Î¤Ç¤¹¡£
+
+1. ¥¹¥¯¥í¡¼¥ë¡£¥¹¥¯¥í¡¼¥ë¥Ð¡¼¤Ç¥Æ¥­¥¹¥È¤Îɽ¼¨Éôʬ¤òư¤«¤¹¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+
+2. ¥¹¥­¥ã¥Ë¥ó¥°¡£¥Æ¥­¥¹¥È¤Î¥¦¥£¥ó¥É¥¦¤Ç¥Þ¥¦¥¹¥Ü¥¿¥ó2 (Ãæ¥Ü¥¿¥ó¤ò) ¤ò²¡
+¤·¤Æ¾å²¼¤Ë¥É¥é¥Ã¥°¤·¤Æ¤¯¤À¤µ¤¤¡£¤½¤¦¤¹¤ë¤È¥Æ¥­¥¹¥È¤¬¹â®¤Ç¥É¥é¥Ã¥°¤µ¤ì¡¢
+ÆâÍÆ¤ò¤¶¤Ã¤Èį¤á¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+
+3. ¥Æ¥­¥¹¥È¤ÎÁÞÆþ¡£¥Þ¥¦¥¹¥Ü¥¿¥ó1 (º¸¥Ü¥¿¥ó) ¤ò²¡¤·¡¢ÁÞÆþ¥«¡¼¥½¥ë¤ò¥»¥Ã
+¥È¤·¤Æ¤«¤é¥Æ¥­¥¹¥È¤òÆþÎϤ·¤Æ¤¯¤À¤µ¤¤¡£ÆþÎϤ·¤¿¤â¤Î¤¬ widget ¤ËÆþ¤ê¤Þ¤¹¡£
+
+4. ÁªÂò¡£¤¢¤ëÈϰϤÎʸ»ú¤òÁªÂò¤¹¤ë¤Ë¤Ï¥Þ¥¦¥¹¥Ü¥¿¥ó1 ¤ò²¡¤·¡¢¥É¥é¥Ã¥°¤·
+¤Æ¤¯¤À¤µ¤¤¡£°ìÅ٥ܥ¿¥ó¤òÎ¥¤·¤¿¤é¡¢¥·¥Õ¥È¥­¡¼¤ò²¡¤·¤Ê¤¬¤é¥Ü¥¿¥ó1 ¤ò²¡¤¹
+¤³¤È¤ÇÁªÂòÈϰϤÎÄ´À°¤¬¤Ç¤­¤Þ¤¹¡£¤³¤ì¤ÏÁªÂòÈϰϤκǸå¤ò¥Þ¥¦¥¹¥«¡¼¥½¥ë¤Ë
+ºÇ¤â¶á¤¤°ÌÃ֤˥ꥻ¥Ã¥È¤·¡¢¥Ü¥¿¥ó¤òÎ¥¤¹Á°¤Ë¥Þ¥¦¥¹¤ò¥É¥é¥Ã¥°¤¹¤ë¤³¤È¤Ç¤µ
+¤é¤ËÁªÂòÈϰϤòÄ´À°¤Ç¤­¤Þ¤¹¡£¥À¥Ö¥ë¥¯¥ê¥Ã¥¯¤Ç¥ï¡¼¥É¤ò¡¢¤Þ¤¿¥È¥ê¥×¥ë¥¯¥ê¥Ã
+¥¯¤Ç¹ÔÁ´ÂΤòÁªÂò¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£
+
+5. ¾Ãµî¤ÈÃÖ´¹¡£¥Æ¥­¥¹¥È¤ò¾Ãµî¤¹¤ë¤Ë¤Ï¡¢¾Ãµî¤·¤¿¤¤Ê¸»ú¤òÁªÂò¤·¤Æ¥Ð¥Ã¥¯
+¥¹¥Ú¡¼¥¹¤«¥Ç¥ê¡¼¥È¥­¡¼¤òÆþÎϤ·¤Æ¤¯¤À¤µ¤¤¡£¤¢¤ë¤¤¤Ï¡¢¿·¤·¤¤¥Æ¥­¥¹¥È¤ò
+ÆþÎϤ¹¤ë¤ÈÁªÂò¤µ¤ì¤¿¥Æ¥­¥¹¥È¤ÈÃÖ´¹¤µ¤ì¤Þ¤¹¡£
+
+6. ÁªÂòÉôʬ¤Î¥³¥Ô¡¼¡£ÁªÂòÉôʬ¤ò¤³¤Î¥¦¥£¥ó¥É¥¦¤ÎÃæ¤Î¤É¤³¤«¤Ë¥³¥Ô¡¼¤¹¤ë
+¤Ë¤Ï¡¢¤Þ¤º¥³¥Ô¡¼¤·¤¿¤¤½ê¤òÁªÂò(¤³¤³¤Ç¡¢¤¢¤ë¤¤¤ÏÊ̤Υ¢¥×¥ê¥±¡¼¥·¥ç¥ó¤Ç)
+¤·¡¢¥Ü¥¿¥ó 2 ¤ò¥¯¥ê¥Ã¥¯¤·¤Æ¡¢ÁÞÆþ¥«¡¼¥½¥ë¤Î°ÌÃ֤˥³¥Ô¡¼¤·¤Æ¤¯¤À¤µ¤¤¡£
+
+7. ÊÔ½¸¡£¥Æ¥­¥¹¥È widget ¤Ï Emacs ¤Î¥­¡¼¥Ð¥¤¥ó¥É¤Ë²Ã¤¨¤ÆÉ¸½àŪ¤Ê¤Î Motif
+¤ÎÊÔ½¸µ¡Ç½¤ò¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤Þ¤¹¡£¥Ð¥Ã¥¯¥¹¥Ú¡¼¥¹¤È¥³¥ó¥È¥í¡¼¥ë-H ¤ÏÁÞÆþ
+¥«¡¼¥½¥ë¤Îº¸Â¦¤Îʸ»ú¤òºï½ü¤·¤Þ¤¹¡£¥Ç¥ê¡¼¥È¥­¡¼¤È¥³¥ó¥È¥í¡¼¥ë-D ¤ÏÁÞÆþ
+¥«¡¼¥½¥ë¤Î±¦Â¦¤Îʸ»ú¤òºï½ü¤·¤Þ¤¹¡£Meta-¥Ð¥Ã¥¯¥¹¥Ú¡¼¥¹¤ÏÁÞÆþ¥«¡¼¥½¥ë¤Î
+±¦Â¦¤Îñ¸ì¤òºï½ü¤·¡¢Meta-D ¤ÏÁÞÆþ¥«¡¼¥½¥ë¤Îº¸Â¦¤Îñ¸ì¤òºï½ü¤·¤Þ¤¹¡£
+¥³¥ó¥È¥í¡¼¥ë-K ¤ÏÁÞÆþ¥«¡¼¥½¥ë¤«¤é¹ÔËö¤Þ¤Ç¤òºï½ü¤·¡¢¤½¤Î°ÌÃ֤˲þ¹Ô
+¤·¤«¤Ê¤«¤Ã¤¿¾ì¹ç¤Ï¡¢²þ¹Ô¤òºï½ü¤·¤Þ¤¹¡£
+
+8. ¥¦¥£¥ó¥É¥¦¤Î¥ê¥µ¥¤¥º¡£¤³¤Î widget ¤Ï "setGrid" ¥ª¥×¥·¥ç¥ó¤ò¥ª¥ó¤Ë¤·
+¤Æ¤¢¤ê¤Þ¤¹¤Î¤Ç¡¢¥¦¥£¥ó¥É¥¦¤ò¥ê¥µ¥¤¥º¤¹¤ë»þ¤Ë¤Ï¹â¤µ¤ÈÉý¤Ï¾ï¤Ëʸ»ú¹â¤Èʸ
+»úÉý¤ÎÀ°¿ôÇܤˤʤê¤Þ¤¹¡£¤Þ¤¿¡¢¥¦¥£¥ó¥É¥¦¤ò¶¹¤¯¤·¤¿¾ì¹ç¤Ë¤ÏŤ¤¹Ô¤¬¼«Æ°
+Ū¤ËÀÞ¤êÊÖ¤µ¤ì¡¢¾ï¤ËÁ´¤Æ¤ÎÆâÍÆ¤¬¸«¤¨¤ë¤è¤¦¤Ë¤Ê¤Ã¤Æ¤¤¤Þ¤¹¡£|)
+
+ set_insert('0.0')
+}
+
diff --git a/ext/tk/sample/demos-jp/timer b/ext/tk/sample/demos-jp/timer
new file mode 100644
index 0000000000..232f4ae5ca
--- /dev/null
+++ b/ext/tk/sample/demos-jp/timer
@@ -0,0 +1,120 @@
+#!/usr/local/bin/ruby
+#
+# timer --
+# This script generates a counter with start,stop and reset buttons.
+#
+# Copyright (C) 1998 Takaaki Tateishi (ttate@jaist.ac.jp)
+# last update: Sat Jun 27 12:24:14 JST 1998
+#
+
+require "tk"
+require "thread"
+require "tkafter"
+
+$time = "0.00"
+$m = Mutex.new
+$loop = false
+
+def timer_stop
+ $loop = false
+ $m.lock
+end
+
+def timer_start
+ $loop = true
+ $m.unlock
+end
+
+def timer_reset
+ $time = "0.00"
+ $root.countframe.counter['text'] = $time
+end
+
+def timer_loop
+ if $loop
+ $time = $time.succ
+ $root.countframe.counter['text'] = $time
+ end
+ Tk.after(10,proc{timer_loop})
+end
+
+
+#
+# thread version
+#
+def timer_loop2
+ while true
+ $m.lock
+ $time = $time.succ
+ $root.countframe.counter['text'] = $time
+ sleep(0.01)
+ $m.unlock
+ end
+end
+
+#
+# TkAfter
+#
+def timer_loop3
+ if $loop
+ $time = $time.succ
+ $root.countframe.counter['text'] = $time
+ end
+end
+
+
+class CountFrame < TkFrame
+ attr_reader :counter
+
+ def initialize(parent=nil,keys=nil)
+ super(parent,keys)
+ @counter = TkLabel.new(self,
+ 'text'=>$time,
+ 'relief'=>'raised')
+ @counter.pack('fill'=>'both')
+ self
+ end
+end
+
+
+class ButtonFrame < TkFrame
+ def initialize(parent=nil,keys=nil)
+ super(parent,keys)
+ @stop = TkButton.new(self,
+ 'text'=>'Stop',
+ 'command'=>proc{timer_stop})
+ @start = TkButton.new(self,
+ 'text'=>'Start',
+ 'command'=>proc{timer_start})
+ @reset = TkButton.new(self,
+ 'text'=>'Reset',
+ 'command'=>proc{timer_reset})
+ for b in [@stop,@start,@reset]
+ b.pack('side'=>'left', 'fill'=>'both', 'expand'=>'yes')
+ end
+ end
+end
+
+
+class Timer < TkRoot
+ attr_reader :countframe
+
+ def initialize
+ super
+ @countframe = CountFrame.new(self)
+ @buttonframe = ButtonFrame.new(self)
+ for f in [@buttonframe,@countframe]
+ f.pack('side'=>'top', 'fill'=>'both')
+ end
+ self
+ end
+end
+
+
+$root = Timer.new
+
+#$thread = Thread.start{timer_loop2}
+#timer_loop
+TkAfter.new(10,-1,proc{timer_loop3}).start
+
+Tk.mainloop
diff --git a/ext/tk/sample/demos-jp/twind.rb b/ext/tk/sample/demos-jp/twind.rb
new file mode 100644
index 0000000000..d35acd24d0
--- /dev/null
+++ b/ext/tk/sample/demos-jp/twind.rb
@@ -0,0 +1,284 @@
+#
+# text (embedded windows) widget demo (called by 'widget')
+#
+
+# toplevel widget ¤¬Â¸ºß¤¹¤ì¤Ðºï½ü¤¹¤ë
+if defined?($twind_demo) && $twind_demo
+ $twind_demo.destroy
+ $twind_demo = nil
+end
+
+# demo ÍѤΠtoplevel widget ¤òÀ¸À®
+$twind_demo = TkToplevel.new {|w|
+ title("Text Demonstration - Embedded Windows")
+ iconname("Embedded Windows")
+ positionWindow(w)
+}
+
+# frame À¸À®
+$twind_buttons = TkFrame.new($twind_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc{
+ tmppath = $twind_demo
+ $twind_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc{showCode 'twind'}
+ }.pack('side'=>'left', 'expand'=>'yes')
+}
+$twind_buttons.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+# frame À¸À®
+$twind_text = nil
+TkFrame.new($twind_demo, 'highlightthickness'=>2, 'borderwidth'=>2,
+ 'relief'=>'sunken') {|f|
+ $twind_text = TkText.new(f, 'setgrid'=>'true', 'font'=>$font,
+ 'width'=>'70', 'height'=>35, 'wrap'=>'word',
+ 'highlightthickness'=>0, 'borderwidth'=>0 ){|t|
+ TkScrollbar.new(f) {|s|
+ command proc{|*args| t.yview(*args)}
+ t.yscrollcommand proc{|first,last| s.set first,last}
+ }.pack('side'=>'right', 'fill'=>'y')
+ }.pack('expand'=>'yes', 'fill'=>'both')
+}.pack('expand'=>'yes', 'fill'=>'both')
+
+# ¥¿¥°À¸À®
+$tag_center = TkTextTag.new($twind_text,
+ 'justify' =>'center',
+ 'spacing1'=>'5m',
+ 'spacing3'=>'5m' )
+$tag_buttons = TkTextTag.new($twind_text,
+ 'lmargin1'=>'1c',
+ 'lmargin2'=>'1c',
+ 'rmargin' =>'1c',
+ 'spacing1'=>'3m',
+ 'spacing2'=>0,
+ 'spacing3'=>0 )
+
+# ¥Æ¥­¥¹¥È¤ÎÀ¸À®
+$twind_text.insert('end',
+ '¥Æ¥­¥¹¥Èwidget¾å¤Ë¾¤Îwidget¤òÁȤ߹þ¤à¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£')
+$twind_text.insert('end',
+ 'ÁȤ߹þ¤ß¥¦¥£¥ó¥É¥¦¤È¸Æ¤Ð¤ì¡¢Ç¤°Õ¤Îwidget¤¬²Äǽ¤Ç¤¹¡£')
+$twind_text.insert('end',
+ 'Î㤨¤Ð¡¢¤³¤³¤Ë2¤Ä¤Î¥Ü¥¿¥ówidget¤¬ÁȤ߹þ¤Þ¤ì¤Æ¤¤¤Þ¤¹¡£')
+$twind_text.insert('end', 'ºÇ½é¤Î¥Ü¥¿¥ó¤ò¥¯¥ê¥Ã¥¯¤¹¤È¿åÊ¿Êý¸þ¤Î¥¹¥¯¥í¡¼¥ë¤ò')
+TkTextWindow.new($twind_text, 'end',
+ 'window'=>TkButton.new($twind_text) {
+ #text 'ON'
+ text '¥ª¥ó'
+ command proc{textWindOn $twind_text,$twind_buttons}
+ cursor 'top_left_arrow'
+ })
+$twind_text.insert('end', '¤Ë¤·¤Þ¤¹¡£¤Þ¤¿2¤Ä¤á¤Î¥Ü¥¿¥ó¤ò¥¯¥ê¥Ã¥¯¤¹¤ë¤È')
+$twind_text.insert('end', '¿åÊ¿Êý¸þ¤Î¥¹¥¯¥í¡¼¥ë¤ò')
+TkTextWindow.new($twind_text, 'end',
+ 'window'=>TkButton.new($twind_text) {
+ #text 'OFF'
+ text '¥ª¥Õ'
+ command proc{textWindOff $twind_text}
+ cursor 'top_left_arrow'
+ })
+$twind_text.insert('end', '¤Ë¤·¤Þ¤¹¡£')
+
+$twind_text.insert('end', '¤â¤¦¤Ò¤È¤Ä¤ÎÎã¤Ç¤¹¡£')
+TkTextWindow.new($twind_text, 'end',
+ 'window'=>TkButton.new($twind_text) {
+ text '¤³¤³¤ò¥¯¥ê¥Ã¥¯'
+ command proc{textWindPlot $twind_text}
+ cursor 'top_left_arrow'
+ })
+$twind_text.insert('end', '¤¹¤ë¤È¡¢x-y¥×¥í¥Ã¥È¤¬¤³¤³¤Ë¸½¤ì¤Þ¤¹¡£')
+$mark_plot = TkTextMark.new($twind_text, 'insert')
+$mark_plot.gravity='left'
+$twind_text.insert('end', '¥Þ¥¦¥¹¤Ç¥Ç¡¼¥¿¤òÉÁ²è¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£')
+TkTextWindow.new($twind_text, 'end',
+ 'window'=>TkButton.new($twind_text) {
+ text '¾Ãµî'
+ command proc{textWindDel $twind_text}
+ cursor 'top_left_arrow'
+ })
+$twind_text.insert('end', '¤ò¥¯¥ê¥Ã¥¯¤¹¤ë¤È¸µ¤ËÌá¤ê¤Þ¤¹¡£
+
+')
+
+$twind_text.insert('end', 'ÁȤ߹þ¤ß¥¦¥£¥ó¥É¥¦¤À¤±¤ò¥Æ¥­¥¹¥Èwidget¾å¤Ë¡¢¼ÂºÝ¤Î')
+$twind_text.insert('end', '¥Æ¥­¥¹¥È¤Ï¤Ê¤·¤ÇÁȤ߹þ¤à¤³¤È¤ÏÊØÍø¤Ç¤¹¡£')
+$twind_text.insert('end', '¤³¤Î¾ì¹ç¤Ï¡¢¥Æ¥­¥¹¥Èwidget¤Ï¥¦¥£¥ó¥É¥¦¥Þ¥Í¡¼¥¸¥ã¤Î')
+$twind_text.insert('end', '¤è¤¦¤Ëưºî¤·¤Þ¤¹¡£Î㤨¤Ð¡¢¤³¤³¤Ë¤Ï¥Æ¥­¥¹¥Èwidget¤Ë')
+$twind_text.insert('end', '¤è¤Ã¤Æ¥Ü¥¿¥ó¤¬¤­¤ì¤¤¤Ëʤ٤é¤ì¤Æ¤¤¤Þ¤¹¡£')
+$twind_text.insert('end', '¤³¤ì¤é¤Î¥Ü¥¿¥ó¤ÇÇØ·Ê¿§¤òÊѤ¨¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹')
+$twind_text.insert('end', '("Default"¤Ç¸µ¤Î¿§¤ËÌ᤹¤³¤È¤¬¤Ç¤­¤Þ¤¹)¡£')
+$twind_text.insert('end', '"Short"¤È¤¤¤¦¥Ü¥¿¥ó¤ò¥¯¥ê¥Ã¥¯¤¹¤ë¤Èʸ»úÎó¤ÎŤµ¤¬')
+$twind_text.insert('end', 'ÊѤï¤ê¤Þ¤¹¡£¤¹¤ë¤È¼«Æ°Åª¤Ë¥Æ¥­¥¹¥Èwidget¤¬')
+$twind_text.insert('end', '¥ì¥¤¥¢¥¦¥È¤òÀ°¤¨¤Æ¤¯¤ì¤Þ¤¹¡£')
+$twind_text.insert('end', '¤â¤¦°ìÅÙÆ±¤¸¥Ü¥¿¥ó¤ò²¡¤¹¤È¸µ¤ËÌá¤ê¤Þ¤¹¡£
+
+')
+
+TkTextWindow.new($twind_text, 'end',
+ 'window'=>TkButton.new($twind_text) {|b|
+ text '¥Ç¥Õ¥©¥ë¥È'
+ command proc{embDefBg $twind_text}
+ cursor 'top_left_arrow'
+ $tag_buttons.add('end')
+ },
+ 'padx'=>3 )
+embToggle = TkVariable.new('Short')
+TkTextWindow.new($twind_text, 'end',
+ 'window'=>TkCheckButton.new($twind_text) {
+ textvariable embToggle
+ indicatoron 0
+ variable embToggle
+ onvalue 'A much longer string'
+ offvalue 'Short'
+ cursor 'top_left_arrow'
+ pady 5
+ padx 2
+ },
+ 'padx'=>3,
+ 'pady'=>2 )
+
+[ 'AntiqueWhite3', 'Bisque1', 'Bisque2', 'Bisque3', 'Bisque4',
+ 'SlateBlue3', 'RoyalBlue1', 'SteelBlue2', 'DeepSkyBlue3', 'LightBlue1',
+ 'DarkSlateGray1', 'Aquamarine2', 'DarkSeaGreen2', 'SeaGreen1',
+ 'Yellow1', 'IndianRed1', 'IndianRed2', 'Tan1', 'Tan4'
+].each{|twind_color|
+ TkTextWindow.new($twind_text, 'end',
+ 'window'=>TkButton.new($twind_text) {
+ text twind_color
+ cursor 'top_left_arrow'
+ command proc{$twind_text.bg twind_color}
+ },
+ 'padx'=>3,
+ 'pady'=>2 )
+}
+
+# ¥á¥½¥Ã¥ÉÄêµÁ
+def textWindOn (w,f)
+ if defined? $twind_scroll
+ begin
+ $twind_scroll.destroy
+ rescue
+ end
+ $twind_scroll = nil
+ end
+
+ base = TkWinfo.parent( TkWinfo.parent(w) )
+ $twind_scroll = TkScrollbar.new(base) {|s|
+ orient 'horizontal'
+ command proc{|*args| w.xview(*args)}
+ w.xscrollcommand proc{|first,last| s.set first,last}
+ w.wrap 'none'
+ pack('after'=>f, 'side'=>'bottom', 'fill'=>'x')
+ }
+
+ return nil
+end
+
+def textWindOff (w)
+ if defined? $twind_scroll
+ begin
+ $twind_scroll.destroy
+ rescue
+ end
+ $twind_scroll = nil
+ end
+ w.xscrollcommand ''
+ w.wrap 'word'
+end
+
+def textWindPlot (t)
+ if (defined? $twind_plot) && (TkWinfo.exist?($twind_plot))
+ return
+ end
+
+ $twind_plot = TkCanvas.new(t) {
+ relief 'sunken'
+ width 450
+ height 300
+ cursor 'top_left_arrow'
+ }
+
+ font = '-Adobe-Helvetica-Medium-R-Normal--*-180-*-*-*-*-*-*'
+
+ TkcLine.new($twind_plot, 100, 250, 400, 250, 'width'=>2)
+ TkcLine.new($twind_plot, 100, 250, 100, 50, 'width'=>2)
+ TkcText.new($twind_plot, 225, 20,
+ 'text'=>"A Simple Plot", 'font'=>font, 'fill'=>'brown')
+
+ (0..10).each {|i|
+ x = 100 + (i * 30)
+ TkcLine.new($twind_plot, x, 250, x, 245, 'width'=>2)
+ TkcText.new($twind_plot, x, 254,
+ 'text'=>10*i, 'font'=>font, 'anchor'=>'n')
+ }
+ (0..5).each {|i|
+ y = 250 - (i * 40)
+ TkcLine.new($twind_plot, 100, y, 105, y, 'width'=>2)
+ TkcText.new($twind_plot, 96, y,
+ 'text'=>"#{i*50}.0", 'font'=>font, 'anchor'=>'e')
+ }
+
+ for xx, yy in [[12,56],[20,94],[33,98],[32,120],[61,180],[75,160],[98,223]]
+ x = 100 + (3*xx)
+ y = 250 - (4*yy)/5
+ item = TkcOval.new($twind_plot, x-6, y-6, x+6, y+6,
+ 'width'=>1, 'outline'=>'black', 'fill'=>'SkyBlue2')
+ item.addtag 'point'
+ end
+
+ $twind_plot.itembind('point', 'Any-Enter',
+ proc{$twind_plot.itemconfigure 'current', 'fill', 'red'})
+ $twind_plot.itembind('point', 'Any-Leave',
+ proc{$twind_plot.itemconfigure 'current', 'fill', 'SkyBlue2'})
+ $twind_plot.itembind('point', '1',
+ proc{|x,y| embPlotDown $twind_plot,x,y}, "%x %y")
+ $twind_plot.itembind('point', 'ButtonRelease-1',
+ proc{$twind_plot.dtag 'selected'})
+ $twind_plot.bind('B1-Motion',
+ proc{|x,y| embPlotMove $twind_plot,x,y}, "%x %y")
+ while ($twind_text.get($mark_plot) =~ /[ \t\n]/)
+ $twind_text.delete $mark_plot
+ end
+ $twind_text.insert $mark_plot,"\n"
+ TkTextWindow.new($twind_text, $mark_plot, 'window'=>$twind_plot)
+ $tag_center.add $mark_plot
+ $twind_text.insert $mark_plot,"\n"
+end
+
+$embPlot = {'lastX'=>0, 'lastY'=>0}
+
+def embPlotDown (w, x, y)
+ w.dtag 'selected'
+ w.addtag_withtag 'selected', 'current'
+ w.raise 'current'
+ $embPlot['lastX'] = x
+ $embPlot['lastY'] = y
+end
+
+def embPlotMove (w, x, y)
+ w.move 'selected', x - $embPlot['lastX'], y - $embPlot['lastY']
+ $embPlot['lastX'] = x
+ $embPlot['lastY'] = y
+end
+
+def textWindDel (w)
+ if (defined? $twind_text) && TkWinfo.exist?($twind_plot)
+ $twind_text.delete $twind_plot
+ $twind_plot = nil
+ while ($twind_text.get($mark_plot) =~ /[ \t\n]/)
+ $twind_text.delete $mark_plot
+ end
+ $twind_text.insert $mark_plot," "
+ end
+end
+
+def embDefBg (w)
+ w['background'] = w.configinfo('background')[3]
+end
diff --git a/ext/tk/sample/demos-jp/vscale.rb b/ext/tk/sample/demos-jp/vscale.rb
new file mode 100644
index 0000000000..4cb8e0f6b7
--- /dev/null
+++ b/ext/tk/sample/demos-jp/vscale.rb
@@ -0,0 +1,77 @@
+require "tkcanvas"
+
+if defined?($vscale_demo) && $vscale_demo
+ $vscale_demo.destroy
+ $vscale_demo = nil
+end
+
+$vscale_demo = TkToplevel.new {|w|
+ title("Vertical Scale Demonstration")
+ iconname("vscale")
+}
+positionWindow($vscale_demo)
+
+msg = TkLabel.new($vscale_demo) {
+ font $font
+ wraplength '3.5i'
+ justify 'left'
+# text "²¼¤Ë¤ÏÌð°õ¤¬1¤Ä¤È¾èľ¤Ê¥¹¥±¡¼¥ë¤¬É½¼¨¤µ¤ì¤Æ¤¤¤Þ¤¹¡£\
+#¥¹¥±¡¼¥ë¾å¤Ç¥Þ¥¦¥¹¥Ü¥¿¥ó1¤ò¥¯¥ê¥Ã¥¯¡¢¤Þ¤¿¤Ï¥É¥é¥Ã¥°¤¹¤ë¤È\
+#Ìð°õ¤ÎŤµ¤òÊѤ¨¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£"
+ text "¤Ë¤Ï¥Ð¡¼¤È½Ä·¿¤Î¥¹¥±¡¼¥ë¤¬É½¼¨¤µ¤ì¤Æ¤¤¤Þ¤¹¡£¥¹¥±¡¼¥ë¤Ç¥Þ¥¦¥¹¤Î¥Ü¥¿¥ó1 ¤ò¥¯¥ê¥Ã¥¯¤¹¤ë¤«¥É¥é¥Ã¥°¤·¤Æ¥Ð¡¼¤Î¹â¤µ¤òÊѤ¨¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£½ª¤Ã¤¿¤é¡Öλ²ò¡×¥Ü¥¿¥ó¤ò²¡¤·¤Æ¤¯¤À¤µ¤¤¡£"
+}
+msg.pack('side'=>'top', 'padx'=>'.5c')
+
+TkFrame.new($vscale_demo) {|frame|
+ TkButton.new(frame) {
+ text 'λ²ò'
+ command proc {
+ tmppath = $vscale_demo
+ $vscale_demo = nil
+ tmppath.destroy
+ }
+ }.pack('side'=>'left', 'expand'=>'yes')
+
+ TkButton.new(frame) {
+ text '¥³¡¼¥É»²¾È'
+ command proc { showCode 'vscale' }
+ }.pack('side'=>'left', 'expand'=>'yes')
+}.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>'2m')
+
+TkFrame.new($vscale_demo) {|frame|
+ borderwidth 10
+ canvas = TkCanvas.new(frame) {|c|
+ width 50
+ height 50
+ bd 0
+ highlightthickness 0
+ TkcPolygon.new(c, 0, 0, 1, 1, 2, 2) {
+ fill 'SeaGreen3'
+ tags 'poly'
+ }
+ TkcLine.new(c, 0, 0, 1, 1, 2, 2, 0, 0) {
+ fill 'black'
+ tags 'line'
+ }
+ }.pack('side'=>'left', 'anchor'=>'nw', 'fill'=>'y')
+ scale = TkScale.new(frame) {
+ orient 'vertical'
+ length 284
+ from 0
+ to 250
+ command proc{|value| setHeight(canvas, value)}
+ tickinterval 50
+ }.pack('side'=>'left', 'anchor'=>'ne')
+ scale.set 75
+}.pack
+
+
+def setHeight(w, height)
+ height = height + 21
+ y2 = height - 30
+ if y2 < 21
+ y2 = 21
+ end
+ w.coords 'poly',15,20,35,20,35,y2,45,y2,25,height,5,y2,15,y2,15,20
+ w.coords 'line',15,20,35,20,35,y2,45,y2,25,height,5,y2,15,y2,15,20
+end
diff --git a/ext/tk/sample/demos-jp/widget b/ext/tk/sample/demos-jp/widget
new file mode 100644
index 0000000000..3d4690d57a
--- /dev/null
+++ b/ext/tk/sample/demos-jp/widget
@@ -0,0 +1,504 @@
+#!/usr/local/bin/ruby
+
+# tk ´Ø·¸¥é¥¤¥Ö¥é¥ê¤ÎÆÉ¤ß¹þ¤ß
+require 'tk'
+require 'tkafter'
+
+# widget demo directory °ÌÃÖ¤Î³ÍÆÀ
+$demo_dir = File.dirname($0)
+
+# root ¤ÎÀ¸À®
+$root = TkRoot.new{title "Widget Demonstration"}
+
+# tk ¥Ð¡¼¥¸¥ç¥ó¤Î¼èÆÀ
+$tk_version = Tk::TK_VERSION
+
+# tcl_platform ¾ðÊó¤Ø¤Î¥¢¥¯¥»¥¹¥ª¥Ö¥¸¥§¥¯¥È
+$tk_platform = TkVarAccess.new('tcl_platform')
+
+# ¥Õ¥©¥ó¥ÈÀßÄê
+$font = TkFont.new('-*-Helvetica-Medium-R-Normal--*-140-*-*-*-*-*-*', nil)
+knjfont = '-*--16-*-jisx0208.1983-0'
+$kanji_font = TkFont.new('-*-Helvetica-Medium-R-Normal--*-140-*-*-*-*-*-*',
+ knjfont)
+TkOption.add('*kanjiFont', knjfont, 'startupFile')
+$msg_kanji_font = TkFont.new('-*-Helvetica-Medium-R-Normal--*-140-*-*-*-*-*-*',
+ '-*--24-*-jisx0208.1983-0')
+#######
+#case($tk_version)
+#when /^4.*/
+# $font = '-*-Helvetica-Medium-R-Normal--*-140-*-*-*-*-*-*'
+# $kanji_font = '-*--16-*-jisx0208.1983-0'
+# $msg_kanji_font = '-*--24-*-jisx0208.1983-0'
+# $knjfont_opt = 'kanjifont'
+# TkOption.add('*kanjiFont', $kanji_font, 'startupFile')
+#
+#when /^8.*/
+# Tk.tk_call('font', 'create', '@ascii',
+# '-copy', '-*-Helvetica-Medium-R-Normal--*-140-*-*-*-*-*-*')
+# Tk.tk_call('font', 'create', '@kanji',
+# '-copy', '-*--16-*-jisx0208.1983-0')
+# Tk.tk_call('font', 'create', '@msg_knj',
+# '-copy', '-*--24-*-jisx0208.1983-0')
+# Tk.tk_call('font', 'create', '@cFont', '-compound', '@ascii @kanji')
+# Tk.tk_call('font', 'create', '@cMsgFont', '-compound', '@ascii @msg_knj')
+# $font = '-*-Helvetica-Medium-R-Normal--*-140-*-*-*-*-*-*'
+# $kanji_font = '@cFont'
+# $msg_kanji_font = '@cMsgFont'
+# $knjfont_opt = 'font'
+#end
+#######
+
+# ¥á¥Ë¥å¡¼ÀßÄê
+TkMenubar.new($root,
+ [[['File', 0],
+ ['About ... ', proc{aboutBox}, 0, '<F1>'],
+ '---',
+ ['Quit', proc{exit}, 0, 'Meta-Q']
+ ]]).pack('side'=>'top', 'fill'=>'x')
+$root.bind('F1', proc{aboutBox})
+
+=begin
+TkFrame.new($root){|frame|
+ TkMenubutton.new(frame){|button|
+ m = TkMenu.new(button) {
+ add 'command', 'label'=>'Quit', 'command'=>proc{exit}, 'underline'=>0
+ }
+ menu m
+ text 'File'
+ underline 0
+ }.pack('side'=>'left')
+}.pack('side'=>'top', 'fill'=>'x')
+=end
+
+# ¥Æ¥­¥¹¥È¥Ü¥Ã¥¯¥¹¤ÎÀ¸À®
+if $tk_version =~ /^4\.[01]/
+ scr = TkScrollbar.new($root, 'orient'=>'vertical')
+ txt = TkText.new($root) {
+ #wrap 'word'
+ wrap 'char'
+ width 60
+ height 30
+ font $font
+ setgrid 'yes'
+ yscrollcommand proc{|first,last| scr.set first,last}
+ }
+ scr.command(proc{|*args| txt.yview(*args)})
+ scr.pack('side'=>'right', 'fill'=>'y')
+ txt.pack('expand'=>'yes', 'fill'=>'both')
+else
+ textFrame = TkFrame.new($root)
+ scr = TkScrollbar.new($root, 'orient'=>'vertical',
+ 'highlightthickness'=>0, 'takefocus'=>1) {
+ pack('in'=>textFrame, 'side'=>'right', 'fill'=>'y', 'padx'=>1)
+ }
+ txt = TkText.new($root) {
+ #wrap 'word'
+ wrap 'char'
+ width 60
+ height 30
+ font $font
+ setgrid 'yes'
+ highlightthickness 0
+ padx 4
+ pady 2
+ takefocus 0
+ yscrollcommand proc{|first,last| scr.set first,last}
+ }
+ scr.command(proc{|*args| txt.yview(*args)})
+# txt.pack('in'=>textFrame, 'expand'=>'yes', 'fill'=>'both', 'padx'=>1)
+ txt.pack('in'=>textFrame, 'expand'=>'yes', 'fill'=>'both')
+# textFrame.pack('expand'=>'yes', 'fill'=>'both', 'padx'=>1, 'pady'=>2)
+ textFrame.pack('expand'=>'yes', 'fill'=>'both')
+
+ statusBar = TkFrame.new($root) {|f|
+ $statusBarLabel = \
+ TkLabel.new(f, 'text'=>" ", 'relief'=>'sunken', 'bd'=>1, 'anchor'=>'w',
+ 'font'=>'-*-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*') \
+ .pack('side'=>'left', 'padx'=>2, 'expand'=>'yes', 'fill'=>'both')
+ TkLabel.new(f, 'width'=>8, 'relief'=>'sunken', 'bd'=>1, 'anchor'=>'w',
+ 'font'=>'-*-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*') \
+ .pack('side'=>'left', 'padx'=>2)
+ }.pack('side'=>'bottom', 'fill'=>'x', 'pady'=>2)
+end
+
+# ¥Æ¥­¥¹¥È¥¿¥°ÀßÄê
+tag_title = TkTextTag.new(txt, 'font'=>'-*-Helvetica-Bold-R-Normal--*-180-*-*-*-*-*-*')
+tag_kanji_title = TkTextTag.new(txt, 'kanjifont'=>$msg_kanji_font)
+tag_middle = TkTextTag.new(txt, 'kanjifont'=>$kanji_font)
+tag_demospace = TkTextTag.new(txt, 'lmargin1'=>'1c', 'lmargin2'=>'1c')
+
+if TkWinfo.depth($root) == '1'
+ tag_demo = TkTextTag.new(txt, 'lmargin1'=>'1c', 'lmargin2'=>'1c',
+ 'underline'=>1)
+ $tag_visited = TkTextTag.new(txt, 'lmargin1'=>'1c', 'lmargin2'=>'1c',
+ 'underline'=>1)
+ tag_hot = TkTextTag.new(txt, 'background'=>'black', 'foreground'=>'white')
+else
+ tag_demo = TkTextTag.new(txt, 'lmargin1'=>'1c', 'lmargin2'=>'1c',
+ 'foreground'=>'blue', 'underline'=>1)
+ $tag_visited = TkTextTag.new(txt, 'lmargin1'=>'1c', 'lmargin2'=>'1c',
+ 'foreground'=>'#303080', 'underline'=>1)
+# tag_hot = TkTextTag.new(txt, 'relief'=>'raised', 'borderwidth'=>1,
+# 'background'=>'SeaGreen3')
+ tag_hot = TkTextTag.new(txt, 'borderwidth'=>1, 'foreground'=>'red')
+end
+
+#tag_demo.bind('Button-1', proc{invoke txt, txt.index('current')})
+tag_demo.bind('ButtonRelease-1',
+ proc{|x,y|invoke txt, txt.index("@#{x},#{y}")}, '%x %y')
+
+lastLine = TkVariable.new("")
+newLine = TkVariable.new("")
+tag_demo.bind('Enter', proc{|x,y|
+ lastLine.value = txt.index("@#{x},#{y} linestart")
+ tag_hot.add(lastLine.value, "#{lastLine.value} lineend")
+ showStatus txt, txt.index("@#{x},#{y}")
+ },
+ '%x %y')
+tag_demo.bind('Leave',
+ proc{
+ tag_hot.remove('1.0','end')
+ txt.configure('cursor','xterm')
+ $statusBarLabel.configure('text'=>"")
+ })
+tag_demo.bind('Motion', proc{|x, y|
+ newLine.value = txt.index("@#{x},#{y} linestart")
+ if newLine.value != lastLine.value
+ tag_hot.remove('1.0','end')
+ lastLine.value = newLine.value
+ if ( txt.tag_names("@#{x},#{y}").find{|t|
+ t.kind_of?(String) && t =~ /^demo-/
+ } )
+ tag_hot.add(lastLine.value,
+ "#{lastLine.value} lineend -1 chars")
+ end
+ end
+ showStatus txt, txt.index("@#{x},#{y}")
+ },
+ '%x %y')
+
+# ¥Æ¥­¥¹¥ÈÀ¸À®
+txt.insert('end', 'Ruby/Tk : Widget', tag_title)
+#txt.insert('end', " ¥Ç¥â¥ó¥¹¥È¥ì¡¼¥·¥ç¥ó\n", tag_middle)
+txt.insert('end', " ¥Ç¥â¥ó¥¹¥È¥ì¡¼¥·¥ç¥ó\n", tag_kanji_title)
+txt.insert('end', <<"EOT")
+
+¤³¤Î¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¤Ï¡¢Tk Widget ¤òÍѤ¤¤Æ¤É¤Î¤è¤¦¤Ê¤³¤È¤¬¤Ç¤­¤ë¤«\
+¤ò¼¨¤¹¤¿¤á¤Î¡¢¤¤¤¯¤Ä¤«¤Î¾®¤µ¤Ê¥¹¥¯¥ê¥×¥È¤ËÂФ¹¤ë¥Õ¥í¥ó¥È¥¨¥ó¥É¤òÄó\
+¶¡¤·¤Æ¤¤¤Þ¤¹¡£°Ê²¼¤Ë½çÈ֤˵󤲤é¤ì¤Æ¤¤¤ë¥Ç¥â¥ó¥¹¥È¥ì¡¼¥·¥ç¥ó¤ò¼Â¹Ô\
+¤¹¤ë¤Ë¤Ï¥Þ¥¦¥¹¤Ç¥¯¥ê¥Ã¥¯¤·¤Æ¤¯¤À¤µ¤¤¡£¥Ç¥â¥ó¥¹¥È¥ì¡¼¥·¥ç¥ó¤Î¥¦¥£¥ó\
+¥É¥¦¤¬¸½¤ì¤ë¤È¡¢¥Ç¥â¥ó¥¹¥È¥ì¡¼¥·¥ç¥ó¤òÀ¸À®¤·¤¿ Ruby/Tk ¤Î¥³¡¼¥É¤ò¸«\
+¤ë¤¿¤á¤Ë¡¢"¥³¡¼¥É»²¾È"¥Ü¥¿¥ó¤ò¥¯¥ê¥Ã¥¯¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£¤¢¤Ê¤¿¤¬\
+˾¤à¤Ê¤é¡¢¤½¤Î¥³¡¼¥É¤ò½¤Àµ¤¹¤ë¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£½¤Àµ¤·¤¿¥³¡¼¥É¤Ç¥Ç¥â\
+¥ó¥¹¥È¥ì¡¼¥·¥ç¥ó¤òºÆ¼Â¹Ô¤¹¤ë¤¿¤á¤Ë¤Ï¡¢¥³¡¼¥É¤¬½ñ¤«¤ì¤¿¥¦¥£¥ó¥É¥¦¤Ë\
+¤¢¤ë"¥Ç¥âºÆ¼Â¹Ô" ¥Ü¥¿¥ó¤ò¥¯¥ê¥Ã¥¯¤·¤Æ¤¯¤À¤µ¤¤¡£
+
+EOT
+
+#txt.insert('end',"¥é¥Ù¥ë, ¥Ü¥¿¥ó, ¥Á¥§¥Ã¥¯¥Ü¥¿¥ó, ¥é¥¸¥ª¥Ü¥¿¥ó\n",tag_middle)
+txt.insert('end', "¥é¥Ù¥ë, ¥Ü¥¿¥ó, ¥Á¥§¥Ã¥¯¥Ü¥¿¥ó, ¥é¥¸¥ª¥Ü¥¿¥ó\n",
+ tag_kanji_title)
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "1. ¥é¥Ù¥ë (¥Æ¥­¥¹¥È, ¥Ó¥Ã¥È¥Þ¥Ã¥×)\n",
+ tag_demo, "demo-label")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "2. ¥Ü¥¿¥ó \n", tag_demo, "demo-button")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "3. ¥Á¥§¥Ã¥¯¥Ü¥¿¥ó (Ê£¿ô¤òÁªÂò²Äǽ)\n",
+ tag_demo, "demo-check")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "4. ¥é¥¸¥ª¥Ü¥¿¥ó (Ǥ°Õ¤Î°ì¤Ä¤òÁªÂò²Äǽ)\n",
+ tag_demo, "demo-radio")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "5. ¥Ü¥¿¥ó¤Çºî¤é¤ì¤¿15-¥Ñ¥º¥ë¥²¡¼¥à\n",
+ tag_demo, "demo-puzzle")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "6. ¥Ó¥Ã¥È¥Þ¥Ã¥×¤ò»ÈÍѤ·¤¿¥¢¥¤¥³¥ó¥Ü¥¿¥ó\n",
+ tag_demo, "demo-icon")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "7. ²èÁü¤òɽ¼¨¤¹¤ëÆó¤Ä¤Î¥é¥Ù¥ë\n",
+ tag_demo, "demo-image1")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "8. ²èÁü¤ò¸«¤ë¤¿¤á¤Î´Êñ¤Ê¥æ¡¼¥¶¥¤¥ó¥¿¡¼¥Õ¥§¡¼¥¹\n",
+ tag_demo, "demo-image2")
+txt.insert('end', " \n ", tag_demospace)
+
+txt.insert('end', "\n")
+#txt.insert('end', "¥ê¥¹¥È¥Ü¥Ã¥¯¥¹\n", tag_middle)
+txt.insert('end', "¥ê¥¹¥È¥Ü¥Ã¥¯¥¹\n", tag_kanji_title)
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "1. ÅÔÆ»Éܸ©.\n", tag_demo, "demo-states")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "2. ¿§: ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¤Î¤¿¤á¤ÎÇÛ¿§¤òÊѤ¨¤ë\n",
+ "#{tag_demo.id} demo-colors")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "3. ³Ê¸À½¸\n", tag_demo, "demo-sayings")
+txt.insert('end', " \n ", tag_demospace)
+
+txt.insert('end', "\n")
+#txt.insert('end', "¥¨¥ó¥È¥ê\n", tag_middle)
+txt.insert('end', "¥¨¥ó¥È¥ê\n", tag_kanji_title)
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "1. ¥¹¥¯¥í¡¼¥ë¥Ð¡¼¤Ê¤·\n", tag_demo, "demo-entry1")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "2. ¥¹¥¯¥í¡¼¥ë¥Ð¡¼¤¢¤ê\n", tag_demo, "demo-entry2")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "3. ´Êñ¤Ê¥Õ¥©¡¼¥à\n", tag_demo, "demo-form")
+txt.insert('end', " \n ", tag_demospace)
+
+txt.insert('end', "\n")
+#txt.insert('end', "¥Æ¥­¥¹¥È\n", tag_middle)
+txt.insert('end', "¥Æ¥­¥¹¥È\n", tag_kanji_title)
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "1. ´ðËÜŪ¤Ê¥Æ¥­¥¹¥È\n", tag_demo, "demo-text")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "2. ɽ¼¨¥¹¥¿¥¤¥ë.\n", tag_demo, "demo-style")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "3. ¥Ï¥¤¥Ñ¡¼¥Æ¥­¥¹¥È(¥¿¥°¥Ð¥¤¥ó¥É).\n",
+ tag_demo, "demo-bind")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "4. ¥¦¥£¥ó¥É¥¦¤òËä¤á¹þ¤ó¤À¥Æ¥­¥¹¥È\n",
+ tag_demo, "demo-twind")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "5. ¸¡º÷\n", tag_demo, "demo-search")
+txt.insert('end', " \n ", tag_demospace)
+
+txt.insert('end', "\n")
+#txt.insert('end', "¥­¥ã¥ó¥Ð¥¹\n", tag_middle)
+txt.insert('end', "¥­¥ã¥ó¥Ð¥¹\n", tag_kanji_title)
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "1. ¥¢¥¤¥Æ¥à¤Î·¿\n", tag_demo, "demo-items")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "2. 2 ¼¡¸µ¤Î¥×¥í¥Ã¥È\n", tag_demo, "demo-plot")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "3. ¥Æ¥­¥¹¥È\n", tag_demo, "demo-ctext")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "4. Ìð°õ¤Î·Á\n", tag_demo, "demo-arrow")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "5. ¥ë¡¼¥é¡¼\n", tag_demo, "demo-ruler")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "6. ¥Õ¥í¥¢¥×¥é¥ó\n", tag_demo, "demo-floor")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "7. ¥¹¥¯¥í¡¼¥ë²Äǽ¤Ê¥­¥ã¥ó¥Ð¥¹\n",
+ tag_demo, "demo-cscroll")
+txt.insert('end', " \n ", tag_demospace)
+
+txt.insert('end', "\n")
+#txt.insert('end', "¥¹¥±¡¼¥ë\n", tag_middle)
+txt.insert('end', "¥¹¥±¡¼¥ë\n", tag_kanji_title)
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "1. ¿âľ\n", tag_demo.id, "demo-vscale")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "2. ¿åÊ¿\n", tag_demo.id, "demo-hscale")
+txt.insert('end', " \n ", tag_demospace)
+
+txt.insert('end', "\n")
+#txt.insert('end', "¥á¥Ë¥å¡¼\n", tag_middle)
+txt.insert('end', "¥á¥Ë¥å¡¼\n", tag_kanji_title)
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "1. ¥á¥Ë¥å¡¼¤È¥«¥¹¥±¡¼¥É¤ò´Þ¤ó¤À¥¦¥£¥ó¥É¥¦\n",
+ tag_demo, "demo-menu")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "2. ¥á¥Ë¥å¡¼¤È¥«¥¹¥±¡¼¥É¤ò´Þ¤ó¤À¥¦¥£¥ó¥É¥¦ (Tk8.x ÀìÍÑ)\n",
+ tag_demo, "demo-menu8x")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "3. ¥á¥Ë¥å¡¼¥Ü¥¿¥ó (Tk8.x ÀìÍÑ)\n",
+ tag_demo, "demo-menubu")
+txt.insert('end', " \n ", tag_demospace)
+
+txt.insert('end', "\n")
+#txt.insert('end', "¥À¥¤¥¢¥í¥°¥¦¥£¥ó¥É¥¦\n", tag_middle)
+txt.insert('end', "¥À¥¤¥¢¥í¥°¥¦¥£¥ó¥É¥¦\n", tag_kanji_title)
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "1. ¥á¥Ã¥»¡¼¥¸¥Ü¥Ã¥¯¥¹\n", tag_demo, "demo-msgbox")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "2. ¥Õ¥¡¥¤¥ëÁªÂò¥À¥¤¥¢¥í¥°\n", tag_demo, "demo-filebox")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "3. ¿§ÁªÂò¥À¥¤¥¢¥í¥°\n", tag_demo, "demo-clrpick")
+txt.insert('end', " \n ", tag_demospace)
+
+txt.insert('end', "\n")
+#txt.insert('end', "¤½¤Î¾\n", tag_middle)
+txt.insert('end', "¤½¤Î¾\n", tag_kanji_title)
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "1. ÁȤ߹þ¤ß¤Î¥Ó¥Ã¥È¥Þ¥Ã¥×\n", tag_demo, "demo-bitmap")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "2. ¥â¡¼¥À¥ë¥À¥¤¥¢¥í¥°(¥í¡¼¥«¥ë¥°¥é¥Ö)\n",
+ tag_demo, "demo-dialog1")
+txt.insert('end', " \n ", tag_demospace)
+txt.insert('end', "3. ¥â¡¼¥À¥ë¥À¥¤¥¢¥í¥°(¥°¥í¡¼¥Ð¥ë¥°¥é¥Ö)\n",
+ tag_demo, "demo-dialog2")
+txt.insert('end', " \n ", tag_demospace)
+
+txt.state('disabled')
+scr.focus
+
+################################
+# method ÄêµÁ
+################################
+def positionWindow(w)
+ w.geometry('+300+300')
+end
+
+# ¿Æ¥¦¥£¥¸¥§¥Ã¥È¤È¡¤ÊÑ¿ô̾¤È TkVariable ¤È¤ÎÁÈ(ÇÛÎó)¤ÎʤӤòÅϤ¹
+$showVarsWin = {}
+def showVars (parent, *args)
+ if $showVarsWin[parent.path]
+ begin
+ $showVarsWin[parent.path].destroy
+ rescue
+ end
+ end
+ w = TkToplevel.new(parent) {|w|
+ title "Variable values"
+ TkLabel.new(w) {
+ text "ÊÑ¿ôÃÍ:"
+ width 20
+ anchor 'center'
+ font '-Adobe-helvetica-medium-r-normal--*-180-*-*-*-*-*-*'
+ }.pack('side'=>'top', 'fill'=>'x')
+ len = 1
+ args.each{|vnam,vbody|
+ len = vnam.to_s.length if vnam.to_s.length > len
+ }
+ args.each{|vnam,vbody|
+ TkFrame.new(w){|f|
+ #TkLabel.new(f, 'text'=>"#{vnam}: ").pack('side'=>'left')
+ TkLabel.new(f, 'text'=>"#{vnam}: ",'width'=>len+2).pack('side'=>'left')
+ TkLabel.new(f, 'textvariable'=>vbody, 'anchor'=>'w')\
+ .pack('side'=>'left', 'expand'=>'yes', 'fill'=>'x')
+ }.pack('side'=>'top', 'anchor'=>'w', 'fill'=>'x')
+ }
+ TkButton.new(w) {
+ text "λ²ò"
+ command proc{w.destroy}
+ }.pack('side'=>'bottom', 'pady'=>2)
+ }
+ $showVarsWin[parent.path] = w
+end
+
+# ¥Æ¥­¥¹¥È¾å¤Ç¤Î click ¤ËÂФ¹¤ëưºî
+def invoke (txt, index)
+ tag = txt.tag_names(index).find{|t| t.kind_of?(String) && t =~ /^demo-/}
+ return unless tag
+ cursor = txt.cget('cursor')
+ txt.cursor('watch')
+ Tk.update
+# eval `cat #{tag[5..-1]}.rb`
+ eval `cat #{[$demo_dir, tag[5..-1]].join(File::Separator)}.rb`
+ Tk.update
+# txt.cursor('xterm')
+ txt.cursor(cursor)
+
+ $tag_visited.add("#{index} linestart +1 chars", "#{index} lineend +1 chars")
+end
+
+# ¾õÂÖɽ¼¨
+def showStatus (txt, index)
+ tag = txt.tag_names(index).find{|t| t.kind_of?(String) && t =~ /^demo-/}
+ cursor = txt.cget('cursor')
+ unless tag
+ $statusBarLabel.configure('text', " ")
+ newcursor = 'xterm'
+ else
+ demoname = tag[5..-1]
+ $statusBarLabel.configure('text',
+ "¥µ¥ó¥×¥ë¥×¥í¥°¥é¥à \"#{demoname}\" ¤Î¼Â¹Ô ")
+ newcursor = 'hand2'
+ end
+ txt.configure('cursor'=>newcursor) if cursor != newcursor
+end
+
+# ¥½¡¼¥¹¥³¡¼¥É¤Îɽ¼¨
+def showCode (demo)
+ file = "#{demo}.rb"
+ $code_window = nil unless defined? $code_window
+ if $code_window == nil || TkWinfo.exist?($code_window) == '0'
+ $code_window = TkToplevel.new(nil)
+ f = TkFrame.new($code_window)
+ TkButton.new(f) {
+ text "λ²ò"
+ command proc{
+ $code_window.destroy
+ $code_window = nil
+ }
+ }.pack('side'=>'left', 'expand'=>'yes', 'pady'=>2)
+ TkButton.new(f) {
+ text "ºÆ¼Â¹Ô"
+ command proc{eval($code_text.get('1.0','end'))}
+ }.pack('side'=>'left', 'expand'=>'yes', 'pady'=>2)
+# f.pack('side'=>'bottom', 'expand'=>'yes', 'fill'=>'x')
+ f.pack('side'=>'bottom', 'fill'=>'x')
+
+ if $tk_version =~ /^4\.[01]/
+ s = TkScrollbar.new($code_window, 'orient'=>'vertical')
+ $code_text = TkText.new($code_window) {
+ height 40
+ setgrid 'yes'
+ yscrollcommand proc{|first,last| s.set first,last}
+ }
+ s.command(proc{|*args| $code_text.yview(*args)})
+ s.pack('side'=>'right', 'fill'=>'y')
+ $code_text.pack('side'=>'left', 'expand'=>'yes', 'fill'=>'both')
+ else
+ TkFrame.new($code_window) {|f|
+ pack('expand'=>'yes', 'fill'=>'both', 'padx'=>1, 'pady'=>1)
+
+ hs = TkScrollbar.new($code_window, 'highlightthickness'=>0,
+ 'orient'=>'horizontal')
+ vs = TkScrollbar.new($code_window, 'highlightthickness'=>0,
+ 'orient'=>'vertical')
+ $code_text = TkText.new($code_window) {|t|
+ height 40
+ #wrap 'word'
+ wrap 'char'
+ xscrollcommand proc{|first,last| hs.set first,last}
+ yscrollcommand proc{|first,last| vs.set first,last}
+ setgrid 'yes'
+ highlightthickness 0
+ pady 2
+ padx 3
+ hs.command(proc{|*args| $code_text.xview(*args)})
+ vs.command(proc{|*args| $code_text.yview(*args)})
+ }
+
+ $code_text.grid('in'=>f, 'padx'=>1, 'pady'=>1, 'row'=>0, 'column'=>0,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ vs.grid('in'=>f, 'padx'=>1, 'pady'=>1, 'row'=>0, 'column'=>1,
+ 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+# xs.grid('in'=>f, 'padx'=>1, 'pady'=>1, 'row'=>1, 'column'=>0,
+# 'rowspan'=>1, 'columnspan'=>1, 'sticky'=>'news')
+ TkGrid.rowconfigure(f, 0, 'weight'=>1, 'minsize'=>0)
+ TkGrid.columnconfigure(f, 0, 'weight'=>1, 'minsize'=>0)
+ }
+ end
+ else
+ $code_window.deiconify
+ $code_window.raise
+ end
+ $code_window.title("Demo code: #{file}")
+ $code_window.iconname(file)
+# fid = open(file, 'r')
+ fid = open([$demo_dir, file].join(File::Separator), 'r')
+ $code_text.delete('1.0', 'end')
+ #$code_text.insert('1.0', `cat #{file}`)
+ $code_text.insert('1.0', fid.read)
+ #$code_mark = TkTextMark.new($code_text, '1.0')
+ #$code_text.set_insert('1.0')
+ TkTextMarkInsert.new($code_text,'1.0')
+ fid.close
+end
+
+# aboutBox
+#
+# Pops up a message box with an "about" message
+#
+def aboutBox
+ Tk.messageBox('icon'=>'info', 'type'=>'ok', 'title'=>'About Widget Demo',
+ 'message'=>"Ruby/Tk ¥¦¥£¥¸¥§¥Ã¥È¥Ç¥â Ver.1.2.2\n\n( based on Tk ¥¦¥£¥¸¥§¥Ã¥È¥Ç¥â :: Copyright (c) 1996-1997 Sun Microsystems, Inc. )\n\nRunning Version :: Ruby#{VERSION}/Tk#{$tk_version}#{(Tk::JAPANIZED_TK)? 'jp': ''}")
+end
+
+################################
+# ¥¤¥Ù¥ó¥ÈÂÔ¤Á¤ËÆþ¤ë
+Tk.mainloop
diff --git a/ext/tk/sample/resource.en b/ext/tk/sample/resource.en
new file mode 100644
index 0000000000..bfdc809278
--- /dev/null
+++ b/ext/tk/sample/resource.en
@@ -0,0 +1,12 @@
+!
+! see Tcl/Tk's "options" manual for "Database Name" and "Database Class"
+!
+*BtnFrame.borderWidth: 5
+*BtnFrame.relief: ridge
+*BtnFrame.Button.background: wheat
+*BtnFrame.Button.foreground: red
+*hello.text: HELLO
+*quit.text: QUIT
+*BTN_CMD.show_msg: {|arg| print "($SAFE=#{$SAFE}) ";\
+ print "Hello!! This is a sample of #{arg}.\n"}
+*BTN_CMD.bye_msg: {print "($SAFE=#{$SAFE}) Good-bye¡¥\n"}
diff --git a/ext/tk/sample/resource.ja b/ext/tk/sample/resource.ja
new file mode 100644
index 0000000000..8b715f36b0
--- /dev/null
+++ b/ext/tk/sample/resource.ja
@@ -0,0 +1,12 @@
+!
+! see Tcl/Tk's "options" manual for "Database Name" and "Database Class"
+!
+*BtnFrame.borderWidth: 5
+*BtnFrame.relief: ridge
+*BtnFrame.Button.background: wheat
+*BtnFrame.Button.foreground: red
+*hello.text: ¤³¤ó¤Ë¤Á¤Ï
+*quit.text: ½ªÎ»
+*BTN_CMD.show_msg: {|arg| print "($SAFE=#{$SAFE}) ";\
+ print "¤³¤ó¤Ë¤Á¤Ï¡ª¡ª #{arg} ¤Î¥µ¥ó¥×¥ë¤Ç¤¹¡¥\n"}
+*BTN_CMD.bye_msg: {print "($SAFE=#{$SAFE}) ¤µ¤è¤¦¤Ê¤é¡¥\n"}
diff --git a/ext/tk/sample/safe-tk.rb b/ext/tk/sample/safe-tk.rb
new file mode 100644
index 0000000000..6b6de8f848
--- /dev/null
+++ b/ext/tk/sample/safe-tk.rb
@@ -0,0 +1,71 @@
+#!/usr/bin/env ruby
+# This script is a sample of MultiTkIp class
+
+require "multi-tk"
+
+# create slave interpreters
+trusted_slave = MultiTkIp.new_slave
+safe_slave1 = MultiTkIp.new_safeTk
+safe_slave2 = MultiTkIp.new_safeTk('fill'=>:none, 'expand'=>false)
+#safe_slave2 = MultiTkIp.new_safeTk('fill'=>:none)
+#safe_slave2 = MultiTkIp.new_safeTk('expand'=>false)
+
+
+cmd = Proc.new{|txt|
+ #####################
+ ## from TkTimer2.rb
+
+ if TkCore::INTERP.safe?
+ # safeTk doesn't have permission to call 'wm' command
+ else
+ root = TkRoot.new(:title=>'timer sample')
+ end
+
+ label = TkLabel.new(:parent=>root, :relief=>:raised, :width=>10) \
+ .pack(:side=>:bottom, :fill=>:both)
+
+ tick = proc{|aobj|
+ cnt = aobj.return_value + 5
+ label.text format("%d.%02d", *(cnt.divmod(100)))
+ cnt
+ }
+
+ timer = TkTimer.new(50, -1, tick).start(0, proc{ label.text('0.00'); 0 })
+
+ TkButton.new(:text=>'Start') {
+ command proc{ timer.continue unless timer.running? }
+ pack(:side=>:left, :fill=>:both, :expand=>true)
+ }
+ TkButton.new(:text=>'Restart') {
+ command proc{ timer.restart(0, proc{ label.text('0.00'); 0 }) }
+ pack('side'=>'right','fill'=>'both','expand'=>'yes')
+ }
+ TkButton.new(:text=>'Stop') {
+ command proc{ timer.stop if timer.running? }
+ pack('side'=>'right','fill'=>'both','expand'=>'yes')
+ }
+
+ ev_quit = TkVirtualEvent.new('Control-c', 'Control-q')
+ Tk.root.bind(ev_quit, proc{Tk.exit}).focus
+}
+
+# call on the default master interpreter
+trusted_slave.eval_proc(cmd, 'trusted') # label -> .w00012
+safe_slave1.eval_proc(cmd, 'safe1') # label -> .w00016
+safe_slave2.eval_proc(cmd, 'safe2') # label -> .w00020
+cmd.call('master') # label -> .w00024
+
+#second_master = MultiTkIp.new(&cmd)
+
+TkTimer.new(2000, -1, proc{p ['safe1', safe_slave1.deleted?]}).start
+TkTimer.new(2000, -1, proc{p ['safe2', safe_slave2.deleted?]}).start
+TkTimer.new(2000, -1, proc{p ['trusted', trusted_slave.deleted?]}).start
+
+TkTimer.new(10000, 1,
+ proc{
+ trusted_slave.eval_proc{Tk.root.destroy}
+ trusted_slave.delete
+ print "*** The trusted_slave is deleted by the timer.\n"
+ }).start
+
+Tk.mainloop
diff --git a/ext/tk/sample/tkbiff.rb b/ext/tk/sample/tkbiff.rb
index ac27184437..5fe367fd97 100644
--- a/ext/tk/sample/tkbiff.rb
+++ b/ext/tk/sample/tkbiff.rb
@@ -12,7 +12,7 @@ if ARGV.length == 0
if ENV['MAIL']
$spool = ENV['MAIL']
else
- $spool = '/usr/spool/mail/' + ENV['USER']
+ $spool = '/var/spool/mail/' + ENV['USER']
end
else
$spool = ARGV[0]
@@ -70,7 +70,13 @@ end
require "tkscrollbox"
+my_appname = Tk.appname('tkbiff')
$top = TkRoot.new
+if ((TkWinfo.interps($top) - [my_appname]).find{|ip| ip =~ /^tkbiff/})
+ STDERR.print("Probably other 'tkbiff's are running. Bye.\n")
+ exit
+end
+
$top.withdraw
$list = TkScrollbox.new($top) {
relief 'raised'
@@ -136,12 +142,12 @@ def pop_up
$list.see 'end'
end
$top.deiconify
- Tk.after 2000, proc{$top.withdraw}
+ Tk.after 2000, proc{$top.iconify}
end
$list.insert 'end', "You have no mail."
check
-Tk.after 2000, proc{$top.withdraw}
+Tk.after 2000, proc{$top.iconify}
begin
Tk.mainloop
rescue
diff --git a/ext/tk/sample/tkdialog.rb b/ext/tk/sample/tkdialog.rb
index e83e16d0a8..73708945fb 100644
--- a/ext/tk/sample/tkdialog.rb
+++ b/ext/tk/sample/tkdialog.rb
@@ -54,7 +54,6 @@ bot.pack
root.pack('side'=>'top', 'fill'=>'both', 'expand'=>'yes')
def quit(button)
- print "aaa\n"
print "You pressed the \"#{button}\" button; bye-bye!\n"
exit
end
diff --git a/ext/tk/sample/tkfrom.rb b/ext/tk/sample/tkfrom.rb
index 8c3efb8137..e0e7f4810f 100644
--- a/ext/tk/sample/tkfrom.rb
+++ b/ext/tk/sample/tkfrom.rb
@@ -54,9 +54,9 @@ if ARGV.length == 0
if ENV['MAIL']
ARGV[0] = ENV['MAIL']
elsif ENV['USER']
- ARGV[0] = '/usr/spool/mail/' + ENV['USER']
+ ARGV[0] = '/var/spool/mail/' + ENV['USER']
elsif ENV['LOGNAME']
- ARGV[0] = '/usr/spool/mail/' + ENV['LOGNAME']
+ ARGV[0] = '/var/spool/mail/' + ENV['LOGNAME']
end
end
@@ -64,7 +64,7 @@ require "tk"
list = scroll = nil
TkFrame.new{|f|
list = TkListbox.new(f) {
- yscroll proc{|idx|
+ yscroll proc{|*idx|
scroll.set *idx
}
relief 'raised'
@@ -94,7 +94,7 @@ root.bind "space", proc{exit}
$outcount = 0;
for file in ARGV
- next if File.exist?(file)
+ next unless File.exist?(file)
atime = File.atime(file)
mtime = File.mtime(file)
f = open(file, "r")
diff --git a/ext/tk/sample/tkmenubutton.rb b/ext/tk/sample/tkmenubutton.rb
new file mode 100644
index 0000000000..02a903ebb8
--- /dev/null
+++ b/ext/tk/sample/tkmenubutton.rb
@@ -0,0 +1,136 @@
+#!/usr/bin/env ruby
+#
+# menubutton sample : based on sample menubuttons on the Tcl/Tk demo script
+#
+require 'tk'
+
+TkLabel.new(:text=>'Sample of TkMenubutton').pack(:side=>:top)
+
+TkFrame.new{|f|
+ pack(:side=>:top)
+
+
+ TkMenubutton.new(:parent=>f, :text=>'Right', :underline=>0,
+ :direction=>:right, :relief=>:raised){|mb|
+ menu TkMenu.new(:parent=>mb, :tearoff=>0){
+ add(:command, :label=>'Right menu: first item',
+ :command=>proc{print 'You have selected the first item' +
+ " from the Right menu.\n"})
+ add(:command, :label=>'Right menu: second item',
+ :command=>proc{print 'You have selected the second item' +
+ " from the Right menu.\n"})
+ }
+ pack(:side=>:left, :padx=>25, :pady=>25)
+ }
+
+ TkMenubutton.new(:parent=>f, :text=>'Below', :underline=>0,
+ :direction=>:below, :relief=>:raised){|mb|
+ menu(TkMenu.new(:parent=>mb, :tearoff=>0){
+ add(:command, :label=>'Below menu: first item',
+ :command=>proc{print 'You have selected the first item' +
+ " from the Below menu.\n"})
+ add(:command, :label=>'Below menu: second item',
+ :command=>proc{print 'You have selected the second item' +
+ " from the Below menu.\n"})
+ })
+ pack(:side=>:left, :padx=>25, :pady=>25)
+ }
+
+ TkMenubutton.new(:parent=>f, :text=>'Above', :underline=>0,
+ :direction=>:above, :relief=>:raised){|mb|
+ menu TkMenu.new(:parent=>mb, :tearoff=>0){
+ add(:command, :label=>'Above menu: first item',
+ :command=>proc{print 'You have selected the first item' +
+ " from the Above menu.\n"})
+ add(:command, :label=>'Above menu: second item',
+ :command=>proc{print 'You have selected the second item' +
+ " from the Above menu.\n"})
+ }
+ pack(:side=>:left, :padx=>25, :pady=>25)
+ }
+
+ TkMenubutton.new(:parent=>f, :text=>'Left', :underline=>0,
+ :direction=>:left, :relief=>:raised){|mb|
+ menu(TkMenu.new(:parent=>mb, :tearoff=>0){
+ add(:command, :label=>'Left menu: first item',
+ :command=>proc{print 'You have selected the first item' +
+ " from the Left menu.\n"})
+ add(:command, :label=>'Left menu: second item',
+ :command=>proc{print 'You have selected the second item' +
+ " from the Left menu.\n"})
+ })
+ pack(:side=>:left, :padx=>25, :pady=>25)
+ }
+}
+
+############################
+TkFrame.new(:borderwidth=>2, :relief=>:sunken,
+ :height=>5).pack(:side=>:top, :fill=>:x, :padx=>20)
+############################
+
+TkLabel.new(:text=>'Sample of TkOptionMenu').pack(:side=>:top)
+
+colors = %w(Black red4 DarkGreen NavyBlue gray75 Red Green Blue gray50
+ Yellow Cyan Magenta White Brown DarkSeaGreen DarkViolet)
+
+TkFrame.new{|f|
+ pack(:side=>:top)
+
+ b1 = TkOptionMenubutton .
+ new(:parent=>f, :values=>%w(one two three)) .
+ pack(:side=>:left, :padx=>25, :pady=>25)
+
+ b2 = TkOptionMenubutton.new(:parent=>f, :values=>colors) {|optMB|
+ colors.each{|color|
+ no_sel = TkPhotoImage.new(:height=>16, :width=>16){
+ put 'gray50', *[ 0, 0, 16, 1]
+ put 'gray50', *[ 0, 1, 1, 16]
+ put 'gray75', *[ 0, 15, 16, 16]
+ put 'gray75', *[15, 1, 16, 16]
+ put color, *[ 1, 1, 15, 15]
+ }
+ sel = TkPhotoImage.new(:height=>16, :width=>16){
+ put 'Black', *[ 0, 0, 16, 2]
+ put 'Black', *[ 0, 2, 2, 16]
+ put 'Black', *[ 2, 14, 16, 16]
+ put 'Black', *[14, 2, 16, 14]
+ put color, *[ 2, 2, 14, 14]
+ }
+ optMB.entryconfigure(color, :hidemargin=>1,
+ :image=>no_sel, :selectimage=>sel)
+ }
+ optMB.menuconfigure(:tearoff, 1)
+ %w(Black gray75 gray50 White).each{|color|
+ optMB.entryconfigure(color, :columnbreak=>true)
+ }
+ pack(:side=>:left, :padx=>25, :pady=>25)
+ }
+
+ TkButton.new(:parent=>f){
+ text 'show values'
+ command proc{p [b1.value, b2.value]}
+ pack(:side=>:left, :padx=>25, :pady=>5, :anchor=>:s)
+ }
+}
+
+
+############################
+TkFrame.new(:borderwidth=>2, :relief=>:sunken,
+ :height=>5).pack(:side=>:top, :fill=>:x, :padx=>20)
+############################
+
+root = TkRoot.new(:title=>'menubutton samples')
+
+TkButton.new(root, :text=>'exit', :command=>proc{exit}){
+ pack(:side=>:top, :padx=>25, :pady=>5, :anchor=>:e)
+}
+
+# VirtualEvent <<MenuSelect>> on Tcl/Tk ==> '<MenuSelect>' on Ruby/Tk
+# ( remove the most external <, > for Ruby/Tk notation )
+TkMenu.bind('<MenuSelect>', proc{|widget|
+ p widget.entrycget('active', :label)
+ }, '%W')
+
+############################
+
+Tk.mainloop
diff --git a/ext/tk/sample/tkoptdb-safeTk.rb b/ext/tk/sample/tkoptdb-safeTk.rb
new file mode 100644
index 0000000000..169cd3c171
--- /dev/null
+++ b/ext/tk/sample/tkoptdb-safeTk.rb
@@ -0,0 +1,18 @@
+#!/usr/bin/env ruby
+
+require 'multi-tk'
+
+TkMessage.new(:text => <<EOM).pack
+This is a sample of the safe-Tk slave interpreter. \
+On the slave interpreter, 'tkoptdb.rb' demo is running.
+The window shown this message is a root widget of \
+the default master interpreter. The other window \
+is a toplevel widget of the master interpreter, and it \
+has a container frame of the safe-Tk slave interpreter. \
+You can delete the slave by the button on the toplevel widget.
+EOM
+
+file = File.expand_path('tkoptdb.rb', File.dirname(__FILE__))
+MultiTkIp.new_safeTk{load file}
+
+# mainloop is started on 'tkoptdb.rb'
diff --git a/ext/tk/sample/tkoptdb.rb b/ext/tk/sample/tkoptdb.rb
new file mode 100644
index 0000000000..6cb3d17993
--- /dev/null
+++ b/ext/tk/sample/tkoptdb.rb
@@ -0,0 +1,84 @@
+#!/usr/bin/env ruby
+#
+# sample script of TkOptionDB
+#
+# If 'LANG' environment variable's value is started by 'ja',
+# then read Japanese resource data and display Japanese button text.
+# In other case, read English resource data and display English text.
+#
+require "tk"
+
+if ENV['LANG'] =~ /^ja/
+ # read Japanese resource
+ TkOptionDB.read_with_encoding(File.expand_path('resource.ja',
+ File.dirname(__FILE__)),
+ 'euc-jp')
+else
+ # read English resource
+ TkOptionDB.readfile(File.expand_path('resource.en', File.dirname(__FILE__)))
+end
+
+# 'show_msg' and 'bye_msg' procedures can be defined on BTN_CMD resource.
+# Those procedures are called under $SAFE==2
+cmd = TkOptionDB.new_proc_class(:BTN_CMD, [:show_msg, :bye_msg], 2) {
+ # If you want to check resource string (str),
+ # please define __check_proc_string__(str) like this.
+ class << self
+ def __check_proc_string__(str)
+ print "($SAFE=#{$SAFE}) check!! str.tainted?::#{str.tainted?}"
+ str.untaint
+ print "==>#{str.tainted?} : "
+ str
+ end
+ end
+}
+
+# following two frame widgets use same database entry
+TkFrame.new(:class=>'BtnFrame'){|f|
+ pack(:padx=>5, :pady=>5)
+ TkButton.new(:parent=>f, :widgetname=>'hello'){
+ command proc{
+ print "($SAFE=#{$SAFE}) : "
+ cmd.show_msg(TkOptionDB.inspect)
+ }
+ pack(:fill=>:x, :padx=>10, :pady=>10)
+ }
+ TkButton.new(:command=>proc{print "($SAFE=#{$SAFE}) : "; cmd.bye_msg; exit},
+ :parent=>f, :widgetname=>'quit'){
+ pack(:fill=>:x, :padx=>10, :pady=>10)
+ }
+}
+
+class BtnFrame < TkFrame; end
+BtnFrame.new{|f|
+ pack(:padx=>5, :pady=>5)
+ TkButton.new(:parent=>f, :widgetname=>'hello'){
+ command proc{
+ print "($SAFE=#{$SAFE}) : "
+ cmd.show_msg(TkOptionDB.inspect)
+ }
+ pack(:fill=>:x, :padx=>10, :pady=>10)
+ }
+ TkButton.new(:command=>proc{print "($SAFE=#{$SAFE}) : "; cmd.bye_msg; exit},
+ :parent=>f, :widgetname=>'quit'){
+ pack(:fill=>:x, :padx=>10, :pady=>10)
+ }
+}
+
+# if unknown class, use default option values
+TkFrame.new(:class=>'BtnFrame2'){|f|
+ pack(:padx=>5, :pady=>5)
+ TkButton.new(:parent=>f, :widgetname=>'hello'){
+ command proc{
+ print "($SAFE=#{$SAFE}) : "
+ cmd.show_msg(TkOptionDB.inspect)
+ }
+ pack(:fill=>:x, :padx=>10, :pady=>10)
+ }
+ TkButton.new(:command=>proc{print "($SAFE=#{$SAFE}) : "; cmd.bye_msg; exit},
+ :parent=>f, :widgetname=>'quit'){
+ pack(:fill=>:x, :padx=>10, :pady=>10)
+ }
+}
+
+Tk.mainloop
diff --git a/ext/tk/sample/tktimer2.rb b/ext/tk/sample/tktimer2.rb
new file mode 100644
index 0000000000..dd31bb098e
--- /dev/null
+++ b/ext/tk/sample/tktimer2.rb
@@ -0,0 +1,47 @@
+#!/usr/bin/env ruby
+# This script is a re-implementation of tktimer.rb with TkTimer(TkAfter) class.
+
+require "tk"
+
+# new notation :
+# * symbols are acceptable as keys or values of the option hash
+# * the parent widget can be given by :parent key on the option hash
+root = TkRoot.new(:title=>'timer sample')
+label = TkLabel.new(:parent=>root, :relief=>:raised, :width=>10) \
+ .pack(:side=>:bottom, :fill=>:both)
+
+# define the procedure repeated by the TkTimer object
+tick = proc{|aobj| #<== TkTimer object
+ cnt = aobj.return_value + 5 # return_value keeps a result of the last proc
+ label.text format("%d.%02d", *(cnt.divmod(100)))
+ cnt #==> return value is kept by TkTimer object
+ # (so, can be send to the next repeat-proc)
+}
+
+timer = TkTimer.new(50, -1, tick).start(0, proc{ label.text('0.00'); 0 })
+ # ==> repeat-interval : (about) 50 ms,
+ # repeat : infinite (-1) times,
+ # repeat-procedure : tick (only one, in this case)
+ #
+ # ==> wait-before-call-init-proc : 0 ms,
+ # init_proc : proc{ label.text('0.00'); 0 }
+ #
+ # (0ms)-> init_proc ->(50ms)-> tick ->(50ms)-> tick ->....
+
+TkButton.new(:text=>'Start') {
+ command proc{ timer.continue unless timer.running? }
+ pack(:side=>:left, :fill=>:both, :expand=>true)
+}
+TkButton.new(:text=>'Restart') {
+ command proc{ timer.restart(0, proc{ label.text('0.00'); 0 }) }
+ pack(:side=>:left, :fill=>:both, :expand=>true)
+}
+TkButton.new(:text=>'Stop') {
+ command proc{ timer.stop if timer.running? }
+ pack('side'=>'right','fill'=>'both','expand'=>'yes')
+}
+
+ev_quit = TkVirtualEvent.new('Control-c', 'Control-q')
+Tk.root.bind(ev_quit, proc{Tk.exit}).focus
+
+Tk.mainloop
diff --git a/ext/win32ole/tests/testOLEMETHOD.rb b/ext/win32ole/tests/testOLEMETHOD.rb
index 4f65ec96b4..b52052e669 100644
--- a/ext/win32ole/tests/testOLEMETHOD.rb
+++ b/ext/win32ole/tests/testOLEMETHOD.rb
@@ -22,6 +22,10 @@ class TestOLEMETHOD < RUNIT::TestCase
m = WIN32OLE_METHOD.new(@excel_app, 'Quit')
assert_equal('Quit', m.name)
end
+ def test_to_s
+ m = WIN32OLE_METHOD.new(@excel_app, 'Quit')
+ assert_equal('Quit', "#{m}")
+ end
def test_return_type
m = WIN32OLE_METHOD.new(@excel_app, 'ActiveCell')
assert_equal('Range', m.return_type)
diff --git a/ext/win32ole/tests/testOLEPARAM.rb b/ext/win32ole/tests/testOLEPARAM.rb
index 62fd2a1890..685b548aa5 100644
--- a/ext/win32ole/tests/testOLEPARAM.rb
+++ b/ext/win32ole/tests/testOLEPARAM.rb
@@ -15,6 +15,13 @@ class TestOLEPARAM < RUNIT::TestCase
assert(param_names.size > 0)
assert(param_names.include?('Filename'))
end
+ def test_to_s
+ classes = WIN32OLE_TYPE.ole_classes(MS_EXCEL_TYPELIB)
+ sh = classes.find {|c| c.name == 'Worksheet'}
+ saveas = sh.ole_methods.find {|m| m.name == 'SaveAs'}
+ param_names = saveas.params.collect{|p| "#{p}"}
+ assert(param_names.include?('Filename'))
+ end
def test_ole_type
classes = WIN32OLE_TYPE.ole_classes(MS_EXCEL_TYPELIB)
methods = classes.find {|c| c.name == 'Worksheet'}.ole_methods
diff --git a/ext/win32ole/tests/testOLETYPE.rb b/ext/win32ole/tests/testOLETYPE.rb
index 9840aac940..d4eb1146e1 100644
--- a/ext/win32ole/tests/testOLETYPE.rb
+++ b/ext/win32ole/tests/testOLETYPE.rb
@@ -31,6 +31,15 @@ class TestOLETYPE < RUNIT::TestCase
}
assert(class_names.include?('Application'))
end
+
+ def test_class_to_s
+ classes = WIN32OLE_TYPE.ole_classes(MS_EXCEL_TYPELIB)
+ class_names = classes.collect{|c|
+ "#{c}"
+ }
+ assert(class_names.include?('Application'))
+ end
+
def test_ole_type
classes = WIN32OLE_TYPE.ole_classes(MS_EXCEL_TYPELIB)
app = classes.find {|c| c.name == 'Application'}
@@ -80,4 +89,8 @@ class TestOLETYPE < RUNIT::TestCase
worksheet = classes.find {|c| c.name == 'Worksheet'}
assert_equal(131088, worksheet.helpcontext)
end
+ def test_to_s
+ type = WIN32OLE_TYPE.new(MS_EXCEL_TYPELIB, 'Application')
+ assert_equal("Application", "#{type}");
+ end
end
diff --git a/ext/win32ole/tests/testOLEVARIABLE.rb b/ext/win32ole/tests/testOLEVARIABLE.rb
index b237d9b616..b4bb0b57d9 100644
--- a/ext/win32ole/tests/testOLEVARIABLE.rb
+++ b/ext/win32ole/tests/testOLEVARIABLE.rb
@@ -14,6 +14,13 @@ class TestOLEVARIABLE < RUNIT::TestCase
assert(var_names.size > 0)
assert(var_names.include?('xl3DColumn'))
end
+ def test_to_s
+ classes = WIN32OLE_TYPE.ole_classes(MS_EXCEL_TYPELIB)
+ chart = classes.find {|c| c.name == 'XlChartType'}
+ var_names = chart.variables.collect {|m| "#{m}"}
+ assert(var_names.size > 0)
+ assert(var_names.include?('xl3DColumn'))
+ end
def test_ole_type
classes = WIN32OLE_TYPE.ole_classes(MS_EXCEL_TYPELIB)
chart = classes.find {|c| c.name == 'XlChartType'}
diff --git a/ext/win32ole/tests/testall.rb b/ext/win32ole/tests/testall.rb
index ecc94e33ea..17bae2c810 100644
--- a/ext/win32ole/tests/testall.rb
+++ b/ext/win32ole/tests/testall.rb
@@ -1,11 +1,11 @@
require 'rubyunit'
require 'win32ole'
puts "Now Test Win32OLE version #{WIN32OLE::VERSION}"
-RUNIT::CUI::TestRunner.quiet_mode = true
+# RUNIT::CUI::TestRunner.quiet_mode = true
require "testWIN32OLE"
require "testOLETYPE"
require "testOLEPARAM"
require "testOLEMETHOD"
require "testOLEVARIABLE"
require "testVARIANT"
-require "testOLEEVENT"
+# require "testOLEEVENT"
diff --git a/ext/win32ole/win32ole.c b/ext/win32ole/win32ole.c
index 29be931534..2429663a5f 100644
--- a/ext/win32ole/win32ole.c
+++ b/ext/win32ole/win32ole.c
@@ -79,7 +79,7 @@
#define WC2VSTR(x) ole_wc2vstr((x), TRUE)
-#define WIN32OLE_VERSION "0.5.2"
+#define WIN32OLE_VERSION "0.5.4"
typedef HRESULT (STDAPICALLTYPE FNCOCREATEINSTANCEEX)
(REFCLSID, IUnknown*, DWORD, COSERVERINFO*, DWORD, MULTI_QI*);
@@ -715,6 +715,7 @@ ole_set_member(self, dispatch)
return self;
}
+static VALUE fole_s_allocate _((VALUE));
static VALUE
fole_s_allocate(klass)
VALUE klass;
@@ -1322,10 +1323,15 @@ fole_s_connect(argc, argv, self)
IDispatch *pDispatch;
IUnknown *pUnknown;
+ rb_secure(4);
/* initialize to use OLE */
ole_initialize();
rb_scan_args(argc, argv, "1*", &svr_name, &others);
+ if (ruby_safe_level > 0 && OBJ_TAINTED(svr_name)) {
+ rb_raise(rb_eSecurityError, "Insecure Object Connection - %s",
+ StringValuePtr(svr_name));
+ }
/* get CLSID from OLE server name */
pBuf = ole_mb2wc(StringValuePtr(svr_name), -1);
@@ -1380,6 +1386,7 @@ fole_s_const_load(argc, argv, self)
VALUE file;
LCID lcid = LOCALE_SYSTEM_DEFAULT;
+ rb_secure(4);
rb_scan_args(argc, argv, "11", &ole, &klass);
if (TYPE(klass) != T_CLASS &&
TYPE(klass) != T_MODULE &&
@@ -1444,6 +1451,7 @@ ole_classes_from_typelib(pTypeLib, classes)
ITypeInfo *pTypeInfo;
VALUE type;
+ rb_secure(4);
count = pTypeLib->lpVtbl->GetTypeInfoCount(pTypeLib);
for (i = 0; i < count; i++) {
hr = pTypeLib->lpVtbl->GetDocumentation(pTypeLib, i,
@@ -1595,11 +1603,21 @@ fole_initialize(argc, argv, self)
OLECHAR *pBuf;
IDispatch *pDispatch;
+ rb_secure(4);
rb_call_super(0, 0);
rb_scan_args(argc, argv, "11*", &svr_name, &host, &others);
- if (!NIL_P(host))
+ if (ruby_safe_level > 0 && OBJ_TAINTED(svr_name)) {
+ rb_raise(rb_eSecurityError, "Insecure Object Creation - %s",
+ StringValuePtr(svr_name));
+ }
+ if (!NIL_P(host)) {
+ if (ruby_safe_level > 0 && OBJ_TAINTED(host)) {
+ rb_raise(rb_eSecurityError, "Insecure Object Creation - %s",
+ StringValuePtr(svr_name));
+ }
return ole_create_dcom(argc, argv, self);
+ }
/* get CLSID from OLE server name */
pBuf = ole_mb2wc(StringValuePtr(svr_name), -1);
@@ -2202,6 +2220,7 @@ fole_free(self)
VALUE self;
{
struct oledata *pole;
+ rb_secure(4);
OLEData_Get_Struct(self, pole);
OLE_FREE(pole->pDispatch);
pole->pDispatch = NULL;
@@ -2875,6 +2894,7 @@ foletype_s_ole_classes(self, typelib)
ITypeLib *pTypeLib;
HRESULT hr;
+ rb_secure(4);
classes = rb_ary_new();
if(TYPE(typelib) == T_STRING) {
file = typelib_file(typelib);
@@ -3170,6 +3190,7 @@ ole_type_guid(pTypeInfo)
*/
static VALUE
foletype_guid(self)
+ VALUE self;
{
struct oletypedata *ptype;
Data_Get_Struct(self, struct oletypedata, ptype);
@@ -3201,6 +3222,7 @@ ole_type_progid(pTypeInfo)
*/
static VALUE
foletype_progid(self)
+ VALUE self;
{
struct oletypedata *ptype;
Data_Get_Struct(self, struct oletypedata, ptype);
@@ -5071,6 +5093,7 @@ ole_event_free(poleev)
}
}
+static VALUE fev_s_allocate _((VALUE));
static VALUE
fev_s_allocate(klass)
VALUE klass;
@@ -5101,6 +5124,7 @@ fev_initialize(argc, argv, self)
DWORD dwCookie;
struct oleeventdata *poleev;
+ rb_secure(4);
rb_scan_args(argc, argv, "11", &ole, &itf);
if (!rb_obj_is_kind_of(ole, cWIN32OLE)) {
@@ -5108,6 +5132,10 @@ fev_initialize(argc, argv, self)
}
if(TYPE(itf) != T_NIL) {
+ if (ruby_safe_level > 0 && OBJ_TAINTED(itf)) {
+ rb_raise(rb_eSecurityError, "Insecure Event Creation - %s",
+ StringValuePtr(itf));
+ }
Check_SafeStr(itf);
pitf = StringValuePtr(itf);
hr = find_iid(ole, pitf, &iid, &pTypeInfo);
@@ -5198,7 +5226,7 @@ ev_on_event(argc, argv, self, is_ary_arg)
if(!NIL_P(event)) {
Check_SafeStr(event);
}
- data = rb_ary_new3(4, rb_f_lambda(), event, args, is_ary_arg);
+ data = rb_ary_new3(4, rb_block_proc(), event, args, is_ary_arg);
add_event_call_back(self, data);
return Qnil;
}
@@ -5244,10 +5272,9 @@ Init_win32ole()
cWIN32OLE = rb_define_class("WIN32OLE", rb_cObject);
- rb_define_singleton_method(cWIN32OLE, "allocate", fole_s_allocate, 0);
+ rb_define_alloc_func(cWIN32OLE, fole_s_allocate);
rb_define_method(cWIN32OLE, "initialize", fole_initialize, -1);
- rb_enable_super(cWIN32OLE, "initialize");
rb_define_singleton_method(cWIN32OLE, "connect", fole_s_connect, -1);
rb_define_singleton_method(cWIN32OLE, "const_load", fole_s_const_load, -1);
@@ -5314,14 +5341,14 @@ Init_win32ole()
rb_define_singleton_method(cWIN32OLE_TYPE, "ole_classes", foletype_s_ole_classes, 1);
rb_define_singleton_method(cWIN32OLE_TYPE, "typelibs", foletype_s_typelibs, 0);
rb_define_singleton_method(cWIN32OLE_TYPE, "progids", foletype_s_progids, 0);
- rb_define_singleton_method(cWIN32OLE_TYPE, "allocate", foletype_s_allocate, 0);
+ rb_define_alloc_func(cWIN32OLE_TYPE, foletype_s_allocate);
rb_define_method(cWIN32OLE_TYPE, "initialize", foletype_initialize, 2);
- rb_enable_super(cWIN32OLE_TYPE, "initialize");
rb_define_method(cWIN32OLE_TYPE, "name", foletype_name, 0);
rb_define_method(cWIN32OLE_TYPE, "ole_type", foletype_ole_type, 0);
rb_define_method(cWIN32OLE_TYPE, "guid", foletype_guid, 0);
rb_define_method(cWIN32OLE_TYPE, "progid", foletype_progid, 0);
rb_define_method(cWIN32OLE_TYPE, "visible?", foletype_visible, 0);
+ rb_define_alias(cWIN32OLE_TYPE, "to_s", "name");
rb_define_method(cWIN32OLE_TYPE, "major_version", foletype_major_version, 0);
rb_define_method(cWIN32OLE_TYPE, "minor_version", foletype_minor_version, 0);
@@ -5341,11 +5368,11 @@ Init_win32ole()
rb_define_method(cWIN32OLE_VARIABLE, "visible?", folevariable_visible, 0);
rb_define_method(cWIN32OLE_VARIABLE, "variable_kind", folevariable_variable_kind, 0);
rb_define_method(cWIN32OLE_VARIABLE, "varkind", folevariable_varkind, 0);
+ rb_define_alias(cWIN32OLE_VARIABLE, "to_s", "name");
cWIN32OLE_METHOD = rb_define_class("WIN32OLE_METHOD", rb_cObject);
- rb_define_singleton_method(cWIN32OLE_METHOD, "allocate", folemethod_s_allocate, 0);
+ rb_define_alloc_func(cWIN32OLE_METHOD, folemethod_s_allocate);
rb_define_method(cWIN32OLE_METHOD, "initialize", folemethod_initialize, 2);
- rb_enable_super(cWIN32OLE_METHOD, "initialize");
rb_define_method(cWIN32OLE_METHOD, "name", folemethod_name, 0);
rb_define_method(cWIN32OLE_METHOD, "return_type", folemethod_return_type, 0);
@@ -5364,6 +5391,7 @@ Init_win32ole()
rb_define_method(cWIN32OLE_METHOD, "size_params", folemethod_size_params, 0);
rb_define_method(cWIN32OLE_METHOD, "size_opt_params", folemethod_size_opt_params, 0);
rb_define_method(cWIN32OLE_METHOD, "params", folemethod_params, 0);
+ rb_define_alias(cWIN32OLE_METHOD, "to_s", "name");
cWIN32OLE_PARAM = rb_define_class("WIN32OLE_PARAM", rb_cObject);
rb_define_method(cWIN32OLE_PARAM, "name", foleparam_name, 0);
@@ -5374,12 +5402,12 @@ Init_win32ole()
rb_define_method(cWIN32OLE_PARAM, "optional?", foleparam_optional, 0);
rb_define_method(cWIN32OLE_PARAM, "retval?", foleparam_retval, 0);
rb_define_method(cWIN32OLE_PARAM, "default", foleparam_default, 0);
+ rb_define_alias(cWIN32OLE_PARAM, "to_s", "name");
cWIN32OLE_EVENT = rb_define_class("WIN32OLE_EVENT", rb_cObject);
- rb_define_singleton_method(cWIN32OLE_EVENT, "allocate", fev_s_allocate, 0);
+ rb_define_alloc_func(cWIN32OLE_EVENT, fev_s_allocate);
rb_define_method(cWIN32OLE_EVENT, "initialize", fev_initialize, -1);
- rb_enable_super(cWIN32OLE_EVENT, "initialize");
rb_define_singleton_method(cWIN32OLE_EVENT, "message_loop", fev_s_msg_loop, 0);
rb_define_method(cWIN32OLE_EVENT, "on_event", fev_on_event, -1);
diff --git a/ext/zlib/.cvsignore b/ext/zlib/.cvsignore
new file mode 100644
index 0000000000..4088712231
--- /dev/null
+++ b/ext/zlib/.cvsignore
@@ -0,0 +1,3 @@
+Makefile
+mkmf.log
+*.def
diff --git a/ext/zlib/MANIFEST b/ext/zlib/MANIFEST
new file mode 100644
index 0000000000..746275d5c8
--- /dev/null
+++ b/ext/zlib/MANIFEST
@@ -0,0 +1,4 @@
+MANIFEST
+extconf.rb
+zlib.c
+doc/zlib.rd
diff --git a/ext/zlib/doc/zlib.rd b/ext/zlib/doc/zlib.rd
new file mode 100644
index 0000000000..6adff7f89c
--- /dev/null
+++ b/ext/zlib/doc/zlib.rd
@@ -0,0 +1,911 @@
+=begin
+#
+# zlib.rd.src
+#
+# Copyright (C) UENO Katsuhiro 2000-2003
+#
+# $Id$
+#
+
+= Ruby/zlib version 0.6.0
+
+Ruby/zlib is an extension library to use zlib from Ruby.
+Ruby/zlib also provides the features for accessing gzipped files.
+
+You can modify or redistribute Ruby/zlib in the same manner of
+Ruby interpreter. The latest version of Ruby/zlib would be found
+at ((<URL:http://www.blue.sky.or.jp/>)).
+
+Any comments and suggestions are always welcome. Please send
+them to ruby-list ML, ruby-ext ML, ruby-talk ML, or the author's
+mail address ((<URL:mailto:katsu@blue.sky.or.jp>)).
+
+This document is experimental and broken English version.
+If you find some mistakes or strange expressions (including
+kidding or unnatural ones) in this document, please let me know
+for my study.
+
+* ((<Zlib>))
+
+ * ((<Zlib::Error>))
+ * ((<Zlib::ZStream>))
+ * ((<Zlib::Deflate>))
+ * ((<Zlib::Inflate>))
+ * ((<Zlib::GzipFile>))
+ * ((<Zlib::GzipFile::Error>))
+ * ((<Zlib::GzipWriter>))
+ * ((<Zlib::GzipReader>))
+
+* ((<Changes from 0.5 to 0.6>))
+* ((<Changes from 0.4 to 0.5>))
+
+== Zlib
+
+Zlib is the module which provides the other features in zlib C
+library. See zlib.h for detail of each module function.
+
+=== Module Functions:
+
+--- Zlib.zlib_version
+
+ Returns the string which represents the version of zlib
+ library.
+
+--- Zlib.adler32([string[, adler]])
+
+ Calculates Alder-32 checksum for ((|string|)),
+ and returns updated value of ((|alder|)).
+ If ((|string|)) is omitted, it returns the Adler-32 initial
+ value. If ((|alder|)) is omitted, it assumes that the initial
+ value is given to ((|alder|)).
+
+--- Zlib.crc32([string[, crc]])
+
+ Calculates CRC checksum for ((|string|)), and returns
+ updated value of ((|crc|)). If ((|string|)) is omitted,
+ it returns the CRC initial value. ((|crc|)) is omitted,
+ it assumes that the initial value is given to ((|crc|)).
+
+--- Zlib.crc_table
+
+ Returns the table for calculating CRC checksum as an array.
+
+=== Constants:
+
+--- Zlib::VERSION
+
+ The Ruby/zlib version string.
+
+--- Zlib::ZLIB_VERSION
+
+ The string which represents the version of zlib.h.
+
+--- Zlib::BINARY
+--- Zlib::ASCII
+--- Zlib::UNKNOWN
+
+ The integers representing data types which
+ ((<Zlib::ZStream#data_type>)) method returns.
+
+--- Zlib::NO_COMPRESSION
+--- Zlib::BEST_SPEED
+--- Zlib::BEST_COMPRESSION
+--- Zlib::DEFAULT_COMPRESSION
+
+ The integers representing compression levels which are
+ an argument for ((<Zlib::Deflate.new>)),
+ ((<Zlib::Deflate#deflate>)), and so on.
+
+--- Zlib::FILTERED
+--- Zlib::HUFFMAN_ONLY
+--- Zlib::DEFAULT_STRATEGY
+
+ The integers representing compression methods which are
+ an argument for ((<Zlib::Deflate.new>)) and
+ ((<Zlib::Deflate#params>)).
+
+--- Zlib::DEF_MEM_LEVEL
+--- Zlib::MAX_MEM_LEVEL
+
+ The integers representing memory levels which are an
+ argument for ((<Zlib::Deflate.new>)),
+ ((<Zlib::Deflate#params>)), and so on.
+
+--- Zlib::MAX_WBITS
+
+ The default value of ((|windowBits|)) which is an argument for
+ ((<Zlib::Deflate.new>)) and ((<Zlib::Inflate.new>)).
+
+--- Zlib::NO_FLUSH
+--- Zlib::SYNC_FLUSH
+--- Zlib::FULL_FLUSH
+--- Zlib::FINISH
+
+ The integers to control the output of the deflate stream,
+ which are an argument for ((<Zlib::Deflate#deflate>)) and so on.
+
+--- Zlib::OS_CODE
+--- Zlib::OS_MSDOS
+--- Zlib::OS_AMIGA
+--- Zlib::OS_VMS
+--- Zlib::OS_UNIX
+--- Zlib::OS_VMCMS
+--- Zlib::OS_ATARI
+--- Zlib::OS_OS2
+--- Zlib::OS_MACOS
+--- Zlib::OS_ZSYSTEM
+--- Zlib::OS_CPM
+--- Zlib::OS_TOPS20
+--- Zlib::OS_WIN32
+--- Zlib::OS_QDOS
+--- Zlib::OS_RISCOS
+--- Zlib::OS_UNKNOWN
+
+ The return values of ((<Zlib::GzipFile#os_code>)) method.
+
+
+== Zlib::Error
+
+The superclass for all exceptions raised by Ruby/zlib.
+
+The following exceptions are defined as subclasses of Zlib::Error.
+These exceptions are raised when zlib library functions return
+with an error status.
+
+ * Zlib::StreamEnd
+ * Zlib::NeedDict
+ * Zlib::DataError
+ * Zlib::StreamError
+ * Zlib::MemError
+ * Zlib::BufError
+ * Zlib::VersionError
+
+=== SuperClass:
+
+* StandardError
+
+
+== Zlib::ZStream
+
+The abstract class for the stream which handles the compressed
+data. The operations are defined in the subclasses,
+((<Zlib::Deflate>)) for compression, and ((<Zlib::Inflate>))
+for decompression.
+
+An instance of Zlib::ZStream has one stream (struct zstream) and
+two variable-length buffers which associated to the input
+(next_in) of the stream and the output (next_out) of the stream.
+In this document, "input buffer" means the buffer for input, and
+"output buffer" means the buffer for output.
+
+Data inputed into an instance of Zlib::ZStream are temporally
+stored into the end of input buffer, and then data in input buffer
+are processed from the beginning of the buffer until no more
+output from the stream is produced (i.e. until avail_out > 0
+after processing). During processing, output buffer is allocated
+and expanded automatically to hold all output data.
+
+Some particular instance methods consume the data in output buffer
+and return them as a String.
+
+Here is an ascii art for describing above:
+
+ +================ an instance of Zlib::ZStream ================+
+ || ||
+ || +--------+ +-------+ +--------+ ||
+ || +--| output |<---------|zstream|<---------| input |<--+ ||
+ || | | buffer | next_out+-------+next_in | buffer | | ||
+ || | +--------+ +--------+ | ||
+ || | | ||
+ +===|======================================================|===+
+ | |
+ v |
+ "output data" "input data"
+
+If an error is occurred during processing input buffer,
+an exception which is a subclass of ((<Zlib::Error>)) is raised.
+At that time, both input and output buffer keeps their conditions
+at the time when the error is occurred.
+
+=== SuperClass:
+
+* Object
+
+=== Class Methods:
+
+--- Zlib::ZStream.new
+
+ See ((<Zlib::Deflate.new>)) and ((<Zlib::Inflate.new>)).
+
+=== Methods:
+
+--- Zlib::ZStream#avail_in
+
+ Returns bytes of data in input buffer.
+ Normally, returns 0.
+
+--- Zlib::ZStream#avail_out
+
+ Returns bytes of free spaces in output buffer.
+ Because the free spaces are allocated automatically,
+ this method returns 0 normally.
+
+--- Zlib::ZStream#avail_out = size
+
+ Allocates free spaces of ((|size|)) bytes in output buffer.
+ If there are more than ((|size|)) bytes spaces in the buffer,
+ the buffer is truncated.
+ Because the free spaces are allocated automatically,
+ you usually need not to use this method.
+
+--- Zlib::ZStream#flush_next_in
+
+ Flushes input buffer and returns all data in that buffer.
+
+--- Zlib::ZStream#flush_next_out
+
+ Flushes output buffer and returns all data in that buffer.
+
+--- Zlib::ZStream#total_in
+
+ Returns the total bytes of the input data to the stream.
+
+--- Zlib::ZStream#total_out
+
+ Returns the total bytes of the output data from the stream.
+
+--- Zlib::ZStream#data_type
+
+ Guesses the type of the data which have been inputed into
+ the stream. The returned value is either ((<Zlib::BINARY>)),
+ ((<Zlib::ASCII>)), or ((<Zlib::UNKNOWN>)).
+
+--- Zlib::ZStream#adler
+
+ Returns the alder-32 checksum.
+
+--- Zlib::ZStream#reset
+
+ Resets and initializes the stream. All data in both
+ input and output buffer are discarded.
+
+--- Zlib::ZStream#finish
+
+ Finishes the stream and flushes output buffer.
+ See ((<Zlib::Deflate#finish>)) and ((<Zlib::Inflate#finish>))
+ for detail of the behavior.
+
+--- Zlib::ZStream#finished?
+--- Zlib::ZStream#stream_end?
+
+ Returns true if the stream is finished.
+
+--- Zlib::ZStream#close
+--- Zlib::ZStream#end
+
+ Closes the stream. All operations on the closed stream
+ will raise an exception.
+
+--- Zlib::ZStream#closed?
+--- Zlib::ZStream#ended?
+
+ Returns true if the stream closed.
+
+
+== Zlib::Deflate
+
+The class for compressing string data.
+
+=== SuperClass:
+
+* ((<Zlib::ZStream>))
+
+=== Class Methods:
+
+--- Zlib::Deflate.deflate(string[, level])
+
+ Compresses ((|string|)). The avail values of ((|level|)) are
+ ((<Zlib::NO_COMPRESSION>)), ((<Zlib::BEST_SPEED>)),
+ ((<Zlib::BEST_COMPRESSION>)), ((<Zlib::DEFAULT_COMPRESSION>)),
+ and the integer from 0 to 9.
+
+ This method is almost equivalent to the following code:
+
+ def deflate(string, level)
+ z = Zlib::Deflate.new(level)
+ dst = z.deflate(string, Zlib::FINISH)
+ z.close
+ dst
+ end
+
+--- Zlib::Deflate.new([level[, windowBits[, memlevel[, strategy]]]])
+
+ Creates a new deflate stream for compression.
+ See zlib.h for details of each argument.
+ If an argument is nil, the default value of that
+ argument is used.
+
+=== Methods:
+
+--- Zlib::Deflate#clone
+
+ Duplicates the deflate stream.
+
+--- Zlib::Deflate#deflate(string[, flush])
+
+ Inputs ((|string|)) into the deflate stream and returns
+ the output from the stream. Calling this method,
+ both input and output buffer of the stream are flushed.
+ If ((|string|)) is nil, this method finishes the stream,
+ just like ((<Zlib::ZStream#finish>)).
+ The value of ((|flush|)) should be either ((<Zlib::NO_FLUSH>)),
+ ((<Zlib::SYNC_FLUSH>)), ((<Zlib::FULL_FLUSH>)), or
+ ((<Zlib::FINISH>)).
+ See zlib.h for details.
+
+--- Zlib::Deflate#<< string
+
+ Inputs ((|string|)) into the deflate stream just like
+ ((<Zlib::Deflate#deflate>)), but returns Zlib::Deflate object
+ itself. The output from the stream is preserved in output
+ buffer.
+
+--- Zlib::Deflate#flush([flush])
+
+ This method is equivalent to (({deflate('', ((|flush|)))})).
+ If ((|flush|)) is omitted, ((<Zlib::SYNC_FLUSH>)) is used
+ as ((|flush|)). This method is just provided for
+ readability of your Ruby script.
+
+--- Zlib::Deflate#finish
+
+ Finishes the stream. This method is equivalent to
+ (({deflate('', Zlib::FINISH)})).
+
+--- Zlib::Deflate#params(level, strategy)
+
+ Changes the parameters of the deflate stream.
+ See zlib.h for details. The output from the stream
+ by changing the params is preserved in output buffer.
+
+--- Zlib::Deflate#set_dictionary(string)
+
+ Sets the preset dictionary and returns ((|string|)).
+ This method is available just only after
+ ((<Zlib::Deflate.new>)) or ((<Zlib::ZStream#reset>)) method
+ was called. See zlib.h for details.
+
+
+== Zlib::Inflate
+
+The class for decompressing compressed data.
+Unlike ((<Zlib::Deflate>)), an instance of this class is not able
+to duplicate (clone, dup) itself.
+
+=== SuperClass:
+
+* ((<Zlib::ZStream>))
+
+=== Class Methods:
+
+--- Zlib::Inflate.inflate(string)
+
+ Decompresses ((|string|)). Raises a ((<Zlib::NeedDict>))
+ exception if a preset dictionary is needed for decompression.
+
+ This method is almost equivalent to the following code:
+
+ def inflate(string)
+ zstream = Zlib::Inflate.new
+ buf = zstream.inflate(string)
+ zstream.finish
+ zstream.close
+ buf
+ end
+
+--- Zlib::Inflate.new([windowBits])
+
+ Creates a new inflate stream for decompression.
+ See zlib.h for details of the argument.
+ If ((|windowBits|)) is nil, the default value is used.
+
+=== Methods:
+
+--- Zlib::Inflate#inflate(string)
+
+ Inputs ((|string|)) into the inflate stream and returns
+ the output from the stream. Calling this method,
+ both input and output buffer of the stream are flushed.
+ If ((|string|)) is nil, this method finishes the stream,
+ just like ((<Zlib::ZStream#finish>)).
+
+ Raises a ((<Zlib::NeedDict>)) exception if a preset
+ dictionary is needed to decompress. Set the dictionary
+ by ((<Zlib::Inflate#set_dictionary>)) and then call
+ this method again with an empty string.
+
+--- Zlib::Inflate#<< string
+
+ Inputs ((|string|)) into the inflate stream just like
+ ((<Zlib::Inflate#inflate>)), but returns Zlib::Inflate object
+ itself. The output from the stream is preserved in output
+ buffer.
+
+--- Zlib::Inflate#finish
+
+ Finishes the inflate stream and returns the garbage
+ following the compressed data. Raises an exception
+ if the stream is not finished
+ (i.e. ((<Zlib::ZStream#finished?>)) doesn't returns true).
+
+ The inflate stream finishes itself as soon as it meets
+ the end code of the compressed data, you need not to call
+ this method explicitly. However, this method is useful
+ for checking whether the data is correctly ended or not.
+
+--- Zlib::Inflate#set_dictionary(string)
+
+ Sets the preset dictionary and returns ((|string|))
+ This method is available just only after a ((<Zlib::NeedDict>))
+ exception was raised. See zlib.h for details.
+
+--- Zlib::Inflate#sync(string)
+
+ Inputs ((|string|)) into the end of input buffer and
+ skips data until a full flush point can be found.
+ If the point is found in the buffer, this method flushes
+ the buffer and returns false. Otherwise it returns true
+ and the following data of full flush point is preserved
+ in the buffer.
+
+--- Zlib::Inflate#sync_point?
+
+ What is this?
+
+
+== Zlib::GzipFile
+
+The abstruct class for handling a gzip formatted compressed file.
+The operations are defined in the subclasses,
+((<Zlib::GzipReader>)) for reading, and ((<Zlib::GzipWriter>))
+for writing.
+
+GzipReader should be used with associating an instance of IO class
+(or an object which has the same methods as IO has).
+
+=== SuperClass:
+
+* Object
+
+=== Class Methods:
+
+--- Zlib::GzipFile.new(args...)
+
+ See ((<Zlib::GzipReader.new>)) and ((<Zlib::GzipWriter.new>)).
+
+--- Zlib::GzipFile.wrap(args...) {|gz| ... }
+
+ See ((<Zlib::GzipReader.wrap>)) and ((<Zlib::GzipWriter.wrap>)).
+
+--- Zlib::GzipFile.open(args...) {|gz| ... }
+
+ See ((<Zlib::GzipReader.open>)) and ((<Zlib::GzipWriter.open>)).
+
+=== Methods:
+
+--- Zlib::GzipFile#closed?
+--- Zlib::GzipFile#to_io
+
+ Same as IO.
+
+--- Zlib::GzipFile#close
+
+ Closes the GzipFile object. This method calls close method
+ of the associated IO object. Returns the associated IO object.
+
+--- Zlib::GzipFile#finish
+
+ Closes the GzipFile object. Unlike ((<Zlib::GzipFile#close>)),
+ this method ((*never*)) calls close method of the associated IO
+ object. Returns the associated IO object.
+
+--- Zlib::GzipFile#crc
+
+ Returns CRC value of the uncompressed data.
+
+--- Zlib::GzipFile#level
+
+ Returns compression level.
+
+--- Zlib::GzipFile#mtime
+
+ Returns last modification time recorded in the gzip
+ file header.
+
+--- Zlib::GzipFile#os_code
+
+ Returns OS code number recorded in the gzip file header.
+
+--- Zlib::GzipFile#orig_name
+
+ Returns original filename recorded in the gzip file header,
+ or nil if original filename is not present.
+
+--- Zlib::GzipFile#comment
+
+ Returns comments recorded in the gzip file header, or
+ nil if the comments is not present.
+
+--- Zlib::GzipFile#sync
+--- Zlib::GzipFile#sync= flag
+
+ Same as IO. If ((|flag|)) is true, the associated IO object
+ must respond to flush method. While `sync' mode is true,
+ the compression ratio decreases sharply.
+
+
+== Zlib::GzipFile::Error
+
+The superclass for all exceptions raised during processing a gzip
+file.
+
+The following exceptions are defined as subclasses of
+Zlib::GzipFile::Error.
+
+: Zlib::GzipFile::NoFooter
+
+ Raised when gzip file footer has not found.
+
+: Zlib::GzipFile::CRCError
+
+ Raised when the CRC checksum recorded in gzip file footer
+ is not equivalent to CRC checksum of the actually
+ uncompressed data.
+
+: Zlib::GzipFile::LengthError
+
+ Raised when the data length recorded in gzip file footer
+ is not equivalent to length of the actually uncompressed data.
+
+=== SuperClass:
+
+* ((<Zlib::Error>))
+
+
+== Zlib::GzipReader
+
+The class for reading a gzipped file. GzipReader should be used
+with associating an instance of IO class (or an object which has
+the same methods as IO has).
+
+ Zlib::GzipReader.open('hoge.gz') {|gz|
+ print gz.read
+ }
+
+ f = File.open('hoge.gz')
+ gz = Zlib::GzipReader.new(f)
+ print gz.read
+ gz.close
+
+=== SuperClass:
+
+* ((<Zlib::GzipFile>))
+
+=== Included Modules:
+
+* Enumerable
+
+=== Class Methods:
+
+--- Zlib::GzipReader.new(io)
+
+ Creates a GzipReader object associated with ((|io|)).
+ The GzipReader object reads gzipped data from ((|io|)),
+ and parses/decompresses them. At least, ((|io|)) must have
+ read method that behaves same as read method in IO class.
+
+ If the gzip file header is incorrect, raises an
+ ((<Zlib::GzipFile::Error>)) exception.
+
+--- Zlib::GzipReader.wrap(io) {|gz| ... }
+
+ Creates a GzipReader object associated with ((|io|)), and
+ executes the block with the newly created GzipReader object,
+ just like File::open. The GzipReader object will be closed
+ automatically after executing the block. If you want to keep
+ the associated IO object opening, you may call
+ ((<Zlib::GzipFile#finish>)) method in the block.
+
+--- Zlib::GzipReader.open(filename)
+--- Zlib::GzipReader.open(filename) {|gz| ... }
+
+ Opens a file specified by ((|filename|)) as a gzipped file,
+ and returns a GzipReader object associated with that file.
+ Further details of this method are same as
+ ((<Zlib::GzipReader.new>)) and ((<ZLib::GzipReader.wrap>)).
+
+=== ¥á¥½¥Ã¥É:
+
+--- Zlib::GzipReader#eof
+--- Zlib::GzipReader#eof?
+
+ Returns true if the object reaches the end of compressed data.
+ Note that eof? does ((*not*)) return true when reaches the
+ end of ((*file*)).
+
+--- Zlib::GzipReader#pos
+--- Zlib::GzipReader#tell
+
+ Returns the total bytes of data decompressed until now.
+ Not that it does ((*not*)) the position of file pointer.
+
+--- Zlib::GzipReader#each([rs])
+--- Zlib::GzipReader#each_line([rs])
+--- Zlib::GzipReader#each_byte([rs])
+--- Zlib::GzipReader#gets([rs])
+--- Zlib::GzipReader#getc
+--- Zlib::GzipReader#lineno
+--- Zlib::GzipReader#lineno=
+--- Zlib::GzipReader#read([length])
+--- Zlib::GzipReader#readchar
+--- Zlib::GzipReader#readline([rs])
+--- Zlib::GzipReader#readlines([rs])
+--- Zlib::GzipReader#ungetc(char)
+
+ Same as IO, but raises ((<Zlib::Error>)) or
+ ((<Zlib::GzipFile::Error>)) exception if an error was found
+ in the gzip file.
+
+ Be careful of the footer of gzip file. A gzip file has
+ the checksum of pre-compressed data in its footer.
+ GzipReader checks all uncompressed data against that checksum
+ at the following cases, and if failed, raises
+ ((<Zlib::GzipFile::NoFooter>)), ((<Zlib::GzipFile::CRCError>)),
+ or ((<Zlib::GzipFile::LengthError>)) exception.
+
+ * When an reading request is received beyond the end of file
+ (the end of compressed data).
+ That is, when ((<Zlib::GzipReader#read>)),
+ ((<Zlib::GzipReader#gets>)), or some other methods for reading
+ returns nil.
+
+ * When ((<Zlib::GzipFile#close>)) method is called after
+ the object reaches the end of file.
+
+ * When ((<Zlib::GzipReader#unused>)) method is called after
+ the object reaches the end of file.
+
+--- Zlib::GzipReader#rewind
+
+ Resets the position of the file pointer to the point
+ created the GzipReader object.
+ The associated IO object need to respond to seek method.
+
+--- Zlib::GzipReader#unused
+
+ Returns the rest of the data which had read for parsing gzip
+ format, or nil if the whole gzip file is not parsed yet.
+
+
+== Zlib::GzipWriter
+
+The class for writing a gzipped file. GzipWriter should be used
+with associate with an instance of IO class (or an object which
+has the same methods as IO has).
+
+ Zlib::GzipWriter.open('hoge.gz') {|gz|
+ gz.write 'jugemu jugemu gokou no surikire...'
+ }
+
+ f = File.open('hoge.gz', 'w')
+ gz = Zlib::GzipWriter.new(f)
+ gz.write 'jugemu jugemu gokou no surikire...'
+ gz.close
+
+NOTE: Due to the limitation in finalizer of Ruby, you must close
+explicitly GzipWriter object by ((<Zlib::GzipWriter#close>)) etc.
+Otherwise, GzipWriter should be not able to write gzip footer and
+generate broken gzip file.
+
+=== SuperClass:
+
+* ((<Zlib::GzipFile>))
+
+=== Class Methods:
+
+--- Zlib::GzipWriter.new(io[, level[, strategy]])
+
+ Creates a GzipWriter object associated with ((|io|)).
+ ((|level|)) and ((|strategy|)) should be same as the
+ arguments of ((<Zlib::Deflate.new>)). The GzipWriter object
+ writes gzipped data to ((|io|)). At least, ((|io|)) must
+ respond to write method that behaves same as write method
+ in IO class.
+
+--- Zlib::GzipWriter.wrap(io[, level[, strategy]]) {|gz| ... }
+
+ Creates a GzipWriter object associated with ((|io|)), and
+ executes the block with the newly created GzipWriter object,
+ just like File::open. The GzipWriter object will be closed
+ automatically after executing the block. If you want to keep
+ the associated IO object opening, you may call
+ ((<Zlib::GzipFile#finish>)) method in the block.
+
+--- Zlib::GzipWriter.open(filename[, level[, strategy]])
+--- Zlib::GzipWriter.open(filename[, level[, strategy]]) {|gz| ... }
+
+ Opens a file specified by ((|filename|)) for writing
+ gzip compressed data, and returns a GzipWriter object
+ associated with that file. Further details of this method
+ are same as ((<Zlib::GzipWriter.new>)) and
+ ((<Zlib::GzipWriter#wrap>)).
+
+
+=== Methods:
+
+--- Zlib::GzipWriter#close
+--- Zlib::GzipWriter#finish
+
+ Closes the GzipFile object. This method calls close method
+ of the associated IO object. Returns the associated IO object.
+ See ((<Zlib::GzipFile#close>)) and ((<Zlib::GzipFile#finish>))
+ for the difference between close and finish.
+
+ ((*NOTE: Due to the limitation in finalizer of Ruby, you must
+ close GzipWriter object explicitly. Otherwise, GzipWriter
+ should be not able to write gzip footer and generate broken
+ gzip file.*))
+
+--- Zlib::GzipWriter#pos
+--- Zlib::GzipWriter#tell
+
+ Returns the total bytes of data compressed until now.
+ Note that it does ((*not*)) the position of file pointer.
+
+--- Zlib::GzipWriter#<< str
+--- Zlib::GzipWriter#putc(ch)
+--- Zlib::GzipWriter#puts(obj...)
+--- Zlib::GzipWriter#print(arg...)
+--- Zlib::GzipWriter#printf(format, arg...)
+--- Zlib::GzipWriter#write(str)
+
+ Same as IO.
+
+--- Zlib::GzipWriter#flush([flush])
+
+ Flushes all the internal buffers of the GzipWriter object.
+ The meaning of ((|flush|)) is same as one of the argument of
+ ((<Zlib::Deflate#deflate>)).
+ ((<Zlib::SYNC_FLUSH>)) is used if ((|flush|)) is omitted.
+ It is no use giving ((|flush|)) ((<Zlib::NO_FLUSH>)).
+
+--- Zlib::GzipWriter#mtime= time
+
+ Sets last modification time to be stored in the gzip file
+ header. ((<Zlib::GzipFile::Error>)) exception will be raised
+ if this method is called after writing method (like
+ ((<Zlib::GzipWriter#write>))) was called.
+
+--- Zlib::GzipWriter#orig_name= filename
+
+ Sets original filename to be stored in the gzip file header.
+ ((<Zlib::GzipFile::Error>)) exception will be raised
+ if this method is called after writing method (like
+ ((<Zlib::GzipWriter#write>))) was called.
+
+--- Zlib::GzipWriter#comment= string
+
+ Sets comments to be stored in the gzip file header.
+ ((<Zlib::GzipFile::Error>)) exception will be raised
+ if this method is called after writing method (like
+ ((<Zlib::GzipWriter#write>))) was called.
+
+
+== Changes from 0.5 to 0.6
+
+* New methods:
+
+ * ((<Zlib::GzipFile.wrap>))
+ * ((<Zlib::GzipFile#finish>))
+
+* New constants:
+
+ * ((<Zlib::ZLIB_VERSION>))
+ * ((<Zlib::OS_VMCMS>))
+ * ((<Zlib::OS_ZSYSTEM>))
+ * ((<Zlib::OS_CPM>))
+ * ((<Zlib::OS_QDOS>))
+ * ((<Zlib::OS_RISCOS>))
+ * ((<Zlib::OS_UNKNOWN>))
+
+* Changed methods:
+
+ * ((<Zlib::GzipFile.new>)) now takes no block. Use
+ ((<Zlib::GzipFile.wrap>)) instead.
+
+ * ((<Zlib::GzipFile#close>)) now takes no argument. Use
+ ((<Zlib::GzipFile#finish>)) instead.
+
+* Renamed methods:
+
+ * Zlib.version is renamed to ((<Zlib.zlib_version>)).
+
+* Changed constants:
+
+ * ((<Zlib::VERSION>)) indicates the version of Ruby/zlib.
+ The zlib.h version is now in ((<Zlib::ZLIB_VERSION>)).
+
+* Backward compatibility:
+
+ * For backward compatibility for 0.5, the obsoleted methods and
+ arguments are still available.
+
+ * Obsoleted classes, methods, and constants for backward
+ compatibility for 0.4 or earlier are removed.
+
+== Changes from 0.4 to 0.5
+
+Almost all the code are rewritten.
+I hope all changes are enumerated below :-)
+
+* The names of almost classes and some methods are changed.
+ All classes and constants are now defined under module
+ ((<Zlib>)). The obsoleted names are also available for backward
+ compatibility.
+
+ * Classes
+
+ * Deflate -> ((<Zlib::Deflate>))
+ * Inflate -> ((<Zlib::Inflate>))
+ * Zlib::Gzip -> ((<Zlib::GzipFile>))
+ * GzipReader -> ((<Zlib::GzipReader>))
+ * GzipWriter -> ((<Zlib::GzipWriter>))
+ * Zlib::Gzip::Error -> ((<Zlib::GzipFile::Error>))
+ * Zlib::GzipReader::NoFooter -> ((<Zlib::GzipFile::NoFooter>))
+ * Zlib::GzipReader::CRCError -> ((<Zlib::GzipFile::CRCError>))
+ * Zlib::GzipReader::LengthError -> ((<Zlib::GzipFile::LengthError>))
+
+ * Constants
+
+ * Zlib::ZStream::BINARY -> ((<Zlib::BINARY>))
+ * Zlib::ZStream::ASCII -> ((<Zlib::ASCII>))
+ * Zlib::ZStream::UNKNOWN -> ((<Zlib::UNKNOWN>))
+ * Zlib::Deflate::NO_COMPRESSION -> ((<Zlib::NO_COMPRESSION>))
+ * Zlib::Deflate::BEST_SPEED -> ((<Zlib::BEST_SPEED>))
+ * Zlib::Deflate::BEST_COMPRESSION -> ((<Zlib::BEST_COMPRESSION>))
+ * Zlib::Deflate::DEFAULT_COMPRESSION -> ((<Zlib::DEFAULT_COMPRESSION>))
+ * Zlib::Deflate::FILTERED -> ((<Zlib::FILTERED>))
+ * Zlib::Deflate::HUFFMAN_ONLY -> ((<Zlib::HUFFMAN_ONLY>))
+ * Zlib::Deflate::DEFAULT_STRATEGY -> ((<Zlib::DEFAULT_STRATEGY>))
+ * Zlib::Deflate::MAX_WBITS -> ((<Zlib::MAX_WBITS>))
+ * Zlib::Deflate::DEF_MEM_LEVEL -> ((<Zlib::DEF_MEM_LEVEL>))
+ * Zlib::Deflate::MAX_MEM_LEVEL -> ((<Zlib::MAX_MEM_LEVEL>))
+ * Zlib::Deflate::NO_FLUSH -> ((<Zlib::NO_FLUSH>))
+ * Zlib::Deflate::SYNC_FLUSH -> ((<Zlib::SYNC_FLUSH>))
+ * Zlib::Deflate::FULL_FLUSH -> ((<Zlib::FULL_FLUSH>))
+ * Zlib::Inflate::MAX_WBITS -> ((<Zlib::MAX_WBITS>))
+ * Zlib::GzipReader::OS_* -> ((<Zlib::OS_*|Zlib::OS_CODE>))
+
+ * Methods
+
+ * Zlib::ZStream#flush_out -> ((<Zlib::ZStream#flush_next_out>))
+
+* Made buffer for input (next_in).
+
+* ((<Zlib::GzipReader#unused>)) returns nil after closing.
+
+* Now you are up to call ((<Zlib::GzipWriter#close>)) explicitly
+to avoid segv in finalizer.
+((<[ruby-dev:11915]|URL:http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-dev/11915>))
+
+* divided initialize from new.
+
+* remove sanity checks for arguments for deflateInit2 and
+ inflateInit2.
+
+* adapted the behavior of ((<Zlib::GzipWriter#puts>)) to Ruby-1.7.
+
+* Made all functions static.
+
+
+=end
diff --git a/ext/zlib/extconf.rb b/ext/zlib/extconf.rb
new file mode 100644
index 0000000000..bf3349a65d
--- /dev/null
+++ b/ext/zlib/extconf.rb
@@ -0,0 +1,66 @@
+#
+# extconf.rb
+#
+# $Id$
+#
+
+require 'mkmf'
+require 'rbconfig'
+
+dir_config 'zlib'
+
+
+if %w'z libz zlib'.find {|z| have_library(z, 'deflateReset')} and
+ have_header('zlib.h') then
+
+ defines = []
+
+ message 'checking for kind of operating system... '
+ os_code = with_config('os-code') ||
+ case RUBY_PLATFORM.split('-',2)[1]
+ when 'amigaos' then
+ os_code = 'AMIGA'
+ when /\Aos2[\-_]emx\z/ then
+ os_code = 'OS2'
+ when 'mswin32', 'mingw32', 'bccwin32' then
+ # NOTE: cygwin should be regarded as Unix.
+ os_code = 'WIN32'
+ else
+ os_code = 'UNIX'
+ end
+ os_code = 'OS_' + os_code.upcase
+
+ OS_NAMES = {
+ 'OS_MSDOS' => 'MS-DOS',
+ 'OS_AMIGA' => 'Amiga',
+ 'OS_VMS' => 'VMS',
+ 'OS_UNIX' => 'Unix',
+ 'OS_ATARI' => 'Atari',
+ 'OS_OS2' => 'OS/2',
+ 'OS_MACOS' => 'MacOS',
+ 'OS_TOPS20' => 'TOPS20',
+ 'OS_WIN32' => 'Win32',
+ 'OS_VMCMS' => 'VM/CMS',
+ 'OS_ZSYSTEM' => 'Z-System',
+ 'OS_CPM' => 'CP/M',
+ 'OS_QDOS' => 'QDOS',
+ 'OS_RISCOS' => 'RISCOS',
+ 'OS_UNKNOWN' => 'Unknown',
+ }
+ unless OS_NAMES.key? os_code then
+ puts "invalid OS_CODE `#{os_code}'"
+ exit
+ end
+ message "#{OS_NAMES[os_code]}\n"
+ defines << "OS_CODE=#{os_code}"
+
+ defines = defines.collect{|d|' -D'+d}.join
+ if $CPPFLAGS then
+ $CPPFLAGS += defines
+ else
+ $CFLAGS += defines
+ end
+
+ create_makefile('zlib')
+
+end
diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c
new file mode 100644
index 0000000000..e1aebe446f
--- /dev/null
+++ b/ext/zlib/zlib.c
@@ -0,0 +1,2773 @@
+/*
+ * zlib.c - An interface for zlib.
+ *
+ * Copyright (C) UENO Katsuhiro 2000-2003
+ *
+ * $Id$
+ */
+
+#include <ruby.h>
+#include <zlib.h>
+#include <time.h>
+
+#define RUBY_ZLIB_VERSION "0.6.0"
+
+
+#define OBJ_IS_FREED(val) (RBASIC(val)->flags == 0)
+
+#ifndef GZIP_SUPPORT
+#define GZIP_SUPPORT 1
+#endif
+
+/* from zutil.h */
+#ifndef DEF_MEM_LEVEL
+#if MAX_MEM_LEVEL >= 8
+#define DEF_MEM_LEVEL 8
+#else
+#define DEF_MEM_LEVEL MAX_MEM_LEVEL
+#endif
+#endif
+
+
+
+/*--------- Prototypes --------*/
+
+static NORETURN(void raise_zlib_error _((int, const char *)));
+static VALUE rb_zlib_version _((VALUE));
+static VALUE do_checksum _((int, VALUE*, uLong (*) _((uLong, const Bytef*, uInt))));
+static VALUE rb_zlib_adler32 _((int, VALUE*, VALUE));
+static VALUE rb_zlib_crc32 _((int, VALUE*, VALUE));
+static VALUE rb_zlib_crc_table _((VALUE));
+static voidpf zlib_mem_alloc _((voidpf, uInt, uInt));
+static void zlib_mem_free _((voidpf, voidpf));
+
+struct zstream;
+struct zstream_funcs;
+static void zstream_init _((struct zstream*, const struct zstream_funcs *));
+static void zstream_expand_buffer _((struct zstream*));
+static void zstream_expand_buffer_into _((struct zstream*, int));
+static void zstream_append_buffer _((struct zstream*, const char*, int));
+static VALUE zstream_detach_buffer _((struct zstream*));
+static VALUE zstream_shift_buffer _((struct zstream*, int));
+static void zstream_buffer_ungetc _((struct zstream*, int));
+static void zstream_append_input _((struct zstream*, const char*, unsigned int));
+static void zstream_discard_input _((struct zstream*, unsigned int));
+static void zstream_reset_input _((struct zstream*));
+static void zstream_passthrough_input _((struct zstream*));
+static VALUE zstream_detach_input _((struct zstream*));
+static void zstream_reset _((struct zstream*));
+static void zstream_end _((struct zstream*));
+static void zstream_run _((struct zstream*, Bytef*, uInt, int));
+static VALUE zstream_sync _((struct zstream*, Bytef*, uInt));
+static void zstream_mark _((struct zstream*));
+static void zstream_free _((struct zstream*));
+static VALUE zstream_new _((VALUE, const struct zstream_funcs*));
+static struct zstream *get_zstream _((VALUE));
+
+static VALUE rb_zstream_end _((VALUE));
+static VALUE rb_zstream_reset _((VALUE));
+static VALUE rb_zstream_finish _((VALUE));
+static VALUE rb_zstream_flush_next_in _((VALUE));
+static VALUE rb_zstream_flush_next_out _((VALUE));
+static VALUE rb_zstream_avail_out _((VALUE));
+static VALUE rb_zstream_set_avail_out _((VALUE, VALUE));
+static VALUE rb_zstream_avail_in _((VALUE));
+static VALUE rb_zstream_total_in _((VALUE));
+static VALUE rb_zstream_total_out _((VALUE));
+static VALUE rb_zstream_data_type _((VALUE));
+static VALUE rb_zstream_adler _((VALUE));
+static VALUE rb_zstream_finished_p _((VALUE));
+static VALUE rb_zstream_closed_p _((VALUE));
+
+static VALUE rb_deflate_s_allocate _((VALUE));
+static VALUE rb_deflate_initialize _((int, VALUE*, VALUE));
+static VALUE rb_deflate_clone _((VALUE));
+static VALUE rb_deflate_s_deflate _((int, VALUE*, VALUE));
+static void do_deflate _((struct zstream*, VALUE, int));
+static VALUE rb_deflate_deflate _((int, VALUE*, VALUE));
+static VALUE rb_deflate_addstr _((VALUE, VALUE));
+static VALUE rb_deflate_flush _((int, VALUE*, VALUE));
+static VALUE rb_deflate_params _((VALUE, VALUE, VALUE));
+static VALUE rb_deflate_set_dictionary _((VALUE, VALUE));
+
+static VALUE rb_inflate_s_allocate _((VALUE));
+static VALUE rb_inflate_initialize _((int, VALUE*, VALUE));
+static VALUE rb_inflate_s_inflate _((VALUE, VALUE));
+static void do_inflate _((struct zstream*, VALUE));
+static VALUE rb_inflate_inflate _((VALUE, VALUE));
+static VALUE rb_inflate_addstr _((VALUE, VALUE));
+static VALUE rb_inflate_sync _((VALUE, VALUE));
+static VALUE rb_inflate_sync_point_p _((VALUE));
+static VALUE rb_inflate_set_dictionary _((VALUE, VALUE));
+
+#if GZIP_SUPPORT
+struct gzfile;
+static void gzfile_mark _((struct gzfile*));
+static void gzfile_free _((struct gzfile*));
+static VALUE gzfile_new _((VALUE, const struct zstream_funcs*, void (*) _((struct gzfile*))));
+static void gzfile_reset _((struct gzfile*));
+static void gzfile_close _((struct gzfile*, int));
+static void gzfile_write_raw _((struct gzfile*));
+static VALUE gzfile_read_raw _((struct gzfile*));
+static int gzfile_read_raw_ensure _((struct gzfile*, int));
+static char *gzfile_read_raw_until_zero _((struct gzfile*, long));
+static unsigned int gzfile_get16 _((const unsigned char*));
+static unsigned long gzfile_get32 _((const unsigned char*));
+static void gzfile_set32 _((unsigned long n, unsigned char*));
+static void gzfile_make_header _((struct gzfile*));
+static void gzfile_make_footer _((struct gzfile*));
+static void gzfile_read_header _((struct gzfile*));
+static void gzfile_check_footer _((struct gzfile*));
+static void gzfile_write _((struct gzfile*, Bytef*, uInt));
+static long gzfile_read_more _((struct gzfile*));
+static VALUE gzfile_read _((struct gzfile*, int));
+static VALUE gzfile_read_all _((struct gzfile*));
+static void gzfile_ungetc _((struct gzfile*, int));
+static VALUE gzfile_finalize _((VALUE));
+static void gzfile_writer_end _((struct gzfile*));
+static void gzfile_reader_end _((struct gzfile*));
+static void gzfile_reader_rewind _((struct gzfile*));
+static VALUE gzfile_reader_get_unused _((struct gzfile*));
+static struct gzfile *get_gzfile _((VALUE));
+static VALUE gzfile_ensure_close _((VALUE));
+static VALUE rb_gzfile_s_wrap _((int, VALUE*, VALUE));
+static VALUE gzfile_s_open _((int, VALUE*, VALUE, const char*));
+
+static VALUE rb_gzfile_to_io _((VALUE));
+static VALUE rb_gzfile_crc _((VALUE));
+static VALUE rb_gzfile_mtime _((VALUE));
+static VALUE rb_gzfile_level _((VALUE));
+static VALUE rb_gzfile_os_code _((VALUE));
+static VALUE rb_gzfile_orig_name _((VALUE));
+static VALUE rb_gzfile_comment _((VALUE));
+static VALUE rb_gzfile_lineno _((VALUE));
+static VALUE rb_gzfile_set_lineno _((VALUE, VALUE));
+static VALUE rb_gzfile_set_mtime _((VALUE, VALUE));
+static VALUE rb_gzfile_set_orig_name _((VALUE, VALUE));
+static VALUE rb_gzfile_set_comment _((VALUE, VALUE));
+static VALUE rb_gzfile_close _((VALUE));
+static VALUE rb_gzfile_finish _((VALUE));
+static VALUE rb_gzfile_closed_p _((VALUE));
+static VALUE rb_gzfile_eof_p _((VALUE));
+static VALUE rb_gzfile_sync _((VALUE));
+static VALUE rb_gzfile_set_sync _((VALUE, VALUE));
+static VALUE rb_gzfile_total_in _((VALUE));
+static VALUE rb_gzfile_total_out _((VALUE));
+
+static VALUE rb_gzwriter_s_allocate _((VALUE));
+static VALUE rb_gzwriter_s_open _((int, VALUE*, VALUE));
+static VALUE rb_gzwriter_initialize _((int, VALUE*, VALUE));
+static VALUE rb_gzwriter_flush _((int, VALUE*, VALUE));
+static VALUE rb_gzwriter_write _((VALUE, VALUE));
+static VALUE rb_gzwriter_putc _((VALUE, VALUE));
+
+static VALUE rb_gzreader_s_allocate _((VALUE));
+static VALUE rb_gzreader_s_open _((int, VALUE*, VALUE));
+static VALUE rb_gzreader_initialize _((VALUE, VALUE));
+static VALUE rb_gzreader_rewind _((VALUE));
+static VALUE rb_gzreader_unused _((VALUE));
+static VALUE rb_gzreader_read _((int, VALUE*, VALUE));
+static VALUE rb_gzreader_getc _((VALUE));
+static VALUE rb_gzreader_readchar _((VALUE));
+static VALUE rb_gzreader_each_byte _((VALUE));
+static VALUE rb_gzreader_ungetc _((VALUE, VALUE));
+static void gzreader_skip_linebreaks _((struct gzfile*));
+static VALUE gzreader_gets _((int, VALUE*, VALUE));
+static VALUE rb_gzreader_gets _((int, VALUE*, VALUE));
+static VALUE rb_gzreader_readline _((int, VALUE*, VALUE));
+static VALUE rb_gzreader_each _((int, VALUE*, VALUE));
+static VALUE rb_gzreader_readlines _((int, VALUE*, VALUE));
+#endif /* GZIP_SUPPORT */
+
+
+void Init_zlib _((void));
+
+
+
+/*--------- Exceptions --------*/
+
+static VALUE cZError, cStreamEnd, cNeedDict;
+static VALUE cStreamError, cDataError, cMemError, cBufError, cVersionError;
+
+static void
+raise_zlib_error(err, msg)
+ int err;
+ const char *msg;
+{
+ VALUE exc;
+
+ if (!msg) {
+ msg = zError(err);
+ }
+
+ switch(err) {
+ case Z_STREAM_END:
+ exc = rb_exc_new2(cStreamEnd, msg);
+ break;
+ case Z_NEED_DICT:
+ exc = rb_exc_new2(cNeedDict, msg);
+ break;
+ case Z_STREAM_ERROR:
+ exc = rb_exc_new2(cStreamError, msg);
+ break;
+ case Z_DATA_ERROR:
+ exc = rb_exc_new2(cDataError, msg);
+ break;
+ case Z_BUF_ERROR:
+ exc = rb_exc_new2(cBufError, msg);
+ break;
+ case Z_VERSION_ERROR:
+ exc = rb_exc_new2(cVersionError, msg);
+ break;
+ case Z_MEM_ERROR:
+ exc = rb_exc_new2(cMemError, msg);
+ break;
+ case Z_ERRNO:
+ rb_sys_fail(msg);
+ /* no return */
+ default:
+ {
+ char buf[BUFSIZ];
+ snprintf(buf, BUFSIZ, "unknown zlib error %d: %s", err, msg);
+ exc = rb_exc_new2(cZError, buf);
+ }
+ }
+
+ rb_exc_raise(exc);
+}
+
+
+
+/*-------- module Zlib --------*/
+
+static VALUE
+rb_zlib_version(klass)
+ VALUE klass;
+{
+ VALUE str;
+
+ str = rb_str_new2(zlibVersion());
+ OBJ_TAINT(str); /* for safe */
+ return str;
+}
+
+static VALUE
+do_checksum(argc, argv, func)
+ int argc;
+ VALUE *argv;
+ uLong (*func) _((uLong, const Bytef *, uInt));
+{
+ VALUE str, vsum;
+ unsigned long sum;
+
+ rb_scan_args(argc, argv, "02", &str, &vsum);
+
+ if (!NIL_P(vsum)) {
+ sum = NUM2ULONG(vsum);
+ }
+ else if (NIL_P(str)) {
+ sum = 0;
+ }
+ else {
+ sum = func(0, Z_NULL, 0);
+ }
+
+ if (NIL_P(str)) {
+ sum = func(sum, Z_NULL, 0);
+ }
+ else {
+ StringValue(str);
+ sum = func(sum, RSTRING(str)->ptr, RSTRING(str)->len);
+ }
+ return rb_uint2inum(sum);
+}
+
+static VALUE
+rb_zlib_adler32(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ return do_checksum(argc, argv, adler32);
+}
+
+static VALUE
+rb_zlib_crc32(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ return do_checksum(argc, argv, crc32);
+}
+
+static VALUE
+rb_zlib_crc_table(obj)
+ VALUE obj;
+{
+ const unsigned long *crctbl;
+ VALUE dst;
+ int i;
+
+ crctbl = get_crc_table();
+ dst = rb_ary_new2(256);
+
+ for (i = 0; i < 256; i++) {
+ rb_ary_push(dst, rb_uint2inum(crctbl[i]));
+ }
+ return dst;
+}
+
+
+
+/*-------- zstream - internal APIs --------*/
+
+struct zstream {
+ unsigned long flags;
+ VALUE buf;
+ long buf_filled;
+ VALUE input;
+ z_stream stream;
+ const struct zstream_funcs {
+ int (*reset) _((z_streamp));
+ int (*end) _((z_streamp));
+ int (*run) _((z_streamp, int));
+ } *func;
+};
+
+#define ZSTREAM_FLAG_READY 0x1
+#define ZSTREAM_FLAG_IN_STREAM 0x2
+#define ZSTREAM_FLAG_FINISHED 0x4
+#define ZSTREAM_FLAG_FINALIZE 0x8
+#define ZSTREAM_FLAG_UNUSED 0x10
+
+#define ZSTREAM_READY(z) ((z)->flags |= ZSTREAM_FLAG_READY)
+#define ZSTREAM_IS_READY(z) ((z)->flags & ZSTREAM_FLAG_READY)
+#define ZSTREAM_IS_FINISHED(z) ((z)->flags & ZSTREAM_FLAG_FINISHED)
+#define ZSTREAM_IS_FINALIZE(z) ((z)->flags & ZSTREAM_FLAG_FINALIZE)
+
+/* I think that more better value should be found,
+ but I gave up finding it. B) */
+#define ZSTREAM_INITIAL_BUFSIZE 1024
+#define ZSTREAM_AVAIL_OUT_STEP_MAX 16384
+#define ZSTREAM_AVAIL_OUT_STEP_MIN 2048
+
+static const struct zstream_funcs deflate_funcs = {
+ deflateReset, deflateEnd, deflate,
+};
+
+static const struct zstream_funcs inflate_funcs = {
+ inflateReset, inflateEnd, inflate,
+};
+
+
+static voidpf
+zlib_mem_alloc(opaque, items, size)
+ voidpf opaque;
+ uInt items, size;
+{
+ return xmalloc(items * size);
+}
+
+static void
+zlib_mem_free(opaque, address)
+ voidpf opaque, address;
+{
+ free(address);
+}
+
+static void
+zstream_init(z, func)
+ struct zstream *z;
+ const struct zstream_funcs *func;
+{
+ z->flags = 0;
+ z->buf = Qnil;
+ z->buf_filled = 0;
+ z->input = Qnil;
+ z->stream.zalloc = zlib_mem_alloc;
+ z->stream.zfree = zlib_mem_free;
+ z->stream.opaque = Z_NULL;
+ z->stream.msg = Z_NULL;
+ z->stream.next_in = Z_NULL;
+ z->stream.avail_in = 0;
+ z->stream.next_out = Z_NULL;
+ z->stream.avail_out = 0;
+ z->func = func;
+}
+
+#define zstream_init_deflate(z) zstream_init((z), &deflate_funcs)
+#define zstream_init_inflate(z) zstream_init((z), &inflate_funcs)
+
+static void
+zstream_expand_buffer(z)
+ struct zstream *z;
+{
+ long inc;
+
+ if (NIL_P(z->buf)) {
+ /* I uses rb_str_new here not rb_str_buf_new because
+ rb_str_buf_new makes a zero-length string. */
+ z->buf = rb_str_new(0, ZSTREAM_INITIAL_BUFSIZE);
+ z->buf_filled = 0;
+ z->stream.next_out = RSTRING(z->buf)->ptr;
+ z->stream.avail_out = ZSTREAM_INITIAL_BUFSIZE;
+ return;
+ }
+
+ if (RSTRING(z->buf)->len - z->buf_filled >= ZSTREAM_AVAIL_OUT_STEP_MAX) {
+ /* to keep other threads from freezing */
+ z->stream.avail_out = ZSTREAM_AVAIL_OUT_STEP_MAX;
+ }
+ else {
+ inc = z->buf_filled / 2;
+ if (inc < ZSTREAM_AVAIL_OUT_STEP_MIN) {
+ inc = ZSTREAM_AVAIL_OUT_STEP_MIN;
+ }
+ rb_str_resize(z->buf, z->buf_filled + inc);
+ z->stream.avail_out = (inc < ZSTREAM_AVAIL_OUT_STEP_MAX) ?
+ inc : ZSTREAM_AVAIL_OUT_STEP_MAX;
+ }
+ z->stream.next_out = RSTRING(z->buf)->ptr + z->buf_filled;
+}
+
+static void
+zstream_expand_buffer_into(z, size)
+ struct zstream *z;
+ int size;
+{
+ if (NIL_P(z->buf)) {
+ /* I uses rb_str_new here not rb_str_buf_new because
+ rb_str_buf_new makes a zero-length string. */
+ z->buf = rb_str_new(0, size);
+ z->buf_filled = 0;
+ z->stream.next_out = RSTRING(z->buf)->ptr;
+ z->stream.avail_out = size;
+ }
+ else if (z->stream.avail_out != size) {
+ rb_str_resize(z->buf, z->buf_filled + size);
+ z->stream.next_out = RSTRING(z->buf)->ptr + z->buf_filled;
+ z->stream.avail_out = size;
+ }
+}
+
+static void
+zstream_append_buffer(z, src, len)
+ struct zstream *z;
+ const char *src;
+ int len;
+{
+ if (NIL_P(z->buf)) {
+ z->buf = rb_str_buf_new(len);
+ rb_str_buf_cat(z->buf, src, len);
+ z->buf_filled = len;
+ z->stream.next_out = RSTRING(z->buf)->ptr;
+ z->stream.avail_out = 0;
+ return;
+ }
+
+ if (RSTRING(z->buf)->len < z->buf_filled + len) {
+ rb_str_resize(z->buf, z->buf_filled + len);
+ z->stream.avail_out = 0;
+ }
+ else {
+ if (z->stream.avail_out >= len) {
+ z->stream.avail_out -= len;
+ }
+ else {
+ z->stream.avail_out = 0;
+ }
+ }
+ memcpy(RSTRING(z->buf)->ptr + z->buf_filled, src, len);
+ z->buf_filled += len;
+ z->stream.next_out = RSTRING(z->buf)->ptr + z->buf_filled;
+}
+
+#define zstream_append_buffer2(z,v) \
+ zstream_append_buffer((z),RSTRING(v)->ptr,RSTRING(v)->len)
+
+static VALUE
+zstream_detach_buffer(z)
+ struct zstream *z;
+{
+ VALUE dst;
+
+ if (NIL_P(z->buf)) {
+ dst = rb_str_new(0, 0);
+ }
+ else {
+ dst = z->buf;
+ rb_str_resize(dst, z->buf_filled);
+ }
+
+ z->buf = Qnil;
+ z->buf_filled = 0;
+ z->stream.next_out = 0;
+ z->stream.avail_out = 0;
+ return dst;
+}
+
+static VALUE
+zstream_shift_buffer(z, len)
+ struct zstream *z;
+ int len;
+{
+ VALUE dst;
+
+ if (z->buf_filled <= len) {
+ return zstream_detach_buffer(z);
+ }
+
+ dst = rb_str_substr(z->buf, 0, len);
+ z->buf_filled -= len;
+ memmove(RSTRING(z->buf)->ptr, RSTRING(z->buf)->ptr + len,
+ z->buf_filled);
+ z->stream.next_out = RSTRING(z->buf)->ptr + z->buf_filled;
+ z->stream.avail_out = RSTRING(z->buf)->len - z->buf_filled;
+ if (z->stream.avail_out > ZSTREAM_AVAIL_OUT_STEP_MAX) {
+ z->stream.avail_out = ZSTREAM_AVAIL_OUT_STEP_MAX;
+ }
+
+ return dst;
+}
+
+static void
+zstream_buffer_ungetc(z, c)
+ struct zstream *z;
+ int c;
+{
+ if (NIL_P(z->buf) || RSTRING(z->buf)->len - z->buf_filled == 0) {
+ zstream_expand_buffer(z);
+ }
+
+ memmove(RSTRING(z->buf)->ptr + 1, RSTRING(z->buf)->ptr, z->buf_filled);
+ RSTRING(z->buf)->ptr[0] = (char)c;
+ z->buf_filled++;
+ if (z->stream.avail_out > 0) {
+ z->stream.next_out++;
+ z->stream.avail_out--;
+ }
+}
+
+static void
+zstream_append_input(z, src, len)
+ struct zstream *z;
+ const char *src;
+ unsigned int len;
+{
+ if (len <= 0) return;
+
+ if (NIL_P(z->input)) {
+ z->input = rb_str_buf_new(len);
+ rb_str_buf_cat(z->input, src, len);
+ }
+ else {
+ rb_str_buf_cat(z->input, src, len);
+ }
+}
+
+#define zstream_append_input2(z,v)\
+ zstream_append_input((z), RSTRING(v)->ptr, RSTRING(v)->len)
+
+static void
+zstream_discard_input(z, len)
+ struct zstream *z;
+ unsigned int len;
+{
+ if (NIL_P(z->input) || RSTRING(z->input)->len <= len) {
+ z->input = Qnil;
+ }
+ else {
+ memmove(RSTRING(z->input)->ptr, RSTRING(z->input)->ptr + len,
+ RSTRING(z->input)->len - len);
+ rb_str_resize(z->input, RSTRING(z->input)->len - len);
+ }
+}
+
+static void
+zstream_reset_input(z)
+ struct zstream *z;
+{
+ z->input = Qnil;
+}
+
+static void
+zstream_passthrough_input(z)
+ struct zstream *z;
+{
+ if (!NIL_P(z->input)) {
+ zstream_append_buffer2(z, z->input);
+ z->input = Qnil;
+ }
+}
+
+static VALUE
+zstream_detach_input(z)
+ struct zstream *z;
+{
+ VALUE dst;
+
+ dst = NIL_P(z->input) ? rb_str_new(0, 0) : z->input;
+ z->input = Qnil;
+ return dst;
+}
+
+static void
+zstream_reset(z)
+ struct zstream *z;
+{
+ int err;
+
+ err = z->func->reset(&z->stream);
+ if (err != Z_OK && !ZSTREAM_IS_FINALIZE(z)) {
+ raise_zlib_error(err, z->stream.msg);
+ }
+ z->flags = ZSTREAM_FLAG_READY;
+ z->buf = Qnil;
+ z->buf_filled = 0;
+ z->stream.next_out = 0;
+ z->stream.avail_out = 0;
+ zstream_reset_input(z);
+}
+
+static void
+zstream_end(z)
+ struct zstream *z;
+{
+ int err;
+
+ if (!ZSTREAM_IS_READY(z) && !ZSTREAM_IS_FINALIZE(z)) {
+ if (RTEST(ruby_debug)) {
+ rb_warning("attempt to close uninitialized zstream; ignored.");
+ }
+ return;
+ }
+ if (z->flags & ZSTREAM_FLAG_IN_STREAM) {
+ if (RTEST(ruby_debug)) {
+ rb_warning("attempt to close unfinished zstream; reset forced.");
+ }
+ zstream_reset(z);
+ }
+
+ zstream_reset_input(z);
+ err = z->func->end(&z->stream);
+ if (err != Z_OK && !ZSTREAM_IS_FINALIZE(z)) {
+ raise_zlib_error(err, z->stream.msg);
+ }
+ z->flags = 0;
+}
+
+static void
+zstream_run(z, src, len, flush)
+ struct zstream *z;
+ Bytef *src;
+ uInt len;
+ int flush;
+{
+ uInt n;
+ int err;
+
+ if (NIL_P(z->input)) {
+ z->stream.next_in = src;
+ z->stream.avail_in = len;
+ }
+ else {
+ zstream_append_input(z, src, len);
+ z->stream.next_in = RSTRING(z->input)->ptr;
+ z->stream.avail_in = RSTRING(z->input)->len;
+ }
+
+ if (z->stream.avail_out == 0) {
+ zstream_expand_buffer(z);
+ }
+
+ for (;;) {
+ n = z->stream.avail_out;
+ err = z->func->run(&z->stream, flush);
+ z->buf_filled += n - z->stream.avail_out;
+ rb_thread_schedule();
+
+ if (err == Z_STREAM_END) {
+ z->flags &= ~ZSTREAM_FLAG_IN_STREAM;
+ z->flags |= ZSTREAM_FLAG_FINISHED;
+ break;
+ }
+ if (err != Z_OK) {
+ if (flush != Z_FINISH && err == Z_BUF_ERROR
+ && z->stream.avail_out > 0) {
+ z->flags |= ZSTREAM_FLAG_IN_STREAM;
+ break;
+ }
+ zstream_reset_input(z);
+ if (z->stream.avail_in > 0) {
+ zstream_append_input(z, z->stream.next_in, z->stream.avail_in);
+ }
+ raise_zlib_error(err, z->stream.msg);
+ }
+ if (z->stream.avail_out > 0) {
+ z->flags |= ZSTREAM_FLAG_IN_STREAM;
+ break;
+ }
+ zstream_expand_buffer(z);
+ }
+
+ zstream_reset_input(z);
+ if (z->stream.avail_in > 0) {
+ zstream_append_input(z, z->stream.next_in, z->stream.avail_in);
+ }
+}
+
+static VALUE
+zstream_sync(z, src, len)
+ struct zstream *z;
+ Bytef *src;
+ uInt len;
+{
+ VALUE rest;
+ int err;
+
+ if (!NIL_P(z->input)) {
+ z->stream.next_in = RSTRING(z->input)->ptr;
+ z->stream.avail_in = RSTRING(z->input)->len;
+ err = inflateSync(&z->stream);
+ if (err == Z_OK) {
+ zstream_discard_input(z,
+ RSTRING(z->input)->len - z->stream.avail_in);
+ zstream_append_input(z, src, len);
+ return Qtrue;
+ }
+ zstream_reset_input(z);
+ if (err != Z_DATA_ERROR) {
+ rest = rb_str_new(z->stream.next_in, z->stream.avail_in);
+ raise_zlib_error(err, z->stream.msg);
+ }
+ }
+
+ if (len <= 0) return Qfalse;
+
+ z->stream.next_in = src;
+ z->stream.avail_in = len;
+ err = inflateSync(&z->stream);
+ if (err == Z_OK) {
+ zstream_append_input(z, z->stream.next_in, z->stream.avail_in);
+ return Qtrue;
+ }
+ if (err != Z_DATA_ERROR) {
+ rest = rb_str_new(z->stream.next_in, z->stream.avail_in);
+ raise_zlib_error(err, z->stream.msg);
+ }
+ return Qfalse;
+}
+
+static void
+zstream_mark(z)
+ struct zstream *z;
+{
+ rb_gc_mark(z->buf);
+ rb_gc_mark(z->input);
+}
+
+static void
+zstream_free(z)
+ struct zstream *z;
+{
+ z->flags |= ZSTREAM_FLAG_FINALIZE;
+ zstream_end(z);
+ free(z);
+}
+
+static VALUE
+zstream_new(klass, funcs)
+ VALUE klass;
+ const struct zstream_funcs *funcs;
+{
+ VALUE obj;
+ struct zstream *z;
+
+ obj = Data_Make_Struct(klass, struct zstream,
+ zstream_mark, zstream_free, z);
+ zstream_init(z, funcs);
+ return obj;
+}
+
+#define zstream_deflate_new(klass) zstream_new((klass), &deflate_funcs)
+#define zstream_inflate_new(klass) zstream_new((klass), &inflate_funcs)
+
+static struct zstream *
+get_zstream(obj)
+ VALUE obj;
+{
+ struct zstream *z;
+
+ Data_Get_Struct(obj, struct zstream, z);
+ if (!ZSTREAM_IS_READY(z)) {
+ rb_raise(cZError, "stream is not ready");
+ }
+ return z;
+}
+
+
+
+/*-------- class Zlib::ZStream ---------*/
+
+static VALUE
+rb_zstream_end(obj)
+ VALUE obj;
+{
+ zstream_end(get_zstream(obj));
+ return Qnil;
+}
+
+static VALUE
+rb_zstream_reset(obj)
+ VALUE obj;
+{
+ zstream_reset(get_zstream(obj));
+ return Qnil;
+}
+
+static VALUE
+rb_zstream_finish(obj)
+ VALUE obj;
+{
+ struct zstream *z = get_zstream(obj);
+ VALUE dst;
+
+ zstream_run(z, "", 0, Z_FINISH);
+ dst = zstream_detach_buffer(z);
+
+ OBJ_INFECT(dst, obj);
+ return dst;
+}
+
+static VALUE
+rb_zstream_flush_next_in(obj)
+ VALUE obj;
+{
+ struct zstream *z;
+ VALUE dst;
+
+ Data_Get_Struct(obj, struct zstream, z);
+ dst = zstream_detach_input(z);
+ OBJ_INFECT(dst, obj);
+ return dst;
+}
+
+static VALUE
+rb_zstream_flush_next_out(obj)
+ VALUE obj;
+{
+ struct zstream *z;
+ VALUE dst;
+
+ Data_Get_Struct(obj, struct zstream, z);
+ dst = zstream_detach_buffer(z);
+ OBJ_INFECT(dst, obj);
+ return dst;
+}
+
+static VALUE
+rb_zstream_avail_out(obj)
+ VALUE obj;
+{
+ struct zstream *z;
+ Data_Get_Struct(obj, struct zstream, z);
+ return rb_uint2inum(z->stream.avail_out);
+}
+
+static VALUE
+rb_zstream_set_avail_out(obj, size)
+ VALUE obj, size;
+{
+ struct zstream *z = get_zstream(obj);
+
+ Check_Type(size, T_FIXNUM);
+ zstream_expand_buffer_into(z, FIX2INT(size));
+ return size;
+}
+
+static VALUE
+rb_zstream_avail_in(obj)
+ VALUE obj;
+{
+ struct zstream *z;
+ Data_Get_Struct(obj, struct zstream, z);
+ return INT2FIX(NIL_P(z->input) ? 0 : (int)(RSTRING(z->input)->len));
+}
+
+static VALUE
+rb_zstream_total_in(obj)
+ VALUE obj;
+{
+ return rb_uint2inum(get_zstream(obj)->stream.total_in);
+}
+
+static VALUE
+rb_zstream_total_out(obj)
+ VALUE obj;
+{
+ return rb_uint2inum(get_zstream(obj)->stream.total_out);
+}
+
+static VALUE
+rb_zstream_data_type(obj)
+ VALUE obj;
+{
+ return INT2FIX(get_zstream(obj)->stream.data_type);
+}
+
+static VALUE
+rb_zstream_adler(obj)
+ VALUE obj;
+{
+ return rb_uint2inum(get_zstream(obj)->stream.adler);
+}
+
+static VALUE
+rb_zstream_finished_p(obj)
+ VALUE obj;
+{
+ return ZSTREAM_IS_FINISHED(get_zstream(obj)) ? Qtrue : Qfalse;
+}
+
+static VALUE
+rb_zstream_closed_p(obj)
+ VALUE obj;
+{
+ struct zstream *z;
+ Data_Get_Struct(obj, struct zstream, z);
+ return ZSTREAM_IS_READY(z) ? Qfalse : Qtrue;
+}
+
+
+
+/*-------- class Zlib::Deflate --------*/
+
+#define FIXNUMARG(val, ifnil) \
+ (NIL_P((val)) ? (ifnil) \
+ : ((void)Check_Type((val), T_FIXNUM), FIX2INT((val))))
+
+#define ARG_LEVEL(val) FIXNUMARG((val), Z_DEFAULT_COMPRESSION)
+#define ARG_WBITS(val) FIXNUMARG((val), MAX_WBITS)
+#define ARG_MEMLEVEL(val) FIXNUMARG((val), DEF_MEM_LEVEL)
+#define ARG_STRATEGY(val) FIXNUMARG((val), Z_DEFAULT_STRATEGY)
+#define ARG_FLUSH(val) FIXNUMARG((val), Z_NO_FLUSH)
+
+
+static VALUE
+rb_deflate_s_allocate(klass)
+ VALUE klass;
+{
+ return zstream_deflate_new(klass);
+}
+
+static VALUE
+rb_deflate_initialize(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ struct zstream *z;
+ VALUE level, wbits, memlevel, strategy;
+ int err;
+
+ rb_scan_args(argc, argv, "04", &level, &wbits, &memlevel, &strategy);
+ Data_Get_Struct(obj, struct zstream, z);
+
+ err = deflateInit2(&z->stream, ARG_LEVEL(level), Z_DEFLATED,
+ ARG_WBITS(wbits), ARG_MEMLEVEL(memlevel),
+ ARG_STRATEGY(strategy));
+ if (err != Z_OK) {
+ raise_zlib_error(err, z->stream.msg);
+ }
+ ZSTREAM_READY(z);
+
+ return obj;
+}
+
+static VALUE
+rb_deflate_clone(obj)
+ VALUE obj;
+{
+ struct zstream *z = get_zstream(obj);
+ struct zstream *z2;
+ VALUE clone;
+ int err;
+
+ clone = zstream_deflate_new(rb_class_of(obj));
+ Data_Get_Struct(clone, struct zstream, z2);
+
+ err = deflateCopy(&z2->stream, &z->stream);
+ if (err != Z_OK) {
+ raise_zlib_error(err, 0);
+ }
+
+ z2->flags = z->flags;
+ CLONESETUP(clone, obj);
+ OBJ_INFECT(clone, obj);
+ return clone;
+}
+
+static VALUE
+rb_deflate_s_deflate(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ struct zstream z;
+ VALUE src, level, dst;
+ int err;
+
+ rb_scan_args(argc, argv, "11", &src, &level);
+
+ zstream_init_deflate(&z);
+ err = deflateInit(&z.stream, ARG_LEVEL(level));
+ if (err != Z_OK) {
+ raise_zlib_error(err, z.stream.msg);
+ }
+ ZSTREAM_READY(&z);
+
+ StringValue(src);
+ zstream_run(&z, RSTRING(src)->ptr, RSTRING(src)->len, Z_FINISH);
+ dst = zstream_detach_buffer(&z);
+ zstream_end(&z);
+
+ OBJ_INFECT(dst, src);
+ return dst;
+}
+
+static void
+do_deflate(z, src, flush)
+ struct zstream *z;
+ VALUE src;
+ int flush;
+{
+ if (NIL_P(src)) {
+ zstream_run(z, "", 0, Z_FINISH);
+ return;
+ }
+ StringValue(src);
+ if (flush != Z_NO_FLUSH || RSTRING(src)->len > 0) { /* prevent BUF_ERROR */
+ zstream_run(z, RSTRING(src)->ptr, RSTRING(src)->len, flush);
+ }
+}
+
+static VALUE
+rb_deflate_deflate(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ struct zstream *z = get_zstream(obj);
+ VALUE src, flush, dst;
+
+ rb_scan_args(argc, argv, "11", &src, &flush);
+ OBJ_INFECT(obj, src);
+ do_deflate(z, src, ARG_FLUSH(flush));
+ dst = zstream_detach_buffer(z);
+
+ OBJ_INFECT(dst, obj);
+ return dst;
+}
+
+static VALUE
+rb_deflate_addstr(obj, src)
+ VALUE obj, src;
+{
+ OBJ_INFECT(obj, src);
+ do_deflate(get_zstream(obj), src, Z_NO_FLUSH);
+ return obj;
+}
+
+static VALUE
+rb_deflate_flush(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ struct zstream *z = get_zstream(obj);
+ VALUE v_flush, dst;
+ int flush;
+
+ rb_scan_args(argc, argv, "01", &v_flush);
+ flush = FIXNUMARG(v_flush, Z_SYNC_FLUSH);
+ if (flush != Z_NO_FLUSH) { /* prevent Z_BUF_ERROR */
+ zstream_run(z, "", 0, flush);
+ }
+ dst = zstream_detach_buffer(z);
+
+ OBJ_INFECT(dst, obj);
+ return dst;
+}
+
+static VALUE
+rb_deflate_params(obj, v_level, v_strategy)
+ VALUE obj, v_level, v_strategy;
+{
+ struct zstream *z = get_zstream(obj);
+ int level, strategy;
+ int err;
+
+ level = ARG_LEVEL(v_level);
+ strategy = ARG_STRATEGY(v_strategy);
+
+ err = deflateParams(&z->stream, level, strategy);
+ while (err == Z_BUF_ERROR) {
+ if (RTEST(ruby_debug)) {
+ rb_warning("deflateParams() returned Z_BUF_ERROR");
+ }
+ zstream_expand_buffer(z);
+ err = deflateParams(&z->stream, level, strategy);
+ }
+ if (err != Z_OK) {
+ raise_zlib_error(err, z->stream.msg);
+ }
+
+ return Qnil;
+}
+
+static VALUE
+rb_deflate_set_dictionary(obj, dic)
+ VALUE obj, dic;
+{
+ struct zstream *z = get_zstream(obj);
+ VALUE src = dic;
+ int err;
+
+ OBJ_INFECT(obj, dic);
+ StringValue(src);
+ err = deflateSetDictionary(&z->stream,
+ RSTRING(src)->ptr, RSTRING(src)->len);
+ if (err != Z_OK) {
+ raise_zlib_error(err, z->stream.msg);
+ }
+
+ return dic;
+}
+
+
+
+/*-------- class Zlib::Inflate --------*/
+
+static VALUE
+rb_inflate_s_allocate(klass)
+ VALUE klass;
+{
+ return zstream_inflate_new(klass);
+}
+
+static VALUE
+rb_inflate_initialize(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ struct zstream *z;
+ VALUE wbits;
+ int err;
+
+ rb_scan_args(argc, argv, "01", &wbits);
+ Data_Get_Struct(obj, struct zstream, z);
+
+ err = inflateInit2(&z->stream, ARG_WBITS(wbits));
+ if (err != Z_OK) {
+ raise_zlib_error(err, z->stream.msg);
+ }
+ ZSTREAM_READY(z);
+
+ return obj;
+}
+
+static VALUE
+rb_inflate_s_inflate(obj, src)
+ VALUE obj, src;
+{
+ struct zstream z;
+ VALUE dst;
+ int err;
+
+ zstream_init_inflate(&z);
+ err = inflateInit(&z.stream);
+ if (err != Z_OK) {
+ raise_zlib_error(err, z.stream.msg);
+ }
+ ZSTREAM_READY(&z);
+
+ StringValue(src);
+ zstream_run(&z, RSTRING(src)->ptr, RSTRING(src)->len, Z_SYNC_FLUSH);
+ zstream_run(&z, "", 0, Z_FINISH); /* for checking errors */
+ dst = zstream_detach_buffer(&z);
+ zstream_end(&z);
+
+ OBJ_INFECT(dst, src);
+ return dst;
+}
+
+static void
+do_inflate(z, src)
+ struct zstream *z;
+ VALUE src;
+{
+ if (NIL_P(src)) {
+ zstream_run(z, "", 0, Z_FINISH);
+ return;
+ }
+ StringValue(src);
+ if (RSTRING(src)->len > 0) { /* prevent Z_BUF_ERROR */
+ zstream_run(z, RSTRING(src)->ptr, RSTRING(src)->len, Z_SYNC_FLUSH);
+ }
+}
+
+static VALUE
+rb_inflate_inflate(obj, src)
+ VALUE obj, src;
+{
+ struct zstream *z = get_zstream(obj);
+ VALUE dst;
+
+ OBJ_INFECT(obj, src);
+
+ if (ZSTREAM_IS_FINISHED(z)) {
+ if (NIL_P(src)) {
+ dst = zstream_detach_buffer(z);
+ }
+ else {
+ StringValue(src);
+ zstream_append_buffer2(z, src);
+ dst = rb_str_new(0, 0);
+ }
+ }
+ else {
+ do_inflate(z, src);
+ dst = zstream_detach_buffer(z);
+ if (ZSTREAM_IS_FINISHED(z)) {
+ zstream_passthrough_input(z);
+ }
+ }
+
+ OBJ_INFECT(dst, obj);
+ return dst;
+}
+
+static VALUE
+rb_inflate_addstr(obj, src)
+ VALUE obj, src;
+{
+ struct zstream *z = get_zstream(obj);
+
+ OBJ_INFECT(obj, src);
+
+ if (ZSTREAM_IS_FINISHED(z)) {
+ if (!NIL_P(src)) {
+ StringValue(src);
+ zstream_append_buffer2(z, src);
+ }
+ }
+ else {
+ do_inflate(z, src);
+ if (ZSTREAM_IS_FINISHED(z)) {
+ zstream_passthrough_input(z);
+ }
+ }
+
+ return obj;
+}
+
+static VALUE
+rb_inflate_sync(obj, src)
+ VALUE obj, src;
+{
+ struct zstream *z = get_zstream(obj);
+
+ OBJ_INFECT(obj, src);
+ StringValue(src);
+ return zstream_sync(z, RSTRING(src)->ptr, RSTRING(src)->len);
+}
+
+static VALUE
+rb_inflate_sync_point_p(obj)
+ VALUE obj;
+{
+ struct zstream *z = get_zstream(obj);
+ int err;
+
+ err = inflateSyncPoint(&z->stream);
+ if (err == 1) {
+ return Qtrue;
+ }
+ if (err != Z_OK) {
+ raise_zlib_error(err, z->stream.msg);
+ }
+ return Qfalse;
+}
+
+static VALUE
+rb_inflate_set_dictionary(obj, dic)
+ VALUE obj, dic;
+{
+ struct zstream *z = get_zstream(obj);
+ VALUE src = dic;
+ int err;
+
+ OBJ_INFECT(obj, dic);
+ StringValue(src);
+ err = inflateSetDictionary(&z->stream,
+ RSTRING(src)->ptr, RSTRING(src)->len);
+ if (err != Z_OK) {
+ raise_zlib_error(err, z->stream.msg);
+ }
+
+ return dic;
+}
+
+
+
+#if GZIP_SUPPORT
+
+/* NOTE: Features for gzip files of Ruby/zlib are written from scratch
+ * and using undocumented feature of zlib, negative wbits.
+ * I don't think gzFile APIs of zlib are good for Ruby.
+ */
+
+/*------- .gz file header --------*/
+
+#define GZ_MAGIC1 0x1f
+#define GZ_MAGIC2 0x8b
+#define GZ_METHOD_DEFLATE 8
+#define GZ_FLAG_MULTIPART 0x2
+#define GZ_FLAG_EXTRA 0x4
+#define GZ_FLAG_ORIG_NAME 0x8
+#define GZ_FLAG_COMMENT 0x10
+#define GZ_FLAG_ENCRYPT 0x20
+#define GZ_FLAG_UNKNOWN_MASK 0xc0
+
+#define GZ_EXTRAFLAG_FAST 0x4
+#define GZ_EXTRAFLAG_SLOW 0x2
+
+/* from zutil.h */
+#define OS_MSDOS 0x00
+#define OS_AMIGA 0x01
+#define OS_VMS 0x02
+#define OS_UNIX 0x03
+#define OS_ATARI 0x05
+#define OS_OS2 0x06
+#define OS_MACOS 0x07
+#define OS_TOPS20 0x0a
+#define OS_WIN32 0x0b
+
+#define OS_VMCMS 0x04
+#define OS_ZSYSTEM 0x08
+#define OS_CPM 0x09
+#define OS_QDOS 0x0c
+#define OS_RISCOS 0x0d
+#define OS_UNKNOWN 0xff
+
+#ifndef OS_CODE
+#define OS_CODE OS_UNIX
+#endif
+
+static ID id_write, id_read, id_flush, id_seek, id_close;
+static VALUE cGzError, cNoFooter, cCRCError, cLengthError;
+
+
+
+/*-------- gzfile internal APIs --------*/
+
+struct gzfile {
+ struct zstream z;
+ VALUE io;
+ int level;
+ time_t mtime; /* for header */
+ int os_code; /* for header */
+ VALUE orig_name; /* for header; must be a String */
+ VALUE comment; /* for header; must be a String */
+ unsigned long crc;
+ int lineno;
+ int ungetc;
+ void (*end)(struct gzfile *);
+};
+
+#define GZFILE_FLAG_SYNC ZSTREAM_FLAG_UNUSED
+#define GZFILE_FLAG_HEADER_FINISHED (ZSTREAM_FLAG_UNUSED << 1)
+#define GZFILE_FLAG_FOOTER_FINISHED (ZSTREAM_FLAG_UNUSED << 2)
+
+#define GZFILE_IS_FINISHED(gz) \
+ (ZSTREAM_IS_FINISHED(&gz->z) && (gz)->z.buf_filled == 0)
+
+#define GZFILE_READ_SIZE 2048
+
+
+static void
+gzfile_mark(gz)
+ struct gzfile *gz;
+{
+ rb_gc_mark(gz->io);
+ rb_gc_mark(gz->orig_name);
+ rb_gc_mark(gz->comment);
+ zstream_mark(&gz->z);
+}
+
+static void
+gzfile_free(gz)
+ struct gzfile *gz;
+{
+ gz->z.flags |= ZSTREAM_FLAG_FINALIZE;
+ if (ZSTREAM_IS_READY(&gz->z)) {
+ gz->end(gz);
+ }
+ free(gz);
+}
+
+static VALUE
+gzfile_new(klass, funcs, endfunc)
+ VALUE klass;
+ const struct zstream_funcs *funcs;
+ void (*endfunc) _((struct gzfile *));
+{
+ VALUE obj;
+ struct gzfile *gz;
+
+ obj = Data_Make_Struct(klass, struct gzfile, gzfile_mark, gzfile_free, gz);
+ zstream_init(&gz->z, funcs);
+ gz->io = Qnil;
+ gz->level = 0;
+ gz->mtime = 0;
+ gz->os_code = OS_CODE;
+ gz->orig_name = Qnil;
+ gz->comment = Qnil;
+ gz->crc = crc32(0, Z_NULL, 0);
+ gz->lineno = 0;
+ gz->ungetc = 0;
+ gz->end = endfunc;
+
+ return obj;
+}
+
+#define gzfile_writer_new(gz) gzfile_new((gz),&deflate_funcs,gzfile_writer_end)
+#define gzfile_reader_new(gz) gzfile_new((gz),&inflate_funcs,gzfile_reader_end)
+
+static void
+gzfile_reset(gz)
+ struct gzfile *gz;
+{
+ zstream_reset(&gz->z);
+ gz->crc = crc32(0, Z_NULL, 0);
+ gz->lineno = 0;
+ gz->ungetc = 0;
+}
+
+static void
+gzfile_close(gz, closeflag)
+ struct gzfile *gz;
+ int closeflag;
+{
+ VALUE io = gz->io;
+ gz->end(gz);
+ gz->io = Qnil;
+ gz->orig_name = Qnil;
+ gz->comment = Qnil;
+ if (closeflag && rb_respond_to(io, id_close)) {
+ rb_funcall(io, id_close, 0);
+ }
+}
+
+static void
+gzfile_write_raw(gz)
+ struct gzfile *gz;
+{
+ VALUE str;
+
+ if (gz->z.buf_filled > 0) {
+ str = zstream_detach_buffer(&gz->z);
+ OBJ_TAINT(str); /* for safe */
+ rb_funcall(gz->io, id_write, 1, str);
+ if ((gz->z.flags & GZFILE_FLAG_SYNC)
+ && rb_respond_to(gz->io, id_flush))
+ rb_funcall(gz->io, id_flush, 0);
+ }
+}
+
+static VALUE
+gzfile_read_raw(gz)
+ struct gzfile *gz;
+{
+ VALUE str;
+
+ str = rb_funcall(gz->io, id_read, 1, INT2FIX(GZFILE_READ_SIZE));
+ if (!NIL_P(str)) {
+ Check_Type(str, T_STRING);
+ }
+ return str;
+}
+
+static int
+gzfile_read_raw_ensure(gz, size)
+ struct gzfile *gz;
+ int size;
+{
+ VALUE str;
+
+ while (NIL_P(gz->z.input) || RSTRING(gz->z.input)->len < size) {
+ str = gzfile_read_raw(gz);
+ if (NIL_P(str)) return Qfalse;
+ zstream_append_input2(&gz->z, str);
+ }
+ return Qtrue;
+}
+
+static char *
+gzfile_read_raw_until_zero(gz, offset)
+ struct gzfile *gz;
+ long offset;
+{
+ VALUE str;
+ char *p;
+
+ for (;;) {
+ p = memchr(RSTRING(gz->z.input)->ptr + offset, '\0',
+ RSTRING(gz->z.input)->len - offset);
+ if (p) break;
+ str = gzfile_read_raw(gz);
+ if (NIL_P(str)) {
+ rb_raise(cGzError, "unexpected end of file");
+ }
+ offset = RSTRING(gz->z.input)->len;
+ zstream_append_input2(&gz->z, str);
+ }
+ return p;
+}
+
+static unsigned int
+gzfile_get16(src)
+ const unsigned char *src;
+{
+ unsigned int n;
+ n = *(src++) & 0xff;
+ n |= (*(src++) & 0xff) << 8;
+ return n;
+}
+
+static unsigned long
+gzfile_get32(src)
+ const unsigned char *src;
+{
+ unsigned long n;
+ n = *(src++) & 0xff;
+ n |= (*(src++) & 0xff) << 8;
+ n |= (*(src++) & 0xff) << 16;
+ n |= (*(src++) & 0xff) << 24;
+ return n;
+}
+
+static void
+gzfile_set32(n, dst)
+ unsigned long n;
+ unsigned char *dst;
+{
+ *(dst++) = n & 0xff;
+ *(dst++) = (n >> 8) & 0xff;
+ *(dst++) = (n >> 16) & 0xff;
+ *dst = (n >> 24) & 0xff;
+}
+
+static void
+gzfile_make_header(gz)
+ struct gzfile *gz;
+{
+ unsigned char buf[10]; /* the size of gzip header */
+ unsigned char flags = 0, extraflags = 0;
+
+ if (!NIL_P(gz->orig_name)) {
+ flags |= GZ_FLAG_ORIG_NAME;
+ }
+ if (!NIL_P(gz->comment)) {
+ flags |= GZ_FLAG_COMMENT;
+ }
+ if (gz->mtime == 0) {
+ gz->mtime = time(0);
+ }
+
+ if (gz->level == Z_BEST_SPEED) {
+ extraflags |= GZ_EXTRAFLAG_FAST;
+ }
+ else if (gz->level == Z_BEST_COMPRESSION) {
+ extraflags |= GZ_EXTRAFLAG_SLOW;
+ }
+
+ buf[0] = GZ_MAGIC1;
+ buf[1] = GZ_MAGIC2;
+ buf[2] = GZ_METHOD_DEFLATE;
+ buf[3] = flags;
+ gzfile_set32(gz->mtime, &buf[4]);
+ buf[8] = extraflags;
+ buf[9] = gz->os_code;
+ zstream_append_buffer(&gz->z, buf, sizeof(buf));
+
+ if (!NIL_P(gz->orig_name)) {
+ zstream_append_buffer2(&gz->z, gz->orig_name);
+ zstream_append_buffer(&gz->z, "\0", 1);
+ }
+ if (!NIL_P(gz->comment)) {
+ zstream_append_buffer2(&gz->z, gz->comment);
+ zstream_append_buffer(&gz->z, "\0", 1);
+ }
+
+ gz->z.flags |= GZFILE_FLAG_HEADER_FINISHED;
+}
+
+static void
+gzfile_make_footer(gz)
+ struct gzfile *gz;
+{
+ unsigned char buf[8]; /* 8 is the size of gzip footer */
+
+ gzfile_set32(gz->crc, buf);
+ gzfile_set32(gz->z.stream.total_in, &buf[4]);
+ zstream_append_buffer(&gz->z, buf, sizeof(buf));
+ gz->z.flags |= GZFILE_FLAG_FOOTER_FINISHED;
+}
+
+static void
+gzfile_read_header(gz)
+ struct gzfile *gz;
+{
+ const unsigned char *head;
+ long len;
+ char flags, *p;
+
+ if (!gzfile_read_raw_ensure(gz, 10)) { /* 10 is the size of gzip header */
+ rb_raise(cGzError, "not in gzip format");
+ }
+
+ head = RSTRING(gz->z.input)->ptr;
+
+ if (head[0] != GZ_MAGIC1 || head[1] != GZ_MAGIC2) {
+ rb_raise(cGzError, "not in gzip format");
+ }
+ if (head[2] != GZ_METHOD_DEFLATE) {
+ rb_raise(cGzError, "unsupported compression method %d", head[2]);
+ }
+
+ flags = head[3];
+ if (flags & GZ_FLAG_MULTIPART) {
+ rb_raise(cGzError, "multi-part gzip file is not supported");
+ }
+ else if (flags & GZ_FLAG_ENCRYPT) {
+ rb_raise(cGzError, "encrypted gzip file is not supported");
+ }
+ else if (flags & GZ_FLAG_UNKNOWN_MASK) {
+ rb_raise(cGzError, "unknown flags 0x%02x", flags);
+ }
+
+ if (head[8] & GZ_EXTRAFLAG_FAST) {
+ gz->level = Z_BEST_SPEED;
+ }
+ else if (head[8] & GZ_EXTRAFLAG_SLOW) {
+ gz->level = Z_BEST_COMPRESSION;
+ }
+ else {
+ gz->level = Z_DEFAULT_COMPRESSION;
+ }
+
+ gz->mtime = gzfile_get32(&head[4]);
+ gz->os_code = head[9];
+ zstream_discard_input(&gz->z, 10);
+
+ if (flags & GZ_FLAG_EXTRA) {
+ if (!gzfile_read_raw_ensure(gz, 2)) {
+ rb_raise(cGzError, "unexpected end of file");
+ }
+ len = gzfile_get16(RSTRING(gz->z.input)->ptr);
+ if (!gzfile_read_raw_ensure(gz, 2 + len)) {
+ rb_raise(cGzError, "unexpected end of file");
+ }
+ zstream_discard_input(&gz->z, 2 + len);
+ }
+ if (flags & GZ_FLAG_ORIG_NAME) {
+ p = gzfile_read_raw_until_zero(gz, 0);
+ len = p - RSTRING(gz->z.input)->ptr;
+ gz->orig_name = rb_str_new(RSTRING(gz->z.input)->ptr, len);
+ OBJ_TAINT(gz->orig_name); /* for safe */
+ zstream_discard_input(&gz->z, len + 1);
+ }
+ if (flags & GZ_FLAG_COMMENT) {
+ p = gzfile_read_raw_until_zero(gz, 0);
+ len = p - RSTRING(gz->z.input)->ptr;
+ gz->comment = rb_str_new(RSTRING(gz->z.input)->ptr, len);
+ OBJ_TAINT(gz->comment); /* for safe */
+ zstream_discard_input(&gz->z, len + 1);
+ }
+
+ if (gz->z.input != Qnil && RSTRING(gz->z.input)->len > 0) {
+ zstream_run(&gz->z, 0, 0, Z_SYNC_FLUSH);
+ }
+}
+
+static void
+gzfile_check_footer(gz)
+ struct gzfile *gz;
+{
+ unsigned long crc, length;
+
+ gz->z.flags |= GZFILE_FLAG_FOOTER_FINISHED;
+
+ if (!gzfile_read_raw_ensure(gz, 8)) { /* 8 is the size of gzip footer */
+ rb_raise(cNoFooter, "footer is not found");
+ }
+
+ crc = gzfile_get32(RSTRING(gz->z.input)->ptr);
+ length = gzfile_get32(RSTRING(gz->z.input)->ptr + 4);
+
+ gz->z.stream.total_in += 8; /* to rewind correctly */
+ zstream_discard_input(&gz->z, 8);
+
+ if (gz->crc != crc) {
+ rb_raise(cCRCError, "invalid compressed data -- crc error");
+ }
+ if (gz->z.stream.total_out != length) {
+ rb_raise(cLengthError, "invalid compressed data -- length error");
+ }
+}
+
+static void
+gzfile_write(gz, str, len)
+ struct gzfile *gz;
+ Bytef *str;
+ uInt len;
+{
+ if (!(gz->z.flags & GZFILE_FLAG_HEADER_FINISHED)) {
+ gzfile_make_header(gz);
+ }
+
+ if (len > 0 || (gz->z.flags & GZFILE_FLAG_SYNC)) {
+ gz->crc = crc32(gz->crc, str, len);
+ zstream_run(&gz->z, str, len, (gz->z.flags & GZFILE_FLAG_SYNC)
+ ? Z_SYNC_FLUSH : Z_NO_FLUSH);
+ }
+ gzfile_write_raw(gz);
+}
+
+static long
+gzfile_read_more(gz)
+ struct gzfile *gz;
+{
+ VALUE str;
+
+ while (!ZSTREAM_IS_FINISHED(&gz->z)) {
+ str = gzfile_read_raw(gz);
+ if (NIL_P(str)) {
+ if (!ZSTREAM_IS_FINISHED(&gz->z)) {
+ rb_raise(cGzError, "unexpected end of file");
+ }
+ break;
+ }
+ if (RSTRING(str)->len > 0) { /* prevent Z_BUF_ERROR */
+ zstream_run(&gz->z, RSTRING(str)->ptr, RSTRING(str)->len,
+ Z_SYNC_FLUSH);
+ }
+ if (gz->z.buf_filled > 0) break;
+ }
+ return gz->z.buf_filled;
+}
+
+static VALUE
+gzfile_read(gz, len)
+ struct gzfile *gz;
+ int len;
+{
+ VALUE dst;
+
+ if (len <= 0) return Qnil;
+ while (!ZSTREAM_IS_FINISHED(&gz->z) && gz->z.buf_filled < len) {
+ gzfile_read_more(gz);
+ }
+ if (GZFILE_IS_FINISHED(gz)) {
+ if (!(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) {
+ gzfile_check_footer(gz);
+ }
+ return Qnil;
+ }
+
+ dst = zstream_shift_buffer(&gz->z, len);
+ if (RSTRING(dst)->len <= gz->ungetc) {
+ gz->ungetc -= RSTRING(dst)->len;
+ }
+ else {
+ gz->crc = crc32(gz->crc, RSTRING(dst)->ptr + gz->ungetc,
+ RSTRING(dst)->len - gz->ungetc);
+ }
+
+ OBJ_TAINT(dst); /* for safe */
+ return dst;
+}
+
+static VALUE
+gzfile_read_all(gz)
+ struct gzfile *gz;
+{
+ VALUE dst;
+
+ while (!ZSTREAM_IS_FINISHED(&gz->z)) {
+ gzfile_read_more(gz);
+ }
+ if (GZFILE_IS_FINISHED(gz)) {
+ if (!(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) {
+ gzfile_check_footer(gz);
+ }
+ return Qnil;
+ }
+
+ dst = zstream_detach_buffer(&gz->z);
+ if (RSTRING(dst)->len <= gz->ungetc) {
+ gz->ungetc -= RSTRING(dst)->len;
+ }
+ else {
+ gz->crc = crc32(gz->crc, RSTRING(dst)->ptr + gz->ungetc,
+ RSTRING(dst)->len - gz->ungetc);
+ }
+
+ OBJ_TAINT(dst); /* for safe */
+ return dst;
+}
+
+static void
+gzfile_ungetc(gz, c)
+ struct gzfile *gz;
+ int c;
+{
+ zstream_buffer_ungetc(&gz->z, c);
+ gz->ungetc++;
+}
+
+static VALUE
+gzfile_finalize(obj)
+ VALUE obj;
+{
+ struct gzfile *gz = (struct gzfile *)obj;
+ gzfile_write_raw(gz);
+ return Qnil;
+}
+
+static void
+gzfile_writer_end(gz)
+ struct gzfile *gz;
+{
+ int aborted;
+
+ if (!(gz->z.flags & GZFILE_FLAG_HEADER_FINISHED)) {
+ gzfile_make_header(gz);
+ }
+
+ zstream_run(&gz->z, "", 0, Z_FINISH);
+ gzfile_make_footer(gz);
+
+ if (ZSTREAM_IS_FINALIZE(&gz->z)) {
+ rb_warn("Zlib::GzipWriter object must be closed explicitly.");
+ if (OBJ_IS_FREED(gz->io)) {
+ aborted = 1;
+ }
+ else {
+ rb_protect(gzfile_finalize, (VALUE)gz, &aborted);
+ }
+ if (aborted) {
+ rb_warn("gzip footer is not written; broken gzip file");
+ }
+ zstream_end(&gz->z);
+ return;
+ }
+ gzfile_write_raw(gz);
+ zstream_end(&gz->z);
+}
+
+static void
+gzfile_reader_end(gz)
+ struct gzfile *gz;
+{
+ if (GZFILE_IS_FINISHED(gz)
+ && !ZSTREAM_IS_FINALIZE(&gz->z)
+ && !(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) {
+ gzfile_check_footer(gz);
+ }
+
+ zstream_end(&gz->z);
+}
+
+static void
+gzfile_reader_rewind(gz)
+ struct gzfile *gz;
+{
+ long n;
+
+ n = gz->z.stream.total_in;
+ if (!NIL_P(gz->z.input)) {
+ n += RSTRING(gz->z.input)->len;
+ }
+
+ rb_funcall(gz->io, id_seek, 2, rb_int2inum(-n), INT2FIX(1));
+ gzfile_reset(gz);
+}
+
+static VALUE
+gzfile_reader_get_unused(gz)
+ struct gzfile *gz;
+{
+ VALUE str;
+
+ if (!ZSTREAM_IS_READY(&gz->z)) return Qnil;
+ if (!GZFILE_IS_FINISHED(gz)) return Qnil;
+ if (!(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) {
+ gzfile_check_footer(gz);
+ }
+ if (NIL_P(gz->z.input)) return Qnil;
+
+ str = rb_str_dup(gz->z.input);
+ OBJ_TAINT(str); /* for safe */
+ return str;
+}
+
+static struct gzfile *
+get_gzfile(obj)
+ VALUE obj;
+{
+ struct gzfile *gz;
+
+ Data_Get_Struct(obj, struct gzfile, gz);
+ if (!ZSTREAM_IS_READY(&gz->z)) {
+ rb_raise(cGzError, "closed gzip stream");
+ }
+ return gz;
+}
+
+
+
+/*-------- class Zlib::GzipFile --------*/
+
+static VALUE
+gzfile_ensure_close(obj)
+ VALUE obj;
+{
+ struct gzfile *gz;
+
+ Data_Get_Struct(obj, struct gzfile, gz);
+ if (ZSTREAM_IS_READY(&gz->z)) {
+ gzfile_close(gz, 1);
+ }
+ return Qnil;
+}
+
+static VALUE
+rb_gzfile_s_wrap(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ VALUE obj = rb_class_new_instance(argc, argv, klass);
+
+ if (rb_block_given_p()) {
+ return rb_ensure(rb_yield, obj, gzfile_ensure_close, obj);
+ }
+ else {
+ return obj;
+ }
+}
+
+static VALUE
+gzfile_s_open(argc, argv, klass, mode)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+ const char *mode;
+{
+ VALUE io, filename;
+
+ if (argc < 1) {
+ rb_raise(rb_eArgError, "wrong number of arguments (0 for 1)");
+ }
+ filename = argv[0];
+ SafeStringValue(filename);
+ io = rb_file_open(RSTRING(filename)->ptr, mode);
+
+ argv[0] = io;
+ return rb_gzfile_s_wrap(argc, argv, klass);
+}
+
+static VALUE
+rb_gzfile_to_io(obj)
+ VALUE obj;
+{
+ return get_gzfile(obj)->io;
+}
+
+static VALUE
+rb_gzfile_crc(obj)
+ VALUE obj;
+{
+ return rb_uint2inum(get_gzfile(obj)->crc);
+}
+
+static VALUE
+rb_gzfile_mtime(obj)
+ VALUE obj;
+{
+ return rb_time_new(get_gzfile(obj)->mtime, (time_t)0);
+}
+
+static VALUE
+rb_gzfile_level(obj)
+ VALUE obj;
+{
+ return INT2FIX(get_gzfile(obj)->level);
+}
+
+static VALUE
+rb_gzfile_os_code(obj)
+ VALUE obj;
+{
+ return INT2FIX(get_gzfile(obj)->os_code);
+}
+
+static VALUE
+rb_gzfile_orig_name(obj)
+ VALUE obj;
+{
+ VALUE str = get_gzfile(obj)->orig_name;
+ if (!NIL_P(str)) {
+ str = rb_str_dup(str);
+ }
+ OBJ_TAINT(str); /* for safe */
+ return str;
+}
+
+static VALUE
+rb_gzfile_comment(obj)
+ VALUE obj;
+{
+ VALUE str = get_gzfile(obj)->comment;
+ if (!NIL_P(str)) {
+ str = rb_str_dup(str);
+ }
+ OBJ_TAINT(str); /* for safe */
+ return str;
+}
+
+static VALUE
+rb_gzfile_lineno(obj)
+ VALUE obj;
+{
+ return INT2NUM(get_gzfile(obj)->lineno);
+}
+
+static VALUE
+rb_gzfile_set_lineno(obj, lineno)
+ VALUE obj, lineno;
+{
+ struct gzfile *gz = get_gzfile(obj);
+ gz->lineno = NUM2INT(lineno);
+ return lineno;
+}
+
+static VALUE
+rb_gzfile_set_mtime(obj, mtime)
+ VALUE obj, mtime;
+{
+ struct gzfile *gz = get_gzfile(obj);
+ VALUE val;
+
+ if (gz->z.flags & GZFILE_FLAG_HEADER_FINISHED) {
+ rb_raise(cGzError, "header is already written");
+ }
+
+ if (FIXNUM_P(time)) {
+ gz->mtime = FIX2INT(mtime);
+ }
+ else {
+ val = rb_Integer(mtime);
+ gz->mtime = FIXNUM_P(val) ? FIX2INT(val) : rb_big2ulong(val);
+ }
+ return mtime;
+}
+
+static VALUE
+rb_gzfile_set_orig_name(obj, str)
+ VALUE obj, str;
+{
+ struct gzfile *gz = get_gzfile(obj);
+ VALUE s;
+ char *p;
+
+ if (gz->z.flags & GZFILE_FLAG_HEADER_FINISHED) {
+ rb_raise(cGzError, "header is already written");
+ }
+ s = rb_str_dup(rb_str_to_str(str));
+ p = memchr(RSTRING(s)->ptr, '\0', RSTRING(s)->len);
+ if (p) {
+ rb_str_resize(s, p - RSTRING(s)->ptr);
+ }
+ gz->orig_name = s;
+ return str;
+}
+
+static VALUE
+rb_gzfile_set_comment(obj, str)
+ VALUE obj, str;
+{
+ struct gzfile *gz = get_gzfile(obj);
+ VALUE s;
+ char *p;
+
+ if (gz->z.flags & GZFILE_FLAG_HEADER_FINISHED) {
+ rb_raise(cGzError, "header is already written");
+ }
+ s = rb_str_dup(rb_str_to_str(str));
+ p = memchr(RSTRING(s)->ptr, '\0', RSTRING(s)->len);
+ if (p) {
+ rb_str_resize(s, p - RSTRING(s)->ptr);
+ }
+ gz->comment = s;
+ return str;
+}
+
+static VALUE
+rb_gzfile_close(obj)
+ VALUE obj;
+{
+ struct gzfile *gz = get_gzfile(obj);
+ VALUE io;
+
+ io = gz->io;
+ gzfile_close(gz, 1);
+ return io;
+}
+
+static VALUE
+rb_gzfile_finish(obj)
+ VALUE obj;
+{
+ struct gzfile *gz = get_gzfile(obj);
+ VALUE io;
+
+ io = gz->io;
+ gzfile_close(gz, 0);
+ return io;
+}
+
+static VALUE
+rb_gzfile_closed_p(obj)
+ VALUE obj;
+{
+ struct gzfile *gz;
+ Data_Get_Struct(obj, struct gzfile, gz);
+ return NIL_P(gz->io) ? Qtrue : Qfalse;
+}
+
+static VALUE
+rb_gzfile_eof_p(obj)
+ VALUE obj;
+{
+ struct gzfile *gz = get_gzfile(obj);
+ return GZFILE_IS_FINISHED(gz) ? Qtrue : Qfalse;
+}
+
+static VALUE
+rb_gzfile_sync(obj)
+ VALUE obj;
+{
+ return (get_gzfile(obj)->z.flags & GZFILE_FLAG_SYNC) ? Qtrue : Qfalse;
+}
+
+static VALUE
+rb_gzfile_set_sync(obj, mode)
+ VALUE obj, mode;
+{
+ struct gzfile *gz = get_gzfile(obj);
+
+ if (RTEST(mode)) {
+ gz->z.flags |= GZFILE_FLAG_SYNC;
+ }
+ else {
+ gz->z.flags &= ~GZFILE_FLAG_SYNC;
+ }
+ return mode;
+}
+
+static VALUE
+rb_gzfile_total_in(obj)
+ VALUE obj;
+{
+ return rb_uint2inum(get_gzfile(obj)->z.stream.total_in);
+}
+
+static VALUE
+rb_gzfile_total_out(obj)
+ VALUE obj;
+{
+ struct gzfile *gz = get_gzfile(obj);
+ return rb_uint2inum(gz->z.stream.total_out - gz->z.buf_filled);
+}
+
+
+
+/*-------- class Zlib::GzipWriter --------*/
+
+static VALUE
+rb_gzwriter_s_allocate(klass)
+ VALUE klass;
+{
+ return gzfile_writer_new(klass);
+}
+
+static VALUE
+rb_gzwriter_s_open(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ return gzfile_s_open(argc, argv, klass, "wb");
+}
+
+static VALUE
+rb_gzwriter_initialize(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ struct gzfile *gz;
+ VALUE io, level, strategy;
+ int err;
+
+ rb_scan_args(argc, argv, "12", &io, &level, &strategy);
+ Data_Get_Struct(obj, struct gzfile, gz);
+
+ /* this is undocumented feature of zlib */
+ gz->level = ARG_LEVEL(level);
+ err = deflateInit2(&gz->z.stream, gz->level, Z_DEFLATED,
+ -MAX_WBITS, DEF_MEM_LEVEL, ARG_STRATEGY(strategy));
+ if (err != Z_OK) {
+ raise_zlib_error(err, gz->z.stream.msg);
+ }
+ gz->io = io;
+ ZSTREAM_READY(&gz->z);
+
+ return obj;
+}
+
+static VALUE
+rb_gzwriter_flush(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ struct gzfile *gz = get_gzfile(obj);
+ VALUE v_flush;
+ int flush;
+
+ rb_scan_args(argc, argv, "01", &v_flush);
+
+ flush = FIXNUMARG(v_flush, Z_SYNC_FLUSH);
+ if (flush != Z_NO_FLUSH) { /* prevent Z_BUF_ERROR */
+ zstream_run(&gz->z, "", 0, flush);
+ }
+
+ gzfile_write_raw(gz);
+ if (rb_respond_to(gz->io, id_flush)) {
+ rb_funcall(gz->io, id_flush, 0);
+ }
+ return obj;
+}
+
+static VALUE
+rb_gzwriter_write(obj, str)
+ VALUE obj, str;
+{
+ struct gzfile *gz = get_gzfile(obj);
+
+ if (TYPE(str) != T_STRING) {
+ str = rb_obj_as_string(str);
+ }
+ gzfile_write(gz, RSTRING(str)->ptr, RSTRING(str)->len);
+ return INT2FIX(RSTRING(str)->len);
+}
+
+static VALUE
+rb_gzwriter_putc(obj, ch)
+ VALUE obj, ch;
+{
+ struct gzfile *gz = get_gzfile(obj);
+ char c = NUM2CHR(ch);
+
+ gzfile_write(gz, &c, 1);
+ return ch;
+}
+
+#define rb_gzwriter_addstr rb_io_addstr
+#define rb_gzwriter_printf rb_io_printf
+#define rb_gzwriter_print rb_io_print
+#define rb_gzwriter_puts rb_io_puts
+
+
+/*-------- class Zlib::GzipReader --------*/
+
+static VALUE
+rb_gzreader_s_allocate(klass)
+ VALUE klass;
+{
+ return gzfile_reader_new(klass);
+}
+
+static VALUE
+rb_gzreader_s_open(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ return gzfile_s_open(argc, argv, klass, "rb");
+}
+
+static VALUE
+rb_gzreader_initialize(obj, io)
+ VALUE obj, io;
+{
+ struct gzfile *gz;
+ int err;
+
+ Data_Get_Struct(obj, struct gzfile, gz);
+
+ /* this is undocumented feature of zlib */
+ err = inflateInit2(&gz->z.stream, -MAX_WBITS);
+ if (err != Z_OK) {
+ raise_zlib_error(err, gz->z.stream.msg);
+ }
+ gz->io = io;
+ ZSTREAM_READY(&gz->z);
+ gzfile_read_header(gz);
+
+ return obj;
+}
+
+static VALUE
+rb_gzreader_rewind(obj)
+ VALUE obj;
+{
+ struct gzfile *gz = get_gzfile(obj);
+ gzfile_reader_rewind(gz);
+ return INT2FIX(0);
+}
+
+static VALUE
+rb_gzreader_unused(obj)
+ VALUE obj;
+{
+ struct gzfile *gz;
+ Data_Get_Struct(obj, struct gzfile, gz);
+ return gzfile_reader_get_unused(gz);
+}
+
+static VALUE
+rb_gzreader_read(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ struct gzfile *gz = get_gzfile(obj);
+ VALUE vlen;
+ int len;
+
+ rb_scan_args(argc, argv, "01", &vlen);
+ if (NIL_P(vlen)) {
+ return gzfile_read_all(gz);
+ }
+
+ len = NUM2INT(vlen);
+ if (len < 0) {
+ rb_raise(rb_eArgError, "negative length %d given", len);
+ }
+ return gzfile_read(gz, len);
+}
+
+static VALUE
+rb_gzreader_getc(obj)
+ VALUE obj;
+{
+ struct gzfile *gz = get_gzfile(obj);
+ VALUE dst;
+
+ dst = gzfile_read(gz, 1);
+ if (!NIL_P(dst)) {
+ dst = INT2FIX((unsigned int)(RSTRING(dst)->ptr[0]) & 0xff);
+ }
+ return dst;
+}
+
+static VALUE
+rb_gzreader_readchar(obj)
+ VALUE obj;
+{
+ VALUE dst;
+ dst = rb_gzreader_getc(obj);
+ if (NIL_P(dst)) {
+ rb_raise(rb_eEOFError, "End of file reached");
+ }
+ return dst;
+}
+
+static VALUE
+rb_gzreader_each_byte(obj)
+ VALUE obj;
+{
+ VALUE c;
+ while (!NIL_P(c = rb_gzreader_getc(obj))) {
+ rb_yield(c);
+ }
+ return Qnil;
+}
+
+static VALUE
+rb_gzreader_ungetc(obj, ch)
+ VALUE obj, ch;
+{
+ struct gzfile *gz = get_gzfile(obj);
+ gzfile_ungetc(gz, NUM2CHR(ch));
+ return Qnil;
+}
+
+static void
+gzreader_skip_linebreaks(gz)
+ struct gzfile *gz;
+{
+ VALUE str;
+ char *p;
+ int n;
+
+ while (gz->z.buf_filled == 0) {
+ if (GZFILE_IS_FINISHED(gz)) return;
+ gzfile_read_more(gz);
+ }
+ n = 0;
+ p = RSTRING(gz->z.buf)->ptr;
+
+ while (n++, *(p++) == '\n') {
+ if (n >= gz->z.buf_filled) {
+ str = zstream_detach_buffer(&gz->z);
+ gz->crc = crc32(gz->crc, RSTRING(str)->ptr,
+ RSTRING(str)->len);
+ while (gz->z.buf_filled == 0) {
+ if (GZFILE_IS_FINISHED(gz)) return;
+ gzfile_read_more(gz);
+ }
+ n = 0;
+ p = RSTRING(gz->z.buf)->ptr;
+ }
+ }
+
+ str = zstream_shift_buffer(&gz->z, n - 1);
+ gz->crc = crc32(gz->crc, RSTRING(str)->ptr, RSTRING(str)->len);
+}
+
+static VALUE
+gzreader_gets(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ struct gzfile *gz = get_gzfile(obj);
+ VALUE rs, dst;
+ char *rsptr, *p;
+ long rslen, n;
+ int rspara;
+
+ if (argc == 0) {
+ rs = rb_rs;
+ }
+ else {
+ rb_scan_args(argc, argv, "1", &rs);
+ if (!NIL_P(rs)) {
+ Check_Type(rs, T_STRING);
+ }
+ }
+
+ if (NIL_P(rs)) {
+ dst = gzfile_read_all(gz);
+ if (!NIL_P(dst)) gz->lineno++;
+ return dst;
+ }
+
+ if (RSTRING(rs)->len == 0) {
+ rsptr = "\n\n";
+ rslen = 2;
+ rspara = 1;
+ } else {
+ rsptr = RSTRING(rs)->ptr;
+ rslen = RSTRING(rs)->len;
+ rspara = 0;
+ }
+
+ if (rspara) {
+ gzreader_skip_linebreaks(gz);
+ }
+
+ while (gz->z.buf_filled < rslen) {
+ if (ZSTREAM_IS_FINISHED(&gz->z)) {
+ if (gz->z.buf_filled > 0) gz->lineno++;
+ return gzfile_read(gz, rslen);
+ }
+ gzfile_read_more(gz);
+ }
+
+ n = rslen;
+ p = RSTRING(gz->z.buf)->ptr;
+ for (;;) {
+ if (n > gz->z.buf_filled) {
+ if (ZSTREAM_IS_FINISHED(&gz->z)) break;
+ gzfile_read_more(gz);
+ p = RSTRING(gz->z.buf)->ptr + n - rslen;
+ }
+ if (memcmp(p, rsptr, rslen) == 0) break;
+ p++, n++;
+ }
+
+ gz->lineno++;
+ dst = gzfile_read(gz, n);
+ if (rspara) {
+ gzreader_skip_linebreaks(gz);
+ }
+
+ return dst;
+}
+
+static VALUE
+rb_gzreader_gets(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE dst;
+ dst = gzreader_gets(argc, argv, obj);
+ if (!NIL_P(dst)) {
+ rb_lastline_set(dst);
+ }
+ return dst;
+}
+
+static VALUE
+rb_gzreader_readline(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE dst;
+ dst = rb_gzreader_gets(argc, argv, obj);
+ if (NIL_P(dst)) {
+ rb_raise(rb_eEOFError, "End of file reached");
+ }
+ return dst;
+}
+
+static VALUE
+rb_gzreader_each(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE str;
+ while (!NIL_P(str = gzreader_gets(argc, argv, obj))) {
+ rb_yield(str);
+ }
+ return obj;
+}
+
+static VALUE
+rb_gzreader_readlines(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE str, dst;
+ dst = rb_ary_new();
+ while (!NIL_P(str = gzreader_gets(argc, argv, obj))) {
+ rb_ary_push(dst, str);
+ }
+ return dst;
+}
+
+#endif /* GZIP_SUPPORT */
+
+
+void Init_zlib()
+{
+ VALUE mZlib, cZStream, cDeflate, cInflate;
+#if GZIP_SUPPORT
+ VALUE cGzipFile, cGzipWriter, cGzipReader;
+#endif
+
+ mZlib = rb_define_module("Zlib");
+
+ cZError = rb_define_class_under(mZlib, "Error", rb_eStandardError);
+ cStreamEnd = rb_define_class_under(mZlib, "StreamEnd", cZError);
+ cNeedDict = rb_define_class_under(mZlib, "NeedDict", cZError);
+ cDataError = rb_define_class_under(mZlib, "DataError", cZError);
+ cStreamError = rb_define_class_under(mZlib, "StreamError", cZError);
+ cMemError = rb_define_class_under(mZlib, "MemError", cZError);
+ cBufError = rb_define_class_under(mZlib, "BufError", cZError);
+ cVersionError = rb_define_class_under(mZlib, "VersionError", cZError);
+
+ rb_define_module_function(mZlib, "zlib_version", rb_zlib_version, 0);
+ rb_define_module_function(mZlib, "adler32", rb_zlib_adler32, -1);
+ rb_define_module_function(mZlib, "crc32", rb_zlib_crc32, -1);
+ rb_define_module_function(mZlib, "crc_table", rb_zlib_crc_table, 0);
+
+ rb_define_const(mZlib, "VERSION", rb_str_new2(RUBY_ZLIB_VERSION));
+ rb_define_const(mZlib, "ZLIB_VERSION", rb_str_new2(ZLIB_VERSION));
+
+ cZStream = rb_define_class_under(mZlib, "ZStream", rb_cObject);
+ rb_undef_alloc_func(cZStream);
+ rb_define_method(cZStream, "avail_out", rb_zstream_avail_out, 0);
+ rb_define_method(cZStream, "avail_out=", rb_zstream_set_avail_out, 0);
+ rb_define_method(cZStream, "avail_in", rb_zstream_avail_in, 0);
+ rb_define_method(cZStream, "total_in", rb_zstream_total_in, 0);
+ rb_define_method(cZStream, "total_out", rb_zstream_total_out, 0);
+ rb_define_method(cZStream, "data_type", rb_zstream_data_type, 0);
+ rb_define_method(cZStream, "adler", rb_zstream_adler, 0);
+ rb_define_method(cZStream, "finished?", rb_zstream_finished_p, 0);
+ rb_define_method(cZStream, "stream_end?", rb_zstream_finished_p, 0);
+ rb_define_method(cZStream, "closed?", rb_zstream_closed_p, 0);
+ rb_define_method(cZStream, "ended?", rb_zstream_closed_p, 0);
+ rb_define_method(cZStream, "close", rb_zstream_end, 0);
+ rb_define_method(cZStream, "end", rb_zstream_end, 0);
+ rb_define_method(cZStream, "reset", rb_zstream_reset, 0);
+ rb_define_method(cZStream, "finish", rb_zstream_finish, 0);
+ rb_define_method(cZStream, "flush_next_in", rb_zstream_flush_next_in, 0);
+ rb_define_method(cZStream, "flush_next_out", rb_zstream_flush_next_out, 0);
+
+ rb_define_const(mZlib, "BINARY", INT2FIX(Z_BINARY));
+ rb_define_const(mZlib, "ASCII", INT2FIX(Z_ASCII));
+ rb_define_const(mZlib, "UNKNOWN", INT2FIX(Z_UNKNOWN));
+
+ cDeflate = rb_define_class_under(mZlib, "Deflate", cZStream);
+ rb_define_singleton_method(cDeflate, "deflate", rb_deflate_s_deflate, -1);
+ rb_define_alloc_func(cDeflate, rb_deflate_s_allocate);
+ rb_define_method(cDeflate, "initialize", rb_deflate_initialize, -1);
+ rb_define_method(cDeflate, "clone", rb_deflate_clone, 0);
+ rb_define_method(cDeflate, "deflate", rb_deflate_deflate, -1);
+ rb_define_method(cDeflate, "<<", rb_deflate_addstr, 1);
+ rb_define_method(cDeflate, "flush", rb_deflate_flush, -1);
+ rb_define_method(cDeflate, "params", rb_deflate_params, 2);
+ rb_define_method(cDeflate, "set_dictionary", rb_deflate_set_dictionary, 1);
+
+ cInflate = rb_define_class_under(mZlib, "Inflate", cZStream);
+ rb_define_singleton_method(cInflate, "inflate", rb_inflate_s_inflate, 1);
+ rb_define_alloc_func(cInflate, rb_inflate_s_allocate);
+ rb_define_method(cInflate, "initialize", rb_inflate_initialize, -1);
+ rb_define_method(cInflate, "inflate", rb_inflate_inflate, 1);
+ rb_define_method(cInflate, "<<", rb_inflate_addstr, 1);
+ rb_define_method(cInflate, "sync", rb_inflate_sync, 1);
+ rb_define_method(cInflate, "sync_point?", rb_inflate_sync_point_p, 0);
+ rb_define_method(cInflate, "set_dictionary", rb_inflate_set_dictionary, 1);
+
+ rb_define_const(mZlib, "NO_COMPRESSION", INT2FIX(Z_NO_COMPRESSION));
+ rb_define_const(mZlib, "BEST_SPEED", INT2FIX(Z_BEST_SPEED));
+ rb_define_const(mZlib, "BEST_COMPRESSION", INT2FIX(Z_BEST_COMPRESSION));
+ rb_define_const(mZlib, "DEFAULT_COMPRESSION",
+ INT2FIX(Z_DEFAULT_COMPRESSION));
+
+ rb_define_const(mZlib, "FILTERED", INT2FIX(Z_FILTERED));
+ rb_define_const(mZlib, "HUFFMAN_ONLY", INT2FIX(Z_HUFFMAN_ONLY));
+ rb_define_const(mZlib, "DEFAULT_STRATEGY", INT2FIX(Z_DEFAULT_STRATEGY));
+
+ rb_define_const(mZlib, "MAX_WBITS", INT2FIX(MAX_WBITS));
+ rb_define_const(mZlib, "DEF_MEM_LEVEL", INT2FIX(DEF_MEM_LEVEL));
+ rb_define_const(mZlib, "MAX_MEM_LEVEL", INT2FIX(MAX_MEM_LEVEL));
+
+ rb_define_const(mZlib, "NO_FLUSH", INT2FIX(Z_NO_FLUSH));
+ rb_define_const(mZlib, "SYNC_FLUSH", INT2FIX(Z_SYNC_FLUSH));
+ rb_define_const(mZlib, "FULL_FLUSH", INT2FIX(Z_FULL_FLUSH));
+ rb_define_const(mZlib, "FINISH", INT2FIX(Z_FINISH));
+
+#if GZIP_SUPPORT
+ id_write = rb_intern("write");
+ id_read = rb_intern("read");
+ id_flush = rb_intern("flush");
+ id_seek = rb_intern("seek");
+ id_close = rb_intern("close");
+
+ cGzipFile = rb_define_class_under(mZlib, "GzipFile", rb_cObject);
+ cGzError = rb_define_class_under(cGzipFile, "Error", cZError);
+
+ cNoFooter = rb_define_class_under(cGzipFile, "NoFooter", cGzError);
+ cCRCError = rb_define_class_under(cGzipFile, "CRCError", cGzError);
+ cLengthError = rb_define_class_under(cGzipFile,"LengthError",cGzError);
+
+ cGzipWriter = rb_define_class_under(mZlib, "GzipWriter", cGzipFile);
+ cGzipReader = rb_define_class_under(mZlib, "GzipReader", cGzipFile);
+ rb_include_module(cGzipReader, rb_mEnumerable);
+
+ rb_define_singleton_method(cGzipFile, "wrap", rb_gzfile_s_wrap, -1);
+ rb_undef_alloc_func(cGzipFile);
+ rb_define_method(cGzipFile, "to_io", rb_gzfile_to_io, 0);
+ rb_define_method(cGzipFile, "crc", rb_gzfile_crc, 0);
+ rb_define_method(cGzipFile, "mtime", rb_gzfile_mtime, 0);
+ rb_define_method(cGzipFile, "level", rb_gzfile_level, 0);
+ rb_define_method(cGzipFile, "os_code", rb_gzfile_os_code, 0);
+ rb_define_method(cGzipFile, "orig_name", rb_gzfile_orig_name, 0);
+ rb_define_method(cGzipFile, "comment", rb_gzfile_comment, 0);
+ rb_define_method(cGzipReader, "lineno", rb_gzfile_lineno, 0);
+ rb_define_method(cGzipReader, "lineno=", rb_gzfile_set_lineno, 1);
+ rb_define_method(cGzipWriter, "mtime=", rb_gzfile_set_mtime, 1);
+ rb_define_method(cGzipWriter, "orig_name=", rb_gzfile_set_orig_name,1);
+ rb_define_method(cGzipWriter, "comment=", rb_gzfile_set_comment, 1);
+ rb_define_method(cGzipFile, "close", rb_gzfile_close, 0);
+ rb_define_method(cGzipFile, "finish", rb_gzfile_finish, 0);
+ rb_define_method(cGzipFile, "closed?", rb_gzfile_closed_p, 0);
+ rb_define_method(cGzipReader, "eof", rb_gzfile_eof_p, 0);
+ rb_define_method(cGzipReader, "eof?", rb_gzfile_eof_p, 0);
+ rb_define_method(cGzipFile, "sync", rb_gzfile_sync, 0);
+ rb_define_method(cGzipFile, "sync=", rb_gzfile_set_sync, 1);
+ rb_define_method(cGzipReader, "pos", rb_gzfile_total_out, 0);
+ rb_define_method(cGzipWriter, "pos", rb_gzfile_total_in, 0);
+ rb_define_method(cGzipReader, "tell", rb_gzfile_total_out, 0);
+ rb_define_method(cGzipWriter, "tell", rb_gzfile_total_in, 0);
+
+ rb_define_singleton_method(cGzipWriter, "open", rb_gzwriter_s_open,-1);
+ rb_define_alloc_func(cGzipWriter, rb_gzwriter_s_allocate);
+ rb_define_method(cGzipWriter, "initialize", rb_gzwriter_initialize,-1);
+ rb_define_method(cGzipWriter, "flush", rb_gzwriter_flush, -1);
+ rb_define_method(cGzipWriter, "write", rb_gzwriter_write, 1);
+ rb_define_method(cGzipWriter, "putc", rb_gzwriter_putc, 1);
+ rb_define_method(cGzipWriter, "<<", rb_gzwriter_addstr, 1);
+ rb_define_method(cGzipWriter, "printf", rb_gzwriter_printf, -1);
+ rb_define_method(cGzipWriter, "print", rb_gzwriter_print, -1);
+ rb_define_method(cGzipWriter, "puts", rb_gzwriter_puts, -1);
+
+ rb_define_singleton_method(cGzipReader, "open", rb_gzreader_s_open,-1);
+ rb_define_alloc_func(cGzipReader, rb_gzreader_s_allocate);
+ rb_define_method(cGzipReader, "initialize", rb_gzreader_initialize, 1);
+ rb_define_method(cGzipReader, "rewind", rb_gzreader_rewind, 0);
+ rb_define_method(cGzipReader, "unused", rb_gzreader_unused, 0);
+ rb_define_method(cGzipReader, "read", rb_gzreader_read, -1);
+ rb_define_method(cGzipReader, "getc", rb_gzreader_getc, 0);
+ rb_define_method(cGzipReader, "readchar", rb_gzreader_readchar, 0);
+ rb_define_method(cGzipReader, "each_byte", rb_gzreader_each_byte, 0);
+ rb_define_method(cGzipReader, "ungetc", rb_gzreader_ungetc, 1);
+ rb_define_method(cGzipReader, "gets", rb_gzreader_gets, -1);
+ rb_define_method(cGzipReader, "readline", rb_gzreader_readline, -1);
+ rb_define_method(cGzipReader, "each", rb_gzreader_each, -1);
+ rb_define_method(cGzipReader, "readlines", rb_gzreader_readlines, -1);
+
+ rb_define_const(mZlib, "OS_CODE", INT2FIX(OS_CODE));
+ rb_define_const(mZlib, "OS_MSDOS", INT2FIX(OS_MSDOS));
+ rb_define_const(mZlib, "OS_AMIGA", INT2FIX(OS_AMIGA));
+ rb_define_const(mZlib, "OS_VMS", INT2FIX(OS_VMS));
+ rb_define_const(mZlib, "OS_UNIX", INT2FIX(OS_UNIX));
+ rb_define_const(mZlib, "OS_ATARI", INT2FIX(OS_ATARI));
+ rb_define_const(mZlib, "OS_OS2", INT2FIX(OS_OS2));
+ rb_define_const(mZlib, "OS_MACOS", INT2FIX(OS_MACOS));
+ rb_define_const(mZlib, "OS_TOPS20", INT2FIX(OS_TOPS20));
+ rb_define_const(mZlib, "OS_WIN32", INT2FIX(OS_WIN32));
+
+ rb_define_const(mZlib, "OS_VMCMS", INT2FIX(OS_VMCMS));
+ rb_define_const(mZlib, "OS_ZSYSTEM", INT2FIX(OS_ZSYSTEM));
+ rb_define_const(mZlib, "OS_CPM", INT2FIX(OS_CPM));
+ rb_define_const(mZlib, "OS_QDOS", INT2FIX(OS_QDOS));
+ rb_define_const(mZlib, "OS_RISCOS", INT2FIX(OS_RISCOS));
+ rb_define_const(mZlib, "OS_UNKNOWN", INT2FIX(OS_UNKNOWN));
+
+#endif /* GZIP_SUPPORT */
+}
diff --git a/file.c b/file.c
index f8a0037e3c..12d71f0bf3 100644
--- a/file.c
+++ b/file.c
@@ -6,7 +6,7 @@
$Date$
created at: Mon Nov 15 12:24:34 JST 1993
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
Copyright (C) 2000 Information-technology Promotion Agency, Japan
@@ -85,7 +85,7 @@ apply2files(func, vargs, arg)
for (i=0; i<args->len; i++) {
path = args->ptr[i];
SafeStringValue(path);
- (*func)(RSTRING(path)->ptr, arg);
+ (*func)(StringValueCStr(path), arg);
}
return args->len;
@@ -307,7 +307,7 @@ rb_stat_inspect(self)
};
str = rb_str_buf_new2("#<");
- rb_str_buf_cat2(str, rb_class2name(CLASS_OF(self)));
+ rb_str_buf_cat2(str, rb_obj_classname(self));
rb_str_buf_cat2(str, " ");
for (i = 0; i < sizeof(member)/sizeof(member[0]); i++) {
@@ -346,18 +346,18 @@ rb_stat(file, st)
VALUE file;
struct stat *st;
{
- if (TYPE(file) == T_FILE) {
+ VALUE tmp;
+
+ tmp = rb_check_convert_type(file, T_FILE, "IO", "to_io");
+ if (!NIL_P(tmp)) {
OpenFile *fptr;
rb_secure(2);
- GetOpenFile(file, fptr);
+ GetOpenFile(tmp, fptr);
return fstat(fileno(fptr->f), st);
}
SafeStringValue(file);
-#if defined DJGPP
- if (RSTRING(file)->len == 0) return -1;
-#endif
- return stat(RSTRING(file)->ptr, st);
+ return stat(StringValueCStr(file), st);
}
static VALUE
@@ -367,8 +367,8 @@ rb_file_s_stat(klass, fname)
struct stat st;
SafeStringValue(fname);
- if (stat(RSTRING(fname)->ptr, &st) == -1) {
- rb_sys_fail(RSTRING(fname)->ptr);
+ if (rb_stat(fname, &st) < 0) {
+ rb_sys_fail(StringValueCStr(fname));
}
return stat_new(&st);
}
@@ -395,7 +395,7 @@ rb_file_s_lstat(klass, fname)
struct stat st;
SafeStringValue(fname);
- if (lstat(RSTRING(fname)->ptr, &st) == -1) {
+ if (lstat(StringValueCStr(fname), &st) == -1) {
rb_sys_fail(RSTRING(fname)->ptr);
}
return stat_new(&st);
@@ -463,7 +463,7 @@ eaccess(path, mode)
const char *path;
int mode;
{
-#ifdef S_IXGRP
+#if defined(S_IXGRP) && !defined(_WIN32)
struct stat st;
int euid;
@@ -560,7 +560,7 @@ test_l(obj, fname)
struct stat st;
SafeStringValue(fname);
- if (lstat(RSTRING(fname)->ptr, &st) < 0) return Qfalse;
+ if (lstat(StringValueCStr(fname), &st) < 0) return Qfalse;
if (S_ISLNK(st.st_mode)) return Qtrue;
#endif
@@ -656,7 +656,7 @@ test_r(obj, fname)
VALUE obj, fname;
{
SafeStringValue(fname);
- if (eaccess(RSTRING(fname)->ptr, R_OK) < 0) return Qfalse;
+ if (eaccess(StringValueCStr(fname), R_OK) < 0) return Qfalse;
return Qtrue;
}
@@ -665,7 +665,7 @@ test_R(obj, fname)
VALUE obj, fname;
{
SafeStringValue(fname);
- if (access(RSTRING(fname)->ptr, R_OK) < 0) return Qfalse;
+ if (access(StringValueCStr(fname), R_OK) < 0) return Qfalse;
return Qtrue;
}
@@ -674,7 +674,7 @@ test_w(obj, fname)
VALUE obj, fname;
{
SafeStringValue(fname);
- if (eaccess(RSTRING(fname)->ptr, W_OK) < 0) return Qfalse;
+ if (eaccess(StringValueCStr(fname), W_OK) < 0) return Qfalse;
return Qtrue;
}
@@ -683,7 +683,7 @@ test_W(obj, fname)
VALUE obj, fname;
{
SafeStringValue(fname);
- if (access(RSTRING(fname)->ptr, W_OK) < 0) return Qfalse;
+ if (access(StringValueCStr(fname), W_OK) < 0) return Qfalse;
return Qtrue;
}
@@ -692,7 +692,7 @@ test_x(obj, fname)
VALUE obj, fname;
{
SafeStringValue(fname);
- if (eaccess(RSTRING(fname)->ptr, X_OK) < 0) return Qfalse;
+ if (eaccess(StringValueCStr(fname), X_OK) < 0) return Qfalse;
return Qtrue;
}
@@ -701,7 +701,7 @@ test_X(obj, fname)
VALUE obj, fname;
{
SafeStringValue(fname);
- if (access(RSTRING(fname)->ptr, X_OK) < 0) return Qfalse;
+ if (access(StringValueCStr(fname), X_OK) < 0) return Qfalse;
return Qtrue;
}
@@ -786,7 +786,7 @@ check3rdbyte(fname, mode)
struct stat st;
SafeStringValue(fname);
- if (stat(RSTRING(fname)->ptr, &st) < 0) return Qfalse;
+ if (stat(StringValueCStr(fname), &st) < 0) return Qfalse;
if (st.st_mode & mode) return Qtrue;
return Qfalse;
}
@@ -832,7 +832,7 @@ rb_file_s_size(klass, fname)
struct stat st;
if (rb_stat(fname, &st) < 0)
- rb_sys_fail(RSTRING(fname)->ptr);
+ rb_sys_fail(StringValueCStr(fname));
return OFFT2NUM(st.st_size);
}
@@ -885,7 +885,7 @@ rb_file_s_ftype(klass, fname)
struct stat st;
SafeStringValue(fname);
- if (lstat(RSTRING(fname)->ptr, &st) == -1) {
+ if (lstat(StringValueCStr(fname), &st) == -1) {
rb_sys_fail(RSTRING(fname)->ptr);
}
@@ -899,7 +899,7 @@ rb_file_s_atime(klass, fname)
struct stat st;
if (rb_stat(fname, &st) < 0)
- rb_sys_fail(RSTRING(fname)->ptr);
+ rb_sys_fail(StringValueCStr(fname));
return rb_time_new(st.st_atime, 0);
}
@@ -990,7 +990,7 @@ rb_file_s_chmod(argc, argv)
rb_scan_args(argc, argv, "1*", &vmode, &rest);
mode = NUM2INT(vmode);
- n = apply2files(chmod_internal, rest, mode);
+ n = apply2files(chmod_internal, rest, (void *)(long)mode);
return LONG2FIX(n);
}
@@ -1041,7 +1041,7 @@ rb_file_s_lchmod(argc, argv)
rb_scan_args(argc, argv, "1*", &vmode, &rest);
mode = NUM2INT(vmode);
- n = apply2files(lchmod_internal, rest, mode);
+ n = apply2files(lchmod_internal, rest, (void *)(long)mode);
return LONG2FIX(n);
}
#else
@@ -1051,6 +1051,7 @@ rb_file_s_lchmod(argc, argv)
VALUE *argv;
{
rb_notimplement();
+ return Qnil; /* not reached */
}
#endif
@@ -1201,9 +1202,6 @@ struct utimbuf {
long modtime;
};
#endif
-#if defined(_WIN32) && !defined(_WIN32_WCE)&& (defined(_MSC_VER) || defined(__MINGW__))
-# define utimbuf _utimbuf
-#endif
static void
utime_internal(path, utp)
@@ -1237,6 +1235,20 @@ rb_file_s_utime(argc, argv)
#endif
+NORETURN(static void sys_fail2 _((VALUE,VALUE)));
+static void
+sys_fail2(s1, s2)
+ VALUE s1, s2;
+{
+ char *buf;
+ int len;
+
+ len = RSTRING(s1)->len + RSTRING(s2)->len + 5;
+ buf = ALLOCA_N(char, len);
+ snprintf(buf, len, "%s or %s", RSTRING(s1)->ptr, RSTRING(s2)->ptr);
+ rb_sys_fail(buf);
+}
+
static VALUE
rb_file_s_link(klass, from, to)
VALUE klass, from, to;
@@ -1244,8 +1256,9 @@ rb_file_s_link(klass, from, to)
SafeStringValue(from);
SafeStringValue(to);
- if (link(RSTRING(from)->ptr, RSTRING(to)->ptr) < 0)
- rb_sys_fail(RSTRING(from)->ptr);
+ if (link(StringValueCStr(from), StringValueCStr(to)) < 0) {
+ sys_fail2(from, to);
+ }
return INT2FIX(0);
}
@@ -1257,8 +1270,9 @@ rb_file_s_symlink(klass, from, to)
SafeStringValue(from);
SafeStringValue(to);
- if (symlink(RSTRING(from)->ptr, RSTRING(to)->ptr) < 0)
- rb_sys_fail(RSTRING(from)->ptr);
+ if (symlink(StringValueCStr(from), StringValueCStr(to)) < 0) {
+ sys_fail2(from, to);
+ }
return INT2FIX(0);
#else
rb_notimplement();
@@ -1278,7 +1292,7 @@ rb_file_s_readlink(klass, path)
SafeStringValue(path);
buf = xmalloc(size);
- if ((rv = readlink(RSTRING(path)->ptr, buf, size)) == size) {
+ while ((rv = readlink(StringValueCStr(path), buf, size)) == size) {
size *= 2;
buf = xrealloc(buf, size);
}
@@ -1319,15 +1333,29 @@ static VALUE
rb_file_s_rename(klass, from, to)
VALUE klass, from, to;
{
+ const char *src, *dst;
SafeStringValue(from);
SafeStringValue(to);
- if (rename(RSTRING(from)->ptr, RSTRING(to)->ptr) < 0) {
+ src = StringValueCStr(from);
+ dst = StringValueCStr(to);
+ if (rename(src, dst) < 0) {
#if defined __CYGWIN__
extern unsigned long __attribute__((stdcall)) GetLastError();
errno = GetLastError(); /* This is a Cygwin bug */
+#elif defined DOSISH && !defined _WIN32
+ if (errno == EEXIST
+#if defined (__EMX__)
+ || errno == EACCES
#endif
- rb_sys_fail(RSTRING(from)->ptr);
+ ) {
+ if (chmod(dst, 0666) == 0 &&
+ unlink(dst) == 0 &&
+ rename(src, dst) == 0)
+ return INT2FIX(0);
+ }
+#endif
+ sys_fail2(from, to);
}
return INT2FIX(0);
@@ -1355,6 +1383,7 @@ rb_file_s_umask(argc, argv)
}
#if defined DOSISH
+#define DOSISH_UNC
#define isdirsep(x) ((x) == '/' || (x) == '\\')
#else
#define isdirsep(x) ((x) == '/')
@@ -1367,14 +1396,108 @@ rb_file_s_umask(argc, argv)
# endif
#endif
+#ifdef __CYGWIN__
+#undef DOSISH
+#define DOSISH_UNC
+#define DOSISH_DRIVE_LETTER
+#endif
+
+#ifdef DOSISH_DRIVE_LETTER
+static inline int
+has_drive_letter(buf)
+ const char *buf;
+{
+ if (ISALPHA(buf[0]) && buf[1] == ':') {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+static void
+getcwdofdrv(drv, buf, len)
+ int drv;
+ char *buf;
+ int len;
+{
+ char drive[4];
+ char oldcwd[MAXPATHLEN+1];
+
+ drive[0] = drv;
+ drive[1] = ':';
+ drive[2] = '\0';
+
+ /* the only way that I know to get the current directory
+ of a particular drive is to change chdir() to that drive,
+ so save the old cwd before chdir()
+ */
+ getcwd(oldcwd, MAXPATHLEN);
+ if (chdir(drive) == 0) {
+ getcwd(buf, len);
+ chdir(oldcwd);
+ }
+ else {
+ /* perhaps the drive is not exist. we return only drive letter */
+ strncpy(buf, drive, len);
+ }
+}
+#endif
+
+static inline char *
+skiproot(path)
+ const char *path;
+{
+#ifdef DOSISH_DRIVE_LETTER
+ if (has_drive_letter(path)) path += 2;
+#endif
+ while (isdirsep(*path)) path++;
+ return (char *)path;
+}
+
+static inline char *
+nextdirsep(s)
+ const char *s;
+{
+ while (*s && !isdirsep(*s)) {
+ s = CharNext(s);
+ }
+ return (char *)s;
+}
+
+#if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
+static inline char *
+skipprefix(path)
+ const char *path;
+{
+#ifdef DOSISH_UNC
+ if (isdirsep(path[0]) && isdirsep(path[1])) {
+ if (*(path = nextdirsep(path + 2)))
+ path = nextdirsep(path + 1);
+ return (char *)path;
+ }
+#endif
+#ifdef DOSISH_DRIVE_LETTER
+ if (has_drive_letter(path))
+ return (char *)(path + 2);
+#endif
+ return (char *)path;
+}
+#else
+#define skipprefix(path) (path)
+#endif
+
static char *
strrdirsep(path)
- char *path;
+ const char *path;
{
char *last = NULL;
while (*path) {
if (isdirsep(*path)) {
- last = path++;
+ const char *tmp = path++;
+ while (isdirsep(*path)) path++;
+ if (!*path) break;
+ last = (char *)tmp;
}
else {
path = CharNext(path);
@@ -1383,6 +1506,23 @@ strrdirsep(path)
return last;
}
+static char *
+chompdirsep(path)
+ const char *path;
+{
+ while (*path) {
+ if (isdirsep(*path)) {
+ const char *last = path++;
+ while (isdirsep(*path)) path++;
+ if (!*path) return (char *)last;
+ }
+ else {
+ path = CharNext(path);
+ }
+ }
+ return (char *)path;
+}
+
#define BUFCHECK(cond) while (cond) {\
long bdiff = p - buf;\
buflen *= 2;\
@@ -1392,22 +1532,27 @@ strrdirsep(path)
pend = buf + buflen;\
}
-VALUE
-rb_file_s_expand_path(argc, argv)
- int argc;
- VALUE *argv;
-{
+#define BUFINIT() (\
+ p = buf = RSTRING(result)->ptr,\
+ buflen = RSTRING(result)->len,\
+ pend = p + buflen)
+
+#if !defined(TOLOWER)
+#define TOLOWER(c) (ISUPPER(c) ? tolower(c) : (c))
+#endif
+
+static int is_absolute_path _((const char*));
+
+static VALUE
+file_expand_path(fname, dname, result)
VALUE fname, dname, result;
- char *s, *buf, *b, *p, *pend;
- long buflen = MAXPATHLEN;
+{
+ char *s, *buf, *b, *p, *pend, *root;
+ long buflen;
int tainted;
- rb_scan_args(argc, argv, "11", &fname, &dname);
- result = rb_str_new(0, buflen + 2);
-
s = StringValuePtr(fname);
- p = buf = RSTRING(result)->ptr;
- pend = p + buflen;
+ BUFINIT();
tainted = OBJ_TAINTED(fname);
if (s[0] == '~') {
@@ -1419,7 +1564,15 @@ rb_file_s_expand_path(argc, argv)
}
BUFCHECK(strlen(dir) > buflen);
strcpy(buf, dir);
+#if defined DOSISH || defined __CYGWIN__
+ for (p = buf; *p; p = CharNext(p)) {
+ if (*p == '\\') {
+ *p = '/';
+ }
+ }
+#else
p = buf + strlen(dir);
+#endif
s++;
tainted = 1;
}
@@ -1428,10 +1581,7 @@ rb_file_s_expand_path(argc, argv)
struct passwd *pwPtr;
s++;
#endif
- b = s;
- while (*s && !isdirsep(*s)) {
- s = CharNext(s);
- }
+ s = nextdirsep(b = s);
BUFCHECK(p + (s-b) >= pend);
memcpy(p, b, s-b);
p += s-b;
@@ -1449,25 +1599,42 @@ rb_file_s_expand_path(argc, argv)
#endif
}
}
-#if defined DOSISH_DRIVE_LETTER || defined __CYGWIN__
+#ifdef DOSISH_DRIVE_LETTER
/* skip drive letter */
- else if (ISALPHA(s[0]) && s[1] == ':' && isdirsep(s[2])) {
- b = s;
- while (*s && !isdirsep(*s)) {
- s = CharNext(s);
+ else if (has_drive_letter(s)) {
+ if (isdirsep(s[2])) {
+ /* specified drive letter, and full path */
+ /* skip drive letter */
+ BUFCHECK(p + 2 >= pend);
+ memcpy(p, s, 2);
+ p += 2;
+ s += 2;
+ }
+ else {
+ /* specified drive, but not full path */
+ int same = 0;
+ if (!NIL_P(dname)) {
+ file_expand_path(dname, Qnil, result);
+ BUFINIT();
+ if (has_drive_letter(p) && TOLOWER(p[0]) == TOLOWER(s[0])) {
+ /* ok, same drive */
+ same = 1;
+ }
+ }
+ if (!same) {
+ BUFCHECK(buflen < MAXPATHLEN);
+ getcwdofdrv(*s, buf, MAXPATHLEN);
+ tainted = 1;
+ }
+ p = chompdirsep(skiproot(buf));
+ s += 2;
}
- BUFCHECK(p + (s-b) >= pend);
- memcpy(p, b, s-b);
- p += s-b;
}
#endif
- else if (!isdirsep(*s)) {
+ else if (!is_absolute_path(s)) {
if (!NIL_P(dname)) {
- dname = rb_file_s_expand_path(1, &dname);
- if (OBJ_TAINTED(dname)) tainted = 1;
- BUFCHECK(RSTRING(dname)->len > buflen);
- memcpy(buf, RSTRING(dname)->ptr, RSTRING(dname)->len);
- p += RSTRING(dname)->len;
+ file_expand_path(dname, Qnil, result);
+ BUFINIT();
}
else {
char *dir = my_getcwd();
@@ -1476,19 +1643,31 @@ rb_file_s_expand_path(argc, argv)
BUFCHECK(strlen(dir) > buflen);
strcpy(buf, dir);
free(dir);
- p = &buf[strlen(buf)];
}
- while (p > buf && *(p - 1) == '/') p--;
+#if defined DOSISH || defined __CYGWIN__
+ if (isdirsep(*s)) {
+ /* specified full path, but not drive letter nor UNC */
+ /* we need to get the drive letter or UNC share name */
+ p = skipprefix(buf);
+ }
+ else
+#endif
+ p = chompdirsep(skiproot(buf));
}
else {
- while (*s && isdirsep(*s)) {
- *p++ = '/';
- BUFCHECK(p >= pend);
- s++;
- }
- if (p > buf && *s) p--;
+ b = s;
+ do s++; while (isdirsep(*s));
+ p = buf + (s - b);
+ BUFCHECK(p >= pend);
+ memset(buf, '/', p - buf);
}
- *p = '/';
+ if (p > buf && p[-1] == '/')
+ --p;
+ else
+ *p = '/';
+
+ p[1] = 0;
+ root = skipprefix(buf);
b = s;
while (*s) {
@@ -1503,7 +1682,7 @@ rb_file_s_expand_path(argc, argv)
if (*(s+1) == '\0' || isdirsep(*(s+1))) {
/* We must go back to the parent */
*p = '\0';
- if (!(b = strrdirsep(buf))) {
+ if (!(b = strrdirsep(root))) {
*p = '/';
}
else {
@@ -1513,7 +1692,7 @@ rb_file_s_expand_path(argc, argv)
}
break;
case '/':
-#if defined DOSISH
+#if defined DOSISH || defined __CYGWIN__
case '\\':
#endif
b = ++s;
@@ -1525,11 +1704,13 @@ rb_file_s_expand_path(argc, argv)
}
break;
case '/':
-#if defined DOSISH
+#if defined DOSISH || defined __CYGWIN__
case '\\':
#endif
if (s > b) {
+ long rootdiff = root - buf;
BUFCHECK(p + (s-b+1) >= pend);
+ root = buf + rootdiff;
memcpy(++p, b, s-b);
p += s-b;
*p = '/';
@@ -1547,16 +1728,7 @@ rb_file_s_expand_path(argc, argv)
memcpy(++p, b, s-b);
p += s-b;
}
-#if defined DOSISH_DRIVE_LETTER || defined __CYGWIN__
- else if (ISALPHA(buf[0]) && (buf[1] == ':') && isdirsep(buf[2])) {
- /* root directory needs a trailing backslash,
- otherwise it mean the current directory of the drive */
- if (p == (buf+2)) p++;
- }
- else if (isdirsep(buf[0]) && isdirsep(buf[1])) {
- if (p == (buf+1)) p++;
- }
-#endif
+ if (p == skiproot(buf) - 1) p++;
if (tainted) OBJ_TAINT(result);
RSTRING(result)->len = p - buf;
@@ -1564,6 +1736,24 @@ rb_file_s_expand_path(argc, argv)
return result;
}
+VALUE
+rb_file_expand_path(fname, dname)
+ VALUE fname, dname;
+{
+ return file_expand_path(fname, dname, rb_str_new(0, MAXPATHLEN + 2));
+}
+
+VALUE
+rb_file_s_expand_path(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE fname, dname;
+ rb_scan_args(argc, argv, "11", &fname, &dname);
+
+ return rb_file_expand_path(fname, dname);
+}
+
static int
rmext(p, e)
const char *p, *e;
@@ -1572,7 +1762,7 @@ rmext(p, e)
if (!e) return 0;
- l1 = strlen(p);
+ l1 = chompdirsep(p) - p;
l2 = strlen(e);
if (l2 == 2 && e[1] == '*') {
e = strrchr(p, *e);
@@ -1581,7 +1771,7 @@ rmext(p, e)
}
if (l1 < l2) return l1;
- if (strcmp(p+l1-l2, e) == 0) {
+ if (strncmp(p+l1-l2, e, l2) == 0) {
return l1-l2;
}
return 0;
@@ -1597,24 +1787,35 @@ rb_file_s_basename(argc, argv)
int f;
if (rb_scan_args(argc, argv, "11", &fname, &fext) == 2) {
- ext = StringValuePtr(fext);
+ ext = StringValueCStr(fext);
+ }
+ StringValue(fname);
+ if (RSTRING(fname)->len == 0 || !*(name = RSTRING(fname)->ptr))
+ return fname;
+ if (!*(name = skiproot(name))) {
+ p = name - 1;
+ f = 1;
+#ifdef DOSISH_DRIVE_LETTER
+ if (*p == ':') {
+ p++;
+ f = 0;
+ }
+#endif
}
- name = StringValuePtr(fname);
- p = strrdirsep(name);
- if (!p) {
- if (NIL_P(fext) || !(f = rmext(name, ext)))
- return fname;
- basename = rb_str_new(name, f);
+ else if (!(p = strrdirsep(name))) {
+ if (NIL_P(fext) || !(f = rmext(name, ext))) {
+ f = chompdirsep(name) - name;
+ if (f == RSTRING(fname)->len) return fname;
+ }
+ p = name;
}
else {
- p++; /* skip last / */
+ while (isdirsep(*p)) p++; /* skip last / */
if (NIL_P(fext) || !(f = rmext(p, ext))) {
- basename = rb_str_new2(p);
- }
- else {
- basename = rb_str_new(p, f);
+ f = chompdirsep(p) - p;
}
}
+ basename = rb_str_new(p, f);
OBJ_INFECT(basename, fname);
return basename;
}
@@ -1623,17 +1824,29 @@ static VALUE
rb_file_s_dirname(klass, fname)
VALUE klass, fname;
{
- char *name, *p;
+ char *name, *root, *p;
VALUE dirname;
- name = StringValuePtr(fname);
- p = strrdirsep(name);
+ name = StringValueCStr(fname);
+ root = skiproot(name);
+#ifdef DOSISH_UNC
+ if (root > name + 2 && isdirsep(*name))
+ name = root - 2;
+#else
+ if (root > name + 1)
+ name = root - 1;
+#endif
+ p = strrdirsep(root);
if (!p) {
- return rb_str_new2(".");
+ p = root;
}
if (p == name)
- p++;
+ return rb_str_new2(".");
dirname = rb_str_new(name, p - name);
+#ifdef DOSISH_DRIVE_LETTER
+ if (root == name + 2 && name[1] == ':')
+ rb_str_cat(dirname, ".", 1);
+#endif
OBJ_INFECT(dirname, fname);
return dirname;
}
@@ -1645,7 +1858,7 @@ rb_file_s_extname(klass, fname)
char *name, *p, *e;
VALUE extname;
- name = StringValuePtr(fname);
+ name = StringValueCStr(fname);
p = strrdirsep(name); /* get the last path component */
if (!p)
p = name;
@@ -1655,7 +1868,7 @@ rb_file_s_extname(klass, fname)
e = strrchr(p, '.'); /* get the last dot of the last component */
if (!e || e == p) /* no dot, or the only dot is first? */
return rb_str_new2("");
- extname = rb_str_new2(e); /* keep the dot, too! */
+ extname = rb_str_new(e, chompdirsep(e) - e); /* keep the dot, too! */
OBJ_INFECT(extname, fname);
return extname;
}
@@ -1664,16 +1877,84 @@ static VALUE
rb_file_s_split(klass, path)
VALUE klass, path;
{
+ StringValue(path); /* get rid of converting twice */
return rb_assoc_new(rb_file_s_dirname(Qnil, path), rb_file_s_basename(1,&path));
}
static VALUE separator;
+static VALUE rb_file_join _((VALUE ary, VALUE sep));
+
+static VALUE
+file_inspect_join(ary, arg)
+ VALUE ary;
+ VALUE *arg;
+{
+ return rb_file_join(arg[0], arg[1]);
+}
+
+static VALUE
+rb_file_join(ary, sep)
+ VALUE ary, sep;
+{
+ long len, i;
+ int taint = 0;
+ VALUE result, tmp;
+ char *name;
+
+ if (RARRAY(ary)->len == 0) return rb_str_new(0, 0);
+ if (OBJ_TAINTED(ary)) taint = 1;
+ if (OBJ_TAINTED(sep)) taint = 1;
+
+ len = 1;
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ if (TYPE(RARRAY(ary)->ptr[i]) == T_STRING) {
+ len += RSTRING(RARRAY(ary)->ptr[i])->len;
+ }
+ else {
+ len += 10;
+ }
+ }
+ if (!NIL_P(sep) && TYPE(sep) == T_STRING) {
+ len += RSTRING(sep)->len * RARRAY(ary)->len - 1;
+ }
+ result = rb_str_buf_new(len);
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ tmp = RARRAY(ary)->ptr[i];
+ switch (TYPE(tmp)) {
+ case T_STRING:
+ break;
+ case T_ARRAY:
+ if (rb_inspecting_p(tmp)) {
+ tmp = rb_str_new2("[...]");
+ }
+ else {
+ VALUE args[2];
+
+ args[0] = tmp;
+ args[1] = sep;
+ tmp = rb_protect_inspect(file_inspect_join, ary, (VALUE)args);
+ }
+ break;
+ default:
+ tmp = rb_obj_as_string(tmp);
+ }
+ name = StringValueCStr(result);
+ if (i > 0 && !NIL_P(sep) && !*chompdirsep(name))
+ rb_str_buf_append(result, sep);
+ rb_str_buf_append(result, tmp);
+ if (OBJ_TAINTED(tmp)) taint = 1;
+ }
+
+ if (taint) OBJ_TAINT(result);
+ return result;
+}
+
static VALUE
rb_file_s_join(klass, args)
VALUE klass, args;
{
- return rb_ary_join(args, separator);
+ return rb_file_join(args, separator);
}
static VALUE
@@ -1684,7 +1965,7 @@ rb_file_s_truncate(klass, path, len)
SafeStringValue(path);
#ifdef HAVE_TRUNCATE
- if (truncate(RSTRING(path)->ptr, NUM2OFFT(len)) < 0)
+ if (truncate(StringValueCStr(path), NUM2OFFT(len)) < 0)
rb_sys_fail(RSTRING(path)->ptr);
#else
# ifdef HAVE_CHSIZE
@@ -1692,11 +1973,11 @@ rb_file_s_truncate(klass, path, len)
int tmpfd;
# ifdef _WIN32
- if ((tmpfd = open(RSTRING(path)->ptr, O_RDWR)) < 0) {
+ if ((tmpfd = open(StringValueCStr(path), O_RDWR)) < 0) {
rb_sys_fail(RSTRING(path)->ptr);
}
# else
- if ((tmpfd = open(RSTRING(path)->ptr, 0)) < 0) {
+ if ((tmpfd = open(StringValueCStr(path), 0)) < 0) {
rb_sys_fail(RSTRING(path)->ptr);
}
# endif
@@ -1795,6 +2076,7 @@ rb_file_flock(obj, operation)
if (fptr->mode & FMODE_WRITABLE) {
fflush(GetWriteFile(fptr));
}
+ retry:
TRAP_BEG;
ret = flock(fileno(fptr->f), NUM2INT(operation));
TRAP_END;
@@ -1806,6 +2088,11 @@ rb_file_flock(obj, operation)
case EWOULDBLOCK:
#endif
return Qfalse;
+ case EINTR:
+#if defined(ERESTART)
+ case ERESTART:
+#endif
+ goto retry;
}
rb_sys_fail(fptr->path);
}
@@ -1826,13 +2113,11 @@ test_check(n, argc, argv)
for (i=1; i<n; i++) {
switch (TYPE(argv[i])) {
case T_STRING:
+ default:
SafeStringValue(argv[i]);
break;
case T_FILE:
break;
- default:
- Check_Type(argv[i], T_STRING);
- break;
}
}
}
@@ -1975,6 +2260,7 @@ rb_f_test(argc, argv)
return Qnil; /* not reached */
}
+static VALUE rb_stat_s_alloc _((VALUE));
static VALUE
rb_stat_s_alloc(klass)
VALUE klass;
@@ -1990,7 +2276,7 @@ rb_stat_init(obj, fname)
SafeStringValue(fname);
- if (stat(RSTRING(fname)->ptr, &st) == -1) {
+ if (stat(StringValueCStr(fname), &st) == -1) {
rb_sys_fail(RSTRING(fname)->ptr);
}
if (DATA_PTR(obj)) {
@@ -2005,7 +2291,7 @@ rb_stat_init(obj, fname)
}
static VALUE
-rb_stat_copy_object(copy, orig)
+rb_stat_init_copy(copy, orig)
VALUE copy, orig;
{
struct stat *nst;
@@ -2306,18 +2592,16 @@ rb_file_const(name, value)
VALUE value;
{
rb_define_const(rb_mFConst, name, value);
- rb_define_const(rb_cIO, name, value);
- rb_define_const(rb_cFile, name, value);
}
static int
is_absolute_path(path)
const char *path;
{
-#if defined DOSISH_DRIVE_LETTER || defined __CYGWIN__
- if (ISALPHA(path[0]) && path[1] == ':' && isdirsep(path[2])) return 1;
+#ifdef DOSISH_DRIVE_LETTER
+ if (has_drive_letter(path) && isdirsep(path[2])) return 1;
#endif
-#if defined DOSISH || defined __CYGWIN__
+#ifdef DOSISH_UNC
if (isdirsep(path[0]) && isdirsep(path[1])) return 1;
#endif
#ifndef DOSISH
@@ -2332,7 +2616,7 @@ path_check_1(path)
VALUE path;
{
struct stat st;
- char *p0 = RSTRING(path)->ptr;
+ char *p0 = StringValueCStr(path);
char *p = 0, *s;
if (!is_absolute_path(p0)) {
@@ -2432,11 +2716,11 @@ rb_find_file_ext(filep, ext)
long i, j;
if (f[0] == '~') {
- fname = rb_file_s_expand_path(1, filep);
+ fname = rb_file_expand_path(*filep, Qnil);
if (rb_safe_level() >= 2 && OBJ_TAINTED(fname)) {
rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
}
- f = StringValuePtr(fname);
+ f = StringValueCStr(fname);
*filep = fname;
}
@@ -2444,7 +2728,7 @@ rb_find_file_ext(filep, ext)
for (i=0; ext[i]; i++) {
fname = rb_str_dup(*filep);
rb_str_cat2(fname, ext[i]);
- if (file_load_ok(RSTRING(fname)->ptr)) {
+ if (file_load_ok(StringValueCStr(fname))) {
*filep = fname;
return i+1;
}
@@ -2464,7 +2748,7 @@ rb_find_file_ext(filep, ext)
for (j=0; ext[j]; j++) {
fname = rb_str_dup(*filep);
rb_str_cat2(fname, ext[j]);
- found = dln_find_file(RSTRING(fname)->ptr, path);
+ found = dln_find_file(StringValueCStr(fname), path);
if (found && file_load_ok(found)) {
*filep = fname;
return j+1;
@@ -2479,20 +2763,20 @@ rb_find_file(path)
VALUE path;
{
VALUE tmp;
- char *f = RSTRING(path)->ptr;
+ char *f = StringValueCStr(path);
char *lpath;
if (f[0] == '~') {
- path = rb_file_s_expand_path(1, &path);
- if (rb_safe_level() >= 2 && OBJ_TAINTED(path)) {
- rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
+ path = rb_file_expand_path(path, Qnil);
+ if (rb_safe_level() >= 1 && OBJ_TAINTED(path)) {
+ rb_raise(rb_eSecurityError, "loading from unsafe path %s", f);
}
- f = StringValuePtr(path);
+ f = StringValueCStr(path);
}
#if defined(__MACOS__) || defined(riscos)
if (is_macos_native_path(f)) {
- if (rb_safe_level() >= 2 && !rb_path_check(f)) {
+ if (rb_safe_level() >= 1 && !rb_path_check(f)) {
rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
}
if (file_load_ok(f)) return path;
@@ -2500,7 +2784,7 @@ rb_find_file(path)
#endif
if (is_absolute_path(f)) {
- if (rb_safe_level() >= 2 && !rb_path_check(f)) {
+ if (rb_safe_level() >= 1 && !rb_path_check(f)) {
rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
}
if (file_load_ok(f)) return path;
@@ -2528,7 +2812,7 @@ rb_find_file(path)
}
else {
lpath = RSTRING(tmp)->ptr;
- if (rb_safe_level() >= 2 && !rb_path_check(lpath)) {
+ if (rb_safe_level() >= 1 && !rb_path_check(lpath)) {
rb_raise(rb_eSecurityError, "loading from unsafe path %s", lpath);
}
}
@@ -2541,6 +2825,9 @@ rb_find_file(path)
return 0; /* no path, no load */
}
f = dln_find_file(f, lpath);
+ if (rb_safe_level() >= 1 && !rb_path_check(f)) {
+ rb_raise(rb_eSecurityError, "loading from unsafe file %s", f);
+ }
if (file_load_ok(f)) {
return rb_str_new2(f);
}
@@ -2624,7 +2911,7 @@ Init_File()
rb_define_singleton_method(rb_cFile, "split", rb_file_s_split, 1);
rb_define_singleton_method(rb_cFile, "join", rb_file_s_join, -2);
-#if defined DOSISH && !defined __CYGWIN__
+#ifdef DOSISH
rb_define_const(rb_cFile, "ALT_SEPARATOR", rb_obj_freeze(rb_str_new2("\\")));
#else
rb_define_const(rb_cFile, "ALT_SEPARATOR", Qnil);
@@ -2645,7 +2932,7 @@ Init_File()
rb_define_method(rb_cFile, "flock", rb_file_flock, 1);
rb_mFConst = rb_define_module_under(rb_cFile, "Constants");
- rb_include_module(rb_cFile, rb_mFConst);
+ rb_include_module(rb_cIO, rb_mFConst);
rb_file_const("LOCK_SH", INT2FIX(LOCK_SH));
rb_file_const("LOCK_EX", INT2FIX(LOCK_EX));
rb_file_const("LOCK_UN", INT2FIX(LOCK_UN));
@@ -2655,9 +2942,9 @@ Init_File()
rb_define_global_function("test", rb_f_test, -1);
rb_cStat = rb_define_class_under(rb_cFile, "Stat", rb_cObject);
- rb_define_singleton_method(rb_cStat, "allocate", rb_stat_s_alloc, 0);
+ rb_define_alloc_func(rb_cStat, rb_stat_s_alloc);
rb_define_method(rb_cStat, "initialize", rb_stat_init, 1);
- rb_define_method(rb_cStat, "copy_object", rb_stat_copy_object, 1);
+ rb_define_method(rb_cStat, "initialize_copy", rb_stat_init_copy, 1);
rb_include_module(rb_cStat, rb_mComparable);
diff --git a/gc.c b/gc.c
index f925e55e0c..7b0a05d617 100644
--- a/gc.c
+++ b/gc.c
@@ -6,7 +6,7 @@
$Date$
created at: Tue Oct 5 09:44:46 JST 1993
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
Copyright (C) 2000 Information-technology Promotion Agency, Japan
@@ -30,6 +30,11 @@
#include <sys/resource.h>
#endif
+#ifdef __ia64__
+#include <ucontext.h>
+extern unsigned long __libc_ia64_register_backing_store_base;
+#endif
+
void re_free_registers _((struct re_registers*));
void rb_io_fptr_finalize _((struct OpenFile*));
@@ -347,7 +352,7 @@ rb_data_object_alloc(klass, datap, dmark, dfree)
extern st_table *rb_class_tbl;
VALUE *rb_gc_stack_start = 0;
-#ifdef DJGPP
+#if defined(DJGPP) || defined(_WIN32_WCE)
static unsigned int STACK_LEVEL_MAX = 65535;
#else
#ifdef __human68k__
@@ -367,23 +372,45 @@ static unsigned int STACK_LEVEL_MAX = 655300;
# define SET_STACK_END VALUE stack_end; alloca(0);
# define STACK_END (&stack_end)
#else
-# if defined(__GNUC__) && defined(USE_BUILTIN_FRAME_ADDRESS)
+# if defined(__GNUC__) && defined(USE_BUILTIN_FRAME_ADDRESS) && !defined(__ia64__)
# define SET_STACK_END VALUE *stack_end = __builtin_frame_address(0)
# else
# define SET_STACK_END VALUE *stack_end = alloca(1)
# endif
# define STACK_END (stack_end)
#endif
-#ifdef __sparc__
+#if defined(sparc) || defined(__sparc__)
# define STACK_LENGTH (rb_gc_stack_start - STACK_END + 0x80)
+#elif STACK_GROW_DIRECTION < 0
+# define STACK_LENGTH (rb_gc_stack_start - STACK_END)
+#elif STACK_GROW_DIRECTION > 0
+# define STACK_LENGTH (STACK_END - rb_gc_stack_start)
#else
# define STACK_LENGTH ((STACK_END < rb_gc_stack_start) ? rb_gc_stack_start - STACK_END\
: STACK_END - rb_gc_stack_start)
#endif
+#if STACK_GROW_DIRECTION > 0
+# define STACK_UPPER(x, a, b) a
+#elif STACK_GROW_DIRECTION < 0
+# define STACK_UPPER(x, a, b) b
+#else
+static int
+stack_growup_p(addr)
+ VALUE *addr;
+{
+ SET_STACK_END;
+
+ if (STACK_END > addr) return Qtrue;
+ return Qfalse;
+}
+# define STACK_UPPER(x, a, b) (stack_growup_p(x) ? a : b)
+#endif
+
+#define GC_WARTER_MARK 512
#define CHECK_STACK(ret) do {\
SET_STACK_END;\
- (ret) = (STACK_LENGTH > STACK_LEVEL_MAX);\
+ (ret) = (STACK_LENGTH > STACK_LEVEL_MAX + GC_WARTER_MARK);\
} while (0)
int
@@ -391,7 +418,7 @@ ruby_stack_length(p)
VALUE **p;
{
SET_STACK_END;
- if (p) *p = STACK_END;
+ if (p) *p = STACK_UPPER(STACK_END, rb_gc_stack_start, STACK_END);
return STACK_LENGTH;
}
@@ -418,8 +445,6 @@ init_mark_stack()
#define MARK_STACK_EMPTY (mark_stack_ptr == mark_stack)
-static void rb_gc_mark_children(VALUE ptr);
-
static st_table *source_filenames;
char *
@@ -428,12 +453,12 @@ rb_source_filename(f)
{
char *name;
- if (!st_lookup(source_filenames, f, &name)) {
+ if (!st_lookup(source_filenames, (st_data_t)f, (st_data_t *)&name)) {
long len = strlen(f) + 1;
char *ptr = name = ALLOC_N(char, len + 1);
*ptr++ = 0;
MEMCPY(ptr, f, char, len);
- st_add_direct(source_filenames, ptr, name);
+ st_add_direct(source_filenames, (st_data_t)ptr, (st_data_t)name);
return ptr;
}
return name + 1;
@@ -448,7 +473,7 @@ mark_source_filename(f)
}
}
-static enum st_retval
+static int
sweep_source_filename(key, value)
char *key, *value;
{
@@ -462,6 +487,8 @@ sweep_source_filename(key, value)
}
}
+static void rb_gc_mark_children _((VALUE ptr));
+
static void
gc_mark_all()
{
@@ -491,7 +518,6 @@ gc_mark_rest()
MEMCPY(tmp_arry, mark_stack, VALUE, MARK_STACK_MAX);
init_mark_stack();
-
while(p != tmp_arry){
p--;
rb_gc_mark_children(*p);
@@ -543,7 +569,7 @@ rb_gc_mark_locations(start, end)
start = end;
end = tmp;
}
- n = end - start + 1;
+ n = end - start;
mark_locations_array(start,n);
}
@@ -565,7 +591,7 @@ rb_mark_tbl(tbl)
}
static int
-mark_hashentry(key, value)
+mark_keyvalue(key, value)
VALUE key;
VALUE value;
{
@@ -579,7 +605,7 @@ rb_mark_hash(tbl)
st_table *tbl;
{
if (!tbl) return;
- st_foreach(tbl, mark_hashentry, 0);
+ st_foreach(tbl, mark_keyvalue, 0);
}
void
@@ -596,12 +622,12 @@ rb_gc_mark(ptr)
VALUE ptr;
{
int ret;
- register RVALUE *obj = RANY(ptr);
+ register RVALUE *obj;
+ obj = RANY(ptr);
if (rb_special_const_p(ptr)) return; /* special const not marked */
if (obj->as.basic.flags == 0) return; /* free cell */
if (obj->as.basic.flags & FL_MARK) return; /* already marked */
-
obj->as.basic.flags |= FL_MARK;
CHECK_STACK(ret);
@@ -615,20 +641,29 @@ rb_gc_mark(ptr)
mark_stack_overflow = 1;
}
}
+ return;
}
- else {
- rb_gc_mark_children(ptr);
- }
+ rb_gc_mark_children(ptr);
}
-void
+static void
rb_gc_mark_children(ptr)
VALUE ptr;
{
- register RVALUE *obj = RANY(ptr);
-
+ register RVALUE *obj = RANY(ptr);
+
+ goto marking; /* skip */
+
+ again:
+ obj = RANY(ptr);
+ if (rb_special_const_p(ptr)) return; /* special const not marked */
+ if (obj->as.basic.flags == 0) return; /* free cell */
+ if (obj->as.basic.flags & FL_MARK) return; /* already marked */
+ obj->as.basic.flags |= FL_MARK;
+
+ marking:
if (FL_TEST(obj, FL_EXIVAR)) {
- rb_mark_generic_ivar((VALUE)obj);
+ rb_mark_generic_ivar(ptr);
}
switch (obj->as.basic.flags & T_MASK) {
@@ -648,6 +683,7 @@ rb_gc_mark_children(ptr)
case NODE_MASGN:
case NODE_RESCUE:
case NODE_RESBODY:
+ case NODE_CLASS:
rb_gc_mark((VALUE)obj->as.node.u2.node);
/* fall through */
case NODE_BLOCK: /* 1,3 */
@@ -667,8 +703,8 @@ rb_gc_mark_children(ptr)
case NODE_FCALL:
case NODE_DEFN:
case NODE_NEWLINE:
- rb_gc_mark((VALUE)obj->as.node.u3.node);
- break;
+ ptr = (VALUE)obj->as.node.u3.node;
+ goto again;
case NODE_WHILE: /* 1,2 */
case NODE_UNTIL:
@@ -684,6 +720,7 @@ rb_gc_mark_children(ptr)
case NODE_MATCH3:
case NODE_OP_ASGN_OR:
case NODE_OP_ASGN_AND:
+ case NODE_MODULE:
rb_gc_mark((VALUE)obj->as.node.u1.node);
/* fall through */
case NODE_METHOD: /* 2 */
@@ -693,15 +730,13 @@ rb_gc_mark_children(ptr)
case NODE_DASGN:
case NODE_DASGN_CURR:
case NODE_IASGN:
- case NODE_CDECL:
case NODE_CVDECL:
case NODE_CVASGN:
- case NODE_MODULE:
case NODE_COLON3:
case NODE_OPT_N:
case NODE_EVSTR:
- rb_gc_mark((VALUE)obj->as.node.u2.node);
- break;
+ ptr = (VALUE)obj->as.node.u2.node;
+ goto again;
case NODE_HASH: /* 1 */
case NODE_LIT:
@@ -715,15 +750,17 @@ rb_gc_mark_children(ptr)
case NODE_YIELD:
case NODE_COLON2:
case NODE_ARGS:
- rb_gc_mark((VALUE)obj->as.node.u1.node);
- break;
+ case NODE_SPLAT:
+ case NODE_SVALUE:
+ ptr = (VALUE)obj->as.node.u1.node;
+ goto again;
case NODE_SCOPE: /* 2,3 */
- case NODE_CLASS:
case NODE_BLOCK_PASS:
+ case NODE_CDECL:
rb_gc_mark((VALUE)obj->as.node.u3.node);
- rb_gc_mark((VALUE)obj->as.node.u2.node);
- break;
+ ptr = (VALUE)obj->as.node.u2.node;
+ goto again;
case NODE_ZARRAY: /* - */
case NODE_ZSUPER:
@@ -753,11 +790,11 @@ rb_gc_mark_children(ptr)
case NODE_ALLOCA:
mark_locations_array((VALUE*)obj->as.node.u1.value,
obj->as.node.u3.cnt);
- rb_gc_mark((VALUE)obj->as.node.u2.node);
- break;
+ ptr = (VALUE)obj->as.node.u2.node;
+ goto again;
#endif
- default:
+ default: /* unlisted NODE */
if (is_pointer_to_heap(obj->as.node.u1.node)) {
rb_gc_mark((VALUE)obj->as.node.u1.node);
}
@@ -776,14 +813,15 @@ rb_gc_mark_children(ptr)
case T_ICLASS:
case T_CLASS:
case T_MODULE:
- rb_gc_mark(obj->as.klass.super);
rb_mark_tbl(obj->as.klass.m_tbl);
rb_mark_tbl(obj->as.klass.iv_tbl);
- break;
+ ptr = obj->as.klass.super;
+ goto again;
case T_ARRAY:
if (FL_TEST(obj, ELTS_SHARED)) {
- rb_gc_mark(obj->as.array.aux.shared);
+ ptr = obj->as.array.aux.shared;
+ goto again;
}
else {
long i, len = obj->as.array.len;
@@ -797,13 +835,14 @@ rb_gc_mark_children(ptr)
case T_HASH:
rb_mark_hash(obj->as.hash.tbl);
- rb_gc_mark(obj->as.hash.ifnone);
- break;
+ ptr = obj->as.hash.ifnone;
+ goto again;
case T_STRING:
#define STR_ASSOC FL_USER3 /* copied from string.c */
if (FL_TEST(obj, ELTS_SHARED|STR_ASSOC)) {
- rb_gc_mark(obj->as.string.aux.shared);
+ ptr = obj->as.string.aux.shared;
+ goto again;
}
break;
@@ -824,14 +863,15 @@ rb_gc_mark_children(ptr)
case T_MATCH:
if (obj->as.match.str) {
- rb_gc_mark((VALUE)obj->as.match.str);
+ ptr = obj->as.match.str;
+ goto again;
}
break;
case T_VARMAP:
rb_gc_mark(obj->as.varmap.val);
- rb_gc_mark((VALUE)obj->as.varmap.next);
- break;
+ ptr = (VALUE)obj->as.varmap.next;
+ goto again;
case T_SCOPE:
if (obj->as.scope.local_vars && (obj->as.scope.flags & SCOPE_MALLOC)) {
@@ -839,19 +879,19 @@ rb_gc_mark_children(ptr)
VALUE *vars = &obj->as.scope.local_vars[-1];
while (n--) {
- rb_gc_mark(*vars);
- vars++;
+ rb_gc_mark(*vars++);
}
}
break;
case T_STRUCT:
{
- long i, len = obj->as.rstruct.len;
+ long len = obj->as.rstruct.len;
VALUE *ptr = obj->as.rstruct.ptr;
- for (i=0; i < len; i++)
+ while (len--) {
rb_gc_mark(*ptr++);
+ }
}
break;
@@ -864,15 +904,6 @@ rb_gc_mark_children(ptr)
static void obj_free _((VALUE));
-static unsigned long
-size_of_table(tbl)
- struct st_table *tbl;
-{
- if (!tbl) return 0;
- return tbl->num_bins * sizeof(struct st_table_entry *) +
- tbl->num_entries * 4 * sizeof(VALUE);
-}
-
static void
gc_sweep()
{
@@ -929,32 +960,7 @@ gc_sweep()
}
else {
RBASIC(p)->flags &= ~FL_MARK;
- live += sizeof(VALUE);
- switch (BUILTIN_TYPE(p)) {
- case T_OBJECT:
- live += size_of_table(ROBJECT(p)->iv_tbl);
- break;
- case T_CLASS:
- case T_ICLASS:
- live += size_of_table(RCLASS(p)->iv_tbl);
- live += size_of_table(RCLASS(p)->m_tbl);
- break;
- case T_STRING:
- live += RSTRING(p)->len+1;
- break;
- case T_ARRAY:
- live += RARRAY(p)->len * sizeof(VALUE);
- break;
- case T_HASH:
- live += size_of_table(RHASH(p)->tbl);
- break;
- case T_BIGNUM:
- live += RBIGNUM(p)->len * sizeof(BDIGIT);
- break;
- case T_STRUCT:
- live += RSTRUCT(p)->len * sizeof(VALUE);
- break;
- }
+ live++;
}
p++;
}
@@ -971,7 +977,8 @@ gc_sweep()
freed += n;
}
}
- malloc_limit = live;
+ malloc_limit += (malloc_increase - malloc_limit) * (double)live / (live + freed);
+ if (malloc_limit < GC_MALLOC_LIMIT) malloc_limit = GC_MALLOC_LIMIT;
malloc_increase = 0;
if (freed < FREE_MIN) {
add_heap();
@@ -1046,6 +1053,7 @@ obj_free(obj)
break;
case T_MODULE:
case T_CLASS:
+ rb_clear_cache_by_class((VALUE)obj);
st_free_table(RANY(obj)->as.klass.m_tbl);
if (RANY(obj)->as.object.iv_tbl) {
st_free_table(RANY(obj)->as.object.iv_tbl);
@@ -1154,6 +1162,7 @@ rb_gc_mark_frame(frame)
{
mark_locations_array(frame->argv, frame->argc);
rb_gc_mark(frame->cbase);
+ rb_gc_mark((VALUE)frame->node);
}
#ifdef __GNUC__
@@ -1208,7 +1217,6 @@ rb_gc()
}
return;
}
-
if (during_gc) return;
during_gc++;
@@ -1233,19 +1241,42 @@ rb_gc()
}
FLUSH_REGISTER_WINDOWS;
- /* This assumes that all registers are saved into the jmp_buf */
+ /* This assumes that all registers are saved into the jmp_buf (and stack) */
setjmp(save_regs_gc_mark);
mark_locations_array((VALUE*)save_regs_gc_mark, sizeof(save_regs_gc_mark) / sizeof(VALUE *));
+#if STACK_GROW_DIRECTION < 0
+ rb_gc_mark_locations((VALUE*)STACK_END, rb_gc_stack_start);
+#elif STACK_GROW_DIRECTION > 0
rb_gc_mark_locations(rb_gc_stack_start, (VALUE*)STACK_END);
-#if defined(__human68k__)
- rb_gc_mark_locations((VALUE*)((char*)rb_gc_stack_start + 2),
- (VALUE*)((char*)STACK_END + 2));
+#else
+ if ((VALUE*)STACK_END < rb_gc_stack_start)
+ rb_gc_mark_locations((VALUE*)STACK_END, rb_gc_stack_start);
+ else
+ rb_gc_mark_locations(rb_gc_stack_start, (VALUE*)STACK_END);
+#endif
+#ifdef __ia64__
+ /* mark backing store (flushed register window on the stack) */
+ /* the basic idea from guile GC code */
+ {
+ ucontext_t ctx;
+ VALUE *top, *bot;
+ getcontext(&ctx);
+ mark_locations_array((VALUE*)&ctx.uc_mcontext,
+ ((size_t)(sizeof(VALUE)-1 + sizeof ctx.uc_mcontext)/sizeof(VALUE)));
+ bot = (VALUE*)__libc_ia64_register_backing_store_base;
+ top = (VALUE*)ctx.uc_mcontext.sc_ar_bsp;
+ rb_gc_mark_locations(bot, top);
+ }
+#endif
+#if defined(__human68k__) || defined(__mc68000__)
+ rb_gc_mark_locations((VALUE*)((char*)STACK_END + 2),
+ (VALUE*)((char*)rb_gc_stack_start + 2));
#endif
rb_gc_mark_threads();
/* mark protected global variables */
for (list = global_List; list; list = list->next) {
- rb_gc_mark(*list->varptr);
+ rb_gc_mark_maybe(*list->varptr);
}
rb_mark_end_proc();
rb_gc_mark_global_tbl();
@@ -1286,6 +1317,13 @@ Init_stack(addr)
rb_gc_stack_start = _SEND;
#else
if (!addr) addr = (VALUE *)&addr;
+ if (rb_gc_stack_start) {
+ if (STACK_UPPER(&addr,
+ rb_gc_stack_start > --addr,
+ rb_gc_stack_start < ++addr))
+ rb_gc_stack_start = addr;
+ return;
+ }
rb_gc_stack_start = addr;
#endif
#ifdef HAVE_GETRLIMIT
@@ -1299,6 +1337,14 @@ Init_stack(addr)
STACK_LEVEL_MAX = (rlim.rlim_cur - space) / sizeof(VALUE);
}
}
+#ifdef __ia64__
+ /* ruby crashes on IA64 if compiled with optimizer on */
+ /* when if STACK_LEVEL_MAX is greater than this magic number */
+ /* I know this is a kludge. I suspect optimizer bug */
+#define IA64_MAGIC_STACK_LIMIT 49152
+ if (STACK_LEVEL_MAX > IA64_MAGIC_STACK_LIMIT)
+ STACK_LEVEL_MAX = IA64_MAGIC_STACK_LIMIT;
+#endif
#endif
}
@@ -1385,6 +1431,7 @@ os_each_obj(argc, argv)
{
VALUE of;
+ rb_secure(4);
if (rb_scan_args(argc, argv, "01", &of) == 0) {
return os_live_obj();
}
@@ -1396,25 +1443,25 @@ os_each_obj(argc, argv)
static VALUE finalizers;
static VALUE
-add_final(os, proc)
- VALUE os, proc;
+add_final(os, block)
+ VALUE os, block;
{
rb_warn("ObjectSpace::add_finalizer is deprecated; use define_finalizer");
- if (!rb_obj_is_kind_of(proc, rb_cProc)) {
- rb_raise(rb_eArgError, "wrong type argument %s (Proc required)",
- rb_class2name(CLASS_OF(proc)));
+ if (!rb_respond_to(block, rb_intern("call"))) {
+ rb_raise(rb_eArgError, "wrong type argument %s (should be callable)",
+ rb_obj_classname(block));
}
- rb_ary_push(finalizers, proc);
- return proc;
+ rb_ary_push(finalizers, block);
+ return block;
}
static VALUE
-rm_final(os, proc)
- VALUE os, proc;
+rm_final(os, block)
+ VALUE os, block;
{
rb_warn("ObjectSpace::remove_finalizer is deprecated; use undefine_finalizer");
- rb_ary_delete(finalizers, proc);
- return proc;
+ rb_ary_delete(finalizers, block);
+ return block;
}
static VALUE
@@ -1439,7 +1486,7 @@ undefine_final(os, obj)
VALUE os, obj;
{
if (finalizer_table) {
- st_delete(finalizer_table, &obj, 0);
+ st_delete(finalizer_table, (st_data_t*)&obj, 0);
}
return obj;
}
@@ -1450,15 +1497,15 @@ define_final(argc, argv, os)
VALUE *argv;
VALUE os;
{
- VALUE obj, proc, table;
+ VALUE obj, block, table;
- rb_scan_args(argc, argv, "11", &obj, &proc);
+ rb_scan_args(argc, argv, "11", &obj, &block);
if (argc == 1) {
- proc = rb_f_lambda();
+ block = rb_block_proc();
}
- else if (!rb_obj_is_kind_of(proc, rb_cProc)) {
- rb_raise(rb_eArgError, "wrong type argument %s (Proc required)",
- rb_class2name(CLASS_OF(proc)));
+ else if (!rb_respond_to(block, rb_intern("call"))) {
+ rb_raise(rb_eArgError, "wrong type argument %s (should be callable)",
+ rb_obj_classname(block));
}
need_call_final = 1;
FL_SET(obj, FL_FINALIZE);
@@ -1467,12 +1514,12 @@ define_final(argc, argv, os)
finalizer_table = st_init_numtable();
}
if (st_lookup(finalizer_table, obj, &table)) {
- rb_ary_push(table, proc);
+ rb_ary_push(table, block);
}
else {
- st_add_direct(finalizer_table, obj, rb_ary_new3(1, proc));
+ st_add_direct(finalizer_table, obj, rb_ary_new3(1, block));
}
- return proc;
+ return block;
}
void
@@ -1504,20 +1551,22 @@ run_final(obj)
VALUE obj;
{
long i;
- int status;
+ int status, critical_save = rb_thread_critical;
VALUE args[2], table;
+ rb_thread_critical = Qtrue;
args[1] = rb_ary_new3(1, rb_obj_id(obj)); /* make obj into id */
for (i=0; i<RARRAY(finalizers)->len; i++) {
args[0] = RARRAY(finalizers)->ptr[i];
rb_protect((VALUE(*)_((VALUE)))run_single_final, (VALUE)args, &status);
}
- if (finalizer_table && st_delete(finalizer_table, &obj, &table)) {
+ if (finalizer_table && st_delete(finalizer_table, (st_data_t*)&obj, &table)) {
for (i=0; i<RARRAY(table)->len; i++) {
args[0] = RARRAY(table)->ptr[i];
rb_protect((VALUE(*)_((VALUE)))run_single_final, (VALUE)args, &status);
}
}
+ rb_thread_critical = critical_save;
}
void
@@ -1555,7 +1604,12 @@ rb_gc_call_finalizer_at_exit()
if (BUILTIN_TYPE(p) == T_DATA &&
DATA_PTR(p) && RANY(p)->as.data.dfree) {
p->as.free.flags = 0;
- (*RANY(p)->as.data.dfree)(DATA_PTR(p));
+ if ((long)RANY(p)->as.data.dfree == -1) {
+ RUBY_CRITICAL(free(DATA_PTR(p)));
+ }
+ else if (RANY(p)->as.data.dfree) {
+ (*RANY(p)->as.data.dfree)(DATA_PTR(p));
+ }
}
else if (BUILTIN_TYPE(p) == T_FILE) {
p->as.free.flags = 0;
diff --git a/hash.c b/hash.c
index 245ba53e5a..1a4ab1edbb 100644
--- a/hash.c
+++ b/hash.c
@@ -6,7 +6,7 @@
$Date$
created at: Mon Nov 22 18:51:18 JST 1993
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
Copyright (C) 2000 Information-technology Promotion Agency, Japan
@@ -16,6 +16,7 @@
#include "st.h"
#include "util.h"
#include "rubysig.h"
+#include "version.h"
#ifdef __APPLE__
#include <crt_externs.h>
@@ -44,7 +45,7 @@ rb_hash_freeze(hash)
VALUE rb_cHash;
static VALUE envtbl;
-static ID id_hash, id_yield, id_default;
+static ID id_hash, id_call, id_default;
VALUE
rb_hash(obj)
@@ -65,6 +66,8 @@ rb_any_cmp(a, b)
VALUE a, b;
{
VALUE args[2];
+
+ if (a == b) return 0;
if (FIXNUM_P(a) && FIXNUM_P(b)) {
return a != b;
}
@@ -72,10 +75,10 @@ rb_any_cmp(a, b)
TYPE(b) == T_STRING && RBASIC(b)->klass == rb_cString) {
return rb_str_cmp(a, b);
}
+ if (a == Qundef || b == Qundef) return -1;
if (SYMBOL_P(a) && SYMBOL_P(b)) {
return a != b;
}
- if (a == Qundef || b == Qundef) return -1;
args[0] = a;
args[1] = b;
@@ -99,15 +102,10 @@ rb_any_hash(a)
break;
default:
- DEFER_INTS;
hval = rb_funcall(a, id_hash, 0);
- if (FIXNUM_P(hval)) {
- hval %= 536870923;
- }
- else {
+ if (!FIXNUM_P(hval)) {
hval = rb_funcall(hval, '%', 1, INT2FIX(536870923));
}
- ENABLE_INTS;
return (int)FIX2LONG(hval);
}
}
@@ -120,7 +118,7 @@ static struct st_hash_type objhash = {
struct rb_hash_foreach_arg {
VALUE hash;
enum st_retval (*func)();
- char *arg;
+ VALUE arg;
};
static int
@@ -144,7 +142,7 @@ static VALUE
rb_hash_foreach_call(arg)
struct rb_hash_foreach_arg *arg;
{
- st_foreach(RHASH(arg->hash)->tbl, rb_hash_foreach_iter, arg);
+ st_foreach(RHASH(arg->hash)->tbl, rb_hash_foreach_iter, (st_data_t)arg);
return Qnil;
}
@@ -167,7 +165,7 @@ static int
rb_hash_foreach(hash, func, farg)
VALUE hash;
enum st_retval (*func)();
- char *farg;
+ VALUE farg;
{
struct rb_hash_foreach_arg arg;
@@ -178,8 +176,9 @@ rb_hash_foreach(hash, func, farg)
return rb_ensure(rb_hash_foreach_call, (VALUE)&arg, rb_hash_foreach_ensure, hash);
}
+static VALUE hash_alloc _((VALUE));
static VALUE
-rb_hash_s_alloc(klass)
+hash_alloc(klass)
VALUE klass;
{
NEWOBJ(hash, struct RHash);
@@ -194,7 +193,7 @@ rb_hash_s_alloc(klass)
VALUE
rb_hash_new()
{
- return rb_hash_s_alloc(rb_cHash);
+ return hash_alloc(rb_cHash);
}
static VALUE
@@ -210,7 +209,7 @@ rb_hash_initialize(argc, argv, hash)
if (argc > 0) {
rb_raise(rb_eArgError, "wrong number of arguments");
}
- RHASH(hash)->ifnone = rb_f_lambda();
+ RHASH(hash)->ifnone = rb_block_proc();
FL_SET(hash, HASH_PROC_DEFAULT);
}
else {
@@ -231,7 +230,7 @@ rb_hash_s_create(argc, argv, klass)
int i;
if (argc == 1 && TYPE(argv[0]) == T_HASH) {
- hash = rb_obj_alloc(klass);
+ hash = hash_alloc(klass);
RHASH(hash)->ifnone = Qnil;
RHASH(hash)->tbl = st_copy(RHASH(argv[0])->tbl);
@@ -243,7 +242,7 @@ rb_hash_s_create(argc, argv, klass)
rb_raise(rb_eArgError, "odd number args for Hash");
}
- hash = rb_obj_alloc(klass);
+ hash = hash_alloc(klass);
for (i=0; i<argc; i+=2) {
rb_hash_aset(hash, argv[i], argv[i + 1]);
}
@@ -275,7 +274,7 @@ rb_hash_rehash(hash)
rb_hash_modify(hash);
tbl = st_init_table_with_size(&objhash, RHASH(hash)->tbl->num_entries);
- st_foreach(RHASH(hash)->tbl, rb_hash_rehash_i, tbl);
+ st_foreach(RHASH(hash)->tbl, rb_hash_rehash_i, (st_data_t)tbl);
st_free_table(RHASH(hash)->tbl);
RHASH(hash)->tbl = tbl;
@@ -330,7 +329,7 @@ rb_hash_default(argc, argv, hash)
rb_scan_args(argc, argv, "01", &key);
if (FL_TEST(hash, HASH_PROC_DEFAULT)) {
- return rb_funcall(RHASH(hash)->ifnone, id_yield, 2, hash, key);
+ return rb_funcall(RHASH(hash)->ifnone, id_call, 2, hash, key);
}
return RHASH(hash)->ifnone;
}
@@ -376,7 +375,7 @@ rb_hash_index(hash, value)
args[0] = value;
args[1] = Qnil;
- st_foreach(RHASH(hash)->tbl, index_i, args);
+ st_foreach(RHASH(hash)->tbl, index_i, (st_data_t)args);
return args[1];
}
@@ -390,7 +389,7 @@ rb_hash_indexes(argc, argv, hash)
VALUE indexes;
int i;
- rb_warn("Hash#%s is deprecated; use Hash#select",
+ rb_warn("Hash#%s is deprecated; use Hash#values_at",
rb_id2name(rb_frame_last_func()));
indexes = rb_ary_new2(argc);
for (i=0; i<argc; i++) {
@@ -408,12 +407,12 @@ rb_hash_delete(hash, key)
rb_hash_modify(hash);
if (RHASH(hash)->iter_lev > 0) {
- if (st_delete_safe(RHASH(hash)->tbl, &key, &val, Qundef)) {
+ if (st_delete_safe(RHASH(hash)->tbl, (st_data_t*)&key, &val, Qundef)) {
FL_SET(hash, HASH_DELETED);
return val;
}
}
- else if (st_delete(RHASH(hash)->tbl, &key, &val))
+ else if (st_delete(RHASH(hash)->tbl, (st_data_t*)&key, &val))
return val;
if (rb_block_given_p()) {
return rb_yield(key);
@@ -448,25 +447,25 @@ rb_hash_shift(hash)
rb_hash_modify(hash);
var.stop = 0;
- st_foreach(RHASH(hash)->tbl, shift_i, &var);
+ st_foreach(RHASH(hash)->tbl, shift_i, (st_data_t)&var);
if (var.stop) {
return rb_assoc_new(var.key, var.val);
}
else if (FL_TEST(hash, HASH_PROC_DEFAULT)) {
- return rb_funcall(RHASH(hash)->ifnone, id_yield, 2, hash, Qnil);
+ return rb_funcall(RHASH(hash)->ifnone, id_call, 2, hash, Qnil);
}
else {
return RHASH(hash)->ifnone;
}
}
-static int
+static enum st_retval
delete_if_i(key, value)
VALUE key, value;
{
if (key == Qundef) return ST_CONTINUE;
- if (RTEST(rb_yield(rb_assoc_new(key, value))))
+ if (RTEST(rb_yield_values(2, key, value)))
return ST_DELETE;
return ST_CONTINUE;
}
@@ -497,21 +496,18 @@ rb_hash_reject(hash)
return rb_hash_delete_if(rb_obj_dup(hash));
}
-static int
+static enum st_retval
select_i(key, value, result)
- VALUE key, value;
+ VALUE key, value, result;
{
- VALUE assoc;
-
if (key == Qundef) return ST_CONTINUE;
- assoc = rb_assoc_new(key, value);
- if (RTEST(rb_yield(assoc)))
- rb_ary_push(result, assoc);
+ if (RTEST(rb_yield_values(2, key, value)))
+ rb_ary_push(result, rb_assoc_new(key, value));
return ST_CONTINUE;
}
VALUE
-rb_hash_select(argc, argv, hash)
+rb_hash_values_at(argc, argv, hash)
int argc;
VALUE *argv;
VALUE hash;
@@ -519,17 +515,31 @@ rb_hash_select(argc, argv, hash)
VALUE result = rb_ary_new();
long i;
- if (rb_block_given_p()) {
- if (argc > 0) {
- rb_raise(rb_eArgError, "wrong number arguments(%d for 0)", argc);
- }
- rb_hash_foreach(hash, select_i, result);
+ for (i=0; i<argc; i++) {
+ rb_ary_push(result, rb_hash_aref(hash, argv[i]));
}
- else {
- for (i=0; i<argc; i++) {
- rb_ary_push(result, rb_hash_aref(hash, argv[i]));
- }
+ return result;
+}
+
+VALUE
+rb_hash_select(argc, argv, hash)
+ int argc;
+ VALUE *argv;
+ VALUE hash;
+{
+ VALUE result;
+
+ if (!rb_block_given_p()) {
+#if RUBY_VERSION_CODE < 181
+ rb_warn("Hash#select(key..) is deprecated; use Hash#values_at");
+#endif
+ return rb_hash_values_at(argc, argv, hash);
+ }
+ if (argc > 0) {
+ rb_raise(rb_eArgError, "wrong number arguments(%d for 0)", argc);
}
+ result = rb_ary_new();
+ rb_hash_foreach(hash, select_i, result);
return result;
}
@@ -610,7 +620,7 @@ rb_hash_empty_p(hash)
return Qfalse;
}
-static int
+static enum st_retval
each_value_i(key, value)
VALUE key, value;
{
@@ -627,7 +637,7 @@ rb_hash_each_value(hash)
return hash;
}
-static int
+static enum st_retval
each_key_i(key, value)
VALUE key, value;
{
@@ -644,12 +654,12 @@ rb_hash_each_key(hash)
return hash;
}
-static int
+static enum st_retval
each_pair_i(key, value)
VALUE key, value;
{
if (key == Qundef) return ST_CONTINUE;
- rb_yield(rb_assoc_new(key, value));
+ rb_yield_values(2, key, value);
return ST_CONTINUE;
}
@@ -833,7 +843,7 @@ rb_hash_has_value(hash, val)
data[0] = Qfalse;
data[1] = val;
- st_foreach(RHASH(hash)->tbl, rb_hash_search_value, data);
+ st_foreach(RHASH(hash)->tbl, rb_hash_search_value, (st_data_t)data);
return data[0];
}
@@ -868,7 +878,12 @@ rb_hash_equal(hash1, hash2)
struct equal_data data;
if (hash1 == hash2) return Qtrue;
- if (TYPE(hash2) != T_HASH) return Qfalse;
+ if (TYPE(hash2) != T_HASH) {
+ if (!rb_respond_to(hash2, rb_intern("to_hash"))) {
+ return Qfalse;
+ }
+ return rb_equal(hash2, hash1);
+ }
if (RHASH(hash1)->tbl->num_entries != RHASH(hash2)->tbl->num_entries)
return Qfalse;
if (!(rb_equal(RHASH(hash1)->ifnone, RHASH(hash2)->ifnone) &&
@@ -877,7 +892,7 @@ rb_hash_equal(hash1, hash2)
data.tbl = RHASH(hash2)->tbl;
data.result = Qtrue;
- st_foreach(RHASH(hash1)->tbl, equal_i, &data);
+ st_foreach(RHASH(hash1)->tbl, equal_i, (st_data_t)&data);
return data.result;
}
@@ -919,7 +934,7 @@ rb_hash_update_block_i(key, value, hash)
{
if (key == Qundef) return ST_CONTINUE;
if (rb_hash_has_key(hash, key)) {
- value = rb_yield(rb_ary_new3(3, key, rb_hash_aref(hash, key), value));
+ value = rb_yield_values(3, key, rb_hash_aref(hash, key), value);
}
rb_hash_aset(hash, key, value);
return ST_CONTINUE;
@@ -939,6 +954,13 @@ rb_hash_update(hash1, hash2)
return hash1;
}
+static VALUE
+rb_hash_merge(hash1, hash2)
+ VALUE hash1, hash2;
+{
+ return rb_hash_update(rb_obj_dup(hash1), hash2);
+}
+
static int path_tainted = -1;
static char **origenviron;
@@ -960,6 +982,25 @@ extern char **environ;
#endif
static VALUE
+env_str_new(ptr, len)
+ const char *ptr;
+ long len;
+{
+ VALUE str = rb_tainted_str_new(ptr, len);
+
+ rb_obj_freeze(str);
+ return str;
+}
+
+static VALUE
+env_str_new2(ptr)
+ const char *ptr;
+{
+ if (!ptr) return Qnil;
+ return env_str_new(ptr, strlen(ptr));
+}
+
+static VALUE
env_delete(obj, name)
VALUE obj, name;
{
@@ -973,14 +1014,15 @@ env_delete(obj, name)
}
val = getenv(nam);
if (val) {
- VALUE value = rb_tainted_str_new2(val);
+ VALUE value = env_str_new2(val);
ruby_setenv(nam, 0);
-#ifdef __BORLANDC__
- if (strcmpi(nam, "PATH") == 0) {
+#ifdef ENV_IGNORECASE
+ if (strcasecmp(nam, PATH_ENV) == 0)
#else
- if (strcmp(nam, "PATH") == 0) {
+ if (strcmp(nam, PATH_ENV) == 0)
#endif
+ {
path_tainted = 0;
}
return value;
@@ -992,8 +1034,10 @@ static VALUE
env_delete_m(obj, name)
VALUE obj, name;
{
- VALUE val = env_delete(obj, name);
- if (rb_block_given_p()) rb_yield(name);
+ VALUE val;
+
+ val = env_delete(obj, name);
+ if (NIL_P(val) && rb_block_given_p()) rb_yield(name);
return val;
}
@@ -1010,13 +1054,18 @@ rb_f_getenv(obj, name)
}
env = getenv(nam);
if (env) {
-#ifdef __BORLANDC__
- if (strcmpi(nam, "PATH") == 0 && !rb_env_path_tainted())
+#ifdef ENV_IGNORECASE
+ if (strcasecmp(nam, PATH_ENV) == 0 && !rb_env_path_tainted())
#else
- if (strcmp(nam, "PATH") == 0 && !rb_env_path_tainted())
+ if (strcmp(nam, PATH_ENV) == 0 && !rb_env_path_tainted())
#endif
- return rb_str_new2(env);
- return rb_tainted_str_new2(env);
+ {
+ VALUE str = rb_str_new2(env);
+
+ rb_obj_freeze(str);
+ return str;
+ }
+ return env_str_new2(env);
}
return Qnil;
}
@@ -1048,13 +1097,13 @@ env_fetch(argc, argv)
}
return if_none;
}
-#ifdef __BORLANDC__
- if (strcmpi(nam, "PATH") == 0 && !rb_env_path_tainted())
+#ifdef ENV_IGNORECASE
+ if (strcasecmp(nam, PATH_ENV) == 0 && !rb_env_path_tainted())
#else
- if (strcmp(nam, "PATH") == 0 && !rb_env_path_tainted())
+ if (strcmp(nam, PATH_ENV) == 0 && !rb_env_path_tainted())
#endif
return rb_str_new2(env);
- return rb_tainted_str_new2(env);
+ return env_str_new2(env);
}
static void
@@ -1068,14 +1117,14 @@ int
rb_env_path_tainted()
{
if (path_tainted < 0) {
- path_tainted_p(getenv("PATH"));
+ path_tainted_p(getenv(PATH_ENV));
}
return path_tainted;
}
static int
envix(nam)
-char *nam;
+ const char *nam;
{
register int i, len = strlen(nam);
char **env;
@@ -1083,8 +1132,8 @@ char *nam;
env = GET_ENVIRON(environ);
for (i = 0; env[i]; i++) {
if (
-#ifdef WIN32
- strnicmp(env[i],nam,len) == 0
+#ifdef ENV_IGNORECASE
+ strncasecmp(env[i],nam,len) == 0
#else
memcmp(env[i],nam,len) == 0
#endif
@@ -1100,7 +1149,7 @@ ruby_setenv(name, value)
const char *name;
const char *value;
{
-#if defined(WIN32) && !defined(__CYGWIN32__)
+#if defined(_WIN32)
/* The sane way to deal with the environment.
* Has these advantages over putenv() & co.:
* * enables us to store a truly empty value in the
@@ -1211,7 +1260,11 @@ env_aset(obj, nm, val)
rb_raise(rb_eArgError, "bad environment variable value");
ruby_setenv(name, value);
- if (strcmp(name, "PATH") == 0) {
+#ifdef ENV_IGNORECASE
+ if (strcasecmp(name, PATH_ENV) == 0) {
+#else
+ if (strcmp(name, PATH_ENV) == 0) {
+#endif
if (OBJ_TAINTED(val)) {
/* already tainted, no check */
path_tainted = 1;
@@ -1234,7 +1287,7 @@ env_keys()
while (*env) {
char *s = strchr(*env, '=');
if (s) {
- rb_ary_push(ary, rb_tainted_str_new(*env, s-*env));
+ rb_ary_push(ary, env_str_new(*env, s-*env));
}
env++;
}
@@ -1252,7 +1305,7 @@ env_each_key(hash)
while (*env) {
char *s = strchr(*env, '=');
if (s) {
- rb_yield(rb_tainted_str_new(*env, s-*env));
+ rb_yield(env_str_new(*env, s-*env));
}
env++;
}
@@ -1270,7 +1323,7 @@ env_values()
while (*env) {
char *s = strchr(*env, '=');
if (s) {
- rb_ary_push(ary, rb_tainted_str_new2(s+1));
+ rb_ary_push(ary, env_str_new2(s+1));
}
env++;
}
@@ -1288,7 +1341,7 @@ env_each_value(hash)
while (*env) {
char *s = strchr(*env, '=');
if (s) {
- rb_yield(rb_tainted_str_new2(s+1));
+ rb_yield(env_str_new2(s+1));
}
env++;
}
@@ -1306,8 +1359,8 @@ env_each(hash)
while (*env) {
char *s = strchr(*env, '=');
if (s) {
- rb_yield(rb_assoc_new(rb_tainted_str_new(*env, s-*env),
- rb_tainted_str_new2(s+1)));
+ rb_yield_values(2, env_str_new(*env, s-*env),
+ env_str_new2(s+1));
}
env++;
}
@@ -1319,24 +1372,21 @@ static VALUE
env_reject_bang()
{
volatile VALUE keys;
- VALUE *ptr;
- long len;
+ long i;
int del = 0;
rb_secure(4);
keys = env_keys();
- ptr = RARRAY(keys)->ptr;
- len = RARRAY(keys)->len;
- while (len--) {
- VALUE val = rb_f_getenv(Qnil, *ptr);
+ for (i=0; i<RARRAY(keys)->len; i++) {
+ VALUE val = rb_f_getenv(Qnil, RARRAY(keys)->ptr[i]);
if (!NIL_P(val)) {
- if (RTEST(rb_yield(rb_assoc_new(*ptr, val)))) {
- env_delete(Qnil, *ptr);
+ if (RTEST(rb_yield_values(2, RARRAY(keys)->ptr[i], val))) {
+ FL_UNSET(RARRAY(keys)->ptr[i], FL_TAINT);
+ env_delete(Qnil, RARRAY(keys)->ptr[i]);
del++;
}
}
- ptr++;
}
if (del == 0) return Qnil;
return envtbl;
@@ -1350,39 +1400,68 @@ env_delete_if()
}
static VALUE
-env_select(argc, argv)
+env_values_at(argc, argv)
int argc;
VALUE *argv;
{
VALUE result = rb_ary_new();
long i;
- if (rb_block_given_p()) {
- char **env;
+ for (i=0; i<argc; i++) {
+ rb_ary_push(result, rb_f_getenv(Qnil, argv[i]));
+ }
+ return result;
+}
- if (argc > 0) {
- rb_raise(rb_eArgError, "wrong number arguments(%d for 0)", argc);
- }
- env = GET_ENVIRON(environ);
- while (*env) {
- char *s = strchr(*env, '=');
- if (s) {
- VALUE assoc = rb_assoc_new(rb_tainted_str_new(*env, s-*env),
- rb_tainted_str_new2(s+1));
- if (RTEST(rb_yield(assoc))) {
- rb_ary_push(result, assoc);
- }
+static VALUE
+env_select(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE result;
+ char **env;
+
+ if (!rb_block_given_p()) {
+ rb_warn("ENV.select(index..) is deprecated; use ENV.values_at");
+ return env_values_at(argc, argv);
+ }
+ if (argc > 0) {
+ rb_raise(rb_eArgError, "wrong number arguments(%d for 0)", argc);
+ }
+ result = rb_ary_new();
+ env = GET_ENVIRON(environ);
+ while (*env) {
+ char *s = strchr(*env, '=');
+ if (s) {
+ VALUE k = env_str_new(*env, s-*env);
+ VALUE v = env_str_new2(s+1);
+ if (RTEST(rb_yield_values(2, k, v))) {
+ rb_ary_push(result, rb_assoc_new(k, v));
}
- env++;
}
- FREE_ENVIRON(environ);
+ env++;
}
- else {
- for (i=0; i<argc; i++) {
- rb_ary_push(result, rb_f_getenv(Qnil, argv[i]));
+ FREE_ENVIRON(environ);
+
+ return result;
+}
+
+static VALUE
+env_clear()
+{
+ volatile VALUE keys;
+ long i;
+
+ rb_secure(4);
+ keys = env_keys();
+
+ for (i=0; i<RARRAY(keys)->len; i++) {
+ VALUE val = rb_f_getenv(Qnil, RARRAY(keys)->ptr[i]);
+ if (!NIL_P(val)) {
+ env_delete(Qnil, RARRAY(keys)->ptr[i]);
}
}
- return result;
+ return envtbl;
}
static VALUE
@@ -1431,8 +1510,8 @@ env_to_a()
while (*env) {
char *s = strchr(*env, '=');
if (s) {
- rb_ary_push(ary, rb_assoc_new(rb_tainted_str_new(*env, s-*env),
- rb_tainted_str_new2(s+1)));
+ rb_ary_push(ary, rb_assoc_new(env_str_new(*env, s-*env),
+ env_str_new2(s+1)));
}
env++;
}
@@ -1495,9 +1574,13 @@ env_has_value(dmy, value)
if (TYPE(value) != T_STRING) return Qfalse;
env = GET_ENVIRON(environ);
while (*env) {
- char *s = strchr(*env, '=')+1;
- if (s) {
+ char *s = strchr(*env, '=');
+ if (s++) {
+#ifdef ENV_IGNORECASE
+ if (strncasecmp(s, RSTRING(value)->ptr, strlen(s)) == 0) {
+#else
if (strncmp(s, RSTRING(value)->ptr, strlen(s)) == 0) {
+#endif
FREE_ENVIRON(environ);
return Qtrue;
}
@@ -1515,13 +1598,17 @@ env_index(dmy, value)
char **env;
VALUE str;
- if (TYPE(value) != T_STRING) return Qnil;
+ StringValue(value);
env = GET_ENVIRON(environ);
while (*env) {
- char *s = strchr(*env, '=')+1;
- if (s) {
+ char *s = strchr(*env, '=');
+ if (s++) {
+#ifdef ENV_IGNORECASE
+ if (strncasecmp(s, RSTRING(value)->ptr, strlen(s)) == 0) {
+#else
if (strncmp(s, RSTRING(value)->ptr, strlen(s)) == 0) {
- str = rb_tainted_str_new(*env, s-*env-1);
+#endif
+ str = env_str_new(*env, s-*env-1);
FREE_ENVIRON(environ);
return str;
}
@@ -1540,18 +1627,15 @@ env_indexes(argc, argv)
int i;
VALUE indexes = rb_ary_new2(argc);
- rb_warn("ENV.%s is deprecated; use ENV.select",
+ rb_warn("ENV.%s is deprecated; use ENV.values_at",
rb_id2name(rb_frame_last_func()));
for (i=0;i<argc;i++) {
- char *v = 0;
- if (TYPE(argv[i]) == T_STRING) {
- v = getenv(RSTRING(argv[i])->ptr);
- }
- if (v) {
- RARRAY(indexes)->ptr[i] = rb_tainted_str_new2(v);
+ VALUE tmp = rb_check_string_type(argv[i]);
+ if (NIL_P(tmp)) {
+ RARRAY(indexes)->ptr[i] = Qnil;
}
else {
- RARRAY(indexes)->ptr[i] = Qnil;
+ RARRAY(indexes)->ptr[i] = env_str_new2(getenv(RSTRING(tmp)->ptr));
}
RARRAY(indexes)->len = i+1;
}
@@ -1569,8 +1653,8 @@ env_to_hash()
while (*env) {
char *s = strchr(*env, '=');
if (s) {
- rb_hash_aset(hash, rb_tainted_str_new(*env, s-*env),
- rb_tainted_str_new2(s+1));
+ rb_hash_aset(hash, env_str_new(*env, s-*env),
+ env_str_new2(s+1));
}
env++;
}
@@ -1584,22 +1668,99 @@ env_reject()
return rb_hash_delete_if(env_to_hash());
}
+static VALUE
+env_shift()
+{
+ char **env;
+
+ env = GET_ENVIRON(environ);
+ if (*env) {
+ char *s = strchr(*env, '=');
+ if (s) {
+ VALUE key = env_str_new(*env, s-*env);
+ VALUE val = env_str_new2(getenv(RSTRING(key)->ptr));
+ env_delete(Qnil, key);
+ return rb_assoc_new(key, val);
+ }
+ }
+ FREE_ENVIRON(environ);
+ return Qnil;
+}
+
+static VALUE
+env_invert()
+{
+ return rb_hash_invert(env_to_hash());
+}
+
+static int
+env_replace_i(key, val, keys)
+ VALUE key, val, keys;
+{
+ if (key != Qundef) {
+ env_aset(Qnil, key, val);
+ if (rb_ary_includes(keys, key)) {
+ rb_ary_delete(keys, key);
+ }
+ }
+ return ST_CONTINUE;
+}
+
+static VALUE
+env_replace(env, hash)
+ VALUE env, hash;
+{
+ volatile VALUE keys = env_keys();
+ long i;
+
+ if (env == hash) return env;
+ hash = to_hash(hash);
+ st_foreach(RHASH(hash)->tbl, env_replace_i, keys);
+
+ for (i=0; i<RARRAY(keys)->len; i++) {
+ env_delete(env, RARRAY(keys)->ptr[i]);
+ }
+ return env;
+}
+
+static int
+env_update_i(key, val)
+ VALUE key, val;
+{
+ if (key != Qundef) {
+ if (rb_block_given_p()) {
+ val = rb_yield_values(3, key, rb_f_getenv(Qnil, key), val);
+ }
+ env_aset(Qnil, key, val);
+ }
+ return ST_CONTINUE;
+}
+
+static VALUE
+env_update(env, hash)
+ VALUE env, hash;
+{
+ if (env == hash) return env;
+ hash = to_hash(hash);
+ st_foreach(RHASH(hash)->tbl, env_update_i, 0);
+ return env;
+}
+
void
Init_Hash()
{
id_hash = rb_intern("hash");
- id_yield = rb_intern("yield");
+ id_call = rb_intern("call");
id_default = rb_intern("default");
rb_cHash = rb_define_class("Hash", rb_cObject);
rb_include_module(rb_cHash, rb_mEnumerable);
- rb_define_singleton_method(rb_cHash, "allocate", rb_hash_s_alloc, 0);
+ rb_define_alloc_func(rb_cHash, hash_alloc);
rb_define_singleton_method(rb_cHash, "[]", rb_hash_s_create, -1);
rb_define_method(rb_cHash,"initialize", rb_hash_initialize, -1);
-
- rb_define_method(rb_cHash,"copy_object", rb_hash_replace, 1);
+ rb_define_method(rb_cHash,"initialize_copy", rb_hash_replace, 1);
rb_define_method(rb_cHash,"rehash", rb_hash_rehash, 0);
rb_define_method(rb_cHash,"to_hash", rb_hash_to_hash, 0);
@@ -1630,6 +1791,7 @@ Init_Hash()
rb_define_method(rb_cHash,"keys", rb_hash_keys, 0);
rb_define_method(rb_cHash,"values", rb_hash_values, 0);
+ rb_define_method(rb_cHash,"values_at", rb_hash_values_at, -1);
rb_define_method(rb_cHash,"shift", rb_hash_shift, 0);
rb_define_method(rb_cHash,"delete", rb_hash_delete, 1);
@@ -1641,6 +1803,8 @@ Init_Hash()
rb_define_method(rb_cHash,"invert", rb_hash_invert, 0);
rb_define_method(rb_cHash,"update", rb_hash_update, 1);
rb_define_method(rb_cHash,"replace", rb_hash_replace, 1);
+ rb_define_method(rb_cHash,"merge!", rb_hash_update, 1);
+ rb_define_method(rb_cHash,"merge", rb_hash_merge, 1);
rb_define_method(rb_cHash,"include?", rb_hash_has_key, 1);
rb_define_method(rb_cHash,"member?", rb_hash_has_key, 1);
@@ -1664,13 +1828,18 @@ Init_Hash()
rb_define_singleton_method(envtbl,"each_value", env_each_value, 0);
rb_define_singleton_method(envtbl,"delete", env_delete_m, 1);
rb_define_singleton_method(envtbl,"delete_if", env_delete_if, 0);
+ rb_define_singleton_method(envtbl,"clear", env_clear, 0);
rb_define_singleton_method(envtbl,"reject", env_reject, 0);
rb_define_singleton_method(envtbl,"reject!", env_reject_bang, 0);
rb_define_singleton_method(envtbl,"select", env_select, -1);
- rb_define_singleton_method(envtbl,"to_s", env_to_s, 0);
+ rb_define_singleton_method(envtbl,"shift", env_shift, 0);
+ rb_define_singleton_method(envtbl,"invert", env_invert, 0);
+ rb_define_singleton_method(envtbl,"replace", env_replace, 1);
+ rb_define_singleton_method(envtbl,"update", env_update, 1);
rb_define_singleton_method(envtbl,"inspect", env_inspect, 0);
rb_define_singleton_method(envtbl,"rehash", env_none, 0);
rb_define_singleton_method(envtbl,"to_a", env_to_a, 0);
+ rb_define_singleton_method(envtbl,"to_s", env_to_s, 0);
rb_define_singleton_method(envtbl,"index", env_index, 1);
rb_define_singleton_method(envtbl,"indexes", env_indexes, -1);
rb_define_singleton_method(envtbl,"indices", env_indexes, -1);
@@ -1679,6 +1848,7 @@ Init_Hash()
rb_define_singleton_method(envtbl,"empty?", env_empty_p, 0);
rb_define_singleton_method(envtbl,"keys", env_keys, 0);
rb_define_singleton_method(envtbl,"values", env_values, 0);
+ rb_define_singleton_method(envtbl,"values_at", env_values_at, -1);
rb_define_singleton_method(envtbl,"include?", env_has_key, 1);
rb_define_singleton_method(envtbl,"member?", env_has_key, 1);
rb_define_singleton_method(envtbl,"has_key?", env_has_key, 1);
diff --git a/inits.c b/inits.c
index dd7af5afb1..1e89a36caf 100644
--- a/inits.c
+++ b/inits.c
@@ -6,7 +6,7 @@
$Date$
created at: Tue Dec 28 16:01:58 JST 1993
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
**********************************************************************/
diff --git a/instruby.rb b/instruby.rb
index d6bfa20c72..f005597ae2 100644
--- a/instruby.rb
+++ b/instruby.rb
@@ -3,55 +3,72 @@
load "./rbconfig.rb"
include Config
+$:.unshift File.join(CONFIG["srcdir"], "lib")
+require 'fileutils'
+require 'shellwords'
+require 'getopts'
+require 'tempfile'
+
File.umask(0)
-while arg = ARGV.shift
- case arg
- when /^--/ # ignore
- when /^-/
- $dryrun = /n/ =~ arg
- when /=/ # ignore
+def parse_args()
+ getopts('n', 'dest-dir:',
+ 'make:', 'make-flags:', 'mflags:',
+ 'mantype:doc')
+
+ $dryrun = $OPT['n']
+ $destdir = $OPT['dest-dir'] || ''
+ $make = $OPT['make'] || $make || 'make'
+ $mantype = $OPT['mantype']
+ mflags = ($OPT['make-flags'] || '').strip
+ mflags = ($OPT['mflags'] || '').strip if mflags.empty?
+
+ $mflags = Shellwords.shellwords(mflags)
+ if arg = $mflags.first
+ arg.insert(0, '-') if /\A[^-][^=]*\Z/ =~ arg
+ end
+
+ $make, *rest = Shellwords.shellwords($make)
+ $mflags.unshift(*rest) unless rest.empty?
+
+ def $mflags.set?(flag)
+ grep(/\A-(?!-).*#{'%c' % flag}/i) { return true }
+ false
+ end
+
+ if $mflags.set?(?n)
+ $dryrun = true
else
- destdir ||= arg
- break
+ $mflags << '-n' if $dryrun
end
+
+ $mflags << "DESTDIR=#{$destdir}"
+
+ $continue = $mflags.set?(?k)
end
-destdir ||= ''
-$:.unshift File.join(CONFIG["srcdir"], "lib")
-require 'ftools'
-require 'shellwords'
+parse_args()
-class Installer < File; end
-class << Installer
- if $dryrun
- def makedirs(*dirs)
- String === dirs.last or dirs.pop
- for dir in dirs
- File.directory?(dir) or print "mkdir -p #{dir}\n"
- end
- end
- def install(file, dir, mode = nil, verbose = false)
- to = catname(file, dir)
- unless FileTest.exist? to and cmp file, to
- print "install#{' -m %#o'%mode if mode} #{file} #{dir}\n"
- end
- end
- def makelink(orig, link, verbose = false)
- unless File.symlink?(link) and File.readlink(link) == orig
- print "ln -sf #{orig} #{link}\n"
- end
- end
- else
- require "ftools"
- def makelink(orig, link, verbose = false)
- if exist? link
- delete link
- end
- symlink orig, link
- print "link #{orig} -> #{link}\n"
+include FileUtils::Verbose
+include FileUtils::NoWrite if $dryrun
+@fileutils_output = STDOUT
+@fileutils_label = ''
+
+def install(src, dest, options = {})
+ options[:preserve] = true
+ super
+end
+
+$made_dirs = {}
+def makedirs(dirs)
+ dirs = fu_list(dirs)
+ dirs.reject! do |dir|
+ $made_dirs.fetch(dir) do
+ $made_dirs[dir] = true
+ File.directory?(dir)
end
end
+ super(dirs, :mode => 0755, :verbose => true) unless dirs.empty?
end
exeext = CONFIG["EXEEXT"]
@@ -60,44 +77,47 @@ ruby_install_name = CONFIG["ruby_install_name"]
rubyw_install_name = CONFIG["rubyw_install_name"]
version = CONFIG["ruby_version"]
-bindir = destdir+CONFIG["bindir"]
-libdir = destdir+CONFIG["libdir"]
-rubylibdir = destdir+CONFIG["rubylibdir"]
-archlibdir = destdir+CONFIG["archdir"]
-sitelibdir = destdir+CONFIG["sitelibdir"]
-sitearchlibdir = destdir+CONFIG["sitearchdir"]
-mandir = File.join(destdir+CONFIG["mandir"], "man1")
+bindir = $destdir+CONFIG["bindir"]
+libdir = $destdir+CONFIG["libdir"]
+rubylibdir = $destdir+CONFIG["rubylibdir"]
+archlibdir = $destdir+CONFIG["archdir"]
+sitelibdir = $destdir+CONFIG["sitelibdir"]
+sitearchlibdir = $destdir+CONFIG["sitearchdir"]
+mandir = File.join($destdir+CONFIG["mandir"], "man")
configure_args = Shellwords.shellwords(CONFIG["configure_args"])
enable_shared = CONFIG["ENABLE_SHARED"] == 'yes'
dll = CONFIG["LIBRUBY_SO"]
lib = CONFIG["LIBRUBY"]
arc = CONFIG["LIBRUBY_A"]
-Installer.makedirs bindir, libdir, rubylibdir, archlibdir, sitelibdir, sitearchlibdir, mandir, true
+makedirs [bindir, libdir, rubylibdir, archlibdir, sitelibdir, sitearchlibdir]
+
+ruby_bin = File.join(bindir, ruby_install_name)
-Installer.install ruby_install_name+exeext, File.join(bindir, ruby_install_name+exeext), 0755, true
+install ruby_install_name+exeext, ruby_bin+exeext, :mode => 0755
if rubyw_install_name and !rubyw_install_name.empty?
- Installer.install rubyw_install_name+exeext, bindir, 0755, true
+ install rubyw_install_name+exeext, bindir, :mode => 0755
end
-Installer.install dll, bindir, 0755, true if enable_shared and dll != lib
-Installer.install lib, libdir, 0555, true unless lib == arc
-Installer.install arc, libdir, 0644, true
-Installer.install "config.h", archlibdir, 0644, true
-Installer.install "rbconfig.rb", archlibdir, 0644, true
+install dll, bindir, :mode => 0755 if enable_shared and dll != lib
+install lib, libdir, :mode => 0755 unless lib == arc
+install arc, libdir, :mode => 0644
+install "config.h", archlibdir, :mode => 0644
+install "rbconfig.rb", archlibdir, :mode => 0644
if CONFIG["ARCHFILE"]
for file in CONFIG["ARCHFILE"].split
- Installer.install file, archlibdir, 0644, true
+ install file, archlibdir, :mode => 0644
end
end
if dll == lib and dll != arc
for link in CONFIG["LIBRUBY_ALIASES"].split
- Installer.makelink(dll, File.join(libdir, link), true)
+ ln_sf(dll, File.join(libdir, link))
end
end
Dir.chdir CONFIG["srcdir"]
+ruby_shebang = File.join(CONFIG["bindir"], ruby_install_name)
for src in Dir["bin/*"]
next unless File.file?(src)
next if /\/[.#]|(\.(old|bak|orig|rej|diff|patch|core)|~|\/core)$/i =~ src
@@ -105,51 +125,83 @@ for src in Dir["bin/*"]
name = ruby_install_name.sub(/ruby/, File.basename(src))
dest = File.join(bindir, name)
- Installer.install src, dest, 0755, true
+ install src, dest, :mode => 0755
+
+ next if $dryrun
+ shebang = ''
+ body = ''
open(dest, "r+") { |f|
- shebang = f.gets.sub(/ruby/, ruby_install_name)
+ shebang = f.gets
body = f.read
- f.rewind
- f.print shebang, body
- f.truncate(f.pos)
- f.close
+ if shebang.sub!(/^\#!.*?ruby\b/) {"#!" + ruby_shebang}
+ f.rewind
+ f.print shebang, body
+ f.truncate(f.pos)
+ end
+ }
- if RUBY_PLATFORM =~ /mswin32|mingw|bccwin32/
- open(dest + ".bat", "w") { |b|
- b.print <<EOH, shebang, body, <<EOF
+ if RUBY_PLATFORM =~ /mswin32|mingw|bccwin32/
+ ruby_bin_dosish = ruby_bin.gsub(Regexp.compile(File::SEPARATOR), File::ALT_SEPARATOR)
+ batfile = dest + ".bat"
+ open(batfile, "w") { |b|
+ b.print <<EOH, shebang, body, <<EOF
@echo off
if "%OS%" == "Windows_NT" goto WinNT
-ruby -Sx "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9
+#{ruby_bin_dosish} -x "#{batfile}" %1 %2 %3 %4 %5 %6 %7 %8 %9
goto endofruby
:WinNT
-ruby -Sx "%~nx0" %*
+#{ruby_bin_dosish} -x "#{batfile}" %*
goto endofruby
EOH
__END__
:endofruby
EOF
- }
- end
- } unless $dryrun
+ }
+ end
end
Dir.glob("lib/**/*{.rb,help-message}") do |f|
dir = File.dirname(f).sub!(/\Alib/, rubylibdir) || rubylibdir
- Installer.makedirs dir, true unless File.directory? dir
- Installer.install f, dir, 0644, true
+ makedirs dir
+ install f, dir, :mode => 0644
end
-for f in Dir["*.h"]
- Installer.install f, archlibdir, 0644, true
+Dir.glob("*.h") do |f|
+ install f, archlibdir, :mode => 0644
end
+
if RUBY_PLATFORM =~ /mswin32|mingw|bccwin32/
- Installer.makedirs File.join(archlibdir, "win32"), true
- Installer.install "win32/win32.h", File.join(archlibdir, "win32"), 0644, true
+ makedirs File.join(archlibdir, "win32")
+ install "win32/win32.h", File.join(archlibdir, "win32"), :mode => 0644
end
-Installer.makedirs mandir, true
-Installer.install "ruby.1", File.join(mandir, ruby_install_name+".1"), 0644, true
+Dir.glob("*.[1-9]") do |mdoc|
+ next unless File.file?(mdoc) and open(mdoc){|fh| fh.read(1) == '.'}
+
+ section = mdoc[-1,1]
+
+ destdir = mandir + section
+ destfile = File.join(destdir, mdoc.sub(/ruby/, ruby_install_name))
+
+ makedirs destdir
+
+ if $mantype == "doc"
+ install mdoc, destfile, :mode => 0644
+ else
+ require 'mdoc2man.rb'
+
+ w = Tempfile.open(mdoc)
+
+ open(mdoc) { |r|
+ Mdoc2Man.mdoc2man(r, w)
+ }
+
+ w.close
+
+ install w.path, destfile, :mode => 0644
+ end
+end
# vi:set sw=2:
diff --git a/intern.h b/intern.h
index 07b5b5e2e8..e94077e70a 100644
--- a/intern.h
+++ b/intern.h
@@ -6,7 +6,7 @@
$Date$
created at: Thu Jun 10 14:22:17 JST 1993
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
Copyright (C) 2000 Information-technology Promotion Agency, Japan
@@ -17,9 +17,12 @@
* the kernel.
*/
+#define ID_ALLOCATOR 1
+
/* array.c */
void rb_mem_clear _((register VALUE*, register long));
VALUE rb_assoc_new _((VALUE, VALUE));
+VALUE rb_check_array_type _((VALUE));
VALUE rb_ary_new _((void));
VALUE rb_ary_new2 _((long));
VALUE rb_ary_new3 __((long,...));
@@ -52,6 +55,8 @@ VALUE rb_ary_includes _((VALUE, VALUE));
VALUE rb_ary_cmp _((VALUE, VALUE));
VALUE rb_protect_inspect _((VALUE(*)(ANYARGS),VALUE,VALUE));
VALUE rb_inspecting_p _((VALUE));
+VALUE rb_check_array_value _((VALUE));
+VALUE rb_values_at _((VALUE, long, int, VALUE*, VALUE(*) _((VALUE,long))));
/* bignum.c */
VALUE rb_big_clone _((VALUE));
void rb_big_2comp _((VALUE));
@@ -115,13 +120,13 @@ void rb_undef _((VALUE, ID));
void rb_define_protected_method _((VALUE, const char*, VALUE (*)(ANYARGS), int));
void rb_define_private_method _((VALUE, const char*, VALUE (*)(ANYARGS), int));
void rb_define_singleton_method _((VALUE, const char*, VALUE(*)(ANYARGS), int));
-void rb_define_private_method _((VALUE, const char*, VALUE(*)(ANYARGS), int));
VALUE rb_singleton_class _((VALUE));
/* compar.c */
-int rb_cmpint _((VALUE));
+int rb_cmpint _((VALUE, VALUE, VALUE));
+NORETURN(void rb_cmperr _((VALUE, VALUE)));
/* enum.c */
/* error.c */
-EXTERN int ruby_nerrs;
+RUBY_EXTERN int ruby_nerrs;
VALUE rb_exc_new _((VALUE, const char*, long));
VALUE rb_exc_new2 _((VALUE, const char*));
VALUE rb_exc_new3 _((VALUE, VALUE));
@@ -134,7 +139,7 @@ NORETURN(void rb_load_fail _((char*)));
NORETURN(void rb_error_frozen _((char*)));
void rb_check_frozen _((VALUE));
/* eval.c */
-EXTERN struct RNode *ruby_current_node;
+RUBY_EXTERN struct RNode *ruby_current_node;
void ruby_set_current_source _((void));
NORETURN(void rb_exc_raise _((VALUE)));
NORETURN(void rb_exc_fatal _((VALUE)));
@@ -143,7 +148,11 @@ VALUE rb_f_abort _((int,VALUE*));
void rb_remove_method _((VALUE, const char*));
void rb_disable_super _((VALUE, const char*));
void rb_enable_super _((VALUE, const char*));
+#define HAVE_RB_DEFINE_ALLOC_FUNC 1
+void rb_define_alloc_func _((VALUE, VALUE (*)(VALUE)));
+void rb_undef_alloc_func _((VALUE));
void rb_clear_cache _((void));
+void rb_clear_cache_by_class _((VALUE));
void rb_alias _((VALUE, ID, ID));
void rb_attr _((VALUE,ID,int,int,int));
int rb_method_boundp _((VALUE, ID, int));
@@ -169,6 +178,7 @@ void rb_provide _((const char*));
VALUE rb_f_require _((VALUE, VALUE));
void rb_obj_call_init _((VALUE, int, VALUE*));
VALUE rb_class_new_instance _((int, VALUE*, VALUE));
+VALUE rb_block_proc _((void));
VALUE rb_f_lambda _((void));
VALUE rb_proc_new _((VALUE (*)(ANYARGS/* VALUE yieldarg[, VALUE procarg] */), VALUE));
VALUE rb_protect _((VALUE (*)(VALUE), VALUE, int*));
@@ -193,6 +203,7 @@ void rb_thread_sleep_forever _((void));
VALUE rb_thread_stop _((void));
VALUE rb_thread_wakeup _((VALUE));
VALUE rb_thread_run _((VALUE));
+VALUE rb_thread_kill _((VALUE));
VALUE rb_thread_create _((VALUE (*)(ANYARGS), void*));
void rb_thread_interrupt _((void));
void rb_thread_trap_eval _((VALUE, int));
@@ -238,11 +249,12 @@ VALUE rb_hash_delete _((VALUE,VALUE));
int rb_path_check _((char*));
int rb_env_path_tainted _((void));
/* io.c */
-EXTERN VALUE rb_fs;
-EXTERN VALUE rb_output_fs;
-EXTERN VALUE rb_rs;
-EXTERN VALUE rb_default_rs;
-EXTERN VALUE rb_output_rs;
+#define rb_defout rb_stdout
+RUBY_EXTERN VALUE rb_fs;
+RUBY_EXTERN VALUE rb_output_fs;
+RUBY_EXTERN VALUE rb_rs;
+RUBY_EXTERN VALUE rb_default_rs;
+RUBY_EXTERN VALUE rb_output_rs;
VALUE rb_io_write _((VALUE, VALUE));
VALUE rb_io_gets _((VALUE));
VALUE rb_io_getc _((VALUE));
@@ -256,12 +268,16 @@ VALUE rb_io_print _((int, VALUE*, VALUE));
VALUE rb_io_puts _((int, VALUE*, VALUE));
VALUE rb_file_open _((const char*, const char*));
VALUE rb_gets _((void));
+void rb_write_error _((const char*));
+void rb_write_error2 _((const char*, long));
/* marshal.c */
VALUE rb_marshal_dump _((VALUE, VALUE));
VALUE rb_marshal_load _((VALUE));
/* numeric.c */
void rb_num_zerodiv _((void));
VALUE rb_num_coerce_bin _((VALUE, VALUE));
+VALUE rb_num_coerce_cmp _((VALUE, VALUE));
+VALUE rb_num_coerce_relop _((VALUE, VALUE));
VALUE rb_float_new _((double));
VALUE rb_num2fix _((VALUE));
VALUE rb_fix2str _((VALUE, int));
@@ -275,6 +291,7 @@ VALUE rb_obj_is_kind_of _((VALUE, VALUE));
VALUE rb_obj_alloc _((VALUE));
VALUE rb_obj_clone _((VALUE));
VALUE rb_obj_dup _((VALUE));
+VALUE rb_obj_init_copy _((VALUE,VALUE));
VALUE rb_obj_taint _((VALUE));
VALUE rb_obj_tainted _((VALUE));
VALUE rb_obj_untaint _((VALUE));
@@ -292,8 +309,8 @@ VALUE rb_Array _((VALUE));
double rb_cstr_to_dbl _((const char*, int));
double rb_str_to_dbl _((VALUE, int));
/* parse.y */
-EXTERN int ruby_sourceline;
-EXTERN char *ruby_sourcefile;
+RUBY_EXTERN int ruby_sourceline;
+RUBY_EXTERN char *ruby_sourcefile;
int ruby_yyparse _((void));
ID rb_id_attrset _((ID));
void rb_parser_append_print _((void));
@@ -316,6 +333,7 @@ VALUE rb_f_exec _((int,VALUE*));
int rb_waitpid _((int,int*,int));
void rb_syswait _((int));
VALUE rb_proc_times _((VALUE));
+VALUE rb_detach_process _((int));
/* range.c */
VALUE rb_range_new _((VALUE, VALUE, int));
VALUE rb_range_beg_len _((VALUE, long*, long*, long, int));
@@ -323,6 +341,7 @@ VALUE rb_length_by_each _((VALUE));
/* re.c */
int rb_memcmp _((char*,char*,long));
int rb_memcicmp _((char*,char*,long));
+long rb_memsearch _((char*,long,char*,long));
VALUE rb_reg_nth_defined _((int, VALUE));
VALUE rb_reg_nth_match _((int, VALUE));
VALUE rb_reg_last_match _((VALUE));
@@ -336,8 +355,8 @@ int rb_reg_options _((VALUE));
void rb_set_kcode _((const char*));
const char* rb_get_kcode _((void));
/* ruby.c */
-EXTERN VALUE rb_argv;
-EXTERN VALUE rb_argv0;
+RUBY_EXTERN VALUE rb_argv;
+RUBY_EXTERN VALUE rb_argv0;
void rb_load_file _((char*));
void ruby_script _((char*));
void ruby_prog_init _((void));
@@ -371,6 +390,7 @@ VALUE rb_str_buf_append _((VALUE, VALUE));
VALUE rb_str_buf_cat _((VALUE, const char*, long));
VALUE rb_str_buf_cat2 _((VALUE, const char*));
VALUE rb_obj_as_string _((VALUE));
+VALUE rb_check_string_type _((VALUE));
VALUE rb_str_dup _((VALUE));
VALUE rb_str_dup_frozen _((VALUE));
VALUE rb_str_plus _((VALUE, VALUE));
@@ -410,8 +430,9 @@ VALUE rb_class_path _((VALUE));
void rb_set_class_path _((VALUE, VALUE, const char*));
VALUE rb_path2class _((const char*));
void rb_name_class _((VALUE, ID));
-void rb_autoload _((const char*, const char*));
-VALUE rb_f_autoload _((VALUE, VALUE, VALUE));
+void rb_autoload _((VALUE, ID, const char*));
+void rb_autoload_load _((VALUE, ID));
+VALUE rb_autoload_p _((VALUE, ID));
void rb_gc_mark_global_tbl _((void));
VALUE rb_f_trace_var _((int, VALUE*));
VALUE rb_f_untrace_var _((int, VALUE*));
@@ -427,6 +448,7 @@ VALUE rb_ivar_set _((VALUE, ID, VALUE));
VALUE rb_ivar_defined _((VALUE, ID));
VALUE rb_iv_set _((VALUE, const char*, VALUE));
VALUE rb_iv_get _((VALUE, const char*));
+VALUE rb_attr_get _((VALUE, ID));
VALUE rb_obj_instance_variables _((VALUE));
VALUE rb_obj_remove_instance_variable _((VALUE, VALUE));
void *rb_mod_const_at _((VALUE, void*));
@@ -434,16 +456,17 @@ void *rb_mod_const_of _((VALUE, void*));
VALUE rb_const_list _((void*));
VALUE rb_mod_constants _((VALUE));
VALUE rb_mod_remove_const _((VALUE, VALUE));
-int rb_const_defined_at _((VALUE, ID));
-int rb_autoload_defined _((ID));
int rb_const_defined _((VALUE, ID));
+int rb_const_defined_at _((VALUE, ID));
+int rb_const_defined_from _((VALUE, ID));
VALUE rb_const_get _((VALUE, ID));
VALUE rb_const_get_at _((VALUE, ID));
+VALUE rb_const_get_from _((VALUE, ID));
void rb_const_set _((VALUE, ID, VALUE));
-void rb_const_assign _((VALUE, ID, VALUE));
VALUE rb_mod_constants _((VALUE));
-void rb_autoload_load _((ID));
+VALUE rb_mod_const_missing _((VALUE,VALUE));
VALUE rb_cvar_defined _((VALUE, ID));
+#define RB_CVAR_SET_4ARGS 1
void rb_cvar_set _((VALUE, ID, VALUE, int));
VALUE rb_cvar_get _((VALUE, ID));
void rb_cv_set _((VALUE, const char*, VALUE));
diff --git a/io.c b/io.c
index b9060773e1..1256663f13 100644
--- a/io.c
+++ b/io.c
@@ -6,7 +6,7 @@
$Date$
created at: Fri Oct 15 18:08:59 JST 1993
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
Copyright (C) 2000 Information-technology Promotion Agency, Japan
@@ -94,9 +94,9 @@ VALUE rb_cIO;
VALUE rb_eEOFError;
VALUE rb_eIOError;
-VALUE rb_stdin, rb_stdout, rb_stderr, rb_defout;
-static VALUE orig_stdin, orig_stdout, orig_stderr;
-static int saved_fd[3] = {0, 1, 2};
+VALUE rb_stdin, rb_stdout, rb_stderr;
+VALUE rb_deferr; /* rescue VIM plugin */
+static VALUE orig_stdout, orig_stderr;
VALUE rb_output_fs;
VALUE rb_rs;
@@ -105,7 +105,7 @@ VALUE rb_default_rs;
static VALUE argf;
-static ID id_write;
+static ID id_write, id_read, id_getc;
extern char *ruby_inplace_mode;
@@ -113,7 +113,7 @@ struct timeval rb_time_interval _((VALUE));
static VALUE filename, current_file;
static int gets_lineno;
-static int init_p = 0, next_p = 0, first_p = 1;
+static int init_p = 0, next_p = 0;
static VALUE lineno;
#ifdef _STDIO_USES_IOSTREAM /* GNU libc */
@@ -144,7 +144,7 @@ extern int ReadDataPending();
#ifndef READ_DATA_PENDING_PTR
# ifdef FILE_READPTR
-# define READ_DATA_PENDING_PTR(fp) ((fp)->FILE_READPTR)
+# define READ_DATA_PENDING_PTR(fp) ((char *)(fp)->FILE_READPTR)
# endif
#endif
@@ -195,12 +195,8 @@ static OpenFile *
flush_before_seek(fptr)
OpenFile *fptr;
{
- int mode = fptr->mode;
- if (mode & FMODE_RBUF) {
- if (!fptr->f2) io_fflush(fptr->f, fptr);
- }
- if (mode & FMODE_WBUF) {
- io_fflush((fptr->f2 ? fptr->f2 : fptr->f), fptr);
+ if (fptr->mode & FMODE_WBUF) {
+ io_fflush(GetWriteFile(fptr), fptr);
}
return fptr;
}
@@ -211,16 +207,27 @@ flush_before_seek(fptr)
#define io_seek(fptr, ofs, whence) fseeko(flush_before_seek(fptr)->f, ofs, whence)
#define io_tell(fptr) ftello(flush_before_seek(fptr)->f)
+#ifndef SEEK_CUR
+# define SEEK_SET 0
+# define SEEK_CUR 1
+# define SEEK_END 2
+#endif
+
+#define FMODE_SYNCWRITE (FMODE_SYNC|FMODE_WRITABLE)
+
void
rb_io_check_readable(fptr)
OpenFile *fptr;
{
+ rb_io_check_closed(fptr);
if (!(fptr->mode & FMODE_READABLE)) {
rb_raise(rb_eIOError, "not opened for reading");
}
-#if NEED_IO_FLUSH_BETWEEN_RW
- if ((fptr->mode & FMODE_WBUF) && !fptr->f2) {
- io_fflush(fptr->f, fptr);
+#if NEED_IO_SEEK_BETWEEN_RW
+ if (((fptr->mode & FMODE_WBUF) ||
+ (fptr->mode & (FMODE_SYNCWRITE|FMODE_RBUF)) == FMODE_SYNCWRITE) &&
+ !fptr->f2) {
+ io_seek(fptr, 0, SEEK_CUR);
}
fptr->mode |= FMODE_RBUF;
#endif
@@ -230,12 +237,13 @@ void
rb_io_check_writable(fptr)
OpenFile *fptr;
{
+ rb_io_check_closed(fptr);
if (!(fptr->mode & FMODE_WRITABLE)) {
rb_raise(rb_eIOError, "not opened for writing");
}
-#if NEED_IO_FLUSH_BETWEEN_RW
+#if NEED_IO_SEEK_BETWEEN_RW
if ((fptr->mode & FMODE_RBUF) && !fptr->f2) {
- io_fflush(fptr->f, fptr);
+ io_seek(fptr, 0, SEEK_CUR);
}
#endif
}
@@ -275,6 +283,19 @@ ruby_dup(orig)
return fd;
}
+static VALUE io_alloc _((VALUE));
+static VALUE
+io_alloc(klass)
+ VALUE klass;
+{
+ NEWOBJ(io, struct RFile);
+ OBJSETUP(io, klass, T_FILE);
+
+ io->fptr = 0;
+
+ return (VALUE)io;
+}
+
static void
io_fflush(f, fptr)
FILE *f;
@@ -282,7 +303,9 @@ io_fflush(f, fptr)
{
int n;
- rb_thread_fd_writable(fileno(f));
+ if (!rb_thread_fd_writable(fileno(f))) {
+ rb_io_check_closed(fptr);
+ }
for (;;) {
TRAP_BEG;
n = fflush(f);
@@ -351,50 +374,60 @@ rb_io_wait_writable(f)
}
/* writing functions */
+long
+rb_io_fwrite(ptr, len, f)
+ const char *ptr;
+ long len;
+ FILE *f;
+{
+ long n, r;
+
+ if ((n = len) <= 0) return n;
+#ifdef __human68k__
+ do {
+ if (fputc(*ptr++, f) == EOF) {
+ if (ferror(f)) return -1L;
+ break;
+ }
+ } while (--n > 0);
+#else
+ while (ptr += (r = fwrite(ptr, 1, n, f)), (n -= r) > 0) {
+ if (ferror(f)) {
+ if (rb_io_wait_writable(fileno(f))) {
+ clearerr(f);
+ continue;
+ }
+ return -1L;
+ }
+ }
+#endif
+ return len - n;
+}
+
static VALUE
io_write(io, str)
VALUE io, str;
{
OpenFile *fptr;
FILE *f;
- long n, r;
- register char *ptr;
+ long n;
rb_secure(4);
if (TYPE(str) != T_STRING)
str = rb_obj_as_string(str);
- if (RSTRING(str)->len == 0) return INT2FIX(0);
if (TYPE(io) != T_FILE) {
/* port is not IO, call write method for it. */
return rb_funcall(io, id_write, 1, str);
}
+ if (RSTRING(str)->len == 0) return INT2FIX(0);
GetOpenFile(io, fptr);
rb_io_check_writable(fptr);
f = GetWriteFile(fptr);
- ptr = RSTRING(str)->ptr;
- n = RSTRING(str)->len;
-#ifdef __human68k__
- do {
- if (fputc(*ptr++, f) == EOF) {
- if (ferror(f)) rb_sys_fail(fptr->path);
- break;
- }
- } while (--n > 0);
-#else
- while (ptr += (r = fwrite(ptr, 1, n, f)), (n -= r) > 0) {
- if (ferror(f)) {
- if (rb_io_wait_writable(fileno(f))) {
- clearerr(f);
- continue;
- }
- rb_sys_fail(fptr->path);
- }
- }
-#endif
- n = ptr - RSTRING(str)->ptr;
+ n = rb_io_fwrite(RSTRING(str)->ptr, RSTRING(str)->len, f);
+ if (n == -1L) rb_sys_fail(fptr->path);
if (fptr->mode & FMODE_SYNC) {
io_fflush(f, fptr);
}
@@ -449,12 +482,6 @@ rb_io_tell(io)
return OFFT2NUM(pos);
}
-#ifndef SEEK_CUR
-# define SEEK_SET 0
-# define SEEK_CUR 1
-# define SEEK_END 2
-#endif
-
static VALUE
rb_io_seek(io, offset, whence)
VALUE io, offset;
@@ -624,7 +651,7 @@ rb_io_inspect(obj)
fptr = RFILE(rb_io_taint_check(obj))->fptr;
if (!fptr || !(fptr->f || fptr->f2) || !fptr->path) return rb_any_to_s(obj);
- cname = rb_class2name(CLASS_OF(obj));
+ cname = rb_obj_classname(obj);
buf = ALLOCA_N(char, strlen(cname) + strlen(fptr->path) + 5);
sprintf(buf, "#<%s:%s>", cname, fptr->path);
return rb_str_new2(buf);
@@ -687,8 +714,10 @@ rb_io_fread(ptr, len, f)
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
case EWOULDBLOCK:
#endif
- clearerr(f);
- return len - n;
+ if (len - n >= 0) {
+ clearerr(f);
+ return len - n;
+ }
}
return 0;
}
@@ -756,8 +785,9 @@ read_all(fptr, siz, str)
for (;;) {
n = rb_io_fread(RSTRING(str)->ptr+bytes, siz-bytes, fptr->f);
if (pos > 0 && n == 0 && bytes == 0) {
+ rb_str_resize(str,0);
if (feof(fptr->f)) return Qnil;
- if (!ferror(fptr->f)) return rb_str_new(0, 0);
+ if (!ferror(fptr->f)) return str;
rb_sys_fail(fptr->path);
}
bytes += n;
@@ -797,22 +827,20 @@ io_read(argc, argv, io)
if (feof(fptr->f)) return Qnil;
if (NIL_P(str)) {
str = rb_str_new(0, len);
- if (len == 0) return str;
}
else {
StringValue(str);
rb_str_modify(str);
- if (len == 0) {
- rb_str_resize(str, 0);
- return str;
- }
+ rb_str_resize(str,len);
}
+ if (len == 0) return str;
READ_CHECK(fptr->f);
n = rb_io_fread(RSTRING(str)->ptr, len, fptr->f);
if (n == 0) {
+ rb_str_resize(str,0);
if (feof(fptr->f)) return Qnil;
- rb_sys_fail(fptr->path);
+ if (len > 0) rb_sys_fail(fptr->path);
}
RSTRING(str)->len = n;
RSTRING(str)->ptr[n] = '\0';
@@ -981,6 +1009,7 @@ rb_io_getline(rs, fptr)
{
VALUE str = Qnil;
+ rb_io_check_readable(fptr);
if (NIL_P(rs)) {
str = read_all(fptr, 0, Qnil);
}
@@ -1056,7 +1085,6 @@ rb_io_gets_m(argc, argv, io)
rb_scan_args(argc, argv, "1", &rs);
}
GetOpenFile(io, fptr);
- rb_io_check_readable(fptr);
str = rb_io_getline(rs, fptr);
if (!NIL_P(str)) {
@@ -1144,7 +1172,6 @@ rb_io_readlines(argc, argv, io)
rb_scan_args(argc, argv, "1", &rs);
}
GetOpenFile(io, fptr);
- rb_io_check_readable(fptr);
ary = rb_ary_new();
while (!NIL_P(line = rb_io_getline(rs, fptr))) {
rb_ary_push(ary, line);
@@ -1169,7 +1196,6 @@ rb_io_each_line(argc, argv, io)
rb_scan_args(argc, argv, "1", &rs);
}
GetOpenFile(io, fptr);
- rb_io_check_readable(fptr);
while (!NIL_P(str = rb_io_getline(rs, fptr))) {
rb_yield(str);
}
@@ -1185,10 +1211,10 @@ rb_io_each_byte(io)
int c;
GetOpenFile(io, fptr);
- rb_io_check_readable(fptr);
- f = fptr->f;
for (;;) {
+ rb_io_check_readable(fptr);
+ f = fptr->f;
READ_CHECK(f);
TRAP_BEG;
c = getc(f);
@@ -1294,9 +1320,9 @@ rb_io_isatty(io)
}
static void
-fptr_finalize(fptr, fin)
+fptr_finalize(fptr, noraise)
OpenFile *fptr;
- int fin;
+ int noraise;
{
int n1 = 0, n2 = 0, e = 0, f1, f2 = -1;
@@ -1307,6 +1333,7 @@ fptr_finalize(fptr, fin)
e = errno;
break;
}
+ if (!fptr->f2) break;
}
fptr->f2 = 0;
}
@@ -1315,30 +1342,29 @@ fptr_finalize(fptr, fin)
while ((n1 = fclose(fptr->f)) < 0) {
if (f2 != -1 || !(fptr->mode & FMODE_WBUF)) break;
if (!rb_io_wait_writable(f1)) break;
+ if (!fptr->f) break;
}
fptr->f = 0;
- if (n1 < 0 && errno == EBADF) {
- if (f1 == f2 || !(fptr->mode & FMODE_WBUF)) {
- n1 = 0;
- }
+ if (n1 < 0 && errno == EBADF && f1 == f2) {
+ n1 = 0;
}
}
- if (!fin && (n1 < 0 || n2 < 0)) {
+ if (!noraise && (n1 < 0 || n2 < 0)) {
if (n1 == 0) errno = e;
rb_sys_fail(fptr->path);
}
}
static void
-rb_io_fptr_cleanup(fptr, fin)
+rb_io_fptr_cleanup(fptr, noraise)
OpenFile *fptr;
- int fin;
+ int noraise;
{
if (fptr->finalize) {
- (*fptr->finalize)(fptr);
+ (*fptr->finalize)(fptr, noraise);
}
else {
- fptr_finalize(fptr, fin);
+ fptr_finalize(fptr, noraise);
}
if (fptr->path) {
@@ -1363,15 +1389,22 @@ rb_io_close(io)
VALUE io;
{
OpenFile *fptr;
- int fd;
+ int fd, fd2;
fptr = RFILE(io)->fptr;
if (!fptr) return Qnil;
- if (!fptr->f && !fptr->f2) return Qnil;
+ if (fptr->f2) {
+ fd2 = fileno(fptr->f2);
+ }
+ else {
+ if (!fptr->f) return Qnil;
+ fd2 = -1;
+ }
fd = fileno(fptr->f);
rb_io_fptr_cleanup(fptr, Qfalse);
rb_thread_fd_close(fd);
+ if (fd2 >= 0) rb_thread_fd_close(fd2);
if (fptr->pid) {
rb_syswait(fptr->pid);
@@ -1394,6 +1427,13 @@ rb_io_close_m(io)
}
static VALUE
+io_close(io)
+ VALUE io;
+{
+ return rb_funcall(io, rb_intern("close"), 0, 0);
+}
+
+static VALUE
rb_io_closed(io)
VALUE io;
{
@@ -1538,6 +1578,7 @@ rb_io_sysread(argc, argv, io)
rb_str_modify(str);
rb_str_resize(str, ilen);
}
+ if (ilen == 0) return str;
n = fileno(fptr->f);
rb_thread_wait_fd(fileno(fptr->f));
@@ -1545,8 +1586,12 @@ rb_io_sysread(argc, argv, io)
n = read(fileno(fptr->f), RSTRING(str)->ptr, RSTRING(str)->len);
TRAP_END;
- if (n == -1) rb_sys_fail(fptr->path);
+ if (n == -1) {
+ rb_str_resize(str, 0);
+ rb_sys_fail(fptr->path);
+ }
if (n == 0 && ilen > 0) {
+ rb_str_resize(str, 0);
rb_eof_error();
}
@@ -1582,6 +1627,39 @@ rb_io_binmode(io)
return io;
}
+char*
+rb_io_flags_mode(flags, mode)
+ int flags;
+ char *mode;
+{
+ char *p = mode;
+
+ switch (flags & FMODE_READWRITE) {
+ case FMODE_READABLE:
+ *p++ = 'r';
+ break;
+ case FMODE_WRITABLE:
+ *p++ = 'w';
+ break;
+ case FMODE_READWRITE:
+ *p++ = 'r';
+ *p++ = '+';
+ break;
+ }
+ *p++ = '\0';
+#ifdef O_BINARY
+ if (flags & FMODE_BINMODE) {
+ if (mode[1] == '+') {
+ mode[1] = 'b'; mode[2] = '+'; mode[3] = '\0';
+ }
+ else {
+ mode[1] = 'b'; mode[2] = '\0';
+ }
+ }
+#endif
+ return mode;
+}
+
int
rb_io_mode_flags(mode)
const char *mode;
@@ -1782,6 +1860,9 @@ rb_fdopen(fd, mode)
file = fdopen(fd, mode);
}
if (!file) {
+#ifdef _WIN32
+ if (errno == 0) errno = EINVAL;
+#endif
rb_sys_fail(0);
}
}
@@ -1813,9 +1894,7 @@ VALUE
rb_file_open(fname, mode)
const char *fname, *mode;
{
- VALUE io = rb_obj_alloc(rb_cFile);
-
- return rb_file_open_internal(io, fname, mode);
+ return rb_file_open_internal(io_alloc(rb_cFile), fname, mode);
}
static VALUE
@@ -1845,9 +1924,7 @@ rb_file_sysopen(fname, flags, mode)
const char *fname;
int flags, mode;
{
- VALUE io = rb_obj_alloc(rb_cFile);
-
- return rb_file_sysopen_internal(io, fname, flags, mode);
+ return rb_file_sysopen_internal(io_alloc(rb_cFile), fname, flags, mode);
}
#if defined (_WIN32) || defined(DJGPP) || defined(__CYGWIN__) || defined(__human68k__) || defined(__VMS)
@@ -1892,7 +1969,6 @@ pipe_del_fptr(fptr)
}
}
-#if defined (_WIN32) || defined(DJGPP) || defined(__CYGWIN__) || defined(__human68k__) || defined(__VMS)
static void
pipe_atexit _((void))
{
@@ -1905,13 +1981,13 @@ pipe_atexit _((void))
list = tmp;
}
}
-#endif
-static void pipe_finalize _((OpenFile *fptr));
+static void pipe_finalize _((OpenFile *fptr,int));
static void
-pipe_finalize(fptr)
+pipe_finalize(fptr, noraise)
OpenFile *fptr;
+ int noraise;
{
#if !defined (__CYGWIN__) && !defined(_WIN32)
extern VALUE rb_last_status;
@@ -1928,7 +2004,7 @@ pipe_finalize(fptr)
#endif
rb_last_status = INT2FIX(status);
#else
- fptr_finalize(fptr, Qtrue);
+ fptr_finalize(fptr, noraise);
#endif
pipe_del_fptr(fptr);
}
@@ -1960,7 +2036,7 @@ pipe_open(pname, mode)
if (!f) rb_sys_fail(pname);
else {
- VALUE port = rb_obj_alloc(rb_cIO);
+ VALUE port = io_alloc(rb_cIO);
MakeOpenFile(port, fptr);
fptr->finalize = pipe_finalize;
@@ -1990,7 +2066,7 @@ retry:
rb_sys_fail(pname);
}
else {
- VALUE port = rb_obj_alloc(rb_cIO);
+ VALUE port = io_alloc(rb_cIO);
MakeOpenFile(port, fptr);
fptr->mode = modef;
@@ -2067,7 +2143,7 @@ retry:
default: /* parent */
if (pid < 0) rb_sys_fail(pname);
else {
- VALUE port = rb_obj_alloc(rb_cIO);
+ VALUE port = io_alloc(rb_cIO);
MakeOpenFile(port, fptr);
fptr->mode = modef;
@@ -2130,7 +2206,7 @@ rb_io_popen(str, argc, argv, klass)
}
RBASIC(port)->klass = klass;
if (rb_block_given_p()) {
- return rb_ensure(rb_yield, port, rb_io_close, port);
+ return rb_ensure(rb_yield, port, io_close, port);
}
return port;
}
@@ -2164,7 +2240,13 @@ rb_open_file(argc, argv, io)
path = RSTRING(fname)->ptr;
if (FIXNUM_P(vmode) || !NIL_P(perm)) {
- flags = FIXNUM_P(vmode) ? NUM2INT(vmode) : rb_io_mode_modenum(StringValuePtr(vmode));
+ if (FIXNUM_P(vmode)) {
+ flags = NUM2INT(vmode);
+ }
+ else {
+ SafeStringValue(vmode);
+ flags = rb_io_mode_modenum(RSTRING(vmode)->ptr);
+ }
fmode = NIL_P(perm) ? 0666 : NUM2INT(perm);
rb_file_sysopen_internal(io, path, flags, fmode);
@@ -2185,7 +2267,7 @@ rb_io_s_open(argc, argv, klass)
VALUE io = rb_class_new_instance(argc, argv, klass);
if (rb_block_given_p()) {
- return rb_ensure(rb_yield, io, rb_io_close, io);
+ return rb_ensure(rb_yield, io, io_close, io);
}
return io;
@@ -2205,7 +2287,8 @@ rb_io_s_sysopen(argc, argv)
if (NIL_P(vmode)) flags = O_RDONLY;
else if (FIXNUM_P(vmode)) flags = NUM2INT(vmode);
else {
- flags = rb_io_mode_modenum(StringValuePtr(vmode));
+ SafeStringValue(vmode);
+ flags = rb_io_mode_modenum(RSTRING(vmode)->ptr);
}
if (NIL_P(perm)) fmode = 0666;
else fmode = NUM2INT(perm);
@@ -2269,7 +2352,7 @@ io_reopen(io, nfile)
{
OpenFile *fptr, *orig;
char *mode;
- int fd;
+ int fd, fd2;
off_t pos = 0;
nfile = rb_io_get_io(nfile);
@@ -2289,7 +2372,9 @@ io_reopen(io, nfile)
else if (orig->mode & FMODE_WRITABLE) {
io_fflush(orig->f, orig);
}
- rb_thread_fd_close(fileno(fptr->f));
+ if (fptr->mode & FMODE_WRITABLE) {
+ io_fflush(GetWriteFile(fptr), fptr);
+ }
/* copy OpenFile structure */
fptr->mode = orig->mode;
@@ -2302,34 +2387,41 @@ io_reopen(io, nfile)
mode = rb_io_mode_string(fptr);
fd = fileno(fptr->f);
- if (fd < 3) {
- clearerr(fptr->f);
- /* need to keep stdio objects */
- if (dup2(fileno(orig->f), fd) < 0)
- rb_sys_fail(orig->path);
- }
- else {
- fclose(fptr->f);
- if (dup2(fileno(orig->f), fd) < 0)
- rb_sys_fail(orig->path);
- fptr->f = rb_fdopen(fd, mode);
- }
- if ((orig->mode & FMODE_READABLE) && pos >= 0) {
- io_seek(fptr, pos, SEEK_SET);
- io_seek(orig, pos, SEEK_SET);
+ fd2 = fileno(orig->f);
+ if (fd != fd2) {
+ if (fptr->f == stdin || fptr->f == stdout || fptr->f == stderr) {
+ clearerr(fptr->f);
+ /* need to keep stdio objects */
+ if (dup2(fd2, fd) < 0)
+ rb_sys_fail(orig->path);
+ }
+ else {
+ fclose(fptr->f);
+ if (dup2(fd2, fd) < 0)
+ rb_sys_fail(orig->path);
+ fptr->f = rb_fdopen(fd, mode);
+ }
+ rb_thread_fd_close(fd);
+ if ((orig->mode & FMODE_READABLE) && pos >= 0) {
+ io_seek(fptr, pos, SEEK_SET);
+ io_seek(orig, pos, SEEK_SET);
+ }
}
- if (fptr->f2) {
+ if (fptr->f2 && fd != fileno(fptr->f2)) {
fd = fileno(fptr->f2);
- fclose(fptr->f2);
- if (orig->f2) {
- if (dup2(fileno(orig->f2), fd) < 0)
+ if (!orig->f2) {
+ fclose(fptr->f2);
+ rb_thread_fd_close(fd);
+ fptr->f2 = 0;
+ }
+ else if (fd != (fd2 = fileno(orig->f2))) {
+ fclose(fptr->f2);
+ rb_thread_fd_close(fd);
+ if (dup2(fd2, fd) < 0)
rb_sys_fail(orig->path);
fptr->f2 = rb_fdopen(fd, "w");
}
- else {
- fptr->f2 = 0;
- }
}
if (fptr->mode & FMODE_BINMODE) {
@@ -2358,14 +2450,21 @@ rb_io_reopen(argc, argv, file)
}
SafeStringValue(fname);
+
+ rb_io_taint_check(file);
+ fptr = RFILE(file)->fptr;
+ if (!fptr) {
+ fptr = RFILE(file)->fptr = ALLOC(OpenFile);
+ }
+
if (!NIL_P(nmode)) {
mode = StringValuePtr(nmode);
}
else {
- mode = "r";
+ mode = ALLOCA_N(char, 4);
+ rb_io_flags_mode(fptr->mode, mode);
}
- GetOpenFile(file, fptr);
if (fptr->path) {
free(fptr->path);
fptr->path = 0;
@@ -2401,7 +2500,7 @@ rb_io_reopen(argc, argv, file)
}
static VALUE
-rb_io_copy_object(dest, io)
+rb_io_init_copy(dest, io)
VALUE dest, io;
{
OpenFile *fptr, *orig;
@@ -2415,10 +2514,14 @@ rb_io_copy_object(dest, io)
if (orig->f2) {
io_fflush(orig->f2, orig);
+ fseeko(orig->f, 0L, SEEK_CUR);
}
else if (orig->mode & FMODE_WRITABLE) {
io_fflush(orig->f, orig);
}
+ else {
+ fseeko(orig->f, 0L, SEEK_CUR);
+ }
/* copy OpenFile structure */
fptr->mode = orig->mode;
@@ -2472,7 +2575,7 @@ rb_f_printf(argc, argv)
if (argc == 0) return Qnil;
if (TYPE(argv[0]) == T_STRING) {
- out = rb_defout;
+ out = rb_stdout;
}
else {
out = argv[0];
@@ -2524,7 +2627,7 @@ rb_f_print(argc, argv)
int argc;
VALUE *argv;
{
- rb_io_print(argc, argv, rb_defout);
+ rb_io_print(argc, argv, rb_stdout);
return Qnil;
}
@@ -2542,7 +2645,7 @@ static VALUE
rb_f_putc(recv, ch)
VALUE recv, ch;
{
- return rb_io_putc(rb_defout, ch);
+ return rb_io_putc(rb_stdout, ch);
}
static VALUE
@@ -2581,7 +2684,7 @@ rb_io_puts(argc, argv, out)
line = rb_str_new2("nil");
}
else {
- line = rb_check_convert_type(argv[i], T_ARRAY, "Array", "to_ary");
+ line = rb_check_array_type(argv[i]);
if (!NIL_P(line)) {
rb_protect_inspect(io_puts_ary, line, out);
continue;
@@ -2603,7 +2706,7 @@ rb_f_puts(argc, argv)
int argc;
VALUE *argv;
{
- rb_io_puts(argc, argv, rb_defout);
+ rb_io_puts(argc, argv, rb_stdout);
return Qnil;
}
@@ -2611,8 +2714,8 @@ void
rb_p(obj) /* for debug print within C code */
VALUE obj;
{
- rb_io_write(rb_defout, rb_obj_as_string(rb_inspect(obj)));
- rb_io_write(rb_defout, rb_default_rs);
+ rb_io_write(rb_stdout, rb_obj_as_string(rb_inspect(obj)));
+ rb_io_write(rb_stdout, rb_default_rs);
}
static VALUE
@@ -2625,8 +2728,8 @@ rb_f_p(argc, argv)
for (i=0; i<argc; i++) {
rb_p(argv[i]);
}
- if (TYPE(rb_defout) == T_FILE) {
- rb_io_flush(rb_defout);
+ if (TYPE(rb_stdout) == T_FILE) {
+ rb_io_flush(rb_stdout);
}
return Qnil;
}
@@ -2640,7 +2743,7 @@ rb_obj_display(argc, argv, self)
VALUE out;
if (rb_scan_args(argc, argv, "01", &out) == 0) {
- out = rb_defout;
+ out = rb_stdout;
}
rb_io_write(out, self);
@@ -2648,107 +2751,62 @@ rb_obj_display(argc, argv, self)
return Qnil;
}
-static void
-rb_io_defset(val, id)
- VALUE val;
- ID id;
+void
+rb_write_error2(mesg, len)
+ const char *mesg;
+ long len;
{
- if (!rb_respond_to(val, id_write)) {
- rb_raise(rb_eTypeError, "$> must have write method, %s given",
- rb_class2name(CLASS_OF(val)));
- }
- rb_defout = val;
+ rb_io_write(rb_stderr, rb_str_new(mesg, len));
+}
+
+void
+rb_write_error(mesg)
+ const char *mesg;
+{
+ rb_write_error2(mesg, strlen(mesg));
}
static void
-set_stdin(val, id, var)
+must_respond_to(mid, val, id)
+ ID mid;
VALUE val;
ID id;
- VALUE *var;
{
- OpenFile *fptr;
-
- if (val == *var) return;
- if (TYPE(val) != T_FILE) {
- *var = val;
- return;
- }
- if (TYPE(*var) != T_FILE) {
- *var = orig_stdin;
- }
-
- GetOpenFile(val, fptr);
- rb_io_check_readable(fptr);
- if (fileno(fptr->f) == 0 && saved_fd[0] != 0) {
- dup2(saved_fd[0], 0);
- close(saved_fd[0]);
- saved_fd[0] = 0;
+ if (!rb_respond_to(val, mid)) {
+ rb_raise(rb_eTypeError, "%s must have %s method, %s given",
+ rb_id2name(id), rb_id2name(mid),
+ rb_obj_classname(val));
}
- else {
- saved_fd[0] = dup(0);
- dup2(fileno(fptr->f), 0);
- }
-
- *var = val;
}
static void
-set_outfile(val, var, orig, stdf)
+stdout_setter(val, id, variable)
VALUE val;
- VALUE *var;
- VALUE orig;
- FILE *stdf;
+ ID id;
+ VALUE *variable;
{
- OpenFile *fptr;
- FILE *f;
- int fd;
-
- if (val == *var) return;
-
- if (TYPE(*var) == T_FILE && !rb_io_closed(*var)) {
- rb_io_flush(*var);
- }
- if (TYPE(val) != T_FILE) {
- *var = val;
- return;
- }
- if (TYPE(*var) != T_FILE) {
- *var = orig;
- }
-
- GetOpenFile(val, fptr);
- rb_io_check_writable(fptr);
- f = GetWriteFile(fptr);
- fd = fileno(stdf);
- if (fileno(fptr->f) == fd && saved_fd[fd] != fd) {
- dup2(saved_fd[fd], fd);
- close(saved_fd[fd]);
- saved_fd[fd] = fd;
- }
- else {
- saved_fd[fd] = dup(fd);
- dup2(fileno(fptr->f), fd);
- }
-
- *var = val;
+ must_respond_to(id_write, val, id);
+ *variable = val;
}
static void
-set_stdout(val, id, var)
+defout_setter(val, id, variable)
VALUE val;
ID id;
- VALUE *var;
+ VALUE *variable;
{
- set_outfile(val, var, orig_stdout, stdout);
+ stdout_setter(val, id, variable);
+ rb_warn("$defout is obslete; use $stdout instead");
}
static void
-set_stderr(val, id, var)
+deferr_setter(val, id, variable)
VALUE val;
ID id;
- VALUE *var;
+ VALUE *variable;
{
- set_outfile(val, var, orig_stderr, stderr);
+ stdout_setter(val, id, variable);
+ rb_warn("$deferr is obslete; use $stderr instead");
}
static VALUE
@@ -2758,9 +2816,15 @@ prep_stdio(f, mode, klass)
VALUE klass;
{
OpenFile *fp;
- VALUE io = rb_obj_alloc(klass);
+ VALUE io = io_alloc(klass);
MakeOpenFile(io, fp);
+#ifdef __CYGWIN__
+ if (!isatty(fileno(f))) {
+ mode |= O_BINARY;
+ setmode(fileno(f), O_BINARY);
+ }
+#endif
fp->f = f;
fp->mode = mode;
@@ -2780,18 +2844,6 @@ prep_path(io, path)
}
static VALUE
-rb_io_s_alloc(klass)
- VALUE klass;
-{
- NEWOBJ(io, struct RFile);
- OBJSETUP(io, klass, T_FILE);
-
- io->fptr = 0;
-
- return (VALUE)io;
-}
-
-static VALUE
rb_io_initialize(argc, argv, io)
int argc;
VALUE *argv;
@@ -2802,11 +2854,17 @@ rb_io_initialize(argc, argv, io)
int fd, flags;
char mbuf[4];
+ rb_secure(4);
rb_scan_args(argc, argv, "11", &fnum, &mode);
fd = NUM2INT(fnum);
if (argc == 2) {
- SafeStringValue(mode);
- flags = rb_io_mode_modenum(RSTRING(mode)->ptr);
+ if (FIXNUM_P(mode)) {
+ flags = FIX2LONG(mode);
+ }
+ else {
+ SafeStringValue(mode);
+ flags = rb_io_mode_modenum(RSTRING(mode)->ptr);
+ }
}
else {
#if defined(HAVE_FCNTL) && defined(F_GETFL)
@@ -2861,6 +2919,17 @@ rb_io_s_new(argc, argv, klass)
return rb_class_new_instance(argc, argv, klass);
}
+static VALUE
+rb_io_s_for_fd(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ VALUE io = rb_obj_alloc(klass);
+ rb_io_initialize(argc, argv, io);
+ return io;
+}
+
static int binmode = 0;
static VALUE
@@ -2870,11 +2939,33 @@ argf_forward()
ruby_frame->argc, ruby_frame->argv);
}
+#define ARGF_FORWARD() do { if (TYPE(current_file) != T_FILE) return argf_forward(); } while (0)
+#define NEXT_ARGF_FORWARD() do {\
+ if (!next_argv()) return Qnil;\
+ ARGF_FORWARD();\
+} while (0)
+
+static void
+argf_close(file)
+ VALUE file;
+{
+ if (TYPE(file) == T_FILE)
+ rb_io_close(file);
+ else
+ rb_funcall3(file, rb_intern("close"), 0, 0);
+}
+
static int
next_argv()
{
extern VALUE rb_argv;
char *fn;
+ OpenFile *fptr;
+ int stdout_binmode = 0;
+
+ GetOpenFile(rb_stdout, fptr);
+ if (fptr->mode & FMODE_BINMODE)
+ stdout_binmode = 1;
if (init_p == 0) {
if (RARRAY(rb_argv)->len > 0) {
@@ -2886,7 +2977,6 @@ next_argv()
filename = rb_str_new2("-");
}
init_p = 1;
- first_p = 0;
gets_lineno = 0;
}
@@ -2900,7 +2990,6 @@ next_argv()
current_file = rb_stdin;
if (ruby_inplace_mode) {
rb_warn("Can't do inplace edit for stdio");
- rb_defout = rb_stdout;
}
}
else {
@@ -2911,8 +3000,8 @@ next_argv()
VALUE str;
FILE *fw;
- if (TYPE(rb_defout) == T_FILE && rb_defout != rb_stdout) {
- rb_io_close(rb_defout);
+ if (TYPE(rb_stdout) == T_FILE && rb_stdout != orig_stdout) {
+ rb_io_close(rb_stdout);
}
fstat(fileno(fr), &st);
if (*ruby_inplace_mode) {
@@ -2960,8 +3049,9 @@ next_argv()
fchown(fileno(fw), st.st_uid, st.st_gid);
}
#endif
- rb_defout = prep_stdio(fw, FMODE_WRITABLE, rb_cFile);
- prep_path(rb_defout, fn);
+ rb_stdout = prep_stdio(fw, FMODE_WRITABLE, rb_cFile);
+ prep_path(rb_stdout, fn);
+ if (stdout_binmode) rb_io_binmode(rb_stdout);
}
current_file = prep_stdio(fr, FMODE_READABLE, rb_cFile);
prep_path(current_file, fn);
@@ -2970,22 +3060,15 @@ next_argv()
}
else {
init_p = 0;
+ if (ruby_inplace_mode) {
+ rb_stdout = orig_stdout;
+ }
return Qfalse;
}
}
return Qtrue;
}
-static void
-any_close(file)
- VALUE file;
-{
- if (TYPE(file) == T_FILE)
- rb_io_close(file);
- else
- rb_funcall3(file, rb_intern("close"), 0, 0);
-}
-
static VALUE
argf_getline(argc, argv)
int argc;
@@ -2995,10 +3078,7 @@ argf_getline(argc, argv)
retry:
if (!next_argv()) return Qnil;
- if (TYPE(current_file) != T_FILE) {
- line = rb_funcall3(current_file, rb_intern("gets"), argc, argv);
- }
- else if (argc == 0 && rb_rs == rb_default_rs) {
+ if (argc == 0 && rb_rs == rb_default_rs) {
line = rb_io_gets(current_file);
}
else {
@@ -3012,17 +3092,17 @@ argf_getline(argc, argv)
rb_scan_args(argc, argv, "1", &rs);
}
GetOpenFile(current_file, fptr);
- rb_io_check_readable(fptr);
line = rb_io_getline(rs, fptr);
}
if (NIL_P(line) && next_p != -1) {
- any_close(current_file);
+ argf_close(current_file);
next_p = 1;
goto retry;
}
- gets_lineno++;
- lineno = INT2FIX(gets_lineno);
-
+ if (!NIL_P(line)) {
+ gets_lineno++;
+ lineno = INT2FIX(gets_lineno);
+ }
return line;
}
@@ -3031,8 +3111,15 @@ rb_f_gets(argc, argv)
int argc;
VALUE *argv;
{
- VALUE line = argf_getline(argc, argv);
+ VALUE line;
+ if (TYPE(current_file) != T_FILE) {
+ if (!next_argv()) return Qnil;
+ line = rb_funcall3(current_file, rb_intern("gets"), argc, argv);
+ }
+ else {
+ line = argf_getline(argc, argv);
+ }
rb_lastline_set(line);
return line;
}
@@ -3050,7 +3137,7 @@ rb_gets()
if (!next_argv()) return Qnil;
line = rb_io_gets(current_file);
if (NIL_P(line) && next_p != -1) {
- any_close(current_file);
+ argf_close(current_file);
next_p = 1;
goto retry;
}
@@ -3068,8 +3155,10 @@ rb_f_readline(argc, argv)
int argc;
VALUE *argv;
{
- VALUE line = rb_f_gets(argc, argv);
+ VALUE line;
+ NEXT_ARGF_FORWARD();
+ line = rb_f_gets(argc, argv);
if (NIL_P(line)) {
rb_eof_error();
}
@@ -3081,6 +3170,9 @@ static VALUE
rb_f_getc()
{
rb_warn("getc is obsolete; use STDIN.getc instead");
+ if (TYPE(rb_stdin) != T_FILE) {
+ return rb_funcall3(rb_stdin, rb_intern("getc"), 0, 0);
+ }
return rb_io_getc(rb_stdin);
}
@@ -3091,6 +3183,7 @@ rb_f_readlines(argc, argv)
{
VALUE line, ary;
+ NEXT_ARGF_FORWARD();
ary = rb_ary_new();
while (!NIL_P(line = argf_getline(argc, argv))) {
rb_ary_push(ary, line);
@@ -3312,32 +3405,35 @@ rb_io_ctl(io, req, arg, io_p)
else if (arg == Qtrue) {
narg = 1;
}
- else if (rb_obj_is_kind_of(arg, rb_cInteger)) {
- narg = NUM2LONG(arg);
- }
else {
- StringValue(arg);
+ VALUE tmp = rb_check_string_type(arg);
+ if (NIL_P(tmp)) {
+ narg = NUM2LONG(arg);
+ }
+ else {
+ arg = tmp;
#ifdef IOCPARM_MASK
#ifndef IOCPARM_LEN
#define IOCPARM_LEN(x) (((x) >> 16) & IOCPARM_MASK)
#endif
#endif
#ifdef IOCPARM_LEN
- len = IOCPARM_LEN(cmd); /* on BSDish systems we're safe */
+ len = IOCPARM_LEN(cmd); /* on BSDish systems we're safe */
#else
- len = 256; /* otherwise guess at what's safe */
+ len = 256; /* otherwise guess at what's safe */
#endif
- rb_str_modify(arg);
+ rb_str_modify(arg);
- if (len <= RSTRING(arg)->len) {
- len = RSTRING(arg)->len;
- }
- if (RSTRING(arg)->len < len) {
- rb_str_resize(arg, len+1);
+ if (len <= RSTRING(arg)->len) {
+ len = RSTRING(arg)->len;
+ }
+ if (RSTRING(arg)->len < len) {
+ rb_str_resize(arg, len+1);
+ }
+ RSTRING(arg)->ptr[len] = 17; /* a little sanity check here */
+ narg = (long)RSTRING(arg)->ptr;
}
- RSTRING(arg)->ptr[len] = 17; /* a little sanity check here */
- narg = (long)RSTRING(arg)->ptr;
}
retval = io_cntl(fileno(fptr->f), cmd, narg, io_p);
if (retval < 0) rb_sys_fail(fptr->path);
@@ -3411,16 +3507,16 @@ rb_f_syscall(argc, argv)
rb_raise(rb_eArgError, "too few arguments for syscall");
arg[0] = NUM2LONG(argv[0]); argv++;
while (items--) {
- if (FIXNUM_P(*argv)) {
- arg[i] = (unsigned long)NUM2LONG(*argv);
- }
- else {
- VALUE v = *argv;
+ VALUE v = rb_check_string_type(*argv);
+ if (!NIL_P(v)) {
StringValue(v);
rb_str_modify(v);
arg[i] = (unsigned long)RSTRING(v)->ptr;
}
+ else {
+ arg[i] = (unsigned long)NUM2LONG(*argv);
+ }
argv++;
i++;
}
@@ -3487,12 +3583,21 @@ rb_f_syscall(argc, argv)
#endif
}
+static VALUE io_new_instance _((VALUE));
static VALUE
-rb_io_s_pipe()
+io_new_instance(args)
+ VALUE args;
+{
+ return rb_class_new_instance(2, (VALUE*)args+1, *(VALUE*)args);
+}
+
+static VALUE
+rb_io_s_pipe(klass)
+ VALUE klass;
{
#ifndef __human68k__
- int pipes[2];
- VALUE r, w;
+ int pipes[2], state;
+ VALUE r, w, args[3];
#ifdef _WIN32
if (_pipe(pipes, 1024, O_BINARY) == -1)
@@ -3501,8 +3606,24 @@ rb_io_s_pipe()
#endif
rb_sys_fail(0);
- r = prep_stdio(rb_fdopen(pipes[0], "r"), FMODE_READABLE, rb_cIO);
- w = prep_stdio(rb_fdopen(pipes[1], "w"), FMODE_WRITABLE|FMODE_SYNC, rb_cIO);
+ args[0] = klass;
+ args[1] = INT2NUM(pipes[0]);
+ args[2] = INT2FIX(O_RDONLY);
+ r = rb_protect(io_new_instance, (VALUE)args, &state);
+ if (state) {
+ close(pipes[0]);
+ close(pipes[1]);
+ rb_jump_tag(state);
+ }
+ args[1] = INT2NUM(pipes[1]);
+ args[2] = INT2FIX(O_WRONLY);
+ w = rb_protect(io_new_instance, (VALUE)args, &state);
+ if (state) {
+ close(pipes[1]);
+ if (!NIL_P(r)) rb_io_close(r);
+ rb_jump_tag(state);
+ }
+ rb_io_synchronized(RFILE(w)->fptr);
return rb_assoc_new(r, w);
#else
@@ -3612,10 +3733,7 @@ argf_tell()
if (!next_argv()) {
rb_raise(rb_eArgError, "no stream to tell");
}
-
- if (TYPE(current_file) != T_FILE) {
- return argf_forward();
- }
+ ARGF_FORWARD();
return rb_io_tell(current_file);
}
@@ -3628,10 +3746,7 @@ argf_seek_m(argc, argv, self)
if (!next_argv()) {
rb_raise(rb_eArgError, "no stream to seek");
}
-
- if (TYPE(current_file) != T_FILE) {
- return argf_forward();
- }
+ ARGF_FORWARD();
return rb_io_seek_m(argc, argv, current_file);
}
@@ -3642,10 +3757,7 @@ argf_set_pos(self, offset)
if (!next_argv()) {
rb_raise(rb_eArgError, "no stream to set position");
}
-
- if (TYPE(current_file) != T_FILE) {
- return argf_forward();
- }
+ ARGF_FORWARD();
return rb_io_set_pos(current_file, offset);
}
@@ -3655,9 +3767,7 @@ argf_rewind()
if (!next_argv()) {
rb_raise(rb_eArgError, "no stream to rewind");
}
- if (TYPE(current_file) != T_FILE) {
- return argf_forward();
- }
+ ARGF_FORWARD();
return rb_io_rewind(current_file);
}
@@ -3667,15 +3777,15 @@ argf_fileno()
if (!next_argv()) {
rb_raise(rb_eArgError, "no stream");
}
- if (TYPE(current_file) != T_FILE) {
- return argf_forward();
- }
+ ARGF_FORWARD();
return rb_io_fileno(current_file);
}
static VALUE
argf_to_io()
{
+ next_argv();
+ ARGF_FORWARD();
return current_file;
}
@@ -3694,27 +3804,19 @@ argf_read(argc, argv)
if (!next_argv()) return str;
if (TYPE(current_file) != T_FILE) {
tmp = argf_forward();
- StringValue(tmp);
}
else {
tmp = io_read(argc, argv, current_file);
}
if (NIL_P(tmp) && next_p != -1) {
- any_close(current_file);
+ argf_close(current_file);
next_p = 1;
- goto retry;
+ if (argc == 0) goto retry;
}
if (NIL_P(tmp) || RSTRING(tmp)->len == 0) return str;
else if (NIL_P(str)) str = tmp;
else rb_str_append(str, tmp);
- if (argc == 0) {
- goto retry;
- }
- if (RSTRING(tmp)->len < len) {
- len -= RSTRING(tmp)->len;
- argv[0] = LONG2FIX(len);
- goto retry;
- }
+ if (argc == 0) goto retry;
return str;
}
@@ -3733,7 +3835,7 @@ argf_getc()
byte = rb_io_getc(current_file);
}
if (NIL_P(byte) && next_p != -1) {
- any_close(current_file);
+ argf_close(current_file);
next_p = 1;
goto retry;
}
@@ -3744,8 +3846,10 @@ argf_getc()
static VALUE
argf_readchar()
{
- VALUE c = argf_getc();
+ VALUE c;
+ NEXT_ARGF_FORWARD();
+ c = argf_getc();
if (NIL_P(c)) {
rb_eof_error();
}
@@ -3755,16 +3859,13 @@ argf_readchar()
static VALUE
argf_eof()
{
- if (!next_argv()) return Qtrue;
- if (next_p == 1) {
- return Qtrue;
- }
- if (TYPE(current_file) != T_FILE) {
- return argf_forward();
- }
- if (rb_io_eof(current_file)) {
- next_p = 1;
- return Qtrue;
+ if (current_file) {
+ if (init_p == 0) return Qtrue;
+ ARGF_FORWARD();
+ if (rb_io_eof(current_file)) {
+ next_p = 1;
+ return Qtrue;
+ }
}
return Qfalse;
}
@@ -3776,7 +3877,15 @@ argf_each_line(argc, argv)
{
VALUE str;
- while (RTEST(str = argf_getline(argc, argv))) {
+ if (!next_argv()) return Qnil;
+ if (TYPE(current_file) != T_FILE) {
+ for (;;) {
+ if (!next_argv()) return argf;
+ rb_iterate(rb_each, current_file, rb_yield, 0);
+ next_p = 1;
+ }
+ }
+ while (!NIL_P(str = argf_getline(argc, argv))) {
rb_yield(str);
}
return argf;
@@ -3812,12 +3921,8 @@ argf_binmode()
{
binmode = 1;
next_argv();
- if (TYPE(current_file) != T_FILE) {
- argf_forward();
- }
- else {
- rb_io_binmode(current_file);
- }
+ ARGF_FORWARD();
+ rb_io_binmode(current_file);
return argf;
}
@@ -3825,16 +3930,17 @@ static VALUE
argf_skip()
{
if (next_p != -1) {
- any_close(current_file);
+ argf_close(current_file);
next_p = 1;
}
return argf;
}
static VALUE
-argf_close()
+argf_close_m()
{
- any_close(current_file);
+ next_argv();
+ argf_close(current_file);
if (next_p != -1) {
next_p = 1;
}
@@ -3845,13 +3951,18 @@ argf_close()
static VALUE
argf_closed()
{
- if (TYPE(current_file) != T_FILE) {
- return argf_forward();
- }
+ next_argv();
+ ARGF_FORWARD();
return rb_io_closed(current_file);
}
static VALUE
+argf_to_s()
+{
+ return rb_str_new2("ARGF");
+}
+
+static VALUE
opt_i_get()
{
if (!ruby_inplace_mode) return Qnil;
@@ -3876,10 +3987,25 @@ opt_i_set(val)
void
Init_IO()
{
+#ifdef __CYGWIN__
+#include <sys/cygwin.h>
+ static struct __cygwin_perfile pf[] =
+ {
+ {"", O_RDONLY | O_BINARY},
+ {"", O_WRONLY | O_BINARY},
+ {"", O_RDWR | O_BINARY},
+ {"", O_APPEND | O_BINARY},
+ {NULL, 0}
+ };
+ cygwin_internal(CW_PERFILE, pf);
+#endif
+
rb_eIOError = rb_define_class("IOError", rb_eStandardError);
rb_eEOFError = rb_define_class("EOFError", rb_eIOError);
id_write = rb_intern("write");
+ id_read = rb_intern("read");
+ id_getc = rb_intern("getc");
rb_define_global_function("syscall", rb_f_syscall, -1);
@@ -3903,11 +4029,11 @@ Init_IO()
rb_cIO = rb_define_class("IO", rb_cObject);
rb_include_module(rb_cIO, rb_mEnumerable);
- rb_define_singleton_method(rb_cIO, "allocate", rb_io_s_alloc, 0);
+ rb_define_alloc_func(rb_cIO, io_alloc);
rb_define_singleton_method(rb_cIO, "new", rb_io_s_new, -1);
rb_define_singleton_method(rb_cIO, "open", rb_io_s_open, -1);
rb_define_singleton_method(rb_cIO, "sysopen", rb_io_s_sysopen, -1);
- rb_define_singleton_method(rb_cIO, "for_fd", rb_class_new_instance, -1);
+ rb_define_singleton_method(rb_cIO, "for_fd", rb_io_s_for_fd, -1);
rb_define_singleton_method(rb_cIO, "popen", rb_io_s_popen, -1);
rb_define_singleton_method(rb_cIO, "foreach", rb_io_s_foreach, -1);
rb_define_singleton_method(rb_cIO, "readlines", rb_io_s_readlines, -1);
@@ -3924,14 +4050,14 @@ Init_IO()
rb_output_rs = Qnil;
rb_global_variable(&rb_default_rs);
OBJ_FREEZE(rb_default_rs); /* avoid modifying RS_default */
- rb_define_variable("$/", &rb_rs);
- rb_define_variable("$-0", &rb_rs);
- rb_define_variable("$\\", &rb_output_rs);
+ rb_define_hooked_variable("$/", &rb_rs, 0, rb_str_setter);
+ rb_define_hooked_variable("$-0", &rb_rs, 0, rb_str_setter);
+ rb_define_hooked_variable("$\\", &rb_output_rs, 0, rb_str_setter);
rb_define_hooked_variable("$.", &lineno, 0, lineno_setter);
rb_define_virtual_variable("$_", rb_lastline_get, rb_lastline_set);
- rb_define_method(rb_cIO, "copy_object", rb_io_copy_object, 1);
+ rb_define_method(rb_cIO, "initialize_copy", rb_io_init_copy, 1);
rb_define_method(rb_cIO, "reopen", rb_io_reopen, -1);
rb_define_method(rb_cIO, "print", rb_io_print, -1);
@@ -3994,16 +4120,21 @@ Init_IO()
rb_define_method(rb_cIO, "pid", rb_io_pid, 0);
rb_define_method(rb_cIO, "inspect", rb_io_inspect, 0);
- rb_stdin = orig_stdin = prep_stdio(stdin, FMODE_READABLE, rb_cIO);
- rb_define_hooked_variable("$stdin", &rb_stdin, 0, set_stdin);
- rb_stdout = orig_stdout = prep_stdio(stdout, FMODE_WRITABLE, rb_cIO);
- rb_define_hooked_variable("$stdout", &rb_stdout, 0, set_stdout);
- rb_stderr = orig_stderr = prep_stdio(stderr, FMODE_WRITABLE, rb_cIO);
- rb_define_hooked_variable("$stderr", &rb_stderr, 0, set_stderr);
- rb_defout = rb_stdout;
- rb_define_hooked_variable("$>", &rb_defout, 0, rb_io_defset);
- rb_define_hooked_variable("$defout", &rb_defout, 0, rb_io_defset);
-
+ rb_stdin = prep_stdio(stdin, FMODE_READABLE, rb_cIO);
+ rb_define_variable("$stdin", &rb_stdin);
+ rb_stdout = prep_stdio(stdout, FMODE_WRITABLE, rb_cIO);
+ rb_define_hooked_variable("$stdout", &rb_stdout, 0, stdout_setter);
+ rb_stderr = prep_stdio(stderr, FMODE_WRITABLE, rb_cIO);
+ rb_define_hooked_variable("$stderr", &rb_stderr, 0, stdout_setter);
+ rb_define_hooked_variable("$>", &rb_stdout, 0, stdout_setter);
+ orig_stdout = rb_stdout;
+ rb_deferr = orig_stderr = rb_stderr;
+
+ /* variables to be removed in 1.8.1 */
+ rb_define_hooked_variable("$defout", &rb_stdout, 0, defout_setter);
+ rb_define_hooked_variable("$deferr", &rb_stderr, 0, deferr_setter);
+
+ /* constants to hold original stdin/stdout/stderr */
rb_define_global_const("STDIN", rb_stdin);
rb_define_global_const("STDOUT", rb_stdout);
rb_define_global_const("STDERR", rb_stderr);
@@ -4014,6 +4145,8 @@ Init_IO()
rb_define_readonly_variable("$<", &argf);
rb_define_global_const("ARGF", argf);
+ rb_define_singleton_method(argf, "to_s", argf_to_s, 0);
+
rb_define_singleton_method(argf, "fileno", argf_fileno, 0);
rb_define_singleton_method(argf, "to_i", argf_fileno, 0);
rb_define_singleton_method(argf, "to_io", argf_to_io, 0);
@@ -4037,17 +4170,16 @@ Init_IO()
rb_define_singleton_method(argf, "eof?", argf_eof, 0);
rb_define_singleton_method(argf, "binmode", argf_binmode, 0);
- rb_define_singleton_method(argf, "to_s", argf_filename, 0);
rb_define_singleton_method(argf, "filename", argf_filename, 0);
+ rb_define_singleton_method(argf, "path", argf_filename, 0);
rb_define_singleton_method(argf, "file", argf_file, 0);
rb_define_singleton_method(argf, "skip", argf_skip, 0);
- rb_define_singleton_method(argf, "close", argf_close, 0);
+ rb_define_singleton_method(argf, "close", argf_close_m, 0);
rb_define_singleton_method(argf, "closed?", argf_closed, 0);
rb_define_singleton_method(argf, "lineno", argf_lineno, 0);
rb_define_singleton_method(argf, "lineno=", argf_set_lineno, 1);
- current_file = rb_stdin;
rb_global_variable(&current_file);
filename = rb_str_new2("-");
rb_define_readonly_variable("$FILENAME", &filename);
diff --git a/lib/README b/lib/README
index 0aa5e37549..421681583d 100644
--- a/lib/README
+++ b/lib/README
@@ -5,7 +5,7 @@ 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 CGI session class
+cgi/session.rb CGI session class
complex.rb complex number suppor
date.rb date object
date/format.rb date parsing and formatting
@@ -13,17 +13,17 @@ date2.rb date object (obsolete; use date)
debug.rb ruby debugger
delegate.rb delegates messages to other object
e2mmap.rb exception utilities
+erb.rb tiny eRuby library
eregex.rb extended regular expression (just a proof of concept)
fileutils.rb file utilities
-final.rb adds finalizer to the object (simple)
finalize.rb adds finalizer to the object
find.rb traverses directory tree
forwardable.rb explicit delegation library
ftools.rb file tools
-ftplib.rb obsolete - use net/ftp
getoptlong.rb GNU getoptlong compatible
getopts.rb parses command line options (use getoptlong)
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)
mailread.rb reads mail headers
@@ -40,6 +40,7 @@ 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
ostruct.rb python style object
parsearg.rb argument parser using getopts
@@ -47,22 +48,23 @@ parsedate.rb parses date string
ping.rb checks whether host is up, using TCP echo.
pp.rb pretty print objects
prettyprint.rb pretty printing algorithm
-profile.rb ruby profiler
+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
readbytes.rb define IO#readbytes
-resolv.rb DNS resolver in Ruby
resolv-replace.rb replace Socket DNS by resolve.rb
+resolv.rb DNS resolver in 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
sync.rb 2 phase lock
-telnet.rb obsolete - use net/telnet
tempfile.rb temporary file with automatic removal
-time.rb RFC2822, RFC2616, ISO8601 style time formatting/parsing
thread.rb thread support
thwait.rb thread syncronization class
+time.rb RFC2822, RFC2616, ISO8601 style time formatting/parsing
timeout.rb provides timeout
tracer.rb execution tracer
tsort.rb topological sorting
diff --git a/lib/benchmark.rb b/lib/benchmark.rb
index 8237c0f3ed..547f21eac2 100644
--- a/lib/benchmark.rb
+++ b/lib/benchmark.rb
@@ -2,18 +2,6 @@
# benchmark.rb
#
=begin
- 2002-04-25: bmbm(): killed unused parameter @fmtstr (gotoken)
- 2001-11-26: Time.times renamed Process.times for ruby17 (gotoken#notwork.org)
- 2001-01-12: made bmbm module func. bmbm return Tms array.
- 2001-01-10: added bmbm, Job and INSTALL.rb (gotoken#notwork.org)
- 2000-04-00: report() prints tag before eval block (gotoken#notwork.org)
- 2000-02-22: report(): measure -> Benchmark::measure (nakahiro#sarion.co.jp)
- 2000-01-02: bug fix, documentation (gotoken#notwork.org)
- 2000-01-01: measure can take a tag as opt. (nobu.nakada#nifty.ne.jp)
- 2000-01-01: first release (gotoken#notwork.org)
-=end
-
-=begin
= benchmark.rb
== NAME
@@ -432,7 +420,8 @@ returns a new array as follows
same as (({format()})). See also ((<format>)).
== HISTORY
-<<< benchmark.rb
+
+A benchmark.rb appeared in RAA January 1st 2000.
== AUTHOR
@@ -461,11 +450,11 @@ module Benchmark
STDOUT.sync = sync
end
- def bm(label_width = 0, *labels, &blk)
- benchmark(" "*label_width + CAPTION, label_width, FMTSTR, *labels, &blk)
+ def bm(label_width = 0, *labels)
+ benchmark(" "*label_width + CAPTION, label_width, FMTSTR, *labels){|*x|yield(*x)}
end
- def bmbm(width = 0, &blk)
+ def bmbm(width = 0)
job = Job.new(width)
yield(job)
width = job.width
diff --git a/lib/cgi-lib.rb b/lib/cgi-lib.rb
index ab4eef1068..bc780ffc4e 100644
--- a/lib/cgi-lib.rb
+++ b/lib/cgi-lib.rb
@@ -181,7 +181,7 @@ class CGI < SimpleDelegator
super(@inputs)
if ENV.has_key?('HTTP_COOKIE') or ENV.has_key?('COOKIE')
- (ENV['HTTP_COOKIE'] or ENV['COOKIE']).split("; ").each do |x|
+ (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")
diff --git a/lib/cgi.rb b/lib/cgi.rb
index 714a3a2099..705b0ccf13 100644
--- a/lib/cgi.rb
+++ b/lib/cgi.rb
@@ -18,8 +18,8 @@ Wakou Aoyama <wakou@ruby-lang.org>
require "cgi"
cgi = CGI.new
- values = cgi['field_name'] # <== array of 'field_name'
- # if not 'field_name' included, then return [].
+ 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'
@@ -27,6 +27,8 @@ Wakou Aoyama <wakou@ruby-lang.org>
cgi.has_key?('field_name')
cgi.include?('field_name')
+CAUTION! cgi['field_name'] retuen Array with old cgi.rb(included ruby 1.6)
+
=== GET FORM VALUES AS HASH
@@ -64,13 +66,13 @@ cgi.params is a hash.
require "cgi"
cgi = CGI.new
- values = cgi['field_name'] # <== array of 'field_name'
- values[0].read # <== body of values[0]
- values[0].local_path # <== path to local file of values[0]
- values[0].original_filename # <== original filename of values[0]
- values[0].content_type # <== content_type of values[0]
+ 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 values[0] has StringIO or Tempfile class methods.
+and value has StringIO or Tempfile class methods.
=== GET COOKIE VALUES
@@ -492,10 +494,10 @@ status:
if defined?(MOD_RUBY)
table = Apache::request.headers_out
buf.scan(/([^:]+): (.+)#{EOL}/n){ |name, value|
- $stderr.printf("name:%s value:%s\n", name, value) if $DEBUG
+ warn sprintf("name:%s value:%s\n", name, value) if $DEBUG
case name
when 'Set-Cookie'
- table.add($1, $2)
+ table.add(name, value)
when /^status$/ni
Apache::request.status_line = value
Apache::request.status = value.to_i
@@ -698,7 +700,7 @@ convert string charset, and set language to "ja".
cookies = Hash.new([])
return cookies unless raw_cookie
- raw_cookie.split('; ').each do |pairs|
+ raw_cookie.split(/; /).each do |pairs|
name, values = pairs.split('=',2)
name = CGI::unescape(name)
values ||= ""
@@ -798,7 +800,7 @@ convert string charset, and set language to "ja".
body = Tempfile.new("CGI")
else
begin
- require "stringio" if not defined? StringIO
+ require "stringio"
body = StringIO.new
rescue LoadError
require "tempfile"
@@ -915,6 +917,7 @@ convert string charset, and set language to "ja".
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
@params = CGI::parse(
@@ -939,8 +942,42 @@ convert string charset, and set language to "ja".
end
private :initialize_query
- def [](*args)
- @params[*args]
+ class Value < String
+ def initialize(str, params)
+ @params = params
+ super(str)
+ end
+ def [](idx)
+ warn "#{caller(1)[0]}:CAUTION! cgi['key'] == cgi.params['key'][0]; if want Array, use cgi.params['key']"
+ self
+ 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
+ end
+ def to_ary # to be rhs of multiple assignment
+ @params
+ end
+ end
+
+ def [](key)
+ params = @params[key]
+ value = params[0]
+ if @multipart
+ if value
+ return value
+ elsif defined? StringIO
+ StringIO.new("")
+ else
+ Tempfile.new("CGI")
+ end
+ else
+ Value.new(value || "", params)
+ end
end
def keys(*args)
@@ -1906,6 +1943,7 @@ The hash keys are case sensitive. Ask the samples.
end
extend QueryExtension
+ @multipart = false
if "POST" != env_table['REQUEST_METHOD']
initialize_query() # set @params, @cookies
else
diff --git a/lib/cgi/session.rb b/lib/cgi/session.rb
index 64a7bd7016..0ce82b3920 100644
--- a/lib/cgi/session.rb
+++ b/lib/cgi/session.rb
@@ -3,7 +3,7 @@
# Copyright (C) 2000 Information-technology Promotion Agency, Japan
require 'cgi'
-require 'final'
+require 'tmpdir'
class CGI
class Session
@@ -28,15 +28,17 @@ class CGI
def initialize(request, option={})
session_key = option['session_key'] || '_session_id'
- id, = option['session_id']
+ id = option['session_id']
unless id
if option['new_session']
id = Session::create_new_id
end
end
unless id
- id, = request[session_key]
- id = id.read if id.respond_to?(:read)
+ if request.key?(session_key)
+ id = request[session_key]
+ id = id.read if id.respond_to?(:read)
+ end
unless id
id, = request.cookies[session_key]
end
@@ -108,7 +110,7 @@ class CGI
end
def initialize(session, option={})
- dir = option['tmpdir'] || ENV['TMP'] || '/tmp'
+ dir = option['tmpdir'] || Dir::tmpdir
prefix = option['prefix'] || ''
id = session.session_id
unless check_id(id)
diff --git a/lib/cgi/session/pstore.rb b/lib/cgi/session/pstore.rb
new file mode 100644
index 0000000000..d681d994a9
--- /dev/null
+++ b/lib/cgi/session/pstore.rb
@@ -0,0 +1,78 @@
+require 'cgi/session'
+require 'pstore'
+
+class CGI
+ class Session
+ def []=(key, val)
+ unless @write_lock
+ @write_lock = true
+ end
+ unless @data
+ @data = @dbman.restore
+ end
+ #@data[key] = String(val)
+ @data[key] = val
+ end
+
+ class PStore
+ def check_id(id)
+ /[^0-9a-zA-Z]/ =~ id.to_s ? false : true
+ end
+
+ def initialize session, option={}
+ dir = option['tmpdir'] || ENV['TMP'] || '/tmp'
+ prefix = option['prefix'] || ''
+ id = session.session_id
+ unless check_id(id)
+ raise ArgumentError, "session_id `%s' is invalid" % id
+ end
+ path = dir+"/"+prefix+id
+ path.untaint
+ unless File::exist? path
+ @hash = {}
+ end
+ @p = ::PStore.new path
+ end
+
+ def restore
+ unless @hash
+ @p.transaction do
+ begin
+ @hash = @p['hash']
+ rescue
+ @hash = {}
+ end
+ end
+ end
+ @hash
+ end
+
+ def update
+ @p.transaction do
+ @p['hash'] = @hash
+ end
+ end
+
+ def close
+ update
+ end
+
+ def delete
+ path = @p.path
+ File::unlink path
+ end
+
+ end
+ end
+end
+
+if $0 == __FILE__
+ 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/lib/complex.rb b/lib/complex.rb
index c27746a82f..9300f391e8 100644
--- a/lib/complex.rb
+++ b/lib/complex.rb
@@ -5,86 +5,136 @@
# $Date: 1998/07/08 10:05:28 $
# by Keiju ISHITSUKA(SHL Japan Inc.)
#
-# --
-# Usage:
-# class Complex < Numeric
+# ----
#
-# Complex(x, y) --> x + yi
-# y.im --> 0 + yi
+# 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::polar
+# 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>)
#
-# Complex::+
-# Complex::-
-# Complex::*
-# Complex::/
-# Complex::**
-# Complex::%
-# Complex::divmod -- obsolete
-# Complex::abs
-# Complex::abs2
-# Complex::arg
-# Complex::polar
-# Complex::conjugate
-# Complex::<=>
-# Complex::==
-# Complex::to_f
-# Complex::to_r
-# Complex::to_s
+# 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
#
-# Complex::I
+
+
#
-# Numeric::im
+# 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.
#
-# Math.sqrt
-# Math.exp
-# Math.cos
-# Math.sin
-# Math.tan
-# Math.log
-# Math.log10
-# Math.atan2
+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 a.kind_of?(Complex) and b == 0
- a
- elsif b.kind_of?(Complex)
- if a.kind_of?(Complex)
- Complex(a.real-b.image, a.image + b.real)
- else
- Complex(a-b.image, b.real)
- end
- elsif b == 0 and defined? Complex::Unify
+ if b == 0 and (a.kind_of?(Complex) or defined? Complex::Unify)
a
else
- Complex.new(a, b)
+ 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)
+ 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
-
- def initialize(a, b = 0)
- raise "non numeric 1st arg `#{a.inspect}'" if !a.kind_of? Numeric
- raise "non numeric 2nd arg `#{b.inspect}'" if !b.kind_of? Numeric
+
+ #
+ # 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
@@ -98,6 +148,9 @@ class Complex < Numeric
end
end
+ #
+ # Subtraction with real or complex number.
+ #
def - (other)
if other.kind_of?(Complex)
re = @real - other.real
@@ -111,6 +164,9 @@ class Complex < Numeric
end
end
+ #
+ # Multiplication with real or complex number.
+ #
def * (other)
if other.kind_of?(Complex)
re = @real*other.real - @image*other.image
@@ -124,6 +180,9 @@ class Complex < Numeric
end
end
+ #
+ # Division by real or complex number.
+ #
def / (other)
if other.kind_of?(Complex)
self*other.conjugate/other.abs2
@@ -135,6 +194,9 @@ class Complex < Numeric
end
end
+ #
+ # Raise this complex number to the given (real or complex) power.
+ #
def ** (other)
if other == 0
return Complex(1)
@@ -170,13 +232,16 @@ class Complex < Numeric
end
elsif Complex.generic?(other)
r, theta = polar
- Complex.polar(r.power!(other), theta * other)
+ Complex.polar(r**other, theta*other)
else
x, y = other.coerce(self)
- x/y
+ 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)
@@ -188,6 +253,7 @@ class Complex < Numeric
end
end
+#--
# def divmod(other)
# if other.kind_of?(Complex)
# rdiv, rmod = @real.divmod(other.real)
@@ -200,60 +266,96 @@ class Complex < Numeric
# x.divmod(y)
# end
# end
+#++
+ #
+ # Absolute value (aka modulus): distance from the zero point on the complex
+ # plane.
+ #
def abs
- Math.sqrt!((@real*@real + @image*@image).to_f)
+ 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.to_f, @real.to_f)
+ 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
- x , y = other.coerce(self)
- x == y
+ other == self
end
end
+ #
+ # Attempts to coerce +other+ to a Complex number.
+ #
def coerce(other)
if Complex.generic?(other)
- return Complex.new(other), self
+ 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
@@ -278,95 +380,58 @@ class Complex < Numeric
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
- attr :image
-
-end
-class Numeric
- def im
- Complex(0, self)
- end
-
- def real
- self
- end
-
- def image
- 0
- end
-
- def arg
- if self >= 0
- return 0
- else
- return Math.atan2(1,1)*4
- end
- end
-
- def polar
- return abs, arg
- end
+ # The imaginary part of a complex number.
+ attr :image
+ alias imag image
- def conjugate
- self
- end
end
-class Fixnum
- if not defined? Rational
- alias power! **
- end
-
- def ** (other)
- if self < 0
- Complex.new(self) ** other
- else
- if defined? Rational
- if other >= 0
- self.power!(other)
- else
- Rational.new!(self,1)**other
- end
- else
- self.power!(other)
- end
- end
- end
-end
-class Bignum
- if not defined? Rational
- alias power! **
- end
-end
-class Float
- alias power! **
-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 log! log
- alias atan! atan
- alias log10! log10
+ 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
@@ -375,10 +440,17 @@ module Math
Complex(0,sqrt!(-z))
end
else
- z**Rational(1,2)
+ 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)
@@ -387,14 +459,7 @@ module Math
end
end
- def cosh!(x)
- (exp!(x) + exp!(-x))/2.0
- end
-
- def sinh!(x)
- (exp!(x) - exp!(-x))/2.0
- end
-
+ # Redefined to handle a Complex argument.
def cos(z)
if Complex.generic?(z)
cos!(z)
@@ -404,6 +469,7 @@ module Math
end
end
+ # Redefined to handle a Complex argument.
def sin(z)
if Complex.generic?(z)
sin!(z)
@@ -413,6 +479,7 @@ module Math
end
end
+ # Redefined to handle a Complex argument.
def tan(z)
if Complex.generic?(z)
tan!(z)
@@ -420,7 +487,32 @@ module Math
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)
@@ -430,6 +522,7 @@ module Math
end
end
+ # Redefined to handle a Complex argument.
def log10(z)
if Complex.generic?(z)
log10!(z)
@@ -437,56 +530,102 @@ module Math
log(z)/log!(10)
end
end
-
- def atan2(x, y)
- if Complex.generic?(x) and Complex.generic?(y)
- atan2!(x, y)
+
+ def acos(z)
+ if Complex.generic?(z) and z >= -1 and z <= 1
+ acos!(z)
else
- fail "Not yet implemented."
+ -1.0.im * log( z + 1.0.im * sqrt(1.0-z*z) )
end
end
-
- def atanh!(x)
- log((1.0 + x.to_f) / ( 1.0 - x.to_f)) / 2.0
+
+ 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)
- atan2!(z, 1)
- elsif z.image == 0
- atan2(z.real,1)
+ atan!(z)
else
- a = z.real
- b = z.image
-
- c = (a*a + b*b - 1.0)
- d = (a*a + b*b + 1.0)
-
- Complex(atan2!((c + sqrt(c*c + 4.0*a*a)), 2.0*a),
- atanh!((-d + sqrt(d*d - 4.0*b*b))/(2.0*b)))
+ 1.0.im * log( (1.0.im+z) / (1.0.im-z) ) / 2.0
end
end
-
- module_function :sqrt
+
+ 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 :log!
- module_function :log
- module_function :log10!
- module_function :log
+ 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 :atan!
- module_function :atan
+ 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/lib/csv.rb b/lib/csv.rb
new file mode 100644
index 0000000000..947eacbcfa
--- /dev/null
+++ b/lib/csv.rb
@@ -0,0 +1,1322 @@
+# CSV -- module for generating/parsing CSV data.
+
+# $Id$
+
+# This module is copyrighted free software by NAKAMURA, Hiroshi.
+# You can redistribute it and/or modify it under the same term as Ruby.
+
+
+class CSV
+public
+
+ # DESCRIPTION
+ # CSV::Cell -- Describes 1 cell of CSV.
+ #
+ class Cell
+ public
+
+ # Datum as string.
+ attr_accessor :data
+
+ # Is this datum null?
+ attr_accessor :is_null
+
+ # SYNOPSIS
+ # cell = CSV::Cell.new(data = '', is_null = true)
+ #
+ # ARGS
+ # data: datum as String
+ # is_null: is this datum null?
+ #
+ # RETURNS
+ # cell: Created instance.
+ #
+ # DESCRIPTION
+ # Create instance. If is_null is true, datum is stored in the instance
+ # created but it should be treated as 'NULL'.
+ #
+ def initialize(data = '', is_null = true)
+ @data = data
+ @is_null = is_null
+ end
+
+ # SYNOPSIS
+ # CSV::Cell#match(rhs)
+ #
+ # ARGS
+ # rhs: an instance of CSV::Cell to be compared.
+ #
+ # RETURNS
+ # true/false. See the souce if you want to know matching algorithm.
+ #
+ # DESCRIPTION
+ # Compare another cell with me. Bare in mind Null matches with Null
+ # using this method. Use CSV::Cell#== if you want Null never matches
+ # with other data including Null.
+ #
+ def match(rhs)
+ if @is_null and rhs.is_null
+ true
+ elsif @is_null or rhs.is_null
+ false
+ else
+ @data == rhs.data
+ end
+ end
+
+ # SYNOPSIS
+ # CSV::Cell#==(rhs)
+ #
+ # ARGS
+ # rhs: an instance of CSV::Cell to be compared.
+ #
+ # RETURNS
+ # true/false. See the souce if you want to know matching algorithm.
+ #
+ # DESCRIPTION
+ # Compare another cell with me. Bare in mind Null is not match with
+ # Null using this method. Null never matches with other data including
+ # Null. Use CSV::Cell#match if you want Null matches with Null.
+ #
+ def ==(rhs)
+ if @is_null or rhs.is_null
+ false
+ else
+ @data == rhs.data
+ end
+ end
+ end
+
+
+ # DESCRIPTION
+ # CSV::Row -- Describes a row of CSV. Each element must be a CSV::Cell.
+ #
+ class Row < Array
+ public
+
+ # SYNOPSIS
+ # CSV::Row#to_a
+ #
+ # RETURNS
+ # An Array of String.
+ #
+ # DESCRIPTION
+ # Convert CSV::Cell to String. Null is converted to nil.
+ #
+ def to_a
+ self.collect { |cell| cell.is_null ? nil : cell.data }
+ end
+
+ # SYNOPSIS
+ # CSV::Row#match(rhs)
+ #
+ # ARGS
+ # rhs: an Array of cells. Each cell is a instance of CSV::Cell.
+ #
+ # RETURNS
+ # true/false. See the souce if you want to know matching algorithm.
+ #
+ # DESCRIPTION
+ # Compare another row with me.
+ #
+ def match(rhs)
+ if self.size != rhs.size
+ return false
+ end
+ for idx in 0...(self.size)
+ unless self[idx].match(rhs[idx])
+ return false
+ end
+ end
+ true
+ end
+ end
+
+
+ # SYNOPSIS
+ # 1. reader = CSV.open(filename, 'r')
+ #
+ # 2. CSV.open(filename, 'r') do |row|
+ # ...
+ # end
+ #
+ # 3. writer = CSV.open(filename, 'w')
+ #
+ # 4. CSV.open(filename, 'w') do |writer|
+ # ...
+ # end
+ #
+ # ARGS
+ # filename: filename to open.
+ # mode: 'r' for read (parse)
+ # 'w' for write (generate)
+ # row: an Array of cells which is a parsed line.
+ # writer: Created writer instance. See CSV::Writer#<< and
+ # CSV::Writer#add_row to know how to generate CSV string.
+ #
+ # RETURNS
+ # reader: Create reader instance. To get parse result, see
+ # CSV::Reader#each.
+ # writer: Created writer instance. See CSV::Writer#<< and
+ # CSV::Writer#add_row to know how to generate CSV string.
+ #
+ # DESCRIPTION
+ # Open a CSV formatted file to read or write.
+ #
+ # EXAMPLE 1
+ # reader = CSV.open('csvfile.csv', 'r')
+ # row1 = reader.shift
+ # row2 = reader.shift
+ # if row2.empty?
+ # p 'row2 not find.'
+ # end
+ # reader.close
+ #
+ # EXAMPLE 2
+ # CSV.open('csvfile.csv', 'r') do |row|
+ # p row
+ # end
+ #
+ # EXAMPLE 3
+ # writer = CSV.open('csvfile.csv', 'w')
+ # writer << ['r1c1', 'r1c2'] << ['r2c1', 'r2c2'] << [nil, nil]
+ # writer.close
+ #
+ # EXAMPLE 4
+ # CSV.open('csvfile.csv', 'w') do |writer|
+ # writer << ['r1c1', 'r1c2']
+ # writer << ['r2c1', 'r2c2']
+ # writer << [nil, nil]
+ # end
+ #
+ def CSV.open(filename, mode, col_sep = ?,, &block)
+ if mode == 'r' or mode == 'rb'
+ open_reader(filename, col_sep, &block)
+ elsif mode == 'w' or mode == 'wb'
+ open_writer(filename, col_sep, &block)
+ else
+ raise ArgumentError.new("'mode' must be 'r', 'rb', 'w', or 'wb'")
+ end
+ end
+
+ def CSV.parse(filename, col_sep = ?,, &block)
+ open_reader(filename, col_sep, &block)
+ end
+
+ def CSV.generate(filename, col_sep = ?,, &block)
+ open_writer(filename, col_sep, &block)
+ end
+
+ # Private class methods.
+ class << self
+ private
+ def open_reader(filename, col_sep, &block)
+ file = File.open(filename, 'rb')
+ if block
+ begin
+ CSV::Reader.parse(file, col_sep) do |row|
+ yield(row)
+ end
+ ensure
+ file.close
+ end
+ nil
+ else
+ reader = CSV::Reader.create(file, col_sep)
+ reader.close_on_terminate
+ reader
+ end
+ end
+
+ def open_writer(filename, col_sep, &block)
+ file = File.open(filename, 'wb')
+ if block
+ begin
+ CSV::Writer.generate(file, col_sep) do |writer|
+ yield(writer)
+ end
+ ensure
+ file.close
+ end
+ nil
+ else
+ writer = CSV::Writer.create(file, col_sep)
+ writer.close_on_terminate
+ writer
+ end
+ end
+ end
+
+
+ # DESCRIPTION
+ # CSV::Reader -- 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
+ public
+
+ # SYNOPSIS
+ # reader = CSV::Reader.create(str_or_readable)
+ #
+ # ARGS
+ # str_or_readable: a CSV data to be parsed. A String or an IO.
+ #
+ # RETURNS
+ # reader: Created instance.
+ #
+ # DESCRIPTION
+ # Create instance. To get parse result, see CSV::Reader#each.
+ #
+ def Reader.create(str_or_readable, col_sep = ?,)
+ case str_or_readable
+ when IO
+ IOReader.new(str_or_readable, col_sep)
+ when String
+ StringReader.new(str_or_readable, col_sep)
+ else
+ IOReader.new(str_or_readable, col_sep)
+ end
+ end
+
+ # SYNOPSIS
+ # CSV::Reader.parse(str_or_readable) do |row|
+ # ...
+ # end
+ #
+ # ARGS
+ # str_or_readable: a CSV data to be parsed. A String or an IO.
+ # row: a CSV::Row; an Array of a CSV::Cell in a line.
+ #
+ # RETURNS
+ # nil
+ #
+ # DESCRIPTION
+ # Parse CSV data and get lines. Caller block is called for each line
+ # with an argument which is a chunk of cells in a row.
+ #
+ # Block value is always nil. Rows are not cached for performance
+ # reason.
+ #
+ def Reader.parse(str_or_readable, col_sep = ?,)
+ reader = create(str_or_readable, col_sep)
+ reader.each do |row|
+ yield(row)
+ end
+ reader.close
+ nil
+ end
+
+ # SYNOPSIS
+ # CSV::Reader#each do |row|
+ # ...
+ # end
+ #
+ # ARGS
+ # row: a CSV::Row; an Array of a CSV::Cell in a line.
+ #
+ # RETURNS
+ # nil
+ #
+ # DESCRIPTION
+ # Caller block is called for each line with an argument which is a chunk
+ # of cells in a row.
+ #
+ # Block value is always nil. Rows are not cached for performance
+ # reason.
+ #
+ def each
+ while true
+ row = Row.new
+ parsed_cells = get_row(row)
+ if parsed_cells == 0
+ break
+ end
+ yield(row)
+ end
+ nil
+ end
+
+ # SYNOPSIS
+ # cell = CSV::Reader#shift
+ #
+ # RETURNS
+ # cell: a CSV::Row; an Array of a CSV::Cell.
+ #
+ # DESCRIPTION
+ # Extract cells of next line.
+ #
+ def shift
+ row = Row.new
+ parsed_cells = get_row(row)
+ row
+ end
+
+ # SYNOPSIS
+ # CSV::Reader#close
+ #
+ # RETURNS
+ # nil
+ #
+ # DESCRIPTION
+ # Close this reader.
+ #
+ 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
+
+
+ # DESCRIPTION
+ # CSV::StringReader -- CSV formatted 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 StringReader < Reader
+ public
+
+ # SYNOPSIS
+ # reader = CSV::StringReader.new(string)
+ #
+ # ARGS
+ # string: a CSV String to be parsed.
+ #
+ # RETURNS
+ # reader: Created instance.
+ #
+ # DESCRIPTION
+ # Create instance. To get parse result, see CSV::Reader#each.
+ #
+ def initialize(string, col_sep = ?,)
+ @col_sep = col_sep
+ @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, @col_sep)
+ if parsed_cells == 0 && next_idx == 0 && @idx != @dev.size
+ raise IllegalFormatError.new
+ end
+ @idx = next_idx
+ parsed_cells
+ end
+ end
+
+
+ # DESCRIPTION
+ # CSV::IOReader -- CSV formatted 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 IOReader < Reader
+ public
+
+ # SYNOPSIS
+ # reader = CSV::IOReader.new(io)
+ #
+ # ARGS
+ # io: a CSV data to be parsed. Must be an IO. (io#read is called.)
+ #
+ # RETURNS
+ # reader: Created instance.
+ #
+ # DESCRIPTION
+ # Create instance. To get parse result, see CSV::Reader#each.
+ #
+ def initialize(io, col_sep = ?,)
+ @io = io
+ @col_sep = col_sep
+ @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
+
+ # SYNOPSIS
+ # CSV::IOReader#close_on_terminate
+ #
+ # RETURNS
+ # true
+ #
+ # DESCRIPTION
+ # 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, @col_sep)
+ if parsed_cells == 0 && next_idx == 0 && !@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
+
+
+ # DESCRIPTION
+ # CSV::Writer -- 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']
+ # # or
+ # csv.add_row [
+ # CSV::Cell.new('c1', false),
+ # CSV::Cell.new('dummy', true),
+ # CSV::Cell.new('', false),
+ # CSV::Cell.new('"', false),
+ # CSV::Cell.new("\r\n", false)
+ # CSV::Cell.new('c2', false)
+ # ]
+ # ...
+ # ...
+ # end
+ #
+ # outfile.close
+ #
+ class Writer
+ public
+
+ # SYNOPSIS
+ # writer = CSV::Writer.create(str_or_readable)
+ #
+ # ARGS
+ # str_or_writable: device for generated CSV string. Must respond to
+ # '<<(string)'.
+ #
+ # RETURNS
+ # writer: Created instance.
+ #
+ # DESCRIPTION
+ # Create instance. To add CSV data to generate CSV string, see
+ # CSV::Writer#<< or CSV::Writer#add_row.
+ #
+ def Writer.create(str_or_readable, col_sep = ?,)
+ BasicWriter.new(str_or_readable, col_sep)
+ end
+
+ # SYNOPSIS
+ # CSV::Writer.generate(str_or_writable) do |writer|
+ # ...
+ # end
+ #
+ # ARGS
+ # str_or_writable: device for generated CSV string. Must respond to
+ # '<<(string)'.
+ # writer: Created writer instance. See CSV::Writer#<< and
+ # CSV::Writer#add_row to know how to generate CSV string.
+ #
+ # RETURNS
+ # nil
+ #
+ # DESCRIPTION
+ # Create writer instance. Caller block is called with the new instance.
+ # To add CSV data to generate CSV string, see CSV::Writer#<< or
+ # CSV::Writer#add_row.
+ #
+ def Writer.generate(str_or_writable, col_sep = ?,)
+ writer = Writer.create(str_or_writable, col_sep)
+ yield(writer)
+ writer.close
+ nil
+ end
+
+ # SYNOPSIS
+ # CSV::Writer#<<(row)
+ #
+ # ARGS
+ # row: an Array of a String.
+ #
+ # RETURNS
+ # self
+ #
+ # DESCRIPTION
+ # Dump CSV stream to the device. Argument is an array of a String like
+ # ['c1', 'c2', 'c3'].
+ #
+ def <<(ary)
+ row = ary.collect { |item|
+ if item.is_a?(Cell)
+ item
+ elsif (item.nil?)
+ Cell.new('', true)
+ else
+ Cell.new(item.to_s, false)
+ end
+ }
+ CSV.generate_row(row, row.size, @dev, @col_sep)
+ self
+ end
+
+ # SYNOPSIS
+ # CSV::Writer#<<(row)
+ #
+ # ARGS
+ # row: an Array of a CSV::Cell.
+ #
+ # RETURNS
+ # self
+ #
+ # DESCRIPTION
+ # Dump CSV stream to the device. Argument is an array of a CSV::Cell
+ # like [CSV::Cell.new('c1', false), CSV::Cell.new('dummy', true)].
+ # (Formar is 'c1' and latter is Null.)
+ #
+ def add_row(row)
+ CSV.generate_row(row, row.size, @dev, @col_sep)
+ self
+ end
+
+ # SYNOPSIS
+ # CSV::Writer#close
+ #
+ # RETURNS
+ # nil
+ #
+ # DESCRIPTION
+ # Close this writer.
+ #
+ 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
+
+
+ # DESCRIPTION
+ # CSV::BasicWriter -- CSV formatted string/stream writer using <<.
+ #
+ class BasicWriter < Writer
+ public
+
+ # SYNOPSIS
+ # writer = CSV::BasicWriter.new(str_or_writable)
+ #
+ # ARGS
+ # str_or_writable: device for generated CSV string. Must respond to
+ # '<<(string)'.
+ #
+ # RETURNS
+ # writer: Created instance.
+ #
+ # DESCRIPTION
+ # Create instance. To add CSV data to generate CSV string, see
+ # CSV::Writer#<< or CSV::Writer#add_row.
+ #
+ def initialize(str_or_writable, col_sep = ?,)
+ @col_sep = col_sep
+ @dev = str_or_writable
+ @close_on_terminate = false
+ end
+
+ # SYNOPSIS
+ # CSV::BasicWriter#close_on_terminate
+ #
+ # RETURNS
+ # true
+ #
+ # DESCRIPTION
+ # 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
+
+ # SYNOPSIS
+ # cells = CSV.parse_line(src, col_sep = ?,)
+ #
+ # ARGS
+ # src: a CSV String.
+ # col_sep: Column separator. ?, by default. If you want to separate
+ # fields with semicolon, give ?; here.
+ #
+ # RETURNS
+ # cells: an Array of parsed cells in first line. Each cell is a String.
+ #
+ # DESCRIPTION
+ # Parse one line from given string. Bare 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, col_sep = ?,)
+ idx = 0
+ res_type = :DT_COLSEP
+ cells = Row.new
+ begin
+ while (res_type.equal?(:DT_COLSEP))
+ cell = Cell.new
+ res_type, idx = parse_body(src, idx, cell, col_sep)
+ cells.push(cell.is_null ? nil : cell.data)
+ end
+ rescue IllegalFormatError
+ return Row.new
+ end
+ cells
+ end
+
+
+ # SYNOPSIS
+ # str = CSV.generate_line(cells, col_sep = ?,)
+ #
+ # ARGS
+ # cells: an Array of cell to be converted to CSV string. Each cell must
+ # respond to 'to_s'.
+ # col_sep: Column separator. ?, by default. If you want to separate
+ # fields with semicolon, give ?; here.
+ #
+ # RETURNS
+ # str: a String of generated CSV string.
+ #
+ # DESCRIPTION
+ # Create a line from cells. Each cell is stringified by to_s.
+ #
+ def CSV.generate_line(cells, col_sep = ?,)
+ if (cells.size == 0)
+ return ''
+ end
+ res_type = :DT_COLSEP
+ result_str = ''
+ idx = 0
+ while true
+ cell = if (cells[idx].nil?)
+ Cell.new('', true)
+ else
+ Cell.new(cells[idx].to_s, false)
+ end
+ generate_body(cell, result_str, col_sep)
+ idx += 1
+ if (idx == cells.size)
+ break
+ end
+ generate_separator(:DT_COLSEP, result_str, col_sep)
+ end
+ result_str
+ end
+
+ # SYNOPSIS
+ # parsed_cells, idx = CSV.parse_row(src, idx, out_dev, col_sep = ?,)
+ #
+ # 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 '<<(CSV::Cell)'.
+ # col_sep: Column separator. ?, by default. If you want to separate
+ # fields with semicolon, give ?; here.
+ #
+ # RETURNS
+ # parsed_cells: num of parsed cells.
+ # idx: index of next parsing location of 'src'.
+ #
+ # DESCRIPTION
+ # Parse a line from string. 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
+ #
+ def CSV.parse_row(src, idx, out_dev, col_sep = ?,)
+ idx_backup = idx
+ parsed_cells = 0
+ res_type = :DT_COLSEP
+ begin
+ while (!res_type.equal?(:DT_ROWSEP))
+ cell = Cell.new
+ res_type, idx = parse_body(src, idx, cell, col_sep)
+ if res_type.equal?(:DT_EOS)
+ if idx == idx_backup #((parsed_cells == 0) && (cell.is_null))
+ 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
+
+ # SYNOPSIS
+ # parsed_cells = CSV.generate_row(src, cells, out_dev, col_sep = ?,)
+ #
+ # ARGS
+ # src: an Array of CSV::Cell to be converted to CSV string. Must respond to
+ # 'size' and '[](idx)'. src[idx] must return CSV::Cell.
+ # 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.
+ #
+ # RETURNS
+ # parsed_cells: num of converted cells.
+ #
+ # DESCRIPTION
+ # Convert a line from cells data to string. To generate multi-row CSV
+ # string, See EXAMPLE below.
+ #
+ # EXAMPLE
+ # def d(str)
+ # CSV::Cell.new(str, false)
+ # end
+ #
+ # row1 = [d('a'), d('b')]
+ # row2 = [d('c'), d('d')]
+ # row3 = [d('e'), d('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
+ #
+ def CSV.generate_row(src, cells, out_dev, col_sep = ?,)
+ src_size = src.size
+ if (src_size == 0)
+ if cells == 0
+ generate_separator(:DT_ROWSEP, out_dev, col_sep)
+ end
+ return 0
+ end
+ res_type = :DT_COLSEP
+ parsed_cells = 0
+ generate_body(src[parsed_cells], out_dev, col_sep)
+ parsed_cells += 1
+ while ((parsed_cells < cells) && (parsed_cells != src_size))
+ generate_separator(:DT_COLSEP, out_dev, col_sep)
+ generate_body(src[parsed_cells], out_dev, col_sep)
+ parsed_cells += 1
+ end
+ if (parsed_cells == cells)
+ generate_separator(:DT_ROWSEP, out_dev, col_sep)
+ else
+ generate_separator(:DT_COLSEP, out_dev, col_sep)
+ end
+ parsed_cells
+ end
+
+private
+ class IllegalFormatError < RuntimeError; end
+
+ # Private class methods.
+ class << self
+ private
+
+ def parse_body(src, idx, cell, col_sep)
+ cell.is_null = false
+ state = :ST_START
+ quoted = false
+ cr = false
+ c = nil
+ while (c = src[idx])
+ idx += 1
+ result_state = :DT_UNKNOWN
+ if (c == col_sep)
+ if state.equal?(:ST_DATA)
+ if cr
+ raise IllegalFormatError.new
+ end
+ if (!quoted)
+ state = :ST_END
+ result_state = :DT_COLSEP
+ else
+ cell.data << c.chr
+ end
+ elsif state.equal?(:ST_QUOTE)
+ if cr
+ raise IllegalFormatError.new
+ end
+ state = :ST_END
+ result_state = :DT_COLSEP
+ else # :ST_START
+ cell.is_null = true
+ state = :ST_END
+ result_state = :DT_COLSEP
+ end
+ elsif (c == ?") # " for vim syntax hilighting.
+ if state.equal?(:ST_DATA)
+ if cr
+ raise IllegalFormatError.new
+ end
+ if quoted
+ quoted = false
+ state = :ST_QUOTE
+ else
+ raise IllegalFormatError.new
+ end
+ elsif state.equal?(:ST_QUOTE)
+ cell.data << c.chr
+ quoted = true
+ state = :ST_DATA
+ else # :ST_START
+ quoted = true
+ state = :ST_DATA
+ end
+ elsif (c == ?\r)
+ if cr
+ raise IllegalFormatError.new
+ end
+ if quoted
+ cell.data << c.chr
+ state = :ST_DATA
+ else
+ cr = true
+ end
+ elsif (c == ?\n)
+ if state.equal?(:ST_DATA)
+ if cr
+ state = :ST_END
+ result_state = :DT_ROWSEP
+ cr = false
+ else
+ if quoted
+ cell.data << c.chr
+ state = :ST_DATA
+ else
+ state = :ST_END
+ result_state = :DT_ROWSEP
+ end
+ end
+ elsif state.equal?(:ST_QUOTE)
+ state = :ST_END
+ result_state = :DT_ROWSEP
+ if cr
+ cr = false
+ end
+ else # :ST_START
+ cell.is_null = true
+ state = :ST_END
+ result_state = :DT_ROWSEP
+ end
+ else
+ if state.equal?(:ST_DATA) || state.equal?(:ST_START)
+ if cr
+ raise IllegalFormatError.new
+ end
+ cell.data << c.chr
+ state = :ST_DATA
+ else # :ST_QUOTE
+ raise IllegalFormatError.new
+ end
+ end
+ if state.equal?(:ST_END)
+ return result_state, idx;
+ end
+ end
+ if state.equal?(:ST_START)
+ cell.is_null = true
+ elsif state.equal?(:ST_QUOTE)
+ true # dummy for coverate; only a data
+ elsif quoted
+ raise IllegalFormatError.new
+ elsif cr
+ raise IllegalFormatError.new
+ end
+ return :DT_EOS, idx
+ end
+
+ def generate_body(cells, out_dev, col_sep)
+ row_data = cells.data.dup
+ if (!cells.is_null)
+ if (row_data.gsub!('"', '""') ||
+ row_data.include?(col_sep) ||
+ (/[\r\n]/ =~ row_data) || (cells.data.empty?))
+ out_dev << '"' << row_data << '"'
+ else
+ out_dev << row_data
+ end
+ end
+ end
+
+ def generate_separator(type, out_dev, col_sep)
+ case type
+ when :DT_COLSEP
+ out_dev << col_sep.chr
+ when :DT_ROWSEP
+ out_dev << "\r\n"
+ end
+ end
+ end
+
+
+ # DESCRIPTION
+ # CSV::StreamBuf -- a class for a bufferd 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 # pure virtual. (do not instanciate it directly)
+ public
+
+ # SYNOPSIS
+ # char/str = CSV::StreamBuf#get(idx, n = nil)
+ # char/str = CSV::StreamBuf#[idx, n = nil]
+ #
+ # ARGS
+ # 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: if n == nil. A char at idx.
+ # str: if n != nil. A partial string, from idx to (idx + size). At
+ # EOF, the string size could not equal to arg n.
+ #
+ # DESCRIPTION
+ # Get a char or a partial string from the stream.
+ #
+ 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 []
+
+ # SYNOPSIS
+ # size_dropped = CSV::StreamBuf#drop(n)
+ #
+ # ARGS
+ # n: drop size
+ #
+ # RETURNS
+ # size_dropped: droped size. At EOF, dropped size might not equals to arg n.
+ # 0 if n <= 0.
+ #
+ # DESCRIPTION
+ # Drop a string from the stream. 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 || (@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
+
+ # SYNOPSIS
+ # is_eos = CSV::StreamBuf#is_eos?
+ #
+ # RETURNS
+ # is_eos: true if end of the stream or false.
+ #
+ # DESCRIPTION
+ # Check EOF or not.
+ #
+ def is_eos?
+ return idx_is_eos?(0)
+ end
+
+ # SYNOPSIS
+ # N/A
+ #
+ # DESCRIPTION
+ # Do not instanciate 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 && ((@cur_buf < 0) || (@cur_buf == @buf_tail_idx)))
+ end
+
+ BufSize = 1024 * 8
+ end
+
+ # DESCRIPTION
+ # CSV::IOBuf -- a class for a bufferd 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
+ public
+ 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/lib/date.rb b/lib/date.rb
index 51cc95ee18..6dac752654 100644
--- a/lib/date.rb
+++ b/lib/date.rb
@@ -218,7 +218,7 @@ class Date
def self.new_with_hash(elem, sg)
elem ||= {}
- y, m, d = elem.select(:year, :mon, :mday)
+ y, m, d = elem.values_at(:year, :mon, :mday)
if [y, m, d].include? nil
raise ArgumentError, 'invalid date'
else
@@ -365,7 +365,7 @@ class Date
when Numeric; return @ajd <=> other
when Date; return @ajd <=> other.ajd
end
- raise TypeError, 'expected numeric or date'
+ nil
end
def === (other)
@@ -373,7 +373,7 @@ class Date
when Numeric; return jd == other
when Date; return jd == other.jd
end
- raise TypeError, 'expected numeric or date'
+ false
end
def >> (n)
@@ -476,7 +476,7 @@ class DateTime < Date
def self.new_with_hash(elem, sg)
elem ||= {}
y, m, d, h, min, s, of =
- elem.select(:year, :mon, :mday, :hour, :min, :sec, :offset)
+ elem.values_at(:year, :mon, :mday, :hour, :min, :sec, :offset)
h ||= 0
min ||= 0
s ||= 0
@@ -532,9 +532,9 @@ class Date
module_eval <<-"end;"
def self.#{old}(*args, &block)
if $VERBOSE
- $stderr.puts("\#{caller.shift.sub(/:in .*/, '')}: " \
- "warning: \#{self}::#{old} is deprecated; " \
- "use \#{self}::#{new}")
+ warn("\#{caller.shift.sub(/:in .*/, '')}: " \
+ "warning: \#{self}::#{old} is deprecated; " \
+ "use \#{self}::#{new}")
end
#{new}(*args, &block)
end
@@ -549,9 +549,9 @@ class Date
module_eval <<-"end;"
def #{old}(*args, &block)
if $VERBOSE
- $stderr.puts("\#{caller.shift.sub(/:in .*/, '')}: " \
- "warning: \#{self.class}\##{old} is deprecated; " \
- "use \#{self.class}\##{new}")
+ warn("\#{caller.shift.sub(/:in .*/, '')}: " \
+ "warning: \#{self.class}\##{old} is deprecated; " \
+ "use \#{self.class}\##{new}")
end
#{new}(*args, &block)
end
diff --git a/lib/date/format.rb b/lib/date/format.rb
index ac096f0751..7aa4747ecf 100644
--- a/lib/date/format.rb
+++ b/lib/date/format.rb
@@ -1,5 +1,5 @@
-# format.rb: Written by Tadayoshi Funaba 1999-2002
-# $Id: format.rb,v 2.8 2002-06-08 00:39:51+09 tadf Exp $
+# format.rb: Written by Tadayoshi Funaba 1999-2003
+# $Id: format.rb,v 2.9 2003-04-19 19:19:35+09 tadf Exp $
class Date
@@ -173,7 +173,7 @@ class Date
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, '')
+ return unless str.sub!(/\A([-+:a-z0-9]+(?:\s+dst\b)?)/io, '')
val = $1
elem[:zone] = val
offset = zone_to_diff(val)
@@ -321,12 +321,12 @@ class Date
end
# jis
- elsif str.sub!(/([MTSH])(\d+)\.(\d+)\.(\d+)/no, ' ')
- e = { 'M'=>1867,
- 'T'=>1911,
- 'S'=>1925,
- 'H'=>1988
- }[$1]
+ 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
@@ -379,7 +379,7 @@ class Date
)
\b
)?
- /nox,
+ /inox,
' ')
case $2.size
when 4
@@ -444,7 +444,7 @@ class Date
if ZONES.include?(abb)
offset = ZONES[abb]
offset += 3600 if dst
- elsif /\A([+-])(\d{2}):?(\d{2})?\Z/no =~ str
+ elsif /\A([-+])(\d{2}):?(\d{2})?\Z/no =~ str
offset = $2.to_i * 3600 + $3.to_i * 60
offset *= -1 if $1 == '-'
end
@@ -483,7 +483,7 @@ class Date
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(type.civil_to_jd(1970,1,1), 0)
+ 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
@@ -506,8 +506,8 @@ class Date
when '%%'; o << '%'
when '%+'; o << strftime('%a %b %e %H:%M:%S %Z %Y') # TZ
when '%1'; o << '%d' % jd
- when '%2'; o << strftime('%Y-%j')
- when '%3'; o << strftime('%Y-%m-%d')
+ when '%2'; o << strftime('%Y-%j')
+ when '%3'; o << strftime('%Y-%m-%d')
else; o << c
end
end
diff --git a/lib/debug.rb b/lib/debug.rb
index dc48f0de1c..9126a11b5f 100644
--- a/lib/debug.rb
+++ b/lib/debug.rb
@@ -1,5 +1,6 @@
# 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"
@@ -30,6 +31,7 @@ class Mutex
end
def lock
+ return if Thread.critical
return if @locker == Thread.current
while (Thread.critical = true; @locked)
@waiting.push Thread.current
@@ -42,6 +44,7 @@ class Mutex
end
def unlock
+ return if Thread.critical
return unless @locked
unless @locker == Thread.current
raise RuntimeError, "unlocked by other"
@@ -115,6 +118,7 @@ class Context
end
def check_suspend
+ return if Thread.critical
while (Thread.critical = true; @suspend_next)
DEBUGGER__.waiting.push Thread.current
@suspend_next = false
@@ -158,10 +162,9 @@ class Context
def debug_eval(str, binding)
begin
val = eval(str, binding)
- val
- rescue StandardError, ScriptError
- at = eval("caller(0)", binding)
- stdout.printf "%s:%s\n", at.shift, $!.to_s.sub(/\(eval\):1:(in `.*?':)?/, '') #`
+ 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
@@ -229,7 +232,7 @@ class Context
stdout.print "Should be Class/Module: ", input, "\n"
else
len = 0
- for v in obj.instance_methods.sort
+ for v in obj.instance_methods(false).sort
len += v.size + 1
if len > 70
len = v.size + 1
@@ -297,17 +300,24 @@ class Context
stdout.print "Trace off.\n"
end
- when /^\s*b(?:reak)?\s+(?:(.+):)?(.+)$/
+ when /^\s*b(?:reak)?\s+(?:(.+):)?([^.:]+)$/
pos = $2
- file = File.basename($1) if $1
+ file = $1 || file
+ klass = debug_silent_eval($1, binding)
if pos =~ /^\d+$/
pname = pos
pos = pos.to_i
else
pname = pos = pos.intern.id2name
end
- break_points.push [true, 0, file, pos]
- stdout.printf "Set breakpoint %d at %s:%s\n", break_points.size, file, pname
+ 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
@@ -529,7 +539,8 @@ class Context
stdout.print <<EOHELP
Debugger help v.-0.002b
Commands
- b[reak] [file:]<line|method>
+ 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] <an Exception> set catchpoint to an exception
@@ -644,16 +655,18 @@ EOHELP
end
end
- def check_break_points(file, pos, binding, id)
+ def check_break_points(file, klass, pos, binding, id)
return false if break_points.empty?
- file = File.basename(file)
n = 1
for b in break_points
- if b[0]
- if b[1] == 0 and b[2] == file and b[3] == pos
- stdout.printf "Breakpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos
- return true
- elsif b[1] == 1
+ 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
@@ -695,12 +708,13 @@ EOHELP
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, line, binding, id)
+ if @stop_next == 0 or check_break_points(file, nil, line, binding, id)
@no_step = nil
suspend_all
debug_command(file, line, id, binding)
@@ -708,8 +722,7 @@ EOHELP
when 'call'
@frames.unshift [binding, file, line, id]
- if check_break_points(file, id.id2name, binding, id) or
- check_break_points(klass.to_s, id.id2name, binding, id)
+ if check_break_points(file, klass, id.id2name, binding, id)
suspend_all
debug_command(file, line, id, binding)
end
@@ -769,12 +782,13 @@ class << DEBUGGER__
end
def set_trace( arg )
+ saved_crit = Thread.critical
Thread.critical = true
make_thread_list
- for th in @thread_list
- context(th[0]).set_trace arg
+ for th, in @thread_list
+ context(th).set_trace arg
end
- Thread.critical = false
+ Thread.critical = saved_crit
arg
end
@@ -783,29 +797,31 @@ class << DEBUGGER__
end
def suspend
+ saved_crit = Thread.critical
Thread.critical = true
make_thread_list
- for th in @thread_list
- next if th[0] == Thread.current
- context(th[0]).set_suspend
+ for th, in @thread_list
+ next if th == Thread.current
+ context(th).set_suspend
end
- Thread.critical = false
+ Thread.critical = saved_crit
# Schedule other threads to suspend as soon as possible.
- Thread.pass
+ 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[0] == Thread.current
- context(th[0]).clear_suspend
+ 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 = false
+ Thread.critical = saved_crit
# Schedule other threads to restart as soon as possible.
Thread.pass
end
diff --git a/lib/delegate.rb b/lib/delegate.rb
index 707bc2144b..651164e274 100644
--- a/lib/delegate.rb
+++ b/lib/delegate.rb
@@ -19,12 +19,12 @@
class Delegator
def initialize(obj)
- preserved = ::Kernel.public_instance_methods
+ preserved = ::Kernel.public_instance_methods(false)
preserved -= ["to_s","to_a","inspect","==","=~","==="]
for t in self.class.ancestors
- preserved |= t.public_instance_methods
- preserved |= t.private_instance_methods
- preserved |= t.protected_instance_methods
+ preserved |= t.public_instance_methods(false)
+ preserved |= t.private_instance_methods(false)
+ preserved |= t.protected_instance_methods(false)
break if t == Delegator
end
for method in obj.methods
@@ -48,7 +48,7 @@ class Delegator
end
def __getobj__
- raise NotImplementError, "need to define `__getobj__'"
+ raise NotImplementedError, "need to define `__getobj__'"
end
end
@@ -77,7 +77,7 @@ SimpleDelegater = SimpleDelegator
def DelegateClass(superclass)
klass = Class.new
methods = superclass.public_instance_methods(true)
- methods -= ::Kernel.public_instance_methods
+ methods -= ::Kernel.public_instance_methods(false)
methods |= ["to_s","to_a","inspect","==","=~","==="]
klass.module_eval <<-EOS
def initialize(obj)
diff --git a/lib/drb.rb b/lib/drb.rb
new file mode 100644
index 0000000000..93cc811e14
--- /dev/null
+++ b/lib/drb.rb
@@ -0,0 +1,2 @@
+require 'drb/drb'
+
diff --git a/lib/drb/drb.rb b/lib/drb/drb.rb
new file mode 100644
index 0000000000..e6ea35cfc1
--- /dev/null
+++ b/lib/drb/drb.rb
@@ -0,0 +1,807 @@
+=begin
+= distributed Ruby --- dRuby 2.0.4
+ Copyright (c) 1999-2003 Masatoshi SEKI
+ You can redistribute it and/or modify it under the same terms as Ruby.
+=end
+
+require 'socket'
+require 'thread'
+require 'fcntl'
+
+module DRb
+ class DRbError < RuntimeError; end
+ class DRbConnError < DRbError; end
+
+ class DRbIdConv
+ def to_obj(ref)
+ ObjectSpace._id2ref(ref)
+ end
+
+ def to_id(obj)
+ obj.nil? ? nil : obj.__id__
+ end
+ end
+
+ module DRbUndumped
+ def _dump(dummy)
+ raise TypeError, 'can\'t dump'
+ end
+ end
+
+ class DRbServerNotFound < DRbError; end
+ class DRbBadURI < DRbError; end
+ class DRbBadScheme < DRbError; end
+
+ class DRbUnknownError < DRbError
+ def initialize(unknown)
+ @unknown = unknown
+ super(unknown.name)
+ end
+ attr_reader :unknown
+
+ def self._load(s)
+ Marshal::load(s)
+ end
+
+ def _dump(lv)
+ Marshal::dump(@unknown)
+ end
+ end
+
+ class DRbUnknown
+ def initialize(err, buf)
+ case err
+ when /uninitialized constant (\S+)/
+ @name = $1
+ when /undefined class\/module (\S+)/
+ @name = $1
+ else
+ @name = nil
+ end
+ @buf = buf
+ end
+ attr_reader :name, :buf
+
+ def self._load(s)
+ begin
+ Marshal::load(s)
+ rescue NameError, ArgumentError
+ DRbUnknown.new($!, s)
+ end
+ end
+
+ def _dump(lv)
+ @buf
+ end
+
+ def reload
+ self.class._load(@buf)
+ end
+
+ def exception
+ DRbUnknownError.new(self)
+ end
+ end
+
+ class DRbMessage
+ def initialize(config)
+ @load_limit = config[:load_limit]
+ @argc_limit = config[:argc_limit]
+ end
+
+ def dump(obj)
+ obj = DRbObject.new(obj) if obj.kind_of? DRbUndumped
+ begin
+ str = Marshal::dump(obj)
+ rescue
+ str = Marshal::dump(DRbObject.new(obj))
+ end
+ [str.size].pack('N') + str
+ end
+
+ def load(soc)
+ sz = soc.read(4) # sizeof (N)
+ 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
+ str = soc.read(sz)
+ raise(DRbConnError, 'connection closed') if sz.nil?
+ raise(DRbConnError, 'premature marshal format(can\'t read)') if str.size < sz
+ begin
+ Marshal::load(str)
+ rescue NameError, ArgumentError
+ DRbUnknown.new($!, str)
+ end
+ end
+
+ def send_request(stream, ref, msg_id, arg, b)
+ 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(''))
+ end
+
+ def recv_request(stream)
+ 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)
+ stream.write(dump(succ) + dump(result))
+ end
+
+ def recv_reply(stream)
+ succ = load(stream)
+ result = load(stream)
+ [succ, result]
+ end
+ end
+
+ module DRbProtocol
+ module_function
+ def add_protocol(prot)
+ @protocol.push(prot)
+ end
+
+ module_function
+ 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
+ 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
+ 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
+ def auto_load(uri, config)
+ if uri =~ /^drb([a-z0-9]+):/
+ require("drb/#{$1}") rescue nil
+ end
+ end
+ end
+
+ 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
+ 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.open_server(uri, config)
+ uri = 'druby://:0' unless uri
+ host, port, opt = parse_uri(uri)
+ if host.size == 0
+ soc = TCPServer.open(port)
+ host = Socket.gethostname
+ else
+ soc = TCPServer.open(host, port)
+ end
+ port = soc.addr[1] if port == 0
+ uri = "druby://#{host}:#{port}"
+ self.new(uri, soc, config)
+ end
+
+ def self.uri_option(uri, config)
+ host, port, option = parse_uri(uri)
+ return "druby://#{host}:#{port}", option
+ end
+
+ def initialize(uri, soc, config={})
+ @uri = uri
+ @socket = soc
+ @config = config
+ @acl = config[:tcp_acl]
+ @msg = DRbMessage.new(config)
+ set_sockopt(@socket)
+ end
+ attr_reader :uri
+
+ def peeraddr
+ @socket.peeraddr
+ end
+
+ def stream; @socket; end
+
+ def send_request(ref, msg_id, arg, b)
+ @msg.send_request(stream, ref, msg_id, arg, b)
+ end
+
+ def recv_request
+ @msg.recv_request(stream)
+ end
+
+ def send_reply(succ, result)
+ @msg.send_reply(stream, succ, result)
+ end
+
+ def recv_reply
+ @msg.recv_reply(stream)
+ end
+
+ public
+ def close
+ if @socket
+ @socket.close
+ @socket = nil
+ end
+ end
+
+ 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
+
+ def alive?
+ return false unless @socket
+ if IO.select([@socket], nil, nil, 0)
+ close
+ return false
+ end
+ true
+ end
+
+ def set_sockopt(soc)
+ soc.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
+ soc.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK) if defined? Fcntl::O_NONBLOCK
+ soc.fcntl(Fcntl::F_SETFL, Fcntl::FD_CLOEXEC) if defined? Fcntl::FD_CLOEXEC
+ end
+ end
+
+ module DRbProtocol
+ @protocol = [DRbTCPSocket] # default
+ end
+
+ class DRbURIOption
+ 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
+
+ class DRbObject
+ def self._load(s)
+ uri, ref = Marshal.load(s)
+ if DRb.here?(uri)
+ return DRb.to_obj(ref)
+ end
+
+ it = self.new(nil)
+ it.reinit(uri, ref)
+ it
+ end
+
+ def self.new_with_uri(uri)
+ self.new(nil, uri)
+ end
+
+ def _dump(lv)
+ Marshal.dump([@uri, @ref])
+ end
+
+ 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
+
+ def reinit(uri, ref)
+ @uri = uri
+ @ref = ref
+ end
+
+ def __drburi
+ @uri
+ end
+
+ def __drbref
+ @ref
+ end
+
+ undef :to_s
+ undef :to_a
+ undef :respond_to?
+
+ 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 = DRbConn.open(@uri) do |conn|
+ conn.send_message(self, msg_id, a, b)
+ end
+ return result if succ
+ unless DRbUnknown === 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
+ raise result, result.message, bt + caller
+ else
+ raise result
+ end
+ end
+ end
+
+ class DRbConn
+ POOL_SIZE = 16
+ @mutex = Mutex.new
+ @pool = []
+
+ def self.open(remote_uri)
+ 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
+ @mutex.synchronize do
+ if @pool.size > POOL_SIZE or ! succ
+ conn.close if conn
+ else
+ @pool.unshift(conn)
+ end
+ end
+ end
+ end
+
+ def initialize(remote_uri)
+ @uri = remote_uri
+ @protocol = DRbProtocol.open(remote_uri, DRb.config)
+ end
+ attr_reader :uri
+
+ def send_message(ref, msg_id, arg, block)
+ @protocol.send_request(ref, msg_id, arg, block)
+ @protocol.recv_reply
+ end
+
+ def close
+ @protocol.close
+ @protocol = nil
+ end
+
+ def alive?
+ @protocol.alive?
+ end
+ end
+
+ class DRbServer
+ @@acl = nil
+ @@idconv = DRbIdConv.new
+ @@secondary_server = nil
+ @@argc_limit = 256
+ @@load_limit = 256 * 102400
+ @@verbose = false
+
+ def self.default_argc_limit(argc)
+ @@argc_limit = argc
+ end
+
+ def self.default_load_limit(sz)
+ @@load_limit = sz
+ end
+
+ def self.default_acl(acl)
+ @@acl = acl
+ end
+
+ def self.default_id_conv(idconv)
+ @@idconv = idconv
+ end
+
+ def self.verbose=(on)
+ @@verbose = on
+ end
+
+ def self.verbose
+ @@verbose
+ end
+
+ def self.make_config(hash={})
+ default_config = {
+ :idconv => @@idconv,
+ :verbose => @@verbose,
+ :tcp_acl => @@acl,
+ :load_limit => @@load_limit,
+ :argc_limit => @@argc_limit
+ }
+ default_config.update(hash)
+ end
+
+ 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]
+
+ @grp = ThreadGroup.new
+ @thread = run
+
+ Thread.exclusive do
+ DRb.primary_server = self unless DRb.primary_server
+ end
+ end
+ attr_reader :uri, :thread, :front
+ attr_reader :config
+
+ def verbose=(v); @config[:verbose]=v; end
+ def verbose; @config[:verbose]; end
+
+ def alive?
+ @thread.alive?
+ end
+
+ def stop_service
+ @thread.kill
+ end
+
+ def to_obj(ref)
+ return front if ref.nil?
+ return front[ref.to_s] if DRbURIOption === ref
+ @idconv.to_obj(ref)
+ end
+
+ 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
+
+ INSECURE_METHOD = [
+ :__send__
+ ]
+ def insecure_method?(msg_id)
+ INSECURE_METHOD.include?(msg_id)
+ end
+
+ def any_to_s(obj)
+ obj.to_s rescue sprintf("#<%s:0x%lx>", obj.class, obj.__id__)
+ end
+
+ 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)
+ unless obj.respond_to?(msg_id)
+ desc = any_to_s(obj)
+ if desc.nil? || desc[0] == '#'
+ desc << ":#{obj.class}"
+ end
+
+ if obj.private_methods.include?(msg_id.to_s)
+ raise NameError, "private method `#{msg_id}' called for #{desc}"
+ else
+ raise NameError, "undefined method `#{msg_id}' called for #{desc}"
+ end
+ end
+ true
+ end
+ public :check_insecure_method
+
+ class InvokeMethod
+ def initialize(drb_server, client)
+ @drb_server = drb_server
+ @client = client
+ end
+
+ def perform
+ @result = nil
+ @succ = false
+ setup_message
+ if @block
+ @result = perform_with_block
+ else
+ @result = perform_without_block
+ end
+ @succ = true
+ 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
+
+ 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
+ unless succ
+ client.close
+ return
+ end
+ end
+ end
+ end
+ end
+ end
+
+ @primary_server = nil
+
+ def start_service(uri=nil, front=nil, config=nil)
+ @primary_server = DRbServer.new(uri, front, config)
+ end
+ module_function :start_service
+
+ attr_accessor :primary_server
+ module_function :primary_server=, :primary_server
+
+ 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
+
+ def stop_service
+ @primary_server.stop_service if @primary_server
+ @primary_server = nil
+ end
+ module_function :stop_service
+
+ def uri
+ current_server.uri
+ end
+ module_function :uri
+
+ def here?(uri)
+ (current_server.uri rescue nil) == uri
+ end
+ module_function :here?
+
+ def config
+ current_server.config
+ rescue
+ DRbServer.make_config
+ end
+ module_function :config
+
+ def front
+ current_server.front
+ end
+ module_function :front
+
+ def to_obj(ref)
+ current_server.to_obj(ref)
+ end
+ def to_id(obj)
+ current_server.to_id(obj)
+ end
+ module_function :to_id
+ module_function :to_obj
+
+ def thread
+ @primary_server ? @primary_server.thread : nil
+ end
+ module_function :thread
+
+ def install_id_conv(idconv)
+ DRbServer.default_id_conv(idconv)
+ end
+ module_function :install_id_conv
+
+ def install_acl(acl)
+ DRbServer.default_acl(acl)
+ end
+ module_function :install_acl
+end
+
+DRbObject = DRb::DRbObject
+DRbUndumped = DRb::DRbUndumped
+DRbIdConv = DRb::DRbIdConv
diff --git a/lib/drb/eq.rb b/lib/drb/eq.rb
new file mode 100644
index 0000000000..e24512d6a7
--- /dev/null
+++ b/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/lib/drb/extserv.rb b/lib/drb/extserv.rb
new file mode 100644
index 0000000000..f05bae5264
--- /dev/null
+++ b/lib/drb/extserv.rb
@@ -0,0 +1,67 @@
+=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
+ Thread.new do
+ sleep 1
+ server.stop_service
+ end
+ 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::ExtService.new(ARGV.shift, ARGV.shift, manager)
+ es.server.thread.join
+ end
+end
+
diff --git a/lib/drb/extservm.rb b/lib/drb/extservm.rb
new file mode 100644
index 0000000000..50009dbae2
--- /dev/null
+++ b/lib/drb/extservm.rb
@@ -0,0 +1,94 @@
+=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
+ end
+ attr_reader :server
+
+ 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
+ if RUBY_PLATFORM =~ /mswin32/
+ system("cmd /c start /b #{command} #{DRb.uri} #{name}")
+ else
+ system("#{command} #{DRb.uri} #{name} &")
+ end
+ end
+ end
+end
diff --git a/lib/drb/gw.rb b/lib/drb/gw.rb
new file mode 100644
index 0000000000..012a2b0a11
--- /dev/null
+++ b/lib/drb/gw.rb
@@ -0,0 +1,60 @@
+require 'drb/drb'
+require 'monitor'
+
+module DRb
+ class GWIdConv < DRbIdConv
+ def to_obj(ref)
+ if Array === ref && ref[0] == :DRbObject
+ it = DRbObject.new(nil)
+ it.reinit(ref[1], ref[2])
+ return it
+ 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
+
+ it = self.new(nil)
+ it.reinit(DRb.uri, [:DRbObject, uri, ref])
+ it
+ 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
diff --git a/lib/drb/invokemethod.rb b/lib/drb/invokemethod.rb
new file mode 100644
index 0000000000..6d6c9562c3
--- /dev/null
+++ b/lib/drb/invokemethod.rb
@@ -0,0 +1,33 @@
+# for ruby-1.8.0
+
+module DRb
+ class DRbServer
+ module InvokeMethod18Mixin
+ def block_yield(x)
+ 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/lib/drb/observer.rb b/lib/drb/observer.rb
new file mode 100644
index 0000000000..e7f1668c52
--- /dev/null
+++ b/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/lib/drb/timeridconv.rb b/lib/drb/timeridconv.rb
new file mode 100644
index 0000000000..bb2c48d528
--- /dev/null
+++ b/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/lib/drb/unix.rb b/lib/drb/unix.rb
new file mode 100644
index 0000000000..e92a7a88ca
--- /dev/null
+++ b/lib/drb/unix.rb
@@ -0,0 +1,106 @@
+require 'socket'
+require 'drb/drb'
+require 'tmpdir'
+
+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
+ 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::O_NONBLOCK) if defined? Fcntl::O_NONBLOCK
+ soc.fcntl(Fcntl::F_SETFL, Fcntl::FD_CLOEXEC) if defined? Fcntl::FD_CLOEXEC
+ end
+ end
+
+ DRbProtocol.add_protocol(DRbUNIXSocket)
+end
diff --git a/lib/erb.rb b/lib/erb.rb
index f7925db697..1b712c92c2 100644
--- a/lib/erb.rb
+++ b/lib/erb.rb
@@ -1,169 +1,382 @@
# Tiny eRuby --- ERB2
-# Copyright (c) 1999-2000,2002 Masatoshi SEKI
+# Copyright (c) 1999-2000,2002,2003 Masatoshi SEKI
# You can redistribute it and/or modify it under the same terms as Ruby.
class ERB
Revision = '$Date$' #'
def self.version
- "erb.rb [2.0.1 #{ERB::Revision.split[1]}]"
+ "erb.rb [2.0.4 #{ERB::Revision.split[1]}]"
end
end
# ERB::Compiler
class ERB
class Compiler
- ERbTag = "<%% %%> <%= <%# <% %>".split
- private
- def is_erb_tag?(s)
- ERbTag.member?(s)
+ class PercentLine
+ def initialize(str)
+ @value = str
+ end
+ attr_reader :value
+ alias :to_s :value
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, '>']
+ class Scanner
+ 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
+ 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
- [perc, nil]
+ @scan_line = self.method(:scan_line)
end
- else
- return [false, nil]
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
- SplitRegexp = /(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>)|(\n)/
+ def percent_line(line, &block)
+ if @stag || line[0] != ?%
+ return @scan_line.call(line, &block)
+ end
- public
- def pre_compile(s, trim_mode)
- perc, trim_mode = prepare_trim_mode(trim_mode)
- re = SplitRegexp
- if trim_mode.nil? && !perc
- list = s.split(re)
- else
- list = []
- has_cr = (s[-1] == ?\n)
- s.each do |line|
- line = line.chomp
- if perc && (/^(%{1,2})/ =~ line)
- line[0] = ''
- if $1 == '%%'
- list.push(line)
- list.push("\n")
+ 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
- list.push('<%')
- list.push(line)
- list.push('%>')
+ 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
- line = line.split(re)
- line.shift if line[0]==''
- list += line
- unless ((trim_mode == '>' && line[-1] == '%>') ||
- (trim_mode == '<>' && (is_erb_tag?(line[0])) &&
- line[-1] == '%>'))
- list.push("\n")
- end
+ yield(token)
end
end
- list.pop unless has_cr
end
- list
+
+ ERB_STAG = %w(<%= <%# <%)
+ def is_erb_stag?(s)
+ ERB_STAG.member?(s)
+ end
end
- def compile(s)
- list = pre_compile(s, @trim_mode)
- cmd = []
- cmd.concat(@pre_cmd)
-
- stag = nil
- content = []
- while (token = list.shift)
- if token == '<%%'
- token = '<'
- list.unshift '%'
- elsif token == '%%>'
- token = '%'
- list.unshift '>'
+ Scanner.default_scanner = TrimScanner
+
+ class SimpleScanner < Scanner
+ def scan
+ @src.each do |line|
+ line.split(SplitRegexp).each do |token|
+ next if token.empty?
+ yield(token)
+ end
end
- if stag.nil?
- if ['<%', '<%=', '<%#'].include?(token)
- stag = token
- str = content.join
- if str.size > 0
- cmd.push("#{@put_cmd} #{str.dump}")
+ end
+ end
+
+ Scanner.regist_scanner(SimpleScanner, nil, false)
+
+ begin
+ require 'strscan'
+ class SimpleScanner2 < Scanner
+ 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
+ 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
+ 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
- content = []
- elsif token == "\n"
- content.push("\n")
- cmd.push("#{@put_cmd} #{content.join.dump}")
- cmd.push(:cr)
- content = []
+ 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
+ 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.push(token)
+ content << token
end
else
- if token == '%>'
- case stag
+ case token
+ when '%>'
+ case scanner.stag
when '<%'
- str = content.join
- if str[-1] == ?\n
- str.chop!
- cmd.push(str)
- cmd.push(:cr)
+ if content[-1] == ?\n
+ content.chop!
+ out.push(content)
+ out.cr
else
- cmd.push(str)
+ out.push(content)
end
when '<%='
- cmd.push("#{@put_cmd}((#{content.join}).to_s)")
+ out.push("#{@put_cmd}((#{content}).to_s)")
when '<%#'
- # cmd.push("# #{content.dump}")
+ # out.push("# #{content.dump}")
end
- stag = nil
- content = []
+ scanner.stag = nil
+ content = ''
+ when '%%>'
+ content << '%>'
else
- content.push(token)
+ content << token
end
end
end
- if content.size > 0
- cmd.push("#{@put_cmd} #{content.join.dump}")
- end
- cmd.push(:cr)
- cmd.concat(@post_cmd)
-
- ary = []
- cmd.each do |x|
- if x == :cr
- ary.pop
- ary.push("\n")
+ 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
- ary.push(x)
- ary.push('; ')
+ [perc, nil]
end
+ else
+ return [false, nil]
end
- ary.join
end
- def initialize
- @trim_mode = nil
+ 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'
@pre_cmd = []
@post_cmd = []
end
- attr :trim_mode, true
- attr :put_cmd, true
- attr :pre_cmd, true
- attr :post_cmd, true
+ attr_reader :percent, :trim_mode
+ attr_accessor :put_cmd, :pre_cmd, :post_cmd
end
end
@@ -171,8 +384,7 @@ end
class ERB
def initialize(str, safe_level=nil, trim_mode=nil, eoutvar='_erbout')
@safe_level = safe_level
- compiler = ERB::Compiler.new
- compiler.trim_mode = trim_mode
+ compiler = ERB::Compiler.new(trim_mode)
set_eoutvar(compiler, eoutvar)
@src = compiler.compile(str)
end
@@ -249,10 +461,11 @@ class 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
- erb.def_method(self, methodname, fname)
end
module_function :def_erb_method
end
end
-
diff --git a/lib/fileutils.rb b/lib/fileutils.rb
index dcdd620c4a..d6714229a9 100644
--- a/lib/fileutils.rb
+++ b/lib/fileutils.rb
@@ -1,275 +1,123 @@
-=begin
+#
+# = fileutils.rb
+#
+# Copyright (c) 2000-2003 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_p( dir, options )
+# rmdir( dir, 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 )
+# touch( list, options )
+#
+# The <tt>options</tt> parameter is a hash of options, taken from the list
+# +:force+, +:noop+, +:preserve+, and +:verbose+. +:noop+ 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 does not accept any option:
+#
+# uptodate?( file, cmp_list )
+# copy_file( srcfilename, destfilename )
+# copy_stream( srcstream, deststream )
+# compare_file( file_a, file_b )
+# compare_stream( stream_a, stream_b )
+#
+# == module FileUtils::Verbose
+#
+# This module has all methods of FileUtils module, but it outputs messages
+# before acting. This equates to passing the +:verbose+ 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 +:noop+ flag to methods in
+# FileUtils.
+#
-= fileutils.rb
- Copyright (c) 2000-2002 Minero Aoki <aamine@loveruby.net>
-
- This program is free software.
- You can distribute/modify this program under the same terms of ruby.
-
-== module FileUtils
-
-The module which implements basic file operations.
-
-=== Module Functions
-
---- FileUtils.cd( dir, *options )
---- FileUtils.cd( dir, *options ) {|dir| .... }
- Options: noop 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 # chdir and report it
-
---- FileUtils.uptodate?( newer, older_list, *options )
- Options: verbose
-
- returns true if NEWER is newer than all OLDER_LIST.
- Non-exist files are older than any file.
-
- FileUtils.newest? 'hello.o', 'hello.c', 'hello.h' or system 'make'
-
---- FileUtils.mkdir( dir, *options )
- Options: noop verbose
-
- creates directorie(s) DIR.
-
- FileUtils.mkdir 'test'
- FileUtils.mkdir %w( tmp data )
- FileUtils.mkdir 'notexist', :noop # does not create really
-
---- FileUtils.mkdir_p( dir, *options )
- Options: noop verbose
-
- makes dirctories DIR and all its parent directories.
- For example,
-
- FileUtils.mkdir_p '/usr/local/bin/ruby'
-
- causes to make following directories (if it does not exist).
- * /usr
- * /usr/local
- * /usr/local/bin
- * /usr/local/bin/ruby
-
---- FileUtils.rmdir( dir, *options )
- Options: noop, verbose
-
- removes directory DIR.
-
- FileUtils.rmdir 'somedir'
- FileUtils.rmdir %w(somedir anydir otherdir)
- # does not remove directory really, outputing message.
- FileUtils.rmdir 'somedir', :verbose, :noop
-
---- FileUtils.ln( old, new, *options )
- Options: force noop verbose
-
- creates a hard link NEW which points 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 'gcc', 'cc', :verbose
- FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs'
-
---- FileUtils.ln( list, destdir, *options )
- Options: force noop verbose
-
- creates hard links DESTDIR/LIST[0], DESTDIR/LIST[1], DESTDIR/LIST[2], ...
- And each link points LIST[0], LIST[1], LIST[2], ...
- If DESTDIR is not a directory, raises Errno::ENOTDIR.
-
- include FileUtils
- cd '/bin'
- ln %w(cp mv mkdir), '/usr/bin'
-
---- FileUtils.ln_s( old, new, *options )
- Options: force noop verbose
-
- creates a symbolic link NEW which points 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
-
---- FileUtils.ln_s( list, destdir, *options )
- Options: force noop verbose
-
- creates symbolic link dir/file1, dir/file2 ... which point to
- file1, file2 ... If DIR is not a directory, raises Errno::ENOTDIR.
- If last argument is a directory, links DIR/LIST[0] to LIST[0],
- DIR/LIST[1] to LIST[1], ....
- creates symbolic links DESTDIR/LIST[0] which points LIST[0].
- DESTDIR/LIST[1] to LIST[1], ....
- If DESTDIR is not a directory, raises Errno::ENOTDIR.
-
- FileUtils.ln_s Dir.glob('bin/*.rb'), '/home/aamine/bin'
-
---- FileUtils.ln_sf( src, dest, *options )
- Options: noop verbose
-
- same to ln_s(src,dest,:force)
-
---- FileUtils.cp( src, dest, *options )
- Options: preserve noop verbose
-
- copies a file SRC to DEST. If DEST is a directory, copies
- SRC to DEST/SRC.
-
- FileUtils.cp 'eval.c', 'eval.c.org'
-
---- FileUtils.cp( list, dir, *options )
- Options: preserve noop verbose
-
- copies FILE1 to DIR/FILE1, FILE2 to DIR/FILE2 ...
-
- FileUtils.cp 'cgi.rb', 'complex.rb', 'date.rb', '/usr/lib/ruby/1.6'
- FileUtils.cp :verbose, %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6'
-
---- FileUtils.cp_r( src, dest, *options )
- Options: preserve noop verbose
-
- copies SRC to DEST. If SRC is a directory, this method copies
- its all contents recursively. If DEST is a directory, copies
- SRC to DEST/SRC.
-
- # installing ruby library "mylib" under the site_ruby
- FileUtils.rm_r site_ruby + '/mylib', :force
- FileUtils.cp_r 'lib/', site_ruby + '/mylib'
-
---- FileUtils.cp_r( list, dir, *options )
- Options: preserve noop verbose
-
- copies a file or a directory LIST[0] to DIR/LIST[0], LIST[1] to DIR/LIST[1], ...
- If LIST[n] is a directory, copies its contents recursively.
-
- FileUtils.cp_r %w(mail.rb field.rb debug/) site_ruby + '/tmail'
- FileUtils.cp_r Dir.glob('*.rb'), '/home/aamine/lib/ruby', :noop, :verbose
-
---- FileUtils.mv( src, dest, *options )
- Options: noop verbose
-
- moves a file SRC to DEST.
- If FILE and DEST exist on the different disk partition,
- copies it.
-
- FileUtils.mv 'badname.rb', 'goodname.rb'
- FileUtils.mv 'stuff.rb', 'lib/ruby', :force
-
---- FileUtils.mv( list, dir, *options )
- Options: noop verbose
-
- moves FILE1 to DIR/FILE1, FILE2 to DIR/FILE2 ...
- If FILE and DEST exist on the different disk partition,
- copies it.
-
- FileUtils.mv 'junk.txt', 'dust.txt', '/home/aamine/.trash/'
- FileUtils.mv Dir.glob('test*.rb'), 'T', :noop, :verbose
-
---- FileUtils.rm( list, *options )
- Options: force noop verbose
-
- remove files LIST[0], LIST[1]... This method cannot remove directory.
- This method ignores all errors when :force option is set.
-
- FileUtils.rm %w( junk.txt dust.txt )
- FileUtils.rm Dir['*.so']
- FileUtils.rm 'NotExistFile', :force # never raises exception
-
---- FileUtils.rm_r( list, *options )
- Options: force noop verbose
-
- 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 # :-)
-
---- FileUtils.rm_rf( list, *options )
- Options: noop verbose
-
- same to rm_r(list,:force)
-
---- FileUtils.cmp( file_a, file_b, *options )
- Options: verbose
-
- returns true if contents of a file A and a file B is identical.
-
- FileUtils.cmp 'somefile', 'somefile' #=> true
- FileUtils.cmp '/bin/cp', '/bin/mv' #=> maybe false.
-
---- FileUtils.install( src, dest, mode = <src's>, *options )
- Options: noop verbose
-
- If SRC is not same to DEST, copies it and changes the permittion
- mode to MODE.
-
- FileUtils.install 'ruby', '/usr/local/bin/ruby', 0755, :verbose
- FileUtils.install 'lib.rb', '/usr/local/lib/ruby/site_ruby', :verbose
-
---- FileUtils.chmod( mode, list, *options )
- Options: noop verbose
-
- changes permittion bits on the named FILEs to the bit pattern
- represented by MODE.
-
- FileUtils.chmod 0644, 'my.rb', 'your.rb'
- FileUtils.chmod 0755, 'somecommand'
- FileUtils.chmod 0755, '/usr/bin/ruby', :verbose
-
---- FileUtils.touch( list, *options )
- Options: noop verbose
-
- updates modification time (mtime) and access time (atime) of
- LIST[0], LIST[1]...
- If LIST[n] does not exist, creates an empty file.
-
- FileUtils.touch 'timestamp'
- FileUtils.touch Dir.glob('*.c'); system 'make'
-
-== module FileUtils::Verbose
-
-This class has all methods of FileUtils module and it works as
-same, but outputs messages before action. You can also pass
-verbose flag to all methods.
-
-== module FileUtils::NoWrite
-
-This class has all methods of FileUtils module,
-but never changes files/directories.
-
-=end
+module FileUtils
+ # All methods are module_function.
-module FileUtils
+ #
+ # Options: (none)
+ #
+ # Returns the name of the current directory.
+ #
+ def pwd
+ Dir.pwd
+ end
- # all methods are module_function.
+ alias getwd pwd
- def cd( dir, *options, &block )
- noop, verbose, = fu_parseargs(options, :noop, :verbose)
- fu_output_message "cd #{dir}" if verbose
- Dir.chdir dir, &block unless noop
- fu_output_message 'cd -' if verbose and block
+ #
+ # Options: noop 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, :noop, :verbose
+ fu_output_message "cd #{dir}" if options[:verbose]
+ Dir.chdir(dir, &block) unless options[:noop]
+ fu_output_message 'cd -' if options[:verbose] and block
end
alias chdir cd
- def uptodate?( new, *args )
- verbose, = fu_parseargs(args, :verbose)
- fu_output_message "newest? #{args.join ' '}" if verbose
-
- return false unless FileTest.exist? new
- new_time = File.ctime(new)
- args.each do |old|
- if FileTest.exist? old
+ #
+ # 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 FileTest.exist?(new)
+ new_time = File.mtime(new)
+ old_list.each do |old|
+ if FileTest.exist?(old)
return false unless new_time > File.mtime(old)
end
end
@@ -277,31 +125,59 @@ module FileUtils
end
- def mkdir( list, *options )
- noop, verbose, = fu_parseargs(options, :noop, :verbose)
+ #
+ # 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 #{list.join ' '}" if verbose
- return if noop
+ fu_output_message "mkdir #{options[:mode] ? ('-m %03o ' % options[:mode]) : ''}#{list.join ' '}" if options[:verbose]
+ return if options[:noop]
+ mode = options[:mode] || (0777 & ~File.umask)
list.each do |dir|
- Dir.mkdir dir
+ Dir.mkdir dir, mode
end
end
- def mkdir_p( list, *options )
- noop, verbose, = fu_parseargs(options, :noop, :verbose)
+ #
+ # 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 #{list.join ' '}" if verbose
- return *list if noop
+ fu_output_message "mkdir -p #{options[:mode] ? ('-m %03o ' % options[:mode]) : ''}#{list.join ' '}" if options[:verbose]
+ return *list if options[:noop]
+ mode = options[:mode] || (0777 & ~File.umask)
list.map {|n| File.expand_path(n) }.each do |dir|
stack = []
- until FileTest.directory? dir
+ until FileTest.directory?(dir)
stack.push dir
dir = File.dirname(dir)
end
stack.reverse_each do |n|
- Dir.mkdir n
+ Dir.mkdir n, mode
end
end
@@ -312,11 +188,21 @@ module FileUtils
alias makedirs mkdir_p
- def rmdir( list, *options )
- noop, verbose, = fu_parseargs(options, :noop, :verbose)
+ #
+ # 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 verbose
- return if noop
+ fu_output_message "rmdir #{list.join ' '}" if options[:verbose]
+ return if options[:noop]
list.each do |dir|
Dir.rmdir dir
@@ -324,45 +210,109 @@ module FileUtils
end
- def ln( src, dest, *options )
- force, noop, verbose, = fu_parseargs(options, :force, :noop, :verbose)
- fu_output_message "ln #{argv.join ' '}" if verbose
- return if noop
+ #
+ # 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 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 '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 '/bin'
+ # ln %w(cp mv mkdir), '/usr/bin' # Now /usr/bin/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_dest(src, dest) do |s,d|
- remove_file d, true if force
+ remove_file d, true if options[:force]
File.link s, d
end
end
alias link ln
- def ln_s( src, dest, *options )
- force, noop, verbose, = fu_parseargs(options, :force, :noop, :verbose)
- fu_output_message "ln -s#{force ? 'f' : ''} #{[src,dest].flatten.join ' '}" if verbose
- return if noop
+ #
+ # 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_dest(src, dest) do |s,d|
- remove_file d, true if force
+ remove_file d, true if options[:force]
File.symlink s, d
end
end
alias symlink ln_s
- def ln_sf( src, dest, *options )
- noop, verbose, = fu_parseargs(options, :noop, :verbose)
- ln_s src, dest, :force, *options
- end
-
-
- def cp( src, dest, *options )
- preserve, noop, verbose, = fu_parseargs(options, :preserve, :noop, :verbose)
- fu_output_message "cp#{preserve ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if verbose
- return if noop
+ #
+ # 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
+
+
+ #
+ # Options: preserve noop verbose
+ #
+ # Copies a file +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
+ #
+ 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|
- fu_preserve_attr(preserve, s, d) {
+ fu_preserve_attr(options[:preserve], s, d) {
copy_file s, d
}
end
@@ -370,45 +320,61 @@ module FileUtils
alias copy cp
- def cp_r( src, dest, *options )
- preserve, noop, verbose, = fu_parseargs(options, :preserve, :noop, :verbose)
- fu_output_message "cp -r #{[src,dest].flatten.join ' '}" if verbose
- return if noop
+ #
+ # Options: preserve noop verbose
+ #
+ # 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, :verbose
+ #
+ def cp_r( src, dest, options = {} )
+ fu_check_options options, :preserve, :noop, :verbose
+ fu_output_message "cp -r#{options[:preserve] ? 'p' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
+ return if options[:noop]
fu_each_src_dest(src, dest) do |s,d|
- if FileTest.directory? s
- fu_copy_dir s, d, '.', preserve
+ if FileTest.directory?(s)
+ fu_copy_dir s, d, '.', options[:preserve]
else
- fu_p_copy s, d, preserve
+ fu_p_copy s, d, options[:preserve]
end
end
end
- def fu_copy_dir( src, dest, rel, preserve )
- fu_preserve_attr( preserve, "#{src}/#{rel}",
- "#{dest}/#{rel}" ) {|s,d|
+ def fu_copy_dir( src, dest, rel, preserve ) #:nodoc:
+ fu_preserve_attr(preserve, "#{src}/#{rel}", "#{dest}/#{rel}") {|s,d|
dir = File.expand_path(d) # to remove '/./'
Dir.mkdir dir unless FileTest.directory? dir
}
- Dir.entries( "#{src}/#{rel}" ).each do |fn|
- if FileTest.directory? File.join(src,rel,fn)
- next if /\A\.\.?\z/ === fn
- fu_copy_dir src, dest, "#{rel}/#{fn}", preserve
+ Dir.entries("#{src}/#{rel}").each do |fname|
+ if FileTest.directory? File.join(src,rel,fname)
+ next if /\A\.\.?\z/ === fname
+ fu_copy_dir src, dest, "#{rel}/#{fname}", preserve
else
- fu_p_copy File.join(src,rel,fn), File.join(dest,rel,fn), preserve
+ fu_p_copy File.join(src,rel,fname), File.join(dest,rel,fname), preserve
end
end
end
private :fu_copy_dir
- def fu_p_copy( src, dest, really )
- fu_preserve_attr( really, src, dest ) {
+ def fu_p_copy( src, dest, really ) #:nodoc:
+ fu_preserve_attr(really, src, dest) {
copy_file src, dest
}
end
private :fu_p_copy
- def fu_preserve_attr( really, src, dest )
+ def fu_preserve_attr( really, src, dest ) #:nodoc:
unless really
yield src, dest
return
@@ -427,34 +393,57 @@ module FileUtils
end
private :fu_preserve_attr
+ #
+ # Copies file +src+ to +dest+.
+ # Both of +src+ and +dest+ must be a filename.
+ #
def copy_file( src, dest )
- bsize = fu_blksize(File.stat(src).blksize)
File.open(src, 'rb') {|r|
File.open(dest, 'wb') {|w|
- begin
- while true
- w.syswrite r.sysread(bsize)
- end
- rescue EOFError
- end
+ copy_stream r, w
} }
end
+ #
+ # Copies stream +src+ to +dest+.
+ # Both of +src+ and +dest+ must be a IO.
+ #
+ def copy_stream( src, dest )
+ bsize = fu_stream_blksize(src, dest)
+ begin
+ while true
+ dest.syswrite src.sysread(bsize)
+ end
+ rescue EOFError
+ end
+ end
- def mv( src, dest, *options )
- noop, verbose, = fu_parseargs(options, :noop, :verbose)
- fu_output_message "mv #{[src,dest].flatten.join ' '}" if verbose
- return if noop
+ #
+ # Options: 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', 'lib/ruby', :force => true
+ #
+ # FileUtils.mv %w(junk.txt dust.txt), '/home/aamine/.trash/'
+ # FileUtils.mv Dir.glob('test*.rb'), 'test', :noop, :verbose => true
+ #
+ def mv( src, dest, options = {} )
+ fu_check_options options, :noop, :verbose
+ fu_output_message "mv #{[src,dest].flatten.join ' '}" if options[:verbose]
+ return if options[:noop]
fu_each_src_dest(src, dest) do |s,d|
- if cannot_overwrite_file? and FileTest.file? d
+ if cannot_overwrite_file? and FileTest.file?(d)
File.unlink d
end
begin
File.rename s, d
rescue
- if FileTest.symlink? s
+ if FileTest.symlink?(s)
File.symlink File.readlink(s), dest
File.unlink s
else
@@ -474,179 +463,248 @@ module FileUtils
alias move mv
- def cannot_overwrite_file?
+ def cannot_overwrite_file? #:nodoc:
/djgpp|cygwin|mswin32/ === RUBY_PLATFORM
end
-
-
- def rm( list, *options )
- force, noop, verbose, = fu_parseargs(options, :force, :noop, :verbose)
+ private :cannot_overwrite_file?
+
+
+ #
+ # Options: force noop verbose
+ #
+ # Remove file(s) specified in +list+. This method cannot remove directories.
+ # All errors 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#{force ? ' -f' : ''} #{list.join ' '}" if verbose
- return if noop
+ fu_output_message "rm#{options[:force] ? ' -f' : ''} #{list.join ' '}" if options[:verbose]
+ return if options[:noop]
list.each do |fname|
- remove_file fname, force
+ remove_file fname, options[:force]
end
end
alias remove rm
- def rm_f( list, *options )
- noop, verbose, = fu_parseargs(options, :noop, :verbose)
- rm list, :force, *options
+ #
+ # Options: noop verbose
+ #
+ # Same as
+ # #rm(list, :force)
+ #
+ def rm_f( list, options = {} )
+ fu_check_options options, :noop, :verbose
+ options = options.dup
+ options[:force] = true
+ rm list, options
end
alias safe_unlink rm_f
- def rm_r( list, *options )
- force, noop, verbose, = fu_parseargs(options, :force, :noop, :verbose)
+ #
+ # Options: force noop verbose
+ #
+ # 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 # :-)
+ #
+ def rm_r( list, options = {} )
+ fu_check_options options, :force, :noop, :verbose
list = fu_list(list)
- fu_output_message "rm -r#{force ? 'f' : ''} #{list.join ' '}" if verbose
- return if noop
+ fu_output_message "rm -r#{options[:force] ? 'f' : ''} #{list.join ' '}" if options[:verbose]
+ return if options[:noop]
list.each do |fname|
begin
st = File.lstat(fname)
rescue
- next if force
+ next if options[:force]
+ raise
end
- if st.symlink? then remove_file fname, force
- elsif st.directory? then remove_dir fname, force
- else remove_file fname, force
+ if st.symlink? then remove_file fname, options[:force]
+ elsif st.directory? then remove_dir fname, options[:force]
+ else remove_file fname, options[:force]
end
end
end
- def rm_rf( list, *options )
- noop, verbose, = fu_parseargs(options, :noop, :verbose)
- rm_r list, :force, *options
+ #
+ # Options: noop verbose
+ #
+ # Same as
+ # #rm_r(list, :force => true)
+ #
+ def rm_rf( list, options = {} )
+ fu_check_options options, :noop, :verbose
+ options = options.dup
+ options[:force] = true
+ rm_r list, options
end
- def remove_file( fn, force = false )
- first = true
+ alias rmtree rm_rf
+
+ def remove_file( fname, force = false ) #:nodoc:
+ first_time_p = true
begin
- File.unlink fn
+ File.unlink fname
rescue Errno::ENOENT
- force or raise
+ raise unless force
rescue
- # rescue dos?
- begin
- if first
- first = false
- File.chmod 0777, fn
- retry
- end
- rescue
+ if first_time_p
+ # try once more for Windows
+ first_time_p = false
+ File.chmod 0777, fname
+ retry
end
+ raise
end
end
- def remove_dir( dir, force = false )
+ def remove_dir( dir, force = false ) #:nodoc:
Dir.foreach(dir) do |file|
next if /\A\.\.?\z/ === file
path = "#{dir}/#{file}"
- if FileTest.directory? path then remove_dir path, force
- else remove_file path, force
+ if FileTest.directory? path
+ remove_dir path, force
+ else
+ remove_file path, force
end
end
begin
Dir.rmdir dir
rescue Errno::ENOENT
- force or raise
+ raise unless force
end
end
- def cmp( filea, fileb, *options )
- verbose, = fu_parseargs(options, :verbose)
- fu_output_message "cmp #{filea} #{fileb}" if verbose
+ #
+ # 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
- sa = sb = nil
- st = File.stat(filea)
- bsize = fu_blksize(st.blksize)
- File.size(fileb) == st.size or return true
+ alias identical? compare_file
+ alias cmp compare_file
- File.open(filea, 'rb') {|a|
- File.open(fileb, 'rb') {|b|
- begin
- 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
+ #
+ # 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
- rescue EOFError
- ;
end
- } }
-
+ end
false
end
- alias identical? cmp
- def install( src, dest, mode, *options )
- noop, verbose, = fu_parseargs(options, :noop, :verbose)
- fu_output_message "install #{[src,dest].flatten.join ' '}#{mode ? ' %o'%mode : ''}" if verbose
- return if noop
+ #
+ # 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 FileTest.exist? d and cmp(s,d)
- remove_file d, true
- copy_file s, d
- File.chmod mode, d if mode
+ fu_each_src_dest(src, dest) do |s,d|
+ unless FileTest.exist?(d) and compare_file(s,d)
+ fu_preserve_attr(options[:preserve], s, d) {
+ remove_file d, true
+ copy_file s, d
+ }
+ File.chmod options[:mode], d if options[:mode]
end
end
end
- def chmod( mode, list, *options )
- noop, verbose, = fu_parseargs(options, :noop, :verbose)
+ #
+ # 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 verbose
- return if noop
+ fu_output_message sprintf('chmod %o %s', mode, list.join(' ')) if options[:verbose]
+ return if options[:noop]
File.chmod mode, *list
end
- def touch( list, *options )
- noop, verbose, = fu_parseargs(options, :noop, :verbose)
+
+ #
+ # 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 verbose
- return if noop
+ fu_output_message "touch #{list.join ' '}" if options[:verbose]
+ return if options[:noop]
t = Time.now
list.each do |fname|
begin
File.utime(t, t, fname)
rescue Errno::ENOENT
- File.open(fname, 'a') { }
+ File.open(fname, 'a') {
+ ;
+ }
end
end
end
-
private
- def fu_parseargs( opts, *flagdecl )
- tab = {}
- if opts.last == true or opts.last == false
- tab[:verbose] = opts.pop
- end
- while Symbol === opts.last
- tab[opts.pop] = true
+ def fu_check_options( options, *optdecl )
+ h = options.dup
+ optdecl.each do |name|
+ h.delete name
end
-
- flags = flagdecl.collect {|s| tab.delete(s) }
- tab.empty? or raise ArgumentError, "wrong option :#{tab.keys.join(' :')}"
-
- flags
+ raise ArgumentError, "no such option: #{h.keys.join(' ')}" unless h.empty?
end
-
def fu_list( arg )
Array === arg ? arg : [arg]
end
@@ -658,8 +716,8 @@ module FileUtils
dir = dest
# FileTest.directory? dir or raise ArgumentError, "must be dir: #{dir}"
dir += (dir[-1,1] == '/') ? '' : '/'
- src.each do |fn|
- yield fn, dir + File.basename(fn)
+ src.each do |fname|
+ yield fname, dir + File.basename(fname)
end
end
end
@@ -672,45 +730,53 @@ module FileUtils
end
end
- def fu_blksize( size )
- if size.nil? or size.zero?
- 2048
- else
- size
+ def fu_stream_blksize( *streams )
+ streams.each do |s|
+ next unless s.respond_to?(:stat)
+ size = s.stat.blksize
+ return size unless size.nil? or size.zero?
end
+ 1024
end
-
@fileutils_output = $stderr
- @fileutils_label = 'fileutils.'
+ @fileutils_label = ''
def fu_output_message( msg )
@fileutils_output ||= $stderr
- @fileutils_label ||= 'fileutils.'
+ @fileutils_label ||= ''
@fileutils_output.puts @fileutils_label + msg
end
+ def fu_update_option( args, new )
+ if Hash === args.last
+ args.last.update new
+ else
+ args.push new
+ end
+ args
+ end
+
extend self
OPT_TABLE = {
+ 'pwd' => %w(),
'cd' => %w( noop verbose ),
'chdir' => %w( noop verbose ),
'chmod' => %w( noop verbose ),
- 'cmp' => %w( verbose ),
'copy' => %w( preserve noop verbose ),
'cp' => %w( preserve noop verbose ),
'cp_r' => %w( preserve noop verbose ),
- 'identical?' => %w( verbose ),
- 'install' => %w( noop verbose ),
+ 'install' => %w( mode noop verbose ),
'link' => %w( force noop verbose ),
'ln' => %w( force noop verbose ),
'ln_s' => %w( force noop verbose ),
'ln_sf' => %w( noop verbose ),
'makedirs' => %w( noop verbose ),
- 'mkdir' => %w( noop verbose ),
- 'mkdir_p' => %w( noop verbose ),
+ 'mkdir' => %w( mode noop verbose ),
+ 'mkdir_p' => %w( mode noop verbose ),
'mkpath' => %w( noop verbose ),
'move' => %w( noop verbose ),
'mv' => %w( noop verbose ),
@@ -719,33 +785,35 @@ module FileUtils
'rm_f' => %w( noop verbose ),
'rm_r' => %w( force noop verbose ),
'rm_rf' => %w( noop verbose ),
+ 'rmtree' => %w( noop verbose ),
'rmdir' => %w( noop verbose ),
'safe_unlink' => %w( noop verbose ),
'symlink' => %w( force noop verbose ),
- 'touch' => %w( noop verbose ),
- 'uptodate?' => %w( verbose )
+ 'touch' => %w( noop verbose )
}
+ #
+ # This module has all methods of FileUtils module, but it outputs messages
+ # before acting. This equates to passing the +:verbose+ flag to methods in
+ # FileUtils.
+ #
module Verbose
include FileUtils
@fileutils_output = $stderr
- @fileutils_label = 'fileutils.'
+ @fileutils_label = ''
@fileutils_verbose = true
FileUtils::OPT_TABLE.each do |name, opts|
- next unless opts.include? 'verbose'
- module_eval <<-End, __FILE__, __LINE__ + 1
- def #{name}( *args )
- unless defined? @fileutils_verbose
- @fileutils_verbose = true
- end
- args.push :verbose if @fileutils_verbose
- super(*args)
- end
- End
+ next unless opts.include?('verbose')
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
+ def #{name}( *args )
+ @fileutils_verbose = true unless defined?(@fileutils_verbose)
+ super(*fu_update_option(args, :verbose => @fileutils_verbose))
+ end
+ EOS
end
extend self
@@ -753,58 +821,39 @@ module FileUtils
end
+ #
+ # This module has all methods of FileUtils module, but never changes
+ # files/directories. This equates to passing the +:noop+ flag to methods in
+ # FileUtils.
+ #
module NoWrite
include FileUtils
@fileutils_output = $stderr
- @fileutils_label = 'fileutils.'
+ @fileutils_label = ''
@fileutils_nowrite = true
FileUtils::OPT_TABLE.each do |name, opts|
next unless opts.include? 'noop'
- module_eval <<-End, __FILE__, __LINE__ + 1
- def #{name}( *args )
- unless defined? @fileutils_nowrite
- @fileutils_nowrite = true
- end
- args.push :noop if @fileutils_nowrite
- super(*args)
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
+ def #{name}( *args )
+ unless defined?(@fileutils_nowrite)
+ @fileutils_nowrite ||= true
end
- End
+ super(*fu_update_option(args, :noop => true))
+ end
+ EOS
end
extend self
end
+end
- class Operator
-
- include FileUtils
-
- def initialize( v = false )
- @verbose = v
- @noop = false
- @force = false
- @preserve = false
- end
-
- attr_accessor :verbose
- attr_accessor :noop
- attr_accessor :force
- attr_accessor :preserve
-
- FileUtils::OPT_TABLE.each do |name, opts|
- s = opts.collect {|i| "args.unshift :#{i} if @#{i}" }.join(' '*10+"\n")
- module_eval <<-End, __FILE__, __LINE__ + 1
- def #{name}( *args )
- #{s}
- super(*args)
- end
- End
- end
-
- end
-end
+# Documentation comments:
+# - Some RDoc markup used here doesn't work (namely, +file1+, +:noop+,
+# +dir/file+). I consider this a bug and expect that these will be valid in
+# the near future.
diff --git a/lib/final.rb b/lib/final.rb
deleted file mode 100644
index 9d81238823..0000000000
--- a/lib/final.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-# $Id$
-# Copyright (C) 1998 Yukihiro Matsumoto. All rights reserved.
-
-# final.rb is integrated into ObjectSpace; no longer needed.
diff --git a/lib/find.rb b/lib/find.rb
index 3c16533794..9ca39cabcb 100644
--- a/lib/find.rb
+++ b/lib/find.rb
@@ -1,16 +1,40 @@
-# Usage:
-# require "find"
#
-# Find.find('/foo','/bar') {|f| ...}
-# or
-# include Find
-# find('/foo','/bar') {|f| ...}
+# 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
- def find(*path)
- path.collect!{|d| d.dup}
- while file = path.shift
+
+ #
+ # 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
begin
@@ -26,7 +50,7 @@ module Find
else
f = File.join(file, f)
end
- path.unshift f
+ paths.unshift f
end
ensure
d.close
@@ -38,8 +62,17 @@ module Find
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/lib/ftools.rb b/lib/ftools.rb
index 6e1c886e56..c605bc085e 100644
--- a/lib/ftools.rb
+++ b/lib/ftools.rb
@@ -39,7 +39,7 @@ class << File
end
def copy from, to, verbose = false
- $stderr.print from, " -> ", catname(from, to), "\n" if verbose
+ $deferr.print from, " -> ", catname(from, to), "\n" if verbose
syscopy from, to
end
@@ -49,7 +49,7 @@ class << File
def move from, to, verbose = false
to = catname(from, to)
- $stderr.print from, " -> ", to, "\n" if verbose
+ $deferr.print from, " -> ", to, "\n" if verbose
if RUBY_PLATFORM =~ /djgpp|(cyg|ms|bcc)win|mingw/ and FileTest.file? to
unlink to
@@ -79,7 +79,7 @@ class << File
# false: not identical
def compare from, to, verbose = false
- $stderr.print from, " <=> ", to, "\n" if verbose
+ $deferr.print from, " <=> ", to, "\n" if verbose
return false if stat(from).size != stat(to).size
@@ -116,11 +116,11 @@ class << File
def safe_unlink(*files)
verbose = if files[-1].is_a? String then false else files.pop end
begin
- $stderr.print files.join(" "), "\n" if verbose
+ $deferr.print files.join(" "), "\n" if verbose
chmod 0777, *files
unlink(*files)
rescue
-# STDERR.print "warning: Couldn't unlink #{files.join ' '}\n"
+# $deferr.print "warning: Couldn't unlink #{files.join ' '}\n"
end
end
@@ -131,10 +131,10 @@ class << File
# mode = if dirs[-1].is_a? Fixnum then dirs.pop else 0755 end
mode = 0755
for dir in dirs
- next if FileTest.directory? dir
parent = dirname(dir)
+ next if parent == dir or FileTest.directory? dir
makedirs parent unless FileTest.directory? parent
- $stderr.print "mkdir ", dir, "\n" if verbose
+ $deferr.print "mkdir ", dir, "\n" if verbose
if basename(dir) != ""
Dir.mkdir dir, mode
end
@@ -148,7 +148,7 @@ class << File
vsave, $VERBOSE = $VERBOSE, false
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
+ $deferr.printf "chmod %04o %s\n", mode, files.join(" ") if verbose
o_chmod mode, *files
end
$VERBOSE = vsave
diff --git a/lib/ftplib.rb b/lib/ftplib.rb
deleted file mode 100644
index 4cb1b752d6..0000000000
--- a/lib/ftplib.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-#
-# ftplib.rb
-#
-
-$stderr.puts 'Warning: ftplib.rb is obsolete: use net/ftp'
-
-require 'net/ftp'
-
-FTP = ::Net::FTP
-FTPError = ::Net::FTPError
-FTPReplyError = ::Net::FTPReplyError
-FTPTempError = ::Net::FTPTempError
-FTPPermError = ::Net::FTPPermError
-FTPProtoError = ::Net::FTPProtoError
diff --git a/lib/getoptlong.rb b/lib/getoptlong.rb
index 9c8ed09db9..7bb9baef2d 100644
--- a/lib/getoptlong.rb
+++ b/lib/getoptlong.rb
@@ -67,7 +67,7 @@ class GetoptLong
@argument_flags = Hash.new
#
- # Whether error messages are output to stderr.
+ # Whether error messages are output to $deferr.
#
@quiet = FALSE
@@ -253,7 +253,7 @@ class GetoptLong
# Set an error (protected).
#
def set_error(type, message)
- $stderr.print("#{$0}: #{message}\n") if !@quiet
+ $deferr.print("#{$0}: #{message}\n") if !@quiet
@error = type
@error_message = message
diff --git a/lib/gserver.rb b/lib/gserver.rb
new file mode 100644
index 0000000000..14f765a90c
--- /dev/null
+++ b/lib/gserver.rb
@@ -0,0 +1,175 @@
+# Copyright (C) 2001 John W. Small All Rights Reserved
+# mailto:jsmall@laser.net subject:ruby-generic-server
+# Freeware
+
+require "socket"
+require "thread"
+
+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/lib/ipaddr.rb b/lib/ipaddr.rb
new file mode 100644
index 0000000000..b8befe8499
--- /dev/null
+++ b/lib/ipaddr.rb
@@ -0,0 +1,700 @@
+#
+# 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$
+#--
+# TODO:
+# - scope_id support
+#++
+#
+#== 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>
+
+require 'socket'
+
+# IPAddr provides a set of methods to manipulate an IP address. Both
+# IPv4 and IPv6 are supported.
+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
+ return IPSocket.getaddress(to_string)
+ 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
+
+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/lib/irb.rb b/lib/irb.rb
index 0d0f6f4136..1e59d6f669 100644
--- a/lib/irb.rb
+++ b/lib/irb.rb
@@ -141,10 +141,10 @@ module IRB
end
end
- @scanner.each_top_level_statement do
- |line, line_no|
+ @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
@@ -180,6 +180,10 @@ module IRB
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
diff --git a/lib/irb/completion.rb b/lib/irb/completion.rb
index 30d60c2449..c207cf09ef 100644
--- a/lib/irb/completion.rb
+++ b/lib/irb/completion.rb
@@ -122,7 +122,7 @@ module IRB
gv = eval "global_variables", bind
lv = eval "local_variables", bind
- cv = eval "type.constants", bind
+ cv = eval "self.class.constants", bind
if (gv | lv | cv).include?(receiver)
# foo.func and foo is local var.
@@ -140,7 +140,7 @@ module IRB
ObjectSpace.each_object(Module){|m|
next if m.name != "IRB::Context" and
/^(IRB|SLex|RubyLex|RubyToken)/ =~ m.name
- candidates.concat m.instance_methods
+ candidates.concat m.instance_methods(false)
}
candidates.sort!
candidates.uniq!
diff --git a/lib/irb/context.rb b/lib/irb/context.rb
index a041a001e4..f4d0d98289 100644
--- a/lib/irb/context.rb
+++ b/lib/irb/context.rb
@@ -40,7 +40,7 @@ module IRB
self.math_mode = IRB.conf[:MATH_MODE] if IRB.conf[:MATH_MODE]
self.use_tracer = IRB.conf[:USE_TRACER] if IRB.conf[:USE_TRASER]
self.use_loader = IRB.conf[:USE_LOADER] if IRB.conf[:USE_LOADER]
- self.eval_history = IRB.conf[:EVAL_HISTORY] if IRB.conf[:EVEL_HISTORY]
+ self.eval_history = IRB.conf[:EVAL_HISTORY] if IRB.conf[:EVAL_HISTORY]
@ignore_sigint = IRB.conf[:IGNORE_SIGINT]
@ignore_eof = IRB.conf[:IGNORE_EOF]
@@ -58,8 +58,8 @@ module IRB
case input_method
when nil
- if (use_readline.nil? && IRB.conf[:PROMPT_MODE] != :INF_RUBY && STDIN.tty? ||
- use_readline?)
+ if (defined? (ReadlineInputMethod) &&
+ (use_readline? || IRB.conf[:PROMPT_MODE] != :INF_RUBY && STDIN.tty?))
@io = ReadlineInputMethod.new
else
@io = StdioInputMethod.new
@@ -177,6 +177,7 @@ module IRB
@inspect_mode
end
+ undef use_readline=
def use_readline=(opt)
@use_readline = opt
print "use readline module\n" if @use_readline
diff --git a/lib/irb/ext/math-mode.rb b/lib/irb/ext/math-mode.rb
index a98664448e..5e268bb3e5 100644
--- a/lib/irb/ext/math-mode.rb
+++ b/lib/irb/ext/math-mode.rb
@@ -18,7 +18,7 @@ module IRB
def math_mode=(opt)
if @math_mode == true && opt == false
- IRB.fail CantRetuenNormalMode
+ IRB.fail CantReturnToNormalMode
return
end
diff --git a/lib/irb/ext/multi-irb.rb b/lib/irb/ext/multi-irb.rb
index 86569ae333..e815889383 100644
--- a/lib/irb/ext/multi-irb.rb
+++ b/lib/irb/ext/multi-irb.rb
@@ -9,7 +9,7 @@
#
#
#
-IRB.fail CanNotGoMultiIrbMode unless defined?(Thread)
+IRB.fail CantShiftToMultiIrbMode unless defined?(Thread)
require "thread"
module IRB
@@ -54,7 +54,7 @@ module IRB
def switch(key)
th, irb = search(key)
IRB.fail IrbAlreadyDead unless th.alive?
- IRB.fail IrbSwitchToCurrentThread if th == Thread.current
+ IRB.fail IrbSwitchedToCurrentThread if th == Thread.current
@current_job = irb
th.run
Thread.stop
diff --git a/lib/irb/input-method.rb b/lib/irb/input-method.rb
index 65a016fd07..ec834b17bc 100644
--- a/lib/irb/input-method.rb
+++ b/lib/irb/input-method.rb
@@ -28,7 +28,7 @@ module IRB
attr_accessor :prompt
def gets
- IRB.fail NotImplementError, "gets"
+ IRB.fail NotImplementedError, "gets"
end
public :gets
diff --git a/lib/irb/lc/error.rb b/lib/irb/lc/error.rb
index a80da7567f..68a488a48a 100644
--- a/lib/irb/lc/error.rb
+++ b/lib/irb/lc/error.rb
@@ -16,14 +16,14 @@ module IRB
# exceptions
extend Exception2MessageMapper
def_exception :UnrecognizedSwitch, "Unrecognized switch: %s"
- def_exception :NotImplementError, "Need to define `%s'"
- def_exception :CantRetuenNormalMode, "Can't return normal mode."
+ 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 :IrbSwitchToCurrentThread, "Change to current thread."
+ def_exception :IrbSwitchedToCurrentThread, "Switched to current thread."
def_exception :NoSuchJob, "No such job(%s)."
- def_exception :CanNotGoMultiIrbMode, "Can't go multi irb mode."
- def_exception :CanNotChangeBinding, "Can't change binding to (%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/lib/irb/lc/help-message b/lib/irb/lc/help-message
index 29c7c0fe4a..42e861a343 100644
--- a/lib/irb/lc/help-message
+++ b/lib/irb/lc/help-message
@@ -10,25 +10,25 @@
#
#
Usage: irb.rb [options] [programfile] [arguments]
- -f suppress read ~/.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'
- --inspect uses `inspect' for output (the default except bc mode)
- --noinspect doesn't uses inspect for output
- --readline uses Readline extension module
- --noreadline doesn't use Readline extension module
+ -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'
+ --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
- switches prompt mode. Pre-defined prompt modes are
+ Switch prompt mode. Pre-defined prompt modes are
`default', `simple', `xmp' and `inf-ruby'
- --inf-ruby-mode uses prompt appreciate for inf-ruby-mode on emacs.
+ --inf-ruby-mode Use prompt appropriate for inf-ruby-mode on emacs.
Suppresses --readline.
- --simple-prompt simple prompt mode
- --noprompt no prompt
- --tracer display trace for each execution of commands.
+ --simple-prompt Simple prompt mode
+ --noprompt No prompt mode
+ --tracer Display trace for each execution of commands.
--back-trace-limit n
- displayes backtrace top n and tail n. The default
+ Display backtrace top n and tail n. The default
value is 16.
- --irb_debug n sets internal debug level to n (It shouldn't be used)
- -v, --version prints the version of irb
+ --irb_debug n Set internal debug level to n (not for popular use)
+ -v, --version Print the version of irb
diff --git a/lib/irb/lc/ja/error.rb b/lib/irb/lc/ja/error.rb
index 19585467a0..e1afaf5d71 100644
--- a/lib/irb/lc/ja/error.rb
+++ b/lib/irb/lc/ja/error.rb
@@ -15,14 +15,14 @@ module IRB
# exceptions
extend Exception2MessageMapper
def_exception :UnrecognizedSwitch, '$B%9%$%C%A(B(%s)$B$,J,$j$^$;$s(B'
- def_exception :NotImplementError, '`%s\'$B$NDj5A$,I,MW$G$9(B'
- def_exception :CantRetuenNormalMode, 'Normal$B%b!<%I$KLa$l$^$;$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 :IrbSwitchToCurrentThread, 'Change to current thread.'
+ 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 :CanNotGoMultiIrbMode, 'multi-irb mode$B$K0\$l$^$;$s(B.'
- def_exception :CanNotChangeBinding, '$B%P%$%s%G%#%s%0(B(%s)$B$KJQ99$G$-$^$;$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/lib/irb/locale.rb b/lib/irb/locale.rb
index dad6b2f075..b2c90e38e2 100644
--- a/lib/irb/locale.rb
+++ b/lib/irb/locale.rb
@@ -21,27 +21,26 @@ module IRB
LOCALE_DIR = "/lc/"
def initialize(locale = nil)
- @lang = locale || ENV["IRB_LANG"] || ENV["LC_MESSAGES"] || ENV["LC_ALL"] || ENV["LANG"]
- @lang = "C" unless @lang
+ @lang = locale || ENV["IRB_LANG"] || ENV["LC_MESSAGES"] || ENV["LC_ALL"] || ENV["LANG"] || "C"
end
attr_reader :lang
- @@LC2KCONV = {
- # "ja" => Kconv::JIS,
- # "ja_JP" => Kconv::JIS,
- "ja_JP.ujis" => Kconv::EUC,
- "ja_JP.euc" => Kconv::EUC,
- "ja_JP.eucJP" => Kconv::EUC,
- "ja_JP.sjis" => Kconv::SJIS,
- "ja_JP.SJIS" => Kconv::SJIS,
- }
+ 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
+ end
+ end
+ private :lc2kconv
def String(mes)
mes = super(mes)
case @lang
when /^ja/
- mes = Kconv::kconv(mes, @@LC2KCONV[@lang])
+ mes = Kconv::kconv(mes, lc2kconv(@lang))
else
mes
end
diff --git a/lib/irb/ruby-lex.rb b/lib/irb/ruby-lex.rb
index 26b0b3b7c2..3b62ef8676 100644
--- a/lib/irb/ruby-lex.rb
+++ b/lib/irb/ruby-lex.rb
@@ -67,14 +67,14 @@ class RubyLex
attr_reader :indent
# io functions
- def set_input(io, p = nil)
+ def set_input(io, p = nil, &block)
@io = io
- if p.kind_of?(Proc)
+ if p.respond_to?(:call)
@input = p
- elsif iterator?
- @input = proc
+ elsif block_given?
+ @input = block
else
- @input = proc{@io.gets}
+ @input = Proc.new{@io.gets}
end
end
@@ -183,11 +183,12 @@ class RubyLex
end
private :buf_input
- def set_prompt(p = proc)
- if p.kind_of?(Proc)
+ def set_prompt(p=nil, &block)
+ p = block if block_given?
+ if p.respond_to?(:call)
@prompt = p
else
- @prompt = proc{print p}
+ @prompt = Proc.new{print p}
end
end
diff --git a/lib/irb/slex.rb b/lib/irb/slex.rb
index 1cf23255ad..ceee90b3d9 100644
--- a/lib/irb/slex.rb
+++ b/lib/irb/slex.rb
@@ -31,22 +31,22 @@ class SLex
@head = Node.new("")
end
- def def_rule(token, preproc = nil, postproc = nil)
+ def def_rule(token, preproc = nil, postproc = nil, &block)
# print node.inspect, "\n" if SLex.debug?
- postproc = proc if iterator?
+ postproc = block if block_given?
node = create(token, preproc, postproc)
end
- def def_rules(*tokens)
- if iterator?
- p = proc
+ def def_rules(*tokens, &block)
+ if block_given?
+ p = block
end
for token in tokens
def_rule(token, nil, p)
end
end
- def preporc(token, proc)
+ def preproc(token, proc)
node = search(token)
node.preproc=proc
end
diff --git a/lib/irb/workspace.rb b/lib/irb/workspace.rb
index 6dcb450fd7..3659fa8b56 100644
--- a/lib/irb/workspace.rb
+++ b/lib/irb/workspace.rb
@@ -67,7 +67,7 @@ EOF
begin
@binding = eval("IRB.conf[:__MAIN__].instance_eval('binding', __FILE__, __LINE__)", @binding, __FILE__, __LINE__)
rescue TypeError
- IRB.fail CanNotChangeBinding, @main.inspect
+ IRB.fail CantChangeBinding, @main.inspect
end
end
end
diff --git a/lib/jcode.rb b/lib/jcode.rb
index 0d796a2b01..8cd770be26 100644
--- a/lib/jcode.rb
+++ b/lib/jcode.rb
@@ -1,15 +1,15 @@
# jcode.rb - ruby code to handle japanese (EUC/SJIS) string
if $VERBOSE && $KCODE == "NONE"
- STDERR.puts "Warning: $KCODE is NONE."
+ warn "Warning: $KCODE is NONE."
end
$vsave, $VERBOSE = $VERBOSE, false
class String
- printf STDERR, "feel free for some warnings:\n" if $VERBOSE
+ warn "feel free for some warnings:\n" if $VERBOSE
def _regex_quote(str)
- str.gsub(/(\\[][\-\\])|\\(.)|([][\\])/) do
+ str.gsub(/(\\[\[\]\-\\])|\\(.)|([\[\]\\])/) do
$1 || $2 || '\\' + $3
end
end
@@ -19,9 +19,9 @@ class String
PATTERN_EUC = '[\xa1-\xfe][\xa1-\xfe]'
PATTERN_UTF8 = '[\xc0-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf][\x80-\xbf]'
- RE_SJIS = Regexp.new(PATTERN_SJIS, 'n')
- RE_EUC = Regexp.new(PATTERN_EUC, 'n')
- RE_UTF8 = Regexp.new(PATTERN_UTF8, 'n')
+ 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)
@@ -59,13 +59,13 @@ class String
def end_regexp
case $KCODE[0]
when ?s, ?S
- /#{PATTERN_SJIS}$/o
+ /#{PATTERN_SJIS}$/on
when ?e, ?E
- /#{PATTERN_EUC}$/o
+ /#{PATTERN_EUC}$/on
when ?u, ?U
- /#{PATTERN_UTF8}$/o
+ /#{PATTERN_UTF8}$/on
else
- /.$/o
+ /.$/on
end
end
@@ -130,7 +130,8 @@ class String
public
def tr!(from, to)
- return self.delete!(from) if to.length == 0
+ return nil if from == ""
+ return self.delete!(from) if to == ""
pattern = TrPatternCache[from] ||= /[#{_regex_quote(from)}]/
if from[0] == ?^
@@ -147,6 +148,7 @@ class String
end
def delete!(del)
+ return nil if del == ""
self.gsub!(DeletePatternCache[del] ||= /[#{_regex_quote(del)}]+/, '')
end
@@ -155,6 +157,7 @@ class String
end
def squeeze!(del=nil)
+ return nil if del == ""
pattern =
if del
SqueezePatternCache[del] ||= /([#{_regex_quote(del)}])\1+/
diff --git a/lib/mathn.rb b/lib/mathn.rb
index f3f7a95a48..26dd0b4a27 100644
--- a/lib/mathn.rb
+++ b/lib/mathn.rb
@@ -10,8 +10,8 @@
#
#
-require "rational.rb"
require "complex.rb"
+require "rational.rb"
require "matrix.rb"
class Integer
@@ -106,25 +106,18 @@ class Prime
end
class Fixnum
- alias divmod! divmod
- alias / rdiv
- def divmod(other)
- a = self.div(other)
- b = self % other
- return a,b
- end
+ alias / quo
end
class Bignum
- alias divmod! divmod
- alias / rdiv
+ alias / quo
end
class Rational
Unify = true
def inspect
- format "%s/%s", @numerator.inspect, @denominator.inspect
+ format "%s/%s", numerator.inspect, denominator.inspect
end
alias power! **
@@ -141,8 +134,8 @@ class Rational
return Rational(1,1)
end
- npd = @numerator.prime_division
- dpd = @denominator.prime_division
+ npd = numerator.prime_division
+ dpd = denominator.prime_division
if other < 0
other = -other
npd, dpd = dpd, npd
@@ -171,11 +164,11 @@ class Rational
elsif other.kind_of?(Integer)
if other > 0
- num = @numerator ** other
- den = @denominator ** other
+ num = numerator ** other
+ den = denominator ** other
elsif other < 0
- num = @denominator ** -other
- den = @numerator ** -other
+ num = denominator ** -other
+ den = numerator ** -other
elsif other == 0
num = 1
den = 1
@@ -215,11 +208,11 @@ class Rational
elsif other.kind_of?(Integer)
if other > 0
- num = @numerator ** other
- den = @denominator ** other
+ num = numerator ** other
+ den = denominator ** other
elsif other < 0
- num = @denominator ** -other
- den = @numerator ** -other
+ num = denominator ** -other
+ den = numerator ** -other
elsif other == 0
num = 1
den = 1
diff --git a/lib/matrix.rb b/lib/matrix.rb
index 706252b091..8e56fcbd97 100644
--- a/lib/matrix.rb
+++ b/lib/matrix.rb
@@ -126,13 +126,7 @@
# column_vectors
# array of column vectors
# to_a
-# converts each element to Array
-# to_f
-# converts each element to Float
-# to_i
-# converts each element to Integer
-# to_r
-# converts each element to Rational
+# converts to Array of Arrays
# PRINTING:
# to_s
# returns string representation
@@ -163,9 +157,6 @@
# CONVERTING:
# covector
# to_a
-# to_f
-# to_i
-# to_r
# coerce(other)
# PRINTING:
# to_s
@@ -183,6 +174,19 @@ module ExceptionForMatrix
def_exception("ErrOperationNotDefined", "This operation(%s) can\\'t defined")
end
+#
+# 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).
+#
+# The capabilities of the class indicated in the above paragraph are probably
+# not exhaustive. Browse the methods and their documentation for more
+# information.
+#
+# Note that although matrices should theoretically be rectangular, this is not
+# enforced by the class.
+#
class Matrix
@RCS_ID='-$Id: matrix.rb,v 1.11 1999/10/06 11:01:53 keiju Exp keiju $-'
@@ -192,14 +196,34 @@ class Matrix
# 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|
@@ -211,6 +235,13 @@ class Matrix
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 {
@@ -222,10 +253,23 @@ class Matrix
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
@@ -234,10 +278,22 @@ class Matrix
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
@@ -245,10 +301,18 @@ class Matrix
when Array
Matrix.rows([row.dup], false)
else
- Matrix.row([[row]], false)
+ 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
@@ -260,7 +324,10 @@ class Matrix
end
end
- # initializing
+ #
+ # 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
@@ -275,31 +342,50 @@ class Matrix
end
private :init_rows
- #accessing
+ #
+ # 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
- def row(i)
+ #
+ # 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
- def column(j)
+ #
+ # 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|
@@ -314,23 +400,37 @@ class Matrix
end
end
- def collect
+ #
+ # 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
#
- # param: (from_row, row_size, from_col, size_col)
- # (from_row..to_row, from_col..to_col)
+ # 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].size
+ size_row = param[0].end - from_row
+ size_row += 1 unless param[0].exclude_end?
from_col = param[1].first
- size_col = param[1].size
+ 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]
@@ -346,21 +446,40 @@ class Matrix
}
Matrix.rows(rows, false)
end
-
- # TESTING
+
+ #--
+ # 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
- # COMPARING
+ #--
+ # OBJECT METHODS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ #++
+
+ #
+ # Returns +true+ if and only if the two matrices contain equal elements.
+ #
def ==(other)
return false unless Matrix === other
@@ -368,6 +487,9 @@ class Matrix
end
alias eql? ==
+ #
+ # Not really intended for general consumption.
+ #
def compare_by_row_vectors(rows)
return false unless @rows.size == rows.size
@@ -378,10 +500,17 @@ class Matrix
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
@@ -392,8 +521,16 @@ class Matrix
return value
end
- # ARITHMETIC
+ #--
+ # 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
@@ -431,6 +568,12 @@ class Matrix
end
end
+ #
+ # Matrix addition.
+ # Matrix.scalar(2,5) + Matrix[[1,0], [-4,7]]
+ # => 6 0
+ # -4 12
+ #
def +(m)
case m
when Numeric
@@ -455,6 +598,12 @@ class Matrix
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
@@ -479,6 +628,12 @@ class Matrix
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
@@ -498,12 +653,21 @@ class Matrix
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
@@ -547,6 +711,13 @@ class Matrix
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
@@ -574,8 +745,16 @@ class Matrix
end
end
- # Matrix functions
+ #--
+ # 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?
@@ -608,6 +787,13 @@ class Matrix
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
@@ -666,6 +852,11 @@ class Matrix
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
@@ -676,13 +867,28 @@ class Matrix
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
+ #--
+ # CONVERTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ #++
+ #
+ # FIXME: describe #coerce.
+ #
def coerce(other)
case other
when Numeric
@@ -692,6 +898,9 @@ class Matrix
end
end
+ #
+ # Returns an array of the row vectors of the matrix. See Vector.
+ #
def row_vectors
rows = (0 .. row_size - 1).collect {
|i|
@@ -700,6 +909,9 @@ class Matrix
rows
end
+ #
+ # Returns an array of the column vectors of the matrix. See Vector.
+ #
def column_vectors
columns = (0 .. column_size - 1).collect {
|i|
@@ -708,23 +920,20 @@ class Matrix
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
- def to_f
- collect{|e| e.to_f}
- end
-
- def to_i
- collect{|e| e.to_i}
- end
+ #--
+ # PRINTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ #++
- def to_r
- collect{|e| e.to_r}
- end
-
- # PRINTING
+ #
+ # Overrides Object#to_s
+ #
def to_s
"Matrix[" + @rows.collect{
|row|
@@ -732,13 +941,16 @@ class Matrix
}.join(", ")+"]"
end
+ #
+ # Overrides Object#inspect
+ #
def inspect
"Matrix"+@rows.inspect
end
# Private CLASS
- class Scalar < Numeric
+ class Scalar < Numeric # :nodoc:
include ExceptionForMatrix
def initialize(value)
@@ -827,18 +1039,33 @@ class Vector
#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
@@ -849,16 +1076,28 @@ class Vector
# ACCSESSING
+ #
+ # 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
- # ENUMRATIONS
- def each2(v)
+ #--
+ # ENUMRATIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ #++
+
+ #
+ # 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|
@@ -866,7 +1105,11 @@ class Vector
end
end
- def collect2(v)
+ #
+ # 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|
@@ -874,7 +1117,13 @@ class Vector
end
end
- # COMPARING
+ #--
+ # COMPARING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ #++
+
+ #
+ # Returns +true+ iff the two vectors have the same elements in the same order.
+ #
def ==(other)
return false unless Vector === other
@@ -882,21 +1131,35 @@ class Vector
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
+ #--
+ # ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+ #++
- def *(x) #x is matrix or number
+ #
+ # 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}
@@ -909,6 +1172,9 @@ class Vector
end
end
+ #
+ # Vector addition.
+ #
def +(v)
case v
when Vector
@@ -926,6 +1192,9 @@ class Vector
end
end
+ #
+ # Vector subtraction.
+ #
def -(v)
case v
when Vector
@@ -943,8 +1212,14 @@ class Vector
end
end
- # VECTOR FUNCTIONS
+ #--
+ # 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
@@ -956,7 +1231,10 @@ class Vector
p
end
- def collect
+ #
+ # Like Array#collect.
+ #
+ def collect # :yield: e
els = @elements.collect {
|v|
yield v
@@ -965,7 +1243,10 @@ class Vector
end
alias map collect
- def map2(v)
+ #
+ # 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
@@ -973,6 +1254,10 @@ class Vector
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
@@ -981,27 +1266,27 @@ class Vector
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
- def to_f
- collect{|e| e.to_f}
- end
-
- def to_i
- collect{|e| e.to_i}
- end
-
- def to_r
- collect{|e| e.to_r}
- end
-
+ #
+ # FIXME: describe Vector#coerce.
+ #
def coerce(other)
case other
when Numeric
@@ -1011,13 +1296,27 @@ class Vector
end
end
- # PRINTING
+ #--
+ # 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
+# - Matrix class methods (aliases) unit and I don't appear in RDoc output
+# becuase of "class << Matrix". This is an RDoc issue.
diff --git a/lib/mkmf.rb b/lib/mkmf.rb
index 0dcd32bc56..d56fe8896e 100644
--- a/lib/mkmf.rb
+++ b/lib/mkmf.rb
@@ -2,12 +2,14 @@
# invoke like: ruby -r mkmf extconf.rb
require 'rbconfig'
+require 'fileutils'
require 'shellwords'
CONFIG = Config::MAKEFILE_CONFIG
ORIG_LIBPATH = ENV['LIB']
SRC_EXT = ["c", "cc", "m", "cxx", "cpp", "C"]
+$static = $config_h = nil
unless defined? $configure_args
$configure_args = {}
@@ -17,6 +19,7 @@ unless defined? $configure_args
end
for arg in Shellwords::shellwords(args)
arg, val = arg.split('=', 2)
+ next unless arg
if arg.sub!(/^(?!--)/, '--')
val or next
arg.downcase!
@@ -26,6 +29,7 @@ unless defined? $configure_args
end
for arg in ARGV
arg, val = arg.split('=', 2)
+ next unless arg
if arg.sub!(/^(?!--)/, '--')
val or next
arg.downcase!
@@ -78,7 +82,7 @@ if not $extmk and File.exist? Config::CONFIG["archdir"] + "/ruby.h"
elsif File.exist? $srcdir + "/ruby.h"
$hdrdir = $srcdir
else
- STDERR.print "can't find header files for ruby.\n"
+ warn "can't find header files for ruby."
exit 1
end
$topdir = $hdrdir
@@ -88,8 +92,6 @@ CPPOUTFILE = CONFIG['CPPOUTFILE']
CONFTEST_C = "conftest.c"
-$INSTALLFILES ||= nil
-
class String
def quote
/\s/ =~ self ? "\"#{self}\"" : self
@@ -102,25 +104,13 @@ class Array
end
def rm_f(*files)
- targets = []
- for file in files
- targets.concat Dir[file]
- end
- if not targets.empty?
- File::chmod(0777, *targets)
- File::unlink(*targets)
- end
+ FileUtils.rm_f(Dir[files.join("\0")])
end
-def older(target, *files)
- mtime = proc do |f|
- Time === f ? f : f.respond_to?(:mtime) ? f.mtime : File.mtime(f) rescue nil
- end
- t = mtime[target] or return true
- for f in files
- return true if t < (mtime[f] or next)
- end
- false
+def modified?(target, times)
+ (t = File.mtime(target)) rescue return nil
+ Array === times or times = [times]
+ t if times.all? {|n| n <= t}
end
module Logging
@@ -179,10 +169,10 @@ def xpopen command, *mode, &block
end
def log_src(src)
- Logging::message <<"EOM"
+ Logging::message <<"EOM", src
checked program was:
/* begin */
-#{src}/* end */
+%s/* end */
EOM
end
@@ -259,10 +249,63 @@ def cpp_include(header)
end
end
+def try_static_assert(expr, headers = nil, opt = "")
+ headers = cpp_include(headers)
+ try_compile(<<SRC, opt)
+#{COMMON_HEADERS}
+#{headers}
+int tmp[(#{expr}) ? 1 : -1];
+SRC
+end
+
+def try_constant(const, headers = nil, opt = "")
+ if CROSS_COMPILING
+ unless try_compile(<<"SRC", opt)
+#{COMMON_HEADERS}
+#{cpp_include(headers)}
+int tmp = #{const};
+SRC
+ return nil
+ end
+ if try_static_assert("#{const} < 0", headers, opt)
+ neg = true
+ const = "-(#{const})"
+ elsif try_static_assert("#{const} == 0", headers, opt)
+ return 0
+ end
+ upper = 1
+ until try_static_assert("#{const} < #{upper}", headers, opt)
+ lower = upper
+ upper <<= 1
+ end
+ return nil unless lower
+ until try_static_assert("#{const} == #{upper}", headers, opt)
+ if try_static_assert("#{const} > #{(upper+lower)/2}", headers, opt)
+ lower = (upper+lower)/2
+ else
+ upper = (upper+lower)/2
+ end
+ end
+ upper = -upper if neg
+ return upper
+ else
+ src = %{#{COMMON_HEADERS}
+#{cpp_include(headers)}
+#include <stdio.h>
+int main() {printf("%d\\n", (int)(#{const})); return 0;}
+}
+ if try_link0(src, opt)
+ xpopen("./conftest") do |f|
+ return Integer(f.gets)
+ end
+ end
+ end
+ nil
+end
+
def try_func(func, libs, headers = nil)
headers = cpp_include(headers)
try_link(<<"SRC", libs) or try_link(<<"SRC", libs)
-#{COMMON_HEADERS}
#{headers}
int main() { return 0; }
int t() { #{func}(); return 0; }
@@ -279,7 +322,7 @@ def egrep_cpp(pat, src, opt="")
create_tmpsrc(src)
xpopen(cpp_command('', opt)) do |f|
if Regexp === pat
- puts(" ruby -ne 'print if /#{pat.source}/'")
+ puts(" ruby -ne 'print if #{pat.inspect}'")
f.grep(pat) {|l|
puts "#{f.lineno}: #{l}"
return true
@@ -337,6 +380,7 @@ def install_files(mfile, ifiles, map = nil, srcprefix = nil)
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)
@@ -345,6 +389,12 @@ def install_files(mfile, ifiles, map = nil, srcprefix = nil)
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
@@ -365,12 +415,13 @@ def message(*s)
end
def checking_for(m)
- f = caller[0][/in \`(.*)\'$/, 1] and f << ": "
+ f = caller[0][/in `(.*)'$/, 1] and f << ": " #` for vim
m = "checking for #{m}... "
- message m
- Logging::message "#{f}#{m}\n"
+ message "%s", m
+ Logging::message "%s%s--------------------\n", f, m
r = yield
- message(r ? "yes\n" : "no\n")
+ message(a = r ? "yes\n" : "no\n")
+ Logging::message "-------------------- %s\n", a
r
end
@@ -445,6 +496,38 @@ SRC
end
end
+def have_type(type, header=nil, opt="")
+ checking_for type do
+ if try_compile(<<"SRC", opt) or try_compile(<<"SRC", opt)
+#{COMMON_HEADERS}
+#{cpp_include(header)}
+static #{type} t;
+SRC
+#{COMMON_HEADERS}
+#{cpp_include(header)}
+static #{type} *t;
+SRC
+ $defs.push(format("-DHAVE_TYPE_%s", type.upcase))
+ true
+ else
+ false
+ end
+ end
+end
+
+def check_sizeof(type, header=nil)
+ expr = "sizeof(#{type})"
+ m = "checking size of #{type}... "
+ message "%s", m
+ Logging::message "check_sizeof: %s--------------------\n", m
+ if size = try_constant(expr, header)
+ $defs.push(format("-DSIZEOF_%s", type.upcase))
+ end
+ message(a = size ? "#{size}\n" : "failed\n")
+ Logging::message "-------------------- %s\n", a
+ r
+end
+
def find_executable0(bin, path = nil)
path = (path || ENV['PATH']).split(File::PATH_SEPARATOR)
ext = config_string('EXEEXT')
@@ -484,7 +567,7 @@ def enable_config(config, default=nil)
end
def create_header(header = "extconf.h")
- message "creating #{header}\n"
+ message "creating %s\n", header
if $defs.length > 0
sym = header.tr("a-z./\055", "A-Z___")
open(header, "w") do |hfile|
@@ -504,21 +587,32 @@ end
def dir_config(target, idefault=nil, ldefault=nil)
if dir = with_config(target + "-dir", (idefault unless ldefault))
- idefault = dir + "/include"
- ldefault = dir + "/lib"
+ defaults = dir.split(File::PATH_SEPARATOR)
+ idefault = ldefault = nil
end
idir = with_config(target + "-include", idefault)
ldir = with_config(target + "-lib", ldefault)
- if idir
- idircflag = "-I" + idir
- $CPPFLAGS += " " + idircflag unless $CPPFLAGS.split.include?(idircflag)
+ idirs = 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 << $CPPFLAGS).join(" ")
+ end
end
- if ldir
- $LIBPATH << ldir unless $LIBPATH.include?(ldir)
+ ldirs = 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
[idir, ldir]
end
@@ -541,7 +635,7 @@ SHELL = /bin/sh
srcdir = #{srcdir}
topdir = #{$topdir}
hdrdir = #{$hdrdir}
-VPATH = $(srcdir)
+VPATH = #{$mingw && CONFIG['build_os'] == 'cygwin' ? '$(shell cygpath -u $(srcdir))' : '$(srcdir)'}
}
drive = File::PATH_SEPARATOR == ';' ? /\A\w:/ : /\A/
if destdir = CONFIG["prefix"].scan(drive)[0] and !destdir.empty?
@@ -577,14 +671,24 @@ arch = #{CONFIG['arch']}
sitearch = #{CONFIG['sitearch']}
ruby_version = #{Config::CONFIG['ruby_version']}
RUBY = #{$ruby}
-RM = $(RUBY) -rftools -e "File::rm_f(*ARGV.map do|x|Dir[x]end.flatten.uniq)"
-MAKEDIRS = $(RUBY) -r ftools -e 'File::makedirs(*ARGV)'
-INSTALL_PROG = $(RUBY) -r ftools -e 'File::install(ARGV[0], ARGV[1], 0555, true)'
-INSTALL_DATA = $(RUBY) -r ftools -e 'File::install(ARGV[0], ARGV[1], 0644, true)'
+RM = $(RUBY) -run -e rm -- -f
+MAKEDIRS = $(RUBY) -run -e mkdir -- -p
+INSTALL_PROG = $(RUBY) -run -e install -- -vpm 0755
+INSTALL_DATA = $(RUBY) -run -e install -- -vpm 0644
#### End of system configuration section. ####
}
+ 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)
@@ -613,9 +717,21 @@ def create_makefile(target, srcprefix = nil)
srcprefix ||= '$(srcdir)'
Config::expand(srcdir = srcprefix.dup)
- cleanfiles = []
- distcleanfiles = []
- if EXPORT_PREFIX
+ unless $objs then
+ $objs = []
+ for f in Dir[File.join(srcdir, "*.{#{SRC_EXT.join(%q{,})}}")]
+ $objs.push(File.basename(f, ".*") << "." << $OBJEXT)
+ end
+ else
+ for i in $objs
+ i.sub!(/\.o\z/, ".#{$OBJEXT}")
+ end
+ end
+ $objs = $objs.join(" ")
+
+ target = nil if $objs == ""
+
+ if target and EXPORT_PREFIX
origdef = target + '.def'
deffile = EXPORT_PREFIX + origdef
unless File.exist? deffile
@@ -639,38 +755,27 @@ def create_makefile(target, srcprefix = nil)
end
end
end
- distcleanfiles << deffile unless deffile == origdef
+ $distcleanfiles << deffile unless deffile == origdef
end
libpath = libpathflag(libpath)
- unless $objs then
- $objs = []
- for f in Dir[File.join(srcdir, "*.{#{SRC_EXT.join(%q{,})}}")]
- $objs.push(File.basename(f, ".*") << "." << $OBJEXT)
- end
- else
- for i in $objs
- i.sub!(/\.o\z/, ".#{$OBJEXT}")
- end
- end
- $objs = $objs.join(" ")
-
+ dllib = target ? "$(TARGET).#{$static ? $LIBEXT : CONFIG['DLEXT']}" : ""
mfile = open("Makefile", "wb")
mfile.print configuration(srcdir)
mfile.print %{
LIBPATH = #{libpath}
DEFFILE = #{deffile}
-CLEANFILES = #{cleanfiles.join(' ')}
-DISTCLEANFILES = #{distcleanfiles.join(' ')}
+CLEANFILES = #{$cleanfiles.join(' ')}
+DISTCLEANFILES = #{$distcleanfiles.join(' ')}
target_prefix = #{target_prefix}
LOCAL_LIBS = #{$LOCAL_LIBS}
LIBS = #{$LIBRUBYARG} #{$libs} #{$LIBS}
OBJS = #{$objs}
TARGET = #{target}
-DLLIB = $(TARGET).#{$static ? $LIBEXT : CONFIG['DLEXT']}
+DLLIB = #{dllib}
}
if $extmk
mfile.print %{
@@ -689,11 +794,11 @@ RUBYARCHDIR = $(sitearchdir)$(target_prefix)
CLEANLIBS = "$(TARGET).{lib,exp,il?,tds,map}" $(DLLIB)
CLEANOBJS = "*.{#{$OBJEXT},#{$LIBEXT},s[ol],pdb,bak}"
-all: $(DLLIB)
+all: #{target ? "$(DLLIB)" : "Makefile"}
}
mfile.print CLEANINGS
dirs = []
- unless $static
+ if not $static and target
dirs << (dir = "$(RUBYARCHDIR)")
mfile.print("install: #{dir}\n")
f = "$(DLLIB)"
@@ -723,6 +828,8 @@ all: $(DLLIB)
mfile.print "\nsite-install: install\n\n"
+ return unless target
+
mfile.print ".SUFFIXES: .#{SRC_EXT.join(' .')} .#{$OBJEXT}\n"
mfile.print "\n"
@@ -741,6 +848,7 @@ all: $(DLLIB)
mfile.print "$(DLLIB): $(OBJS)\n\t"
mfile.print "@-$(RM) $@\n\t"
+ mfile.print "@-$(RM) $(TARGET).lib\n\t" if $mswin
if $static
mfile.print "$(AR) #{config_string('ARFLAGS') || 'cru '}$(DLLIB) $(OBJS)"
if ranlib = config_string('RANLIB')
@@ -763,6 +871,7 @@ all: $(DLLIB)
end
end
end
+ensure
mfile.close
end
@@ -771,38 +880,43 @@ def init_mkmf(config = CONFIG)
$defs = []
$CFLAGS = with_config("cflags", arg_config("CFLAGS", config["CFLAGS"])).dup
$CPPFLAGS = with_config("cppflags", arg_config("CPPFLAGS", config["CPPFLAGS"])).dup
- $LDFLAGS = with_config("ldflags", arg_config("LDFLAGS", config["LDFLAGS"])).dup
+ $LDFLAGS = (with_config("ldflags") || "").dup
$INCFLAGS = "-I#{$topdir}"
- $DLDFLAGS = ""
+ $DLDFLAGS = (arg_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)"] : []
+ $LIBPATH = CROSS_COMPILING ? [] : ["$(libdir)"]
+ $LIBPATH.unshift("$(topdir)") if $extmk
+ $INSTALLFILES = nil
$objs = nil
$libs = ""
if $enable_shared or Config.expand(config["LIBRUBY"].dup) != Config.expand(config["LIBRUBY_A"].dup)
- $LIBPATH.unshift("$(libdir)") unless $extmk or defined? CROSS_COMPILING
$LIBRUBYARG = config['LIBRUBYARG']
end
$LOCAL_LIBS = ""
+
+ $cleanfiles = []
+ $distcleanfiles = []
+
dir_config("opt")
end
init_mkmf
-dir_config("opt")
$make = with_config("make-prog", ENV["MAKE"] || "make")
+make, = Shellwords.shellwords($make)
$nmake = nil
case
when $mswin
- $nmake = ?m if /nmake/i =~ $make
+ $nmake = ?m if /nmake/i =~ make
when $bccwin
- $nmake = ?b if /Borland/i =~ `#$make -h`
+ $nmake = ?b if /Borland/i =~ `#{make} -h`
end
Config::CONFIG["srcdir"] = CONFIG["srcdir"] =
diff --git a/lib/monitor.rb b/lib/monitor.rb
index 721c51a9f5..5eb7bb4e03 100644
--- a/lib/monitor.rb
+++ b/lib/monitor.rb
@@ -42,6 +42,40 @@ 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
module Accessible
protected
@@ -59,6 +93,12 @@ module MonitorMixin
end
end
+ #
+ # 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
@@ -169,6 +209,9 @@ module MonitorMixin
obj.mon_initialize
end
+ #
+ # Attempts to enter exclusive section. Returns +false+ if lock fails.
+ #
def try_mon_enter
result = false
Thread.critical = true
@@ -183,6 +226,9 @@ module MonitorMixin
return result
end
+ #
+ # Enters exlusive section.
+ #
def mon_enter
Thread.critical = true
while mon_owner != nil && mon_owner != Thread.current
@@ -195,6 +241,9 @@ module MonitorMixin
Thread.critical = false
end
+ #
+ # Leaves exclusive section.
+ #
def mon_exit
if mon_owner != Thread.current
raise ThreadError, "current thread not owner"
@@ -214,6 +263,11 @@ module MonitorMixin
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
@@ -224,6 +278,9 @@ module MonitorMixin
end
alias synchronize mon_synchronize
+ #
+ # FIXME: This isn't documented in Nutshell.
+ #
def new_cond
return ConditionVariable.new(self)
end
@@ -243,6 +300,25 @@ class Monitor
alias owner mon_owner
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
diff --git a/lib/net/ftp.rb b/lib/net/ftp.rb
index fdd1b5ffa9..585c14b004 100644
--- a/lib/net/ftp.rb
+++ b/lib/net/ftp.rb
@@ -1,13 +1,15 @@
-=begin
-
-= net/ftp.rb
-
-written by Shugo Maeda <shugo@ruby-lang.org>
-
-This library is distributed under the terms of the Ruby license.
-You can freely distribute/modify this library.
-
-=end
+#
+# = net/ftp.rb
+#
+# Written by Shugo Maeda <shugo@ruby-lang.org>.
+#
+# 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"
@@ -20,6 +22,37 @@ module Net
class FTPPermError < FTPError; end
class FTPProtoError < FTPError; end
+ #
+ # 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'
+ #
+ # 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
+ #
+ # == Major Methods
+ #
+ # The following are the methods most likely to be useful to users:
+ # - #connect
+ # - #login (note: <tt>FTP.new</tt> can do both connect and login instead)
+ # - #getbinaryfile
+ # - #gettextfile
+ # - #putbinaryfile
+ # - #puttextfile
+ # - #chdir
+ # - #size
+ # - #rename
+ # - #delete
+ #
class FTP
include MonitorMixin
@@ -28,18 +61,58 @@ module Net
DEFAULT_BLOCKSIZE = 4096
- attr_accessor :binary, :passive, :return_code, :debug_mode, :resume
- attr_reader :welcome, :lastresp
-
+ # 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 +FTP.new+, 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)
- new(host, user, passwd, acct)
+ 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
- @return_code = "\n"
@debug_mode = false
@resume = false
if host
@@ -50,6 +123,15 @@ module Net
end
end
+ def return_code
+ $stderr.puts("warning: Net::FTP#return_code is obsolete and do nothing")
+ return "\n"
+ end
+
+ 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
@@ -60,6 +142,12 @@ module Net
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
+ # +Errno::ECONNREFUSED+) if the connection cannot be established.
+ #
def connect(host, port = FTP_PORT)
if @debug_mode
print "connect: ", host, ", ", port, "\n"
@@ -70,6 +158,9 @@ module Net
end
end
+ #
+ # WRITEME or make private
+ #
def set_socket(sock, get_greeting = true)
synchronize do
@sock = sock
@@ -99,12 +190,7 @@ module Net
def getline
line = @sock.readline # if get EOF, raise EOFError
- if line[-2, 2] == CRLF
- line = line[0 .. -3]
- elsif line[-1] == ?\r or
- line[-1] == ?\n
- line = line[0 .. -2]
- end
+ line.sub!(/(\r\n|\n|\r)\z/n, "")
if @debug_mode
print "get: ", sanitize(line), "\n"
end
@@ -127,18 +213,17 @@ module Net
private :getmultiline
def getresp
- resp = getmultiline
- @lastresp = resp[0, 3]
- c = resp[0]
- case c
- when ?1, ?2, ?3
- return resp
- when ?4
- raise FTPTempError, resp
- when ?5
- raise FTPPermError, resp
+ @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, resp
+ raise FTPProtoError, @last_response
end
end
private :getresp
@@ -151,6 +236,9 @@ module Net
end
private :voidresp
+ #
+ # Sends a command and returns the response.
+ #
def sendcmd(cmd)
synchronize do
putline(cmd)
@@ -158,6 +246,9 @@ module Net
end
end
+ #
+ # Sends a command and expect a response beginning with '2'.
+ #
def voidcmd(cmd)
synchronize do
putline(cmd)
@@ -250,6 +341,14 @@ module Net
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
@@ -271,7 +370,13 @@ module Net
@welcome = resp
end
- def retrbinary(cmd, blocksize, rest_offset = nil)
+ #
+ # 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)
@@ -285,7 +390,13 @@ module Net
end
end
- def retrlines(cmd)
+ #
+ # 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)
@@ -304,7 +415,16 @@ module Net
end
end
- def storbinary(cmd, file, blocksize, rest_offset = nil, &block)
+ #
+ # 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)
@@ -319,7 +439,13 @@ module Net
end
end
- def storlines(cmd, file, &block)
+ #
+ # 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)
@@ -337,8 +463,13 @@ module Net
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)
+ blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data
if @resume
rest_offset = File.size?(localfile)
f = open(localfile, "a")
@@ -357,12 +488,16 @@ module Net
end
end
- def gettextfile(remotefile, localfile = File.basename(remotefile), &block)
+ #
+ # 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|
- line = line + @return_code
- f.write(line)
+ f.puts(line)
yield(line) if block
end
ensure
@@ -370,19 +505,32 @@ module Net
end
end
- def get(localfile, remotefile = File.basename(localfile),
- blocksize = DEFAULT_BLOCKSIZE, &block)
+ #
+ # 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(localfile, remotefile, &block)
+ gettextfile(remotefile, localfile, &block)
else
- getbinaryfile(localfile, remotefile, blocksize, &block)
+ 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)
+ blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: line/data
if @resume
- rest_offset = size(remotefile)
+ begin
+ rest_offset = size(remotefile)
+ rescue Net::FTPPermError
+ rest_offset = nil
+ end
else
rest_offset = nil
end
@@ -395,7 +543,12 @@ module Net
end
end
- def puttextfile(localfile, remotefile = File.basename(localfile), &block)
+ #
+ # 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)
@@ -404,6 +557,10 @@ module Net
end
end
+ #
+ # Tranfers +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
@@ -413,11 +570,17 @@ module Net
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
@@ -430,7 +593,11 @@ module Net
return files
end
- def list(*args, &block)
+ #
+ # 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
@@ -448,6 +615,9 @@ module Net
alias ls list
alias dir list
+ #
+ # Renames a file on the server.
+ #
def rename(fromname, toname)
resp = sendcmd("RNFR " + fromname)
if resp[0] != ?3
@@ -456,6 +626,9 @@ module Net
voidcmd("RNTO " + toname)
end
+ #
+ # Deletes a file on the server.
+ #
def delete(filename)
resp = sendcmd("DELE " + filename)
if resp[0, 3] == "250"
@@ -467,6 +640,9 @@ module Net
end
end
+ #
+ # Changes the (remote) directory.
+ #
def chdir(dirname)
if dirname == ".."
begin
@@ -482,6 +658,9 @@ module Net
voidcmd(cmd)
end
+ #
+ # Returns the size of the given (remote) filename.
+ #
def size(filename)
voidcmd("TYPE I")
resp = sendcmd("SIZE " + filename)
@@ -493,27 +672,43 @@ module Net
MDTM_REGEXP = /^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/
+ #
+ # 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"
@@ -522,6 +717,9 @@ module Net
return resp[4 .. -1]
end
+ #
+ # Aborts the previous command (ABOR command).
+ #
def abort
line = "ABOR" + CRLF
print "put: ABOR\n" if @debug_mode
@@ -533,6 +731,9 @@ module Net
return resp
end
+ #
+ # Returns the status (STAT command).
+ #
def status
line = "STAT" + CRLF
print "put: STAT\n" if @debug_mode
@@ -540,6 +741,9 @@ module Net
return getresp
end
+ #
+ # Issues the MDTM command. TODO: more info.
+ #
def mdtm(filename)
resp = sendcmd("MDTM " + filename)
if resp[0, 3] == "213"
@@ -547,6 +751,9 @@ module Net
end
end
+ #
+ # Issues the HELP command.
+ #
def help(arg = nil)
cmd = "HELP"
if arg
@@ -555,23 +762,39 @@ module Net
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
@@ -673,3 +896,9 @@ module Net
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/lib/net/http.rb b/lib/net/http.rb
index 82274b11dd..fbfa1f6aa1 100644
--- a/lib/net/http.rb
+++ b/lib/net/http.rb
@@ -2,7 +2,8 @@
= net/http.rb
-Copyright (c) 1999-2002 Yukihiro Matsumoto
+Copyright (c) 1999-2003 Yukihiro Matsumoto
+Copyright (c) 1999-2003 Minero Aoki
written & maintained by Minero Aoki <aamine@loveruby.net>.
This file is derived from "http-access.rb".
@@ -563,8 +564,9 @@ module Net
class HTTP < Protocol
- HTTPVersion = '1.1'
+ Revision = %q$Revision$.split[1]
+ HTTPVersion = '1.1'
#
# for backward compatibility
@@ -590,7 +592,6 @@ module Net
end
private_class_method :setimplversion
-
#
# short cut methods
#
@@ -610,6 +611,11 @@ module Net
nil
end
+ #
+ # Use as either of the following:
+ # Net::HTTP.get(uri)
+ # Net::HTTP.get(host, path [, port])
+ #
def HTTP.get( arg1, arg2 = nil, arg3 = nil )
get_response(arg1,arg2,arg3).body
end
@@ -638,13 +644,17 @@ module Net
end
private_class_method :get_by_uri
-
#
- # connection
+ # HTTP session management
#
- protocol_param :default_port, '80'
- protocol_param :socket_type, '::Net::InternetMessageIO'
+ def HTTP.default_port
+ 80
+ end
+
+ def HTTP.socket_type
+ InternetMessageIO
+ end
class << HTTP
def start( address, port = nil, p_addr = nil, p_port = nil, p_user = nil, p_pass = nil, &block )
@@ -660,25 +670,84 @@ module Net
end
end
- def initialize( addr, port = nil )
- super
+ 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 = 30
+ @read_timeout = 60
+
+ @debug_output = nil
+ end
+
+ def inspect
+ "#<#{self.class} #{@address}:#{@port} open=#{active?}>"
+ end
+
+ def set_debug_output( arg ) # :nodoc:
+ @debug_output = arg
end
+ attr_reader :address
+ attr_reader :port
+
+ attr_accessor :open_timeout
+
+ attr_reader :read_timeout
+
+ def read_timeout=( sec )
+ @socket.read_timeout = sec if @socket
+ @read_timeout = sec
+ end
+
+ def started?
+ @started
+ end
+
+ alias active? started?
+
attr_accessor :close_on_empty_response
- private
+ def start
+ raise IOError, 'HTTP session already opened' if @started
+ if block_given?
+ begin
+ do_start
+ return yield(self)
+ ensure
+ finish if @started
+ end
+ end
+ do_start
+ self
+ end
def do_start
- conn_socket
+ @socket = self.class.socket_type.open(conn_address(), conn_port(),
+ @open_timeout, @read_timeout,
+ @debug_output)
+ on_connect
+ @started = true
end
+ private :do_start
- def do_finish
- disconn_socket
+ def on_connect
end
+ private :on_connect
+ def finish
+ raise IOError, 'closing already closed HTTP session' unless @started
+ @socket.close if @socket and not @socket.closed?
+ @socket = nil
+ @started = false
+ nil
+ end
#
# proxy
@@ -778,9 +847,8 @@ module Net
end
end
-
#
- # http operations
+ # HTTP operations
#
public
@@ -882,7 +950,8 @@ module Net
def begin_transport( req )
if @socket.closed?
- reconn_socket
+ @socket.reopen @open_timeout
+ on_connect
end
if @seems_1_0_server
req['connection'] = 'close'
@@ -924,7 +993,6 @@ module Net
false
end
-
#
# utils
#
@@ -948,7 +1016,7 @@ module Net
###
- ### header
+ ### Header
###
module HTTPHeader
@@ -1006,6 +1074,7 @@ module Net
def canonical( k )
k.split(/-/).map {|i| i.capitalize }.join('-')
end
+ private :canonical
def range
s = @header['range'] or return nil
@@ -1075,7 +1144,7 @@ module Net
def range_length
r = self.content_range
- r and r.length
+ r and (r.end - r.begin)
end
def basic_auth( account, password )
@@ -1083,7 +1152,7 @@ module Net
end
def proxy_basic_auth( account, password )
- header['proxy-authorization'] = basic_encode(account, password)
+ @header['proxy-authorization'] = basic_encode(account, password)
end
def basic_encode( account, password )
@@ -1095,7 +1164,7 @@ module Net
###
- ### request
+ ### Request
###
class HTTPGenericRequest
@@ -1112,7 +1181,7 @@ module Net
return unless initheader
initheader.each do |k,v|
key = k.downcase
- $stderr.puts "net/http: WARNING: duplicated HTTP header: #{k}" if @header.key?(key) and $VERBOSE
+ $stderr.puts "net/http: warning: duplicated HTTP header: #{k}" if @header.key?(key) and $VERBOSE
@header[key] = v.strip
end
@header['accept'] ||= '*/*'
@@ -1161,7 +1230,7 @@ module Net
@header.delete 'transfer-encoding'
unless @header['content-type']
- $stderr.puts 'net/http: WARNING: Content-Type did not set; using application/x-www-form-urlencoded' if $VERBOSE
+ $stderr.puts 'net/http: warning: Content-Type did not set; using application/x-www-form-urlencoded' if $VERBOSE
@header['content-type'] = 'application/x-www-form-urlencoded'
end
@@ -1170,25 +1239,24 @@ module Net
end
def request( sock, ver, path )
- sock.writeline sprintf('%s %s HTTP/%s', @method, path, ver)
+ buf = "#{@method} #{path} HTTP/#{ver}\r\n"
canonical_each do |k,v|
- sock.writeline k + ': ' + v
+ buf << k + ': ' + v + "\r\n"
end
- sock.writeline ''
+ buf << "\r\n"
+ sock.write buf
end
end
class HTTPRequest < HTTPGenericRequest
-
def initialize( path, initheader = nil )
super self.class::METHOD,
self.class::REQUEST_HAS_BODY,
self.class::RESPONSE_HAS_BODY,
path, initheader
end
-
end
@@ -1221,11 +1289,33 @@ module Net
end
-
###
- ### response
+ ### Response
###
+ module HTTPExceptions
+ def initialize( msg, res )
+ super msg
+ @response = res
+ end
+ attr_reader :response
+ alias data response
+ end
+ class HTTPError < ProtocolError
+ include HTTPExceptions
+ end
+ class HTTPRetriableError < ProtoRetriableError
+ include HTTPExceptions
+ end
+ # We cannot use the name "HTTPServerError", it is the name of the response.
+ class HTTPServerException < ProtoServerError
+ include HTTPExceptions
+ end
+ class HTTPFatalError < ProtoFatalError
+ include HTTPExceptions
+ end
+
+
class HTTPResponse
# predefine HTTPResponse class to allow inheritance
@@ -1238,30 +1328,29 @@ module Net
end
end
-
class HTTPUnknownResponse < HTTPResponse
HAS_BODY = true
- EXCEPTION_TYPE = ProtocolError
+ EXCEPTION_TYPE = HTTPError
end
class HTTPInformation < HTTPResponse # 1xx
HAS_BODY = false
- EXCEPTION_TYPE = ProtocolError
+ EXCEPTION_TYPE = HTTPError
end
class HTTPSuccess < HTTPResponse # 2xx
HAS_BODY = true
- EXCEPTION_TYPE = ProtocolError
+ EXCEPTION_TYPE = HTTPError
end
class HTTPRedirection < HTTPResponse # 3xx
HAS_BODY = true
- EXCEPTION_TYPE = ProtoRetriableError
+ EXCEPTION_TYPE = HTTPRetriableError
end
class HTTPClientError < HTTPResponse # 4xx
HAS_BODY = true
- EXCEPTION_TYPE = ProtoFatalError
+ EXCEPTION_TYPE = HTTPServerException # for backward compatibility
end
class HTTPServerError < HTTPResponse # 5xx
HAS_BODY = true
- EXCEPTION_TYPE = ProtoServerError
+ EXCEPTION_TYPE = HTTPFatalError # for backward compatibility
end
class HTTPContinue < HTTPInformation # 100
@@ -1509,12 +1598,22 @@ module Net
attr_reader :http_version
attr_reader :code
attr_reader :message
- alias msg message
+ alias msg message # for backward compatibility
def inspect
"#<#{self.class} #{@code} 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: warning: old style assignment found at #{caller(1)[0]}" if $VERBOSE
+ [self, body()]
+ end
+
#
# response <-> exception relationship
#
@@ -1639,15 +1738,15 @@ module Net
end
-
# for backward compatibility
+ class HTTP
+ ProxyMod = ProxyDelta
+ end
module NetPrivate
- HTTPResponse = ::Net::HTTPResponse
- HTTPGenericRequest = ::Net::HTTPGenericRequest
- HTTPRequest = ::Net::HTTPRequest
- HTTPHeader = ::Net::HTTPHeader
+ HTTPRequest = ::Net::HTTPRequest
end
+
HTTPInformationCode = HTTPInformation
HTTPSuccessCode = HTTPSuccess
HTTPRedirectionCode = HTTPRedirection
diff --git a/lib/net/imap.rb b/lib/net/imap.rb
index e31b80a461..2cbb408168 100644
--- a/lib/net/imap.rb
+++ b/lib/net/imap.rb
@@ -1055,6 +1055,40 @@ module Net
@response_handlers.delete(handler)
end
+ def thread(argorithm, search_keys, charset)
+ return thread_internal("THREAD", argorithm, search_keys, charset)
+ end
+
+ def uid_thread(argorithm, search_keys, charset)
+ return thread_internal("UID THREAD", argorithm, search_keys, charset)
+ end
+
+ 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
+
+ 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"
@@ -1322,6 +1356,17 @@ module Net
end
end
+ def thread_internal(cmd, argorithm, 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, argorithm, charset, *search_keys)
+ return @responses.delete("THREAD")[-1]
+ end
+
def normalize_searching_criteria(keys)
keys.collect! do |i|
case i
@@ -1333,6 +1378,125 @@ module Net
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 &&
+ inlen >= 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
def format_data
return @data
@@ -1408,6 +1572,9 @@ module Net
":" + 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
@@ -1435,6 +1602,7 @@ module Net
:to, :cc, :bcc, :in_reply_to, :message_id)
Address = Struct.new(:name, :route, :mailbox, :host)
ContentDisposition = Struct.new(:dsp_type, :param)
+ ThreadMember = Struct.new(:seqno, :children)
class BodyTypeBasic < Struct.new(:media_type, :subtype,
:param, :content_id,
@@ -1618,6 +1786,8 @@ module Net
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
@@ -2244,6 +2414,68 @@ module Net
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
diff --git a/lib/net/pop.rb b/lib/net/pop.rb
index 840aafb46c..09d172df06 100644
--- a/lib/net/pop.rb
+++ b/lib/net/pop.rb
@@ -2,7 +2,8 @@
= net/pop.rb
-Copyright (c) 1999-2002 Yukihiro Matsumoto
+Copyright (c) 1999-2003 Yukihiro Matsumoto
+Copyright (c) 1999-2003 Minero Aoki
written & maintained by Minero Aoki <aamine@loveruby.net>
@@ -17,7 +18,7 @@ $Id$
== What is This Module?
-This module provides your program the functions to retrieve
+This module provides your program the function to retrieve
mails via POP3, Post Office Protocol version 3. For details
of POP3, refer [RFC1939] ((<URL:http://www.ietf.org/rfc/rfc1939.txt>)).
@@ -31,28 +32,29 @@ Replace 'pop3.server.address' your POP3 server address.
require 'net/pop'
- pop = Net::POP3.new( 'pop3.server.address', 110 )
- pop.start( 'YourAccount', 'YourPassword' ) ###
- if pop.mails.empty? then
+ pop = Net::POP3.new('pop.example.com', 110)
+ pop.start('YourAccount', 'YourPassword') # (1)
+ if pop.mails.empty?
puts 'no mail.'
else
i = 0
- pop.each_mail do |m| # or "pop.mails.each ..."
- File.open( 'inbox/' + i.to_s, 'w' ) {|f|
- f.write m.pop
+ pop.each_mail do |m| # or "pop.mails.each ..." # (2)
+ File.open("inbox/#{i}", 'w') {|f|
+ f.write m.pop
}
m.delete
i += 1
end
puts "#{pop.mails.size} mails popped."
end
- pop.finish ###
+ pop.finish # (3)
(1) call Net::POP3#start and start POP session
(2) access mails by using POP3#each_mail and/or POP3#mails
(3) close POP session by calling POP3#finish or use block form #start.
This example is using block form #start to close the session.
+
=== Enshort Code
The example above is very verbose. You can enshort code by using
@@ -61,40 +63,40 @@ alternates POP3.new, POP3#start and POP3#finish.
require 'net/pop'
- Net::POP3.start( 'pop3.server.address', 110 )
- 'YourAccount', 'YourPassword' )
- if pop.mails.empty? then
- puts 'no mail.'
- else
- i = 0
- pop.each_mail do |m| # or "pop.mails.each ..."
- File.open( 'inbox/' + i.to_s, 'w' ) {|f|
- f.write m.pop
- }
- m.delete
- i += 1
- end
- puts "#{pop.mails.size} mails popped."
- end
+ Net::POP3.start('pop.example.com', 110,
+ 'YourAccount', 'YourPassword') {|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') {|f|
+ f.write m.pop
+ }
+ m.delete
+ i += 1
+ end
+ puts "#{pop.mails.size} mails popped."
+ end
}
POP3#delete_all alternates #each_mail and m.delete.
require 'net/pop'
- Net::POP3.start( 'pop3.server.address', 110,
- 'YourAccount', 'YourPassword' ) {|pop|
- if pop.mails.empty? then
- puts 'no mail.'
- else
- i = 0
- pop.delete_all do |m|
- File.open( 'inbox/' + i.to_s, 'w' ) {|f|
- f.write m.pop
- }
- i += 1
- end
- end
+ Net::POP3.start('pop.example.com', 110,
+ 'YourAccount', 'YourPassword') {|pop|
+ if pop.mails.empty?
+ puts 'no mail.'
+ else
+ i = 1
+ pop.delete_all do |m|
+ File.open("inbox/#{i}", 'w') {|f|
+ f.write m.pop
+ }
+ i += 1
+ end
+ end
}
And here is more shorter example.
@@ -102,24 +104,29 @@ And here is more shorter example.
require 'net/pop'
i = 0
- Net::POP3.delete_all( 'pop3.server.address', 110,
- 'YourAccount', 'YourPassword' ) do |m|
- File.open( 'inbox/' + i.to_s, 'w' ) {|f|
- f.write m.pop
+ Net::POP3.delete_all('pop.example.com', 110,
+ 'YourAccount', 'YourPassword') do |m|
+ File.open("inbox/#{i}", 'w') {|f|
+ f.write m.pop
}
i += 1
end
-=== Writing to File directly
+=== Memory Space Issue
All examples above get mail as one big string.
This example does not create such one.
require 'net/pop'
- Net::POP3.delete_all( 'pop3.server.address', 110,
- 'YourAccount', 'YourPassword' ) do |m|
- File.open( 'inbox', 'w' ) {|f|
- m.pop f ####
+
+ i = 1
+ Net::POP3.delete_all('pop.example.com', 110,
+ 'YourAccount', 'YourPassword') do |m|
+ File.open("inbox/#{i}", 'w') {|f|
+ m.pop do |chunk| # get a message little by little.
+ f.write chunk
+ end
+ i += 1
}
end
@@ -131,82 +138,111 @@ You can use utility method, Net::POP3.APOP(). Example:
require 'net/pop'
- # use APOP authentication if $isapop == true
- pop = Net::POP3.APOP($isapop).new( 'apop.server.address', 110 )
- pop.start( YourAccount', 'YourPassword' ) {|pop|
- # Rest code is same.
+ # Use APOP authentication if $isapop == true
+ pop = Net::POP3.APOP($is_apop).new('apop.example.com', 110)
+ pop.start(YourAccount', 'YourPassword') {|pop|
+ # Rest code is same.
}
+=== Fetch Only Selected Mail Using `UIDL' POP Command
-== Net::POP3 class
+If your POP server provides UIDL function,
+you can pop only selected mails from POP server.
+e.g.
-=== Class Methods
+ def need_pop?( id )
+ # determine if we need pop this mail...
+ end
-: new( address, port = 110, apop = false )
- creates a new Net::POP3 object.
- This method does not open TCP connection yet.
+ Net::POP3.start('pop.example.com', 110,
+ 'Your account', 'Your password') {|pop|
+ pop.mails.select {|m| need_pop?(m.unique_id) }.each do |m|
+ do_something(m.pop)
+ end
+ }
-: start( address, port = 110, account, password )
-: start( address, port = 110, account, password ) {|pop| .... }
- equals to Net::POP3.new( address, port ).start( account, password )
+POPMail#unique_id method returns the unique-id of the message (String).
+Normally unique-id is a hash of the message.
- Net::POP3.start( addr, port, account, password ) {|pop|
- pop.each_mail do |m|
- file.write m.pop
- m.delete
- end
+
+== class Net::POP3
+
+=== Class Methods
+
+: new( address, port = 110, isapop = false )
+ creates a new Net::POP3 object.
+ This method does NOT open TCP connection yet.
+
+: start( address, port = 110, account, password, isapop = false )
+: start( address, port = 110, account, password, isapop = false ) {|pop| .... }
+ equals to Net::POP3.new(address, port, isapop).start(account, password).
+ This method raises POPAuthenticationError if authentication is failed.
+
+ # Typical usage
+ Net::POP3.start(addr, port, account, password) {|pop|
+ pop.each_mail do |m|
+ file.write m.pop
+ m.delete
+ end
}
: APOP( is_apop )
- returns Net::APOP class object if IS_APOP is true.
- returns Net::POP3 class object if false.
- Use this method like:
+ returns Net::APOP class object if IS_APOP.
+ Else, IS_APOP. Use this method like:
- # example 1
- pop = Net::POP3::APOP($isapop).new( addr, port )
+ # Example 1
+ pop = Net::POP3::APOP($is_apop).new(addr, port)
- # example 2
- Net::POP3::APOP($isapop).start( addr, port ) {|pop|
- ....
+ # Example 2
+ Net::POP3::APOP($is_apop).start(addr, port) {|pop|
+ ....
}
-: foreach( address, port = 110, account, password ) {|mail| .... }
+: foreach( address, port = 110, account, password, isapop = false ) {|popmail| .... }
starts POP3 protocol and iterates for each POPMail object.
- This method equals to
+ This method equals to:
- Net::POP3.start( address, port, account, password ) {|pop|
- pop.each_mail do |m|
- yield m
- end
+ Net::POP3.start(address, port, account, password) {|pop|
+ pop.each_mail do |m|
+ yield m
+ end
}
- # example
- Net::POP3.foreach( 'your.pop.server', 110,
- 'YourAccount', 'YourPassword' ) do |m|
+ This method raises POPAuthenticationError if authentication is failed.
+
+ # Typical usage
+ Net::POP3.foreach('pop.example.com', 110,
+ 'YourAccount', 'YourPassword') do |m|
file.write m.pop
m.delete if $DELETE
end
-: delete_all( address, port = 110, account, password )
-: delete_all( address, port = 110, account, password ) {|mail| .... }
+: delete_all( address, port = 110, account, password, isapop = false )
+: delete_all( address, port = 110, account, password, isapop = false ) {|popmail| .... }
starts POP3 session and delete all mails.
If block is given, iterates for each POPMail object before delete.
+ This method raises POPAuthenticationError if authentication is failed.
- # example
- Net::POP3.delete_all( addr, nil, 'YourAccount', 'YourPassword' ) do |m|
- m.pop file
+ # Example
+ Net::POP3.delete_all('pop.example.com', 110,
+ 'YourAccount', 'YourPassword') do |m|
+ file.write m.pop
end
-: auth_only( address, port = 110, account, password )
+: auth_only( address, port = 110, account, password, isapop = false )
(just for POP-before-SMTP)
+
opens POP3 session and does autholize and quit.
This method must not be called while POP3 session is opened.
+ This method raises POPAuthenticationError if authentication is failed.
+
+ # Example 1: normal POP3
+ Net::POP3.auth_only('pop.example.com', 110,
+ 'YourAccount', 'YourPassword')
- # example
- Net::POP3.auth_only( 'your.pop3.server',
- nil, # using default (110)
- 'YourAccount',
- 'YourPassword' )
+ # Example 2: APOP
+ Net::POP3.auth_only('pop.example.com', 110,
+ 'YourAccount', 'YourPassword', true)
=== Instance Methods
@@ -217,7 +253,10 @@ You can use utility method, Net::POP3.APOP(). Example:
When called with block, gives a POP3 object to block and
closes the session after block call finish.
-: active?
+ This method raises POPAuthenticationError if authentication is failed.
+
+: started?
+: active? OBSOLETE
true if POP3 session is started.
: address
@@ -242,87 +281,174 @@ You can use utility method, Net::POP3.APOP(). Example:
finishes POP3 session.
If POP3 session had not be started, raises an IOError.
+: n_mails
+ returns the number of mails on the POP server.
+
+: n_bytes
+ returns the bytes of all mails on the POP server.
+
: mails
an array of Net::POPMail objects.
- This array is renewed when session started.
+ This array is renewed when session restarts.
+
+ This method raises POPError if any problem happened.
: each_mail {|popmail| .... }
: each {|popmail| .... }
- is equals to "pop3.mails.each"
+ is equals to:
+
+ pop3.mails.each do |popmail|
+ ....
+ end
+
+ This method raises POPError if any problem happened.
: delete_all
: delete_all {|popmail| .... }
deletes all mails on server.
If called with block, gives mails to the block before deleting.
- # example
+ # Example
n = 1
pop.delete_all do |m|
- File.open("inbox/#{n}") {|f| f.write m.pop }
+ File.open("inbox/#{n}") {|f|
+ f.write m.pop
+ }
n += 1
end
-: auth_only( account, password )
- (just for POP-before-SMTP)
- opens POP3 session and does autholize and quit.
- This method must not be called while POP3 session is opened.
- # example
- pop = Net::POP3.new( 'your.pop3.server' )
- pop.auth_only 'YourAccount', 'YourPassword'
+ This method raises POPError if any problem happened.
: reset
reset the session. All "deleted mark" are removed.
-== Net::APOP
+ This method raises POPError if any problem happened.
+
+: set_debug_output( output )
+ WARNING: This method causes serious security hole.
+ Use this method for only debugging.
+
+ set output stream for debugging.
+
+ # Example
+ pop = Net::POP.new(addr, port)
+ pop.set_debug_output $stderr
+ pop.start(account, passwd) {
+ ....
+ }
+
+
+== class Net::APOP
This class defines no new methods.
Only difference from POP3 is using APOP authentification.
=== Super Class
+
Net::POP3
-== Net::POPMail
+
+== class Net::POPMail
A class of mail which exists on POP server.
=== Instance Methods
-: pop( dest = '' )
- This method fetches a mail and write to 'dest' using '<<' method.
-
- # example
- allmails = nil
- POP3.start( 'your.pop3.server', 110,
- 'YourAccount, 'YourPassword' ) {|pop|
- allmails = pop.mails.collect {|popmail| popmail.pop }
+: pop
+ This method fetches a message as a String.
+
+ This method may raise POPError.
+
+ # Example
+ POP3.start('pop.example.com', 110,
+ 'YourAccount, 'YourPassword') {|pop|
+ n = 1
+ pop.mails.each do |popmail|
+ File.open("inbox/#{n}", 'w') {|f|
+ f.write popmail.pop ####
+ }
+ popmail.delete
+ n += 1
+ end
}
-: pop {|str| .... }
- gives the block part strings of a mail.
-
- # example
- POP3.start( 'localhost', 110 ) {|pop3|
- pop3.each_mail do |m|
- m.pop do |str|
- # do anything
- end
- end
+: pop {|chunk| .... }
+ gives the block parts of the message.
+
+ This method may raise POPError.
+
+ # Example
+ POP3.start('pop.example.com', 110,
+ 'YourAccount, 'YourPassword') {|pop|
+ n = 1
+ pop.mails.each do |popmail|
+ File.open("inbox/#{n}", 'w') {|f|
+ popmail.pop do |chunk| ####
+ f.write chunk
+ end
+ }
+ n += 1
+ end
}
: header
- This method fetches only mail header.
+ fetches the message header.
+
+ This method may raise POPError.
: top( lines )
- This method fetches mail header and LINES lines of body.
+ fetches the message header and LINES lines of body.
+
+ This method may raise POPError.
: delete
- deletes mail on server.
+ deletes the message on the POP server.
+
+ This method may raise POPError.
+
+ # Example
+ POP3.start('pop.example.com', 110,
+ 'YourAccount, 'YourPassword') {|pop|
+ n = 1
+ pop.mails.each do |popmail|
+ File.open("inbox/#{n}", 'w') {|f|
+ f.write popmail.pop
+ }
+ popmail.delete ####
+ n += 1
+ end
+ }
+: length
: size
- mail size (bytes)
+ the length of the message (in octets).
: deleted?
- true if mail was deleted
+ true if mail was deleted.
+
+: unique_id
+ returns the unique-id of the message.
+ Normally unique-id is a hash string of the message.
+
+ This method may raise POPError.
+
+
+== POP3 Related Exception Classes
+
+: POPError
+ POP3 protocol error (reply code "-ERR", except authentication).
+
+ ancestors: ProtocolError (obsolete)
+
+: POPAuthenticationError
+ POP3 authentication error.
+
+ ancestors: POPError, ProtoAuthError (obsolete), ProtocolError (obsolete)
+
+: POPBadResponse
+ Unexpected response got from server.
+
+ ancestors: POPError
=end
@@ -332,92 +458,200 @@ require 'digest/md5'
module Net
+ class POPError < ProtocolError; end
+ class POPAuthenticationError < ProtoAuthError; end
+ class POPBadResponse < POPError; end
+
+
class POP3 < Protocol
- protocol_param :default_port, '110'
- protocol_param :command_type, '::Net::POP3Command'
- protocol_param :apop_command_type, '::Net::APOPCommand'
- protocol_param :mail_type, '::Net::POPMail'
- protocol_param :socket_type, '::Net::InternetMessageIO'
+ Revision = %q$Revision$.split[1]
+ #
+ # Class Parameters
+ #
+
+ def POP3.default_port
+ 110
+ end
+
+ # obsolete
+ def POP3.socket_type
+ Net::InternetMessageIO
+ end
+
+ #
+ # Utilities
+ #
def POP3.APOP( isapop )
isapop ? APOP : POP3
end
def POP3.foreach( address, port = nil,
- account = nil, password = nil, &block )
- start(address, port, account, password) {|pop|
- pop.each_mail(&block)
+ account = nil, password = nil,
+ isapop = false, &block )
+ start(address, port, account, password, isapop) {|pop|
+ pop.each_mail(&block)
}
end
def POP3.delete_all( address, port = nil,
- account = nil, password = nil, &block )
- start(address, port, account, password) {|pop|
- pop.delete_all(&block)
+ account = nil, password = nil,
+ isapop = false, &block )
+ start(address, port, account, password, isapop) {|pop|
+ pop.delete_all(&block)
}
end
def POP3.auth_only( address, port = nil,
- account = nil, password = nil )
- new(address, port).auth_only account, password
+ account = nil, password = nil,
+ isapop = false )
+ new(address, port, isapop).auth_only account, password
end
-
def auth_only( account, password )
- raise IOError, 'opening already opened POP session' if active?
+ raise IOError, 'opening already opened POP session' if started?
start(account, password) {
- # none
+ ;
}
end
-
#
- # connection
+ # Session management
#
- def initialize( addr, port = nil, apop = false )
- super addr, port
+ def POP3.start( address, port = nil,
+ account = nil, password = nil,
+ isapop = false, &block )
+ new(address, port, isapop).start(account, password, &block)
+ end
+
+ 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
- @apop = false
+ @n_mails = nil
+ @n_bytes = nil
end
- private
+ def apop?
+ @apop
+ end
+
+ def inspect
+ "#<#{self.class} #{@address}:#{@port} open=#{@started}>"
+ end
+
+ def set_debug_output( arg )
+ @debug_output = arg
+ end
+
+ attr_reader :address
+ attr_reader :port
+
+ attr_accessor :open_timeout
+ attr_reader :read_timeout
+
+ def read_timeout=( sec )
+ @command.socket.read_timeout = sec if @command
+ @read_timeout = sec
+ end
+
+ def started?
+ @started
+ end
+
+ alias active? started? # obsolete
+
+ def start( account, password )
+ raise IOError, 'POP session already started' if @started
+
+ if block_given?
+ begin
+ do_start account, password
+ return yield(self)
+ ensure
+ finish if @started
+ end
+ else
+ do_start account, password
+ return self
+ end
+ end
def do_start( account, password )
- conn_socket
- conn_command
- @command.auth account, password
+ @socket = self.class.socket_type.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
end
+ private :do_start
- def conn_command
- @command = (@apop ? self.class.apop_command_type :
- self.class.command_type).new(socket())
+ def on_connect
end
+ private :on_connect
- def do_finish
+ def finish
+ raise IOError, 'already closed POP session' unless @started
@mails = nil
- disconn_command
- disconn_socket
+ @command.quit if @command
+ @command = nil
+ @socket.close if @socket and not @socket.closed?
+ @socket = nil
+ @started = false
end
+ def command
+ raise IOError, 'POP session not opened yet' \
+ if not @socket or @socket.closed?
+ @command
+ end
+ private :command
#
- # POP operations
+ # POP protocol wrapper
#
- public
+ def n_mails
+ return @n_mails if @n_mails
+ @n_mails, @n_bytes = command().stat
+ @n_mails
+ end
- def mails
- return @mails if @mails
+ def n_bytes
+ return @n_bytes if @n_bytes
+ @n_mails, @n_bytes = command().stat
+ @n_bytes
+ end
- mails = []
- mailclass = self.class.mail_type
- command().list.each_with_index do |size,idx|
- mails.push mailclass.new(idx, size, command()) if size
+ 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 = mails.freeze
+
+ @mails = command().list.map {|num, size|
+ POPMail.new(num, size, self, command())
+ }
+ @mails.dup
end
def each_mail( &block )
@@ -436,31 +670,29 @@ module Net
def reset
command().rset
mails().each do |m|
- m.instance_eval { @deleted = false }
+ m.instance_eval {
+ @deleted = false
+ }
end
end
-
- def command
- io_check
- super
- end
-
- def io_check
- (not socket() or socket().closed?) and
- raise IOError, 'POP session is not opened yet'
+ # internal use only (called from POPMail#uidl).
+ def set_all_uids
+ command().uidl.each do |num, uid|
+ @mails.find {|m| m.number == num }.uid = uid
+ end
end
- end
+ end # class POP3
- POP = POP3
+ # class aliases
+ POP = POP3
POPSession = POP3
POP3Session = POP3
-
class APOP < POP3
- def APOP.command_type
- APOPCommand
+ def apop?
+ true
end
end
@@ -469,168 +701,207 @@ module Net
class POPMail
- def initialize( n, s, cmd )
- @num = n
- @size = s
+ def initialize( num, len, pop, cmd )
+ @number = num
+ @length = len
+ @pop = pop
@command = cmd
-
@deleted = false
+ @uid = nil
end
- attr :size
+ attr_reader :number
+ attr_reader :length
+ alias size length
def inspect
- "#<#{self.class} #{@num}#{@deleted ? ' deleted' : ''}>"
+ "#<#{self.class} #{@number}#{@deleted ? ' deleted' : ''}>"
end
def pop( dest = '', &block )
- if block
- dest = ReadAdapter.new(block)
+ if block_given?
+ @command.retr(@number, &block)
+ nil
+ else
+ @command.retr(@number) do |chunk|
+ dest << chunk
+ end
+ dest
end
- @command.retr @num, dest
end
- alias all pop
- alias mail pop
+ alias all pop # backward compatibility
+ alias mail pop # backward compatibility
+ # `dest' argument is obsolete
def top( lines, dest = '' )
- @command.top @num, lines, dest
+ @command.top(@number, lines) do |chunk|
+ dest << chunk
+ end
+ dest
end
+ # `dest' argument is obsolete
def header( dest = '' )
- top 0, dest
+ top(0, dest)
end
def delete
- @command.dele @num
+ @command.dele @number
@deleted = true
end
- alias delete! delete
+ alias delete! delete # backward compatibility
def deleted?
@deleted
end
- def uidl
- @command.uidl @num
+ def unique_id
+ return @uid if @uid
+ @pop.set_all_uids
+ @uid
end
- end
+ alias uidl unique_id
+
+ # internal use only (used from POP3#set_all_uids).
+ def uid=( uid )
+ @uid = uid
+ end
+
+ end # class POPMail
- class POP3Command < Command
+ class POP3Command
def initialize( sock )
- super
- atomic {
- check_reply SuccessCode
- }
+ @socket = sock
+ @error_occured = false
+ res = check_response(critical { recv_response() })
+ @apop_stamp = res.slice(/<.+>/)
end
- def auth( account, pass )
- atomic {
- @socket.writeline 'USER ' + account
- check_reply_auth
+ def inspect
+ "#<#{self.class} socket=#{@socket}>"
+ end
- @socket.writeline 'PASS ' + pass
- check_reply_auth
- }
+ def auth( account, password )
+ check_response_auth(critical {
+ check_response_auth(get_response('USER ' + account))
+ get_response('PASS ' + 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
- arr = []
- atomic {
- getok 'LIST'
- @socket.each_list_item do |line|
- m = /\A(\d+)[ \t]+(\d+)/.match(line) or
- raise BadResponse, "illegal response: #{line}"
- arr[m[1].to_i] = m[2].to_i
- end
+ 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
}
- arr
end
- def rset
- atomic {
- getok 'RSET'
- }
+ 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, dest = '' )
- atomic {
- getok sprintf('TOP %d %d', num, lines)
- @socket.read_message_to dest
+ def top( num, lines = 0, &block )
+ critical {
+ getok('TOP %d %d', num, lines)
+ @socket.each_message_chunk(&block)
}
end
- def retr( num, dest = '' )
- atomic {
- getok sprintf('RETR %d', num)
- @socket.read_message_to dest
+ def retr( num, &block )
+ critical {
+ getok('RETR %d', num)
+ @socket.each_message_chunk(&block)
}
end
def dele( num )
- atomic {
- getok sprintf('DELE %d', num)
- }
+ check_response(critical { get_response('DELE %d', num) })
end
- def uidl( num )
- atomic {
- getok(sprintf('UIDL %d', num)).message.split(/ /)[1]
- }
+ 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
- atomic {
- getok 'QUIT'
- }
+ check_response(critical { get_response('QUIT') })
end
private
- def check_reply_auth
- begin
- return check_reply(SuccessCode)
- rescue ProtocolError => err
- raise ProtoAuthError.new('Fail to POP authentication', err.response)
- end
+ def getok( fmt, *fargs )
+ @socket.writeline sprintf(fmt, *fargs)
+ check_response(recv_response())
end
- def get_reply
- str = @socket.readline
-
- if /\A\+/ === str
- Response.new(SuccessCode, str[0,3], str[3, str.size - 3].strip)
- else
- Response.new(ErrorCode, str[0,4], str[4, str.size - 4].strip)
- end
+ def get_response( fmt, *fargs )
+ @socket.writeline sprintf(fmt, *fargs)
+ recv_response()
end
- end
+ def recv_response
+ @socket.readline
+ end
+ def check_response( res )
+ raise POPError, res unless /\A\+OK/i === res
+ res
+ end
- class APOPCommand < POP3Command
+ def check_response_auth( res )
+ raise POPAuthenticationError, res unless /\A\+OK/i === res
+ res
+ end
- def initialize( sock )
- response = super(sock)
- m = /<.+>/.match(response.msg) or
- raise ProtoAuthError.new("not APOP server: cannot login", nil)
- @stamp = m[0]
- end
-
- def auth( account, pass )
- atomic {
- @socket.writeline sprintf('APOP %s %s',
- account,
- Digest::MD5.hexdigest(@stamp + pass))
- check_reply_auth
- }
+ def critical
+ return '+OK dummy ok response' if @error_occured
+ begin
+ return yield()
+ rescue Exception
+ @error_occured = true
+ raise
+ end
end
- end
+ end # class POP3Command
end # module Net
diff --git a/lib/net/protocol.rb b/lib/net/protocol.rb
index 9da1ad8909..d33e93b266 100644
--- a/lib/net/protocol.rb
+++ b/lib/net/protocol.rb
@@ -2,19 +2,20 @@
= net/protocol.rb
-Copyright (c) 1999-2002 Yukihiro Matsumoto
+Copyright (c) 1999-2003 Yukihiro Matsumoto
+Copyright (c) 1999-2003 Minero Aoki
-written & maintained by Minero Aoki <aamine@loveruby.net>
+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.
-NOTE: You can find Japanese version of this document in
-the doc/net directory of the standard ruby interpreter package.
-
$Id$
+WARNING: This file is going to remove.
+Do not rely on the implementation written in this file.
+
=end
require 'socket'
@@ -24,210 +25,16 @@ require 'timeout'
module Net
class Protocol
-
- Version = '1.2.3'
- Revision = '$Revision$'.slice(/[\d\.]+/)
-
-
- class << self
-
- def port
- default_port
- end
-
- private
-
- def protocol_param( name, val )
- module_eval <<-EOS, __FILE__, __LINE__ + 1
- def self.#{name.id2name}
- #{val}
- end
- EOS
- end
-
- end
-
-
- #
- # --- Configuration Staffs for Sub Classes ---
- #
- # class method default_port
- # class method command_type
- # class method socket_type
- #
- # private method do_start
- # private method do_finish
- #
- # private method conn_address
- # private method conn_port
- #
-
-
- def Protocol.start( address, port = nil, *args )
- instance = new(address, port)
- if block_given?
- instance.start(*args) {
- return yield(instance)
- }
- else
- instance.start(*args)
- instance
- end
- end
-
- def initialize( addr, port = nil )
- @address = addr
- @port = port || self.class.default_port
-
- @command = nil
- @socket = nil
-
- @started = false
-
- @open_timeout = 30
- @read_timeout = 60
-
- @debug_output = nil
- end
-
- attr_reader :address
- attr_reader :port
-
- attr_reader :command
- attr_reader :socket
-
- attr_accessor :open_timeout
-
- attr_reader :read_timeout
-
- def read_timeout=( sec )
- @socket.read_timeout = sec if @socket
- @read_timeout = sec
- end
-
- def started?
- @started
- end
-
- alias active? started?
-
- def set_debug_output( arg ) # un-documented
- @debug_output = arg
- end
-
- def inspect
- "#<#{self.class} #{@address}:#{@port} open=#{active?}>"
- end
-
- #
- # open
- #
-
- def start( *args )
- @started and raise IOError, 'protocol has been opened already'
-
- if block_given?
- begin
- do_start(*args)
- @started = true
- return yield(self)
- ensure
- finish if @started
- end
- end
-
- do_start(*args)
- @started = true
- self
- end
-
private
-
- # abstract do_start()
-
- def conn_socket
- @socket = self.class.socket_type.open(
- conn_address(), conn_port(),
- @open_timeout, @read_timeout, @debug_output )
- on_connect
- end
-
- alias conn_address address
- alias conn_port port
-
- def reconn_socket
- @socket.reopen @open_timeout
- on_connect
- end
-
- def conn_command
- @command = self.class.command_type.new(@socket)
- end
-
- def on_connect
- end
-
- #
- # close
- #
-
- public
-
- def finish
- raise IOError, 'closing already closed protocol' unless @started
- do_finish
- @started = false
- nil
- end
-
- private
-
- # abstract do_finish()
-
- def disconn_command
- @command.quit if @command and not @command.critical?
- @command = nil
- end
-
- def disconn_socket
- @socket.close if @socket and not @socket.closed?
- @socket = nil
- end
-
- end
-
- Session = Protocol
-
-
- class Response
-
- def initialize( ctype, code, msg )
- @code_type = ctype
- @code = code
- @message = msg
- super()
- end
-
- attr_reader :code_type
- attr_reader :code
- attr_reader :message
- alias msg message
-
- def inspect
- "#<#{self.class} #{@code}>"
- end
-
- def error!
- raise error_type().new(code + ' ' + @message.dump, self)
- end
-
- def error_type
- @code_type.error_type
+ 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
@@ -238,129 +45,6 @@ module Net
class ProtoRetriableError < ProtocolError; end
ProtocRetryError = ProtoRetriableError
- class ProtocolError
-
- def initialize( msg, resp )
- super msg
- @response = resp
- end
-
- attr_reader :response
- alias data response
-
- def inspect
- "#<#{self.class} #{self.message}>"
- end
-
- end
-
-
- class Code
-
- def initialize( paren, err )
- @parents = [self] + paren
- @error_type = err
- end
-
- def parents
- @parents.dup
- end
-
- attr_reader :error_type
-
- def inspect
- "#<#{self.class} #{sprintf '0x%x', __id__}>"
- end
-
- def ===( response )
- response.code_type.parents.each do |c|
- return true if c == self
- end
- false
- end
-
- def mkchild( err = nil )
- self.class.new(@parents, err || @error_type)
- end
-
- end
-
- ReplyCode = Code.new([], ProtoUnknownError)
- InformationCode = ReplyCode.mkchild(ProtoUnknownError)
- SuccessCode = ReplyCode.mkchild(ProtoUnknownError)
- ContinueCode = ReplyCode.mkchild(ProtoUnknownError)
- ErrorCode = ReplyCode.mkchild(ProtocolError)
- SyntaxErrorCode = ErrorCode.mkchild(ProtoSyntaxError)
- FatalErrorCode = ErrorCode.mkchild(ProtoFatalError)
- ServerErrorCode = ErrorCode.mkchild(ProtoServerError)
- AuthErrorCode = ErrorCode.mkchild(ProtoAuthError)
- RetriableCode = ReplyCode.mkchild(ProtoRetriableError)
- UnknownCode = ReplyCode.mkchild(ProtoUnknownError)
-
-
- class Command
-
- def initialize( sock )
- @socket = sock
- @last_reply = nil
- @atomic = false
- end
-
- attr_accessor :socket
- attr_reader :last_reply
-
- def inspect
- "#<#{self.class} socket=#{@socket.inspect} critical=#{@atomic}>"
- end
-
- # abstract quit()
-
- private
-
- def check_reply( *oks )
- @last_reply = get_reply()
- reply_must @last_reply, *oks
- end
-
- # abstract get_reply()
-
- def reply_must( rep, *oks )
- oks.each do |i|
- return rep if i === rep
- end
- rep.error!
- end
-
- def getok( line, expect = SuccessCode )
- @socket.writeline line
- check_reply expect
- end
-
- #
- # critical session
- #
-
- public
-
- def critical?
- @atomic
- end
-
- def error_ok
- @atomic = false
- end
-
- private
-
- def atomic
- @atomic = true
- ret = yield
- @atomic = false
- ret
- end
-
- end
-
class InternetMessageIO
@@ -368,24 +52,25 @@ module Net
alias open new
end
- def initialize( addr, port, otime = nil, rtime = nil, dout = nil )
+ def initialize( addr, port,
+ open_timeout = nil, read_timeout = nil,
+ debug_output = nil )
@address = addr
@port = port
- @read_timeout = rtime
- @debug_output = dout
-
- @socket = nil
- @rbuf = nil
-
- connect otime
- D 'opened'
+ @read_timeout = read_timeout
+ @debug_output = debug_output
+ @socket = nil
+ @rbuf = nil # read buffer
+ @wbuf = nil # write buffer
+ connect open_timeout
+ LOG 'opened'
end
attr_reader :address
attr_reader :port
def ip_address
- @socket or return ''
+ return '' unless @socket
@socket.addr[3]
end
@@ -393,10 +78,10 @@ module Net
attr_reader :socket
- def connect( otime )
- D "opening connection to #{@address}..."
- timeout(otime) {
- @socket = TCPsocket.new(@address, @port)
+ def connect( open_timeout )
+ LOG "opening connection to #{@address}..."
+ timeout(open_timeout) {
+ @socket = TCPsocket.new(@address, @port)
}
@rbuf = ''
end
@@ -405,19 +90,19 @@ module Net
def close
if @socket
@socket.close
- D 'closed'
+ LOG 'closed'
else
- D 'close call for already closed socket'
+ LOG 'close call for already closed socket'
end
@socket = nil
@rbuf = ''
end
- def reopen( otime = nil )
- D 'reopening...'
+ def reopen( open_timeout = nil )
+ LOG 'reopening...'
close
- connect otime
- D 'reopened'
+ connect open_timeout
+ LOG 'reopened'
end
def closed?
@@ -425,7 +110,7 @@ module Net
end
def inspect
- "#<#{self.class} #{closed? ? 'closed' : 'opened'}>"
+ "#<#{self.class} #{closed?() ? 'closed' : 'opened'}>"
end
###
@@ -434,74 +119,85 @@ module Net
public
- def read( len, dest = '', ignore = false )
- D_off "reading #{len} bytes..."
-
- rsize = 0
+ def read( len, dest = '', ignore_eof = false )
+ LOG "reading #{len} bytes..."
+ LOG_off()
+ read_bytes = 0
begin
- while rsize + @rbuf.size < len
- rsize += rbuf_moveto(dest, @rbuf.size)
+ while read_bytes + @rbuf.size < len
+ read_bytes += rbuf_moveto(dest, @rbuf.size)
rbuf_fill
end
- rbuf_moveto dest, len - rsize
+ rbuf_moveto dest, len - read_bytes
rescue EOFError
- raise unless ignore
+ raise unless ignore_eof
end
-
- D_on "read #{len} bytes"
+ LOG_on()
+ LOG "read #{read_bytes} bytes"
dest
end
def read_all( dest = '' )
- D_off 'reading all...'
-
- rsize = 0
+ LOG 'reading all...'
+ LOG_off()
+ read_bytes = 0
begin
while true
- rsize += rbuf_moveto(dest, @rbuf.size)
+ read_bytes += rbuf_moveto(dest, @rbuf.size)
rbuf_fill
end
rescue EOFError
;
end
-
- D_on "read #{rsize} bytes"
+ LOG_on()
+ LOG "read #{read_bytes} bytes"
dest
end
- def readuntil( target, ignore = false )
+ def readuntil( terminator, ignore_eof = false )
dest = ''
begin
- until idx = @rbuf.index(target)
+ until idx = @rbuf.index(terminator)
rbuf_fill
end
- rbuf_moveto dest, idx + target.size
+ rbuf_moveto dest, idx + terminator.size
rescue EOFError
- raise unless ignore
+ raise unless ignore_eof
rbuf_moveto dest, @rbuf.size
end
dest
end
def readline
- ret = readuntil("\n")
- ret.chop!
- ret
+ readuntil("\n").chop
end
- private
+ 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
- BLOCK_SIZE = 1024
+ private
def rbuf_fill
- until IO.select [@socket], nil, nil, @read_timeout
- on_read_timeout
+ until IO.select([@socket], nil, nil, @read_timeout)
+ raise TimeoutError, "socket read timeout (#{@read_timeout} sec)"
end
- @rbuf << @socket.sysread(BLOCK_SIZE)
- end
-
- def on_read_timeout
- raise TimeoutError, "socket read timeout (#{@read_timeout} sec)"
+ @rbuf << @socket.sysread(1024)
end
def rbuf_moveto( dest, len )
@@ -510,173 +206,147 @@ module Net
len
end
- #
- # message read
- #
-
- public
-
- def read_message_to( dest )
- D_off 'reading text...'
-
- rsize = 0
- while (str = readuntil("\r\n")) != ".\r\n"
- rsize += str.size
- dest << str.sub(/\A\./, '')
- end
-
- D_on "read #{rsize} bytes"
- dest
- end
-
- # private use only (cannot handle 'break')
- def each_list_item
- while (str = readuntil("\r\n")) != ".\r\n"
- yield str.chop
- end
- end
-
-
###
### WRITE
###
- #
- # basic write
- #
-
public
def write( str )
writing {
- do_write str
+ write0 str
}
end
def writeline( str )
writing {
- do_write str + "\r\n"
+ write0 str + "\r\n"
}
end
+ def write_message( src )
+ LOG "writing message from #{src.class}"
+ LOG_off()
+ len = 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 = 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 writing
- @writtensize = 0
+ @written_bytes = 0
@debug_output << '<- ' if @debug_output
yield
@socket.flush
@debug_output << "\n" if @debug_output
- @writtensize
+ bytes = @written_bytes
+ @written_bytes = nil
+ bytes
end
- def do_write( str )
+ def write0( str )
@debug_output << str.dump if @debug_output
- @writtensize += (n = @socket.write(str))
- n
+ len = @socket.write(str)
+ @written_bytes += len
+ len
end
#
- # message write
+ # Reads string from src calling :each, and write to @socket.
+ # Escapes '.' on the each line head.
#
-
- public
-
- def write_message( src )
- D_off "writing text from #{src.class}"
-
- wsize = using_each_crlf_line {
- wpend_in src
- }
-
- D_on "wrote #{wsize} bytes text"
- wsize
- end
-
- def through_message
- D_off 'writing text from block'
-
- wsize = using_each_crlf_line {
- yield WriteAdapter.new(self, :wpend_in)
- }
-
- D_on "wrote #{wsize} bytes text"
- wsize
- end
-
- private
-
- def wpend_in( src )
- line = nil
- pre = @writtensize
+ def write_message_0( src )
+ prev = @written_bytes
each_crlf_line(src) do |line|
- do_write '.' if line[0] == ?.
- do_write line
+ if line[0] == ?.
+ then write0 '.' + line
+ else write0 line
+ end
end
-
- @writtensize - pre
+ @written_bytes - prev
end
+ #
+ # setup @wbuf for each_crlf_line.
+ #
def using_each_crlf_line
writing {
@wbuf = ''
-
yield
-
if not @wbuf.empty? # unterminated last line
if @wbuf[-1] == ?\r
@wbuf.chop!
end
@wbuf.concat "\r\n"
- do_write @wbuf
- elsif @writtensize == 0 # empty src
- do_write "\r\n"
+ write0 @wbuf
+ elsif @written_bytes == 0 # empty src
+ write0 "\r\n"
end
- do_write ".\r\n"
-
+ write0 ".\r\n"
@wbuf = nil
}
end
+ #
+ # extract a CR-LF-terminating-line from @wbuf and yield it.
+ #
def each_crlf_line( src )
- str = m = beg = nil
-
adding(src) do
beg = 0
buf = @wbuf
while buf.index(/\n|\r\n|\r/, beg)
m = Regexp.last_match
- if m.begin(0) == buf.size - 1 and buf[-1] == ?\r
+ if (m.begin(0) == buf.length - 1) and buf[-1] == ?\r
# "...\r" : can follow "\n..."
break
end
- str = buf[ beg ... m.begin(0) ]
+ str = buf[beg ... m.begin(0)]
str.concat "\r\n"
yield str
beg = m.end(0)
end
- @wbuf = buf[ beg ... buf.size ]
+ @wbuf = buf[beg ... buf.length]
end
end
+ #
+ # Reads strings from SRC and add to @wbuf, then yield.
+ #
def adding( src )
- i = s = nil
-
case src
- when String
+ when String # for speeding up.
0.step(src.size - 1, 2048) do |i|
@wbuf << src[i,2048]
yield
end
- when File
+ when File # for speeding up.
while s = src.read(2048)
s[0,0] = @wbuf
@wbuf = s
yield
end
- else
+ else # generic reader
src.each do |s|
@wbuf << s
yield if @wbuf.size > 2048
@@ -691,18 +361,17 @@ module Net
private
- def D_off( msg )
- D msg
- @savedo, @debug_output = @debug_output, nil
+ def LOG_off
+ @save_debug_out = @debug_output
+ @debug_output = nil
end
- def D_on( msg )
- @debug_output = @savedo
- D msg
+ def LOG_on
+ @debug_output = @save_debug_out
end
- def D( msg )
- @debug_output or return
+ def LOG( msg )
+ return unless @debug_output
@debug_output << msg
@debug_output << "\n"
end
@@ -710,11 +379,14 @@ module Net
end
+ #
+ # The writer adapter class
+ #
class WriteAdapter
def initialize( sock, mid )
@socket = sock
- @mid = mid
+ @method_id = mid
end
def inspect
@@ -722,7 +394,7 @@ module Net
end
def write( str )
- @socket.__send__ @mid, str
+ @socket.__send__(@method_id, str)
end
alias print write
@@ -743,6 +415,9 @@ module Net
end
+ #
+ # The reader adapter class for internal use only.
+ #
class ReadAdapter
def initialize( block )
@@ -759,6 +434,11 @@ module Net
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
@@ -766,15 +446,9 @@ module Net
end
- # for backward compatibility
+ # For backward compatibility
module NetPrivate
- Response = ::Net::Response
- Command = ::Net::Command
Socket = ::Net::InternetMessageIO
- BufferedSocket = ::Net::InternetMessageIO
- WriteAdapter = ::Net::WriteAdapter
- ReadAdapter = ::Net::ReadAdapter
end
- BufferedSocket = ::Net::InternetMessageIO
end # module Net
diff --git a/lib/net/smtp.rb b/lib/net/smtp.rb
index 4b70f9e11a..ba4f43c377 100644
--- a/lib/net/smtp.rb
+++ b/lib/net/smtp.rb
@@ -2,7 +2,8 @@
= net/smtp.rb
-Copyright (c) 1999-2002 Yukihiro Matsumoto
+Copyright (c) 1999-2003 Yukihiro Matsumoto
+Copyright (c) 1999-2003 Minero Aoki
written & maintained by Minero Aoki <aamine@loveruby.net>
@@ -33,9 +34,9 @@ FYI: official documentation of internet mail is:
== Examples
-=== Sending Mail
+=== Sending Message
-You must open connection to SMTP server before sending mails.
+You must open connection to SMTP server before sending messages.
First argument is the address of SMTP server, and second argument
is port number. Using SMTP.start with block is the most simple way
to do it. SMTP connection is closed automatically after block is
@@ -43,61 +44,51 @@ executed.
require 'net/smtp'
Net::SMTP.start('your.smtp.server', 25) {|smtp|
- # use smtp object only in this block
+ # use a SMTP object only in this block
}
Replace 'your.smtp.server' by your SMTP server. Normally
your system manager or internet provider is supplying a server
for you.
-Then you can send mail.
+Then you can send messages.
- mail_text = <<END_OF_MAIL
+ msgstr = <<END_OF_MESSAGE
From: Your Name <your@mail.address>
- To: Dest Address <to@some.domain>
- Subject: test mail
+ To: Destination Address <someone@example.com>
+ Subject: test message
Date: Sat, 23 Jun 2001 16:26:43 +0900
- Message-Id: <unique.message.id.string@some.domain>
+ Message-Id: <unique.message.id.string@example.com>
- This is test mail.
- END_OF_MAIL
+ This is a test message.
+ END_OF_MESSAGE
require 'net/smtp'
Net::SMTP.start('your.smtp.server', 25) {|smtp|
- smtp.send_mail mail_text,
- 'your@mail.address',
- 'his_addess@example.com'
+ smtp.send_message msgstr,
+ 'your@mail.address',
+ 'his_addess@example.com'
}
=== Closing Session
-You MUST close SMTP session after sending mails, by calling #finish
-method. You can also use block form of SMTP.start/SMTP#start, which
-closes session automatically. I strongly recommend later one. It is
-more beautiful and simple.
+You MUST close SMTP session after sending messages, by calling #finish
+method:
# using SMTP#finish
smtp = Net::SMTP.start('your.smtp.server', 25)
- smtp.send_mail mail_string, 'from@address', 'to@address'
+ smtp.send_message msgstr, 'from@address', 'to@address'
smtp.finish
+You can also use block form of SMTP.start/SMTP#start. They closes
+SMTP session automatically:
+
# using block form of SMTP.start
Net::SMTP.start('your.smtp.server', 25) {|smtp|
- smtp.send_mail mail_string, 'from@address', 'to@address'
+ smtp.send_message msgstr, 'from@address', 'to@address'
}
-=== Sending Mails From non-String Sources
-
-In an example above I has sent mail from String (here document literal).
-SMTP#send_mail accepts any objects which has "each" method
-like File and Array.
-
- require 'net/smtp'
- Net::SMTP.start('your.smtp.server', 25) {|smtp|
- File.open('Mail/draft/1') {|f|
- smtp.send_mail f, 'your@mail.address', 'to@some.domain'
- }
- }
+I strongly recommend this scheme. This form is more simple and robust.
=== HELO domain
@@ -107,9 +98,26 @@ of SMTP.start/SMTP#start. It is the domain name which you are on
SMTP server will judge if he/she should send or reject
the SMTP session by inspecting HELO domain.
- Net::SMTP.start( 'your.smtp.server', 25,
- 'mail.from.domain' ) {|smtp|
+ 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 methods.
+
+ # 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 Net::SMTP
@@ -117,17 +125,28 @@ the SMTP session by inspecting HELO domain.
: new( address, port = 25 )
creates a new Net::SMTP object.
+ This method does not open TCP connection.
: start( address, port = 25, helo_domain = 'localhost.localdomain', account = nil, password = nil, authtype = nil )
: start( address, port = 25, helo_domain = 'localhost.localdomain', account = nil, password = nil, authtype = nil ) {|smtp| .... }
- is equal to
+ is equal to:
Net::SMTP.new(address,port).start(helo_domain,account,password,authtype)
# example
- Net::SMTP.start( 'your.smtp.server' ) {
- smtp.send_mail mail_string, 'from@mail.address', 'dest@mail.address'
+ Net::SMTP.start('your.smtp.server') {
+ smtp.send_message msgstr, 'from@example.com', ['dest@example.com']
}
+ This method may raise:
+
+ * Net::SMTPAuthenticationError
+ * Net::SMTPServerBusy
+ * Net::SMTPSyntaxError
+ * Net::SMTPFatalError
+ * Net::SMTPUnknownError
+ * IOError
+ * TimeoutError
+
=== Instance Methods
: start( helo_domain = <local host name>, account = nil, password = nil, authtype = nil )
@@ -140,12 +159,29 @@ the SMTP session by inspecting HELO domain.
close session after block call finished.
If both of account and password are given, is trying to get
- authentication by using AUTH command. :plain or :cram_md5 is
- allowed for AUTHTYPE.
+ authentication by using AUTH command. AUTHTYPE is an either of
+ :login, :plain, and :cram_md5.
-: active?
+ This method may raise:
+
+ * Net::SMTPAuthenticationError
+ * Net::SMTPServerBusy
+ * Net::SMTPSyntaxError
+ * Net::SMTPFatalError
+ * Net::SMTPUnknownError
+ * IOError
+ * TimeoutError
+
+: started?
+: active? OBSOLETE
true if SMTP session is started.
+: esmtp?
+ true if the SMTP object uses ESMTP.
+
+: esmtp=(b)
+ set wheather SMTP should use ESMTP.
+
: address
the address to connect
@@ -160,62 +196,128 @@ the SMTP session by inspecting HELO domain.
: read_timeout
: read_timeout=(n)
- seconds to wait until reading one block (by one read(1) call).
+ seconds to wait until reading one block (by one read(2) call).
If SMTP object cannot open a conection in this seconds,
it raises TimeoutError exception.
: finish
finishes SMTP session.
If SMTP session had not started, raises an IOError.
+ If SMTP session timed out, raises TimeoutError.
-: send_mail( mailsrc, from_addr, *to_addrs )
- This method sends MAILSRC as mail. A SMTP object read strings
- from MAILSRC by calling "each" iterator, with converting them
- into CRLF ("\r\n") terminated string when write.
+: send_message( msgstr, from_addr, *dest_addrs )
+: send_mail( msgstr, from_addr, *dest_addrs )
+: sendmail( msgstr, from_addr, *dest_addrs ) OBSOLETE
+ sends a String MSGSTR. If a single CR ("\r") or LF ("\n") found
+ in the MEGSTR, converts it to the CR LF pair. You cannot send a
+ binary message with this class.
FROM_ADDR must be a String, representing source mail address.
TO_ADDRS must be Strings or an Array of Strings, representing
destination mail addresses.
# example
- Net::SMTP.start( 'your.smtp.server' ) {|smtp|
- smtp.send_mail mail_string,
- 'from@mail.address',
- 'dest@mail.address' 'dest2@mail.address'
+ Net::SMTP.start('smtp.example.com') {|smtp|
+ smtp.send_message msgstr,
+ 'from@example.com',
+ ['dest@example.com', 'dest2@example.com']
}
-: ready( from_addr, *to_addrs ) {|adapter| .... }
- This method stands by the SMTP object for sending mail and
- gives adapter object to the block. ADAPTER has these 5 methods:
-
- puts print printf write <<
+ This method may raise:
+
+ * Net::SMTPServerBusy
+ * Net::SMTPSyntaxError
+ * Net::SMTPFatalError
+ * Net::SMTPUnknownError
+ * IOError
+ * TimeoutError
+
+: open_message_stream( from_addr, *dest_addrs ) {|stream| .... }
+: ready( from_addr, *dest_addrs ) {|stream| .... } OBSOLETE
+ opens a message writer stream and gives it to the block.
+ 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") found in the message,
+ converts it to the CR LF pair. You cannot send a binary
+ message with this class.
FROM_ADDR must be a String, representing source mail address.
TO_ADDRS must be Strings or an Array of Strings, representing
destination mail addresses.
# example
- Net::SMTP.start( 'your.smtp.server', 25 ) {|smtp|
- smtp.ready( 'from@mail.addr', 'dest@mail.addr' ) {|f|
- f.puts 'From: aamine@loveruby.net'
- f.puts 'To: someone@somedomain.org'
- f.puts 'Subject: test mail'
- f.puts
- f.puts 'This is test mail.'
- }
+ Net::SMTP.start('smtp.example.com', 25) {|smtp|
+ smtp.open_message_stream('from@example.com', ['dest@example.com']) {|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.'
+ }
}
-== Exceptions
+ This method may raise:
+
+ * Net::SMTPServerBusy
+ * Net::SMTPSyntaxError
+ * Net::SMTPFatalError
+ * Net::SMTPUnknownError
+ * IOError
+ * TimeoutError
+
+: set_debug_output( output )
+ 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 {
+ ....
+ }
+
+
+== SMTP Related Exception Classes
+
+: Net::SMTPAuthenticationError
+ SMTP authentication error.
+
+ ancestors: SMTPError, ProtoAuthError (obsolete), ProtocolError (obsolete)
+
+: Net::SMTPServerBusy
+ Temporal error; error number 420/450.
+
+ ancestors: SMTPError, ProtoServerError (obsolete), ProtocolError (obsolete)
+
+: Net::SMTPSyntaxError
+ SMTP command syntax error (error number 500)
+
+ ancestors: SMTPError, ProtoSyntaxError (obsolete), ProtocolError (obsolete)
+
+: Net::SMTPFatalError
+ Fatal error (error number 5xx, except 500)
-SMTP objects raise these exceptions:
-: Net::ProtoSyntaxError
- syntax error (errno.500)
-: Net::ProtoFatalError
- fatal error (errno.550)
-: Net::ProtoUnknownError
- unknown error. (is probably bug)
-: Net::ProtoServerBusy
- temporary error (errno.420/450)
+ ancestors: SMTPError, ProtoFatalError (obsolete), ProtocolError (obsolete)
+
+: Net::SMTPUnknownError
+ Unexpected reply code returned from server
+ (might be a bug of this library).
+
+ ancestors: SMTPError, ProtoUnkownError (obsolete), ProtocolError (obsolete)
=end
@@ -225,16 +327,49 @@ require 'digest/md5'
module Net
- class SMTP < Protocol
+ module SMTPError
+ # This *class* is module for some reason.
+ # In ruby 1.9.x, this module becomes a class.
+ end
+ class SMTPAuthenticationError < ProtoAuthError
+ include SMTPError
+ end
+ class SMTPServerBusy < ProtoServerError
+ include SMTPError
+ end
+ class SMTPSyntaxError < ProtoSyntaxError
+ include SMTPError
+ end
+ class SMTPFatalError < ProtoFatalError
+ include SMTPError
+ end
+ class SMTPUnknownError < ProtoUnknownError
+ include SMTPError
+ end
- protocol_param :default_port, '25'
- protocol_param :command_type, '::Net::SMTPCommand'
- protocol_param :socket_type, '::Net::InternetMessageIO'
-
- def initialize( addr, port = nil )
- super
+ class SMTP
+
+ Revision = %q$Revision$.split[1]
+
+ def SMTP.default_port
+ 25
+ end
+
+ 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
+
+ def inspect
+ "#<#{self.class} #{@address}:#{@port} started=#{@started}>"
end
def esmtp?
@@ -247,201 +382,272 @@ module Net
alias esmtp esmtp?
- private
+ attr_reader :address
+ attr_reader :port
- def do_start( helo = 'localhost.localdomain',
- user = nil, secret = nil, authtype = nil )
- conn_socket
- conn_command
+ attr_accessor :open_timeout
+ attr_reader :read_timeout
+
+ def read_timeout=( sec )
+ @socket.read_timeout = sec if @socket
+ @read_timeout = sec
+ end
+
+ def set_debug_output( arg )
+ @debug_output = arg
+ end
+
+ #
+ # SMTP session control
+ #
+
+ def SMTP.start( address, port = nil,
+ helo = 'localhost.localdomain',
+ user = nil, secret = nil, authtype = nil,
+ &block)
+ new(address, port).start(helo, user, secret, authtype, &block)
+ end
+
+ def started?
+ @started
+ end
+
+ def start( helo = 'localhost.localdomain',
+ user = nil, secret = nil, authtype = nil )
+ if block_given?
+ begin
+ do_start(helo, user, secret, authtype)
+ return yield(self)
+ ensure
+ finish if @started
+ 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.open(@address, @port,
+ @open_timeout, @read_timeout,
+ @debug_output)
+ check_response(critical { recv_response() })
begin
if @esmtp
- command().ehlo helo
+ ehlo helodomain
else
- command().helo helo
+ helo helodomain
end
rescue ProtocolError
if @esmtp
@esmtp = false
- command().error_ok
+ @error_occured = false
retry
- else
- raise
end
+ raise
end
-
- if user or secret
- raise ArgumentError, 'both of account and password are required'\
- unless user and secret
- mid = 'auth_' + (authtype || 'cram_md5').to_s
- raise ArgumentError, "wrong auth type #{authtype}"\
- unless command().respond_to?(mid)
- command().__send__ mid, user, secret
- end
+ authenticate user, secret, authtype if user
end
-
- def do_finish
- disconn_command
- disconn_socket
+ private :do_start
+
+ def finish
+ raise IOError, 'closing already closed SMTP session' unless @started
+ quit if @socket and not @socket.closed? and not @error_occured
+ @socket.close if @socket and not @socket.closed?
+ @socket = nil
+ @error_occured = false
+ @started = false
end
-
#
- # SMTP operations
+ # message send
#
public
- def send_mail( mailsrc, from_addr, *to_addrs )
- do_ready from_addr, to_addrs.flatten
- command().write_mail mailsrc
+ def send_message( msgstr, from_addr, *to_addrs )
+ send0(from_addr, to_addrs.flatten) {
+ @socket.write_message msgstr
+ }
end
- alias sendmail send_mail
+ alias send_mail send_message
+ alias sendmail send_message # obsolete
- def ready( from_addr, *to_addrs, &block )
- do_ready from_addr, to_addrs.flatten
- command().through_mail(&block)
+ def open_message_stream( from_addr, *to_addrs, &block )
+ send0(from_addr, to_addrs.flatten) {
+ @socket.write_message_by_block(&block)
+ }
end
+ alias ready open_message_stream # obsolete
+
private
- def do_ready( from_addr, to_addrs )
+ def send0( from_addr, to_addrs )
+ raise IOError, 'closed session' unless @socket
raise ArgumentError, 'mail destination does not given' if to_addrs.empty?
- command().mailfrom from_addr
- command().rcpt to_addrs
- end
-
- end
+ 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
- SMTPSession = SMTP
+ 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
+ #
- class SMTPCommand < Command
+ private
- def initialize( sock )
- super
- atomic {
- check_reply SuccessCode
- }
+ def check_auth_args( user, secret, authtype )
+ raise ArgumentError, 'both of 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)
end
- def helo( domain )
- atomic {
- getok sprintf('HELO %s', domain)
- }
+ def authenticate( user, secret, authtype )
+ __send__("auth_#{authtype || 'cram_md5'}", user, secret)
end
- def ehlo( domain )
- atomic {
- getok sprintf('EHLO %s', domain)
- }
+ 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
- # "PLAIN" authentication [RFC2554]
- def auth_plain( user, secret )
- atomic {
- getok sprintf('AUTH PLAIN %s',
- ["\0#{user}\0#{secret}"].pack('m').chomp)
+ 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
- # "CRAM-MD5" authentication [RFC2195]
def auth_cram_md5( user, secret )
- atomic {
- rep = getok('AUTH CRAM-MD5', ContinueCode)
- challenge = rep.msg.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)
-
- getok [user + ' ' + tmp].pack('m').chomp
+ # 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 mailfrom( fromaddr )
- atomic {
- getok sprintf('MAIL FROM:<%s>', fromaddr)
- }
+ def base64_encode( str )
+ # expects "str" may not become too long
+ [str].pack('m').gsub(/\s+/, '')
end
- def rcpt( toaddrs )
- toaddrs.each do |i|
- atomic {
- getok sprintf('RCPT TO:<%s>', i)
- }
- end
+ #
+ # SMTP command dispatcher
+ #
+
+ private
+
+ def helo( domain )
+ getok('HELO %s', domain)
end
- def write_mail( src )
- atomic {
- getok 'DATA', ContinueCode
- @socket.write_message src
- check_reply SuccessCode
- }
+ def ehlo( domain )
+ getok('EHLO %s', domain)
end
- def through_mail( &block )
- atomic {
- getok 'DATA', ContinueCode
- @socket.through_message(&block)
- check_reply SuccessCode
- }
+ def mailfrom( fromaddr )
+ getok('MAIL FROM:<%s>', fromaddr)
+ end
+
+ def rcptto( to )
+ getok('RCPT TO:<%s>', to)
end
def quit
- atomic {
- getok 'QUIT'
- }
+ getok('QUIT')
end
+ #
+ # row level library
+ #
+
private
- def get_reply
- arr = read_reply
- stat = arr[0][0,3]
-
- klass = case stat[0]
- when ?2 then SuccessCode
- when ?3 then ContinueCode
- when ?4 then ServerErrorCode
- when ?5 then
- case stat[1]
- when ?0 then SyntaxErrorCode
- when ?3 then AuthErrorCode
- when ?5 then FatalErrorCode
- end
- end
- klass ||= UnknownCode
-
- Response.new(klass, stat, arr.join(''))
+ 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 read_reply
- arr = []
+ def recv_response
+ res = ''
while true
- str = @socket.readline
- break unless str[3] == ?- # "210-PIPELINING"
- arr.push str
+ line = @socket.readline
+ res << line << "\n"
+ break unless line[3] == ?- # "210-PIPELINING"
end
- arr.push str
+ res
+ end
- arr
+ def check_response( res, allow_continue = false )
+ return res if /\A2/ === res
+ return res if allow_continue and /\A354/ === res
+ err = case res
+ when /\A4/ then SMTPServerBusy
+ when /\A50/ then SMTPSyntaxError
+ when /\A55/ then SMTPFatalError
+ else SMTPUnknownError
+ end
+ raise err, res
end
- 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
- # for backward compatibility
- module NetPrivate
- SMTPCommand = ::Net::SMTPCommand
- end
+ SMTPSession = SMTP
end # module Net
diff --git a/lib/net/telnet.rb b/lib/net/telnet.rb
index 62a8246d11..89bb8ef044 100644
--- a/lib/net/telnet.rb
+++ b/lib/net/telnet.rb
@@ -4,7 +4,7 @@
net/telnet.rb - simple telnet client library
-Wakou Aoyama <wakou@fsinet.or.jp>
+Wakou Aoyama <wakou@ruby-lang.org>
=== MAKE NEW TELNET OBJECT
@@ -90,7 +90,7 @@ of cource, set sync=true or flush is necessary.
Telnet#puts() adds "\n" to the last of "string".
-WARNING: Telnet#print() NOT adds "\n" to the last of "string", in the future.
+CAUTION: Telnet#print() NOT adds "\n" to the last of "string".
If "Telnetmode" option is true, then escape IAC code ("\xFF"). If
"Binmode" option is false, then convert "\n" to EOL(end of line) code.
@@ -480,6 +480,14 @@ module Net
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
@@ -504,7 +512,7 @@ module Net
end
end
- def _print(string)
+ def print(string)
string = string.gsub(/#{IAC}/no, IAC + IAC) if @options["Telnetmode"]
if @options["Binmode"]
@@ -524,15 +532,7 @@ module Net
end
def puts(string)
- self._print(string + "\n")
- end
-
- def print(string)
- if $VERBOSE
- $stderr.puts 'WARNING: Telnet#print("string") NOT adds "\n" to the last of "string", in the future.'
- $stderr.puts ' cf. Telnet#puts().'
- end
- self.puts(string)
+ self.print(string + "\n")
end
def cmd(options)
diff --git a/lib/observer.rb b/lib/observer.rb
index 161297e70c..64c7d81351 100644
--- a/lib/observer.rb
+++ b/lib/observer.rb
@@ -1,8 +1,125 @@
-# Observable Mixin
-#
-# Observers must respond to update
+#
+# 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
@@ -10,12 +127,25 @@ module Observable
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
@@ -23,9 +153,18 @@ module Observable
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
@@ -33,6 +172,12 @@ module Observable
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
@@ -43,4 +188,5 @@ module Observable
@observer_state = false
end
end
+
end
diff --git a/lib/open-uri.rb b/lib/open-uri.rb
index 54afb4d88b..dc2f675f24 100644
--- a/lib/open-uri.rb
+++ b/lib/open-uri.rb
@@ -58,31 +58,46 @@ require 'uri'
require 'stringio'
require 'time'
-module OpenURI
- def OpenURI.open_dispatch(name, *rest, &block) #:nodoc:
- DispatchTable.each {|cond, meth|
- return meth.call(name, *rest, &block) if cond === name
- }
- return open_uri_original_open(name, *rest, &block)
+module Kernel
+ private
+ alias open_uri_original_open open # :nodoc:
+
+ # makes possible to open URIs.
+ # If the first argument is URI::HTTP, URI::FTP or
+ # String beginning with http:// or ftp://,
+ # the URI is opened.
+ # The opened file object is extended by OpenURI::Meta.
+ def open(name, *rest, &block)
+ if name.respond_to?("open")
+ name.open(*rest, &block)
+ elsif name.respond_to?("to_str") && %r{\A(http|ftp)://} =~ name
+ OpenURI.open_uri(name, *rest, &block)
+ else
+ open_uri_original_open(name, *rest, &block)
+ end
end
+ module_function :open
+end
- def OpenURI.open_uri(name, *rest) #:nodoc:
- uri = URI::Generic === name ? name : URI.parse(name)
+module OpenURI
+ 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
- if !rest.empty? && Hash === rest.first
- options = rest.shift
- end
- if !rest.empty?
- raise ArgumentError.new("extra arguments")
- 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?
unless mode == nil ||
- mode == 'r' || mode == 'rb'
+ mode == 'r' || mode == 'rb' ||
mode == O_RDONLY
raise ArgumentError.new("invalid access mode #{mode} (#{uri.class} resource is read only.)")
end
@@ -99,7 +114,7 @@ module OpenURI
end
end
- def OpenURI.open_loop(uri, options) #:nodoc:
+ def OpenURI.open_loop(uri, options) # :nodoc:
header = {}
options.each {|k, v|
if String === k
@@ -130,7 +145,13 @@ module OpenURI
uri.direct_open(buf, header)
end
rescue Redirect
- uri = $!.uri
+ loc = $!.uri
+ if loc.relative?
+ # Although it violates RFC 2616, Location: field may have relative URI.
+ # It is converted to absolute URI using uri.
+ loc = uri + loc
+ end
+ uri = loc
raise "HTTP redirection loop: #{uri}" if uri_set.include? uri.to_s
uri_set[uri.to_s] = true
retry
@@ -140,14 +161,9 @@ module OpenURI
io
end
- DispatchTable = [
- [URI::HTTP, method(:open_uri)],
- [URI::FTP, method(:open_uri)],
- [%r{\A(http|ftp)://}, method(:open_uri)],
- ]
-
- class Redirect < StandardError #:nodoc:
+ class Redirect < StandardError # :nodoc:
def initialize(uri)
+ super("redirection to #{uri.to_s}")
@uri = uri
end
attr_reader :uri
@@ -161,7 +177,7 @@ module OpenURI
attr_reader :io
end
- class Buffer #:nodoc:
+ class Buffer # :nodoc:
def initialize
@io = StringIO.new
end
@@ -186,7 +202,7 @@ module OpenURI
# Mixin for holding meta-information.
module Meta
- def Meta.init(obj, src=nil) #:nodoc:
+ def Meta.init(obj, src=nil) # :nodoc:
obj.extend Meta
obj.instance_eval {
@base_uri = nil
@@ -212,7 +228,7 @@ module OpenURI
# The Hash keys are downcased for canonicalization.
attr_reader :meta
- def meta_add_field(name, value) #:nodoc:
+ def meta_add_field(name, value) # :nodoc:
@meta[name.downcase] = value
end
@@ -230,7 +246,7 @@ module OpenURI
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:
+ def content_type_parse # :nodoc:
v = @meta['content-type']
if v && %r{\A#{RE_LWS}?(#{RE_TOKEN})#{RE_LWS}?/(#{RE_TOKEN})#{RE_LWS}?(#{RE_PARAMETERS})\z}o =~ v
type = $1.downcase
@@ -256,10 +272,20 @@ module OpenURI
# 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 && @base_uri.scheme == 'http'
"iso-8859-1" # RFC2616 3.7.1
@@ -284,8 +310,8 @@ module OpenURI
# Mixin for URIs.
module OpenRead
# opens the URI.
- def open(options={}, &block)
- OpenURI.open_uri(self, options, &block)
+ def open(*rest, &block)
+ OpenURI.open_uri(self, *rest, &block)
end
# reads a content of the URI.
@@ -327,11 +353,11 @@ module URI
end
class HTTP
- def direct_open(buf, header) #:nodoc:
+ def direct_open(buf, header) # :nodoc:
proxy_open(buf, request_uri, header)
end
- def proxy_open(buf, uri, header) #:nodoc:
+ def proxy_open(buf, uri, header) # :nodoc:
require 'net/http'
resp = Net::HTTP.start(self.host, self.port) {|http|
http.get(uri.to_s, header) {|str| buf << str}
@@ -356,7 +382,7 @@ module URI
end
class FTP
- def direct_open(buf, header) #:nodoc:
+ def direct_open(buf, header) # :nodoc:
require 'net/ftp'
# xxx: header is discarded.
# todo: extract user/passwd from .netrc.
@@ -374,17 +400,3 @@ module URI
include OpenURI::OpenRead
end
end
-
-module Kernel
- private
- alias open_uri_original_open open
-
- # makes possible to open URIs.
- # If the first argument is URI::HTTP, URI::FTP or
- # String beginning with http:// or ftp://,
- # the URI is opened.
- # The opened file object is extended by OpenURI::Meta.
- def open(name, *rest, &block)
- OpenURI.open_dispatch(name, *rest, &block)
- end
-end
diff --git a/lib/optparse.rb b/lib/optparse.rb
index ad47d686e6..50d72f8a2e 100644
--- a/lib/optparse.rb
+++ b/lib/optparse.rb
@@ -142,12 +142,24 @@ Individual switch class.
def self.guess(arg)
case arg
when ""
- self
- when /\A[=\s]?\[/
- Switch::OptionalArgument
+ t = self
+ when /\A=?\[/
+ t = Switch::OptionalArgument
+ when /\A\s+\[/
+ t = Switch::PlacedArgument
else
- Switch::RequiredArgument
+ 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
=begin private
@@ -184,17 +196,17 @@ Individual switch class.
else
m = m.to_a
s = m[0]
- return nil, *m unless String === s
+ return nil, m unless String === s
end
raise InvalidArgument, arg unless arg.rindex(s, 0)
- return nil, *m if s.length == arg.length
+ return nil, m if s.length == arg.length
yield(InvalidArgument, arg) # didn't match whole arg
- return arg[s.length..-1], *m
+ return arg[s.length..-1], m
end
private :parse_arg
-=begin
---- OptionParser::Switch#parse(arg, val) {semi-error handler}
+=begin private
+--- OptionParser::Switch#conv_arg(arg, val) {semi-error handler}
Parses argument, convert and returns ((|arg|)), ((|block|)) and
result of conversion.
: Arguments to ((|@conv|))
@@ -208,14 +220,19 @@ Individual switch class.
: (({block}))
(({yields})) at semi-error condition, instead of raises exception.
=end #'#"#`#
- def parse(arg, *val)
+ def conv_arg(arg, val = nil)
if block
- val = conv.yield(*val) if conv
+ if conv
+ val = conv.call(*val)
+ else
+ val = *val
+ end
return arg, block, val
else
return arg, nil
end
end
+ private :conv_arg
=begin private
--- OptionParser::Switch#summarize(sdone, ldone, width, max, indent)
@@ -282,7 +299,12 @@ Switch that takes no arguments.
class NoArgument < self
def parse(arg, argv, &error)
yield(NeedlessArgument, arg) if arg
- super(arg)
+ conv_arg(arg)
+ end
+ def self.incompatible_argument_styles(*)
+ end
+ def self.pattern
+ Object
end
end
@@ -301,12 +323,7 @@ Switch that takes an argument.
raise MissingArgument if argv.empty?
arg = argv.shift
end
- super(*parse_arg(arg, &error))
- end
- def self.guess(arg)
- self >= (t = super) or
- raise ArgumentError, "#{arg}: incompatible argument styles\n #{self}, #{t}"
- t
+ conv_arg(*parse_arg(arg, &error))
end
end
@@ -322,15 +339,26 @@ Switch that can omit argument.
class OptionalArgument < self
def parse(arg, argv, &error)
if arg
- super(*parse_arg(arg, &error))
+ conv_arg(*parse_arg(arg, &error))
else
- super(arg)
+ conv_arg(arg)
end
end
- def self.guess(arg)
- self >= (t = super) or
- raise ArgumentError, "#{arg}: incompatible argument styles\n #{self}, #{t}"
- t
+ end
+
+ class PlacedArgument < self
+ 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
+ argv.shift
+ else
+ val[0] = nil
+ end
+ val
end
end
end
@@ -379,7 +407,7 @@ summary feature.
--- OptionParser::List#reject(type)
see ((<OptionParser.reject>)).
=end #'#"#`#
- def accept(t, pat = nil, &block)
+ def accept(t, pat = /.*/, &block)
if pat
pat.respond_to?(:match) or raise TypeError, "has no `match'"
else
@@ -454,7 +482,7 @@ summary feature.
searching list.
: ((|k|))
searching key.
- : (({Block}))
+ : (({block}))
yielded with the found value when succeeded.
=end #'#"#`#
def search(id, key)
@@ -475,7 +503,7 @@ summary feature.
searching key.
: ((|*pat|))
optional pattern for completion.
- : (({Block}))
+ : (({block}))
yielded with the found value when succeeded.
=end #'#"#`#
def complete(id, opt, *pat, &block)
@@ -606,7 +634,7 @@ Default options, which never appear in option summary.
summary width.
: ((|indent|))
summary indent.
- : (({Block}))
+ : (({block}))
to be evaluated in the new instance context.
=end #'#"#`#
def self.with(*args, &block)
@@ -642,7 +670,7 @@ Default options, which never appear in option summary.
summary width.
: ((|indent|))
summary indent.
- : (({Block}))
+ : (({block}))
to be evaluated in the new instance context.
=end #'#"#`#
def initialize(banner = nil, width = 32, indent = ' ' * 4)
@@ -681,7 +709,7 @@ Default options, which never appear in option summary.
argument class specifier, any object including Class.
: ((|pat|))
pattern for argument, defaulted to ((|t|)) if it respond to (({match})).
- : (({Block}))
+ : (({block}))
receives argument string and should be convert to desired class.
=end #'#"#`#
def accept(*args, &blk) top.accept(*args, &blk) end
@@ -814,7 +842,7 @@ Default options, which never appear in option summary.
maximum length allowed for left side. Defaulted to (({((|width|)) - 1}))
: ((|indent|))
indentation. Defaulted to ((|@summary_indent|))
- : (({Block}))
+ : (({block}))
yields with each line if called as iterator.
=end #'#"#`#
def summarize(to = [], width = @summary_width, max = width - 1, indent = @summary_indent, &blk)
@@ -827,8 +855,7 @@ Default options, which never appear in option summary.
--- OptionParser#to_s
Returns option summary string.
=end #'#"#`#
- def to_str; summarize(banner.to_s.sub(/\n?\z/, "\n")) end
- alias to_s to_str
+ def to_s; summarize(banner.to_s.sub(/\n?\z/, "\n")) end
=begin
--- OptionParser#to_a
@@ -871,7 +898,7 @@ Default options, which never appear in option summary.
argument style and description.
: "description", ...
((*description*)) for this option.
- : (({Block}))
+ : (({block}))
((*handler*)) to convert option argument to arbitrary (({Class})).
=end #'#"#`#
=begin private
@@ -948,55 +975,64 @@ Default options, which never appear in option summary.
raise ArgumentError, "unsupported argument type: #{o}"
when *ArgumentStyle.keys
style = notwice(ArgumentStyle[o], style, 'style')
- when /^--no-([^][=\s]*)(.+)?/
+ 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)
+ default_pattern, conv = search(:atype, FalseClass) unless default_pattern
ldesc << "--no-#{q}"
long << 'no-' + (q = q.downcase)
nolong << q
- when /^--\[no-\]([^][=\s]*)(.+)?/
+ when /^--\[no-\]([^\[\]=\s]*)(.+)?/
q, a = $1, $2
o = notwice(a ? Object : TrueClass, klass, 'type')
- default_style = default_style.guess(arg = a) if a
- default_pattern, conv = search(:atype, o)
+ if a
+ default_style = default_style.guess(arg = a)
+ default_pattern, conv = search(:atype, o) unless default_pattern
+ end
ldesc << "--#{q}"
long << (o = q.downcase)
not_pattern, not_conv = search(:atype, FalseClass) unless not_style
not_style = Switch::NoArgument
nolong << 'no-' + o
- when /^--([^][=\s]*)(.+)?/
+ when /^--([^\[\]=\s]*)(.+)?/
q, a = $1, $2
- o = notwice(a ? NilClass : TrueClass, klass, 'type')
- default_style = default_style.guess(arg = a) if a
- default_pattern, conv = search(:atype, o)
+ 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')
- default_style = default_style.guess(arg = a) if a
- default_pattern, conv = search(:atype, o)
+ 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
- o = notwice((a ? Object : TrueClass), klass, 'type')
- default_style = default_style.guess(arg = a) if a
- default_pattern, conv = search(:atype, o)
+ 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)
+ 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
@@ -1071,7 +1107,7 @@ Default options, which never appear in option summary.
:Parameters:
: ((|argv|))
command line arguments to be parsed.
- : (({Block}))
+ : (({block}))
called with each non-option argument.
=end #'#"#`#
def order(*argv, &block) order!(argv, &block) end
@@ -1092,7 +1128,7 @@ Default options, which never appear in option summary.
end
begin
opt, sw, val = sw.parse(rest, argv) {|*exc| raise(*exc)}
- sw.yield(val) if sw
+ sw.call(val) if sw
rescue ParseError
raise $!.set_option(arg, rest)
end
@@ -1121,7 +1157,7 @@ Default options, which never appear in option summary.
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.yield(val) if sw
+ sw.call(val) if sw
rescue ParseError
raise $!.set_option(arg, has_arg)
end
@@ -1197,7 +1233,7 @@ Default options, which never appear in option summary.
called method in each elements of (({stack}))s.
: ((|*args|))
passed to ((|id|)).
- : (({Block}))
+ : (({block}))
passed to ((|id|)).
=end #'#"#`#
def visit(id, *args, &block)
@@ -1218,7 +1254,7 @@ Default options, which never appear in option summary.
searching table.
: ((|k|))
searching key.
- : (({Block}))
+ : (({block}))
yielded with the found value when succeeded.
=end #'#"#`#
def search(id, k)
@@ -1240,7 +1276,7 @@ Default options, which never appear in option summary.
searching key.
: ((|*pat|))
optional pattern for completion.
- : (({Block}))
+ : (({block}))
yielded with the found value when succeeded.
=end #'#"#`#
def complete(typ, opt, *pat)
@@ -1299,9 +1335,9 @@ Default options, which never appear in option summary.
: Object
any string, and no conversion. this is fall-back.
=end #'#"#`#
- accept(Object) {|s|s or s.nil?}
+ accept(Object) {|s,|s or s.nil?}
- accept(NilClass) {|s|s}
+ accept(NilClass) {|s,|s}
=begin
: String
@@ -1320,7 +1356,7 @@ Default options, which never appear in option summary.
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}
+ accept(Integer, %r"\A[-+]?(?:#{integer})"io) {|s,| Integer(s) if s}
=begin
: Float
@@ -1328,21 +1364,21 @@ Default options, which never appear in option summary.
=end #'#"#`#
float = "(?:#{decimal}(?:\\.(?:#{decimal})?)?|\\.#{decimal})(?:E[-+]?#{decimal})?"
floatpat = %r"\A[-+]?#{float}"io
- accept(Float, floatpat) {|s| s.to_f if s}
+ accept(Float, floatpat) {|s,| s.to_f if s}
=begin
: Numeric
Generic numeric format, and converts to (({Integer})) for integer
format, (({Float})) for float format.
=end #'#"#`#
- accept(Numeric, %r"\A[-+]?(?:#{octal}|#{float})"io) {|s| eval(s) if s}
+ accept(Numeric, %r"\A[-+]?(?:#{octal}|#{float})"io) {|s,| eval(s) if s}
=begin
: OptionParser::DecimalInteger
Decimal integer format, to be converted to (({Integer})).
=end #'#"#`#
DecimalInteger = /\A[-+]?#{decimal}/io
- accept(DecimalInteger) {|s| s.to_i if s}
+ accept(DecimalInteger) {|s,| s.to_i if s}
=begin
: OptionParser::OctalInteger
@@ -1350,7 +1386,7 @@ Default options, which never appear in option summary.
to (({Integer})).
=end #'#"#`#
OctalInteger = /\A[-+]?(?:[0-7]+(?:_[0-7]+)*|0(?:#{binary}|#{hex}))/io
- accept(OctalInteger) {|s| s.oct if s}
+ accept(OctalInteger) {|s,| s.oct if s}
=begin
: OptionParser::DecimalNumeric
@@ -1358,7 +1394,7 @@ Default options, which never appear in option summary.
(({Integer})) for integer format, (({Float})) for float format.
=end #'#"#`#
DecimalNumeric = floatpat # decimal integer is allowed as float also.
- accept(DecimalNumeric) {|s| eval(s) if s}
+ accept(DecimalNumeric) {|s,| eval(s) if s}
=begin
: TrueClass
@@ -1379,10 +1415,9 @@ Default options, which never appear in option summary.
: Array
List of strings separated by ","
=end #'#"#`#
- accept(Array) do |s|
+ accept(Array) do |s,|
if s
s = s.split(',').collect {|s| s unless s.empty?}
- s.size > 1 or s = [s] # guard empty or single.
end
s
end
@@ -1562,7 +1597,7 @@ Extends command line arguments array to parse itself.
begin
yield @optparse
rescue ParseError
- STDERR.puts @optparse.program_name + ': ' + $!
+ warn @optparse.program_name + ': ' + $!
nil
end
end
diff --git a/lib/optparse/date.rb b/lib/optparse/date.rb
new file mode 100644
index 0000000000..c9f072f290
--- /dev/null
+++ b/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
+ DateTime.parse(s) if s
+ rescue ArgumentError
+ raise OptionParser::InvalidArgument, s
+ end
+end
diff --git a/lib/optparse/shellwords.rb b/lib/optparse/shellwords.rb
index 75cb57b72b..0422d7c887 100644
--- a/lib/optparse/shellwords.rb
+++ b/lib/optparse/shellwords.rb
@@ -3,4 +3,4 @@
require 'shellwords'
require 'optparse'
-OptionParser.accept(Shellwords) {|s| Shellwords.shellwords(s)}
+OptionParser.accept(Shellwords) {|s,| Shellwords.shellwords(s)}
diff --git a/lib/optparse/time.rb b/lib/optparse/time.rb
index 9f677a8d31..402cadcf16 100644
--- a/lib/optparse/time.rb
+++ b/lib/optparse/time.rb
@@ -1,7 +1,7 @@
require 'optparse'
require 'time'
-OptionParser.accept(Time) do |s|
+OptionParser.accept(Time) do |s,|
begin
(Time.httpdate(s) rescue Time.parse(s)) if s
rescue
diff --git a/lib/optparse/uri.rb b/lib/optparse/uri.rb
index 5bdcf57c96..024dc69eac 100644
--- a/lib/optparse/uri.rb
+++ b/lib/optparse/uri.rb
@@ -3,4 +3,4 @@
require 'optparse'
require 'uri'
-OptionParser.accept(URI) {|s| URI.parse(s) if s}
+OptionParser.accept(URI) {|s,| URI.parse(s) if s}
diff --git a/lib/parsedate.rb b/lib/parsedate.rb
index 7068da40f4..405ab46907 100644
--- a/lib/parsedate.rb
+++ b/lib/parsedate.rb
@@ -7,7 +7,7 @@ module ParseDate
def parsedate(str, comp=false)
Date._parse(str, comp).
- select(:year, :mon, :mday, :hour, :min, :sec, :zone, :wday)
+ values_at(:year, :mon, :mday, :hour, :min, :sec, :zone, :wday)
end
module_function :parsedate
diff --git a/lib/pathname.rb b/lib/pathname.rb
new file mode 100644
index 0000000000..3a8edfc136
--- /dev/null
+++ b/lib/pathname.rb
@@ -0,0 +1,523 @@
+# Object-Oriented Pathname Class
+#
+# Author:: Tanaka Akira <akr@m17n.org>
+
+class Pathname
+ def initialize(path)
+ @path = path.to_str
+ end
+
+ def ==(other)
+ return false unless Pathname === other
+ other.to_s == @path
+ end
+ alias === ==
+ alias eql? ==
+
+ def <=>(other)
+ return nil unless Pathname === other
+ @path.tr('/', "\0") <=> other.to_s.tr('/', "\0")
+ end
+
+ def hash
+ @path.hash
+ end
+
+ def to_s
+ @path
+ end
+ alias to_str to_s
+
+ def inspect
+ "#<#{self.class}:#{@path}>"
+ end
+
+ # cleanpath returns clean pathname of self which is without consecutive
+ # slashes and useless dots.
+ #
+ # If true is given to the optional artument consider_symlink,
+ # symbolic links are considered. It makes more dots are retained.
+ #
+ # cleanpath doesn't access actual filesystem.
+ def cleanpath(consider_symlink=false)
+ if consider_symlink
+ cleanpath_conservative
+ else
+ cleanpath_aggressive
+ end
+ end
+
+ def cleanpath_aggressive # :nodoc:
+ # cleanpath_aggressive assumes:
+ # * no symlink
+ # * all pathname prefix contained in the pathname is existing directory
+ return Pathname.new('') if @path == ''
+ absolute = absolute?
+ names = []
+ @path.scan(%r{[^/]+}) {|name|
+ next if name == '.'
+ if name == '..'
+ if names.empty?
+ next if absolute
+ else
+ if names.last != '..'
+ names.pop
+ next
+ end
+ end
+ end
+ names << name
+ }
+ return Pathname.new(absolute ? '/' : '.') if names.empty?
+ path = absolute ? '/' : ''
+ path << names.join('/')
+ Pathname.new(path)
+ end
+
+ def cleanpath_conservative # :nodoc:
+ return Pathname.new('') if @path == ''
+ names = @path.scan(%r{[^/]+})
+ last_dot = names.last == '.'
+ names.delete('.')
+ names.shift while names.first == '..' if absolute?
+ return Pathname.new(absolute? ? '/' : '.') if names.empty?
+ path = absolute? ? '/' : ''
+ path << names.join('/')
+ if names.last != '..'
+ if last_dot
+ path << '/.'
+ elsif %r{/\z} =~ @path
+ path << '/'
+ end
+ end
+ Pathname.new(path)
+ end
+
+ # realpath returns real pathname of self in actual filesystem.
+ #
+ # If false is given for the optional argument force_absolute,
+ # it may return relative pathname.
+ # Otherwise it returns absolute pathname.
+ def realpath(force_absolute=true)
+ if relative? && force_absolute
+ path = File.join Dir.pwd, @path
+ else
+ path = @path
+ end
+ stats = {}
+ if %r{\A/} =~ path || realpath_root?('.', stats)
+ resolved = '/'
+ else
+ resolved = '.'
+ end
+ resolved = realpath_rec(resolved, path, stats)
+ Pathname.new(resolved)
+ end
+
+ def realpath_root?(path, stats) # :nodoc:
+ path_stat = stats[path] ||= File.lstat(path)
+ parent = path == '.' ? '..' : File.join(path, '..')
+ parent_stat = stats[parent] ||= File.lstat(parent)
+ path_stat.dev == parent_stat.dev && path_stat.ino == parent_stat.ino
+ end
+
+ def realpath_rec(resolved, unresolved, stats, rec={}) # :nodoc:
+ unresolved.scan(%r{[^/]+}) {|f|
+ next if f == '.'
+ if f == '..'
+ case resolved
+ when '/'
+ # Since the parent directory of '/' is '/', do nothing.
+ when '.'
+ resolved = '..'
+ resolved = '/' if realpath_root?(resolved, stats)
+ when %r{(\A|/)\.\.\z}
+ resolved << '/..'
+ resolved = '/' if realpath_root?(resolved, stats)
+ when %r{/}
+ resolved = File.dirname(resolved)
+ else
+ resolved = '.'
+ end
+ else
+ path = resolved == '.' ? f : File.join(resolved, f)
+ if File.lstat(path).symlink?
+ raise Errno::ELOOP.new(path) if rec.include? path
+ link = File.readlink path
+ begin
+ rec[path] = true
+ resolved = realpath_rec(resolved, link, stats, rec)
+ ensure
+ rec.delete path
+ end
+ else
+ resolved = path
+ end
+ end
+ }
+ resolved
+ end
+
+ # parent method returns parent directory, i.e. ".." is joined at last.
+ def parent
+ self.join('..')
+ end
+
+ # mountpoint? method returns true if self points 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
+
+=begin
+ def rootdir?
+ stat1 = self.lstat
+ stat2 = self.parent.lstat
+ stat1.dev == stat2.dev && stat1.ino == stat2.ino
+ end
+=end
+
+ # root? method is a predicate for root directory.
+ #
+ # It doesn't access actual filesystem.
+ # So it may return false for some pathnames
+ # which points root such as "/usr/..".
+ def root?
+ %r{\A/+\z} =~ @path ? true : false
+ end
+
+=begin
+ def working_directory?
+ %r{\A\./*\z} =~ @path ? true : false
+ end
+=end
+
+ # absolute? method is a predicate for absolute pathname.
+ # It returns true if self is beginning with a slash.
+ def absolute?
+ %r{\A/} =~ @path ? true : false
+ end
+
+ # relative? method is a predicate for relative pathname.
+ # It returns true unless self is beginning with a slash.
+ def relative?
+ !absolute?
+ end
+
+ # each_filename iterates over self for each filename components.
+ def each_filename
+ @path.scan(%r{[^/]+}) { yield $& }
+ end
+
+ # Pathname#+ return new pathname which is concatinated with self and an argument.
+ # If the argument is absolute pathname, it is just returned.
+ def +(other)
+ other = Pathname.new(other) unless Pathname === other
+ if other.absolute?
+ other
+ elsif %r{/\z} =~ @path
+ Pathname.new(@path + other.to_s)
+ else
+ Pathname.new(@path + '/' + other.to_s)
+ end
+ end
+
+end
+
+# IO
+class Pathname
+ def foreachline(*args, &block) IO.foreach(@path, *args, &block) end
+ def read(*args) IO.read(@path, *args) end
+ def readlines(*args) IO.readlines(@path, *args) end
+ def sysopen(*args) IO.sysopen(@path, *args) end
+end
+
+# File
+class Pathname
+ def atime() File.atime(@path) end
+ def ctime() File.ctime(@path) end
+ def mtime() File.mtime(@path) end
+ def chmod(mode) File.chmod(mode, @path) end
+ def lchmod(mode) File.chmod(mode, @path) end
+ def chown(owner, group) File.chown(owner, group, @path) end
+ def lchown(owner, group) File.lchown(owner, group, @path) end
+ def fnmatch(pattern, *args) File.fnmatch(pattern, @path, *args) end
+ def fnmatch?(pattern, *args) File.fnmatch?(pattern, @path, *args) end
+ def ftype() File.ftype(@path) end
+ def link(old) File.link(old, @path) end
+ def open(*args, &block) File.open(@path, *args, &block) end
+ def readlink() Pathname.new(File.readlink(@path)) end
+ def rename(to) File.rename(@path, to) end
+ def stat() File.stat(@path) end
+ def lstat() File.lstat(@path) end
+ def symlink(old) File.symlink(old, @path) end
+ def truncate(length) File.truncate(@path, length) end
+ def utime(atime, mtime) File.utime(atime, mtime, @path) end
+ def basename(*args) Pathname.new(File.basename(@path, *args)) end
+ def dirname() Pathname.new(File.dirname(@path)) end
+ def extname() File.extname(@path) end
+ def expand_path(*args) Pathname.new(File.expand_path(@path, *args)) end
+ def join(*args) Pathname.new(File.join(@path, *args)) end
+ def split() File.split(@path).map {|f| Pathname.new(f) } end
+end
+
+# FileTest
+class Pathname
+ def blockdev?() FileTest.blockdev?(@path) end
+ def chardev?() FileTest.chardev?(@path) end
+ def executable?() FileTest.executable?(@path) end
+ def executable_real?() FileTest.executable_real?(@path) end
+ def exist?() FileTest.exist?(@path) end
+ def grpowned?() FileTest.grpowned?(@path) end
+ def directory?() FileTest.directory?(@path) end
+ def file?() FileTest.file?(@path) end
+ def pipe?() FileTest.pipe?(@path) end
+ def socket?() FileTest.socket?(@path) end
+ def owned?() FileTest.owned?(@path) end
+ def readable?() FileTest.readable?(@path) end
+ def readable_real?() FileTest.readable_real?(@path) end
+ def setuid?() FileTest.setuid?(@path) end
+ def setgid?() FileTest.setgid?(@path) end
+ def size() FileTest.size(@path) end
+ def size?() FileTest.size?(@path) end
+ def sticky?() FileTest.sticky?(@path) end
+ def symlink?() FileTest.symlink?(@path) end
+ def writable?() FileTest.writable?(@path) end
+ def writable_real?() FileTest.writable_real?(@path) end
+ def zero?() FileTest.zero?(@path) end
+end
+
+# Dir
+class Pathname
+ def Pathname.glob(*args)
+ if block_given?
+ Dir.glob(*args) {|f| yield Pathname.new(f) }
+ else
+ Dir.glob(*args).map {|f| Pathname.new(f) }
+ end
+ end
+
+ def Pathname.getwd() Pathname.new(Dir.getwd) end
+ class << self; alias pwd getwd end
+
+ def chdir(&block) Dir.chdir(@path, &block) end
+ def chroot() Dir.chroot(@path) end
+ def rmdir() Dir.rmdir(@path) end
+ def entries() Dir.entries(@path).map {|f| Pathname.new(f) } end
+ def dir_foreach(&block) Dir.foreach(@path) {|f| yield Pathname.new(f) } end
+ def mkdir(*args) Dir.mkdir(@path, *args) end
+ def opendir(&block) Dir.open(@path, &block) end
+end
+
+# Find
+class Pathname
+ def find(&block)
+ require 'find'
+ Find.find(@path) {|f| yield Pathname.new(f) }
+ end
+end
+
+# FileUtils
+class Pathname
+ def mkpath
+ require 'fileutils'
+ FileUtils.mkpath(@path)
+ nil
+ end
+
+ 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
+
+# mixed
+class Pathname
+ def unlink()
+ if FileTest.directory? @path
+ Dir.unlink @path
+ else
+ File.unlink @path
+ end
+ end
+ alias delete unlink
+
+ def foreach(*args, &block)
+ 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
+
+if $0 == __FILE__
+ require 'test/unit'
+
+ class PathnameTest < Test::Unit::TestCase # :nodoc:
+ class AnotherStringLike # :nodoc:
+ def initialize(s) @s = s end
+ def to_str() @s end
+ def ==(other) @s == other end
+ end
+
+ def test_equality
+ obj = Pathname.new("a")
+ str = "a"
+ sym = :a
+ ano = AnotherStringLike.new("a")
+ assert_equal(false, obj == str)
+ assert_equal(false, str == obj)
+ assert_equal(false, obj == ano)
+ assert_equal(false, ano == obj)
+ assert_equal(false, obj == sym)
+ assert_equal(false, sym == obj)
+
+ obj2 = Pathname.new("a")
+ assert_equal(true, obj == obj2)
+ assert_equal(true, obj === obj2)
+ assert_equal(true, obj.eql?(obj2))
+ end
+
+ def test_hashkey
+ h = {}
+ h[Pathname.new("a")] = 1
+ h[Pathname.new("a")] = 2
+ assert_equal(1, h.size)
+ end
+
+ def assert_pathname_cmp(e, s1, s2)
+ p1 = Pathname.new(s1)
+ p2 = Pathname.new(s2)
+ r = p1 <=> p2
+ assert(e == r,
+ "#{p1.inspect} <=> #{p2.inspect}: <#{e}> expected but was <#{r}>")
+ end
+ def test_comparison
+ assert_pathname_cmp( 0, "a", "a")
+ assert_pathname_cmp( 1, "b", "a")
+ assert_pathname_cmp(-1, "a", "b")
+ ss = %w(
+ a
+ a/
+ a/b
+ a.
+ a0
+ )
+ s1 = ss.shift
+ ss.each {|s2|
+ assert_pathname_cmp(-1, s1, s2)
+ s1 = s2
+ }
+ end
+
+ def test_comparison_string
+ assert_equal(nil, Pathname.new("a") <=> "a")
+ assert_equal(nil, "a" <=> Pathname.new("a"))
+ end
+
+ def test_syntactical
+ assert_equal(true, Pathname.new("/").root?)
+ assert_equal(true, Pathname.new("//").root?)
+ assert_equal(true, Pathname.new("///").root?)
+ assert_equal(false, Pathname.new("").root?)
+ assert_equal(false, Pathname.new("a").root?)
+ #assert_equal(true, Pathname.new(".").working_directory?)
+ #assert_equal(true, Pathname.new("./").working_directory?)
+ #assert_equal(true, Pathname.new(".//").working_directory?)
+ #assert_equal(false, Pathname.new("").working_directory?)
+ #assert_equal(false, Pathname.new("a").working_directory?)
+ end
+
+ def test_cleanpath
+ assert_equal('/', Pathname.new('/').cleanpath(true).to_s)
+ assert_equal('/', Pathname.new('//').cleanpath(true).to_s)
+ assert_equal('', Pathname.new('').cleanpath(true).to_s)
+
+ assert_equal('.', Pathname.new('.').cleanpath(true).to_s)
+ assert_equal('..', Pathname.new('..').cleanpath(true).to_s)
+ assert_equal('a', Pathname.new('a').cleanpath(true).to_s)
+ assert_equal('/', Pathname.new('/.').cleanpath(true).to_s)
+ assert_equal('/', Pathname.new('/..').cleanpath(true).to_s)
+ assert_equal('/a', Pathname.new('/a').cleanpath(true).to_s)
+ assert_equal('.', Pathname.new('./').cleanpath(true).to_s)
+ assert_equal('..', Pathname.new('../').cleanpath(true).to_s)
+ assert_equal('a/', Pathname.new('a/').cleanpath(true).to_s)
+
+ assert_equal('a/b', Pathname.new('a//b').cleanpath(true).to_s)
+ assert_equal('a/.', Pathname.new('a/.').cleanpath(true).to_s)
+ assert_equal('a/.', Pathname.new('a/./').cleanpath(true).to_s)
+ assert_equal('a/..', Pathname.new('a/../').cleanpath(true).to_s)
+ assert_equal('/a/.', Pathname.new('/a/.').cleanpath(true).to_s)
+ assert_equal('..', Pathname.new('./..').cleanpath(true).to_s)
+ assert_equal('..', Pathname.new('../.').cleanpath(true).to_s)
+ assert_equal('..', Pathname.new('./../').cleanpath(true).to_s)
+ assert_equal('..', Pathname.new('.././').cleanpath(true).to_s)
+ assert_equal('/', Pathname.new('/./..').cleanpath(true).to_s)
+ assert_equal('/', Pathname.new('/../.').cleanpath(true).to_s)
+ assert_equal('/', Pathname.new('/./../').cleanpath(true).to_s)
+ assert_equal('/', Pathname.new('/.././').cleanpath(true).to_s)
+
+ assert_equal('a/b/c', Pathname.new('a/b/c').cleanpath(true).to_s)
+ assert_equal('b/c', Pathname.new('./b/c').cleanpath(true).to_s)
+ assert_equal('a/c', Pathname.new('a/./c').cleanpath(true).to_s)
+ assert_equal('a/b/.', Pathname.new('a/b/.').cleanpath(true).to_s)
+ assert_equal('a/..', Pathname.new('a/../.').cleanpath(true).to_s)
+
+ assert_equal('/a', Pathname.new('/../.././../a').cleanpath(true).to_s)
+ assert_equal('a/b/../../../../c/../d', Pathname.new('a/b/../../../../c/../d').cleanpath(true).to_s)
+ end
+
+
+ def test_cleanpath_no_symlink
+ assert_equal('/', Pathname.new('/').cleanpath.to_s)
+ assert_equal('/', Pathname.new('//').cleanpath.to_s)
+ assert_equal('', Pathname.new('').cleanpath.to_s)
+
+ assert_equal('.', Pathname.new('.').cleanpath.to_s)
+ assert_equal('..', Pathname.new('..').cleanpath.to_s)
+ assert_equal('a', Pathname.new('a').cleanpath.to_s)
+ assert_equal('/', Pathname.new('/.').cleanpath.to_s)
+ assert_equal('/', Pathname.new('/..').cleanpath.to_s)
+ assert_equal('/a', Pathname.new('/a').cleanpath.to_s)
+ assert_equal('.', Pathname.new('./').cleanpath.to_s)
+ assert_equal('..', Pathname.new('../').cleanpath.to_s)
+ assert_equal('a', Pathname.new('a/').cleanpath.to_s)
+
+ assert_equal('a/b', Pathname.new('a//b').cleanpath.to_s)
+ assert_equal('a', Pathname.new('a/.').cleanpath.to_s)
+ assert_equal('a', Pathname.new('a/./').cleanpath.to_s)
+ assert_equal('.', Pathname.new('a/../').cleanpath.to_s)
+ assert_equal('/a', Pathname.new('/a/.').cleanpath.to_s)
+ assert_equal('..', Pathname.new('./..').cleanpath.to_s)
+ assert_equal('..', Pathname.new('../.').cleanpath.to_s)
+ assert_equal('..', Pathname.new('./../').cleanpath.to_s)
+ assert_equal('..', Pathname.new('.././').cleanpath.to_s)
+ assert_equal('/', Pathname.new('/./..').cleanpath.to_s)
+ assert_equal('/', Pathname.new('/../.').cleanpath.to_s)
+ assert_equal('/', Pathname.new('/./../').cleanpath.to_s)
+ assert_equal('/', Pathname.new('/.././').cleanpath.to_s)
+
+ assert_equal('a/b/c', Pathname.new('a/b/c').cleanpath.to_s)
+ assert_equal('b/c', Pathname.new('./b/c').cleanpath.to_s)
+ assert_equal('a/c', Pathname.new('a/./c').cleanpath.to_s)
+ assert_equal('a/b', Pathname.new('a/b/.').cleanpath.to_s)
+ assert_equal('.', Pathname.new('a/../.').cleanpath.to_s)
+
+ assert_equal('/a', Pathname.new('/../.././../a').cleanpath.to_s)
+ assert_equal('../../d', Pathname.new('a/b/../../../../c/../d').cleanpath.to_s)
+ end
+
+ end
+end
diff --git a/lib/pp.rb b/lib/pp.rb
index 3718840680..9babc1e7a9 100644
--- a/lib/pp.rb
+++ b/lib/pp.rb
@@ -6,47 +6,25 @@
== Which seems better?
non-pretty-printed output by (({p})) is:
- #<PP:0x81a0d10 @stack=[], @genspace=#<Proc:0x81a0cc0>, @nest=[0], @newline="\n", @buf=#<PrettyPrint::Group:0x81a0c98 @group=0, @tail=0, @buf=[#<PrettyPrint::Group:0x81a0ba8 @group=1, @tail=0, @buf=[#<PrettyPrint::Text:0x81a0b30 @tail=2, @width=1, @text="[">, #<PrettyPrint::Group:0x81a0a68 @group=2, @tail=1, @buf=[#<PrettyPrint::Text:0x81a09f0 @tail=1, @width=1, @text="1">], @singleline_width=1>, #<PrettyPrint::Text:0x81a0a7c @tail=0, @width=1, @text=",">, #<PrettyPrint::Breakable:0x81a0a2c @group=2, @genspace=#<Proc:0x81a0cc0>, @newline="\n", @indent=1, @tail=2, @sep=" ", @width=1>, #<PrettyPrint::Group:0x81a09c8 @group=2, @tail=1, @buf=[#<PrettyPrint::Text:0x81a0950 @tail=1, @width=1, @text="2">], @singleline_width=1>, #<PrettyPrint::Text:0x81a0af4 @tail=0, @width=1, @text="]">], @singleline_width=6>], @singleline_width=6>, @sharing_detection=false>
+ #<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:0x40d0688
- @buf=
- #<PrettyPrint::Group:0x40d064c
- @buf=
- [#<PrettyPrint::Group:0x40d05d4
- @buf=
- [#<PrettyPrint::Text:0x40d0598 @tail=2, @text="[", @width=1>,
- #<PrettyPrint::Group:0x40d0534
- @buf=[#<PrettyPrint::Text:0x40d04f8 @tail=1, @text="1", @width=1>],
- @group=2,
- @singleline_width=1,
- @tail=1>,
- #<PrettyPrint::Text:0x40d053e @tail=0, @text=",", @width=1>,
- #<PrettyPrint::Breakable:0x40d0516
- @genspace=#<Proc:0x40d0656>,
- @group=2,
- @indent=1,
- @newline="\n",
- @sep=" ",
- @tail=2,
- @width=1>,
- #<PrettyPrint::Group:0x40d04e4
- @buf=[#<PrettyPrint::Text:0x40d04a8 @tail=1, @text="2", @width=1>],
- @group=2,
- @singleline_width=1,
- @tail=1>,
- #<PrettyPrint::Text:0x40d057a @tail=0, @text="]", @width=1>],
- @group=1,
- @singleline_width=6,
- @tail=0>],
- @group=0,
- @singleline_width=6,
- @tail=0>,
- @genspace=#<Proc:0x40d0656>,
- @nest=[0],
+ #<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",
- @sharing_detection=false,
- @stack=[]>
+ @output=#<IO:0x8114ee4>,
+ @output_width=2>
I like the latter. If you do too, this library is for you.
@@ -131,6 +109,15 @@ PP#pp to print the object.
This method should return an array of names of instance variables as symbols or strings as:
(({[:@a, :@b]})).
+
+--- pretty_print_inspect
+ is (({inspect})) implementation using (({pretty_print})).
+ If you implement (({pretty_print})), it can be used as follows.
+
+ alias inspect pretty_print_inspect
+
+== AUTHOR
+Tanaka Akira <akr@m17n.org>
=end
require 'prettyprint'
@@ -143,6 +130,7 @@ module Kernel
}
nil
end
+ module_function :pp
end
class PP < PrettyPrint
@@ -205,7 +193,7 @@ class PP < PrettyPrint
end
def object_address_group(obj, &block)
- group(1, sprintf('#<%s:0x%x', obj.class.name, obj.__id__ * 2), '>', &block)
+ group(1, sprintf('#<%s:0x%x', obj.class.to_s, obj.__id__ * 2), '>', &block)
end
def comma_breakable
@@ -255,37 +243,16 @@ class PP < PrettyPrint
module ObjectMixin
# 1. specific pretty_print
# 2. specific inspect
- # 3. generic pretty_print
-
- Key = :__pp_instead_of_inspect__
+ # 3. specific to_s if instance variable is empty
+ # 4. generic pretty_print
def pretty_print(pp)
- # specific pretty_print is not defined, try specific inspect.
- begin
- old = Thread.current[Key]
- result1 = sprintf('#<%s:0x%x pretty_printed>', self.class.name, self.__id__ * 2)
- Thread.current[Key] = [pp, result1]
- result2 = ObjectMixin.pp_call_inspect(self)
- ensure
- Thread.current[Key] = old
- end
-
- unless result1.equal? result2
- pp.text result2
- end
- end
-
- def ObjectMixin.pp_call_inspect(obj); obj.inspect; end
- CallInspectFrame = "#{__FILE__}:#{__LINE__ - 1}:in `pp_call_inspect'"
-
- def inspect
- if CallInspectFrame == caller[0]
- # specific inspect is also not defined, use generic pretty_print.
- pp, result = Thread.current[Key]
- pp.pp_object(self)
- result
+ if /\(Kernel\)#/ !~ method(:inspect).inspect
+ pp.text self.inspect
+ elsif /\(Kernel\)#/ !~ method(:to_s).inspect && instance_variables.empty?
+ pp.text self.to_s
else
- super
+ pp.pp_object(self)
end
end
@@ -299,16 +266,15 @@ class PP < PrettyPrint
def pretty_print_instance_variables
instance_variables.sort
end
- end
-end
-[Numeric, FalseClass, TrueClass, Module].each {|c|
- c.class_eval {
- def pretty_print(pp)
- pp.text self.to_s
+ def pretty_print_inspect
+ if /\(PP::ObjectMixin\)#/ =~ method(:pretty_print).inspect
+ raise "pretty_print is not overriden."
+ end
+ PP.singleline_pp(self, '')
end
- }
-}
+ end
+end
class Array
def pretty_print(pp)
@@ -450,9 +416,15 @@ class File
end
end
-class << ARGF
+class MatchData
def pretty_print(pp)
- pp.text self.to_s
+ pp.object_group(self) {
+ pp.breakable
+ 1.upto(self.size) {|i|
+ pp.breakable unless pp.first?
+ pp.pp self[i-1]
+ }
+ }
end
end
@@ -462,15 +434,16 @@ end
[Numeric, Symbol, FalseClass, TrueClass, NilClass, Module].each {|c|
c.class_eval {
- alias :pretty_print_cycle :pretty_print
+ def pretty_print_cycle(pp)
+ pp.text inspect
+ end
}
}
if __FILE__ == $0
- require 'runit/testcase'
- require 'runit/cui/testrunner'
+ require 'test/unit'
- class PPTest < RUNIT::TestCase
+ class PPTest < Test::Unit::TestCase
def test_list0123_12
assert_equal("[0, 1, 2, 3]\n", PP.pp([0,1,2,3], '', 12))
end
@@ -518,7 +491,15 @@ if __FILE__ == $0
end
end
- class PPInspectTest < RUNIT::TestCase
+ 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, ''))
@@ -533,9 +514,38 @@ if __FILE__ == $0
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_raises(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)
+ 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 < RUNIT::TestCase
+ class PPCycleTest < Test::Unit::TestCase
def test_array
a = []
a << a
@@ -561,14 +571,25 @@ if __FILE__ == $0
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, ''))
end
- end
- RUNIT::CUI::TestRunner.run(PPTest.suite)
- RUNIT::CUI::TestRunner.run(PPInspectTest.suite)
- RUNIT::CUI::TestRunner.run(PPCycleTest.suite)
+ 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
end
diff --git a/lib/prettyprint.rb b/lib/prettyprint.rb
index 7f1497b541..a815f64538 100644
--- a/lib/prettyprint.rb
+++ b/lib/prettyprint.rb
@@ -119,6 +119,8 @@ Christian Lindig, Strictly Pretty, March 2000,
Philip Wadler, A prettier printer, March 1998,
((<URL:http://www.research.avayalabs.com/user/wadler/topics/recent.html#prettier>))
+== AUTHOR
+Tanaka Akira <akr@m17n.org>
=end
class PrettyPrint
@@ -392,10 +394,9 @@ class PrettyPrint
end
if __FILE__ == $0
- require 'runit/testcase'
- require 'runit/cui/testrunner'
+ require 'test/unit'
- class WadlerExample < RUNIT::TestCase
+ class WadlerExample < Test::Unit::TestCase
def setup
@tree = Tree.new("aaaa", Tree.new("bbbbb", Tree.new("ccc"),
Tree.new("dd")),
@@ -632,7 +633,7 @@ End
end
end
- class StrictPrettyExample < RUNIT::TestCase
+ class StrictPrettyExample < Test::Unit::TestCase
def prog(width)
PrettyPrint.format('', width) {|pp|
pp.group {
@@ -777,7 +778,7 @@ End
end
- class TailGroup < RUNIT::TestCase
+ class TailGroup < Test::Unit::TestCase
def test_1
out = PrettyPrint.format('', 10) {|pp|
pp.group {
@@ -797,7 +798,7 @@ End
end
end
- class NonString < RUNIT::TestCase
+ class NonString < Test::Unit::TestCase
def format(width)
PrettyPrint.format([], width, 'newline', lambda {|n| "#{n} spaces"}) {|pp|
pp.text(3, 3)
@@ -816,7 +817,7 @@ End
end
- class Fill < RUNIT::TestCase
+ class Fill < Test::Unit::TestCase
def format(width)
PrettyPrint.format('', width) {|pp|
pp.group {
@@ -907,10 +908,4 @@ End
end
end
-
- RUNIT::CUI::TestRunner.run(WadlerExample.suite)
- RUNIT::CUI::TestRunner.run(StrictPrettyExample.suite)
- RUNIT::CUI::TestRunner.run(TailGroup.suite)
- RUNIT::CUI::TestRunner.run(NonString.suite)
- RUNIT::CUI::TestRunner.run(Fill.suite)
end
diff --git a/lib/profile.rb b/lib/profile.rb
index 23840c4e19..104cb205b9 100644
--- a/lib/profile.rb
+++ b/lib/profile.rb
@@ -1,57 +1,6 @@
+require 'profiler'
-module Profiler__
- Times = if defined? Process.times then Process else Time end
- Start = Float(Times::times[0])
- top = "toplevel".intern
- Stack = [[0, 0, top]]
- MAP = {"#toplevel" => [1, 0, 0, "#toplevel"]}
-
- p = proc{|event, file, line, id, binding, klass|
- case event
- when "call", "c-call"
- now = Float(Times::times[0])
- Stack.push [now, 0.0, id]
- when "return", "c-return"
- now = Float(Times::times[0])
- tick = Stack.pop
- name = klass.to_s
- if name.nil? then name = '' end
- if klass.kind_of? Class
- name += "#"
- else
- name += "."
- end
- name += id.id2name
- data = MAP[name]
- unless data
- data = [0.0, 0.0, 0.0, name]
- MAP[name] = data
- end
- data[0] += 1
- cost = now - tick[0]
- data[1] += cost
- data[2] += cost - tick[1]
- Stack[-1][1] += cost
- end
- }
- END {
- set_trace_func nil
- total = Float(Times::times[0]) - Start
- if total == 0 then total = 0.01 end
- MAP["#toplevel"][1] = total
-# f = open("./rmon.out", "w")
- f = STDERR
- 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], d[3]
- end
- f.close
- }
- set_trace_func p
-end
+END {
+ Profiler__::print_profile(STDERR)
+}
+Profiler__::start_profile
diff --git a/lib/profiler.rb b/lib/profiler.rb
new file mode 100644
index 0000000000..74e9b96295
--- /dev/null
+++ b/lib/profiler.rb
@@ -0,0 +1,59 @@
+module Profiler__
+ Times = if defined? Process.times then Process else Time end
+ # internal values
+ @@start = @@stack = @@map = nil
+ PROFILE_PROC = proc{|event, file, line, id, binding, klass|
+ case event
+ when "call", "c-call"
+ now = Float(Times::times[0])
+ @@stack.push [now, 0.0, id]
+ when "return", "c-return"
+ now = Float(Times::times[0])
+ tick = @@stack.pop
+ name = klass.to_s
+ if name.nil? then name = '' end
+ if klass.kind_of? Class
+ name += "#"
+ else
+ name += "."
+ end
+ name += id.id2name
+ data = @@map[name]
+ unless data
+ data = [0.0, 0.0, 0.0, name]
+ @@map[name] = data
+ end
+ data[0] += 1
+ cost = now - tick[0]
+ data[1] += cost
+ data[2] += cost - tick[1]
+ @@stack[-1][1] += cost
+ end
+ }
+module_function
+ def start_profile
+ @@start = Float(Times::times[0])
+ @@stack = [[0, 0, :toplevel], [0, 0, :dummy]]
+ @@map = {"#toplevel" => [1, 0, 0, "#toplevel"]}
+ set_trace_func PROFILE_PROC
+ end
+ def stop_profile
+ set_trace_func nil
+ end
+ def print_profile(f)
+ stop_profile
+ total = Float(Times::times[0]) - @@start
+ if total == 0 then total = 0.01 end
+ @@map["#toplevel"][1] = total
+ 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], d[3]
+ end
+ end
+end
diff --git a/lib/pstore.rb b/lib/pstore.rb
index dd74f4fc2f..93795495fc 100644
--- a/lib/pstore.rb
+++ b/lib/pstore.rb
@@ -24,9 +24,6 @@ class PStore
unless File::directory? dir
raise PStore::Error, format("directory %s does not exist", dir)
end
- unless File::writable? dir
- raise PStore::Error, format("directory %s not writable", dir)
- end
if File::exist? file and not File::readable? file
raise PStore::Error, format("file %s not readable", file)
end
@@ -93,7 +90,7 @@ class PStore
value = nil
backup = @filename+"~"
begin
- file = File::open(@filename, "rb+")
+ file = File::open(@filename, read_only ? "rb" : "rb+")
orig = true
rescue Errno::ENOENT
raise if read_only
diff --git a/lib/racc/parser.rb b/lib/racc/parser.rb
index 25590fa228..ae7bec577a 100644
--- a/lib/racc/parser.rb
+++ b/lib/racc/parser.rb
@@ -13,11 +13,6 @@
# $Id$
#
-unless defined? NotImplementedError then
- NotImplementedError = NotImplementError
-end
-
-
module Racc
class ParseError < StandardError; end
end
diff --git a/lib/rational.rb b/lib/rational.rb
index 16ee00890f..38af7b8a81 100644
--- a/lib/rational.rb
+++ b/lib/rational.rb
@@ -31,8 +31,9 @@
# Integer::to_r
#
# Fixnum::**
+# Fixnum::quo
# Bignum::**
-#
+# Bignum::quo
#
def Rational(a, b = 1)
@@ -66,7 +67,9 @@ class Rational < Numeric
def Rational.new!(num, den = 1)
new(num, den)
end
-
+
+ private_class_method :new
+
def initialize(num, den)
if den < 0
num = -num
@@ -91,7 +94,7 @@ class Rational < Numeric
elsif a.kind_of?(Float)
Float(self) + a
else
- x , y = a.coerce(self)
+ x, y = a.coerce(self)
x + y
end
end
@@ -106,7 +109,7 @@ class Rational < Numeric
elsif a.kind_of?(Float)
Float(self) - a
else
- x , y = a.coerce(self)
+ x, y = a.coerce(self)
x - y
end
end
@@ -121,7 +124,7 @@ class Rational < Numeric
elsif a.kind_of?(Float)
Float(self) * a
else
- x , y = a.coerce(self)
+ x, y = a.coerce(self)
x * y
end
end
@@ -137,7 +140,7 @@ class Rational < Numeric
elsif a.kind_of?(Float)
Float(self) / a
else
- x , y = a.coerce(self)
+ x, y = a.coerce(self)
x / y
end
end
@@ -160,7 +163,7 @@ class Rational < Numeric
elsif other.kind_of?(Float)
Float(self) ** other
else
- x , y = other.coerce(self)
+ x, y = other.coerce(self)
x ** y
end
end
@@ -183,6 +186,18 @@ class Rational < Numeric
end
end
+ 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
+
def <=> (other)
if other.kind_of?(Rational)
num = @numerator * other.denominator
@@ -199,9 +214,11 @@ class Rational < Numeric
return self <=> Rational.new!(other, 1)
elsif other.kind_of?(Float)
return Float(self) <=> other
- else
- x , y = other.coerce(self)
+ elsif defined? other.coerce
+ x, y = other.coerce(self)
return x <=> y
+ else
+ return nil
end
end
@@ -254,7 +271,7 @@ class Integer
self
end
- def denomerator
+ def denominator
1
end
@@ -312,41 +329,11 @@ class Integer
end
class Fixnum
- alias div! /;
- def div(other)
- if other.kind_of?(Fixnum)
- self.div!(other)
- elsif other.kind_of?(Bignum)
- x, y = other.coerce(self)
- x.div!(y)
- else
- x, y = other.coerce(self)
- x / y
- end
- end
-
-# alias divmod! divmod
-
- if not defined? Complex
- alias power! **;
- end
-
-# def rdiv(other)
-# if other.kind_of?(Fixnum)
-# Rational(self, other)
-# elsif
-# x, y = other.coerce(self)
-# if defined?(x.div())
-# x.div(y)
-# else
-# x / y
-# end
-# end
- # end
-
- def rdiv(other)
+ undef quo
+ def quo(other)
Rational.new!(self,1) / other
end
+ alias rdiv quo
def rpower (other)
if other >= 0
@@ -355,24 +342,23 @@ class Fixnum
Rational.new!(self,1)**other
end
end
-
- if not defined? Complex
+
+ unless defined? 1.power!
+ alias power! **
alias ** rpower
end
end
class Bignum
- alias div! /;
- alias div /;
- alias divmod! divmod
-
- if not defined? power!
+ unless defined? Complex
alias power! **
end
-
- def rdiv(other)
+
+ undef quo
+ def quo(other)
Rational.new!(self,1) / other
end
+ alias rdiv quo
def rpower (other)
if other >= 0
@@ -382,9 +368,7 @@ class Bignum
end
end
- if not defined? Complex
+ unless defined? Complex
alias ** rpower
end
-
end
-
diff --git a/lib/resolv.rb b/lib/resolv.rb
index a29d8de27f..6dac504747 100644
--- a/lib/resolv.rb
+++ b/lib/resolv.rb
@@ -235,7 +235,7 @@ class Resolv
@resolvers.each {|r|
r.each_address(name) {|address|
yield address.to_s
- yielded = true
+ yielded = true
}
return if yielded
}
@@ -257,7 +257,7 @@ class Resolv
@resolvers.each {|r|
r.each_name(address) {|name|
yield name.to_s
- yielded = true
+ yielded = true
}
return if yielded
}
@@ -270,7 +270,12 @@ class Resolv
end
class Hosts
- DefaultFileName = '/etc/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
@@ -288,15 +293,15 @@ class Resolv
line.sub!(/#.*/, '')
addr, hostname, *aliases = line.split(/\s+/)
next unless addr
- addr.untaint
- hostname.untaint
+ 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
+ n.untaint
@name2addr[n] = [] unless @name2addr.include? n
@name2addr[n] << addr
}
@@ -322,7 +327,7 @@ class Resolv
def each_address(name, &proc)
lazy_initialize
if @name2addr.include?(name)
- @name2addr[name].each(&proc)
+ @name2addr[name].each(&proc)
end
end
@@ -358,9 +363,9 @@ class Resolv
dns = new(*args)
return dns unless block_given?
begin
- yield dns
+ yield dns
ensure
- dns.close
+ dns.close
end
end
@@ -466,7 +471,7 @@ class Resolv
case reply.rcode
when RCode::NoError
extract_resources(reply, reply_name, typeclass, &proc)
- return
+ return
when RCode::NXDomain
raise Config::NXDomain.new(reply_name.to_s)
else
@@ -480,10 +485,10 @@ class Resolv
def extract_resources(msg, name, typeclass)
if typeclass < Resource::ANY
- n0 = Name.create(name)
+ n0 = Name.create(name)
msg.each_answer {|n, ttl, data|
- yield data if n0 == n
- }
+ yield data if n0 == n
+ }
end
yielded = false
n0 = Name.create(name)
@@ -491,8 +496,8 @@ class Resolv
if n0 == n
case data
when typeclass
- yield data
- yielded = true
+ yield data
+ yielded = true
when Resource::CNAME
n0 = data.name
end
@@ -503,7 +508,7 @@ class Resolv
if n0 == n
case data
when typeclass
- yield data
+ yield data
end
end
}
@@ -555,11 +560,11 @@ class Resolv
def initialize
super()
@sock = UDPSocket.new
- @sock.fcntl(Fcntl::F_SETFD, 1)
+ @sock.fcntl(Fcntl::F_SETFD, 1) if defined? Fcntl::F_SETFD
@id = {}
@id.default = -1
@thread = Thread.new {
- DNSThreadGroup.add Thread.current
+ DNSThreadGroup.add Thread.current
loop {
reply, from = @sock.recvfrom(UDPSize)
msg = begin
@@ -608,10 +613,10 @@ class Resolv
@port = port
@sock = UDPSocket.new
@sock.connect(host, port)
- @sock.fcntl(Fcntl::F_SETFD, 1)
+ @sock.fcntl(Fcntl::F_SETFD, 1) if defined? Fcntl::F_SETFD
@id = -1
@thread = Thread.new {
- DNSThreadGroup.add Thread.current
+ DNSThreadGroup.add Thread.current
loop {
reply = @sock.recv(UDPSize)
msg = begin
@@ -653,11 +658,11 @@ class Resolv
@port = port
@sock = TCPSocket.new
@sock.connect(host, port)
- @sock.fcntl(Fcntl::F_SETFD, 1)
+ @sock.fcntl(Fcntl::F_SETFD, 1) if defined? Fcntl::F_SETFD
@id = -1
@senders = {}
@thread = Thread.new {
- DNSThreadGroup.add Thread.current
+ DNSThreadGroup.add Thread.current
loop {
len = @sock.read(2).unpack('n')
reply = @sock.read(len)
@@ -702,7 +707,7 @@ class Resolv
def initialize(filename="/etc/resolv.conf")
@mutex = Mutex.new
@filename = filename
- @initialized = nil
+ @initialized = nil
end
def lazy_initialize
@@ -716,9 +721,9 @@ class Resolv
f.each {|line|
line.sub!(/[#;].*/, '')
keyword, *args = line.split(/\s+/)
- args.each { |arg|
- arg.untaint
- }
+ args.each { |arg|
+ arg.untaint
+ }
next unless keyword
case keyword
when 'nameserver'
@@ -731,6 +736,11 @@ class Resolv
}
}
rescue Errno::ENOENT
+ if /mswin32|cygwin|mingw|bccwin/ =~ RUBY_PLATFORM
+ search, nameserver = Win32::Resolv.get_resolv_info
+ @search = [search] if search
+ @nameserver = nameserver if nameserver
+ end
end
@nameserver = ['0.0.0.0'] if @nameserver.empty?
@@ -758,17 +768,17 @@ class Resolv
def generate_candidates(name)
candidates = nil
- name = Name.create(name)
- if name.absolute?
+ 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
+ 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
@@ -866,9 +876,9 @@ class Resolv
return @string
end
- def inspect
- return "#<#{self.class} #{self.to_s}>"
- end
+ def inspect
+ return "#<#{self.class} #{self.to_s}>"
+ end
def ==(other)
return @downcase == other.downcase
@@ -898,7 +908,7 @@ class Resolv
def initialize(labels, absolute=true)
@labels = labels
- @absolute = absolute
+ @absolute = absolute
end
def absolute?
@@ -1272,7 +1282,7 @@ class Resolv
def hash
h = 0
self.instance_variables.each {|name|
- h += self.instance_eval("#{name}.hash")
+ h ^= self.instance_eval("#{name}.hash")
}
return h
end
@@ -1550,7 +1560,7 @@ class Resolv
def initialize(address)
unless address.kind_of?(String) && address.length == 4
- raise ArgumentError.new('IPv4 address muse be 4 bytes')
+ raise ArgumentError.new('IPv4 address must be 4 bytes')
end
@address = address
end
@@ -1585,7 +1595,7 @@ class Resolv
class IPv6
Regex_8Hex = /\A
(?:[0-9A-Fa-f]{1,4}:){7}
- [0-9A-Fa-f]{1,4}
+ [0-9A-Fa-f]{1,4}
\z/x
Regex_CompressedHex = /\A
@@ -1658,7 +1668,7 @@ class Resolv
def initialize(address)
unless address.kind_of?(String) && address.length == 16
- raise ArgumentError.new('IPv6 address muse be 16 bytes')
+ raise ArgumentError.new('IPv6 address must be 16 bytes')
end
@address = address
end
diff --git a/lib/rexml/attlistdecl.rb b/lib/rexml/attlistdecl.rb
new file mode 100644
index 0000000000..d4b5c38af6
--- /dev/null
+++ b/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/lib/rexml/attribute.rb b/lib/rexml/attribute.rb
new file mode 100644
index 0000000000..4aef0944dc
--- /dev/null
+++ b/lib/rexml/attribute.rb
@@ -0,0 +1,151 @@
+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
+ else
+ raise "illegal argument #{first.type} 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
+ @value = @normalized = Text::normalize( @value, doctype )
+ end
+
+ # Returns the UNNORMALIZED value of this attribute. That is, entities
+ # have been expanded to their values
+ def value
+ @unnormalized if @unnormalized
+ doctype = nil
+ if @element
+ doc = @element.document
+ doctype = doc.doctype if doc
+ end
+ @normalized = nil
+ @value = @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
+ end
+end
+#vim:ts=2 sw=2 noexpandtab:
diff --git a/lib/rexml/cdata.rb b/lib/rexml/cdata.rb
new file mode 100644
index 0000000000..402a0187ff
--- /dev/null
+++ b/lib/rexml/cdata.rb
@@ -0,0 +1,68 @@
+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
+
+ # 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 )
+ output << START
+ output << @string
+ output << STOP
+ end
+ end
+end
diff --git a/lib/rexml/child.rb b/lib/rexml/child.rb
new file mode 100644
index 0000000000..6d3c9df5e6
--- /dev/null
+++ b/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/lib/rexml/comment.rb b/lib/rexml/comment.rb
new file mode 100644
index 0000000000..e439ddf9d8
--- /dev/null
+++ b/lib/rexml/comment.rb
@@ -0,0 +1,79 @@
+require "rexml/child"
+
+module REXML
+ ##
+ # Represents an XML comment; that is, text between <!-- ... -->
+ class Comment < Child
+ include Comparable
+ START = "<!--"
+ STOP = "-->"
+
+ attr_accessor :string # The content text
+
+ ##
+ # 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::
+ # Who knows?
+ # 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
+ 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/lib/rexml/doctype.rb b/lib/rexml/doctype.rb
new file mode 100644
index 0000000000..d70ea6fd6c
--- /dev/null
+++ b/lib/rexml/doctype.rb
@@ -0,0 +1,182 @@
+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
+ # source = Source.new( '<!DOCTYPE foo "bar">' )
+ # dt = DocType.new( source )
+ # # <!DOCTYPE foo "bar">
+ # dt = DocType.new( source, some_document )
+ # # Creates a doctype, and adds to the supplied document
+ 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]
+ 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::
+ # Who knows?
+ # 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 + 2
+ output << ' ['
+ child = nil # speed
+ @children.each { |child|
+ output << "\n"
+ child.write( output, next_indent )
+ }
+ output << "\n"
+ #output << ' '*next_indent
+ output << "]"
+ end
+ output << STOP
+ 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
+ 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 NotationDecl < Child
+ def initialize name, middle, rest
+ @name = name
+ @middle = middle
+ @rest = rest
+ end
+
+ def to_s
+ "<!NOTATION #@name #@middle #@rest>"
+ end
+
+ def write( output, indent=-1 )
+ output << (' '*indent) if indent > 0
+ output << to_s
+ end
+ end
+end
diff --git a/lib/rexml/document.rb b/lib/rexml/document.rb
new file mode 100644
index 0000000000..8617f904e6
--- /dev/null
+++ b/lib/rexml/document.rb
@@ -0,0 +1,237 @@
+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"
+
+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
+ DECLARATION = XMLDecl.new( "1.0", "UTF-8" )
+
+ # 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
+ if @children[0].kind_of? XMLDecl
+ @children[1,0] = child
+ else
+ @children.unshift 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
+ @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.find { |item| item.kind_of? XMLDecl }
+ rv = DECLARATION if rv.nil?
+ rv
+ end
+
+ # @return the XMLDecl version of this document as a String.
+ # If no XMLDecl has been set, returns the default version.
+ def version
+ decl = xml_decl()
+ decl.nil? ? XMLDecl.DEFAULT_VERSION : 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
+ decl = xml_decl()
+ decl.nil? or decl.encoding.nil? ? XMLDecl.DEFAULT_ENCODING : 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?
+ decl = xml_decl()
+ decl.nil? ? XMLDecl.DEFAULT_STANDALONE : 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::
+ # What the heck does this do? Defaults to false
+ # 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"
+ @children.each { |node|
+ node.write( output, indent, transitive, ie_hack )
+ output << "\n" unless indent<0 or node == @children[-1]
+ }
+ end
+
+
+ def Document::parse_stream( source, listener )
+ Parsers::StreamParser.new( source, listener ).parse
+ end
+
+ private
+ def build( source )
+ build_context = self
+ parser = Parsers::BaseParser.new( source )
+ tag_stack = []
+ in_doctype = false
+ entities = nil
+ while true
+ event = parser.pull
+ case event[0]
+ when :end_document
+ 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], true, nil, true )
+ ) unless (
+ event[1].strip.size == 0 and
+ build_context.ignore_whitespace_nodes
+ )
+ 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 :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
+ end
+ end
+end
diff --git a/lib/rexml/dtd/attlistdecl.rb b/lib/rexml/dtd/attlistdecl.rb
new file mode 100644
index 0000000000..e176bb0749
--- /dev/null
+++ b/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/lib/rexml/dtd/dtd.rb b/lib/rexml/dtd/dtd.rb
new file mode 100644
index 0000000000..81119cfa9b
--- /dev/null
+++ b/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 << EleemntDecl.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/lib/rexml/dtd/elementdecl.rb b/lib/rexml/dtd/elementdecl.rb
new file mode 100644
index 0000000000..c4e620f389
--- /dev/null
+++ b/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/lib/rexml/dtd/entitydecl.rb b/lib/rexml/dtd/entitydecl.rb
new file mode 100644
index 0000000000..83156dfc71
--- /dev/null
+++ b/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 )
+ output << (' '*indent) if indent > 0
+ 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/lib/rexml/dtd/notationdecl.rb b/lib/rexml/dtd/notationdecl.rb
new file mode 100644
index 0000000000..09b6743c5c
--- /dev/null
+++ b/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 )
+ output << (' '*indent) if indent > 0
+ 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/lib/rexml/element.rb b/lib/rexml/element.rb
new file mode 100644
index 0000000000..89e419345c
--- /dev/null
+++ b/lib/rexml/element.rb
@@ -0,0 +1,1147 @@
+require "rexml/parent"
+require "rexml/namespace"
+require "rexml/attribute"
+require "rexml/cdata"
+require "rexml/xpath"
+require "rexml/parseexception"
+
+module REXML
+ # 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.
+ # If a Source, the source will be scanned and parsed for an Element,
+ # and all child elements will be recursively parsed as well.
+ # 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
+
+ # 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 element 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.
+ # d = Document.new '<a><b><c/></b></a>'
+ # a = d[1] ; c = a[1][1]
+ # d.root # These all evaluate to the same Element,
+ # a.root # namely, <a>
+ # c.root #
+ def root
+ parent.nil? ? self : 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
+ root.parent if root
+ 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=nil, attrs=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 = 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 )
+ text = Text.new( text, whitespace(), nil, raw() ) if text.kind_of? String
+ 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
+
+ #################################################
+ # 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::
+ # What the heck does this do? Defaults to false
+ # 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?
+ writer << " " if ie_hack
+ writer << "/"
+ else
+ if transitive and indent>-1 and !@children[0].kind_of? Text
+ writer << "\n"
+ indent writer, indent+2
+ end
+ writer << ">"
+ write_children( writer, indent, transitive, ie_hack )
+ writer << "</#{expanded_name}"
+ end
+ if transitive and indent>-1
+ writer << "\n"
+ indent -= 2 if next_sibling.nil?
+ indent(writer, indent)
+ end
+ writer << ">"
+ end
+
+
+ private
+ # 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 size == 1 and @children[0].kind_of?(Text)
+ # self[0].write( writer, -1 )
+ if indent == -1
+ each { |child| child.write( writer, indent, transitive, ie_hack ) }
+ else
+ next_indent = indent+2
+ 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
+
+ # 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
+ 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
+ end
+end
diff --git a/lib/rexml/encoding.rb b/lib/rexml/encoding.rb
new file mode 100644
index 0000000000..06385d8d52
--- /dev/null
+++ b/lib/rexml/encoding.rb
@@ -0,0 +1,62 @@
+module REXML
+ module Encoding
+ @@uconv_available = false
+
+ ENCODING_CLAIMS = { }
+
+ def Encoding.claim( encoding_str, match=nil )
+ if match
+ ENCODING_CLAIMS[ match ] = encoding_str
+ else
+ ENCODING_CLAIMS[ /^\s*<?xml\s*version=(['"]).*?\1\s*encoding=(["'])#{encoding_str}\2/i ] = encoding_str
+ end
+ end
+
+ # Native, default format is UTF-8, so it is declared here rather than in
+ # an encodings/ definition.
+ UTF_8 = 'UTF-8'
+ claim( UTF_8 )
+
+ # ID ---> Encoding name
+ attr_reader :encoding
+ def encoding=( enc )
+ enc = UTF_8 unless enc
+ @encoding = enc.upcase
+ require "rexml/encodings/#@encoding" unless @encoding == UTF_8
+ end
+
+ def check_encoding str
+ rv = ENCODING_CLAIMS.find{|k,v| str =~ k }
+ # Raise an exception if there is a declared encoding and we don't
+ # recognize it
+ unless rv
+ if str =~ /^\s*<?xml\s*version=(['"]).*?\1\s*encoding=(["'])(.*?)\2/
+ raise "A matching encoding handler was not found for encoding '#{$3}', or the encoding handler failed to load due to a missing support library (such as uconv)."
+ else
+ return UTF_8
+ end
+ end
+ return rv[1]
+ end
+
+ def to_utf_8(str)
+ return str
+ end
+
+ def from_utf_8 content
+ return content
+ end
+ end
+
+ module Encodingses
+ encodings = []
+ $:.each do |incl_dir|
+ if Dir[ File.join(incl_dir, 'rexml', 'encodings') ].size > 0
+ encodings |= Dir[ File.join(incl_dir, 'rexml', 'encodings', '*_decl.rb') ]
+ end
+ encodings.collect!{ |f| File.basename(f) }
+ encodings.uniq!
+ end
+ encodings.each { |enc| require "rexml/encodings/#{enc}" }
+ end
+end
diff --git a/lib/rexml/encodings/EUC-JP.rb b/lib/rexml/encodings/EUC-JP.rb
new file mode 100644
index 0000000000..def760b303
--- /dev/null
+++ b/lib/rexml/encodings/EUC-JP.rb
@@ -0,0 +1,32 @@
+begin
+ require 'uconv'
+
+ module REXML
+ module Encoding
+ def from_euc_jp(str)
+ return Uconv::euctou8(str)
+ end
+
+ def to_euc_jp content
+ return Uconv::u8toeuc(content)
+ end
+ end
+ end
+rescue LoadError
+ begin
+ require 'iconv'
+ module REXML
+ module Encoding
+ def from_euc_jp(str)
+ return Iconv::iconv("utf-8", "euc-jp", str).join('')
+ end
+
+ def to_euc_jp content
+ return Iconv::iconv("euc-jp", "utf-8", content).join('')
+ end
+ end
+ end
+ rescue LoadError
+ raise "uconv or iconv is required for Japanese encoding support."
+ end
+end
diff --git a/lib/rexml/encodings/EUC-JP_decl.rb b/lib/rexml/encodings/EUC-JP_decl.rb
new file mode 100644
index 0000000000..4c7cd828a6
--- /dev/null
+++ b/lib/rexml/encodings/EUC-JP_decl.rb
@@ -0,0 +1,6 @@
+module REXML
+ module Encoding
+ EUC_JP = 'EUC-JP'
+ claim( EUC_JP )
+ end
+end
diff --git a/lib/rexml/encodings/ISO-8859-1.rb b/lib/rexml/encodings/ISO-8859-1.rb
new file mode 100644
index 0000000000..98c5aff3b2
--- /dev/null
+++ b/lib/rexml/encodings/ISO-8859-1.rb
@@ -0,0 +1,23 @@
+module REXML
+ module Encoding
+ # Convert from UTF-8
+ def to_iso_8859_1 content
+ array_utf8 = content.unpack('U*')
+ array_enc = []
+ array_utf8.each do |num|
+ if num <= 0xFF
+ 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 from_iso_8859_1(str)
+ str.unpack('C*').pack('U*')
+ end
+ end
+end
diff --git a/lib/rexml/encodings/ISO-8859-1_decl.rb b/lib/rexml/encodings/ISO-8859-1_decl.rb
new file mode 100644
index 0000000000..a738d30472
--- /dev/null
+++ b/lib/rexml/encodings/ISO-8859-1_decl.rb
@@ -0,0 +1,6 @@
+module REXML
+ module Encoding
+ ISO_8859_1 = 'ISO-8859-1'
+ claim( ISO_8859_1 )
+ end
+end
diff --git a/lib/rexml/encodings/SHIFT-JIS.rb b/lib/rexml/encodings/SHIFT-JIS.rb
new file mode 100644
index 0000000000..2c2a6890c8
--- /dev/null
+++ b/lib/rexml/encodings/SHIFT-JIS.rb
@@ -0,0 +1 @@
+require 'rexml/encodings/SHIFT_JIS'
diff --git a/lib/rexml/encodings/SHIFT_JIS.rb b/lib/rexml/encodings/SHIFT_JIS.rb
new file mode 100644
index 0000000000..27e4569403
--- /dev/null
+++ b/lib/rexml/encodings/SHIFT_JIS.rb
@@ -0,0 +1,33 @@
+begin
+ require 'uconv'
+
+ module REXML
+ module Encoding
+ def to_shift_jis content
+ Uconv::u8tosjis(content)
+ end
+
+ def from_shift_jis(str)
+ Uconv::sjistou8(str)
+ end
+ end
+ end
+rescue LoadError
+ begin
+ require 'iconv'
+ module REXML
+ module Encoding
+ def from_shift_jis(str)
+ return Iconv::iconv("utf-8", "shift_jis", str).join('')
+ end
+
+ def to_shift_jis content
+ return Iconv::iconv("shift_jis", "utf-8", content).join('')
+ end
+ end
+ end
+ rescue LoadError
+ raise "uconv or iconv is required for Japanese encoding support."
+ end
+
+end
diff --git a/lib/rexml/encodings/Shift-JIS_decl.rb b/lib/rexml/encodings/Shift-JIS_decl.rb
new file mode 100644
index 0000000000..66f650144a
--- /dev/null
+++ b/lib/rexml/encodings/Shift-JIS_decl.rb
@@ -0,0 +1,6 @@
+module REXML
+ module Encoding
+ claim( 'Shift-JIS' )
+ claim( 'Shift_JIS' )
+ end
+end
diff --git a/lib/rexml/encodings/UNILE.rb b/lib/rexml/encodings/UNILE.rb
new file mode 100644
index 0000000000..74bed14340
--- /dev/null
+++ b/lib/rexml/encodings/UNILE.rb
@@ -0,0 +1,27 @@
+module REXML
+ module Encoding
+ def to_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 from_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
+ end
+end
diff --git a/lib/rexml/encodings/UNILE_decl.rb b/lib/rexml/encodings/UNILE_decl.rb
new file mode 100644
index 0000000000..9e1c11dc03
--- /dev/null
+++ b/lib/rexml/encodings/UNILE_decl.rb
@@ -0,0 +1,6 @@
+module REXML
+ module Encoding
+ UNILE = 'UNILE'
+ claim( UNILE, /^\377\376/ )
+ end
+end
diff --git a/lib/rexml/encodings/US-ASCII.rb b/lib/rexml/encodings/US-ASCII.rb
new file mode 100644
index 0000000000..4ca2c82a83
--- /dev/null
+++ b/lib/rexml/encodings/US-ASCII.rb
@@ -0,0 +1,23 @@
+module REXML
+ module Encoding
+ # Convert from UTF-8
+ def to_us_ascii content
+ array_utf8 = content.unpack('U*')
+ array_enc = []
+ array_utf8.each do |num|
+ if num <= 0xFF
+ 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 from_us_ascii(str)
+ str.unpack('C*').pack('U*')
+ end
+ end
+end
diff --git a/lib/rexml/encodings/US-ASCII_decl.rb b/lib/rexml/encodings/US-ASCII_decl.rb
new file mode 100644
index 0000000000..1e69234fff
--- /dev/null
+++ b/lib/rexml/encodings/US-ASCII_decl.rb
@@ -0,0 +1,6 @@
+module REXML
+ module Encoding
+ US_ASCII = 'US-ASCII'
+ claim( US_ASCII )
+ end
+end
diff --git a/lib/rexml/encodings/UTF-16.rb b/lib/rexml/encodings/UTF-16.rb
new file mode 100644
index 0000000000..2aeef76a0c
--- /dev/null
+++ b/lib/rexml/encodings/UTF-16.rb
@@ -0,0 +1,27 @@
+module REXML
+ module Encoding
+ def to_utf_16 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 from_utf_16(str)
+ array_enc=str.unpack('C*')
+ array_utf8 = []
+ 2.step(arrayEnc.size-1, 2){|i|
+ array_utf8 << (array_enc.at(i+1) + array_enc.at(i)*0x100)
+ }
+ array_utf8.pack('U*')
+ end
+ end
+end
diff --git a/lib/rexml/encodings/UTF-16_decl.rb b/lib/rexml/encodings/UTF-16_decl.rb
new file mode 100644
index 0000000000..f405a9f259
--- /dev/null
+++ b/lib/rexml/encodings/UTF-16_decl.rb
@@ -0,0 +1,6 @@
+module REXML
+ module Encoding
+ UTF_16 = 'UTF-16'
+ claim( UTF_16, /^\376\377/ )
+ end
+end
diff --git a/lib/rexml/entity.rb b/lib/rexml/entity.rb
new file mode 100644
index 0000000000..4b88a3c553
--- /dev/null
+++ b/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/lib/rexml/functions.rb b/lib/rexml/functions.rb
new file mode 100644
index 0000000000..d2d078640b
--- /dev/null
+++ b/lib/rexml/functions.rb
@@ -0,0 +1,360 @@
+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
+ @@node = nil
+ @@index = nil
+ @@size = nil
+ @@variables = {}
+ @@namespace_context = {}
+
+ def Functions::node=(value); @@node = value; end
+ def Functions::index=(value); @@index = value; end
+ def Functions::size=(value); @@size = value; end
+ def Functions::variables=(value); @@variables = value; end
+ def Functions::namespace_context=(value)
+ @@namespace_context = value
+ end
+ def Functions::node; @@node; end
+ def Functions::index; @@index; end
+ def Functions::size; @@size; end
+ def Functions::variables; @@variables; end
+ def Functions::namespace_context; @@namespace_context; end
+
+ def Functions::text( )
+ return true if @@node.node_type == :text
+ end
+
+ def Functions::last( )
+ @@size
+ end
+
+ def Functions::position( )
+ @@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 @@node if defined? @@node.namespace
+ else
+ if node_set.namespace
+ yield node_set
+ else
+ return unless node_set.kind_of? Enumerable
+ node_set.each { |node| yield node if defined? node.namespace }
+ 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)
+ ruby_index = ruby_string.index(string(test))
+ if ruby_index.nil?
+ ""
+ else
+ ruby_string[ ruby_index+1..-1 ]
+ end
+ 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(@@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 = @@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 = @@node unless object
+ if object == true
+ Float(1)
+ elsif object == false
+ Float(0)
+ elsif object.kind_of? Array
+ string( object ).to_f
+ elsif object.kind_of? Float
+ object
+ else
+ object.to_s.to_f
+ end
+ end
+
+ def Functions::sum( nodes )
+ 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::method_missing( id )
+ puts "METHOD MISSING #{id.id2name}"
+ XPath.match( @@node, id.id2name )
+ end
+ end
+end
diff --git a/lib/rexml/instruction.rb b/lib/rexml/instruction.rb
new file mode 100644
index 0000000000..0b770d4b3d
--- /dev/null
+++ b/lib/rexml/instruction.rb
@@ -0,0 +1,62 @@
+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
+ end
+end
diff --git a/lib/rexml/light/node.rb b/lib/rexml/light/node.rb
new file mode 100644
index 0000000000..680f2c23fe
--- /dev/null
+++ b/lib/rexml/light/node.rb
@@ -0,0 +1,231 @@
+require 'rexml/xmltokens'
+require 'rexml/light/node'
+
+# Development model
+# document = Node.new
+
+# Add an element "foo" to the document
+# foo = document << "foo"
+# # Set attribute "attr" on foo
+# foo["attr"] = "la"
+# # Set another attribute in a different namespace
+# foo["attr", "namespace"] = "too"
+# # Swap foo into another namespace
+# foo.namespace = "blah"
+# # Add a couple of element nodes to foo
+# foo << "a"
+# foo << "b"
+# # Access the children of foo in various ways
+# a = foo[0]
+# foo.each { |child|
+# #...
+# }
+# # Add text to foo
+# # Add instruction
+# # Add comment
+# # Get the root of the document
+# document == a.root
+# # Write the document out
+# puts document.to_s
+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 < Array
+ alias :_old_get :[]
+ alias :_old_put :[]=
+
+ NAMESPLIT = /^(?:(#{XMLTokens::NCNAME_STR}):)?(#{XMLTokens::NCNAME_STR})/u
+ # Create a new element.
+ def initialize node=nil
+ if node.kind_of? String
+ node = [ :text, node ]
+ elsif node.nil?
+ node = [ :document, nil, nil ]
+ elsif node[0] == :start_element
+ node[0] = :element
+ end
+ replace( node )
+ _old_put( 1, 0, 1 )
+ _old_put( 1, nil )
+ end
+
+ def size
+ el!()
+ super-4
+ end
+
+ def each( &block )
+ el!()
+ size.times { |x| yield( at(x+4) ) }
+ end
+
+ def name
+ el!()
+ at(2)
+ end
+
+ def name=( name_str, ns=nil )
+ el!()
+ pfx = ''
+ pfx = "#{prefix(ns)}:" if ns
+ _old_put(1, "#{pfx}#{name_str}")
+ end
+
+ def parent=( node )
+ _old_put(1,node)
+ end
+
+ def local_name
+ el!()
+ namesplit
+ @name
+ end
+
+ def local_name=( name_str )
+ el!()
+ _old_put( 1, "#@prefix:#{name_str}" )
+ end
+
+ def prefix( namespace=nil )
+ el!()
+ prefix_of( self, namespace )
+ end
+
+ def namespace( prefix=prefix() )
+ el!()
+ namespace_of( self, prefix )
+ end
+
+ def namespace=( namespace )
+ el!()
+ @prefix = prefix( namespace )
+ pfx = ''
+ pfx = "#@prefix:" if @prefix.size > 0
+ _old_put(1, "#{pfx}#@name")
+ end
+
+ def []( reference, ns=nil )
+ el!()
+ 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 )
+ el!()
+ 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 = '' )
+ el!()
+ at(3) == name and namespace() == namespace
+ end
+
+ def children
+ el!()
+ self
+ end
+
+ def parent
+ at(1)
+ end
+
+ def to_s
+
+ end
+
+ def el!
+ if node_type() != :element and node_type() != :document
+ _old_put( 0, :element )
+ push({})
+ end
+ self
+ 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/lib/rexml/namespace.rb b/lib/rexml/namespace.rb
new file mode 100644
index 0000000000..3e8790580b
--- /dev/null
+++ b/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/lib/rexml/node.rb b/lib/rexml/node.rb
new file mode 100644
index 0000000000..41d9eee43b
--- /dev/null
+++ b/lib/rexml/node.rb
@@ -0,0 +1,35 @@
+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
+ to << " "*ind unless ind<1
+ end
+
+ def parent?
+ false;
+ end
+ end
+end
diff --git a/lib/rexml/output.rb b/lib/rexml/output.rb
new file mode 100644
index 0000000000..7d4ab2e13b
--- /dev/null
+++ b/lib/rexml/output.rb
@@ -0,0 +1,22 @@
+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
+
+ eval <<-EOL
+ alias :encode :to_#{encoding.tr('-', '_').downcase}
+ alias :decode :from_#{encoding.tr('-', '_').downcase}
+ EOL
+ @to_utf = encd == UTF_8 ? false : true
+ end
+
+ def <<( content )
+ @output << (@to_utf ? encode(content) : content)
+ end
+ end
+end
diff --git a/lib/rexml/parent.rb b/lib/rexml/parent.rb
new file mode 100644
index 0000000000..5c1ed97324
--- /dev/null
+++ b/lib/rexml/parent.rb
@@ -0,0 +1,165 @@
+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 )
+ return unless @children.include? object
+ @children.delete object
+ object.parent = nil
+ 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
+
+ # 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 )
+ ind = @children.index( to_replace )
+ to_replace.parent = nil
+ @children[ind,0] = replacement
+ 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/lib/rexml/parseexception.rb b/lib/rexml/parseexception.rb
new file mode 100644
index 0000000000..04928d9175
--- /dev/null
+++ b/lib/rexml/parseexception.rb
@@ -0,0 +1,44 @@
+module REXML
+ class ParseException < Exception
+ 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.message
+ err << "\n"
+ err << @continued_exception.backtrace[0..3].join("\n")
+ err << "\n...\n"
+ else
+ err = ""
+ end
+
+ # Get the stack trace and error message
+ err << super
+
+ # Add contextual information
+ err << "\n#{@source.current_line}\nLast 80 unconsumed characters:\n#{@source.buffer[0..80].gsub(/\n/, ' ')}\n" if @source
+ err << "\nContext:\n#{@parser.context}" if @parser
+ err
+ end
+
+ def position
+ @source.current_line[0] if @source
+ end
+
+ def line
+ @source.current_line[2] if @source
+ end
+
+ def context
+ @source.current_line
+ end
+ end
+end
diff --git a/lib/rexml/parsers/baseparser.rb b/lib/rexml/parsers/baseparser.rb
new file mode 100644
index 0000000000..cfcea16f1d
--- /dev/null
+++ b/lib/rexml/parsers/baseparser.rb
@@ -0,0 +1,394 @@
+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
+ 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+((["']).*?\4)\s*>/um
+ SYSTEM = /^\s*<!NOTATION\s+(\w[\-\w]*)\s+(SYSTEM)\s+((["']).*?\4)\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 stream=( source )
+ if source.kind_of? String
+ @source = Source.new(source)
+ elsif source.kind_of? IO
+ @source = IOSource.new(source)
+ elsif source.kind_of? Source
+ @source = source
+ else
+ raise "#{source.type} is not a valid input stream. It must be \n"+
+ "either a String, IO, or Source."
+ end
+ @closed = nil
+ @document_status = nil
+ @tags = []
+ @stack = []
+ @entities = []
+ end
+
+ # Returns true if there are no more events
+ def empty?
+ !has_next?
+ end
+
+ # Returns true if there are more events. Synonymous with !empty?
+ def has_next?
+ @source.read if @source.buffer.size==0 and !@source.empty?
+ (!@source.empty? and @source.buffer.strip.size>0) or @stack.size>0 or @closed
+ 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 '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
+ return [ :end_document ] if empty?
+ if @closed
+ x, @closed = @closed, nil
+ return [ :end_element, x ]
+ end
+ return @stack.shift if @stack.size > 0
+ @source.read if @source.buffer.size==0
+ if @document_status == nil
+ @source.match( /^\s*/um, true )
+ word = @source.match( /^\s*(<.*?)>/um )
+ word = word[1] unless word.nil?
+ case word
+ when COMMENT_START
+ return [ :comment, @source.match( COMMENT_PATTERN, true )[1] ]
+ when XMLDECL_START
+ 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 "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==0
+ md = @source.match(/^\s*/um, true)
+ @stack << [ :end_doctype ]
+ else
+ @document_status = :in_doctype
+ end
+ return args
+ else
+ @document_status = :after_doctype
+ @source.read if @source.buffer.size==0
+ md = @source.match(/\s*/um, true)
+ end
+ end
+ if @document_status == :in_doctype
+ md = @source.match(/\s*(.*?>)/um)
+ case md[1]
+ 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 )
+ elsif @source.match( SYSTEM )
+ md = @source.match( SYSTEM, true )
+ else
+ raise REXML::ParseException.new( "error parsing notation: no matching pattern", @source )
+ end
+ return [ :notationdecl, md[1], md[2], md[3] ]
+ 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( 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)
+ #puts "SOURCE BUFFER = #{source.buffer}, #{source.buffer.size}"
+ raise REXML::ParseException.new("Malformed node", @source) unless md
+ case md[1]
+ when CDATA_START
+ return [ :cdata, @source.match( CDATA_PATTERN, true )[1] ]
+ when COMMENT_START
+ return [ :comment, @source.match( COMMENT_PATTERN, true )[1] ]
+ else
+ raise REXML::ParseException.new( "Declarations can only occur "+
+ "in the doctype declaration.", @source)
+ end
+ elsif @source.buffer[1] == ??
+ md = @source.match( INSTRUCTION_PATTERN, true )
+ return [ :processing_instruction, md[1], md[2] ]
+ 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)
+ raise "no text to add" 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 )
+ 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[2], 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
diff --git a/lib/rexml/parsers/lightparser.rb b/lib/rexml/parsers/lightparser.rb
new file mode 100644
index 0000000000..e2f083bc8e
--- /dev/null
+++ b/lib/rexml/parsers/lightparser.rb
@@ -0,0 +1,56 @@
+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 rewind
+ @stream.rewind
+ @parser.stream = @stream
+ end
+
+ def parse
+ root = context = REXML::Light::Node.new([ :document ])
+ while true
+ event = @parser.pull
+ case event[0]
+ when :end_document
+ break
+ when :end_doctype
+ context = context.parent
+ when :start_element, :start_doctype
+ new_node = REXML::Light::Node.new(event)
+ context << new_node
+ new_node.parent = context
+ context = new_node
+ when :end_element, :end_doctype
+ context = context.parent
+ else
+ new_node = REXML::Light::Node.new(event)
+ context << new_node
+ new_node.parent = 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/lib/rexml/parsers/pullparser.rb b/lib/rexml/parsers/pullparser.rb
new file mode 100644
index 0000000000..aeda6251fe
--- /dev/null
+++ b/lib/rexml/parsers/pullparser.rb
@@ -0,0 +1,143 @@
+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 < BaseParser
+ include XMLTokens
+
+ def initialize stream
+ super
+ @entities = {}
+ end
+
+ def each
+ while has_next?
+ yield self.pull
+ end
+ end
+
+ def peek depth=0
+ PullEvent.new(super)
+ end
+
+ def pull
+ event = super
+ case event[0]
+ when :entitydecl
+ @entities[ event[1] ] =
+ event[2] unless event[2] =~ /PUBLIC|SYSTEM/
+ when :text
+ unnormalized = unnormalize( event[1], @entities )
+ event << unnormalized
+ end
+ PullEvent.new( event )
+ 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 []( index )
+ @contents[index+1]
+ 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/lib/rexml/parsers/sax2parser.rb b/lib/rexml/parsers/sax2parser.rb
new file mode 100644
index 0000000000..2280b983a3
--- /dev/null
+++ b/lib/rexml/parsers/sax2parser.rb
@@ -0,0 +1,204 @@
+module REXML
+ module Parsers
+ class SAX2Parser
+ def initialize source
+ @parser = BaseParser.new(source)
+ @listeners = []
+ @procs = []
+ @namespace_stack = []
+ @has_listeners = false
+ @tag_stack = []
+ 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
+ #
+ # 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], /.*/, 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, /.*/, 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]
+ 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 )
+ when :processing_instruction, :comment, :doctype, :attlistdecl,
+ :elementdecl, :entitydecl, :cdata, :notationdecl, :xmldecl
+ handle( *event )
+ end
+ 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|
+ (
+ (sym.nil? or symbol == sym) and
+ (name.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? 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 )
+ uri = @namespace_stack.find do |ns|
+ not ns[prefix].nil?
+ end
+ uri[prefix] unless uri.nil?
+ end
+ end
+ end
+end
diff --git a/lib/rexml/parsers/streamparser.rb b/lib/rexml/parsers/streamparser.rb
new file mode 100644
index 0000000000..51441289d9
--- /dev/null
+++ b/lib/rexml/parsers/streamparser.rb
@@ -0,0 +1,33 @@
+module REXML
+ module Parsers
+ class StreamParser
+ def initialize source, listener
+ @listener = listener
+ @parser = BaseParser.new( source )
+ end
+
+ def parse
+ # entity string
+ while true
+ event = @parser.pull
+ case event[0]
+ when :end_document
+ return
+ when :start_element
+ @listener.tag_start( event[1], event[2] )
+ 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 :comment, :doctype, :attlistdecl,
+ :elementdecl, :entitydecl, :cdata, :notationdecl, :xmldecl
+ @listener.send( event[0].to_s, *event[1..-1] )
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/rexml/parsers/ultralightparser.rb b/lib/rexml/parsers/ultralightparser.rb
new file mode 100644
index 0000000000..f3b208bf0f
--- /dev/null
+++ b/lib/rexml/parsers/ultralightparser.rb
@@ -0,0 +1,52 @@
+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 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/lib/rexml/parsers/xpathparser.rb b/lib/rexml/parsers/xpathparser.rb
new file mode 100644
index 0000000000..da27e7c705
--- /dev/null
+++ b/lib/rexml/parsers/xpathparser.rb
@@ -0,0 +1,598 @@
+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 = LocationPath(path, parsed)
+ parsed
+ end
+
+ def predicate path
+ parsed = []
+ Predicate( "[#{path}]", parsed )
+ parsed
+ end
+
+ def to_string( path )
+ string = ""
+ while path.size > 0
+ case path[0]
+ when :ancestor, :ancestor_or_self, :attribute, :child, :descendant, :descendant_or_self, :following, :following_sibling, :namespace, :parent, :preceding, :preceding_sibling, :self
+ op = path.shift
+ string << "/" unless string.size == 0
+ string << op.to_s
+ string << "::"
+ when :any
+ path.shift
+ string << "*"
+ when :qname
+ path.shift
+ prefix = path.shift
+ name = path.shift
+ string << prefix+":" if prefix.size > 0
+ string << name
+ when :predicate
+ path.shift
+ string << '['
+ string << predicate_to_string( path.shift )
+ string << ' ]'
+ else
+ string << "/" unless string.size == 0
+ string << "UNKNOWN("
+ string << path.shift.inspect
+ string << ")"
+ end
+ end
+ return string
+ end
+
+ def predicate_to_string( path )
+ string = ""
+ case path[0]
+ when :and, :or, :mult, :plus, :minus, :neq, :eq, :lt, :gt, :lteq, :gteq, :div, :mod, :neq, :union
+ op = path.shift
+ left = predicate_to_string( path.shift )
+ right = predicate_to_string( path.shift )
+ 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 )
+ string << " )"
+ when :literal
+ path.shift
+ string << " "
+ string << path.shift.inspect
+ string << " "
+ else
+ string << " "
+ string << to_string( 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]*)(?:\()/
+ fname = $1
+ path = $'
+ return nil if fname =~ NT
+ 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
+ depth = 1
+ begin
+ 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 = 0
+ 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/lib/rexml/quickpath.rb b/lib/rexml/quickpath.rb
new file mode 100644
index 0000000000..c099db8579
--- /dev/null
+++ b/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/lib/rexml/rexml.rb b/lib/rexml/rexml.rb
new file mode 100644
index 0000000000..b7de03f3f9
--- /dev/null
+++ b/lib/rexml/rexml.rb
@@ -0,0 +1,26 @@
+# REXML is an XML parser for Ruby, in Ruby.
+#
+# URL: http://www.germane-software.com/software/rexml
+# Author: Sean Russell <ser@germane-software.com>
+# Version: 2.5.6
+# Date: +2003/054
+
+
+
+#
+# Short Description:
+# Why did I write REXML? At the time of this writing, there were already
+# two XML parsers for Ruby. The first is a Ruby binding to a native XML
+# parser. This is a fast parser, using proven technology. However,
+# it isn't very portable. The second is a native Ruby implementation, but
+# I didn't like its API very much. I wrote REXML for myself, so that I'd
+# have an XML parser that had an intuitive API.
+#
+# API documentation can be downloaded from the REXML home page, or can
+# be accessed online at http://www.germane-software.com/software/rexml_doc
+# A tutorial is available in docs/tutorial.html
+module REXML
+ Copyright = "Copyright #{Time.now.year} Sean Russell <ser@germane-software.com>"
+ Date = "+2003/110"
+ Version = "2.7.1"
+end
diff --git a/lib/rexml/sax2listener.rb b/lib/rexml/sax2listener.rb
new file mode 100644
index 0000000000..40a77ed464
--- /dev/null
+++ b/lib/rexml/sax2listener.rb
@@ -0,0 +1,94 @@
+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
+ def xmldecl version, encoding, standalone
+ end
+ # Called when a comment is encountered.
+ # @p comment The content of the comment
+ def comment comment
+ end
+ end
+end
diff --git a/lib/rexml/source.rb b/lib/rexml/source.rb
new file mode 100644
index 0000000000..8c175785b7
--- /dev/null
+++ b/lib/rexml/source.rb
@@ -0,0 +1,191 @@
+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 = Source.new(arg)
+ elsif arg.kind_of? IO
+ source = IOSource.new(arg)
+ end
+ source
+ 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 )
+ #@buffer = decode(@buffer) unless @encoding == UTF_8
+ @line = 0
+ end
+
+ # Inherited from Encoding
+ # Overridden to support optimized en/decoding
+ def encoding=(enc)
+ super
+ eval <<-EOL
+ alias :encode :to_#{encoding.tr('-', '_').downcase}
+ alias :decode :from_#{encoding.tr('-', '_').downcase}
+ EOL
+ @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, consume=false
+ return nil if @buffer.nil?
+ rv = @buffer.scan(pattern)
+ @buffer = $' if consume and rv.size>0
+ rv
+ end
+
+ def read
+ end
+
+ def match pattern, consume=false
+ md = pattern.match @buffer
+ @buffer = $' if consume and md
+ return md
+ end
+
+ # @return true if the Source is exhausted
+ def empty?
+ @buffer.nil? or @buffer.strip.nil?
+ 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
+
+ def initialize arg, block_size=500
+ @er_source = @source = arg
+ @to_utf = false
+ # READLINE OPT
+ # The following was commented out when IOSource started using readline
+ # to pull the data from the stream.
+ #@block_size = block_size
+ #super @source.read(@block_size)
+ @line_break = '>'
+ super @source.readline( @line_break )
+ end
+
+ def scan pattern, consume=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('>')
+ str = decode(str) if @to_utf and str
+ @buffer << str
+ rescue
+ @source = nil
+ end
+ end
+
+ def match pattern, consume=false
+ rv = pattern.match(@buffer)
+ @buffer = $' if consume and rv
+ while !rv and @source
+ begin
+ str = @source.readline('>')
+ str = decode(str) if @to_utf and str
+ @buffer << str
+ rv = pattern.match(@buffer)
+ @buffer = $' if consume and rv
+ rescue
+ @source = nil
+ end
+ end
+ rv.taint
+ rv
+ end
+
+ def empty?
+ super and ( @source.nil? || @source.eof? )
+ end
+
+ # @return the current line in the source
+ def current_line
+ 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
+ [pos, lineno, line]
+ end
+ end
+end
diff --git a/lib/rexml/streamlistener.rb b/lib/rexml/streamlistener.rb
new file mode 100644
index 0000000000..3c3c5e3684
--- /dev/null
+++ b/lib/rexml/streamlistener.rb
@@ -0,0 +1,89 @@
+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
+ # 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/lib/rexml/text.rb b/lib/rexml/text.rb
new file mode 100644
index 0000000000..906f4d41fc
--- /dev/null
+++ b/lib/rexml/text.rb
@@ -0,0 +1,279 @@
+require 'rexml/entity'
+
+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.
+ # Text.new( "<&", false, nil, false ) #-> "&lt;&amp;"
+ # Text.new( "<&", false, nil, true ) #-> IllegalArgumentException
+ # 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 Exception.new( "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 Exception.new(
+ "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
+
+ # 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.string #-> "< & sean russell"
+ # t = Text.new( "< & &s; russell", false, nil, false )
+ # t.string #-> "< & sean russell"
+ # u = Text.new( "sean russell", false, nil, true )
+ # u.string #-> "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 write( writer, indent=-1, transitive=false, ie_hack=false )
+ writer << to_s()
+ 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.clone
+ # Doing it like this rather than in a loop improves the speed
+ if doctype
+ copy.gsub!( EREFERENCE, '&amp;' )
+ doctype.entities.each_value do |entity|
+ copy.gsub!( entity.value,
+ "&#{entity.name};" ) if entity.value and
+ not( entity_filter and entity_filter.include?(entity) )
+ end
+ else
+ copy.gsub!( EREFERENCE, '&amp;' )
+ DocType::DEFAULT_ENTITIES.each_value do |entity|
+ 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/lib/rexml/xmldecl.rb b/lib/rexml/xmldecl.rb
new file mode 100644
index 0000000000..6a6cc31a53
--- /dev/null
+++ b/lib/rexml/xmldecl.rb
@@ -0,0 +1,72 @@
+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
+
+ def initialize(version=DEFAULT_VERSION, encoding=nil, standalone=nil)
+ @encoding_set = !encoding.nil?
+ if version.kind_of? XMLDecl
+ super()
+ @version = version.version
+ self.encoding = version.encoding
+ @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
+ indent( writer, indent )
+ writer << START.sub(/\\/u, '')
+ writer << " #{content}"
+ 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
+ @encoding_set = !encoding.nil?
+ self.encoding = encoding
+ @standalone = standalone
+ end
+
+ def node_type
+ :xmldecl
+ end
+
+ alias :stand_alone? :standalone
+
+ private
+ def content
+ rv = "version='#@version'"
+ rv << " encoding='#{encoding}'" if @encoding_set
+ rv << " standalone='#@standalone'" if @standalone
+ rv
+ end
+ end
+end
diff --git a/lib/rexml/xmltokens.rb b/lib/rexml/xmltokens.rb
new file mode 100644
index 0000000000..6bbe5b07d5
--- /dev/null
+++ b/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/lib/rexml/xpath.rb b/lib/rexml/xpath.rb
new file mode 100644
index 0000000000..c9c216fe27
--- /dev/null
+++ b/lib/rexml/xpath.rb
@@ -0,0 +1,62 @@
+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={}
+ parser = XPathParser.new
+ parser.namespaces = namespaces
+ parser.variables = variables
+ path = "*" unless path
+ element = [element] unless element.kind_of? Array
+ parser.parse(path, element)[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
+ 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/lib/rexml/xpath_parser.rb b/lib/rexml/xpath_parser.rb
new file mode 100644
index 0000000000..215078b766
--- /dev/null
+++ b/lib/rexml/xpath_parser.rb
@@ -0,0 +1,530 @@
+require 'rexml/namespace'
+require 'rexml/xmltokens'
+require 'rexml/parsers/xpathparser'
+
+# Ignore this class. It adds a __ne__ method, because Ruby doesn't seem to
+# understand object.send( "!=", foo ), whereas it *does* understand "<", "==",
+# and all of the other comparison methods. Stupid, and annoying, and not at
+# all POLS.
+class Object
+ def __ne__(b)
+ self != b
+ 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
+ path_stack = @parser.parse( path )
+ #puts "PARSE: #{path} => #{path_stack.inspect}"
+ match( path_stack, nodeset )
+ end
+
+ def predicate path, nodeset
+ path_stack = @parser.predicate( path )
+ return Predicate( path_stack, nodeset )
+ end
+
+ def []=( variable_name, value )
+ @variables[ variable_name ] = value
+ end
+
+ private
+
+ def match( path_stack, nodeset )
+ while ( path_stack.size > 0 and nodeset.size > 0 )
+ #puts "PARSE: #{path_stack.inspect} '#{nodeset.collect{|n|n.type}.inspect}'"
+ nodeset = internal_parse( path_stack, nodeset )
+ #puts "NODESET: #{nodeset.size}"
+ #puts "PATH_STACK: #{path_stack.inspect}"
+ end
+ nodeset
+ end
+
+ def internal_parse path_stack, nodeset
+ return nodeset if nodeset.size == 0 or path_stack.size == 0
+ #puts "INTERNAL_PARSE: #{path_stack.inspect}, #{nodeset.collect{|n| n.type}.inspect}"
+ case path_stack.shift
+ when :document
+ return [ nodeset[0].root.parent ]
+
+ when :qname
+ prefix = path_stack.shift
+ name = path_stack.shift
+ #puts "QNAME #{prefix}#{prefix.size>0?':':''}#{name}"
+ n = nodeset.clone
+ ns = @namespaces[prefix]
+ ns = ns ? ns : ''
+ n.delete_if do |node|
+ # FIXME: This DOUBLES the time XPath searches take
+ ns = node.namespace( prefix ) if node.node_type == :element and ns == ''
+ #puts "NODE: '#{node.to_s}'; node.has_name?( #{name.inspect}, #{ns.inspect} ): #{ node.has_name?( name, ns )}; node.namespace() = #{node.namespace().inspect}; node.prefix = #{node.prefix().inspect}" if node.node_type == :element
+ !(node.node_type == :element and node.name == name and node.namespace == ns )
+ end
+ return n
+
+ when :any
+ n = nodeset.clone
+ n.delete_if { |node| node.node_type != :element }
+ return n
+
+ when :self
+ # THIS SPACE LEFT INTENTIONALLY BLANK
+
+ when :processing_instruction
+ target = path_stack.shift
+ n = nodeset.clone
+ n.delete_if do |node|
+ (node.node_type != :processing_instruction) or
+ ( !target.nil? and ( node.target != target ) )
+ end
+ return n
+
+ when :text
+ #puts ":TEXT"
+ n = nodeset.clone
+ n.delete_if do |node|
+ #puts "#{node} :: #{node.node_type}"
+ node.node_type != :text
+ end
+ return n
+
+ when :comment
+ n = nodeset.clone
+ n.delete_if do |node|
+ node.node_type != :comment
+ end
+ return n
+
+ when :node
+ return nodeset
+ #n = nodeset.clone
+ #n.delete_if do |node|
+ # !node.node?
+ #end
+ #return n
+
+ # FIXME: I suspect the following XPath will fail:
+ # /a/*/*[1]
+ when :child
+ #puts "CHILD"
+ new_nodeset = []
+ ps_clone = nil
+ for node in nodeset
+ #ps_clone = path_stack.clone
+ #new_nodeset += internal_parse( ps_clone, node.children ) if node.parent?
+ new_nodeset += node.children if node.parent?
+ end
+ #path_stack[0,(path_stack.size-ps_clone.size)] = []
+ return new_nodeset
+
+ when :literal
+ literal = path_stack.shift
+ if literal =~ /^\d+(\.\d+)?$/
+ return ($1 ? literal.to_f : literal.to_i)
+ end
+ #puts "RETURNING '#{literal}'"
+ return literal
+
+ when :attribute
+ #puts ":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
+ #puts "looking for attribute #{name} in '#{@namespaces[prefix]}'"
+ attr = element.attribute( name, @namespaces[prefix] )
+ #puts ":ATTRIBUTE: attr => #{attr}"
+ new_nodeset << attr if attr
+ end
+ end
+ when :any
+ for element in nodeset
+ if element.node_type == :element
+ attr = element.attributes
+ end
+ end
+ end
+ #puts "RETURNING #{new_nodeset.collect{|n|n.to_s}.inspect}"
+ return new_nodeset
+
+ when :parent
+ return internal_parse( path_stack, nodeset.collect{|n| n.parent}.compact )
+
+ when :ancestor
+ #puts "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.uniq
+ return new_nodeset
+
+ 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.includes? node
+ end
+ end
+ end
+ #nodeset = new_nodeset.uniq
+ return new_nodeset
+
+ when :predicate
+ #puts "@"*80
+ #puts "NODESET = #{nodeset.collect{|n|n.to_s}.inspect}"
+ predicate = path_stack.shift
+ new_nodeset = []
+ Functions::size = nodeset.size
+ nodeset.size.times do |index|
+ node = nodeset[index]
+ Functions::node = node
+ Functions::index = index+1
+ #puts "Node #{node} and index=#{index+1}"
+ result = Predicate( predicate, node )
+ #puts "Predicate returned #{result} (#{result.type}) for #{node.type}"
+ if result.kind_of? Numeric
+ #puts "#{result} == #{index} => #{result == index}"
+ new_nodeset << node if result == (index+1)
+ elsif result.instance_of? Array
+ new_nodeset << node if result.size > 0
+ else
+ new_nodeset << node if result
+ end
+ end
+ #puts "Nodeset after predicate #{predicate.inspect} has #{new_nodeset.size} nodes"
+ #puts "NODESET: #{new_nodeset.collect{|n|n.to_s}.inspect}"
+ return new_nodeset
+
+ when :descendant_or_self
+ rv = descendant_or_self( path_stack, nodeset )
+ path_stack.clear
+ return rv
+
+ when :descendant
+ #puts ":DESCENDANT"
+ results = []
+ for node in nodeset
+ results += internal_parse( path_stack.clone.unshift( :descendant_or_self ),
+ node.children ) if node.parent?
+ end
+ return results
+
+ when :following_sibling
+ 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 += internal_parse( path_stack.clone, following_siblings )
+ end
+ return 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 ]
+ results += internal_parse( path_stack.clone, preceding_siblings )
+ end
+ return results
+
+ when :preceding
+ new_nodeset = []
+ for node in nodeset
+ new_nodeset += preceding( node )
+ end
+ return new_nodeset
+
+ when :following
+ new_nodeset = []
+ for node in nodeset
+ new_nodeset += following( node )
+ end
+ return new_nodeset
+
+ when :namespace
+ new_set = []
+ for node in nodeset
+ new_nodeset << node.namespace if node.node_type == :element or node.node_type == :attribute
+ end
+ return new_nodeset
+
+ when :variable
+ var_name = path_stack.shift
+ return @variables[ var_name ]
+
+ end
+ nodeset
+ end
+
+ ##########################################################
+ # 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 = []
+ d_o_s( path_stack, nodeset, rs )
+ #puts "RS = #{rs.collect{|n|n.to_s}.inspect}"
+ rs.flatten.compact
+ end
+
+ def d_o_s( p, ns, r )
+ #puts r.collect{|n|n.to_s}.inspect
+ #puts ns.collect{|n|n.to_s}.inspect
+ ns.each_index do |i|
+ n = ns[i]
+ x = match( p.clone, [ n ] )
+ #puts "Got a match on #{p.inspect} for #{ns.collect{|n|n.to_s+"("+n.type.to_s+")"}.inspect}"
+ d_o_s( p, n.children, x ) if n.parent?
+ r[i,0] = [x] if x.size > 0
+ end
+ end
+
+ def recurse( nodeset, &block )
+ for node in nodeset
+ yield node
+ recurse( node, &block ) if node.node_type == :element
+ end
+ end
+
+
+ # Given a predicate, a node, and a context, evaluates to true or false.
+ def Predicate( predicate, node )
+ predicate = predicate.clone
+ #puts "#"*20
+ #puts "Predicate( #{predicate.inspect}, #{node.type} )"
+ results = []
+ case (predicate[0])
+ when :and, :or, :eq, :neq, :lt, :lteq, :gt, :gteq
+ eq = predicate.shift
+ left = Predicate( predicate.shift, node )
+ right = Predicate( predicate.shift, node )
+ return equality_relational_compare( left, eq, right )
+
+ when :div, :mod, :mult, :plus, :minus, :union
+ op = predicate.shift
+ left = Predicate( predicate.shift, node )
+ right = Predicate( predicate.shift, node )
+ left = Functions::number( left )
+ right = Functions::number( right )
+ case op
+ when :div
+ return left.to_f / right.to_f
+ when :mod
+ return left % right
+ when :mult
+ return left * right
+ when :plus
+ return left + right
+ when :minus
+ return left - right
+ when :union
+ return (left | right)
+ end
+
+ when :neg
+ predicate.shift
+ operand = Functions::number(Predicate( predicate, node ))
+ return -operand
+
+ when :not
+ predicate.shift
+ return !Predicate( predicate.shift, node )
+
+ when :function
+ predicate.shift
+ func_name = predicate.shift.tr('-', '_')
+ arguments = predicate.shift
+ #puts "\nFUNCTION: #{func_name}"
+ #puts "ARGUMENTS: #{arguments.inspect} #{node.to_s}"
+ args = arguments.collect { |arg| Predicate( arg, node ) }
+ #puts "FUNCTION: #{func_name}( #{args.collect{|n|n.to_s}.inspect} )"
+ result = Functions.send( func_name, *args )
+ #puts "RESULTS: #{result.inspect}"
+ return result
+
+ else
+ return match( predicate, [ node ] )
+
+ end
+ end
+
+ # Builds a nodeset of all of the following nodes of the supplied node,
+ # in document order
+ def following( node )
+ all_siblings = node.parent.children
+ current_index = all_siblings.index( node )
+ following_siblings = all_siblings[ current_index+1 .. -1 ]
+ following = []
+ recurse( following_siblings ) { |node| following << node }
+ following.shift
+ #puts "following is returning #{puta following}"
+ following
+ end
+
+ # Builds a nodeset of all of the preceding nodes of the supplied node,
+ # in reverse document order
+ def preceding( node )
+ all_siblings = node.parent.children
+ current_index = all_siblings.index( node )
+ preceding_siblings = all_siblings[ 0 .. current_index-1 ]
+
+ preceding_siblings.reverse!
+ preceding = []
+ recurse( preceding_siblings ) { |node| preceding << node }
+ preceding.reverse
+ end
+
+ def equality_relational_compare( set1, op, set2 )
+ #puts "EQ_REL_COMP: #{set1.to_s}, #{op}, #{set2.to_s}"
+ if set1.kind_of? Array and set2.kind_of? Array
+ if set1.size == 1 and set2.size == 1
+ set1 = set1[0]
+ set2 = set2[0]
+ else
+ set1.each do |i1|
+ i1 = i1.to_s
+ set2.each do |i2|
+ i2 = i2.to_s
+ return true if compare( i1, op, i2 )
+ end
+ end
+ return false
+ end
+ end
+ #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.to_s
+ else
+ a = set2
+ b = set1.to_s
+ end
+
+ case b
+ when 'true', 'false'
+ b = Functions::boolean( b )
+ for v in a
+ v = Functions::boolean(v)
+ return true if compare( v, op, b )
+ end
+ when /^\d+(\.\d+)?$/
+ b = Functions::number( b )
+ for v in a
+ v = Functions::number(v)
+ return true if compare( v, op, b )
+ end
+ else
+ b = Functions::string( b )
+ for v in a
+ v = Functions::string(v)
+ return true if compare( v, op, b )
+ end
+ 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}"
+ return compare( set1, op, set2 )
+ end
+ return false
+ end
+
+ def compare a, op, b
+ 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/lib/rubyunit.rb b/lib/rubyunit.rb
new file mode 100644
index 0000000000..8f1086c81d
--- /dev/null
+++ b/lib/rubyunit.rb
@@ -0,0 +1,8 @@
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+require 'runit/testcase'
+require 'test/unit'
+
+TestCase = RUNIT::TestCase
diff --git a/lib/runit/assert.rb b/lib/runit/assert.rb
new file mode 100644
index 0000000000..ede7df1c92
--- /dev/null
+++ b/lib/runit/assert.rb
@@ -0,0 +1,71 @@
+# 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, actual_string, expected_re) {
+ | arg1, arg2 |
+ "Expected <#{arg1}> to match <#{arg2}>"
+ }
+ assert_block(full_message) {
+ expected_re =~ actual_string
+ }
+ Regexp.last_match
+ }
+ 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/lib/runit/cui/testrunner.rb b/lib/runit/cui/testrunner.rb
new file mode 100644
index 0000000000..0106b6c859
--- /dev/null
+++ b/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 ? PROGRESS_ONLY : NORMAL)
+ 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/lib/runit/error.rb b/lib/runit/error.rb
new file mode 100644
index 0000000000..4a727fb02b
--- /dev/null
+++ b/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/lib/runit/testcase.rb b/lib/runit/testcase.rb
new file mode 100644
index 0000000000..4576cb8644
--- /dev/null
+++ b/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/lib/runit/testresult.rb b/lib/runit/testresult.rb
new file mode 100644
index 0000000000..7f70778171
--- /dev/null
+++ b/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/lib/runit/testsuite.rb b/lib/runit/testsuite.rb
new file mode 100644
index 0000000000..63baf65707
--- /dev/null
+++ b/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/lib/runit/topublic.rb b/lib/runit/topublic.rb
new file mode 100644
index 0000000000..566f0dd35c
--- /dev/null
+++ b/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/lib/scanf.rb b/lib/scanf.rb
new file mode 100644
index 0000000000..e610cea24a
--- /dev/null
+++ b/lib/scanf.rb
@@ -0,0 +1,697 @@
+# scanf for Ruby
+#
+# $Release Version: 1.1.2 $
+# $Revision$
+# $Id$
+# $Author$
+# $Date$
+#
+# 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
+ attr_writer :i
+
+ 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)
+ s = str.dup
+ s.sub!(/\A\s+/,'') unless count_space?
+ res = to_re.match(s)
+ if res
+ @conversion = send(@handler, res[1])
+ @matched_string = @matched_item.to_s
+ 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 || 0
+ end
+
+ def mid_match?
+ cc_no_width = letter == '[' && width.zero?
+ c_or_cc_width = (letter == 'c' || letter == '[') &&! width.zero?
+ c_or_cc_open = c_or_cc_width && (matched_string.size < width)
+
+ return c_or_cc_open || 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 = []
+ 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
+ @spec_string
+ 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.
+#
+# Match succeeds (non-emptily)
+# and the last attempted spec/string sub-match succeeded:
+#
+# is the current matched spec a '%[...]' or '%c' with a width?
+# yes: is current.string.size < available width?
+# yes: save interim results
+# no: width is used up, so move on (next)
+# no: is it a '%[...]' with no width?
+# yes: evidently nothing violated it yet, so store
+# interim results and continue (next)
+#
+# 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
+ matched_so_far = 0
+ source_buffer = ""
+ result_buffer = []
+ final_result = []
+
+ fstr = Scanf::FormatString.new(str)
+
+ loop do
+ if eof
+ final_result.concat(result_buffer)
+ break
+ end
+
+ source_buffer << gets
+ current_match = fstr.match(source_buffer)
+
+ spec = fstr.last_spec_tried
+
+ if fstr.last_match_tried
+ 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
+ end
+
+ def block_scanf(str)
+ final = []
+ begin
+ current = scanf(str)
+ final.push(yield(current)) unless current.empty?
+ end until current.empty? || eof
+ 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)
+ STDIN.scanf(fs)
+ end
+end
diff --git a/lib/set.rb b/lib/set.rb
index 05a9455d2c..935c07c3e0 100644
--- a/lib/set.rb
+++ b/lib/set.rb
@@ -8,223 +8,47 @@
#
# You can redistribute and/or modify it under the same terms as Ruby.
#
+# $Id$
+#
+# This library provides the Set class that 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.
+#
+#== Example
+#
+# require 'set'
+#
+# set1 = Set.new ["foo", "bar", "baz"]
+#
+# p set1 #=> #<Set: {"baz", "foo", "bar"}>
+#
+# p set1.include?("bar") #=> true
+#
+# set1.add("heh")
+# set1.delete("foo")
+#
+# p set1 #=> #<Set: {"heh", "baz", "bar"}>
-=begin
-= set.rb
-
-This library provides the Set class that 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.
-
-== Example
-
- require 'set'
-
- set1 = Set.new ["foo", "bar", "baz"]
-
- p set1 #=> #<Set: {"baz", "foo", "bar"}>
-
- p set1.include?("bar") #=> true
-
- set1.add("heh")
- set1.delete("foo")
-
- p set1 #=> #<Set: {"heh", "baz", "bar"}>
-
-== Set class
-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.
-
-The equality of each couple of elements is determined according to
-Object#eql? and Object#hash, since Set uses Hash as storage.
-
-=== Included Modules
- Enumerable
-
-=== Class Methods
---- Set::new(enum = nil)
---- Set::new(enum = nil) { |o| ... }
- 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.
-
---- Set[*ary]
- Creates a new set containing the given objects.
-
-=== Instance Methods
---- dup
- Duplicates the set.
-
---- size
---- length
- Returns the number of elements.
-
---- empty?
- Returns true if the set contains no elements.
-
---- clear
- Removes all elements and returns self.
-
---- replace(enum)
- Replaces the contents of the set with the contents of the given
- enumerable object and returns self.
-
---- flatten
- Returns a new set that is a copy of the set, flattening each
- containing set recursively.
-
---- flatten!
- Equivalent to Set#flatten, but replaces the receiver with the
- result in place. Returns nil if no modifications were made.
-
---- to_a
- Converts the set to an array. (the order is uncertain)
-
---- include?(o)
---- member?(o)
- Returns true if the set contains the given object.
-
---- superset?(set)
- Returns true if the set is a superset of or is equal to the given
- set.
-
---- proper_superset?(set)
- Returns true if the set is a superset of or is equal to the given
- set.
-
---- subset?(set)
- Returns true if the set is a proper subset of the given set.
-
---- proper_subset?(set)
- Returns true if the set is a proper subset of the given set.
-
---- each { |o| ... }
- Calls the given block once for each element in the set, passing
- the element as parameter.
-
---- add(o)
---- << o
- Adds the given object to the set and returns self.
-
---- add?(o)
- Adds the given object to the set and returns self. If it the
- object is already in the set, returns nil.
-
---- delete(o)
- Deletes the given object from the set and returns self.
-
---- delete?(o)
- Deletes the given object from the set and returns self. If the
- object is not in the set, returns nil.
-
---- delete_if { |o| ... }
- Deletes every element of the set for which block evaluates to
- true, and returns self.
-
---- collect! { |o| ... }
---- map! { |o| ... }
- Do collect() destructively.
-
---- reject! { |o| ... }
- Equivalent to Set#delete_if, but returns nil if no changes were
- made.
-
---- merge(enum)
- Merges the elements of the given enumerable object to the set and
- returns self.
-
---- subtract(enum)
- Deletes every element that appears in the given enumerable object
- and returns self.
-
---- + enum
---- | enum
---- union(enum)
- Returns a new set built by merging the set and the elements of the
- given enumerable object.
-
---- - enum
- Returns a new set built by duplicating the set, removing every
- element that appear in the given enumerable object.
-
---- & enum
---- intersection(enum)
- Returns a new array containing elements common to the set and the
- given enumerable object.
-
---- ^ enum
- Returns a new array containing elements exclusive between the set
- and the given enumerable object. (set ^ enum) is equivalent to
- ((set | enum) - (set & enum)).
-
---- == set
- Returns true if two sets are equal. The equality of each couple
- of elements is defined according to Object#eql?.
-
---- classify { |o| ... }
- 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"}>}
-
---- divide { |o| ... }
---- divide { |o1, o2| ... }
- 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}>}>
-
---- inspect
- Returns a string containing a human-readable representation of the
- set. ("#<Set: {element1, element2, ...}>")
-
-== SortedSet class
-SortedSet implements a set which elements are sorted in order.
-
-=== Super class
- Set
-
-== Enumerable module
-
-=== Instance Methods
---- to_set(klass = Set, *args)
---- to_set(klass = Set, *args) { |o| ... }
- Makes a set from the enumerable object with given arguments.
-
-=end
-
+# 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.
+#
+# The equality of each couple of elements is determined according to
+# Object#eql? and Object#hash, since Set uses Hash as storage.
class Set
include Enumerable
+ # Creates a new set containing the given objects.
def self.[](*ary)
new(ary)
end
- def initialize(enum = nil, &block)
+ # 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
@@ -236,6 +60,7 @@ class Set
end
end
+ # Duplicates the set.
def dup
myhash = @hash
self.class.new.instance_eval {
@@ -244,20 +69,25 @@ class Set
}
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 })
@@ -270,6 +100,7 @@ class Set
self
end
+ # Converts the set to an array. The order of elements is uncertain.
def to_a
@hash.keys
end
@@ -293,10 +124,14 @@ class Set
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())
@@ -305,45 +140,56 @@ class Set
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.
def add(o)
@hash[o] = true
self
end
alias << add
+ # Adds the given object to the set and returns self. If it the
+ # object is already in the set, returns nil.
def add?(o)
if include?(o)
nil
@@ -352,11 +198,14 @@ class Set
end
end
+ # Deletes the given object from the set and returns self.
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)
@@ -365,11 +214,14 @@ class Set
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) }
@@ -377,12 +229,16 @@ class 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.class == self.class
@hash.update(enum.instance_eval { @hash })
@@ -394,12 +250,16 @@ class Set
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)
@@ -407,12 +267,16 @@ class Set
alias + | ##
alias union | ##
+ # Returns a new set built by duplicating the set, removing every
+ # element that appear 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
@@ -421,6 +285,9 @@ class Set
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
@@ -428,6 +295,8 @@ class Set
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
@@ -436,15 +305,28 @@ class Set
set.all? { |o| include?(o) }
end
- def hash
+ def hash # :nodoc:
@hash.hash
end
- def eql?(o)
+ def eql?(o) # :nodoc:
@hash.hash == o.hash
end
- def classify
+ # 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|
@@ -455,11 +337,27 @@ class Set
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 = {}
+ class << dig = {} # :nodoc:
include TSort
alias tsort_each_node each_key
@@ -485,6 +383,8 @@ class Set
InspectKey = :__inspect_key__
+ # Returns a string containing a human-readable representation of the
+ # set. ("#<Set: {element1, element2, ...}>")
def inspect
ids = (Thread.current[InspectKey] ||= [])
@@ -500,7 +400,7 @@ class Set
end
end
- def pretty_print(pp)
+ def pretty_print(pp) # :nodoc:
pp.text sprintf('#<%s: {', self.class.name)
pp.nest(1) {
first = true
@@ -517,20 +417,21 @@ class Set
pp.text "}>"
end
- def pretty_print_cycle(pp)
+ 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.
class SortedSet < Set
@@setup = false
class << self
- def [](*ary)
+ def [](*ary) # :nodoc:
new(ary)
end
- def setup
+ def setup # :nodoc:
@@setup and return
begin
@@ -599,13 +500,14 @@ class SortedSet < Set
end
end
- def initialize(*args, &block)
+ def initialize(*args, &block) # :nodoc:
SortedSet.setup
initialize(*args, &block)
end
end
module Enumerable
+ # Makes a set from the enumerable object with given arguments.
def to_set(klass = Set, *args, &block)
klass.new(self, *args, &block)
end
diff --git a/lib/shell.rb b/lib/shell.rb
index acc4adec0b..039f849ef5 100644
--- a/lib/shell.rb
+++ b/lib/shell.rb
@@ -267,4 +267,3 @@ class Shell
CommandProcessor.initialize
CommandProcessor.run_config
end
-
diff --git a/lib/shell/command-processor.rb b/lib/shell/command-processor.rb
index 79310359e6..07879567fd 100644
--- a/lib/shell/command-processor.rb
+++ b/lib/shell/command-processor.rb
@@ -31,7 +31,7 @@ class Shell
install_builtin_commands
# define CommandProccessor#methods to Shell#methods and Filter#methods
- for m in CommandProcessor.instance_methods - NoDelegateMethods
+ for m in CommandProcessor.instance_methods(false) - NoDelegateMethods
add_delegate_command_to_shell(m)
end
@@ -277,7 +277,7 @@ class Shell
when IO
AppendIO.new(@shell, to, filter)
else
- Shell.Fail CanNotMethodApply, "append", to.class
+ Shell.Fail CantApplyMethod, "append", to.class
end
end
@@ -558,7 +558,7 @@ class Shell
# method related FileTest
def_builtin_commands(FileTest,
- FileTest.singleton_methods.collect{|m| [m, ["FILENAME"]]})
+ FileTest.singleton_methods(false).collect{|m| [m, ["FILENAME"]]})
# method related ftools
normal_delegation_ftools_methods = [
diff --git a/lib/shell/error.rb b/lib/shell/error.rb
index df5e669af6..d338b1c5d1 100644
--- a/lib/shell/error.rb
+++ b/lib/shell/error.rb
@@ -18,8 +18,8 @@ class Shell
def_e2message TypeError, "wrong argument type %s (expected %s)"
def_exception :DirStackEmpty, "Directory stack empty."
- def_exception :CanNotDefine, "Can't define method(%s, %s)."
- def_exception :CanNotMethodApply, "This method(%s) can't apply this type(%s)."
+ 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/lib/shell/filter.rb b/lib/shell/filter.rb
index 727826b7c9..4cf793a3b3 100644
--- a/lib/shell/filter.rb
+++ b/lib/shell/filter.rb
@@ -47,7 +47,7 @@ class Shell
self.input = src
self
else
- Filter.Fail CanNotMethodApply, "<", to.class
+ Filter.Fail CantApplyMethod, "<", to.class
end
end
@@ -63,7 +63,7 @@ class Shell
when IO
each(){|l| to << l}
else
- Filter.Fail CanNotMethodApply, ">", to.class
+ Filter.Fail CantApplyMethod, ">", to.class
end
self
end
@@ -71,8 +71,8 @@ class Shell
def >> (to)
begin
Shell.cd(@shell.pwd).append(to, self)
- rescue CanNotMethodApply
- Shell.Fail CanNotMethodApply, ">>", to.class
+ rescue CantApplyMethod
+ Shell.Fail CantApplyMethod, ">>", to.class
end
end
diff --git a/lib/shellwords.rb b/lib/shellwords.rb
index 9fd7571172..99c0bf8437 100644
--- a/lib/shellwords.rb
+++ b/lib/shellwords.rb
@@ -1,42 +1,52 @@
-# shellwords.rb
-# original is shellwords.pl
#
-# Usage:
-# require 'shellwords'
-# words = Shellwords.shellwords(line)
+# shellwords.rb: Split text into an array of tokens a la UNIX shell
#
-# or
-#
-# require 'shellwords'
-# include Shellwords
-# words = shellwords(line)
+#
+# 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)
- unless line.kind_of?(String)
- raise ArgumentError, "Argument must be String class object."
- end
- line = line.sub(/\A\s+/, '')
+ line = String.new(line) rescue
+ raise(ArgumentError, "Argument must be a string")
+ line.lstrip!
words = []
- while line != ''
+ until line.empty?
field = ''
- while true
- if line.sub!(/\A"(([^"\\]|\\.)*)"/, '') then #"
- snippet = $1
- snippet.gsub!(/\\(.)/, '\1')
- elsif line =~ /\A"/ then #"
+ 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 #'
+ elsif line.sub!(/\A'([^']*)'/, '') then
snippet = $1
- snippet.gsub!(/\\(.)/, '\1')
- elsif line =~ /\A'/ then #'
+ elsif line =~ /\A'/ then
raise ArgumentError, "Unmatched single quote: #{line}"
elsif line.sub!(/\A\\(.)/, '') then
snippet = $1
- elsif line.sub!(/\A([^\s\\'"]+)/, '') then #'
+ elsif line.sub!(/\A([^\s\\'"]+)/, '') then
snippet = $1
else
- line.sub!(/\A\s+/, '')
+ line.lstrip!
break
end
field.concat(snippet)
@@ -45,5 +55,6 @@ module Shellwords
end
words
end
+
module_function :shellwords
end
diff --git a/lib/singleton.rb b/lib/singleton.rb
index 3c20c13253..939159496b 100644
--- a/lib/singleton.rb
+++ b/lib/singleton.rb
@@ -142,7 +142,7 @@ module Singleton
@__instance__ = new
ensure
if @__instance__
- def self.instance() @__instance__ end
+ define_method(:instance) {@__instance__}
else
@__instance__ = nil # failed instance creation
end
@@ -156,7 +156,7 @@ module Singleton
@__instance__ = new
ensure
if @__instance__
- def self.instance() @__instance__ end
+ define_method(:instance) {@__instance__}
else
@__instance__ = nil
end
diff --git a/lib/telnet.rb b/lib/telnet.rb
deleted file mode 100644
index b861ffa783..0000000000
--- a/lib/telnet.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-# telnet.rb
-#
-
-$stderr.puts 'Warning: telnet.rb is obsolete: use net/telnet'
-
-require 'net/telnet'
-
-Telnet = ::Net::Telnet
diff --git a/lib/tempfile.rb b/lib/tempfile.rb
index 201a0f14f7..f5dc801b21 100644
--- a/lib/tempfile.rb
+++ b/lib/tempfile.rb
@@ -1,49 +1,27 @@
#
-# $Id$
+# tempfile - manipulates temporary files
#
-# This is a class for managing temporary files.
+# $Id$
#
-# o Tempfile::new("basename") creates a temporary file whose name is
-# "basename.pid.n" and opens with mode "w+".
-# o A Tempfile object can be treated as an IO object.
-# o The temporary directory is determined by ENV['TMPDIR'],
-# ENV['TMP'], and ENV['TEMP'] in the order named, and if none of
-# them is available, it is set to /tmp.
-# o When $SAFE > 0, you should specify a directory via the second argument
-# of Tempfile::new(), or it will end up finding an ENV value tainted and
-# pick /tmp. In case you don't have it, an exception will be raised.
-# o Tempfile#close(true) gets the temporary file removed immediately.
-# o Otherwise, the removal is delayed until the object is finalized.
-# o With Tempfile#open, you can reopen the temporary file.
-# o The file mode for the temporary files is 0600.
-# o This library is (considered to be) thread safe.
require 'delegate'
+require 'tmpdir'
+# A class for managing temporary files. This library is written to be
+# thread safe.
class Tempfile < SimpleDelegator
- Max_try = 10
+ MAX_TRY = 10
@@cleanlist = []
- def Tempfile.callback(data)
- 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
-
- def initialize(basename, tmpdir=ENV['TMPDIR']||ENV['TMP']||ENV['TEMP']||'/tmp')
+ # 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
@@ -64,7 +42,7 @@ class Tempfile < SimpleDelegator
Dir.mkdir(lock)
rescue
failure += 1
- retry if failure < Max_try
+ retry if failure < MAX_TRY
raise "cannot generate tempfile `%s'" % tmpname
ensure
Thread.critical = false
@@ -88,10 +66,7 @@ class Tempfile < SimpleDelegator
Dir.rmdir(lock)
end
- def Tempfile.open(*args)
- Tempfile.new(*args)
- end
-
+ # Opens or reopens the file with mode "r+".
def open
@tmpfile.close if @tmpfile
@tmpfile = File.open(@tmpname, 'r+')
@@ -99,19 +74,50 @@ class Tempfile < SimpleDelegator
__setobj__(@tmpfile)
end
- def close(real=false)
+ def _close # :nodoc:
@tmpfile.close if @tmpfile
@data[1] = @tmpfile = nil
- if real
- @clean_proc.call
- ObjectSpace.undefine_finalizer(self)
+ 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
+ File.unlink(@tmpname) if File.exist?(@tmpname)
+ @@cleanlist.delete(@tmpname) if @@cleanlist
+ 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
@@ -120,6 +126,49 @@ class Tempfile < SimpleDelegator
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
@@ -129,5 +178,5 @@ if __FILE__ == $0
f.close
f.open
p f.gets # => "foo\n"
- f.close(true)
+ f.close!
end
diff --git a/lib/test/unit.rb b/lib/test/unit.rb
new file mode 100644
index 0000000000..f4202c5294
--- /dev/null
+++ b/lib/test/unit.rb
@@ -0,0 +1,214 @@
+# :include: ../../../../README
+#
+# ----
+#
+# = 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).
+
+
+
+
+require 'test/unit/testcase'
+require 'test/unit/ui/testrunnermediator'
+
+at_exit {
+ # We can't debug tests run with at_exit unless we add the following:
+ set_trace_func DEBUGGER__.context.method(:trace_func).to_proc if (defined? DEBUGGER__)
+
+ if (!Test::Unit::UI::TestRunnerMediator.run?)
+ suite_name = $0.sub(/\.rb$/, '')
+ suite = Test::Unit::TestSuite.new(suite_name)
+ test_classes = []
+ ObjectSpace.each_object(Class) {
+ | klass |
+ test_classes << klass if (Test::Unit::TestCase > klass)
+ }
+
+ runners = {
+ '--console' => proc do |suite|
+ require 'test/unit/ui/console/testrunner'
+ Test::Unit::UI::Console::TestRunner.run(suite)
+ end,
+ '--gtk' => proc do |suite|
+ require 'test/unit/ui/gtk/testrunner'
+ Test::Unit::UI::GTK::TestRunner.run(suite)
+ end,
+ '--fox' => proc do |suite|
+ require 'test/unit/ui/fox/testrunner'
+ Test::Unit::UI::Fox::TestRunner.run(suite)
+ end,
+ }
+
+ unless (ARGV.empty?)
+ runner = runners[ARGV[0]]
+ ARGV.shift unless (runner.nil?)
+ end
+ runner = runners['--console'] if (runner.nil?)
+
+ if ARGV.empty?
+ test_classes.each { |klass| suite << klass.suite }
+ else
+ tests = test_classes.map { |klass| klass.suite.tests }.flatten
+ criteria = ARGV.map { |arg| (arg =~ %r{^/(.*)/$}) ? Regexp.new($1) : arg }
+ criteria.each {
+ | criterion |
+ if (criterion.instance_of?(Regexp))
+ tests.each { |test| suite << test if (criterion =~ test.name) }
+ elsif (/^A-Z/ =~ criterion)
+ tests.each { |test| suite << test if (criterion == test.class.name) }
+ else
+ tests.each { |test| suite << test if (criterion == test.method_name) }
+ end
+ }
+ end
+ runner.call(suite)
+ end
+}
diff --git a/lib/test/unit/assertionfailederror.rb b/lib/test/unit/assertionfailederror.rb
new file mode 100644
index 0000000000..9b9e53f16c
--- /dev/null
+++ b/lib/test/unit/assertionfailederror.rb
@@ -0,0 +1,14 @@
+# :nodoc:
+#
+# 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 < Exception
+ end
+ end
+end
diff --git a/lib/test/unit/assertions.rb b/lib/test/unit/assertions.rb
new file mode 100644
index 0000000000..0af3468b9b
--- /dev/null
+++ b/lib/test/unit/assertions.rb
@@ -0,0 +1,394 @@
+# :nodoc:
+#
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+require 'test/unit/assertionfailederror'
+
+module Test # :nodoc:
+ module Unit # :nodoc:
+
+ # Contains all of the standard Test::Unit assertions. Mixed in
+ # to Test::Unit::TestCase. To mix it in and use its
+ # functionality, you simply need to rescue
+ # Test::Unit::AssertionFailedError, and you can additionally
+ # override add_assertion to be notified whenever an assertion
+ # is made.
+ #
+ # Notes:
+ # * The message to each assertion, if given, will be
+ # propagated with the failure.
+ # * It's easy to add your own assertions based on assert_block().
+ module Assertions
+
+ # The assertion upon which all other assertions are
+ # based. Passes if the block yields true.
+ public
+ def assert_block(message="") # :yields:
+ _wrap_assertion do
+ if (! yield)
+ raise AssertionFailedError.new(message.to_s)
+ end
+ end
+ end
+
+ # Passes if boolean is true.
+ public
+ def assert(boolean, message="")
+ _wrap_assertion do
+ assert_block("assert should not be called with a block.") { !block_given? }
+ assert_block(message) { 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.
+ public
+ def assert_equal(expected, actual, message=nil)
+ full_message = build_message(message, expected, actual) do |arg1, arg2|
+ "<#{arg1}> expected but was\n" +
+ "<#{arg2}>"
+ end
+ assert_block(full_message) { expected == actual }
+ end
+
+ # Passes if block raises exception.
+ public
+ def assert_raises(expected_exception_klass, message="")
+ _wrap_assertion do
+ assert_instance_of(Class, expected_exception_klass, "Should expect a class of exception")
+ actual_exception = nil
+ full_message = build_message(message, expected_exception_klass) do |arg|
+ "<#{arg}> exception expected but none was thrown"
+ end
+ assert_block(full_message) do
+ thrown = false
+ begin
+ yield
+ rescue Exception => thrown_exception
+ actual_exception = thrown_exception
+ thrown = true
+ end
+ thrown
+ end
+ full_message = build_message(message, expected_exception_klass, actual_exception) do |arg1, arg2|
+ "<#{arg1}> exception expected but was\n" +
+ arg2
+ end
+ assert_block(full_message) { expected_exception_klass == actual_exception.class }
+ actual_exception
+ end
+ end
+
+ # Passes if object.class == klass.
+ 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, object, klass, object.class) do |arg1, arg2, arg3|
+ "<#{arg1}> expected to be an instance of\n" +
+ "<#{arg2}> but was\n" +
+ "<#{arg3}>"
+ end
+ assert_block(full_message) { klass == object.class }
+ end
+ end
+
+ # Passes if object.nil?.
+ public
+ def assert_nil(object, message="")
+ assert_equal(nil, object, message)
+ end
+
+ # Passes if object.kind_of?(klass).
+ 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, object, klass) do |arg1, arg2|
+ "<#{arg1}>\n" +
+ "expected to be kind_of?<#{arg2}>"
+ end
+ assert_block(full_message) { object.kind_of?(klass) }
+ end
+ end
+
+ # Passes if object.respond_to?(method) is true.
+ public
+ def assert_respond_to(object, method, message="")
+ _wrap_assertion do
+ assert(method.kind_of?(Symbol) || method.kind_of?(String), "The method argument to #assert_respond_to should be specified as a Symbol or a String.")
+ full_message = build_message(message, object, object.class, method) do |arg1, arg2, arg3|
+ "<#{arg1}>\n" +
+ "of type <#{arg2}>\n" +
+ "expected to respond_to?<#{arg3}>"
+ end
+ assert_block(full_message) { object.respond_to?(method) }
+ end
+ end
+
+ # Passes if string =~ pattern.
+ public
+ def assert_match(pattern, string, message="")
+ _wrap_assertion do
+ full_message = build_message(message, string, pattern) do |arg1, arg2|
+ "<#{arg1}> expected to be =~\n" +
+ "<#{arg2}>"
+ end
+ assert_block(full_message) { string =~ pattern }
+ end
+ end
+
+ # Passes if actual.equal?(expected) (i.e. they are the
+ # same instance).
+ public
+ def assert_same(expected, actual, message="")
+ full_message = build_message(message, expected, expected.__id__, actual, actual.__id__) do |arg1, arg2, arg3, arg4|
+ "<#{arg1}:#{arg2}> expected to be equal? to\n" +
+ "<#{arg3}:#{arg4}>"
+ end
+ assert_block(full_message) { actual.equal?(expected) }
+ end
+
+ # Compares the two objects based on the passed
+ # operator. Passes if object1.send(operator, object2) is
+ # true.
+ public
+ def assert_operator(object1, operator, object2, message="")
+ full_message = build_message(message, object1, operator, object2) do |arg1, arg2, arg3|
+ "<#{arg1}> expected to be\n" +
+ "#{arg2}\n" +
+ "<#{arg3}>"
+ end
+ assert_block(full_message) { object1.send(operator, object2) }
+ end
+
+ # Passes if block does not raise an exception.
+ public
+ def assert_nothing_raised(*args)
+ _wrap_assertion do
+ message = ""
+ if (!args[-1].instance_of?(Class))
+ message = args.pop
+ end
+ begin
+ yield
+ rescue Exception => thrown_exception
+ if (args.empty? || args.include?(thrown_exception.class))
+ full_message = build_message(message, thrown_exception) do |arg1|
+ "Exception raised:\n" +
+ arg1
+ end
+ flunk(full_message)
+ else
+ raise thrown_exception.class, thrown_exception.message, thrown_exception.backtrace
+ end
+ end
+ nil
+ end
+ end
+
+ # Always fails.
+ public
+ def flunk(message="")
+ assert(false, message)
+ end
+
+ # Passes if !actual.equal?(expected).
+ public
+ def assert_not_same(expected, actual, message="")
+ full_message = build_message(message, expected, expected.__id__, actual, actual.__id__) do |arg1, arg2, arg3, arg4|
+ "<#{arg1}:#{arg2}> expected to not be equal? to\n" +
+ "<#{arg3}:#{arg4}>"
+ end
+ assert_block(full_message) { !actual.equal?(expected) }
+ end
+
+ # Passes if expected != actual.
+ public
+ def assert_not_equal(expected, actual, message="")
+ full_message = build_message(message, expected, actual) do |arg1, arg2|
+ "<#{arg1}> expected to be != to\n" +
+ "<#{arg2}>"
+ end
+ assert_block(full_message) { expected != actual }
+ end
+
+ # Passes if !object.nil?.
+ public
+ def assert_not_nil(object, message="")
+ full_message = build_message(message, object) do |arg|
+ "<#{arg}> expected to not be nil"
+ end
+ assert_block(full_message) { !object.nil? }
+ end
+
+ # Passes if string !~ regularExpression.
+ public
+ def assert_no_match(regexp, string, message="")
+ _wrap_assertion do
+ assert_instance_of(Regexp, regexp, "The first argument to assert_does_not_match should be a Regexp.")
+ full_message = build_message(message, regexp.source, string) do |arg1, arg2|
+ "</#{arg1}/> expected to not match\n" +
+ " <#{arg2}>"
+ end
+ assert_block(full_message) { regexp !~ string }
+ end
+ end
+
+ # Passes if block throws symbol.
+ 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_given?, "Should have passed a block to assert_throws")
+ caught = true
+ begin
+ catch(expected_symbol) do
+ proc.call
+ caught = false
+ end
+ full_message = build_message(message, expected_symbol) do |arg|
+ "<:#{arg}> should have been thrown"
+ end
+ assert(caught, full_message)
+ rescue NameError => name_error
+ if ( name_error.message !~ /^uncaught throw `(.+)'$/ ) #`
+ raise name_error
+ end
+ full_message = build_message(message, expected_symbol, $1) do |arg1, arg2|
+ "<:#{arg1}> expected to be thrown but\n" +
+ "<:#{arg2}> was thrown"
+ end
+ flunk(full_message)
+ end
+ end
+ end
+
+ # Passes if block does not throw anything.
+ 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 => name_error
+ if (name_error.message !~ /^uncaught throw `(.+)'$/ ) #`
+ raise name_error
+ end
+ full_message = build_message(message, $1) do |arg|
+ "<:#{arg}> was thrown when nothing was expected"
+ end
+ flunk(full_message)
+ end
+ full_message = build_message(message) { || "Expected nothing to be thrown" }
+ assert(true, full_message)
+ end
+ end
+
+ # Passes if expected_float and actual_float are equal
+ # within delta tolerance.
+ 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, expected_float, actual_float, delta) do |arg1, arg2, arg3|
+ "<#{arg1}> and\n" +
+ "<#{arg2}> expected to be within\n" +
+ "<#{arg3}> of each other"
+ end
+ assert_block(full_message) { (expected_float.to_f - actual_float.to_f).abs <= delta.to_f }
+ end
+ end
+
+ # Passes if the method sent returns a true value.
+ 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, send_array[0], send_array[1], send_array[2..-1]) do |arg1, arg2, arg3|
+ "<#{arg1}> expected to respond to\n" +
+ "<#{arg2}(#{arg3})> with true"
+ end
+ assert_block(full_message) { send_array[0].__send__(send_array[1], *send_array[2..-1]) }
+ end
+ end
+
+ public
+ def build_message(message, *arguments, &block) # :nodoc:
+ return AssertionMessage.new(message.to_s, arguments, block)
+ end
+
+ private
+ def _wrap_assertion # :nodoc:
+ @_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.
+ private
+ def add_assertion
+ end
+
+ class AssertionMessage # :nodoc: all
+ def self.convert(object)
+ case object
+ when String
+ return object
+ when Symbol
+ return object.to_s
+ when Regexp
+ return "/#{object.source}/"
+ when Exception
+ return "Class: <#{object.class}>\n" +
+ "Message: <#{object.message}>\n" +
+ "---Backtrace---\n" +
+ object.backtrace.join("\n") + "\n" +
+ "---------------"
+ else
+ return object.inspect
+ end
+ end
+
+ def initialize(message, parameters, block)
+ @message = message
+ @parameters = parameters
+ @block = block
+ end
+
+ def to_s
+ message_parts = []
+ if (@message != nil && @message != "")
+ if (@message !~ /\.$/)
+ @message << "."
+ end
+ message_parts << @message
+ end
+ @parameters = @parameters.collect {
+ | parameter |
+ self.class.convert(parameter)
+ }
+ message_parts << @block.call(*@parameters)
+ return message_parts.join("\n")
+ end
+ end
+ end
+ end
+end
diff --git a/lib/test/unit/error.rb b/lib/test/unit/error.rb
new file mode 100644
index 0000000000..46c6663d86
--- /dev/null
+++ b/lib/test/unit/error.rb
@@ -0,0 +1,68 @@
+# :nodoc:
+#
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+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
+ attr_reader(:location, :exception)
+
+ SINGLE_CHARACTER = 'E'
+
+ # Creates a new Error with the given location and
+ # exception.
+ def initialize(location, exception)
+ @location = location
+ @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
+ "#{@location}:\n#{message}"
+ end
+
+ # Returns a verbose version of the error description.
+ def long_display
+ backtrace = self.class.filter(@exception.backtrace).join("\n ")
+ "Error!!!\n#{short_display}\n #{backtrace}"
+ end
+
+ # Overridden to return long_display.
+ def to_s
+ long_display
+ end
+
+ SEPARATOR_PATTERN = '[\\\/:]'
+ def self.filter(backtrace) # :nodoc:
+ @test_unit_patterns ||= $:.collect {
+ | path |
+ /^#{Regexp.escape(path)}#{SEPARATOR_PATTERN}test#{SEPARATOR_PATTERN}unit#{SEPARATOR_PATTERN}/
+ }.push(/#{SEPARATOR_PATTERN}test#{SEPARATOR_PATTERN}unit\.rb/)
+
+ return backtrace.delete_if {
+ | line |
+ @test_unit_patterns.detect {
+ | pattern |
+ line =~ pattern
+ }
+ }
+ end
+ end
+ end
+end
diff --git a/lib/test/unit/failure.rb b/lib/test/unit/failure.rb
new file mode 100644
index 0000000000..6817d8f3bf
--- /dev/null
+++ b/lib/test/unit/failure.rb
@@ -0,0 +1,45 @@
+# :nodoc:
+#
+# 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(:location, :message)
+
+ SINGLE_CHARACTER = 'F'
+
+ # Creates a new Failure with the given location and
+ # message.
+ def initialize(location, message)
+ @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
+ "#{@location}:\n#{@message}"
+ end
+
+ # Returns a verbose version of the error description.
+ def long_display
+ "Failure!!!\n#{short_display}"
+ end
+
+ # Overridden to return long_display.
+ def to_s
+ long_display
+ end
+ end
+ end
+end
diff --git a/lib/test/unit/testcase.rb b/lib/test/unit/testcase.rb
new file mode 100644
index 0000000000..2ccf192906
--- /dev/null
+++ b/lib/test/unit/testcase.rb
@@ -0,0 +1,152 @@
+# :nodoc:
+#
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 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'
+
+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
+
+ 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)
+ if ((!respond_to?(test_method_name)) || (method(test_method_name).arity != 0))
+ 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.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 # :nodoc:
+ 1
+ end
+
+ def add_assertion # :nodoc:
+ @_result.add_assertion
+ end
+ private :add_assertion
+
+ def add_failure(message, all_locations=caller()) # :nodoc:
+ @test_passed = false
+ assertions_pattern = /[^A-Za-z_]assertions\.rb:/
+ if (all_locations.detect { |entry| entry =~ assertions_pattern })
+ all_locations.shift
+ until (all_locations[0] =~ assertions_pattern || all_locations.empty?)
+ all_locations.shift
+ end
+ location = all_locations.detect { |entry| entry !~ assertions_pattern }
+ else
+ location = all_locations[0]
+ end
+ location = location[/^.+:\d+/]
+ @_result.add_failure(Failure.new("#{name} [#{location}]", message))
+ end
+ private :add_failure
+
+ def add_error(exception) # :nodoc:
+ @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
+
+ # Overriden to return #name.
+ def to_s
+ name
+ end
+ end
+ end
+end
diff --git a/lib/test/unit/testresult.rb b/lib/test/unit/testresult.rb
new file mode 100644
index 0000000000..b1bfbf72e4
--- /dev/null
+++ b/lib/test/unit/testresult.rb
@@ -0,0 +1,81 @@
+# :nodoc:
+#
+# 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/lib/test/unit/testsuite.rb b/lib/test/unit/testsuite.rb
new file mode 100644
index 0000000000..e3a164cb6e
--- /dev/null
+++ b/lib/test/unit/testsuite.rb
@@ -0,0 +1,64 @@
+# :nodoc:
+#
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 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.sort { |test1, test2| test1.name <=> test2.name }.each do |test|
+ test.run(result, &progress_block)
+ end
+ yield(FINISHED, name)
+ end
+
+ # Adds the test to the suite.
+ def <<(test)
+ @tests << 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
+
+ # Overriden to return the name given the suite at
+ # creation.
+ def to_s
+ @name
+ end
+ end
+ end
+end
diff --git a/lib/test/unit/ui/console/testrunner.rb b/lib/test/unit/ui/console/testrunner.rb
new file mode 100644
index 0000000000..0a5e264006
--- /dev/null
+++ b/lib/test/unit/ui/console/testrunner.rb
@@ -0,0 +1,135 @@
+# :nodoc:
+#
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 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 # :nodoc:
+
+ # Runs a Test::Unit::TestSuite on the console.
+ class TestRunner
+ extend TestRunnerUtilities
+
+ SILENT = 0
+ PROGRESS_ONLY = 1
+ NORMAL = 2
+ VERBOSE = 3
+
+ # Creates a new TestRunner and runs the suite.
+ def self.run(suite, output_level=NORMAL)
+ return new(suite, output_level).start
+ end
+
+ # 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
+ # STDERR.
+ 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 # :nodoc:
+ @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) # :nodoc:
+ return TestRunnerMediator.new(suite)
+ end
+
+ def attach_to_mediator # :nodoc:
+ @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 # :nodoc:
+ return @mediator.run_suite
+ end
+
+ def add_fault(fault) # :nodoc:
+ @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))
+ end
+
+ def output_single(something, level=NORMAL)
+ @io.write(something) if (output?(level))
+ 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/lib/test/unit/ui/fox/testrunner.rb b/lib/test/unit/ui/fox/testrunner.rb
new file mode 100644
index 0000000000..8b82ec634d
--- /dev/null
+++ b/lib/test/unit/ui/fox/testrunner.rb
@@ -0,0 +1,270 @@
+# :nodoc:
+#
+# 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 # :nodoc:
+
+ # 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 and runs the suite.
+ def self.run(suite)
+ new(suite).start
+ end
+
+ # Creates a new TestRunner for running the passed
+ # suite.
+ def initialize(suite)
+ if (suite.respond_to?(:suite))
+ @suite = suite.suite
+ else
+ @suite = suite
+ end
+
+ @red = false
+ end
+
+ # Begins the test run.
+ def start
+ setup_ui
+ setup_mediator
+ attach_to_mediator
+ start_ui
+ end
+
+ def setup_mediator # :nodoc:
+ @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 # :nodoc:
+ @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 # :nodoc:
+ @application.create
+ @window.show(PLACEMENT_SCREEN)
+ @application.addTimeout(1) do
+ @mediator.run_suite
+ end
+ @application.run
+ end
+
+ def stop # :nodoc:
+ @application.exit(0)
+ end
+
+ def reset_ui(count) # :nodoc:
+ @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) # :nodoc:
+ if ( ! @red )
+ @test_progress_bar.barColor = RED_STYLE
+ @red = true
+ end
+ item = FaultListItem.new(fault)
+ @fault_list.appendItem(item)
+ end
+
+ def show_fault(fault) # :nodoc:
+ raw_show_fault(fault.long_display)
+ end
+
+ def raw_show_fault(string) # :nodoc:
+ @detail_text.setText(string)
+ end
+
+ def clear_fault # :nodoc:
+ raw_show_fault("")
+ end
+
+ def result_changed(result) # :nodoc:
+ @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) # :nodoc:
+ 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 # :nodoc:
+ @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 # :nodoc:
+ 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) # :nodoc:
+ panel = FXVerticalFrame.new(parent, LAYOUT_FILL_X | LAYOUT_FILL_Y)
+ panel.vSpacing = 10
+ panel
+ end
+
+ def create_suite_panel(parent) # :nodoc:
+ FXHorizontalFrame.new(parent, LAYOUT_SIDE_LEFT | LAYOUT_FILL_X)
+ end
+
+ def create_button(parent, text, action) # :nodoc:
+ FXButton.new(parent, text).connect(SEL_COMMAND, &action)
+ end
+
+ def create_progress_bar(parent) # :nodoc:
+ FXProgressBar.new(parent, nil, 0, PROGRESSBAR_NORMAL | LAYOUT_FILL_X)
+ end
+
+ def create_info_panel(parent) # :nodoc:
+ 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) # :nodoc:
+ FXHorizontalFrame.new(parent, LAYOUT_FILL_X | FRAME_SUNKEN | FRAME_THICK)
+ end
+
+ def create_fault_list(parent) # :nodoc:
+ 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) # :nodoc:
+ FXHorizontalFrame.new(parent, LAYOUT_FILL_X | LAYOUT_FILL_Y | FRAME_SUNKEN | FRAME_THICK)
+ end
+
+ def create_text(parent) # :nodoc:
+ FXText.new(parent, nil, 0, TEXT_READONLY | LAYOUT_FILL_X | LAYOUT_FILL_Y)
+ end
+
+ def create_entry(parent) # :nodoc:
+ entry = FXTextField.new(parent, 30, nil, 0, TEXTFIELD_NORMAL | LAYOUT_SIDE_BOTTOM | LAYOUT_FILL_X)
+ entry.disable
+ entry
+ end
+ end
+
+ class FaultListItem < FXListItem # :nodoc: all
+ 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/lib/test/unit/ui/gtk/testrunner.rb b/lib/test/unit/ui/gtk/testrunner.rb
new file mode 100644
index 0000000000..229df3e358
--- /dev/null
+++ b/lib/test/unit/ui/gtk/testrunner.rb
@@ -0,0 +1,389 @@
+# :nodoc:
+#
+# 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 # :nodoc:
+
+ # 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 and runs the suite.
+ def self.run(suite)
+ new(suite).start
+
+ end
+
+ # Creates a new TestRunner for running the passed
+ # suite.
+ def initialize(suite)
+ if (suite.respond_to?(:suite))
+ @suite = suite.suite
+ else
+ @suite = suite
+ end
+ end
+
+ # Begins the test run.
+ def start
+ setup_mediator
+ setup_ui
+ attach_to_mediator
+ start_ui
+ end
+
+ private
+ def setup_mediator # :nodoc:
+ @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 # :nodoc:
+ run_button.signal_connect("clicked", nil) { @mediator.run_suite }
+ @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 # :nodoc:
+ timer = Gtk::timeout_add(0) {
+ Gtk::timeout_remove(timer)
+ @mediator.run_suite
+ }
+ Gtk.main
+ end
+
+ def stop # :nodoc:
+ Gtk.main_quit
+ end
+
+ def reset_ui(count) # :nodoc:
+ 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) # :nodoc:
+ 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) # :nodoc:
+ raw_show_fault(fault.longDisplay)
+ end
+
+ def raw_show_fault(string) # :nodoc:
+ faultDetailLabel.set_text(string)
+ outerDetailSubPanel.queue_resize
+ end
+
+ def clear_fault # :nodoc:
+ raw_show_fault("")
+ end
+
+ def result_changed(result) # :nodoc:
+ test_progress_bar.set_value(test_progress_bar.get_value + 1)
+
+ 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) # :nodoc:
+ 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) # :nodoc:
+ status_entry.set_text(string)
+ end
+
+ def setup_ui # :nodoc:
+ main_window.signal_connect("destroy", nil) { 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 # :nodoc:
+ 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 # :nodoc:
+ 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 # :nodoc:
+ 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 # :nodoc:
+ lazy_initialize(:suite_name_entry) {
+ @suite_name_entry = Gtk::Entry.new
+ @suite_name_entry.set_editable(false)
+ }
+ end
+
+ def run_button # :nodoc:
+ lazy_initialize(:run_button) {
+ @run_button = Gtk::Button.new("Run")
+ }
+ end
+
+ def progress_panel # :nodoc:
+ 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 # :nodoc:
+ lazy_initialize(:test_progress_bar) {
+ @test_progress_bar = EnhancedProgressBar.new
+ @test_progress_bar.set_usize(@test_progress_bar.allocation.width, 50)
+ @test_progress_bar.set_style(green_style)
+ }
+ end
+
+ def green_style # :nodoc:
+ lazy_initialize(:green_style) {
+ @green_style = Gtk::Style.new
+ @green_style.set_bg(Gtk::STATE_PRELIGHT, 0x0000, 0xFFFF, 0x0000)
+ }
+ end
+
+ def red_style # :nodoc:
+ lazy_initialize(:red_style) {
+ @red_style = Gtk::Style.new
+ @red_style.set_bg(Gtk::STATE_PRELIGHT, 0xFFFF, 0x0000, 0x0000)
+ }
+ end
+
+ def info_panel # :nodoc:
+ 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 # :nodoc:
+ 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 # :nodoc:
+ 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 # :nodoc:
+ 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 # :nodoc:
+ lazy_initialize(:error_count_label) {
+ @error_count_label = Gtk::Label.new("0")
+ @error_count_label.set_justify(Gtk::JUSTIFY_LEFT)
+ }
+ end
+
+ def list_panel # :nodoc:
+ 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 # :nodoc:
+ 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 # :nodoc:
+ lazy_initialize(:fault_list) {
+ @fault_list = Gtk::List.new
+ }
+ end
+
+ def detail_panel # :nodoc:
+ 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 # :nodoc:
+ 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 # :nodoc:
+ 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 # :nodoc:
+ 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 # :nodoc:
+ 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-*-*-*-*-*-*")
+ style.set_font(font)
+ @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 # :nodoc:
+ 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 # :nodoc:
+ lazy_initialize(:status_entry) {
+ @status_entry = Gtk::Entry.new
+ @status_entry.set_editable(false)
+ }
+ end
+
+ def lazy_initialize(symbol) # :nodoc:
+ if (!instance_eval("defined?(@#{symbol.to_s})"))
+ yield
+ end
+ return instance_eval("@" + symbol.to_s)
+ end
+ end
+
+ class EnhancedProgressBar < Gtk::ProgressBar # :nodoc: all
+ def set_style(style)
+ super
+ hide
+ show
+ end
+ end
+
+ class EnhancedLabel < Gtk::Label # :nodoc: all
+ def set_text(text)
+ super(text.gsub(/\n\t/, "\n" + (" " * 4)))
+ end
+ end
+
+ class FaultListItem < Gtk::ListItem # :nodoc: all
+ 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/lib/test/unit/ui/testrunnermediator.rb b/lib/test/unit/ui/testrunnermediator.rb
new file mode 100644
index 0000000000..41c77dc7a9
--- /dev/null
+++ b/lib/test/unit/ui/testrunnermediator.rb
@@ -0,0 +1,75 @@
+# :nodoc:
+#
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+require 'test/unit/util/observable'
+require 'test/unit/testresult'
+
+module Test
+ module Unit
+ module UI # :nodoc:
+
+ # 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
+
+ @@run = false
+
+ # Returns true if any TestRunnerMediator instances
+ # have been run.
+ def self.run?
+ return @@run
+ end
+
+ # 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
+ @@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/lib/test/unit/ui/testrunnerutilities.rb b/lib/test/unit/ui/testrunnerutilities.rb
new file mode 100644
index 0000000000..69811608c2
--- /dev/null
+++ b/lib/test/unit/ui/testrunnerutilities.rb
@@ -0,0 +1,36 @@
+# :nodoc:
+#
+# Author:: Nathaniel Talbott.
+# Copyright:: Copyright (c) 2000-2002 Nathaniel Talbott. All rights reserved.
+# License:: Ruby license.
+
+module Test
+ module Unit
+ module UI
+
+ # 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
+
+ # 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/lib/test/unit/util/observable.rb b/lib/test/unit/util/observable.rb
new file mode 100644
index 0000000000..f5066d3425
--- /dev/null
+++ b/lib/test/unit/util/observable.rb
@@ -0,0 +1,90 @@
+# :nodoc:
+#
+# 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 # :nodoc:
+
+ # 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 # :nodoc:
+ @channels ||= {}
+ return @channels
+ end
+ end
+ end
+ end
+end
diff --git a/lib/test/unit/util/procwrapper.rb b/lib/test/unit/util/procwrapper.rb
new file mode 100644
index 0000000000..fea66d7671
--- /dev/null
+++ b/lib/test/unit/util/procwrapper.rb
@@ -0,0 +1,48 @@
+# :nodoc:
+#
+# 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 # :nodoc:
+ return @hash
+ end
+
+ def ==(other) # :nodoc:
+ case(other)
+ when ProcWrapper
+ return @a_proc == other.to_proc
+ else
+ return super
+ end
+ end
+ alias :eql? :==
+
+ def to_proc # :nodoc:
+ return @a_proc
+ end
+ end
+ end
+ end
+end
diff --git a/lib/thread.rb b/lib/thread.rb
index 1a08aa7761..2769c54c3e 100644
--- a/lib/thread.rb
+++ b/lib/thread.rb
@@ -21,6 +21,9 @@ if $DEBUG
Thread.abort_on_exception = true
end
+#
+# FIXME: not documented in Pickaxe or Nutshell.
+#
def Thread.exclusive
_old = Thread.critical
begin
@@ -31,6 +34,27 @@ def Thread.exclusive
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
def initialize
@waiting = []
@@ -39,10 +63,17 @@ class Mutex
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
@@ -54,6 +85,9 @@ class Mutex
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
@@ -64,6 +98,9 @@ class Mutex
self
end
+ #
+ # Releases the lock. Returns +nil+ if ref wasn't locked.
+ #
def unlock
return unless @locked
Thread.critical = true
@@ -82,6 +119,10 @@ class Mutex
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
@@ -91,6 +132,9 @@ class Mutex
end
end
+ #
+ # FIXME: not documented in Pickaxe/Nutshell.
+ #
def exclusive_unlock
return unless @locked
Thread.exclusive do
@@ -107,11 +151,41 @@ class Mutex
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 (see the discussion on page 117).
+#
+# 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
def initialize
@waiters = []
end
+ #
+ # Releases the lock held in +mutex+ and waits; reacquires the lock on wakeup.
+ #
def wait(mutex)
mutex.exclusive_unlock do
@waiters.push(Thread.current)
@@ -120,6 +194,9 @@ class ConditionVariable
mutex.lock
end
+ #
+ # Wakes up the first thread in line waiting for this lock.
+ #
def signal
begin
t = @waiters.shift
@@ -129,6 +206,9 @@ class ConditionVariable
end
end
+ #
+ # Wakes up all threads waiting for this lock.
+ #
def broadcast
waiters0 = nil
Thread.exclusive do
@@ -144,7 +224,16 @@ class ConditionVariable
end
end
+#
+# This class provides a way to communicate data between threads.
+#
+# TODO: an example (code or English) would really help here. How do you set up
+# a queue between two threads?
+#
class Queue
+ #
+ # Creates a new queue.
+ #
def initialize
@que = []
@waiting = []
@@ -153,6 +242,9 @@ class Queue
self.taint
end
+ #
+ # Pushes +obj+ to the queue.
+ #
def push(obj)
Thread.critical = true
@que.push obj
@@ -172,6 +264,11 @@ class Queue
alias << 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
@@ -185,27 +282,50 @@ class Queue
alias shift 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.
+ #
def size
length
end
+ #
+ # 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.
+#
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
@@ -214,10 +334,16 @@ class SizedQueue<Queue
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
@@ -277,3 +403,13 @@ class SizedQueue<Queue
@waiting.size + @queue_wait.size
end
end
+
+# Documentation comments:
+# - SizedQueue #push and #pop deserve some documentation, as they are different
+# from the Queue implementations.
+# - Some methods are not documented in Pickaxe/Nutshell, and are therefore not
+# documented here. See FIXME notes.
+# - Reference to Pickaxe page numbers should be replaced with either a section
+# name or a summary.
+# - How do you document aliases?
+# - How do you make RDoc inherit documentation from superclass?
diff --git a/lib/thwait.rb b/lib/thwait.rb
index 00c8a8dd36..4512209604 100644
--- a/lib/thwait.rb
+++ b/lib/thwait.rb
@@ -39,6 +39,18 @@
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 $-'
@@ -46,7 +58,11 @@ class ThreadsWait
def_exception("ErrNoWaitingThread", "No threads for waiting.")
def_exception("ErrNoFinishedThread", "No finished threads.")
- def ThreadsWait.all_waits(*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|
@@ -57,43 +73,45 @@ class ThreadsWait
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
- # accessing
- # threads - list threads to be synchronized
+ # Returns the array of threads in the wait queue.
attr :threads
- # testing
- # empty?
- # finished?
-
- # is there any thread to be synchronized.
+ #
+ # Returns +true+ if there are no threads to be synchronized.
+ #
def empty?
@threads.empty?
end
- # is there already terminated thread.
+ #
+ # Returns +true+ if any thread has terminated.
+ #
def finished?
!@wait_queue.empty?
end
- # main process:
- # join
- # join_nowait
- # next_wait
- # all_wait
-
- # adds thread(s) to join, waits for any of waiting threads to terminate.
+ #
+ # Waits for specified threads to terminate.
+ #
def join(*threads)
join_nowait(*threads)
next_wait
end
- # adds thread(s) to join, no wait.
+ #
+ # Specifies the threads that this object will wait for, but does not actually
+ # wait.
+ #
def join_nowait(*threads)
threads.flatten!
@threads.concat threads
@@ -105,10 +123,13 @@ class ThreadsWait
end
end
- # waits for any of waiting threads to terminate
- # if there is no thread to wait, raises ErrNoWaitingThread.
- # if `nonblock' is true, and there is no terminated thread,
- # raises ErrNoFinishedThread.
+ #
+ # 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
@@ -119,9 +140,12 @@ class ThreadsWait
end
end
- # waits until all of specified threads are terminated.
- # if a block is supplied for the method, evaluates it for
- # each thread termination.
+ #
+ # 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
@@ -131,3 +155,12 @@ class ThreadsWait
end
ThWait = ThreadsWait
+
+
+# Documentation comments:
+# - Source of doumentation 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/lib/time.rb b/lib/time.rb
index bc1147104b..043257a4d1 100644
--- a/lib/time.rb
+++ b/lib/time.rb
@@ -1,44 +1,51 @@
+
+#
+# == 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 ParseDate (string to time only)
+#
+# == Design Issues
+#
+# === Specialized interface
+#
+# This library provides methods dedicated to special puposes:
+# * 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'
-=begin
-= time
-
-This library extends 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 format handled by ParseDate (string to time only)
-
-== Design Issue
-
-* specialized interface
-
- This library provides methods dedicated to special puposes:
- RFC 2822, RFC 2616 and XML Schema.
- They makes usual life easier.
-
-* doesn't depend on strftime
-
- This library doesn't use strftime.
- Especially Time#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.
-=end
-
+#
+# Implements the extensions to the Time class that are described in the
+# documentation for the time.rb library.
+#
class Time
class << Time
@@ -62,9 +69,9 @@ class Time
def zone_offset(zone, year=Time.now.year)
off = nil
zone = zone.upcase
- if /\A([-+])(\d\d):?(\d\d)\z/ =~ zone
+ 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
+ elsif /\A[+-]\d\d\z/ =~ zone
off = zone.to_i * 3600
elsif ZoneOffset.include?(zone)
off = ZoneOffset[zone] * 3600
@@ -76,63 +83,56 @@ class Time
off
end
-=begin
-== class methods
-
---- Time.parse(date, now=Time.now)
---- Time.parse(date, now=Time.now) {|year| year}
- parses ((|date|)) using ParseDate.parsedate 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("2002/1") #=> Tue Jan 01 00:00:00 GMT 2002
-
- 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
- (({Time.local(year, 1, 1).zone})) and
- (({Time.local(year, 7, 1).zone})).
- 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 ParseDate 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.
-=end
+ #
+ # Parses +date+ using ParseDate.parsedate 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("2002/1") #=> Tue Jan 01 00:00:00 GMT 2002
+ #
+ # 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 ParseDate 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)
year, mon, day, hour, min, sec, zone, _ = ParseDate.parsedate(date)
year = yield year if year && block_given?
@@ -172,17 +172,16 @@ class Time
'JUL' => 7, 'AUG' => 8, 'SEP' => 9, 'OCT' =>10, 'NOV' =>11, 'DEC' =>12
}
-=begin
---- Time.rfc2822(date)
---- Time.rfc822(date)
- 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.
-=end
+ #
+ # 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*)?
@@ -192,7 +191,7 @@ class Time
(\d{2})\s*
:\s*(\d{2})\s*
(?::\s*(\d{2}))?\s+
- ([+\-]\d{4}|
+ ([+-]\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
@@ -222,14 +221,15 @@ class Time
end
alias rfc822 rfc2822
-=begin
---- Time.httpdate(date)
- 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.
-=end
+ #
+ # 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
@@ -261,23 +261,23 @@ class Time
end
end
-=begin
---- Time.xmlschema(date)
---- Time.iso8601(date)
- 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.
-=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)?
+ (Z|[+-]\d\d:\d\d)?
\s*\z/ix =~ date
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
@@ -291,31 +291,17 @@ class Time
end
end
alias iso8601 xmlschema
- end
-
-=begin
-== methods
-=end
-
-=begin
---- Time#rfc2822
---- Time#rfc822
- 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.
-=end
-
- 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'
- ]
+ 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],
@@ -331,15 +317,22 @@ class Time
end
alias rfc822 rfc2822
-=begin
---- Time#httpdate
- 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
+ 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'
+ ]
- Note that the result is always UTC (GMT).
-=end
+ #
+ # 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',
@@ -348,24 +341,20 @@ class Time
t.hour, t.min, t.sec)
end
-=begin
---- Time#xmlschema([fractional_seconds])
---- Time#iso8601([fractional_seconds])
- 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|)) specify a number of digits of
- fractional seconds.
- The default value of ((|fractional_seconds|)) is 0.
-=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 os 0.
+ #
def xmlschema(fraction_digits=0)
sprintf('%d-%02d-%02dT%02d:%02d:%02d',
year, mon, day, hour, min, sec) +
@@ -388,10 +377,9 @@ class Time
end
if __FILE__ == $0
- require 'runit/testcase'
- require 'runit/cui/testrunner'
+ require 'test/unit'
- class TimeExtentionTest < RUNIT::TestCase
+ 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"))
@@ -423,7 +411,7 @@ if __FILE__ == $0
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_exception(ArgumentError) {
+ assert_raises(ArgumentError) {
# inner comment is not supported.
Time.rfc2822("Fri, 21 Nov 1997 09(comment): 55 : 06 -0600")
}
@@ -548,67 +536,66 @@ if __FILE__ == $0
def test_invalid
# They were actually used in some web sites.
- assert_exception(ArgumentError) { Time.httpdate("1 Dec 2001 10:23:57 GMT") }
- assert_exception(ArgumentError) { Time.httpdate("Sat, 1 Dec 2001 10:25:42 GMT") }
- assert_exception(ArgumentError) { Time.httpdate("Sat, 1-Dec-2001 10:53:55 GMT") }
- assert_exception(ArgumentError) { Time.httpdate("Saturday, 01-Dec-2001 10:15:34 GMT") }
- assert_exception(ArgumentError) { Time.httpdate("Saturday, 01-Dec-101 11:10:07 GMT") }
- assert_exception(ArgumentError) { Time.httpdate("Fri, 30 Nov 2001 21:30:00 JST") }
+ assert_raises(ArgumentError) { Time.httpdate("1 Dec 2001 10:23:57 GMT") }
+ assert_raises(ArgumentError) { Time.httpdate("Sat, 1 Dec 2001 10:25:42 GMT") }
+ assert_raises(ArgumentError) { Time.httpdate("Sat, 1-Dec-2001 10:53:55 GMT") }
+ assert_raises(ArgumentError) { Time.httpdate("Saturday, 01-Dec-2001 10:15:34 GMT") }
+ assert_raises(ArgumentError) { Time.httpdate("Saturday, 01-Dec-101 11:10:07 GMT") }
+ assert_raises(ArgumentError) { Time.httpdate("Fri, 30 Nov 2001 21:30:00 JST") }
# They were actually used in some mails.
- assert_exception(ArgumentError) { Time.rfc2822("01-5-20") }
- assert_exception(ArgumentError) { Time.rfc2822("7/21/00") }
- assert_exception(ArgumentError) { Time.rfc2822("2001-8-28") }
- assert_exception(ArgumentError) { Time.rfc2822("00-5-6 1:13:06") }
- assert_exception(ArgumentError) { Time.rfc2822("2001-9-27 9:36:49") }
- assert_exception(ArgumentError) { Time.rfc2822("2000-12-13 11:01:11") }
- assert_exception(ArgumentError) { Time.rfc2822("2001/10/17 04:29:55") }
- assert_exception(ArgumentError) { Time.rfc2822("9/4/2001 9:23:19 PM") }
- assert_exception(ArgumentError) { Time.rfc2822("01 Nov 2001 09:04:31") }
- assert_exception(ArgumentError) { Time.rfc2822("13 Feb 2001 16:4 GMT") }
- assert_exception(ArgumentError) { Time.rfc2822("01 Oct 00 5:41:19 PM") }
- assert_exception(ArgumentError) { Time.rfc2822("2 Jul 00 00:51:37 JST") }
- assert_exception(ArgumentError) { Time.rfc2822("01 11 2001 06:55:57 -0500") }
- assert_exception(ArgumentError) { Time.rfc2822("18 \343\366\356\341\370 2000") }
- assert_exception(ArgumentError) { Time.rfc2822("Fri, Oct 2001 18:53:32") }
- assert_exception(ArgumentError) { Time.rfc2822("Fri, 2 Nov 2001 03:47:54") }
- assert_exception(ArgumentError) { Time.rfc2822("Fri, 27 Jul 2001 11.14.14 +0200") }
- assert_exception(ArgumentError) { Time.rfc2822("Thu, 2 Nov 2000 04:13:53 -600") }
- assert_exception(ArgumentError) { Time.rfc2822("Wed, 5 Apr 2000 22:57:09 JST") }
- assert_exception(ArgumentError) { Time.rfc2822("Mon, 11 Sep 2000 19:47:33 00000") }
- assert_exception(ArgumentError) { Time.rfc2822("Fri, 28 Apr 2000 20:40:47 +-900") }
- assert_exception(ArgumentError) { Time.rfc2822("Fri, 19 Jan 2001 8:15:36 AM -0500") }
- assert_exception(ArgumentError) { Time.rfc2822("Thursday, Sep 27 2001 7:42:35 AM EST") }
- assert_exception(ArgumentError) { Time.rfc2822("3/11/2001 1:31:57 PM Pacific Daylight Time") }
- assert_exception(ArgumentError) { Time.rfc2822("Mi, 28 Mrz 2001 11:51:36") }
- assert_exception(ArgumentError) { Time.rfc2822("P, 30 sept 2001 23:03:14") }
- assert_exception(ArgumentError) { Time.rfc2822("fr, 11 aug 2000 18:39:22") }
- assert_exception(ArgumentError) { Time.rfc2822("Fr, 21 Sep 2001 17:44:03 -1000") }
- assert_exception(ArgumentError) { Time.rfc2822("Mo, 18 Jun 2001 19:21:40 -1000") }
- assert_exception(ArgumentError) { Time.rfc2822("l\366, 12 aug 2000 18:53:20") }
- assert_exception(ArgumentError) { Time.rfc2822("l\366, 26 maj 2001 00:15:58") }
- assert_exception(ArgumentError) { Time.rfc2822("Dom, 30 Sep 2001 17:36:30") }
- assert_exception(ArgumentError) { Time.rfc2822("%&, 31 %2/ 2000 15:44:47 -0500") }
- assert_exception(ArgumentError) { Time.rfc2822("dom, 26 ago 2001 03:57:07 -0300") }
- assert_exception(ArgumentError) { Time.rfc2822("ter, 04 set 2001 16:27:58 -0300") }
- assert_exception(ArgumentError) { Time.rfc2822("Wen, 3 oct 2001 23:17:49 -0400") }
- assert_exception(ArgumentError) { Time.rfc2822("Wen, 3 oct 2001 23:17:49 -0400") }
- assert_exception(ArgumentError) { Time.rfc2822("ele, 11 h: 2000 12:42:15 -0500") }
- assert_exception(ArgumentError) { Time.rfc2822("Tue, 14 Aug 2001 3:55:3 +0200") }
- assert_exception(ArgumentError) { Time.rfc2822("Fri, 25 Aug 2000 9:3:48 +0800") }
- assert_exception(ArgumentError) { Time.rfc2822("Fri, 1 Dec 2000 0:57:50 EST") }
- assert_exception(ArgumentError) { Time.rfc2822("Mon, 7 May 2001 9:39:51 +0200") }
- assert_exception(ArgumentError) { Time.rfc2822("Wed, 1 Aug 2001 16:9:15 +0200") }
- assert_exception(ArgumentError) { Time.rfc2822("Wed, 23 Aug 2000 9:17:36 +0800") }
- assert_exception(ArgumentError) { Time.rfc2822("Fri, 11 Aug 2000 10:4:42 +0800") }
- assert_exception(ArgumentError) { Time.rfc2822("Sat, 15 Sep 2001 13:22:2 +0300") }
- assert_exception(ArgumentError) { Time.rfc2822("Wed,16 \276\305\324\302 2001 20:06:25 +0800") }
- assert_exception(ArgumentError) { Time.rfc2822("Wed,7 \312\256\322\273\324\302 2001 23:47:22 +0800") }
- assert_exception(ArgumentError) { Time.rfc2822("=?iso-8859-1?Q?(=C5=DA),?= 10 2 2001 23:32:26 +0900 (JST)") }
- assert_exception(ArgumentError) { Time.rfc2822("\307\341\314\343\332\311, 30 \344\346\335\343\310\321 2001 10:01:06") }
- assert_exception(ArgumentError) { Time.rfc2822("=?iso-8859-1?Q?(=BF=E5),?= 12 =?iso-8859-1?Q?9=B7=EE?= 2001 14:52:41\n+0900 (JST)") }
+ assert_raises(ArgumentError) { Time.rfc2822("01-5-20") }
+ assert_raises(ArgumentError) { Time.rfc2822("7/21/00") }
+ assert_raises(ArgumentError) { Time.rfc2822("2001-8-28") }
+ assert_raises(ArgumentError) { Time.rfc2822("00-5-6 1:13:06") }
+ assert_raises(ArgumentError) { Time.rfc2822("2001-9-27 9:36:49") }
+ assert_raises(ArgumentError) { Time.rfc2822("2000-12-13 11:01:11") }
+ assert_raises(ArgumentError) { Time.rfc2822("2001/10/17 04:29:55") }
+ assert_raises(ArgumentError) { Time.rfc2822("9/4/2001 9:23:19 PM") }
+ assert_raises(ArgumentError) { Time.rfc2822("01 Nov 2001 09:04:31") }
+ assert_raises(ArgumentError) { Time.rfc2822("13 Feb 2001 16:4 GMT") }
+ assert_raises(ArgumentError) { Time.rfc2822("01 Oct 00 5:41:19 PM") }
+ assert_raises(ArgumentError) { Time.rfc2822("2 Jul 00 00:51:37 JST") }
+ assert_raises(ArgumentError) { Time.rfc2822("01 11 2001 06:55:57 -0500") }
+ assert_raises(ArgumentError) { Time.rfc2822("18 \343\366\356\341\370 2000") }
+ assert_raises(ArgumentError) { Time.rfc2822("Fri, Oct 2001 18:53:32") }
+ assert_raises(ArgumentError) { Time.rfc2822("Fri, 2 Nov 2001 03:47:54") }
+ assert_raises(ArgumentError) { Time.rfc2822("Fri, 27 Jul 2001 11.14.14 +0200") }
+ assert_raises(ArgumentError) { Time.rfc2822("Thu, 2 Nov 2000 04:13:53 -600") }
+ assert_raises(ArgumentError) { Time.rfc2822("Wed, 5 Apr 2000 22:57:09 JST") }
+ assert_raises(ArgumentError) { Time.rfc2822("Mon, 11 Sep 2000 19:47:33 00000") }
+ assert_raises(ArgumentError) { Time.rfc2822("Fri, 28 Apr 2000 20:40:47 +-900") }
+ assert_raises(ArgumentError) { Time.rfc2822("Fri, 19 Jan 2001 8:15:36 AM -0500") }
+ assert_raises(ArgumentError) { Time.rfc2822("Thursday, Sep 27 2001 7:42:35 AM EST") }
+ assert_raises(ArgumentError) { Time.rfc2822("3/11/2001 1:31:57 PM Pacific Daylight Time") }
+ assert_raises(ArgumentError) { Time.rfc2822("Mi, 28 Mrz 2001 11:51:36") }
+ assert_raises(ArgumentError) { Time.rfc2822("P, 30 sept 2001 23:03:14") }
+ assert_raises(ArgumentError) { Time.rfc2822("fr, 11 aug 2000 18:39:22") }
+ assert_raises(ArgumentError) { Time.rfc2822("Fr, 21 Sep 2001 17:44:03 -1000") }
+ assert_raises(ArgumentError) { Time.rfc2822("Mo, 18 Jun 2001 19:21:40 -1000") }
+ assert_raises(ArgumentError) { Time.rfc2822("l\366, 12 aug 2000 18:53:20") }
+ assert_raises(ArgumentError) { Time.rfc2822("l\366, 26 maj 2001 00:15:58") }
+ assert_raises(ArgumentError) { Time.rfc2822("Dom, 30 Sep 2001 17:36:30") }
+ assert_raises(ArgumentError) { Time.rfc2822("%&, 31 %2/ 2000 15:44:47 -0500") }
+ assert_raises(ArgumentError) { Time.rfc2822("dom, 26 ago 2001 03:57:07 -0300") }
+ assert_raises(ArgumentError) { Time.rfc2822("ter, 04 set 2001 16:27:58 -0300") }
+ assert_raises(ArgumentError) { Time.rfc2822("Wen, 3 oct 2001 23:17:49 -0400") }
+ assert_raises(ArgumentError) { Time.rfc2822("Wen, 3 oct 2001 23:17:49 -0400") }
+ assert_raises(ArgumentError) { Time.rfc2822("ele, 11 h: 2000 12:42:15 -0500") }
+ assert_raises(ArgumentError) { Time.rfc2822("Tue, 14 Aug 2001 3:55:3 +0200") }
+ assert_raises(ArgumentError) { Time.rfc2822("Fri, 25 Aug 2000 9:3:48 +0800") }
+ assert_raises(ArgumentError) { Time.rfc2822("Fri, 1 Dec 2000 0:57:50 EST") }
+ assert_raises(ArgumentError) { Time.rfc2822("Mon, 7 May 2001 9:39:51 +0200") }
+ assert_raises(ArgumentError) { Time.rfc2822("Wed, 1 Aug 2001 16:9:15 +0200") }
+ assert_raises(ArgumentError) { Time.rfc2822("Wed, 23 Aug 2000 9:17:36 +0800") }
+ assert_raises(ArgumentError) { Time.rfc2822("Fri, 11 Aug 2000 10:4:42 +0800") }
+ assert_raises(ArgumentError) { Time.rfc2822("Sat, 15 Sep 2001 13:22:2 +0300") }
+ assert_raises(ArgumentError) { Time.rfc2822("Wed,16 \276\305\324\302 2001 20:06:25 +0800") }
+ assert_raises(ArgumentError) { Time.rfc2822("Wed,7 \312\256\322\273\324\302 2001 23:47:22 +0800") }
+ assert_raises(ArgumentError) { Time.rfc2822("=?iso-8859-1?Q?(=C5=DA),?= 10 2 2001 23:32:26 +0900 (JST)") }
+ assert_raises(ArgumentError) { Time.rfc2822("\307\341\314\343\332\311, 30 \344\346\335\343\310\321 2001 10:01:06") }
+ assert_raises(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
end
- RUNIT::CUI::TestRunner.run(TimeExtentionTest.suite)
end
diff --git a/lib/timeout.rb b/lib/timeout.rb
index 8c6ccb7128..5c6a72652a 100644
--- a/lib/timeout.rb
+++ b/lib/timeout.rb
@@ -23,30 +23,52 @@
#
# The time in seconds to wait for block teminatation.
#
+# : [exception]
+#
+# The exception classs to be raised on timeout.
+#
#=end
-class TimeoutError<Interrupt
-end
+module Timeout
+ class Error<Interrupt
+ end
-def timeout(sec, exception=TimeoutError)
- return yield if sec == nil
- 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?
+ def timeout(sec, exception=Error)
+ return yield if sec == nil or sec.zero?
+ 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
+# compatible
+def timeout(n, e=Timeout::Error, &block)
+ Timeout::timeout(n, e, &block)
+end
+TimeoutError = Timeout::Error
+
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
diff --git a/lib/tmpdir.rb b/lib/tmpdir.rb
new file mode 100644
index 0000000000..bee99c41cb
--- /dev/null
+++ b/lib/tmpdir.rb
@@ -0,0 +1,42 @@
+#
+# tmpdir - retrieve temporary directory path
+#
+# $Id$
+#
+
+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
+
+ 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/lib/tracer.rb b/lib/tracer.rb
index f522a9178d..3ccf1b5017 100644
--- a/lib/tracer.rb
+++ b/lib/tracer.rb
@@ -119,6 +119,7 @@ class Tracer
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,
@@ -127,7 +128,7 @@ class Tracer
klass || '',
EVENT_SYMBOL[event],
get_line(file, line))
- Thread.critical = false
+ Thread.critical = saved_crit
end
Single = new
diff --git a/lib/tsort.rb b/lib/tsort.rb
index 21adeaabfe..3bf8c33bbf 100644
--- a/lib/tsort.rb
+++ b/lib/tsort.rb
@@ -199,18 +199,20 @@ module TSort
result
end
- def each_strongly_connected_component(&block)
+ def each_strongly_connected_component
id_map = {}
stack = []
tsort_each_node {|node|
unless id_map.include? node
- each_strongly_connected_component_from(node, id_map, stack, &block)
+ each_strongly_connected_component_from(node, id_map, stack) {|c|
+ yield c
+ }
end
}
nil
end
- def each_strongly_connected_component_from(node, id_map={}, stack=[], &block)
+ def each_strongly_connected_component_from(node, id_map={}, stack=[])
minimum_id = node_id = id_map[node] = id_map.size
stack_length = stack.length
stack << node
@@ -221,7 +223,9 @@ module TSort
minimum_id = child_id if child_id && child_id < minimum_id
else
sub_minimum_id =
- each_strongly_connected_component_from(child, id_map, stack, &block)
+ each_strongly_connected_component_from(child, id_map, stack) {|c|
+ yield c
+ }
minimum_id = sub_minimum_id if sub_minimum_id < minimum_id
end
}
@@ -245,8 +249,7 @@ module TSort
end
if __FILE__ == $0
- require 'runit/testcase'
- require 'runit/cui/testrunner'
+ require 'test/unit'
class Hash
include TSort
@@ -264,7 +267,7 @@ if __FILE__ == $0
end
end
- class TSortTest < RUNIT::TestCase
+ class TSortTest < Test::Unit::TestCase
def test_dag
h = {1=>[2, 3], 2=>[3], 3=>[]}
assert_equal([3, 2, 1], h.tsort)
@@ -275,7 +278,7 @@ if __FILE__ == $0
h = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
assert_equal([[4], [2, 3], [1]],
h.strongly_connected_components.map {|nodes| nodes.sort})
- assert_exception(TSort::Cyclic) { h.tsort }
+ assert_raises(TSort::Cyclic) { h.tsort }
end
def test_array
@@ -287,8 +290,21 @@ if __FILE__ == $0
assert_equal([[0], [1]],
a.strongly_connected_components.map {|nodes| nodes.sort})
end
+
+ def orphaned_proc(block_str)
+ eval "lambda {#{block_str}}"
+ end
+
+ def test_orphaned_break
+ a = [[1], [2], []]
+ @n = 0
+ x = orphaned_proc %{|c| @n += 1; break :break_value}
+ assert_nothing_raised {
+ assert_equal(:break_value, a.each_strongly_connected_component(&x))
+ }
+ assert_equal(1, @n)
+ end
end
- RUNIT::CUI::TestRunner.run(TSortTest.suite)
end
diff --git a/lib/un.rb b/lib/un.rb
new file mode 100644
index 0000000000..8f630801d8
--- /dev/null
+++ b/lib/un.rb
@@ -0,0 +1,183 @@
+#
+# = 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
+
+require 'fileutils'
+require 'getopts'
+
+module FileUtils
+# @fileutils_label = ''
+ @fileutils_output = $stdout
+end
+
+def setup(options = "")
+ options += "v"
+ ARGV.map! do |x|
+ case x
+ when /^-/
+ x.delete "^-#{options}"
+ when /[*?\[{]/
+ Dir[x]
+ else
+ x
+ end
+ end
+ ARGV.flatten!
+ ARGV.delete_if{|x| x == '-'}
+ getopts(options)
+ options = {}
+ options[:verbose] = true if $OPT["v"]
+ options[:force] = true if $OPT["f"]
+ options[:preserve] = true if $OPT["p"]
+ yield ARGV, options, $OPT
+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
+#
+def cp
+ setup("pr") do |argv, options, opt|
+ cmd = "cp"
+ cmd += "_r" if opt["r"]
+ 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
+#
+def ln
+ setup("sf") do |argv, options, opt|
+ cmd = "ln"
+ cmd += "_s" if opt["s"]
+ 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 SOURCE DEST
+#
+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
+#
+def rm
+ setup("fr") do |argv, options, opt|
+ cmd = "rm"
+ cmd += "_r" if opt["r"]
+ 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
+#
+def mkdir
+ setup("p") do |argv, options, opt|
+ cmd = "mkdir"
+ cmd += "_p" if options.delete :preserve
+ FileUtils.send cmd, argv, options
+ end
+end
+
+#
+# Remove the DIR.
+#
+# ruby -run -e rmdir DIR
+#
+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
+#
+def install
+ setup("pm:") do |argv, options, opt|
+ options[:mode] = opt["m"] ? opt["m"].oct : 0755
+ 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 OCTAL-MODE FILE
+#
+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 FILE
+#
+def touch
+ setup do |argv, options|
+ FileUtils.touch argv, options
+ end
+end
diff --git a/lib/uri.rb b/lib/uri.rb
index f95d72b7ce..dec18a110b 100644
--- a/lib/uri.rb
+++ b/lib/uri.rb
@@ -15,7 +15,7 @@
=end
module URI
- VERSION_CODE = '000909'.freeze
+ VERSION_CODE = '000910'.freeze
VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze
end
diff --git a/lib/uri/ftp.rb b/lib/uri/ftp.rb
index 391cd550e9..fc22a99753 100644
--- a/lib/uri/ftp.rb
+++ b/lib/uri/ftp.rb
@@ -116,6 +116,7 @@ module URI
def typecode=(typecode)
check_typecode(typecode)
set_typecode(typecode)
+ typecode
end
=begin
diff --git a/lib/uri/generic.rb b/lib/uri/generic.rb
index 4a671e9ed5..59f36ad4c3 100644
--- a/lib/uri/generic.rb
+++ b/lib/uri/generic.rb
@@ -243,6 +243,7 @@ Object
def scheme=(v)
check_scheme(v)
set_scheme(v)
+ v
end
=begin
@@ -315,16 +316,19 @@ Object
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)
@@ -420,6 +424,7 @@ Object
def host=(v)
check_host(v)
set_host(v)
+ v
end
=begin
@@ -463,6 +468,7 @@ Object
def port=(v)
check_port(v)
set_port(v)
+ port
end
=begin
@@ -502,6 +508,7 @@ Object
def registry=(v)
check_registry(v)
set_registry(v)
+ v
end
=begin
@@ -548,6 +555,7 @@ Object
def path=(v)
check_path(v)
set_path(v)
+ v
end
=begin
@@ -589,6 +597,7 @@ Object
def query=(v)
check_query(v)
set_query(v)
+ v
end
=begin
@@ -628,6 +637,7 @@ Object
def opaque=(v)
check_opaque(v)
set_opaque(v)
+ v
end
=begin
@@ -661,6 +671,7 @@ Object
def fragment=(v)
check_fragment(v)
set_fragment(v)
+ v
end
=begin
@@ -1030,7 +1041,7 @@ Object
end
private :path_query
- def to_str
+ def to_s
str = ''
if @scheme
str << @scheme
@@ -1071,20 +1082,12 @@ Object
str
end
- def to_s
- to_str
- end
-
=begin
--- URI::Generic#==(oth)
=end
def ==(oth)
- if oth.kind_of?(String)
- oth = URI.parse(oth)
- end
-
if self.class == oth.class
self.normalize.component_ary == oth.normalize.component_ary
else
diff --git a/lib/uri/ldap.rb b/lib/uri/ldap.rb
index 2911aa5738..ae85bb2164 100644
--- a/lib/uri/ldap.rb
+++ b/lib/uri/ldap.rb
@@ -140,6 +140,7 @@ URI::LDAP is copyrighted free software by Takaaki Tateishi and akira yamada.
def dn=(val)
set_dn(val)
+ val
end
=begin
@@ -163,6 +164,7 @@ URI::LDAP is copyrighted free software by Takaaki Tateishi and akira yamada.
def attributes=(val)
set_attributes(val)
+ val
end
=begin
@@ -186,6 +188,7 @@ URI::LDAP is copyrighted free software by Takaaki Tateishi and akira yamada.
def scope=(val)
set_scope(val)
+ val
end
=begin
@@ -209,6 +212,7 @@ URI::LDAP is copyrighted free software by Takaaki Tateishi and akira yamada.
def filter=(val)
set_filter(val)
+ val
end
=begin
@@ -232,6 +236,7 @@ URI::LDAP is copyrighted free software by Takaaki Tateishi and akira yamada.
def extensions=(val)
set_extensions(val)
+ val
end
end
diff --git a/lib/uri/mailto.rb b/lib/uri/mailto.rb
index d50d2b792f..0b0d0a5878 100644
--- a/lib/uri/mailto.rb
+++ b/lib/uri/mailto.rb
@@ -172,6 +172,7 @@ module URI
def to=(v)
check_to(v)
set_to(v)
+ v
end
=begin
@@ -213,6 +214,7 @@ module URI
def headers=(v)
check_headers(v)
set_headers(v)
+ v
end
def to_str
diff --git a/lib/webrick.rb b/lib/webrick.rb
new file mode 100644
index 0000000000..8fca81bafb
--- /dev/null
+++ b/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/lib/webrick/accesslog.rb b/lib/webrick/accesslog.rb
new file mode 100644
index 0000000000..10a801196c
--- /dev/null
+++ b/lib/webrick/accesslog.rb
@@ -0,0 +1,64 @@
+#
+# 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["o"] = res
+ params["p"] = config[: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, ?o
+ raise AccessLogError,
+ "parameter is required for \"#{spec}\"" unless param
+ params[spec][param] || "-"
+ when ?t
+ params[spec].strftime(param || CLF_TIME_FORMAT)
+ else
+ params[spec]
+ end
+ }
+ end
+ end
+end
diff --git a/lib/webrick/compat.rb b/lib/webrick/compat.rb
new file mode 100644
index 0000000000..a972204ff1
--- /dev/null
+++ b/lib/webrick/compat.rb
@@ -0,0 +1,30 @@
+#
+# 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
+
+unless File.respond_to?(:fnmatch)
+ def File.fnmatch(pat, str)
+ case pat[0]
+ when nil
+ not str[0]
+ when ?*
+ fnmatch(pat[1..-1], str) || str[0] && fnmatch(pat, str[1..-1])
+ when ??
+ str[0] && fnmatch(pat[1..-1], str[1..-1])
+ else
+ pat[0] == str[0] && fnmatch(pat[1..-1], str[1..-1])
+ end
+ end
+end
diff --git a/lib/webrick/config.rb b/lib/webrick/config.rb
new file mode 100644
index 0000000000..229beada08
--- /dev/null
+++ b/lib/webrick/config.rb
@@ -0,0 +1,96 @@
+#
+# 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!!
+ :Listen => [], # list of pairs of alt addr/port.
+ :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,
+ :ProxyAuthProc => nil,
+ :ProxyContentHandler => nil,
+ :ProxyVia => true,
+ :ProxyTimeout => true,
+
+ # upstream proxy server
+ :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 => "public_html",
+ }
+
+ 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/lib/webrick/cookie.rb b/lib/webrick/cookie.rb
new file mode 100644
index 0000000000..4785b2bb33
--- /dev/null
+++ b/lib/webrick/cookie.rb
@@ -0,0 +1,80 @@
+#
+# 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
+
+ end
+end
diff --git a/lib/webrick/htmlutils.rb b/lib/webrick/htmlutils.rb
new file mode 100644
index 0000000000..cf8d542c09
--- /dev/null
+++ b/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/lib/webrick/httpauth.rb b/lib/webrick/httpauth.rb
new file mode 100644
index 0000000000..3f0b83339d
--- /dev/null
+++ b/lib/webrick/httpauth.rb
@@ -0,0 +1,46 @@
+#
+# 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 'base64'
+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 = decode64(userpass).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/lib/webrick/httpauth/authenticator.rb b/lib/webrick/httpauth/authenticator.rb
new file mode 100644
index 0000000000..fe2dbf4e0c
--- /dev/null
+++ b/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/lib/webrick/httpauth/basicauth.rb b/lib/webrick/httpauth/basicauth.rb
new file mode 100644
index 0000000000..926a6b8289
--- /dev/null
+++ b/lib/webrick/httpauth/basicauth.rb
@@ -0,0 +1,66 @@
+#
+# 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'
+require 'base64'
+
+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 = decode64(basic_credentials).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/lib/webrick/httpauth/digestauth.rb b/lib/webrick/httpauth/digestauth.rb
new file mode 100644
index 0000000000..2b0660ed29
--- /dev/null
+++ b/lib/webrick/httpauth/digestauth.rb
@@ -0,0 +1,348 @@
+#
+# 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'
+require 'base64'
+
+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 = digest("", auth_req['uri'])
+ elsif auth_req['qop'] == "auth-int"
+ ha2 = hexdigest(req.request_method, auth_req['uri'],
+ hexdigest(req.body))
+ ha2_res = digest("", auth_req['uri'], hexdigest(req.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 = encode64(now + ":" + pk).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 = decode64(nonce).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
+
+ def digest(*args)
+ @h.digest(args.join(":"))
+ end
+ end
+
+ class ProxyDigestAuth < DigestAuth
+ include ProxyAuthenticator
+
+ def check_uri(req, auth_req)
+ return true
+ end
+ end
+ end
+end
diff --git a/lib/webrick/httpauth/htdigest.rb b/lib/webrick/httpauth/htdigest.rb
new file mode 100644
index 0000000000..3949756f2b
--- /dev/null
+++ b/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/lib/webrick/httpauth/htgroup.rb b/lib/webrick/httpauth/htgroup.rb
new file mode 100644
index 0000000000..c9270c61cc
--- /dev/null
+++ b/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/lib/webrick/httpauth/htpasswd.rb b/lib/webrick/httpauth/htpasswd.rb
new file mode 100644
index 0000000000..a4a80647d8
--- /dev/null
+++ b/lib/webrick/httpauth/htpasswd.rb
@@ -0,0 +1,75 @@
+#
+# 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!
+ user, pass = line.split(":")
+ @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/lib/webrick/httpauth/userdb.rb b/lib/webrick/httpauth/userdb.rb
new file mode 100644
index 0000000000..33e01405f4
--- /dev/null
+++ b/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/lib/webrick/httpproxy.rb b/lib/webrick/httpproxy.rb
new file mode 100644
index 0000000000..c3bbbc54be
--- /dev/null
+++ b/lib/webrick/httpproxy.rb
@@ -0,0 +1,237 @@
+#
+# 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
+ 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 " + encode64(proxy.userinfo)
+ 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 " + encode64(proxy.userinfo)
+ 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
+ res.send_response(ua)
+ access_log(@config, req, res)
+ 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/lib/webrick/httprequest.rb b/lib/webrick/httprequest.rb
new file mode 100644
index 0000000000..8b0803878e
--- /dev/null
+++ b/lib/webrick/httprequest.rb
@@ -0,0 +1,339 @@
+#
+# 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, :query_string
+ attr_accessor :script_name, :path_info
+
+ # Header and entity body
+ attr_reader :raw_header, :header, :cookies
+
+ # 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 = []
+ @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)
+ }
+ 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 [](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.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"] = @request_uri.host
+ meta["SERVER_PORT"] = @config[:Port].to_s
+ meta["SERVER_PROTOCOL"] = "HTTP/" + @config[:HTTPVersion].to_s
+ meta["SERVER_SOFTWARE"] = @config[:ServerSoftware].dup
+
+ self.each{|key, val|
+ 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"]
+ host, port = self['host'].split(":", 2)
+ 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 = read_data(socket, chunk_size) # read chunk-data
+ 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/lib/webrick/httpresponse.rb b/lib/webrick/httpresponse.rb
new file mode 100644
index 0000000000..6b00c2b88b
--- /dev/null
+++ b/lib/webrick/httpresponse.rb
@@ -0,0 +1,304 @@
+#
+# 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_reader :config, :keep_alive, :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 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
+ @logger.error("HTTPResponse#send_response: EPIPE occured.")
+ @keep_alive = false
+ rescue => 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
+ end
+ if keep_alive?
+ if chunked? || @header['content-length']
+ @header['connection'] = "Keep-Alive"
+ end
+ 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)
+ 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.to_i)
+ @sent_size = size
+ end
+ @body.close
+ 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/lib/webrick/https.rb b/lib/webrick/https.rb
new file mode 100644
index 0000000000..00fd469f1b
--- /dev/null
+++ b/lib/webrick/https.rb
@@ -0,0 +1,158 @@
+#
+# 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'
+require 'openssl'
+
+module WEBrick
+ module Config
+ HTTP.update(
+ :SSLEnable => true,
+ :SSLCertificate => nil,
+ :SSLPrivateKey => nil,
+ :SSLClientCA => nil,
+ :SSLCACertificateFile => nil,
+ :SSLCACertificatePath => nil,
+ :SSLCertStore => nil,
+ :SSLVerifyClient => ::OpenSSL::SSL::VERIFY_NONE,
+ :SSLVerifyDepth => nil,
+ :SSLVerifyCallback => nil, # custom verification
+ :SSLTimeout => nil,
+ :SSLOptions => nil,
+ # Must specify if you use auto generated certificate.
+ :SSLCertName => nil,
+ :SSLCertComment => "Generated by Ruby/OpenSSL"
+ )
+
+ osslv = ::OpenSSL::OPENSSL_VERSION.split[1]
+ HTTP[:ServerSoftware] << " OpenSSL/#{osslv}"
+ end
+
+ class HTTPRequest
+ attr_reader :cipher, :server_cert, :client_cert
+
+ alias orig_parse parse
+
+ def parse(socket=nil)
+ orig_parse(socket)
+ @cipher = socket.respond_to?(:cipher) ? socket.cipher : nil
+ @client_cert = socket.respond_to?(:peer_cert) ? socket.peer_cert : nil
+ @server_cert = @config[:SSLCertificate]
+ end
+
+ alias orig_parse_uri parse_uri
+
+ def parse_uri(str, scheme="https")
+ if @config[:SSLEnable]
+ 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 @config[:SSLEnable]
+ meta["HTTPS"] = "on"
+ meta["SSL_CIPHER"] = @cipher ? @cipher[0] : ""
+ meta["SSL_CLIENT_CERT"] = @client_cert ? @client_cert.to_pem : ""
+ meta["SSL_SERVER_CERT"] = @server_cert ? @server_cert.to_pem : ""
+ end
+ meta
+ end
+ end
+
+ class HTTPServer
+ alias orig_init initialize
+
+ def initialize(*args)
+ orig_init(*args)
+
+ if @config[:SSLEnable]
+ unless @config[:SSLCertificate]
+ rsa = OpenSSL::PKey::RSA.new(512){|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(@config[: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.create_extension("extendedKeyUsage", "serverAuth")
+ ]
+ ef.issuer_certificate = cert
+ ext = ef.create_extension("authorityKeyIdentifier",
+ "keyid:always,issuer:always")
+ cert.add_extension(ext)
+ if comment = @config[:SSLCertComment]
+ cert.add_extension(ef.create_extension("nsComment", comment))
+ end
+ cert.sign(rsa, OpenSSL::Digest::SHA1.new)
+
+ @config[:SSLPrivateKey] = rsa
+ @config[:SSLCertificate] = cert
+ @logger.info cert.to_s
+ end
+ @ctx = OpenSSL::SSL::SSLContext.new
+ set_ssl_context(@ctx, @config)
+ end
+ end
+
+ alias orig_run run
+
+ def run(sock)
+ if @config[:SSLEnable]
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
+ ssl.accept
+ Thread.current[:WEBrickSocket] = ssl
+ orig_run(ssl)
+ Thread.current[:WEBrickSocket] = sock
+ ssl.close
+ else
+ orig_run(sock)
+ end
+ end
+
+ private
+
+ def set_ssl_context(ctx, config)
+ ctx.key = config[:SSLPrivateKey]
+ ctx.cert = config[:SSLCertificate]
+ ctx.client_ca = config[:SSLClientCA]
+ ctx.ca_file = config[:SSLCACertificateFile]
+ ctx.ca_path = config[:SSLCACertificatePath]
+ ctx.cert_store = config[:SSLCertStore]
+ ctx.verify_mode = config[:SSLVerifyClient]
+ ctx.verify_depth = config[:SSLVerifyDepth]
+ ctx.verify_callback = config[:SSLVerifyCallback]
+ ctx.timeout = config[:SSLTimeout]
+ ctx.options = config[:SSLOptions]
+ end
+ end
+end
diff --git a/lib/webrick/httpserver.rb b/lib/webrick/httpserver.rb
new file mode 100644
index 0000000000..df06e19e2c
--- /dev/null
+++ b/lib/webrick/httpserver.rb
@@ -0,0 +1,179 @@
+#
+# 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]
+ basic_log = BasicLog::new
+ @config[:AccessLog] = [
+ [ basic_log, AccessLog::COMMON_LOG_FORMAT ],
+ [ basic_log, AccessLog::REFERER_LOG_FORMAT ]
+ ]
+ end
+ end
+
+ def run(sock)
+ while true
+ res = HTTPResponse.new(@config)
+ req = HTTPRequest.new(@config)
+ begin
+ req.parse(sock)
+ res.request_method = req.request_method
+ res.request_uri = req.request_uri
+ res.request_http_version = req.http_version
+ if handler = @config[:RequestHandler]
+ handler.call(req, res)
+ end
+ service(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
+ @logger.error(ex)
+ res.set_error(ex, true)
+ ensure
+ if req.request_line
+ req.fixup()
+ res.send_response(sock)
+ 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.", inspect, 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 access_log(config, req, res)
+ param = AccessLog::setup_params(config, req, res)
+ level = Log::INFO
+ @config[:AccessLog].each{|logger, fmt|
+ logger.log(level, AccessLog::format(fmt, 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/lib/webrick/httpservlet.rb b/lib/webrick/httpservlet.rb
new file mode 100644
index 0000000000..ac7c022bd7
--- /dev/null
+++ b/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/lib/webrick/httpservlet/abstract.rb b/lib/webrick/httpservlet/abstract.rb
new file mode 100644
index 0000000000..03861e8fc7
--- /dev/null
+++ b/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/lib/webrick/httpservlet/cgi_runner.rb b/lib/webrick/httpservlet/cgi_runner.rb
new file mode 100644
index 0000000000..1069a68d58
--- /dev/null
+++ b/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/lib/webrick/httpservlet/cgihandler.rb b/lib/webrick/httpservlet/cgihandler.rb
new file mode 100644
index 0000000000..70708610d1
--- /dev/null
+++ b/lib/webrick/httpservlet/cgihandler.rb
@@ -0,0 +1,93 @@
+#
+# 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'])
+ 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, "w")
+ 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]
+ 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 = $? >> 8
+ 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,
+ "The server encontered a script error." if body.nil?
+
+ begin
+ header = HTTPUtils::parse_header(raw_header)
+ if /^(\d+)/ =~ header['status'][0]
+ res.status = $1.to_i
+ header.delete('status')
+ 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/lib/webrick/httpservlet/erbhandler.rb b/lib/webrick/httpservlet/erbhandler.rb
new file mode 100644
index 0000000000..40b7a57610
--- /dev/null
+++ b/lib/webrick/httpservlet/erbhandler.rb
@@ -0,0 +1,53 @@
+#
+# 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'] = "text/html"
+ 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/lib/webrick/httpservlet/filehandler.rb b/lib/webrick/httpservlet/filehandler.rb
new file mode 100644
index 0000000000..f6db991bf6
--- /dev/null
+++ b/lib/webrick/httpservlet/filehandler.rb
@@ -0,0 +1,330 @@
+#
+# 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_valie(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 BadRequest, "Unrecognized range-spec: \"#{range}\""
+ end
+ open(filename, "rb"){|io|
+ if ranges.size > 1
+ boundary = "#{time.sec}_#{time.usec}_#{Process::pid}"
+ body = ''
+ ranges.each{|r|
+ 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
+ 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(DefaultFileHandler)
+
+ 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 = 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)
+ suffix = (/\.(\w+)$/ =~ res.filename) && $1
+ handler = @options[:HandlerTable][suffix] || HandlerTable[suffix]
+ 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 set_filename(req, res)
+ handler = nil
+ res.filename = @root.dup
+ path_info = req.path_info.scan(%r|/[^/]*|)
+
+ while name = path_info.shift
+ if name == "/"
+ indices = @config[:DirectoryIndex]
+ index = indices.find{|i| FileTest::file?("#{res.filename}/#{i}") }
+ name = "/#{index}" if index
+ end
+ res.filename << name
+ req.script_name << name
+ req.path_info = path_info.join
+
+ if File::fnmatch("/#{@options[:NondisclosureName]}", name)
+ @logger.log(Log::WARN,
+ "the request refers nondisclosure name `#{name}'.")
+ raise HTTPStatus::Forbidden, "`#{req.path}' not found."
+ end
+ st = (File::stat(res.filename) rescue nil)
+ raise HTTPStatus::NotFound, "`#{req.path}' not found." unless st
+ raise HTTPStatus::Forbidden,
+ "no access permission to `#{req.path}'." unless st.readable?
+
+ if st.directory?
+ call_callback(:DirectoryCallback, req, res)
+ else
+ call_callback(:FileCallback, req, res)
+ return true
+ end
+ end
+ return false
+ end
+
+ def call_callback(callback_name, req, res)
+ if cb = @options[callback_name]
+ cb.call(req, res)
+ end
+ 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 File::fnmatch(@options[:NondisclosureName], 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.request_uri.host}:#{@config[:Port]}
+ </ADDRESS>
+ </BODY>
+</HTML>
+ _end_of_html_
+ end
+
+ end
+ end
+end
diff --git a/lib/webrick/httpservlet/prochandler.rb b/lib/webrick/httpservlet/prochandler.rb
new file mode 100644
index 0000000000..783cb27896
--- /dev/null
+++ b/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/lib/webrick/httpstatus.rb b/lib/webrick/httpstatus.rb
new file mode 100644
index 0000000000..0b22c992b3
--- /dev/null
+++ b/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/lib/webrick/httputils.rb b/lib/webrick/httputils.rb
new file mode 100644
index 0000000000..ce4defbb28
--- /dev/null
+++ b/lib/webrick/httputils.rb
@@ -0,0 +1,374 @@
+#
+# 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)
+ if suffix = (/\.(\w+)$/ =~ filename && $1)
+ mtype = mime_tab[suffix.downcase]
+ end
+ mtype || "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 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|
+ data.next_data = nil
+ ret << data
+ }
+ 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|
+ 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
+ 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
+
+ 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
+ module_function :_make_regex, :_escape, :_unescape
+
+ UNESCAPED = _make_regex(control+delims+unwise+nonascii)
+ UNESCAPED_FORM = _make_regex(reserved+control+delims+unwise+nonascii)
+ NONASCII = _make_regex(nonascii)
+ ESCAPED = /%([0-9a-fA-F]{2})/
+
+ 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 escape8bit(str)
+ _escape(str, NONASCII)
+ end
+
+ module_function :escape, :unescape, :escape_form, :unescape_form,
+ :escape8bit
+
+ end
+end
diff --git a/lib/webrick/httpversion.rb b/lib/webrick/httpversion.rb
new file mode 100644
index 0000000000..86907a26bd
--- /dev/null
+++ b/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/lib/webrick/log.rb b/lib/webrick/log.rb
new file mode 100644
index 0000000000..2f56102736
--- /dev/null
+++ b/lib/webrick/log.rb
@@ -0,0 +1,83 @@
+#
+# 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
+ @log << (data + "\n")
+ end
+ 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")
+ 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/lib/webrick/server.rb b/lib/webrick/server.rb
new file mode 100644
index 0000000000..911f78b66a
--- /dev/null
+++ b/lib/webrick/server.rb
@@ -0,0 +1,189 @@
+#
+# 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, STDOUT, STDERR ].each{|io|
+ io.reopen("/dev/null", "r+")
+ }
+ 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}")
+
+ if @config[:DoNotListen]
+ @listeners = []
+ else
+ @listeners = listen(@config[:BindAddress], @config[:Port])
+ @config[:Listen].each{|addr, port|
+ listen(addr, port).each{|sock| @listeners << sock }
+ }
+ end
+ end
+
+ def [](key)
+ @config[key]
+ end
+
+ def listen(address, port)
+ 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]}, #{ai[1]})")
+ sock = TCPServer.new(ai[3], ai[1])
+ Utils::set_close_on_exec(sock)
+ sockets << sock
+ rescue => ex
+ @logger.warn("TCPServer Error: #{ex}")
+ last_error = ex
+ end
+ }
+ raise last_error if sockets.empty?
+ return sockets
+ 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.
+ sock = svr.accept
+ sock.sync = true
+ Utils::set_close_on_exec(sock)
+ th = start_thread(sock, &block)
+ th[:WEBrickThread] = true
+ thgroup.add(th)
+ }
+ end
+ rescue Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPROTO => ex
+ msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
+ @logger.error msg
+ rescue Errno::EBADF => ex # IO::select causes by shutdown
+ rescue => ex
+ @logger.error ex
+ break
+ 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 start_thread(sock, &block)
+ Thread.start{
+ begin
+ Thread.current[:WEBrickSocket] = sock
+ addr = sock.peeraddr
+ @logger.debug "accept: #{addr[3]}:#{addr[1]}"
+ call_callback(:AcceptCallback, sock)
+ block ? block.call(sock) : run(sock)
+ rescue ServerError, Errno::ENOTCONN => ex
+ msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
+ @logger.error msg
+ rescue Exception => ex
+ @logger.error ex
+ ensure
+ Thread.current[:WEBrickSocket] = nil
+ @logger.debug "close: #{addr[3]}:#{addr[1]}"
+ sock.close
+ end
+ @tokens.push(nil)
+ }
+ 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/lib/webrick/utils.rb b/lib/webrick/utils.rb
new file mode 100644
index 0000000000..646880d655
--- /dev/null
+++ b/lib/webrick/utils.rb
@@ -0,0 +1,64 @@
+#
+# 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_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, group=nil)
+ if defined?(Etc)
+ pw = Etc.getpwnam(user)
+ gr = group ? Etc.getgrnam(group) : pw
+ Process::gid = gr.gid
+ Process::egid = gr.gid
+ Process::uid = pw.uid
+ Process::euid = pw.uid
+ end
+ end
+ module_function :su
+
+ def getservername
+ host = Socket::gethostname
+ begin
+ Socket::gethostbyname(host)[0]
+ rescue
+ host
+ end
+ end
+ module_function :getservername
+
+ 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/lib/webrick/version.rb b/lib/webrick/version.rb
new file mode 100644
index 0000000000..b2b9fd3b78
--- /dev/null
+++ b/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/lib/xmlrpc/base64.rb b/lib/xmlrpc/base64.rb
new file mode 100644
index 0000000000..f9a21c703a
--- /dev/null
+++ b/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$
+=end
diff --git a/lib/xmlrpc/client.rb b/lib/xmlrpc/client.rb
new file mode 100644
index 0000000000..198819ba66
--- /dev/null
+++ b/lib/xmlrpc/client.rb
@@ -0,0 +1,570 @@
+=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)
+: 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={} )
+ 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>)).
+
+--- 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::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$
+
+=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)
+
+ @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
+
+
+ def self.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
+
+
+ def self.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
+
+
+ # Attribute Accessors -------------------------------------------------------------------
+
+ 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, *args)
+ Proxy.new(self, prefix, args, :call)
+ end
+
+ def proxy2(prefix, *args)
+ Proxy.new(self, prefix, args, :call2)
+ end
+
+ def proxy_async(prefix, *args)
+ Proxy.new(self, prefix, args, :call_async)
+ end
+
+ def proxy2_async(prefix, *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",
+ "Content-Length" => request.size.to_s,
+ "Connection" => (async ? "close" : "keep-alive")
+ }
+
+ if @auth != nil
+ # add authorization header
+ header["Authorization"] = @auth
+ end
+
+ resp = 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
+
+ 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
+
+ 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 + 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/lib/xmlrpc/config.rb b/lib/xmlrpc/config.rb
new file mode 100644
index 0000000000..c4d2c41aac
--- /dev/null
+++ b/lib/xmlrpc/config.rb
@@ -0,0 +1,40 @@
+#
+# $Id$
+# 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/lib/xmlrpc/create.rb b/lib/xmlrpc/create.rb
new file mode 100644
index 0000000000..072e72ab46
--- /dev/null
+++ b/lib/xmlrpc/create.rb
@@ -0,0 +1,280 @@
+#
+# Creates XML-RPC call/response documents
+#
+# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
+#
+# $Id$
+#
+
+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
+
+ 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 Date
+ t = param
+ @writer.tag("dateTime.iso8601",
+ format("%.4d%02d%02dT00:00:00", t.year, t.month, t.day))
+
+ when Time
+ @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.__get_instance_variables.each {|name, val|
+ 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/lib/xmlrpc/datetime.rb b/lib/xmlrpc/datetime.rb
new file mode 100644
index 0000000000..e3bc6943f0
--- /dev/null
+++ b/lib/xmlrpc/datetime.rb
@@ -0,0 +1,138 @@
+=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
+
+end
+
+
+end # module XMLRPC
+
+
+=begin
+= History
+ $Id$
+=end
diff --git a/lib/xmlrpc/httpserver.rb b/lib/xmlrpc/httpserver.rb
new file mode 100644
index 0000000000..9afb5fd5ec
--- /dev/null
+++ b/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$
+#
+
+
+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/lib/xmlrpc/marshal.rb b/lib/xmlrpc/marshal.rb
new file mode 100644
index 0000000000..26510124c2
--- /dev/null
+++ b/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$
+#
+
+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/lib/xmlrpc/parser.rb b/lib/xmlrpc/parser.rb
new file mode 100644
index 0000000000..233dd596fd
--- /dev/null
+++ b/lib/xmlrpc/parser.rb
@@ -0,0 +1,803 @@
+#
+# Parser for XML-RPC call and response
+#
+# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
+#
+# $Id$
+#
+
+
+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 < Exception
+ attr_reader :faultCode, :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)
+ if str =~ /^(-?\d\d\d\d)(\d\d)(\d\d)T(\d\d):(\d\d):(\d\d)$/ then
+ # TODO: Time.gm ??? .local ???
+ a = [$1, $2, $3, $4, $5, $6].collect{|i| i.to_i}
+
+ XMLRPC::DateTime.new(*a)
+ #if a[0] >= 1970 then
+ # Time.gm(*a)
+ #else
+ # Date.new(*a[0,3])
+ #end
+ else
+ raise "wrong dateTime.iso8601 format"
+ 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 }
+
+ Thread.critical = true
+ # let initialize take 0 parameters
+ mod.module_eval %{
+ begin
+ alias __initialize initialize
+ rescue NameError
+ end
+ def initialize; end
+ }
+
+ obj = mod.new
+
+ # restore old initialize
+ mod.module_eval %{
+ undef initialize
+ begin
+ alias initialize __initialize
+ rescue NameError
+ end
+ }
+ Thread.critical = false
+
+ hash.delete "___class___"
+ hash.each {|k,v| obj.__set_instance_variable(k, v) }
+ 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 "To 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"
+ eval %{
+ class XMLRPCParser < ::XMLParser
+ include StreamParserMixin
+ end
+ }
+ @parser_class = XMLRPCParser
+ 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
+
+ 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_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]
+
+ end # module XMLParser
+
+
+end # module XMLRPC
+
diff --git a/lib/xmlrpc/server.rb b/lib/xmlrpc/server.rb
new file mode 100644
index 0000000000..f5d8059912
--- /dev/null
+++ b/lib/xmlrpc/server.rb
@@ -0,0 +1,839 @@
+=begin
+= xmlrpc/server.rb
+Copyright (C) 2001, 2002, 2003 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/httpserver"
+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 ENV['CONTENT_TYPE'] == "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")
+ }
+ 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
+ @@obj = nil
+
+ def ModRubyServer.new(*a)
+ @@obj = super(*a) if @@obj.nil?
+ @@obj
+ end
+
+ def initialize(*a)
+ @ap = Apache::request
+ super(*a)
+ end
+
+ def serve
+ catch(:exit_serve) {
+ header = {}
+ @ap.each_header {|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")
+ }
+ 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[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::BasicServer>))
+
+== 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>)).
+
+ The parameters ((|maxConnections|)), ((|stdlog|)), ((|audit|)) and ((|debug|)) are passed to the HTTP server and
+ specify it's behaviour more precise.
+
+ 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.
+
+--- XMLRPC::Server#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::Server#get_valid_ip
+ Return the via method ((<set_valid_ip|XMLRPC::Server#set_valid_ip>)) specified
+ valid IP addresses.
+
+=end
+
+class Server < BasicServer
+
+ def initialize(port=8080, host="127.0.0.1", maxConnections=4, stdlog=$stdout, audit=true, debug=true, *a)
+ super(*a)
+ @server = ::HttpServer.new(self, port, host, maxConnections, stdlog, audit, debug)
+ @valid_ip = nil
+ end
+
+ def serve
+ if RUBY_PLATFORM =~ /mingw|mswin32/
+ signal = 1
+ else
+ signal = "HUP"
+ end
+ trap(signal) { @server.shutdown }
+
+ @server.start.join
+ end
+
+ def shutdown
+ @server.shutdown
+ 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
+
+ # methods that get called by HttpServer ------------------------------------------
+
+ def request_handler(request, response)
+ $stderr.puts "in request_handler" if $DEBUG
+
+ if request.method != "POST"
+ # Method not allowed
+ response.status = 405
+ return
+ end
+
+ if parse_content_type(request.header['Content-type']).first != "text/xml"
+ # Bad request
+ response.status = 400
+ return
+ end
+
+ length = request.content_length || 0
+
+ unless length > 0
+ # Length required
+ response.status = 411
+ return
+ end
+
+ data = request.data.read(length)
+
+ if data.nil? or data.size != length
+ # Bad request
+ response.status = 400
+ return
+ end
+
+ resp = process(data)
+ raise if resp.nil? or resp.size <= 0 # => Internal Server Error
+
+ response.status = 200
+ response.header['Content-Length'] = resp.size
+ response.header['Content-Type'] = "text/xml"
+ response.body = resp
+
+ end
+
+ ##
+ # Is called before request_handler and should return true if
+ # the client is allowed to connect to the server.
+ # `io' is a Socket object.
+ def ip_auth_handler(io)
+ if @valid_ip
+ client_ip = io.peeraddr[3]
+ @valid_ip.each { |ip|
+ return true if client_ip =~ ip
+ }
+ false
+ else
+ true
+ end
+ 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
+== 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"
+ 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 service(request, response)
+ if request.request_method != "POST"
+ raise HTTPStatus::MethodNotAllowed,
+ "unsupported method `#{request.request_method}'."
+ end
+
+ if parse_content_type(request['Content-type']).first != "text/xml"
+ raise HTTPStatus::BadRequest
+ end
+
+ length = (request['Content-length'] || 0).to_i
+
+ raise HTTPStatus::LengthRequired unless length > 0
+
+ data = request.body
+
+ if data.nil? or data.size != length
+ raise HTTPStatus::BadRequest
+ end
+
+ resp = process(data)
+ if resp.nil? or resp.size <= 0
+ raise HTTPStatus::InternalServerError
+ end
+
+ response.status = 200
+ response['Content-Length'] = resp.size
+ response['Content-Type'] = "text/xml"
+ response.body = resp
+ end
+end
+
+
+end # module XMLRPC
+
+
+=begin
+= History
+ $Id$
+=end
+
diff --git a/lib/xmlrpc/utils.rb b/lib/xmlrpc/utils.rb
new file mode 100644
index 0000000000..675f23275b
--- /dev/null
+++ b/lib/xmlrpc/utils.rb
@@ -0,0 +1,172 @@
+#
+# 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$
+#
+
+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
+ def __get_instance_variables
+ instance_variables.collect {|var| [var[1..-1], eval(var)] }
+ end
+
+ def __set_instance_variable(key, value)
+ eval("@#$1 = value") if key =~ /^([\w_][\w_0-9]*)$/
+ end
+ 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.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/lib/yaml.rb b/lib/yaml.rb
new file mode 100644
index 0000000000..d33f2e202a
--- /dev/null
+++ b/lib/yaml.rb
@@ -0,0 +1,222 @@
+# vim:sw=4:ts=4
+# $Id$
+#
+# YAML.rb
+#
+# Loads the parser/loader and emitter/writer.
+#
+
+module YAML
+
+ begin
+ require 'yaml/syck'
+ @@parser = YAML::Syck::Parser
+ @@loader = YAML::Syck::DefaultLoader
+ @@emitter = YAML::Syck::Emitter
+ rescue LoadError
+ require 'yaml/parser'
+ @@parser = YAML::Parser
+ @@loader = YAML::DefaultLoader
+ require 'yaml/emitter'
+ @@emitter = YAML::Emitter
+ end
+ require 'yaml/loader'
+ require 'yaml/stream'
+
+ #
+ # Load a single document from the current stream
+ #
+ def YAML.dump( obj, io = nil )
+ io ||= ""
+ io << obj.to_yaml
+ io
+ end
+
+ #
+ # Load a single document from the current stream
+ #
+ def YAML.load( io )
+ yp = @@parser.new.load( io )
+ end
+
+ #
+ # Parse a single document from the current stream
+ #
+ def YAML.parse( io )
+ yp = @@parser.new( :Model => :Generic ).load( io )
+ end
+
+ #
+ # Load all documents from the current stream
+ #
+ def YAML.each_document( io, &doc_proc )
+ yp = @@parser.new.load_documents( io, &doc_proc )
+ end
+
+ #
+ # Identical to each_document
+ #
+ def YAML.load_documents( io, &doc_proc )
+ YAML.each_document( io, &doc_proc )
+ end
+
+ #
+ # Parse all documents from the current stream
+ #
+ def YAML.each_node( io, &doc_proc )
+ yp = @@parser.new( :Model => :Generic ).load_documents( io, &doc_proc )
+ end
+
+ #
+ # Parse all documents from the current stream
+ #
+ def YAML.parse_documents( io, &doc_proc )
+ YAML.each_node( io, &doc_proc )
+ end
+
+ #
+ # Load all documents from the current stream
+ #
+ def YAML.load_stream( io )
+ yp = @@parser.new
+ d = nil
+ yp.load_documents( io ) { |doc|
+ d = YAML::Stream.new( yp.options ) if not d
+ d.add( doc )
+ }
+ return d
+ end
+
+ #
+ # Add a transfer method to a domain
+ #
+ def YAML.add_domain_type( domain, type_re, &transfer_proc )
+ @@loader.add_domain_type( domain, type_re, &transfer_proc )
+ end
+
+ #
+ # Add a transfer method for a builtin type
+ #
+ def YAML.add_builtin_type( type_re, &transfer_proc )
+ @@loader.add_builtin_type( type_re, &transfer_proc )
+ end
+
+ #
+ # Add a transfer method for a builtin type
+ #
+ def YAML.add_ruby_type( type, &transfer_proc )
+ @@loader.add_ruby_type( type, &transfer_proc )
+ end
+
+ #
+ # Add a private document type
+ #
+ def YAML.add_private_type( type_re, &transfer_proc )
+ @@loader.add_private_type( type_re, &transfer_proc )
+ end
+
+ #
+ # Detect typing of a string
+ #
+ def YAML.detect_implicit( val )
+ @@loader.detect_implicit( val )
+ end
+
+ #
+ # Apply a transfer method to a Ruby object
+ #
+ def YAML.transfer( type_id, obj )
+ @@loader.transfer( 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, is_attr = false )
+ if Hash === val
+ name = obj_class.name
+ ostr = sprintf( "\004\006o:%c%s\000", name.length + 5, name )
+ if is_attr
+ ostr[ -1, 1 ] = Marshal.dump( val ).sub( /^[^{]+\{/, '' )
+ p ostr
+ end
+ o = ::Marshal.load( ostr )
+ unless is_attr
+ val.each_pair { |k,v|
+ o.instance_eval "@#{k} = v"
+ }
+ end
+ 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 )
+ old_opt = nil
+ if opts[:Emitter].is_a? @@emitter
+ out = opts.delete( :Emitter )
+ old_opt = out.options.dup
+ out.options.update( opts )
+ else
+ out = @@emitter.new( opts )
+ end
+ aidx = out.start_object( oid )
+ if aidx
+ out.simple( "*#{ aidx }" )
+ else
+ e.call( out )
+ end
+ if old_opt.is_a? Hash
+ out.options = old_opt
+ end
+ out.end_object
+ end
+
+end
+
+require 'yaml/rubytypes'
+require 'yaml/types'
+
+#
+# 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.
+#
+module Kernel
+ def y( x )
+ puts x.to_yaml
+ end
+end
+
+
diff --git a/lib/yaml/baseemitter.rb b/lib/yaml/baseemitter.rb
new file mode 100644
index 0000000000..007ee7be5e
--- /dev/null
+++ b/lib/yaml/baseemitter.rb
@@ -0,0 +1,241 @@
+#
+# 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 = '>' )
+ @seq_map = false
+ valx = value.dup
+ if options(:UseBlock)
+ block = '|'
+ elsif not options(:UseFold) and valx =~ /\n[ \t]/ and not valx =~ /#{YAML::ESCAPE_CHAR}/
+ block = '|'
+ end
+ str = block.dup
+ if valx =~ /\n\Z\n/
+ str << "+"
+ elsif valx =~ /\Z\n/
+ else
+ str << "-"
+ end
+ if valx =~ /#{YAML::ESCAPE_CHAR}/
+ valx = YAML::escape( valx )
+ end
+ if valx =~ /\A[ \t#]/
+ str << options(:Indent).to_s
+ end
+ if block == '>'
+ valx = fold( valx )
+ end
+ self << str + indent_text( valx ) + "\n"
+ 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 )
+ return "" if text.to_s.empty?
+ spacing = " " * ( level * options(:Indent) )
+ return "\n" + text.gsub( /^([^\n])/, "#{spacing}\\1" )
+ end
+
+ #
+ # Write a current indent
+ #
+ def indent
+ #p [ self.id, @level, :INDENT ]
+ return " " * ( level * options(:Indent) )
+ end
+
+ #
+ # Add indent to the buffer
+ #
+ def indent!
+ self << indent
+ end
+
+ #
+ # Folding paragraphs within a column
+ #
+ def fold( value )
+ value.gsub!( /\A\n+/, '' )
+ folded = $&.to_s
+ width = (0..options(:BestWidth))
+ while not value.empty?
+ last = value.index( /(\n+)/ )
+ chop_s = false
+ if width.include?( last )
+ last += $1.length - 1
+ elsif width.include?( value.length )
+ last = value.length
+ else
+ last = value.rindex( /[ \t]/, options(:BestWidth) )
+ chop_s = true
+ end
+ folded += value.slice!( 0, width.include?( last ) ? last + 1 : options(:BestWidth) )
+ folded.chop! if chop_s
+ folded += "\n" unless value.empty?
+ end
+ folded
+ 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/lib/yaml/basenode.rb b/lib/yaml/basenode.rb
new file mode 100644
index 0000000000..e88a76f3d2
--- /dev/null
+++ b/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
+ }
+ self.class.new( '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 and @value.has_key?( seg )
+ @value[seg][1]
+ 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], v[1][1]]
+ match_deep = v[1][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], h[1][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 []( *k )
+ if Hash === @value
+ v = @value.[]( *k )
+ v[1] if v
+ elsif Array === @value
+ @value.[]( *k )
+ 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/lib/yaml/constants.rb b/lib/yaml/constants.rb
new file mode 100644
index 0000000000..fb833d3077
--- /dev/null
+++ b/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/lib/yaml/dbm.rb b/lib/yaml/dbm.rb
new file mode 100644
index 0000000000..87d6009250
--- /dev/null
+++ b/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/lib/yaml/emitter.rb b/lib/yaml/emitter.rb
new file mode 100644
index 0000000000..a6be4a6733
--- /dev/null
+++ b/lib/yaml/emitter.rb
@@ -0,0 +1,107 @@
+#
+# Output classes and methods
+#
+
+require 'yaml/baseemitter'
+require 'yaml/encoding'
+
+module YAML
+
+ #
+ # Emit a set of values
+ #
+
+ class Emitter
+
+ include BaseEmitter
+
+ attr_accessor :options
+
+ def initialize( opts )
+ opts = {} if opts.class != Hash
+ @options = YAML::DEFAULTS.dup.update( opts )
+ @headless = 0
+ @seq_map = false
+ @anchors = {}
+ @anchor_extras = {}
+ @active_anchors = []
+ @level = -1
+ self.clear
+ end
+
+ def clear
+ @buffer = []
+ end
+
+ def level
+ @level
+ end
+
+ #
+ # Version string
+ #
+ def version_s
+ " %YAML:#{@options[:Version]}" if @options[:UseVersion]
+ end
+
+ #
+ # Header
+ #
+ def header
+ if @headless.nonzero?
+ ""
+ else
+ "---#{version_s} "
+ end
+ end
+
+ #
+ # Concatenate to the buffer
+ #
+ def <<( str )
+ #p [ self.id, @level, str ]
+ @buffer.last << str
+ end
+
+ #
+ # Monitor objects and allow references
+ #
+ def start_object( oid )
+ @level += 1
+ @buffer.push( "" )
+ #p [ self.id, @level, :OPEN ]
+ idx = nil
+ if oid
+ if @anchors.has_key?( oid )
+ idx = @active_anchors.index( oid )
+ unless idx
+ idx = @active_anchors.length
+ af_str = "&#{@options[:AnchorFormat]} " % [ idx + 1 ]
+ af_str += @anchor_extras[ @anchors[ oid ] ].to_s
+ @buffer[ @anchors[ oid ] ][0,0] = af_str
+ @headless = 0 if @anchors[ oid ].zero?
+ end
+ idx += 1
+ @active_anchors.push( oid )
+ else
+ @anchors[ oid ] = @buffer.length - 1
+ end
+ end
+ return idx
+ end
+
+ #
+ # Output method
+ #
+ def end_object
+ @level -= 1
+ @buffer.push( "" )
+ #p [ self.id, @level, :END ]
+ if @level < 0
+ header + @buffer.to_s[@headless..-1].to_s
+ end
+ end
+ end
+
+end
+
diff --git a/lib/yaml/encoding.rb b/lib/yaml/encoding.rb
new file mode 100644
index 0000000000..e361163ac6
--- /dev/null
+++ b/lib/yaml/encoding.rb
@@ -0,0 +1,29 @@
+#
+# Handle Unicode-to-Internal conversion
+#
+
+module YAML
+
+ #
+ # Escape the string, condensing common escapes
+ #
+ def YAML.escape( value )
+ value.gsub( /\\/, "\\\\\\" ).gsub( /"/, "\\\"" ).gsub( /([\x00-\x1f])/ ) { |x| ESCAPES[ x.unpack("C")[0] ] }
+ 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/lib/yaml/error.rb b/lib/yaml/error.rb
new file mode 100644
index 0000000000..a9df22749b
--- /dev/null
+++ b/lib/yaml/error.rb
@@ -0,0 +1,33 @@
+#
+# 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
+
+end
diff --git a/lib/yaml/loader.rb b/lib/yaml/loader.rb
new file mode 100644
index 0000000000..eb0709e103
--- /dev/null
+++ b/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/lib/yaml/rubytypes.rb b/lib/yaml/rubytypes.rb
new file mode 100644
index 0000000000..9f661b0b6c
--- /dev/null
+++ b/lib/yaml/rubytypes.rb
@@ -0,0 +1,583 @@
+require 'date'
+#
+# Type conversions
+#
+
+class Class
+ def to_yaml( opts = {} )
+ raise ArgumentError, "can't dump anonymous class %s" % self.class
+ end
+end
+
+class Object
+ def is_complex_yaml?
+ true
+ end
+ def to_yaml_type
+ "!ruby/object:#{self.class}"
+ end
+ def to_yaml_properties
+ instance_variables.sort
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( self.object_id, opts ) { |out|
+ out.map( self.to_yaml_type ) { |map|
+ to_yaml_properties.each { |m|
+ map.add( m[1..-1], instance_eval( m ) )
+ }
+ }
+ }
+ end
+end
+
+YAML.add_ruby_type( /^object/ ) { |type, val|
+ type, obj_class = YAML.read_type_class( type, Object )
+ YAML.object_maker( obj_class, val )
+}
+
+#
+# Maps: Hash#to_yaml
+#
+class Hash
+ def is_complex_yaml?
+ true
+ end
+ def to_yaml_type
+ if self.class == Hash or self.class == YAML::SpecialHash
+ "!map"
+ else
+ "!ruby/hash:#{self.class}"
+ end
+ end
+ def to_yaml( opts = {} )
+ opts[:DocType] = self.class if Hash === opts
+ YAML::quick_emit( self.object_id, opts ) { |out|
+ hash_type = to_yaml_type
+ if not out.options(:ExplicitTypes) and hash_type == "!map"
+ hash_type = ""
+ end
+ out.map( hash_type ) { |map|
+ #
+ # Sort the hash
+ #
+ if out.options(:SortKeys)
+ map.concat( self.sort )
+ else
+ map.concat( self.to_a )
+ end
+ }
+ }
+ end
+end
+
+hash_proc = Proc.new { |type, val|
+ if Array === val
+ val = Hash.[]( *val ) # Convert the map to a sequence
+ elsif Hash === val
+ type, obj_class = YAML.read_type_class( type, Hash )
+ if obj_class != Hash
+ o = obj_class.new
+ o.update( val )
+ val = o
+ end
+ else
+ raise YAML::Error, "Invalid map explicitly tagged !map: " + val.inspect
+ end
+ val
+}
+YAML.add_builtin_type( 'map', &hash_proc )
+YAML.add_ruby_type( /^hash/, &hash_proc )
+
+module YAML
+
+ #
+ # Ruby-specific collection: !ruby/flexhash
+ #
+ class FlexHash < Array
+ 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 ) { |out|
+ out.seq( "!ruby/flexhash" ) { |seq|
+ self.each { |v|
+ if v[1]
+ seq.add( Hash.[]( *v ) )
+ else
+ seq.add( v[0] )
+ end
+ }
+ }
+ }
+ end
+ end
+
+ YAML.add_ruby_type( 'flexhash' ) { |type, val|
+ if Array === val
+ p = FlexHash.new
+ val.each { |v|
+ if Hash === v
+ p.concat( v.to_a ) # Convert the map to a sequence
+ else
+ p << [ v, nil ]
+ end
+ }
+ p
+ else
+ raise YAML::Error, "Invalid !ruby/flexhash: " + val.inspect
+ end
+ }
+end
+
+#
+# Structs: export as a !map
+#
+class Struct
+ def is_complex_yaml?
+ true
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( self.object_id, opts ) { |out|
+ #
+ # Basic struct is passed as a YAML map
+ #
+ struct_name = self.class.name.gsub( "Struct:", "" )
+ out.map( "!ruby/struct#{struct_name}" ) { |map|
+ self.members.each { |m|
+ map.add( m, self[m] )
+ }
+ }
+ }
+ end
+end
+
+YAML.add_ruby_type( /^struct/ ) { |type, val|
+ if Hash === val
+ struct_type = nil
+
+ #
+ # Use existing Struct if it exists
+ #
+ begin
+ struct_name, struct_type = YAML.read_type_class( type, Struct )
+ rescue NameError
+ end
+ if not struct_type
+ struct_def = [ type.split( ':', 4 ).last ]
+ struct_type = Struct.new( *struct_def.concat( val.keys.collect { |k| k.intern } ) )
+ end
+
+ #
+ # Set the Struct properties
+ #
+ st = struct_type.new
+ st.members.each { |m|
+ st.send( "#{m}=", val[m] )
+ }
+ st
+ else
+ raise YAML::Error, "Invalid Ruby Struct: " + val.inspect
+ end
+}
+
+#
+# Sequences: Array#to_yaml
+#
+class Array
+ def is_complex_yaml?
+ true
+ end
+ def to_yaml_type
+ if self.class == Array
+ "!seq"
+ else
+ "!ruby/array:#{self.class}"
+ end
+ end
+ def to_yaml( opts = {} )
+ opts[:DocType] = self.class if Hash === opts
+ YAML::quick_emit( self.object_id, opts ) { |out|
+ array_type = to_yaml_type
+ if not out.options(:ExplicitTypes) and array_type == "!seq"
+ array_type = ""
+ end
+
+ out.seq( array_type ) { |seq|
+ seq.concat( self )
+ }
+ }
+ end
+end
+
+array_proc = Proc.new { |type, val|
+ if Array === val
+ type, obj_class = YAML.read_type_class( type, Array )
+ if obj_class != Array
+ o = obj_class.new
+ o.concat( val )
+ val = o
+ end
+ val
+ else
+ val.to_a
+ end
+}
+YAML.add_builtin_type( 'seq', &array_proc )
+YAML.add_ruby_type( /^array/, &array_proc )
+
+#
+# Exception#to_yaml
+#
+class Exception
+ def is_complex_yaml?
+ true
+ end
+ def to_yaml_type
+ "!ruby/exception:#{self.class}"
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( self.object_id, opts ) { |out|
+ out.map( self.to_yaml_type ) { |map|
+ map.add( 'message', self.message )
+ to_yaml_properties.each { |m|
+ map.add( m[1..-1], instance_eval( m ) )
+ }
+ }
+ }
+ end
+end
+
+YAML.add_ruby_type( /^exception/ ) { |type, val|
+ type, obj_class = YAML.read_type_class( type, Exception )
+ o = YAML.object_maker( obj_class, { 'mesg' => val.delete( 'message' ) }, true )
+ val.each_pair { |k,v|
+ o.instance_eval "@#{k} = v"
+ }
+ o
+}
+
+
+#
+# String#to_yaml
+#
+class String
+ def is_complex_yaml?
+ ( self =~ /\n.+/ ? true : false )
+ end
+ def is_binary_data?
+ ( self.count( "^ -~", "^\r\n" ) / self.size > 0.3 || self.count( "\x00" ) > 0 )
+ end
+ def to_yaml( opts = {} )
+ complex = false
+ if self.is_complex_yaml?
+ complex = true
+ elsif opts[:BestWidth].to_i > 0
+ if self.length > opts[:BestWidth] and opts[:UseFold]
+ complex = true
+ end
+ end
+ YAML::quick_emit( complex ? self.object_id : nil, opts ) { |out|
+ if complex
+ if self.is_binary_data?
+ out.binary_base64( self )
+ elsif self =~ /^ |#{YAML::ESCAPE_CHAR}| $/
+ complex = false
+ else
+ out.node_text( self )
+ end
+ end
+ if not complex
+ ostr = if out.options(:KeepValue)
+ self
+ elsif empty?
+ "''"
+ elsif self =~ /^[^#{YAML::WORD_CHAR}]|#{YAML::ESCAPE_CHAR}|[#{YAML::SPACE_INDICATORS}]( |$)| $|\n|\'/
+ "\"#{YAML.escape( self )}\""
+ elsif YAML.detect_implicit( self ) != 'str'
+ "\"#{YAML.escape( self )}\""
+ else
+ self
+ end
+ out.simple( ostr )
+ end
+ }
+ end
+end
+
+YAML.add_builtin_type( 'str' ) { |type,val| val.to_s }
+YAML.add_builtin_type( 'binary' ) { |type,val|
+ enctype = "m"
+ if String === val
+ val.gsub( /\s+/, '' ).unpack( enctype )[0]
+ else
+ raise YAML::Error, "Binary data must be represented by a string: " + val.inspect
+ end
+}
+
+#
+# Symbol#to_yaml
+#
+class Symbol
+ def is_complex_yaml?
+ false
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( nil, opts ) { |out|
+ out << "!ruby/sym "
+ self.id2name.to_yaml( :Emitter => out )
+ }
+ end
+end
+
+symbol_proc = Proc.new { |type, val|
+ if String === val
+ val.intern
+ else
+ raise YAML::Error, "Invalid Symbol: " + val.inspect
+ end
+}
+YAML.add_ruby_type( 'symbol', &symbol_proc )
+YAML.add_ruby_type( 'sym', &symbol_proc )
+
+#
+# Range#to_yaml
+#
+class Range
+ def is_complex_yaml?
+ false
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( nil, opts ) { |out|
+ out << "!ruby/range "
+ self.inspect.to_yaml( :Emitter => out )
+ }
+ end
+end
+
+YAML.add_ruby_type( 'range' ) { |type, val|
+ if String === val and val =~ /^(.*[^.])(\.{2,3})([^.].*)$/
+ r1, rdots, r2 = $1, $2, $3
+ Range.new( YAML.try_implicit( r1 ), YAML.try_implicit( r2 ), rdots.length == 3 )
+ elsif Hash === val
+ Range.new( val['begin'], val['end'], val['exclude_end?'] )
+ else
+ raise YAML::Error, "Invalid Range: " + val.inspect
+ end
+}
+
+#
+# Make an RegExp
+#
+class Regexp
+ def is_complex_yaml?
+ false
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( nil, opts ) { |out|
+ out << "!ruby/regexp "
+ self.inspect.to_yaml( :Emitter => out )
+ }
+ end
+end
+
+regexp_proc = Proc.new { |type, val|
+ if String === val and val =~ /^\/(.*)\/([mix]*)$/
+ val = { 'REGEXP' => $1, 'MODIFIERS' => $2 }
+ end
+ if Hash === val
+ mods = nil
+ unless val['MODIFIERS'].to_s.empty?
+ mods = 0x00
+ if val['MODIFIERS'].include?( 'x' )
+ mods |= Regexp::EXTENDED
+ elsif val['MODIFIERS'].include?( 'i' )
+ mods |= Regexp::IGNORECASE
+ elsif val['MODIFIERS'].include?( 'm' )
+ mods |= Regexp::POSIXLINE
+ end
+ end
+ Regexp::compile( val['REGEXP'], mods )
+ else
+ raise YAML::Error, "Invalid Regular expression: " + val.inspect
+ end
+}
+YAML.add_domain_type( "perl.yaml.org,2002", /^regexp/, &regexp_proc )
+YAML.add_ruby_type( 'regexp', &regexp_proc )
+
+#
+# Emit a Time object as an ISO 8601 timestamp
+#
+class Time
+ def is_complex_yaml?
+ false
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( nil, opts ) { |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
+ ( self.strftime( "%Y-%m-%d %H:%M:%S." ) +
+ "%06d %s" % [usec, tz] ).
+ to_yaml( :Emitter => out, :KeepValue => true )
+ }
+ end
+end
+
+YAML.add_builtin_type( 'time#ymd' ) { |type, val|
+ if val =~ /\A(\d{4})\-(\d{1,2})\-(\d{1,2})\Z/
+ Date.new($1.to_i, $2.to_i, $3.to_i)
+ else
+ raise YAML::TypeError, "Invalid !time string: " + val.inspect
+ end
+}
+
+#
+# Emit a Date object as a simple implicit
+#
+class Date
+ def is_complex_yaml?
+ false
+ end
+ def to_yaml( opts = {} )
+ opts[:KeepValue] = true
+ self.to_s.to_yaml( opts )
+ end
+end
+
+#
+# Send Integer, Booleans, NilClass to String
+#
+class Numeric
+ def is_complex_yaml?
+ false
+ end
+ def to_yaml( opts = {} )
+ str = self.to_s
+ if str == "Infinity"
+ str = ".Inf"
+ elsif str == "-Infinity"
+ str = "-.Inf"
+ elsif str == "NaN"
+ str = ".NaN"
+ end
+ opts[:KeepValue] = true
+ str.to_yaml( opts )
+ end
+end
+
+YAML.add_builtin_type( 'float' ) { |type, val|
+ if val =~ /\A[-+]?[\d][\d,]*\.[\d,]*[eE][-+][0-9]+\Z/ # Float (exponential)
+ $&.tr( ',', '' ).to_f
+ elsif val =~ /\A[-+]?[\d][\d,]*\.[\d,]*\Z/ # Float (fixed)
+ $&.tr( ',', '' ).to_f
+ elsif val =~ /\A([-+]?)\.(inf|Inf|INF)\Z/ # Float (english)
+ ( $1 == "-" ? -1.0/0.0 : 1.0/0.0 )
+ elsif val =~ /\A\.(nan|NaN|NAN)\Z/
+ 0.0/0.0
+ elsif type == :Implicit
+ :InvalidType
+ else
+ val.to_f
+ end
+}
+
+YAML.add_builtin_type( 'int' ) { |type, val|
+ if val =~ /\A[-+]?0[0-7,]+\Z/ # Integer (octal)
+ $&.oct
+ elsif val =~ /\A[-+]?0x[0-9a-fA-F,]+\Z/ # Integer (hex)
+ $&.hex
+ elsif val =~ /\A[-+]?\d[\d,]*\Z/ # Integer (canonical)
+ $&.tr( ',', '' ).to_i
+ elsif val =~ /\A([-+]?)(\d[\d,]*(?::[0-5]?[0-9])+)\Z/
+ sign = ( $1 == '-' ? -1 : 1 )
+ digits = $2.split( /:/ ).collect { |x| x.to_i }
+ val = 0; digits.each { |x| val = ( val * 60 ) + x }; val *= sign
+ elsif type == :Implicit
+ :InvalidType
+ else
+ val.to_i
+ end
+}
+
+class TrueClass
+ def is_complex_yaml?
+ false
+ end
+ def to_yaml( opts = {} )
+ opts[:KeepValue] = true
+ "true".to_yaml( opts )
+ end
+end
+
+class FalseClass
+ def is_complex_yaml?
+ false
+ end
+ def to_yaml( opts = {} )
+ opts[:KeepValue] = true
+ "false".to_yaml( opts )
+ end
+end
+
+YAML.add_builtin_type( 'bool' ) { |type, val|
+ if val =~ /\A(\+|true|True|TRUE|yes|Yes|YES|on|On|ON)\Z/
+ true
+ elsif val =~ /\A(\-|false|False|FALSE|no|No|NO|off|Off|OFF)\Z/
+ false
+ elsif type == :Implicit
+ :InvalidType
+ else
+ raise YAML::TypeError, "Invalid !bool string: " + val.inspect
+ end
+}
+
+class NilClass
+ def is_complex_yaml?
+ false
+ end
+ def to_yaml( opts = {} )
+ opts[:KeepValue] = true
+ "".to_yaml( opts )
+ end
+end
+
+YAML.add_builtin_type( 'null' ) { |type, val|
+ if val =~ /\A(\~|null|Null|NULL)\Z/
+ nil
+ elsif val.empty?
+ nil
+ elsif type == :Implicit
+ :InvalidType
+ else
+ raise YAML::TypeError, "Invalid !null string: " + val.inspect
+ end
+}
+
diff --git a/lib/yaml/store.rb b/lib/yaml/store.rb
new file mode 100644
index 0000000000..b2924b0660
--- /dev/null
+++ b/lib/yaml/store.rb
@@ -0,0 +1,75 @@
+#
+# YAML::Store
+#
+require 'yaml'
+require 'pstore'
+
+module YAML
+
+ class Store < PStore
+ #
+ # Constructor
+ #
+ def initialize( *o )
+ @opt = YAML::DEFAULTS.dup
+ if String === o.first
+ super(o.pop)
+ end
+ if o.last.is_a? Hash
+ @opt.update(o.pop)
+ end
+ end
+
+ #
+ # Override Pstore#transaction
+ #
+ def transaction
+ raise YAML::Error, "nested transaction" if @transaction
+ raise YAML::Error, "no filename for transaction" unless @filename
+ begin
+ @transaction = true
+ value = nil
+ backup = @filename+"~"
+ if File::exist?(@filename)
+ file = File::open(@filename, "rb+")
+ orig = true
+ else
+ @table = {}
+ file = File::open(@filename, "wb+")
+ file.write( @table.to_yaml( @opt ) )
+ end
+ file.flock(File::LOCK_EX)
+ if orig
+ File::copy @filename, backup
+ @table = YAML::load( file )
+ end
+ begin
+ catch(:pstore_abort_transaction) do
+ value = yield(self)
+ end
+ rescue Exception
+ @abort = true
+ raise
+ ensure
+ unless @abort
+ begin
+ file.rewind
+ file.write( @table.to_yaml( @opt ) )
+ file.truncate(file.pos)
+ rescue
+ File::rename backup, @filename if File::exist?(backup)
+ raise
+ end
+ end
+ @abort = false
+ end
+ ensure
+ @table = nil
+ @transaction = false
+ file.close if file
+ end
+ value
+ end
+ end
+
+end
diff --git a/lib/yaml/stream.rb b/lib/yaml/stream.rb
new file mode 100644
index 0000000000..943c51526b
--- /dev/null
+++ b/lib/yaml/stream.rb
@@ -0,0 +1,44 @@
+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
+ opts = @options.dup
+ opts[:UseHeader] = true if @documents.length > 1
+ ct = 0
+ out = YAML::Syck::Emitter.new( opts )
+ @documents.each { |v|
+ if ct > 0
+ out << "\n--- "
+ end
+ v.to_yaml( :Emitter => out )
+ ct += 1
+ }
+ out.end_object
+ end
+
+ end
+
+end
diff --git a/lib/yaml/stringio.rb b/lib/yaml/stringio.rb
new file mode 100644
index 0000000000..8ad949fa2b
--- /dev/null
+++ b/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/lib/yaml/syck.rb b/lib/yaml/syck.rb
new file mode 100644
index 0000000000..267067feb5
--- /dev/null
+++ b/lib/yaml/syck.rb
@@ -0,0 +1,27 @@
+#
+# YAML::Syck module
+# .. glues syck and yaml.rb together ..
+#
+require 'syck'
+require 'yaml/basenode'
+require 'yaml/baseemitter'
+
+module YAML
+ module Syck
+
+ #
+ # Mixin BaseNode functionality
+ #
+ class Node
+ include YAML::BaseNode
+ end
+
+ #
+ # Mixin BaseEmitter functionality
+ #
+ class Emitter
+ include YAML::BaseEmitter
+ end
+
+ end
+end
diff --git a/lib/yaml/types.rb b/lib/yaml/types.rb
new file mode 100644
index 0000000000..f7772cb3a0
--- /dev/null
+++ b/lib/yaml/types.rb
@@ -0,0 +1,196 @@
+#
+# Classes required by the full core typeset
+#
+
+module YAML
+
+ #
+ # Default private type
+ #
+ class PrivateType
+ attr_accessor :type_id, :value
+ def initialize( type, val )
+ @type_id = type; @value = val
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( self.object_id, opts ) { |out|
+ out << " !!#{@type_id}"
+ value.to_yaml( :Emitter => out )
+ }
+ end
+ end
+
+ #
+ # Default domain type
+ #
+ class DomainType
+ attr_accessor :domain, :type_id, :value
+ def initialize( domain, type, val )
+ @domain = domain; @type_id = type; @value = val
+ end
+ def to_yaml_type
+ dom = @domain.dup
+ if dom =~ /\.yaml\.org,2002$/
+ dom = $`
+ end
+ "#{dom}/#{@type_id}"
+ end
+ def to_yaml( opts = {} )
+ YAML::quick_emit( self.object_id, opts ) { |out|
+ out << " !#{to_yaml_type} "
+ value.to_yaml( :Emitter => out )
+ }
+ end
+ end
+
+ #
+ # YAML Hash class to support comments and defaults
+ #
+ class SpecialHash < Object::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
+ def self.[]( *vals )
+ o = Omap.new
+ 0.step( vals.length - 1, 2 ) { |i|
+ o[vals[i]] = vals[i+1]
+ }
+ 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 ) { |out|
+ out.seq( "!omap" ) { |seq|
+ self.each { |v|
+ seq.add( Hash[ *v ] )
+ }
+ }
+ }
+ end
+ end
+
+ YAML.add_builtin_type( "omap" ) { |type, val|
+ if Array === val
+ p = Omap.new
+ val.each { |v|
+ if Hash === v
+ p.concat( v.to_a ) # Convert the map to a sequence
+ else
+ raise YAML::Error, "Invalid !omap entry: " + val.inspect
+ end
+ }
+ else
+ raise YAML::Error, "Invalid !omap: " + val.inspect
+ end
+ p
+ }
+
+ #
+ # Builtin collection: !pairs
+ #
+ class Pairs < Array
+ 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 ) { |out|
+ out.seq( "!pairs" ) { |seq|
+ self.each { |v|
+ seq.add( Hash[ *v ] )
+ }
+ }
+ }
+ end
+ end
+
+ YAML.add_builtin_type( "pairs" ) { |type, val|
+ if Array === val
+ p = Pairs.new
+ val.each { |v|
+ if Hash === v
+ p.concat( v.to_a ) # Convert the map to a sequence
+ else
+ raise YAML::Error, "Invalid !pairs entry: " + val.inspect
+ end
+ }
+ else
+ raise YAML::Error, "Invalid !pairs: " + val.inspect
+ end
+ p
+ }
+
+ #
+ # Builtin collection: !set
+ #
+ class Set < Hash
+ def to_yaml_type
+ "!set"
+ end
+ end
+
+ YAML.add_builtin_type( 'set' ) { |type, val|
+ if Array === val
+ val = Set[ *val ]
+ elsif Hash === val
+ Set[ val ]
+ else
+ raise YAML::Error, "Invalid map explicitly tagged !map: " + val.inspect
+ end
+ val
+ }
+
+end
diff --git a/lib/yaml/yamlnode.rb b/lib/yaml/yamlnode.rb
new file mode 100644
index 0000000000..e36a18e694
--- /dev/null
+++ b/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/lib/yaml/ypath.rb b/lib/yaml/ypath.rb
new file mode 100644
index 0000000000..81348ca043
--- /dev/null
+++ b/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
diff --git a/main.c b/main.c
index 7dd981e99f..02d8d1458e 100644
--- a/main.c
+++ b/main.c
@@ -6,7 +6,7 @@
$Date$
created at: Fri Aug 19 13:19:58 JST 1994
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
**********************************************************************/
diff --git a/marshal.c b/marshal.c
index 5d34b9e4c8..c5f68bd1e8 100644
--- a/marshal.c
+++ b/marshal.c
@@ -6,17 +6,20 @@
$Date$
created at: Thu Apr 27 16:30:01 JST 1995
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
**********************************************************************/
-#include <math.h>
-
#include "ruby.h"
#include "rubyio.h"
#include "st.h"
#include "util.h"
+#include <math.h>
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+
#define BITSPERSHORT (2*CHAR_BIT)
#define SHORTMASK ((1<<BITSPERSHORT)-1)
#define SHORTDN(x) RSHIFT(x,BITSPERSHORT)
@@ -55,7 +58,7 @@ shortlen(len, ds)
#define TYPE_OBJECT 'o'
#define TYPE_DATA 'd'
#define TYPE_USERDEF 'u'
-#define TYPE_USRMARHAL 'U'
+#define TYPE_USRMARSHAL 'U'
#define TYPE_FLOAT 'f'
#define TYPE_BIGNUM 'l'
#define TYPE_STRING '"'
@@ -74,13 +77,12 @@ shortlen(len, ds)
#define TYPE_IVAR 'I'
#define TYPE_LINK '@'
-static ID s_dump, s_load;
+static ID s_dump, s_load, s_mdump, s_mload;
static ID s_dump_data, s_load_data, s_alloc;
-static ID s_getc, s_read, s_write;
+static ID s_getc, s_read, s_write, s_binmode;
struct dump_arg {
VALUE obj;
- FILE *fp;
VALUE str, dest;
st_table *symbol;
st_table *data;
@@ -96,22 +98,17 @@ struct dump_call_arg {
static void w_long _((long, struct dump_arg*));
static void
-w_byten(s, n, arg)
+w_nbyte(s, n, arg)
char *s;
int n;
struct dump_arg *arg;
{
- if (arg->fp) {
- fwrite(s, 1, n, arg->fp);
- }
- else {
- VALUE buf = arg->str;
- rb_str_buf_cat(buf, s, n);
- if (arg->dest && RSTRING(buf)->len >= BUFSIZ) {
- if (arg->taint) OBJ_TAINT(buf);
- rb_io_write(arg->dest, buf);
- rb_str_resize(buf, 0);
- }
+ VALUE buf = arg->str;
+ rb_str_buf_cat(buf, s, n);
+ if (arg->dest && RSTRING(buf)->len >= BUFSIZ) {
+ if (arg->taint) OBJ_TAINT(buf);
+ rb_io_write(arg->dest, buf);
+ rb_str_resize(buf, 0);
}
}
@@ -120,7 +117,7 @@ w_byte(c, arg)
char c;
struct dump_arg *arg;
{
- w_byten(&c, 1, arg);
+ w_nbyte(&c, 1, arg);
}
static void
@@ -130,7 +127,7 @@ w_bytes(s, n, arg)
struct dump_arg *arg;
{
w_long(n, arg);
- w_byten(s, n, arg);
+ w_nbyte(s, n, arg);
}
static void
@@ -187,6 +184,94 @@ w_long(x, arg)
}
}
+#ifdef DBL_MANT_DIG
+#define DECIMAL_MANT (53-16) /* from IEEE754 double precision */
+
+#if DBL_MANT_DIG > 32
+#define MANT_BITS 32
+#elif DBL_MANT_DIG > 24
+#define MANT_BITS 24
+#elif DBL_MANT_DIG > 16
+#define MANT_BITS 16
+#else
+#define MANT_BITS 8
+#endif
+
+static int
+save_mantissa(d, buf)
+ double d;
+ char *buf;
+{
+ int e, i = 0;
+ unsigned long m;
+ double n;
+
+ d = modf(ldexp(frexp(fabs(d), &e), DECIMAL_MANT), &d);
+ if (d > 0) {
+ buf[i++] = 0;
+ do {
+ d = modf(ldexp(d, MANT_BITS), &n);
+ m = (unsigned long)n;
+#if MANT_BITS > 24
+ buf[i++] = m >> 24;
+#endif
+#if MANT_BITS > 16
+ buf[i++] = m >> 16;
+#endif
+#if MANT_BITS > 8
+ buf[i++] = m >> 8;
+#endif
+ buf[i++] = m;
+ } while (d > 0);
+ while (!buf[i - 1]) --i;
+ }
+ return i;
+}
+
+static double
+load_mantissa(d, buf, len)
+ double d;
+ const char *buf;
+ int len;
+{
+ if (--len > 0 && !*buf++) { /* binary mantissa mark */
+ int e, s = d < 0, dig = 0;
+ unsigned long m;
+
+ modf(ldexp(frexp(fabs(d), &e), DECIMAL_MANT), &d);
+ do {
+ m = 0;
+ switch (len) {
+ default: m = *buf++ & 0xff;
+#if MANT_BITS > 24
+ case 3: m = (m << 8) | (*buf++ & 0xff);
+#endif
+#if MANT_BITS > 16
+ case 2: m = (m << 8) | (*buf++ & 0xff);
+#endif
+#if MANT_BITS > 8
+ case 1: m = (m << 8) | (*buf++ & 0xff);
+#endif
+ }
+ dig -= len < MANT_BITS / 8 ? 8 * (unsigned)len : MANT_BITS;
+ d += ldexp((double)m, dig);
+ } while ((len -= MANT_BITS / 8) > 0);
+ d = ldexp(d, e - DECIMAL_MANT);
+ if (s) d = -d;
+ }
+ return d;
+}
+#else
+#define load_mantissa(d, buf, len) (d)
+#define save_mantissa(d, buf) 0
+#endif
+
+#ifdef DBL_DIG
+#define FLOAT_DIG (DBL_DIG+2)
+#else
+#define FLOAT_DIG 17
+#endif
+
static void
w_float(d, arg)
double d;
@@ -206,8 +291,13 @@ w_float(d, arg)
else strcpy(buf, "0");
}
else {
+ int len;
+
/* xxx: should not use system's sprintf(3) */
- sprintf(buf, "%.16g", d);
+ sprintf(buf, "%.*g", FLOAT_DIG, d);
+ len = strlen(buf);
+ w_bytes(buf, len + save_mantissa(d, buf + len), arg);
+ return;
}
w_bytes(buf, strlen(buf), arg);
}
@@ -237,7 +327,7 @@ w_unique(s, arg)
struct dump_arg *arg;
{
if (s[0] == '#') {
- rb_raise(rb_eArgError, "can't dump anonymous class %s", s);
+ rb_raise(rb_eTypeError, "can't dump anonymous class %s", s);
}
w_symbol(rb_intern(s), arg);
}
@@ -312,7 +402,7 @@ w_uclass(obj, base_klass, arg)
w_extended(klass, arg);
if (klass != base_klass) {
w_byte(TYPE_UCLASS, arg);
- w_unique(rb_class2name(CLASS_OF(obj)), arg);
+ w_unique(rb_obj_classname(obj), arg);
}
}
@@ -323,7 +413,7 @@ w_ivar(tbl, arg)
{
if (tbl) {
w_long(tbl->num_entries, arg->arg);
- st_foreach(tbl, obj_each, arg);
+ st_foreach(tbl, obj_each, (st_data_t)arg);
}
else {
w_long(0, arg->arg);
@@ -390,14 +480,23 @@ w_object(obj, arg, limit)
}
st_add_direct(arg->data, obj, arg->data->num_entries);
+ if (rb_respond_to(obj, s_mdump)) {
+ VALUE v;
+
+ v = rb_funcall(obj, s_mdump, 0, 0);
+ w_class(TYPE_USRMARSHAL, obj, arg);
+ w_object(v, arg, limit);
+ if (ivtbl) w_ivar(ivtbl, &c_arg);
+ return;
+ }
if (rb_respond_to(obj, s_dump)) {
VALUE v;
- w_class(TYPE_USERDEF, obj, arg);
v = rb_funcall(obj, s_dump, 1, INT2NUM(limit));
if (TYPE(v) != T_STRING) {
- rb_raise(rb_eTypeError, "_dump() must return String");
+ rb_raise(rb_eTypeError, "_dump() must return string");
}
+ w_class(TYPE_USERDEF, obj, arg);
w_bytes(RSTRING(v)->ptr, RSTRING(v)->len, arg);
if (ivtbl) w_ivar(ivtbl, &c_arg);
return;
@@ -412,7 +511,7 @@ w_object(obj, arg, limit)
{
VALUE path = rb_class_path(obj);
if (RSTRING(path)->ptr[0] == '#') {
- rb_raise(rb_eArgError, "can't dump anonymous class %s",
+ rb_raise(rb_eTypeError, "can't dump anonymous class %s",
RSTRING(path)->ptr);
}
w_bytes(RSTRING(path)->ptr, RSTRING(path)->len, arg);
@@ -424,7 +523,7 @@ w_object(obj, arg, limit)
{
VALUE path = rb_class_path(obj);
if (RSTRING(path)->ptr[0] == '#') {
- rb_raise(rb_eArgError, "can't dump anonymous module %s",
+ rb_raise(rb_eTypeError, "can't dump anonymous module %s",
RSTRING(path)->ptr);
}
w_bytes(RSTRING(path)->ptr, RSTRING(path)->len, arg);
@@ -498,26 +597,25 @@ w_object(obj, arg, limit)
}
else if (FL_TEST(obj, FL_USER2)) {
/* FL_USER2 means HASH_PROC_DEFAULT (see hash.c) */
- rb_raise(rb_eArgError, "cannot dump hash with default proc");
+ rb_raise(rb_eTypeError, "cannot dump hash with default proc");
}
else {
w_byte(TYPE_HASH_DEF, arg);
}
w_long(RHASH(obj)->tbl->num_entries, arg);
- st_foreach(RHASH(obj)->tbl, hash_each, &c_arg);
+ st_foreach(RHASH(obj)->tbl, hash_each, (st_data_t)&c_arg);
if (!NIL_P(RHASH(obj)->ifnone)) {
w_object(RHASH(obj)->ifnone, arg, limit);
}
break;
case T_STRUCT:
- w_byte(TYPE_STRUCT, arg);
+ w_class(TYPE_STRUCT, obj, arg);
{
long len = RSTRUCT(obj)->len;
VALUE mem;
long i;
- w_unique(rb_class2name(CLASS_OF(obj)), arg);
w_long(len, arg);
mem = rb_struct_iv_get(rb_obj_class(obj), "__member__");
if (mem == Qnil) {
@@ -535,24 +633,24 @@ w_object(obj, arg, limit)
w_ivar(ROBJECT(obj)->iv_tbl, &c_arg);
break;
- case T_DATA:
- {
- VALUE v;
-
- w_class(TYPE_DATA, obj, arg);
- if (!rb_respond_to(obj, s_dump_data)) {
- rb_raise(rb_eTypeError,
- "class %s needs to have instance method `_dump_data'",
- rb_class2name(CLASS_OF(obj)));
- }
- v = rb_funcall(obj, s_dump_data, 0);
- w_object(v, arg, limit);
- }
- break;
+ case T_DATA:
+ {
+ VALUE v;
+
+ w_class(TYPE_DATA, obj, arg);
+ if (!rb_respond_to(obj, s_dump_data)) {
+ rb_raise(rb_eTypeError,
+ "class %s needs to have instance method `_dump_data'",
+ rb_obj_classname(obj));
+ }
+ v = rb_funcall(obj, s_dump_data, 0);
+ w_object(v, arg, limit);
+ }
+ break;
default:
rb_raise(rb_eTypeError, "can't dump %s",
- rb_class2name(CLASS_OF(obj)));
+ rb_obj_classname(obj));
break;
}
}
@@ -579,7 +677,7 @@ dump_ensure(arg)
{
st_free_table(arg->symbol);
st_free_table(arg->data);
- if (!arg->fp && arg->taint) {
+ if (arg->taint) {
OBJ_TAINT(arg->str);
}
return 0;
@@ -595,37 +693,31 @@ marshal_dump(argc, argv)
struct dump_arg arg;
struct dump_call_arg c_arg;
- port = 0;
+ port = Qnil;
rb_scan_args(argc, argv, "12", &obj, &a1, &a2);
if (argc == 3) {
if (!NIL_P(a2)) limit = NUM2INT(a2);
+ if (NIL_P(a1)) goto type_error;
port = a1;
}
else if (argc == 2) {
if (FIXNUM_P(a1)) limit = FIX2INT(a1);
+ else if (NIL_P(a1)) goto type_error;
else port = a1;
}
arg.dest = 0;
- if (port) {
- if (rb_obj_is_kind_of(port, rb_cIO)) {
- OpenFile *fptr;
-
- rb_io_binmode(port);
- GetOpenFile(port, fptr);
- rb_io_check_writable(fptr);
- arg.fp = (fptr->f2) ? fptr->f2 : fptr->f;
- }
- else if (rb_respond_to(port, s_write)) {
- arg.fp = 0;
- arg.str = rb_str_buf_new(0);
- arg.dest = port;
- }
- else {
+ if (!NIL_P(port)) {
+ if (!rb_respond_to(port, s_write)) {
+ type_error:
rb_raise(rb_eTypeError, "instance of IO needed");
}
+ arg.str = rb_str_buf_new(0);
+ arg.dest = port;
+ if (rb_respond_to(port, s_binmode)) {
+ rb_funcall2(port, s_binmode, 0, 0);
+ }
}
else {
- arg.fp = 0;
port = rb_str_buf_new(0);
arg.str = port;
}
@@ -646,7 +738,6 @@ marshal_dump(argc, argv)
}
struct load_arg {
- FILE *fp;
char *ptr, *end;
st_table *symbol;
VALUE data;
@@ -662,11 +753,7 @@ r_byte(arg)
{
int c;
- if (arg->fp) {
- c = rb_getc(arg->fp);
- if (c == EOF) rb_eof_error();
- }
- else if (!arg->end) {
+ if (!arg->end) {
VALUE src = (VALUE)arg->ptr;
VALUE v = rb_funcall2(src, s_getc, 0, 0);
if (NIL_P(v)) rb_eof_error();
@@ -740,25 +827,19 @@ r_bytes0(len, arg)
{
VALUE str;
- if (arg->fp) {
- str = rb_str_new(0, len);
- if (rb_io_fread(RSTRING(str)->ptr, len, arg->fp) != len) {
- too_short:
- rb_raise(rb_eArgError, "marshal data too short");
- }
- }
- else if (!arg->end) {
+ if (!arg->end) {
VALUE src = (VALUE)arg->ptr;
VALUE n = LONG2NUM(len);
str = rb_funcall2(src, s_read, 1, &n);
if (NIL_P(str)) goto too_short;
- Check_Type(str, T_STRING);
+ StringValue(str);
if (RSTRING(str)->len != len) goto too_short;
if (OBJ_TAINTED(str)) arg->taint = Qtrue;
}
else {
if (arg->ptr + len > arg->end) {
- goto too_short;
+ too_short:
+ rb_raise(rb_eArgError, "marshal data too short");
}
str = rb_str_new(arg->ptr, len);
arg->ptr += len;
@@ -776,7 +857,7 @@ r_symlink(arg)
if (st_lookup(arg->symbol, num, &id)) {
return id;
}
- rb_raise(rb_eTypeError, "bad symbol");
+ rb_raise(rb_eArgError, "bad symbol");
}
static ID
@@ -849,7 +930,7 @@ path2class(path)
VALUE v = rb_path2class(path);
if (TYPE(v) != T_CLASS) {
- rb_raise(rb_eTypeError, "%s does not refer class", path);
+ rb_raise(rb_eArgError, "%s does not refer class", path);
}
return v;
}
@@ -861,7 +942,7 @@ path2module(path)
VALUE v = rb_path2class(path);
if (TYPE(v) != T_MODULE) {
- rb_raise(rb_eTypeError, "%s does not refer module", path);
+ rb_raise(rb_eArgError, "%s does not refer module", path);
}
return v;
}
@@ -942,18 +1023,21 @@ r_object0(arg, proc)
{
double d, t = 0.0;
VALUE str = r_bytes(arg);
+ const char *ptr = RSTRING(str)->ptr;
- if (strcmp(RSTRING(str)->ptr, "nan") == 0) {
+ if (strcmp(ptr, "nan") == 0) {
d = t / t;
}
- else if (strcmp(RSTRING(str)->ptr, "inf") == 0) {
+ else if (strcmp(ptr, "inf") == 0) {
d = 1.0 / t;
}
- else if (strcmp(RSTRING(str)->ptr, "-inf") == 0) {
+ else if (strcmp(ptr, "-inf") == 0) {
d = -1.0 / t;
}
else {
- d = strtod(RSTRING(str)->ptr, 0);
+ char *e;
+ d = strtod(ptr, &e);
+ d = load_mantissa(d, e, RSTRING(str)->len - (e - ptr));
}
v = rb_float_new(d);
r_regist(v, arg);
@@ -1094,6 +1178,20 @@ r_object0(arg, proc)
}
break;
+ case TYPE_USRMARSHAL:
+ {
+ VALUE klass = path2class(r_unique(arg));
+
+ v = rb_obj_alloc(klass);
+ if (!rb_respond_to(v, s_mload)) {
+ rb_raise(rb_eTypeError, "instance of %s needs to have method `marshal_load'",
+ rb_class2name(klass));
+ }
+ r_regist(v, arg);
+ rb_funcall(v, s_mload, 1, r_object(arg));
+ }
+ break;
+
case TYPE_OBJECT:
{
VALUE klass = path2class(r_unique(arg));
@@ -1136,16 +1234,16 @@ r_object0(arg, proc)
case TYPE_MODULE_OLD:
{
- VALUE str = r_bytes(arg);
+ volatile VALUE str = r_bytes(arg);
- v = path2module(RSTRING(str)->ptr);
+ v = rb_path2class(RSTRING(str)->ptr);
r_regist(v, arg);
}
break;
case TYPE_CLASS:
{
- VALUE str = r_bytes(arg);
+ volatile VALUE str = r_bytes(arg);
v = path2class(RSTRING(str)->ptr);
r_regist(v, arg);
@@ -1154,7 +1252,7 @@ r_object0(arg, proc)
case TYPE_MODULE:
{
- VALUE str = r_bytes(arg);
+ volatile VALUE str = r_bytes(arg);
v = path2module(RSTRING(str)->ptr);
r_regist(v, arg);
@@ -1173,7 +1271,7 @@ r_object0(arg, proc)
break;
}
if (proc) {
- rb_funcall(proc, rb_intern("yield"), 1, v);
+ rb_funcall(proc, rb_intern("call"), 1, v);
}
return v;
}
@@ -1208,28 +1306,20 @@ marshal_load(argc, argv)
VALUE port, proc;
int major, minor;
VALUE v;
- OpenFile *fptr;
struct load_arg arg;
- volatile VALUE hash; /* protect from GC */
rb_scan_args(argc, argv, "11", &port, &proc);
- if (rb_obj_is_kind_of(port, rb_cIO)) {
- rb_io_binmode(port);
- GetOpenFile(port, fptr);
- rb_io_check_readable(fptr);
- arg.fp = fptr->f;
- arg.taint = Qtrue;
- }
- else if (rb_respond_to(port, rb_intern("to_str"))) {
+ if (rb_respond_to(port, rb_intern("to_str"))) {
arg.taint = OBJ_TAINTED(port); /* original taintedness */
StringValue(port); /* possible conversion */
- arg.fp = 0;
arg.ptr = RSTRING(port)->ptr;
arg.end = arg.ptr + RSTRING(port)->len;
}
else if (rb_respond_to(port, s_getc) && rb_respond_to(port, s_read)) {
- arg.taint = Qfalse;
- arg.fp = 0;
+ if (rb_respond_to(port, s_binmode)) {
+ rb_funcall2(port, s_binmode, 0, 0);
+ }
+ arg.taint = Qtrue;
arg.ptr = (char *)port;
arg.end = 0;
}
@@ -1251,7 +1341,7 @@ marshal_load(argc, argv)
}
arg.symbol = st_init_numtable();
- arg.data = hash = rb_hash_new();
+ arg.data = rb_hash_new();
if (NIL_P(proc)) arg.proc = 0;
else arg.proc = proc;
v = rb_ensure(load, (VALUE)&arg, load_ensure, (VALUE)&arg);
@@ -1266,12 +1356,15 @@ Init_marshal()
s_dump = rb_intern("_dump");
s_load = rb_intern("_load");
+ s_mdump = rb_intern("marshal_dump");
+ s_mload = rb_intern("marshal_load");
s_dump_data = rb_intern("_dump_data");
s_load_data = rb_intern("_load_data");
s_alloc = rb_intern("_alloc");
s_getc = rb_intern("getc");
s_read = rb_intern("read");
s_write = rb_intern("write");
+ s_binmode = rb_intern("binmode");
rb_define_module_function(rb_mMarshal, "dump", marshal_dump, -1);
rb_define_module_function(rb_mMarshal, "load", marshal_load, -1);
diff --git a/math.c b/math.c
index f422609f18..5268598750 100644
--- a/math.c
+++ b/math.c
@@ -6,7 +6,7 @@
$Date$
created at: Tue Jan 25 14:12:56 JST 1994
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
**********************************************************************/
@@ -193,8 +193,12 @@ math_exp(obj, x)
}
#if defined __CYGWIN__
-#define log(x) ((x) < 0.0 ? nan() : log(x))
-#define log10(x) ((x) < 0.0 ? nan() : log10(x))
+# include <cygwin/version.h>
+# if CYGWIN_VERSION_DLL_MAJOR < 1005
+# define nan(x) nan()
+# endif
+# define log(x) ((x) < 0.0 ? nan("") : log(x))
+# define log10(x) ((x) < 0.0 ? nan("") : log10(x))
#endif
static VALUE
@@ -271,6 +275,22 @@ math_hypot(obj, x, y)
return rb_float_new(hypot(RFLOAT(x)->value, RFLOAT(y)->value));
}
+static VALUE
+math_erf(obj, x)
+ VALUE obj, x;
+{
+ Need_Float(x);
+ return rb_float_new(erf(RFLOAT(x)->value));
+}
+
+static VALUE
+math_erfc(obj, x)
+ VALUE obj, x;
+{
+ Need_Float(x);
+ return rb_float_new(erfc(RFLOAT(x)->value));
+}
+
void
Init_Math()
{
@@ -314,4 +334,7 @@ Init_Math()
rb_define_module_function(rb_mMath, "ldexp", math_ldexp, 2);
rb_define_module_function(rb_mMath, "hypot", math_hypot, 2);
+
+ rb_define_module_function(rb_mMath, "erf", math_erf, 1);
+ rb_define_module_function(rb_mMath, "erfc", math_erfc, 1);
}
diff --git a/mdoc2man.rb b/mdoc2man.rb
new file mode 100755
index 0000000000..910b2e5745
--- /dev/null
+++ b/mdoc2man.rb
@@ -0,0 +1,465 @@
+#!/usr/bin/env ruby
+###
+### mdoc2man - mdoc to man converter
+###
+### Quick usage: mdoc2man.rb < mdoc_manpage.8 > man_manpage.8
+###
+### Ported from Perl by Akinori MUSHA.
+###
+### Copyright (c) 2001 University of Illinois Board of Trustees
+### Copyright (c) 2001 Mark D. Roth
+### Copyright (c) 2002, 2003 Akinori MUSHA
+### All rights reserved.
+###
+### Redistribution and use in source and binary forms, with or without
+### modification, are permitted provided that the following conditions
+### are met:
+### 1. Redistributions of source code must retain the above copyright
+### notice, this list of conditions and the following disclaimer.
+### 2. Redistributions in binary form must reproduce the above copyright
+### notice, this list of conditions and the following disclaimer in the
+### documentation and/or other materials provided with the distribution.
+### 3. All advertising materials mentioning features or use of this software
+### must display the following acknowledgement:
+### This product includes software developed by the University of
+### Illinois at Urbana, and their contributors.
+### 4. The University nor the names of their
+### contributors may be used to endorse or promote products derived from
+### this software without specific prior written permission.
+###
+### THIS SOFTWARE IS PROVIDED BY THE TRUSTEES AND CONTRIBUTORS ``AS IS'' AND
+### ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+### IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+### ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR CONTRIBUTORS BE LIABLE
+### FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+### DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+### OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+### HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+### LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+### OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+### SUCH DAMAGE.
+###
+### $Id$
+###
+
+class Mdoc2Man
+ ANGLE = 1
+ OPTION = 2
+ PAREN = 3
+
+ RE_PUNCT = /^[!"'),\.\/:;>\?\]`]$/
+
+ def initialize
+ @name = @date = @id = nil
+ @refauthors = @reftitle = @refissue = @refdate = @refopt = nil
+
+ @optlist = 0 ### 1 = bullet, 2 = enum, 3 = tag, 4 = item
+ @oldoptlist = 0
+ @nospace = 0 ### 0, 1, 2
+ @enum = 0
+ @synopsis = true
+ @reference = false
+ @ext = false
+ @extopt = false
+ @literal = false
+ end
+
+ def mdoc2man(i, o)
+ i.each { |line|
+ if /^\./ !~ line
+ o.print line
+ o.print ".br\n" if @literal
+ next
+ end
+
+ line.slice!(0, 1)
+
+ next if /\\"/ =~ line
+
+ line = parse_macro(line) and o.print line
+ }
+
+ initialize
+ end
+
+ def parse_macro(line)
+ words = line.split
+ retval = ''
+
+ quote = []
+ dl = false
+
+ while word = words.shift
+ case word
+ when RE_PUNCT
+ while q = quote.pop
+ case q
+ when OPTION
+ retval << ']'
+ when PAREN
+ retval << ')'
+ when ANGLE
+ retval << '>'
+ end
+ end
+ retval << word
+ next
+ when 'Li', 'Pf'
+ @nospace = 1
+ next
+ when 'Xo'
+ @ext = true
+ retval << ' ' unless retval.empty? || /[\n ]\z/ =~ retval
+ next
+ when 'Xc'
+ @ext = false
+ retval << "\n" unless @extopt
+ break
+ when 'Bd'
+ @literal = true if words[0] == '-literal'
+ retval << "\n"
+ break
+ when 'Ed'
+ @literal = false
+ break
+ when 'Ns'
+ @nospace = 1 if @nospace == 0
+ retval.chomp!(' ')
+ next
+ when 'No'
+ retval.chomp!(' ')
+ retval << words.shift
+ next
+ when 'Dq'
+ retval << '``'
+ begin
+ retval << words.shift << ' '
+ end until words.empty? || RE_PUNCT =~ words[0]
+ retval.chomp!(' ')
+ retval << '\'\''
+ @nospace = 1 if @nospace == 0 && RE_PUNCT =~ words[0]
+ next
+ when 'Sq', 'Ql'
+ retval << '`' << words.shift << '\''
+ @nospace = 1 if @nospace == 0 && RE_PUNCT =~ words[0]
+ next
+ # when 'Ic'
+ # retval << '\\fB' << words.shift << '\\fP'
+ # next
+ when 'Oo'
+ #retval << "[\\c\n"
+ @extopt = true
+ @nospace = 1 if @nospace == 0
+ retval << '['
+ next
+ when 'Oc'
+ @extopt = false
+ retval << ']'
+ next
+ when 'Ao'
+ @nospace = 1 if @nospace == 0
+ retval << '<'
+ next
+ when 'Ac'
+ retval << '>'
+ next
+ end
+
+ retval << ' ' if @nospace == 0 && !(retval.empty? || /[\n ]\z/ =~ retval)
+ @nospace = 0 if @nospace == 1
+
+ case word
+ when 'Dd'
+ @date = words.join(' ')
+ return nil
+ when 'Dt'
+ if words.size >= 2 && words[1] == '""' &&
+ /^(.*)\(([0-9])\)$/ =~ words[0]
+ words[0] = $1
+ words[1] = $2
+ end
+ @id = words.join(' ')
+ return nil
+ when 'Os'
+ retval << '.TH ' << @id << ' "' << @date << '" "' <<
+ words.join(' ') << '"'
+ break
+ when 'Sh'
+ retval << '.SH'
+ @synopsis = (words[0] == 'SYNOPSIS')
+ next
+ when 'Xr'
+ retval << '\\fB' << words.shift <<
+ '\\fP(' << words.shift << ')' << words.shift
+ break
+ when 'Rs'
+ @refauthors = []
+ @reftitle = ''
+ @refissue = ''
+ @refdate = ''
+ @refopt = ''
+ @reference = true
+ break
+ when 'Re'
+ retval << "\n"
+
+ # authors
+ while @refauthors.size > 1
+ retval << @refauthors.shift << ', '
+ end
+ retval << 'and ' unless retval.empty?
+ retval << @refauthors.shift
+
+ # title
+ retval << ', \\fI' << @reftitle << '\\fP'
+
+ # issue
+ retval << ', ' << @refissue unless @refissue.empty?
+
+ # date
+ retval << ', ' << @refdate unless @refdate.empty?
+
+ # optional info
+ retval << ', ' << @refopt unless @refopt.empty?
+
+ retval << ".\n"
+
+ @reference = false
+ break
+ when 'An'
+ next
+ when 'Dl'
+ retval << ".nf\n" << '\\& '
+ dl = true
+ next
+ when 'Ux'
+ retval << "UNIX"
+ next
+ end
+
+ if @reference
+ case word
+ when '%A'
+ @refauthors.unshift(words.join(' '))
+ break
+ when '%T'
+ @reftitle = words.join(' ')
+ @reftitle.sub!(/^"/, '')
+ @reftitle.sub!(/"$/, '')
+ break
+ when '%N'
+ @refissue = words.join(' ')
+ break
+ when '%D'
+ @refdate = words.join(' ')
+ break
+ when '%O'
+ @refopt = words.join(' ')
+ break
+ end
+ end
+
+ case word
+ when 'Nm'
+ name = words.empty? ? @name : words.shift
+ @name ||= name
+ retval << ".br\n" if @synopsis
+ retval << "\\fB" << name << "\\fP"
+ @nospace = 1 if @nospace == 0 && RE_PUNCT =~ words[0]
+ next
+ when 'Nd'
+ retval << '\\-'
+ next
+ when 'Fl'
+ retval << '\\fB\\-' << words.shift << '\\fP'
+ @nospace = 1 if @nospace == 0 && RE_PUNCT =~ words[0]
+ next
+ when 'Ar'
+ retval << '\\fI'
+ if words.empty?
+ retval << 'file ...\\fP'
+ else
+ retval << words.shift << '\\fP'
+ while words[0] == '|'
+ retval << ' ' << words.shift << ' \\fI' << words.shift << '\\fP'
+ end
+ @nospace = 1 if @nospace == 0 && RE_PUNCT =~ words[0]
+ next
+ end
+ when 'Cm'
+ retval << '\\fB' << words.shift << '\\fP'
+ while RE_PUNCT =~ words[0]
+ retval << words.shift
+ end
+ next
+ when 'Op'
+ quote << OPTION
+ @nospace = 1 if @nospace == 0
+ retval << '['
+ # words.push(words.pop + ']')
+ next
+ when 'Aq'
+ quote << ANGLE
+ @nospace = 1 if @nospace == 0
+ retval << '<'
+ # words.push(words.pop + '>')
+ next
+ when 'Pp'
+ retval << "\n"
+ next
+ when 'Ss'
+ retval << '.SS'
+ next
+ end
+
+ if word == 'Pa' && !quote.include?(OPTION)
+ retval << '\\fI'
+ retval << '\\&' if /^\./ =~ words[0]
+ retval << words.shift << '\\fP'
+ while RE_PUNCT =~ words[0]
+ retval << words.shift
+ end
+ # @nospace = 1 if @nospace == 0 && RE_PUNCT =~ words[0]
+ next
+ end
+
+ case word
+ when 'Dv'
+ retval << '.BR'
+ next
+ when 'Em', 'Ev'
+ retval << '.IR'
+ next
+ when 'Pq'
+ retval << '('
+ @nospace = 1
+ quote << PAREN
+ next
+ when 'Sx', 'Sy'
+ retval << '.B ' << words.join(' ')
+ break
+ when 'Ic'
+ retval << '\\fB'
+ until words.empty? || RE_PUNCT =~ words[0]
+ case words[0]
+ when 'Op'
+ words.shift
+ retval << '['
+ words.push(words.pop + ']')
+ next
+ when 'Aq'
+ words.shift
+ retval << '<'
+ words.push(words.pop + '>')
+ next
+ when 'Ar'
+ words.shift
+ retval << '\\fI' << words.shift << '\\fP'
+ else
+ retval << words.shift
+ end
+
+ retval << ' ' if @nospace == 0
+ end
+
+ retval.chomp!(' ')
+ retval << '\\fP'
+ retval << words.shift unless words.empty?
+ break
+ when 'Bl'
+ @oldoptlist = @optlist
+
+ case words[0]
+ when '-bullet'
+ @optlist = 1
+ when '-enum'
+ @optlist = 2
+ @enum = 0
+ when '-tag'
+ @optlist = 3
+ when '-item'
+ @optlist = 4
+ end
+
+ break
+ when 'El'
+ @optlist = @oldoptlist
+ next
+ end
+
+ if @optlist != 0 && word == 'It'
+ case @optlist
+ when 1
+ # bullets
+ retval << '.IP \\(bu'
+ when 2
+ # enum
+ @enum += 1
+ retval << '.IP ' << @enum << '.'
+ when 3
+ # tags
+ retval << ".TP\n"
+ case words[0]
+ when 'Pa', 'Ev'
+ words.shift
+ retval << '.B'
+ end
+ when 4
+ # item
+ retval << ".IP\n"
+ end
+
+ next
+ end
+
+ case word
+ when 'Sm'
+ case words[0]
+ when 'off'
+ @nospace = 2
+ when 'on'
+ # retval << "\n"
+ @nospace = 0
+ end
+ words.shift
+ next
+ end
+
+ retval << word
+ end
+
+ return nil if retval == '.'
+
+ retval.sub!(/\A\.([^a-zA-Z])/, "\\1")
+ # retval.chomp!(' ')
+
+ while q = quote.pop
+ case q
+ when OPTION
+ retval << ']'
+ when PAREN
+ retval << ')'
+ when ANGLE
+ retval << '>'
+ end
+ end
+
+ # retval << ' ' unless @nospace == 0 || retval.empty? || /\n\z/ =~ retval
+
+ retval << ' ' unless !@ext || @extopt || / $/ =~ retval
+
+ retval << "\n" unless @ext || @extopt || retval.empty? || /\n\z/ =~ retval
+
+ retval << ".fi\n" if dl
+
+ return retval
+ end
+
+ def self.mdoc2man(i, o)
+ new.mdoc2man(i, o)
+ end
+end
+
+if $0 == __FILE__
+ Mdoc2Man.mdoc2man(ARGF, STDOUT)
+end
diff --git a/misc/ruby-mode.el b/misc/ruby-mode.el
index 304c481a83..3b03e0ffc1 100644
--- a/misc/ruby-mode.el
+++ b/misc/ruby-mode.el
@@ -79,11 +79,12 @@
(define-key ruby-mode-map "}" 'ruby-electric-brace)
(define-key ruby-mode-map "\e\C-a" 'ruby-beginning-of-defun)
(define-key ruby-mode-map "\e\C-e" 'ruby-end-of-defun)
- (define-key ruby-mode-map "\e\C-b" 'ruby-beginning-of-block)
- (define-key ruby-mode-map "\e\C-f" 'ruby-end-of-block)
+ (define-key ruby-mode-map "\e\C-b" 'ruby-backward-sexp)
+ (define-key ruby-mode-map "\e\C-f" 'ruby-forward-sexp)
(define-key ruby-mode-map "\e\C-p" 'ruby-beginning-of-block)
(define-key ruby-mode-map "\e\C-n" 'ruby-end-of-block)
(define-key ruby-mode-map "\e\C-h" 'ruby-mark-defun)
+ (define-key ruby-mode-map "\e\C-q" 'ruby-indent-exp)
(define-key ruby-mode-map "\t" 'ruby-indent-command)
(define-key ruby-mode-map "\C-c\C-e" 'ruby-insert-end)
(define-key ruby-mode-map "\C-j" 'ruby-reindent-then-newline-and-indent)
@@ -123,16 +124,35 @@
(modify-syntax-entry ?\] ")[" ruby-mode-syntax-table)
)
-(defvar ruby-indent-level 2
- "*Indentation of ruby statements.")
+(defcustom ruby-indent-tabs-mode nil
+ "*Indentation can insert tabs in ruby mode if this is non-nil."
+ :type 'boolean :group 'ruby)
-(defvar ruby-deep-arglist t
- "*Deep indent argument lists when non-nil.
-Also ignores spaces after parenthesis when 'space.")
+(defcustom ruby-indent-level 2
+ "*Indentation of ruby statements."
+ :type 'integer :group 'ruby)
+
+(defcustom ruby-comment-column 32
+ "*Indentation column of comments."
+ :type 'integer :group 'ruby)
+
+(defcustom ruby-deep-arglist t
+ "*Deep indent lists in parenthesis when non-nil.
+Also ignores spaces after parenthesis when 'space."
+ :group 'ruby)
+
+(defcustom ruby-deep-indent-paren '(?\( t)
+ "*Deep indent lists in parenthesis when non-nil. t means continuous line.
+Also ignores spaces after parenthesis when 'space."
+ :group 'ruby)
+
+(defcustom ruby-deep-indent-paren-style 'space
+ "Default deep indent style."
+ :options '(t nil space) :group 'ruby)
(eval-when-compile (require 'cl))
(defun ruby-imenu-create-index-in-block (prefix beg end)
- (let ((index-alist '()) (nest '()) (case-fold-search nil)
+ (let ((index-alist '()) (case-fold-search nil)
name next pos decl sing)
(goto-char beg)
(while (re-search-forward "^\\s *\\(\\(class\\>\\(\\s *<<\\)?\\|module\\>\\)\\s *\\([^\(<\n ]+\\)\\|\\(def\\|alias\\)\\>\\s *\\([^\(\n ]+\\)\\)" end t)
@@ -189,9 +209,10 @@ Also ignores spaces after parenthesis when 'space.")
(make-variable-buffer-local 'comment-end)
(setq comment-end "")
(make-variable-buffer-local 'comment-column)
- (setq comment-column 32)
+ (setq comment-column ruby-comment-column)
(make-variable-buffer-local 'comment-start-skip)
(setq comment-start-skip "\\(^\\|\\s-\\);?#+ *")
+ (setq indent-tabs-mode ruby-indent-tabs-mode)
(make-local-variable 'parse-sexp-ignore-comments)
(setq parse-sexp-ignore-comments t)
(make-local-variable 'paragraph-start)
@@ -260,18 +281,25 @@ The variable ruby-indent-level controls the amount of indentation.
(indent-to x)
(move-to-column (+ x shift))))))
+(defun ruby-special-char-p (&optional pnt)
+ (let ((c (char-before (or pnt (point)))))
+ (cond ((or (eq c ??) (eq c ?$)))
+ ((eq c ?\\)
+ (eq (char-before (1- (or pnt (point)))) ??)))))
+
(defun ruby-expr-beg (&optional option)
(save-excursion
(store-match-data nil)
(let ((space (skip-chars-backward " \t")))
(cond
((bolp) t)
- ((looking-at "\\?")
- (or (bolp) (forward-char -1))
- (not (looking-at "\\sw")))
- (t
- (forward-char -1)
- (or (looking-at ruby-operator-re)
+ ((progn
+ (forward-char -1)
+ (and (looking-at "\\?")
+ (or (eq (char-syntax (char-before (point))) ?w)
+ (ruby-special-char-p))))
+ nil)
+ ((or (looking-at ruby-operator-re)
(looking-at "[\\[({,;]")
(and (or (not (eq option 'heredoc))
(< space 0))
@@ -306,7 +334,17 @@ The variable ruby-indent-level controls the amount of indentation.
(forward-char -1))
(cond ((zerop n))
(no-error nil)
- (error "unterminated string"))))
+ ((error "unterminated string")))))
+
+(defun ruby-deep-indent-paren-p (c)
+ (cond ((listp ruby-deep-indent-paren)
+ (let ((deep (assoc c ruby-deep-indent-paren)))
+ (cond (deep
+ (or (cdr deep) ruby-deep-indent-paren-style))
+ ((memq c ruby-deep-indent-paren)
+ ruby-deep-indent-paren-style))))
+ ((eq c ruby-deep-indent-paren) ruby-deep-indent-paren-style)
+ ((eq c ?\( ) ruby-deep-arglist)))
(defun ruby-parse-partial (&optional end in-string nest depth pcol indent)
(or depth (setq depth 0))
@@ -315,8 +353,9 @@ The variable ruby-indent-level controls the amount of indentation.
(let ((pnt (point)) w re expand)
(goto-char (match-beginning 0))
(cond
- ((or (looking-at "\"") ;skip string
- (looking-at "`"))
+ ((and (memq (char-before) '(?@ ?$)) (looking-at "\\sw"))
+ (goto-char pnt))
+ ((looking-at "[\"`]") ;skip string
(cond
((and (not (eobp))
(ruby-forward-string (buffer-substring (point) (1+ (point))) end t t))
@@ -371,7 +410,7 @@ The variable ruby-indent-level controls the amount of indentation.
((looking-at "\\?") ;skip ?char
(cond
((and (ruby-expr-beg)
- (looking-at "?\\(\\\\C-\\|\\\\M-\\)*."))
+ (looking-at "?\\(\\\\C-\\|\\\\M-\\)*\\\\?."))
(goto-char (match-end 0)))
(t
(goto-char pnt))))
@@ -382,27 +421,24 @@ The variable ruby-indent-level controls the amount of indentation.
(forward-line 1)
(goto-char (point))
)
- ((and (looking-at "(") ruby-deep-arglist)
- (and (eq ruby-deep-arglist 'space) (looking-at ".\\s +")
- (setq pnt (match-end 0)))
- (setq nest (cons (cons (char-after (point)) pnt) nest))
- (setq pcol (cons (cons pnt depth) pcol))
- (setq depth 0)
- (goto-char pnt)
- )
((looking-at "[\\[{(]")
- (setq nest (cons (cons (char-after (point)) pnt) nest))
- (setq depth (1+ depth))
+ (let ((deep (ruby-deep-indent-paren-p (char-after))))
+ (if (and deep (or (not (eq (char-after) ?\{)) (ruby-expr-beg)))
+ (progn
+ (and (eq deep 'space) (looking-at ".\\s +[^# \t\n]")
+ (setq pnt (1- (match-end 0))))
+ (setq nest (cons (cons (char-after (point)) pnt) nest))
+ (setq pcol (cons (cons pnt depth) pcol))
+ (setq depth 0))
+ (setq nest (cons (cons (char-after (point)) pnt) nest))
+ (setq depth (1+ depth))))
(goto-char pnt)
)
- ((and (looking-at ")") ruby-deep-arglist)
- (setq nest (cdr nest))
- (setq depth (cdr (car pcol)))
- (setq pcol (cdr pcol))
- (goto-char pnt))
((looking-at "[])}]")
+ (if (ruby-deep-indent-paren-p (matching-paren (char-after)))
+ (setq depth (cdr (car pcol)) pcol (cdr pcol))
+ (setq depth (1- depth)))
(setq nest (cdr nest))
- (setq depth (1- depth))
(goto-char pnt))
((looking-at ruby-block-end-re)
(if (or (and (not (bolp))
@@ -506,14 +542,46 @@ The variable ruby-indent-level controls the amount of indentation.
)))
(defun ruby-indent-size (pos nest)
- (+ pos (* (if nest nest 1) ruby-indent-level)))
+ (+ pos (* (or nest 1) ruby-indent-level)))
+
+(defconst ruby-assign-re "\\s *\\(&&\\|||\\|<<\\|>>\\|[-+*/%&|^]\\)?=\\s *")
+
+(defun ruby-beginning-of-arg (start end)
+ (save-restriction
+ (narrow-to-region start (1+ end))
+ (goto-char start)
+ (let ((beg t) arg)
+ (while
+ (progn
+ (skip-chars-forward " \t\n")
+ (and (not (eobp))
+ (= (ruby-forward-sexp) 0)))
+ (skip-syntax-forward " ")
+ (cond ((looking-at ",")
+ (forward-char)
+ (setq arg start beg t))
+ ((ruby-expr-beg) t)
+ ((looking-at "=>\\s *")
+ (goto-char (match-end 0))
+ (setq arg nil beg nil))
+ ((looking-at ruby-assign-re)
+ (goto-char (match-end 0))
+ (if beg (setq beg nil arg (point))))
+ ((looking-at ruby-operator-re)
+ (goto-char (match-end 0))
+ (if beg (setq beg nil arg (match-end 0))))
+ ((not (eq (char-syntax (char-after)) ?\())
+ (setq start (point)))))
+ (goto-char (or arg start)))))
(defun ruby-calculate-indent (&optional parse-start)
(save-excursion
(beginning-of-line)
(let ((indent-point (point))
(case-fold-search nil)
- state bol eol
+ state bol eol begin
+ (paren (progn (skip-syntax-forward " ")
+ (and (char-after) (matching-paren (char-after)))))
(indent 0))
(if parse-start
(goto-char parse-start)
@@ -526,55 +594,59 @@ The variable ruby-indent-level controls the amount of indentation.
((nth 0 state) ; within string
(setq indent nil)) ; do nothing
((car (nth 1 state)) ; in paren
- (goto-char (cdr (nth 1 state)))
- (if (and (eq (car (nth 1 state)) ?\( ) ruby-deep-arglist)
- (let ((column (current-column))
- (s (ruby-parse-region (point) indent-point)))
- (cond
- ((and (nth 2 s) (> (nth 2 s) 0))
- (goto-char (cdr (nth 1 s)))
- (forward-word -1)
- (setq indent (ruby-indent-size (current-column) (nth 2 state))))
- (t
- (setq indent (current-column)))))
- (cond
- ((nth 3 state)
- (goto-char (nth 3 state))
- (setq indent (ruby-indent-size (current-column) (nth 2 state))))
- (t
- (goto-char parse-start)
- (back-to-indentation)
- (setq indent (ruby-indent-size (current-column) (nth 2 state)))))
- ))
- ((and (nth 2 state)(> (nth 2 state) 0)) ; in nest
+ (cond
+ ((and (eq (car (nth 1 state)) ?\{) ; brace block
+ (save-excursion
+ (goto-char (1- (cdr (nth 1 state))))
+ (not (ruby-expr-beg))))
+ (setq paren nil)
+ (back-to-indentation)
+ (setq indent (ruby-indent-size (current-column) (nth 2 state))))
+ (t
+ (goto-char (setq begin (cdr (nth 1 state))))
+ (let ((deep (ruby-deep-indent-paren-p (car (nth 1 state)))))
+ (if deep
+ (cond ((and (eq deep t) (eq (car (nth 1 state)) paren))
+ (skip-syntax-backward " ")
+ (setq indent (1- (current-column))))
+ ((let ((s (ruby-parse-region (point) indent-point)))
+ (and (nth 2 s) (> (nth 2 s) 0)
+ (or (goto-char (cdr (nth 1 s))) t)))
+ (forward-word -1)
+ (setq indent (ruby-indent-size (current-column) (nth 2 state))))
+ (t
+ (setq indent (current-column))
+ (cond ((eq deep 'space))
+ (paren (setq indent (1- indent)))
+ (t (setq indent (ruby-indent-size (1- indent) 1))))))
+ (if (nth 3 state) (goto-char (nth 3 state))
+ (goto-char parse-start) (back-to-indentation))
+ (setq indent (ruby-indent-size (current-column) (nth 2 state))))))))
+ ((and (nth 2 state) (> (nth 2 state) 0)) ; in nest
(if (null (cdr (nth 1 state)))
(error "invalid nest"))
(goto-char (cdr (nth 1 state)))
(forward-word -1) ; skip back a keyword
+ (setq begin (point))
(cond
((looking-at "do\\>[^_]") ; iter block is a special case
- (cond
- ((nth 3 state)
- (goto-char (nth 3 state))
- (setq indent (ruby-indent-size (current-column) (nth 2 state))))
- (t
- (goto-char parse-start)
- (back-to-indentation)
- (setq indent (ruby-indent-size (current-column) (nth 2 state))))))
+ (if (nth 3 state) (goto-char (nth 3 state))
+ (goto-char parse-start) (back-to-indentation))
+ (setq indent (ruby-indent-size (current-column) (nth 2 state))))
(t
(setq indent (+ (current-column) ruby-indent-level)))))
((and (nth 2 state) (< (nth 2 state) 0)) ; in negative nest
(setq indent (ruby-indent-size (current-column) (nth 2 state)))))
- (cond
- (indent
+ (when indent
(goto-char indent-point)
(end-of-line)
(setq eol (point))
(beginning-of-line)
(cond
- ((re-search-forward ruby-negative eol t)
+ ((and (not (ruby-deep-indent-paren-p paren))
+ (re-search-forward ruby-negative eol t))
(and (not (eq ?_ (char-after (match-end 0))))
(setq indent (- indent ruby-indent-level))))
;;operator terminated lines
@@ -582,12 +654,8 @@ The variable ruby-indent-level controls the amount of indentation.
(save-excursion
(beginning-of-line)
(not (bobp)))
- (or (null (car (nth 1 state))) ;not in parens
- (and (eq (car (nth 1 state)) ?\{)
- (save-excursion ;except non-block braces
- (goto-char (cdr (nth 1 state)))
- (or (bobp) (forward-char -1))
- (not (ruby-expr-beg))))))
+ (or (ruby-deep-indent-paren-p t)
+ (null (car (nth 1 state)))))
;; goto beginning of non-empty no-comment line
(let (end done)
(while (not done)
@@ -602,12 +670,13 @@ The variable ruby-indent-level controls the amount of indentation.
(skip-chars-backward " \t")
(let ((pos (point)))
(while (and (re-search-backward "#" bol t)
- (eq (char-before) ??))
+ (ruby-special-char-p))
(forward-char -1))
(skip-chars-backward " \t")
(and
(setq state (ruby-parse-region parse-start (point)))
(nth 0 state)
+ (setq begin (nth 1 state))
(goto-char pos)))
(or (bobp) (forward-char -1))
(and
@@ -619,8 +688,7 @@ The variable ruby-indent-level controls the amount of indentation.
(goto-char (match-end 0))
(not (looking-at "[a-z_]"))))
(and (looking-at ruby-operator-re)
- (not (eq (char-after (1- (point))) ??))
- (not (eq (char-after (1- (point))) ?$))
+ (not (ruby-special-char-p))
(or (not (eq ?/ (char-after (point))))
(null (nth 0 (ruby-parse-region parse-start (point)))))
(or (not (eq ?| (char-after (point))))
@@ -637,15 +705,36 @@ The variable ruby-indent-level controls the amount of indentation.
(forward-word -1)
(not (looking-at "do\\>[^_]")))))
(t t))))))
- (setq indent (+ indent ruby-indent-level)))))))
+ (setq indent
+ (cond
+ ((and
+ (not (looking-at ruby-block-hanging-re))
+ (eq (ruby-deep-indent-paren-p t) 'space)
+ (not (bobp)))
+ (ruby-beginning-of-arg (or begin parse-start) (point))
+ (current-column))
+ (t
+ (+ indent ruby-indent-level))))))))
indent)))
(defun ruby-electric-brace (arg)
(interactive "P")
- (self-insert-command (prefix-numeric-value arg))
- (ruby-indent-line t))
-
-(defun ruby-beginning-of-defun (&optional arg)
+ (insert-char last-command-char 1)
+ (ruby-indent-line t)
+ (delete-char -1)
+ (self-insert-command (prefix-numeric-value arg)))
+
+(eval-when-compile
+ (defmacro defun-region-command (func args &rest body)
+ (let ((intr (car body)))
+ (when (featurep 'xemacs)
+ (if (stringp intr) (setq intr (cadr body)))
+ (and (eq (car intr) 'interactive)
+ (setq intr (cdr intr))
+ (setcar intr (concat "_" (car intr)))))
+ (cons 'defun (cons func (cons args body))))))
+
+(defun-region-command ruby-beginning-of-defun (&optional arg)
"Move backward to next beginning-of-defun.
With argument, do this that many times.
Returns t unless search stops due to end of buffer."
@@ -661,7 +750,7 @@ Returns t unless search stops due to end of buffer."
(beginning-of-line)
t)))
-(defun ruby-end-of-defun (&optional arg)
+(defun-region-command ruby-end-of-defun (&optional arg)
"Move forward to next end of defun.
An end of a defun is found by moving forward from the beginning of one."
(interactive "p")
@@ -699,15 +788,95 @@ An end of a defun is found by moving forward from the beginning of one."
(setq done nil))))))
(back-to-indentation))
-(defun ruby-beginning-of-block ()
+(defun-region-command ruby-beginning-of-block (&optional arg)
"Move backward to next beginning-of-block"
- (interactive)
- (ruby-move-to-block -1))
+ (interactive "p")
+ (ruby-move-to-block (- (or arg 1))))
-(defun ruby-end-of-block ()
+(defun-region-command ruby-end-of-block (&optional arg)
"Move forward to next beginning-of-block"
- (interactive)
- (ruby-move-to-block 1))
+ (interactive "p")
+ (ruby-move-to-block (or arg 1)))
+
+(defun-region-command ruby-forward-sexp (&optional cnt)
+ (interactive "p")
+ (if (and (numberp cnt) (< cnt 0))
+ (ruby-backward-sexp (- cnt))
+ (let ((i (or cnt 1)))
+ (condition-case nil
+ (while (> i 0)
+ (skip-syntax-forward " ")
+ (cond ((looking-at "\\?\\(\\\\[CM]-\\)*\\\\?\\S ")
+ (goto-char (match-end 0)))
+ ((progn
+ (skip-chars-forward ",.:;|&^~=!?\\+\\-\\*")
+ (looking-at "\\s("))
+ (goto-char (scan-sexps (point) 1)))
+ ((looking-at ruby-block-beg-re)
+ (ruby-end-of-block)
+ (forward-word 1))
+ ((looking-at "\\(\\$\\|@@?\\)?\\sw")
+ (while (progn
+ (while (progn (forward-word 1) (looking-at "_")))
+ (cond ((looking-at "::") (forward-char 2) t)
+ ((> (skip-chars-forward ".") 0))
+ ((looking-at "\\?\\|!\\(=[~=>]\\|[^~=]\\)")
+ (forward-char 1) nil)))))
+ ((let (state expr)
+ (while
+ (progn
+ (setq expr (or expr (ruby-expr-beg)
+ (looking-at "%\\sw?\\Sw\\|[\"'`/]")))
+ (nth 1 (setq state (apply 'ruby-parse-partial nil state))))
+ (setq expr t)
+ (skip-chars-forward "<"))
+ (not expr))))
+ (setq i (1- i)))
+ ((error) (forward-word 1)))
+ i)))
+
+(defun-region-command ruby-backward-sexp (&optional cnt)
+ (interactive "p")
+ (if (and (numberp cnt) (< cnt 0))
+ (ruby-forward-sexp (- cnt))
+ (let ((i (or cnt 1)))
+ (condition-case nil
+ (while (> i 0)
+ (skip-chars-backward " \t\n,.:;|&^~=!?\\+\\-\\*")
+ (forward-char -1)
+ (cond ((looking-at "\\s)")
+ (goto-char (scan-sexps (1+ (point)) -1))
+ (case (char-before)
+ (?% (forward-char -1))
+ ('(?q ?Q ?w ?W ?r ?x)
+ (if (eq (char-before (1- (point))) ?%) (forward-char -2))))
+ nil)
+ ((looking-at "\\s\"\\|\\\\\\S_")
+ (let ((c (char-to-string (char-before (match-end 0)))))
+ (while (and (search-backward c)
+ (oddp (skip-chars-backward "\\")))))
+ nil)
+ ((looking-at "\\s.\\|\\s\\")
+ (if (ruby-special-char-p) (forward-char -1)))
+ ((looking-at "\\s(") nil)
+ (t
+ (forward-char 1)
+ (while (progn (forward-word -1)
+ (case (char-before)
+ (?_ t)
+ (?. (forward-char -1) t)
+ ((?$ ?@)
+ (forward-char -1)
+ (and (eq (char-before) (char-after)) (forward-char -1)))
+ (?:
+ (forward-char -1)
+ (eq (char-before) :)))))
+ (if (looking-at ruby-block-end-re)
+ (ruby-beginning-of-block))
+ nil))
+ (setq i (1- i)))
+ ((error)))
+ i)))
(defun ruby-reindent-then-newline-and-indent ()
(interactive "*")
@@ -744,6 +913,29 @@ An end of a defun is found by moving forward from the beginning of one."
(ruby-beginning-of-defun)
(re-search-backward "^\n" (- (point) 1) t))
+(defun ruby-indent-exp (&optional shutup-p)
+ "Indent each line in the balanced expression following point syntactically.
+If optional SHUTUP-P is non-nil, no errors are signalled if no
+balanced expression is found."
+ (interactive "*P")
+ (let ((here (point-marker)) start top column (nest t))
+ (set-marker-insertion-type here t)
+ (unwind-protect
+ (progn
+ (beginning-of-line)
+ (setq start (point) top (current-indentation))
+ (while (and (not (eobp))
+ (progn
+ (setq column (ruby-calculate-indent start))
+ (cond ((> column top)
+ (setq nest t))
+ ((and (= column top) nest)
+ (setq nest nil) t))))
+ (ruby-indent-to column)
+ (beginning-of-line 2)))
+ (goto-char here)
+ (set-marker here nil))))
+
(defun ruby-add-log-current-method ()
"Return current method string."
(condition-case nil
@@ -786,30 +978,20 @@ An end of a defun is found by moving forward from the beginning of one."
'(
;; #{ }, #$hoge, #@foo are not comments
("\\(#\\)[{$@]" 1 (1 . nil))
- ;; the last $' in the string ,'...$' is not variable
- ;; the last ?' in the string ,'...?' is not ascii code
- ("\\(^\\|[[\\s <+(,=]\\)\\('\\)[^'\n\\\\]*\\(\\\\.[^'\n\\\\]*\\)*[?$]\\('\\)"
- (2 (7 . nil))
- (4 (7 . nil)))
- ;; the last $` in the string ,`...$` is not variable
- ;; the last ?` in the string ,`...?` is not ascii code
- ("\\(^\\|[[\\s <+(,=]\\)\\(`\\)[^`\n\\\\]*\\(\\\\.[^`\n\\\\]*\\)*[?$]\\(`\\)"
- (2 (7 . nil))
- (4 (7 . nil)))
- ;; the last $" in the string ,"...$" is not variable
- ;; the last ?" in the string ,"...?" is not ascii code
- ("\\(^\\|[[\\s <+(,=]\\)\\(\"\\)[^\"\n\\\\]*\\(\\\\.[^\"\n\\\\]*\\)*[?$]\\(\"\\)"
+ ;; the last $', $", $` in the respective string is not variable
+ ;; the last ?', ?", ?` in the respective string is not ascii code
+ ("\\(^\\|[\[ \t\n<+\(,=]\\)\\(['\"`]\\)\\(\\\\.\\|\\2\\|[^'\"`\n\\\\]\\)*\\\\?[?$]\\(\\2\\)"
(2 (7 . nil))
(4 (7 . nil)))
;; $' $" $` .... are variables
;; ?' ?" ?` are ascii codes
- ("[?$][#\"'`]" 0 (1 . nil))
+ ("\\(^\\|[^\\\\]\\)\\(\\\\\\\\\\)*[?$]\\([#\"'`]\\)" 3 (1 . nil))
;; regexps
("\\(^\\|[=(,~?:;]\\|\\(^\\|\\s \\)\\(if\\|elsif\\|unless\\|while\\|until\\|when\\|and\\|or\\|&&\\|||\\)\\|g?sub!?\\|scan\\|split!?\\)\\s *\\(/\\)[^/\n\\\\]*\\(\\\\.[^/\n\\\\]*\\)*\\(/\\)"
(4 (7 . ?/))
(6 (7 . ?/)))
;; %Q!...!
- ("\\(^\\|[[\\s <+(,=]\\)%[xrqQ]?\\([^a-zA-Z0-9 \n]\\)[^\n\\\\]*\\(\\\\.[^\n\\\\]*\\)*\\(\\2\\)"
+ ("\\(^\\|[[ \t\n<+(,=]\\)%[xrqQwW]?\\([^<[{(a-zA-Z0-9 \n]\\)[^\n\\\\]*\\(\\\\.[^\n\\\\]*\\)*\\(\\2\\)"
(2 (7 . nil))
(4 (7 . nil)))
("^\\(=\\)begin\\(\\s \\|$\\)" 1 (7 . nil))
@@ -868,11 +1050,11 @@ An end of a defun is found by moving forward from the beginning of one."
(defvar ruby-font-lock-keywords
(list
;; functions
- '("^\\s *def\\s +\\([^( ]+\\)"
+ '("^\\s *def\\s +\\([^( \t\n]+\\)"
1 font-lock-function-name-face)
;; keywords
(cons (concat
- "\\(^\\|[^_:.@$]\\|\\.\\.\\)\\b\\("
+ "\\(^\\|[^_:.@$]\\|\\.\\.\\)\\b\\(defined\\?\\|\\("
(mapconcat
'identity
'("alias"
@@ -912,7 +1094,7 @@ An end of a defun is found by moving forward from the beginning of one."
"yield"
)
"\\|")
- "\\)\\>")
+ "\\)\\>\\)")
2)
;; variables
'("\\(^\\|[^_:.@$]\\|\\.\\.\\)\\b\\(nil\\|self\\|true\\|false\\)\\>"
@@ -935,7 +1117,11 @@ An end of a defun is found by moving forward from the beginning of one."
2 font-lock-reference-face)
;; expression expansion
'("#\\({[^}\n\\\\]*\\(\\\\.[^}\n\\\\]*\\)*}\\|\\(\\$\\|@\\|@@\\)\\(\\w\\|_\\)+\\)"
- 0 font-lock-variable-name-face t))
+ 0 font-lock-variable-name-face t)
+ ;; warn lower camel case
+ ;'("\\<[a-z]+[a-z0-9]*[A-Z][A-Za-z0-9]*\\([!?]?\\|\\>\\)"
+ ; 0 font-lock-warning-face)
+ )
"*Additional expressions to highlight in ruby mode."))
((featurep 'hilit19)
diff --git a/missing.h b/missing.h
index 877ee6bf7e..0603db1a40 100644
--- a/missing.h
+++ b/missing.h
@@ -54,6 +54,11 @@ extern double frexp _((double, int *));
extern double hypot _((double, double));
#endif
+#ifndef HAVE_ERF
+extern double erf _((double));
+extern double erfc _((double));
+#endif
+
#ifndef HAVE_ISINF
extern int isinf _((double));
#endif
@@ -114,10 +119,15 @@ extern long strtol _((char *, char **, int));
*/
#ifndef HAVE_STRTOUL
-extern long strtoul _((char *, char **, int));
+extern unsigned long strtoul _((char *, char **, int));
#endif
#ifndef HAVE_VSNPRINTF
+# ifdef HAVE_STDARG_PROTOTYPES
+# include <stdarg.h>
+# else
+# include <varargs.h>
+# endif
extern snprintf __((char *, size_t n, char const *, ...));
extern vsnprintf _((char *, size_t n, char const *, va_list));
#endif
diff --git a/missing/erf.c b/missing/erf.c
new file mode 100644
index 0000000000..e30a90051e
--- /dev/null
+++ b/missing/erf.c
@@ -0,0 +1,91 @@
+/* erf.c
+reference - Haruhiko Okumura: C-gengo niyoru saishin algorithm jiten
+ (New Algorithm handbook in C language) (Gijyutsu hyouron
+ sha, Tokyo, 1991) p.227 [in Japanese] */
+#include <stdio.h>
+#include <math.h>
+
+#ifdef _WIN32
+# include <float.h>
+# if !defined __MINGW32__ || defined __NO_ISOCEXT
+# ifndef isnan
+# define isnan(x) _isnan(x)
+# endif
+# ifndef isinf
+# define isinf(x) (!_finite(x) && !_isnan(x))
+# endif
+# ifndef finite
+# define finite(x) _finite(x)
+# endif
+# endif
+#endif
+
+static double q_gamma(double, double, double);
+
+/* Incomplete gamma function
+ 1 / Gamma(a) * Int_0^x exp(-t) t^(a-1) dt */
+static double p_gamma(a, x, loggamma_a)
+ double a, x, loggamma_a;
+{
+ int k;
+ double result, term, previous;
+
+ if (x >= 1 + a) return 1 - q_gamma(a, x, loggamma_a);
+ if (x == 0) return 0;
+ result = term = exp(a * log(x) - x - loggamma_a) / a;
+ for (k = 1; k < 1000; k++) {
+ term *= x / (a + k);
+ previous = result; result += term;
+ if (result == previous) return result;
+ }
+ fprintf(stderr, "erf.c:%d:p_gamma() could not converge.", __LINE__);
+ return result;
+}
+
+/* Incomplete gamma function
+ 1 / Gamma(a) * Int_x^inf exp(-t) t^(a-1) dt */
+static double q_gamma(a, x, loggamma_a)
+ double a, x, loggamma_a;
+{
+ int k;
+ double result, w, temp, previous;
+ double la = 1, lb = 1 + x - a; /* Laguerre polynomial */
+
+ if (x < 1 + a) return 1 - p_gamma(a, x, loggamma_a);
+ w = exp(a * log(x) - x - loggamma_a);
+ result = w / lb;
+ for (k = 2; k < 1000; k++) {
+ temp = ((k - 1 - a) * (lb - la) + (k + x) * lb) / k;
+ la = lb; lb = temp;
+ w *= (k - 1 - a) / k;
+ temp = w / (la * lb);
+ previous = result; result += temp;
+ if (result == previous) return result;
+ }
+ fprintf(stderr, "erf.c:%d:q_gamma() could not converge.", __LINE__);
+ return result;
+}
+
+#define LOG_PI_OVER_2 0.572364942924700087071713675675 /* log_e(PI)/2 */
+
+double erf(x)
+ double x;
+{
+ if (!finite(x)) {
+ if (isnan(x)) return x; /* erf(NaN) = NaN */
+ return (x>0 ? 1.0 : -1.0); /* erf(+-inf) = +-1.0 */
+ }
+ if (x >= 0) return p_gamma(0.5, x * x, LOG_PI_OVER_2);
+ else return - p_gamma(0.5, x * x, LOG_PI_OVER_2);
+}
+
+double erfc(x)
+ double x;
+{
+ if (!finite(x)) {
+ if (isnan(x)) return x; /* erfc(NaN) = NaN */
+ return (x>0 ? 0.0 : 2.0); /* erfc(+-inf) = 0.0, 2.0 */
+ }
+ if (x >= 0) return q_gamma(0.5, x * x, LOG_PI_OVER_2);
+ else return 1 + p_gamma(0.5, x * x, LOG_PI_OVER_2);
+}
diff --git a/missing/strftime.c b/missing/strftime.c
index 07d2ed10d9..5522226ec1 100644
--- a/missing/strftime.c
+++ b/missing/strftime.c
@@ -121,9 +121,13 @@ extern int daylight;
#ifdef SOLARIS
extern long timezone, altzone;
#else
+#ifdef __hpux
+extern long timezone;
+#else
extern int timezone, altzone;
#endif
#endif
+#endif
#undef min /* just in case */
@@ -189,6 +193,11 @@ strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr)
extern char *timezone();
struct timeval tv;
struct timezone zone;
+#else
+#ifdef __hpux
+ struct timeval tv;
+ struct timezone zone;
+#endif
#endif /* HAVE_TZNAME */
#endif /* HAVE_TM_NAME */
#endif /* HAVE_TM_ZONE */
@@ -418,7 +427,12 @@ strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr)
* Systems with tzname[] probably have timezone as
* secs west of GMT. Convert to mins east of GMT.
*/
+#ifdef __hpux
+ gettimeofday(&tv, &zone);
+ off = -zone.tz_minuteswest;
+#else
off = -(daylight ? timezone : altzone) / 60;
+#endif
#else /* !HAVE_TZNAME */
gettimeofday(&tv, &zone);
off = -zone.tz_minuteswest;
diff --git a/mkconfig.rb b/mkconfig.rb
index 9ee960ed34..b697dfb9d1 100644
--- a/mkconfig.rb
+++ b/mkconfig.rb
@@ -1,5 +1,10 @@
#!./miniruby -s
+# avoid warnings with -d.
+$srcdir ||= nil
+$install_name ||= nil
+$so_name ||= nil
+
require File.dirname($0)+"/lib/ftools"
mkconfig = File.basename($0)
@@ -48,7 +53,7 @@ File.foreach "config.status" do |line|
end
has_version = true if name == "MAJOR"
elsif /^(?:ac_given_)?srcdir=(.*)/ =~ line
- srcdir = $1
+ srcdir = $1.strip
elsif /^ac_given_INSTALL=(.*)/ =~ line
v_fast << " CONFIG[\"INSTALL\"] = " + $1 + "\n"
end
@@ -84,7 +89,7 @@ end
dest = drive ? /= \"(?!\$[\(\{])(?:[a-z]:)?/i : /= \"(?!\$[\(\{])/
v_others.collect! do |x|
- if /^\s*CONFIG\["(?!abs_|old)[a-z]+(?:_prefix|dir)"]/ === x
+ if /^\s*CONFIG\["(?!abs_|old)[a-z]+(?:_prefix|dir)"\]/ === x
x.sub(dest, '= "$(DESTDIR)')
else
x
@@ -127,6 +132,7 @@ print <<EOS
Config::expand(val)
end
end
+CROSS_COMPILING = nil unless defined? CROSS_COMPILING
EOS
$stdout.flush
$stdout.reopen($orgout)
diff --git a/node.h b/node.h
index 691a4cd8c1..e335c8dfc4 100644
--- a/node.h
+++ b/node.h
@@ -6,7 +6,7 @@
$Date$
created at: Fri May 28 15:14:02 JST 1993
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
**********************************************************************/
@@ -87,9 +87,10 @@ enum node_type {
NODE_ARGS,
NODE_ARGSCAT,
NODE_ARGSPUSH,
- NODE_RESTARGS,
NODE_RESTARY,
- NODE_REXPAND,
+ NODE_RESTARY2,
+ NODE_SPLAT,
+ NODE_SVALUE,
NODE_BLOCK_ARG,
NODE_BLOCK_PASS,
NODE_DEFN,
@@ -123,6 +124,7 @@ enum node_type {
NODE_MEMO,
NODE_IFUNC,
NODE_DSYM,
+ NODE_ATTRASGN,
NODE_LAST
};
@@ -139,7 +141,7 @@ typedef struct RNode {
union {
struct RNode *node;
ID id;
- int argc;
+ long argc;
VALUE value;
} u2;
union {
@@ -216,7 +218,7 @@ typedef struct RNode {
#define nd_cfnc u1.cfunc
#define nd_argc u2.argc
-#define nd_cname u1.id
+#define nd_cpath u1.node
#define nd_super u3.node
#define nd_modl u1.id
@@ -232,106 +234,110 @@ typedef struct RNode {
#define nd_tag u1.id
#define nd_tval u2.value
-#define NEW_METHOD(n,x) rb_node_newnode(NODE_METHOD,x,n,0)
-#define NEW_FBODY(n,i,o) rb_node_newnode(NODE_FBODY,n,i,o)
-#define NEW_DEFN(i,a,d,p) rb_node_newnode(NODE_DEFN,p,i,NEW_RFUNC(a,d))
-#define NEW_DEFS(r,i,a,d) rb_node_newnode(NODE_DEFS,r,i,NEW_RFUNC(a,d))
-#define NEW_CFUNC(f,c) rb_node_newnode(NODE_CFUNC,f,c,0)
-#define NEW_IFUNC(f,c) rb_node_newnode(NODE_IFUNC,f,c,0)
+#define NEW_NODE(t,a0,a1,a2) rb_node_newnode((t),(VALUE)(a0),(VALUE)(a1),(VALUE)(a2))
+
+#define NEW_METHOD(n,x) NEW_NODE(NODE_METHOD,x,n,0)
+#define NEW_FBODY(n,i,o) NEW_NODE(NODE_FBODY,n,i,o)
+#define NEW_DEFN(i,a,d,p) NEW_NODE(NODE_DEFN,p,i,NEW_RFUNC(a,d))
+#define NEW_DEFS(r,i,a,d) NEW_NODE(NODE_DEFS,r,i,NEW_RFUNC(a,d))
+#define NEW_CFUNC(f,c) NEW_NODE(NODE_CFUNC,f,c,0)
+#define NEW_IFUNC(f,c) NEW_NODE(NODE_IFUNC,f,c,0)
#define NEW_RFUNC(b1,b2) NEW_SCOPE(block_append(b1,b2))
-#define NEW_SCOPE(b) rb_node_newnode(NODE_SCOPE,local_tbl(),0,(b))
-#define NEW_BLOCK(a) rb_node_newnode(NODE_BLOCK,a,0,0)
-#define NEW_IF(c,t,e) rb_node_newnode(NODE_IF,c,t,e)
+#define NEW_SCOPE(b) NEW_NODE(NODE_SCOPE,local_tbl(),0,(b))
+#define NEW_BLOCK(a) NEW_NODE(NODE_BLOCK,a,0,0)
+#define NEW_IF(c,t,e) NEW_NODE(NODE_IF,c,t,e)
#define NEW_UNLESS(c,t,e) NEW_IF(c,e,t)
-#define NEW_CASE(h,b) rb_node_newnode(NODE_CASE,h,b,0)
-#define NEW_WHEN(c,t,e) rb_node_newnode(NODE_WHEN,c,t,e)
-#define NEW_OPT_N(b) rb_node_newnode(NODE_OPT_N,0,b,0)
-#define NEW_WHILE(c,b,n) rb_node_newnode(NODE_WHILE,c,b,n)
-#define NEW_UNTIL(c,b,n) rb_node_newnode(NODE_UNTIL,c,b,n)
-#define NEW_FOR(v,i,b) rb_node_newnode(NODE_FOR,v,b,i)
-#define NEW_ITER(v,i,b) rb_node_newnode(NODE_ITER,v,b,i)
-#define NEW_BREAK(s) rb_node_newnode(NODE_BREAK,s,0,0)
-#define NEW_NEXT(s) rb_node_newnode(NODE_NEXT,s,0,0)
-#define NEW_REDO() rb_node_newnode(NODE_REDO,0,0,0)
-#define NEW_RETRY() rb_node_newnode(NODE_RETRY,0,0,0)
-#define NEW_BEGIN(b) rb_node_newnode(NODE_BEGIN,0,b,0)
-#define NEW_RESCUE(b,res,e) rb_node_newnode(NODE_RESCUE,b,res,e)
-#define NEW_RESBODY(a,ex,n) rb_node_newnode(NODE_RESBODY,n,ex,a)
-#define NEW_ENSURE(b,en) rb_node_newnode(NODE_ENSURE,b,0,en)
-#define NEW_RETURN(s) rb_node_newnode(NODE_RETURN,s,0,0)
-#define NEW_YIELD(a) rb_node_newnode(NODE_YIELD,a,0,0)
+#define NEW_CASE(h,b) NEW_NODE(NODE_CASE,h,b,0)
+#define NEW_WHEN(c,t,e) NEW_NODE(NODE_WHEN,c,t,e)
+#define NEW_OPT_N(b) NEW_NODE(NODE_OPT_N,0,b,0)
+#define NEW_WHILE(c,b,n) NEW_NODE(NODE_WHILE,c,b,n)
+#define NEW_UNTIL(c,b,n) NEW_NODE(NODE_UNTIL,c,b,n)
+#define NEW_FOR(v,i,b) NEW_NODE(NODE_FOR,v,b,i)
+#define NEW_ITER(v,i,b) NEW_NODE(NODE_ITER,v,b,i)
+#define NEW_BREAK(s) NEW_NODE(NODE_BREAK,s,0,0)
+#define NEW_NEXT(s) NEW_NODE(NODE_NEXT,s,0,0)
+#define NEW_REDO() NEW_NODE(NODE_REDO,0,0,0)
+#define NEW_RETRY() NEW_NODE(NODE_RETRY,0,0,0)
+#define NEW_BEGIN(b) NEW_NODE(NODE_BEGIN,0,b,0)
+#define NEW_RESCUE(b,res,e) NEW_NODE(NODE_RESCUE,b,res,e)
+#define NEW_RESBODY(a,ex,n) NEW_NODE(NODE_RESBODY,n,ex,a)
+#define NEW_ENSURE(b,en) NEW_NODE(NODE_ENSURE,b,0,en)
+#define NEW_RETURN(s) NEW_NODE(NODE_RETURN,s,0,0)
+#define NEW_YIELD(a,s) NEW_NODE(NODE_YIELD,a,0,s)
#define NEW_LIST(a) NEW_ARRAY(a)
-#define NEW_ARRAY(a) rb_node_newnode(NODE_ARRAY,a,1,0)
-#define NEW_ZARRAY() rb_node_newnode(NODE_ZARRAY,0,0,0)
-#define NEW_HASH(a) rb_node_newnode(NODE_HASH,a,0,0)
-#define NEW_NOT(a) rb_node_newnode(NODE_NOT,0,a,0)
-#define NEW_MASGN(l,r) rb_node_newnode(NODE_MASGN,l,0,r)
-#define NEW_GASGN(v,val) rb_node_newnode(NODE_GASGN,v,val,rb_global_entry(v))
-#define NEW_LASGN(v,val) rb_node_newnode(NODE_LASGN,v,val,local_cnt(v))
-#define NEW_DASGN(v,val) rb_node_newnode(NODE_DASGN,v,val,0)
-#define NEW_DASGN_CURR(v,val) rb_node_newnode(NODE_DASGN_CURR,v,val,0)
-#define NEW_IASGN(v,val) rb_node_newnode(NODE_IASGN,v,val,0)
-#define NEW_CDECL(v,val) rb_node_newnode(NODE_CDECL,v,val,0)
-#define NEW_CVASGN(v,val) rb_node_newnode(NODE_CVASGN,v,val,0)
-#define NEW_CVDECL(v,val) rb_node_newnode(NODE_CVDECL,v,val,0)
-#define NEW_OP_ASGN1(p,id,a) rb_node_newnode(NODE_OP_ASGN1,p,id,a)
-#define NEW_OP_ASGN2(r,i,o,val) rb_node_newnode(NODE_OP_ASGN2,r,val,NEW_OP_ASGN22(i,o))
-#define NEW_OP_ASGN22(i,o) rb_node_newnode(NODE_OP_ASGN2,i,o,rb_id_attrset(i))
-#define NEW_OP_ASGN_OR(i,val) rb_node_newnode(NODE_OP_ASGN_OR,i,val,0)
-#define NEW_OP_ASGN_AND(i,val) rb_node_newnode(NODE_OP_ASGN_AND,i,val,0)
-#define NEW_GVAR(v) rb_node_newnode(NODE_GVAR,v,0,rb_global_entry(v))
-#define NEW_LVAR(v) rb_node_newnode(NODE_LVAR,v,0,local_cnt(v))
-#define NEW_DVAR(v) rb_node_newnode(NODE_DVAR,v,0,0);
-#define NEW_IVAR(v) rb_node_newnode(NODE_IVAR,v,0,0)
-#define NEW_CONST(v) rb_node_newnode(NODE_CONST,v,0,0)
-#define NEW_CVAR(v) rb_node_newnode(NODE_CVAR,v,0,0)
-#define NEW_NTH_REF(n) rb_node_newnode(NODE_NTH_REF,0,n,local_cnt('~'))
-#define NEW_BACK_REF(n) rb_node_newnode(NODE_BACK_REF,0,n,local_cnt('~'))
-#define NEW_MATCH(c) rb_node_newnode(NODE_MATCH,c,0,0)
-#define NEW_MATCH2(n1,n2) rb_node_newnode(NODE_MATCH2,n1,n2,0)
-#define NEW_MATCH3(r,n2) rb_node_newnode(NODE_MATCH3,r,n2,0)
-#define NEW_LIT(l) rb_node_newnode(NODE_LIT,l,0,0)
-#define NEW_STR(s) rb_node_newnode(NODE_STR,s,0,0)
-#define NEW_DSTR(s) rb_node_newnode(NODE_DSTR,s,0,0)
-#define NEW_XSTR(s) rb_node_newnode(NODE_XSTR,s,0,0)
-#define NEW_DXSTR(s) rb_node_newnode(NODE_DXSTR,s,0,0)
-#define NEW_DSYM(s) rb_node_newnode(NODE_DSYM,s,0,0)
-#define NEW_EVSTR(n) rb_node_newnode(NODE_EVSTR,0,(n),0)
-#define NEW_CALL(r,m,a) rb_node_newnode(NODE_CALL,r,m,a)
-#define NEW_FCALL(m,a) rb_node_newnode(NODE_FCALL,0,m,a)
-#define NEW_VCALL(m) rb_node_newnode(NODE_VCALL,0,m,0)
-#define NEW_SUPER(a) rb_node_newnode(NODE_SUPER,0,0,a)
-#define NEW_ZSUPER() rb_node_newnode(NODE_ZSUPER,0,0,0)
-#define NEW_ARGS(f,o,r) rb_node_newnode(NODE_ARGS,o,r,f)
-#define NEW_ARGSCAT(a,b) rb_node_newnode(NODE_ARGSCAT,a,b,0)
-#define NEW_ARGSPUSH(a,b) rb_node_newnode(NODE_ARGSPUSH,a,b,0)
-#define NEW_RESTARGS(a) rb_node_newnode(NODE_RESTARGS,a,0,0)
-#define NEW_RESTARY(a) rb_node_newnode(NODE_RESTARY,a,0,0)
-#define NEW_REXPAND(a) rb_node_newnode(NODE_REXPAND,a,0,0)
-#define NEW_BLOCK_ARG(v) rb_node_newnode(NODE_BLOCK_ARG,v,0,local_cnt(v))
-#define NEW_BLOCK_PASS(b) rb_node_newnode(NODE_BLOCK_PASS,0,b,0)
-#define NEW_ALIAS(n,o) rb_node_newnode(NODE_ALIAS,o,n,0)
-#define NEW_VALIAS(n,o) rb_node_newnode(NODE_VALIAS,o,n,0)
-#define NEW_UNDEF(i) rb_node_newnode(NODE_UNDEF,0,i,0)
-#define NEW_CLASS(n,b,s) rb_node_newnode(NODE_CLASS,n,NEW_SCOPE(b),(s))
-#define NEW_SCLASS(r,b) rb_node_newnode(NODE_SCLASS,r,NEW_SCOPE(b),0)
-#define NEW_MODULE(n,b) rb_node_newnode(NODE_MODULE,n,NEW_SCOPE(b),0)
-#define NEW_COLON2(c,i) rb_node_newnode(NODE_COLON2,c,i,0)
-#define NEW_COLON3(i) rb_node_newnode(NODE_COLON3,0,i,0)
-#define NEW_CREF(c) (rb_node_newnode(NODE_CREF,0,0,c))
-#define NEW_DOT2(b,e) rb_node_newnode(NODE_DOT2,b,e,0)
-#define NEW_DOT3(b,e) rb_node_newnode(NODE_DOT3,b,e,0)
-#define NEW_ATTRSET(a) rb_node_newnode(NODE_ATTRSET,a,0,0)
-#define NEW_SELF() rb_node_newnode(NODE_SELF,0,0,0)
-#define NEW_NIL() rb_node_newnode(NODE_NIL,0,0,0)
-#define NEW_TRUE() rb_node_newnode(NODE_TRUE,0,0,0)
-#define NEW_FALSE() rb_node_newnode(NODE_FALSE,0,0,0)
-#define NEW_DEFINED(e) rb_node_newnode(NODE_DEFINED,e,0,0)
-#define NEW_NEWLINE(n) rb_node_newnode(NODE_NEWLINE,0,0,n)
+#define NEW_ARRAY(a) NEW_NODE(NODE_ARRAY,a,1,0)
+#define NEW_ZARRAY() NEW_NODE(NODE_ZARRAY,0,0,0)
+#define NEW_HASH(a) NEW_NODE(NODE_HASH,a,0,0)
+#define NEW_NOT(a) NEW_NODE(NODE_NOT,0,a,0)
+#define NEW_MASGN(l,r) NEW_NODE(NODE_MASGN,l,0,r)
+#define NEW_GASGN(v,val) NEW_NODE(NODE_GASGN,v,val,rb_global_entry(v))
+#define NEW_LASGN(v,val) NEW_NODE(NODE_LASGN,v,val,local_cnt(v))
+#define NEW_DASGN(v,val) NEW_NODE(NODE_DASGN,v,val,0)
+#define NEW_DASGN_CURR(v,val) NEW_NODE(NODE_DASGN_CURR,v,val,0)
+#define NEW_IASGN(v,val) NEW_NODE(NODE_IASGN,v,val,0)
+#define NEW_CDECL(v,val,path) NEW_NODE(NODE_CDECL,v,val,path)
+#define NEW_CVASGN(v,val) NEW_NODE(NODE_CVASGN,v,val,0)
+#define NEW_CVDECL(v,val) NEW_NODE(NODE_CVDECL,v,val,0)
+#define NEW_OP_ASGN1(p,id,a) NEW_NODE(NODE_OP_ASGN1,p,id,a)
+#define NEW_OP_ASGN2(r,i,o,val) NEW_NODE(NODE_OP_ASGN2,r,val,NEW_OP_ASGN22(i,o))
+#define NEW_OP_ASGN22(i,o) NEW_NODE(NODE_OP_ASGN2,i,o,rb_id_attrset(i))
+#define NEW_OP_ASGN_OR(i,val) NEW_NODE(NODE_OP_ASGN_OR,i,val,0)
+#define NEW_OP_ASGN_AND(i,val) NEW_NODE(NODE_OP_ASGN_AND,i,val,0)
+#define NEW_GVAR(v) NEW_NODE(NODE_GVAR,v,0,rb_global_entry(v))
+#define NEW_LVAR(v) NEW_NODE(NODE_LVAR,v,0,local_cnt(v))
+#define NEW_DVAR(v) NEW_NODE(NODE_DVAR,v,0,0);
+#define NEW_IVAR(v) NEW_NODE(NODE_IVAR,v,0,0)
+#define NEW_CONST(v) NEW_NODE(NODE_CONST,v,0,0)
+#define NEW_CVAR(v) NEW_NODE(NODE_CVAR,v,0,0)
+#define NEW_NTH_REF(n) NEW_NODE(NODE_NTH_REF,0,n,local_cnt('~'))
+#define NEW_BACK_REF(n) NEW_NODE(NODE_BACK_REF,0,n,local_cnt('~'))
+#define NEW_MATCH(c) NEW_NODE(NODE_MATCH,c,0,0)
+#define NEW_MATCH2(n1,n2) NEW_NODE(NODE_MATCH2,n1,n2,0)
+#define NEW_MATCH3(r,n2) NEW_NODE(NODE_MATCH3,r,n2,0)
+#define NEW_LIT(l) NEW_NODE(NODE_LIT,l,0,0)
+#define NEW_STR(s) NEW_NODE(NODE_STR,s,0,0)
+#define NEW_DSTR(s) NEW_NODE(NODE_DSTR,s,0,0)
+#define NEW_XSTR(s) NEW_NODE(NODE_XSTR,s,0,0)
+#define NEW_DXSTR(s) NEW_NODE(NODE_DXSTR,s,0,0)
+#define NEW_DSYM(s) NEW_NODE(NODE_DSYM,s,0,0)
+#define NEW_EVSTR(n) NEW_NODE(NODE_EVSTR,0,(n),0)
+#define NEW_CALL(r,m,a) NEW_NODE(NODE_CALL,r,m,a)
+#define NEW_FCALL(m,a) NEW_NODE(NODE_FCALL,0,m,a)
+#define NEW_VCALL(m) NEW_NODE(NODE_VCALL,0,m,0)
+#define NEW_SUPER(a) NEW_NODE(NODE_SUPER,0,0,a)
+#define NEW_ZSUPER() NEW_NODE(NODE_ZSUPER,0,0,0)
+#define NEW_ARGS(f,o,r) NEW_NODE(NODE_ARGS,o,r,f)
+#define NEW_ARGSCAT(a,b) NEW_NODE(NODE_ARGSCAT,a,b,0)
+#define NEW_ARGSPUSH(a,b) NEW_NODE(NODE_ARGSPUSH,a,b,0)
+#define NEW_RESTARY(a) NEW_NODE(NODE_RESTARY,a,0,0)
+#define NEW_RESTARY2(a) NEW_NODE(NODE_RESTARY2,a,0,0)
+#define NEW_SPLAT(a) NEW_NODE(NODE_SPLAT,a,0,0)
+#define NEW_SVALUE(a) NEW_NODE(NODE_SVALUE,a,0,0)
+#define NEW_BLOCK_ARG(v) NEW_NODE(NODE_BLOCK_ARG,v,0,local_cnt(v))
+#define NEW_BLOCK_PASS(b) NEW_NODE(NODE_BLOCK_PASS,0,b,0)
+#define NEW_ALIAS(n,o) NEW_NODE(NODE_ALIAS,o,n,0)
+#define NEW_VALIAS(n,o) NEW_NODE(NODE_VALIAS,o,n,0)
+#define NEW_UNDEF(i) NEW_NODE(NODE_UNDEF,0,i,0)
+#define NEW_CLASS(n,b,s) NEW_NODE(NODE_CLASS,n,NEW_SCOPE(b),(s))
+#define NEW_SCLASS(r,b) NEW_NODE(NODE_SCLASS,r,NEW_SCOPE(b),0)
+#define NEW_MODULE(n,b) NEW_NODE(NODE_MODULE,n,NEW_SCOPE(b),0)
+#define NEW_COLON2(c,i) NEW_NODE(NODE_COLON2,c,i,0)
+#define NEW_COLON3(i) NEW_NODE(NODE_COLON3,0,i,0)
+#define NEW_CREF(c) (NEW_NODE(NODE_CREF,0,0,c))
+#define NEW_DOT2(b,e) NEW_NODE(NODE_DOT2,b,e,0)
+#define NEW_DOT3(b,e) NEW_NODE(NODE_DOT3,b,e,0)
+#define NEW_ATTRSET(a) NEW_NODE(NODE_ATTRSET,a,0,0)
+#define NEW_SELF() NEW_NODE(NODE_SELF,0,0,0)
+#define NEW_NIL() NEW_NODE(NODE_NIL,0,0,0)
+#define NEW_TRUE() NEW_NODE(NODE_TRUE,0,0,0)
+#define NEW_FALSE() NEW_NODE(NODE_FALSE,0,0,0)
+#define NEW_DEFINED(e) NEW_NODE(NODE_DEFINED,e,0,0)
+#define NEW_NEWLINE(n) NEW_NODE(NODE_NEWLINE,0,0,n)
#define NEW_PREEXE(b) NEW_SCOPE(b)
-#define NEW_POSTEXE() rb_node_newnode(NODE_POSTEXE,0,0,0)
-#define NEW_DMETHOD(b) rb_node_newnode(NODE_DMETHOD,0,0,b)
-#define NEW_BMETHOD(b) rb_node_newnode(NODE_BMETHOD,0,0,b)
+#define NEW_POSTEXE() NEW_NODE(NODE_POSTEXE,0,0,0)
+#define NEW_DMETHOD(b) NEW_NODE(NODE_DMETHOD,0,0,b)
+#define NEW_BMETHOD(b) NEW_NODE(NODE_BMETHOD,0,0,b)
+#define NEW_ATTRASGN(r,m,a) NEW_NODE(NODE_ATTRASGN,r,m,a)
#define NOEX_PUBLIC 0
#define NOEX_NOSUPER 1
@@ -340,14 +346,13 @@ typedef struct RNode {
#define NOEX_MASK 6
#define NOEX_UNDEF NOEX_NOSUPER
-#define NOEX_CFUNC NOEX_NOSUPER
NODE *rb_compile_cstr _((const char*, const char*, int, int));
NODE *rb_compile_string _((const char*, VALUE, int));
NODE *rb_compile_file _((const char*, VALUE, int));
void rb_add_method _((VALUE, ID, NODE *, int));
-NODE *rb_node_newnode(ANYARGS);
+NODE *rb_node_newnode _((enum node_type,VALUE,VALUE,VALUE));
struct global_entry *rb_global_entry _((ID));
VALUE rb_gvar_get _((struct global_entry *));
diff --git a/numeric.c b/numeric.c
index c1e064588b..d9278f06ba 100644
--- a/numeric.c
+++ b/numeric.c
@@ -6,18 +6,59 @@
$Date$
created at: Fri Aug 13 18:33:09 JST 1993
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
**********************************************************************/
#include "ruby.h"
+#include "env.h"
#include <math.h>
#include <stdio.h>
+
#if defined(__FreeBSD__) && __FreeBSD__ < 4
#include <floatingpoint.h>
#endif
-static ID id_coerce, id_to_i, id_div;
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+
+/* use IEEE 64bit values if not defined */
+#ifndef FLT_RADIX
+#define FLT_RADIX 2
+#endif
+#ifndef FLT_ROUNDS
+#define FLT_ROUNDS 1
+#endif
+#ifndef DBL_MIN
+#define DBL_MIN 2.2250738585072014e-308
+#endif
+#ifndef DBL_MAX
+#define DBL_MAX 1.7976931348623157e+308
+#endif
+#ifndef DBL_MIN_EXP
+#define DBL_MIN_EXP (-1021)
+#endif
+#ifndef DBL_MAX_EXP
+#define DBL_MAX_EXP 1024
+#endif
+#ifndef DBL_MIN_10_EXP
+#define DBL_MIN_10_EXP (-307)
+#endif
+#ifndef DBL_MAX_10_EXP
+#define DBL_MAX_10_EXP 308
+#endif
+#ifndef DBL_DIG
+#define DBL_DIG 15
+#endif
+#ifndef DBL_MANT_DIG
+#define DBL_MANT_DIG 53
+#endif
+#ifndef DBL_EPSILON
+#define DBL_EPSILON 2.2204460492503131e-16
+#endif
+
+static ID id_coerce, id_to_i, id_eq;
VALUE rb_cNumeric;
VALUE rb_cFloat;
@@ -53,46 +94,76 @@ static VALUE
coerce_rescue(x)
VALUE *x;
{
+ volatile VALUE v = rb_inspect(x[1]);
+
rb_raise(rb_eTypeError, "%s can't be coerced into %s",
rb_special_const_p(x[1])?
- RSTRING(rb_inspect(x[1]))->ptr:
- rb_class2name(CLASS_OF(x[1])),
- rb_class2name(CLASS_OF(x[0])));
+ RSTRING(v)->ptr:
+ rb_obj_classname(x[1]),
+ rb_obj_classname(x[0]));
return Qnil; /* dummy */
}
-static void
-do_coerce(x, y)
+static int
+do_coerce(x, y, err)
VALUE *x, *y;
+ int err;
{
VALUE ary;
VALUE a[2];
a[0] = *x; a[1] = *y;
- ary = rb_rescue(coerce_body, (VALUE)a, coerce_rescue, (VALUE)a);
+ ary = rb_rescue(coerce_body, (VALUE)a, err?coerce_rescue:0, (VALUE)a);
if (TYPE(ary) != T_ARRAY || RARRAY(ary)->len != 2) {
- rb_raise(rb_eTypeError, "coerce must return [x, y]");
+ if (err) {
+ rb_raise(rb_eTypeError, "coerce must return [x, y]");
+ }
+ return Qfalse;
}
*x = RARRAY(ary)->ptr[0];
*y = RARRAY(ary)->ptr[1];
+ return Qtrue;
}
VALUE
rb_num_coerce_bin(x, y)
VALUE x, y;
{
- do_coerce(&x, &y);
- return rb_funcall(x, rb_frame_last_func(), 1, y);
+ do_coerce(&x, &y, Qtrue);
+ return rb_funcall(x, ruby_frame->orig_func, 1, y);
+}
+
+VALUE
+rb_num_coerce_cmp(x, y)
+ VALUE x, y;
+{
+ if (do_coerce(&x, &y, Qfalse))
+ return rb_funcall(x, ruby_frame->orig_func, 1, y);
+ return Qnil;
+}
+
+VALUE
+rb_num_coerce_relop(x, y)
+ VALUE x, y;
+{
+ VALUE c, x0 = x, y0 = y;
+
+ if (!do_coerce(&x, &y, Qfalse) ||
+ NIL_P(c = rb_funcall(x, ruby_frame->orig_func, 1, y))) {
+ rb_cmperr(x0, y0);
+ return Qnil; /* not reached */
+ }
+ return c;
}
static VALUE
-num_copy_object(x, y)
+num_init_copy(x, y)
VALUE x, y;
{
/* Numerics are immutable values, which should not be copied */
- rb_raise(rb_eTypeError, "can't copy %s", rb_class2name(CLASS_OF(x)));
+ rb_raise(rb_eTypeError, "can't copy %s", rb_obj_classname(x));
return Qnil; /* not reached */
}
@@ -110,34 +181,30 @@ num_uminus(num)
VALUE zero;
zero = INT2FIX(0);
- do_coerce(&zero, &num);
+ do_coerce(&zero, &num, Qtrue);
return rb_funcall(zero, '-', 1, num);
}
static VALUE
+num_quo(x, y)
+ VALUE x, y;
+{
+ return rb_funcall(x, '/', 1, y);
+}
+
+static VALUE
num_div(x, y)
VALUE x, y;
{
- return rb_funcall(x, id_div, 1, y);
+ return rb_Integer(rb_funcall(x, '/', 1, y));
}
static VALUE
num_divmod(x, y)
VALUE x, y;
{
- VALUE div, mod;
-
- div = rb_funcall(x, id_div, 1, y);
- if (TYPE(div) == T_FLOAT) {
- double d = floor(RFLOAT(div)->value);
-
- if (RFLOAT(div)->value > d) {
- div = rb_float_new(d);
- }
- }
- mod = rb_funcall(x, '%', 1, y);
- return rb_assoc_new(div, mod);
+ return rb_assoc_new(num_div(x, y), rb_funcall(x, '%', 1, y));
}
static VALUE
@@ -223,7 +290,7 @@ flo_to_s(flt)
VALUE flt;
{
char buf[24];
- char *fmt = "%.16g";
+ char *fmt = "%.15g";
double value = RFLOAT(flt)->value;
double avalue, d1, d2;
@@ -449,24 +516,32 @@ static VALUE
num_equal(x, y)
VALUE x, y;
{
- return rb_equal(y, x);
+ if (x == y) return Qtrue;
+ return rb_funcall(y, id_eq, 1, x);
}
static VALUE
flo_eq(x, y)
VALUE x, y;
{
+ double a, b;
+
switch (TYPE(y)) {
case T_FIXNUM:
- if (RFLOAT(x)->value == FIX2LONG(y)) return Qtrue;
- return Qfalse;
+ b = FIX2LONG(y);
+ break;
case T_BIGNUM:
- return (RFLOAT(x)->value == rb_big2dbl(y))?Qtrue:Qfalse;
+ b = rb_big2dbl(y);
+ break;
case T_FLOAT:
- return (RFLOAT(x)->value == RFLOAT(y)->value)?Qtrue:Qfalse;
+ b = RFLOAT(y)->value;
+ break;
default:
return num_equal(x, y);
}
+ a = RFLOAT(x)->value;
+ if (isnan(a) || isnan(b)) return Qfalse;
+ return (a == b)?Qtrue:Qfalse;
}
static VALUE
@@ -491,10 +566,11 @@ VALUE
rb_dbl_cmp(a, b)
double a, b;
{
+ if (isnan(a) || isnan(b)) return Qnil;
if (a == b) return INT2FIX(0);
if (a > b) return INT2FIX(1);
if (a < b) return INT2FIX(-1);
- rb_raise(rb_eFloatDomainError, "comparing NaN");
+ return Qnil;
}
static VALUE
@@ -518,7 +594,7 @@ flo_cmp(x, y)
break;
default:
- return rb_num_coerce_bin(x, y);
+ return rb_num_coerce_cmp(x, y);
}
return rb_dbl_cmp(a, b);
}
@@ -544,8 +620,9 @@ flo_gt(x, y)
break;
default:
- return rb_num_coerce_bin(x, y);
+ return rb_num_coerce_relop(x, y);
}
+ if (isnan(a) || isnan(b)) return Qfalse;
return (a > b)?Qtrue:Qfalse;
}
@@ -570,8 +647,9 @@ flo_ge(x, y)
break;
default:
- return rb_num_coerce_bin(x, y);
+ return rb_num_coerce_relop(x, y);
}
+ if (isnan(a) || isnan(b)) return Qfalse;
return (a >= b)?Qtrue:Qfalse;
}
@@ -596,8 +674,9 @@ flo_lt(x, y)
break;
default:
- return rb_num_coerce_bin(x, y);
+ return rb_num_coerce_relop(x, y);
}
+ if (isnan(a) || isnan(b)) return Qfalse;
return (a < b)?Qtrue:Qfalse;
}
@@ -622,8 +701,9 @@ flo_le(x, y)
break;
default:
- return rb_num_coerce_bin(x, y);
+ return rb_num_coerce_relop(x, y);
}
+ if (isnan(a) || isnan(b)) return Qfalse;
return (a <= b)?Qtrue:Qfalse;
}
@@ -631,8 +711,12 @@ static VALUE
flo_eql(x, y)
VALUE x, y;
{
- if (TYPE(y) == T_FLOAT && RFLOAT(x)->value == RFLOAT(y)->value) {
- return Qtrue;
+ if (TYPE(y) == T_FLOAT) {
+ double a = RFLOAT(x)->value;
+ double b = RFLOAT(y)->value;
+
+ if (isnan(a) || isnan(b)) return Qfalse;
+ if (a == b) return Qtrue;
}
return Qfalse;
}
@@ -666,37 +750,40 @@ static VALUE
flo_is_nan_p(num)
VALUE num;
{
+ double value = RFLOAT(num)->value;
- double value = RFLOAT(num)->value;
-
- return isnan(value) ? Qtrue : Qfalse;
+ return isnan(value) ? Qtrue : Qfalse;
}
static VALUE
flo_is_infinite_p(num)
VALUE num;
{
- double value = RFLOAT(num)->value;
+ double value = RFLOAT(num)->value;
- if (isinf(value)) {
- return INT2FIX( value < 0 ? -1 : 1 );
- }
+ if (isinf(value)) {
+ return INT2FIX( value < 0 ? -1 : 1 );
+ }
- return Qnil;
+ return Qnil;
}
static VALUE
flo_is_finite_p(num)
VALUE num;
{
- double value = RFLOAT(num)->value;
+ double value = RFLOAT(num)->value;
- if (isinf(value) || isnan(value))
- return Qfalse;
-
- return Qtrue;
-}
+#if HAVE_FINITE
+ if (!finite(value))
+ return Qfalse;
+#else
+ if (isinf(value) || isnan(value))
+ return Qfalse;
+#endif
+ return Qtrue;
+}
static VALUE
flo_floor(num)
@@ -796,11 +883,21 @@ num_step(argc, argv, from)
{
VALUE to, step;
- if (rb_scan_args(argc, argv, "11", &to, &step) == 1) {
+ if (argc == 1) {
+ to = argv[0];
step = INT2FIX(1);
}
- else if (rb_equal(step, INT2FIX(0))) {
- rb_raise(rb_eArgError, "step cannot be 0");
+ else {
+ if (argc == 2) {
+ to = argv[0];
+ step = argv[1];
+ }
+ else {
+ rb_raise(rb_eArgError, "wrong number of arguments");
+ }
+ if (rb_equal(step, INT2FIX(0))) {
+ rb_raise(rb_eArgError, "step cannot be 0");
+ }
}
if (FIXNUM_P(from) && FIXNUM_P(to) && FIXNUM_P(step)) {
@@ -824,14 +921,16 @@ num_step(argc, argv, from)
}
}
else if (TYPE(from) == T_FLOAT || TYPE(to) == T_FLOAT || TYPE(step) == T_FLOAT) {
- const double epsilon = 2.2204460492503131E-16;
+ const double epsilon = DBL_EPSILON;
double beg = NUM2DBL(from);
double end = NUM2DBL(to);
double unit = NUM2DBL(step);
double n = (end - beg)/unit;
+ double err = (fabs(beg) + fabs(end) + fabs(end-beg)) / fabs(unit) * epsilon;
long i;
- n = floor(n + n*epsilon) + 1;
+ if (err>0.5) err=0.5;
+ n = floor(n + err) + 1;
for (i=0; i<n; i++) {
rb_yield(rb_float_new(i*unit+beg));
}
@@ -883,6 +982,9 @@ rb_num2long(val)
case T_BIGNUM:
return rb_big2long(val);
+ case T_SYMBOL:
+ rb_warning("treating Symbol as an integer");
+ /* fall through */
default:
val = rb_to_int(val);
return NUM2LONG(val);
@@ -900,15 +1002,40 @@ rb_num2ulong(val)
}
#if SIZEOF_INT < SIZEOF_LONG
+static void
+check_int(num)
+ long num;
+{
+ const char *s;
+
+ if (num < INT_MIN) {
+ s = "small";
+ }
+ else if (num > INT_MAX) {
+ s = "big";
+ }
+ else {
+ return;
+ }
+ rb_raise(rb_eRangeError, "integer %ld too %s to convert to `int'", num, s);
+}
+
+static void
+check_uint(num)
+ unsigned long num;
+{
+ if (num > INT_MAX) {
+ rb_raise(rb_eRangeError, "integer %lu too big to convert to `int'", num);
+ }
+}
+
int
rb_num2int(val)
VALUE val;
{
long num = rb_num2long(val);
- if (num < INT_MIN || INT_MAX < num) {
- rb_raise(rb_eRangeError, "integer %ld too big to convert to `int'", num);
- }
+ check_int(num);
return (int)num;
}
@@ -918,9 +1045,27 @@ rb_fix2int(val)
{
long num = FIXNUM_P(val)?FIX2LONG(val):rb_num2long(val);
- if (num < INT_MIN || INT_MAX < num) {
- rb_raise(rb_eRangeError, "integer %ld too big to convert to `int'", num);
- }
+ check_int(num);
+ return (int)num;
+}
+
+unsigned int
+rb_num2uint(val)
+ VALUE val;
+{
+ unsigned long num = rb_num2ulong(val);
+
+ check_uint(num);
+ return (int)num;
+}
+
+unsigned int
+rb_fix2uint(val)
+ VALUE val;
+{
+ unsigned long num = FIXNUM_P(val)?FIX2LONG(val):rb_num2ulong(val);
+
+ check_uint(num);
return (int)num;
}
#else
@@ -1067,7 +1212,7 @@ rb_int_induced_from(klass, x)
return rb_funcall(x, id_to_i, 0);
default:
rb_raise(rb_eTypeError, "failed to convert %s into Integer",
- rb_class2name(CLASS_OF(x)));
+ rb_obj_classname(x));
}
}
@@ -1083,7 +1228,7 @@ rb_flo_induced_from(klass, x)
return x;
default:
rb_raise(rb_eTypeError, "failed to convert %s into Float",
- rb_class2name(CLASS_OF(x)));
+ rb_obj_classname(x));
}
}
@@ -1099,21 +1244,30 @@ rb_fix2str(x, base)
VALUE x;
int base;
{
- char fmt[4], buf[22], *b = buf;
+ extern const char ruby_digitmap[];
+ char buf[SIZEOF_LONG*CHAR_BIT + 2], *b = buf + sizeof buf;
long val = FIX2LONG(x);
+ int neg = 0;
- fmt[0] = '%'; fmt[1] = 'l'; fmt[3] = '\0';
- if (base == 10) fmt[2] = 'd';
- else if (base == 16) fmt[2] = 'x';
- else if (base == 8) fmt[2] = 'o';
- else rb_raise(rb_eArgError, "illegal radix %d", base);
+ if (base < 2 || 36 < base) {
+ rb_raise(rb_eArgError, "illegal radix %d", base);
+ }
+ if (val == 0) {
+ return rb_str_new2("0");
+ }
if (val < 0) {
val = -val;
- *b++ = '-';
+ neg = 1;
+ }
+ *--b = '\0';
+ do {
+ *--b = ruby_digitmap[(int)(val % base)];
+ } while (val /= base);
+ if (neg) {
+ *--b = '-';
}
- sprintf(b, fmt, val);
- return rb_str_new2(buf);
+ return rb_str_new2(b);
}
static VALUE
@@ -1240,6 +1394,16 @@ fixdivmod(x, y, divp, modp)
}
static VALUE
+fix_quo(x, y)
+ VALUE x, y;
+{
+ if (FIXNUM_P(y)) {
+ return rb_float_new((double)FIX2LONG(x) / (double)FIX2LONG(y));
+ }
+ return rb_num_coerce_bin(x, y);
+}
+
+static VALUE
fix_div(x, y)
VALUE x, y;
{
@@ -1322,7 +1486,7 @@ fix_cmp(x, y)
return INT2FIX(-1);
}
else {
- return rb_num_coerce_bin(x, y);
+ return rb_num_coerce_cmp(x, y);
}
}
@@ -1337,7 +1501,7 @@ fix_gt(x, y)
return Qfalse;
}
else {
- return rb_num_coerce_bin(x, y);
+ return rb_num_coerce_relop(x, y);
}
}
@@ -1352,7 +1516,7 @@ fix_ge(x, y)
return Qfalse;
}
else {
- return rb_num_coerce_bin(x, y);
+ return rb_num_coerce_relop(x, y);
}
}
@@ -1367,7 +1531,7 @@ fix_lt(x, y)
return Qfalse;
}
else {
- return rb_num_coerce_bin(x, y);
+ return rb_num_coerce_relop(x, y);
}
}
@@ -1382,7 +1546,7 @@ fix_le(x, y)
return Qfalse;
}
else {
- return rb_num_coerce_bin(x, y);
+ return rb_num_coerce_relop(x, y);
}
}
@@ -1564,13 +1728,13 @@ int_upto(from, to)
}
}
else {
- VALUE i = from;
+ VALUE i = from, c;
- for (;;) {
- if (RTEST(rb_funcall(i, '>', 1, to))) break;
+ while (!(c = rb_funcall(i, '>', 1, to))) {
rb_yield(i);
i = rb_funcall(i, '+', 1, INT2FIX(1));
}
+ if (NIL_P(c)) rb_cmperr(i, to);
}
return from;
}
@@ -1588,13 +1752,13 @@ int_downto(from, to)
}
}
else {
- VALUE i = from;
+ VALUE i = from, c;
- for (;;) {
- if (RTEST(rb_funcall(i, '<', 1, to))) break;
+ while (!(c = rb_funcall(i, '<', 1, to))) {
rb_yield(i);
i = rb_funcall(i, '-', 1, INT2FIX(1));
}
+ if (NIL_P(c)) rb_cmperr(i, to);
}
return from;
}
@@ -1642,22 +1806,22 @@ Init_Numeric()
#endif
id_coerce = rb_intern("coerce");
id_to_i = rb_intern("to_i");
- id_div = rb_intern("div");
+ id_eq = rb_intern("==");
rb_eZeroDivError = rb_define_class("ZeroDivisionError", rb_eStandardError);
rb_eFloatDomainError = rb_define_class("FloatDomainError", rb_eRangeError);
rb_cNumeric = rb_define_class("Numeric", rb_cObject);
rb_include_module(rb_cNumeric, rb_mComparable);
+ rb_define_method(rb_cNumeric, "initialize_copy", num_init_copy, 1);
rb_define_method(rb_cNumeric, "coerce", num_coerce, 1);
- rb_define_method(rb_cNumeric, "copy_object", num_copy_object, 1);
rb_define_method(rb_cNumeric, "+@", num_uplus, 0);
rb_define_method(rb_cNumeric, "-@", num_uminus, 0);
- rb_define_method(rb_cNumeric, "===", num_equal, 1);
rb_define_method(rb_cNumeric, "<=>", num_cmp, 1);
rb_define_method(rb_cNumeric, "eql?", num_eql, 1);
- rb_define_method(rb_cNumeric, "/", num_div, 1);
+ rb_define_method(rb_cNumeric, "quo", num_quo, 1);
+ rb_define_method(rb_cNumeric, "div", num_div, 1);
rb_define_method(rb_cNumeric, "divmod", num_divmod, 1);
rb_define_method(rb_cNumeric, "modulo", num_modulo, 1);
rb_define_method(rb_cNumeric, "remainder", num_remainder, 1);
@@ -1675,7 +1839,7 @@ Init_Numeric()
rb_define_method(rb_cNumeric, "step", num_step, -1);
rb_cInteger = rb_define_class("Integer", rb_cNumeric);
- rb_undef_method(CLASS_OF(rb_cInteger), "allocate");
+ rb_undef_alloc_func(rb_cInteger);
rb_undef_method(CLASS_OF(rb_cInteger), "new");
rb_define_method(rb_cInteger, "integer?", int_int_p, 0);
@@ -1712,6 +1876,7 @@ Init_Numeric()
rb_define_method(rb_cFixnum, "%", fix_mod, 1);
rb_define_method(rb_cFixnum, "modulo", fix_mod, 1);
rb_define_method(rb_cFixnum, "divmod", fix_divmod, 1);
+ rb_define_method(rb_cFixnum, "quo", fix_quo, 1);
rb_define_method(rb_cFixnum, "**", fix_pow, 1);
rb_define_method(rb_cFixnum, "abs", fix_abs, 0);
@@ -1738,12 +1903,24 @@ Init_Numeric()
rb_cFloat = rb_define_class("Float", rb_cNumeric);
- rb_undef_method(CLASS_OF(rb_cFloat), "allocate");
+ rb_undef_alloc_func(rb_cFloat);
rb_undef_method(CLASS_OF(rb_cFloat), "new");
rb_define_singleton_method(rb_cFloat, "induced_from", rb_flo_induced_from, 1);
rb_include_module(rb_cFloat, rb_mPrecision);
+ rb_define_const(rb_cFloat, "ROUNDS", INT2FIX(FLT_ROUNDS));
+ rb_define_const(rb_cFloat, "RADIX", INT2FIX(FLT_RADIX));
+ rb_define_const(rb_cFloat, "MANT_DIG", INT2FIX(DBL_MANT_DIG));
+ rb_define_const(rb_cFloat, "DIG", INT2FIX(DBL_DIG));
+ rb_define_const(rb_cFloat, "MIN_EXP", INT2FIX(DBL_MIN_EXP));
+ rb_define_const(rb_cFloat, "MAX_EXP", INT2FIX(DBL_MAX_EXP));
+ rb_define_const(rb_cFloat, "MIN_10_EXP", INT2FIX(DBL_MIN_10_EXP));
+ rb_define_const(rb_cFloat, "MAX_10_EXP", INT2FIX(DBL_MAX_10_EXP));
+ rb_define_const(rb_cFloat, "MIN", rb_float_new(DBL_MIN));
+ rb_define_const(rb_cFloat, "MAX", rb_float_new(DBL_MAX));
+ rb_define_const(rb_cFloat, "EPSILON", rb_float_new(DBL_EPSILON));
+
rb_define_method(rb_cFloat, "to_s", flo_to_s, 0);
rb_define_method(rb_cFloat, "coerce", flo_coerce, 1);
rb_define_method(rb_cFloat, "-@", flo_uminus, 0);
@@ -1778,4 +1955,3 @@ Init_Numeric()
rb_define_method(rb_cFloat, "infinite?", flo_is_infinite_p, 0);
rb_define_method(rb_cFloat, "finite?", flo_is_finite_p, 0);
}
-
diff --git a/object.c b/object.c
index 7eed51b3d8..712c8b7581 100644
--- a/object.c
+++ b/object.c
@@ -6,7 +6,7 @@
$Date$
created at: Thu Jul 15 12:01:24 JST 1993
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
Copyright (C) 2000 Information-technology Promotion Agency, Japan
@@ -31,10 +31,7 @@ VALUE rb_cTrueClass;
VALUE rb_cFalseClass;
VALUE rb_cSymbol;
-static ID eq, eql;
-static ID inspect;
-static ID copy_obj;
-static ID alloc;
+static ID id_eq, id_eql, id_inspect, id_init_copy;
VALUE
rb_equal(obj1, obj2)
@@ -43,7 +40,7 @@ rb_equal(obj1, obj2)
VALUE result;
if (obj1 == obj2) return Qtrue;
- result = rb_funcall(obj1, eq, 1, obj2);
+ result = rb_funcall(obj1, id_eq, 1, obj2);
if (RTEST(result)) return Qtrue;
return Qfalse;
}
@@ -52,7 +49,7 @@ int
rb_eql(obj1, obj2)
VALUE obj1, obj2;
{
- return RTEST(rb_funcall(obj1, eql, 1, obj2));
+ return RTEST(rb_funcall(obj1, id_eql, 1, obj2));
}
static VALUE
@@ -107,15 +104,14 @@ rb_obj_class(obj)
}
static void
-copy_object(dest, obj)
+init_copy(dest, obj)
VALUE dest, obj;
{
if (OBJ_FROZEN(dest)) {
- rb_raise(rb_eTypeError, "[bug] frozen object (%s) allocated", rb_class2name(CLASS_OF(dest)));
+ rb_raise(rb_eTypeError, "[bug] frozen object (%s) allocated", rb_obj_classname(dest));
}
RBASIC(dest)->flags &= ~(T_MASK|FL_EXIVAR);
RBASIC(dest)->flags |= RBASIC(obj)->flags & (T_MASK|FL_EXIVAR|FL_TAINT);
- rb_funcall(dest, copy_obj, 1, obj);
if (FL_TEST(obj, FL_EXIVAR)) {
rb_copy_generic_ivar(dest, obj);
}
@@ -132,6 +128,7 @@ copy_object(dest, obj)
ROBJECT(dest)->iv_tbl = st_copy(ROBJECT(obj)->iv_tbl);
}
}
+ rb_funcall(dest, id_init_copy, 1, obj);
}
VALUE
@@ -141,12 +138,13 @@ rb_obj_clone(obj)
VALUE clone;
if (rb_special_const_p(obj)) {
- rb_raise(rb_eTypeError, "can't clone %s", rb_class2name(CLASS_OF(obj)));
+ rb_raise(rb_eTypeError, "can't clone %s", rb_obj_classname(obj));
}
clone = rb_obj_alloc(rb_obj_class(obj));
- copy_object(clone, obj);
RBASIC(clone)->klass = rb_singleton_class_clone(obj);
- RBASIC(clone)->flags = RBASIC(obj)->flags | FL_TEST(clone, FL_TAINT);
+ RBASIC(clone)->flags = (RBASIC(obj)->flags | FL_TEST(clone, FL_TAINT)) & ~FL_FREEZE;
+ init_copy(clone, obj);
+ RBASIC(clone)->flags |= RBASIC(obj)->flags & FL_FREEZE;
return clone;
}
@@ -158,22 +156,22 @@ rb_obj_dup(obj)
VALUE dup;
if (rb_special_const_p(obj)) {
- rb_raise(rb_eTypeError, "can't dup %s", rb_class2name(CLASS_OF(obj)));
+ rb_raise(rb_eTypeError, "can't dup %s", rb_obj_classname(obj));
}
dup = rb_obj_alloc(rb_obj_class(obj));
- copy_object(dup, obj);
+ init_copy(dup, obj);
return dup;
}
VALUE
-rb_obj_copy_object(obj, orig)
+rb_obj_init_copy(obj, orig)
VALUE obj, orig;
{
if (obj == orig) return obj;
rb_check_frozen(obj);
if (TYPE(obj) != TYPE(orig) || rb_obj_class(obj) != rb_obj_class(orig)) {
- rb_raise(rb_eTypeError, "copy_object should take same class object");
+ rb_raise(rb_eTypeError, "initialize_copy should take same class object");
}
return obj;
}
@@ -190,7 +188,7 @@ VALUE
rb_any_to_s(obj)
VALUE obj;
{
- char *cname = rb_class2name(CLASS_OF(obj));
+ char *cname = rb_obj_classname(obj);
VALUE str;
str = rb_str_new(0, strlen(cname)+6+16+1); /* 6:tags 16:addr 1:nul */
@@ -205,7 +203,7 @@ VALUE
rb_inspect(obj)
VALUE obj;
{
- return rb_obj_as_string(rb_funcall(obj, inspect, 0, 0));
+ return rb_obj_as_string(rb_funcall(obj, id_inspect, 0, 0));
}
static int
@@ -259,7 +257,7 @@ rb_obj_inspect(obj)
VALUE str;
char *c;
- c = rb_class2name(CLASS_OF(obj));
+ c = rb_obj_classname(obj);
if (rb_inspecting_p(obj)) {
str = rb_str_new(0, strlen(c)+10+16+1); /* 10:tags 16:addr 1:nul */
sprintf(RSTRING(str)->ptr, "#<%s:0x%lx ...>", c, obj);
@@ -358,6 +356,13 @@ rb_obj_untaint(obj)
return obj;
}
+void
+rb_obj_infect(obj1, obj2)
+ VALUE obj1, obj2;
+{
+ OBJ_INFECT(obj1, obj2);
+}
+
VALUE
rb_obj_freeze(obj)
VALUE obj;
@@ -430,7 +435,7 @@ nil_plus(x, y)
default:
rb_raise(rb_eTypeError, "tried to add %s(%s) to nil",
RSTRING(rb_inspect(y))->ptr,
- rb_class2name(CLASS_OF(y)));
+ rb_obj_classname(y));
}
/* not reached */
}
@@ -591,6 +596,8 @@ static VALUE
rb_mod_le(mod, arg)
VALUE mod, arg;
{
+ VALUE start = mod;
+
if (mod == arg) return Qtrue;
switch (TYPE(arg)) {
case T_MODULE:
@@ -605,7 +612,13 @@ rb_mod_le(mod, arg)
return Qtrue;
mod = RCLASS(mod)->super;
}
- return Qfalse;
+ /* not mod < arg; check if mod > arg */
+ while (arg) {
+ if (RCLASS(arg)->m_tbl == RCLASS(start)->m_tbl)
+ return Qfalse;
+ arg = RCLASS(arg)->super;
+ }
+ return Qnil;
}
static VALUE
@@ -643,7 +656,7 @@ static VALUE
rb_mod_cmp(mod, arg)
VALUE mod, arg;
{
- VALUE start = mod;
+ VALUE cmp;
if (mod == arg) return INT2FIX(0);
switch (TYPE(arg)) {
@@ -651,21 +664,15 @@ rb_mod_cmp(mod, arg)
case T_CLASS:
break;
default:
- rb_raise(rb_eTypeError, "<=> requires Class or Module (%s given)",
- rb_class2name(CLASS_OF(arg)));
- break;
+ return Qnil;
}
- if (rb_mod_le(mod, arg)) {
+ cmp = rb_mod_le(mod, arg);
+ if (NIL_P(cmp)) return Qnil;
+ if (cmp) {
return INT2FIX(-1);
}
-
- while (arg) {
- if (RCLASS(arg)->m_tbl == RCLASS(start)->m_tbl)
- return INT2FIX(1);
- arg = RCLASS(arg)->super;
- }
- return Qnil;
+ return INT2FIX(1);
}
static VALUE
@@ -687,6 +694,7 @@ rb_class_initialize(argc, argv, klass)
return rb_mod_initialize(klass);
}
+static VALUE rb_module_s_alloc _((VALUE));
static VALUE
rb_module_s_alloc(klass)
VALUE klass;
@@ -719,29 +727,26 @@ VALUE
rb_obj_alloc(klass)
VALUE klass;
{
- VALUE obj = rb_funcall(klass, alloc, 0, 0);
+ VALUE obj;
+ if (FL_TEST(klass, FL_SINGLETON)) {
+ rb_raise(rb_eTypeError, "can't create instance of virtual class");
+ }
+ obj = rb_funcall(klass, ID_ALLOCATOR, 0, 0);
if (rb_obj_class(obj) != rb_class_real(klass)) {
rb_raise(rb_eTypeError, "wrong instance allocation");
}
return obj;
}
+static VALUE rb_class_allocate_instance _((VALUE));
static VALUE
rb_class_allocate_instance(klass)
VALUE klass;
{
- if (FL_TEST(klass, FL_SINGLETON)) {
- rb_raise(rb_eTypeError, "can't create instance of virtual class");
- }
- if (rb_frame_last_func() != alloc) {
- return rb_obj_alloc(klass);
- }
- else {
- NEWOBJ(obj, struct RObject);
- OBJSETUP(obj, klass, T_OBJECT);
- return (VALUE)obj;
- }
+ NEWOBJ(obj, struct RObject);
+ OBJSETUP(obj, klass, T_OBJECT);
+ return (VALUE)obj;
}
VALUE
@@ -773,16 +778,28 @@ rb_class_superclass(klass)
return super;
}
+static ID
+str_to_id(str)
+ VALUE str;
+{
+ if (!RSTRING(str)->ptr || RSTRING(str)->len == 0) {
+ rb_raise(rb_eArgError, "empty symbol string");
+ }
+ return rb_intern(RSTRING(str)->ptr);
+}
+
ID
rb_to_id(name)
VALUE name;
{
+ VALUE tmp;
ID id;
switch (TYPE(name)) {
case T_STRING:
- return rb_intern(RSTRING(name)->ptr);
+ return str_to_id(name);
case T_FIXNUM:
+ rb_warn("do not use Fixnums as Symbols");
id = FIX2LONG(name);
if (!rb_id2name(id)) {
rb_raise(rb_eArgError, "%ld is not a symbol", id);
@@ -792,6 +809,10 @@ rb_to_id(name)
id = SYM2ID(name);
break;
default:
+ tmp = rb_check_string_type(name);
+ if (!NIL_P(tmp)) {
+ return str_to_id(tmp);
+ }
rb_raise(rb_eTypeError, "%s is not a symbol", RSTRING(rb_inspect(name))->ptr);
}
return id;
@@ -890,43 +911,97 @@ rb_mod_const_defined(mod, name)
}
static VALUE
-rb_obj_methods(obj)
+rb_obj_methods(argc, argv, obj)
+ int argc;
+ VALUE *argv;
VALUE obj;
{
- VALUE argv[1];
+ retry:
+ if (argc == 0) {
+ VALUE args[1];
- argv[0] = Qtrue;
- return rb_class_instance_methods(1, argv, CLASS_OF(obj));
+ args[0] = Qtrue;
+ return rb_class_instance_methods(1, args, CLASS_OF(obj));
+ }
+ else {
+ VALUE recur;
+
+ rb_scan_args(argc, argv, "1", &recur);
+ if (RTEST(recur)) {
+ argc = 0;
+ goto retry;
+ }
+ return rb_obj_singleton_methods(argc, argv, obj);
+ }
}
static VALUE
-rb_obj_protected_methods(obj)
+rb_obj_protected_methods(argc, argv, obj)
+ int argc;
+ VALUE *argv;
VALUE obj;
{
- VALUE argv[1];
+ if (argc == 0) { /* hack to stop warning */
+ VALUE args[1];
- argv[0] = Qtrue;
- return rb_class_protected_instance_methods(1, argv, CLASS_OF(obj));
+ args[0] = Qtrue;
+ return rb_class_protected_instance_methods(1, args, CLASS_OF(obj));
+ }
+ return rb_class_protected_instance_methods(argc, argv, CLASS_OF(obj));
}
static VALUE
-rb_obj_private_methods(obj)
+rb_obj_private_methods(argc, argv, obj)
+ int argc;
+ VALUE *argv;
VALUE obj;
{
- VALUE argv[1];
+ if (argc == 0) { /* hack to stop warning */
+ VALUE args[1];
- argv[0] = Qtrue;
- return rb_class_private_instance_methods(1, argv, CLASS_OF(obj));
+ args[0] = Qtrue;
+ return rb_class_private_instance_methods(1, args, CLASS_OF(obj));
+ }
+ return rb_class_private_instance_methods(argc, argv, CLASS_OF(obj));
}
static VALUE
-rb_obj_public_methods(obj)
+rb_obj_public_methods(argc, argv, obj)
+ int argc;
+ VALUE *argv;
VALUE obj;
{
- VALUE argv[1];
+ if (argc == 0) { /* hack to stop warning */
+ VALUE args[1];
+
+ args[0] = Qtrue;
+ return rb_class_public_instance_methods(1, args, CLASS_OF(obj));
+ }
+ return rb_class_public_instance_methods(argc, argv, CLASS_OF(obj));
+}
+
+static VALUE
+rb_obj_ivar_get(obj, iv)
+ VALUE obj, iv;
+{
+ ID id = rb_to_id(iv);
+
+ if (!rb_is_instance_id(id)) {
+ rb_name_error(id, "`%s' is not allowed as an instance variable name", rb_id2name(id));
+ }
+ return rb_ivar_get(obj, id);
+}
+
+static VALUE
+rb_obj_ivar_set(obj, iv, val)
+ VALUE obj, iv, val;
+{
+ ID id = rb_to_id(iv);
- argv[0] = Qtrue;
- return rb_class_public_instance_methods(1, argv, CLASS_OF(obj));
+ if (!rb_is_instance_id(id)) {
+ rb_name_error(id, "`%s' is not allowed as an instance variable name", rb_id2name(id));
+ }
+ return rb_ivar_set(obj, id, val);
}
static VALUE
@@ -944,7 +1019,7 @@ convert_type(val, tname, method, raise)
NIL_P(val) ? "nil" :
val == Qtrue ? "true" :
val == Qfalse ? "false" :
- rb_class2name(CLASS_OF(val)),
+ rb_obj_classname(val),
tname);
}
else {
@@ -966,7 +1041,7 @@ rb_convert_type(val, type, tname, method)
v = convert_type(val, tname, method, Qtrue);
if (TYPE(v) != type) {
rb_raise(rb_eTypeError, "%s#%s should return %s",
- rb_class2name(CLASS_OF(val)), method, tname);
+ rb_obj_classname(val), method, tname);
}
return v;
}
@@ -985,7 +1060,7 @@ rb_check_convert_type(val, type, tname, method)
if (NIL_P(v)) return Qnil;
if (TYPE(v) != type) {
rb_raise(rb_eTypeError, "%s#%s should return %s",
- rb_class2name(CLASS_OF(val)), method, tname);
+ rb_obj_classname(val), method, tname);
}
return v;
}
@@ -999,7 +1074,7 @@ rb_to_integer(val, method)
VALUE v = convert_type(val, "Integer", method, Qtrue);
if (!rb_obj_is_kind_of(v, rb_cInteger)) {
rb_raise(rb_eTypeError, "%s#%s should return Integer",
- rb_class2name(CLASS_OF(val)), method);
+ rb_obj_classname(val), method);
}
return v;
}
@@ -1064,6 +1139,10 @@ rb_cstr_to_dbl(p, badcheck)
while (ISSPACE(*p) || *p == '_') p++;
}
d = strtod(p, &end);
+ if (errno == ERANGE) {
+ rb_warn("Float %*s out of range", end-p, p);
+ errno = 0;
+ }
if (p == end) {
if (badcheck) {
bad:
@@ -1094,6 +1173,10 @@ rb_cstr_to_dbl(p, badcheck)
*n = '\0';
p = buf;
d = strtod(p, &end);
+ if (errno == ERANGE) {
+ rb_warn("Float %*s out of range", end-p, p);
+ errno = 0;
+ }
if (badcheck) {
if (p == end) goto bad;
while (*end && ISSPACE(*end)) end++;
@@ -1222,28 +1305,30 @@ rb_f_string(obj, arg)
return rb_String(arg);
}
+#if 0
VALUE
rb_Array(val)
VALUE val;
{
- ID to_ary;
+ VALUE tmp = rb_check_array_type(val);
+ ID to_a;
- if (NIL_P(val)) {
- rb_raise(rb_eTypeError, "cannot convert nil into Array");
- }
- if (TYPE(val) == T_ARRAY) return val;
- to_ary = rb_intern("to_ary");
- if (rb_respond_to(val, to_ary)) {
- val = rb_funcall(val, to_ary, 0);
- }
- else {
- val = rb_funcall(val, rb_intern("to_a"), 0);
- }
- if (TYPE(val) != T_ARRAY) {
- rb_raise(rb_eTypeError, "`to_a' did not return Array");
+ if (NIL_P(tmp)) {
+ to_a = rb_intern("to_a");
+ if (rb_respond_to(val, to_a)) {
+ val = rb_funcall(val, to_a, 0);
+ if (TYPE(val) != T_ARRAY) {
+ rb_raise(rb_eTypeError, "`to_a' did not return Array");
+ }
+ return val;
+ }
+ else {
+ return rb_ary_new3(1, val);
+ }
}
- return val;
+ return tmp;
}
+#endif
static VALUE
rb_f_array(obj, arg)
@@ -1263,6 +1348,7 @@ boot_defclass(name, super)
rb_name_class(obj, id);
st_add_direct(rb_class_tbl, id, obj);
+ rb_const_set((rb_cObject ? rb_cObject : obj), id, obj);
return obj;
}
@@ -1273,8 +1359,6 @@ Init_Object()
{
VALUE metaclass;
- alloc = rb_intern("allocate");
-
rb_cObject = boot_defclass("Object", 0);
rb_cModule = boot_defclass("Module", rb_cObject);
rb_cClass = boot_defclass("Class", rb_cModule);
@@ -1285,6 +1369,7 @@ Init_Object()
rb_mKernel = rb_define_module("Kernel");
rb_include_module(rb_cObject, rb_mKernel);
+ rb_define_alloc_func(rb_cObject, rb_class_allocate_instance);
rb_define_private_method(rb_cObject, "initialize", rb_obj_dummy, 0);
rb_define_private_method(rb_cClass, "inherited", rb_obj_dummy, 1);
rb_define_private_method(rb_cModule, "included", rb_obj_dummy, 1);
@@ -1319,7 +1404,7 @@ Init_Object()
rb_define_method(rb_mKernel, "nil?", rb_false, 0);
rb_define_method(rb_mKernel, "==", rb_obj_equal, 1);
rb_define_method(rb_mKernel, "equal?", rb_obj_equal, 1);
- rb_define_method(rb_mKernel, "===", rb_obj_equal, 1);
+ rb_define_method(rb_mKernel, "===", rb_equal, 1);
rb_define_method(rb_mKernel, "=~", rb_false, 1);
rb_define_method(rb_mKernel, "eql?", rb_obj_equal, 1);
@@ -1333,7 +1418,7 @@ Init_Object()
rb_define_method(rb_mKernel, "clone", rb_obj_clone, 0);
rb_define_method(rb_mKernel, "dup", rb_obj_dup, 0);
- rb_define_method(rb_mKernel, "copy_object", rb_obj_copy_object, 1);
+ rb_define_method(rb_mKernel, "initialize_copy", rb_obj_init_copy, 1);
rb_define_method(rb_mKernel, "taint", rb_obj_taint, 0);
rb_define_method(rb_mKernel, "tainted?", rb_obj_tainted, 0);
@@ -1344,13 +1429,14 @@ Init_Object()
rb_define_method(rb_mKernel, "to_a", rb_any_to_a, 0); /* to be removed */
rb_define_method(rb_mKernel, "to_s", rb_any_to_s, 0);
rb_define_method(rb_mKernel, "inspect", rb_obj_inspect, 0);
- rb_define_method(rb_mKernel, "methods", rb_obj_methods, 0);
- rb_define_method(rb_mKernel, "public_methods", rb_obj_methods, 0);
+ rb_define_method(rb_mKernel, "methods", rb_obj_methods, -1);
rb_define_method(rb_mKernel, "singleton_methods", rb_obj_singleton_methods, -1);
- rb_define_method(rb_mKernel, "protected_methods", rb_obj_protected_methods, 0);
- rb_define_method(rb_mKernel, "private_methods", rb_obj_private_methods, 0);
- rb_define_method(rb_mKernel, "public_methods", rb_obj_public_methods, 0);
+ rb_define_method(rb_mKernel, "protected_methods", rb_obj_protected_methods, -1);
+ rb_define_method(rb_mKernel, "private_methods", rb_obj_private_methods, -1);
+ rb_define_method(rb_mKernel, "public_methods", rb_obj_public_methods, -1);
rb_define_method(rb_mKernel, "instance_variables", rb_obj_instance_variables, 0);
+ rb_define_method(rb_mKernel, "instance_variable_get", rb_obj_ivar_get, 1);
+ rb_define_method(rb_mKernel, "instance_variable_set", rb_obj_ivar_set, 2);
rb_define_private_method(rb_mKernel, "remove_instance_variable",
rb_obj_remove_instance_variable, 1);
@@ -1358,9 +1444,9 @@ Init_Object()
rb_define_method(rb_mKernel, "kind_of?", rb_obj_is_kind_of, 1);
rb_define_method(rb_mKernel, "is_a?", rb_obj_is_kind_of, 1);
- rb_define_global_function("singleton_method_added", rb_obj_dummy, 1);
- rb_define_global_function("singleton_method_removed", rb_obj_dummy, 1);
- rb_define_global_function("singleton_method_undefined", rb_obj_dummy, 1);
+ rb_define_private_method(rb_mKernel, "singleton_method_added", rb_obj_dummy, 1);
+ rb_define_private_method(rb_mKernel, "singleton_method_removed", rb_obj_dummy, 1);
+ rb_define_private_method(rb_mKernel, "singleton_method_undefined", rb_obj_dummy, 1);
rb_define_global_function("sprintf", rb_f_sprintf, -1);
rb_define_global_function("format", rb_f_sprintf, -1);
@@ -1382,13 +1468,13 @@ Init_Object()
rb_define_method(rb_cNilClass, "^", false_xor, 1);
rb_define_method(rb_cNilClass, "nil?", rb_true, 0);
- rb_undef_method(CLASS_OF(rb_cNilClass), "allocate");
+ rb_undef_alloc_func(rb_cNilClass);
rb_undef_method(CLASS_OF(rb_cNilClass), "new");
rb_define_global_const("NIL", Qnil);
rb_cSymbol = rb_define_class("Symbol", rb_cObject);
rb_define_singleton_method(rb_cSymbol, "all_symbols", rb_sym_all_symbols, 0);
- rb_undef_method(CLASS_OF(rb_cSymbol), "allocate");
+ rb_undef_alloc_func(rb_cSymbol);
rb_undef_method(CLASS_OF(rb_cSymbol), "new");
rb_define_method(rb_cSymbol, "to_i", sym_to_i, 0);
@@ -1418,7 +1504,7 @@ Init_Object()
rb_define_private_method(rb_cModule, "attr_writer", rb_mod_attr_writer, -1);
rb_define_private_method(rb_cModule, "attr_accessor", rb_mod_attr_accessor, -1);
- rb_define_singleton_method(rb_cModule, "allocate", rb_module_s_alloc, 0);
+ rb_define_alloc_func(rb_cModule, rb_module_s_alloc);
rb_define_method(rb_cModule, "initialize", rb_mod_initialize, 0);
rb_define_method(rb_cModule, "instance_methods", rb_class_instance_methods, -1);
rb_define_method(rb_cModule, "public_instance_methods", rb_class_public_instance_methods, -1);
@@ -1430,20 +1516,21 @@ Init_Object()
rb_define_method(rb_cModule, "const_set", rb_mod_const_set, 2);
rb_define_method(rb_cModule, "const_defined?", rb_mod_const_defined, 1);
rb_define_private_method(rb_cModule, "remove_const", rb_mod_remove_const, 1);
+ rb_define_method(rb_cModule, "const_missing", rb_mod_const_missing, 1);
rb_define_method(rb_cModule, "class_variables", rb_mod_class_variables, 0);
rb_define_private_method(rb_cModule, "remove_class_variable", rb_mod_remove_cvar, 1);
- rb_define_method(rb_cClass, "allocate", rb_class_allocate_instance, 0);
+ rb_define_method(rb_cClass, "allocate", rb_obj_alloc, 0);
rb_define_method(rb_cClass, "new", rb_class_new_instance, -1);
rb_define_method(rb_cClass, "initialize", rb_class_initialize, -1);
rb_define_method(rb_cClass, "superclass", rb_class_superclass, 0);
- rb_undef_method(CLASS_OF(rb_cClass), "allocate");
+ rb_undef_alloc_func(rb_cClass);
rb_define_singleton_method(rb_cClass, "new", rb_class_s_new, -1);
rb_undef_method(rb_cClass, "extend_object");
rb_undef_method(rb_cClass, "append_features");
rb_cData = rb_define_class("Data", rb_cObject);
- rb_undef_method(CLASS_OF(rb_cData), "allocate");
+ rb_undef_alloc_func(rb_cData);
ruby_top_self = rb_obj_alloc(rb_cObject);
rb_global_variable(&ruby_top_self);
@@ -1454,7 +1541,7 @@ Init_Object()
rb_define_method(rb_cTrueClass, "&", true_and, 1);
rb_define_method(rb_cTrueClass, "|", true_or, 1);
rb_define_method(rb_cTrueClass, "^", true_xor, 1);
- rb_undef_method(CLASS_OF(rb_cTrueClass), "allocate");
+ rb_undef_alloc_func(rb_cTrueClass);
rb_undef_method(CLASS_OF(rb_cTrueClass), "new");
rb_define_global_const("TRUE", Qtrue);
@@ -1463,12 +1550,12 @@ Init_Object()
rb_define_method(rb_cFalseClass, "&", false_and, 1);
rb_define_method(rb_cFalseClass, "|", false_or, 1);
rb_define_method(rb_cFalseClass, "^", false_xor, 1);
- rb_undef_method(CLASS_OF(rb_cFalseClass), "allocate");
+ rb_undef_alloc_func(rb_cFalseClass);
rb_undef_method(CLASS_OF(rb_cFalseClass), "new");
rb_define_global_const("FALSE", Qfalse);
- eq = rb_intern("==");
- eql = rb_intern("eql?");
- inspect = rb_intern("inspect");
- copy_obj = rb_intern("copy_object");
+ id_eq = rb_intern("==");
+ id_eql = rb_intern("eql?");
+ id_inspect = rb_intern("inspect");
+ id_init_copy = rb_intern("initialize_copy");
}
diff --git a/pack.c b/pack.c
index d47eaee45a..6df527605a 100644
--- a/pack.c
+++ b/pack.c
@@ -6,7 +6,7 @@
$Date$
created at: Thu Feb 10 15:17:05 JST 1994
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
**********************************************************************/
@@ -57,10 +57,10 @@ TOKEN_PASTE(swap,x)(z) \
unsigned char *s, *t; \
int i; \
\
- zp = (xtype *)malloc(sizeof(xtype));\
+ zp = malloc(sizeof(xtype)); \
*zp = z; \
- s = (char *)zp; \
- t = (char *)malloc(sizeof(xtype)); \
+ s = (unsigned char*)zp; \
+ t = malloc(sizeof(xtype)); \
for (i=0; i<sizeof(xtype); i++) { \
t[sizeof(xtype)-i-1] = s[i]; \
} \
@@ -351,6 +351,18 @@ num2u32(x)
# endif
#endif
+#if SIZEOF_LONG == SIZE32 || SIZEOF_INT == SIZE32
+# define EXTEND32(x) ((I32)(x))
+#else
+/* invariant in modulo 1<<31 */
+# define EXTEND32(x) (I32)(((1<<31)-1-(x))^~(~0<<31))
+#endif
+#if SIZEOF_SHORT == SIZE16
+# define EXTEND16(x) (short)(x)
+#else
+# define EXTEND16(x) (short)(((1<<15)-1-(x))^~(~0<<15))
+#endif
+
#ifdef HAVE_LONG_LONG
# define QUAD_SIZE sizeof(LONG_LONG)
#else
@@ -489,7 +501,8 @@ pack_pack(ary, fmt)
c = byte & 0xff;
rb_str_buf_cat(res, &c, 1);
}
- rb_str_buf_cat(res, 0, j);
+ len = j;
+ goto grow;
}
break;
@@ -518,7 +531,8 @@ pack_pack(ary, fmt)
c = byte & 0xff;
rb_str_buf_cat(res, &c, 1);
}
- rb_str_buf_cat(res, 0, j);
+ len = j;
+ goto grow;
}
break;
@@ -548,7 +562,8 @@ pack_pack(ary, fmt)
char c = byte & 0xff;
rb_str_buf_cat(res, &c, 1);
}
- rb_str_buf_cat(res, 0, j);
+ len = j;
+ goto grow;
}
break;
@@ -578,7 +593,8 @@ pack_pack(ary, fmt)
char c = byte & 0xff;
rb_str_buf_cat(res, &c, 1);
}
- rb_str_buf_cat(res, 0, j);
+ len = j;
+ goto grow;
}
break;
}
@@ -873,8 +889,7 @@ pack_pack(ary, fmt)
t = 0;
}
else {
- StringValue(from);
- t = RSTRING(from)->ptr;
+ t = StringValuePtr(from);
}
if (!associates) {
associates = rb_ary_new();
@@ -1313,6 +1328,9 @@ pack_unpack(str, fmt)
while (len-- > 0) {
short tmp = 0;
memcpy(OFF16(&tmp), s, NATINT_LEN(short,2));
+#if SIZEOF_SHORT != SIZE16
+ if (!natint) tmp = EXTEND16(tmp);
+#endif
s += NATINT_LEN(short,2);
rb_ary_push(ary, INT2FIX(tmp));
}
@@ -1357,6 +1375,9 @@ pack_unpack(str, fmt)
while (len-- > 0) {
long tmp = 0;
memcpy(OFF32(&tmp), s, NATINT_LEN(long,4));
+#if SIZEOF_LONG != SIZE32
+ if (!natint) tmp = EXTEND32(tmp);
+#endif
s += NATINT_LEN(long,4);
rb_ary_push(ary, LONG2NUM(tmp));
}
@@ -1588,7 +1609,7 @@ pack_unpack(str, fmt)
{
VALUE buf = infected_str_new(0, (send - s)*3/4, str);
char *ptr = RSTRING(buf)->ptr;
- int a,b,c = 0,d;
+ int a = -1,b = -1,c = 0,d;
static int first = 1;
static int b64_xtable[256];
@@ -1603,7 +1624,7 @@ pack_unpack(str, fmt)
b64_xtable[(int)b64_table[i]] = i;
}
}
- for (;;) {
+ while (s < send) {
while (s[0] == '\r' || s[0] == '\n') { s++; }
if ((a = b64_xtable[(int)s[0]]) == -1) break;
if ((b = b64_xtable[(int)s[1]]) == -1) break;
@@ -1614,12 +1635,13 @@ pack_unpack(str, fmt)
*ptr++ = c << 6 | d;
s += 4;
}
- if (a != -1 && b != -1 && s[2] == '=') {
- *ptr++ = a << 2 | b >> 4;
- }
- if (a != -1 && b != -1 && c != -1 && s[3] == '=') {
- *ptr++ = a << 2 | b >> 4;
- *ptr++ = b << 4 | c >> 2;
+ if (a != -1 && b != -1) {
+ if (s + 2 < send && s[2] == '=')
+ *ptr++ = a << 2 | b >> 4;
+ if (c != -1 && s + 3 < send && s[3] == '=') {
+ *ptr++ = a << 2 | b >> 4;
+ *ptr++ = b << 4 | c >> 2;
+ }
}
*ptr = '\0';
RSTRING(buf)->len = ptr - RSTRING(buf)->ptr;
@@ -1655,6 +1677,8 @@ pack_unpack(str, fmt)
break;
case '@':
+ if (len > RSTRING(str)->len)
+ rb_raise(rb_eArgError, "@ outside of string");
s = RSTRING(str)->ptr + len;
break;
@@ -1852,7 +1876,7 @@ utf8_to_uv(p, lenp)
long *lenp;
{
int c = *p++ & 0xff;
- unsigned LONG_LONG uv = c;
+ unsigned long uv = c;
long n;
if (!(uv & 0x80)) {
diff --git a/parse.y b/parse.y
index 1e5696b97c..fd107555d7 100644
--- a/parse.y
+++ b/parse.y
@@ -6,7 +6,7 @@
$Date$
created at: Fri May 28 18:02:42 JST 1993
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
**********************************************************************/
@@ -41,7 +41,7 @@
#define ID_JUNK 0x07
#define ID_INTERNAL ID_JUNK
-#define is_notop_id(id) ((id)>LAST_TOKEN)
+#define is_notop_id(id) ((id)>tLAST_TOKEN)
#define is_local_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_LOCAL)
#define is_global_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_GLOBAL)
#define is_instance_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_INSTANCE)
@@ -76,7 +76,6 @@ static enum lex_state {
EXPR_CLASS, /* immediate after `class', no here document. */
} lex_state;
static NODE *lex_strterm;
-static int lex_strnest;
#ifdef HAVE_LONG_LONG
typedef unsigned LONG_LONG stack_type;
@@ -116,6 +115,7 @@ static int quoted_term;
static NODE *cond();
static NODE *logop();
+static int cond_negative();
static NODE *newline_node();
static void fixpos();
@@ -134,14 +134,17 @@ static NODE *arg_concat();
static NODE *arg_prepend();
static NODE *literal_concat();
static NODE *new_evstr();
+static NODE *evstr2dstr();
static NODE *call_op();
static int in_defined = 0;
+static NODE *negate_lit();
static NODE *ret_args();
static NODE *arg_blk_pass();
static NODE *new_call();
static NODE *new_fcall();
static NODE *new_super();
+static NODE *new_yield();
static NODE *gettable();
static NODE *assignable();
@@ -162,6 +165,7 @@ static ID internal_id();
static struct RVarmap *dyna_push();
static void dyna_pop();
static int dyna_in_block();
+static NODE *dyna_init();
static void top_local_init();
static void top_local_setup();
@@ -170,9 +174,16 @@ static void top_local_setup();
#define NODE_STRTERM NODE_ZARRAY /* nothing to gc */
#define NODE_HEREDOC NODE_ARRAY /* 1, 3 to gc */
+#define ESCAPED_TERM (1 << CHAR_BIT)
+#define SIGN_EXTEND(x,n) (((1<<(n))-1-((x)&~(~0<<(n))))^~(~0<<(n)))
#define nd_func u1.id
-#define nd_term u2.id
-#define nd_paren u3.id
+#if SIZEOF_SHORT == 2
+#define nd_term(node) ((signed short)(node)->u2.id)
+#else
+#define nd_term(node) SIGN_EXTEND((node)->u2.id, CHAR_BIT*2)
+#endif
+#define nd_paren(node) (char)((node)->u2.id >> CHAR_BIT*2)
+#define nd_nest u3.id
%}
@@ -239,16 +250,16 @@ static void top_local_setup();
%type <node> singleton strings string string1 xstring regexp
%type <node> string_contents xstring_contents string_content
%type <node> words qwords word_list qword_list word
-%type <node> literal numeric dsym
+%type <node> literal numeric dsym cpath
%type <node> bodystmt compstmt stmts stmt expr arg primary command command_call method_call
%type <node> expr_value arg_value primary_value
%type <node> if_tail opt_else case_body cases opt_rescue exc_list exc_var opt_ensure
%type <node> args when_args call_args call_args2 open_args paren_args opt_paren_args
%type <node> command_args aref_args opt_block_arg block_arg var_ref var_lhs
-%type <node> mrhs mrhs_basic superclass block_call block_command
+%type <node> mrhs superclass block_call block_command
%type <node> f_arglist f_args f_optarg f_opt f_block_arg opt_f_block_arg
%type <node> assoc_list assocs assoc undef_list backref string_dvar
-%type <node> block_var opt_block_var brace_block do_block lhs none
+%type <node> block_var opt_block_var brace_block cmd_brace_block do_block lhs none
%type <node> mlhs mlhs_head mlhs_basic mlhs_entry mlhs_item mlhs_node
%type <id> fitem variable sym symbol operation operation2 operation3
%type <id> cname fname op f_rest_arg
@@ -286,7 +297,10 @@ static void top_local_setup();
* precedence table
*/
-%left kIF_MOD kUNLESS_MOD kWHILE_MOD kUNTIL_MOD
+%nonassoc tLOWEST
+%nonassoc tLBRACE_ARG
+
+%nonassoc kIF_MOD kUNLESS_MOD kWHILE_MOD kUNTIL_MOD
%left kOR kAND
%right kNOT
%nonassoc kDEFINED
@@ -303,10 +317,11 @@ static void top_local_setup();
%left tLSHFT tRSHFT
%left '+' '-'
%left '*' '/' '%'
-%right '!' '~' tUPLUS tUMINUS
+%right tUMINUS_NUM tUMINUS
%right tPOW
+%right '!' '~' tUPLUS
-%token LAST_TOKEN
+%token tLAST_TOKEN
%%
program : {
@@ -388,7 +403,7 @@ stmt : kALIAS fitem {lex_state = EXPR_FNAME;} fitem
{
char buf[3];
- sprintf(buf, "$%c", $3->nd_nth);
+ sprintf(buf, "$%c", (char)$3->nd_nth);
$$ = NEW_VALIAS($2, rb_intern(buf));
}
| kALIAS tGVAR tNTH_REF
@@ -404,11 +419,19 @@ stmt : kALIAS fitem {lex_state = EXPR_FNAME;} fitem
{
$$ = NEW_IF(cond($3), $1, 0);
fixpos($$, $3);
+ if (cond_negative(&$$->nd_cond)) {
+ $$->nd_else = $$->nd_body;
+ $$->nd_body = 0;
+ }
}
| stmt kUNLESS_MOD expr_value
{
$$ = NEW_UNLESS(cond($3), $1, 0);
fixpos($$, $3);
+ if (cond_negative(&$$->nd_cond)) {
+ $$->nd_body = $$->nd_else;
+ $$->nd_else = 0;
+ }
}
| stmt kWHILE_MOD expr_value
{
@@ -418,6 +441,9 @@ stmt : kALIAS fitem {lex_state = EXPR_FNAME;} fitem
else {
$$ = NEW_WHILE(cond($3), $1, 1);
}
+ if (cond_negative(&$$->nd_cond)) {
+ nd_set_type($$, NODE_UNTIL);
+ }
}
| stmt kUNTIL_MOD expr_value
{
@@ -427,6 +453,13 @@ stmt : kALIAS fitem {lex_state = EXPR_FNAME;} fitem
else {
$$ = NEW_UNTIL(cond($3), $1, 1);
}
+ if (cond_negative(&$$->nd_cond)) {
+ nd_set_type($$, NODE_WHILE);
+ }
+ }
+ | stmt kRESCUE_MOD stmt
+ {
+ $$ = NEW_RESCUE($1, NEW_RESBODY(0,$3,0), 0);
}
| klBEGIN
{
@@ -452,12 +485,13 @@ stmt : kALIAS fitem {lex_state = EXPR_FNAME;} fitem
}
| lhs '=' command_call
{
+ value_expr($3);
$$ = node_assign($1, $3);
}
| mlhs '=' command_call
{
value_expr($3);
- $1->nd_value = $3;
+ $1->nd_value = NEW_RESTARY($3);
$$ = $1;
}
| var_lhs tOP_ASGN command_call
@@ -543,9 +577,14 @@ stmt : kALIAS fitem {lex_state = EXPR_FNAME;} fitem
rb_backref_error($1);
$$ = 0;
}
- | lhs '=' mrhs_basic
+ | lhs '=' mrhs
{
- $$ = node_assign($1, NEW_REXPAND($3));
+ $$ = node_assign($1, NEW_SVALUE($3));
+ }
+ | mlhs '=' arg_value
+ {
+ $1->nd_value = $3;
+ $$ = $1;
}
| mlhs '=' mrhs
{
@@ -572,10 +611,6 @@ expr : command_call
{
$$ = NEW_NOT(cond($2));
}
- | arg kRESCUE_MOD command_call
- {
- $$ = NEW_RESCUE($1, NEW_RESBODY(0,$3,0), 0);
- }
| arg
;
@@ -613,21 +648,72 @@ block_command : block_call
}
;
-command : operation command_args
+cmd_brace_block : tLBRACE_ARG
+ {
+ $<vars>$ = dyna_push();
+ $<num>1 = ruby_sourceline;
+ }
+ opt_block_var {$<vars>$ = ruby_dyna_vars;}
+ compstmt
+ '}'
+ {
+ $$ = NEW_ITER($3, 0, dyna_init($5, $<vars>4));
+ nd_set_line($$, $<num>1);
+ dyna_pop($<vars>2);
+ }
+ ;
+
+command : operation command_args %prec tLOWEST
+ {
+ $$ = new_fcall($1, $2);
+ fixpos($$, $2);
+ }
+ | operation command_args cmd_brace_block
{
$$ = new_fcall($1, $2);
+ if ($3) {
+ if (nd_type($$) == NODE_BLOCK_PASS) {
+ rb_compile_error("both block arg and actual block given");
+ }
+ $3->nd_iter = $$;
+ $$ = $3;
+ }
fixpos($$, $2);
}
- | primary_value '.' operation2 command_args
+ | primary_value '.' operation2 command_args %prec tLOWEST
{
$$ = new_call($1, $3, $4);
fixpos($$, $1);
}
- | primary_value tCOLON2 operation2 command_args
+ | primary_value '.' operation2 command_args cmd_brace_block
+ {
+ $$ = new_call($1, $3, $4);
+ if ($5) {
+ if (nd_type($$) == NODE_BLOCK_PASS) {
+ rb_compile_error("both block arg and actual block given");
+ }
+ $5->nd_iter = $$;
+ $$ = $5;
+ }
+ fixpos($$, $1);
+ }
+ | primary_value tCOLON2 operation2 command_args %prec tLOWEST
{
$$ = new_call($1, $3, $4);
fixpos($$, $1);
}
+ | primary_value tCOLON2 operation2 command_args cmd_brace_block
+ {
+ $$ = new_call($1, $3, $4);
+ if ($5) {
+ if (nd_type($$) == NODE_BLOCK_PASS) {
+ rb_compile_error("both block arg and actual block given");
+ }
+ $5->nd_iter = $$;
+ $$ = $5;
+ }
+ fixpos($$, $1);
+ }
| kSUPER command_args
{
$$ = new_super($2);
@@ -635,7 +721,7 @@ command : operation command_args
}
| kYIELD command_args
{
- $$ = NEW_YIELD(ret_args($2));
+ $$ = new_yield($2);
fixpos($$, $2);
}
;
@@ -717,6 +803,12 @@ mlhs_node : variable
{
$$ = attrset($1, $3);
}
+ | primary_value tCOLON2 tCONSTANT
+ {
+ if (in_def || in_single)
+ yyerror("dynamic constant assignment");
+ $$ = NEW_CDECL(0, 0, NEW_COLON2($1, $3));
+ }
| backref
{
rb_backref_error($1);
@@ -744,6 +836,12 @@ lhs : variable
{
$$ = attrset($1, $3);
}
+ | primary_value tCOLON2 tCONSTANT
+ {
+ if (in_def || in_single)
+ yyerror("dynamic constant assignment");
+ $$ = NEW_CDECL(0, 0, NEW_COLON2($1, $3));
+ }
| backref
{
rb_backref_error($1);
@@ -758,6 +856,20 @@ cname : tIDENTIFIER
| tCONSTANT
;
+cpath : tCOLON3 cname
+ {
+ $$ = NEW_COLON3($2);
+ }
+ | cname
+ {
+ $$ = NEW_COLON2(0, $$);
+ }
+ | primary_value tCOLON2 cname
+ {
+ $$ = NEW_COLON2($1, $3);
+ }
+ ;
+
fname : tIDENTIFIER
| tCONSTANT
| tFID
@@ -818,16 +930,20 @@ op : '|' { $$ = '|'; }
reswords : k__LINE__ | k__FILE__ | klBEGIN | klEND
| kALIAS | kAND | kBEGIN | kBREAK | kCASE | kCLASS | kDEF
| kDEFINED | kDO | kELSE | kELSIF | kEND | kENSURE | kFALSE
- | kFOR | kIF_MOD | kIN | kMODULE | kNEXT | kNIL | kNOT
+ | kFOR | kIN | kMODULE | kNEXT | kNIL | kNOT
| kOR | kREDO | kRESCUE | kRETRY | kRETURN | kSELF | kSUPER
- | kTHEN | kTRUE | kUNDEF | kUNLESS_MOD | kUNTIL_MOD | kWHEN
- | kWHILE_MOD | kYIELD | kRESCUE_MOD
+ | kTHEN | kTRUE | kUNDEF | kWHEN | kYIELD
+ | kIF_MOD | kUNLESS_MOD | kWHILE_MOD | kUNTIL_MOD | kRESCUE_MOD
;
arg : lhs '=' arg
{
$$ = node_assign($1, $3);
}
+ | lhs '=' arg kRESCUE_MOD arg
+ {
+ $$ = node_assign($1, NEW_RESCUE($3, NEW_RESBODY(0,$5,0), 0));
+ }
| var_lhs tOP_ASGN arg
{
value_expr($3);
@@ -906,6 +1022,10 @@ arg : lhs '=' arg
$$ = NEW_OP_ASGN2($1, $3, $4, $5);
fixpos($$, $1);
}
+ | primary_value tCOLON2 tCONSTANT tOP_ASGN arg
+ {
+ yyerror("constant re-assignment");
+ }
| backref tOP_ASGN arg
{
rb_backref_error($1);
@@ -947,6 +1067,14 @@ arg : lhs '=' arg
{
$$ = call_op($1, tPOW, 1, $3);
}
+ | tUMINUS_NUM tINTEGER tPOW arg
+ {
+ $$ = call_op(call_op($2, tPOW, 1, $4), tUMINUS, 0, 0);
+ }
+ | tUMINUS_NUM tFLOAT tPOW arg
+ {
+ $$ = call_op(call_op($2, tPOW, 1, $4), tUMINUS, 0, 0);
+ }
| tUPLUS arg
{
if ($2 && nd_type($2) == NODE_LIT) {
@@ -958,15 +1086,7 @@ arg : lhs '=' arg
}
| tUMINUS arg
{
- if ($2 && nd_type($2) == NODE_LIT && FIXNUM_P($2->nd_lit)) {
- long i = FIX2LONG($2->nd_lit);
-
- $2->nd_lit = LONG2NUM(-i);
- $$ = $2;
- }
- else {
- $$ = call_op($2, tUMINUS, 0, 0);
- }
+ $$ = call_op($2, tUMINUS, 0, 0);
}
| arg '|' arg
{
@@ -1044,10 +1164,6 @@ arg : lhs '=' arg
{
$$ = logop(NODE_OR, $1, $3);
}
- | arg kRESCUE_MOD arg
- {
- $$ = NEW_RESCUE($1, NEW_RESBODY(0,$3,0), 0);
- }
| kDEFINED opt_nl {in_defined = 1;} arg
{
in_defined = 0;
@@ -1093,7 +1209,7 @@ aref_args : none
| tSTAR arg opt_nl
{
value_expr($2);
- $$ = NEW_RESTARY($2);
+ $$ = NEW_RESTARY2($2);
}
;
@@ -1158,7 +1274,7 @@ call_args : command
}
| tSTAR arg_value opt_block_arg
{
- $$ = arg_blk_pass(NEW_RESTARGS($2), $3);
+ $$ = arg_blk_pass(NEW_RESTARY($2), $3);
}
| block_arg
;
@@ -1178,7 +1294,7 @@ call_args2 : arg_value ',' args opt_block_arg
}
| arg_value ',' args ',' tSTAR arg_value opt_block_arg
{
- $$ = arg_concat(list_concat($1,$3), $6);
+ $$ = arg_concat(list_concat(NEW_LIST($1),$3), $6);
$$ = arg_blk_pass($$, $7);
}
| assocs opt_block_arg
@@ -1213,7 +1329,7 @@ call_args2 : arg_value ',' args opt_block_arg
}
| tSTAR arg_value opt_block_arg
{
- $$ = arg_blk_pass(NEW_RESTARGS($2), $3);
+ $$ = arg_blk_pass(NEW_RESTARY($2), $3);
}
| block_arg
;
@@ -1233,14 +1349,12 @@ command_args : {
open_args : call_args
| tLPAREN_ARG {lex_state = EXPR_ENDARG;} ')'
{
- rb_warning("%s (...) interpreted as method call",
- rb_id2name($<id>1));
+ rb_warn("don't put space before argument parentheses");
$$ = 0;
}
| tLPAREN_ARG call_args2 {lex_state = EXPR_ENDARG;} ')'
{
- rb_warning("%s (...) interpreted as method call",
- rb_id2name($<id>1));
+ rb_warn("don't put space before argument parentheses");
$$ = $2;
}
;
@@ -1268,17 +1382,7 @@ args : arg_value
}
;
-mrhs : arg_value
- {
- $$ = $1;
- }
- | mrhs_basic
- {
- $$ = NEW_REXPAND($1);
- }
- ;
-
-mrhs_basic : args ',' arg_value
+mrhs : args ',' arg_value
{
$$ = list_append($1, $3);
}
@@ -1288,7 +1392,7 @@ mrhs_basic : args ',' arg_value
}
| tSTAR arg_value
{
- $$ = $2;
+ $$ = NEW_SPLAT($2);
}
;
@@ -1302,7 +1406,7 @@ primary : literal
| backref
| tFID
{
- $$ = NEW_VCALL($1);
+ $$ = NEW_FCALL($1, 0);
}
| kBEGIN
{
@@ -1333,7 +1437,10 @@ primary : literal
}
| primary_value '[' aref_args ']'
{
- $$ = NEW_CALL($1, tAREF, $3);
+ if (nd_type($1) == NODE_SELF)
+ $$ = NEW_FCALL(tAREF, $3);
+ else
+ $$ = NEW_CALL($1, tAREF, $3);
}
| tLBRACK aref_args ']'
{
@@ -1354,15 +1461,15 @@ primary : literal
}
| kYIELD '(' call_args ')'
{
- $$ = NEW_YIELD(ret_args($3));
+ $$ = new_yield($3);
}
| kYIELD '(' ')'
{
- $$ = NEW_YIELD(0);
+ $$ = NEW_YIELD(0, Qfalse);
}
| kYIELD
{
- $$ = NEW_YIELD(0);
+ $$ = NEW_YIELD(0, Qfalse);
}
| kDEFINED opt_nl '(' {in_defined = 1;} expr ')'
{
@@ -1391,6 +1498,11 @@ primary : literal
{
$$ = NEW_IF(cond($2), $4, $5);
fixpos($$, $2);
+ if (cond_negative(&$$->nd_cond)) {
+ NODE *tmp = $$->nd_body;
+ $$->nd_body = $$->nd_else;
+ $$->nd_else = tmp;
+ }
}
| kUNLESS expr_value then
compstmt
@@ -1399,6 +1511,11 @@ primary : literal
{
$$ = NEW_UNLESS(cond($2), $4, $5);
fixpos($$, $2);
+ if (cond_negative(&$$->nd_cond)) {
+ NODE *tmp = $$->nd_body;
+ $$->nd_body = $$->nd_else;
+ $$->nd_else = tmp;
+ }
}
| kWHILE {COND_PUSH(1);} expr_value do {COND_POP();}
compstmt
@@ -1406,6 +1523,9 @@ primary : literal
{
$$ = NEW_WHILE(cond($3), $6, 1);
fixpos($$, $3);
+ if (cond_negative(&$$->nd_cond)) {
+ nd_set_type($$, NODE_UNTIL);
+ }
}
| kUNTIL {COND_PUSH(1);} expr_value do {COND_POP();}
compstmt
@@ -1413,6 +1533,9 @@ primary : literal
{
$$ = NEW_UNTIL(cond($3), $6, 1);
fixpos($$, $3);
+ if (cond_negative(&$$->nd_cond)) {
+ nd_set_type($$, NODE_WHILE);
+ }
}
| kCASE expr_value opt_terms
case_body
@@ -1436,7 +1559,7 @@ primary : literal
$$ = NEW_FOR($2, $5, $8);
fixpos($$, $2);
}
- | kCLASS cname superclass
+ | kCLASS cpath superclass
{
if (in_def || in_single)
yyerror("class definition in method body");
@@ -1474,7 +1597,7 @@ primary : literal
in_def = $<num>4;
in_single = $<num>6;
}
- | kMODULE cname
+ | kMODULE cpath
{
if (in_def || in_single)
yyerror("module definition in method body");
@@ -1502,7 +1625,6 @@ primary : literal
kEND
{
$$ = NEW_DEFN($2, $4, $5, NOEX_PRIVATE);
- if (is_attrset_id($2)) $$->nd_noex = NOEX_PUBLIC;
fixpos($$, $4);
local_pop();
in_def--;
@@ -1549,11 +1671,13 @@ primary_value : primary
;
then : term
+ | ':'
| kTHEN
| term kTHEN
;
do : term
+ | ':'
| kDO_COND
;
@@ -1598,28 +1722,14 @@ do_block : kDO_BLOCK
$<vars>$ = dyna_push();
$<num>1 = ruby_sourceline;
}
- opt_block_var
+ opt_block_var {$<vars>$ = ruby_dyna_vars;}
compstmt
kEND
{
- $$ = NEW_ITER($3, 0, $4);
+ $$ = NEW_ITER($3, 0, dyna_init($5, $<vars>4));
nd_set_line($$, $<num>1);
dyna_pop($<vars>2);
}
- | tLBRACE_ARG
- {
- $<vars>$ = dyna_push();
- $<num>1 = ruby_sourceline;
- }
- opt_block_var
- compstmt
- '}'
- {
- $$ = NEW_ITER($3, 0, $4);
- nd_set_line($$, $<num>1);
- dyna_pop($<vars>2);
- }
-
;
block_call : command do_block
@@ -1629,7 +1739,7 @@ block_call : command do_block
}
$2->nd_iter = $1;
$$ = $2;
- fixpos($$, $2);
+ fixpos($$, $1);
}
| block_call '.' operation2 opt_paren_args
{
@@ -1675,10 +1785,10 @@ brace_block : '{'
$<vars>$ = dyna_push();
$<num>1 = ruby_sourceline;
}
- opt_block_var
+ opt_block_var {$<vars>$ = ruby_dyna_vars;}
compstmt '}'
{
- $$ = NEW_ITER($3, 0, $4);
+ $$ = NEW_ITER($3, 0, dyna_init($5, $<vars>4));
nd_set_line($$, $<num>1);
dyna_pop($<vars>2);
}
@@ -1687,10 +1797,10 @@ brace_block : '{'
$<vars>$ = dyna_push();
$<num>1 = ruby_sourceline;
}
- opt_block_var
+ opt_block_var {$<vars>$ = ruby_dyna_vars;}
compstmt kEND
{
- $$ = NEW_ITER($3, 0, $4);
+ $$ = NEW_ITER($3, 0, dyna_init($5, $<vars>4));
nd_set_line($$, $<num>1);
dyna_pop($<vars>2);
}
@@ -1769,6 +1879,9 @@ strings : string
if (!node) {
node = NEW_STR(rb_str_new(0, 0));
}
+ else {
+ node = evstr2dstr(node);
+ }
$$ = node;
}
;
@@ -1801,8 +1914,7 @@ xstring : tXSTRING_BEG xstring_contents tSTRING_END
nd_set_type(node, NODE_DXSTR);
break;
default:
- node = rb_node_newnode(NODE_DXSTR, rb_str_new(0, 0),
- 1, NEW_LIST(node));
+ node = NEW_NODE(NODE_DXSTR, rb_str_new(0, 0), 1, NEW_LIST(node));
break;
}
}
@@ -1828,8 +1940,7 @@ regexp : tREGEXP_BEG xstring_contents tREGEXP_END
}
break;
default:
- node = rb_node_newnode(NODE_DSTR, rb_str_new(0, 0),
- 1, NEW_LIST(node));
+ node = NEW_NODE(NODE_DSTR, rb_str_new(0, 0), 1, NEW_LIST(node));
case NODE_DSTR:
if (options & RE_OPTION_ONCE) {
nd_set_type(node, NODE_DREGX_ONCE);
@@ -1856,12 +1967,11 @@ words : tWORDS_BEG ' ' tSTRING_END
word_list : /* none */
{
- lex_strnest = 0;
$$ = 0;
}
| word_list word ' '
{
- $$ = list_append($1, $2);
+ $$ = list_append($1, evstr2dstr($2));
}
;
@@ -1884,7 +1994,6 @@ qwords : tQWORDS_BEG ' ' tSTRING_END
qword_list : /* none */
{
- lex_strnest = 0;
$$ = 0;
}
| qword_list tSTRING_CONTENT ' '
@@ -1895,7 +2004,6 @@ qword_list : /* none */
string_contents : /* none */
{
- lex_strnest = 0;
$$ = 0;
}
| string_contents string_content
@@ -1906,7 +2014,6 @@ string_contents : /* none */
xstring_contents: /* none */
{
- lex_strnest = 0;
$$ = 0;
}
| xstring_contents string_content
@@ -1918,27 +2025,23 @@ xstring_contents: /* none */
string_content : tSTRING_CONTENT
| tSTRING_DVAR
{
- $<num>1 = lex_strnest;
$<node>$ = lex_strterm;
lex_strterm = 0;
lex_state = EXPR_BEG;
}
string_dvar
{
- lex_strnest = $<num>1;
lex_strterm = $<node>2;
$$ = NEW_EVSTR($3);
}
| tSTRING_DBEG term_push
{
- $<num>1 = lex_strnest;
$<node>$ = lex_strterm;
lex_strterm = 0;
lex_state = EXPR_BEG;
}
compstmt '}'
{
- lex_strnest = $<num>1;
quoted_term = $2;
lex_strterm = $<node>3;
if (($$ = $4) && nd_type($$) == NODE_NEWLINE) {
@@ -1959,8 +2062,8 @@ term_push : /* none */
{
if (($$ = quoted_term) == -1 &&
nd_type(lex_strterm) == NODE_STRTERM &&
- !lex_strterm->nd_paren) {
- quoted_term = lex_strterm->nd_term;
+ !nd_paren(lex_strterm)) {
+ quoted_term = nd_term(lex_strterm);
}
}
;
@@ -1986,16 +2089,18 @@ dsym : tSYMBEG xstring_contents tSTRING_END
}
else {
switch (nd_type($$)) {
- case NODE_STR:
- $$->nd_lit = ID2SYM(rb_intern(RSTRING($$->nd_lit)->ptr));
- nd_set_type($$, NODE_LIT);
- break;
case NODE_DSTR:
nd_set_type($$, NODE_DSYM);
break;
+ case NODE_STR:
+ if (strlen(RSTRING($$->nd_lit)->ptr) == RSTRING($$->nd_lit)->len) {
+ $$->nd_lit = ID2SYM(rb_intern(RSTRING($$->nd_lit)->ptr));
+ nd_set_type($$, NODE_LIT);
+ break;
+ }
+ /* fall through */
default:
- $$ = rb_node_newnode(NODE_DSYM, rb_str_new(0, 0),
- 1, NEW_LIST($$));
+ $$ = NEW_NODE(NODE_DSYM, rb_str_new(0, 0), 1, NEW_LIST($$));
break;
}
}
@@ -2004,6 +2109,14 @@ dsym : tSYMBEG xstring_contents tSTRING_END
numeric : tINTEGER
| tFLOAT
+ | tUMINUS_NUM tINTEGER %prec tLOWEST
+ {
+ $$ = negate_lit($2);
+ }
+ | tUMINUS_NUM tFLOAT %prec tLOWEST
+ {
+ $$ = negate_lit($2);
+ }
;
variable : tIDENTIFIER
@@ -2406,7 +2519,6 @@ yycompile(f, line)
ruby_eval_tree = 0;
heredoc_end = 0;
lex_strterm = 0;
- lex_strnest = 0;
quoted_term = -1;
ruby_current_node = 0;
ruby_sourcefile = rb_source_filename(f);
@@ -2431,6 +2543,7 @@ yycompile(f, line)
rb_gc_force_recycle((VALUE)tmp);
}
if (n == 0) node = ruby_eval_tree;
+ else ruby_eval_tree_begin = 0;
return node;
}
@@ -2545,6 +2658,7 @@ pushback(c)
lex_p--;
}
+#define was_bol() (lex_p == lex_pbeg + 1)
#define peek(c) (lex_p != lex_pend && (c) == *lex_p)
#define tokfix() (tokenbuf[tokidx]='\0')
@@ -2838,21 +2952,21 @@ dispose_string(str)
}
static int
-tokadd_string(func, term, paren)
- int func, term, paren;
+tokadd_string(func, term, paren, nest)
+ int func, term, paren, *nest;
{
int c;
while ((c = nextc()) != -1) {
if (paren && c == paren) {
- lex_strnest++;
+ ++*nest;
}
else if (c == term) {
- if (!lex_strnest) {
+ if (!nest || !*nest) {
pushback(c);
break;
}
- --lex_strnest;
+ --*nest;
}
else if ((func & STR_FUNC_EXPAND) && c == '#' && lex_p < lex_pend) {
int c2 = *lex_p;
@@ -2918,15 +3032,15 @@ tokadd_string(func, term, paren)
}
#define NEW_STRTERM(func, term, paren) \
- rb_node_newnode(NODE_STRTERM, (func), (term), (paren))
+ rb_node_newnode(NODE_STRTERM, (func), (term) | ((paren) << (CHAR_BIT * 2)), 0)
static int
parse_string(quote)
NODE *quote;
{
int func = quote->nd_func;
- int term = quote->nd_term;
- int paren = quote->nd_paren;
+ int term = nd_term(quote);
+ int paren = nd_paren(quote);
int c, space = 0;
if (func == -1) return tSTRING_END;
@@ -2935,20 +3049,16 @@ parse_string(quote)
do {c = nextc();} while (ISSPACE(c));
space = 1;
}
- if (c == term) {
- if (!lex_strnest) {
- eos:
- if (func & STR_FUNC_QWORDS) {
- quote->nd_func = -1;
- return ' ';
- }
- if (!(func & STR_FUNC_REGEXP)) return tSTRING_END;
- yylval.num = regx_options();
- return tREGEXP_END;
- }
- }
- if (c == '\\' && WHEN_QUOTED_TERM(peek(quoted_term_char))) {
- if ((c = nextc()) == term) goto eos;
+ if ((c == term && !quote->nd_nest) ||
+ (c == '\\' && WHEN_QUOTED_TERM(peek(quoted_term_char)) &&
+ (c = nextc()) == term)) {
+ if (func & STR_FUNC_QWORDS) {
+ quote->nd_func = -1;
+ return ' ';
+ }
+ if (!(func & STR_FUNC_REGEXP)) return tSTRING_END;
+ yylval.num = regx_options();
+ return tREGEXP_END;
}
if (space) {
pushback(c);
@@ -2967,7 +3077,7 @@ parse_string(quote)
tokadd('#');
}
pushback(c);
- if (tokadd_string(func, term, paren) == -1) {
+ if (tokadd_string(func, term, paren, &quote->nd_nest) == -1) {
ruby_sourceline = nd_line(quote);
rb_compile_error("unterminated string meets end of file");
return tSTRING_END;
@@ -2985,18 +3095,8 @@ heredoc_identifier()
if (c == '-') {
c = nextc();
- if (ISSPACE(c)) {
- pushback(c);
- pushback('-');
- return 0;
- }
func = STR_FUNC_INDENT;
}
- else if (ISSPACE(c)) {
- not_heredoc:
- pushback(c);
- return 0;
- }
switch (c) {
case '\'':
func |= str_squote; goto quoted;
@@ -3019,7 +3119,13 @@ heredoc_identifier()
break;
default:
- if (!is_identchar(c)) goto not_heredoc;
+ if (!is_identchar(c)) {
+ pushback(c);
+ if (func & STR_FUNC_INDENT) {
+ pushback('-');
+ }
+ return 0;
+ }
newtok();
term = '"';
tokadd(func |= str_dquote);
@@ -3062,14 +3168,14 @@ whole_match_p(eos, len, indent)
int len, indent;
{
char *p = lex_pbeg;
+ int n;
if (indent) {
while (*p && ISSPACE(*p)) p++;
}
- if (strncmp(eos, p, len) == 0) {
- if (p[len] == '\n' || p[len] == '\r') return Qtrue;
- if (p + len == lex_pend) return Qtrue;
- }
+ n= lex_pend - (p + len);
+ if (n < 0 || (n > 0 && p[len] != '\n' && p[len] != '\r')) return Qfalse;
+ if (strncmp(eos, p, len) == 0) return Qtrue;
return Qfalse;
}
@@ -3078,9 +3184,9 @@ here_document(here)
NODE *here;
{
int c, func, indent = 0;
- char *eos;
+ char *eos, *p, *pend;
long len;
- VALUE str = 0, line;
+ VALUE str = 0;
eos = RSTRING(here->nd_lit)->ptr;
len = RSTRING(here->nd_lit)->len - 1;
@@ -3093,18 +3199,31 @@ here_document(here)
lex_strterm = 0;
return 0;
}
- if (lex_p - 1 == lex_pbeg && whole_match_p(eos, len, indent)) {
+ if (was_bol() && whole_match_p(eos, len, indent)) {
heredoc_restore(lex_strterm);
return tSTRING_END;
}
if (!(func & STR_FUNC_EXPAND)) {
do {
- line = lex_lastline;
+ p = RSTRING(lex_lastline)->ptr;
+ pend = lex_pend;
+ if (pend > p) {
+ switch (pend[-1]) {
+ case '\n':
+ if (--pend == p || pend[-1] != '\r') {
+ pend++;
+ break;
+ }
+ case '\r':
+ --pend;
+ }
+ }
if (str)
- rb_str_cat(str, RSTRING(line)->ptr, RSTRING(line)->len);
+ rb_str_cat(str, p, pend - p);
else
- str = rb_str_new(RSTRING(line)->ptr, RSTRING(line)->len);
+ str = rb_str_new(p, pend - p);
+ if (pend < lex_pend) rb_str_cat(str, "\n", 1);
lex_p = lex_pend;
if (nextc() == -1) {
if (str) dispose_string(str);
@@ -3127,7 +3246,7 @@ here_document(here)
}
do {
pushback(c);
- if ((c = tokadd_string(func, '\n', 0)) == -1) goto error;
+ if ((c = tokadd_string(func, '\n', 0, NULL)) == -1) goto error;
if (c != '\n') {
yylval.node = NEW_STR(rb_str_new(tok(), toklen()));
return tSTRING_CONTENT;
@@ -3148,7 +3267,7 @@ here_document(here)
static void
arg_ambiguous()
{
- rb_warning("ambiguous first argument; make sure");
+ rb_warning("ambiguous first argument; put parentheses or even spaces");
}
#define IS_ARG() (lex_state == EXPR_ARG || lex_state == EXPR_CMDARG)
@@ -3156,7 +3275,6 @@ arg_ambiguous()
static int
yylex()
{
- static ID last_id = 0;
register int c;
int space_seen = 0;
int cmd_state;
@@ -3264,7 +3382,7 @@ yylex()
return '!';
case '=':
- if (lex_p == lex_pbeg + 1) {
+ if (was_bol()) {
/* skip embedded rd document */
if (strncmp(lex_p, "begin", 5) == 0 && ISSPACE(lex_p[5])) {
for (;;) {
@@ -3556,8 +3674,7 @@ yylex()
lex_state = EXPR_BEG;
pushback(c);
if (ISDIGIT(c)) {
- c = '-';
- goto start_num;
+ return tUMINUS_NUM;
}
return tUMINUS;
}
@@ -3575,12 +3692,11 @@ yylex()
return tDOT2;
}
pushback(c);
- if (!ISDIGIT(c)) {
- lex_state = EXPR_DOT;
- return '.';
+ if (ISDIGIT(c)) {
+ yyerror("no .<digit> floating literal anymore; put 0 before dot");
}
- c = '.';
- /* fall through */
+ lex_state = EXPR_DOT;
+ return '.';
start_num:
case '0': case '1': case '2': case '3': case '4':
@@ -3910,7 +4026,6 @@ yylex()
}
else if (lex_state == EXPR_ARG) {
c = tLPAREN_ARG;
- yylval.id = last_id;
}
}
COND_PUSH(0);
@@ -3962,9 +4077,9 @@ yylex()
}
pushback(c);
if (QUOTED_TERM_P(c)) {
- if (!(quoted_term & (1 << CHAR_BIT))) {
+ if (!(quoted_term & ESCAPED_TERM)) {
rb_warn("escaped terminator '%c' inside string interpolation", c);
- quoted_term |= 1 << CHAR_BIT;
+ quoted_term |= ESCAPED_TERM;
}
goto retry;
}
@@ -3979,10 +4094,10 @@ yylex()
quotation:
if (c == '\\' && WHEN_QUOTED_TERM(peek(quoted_term_char))) {
c = nextc();
- if (!(quoted_term & (1 << CHAR_BIT))) {
+ if (!(quoted_term & ESCAPED_TERM)) {
rb_warn("escaped terminator '%s%c' inside string interpolation",
(c == '\'' ? "\\" : ""), c);
- quoted_term |= 1 << CHAR_BIT;
+ quoted_term |= ESCAPED_TERM;
}
}
if (!ISALNUM(c)) {
@@ -4123,12 +4238,10 @@ yylex()
case '4': case '5': case '6':
case '7': case '8': case '9':
tokadd('$');
- while (ISDIGIT(c)) {
+ do {
tokadd(c);
c = nextc();
- }
- if (is_identchar(c))
- break;
+ } while (ISDIGIT(c));
pushback(c);
tokfix();
yylval.node = NEW_NTH_REF(atoi(tok()+1));
@@ -4154,10 +4267,10 @@ yylex()
}
if (ISDIGIT(c)) {
if (tokidx == 1) {
- rb_compile_error("`@%c' is not a valid instance variable name", c);
+ rb_compile_error("`@%c' is not allowed as an instance variable name", c);
}
else {
- rb_compile_error("`@@%c' is not a valid class variable name", c);
+ rb_compile_error("`@@%c' is not allowed as a class variable name", c);
}
}
if (!is_identchar(c)) {
@@ -4166,8 +4279,17 @@ yylex()
}
break;
+ case '_':
+ if (was_bol() && whole_match_p("__END__", 7, 0)) {
+ ruby__end__seen = 1;
+ lex_lastline = 0;
+ return -1;
+ }
+ newtok();
+ break;
+
default:
- if (!is_identchar(c) || ISDIGIT(c)) {
+ if (!is_identchar(c)) {
rb_compile_error("Invalid char `\\%03o' in expression", c);
goto retry;
}
@@ -4176,7 +4298,7 @@ yylex()
break;
}
- while (is_identchar(c)) {
+ do {
tokadd(c);
if (ismbchar(c)) {
int i, len = mbclen(c)-1;
@@ -4187,7 +4309,7 @@ yylex()
}
}
c = nextc();
- }
+ } while (is_identchar(c));
if ((c == '!' || c == '?') && is_identchar(tok()[0]) && !peek('=')) {
tokadd(c);
}
@@ -4222,6 +4344,7 @@ yylex()
(!peek('=') || (lex_p + 1 < lex_pend && lex_p[1] == '>'))) {
result = tIDENTIFIER;
tokadd(c);
+ tokfix();
}
else {
pushback(c);
@@ -4280,16 +4403,9 @@ yylex()
lex_state = EXPR_END;
}
}
- tokfix();
- if (strcmp(tok(), "__END__") == 0 &&
- lex_p - lex_pbeg == 7 &&
- (lex_pend == lex_p || *lex_p == '\n' || *lex_p == '\r')) {
- ruby__end__seen = 1;
- lex_lastline = 0;
- return -1;
- }
- last_id = yylval.id = rb_intern(tok());
- if ((dyna_in_block() && rb_dvar_defined(last_id)) || local_id(last_id)) {
+ yylval.id = rb_intern(tok());
+ if (is_local_id(yylval.id) &&
+ ((dyna_in_block() && rb_dvar_defined(yylval.id)) || local_id(yylval.id))) {
lex_state = EXPR_END;
}
return result;
@@ -4299,7 +4415,7 @@ yylex()
NODE*
rb_node_newnode(type, a0, a1, a2)
enum node_type type;
- NODE *a0, *a1, *a2;
+ VALUE a0, a1, a2;
{
NODE *n = (NODE*)rb_newobj();
@@ -4308,9 +4424,9 @@ rb_node_newnode(type, a0, a1, a2)
nd_set_line(n, ruby_sourceline);
n->nd_file = ruby_sourcefile;
- n->u1.node = a0;
- n->u2.node = a1;
- n->u3.node = a2;
+ n->u1.value = a0;
+ n->u2.value = a1;
+ n->u3.value = a2;
return n;
}
@@ -4354,6 +4470,28 @@ fixpos(node, orig)
nd_set_line(node, nd_line(orig));
}
+static void
+parser_warning(node, mesg)
+ NODE *node;
+ const char *mesg;
+{
+ int line = ruby_sourceline;
+ ruby_sourceline = nd_line(node);
+ rb_warning(mesg);
+ ruby_sourceline = line;
+}
+
+static void
+parser_warn(node, mesg)
+ NODE *node;
+ const char *mesg;
+{
+ int line = ruby_sourceline;
+ ruby_sourceline = nd_line(node);
+ rb_warn(mesg);
+ ruby_sourceline = line;
+}
+
static NODE*
block_append(head, tail)
NODE *head, *tail;
@@ -4370,6 +4508,7 @@ block_append(head, tail)
goto again;
case NODE_LIT:
case NODE_STR:
+ parser_warning(h, "unused literal ignored");
return tail;
default:
end = NEW_BLOCK(head);
@@ -4391,7 +4530,7 @@ block_append(head, tail)
case NODE_NEXT:
case NODE_REDO:
case NODE_RETRY:
- rb_warning("statement not reached");
+ parser_warning(nd, "statement not reached");
break;
case NODE_NEWLINE:
@@ -4420,14 +4559,16 @@ list_append(list, item)
NODE *last;
if (list == 0) return NEW_LIST(item);
-
- last = list;
- while (last->nd_next) {
- last = last->nd_next;
+ if (list->nd_next) {
+ last = list->nd_next->nd_end;
+ }
+ else {
+ last = list;
}
- last->nd_next = NEW_LIST(item);
list->nd_alen += 1;
+ last->nd_next = NEW_LIST(item);
+ list->nd_next->nd_end = last->nd_next;
return list;
}
@@ -4438,13 +4579,21 @@ list_concat(head, tail)
{
NODE *last;
- last = head;
- while (last->nd_next) {
- last = last->nd_next;
+ if (head->nd_next) {
+ last = head->nd_next->nd_end;
+ }
+ else {
+ last = head;
}
- last->nd_next = tail;
head->nd_alen += tail->nd_alen;
+ last->nd_next = tail;
+ if (tail->nd_next) {
+ head->nd_next->nd_end = tail->nd_next->nd_end;
+ }
+ else {
+ head->nd_next->nd_end = tail;
+ }
return head;
}
@@ -4462,9 +4611,7 @@ literal_concat(head, tail)
htype = nd_type(head);
if (htype == NODE_EVSTR) {
NODE *node = NEW_DSTR(rb_str_new(0, 0));
- node->nd_next = NEW_LIST(head);
- node->nd_alen += 1;
- head = node;
+ head = list_append(node, head);
}
switch (nd_type(tail)) {
case NODE_STR:
@@ -4502,6 +4649,16 @@ literal_concat(head, tail)
}
static NODE *
+evstr2dstr(node)
+ NODE *node;
+{
+ if (nd_type(node) == NODE_EVSTR) {
+ node = list_append(NEW_DSTR(rb_str_new(0, 0)), node);
+ }
+ return node;
+}
+
+static NODE *
new_evstr(node)
NODE *node;
{
@@ -4530,9 +4687,12 @@ call_op(recv, id, narg, arg1)
value_expr(recv);
if (narg == 1) {
value_expr(arg1);
+ arg1 = NEW_LIST(arg1);
}
-
- return NEW_CALL(recv, id, narg==1?NEW_LIST(arg1):0);
+ else {
+ arg1 = 0;
+ }
+ return NEW_CALL(recv, id, arg1);
}
static NODE*
@@ -4670,7 +4830,7 @@ assignable(id, val)
else if (is_const_id(id)) {
if (in_def || in_single)
yyerror("dynamic constant assignment");
- return NEW_CDECL(id, val);
+ return NEW_CDECL(id, val, 0);
}
else if (is_class_id(id)) {
if (in_def || in_single) return NEW_CVASGN(id, val);
@@ -4686,8 +4846,11 @@ static NODE *
aryset(recv, idx)
NODE *recv, *idx;
{
- value_expr(recv);
- return NEW_CALL(recv, tASET, idx);
+ if (recv && nd_type(recv) == NODE_SELF)
+ recv = (NODE *)1;
+ else
+ value_expr(recv);
+ return NEW_ATTRASGN(recv, tASET, idx);
}
ID
@@ -4704,8 +4867,11 @@ attrset(recv, id)
NODE *recv;
ID id;
{
- value_expr(recv);
- return NEW_CALL(recv, rb_id_attrset(id), 0);
+ if (recv && nd_type(recv) == NODE_SELF)
+ recv = (NODE *)1;
+ else
+ value_expr(recv);
+ return NEW_ATTRASGN(recv, rb_id_attrset(id), 0);
}
static void
@@ -4717,7 +4883,7 @@ rb_backref_error(node)
rb_compile_error("Can't set variable $%d", node->nd_nth);
break;
case NODE_BACK_REF:
- rb_compile_error("Can't set variable $%c", node->nd_nth);
+ rb_compile_error("Can't set variable $%c", (int)node->nd_nth);
break;
}
}
@@ -4765,6 +4931,7 @@ node_assign(lhs, rhs)
lhs->nd_value = rhs;
break;
+ case NODE_ATTRASGN:
case NODE_CALL:
lhs->nd_args = arg_add(lhs->nd_args, rhs);
break;
@@ -4785,11 +4952,9 @@ value_expr0(node)
while (node) {
switch (nd_type(node)) {
- case NODE_CLASS:
- case NODE_MODULE:
case NODE_DEFN:
case NODE_DEFS:
- rb_warning("void value expression");
+ parser_warning(node, "void value expression");
return Qfalse;
case NODE_RETURN:
@@ -4842,9 +5007,9 @@ void_expr0(node)
char *useless = 0;
if (!RTEST(ruby_verbose)) return;
- if (!node) return;
again:
+ if (!node) return;
switch (nd_type(node)) {
case NODE_NEWLINE:
node = node->nd_next;
@@ -4992,7 +5157,7 @@ assign_in_cond(node)
case NODE_TRUE:
case NODE_FALSE:
/* reports always */
- rb_warn("found = in conditional, should be ==");
+ parser_warn(node->nd_value, "found = in conditional, should be ==");
return 1;
case NODE_DSTR:
@@ -5005,7 +5170,7 @@ assign_in_cond(node)
}
#if 0
if (assign_in_cond(node->nd_value) == 0) {
- rb_warning("assignment in condition");
+ parser_warning(node->nd_value, "assignment in condition");
}
#endif
return 1;
@@ -5020,17 +5185,19 @@ e_option_supplied()
}
static void
-warn_unless_e_option(str)
+warn_unless_e_option(node, str)
+ NODE *node;
const char *str;
{
- if (!e_option_supplied()) rb_warn(str);
+ if (!e_option_supplied()) parser_warn(node, str);
}
static void
-warning_unless_e_option(str)
+warning_unless_e_option(node, str)
+ NODE *node;
const char *str;
{
- if (!e_option_supplied()) rb_warning(str);
+ if (!e_option_supplied()) parser_warning(node, str);
}
static NODE *cond0();
@@ -5052,12 +5219,34 @@ range_op(node)
type = nd_type(node);
}
if (type == NODE_LIT && FIXNUM_P(node->nd_lit)) {
- warn_unless_e_option("integer literal in conditional range");
+ warn_unless_e_option(node, "integer literal in conditional range");
return call_op(node,tEQ,1,NEW_GVAR(rb_intern("$.")));
}
return node;
}
+static int
+literal_node(node)
+ NODE *node;
+{
+ if (!node) return 1; /* same as NODE_NIL */
+ switch (nd_type(node)) {
+ case NODE_LIT:
+ case NODE_STR:
+ case NODE_DSTR:
+ case NODE_EVSTR:
+ case NODE_DREGX:
+ case NODE_DREGX_ONCE:
+ case NODE_DSYM:
+ return 2;
+ case NODE_TRUE:
+ case NODE_FALSE:
+ case NODE_NIL:
+ return 1;
+ }
+ return 0;
+}
+
static NODE*
cond0(node)
NODE *node;
@@ -5068,13 +5257,14 @@ cond0(node)
switch (type) {
case NODE_DSTR:
+ case NODE_EVSTR:
case NODE_STR:
rb_warn("string literal in condition");
break;
case NODE_DREGX:
case NODE_DREGX_ONCE:
- warning_unless_e_option("regex literal in condition");
+ warning_unless_e_option(node, "regex literal in condition");
local_cnt('_');
local_cnt('~');
return NEW_MATCH2(node, NEW_GVAR(rb_intern("$_")));
@@ -5092,18 +5282,28 @@ cond0(node)
if (type == NODE_DOT2) nd_set_type(node,NODE_FLIP2);
else if (type == NODE_DOT3) nd_set_type(node, NODE_FLIP3);
node->nd_cnt = local_append(internal_id());
- warning_unless_e_option("range literal in condition");
+ if (!e_option_supplied()) {
+ int b = literal_node(node->nd_beg);
+ int e = literal_node(node->nd_end);
+ if ((b == 1 && e == 1) || (b + e >= 2 && RTEST(ruby_verbose))) {
+ parser_warn(node, "range literal in condition");
+ }
+ }
+ break;
+
+ case NODE_DSYM:
+ parser_warning(node, "literal in condition");
break;
case NODE_LIT:
if (TYPE(node->nd_lit) == T_REGEXP) {
- warn_unless_e_option("regex literal in condition");
+ warn_unless_e_option(node, "regex literal in condition");
nd_set_type(node, NODE_MATCH);
local_cnt('_');
local_cnt('~');
}
else {
- rb_warning("literal in condition");
+ parser_warning(node, "literal in condition");
}
default:
break;
@@ -5135,10 +5335,39 @@ logop(type, left, right)
while ((second = node->nd_2nd) != 0 && nd_type(second) == type) {
node = second;
}
- node->nd_2nd = rb_node_newnode(type, second, right, 0);
+ node->nd_2nd = NEW_NODE(type, second, right, 0);
return left;
}
- return rb_node_newnode(type, left, right, 0);
+ return NEW_NODE(type, left, right, 0);
+}
+
+static int
+cond_negative(nodep)
+ NODE **nodep;
+{
+ NODE *c = *nodep;
+
+ if (!c) return 0;
+ switch (nd_type(c)) {
+ case NODE_NOT:
+ *nodep = c->nd_body;
+ return 1;
+ case NODE_NEWLINE:
+ if (c->nd_next && nd_type(c->nd_next) == NODE_NOT) {
+ c->nd_next = c->nd_next->nd_body;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void
+no_blockarg(node)
+ NODE *node;
+{
+ if (node && nd_type(node) == NODE_BLOCK_PASS) {
+ rb_compile_error("block argument should not be given");
+ }
}
static NODE *
@@ -5146,9 +5375,56 @@ ret_args(node)
NODE *node;
{
if (node) {
- if (nd_type(node) == NODE_BLOCK_PASS) {
- rb_compile_error("block argument should not be given");
+ no_blockarg(node);
+ if (nd_type(node) == NODE_ARRAY && node->nd_next == 0) {
+ node = node->nd_head;
}
+ if (nd_type(node) == NODE_RESTARY) {
+ nd_set_type(node, NODE_SPLAT);
+ }
+ }
+ return node;
+}
+
+static NODE *
+new_yield(node)
+ NODE *node;
+{
+ long state = Qtrue;
+
+ if (node) {
+ no_blockarg(node);
+ if (nd_type(node) == NODE_ARRAY && node->nd_next == 0) {
+ node = node->nd_head;
+ state = Qfalse;
+ }
+ if (nd_type(node) == NODE_RESTARY) {
+ nd_set_type(node, NODE_SPLAT);
+ state = Qfalse;
+ }
+ }
+ else {
+ state = Qfalse;
+ }
+ return NEW_YIELD(node, state);
+}
+
+static NODE*
+negate_lit(node)
+ NODE *node;
+{
+ switch (TYPE(node->nd_lit)) {
+ case T_FIXNUM:
+ node->nd_lit = LONG2FIX(-FIX2LONG(node->nd_lit));
+ break;
+ case T_BIGNUM:
+ node->nd_lit = rb_funcall(node->nd_lit,tUMINUS,0,0);
+ break;
+ case T_FLOAT:
+ RFLOAT(node->nd_lit)->value = -RFLOAT(node->nd_lit)->value;
+ break;
+ default:
+ break;
}
return node;
}
@@ -5173,7 +5449,8 @@ arg_prepend(node1, node2)
case NODE_ARRAY:
return list_concat(NEW_LIST(node1), node2);
- case NODE_RESTARGS:
+ case NODE_RESTARY:
+ case NODE_RESTARY2:
return arg_concat(node1, node2->nd_head);
case NODE_BLOCK_PASS:
@@ -5403,6 +5680,21 @@ dyna_in_block()
return (lvtbl->dlev > 0);
}
+static NODE *
+dyna_init(node, pre)
+ NODE *node;
+ struct RVarmap *pre;
+{
+ struct RVarmap *post = ruby_dyna_vars;
+ NODE *var;
+
+ if (!node || !post || pre == post) return node;
+ for (var = 0; post != pre && post->id; post = post->next) {
+ var = NEW_DASGN_CURR(post->id, var);
+ }
+ return block_append(var, node);
+}
+
int
ruby_parser_stack_on_heap()
{
@@ -5509,7 +5801,7 @@ Init_sym()
sym_rev_tbl = st_init_numtable_with_size(200);
}
-static ID last_id = LAST_TOKEN;
+static ID last_id = tLAST_TOKEN;
static ID
internal_id()
@@ -5525,9 +5817,10 @@ rb_intern(name)
ID id;
int last;
- if (st_lookup(sym_tbl, name, &id))
+ if (st_lookup(sym_tbl, (st_data_t)name, (st_data_t *)&id))
return id;
+ last = strlen(name)-1;
id = 0;
switch (*name) {
case '$':
@@ -5559,7 +5852,6 @@ rb_intern(name)
}
}
- last = strlen(name)-1;
if (name[last] == '=') {
/* attribute assignment */
char *buf = ALLOCA_N(char,last+1);
@@ -5567,7 +5859,7 @@ rb_intern(name)
strncpy(buf, name, last);
buf[last] = '\0';
id = rb_intern(buf);
- if (id > LAST_TOKEN && !is_attrset_id(id)) {
+ if (id > tLAST_TOKEN && !is_attrset_id(id)) {
id = rb_id_attrset(id);
goto id_regist;
}
@@ -5581,15 +5873,15 @@ rb_intern(name)
}
break;
}
- while (*m && is_identchar(*m)) {
- m++;
+ while (m <= name + last && is_identchar(*m)) {
+ m += mbclen(*m);
}
if (*m) id = ID_JUNK;
id |= ++last_id << ID_SCOPE_SHIFT;
id_regist:
name = strdup(name);
- st_add_direct(sym_tbl, name, id);
- st_add_direct(sym_rev_tbl, id, name);
+ st_add_direct(sym_tbl, (st_data_t)name, id);
+ st_add_direct(sym_rev_tbl, id, (st_data_t)name);
return id;
}
@@ -5599,7 +5891,7 @@ rb_id2name(id)
{
char *name;
- if (id < LAST_TOKEN) {
+ if (id < tLAST_TOKEN) {
int i = 0;
for (i=0; op_tbl[i].token; i++) {
@@ -5608,7 +5900,7 @@ rb_id2name(id)
}
}
- if (st_lookup(sym_rev_tbl, id, &name))
+ if (st_lookup(sym_rev_tbl, id, (st_data_t *)&name))
return name;
if (is_attrset_id(id)) {
diff --git a/prec.c b/prec.c
index aad80d9c1f..8f28648715 100644
--- a/prec.c
+++ b/prec.c
@@ -6,7 +6,7 @@
$Date$
created at: Tue Jan 26 02:40:41 2000
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
**********************************************************************/
@@ -46,7 +46,7 @@ prec_induced_from(module, x)
VALUE module, x;
{
rb_raise(rb_eTypeError, "undefined conversion from %s into %s",
- rb_class2name(CLASS_OF(x)), rb_class2name(module));
+ rb_obj_classname(x), rb_class2name(module));
return Qnil; /* not reached */
}
diff --git a/process.c b/process.c
index 0052846ca6..3c46f836d0 100644
--- a/process.c
+++ b/process.c
@@ -6,7 +6,7 @@
$Date$
created at: Tue Aug 10 14:30:50 JST 1993
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
Copyright (C) 2000 Information-technology Promotion Agency, Japan
@@ -20,6 +20,9 @@
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
+#ifdef __DJGPP__
+#include <process.h>
+#endif
#include <time.h>
#include <ctype.h>
@@ -42,6 +45,10 @@ struct timeval rb_time_interval _((VALUE));
#include <sys/times.h>
#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+
#if defined(HAVE_TIMES) || defined(_WIN32)
static VALUE S_Tms;
#endif
@@ -65,6 +72,29 @@ static VALUE S_Tms;
#define WSTOPSIG WEXITSTATUS
#endif
+#if defined(__APPLE__) && defined(__MACH__) && !defined(__MacOS_X__)
+#define __MacOS_X__ 1
+#endif
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__)
+#define HAVE_44BSD_SETUID 1
+#define HAVE_44BSD_SETGID 1
+#endif
+
+#if defined(__MacOS_X__) || defined(__bsdi__)
+#define BROKEN_SETREUID 1
+#define BROKEN_SETREGID 1
+#endif
+
+#if defined(HAVE_44BSD_SETUID) || defined(__MacOS_X__)
+#if !defined(USE_SETREUID) && !defined(BROKEN_SETREUID)
+#define OBSOLETE_SETREUID 1
+#endif
+#if !defined(USE_SETREGID) && !defined(BROKEN_SETREGID)
+#define OBSOLETE_SETREGID 1
+#endif
+#endif
+
static VALUE
get_pid()
{
@@ -267,7 +297,7 @@ rb_waitpid(pid, st, flags)
#else /* NO_WAITPID */
if (pid_tbl && st_lookup(pid_tbl, pid, st)) {
last_status_set(*st, pid);
- st_delete(pid_tbl, &pid, NULL);
+ st_delete(pid_tbl, (st_data_t*)&pid, NULL);
return pid;
}
@@ -408,6 +438,33 @@ proc_waitall()
return result;
}
+static VALUE
+detach_process_watcer(pid_p)
+ int *pid_p;
+{
+ int cpid, status;
+
+ for (;;) {
+ cpid = rb_waitpid(*pid_p, &status, WNOHANG);
+ if (cpid == -1) return Qnil;
+ rb_thread_sleep(1);
+ }
+}
+
+VALUE
+rb_detach_process(pid)
+ int pid;
+{
+ return rb_thread_create(detach_process_watcer, (void*)&pid);
+}
+
+static VALUE
+proc_detach(obj, pid)
+ VALUE pid;
+{
+ return rb_detach_process(NUM2INT(pid));
+}
+
#ifndef HAVE_STRING_H
char *strtok();
#endif
@@ -438,17 +495,13 @@ proc_exec_v(argv, prog)
char **argv;
char *prog;
{
- if (prog) {
- security(prog);
- }
- else {
- security(argv[0]);
- prog = dln_find_exe(argv[0], 0);
- if (!prog) {
- errno = ENOENT;
- return -1;
- }
- }
+ if (!prog)
+ prog = argv[0];
+ security(prog);
+ prog = dln_find_exe(prog, 0);
+ if (!prog)
+ return -1;
+
#if (defined(MSDOS) && !defined(DJGPP)) || defined(__human68k__) || defined(__EMX__) || defined(OS2)
{
#if defined(__human68k__)
@@ -487,7 +540,11 @@ proc_exec_v(argv, prog)
}
#endif /* MSDOS or __human68k__ or __EMX__ */
before_exec();
+#ifdef _WIN32
+ do_aspawn(P_OVERLAY, prog, argv);
+#else
execv(prog, argv);
+#endif
after_exec();
return -1;
}
@@ -528,6 +585,11 @@ rb_proc_exec(str)
while (*str && ISSPACE(*str))
str++;
+#ifdef _WIN32
+ before_exec();
+ do_spawn(P_OVERLAY, (char *)str);
+ after_exec();
+#else
for (s=str; *s; s++) {
if (*s != ' ' && !ISALPHA(*s) && strchr("*?{}[]<>()~&|\\$;'`\"\n",*s)) {
#if defined(MSDOS)
@@ -571,10 +633,11 @@ rb_proc_exec(str)
return proc_exec_v(argv, 0);
}
errno = ENOENT;
+#endif /* _WIN32 */
return -1;
}
-#if defined(__human68k__)
+#if defined(__human68k__) || defined(__DJGPP__) || defined(_WIN32)
static int
proc_spawn_v(argv, prog)
char **argv;
@@ -583,16 +646,14 @@ proc_spawn_v(argv, prog)
char *extension;
int status;
- if (prog) {
- security(prog);
- }
- else {
- security(argv[0]);
- prog = dln_find_exe(argv[0], 0);
- if (!prog)
- return -1;
- }
+ if (!prog)
+ prog = argv[0];
+ security(prog);
+ prog = dln_find_exe(prog, 0);
+ if (!prog)
+ return -1;
+#if defined(__human68k__)
if ((extension = strrchr(prog, '.')) != NULL && strcasecmp(extension, ".bat") == 0) {
char **new_argv;
char *p;
@@ -615,8 +676,13 @@ proc_spawn_v(argv, prog)
return -1;
}
}
+#endif
before_exec();
+#if defined(_WIN32)
+ status = do_aspawn(P_WAIT, prog, argv);
+#else
status = spawnv(P_WAIT, prog, argv);
+#endif
after_exec();
return status;
}
@@ -632,16 +698,18 @@ proc_spawn_n(argc, argv, prog)
args = ALLOCA_N(char*, argc + 1);
for (i = 0; i < argc; i++) {
- SafeStr(argv[i]);
+ SafeStringValue(argv[i]);
args[i] = RSTRING(argv[i])->ptr;
}
- SafeStringValue(prog);
+ if (prog)
+ SafeStringValue(prog);
args[i] = (char*) 0;
if (args[0])
- return proc_spawn_v(args, RSTRING(prog)->ptr);
+ return proc_spawn_v(args, prog ? RSTRING(prog)->ptr : 0);
return -1;
}
+#if !defined(_WIN32)
static int
proc_spawn(sv)
VALUE sv;
@@ -672,7 +740,8 @@ proc_spawn(sv)
}
return argv[0] ? proc_spawn_v(argv, 0) : -1;
}
-#endif /* __human68k__ */
+#endif
+#endif
VALUE
rb_f_exec(argc, argv)
@@ -680,20 +749,20 @@ rb_f_exec(argc, argv)
VALUE *argv;
{
VALUE prog = 0;
+ VALUE tmp;
if (argc == 0) {
rb_raise(rb_eArgError, "wrong number of arguments");
}
- if (TYPE(argv[0]) == T_ARRAY) {
- if (RARRAY(argv[0])->len != 2) {
+ tmp = rb_check_array_type(argv[0]);
+ if (!NIL_P(tmp)) {
+ if (RARRAY(tmp)->len != 2) {
rb_raise(rb_eArgError, "wrong first argument");
}
- prog = RARRAY(argv[0])->ptr[0];
- argv[0] = RARRAY(argv[0])->ptr[1];
- }
- if (prog) {
+ prog = RARRAY(tmp)->ptr[0];
SafeStringValue(prog);
+ argv[0] = RARRAY(tmp)->ptr[1];
}
if (argc == 1 && prog == 0) {
VALUE cmd = argv[0];
@@ -805,7 +874,7 @@ rb_f_system(argc, argv)
int argc;
VALUE *argv;
{
-#if defined(_WIN32) || defined(__EMX__)
+#if defined(__EMX__)
VALUE cmd;
int status;
@@ -830,34 +899,10 @@ rb_f_system(argc, argv)
if (status == 0) return Qtrue;
return Qfalse;
-#elif defined(DJGPP)
- VALUE cmd;
- int status;
-
- if (argc == 0) {
- rb_last_status = Qnil;
- rb_raise(rb_eArgError, "wrong number of arguments");
- }
-
- if (TYPE(argv[0]) == T_ARRAY) {
- if (RARRAY(argv[0])->len != 2) {
- rb_raise(rb_eArgError, "wrong first argument");
- }
- argv[0] = RARRAY(argv[0])->ptr[0];
- }
- cmd = rb_ary_join(rb_ary_new4(argc, argv), rb_str_new2(" "));
-
- SafeStringValue(cmd);
- status = system(RSTRING(cmd)->ptr);
- last_status_set((status & 0xff) << 8, 0);
-
- if (status == 0) return Qtrue;
- return Qfalse;
-#elif defined(__human68k__)
- VALUE prog = 0;
+#elif defined(__human68k__) || defined(__DJGPP__) || defined(_WIN32)
+ volatile VALUE prog = 0;
int status;
- fflush(stdin);
fflush(stdout);
fflush(stderr);
if (argc == 0) {
@@ -874,12 +919,20 @@ rb_f_system(argc, argv)
}
if (argc == 1 && prog == 0) {
+#if defined(_WIN32)
+ status = do_spawn(P_WAIT, RSTRING(argv[0])->ptr);
+#else
status = proc_spawn(argv[0]);
+#endif
}
else {
status = proc_spawn_n(argc, argv, prog);
}
+#if defined(_WIN32)
+ last_status_set(status, 0);
+#else
last_status_set(status == -1 ? 127 : status, 0);
+#endif
return status == 0 ? Qtrue : Qfalse;
#elif defined(__VMS)
VALUE cmd;
@@ -1128,6 +1181,66 @@ proc_setpriority(obj, which, who, prio)
}
static VALUE
+p_sys_setuid(obj, id)
+ VALUE obj, id;
+{
+#if defined HAVE_SETUID
+ if (setuid(NUM2INT(id)) != 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return Qnil;
+}
+
+static VALUE
+p_sys_setruid(obj, id)
+ VALUE obj, id;
+{
+#if defined HAVE_SETRUID
+ if (setruid(NUM2INT(id)) != 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return Qnil;
+}
+
+static VALUE
+p_sys_seteuid(obj, id)
+ VALUE obj, id;
+{
+#if defined HAVE_SETEUID
+ if (seteuid(NUM2INT(id)) != 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return Qnil;
+}
+
+static VALUE
+p_sys_setreuid(obj, rid, eid)
+ VALUE obj, rid, eid;
+{
+#if defined HAVE_SETREUID
+ if (setreuid(NUM2INT(rid),NUM2INT(eid)) != 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return Qnil;
+}
+
+static VALUE
+p_sys_setresuid(obj, rid, eid, sid)
+ VALUE obj, rid, eid, sid;
+{
+#if defined HAVE_SETRESUID
+ if (setresuid(NUM2INT(rid),NUM2INT(eid),NUM2INT(sid)) != 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return Qnil;
+}
+
+static VALUE
proc_getuid(obj)
VALUE obj;
{
@@ -1147,7 +1260,7 @@ proc_setuid(obj, id)
if (setreuid(uid, -1) < 0) rb_sys_fail(0);
#elif defined HAVE_SETRUID
if (setruid(uid) < 0) rb_sys_fail(0);
-#else
+#elif defined HAVE_SETUID
{
if (geteuid() == uid) {
if (setuid(uid) < 0) rb_sys_fail(0);
@@ -1156,10 +1269,228 @@ proc_setuid(obj, id)
rb_notimplement();
}
}
+#else
+ rb_notimplement();
#endif
return INT2FIX(uid);
}
+static int SAVED_USER_ID;
+
+static VALUE
+p_uid_change_privilege(obj, id)
+ VALUE obj, id;
+{
+ extern int errno;
+ int uid;
+
+ uid = NUM2INT(id);
+
+ if (geteuid() == 0) { /* root-user */
+#if defined(HAVE_SETRESUID)
+ if (setresuid(uid, uid, uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+#elif defined(HAVE_SETUID)
+ if (setuid(uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
+ if (getuid() == uid) {
+ if (SAVED_USER_ID == uid) {
+ if (setreuid(-1, uid) < 0) rb_sys_fail(0);
+ } else {
+ if (uid == 0) { /* (r,e,s) == (root, root, x) */
+ if (setreuid(-1, SAVED_USER_ID) < 0) rb_sys_fail(0);
+ if (setreuid(SAVED_USER_ID, 0) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = 0; /* (r,e,s) == (x, root, root) */
+ if (setreuid(uid, uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ } else {
+ if (setreuid(0, -1) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = 0;
+ if (setreuid(uid, uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ }
+ }
+ } else {
+ if (setreuid(uid, uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ }
+#elif defined(HAVE_SETRUID) && defined(HAVE_SETEUID)
+ if (getuid() == uid) {
+ if (SAVED_USER_ID == uid) {
+ if (seteuid(uid) < 0) rb_sys_fail(0);
+ } else {
+ if (uid == 0) {
+ if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = 0;
+ if (setruid(0) < 0) rb_sys_fail(0);
+ } else {
+ if (setruid(0) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = 0;
+ if (seteuid(uid) < 0) rb_sys_fail(0);
+ if (setruid(uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ }
+ }
+ } else {
+ if (seteuid(uid) < 0) rb_sys_fail(0);
+ if (setruid(uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ }
+#else
+ rb_notimplement();
+#endif
+ } else { /* unprivileged user */
+#if defined(HAVE_SETRESUID)
+ if (setresuid((getuid() == uid)? -1: uid,
+ (geteuid() == uid)? -1: uid,
+ (SAVED_USER_ID == uid)? -1: uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
+ if (SAVED_USER_ID == uid) {
+ if (setreuid((getuid() == uid)? -1: uid,
+ (geteuid() == uid)? -1: uid) < 0) rb_sys_fail(0);
+ } else if (getuid() != uid) {
+ if (setreuid(uid, (geteuid() == uid)? -1: uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ } else if (/* getuid() == uid && */ geteuid() != uid) {
+ if (setreuid(geteuid(), uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ if (setreuid(uid, -1) < 0) rb_sys_fail(0);
+ } else { /* getuid() == uid && geteuid() == uid */
+ if (setreuid(-1, SAVED_USER_ID) < 0) rb_sys_fail(0);
+ if (setreuid(SAVED_USER_ID, uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ if (setreuid(uid, -1) < 0) rb_sys_fail(0);
+ }
+#elif defined(HAVE_SETRUID) && defined(HAVE_SETEUID)
+ if (SAVED_USER_ID == uid) {
+ if (geteuid() != uid && seteuid(uid) < 0) rb_sys_fail(0);
+ if (getuid() != uid && setruid(uid) < 0) rb_sys_fail(0);
+ } else if (/* SAVED_USER_ID != uid && */ geteuid() == uid) {
+ if (getuid() != uid) {
+ if (setruid(uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ } else {
+ if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ if (setruid(uid) < 0) rb_sys_fail(0);
+ }
+ } else if (/* geteuid() != uid && */ getuid() == uid) {
+ if (seteuid(uid) < 0) rb_sys_fail(0);
+ if (setruid(SAVED_USER_ID) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ if (setruid(uid) < 0) rb_sys_fail(0);
+ } else {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+#elif defined HAVE_44BSD_SETUID
+ if (getuid() == uid) {
+ /* (r,e,s)==(uid,?,?) ==> (uid,uid,uid) */
+ if (setuid(uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+ } else {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+#elif defined HAVE_SETEUID
+ if (getuid() == uid && SAVED_USER_ID == uid) {
+ if (seteuid(uid) < 0) rb_sys_fail(0);
+ } else {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+#elif defined HAVE_SETUID
+ if (getuid() == uid && SAVED_USER_ID == uid) {
+ if (setuid(uid) < 0) rb_sys_fail(0);
+ } else {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+#else
+ rb_notimplement();
+#endif
+ }
+ return INT2FIX(uid);
+}
+
+static VALUE
+p_sys_setgid(obj, id)
+ VALUE obj, id;
+{
+#if defined HAVE_SETGID
+ if (setgid(NUM2INT(id)) != 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return Qnil;
+}
+
+static VALUE
+p_sys_setrgid(obj, id)
+ VALUE obj, id;
+{
+#if defined HAVE_SETRGID
+ if (setrgid(NUM2INT(id)) != 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return Qnil;
+}
+
+static VALUE
+p_sys_setegid(obj, id)
+ VALUE obj, id;
+{
+#if defined HAVE_SETEGID
+ if (setegid(NUM2INT(id)) != 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return Qnil;
+}
+
+static VALUE
+p_sys_setregid(obj, rid, eid)
+ VALUE obj, rid, eid;
+{
+#if defined HAVE_SETREGID
+ if (setregid(NUM2INT(rid),NUM2INT(eid)) != 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return Qnil;
+}
+
+static VALUE
+p_sys_setresgid(obj, rid, eid, sid)
+ VALUE obj, rid, eid, sid;
+{
+#if defined HAVE_SETRESGID
+ if (setresgid(NUM2INT(rid),NUM2INT(eid),NUM2INT(sid)) != 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return Qnil;
+}
+
+static VALUE
+p_sys_issetugid(obj)
+ VALUE obj;
+{
+#if defined HAVE_ISSETUGID
+ if (issetugid()) {
+ return Qtrue;
+ } else {
+ return Qfalse;
+ }
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif
+}
+
static VALUE
proc_getgid(obj)
VALUE obj;
@@ -1178,9 +1509,9 @@ proc_setgid(obj, id)
if (setresgid(gid, -1, -1) < 0) rb_sys_fail(0);
#elif defined HAVE_SETREGID
if (setregid(gid, -1) < 0) rb_sys_fail(0);
-#elif defined HAS_SETRGID
+#elif defined HAVE_SETRGID
if (setrgid((GIDTYPE)gid) < 0) rb_sys_fail(0);
-#else
+#elif defined HAVE_SETGID
{
if (getegid() == gid) {
if (setgid(gid) < 0) rb_sys_fail(0);
@@ -1189,10 +1520,268 @@ proc_setgid(obj, id)
rb_notimplement();
}
}
+#else
+ rb_notimplement();
#endif
return INT2FIX(gid);
}
+
+static size_t maxgroups = 32;
+
+static VALUE
+proc_getgroups(VALUE obj)
+{
+#ifdef HAVE_GETGROUPS
+ VALUE ary;
+ size_t ngroups;
+ gid_t *groups;
+ int i;
+
+ groups = ALLOCA_N(gid_t, maxgroups);
+
+ ngroups = getgroups(maxgroups, groups);
+ if (ngroups == -1)
+ rb_sys_fail(0);
+
+ ary = rb_ary_new();
+ for (i = 0; i < ngroups; i++)
+ rb_ary_push(ary, INT2NUM(groups[i]));
+
+ return ary;
+#else
+ rb_notimplement();
+ return Qnil;
+#endif
+}
+
+static VALUE
+proc_setgroups(VALUE obj, VALUE ary)
+{
+#ifdef HAVE_SETGROUPS
+ size_t ngroups;
+ gid_t *groups;
+ int i;
+ struct group *gr;
+
+ Check_Type(ary, T_ARRAY);
+
+ ngroups = RARRAY(ary)->len;
+ if (ngroups > maxgroups)
+ rb_raise(rb_eArgError, "too many groups, %d max", maxgroups);
+
+ groups = ALLOCA_N(gid_t, ngroups);
+
+ for (i = 0; i < ngroups; i++) {
+ VALUE g = RARRAY(ary)->ptr[i];
+
+ if (FIXNUM_P(g)) {
+ groups[i] = FIX2INT(g);
+ }
+ else {
+ VALUE tmp = rb_check_string_type(g);
+
+ if (NIL_P(tmp)) {
+ groups[i] = NUM2INT(g);
+ }
+ else {
+ gr = getgrnam(RSTRING(g)->ptr);
+ if (gr == NULL)
+ rb_raise(rb_eArgError,
+ "can't find group for %s", RSTRING(g)->ptr);
+ groups[i] = gr->gr_gid;
+ }
+ }
+ }
+
+ i = setgroups(ngroups, groups);
+ if (i == -1)
+ rb_sys_fail(0);
+
+ return proc_getgroups(obj);
+#else
+ rb_notimplement();
+ return Qnil;
+#endif
+}
+
+static VALUE
+proc_initgroups(obj, uname, base_grp)
+ VALUE obj, uname, base_grp;
+{
+#ifdef HAVE_INITGROUPS
+ if (initgroups(StringValuePtr(uname), (gid_t)NUM2INT(base_grp)) != 0) {
+ rb_sys_fail(0);
+ }
+ return proc_getgroups(obj);
+#else
+ rb_notimplement();
+ return Qnil;
+#endif
+}
+
+static VALUE
+proc_getmaxgroups(obj)
+ VALUE obj;
+{
+ return INT2FIX(maxgroups);
+}
+
+static VALUE
+proc_setmaxgroups(obj, val)
+ VALUE obj;
+{
+ size_t ngroups = FIX2INT(val);
+
+ if (ngroups > 4096)
+ ngroups = 4096;
+
+ maxgroups = ngroups;
+
+ return INT2FIX(maxgroups);
+}
+
+static int SAVED_GROUP_ID;
+
+static VALUE
+p_gid_change_privilege(obj, id)
+ VALUE obj, id;
+{
+ extern int errno;
+ int gid;
+
+ gid = NUM2INT(id);
+
+ if (geteuid() == 0) { /* root-user */
+#if defined(HAVE_SETRESGID)
+ if (setresgid(gid, gid, gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+#elif defined HAVE_SETGID
+ if (setgid(gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
+ if (getgid() == gid) {
+ if (SAVED_GROUP_ID == gid) {
+ if (setregid(-1, gid) < 0) rb_sys_fail(0);
+ } else {
+ if (gid == 0) { /* (r,e,s) == (root, y, x) */
+ if (setregid(-1, SAVED_GROUP_ID) < 0) rb_sys_fail(0);
+ if (setregid(SAVED_GROUP_ID, 0) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = 0; /* (r,e,s) == (x, root, root) */
+ if (setregid(gid, gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ } else { /* (r,e,s) == (z, y, x) */
+ if (setregid(0, 0) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = 0;
+ if (setregid(gid, gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ }
+ }
+ } else {
+ if (setregid(gid, gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ }
+#elif defined(HAVE_SETRGID) && defined (HAVE_SETEGID)
+ if (getgid() == gid) {
+ if (SAVED_GROUP_ID == gid) {
+ if (setegid(gid) < 0) rb_sys_fail(0);
+ } else {
+ if (gid == 0) {
+ if (setegid(gid) < 0) rb_sys_fail(0);
+ if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = 0;
+ if (setrgid(0) < 0) rb_sys_fail(0);
+ } else {
+ if (setrgid(0) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = 0;
+ if (setegid(gid) < 0) rb_sys_fail(0);
+ if (setrgid(gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ }
+ }
+ } else {
+ if (setegid(gid) < 0) rb_sys_fail(0);
+ if (setrgid(gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ }
+#else
+ rb_notimplement();
+#endif
+ } else { /* unprivileged user */
+#if defined(HAVE_SETRESGID)
+ if (setresgid((getgid() == gid)? -1: gid,
+ (getegid() == gid)? -1: gid,
+ (SAVED_GROUP_ID == gid)? -1: gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
+ if (SAVED_GROUP_ID == gid) {
+ if (setregid((getgid() == gid)? -1: gid,
+ (getegid() == gid)? -1: gid) < 0) rb_sys_fail(0);
+ } else if (getgid() != gid) {
+ if (setregid(gid, (getegid() == gid)? -1: gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ } else if (/* getgid() == gid && */ getegid() != gid) {
+ if (setregid(getegid(), gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ if (setregid(gid, -1) < 0) rb_sys_fail(0);
+ } else { /* getgid() == gid && getegid() == gid */
+ if (setregid(-1, SAVED_GROUP_ID) < 0) rb_sys_fail(0);
+ if (setregid(SAVED_GROUP_ID, gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ if (setregid(gid, -1) < 0) rb_sys_fail(0);
+ }
+#elif defined(HAVE_SETRGID) && defined(HAVE_SETEGID)
+ if (SAVED_GROUP_ID == gid) {
+ if (getegid() != gid && setegid(gid) < 0) rb_sys_fail(0);
+ if (getgid() != gid && setrgid(gid) < 0) rb_sys_fail(0);
+ } else if (/* SAVED_GROUP_ID != gid && */ getegid() == gid) {
+ if (getgid() != gid) {
+ if (setrgid(gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ } else {
+ if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ if (setrgid(gid) < 0) rb_sys_fail(0);
+ }
+ } else if (/* getegid() != gid && */ getgid() == gid) {
+ if (setegid(gid) < 0) rb_sys_fail(0);
+ if (setrgid(SAVED_GROUP_ID) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ if (setrgid(gid) < 0) rb_sys_fail(0);
+ } else {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+#elif defined HAVE_44BSD_SETGID
+ if (getgid() == gid) {
+ /* (r,e,s)==(gid,?,?) ==> (gid,gid,gid) */
+ if (setgid(gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+ } else {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+#elif defined HAVE_SETEGID
+ if (getgid() == gid && SAVED_GROUP_ID == gid) {
+ if (setegid(gid) < 0) rb_sys_fail(0);
+ } else {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+#elif defined HAVE_SETGID
+ if (getgid() == gid && SAVED_GROUP_ID == gid) {
+ if (setgid(gid) < 0) rb_sys_fail(0);
+ } else {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+#else
+ rb_notimplement();
+#endif
+ }
+ return INT2FIX(gid);
+}
+
static VALUE
proc_geteuid(obj)
VALUE obj;
@@ -1211,7 +1800,7 @@ proc_seteuid(obj, euid)
if (setreuid(-1, NUM2INT(euid)) < 0) rb_sys_fail(0);
#elif defined HAVE_SETEUID
if (seteuid(NUM2INT(euid)) < 0) rb_sys_fail(0);
-#else
+#elif defined HAVE_SETUID
euid = NUM2INT(euid);
if (euid == getuid()) {
if (setuid(euid) < 0) rb_sys_fail(0);
@@ -1219,11 +1808,53 @@ proc_seteuid(obj, euid)
else {
rb_notimplement();
}
+#else
+ rb_notimplement();
#endif
return euid;
}
static VALUE
+rb_seteuid_core(euid)
+ int euid;
+{
+ int uid;
+
+ uid = getuid();
+
+#if defined(HAVE_SETRESUID) && !defined(__CHECKER__)
+ if (uid != euid) {
+ if (setresuid(-1,euid,euid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = euid;
+ } else {
+ if (setresuid(-1,euid,-1) < 0) rb_sys_fail(0);
+ }
+#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
+ if (setreuid(-1, euid) < 0) rb_sys_fail(0);
+ if (uid != euid) {
+ if (setreuid(euid,uid) < 0) rb_sys_fail(0);
+ if (setreuid(uid,euid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = euid;
+ }
+#elif defined HAVE_SETEUID
+ if (seteuid(euid) < 0) rb_sys_fail(0);
+#elif defined HAVE_SETUID
+ if (geteuid() == 0) rb_sys_fail(0);
+ if (setuid(euid) < 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return INT2FIX(euid);
+}
+
+static VALUE
+p_uid_grant_privilege(obj, id)
+ VALUE obj, id;
+{
+ return rb_seteuid_core(NUM2INT(id));
+}
+
+static VALUE
proc_getegid(obj)
VALUE obj;
{
@@ -1243,7 +1874,7 @@ proc_setegid(obj, egid)
if (setregid(-1, NUM2INT(egid)) < 0) rb_sys_fail(0);
#elif defined HAVE_SETEGID
if (setegid(NUM2INT(egid)) < 0) rb_sys_fail(0);
-#else
+#elif defined HAVE_SETGID
egid = NUM2INT(egid);
if (egid == getgid()) {
if (setgid(egid) < 0) rb_sys_fail(0);
@@ -1251,10 +1882,225 @@ proc_setegid(obj, egid)
else {
rb_notimplement();
}
+#else
+ rb_notimplement();
#endif
return egid;
}
+static VALUE
+rb_setegid_core(egid)
+ int egid;
+{
+ int gid;
+
+ gid = getgid();
+
+#if defined(HAVE_SETRESGID) && !defined(__CHECKER__)
+ if (gid != egid) {
+ if (setresgid(-1,egid,egid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = egid;
+ } else {
+ if (setresgid(-1,egid,-1) < 0) rb_sys_fail(0);
+ }
+#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
+ if (setregid(-1, egid) < 0) rb_sys_fail(0);
+ if (gid != egid) {
+ if (setregid(egid,gid) < 0) rb_sys_fail(0);
+ if (setregid(gid,egid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = egid;
+ }
+#elif defined HAVE_SETEGID
+ if (setegid(egid) < 0) rb_sys_fail(0);
+#elif defined HAVE_SETGID
+ if (geteuid() == 0 /* root user */) rb_sys_fail(0);
+ if (setgid(egid) < 0) rb_sys_fail(0);
+#else
+ rb_notimplement();
+#endif
+ return INT2FIX(egid);
+}
+
+static VALUE
+p_gid_grant_privilege(obj, id)
+ VALUE obj, id;
+{
+ return rb_setegid_core(NUM2INT(id));
+}
+
+static VALUE
+p_uid_exchangeable()
+{
+#if defined(HAVE_SETRESUID) && !defined(__CHECKER__)
+ return Qtrue;
+#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
+ return Qtrue;
+#else
+ return Qfalse;
+#endif
+}
+
+static VALUE
+p_uid_exchange(obj)
+ VALUE obj;
+{
+ int uid, euid;
+
+ uid = getuid();
+ euid = geteuid();
+
+#if defined(HAVE_SETRESUID) && !defined(__CHECKER__)
+ if (setresuid(euid, uid, uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+#elif defined(HAVE_SETREUID) && !defined(OBSOLETE_SETREUID)
+ if (setreuid(euid,uid) < 0) rb_sys_fail(0);
+ SAVED_USER_ID = uid;
+#else
+ rb_notimplement();
+#endif
+ return INT2FIX(uid);
+}
+
+static VALUE
+p_gid_exchangeable()
+{
+#if defined(HAVE_SETRESGID) && !defined(__CHECKER__)
+ return Qtrue;
+#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
+ return Qtrue;
+#else
+ return Qfalse;
+#endif
+}
+
+static VALUE
+p_gid_exchange(obj)
+ VALUE obj;
+{
+ int gid, egid;
+
+ gid = getgid();
+ egid = getegid();
+
+#if defined(HAVE_SETRESGID) && !defined(__CHECKER__)
+ if (setresgid(egid, gid, gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+#elif defined(HAVE_SETREGID) && !defined(OBSOLETE_SETREGID)
+ if (setregid(egid,gid) < 0) rb_sys_fail(0);
+ SAVED_GROUP_ID = gid;
+#else
+ rb_notimplement();
+#endif
+ return INT2FIX(gid);
+}
+
+static VALUE
+p_uid_have_saved_id()
+{
+#if defined(HAVE_SETRESUID) || defined(HAVE_SETEUID) || _POSIX_SAVED_IDS
+ return Qtrue;
+#else
+ return Qfalse;
+#endif
+}
+
+static VALUE
+p_uid_switch(obj)
+ VALUE obj;
+{
+ extern int errno;
+ int uid, euid;
+
+ uid = getuid();
+ euid = geteuid();
+
+#if defined(HAVE_SETRESUID) || defined(HAVE_SETEUID) || _POSIX_SAVED_IDS
+ if (uid != euid) {
+ proc_seteuid(obj, INT2FIX(uid));
+ if (rb_block_given_p()) {
+ return rb_ensure(rb_yield, Qnil, rb_seteuid_core, SAVED_USER_ID);
+ } else {
+ return INT2FIX(euid);
+ }
+ } else if (euid != SAVED_USER_ID) {
+ proc_seteuid(obj, INT2FIX(SAVED_USER_ID));
+ if (rb_block_given_p()) {
+ return rb_ensure(rb_yield, Qnil, rb_seteuid_core, euid);
+ } else {
+ return INT2FIX(uid);
+ }
+ } else {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+#else
+ if (uid == euid) {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+ p_uid_switch(obj);
+ if (rb_block_given_p()) {
+ return rb_ensure(rb_yield, Qnil, p_uid_switch, obj);
+ } else {
+ return INT2FIX(euid);
+ }
+#endif
+}
+
+static VALUE
+p_gid_have_saved_id()
+{
+#if defined(HAVE_SETRESGID) || defined(HAVE_SETEGID) || _POSIX_SAVED_IDS
+ return Qtrue;
+#else
+ return Qfalse;
+#endif
+}
+
+static VALUE
+p_gid_switch(obj)
+ VALUE obj;
+{
+ extern int errno;
+ int gid, egid;
+
+ gid = getgid();
+ egid = getegid();
+
+#if defined(HAVE_SETRESGID) || defined(HAVE_SETEGID) || _POSIX_SAVED_IDS
+ if (gid != egid) {
+ proc_setegid(obj, INT2FIX(gid));
+ if (rb_block_given_p()) {
+ return rb_ensure(rb_yield, Qnil, proc_setegid,
+ INT2FIX(SAVED_GROUP_ID));
+ } else {
+ return INT2FIX(egid);
+ }
+ } else if (egid != SAVED_GROUP_ID) {
+ proc_setegid(obj, INT2FIX(SAVED_GROUP_ID));
+ if (rb_block_given_p()) {
+ return rb_ensure(rb_yield, Qnil, proc_setegid, INT2FIX(egid));
+ } else {
+ return INT2FIX(gid);
+ }
+ } else {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+#else
+ if (gid == egid) {
+ errno = EPERM;
+ rb_sys_fail(0);
+ }
+ p_gid_switch(obj);
+ if (rb_block_given_p()) {
+ return rb_ensure(rb_yield, Qnil, p_gid_switch, obj);
+ } else {
+ return INT2FIX(egid);
+ }
+#endif
+}
+
VALUE
rb_proc_times(obj)
VALUE obj;
@@ -1268,19 +2114,23 @@ rb_proc_times(obj)
# endif
#endif /* HZ */
struct tms buf;
+ volatile VALUE utime, stime, cutime, sctime;
times(&buf);
return rb_struct_new(S_Tms,
- rb_float_new((double)buf.tms_utime / HZ),
- rb_float_new((double)buf.tms_stime / HZ),
- rb_float_new((double)buf.tms_cutime / HZ),
- rb_float_new((double)buf.tms_cstime / HZ));
+ utime = rb_float_new((double)buf.tms_utime / HZ),
+ stime = rb_float_new((double)buf.tms_stime / HZ),
+ cutime = rb_float_new((double)buf.tms_cutime / HZ),
+ sctime = rb_float_new((double)buf.tms_cstime / HZ));
#else
rb_notimplement();
#endif
}
VALUE rb_mProcess;
+VALUE rb_mProcUID;
+VALUE rb_mProcGID;
+VALUE rb_mProcID_Syscall;
void
Init_process()
@@ -1319,6 +2169,7 @@ Init_process()
rb_define_module_function(rb_mProcess, "waitpid", proc_wait, -1);
rb_define_module_function(rb_mProcess, "waitpid2", proc_wait2, -1);
rb_define_module_function(rb_mProcess, "waitall", proc_waitall, 0);
+ rb_define_module_function(rb_mProcess, "detach", proc_detach, 1);
rb_cProcStatus = rb_define_class_under(rb_mProcess, "Status", rb_cObject);
rb_undef_method(CLASS_OF(rb_cProcStatus), "new");
@@ -1368,10 +2219,76 @@ Init_process()
rb_define_module_function(rb_mProcess, "euid=", proc_seteuid, 1);
rb_define_module_function(rb_mProcess, "egid", proc_getegid, 0);
rb_define_module_function(rb_mProcess, "egid=", proc_setegid, 1);
+ rb_define_module_function(rb_mProcess, "initgroups", proc_initgroups, 2);
+ rb_define_module_function(rb_mProcess, "groups", proc_getgroups, 0);
+ rb_define_module_function(rb_mProcess, "groups=", proc_setgroups, 1);
+ rb_define_module_function(rb_mProcess, "maxgroups", proc_getmaxgroups, 0);
+ rb_define_module_function(rb_mProcess, "maxgroups=", proc_setmaxgroups, 1);
rb_define_module_function(rb_mProcess, "times", rb_proc_times, 0);
#if defined(HAVE_TIMES) || defined(_WIN32)
- S_Tms = rb_struct_define("Tms", "utime", "stime", "cutime", "cstime", 0);
+ S_Tms = rb_struct_define("Tms", "utime", "stime", "cutime", "cstime", NULL);
#endif
+
+ SAVED_USER_ID = geteuid();
+ SAVED_GROUP_ID = getegid();
+
+ rb_mProcUID = rb_define_module_under(rb_mProcess, "UID");
+ rb_mProcGID = rb_define_module_under(rb_mProcess, "GID");
+
+ rb_define_module_function(rb_mProcUID, "rid", proc_getuid, 0);
+ rb_define_module_function(rb_mProcGID, "rid", proc_getgid, 0);
+ rb_define_module_function(rb_mProcUID, "eid", proc_geteuid, 0);
+ rb_define_module_function(rb_mProcGID, "eid", proc_getegid, 0);
+ rb_define_module_function(rb_mProcUID, "change_privilege",
+ p_uid_change_privilege, 1);
+ rb_define_module_function(rb_mProcGID, "change_privilege",
+ p_gid_change_privilege, 1);
+ rb_define_module_function(rb_mProcUID, "grant_privilege",
+ p_uid_grant_privilege, 1);
+ rb_define_module_function(rb_mProcGID, "grant_privilege",
+ p_gid_grant_privilege, 1);
+ rb_define_alias(rb_mProcUID, "eid=", "grant_privilege");
+ rb_define_alias(rb_mProcGID, "eid=", "grant_privilege");
+ rb_define_module_function(rb_mProcUID, "re_exchange", p_uid_exchange, 0);
+ rb_define_module_function(rb_mProcGID, "re_exchange", p_gid_exchange, 0);
+ rb_define_module_function(rb_mProcUID, "re_exchangeable?",
+ p_uid_exchangeable, 0);
+ rb_define_module_function(rb_mProcGID, "re_exchangeable?",
+ p_gid_exchangeable, 0);
+ rb_define_module_function(rb_mProcUID, "sid_available?",
+ p_uid_have_saved_id, 0);
+ rb_define_module_function(rb_mProcGID, "sid_available?",
+ p_gid_have_saved_id, 0);
+ rb_define_module_function(rb_mProcUID, "switch", p_uid_switch, 0);
+ rb_define_module_function(rb_mProcGID, "switch", p_gid_switch, 0);
+
+ rb_mProcID_Syscall = rb_define_module_under(rb_mProcess, "Sys");
+
+ rb_define_module_function(rb_mProcID_Syscall, "getuid", proc_getuid, 0);
+ rb_define_module_function(rb_mProcID_Syscall, "geteuid", proc_geteuid, 0);
+ rb_define_module_function(rb_mProcID_Syscall, "getgid", proc_getgid, 0);
+ rb_define_module_function(rb_mProcID_Syscall, "getegid", proc_getegid, 0);
+
+ rb_define_module_function(rb_mProcID_Syscall, "setuid", p_sys_setuid, 1);
+ rb_define_module_function(rb_mProcID_Syscall, "setgid", p_sys_setgid, 1);
+
+ rb_define_module_function(rb_mProcID_Syscall, "setruid", p_sys_setruid, 1);
+ rb_define_module_function(rb_mProcID_Syscall, "setrgid", p_sys_setrgid, 1);
+
+ rb_define_module_function(rb_mProcID_Syscall, "seteuid", p_sys_seteuid, 1);
+ rb_define_module_function(rb_mProcID_Syscall, "setegid", p_sys_setegid, 1);
+
+ rb_define_module_function(rb_mProcID_Syscall, "setreuid",
+ p_sys_setreuid, 2);
+ rb_define_module_function(rb_mProcID_Syscall, "setregid",
+ p_sys_setregid, 2);
+
+ rb_define_module_function(rb_mProcID_Syscall, "setresuid",
+ p_sys_setresuid, 3);
+ rb_define_module_function(rb_mProcID_Syscall, "setresgid",
+ p_sys_setresgid, 3);
+ rb_define_module_function(rb_mProcID_Syscall, "issetugid",
+ p_sys_issetugid, 0);
}
diff --git a/random.c b/random.c
index 4f1f5fabe8..86c8198d41 100644
--- a/random.c
+++ b/random.c
@@ -6,7 +6,7 @@
$Date$
created at: Fri Dec 24 16:39:21 JST 1993
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
**********************************************************************/
@@ -214,6 +214,8 @@ rb_f_rand(argc, argv, obj)
}
vmax = rb_dbl2big(RFLOAT(vmax)->value);
/* fall through */
+ case T_BIGNUM:
+ bignum:
{
long len = RBIGNUM(vmax)->len;
double *buf = ALLOCA_N(double, len);
@@ -227,6 +229,9 @@ rb_f_rand(argc, argv, obj)
max = 0;
break;
default:
+ vmax = rb_Integer(vmax);
+ if (TYPE(vmax) == T_BIGNUM) goto bignum;
+ case T_FIXNUM:
max = NUM2LONG(vmax);
break;
}
diff --git a/range.c b/range.c
index a004f408ec..a7f553be57 100644
--- a/range.c
+++ b/range.c
@@ -6,7 +6,7 @@
$Date$
created at: Thu Aug 19 17:46:47 JST 1993
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
**********************************************************************/
@@ -19,19 +19,21 @@ static ID id_cmp, id_succ, id_beg, id_end, id_excl;
#define SET_EXCL(r,v) rb_ivar_set((r), id_excl, (v) ? Qtrue : Qfalse)
static VALUE
-range_check(args)
- VALUE *args;
+range_failed()
{
- rb_funcall(args[0], id_cmp, 1, args[1]);
- /* rb_funcall(args[0], id_succ, 0, 0); */
- return Qnil;
+ rb_raise(rb_eArgError, "bad value for range");
+ return Qnil; /* dummy */
}
static VALUE
-range_failed()
+range_check(args)
+ VALUE *args;
{
- rb_raise(rb_eArgError, "bad value for range");
- return Qnil; /* dummy */
+ VALUE v;
+
+ v = rb_funcall(args[0], id_cmp, 1, args[1]);
+ if (NIL_P(v)) range_failed();
+ return Qnil;
}
static void
@@ -107,23 +109,13 @@ range_eq(range, obj)
}
static int
-r_eq(a, b)
- VALUE a, b;
-{
- if (a == b) return Qtrue;
-
- if (rb_funcall(a, id_cmp, 1, b) == INT2FIX(0))
- return Qtrue;
- return Qfalse;
-}
-
-static int
r_lt(a, b)
VALUE a, b;
{
VALUE r = rb_funcall(a, id_cmp, 1, b);
- if (rb_cmpint(r) < 0) return Qtrue;
+ if (NIL_P(r)) return Qfalse;
+ if (rb_cmpint(r, a, b) < 0) return Qtrue;
return Qfalse;
}
@@ -133,19 +125,11 @@ r_le(a, b)
{
VALUE r = rb_funcall(a, id_cmp, 1, b);
- if (rb_cmpint(r) <= 0) return Qtrue;
+ if (NIL_P(r)) return Qfalse;
+ if (rb_cmpint(r, a, b) <= 0) return Qtrue;
return Qfalse;
}
-static int
-r_gt(a,b)
- VALUE a, b;
-{
- VALUE r = rb_funcall(a, id_cmp, 1, b);
-
- if (rb_cmpint(r) > 0) return Qtrue;
- return Qfalse;
-}
static VALUE
range_eql(range, obj)
@@ -204,7 +188,7 @@ step_i(i, iter)
static void
range_each_func(range, func, v, e, arg)
VALUE range;
- void (*func) _((VALUE, void*));
+ VALUE (*func) _((VALUE, void*));
VALUE v, e;
void *arg;
{
@@ -238,55 +222,64 @@ range_step(argc, argv, range)
}
unit = NUM2LONG(step);
- if (unit <= 0) {
- rb_raise(rb_eArgError, "step can't be <= 0");
- }
+ if (unit < 0) {
+ rb_raise(rb_eArgError, "step can't be negative");
+ }
if (FIXNUM_P(b) && FIXNUM_P(e)) { /* fixnums are special */
long end = FIX2LONG(e);
long i;
+ if (unit == 0) rb_raise(rb_eArgError, "step can't be 0");
if (!EXCL(range)) end += 1;
for (i=FIX2LONG(b); i<end; i+=unit) {
rb_yield(LONG2NUM(i));
}
}
- else if (rb_obj_is_kind_of(b, rb_cNumeric)) {
- ID c = rb_intern(EXCL(range) ? "<" : "<=");
+ else {
+ VALUE tmp = rb_check_string_type(b);
- while (RTEST(rb_funcall(b, c, 1, e))) {
- rb_yield(b);
- b = rb_funcall(b, '+', 1, step);
- }
- }
- else if (TYPE(b) == T_STRING) {
- VALUE args[5];
- long iter[2];
+ if (!NIL_P(tmp)) {
+ VALUE args[5];
+ long iter[2];
- args[0] = b; args[1] = e; args[2] = range;
- iter[0] = 1; iter[1] = unit;
- rb_iterate((VALUE(*)_((VALUE)))str_step, (VALUE)args, step_i, (VALUE)iter);
- }
- else {
- long args[2];
+ b = tmp;
+ if (unit == 0) rb_raise(rb_eArgError, "step can't be 0");
+ args[0] = b; args[1] = e; args[2] = range;
+ iter[0] = 1; iter[1] = unit;
+ rb_iterate((VALUE(*)_((VALUE)))str_step, (VALUE)args, step_i, (VALUE)iter);
+ }
+ else if (rb_obj_is_kind_of(b, rb_cNumeric)) {
+ ID c = rb_intern(EXCL(range) ? "<" : "<=");
- if (!rb_respond_to(b, id_succ)) {
- rb_raise(rb_eTypeError, "cannot iterate from %s",
- rb_class2name(CLASS_OF(b)));
+ if (rb_equal(step, INT2FIX(0))) rb_raise(rb_eArgError, "step can't be 0");
+ while (RTEST(rb_funcall(b, c, 1, e))) {
+ rb_yield(b);
+ b = rb_funcall(b, '+', 1, step);
+ }
}
+ else {
+ long args[2];
+
+ if (unit == 0) rb_raise(rb_eArgError, "step can't be 0");
+ if (!rb_respond_to(b, id_succ)) {
+ rb_raise(rb_eTypeError, "cannot iterate from %s",
+ rb_obj_classname(b));
+ }
- args[0] = 1;
- args[1] = unit;
- range_each_func(range, step_i, b, e, args);
+ args[0] = 1;
+ args[1] = unit;
+ range_each_func(range, step_i, b, e, args);
+ }
}
return range;
}
-static void
+static VALUE
each_i(v, arg)
VALUE v;
void *arg;
{
- rb_yield(v);
+ return rb_yield(v);
}
static VALUE
@@ -300,9 +293,18 @@ range_each(range)
if (!rb_respond_to(beg, id_succ)) {
rb_raise(rb_eTypeError, "cannot iterate from %s",
- rb_class2name(CLASS_OF(beg)));
+ rb_obj_classname(beg));
+ }
+ if (FIXNUM_P(beg) && FIXNUM_P(end)) { /* fixnums are special */
+ long lim = FIX2LONG(end);
+ long i;
+
+ if (!EXCL(range)) lim += 1;
+ for (i=FIX2LONG(beg); i<lim; i++) {
+ rb_yield(LONG2NUM(i));
+ }
}
- if (TYPE(beg) == T_STRING) {
+ else if (TYPE(beg) == T_STRING) {
VALUE args[5];
long iter[2];
@@ -311,7 +313,7 @@ range_each(range)
rb_iterate((VALUE(*)_((VALUE)))str_step, (VALUE)args, step_i, (VALUE)iter);
}
else {
- range_each_func(range, each_i, beg, end, 0);
+ range_each_func(range, each_i, beg, end, NULL);
}
return range;
}
@@ -350,27 +352,17 @@ rb_range_beg_len(range, begp, lenp, len, err)
}
if (err == 0 || err == 2) {
if (beg > len) goto out_of_range;
- if (end > len || (!EXCL(range) && end == len))
+ if (end > len)
end = len;
}
- if (end < 0) {
- end += len;
- if (end < 0) {
- if (beg == 0 && end == -1 && !EXCL(range)) {
- len = 0;
- goto length_set;
- }
- goto out_of_range;
- }
- }
+ if (end < 0) end += len;
+ if (!EXCL(range)) end++; /* include end point */
+ if (end < 0) goto out_of_range;
len = end - beg;
- if (!EXCL(range)) len++; /* include end point */
if (len < 0) goto out_of_range;
- length_set:
*begp = beg;
*lenp = len;
-
return Qtrue;
out_of_range:
@@ -382,29 +374,6 @@ rb_range_beg_len(range, begp, lenp, len, err)
}
static VALUE
-range_min(range)
- VALUE range;
-
-{
- VALUE b = rb_ivar_get(range, id_beg);
- VALUE e = rb_ivar_get(range, id_end);
-
- if (r_le(b, e)) return b;
- return e;
-}
-
-static VALUE
-range_max(range)
- VALUE range;
-{
- VALUE b = rb_ivar_get(range, id_beg);
- VALUE e = rb_ivar_get(range, id_end);
-
- if (r_gt(b, e)) return b;
- return e;
-}
-
-static VALUE
range_to_s(range)
VALUE range;
{
@@ -458,7 +427,7 @@ range_member(range, val)
if (!rb_respond_to(beg, id_succ)) {
rb_raise(rb_eTypeError, "cannot iterate from %s",
- rb_class2name(CLASS_OF(beg)));
+ rb_obj_classname(beg));
}
args[0] = val;
args[1] = Qfalse;
@@ -474,12 +443,13 @@ range_include(range, val)
beg = rb_ivar_get(range, id_beg);
end = rb_ivar_get(range, id_end);
- if (r_gt(beg, val)) return Qfalse;
- if (EXCL(range)) {
- if (r_lt(val, end)) return Qtrue;
- }
- else {
- if (r_le(val, end)) return Qtrue;
+ if (r_le(beg, val)) {
+ if (EXCL(range)) {
+ if (r_lt(val, end)) return Qtrue;
+ }
+ else {
+ if (r_le(val, end)) return Qtrue;
+ }
}
return Qfalse;
}
@@ -500,11 +470,8 @@ Init_Range()
rb_define_method(rb_cRange, "last", range_last, 0);
rb_define_method(rb_cRange, "begin", range_first, 0);
rb_define_method(rb_cRange, "end", range_last, 0);
- rb_define_method(rb_cRange, "min", range_min, 0);
- rb_define_method(rb_cRange, "max", range_max, 0);
rb_define_method(rb_cRange, "to_s", range_to_s, 0);
rb_define_method(rb_cRange, "inspect", range_inspect, 0);
- rb_define_alias(rb_cRange, "to_ary", "to_a");
rb_define_method(rb_cRange, "exclude_end?", range_exclude_end_p, 0);
diff --git a/re.c b/re.c
index 8d482736d4..e81b093a3e 100644
--- a/re.c
+++ b/re.c
@@ -5,7 +5,7 @@
$Author$
created at: Mon Aug 9 18:24:49 JST 1993
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
**********************************************************************/
@@ -96,6 +96,60 @@ rb_memcmp(p1, p2, len)
return rb_memcicmp(p1, p2, len);
}
+long
+rb_memsearch(x0, m, y0, n)
+ char *x0, *y0;
+ long m, n;
+{
+ unsigned char *x = x0, *y = y0;
+ unsigned char *s, *e;
+ long i;
+ int d;
+ unsigned long hx, hy;
+
+#define KR_REHASH(a, b, h) (((h) << 1) - ((a)<<d) + (b))
+
+ s = y; e = s + n - m + 1;
+
+ /* Preprocessing */
+ /* computes d = 2^(m-1) with
+ the left-shift operator */
+ d = sizeof(hx) * CHAR_BIT - 1;
+ if (d > m) d = m;
+
+ if (ruby_ignorecase) {
+ /* Prepare hash value */
+ for (hy = hx = i = 0; i < d; ++i) {
+ hx = KR_REHASH(0, casetable[x[i]], hx);
+ hy = KR_REHASH(0, casetable[s[i]], hy);
+ }
+ /* Searching */
+ while (s < e) {
+ if (hx == hy && rb_memcicmp(x, s, m) == 0) {
+ return s-y;
+ }
+ hy = KR_REHASH(casetable[*s], casetable[*(s+d)], hy);
+ s++;
+ }
+ }
+ else {
+ /* Prepare hash value */
+ for (hy = hx = i = 0; i < d; ++i) {
+ hx = KR_REHASH(0, x[i], hx);
+ hy = KR_REHASH(0, s[i], hy);
+ }
+ /* Searching */
+ while (s < e) {
+ if (hx == hy && memcmp(x, s, m) == 0) {
+ return s-y;
+ }
+ hy = KR_REHASH(*s, *(s+d), hy);
+ s++;
+ }
+ }
+ return -1;
+}
+
#define REG_CASESTATE FL_USER0
#define KCODE_NONE 0
#define KCODE_EUC FL_USER1
@@ -448,8 +502,8 @@ static VALUE
rb_reg_options_m(re)
VALUE re;
{
- rb_reg_check(re);
- return INT2NUM(RREGEXP(re)->ptr->options);
+ int options = rb_reg_options(re);
+ return INT2NUM(options);
}
static VALUE
@@ -469,7 +523,7 @@ rb_reg_kcode_m(re)
case KCODE_UTF8:
kcode = "utf8"; break;
default:
- rb_bug("unknow kcode - should not happen");
+ rb_bug("unknown kcode - should not happen");
break;
}
return rb_str_new2(kcode);
@@ -511,6 +565,7 @@ make_regexp(s, len, flags)
static VALUE rb_cMatch;
+static VALUE match_alloc _((VALUE));
static VALUE
match_alloc(klass)
VALUE klass;
@@ -527,7 +582,7 @@ match_alloc(klass)
}
static VALUE
-match_copy_object(obj, orig)
+match_init_copy(obj, orig)
VALUE obj, orig;
{
if (obj == orig) return obj;
@@ -778,8 +833,8 @@ rb_reg_nth_match(nth, match)
if (start == -1) return Qnil;
end = RMATCH(match)->END(nth);
len = end - start;
- str = rb_str_new(RSTRING(RMATCH(match)->str)->ptr + start, len);
- if (OBJ_TAINTED(match)) OBJ_TAINT(str);
+ str = rb_str_substr(RMATCH(match)->str, start, len);
+ OBJ_INFECT(str, match);
return str;
}
@@ -798,7 +853,7 @@ rb_reg_match_pre(match)
if (NIL_P(match)) return Qnil;
if (RMATCH(match)->BEG(0) == -1) return Qnil;
- str = rb_str_new(RSTRING(RMATCH(match)->str)->ptr, RMATCH(match)->BEG(0));
+ str = rb_str_substr(RMATCH(match)->str, 0, RMATCH(match)->BEG(0));
if (OBJ_TAINTED(match)) OBJ_TAINT(str);
return str;
}
@@ -808,11 +863,13 @@ rb_reg_match_post(match)
VALUE match;
{
VALUE str;
+ long pos;
if (NIL_P(match)) return Qnil;
if (RMATCH(match)->BEG(0) == -1) return Qnil;
- str = rb_str_new(RSTRING(RMATCH(match)->str)->ptr+RMATCH(match)->END(0),
- RSTRING(RMATCH(match)->str)->len-RMATCH(match)->END(0));
+ str = RMATCH(match)->str;
+ pos = RMATCH(match)->END(0);
+ str = rb_str_substr(str, pos, RSTRING(str)->len - pos);
if (OBJ_TAINTED(match)) OBJ_TAINT(str);
return str;
}
@@ -857,21 +914,22 @@ last_paren_match_getter()
}
static VALUE
-match_to_a(match)
+match_array(match, start)
VALUE match;
+ int start;
{
struct re_registers *regs = RMATCH(match)->regs;
VALUE ary = rb_ary_new2(regs->num_regs);
- char *ptr = RSTRING(RMATCH(match)->str)->ptr;
+ VALUE target = RMATCH(match)->str;
int i;
int taint = OBJ_TAINTED(match);
- for (i=0; i<regs->num_regs; i++) {
+ for (i=start; i<regs->num_regs; i++) {
if (regs->beg[i] == -1) {
rb_ary_push(ary, Qnil);
}
else {
- VALUE str = rb_str_new(ptr+regs->beg[i], regs->end[i]-regs->beg[i]);
+ VALUE str = rb_str_substr(target, regs->beg[i], regs->end[i]-regs->beg[i]);
if (taint) OBJ_TAINT(str);
rb_ary_push(ary, str);
}
@@ -880,6 +938,20 @@ match_to_a(match)
}
static VALUE
+match_to_a(match)
+ VALUE match;
+{
+ return match_array(match, 0);
+}
+
+static VALUE
+match_captures(match)
+ VALUE match;
+{
+ return match_array(match, 1);
+}
+
+static VALUE
match_aref(argc, argv, match)
int argc;
VALUE *argv;
@@ -895,32 +967,53 @@ match_aref(argc, argv, match)
return rb_reg_nth_match(FIX2INT(idx), match);
}
+static VALUE match_entry _((VALUE, long));
static VALUE
-match_select(argc, argv, match)
+match_entry(match, n)
+ VALUE match;
+ long n;
+{
+ return rb_reg_nth_match(n, match);
+}
+
+static VALUE
+match_values_at(argc, argv, match)
int argc;
VALUE *argv;
VALUE match;
{
- struct re_registers *regs = RMATCH(match)->regs;
- char *ptr = RSTRING(RMATCH(match)->str)->ptr;
- VALUE result = rb_ary_new();
- int i;
- long idx;
- int taint = OBJ_TAINTED(match);
+ return rb_values_at(match, RMATCH(match)->regs->num_regs, argc, argv, match_entry);
+}
- for (i=0; i<argc; i++) {
- idx = NUM2LONG(argv[i]);
- if (idx < 0) idx += regs->num_regs;
- if (idx < 0 || regs->num_regs <= idx) {
- rb_ary_push(result, Qnil);
- }
- else {
- VALUE str = rb_str_new(ptr+regs->beg[idx], regs->end[idx]-regs->beg[idx]);
+static VALUE
+match_select(argc, argv, match)
+ int argc;
+ VALUE *argv;
+ VALUE match;
+{
+ if (!rb_block_given_p()) {
+ rb_warn("MatchData#select(index..) is deprecated; use MatchData#values_at");
+ return match_values_at(argc, argv, match);
+ }
+ if (argc > 0) {
+ rb_raise(rb_eArgError, "wrong number arguments(%d for 0)", argc);
+ }
+ else {
+ struct re_registers *regs = RMATCH(match)->regs;
+ VALUE target = RMATCH(match)->str;
+ VALUE result = rb_ary_new();
+ int i;
+ int taint = OBJ_TAINTED(match);
+
+ for (i=0; i<regs->num_regs; i++) {
+ VALUE str = rb_str_substr(target, regs->beg[i], regs->end[i]-regs->beg[i]);
if (taint) OBJ_TAINT(str);
- rb_ary_push(result, str);
+ if (RTEST(rb_yield(str))) {
+ rb_ary_push(result, str);
+ }
}
+ return result;
}
- return result;
}
static VALUE
@@ -1000,6 +1093,7 @@ rb_reg_initialize(obj, s, len, options)
}
}
+static VALUE rb_reg_s_alloc _((VALUE));
static VALUE
rb_reg_s_alloc(klass)
VALUE klass;
@@ -1110,6 +1204,27 @@ rb_reg_match(re, str)
}
VALUE
+rb_reg_eqq(re, str)
+ VALUE re, str;
+{
+ long start;
+
+ if (TYPE(str) != T_STRING) {
+ str = rb_check_string_type(str);
+ if (NIL_P(str)) {
+ rb_backref_set(Qnil);
+ return Qfalse;
+ }
+ }
+ StringValue(str);
+ start = rb_reg_search(re, str, 0, 0);
+ if (start < 0) {
+ return Qfalse;
+ }
+ return Qtrue;
+}
+
+VALUE
rb_reg_match2(re)
VALUE re;
{
@@ -1146,47 +1261,71 @@ rb_reg_initialize_m(argc, argv, self)
VALUE *argv;
VALUE self;
{
- VALUE src;
+ const char *s;
+ long len;
int flags = 0;
+ rb_check_frozen(self);
if (argc == 0 || argc > 3) {
rb_raise(rb_eArgError, "wrong number of argument");
}
- if (argc >= 2) {
- if (FIXNUM_P(argv[1])) flags = FIX2INT(argv[1]);
- else if (RTEST(argv[1])) flags = RE_OPTION_IGNORECASE;
- }
- if (argc == 3) {
- char *kcode = StringValuePtr(argv[2]);
-
- switch (kcode[0]) {
- case 'n': case 'N':
- flags |= 16;
- break;
- case 'e': case 'E':
- flags |= 32;
- break;
- case 's': case 'S':
- flags |= 48;
- break;
- case 'u': case 'U':
- flags |= 64;
- break;
- default:
- break;
+ if (TYPE(argv[0]) == T_REGEXP) {
+ if (argc > 1) {
+ rb_warn("flags%s ignored", (argc == 3) ? " and encoding": "");
}
- }
-
- rb_check_frozen(self);
- src = argv[0];
- if (TYPE(src) == T_REGEXP) {
- rb_reg_check(src);
- rb_reg_initialize(self, RREGEXP(src)->str, RREGEXP(src)->len, flags);
+ rb_reg_check(argv[0]);
+ flags = RREGEXP(argv[0])->ptr->options & 0xf;
+ if (FL_TEST(argv[0], KCODE_FIXED)) {
+ switch (RBASIC(argv[0])->flags & KCODE_MASK) {
+ case KCODE_NONE:
+ flags |= 16;
+ break;
+ case KCODE_EUC:
+ flags |= 32;
+ break;
+ case KCODE_SJIS:
+ flags |= 48;
+ break;
+ case KCODE_UTF8:
+ flags |= 64;
+ break;
+ default:
+ break;
+ }
+ }
+ s = RREGEXP(argv[0])->str;
+ len = RREGEXP(argv[0])->len;
}
else {
- StringValue(src);
- rb_reg_initialize(self, RSTRING(src)->ptr, RSTRING(src)->len, flags);
+ s = StringValuePtr(argv[0]);
+ len = RSTRING(argv[0])->len;
+ if (argc >= 2) {
+ if (FIXNUM_P(argv[1])) flags = FIX2INT(argv[1]);
+ else if (RTEST(argv[1])) flags = RE_OPTION_IGNORECASE;
+ }
+ if (argc == 3 && !NIL_P(argv[2])) {
+ char *kcode = StringValuePtr(argv[2]);
+
+ flags &= ~0x70;
+ switch (kcode[0]) {
+ case 'n': case 'N':
+ flags |= 16;
+ break;
+ case 'e': case 'E':
+ flags |= 32;
+ break;
+ case 's': case 'S':
+ flags |= 48;
+ break;
+ case 'u': case 'U':
+ flags |= 64;
+ break;
+ default:
+ break;
+ }
+ }
}
+ rb_reg_initialize(self, s, len, flags);
return self;
}
@@ -1216,6 +1355,7 @@ rb_reg_quote(str)
case '*': case '.': case '\\':
case '?': case '+': case '^': case '$':
case ' ': case '#':
+ case '\t': case '\f': case '\n': case '\r':
goto meta_found;
}
}
@@ -1243,9 +1383,29 @@ rb_reg_quote(str)
case '(': case ')': case '|': case '-':
case '*': case '.': case '\\':
case '?': case '+': case '^': case '$':
- case ' ': case '#':
+ case '#':
*t++ = '\\';
break;
+ case ' ':
+ *t++ = '\\';
+ *t++ = ' ';
+ continue;
+ case '\t':
+ *t++ = '\\';
+ *t++ = 't';
+ continue;
+ case '\n':
+ *t++ = '\\';
+ *t++ = 'n';
+ continue;
+ case '\r':
+ *t++ = '\\';
+ *t++ = 'r';
+ continue;
+ case '\f':
+ *t++ = '\\';
+ *t++ = 'f';
+ continue;
}
*t++ = c;
}
@@ -1312,15 +1472,11 @@ int
rb_reg_options(re)
VALUE re;
{
- int options = 0;
+ int options;
rb_reg_check(re);
- if (RREGEXP(re)->ptr->options & RE_OPTION_IGNORECASE)
- options |= RE_OPTION_IGNORECASE;
- if (RREGEXP(re)->ptr->options & RE_OPTION_MULTILINE)
- options |= RE_OPTION_MULTILINE;
- if (RREGEXP(re)->ptr->options & RE_OPTION_EXTENDED)
- options |= RE_OPTION_EXTENDED;
+ options = RREGEXP(re)->ptr->options &
+ (RE_OPTION_IGNORECASE|RE_OPTION_MULTILINE|RE_OPTION_EXTENDED);
if (FL_TEST(re, KCODE_FIXED)) {
options |= rb_reg_get_kcode(re);
}
@@ -1328,7 +1484,7 @@ rb_reg_options(re)
}
static VALUE
-rb_reg_copy_object(copy, re)
+rb_reg_init_copy(copy, re)
VALUE copy, re;
{
if (copy == re) return copy;
@@ -1571,19 +1727,19 @@ Init_Regexp()
rb_define_virtual_variable("$-K", kcode_getter, kcode_setter);
rb_cRegexp = rb_define_class("Regexp", rb_cObject);
- rb_define_singleton_method(rb_cRegexp, "allocate", rb_reg_s_alloc, 0);
+ rb_define_alloc_func(rb_cRegexp, rb_reg_s_alloc);
rb_define_singleton_method(rb_cRegexp, "compile", rb_class_new_instance, -1);
rb_define_singleton_method(rb_cRegexp, "quote", rb_reg_s_quote, -1);
rb_define_singleton_method(rb_cRegexp, "escape", rb_reg_s_quote, -1);
rb_define_singleton_method(rb_cRegexp, "last_match", rb_reg_s_last_match, -1);
rb_define_method(rb_cRegexp, "initialize", rb_reg_initialize_m, -1);
- rb_define_method(rb_cRegexp, "copy_object", rb_reg_copy_object, 1);
+ rb_define_method(rb_cRegexp, "initialize_copy", rb_reg_init_copy, 1);
rb_define_method(rb_cRegexp, "hash", rb_reg_hash, 0);
rb_define_method(rb_cRegexp, "eql?", rb_reg_equal, 1);
rb_define_method(rb_cRegexp, "==", rb_reg_equal, 1);
rb_define_method(rb_cRegexp, "=~", rb_reg_match, 1);
- rb_define_method(rb_cRegexp, "===", rb_reg_match, 1);
+ rb_define_method(rb_cRegexp, "===", rb_reg_eqq, 1);
rb_define_method(rb_cRegexp, "~", rb_reg_match2, 0);
rb_define_method(rb_cRegexp, "match", rb_reg_match_m, 1);
rb_define_method(rb_cRegexp, "to_s", rb_reg_to_s, 0);
@@ -1601,19 +1757,20 @@ Init_Regexp()
rb_cMatch = rb_define_class("MatchData", rb_cObject);
rb_define_global_const("MatchingData", rb_cMatch);
- rb_define_singleton_method(rb_cMatch, "allocate", match_alloc, 0);
+ rb_define_alloc_func(rb_cMatch, match_alloc);
rb_undef_method(CLASS_OF(rb_cMatch), "new");
- rb_define_method(rb_cMatch, "copy_object", match_copy_object, 1);
+ rb_define_method(rb_cMatch, "initialize_copy", match_init_copy, 1);
rb_define_method(rb_cMatch, "size", match_size, 0);
rb_define_method(rb_cMatch, "length", match_size, 0);
rb_define_method(rb_cMatch, "offset", match_offset, 1);
rb_define_method(rb_cMatch, "begin", match_begin, 1);
rb_define_method(rb_cMatch, "end", match_end, 1);
rb_define_method(rb_cMatch, "to_a", match_to_a, 0);
- rb_define_method(rb_cMatch, "to_ary", match_to_a, 0);
rb_define_method(rb_cMatch, "[]", match_aref, -1);
+ rb_define_method(rb_cMatch, "captures", match_captures, 0);
rb_define_method(rb_cMatch, "select", match_select, -1);
+ rb_define_method(rb_cMatch, "values_at", match_values_at, -1);
rb_define_method(rb_cMatch, "pre_match", rb_reg_match_pre, 0);
rb_define_method(rb_cMatch, "post_match", rb_reg_match_post, 0);
rb_define_method(rb_cMatch, "to_s", match_to_s, 0);
diff --git a/re.h b/re.h
index d980e2baa8..45b2753dd5 100644
--- a/re.h
+++ b/re.h
@@ -6,7 +6,7 @@
$Date$
created at: Thu Sep 30 14:18:32 JST 1993
- Copyright (C) 1993-2002 Yukihiro Matsumoto
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
**********************************************************************/
@@ -35,7 +35,7 @@ long rb_reg_adjust_startpos _((VALUE, VALUE, long, long));
void rb_match_busy _((VALUE));
VALUE rb_reg_quote _((VALUE));
-EXTERN int ruby_ignorecase;
+RUBY_EXTERN int ruby_ignorecase;
int rb_reg_mbclen2 _((unsigned int, VALUE));
#define mbclen2(c,re) rb_reg_mbclen2((c),(re))
diff --git a/regex.c b/regex.c
index 28fefcb228..e9f382f0a4 100644
--- a/regex.c
+++ b/regex.c
@@ -185,6 +185,12 @@ static int current_mbctype = MBCTYPE_ASCII;
#ifdef RUBY
#include "util.h"
+void rb_warn _((char*));
+# define re_warning(x) rb_warn(x)
+#endif
+
+#ifndef re_warning
+# define re_warning(x)
#endif
static void
@@ -698,7 +704,18 @@ set_list_bits(c1, c2, b)
}
static int
-is_in_list(c, b)
+is_in_list_sbc(c, b)
+ unsigned long c;
+ const unsigned char *b;
+{
+ unsigned short size;
+
+ size = *b++;
+ return ((int)c / BYTEWIDTH < (int)size && b[c / BYTEWIDTH] & 1 << c % BYTEWIDTH);
+}
+
+static int
+is_in_list_mbc(c, b)
unsigned long c;
const unsigned char *b;
{
@@ -706,9 +723,6 @@ is_in_list(c, b)
unsigned short i, j;
size = *b++;
- if ((int)c / BYTEWIDTH < (int)size && b[c / BYTEWIDTH] & 1 << c % BYTEWIDTH) {
- return 1;
- }
b += size + 2;
size = EXTRACT_UNSIGNED(&b[-2]);
if (size == 0) return 0;
@@ -727,6 +741,14 @@ is_in_list(c, b)
return 0;
}
+static int
+is_in_list(c, b)
+ unsigned long c;
+ const unsigned char *b;
+{
+ return is_in_list_sbc(c, b) || (current_mbctype ? is_in_list_mbc(c, b) : 0);
+}
+
static void
print_partial_compiled_pattern(start, end)
unsigned char *start;
@@ -1016,6 +1038,7 @@ calculate_must_string(start, end)
break;
case duplicate:
+ case option_set:
p++;
break;
@@ -1041,7 +1064,6 @@ calculate_must_string(start, end)
case push_dummy_failure:
case start_paren:
case stop_paren:
- case option_set:
break;
case charset:
@@ -1431,8 +1453,7 @@ re_compile_pattern(pattern, size, bufp)
int size;
unsigned last = (unsigned)-1;
- if ((size = EXTRACT_UNSIGNED(&b[(1 << BYTEWIDTH) / BYTEWIDTH]))
- || current_mbctype) {
+ if ((size = EXTRACT_UNSIGNED(&b[(1 << BYTEWIDTH) / BYTEWIDTH])) || current_mbctype) {
/* Ensure the space is enough to hold another interval
of multi-byte chars in charset(_not)?. */
size = (1 << BYTEWIDTH) / BYTEWIDTH + 2 + size*8 + 8;
@@ -1449,6 +1470,7 @@ re_compile_pattern(pattern, size, bufp)
if (p == p0 + 1) {
if (p == pend)
FREE_AND_RETURN(stackb, "invalid regular expression; empty character class");
+ re_warning("character class has `]' without escape");
}
else
/* Stop if this isn't merely a ] inside a bracket
@@ -1466,6 +1488,13 @@ re_compile_pattern(pattern, size, bufp)
}
had_char_class = 0;
+ if (c == '-' && ((p != p0 + 1 && *p != ']') ||
+ (p[0] == '-' && p[1] != ']') ||
+ range))
+ re_warning("character class has `-' without escape");
+ if (c == '[' && *p != ':')
+ re_warning("character class has `[' without escape");
+
/* \ escapes characters when inside [...]. */
if (c == '\\') {
PATFETCH_RAW(c);
@@ -1567,32 +1596,7 @@ re_compile_pattern(pattern, size, bufp)
break;
}
}
-
- /* Get a range. */
- if (range) {
- if (last > c)
- goto invalid_pattern;
-
- range = 0;
- if (had_mbchar == 0) {
- for (;last<=c;last++)
- SET_LIST_BIT(last);
- }
- else if (had_mbchar == 2) {
- set_list_bits(last, c, b);
- }
- else {
- /* restriction: range between sbc and mbc */
- goto invalid_pattern;
- }
- }
- else if (p[0] == '-' && p[1] != ']') {
- last = c;
- PATFETCH(c1);
- range = 1;
- goto range_retry;
- }
- else if (c == '[' && *p == ':') {
+ else if (c == '[' && *p == ':') { /* [:...:] */
/* Leave room for the null. */
char str[CHAR_CLASS_MAX_LENGTH + 1];
@@ -1612,9 +1616,9 @@ re_compile_pattern(pattern, size, bufp)
}
str[c1] = '\0';
- /* If isn't a word bracketed by `[:' and:`]':
- undo the ending character, the letters, and leave
- the leading `:' and `[' (but set bits for them). */
+ /* If isn't a word bracketed by `[:' and `:]':
+ undo the ending character, the letters, and
+ the leading `:' and `['. */
if (c == ':' && *p == ']') {
int ch;
char is_alnum = STREQ(str, "alnum");
@@ -1658,22 +1662,46 @@ re_compile_pattern(pattern, size, bufp)
SET_LIST_BIT(ch);
}
had_char_class = 1;
+ continue;
}
else {
- c1++;
+ c1 += 2;
while (c1--)
PATUNFETCH;
- SET_LIST_BIT(TRANSLATE_P()?translate['[']:'[');
- SET_LIST_BIT(TRANSLATE_P()?translate[':']:':');
- had_char_class = 0;
- last = ':';
+ re_warning("character class has `[' without escape");
+ c = '[';
}
}
+
+ /* Get a range. */
+ if (range) {
+ if (last > c)
+ goto invalid_pattern;
+
+ range = 0;
+ if (had_mbchar == 0) {
+ for (;last<=c;last++)
+ SET_LIST_BIT(last);
+ }
+ else if (had_mbchar == 2) {
+ set_list_bits(last, c, b);
+ }
+ else {
+ /* restriction: range between sbc and mbc */
+ goto invalid_pattern;
+ }
+ }
+ else if (p[0] == '-' && p[1] != ']') {
+ last = c;
+ PATFETCH(c1);
+ range = 1;
+ goto range_retry;
+ }
else if (had_mbchar == 0 && (!current_mbctype || !had_num_literal)) {
SET_LIST_BIT(c);
- had_num_literal = 0;
+ had_num_literal = 0;
}
- else
+ else
set_list_bits(c, c, b);
had_mbchar = 0;
}
@@ -2323,6 +2351,8 @@ re_compile_pattern(pattern, size, bufp)
break;
default:
+ if (c == ']')
+ re_warning("regexp has `]' without escape");
normal_char: /* Expects the character in `c'. */
had_mbchar = 0;
if (ismbchar(c)) {
@@ -2368,7 +2398,6 @@ re_compile_pattern(pattern, size, bufp)
/* set optimize flags */
laststart = bufp->buffer;
if (laststart != b) {
- if (*laststart == start_memory) laststart += 3;
if (*laststart == dummy_failure_jump) laststart += 3;
else if (*laststart == try_next) laststart += 3;
if (*laststart == anychar_repeat) {
@@ -2787,8 +2816,11 @@ re_compile_fastmap(bufp)
case casefold_on:
bufp->options |= RE_MAY_IGNORECASE;
+ options |= RE_OPTION_IGNORECASE;
+ continue;
+
case casefold_off:
- options ^= RE_OPTION_IGNORECASE;
+ options &= ~RE_OPTION_IGNORECASE;
continue;
case option_set:
@@ -3089,6 +3121,9 @@ re_adjust_startpos(bufp, string, size, startpos, range)
}
+static int re_match_exec _((struct re_pattern_buffer *, const char *, int, int, int,
+ struct re_registers *));
+
/* Using the compiled pattern in BUFP->buffer, first tries to match
STRING, starting first at index STARTPOS, then at STARTPOS + 1, and
so on. RANGE is the number of places to try before giving up. If
@@ -3109,7 +3144,7 @@ re_search(bufp, string, size, startpos, range, regs)
struct re_registers *regs;
{
register char *fastmap = bufp->fastmap;
- int val, anchor = 0;
+ int val, anchor = 0, initpos = startpos;
/* Check for out-of-range starting position. */
if (startpos < 0 || startpos > size)
@@ -3238,7 +3273,7 @@ re_search(bufp, string, size, startpos, range, regs)
if (startpos > size) return -1;
if ((anchor || !bufp->