diff options
-rw-r--r-- | ChangeLog | 4305 | ||||
-rw-r--r-- | MANIFEST | 186 | ||||
-rw-r--r-- | Makefile.in | 219 | ||||
-rw-r--r-- | README | 108 | ||||
-rw-r--r-- | README.EXT | 1064 | ||||
-rw-r--r-- | README.jp | 150 | ||||
-rw-r--r-- | ToDo | 4 | ||||
-rw-r--r-- | array.c | 1242 | ||||
-rw-r--r-- | bignum.c | 1249 | ||||
-rw-r--r-- | class.c | 427 | ||||
-rw-r--r-- | compar.c | 100 | ||||
-rw-r--r-- | config.dj | 36 | ||||
-rw-r--r-- | config.guess | 613 | ||||
-rw-r--r-- | config.sub | 931 | ||||
-rw-r--r-- | configure | 4038 | ||||
-rw-r--r-- | configure.bat | 5 | ||||
-rw-r--r-- | configure.in | 474 | ||||
-rw-r--r-- | defines.h | 50 | ||||
-rw-r--r-- | dir.c | 427 | ||||
-rw-r--r-- | dln.c | 1445 | ||||
-rw-r--r-- | dln.h | 22 | ||||
-rw-r--r-- | dmyext.c | 4 | ||||
-rw-r--r-- | enum.c | 369 | ||||
-rw-r--r-- | env.h | 49 | ||||
-rw-r--r-- | error.c | 832 | ||||
-rw-r--r-- | eval.c | 5402 | ||||
-rw-r--r-- | ext/Setup | 12 | ||||
-rw-r--r-- | ext/Setup.dj | 12 | ||||
-rw-r--r-- | ext/Setup.nt | 12 | ||||
-rw-r--r-- | ext/Setup.x68 | 12 | ||||
-rw-r--r-- | ext/aix_ld.rb | 73 | ||||
-rw-r--r-- | ext/curses/MANIFEST | 6 | ||||
-rw-r--r-- | ext/curses/curses.c | 798 | ||||
-rw-r--r-- | ext/curses/extconf.rb | 21 | ||||
-rw-r--r-- | ext/curses/hello.rb | 28 | ||||
-rw-r--r-- | ext/curses/rain.rb | 76 | ||||
-rw-r--r-- | ext/curses/view.rb | 90 | ||||
-rw-r--r-- | ext/dbm/MANIFEST | 4 | ||||
-rw-r--r-- | ext/dbm/dbm.c | 515 | ||||
-rw-r--r-- | ext/dbm/depend | 1 | ||||
-rw-r--r-- | ext/dbm/extconf.rb | 5 | ||||
-rw-r--r-- | ext/etc/MANIFEST | 5 | ||||
-rw-r--r-- | ext/etc/depend | 1 | ||||
-rw-r--r-- | ext/etc/etc.c | 266 | ||||
-rw-r--r-- | ext/etc/etc.doc | 73 | ||||
-rw-r--r-- | ext/etc/extconf.rb | 7 | ||||
-rw-r--r-- | ext/extmk.rb.in | 468 | ||||
-rw-r--r-- | ext/extmk.rb.nt | 480 | ||||
-rw-r--r-- | ext/fcntl/MANIFEST | 3 | ||||
-rw-r--r-- | ext/fcntl/depend | 1 | ||||
-rw-r--r-- | ext/fcntl/fcntl.c | 106 | ||||
-rw-r--r-- | ext/kconv/MANIFEST | 3 | ||||
-rw-r--r-- | ext/kconv/depend | 1 | ||||
-rw-r--r-- | ext/kconv/kconv.c | 1881 | ||||
-rw-r--r-- | ext/marshal/MANIFEST | 5 | ||||
-rw-r--r-- | ext/marshal/depend | 2 | ||||
-rw-r--r-- | ext/marshal/extconf.rb | 1 | ||||
-rw-r--r-- | ext/marshal/marshal.c | 850 | ||||
-rw-r--r-- | ext/marshal/marshal.doc | 48 | ||||
-rw-r--r-- | ext/md5/MANIFEST | 6 | ||||
-rw-r--r-- | ext/md5/depend | 2 | ||||
-rw-r--r-- | ext/md5/md5.doc | 36 | ||||
-rw-r--r-- | ext/md5/md5.h | 86 | ||||
-rw-r--r-- | ext/md5/md5c.c | 337 | ||||
-rw-r--r-- | ext/md5/md5init.c | 90 | ||||
-rw-r--r-- | ext/socket/MANIFEST | 4 | ||||
-rw-r--r-- | ext/socket/depend | 1 | ||||
-rw-r--r-- | ext/socket/extconf.rb | 17 | ||||
-rw-r--r-- | ext/socket/socket.c | 1407 | ||||
-rw-r--r-- | ext/tkutil/MANIFEST | 3 | ||||
-rw-r--r-- | ext/tkutil/depend | 1 | ||||
-rw-r--r-- | ext/tkutil/tkutil.c | 46 | ||||
-rw-r--r-- | file.c | 1756 | ||||
-rw-r--r-- | fnmatch.c | 190 | ||||
-rw-r--r-- | fnmatch.h | 36 | ||||
-rw-r--r-- | gc.c | 989 | ||||
-rw-r--r-- | glob.c | 574 | ||||
-rw-r--r-- | hash.c | 1005 | ||||
-rw-r--r-- | inits.c | 48 | ||||
-rw-r--r-- | install-sh | 238 | ||||
-rw-r--r-- | io.c | 2306 | ||||
-rw-r--r-- | io.h | 53 | ||||
-rw-r--r-- | lib/English.rb | 28 | ||||
-rw-r--r-- | lib/base64.rb | 54 | ||||
-rw-r--r-- | lib/cgi-lib.rb | 56 | ||||
-rw-r--r-- | lib/complex.rb | 490 | ||||
-rw-r--r-- | lib/date.rb | 227 | ||||
-rw-r--r-- | lib/debug.rb | 262 | ||||
-rw-r--r-- | lib/e2mmap.rb | 94 | ||||
-rw-r--r-- | lib/e2mmap1_0.rb | 71 | ||||
-rw-r--r-- | lib/finalize.rb | 205 | ||||
-rw-r--r-- | lib/find.rb | 39 | ||||
-rw-r--r-- | lib/ftplib.rb | 617 | ||||
-rw-r--r-- | lib/getopts.rb | 142 | ||||
-rw-r--r-- | lib/jcode.rb | 207 | ||||
-rw-r--r-- | lib/mailread.rb | 48 | ||||
-rw-r--r-- | lib/mathn.rb | 308 | ||||
-rw-r--r-- | lib/matrix.rb | 777 | ||||
-rw-r--r-- | lib/mutex_m.rb | 183 | ||||
-rw-r--r-- | lib/observer.rb | 40 | ||||
-rw-r--r-- | lib/parsearg.rb | 84 | ||||
-rw-r--r-- | lib/parsedate.rb | 42 | ||||
-rw-r--r-- | lib/ping.rb | 55 | ||||
-rw-r--r-- | lib/rational.rb | 361 | ||||
-rw-r--r-- | lib/sync.rb | 376 | ||||
-rw-r--r-- | lib/thread.rb | 110 | ||||
-rw-r--r-- | lib/thwait.rb | 128 | ||||
-rw-r--r-- | lib/tk.rb | 829 | ||||
-rw-r--r-- | lib/tkcanvas.rb | 326 | ||||
-rw-r--r-- | lib/tkclass.rb | 38 | ||||
-rw-r--r-- | lib/tkcore.rb | 528 | ||||
-rw-r--r-- | lib/tkentry.rb | 67 | ||||
-rw-r--r-- | lib/tkscrollbox.rb | 27 | ||||
-rw-r--r-- | lib/tktext.rb | 164 | ||||
-rw-r--r-- | lib/tkthcore.rb | 550 | ||||
-rw-r--r-- | lib/tracer.rb | 75 | ||||
-rw-r--r-- | main.c | 33 | ||||
-rw-r--r-- | math.c | 135 | ||||
-rw-r--r-- | missing/alloca.c | 189 | ||||
-rw-r--r-- | missing/crypt.c | 276 | ||||
-rw-r--r-- | missing/dir.h | 248 | ||||
-rw-r--r-- | missing/dup2.c | 36 | ||||
-rw-r--r-- | missing/file.h | 32 | ||||
-rw-r--r-- | missing/flock.c | 90 | ||||
-rw-r--r-- | missing/memmove.c | 24 | ||||
-rw-r--r-- | missing/mkdir.c | 104 | ||||
-rw-r--r-- | missing/nt.c | 2191 | ||||
-rw-r--r-- | missing/nt.h | 225 | ||||
-rw-r--r-- | missing/setenv.c | 77 | ||||
-rw-r--r-- | missing/strcasecmp.c | 13 | ||||
-rw-r--r-- | missing/strdup.c | 25 | ||||
-rw-r--r-- | missing/strerror.c | 19 | ||||
-rw-r--r-- | missing/strftime.c | 889 | ||||
-rw-r--r-- | missing/strstr.c | 73 | ||||
-rw-r--r-- | missing/strtol.c | 84 | ||||
-rw-r--r-- | missing/strtoul.c | 184 | ||||
-rw-r--r-- | missing/x68.c | 12 | ||||
-rw-r--r-- | node.h | 290 | ||||
-rw-r--r-- | numeric.c | 1154 | ||||
-rw-r--r-- | object.c | 697 | ||||
-rw-r--r-- | pack.c | 883 | ||||
-rw-r--r-- | parse.y | 3717 | ||||
-rw-r--r-- | process.c | 998 | ||||
-rw-r--r-- | random.c | 100 | ||||
-rw-r--r-- | range.c | 211 | ||||
-rw-r--r-- | re.c | 886 | ||||
-rw-r--r-- | re.h | 34 | ||||
-rw-r--r-- | regex.c | 2807 | ||||
-rw-r--r-- | regex.h | 291 | ||||
-rw-r--r-- | ruby.1 | 329 | ||||
-rw-r--r-- | ruby.c | 670 | ||||
-rw-r--r-- | ruby.h | 380 | ||||
-rw-r--r-- | sample/biorhythm.rb | 138 | ||||
-rw-r--r-- | sample/clnt.rb | 17 | ||||
-rw-r--r-- | sample/dbmtest.rb | 14 | ||||
-rw-r--r-- | sample/dir.rb | 10 | ||||
-rw-r--r-- | sample/eval.rb | 42 | ||||
-rw-r--r-- | sample/export.rb | 40 | ||||
-rw-r--r-- | sample/exyacc.rb | 22 | ||||
-rw-r--r-- | sample/fact.rb | 8 | ||||
-rw-r--r-- | sample/fib.awk | 5 | ||||
-rw-r--r-- | sample/fib.pl | 10 | ||||
-rw-r--r-- | sample/fib.rb | 10 | ||||
-rw-r--r-- | sample/fib.scm | 6 | ||||
-rw-r--r-- | sample/freq.rb | 13 | ||||
-rw-r--r-- | sample/from.rb | 87 | ||||
-rw-r--r-- | sample/fullpath.rb | 23 | ||||
-rw-r--r-- | sample/getopts.test | 36 | ||||
-rw-r--r-- | sample/io.rb | 44 | ||||
-rw-r--r-- | sample/less.rb | 17 | ||||
-rw-r--r-- | sample/list.rb | 80 | ||||
-rw-r--r-- | sample/list2.rb | 16 | ||||
-rw-r--r-- | sample/list3.rb | 18 | ||||
-rw-r--r-- | sample/mkproto.rb | 27 | ||||
-rw-r--r-- | sample/mpart.rb | 44 | ||||
-rw-r--r-- | sample/mrshtest.rb | 14 | ||||
-rw-r--r-- | sample/observ.rb | 31 | ||||
-rw-r--r-- | sample/occur.pl | 9 | ||||
-rw-r--r-- | sample/occur.rb | 12 | ||||
-rw-r--r-- | sample/occur2.rb | 16 | ||||
-rw-r--r-- | sample/philos.rb | 54 | ||||
-rw-r--r-- | sample/pi.rb | 18 | ||||
-rw-r--r-- | sample/rcs.awk | 33 | ||||
-rw-r--r-- | sample/rcs.dat | 17 | ||||
-rw-r--r-- | sample/rcs.rb | 49 | ||||
-rw-r--r-- | sample/regx.rb | 23 | ||||
-rw-r--r-- | sample/ruby-mode.el | 644 | ||||
-rw-r--r-- | sample/rubydb2x.el | 104 | ||||
-rw-r--r-- | sample/rubydb3x.el | 104 | ||||
-rw-r--r-- | sample/sieve.rb | 18 | ||||
-rw-r--r-- | sample/svr.rb | 32 | ||||
-rw-r--r-- | sample/test.rb | 943 | ||||
-rw-r--r-- | sample/time.rb | 8 | ||||
-rw-r--r-- | sample/tkbiff.rb | 149 | ||||
-rw-r--r-- | sample/tkbrowse.rb | 69 | ||||
-rw-r--r-- | sample/tkdialog.rb | 62 | ||||
-rw-r--r-- | sample/tkfrom.rb | 126 | ||||
-rw-r--r-- | sample/tkhello.rb | 13 | ||||
-rw-r--r-- | sample/tkline.rb | 46 | ||||
-rw-r--r-- | sample/tktimer.rb | 50 | ||||
-rw-r--r-- | sample/trojan.rb | 14 | ||||
-rw-r--r-- | sample/tsvr.rb | 23 | ||||
-rw-r--r-- | sample/uumerge.rb | 37 | ||||
-rw-r--r-- | sig.h | 53 | ||||
-rw-r--r-- | signal.c | 534 | ||||
-rw-r--r-- | sprintf.c | 632 | ||||
-rw-r--r-- | st.c | 435 | ||||
-rw-r--r-- | st.h | 52 | ||||
-rw-r--r-- | string.c | 2302 | ||||
-rw-r--r-- | struct.c | 437 | ||||
-rw-r--r-- | time.c | 795 | ||||
-rw-r--r-- | top.sed | 52 | ||||
-rw-r--r-- | util.c | 458 | ||||
-rw-r--r-- | util.h | 18 | ||||
-rw-r--r-- | variable.c | 921 | ||||
-rw-r--r-- | version.c | 36 | ||||
-rw-r--r-- | version.h | 2 | ||||
-rw-r--r-- | win32/Makefile | 233 | ||||
-rw-r--r-- | win32/config.h | 60 | ||||
-rwxr-xr-x | win32/ntsetup.bat | 10 | ||||
-rw-r--r-- | win32/ruby.def | 55 | ||||
-rw-r--r-- | x68/_dtos18.c | 250 | ||||
-rw-r--r-- | x68/_round.c | 45 | ||||
-rw-r--r-- | x68/fconvert.c | 81 | ||||
-rw-r--r-- | x68/select.c | 167 |
225 files changed, 75935 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000000..8c4a1eaa39 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,4305 @@ +Thu Dec 25 17:06:30 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * version 1.0-971225 + + * some minor bug fixes. + +Tue Dec 9 17:54:56 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * version 1.0-971209 + + * sample/ruby-mode.el (ruby-expr-beg): forgot to handle modifiers. + + * parse.y (tokadd): token buffer overrun. + +Thu Dec 4 14:29:59 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * version 1.0-971204 + +Mon Dec 1 15:24:41 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * compar.c (cmp_between): wrong comparison made. + +Wed Nov 26 18:18:05 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * numeric.c (fix2str): enlarge buffer to prevent overflow on some + machines. + +Tue Nov 25 15:03:28 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * version 1.0-971125 + +Fri Nov 21 13:17:12 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * parse.y (yylex): skip multibyte characters in comments. + +Wed Nov 19 17:19:20 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * parse.y (call_args): wrong node generation. + +Tue Nov 18 13:59:59 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * version 1.0-971118 + +Tue Nov 18 10:13:08 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * regex.c (re_compile_pattern): insert initialize code for jump_n, + before entering loops. + +Sat Nov 15 00:11:36 1997 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp> + + * io.c (io_s_popen): "rb" detection + +Wed Nov 12 13:44:47 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * time.c: remove coerce from Time class. + +Wed Nov 2 16:00:00 1997 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp> + + * string.c (str_sub_s): "".sub! "", "" => "\000" + +Thu Oct 30 16:54:01 1997 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp> + + * string.c (str_chop_bang): "".chop caused SEGV. + +Mon Oct 27 13:49:13 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * ext/extmk.rb.in: library may have pathname contains `.' + + * eval.c (rb_rescue): should not protect SystemError. + +Thu Oct 23 11:17:44 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * range.c (range_eqq): fixnum check for last needed too. + +Wed Oct 22 12:52:30 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * array.c (ary_join): call ary_join() recursively for the 1st + array element. + +Tue Oct 21 13:31:29 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * version 1.0-971021 + +Mon Oct 20 12:18:29 1997 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp> + + * ruby.c (load_file): wrong condition for #! check with -x. + + * file.c (file_s_dirname): did return "" for "/a". + +Fri Oct 17 14:29:09 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * bignum.c (bigadd): some undefined side effect order assumed. + +Wed Oct 15 18:08:37 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * version 1.0-971015 + +Fri Oct 3 10:51:10 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * version 1.0-971003 + + * eval.c (ruby_options): f_require() called too early. + + * eval.c (rb_provide): module extentions should always be `.o'. + +Thu Oct 2 17:59:18 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * ext/marshal/marshal.c (r_object): remove temporal regist for + structs. (caused problem if structs form cycles.) + + * version 1.0-971002 + +Wed Oct 1 14:01:49 1997 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp> + + * ext/marshal/marshal.c (w_byte): argument must be char. + +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 + the object table. + + * eval.c (ruby_options): call Init_ext() before any require() + calls by `-r'. + +Tue Sep 30 14:51:07 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * version 1.0-970930 + +Fri Sep 30 14:29:22 1997 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp> + + * ext/marshal/marshal.c (w_object): marshal dumped core. + +Tue Sep 30 10:27:39 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * sample/test.rb: bignum test suits added. + +Mon Sep 29 13:37:58 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * ruby.c (forbid_setid): forbid some options in suid mode. + +Mon Sep 27 09:53:48 1997 EGUCHI Matsumoto <eguchi@shizuokanet.or.jp> + + * bignum.c: modified for speeding. + +Fri Sep 26 18:27:59 1997 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp> + + * sample/from.rb: some extensions. + +Mon Sep 29 13:15:56 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * parse.y (lhs): no more syntax error on `obj.CONSTANT = value'. + +Fri Sep 26 14:41:46 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * eval.c (ruby_run): deferred calling Init_ext() just before eval_node. + +Fri Sep 26 13:27:24 1997 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp> + + * io.c (io_isatty): forgot to return TRUE value. + +Fri Sep 25 11:10:58 1997 EGUCHI Osamu <eguchi@shizuokanet.or.jp> + + * eval.c: use _setjmp/_longjmp instead of setjmp/longjmp on some + platforms. + +Wed Sep 24 17:43:13 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * string.c (Init_String): String#taint and String#taint? added. + +Wed Sep 24 00:57:00 1997 Katsuyuki Okabe <HGC02147@niftyserve.or.jp> + + * X68000 patch. + +Tue Sep 23 20:42:30 1997 EGUCHI Osamu <eguchi@shizuokanet.or.jp> + + * parse.y (node_newnode): SEGV on null node setup. + +Mon Sep 22 11:22:46 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * ruby.c (ruby_prog_init): wrong safe condition check. + +Sun Sep 21 14:46:02 1997 MAEDA shugo <shugo@po.aianet.ne.jp> + + * error.c (exc_inspect): garbage added to classpath. + +Fri Sep 19 11:49:23 1997 <matz@netlab.co.jp> + + * version 1.0-970919 + + * parse.y (newtok): forgot to adjust buffer size when shrinking + the token buffer. + + * enum.c (enum_find): rb_eval_cmd() does not return value. + + * io.c (pipe_open): close fds on pipe exec. fcntl(fd, F_SETFD, 1) + no longer used. + +Tue Sep 16 17:54:25 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * file.c (f_test): problem if wrong command specified. + + * ruby.c (ruby_prog_init): close stdaux and stdprn for MSDOS. + + * ruby.c (ruby_prog_init): should not add path from environment + variable, if ruby is running under seuid. + + * process.c (init_ids): check suid check for setuid/seteuid etc. + +Mon Sep 15 00:42:04 1997 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp> + + * regex.c (re_compile_pattern): \w{3} and \W{3} did not work. + +Thu Sep 11 10:31:48 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * version 1.0-970911 + + * ext/socket/socket.c (sock_new): no setbuf() for NT. + + * io.c (rb_fopen,rb_fdopen): set close-on-exec for every fd. + +Wed Sep 10 15:55:31 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * version 1.0-970910 + + * ext/marshal/marshal.c (r_bytes0): extra big length check. + +Tue Sep 9 16:27:14 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * io.c (pipe_fptr_atexit): clean up popen()'ed fptr. + + * error.c (set_syserr): some system has error code that is bigger + than sys_nerr. grrr. + +Tue Sep 9 16:27:14 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * version 1.0-970909 + + * error.c (set_syserr): some system has error code that is bigger + than sys_nerr. grrr. + +Wed Sep 3 18:11:00 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * version 1.0-970903 + + * eval.c (f_load): expand path if fname begins with `~'. + +Mon Sep 1 13:42:48 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * eval.c (rb_call): alias occured in the module body caused SEGV. + +Fri Aug 29 11:10:21 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * parse.y (yylex): spaces can follow =begin/=end. + + * variable.c (find_class_path): look for class_tbl also for + unnamed fundamental classes, such as Object, String, etc. + + * variable.c (rb_name_class): can't name class before String class + is initilialized. + + * 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. + +Thu Aug 28 13:12:05 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * parse.y (yylex): revised `=begin' skip code. + + * eval.c (is_defined): separated from rb_eval(). + +Wed Aug 27 11:32:42 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * variable.c (fc_i): some classes/modules does not have iv_tbl. + + * variable.c (find_class_path): avoid inifinite loop. + +Tue Aug 26 13:43:47 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * eval.c (rb_eval): undef'ing non-existing method will raise + NameError exception. + + * object.c (class_s_new): needed to create metaclass too. + + * 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. + + * object.c (Init_Object): superclass()'s method argument setting + was wrong again. + +Mon Aug 25 11:53:11 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * sample/ruby-mode.el (ruby-parse-region): auto-indent now + supports "\\" in the strings. + + * struct.c (struct_getmember): new API to get member value from C + language side. + +Fri Aug 22 14:26:40 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * eval.c (error_print): modified exception print format. + +Thu Aug 21 16:10:58 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * sample/ruby-mode.el (ruby-calculate-indent): wrong indent level + calculated with keyword operators. + +Thu Aug 21 11:55:41 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * version 1.0-970821 + +Thu Aug 21 11:36:58 1997 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp> + + * parse.y (arg): ary[0] += 1 cause SEGV + +Wed Aug 20 14:24:42 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * version 1.0-970820 + + * eval.c (rb_call): infinite loop bug + +Tue Aug 19 00:15:38 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * version 1.0-970819 + + * eval.c (rb_call): did not raise ArgumentError if too many + arguments more than optional arguments (without rest arg). + + * eval.c (rb_eval): did not work well for op_asgn2 (attribute + self assignment). + +Mon Aug 18 09:25:56 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * object.c (inspect_i): did not display T_DATA instance variables. + + * parse.y: provides more accurate line number information. + + * eval.c (thread_value): include value's backtrace information in + the variable `$@'. + + * eval.c (f_abort): print backtrace and exit. + +Sat Aug 16 00:17:44 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * object.c (class_s_new): do not make subclass of singleton class. + +Fri Aug 15 15:49:46 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * eval.c (call_trace_func): block context switch in the trace + function. + + * eval.c (rb_eval): clear method cache at class extention. + +Fri Aug 15 19:40:43 1997 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp> + + * ext/socket/socket.c (Init_socket): small typo caused SEGV. + +Tue Aug 12 16:02:18 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * variable.c: option variables: $-0, $-p(readonly), $-v, + $-I(load_path), $-a(readonly), $-K, $-d, $-F, $-i, $-l. + + * parse.y (yylex): ignore rd (ruby document) in the code. + +Mon Aug 11 12:37:58 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * re.c (Init_Regexp): $-K as alias to the $KCODE. + + * io.c (Init_IO): new virtual variable $-i for the value of -i + option. + + * enum.c (Init_Enumerable): include? as alias of member? + +Fri Aug 8 11:16:50 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * io.c (io_foreach): now the record separator can be specified. + + * io.c (io_s_readlines): new method to read in whole file (or + command output) from path. + + * ext/socket/socket.c (Init_socket): recvfrom did not work. + + * ext/socket/socket.c (sock_send): forgot to check nil for false + value. + +Thu Aug 7 11:40:01 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * object.c (Init_Object): remove private_attr/public_attr. + +Wed Aug 6 14:21:36 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * object.c (mod_attr): forgot to check nil for false value. + +Mon Aug 4 11:50:28 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * variable.c (rb_class_path): scan class constants for anonymous + classes/modules to make up pathes. + +Wed Jul 30 08:45:12 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * eval.c (rb_eval): stop to cache const value in nodes. + +Sat Jul 26 03:17:22 1997 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp> + + * numeric.c (flo_to_s): wrong .0 at end. + +Sat Jul 26 00:36:36 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * eval.c (error_print): always print exception type in the + toplevel exception handler. + + * string.c (str_hash): wrong hash value. + +Thu Jul 24 11:05:51 1997 Yukihiro Matsumoto <matz@netlab.co.jp> + + * string.c (uscore_get): proper error message for unset $_. + +Wed Jul 23 09:56:55 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * object.c (obj_methods): returns list of method names of the + specified object. + + * class.c (mod_instance_methods): returns list of method names of + the class instnace. + +Fri Jul 11 22:38:55 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * object.c (class_superclass): returns class's superclass + itself. (1.1) + + * object.c (obj_type): returns object's class itself. (1.1) + + * class.c (mod_included_modules): list included modules. + + * object.c (class_superclass): raises error for Object. + +Thu Jul 3 09:54:02 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (SETUP_ARGS): save source position, remove nd_line(). + + * eval.c (rb_call): replace modulo by bit-masking. + + * eval.c (POP_SCOPE): force recycle scope object to reduce gc rate. + + * gc.c (obj_free): aboid calling run_final() when no finalizer is set. + + * eval.c (PUSH_VARS): do not allocate the dynamic scope's end-mark + object. + +Wed Jul 2 14:25:07 1997 KIMURA Koichi <kkimura@pure.cpdc.canon.co.jp> + + * Native mswin32 support. + +Tue Jul 1 09:59:00 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970701 + + * parse.y (mrhs): allow rest-star(*) in right hand side. + +Tue Jun 24 19:04:31 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970624 + +Sat Jun 20 22:22:51 1997 Michio "Karl" Jinbo <karl@marcer.nagaokaut.ac.jp> + + * eval.c: freebsd 3.0 <sys/select.h> support. + +Fri Jun 20 01:24:45 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970620 + + * gc.c: eliminate uninitilalized field of Hash, Array etc., to + avoid dumping core. + +Thu Jun 19 01:29:44 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970619 + + * string.c (str_split_method): wrong limit. + +Sat Jun 14 01:54:16 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * class.c (rb_singleton_class): no singleton for special + constants (now raises exception). + + * eval.c (ruby_init): cbase in TOPLEVEL_BINDING need to be + initialized. + +Sat Jun 14 01:01:16 1997 maeda shugo <shugo@po.aianet.ne.jp> + + * array.c (sort_2): wrong comparison. + +Sat Jun 14 00:53:44 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * hash.c (hash_foreach): safe iteration. + +Fri Jun 13 14:04:56 1997 Michio "Karl" Jinbo <karl@marcer.nagaokaut.ac.jp> + + * configure.in: -Bshareable option for netbsd. + +Fri Jun 13 01:16:22 1997 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp> + + * io.c (pipe_open): call io_unbuffered() only for writable pipes. + +Thu Jun 12 01:14:15 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970612 + + * ext/socket/socket.c (sock_new): use io_unbuffered(). + + * ext/marshal/marshal.c (w_long): compact long format, which + supports 64 bit architectures (unless longs are >32 bit size). + + * ext/marshal/marshal.c: allows recursive data for marshaling. + + * parse.y (rb_intern): raise exception for non-internable string. + + * ext/marshal/marshal.c (marshal_load): allows direct loading from + strings. + + * ext/marshal/marshal.c (marshal_dump): allows direct dump to strings. + + * ext/marshal/marshal.c (marshal_dump): interface changed. + +Wed Jun 11 18:26:00 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * gc.c (rb_newobj): remove needless memset(). + +Mon Jun 9 13:03:43 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (rb_eval): reduce condition checks from while/until loop. + + * eval.c (rb_eval): wrong jump point for `next'. + +Fri Jun 6 11:47:39 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ruby.c (ruby_set_argv): initialize dln_argv0 for dln_a_out. + + * ext/socket/socket.c (open_unix): display path name for exceptions. + + * ruby.c (proc_options): option -S did not work well. + +Fri May 30 02:14:44 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970530 + + * eval.c (eval): set $! properly if exception raised in eval(). + + * io.c (io_write): now handles non T_FILE object. + + * io.c (io_defset): $< can be anything which has `write' method. + +Thu May 29 15:40:22 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (eval): $@ is always an array (not string). + + * pack.c (pack_unpack): avoid corrupting memory for unexpected + input strings. + +Wed May 28 12:46:13 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970528 + + * process.c (rb_waitpid): do not block other threads. + +Tue May 27 12:02:31 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (ruby_init): split initialize and processing command line + options. + + * ruby.c (ruby_options): ruby_init(0, 0, envp) dumps core. + +Tue May 20 18:59:45 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * variable.c (rb_ivar_set): invalid instance variable access for + built-in object raises TypeError. + +Fri May 16 17:32:21 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970516 + + * dir.c (push_globs): was freeing non heap pointer. + + * gc.c: remove some duplicated prototypes. + + * ext/kconv/kconv.c: fix prototypes. + +Fri May 9 11:38:59 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970509 + + * gc.c (obj_free): avoid free(NULL). + + * eval.c (rb_check_safe_str): argument missing for TypeError(). + +Thu May 8 01:14:28 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * file.c (file_s_dirname): need to return "." for path without + slashes. + +Wed May 7 19:18:48 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * process.c (f_fork): child processe does not inherit parent's + itimer setting on linux. call setitimer() again in the child + process. + +Sat May 3 02:49:43 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ext/curses/curses.c: modified for portability and add to the + standard distribution. + +Wed Apr 30 00:34:00 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * file.c (file_s_size): returns 0 for empty files (not FALSE). + +Fri Apr 25 02:17:50 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970425 + + * eval.c (f_load): free unused name-table. + + * eval.c (f_load): copy local variable name-table. + + * gc.c (obj_free): avoid free(NULL). + + * eval.c (rb_eval): forgot to make link from the scope object to + NODE_SCOPE. It may crash the interpreter. + +Thu Apr 24 00:35:09 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * random.c (f_srand): save old seed anyway. srand() returns no + value on some systems. + + * gc.c (obj_free): avoid double free of the local variable name + table. + + * parse.y (top_local_setup): modify realloc to handle offset. + +Tue Apr 22 12:58:26 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970422 + +Thu Apr 17 00:40:51 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * configure.in (rb_cv_bsdpgrp): proper check for BSD + setpgrp/setpgrp. + +Wed Apr 16 16:14:02 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (proc_call): proc called in other thread must be orphan. + +Tue Apr 15 10:46:31 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970415 + + * gc.c (obj_free): NODE_SCOPE marked from SCOPE object. + + * gc.c (gc_mark): some nodes marked wrong. + + * process.c (proc_getpgrp): wrong argument + +Fri Apr 14 18:32:42 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970414 + +Fri Apr 12 01:20:12 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ruby.h: String pointer changed to unsigned char. + +Fri Apr 11 10:27:29 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970411 + + * Makefile.in: create libruby.a before linking ruby. + + * string.c (str_strip_bang): >0x80 characters for isspace(). + + * eval.c (proc_call): set safe-level temporally + + * eval.c (proc_s_new): save safe-level in the proc context. + + * eval.c (rb_eval): no class/module extention in safe mode. + +Thu Apr 10 02:10:41 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * gc.c (gc_mark): remove some pointer checks for speeding up. + + * ruby.c (ruby_options): set $0 temporally for -r option. + + * eval.c: built-in security feature. + + * gc.c (gc_sweep): do not free nodes during compile. + + * parse.y (yycompile): set flag when compiling. + +Wed Apr 9 10:19:02 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ruby.c: forgot to include <ctype.h> for isspace(). + + * file.c: provide S_ISREG for some platforms. + + * io.c (Init_IO): added some $< operations. + + * lib/ping.rb: check host upness using TCP echo. + +Tue Apr 8 00:10:15 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * io.c (arg_read): bug with 0 length input. + +Mon Apr 7 11:36:16 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ext/fcntl/fcntl.c: module for fcntl constants. + + * eval.c (rb_alias): bug when original was an alias. + + * parse.y (primary): syntax to access singleton class. + + * eval.c (mod_public_method): method's to specify visibitily of + the class methods. make_method_{public,private} removed. + +Fri Apr 4 21:43:57 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970404 + + * gc.c (obj_free): finalizer added for experiment. + +Thu Apr 3 02:12:31 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (thread_schedule): make Fatal rise on main_thread on + deadlocks. + + * eval.c (thread_join): raise ThreadError instead of Fatal, in + case of deadlock. + + * regex.c (re_compile_fastmap): uninitialized local variable. + + * parse.y (parse_regx): new option //[nes] to specify character + code for regexp literals. Last specified code option is valid. + + * re.c (reg_s_new): addtional 3rd argument to specify compiled + regexp's character code. + + * re.c (reg_new_1): regexp character code can be specified for + each regexp object. + +Wed Apr 2 14:51:06 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (thread_create): handle uncaught throw. + + * eval.c (thread_create): halt on some deadlock conditions. + + * regex.c (is_in_list): wrong result for non-mbc higher-byte + characters. + + * regex.c (re_match): wrong skip for multi-byte characters. + + * regex.c (re_compile_fastmap): wrong fastmap in non-mbc mode. + + * hash.c (Init_Hash): hash compatible features added to ENV. + +Tue Apr 1 15:24:06 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (obj_extend): remove Object#extend as an iterator which + is in experimental state, since it unveils internal singleton + classes. + +Mon Mar 31 14:29:39 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970331 + +Sun Mar 30 19:40:57 1997 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp> + + * parse.y (terms): avoided win32 gcc's optimization bug. + +Sat Mar 29 11:21:58 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * struct.c (make_struct): St[val,..] creates new structure. + +Fri Mar 28 11:24:51 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (obj_make_private): new method make_method_{public,private} + to change visibility of singleton methods. + + * regex.c (re_compile_pattern): enables numeric literal >= 0x80 in + the character class. + + * regex.c (re_compile_pattern): enabled numeric literal >= 0x80, + in multibyte mode. + + * regex.c (re_compile_fastmap): modified exantn and charset(_not) + to set fastmap for higher bytes properly. + + * regex.c (is_in_list): now matches numeric literals. + +Thu Mar 27 13:34:20 1997 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp> + + * pack.c (pack_unpack): extra null byte after unpacked string. + +Wed Mar 26 15:20:34 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * regex.c (re_compile_pattern): register numbers must be fit in a + byte (0 <= regnum <= 0xff). + + * regex.c (re_compile_fastmap): forgot to set mbchar map for + charset_not if RE_MBCTYPE is on. + + * regex.c (re_compile_pattern): set list bits for multi-byte + characters for \W, \S, \D in range expression. + + * object.c (obj_is_kind_of): defined that nil itself is kind of + nil. TRUE is kind of TRUE, FALSE is kind of FALSE likewise. + This change makes `obj.kind_of?(eval(obj.type))' always true. + +Tue Mar 25 14:08:43 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * lib/English.rb: provides nicer English alias for the variables. + + * parse.y (expr): alias $var1 $var2 makes alias of the global + variable. + +Mon Mar 24 18:23:20 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970324 + +Thu Mar 20 22:04:59 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (mod_modfunc): forget to clear method cache. + +Wed Mar 19 17:06:55 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (program): set methods' default private/public status + correctly under eval(). + + * eval.c (eval): set the_class correctly while evaluating string. + +Tue Mar 18 12:23:53 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (eval): yield can be called from eval(). + + * version 1.0-970318 + + * parse.y (program): regexp in condition expression should do + matching operation with $_. + + * re.c (reg_regsub): wrong substitution. + +Fri Mar 14 14:36:28 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * hash.c (hash_invert): returns value to key mapping of the + associative array. + + * ext/socket/extconf.rb: set environment variable SOCKS_SERVER to + compile with libsocks.a. + + * ext/socket/socket.c (socks_s_open): SOCKSsocket class to access + internet via SOCKS library. + + * sprintf.c (f_sprintf): unsigned formats display leading double + dots for imaginary sequence of signed bit to the left. + + * sprintf.c (f_sprintf): correct width and precision formatting + for big integers. + + * parse.y (yylex): enables negative hex/octal numbers and `_' in + non-decimal numbers. + + * sprintf.c (f_sprintf): %u added for unsigned decimal format. + +Thu Mar 13 10:24:27 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * sprintf.c (f_sprintf): wrong output for bignums. + + * array.c (ary_reverse_each): iterates in reverse order. + + * pack.c (pack_unpack): L unpacked signed long. + + * io.c (f_backquote): now returns an empty string for no output. + +Wed Mar 12 10:20:30 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ext/socks/socks.c: socket module with socks library. + +Mon Mar 10 20:44:22 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * re.c (reg_regsub): \& for substitution. \`, \', and \+ are + avaiable also. + +Thu Mar 6 01:47:03 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970306 + + * sample/rubydb.el (gud): ruby debugger emacs interface + + * lib/debug.rb: ruby debugger + + * parse.y (exprs): more accurate line number display. + +Wed Mar 5 21:31:46 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970305 + +Tue Mar 4 12:28:32 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ruby.c (proc_options): search through RUBYPATH and PATH for + option -S. + +Mon Mar 3 22:44:55 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (thread_status): returns nil for exception terminated + threads. + + * eval.c (thread_value): re-raise exceptions. + +Sat Mar 1 00:59:47 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (rb_eval): restore $! value after rescue clause, to + re-raise exceptions correctly. + +Fri Feb 28 16:43:38 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970228 + +Thu Feb 27 11:23:41 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (rb_yield_0): redo raises exception + + * eval.c (thread_schedule): bug in interrupt handling by rescue. + +Wed Feb 26 00:55:36 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (eval): forgot to restore dynamic local variable + bindings. + +Tue Feb 25 11:22:08 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ext/aix_ld.rb: AIX dynamic load support (not tested). + + * eval.c (rb_eval): wrong return value for defined? super. + + * error.c (exception): more error check. + + * re.c (reg_regsub): wrong substitution when sub expanded to null + string. + +Fri Feb 21 13:01:47 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970221 + + * eval.c (f_require): volatile added. register variable was + recycled, so that GC did not mark that variable. + + * object.c (Init_Object): forget to mark main object (was mostly + ok, but made trouble with early GC.) + +Thu Feb 20 11:50:50 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970220 + +Thu Feb 20 11:25:50 1997 Yasuo OHBA <jammy@shljapan.co.jp> + + * lib/date.rb: update + +Thu Feb 20 08:25:57 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (yylex): forgot tokfix() before rb_intern(). + + * lib/tk.rb (TkVariable): give up using trace_var. + +Wed Feb 19 00:24:35 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970219 + + * pack.c (pack_pack): packed by null for A specifier. must be + space filled. + + * pack.c (pack_unpack): bug in skipping spaces + + * gc.c (xmalloc): garbage collect for every 4 Meg. allocation. + + * string.c (str_split_method): limit worked wrong way. + + * io.c (io_gets_method): misunderstand 0xff in binary files when + $/ == nil. + + * re.c (reg_regsub): re-implement. + + * ext/socket/socket.c (thread_connect): remove O_NONBLOCK, which + is not defined on some platform like NeXT. + +Mon Feb 17 13:08:30 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970217 + + * object.c (mod_eqq): === extended for subclass check (to use case + as typecase). + +Sat Feb 15 02:07:22 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * regex.c (re_compile_pattern): wrong match backref at end of pattern. + + * io.c (arg_read): now works beyond end of file. + +Thu Feb 13 16:21:24 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (expr): return/yield now accept normal argument format. + + * parse.y (yylex): a star in `yield *x' must not be multiplication + operator. + +Wed Feb 12 15:06:44 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * time.c (time_plus): bug in simple addition. + + * eval.c (thread_raise): raise exceptions from outside. + + * eval.c (Init_Thread): Thread#alive? -- alias for Thread#status. + +Mon Feb 10 00:38:55 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ruby.h (Data_Make_Struct): rename macros. + +Sun Feb 8 11:48:13 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * io.c (f_syscall): argument offset was wrong. + +Fri Feb 7 18:01:17 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970207 + + * eval.c: add volatiles to avoid variable crobbering by longjmp(). + + * eval.c (f_raise): 1st argument can be the GlobalExit object now. + + * array.c (ary_unshift): no longer accept more than 2 args. + + * eval.c (f_raise): bug if 2nd argument is the exception. + +Tue Feb 4 00:37:29 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970204 + + * eval.c (eval): check compile errors by nerrs. + + * eval.c (rb_eval): check syntax error by nerrs, not by the return + value, which may be NULL. + + * eval.c (compile): Do not clear errinfo. + +Mon Feb 3 10:13:06 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (obj_extend): move real inclusion to Module#extend_object + to allow redfinition. + + * object.c (Init_Object): Kernel class is now Module. Object class + became the true root class. + + * object.c (obj_inspect): remove useless buffer. + + * hash.c (any_cmp): disable interrupts and context switching. + + * st.c: remove ALLOW_INTS to disable interrupt during operations. + +Fri Jan 31 22:10:08 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * hash.c (hash_rehash): re-register all key-value. + +Thu Jan 30 02:14:49 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * io.c (io_reopen): re-implement according to clone() way. + + * io.c (io_clone): copy IO object. + + * struct.c (struct_eql): compare elements by eql?. + + * io.c (io_mode_flags): detect "rb", "wb" etc. + + * io.h (FMODE_BINMODE): added. + + * ext/socket/socket.c (Init_socket): undef BasicSocket.new + + * file.c (Init_File): File.new(path[,mode]) + + * io.c (Init_IO): IO.new(fd[,mode]) + + * eval.c (rb_method_boundp): forgot to enable priv argument. + + * object.c (Init_Object): remove `=~' from Kernel class. + + * ext/socket/socket.c (open_inet): initialize sockaddr before + calling bind(2). + + * sample/ruby-mode.el (ruby-calculate-indent): skip comment lines + +Wed Jan 29 18:43:22 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (Init_Thread): DEFER_INTS during initializing threads. + + * hash.c (Init_Hash): Hash#eql? checks for object identity. + + * eval.c (thread_set_critical): wrong value assigned. + +Mon Jan 27 16:10:51 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * io.c (io_print): remove print_on(). + + * eval.c (f_missing): proper error message for undefined method + without argument + +Sat Jan 25 23:32:32 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * string.c (str_sub_s): false alert - sub() does not modify string. + + * array.c (ary_times): negative multiplication detected + + * string.c (str_times): negative multiplication detected + +Fri Jan 24 10:51:39 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * time.c (time_arg): month -> 0 == "jan" == "1" == "01", little bit + confusing but wanted to conform japanese style. + + * version 1.0-970124 + +Fri Jan 24 09:52:49 1997 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp> + + * util.c (_fixpath): supports SJIS filenames on DJGPP. + +Thu Jan 23 16:52:06 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * README.EXT: update. partially translated into English. + + * ext/extmk.rb.in: inherit $LDFLAGS to the final link. + + * ext/socket/socket.c (Init_socket): add various constants. + +Mon Jan 23 11:40:59 1997 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp> + + * eval.c (Init_Thread): allocate main_thread first to avoid crash. + +Thu Jan 23 02:09:26 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * gc.c (ObjectSpace): API modified. each_object method will do all + the iteration. + + * eval.c (proc_call): wrong return from nested lambda. + + * ext/GD/GD.c: debugged. + +Wed Jan 22 16:12:25 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970122 + + * gc.c (gc_mark): forgot to mark match->str. + + * ext/GD/GD.c: GD interface module. + + * eval.c (PUSH_BLOCK): wrong value pushed as the block level. + +Mon Jan 20 14:01:31 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (thread_run): no context switch in the critical section. + +Mon Jan 20 09:40:59 1997 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp> + + * utils.c: supports 8+3 filenames + +Sat Jan 18 01:23:03 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970118 + + * regex.c (PATFETCH): need cast to unsigned char. + + * io.c (io_ctl): bug in case when arg is not a string. + + * lib/tk.rb: forgot that Kernel#type returns the class name now. + + * regex.c (re_search): "abc\n" =~ "^$" should not match. + +Fri Jan 17 12:31:37 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970117 + + * ruby.c (ruby_options): constant PLATFORM, which is in the {cpu}-{os} + form, defined. + + * configure.in: platform infomation embedded in the interpreter. + + * regex.c (re_search): /^$/ did not match to "" by wrong exit condition. + + * lib/thread.rb: re-write Mutex/Queue based on Thread.critical. + + * eval.c (thread_set_critical): remove Thread.exclusive, add + Thread.critical = TRUE/FALSE instead. + + * re.c (reg_search): re-compile pattern if needed + + * regex.c (PATFETCH): do translate at compile time + +Thu Jan 16 00:49:10 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * gc.c (gc_mark_frame): forgot to mark frame->cbase. + + * regex.c (re_compile_pattern): /a$|b)/ causes error. + + * regex.c (re_compile_pattern): /(^|b)/ causes error. + + * version 1.0-970116 + + * re.c (Init_Regexp): set RE_CONTEXTUAL_INVALID_OPS flag. + +Tue Jan 14 02:09:06 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (proc_call): Proc#callをイテレータとして呼んだ時に対応 + + * configure.in: nextstep対応? + + * eval.c (rb_eval): a[b]=cで無駄な配列を割り当てない + + * eval.c (f_send): イテレータとして呼ばれたらイテレータとしてメソッ + ドを呼ぶ. + + * string.c (str_new4): match共有用の生成関数 + + * re.c (reg_search): matchの実体(文字列)をマッチを行った文字列と + copy-on-writeで共有 + + * string.c (str_hash): toupperをかける条件が違っていた + + * array.c (sort_2): FixnumとStringを特別扱いして高速化 + +Mon Jan 13 11:03:53 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (thread_create): threadが生成されるまで割込みを設定しない + + * eval.c (Init_Thread): 割込みタイミングを100msecに + +Sat Jan 11 00:17:05 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * regex.c (re_search): マッチに失敗する場合があった(本当に直ったか?) + + * io.c (io_ioctl,io_fcntl): 第2引数を省略可能に + + * io.c (io_ioctl,io_fcntl): 戻り値がIOだった.整数(システムコール + の戻り値)を返すようにした. + + * io.c (io_ctl): 引数が整数の時に対応 + + * io.c (io_fcntl): file.cから移動 + +Fri Jan 10 17:01:47 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-970110 + + * ext/socket/socket.c (thread_connect): open(connect(2))で他の + threadをブロックしないように + + * eval.c (thread_create): exitでないときにexitだと思い込む + +Mon Jan 6 17:42:22 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * string.c (str_sub_s): 文字列長より長いoffsetの検出 + + * regex.c (re_search): 空にマッチするパターン後の$で失敗 + +Thu Jan 2 16:36:23 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * file.c (file_reopen): Fileのreopen(pathまたはIOで指定). + + * io.c (io_reopen): IOのreopen(IOで指定) -- change classつき + +Wed Jan 1 11:09:01 1997 Yukihiro Matsumoto <matz@caelum.co.jp> + + * io.c (f_select): timeoutでnilを返す + +Fri Dec 27 13:06:44 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * file.c (file_s_open): サブクラスではそのクラスのインスタンスを返 + すように. + +Fri Dec 27 08:58:27 1996 ono@isl.nara.sharp.co.jp + + * numeric.c (flo_to_s): index()を使わない.strstr()に. + +Thu Dec 26 01:34:17 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * lib/tk.rb: placeが使えるように + + * pack.c (endian): マクロDYNAMIC_ENDIANを指定すると実行時にendian + を判定するように. + + * eval.c (thread_alloc): 初期化忘れのメンバがあった. + +Wed Dec 25 00:33:19 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 1.0-961225 + + * io.c (Init_IO): newを無効化 + + * lib/tkthcore.rb: tk_call "global $foo; set foo 5"などもできるように + + * eval.c (thread_restore_context): $~, $_でスタックを壊していた + + * process.c (rb_waitpid): threadに一応対応 + +Tue Dec 24 15:20:58 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.4-961224 + + * configure.in: charがunsignedかどうかもチェック + + * regex.c (SIGN_EXTEND_CHAR): __CHAR_UNSIGNED__にも対応 + + * pack.c (pack_unpack): 明示的にsigned charを指定. + +Mon Dec 23 14:41:23 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ruby.c (load_file): 標準入力からのスクリプトで一時ファイルを使わ + ないように + + * object.c (f_integer): `0x', `0'などでbaseを解釈するように. + +Fri Dec 20 01:44:39 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * Makefile.in (flock.o): flockに対応 + +Thu Dec 19 20:13:32 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.4-961219 + +Wed Dec 18 00:06:48 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * glob.c (glob_filename): strrchrがマクロの場合に対応 + + * configure.in: <sys/select.h>をチェック + + * ext/kconv/kconv.c: 1.62ベースに + + * ext/kconv/kconv.c: Kconvモジュール + + * string.c (str_substr): lenが元の文字列より長い時に対応 + + * parse.y (iterator): 「$bar do .. end」などは許さないように + + * parse.y (iterator): FID(foo!,foo?)をdo形式のイテレータにできる. + + * missing/flock.c (flock): lockf()を使って代替 + + * file.c (file_flock): flockを実装 + +Tue Dec 17 12:13:38 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.4-961217 + +Fri Dec 13 02:05:03 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * configure.in: RUBYLIBのカレントを後回し(@mix/awk offline) + + * dln.c: AIXに対応した?(@mix/awk offline) + + * eval.c (thread_schedule): critical sectionでも明示的なコンテキス + トスイッチは起きないとまずい + + * re.c (reg_search): matchに失敗した時に$~をnilに. + + * re.c (reg_search): 毎回matchを生成するように + +Thu Dec 12 17:03:30 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * numeric.c (flo_to_s): 2.0.to_s -> 2.0に + + * eval.c (thread_save_context): $_, $~をthread毎に保存 + + * eval.c (thread_kill): main threadではexit(0) + + * string.c (str_split_method): 間違った結果を返していた + +Thu Dec 12 15:32:48 1996 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp> + + * dir.c: CYGWIN32対応 + + * ext/socket/socket.c: CYGWIN32対応 + + * io.c: CYGWIN32対応 + +Thu Dec 12 14:43:51 1996 Jun Kuroda <j_kuro@pluto.ai.kutech.ac.jp> + + * lib/tk.rb: wish4.2も探索候補に含める + + * config.guess: JCC対応 + +Thu Dec 12 00:41:17 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.4-961212 + + * parse.y (parse_string): """..."""はやはり無くすことにした + + * parse.y (parse_regx): %r|...|でterminatorを \ でエスケープできる + ように + + * signal.c (posix_signal): sigactionを使うsignal + + * configure.in: posix signal/bsd signalの検出 + +Wed Dec 11 17:47:35 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (thread_schedule): critical sectionではコンテキストスイッ + チが起きないように + + * lib/thread.rb: SharedMutexクラス + + * lib/jcode.rb: String#scanを使うように + +Tue Dec 10 12:21:28 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.3-961210 + + * string.c (str_split_method): 正規表現に()を含む時にバグ + + * lib/jcode.rb: ちょっとましになった + + * string.c (tr_setup_table): 置換文字が短すぎる(2文字)のときのバグ + +Mon Dec 9 11:38:04 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * string.c (str_scan): 文字列のマッチを行う.イテレータとしても動 + 作する + + * regex.c (re_copy_registers): allocatedが初期化されていなかった + + * re.c (match_to_s): $~の文字列化 + + * re.c (match_to_a): $~を配列化できるように + + * re.c (match_getter): レジスタが初期化されていなかった + +Thu Dec 5 11:06:10 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * string.c (str_split_method): マッチしなかった括弧は空文字列を + pushするべきではない + + * string.c (str_succ): アルファベットを含まない文字に対応 + +Wed Dec 4 10:48:09 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.3-961204 + + * io.c (io_binmode): DJGPPでのbinmode対応 + + * sprintf.c (f_sprintf): intの範囲の数値は直接sprintfで変換する + + * sprintf.c (f_sprintf): "%02s"に頼らない + + * re.c (reg_search): indexでSEGV + +Tue Dec 3 10:09:36 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.3-961203 + + * ext/extmk.rb.in (install): INSTALL_DATAからINSTALLに変更 + + * dln.c: hpux対応 + + * string.c (str_aset_method): 負の値を含む範囲でも例外を起こさない + + * array.c (ary_replace): 負の値を含む範囲でも例外を起こさない + + * array.c (beg_len): beg==endの時,長さ0に + +Mon Dec 2 14:07:12 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * configure.in: HP shl対応 + + * string.c (str_upto): beg > endの時無限ループに落ちるのを止めた + + * range.c (range_each): String#uptoが再定義された場合に対応 + + * string.c (str_split_method): "ABC".split(/(B)/)が誤動作 + +Sat Nov 30 01:43:52 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (rb_eval): undefでSEGV + +Fri Nov 29 12:17:59 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * sample/ruby-mode.el (ruby-parse-region): %Q#..#などに対応.しか + し,区切り文字が演算子で行末にある場合には対応できなかった. + + * re.c (reg_raise): 例外でもスラッシュをエスケープ + + * re.c (reg_inspect): スラッシュをエスケープ + + * parse.y (parse_string): `%[QqXxRr](.)..\1'なる文字列形式(テスト + 採用) + + * parse.y (parse_qstring): '''...'''の形式 + + * ext/dbm/dbm.c (Init_dbm): 述語key?,value?の追加 + + * ext/dbm/dbm.c (Init_dbm): includes->include? + + * hash.c (Init_Hash): 述語key?,value?,include?の追加 + + * eval.c (rb_eval): unlessでelse節が実行されない(うーん) + + * string.c (str_sub_iter_s): イテレータブロック内でマッチが行われ + ると位置がずれる(時に無限ループに落ちる) + + * string.c (str_resize): lenが0の時sizeの調整が行われなかった + +Thu Nov 28 00:59:54 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.3-961128 + + * parse.y (parse_string): 3-quote styleの文字列(例:"""abc"d"e""") + + * configure.in (EXTSTATIC): extを静的にリンクする時にはrubyはdllを + 使うように + + * io.c (Init_IO): getsの引数が間違っていた + + * string.c (str_each_line): RSを明示的に指定できるように + +Wed Nov 27 12:37:46 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.3-961127 + + * eval.c (rb_eval): iver defined? でselfを指定するのを忘れた + + * io.c: gets等でRSを明示的に指定できるように + + * ext/extmk.rb.in (install): static linkに失敗 + +Tue Nov 26 10:33:04 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.3-961126 + + * string.c (str_sub_s): 置換後の文字列長さが間違っていた + +Mon Nov 25 09:11:22 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * numeric.c (fix_rshift): 32以上の右シフトで0を返すように(Cの + rshiftは(x>>(y%32))を返していた). + + * string.c (str_gsub): 置換が行われない場合があった + + * string.c (str_resize): 本当に必要な時だけrealloc + +Thu Nov 21 04:13:21 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * configure.in (EXTSTATIC): --with-static-linked-extで全てのモジュー + ルを静的リンクするように + + * pack.c (pack_unpack): 行末の改行がない時にもチェックサムをスキッ + プするように + +Wed Nov 20 96 21:42:51 1996 Yasuo OHBA <jammy@shljapan.co.jp>xc + + * configure.in: freebsd対応 + +Wed Nov 20 10:24:24 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ext/extmk.rb.in (install): 通常リンク用のLDFLAGSとダイナミックリ + ンク用のDLDFALGSを分離 + + * ext/extmk.rb.in (install): コンパイルの成功したものを静的リンク + のリストに追加する + + * eval.c (f_missing): オブジェクトの文字列表現が長すぎる時バッファ + を書き潰していた + + * process.c (proc_exec_v): forkした後例外を発生させてはいけない + +Tue Nov 19 13:28:15 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.3-961119 + + * eval.c (mod_method_defined): Module#method_defined? の追加 + + * parse.y (call_args): 引数が唯一のコマンドコールである時のバグ(戻 + り値が展開されてしまう) + +Mon Nov 18 13:28:18 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * string.c (str_sub): 失敗した時にnilを返していた + + * string.c (str_split_method): 検索開始位置が移動してなかった + + * ext/socket/socket.c (sock_s_getservbyaname): まだ間違っていた + + * version 0.99.3-961118 + + * string.c (str_sub_s): 元の文字列を置換するのを止めた + + * pack.c (encodes): 領域外をアクセスしていた + +Fri Nov 15 17:10:35 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * bignum.c (big_divmod): Bignumが引数の場合の対応忘れ + + * sample/ruby-mode.el (ruby-expr-beg): word?形式への対応が不完全 + +Wed Nov 13 15:42:40 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * string.c (str_tr_s_bang): tr_sでtrが行われていなかった + + * eval.c (rb_eval): autoloadクラスのチェック + + * string.c (f_sub): subがsub!と同じ動作になっていた + + * eval.c (thread_sleep): stopとsleepの分離 + +Mon Nov 11 13:53:19 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.3-961111 + + * numeric.c (fix_step): to, stepが整数以外の場合に対応 + + * eval.c (rb_call): dynamic varがdynamic scopingになっていた(これ + はまずい) + + * string.c (str_chop_bang): 長さ0の文字列のchopで,領域外のアクセ + スが発生していた. + + * parse.y (yyerror): 割り当てた領域外をアクセスしていた + +Fri Nov 8 11:54:46 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (thread_yield): scopeをheapにコピー + +Thu Nov 7 09:56:53 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * numeric.c (num_coerce): とりあえず両辺をFloatに変換することに + +Wed Nov 6 10:45:13 1996 Yasuo OHBA <jammy@shljapan.co.jp> + + * lib/parsearg.rb: 第2引数を変更. + +Tue Nov 5 14:21:09 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.3-961105 + +Sat Nov 2 01:11:40 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * bignum.c (big_pow): typo (dy -> dx) + + * bignum.c (big_divmod): 知らない型はfloatに変換してみる + + * numeric.c (fix_lshift): 境界条件のバグ(負になっていた) + + * bignum.c (big_pow): 無駄なfloatへの変換をなくした + + * math.c (math_atan2): typo(x -> y) + +Fri Nov 1 15:30:59 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ext/socket/socket.c (sock_gethostname): gethostnameがない時には + unameを使ってホスト名を得る + + * ext/etc/etc.c (etc_getlogin): getloginがNULLを返しても環境変数を + 調べるように + + * object.c (krn_clone): オブジェクトのフラグもコピー + + * hash.c (rb_cmp): ハッシュの比較を`=='でなく`eql?'に変更 + + * math.c (Need_Float): Float()を使って変換する + + * compar.c (cmp_gt): 以前の右辺を返す仕様の名残が残っていた + +Thu Oct 31 12:55:51 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.3-961031 + + * numeric.c (Init_Numeric): typo + + * eval.c (error_print): 長すぎるtrace backを途中省略する + + * regex.c (re_compile_pattern): 全角のrangeに対応 + +Wed Oct 30 03:03:18 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.3-961030 + + * io.c (f_ungetc): 関数を追加 + + * eval.c (dyna_var_asgn): return値忘れ + +Tue Oct 29 10:05:28 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * string.c (f_split): 関数splitを追加 + + * eval.c (rb_call): ネストした外側のクラス/モジュールの定数を参照 + できるように + +Mon Oct 28 09:51:03 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * string.c (str_sub): offsetが文字の末尾にある時のチェック + + * regex.c (re_match): 割り当てるレジスタの数が1多かった + + * io.c (io_gets): $/ = ""の動作をperlに合わせる(awkとはちょっと違 + うらしい) + + * io.c (io_gets): $/ = nilの時少し高速化 + + * string.c (str_split_method): 括弧がnullにマッチした時にも無視し + ないように + + * string.c (str_split_method): 括弧にマッチした分はlimitの数に含め + ないように. + + * numeric.c (num_coerce_bin): coerceの定義を変更,2要素の配列 + [x,y]を返すように + + * sample/ruby-mode.el (ruby-calculate-indent): "do |aa|"の対応を改 + 善した. + +Sat Oct 26 01:43:51 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ext/marshal/marshal.c (w_object): ビルトインクラスのサブクラスを + 正しく復旧できるように + + * ext/marshal/marshal.c (w_object): ユーザ定義dumpの優先 + + * numeric.c (flo_coerce): Float()を使って定義 + + * numeric.c (Init_Numeric): Numericのnewのundefはまずい + + * ext/marshal/marshal.c (w_symbol): シンボルの内容(文字列)は一度し + かファイルに書き出さない. + + * sample/ruby-mode.el (ruby-parse-region): if/while修飾子に対応し + なくなっていた + + * bignum.c (Init_Bignum): Bignum.newを除く + + * eval.c (rb_eval): 引数評価後にファイル名と行番号を再設定 + + * numeric.c (flo_div): typo + + * sample/ruby-mode.el (ruby-parse-region): def /, def `に対応 + +Fri Oct 25 09:26:29 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * sample/ruby-mode.el (ruby-calculate-indent): "do |aa|"に対応 + + * array.c (ary_aset): indexがfixnumの場合ちょっと高速化 + + * eval.c (thread_fd_writable): 書き込み前のselectチェック + + * array.c (ary_assoc): 無限ループに落ちた + + * eval.c (thread_wait_for): selectがエラー終了した時,linux以外で + の動作が正しくなかった. + +Thu Oct 24 08:26:48 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (backtrace): `$@'を文字列から配列に変更した. + + * eval.c (eval): eval中の例外発生位置を保存する + + * bignum.c (bigsub): オペランドの大小比較の失敗 + + * re.c (reg_search): 直接参照がない時にも`$~'がセットされるように + +Wed Oct 23 10:40:10 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.2-961023 + + * ext/marshal/marshal.c (r_bytes): mallocをやめ,allocaを使う + + * sample/ruby-mode.el (ruby-calculate-indent): 括弧の対応を変更. + ()内ではインデントをレベルを合わせるように + +Tue Oct 22 12:59:11 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * hash.c (hash_s_new): sizeを指定できるように + + * ext/marshal/marshal.c (w_object): dumpする深さ制限を指定できるよ + うに + + * array.c (ary_s_new): sizeを指定した時の初期化忘れ + + * object.c (f_float): big2dblの宣言忘れ. + + * bignum.c (bigsub): 大きさの近いBignum同士の演算で結果が負になる + 場合に間違いがあった. + + * array.c (ary_aset): 置換先と置換元が同じ長さの時内容を + shift(memmove)しないように. + + * ext/marshal/marshal.c (marshal_dump): ファイルフォーマットにバー + ジョンを埋め込むように + + * ext/marshal/marshal.c (tmpnam): linux-aout-dln用に定義 + +Mon Oct 21 08:40:20 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ext/socket/socket.c (sock_s_gethostbyname): hostent構造体の情報 + を返す + (sock_s_gethostbyaddr): IPアドレスからhostent構造体を得る + (sock_s_getservbyaname): getservbyname(3) + +Fri Oct 18 10:37:36 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * sample/ruby-mode.el (ruby-indent-to): 移動先カラムが負になるバグ + + * eval.c (compile): evalで元ソースの行番号でエラーを表示する + +Thu Oct 17 09:52:28 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (eval): evalで文法エラーがあった時にSEGV + + * lib/safe.rb: Restricted.evalの中だけ制限を加える. + + * eval.c (error_print): バックトレースの出力.callerで例外発生位置 + を調整した時に問題が出る(そんなことをしなければ良いのだが…) + + * eval.c (make_backtrace): バックトレースの生成 + +Wed Oct 16 12:56:22 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ruby-man-0.99.2-jp/index.html: 日本語版ドキュメントの完成(長かった…) + + * re.c (reg_regcomp): $=がnilの時の処理 + + * string.c (f_chop): $_に対するchop + +Tue Oct 15 11:04:23 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.2-961015 + +Mon Oct 14 18:22:38 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (thread_schedule): BOW対応.selectが-1を返した時にバグ(実 + はdo .. whileがcontinueで先頭にジャンプすると思い込んでいた.条 + 件の直前だったのね ^^);;;;; + + * sample/ruby-mode.el (ruby-mode-syntax-table): ?のsyntaxが"/"では + まずいらしい + + * hash.c (rb_hash): name conflict + +Fri Oct 11 00:23:05 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.2-961011 + + * ext/marshal/marshal.c (w_object): 結局動いていなかった循環オブジェ + クト対応を外した. + + * hash.c (rb_hash): Fixnumと文字列の高速化 + + * ext/marshal/marshal.c (w_object): 無駄なデータの削除(フォーマッ + トの非互換性) + + * io.c (io_readline): 戻り値の不備 + + * ext/marshal/marshal.c (marshal_dumps): MSDOS対応 + + * ruby.c (load_file): MSDOS対応 + +Wed Oct 9 17:46:27 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ext/extmk.rb.in (install): 無駄なコピーを避ける + + * string.c (str_sub_method): マッチがなかった時のString#subの値が + 違っていた. + + * eval.c (obj_extend): extendした時にobject_extendedを呼ぶように + +Tue Oct 8 00:55:38 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (thread_alloc): 割当の平均化 + + * eval.c (thread_schedule): joinのバグを修正 + + * eval.c (thread_wait_for): selectへの割込みなどに対応 + + * eval.c (thread_select): linuxのselectの挙動に対応(timeoutが変化 + する) + +Mon Oct 7 09:47:19 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.2-961007 + + * eval.c (PUSH_BLOCK): the_classの保存を忘れていた. + + * ext/dbm/dbm.c (fdbm_store): sizeの保存する場所が間違っていた + + * ext/socket/socket.c (s_accept): thread対応していなかった + +Sat Oct 5 01:32:27 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * io.c (io_readchar): EOFで例外を発生させる + +Fri Oct 4 11:59:54 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ext/marshal/marshal.c (w_object): HashとObjectの復旧に必要なハッ + シュテーブルが渡されていなかった. + + * variable.c (rb_path2class): ユーザ定義クラスの復旧に失敗していた + + * variable.c (rb_path2class): クラスが存在しない時のエラーをFatal + からNameErrorへ. + + * range.c (range_s_new): first,lastが両方Numericの時エラーになって + いた. + + * range.c: start->first, end->last + +Wed Oct 2 02:02:46 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * file.c: DJGPPでchmod,chownを使えるように(ってDOSにchownがあるのか?) + + * class.c (rb_singleton_class): ビルトインクラスもextendしたり特異 + メソッドを追加したりできるように + + * variable.c (rb_set_class_path): ユーザ定義のトップレベルクラスに + pathを設定しない + + * eval.c (eval): 例外がRuntimeErrorに化けていた + + * eval.c (eval): eval中の例外の表現の改善 + + * eval.c (eval): eval_with_bindingとの一本化 + + * eval.c (rb_eval): クラス/モジュール定義の中から定義中のクラス/モ + ジュールが参照できるように + +Tue Oct 1 01:40:09 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.2-961001 + + * parse.y: cur_crefが2度宣言されていた + + * signal.c (trap): SIGSEGV,SIGBUSのない機種に対応 + + * io.c (Init_IO): 引数タイプの指定間違い + +Mon Sep 30 15:28:00 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.2-960930 + + * config.guess,config.sub: $host_osが正しく設定されない + + * eval.c (rb_eval): yieldで正しくないselfが設定されていた + + * eval.c (ruby_run): toplevelの例外処理のバグ + +Mon Sep 30 09:13:26 1996 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp> + + * djgpp対応 + +Sat Sep 28 02:45:10 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.2-960928 + + * sample/ruby-mode.el (ruby-beginning-of-block): ブロックの先頭に + 移動(正しくインデントしていないと動作しない) + (ruby-end-of-block): 同上 + + * eval.c (class_s_new): Class#newがイテレータとして呼ばれた時は + initializeもイテレータとして呼ばれるように + + * signal.c (sigsegv): SEGVでbacktraceを表示するように + +Fri Sep 27 09:51:07 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.2-960927 + + * eval.c (error_print): 引数のないraiseでメッセージが正しく表示さ + れるように. + + * eval.c (rb_longjmp): mesgがnilの時RuntimeErrorを生成する. + + * eval.c (f_raise): 引数がない時に対応 + + * eval.c (thread_mark): stack上にないデータのアドレス変換を行って + いた. + + * eval.c (Init_Thread): 割込みの間隔が1秒と長すぎた. + +Thu Sep 26 16:02:45 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (thread_schedule): 一度ペンディングになるとフラグがクリア + されていなかった. + + * process.c (rb_proc_exec): system/execの引数が空文字列であった場 + 合,例外を発生すべきだった. + + * config.sub/config.guess: 新しいものに置き換え + +Thu Sep 26 15:41:35 1996 WATANABE Hirofumi <watanabe@ase.ptg.sony.co.jp> + + * io.c (next_argv): -i.bakをBOWとDOSに対応. + +Thu Sep 26 01:31:43 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * io.c (io_sysread): EOFで例外 + + * io.c (f_readline): EOFで例外を発生するように.getsは互換性のため + nilを返すままにする + + * eval.c (proc_call): lambdaからのreturnでIN_BLOCKフラグが立ったま + まだった + + * eval.c (PUSH_BLOCK2): threadに対応するためBlockを一度stackにコピー + +Wed Sep 25 11:54:11 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (method_call): Const::method()形式を使えるようにしてみた. + 引数括弧は省略できない. + + * sample/test.rb: Process.killの存在を確かめてからテストを行う + + * eval.c (eval_with_binding): 第2引数としてbinding(またはlambda)を + 与えるとその環境でevalを実行するようにした + + * eval.c (f_binding): 現在のbindingを返す関数 + + * eval.c: block構造体にthe_classを保存するメンバを追加 + + * process.c (Init_process): kill,wait,waitpidをProcessに移動 + +Tue Sep 24 02:44:43 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * sample/ruby-mode.el: いろいろ問題が多いので以前の高速化は破棄. + 別のアプローチを使った. + + * lib/tk.rb (Tk.pack): 複数のウィンドウを受け付けるpack + +Sat Sep 21 11:08:09 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (exprs): 空文も受け付けるように文法を変更.今までは改行 + の連続だけが許されていた. + +Fri Sep 20 11:39:18 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * Failの大半を名前つき例外に変更. + + * re.c (Init_Regexp): 名前つき例外を導入. + + * eval.c (f_missing): Objectはinspectしない. + + * object.c (inspect_i): Object#inspectでloopに対応. + + * regex.c (re_search): /^$/が""にマッチしなかった. + +Thu Sep 19 19:25:12 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * regex.c (re_search): /^$/が非空行にマッチしていた. + +Tue Sep 17 10:28:11 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.2-960917 + +Mon Sep 16 10:47:56 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * sample/ruby-mode.el (ruby-calculate-indent): 演算子継続の場合の + 文字列の判定のバグ + + * sample/ruby-mode.el (ruby-calculate-indent): elseなどの次の行の + インデント計算を正しく. + +Sat Sep 14 08:37:19 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.2-960914 + +Fri Sep 13 08:06:03 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ext/socket/socket.c (tcpaddr): port番号にntohsをつけ忘れ + + * dln.c (link_undef): テーブルの種類が間違っていた. + + * bignum.c (bigadd): 引き算が発生する時に計算違いが起きていた. + + * parse.y (iter_do_block): do..endでもdynamic variableを. + + * bignum.c (big_pow): より正確な計算を(整数同士ではfloatに変換しな + い). + +Thu Sep 12 13:11:55 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * variable.c (rb_set_class_path): Stringクラスが初期化される前に + Stringを作っていた.組込みクラスにはpathはいらない + + * parse.y (yylex): 0.1が0になっていた + + * parse.y (yylex): 行番号の不整合 + + * gc.c (oblist_live_obj): 今「生きている」全部のオブジェクトを返す + イテレータ.そのクラス(またはサブクラス)の全部のインスタンスを返 + すeach_object_ofも定義した. + + * class.c (rb_define_class_id): 無駄なクラスを割り当てていた.結果 + として未初期化のクラスオブジェクトが存在していた. + +Wed Sep 11 00:56:23 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (yylex): octalの定数の検出をより正確に(090はエラーとか). + + * bignum.c (big_minus): yがxより大きい場合にエラー. + + * parse.y (yylex): エラー行番号の表示をより正確に + + * sample/ruby-mode.el (ruby-expr-beg): 変数名が1文字の時誤動作して + いた. + + * sample/ruby-mode.el (ruby-calculate-indent): ?/でループに落ちい + たバグを修正. + + * enum.c (enum_min,enum_max): sortのようにイテレータとしても動作す + るように. + + * enum.c (enum_find_all): typo + +Tue Sep 10 12:07:12 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * node.h (nd_line): NODEのlineをflagsに押し込めてオブジェクトサイ + ズを小さくした.制限:32bit intのマシンの場合,ファイルの行数が + 32767を越えると正常に表示されない. + + * st.c: hashとcompareの関数メンバを構造体にパック,クラス的な使い + 方を行う.1 tableあたり4 byteの節約. + +Mon Sep 9 16:35:54 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * file.c (file_truncate): 提供されない時には特別な例外を発生するよ + うに. + + * eval.c (Init_Proc): 不適切な位置のlocal-jumpを例外に. + +Sat Sep 7 17:06:15 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (proc_call): まだスコープがスタック上にある時には局所脱出 + を有効にする.これで,procを生成してcallすることは,スコープを脱 + 出しない限り,yieldと同じ意味を持つことになる. + +Fri Sep 6 13:30:59 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * sample/ruby-mode.el (ruby-indent-to): インデントが変わらない時に + はバッファを変更しない. + (ruby-calculate-indent): まず文字列の内部か判断してから,前の行 + からパーズを行う.defunが大きくなった時の高速化. + (ruby-in-string-p): 文字列の内部かどうかを判断する関数(以前の + parseから分離) + (ruby-parse-region): 文字列に対する処理をはずす. + (ruby-beginning-of-block): ブロックの先頭に + (ruby-end-of-block): ブロックの末尾に(遅い…) + +Thu Sep 5 14:23:07 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * file.c (file_s_split): [dirname,basename]にsplitする. + + * eval.c (rb_eval): evalの中でも定数の値が正しくなるように.これで + 定数に関しては静的なスコープが保証されるようになった. + + * st.c (rehash): ハッシュ拡大の系数を2から1.79に.割算がより良い値 + を返すように. + +Thu Sep 5 00:32:07 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (class_superclass) クラスのスーパークラスを返すメソッド. + +Wed Sep 4 16:54:56 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * random.c (f_rand): Bignumやlongの範囲を越えるFloatに対する乱数も + 発生できるように. + + * struct.c (struct_alloc): Fatalではなく例外を発生させるように(通 + 常の使用で発生しうる). + + * struct.c (struct_s_members): Structの特異メソッドではなく,生成 + されたStructクラスの特異メソッドにした. + + * st.c (st_init_table): ruby専用にパラメタを固定にした(サイ + ズが減った) + +Mon Sep 2 11:37:59 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * array.c (ary_shift): capaがあまりにも大きい時には領域をREALLOC + (ary_pop): 同上 + + * string.c (str_inspect): multibyte character 対応にミス. + (str_inspect): unsigned charにしないと符号展開されてしまう + + * parse.y (primary): `::'をprimaryに移動 Foo::Bar.Bazがエラーにな + らないように. + + * parse.y (primary): オペレータ形式の特異メソッドが定義できない + + * random.c (f_rand): maxが0の時に対応 + + * io.c (io_printf): 関数を定義していたがインタプリタに登録していな + かった. + + * file.c (file_s_basename): 第2引数が無い時にエラー. + +Thu Aug 29 10:49:40 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (expr): イテレータの新形式に「method do .. end」形式を採 + 用した.もちろん昔の形式も有効. + + * sample/ruby-mode.el (ruby-calculate-indent): endの数の方が多い場 + 合にもエラーを起こさないように. + +Wed Aug 28 09:41:36 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * numeric.c (upto,downto,step,times): 対象がfixnumの範囲を越えても + 動作するように. + +Mon Aug 26 10:04:37 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * missing/setenv.c (envix): typo(missing `== 0' for memcmp) + + * dir.c (dir_foreach): foreach(dir open -> read loop -> closeまで) + + * io.c (io_foreach): foreach(file open -> read loop -> closeまで) + + * Fatalのうち捕捉可能ないくつかを例外に. + +Sat Aug 24 23:56:37 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * bignum.c (bigdivmod): FIX2INT -> INT2FIX 大間違い + +Fri Aug 23 18:13:03 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * regex.c (re_free_registers): allocateしていない時には当然 free + してはいけない. + +Thu Aug 22 01:20:35 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (thread_create): 外側から強制終了させられたthreadは + cleanupする必要が無い. + +Wed Aug 21 09:57:28 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (thread_create): threadを終了させた大域脱出の情報を + main_threadに渡すように. + + * parse.y (call_args): 最終引数に括弧を省略したメソッド呼出しを置 + けるように(例: print foo bar, baz == print(foo(bar,baz))) + +Tue Aug 20 13:37:16 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (masign): 多重代入とrest引数の動作を合わせて空の配列を代 + 入するように. + + * parse.y (arg): defined?の強度をもうちょっと強く + + * eval.c (error_print): -wで例外名も表示するように + + * eval.c (rb_eval): 新構文に対応 + (handle_rescue): 捕捉する例外を kind_of? で同定 + + * parse.y (primary): rescueの構文を変更(同定引数の追加,複数rescue) + + * Fail()のかなりを適当な例外を使うように + + * eval.c (thread_interrupt): Interrupt(今はnon-local jump)は + main-threadに送られるように. + + * eval.c (rb_longjmp): $! の内容を文字列から例外クラスに変更 + (rb_raise): rb_fail から名称変更 + (rb_interrupt): 例外化 + (rb_exit): 例外化 + + * error.c (Init_Exception): 例外クラスの新設(文字列のサブクラス) + +Mon Aug 19 19:40:52 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * signal.c (trap): 古いハンドラを返すように. + +Wed Aug 14 00:07:18 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (rb_trap_eval): ハンドラのためにthreadをforkすることを止 + めた. + + * eval.c (thread_mark): thread毎の $!, $@ をマークし忘れ + + * ext/dbm/dbm.c (fdbm_delete): イテレータとして呼ばれた場合,要素 + が無ければブロックを評価する. + + * hash.c (hash_delete): イテレータとして呼ばれた場合,要素が無けれ + ばブロックを評価する. + + * array.c (ary_delete): イテレータとして呼ばれた場合,要素が無けれ + ばブロックを評価する. + + * eval.c (rb_interrupt): SIGINTのデフォルトをexitから特別な大域脱 + 出に.やはり割り込まれた位置の表示が無いのは寂しいので. + +Tue Aug 13 01:34:00 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (rb_exit): sub-thread内でのexitもstatusを保存するように + (thread_create): 自thread内のexitに対応 + + * signal.c (sighandle): SIGINTのデフォルトハンドラはexitするように + (以前は例外を発生していた). + + * 例外の一部をFatalに. + + * string.c (str_aset): 文字列の置換の対象が部分文字列でなかった時, + 例外を発生させないように + + * eval.c (proc_call): Procの中からbreak/nextは通し,他のものは通さ + ないように + +Mon Aug 12 14:15:09 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * object.c (krn_type): 文字列を返す + + * eval.c (thread_create): sub-thread内でのexitに対応 + + * numeric.c (fix_type): 文字列を返す + + * io.c (f_p): デバッグ用データ表示メソッド + + * eval.c (f_missing): nil/TRUE/FALSEを特別扱い + + * string.c (str_inspect): 長い文字列を短縮表示.inspectの働きを + human readable stringの生成に統一(re-generatable string は正式に + 無くなった). + +Sat Aug 10 16:54:21 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * object.c (Init_Object): kernel/nil/false/trueのクラス名を変更(小 + 文字に),rubyスクリプトからアクセスできないように. + + * eval.c (rb_eval): CONSTANTのアクセス先を単純化.crefを使わない. + + * eval.c (f_eval): 特異メソッド内でも定数の値が正しくなるように + +Fri Aug 9 12:23:17 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * array.c (ary_concat): append -> concat Stringに合わせた + + * parse.y (yylex): `$;'が使えなかった. + + * array.c (ary_push_method): 複数引数を受け付けるように. + (ary_unshift): 複数引数を受け付けるように. + + * io.c (io_popen): IO.popenでcommand pipeが開けるように. + + * object.c (Init_Object): KernelとNilをruby scriptからアクセスでき + ないように. + +Thu Aug 8 01:21:47 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * object.c (f_integer): 整数への変換関数 + (f_float): 実数への変換関数 + (f_string): 文字列への変換関数 + (f_array): 配列への変換関数 + + * bignum.c (big_to_i): FIXNUMの範囲でない時はBignumのまま返すよう + に変更. + +Wed Aug 7 09:28:38 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99.1-960807 + + * parse.y (mlhs): 「*foo = 1,2,3」タイプの多重代入も可能に. + + * object.c (Init_Object): クラスTrue/Falseをruby scriptからアクセ + スできないように. + + * object.c (nil_inspect): inspect表現は"nil"に + + * io.c (io_print): nilのprintをnilに. + + * object.c (nil_to_s): nilの文字列表現を""に. + +Tue Aug 6 01:12:32 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * dir.c (dir_s_open): file descripterが足りない時にはgcしてからも + う一度openしてみる. + + * io.c (rb_fopen): すべてのfopen()についてfile descripterが足りな + い時にはgcしてからもう一度openしてみる. + + * ext/socket/socket.c (Init_socket): 定数の追加. + + * sample/ruby-mode.el (ruby-indent-to): インデント後のカーソル位置 + の調整を正しく. + + * gc.c (gc): 割込みチェックを行わない(Cコードの中で安心して + malloc()が使えなくなるので). + + * st.c (call_hash_func): signalとthreadによる割込みに対応. + + * sig.h (DEFER_INTS): 割込み禁止区間の指定 + + * eval.c (f_require): threadによるrequireの競合に対応(最初の + requireが終了するまで他のthreadは待つ). + + * bignum.c (str2inum): 0x80000000の値が負になっていた + + * sprintf.c (f_sprintf): 文字列末尾,行末の単独の`%'に対応 + + * bignum.c (big_cmp): 比較の結果が逆になる時があった. + +Mon Aug 5 10:58:13 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * process.c (proc_exec_v): 例外のメッセージを分かりやすく. + + * ext/dbm/dbm.c (fdbm_store): nilを格納すると要素の削除になる + + * ext/dbm/dbm.c: サイズをキャッシュ. + +Sat Aug 3 01:52:52 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (rb_fail): `fail'が引数無しで呼ばれた時だけ以前の`$@'を保 + 存するように. + + * eval.c (f_fail): frameの調整 + +Fri Aug 2 11:26:21 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ext/socket/socket.c (bsock_setopt): valとしてTRUE/FALSE/Fixnumも + 受け付けるように. + + * ext/socket/socket.c (Init_socket): SO_REUSEADDR等の定数の追加 + + * ext/md5/md5init.c: md5モジュール(初の複数ファイルからなるモジュー + ルでもある) + + * ruby.h (Make_Data_Struct): Data: objectのinstance変数に格納 -> + Data型のObjectに(Dir,Time,Proc,Thread,DBM) + +Thu Aug 1 11:38:44 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ext/dbm/dbm.c (fdbm_store): valueが文字で無い時に対応 + +Wed Jul 31 10:53:42 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ext/socket/socket.c (open_inet): htonsが必要であった + (tcpaddr): ntohlで変換した + + * process.c (rb_proc_exec): execvp -> execv + +Tue Jul 30 17:48:33 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c: `$?'をthread localに + + * Makefile.in (install): install時にstripを行う + + * configure.in: install時のstripの検出 + + * configure.in: NEXTSTEP対応 + + * version 0.99.1-960730 + +Tue Jul 30 16:40:35 1996 SHIROYAMA Takayuki <psi@fortune.nest.or.jp> + + * dln.c (dln_load): NeXT dln(mach-o)対応.configureは未対応 + +Tue Jul 30 09:46:51 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * process.c (f_system): 複数引数もとれるように + + * process.c (f_exec): 複数引数もとれるように + + * array.c (ary_append): 配列(またはEnum)の要素を破壊的に追加 + + * array.c (ary_plus): Enumはその要素を追加 + + * file.c (file_s_open): File.openを追加 + + * struct.c (struct_new): FIX2INTを忘れていた + + * file.c (Init_File): exists? -> exist? + + * object.c (obj_is_kind_of): is_kind_of? -> kind_of?, is_a? + + * object.c (obj_is_instance_of): is_instance_of? -> instance_of? + +Mon Jul 29 16:40:02 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (parse_regx): 式展開を行った場合,casefoldの設定ができて + いなかった. + + * object.c (true_type): TRUE/FALSEにtypeを実装. + + * parse.y (read_escape): 3文字以内のoctalに対応(\0とか) + +Fri Jul 26 00:31:45 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * array.c (ary_reverse_bang): in-placeで配列を反転させる + (ary_sort_bang): in-placeでsortする + (ary_sort): sortした配列を返すように + (ary_delete_at): 指定した位置の要素を削除する + + * eval.c (rb_call): stack深さチェックを毎回は行わないように + + * error.c (Warning): 実行中のwarningが表示されていなかった + + * eval.c (compile): 例外発生を分離. + + * eval.c (f_eval): 変数rb_in_evalを正しく管理するように + + * ext/dbm/dbm.c (fdbm_store): 格納するkeyを文字列に変換 + + * eval.c (rb_call): 無限再帰のチェックを大域脱出を行うC methodにも + 対応させた.threadのstack深さチェックルーチンを流用. + + * parse.y (yylex): 第1引数のunary -/+の判定が間違っていた. + + * parse.y (yylex): unary +で数字を余計に読んでいた(ex. +5 -> 55) + +Thu Jul 25 12:15:04 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (yylex): 曖昧でない引数に対して警告を出していた. + + * eval.c (iterator_p): 引数で呼んでも正しい結果を返すように. + + * parse.y: break/next/redo/retryのメソッド化. + + * sample/ruby-mode.el (ruby-calculate-indent): nestのチェックミス + + * sample/ruby-mode.el (ruby-parse-region): 予約語のチェックを強化 + + * parse.y (primary): unless/untilの復活 + +Tue Jul 23 18:50:10 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * array.c (Array#empty?), Hash.c (Hash#empty?), ext/dbm/dbm.c (DBM#empty?): + 空の判定述語 + + * eval.c (f_unless): ifの逆をするイテレータ + + * eval.c (f_until): whileの逆をするイテレータ + + * parse.y: notの優先順位をand/orより高く + + * parse.y (expr): `!'を引数括弧を省略したcallでも有効に + +Mon Jul 22 10:15:38 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99-960722 + + * array.c (ary_print_on): OFSのNILチェックが不完全 + + * ruby.c (load_file): 標準入力からのスクリプトが空の時に対応. + + * ruby.c (proc_options): -wでは引数無しの時には標準入力からスクリ + プトをとる(-vではたんに終了する). + + * array.c (ary_compact): nilの要素を取り除くメソッド + + * array.c (ary_nitems): nilでない要素を数えるメソッド + +Sun Jul 20 00:51:53 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ruby.c (proc_options): -w optionを追加 + + * parse.y: {}が閉じていない時には展開しない文字列を + +Fri Jul 19 16:16:05 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99-960719 + + * lib/find.rb: 石塚版(pruneの拡張付き) + + * file.c (test_l): lstatで調べないとね. + + * eval.c (f_throw): 第2引数を省略可能に. + + * parse.y (str_extend): {}のネストに対応 + +Thu Jul 18 18:25:46 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99-960718 + + * parse.y (str_extend): 文字列中の式展開に \" ' ` / を含む事ができ + るように. + +Tue Jul 16 15:55:31 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * sample/ruby-mode.el (ruby-parse-region): 正規表現内のエスケープ + に対応 + + * version 0.99-960716 + +Fri Jul 12 10:06:19 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * io.c (f_select): 引数のclose check. + + * ruby.c (load_file): #!行の引数チェックを第1引数に限定(実をいうと + DOS改行対策) + +Wed Jul 10 17:18:35 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99-960710 + + * time.c (time_s_timegm/time_s_timelocal): 時間を生成するメソッド + +Mon Jun 17 15:59:20 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99-960617 + + * parse.y (yyerror): エラー表示の簡略化. + +Wed Jun 12 14:11:01 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * signal.c (rb_trap_exit): trap 0はthreadを生成せずに処理する. + +Fri Jun 7 10:17:01 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * array.c/hash.c (indexes): 配列1引数のパターンを無くした.配列の + 場合は`*ary'を使ってもらおう. + + * eval.c (thread_wait_threads): main_threadが終了する前に他の + threadを待つ(強制的には終了させない). + (ruby_run): 他のthreadを待っている間にシグナルが来たら,全thread + を強制終了させる. + + * eval.c (rb_fail): メソッド名を`$!'に埋め込む. + + * eval.c (thread_create): main_threadのコンテクストがセーブされな + い場合があった. + + * process.c (f_sleep): 時間を指定せず,threadがひとつしかない状況 + にも対応. + + * eval.c (thread_create): create後,fnを呼び出す前にcontext switch + が起きると違うcontextでfnが実行されてしまうバグ. + +Mon Jun 3 08:03:17 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * struct.c (struct_s_def): メンバの指定を文字列,シンボル(FIXNUM) + 双方で可能にした. + + * ext/etc/etc.c (Init_etc): 構造体オブジェクトをGCから保護した. + + * error.c (rb_sys_fail): nil/FALSEを引数として受け付けるように. + +Thu May 30 16:19:08 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (thread_select): EINTRに対応. + +Wed May 29 11:04:51 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (f_catch): catch/throwを実装した. + +Tue May 28 13:30:52 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99-960528 + + * eval.c (thread_cleanup): main threadが終了すると他のthreadも終了 + することの明確化. + + * signal.c (trap): SIGINTのデフォルトの設定ミス(本当にSIG_DFLでは + まずかった).rubyではちゃんとハンドルしないと. + + * eval.c (thread_interrupt): SIGINTはmain_threadに例外を発生させる + ように. + +Mon May 27 15:13:31 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (thread_status): threadの状態を返すメソッド.threadの終了 + を待たない. + + * eval.c (thread_value): 一種のpromiseを実装するためのメソッド. + + * eval.c (thread_join): 待っているthreadが例外を起こした時には, + joinがその例外を発生するように. + + * eval.c (thread_create): threadでの例外をpropagateしないように. + +Fri May 24 10:47:53 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * enum.c (Init_Enumerable): `size' as alias to the `length' + + * eval.c (thread_save_context): `$@', `$!'をスレッド毎にセーブ. + + * eval.c (superclass): エラー表示をより親切に. + +Thu May 23 10:38:41 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.99-960523 + + * eval.c (superclass): エラー時にスーパークラス名を(分かれば)表示 + するように. + +Wed May 22 19:48:42 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (superclass): スーパークラスの指定子を`:'から`<'に変更. + +Tue May 21 09:27:59 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * lib/thread.rb: threadをサポートするクラス(Mutex, Queue). + +Mon May 20 09:39:49 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * time.c (time_cmp): 浮動小数点数も扱えるように. + (time_minus): Time - Timeが浮動小数点数を返すように. + +Fri May 17 15:40:10 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * process.c (rb_proc_exec): Thread対応時にexecの直前に + ITIMER_VIRTUALをリセットする. + +Tue May 14 02:12:44 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * signal.c (sighandle): SIGINTに対してデフォルトで例外を発生させる + のをやめ,status 130でexitするようにした. + + * eval.c (thread_schedule): Threadのバグはほとんどとれたようだ. + +Fri May 10 11:21:08 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (thread_schedule): ユーザレベルThread機能.効率はともかく + 移植性はある.今後,thread間の通信機能を実装する予定. + +Thu May 2 21:22:31 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * time.c (time_timeval): struct timevalを直接返すように(static変数 + を使わない). + +Wed May 1 17:27:32 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * process.c (f_sleep): 整数以外のtimeを指定できるように. + +Thu Apr 25 08:19:15 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * file.c (file_s_dirname): ファイル名が"/"を含まない時,"."を返す + ように(GNU dirnameの仕様). + + * file.c (file_s_basename): まだnilと0を混同しているソースが残って + いた. + + * parse.y (exprs): エラーリカバリを追加. + +Wed Apr 24 15:51:05 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * string.c (str_chop_bang): CRLFの場合2 bytesをchop!するように. + + * ext/socket/socket.c (tcp_svr_s_open): まだnilと0を混同しているソー + スが残っていた. + +Tue Apr 23 18:14:25 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * pack.c (pack_pack): "A/a"のバグ.余計なpaddingが入っていた. + +Thu Apr 18 13:02:11 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * configure.in: アーキテクチャ依存部を別ディレクトリにインストール + するように. + + * parse.y (yyerror): エラー発生時にエラー行とその位置を表示するよ + うに. + +Wed Apr 17 14:22:42 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * defines.h: SAFE_SIGHANDLEを無くし,危険な選択はできないように. + + * io.c (io_ungetc): 新機能. + + * ruby.c (load_file): ファイルからの読み込み方式が変わったのに対応. + + * parse.y (compile_file): ファイルからの入力を一度全部読み込むのを + 止めて,getsを使うことにした. + +Wed Apr 10 17:40:11 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.98 + +Tue Apr 9 09:54:30 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (iter_block): イテレータブロックの指定をメソッド呼び出し + に限定.文法の明確化. + + * eval.c (rb_eval): 条件式の正規表現の比較をinline化. + + * eval.c (rb_eval): defined? の 定義情報(種別)を文字列で返す. + + * node.h: NODE_BEGIN -> NODE_RESCUE, NODE_ENSUREに分離. + + * eval.c (rb_eval): option -n/-pのトップレベルループのinline展開. + + * parse.y (cond0): 条件式中の文字列は比較の対象としない + +Wed Mar 27 12:33:54 1996 Tairo Nomura <tairo@hucom.tp.titech.ac.jp> + + * defines.h: NeXT対応 + +Wed Mar 27 10:02:44 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y: 予約語の変更 continue -> next + +Mon Mar 25 07:34:37 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (parse_regx): o(once)オプションを追加. + +Fri Mar 22 14:25:35 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.97d + + * eval.c (dyna_var_defined): 動的ローカル変数の定義チェック用ルー + チン. + + * parse.y (gettable): eval()の中での動的ローカル変数(既に値を持っ + ているもの)の検出に失敗していた. + +Tue Mar 19 10:46:47 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.97c + + * re.c (reg_s_new): compile時にsegmentation fault. + + * parse.y (str_extend): いつもevalするように. + +Wed Mar 13 11:00:42 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (str_extend): 文字列中の式展開の不備を無くした. + + * parse.y: 下手なエラーリカバリを外した. + +Tue Mar 12 12:30:20 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (rescue): 間違ってensureでも例外を捕捉していた. + +Wed Mar 6 12:11:03 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (var_extend): 変数展開"#{}"で,任意の式を書けるようにし + た,これで「変数」展開では無くなっちゃったなあ. + + * regex.c (init_syntax_once): `_'をwordに追加. + + * regex.c (re_compile_pattern): `\w',`\W'の判定をsyntax tableを使 + うように. + +Tue Feb 27 10:15:32 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * object.c (obj_inspect): 表示するインスタンス変数が無い時には, + to_sを使う. + + * configure.in: dlnの検出を自動的に. + +Mon Feb 26 19:55:33 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ruby.c (readin): read(2)で一度にファイルが読み込めない場合に対応. + +Sat Feb 24 14:47:18 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.97b + +Fri Feb 23 11:26:02 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * class.c (rb_define_module): C言語で定義されたモジュールのPATHの + 設定忘れ.文字列化でcore dump. + + * eval.c (mod_include): 戻り値をnilに. + + * version 0.97a + +Thu Feb 22 21:03:42 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * array.c (ary_times): 「配列*文字列」がjoinと同じ働きをするように. + +Wed Feb 21 11:18:09 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * configure.in : fileCountをcache. + + * configure.in : LinuxでELF環境を自動的に検出できるよう. + +Tue Feb 20 11:18:09 1996 Mitsuhide Satou <mit-sato@aries.bekkoame.or.jp> + + * FreeBSD dynamic link対応. + +Fri Feb 16 08:50:01 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * object.c (obj_inspect): インスタンス変数を持たないオブジェクトも + 正しく表示されるように. + +Wed Feb 14 16:56:44 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (rb_eval): 条件式の`2..2'など左辺成立直後に右辺が成立する + パターンにバグ. + +Tue Feb 13 18:22:22 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.97 + +Fri Feb 9 21:32:55 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * lib/tkscrollbox.rb: スクロールでtclの設定を行い,ruby<->wishの不 + 要な通信を無くした. + +Wed Feb 7 10:26:52 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * string.c (str_aref): indexをunsigned intでとっていた. + + * string.c (str_aref): 範囲外のindexに対してnilを返す. + + * parse.y (special_local_set): `$_'が宣言無しに使われた場合に対応. + 関数をvariable.cから移動. + + * string.c (str_sub): 置換開始位置が間違っていた. + +Tue Feb 6 16:17:31 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * sample/ruby-mode.el (ruby-parse-region): コメントの読み飛ばしの + バグ. + +Fri Feb 2 18:35:28 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * variable.c (lastline_get): `$_'を`$~'と同じようにSCOPEローカルな + 変数にした. + +Thu Feb 1 14:14:07 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * file.c: statのcacheをやめた. + +Wed Jan 31 07:13:08 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (proc_s_new): procの中でyieldを呼ばれた時にcore dumpして + いた.とりあえず例外を発生させる. + + * variable.c (rb_class2path): singleton classに対応. + + * ext/etc/etc.c (Init_etc): struct_defineのターミネータがnilだった + (0でなければならない). + + * ext/marshal/marshal.c: TRUE/FALSEを吐き出せるように. + + * eval.c (rb_get_method_body): キャッシュのalias対応,いままでは + aliasはキャッシュに入っていなかった. + +Tue Jan 30 09:55:13 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (rb_eval): NODE_BLOCK - tail recursive(というほどでもない + が). + + * io.c (io_pipe): pipe(2)を実装した. + + * eval.c (rb_eval): Qselfをなくした.thread対応への第一歩.先は遠 + いが…. + + * eval.c (proc_call): procの中でのreturnはprocの終了を意味するよう + に.ただし,procからのyieldの中でのreturnは例外を発生する. + +Wed Jan 24 11:33:48 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.96a + + * dir.c (dir_each): `$_'の値を変更するのをやめた. + + * io.c (f_readlines): nilとFALSEの分離のあおりで無限ループに落ちて + いた. + + * ruby.c (ruby_options): $0の設定ミス. + +Tue Jan 23 15:28:21 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (rb_eval): ``は文字列を引数とするメソッド(`)呼び出しのシ + ンタックスシュガーであるとした. + + * ruby.c (addpath): `-I'オプションでディレクトリが「前に」追加され + るように変更. + +Fri Jan 19 11:23:12 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * dln.c (load_1): N_INDR対応(出来たような気がする). + +Thu Jan 18 18:14:20 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ruby.texi: FALSEとnilの分離を反映した. + +Tue Jan 16 17:39:23 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.96 - とりあえずnilとFALSEを区別する版 + +Wed Jan 10 15:31:48 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * re.c (reg_match): マッチしなかった時の戻り値はFALSE. + + * object.c (rb_equal): `0 == nil'がTRUEになるバグ. + +Tue Jan 9 00:44:58 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * nilとFALSEが分離可能に変更. + + * nilとFALSEと0の区別を厳密に. + + * struct.c (struct_new): 引数を0で終る必要が無くなった. + + * object.c (inspect_i): オブジェクトのチェックのバグ(Fixnumでcore + dumpしていた). + + * range.c (range_to_s): Rangeの表示を改善. + + * object.c (true_inspect): TRUEの表示を`TRUE'に. + +Mon Jan 8 15:02:33 1996 Yukihiro Matsumoto <matz@caelum.co.jp> + + * numeric.c (fix_mul): divide by zero errorが発生した(オーバーフロー + 検出のバグ) + + * texinfo.texをパッケージに含めた. + +Sun Dec 31 00:08:49 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (rb_eval): `::'では,そのクラスで定義された定数を参照する + ように変更. + + * string.c (Init_String): eachをeach_lineに戻した. + +Thu Dec 28 12:31:55 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (rb_eval): caseの演算子を`=~'から`==='に. + + * variable.c (rb_const_set): クラス定数の再定義を許す(同じクラスで + は不可).警告は出す. + +Wed Dec 27 13:27:52 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.95c + + * ext/tkutil/tkutil.c: wishがあってもなくても一応コンパイルだけは + するように. + + * lib/tk.rb: 環境変数PATHから{wish|wish4.0}を探すように. + +Tue Dec 26 01:03:42 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * sample/ruby-mode.el (ruby-parse-region): 正規表現の検出強化. + + * numeric.c (fix_mul): 乗算のオーバーフロー検出アルゴリズムのバグ. + + * ext/extmk.rb.in: ./install-shを使う場合のPATHを調整. + + * Makefile.in (install): lib/*.rbを一つずつインストール. + + * io.c (io_each_line): イテレータの戻り値をnilで統一. + +Fri Dec 22 10:34:32 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.95b + + * variable.c (f_untrace_var): 第2引数を指定すると特定のtraceを削除 + できるように. + + * variable.c (f_trace_var): 第2引数がnilの時,traceを削除する. + + * lib/tk.rb (file_readable/file_writable): 第2引数をnilにすること + によるevent handlerの削除. + + * parse.y (variable): ドキュメントに`__FILE__'と`__LINE__'が残って + いた.`caller(0)'で代用したはずだったのに. + + * eval.c (f_eval): $!のリセット. + + * error.c (err_sprintf): 勝手に"\n"を付加するのを止めた. + + * parse.y (f_arglist): 引数リスト直後のif/whileの読み間違い. + lex_stateの値が設定されていなかった. + +Thu Dec 21 00:56:57 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.95a - ^^;;; + + * lib/tkscrollbox.rb: パッケージに入ってなかった. + + * configure.in: FILE structureのチェックにバグ. + + * Makefile.in (clean): ext以下をinstallしていた. + + * ext/socket/extconf.rb: Solarisにおける-lnlsのチェック. + + * array.c (beg_len): バグがあった….悲しい. + + * version 0.95 - fj.sourcesに + + * eval.c (rb_eval): rescueのロジックをrb_rescue()に一元化. + +Wed Dec 20 19:30:58 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * Makefile.in: 不要なコンパイルの回避(より完全に). + + * class.c (singleton_class_new): `single'->`singleton' + +Tue Dec 19 07:14:33 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * io.c (closed?): IOがcloseされているかどうかを知る述語. + + * parse.y (primary): 特異メソッドの引数のlex_stateが不適切. + + * lib/tk.rb: tcl->rubyの変換関数の用意. + + * ext/extmk.rb.in (install): installの2重コンパイルの回避. + + * array.c (range_beg_len): range指定の不適切なエラーを訂正. + + * string.c (str_aref): range指定のバグを削除. + + * lib/tk.rb (tk_split_list): Tclのリストに対応. + +Mon Dec 18 09:58:12 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.94 + + * dln.c (dln_load): HP対応(未確認) + + * eval.c (Init_Proc): BlockをProcに改名. + +Sat Dec 16 13:46:14 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (rb_eval): retryでイテレータの再実行ができるように. + +Fri Dec 15 17:14:30 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c: proc:lambdaの親しみやすい別名 + +Thu Dec 14 17:21:55 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (dyna_var_asgn): イテレータブロック内で最初に初期化された + ローカル変数の有効範囲をそのブロック内に限定.これでlambdaと呼べ + ないことはない. + +Wed Dec 13 02:30:58 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * gc.c: autoloadのマークミス. + + * lib/tk.rb: wishからの複数行の戻り値に対応 + + * lib/tkcomposite.rb: 複合widget + + * variable.c (rb_class2path): ICLASSに対応してなかった. + + * eval.c (ruby_run): exit(0)のバグ + +Sat Dec 9 01:21:24 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ext/marshal/marshal.c (dumps|load): 文字列に対する入出力を可能に + した(ただし実はファイル経由なのだ). + +Fri Dec 8 18:29:11 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ext/marshal/marshal.c: シンボルを一度だけ初期化する. + +Thu Dec 7 07:58:50 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (yylex): 第1引数の正規表現の認識にエラーがあった.同時に + 状態数を減らした. + + * string.c (str_sub): 置換でスキップ幅が大きすぎた. + +Wed Dec 6 15:14:23 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * string.c (str_sub_method): sub/gsub(!なし)は置換が行なわれなかっ + た時,置換前の文字列を返す. + +Tue Dec 5 00:55:15 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (yylex): 括弧を省略した時の引数展開の`*'に対応. + + * eval.c (ruby_run): EXITハンドラ内での例外に対応. + + * bignum.c (big_cmp): BignumとFixnumの比較で落ちる. + +Mon Dec 4 14:21:18 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (call_op): コンパイル時の定数式の展開をやめた.労多くし + て益少ないと判断したので. + +Thu Nov 30 01:35:15 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * lib/tk.rb: {Radio,Check}Buttonのvariableの実装. + + * eval.c (rb_yield_0): Block.callがネストした時のバグ. + + * io.c (f_select): 常に配列3つをふくむ配列を返すように + + * lib/tk.rb: fileeventをruby側で実装. + +Wed Nov 29 17:53:23 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * variable.c (rb_ivar_get): selfを常に指定するように. + +Tue Nov 14 00:07:29 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * lib/tk.rb: Tk4.0対応 + +Mon Nov 13 16:23:32 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.93 + +Thu Nov 9 23:26:01 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * gc.c (gc_mark): モジュールのMixinのマーク忘れ. + + * parse.y (f_arglist): メソッド定義の引数を括弧で括らなくても良い + ようにした. + +Wed Nov 8 00:17:51 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (rb_yield_0): 未初期化のローカル変数があった. + + * eval.c (rb_eval): pendig signalのチェックをeval実行後に行うよう + にした.でないとシグナルの発生と検出が遠く離れてしまう事がある. + + * parse.y: class文のsuperclass部を定数から式に拡張した. + + * lib/tk.rb: Tkのほぼ全ウィンドウクラスに対応.キャンバスとテキス + ト上のオブジェクトが残っている. + +Tue Nov 7 08:18:37 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * signal.c (trap): ブロックを指定できるように. + +Mon Nov 6 16:44:00 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (f_caller): 呼出元の情報を得る. + + * ext/tkutil/tkutil.c: wishのstderr出力を監視することで,エラー処 + 理を行う. + + * ext/tkutil/tkutil.c: wishとの通信部をCで記述. + +Sat Nov 4 01:12:59 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * sample/ruby-mode.el (ruby-calculate-indent): インデントの計算を + もう少しスマートにした(正規表現のチェック,継続行のチェック). + + * eval.c (rb_call): 無限再帰を避けるため,関数のネストレベルの制限 + を行なう. + + * lib/tk.rb: Tkインターフェース.まだ不完全だが. + + * eval.c (rb_yield_0): 空のBlockのバグ. + + * sample/ruby-mode.el (ruby-calculate-indent): 行末の演算子による + 行継続に対応. + +Fri Nov 3 12:56:21 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (rb_call): 本体が空の関数の実行にバグ. + + * parse.y (var_extend): 文字列の末尾の変数展開のバグ. + + * variable.c (rb_gvar_set): traceの評価時にに変数値を与えるように. + + * eval.c (f_require): ruby scriptのrequireにbug. + + * variable.c (rb_const_get): モジュールのinclude対策. + +Thu Oct 19 13:56:06 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * dln.c (dln_load): HP対応でのtypo. + +Wed Oct 18 17:39:39 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.92 + + * object.c (krn_type): オブジェクトの動的な型を返すメソッド. + +Tue Oct 17 00:48:18 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ruby.c (proc_options): -X オプション.chdirだけを行う. + + * re.c (reg_search): 漢字コードを途中で変更できるように.コンパイ + ル時のコードが変更された時にはマッチの直前に正規表現の再コンパイ + ルを行う.定数KCODEから変数$KCODEへ. + + * parse.y: ()のなかにcompexprを許す. + + * re.c (reg_search): メモリリークを直した. + +Fri Oct 13 13:19:19 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * string.c (str_sub): 文字列置換にバグ. + + * string.c (str_strip_bang): 文字列の後ろの長さの調整が行われてい + なかった. + + * re.c (reg_search): $&, $1...はローカルに束縛するようになった.呼 + び出したメソッドでのマッチは現スコープの$&などの値に影響しない. + マッチの情報をスコープ外で得たいときには$~を使って束縛情報を持ち + 出す必要がある. + +Thu Oct 12 00:33:33 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * re.c (reg_search): String:split, String:indexでは$&, $1...が変化 + しないようにした. + + * io.c (rb_str_setter): setterの仕様が変更になっていた. + + * variable.c (f_trace_var): 第2引数を省略してイテレータとして呼べ + るように. + +Wed Oct 11 11:50:59 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.91 + + * variable.c (var_setter): 引数が間違っていた.致命的バグ. + + * io.c (pipe_open): $stderrの値が変更されている時にはそちらを + 子プロセスのstderrに設定する. + +Mon Oct 9 13:06:33 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * object.c (mod_to_s): モジュール内のモジュールは`::'を使った表現 + で表示されるように. + + * variable.c (rb_gvar_set): 代入によるループが発生しないように, + trace内での代入ではtraceを評価しない. + + * struct.c (struct_equal): structのequal判定にクラスの一致を含めた. + +Sat Oct 7 00:18:32 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (rb_eval): defined?の機能を拡張(yieldのチェック,superの + 存在など). + +Fri Oct 6 12:06:47 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.90 + + * st.c (st_foreach): 要素を削除した時に要素数が変化していなかった. + + * hash.c (hash_values): バグ修正.keysを返していた…. + + * parse.y (call_op): defined? の引数では定数の畳み込みを行わない + (チェックする前にコンパイルエラーになっては困る). + + * スコープ生成の一部見直し. + +Thu Oct 5 00:29:43 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * 関数とクラスの命名規則を変更した.関数名,変数名の全面書き換え. + + * gc.c (looks_pointerp): ヒープチェックの高速化. + + * struct.c (Fstruct_aset): 構造体に対する`[]='. + (struct_set): 構造体メンバに対する代入. + +Wed Oct 4 09:54:07 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.89 + + * eval.c (Frequire): ダイナミックロードのエラーチェックを厳しく. + + * struct.c: structの構造を完全に書き換えた.以前は順序付きの + id->valueの連想配列であったが,今度は構造体毎に新しいクラスを生 + 成するようにした. + + * parse.y: `::'の意味をAssocの生成からクラス(モジュール)内の定数ア + クセスへ変更. + + * assoc.c: なくす. + +Tue Oct 3 13:31:08 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * variable.c (Ftrace_var): trace_var, 大域変数への書き込みhookを設 + 定する. + + * variable.c: global_entryの構成を書き換えた.これでtrace_varを実 + 装できる. + + * file.c (Ffile_stat): "&"で直前のfstatの結果も参照できるように. + +Fri Sep 29 14:15:13 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.88 + + * dln.c (dln_load): AIXとHPに対応したコードを入れた(動作は未確認). + + * ext/extmk.rb.in: 必要に応じて,定数EXTLIBを定義するように. + + * dln.c (dln_load): dln独立に書き換える.将来の拡張用. + (load_1): dln_a_outにおいてソースコードでライブラリを明示的にロー + ドする必要がないように変更した. + +Thu Sep 28 13:31:37 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * sample/ruby-mode.el: もっとましなhilit19対応(正規表現). + +Wed Sep 27 04:12:44 1995 Takahasi Mamoru <taka@soum.co.jp> + + * sample/test.rb: echoで-nを使わないように(SysV対策). + + * ext/extmk.rb.in: sub -> sub! + +Tue Sep 26 19:12:42 1995 Yasuo OHBA <jammy@csg.mes.co.jp> + + * dln.c (dln_find_1): `.', `..'から始まるパスに対応した. + +Mon Sep 25 12:33:03 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.87 + +Sat Sep 23 10:00:18 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (Fmod_modfunc): メソッドをprivateにし,同時に特異メソッド + も定義するメソッド.パッケージ的使い方のモジュール用. + +Fri Sep 22 11:02:44 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * lib/find.rb: findを提供するライブラリ + + * variable.c (rb_define_variable): hookの設定を分離. + (add_hook): 1変数に対して複数のhookを設定できるように. + +Thu Sep 21 00:22:11 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * string.c (Fstr_frozen): 文字列が更新不可かどうかをチェックする述 + 語メソッド. + + * hash.c (Fhash_aset): keyが文字列の時,キーの内容が変化しないよう + に,dupしてfreezeする. + +Wed Sep 20 16:12:44 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.86 + + * ext/extmk.rb.in (have_header): キャッシュにバグ. + + * ext/extmk.rb.in (have_library): 引数の順序が変わった. + +Thu Sep 14 18:00:59 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * object.c (obj_is_instance_of): is_member_ofから名称変更. + + Wed Sep 13 15:44:35 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * string.c (Fstr_tr_bang): 範囲外の文字に対する変換バグ. + +Tue Sep 12 14:27:58 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * file.c (Sfile_expand_path): expand_file_name -> expand_pathに名 + 称変更. + + * enum.c (Fenum_member): includes? -> member? に名称変更. + + * string.c (Fstr_each_byte): StringはByteArrayであるという基本に戻っ + て,eachの定義をeach_byteに変更した.今までのeachはeach_lineでア + クセスできる. + +Mon Sep 11 18:31:17 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * file.c (cache_stat): ファイル名として"&"を指定すると直前の + stat(2)の結果を再利用するように. + +Fri Sep 8 14:18:51 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ruby.texi: `!', `?'に対応してアップデート. + + * parse.y: defined -> defined? + + * file.c: FileOpの一文字メソッドをなくす.一文字テストはtestメソッ + ドにまかせる. + + * parse.y (yylex): 変数名の後ろに`?'も許す.述語メソッドの後ろに + `?'を追加する. + +Thu Sep 7 20:01:33 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * string.c: 文字列の中身を更新するメソッドの名前の終りに`!'を付加. + `!'の無いバージョンも用意した. + + * parse.y: 変数名の後ろに`!'を許す. + +Wed Sep 6 14:12:19 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.85 + + * string.c (Fstr_dup): 文字列の複製を作る + (Fstr_freeze): 文字列の更新不可属性を設定できるように. + (Fsub/Fgsub): $_の内容をdupしてから置換を行うように. + + * ruby.h (CLONESETUP): flagsの状態もコピー + +Tue Sep 5 01:27:50 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * sample/test.rb: 失敗の検出を厳しく. + +Fri Aug 25 14:31:02 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * process.c (Ffork): イテレータとしても動作するように. + + * version 0.84 + + * signal.c (sig_beg): ハンドラが設定されている時には再設定しない. + + * ext/extmk.rb.in (create_makefile): shared objectのリンクの際に + `-l'オプションを指定するように. + + * signal.c (trap): `EXIT'で終了処理を行う設定が出来る. + +Wed Aug 16 00:13:22 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * signal.c (sig_beg): デフォルトではbegin節の中でだけSIGINTを捕捉 + するように変更. + + * io.c (io_ctl): fcntlを持たないシステムにも対応. + + * 各ディレクトリに分散していたMANIFESTをまとめた.拡張モジュール毎 + には必要. + + * string.c (Sstr_new,str_sub,Fstr_crypt): 引数を自動的に文字列に変 + 換するように. + +Sat Aug 12 00:44:02 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * string.c (Fstr_crypt): PD cryptを用意した. + +Fri Aug 11 14:37:03 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * assoc.c (Fassoc_clone): assocもcloneできるように. + + * io.c: マクロREAD_DATA_PENDINGの定義を変更(Linux対応) + + * io.c (io_fptr_finalize): fptrの開放時の処理を指定できるように. + +Wed Aug 9 16:52:41 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * eval.c (rb_provided): 複数のfeatureをロードすると無限ループに落 + ちるという単純な(しかし凶悪な)ミス. + + * ext/extmk.rb.in (install): dlopen対応を行った.今までdlnにしか十 + 分に対応していなかった. + +Tue Aug 8 14:17:06 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.83 + +Mon Aug 7 12:47:41 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y: resque -> rescue.恥ずかしいがtypoを残しておくわけには + いかないよなあ.なんで今まで気がつかなかったのか…. + +Thu Aug 3 18:18:05 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * missing/nt.c: NT移植用の関数群をまとめた. + + * variable.c (rb_const_get): また例外を発生するようにした.defined + がある以上例外を発生させない理由がないので(例外が発生した方がタ + イプミスの検出などの点で有利). + + * variable.c (Fautoload): autoloadを実装.今度は使えるか. + +Mon Jul 31 15:44:21 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (arg_ambiguous): 第1引数のあいまいさを警告(-vオプション + で有効). + + * eval.c (rb_eval): `-v'オプションをつけて`def'が呼ばれると不必要 + なエラーメッセージが出た. + + * parse.y (yylex): メソッドの第1引数の判定をもうちょっと賢くした. + +Fri Jul 28 19:04:43 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (yylex): `+/-/['の直前に空白が来るかどうかで動作を変更し + た(混乱のもとか?) + +Wed Jul 26 09:21:23 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.82a + + * sprintf.c (Fsprintf): `%s'で'\0'を含む文字列に対応. + + * pack.c (Fpck_pack): packの要素確保のバグ. + + * eval.c (Floop): 無限ループのイテレータ. + + * io.c (next_argv): 存在しないファイル名が指定された時のエラー処理 + が行われていなかった. + +Mon Jul 24 17:37:34 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.82 + + * ext/extmk.rb.in (install): 拡張モジュールをstatic linkする場合は + そのモジュールが既にrequireされたのと同じようにfeatureを設定する. + これで拡張モジュールの機能が必要な時には(static linkされているか + どうかにかかわらず)requireすればよくなる. + + * eval.c (Frequire): `$"'に格納する文字列をフルパスでなくフィーチャ + 名とする.rubyスクリプトをロードした時には`.rb',オブジェクトを + ロードした時には`.o'をフィーチャ名に付加する.lispのrequireと + provideの働きに(少し)近い. + +Thu Jul 20 12:50:05 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * Makefile.in (test): make testができるように. + + * struct.c (struct_new): typo. + + * eval.c (rb_eval): `defined'を追加.メソッド/変数/定数の定義状態 + を知る事が出来る. + +Wed Jul 19 18:04:01 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.81 + +Mon Jul 17 14:53:51 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * variable.c (rb_const_get): 未初期化のCONSTANTの値をnilにした.し + かし,今後また例外に戻す可能性はある.要はoptionalなクラス/モジュー + ルが存在するかチェックしたいだけなんだな. + + * st.c (int): grow_factorを固定にした(大嶋さんのマシンに対応). + +Fri Jul 14 00:48:40 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * ext/extmk.rb.in: キャッシュのバグを修正. + + * parse.y (var_extend): #{$数字}に対応した. + + * dln.c (dln_load_1): `Init_FILENAME'だけを有効に.`init_*'は今後 + 実行しない. + + * ext/etc/etc.c : Etcモジュールを拡張モジュールとして分離.実はNT + 対応への布石だったりするかもしれない. + +Tue Jul 11 17:12:48 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * gcc -Wallで出たwarningを元にソースを変更. + + * signal.c (trap): typo. + +Fri Jul 7 10:08:51 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.80 + + * ruby.texi: texinfo documentを提供.specとruby.1は無くなった. + + * signal.c (Ftrap): 割込み禁止中の例外発生に対応. + + * eval.c (Flambda): Blockオブジェクトを返す.Block.newと同義. + +Thu Jul 6 00:35:03 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * signal.c (Ftrap): SIG_DFLの処理を変更.SIGINTへのデフォルトハン + ドラを用意(例外を発生する). + + * file.c (Sfile_expand_fname): パス名を絶対パスに展開するメソッド. + (Sfile_basename): basenameを得るメソッド.拡張子も外せる. + (Sfile_dirname): basenameの反対. + + * eval.c (rb_call): argument評価中の例外発生に対応. + + * file.c (Ftest): `M', `A', `C'を追加. + +Tue Jul 4 12:36:33 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * file.c (Ftest): ファイルテスト用メソッド. + + * ruby.c (proc_options): `-r'オプションを追加. + + * parse.y (f_args): デフォルト引数を追加. + + * eval.c (rb_call): 該当する引数が無い時,rest引数の値をnilに. + + * numeric.c (num_equal): 数値以外との比較で例外が発生していた. + FALSEを返すように. + + * eval.c (masign): 多重代入のrest部の動作がおかしかった. + +Sat Jun 17 01:03:16 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * parse.y (gettable): 未初期化のローカル変数の参照(独立した識別子) + は正式にメソッド呼び出しとした. + + * parse.y (read_escape): tokenbufを使わないように修正.それにとも + ない,`\C-x',`\M-x'などのエスケープ表現を復活.これでドキュメン + トと実際の処理系が一致した. + +Thu Jun 15 15:42:00 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * re.c (re_regcomp): cacheのチェックを改善. + +Mon Jun 12 18:50:51 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * version 0.79 + +Sat Jun 10 00:25:01 1995 Yukihiro Matsumoto <matz@caelum.co.jp> + + * re.c (re_regcomp): cache判定に`$='の値も反映させた. + + * sample/test.rb: test suite作成. + +Fri Jun 9 15:58:34 1995 Yukihiro Matsumoto <matz@ix-02> + + * re.c (re_regcomp): cacheの判定が間違っていた. + +Fri Jun 9 00:01:35 1995 Yukihiro Matsumoto (matz@dyna) + + * eval.c (rb_yield): block構造体に初期化していないメンバ(iter)があっ + たのでイテレータのネストが正しく動作しなかった. + +Thu Jun 8 00:59:03 1995 Yukihiro Matsumoto (matz@dyna) + + * re.c (=~): String以外との比較がFALSEを返すように(例外を発生して + いた). + + * extmk.rb.in: 判定した値をファイルにキャッシュするようにした. + + * assoc.c (to_a): to_aメソッドが再定義されていなかった. + + * eval.c (rb_eval): 初期化されていないローカル変数へのアクセスを引 + 数の無いメソッド呼び出しと解釈する.ただし,(現状では)メソッドが + 定義されていない場合,エラーにせず変数未初期化のwaringを出して + nilを返している.「ruby -pe print」などが実行できるという意味で + はありがたいこの仕様は,しかし今後の検討が必要である.-- メソッ + ド呼び出しとするのを止めるか(以前の仕様),いつもメソッド呼び出し + とする(未定義ならばエラー)か,今の仕様で行くか. + + * eval.c (rb_eval): 初期化されていないローカル変数へのアクセスで + (evalなどで)初期化された事が分かった時には以後初期化されたローカ + ル変数とみなす. + +Wed Jun 7 11:58:12 1995 Yukihiro Matsumoto <matz@ix-02> + + * eval.c (rb_fail): 例外処理後も`$!'をクリアしないように. + (rb_fail): `$!'変数に最後に改行を追加しない. + + * io.c (Fprint): privateメソッドに変更.引数を取らない時の動作を変 + 更(`$_'を出力する). + (Fio_print): 出力先指定のprintメソッド. + (Fio_printf): 出力先指定のprintfメソッド. + + * parse.y: not演算子の追加.優先順位の低い`!'演算子. + +Mon Jun 5 19:00:55 1995 Yukihiro Matsumoto <matz@ix-02> + + * version 0.78 + +Fri Jun 2 17:52:03 1995 Yukihiro Matsumoto <matz@ix-02> + + * ruby.c (proc_options): -Iオプションで`$:'への追加される順番を修 + 正した. + +Fri Jun 2 00:36:34 1995 Yukihiro Matsumoto (matz@dyna) + + * parse.y: while修飾子の動作を通常のwhileと同じにした.ただし, + begin式へのwhile修飾子だけはdo..while型のループとなる. + +Wed May 31 18:36:30 1995 Yukihiro Matsumoto <matz@ix-02> + + * version 0.77 + +Mon May 29 18:39:37 1995 Yukihiro Matsumoto <matz@ix-02> + + * ext/extmk.rb.in (install): 拡張モジュールもインストールできるよ + うに. + +Fri May 26 14:43:01 1995 Yukihiro Matsumoto <matz@ix-02> + + * process.c (Fsystem): 戻り値をサブプロセスの失敗/成功を表す真偽値 + にした.終了ステータスは`$?'で得る. + +Tue May 23 10:58:11 1995 Yukihiro Matsumoto <matz@ix-02> + + * string.c (Fstr_upto): 無限ループに陥らないように. + + * parse.y (cond): `||'などの右辺に制御式が書けるように,条件式がか + ならずしも値を持たなくても良いようにした. + + * ext/marshal/marshal.c: オブジェクトの読み書きをメソッドの再定義 + でコントロールできるように.インスタンスが`_dump_to'というメソッ + ドを定義している時はそちらを使うように. + + * ext/extmk.rb.in: static linkも設定できるような仕様にした. + ext/Setupというファイルにディレクトリ名を記述するとそのディレク + トリに存在するモジュールはstatic linkされる(はず). + + * eval.c (rb_eval): `..'を文法に組み込み,`..'と`...'の動作をperl + に合わせた. + +Sat May 20 01:22:48 1995 Yukihiro Matsumoto (matz@dyna) + + * io.c (select): timeout時と割込み時の動作の明確化. + +Fri May 19 15:33:23 1995 Yukihiro Matsumoto <matz@ix-02> + + * version 0.76 + +Fri May 19 00:48:08 1995 Yukihiro Matsumoto (matz@dyna) + + * string.c (Fstr_each): イテレータブロック中で文字列の変更が行われ + たかどうかをチェック.ポインタの値が変わっていれば例外を発生する. + + * ruby-mode.el: ruby-electric-braceの新設. + +Thu May 18 12:27:23 1995 Yukihiro Matsumoto <matz@ix-02> + + * string.c (Fstr_tr): trの置換対象に`\0'を含む時に正しく置換を行わ + ないバグがあった.更に置換文字列をASCII順に指定しないと動作しな + い問題もあった.結果としてtrを書き換えたので,copyrightの問題は + 無くなった(と思う). + + * gc.c (gc): the_scopeをマークしていなかったので,ローカル変数の指 + しているオブジェクトが間違って開放される場合があった. + + * gc.c (mark_locations_array): 若干の高速化. + +Mon May 15 11:43:49 1995 Yukihiro Matsumoto <matz@ix-02> + + * ext/extmk.rb.in: Dynamic Loadモジュールのコンパイル用チェックを + 行うruby script.autoconfに近い感覚で使える.新しいモジュールを + 提供したい人はextの下にディレクトリを作るだけで良い.必須のファ + イルはファイル名の一覧を記録した`MANIFEST'というファイルのみ.必 + 要に応じて`depend'(ファイルの依存関係を記述するファイル gcc -MM + の出力),`extconf.rb'(コンパイル用にライブラリと関数の存在チェッ + クするファイル)を用意できる. + + * eval.c (rb_call): rubyメソッドの引数チェック時に未初期化の + jmp_bufを使用していた. + + * parse.y: `or'と`and'の優先順位を同じにした. + +Wed May 3 18:21:36 1995 Yukihiro Matsumoto (matz@dyna) + + * dln.c: Linuxでは`__.SYMDEF/'であった. + + * dln.c: system callのエラーチェックを忘れていた. + +Wed Apr 26 09:50:56 1995 Yukihiro Matsumoto (matz@ix-02) + + * parse.y: イテレータブロックの変数宣言を`|'で括るようにした.これ + でイテレータ変数がない時は宣言そのものを省略できる.文法の変更は + 久しぶりだ. + +Tue Apr 25 12:04:17 1995 Yukihiro Matsumoto (matz@ix-02) + + * eval.c(require): loadからダイナミックロードの機能を移してきた. + さらに拡張子の補完機能を追加してユーザがdln/dlopenの差を意識する + 必要のないようにした. + + * string.c(sub,sub): イテレータとしても動作するように. + + * object.c: init_object -> initialize. + +Mon Apr 24 14:22:39 1995 Yukihiro Matsumoto (matz@ix-02) + + * NEWS-OS 3.4対応 + + * io.c: Solarisのstdioの動作が違うようだ.signalでEOFを返してしま + う….perlでも同様の問題がある. + +Fri Apr 21 20:04:39 1995 Yukihiro Matsumoto (matz@ix-02) + + * version 0.75 + + * signal.c: trapがなくなっていた.うーむ. + + * configure: Solaris 2.3対応. + + * io.c: #elifのないcppもある. + + * dir.c: autoconf 2.xへの対応が不十分 + +Thu Apr 20 12:31:24 1995 Yukihiro Matsumoto (matz@ix-02) + + * version 0.74 + + * env.h, gc.c, regex.c: IRIXへの移植対応 + + * configure: dlopen用にpicを生成するoptionの検出のため,システムタ + イプをチェックするように. + +Tue Apr 18 19:08:17 1995 Yukihiro Matsumoto (matz@ix-02) + + * gc.c(xrealloc): ptr=nilの時,malloc()と同じ働きを + + * array.c(astore): 空の配列の0番目の要素に代入するとsize=0で + realloc()を呼んでいた. + + * configure, glob.c: Solaris 2.xでコンパイルできるように + +Mon Apr 10 18:36:06 1995 Yukihiro Matsumoto (matz@ix-02) + + * version 0.73 + +Fri Apr 7 13:51:08 1995 Yukihiro Matsumoto (matz@ix-02) + + * cons.c->assoc.c: consの余計な機能は外してpairとしての機能だけを + 残した.Enumerableをincludeするのもやめた. + + * string.c(esub): 文字列置換イテレータ.perlのs///eの相当する. + +Wed Apr 5 11:35:21 1995 Yukihiro Matsumoto (matz@ix-02) + + * version 0.72 + + * EWS4800対応 + + * file.c: utimesがない時はutimeを使うように. + +Mon Apr 3 15:19:41 1995 Yukihiro Matsumoto (matz@ix-02) + + * version 0.71 + + * regexp.c(re_match): バグがあった.match_2を削除した時にenbugして + いたのだった. + +Mon Mar 27 15:41:43 1995 Yukihiro Matsumoto (matz@ix-02) + + * dict.c: Dict->Hashに全面的に移行. + +Thu Mar 23 20:30:00 1995 Yukihiro Matsumoto (matz@ix-02) + + * dbm.c,socket.c: extディレクトリに分離. + + * configure: dln周りのチェックの強化 + + * dln.c: initの呼び出しをdlopen()版に合わせた. + +Mon Mar 20 17:45:08 1995 Yukihiro Matsumoto (matz@ix-02) + + * configure: autoconf 2.2に対応(一部). + +Fri Mar 17 15:56:44 1995 Yukihiro Matsumoto (matz@ix-02) + + * dln.c: dlopenのあるマシンではそちらを使うように.ただし,ちゃん + と動いているかどうかは自信がない. + + * regex.c: virtual concatinationをやめた. + +Thu Mar 16 11:32:57 1995 Yukihiro Matsumoto (matz@ix-02) + + * version 0.70 + + * eval.c,regex.c: gccでのコンパイルエラー. + + * io.c: inplace-editで拡張子が指定されない場合,もとのファイルを削 + 除する. + +Wed Mar 15 14:59:18 1995 Yukihiro Matsumoto (matz@ix-02) + + * version 0.69 + + * eval.c(method_missing): unknownから名称変更. + + * eval.c(single_method_added): 特異メソッドが定義された時に呼ばれ + るメソッド.hookとして使える.実際に定義される直前に呼ばれる. + +Tue Mar 14 14:46:44 1995 Yukihiro Matsumoto (matz@ix-02) + + * ruby.c(proc_options): 引数の解析を自分でやることにより引数指定の + 方法がperlに近付いた.getopt_longはもう使わない. + + * dir.c(glob): `{}'のネストを許すようにした. + +Mon Mar 13 17:56:25 1995 Yukihiro Matsumoto (matz@ix-02) + + * glob.c: Glob(ワイルドカードオブジェクト)はなくなった.ワイルドカー + ドの展開はDir.glob(文字列)を使う.ワイルドカードのマッチは正規表 + 現で代用. + +Fri Mar 10 18:35:46 1995 Yukihiro Matsumoto (matz@ix-02) + + * eval.c: Mathのようなモジュールは自分自身でextendする. + + * eval.c: クラスやモジュールを定義する時,既に同名のものがあれば追 + 加定義となるように.ただし.superクラスの違いなどはチェックする. + + * regex.c: debug. + + * math.c: 定数PIとEを定義. + +Thu Mar 9 21:35:12 1995 Yukihiro Matsumoto (matz@ix-02) + + * regex.c: EUC,SJISモードでは0x80以上の8進,16進リテラルを禁止. + + * regex.c: クラス内でも数値リテラル・文字クラスが使えるようした. + +Wed Mar 8 17:39:05 1995 Yukihiro Matsumoto (matz@ix-02) + + * regex.c: \200など括弧の数以上の表現は8進リテラルと解釈する.ただ + し,\1から\9までは例外. + + * regex.c: \9以上のリファレンスも有効にした. + +Tue Mar 7 14:26:01 1995 Yukihiro Matsumoto (matz@ix-02) + + * eval.c(public/private): スコープ制御メソッドの名称変更.静的なア + クセスも出来るようにしてみたが,不採用. + +Mon Mar 6 19:34:32 1995 Yukihiro Matsumoto (matz@ix-02) + + * eval.c(inlcude): メソッド化.動的にモジュールをインクルードでき + るように.さらに任意のオブジェクトにもモジュールをインクルードで + きるメソッド `extend'も用意した. + + * parse.y: 文法からincludeを削除.メソッド化. + +Tue Feb 28 15:35:10 1995 Yukihiro Matsumoto (matz@ix-02) + + * parse.y: 配列,連想配列の最後に`,'をおけるように. + +Fri Feb 24 13:15:43 1995 Yukihiro Matsumoto (matz@ix-02) + + * version 0.68 + +Thu Feb 23 11:19:19 1995 Yukihiro Matsumoto (matz@ix-02) + + * eval.c: resque節のselfの値が間違っていた. + + * eval.c(rb_clear_cache): キャッシュのクリアし忘れがあった. + + * eval.c: 定数のスコープをクラス内の静的スコープに変更した.これに + よって,特異メソッドから参照される定数は,レシーバのクラスではな + く,定義されたスコープのクラスの定数となる. + +Wed Feb 22 00:51:38 1995 Yukihiro Matsumoto (matz@dyna) + + * regex.c: ignorecaseを正規表現のコンパイル前に指定しないと正しく + 動作しない.修正. + + * string.c(toupper,tolower): bug fix. + + * ENV,VERSION: readonly変数から定数へ. + +Tue Feb 21 18:56:56 1995 Yukihiro Matsumoto (matz@ix-02) + + * io.c(STDIN, STDOUT, STDERR): 定数として定義. + + * io.c(select): bug fix. + + * version 0.67 + +Mon Feb 20 16:10:14 1995 Yukihiro Matsumoto (matz@ix-02) + + * parse.y(yylex): 定数を`%識別子'から,第1文字が大文字の識別子に変 + 更.それにともないクラスは定数となった. + + * eval.c: クラス定義内のselfがクラス定義外部のthe_classだった. + + * variable.c(rb_name_class): クラス名をインスタンス変数に格納する. + +Thu Feb 16 15:36:17 1995 Yukihiro Matsumoto (matz@ix-02) + + * parse.y: BLOCKをbraceで表現する文法に変更したものを作ってみる. + MLに提示してみるが反応がない. + + * object.c(do,forever): なくした. + +Wed Feb 15 13:20:49 1995 Yukihiro Matsumoto (matz@ix-02) + + * re.c(new): 第2引数が与えられて,かつnilでないときだけ設定するよ + うに(以前はnilの時にも設定を行なっていた). + + * parse.y(parse_regexp): 正規表現リテラルで大文字小文字を無視する + かどうか指定できるように. + +Tue Feb 14 00:55:33 1995 Yukihiro Matsumoto (matz@dyna) + + * parse.y: (compexpr) -> (expr). + +Fri Feb 10 16:30:00 1995 Yukihiro Matsumoto (matz@ix-02) + + * ruby.c(load_file): scriptを読み込む時だけ"#!"の解析を行うように. + + * ruby.c(readin): ファイル読み込み時に先頭に"#!"があり,その行が + "ruby"という文字列を含む時,rubyに引数が与えられていれば,その引 + 数も有効になる. + + * parse.y(yylex): コメント行の終りが`\'であった時,次の行に継続し + ているとみなすようにした. + +Thu Feb 9 16:18:37 1995 Yukihiro Matsumoto (matz@ix-02) + + * version 0.66 + + * parse.y: protectをbeginに変更.begin..endは例外処理だけでなく, + 文括弧としても働くことになった. + +Wed Feb 1 19:48:24 1995 Yukihiro Matsumoto (matz@ix-02) + + * version 0.65 + + * string.c(str_replace): 置き換える文字列の長さが等しい時メモリコ + ピーをしない. + + * string.c(rindex): バグ修正. + +Mon Jan 30 11:23:05 1995 Yukihiro Matsumoto (matz@ix-02) + + * parse.y(value_expr): ifのチェックを追加. + + * gc.c(gc_mark): free cellの扱いにバグ. + + * parse.y: 文法の変更(よりシンプルに).例外を減らした. + +Thu Jan 26 00:52:55 1995 Yukihiro Matsumoto (matz@dyna) + + * parse.y: 引数として連想配列を置くことができるように.この場合, + 連想配列リテラルが最終引数となる. + + * parse.y: 配列参照の`[]'内が空でもよいことにした. + +Tue Jan 24 14:45:15 1995 Yukihiro Matsumoto (matz@ix-02) + + * class.c(rb_include_module): `-v'を指定した時にはincludeしたモジュー + ルとクラス定数が衝突していないかチェックする. + +Mon Jan 23 10:42:09 1995 Yukihiro Matsumoto (matz@ix-02) + + * parse.y(rb_class2name): メタクラスに関するbug fix. + + * dict.c: Dict[..]で辞書の生成が出来るように. + + * array.c: Array[..]で配列の生成が出来るように. + + * parse.y: 辞書の表現として{a,b,..}という形式も許すように. + +Fri Jan 20 10:28:38 1995 Yukihiro Matsumoto (matz@ix-02) + + * re.c(Regexp.quote): 正規表現をエスケープするメソッド. + + * 無駄なrb_intern()を減らした. + + * parse.y: `!', `!=', `!~'を特殊演算子にする.よってこれらは再定義 + できなくなった. + +Wed Jan 18 13:20:41 1995 Yukihiro Matsumoto (matz@ix-02) + + * parse.y: 文法の整理(unless,untilをなくした). + +Tue Jan 17 11:11:27 1995 Yukihiro Matsumoto (matz@ix-02) + + * eval.c: defでメソッド再定義時にはスーパークラスのメソッドの可視 + 性を継承する.最初の定義の時は今までと同じデフォルト(トップレベ + ルで関数的,クラス定義内で通常メソッド). + + * object.c(Class#new): オブジェクトの生成時に関数的メソッド + init_objectが必ず呼ばれるように変更. + + * eval.c: 未定義のメソッドに対してunknownメソッドが呼ばれるように + なった.エラー表示が今までと同じになるようにenvを調節している. + +Fri Jan 13 14:40:30 1995 Yukihiro Matsumoto (matz@ix-02) + + * gc.c: gcを若干書き換えて整理した.が,あまり変化はなかったようだ. + + * parse.y(yylex): symbolを\symから:symに変更した. + +Thu Jan 12 01:39:28 1995 Yukihiro Matsumoto (matz@dyna) + + * eval.c: 新規関数 rb_eval_string(). + + * gc.c: gc_mark()を一部非再帰化. + + * variable.c(rb_ivar_{get,set}): インスタンス変数のアクセス周りで + チェックが足りなかった. + + * variable.c: クラス定数とインスタンス変数でハッシュテーブルを共有 + するようにした. + + * ruby.h: iv_tblをRBasicからRObjectとRClassへ移動した.これにより, + ObjectとClass,Moduleしかインスタンス変数を持てなくなる.が,メモ + リ効率は若干向上する. + +Tue Jan 10 00:58:20 1995 Yukihiro Matsumoto (matz@dyna) + + * 0.64 released + + * eval.c: レシーバと引数は常にiterではない. + + * cons.c(aref,aset): negative offset対応. + +Mon Jan 9 14:40:39 1995 Yukihiro Matsumoto (matz@ix-02) + + * parse.y: foo{..}の形式において,fooをローカル変数やクラス名では + なく,引数なしの関数型メソッド呼び出しとみなすようにした. + + * list.c -> cons.c: 名称変更(クラス名も). + + * list.c: a::b::c::nilをリスト(a b c)とみなすlisp形式から,a::b::c + をリスト(a b c)とみなすruby形式に変更.[], []=, eachもそれに会わ + せた仕様とする. + + * list.c: consペアとしての機能を強調.仕様変更. + +Sat Jan 7 01:26:26 1995 Yukihiro Matsumoto (matz@dyna) + + * eval.c: 自己代入の不具合修正. + + * eval.c(masign): 多重代入が配列もリストもとれるようにした. + + * list.c: assocを2要素の配列からList(CONSペア)に変更した. + +Fri Jan 6 13:42:12 1995 Yukihiro Matsumoto (matz@ix-02) + + * parse.y: a[b]+=cやa.b+=cなどの自己代入形式で,aやbを2度評価しな + くなった. + + * eval.c: iterator設定のバグフィックス. + + * list.c: Listクラスを新設. + +Thu Jan 5 13:55:00 1995 Yukihiro Matsumoto (matz@ix-02) + + * parse.y: SCOPEのメモリリークをなくした. + + * eval.c: built-inメソッドへの引数の引き渡し方を変更して,配列の生 + 成数を減らした. + + * re.c: match-dataを毎回生成することをやめた.`$~'をアクセスした時 + にon-demandで生成する. + + * string.c etc: 不必要なmemmoveをmemcpyに置換. + + * parse.y: =~, !~は副作用があるのでコンパイル時に展開できない. + +Tue Jan 3 02:04:36 1995 Yukihiro Matsumoto (matz@dyna) + + * eval.c: rest引数のbug fix. + + * eval.c,gc.c: scopeをオブジェクトにした. + + * eval.c: envとscopeの扱いを変更した. + +Wed Dec 28 09:46:57 1994 Yukihiro Matsumoto (matz@ix-02) + + * parse.y: evalでローカル変数が追加された場合に対応した. + + * parse.y: 演算子を含むaliasのbug fix. + +Tue Dec 27 16:45:20 1994 Yukihiro Matsumoto (matz@ix-02) + + * parse.y: def A Bをalias A Bに変更. + + * eval.c: alias関係のbug修正.nodeをオブジェクト化した時にenbugし + たようだ. + + * signal.c: システムコールの再定義を止めた. + + * io.c(select): write/exceptのフラグ設定にバグ. + + * Makefile.in: static link用オプションをMake変数として独立させた. + +Tue Dec 20 00:46:19 1994 Yukihiro Matsumoto (matz@dyna) + + * 0.63 released + + * eval.c(rb_call): superの呼び出しで落ちる.argc, argvの設定を忘れ + ていた. + + * parse.y(read_escape): 展開エラー. + + * variable.c: 定義済みの変数のhookを変更しないように. + +Mon Dec 19 12:01:10 1994 Yukihiro Matsumoto (matz@ix-02) + + * parse.y(cond): 条件式に代入式が置かれた場合,`-v'オプションで警 + 告が出るように. + + * parse.y(**): 冪乗演算子`**'の優先順位を単項演算子より高くした. + + * parse.y(and,or): 優先順位の低い演算子`and', `or'. + + * 0.62 released. + + * eval.c: 不必要になったPUSH_ENV, POP_ENVを減らした. + + * env.h: ENVIONからselfをはずした.PUSH_ENVはsuperの準備のためだけ + に用いることにした. + + * eval.c: 下記のオブジェクト化で遅くなった実行速度をもとに戻した. + +Mon Dec 17 23:01:10 1994 Yukihiro Matsumoto (matz@ix-02) + + * eval.c: env.{argv,argc}とscope.local_varsのオブジェクト化. + + * eval.c: 1スコープ内で複数Blockを生成したときのバグを修正. + +Fri Dec 16 15:52:06 1994 Yukihiro Matsumoto (matz@ix-02) + + * parse.y: `&&'と`||'の両辺はいつでも条件式とした. + +Thu Dec 15 00:16:04 1994 Yukihiro Matsumoto (matz@dyna) + + * eval.c(Block): Blockオブジェクトを実現. + + * node.h: NODE_QLISTはなくなった. + + * eval.c(rb_call): 引数への代入を名前で一つずつ代入するのをやめて, + 一度にコピーするようにした. + + * eval.c(rb_call): rubyで記述されたメソッドへの引数渡しをinline化. + + * eval.c: イテレータ判定処理の全面書き換え.不適切なイテレータ呼び + 出しをなくした.例えば「[foo(),bar()]{i|baz(i)}」でfooもbarもイ + テレータとして呼び出され*ない*. + + * eval.c(rb_call): SCOPE処理をinline化.メソッド呼び出しの若干の高 + 速化. + +Wed Dec 14 18:09:33 1994 Yukihiro Matsumoto (matz@ix-02) + + * node.h: nodeもオブジェクトにする.よってGCで回収される. + +Thu Dec 8 14:17:29 1994 Yukihiro Matsumoto (matz@ix-02) + + * 0.60 released - alpha test baseline. diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000000..1ae2adcd41 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,186 @@ +ChangeLog +MANIFEST +Makefile.in +README +README.jp +README.EXT +ToDo +array.c +bignum.c +class.c +compar.c +configure +configure.bat +configure.in +config.dj +config.guess +config.sub +defines.h +dir.c +dln.c +dln.h +dmyext.c +enum.c +env.h +error.c +eval.c +file.c +fnmatch.c +fnmatch.h +gc.c +glob.c +hash.c +inits.c +install-sh +io.c +io.h +main.c +math.c +node.h +numeric.c +object.c +pack.c +parse.y +process.c +random.c +range.c +re.c +re.h +regex.c +regex.h +ruby.1 +ruby.c +ruby.h +sig.h +signal.c +sprintf.c +st.c +st.h +string.c +struct.c +time.c +top.sed +util.h +util.c +variable.c +version.c +version.h +ext/Setup +ext/Setup.dj +ext/Setup.nt +ext/Setup.x68 +ext/extmk.rb.in +ext/extmk.rb.nt +ext/aix_ld.rb +lib/English.rb +lib/base64.rb +lib/cgi-lib.rb +lib/complex.rb +lib/date.rb +lib/debug.rb +lib/e2mmap.rb +lib/e2mmap1_0.rb +lib/find.rb +lib/finalize.rb +lib/ftplib.rb +lib/getopts.rb +lib/jcode.rb +lib/mailread.rb +lib/mathn.rb +lib/matrix.rb +lib/mutex_m.rb +lib/observer.rb +lib/parsearg.rb +lib/parsedate.rb +lib/ping.rb +lib/rational.rb +lib/sync.rb +lib/thread.rb +lib/thwait.rb +lib/tk.rb +lib/tkcore.rb +lib/tkcanvas.rb +lib/tkclass.rb +lib/tkentry.rb +lib/tkscrollbox.rb +lib/tktext.rb +lib/tkthcore.rb +lib/tracer.rb +missing/alloca.c +missing/crypt.c +missing/dir.h +missing/dup2.c +missing/file.h +missing/flock.c +missing/memmove.c +missing/mkdir.c +missing/nt.c +missing/nt.h +missing/setenv.c +missing/strcasecmp.c +missing/strdup.c +missing/strerror.c +missing/strftime.c +missing/strstr.c +missing/strtol.c +missing/strtoul.c +missing/x68.c +sample/biorhythm.rb +sample/clnt.rb +sample/dbmtest.rb +sample/dir.rb +sample/eval.rb +sample/export.rb +sample/exyacc.rb +sample/fact.rb +sample/fib.awk +sample/fib.pl +sample/fib.rb +sample/fib.scm +sample/freq.rb +sample/from.rb +sample/fullpath.rb +sample/getopts.test +sample/io.rb +sample/less.rb +sample/list.rb +sample/list2.rb +sample/list3.rb +sample/mrshtest.rb +sample/mkproto.rb +sample/mpart.rb +sample/observ.rb +sample/occur.pl +sample/occur.rb +sample/occur2.rb +sample/philos.rb +sample/pi.rb +sample/rcs.awk +sample/rcs.dat +sample/rcs.rb +sample/regx.rb +sample/ruby-mode.el +sample/rubydb2x.el +sample/rubydb3x.el +sample/sieve.rb +sample/svr.rb +sample/test.rb +sample/time.rb +sample/tkbiff.rb +sample/tkbrowse.rb +sample/tkdialog.rb +sample/tkfrom.rb +sample/tkhello.rb +sample/tkline.rb +sample/tktimer.rb +sample/trojan.rb +sample/tsvr.rb +sample/uumerge.rb +win32/Makefile +win32/config.h +win32/ntsetup.bat +win32/ruby.def +x68/fconvert.c +x68/select.c +x68/_dtos18.c +x68/_round.c diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000000..ee0ac1fcfc --- /dev/null +++ b/Makefile.in @@ -0,0 +1,219 @@ +SHELL = /bin/sh + +#### Start of system configuration section. #### + +srcdir = @srcdir@ +VPATH = @srcdir@:@srcdir@/missing + +CC = @CC@ +YACC = @YACC@ +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +PURIFY = +@SET_MAKE@ + +CFLAGS = @CFLAGS@ -I@srcdir@ +LDFLAGS = @STATIC@ $(CFLAGS) @LDFLAGS@ +LIBS = @LIBS@ $(EXTLIBS) +MISSING = @LIBOBJS@ @ALLOCA@ + +program_transform_name = -e @program_transform_name@ +RUBY_INSTALL_NAME = `t='$(program_transform_name)'; echo ruby | sed $$t` + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +libdir = @libdir@/$(RUBY_INSTALL_NAME) + +binsuffix = @binsuffix@ + +#### End of system configuration section. #### + + +LIBRUBY = libruby.a + +EXTOBJS = dmyext.o + +MAINOBJ = main.o + +OBJS = array.o \ + bignum.o \ + class.o \ + compar.o \ + dir.o \ + dln.o \ + enum.o \ + error.o \ + eval.o \ + file.o \ + fnmatch.o \ + gc.o \ + glob.o \ + hash.o \ + inits.o \ + io.o \ + math.o \ + numeric.o \ + object.o \ + pack.o \ + parse.o \ + process.o \ + random.o \ + range.o \ + re.o \ + regex.o \ + ruby.o \ + signal.o \ + sprintf.o \ + st.o \ + string.o \ + struct.o \ + time.o \ + util.o \ + variable.o \ + version.o \ + $(MISSING) + +all: miniruby$(binsuffix) @srcdir@/ext/Setup + @if test -z "$$UNDER_EXTMAKE_RB"; \ + then echo "Compiling ext modules"; \ + UNDER_EXTMAKE_RB=yes; export UNDER_EXTMAKE_RB; \ + cd ext; ../miniruby ./extmk.rb @EXTSTATIC@; fi + +miniruby$(binsuffix): $(OBJS) $(MAINOBJ) $(EXTOBJS) + @rm -f $@ + $(PURIFY) $(CC) $(LDFLAGS) $(MAINOBJ) $(OBJS) $(EXTOBJS) $(LIBS) -o miniruby + +ruby$(binsuffix): $(LIBRUBY) $(MAINOBJ) $(EXTOBJS) + @rm -f $@ + $(PURIFY) $(CC) $(LDFLAGS) $(MAINOBJ) $(EXTOBJS) $(LIBRUBY) $(LIBS) -o ruby + +$(LIBRUBY): $(OBJS) dmyext.o + @AR@ rcu $(LIBRUBY) $(OBJS) dmyext.o + @-@RANLIB@ $(LIBRUBY) 2> /dev/null || true + +install:; $(INSTALL_PROGRAM) ruby$(binsuffix) $(bindir)/$(RUBY_INSTALL_NAME)$(binsuffix) + @-@STRIP@ $(bindir)/$(RUBY_INSTALL_NAME)$(binsuffix) + @test -d $(libdir) || mkdir $(libdir) + cd ext; ../miniruby ./extmk.rb install + @for rb in `grep '^lib/' @srcdir@/MANIFEST`; do \ + $(INSTALL_DATA) @srcdir@/$$rb $(libdir); \ + done + +clean:; @rm -f $(OBJS) $(LIBRUBY) $(MAINOBJ) + @rm -f ext/extinit.c ext/extinit.o dmyext.o + cd ext; ../miniruby ./extmk.rb clean + +realclean: clean + @rm -f Makefile ext/extmk.rb ext/config.cache parse.c + @rm -f config.cache config.h config.log config.status + @rm -f core ruby$(binsuffix) miniruby$(binsuffix) parse.c *~ *.core gmon.out + +test:; @-./ruby @srcdir@/sample/test.rb > ./ruby_test 2>&1; \ + if grep '^end of test' ./ruby_test > /dev/null; then \ + echo "test succeeded"; \ + else \ + grep '^sample/test.rb' ./ruby_test; \ + grep '^not' ./ruby_test; \ + echo "test failed";\ + fi;\ + rm -f ./ruby_test + +.c.o: + $(CC) $(CFLAGS) $(CPPFLAGS) -c $< + +parse.c: parse.y + $(YACC) $< + mv -f y.tab.c parse.c + +alloca.o: @srcdir@/missing/alloca.c + $(CC) -I. $(CFLAGS) $(CPPFLAGS) -c @srcdir@/missing/alloca.c + +crypt.o: @srcdir@/missing/crypt.c + $(CC) -I. $(CFLAGS) $(CPPFLAGS) -c @srcdir@/missing/crypt.c + +dup2.o: @srcdir@/missing/dup2.c + $(CC) -I. $(CFLAGS) $(CPPFLAGS) -c @srcdir@/missing/dup2.c + +flock.o: @srcdir@/missing/flock.c + $(CC) -I. $(CFLAGS) $(CPPFLAGS) -c @srcdir@/missing/flock.c + +memmove.o: @srcdir@/missing/memmove.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c @srcdir@/missing/memmove.c + +mkdir.o: @srcdir@/missing/mkdir.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c @srcdir@/missing/mkdir.c + +setenv.o: @srcdir@/missing/setenv.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c @srcdir@/missing/setenv.c + +strcasecmp.o: @srcdir@/missing/strcasecmp.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c @srcdir@/missing/strcasecmp.c + +strerror.o: @srcdir@/missing/strerror.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c @srcdir@/missing/strerror.c + +strdup.o: @srcdir@/missing/strdup.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c @srcdir@/missing/strdup.c + +strftime.o: @srcdir@/missing/strftime.c + $(CC) -I. $(CFLAGS) $(CPPFLAGS) -c @srcdir@/missing/strftime.c + +strstr.o: @srcdir@/missing/strstr.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c @srcdir@/missing/strstr.c + +strtol.o: @srcdir@/missing/strtol.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c @srcdir@/missing/strtol.c + +strtoul.o: @srcdir@/missing/strtoul.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c @srcdir@/missing/strtoul.c + +nt.o: @srcdir@/missing/nt.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c @srcdir@/missing/nt.c + +x68.o: @srcdir@/missing/x68.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c @srcdir@/missing/x68.c + +# Prevent GNU make v3 from overflowing arg limit on SysV. +.NOEXPORT: +### +parse.o : parse.y ruby.h defines.h config.h env.h node.h st.h regex.h +### +array.o: array.c ruby.h config.h defines.h +bignum.o: bignum.c ruby.h config.h defines.h +class.o: class.c ruby.h config.h defines.h node.h st.h +compar.o: compar.c ruby.h config.h defines.h +dir.o: dir.c ruby.h config.h defines.h +dln.o: dln.c config.h defines.h dln.h st.h +dmyext.o: dmyext.c +enum.o: enum.c ruby.h config.h defines.h +error.o: error.c ruby.h config.h defines.h env.h +eval.o: eval.c ruby.h config.h defines.h env.h node.h sig.h st.h dln.h +file.o: file.c ruby.h config.h defines.h io.h sig.h +fnmatch.o: fnmatch.c config.h fnmatch.h +gc.o: gc.c ruby.h config.h defines.h env.h sig.h st.h node.h re.h regex.h +glob.o: glob.c config.h fnmatch.h +hash.o: hash.c ruby.h config.h defines.h st.h +inits.o: inits.c ruby.h config.h defines.h +io.o: io.c ruby.h config.h defines.h io.h sig.h +main.o: main.c +math.o: math.c ruby.h config.h defines.h +numeric.o: numeric.c ruby.h config.h defines.h +object.o: object.c ruby.h config.h defines.h st.h +pack.o: pack.c ruby.h config.h defines.h +process.o: process.c ruby.h config.h defines.h sig.h st.h +random.o: random.c ruby.h config.h defines.h +range.o: range.c ruby.h config.h defines.h +re.o: re.c ruby.h config.h defines.h re.h regex.h +regex.o: regex.c config.h defines.h regex.h util.h +ruby.o: ruby.c ruby.h config.h defines.h re.h regex.h dln.h +signal.o: signal.c ruby.h config.h defines.h sig.h +sprintf.o: sprintf.c ruby.h config.h defines.h +st.o: st.c config.h st.h +string.o: string.c ruby.h config.h defines.h re.h regex.h +struct.o: struct.c ruby.h config.h defines.h +time.o: time.c ruby.h config.h defines.h +util.o: util.c defines.h config.h util.h +variable.o: variable.c ruby.h config.h defines.h env.h st.h +version.o: version.c ruby.h config.h defines.h version.h diff --git a/README b/README new file mode 100644 index 0000000000..6583bc889f --- /dev/null +++ b/README @@ -0,0 +1,108 @@ +* What's Ruby + +Ruby is the interpreted scripting language for quick and +easy object-oriented programming. It has many features to +process text files and to do system management tasks (as in +perl). It is simple, straight-forward, and extensible. + +* Features of ruby + + + Simple Syntax + + *Normal* Object-Oriented features(ex. class, method calls) + + *Advanced* Object-Oriented features(ex. Mix-in, Singleton-method) + + Operator Overloading + + Exception Handling + + Iterators and Closures + + Garbage Collection + + Dynamic Loading of Object files(on some architecture) + + Highly Portable(works on many UNIX machines) + +* How to get ruby + +** by ftp + +The ruby distribution can be found on + + ftp://ftp.netlab.co.jp/pub/lang/ruby/ + +* How to compile and install + +This is what you need to do to compile and install ruby: + + 1. Run ./configure, which will generate config.h and Makefile. + + 2. Edit defines.h if you need. Probably this step will not need. + + 3. Remove comment mark(#) before the module names from ext/Setup, if + you want to link modules statically. + + If you want to link all the extension modules, remove comment + mark from the line "#option nodynamic". + + 4. Run make. + + 5. Optionally, run 'make test' to check that the compiled ruby + interpreter works well. If you see the message "test succeeded", + your ruby works as it should. + + 6. Run 'make install' + +If you fail to compile ruby, please send the detailed error report with +the error log and machine/OS type, to help others. + +* Copying + +Ruby is copyrighted by Yukihiro Matsumoto <matz@ruby.club.co.jp>. + +This source is distributed under the conditions blow: + + 1. You may make and give away verbatim copies of the source form of + the software without restriction, provided that you do not modify + the original distribution files. + + If you want to distribute the modified version in any way, contact + the author. + + 2. You may distribute the software in object code or executable + form, provided that you distribute it with instructions on where + to get the software. + + 3. You may modify the software in any way, provided that you do not + distribute the modified version. + + 4. You may modify and include the part of the software into any other + software (possibly commercial). But some files in the distribution + are not written by the author, so that they are not under this terms. + They are gc.c(partly),utils.c(partly), regex.[ch],fnmatch.[ch], + glob.c, st.[ch] and somme files under the ./missing directory. See + each files for the copying condition. + + 5. The scripts and library files supplied as input to or produced as + output from the software do not automatically fall under the + copyright of the software, but belong to whomever generated them, + and may be sold commercially, and may be aggregated with this + software. + + 6. 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. + +* ruby home-page + + The URL of the ruby home-page is: + + http://www.netlab.co.jp/ruby/ + +* The Author + +Feel free to send comments and bug reports to the author. Here is the +author's latest mail address: + + matz@netlab.co.jp + +------------------------------------------------------- +created at: Thu Aug 3 11:57:36 JST 1995 +Local variables: +mode: indented-text +end: diff --git a/README.EXT b/README.EXT new file mode 100644 index 0000000000..c2f81d1a7a --- /dev/null +++ b/README.EXT @@ -0,0 +1,1064 @@ +.\" README.EXT - -*- Text -*- created at: Mon Aug 7 16:45:54 JST 1995 + +This document explains how to make extention modules for ruby. + +1.Basic knowledge + +In C, variables have types and data do not have types. In contrast, +ruby variables do not have static type and data themselves have +types. So, data need to be converted across the languages. + +Data in ruby represented C type `VALUE'. Each VALUE data have its +data-type. + +rubyのデータはVALUEというCの型で表現されます.VALUE型のデー +タはそのデータタイプを自分で知っています.このデータタイプと +いうのはデータ(オブジェクト)の実際の構造を意味していて,ruby +のクラスとはまた違ったものです. + +To retrieve an C data from the VALUE, you need to: + + (1) Identify VALUE's data type + (2) Convert VALUE into C data + +Converting to wrong data type may cause serious promblems. + + +1.1 Data-types + +Ruby interpreter has data-types as below: + + T_NIL nil + T_OBJECT ordinaly object + T_CLASS class + T_MODULE module + T_FLOAT floating point number + T_STRING string + T_REGEXP regular expression + T_ARRAY array + T_FIXNUM Fixnum(31bit integer) + T_HASH assosiative array + T_STRUCT (ruby) structure + T_BIGNUM multi precision integer + T_TRUE true + T_FALSE false + T_DATA data + +Otherwise, there are several other types used internally: + + T_ICLASS + T_MATCH + T_VARMAP + T_SCOPE + T_NODE + +Most of the types are represented by C structures. + +1.2 Check Data Type of the VALUE + +The macro TYPE() defined in ruby.h shows data-type of the VALUE. +TYPE() returns the constant number T_XXXX described above. To handle +data-types, the code will be like: + + switch (TYPE(obj)) { + case T_FIXNUM: + /* process Fixnum */ + break; + case T_STRING: + /* process String */ + break; + case T_ARRAY: + /* process Array */ + break; + default: + /* raise exception */ + Fail("not valid value"); + break; + } + +There is the data-type check function. + + void Check_Type(VALUE value, int type) + +It raises an exception, if the VALUE does not have the type specified. + +There are faster check-macros for fixnums and nil. + + FIXNUM_P(obj) + NIL_P(obj) + +1.3 Convert VALUE into C data + +データタイプがT_NIL, T_FALSE, T_TRUEである時,データはそれぞ +れnil, FALSE, TRUEです.このデータタイプのオブジェクトはひと +つずつしか存在しません. + +データタイプがT_FIXNUMの時,これは31bitのサイズを持つ整数で +す.FIXNUMをCの整数に変換するためにはマクロ「FIX2INT()」を使 +います.それから,FIXNUMに限らずrubyのデータを整数に変換する +「NUM2INT()」というマクロがあります.このマクロはデータタイ +プのチェック無しで使えます(整数に変換できない場合には例外が +発生する). + +それ以外のデータタイプは対応するCの構造体があります.対応す +る構造体のあるVALUEはそのままキャスト(型変換)すれば構造体の +ポインタに変換できます. + +構造体は「struct RXxxxx」という名前でruby.hで定義されていま +す.例えば文字列は「struct RString」です.実際に使う可能性が +あるのは文字列と配列くらいだと思います. + +ruby.hでは構造体へキャストするマクロも「RXXXXX()」(全部大文 +字にしたもの)という名前で提供されています(例: RSTRING()). + +例えば,文字列strの長さを得るためには「RSTRING(str)->len」と +し,文字列strをchar*として得るためには「RSTRING(str)->ptr」 +とします.配列の場合には,それぞれ「RARRAT(str)->len」, +「RARRAT(str)->ptr」となります. + +rubyの構造体を直接アクセスする時に気をつけなければならないこ +とは,配列や文字列の構造体の中身は参照するだけで,直接変更し +ないことです.直接変更した場合,オブジェクトの内容の整合性が +とれなくなって,思わぬバグの原因になります. + +1.4 Convert C data into VALUE + +VALUEの実際の構造は + + * FIXNUMの場合 + + 1bit右シフトして,LSBを立てる. + + * その他のポインタの場合 + + そのままVALUEにキャストする. + +となっています.よって,LSBをチェックすればVALUEがFIXNUMかど +うかわかるわけです(ポインタのLSBが立っていないことを仮定して +いる). + +ですから,FIXNUM以外のrubyのオブジェクトの構造体は単にVALUE +にキャストするだけでVALUEに変換出来ます.ただし,任意の構造 +体がVALUEにキャスト出来るわけではありません.キャストするの +はrubyの知っている構造体(ruby.hで定義されているstruct RXxxx +のもの)だけにしておいてください. + +FIXNUMに関しては変換マクロを経由する必要があります.Cの整数 +からVALUEに変換するマクロは以下のものがあります.必要に応じ +て使い分けてください. + + INT2FIX() もとの整数が31bit以内に収まる時 + INT2NUM() 任意の整数からVALUEへ + +INT2NUM()は整数がFIXNUMの範囲に収まらない場合,Bignumに変換 +してくれます(が,少し遅い). + +1.5 Manipulate ruby data + +先程も述べた通り,rubyの構造体をアクセスする時に内容の更新を +行うことは勧められません.で,rubyのデータを操作する時には +rubyが用意している関数を用いてください. + +ここではもっとも使われるであろう文字列と配列の生成/操作を行 +い関数をあげます(全部ではないです). + + String funtions + + str_new(char *ptr, int len) + + Creates a new ruby string. + + str_new2(char *ptr) + + Creates a new ruby string from C string. This is equivalent to + str_new(ptr, strlen(ptr)). + + str_cat(VALUE str, char *ptr, int len) + + Appends len bytes data from ptr to the ruby string. + + Array functions + + ary_new() + + Creates an array with no element. + + ary_new2(int len) + + Creates an array with no element, with allocating internal buffer + for len elements. + + ary_new3(int n, ...) + + Creates an n-elements array from arguments. + + ary_new4(int n, VALUE *elts) + + Creates an n-elements array from C array. + + ary_push(VALUE ary) + ary_pop(VALUE ary, VALUE val) + ary_shift(VALUE ary) + ary_unshift(VALUE ary, VALUE val) + ary_entry(VALUE ary, int idx) + + Array operations. The first argument to each functions must be an + array. They may dump core if other types given. + +2. Extend ruby with C + +原理的にrubyで書けることはCでも書けます.rubyそのものがCで記 +述されているんですから,当然といえば当然なんですけど.ここで +はrubyの拡張に使うことが多いだろうと予測される機能を中心に紹 +介します. + +2.1 Add new features to ruby + +rubyで提供されている関数を使えばrubyインタプリタに新しい機能 +を追加することができます.rubyでは以下の機能を追加する関数が +提供されています. + + * クラス,モジュール + * メソッド,特異メソッドなど + * 定数 + +では順に紹介します. + +2.1.1 Class/module definition + +クラスやモジュールを定義するためには,以下の関数を使います. + + VALUE rb_define_class(char *name, VALUE super) + VALUE rb_define_module(char *name) + +これらの関数は新しく定義されたクラスやモジュールを返します. +メソッドや定数の定義にこれらの値が必要なので,ほとんどの場合 +は戻り値を変数に格納しておく必要があるでしょう. + +2.1.2 Method/singleton method definition + +メソッドや特異メソッドを定義するには以下の関数を使います. + + void rb_define_method(VALUE class, char *name, + VALUE (*func)(), int argc) + + void rb_define_singleton_method(VALUE object, char *name, + VALUE (*func)(), int argc) + + +念のため説明すると「特異メソッド」とは,その特定のオブジェク +トに対してだけ有効なメソッドです.rubyではよくSmalltalkにお +けるクラスメソッドとして,クラスに対する特異メソッドが使われ +ます. + +これらの関数の argcという引数はCの関数へ渡される引数の数(と +形式)を決めます.argcが正の時は関数に引き渡す引数の数を意味 +します.16個以上の引数は使えません(が,要りませんよね,そん +なに). + +argcが負の時は引数の数ではなく,形式を指定したことになります. +argcが-1の時は引数を配列に入れて渡されます.argcが-2の時は引 +数はrubyの配列として渡されます. + +メソッドを定義する関数はもう二つあります.ひとつはprivateメ +ソッドを定義する関数で,引数はrb_define_method()と同じです. + + void rb_define_private_method(VALUE class, char *name, + VALUE (*func)(), int argc) + +privateメソッドとは関数形式でしか呼び出すことの出来ないメソッ +ドです. + +もうひとつはモジュール関数を定義するものです.モジュール関数 +とはモジュールの特異メソッドであり,同時にprivateメソッドで +もあるものです.例をあげるとMathモジュールのsqrt()などがあげ +られます.このメソッドは + + Math.sqrt(4) + +という形式でも + + include Math + sqrt(4) + +という形式でも使えます.モジュール関数を定義する関数は以下の +通りです. + + void rb_define_module_function(VALUE module, char *name, + VALUE (*func)(), int argc) + +関数的メソッド(Kernelモジュールのprivaet method)を定義するた +めの関数は以下の通りです. + + void rb_define_global_function(char *name, VALUE (*func)(), int argc) + + +2.1.3 Constant definition + +拡張モジュールが必要な定数はあらかじめ定義しておいた方が良い +でしょう.定数を定義する関数は二つあります. + + void rb_define_const(VALUE class, char *name, VALUE val) + void rb_define_global_const(char *name, VALUE val) + +前者は特定のクラス/モジュールに属する定数を定義するもの,後 +者はグローバルな定数を定義するものです. + +2.2 Use ruby features from C + +既に『1.5 rubyのデータを操作する』で一部紹介したような関数を +使えば,rubyの機能を実現している関数を直接呼び出すことが出来 +ます. + +# このような関数の一覧表はいまのところありません.ソースを見 +# るしかないですね. + +それ以外にもrubyの機能を呼び出す方法はいくつかあります. + +2.2.1 rubyのプログラムをevalする + +Cからrubyの機能を呼び出すもっとも簡単な方法として,文字列で +与えられたrubyのプログラムを評価する関数があります. + + VALUE rb_eval_string(char *str) + +この評価は現在の環境で行われます.つまり,現在のローカル変数 +などを受け継ぎます. + +2.2.2 ID or Symbol + +Cから文字列を経由せずにrubyのメソッドを呼び出すこともできま +す.その前に,rubyインタプリタ内でメソッドや変数名を指定する +時に使われているIDについて説明しておきましょう. + +IDとは変数名,メソッド名を表す整数です.rubyの中では + + :識別子 + +でアクセスできます.Cからこの整数を得るためには関数 + + rb_intern(char *name) + +を使います.また一文字の演算子はその文字コードがそのままシン +ボルになっています. + +2.2.3 Invoke ruby method from C + +Cから文字列を経由せずにrubyのメソッドを呼び出すためには以下 +の関数を使います. + + VALUE rb_funcall(VALUE recv, ID mid, int argc, ...) + +この関数はオブジェクトrecvのmidで指定されるメソッドを呼び出 +します. + +2.2.4 変数/定数を参照/更新する + +Cから関数を使って参照・更新できるのは,クラス定数,インスタ +ンス変数です.大域変数は一部のものはCの大域変数としてアクセ +スできます.ローカル変数を参照する方法は公開していません. + +オブジェクトのインスタンス変数を参照・更新する関数は以下の通 +りです. + + VALUE rb_ivar_get(VALUE obj, ID id) + VALUE rb_ivar_set(VALUE obj, ID id, VALUE val) + +idはrb_intern()で得られるものを使ってください. + +クラス定数を参照するには以下の関数を使ってください. + + VALUE rb_const_get(VALUE obj, ID id) + +クラス定数を新しく定義するためには『2.1.3 定数定義』で紹介さ +れている関数を使ってください. + +3. Informatin sharing between ruby and C + +C言語とrubyの間で情報を共有する方法について解説します. + +3.1 Ruby constant that Cから参照できるrubyの定数 + +Following ruby constants can be referred from C. + + TRUE + FALSE + +Boolean values. FALSE is false in the C also (i.e. 0). + + Qnil + +Ruby nil in C scope. + +3.2 Global variables shared between C and ruby + +Cとrubyで大域変数を使って情報を共有できます.共有できる大域 +変数にはいくつかの種類があります.そのなかでもっとも良く使わ +れると思われるのはrb_define_variable()です. + + void rb_define_variable(char *name, VALUE *var) + +この関数はrubyとCとで共有する大域変数を定義します.変数名が +`$'で始まらない時には自動的に追加されます.この変数の値を変 +更すると自動的にrubyの対応する変数の値も変わります. + +またruby側からは更新できない変数もあります.このread onlyの +変数は以下の関数で定義します. + + void rb_define_readonly_variable(char *name, VALUE *var) + +これら変数の他にhookをつけた大域変数を定義できます.hook付き +の大域変数は以下の関数を用いて定義します.hook付き大域変数の +値の参照や設定はhookで行う必要があります. + + void rb_define_hooked_variable(char *name, VALUE *var, + VALUE (*getter)(), VALUE (*setter)()) + +この関数はCの関数によってhookのつけられた大域変数を定義しま +す.変数が参照された時には関数getterが,変数に値がセットされ +た時には関数setterが呼ばれる.hookを指定しない場合はgetterや +setterに0を指定します. + +# getterもsetterも0ならばrb_define_variable()と同じになる. + +それから,Cの関数によって実現されるrubyの大域変数を定義する +関数があります. + + void rb_define_virtual_variable(char *name, + VALUE (*getter)(), VALUE (*setter)()) + +この関数によって定義されたrubyの大域変数が参照された時には +getterが,変数に値がセットされた時にはsetterが呼ばれます. + +The prototypes of the getter and setter functions are as following: + + (*getter)(ID id, void *data, struct global_entry* entry); + (*setter)(VALUE val, ID id, void *data, struct global_entry* entry); + +3.3 Encapsulate C data into ruby object + +Cの世界で定義されたデータ(構造体)をrubyのオブジェクトとして +取り扱いたい場合がありえます.このような場合には,Dataという +rubyオブジェクトにCの構造体(へのポインタ)をくるむことでruby +オブジェクトとして取り扱えるようになります. + +Dataオブジェクトを生成して構造体をrubyオブジェクトにカプセル +化するためには,以下のマクロを使います. + + Data_Wrap_Struct(class,mark,free,ptr) + +このマクロの戻り値は生成されたDataオブジェクトです. + +classはこのDataオブジェクトのクラスです.ptrはカプセル化する +Cの構造体へのポインタです.markはこの構造体がrubyのオブジェ +クトへの参照がある時に使う関数です.そのような参照を含まない +時には0を指定します. + +# そのような参照は勧められません. + +freeはこの構造体がもう不要になった時に呼ばれる関数です.この +関数がガーベージコレクタから呼ばれます. + +Cの構造体の割当とDataオブジェクトの生成を同時に行うマクロと +して以下のものが提供されています. + + Data_Make_Struct(class, type, mark, free, sval) + +このマクロの戻り値は生成されたDataオブジェクトです. + +class, mark, freeはData_Wrap_Structと同じ働きをします.type +は割り当てるC構造体の型です.割り当てられた構造体は変数sval +に代入されます.この変数の型は (type*) である必要があります. + +Dataオブジェクトからポインタを取り出すのは以下のマクロを用い +ます. + + Data_Get_Struct(obj, type, sval) + +Cの構造体へのポインタは変数svalに代入されます. + +これらのDataの使い方はちょっと分かりにくいので,後で説明する +例題を参照してください. + +4.Example - Create dbm module + +ここまでの説明でとりあえず拡張モジュールは作れるはずです. +rubyのextディレクトリにすでに含まれているdbmモジュールを例に +して段階的に説明します. + +(1) make the directory + + % mkdir ext/dbm + +rubyを展開したディレクトリの下,extディレクトリの中に拡張モ +ジュール用のディレクトリを作ります.名前は適当に選んで構いま +せん. + +(2) create MANIFEST file + + % cd ext/dbm + % touch MANIFEST + +拡張モジュールのディレクトリの下にはMANIFESTというファイルが +必要なので,とりあえず空のファイルを作っておきます.後でこの +ファイルには必要なファイル一覧が入ることになります. + +MANIFESTというファイルは,makeの時にディレクトリが拡張モジュー +ルを含んでいるかどうか判定するために使われれています. + +(3) design the library + +まあ,当然なんですけど,どういう機能を実現するかどうかまず設 +計する必要があります.どんなクラスをつくるか,そのクラスには +どんなメソッドがあるか,クラスが提供する定数などについて設計 +します.dbmクラスについてはext/dbm.docを参照してください. + +(4) write C code. + +拡張モジュール本体となるC言語のソースを書きます.C言語のソー +スがひとつの時には「モジュール名.c」を選ぶと良いでしょう.C +言語のソースが複数の場合には逆に「モジュール名.c」というファ +イル名は避ける必要があります.オブジェクトファイルとモジュー +ル生成時に中間的に生成される「モジュール名.o」というファイル +とが衝突するからです. + +rubyは拡張モジュールをロードする時に「Init_モジュール名」と +いう関数を自動的に実行します.dbmモジュールの場合「Init_dbm」 +です.この関数の中でクラス,モジュール,メソッド,定数などの +定義を行います.dbm.cから一部引用します. + +-- +Init_dbm() +{ + /* DBMクラスを定義する */ + cDBM = rb_define_class("DBM", cObject); + /* DBMはEnumerateモジュールをインクルードする */ + rb_include_module(cDBM, mEnumerable); + + /* DBMクラスのクラスメソッドopen(): 引数はCの配列で受ける */ + rb_define_singleton_method(cDBM, "open", fdbm_s_open, -1); + + /* DBMクラスのメソッドclose(): 引数はなし */ + rb_define_method(cDBM, "close", fdbm_close, 0); + /* DBMクラスのメソッド[]: 引数は1個 */ + rb_define_method(cDBM, "[]", fdbm_fetch, 1); + : + + /* DBMデータを格納するインスタンス変数名のためのID */ + id_dbm = rb_intern("dbm"); +} +-- + +DBMモジュールはdbmのデータと対応するオブジェクトになるはずで +すから,Cの世界のdbmをrubyの世界に取り込む必要があります. + + +dbm.cではData_Make_Structを以下のように使っています. + +-- +struct dbmdata { + int di_size; + DBM *di_dbm; +}; + + +obj = Data_Make_Struct(class,struct dbmdata,0,free_dbm,dbmp); +-- + +ここではdbmstruct構造体へのポインタをDataにカプセル化してい +ます.DBM*を直接カプセル化しないのはclose()した時の処理を考 +えてのことです. + +Dataオブジェクトからdbmstruct構造体のポインタを取り出すため +に以下のマクロを使っています. + +-- +#define GetDBM(obj, dbmp) {\ + Data_Get_Struct(obj, struct dbmdata, dbmp);\ + if (dbmp->di_dbm == 0) closed_dbm();\ +} +-- + +ちょっと複雑なマクロですが,要するにdbmdata構造体のポインタ +の取り出しと,closeされているかどうかのチェックをまとめてい +るだけです. + +DBMクラスにはたくさんメソッドがありますが,分類すると3種類の +引数の受け方があります.ひとつは引数の数が固定のもので,例と +してはdeleteメソッドがあります.deleteメソッドを実装している +fdbm_delete()はこのようになっています. + +-- +static VALUE +fdbm_delete(obj, keystr) + VALUE obj, keystr; +{ + : +} +-- + +引数の数が固定のタイプは第1引数がself,第2引数以降がメソッド +の引数となります. + +引数の数が不定のものはCの配列で受けるものとrubyの配列で受け +るものとがあります.dbmモジュールの中で,Cの配列で受けるもの +はDBMのクラスメソッドであるopen()です.これを実装している関 +数fdbm_s_open()はこうなっています. + +-- +static VALUE +fdbm_s_open(argc, argv, class) + int argc; + VALUE *argv; + VALUE class; +{ + : + if (rb_scan_args(argc, argv, "11", &file, &vmode) == 1) { + mode = 0666; /* default value */ + } + : +} +-- + +このタイプの関数は第1引数が与えられた引数の数,第2引数が与え +られた引数の入っている配列になります.selfは第3引数として与 +えられます. + +この配列で与えられた引数を解析するための関数がopen()でも使わ +れているrb_scan_args()です.第3引数に指定したフォーマットに +従い,第4変数以降に指定した変数に値を代入してくれます.この +フォーマットは,第1文字目が省略できない引数の数,第2文字目が +省略できる引数の数,第3文字目が対応する相手が無いあまりの引 +数があるかどうかを示す"*"です.2文字目と3文字目は省略できま +す.dbm.cの例では,フォーマットは"11"ですから,引数は最低1つ +で,2つまで許されるという意味になります.省略されている時の +変数の値はnil(C言語のレベルではQnil)になります. + +rubyの配列で引数を受け取るものはindexesがあります.実装はこ +うです. + +-- +static VALUE +fdbm_indexes(obj, args) + VALUE obj; + struct RArray *args; +{ + : +} +-- + +第1引数はself,第2引数はrubyの配列です.ここではキャストを減 +らすため struct RArray* で受けていますが,VALUEでも同じこと +です. + +** 注意事項 + +rubyと共有はしないがrubyのオブジェクトを格納する可能性のある +Cの大域変数は以下の関数を使ってrubyインタプリタに変数の存在 +を教えてあげてください.でないとGCでトラブルを起こします. + + void rb_global_variable(VALUE *var) + +(5) prepare extconf.rb + +もしディレクトリに「extconf.rb」というファイルが存在すれば, +make時に実行されます.なければ適当にMakefileが生成されます. + +extconf.rbはモジュールのコンパイルに必要な条件のチェックなど +を行うことが目的です.extconf.rbの中では以下のruby関数を使う +ことが出来ます. + + have_library(lib, func): ライブラリの存在チェック + have_func(func): 関数の存在チェック + have_header(header): ヘッダファイルの存在チェック + create_makefile(target): Makefileの生成 + +以下の変数を使うことができます. + + $CFLAGS: コンパイル時に追加的に指定するフラグ(-Iなど) + $LDFLAGS: リンク時に追加的に指定するフラグ(-Lなど) + +モジュールをコンパイルする条件が揃わなず,そのモジュールはコ +ンパイルしない時にはcreate_makefileを呼ばなければMakefileは +生成されず,コンパイルも行われません. + +(6) prepare depend (optional) + +もし,ディレクトリにdependというファイルが存在すれば, +Makefileが依存関係をチェックしてくれます. + + % gcc -MM *.c > depend + +などで作ることが出来ます.あって損は無いでしょう. + +(7) MANIFESTファイルにファイル名を入れる + + % ls > MANIFEST + % vi MANIFEST + +*.o, *~など不必要なファイル以外はMANIFESTに追加しておきます. +make時にはMANIFESTの内容は参照しませんので,空のままでも問題 +は起きませんが,パッケージングの時に参照することがあるのと, +必要なファイルを区別できるので,用意しておいた方が良いでしょ +う. + +(8) make + +rubyのディレクトリでmakeを実行するとMakefileを生成からmake, +必要によってはそのモジュールのrubyへのリンクまで自動的に実行 +してくれます.extconf.rbを書き換えるなどしてMakefileの再生成 +が必要な時はまたrubyディレクトリでmakeしてください. + +(9) debug + +まあ,デバッグしないと動かないでしょうね.ext/Setupにディレ +クトリ名を書くと静的にリンクするのでデバッガが使えるようにな +ります.その分コンパイルが遅くなりますけど. + +(10) done, now you have the extension module + +後はこっそり使うなり,広く公開するなり,売るなり,ご自由にお +使いください.rubyの作者は拡張モジュールに関して一切の権利を +主張しません. + +Appendix A. rubyのソースコードの分類 + +rubyのソースはいくつかに分類することが出来ます.このうちクラ +スライブラリの部分は基本的に拡張モジュールと同じ作り方になっ +ています.これらのソースは今までの説明でほとんど理解できると +思います. + +coore ruby language + + class.c + error.c + eval.c + gc.c + object.c + parse.y + variable.c + +utility functions + + dln.c + fnmatch.c + glob.c + regex.c + st.c + util.c + +ruby interpreter implementation + + dmyext.c + inits.c + main.c + ruby.c + version.c + +class library + + array.c + bignum.c + compar.c + dir.c + enum.c + file.c + hash.c + io.c + math.c + numeric.c + pack.c + process.c + random.c + range.c + re.c + signal.c + sprintf.c + string.c + struct.c + time.c + +Appendix B. 拡張用関数リファレンス + +C言語からrubyの機能を利用するAPIは以下の通りである. + +** 型 + + VALUE + +rubyオブジェクトを表現する型.必要に応じてキャストして用いる. +組み込み型を表現するCの型はruby.hに記述してあるRで始まる構造 +体である.VALUE型をこれらにキャストするためにRで始まる構造体 +名を全て大文字にした名前のマクロが用意されている. + +** Variables and constants + + Qnil + +const: nil object + + TRUE + +const: TRUE object(default true value) + + FALSE + +const: FALSE object + +** Cデータのカプセル化 + + Data_Wrap_Struct(VALUE class, void (*mark)(), void (*free)(), void *sval) + +Cの任意のポインタをカプセル化したrubyオブジェクトを返す.こ +のポインタがrubyからアクセスされなくなった時,freeで指定した +関数が呼ばれる.また,このポインタの指すデータが他のrubyオブ +ジェクトを指している場合,markに指定する関数でマークする必要 +がある. + + Data_Make_Struct(class, type, mark, free, sval) + +type型のメモリをmallocし,変数svalに代入した後,それをカプセ +ル化したデータを返すマクロ. + + Data_Get_Struct(data, type, sval) + +dataからtype型のポインタを取り出し変数svalに代入するマクロ. + +** クラス/モジュール定義 + + VALUE rb_define_class(char *name, VALUE super) + +superのサブクラスとして新しいrubyクラスを定義する. + + VALUE rb_define_class_under(VALUE module, char *name, VALUE super) + +superのサブクラスとして新しいrubyクラスを定義し,moduleの定 +数として定義する. + + VALUE rb_define_module(char *name) + +新しいrubyモジュールを定義する. + + VALUE rb_define_module_under(VALUE module, char *name, VALUE super) + +新しいrubyモジュールを定義し,moduleの定数として定義する. + + void rb_include_module(VALUE class, VALUE module) + +モジュールをインクルードする.classがすでにmoduleをインクルー +ドしている時には何もしない(多重インクルードの禁止). + + void rb_extend_object(VALUE object, VALUE module) + +オブジェクトをモジュール(で定義されているメソッド)で拡張する. + +** 大域変数定義 + + void rb_define_variable(char *name, VALUE *var) + +rubyとCとで共有するグローバル変数を定義する.変数名が`$'で始 +まらない時には自動的に追加される.nameとしてrubyの識別子とし +て許されない文字(例えば` ')を含む場合にはrubyプログラムから +は見えなくなる. + + void rb_define_readonly_variable(char *name, VALUE *var) + +rubyとCとで共有するread onlyのグローバル変数を定義する.read +onlyであること以外はrb_define_variable()と同じ. + + void rb_define_virtual_variable(char *name, + VALUE (*getter)(), VALUE (*setter)()) + +関数によって実現されるruby変数を定義する.変数が参照された時 +にはgetterが,変数に値がセットされた時にはsetterが呼ばれる. + + void rb_define_hooked_variable(char *name, VALUE *var, + VALUE (*getter)(), VALUE (*setter)()) + +関数によってhookのつけられたグローバル変数を定義する.変数が +参照された時にはgetterが,関数に値がセットされた時にはsetter +が呼ばれる.getterやsetterに0を指定した時にはhookを指定しな +いのと同じ事になる. + + void rb_global_variable(VALUE *var) + +GCのため,rubyプログラムからはアクセスされないが, rubyオブジェ +クトを含む大域変数をマークする. + +** クラス定数 + + void rb_define_const(VALUE class, char *name, VALUE val) + +クラス定数を定義する. + + void rb_define_global_const(char *name, VALUE val) + +大域定数を定義する. + + rb_define_const(cKernal, name, val) + +と同じ意味. + +** メソッド定義 + + rb_define_method(VALUE class, char *name, VALUE (*func)(), int argc) + +メソッドを定義する.argcはselfを除く引数の数.argcが-1の時, +関数には引数の数(selfを含まない)を第1引数, 引数の配列を第2引 +数とする形式で与えられる(第3引数はself).argcが-2の時, 第1引 +数がself, 第2引数がargs(argsは引数を含むrubyの配列)という形 +式で与えられる. + + rb_define_private_method(VALUE class, char *name, VALUE (*func)(), int argc) + +privateメソッドを定義する.引数はrb_define_method()と同じ. + + rb_define_singleton_method(VALUE class, char *name, VALUE (*func)(), int argc) + +特異メソッドを定義する.引数はrb_define_method()と同じ. + + rb_scan_args(int atgc, VALUE *argv, char *fmt, ...) + +argc,argv形式で与えられた引数を分解する.fmtは必須引数の数, +付加引数の数, 残りの引数があるかを指定する文字列で, "数字数 +字*"という形式である. 2 番目の数字と"*"はそれぞれ省略可能で +ある.必須引数が一つもない場合は0を指定する.第3引数以降は変 +数へのポインタで, 該当する要素がその変数に格納される.付加引 +数に対応する引数が与えられていない場合は変数にQnilが代入され +る. + +** rubyメソッド呼び出し + + VALUE rb_funcall(VALUE recv, ID mid, int narg, ...) + +メソッド呼び出し.文字列からmidを得るためにはrb_intern()を使う. + + VALUE rb_funcall2(VALUE recv, ID mid, int argc, VALUE *argv) + +メソッド呼び出し.引数をargc,argv形式で渡す. + + VALUE rb_eval_string(char *str) + +文字列をrubyとスクリプトしてコンパイル・実行する. + + ID rb_intern(char *name) + +文字列に対応するIDを返す. + + char *rb_id2name(ID id) + +IDに対応する文字列を返す(デバッグ用). + + char *rb_class2name(VALUE class) + +classの名前を返す(デバッグ用).classが名前を持たない時には, +祖先を遡って名前を持つクラスの名前を返す. + +** インスタンス変数 + + VALUE rb_iv_get(VALUE obj, char *name) + +objのインスタンス変数の値を得る.`@'で始まらないインスタンス +変数は rubyプログラムからアクセスできない「隠れた」インスタ +ンス変数になる. + + VALUE rb_iv_set(VALUE obj, char *name, VALUE val) + +objのインスタンス変数をvalにセットする. + +** 制御構造 + + VALUE rb_iterate(VALUE (*func1)(), void *arg1, VALUE (*func2)(), void *arg2) + +func2をブロックとして設定し, func1をイテレータとして呼ぶ. +func1には arg1が引数として渡され, func2には第1引数にイテレー +タから与えられた値, 第2引数にarg2が渡される. + + VALUE rb_yield(VALUE val) + +valを値としてイテレータブロックを呼び出す. + + VALUE rb_rescue(VALUE (*func1)(), void *arg1, VALUE (*func2)(), void *arg2) + +関数func1をarg1を引数に呼び出す.func1の実行中に例外が発生し +た時には func2をarg2を引数として呼ぶ.戻り値は例外が発生しな +かった時はfunc1の戻り値, 例外が発生した時にはfunc2の戻り値で +ある. + + VALUE rb_ensure(VALUE (*func1)(), void *arg1, void (*func2)(), void *arg2) + +関数func1をarg1を引数として実行し, 実行終了後(たとえ例外が発 +生しても) func2をarg2を引数として実行する.戻り値はfunc1の戻 +り値である(例外が発生した時は戻らない). + +** 例外・エラー + + void Warning(char *fmt, ...) + +verbose時に標準エラー出力に警告情報を表示する.引数はprintf()と同じ. + + void Fail(char *fmt, ...) + +例外を発生させる.引数はprintf()と同じ. + + void Fatal(char *fmt, ...) + +致命的例外を発生させる.通常の例外処理は行なわれず, インター +プリタが終了する(ただしensureで指定されたコードは終了前に実 +行される). + + void Bug(char *fmt, ...) + +インタープリタなどプログラムのバグでしか発生するはずのない状 +況の時呼ぶ.インタープリタはコアダンプし直ちに終了する.例外 +処理は一切行なわれない. + +** rubyの初期化・実行 + +rubyをアプリケーションに埋め込む場合には以下のインタフェース +を使う.通常の拡張モジュールには必要ない. + + void ruby_init(int argc, char **argv, char **envp) + +rubyインタプリタの初期化を行なう. + + void ruby_run() + +rubyインタプリタを実行する. + + void ruby_script(char *name) + +rubyのスクリプト名($0)を設定する. + +Appendix B. extconf.rbで使える関数たち + +extconf.rbの中では利用可能なコンパイル条件チェックの関数は以 +下の通りである. + + have_library(lib, func) + +関数funcを定義しているライブラリlibの存在をチェックする.ラ +イブラリが存在する時,TRUEを返す. + + have_func(func) + +関数funcの存在をチェックする.funcが標準ではリンクされないラ +イブラリ内のものである時には先にhave_libraryでそのライブラリ +をチェックしておく事.関数が存在する時TRUEを返す. + + have_header(header) + +ヘッダファイルの存在をチェックする.ヘッダファイルが存在する +時TRUEを返す. + + create_makefile(target) + +拡張モジュール用のMakefileを生成する.この関数を呼ばなければ +そのモジュールはコンパイルされない.targetはモジュール名を表 +す. + +/* + * Local variables: + * fill-column: 60 + * end: + */ diff --git a/README.jp b/README.jp new file mode 100644 index 0000000000..8a100106b1 --- /dev/null +++ b/README.jp @@ -0,0 +1,150 @@ +* Rubyとは + +Rubyはシンプルかつ強力なオブジェクト指向スクリプト言語です. +Rubyは最初から純粋なオブジェクト指向言語として設計されていま +すから,オブジェクト指向プログラミングを手軽に行う事が出来ま +す.もちろん通常の手続き型のプログラミングも可能です. + +Rubyはテキスト処理関係の能力などに優れ,perlと同じくらい強力 +です.さらにシンプルな文法と,例外処理やイテレータなどの機構 +によって,より分かりやすいプログラミングが出来ます. + +* Rubyの特長. + + + シンプルな文法 + + 普通のオブジェクト指向機能(クラス,メソッドコールなど) + + 特殊なオブジェクト指向機能(Mixin, 特異メソッドなど) + + 演算子オーバーロード + + 例外処理機能 + + イテレータとクロージャ + + ガーベージコレクタ + + ダイナミックローディング (アーキテクチャによる) + + 移植性が高い.多くのUNIX上で動く + +* 入手法 + +** ftpで + +以下の場所においてあります. + + ftp://ftp.netlab.co.jp/pub/lang/ruby/ + +* ホームページ + + RubyのホームページのURLは + + http://www.netlab.co.jp/ruby/jp/ + + です. + +* メイリングリスト + + Rubyに関わる話題のためのメイリングリストを開設しました.ア + ドレスは + + ruby-list@netlab.co.jp + + です.このアドレスにメイルを送れば,自動的に登録されます. + +* コンパイル・インストール + +以下の手順で行ってください. + + 1. configureを実行してMakefileなどを生成する + + 2. (必要ならば)defines.hを編集する + + 多分,必要無いと思います. + + 3. (必要ならば)ext/Setupに静的にリンクする拡張モジュールを + 指定する + + ext/Setupに記述したモジュールは静的にリンクされます. + + ダイナミックローディングをサポートしていないアーキテク + チャではSetupの1行目の「option nodynamic」という行のコ + メントを外す必要があります.また,このアーキテクチャで + 拡張モジュールを利用するためには,あらかじめ静的にリン + クしておく必要があります. + + 4. makeを実行してコンパイルする + + 5. make testでテストを行う. + + 「test succeeded」と表示されれば成功です.ただしテスト + に成功しても完璧だと保証されている訳ではありません. + + 6. make install + +もし,コンパイル時にエラーが発生した場合にはエラーのログとマ +シン,OSの種類を含むできるだけ詳しいレポートを作者に送ってく +ださると他の方のためにもなります. + +* 移植 + +UNIXであればconfigureがほとんどの差異を吸収してくれるはずで +すが,思わぬ見落としがあった場合(あるに違いない),作者にその +ことをレポートすれば,解決できるかも知れません. + +アークテクチャにもっとも依存するのはGC部です.rubyのGCは対象 +のアーキテクチャがsetjmp()によって全てのレジスタを jmp_bufに +格納することと,jmp_bufとスタックが32bitアラインメントされて +いることを仮定しています.特に前者が成立しない場合の対応は非 +常に困難でしょう.後者の解決は比較的簡単で,gc.cでスタックを +マークしている部分にアラインメントのバイト数だけずらしてマー +クするコードを追加するだけで済みます.「defined(THINK_C)」で +括られている部分を参考にしてください + +# 実際にはrubyはThink Cではコンパイルできません. + +レジスタウィンドウを持つCPUでは,レジスタウィンドウをスタッ +クにフラッシュするアセンブラコードを追加する必要があるかも知 +れません. + +* 配布条件 + +作者は以下の条件のもとにrubyを配布します. + + + 再配布 + + 配布した状態を維持する限り自由です.変更を行ったものを再 + 配布することを希望する時には作者に連絡してください. + + 変更を行なわないrubyをコンパイルしたバイナリの配布は禁止 + しませんが,バイナリを受け取った人がソースを入手できるよ + うに,ソースの入手法を明示してください. + + + 変更 + + 再配布を行わない限り,いかなる目的であれ自由です.ただし, + 機能拡張やバグ修正は作者へのフィードバックを期待します + (もちろん強制ではありません). + + + 他のプログラムへの引用 + + いかなる目的であれ自由です.ただし,rubyに含まれる他の作 + 者によるコードは,それぞれの作者の意向による制限が加えら + れます.具体的にはgc.c(一部),util.c(一部),regex.[ch], + fnmatch.[ch],glob.c,st.[ch]と./missingディレクトリ下の + ファイル群が該当します. + + + Rubyスクリプトの権利 + + 全てのrubyスクリプトの権利はそれぞれの著作者に属します. + 作者はこれらに関して一切の権利を主張しません.またrubyに + 組み込むための拡張モジュールに関しても同様です. + + + 無保証 + + Rubyは無保証です.作者はrubyをサポートする意志はあります + が,ruby自身のバグあるいはrubyスクリプトのバグなどから発 + 生するいかなる損害に対しても責任を持ちません. + +* 著者 + +コメント,バグレポートその他は matz@netlab.co.jp まで. +------------------------------------------------------- +created at: Thu Aug 3 11:57:36 JST 1995 +Local variables: +mode: indented-text +end: @@ -0,0 +1,4 @@ +* non-blocking open/write for thread +* パッケージまたは大域変数のアクセス制御 +* format機能 +* re-write regex code for speed and copyright diff --git a/array.c b/array.c new file mode 100644 index 0000000000..39e10a35e3 --- /dev/null +++ b/array.c @@ -0,0 +1,1242 @@ +/************************************************ + + array.c - + + $Author$ + $Date$ + created at: Fri Aug 6 09:46:12 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" + +VALUE cArray; + +VALUE rb_to_a(); + +#define ARY_DEFAULT_SIZE 16 + +void +memclear(mem, size) + register VALUE *mem; + register int size; +{ + while (size--) { + *mem++ = Qnil; + } +} + +#define ARY_FREEZE FL_USER1 + +static void +ary_modify(ary) + VALUE ary; +{ + rb_secure(5); + if (FL_TEST(ary, ARY_FREEZE)) { + TypeError("can't modify frozen array"); + } +} + +VALUE +ary_freeze(ary) + VALUE ary; +{ + FL_SET(ary, ARY_FREEZE); + return ary; +} + +static VALUE +ary_frozen_p(ary) + VALUE ary; +{ + if (FL_TEST(ary, ARY_FREEZE)) + return TRUE; + return FALSE; +} + +VALUE +ary_new2(len) + int len; +{ + NEWOBJ(ary, struct RArray); + OBJSETUP(ary, cArray, T_ARRAY); + + ary->len = 0; + ary->capa = len; + if (len == 0) + ary->ptr = 0; + else { + ary->ptr = ALLOC_N(VALUE, len); + memclear(ary->ptr, len); + } + + return (VALUE)ary; +} + +VALUE +ary_new() +{ + return ary_new2(ARY_DEFAULT_SIZE); +} + +#include <varargs.h> + +VALUE +ary_new3(n, va_alist) + int n; + va_dcl +{ + va_list ar; + struct RArray* ary; + int i; + + if (n < 0) { + IndexError("Negative number of items(%d)", n); + } + ary = (struct RArray*)ary_new2(n<ARY_DEFAULT_SIZE?ARY_DEFAULT_SIZE:n); + + va_start(ar); + for (i=0; i<n; i++) { + ary->ptr[i] = va_arg(ar, VALUE); + } + va_end(ar); + + ary->len = n; + return (VALUE)ary; +} + +VALUE +ary_new4(n, elts) + int n; + VALUE *elts; +{ + struct RArray* ary; + + ary = (struct RArray*)ary_new2(n); + MEMCPY(ary->ptr, elts, VALUE, n); + ary->len = n; + + return (VALUE)ary; +} + +VALUE +assoc_new(car, cdr) + VALUE car, cdr; +{ + struct RArray* ary; + + ary = (struct RArray*)ary_new2(2); + ary->ptr[0] = car; + ary->ptr[1] = cdr; + ary->len = 2; + + return (VALUE)ary; +} + +static VALUE +ary_s_new(argc, argv, class) + int argc; + VALUE *argv; + VALUE class; +{ + VALUE size; + NEWOBJ(ary, struct RArray); + OBJSETUP(ary, class, T_ARRAY); + + rb_scan_args(argc, argv, "01", &size); + ary->len = 0; + ary->capa = NIL_P(size)?ARY_DEFAULT_SIZE:NUM2INT(size); + ary->ptr = ALLOC_N(VALUE, ary->capa); + memclear(ary->ptr, ary->capa); + + return (VALUE)ary; +} + +static VALUE +ary_s_create(argc, argv, class) + int argc; + VALUE *argv; + VALUE class; +{ + NEWOBJ(ary, struct RArray); + OBJSETUP(ary, class, T_ARRAY); + + ary->len = argc; + ary->capa = argc; + if (argc == 0) { + ary->ptr = 0; + } + else { + ary->ptr = ALLOC_N(VALUE, argc); + MEMCPY(ary->ptr, argv, VALUE, argc); + } + + return (VALUE)ary; +} + +void +ary_store(ary, idx, val) + struct RArray *ary; + int idx; + VALUE val; +{ + ary_modify(ary); + if (idx < 0) { + IndexError("negative index for array"); + } + + if (idx >= ary->capa) { + ary->capa = idx + ARY_DEFAULT_SIZE; + REALLOC_N(ary->ptr, VALUE, ary->capa); + } + if (idx > ary->len) { + memclear(ary->ptr+ary->len, idx-ary->len+1); + } + + if (idx >= ary->len) { + ary->len = idx + 1; + } + ary->ptr[idx] = val; +} + +VALUE +ary_push(ary, item) + struct RArray *ary; + VALUE item; +{ + ary_store(ary, ary->len, item); + return (VALUE)ary; +} + +static VALUE +ary_push_method(argc, argv, ary) + int argc; + VALUE *argv; + struct RArray *ary; +{ + while (argc--) { + ary_store(ary, ary->len, *argv++); + } + return (VALUE)ary; +} + +VALUE +ary_pop(ary) + struct RArray *ary; +{ + if (ary->len == 0) return Qnil; + if (ary->len * 10 < ary->capa && ary->capa > ARY_DEFAULT_SIZE) { + ary->capa = ary->len * 2; + REALLOC_N(ary->ptr, VALUE, ary->capa); + } + return ary->ptr[--ary->len]; +} + +VALUE +ary_shift(ary) + struct RArray *ary; +{ + VALUE top; + + if (ary->len == 0) return Qnil; + + top = ary->ptr[0]; + ary->len--; + + /* sliding items */ + MEMMOVE(ary->ptr, ary->ptr+1, VALUE, ary->len); + if (ary->len * 10 < ary->capa && ary->capa > ARY_DEFAULT_SIZE) { + ary->capa = ary->len * 2; + REALLOC_N(ary->ptr, VALUE, ary->capa); + } + + return top; +} + +VALUE +ary_unshift(ary, item) + struct RArray *ary; + int item; +{ + ary_modify(ary); + if (ary->len >= ary->capa) { + ary->capa+=ARY_DEFAULT_SIZE; + REALLOC_N(ary->ptr, VALUE, ary->capa); + } + + /* sliding items */ + MEMMOVE(ary->ptr+1, ary->ptr, VALUE, ary->len); + + ary->len++; + return ary->ptr[0] = item; +} + +VALUE +ary_entry(ary, offset) + struct RArray *ary; + int offset; +{ + if (ary->len == 0) return Qnil; + + if (offset < 0) { + offset = ary->len + offset; + } + if (offset < 0 || ary->len <= offset) { + return Qnil; + } + + return ary->ptr[offset]; +} + +static VALUE +ary_subseq(ary, beg, len) + struct RArray *ary; + int beg, len; +{ + struct RArray *ary2; + + if (beg < 0) { + beg = ary->len + beg; + if (beg < 0) beg = 0; + } + if (len < 0) { + IndexError("negative length %d", ary->len); + } + if (len == 0) { + return ary_new2(0); + } + if (beg + len > ary->len) { + len = ary->len - beg; + } + + ary2 = (struct RArray*)ary_new2(len); + MEMCPY(ary2->ptr, ary->ptr+beg, VALUE, len); + ary2->len = len; + + return (VALUE)ary2; +} + +static VALUE +beg_len(range, begp, lenp, len) + VALUE range; + int *begp, *lenp; + int len; +{ + int beg, end; + + if (!range_beg_end(range, &beg, &end)) return FALSE; + + if ((beg > 0 && end > 0 || beg < 0 && end < 0) && beg > end) { + IndexError("end smaller than beg [%d..%d]", beg, end); + } + + if (beg < 0) { + beg = len + beg; + if (beg < 0) beg = 0; + } + *begp = beg; + if (beg > len) { + *lenp = 0; + } + else { + if (end < 0) { + end = len + end; + if (end < 0) end = -1; + } + if (end > len) end = len; + if (beg > end) { + *lenp = 0; + } + else { + *lenp = end - beg +1; + } + } + return TRUE; +} + +static VALUE +ary_aref(argc, argv, ary) + int argc; + VALUE *argv; + struct RArray *ary; +{ + VALUE arg1, arg2; + int beg, len; + + if (rb_scan_args(argc, argv, "11", &arg1, &arg2) == 2) { + beg = NUM2INT(arg1); + len = NUM2INT(arg2); + if (len <= 0) { + return ary_new(); + } + return ary_subseq(ary, beg, len); + } + + /* special case - speeding up */ + if (FIXNUM_P(arg1)) { + return ary_entry(ary, FIX2INT(arg1)); + } + else { + /* check if idx is Range */ + if (beg_len(arg1, &beg, &len, ary->len)) { + return ary_subseq(ary, beg, len); + } + } + if (TYPE(arg1) == T_BIGNUM) { + IndexError("index too big"); + } + return ary_entry(ary, NUM2INT(arg1)); +} + +static VALUE +ary_index(ary, val) + struct RArray *ary; + VALUE val; +{ + int i; + + for (i=0; i<ary->len; i++) { + if (rb_equal(ary->ptr[i], val)) + return INT2FIX(i); + } + return Qnil; +} + +static VALUE +ary_indexes(ary, args) + struct RArray *ary, *args; +{ + VALUE *p, *pend; + VALUE new_ary; + int i = 0; + + if (!args || NIL_P(args)) { + return ary_new2(0); + } + + new_ary = ary_new2(args->len); + + p = args->ptr; pend = p + args->len; + while (p < pend) { + ary_store(new_ary, i++, ary_entry(ary, NUM2INT(*p))); + p++; + } + return new_ary; +} + +static void +ary_replace(ary, beg, len, rpl) + struct RArray *ary, *rpl; + int beg, len; +{ + ary_modify(ary); + if (TYPE(rpl) != T_ARRAY) { + rpl = (struct RArray*)rb_to_a(rpl); + } + if (beg < 0) { + beg = ary->len + beg; + if (beg < 0) beg = 0; + } + if (beg >= ary->len) { + len = beg + rpl->len; + if (len >= ary->capa) { + ary->capa=len; + REALLOC_N(ary->ptr, VALUE, ary->capa); + } + memclear(ary->ptr+ary->len, beg-ary->len); + MEMCPY(ary->ptr+beg, rpl->ptr, VALUE, rpl->len); + ary->len = len; + } + else { + int alen; + + if (beg + len > ary->len) { + len = ary->len - beg; + } + if (len < 0) { + IndexError("negative length %d", ary->len); + } + + alen = ary->len + rpl->len - len; + if (alen >= ary->capa) { + ary->capa=alen; + REALLOC_N(ary->ptr, VALUE, ary->capa); + } + + if (len != RARRAY(rpl)->len) { + MEMMOVE(ary->ptr+beg+rpl->len, ary->ptr+beg+len, + VALUE, ary->len-(beg+len)); + ary->len = alen; + } + MEMCPY(ary->ptr+beg, rpl->ptr, VALUE, rpl->len); + } +} + +static VALUE +ary_aset(argc, argv, ary) + int argc; + VALUE *argv; + struct RArray *ary; +{ + VALUE arg1, arg2; + struct RArray *arg3; + int offset; + int beg, len; + + if (rb_scan_args(argc, argv, "21", &arg1, &arg2, &arg3) == 3) { + beg = NUM2INT(arg1); + len = NUM2INT(arg2); + ary_replace(ary, beg, len, arg3); + return (VALUE)arg3; + } + else if (FIXNUM_P(arg1)) { + offset = FIX2INT(arg1); + goto fixnum; + } + else if (beg_len(arg1, &beg, &len, ary->len)) { + /* check if idx is Range */ + ary_replace(ary, beg, len, arg2); + return arg2; + } + if (TYPE(arg1) == T_BIGNUM) { + IndexError("index too big"); + } + + offset = NUM2INT(arg1); + fixnum: + if (offset < 0) { + offset = ary->len + offset; + } + ary_store(ary, offset, arg2); + return arg2; +} + +VALUE +ary_each(ary) + struct RArray *ary; +{ + int i; + + for (i=0; i<ary->len; i++) { + rb_yield(ary->ptr[i]); + } + return Qnil; +} + +static VALUE +ary_each_index(ary) + struct RArray *ary; +{ + int i; + + for (i=0; i<ary->len; i++) { + rb_yield(INT2FIX(i)); + } + return Qnil; +} + +static VALUE +ary_reverse_each(ary) + struct RArray *ary; +{ + int len = ary->len; + + while (len--) { + rb_yield(ary->ptr[len]); + } + return Qnil; +} + +static VALUE +ary_length(ary) + struct RArray *ary; +{ + return INT2FIX(ary->len); +} + +static VALUE +ary_empty_p(ary) + struct RArray *ary; +{ + if (ary->len == 0) + return TRUE; + return FALSE; +} + +static VALUE +ary_clone(ary) + struct RArray *ary; +{ + VALUE ary2 = ary_new2(ary->len); + + CLONESETUP(ary2, ary); + MEMCPY(RARRAY(ary2)->ptr, ary->ptr, VALUE, ary->len); + RARRAY(ary2)->len = ary->len; + return ary2; +} + +extern VALUE OFS; + +VALUE +ary_join(ary, sep) + struct RArray *ary; + struct RString *sep; +{ + int i; + VALUE result, tmp; + if (ary->len == 0) return str_new(0, 0); + + switch (TYPE(ary->ptr[0])) { + case T_STRING: + result = str_dup(ary->ptr[0]); + break; + case T_ARRAY: + result = ary_join(ary->ptr[0], sep); + break; + default: + result = obj_as_string(ary->ptr[0]); + break; + } + + for (i=1; i<ary->len; i++) { + tmp = ary->ptr[i]; + switch (TYPE(tmp)) { + case T_STRING: + break; + case T_ARRAY: + tmp = ary_join(tmp, sep); + break; + default: + tmp = obj_as_string(tmp); + } + if (!NIL_P(sep)) str_cat(result, sep->ptr, sep->len); + str_cat(result, RSTRING(tmp)->ptr, RSTRING(tmp)->len); + if (str_tainted(tmp)) str_taint(result); + } + + return result; +} + +static VALUE +ary_join_method(argc, argv, ary) + int argc; + VALUE *argv; + struct RArray *ary; +{ + VALUE sep; + + rb_scan_args(argc, argv, "01", &sep); + if (NIL_P(sep)) sep = OFS; + if (!NIL_P(sep)) Check_Type(sep, T_STRING); + + return ary_join(ary, sep); +} + +VALUE +ary_to_s(ary) + VALUE ary; +{ + VALUE str = ary_join(ary, OFS); + if (NIL_P(str)) return str_new(0, 0); + return str; +} + +VALUE +ary_print_on(ary, port) + struct RArray *ary; + VALUE port; +{ + int i; + + for (i=0; i<ary->len; i++) { + if (!NIL_P(OFS) && i>0) { + io_write(port, OFS); + } + io_write(port, ary->ptr[i]); + } + return port; +} + +static VALUE +ary_inspect(ary) + struct RArray *ary; +{ + int i, len; + VALUE s, str; + + if (ary->len == 0) return str_new2("[]"); + str = str_new2("["); + len = 1; + + for (i=0; i<ary->len; i++) { + s = rb_inspect(ary->ptr[i]); + if (i > 0) str_cat(str, ", ", 2); + str_cat(str, RSTRING(s)->ptr, RSTRING(s)->len); + len += RSTRING(s)->len + 2; + } + str_cat(str, "]", 1); + + return str; +} + +static VALUE +ary_to_a(ary) + VALUE ary; +{ + return ary; +} + +VALUE +rb_to_a(obj) + VALUE obj; +{ + if (TYPE(obj) == T_ARRAY) return obj; + obj = rb_funcall(obj, rb_intern("to_a"), 0); + if (TYPE(obj) != T_ARRAY) { + Bug("`to_a' did not return Array"); + } + return obj; +} + +VALUE +ary_reverse(ary) + struct RArray *ary; +{ + VALUE *p1, *p2; + VALUE tmp; + + p1 = ary->ptr; + p2 = p1 + ary->len - 1; /* points last item */ + + while (p1 < p2) { + tmp = *p1; + *p1 = *p2; + *p2 = tmp; + p1++; p2--; + } + + return (VALUE)ary; +} + +static VALUE +ary_reverse_method(ary) + struct RArray *ary; +{ + return ary_reverse(ary_clone(ary)); +} + +static ID cmp; + +static int +sort_1(a, b) + VALUE *a, *b; +{ + VALUE retval = rb_yield(assoc_new(*a, *b)); + return NUM2INT(retval); +} + +static int +sort_2(a, b) + VALUE *a, *b; +{ + VALUE retval; + + if (FIXNUM_P(*a)) { + if (FIXNUM_P(*b)) return *a - *b; + } + else if (TYPE(*a) == T_STRING) { + if (TYPE(*b) == T_STRING) return str_cmp(*a, *b); + } + + retval = rb_funcall(*a, cmp, 1, *b); + return NUM2INT(retval); +} + +VALUE +ary_sort_bang(ary) + struct RArray *ary; +{ + ary_modify(ary); + qsort(ary->ptr, ary->len, sizeof(VALUE), iterator_p()?sort_1:sort_2); + return (VALUE)ary; +} + +VALUE +ary_sort(ary) + VALUE ary; +{ + return ary_sort_bang(ary_clone(ary)); +} + +VALUE +ary_delete(ary, item) + struct RArray *ary; + VALUE item; +{ + int i1, i2; + + ary_modify(ary); + for (i1 = i2 = 0; i1 < ary->len; i1++) { + if (rb_equal(ary->ptr[i1], item)) continue; + if (i1 != i2) { + ary->ptr[i2] = ary->ptr[i1]; + } + i2++; + } + if (ary->len == i2) { + if (iterator_p()) rb_yield(item); + return Qnil; + } + else { + ary->len = i2; + } + + return item; +} + +VALUE +ary_delete_at(ary, at) + struct RArray *ary; + VALUE at; +{ + int i1, i2, pos; + VALUE del = Qnil; + + ary_modify(ary); + pos = NUM2INT(at); + for (i1 = i2 = 0; i1 < ary->len; i1++) { + if (i1 == pos) { + del = ary->ptr[i1]; + continue; + } + if (i1 != i2) { + ary->ptr[i2] = ary->ptr[i1]; + } + i2++; + } + ary->len = i2; + + return del; +} + +static VALUE +ary_delete_if(ary) + struct RArray *ary; +{ + int i1, i2; + + ary_modify(ary); + for (i1 = i2 = 0; i1 < ary->len; i1++) { + if (rb_yield(ary->ptr[i1])) continue; + if (i1 != i2) { + ary->ptr[i2] = ary->ptr[i1]; + } + i2++; + } + ary->len = i2; + + return (VALUE)ary; +} + +#if 0 +static VALUE +ary_replace(ary) + struct RArray *ary; +{ + int i; + + for (i = 0; i < ary->len; i++) { + ary->ptr[i] = rb_yield(ary->ptr[i]); + } + + return (VALUE)ary; +} +#endif + +static VALUE +ary_clear(ary) + struct RArray *ary; +{ + ary->len = 0; + if (ARY_DEFAULT_SIZE*3 < ary->capa) { + ary->capa = ARY_DEFAULT_SIZE * 2; + REALLOC_N(ary->ptr, VALUE, ary->capa); + } + return (VALUE)ary; +} + +static VALUE +ary_fill(argc, argv, ary) + int argc; + VALUE *argv; + struct RArray *ary; +{ + VALUE item, arg1, arg2; + int beg, len, end; + VALUE *p, *pend; + + rb_scan_args(argc, argv, "12", &item, &arg1, &arg2); + if (NIL_P(arg2) && beg_len(arg1, &beg, &len, ary->len)) { + /* beg and len set already */ + } + else { + beg = NUM2INT(arg1); + if (beg < 0) { + beg = ary->len + beg; + if (beg < 0) beg = 0; + } + if (arg2) { + len = NUM2INT(arg2); + } + else { + len = ary->len - beg; + } + } + end = beg + len; + if (end > ary->len) { + if (end >= ary->capa) { + ary->capa=end; + REALLOC_N(ary->ptr, VALUE, ary->capa); + } + if (beg > ary->len) { + memclear(ary->ptr+ary->len, end-ary->len); + } + ary->len = end; + } + p = ary->ptr + beg; pend = p + len; + + while (p < pend) { + *p++ = item; + } + return (VALUE)ary; +} + +VALUE +ary_plus(x, y) + struct RArray *x, *y; +{ + struct RArray *z; + + if (TYPE(y) != T_ARRAY) { + return ary_plus(x, rb_to_a(y)); + } + + z = (struct RArray*)ary_new2(x->len + y->len); + MEMCPY(z->ptr, x->ptr, VALUE, x->len); + MEMCPY(z->ptr+x->len, y->ptr, VALUE, y->len); + z->len = x->len + RARRAY(y)->len; + return (VALUE)z; +} + +VALUE +ary_concat(x, y) + struct RArray *x, *y; +{ + VALUE *p, *pend; + + if (TYPE(y) != T_ARRAY) { + return ary_concat(x, rb_to_a(y)); + } + + p = y->ptr; + pend = p + y->len; + while (p < pend) { + ary_store(x, x->len, *p); + p++; + } + return (VALUE)x; +} + +static VALUE +ary_times(ary, times) + struct RArray *ary; + VALUE times; +{ + struct RArray *ary2; + int i, len; + + if (TYPE(times) == T_STRING) { + return ary_join(ary, times); + } + + len = NUM2INT(times) * ary->len; + ary2 = (struct RArray*)ary_new2(len); + ary2->len = len; + + if (len < 0) { + ArgError("negative argument"); + } + + for (i=0; i<len; i+=ary->len) { + MEMCPY(ary2->ptr+i, ary->ptr, VALUE, ary->len); + } + + return (VALUE)ary2; +} + +VALUE +ary_assoc(ary, key) + struct RArray *ary; + VALUE key; +{ + VALUE *p, *pend; + + p = ary->ptr; pend = p + ary->len; + while (p < pend) { + if (TYPE(*p) == T_ARRAY + && RARRAY(*p)->len > 1 + && rb_equal(RARRAY(*p)->ptr[0], key)) + return *p; + p++; + } + return Qnil; +} + +VALUE +ary_rassoc(ary, value) + struct RArray *ary; + VALUE value; +{ + VALUE *p, *pend; + + p = ary->ptr; pend = p + ary->len; + while (p < pend) { + if (TYPE(*p) == T_ARRAY + && RARRAY(*p)->len > 1 + && rb_equal(RARRAY(*p)->ptr[1], value)) + return *p; + p++; + } + return Qnil; +} + +static VALUE +ary_equal(ary1, ary2) + struct RArray *ary1, *ary2; +{ + int i; + + if (TYPE(ary2) != T_ARRAY) return FALSE; + if (ary1->len != ary2->len) return FALSE; + for (i=0; i<ary1->len; i++) { + if (!rb_equal(ary1->ptr[i], ary2->ptr[i])) + return FALSE; + } + return TRUE; +} + +static VALUE +ary_eql(ary1, ary2) + struct RArray *ary1, *ary2; +{ + int i; + + if (TYPE(ary2) != T_ARRAY) return FALSE; + if (ary1->len != ary2->len) return FALSE; + for (i=0; i<ary1->len; i++) { + if (!rb_eql(ary1->ptr[i], ary2->ptr[i])) + return FALSE; + } + return TRUE; +} + +static VALUE +ary_hash(ary) + struct RArray *ary; +{ + int h, i; + + h = ary->len; + for (i=0; i<ary->len; i++) { + h ^= rb_hash(ary->ptr[i]); + } + return INT2FIX(h); +} + +VALUE +ary_includes(ary, item) + struct RArray *ary; + VALUE item; +{ + int i; + for (i=0; i<ary->len; i++) { + if (rb_equal(ary->ptr[i], item)) { + return TRUE; + } + } + return FALSE; +} + +static VALUE +ary_diff(ary1, ary2) + struct RArray *ary1, *ary2; +{ + VALUE ary3; + int i; + + Check_Type(ary2, T_ARRAY); + ary3 = ary_new(); + for (i=0; i<ary1->len; i++) { + if (ary_includes(ary2, ary1->ptr[i])) continue; + if (ary_includes(ary3, ary1->ptr[i])) continue; + ary_push(ary3, ary1->ptr[i]); + } + return ary3; +} + +static VALUE +ary_and(ary1, ary2) + struct RArray *ary1, *ary2; +{ + VALUE ary3; + int i; + + Check_Type(ary2, T_ARRAY); + ary3 = ary_new(); + for (i=0; i<ary1->len; i++) { + if (ary_includes(ary2, ary1->ptr[i]) + && !ary_includes(ary3, ary1->ptr[i])) { + ary_push(ary3, ary1->ptr[i]); + } + } + return ary3; +} + +static VALUE +ary_or(ary1, ary2) + struct RArray *ary1, *ary2; +{ + VALUE ary3; + int i; + + if (TYPE(ary2) != T_ARRAY) { + if (ary_includes(ary1, ary2)) return (VALUE)ary1; + else return ary_plus(ary1, ary2); + } + + ary3 = ary_new(); + for (i=0; i<ary1->len; i++) { + if (!ary_includes(ary3, ary1->ptr[i])) + ary_push(ary3, ary1->ptr[i]); + } + for (i=0; i<ary2->len; i++) { + if (!ary_includes(ary3, ary2->ptr[i])) + ary_push(ary3, ary2->ptr[i]); + } + return ary3; +} + +static VALUE +ary_compact_bang(ary) + struct RArray *ary; +{ + VALUE *p, *t, *end; + + ary_modify(ary); + p = t = ary->ptr; + end = p + ary->len; + while (t < end) { + if (NIL_P(*t)) t++; + else *p++ = *t++; + } + ary->len = ary->capa = (p - ary->ptr); + REALLOC_N(ary->ptr, VALUE, ary->len); + + return (VALUE)ary; +} + +static VALUE +ary_compact(ary) + struct RArray *ary; +{ + return ary_compact_bang(ary_clone(ary)); +} + +static VALUE +ary_nitems(ary) + struct RArray *ary; +{ + int n = 0; + VALUE *p, *pend; + + p = ary->ptr; + pend = p + ary->len; + while (p < pend) { + if (!NIL_P(*p)) n++; + p++; + } + return INT2FIX(n); +} + +extern VALUE mEnumerable; + +void +Init_Array() +{ + cArray = rb_define_class("Array", cObject); + rb_include_module(cArray, mEnumerable); + + rb_define_singleton_method(cArray, "new", ary_s_new, -1); + rb_define_singleton_method(cArray, "[]", ary_s_create, -1); + rb_define_method(cArray, "to_s", ary_to_s, 0); + rb_define_method(cArray, "inspect", ary_inspect, 0); + rb_define_method(cArray, "to_a", ary_to_a, 0); + + rb_define_method(cArray, "freeze", ary_freeze, 0); + rb_define_method(cArray, "frozen?", ary_frozen_p, 0); + + rb_define_method(cArray, "==", ary_equal, 1); + rb_define_method(cArray, "eql?", ary_eql, 1); + rb_define_method(cArray, "hash", ary_hash, 0); + + rb_define_method(cArray, "[]", ary_aref, -1); + rb_define_method(cArray, "[]=", ary_aset, -1); + rb_define_method(cArray, "concat", ary_concat, 1); + rb_define_method(cArray, "<<", ary_push, 1); + rb_define_method(cArray, "push", ary_push_method, -1); + rb_define_method(cArray, "pop", ary_pop, 0); + rb_define_method(cArray, "shift", ary_shift, 0); + rb_define_method(cArray, "unshift", ary_unshift, 1); + rb_define_method(cArray, "each", ary_each, 0); + rb_define_method(cArray, "each_index", ary_each_index, 0); + rb_define_method(cArray, "reverse_each", ary_reverse_each, 0); + rb_define_method(cArray, "length", ary_length, 0); + rb_define_alias(cArray, "size", "length"); + rb_define_method(cArray, "empty?", ary_empty_p, 0); + rb_define_method(cArray, "index", ary_index, 1); + rb_define_method(cArray, "indexes", ary_indexes, -2); + rb_define_method(cArray, "clone", ary_clone, 0); + rb_define_method(cArray, "join", ary_join_method, -1); + rb_define_method(cArray, "reverse", ary_reverse_method, 0); + rb_define_method(cArray, "reverse!", ary_reverse, 0); + rb_define_method(cArray, "sort", ary_sort, 0); + rb_define_method(cArray, "sort!", ary_sort_bang, 0); + rb_define_method(cArray, "delete", ary_delete, 1); + rb_define_method(cArray, "delete_at", ary_delete_at, 1); + rb_define_method(cArray, "delete_if", ary_delete_if, 0); +#if 0 + rb_define_method(cArray, "replace", ary_replace, 0); +#endif + rb_define_method(cArray, "clear", ary_clear, 0); + rb_define_method(cArray, "fill", ary_fill, -1); + rb_define_method(cArray, "include?", ary_includes, 1); + + rb_define_method(cArray, "assoc", ary_assoc, 1); + rb_define_method(cArray, "rassoc", ary_rassoc, 1); + + rb_define_method(cArray, "+", ary_plus, 1); + rb_define_method(cArray, "*", ary_times, 1); + + rb_define_method(cArray, "-", ary_diff, 1); + rb_define_method(cArray, "&", ary_and, 1); + rb_define_method(cArray, "|", ary_or, 1); + + rb_define_method(cArray, "compact", ary_compact, 0); + rb_define_method(cArray, "compact!", ary_compact_bang, 0); + rb_define_method(cArray, "nitems", ary_nitems, 0); + + cmp = rb_intern("<=>"); +} diff --git a/bignum.c b/bignum.c new file mode 100644 index 0000000000..7b9581cbd2 --- /dev/null +++ b/bignum.c @@ -0,0 +1,1249 @@ +/************************************************ + + bignum.c - + + $Author$ + $Date$ + created at: Fri Jun 10 00:48:55 JST 1994 + +************************************************/ + +#include "ruby.h" +#include <ctype.h> +#include <math.h> + +extern VALUE cInteger; +VALUE cBignum; + +#define BDIGITS(x) RBIGNUM(x)->digits +#define BITSPERDIG (sizeof(USHORT)*CHAR_BIT) +#define BIGRAD (1L << BITSPERDIG) +#define DIGSPERLONG ((UINT)(sizeof(long)/sizeof(USHORT))) +#define BIGUP(x) ((unsigned long)(x) << BITSPERDIG) +#define BIGDN(x) ((x) >> BITSPERDIG) +#define BIGLO(x) ((x) & (BIGRAD-1)) + +static VALUE +bignew_1(class, len, sign) + VALUE class; + UINT len; + char sign; +{ + NEWOBJ(big, struct RBignum); + OBJSETUP(big, cBignum, T_BIGNUM); + big->sign = sign; + big->len = len; + BDIGITS(big) = ALLOC_N(USHORT, len); + + return (VALUE)big; +} + +#define bignew(len,sign) bignew_1(cBignum,len,sign) + +VALUE +big_clone(x) + struct RBignum *x; +{ + VALUE z = bignew_1(CLASS_OF(x), x->len, x->sign); + + MEMCPY(BDIGITS(z), BDIGITS(x), USHORT, x->len); + return (VALUE)z; +} + +void +big_2comp(x) /* get 2's complement */ + struct RBignum *x; +{ + UINT i = x->len; + USHORT *ds = BDIGITS(x); + long num; + + while (i--) ds[i] = ~ds[i]; + i = 0; num = 1; + do { + num += (long)ds[i]; + ds[i++] = BIGLO(num); + num = BIGDN(num); + } while (i < x->len); + if (ds[0] == 1 || ds[0] == 0) { + for (i=1;i<x->len;i++) { + if (ds[i] != 0) return; + } + REALLOC_N(BDIGITS(x), USHORT, x->len++); + ds = BDIGITS(x); + ds[x->len-1] = 1; + } +} + +static VALUE +bignorm(x) + struct RBignum *x; +{ + UINT len = x->len; + USHORT *ds = BDIGITS(x); + + while (len-- && !ds[len]) ; + x->len = ++len; + + if (len*sizeof(USHORT) < sizeof(VALUE) || + (len*sizeof(USHORT) == sizeof(VALUE) && + ds[sizeof(VALUE)/sizeof(USHORT)-1] <= 0x3fff)) { + long num = 0; + while (len--) { + num = BIGUP(num) + ds[len]; + } + if (x->sign) { + if (POSFIXABLE(num)) return INT2FIX(num); + } + else if (NEGFIXABLE(-num)) return INT2FIX(-num); + } + return (VALUE)x; +} + +VALUE +big_norm(x) + VALUE x; +{ + return bignorm(x); +} + +VALUE +uint2big(n) + UINT n; +{ + UINT i = 0; + USHORT *digits; + struct RBignum *big; + + i = 0; + big = (struct RBignum*)bignew(DIGSPERLONG, 1); + digits = BDIGITS(big); + while (i < DIGSPERLONG) { + digits[i++] = BIGLO(n); + n = BIGDN(n); + } + + i = DIGSPERLONG; + while (i-- && !digits[i]) ; + big->len = i+1; + return (VALUE)big; +} + +VALUE +int2big(n) + int n; +{ + int neg = 0; + struct RBignum *big; + + if (n < 0) { + n = -n; + neg = 1; + } + big = (struct RBignum*)uint2big(n); + if (neg) { + big->sign = 0; + } + return (VALUE)big; +} + +VALUE +uint2inum(n) + UINT n; +{ + if (POSFIXABLE(n)) return INT2FIX(n); + return uint2big(n); +} + +VALUE +int2inum(n) + int n; +{ + if (FIXABLE(n)) return INT2FIX(n); + return int2big(n); +} + +VALUE +str2inum(str, base) + UCHAR *str; + int base; +{ + char sign = 1, c; + unsigned long num; + UINT len, blen = 1, i; + VALUE z; + USHORT *zds; + + while (isspace(*str)) str++; + if (*str == '-') { + str++; + sign = 0; + } + if (base == 0) { + if (*str == '0') { + str++; + if (*str == 'x' || *str == 'X') { + str++; + base = 16; + } + else { + base = 8; + } + if (*str == '\0') return INT2FIX(0); + } + else { + base = 10; + } + } + len = strlen(str); + if (base == 8) { + len = 3*len*sizeof(char); + } + else { /* base == 10 or 16 */ + len = 4*len*sizeof(char); + } + + if (len <= (sizeof(VALUE)*CHAR_BIT)) { + UINT val = strtoul((char*)str, 0, base); + + if (POSFIXABLE(val)) { + if (sign) return INT2FIX(val); + else { + int result = -(int)val; + return INT2FIX(result); + } + } + else { + VALUE big = uint2big(val); + RBIGNUM(big)->sign = sign; + return big; + } + } + len = (len/(sizeof(USHORT)*CHAR_BIT))+1; + + z = bignew(len, sign); + zds = BDIGITS(z); + for (i=len;i--;) zds[i]=0; + while (c = *str++) { + switch (c) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + c = c - '0'; + break; + case 'a': case 'b': case 'c': + case 'd': case 'e': case 'f': + c = c - 'a' + 10; + break; + case 'A': case 'B': case 'C': + case 'D': case 'E': case 'F': + c = c - 'A' + 10; + break; + default: + c = base; + break; + } + if (c >= base) break; + i = 0; + num = c; + for (;;) { + while (i<blen) { + num += zds[i]*base; + zds[i++] = BIGLO(num); + num = BIGDN(num); + } + if (num) { + blen++; + continue; + } + break; + } + } + return bignorm(z); +} + +static char hexmap[] = "0123456789abcdef"; +VALUE +big2str(x, base) + struct RBignum *x; + int base; +{ + VALUE t; + USHORT *ds; + UINT i, j, hbase; + VALUE ss; + UCHAR *s, c; + + if (FIXNUM_P(x)) { + return fix2str(x, base); + } + i = x->len; + if (i == 0) return str_new2("0"); + if (base == 10) { + j = (sizeof(USHORT)/sizeof(char)*CHAR_BIT*i*241L)/800+2; + hbase = 10000; + } + else if (base == 16) { + j = (sizeof(USHORT)/sizeof(char)*CHAR_BIT*i)/4+2; + hbase = 0x10000; + } + else if (base == 8) { + j = (sizeof(USHORT)/sizeof(char)*CHAR_BIT*i)+2; + hbase = 010000; + } + else if (base == 2) { + j = (sizeof(USHORT)*CHAR_BIT*i)+2; + hbase = 020; + } + else { + j = 0; + hbase = 0; + Fail("bignum cannot treat base %d", base); + } + + t = big_clone(x); + ds = BDIGITS(t); + ss = str_new(0, j); + s = RSTRING(ss)->ptr; + + s[0] = x->sign ? '+' : '-'; + while (i && j) { + int k = i; + unsigned long num = 0; + while (k--) { + num = BIGUP(num) + ds[k]; + ds[k] = num / hbase; + num %= hbase; + } + if (ds[i-1] == 0) i--; + k = 4; + while (k--) { + c = num % base; + s[--j] = hexmap[(int)c]; + num /= base; + if (i == 0 && num == 0) break; + } + } + while (s[j] == '0') j++; + RSTRING(ss)->len -= x->sign?j:j-1; + memmove(x->sign?s:s+1, s+j, RSTRING(ss)->len); + s[RSTRING(ss)->len] = '\0'; + + return ss; +} + +static VALUE +big_to_s(x) + struct RBignum *x; +{ + return big2str(x, 10); +} + +int +big2int(x) + struct RBignum *x; +{ + unsigned long num; + UINT len = x->len; + USHORT *ds; + + if (len > sizeof(long)/sizeof(USHORT)) + ArgError("Bignum too big to convert into fixnum"); + ds = BDIGITS(x); + num = 0; + while (len--) { + num = BIGUP(num); + num += ds[len]; + } + if (!x->sign) return -num; + return num; +} + +VALUE +big_to_i(x) + VALUE x; +{ + return bignorm(x); +} + +VALUE +dbl2big(d) + double d; +{ + UINT i = 0; + long c; + USHORT *digits; + VALUE z; + double u = (d < 0)?-d:d; + + while (0 != (long)u) { + u /= (double)(BIGRAD); + i++; + } + z = bignew(i, d>=0); + digits = BDIGITS(z); + while (i--) { + u *= BIGRAD; + c = (long)u; + u -= c; + digits[i] = c; + } + + return bignorm(z); +} + +double +big2dbl(x) + struct RBignum *x; +{ + double d = 0.0; + UINT i = x->len; + USHORT *ds = BDIGITS(x); + + while (i--) { + d = ds[i] + BIGRAD*d; + } + if (!x->sign) d = -d; + return d; +} + +VALUE +big_to_f(x) + VALUE x; +{ + return float_new(big2dbl(x)); +} + +static VALUE +big_cmp(x, y) + struct RBignum *x, *y; +{ + int xlen = x->len; + + switch (TYPE(y)) { + case T_FIXNUM: + y = (struct RBignum*)int2big(FIX2INT(y)); + break; + + case T_BIGNUM: + break; + + default: + return num_coerce_bin(x, y); + } + + if (x->sign > y->sign) return INT2FIX(1); + if (x->sign < y->sign) return INT2FIX(-1); + if (xlen < y->len) + return (x->sign) ? INT2FIX(-1) : INT2FIX(1); + if (xlen > y->len) + return (x->sign) ? INT2FIX(1) : INT2FIX(-1); + + while(xlen-- && (BDIGITS(x)[xlen]==BDIGITS(y)[xlen])); + if (-1 == xlen) return INT2FIX(0); + return (BDIGITS(x)[xlen] > BDIGITS(y)[xlen]) ? + (x->sign ? INT2FIX(1) : INT2FIX(-1)) : + (x->sign ? INT2FIX(-1) : INT2FIX(1)); +} + +static VALUE +big_eq(x, y) + VALUE x, y; +{ + if (big_cmp(x, y) == INT2FIX(0)) return TRUE; + return FALSE; +} + +static VALUE +big_uminus(x) + struct RBignum *x; +{ + VALUE z = big_clone(x); + + RBIGNUM(z)->sign = !x->sign; + + return bignorm(z); +} + +static VALUE +big_neg(x) + struct RBignum *x; +{ + VALUE z = big_clone(x); + UINT i = x->len; + USHORT *ds = BDIGITS(z); + + if (!x->sign) big_2comp(z); + while (i--) ds[i] = ~ds[i]; + if (x->sign) big_2comp(z); + RBIGNUM(z)->sign = !RBIGNUM(z)->sign; + + return bignorm(z); +} + +static VALUE +bigsub(x, y) + struct RBignum *x, *y; +{ + struct RBignum *z = 0; + USHORT *zds; + long num; + UINT i; + + i = x->len; + /* if x is larger than y, swap */ + if (x->len < y->len) { + z = x; x = y; y = z; /* swap x y */ + } + else if (x->len == y->len) { + while (i > 0) { + i--; + if (BDIGITS(x)[i] > BDIGITS(y)[i]) { + break; + } + if (BDIGITS(x)[i] < BDIGITS(y)[i]) { + z = x; x = y; y = z; /* swap x y */ + break; + } + } + } + + z = (struct RBignum*)bignew(x->len, (z == 0)?1:0); + zds = BDIGITS(z); + + for (i = 0, num = 0; i < y->len; i++) { + num += (long)BDIGITS(x)[i] - BDIGITS(y)[i]; + zds[i] = BIGLO(num); + num = BIGDN(num); + } + while (num && i < x->len) { + num += BDIGITS(x)[i]; + zds[i++] = BIGLO(num); + num = BIGDN(num); + } + while (i < x->len) { + zds[i] = BDIGITS(x)[i]; + i++; + } + + return bignorm(z); +} + +static VALUE +bigadd(x, y, sign) + struct RBignum *x, *y; + char sign; +{ + struct RBignum *z; + long num; + UINT i, len; + if (x->sign == (y->sign ^ sign)) { + if (y->sign == sign) return bigsub(y, x); + return bigsub(x, y); + } + + if (x->len > y->len) { + len = x->len + 1; + } + else { + len = y->len + 1; + } + z = (struct RBignum*)bignew(len, sign==y->sign); + + if (x->len > y->len) { + struct RBignum* t = x; x = y; y = t; + } + for (i = 0, num = 0; i < x->len; i++) { + num += (long)(BDIGITS(x)[i]) + BDIGITS(y)[i]; + BDIGITS(z)[i] = BIGLO(num); + num = BIGDN(num); + } + while (num && i < y->len) { + num += BDIGITS(y)[i]; + BDIGITS(z)[i++] = BIGLO(num); + num = BIGDN(num); + } + while (i < y->len) { + BDIGITS(z)[i] = BDIGITS(y)[i]; + i++; + } + BDIGITS(z)[i] = num; + + return bignorm(z); +} + +VALUE +big_plus(x, y) + VALUE x, y; +{ + switch (TYPE(y)) { + case T_FIXNUM: + y = int2big(FIX2INT(y)); + /* fall through */ + case T_BIGNUM: + return bigadd(x, y, 1); + + case T_FLOAT: + return float_new(big2dbl(x) + RFLOAT(y)->value); + + default: + return num_coerce_bin(x, y); + } +} + +VALUE +big_minus(x, y) + VALUE x, y; +{ + switch (TYPE(y)) { + case T_FIXNUM: + y = int2big(FIX2INT(y)); + /* fall through */ + case T_BIGNUM: + return bigadd(x, y, 0); + + case T_FLOAT: + return float_new(big2dbl(x) - RFLOAT(y)->value); + + default: + return num_coerce_bin(x, y); + } +} + +VALUE +big_mul(x, y) + struct RBignum *x, *y; +{ + UINT i = 0, j; + unsigned long n = 0; + VALUE z; + USHORT *zds; + + if (FIXNUM_P(x)) x = (struct RBignum*)int2big(FIX2INT(x)); + switch (TYPE(y)) { + case T_FIXNUM: + y = (struct RBignum*)int2big(FIX2INT(y)); + break; + + case T_BIGNUM: + break; + + case T_FLOAT: + return float_new(big2dbl(x) * RFLOAT(y)->value); + + default: + return num_coerce_bin(x, y); + } + + j = x->len + y->len + 1; + z = bignew(j, x->sign==y->sign); + zds = BDIGITS(z); + while (j--) zds[j] = 0; + for (i = 0; i < x->len; i++) { + unsigned long dd = BDIGITS(x)[i]; + if (dd == 0) continue; + n = 0; + for (j = 0; j < y->len; j++) { + int ee = n + dd * BDIGITS(y)[j]; + n = zds[i + j] + ee; + if (ee) zds[i + j] = BIGLO(n); + n = BIGDN(n); + } + if (n) { + zds[i + j] = n; + } + } + + return bignorm(z); +} + +static void +bigdivmod(x, y, div, mod) + struct RBignum *x, *y; + VALUE *div, *mod; +{ + UINT nx = x->len, ny = y->len, i, j; + VALUE z; + USHORT *xds, *yds, *zds, *tds; + unsigned long t2; + long num; + USHORT dd, q; + + yds = BDIGITS(y); + if (ny == 0 && yds[0] == 0) num_zerodiv(); + if (nx < ny || nx == ny && BDIGITS(x)[nx - 1] < BDIGITS(y)[ny - 1]) { + if (div) *div = INT2FIX(0); + if (mod) *mod = bignorm(x); + return; + } + xds = BDIGITS(x); + if (ny == 1) { + dd = yds[0]; + z = big_clone(x); + zds = BDIGITS(z); + t2 = 0; i = nx; + while (i--) { + t2 = BIGUP(t2) + zds[i]; + zds[i] = t2 / dd; + t2 %= dd; + } + if (div) *div = bignorm(z); + if (mod) { + if (!y->sign) t2 = -t2; + *mod = INT2FIX(t2); + } + return; + } + z = bignew(nx==ny?nx+2:nx+1, x->sign==y->sign); + zds = BDIGITS(z); + if (nx==ny) zds[nx+1] = 0; + while (!yds[ny-1]) ny--; + if ((dd = BIGRAD/(int)(yds[ny-1]+1)) != 1) { + y = (struct RBignum*)big_clone(y); + tds = BDIGITS(y); + j = 0; + num = 0; + while (j<ny) { + num += (unsigned long)yds[j]*dd; + tds[j++] = BIGLO(num); + num = BIGDN(num); + } + yds = tds; + j = 0; + num = 0; + while (j<nx) { + num += (unsigned long)xds[j]*dd; + zds[j++] = BIGLO(num); + num = BIGDN(num); + } + zds[j] = num; + } + else { + zds[nx] = 0; + j = nx; + while (j--) zds[j] = xds[j]; + } + j = nx==ny?nx+1:nx; + do { + if (zds[j] == yds[ny-1]) q = BIGRAD-1; + else q = (BIGUP(zds[j]) + zds[j-1])/yds[ny-1]; + if (q) { + i = 0; num = 0; t2 = 0; + do { /* multiply and subtract */ + int ee; + t2 += (unsigned long)yds[i] * q; + ee = num - BIGLO(t2); + num = zds[j - ny + i] + ee; + if (ee) zds[j - ny + i] = BIGLO(num); + num = BIGDN(num); + t2 = BIGDN(t2); + } while (++i < ny); + num += zds[j - ny + i] - t2; /* borrow from high digit; don't update */ + while (num) { /* "add back" required */ + i = 0; num = 0; q--; + do { + int ee = num + yds[i]; + num = (long) zds[j - ny + i] + ee; + if (ee) zds[j - ny + i] = BIGLO(num); + num = BIGDN(num); + } while (++i < ny); + num--; + } + } + zds[j] = q; + } while (--j >= ny); + if (div) { /* move quotient down in z */ + *div = big_clone(z); + zds = BDIGITS(*div); + j = (nx==ny ? nx+2 : nx+1) - ny; + for (i = 0;i < j;i++) zds[i] = zds[i+ny]; + RBIGNUM(*div)->len = i; + *div = bignorm(*div); + } + if (mod) { /* just normalize remainder */ + *mod = big_clone(z); + if (dd) { + zds = BDIGITS(*mod); + t2 = 0; i = ny; + while(i--) { + t2 = BIGUP(t2) + zds[i]; + zds[i] = t2 / dd; + t2 %= dd; + } + } + RBIGNUM(*mod)->len = ny; + RBIGNUM(*mod)->sign = y->sign; + *mod = bignorm(*mod); + } +} + +static VALUE +big_div(x, y) + VALUE x, y; +{ + VALUE z; + + switch (TYPE(y)) { + case T_FIXNUM: + y = int2big(FIX2INT(y)); + break; + + case T_BIGNUM: + break; + + case T_FLOAT: + return float_new(big2dbl(x) / RFLOAT(y)->value); + + default: + return num_coerce_bin(x, y); + } + bigdivmod(x, y, &z, 0); + + return z; +} + +static VALUE +big_mod(x, y) + VALUE x, y; +{ + VALUE z; + + switch (TYPE(y)) { + case T_FIXNUM: + y = int2big(FIX2INT(y)); + break; + + case T_BIGNUM: + break; + + case T_FLOAT: + y = dbl2big(RFLOAT(y)->value); + break; + + default: + return num_coerce_bin(x, y); + } + bigdivmod(x, y, 0, &z); + + return z; +} + +static VALUE +big_divmod(x, y) + VALUE x, y; +{ + VALUE div, mod; + + switch (TYPE(y)) { + case T_FIXNUM: + y = int2big(FIX2INT(y)); + break; + + case T_FLOAT: + y = dbl2big(RFLOAT(y)->value); + break; + + case T_BIGNUM: + break; + + default: + return num_coerce_bin(x, y); + } + bigdivmod(x, y, &div, &mod); + + return assoc_new(div, mod);; +} + +VALUE +big_pow(x, y) + VALUE x, y; +{ + double d; + VALUE z; + + if (y == INT2FIX(0)) return INT2FIX(1); + switch (TYPE(y)) { + case T_FLOAT: + d = RFLOAT(y)->value; + break; + + case T_BIGNUM: + if (RBIGNUM(y)->sign) goto pos_big; + d = big2dbl(y); + break; + + case T_FIXNUM: + if (FIX2INT(y) > 0) goto pos_big; + d = (double)FIX2INT(y); + break; + + default: + return num_coerce_bin(x, y); + } + return float_new(pow(big2dbl(x), d)); + + pos_big: + z = x; + for (;;) { + y = rb_funcall(y, '-', 1, INT2FIX(1)); + if (y == INT2FIX(0)) break; + while (rb_funcall(y, '%', 1, INT2FIX(2)) == INT2FIX(0)) { + y = rb_funcall(y, '/', 1, INT2FIX(2)); + x = big_mul(x, x); + } + z = big_mul(z, x); + } + return z; +} + +VALUE +big_and(x, y) + struct RBignum *x, *y; +{ + VALUE z; + USHORT *ds1, *ds2, *zds; + UINT i, l1, l2; + char sign; + + if (FIXNUM_P(y)) { + y = (struct RBignum*)int2big(FIX2INT(y)); + } + else { + Check_Type(y, T_BIGNUM); + } + + if (!y->sign) { + y = (struct RBignum*)big_clone(y); + big_2comp(y); + } + if (!x->sign) { + x = (struct RBignum*)big_clone(x); + big_2comp(x); + } + if (x->len > y->len) { + l1 = y->len; + l2 = x->len; + ds1 = BDIGITS(y); + ds2 = BDIGITS(x); + sign = y->sign; + } + else { + l1 = x->len; + l2 = y->len; + ds1 = BDIGITS(x); + ds2 = BDIGITS(y); + sign = x->sign; + } + z = bignew(l2, x->sign && y->sign); + zds = BDIGITS(z); + + for (i=0; i<l1; i++) { + zds[i] = ds1[i] & ds2[i]; + } + for (; i<l2; i++) { + zds[i] = sign?0:ds2[i]; + } + if (!RBIGNUM(z)->sign) big_2comp(z); + return bignorm(z); +} + +VALUE +big_or(x, y) + struct RBignum *x, *y; +{ + VALUE z; + USHORT *ds1, *ds2, *zds; + UINT i, l1, l2; + char sign; + + if (FIXNUM_P(y)) { + y = (struct RBignum*)int2big(FIX2INT(y)); + } + else { + Check_Type(y, T_BIGNUM); + } + + if (!y->sign) { + y = (struct RBignum*)big_clone(y); + big_2comp(y); + } + if (!x->sign) { + x = (struct RBignum*)big_clone(x); + big_2comp(x); + } + if (x->len > y->len) { + l1 = y->len; + l2 = x->len; + ds1 = BDIGITS(y); + ds2 = BDIGITS(x); + sign = y->sign; + } + else { + l1 = x->len; + l2 = y->len; + ds1 = BDIGITS(x); + ds2 = BDIGITS(y); + sign = x->sign; + } + z = bignew(l2, x->sign || y->sign); + zds = BDIGITS(z); + + for (i=0; i<l1; i++) { + zds[i] = ds1[i] | ds2[i]; + } + for (; i<l2; i++) { + zds[i] = sign?ds2[i]:(BIGRAD-1); + } + if (!RBIGNUM(z)->sign) big_2comp(z); + + return bignorm(z); +} + +VALUE +big_xor(x, y) + struct RBignum *x, *y; +{ + VALUE z; + USHORT *ds1, *ds2, *zds; + UINT i, l1, l2; + char sign; + + if (FIXNUM_P(y)) { + y = (struct RBignum*)int2big(FIX2INT(y)); + } + else { + Check_Type(y, T_BIGNUM); + } + + if (!y->sign) { + y = (struct RBignum*)big_clone(y); + big_2comp(y); + } + if (!x->sign) { + x = (struct RBignum*)big_clone(x); + big_2comp(x); + } + if (x->len > y->len) { + l1 = y->len; + l2 = x->len; + ds1 = BDIGITS(y); + ds2 = BDIGITS(x); + sign = y->sign; + } + else { + l1 = x->len; + l2 = y->len; + ds1 = BDIGITS(x); + ds2 = BDIGITS(y); + sign = x->sign; + } + x->sign = x->sign?1:0; + y->sign = y->sign?1:0; + z = bignew(l2, !(x->sign ^ y->sign)); + zds = BDIGITS(z); + + for (i=0; i<l1; i++) { + zds[i] = ds1[i] ^ ds2[i]; + } + for (; i<l2; i++) { + zds[i] = sign?ds2[i]:~ds2[i]; + } + if (!RBIGNUM(z)->sign) big_2comp(z); + + return bignorm(z); +} + +static VALUE big_rshift(); + +VALUE +big_lshift(x, y) + struct RBignum *x; + VALUE y; +{ + USHORT *xds, *zds; + int shift = NUM2INT(y); + UINT s1 = shift/(sizeof(USHORT)*CHAR_BIT); + UINT s2 = shift%(sizeof(USHORT)*CHAR_BIT); + VALUE z; + unsigned long num = 0; + UINT len, i; + + if (shift < 0) return big_rshift(x, INT2FIX(-shift)); + xds = BDIGITS(x); + len = x->len; + z = bignew(len+s1+1, x->sign); + zds = BDIGITS(z); + for (i=0; i<s1; i++) { + *zds++ = 0; + } + for (i=0; i<len; i++) { + num = num | *xds++<<s2; + *zds++ = BIGLO(num); + num = BIGDN(num); + } + *zds = BIGLO(num); + return bignorm(z); +} + +static VALUE +big_rshift(x, y) + struct RBignum *x; + VALUE y; +{ + USHORT *xds, *zds; + int shift = NUM2INT(y); + UINT s1 = shift/(sizeof(USHORT)*CHAR_BIT); + UINT s2 = shift%(sizeof(USHORT)*CHAR_BIT); + VALUE z; + unsigned long num = 0; + UINT i = x->len, j; + + if (shift < 0) return big_lshift(x, INT2FIX(-shift)); + if (s1 > x->len) { + if (x->sign) + return INT2FIX(0); + else + return INT2FIX(-1); + } + xds = BDIGITS(x); + i = x->len; j = i - s1; + z = bignew(j, x->sign); + zds = BDIGITS(z); + while (i--, j--) { + num = (num | xds[i]) >> s2; + zds[j] = BIGLO(num); + num = BIGUP(xds[i]); + } + return bignorm(z); +} + +static VALUE +big_aref(x, y) + struct RBignum *x; + VALUE y; +{ + USHORT *xds; + int shift = NUM2INT(y); + UINT s1, s2; + + if (shift < 0) return INT2FIX(0); + s1 = shift/(sizeof(USHORT)*CHAR_BIT); + s2 = shift%(sizeof(USHORT)*CHAR_BIT); + + if (!x->sign) { + if (s1 >= x->len) return INT2FIX(1); + x = (struct RBignum*)big_clone(x); + big_2comp(x); + } + else { + if (s1 >= x->len) return INT2FIX(0); + } + xds = BDIGITS(x); + if (xds[s1] & (1<<s2)) + return INT2FIX(1); + return INT2FIX(0); +} + +static VALUE +big_hash(x) + struct RBignum *x; +{ + int i, len, key; + USHORT *digits; + + key = 0; digits = BDIGITS(x); + for (i=0,len=x->len; i<x->len; i++) { + key ^= *digits++; + } + return INT2FIX(key); +} + +static VALUE +big_coerce(x, y) + struct RBignum *x; + VALUE y; +{ + if (FIXNUM_P(y)) { + return assoc_new(int2big(FIX2INT(y)), x); + } + else { + TypeError("can't coerce %s to Bignum", rb_class2name(CLASS_OF(y))); + } + /* not reached */ +} + +static VALUE +big_abs(x) + struct RBignum *x; +{ + if (!x->sign) { + x = (struct RBignum*)big_clone(x); + x->sign = 1; + } + return (VALUE)x; +} + +/* !!!warnig!!!! + this is not really a random number!! +*/ + +VALUE +big_rand(max) + struct RBignum *max; +{ + struct RBignum *v; + int len; + + len = max->len; + v = RBIGNUM(bignew(len,1)); + while (len--) { +#ifdef HAVE_RANDOM + BDIGITS(v)[len] = random(); +#else + BDIGITS(v)[len] = rand(); +#endif + } + + return big_mod(v, max); +} + +static VALUE +big_size(big) + struct RBignum *big; +{ + return INT2FIX(big->len*2); +} + +void +Init_Bignum() +{ + cBignum = rb_define_class("Bignum", cInteger); + + rb_undef_method(CLASS_OF(cBignum), "new"); + + rb_define_method(cBignum, "to_s", big_to_s, 0); + rb_define_method(cBignum, "coerce", big_coerce, 1); + rb_define_method(cBignum, "-@", big_uminus, 0); + rb_define_method(cBignum, "+", big_plus, 1); + rb_define_method(cBignum, "-", big_minus, 1); + rb_define_method(cBignum, "*", big_mul, 1); + rb_define_method(cBignum, "/", big_div, 1); + rb_define_method(cBignum, "%", big_mod, 1); + rb_define_method(cBignum, "divmod", big_divmod, 1); + rb_define_method(cBignum, "**", big_pow, 1); + rb_define_method(cBignum, "&", big_and, 1); + rb_define_method(cBignum, "|", big_or, 1); + rb_define_method(cBignum, "^", big_xor, 1); + rb_define_method(cBignum, "~", big_neg, 0); + rb_define_method(cBignum, "<<", big_lshift, 1); + rb_define_method(cBignum, ">>", big_rshift, 1); + rb_define_method(cBignum, "[]", big_aref, 1); + + rb_define_method(cBignum, "<=>", big_cmp, 1); + rb_define_method(cBignum, "==", big_eq, 1); + rb_define_method(cBignum, "eql?", big_eq, 1); + rb_define_method(cBignum, "hash", big_hash, 0); + rb_define_method(cBignum, "to_i", big_to_i, 0); + rb_define_method(cBignum, "to_f", big_to_f, 0); + rb_define_method(cBignum, "abs", big_abs, 0); + rb_define_method(cBignum, "size", big_size, 0); +} diff --git a/class.c b/class.c new file mode 100644 index 0000000000..b78b2aabe0 --- /dev/null +++ b/class.c @@ -0,0 +1,427 @@ +/************************************************ + + class.c - + + $Author$ + $Date$ + created at: Tue Aug 10 15:05:44 JST 1993 + + Copyright (C) 1993-1995 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include "node.h" +#include "st.h" + +struct st_table *new_idhash(); +extern st_table *rb_class_tbl; + +extern VALUE cClass; +extern VALUE cModule; + +VALUE +class_new(super) + struct RClass *super; +{ + NEWOBJ(cls, struct RClass); + OBJSETUP(cls, cClass, T_CLASS); + + cls->super = super; + cls->iv_tbl = 0; + cls->m_tbl = 0; /* safe GC */ + cls->m_tbl = new_idhash(); + + return (VALUE)cls; +} + +VALUE +singleton_class_new(super) + struct RClass *super; +{ + struct RClass *cls = (struct RClass*)class_new(super); + + FL_SET(cls, FL_SINGLETON); + + return (VALUE)cls; +} + +static int +clone_method(mid, body, tbl) + ID mid; + NODE *body; + st_table *tbl; +{ + st_insert(tbl, mid, NEW_METHOD(body->nd_body, body->nd_noex)); + return ST_CONTINUE; +} + +VALUE +singleton_class_clone(class) + struct RClass *class; +{ + if (!FL_TEST(class, FL_SINGLETON)) + return (VALUE)class; + else { + /* copy singleton(unnamed) class */ + NEWOBJ(clone, struct RClass); + CLONESETUP(clone, class); + + clone->super = class->super; + clone->iv_tbl = 0; + clone->m_tbl = 0; + clone->m_tbl = new_idhash(); + st_foreach(class->m_tbl, clone_method, clone->m_tbl); + FL_SET(clone, FL_SINGLETON); + return (VALUE)clone; + } +} + +VALUE +rb_define_class_id(id, super) + ID id; + struct RBasic *super; +{ + struct RClass *cls; + + if (!super) super = (struct RBasic*)cClass; + cls = (struct RClass*)class_new(super); + rb_name_class(cls, id); + /* make metaclass */ + RBASIC(cls)->class = singleton_class_new(super->class); + + return (VALUE)cls; +} + +VALUE +rb_define_class(name, super) + char *name; + VALUE super; +{ + VALUE class; + ID id; + + id = rb_intern(name); + class = rb_define_class_id(id, super); + st_add_direct(rb_class_tbl, id, class); + + return class; +} + +VALUE +rb_define_class_under(under, name, super) + VALUE under; + char *name; + VALUE super; +{ + VALUE class; + ID id; + + id = rb_intern(name); + class = rb_define_class_id(id, super); + rb_const_set(under, id, class); + rb_set_class_path(class, under, name); + + return class; +} + +VALUE +module_new() +{ + NEWOBJ(mdl, struct RClass); + OBJSETUP(mdl, cModule, T_MODULE); + + mdl->super = 0; + mdl->iv_tbl = 0; + mdl->m_tbl = 0; + mdl->m_tbl = new_idhash(); + + return (VALUE)mdl; +} + +VALUE +rb_define_module_id(id) + ID id; +{ + extern st_table *rb_class_tbl; + struct RClass *mdl = (struct RClass*)module_new(); + + rb_name_class(mdl, id); + + return (VALUE)mdl; +} + +VALUE +rb_define_module(name) + char *name; +{ + VALUE module; + ID id; + + id = rb_intern(name); + module = rb_define_module_id(id); + st_add_direct(rb_class_tbl, id, module); + + return module; +} + +VALUE +rb_define_module_under(under, name) + VALUE under; + char *name; +{ + VALUE module; + ID id; + + id = rb_intern(name); + module = rb_define_module_id(id); + rb_const_set(under, id, module); + rb_set_class_path(module, under, name); + + return module; +} + +static struct RClass * +include_class_new(module, super) + struct RClass *module, *super; +{ + NEWOBJ(cls, struct RClass); + OBJSETUP(cls, cClass, T_ICLASS); + + cls->m_tbl = module->m_tbl; + cls->iv_tbl = module->iv_tbl; + cls->super = super; + if (TYPE(module) == T_ICLASS) { + RBASIC(cls)->class = RBASIC(module)->class; + } + else { + RBASIC(cls)->class = (VALUE)module; + } + + return cls; +} + +void +rb_include_module(class, module) + VALUE class, module; +{ + struct RClass *p; + + if (NIL_P(module)) return; + + switch (TYPE(module)) { + case T_MODULE: + case T_CLASS: + break; + default: + Check_Type(module, T_MODULE); + } + + if (class == module) return; + rb_clear_cache(); + + while (module) { + /* ignore if the module included already in superclasses */ + for (p = RCLASS(class)->super; p; p = p->super) { + if (BUILTIN_TYPE(p) == T_ICLASS && p->m_tbl == RCLASS(module)->m_tbl) + return; + } + + RCLASS(class)->super = include_class_new(module, RCLASS(class)->super); + class = (VALUE)RCLASS(class)->super; + module = (VALUE)RCLASS(module)->super; + } +} + +void +rb_define_method_id(class, name, func, argc) + struct RClass *class; + ID name; + VALUE (*func)(); + int argc; +{ + rb_add_method(class, name, NEW_CFUNC(func, argc), NOEX_PUBLIC); +} + +void +rb_define_method(class, name, func, argc) + VALUE class; + char *name; + VALUE (*func)(); + int argc; +{ + rb_add_method(class, rb_intern(name), NEW_CFUNC(func, argc), NOEX_PUBLIC); +} + +void +rb_undef_method(class, name) + VALUE class; + char *name; +{ + rb_add_method(class, rb_intern(name), 0, NOEX_PUBLIC); +} + +void +rb_define_private_method(class, name, func, argc) + struct RClass *class; + char *name; + VALUE (*func)(); + int argc; +{ + rb_add_method(class, rb_intern(name), NEW_CFUNC(func, argc), NOEX_PRIVATE); +} + +VALUE +rb_singleton_class(obj) + struct RBasic *obj; +{ + if (rb_special_const_p((VALUE)obj)) { + TypeError("cannot define singleton"); + } + if (FL_TEST(obj->class, FL_SINGLETON)) { + return (VALUE)obj->class; + } + return obj->class = singleton_class_new(obj->class); +} + +void +rb_define_singleton_method(obj, name, func, argc) + VALUE obj; + char *name; + VALUE (*func)(); + int argc; +{ + rb_define_method(rb_singleton_class(obj), name, func, argc); +} + +void +rb_define_module_function(module, name, func, argc) + VALUE module; + char *name; + VALUE (*func)(); + int argc; +{ + rb_define_private_method(module, name, func, argc); + rb_define_singleton_method(module, name, func, argc); +} + +VALUE mKernel; + +void +rb_define_global_function(name, func, argc) + char *name; + VALUE (*func)(); + int argc; +{ + rb_define_private_method(mKernel, name, func, argc); +} + +void +rb_define_alias(class, name1, name2) + VALUE class; + char *name1, *name2; +{ + rb_alias(class, rb_intern(name1), rb_intern(name2)); +} + +void +rb_define_attr(class, id, pub) + VALUE class; + ID id; + int pub; +{ + char *name; + char *buf; + ID attr, attreq, attriv; + + name = rb_id2name(id); + attr = rb_intern(name); + buf = ALLOCA_N(char,strlen(name)+2); + sprintf(buf, "%s=", name); + attreq = rb_intern(buf); + sprintf(buf, "@%s", name); + attriv = rb_intern(buf); + if (!rb_method_boundp(class, attr, NOEX_PRIVATE)) { + rb_add_method(class, attr, NEW_IVAR(attriv), 0); + } + if (pub && !rb_method_boundp(class, attreq, NOEX_PRIVATE)) { + rb_add_method(class, attreq, NEW_ATTRSET(attriv), 0); + } +} + +#include <varargs.h> +#include <ctype.h> + +int +rb_scan_args(argc, argv, fmt, va_alist) + int argc; + VALUE *argv; + char *fmt; + va_dcl +{ + int n, i; + char *p = fmt; + VALUE *var; + va_list vargs; + + va_start(vargs); + + if (*p == '*') { + var = va_arg(vargs, VALUE*); + *var = ary_new4(argc, argv); + return argc; + } + + if (isdigit(*p)) { + n = *p - '0'; + if (n > argc) + ArgError("Wrong # of arguments (%d for %d)", argc, n); + for (i=0; i<n; i++) { + var = va_arg(vargs, VALUE*); + *var = argv[i]; + } + p++; + } + else { + goto error; + } + + if (isdigit(*p)) { + n = i + *p - '0'; + for (; i<n; i++) { + var = va_arg(vargs, VALUE*); + if (argc > i) { + *var = argv[i]; + } + else { + *var = Qnil; + } + } + p++; + } + + if(*p == '*') { + var = va_arg(vargs, VALUE*); + if (argc > i) { + *var = ary_new4(argc-i, argv+i); + } + else { + *var = ary_new(); + } + } + else if (*p == '\0') { + if (argc > i) { + ArgError("Wrong # of arguments(%d for %d)", argc, i); + } + } + else { + goto error; + } + + va_end(vargs); + return argc; + + error: + Fatal("bad scan arg format: %s", fmt); + return 0; +} diff --git a/compar.c b/compar.c new file mode 100644 index 0000000000..e852fb1e49 --- /dev/null +++ b/compar.c @@ -0,0 +1,100 @@ +/************************************************ + + compar.c - + + $Author$ + $Date$ + created at: Thu Aug 26 14:39:48 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" + +VALUE mComparable; + +static ID cmp; + +static VALUE +cmp_eq(x, y) + VALUE x, y; +{ + VALUE c = rb_funcall(x, cmp, 1, y); + int t = NUM2INT(c); + + if (t == 0) return TRUE; + return FALSE; +} + +static VALUE +cmp_gt(x, y) + VALUE x, y; +{ + VALUE c = rb_funcall(x, cmp, 1, y); + int t = NUM2INT(c); + + if (t > 0) return TRUE; + return FALSE; +} + +static VALUE +cmp_ge(x, y) + VALUE x, y; +{ + VALUE c = rb_funcall(x, cmp, 1, y); + int t = NUM2INT(c); + + if (t >= 0) return TRUE; + return FALSE; +} + +static VALUE +cmp_lt(x, y) + VALUE x, y; +{ + VALUE c = rb_funcall(x, cmp, 1, y); + int t = NUM2INT(c); + + if (t < 0) return TRUE; + return FALSE; +} + +static VALUE +cmp_le(x, y) + VALUE x, y; +{ + VALUE c = rb_funcall(x, cmp, 1, y); + int t = NUM2INT(c); + + if (t <= 0) return TRUE; + return FALSE; +} + +static VALUE +cmp_between(x, min, max) + VALUE x, min, max; +{ + VALUE c = rb_funcall(x, cmp, 1, min); + int t = NUM2INT(c); + if (t < 0) return FALSE; + + c = rb_funcall(x, cmp, 1, max); + t = NUM2INT(c); + if (t > 0) return FALSE; + return TRUE; +} + +void +Init_Comparable() +{ + mComparable = rb_define_module("Comparable"); + rb_define_method(mComparable, "==", cmp_eq, 1); + rb_define_method(mComparable, ">", cmp_gt, 1); + rb_define_method(mComparable, ">=", cmp_ge, 1); + rb_define_method(mComparable, "<", cmp_lt, 1); + rb_define_method(mComparable, "<=", cmp_le, 1); + rb_define_method(mComparable, "between?", cmp_between, 2); + + cmp = rb_intern("<=>"); +} diff --git a/config.dj b/config.dj new file mode 100644 index 0000000000..8ad3883380 --- /dev/null +++ b/config.dj @@ -0,0 +1,36 @@ +#define THREAD 1 +#define HAVE_DIRENT_H 1 +#define HAVE_UNISTD_H 1 +#define HAVE_LIMITS_H 1 +#define HAVE_SYS_FILE_H 1 +#define HAVE_PWD_H 1 +#define HAVE_SYS_TIME_H 1 +#define HAVE_SYS_TIMES_H 1 +#define HAVE_SYS_PARAM_H 1 +#define HAVE_SYS_WAIT_H 1 +#define HAVE_STRING_H 1 +#define HAVE_UTIME_H 1 +#define HAVE_MEMORY_H 1 +#define HAVE_ST_BLKSIZE 1 +#define HAVE_ST_RDEV 1 +#define GETGROUPS_T gid_t +#define RETSIGTYPE void +#define HAVE_ALLOCA 1 +#define vfork fork +#define HAVE_FMOD 1 +#define HAVE_RANDOM 1 +#define HAVE_WAITPID 1 +#define HAVE_GETCWD 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_SIGPROCMASK 1 +#define FILE_COUNT _cnt +#define DLEXT ".so" +#define RUBY_LIB ";/usr/local/lib/ruby;." +#define RUBY_ARCHLIB "/usr/local/lib/ruby/i386-djgpp" +#define RUBY_PLATFORM "i386-djgpp" diff --git a/config.guess b/config.guess new file mode 100644 index 0000000000..91dad5ee6a --- /dev/null +++ b/config.guess @@ -0,0 +1,613 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 93, 94, 95, 1996 Free Software Foundation, Inc. +# +# 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 +# the Free Software Foundation; either version 2 of the License, 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 to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# 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>. +# The master version of this file is at the FSF in /home/gd/gnu/lib. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit system type (host/target name). +# +# Only a few systems have been added to this list; please add others +# (but try to keep the structure clean). +# + +# 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 + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 8/24/94.) +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_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +trap 'rm -f dummy.c dummy.o dummy; exit 1' 1 2 15 + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + alpha:OSF1:*:*) + # A Vn.n version is a released version. + # 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. + echo alpha-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//'` + exit 0 ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit 0 ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-cbm-sysv4 + exit 0;; + amiga:NetBSD:*:*) + echo m68k-cbm-netbsd${UNAME_RELEASE} + exit 0 ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit 0;; + Pyramid*:OSx*:*:*) + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit 0 ;; + sun4*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit 0 ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit 0 ;; + atari*:NetBSD:*:*) + echo m68k-atari-netbsd${UNAME_RELEASE} + exit 0 ;; + sun3*:NetBSD:*:*) + echo m68k-sun-netbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:NetBSD:*:*) + echo m68k-apple-netbsd${UNAME_RELEASE} + exit 0 ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + mips:*:4*:UMIPS) + echo mips-mips-riscos4sysv + exit 0 ;; + mips:*:5*:RISCos) + echo mips-mips-riscos${UNAME_RELEASE} + exit 0 ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit 0 ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit 0 ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit 0 ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit 0 ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 -o $UNAME_PROCESSOR = mc88110 ] ; then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx \ + -o ${TARGET_BINARY_INTERFACE}x = x ] ; then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else echo i586-dg-dgux${UNAME_RELEASE} + fi + exit 0 ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit 0 ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit 0 ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit 0 ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit 0 ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit 0 ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i[34]86:AIX:*:*) + echo i386-ibm-aix + exit 0 ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + sed 's/^ //' << EOF >dummy.c + #include <sys/systemcfg.h> + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + ${CC-cc} dummy.c -o dummy && ./dummy && rm dummy.c dummy && exit 0 + rm -f dummy.c dummy + echo rs6000-ibm-aix3.2.5 + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit 0 ;; + *:AIX:*:4) + if /usr/sbin/lsattr -EHl proc0 | grep POWER >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=4.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit 0 ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit 0 ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC NetBSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit 0 ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit 0 ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit 0 ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit 0 ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit 0 ;; + 9000/[3478]??:HP-UX:*:*) + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/7?? | 9000/8?[679] ) HP_ARCH=hppa1.1 ;; + 9000/8?? ) HP_ARCH=hppa1.0 ;; + esac + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit 0 ;; + 3050*:HI-UX:*:*) + sed 's/^ //' << EOF >dummy.c + #include <unistd.h> + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + ${CC-cc} dummy.c -o dummy && ./dummy && rm dummy.c dummy && exit 0 + rm -f dummy.c dummy + echo unknown-hitachi-hiuxwe2 + exit 0 ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit 0 ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit 0 ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit 0 ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit 0 ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit 0 ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit 0 ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit 0 ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit 0 ;; + 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} + exit 0 ;; + CRAY*C90:*:*:*) + echo c90-cray-unicos${UNAME_RELEASE} + exit 0 ;; + CRAY-2:*:*:*) + echo cray2-cray-unicos + exit 0 ;; + hp3[0-9][05]:NetBSD:*:*) + echo m68k-hp-netbsd${UNAME_RELEASE} + exit 0 ;; + i[34]86:BSD/386:*:* | *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit 0 ;; + *:FreeBSD:*:*) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit 0 ;; + *:NetBSD:*:*) + echo ${UNAME_MACHINE}-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + exit 0 ;; + *:OpenBSD:*:*) + echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + exit 0 ;; + *:*:*BOW*:*) + echo i386-pc-bow + exit 0 ;; + i*:CYGWIN*:*) + echo i386-pc-cygwin32 + exit 0 ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin32 + exit 0 ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + *:GNU:*:*) + echo `echo ${UNAME_MACHINE}|sed -e 's,/.*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit 0 ;; + *:Linux:*:*) + echo ${UNAME_MACHINE}-pc-linux + 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[34]86:DYNIX/ptx:4*:*) + echo i386-sequent-sysv4 + exit 0 ;; + i[34]86:*:4.*:* | i[34]86:SYSTEM_V:4.*:*) + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_RELEASE} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_RELEASE} + fi + exit 0 ;; + i[34]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_MACHINE=i586 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit 0 ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit 0 ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit 0 ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit 0 ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit 0 ;; + M680[234]0:*:R3V[567]*:*) + test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; + 3[34]??:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0) + uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4.3 && exit 0 ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4 && exit 0 ;; + m680[234]0:LynxOS:2.[23]*:*) + echo m68k-lynx-lynxos${UNAME_RELEASE} + exit 0 ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit 0 ;; + i[34]86:LynxOS:2.[23]*:*) + echo i386-lynx-lynxos${UNAME_RELEASE} + exit 0 ;; + TSUNAMI:LynxOS:2.[23]*:*) + echo sparc-lynx-lynxos${UNAME_RELEASE} + exit 0 ;; + rs6000:LynxOS:2.[23]*:*) + echo rs6000-lynx-lynxos${UNAME_RELEASE} + exit 0 ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit 0 ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit 0 ;; + X680[02346]0:Human68k:*:*) + echo m68k-sharp-human + exit 0 ;; + R[34]000:*System_V*:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit 0 ;; + R[34]???:UNIX_SV:4.?MP:*) + if [ -x /sbin/uversion ]; then + UVERSION_RELEASE=`(/sbin/uversion -r) 2>/dev/null` \ + || UVERSION_RELEASE=unknown + UVERSION_SYSTEM=`(/sbin/uversion -s) 2>/dev/null` \ + || UVERSION_SYSTEM=unknown + case "${UVERSION_RELEASE}:${UVERSION_SYSTEM}" in + Release*:EWS4800/*) + suffix=`echo ${UNAME_RELEASE} | tr '[A-Z]' '[a-z]'` + suffix=${suffix}r`echo ${UVERSION_RELEASE} | \ + sed -e 's/Release//' -e 's/ Rev.*$//'` + echo mips-nec-sysv${suffix} + exit 0 ;; + esac + fi;; + *:machten:*:*) + echo ${UNAME_MACHINE}-apple-machten + exit 0 ;; + powerpc:JCC_BSD+:*:*) + echo powerpc-jcc-bsd4.4 + exit 0 ;; + DS/90*:*:*:V20*) + echo sparc-fujitsu-uxpds + exit 0 ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +cat >dummy.c <<EOF +#ifdef _SEQUENT_ +# include <sys/types.h> +# include <sys/utsname.h> +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include <sys/param.h> + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + printf ("%s-next-nextstep%s\n", __ARCHITECTURE__, version==2 ? "2" : "3"); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +#if !defined (ultrix) + printf ("vax-dec-bsd\n"); exit (0); +#else + printf ("vax-dec-ultrix\n"); exit (0); +#endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + +#if defined (__human68k__) || defined (HUMAN68K) + printf ("m68k-sharp-human\n"); exit (0); +#endif + + exit (1); +} +EOF + +${CC-cc} dummy.c -o dummy 2>/dev/null && ./dummy && rm -f dummy.c dummy.x dummy && exit 0 +rm -f dummy.c dummy.x dummy + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit 0 ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + c34*) + echo c34-convex-bsd + exit 0 ;; + c38*) + echo c38-convex-bsd + exit 0 ;; + c4*) + echo c4-convex-bsd + exit 0 ;; + esac +fi + +#echo '(Unable to guess system type)' 1>&2 + +exit 1 diff --git a/config.sub b/config.sub new file mode 100644 index 0000000000..002d408812 --- /dev/null +++ b/config.sub @@ -0,0 +1,931 @@ +#! /bin/sh +# Configuration validation subroutine script, version 1.1. +# Copyright (C) 1991, 92, 93, 94, 95, 1996 Free Software Foundation, Inc. +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# 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 +# the Free Software Foundation; either version 2 of the License, 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 to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +if [ x$1 = x ] +then + echo Configuration name missing. 1>&2 + echo "Usage: $0 CPU-MFR-OPSYS" 1>&2 + echo "or $0 ALIAS" 1>&2 + echo where ALIAS is a recognized configuration type. 1>&2 + exit 1 +fi + +# First pass through any local machine types. +case $1 in + *local*) + echo $1 + exit 0 + ;; + *) + ;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + linux-gnu*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple) + os= + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco5) + os=sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + tahoe | i860 | m68k | m68000 | m88k | ns32k | arm \ + | arme[lb] | pyramid \ + | tron | a29k | 580 | i960 | h8300 | hppa1.0 | hppa1.1 \ + | alpha | we32k | ns16k | clipper | i370 | sh \ + | powerpc | powerpcle | 1750a | dsp16xx | mips64 | mipsel \ + | pdp11 | mips64el | mips64orion | mips64orionel \ + | sparc | sparclet | sparclite | sparc64) + basic_machine=$basic_machine-unknown + ;; + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i[3456]86) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + vax-* | tahoe-* | i[3456]86-* | i860-* | m68k-* | m68000-* | m88k-* \ + | sparc-* | ns32k-* | fx80-* | arm-* | c[123]* \ + | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* | power-* \ + | none-* | 580-* | cray2-* | h8300-* | i960-* | xmp-* | ymp-* \ + | hppa1.0-* | hppa1.1-* | alpha-* | we32k-* | cydra-* | ns16k-* \ + | pn-* | np1-* | xps100-* | clipper-* | orion-* | sparclite-* \ + | pdp11-* | sh-* | powerpc-* | powerpcle-* | sparc64-* | mips64-* | mipsel-* \ + | mips64el-* | mips64orion-* | mips64orionel-*) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-cbm + ;; + amigados) + basic_machine=m68k-cbm + os=-amigados + ;; + amigaunix | amix) + basic_machine=m68k-cbm + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | ymp) + basic_machine=ymp-cray + os=-unicos + ;; + cray2) + basic_machine=cray2-cray + os=-unicos + ;; + [ctj]90-cray) + basic_machine=c90-cray + os=-unicos + ;; + crds | unos) + basic_machine=m68k-crds + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + ews4800) + basic_machine=mips-nec + os=-sysv4 + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k7[0-9][0-9] | hp7[0-9][0-9] | hp9k8[0-9]7 | hp8[0-9]7) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + os=-mvs + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i[3456]86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i[3456]86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i[3456]86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i[3456]86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + miniframe) + basic_machine=m68000-convergent + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + np1) + basic_machine=np1-gould + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5) + basic_machine=i586-intel + ;; + pentiumpro | p6) + basic_machine=i686-intel + ;; + pentium-* | p5-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + k5) + # We don't have specific support for AMD's K5 yet, so just call it a Pentium + basic_machine=i586-amd + ;; + nexen) + # We don't have specific support for Nexgen yet, so just call it a Pentium + basic_machine=i586-nexgen + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=rs6000-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/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + xmp) + basic_machine=xmp-cray + os=-unicos + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + mips) + basic_machine=mips-mips + ;; + romp) + basic_machine=romp-ibm + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sparc) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + human) + basic_machine=m68k-sharp + os=-human + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -unixware* | svr4*) + os=-sysv4 + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigados* | -msdos* | -newsos* | -unicos* | -aof* | -aos* \ + | -nindy* | -vxworks* | -ebmon* | -hms* | -mvs* | -clix* \ + | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \ + | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -cygwin32* | -pe* | -psos* | -moss* | -proelf* \ + | -linux* | -bow*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -ctix* | -uts*) + os=-sysv + ;; + -ns2 ) + os=-nextstep2 + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -xenix) + os=-xenix + ;; + -uxpds) + os=-uxpds + ;; + -human) + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + *-acorn) + os=-riscix1.2 + ;; + arm*-semi) + os=-aout + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-ibm) + os=-aix + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigados + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -lynxos*) + vendor=lynx + ;; + -aix*) + vendor=ibm + ;; + -hpux*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -vxworks*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os diff --git a/configure b/configure new file mode 100644 index 0000000000..dd18efc8b4 --- /dev/null +++ b/configure @@ -0,0 +1,4038 @@ +#! /bin/sh + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.12 +# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: +ac_help="$ac_help +--without-gcc never use gcc" +ac_help="$ac_help +--disable-thread never use user-level thread" +ac_help="$ac_help +--enable-fat-binary build a NeXT Multi Architecture Binary. " +ac_help="$ac_help +--with-dln-a-out use dln_a_out if possible" +ac_help="$ac_help +--with-static-linked-ext link external modules statically" + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Initialize some other variables. +subdirs= +MFLAGS= MAKEFLAGS= +# Maximum number of lines to put in a shell here document. +ac_max_here_lines=12 + +ac_prev= +for ac_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case "$ac_option" in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir="$ac_optarg" ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build="$ac_optarg" ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file="$ac_optarg" ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "enable_${ac_feature}='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix="$ac_optarg" ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he) + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --bindir=DIR user executables in DIR [EPREFIX/bin] + --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] + --libexecdir=DIR program executables in DIR [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data in DIR + [PREFIX/share] + --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data in DIR + [PREFIX/com] + --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] + --libdir=DIR object code libraries in DIR [EPREFIX/lib] + --includedir=DIR C header files in DIR [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] + --infodir=DIR info documentation in DIR [PREFIX/info] + --mandir=DIR man documentation in DIR [PREFIX/man] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM + run sed PROGRAM on installed program names +EOF + cat << EOF +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +EOF + if test -n "$ac_help"; then + echo "--enable and --with options recognized:$ac_help" + fi + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$ac_optarg" ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir="$ac_optarg" ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir="$ac_optarg" ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir="$ac_optarg" ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir="$ac_optarg" ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir="$ac_optarg" ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir="$ac_optarg" ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir="$ac_optarg" ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix="$ac_optarg" ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix="$ac_optarg" ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix="$ac_optarg" ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name="$ac_optarg" ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir="$ac_optarg" ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir="$ac_optarg" ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site="$ac_optarg" ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir="$ac_optarg" ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir="$ac_optarg" ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.12" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval "with_${ac_package}=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes="$ac_optarg" ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries="$ac_optarg" ;; + + -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set these to C if already set. These must not be set unconditionally +# because not all systems understand e.g. LANG=C (notably SCO). +# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! +# Non-C LC_CTYPE values break the ctype check. +if test "${LANG+set}" = set; then LANG=C; export LANG; fi +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi +if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo > confdefs.h + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +ac_unique_file=ruby.h + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + echo "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + echo "loading cache $cache_file" + . $cache_file +else + echo "creating cache $cache_file" + > $cache_file +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + + +# Check whether --with-gcc or --without-gcc was given. +if test "${with_gcc+set}" = set; then + withval="$with_gcc" + + case $withval in + no) CC=cc + without_gcc=yes;; + yes) CC=gcc + without_gcc=no;; + *) CC=$withval + without_gcc=$withval;; + esac +else + without_gcc=no +fi + +if test ! -z "$ac_cv_prog_CC" -a ! -z "$CC" -a "$CC" != "$ac_cv_prog_CC" +then + { echo "configure: error: cached CC is different -- throw away $cache_file +(it is also a good idea to do 'make clean' before compiling)" 1>&2; exit 1; } +fi + +rb_thread=yes +# Check whether --enable-thread or --disable-thread was given. +if test "${enable_thread+set}" = set; then + enableval="$enable_thread" + + rb_thread=$enableval + +fi + +if test $rb_thread = yes; then + cat >> confdefs.h <<\EOF +#define THREAD 1 +EOF + +fi + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } +fi +ac_config_guess=$ac_aux_dir/config.guess +ac_config_sub=$ac_aux_dir/config.sub +ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. + + +# Make sure we can run config.sub. +if $ac_config_sub sun4 >/dev/null 2>&1; then : +else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; } +fi + +echo $ac_n "checking host system type""... $ac_c" 1>&6 +echo "configure:597: checking host system type" >&5 + +host_alias=$host +case "$host_alias" in +NONE) + case $nonopt in + NONE) + if host_alias=`$ac_config_guess`; then : + else { echo "configure: error: can not guess host type; you must specify one" 1>&2; exit 1; } + fi ;; + *) host_alias=$nonopt ;; + esac ;; +esac + +host=`$ac_config_sub $host_alias` +host_cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` +echo "$ac_t""$host" 1>&6 + + + +fat_binary=no +# Check whether --enable-fat-binary or --disable-fat-binary was given. +if test "${enable_fat_binary+set}" = set; then + enableval="$enable_fat_binary" + fat_binary=$enableval +fi + +if test "$fat_binary" = yes ; then + + echo $ac_n "checking target architecture ""... $ac_c" 1>&6 +echo "configure:629: checking target architecture " >&5 + + if test "$TARGET_ARCHS" = "" ; then + if test `/usr/bin/arch` = "m68k" ; then + TARGET_ARCHS="m68k i486" + else + TARGET_ARCHS="m68k `/usr/bin/arch`" + fi + fi + # /usr/lib/arch_tool -archify_list $TARGET_ARCHS + for archs in $TARGET_ARCHS + do + ARCH_FLAG="$ARCH_FLAG -arch $archs " + echo -n " $archs" + done + cat >> confdefs.h <<\EOF +#define NEXT_FAT_BINARY 1 +EOF + + echo "." +fi + + +if test "$program_transform_name" = s,x,x,; then + program_transform_name= +else + # Double any \ or $. echo might interpret backslashes. + cat <<\EOF_SED > conftestsed +s,\\,\\\\,g; s,\$,$$,g +EOF_SED + program_transform_name="`echo $program_transform_name|sed -f conftestsed`" + rm -f conftestsed +fi +test "$program_prefix" != NONE && + program_transform_name="s,^,${program_prefix},; $program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s,\$\$,${program_suffix},; $program_transform_name" + +# sed with no file args requires a program. +test "$program_transform_name" = "" && program_transform_name="s,x,x," + + +# Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:675: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:704: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + ac_prog_rejected=no + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + break + fi + done + IFS="$ac_save_ifs" +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" "$@" + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } +fi + +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:752: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +cat > conftest.$ac_ext <<EOF +#line 762 "configure" +#include "confdefs.h" +main(){return(0);} +EOF +if { (eval echo configure:766: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + ac_cv_prog_cc_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cc_cross=no + else + ac_cv_prog_cc_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cc_works=no +fi +rm -fr conftest* + +echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 +if test $ac_cv_prog_cc_works = no; then + { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:786: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +echo "configure:791: checking whether we are using GNU C" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <<EOF +#ifdef __GNUC__ + yes; +#endif +EOF +if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:800: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + GCC=yes + ac_test_CFLAGS="${CFLAGS+set}" + ac_save_CFLAGS="$CFLAGS" + CFLAGS= + echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +echo "configure:815: checking whether ${CC-cc} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_cc_g=yes +else + ac_cv_prog_cc_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 + if test "$ac_test_CFLAGS" = set; then + CFLAGS="$ac_save_CFLAGS" + elif test $ac_cv_prog_cc_g = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-O2" + fi +else + GCC= + test "${CFLAGS+set}" = set || CFLAGS="-g" +fi + +echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +echo "configure:843: checking how to run the C preprocessor" >&5 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # This must be in double quotes, not single quotes, because CPP may get + # substituted into the Makefile and "${CC-cc}" will confuse make. + CPP="${CC-cc} -E" + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. + cat > conftest.$ac_ext <<EOF +#line 858 "configure" +#include "confdefs.h" +#include <assert.h> +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:864: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -E -traditional-cpp" + cat > conftest.$ac_ext <<EOF +#line 875 "configure" +#include "confdefs.h" +#include <assert.h> +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:881: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP=/lib/cpp +fi +rm -f conftest* +fi +rm -f conftest* + ac_cv_prog_CPP="$CPP" +fi + CPP="$ac_cv_prog_CPP" +else + ac_cv_prog_CPP="$CPP" +fi +echo "$ac_t""$CPP" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + echo $ac_n "checking whether ${CC-cc} needs -traditional""... $ac_c" 1>&6 +echo "configure:905: checking whether ${CC-cc} needs -traditional" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc_traditional'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_pattern="Autoconf.*'x'" + cat > conftest.$ac_ext <<EOF +#line 911 "configure" +#include "confdefs.h" +#include <sgtty.h> +Autoconf TIOCGETP +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "$ac_pattern" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_prog_gcc_traditional=yes +else + rm -rf conftest* + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + + if test $ac_cv_prog_gcc_traditional = no; then + cat > conftest.$ac_ext <<EOF +#line 929 "configure" +#include "confdefs.h" +#include <termio.h> +Autoconf TCGETA +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "$ac_pattern" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi + +echo "$ac_t""$ac_cv_prog_gcc_traditional" 1>&6 + if test $ac_cv_prog_gcc_traditional = yes; then + CC="$CC -traditional" + fi +fi + +for ac_prog in 'bison -y' byacc +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:955: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_YACC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$YACC"; then + ac_cv_prog_YACC="$YACC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_YACC="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +YACC="$ac_cv_prog_YACC" +if test -n "$YACC"; then + echo "$ac_t""$YACC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$YACC" && break +done +test -n "$YACC" || YACC="yacc" + +# Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:987: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_RANLIB="ranlib" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":" +fi +fi +RANLIB="$ac_cv_prog_RANLIB" +if test -n "$RANLIB"; then + echo "$ac_t""$RANLIB" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + +for ac_prog in ar aal +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1019: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_AR'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_AR="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +AR="$ac_cv_prog_AR" +if test -n "$AR"; then + echo "$ac_t""$AR" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$AR" && break +done +test -n "$AR" || AR="ar" + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +echo "configure:1059: checking for a BSD compatible install" >&5 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + for ac_prog in ginstall installbsd scoinst install; do + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + # OSF/1 installbsd also uses dspmsg, but is usable. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + IFS="$ac_save_IFS" + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL="$ac_cv_path_install" + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL="$ac_install_sh" + fi +fi +echo "$ac_t""$INSTALL" 1>&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 +echo "configure:1109: checking whether ${MAKE-make} sets \${MAKE}" >&5 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftestmake <<\EOF +all: + @echo 'ac_maketemp="${MAKE}"' +EOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftestmake +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$ac_t""yes" 1>&6 + SET_MAKE= +else + echo "$ac_t""no" 1>&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + + +# checks for UNIX variants that set C preprocessor variables +ac_safe=`echo "minix/config.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for minix/config.h""... $ac_c" 1>&6 +echo "configure:1139: checking for minix/config.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1144 "configure" +#include "confdefs.h" +#include <minix/config.h> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1149: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + MINIX=yes +else + echo "$ac_t""no" 1>&6 +MINIX= +fi + +if test "$MINIX" = yes; then + cat >> confdefs.h <<\EOF +#define _POSIX_SOURCE 1 +EOF + + cat >> confdefs.h <<\EOF +#define _POSIX_1_SOURCE 2 +EOF + + cat >> confdefs.h <<\EOF +#define _MINIX 1 +EOF + +fi + + +case "$host_os" in +nextstep*) ;; +human*) ;; +*) LIBS="-lm $LIBS";; +esac +echo $ac_n "checking for crypt in -lcrypt""... $ac_c" 1>&6 +echo "configure:1193: checking for crypt in -lcrypt" >&5 +ac_lib_var=`echo crypt'_'crypt | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lcrypt $LIBS" +cat > conftest.$ac_ext <<EOF +#line 1201 "configure" +#include "confdefs.h" +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char crypt(); + +int main() { +crypt() +; return 0; } +EOF +if { (eval echo configure:1212: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo crypt | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <<EOF +#define $ac_tr_lib 1 +EOF + + LIBS="-lcrypt $LIBS" + +else + echo "$ac_t""no" 1>&6 +fi + +echo $ac_n "checking for dlopen in -ldl""... $ac_c" 1>&6 +echo "configure:1240: checking for dlopen in -ldl" >&5 +ac_lib_var=`echo dl'_'dlopen | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ldl $LIBS" +cat > conftest.$ac_ext <<EOF +#line 1248 "configure" +#include "confdefs.h" +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dlopen(); + +int main() { +dlopen() +; return 0; } +EOF +if { (eval echo configure:1259: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo dl | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <<EOF +#define $ac_tr_lib 1 +EOF + + LIBS="-ldl $LIBS" + +else + echo "$ac_t""no" 1>&6 +fi + # Dynamic linking for SunOS/Solaris and SYSV +echo $ac_n "checking for shl_load in -ldld""... $ac_c" 1>&6 +echo "configure:1287: checking for shl_load in -ldld" >&5 +ac_lib_var=`echo dld'_'shl_load | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ldld $LIBS" +cat > conftest.$ac_ext <<EOF +#line 1295 "configure" +#include "confdefs.h" +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char shl_load(); + +int main() { +shl_load() +; return 0; } +EOF +if { (eval echo configure:1306: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo dld | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <<EOF +#define $ac_tr_lib 1 +EOF + + LIBS="-ldld $LIBS" + +else + echo "$ac_t""no" 1>&6 +fi + # Dynamic linking for HP-UX + +ac_header_dirent=no +for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr that defines DIR""... $ac_c" 1>&6 +echo "configure:1339: checking for $ac_hdr that defines DIR" >&5 +if eval "test \"`echo '$''{'ac_cv_header_dirent_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1344 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <$ac_hdr> +int main() { +DIR *dirp = 0; +; return 0; } +EOF +if { (eval echo configure:1352: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + eval "ac_cv_header_dirent_$ac_safe=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_dirent_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_dirent_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <<EOF +#define $ac_tr_hdr 1 +EOF + ac_header_dirent=$ac_hdr; break +else + echo "$ac_t""no" 1>&6 +fi +done +# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. +if test $ac_header_dirent = dirent.h; then +echo $ac_n "checking for opendir in -ldir""... $ac_c" 1>&6 +echo "configure:1377: checking for opendir in -ldir" >&5 +ac_lib_var=`echo dir'_'opendir | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ldir $LIBS" +cat > conftest.$ac_ext <<EOF +#line 1385 "configure" +#include "confdefs.h" +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char opendir(); + +int main() { +opendir() +; return 0; } +EOF +if { (eval echo configure:1396: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + LIBS="$LIBS -ldir" +else + echo "$ac_t""no" 1>&6 +fi + +else +echo $ac_n "checking for opendir in -lx""... $ac_c" 1>&6 +echo "configure:1418: checking for opendir in -lx" >&5 +ac_lib_var=`echo x'_'opendir | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lx $LIBS" +cat > conftest.$ac_ext <<EOF +#line 1426 "configure" +#include "confdefs.h" +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char opendir(); + +int main() { +opendir() +; return 0; } +EOF +if { (eval echo configure:1437: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + LIBS="$LIBS -lx" +else + echo "$ac_t""no" 1>&6 +fi + +fi + +echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 +echo "configure:1460: checking for ANSI C header files" >&5 +if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1465 "configure" +#include "confdefs.h" +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1473: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + ac_cv_header_stdc=yes +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. +cat > conftest.$ac_ext <<EOF +#line 1490 "configure" +#include "confdefs.h" +#include <string.h> +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "memchr" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. +cat > conftest.$ac_ext <<EOF +#line 1508 "configure" +#include "confdefs.h" +#include <stdlib.h> +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "free" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. +if test "$cross_compiling" = yes; then + : +else + cat > conftest.$ac_ext <<EOF +#line 1529 "configure" +#include "confdefs.h" +#include <ctype.h> +#define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int main () { int i; for (i = 0; i < 256; i++) +if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); +exit (0); } + +EOF +if { (eval echo configure:1540: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + : +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_header_stdc=no +fi +rm -fr conftest* +fi + +fi +fi + +echo "$ac_t""$ac_cv_header_stdc" 1>&6 +if test $ac_cv_header_stdc = yes; then + cat >> confdefs.h <<\EOF +#define STDC_HEADERS 1 +EOF + +fi + +for ac_hdr in stdlib.h unistd.h limits.h sys/file.h sys/ioctl.h pwd.h \ + sys/select.h sys/time.h sys/times.h sys/param.h sys/wait.h\ + syscall.h a.out.h string.h utime.h memory.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:1569: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1574 "configure" +#include "confdefs.h" +#include <$ac_hdr> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1579: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <<EOF +#define $ac_tr_hdr 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + + +echo $ac_n "checking for uid_t in sys/types.h""... $ac_c" 1>&6 +echo "configure:1607: checking for uid_t in sys/types.h" >&5 +if eval "test \"`echo '$''{'ac_cv_type_uid_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1612 "configure" +#include "confdefs.h" +#include <sys/types.h> +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "uid_t" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_uid_t=yes +else + rm -rf conftest* + ac_cv_type_uid_t=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_type_uid_t" 1>&6 +if test $ac_cv_type_uid_t = no; then + cat >> confdefs.h <<\EOF +#define uid_t int +EOF + + cat >> confdefs.h <<\EOF +#define gid_t int +EOF + +fi + +echo $ac_n "checking for size_t""... $ac_c" 1>&6 +echo "configure:1641: checking for size_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1646 "configure" +#include "confdefs.h" +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_size_t=yes +else + rm -rf conftest* + ac_cv_type_size_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_size_t" 1>&6 +if test $ac_cv_type_size_t = no; then + cat >> confdefs.h <<\EOF +#define size_t unsigned +EOF + +fi + +echo $ac_n "checking for st_blksize in struct stat""... $ac_c" 1>&6 +echo "configure:1674: checking for st_blksize in struct stat" >&5 +if eval "test \"`echo '$''{'ac_cv_struct_st_blksize'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1679 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <sys/stat.h> +int main() { +struct stat s; s.st_blksize; +; return 0; } +EOF +if { (eval echo configure:1687: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_struct_st_blksize=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_struct_st_blksize=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_struct_st_blksize" 1>&6 +if test $ac_cv_struct_st_blksize = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_ST_BLKSIZE 1 +EOF + +fi + +save_LIBOJBS="$LIBOBJS" +echo $ac_n "checking for st_blocks in struct stat""... $ac_c" 1>&6 +echo "configure:1709: checking for st_blocks in struct stat" >&5 +if eval "test \"`echo '$''{'ac_cv_struct_st_blocks'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1714 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <sys/stat.h> +int main() { +struct stat s; s.st_blocks; +; return 0; } +EOF +if { (eval echo configure:1722: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_struct_st_blocks=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_struct_st_blocks=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_struct_st_blocks" 1>&6 +if test $ac_cv_struct_st_blocks = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_ST_BLOCKS 1 +EOF + +else + LIBOBJS="$LIBOBJS fileblocks.o" +fi + +LIBOBJS="$save_LIBOBJS" +echo $ac_n "checking for st_rdev in struct stat""... $ac_c" 1>&6 +echo "configure:1746: checking for st_rdev in struct stat" >&5 +if eval "test \"`echo '$''{'ac_cv_struct_st_rdev'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1751 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <sys/stat.h> +int main() { +struct stat s; s.st_rdev; +; return 0; } +EOF +if { (eval echo configure:1759: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_struct_st_rdev=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_struct_st_rdev=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_struct_st_rdev" 1>&6 +if test $ac_cv_struct_st_rdev = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_ST_RDEV 1 +EOF + +fi + + +echo $ac_n "checking size of short""... $ac_c" 1>&6 +echo "configure:1781: checking size of short" >&5 +if eval "test \"`echo '$''{'ac_cv_sizeof_short'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else + cat > conftest.$ac_ext <<EOF +#line 1789 "configure" +#include "confdefs.h" +#include <stdio.h> +main() +{ + FILE *f=fopen("conftestval", "w"); + if (!f) exit(1); + fprintf(f, "%d\n", sizeof(short)); + exit(0); +} +EOF +if { (eval echo configure:1800: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + ac_cv_sizeof_short=`cat conftestval` +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_sizeof_short=0 +fi +rm -fr conftest* +fi + +fi +echo "$ac_t""$ac_cv_sizeof_short" 1>&6 +cat >> confdefs.h <<EOF +#define SIZEOF_SHORT $ac_cv_sizeof_short +EOF + + +echo $ac_n "checking size of int""... $ac_c" 1>&6 +echo "configure:1820: checking size of int" >&5 +if eval "test \"`echo '$''{'ac_cv_sizeof_int'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else + cat > conftest.$ac_ext <<EOF +#line 1828 "configure" +#include "confdefs.h" +#include <stdio.h> +main() +{ + FILE *f=fopen("conftestval", "w"); + if (!f) exit(1); + fprintf(f, "%d\n", sizeof(int)); + exit(0); +} +EOF +if { (eval echo configure:1839: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + ac_cv_sizeof_int=`cat conftestval` +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_sizeof_int=0 +fi +rm -fr conftest* +fi + +fi +echo "$ac_t""$ac_cv_sizeof_int" 1>&6 +cat >> confdefs.h <<EOF +#define SIZEOF_INT $ac_cv_sizeof_int +EOF + + +echo $ac_n "checking size of long""... $ac_c" 1>&6 +echo "configure:1859: checking size of long" >&5 +if eval "test \"`echo '$''{'ac_cv_sizeof_long'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else + cat > conftest.$ac_ext <<EOF +#line 1867 "configure" +#include "confdefs.h" +#include <stdio.h> +main() +{ + FILE *f=fopen("conftestval", "w"); + if (!f) exit(1); + fprintf(f, "%d\n", sizeof(long)); + exit(0); +} +EOF +if { (eval echo configure:1878: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + ac_cv_sizeof_long=`cat conftestval` +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_sizeof_long=0 +fi +rm -fr conftest* +fi + +fi +echo "$ac_t""$ac_cv_sizeof_long" 1>&6 +cat >> confdefs.h <<EOF +#define SIZEOF_LONG $ac_cv_sizeof_long +EOF + + +echo $ac_n "checking size of void*""... $ac_c" 1>&6 +echo "configure:1898: checking size of void*" >&5 +if eval "test \"`echo '$''{'ac_cv_sizeof_voidp'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else + cat > conftest.$ac_ext <<EOF +#line 1906 "configure" +#include "confdefs.h" +#include <stdio.h> +main() +{ + FILE *f=fopen("conftestval", "w"); + if (!f) exit(1); + fprintf(f, "%d\n", sizeof(void*)); + exit(0); +} +EOF +if { (eval echo configure:1917: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + ac_cv_sizeof_voidp=`cat conftestval` +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_sizeof_voidp=0 +fi +rm -fr conftest* +fi + +fi +echo "$ac_t""$ac_cv_sizeof_voidp" 1>&6 +cat >> confdefs.h <<EOF +#define SIZEOF_VOIDP $ac_cv_sizeof_voidp +EOF + + + +echo $ac_n "checking type of array argument to getgroups""... $ac_c" 1>&6 +echo "configure:1938: checking type of array argument to getgroups" >&5 +if eval "test \"`echo '$''{'ac_cv_type_getgroups'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + ac_cv_type_getgroups=cross +else + cat > conftest.$ac_ext <<EOF +#line 1946 "configure" +#include "confdefs.h" + +/* Thanks to Mike Rendell for this test. */ +#include <sys/types.h> +#define NGID 256 +#undef MAX +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +main() +{ + gid_t gidset[NGID]; + int i, n; + union { gid_t gval; long lval; } val; + + val.lval = -1; + for (i = 0; i < NGID; i++) + gidset[i] = val.gval; + n = getgroups (sizeof (gidset) / MAX (sizeof (int), sizeof (gid_t)) - 1, + gidset); + /* Exit non-zero if getgroups seems to require an array of ints. This + happens when gid_t is short but getgroups modifies an array of ints. */ + exit ((n > 0 && gidset[n] != val.gval) ? 1 : 0); +} + +EOF +if { (eval echo configure:1971: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + ac_cv_type_getgroups=gid_t +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_type_getgroups=int +fi +rm -fr conftest* +fi + +if test $ac_cv_type_getgroups = cross; then + cat > conftest.$ac_ext <<EOF +#line 1985 "configure" +#include "confdefs.h" +#include <unistd.h> +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "getgroups.*int.*gid_t" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_getgroups=gid_t +else + rm -rf conftest* + ac_cv_type_getgroups=int +fi +rm -f conftest* + +fi +fi + +echo "$ac_t""$ac_cv_type_getgroups" 1>&6 +cat >> confdefs.h <<EOF +#define GETGROUPS_T $ac_cv_type_getgroups +EOF + + +echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6 +echo "configure:2009: checking return type of signal handlers" >&5 +if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2014 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <signal.h> +#ifdef signal +#undef signal +#endif +#ifdef __cplusplus +extern "C" void (*signal (int, void (*)(int)))(int); +#else +void (*signal ()) (); +#endif + +int main() { +int i; +; return 0; } +EOF +if { (eval echo configure:2031: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_type_signal=void +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_type_signal=int +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_type_signal" 1>&6 +cat >> confdefs.h <<EOF +#define RETSIGTYPE $ac_cv_type_signal +EOF + + +# The Ultrix 4.2 mips builtin alloca declared by alloca.h only works +# for constant arguments. Useless! +echo $ac_n "checking for working alloca.h""... $ac_c" 1>&6 +echo "configure:2052: checking for working alloca.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_alloca_h'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2057 "configure" +#include "confdefs.h" +#include <alloca.h> +int main() { +char *p = alloca(2 * sizeof(int)); +; return 0; } +EOF +if { (eval echo configure:2064: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + ac_cv_header_alloca_h=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_alloca_h=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_header_alloca_h" 1>&6 +if test $ac_cv_header_alloca_h = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_ALLOCA_H 1 +EOF + +fi + +echo $ac_n "checking for alloca""... $ac_c" 1>&6 +echo "configure:2085: checking for alloca" >&5 +if eval "test \"`echo '$''{'ac_cv_func_alloca_works'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2090 "configure" +#include "confdefs.h" + +#ifdef __GNUC__ +# define alloca __builtin_alloca +#else +# if HAVE_ALLOCA_H +# include <alloca.h> +# else +# ifdef _AIX + #pragma alloca +# else +# ifndef alloca /* predefined by HP cc +Olibcalls */ +char *alloca (); +# endif +# endif +# endif +#endif + +int main() { +char *p = (char *) alloca(1); +; return 0; } +EOF +if { (eval echo configure:2113: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + ac_cv_func_alloca_works=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_func_alloca_works=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_func_alloca_works" 1>&6 +if test $ac_cv_func_alloca_works = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_ALLOCA 1 +EOF + +fi + +if test $ac_cv_func_alloca_works = no; then + # The SVR3 libPW and SVR4 libucb both contain incompatible functions + # that cause trouble. Some versions do not even contain alloca or + # contain a buggy version. If you still want to use their alloca, + # use ar to extract alloca.o from them instead of compiling alloca.c. + ALLOCA=alloca.o + cat >> confdefs.h <<\EOF +#define C_ALLOCA 1 +EOF + + +echo $ac_n "checking whether alloca needs Cray hooks""... $ac_c" 1>&6 +echo "configure:2145: checking whether alloca needs Cray hooks" >&5 +if eval "test \"`echo '$''{'ac_cv_os_cray'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2150 "configure" +#include "confdefs.h" +#if defined(CRAY) && ! defined(CRAY2) +webecray +#else +wenotbecray +#endif + +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "webecray" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_os_cray=yes +else + rm -rf conftest* + ac_cv_os_cray=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_os_cray" 1>&6 +if test $ac_cv_os_cray = yes; then +for ac_func in _getb67 GETB67 getb67; do + echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:2175: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2180 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:2203: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<EOF +#define CRAY_STACKSEG_END $ac_func +EOF + + break +else + echo "$ac_t""no" 1>&6 +fi + +done +fi + +echo $ac_n "checking stack direction for C alloca""... $ac_c" 1>&6 +echo "configure:2230: checking stack direction for C alloca" >&5 +if eval "test \"`echo '$''{'ac_cv_c_stack_direction'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + ac_cv_c_stack_direction=0 +else + cat > conftest.$ac_ext <<EOF +#line 2238 "configure" +#include "confdefs.h" +find_stack_direction () +{ + static char *addr = 0; + auto char dummy; + if (addr == 0) + { + addr = &dummy; + return find_stack_direction (); + } + else + return (&dummy > addr) ? 1 : -1; +} +main () +{ + exit (find_stack_direction() < 0); +} +EOF +if { (eval echo configure:2257: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + ac_cv_c_stack_direction=1 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_c_stack_direction=-1 +fi +rm -fr conftest* +fi + +fi + +echo "$ac_t""$ac_cv_c_stack_direction" 1>&6 +cat >> confdefs.h <<EOF +#define STACK_DIRECTION $ac_cv_c_stack_direction +EOF + +fi + +echo $ac_n "checking for pid_t""... $ac_c" 1>&6 +echo "configure:2279: checking for pid_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2284 "configure" +#include "confdefs.h" +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "pid_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_pid_t=yes +else + rm -rf conftest* + ac_cv_type_pid_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_pid_t" 1>&6 +if test $ac_cv_type_pid_t = no; then + cat >> confdefs.h <<\EOF +#define pid_t int +EOF + +fi + +ac_safe=`echo "vfork.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for vfork.h""... $ac_c" 1>&6 +echo "configure:2313: checking for vfork.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2318 "configure" +#include "confdefs.h" +#include <vfork.h> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2323: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_VFORK_H 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi + +echo $ac_n "checking for working vfork""... $ac_c" 1>&6 +echo "configure:2348: checking for working vfork" >&5 +if eval "test \"`echo '$''{'ac_cv_func_vfork_works'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + echo $ac_n "checking for vfork""... $ac_c" 1>&6 +echo "configure:2354: checking for vfork" >&5 +if eval "test \"`echo '$''{'ac_cv_func_vfork'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2359 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char vfork(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char vfork(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_vfork) || defined (__stub___vfork) +choke me +#else +vfork(); +#endif + +; return 0; } +EOF +if { (eval echo configure:2382: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_func_vfork=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_vfork=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'vfork`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +fi + +else + cat > conftest.$ac_ext <<EOF +#line 2403 "configure" +#include "confdefs.h" +/* Thanks to Paul Eggert for this test. */ +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_VFORK_H +#include <vfork.h> +#endif +/* On some sparc systems, changes by the child to local and incoming + argument registers are propagated back to the parent. + The compiler is told about this with #include <vfork.h>, + but some compilers (e.g. gcc -O) don't grok <vfork.h>. + Test for this by using a static variable whose address + is put into a register that is clobbered by the vfork. */ +static +#ifdef __cplusplus +sparc_address_test (int arg) +#else +sparc_address_test (arg) int arg; +#endif +{ + static pid_t child; + if (!child) { + child = vfork (); + if (child < 0) { + perror ("vfork"); + _exit(2); + } + if (!child) { + arg = getpid(); + write(-1, "", 0); + _exit (arg); + } + } +} +main() { + pid_t parent = getpid (); + pid_t child; + + sparc_address_test (); + + child = vfork (); + + if (child == 0) { + /* Here is another test for sparc vfork register problems. + This test uses lots of local variables, at least + as many local variables as main has allocated so far + including compiler temporaries. 4 locals are enough for + gcc 1.40.3 on a Solaris 4.1.3 sparc, but we use 8 to be safe. + A buggy compiler should reuse the register of parent + for one of the local variables, since it will think that + parent can't possibly be used any more in this routine. + Assigning to the local variable will thus munge parent + in the parent process. */ + pid_t + p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(), + p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid(); + /* Convince the compiler that p..p7 are live; otherwise, it might + use the same hardware register for all 8 local variables. */ + if (p != p1 || p != p2 || p != p3 || p != p4 + || p != p5 || p != p6 || p != p7) + _exit(1); + + /* On some systems (e.g. IRIX 3.3), + vfork doesn't separate parent from child file descriptors. + If the child closes a descriptor before it execs or exits, + this munges the parent's descriptor as well. + Test for this by closing stdout in the child. */ + _exit(close(fileno(stdout)) != 0); + } else { + int status; + struct stat st; + + while (wait(&status) != child) + ; + exit( + /* Was there some problem with vforking? */ + child < 0 + + /* Did the child fail? (This shouldn't happen.) */ + || status + + /* Did the vfork/compiler bug occur? */ + || parent != getpid() + + /* Did the file descriptor bug occur? */ + || fstat(fileno(stdout), &st) != 0 + ); + } +} +EOF +if { (eval echo configure:2498: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + ac_cv_func_vfork_works=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_func_vfork_works=no +fi +rm -fr conftest* +fi + +fi + +echo "$ac_t""$ac_cv_func_vfork_works" 1>&6 +if test $ac_cv_func_vfork_works = no; then + cat >> confdefs.h <<\EOF +#define vfork fork +EOF + +fi + +for ac_func in dup2 setenv memmove mkdir strcasecmp strerror strftime\ + strstr strtoul strdup crypt flock +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:2524: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2529 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:2552: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <<EOF +#define $ac_tr_func 1 +EOF + +else + echo "$ac_t""no" 1>&6 +LIBOBJS="$LIBOBJS ${ac_func}.o" +fi +done + + +for ac_func in fmod killpg random wait4 waitpid syscall getcwd\ + truncate chsize times utimes fcntl lockf setitimer\ + setruid seteuid setreuid setrgid setegid setregid\ + setpgrp2 getpgid getgroups getpriority\ + dlopen sigprocmask sigaction _setjmp +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:2585: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2590 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:2613: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <<EOF +#define $ac_tr_func 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + +if test "$ac_cv_func_strftime" = no; then + echo $ac_n "checking whether struct tm is in sys/time.h or time.h""... $ac_c" 1>&6 +echo "configure:2639: checking whether struct tm is in sys/time.h or time.h" >&5 +if eval "test \"`echo '$''{'ac_cv_struct_tm'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2644 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <time.h> +int main() { +struct tm *tp; tp->tm_sec; +; return 0; } +EOF +if { (eval echo configure:2652: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_struct_tm=time.h +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_struct_tm=sys/time.h +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_struct_tm" 1>&6 +if test $ac_cv_struct_tm = sys/time.h; then + cat >> confdefs.h <<\EOF +#define TM_IN_SYS_TIME 1 +EOF + +fi + +echo $ac_n "checking for tm_zone in struct tm""... $ac_c" 1>&6 +echo "configure:2673: checking for tm_zone in struct tm" >&5 +if eval "test \"`echo '$''{'ac_cv_struct_tm_zone'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2678 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <$ac_cv_struct_tm> +int main() { +struct tm tm; tm.tm_zone; +; return 0; } +EOF +if { (eval echo configure:2686: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_struct_tm_zone=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_struct_tm_zone=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_struct_tm_zone" 1>&6 +if test "$ac_cv_struct_tm_zone" = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_TM_ZONE 1 +EOF + +else + echo $ac_n "checking for tzname""... $ac_c" 1>&6 +echo "configure:2706: checking for tzname" >&5 +if eval "test \"`echo '$''{'ac_cv_var_tzname'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2711 "configure" +#include "confdefs.h" +#include <time.h> +#ifndef tzname /* For SGI. */ +extern char *tzname[]; /* RS6000 and others reject char **tzname. */ +#endif +int main() { +atoi(*tzname); +; return 0; } +EOF +if { (eval echo configure:2721: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + ac_cv_var_tzname=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_var_tzname=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_var_tzname" 1>&6 + if test $ac_cv_var_tzname = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_TZNAME 1 +EOF + + fi +fi + + cat > conftest.$ac_ext <<EOF +#line 2743 "configure" +#include "confdefs.h" + +int main() { +extern int daylight; int i = daylight; +; return 0; } +EOF +if { (eval echo configure:2750: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + cat >> confdefs.h <<\EOF +#define HAVE_DAYLIGHT 1 +EOF + +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* +fi + +if test "$ac_cv_func_sigprocmask" = yes && test "$ac_cv_func_sigaction" = yes; then + cat >> confdefs.h <<\EOF +#define POSIX_SIGNAL 1 +EOF + +else + echo $ac_n "checking for BSD signal semantics""... $ac_c" 1>&6 +echo "configure:2770: checking for BSD signal semantics" >&5 + if eval "test \"`echo '$''{'rb_cv_bsd_signal'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else + cat > conftest.$ac_ext <<EOF +#line 2778 "configure" +#include "confdefs.h" + +#include <stdio.h> +#include <signal.h> + +void +sig_handler(dummy) + int dummy; +{ +} + +int +main() +{ + signal(SIGINT, sig_handler); + kill(getpid(), SIGINT); + kill(getpid(), SIGINT); + return 0; +} + +EOF +if { (eval echo configure:2800: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + rb_cv_bsd_signal=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + rb_cv_bsd_signal=no +fi +rm -fr conftest* +fi + +fi + + echo "$ac_t""$rb_cv_bsd_signal" 1>&6 + if test "$rb_cv_bsd_signal" = yes; then + cat >> confdefs.h <<\EOF +#define BSD_SIGNAL 1 +EOF + + fi +fi + +if test "$ac_cv_func_setpgrp2" = yes; then + cat >> confdefs.h <<\EOF +#define BSD_GETPGRP getpgrp2 +EOF + + cat >> confdefs.h <<\EOF +#define BSD_SETPGRP setpgrp2 +EOF + +else + echo $ac_n "checking whether getpgrp() has arg""... $ac_c" 1>&6 +echo "configure:2834: checking whether getpgrp() has arg" >&5 + if eval "test \"`echo '$''{'rb_cv_bsdgetpgrp'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2839 "configure" +#include "confdefs.h" +#include <unistd.h> +int main() { +getpgrp(0); +; return 0; } +EOF +if { (eval echo configure:2846: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + rb_cv_bsdgetpgrp=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + rb_cv_bsdgetpgrp=no +fi +rm -f conftest* +fi + + echo "$ac_t""$rb_cv_bsdgetpgrp" 1>&6 + if test "$rb_cv_bsdgetpgrp" = yes; then + cat >> confdefs.h <<\EOF +#define BSD_GETPGRP getpgrp +EOF + + fi + + echo $ac_n "checking whether setpgrp() has args""... $ac_c" 1>&6 +echo "configure:2867: checking whether setpgrp() has args" >&5 + if eval "test \"`echo '$''{'rb_cv_bsdsetpgrp'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 2872 "configure" +#include "confdefs.h" +#include <unistd.h> +int main() { +setpgrp(1, 1); +; return 0; } +EOF +if { (eval echo configure:2879: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + rb_cv_bsdsetpgrp=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + rb_cv_bsdsetpgrp=no +fi +rm -f conftest* +fi + + echo "$ac_t""$rb_cv_bsdsetpgrp" 1>&6 + if test "$rb_cv_bsdsetpgrp" = yes; then + cat >> confdefs.h <<\EOF +#define BSD_SETPGRP setpgrp +EOF + + fi +fi + +echo $ac_n "checking whether byte ordering is bigendian""... $ac_c" 1>&6 +echo "configure:2901: checking whether byte ordering is bigendian" >&5 +if eval "test \"`echo '$''{'ac_cv_c_bigendian'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_cv_c_bigendian=unknown +# See if sys/param.h defines the BYTE_ORDER macro. +cat > conftest.$ac_ext <<EOF +#line 2908 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <sys/param.h> +int main() { + +#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN + bogus endian macros +#endif +; return 0; } +EOF +if { (eval echo configure:2919: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + # It does; now see whether it defined to BIG_ENDIAN or not. +cat > conftest.$ac_ext <<EOF +#line 2923 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <sys/param.h> +int main() { + +#if BYTE_ORDER != BIG_ENDIAN + not big endian +#endif +; return 0; } +EOF +if { (eval echo configure:2934: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_c_bigendian=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_c_bigendian=no +fi +rm -f conftest* +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* +if test $ac_cv_c_bigendian = unknown; then +if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else + cat > conftest.$ac_ext <<EOF +#line 2954 "configure" +#include "confdefs.h" +main () { + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long l; + char c[sizeof (long)]; + } u; + u.l = 1; + exit (u.c[sizeof (long) - 1] == 1); +} +EOF +if { (eval echo configure:2967: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + ac_cv_c_bigendian=no +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_c_bigendian=yes +fi +rm -fr conftest* +fi + +fi +fi + +echo "$ac_t""$ac_cv_c_bigendian" 1>&6 +if test $ac_cv_c_bigendian = yes; then + cat >> confdefs.h <<\EOF +#define WORDS_BIGENDIAN 1 +EOF + +fi + +echo $ac_n "checking whether char is unsigned""... $ac_c" 1>&6 +echo "configure:2991: checking whether char is unsigned" >&5 +if eval "test \"`echo '$''{'ac_cv_c_char_unsigned'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$GCC" = yes; then + # GCC predefines this symbol on systems where it applies. +cat > conftest.$ac_ext <<EOF +#line 2998 "configure" +#include "confdefs.h" +#ifdef __CHAR_UNSIGNED__ + yes +#endif + +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "yes" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_c_char_unsigned=yes +else + rm -rf conftest* + ac_cv_c_char_unsigned=no +fi +rm -f conftest* + +else +if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else + cat > conftest.$ac_ext <<EOF +#line 3020 "configure" +#include "confdefs.h" +/* volatile prevents gcc2 from optimizing the test away on sparcs. */ +#if !defined(__STDC__) || __STDC__ != 1 +#define volatile +#endif +main() { + volatile char c = 255; exit(c < 0); +} +EOF +if { (eval echo configure:3030: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + ac_cv_c_char_unsigned=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_c_char_unsigned=no +fi +rm -fr conftest* +fi + +fi +fi + +echo "$ac_t""$ac_cv_c_char_unsigned" 1>&6 +if test $ac_cv_c_char_unsigned = yes && test "$GCC" != yes; then + cat >> confdefs.h <<\EOF +#define __CHAR_UNSIGNED__ 1 +EOF + +fi + + +echo $ac_n "checking count field in FILE structures""... $ac_c" 1>&6 +echo "configure:3055: checking count field in FILE structures" >&5 +if eval "test \"`echo '$''{'rb_cv_fcnt'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 3060 "configure" +#include "confdefs.h" +#include <stdio.h> +int main() { +FILE *f = stdin; f->_cnt = 0; +; return 0; } +EOF +if { (eval echo configure:3067: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + rb_cv_fcnt="_cnt" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* +if test "$rb_cv_fcnt" = ""; then + cat > conftest.$ac_ext <<EOF +#line 3077 "configure" +#include "confdefs.h" +#include <stdio.h> +int main() { +FILE *f = stdin; f->__cnt = 0; +; return 0; } +EOF +if { (eval echo configure:3084: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + rb_cv_fcnt="__cnt" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* +fi +if test "$rb_cv_fcnt" = ""; then + cat > conftest.$ac_ext <<EOF +#line 3095 "configure" +#include "confdefs.h" +#include <stdio.h> +int main() { +FILE *f = stdin; f->_r = 0; +; return 0; } +EOF +if { (eval echo configure:3102: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + rb_cv_fcnt="_r" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* +fi +if test "$rb_cv_fcnt" = ""; then + cat > conftest.$ac_ext <<EOF +#line 3113 "configure" +#include "confdefs.h" +#include <stdio.h> +int main() { +FILE *f = stdin; f->readCount = 0; +; return 0; } +EOF +if { (eval echo configure:3120: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + rb_cv_fcnt="readCount" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + rb_cv_fcnt="not found" +fi +rm -f conftest* +fi +fi + +if test "$rb_cv_fcnt" = "not found"; then + echo "$ac_t""not found(OK if using GNU libc)" 1>&6 +else + echo "$ac_t""$rb_cv_fcnt" 1>&6 + cat >> confdefs.h <<EOF +#define FILE_COUNT $rb_cv_fcnt +EOF + +fi + +if test "$ac_cv_func_getpwent" = yes; then + echo $ac_n "checking struct passwd""... $ac_c" 1>&6 +echo "configure:3145: checking struct passwd" >&5 + cat > conftest.$ac_ext <<EOF +#line 3147 "configure" +#include "confdefs.h" +#include <pwd.h> +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "pw_change" >/dev/null 2>&1; then + rm -rf conftest* + cat >> confdefs.h <<\EOF +#define PW_CHANGE 1 +EOF + +fi +rm -f conftest* + + cat > conftest.$ac_ext <<EOF +#line 3162 "configure" +#include "confdefs.h" +#include <pwd.h> +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "pw_quota" >/dev/null 2>&1; then + rm -rf conftest* + cat >> confdefs.h <<\EOF +#define PW_QUOTA 1 +EOF + +fi +rm -f conftest* + + cat > conftest.$ac_ext <<EOF +#line 3177 "configure" +#include "confdefs.h" +#include <pwd.h> +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "pw_age" >/dev/null 2>&1; then + rm -rf conftest* + cat >> confdefs.h <<\EOF +#define PW_AGE 1 +EOF + +fi +rm -f conftest* + + cat > conftest.$ac_ext <<EOF +#line 3192 "configure" +#include "confdefs.h" +#include <pwd.h> +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "pw_class" >/dev/null 2>&1; then + rm -rf conftest* + cat >> confdefs.h <<\EOF +#define PW_CLASS 1 +EOF + +fi +rm -f conftest* + + cat > conftest.$ac_ext <<EOF +#line 3207 "configure" +#include "confdefs.h" +#include <pwd.h> +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "pw_comment" >/dev/null 2>&1; then + rm -rf conftest* + cat >> confdefs.h <<\EOF +#define PW_COMMENT 1 +EOF + +fi +rm -f conftest* + + cat > conftest.$ac_ext <<EOF +#line 3222 "configure" +#include "confdefs.h" +#include <pwd.h> +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "pw_expire" >/dev/null 2>&1; then + rm -rf conftest* + cat >> confdefs.h <<\EOF +#define PW_EXPIRE 1 +EOF + +fi +rm -f conftest* + + echo "$ac_t""done" 1>&6 +fi + +# Check whether --with-dln-a-out or --without-dln-a-out was given. +if test "${with_dln_a_out+set}" = set; then + withval="$with_dln_a_out" + + case $withval in + yes) with_dln_a_out=yes;; + *) with_dln_a_out=no;; + esac +else + with_dln_a_out=no +fi + + +case "$host_os" in + linux*) + echo $ac_n "checking whether ELF binaries are produced""... $ac_c" 1>&6 +echo "configure:3255: checking whether ELF binaries are produced" >&5 + if eval "test \"`echo '$''{'rb_cv_linux_elf'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + : +else + cat > conftest.$ac_ext <<EOF +#line 3263 "configure" +#include "confdefs.h" + +/* Test for whether ELF binaries are produced */ +#include <fcntl.h> +#include <stdlib.h> +main() { + char buffer[4]; + int i=open("conftest",O_RDONLY); + if(i==-1) + exit(1); /* fail */ + if(read(i,&buffer[0],4)<4) + exit(1); /* fail */ + if(buffer[0] != 127 || buffer[1] != 'E' || + buffer[2] != 'L' || buffer[3] != 'F') + exit(1); /* fail */ + exit(0); /* succeed (yes, it's ELF) */ +} + +EOF +if { (eval echo configure:3283: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + rb_cv_linux_elf=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + rb_cv_linux_elf=no +fi +rm -fr conftest* +fi + +fi + + echo "$ac_t""$rb_cv_linux_elf" 1>&6 + if test "$rb_cv_linux_elf" = no; then + with_dln_a_out=yes + host_os=linux-a.out + else + LDFLAGS="-rdynamic" + fi;; +esac + + + +STATIC= + +if test "$with_dln_a_out" != yes; then + rb_cv_dlopen=unknown + echo $ac_n "checking whether OS depend dynamic link works""... $ac_c" 1>&6 +echo "configure:3313: checking whether OS depend dynamic link works" >&5 + if test "$GCC" = yes; then + case "$host_os" in + nextstep*) ;; + human*) ;; + *) CCDLFLAGS=-fpic;; + esac + else + case "$host_os" in + hpux*) CCDLFLAGS='+z';; + solaris*|irix*) CCDLFLAGS='-K pic' ;; + sunos*) CCDLFLAGS='-pic' ;; + esix*|uxpds*) CCDLFLAGS='-Kpic' ;; + *) CCDLFLAGS='' ;; + esac + fi + + case "$host_os" in + hpux*) DLDFLAGS="-E" + LDSHARED='ld -b' + LDFLAGS="-Wl,-E" + rb_cv_dlopen=yes;; + solaris*) LDSHARED='ld -G' + rb_cv_dlopen=yes;; + sunos*) LDSHARED='ld -assert nodefinitions' + rb_cv_dlopen=yes;; + irix*) LDSHARED='ld -ignore_unresolved' + rb_cv_dlopen=yes;; + sysv4*) LDSHARED='ld -G' + rb_cv_dlopen=yes;; + esix*|uxpds*) LDSHARED="ld -G" + rb_cv_dlopen=yes ;; + linux*) LDSHARED="gcc -shared" + rb_cv_dlopen=yes ;; + freebsd*) LDSHARED="ld -Bshareable" + rb_cv_dlopen=yes ;; + netbsd*) LDSHARED="ld -Bshareable" + rb_cv_dlopen=yes ;; + openbsd*) LDSHARED="ld -Bshareable" + rb_cv_dlopen=yes ;; + nextstep*) LDSHARED='cc -r' + LDFLAGS="-u libsys_s" + DLDFLAGS="$ARCH_FLAG" + rb_cv_dlopen=yes ;; + aix*) LDSHARED='../../miniruby ../aix_ld.rb $(TARGET)' + rb_cv_dlopen=yes ;; + human*) DLDFLAGS='' + LDSHARED='' + LDFLAGS='' ;; + *) LDSHARED='ld' ;; + esac + echo "$ac_t""$rb_cv_dlopen" 1>&6 +fi + +dln_a_out_works=no +if test "$ac_cv_header_a_out_h" = yes; then + if test "$with_dln_a_out" = yes || test "$rb_cv_dlopen" = unknown; then + echo $ac_n "checking whether matz's dln works""... $ac_c" 1>&6 +echo "configure:3371: checking whether matz's dln works" >&5 + cat confdefs.h > config.h + if eval "test \"`echo '$''{'rb_cv_dln_a_out'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 3377 "configure" +#include "confdefs.h" + +#define USE_DLN_A_OUT +#include "dln.c" + +int main() { + +; return 0; } +EOF +if { (eval echo configure:3387: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + rb_cv_dln_a_out=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + rb_cv_dln_a_out=no +fi +rm -f conftest* +fi + + echo "$ac_t""$rb_cv_dln_a_out" 1>&6 + if test "$rb_cv_dln_a_out" = yes; then + dln_a_out_works=yes + cat >> confdefs.h <<\EOF +#define USE_DLN_A_OUT 1 +EOF + + fi + fi +fi + +if test "$dln_a_out_works" = yes; then + if test "$GCC" = yes; then + STATIC=-static + else + STATIC=-Bstatic + fi + DLEXT=o + cat >> confdefs.h <<\EOF +#define DLEXT ".o" +EOF + + CCDLFLAGS= +else + case "$host_os" in + hpux*) DLEXT=sl + cat >> confdefs.h <<\EOF +#define DLEXT ".sl" +EOF +;; + nextstep*) DLEXT=o + cat >> confdefs.h <<\EOF +#define DLEXT ".o" +EOF +;; + *) DLEXT=so + cat >> confdefs.h <<\EOF +#define DLEXT ".so" +EOF +;; + esac +fi + +if test "$with_dln_a_out" = yes; then + STRIP=true +else + STRIP=strip +fi + +case "$host_os" in + linux*) + STRIP='strip -S -x';; + nextstep*) + STRIP='strip -A -n';; +esac + +EXTSTATIC= +# Check whether --with-static-linked-ext or --without-static-linked-ext was given. +if test "${with_static_linked_ext+set}" = set; then + withval="$with_static_linked_ext" + case $withval in + yes) STATIC= + EXTSTATIC=static;; + *) ;; + esac +fi + + +case "$host_os" in + human*) + echo $ac_n "checking for _harderr in -lsignal""... $ac_c" 1>&6 +echo "configure:3470: checking for _harderr in -lsignal" >&5 +ac_lib_var=`echo signal'_'_harderr | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lsignal $LIBS" +cat > conftest.$ac_ext <<EOF +#line 3478 "configure" +#include "confdefs.h" +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char _harderr(); + +int main() { +_harderr() +; return 0; } +EOF +if { (eval echo configure:3489: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo signal | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <<EOF +#define $ac_tr_lib 1 +EOF + + LIBS="-lsignal $LIBS" + +else + echo "$ac_t""no" 1>&6 +fi + + echo $ac_n "checking for hmemset in -lhmem""... $ac_c" 1>&6 +echo "configure:3517: checking for hmemset in -lhmem" >&5 +ac_lib_var=`echo hmem'_'hmemset | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lhmem $LIBS" +cat > conftest.$ac_ext <<EOF +#line 3525 "configure" +#include "confdefs.h" +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char hmemset(); + +int main() { +hmemset() +; return 0; } +EOF +if { (eval echo configure:3536: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo hmem | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <<EOF +#define $ac_tr_lib 1 +EOF + + LIBS="-lhmem $LIBS" + +else + echo "$ac_t""no" 1>&6 +fi + + for ac_func in select +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:3566: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 3571 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:3594: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <<EOF +#define $ac_tr_func 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + + echo $ac_n "checking whether PD libc _dtos18 fail to convert big number""... $ac_c" 1>&6 +echo "configure:3619: checking whether PD libc _dtos18 fail to convert big number" >&5 + if eval "test \"`echo '$''{'rb_cv_missing__dtos18'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else + cat > conftest.$ac_ext <<EOF +#line 3627 "configure" +#include "confdefs.h" + +#include <stdio.h> +main () +{ + char buf[256]; + sprintf (buf, "%g", 1e+300); + exit (strcmp (buf, "1e+300") ? 0 : 1); +} + +EOF +if { (eval echo configure:3639: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + rb_cv_missing__dtos18=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + rb_cv_missing__dtos18=no +fi +rm -fr conftest* +fi + +fi + + echo "$ac_t""$rb_cv_missing__dtos18" 1>&6 + if test "$rb_cv_missing__dtos18" = yes; then + cat >> confdefs.h <<\EOF +#define MISSING__DTOS18 1 +EOF + + fi + echo $ac_n "checking whether PD libc fconvert fail to round""... $ac_c" 1>&6 +echo "configure:3661: checking whether PD libc fconvert fail to round" >&5 + if eval "test \"`echo '$''{'rb_cv_missing_fconvert'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else + cat > conftest.$ac_ext <<EOF +#line 3669 "configure" +#include "confdefs.h" + +#include <stdio.h> +#include <math.h> +main () +{ + char buf[256]; + sprintf (buf, "%f", log(exp(1.0))); + exit (strcmp (buf, "1.000000") ? 0 : 1); +} + +EOF +if { (eval echo configure:3682: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + rb_cv_missing_fconvert=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + rb_cv_missing_fconvert=no +fi +rm -fr conftest* +fi + +fi + + echo "$ac_t""$rb_cv_missing_fconvert" 1>&6 + if test "$rb_cv_missing_fconvert" = yes; then + cat >> confdefs.h <<\EOF +#define MISSING_FCONVERT 1 +EOF + + fi + LIBOBJS="$LIBOBJS x68.o" + CFLAGS="$CFLAGS -fansi-only -cc1-stack=196608 -cpp-stack=2694144" + binsuffix=.x + setup=Setup.x68 + ;; + *) + binsuffix= + setup=Setup + ;; +esac + + + +if test "$prefix" = NONE; then + prefix=$ac_default_prefix +fi + +if test "$fat_binary" = yes ; then + CFLAGS="$CFLAGS -pipe $ARCH_FLAG" +fi + +cat >> confdefs.h <<EOF +#define RUBY_LIB "${prefix}/lib/ruby" +EOF + + +if test "$fat_binary" = yes ; then + arch="fat-${host_os}" + + cat >> confdefs.h <<EOF +#define RUBY_THIN_ARCHLIB "${prefix}/lib/ruby/" __ARCHITECTURE__ "-${host_os}" +EOF + + + cat >> confdefs.h <<EOF +#define RUBY_ARCHLIB "${prefix}/lib/ruby/${arch}" +EOF + + cat >> confdefs.h <<EOF +#define RUBY_PLATFORM __ARCHITECTURE__ "-${host_os}" +EOF + +else + arch="${host_cpu}-${host_os}" + cat >> confdefs.h <<EOF +#define RUBY_ARCHLIB "${prefix}/lib/ruby/${arch}" +EOF + + cat >> confdefs.h <<EOF +#define RUBY_PLATFORM "${arch}" +EOF + +fi + +echo "creating config.h" +cat confdefs.h > config.h + +trap '' 1 2 15 +cat > confcache <<\EOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +EOF +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +(set) 2>&1 | + case `(ac_space=' '; set) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote substitution + # turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + -e "s/'/'\\\\''/g" \ + -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' + ;; + esac >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +cat > conftest.defs <<\EOF +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%-D\1=\2%g +s%[ `~#$^&*(){}\\|;'"<>?]%\\&%g +s%\[%\\&%g +s%\]%\\&%g +s%\$%$$%g +EOF +DEFS=`sed -f conftest.defs confdefs.h | tr '\012' ' '` +rm -f conftest.defs + + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS <<EOF +#! /bin/sh +# Generated automatically by configure. +# Run this file to recreate the current configuration. +# This directory was configured as follows, +# on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" +for ac_option +do + case "\$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" + exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; + -version | --version | --versio | --versi | --vers | --ver | --ve | --v) + echo "$CONFIG_STATUS generated by autoconf version 2.12" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "\$ac_cs_usage"; exit 0 ;; + *) echo "\$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=$srcdir +ac_given_INSTALL="$INSTALL" + +trap 'rm -fr `echo "Makefile ext/extmk.rb" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 +EOF +cat >> $CONFIG_STATUS <<EOF + +# Protect against being on the right side of a sed subst in config.status. +sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g; + s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF +$ac_vpsub +$extrasub +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@bindir@%$bindir%g +s%@sbindir@%$sbindir%g +s%@libexecdir@%$libexecdir%g +s%@datadir@%$datadir%g +s%@sysconfdir@%$sysconfdir%g +s%@sharedstatedir@%$sharedstatedir%g +s%@localstatedir@%$localstatedir%g +s%@libdir@%$libdir%g +s%@includedir@%$includedir%g +s%@oldincludedir@%$oldincludedir%g +s%@infodir@%$infodir%g +s%@mandir@%$mandir%g +s%@host@%$host%g +s%@host_alias@%$host_alias%g +s%@host_cpu@%$host_cpu%g +s%@host_vendor@%$host_vendor%g +s%@host_os@%$host_os%g +s%@CC@%$CC%g +s%@CPP@%$CPP%g +s%@YACC@%$YACC%g +s%@RANLIB@%$RANLIB%g +s%@AR@%$AR%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_DATA@%$INSTALL_DATA%g +s%@SET_MAKE@%$SET_MAKE%g +s%@LIBOBJS@%$LIBOBJS%g +s%@ALLOCA@%$ALLOCA%g +s%@DLDFLAGS@%$DLDFLAGS%g +s%@STATIC@%$STATIC%g +s%@CCDLFLAGS@%$CCDLFLAGS%g +s%@LDSHARED@%$LDSHARED%g +s%@DLEXT@%$DLEXT%g +s%@STRIP@%$STRIP%g +s%@EXTSTATIC@%$EXTSTATIC%g +s%@binsuffix@%$binsuffix%g +s%@setup@%$setup%g +s%@arch@%$arch%g + +CEOF +EOF + +cat >> $CONFIG_STATUS <<\EOF + +# Split the substitutions into bite-sized pieces for seds with +# small command number limits, like on Digital OSF/1 and HP-UX. +ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. +ac_file=1 # Number of current file. +ac_beg=1 # First line for current file. +ac_end=$ac_max_sed_cmds # Line after last line for current file. +ac_more_lines=: +ac_sed_cmds="" +while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file + else + sed "${ac_end}q" conftest.subs > conftest.s$ac_file + fi + if test ! -s conftest.s$ac_file; then + ac_more_lines=false + rm -f conftest.s$ac_file + else + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f conftest.s$ac_file" + else + ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" + fi + ac_file=`expr $ac_file + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_cmds` + fi +done +if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat +fi +EOF + +cat >> $CONFIG_STATUS <<EOF + +CONFIG_FILES=\${CONFIG_FILES-"Makefile ext/extmk.rb"} +EOF +cat >> $CONFIG_STATUS <<\EOF +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + case "$ac_given_INSTALL" in + [/$]*) INSTALL="$ac_given_INSTALL" ;; + *) INSTALL="$ac_dots$ac_given_INSTALL" ;; + esac + + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +s%@INSTALL@%$INSTALL%g +" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file +fi; done +rm -f conftest.s* + +EOF +cat >> $CONFIG_STATUS <<EOF + +EOF +cat >> $CONFIG_STATUS <<\EOF + +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + diff --git a/configure.bat b/configure.bat new file mode 100644 index 0000000000..35f9d4893d --- /dev/null +++ b/configure.bat @@ -0,0 +1,5 @@ +@echo off +sed -f top.sed Makefile.in >Makefile +sed -f top.sed ext/extmk.rb.in > ext\extmk.rb +copy ext\Setup.dj ext\Setup +copy config.dj config.h diff --git a/configure.in b/configure.in new file mode 100644 index 0000000000..567e58cc3e --- /dev/null +++ b/configure.in @@ -0,0 +1,474 @@ +dnl Process this file with autoconf to produce a configure script. +AC_INIT(ruby.h) + +dnl checks for alternative programs +AC_ARG_WITH(gcc, [--without-gcc never use gcc], [ + case $withval in + no) CC=cc + without_gcc=yes;; + yes) CC=gcc + without_gcc=no;; + *) CC=$withval + without_gcc=$withval;; + esac], [without_gcc=no]) +dnl If the user switches compilers, we can't believe the cache +if test ! -z "$ac_cv_prog_CC" -a ! -z "$CC" -a "$CC" != "$ac_cv_prog_CC" +then + AC_ERROR(cached CC is different -- throw away $cache_file +(it is also a good idea to do 'make clean' before compiling)) +fi + +dnl checks for thread +rb_thread=yes +AC_ARG_ENABLE(thread, [--disable-thread never use user-level thread], [ + rb_thread=$enableval +]) +if test $rb_thread = yes; then + AC_DEFINE(THREAD) +fi + +AC_CANONICAL_HOST + + +dnl checks for fat-binary +fat_binary=no +AC_ARG_ENABLE( fat-binary, + [--enable-fat-binary build a NeXT Multi Architecture Binary. ], + [ fat_binary=$enableval ] ) +if test "$fat_binary" = yes ; then + + AC_MSG_CHECKING( target architecture ) + + if test "$TARGET_ARCHS" = "" ; then + if test `/usr/bin/arch` = "m68k" ; then + TARGET_ARCHS="m68k i486" + else + TARGET_ARCHS="m68k `/usr/bin/arch`" + fi + fi + # /usr/lib/arch_tool -archify_list $TARGET_ARCHS + for archs in $TARGET_ARCHS + do + ARCH_FLAG="$ARCH_FLAG -arch $archs " + echo -n " $archs" + done + AC_DEFINE( NEXT_FAT_BINARY ) + echo "." +fi + + +AC_ARG_PROGRAM + +dnl Checks for programs. +AC_PROG_CC +AC_PROG_GCC_TRADITIONAL +AC_PROG_YACC +AC_PROG_RANLIB +AC_SUBST(AR) +AC_CHECK_PROGS(AR, ar aal, ar) +AC_PROG_INSTALL +AC_PROG_MAKE_SET + +# checks for UNIX variants that set C preprocessor variables +AC_MINIX + +dnl Checks for libraries. +case "$host_os" in +nextstep*) ;; +human*) ;; +*) 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 + +dnl Checks for header files. +AC_HEADER_DIRENT +AC_HEADER_STDC +AC_CHECK_HEADERS(stdlib.h unistd.h limits.h sys/file.h sys/ioctl.h pwd.h \ + sys/select.h sys/time.h sys/times.h sys/param.h sys/wait.h\ + syscall.h a.out.h string.h utime.h memory.h) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_TYPE_UID_T +AC_TYPE_SIZE_T +AC_STRUCT_ST_BLKSIZE +save_LIBOJBS="$LIBOBJS" +AC_STRUCT_ST_BLOCKS +LIBOBJS="$save_LIBOBJS" +AC_STRUCT_ST_RDEV + +AC_CHECK_SIZEOF(short) +AC_CHECK_SIZEOF(int) +AC_CHECK_SIZEOF(long) +AC_CHECK_SIZEOF(void*) + +dnl Checks for library functions. +AC_TYPE_GETGROUPS +AC_TYPE_SIGNAL +AC_FUNC_ALLOCA +AC_FUNC_VFORK +AC_REPLACE_FUNCS(dup2 setenv memmove mkdir strcasecmp strerror strftime\ + strstr strtoul strdup crypt flock) +AC_CHECK_FUNCS(fmod killpg random wait4 waitpid syscall getcwd\ + truncate chsize times utimes fcntl lockf setitimer\ + setruid seteuid setreuid setrgid setegid setregid\ + setpgrp2 getpgid getgroups getpriority\ + dlopen sigprocmask sigaction _setjmp) +if test "$ac_cv_func_strftime" = no; then + AC_STRUCT_TIMEZONE + AC_TRY_LINK([], + [extern int daylight; int i = daylight;], AC_DEFINE(HAVE_DAYLIGHT)) +fi + +if test "$ac_cv_func_sigprocmask" = yes && test "$ac_cv_func_sigaction" = yes; then + AC_DEFINE(POSIX_SIGNAL) +else + AC_MSG_CHECKING(for BSD signal semantics) + AC_CACHE_VAL(rb_cv_bsd_signal, + [AC_TRY_RUN([ +#include <stdio.h> +#include <signal.h> + +void +sig_handler(dummy) + int dummy; +{ +} + +int +main() +{ + signal(SIGINT, sig_handler); + kill(getpid(), SIGINT); + kill(getpid(), SIGINT); + return 0; +} +], + rb_cv_bsd_signal=yes, + rb_cv_bsd_signal=no)]) + AC_MSG_RESULT($rb_cv_bsd_signal) + if test "$rb_cv_bsd_signal" = yes; then + AC_DEFINE(BSD_SIGNAL) + fi +fi + +if test "$ac_cv_func_setpgrp2" = yes; then + AC_DEFINE(BSD_GETPGRP, getpgrp2) + AC_DEFINE(BSD_SETPGRP, setpgrp2) +else + AC_MSG_CHECKING(whether getpgrp() has arg) + AC_CACHE_VAL(rb_cv_bsdgetpgrp, + [AC_TRY_COMPILE([#include <unistd.h>], [getpgrp(0);], + rb_cv_bsdgetpgrp=yes, + rb_cv_bsdgetpgrp=no)]) + AC_MSG_RESULT($rb_cv_bsdgetpgrp) + if test "$rb_cv_bsdgetpgrp" = yes; then + AC_DEFINE(BSD_GETPGRP, getpgrp) + fi + + AC_MSG_CHECKING(whether setpgrp() has args) + AC_CACHE_VAL(rb_cv_bsdsetpgrp, + [AC_TRY_COMPILE([#include <unistd.h>], [setpgrp(1, 1);], + rb_cv_bsdsetpgrp=yes, + rb_cv_bsdsetpgrp=no)]) + AC_MSG_RESULT($rb_cv_bsdsetpgrp) + if test "$rb_cv_bsdsetpgrp" = yes; then + AC_DEFINE(BSD_SETPGRP, setpgrp) + fi +fi + +AC_C_BIGENDIAN +AC_CHAR_UNSIGNED + +AC_MSG_CHECKING([count field in FILE structures]) +AC_CACHE_VAL(rb_cv_fcnt, +[AC_TRY_COMPILE([#include <stdio.h>], + [FILE *f = stdin; f->_cnt = 0;], rb_cv_fcnt="_cnt", ) +if test "$rb_cv_fcnt" = ""; then + AC_TRY_COMPILE([#include <stdio.h>], + [FILE *f = stdin; f->__cnt = 0;], rb_cv_fcnt="__cnt", ) +fi +if test "$rb_cv_fcnt" = ""; then + AC_TRY_COMPILE([#include <stdio.h>], + [FILE *f = stdin; f->_r = 0;], rb_cv_fcnt="_r", ) +fi +if test "$rb_cv_fcnt" = ""; then + AC_TRY_COMPILE([#include <stdio.h>], + [FILE *f = stdin; f->readCount = 0;], + rb_cv_fcnt="readCount", rb_cv_fcnt="not found") +fi]) +if test "$rb_cv_fcnt" = "not found"; then + AC_MSG_RESULT([not found(OK if using GNU libc)]) +else + AC_MSG_RESULT($rb_cv_fcnt) + AC_DEFINE_UNQUOTED(FILE_COUNT, $rb_cv_fcnt) +fi + +if test "$ac_cv_func_getpwent" = yes; then + AC_MSG_CHECKING(struct passwd) + AC_EGREP_HEADER(pw_change, pwd.h, AC_DEFINE(PW_CHANGE)) + AC_EGREP_HEADER(pw_quota, pwd.h, AC_DEFINE(PW_QUOTA)) + AC_EGREP_HEADER(pw_age, pwd.h, AC_DEFINE(PW_AGE)) + AC_EGREP_HEADER(pw_class, pwd.h, AC_DEFINE(PW_CLASS)) + AC_EGREP_HEADER(pw_comment, pwd.h, AC_DEFINE(PW_COMMENT)) + AC_EGREP_HEADER(pw_expire, pwd.h, AC_DEFINE(PW_EXPIRE)) + AC_MSG_RESULT(done) +fi + +dnl wheather use dln_a_out ot not +AC_ARG_WITH(dln-a-out, [--with-dln-a-out use dln_a_out if possible], [ + case $withval in + yes) with_dln_a_out=yes;; + *) with_dln_a_out=no;; + esac], [with_dln_a_out=no]) + +case "$host_os" in + linux*) + AC_MSG_CHECKING(whether ELF binaries are produced) + AC_CACHE_VAL(rb_cv_linux_elf, + [AC_TRY_RUN([ +/* Test for whether ELF binaries are produced */ +#include <fcntl.h> +#include <stdlib.h> +main() { + char buffer[4]; + int i=open("conftest",O_RDONLY); + if(i==-1) + exit(1); /* fail */ + if(read(i,&buffer[0],4)<4) + exit(1); /* fail */ + if(buffer[0] != 127 || buffer[1] != 'E' || + buffer[2] != 'L' || buffer[3] != 'F') + exit(1); /* fail */ + exit(0); /* succeed (yes, it's ELF) */ +} +], + rb_cv_linux_elf=yes, + rb_cv_linux_elf=no, + [:])]) + AC_MSG_RESULT($rb_cv_linux_elf) + if test "$rb_cv_linux_elf" = no; then + with_dln_a_out=yes + host_os=linux-a.out + else + LDFLAGS="-rdynamic" + fi;; +esac + +AC_SUBST(DLDFLAGS)dnl + +AC_SUBST(STATIC)dnl +AC_SUBST(CCDLFLAGS)dnl +AC_SUBST(LDSHARED)dnl +AC_SUBST(DLEXT)dnl + +STATIC= + +if test "$with_dln_a_out" != yes; then + rb_cv_dlopen=unknown + AC_MSG_CHECKING(whether OS depend dynamic link works) + if test "$GCC" = yes; then + case "$host_os" in + nextstep*) ;; + human*) ;; + *) CCDLFLAGS=-fpic;; + esac + else + case "$host_os" in + hpux*) CCDLFLAGS='+z';; + solaris*|irix*) CCDLFLAGS='-K pic' ;; + sunos*) CCDLFLAGS='-pic' ;; + esix*|uxpds*) CCDLFLAGS='-Kpic' ;; + *) CCDLFLAGS='' ;; + esac + fi + + case "$host_os" in + hpux*) DLDFLAGS="-E" + LDSHARED='ld -b' + LDFLAGS="-Wl,-E" + rb_cv_dlopen=yes;; + solaris*) LDSHARED='ld -G' + rb_cv_dlopen=yes;; + sunos*) LDSHARED='ld -assert nodefinitions' + rb_cv_dlopen=yes;; + irix*) LDSHARED='ld -ignore_unresolved' + rb_cv_dlopen=yes;; + sysv4*) LDSHARED='ld -G' + rb_cv_dlopen=yes;; + esix*|uxpds*) LDSHARED="ld -G" + rb_cv_dlopen=yes ;; + linux*) LDSHARED="gcc -shared" + rb_cv_dlopen=yes ;; + freebsd*) LDSHARED="ld -Bshareable" + rb_cv_dlopen=yes ;; + netbsd*) LDSHARED="ld -Bshareable" + rb_cv_dlopen=yes ;; + openbsd*) LDSHARED="ld -Bshareable" + rb_cv_dlopen=yes ;; + nextstep*) LDSHARED='cc -r' + LDFLAGS="-u libsys_s" + DLDFLAGS="$ARCH_FLAG" + rb_cv_dlopen=yes ;; + aix*) LDSHARED='../../miniruby ../aix_ld.rb $(TARGET)' + rb_cv_dlopen=yes ;; + human*) DLDFLAGS='' + LDSHARED='' + LDFLAGS='' ;; + *) LDSHARED='ld' ;; + esac + AC_MSG_RESULT($rb_cv_dlopen) +fi + +dln_a_out_works=no +if test "$ac_cv_header_a_out_h" = yes; then + if test "$with_dln_a_out" = yes || test "$rb_cv_dlopen" = unknown; then + AC_MSG_CHECKING(whether matz's dln works) + cat confdefs.h > config.h + AC_CACHE_VAL(rb_cv_dln_a_out, + [AC_TRY_COMPILE([ +#define USE_DLN_A_OUT +#include "dln.c" +], + [], + rb_cv_dln_a_out=yes, + rb_cv_dln_a_out=no)]) + AC_MSG_RESULT($rb_cv_dln_a_out) + if test "$rb_cv_dln_a_out" = yes; then + dln_a_out_works=yes + AC_DEFINE(USE_DLN_A_OUT) + fi + fi +fi + +if test "$dln_a_out_works" = yes; then + if test "$GCC" = yes; then + STATIC=-static + else + STATIC=-Bstatic + fi + DLEXT=o + AC_DEFINE(DLEXT, ".o") + CCDLFLAGS= +else + case "$host_os" in + hpux*) DLEXT=sl + AC_DEFINE(DLEXT, ".sl");; + nextstep*) DLEXT=o + AC_DEFINE(DLEXT, ".o");; + *) DLEXT=so + AC_DEFINE(DLEXT, ".so");; + esac +fi + +AC_SUBST(STRIP)dnl +if test "$with_dln_a_out" = yes; then + STRIP=true +else + STRIP=strip +fi + +case "$host_os" in + linux*) + STRIP='strip -S -x';; + nextstep*) + STRIP='strip -A -n';; +esac + +EXTSTATIC= +AC_SUBST(EXTSTATIC)dnl +AC_ARG_WITH(static-linked-ext, + [--with-static-linked-ext link external modules statically], + [case $withval in + yes) STATIC= + EXTSTATIC=static;; + *) ;; + esac]) + +case "$host_os" in + human*) + AC_CHECK_LIB(signal, _harderr) + AC_CHECK_LIB(hmem, hmemset) + AC_CHECK_FUNCS(select) + AC_MSG_CHECKING(whether PD libc _dtos18 fail to convert big number) + AC_CACHE_VAL(rb_cv_missing__dtos18, + [AC_TRY_RUN( +changequote(<<, >>)dnl +<< +#include <stdio.h> +main () +{ + char buf[256]; + sprintf (buf, "%g", 1e+300); + exit (strcmp (buf, "1e+300") ? 0 : 1); +} +>>, +changequote([, ])dnl +rb_cv_missing__dtos18=yes, rb_cv_missing__dtos18=no)]) + AC_MSG_RESULT($rb_cv_missing__dtos18) + if test "$rb_cv_missing__dtos18" = yes; then + AC_DEFINE(MISSING__DTOS18) + fi + AC_MSG_CHECKING(whether PD libc fconvert fail to round) + AC_CACHE_VAL(rb_cv_missing_fconvert, + [AC_TRY_RUN( +changequote(<<, >>)dnl +<< +#include <stdio.h> +#include <math.h> +main () +{ + char buf[256]; + sprintf (buf, "%f", log(exp(1.0))); + exit (strcmp (buf, "1.000000") ? 0 : 1); +} +>>, +changequote([, ])dnl +rb_cv_missing_fconvert=yes, rb_cv_missing_fconvert=no)]) + AC_MSG_RESULT($rb_cv_missing_fconvert) + if test "$rb_cv_missing_fconvert" = yes; then + AC_DEFINE(MISSING_FCONVERT) + fi + LIBOBJS="$LIBOBJS x68.o" + CFLAGS="$CFLAGS -fansi-only -cc1-stack=196608 -cpp-stack=2694144" + binsuffix=.x + setup=Setup.x68 + ;; + *) + binsuffix= + setup=Setup + ;; +esac +AC_SUBST(binsuffix) +AC_SUBST(setup) + +if test "$prefix" = NONE; then + prefix=$ac_default_prefix +fi + +if test "$fat_binary" = yes ; then + CFLAGS="$CFLAGS -pipe $ARCH_FLAG" +fi + +AC_DEFINE_UNQUOTED(RUBY_LIB, "${prefix}/lib/ruby") +AC_SUBST(arch)dnl + +if test "$fat_binary" = yes ; then + arch="fat-${host_os}" + + AC_DEFINE_UNQUOTED(RUBY_THIN_ARCHLIB, + "${prefix}/lib/ruby/" __ARCHITECTURE__ "-${host_os}" ) + + AC_DEFINE_UNQUOTED(RUBY_ARCHLIB, "${prefix}/lib/ruby/${arch}") + AC_DEFINE_UNQUOTED(RUBY_PLATFORM, __ARCHITECTURE__ "-${host_os}" ) +else + arch="${host_cpu}-${host_os}" + AC_DEFINE_UNQUOTED(RUBY_ARCHLIB, "${prefix}/lib/ruby/${arch}") + AC_DEFINE_UNQUOTED(RUBY_PLATFORM, "${arch}") +fi + +echo "creating config.h" +cat confdefs.h > config.h + +AC_OUTPUT(Makefile ext/extmk.rb) diff --git a/defines.h b/defines.h new file mode 100644 index 0000000000..74736ad629 --- /dev/null +++ b/defines.h @@ -0,0 +1,50 @@ +/************************************************ + + defines.h - + + $Author$ + $Date$ + created at: Wed May 18 00:21:44 JST 1994 + +************************************************/ +#ifndef DEFINES_H +#define DEFINES_H + +#define RUBY + +/* define EUC/SJIS for default kanji-code */ +#if defined(MSDOS) || defined(__CYGWIN32__) || defined(__human68k__) +#undef EUC +#define SJIS +#else +#define EUC +#undef SJIS +#endif + +#ifdef NeXT +#define DYNAMIC_ENDIAN /* determine endian at runtime */ +#define S_IXUSR _S_IXUSR /* execute/search permission, owner */ +#define S_IXGRP 0000010 /* execute/search permission, group */ +#define S_IXOTH 0000001 /* execute/search permission, other */ +#endif /* NeXT */ + +#ifdef NT +#include "missing/nt.h" +#endif + +#ifdef sparc +#define FLUSH_REGISTER_WINDOWS asm("ta 3") +#else +#define FLUSH_REGISTER_WINDOWS /* empty */ +#endif + +#ifdef __human68k__ +#undef HAVE_RANDOM +#undef HAVE_SETITIMER +#endif + +#ifndef RUBY_PLATFORM +#define RUBY_PLATFORM "unknown-unknown" +#endif + +#endif @@ -0,0 +1,427 @@ +/************************************************ + + dir.c - + + $Author$ + $Date$ + created at: Wed Jan 5 09:51:01 JST 1994 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" + +#include <sys/types.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_SYS_PARAM_H +# include <sys/param.h> +#else +# define MAXPATHLEN 1024 +#endif + +#if HAVE_DIRENT_H +# include <dirent.h> +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# if HAVE_SYS_NDIR_H +# include <sys/ndir.h> +# endif +# if HAVE_SYS_DIR_H +# include <sys/dir.h> +# endif +# if HAVE_NDIR_H +# include <ndir.h> +# endif +# ifdef NT +# include "missing/dir.h" +# endif +#endif + +#include <errno.h> + +#ifndef NT +char *getenv(); +#endif + +static VALUE cDir; + +static void +free_dir(dir) + DIR *dir; +{ + if (dir) closedir(dir); +} + +static VALUE +dir_s_open(dir_class, dirname) + VALUE dir_class; + struct RString *dirname; +{ + VALUE obj; + DIR *dirp; + + Check_SafeStr(dirname); + + dirp = opendir(dirname->ptr); + if (dirp == NULL) { + if (errno == EMFILE || errno == ENFILE) { + gc(); + dirp = opendir(dirname->ptr); + } + if (dirp == NULL) { + rb_sys_fail(dirname->ptr); + } + } + + obj = Data_Wrap_Struct(dir_class, 0, free_dir, dirp); + + return obj; +} + +static void +dir_closed() +{ + Fail("closed directory"); +} + +#define GetDIR(obj, dirp) {\ + Data_Get_Struct(obj, DIR, dirp);\ + if (dirp == NULL) dir_closed();\ +} + +static VALUE +dir_each(dir) + VALUE dir; +{ + DIR *dirp; + struct dirent *dp; + VALUE file; + + GetDIR(dir, dirp); + for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { + file = str_taint(str_new(dp->d_name, NAMLEN(dp))); + rb_yield(file); + } + return dir; +} + +static VALUE +dir_tell(dir) + VALUE dir; +{ + DIR *dirp; + int pos; + +#if !defined(__CYGWIN32__) + GetDIR(dir, dirp); + pos = telldir(dirp); + return int2inum(pos); +#else + rb_notimplement(); +#endif +} + +static VALUE +dir_seek(dir, pos) + VALUE dir, pos; +{ + DIR *dirp; + +#if !defined(__CYGWIN32__) + GetDIR(dir, dirp); + seekdir(dirp, NUM2INT(pos)); + return dir; +#else + rb_notimplement(); +#endif +} + +static VALUE +dir_rewind(dir) + VALUE dir; +{ + DIR *dirp; + + GetDIR(dir, dirp); + rewinddir(dirp); + return dir; +} + +static VALUE +dir_close(dir) + VALUE dir; +{ + DIR *dirp; + + Data_Get_Struct(dir, DIR, dirp); + if (dirp == NULL) dir_closed(); + closedir(dirp); + DATA_PTR(dir) = NULL; + + return Qnil; +} + +static VALUE +dir_s_chdir(argc, argv, obj) + int argc; + VALUE *argv; + VALUE obj; +{ + VALUE path; + char *dist = ""; + + rb_secure(2); + rb_scan_args(argc, argv, "01", &path); + if (path) { + Check_SafeStr(path); + dist = RSTRING(path)->ptr; + } + else { + dist = getenv("HOME"); + if (!dist) { + dist = getenv("LOGDIR"); + } + } + + if (chdir(dist) < 0) + rb_sys_fail(0); + + return INT2FIX(0); +} + +static VALUE +dir_s_getwd(dir) + VALUE dir; +{ + extern char *getwd(); + char path[MAXPATHLEN]; + +#ifdef HAVE_GETCWD + if (getcwd(path, sizeof(path)) == 0) rb_sys_fail(path); +#else + if (getwd(path) == 0) rb_sys_fail(path); +#endif + + return str_taint(str_new2(path)); +} + +static VALUE +dir_s_chroot(dir, path) + VALUE dir, path; +{ +#if !defined(DJGPP) && !defined(__CYGWIN32__) && !defined(NT) && !defined(__human68k__) + rb_secure(2); + Check_SafeStr(path); + + if (chroot(RSTRING(path)->ptr) == -1) + rb_sys_fail(0); + + return INT2FIX(0); +#else + rb_notimplement(); +#endif +} + +static VALUE +dir_s_mkdir(argc, argv, obj) + int argc; + VALUE *argv; + VALUE obj; +{ + VALUE path, vmode; + int mode; + + rb_secure(2); + if (rb_scan_args(argc, argv, "11", &path, &vmode) == 2) { + mode = NUM2INT(vmode); + } + else { + mode = 0777; + } + + Check_SafeStr(path); +#ifndef NT + if (mkdir(RSTRING(path)->ptr, mode) == -1) + rb_sys_fail(RSTRING(path)->ptr); +#else + if (mkdir(RSTRING(path)->ptr) == -1) + rb_sys_fail(RSTRING(path)->ptr); +#endif + + return INT2FIX(0); +} + +static VALUE +dir_s_rmdir(obj, dir) + VALUE obj; + struct RString *dir; +{ + rb_secure(2); + Check_SafeStr(dir); + if (rmdir(dir->ptr) < 0) + rb_sys_fail(dir->ptr); + + return TRUE; +} + +#define isdelim(c) ((c)==' '||(c)=='\t'||(c)=='\n'||(c)=='\0') + +char **glob_filename(); +extern char *glob_error_return; + +static void +push_globs(ary, s) + VALUE ary; + char *s; +{ + char **fnames, **ff; + + fnames = glob_filename(s); + if (fnames == (char**)-1) rb_sys_fail(s); + ff = fnames; + while (*ff) { + ary_push(ary, str_taint(str_new2(*ff))); + free(*ff); + ff++; + } + if (fnames != &glob_error_return) { + free(fnames); + } +} + +static void +push_braces(ary, s) + VALUE ary; + char *s; +{ + char buf[MAXPATHLEN]; + char *p, *t, *b; + char *lbrace, *rbrace; + + p = s; + lbrace = rbrace = 0; + while (*p) { + if (*p == '{') { + lbrace = p; + break; + } + p++; + } + while (*p) { + if (*p == '}' && lbrace) { + rbrace = p; + break; + } + p++; + } + + if (lbrace) { + memcpy(buf, s, lbrace-s); + b = buf + (lbrace-s); + p = lbrace; + while (*p != '}') { + t = p + 1; + for (p = t; *p!='}' && *p!=','; p++) { + /* skip inner braces */ + if (*p == '{') while (*p!='}') p++; + } + memcpy(b, t, p-t); + strcpy(b+(p-t), rbrace+1); + push_braces(ary, buf); + } + } + else { + push_globs(ary, s); + } +} + +static VALUE +dir_s_glob(dir, str) + VALUE dir; + struct RString *str; +{ + char *p, *pend; + char buf[MAXPATHLEN]; + char *t, *t0; + int nest; + VALUE ary; + + Check_SafeStr(str); + + ary = ary_new(); + + p = str->ptr; + pend = p + str->len; + + while (p < pend) { + t = buf; + while (p < pend && isdelim(*p)) p++; + while (p < pend && !isdelim(*p)) { + *t++ = *p++; + } + *t = '\0'; + t0 = buf; + nest = 0; + while (t0 < t) { + if (*t0 == '{') nest+=2; + if (*t0 == '}') nest+=3; + t0++; + } + if (nest == 0) { + push_globs(ary, buf); + } + else if (nest % 5 == 0) { + push_braces(ary, buf); + } + /* else unmatched braces */ + } + return ary; +} + +static VALUE +dir_foreach(io, dirname) + VALUE io; + struct RString *dirname; +{ + VALUE dir; + + dir = dir_s_open(cDir, dirname); + return rb_ensure(dir_each, dir, dir_close, dir); +} + +void +Init_Dir() +{ + extern VALUE mEnumerable; + + cDir = rb_define_class("Dir", cObject); + + rb_include_module(cDir, mEnumerable); + + rb_define_singleton_method(cDir, "open", dir_s_open, 1); + rb_define_singleton_method(cDir, "foreach", dir_foreach, 1); + + rb_define_method(cDir,"each", dir_each, 0); + rb_define_method(cDir,"rewind", dir_rewind, 0); + rb_define_method(cDir,"tell", dir_tell, 0); + rb_define_method(cDir,"seek", dir_seek, 1); + rb_define_method(cDir,"close", dir_close, 0); + + rb_define_singleton_method(cDir,"chdir", dir_s_chdir, -1); + rb_define_singleton_method(cDir,"getwd", dir_s_getwd, 0); + rb_define_singleton_method(cDir,"pwd", dir_s_getwd, 0); + rb_define_singleton_method(cDir,"chroot", dir_s_chroot, 1); + rb_define_singleton_method(cDir,"mkdir", dir_s_mkdir, -1); + rb_define_singleton_method(cDir,"rmdir", dir_s_rmdir, 1); + rb_define_singleton_method(cDir,"delete", dir_s_rmdir, 1); + rb_define_singleton_method(cDir,"unlink", dir_s_rmdir, 1); + + rb_define_singleton_method(cDir,"glob", dir_s_glob, 1); + rb_define_singleton_method(cDir,"[]", dir_s_glob, 1); +} @@ -0,0 +1,1445 @@ +/************************************************ + + dln.c - + + $Author$ + $Date$ + created at: Tue Jan 18 17:05:06 JST 1994 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#ifdef _AIX +#pragma alloca +#endif + +#include "config.h" +#include "defines.h" +#include "dln.h" + +char *dln_argv0; + +#if defined(HAVE_ALLOCA_H) && !defined(__GNUC__) +#include <alloca.h> +#endif + +void *xmalloc(); +void *xcalloc(); +void *xrealloc(); + +#include <stdio.h> +#ifndef NT +#include <sys/file.h> +#else +#include "missing/file.h" +#endif +#include <sys/types.h> +#include <sys/stat.h> + +#ifdef HAVE_SYS_PARAM_H +# include <sys/param.h> +#else +# define MAXPATHLEN 1024 +#endif + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#if defined (HAVE_STRING_H) +# include <string.h> +#else +# include <strings.h> +#endif + +#ifndef NT +char *strdup(); + +char *getenv(); +#endif + +int eaccess(); + +#if defined(HAVE_DLOPEN) && !defined(USE_DLN_A_OUT) +/* dynamic load with dlopen() */ +# define USE_DLN_DLOPEN +#endif + +#ifndef FUNCNAME_PATTERN +# if defined(__hp9000s300) || defined(__NetBSD__) || defined(__BORLANDC__) || defined(__FreeBSD__) || defined(NeXT) +# define FUNCNAME_PATTERN "_Init_%.200s" +# else +# define FUNCNAME_PATTERN "Init_%.200s" +# endif +#endif + +static void +init_funcname(buf, file) + char *buf, *file; +{ + char *p, *slash; + + /* Load the file as an object one */ + for (p = file, slash = p-1; *p; p++) /* Find position of last '/' */ + if (*p == '/') slash = p; + + sprintf(buf, FUNCNAME_PATTERN, slash + 1); + for (p = buf; *p; p++) { /* Delete suffix it it exists */ + if (*p == '.') { + *p = '\0'; break; + } + } +} + +#ifdef USE_DLN_A_OUT + +#ifndef LIBC_NAME +# define LIBC_NAME "libc.a" +#endif + +#ifndef DLN_DEFAULT_LIB_PATH +# define DLN_DEFAULT_LIB_PATH "/lib:/usr/lib:/usr/local/lib:." +#endif + +#include <errno.h> + +static int dln_errno; + +#define DLN_ENOEXEC ENOEXEC /* Exec format error */ +#define DLN_ECONFL 201 /* Symbol name conflict */ +#define DLN_ENOINIT 202 /* No inititalizer given */ +#define DLN_EUNDEF 203 /* Undefine symbol remains */ +#define DLN_ENOTLIB 204 /* Not a library file */ +#define DLN_EBADLIB 205 /* Malformed library file */ +#define DLN_EINIT 206 /* Not initialized */ + +static int dln_init_p = 0; + +#include "st.h" +#include <ar.h> +#include <a.out.h> +#ifndef N_COMM +# define N_COMM 0x12 +#endif +#ifndef N_MAGIC +# define N_MAGIC(x) (x).a_magic +#endif + +#define INVALID_OBJECT(h) (N_MAGIC(h) != OMAGIC) + +static st_table *sym_tbl; +static st_table *undef_tbl; + +static int load_lib(); + +static int +load_header(fd, hdrp, disp) + int fd; + struct exec *hdrp; + long disp; +{ + int size; + + lseek(fd, disp, 0); + size = read(fd, hdrp, sizeof(struct exec)); + if (size == -1) { + dln_errno = errno; + return -1; + } + if (size != sizeof(struct exec) || N_BADMAG(*hdrp)) { + dln_errno = DLN_ENOEXEC; + return -1; + } + return 0; +} + +#if defined(sequent) +#define RELOC_SYMBOL(r) ((r)->r_symbolnum) +#define RELOC_MEMORY_SUB_P(r) ((r)->r_bsr) +#define RELOC_PCREL_P(r) ((r)->r_pcrel || (r)->r_bsr) +#define RELOC_TARGET_SIZE(r) ((r)->r_length) +#endif + +/* Default macros */ +#ifndef RELOC_ADDRESS +#define RELOC_ADDRESS(r) ((r)->r_address) +#define RELOC_EXTERN_P(r) ((r)->r_extern) +#define RELOC_SYMBOL(r) ((r)->r_symbolnum) +#define RELOC_MEMORY_SUB_P(r) 0 +#define RELOC_PCREL_P(r) ((r)->r_pcrel) +#define RELOC_TARGET_SIZE(r) ((r)->r_length) +#endif + +#if defined(sun) && defined(sparc) +/* Sparc (Sun 4) macros */ +# undef relocation_info +# define relocation_info reloc_info_sparc +# define R_RIGHTSHIFT(r) (reloc_r_rightshift[(r)->r_type]) +# define R_BITSIZE(r) (reloc_r_bitsize[(r)->r_type]) +# define R_LENGTH(r) (reloc_r_length[(r)->r_type]) +static int reloc_r_rightshift[] = { + 0, 0, 0, 0, 0, 0, 2, 2, 10, 0, 0, 0, 0, 0, 0, +}; +static int reloc_r_bitsize[] = { + 8, 16, 32, 8, 16, 32, 30, 22, 22, 22, 13, 10, 32, 32, 16, +}; +static int reloc_r_length[] = { + 0, 1, 2, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +}; +# define R_PCREL(r) \ + ((r)->r_type >= RELOC_DISP8 && (r)->r_type <= RELOC_WDISP22) +# define R_SYMBOL(r) ((r)->r_index) +#endif + +#if defined(sequent) +#define R_SYMBOL(r) ((r)->r_symbolnum) +#define R_MEMORY_SUB(r) ((r)->r_bsr) +#define R_PCREL(r) ((r)->r_pcrel || (r)->r_bsr) +#define R_LENGTH(r) ((r)->r_length) +#endif + +#ifndef R_SYMBOL +# define R_SYMBOL(r) ((r)->r_symbolnum) +# define R_MEMORY_SUB(r) 0 +# define R_PCREL(r) ((r)->r_pcrel) +# define R_LENGTH(r) ((r)->r_length) +#endif + +static struct relocation_info * +load_reloc(fd, hdrp, disp) + int fd; + struct exec *hdrp; + long disp; +{ + struct relocation_info *reloc; + int size; + + lseek(fd, disp + N_TXTOFF(*hdrp) + hdrp->a_text + hdrp->a_data, 0); + size = hdrp->a_trsize + hdrp->a_drsize; + reloc = (struct relocation_info*)xmalloc(size); + if (reloc == NULL) { + dln_errno = errno; + return NULL; + } + + if (read(fd, reloc, size) != size) { + dln_errno = errno; + free(reloc); + return NULL; + } + + return reloc; +} + +static struct nlist * +load_sym(fd, hdrp, disp) + int fd; + struct exec *hdrp; + long disp; +{ + struct nlist * buffer; + struct nlist * sym; + struct nlist * end; + long displ; + int size; + + lseek(fd, N_SYMOFF(*hdrp) + hdrp->a_syms + disp, 0); + if (read(fd, &size, sizeof(int)) != sizeof(int)) { + goto err_noexec; + } + + buffer = (struct nlist*)xmalloc(hdrp->a_syms + size); + if (buffer == NULL) { + dln_errno = errno; + return NULL; + } + + lseek(fd, disp + N_SYMOFF(*hdrp), 0); + if (read(fd, buffer, hdrp->a_syms + size) != hdrp->a_syms + size) { + free(buffer); + goto err_noexec; + } + + sym = buffer; + end = sym + hdrp->a_syms / sizeof(struct nlist); + displ = (long)buffer + (long)(hdrp->a_syms); + + while (sym < end) { + sym->n_un.n_name = (char*)sym->n_un.n_strx + displ; + sym++; + } + return buffer; + + err_noexec: + dln_errno = DLN_ENOEXEC; + return NULL; +} + +static st_table * +sym_hash(hdrp, syms) + struct exec *hdrp; + struct nlist *syms; +{ + st_table *tbl; + struct nlist *sym = syms; + struct nlist *end = syms + (hdrp->a_syms / sizeof(struct nlist)); + + tbl = st_init_strtable(); + if (tbl == NULL) { + dln_errno = errno; + return NULL; + } + + while (sym < end) { + st_insert(tbl, sym->n_un.n_name, sym); + sym++; + } + return tbl; +} + +static int +dln_init(prog) + char *prog; +{ + char *file; + int fd; + struct exec hdr; + struct nlist *syms; + + if (dln_init_p == 1) return 0; + + file = dln_find_exe(prog, NULL); + if (file == NULL || (fd = open(file, O_RDONLY)) < 0) { + dln_errno = errno; + return -1; + } + + if (load_header(fd, &hdr, 0) == -1) return -1; + syms = load_sym(fd, &hdr, 0); + if (syms == NULL) { + close(fd); + return -1; + } + sym_tbl = sym_hash(&hdr, syms); + if (sym_tbl == NULL) { /* file may be start with #! */ + char c = '\0'; + char buf[MAXPATHLEN]; + char *p; + + free(syms); + lseek(fd, 0L, 0); + if (read(fd, &c, 1) == -1) { + dln_errno = errno; + return -1; + } + if (c != '#') goto err_noexec; + if (read(fd, &c, 1) == -1) { + dln_errno = errno; + return -1; + } + if (c != '!') goto err_noexec; + + p = buf; + /* skip forwading spaces */ + while (read(fd, &c, 1) == 1) { + if (c == '\n') goto err_noexec; + if (c != '\t' && c != ' ') { + *p++ = c; + break; + } + } + /* read in command name */ + while (read(fd, p, 1) == 1) { + if (*p == '\n' || *p == '\t' || *p == ' ') break; + p++; + } + *p = '\0'; + + return dln_init(buf); + } + dln_init_p = 1; + undef_tbl = st_init_strtable(); + close(fd); + return 0; + + err_noexec: + close(fd); + dln_errno = DLN_ENOEXEC; + return -1; +} + +static long +load_text_data(fd, hdrp, bss, disp) + int fd; + struct exec *hdrp; + int bss; + long disp; +{ + int size; + unsigned char* addr; + + lseek(fd, disp + N_TXTOFF(*hdrp), 0); + size = hdrp->a_text + hdrp->a_data; + + if (bss == -1) size += hdrp->a_bss; + else if (bss > 1) size += bss; + + addr = (unsigned char*)xmalloc(size); + if (addr == NULL) { + dln_errno = errno; + return 0; + } + + if (read(fd, addr, size) != size) { + dln_errno = errno; + free(addr); + return 0; + } + + if (bss == -1) { + memset(addr + hdrp->a_text + hdrp->a_data, 0, hdrp->a_bss); + } + else if (bss > 0) { + memset(addr + hdrp->a_text + hdrp->a_data, 0, bss); + } + + return (long)addr; +} + +static int +undef_print(key, value) + char *key, *value; +{ + fprintf(stderr, " %s\n", key); + return ST_CONTINUE; +} + +static void +dln_print_undef() +{ + fprintf(stderr, " Undefined symbols:\n"); + st_foreach(undef_tbl, undef_print, NULL); +} + +static void +dln_undefined() +{ + if (undef_tbl->num_entries > 0) { + fprintf(stderr, "dln: Calling undefined function\n"); + dln_print_undef(); + rb_exit(1); + } +} + +struct undef { + char *name; + struct relocation_info reloc; + long base; + char *addr; + union { + char c; + short s; + long l; + } u; +}; + +static st_table *reloc_tbl = NULL; +static void +link_undef(name, base, reloc) + char *name; + long base; + struct relocation_info *reloc; +{ + static int u_no = 0; + struct undef *obj; + char *addr = (char*)(reloc->r_address + base); + + obj = (struct undef*)xmalloc(sizeof(struct undef)); + obj->name = strdup(name); + obj->reloc = *reloc; + obj->base = base; + switch (R_LENGTH(reloc)) { + case 0: /* byte */ + obj->u.c = *addr; + break; + case 1: /* word */ + obj->u.s = *(short*)addr; + break; + case 2: /* long */ + obj->u.l = *(long*)addr; + break; + } + if (reloc_tbl == NULL) { + reloc_tbl = st_init_numtable(); + } + st_insert(reloc_tbl, u_no++, obj); +} + +struct reloc_arg { + char *name; + long value; +}; + +static int +reloc_undef(no, undef, arg) + int no; + struct undef *undef; + struct reloc_arg *arg; +{ + int datum; + char *address; +#if defined(sun) && defined(sparc) + unsigned int mask = 0; +#endif + + if (strcmp(arg->name, undef->name) != 0) return ST_CONTINUE; + address = (char*)(undef->base + undef->reloc.r_address); + datum = arg->value; + + if (R_PCREL(&(undef->reloc))) datum -= undef->base; +#if defined(sun) && defined(sparc) + datum += undef->reloc.r_addend; + datum >>= R_RIGHTSHIFT(&(undef->reloc)); + mask = (1 << R_BITSIZE(&(undef->reloc))) - 1; + mask |= mask -1; + datum &= mask; + switch (R_LENGTH(&(undef->reloc))) { + case 0: + *address = undef->u.c; + *address &= ~mask; + *address |= datum; + break; + case 1: + *(short *)address = undef->u.s; + *(short *)address &= ~mask; + *(short *)address |= datum; + break; + case 2: + *(long *)address = undef->u.l; + *(long *)address &= ~mask; + *(long *)address |= datum; + break; + } +#else + switch (R_LENGTH(&(undef->reloc))) { + case 0: /* byte */ + if (R_MEMORY_SUB(&(undef->reloc))) + *address = datum - *address; + else *address = undef->u.c + datum; + break; + case 1: /* word */ + if (R_MEMORY_SUB(&(undef->reloc))) + *(short*)address = datum - *(short*)address; + else *(short*)address = undef->u.s + datum; + break; + case 2: /* long */ + if (R_MEMORY_SUB(&(undef->reloc))) + *(long*)address = datum - *(long*)address; + else *(long*)address = undef->u.l + datum; + break; + } +#endif + free(undef->name); + free(undef); + return ST_DELETE; +} + +static void +unlink_undef(name, value) + char *name; + long value; +{ + struct reloc_arg arg; + + arg.name = name; + arg.value = value; + st_foreach(reloc_tbl, reloc_undef, &arg); +} + +#ifdef N_INDR +struct indr_data { + char *name0, *name1; +}; + +static int +reloc_repl(no, undef, data) + int no; + struct undef *undef; + struct indr_data *data; +{ + if (strcmp(data->name0, undef->name) == 0) { + free(undef->name); + undef->name = strdup(data->name1); + } + return ST_CONTINUE; +} +#endif + +static int +load_1(fd, disp, need_init) + int fd; + long disp; + char *need_init; +{ + static char *libc = LIBC_NAME; + struct exec hdr; + struct relocation_info *reloc = NULL; + long block = 0; + long new_common = 0; /* Length of new common */ + struct nlist *syms = NULL; + struct nlist *sym; + struct nlist *end; + int init_p = 0; + char buf[256]; + + if (load_header(fd, &hdr, disp) == -1) return -1; + if (INVALID_OBJECT(hdr)) { + dln_errno = DLN_ENOEXEC; + return -1; + } + reloc = load_reloc(fd, &hdr, disp); + if (reloc == NULL) return -1; + syms = load_sym(fd, &hdr, disp); + if (syms == NULL) return -1; + + sym = syms; + end = syms + (hdr.a_syms / sizeof(struct nlist)); + while (sym < end) { + struct nlist *old_sym; + int value = sym->n_value; + +#ifdef N_INDR + if (sym->n_type == (N_INDR | N_EXT)) { + 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)) { + unlink_undef(key, old_sym->n_value); + free(key); + } + } + else { + struct indr_data data; + + data.name0 = sym->n_un.n_name; + data.name1 = sym[1].n_un.n_name; + 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)) { + free(key); + } + } + sym += 2; + continue; + } +#endif + if (sym->n_type == (N_UNDF | N_EXT)) { + if (st_lookup(sym_tbl, sym->n_un.n_name, &old_sym) == 0) { + old_sym = NULL; + } + + if (value) { + if (old_sym) { + sym->n_type = N_EXT | N_COMM; + sym->n_value = old_sym->n_value; + } + else { + int rnd = + value >= sizeof(double) ? sizeof(double) - 1 + : value >= sizeof(long) ? sizeof(long) - 1 + : sizeof(short) - 1; + + sym->n_type = N_COMM; + new_common += rnd; + new_common &= ~(long)rnd; + sym->n_value = new_common; + new_common += value; + } + } + else { + if (old_sym) { + sym->n_type = N_EXT | N_COMM; + sym->n_value = old_sym->n_value; + } + else { + sym->n_value = (long)dln_undefined; + st_insert(undef_tbl, strdup(sym->n_un.n_name), NULL); + } + } + } + sym++; + } + + block = load_text_data(fd, &hdr, hdr.a_bss + new_common, disp); + if (block == 0) goto err_exit; + + sym = syms; + while (sym < end) { + struct nlist *new_sym; + char *key; + + switch (sym->n_type) { + case N_COMM: + sym->n_value += hdr.a_text + hdr.a_data; + case N_TEXT|N_EXT: + case N_DATA|N_EXT: + + sym->n_value += block; + + if (st_lookup(sym_tbl, sym->n_un.n_name, &new_sym) != 0 + && new_sym->n_value != (long)dln_undefined) { + dln_errno = DLN_ECONFL; + goto err_exit; + } + + key = sym->n_un.n_name; + if (st_delete(undef_tbl, &key, NULL) != 0) { + unlink_undef(key, sym->n_value); + free(key); + } + + new_sym = (struct nlist*)xmalloc(sizeof(struct nlist)); + *new_sym = *sym; + new_sym->n_un.n_name = strdup(sym->n_un.n_name); + st_insert(sym_tbl, new_sym->n_un.n_name, new_sym); + break; + + case N_TEXT: + case N_DATA: + sym->n_value += block; + break; + } + sym++; + } + + /* + * First comes the text-relocation + */ + { + struct relocation_info * rel = reloc; + struct relocation_info * rel_beg = reloc + + (hdr.a_trsize/sizeof(struct relocation_info)); + struct relocation_info * rel_end = reloc + + (hdr.a_trsize+hdr.a_drsize)/sizeof(struct relocation_info); + + while (rel < rel_end) { + char *address = (char*)(rel->r_address + block); + long datum = 0; +#if defined(sun) && defined(sparc) + unsigned int mask = 0; +#endif + + if(rel >= rel_beg) + address += hdr.a_text; + + if (rel->r_extern) { /* Look it up in symbol-table */ + sym = &(syms[R_SYMBOL(rel)]); + switch (sym->n_type) { + case N_EXT|N_UNDF: + link_undef(sym->n_un.n_name, block, rel); + case N_EXT|N_COMM: + case N_COMM: + datum = sym->n_value; + break; + default: + goto err_exit; + } + } /* end.. look it up */ + else { /* is static */ + switch (R_SYMBOL(rel)) { + case N_TEXT: + case N_DATA: + datum = block; + break; + case N_BSS: + datum = block + new_common; + break; + case N_ABS: + break; + } + } /* end .. is static */ + if (R_PCREL(rel)) datum -= block; + +#if defined(sun) && defined(sparc) + datum += rel->r_addend; + datum >>= R_RIGHTSHIFT(rel); + mask = (1 << R_BITSIZE(rel)) - 1; + mask |= mask -1; + datum &= mask; + + switch (R_LENGTH(rel)) { + case 0: + *address &= ~mask; + *address |= datum; + break; + case 1: + *(short *)address &= ~mask; + *(short *)address |= datum; + break; + case 2: + *(long *)address &= ~mask; + *(long *)address |= datum; + break; + } +#else + switch (R_LENGTH(rel)) { + case 0: /* byte */ + if (datum < -128 || datum > 127) goto err_exit; + *address += datum; + break; + case 1: /* word */ + *(short *)address += datum; + break; + case 2: /* long */ + *(long *)address += datum; + break; + } +#endif + rel++; + } + } + + if (need_init) { + int len; + char **libs_to_be_linked = 0; + + if (undef_tbl->num_entries > 0) { + if (load_lib(libc) == -1) goto err_exit; + } + + init_funcname(buf, need_init); + len = strlen(buf); + + for (sym = syms; sym<end; sym++) { + char *name = sym->n_un.n_name; + if (name[0] == '_' && sym->n_value >= block) { + if (strcmp(name+1, "libs_to_be_linked") == 0) { + libs_to_be_linked = (char**)sym->n_value; + } + else if (strcmp(name+1, buf) == 0) { + init_p = 1; + ((int (*)())sym->n_value)(); + } + } + } + if (libs_to_be_linked && undef_tbl->num_entries > 0) { + while (*libs_to_be_linked) { + load_lib(*libs_to_be_linked); + libs_to_be_linked++; + } + } + } + free(reloc); + free(syms); + if (need_init) { + if (init_p == 0) { + dln_errno = DLN_ENOINIT; + return -1; + } + if (undef_tbl->num_entries > 0) { + if (load_lib(libc) == -1) goto err_exit; + if (undef_tbl->num_entries > 0) { + dln_errno = DLN_EUNDEF; + return -1; + } + } + } + return 0; + + err_exit: + if (syms) free(syms); + if (reloc) free(reloc); + if (block) free((char*)block); + return -1; +} + +static int target_offset; +static int +search_undef(key, value, lib_tbl) + char *key; + int value; + st_table *lib_tbl; +{ + int offset; + + if (st_lookup(lib_tbl, key, &offset) == 0) return ST_CONTINUE; + target_offset = offset; + return ST_STOP; +} + +struct symdef { + int str_index; + int lib_offset; +}; + +char *dln_library_path = DLN_DEFAULT_LIB_PATH; + +static int +load_lib(lib) + char *lib; +{ + char *path, *file; + char armagic[SARMAG]; + int fd, size; + struct ar_hdr ahdr; + st_table *lib_tbl = NULL; + int *data, nsym; + struct symdef *base; + char *name_base; + + if (dln_init_p == 0) { + dln_errno = DLN_ENOINIT; + return -1; + } + + if (undef_tbl->num_entries == 0) return 0; + dln_errno = DLN_EBADLIB; + + if (lib[0] == '-' && lib[1] == 'l') { + char *p = alloca(strlen(lib) + 4); + sprintf(p, "lib%s.a", lib+2); + lib = p; + } + + /* library search path: */ + /* look for environment variable DLN_LIBRARY_PATH first. */ + /* then variable dln_library_path. */ + /* if path is still NULL, use "." for path. */ + path = getenv("DLN_LIBRARY_PATH"); + if (path == NULL) path = dln_library_path; + + file = dln_find_file(lib, path); + fd = open(file, O_RDONLY); + if (fd == -1) goto syserr; + size = read(fd, armagic, SARMAG); + if (size == -1) goto syserr; + + if (size != SARMAG) { + dln_errno = DLN_ENOTLIB; + goto badlib; + } + size = read(fd, &ahdr, sizeof(ahdr)); + if (size == -1) goto syserr; + if (size != sizeof(ahdr) || sscanf(ahdr.ar_size, "%d", &size) != 1) { + goto badlib; + } + + if (strncmp(ahdr.ar_name, "__.SYMDEF", 9) == 0) { + /* make hash table from __.SYMDEF */ + + lib_tbl = st_init_strtable(); + data = (int*)xmalloc(size); + if (data == NULL) goto syserr; + size = read(fd, data, size); + nsym = *data / sizeof(struct symdef); + base = (struct symdef*)(data + 1); + name_base = (char*)(base + nsym) + sizeof(int); + while (nsym > 0) { + char *name = name_base + base->str_index; + + st_insert(lib_tbl, name, base->lib_offset + sizeof(ahdr)); + nsym--; + base++; + } + for (;;) { + target_offset = -1; + st_foreach(undef_tbl, search_undef, lib_tbl); + if (target_offset == -1) break; + if (load_1(fd, target_offset, 0) == -1) { + st_free_table(lib_tbl); + free(data); + goto badlib; + } + if (undef_tbl->num_entries == 0) break; + } + free(data); + st_free_table(lib_tbl); + } + else { + /* linear library, need to scan (FUTURE) */ + + for (;;) { + int offset = SARMAG; + int found = 0; + struct exec hdr; + struct nlist *syms, *sym, *end; + + while (undef_tbl->num_entries > 0) { + found = 0; + lseek(fd, offset, 0); + size = read(fd, &ahdr, sizeof(ahdr)); + if (size == -1) goto syserr; + if (size == 0) break; + if (size != sizeof(ahdr) + || sscanf(ahdr.ar_size, "%d", &size) != 1) { + goto badlib; + } + offset += sizeof(ahdr); + if (load_header(fd, &hdr, offset) == -1) + goto badlib; + syms = load_sym(fd, &hdr, offset); + if (syms == NULL) goto badlib; + sym = syms; + end = syms + (hdr.a_syms / sizeof(struct nlist)); + while (sym < end) { + if (sym->n_type == N_EXT|N_TEXT + && st_lookup(undef_tbl, sym->n_un.n_name, NULL)) { + break; + } + sym++; + } + if (sym < end) { + found++; + free(syms); + if (load_1(fd, offset, 0) == -1) { + goto badlib; + } + } + offset += size; + if (offset & 1) offset++; + } + if (found) break; + } + } + close(fd); + return 0; + + syserr: + dln_errno = errno; + badlib: + if (fd >= 0) close(fd); + return -1; +} + +static int +load(file) + char *file; +{ + int fd; + int result; + + if (dln_init_p == 0) { + if (dln_init(dln_argv0) == -1) return -1; + } + result = strlen(file); + if (file[result-1] == 'a') { + return load_lib(file); + } + + fd = open(file, O_RDONLY); + if (fd == -1) { + dln_errno = errno; + return -1; + } + result = load_1(fd, 0, file); + close(fd); + + return result; +} + +void* +dln_sym(name) + char *name; +{ + struct nlist *sym; + + if (st_lookup(sym_tbl, name, &sym)) + return (void*)sym->n_value; + return NULL; +} + +#endif /* USE_DLN_A_OUT */ + +#ifdef USE_DLN_DLOPEN +# ifdef __NetBSD__ +# include <nlist.h> +# include <link.h> +# else +# include <dlfcn.h> +# endif +#endif + +#ifdef hpux +#include <errno.h> +#include "dl.h" +#endif + +#ifdef _AIX +#include <ctype.h> /* for isdigit() */ +#include <errno.h> /* for global errno */ +#include <string.h> /* for strerror() */ +#include <sys/ldr.h> +#endif + +#ifdef NeXT +/*#include <mach-o/rld.h>*/ +#endif + +static char * +dln_strerror() +{ +#ifdef USE_DLN_A_OUT + char *strerror(); + + switch (dln_errno) { + case DLN_ECONFL: + return "Symbol name conflict"; + case DLN_ENOINIT: + return "No inititalizer given"; + case DLN_EUNDEF: + return "Unresolved symbols"; + case DLN_ENOTLIB: + return "Not a library file"; + case DLN_EBADLIB: + return "Malformed library file"; + case DLN_EINIT: + return "Not initialized"; + default: + return strerror(dln_errno); + } +#endif + +#ifdef USE_DLN_DLOPEN + return (char*)dlerror(); +#endif +} + + +#ifdef _AIX +static void +aix_loaderror(char *pathname) +{ + char *message[8], errbuf[1024]; + int i,j; + + struct errtab { + int errno; + char *errstr; + } load_errtab[] = { + {L_ERROR_TOOMANY, "too many errors, rest skipped."}, + {L_ERROR_NOLIB, "can't load library:"}, + {L_ERROR_UNDEF, "can't find symbol in library:"}, + {L_ERROR_RLDBAD, + "RLD index out of range or bad relocation type:"}, + {L_ERROR_FORMAT, "not a valid, executable xcoff file:"}, + {L_ERROR_MEMBER, + "file not an archive or does not contain requested member:"}, + {L_ERROR_TYPE, "symbol table mismatch:"}, + {L_ERROR_ALIGN, "text allignment in file is wrong."}, + {L_ERROR_SYSTEM, "System error:"}, + {L_ERROR_ERRNO, NULL} + }; + +#define LOAD_ERRTAB_LEN (sizeof(load_errtab)/sizeof(load_errtab[0])) +#define ERRBUF_APPEND(s) strncat(errbuf, s, sizeof(errbuf)-strlen(errbuf)-1) + + sprintf(errbuf, "load failed - %.200s ", pathname); + + if (!loadquery(1, &message[0], sizeof(message))) + ERRBUF_APPEND(strerror(errno)); + for(i = 0; message[i] && *message[i]; i++) { + int nerr = atoi(message[i]); + for (j=0; j<LOAD_ERRTAB_LEN ; j++) { + if (nerr == load_errtab[i].errno && load_errtab[i].errstr) + ERRBUF_APPEND(load_errtab[i].errstr); + } + while (isdigit(*message[i])) message[i]++ ; + ERRBUF_APPEND(message[i]); + ERRBUF_APPEND("\n"); + } + errbuf[strlen(errbuf)-1] = '\0'; /* trim off last newline */ + LoadError(errbuf); + return; +} +#endif + +void +dln_load(file) + char *file; +{ +#ifdef USE_DLN_A_OUT + if (load(file) == -1) { + goto failed; + } + return; +#else + + char buf[MAXPATHLEN]; + /* Load the file as an object one */ + init_funcname(buf, file); + +#ifdef USE_DLN_DLOPEN +#define DLN_DEFINED + { + void *handle; + void (*init_fct)(); + int len = strlen(file); + +# ifndef RTLD_LAZY +# define RTLD_LAZY 1 +# endif +# ifndef RTLD_GLOBAL +# define RTLD_GLOBAL 0 +# endif + + /* Load file */ + if ((handle = dlopen(file, RTLD_LAZY|RTLD_GLOBAL)) == NULL) { + goto failed; + } + + if ((init_fct = (void(*)())dlsym(handle, buf)) == NULL) { + goto failed; + } + /* Call the init code */ + (*init_fct)(); + return; + } +#endif /* USE_DLN_DLOPEN */ + +#ifdef hpux +#define DLN_DEFINED + { + shl_t lib = NULL; + int flags; + void (*init_fct)(); + + flags = BIND_DEFERRED; + lib = shl_load(file, flags, 0); + if (lib == NULL) { + rb_sys_fail(file); + } + shl_findsym(&lib, buf, TYPE_PROCEDURE, (void*)&init_fct); + if (init_fct == NULL) { + shl_findsym(&lib, buf, TYPE_UNDEFINED, (void*)&init_fct); + if (init_fct == NULL) { + extern int errno; + errno = ENOSYM; + rb_sys_fail(file); + } + } + (*init_fct)(); + return; + } +#endif /* hpux */ + +#ifdef _AIX +#define DLN_DEFINED + { + void (*init_fct)(); + + init_fct = (void(*)())load(file, 1, 0); + if (init_fct == NULL) { + aix_loaderror(file); + } + (*init_fct)(); + return; + } +#endif /* _AIX */ + +#ifdef NeXT +#define DLN_DEFINED +/*---------------------------------------------------- + By SHIROYAMA Takayuki Psi@fortune.nest.or.jp + + Special Thanks... + Yu tomoak-i@is.aist-nara.ac.jp, + Mi hisho@tasihara.nest.or.jp, + and... Miss ARAI Akino(^^;) + ----------------------------------------------------*/ + { + unsigned long init_address; + char *object_files[2] = {NULL, NULL}; + + void (*init_fct)(); + + object_files[0] = file; + + /* Load object file, if return value ==0 , load failed*/ + if(rld_load(NULL, NULL, object_files, NULL) == 0) { + LoadError("Failed to load %.200s", file); + } + + /* lookup the initial function */ + if(rld_lookup(NULL, buf, &init_address) == 0) { + LoadError("Failed to lookup Init function %.200s",file); + } + + /* Cannot call *init_address directory, so copy this value to + funtion pointer */ + + init_fct = (void(*)())init_address; + (*init_fct)(); + return ; + } +#endif + +#ifndef DLN_DEFINED + rb_notimplement("dynamic link not supported"); +#endif + +#endif /* USE_DLN_A_OUT */ +#if !defined(_AIX) && !defined(NeXT) + failed: + LoadError("%s - %s", dln_strerror(), file); +#endif +} + +static char *dln_find_1(); + +char * +dln_find_exe(fname, path) + char *fname; + char *path; +{ +#if defined(__human68k__) + if (!path) + path = getenv("path"); + if (!path) + path = "/usr/local/bin;/usr/usb;/usr/bin;/bin;."; +#else + if (!path) path = getenv("PATH"); + if (!path) path = "/usr/local/bin:/usr/ucb:/usr/bin:/bin:."; +#endif + return dln_find_1(fname, path, 1); +} + +char * +dln_find_file(fname, path) + char *fname; + char *path; +{ + if (!path) path = "."; + return dln_find_1(fname, path, 0); +} + +static char fbuf[MAXPATHLEN]; + +static char * +dln_find_1(fname, path, exe_flag) + char *fname; + char *path; + int exe_flag; /* non 0 if looking for executable. */ +{ + register char *dp; + register char *ep; + register char *bp; + struct stat st; + + if (fname[0] == '/') return fname; + if (strncmp("./", fname, 2) == 0 || strncmp("../", fname, 3) == 0) + return fname; +#if defined(MSDOS) || defined(NT) || defined(__human68k__) + if (fname[0] == '\\') return fname; + if (fname[1] == ':') return fname; + if (strncmp(".\\", fname, 2) == 0 || strncmp("..\\", fname, 3) == 0) + return fname; +#endif + + for (dp = path;; dp = ++ep) { + register int l; + int i; + int fspace; + + /* extract a component */ +#if !defined(MSDOS) && !defined(NT) && !defined(__human68k__) + ep = strchr(dp, ':'); +#else + ep = strchr(dp, ';'); +#endif + if (ep == NULL) + ep = dp+strlen(dp); + + /* find the length of that component */ + l = ep - dp; + bp = fbuf; + fspace = sizeof fbuf - 2; + if (l > 0) { + /* + ** If the length of the component is zero length, + ** start from the current directory. If the + ** component begins with "~", start from the + ** user's $HOME environment variable. Otherwise + ** take the path literally. + */ + + if (*dp == '~' && (l == 1 || dp[1] == '/')) { + char *home; + + home = getenv("HOME"); + if (home != NULL) { + i = strlen(home); + if ((fspace -= i) < 0) + goto toolong; + memcpy(bp, home, i); + bp += i; + } + dp++; + l--; + } + if (l > 0) { + if ((fspace -= l) < 0) + goto toolong; + memcpy(bp, dp, l); + bp += l; + } + + /* add a "/" between directory and filename */ + if (ep[-1] != '/') + *bp++ = '/'; + } + + /* now append the file name */ + i = strlen(fname); + if ((fspace -= i) < 0) { + toolong: + fprintf(stderr, "openpath: pathname too long (ignored)\n"); + *bp = '\0'; + fprintf(stderr, "\tDirectory \"%s\"\n", fbuf); + fprintf(stderr, "\tFile \"%s\"\n", fname); + continue; + } + memcpy(bp, fname, i + 1); + + if (stat(fbuf, &st) == 0) { + if (exe_flag == 0) return fbuf; + /* looking for executable */ + if (eaccess(fbuf, X_OK) == 0) return fbuf; + } +#if defined(MSDOS) || defined(NT) || defined(__human68k__) + if (exe_flag) { + static const char *extension[] = { +#if defined(MSDOS) + ".com", ".exe", ".bat", +#if defined(DJGPP) + ".btm", ".sh", ".ksh", ".pl", ".sed", +#endif +#else + ".r", ".R", ".x", ".X", ".bat", ".BAT", +#endif + (char *) NULL + }; + int j; + + for (j = 0; extension[j]; j++) { + if (fspace < strlen(extension[j])) { + fprintf(stderr, "openpath: pathname too long (ignored)\n"); + fprintf(stderr, "\tDirectory \"%.*s\"\n", (int) (bp - fbuf), fbuf); + fprintf(stderr, "\tFile \"%s%s\"\n", fname, extension[j]); + continue; + } + strcpy(bp + i, extension[j]); + if (stat(fbuf, &st) == 0) + return fbuf; + } + } +#endif /* MSDOS or NT or __human68k__ */ + /* if not, and no other alternatives, life is bleak */ + if (*ep == '\0') { + return NULL; + } + + /* otherwise try the next component in the search path */ + } +} @@ -0,0 +1,22 @@ +/************************************************ + + dln.h - + + $Author$ + $Revision$ + $Date$ + created at: Wed Jan 19 16:53:09 JST 1994 + +************************************************/ +#ifndef DLN_H +#define DLN_H + +char *dln_find_exe(); +char *dln_find_file(); + +#ifdef USE_DLN_A_OUT +extern char *dln_argv0; +#endif + +void dln_load(); +#endif diff --git a/dmyext.c b/dmyext.c new file mode 100644 index 0000000000..4120d493c3 --- /dev/null +++ b/dmyext.c @@ -0,0 +1,4 @@ +void +Init_ext() +{ +} diff --git a/enum.c b/enum.c new file mode 100644 index 0000000000..5074170310 --- /dev/null +++ b/enum.c @@ -0,0 +1,369 @@ +/************************************************ + + enum.c - + + $Author$ + $Date$ + created at: Fri Oct 1 15:15:19 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" + +VALUE mEnumerable; +static ID id_each, id_eqq, id_cmp; + +void +rb_each(obj) + VALUE obj; +{ + rb_funcall(obj, id_each, 0, 0); +} + +static void +grep_i(i, arg) + VALUE i, *arg; +{ + if (RTEST(rb_funcall(arg[0], id_eqq, 1, i))) { + ary_push(arg[1], i); + } +} + +static void +grep_iter_i(i, pat) + VALUE i, pat; +{ + if (RTEST(rb_funcall(pat, id_eqq, 1, i))) { + rb_yield(i); + } +} + +static VALUE +enum_grep(obj, pat) + VALUE obj, pat; +{ + if (iterator_p()) { + rb_iterate(rb_each, obj, grep_iter_i, pat); + return obj; + } + else { + VALUE tmp, arg[2]; + + arg[0] = pat; arg[1] = tmp = ary_new(); + rb_iterate(rb_each, obj, grep_i, arg); + + return tmp; + } +} + +struct find_arg { + int found; + VALUE val; +}; + +static void +find_i(i, arg) + VALUE i; + struct find_arg *arg; +{ + if (RTEST(rb_yield(i))) { + arg->found = TRUE; + arg->val = i; + rb_break(); + } +} + +static VALUE +enum_find(argc, argv, obj) + int argc; + VALUE argv; + VALUE obj; +{ + struct find_arg arg; + VALUE if_none; + + rb_scan_args(argc, argv, "01", &if_none); + arg.found = FALSE; + rb_iterate(rb_each, obj, find_i, &arg); + if (arg.found) { + return arg.val; + } + if (!NIL_P(if_none)) { + rb_eval_cmd(if_none, Qnil); + } + return Qnil; +} + +static void +find_all_i(i, tmp) + VALUE i, tmp; +{ + if (RTEST(rb_yield(i))) { + ary_push(tmp, i); + } +} + +static VALUE +enum_find_all(obj) + VALUE obj; +{ + VALUE tmp; + + tmp = ary_new(); + rb_iterate(rb_each, obj, find_all_i, tmp); + + return tmp; +} + +static void +collect_i(i, tmp) + VALUE i, tmp; +{ + VALUE retval; + + retval = rb_yield(i); + if (RTEST(retval)) { + ary_push(tmp, retval); + } +} + +static VALUE +enum_collect(obj) + VALUE obj; +{ + VALUE tmp; + + tmp = ary_new(); + rb_iterate(rb_each, obj, collect_i, tmp); + + return tmp; +} + +static void +reverse_i(i, tmp) + VALUE i, tmp; +{ + ary_unshift(tmp, i); +} + +static VALUE +enum_reverse(obj) + VALUE obj; +{ + VALUE tmp; + + tmp = ary_new(); + rb_iterate(rb_each, obj, reverse_i, tmp); + + return tmp; +} + +static void +enum_all(i, ary) + VALUE i, ary; +{ + ary_push(ary, i); +} + +static VALUE +enum_to_a(obj) + VALUE obj; +{ + VALUE ary; + + ary = ary_new(); + rb_iterate(rb_each, obj, enum_all, ary); + + return ary; +} + +static VALUE +enum_sort(obj) + VALUE obj; +{ + return ary_sort(enum_to_a(obj)); +} + +static void +min_i(i, min) + VALUE i, *min; +{ + VALUE cmp; + + if (NIL_P(*min)) + *min = i; + else { + cmp = rb_funcall(i, id_cmp, 1, *min); + if (FIX2INT(cmp) < 0) + *min = i; + } +} + +static void +min_ii(i, min) + VALUE i, *min; +{ + VALUE cmp; + + if (NIL_P(*min)) + *min = i; + else { + cmp = rb_yield(assoc_new(i, *min)); + if (FIX2INT(cmp) < 0) + *min = i; + } +} + +static VALUE +enum_min(obj) + VALUE obj; +{ + VALUE min = Qnil; + + rb_iterate(rb_each, obj, iterator_p()?min_ii:min_i, &min); + return min; +} + +static void +max_i(i, max) + VALUE i, *max; +{ + VALUE cmp; + + if (NIL_P(*max)) + *max = i; + else { + cmp = rb_funcall(i, id_cmp, 1, *max); + if (FIX2INT(cmp) > 0) + *max = i; + } +} + +static void +max_ii(i, max) + VALUE i, *max; +{ + VALUE cmp; + + if (NIL_P(*max)) + *max = i; + else { + cmp = rb_yield(assoc_new(i, *max)); + if (FIX2INT(cmp) > 0) + *max = i; + } +} + +static VALUE +enum_max(obj) + VALUE obj; +{ + VALUE max = Qnil; + + rb_iterate(rb_each, obj, iterator_p()?max_ii:max_i, &max); + return max; +} + +struct i_v_pair { + int i; + VALUE v; + int found; +}; + +static void +index_i(item, iv) + VALUE item; + struct i_v_pair *iv; +{ + if (rb_equal(item, iv->v)) { + iv->found = 1; + rb_break(); + } + else { + iv->i++; + } +} + +static VALUE +enum_index(obj, val) + VALUE obj, val; +{ + struct i_v_pair iv; + + iv.i = 0; + iv.v = val; + iv.found = 0; + rb_iterate(rb_each, obj, index_i, &iv); + if (iv.found) return INT2FIX(iv.i); + return Qnil; /* not found */ +} + +static void +member_i(item, iv) + VALUE item; + struct i_v_pair *iv; +{ + if (rb_equal(item, iv->v)) { + iv->i = 1; + rb_break(); + } +} + +static VALUE +enum_member(obj, val) + VALUE obj, val; +{ + struct i_v_pair iv; + + iv.i = 0; + iv.v = val; + rb_iterate(rb_each, obj, member_i, &iv); + if (iv.i) return TRUE; + return FALSE; +} + +static void +length_i(i, length) + VALUE i; + int *length; +{ + (*length)++; +} + +VALUE +enum_length(obj) + VALUE obj; +{ + int length = 0; + + rb_iterate(rb_each, obj, length_i, &length); + return INT2FIX(length); +} + +void +Init_Enumerable() +{ + mEnumerable = rb_define_module("Enumerable"); + + rb_define_method(mEnumerable,"to_a", enum_to_a, 0); + + rb_define_method(mEnumerable,"sort", enum_sort, 0); + rb_define_method(mEnumerable,"grep", enum_grep, 1); + rb_define_method(mEnumerable,"find", enum_find, -1); + rb_define_method(mEnumerable,"find_all", enum_find_all, 0); + rb_define_method(mEnumerable,"collect", enum_collect, 0); + rb_define_method(mEnumerable,"reverse", enum_reverse, 0); + rb_define_method(mEnumerable,"min", enum_min, 0); + rb_define_method(mEnumerable,"max", enum_max, 0); + rb_define_method(mEnumerable,"index", enum_index, 1); + rb_define_method(mEnumerable,"member?", enum_member, 1); + rb_define_method(mEnumerable,"include?", enum_member, 1); + rb_define_method(mEnumerable,"length", enum_length, 0); + rb_define_method(mEnumerable,"size", enum_length, 0); + + id_eqq = rb_intern("==="); + id_each = rb_intern("each"); + id_cmp = rb_intern("<=>"); +} @@ -0,0 +1,49 @@ +/************************************************ + + env.h - + + $Author$ + $Revision$ + $Date$ + created at: Mon Jul 11 11:53:03 JST 1994 + +************************************************/ +#ifndef ENV_H +#define ENV_H + +extern struct FRAME { + int argc; + VALUE *argv; + ID last_func; + struct RClass *last_class; + VALUE cbase; + struct FRAME *prev; + char *file; + int line; + int iter; +} *the_frame; + +extern struct SCOPE { + struct RBasic super; + ID *local_tbl; + VALUE *local_vars; + int flag; +} *the_scope; + +#define SCOPE_ALLOCA 0 +#define SCOPE_MALLOC 1 +#define SCOPE_NOSTACK 2 + +extern int rb_in_eval; + +extern struct RClass *the_class; + +struct RVarmap { + struct RBasic super; + ID id; + VALUE val; + struct RVarmap *next; +}; +extern struct RVarmap *the_dyna_vars; + +#endif /* ENV_H */ diff --git a/error.c b/error.c new file mode 100644 index 0000000000..39e63ddfd5 --- /dev/null +++ b/error.c @@ -0,0 +1,832 @@ +/************************************************ + + error.c - + + $Author$ + $Date$ + created at: Mon Aug 9 16:11:34 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include "env.h" +#include <stdio.h> +#include <varargs.h> + +extern char *sourcefile; +extern int sourceline; + +int nerrs; + +static void +err_sprintf(buf, fmt, args) + char *buf, *fmt; + va_list args; +{ + if (!sourcefile) { + vsprintf(buf, fmt, args); + } + else { + sprintf(buf, "%s:%d: ", sourcefile, sourceline); + vsprintf((char*)buf+strlen(buf), fmt, args); + } +} + +static void +err_append(s) + char *s; +{ + extern VALUE errinfo; + + if (rb_in_eval) { + if (NIL_P(errinfo)) { + errinfo = str_new2(s); + } + else { + str_cat(errinfo, "\n", 1); + str_cat(errinfo, s, strlen(s)); + } + } + else { + fputs(s, stderr); + fputs("\n", stderr); + fflush(stderr); + } +} + +static void +err_print(fmt, args) + char *fmt; + va_list args; +{ + char buf[BUFSIZ]; + + err_sprintf(buf, fmt, args); + err_append(buf); +} + +void +Error(fmt, va_alist) + char *fmt; + va_dcl +{ + va_list args; + + va_start(args); + err_print(fmt, args); + va_end(args); + nerrs++; +} + +void +Error_Append(fmt, va_alist) + char *fmt; + va_dcl +{ + va_list args; + char buf[BUFSIZ]; + + va_start(args); + vsprintf(buf, fmt, args); + va_end(args); + err_append(buf); +} + +void +Warning(fmt, va_alist) + char *fmt; + va_dcl +{ + char buf[BUFSIZ]; + va_list args; + + if (!verbose) return; + + sprintf(buf, "warning: %s", fmt); + + va_start(args); + err_print(buf, args); + va_end(args); +} + +void +Bug(fmt, va_alist) + char *fmt; + va_dcl +{ + char buf[BUFSIZ]; + va_list args; + + sprintf(buf, "[BUG] %s", fmt); + rb_in_eval = 0; + + va_start(args); + err_print(buf, args); + va_end(args); + abort(); +} + +static struct types { + int type; + char *name; +} builtin_types[] = { + T_NIL, "nil", + T_OBJECT, "Object", + T_CLASS, "Class", + T_ICLASS, "iClass", /* internal use: mixed-in module holder */ + T_MODULE, "Module", + T_FLOAT, "Float", + T_STRING, "String", + T_REGEXP, "Regexp", + T_ARRAY, "Array", + T_FIXNUM, "Fixnum", + T_HASH, "Hash", + T_STRUCT, "Struct", + T_BIGNUM, "Bignum", + T_FILE, "File", + T_TRUE, "TRUE", + T_FALSE, "FALSE", + T_DATA, "Data", /* internal use: wrapped C pointers */ + T_MATCH, "Match", /* data of $~ */ + T_VARMAP, "Varmap", /* internal use: dynamic variables */ + T_SCOPE, "Scope", /* internal use: variable scope */ + T_NODE, "Node", /* internal use: syntax tree node */ + -1, 0, +}; + +extern void TypeError(); + +void +rb_check_type(x, t) + VALUE x; + int t; +{ + struct types *type = builtin_types; + + if (TYPE(x)!=(t)) { + while (type->type >= 0) { + if (type->type == t) { + TypeError("wrong argument type %s (expected %s)", + rb_class2name(CLASS_OF(x)), type->name); + } + type++; + } + Bug("unknown type 0x%x", t); + } +} + +/* exception classes */ +#include "errno.h" + +extern VALUE cString; +VALUE eGlobalExit, eException; +VALUE eSystemExit, eInterrupt, eFatal; +VALUE eRuntimeError; +VALUE eSyntaxError; +VALUE eTypeError; +VALUE eArgError; +VALUE eNameError; +VALUE eIndexError; +VALUE eNotImpError; +VALUE eLoadError; +VALUE eSecurityError; + +VALUE eSystemCallError; +VALUE mErrno; + +VALUE +exc_new(etype, ptr, len) + VALUE etype; + char *ptr; + UINT len; +{ + NEWOBJ(exc, struct RString); + OBJSETUP(exc, etype, T_STRING); + + exc->len = len; + exc->orig = 0; + exc->ptr = ALLOC_N(char,len+1); + if (ptr) { + memcpy(exc->ptr, ptr, len); + } + exc->ptr[len] = '\0'; + return (VALUE)exc; +} + +VALUE +exc_new2(etype, s) + VALUE etype; + char *s; +{ + return exc_new(etype, s, strlen(s)); +} + +VALUE +exc_new3(etype, str) + VALUE etype; + struct RString *str; +{ + Check_Type(str, T_STRING); + return exc_new(etype, str->ptr, str->len); +} + +static VALUE +exc_s_new(argc, argv, etype) + int argc; + VALUE *argv; + VALUE etype; +{ + VALUE arg; + + if (rb_scan_args(argc, argv, "01", &arg) == 0) { + return exc_new(etype, 0, 0); + } + Check_Type(arg, T_STRING); + return exc_new3(etype, arg); +} + +static VALUE +exc_inspect(exc) + VALUE exc; +{ + struct RString *classpath = RSTRING(rb_class_path(CLASS_OF(exc))); + VALUE str = str_new(classpath->ptr, classpath->len); + + str_cat(str, ":", 1); + if (RSTRING(exc)->len == 0) { + str_cat(str, "\"\"", 2); + } + str_cat(str, RSTRING(exc)->ptr, RSTRING(exc)->len); + return str; +} + +static VALUE +exception(argc, argv) + int argc; + VALUE *argv; +{ + void ArgError(); + VALUE v = Qnil; + int i; + ID id; + + if (argc == 0) { + ArgError("wrong # of arguments"); + } + for (i=0; i<argc; i++) { /* argument check */ + id = rb_to_id(argv[i]); + if (!rb_id2name(id)) { + ArgError("argument needs to be symbol or string"); + } + if (!rb_is_const_id(id)) { + ArgError("identifier %s needs to be constant", rb_id2name(id)); + } + } + for (i=0; i<argc; i++) { + v = rb_define_class(rb_id2name(rb_to_id(argv[i])), eException); + } + return v; +} + +static VALUE *syserr_list; + +#ifndef NT +extern int sys_nerr; +#endif + +static void +set_syserr(i, name) + int i; + char *name; +{ + if (i <= sys_nerr) { + syserr_list[i] = rb_define_class_under(mErrno, name, eSystemCallError); + rb_global_variable(&syserr_list[i]); + } +} + +static void init_syserr(); + +void +Init_Exception() +{ + eGlobalExit = rb_define_class("GlobalExit", cString); + rb_define_singleton_method(eGlobalExit, "new", exc_s_new, -1); + rb_define_method(eGlobalExit, "inspect", exc_inspect, 0); + + eSystemExit = rb_define_class("SystemExit", eGlobalExit); + eFatal = rb_define_class("fatal", eGlobalExit); + eInterrupt = rb_define_class("Interrupt", eGlobalExit); + + eException = rb_define_class("Exception", eGlobalExit); + eSyntaxError = rb_define_class("SyntaxError", eException); + eTypeError = rb_define_class("TypeError", eException); + eArgError = rb_define_class("ArgumentError", eException); + eNameError = rb_define_class("NameError", eException); + eIndexError = rb_define_class("IndexError", eException); + eNotImpError = rb_define_class("NotImplementError", eException); + eLoadError = rb_define_class("LoadError", eException); + + eRuntimeError = rb_define_class("RuntimeError", eException); + eSecurityError = rb_define_class("SecurityError", eException); + + init_syserr(); + + rb_define_global_function("Exception", exception, -1); +} + +#define RAISE_ERROR(class) {\ + va_list args;\ + char buf[BUFSIZ];\ +\ + va_start(args);\ + vsprintf(buf, fmt, args);\ + va_end(args);\ +\ + rb_raise(exc_new2(class, buf));\ +} + +void +Raise(exc, fmt, va_alist) + VALUE exc; + char *fmt; + va_dcl +{ + RAISE_ERROR(exc); +} + +void +TypeError(fmt, va_alist) + char *fmt; + va_dcl +{ + RAISE_ERROR(eTypeError); +} + +void +ArgError(fmt, va_alist) + char *fmt; + va_dcl +{ + RAISE_ERROR(eArgError); +} + +void +NameError(fmt, va_alist) + char *fmt; + va_dcl +{ + RAISE_ERROR(eNameError); +} + +void +IndexError(fmt, va_alist) + char *fmt; + va_dcl +{ + RAISE_ERROR(eIndexError); +} + +void +Fail(fmt, va_alist) + char *fmt; + va_dcl +{ + RAISE_ERROR(eRuntimeError); +} + +void +rb_notimplement() +{ + Raise(eNotImpError, + "The %s() function is unimplemented on this machine", + rb_id2name(the_frame->last_func)); +} + +void +LoadError(fmt, va_alist) + char *fmt; + va_dcl +{ + RAISE_ERROR(eLoadError); +} + +void +Fatal(fmt, va_alist) + char *fmt; + va_dcl +{ + va_list args; + char buf[BUFSIZ]; + + va_start(args); + vsprintf(buf, fmt, args); + va_end(args); + + rb_in_eval = 0; + rb_fatal(exc_new2(eFatal, buf)); +} + +void +rb_sys_fail(mesg) + char *mesg; +{ +#ifndef NT + char *strerror(); +#endif + char buf[BUFSIZ]; + extern int errno; + int n = errno; + + if (RTEST(mesg)) + sprintf(buf, "%s - %s", strerror(errno), mesg); + else + sprintf(buf, "%s", strerror(errno)); + + errno = 0; + if (n > sys_nerr || !syserr_list[n]) { + char name[6]; + + sprintf(name, "E%03d", n); + set_syserr(n, name); + } + rb_raise(exc_new2(syserr_list[n], buf)); +} + +static void +init_syserr() +{ + eSystemCallError = rb_define_class("SystemCallError", eException); + mErrno = rb_define_module("Errno"); + syserr_list = ALLOC_N(VALUE, sys_nerr+1); + MEMZERO(syserr_list, VALUE, sys_nerr+1); + +#ifdef EPERM + set_syserr(EPERM, "EPERM"); +#endif +#ifdef ENOENT + set_syserr(ENOENT, "ENOENT"); +#endif +#ifdef ESRCH + set_syserr(ESRCH, "ESRCH"); +#endif +#ifdef EINTR + set_syserr(EINTR, "EINTR"); +#endif +#ifdef EIO + set_syserr(EIO, "EIO"); +#endif +#ifdef ENXIO + set_syserr(ENXIO, "ENXIO"); +#endif +#ifdef E2BIG + set_syserr(E2BIG, "E2BIG"); +#endif +#ifdef ENOEXEC + set_syserr(ENOEXEC, "ENOEXEC"); +#endif +#ifdef EBADF + set_syserr(EBADF, "EBADF"); +#endif +#ifdef ECHILD + set_syserr(ECHILD, "ECHILD"); +#endif +#ifdef EAGAIN + set_syserr(EAGAIN, "EAGAIN"); +#endif +#ifdef ENOMEM + set_syserr(ENOMEM, "ENOMEM"); +#endif +#ifdef EACCES + set_syserr(EACCES, "EACCES"); +#endif +#ifdef EFAULT + set_syserr(EFAULT, "EFAULT"); +#endif +#ifdef ENOTBLK + set_syserr(ENOTBLK, "ENOTBLK"); +#endif +#ifdef EBUSY + set_syserr(EBUSY, "EBUSY"); +#endif +#ifdef EEXIST + set_syserr(EEXIST, "EEXIST"); +#endif +#ifdef EXDEV + set_syserr(EXDEV, "EXDEV"); +#endif +#ifdef ENODEV + set_syserr(ENODEV, "ENODEV"); +#endif +#ifdef ENOTDIR + set_syserr(ENOTDIR, "ENOTDIR"); +#endif +#ifdef EISDIR + set_syserr(EISDIR, "EISDIR"); +#endif +#ifdef EINVAL + set_syserr(EINVAL, "EINVAL"); +#endif +#ifdef ENFILE + set_syserr(ENFILE, "ENFILE"); +#endif +#ifdef EMFILE + set_syserr(EMFILE, "EMFILE"); +#endif +#ifdef ENOTTY + set_syserr(ENOTTY, "ENOTTY"); +#endif +#ifdef ETXTBSY + set_syserr(ETXTBSY, "ETXTBSY"); +#endif +#ifdef EFBIG + set_syserr(EFBIG, "EFBIG"); +#endif +#ifdef ENOSPC + set_syserr(ENOSPC, "ENOSPC"); +#endif +#ifdef ESPIPE + set_syserr(ESPIPE, "ESPIPE"); +#endif +#ifdef EROFS + set_syserr(EROFS, "EROFS"); +#endif +#ifdef EMLINK + set_syserr(EMLINK, "EMLINK"); +#endif +#ifdef EPIPE + set_syserr(EPIPE, "EPIPE"); +#endif +#ifdef EDOM + set_syserr(EDOM, "EDOM"); +#endif +#ifdef ERANGE + set_syserr(ERANGE, "ERANGE"); +#endif +#ifdef EDEADLK + set_syserr(EDEADLK, "EDEADLK"); +#endif +#ifdef ENAMETOOLONG + set_syserr(ENAMETOOLONG, "ENAMETOOLONG"); +#endif +#ifdef ENOLCK + set_syserr(ENOLCK, "ENOLCK"); +#endif +#ifdef ENOSYS + set_syserr(ENOSYS, "ENOSYS"); +#endif +#ifdef ENOTEMPTY + set_syserr(ENOTEMPTY, "ENOTEMPTY"); +#endif +#ifdef ELOOP + set_syserr(ELOOP, "ELOOP"); +#endif +#ifdef EWOULDBLOCK + set_syserr(EWOULDBLOCK, "EWOULDBLOCK"); +#endif +#ifdef ENOMSG + set_syserr(ENOMSG, "ENOMSG"); +#endif +#ifdef EIDRM + set_syserr(EIDRM, "EIDRM"); +#endif +#ifdef ECHRNG + set_syserr(ECHRNG, "ECHRNG"); +#endif +#ifdef EL2NSYNC + set_syserr(EL2NSYNC, "EL2NSYNC"); +#endif +#ifdef EL3HLT + set_syserr(EL3HLT, "EL3HLT"); +#endif +#ifdef EL3RST + set_syserr(EL3RST, "EL3RST"); +#endif +#ifdef ELNRNG + set_syserr(ELNRNG, "ELNRNG"); +#endif +#ifdef EUNATCH + set_syserr(EUNATCH, "EUNATCH"); +#endif +#ifdef ENOCSI + set_syserr(ENOCSI, "ENOCSI"); +#endif +#ifdef EL2HLT + set_syserr(EL2HLT, "EL2HLT"); +#endif +#ifdef EBADE + set_syserr(EBADE, "EBADE"); +#endif +#ifdef EBADR + set_syserr(EBADR, "EBADR"); +#endif +#ifdef EXFULL + set_syserr(EXFULL, "EXFULL"); +#endif +#ifdef ENOANO + set_syserr(ENOANO, "ENOANO"); +#endif +#ifdef EBADRQC + set_syserr(EBADRQC, "EBADRQC"); +#endif +#ifdef EBADSLT + set_syserr(EBADSLT, "EBADSLT"); +#endif +#ifdef EDEADLOCK + set_syserr(EDEADLOCK, "EDEADLOCK"); +#endif +#ifdef EBFONT + set_syserr(EBFONT, "EBFONT"); +#endif +#ifdef ENOSTR + set_syserr(ENOSTR, "ENOSTR"); +#endif +#ifdef ENODATA + set_syserr(ENODATA, "ENODATA"); +#endif +#ifdef ETIME + set_syserr(ETIME, "ETIME"); +#endif +#ifdef ENOSR + set_syserr(ENOSR, "ENOSR"); +#endif +#ifdef ENONET + set_syserr(ENONET, "ENONET"); +#endif +#ifdef ENOPKG + set_syserr(ENOPKG, "ENOPKG"); +#endif +#ifdef EREMOTE + set_syserr(EREMOTE, "EREMOTE"); +#endif +#ifdef ENOLINK + set_syserr(ENOLINK, "ENOLINK"); +#endif +#ifdef EADV + set_syserr(EADV, "EADV"); +#endif +#ifdef ESRMNT + set_syserr(ESRMNT, "ESRMNT"); +#endif +#ifdef ECOMM + set_syserr(ECOMM, "ECOMM"); +#endif +#ifdef EPROTO + set_syserr(EPROTO, "EPROTO"); +#endif +#ifdef EMULTIHOP + set_syserr(EMULTIHOP, "EMULTIHOP"); +#endif +#ifdef EDOTDOT + set_syserr(EDOTDOT, "EDOTDOT"); +#endif +#ifdef EBADMSG + set_syserr(EBADMSG, "EBADMSG"); +#endif +#ifdef EOVERFLOW + set_syserr(EOVERFLOW, "EOVERFLOW"); +#endif +#ifdef ENOTUNIQ + set_syserr(ENOTUNIQ, "ENOTUNIQ"); +#endif +#ifdef EBADFD + set_syserr(EBADFD, "EBADFD"); +#endif +#ifdef EREMCHG + set_syserr(EREMCHG, "EREMCHG"); +#endif +#ifdef ELIBACC + set_syserr(ELIBACC, "ELIBACC"); +#endif +#ifdef ELIBBAD + set_syserr(ELIBBAD, "ELIBBAD"); +#endif +#ifdef ELIBSCN + set_syserr(ELIBSCN, "ELIBSCN"); +#endif +#ifdef ELIBMAX + set_syserr(ELIBMAX, "ELIBMAX"); +#endif +#ifdef ELIBEXEC + set_syserr(ELIBEXEC, "ELIBEXEC"); +#endif +#ifdef EILSEQ + set_syserr(EILSEQ, "EILSEQ"); +#endif +#ifdef ERESTART + set_syserr(ERESTART, "ERESTART"); +#endif +#ifdef ESTRPIPE + set_syserr(ESTRPIPE, "ESTRPIPE"); +#endif +#ifdef EUSERS + set_syserr(EUSERS, "EUSERS"); +#endif +#ifdef ENOTSOCK + set_syserr(ENOTSOCK, "ENOTSOCK"); +#endif +#ifdef EDESTADDRREQ + set_syserr(EDESTADDRREQ, "EDESTADDRREQ"); +#endif +#ifdef EMSGSIZE + set_syserr(EMSGSIZE, "EMSGSIZE"); +#endif +#ifdef EPROTOTYPE + set_syserr(EPROTOTYPE, "EPROTOTYPE"); +#endif +#ifdef ENOPROTOOPT + set_syserr(ENOPROTOOPT, "ENOPROTOOPT"); +#endif +#ifdef EPROTONOSUPPORT + set_syserr(EPROTONOSUPPORT, "EPROTONOSUPPORT"); +#endif +#ifdef ESOCKTNOSUPPORT + set_syserr(ESOCKTNOSUPPORT, "ESOCKTNOSUPPORT"); +#endif +#ifdef EOPNOTSUPP + set_syserr(EOPNOTSUPP, "EOPNOTSUPP"); +#endif +#ifdef EPFNOSUPPORT + set_syserr(EPFNOSUPPORT, "EPFNOSUPPORT"); +#endif +#ifdef EAFNOSUPPORT + set_syserr(EAFNOSUPPORT, "EAFNOSUPPORT"); +#endif +#ifdef EADDRINUSE + set_syserr(EADDRINUSE, "EADDRINUSE"); +#endif +#ifdef EADDRNOTAVAIL + set_syserr(EADDRNOTAVAIL, "EADDRNOTAVAIL"); +#endif +#ifdef ENETDOWN + set_syserr(ENETDOWN, "ENETDOWN"); +#endif +#ifdef ENETUNREACH + set_syserr(ENETUNREACH, "ENETUNREACH"); +#endif +#ifdef ENETRESET + set_syserr(ENETRESET, "ENETRESET"); +#endif +#ifdef ECONNABORTED + set_syserr(ECONNABORTED, "ECONNABORTED"); +#endif +#ifdef ECONNRESET + set_syserr(ECONNRESET, "ECONNRESET"); +#endif +#ifdef ENOBUFS + set_syserr(ENOBUFS, "ENOBUFS"); +#endif +#ifdef EISCONN + set_syserr(EISCONN, "EISCONN"); +#endif +#ifdef ENOTCONN + set_syserr(ENOTCONN, "ENOTCONN"); +#endif +#ifdef ESHUTDOWN + set_syserr(ESHUTDOWN, "ESHUTDOWN"); +#endif +#ifdef ETOOMANYREFS + set_syserr(ETOOMANYREFS, "ETOOMANYREFS"); +#endif +#ifdef ETIMEDOUT + set_syserr(ETIMEDOUT, "ETIMEDOUT"); +#endif +#ifdef ECONNREFUSED + set_syserr(ECONNREFUSED, "ECONNREFUSED"); +#endif +#ifdef EHOSTDOWN + set_syserr(EHOSTDOWN, "EHOSTDOWN"); +#endif +#ifdef EHOSTUNREACH + set_syserr(EHOSTUNREACH, "EHOSTUNREACH"); +#endif +#ifdef EALREADY + set_syserr(EALREADY, "EALREADY"); +#endif +#ifdef EINPROGRESS + set_syserr(EINPROGRESS, "EINPROGRESS"); +#endif +#ifdef ESTALE + set_syserr(ESTALE, "ESTALE"); +#endif +#ifdef EUCLEAN + set_syserr(EUCLEAN, "EUCLEAN"); +#endif +#ifdef ENOTNAM + set_syserr(ENOTNAM, "ENOTNAM"); +#endif +#ifdef ENAVAIL + set_syserr(ENAVAIL, "ENAVAIL"); +#endif +#ifdef EISNAM + set_syserr(EISNAM, "EISNAM"); +#endif +#ifdef EREMOTEIO + set_syserr(EREMOTEIO, "EREMOTEIO"); +#endif +#ifdef EDQUOT + set_syserr(EDQUOT, "EDQUOT"); +#endif +} diff --git a/eval.c b/eval.c new file mode 100644 index 0000000000..92113a7ce5 --- /dev/null +++ b/eval.c @@ -0,0 +1,5402 @@ +/************************************************ + + eval.c - + + $Author$ + $Date$ + created at: Thu Jun 10 14:22:17 JST 1993 + + Copyright (C) 1993-1997 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include "node.h" +#include "env.h" +#include "sig.h" + +#include <stdio.h> +#include <setjmp.h> +#include "st.h" +#include "dln.h" + +#ifdef HAVE_STRING_H +# include <string.h> +#else +char *strrchr(); +#endif + +#ifndef setjmp +#ifdef HAVE__SETJMP +#define setjmp(env) _setjmp(env) +#define longjmp(env,val) _longjmp(env,val) +#endif +#endif + +extern VALUE cData; +VALUE cProc; +static VALUE proc_call(); +static VALUE f_binding(); + +#define CACHE_SIZE 0x200 +#define CACHE_MASK 0x1ff +#define EXPR1(c,m) ((((int)(c)>>3)^(m))&CACHE_MASK) + +struct cache_entry { /* method hash table. */ + ID mid; /* method's id */ + ID mid0; /* method's original id */ + struct RClass *class; /* receiver's class */ + struct RClass *origin; /* where method defined */ + NODE *method; + int noex; +}; + +static struct cache_entry cache[CACHE_SIZE]; + +void +rb_clear_cache() +{ + struct cache_entry *ent, *end; + + ent = cache; end = ent + CACHE_SIZE; + while (ent < end) { + ent->mid = 0; + ent++; + } +} + +void +rb_add_method(class, mid, node, noex) + struct RClass *class; + ID mid; + NODE *node; + int noex; +{ + NODE *body; + + if (NIL_P(class)) class = (struct RClass*)cObject; + body = NEW_METHOD(node, noex); + st_insert(class->m_tbl, mid, body); +} + +static NODE* +search_method(class, id, origin) + struct RClass *class, **origin; + ID id; +{ + NODE *body; + + while (!st_lookup(class->m_tbl, id, &body)) { + class = class->super; + if (!class) return 0; + } + + if (origin) *origin = class; + return body; +} + +static NODE* +rb_get_method_body(classp, idp, noexp) + struct RClass **classp; + ID *idp; + int *noexp; +{ + ID id = *idp; + struct RClass *class = *classp; + NODE *body; + struct RClass *origin; + struct cache_entry *ent; + + if ((body = search_method(class, id, &origin)) == 0) { + return 0; + } + if (!body->nd_body) return 0; + + /* store in cache */ + ent = cache + EXPR1(class, id); + ent->class = class; + ent->noex = body->nd_noex; + body = body->nd_body; + if (nd_type(body) == NODE_FBODY) { + ent->mid = id; + *classp = ent->origin = (struct RClass*)body->nd_orig; + *idp = ent->mid0 = body->nd_mid; + body = ent->method = body->nd_head; + } + else { + *classp = ent->origin = origin; + ent->mid = ent->mid0 = id; + ent->method = body; + } + + if (noexp) *noexp = ent->noex; + return body; +} + +void +rb_alias(class, name, def) + struct RClass *class; + ID name, def; +{ + struct RClass *origin; + NODE *orig, *body; + + if (name == def) return; + orig = search_method(class, def, &origin); + if (!orig || !orig->nd_body) { + if (TYPE(class) == T_MODULE) { + orig = search_method(cObject, def, &origin); + } + } + if (!orig || !orig->nd_body) { + NameError("undefined method `%s' for `%s'", + rb_id2name(def), rb_class2name((VALUE)class)); + } + body = orig->nd_body; + if (nd_type(body) == NODE_FBODY) { /* was alias */ + body = body->nd_head; + def = body->nd_mid; + origin = (struct RClass*)body->nd_orig; + } + + st_insert(class->m_tbl, name, + NEW_METHOD(NEW_FBODY(body, def, origin), orig->nd_noex)); +} + +static void +rb_export_method(class, name, noex) + struct RClass *class; + ID name; + int noex; +{ + NODE *body; + struct RClass *origin; + + body = search_method(class, name, &origin); + if (!body && TYPE(class) == T_MODULE) { + body = search_method(cObject, name, &origin); + } + if (!body) { + NameError("undefined method `%s' for `%s'", + rb_id2name(name), rb_class2name((VALUE)class)); + } + if (body->nd_noex != noex) { + if (class == origin) { + body->nd_noex = noex; + } + else { + rb_clear_cache(); + rb_add_method(class, name, NEW_ZSUPER(), noex); + } + } +} + +static VALUE +method_boundp(class, id, ex) + struct RClass *class; + ID id; + int ex; +{ + int noex; + + if (rb_get_method_body(&class, &id, &noex)) { + if (ex && noex == NOEX_PRIVATE) + return FALSE; + return TRUE; + } + return FALSE; +} + +int +rb_method_boundp(class, id, priv) + VALUE class; + ID id; + int priv; +{ + if (method_boundp(class, id, priv?NOEX_PRIVATE:NOEX_PUBLIC)) + return TRUE; + return FALSE; +} + +static ID init, eqq, each, aref, aset; +VALUE errinfo = Qnil, errat = Qnil; +extern NODE *eval_tree; +extern int nerrs; + +extern VALUE mKernel; +extern VALUE cModule; +extern VALUE cClass; +extern VALUE eFatal; +extern VALUE eGlobalExit; +extern VALUE eInterrupt; +extern VALUE eSystemExit; +extern VALUE eException; +extern VALUE eRuntimeError; +extern VALUE eSyntaxError; +static VALUE eLocalJumpError; +extern VALUE eSecurityError; + +extern VALUE TopSelf; + +struct FRAME *the_frame; +struct SCOPE *the_scope; +static struct FRAME *top_frame; +static struct SCOPE *top_scope; + +#define PUSH_FRAME() { \ + struct FRAME _frame; \ + _frame.prev = the_frame; \ + _frame.file = sourcefile; \ + _frame.line = sourceline; \ + _frame.iter = the_iter->iter; \ + _frame.cbase = the_frame->cbase; \ + the_frame = &_frame; \ + +#define POP_FRAME() the_frame = _frame.prev; } + +struct BLOCK { + NODE *var; + NODE *body; + VALUE self; + struct FRAME frame; + struct SCOPE *scope; + struct RClass *class; + int level; + int iter; + struct RVarmap *d_vars; +#ifdef THREAD + VALUE orig_thread; +#endif + struct BLOCK *prev; +} *the_block; + +#define PUSH_BLOCK(v,b) { \ + struct BLOCK _block; \ + _block.level = (int)prot_tag; \ + _block.var = v; \ + _block.body = b; \ + _block.self = self; \ + _block.frame = *the_frame; \ + _block.class = the_class; \ + _block.frame.file = sourcefile; \ + _block.frame.line = sourceline; \ + _block.scope = the_scope; \ + _block.d_vars = the_dyna_vars; \ + _block.prev = the_block; \ + _block.iter = the_iter->iter; \ + the_block = &_block; \ + +#define PUSH_BLOCK2(b) { \ + struct BLOCK _block; \ + _block = *b; \ + _block.prev = the_block; \ + the_block = &_block; + +#define POP_BLOCK() \ + the_block = the_block->prev; \ +} + +struct RVarmap *the_dyna_vars; +#define PUSH_VARS() { \ + struct RVarmap *_old; \ + _old = the_dyna_vars; \ + the_dyna_vars = 0; + +#define POP_VARS() \ + the_dyna_vars = _old; \ +} + +VALUE +dyna_var_defined(id) + ID id; +{ + struct RVarmap *vars = the_dyna_vars; + + while (vars) { + if (vars->id == id) return TRUE; + vars = vars->next; + } + return FALSE; +} + +VALUE +dyna_var_ref(id) + ID id; +{ + struct RVarmap *vars = the_dyna_vars; + + while (vars) { + if (vars->id == id) { + return vars->val; + } + vars = vars->next; + } + return Qnil; +} + +VALUE +dyna_var_asgn(id, value) + ID id; + VALUE value; +{ + struct RVarmap *vars = the_dyna_vars; + + while (vars) { + if (vars->id == id) { + vars->val = value; + return value; + } + vars = vars->next; + } + { + NEWOBJ(_vars, struct RVarmap); + OBJSETUP(_vars, 0, T_VARMAP); + _vars->id = id; + _vars->val = value; + _vars->next = the_dyna_vars; + the_dyna_vars = _vars; + } + return value; +} + +static struct iter { + int iter; + struct iter *prev; +} *the_iter; + +#define ITER_NOT 0 +#define ITER_PRE 1 +#define ITER_CUR 2 + +#define PUSH_ITER(i) { \ + struct iter _iter; \ + _iter.prev = the_iter; \ + _iter.iter = (i); \ + the_iter = &_iter; \ + +#define POP_ITER() \ + the_iter = _iter.prev; \ +} + +static struct tag { + jmp_buf buf; + struct FRAME *frame; + struct iter *iter; + struct tag *prev; +} *prot_tag; + +#define PUSH_TAG() { \ + struct tag _tag; \ + _tag.frame = the_frame; \ + _tag.iter = the_iter; \ + _tag.prev = prot_tag; \ + prot_tag = &_tag; + +#define EXEC_TAG() ((NODE*)setjmp(prot_tag->buf)) + +#define JUMP_TAG(st) { \ + the_frame = prot_tag->frame; \ + the_iter = prot_tag->iter; \ + longjmp(prot_tag->buf,(int)(st)); \ +} + +#define JUMP_TAG3(val,data1,data2) \ + JUMP_TAG(node_newnode(NODE_TAG,(val),(data1),(data2))) + +#define JUMP_TAG2(val,data) JUMP_TAG3((val),(data),0) + +#define POP_TAG() \ + prot_tag = _tag.prev; \ +} + +#define TAG_RETURN 0x1 +#define TAG_BREAK 0x2 +#define TAG_NEXT 0x3 +#define TAG_RETRY 0x4 +#define TAG_REDO 0x5 +#define TAG_RAISE 0x6 +#define TAG_THROW 0x7 +#define TAG_FATAL 0x8 + +#define IN_BLOCK 0x10 + +struct RClass *the_class; + +#define PUSH_CLASS() { \ + struct RClass *_class = the_class; \ + +#define POP_CLASS() the_class = _class; } + +#define PUSH_SCOPE() { \ + struct SCOPE *_old; \ + NEWOBJ(_scope, struct SCOPE); \ + OBJSETUP(_scope, 0, T_SCOPE); \ + _scope->local_tbl = 0; \ + _scope->local_vars = 0; \ + _scope->flag = 0; \ + _old = the_scope; \ + the_scope = _scope; \ + +#define POP_SCOPE() \ + if (the_scope->flag == SCOPE_ALLOCA) {\ + the_scope->local_vars = 0;\ + the_scope->local_tbl = 0;\ + if (the_scope != top_scope)\ + gc_force_recycle(the_scope);\ + }\ + else {\ + the_scope->flag |= SCOPE_NOSTACK;\ + }\ + the_scope = _old;\ +} + +static VALUE rb_eval(); +static VALUE eval(); +static NODE *compile(); + +static VALUE rb_call(); +VALUE rb_apply(); +VALUE rb_funcall2(); + +static VALUE module_setup(); + +static VALUE massign(); +static void assign(); + +static int safe_level = 0; +/* safe-level: + 0 - strings from streams/environment/ARGV are tainted (default) + 1 - no dangerous operation by tainted string + 2 - some process operations prohibited + 3 - all genetated strings are tainted + 4 - no global variable value modification/no direct output + 5 - no instance variable value modification +*/ + +int +rb_safe_level() +{ + return safe_level; +} + +void +rb_set_safe_level(level) + int level; +{ + if (level > safe_level) { + safe_level = level; + } +} + +static VALUE +safe_getter() +{ + return INT2FIX(safe_level); +} + +static void +safe_setter(val) + VALUE val; +{ + int level = NUM2INT(val); + + if (level < safe_level) { + Raise(eSecurityError, "tried to downgrade safe level from %d to %d", + safe_level, level); + } + safe_level = level; +} + +void +rb_check_safe_str(x) + VALUE x; +{ + if (TYPE(x)!= T_STRING) { + TypeError("wrong argument type %s (expected String)", + rb_class2name(CLASS_OF(x))); + } + if (rb_safe_level() > 0 && str_tainted(x)) { + Raise(eSecurityError, "Insecure operation - %s", + rb_id2name(the_frame->last_func)); + } +} + +void +rb_secure(level) + int level; +{ + if (level <= safe_level) { + Raise(eSecurityError, "Insecure operation `%s' for level %d", + rb_id2name(the_frame->last_func), level); + } +} + +extern int sourceline; +extern char *sourcefile; + +static VALUE trace_func = 0; +static void call_trace_func(); + +static void +error_pos() +{ + if (sourcefile) { + if (the_frame->last_func) { + fprintf(stderr, "%s:%d:in `%s'", sourcefile, sourceline, + rb_id2name(the_frame->last_func)); + } + else { + fprintf(stderr, "%s:%d", sourcefile, sourceline); + } + } +} + +static void +error_print() +{ + VALUE eclass; + + if (NIL_P(errinfo)) return; + + if (!NIL_P(errat)) { + VALUE mesg = Qnil; + + switch (TYPE(errat)) { + case T_STRING: + mesg = errat; + errat = Qnil; + break; + case T_ARRAY: + mesg = RARRAY(errat)->ptr[0]; + break; + } + if (NIL_P(mesg)) error_pos(); + else { + fwrite(RSTRING(mesg)->ptr, 1, RSTRING(mesg)->len, stderr); + } + } + + eclass = CLASS_OF(errinfo); + if (eclass == eRuntimeError && RSTRING(errinfo)->len == 0) { + fprintf(stderr, ": unhandled exception\n"); + } + else { + PUSH_TAG(); + if (EXEC_TAG() == 0) { + VALUE epath = rb_class_path(eclass); + if (RSTRING(epath)->ptr[0] != '#') { + fprintf(stderr, ": "); + fwrite(RSTRING(epath)->ptr, 1, RSTRING(epath)->len, stderr); + } + } + POP_TAG(); + + if (RSTRING(errinfo)->len > 0) { + fprintf(stderr, ": "); + fwrite(RSTRING(errinfo)->ptr, 1, RSTRING(errinfo)->len, stderr); + } + if (RSTRING(errinfo)->ptr[RSTRING(errinfo)->len - 1] != '\n') { + putc('\n', stderr); + } + } + + if (!NIL_P(errat)) { + int i; + struct RArray *ep = RARRAY(errat); + +#define TRACE_MAX (TRACE_HEAD+TRACE_TAIL+5) +#define TRACE_HEAD 8 +#define TRACE_TAIL 5 + + for (i=1; i<ep->len; i++) { + fprintf(stderr, "\tfrom %s\n", RSTRING(ep->ptr[i])->ptr); + if (i == TRACE_HEAD && ep->len > TRACE_MAX) { + fprintf(stderr, "\t ... %d levels...\n", + ep->len - TRACE_HEAD - TRACE_TAIL); + i = ep->len - TRACE_TAIL; + } + } + } +} + +#ifndef NT +extern char **environ; +#endif +char **origenviron; + +void +ruby_init() +{ + static struct FRAME frame; + static struct iter iter; + NODE *state; + + the_frame = top_frame = &frame; + the_iter = &iter; + + origenviron = environ; + + init_heap(); + PUSH_SCOPE(); + the_scope->local_vars = 0; + the_scope->local_tbl = 0; + top_scope = the_scope; + + PUSH_TAG() + if ((state = EXEC_TAG()) == 0) { + rb_call_inits(); + the_class = (struct RClass*)cObject; + the_frame->cbase = (VALUE)node_newnode(NODE_CREF,cObject,0,0); + rb_define_global_const("TOPLEVEL_BINDING", f_binding(TopSelf)); + ruby_prog_init(); + } + POP_TAG(); + if (state) error_print(); + POP_SCOPE(); + the_scope = top_scope; +} + +static int ext_init = 0; + +void +ruby_options(argc, argv) + int argc; + char **argv; +{ + NODE *state; + + PUSH_TAG() + if ((state = EXEC_TAG()) == 0) { + NODE *save; + + Init_ext(); + ext_init = 1; + ruby_process_options(argc, argv); + save = eval_tree; + rb_require_modules(); + eval_tree = save; + } + POP_TAG(); + if (state) { + error_print(); + exit(1); + } +} + +static VALUE +eval_node(self) + VALUE self; +{ + VALUE result = Qnil; + NODE *tree; + + if (!eval_tree) return Qnil; + + tree = eval_tree; + eval_tree = 0; + + result = rb_eval(self, tree); + return result; +} + +int rb_in_eval; + +#ifdef THREAD +static void thread_cleanup(); +static void thread_wait_other_threads(); +static VALUE thread_current(); +#endif + +static int exit_status; + +void +ruby_run() +{ + NODE *state; + static NODE *ex; + + if (nerrs > 0) exit(nerrs); + + init_stack(); + errat = Qnil; /* clear for execution */ + + PUSH_TAG(); + PUSH_ITER(ITER_NOT); + if ((state = EXEC_TAG()) == 0) { + if (!ext_init) Init_ext(); + eval_node(TopSelf); + } + POP_ITER(); + POP_TAG(); + + if (state && !ex) ex = state; + PUSH_TAG(); + PUSH_ITER(ITER_NOT); + if ((state = EXEC_TAG()) == 0) { + rb_trap_exit(); +#ifdef THREAD + thread_cleanup(); + thread_wait_other_threads(); +#endif + } + else { + ex = state; + } + POP_ITER(); + POP_TAG(); + + if (!ex) { + exit(0); + } + + switch (ex->nd_tag) { + case IN_BLOCK|TAG_RETURN: + case TAG_RETURN: + error_pos(); + fprintf(stderr, "unexpected return\n"); + exit(1); + break; + case TAG_NEXT: + error_pos(); + fprintf(stderr, "unexpected next\n"); + exit(1); + break; + case IN_BLOCK|TAG_BREAK: + case TAG_BREAK: + error_pos(); + fprintf(stderr, "unexpected break\n"); + exit(1); + break; + case TAG_REDO: + error_pos(); + fprintf(stderr, "unexpected redo\n"); + exit(1); + break; + case TAG_RETRY: + error_pos(); + fprintf(stderr, "retry outside of rescue clause\n"); + exit(1); + break; + case TAG_RAISE: + case TAG_FATAL: + if (obj_is_kind_of(errinfo, eSystemExit)) { + exit(exit_status); + } + error_print(); + exit(1); + break; + case TAG_THROW: + error_pos(); + fprintf(stderr, "uncaught throw `%s'\n", rb_id2name(ex->nd_tlev)); + exit(1); + break; + default: + Bug("Unknown longjmp status %d", ex->nd_tag); + break; + } +} + +static void +compile_error(at) + char *at; +{ + VALUE mesg; + + mesg = errinfo; + nerrs = 0; + errinfo = exc_new2(eSyntaxError, "compile error in "); + str_cat(errinfo, at, strlen(at)); + str_cat(errinfo, ":\n", 2); + str_cat(errinfo, RSTRING(mesg)->ptr, RSTRING(mesg)->len); + rb_raise(errinfo); +} + +VALUE +rb_eval_string(str) + char *str; +{ + VALUE v; + char *oldsrc = sourcefile; + + sourcefile = "(eval)"; + v = eval(TopSelf, str_new2(str), Qnil); + sourcefile = oldsrc; + + return v; +} + +void +rb_eval_cmd(cmd, arg) + VALUE cmd, arg; +{ + NODE *state; + struct SCOPE *saved_scope; + volatile int safe = rb_safe_level(); + + if (TYPE(cmd) != T_STRING) { + if (obj_is_kind_of(cmd, cProc)) { + proc_call(cmd, arg); + return; + } + } + + PUSH_CLASS(); + PUSH_TAG(); + saved_scope = the_scope; + the_scope = top_scope; + + the_class = (struct RClass*)cObject; + if (str_tainted(cmd)) { + safe_level = 5; + } + + if ((state = EXEC_TAG()) == 0) { + eval(TopSelf, cmd, Qnil); + } + + the_scope = saved_scope; + safe_level = safe; + POP_TAG(); + POP_CLASS(); + + if (state == 0) return; + switch (state->nd_tag) { + case TAG_RETURN: + Raise(eLocalJumpError, "unexpected return"); + break; + case TAG_NEXT: + Raise(eLocalJumpError, "unexpected next"); + break; + case TAG_BREAK: + Raise(eLocalJumpError, "unexpected break"); + break; + case TAG_REDO: + Raise(eLocalJumpError, "unexpected redo"); + break; + case TAG_RETRY: + Raise(eLocalJumpError, "retry outside of rescue clause"); + break; + default: + JUMP_TAG(state); + break; + } +} + +void +rb_trap_eval(cmd, sig) + VALUE cmd; + int sig; +{ + NODE *state; + + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + rb_eval_cmd(cmd, ary_new3(1, INT2FIX(sig))); + } + POP_TAG(); + if (state) { + trap_immediate = 0; + JUMP_TAG(state); + } +} + +static VALUE +superclass(self, node) + VALUE self; + NODE *node; +{ + VALUE val = 0; + NODE *state; + + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + val = rb_eval(self, node); + } + POP_TAG(); + if (state) { + if (state->nd_tag == TAG_RAISE) { + superclass_error: + switch (nd_type(node)) { + case NODE_COLON2: + TypeError("undefined superclass `%s'", rb_id2name(node->nd_mid)); + case NODE_CVAR: + TypeError("undefined superclass `%s'", rb_id2name(node->nd_vid)); + default: + TypeError("superclass undefined"); + } + } + JUMP_TAG(state); + } + if (TYPE(val) != T_CLASS) goto superclass_error; + if (FL_TEST(val, FL_SINGLETON)) { + TypeError("can't make subclass of virtual class"); + } + + return val; +} + +static VALUE +ev_const_defined(cref, id) + NODE *cref; + ID id; +{ + NODE *cbase = cref; + + while (cbase && cbase->nd_clss != cObject) { + struct RClass *class = RCLASS(cbase->nd_clss); + + if (class->iv_tbl && + st_lookup(class->iv_tbl, id, 0)) { + return TRUE; + } + cbase = cbase->nd_next; + } + return rb_const_defined(cref->nd_clss, id); +} + +static VALUE +ev_const_get(cref, id) + NODE *cref; + ID id; +{ + NODE *cbase = cref; + VALUE result; + + while (cbase && cbase->nd_clss != cObject) { + struct RClass *class = RCLASS(cbase->nd_clss); + + if (class->iv_tbl && + st_lookup(class->iv_tbl, id, &result)) { + return result; + } + cbase = cbase->nd_next; + } + return rb_const_get(cref->nd_clss, id); +} + +#define SETUP_ARGS(anode) {\ + NODE *n = anode;\ + if (!n) {\ + argc = 0;\ + argv = 0;\ + }\ + else if (nd_type(n) == NODE_ARRAY) {\ + argc=n->nd_alen;\ + if (argc > 0) {\ + int i;\ + int line = sourceline;\ + n = anode;\ + argv = ALLOCA_N(VALUE,argc);\ + for (i=0;i<argc;i++) {\ + argv[i] = rb_eval(self,n->nd_head);\ + n=n->nd_next;\ + }\ + sourcefile = anode->file;\ + sourceline = line;\ + }\ + else {\ + argc = 0;\ + argv = 0;\ + }\ + }\ + else {\ + VALUE args = rb_eval(self,n);\ + int line = sourceline;\ + if (TYPE(args) != T_ARRAY)\ + args = rb_to_a(args);\ + argc = RARRAY(args)->len;\ + argv = ALLOCA_N(VALUE, argc);\ + MEMCPY(argv, RARRAY(args)->ptr, VALUE, argc);\ + sourcefile = anode->file;\ + sourceline = line;\ + }\ +} + +int +rb_test_false_or_nil(v) + VALUE v; +{ + return (v != Qnil) && (v != FALSE); +} + +#define MATCH_DATA the_scope->local_vars[node->nd_cnt] + +static char* +is_defined(self, node, buf) + VALUE self; + NODE *node; /* OK */ + char *buf; +{ + VALUE val; /* OK */ + NODE *state; + + node = node->nd_head; + + switch (nd_type(node)) { + case NODE_SUPER: + case NODE_ZSUPER: + if (the_frame->last_func == 0) return 0; + else if (method_boundp(the_frame->last_class->super, + the_frame->last_func, 1)) { + return "super"; + } + break; + + case NODE_FCALL: + case NODE_VCALL: + val = CLASS_OF(self); + goto check_bound; + + case NODE_CALL: + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + val = rb_eval(self, node->nd_recv); + val = CLASS_OF(val); + } + POP_TAG(); + if (state) { + return 0; + } + check_bound: + if (method_boundp(val, node->nd_mid, + nd_type(node)== NODE_CALL)) { + return "method"; + } + break; + + case NODE_YIELD: + if (iterator_p()) { + return "yield"; + } + break; + + case NODE_SELF: + return "self"; + + case NODE_NIL: + return "nil"; + + case NODE_ATTRSET: + case NODE_OP_ASGN1: + case NODE_OP_ASGN2: + case NODE_MASGN: + case NODE_LASGN: + case NODE_DASGN: + case NODE_GASGN: + case NODE_IASGN: + case NODE_CASGN: + return "assignment"; + + case NODE_LVAR: + case NODE_DVAR: + return "local-variable"; + + case NODE_GVAR: + if (rb_gvar_defined(node->nd_entry)) { + return "global-variable"; + } + break; + + case NODE_IVAR: + if (rb_ivar_defined(self, node->nd_vid)) { + return "instance-variable"; + } + break; + + case NODE_CVAR: + if (ev_const_defined(the_frame->cbase, node->nd_vid)) { + return "constant"; + } + break; + + case NODE_COLON2: + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + val = rb_eval(self, node->nd_head); + } + POP_TAG(); + if (state) { + return 0; + } + else { + switch (TYPE(val)) { + case T_CLASS: + case T_MODULE: + if (rb_const_defined_at(val, node->nd_mid)) + return "constant"; + } + } + break; + + case NODE_NTH_REF: + if (reg_nth_defined(node->nd_nth, MATCH_DATA)) { + sprintf(buf, "$%d", node->nd_nth); + return buf; + } + break; + + case NODE_BACK_REF: + if (reg_nth_defined(0, MATCH_DATA)) { + sprintf(buf, "$%c", node->nd_nth); + return buf; + } + break; + + default: + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + rb_eval(self, node); + } + POP_TAG(); + if (!state) { + return "expression"; + } + break; + } + return 0; +} + +static int handle_rescue(); +VALUE rb_yield_0(); + +static void blk_free(); + +static VALUE +set_trace_func(obj, trace) + VALUE obj; + struct RData *trace; +{ + if (NIL_P(trace)) { + trace_func = 0; + return Qnil; + } + if (TYPE(trace) != T_DATA || trace->dfree != blk_free) { + TypeError("trace_func needs to be Proc"); + } + return trace_func = (VALUE)trace; +} + +static void +call_trace_func(event, file, line, self, id) + char *event; + char *file; + int line; + VALUE self; + ID id; +{ + NODE *state; + volatile VALUE trace; + struct FRAME *prev; + + if (!trace_func) return; + + trace = trace_func; + trace_func = 0; +#ifdef THREAD + thread_critical++; +#endif + + prev = the_frame; + PUSH_FRAME(); + *the_frame = *_frame.prev; + the_frame->prev = prev; + + the_frame->line = sourceline = line; + the_frame->file = sourcefile = file; + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + proc_call(trace, ary_new3(5, str_new2(event), + str_new2(sourcefile), + INT2FIX(sourceline), + INT2FIX(id), + self?f_binding(self):Qnil)); + } + POP_TAG(); + POP_FRAME(); + +#ifdef THREAD + thread_critical--; +#endif + if (!trace_func) trace_func = trace; + if (state) JUMP_TAG(state); +} + +static VALUE +rb_eval(self, node) + VALUE self; + NODE * volatile node; +{ + NODE *state; + volatile VALUE result = Qnil; + +#define RETURN(v) { result = (v); goto finish; } + + again: + if (!node) RETURN(Qnil); + +#if 0 + sourceline = nd_line(node); + sourcefile = node->file; +#endif + switch (nd_type(node)) { + case NODE_BLOCK: + while (node) { + result = rb_eval(self, node->nd_head); + node = node->nd_next; + } + break; + + /* begin .. end without clauses */ + case NODE_BEGIN: + node = node->nd_body; + goto again; + + /* nodes for speed-up(default match) */ + case NODE_MATCH: + result = reg_match2(node->nd_head->nd_lit); + break; + + /* nodes for speed-up(top-level loop for -n/-p) */ + case NODE_OPT_N: + while (!NIL_P(f_gets())) { + rb_eval(self, node->nd_body); + } + RETURN(Qnil); + + case NODE_SELF: + RETURN(self); + + case NODE_NIL: + RETURN(Qnil); + + case NODE_IF: + if (RTEST(rb_eval(self, node->nd_cond))) { + node = node->nd_body; + } + else { + node = node->nd_else; + } + goto again; + + case NODE_CASE: + { + VALUE val; + + val = rb_eval(self, node->nd_head); + node = node->nd_body; + while (node) { + NODE *tag; + + if (nd_type(node) != NODE_WHEN) { + goto again; + } + tag = node->nd_head; + while (tag) { + if (trace_func) { + call_trace_func("line", tag->file, nd_line(tag), + self, the_frame->last_func); + } + if (RTEST(rb_funcall2(rb_eval(self, tag->nd_head),eqq,1,&val))){ + node = node->nd_body; + goto again; + } + tag = tag->nd_next; + } + node = node->nd_next; + } + } + RETURN(Qnil); + + case NODE_WHILE: + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + if (node->nd_state && !RTEST(rb_eval(self, node->nd_cond))) + goto while_out; + do { + while_redo: + rb_eval(self, node->nd_body); + while_next: + ; + } while (RTEST(rb_eval(self, node->nd_cond))); + } + else { + switch (state->nd_tag) { + case TAG_REDO: + state = 0; + goto while_redo; + case TAG_NEXT: + state = 0; + goto while_next; + case TAG_BREAK: + state = 0; + default: + break; + } + } + while_out: + POP_TAG(); + if (state) { + JUMP_TAG(state); + } + RETURN(Qnil); + + case NODE_UNTIL: + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + if (node->nd_state && RTEST(rb_eval(self, node->nd_cond))) + goto until_out; + do { + until_redo: + rb_eval(self, node->nd_body); + until_next: + ; + } while (!RTEST(rb_eval(self, node->nd_cond))); + } + else { + switch (state->nd_tag) { + case TAG_REDO: + state = 0; + goto until_redo; + case TAG_NEXT: + state = 0; + goto until_next; + case TAG_BREAK: + state = 0; + default: + break; + } + } + until_out: + POP_TAG(); + if (state) { + JUMP_TAG(state); + } + RETURN(Qnil); + + case NODE_ITER: + case NODE_FOR: + { + int tag_level; + + iter_retry: + PUSH_BLOCK(node->nd_var, node->nd_body); + PUSH_TAG(); + + state = EXEC_TAG(); + if (state == 0) { + if (nd_type(node) == NODE_ITER) { + PUSH_ITER(ITER_PRE); + result = rb_eval(self, node->nd_iter); + POP_ITER(); + } + else { + VALUE recv; + int line = sourceline; + + recv = rb_eval(self, node->nd_iter); + PUSH_ITER(ITER_PRE); + sourcefile = node->file; + sourceline = line; + result = rb_call(CLASS_OF(recv),recv,each,0,0,0); + POP_ITER(); + } + } + POP_TAG(); + tag_level = the_block->level; + POP_BLOCK(); + if (state == 0) break; + switch (state->nd_tag) { + case TAG_RETRY: + goto iter_retry; + + case IN_BLOCK|TAG_BREAK: + if (state->nd_tlev != tag_level) { + JUMP_TAG(state); + } + result = Qnil; + break; + case IN_BLOCK|TAG_RETURN: + if (state->nd_tlev == tag_level) { + state->nd_tag &= ~IN_BLOCK; + } + /* fall through */ + default: + JUMP_TAG(state); + } + } + break; + + case NODE_YIELD: + result = rb_yield_0(rb_eval(self, node->nd_stts), 0); + break; + + case NODE_RESCUE: + retry_entry: + { + volatile VALUE e_info = errinfo, e_at = errat; + + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + result = rb_eval(self, node->nd_head); + } + POP_TAG(); + if (state) { + if (state->nd_tag == TAG_RAISE) { + NODE * volatile resq = node->nd_resq; + while (resq) { + if (handle_rescue(self, resq)) { + state = 0; + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + result = rb_eval(self, resq->nd_body); + } + POP_TAG(); + if (state == 0) { + errinfo = e_info; + errat = e_at; + } + else if (state->nd_tag == TAG_RETRY) { + state = 0; + goto retry_entry; + } + break; + } + resq = resq->nd_head; /* next rescue */ + } + } + if (state) { + JUMP_TAG(state); + } + } + } + break; + + case NODE_ENSURE: + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + result = rb_eval(self, node->nd_head); + } + POP_TAG(); + rb_eval(self, node->nd_ensr); + if (state) { + JUMP_TAG(state); + } + break; + + case NODE_AND: + result = rb_eval(self, node->nd_1st); + if (!RTEST(result)) break; + node = node->nd_2nd; + goto again; + + case NODE_OR: + result = rb_eval(self, node->nd_1st); + if (RTEST(result)) break; + node = node->nd_2nd; + goto again; + + case NODE_NOT: + if (RTEST(rb_eval(self, node->nd_body))) result = FALSE; + else result = TRUE; + break; + + case NODE_DOT2: + case NODE_DOT3: + RETURN(range_new(rb_eval(self, node->nd_beg), rb_eval(self, node->nd_end))); + + case NODE_FLIP2: /* like AWK */ + if (node->nd_state == 0) { + if (RTEST(rb_eval(self, node->nd_beg))) { + node->nd_state = rb_eval(self, node->nd_end)?0:1; + result = TRUE; + } + else { + result = FALSE; + } + } + else { + if (RTEST(rb_eval(self, node->nd_end))) { + node->nd_state = 0; + } + result = TRUE; + } + break; + + case NODE_FLIP3: /* like SED */ + if (node->nd_state == 0) { + if (RTEST(rb_eval(self, node->nd_beg))) { + node->nd_state = 1; + result = TRUE; + } + result = FALSE; + } + else { + if (RTEST(rb_eval(self, node->nd_end))) { + node->nd_state = 0; + } + result = TRUE; + } + break; + + case NODE_RETURN: + JUMP_TAG2(TAG_RETURN,(node->nd_stts)?rb_eval(self, node->nd_stts):Qnil); + break; + + case NODE_CALL: + { + VALUE recv; + int argc; VALUE *argv; /* used in SETUP_ARGS */ + + PUSH_ITER(ITER_NOT); + recv = rb_eval(self, node->nd_recv); + SETUP_ARGS(node->nd_args); + POP_ITER(); + result = rb_call(CLASS_OF(recv),recv,node->nd_mid,argc,argv,0); + } + break; + + case NODE_FCALL: + { + int argc; VALUE *argv; /* used in SETUP_ARGS */ + + PUSH_ITER(ITER_NOT); + SETUP_ARGS(node->nd_args); + POP_ITER(); + result = rb_call(CLASS_OF(self),self,node->nd_mid,argc,argv,1); + } + break; + + case NODE_VCALL: + result = rb_call(CLASS_OF(self),self,node->nd_mid,0,0,2); + break; + + case NODE_SUPER: + case NODE_ZSUPER: + { + int argc; VALUE *argv; /* used in SETUP_ARGS */ + + if (nd_type(node) == NODE_ZSUPER) { + argc = the_frame->argc; + argv = the_frame->argv; + } + else { + PUSH_ITER(ITER_NOT); + SETUP_ARGS(node->nd_args); + POP_ITER(); + } + + PUSH_ITER(the_iter->iter?ITER_PRE:ITER_NOT); + result = rb_call(the_frame->last_class->super, self, + the_frame->last_func, argc, argv, 1); + POP_ITER(); + } + break; + + case NODE_SCOPE: + { + VALUE save = the_frame->cbase; + + PUSH_SCOPE(); + PUSH_TAG(); + if (node->nd_rval) the_frame->cbase = (VALUE)node->nd_rval; + if (node->nd_tbl) { + VALUE *vars = ALLOCA_N(VALUE, node->nd_tbl[0]+1); + *vars++ = (VALUE)node; + the_scope->local_vars = vars; + memclear(the_scope->local_vars, node->nd_tbl[0]); + the_scope->local_tbl = node->nd_tbl; + } + else { + the_scope->local_vars = 0; + the_scope->local_tbl = 0; + } + if ((state = EXEC_TAG()) == 0) { + result = rb_eval(self, node->nd_body); + } + POP_TAG(); + POP_SCOPE(); + the_frame->cbase = save; + if (state) JUMP_TAG(state); + } + break; + + case NODE_OP_ASGN1: + { + int argc; VALUE *argv; /* used in SETUP_ARGS */ + VALUE recv, val; + NODE *rval; + + recv = rb_eval(self, node->nd_recv); + rval = node->nd_args->nd_head; + SETUP_ARGS(node->nd_args->nd_next); + val = rb_funcall2(recv, aref, argc-1, argv); + val = rb_funcall(val, node->nd_mid, 1, rb_eval(self, rval)); + argv[argc-1] = val; + val = rb_funcall2(recv, aset, argc, argv); + result = val; + } + break; + + case NODE_OP_ASGN2: + { + ID id = node->nd_next->nd_vid; + VALUE recv, val; + + recv = rb_eval(self, node->nd_recv); + val = rb_funcall(recv, id, 0); + + val = rb_funcall(val, node->nd_next->nd_mid, 1, + rb_eval(self, node->nd_value)); + + rb_funcall2(recv, id_attrset(id), 1, &val); + result = val; + } + break; + + case NODE_MASGN: + result = massign(self, node, rb_eval(self, node->nd_value)); + break; + + case NODE_LASGN: + if (the_scope->local_vars == 0) + Bug("unexpected local variable assignment"); + the_scope->local_vars[node->nd_cnt] = rb_eval(self, node->nd_value); + result = the_scope->local_vars[node->nd_cnt]; + break; + + case NODE_DASGN: + result = dyna_var_asgn(node->nd_vid, rb_eval(self, node->nd_value)); + break; + + case NODE_GASGN: + { + VALUE val; + + val = rb_eval(self, node->nd_value); + rb_gvar_set(node->nd_entry, val); + result = val; + } + break; + + case NODE_IASGN: + { + VALUE val; + + val = rb_eval(self, node->nd_value); + rb_ivar_set(self, node->nd_vid, val); + result = val; + } + break; + + case NODE_CASGN: + { + VALUE val; + + val = rb_eval(self, node->nd_value); + /* check for static scope constants */ + if (verbose && ev_const_defined(the_frame->cbase, node->nd_vid)) { + Warning("already initialized constant %s", + rb_id2name(node->nd_vid)); + } + rb_const_set(the_class, node->nd_vid, val); + result = val; + } + break; + + case NODE_LVAR: + if (the_scope->local_vars == 0) { + Bug("unexpected local variable"); + } + result = the_scope->local_vars[node->nd_cnt]; + break; + + case NODE_DVAR: + result = dyna_var_ref(node->nd_vid); + break; + + case NODE_GVAR: + result = rb_gvar_get(node->nd_entry); + break; + + case NODE_IVAR: + result = rb_ivar_get(self, node->nd_vid); + break; + + case NODE_CVAR: + result = ev_const_get(the_frame->cbase, node->nd_vid); + break; + + case NODE_COLON2: + { + VALUE cls; + + cls = rb_eval(self, node->nd_head); + switch (TYPE(cls)) { + case T_CLASS: + case T_MODULE: + break; + default: + Check_Type(cls, T_CLASS); + break; + } + result = rb_const_get_at(cls, node->nd_mid); + } + break; + + case NODE_NTH_REF: + result = reg_nth_match(node->nd_nth, MATCH_DATA); + break; + + case NODE_BACK_REF: + switch (node->nd_nth) { + case '&': + result = reg_last_match(MATCH_DATA); + break; + case '`': + result = reg_match_pre(MATCH_DATA); + break; + case '\'': + result = reg_match_post(MATCH_DATA); + break; + case '+': + result = reg_match_last(MATCH_DATA); + break; + default: + Bug("unexpected back-ref"); + } + break; + + case NODE_HASH: + { + NODE *list; + VALUE hash = hash_new(); + VALUE key, val; + + list = node->nd_head; + while (list) { + key = rb_eval(self, list->nd_head); + list = list->nd_next; + if (list == 0) + Bug("odd number list for Hash"); + val = rb_eval(self, list->nd_head); + list = list->nd_next; + hash_aset(hash, key, val); + } + result = hash; + } + break; + + case NODE_ZARRAY: /* zero length list */ + result = ary_new(); + break; + + case NODE_ARRAY: + { + VALUE ary; + int i; + + i = node->nd_alen; + ary = ary_new2(i); + for (i=0;node;node=node->nd_next) { + RARRAY(ary)->ptr[i++] = rb_eval(self, node->nd_head); + RARRAY(ary)->len = i; + } + + result = ary; + } + break; + + case NODE_STR: + result = str_new3(node->nd_lit); + break; + + case NODE_DSTR: + case NODE_DXSTR: + case NODE_DREGX: + case NODE_DREGX_ONCE: + { + VALUE str, str2; + NODE *list = node->nd_next; + + str = str_new3(node->nd_lit); + while (list) { + if (nd_type(list->nd_head) == NODE_STR) { + str2 = list->nd_head->nd_lit; + } + else { + if (nd_type(list->nd_head) == NODE_EVSTR) { + rb_in_eval++; + list->nd_head = compile(list->nd_head->nd_lit); + rb_in_eval--; + if (nerrs > 0) { + compile_error("string expand"); + } + } + str2 = rb_eval(self, list->nd_head); + str2 = obj_as_string(str2); + } + if (str2) { + str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len); + } + list = list->nd_next; + } + switch (nd_type(node)) { + case NODE_DREGX: + result = reg_new(RSTRING(str)->ptr, RSTRING(str)->len, + node->nd_cflag); + break; + case NODE_DREGX_ONCE: /* regexp expand once */ + result = reg_new(RSTRING(str)->ptr, RSTRING(str)->len, + node->nd_cflag); + nd_set_type(node, NODE_LIT); + node->nd_lit = result; + break; + case NODE_DXSTR: + result = rb_funcall(self, '`', 1, str); + break; + default: + result = str; + break; + } + } + break; + + case NODE_XSTR: + result = rb_funcall(self, '`', 1, node->nd_lit); + break; + + case NODE_LIT: + result = node->nd_lit; + break; + + case NODE_ATTRSET: + if (the_frame->argc != 1) + ArgError("Wrong # of arguments(%d for 1)", the_frame->argc); + result = rb_ivar_set(self, node->nd_vid, the_frame->argv[0]); + break; + + case NODE_DEFN: + if (node->nd_defn) { + NODE *body; + VALUE origin; + int noex; + + body = search_method(the_class, node->nd_mid, &origin); + if (body) { + if (origin == (VALUE)the_class) { + Warning("redefine %s", rb_id2name(node->nd_mid)); + } + rb_clear_cache(); + } + + if (body) noex = body->nd_noex; + else noex = node->nd_noex; /* default(1 for toplevel) */ + + rb_add_method(the_class, node->nd_mid, node->nd_defn, noex); + result = Qnil; + } + break; + + case NODE_DEFS: + if (node->nd_defn) { + VALUE recv = rb_eval(self, node->nd_recv); + VALUE class; + NODE *body; + + if (FIXNUM_P(recv)) { + TypeError("Can't define method \"%s\" for Fixnum", + rb_id2name(node->nd_mid)); + } + if (NIL_P(recv)) { + TypeError("Can't define method \"%s\" for nil", + rb_id2name(node->nd_mid)); + } + if (rb_special_const_p(recv)) { + TypeError("Can't define method \"%s\" for special constants", + rb_id2name(node->nd_mid)); + } + + class = rb_singleton_class(recv); + if (st_lookup(RCLASS(class)->m_tbl, node->nd_mid, &body)) { + Warning("redefine %s", rb_id2name(node->nd_mid)); + } + rb_clear_cache(); + rb_funcall(recv, rb_intern("singleton_method_added"), + 1, INT2FIX(node->nd_mid)); + rb_add_method(class, node->nd_mid, node->nd_defn, NOEX_PUBLIC); + result = Qnil; + } + break; + + case NODE_UNDEF: + { + struct RClass *origin; + NODE *body; + + body = search_method(the_class, node->nd_mid, &origin); + if (!body || !body->nd_body) { + NameError("undefined method `%s' for class `%s'", + rb_id2name(node->nd_mid), rb_class2name((VALUE)the_class)); + } + rb_clear_cache(); + rb_add_method(the_class, node->nd_mid, 0, NOEX_PUBLIC); + result = Qnil; + } + break; + + case NODE_ALIAS: + rb_alias(the_class, node->nd_new, node->nd_old); + result = Qnil; + break; + + case NODE_VALIAS: + rb_alias_variable(node->nd_new, node->nd_old); + result = Qnil; + break; + + case NODE_CLASS: + { + VALUE super, class; + struct RClass *tmp; + + if (node->nd_super) { + super = superclass(self, node->nd_super); + } + else { + super = 0; + } + + if (rb_const_defined_at(the_class, node->nd_cname) && + ((VALUE)the_class != cObject || + !rb_autoload_defined(node->nd_cname))) { + + class = rb_const_get_at(the_class, node->nd_cname); + if (TYPE(class) != T_CLASS) { + TypeError("%s is not a class", rb_id2name(node->nd_cname)); + } + if (super) { + tmp = RCLASS(class)->super; + if (FL_TEST(tmp, FL_SINGLETON)) { + tmp = RCLASS(tmp)->super; + } + while (TYPE(tmp) == T_ICLASS) { + tmp = RCLASS(tmp)->super; + } + if (tmp != RCLASS(super)) { + TypeError("superclass mismatch for %s", + rb_id2name(node->nd_cname)); + } + } + if (safe_level >= 4) { + Raise(eSecurityError, "extending class prohibited"); + } + rb_clear_cache(); + Warning("extending class %s", rb_id2name(node->nd_cname)); + } + else { + if (!super) super = cObject; + class = rb_define_class_id(node->nd_cname, super); + rb_const_set(the_class, node->nd_cname, class); + rb_set_class_path(class,the_class,rb_id2name(node->nd_cname)); + } + + result = module_setup(class, node->nd_body); + } + break; + + case NODE_MODULE: + { + VALUE module; + + if (rb_const_defined_at(the_class, node->nd_cname) && + ((VALUE)the_class != cObject || + !rb_autoload_defined(node->nd_cname))) { + + module = rb_const_get_at(the_class, node->nd_cname); + if (TYPE(module) != T_MODULE) { + TypeError("%s is not a module", rb_id2name(node->nd_cname)); + } + if (safe_level >= 4) { + Raise(eSecurityError, "extending module prohibited"); + } + Warning("extending module %s", rb_id2name(node->nd_cname)); + } + else { + module = rb_define_module_id(node->nd_cname); + rb_const_set(the_class, node->nd_cname, module); + rb_set_class_path(module,the_class,rb_id2name(node->nd_cname)); + } + + result = module_setup(module, node->nd_body); + } + break; + + case NODE_SCLASS: + { + VALUE class; + + class = rb_eval(self, node->nd_recv); + if (FIXNUM_P(class)) { + TypeError("No virtual class for Fixnums"); + } + if (NIL_P(class)) { + TypeError("No virtual class for nil"); + } + if (rb_special_const_p(class)) { + TypeError("No virtual class for special constants"); + } + if (FL_TEST(CLASS_OF(class), FL_SINGLETON)) { + rb_clear_cache(); + } + class = rb_singleton_class(class); + + result = module_setup(class, node->nd_body); + } + break; + + case NODE_DEFINED: + { + char buf[20]; + char *desc = is_defined(self, node, buf); + + if (desc) result = str_new2(desc); + else result = FALSE; + } + break; + + case NODE_NEWLINE: + sourcefile = node->file; + sourceline = node->nd_nth; + if (trace_func) { + call_trace_func("line", sourcefile, sourceline, + self, the_frame->last_func); + } + node = node->nd_next; + goto again; + + default: + Bug("unknown node type %d", nd_type(node)); + } + finish: + CHECK_INTS; + return result; +} + +static VALUE +module_setup(module, node) + VALUE module; + NODE * volatile node; +{ + NODE *state; + VALUE save = the_frame->cbase; + VALUE result; /* OK */ + + /* fill c-ref */ + node->nd_clss = module; + node = node->nd_body; + + PUSH_CLASS(); + the_class = (struct RClass*)module; + PUSH_SCOPE(); + + if (node->nd_rval) the_frame->cbase = node->nd_rval; + if (node->nd_tbl) { + VALUE *vars = ALLOCA_N(VALUE, node->nd_tbl[0]+1); + *vars++ = (VALUE)node; + the_scope->local_vars = vars; + memclear(the_scope->local_vars, node->nd_tbl[0]); + the_scope->local_tbl = node->nd_tbl; + } + else { + the_scope->local_vars = 0; + the_scope->local_tbl = 0; + } + + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + if (trace_func) { + call_trace_func("class", node->file, nd_line(node), + the_class, the_frame->last_func); + } + result = rb_eval((VALUE)the_class, node->nd_body); + } + POP_TAG(); + POP_SCOPE(); + POP_CLASS(); + the_frame->cbase = save; + if (trace_func) { + call_trace_func("end", node->file, nd_line(node), 0, + the_frame->last_func); + } + if (state) JUMP_TAG(state); + + return result; +} + +int +rb_respond_to(obj, id) + VALUE obj; + ID id; +{ + if (rb_method_boundp(CLASS_OF(obj), id, 0)) { + return TRUE; + } + return FALSE; +} + +static VALUE +obj_respond_to(argc, argv, obj) + int argc; + VALUE *argv; + VALUE obj; +{ + VALUE mid, priv; + ID id; + + rb_scan_args(argc, argv, "11", &mid, &priv); + id = rb_to_id(mid); + if (rb_method_boundp(CLASS_OF(obj), id, !RTEST(priv))) { + return TRUE; + } + return FALSE; +} + +static VALUE +mod_method_defined(mod, mid) + VALUE mod, mid; +{ + if (rb_method_boundp(mod, rb_to_id(mid), 1)) { + return TRUE; + } + return FALSE; +} + +void +rb_exit(status) + int status; +{ + if (prot_tag) { + exit_status = status; + rb_raise(exc_new(eSystemExit, 0, 0)); + } + exit(status); +} + +static VALUE +f_exit(argc, argv, obj) + int argc; + VALUE *argv; + VALUE obj; +{ + VALUE status; + + rb_secure(2); + if (rb_scan_args(argc, argv, "01", &status) == 1) { + status = NUM2INT(status); + } + else { + status = 0; + } + rb_exit(status); + /* not reached */ +} + +static VALUE +f_abort() +{ + rb_secure(2); + if (errinfo) { + error_print(); + } + rb_exit(1); + /* not reached */ +} + +void +rb_break() +{ + JUMP_TAG2(TAG_BREAK, 0); +} + +static VALUE +f_break() +{ + JUMP_TAG2(TAG_BREAK, 0); +} + +static VALUE +f_next() +{ + JUMP_TAG2(TAG_NEXT, 0); +} + +static VALUE +f_redo() +{ + JUMP_TAG2(TAG_REDO, 0); +} + +static VALUE +f_retry() +{ + JUMP_TAG2(TAG_RETRY, 0); +} + +#ifdef __GNUC__ +static volatile voidfn rb_longjmp; +#endif + +static VALUE make_backtrace(); + +static void +rb_longjmp(tag, mesg) + int tag; + VALUE mesg; +{ + if (NIL_P(errinfo) && NIL_P(mesg)) { + errinfo = exc_new(eRuntimeError, 0, 0); + } + + if (sourcefile && (NIL_P(errat) || !NIL_P(mesg))) { + errat = make_backtrace(); + } + + if (!NIL_P(mesg)) { + if (obj_is_kind_of(mesg, eGlobalExit)) { + errinfo = mesg; + } + else { + errinfo = exc_new3(eRuntimeError, mesg); + } + str_freeze(errinfo); + } + + JUMP_TAG2(tag, 0); +} + +void +rb_raise(mesg) + VALUE mesg; +{ + rb_longjmp(TAG_RAISE, mesg); +} + +void +rb_fatal(mesg) + VALUE mesg; +{ + rb_longjmp(TAG_FATAL, mesg); +} + +void +rb_interrupt() +{ + Raise(eInterrupt, ""); +} + +static VALUE +f_raise(argc, argv) + int argc; + VALUE *argv; +{ + VALUE arg1, arg2; + VALUE etype, mesg; + int n; + + etype = eRuntimeError; + mesg = Qnil; + switch (n = rb_scan_args(argc, argv, "02", &arg1, &arg2)) { + case 1: + mesg = arg1; + break; + case 2: + etype = arg1; + if (obj_is_kind_of(etype, eGlobalExit)) { + etype = CLASS_OF(etype); + } + else { + Check_Type(etype, T_CLASS); + } + mesg = arg2; + break; + } + + if (!NIL_P(mesg)) { + Check_Type(mesg, T_STRING); + if (n == 2 || !obj_is_kind_of(mesg, eException)) { + mesg = exc_new3(etype, mesg); + } + } + + PUSH_FRAME(); /* fake frame */ + *the_frame = *_frame.prev->prev; + rb_raise(mesg); + POP_FRAME(); +} + +int +iterator_p() +{ + if (the_frame->iter) return TRUE; + return FALSE; +} + +static VALUE +f_iterator_p() +{ + if (the_frame->prev && the_frame->prev->iter) return TRUE; + return FALSE; +} + +VALUE +rb_yield_0(val, self) + VALUE val; + volatile VALUE self; +{ + NODE *node; + NODE *state; + volatile VALUE result = Qnil; + struct BLOCK *block; + struct SCOPE *old_scope; + struct FRAME frame; + + if (!iterator_p()) { + Raise(eLocalJumpError, "yield called out of iterator"); + } + + PUSH_VARS(); + PUSH_CLASS(); + block = the_block; + frame = block->frame; + frame.prev = the_frame; + the_frame = &(frame); + old_scope = the_scope; + the_scope = block->scope; + the_block = block->prev; + the_dyna_vars = block->d_vars; + the_class = block->class; + if (!self) self = block->self; + node = block->body; + if (block->var) { + if (nd_type(block->var) == NODE_MASGN) + massign(self, block->var, val); + else + assign(self, block->var, val); + } + PUSH_ITER(block->iter); + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + redo: + if (!node) { + result = Qnil; + } + else if (nd_type(node) == NODE_CFUNC) { + result = (*node->nd_cfnc)(val, node->nd_argc, self); + } + else { + result = rb_eval(self, node); + } + } + else { + switch (state->nd_tag) { + case TAG_REDO: + state = 0; + goto redo; + case TAG_NEXT: + state = 0; + result = Qnil; + break; + case TAG_BREAK: + case TAG_RETURN: + state->nd_tlev = block->level; + state->nd_tag = IN_BLOCK|state->nd_tag; + break; + default: + break; + } + } + POP_TAG(); + POP_ITER(); + POP_CLASS(); + POP_VARS(); + the_block = block; + the_frame = the_frame->prev; + the_scope = old_scope; + if (state) JUMP_TAG(state); + return result; +} + +VALUE +rb_yield(val) + VALUE val; +{ + return rb_yield_0(val, 0); +} + +static VALUE +f_loop() +{ + for (;;) { rb_yield(Qnil); } +} + +static VALUE +massign(self, node, val) + VALUE self; + NODE *node; + VALUE val; +{ + NODE *list; + int i, len; + + list = node->nd_head; + + if (val) { + if (TYPE(val) != T_ARRAY) { + val = rb_to_a(val); + } + len = RARRAY(val)->len; + for (i=0; list && i<len; i++) { + assign(self, list->nd_head, RARRAY(val)->ptr[i]); + list = list->nd_next; + } + if (node->nd_args) { + if (!list && i<len) { + assign(self, node->nd_args, ary_new4(len-i, RARRAY(val)->ptr+i)); + } + else { + assign(self, node->nd_args, ary_new2(0)); + } + } + } + else if (node->nd_args) { + assign(self, node->nd_args, Qnil); + } + while (list) { + assign(self, list->nd_head, Qnil); + list = list->nd_next; + } + return val; +} + +static void +assign(self, lhs, val) + VALUE self; + NODE *lhs; + VALUE val; +{ + switch (nd_type(lhs)) { + case NODE_GASGN: + rb_gvar_set(lhs->nd_entry, val); + break; + + case NODE_IASGN: + rb_ivar_set(self, lhs->nd_vid, val); + break; + + case NODE_LASGN: + if (the_scope->local_vars == 0) + Bug("unexpected iterator variable assignment"); + the_scope->local_vars[lhs->nd_cnt] = val; + break; + + case NODE_DASGN: + dyna_var_asgn(lhs->nd_vid, val); + break; + + case NODE_CASGN: + rb_const_set(the_class, lhs->nd_vid, val); + break; + + case NODE_CALL: + { + VALUE recv; + recv = rb_eval(self, lhs->nd_recv); + if (!lhs->nd_args->nd_head) { + /* attr set */ + rb_funcall2(recv, lhs->nd_mid, 1, &val); + } + else { + /* array set */ + VALUE args; + + args = rb_eval(self, lhs->nd_args); + RARRAY(args)->ptr[RARRAY(args)->len-1] = val; + rb_apply(recv, lhs->nd_mid, args); + } + } + break; + + default: + Bug("bug in variable assignment"); + break; + } +} + +VALUE +rb_iterate(it_proc, data1, bl_proc, data2) + VALUE (*it_proc)(), (*bl_proc)(); + void *data1, *data2; +{ + NODE *state; + volatile VALUE retval = Qnil; + NODE *node = NEW_CFUNC(bl_proc, data2); + VALUE self = TopSelf; + int tag_level; + + iter_retry: + PUSH_ITER(ITER_PRE); + PUSH_BLOCK(0, node); + PUSH_TAG(); + + state = EXEC_TAG(); + if (state == 0) { + retval = (*it_proc)(data1); + } + POP_TAG(); + + tag_level = the_block->level; + POP_BLOCK(); + POP_ITER(); + + if (state) { + switch (state->nd_tag) { + case TAG_RETRY: + goto iter_retry; + + case IN_BLOCK|TAG_BREAK: + if (state->nd_tlev != tag_level) { + JUMP_TAG(state); + } + retval = Qnil; + break; + + case IN_BLOCK|TAG_RETURN: + if (state->nd_tlev == tag_level) { + state->nd_tag &= ~IN_BLOCK; + } + /* fall through */ + default: + JUMP_TAG(state); + } + } + return retval; +} + +static int +handle_rescue(self, node) + VALUE self; + NODE *node; +{ + int argc; VALUE *argv; /* used in SETUP_ARGS */ + + if (!node->nd_args) { + return obj_is_kind_of(errinfo, eException); + } + + PUSH_ITER(ITER_NOT); + SETUP_ARGS(node->nd_args); + POP_ITER(); + while (argc--) { + if (!obj_is_kind_of(argv[0], cModule)) { + TypeError("class or module required for rescue clause"); + } + if (obj_is_kind_of(errinfo, argv[0])) return 1; + argv++; + } + return 0; +} + +VALUE +rb_rescue(b_proc, data1, r_proc, data2) + VALUE (*b_proc)(), (*r_proc)(); + void *data1, *data2; +{ + NODE *state; + volatile VALUE result; + + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + retry_entry: + result = (*b_proc)(data1); + } + else { + if (state->nd_tag == TAG_RAISE) { + if (r_proc) { + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + result = (*r_proc)(data2, errinfo); + } + POP_TAG(); + if (state && state->nd_tag == TAG_RETRY) { + state = 0; + goto retry_entry; + } + } + else { + result = Qnil; + state = 0; + } + if (state == 0) { + errat = Qnil; + } + } + } + POP_TAG(); + if (state) JUMP_TAG(state); + + return result; +} + +VALUE +rb_ensure(b_proc, data1, e_proc, data2) + VALUE (*b_proc)(), (*e_proc)(); + void *data1, *data2; +{ + NODE *state; + volatile VALUE result = Qnil; + + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + result = (*b_proc)(data1); + } + POP_TAG(); + + (*e_proc)(data2); + if (state) { + JUMP_TAG(state); + } + return result; +} + +static int last_call_status; +#define CSTAT_NOEX 1 +#define CSTAT_VCALL 2 + +static VALUE +f_missing(argc, argv, obj) + int argc; + VALUE *argv; + VALUE obj; +{ + VALUE desc = 0; + ID id; + char *format = 0; + char *file = sourcefile; + int line = sourceline; + + id = FIX2INT(argv[0]); + argc--; argv++; + + switch (TYPE(obj)) { + case T_NIL: + format = "undefined method `%s' for nil"; + break; + case T_TRUE: + format = "undefined method `%s' for TRUE"; + break; + case T_FALSE: + format = "undefined method `%s' for FALSE"; + break; + case T_OBJECT: + desc = obj_as_string(obj); + break; + default: + desc = rb_inspect(obj); + break; + } + if (desc) { + if (last_call_status & CSTAT_NOEX) { + format = "private method `%s' called for %s(%s)"; + } + else if (iterator_p()) { + format = "undefined iterator `%s' for %s(%s)"; + } + else if (last_call_status & CSTAT_VCALL) { + char *mname = rb_id2name(id); + + if (('a' <= mname[0] && mname[0] <= 'z') || mname[0] == '_') { + format = "undefined local variable or method `%s' for %s(%s)"; + } + } + if (!format) { + format = "undefined method `%s' for %s(%s)"; + } + if (RSTRING(desc)->len > 65) { + desc = any_to_s(obj); + } + } + + sourcefile = file; + sourceline = line; + PUSH_FRAME(); /* fake frame */ + *the_frame = *_frame.prev->prev; + + NameError(format, + rb_id2name(id), + desc?(char*)RSTRING(desc)->ptr:"", + desc?rb_class2name(CLASS_OF(obj)):""); + POP_FRAME(); + + return Qnil; /* not reached */ +} + +static VALUE +rb_undefined(obj, id, argc, argv, call_status) + VALUE obj; + ID id; + int argc; + VALUE*argv; + int call_status; +{ + VALUE *nargv; + + nargv = ALLOCA_N(VALUE, argc+1); + nargv[0] = INT2FIX(id); + MEMCPY(nargv+1, argv, VALUE, argc); + + last_call_status = call_status; + + return rb_funcall2(obj, rb_intern("method_missing"), argc+1, nargv); +} + +#ifdef DJGPP +# define STACK_LEVEL_MAX 65535 +#else +#ifdef __human68k__ +extern int _stacksize; +# define STACK_LEVEL_MAX (_stacksize - 4096) +#else +# define STACK_LEVEL_MAX 655350 +#endif +#endif +extern VALUE *gc_stack_start; +static int +stack_length() +{ + VALUE pos; + +#ifdef sparc + return gc_stack_start - &pos + 0x80; +#else + return (&pos < gc_stack_start) ? gc_stack_start - &pos + : &pos - gc_stack_start; +#endif +} + +static VALUE +rb_call(class, recv, mid, argc, argv, scope) + struct RClass *class; + VALUE recv; + ID mid; + int argc; /* OK */ + VALUE *argv; /* OK */ + int scope; +{ + NODE *body, *b2; /* OK */ + int noex; + ID id = mid; + struct cache_entry *ent; + volatile VALUE result = Qnil; + int itr; + enum node_type type; + static int tick; + + again: + /* is it in the method cache? */ + ent = cache + EXPR1(class, mid); + if (ent->mid == mid && ent->class == class) { + class = ent->origin; + id = ent->mid0; + noex = ent->noex; + body = ent->method; + } + else if ((body = rb_get_method_body(&class, &id, &noex)) == 0) { + return rb_undefined(recv, mid, argc, argv, scope==2?CSTAT_VCALL:0); + } + + /* receiver specified form for private method */ + if (noex == NOEX_PRIVATE && scope == 0) + return rb_undefined(recv, mid, argc, argv, CSTAT_NOEX); + + switch (the_iter->iter) { + case ITER_PRE: + itr = ITER_CUR; + break; + case ITER_CUR: + default: + itr = ITER_NOT; + break; + } + + type = nd_type(body); + if (type == NODE_ZSUPER) { + /* for re-scoped/renamed method */ + mid = id; + if (scope == 0) scope = 1; + if (class->super == 0) { + /* origin is the Module, so need to scan superclass hierarchy. */ + struct RClass *cl = class; + + class = (struct RClass*)RBASIC(recv)->class; + while (class) { + if (class->m_tbl == cl->m_tbl) + break; + class = class->super; + } + } + else { + class = class->super; + } + goto again; + } + + if ((++tick & 0xfff) == 0 && stack_length() > STACK_LEVEL_MAX) + Fatal("stack level too deep"); + + PUSH_ITER(itr); + PUSH_FRAME(); + the_frame->last_func = id; + the_frame->last_class = class; + the_frame->argc = argc; + the_frame->argv = argv; + + switch (type) { + case NODE_CFUNC: + { + int len = body->nd_argc; + + if (len >= 0 && argc != len) { + ArgError("Wrong # of arguments(%d for %d)", argc, len); + } + + switch (len) { + case -2: + result = (*body->nd_cfnc)(recv, ary_new4(argc, argv)); + break; + case -1: + result = (*body->nd_cfnc)(argc, argv, recv); + break; + case 0: + result = (*body->nd_cfnc)(recv); + break; + case 1: + result = (*body->nd_cfnc)(recv, argv[0]); + break; + case 2: + result = (*body->nd_cfnc)(recv, argv[0], argv[1]); + break; + case 3: + result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2]); + break; + case 4: + result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2], + argv[3]); + break; + case 5: + result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2], + argv[3], argv[4]); + break; + case 6: + result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5]); + break; + case 7: + result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5], + argv[6]); + break; + case 8: + result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5], + argv[6], argv[7]); + break; + case 9: + result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5], + argv[6], argv[7], argv[8]); + break; + case 10: + result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5], + argv[6], argv[7], argv[8], + argv[6], argv[7], argv[8], + argv[9]); + break; + case 11: + result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5], + argv[6], argv[7], argv[8], + argv[6], argv[7], argv[8], + argv[9], argv[10]); + break; + case 12: + result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5], + argv[6], argv[7], argv[8], + argv[6], argv[7], argv[8], + argv[9], argv[10], argv[11]); + break; + case 13: + result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5], + argv[6], argv[7], argv[8], + argv[6], argv[7], argv[8], + argv[9], argv[10], argv[11], + argv[12]); + break; + case 14: + result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5], + argv[6], argv[7], argv[8], + argv[6], argv[7], argv[8], + argv[9], argv[10], argv[11], + argv[12], argv[13]); + break; + case 15: + result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5], + argv[6], argv[7], argv[8], + argv[6], argv[7], argv[8], + argv[9], argv[10], argv[11], + argv[12], argv[13], argv[14]); + break; + default: + if (len < 0) { + Bug("bad argc(%d) specified for `%s(%s)'", + len, rb_class2name((VALUE)class), rb_id2name(mid)); + } + else { + ArgError("too many arguments(%d)", len); + } + break; + } + } + break; + + /* for attr get/set */ + case NODE_ATTRSET: + case NODE_IVAR: + result = rb_eval(recv, body); + break; + + default: + { + NODE *state; + VALUE *local_vars; + + PUSH_SCOPE(); + + if (body->nd_rval) the_frame->cbase = body->nd_rval; + if (body->nd_tbl) { + local_vars = ALLOCA_N(VALUE, body->nd_tbl[0]+1); + *local_vars++ = (VALUE)body; + memclear(local_vars, body->nd_tbl[0]); + the_scope->local_tbl = body->nd_tbl; + the_scope->local_vars = local_vars; + } + else { + local_vars = the_scope->local_vars = 0; + the_scope->local_tbl = 0; + } + b2 = body = body->nd_body; + + PUSH_TAG(); + PUSH_VARS(); + + if ((state = EXEC_TAG()) == 0) { + if (nd_type(body) == NODE_BLOCK) { + NODE *node = body->nd_head; + int i; + + if (nd_type(node) != NODE_ARGS) { + Bug("no argument-node"); + } + + body = body->nd_next; + i = node->nd_cnt; + if (i > argc) { + ArgError("Wrong # of arguments(%d for %d)", argc, i); + } + if (node->nd_rest == -1) { + int opt = argc - i; + NODE *optnode = node->nd_opt; + + while (optnode) { + opt--; + optnode = optnode->nd_next; + } + if (opt > 0) { + ArgError("Wrong # of arguments(%d for %d)", + argc, argc-opt); + } + } + + if (local_vars) { + if (i > 0) { + MEMCPY(local_vars, argv, VALUE, i); + } + argv += i; argc -= i; + if (node->nd_opt) { + NODE *opt = node->nd_opt; + + while (opt && argc) { + assign(recv, opt->nd_head, *argv); + argv++; argc--; + opt = opt->nd_next; + } + rb_eval(recv, opt); + } + if (node->nd_rest >= 0) { + if (argc > 0) + local_vars[node->nd_rest]=ary_new4(argc,argv); + else + local_vars[node->nd_rest]=ary_new2(0); + } + } + } + else if (nd_type(body) == NODE_ARGS) { + body = 0; + } + if (trace_func) { + call_trace_func("call", b2->file, nd_line(b2), + recv, the_frame->last_func); + } + result = rb_eval(recv, body); + } + POP_VARS(); + POP_TAG(); + POP_SCOPE(); + if (trace_func) { + char *file = the_frame->prev->file; + int line = the_frame->prev->line; + if (!file) { + file = sourcefile; + line = sourceline; + } + call_trace_func("return", file, line, 0, the_frame->last_func); + } + if (state) { + switch (state->nd_tag) { + case TAG_NEXT: + Raise(eLocalJumpError, "unexpected next"); + break; + case TAG_BREAK: + Raise(eLocalJumpError, "unexpected break"); + break; + case TAG_REDO: + Raise(eLocalJumpError, "unexpected redo"); + break; + case TAG_RETURN: + result = state->nd_tval; + break; + case TAG_RETRY: + if (!iterator_p()) { + Raise(eLocalJumpError, "retry outside of rescue clause"); + } + default: + JUMP_TAG(state); + } + } + } + } + POP_FRAME(); + POP_ITER(); + return result; +} + +VALUE +rb_apply(recv, mid, args) + VALUE recv; + struct RArray *args; + ID mid; +{ + int argc; + VALUE *argv; + + argc = RARRAY(args)->len; + argv = ALLOCA_N(VALUE, argc); + MEMCPY(argv, RARRAY(args)->ptr, VALUE, argc); + return rb_call(CLASS_OF(recv), recv, mid, argc, argv, 1); +} + +static VALUE +f_send(argc, argv, recv) + int argc; + VALUE *argv; + VALUE recv; +{ + VALUE vid; + ID mid; + + if (argc == 0) ArgError("no method name given"); + + vid = argv[0]; argc--; argv++; + if (TYPE(vid) == T_STRING) { + mid = rb_intern(RSTRING(vid)->ptr); + } + else { + mid = NUM2INT(vid); + } + PUSH_ITER(iterator_p()?ITER_PRE:ITER_NOT); + vid = rb_call(CLASS_OF(recv), recv, mid, argc, argv, 1); + POP_ITER(); + + return vid; +} + +#include <varargs.h> + +VALUE +rb_funcall(recv, mid, n, va_alist) + VALUE recv; + ID mid; + int n; + va_dcl +{ + va_list ar; + VALUE *argv; + + if (n > 0) { + int i; + + argv = ALLOCA_N(VALUE, n); + + va_start(ar); + for (i=0;i<n;i++) { + argv[i] = va_arg(ar, VALUE); + } + va_end(ar); + } + else { + argv = 0; + } + + return rb_call(CLASS_OF(recv), recv, mid, n, argv, 1); +} + +VALUE +rb_funcall2(recv, mid, argc, argv) + VALUE recv; + ID mid; + int argc; + VALUE *argv; +{ + return rb_call(CLASS_OF(recv), recv, mid, argc, argv, 1); +} + +static VALUE +backtrace(lev) + int lev; +{ + struct FRAME *frame = the_frame; + char buf[BUFSIZ]; + VALUE ary; + int slev = safe_level; + + safe_level = 0; + ary = ary_new(); + if (lev < 0) { + if (frame->last_func) { + sprintf(buf, "%s:%d:in `%s'", sourcefile, sourceline, + rb_id2name(frame->last_func)); + } + else { + sprintf(buf, "%s:%d", sourcefile, sourceline); + } + ary_push(ary, str_new2(buf)); + } + else { + while (lev-- > 0) { + frame = frame->prev; + if (!frame) return Qnil; + } + } + while (frame && frame->file) { + if (frame->prev && frame->prev->last_func) { + sprintf(buf, "%s:%d:in `%s'", + frame->file, frame->line, + rb_id2name(frame->prev->last_func)); + } + else { + sprintf(buf, "%s:%d", frame->file, frame->line); + } + ary_push(ary, str_new2(buf)); + frame = frame->prev; + } + safe_level = slev; + return ary; +} + +static VALUE +f_caller(argc, argv) + int argc; + VALUE *argv; +{ + VALUE level; + int lev; + + rb_scan_args(argc, argv, "01", &level); + + if (NIL_P(level)) lev = 1; + else lev = NUM2INT(level); + if (lev < 0) ArgError("negative level(%d)", lev); + + return backtrace(lev); +} + +void +rb_backtrace() +{ + int i, lev; + VALUE ary; + + lev = INT2FIX(0); + ary = backtrace(-1); + for (i=0; i<RARRAY(ary)->len; i++) { + printf("\tfrom %s\n", RSTRING(RARRAY(ary)->ptr[i])->ptr); + } +} + +static VALUE +make_backtrace() +{ + VALUE lev; + + lev = INT2FIX(0); + return backtrace(-1); +} + +ID +rb_frame_last_func() +{ + return the_frame->last_func; +} + +static NODE* +compile(src) + struct RString *src; +{ + NODE *node; + + Check_Type(src, T_STRING); + + node = compile_string(sourcefile, src->ptr, src->len); + + if (nerrs == 0) return node; + return 0; +} + +static VALUE +eval(self, src, scope) + VALUE self; + struct RString *src; + struct RData *scope; +{ + struct BLOCK *data; + volatile VALUE result = Qnil; + NODE *state; + volatile VALUE old_block; + volatile VALUE old_scope; + volatile VALUE old_d_vars; + struct FRAME frame; + char *file = sourcefile; + int line = sourceline; + volatile int iter = the_frame->iter; + + if (!NIL_P(scope)) { + if (TYPE(scope) != T_DATA || scope->dfree != blk_free) { + TypeError("wrong argument type %s (expected Proc/Binding)", + rb_class2name(CLASS_OF(scope))); + } + + Data_Get_Struct(scope, struct BLOCK, data); + + /* PUSH BLOCK from data */ + frame = data->frame; + frame.prev = the_frame; + the_frame = &(frame); + old_scope = (VALUE)the_scope; + the_scope = data->scope; + old_block = (VALUE)the_block; + the_block = data->prev; + old_d_vars = (VALUE)the_dyna_vars; + the_dyna_vars = data->d_vars; + + self = data->self; + the_frame->iter = data->iter; + } + else { + if (the_frame->prev) { + the_frame->iter = the_frame->prev->iter; + } + } + PUSH_CLASS(); + the_class = (struct RClass*)((NODE*)the_frame->cbase)->nd_clss; + + rb_in_eval++; + if (TYPE(the_class) == T_ICLASS) { + the_class = (struct RClass*)RBASIC(the_class)->class; + } + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + compile(src); + if (nerrs > 0) { + compile_error("eval()"); + } + result = eval_node(self); + } + POP_TAG(); + POP_CLASS(); + rb_in_eval--; + if (!NIL_P(scope)) { + the_frame = the_frame->prev; + the_scope = (struct SCOPE*)old_scope; + the_block = (struct BLOCK*)old_block; + the_dyna_vars = (struct RVarmap*)old_d_vars; + } + else { + the_frame->iter = iter; + } + if (state) { + VALUE err ; + + switch (state->nd_tag) { + case TAG_RAISE: + sourcefile = file; + sourceline = line; + if (strcmp(sourcefile, "(eval)") == 0) { + err = errinfo; + if (sourceline > 1) { + err = RARRAY(errat)->ptr[0]; + str_cat(err, ": ", 2); + str_cat(err, RSTRING(errinfo)->ptr, RSTRING(errinfo)->len); + } + errat = Qnil; + rb_raise(exc_new3(CLASS_OF(errinfo), err)); + } + rb_raise(Qnil); + } + JUMP_TAG(state); + } + + return result; +} + +static VALUE +f_eval(argc, argv, self) + int argc; + VALUE *argv; + VALUE self; +{ + VALUE src, scope; + + rb_scan_args(argc, argv, "11", &src, &scope); + + Check_SafeStr(src); + return eval(self, src, scope); +} + +VALUE rb_load_path; + +char *dln_find_file(); + +static char* +find_file(file) + char *file; +{ + extern VALUE rb_load_path; + VALUE vpath; + char *path; + + if (file[0] == '/') return file; +#if defined(MSDOS) || defined(NT) || defined(__human68k__) + if (file[0] == '\\') return file; + if (file[1] == ':') return file; +#endif + + if (rb_load_path) { + int i; + + Check_Type(rb_load_path, T_ARRAY); + for (i=0;i<RARRAY(rb_load_path)->len;i++) { + Check_SafeStr(RARRAY(rb_load_path)->ptr[i]); + } +#if !defined(MSDOS) && !defined(NT) && !defined(__human68k__) + vpath = ary_join(rb_load_path, str_new2(":")); +#else + vpath = ary_join(rb_load_path, str_new2(";")); +#endif + Check_SafeStr(vpath); + path = RSTRING(vpath)->ptr; + } + else { + path = 0; + } + + return dln_find_file(file, path); +} + +VALUE +f_load(obj, fname) + VALUE obj; + struct RString *fname; +{ + NODE *state; + char *file; + volatile ID last_func; + + Check_SafeStr(fname); + if (fname->ptr[0] == '~') { + fname = (struct RString*)file_s_expand_path(0, fname); + } + file = find_file(fname->ptr); + if (!file) LoadError("No such file to load -- %s", fname->ptr); + + PUSH_TAG(); + PUSH_CLASS(); + the_class = (struct RClass*)cObject; + PUSH_SCOPE(); + if (top_scope->local_tbl) { + int len = top_scope->local_tbl[0]+1; + ID *tbl = ALLOC_N(ID, len); + VALUE *vars = ALLOCA_N(VALUE, len); + *vars++ = 0; + MEMCPY(tbl, top_scope->local_tbl, ID, len); + MEMCPY(vars, top_scope->local_vars, ID, len-1); + the_scope->local_tbl = tbl; + the_scope->local_vars = vars; + } + + state = EXEC_TAG(); + last_func = the_frame->last_func; + the_frame->last_func = 0; + if (state == 0) { + rb_in_eval++; + rb_load_file(file); + rb_in_eval--; + if (nerrs == 0) { + eval_node(TopSelf); + } + } + the_frame->last_func = last_func; + if (the_scope->flag == SCOPE_ALLOCA && the_scope->local_tbl) { + free(the_scope->local_tbl); + } + POP_SCOPE(); + POP_CLASS(); + POP_TAG(); + if (nerrs > 0) { + rb_raise(errinfo); + } + if (state) JUMP_TAG(state); + + return TRUE; +} + +static VALUE rb_features; + +static int +rb_provided(feature) + char *feature; +{ + struct RArray *features = RARRAY(rb_features); + VALUE *p, *pend; + char *f; + int len; + + p = features->ptr; + pend = p + features->len; + while (p < pend) { + Check_Type(*p, T_STRING); + f = RSTRING(*p)->ptr; + if (strcmp(f, feature) == 0) return TRUE; + len = strlen(feature); + if (strncmp(f, feature, len) == 0 + && (strcmp(f+len, ".rb") == 0 ||strcmp(f+len, ".o") == 0)) { + return TRUE; + } + p++; + } + return FALSE; +} + +#ifdef THREAD +static int thread_loading(); +static void thread_loading_done(); +#endif + +void +rb_provide(feature) + char *feature; +{ + char *buf, *ext; + + if (!rb_provided(feature)) { + ext = strrchr(feature, '.'); + if (strcmp(DLEXT, ext) == 0) { + buf = ALLOCA_N(char, strlen(feature)+1); + strcpy(buf, feature); + ext = strrchr(buf, '.'); + strcpy(ext, ".o"); + feature = buf; + } + ary_push(rb_features, str_new2(feature)); + } +} + +VALUE +f_require(obj, fname) + VALUE obj; + struct RString *fname; +{ + char *ext, *file, *feature, *buf; + volatile VALUE load; + + Check_SafeStr(fname); + if (rb_provided(fname->ptr)) + return FALSE; + + ext = strrchr(fname->ptr, '.'); + if (ext) { + if (strcmp(".rb", ext) == 0) { + feature = file = fname->ptr; + file = find_file(file); + if (file) goto rb_load; + } + else if (strcmp(".o", ext) == 0) { + file = feature = fname->ptr; + if (strcmp(".o", DLEXT) != 0) { + buf = ALLOCA_N(char, strlen(fname->ptr)+sizeof(DLEXT)+1); + strcpy(buf, feature); + ext = strrchr(buf, '.'); + strcpy(ext, DLEXT); + file = find_file(buf); + } + if (file) goto dyna_load; + } + else if (strcmp(DLEXT, ext) == 0) { + feature = fname->ptr; + file = find_file(feature); + if (file) goto dyna_load; + } + } + buf = ALLOCA_N(char, strlen(fname->ptr) + 5); + sprintf(buf, "%s.rb", fname->ptr); + file = find_file(buf); + if (file) { + fname = (struct RString*)str_new2(file); + feature = buf; + goto rb_load; + } + sprintf(buf, "%s%s", fname->ptr, DLEXT); + file = find_file(buf); + if (file) { + feature = buf; + goto dyna_load; + } + LoadError("No such file to load -- %s", fname->ptr); + + dyna_load: +#ifdef THREAD + if (thread_loading(feature)) return FALSE; + else { + NODE *state; + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { +#endif + load = str_new2(file); + file = RSTRING(load)->ptr; + dln_load(file); + rb_provide(feature); +#ifdef THREAD + } + POP_TAG(); + thread_loading_done(); + if (state) JUMP_TAG(state); + } +#endif + return TRUE; + + rb_load: +#ifdef THREAD + if (thread_loading(feature)) return FALSE; + else { + NODE *state; + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { +#endif + f_load(obj, fname); + rb_provide(feature); +#ifdef THREAD + } + POP_TAG(); + thread_loading_done(); + if (state) JUMP_TAG(state); + } +#endif + return TRUE; +} + +static void +set_method_visibility(self, argc, argv, ex) + VALUE self; + int argc; + VALUE *argv; + int ex; +{ + int i; + + for (i=0; i<argc; i++) { + rb_export_method(self, rb_to_id(argv[i]), ex); + } +} + +static VALUE +mod_public(argc, argv, module) + int argc; + VALUE *argv; + VALUE module; +{ + set_method_visibility(module, argc, argv, NOEX_PUBLIC); + return module; +} + +static VALUE +mod_private(argc, argv, module) + int argc; + VALUE *argv; + VALUE module; +{ + set_method_visibility(module, argc, argv, NOEX_PRIVATE); + return module; +} + +static VALUE +mod_public_method(argc, argv, obj) + int argc; + VALUE *argv; + VALUE obj; +{ + set_method_visibility(CLASS_OF(obj), argc, argv, NOEX_PUBLIC); + return obj; +} + +static VALUE +mod_private_method(argc, argv, obj) + int argc; + VALUE *argv; + VALUE obj; +{ + set_method_visibility(CLASS_OF(obj), argc, argv, NOEX_PRIVATE); + return obj; +} + +static VALUE +mod_modfunc(argc, argv, module) + int argc; + VALUE *argv; + VALUE module; +{ + int i; + ID id; + NODE *body; + + rb_clear_cache(); + set_method_visibility(module, argc, argv, NOEX_PRIVATE); + for (i=0; i<argc; i++) { + id = rb_to_id(argv[i]); + body = search_method(module, id, 0); + if (body == 0 || body->nd_body == 0) { + NameError("undefined method `%s' for module `%s'", + rb_id2name(id), rb_class2name(module)); + } + rb_add_method(rb_singleton_class(module), id, body->nd_body, NOEX_PUBLIC); + } + return module; +} + +static VALUE +mod_include(argc, argv, module) + int argc; + VALUE *argv; + VALUE module; +{ + int i; + + for (i=0; i<argc; i++) { + Check_Type(argv[i], T_MODULE); + rb_include_module(module, argv[i]); + } + return module; +} + +VALUE +class_s_new(argc, argv, class) + int argc; + VALUE *argv; + VALUE class; +{ + VALUE obj = obj_alloc(class); + + if (FL_TEST(class, FL_SINGLETON)) { + TypeError("can't create instance of virtual class"); + } + obj = obj_alloc(class); + PUSH_ITER(iterator_p()?ITER_PRE:ITER_NOT); + rb_funcall2(obj, init, argc, argv); + POP_ITER(); + return obj; +} + + +VALUE +class_new_instance(argc, argv, class) + int argc; + VALUE *argv; + VALUE class; +{ + VALUE obj; + + if (FL_TEST(class, FL_SINGLETON)) { + TypeError("can't create instance of virtual class"); + } + obj = obj_alloc(class); + PUSH_ITER(iterator_p()?ITER_PRE:ITER_NOT); + rb_funcall2(obj, init, argc, argv); + POP_ITER(); + return obj; +} + +static VALUE +top_include(argc, argv) + int argc; + VALUE *argv; +{ + rb_secure(4); + return mod_include(argc, argv, cObject); +} + +void +rb_extend_object(obj, module) + VALUE obj, module; +{ + rb_include_module(rb_singleton_class(obj), module); +} + +static VALUE +mod_extend_object(mod, obj) + VALUE mod, obj; +{ + rb_extend_object(obj, mod); + return obj; +} + +static VALUE +obj_extend(argc, argv, obj) + int argc; + VALUE *argv; + VALUE obj; +{ + int i; + + for (i=0; i<argc; i++) Check_Type(argv[i], T_MODULE); + for (i=0; i<argc; i++) { + rb_funcall(argv[i], rb_intern("extend_object"), 1, obj); + } + return obj; +} + +VALUE f_trace_var(); +VALUE f_untrace_var(); + +extern void rb_str_setter(); + +static void +errat_setter(val, id, var) + VALUE val; + ID id; + VALUE *var; +{ + int i; + static char *err = "value of $@ must be Array of String"; + + if (!NIL_P(val)) { + if (TYPE(val) != T_ARRAY) { + TypeError(err); + } + for (i=0;i<RARRAY(val)->len;i++) { + if (TYPE(RARRAY(val)->ptr[i]) != T_STRING) { + TypeError(err); + } + } + } + *var = val; +} + +static VALUE +f_catch(dmy, tag) + VALUE dmy, tag; +{ + NODE *state; + ID t; + VALUE val; + + t = rb_to_id(tag); + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + val = rb_yield(tag); + } + POP_TAG(); + if (state) { + if (state->nd_tag == TAG_THROW && state->nd_tlev == t) { + return state->nd_tval; + } + JUMP_TAG(state); + } + return val; +} + +static VALUE +f_throw(argc, argv) + int argc; + VALUE *argv; +{ + VALUE tag, value; + ID t; + + rb_scan_args(argc, argv, "11", &tag, &value); + t = rb_to_id(tag); + JUMP_TAG3(TAG_THROW, value, t); + /* not reached */ +} + +void +Init_eval() +{ + init = rb_intern("initialize"); + eqq = rb_intern("==="); + each = rb_intern("each"); + + aref = rb_intern("[]"); + aset = rb_intern("[]="); + + rb_global_variable(&top_scope); + rb_global_variable(&eval_tree); + rb_global_variable(&the_dyna_vars); + + rb_define_hooked_variable("$@", &errat, 0, errat_setter); + rb_define_hooked_variable("$!", &errinfo, 0, rb_str_setter); + + rb_define_global_function("eval", f_eval, -1); + rb_define_global_function("iterator?", f_iterator_p, 0); + rb_define_global_function("method_missing", f_missing, -1); + rb_define_global_function("loop", f_loop, 0); + + rb_define_method(mKernel, "respond_to?", obj_respond_to, -1); + + rb_define_global_function("break", f_break, 0); + rb_define_alias(mKernel, "break!", "break"); + rb_define_global_function("next", f_next, 0); + rb_define_alias(mKernel, "next!", "next"); + rb_define_alias(mKernel, "continue", "next"); + rb_define_global_function("redo", f_redo, 0); + rb_define_alias(mKernel, "redo!", "redo"); + rb_define_global_function("retry", f_retry, 0); + rb_define_alias(mKernel, "retry!", "retry"); + rb_define_global_function("raise", f_raise, -1); + rb_define_alias(mKernel, "fail", "raise"); + + rb_define_global_function("caller", f_caller, -1); + + rb_define_global_function("exit", f_exit, -1); + rb_define_global_function("abort", f_abort, 0); + + rb_define_global_function("catch", f_catch, 1); + rb_define_global_function("throw", f_throw, -1); + + rb_define_method(mKernel, "send", f_send, -1); + + rb_define_private_method(cModule, "include", mod_include, -1); + rb_define_private_method(cModule, "public", mod_public, -1); + rb_define_private_method(cModule, "private", mod_private, -1); + rb_define_private_method(cModule, "module_function", mod_modfunc, -1); + rb_define_method(cModule, "method_defined?", mod_method_defined, 1); + rb_define_method(cModule, "extend_object", mod_extend_object, 1); + rb_define_method(cModule, "public_class_method", mod_public_method, -1); + rb_define_method(cModule, "private_class_method", mod_private_method, -1); + + rb_define_method(CLASS_OF(TopSelf), "include", top_include, -1); + rb_define_method(mKernel, "extend", obj_extend, -1); + + rb_define_global_function("trace_var", f_trace_var, -1); + rb_define_global_function("untrace_var", f_untrace_var, -1); + + rb_define_global_function("set_trace_func", set_trace_func, 1); + + rb_define_virtual_variable("$SAFE", safe_getter, safe_setter); +} + +VALUE f_autoload(); + +void +Init_load() +{ + rb_load_path = ary_new(); + rb_define_readonly_variable("$:", &rb_load_path); + rb_define_readonly_variable("$-I", &rb_load_path); + rb_define_readonly_variable("$LOAD_PATH", &rb_load_path); + + rb_features = ary_new(); + rb_define_readonly_variable("$\"", &rb_features); + + rb_define_global_function("load", f_load, 1); + rb_define_global_function("require", f_require, 1); + rb_define_global_function("autoload", f_autoload, 2); +} + +static void +scope_dup(scope) + struct SCOPE *scope; +{ + ID *tbl; + VALUE *vars; + + if (scope->flag & SCOPE_MALLOC) return; + + if (scope->local_tbl) { + tbl = scope->local_tbl; + vars = ALLOC_N(VALUE, tbl[0]+1); + *vars++ = scope->local_vars[-1]; + MEMCPY(vars, scope->local_vars, VALUE, tbl[0]); + scope->local_vars = vars; + scope->flag = SCOPE_MALLOC; + } + else { + scope->flag = SCOPE_NOSTACK; + } +} + +static void +blk_mark(data) + struct BLOCK *data; +{ + gc_mark_frame(&data->frame); + gc_mark(data->scope); + gc_mark(data->var); + gc_mark(data->body); + gc_mark(data->self); + gc_mark(data->d_vars); +} + +static void +blk_free(data) + struct BLOCK *data; +{ + free(data->frame.argv); +} + +static VALUE +f_binding(self) + VALUE self; +{ + struct BLOCK *data; + VALUE bind; + + PUSH_BLOCK(0,0); + bind = Data_Make_Struct(cData, struct BLOCK, blk_mark, blk_free, data); + MEMCPY(data, the_block, struct BLOCK, 1); + + data->iter = f_iterator_p(); + data->frame.last_func = 0; + data->frame.argv = ALLOC_N(VALUE, data->frame.argc); + MEMCPY(data->frame.argv, the_block->frame.argv, VALUE, data->frame.argc); + + scope_dup(data->scope); + POP_BLOCK(); + + return bind; +} + +#define PROC_TAINT FL_USER0 +#define PROC_T3 FL_USER1 +#define PROC_T4 FL_USER2 +#define PROC_T5 (FL_USER1|FL_USER2) +#define PROC_TMASK (FL_USER1|FL_USER2) + +static VALUE +proc_s_new(class) + VALUE class; +{ + VALUE proc; + struct BLOCK *data; + + if (!iterator_p() && !f_iterator_p()) { + ArgError("tryed to create Procedure-Object out of iterator"); + } + + proc = Data_Make_Struct(class, struct BLOCK, blk_mark, blk_free, data); + *data = *the_block; + +#ifdef THREAD + data->orig_thread = thread_current(); +#endif + data->iter = f_iterator_p(); + data->frame.argv = ALLOC_N(VALUE, data->frame.argc); + MEMCPY(data->frame.argv, the_block->frame.argv, VALUE, data->frame.argc); + + scope_dup(data->scope); + if (safe_level >= 3) { + FL_SET(proc, PROC_TAINT); + switch (safe_level) { + case 3: + FL_SET(proc, PROC_T3); + break; + case 4: + FL_SET(proc, PROC_T4); + break; + case 5: + FL_SET(proc, PROC_T5); + break; + } + } + + return proc; +} + +VALUE +f_lambda() +{ + return proc_s_new(cProc); +} + +static VALUE +proc_call(proc, args) + VALUE proc, args; +{ + struct BLOCK *data; + volatile VALUE result = Qnil; + NODE *state; + int tag_level; + volatile int orphan; + volatile int safe = safe_level; + + if (TYPE(args) == T_ARRAY) { + switch (RARRAY(args)->len) { + case 0: + args = 0; + break; + case 1: + args = RARRAY(args)->ptr[0]; + break; + } + } + + Data_Get_Struct(proc, struct BLOCK, data); + + if (data->scope && (data->scope->flag & SCOPE_NOSTACK)) { + orphan = 1; + } + else { +#ifdef THREAD + if (data->orig_thread != thread_current()) { + orphan = 1; + } + else +#endif + orphan = 0; + } + if (orphan) {/* orphan procedure */ + if (iterator_p()) { + data->frame.iter = ITER_CUR; + } + else { + data->frame.iter = ITER_NOT; + } + } + + /* PUSH BLOCK from data */ + PUSH_BLOCK2(data); + PUSH_ITER(ITER_CUR); + the_frame->iter = ITER_CUR; + if (FL_TEST(proc, PROC_TAINT)) { + switch (RBASIC(proc)->flags & PROC_TMASK) { + case PROC_T3: + safe_level = 3; + break; + case PROC_T4: + safe_level = 4; + break; + case PROC_T5: + safe_level = 5; + break; + } + } + + PUSH_TAG(); + state = EXEC_TAG(); + if (state == 0) { + result = rb_yield(args); + } + POP_TAG(); + + POP_ITER(); + tag_level = the_block->level; + POP_BLOCK(); + safe_level = safe; + + if (state) { + if (orphan) {/* orphan procedure */ + switch (state->nd_tag) { + case TAG_BREAK: /* never happen */ + case IN_BLOCK|TAG_BREAK: + if (state->nd_tlev == tag_level) + Raise(eLocalJumpError, "break from proc-closure"); + break; + case TAG_RETRY: + Raise(eLocalJumpError, "retry from proc-closure"); + break; + case TAG_RETURN: /* never happen */ + case IN_BLOCK|TAG_RETURN: + if (state->nd_tlev == tag_level) + Raise(eLocalJumpError, "return from proc-closure"); + break; + } + } + else if (state->nd_tlev == tag_level) { + state->nd_tag &= ~IN_BLOCK; + } + JUMP_TAG(state); + } + return result; +} + +void +Init_Proc() +{ + eLocalJumpError = rb_define_class("LocalJumpError", eException); + + cProc = rb_define_class("Proc", cObject); + rb_define_singleton_method(cProc, "new", proc_s_new, 0); + + rb_define_method(cProc, "call", proc_call, -2); + rb_define_global_function("proc", f_lambda, 0); + rb_define_global_function("lambda", f_lambda, 0); + rb_define_global_function("binding", f_binding, 0); +} + +#ifdef THREAD + +static VALUE eThreadError; + +int thread_pending = 0; + +static VALUE cThread; + +#include <sys/types.h> +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#else +#ifndef NT +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; +#endif /* NT */ +#endif +#include <signal.h> +#include <errno.h> + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + +extern VALUE last_status; + +enum thread_status { + THREAD_RUNNABLE, + THREAD_STOPPED, + THREAD_TO_KILL, + THREAD_KILLED, +}; + +#define WAIT_FD (1<<0) +#define WAIT_TIME (1<<1) +#define WAIT_JOIN (1<<2) + +/* +infty, for this purpose */ +#define DELAY_INFTY 1E30 + +typedef struct thread * thread_t; + +struct thread { + struct thread *next, *prev; + jmp_buf context; + + VALUE result; + + int stk_len; + int stk_max; + VALUE*stk_ptr; + VALUE*stk_pos; + + struct FRAME *frame; + struct SCOPE *scope; + struct RClass *class; + struct RVarmap *dyna_vars; + struct BLOCK *block; + struct iter *iter; + struct tag *tag; + + VALUE trace; + + char *file; + int line; + + VALUE errat, errinfo; + VALUE last_status; + VALUE last_line; + VALUE last_match; + + int safe; + + enum thread_status status; + int wait_for; + int fd; + double delay; + thread_t join; + + int abort; + + VALUE thread; +}; + +static thread_t curr_thread; +static int num_waiting_on_fd; +static int num_waiting_on_timer; +static int num_waiting_on_join; + +#define FOREACH_THREAD_FROM(f,x) x = f; do { x = x->next; +#define END_FOREACH_FROM(f,x) } while (x != f) + +#define FOREACH_THREAD(x) FOREACH_THREAD_FROM(curr_thread,x) +#define END_FOREACH(x) END_FOREACH_FROM(curr_thread,x) + +/* Return the current time as a floating-point number */ +static double +timeofday() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (double)tv.tv_sec + (double)tv.tv_usec * 1e-6; +} + +static thread_t main_thread; + +#define ADJ(addr) (void*)(((VALUE*)(addr)-th->stk_pos)+th->stk_ptr) +#define STACK(addr) (th->stk_pos<(addr) && (addr)<th->stk_pos+th->stk_len) + +static void +thread_mark(th) + thread_t th; +{ + struct FRAME *frame; + struct BLOCK *block; + + gc_mark(th->result); + if (th->stk_ptr) { + gc_mark_locations(th->stk_ptr, th->stk_ptr+th->stk_len); +#if defined(THINK_C) || defined(__human68k__) + gc_mark_locations(th->stk_ptr+2, th->stk_ptr+th->stk_len+2); +#endif + } + gc_mark(th->thread); + if (th->join) gc_mark(th->join->thread); + + gc_mark(th->scope); + gc_mark(th->dyna_vars); + gc_mark(th->errat); + gc_mark(th->errinfo); + gc_mark(th->last_line); + gc_mark(th->last_match); + + /* mark data in copied stack */ + frame = th->frame; + while (frame && frame != top_frame) { + frame = ADJ(frame); + if (frame->argv && !STACK(frame->argv)) { + gc_mark_frame(frame); + } + frame = frame->prev; + } + block = th->block; + while (block) { + block = ADJ(block); + if (block->frame.argv && !STACK(block->frame.argv)) { + gc_mark_frame(&block->frame); + } + block = block->prev; + } +} + +void +gc_mark_threads() +{ + thread_t th; + + FOREACH_THREAD(th) { + thread_mark(th); + } END_FOREACH(th); +} + +static void +thread_free(th) + thread_t th; +{ + if (th->stk_ptr) free(th->stk_ptr); + th->stk_ptr = 0; +} + +static thread_t +thread_check(data) + struct RData *data; +{ + if (TYPE(data) != T_DATA || data->dfree != thread_free) { + TypeError("wrong argument type %s (expected Thread)", + rb_class2name(CLASS_OF(data))); + } + return (thread_t)data->data; +} + +VALUE lastline_get(); +void lastline_set(); +VALUE backref_get(); +void backref_set(); + +static void +thread_save_context(th) + thread_t th; +{ + VALUE v; + + th->stk_len = stack_length(); + th->stk_pos = (gc_stack_start<(VALUE*)&v)?gc_stack_start + :gc_stack_start - th->stk_len; + if (th->stk_len > th->stk_max) { + th->stk_max = th->stk_len; + REALLOC_N(th->stk_ptr, VALUE, th->stk_max); + } + FLUSH_REGISTER_WINDOWS; + MEMCPY(th->stk_ptr, th->stk_pos, VALUE, th->stk_len); + + th->frame = the_frame; + th->scope = the_scope; + th->class = the_class; + th->dyna_vars = the_dyna_vars; + th->block = the_block; + th->iter = the_iter; + th->tag = prot_tag; + th->errat = errat; + th->errinfo = errinfo; + th->last_status = last_status; + th->last_line = lastline_get(); + th->last_match = backref_get(); + th->safe = safe_level; + + th->trace = trace_func; + th->file = sourcefile; + th->line = sourceline; +} + +static void thread_restore_context(); + +static void +stack_extend(th, exit) + thread_t th; + int exit; +{ + VALUE space[1024]; + + memset(space, 0, 1); /* prevent array from optimization */ + thread_restore_context(th, exit); +} + +static int th_raise_argc; +static VALUE th_raise_argv[2]; +static char *th_raise_file; +static int th_raise_line; + +static void +thread_restore_context(th, exit) + thread_t th; + int exit; +{ + VALUE v; + static thread_t tmp; + static int ex; + + if (!th->stk_ptr) Bug("unsaved context"); + + if (&v < gc_stack_start) { + /* Stack grows downward */ + if (&v > th->stk_pos) stack_extend(th, exit); + } + else { + /* Stack grows upward */ + if (&v < th->stk_pos + th->stk_len) stack_extend(th, exit); + } + + the_frame = th->frame; + the_scope = th->scope; + the_class = th->class; + the_dyna_vars = th->dyna_vars; + the_block = th->block; + the_iter = th->iter; + prot_tag = th->tag; + the_class = th->class; + errat = th->errat; + errinfo = th->errinfo; + last_status = th->last_status; + safe_level = th->safe; + + trace_func = th->trace; + sourcefile = th->file; + sourceline = th->line; + + tmp = th; + ex = exit; + FLUSH_REGISTER_WINDOWS; + MEMCPY(tmp->stk_pos, tmp->stk_ptr, VALUE, tmp->stk_len); + + lastline_set(tmp->last_line); + backref_set(tmp->last_match); + + switch (ex) { + case 1: + JUMP_TAG2(TAG_FATAL, INT2FIX(0)); + break; + + case 2: + rb_interrupt(); + break; + + case 3: + the_frame->last_func = 0; + sourcefile = th_raise_file; + sourceline = th_raise_line; + f_raise(th_raise_argc, th_raise_argv); + break; + + default: + longjmp(tmp->context, 1); + } +} + +static void +thread_ready(th) + thread_t th; +{ + /* The thread is no longer waiting on anything */ + if (th->wait_for & WAIT_FD) { + num_waiting_on_fd--; + } + if (th->wait_for & WAIT_TIME) { + num_waiting_on_timer--; + } + if (th->wait_for & WAIT_JOIN) { + num_waiting_on_join--; + } + th->wait_for = 0; + th->status = THREAD_RUNNABLE; +} + +static void +thread_remove() +{ + thread_ready(curr_thread); + curr_thread->status = THREAD_KILLED; + curr_thread->prev->next = curr_thread->next; + curr_thread->next->prev = curr_thread->prev; + thread_schedule(); +} + +static int +thread_dead(th) + thread_t th; +{ + return th->status == THREAD_KILLED; +} + +static void +thread_deadlock() +{ + curr_thread = main_thread; + th_raise_argc = 1; + th_raise_argv[0] = exc_new2(eFatal, "Thread: deadlock"); + th_raise_file = sourcefile; + th_raise_line = sourceline; + f_abort(); +} + +void +thread_schedule() +{ + thread_t next; + thread_t th; + thread_t curr; + + select_err: + thread_pending = 0; + if (curr_thread == curr_thread->next) return; + + next = 0; + curr = curr_thread; /* starting thread */ + + while (curr->status == THREAD_KILLED) { + curr = curr->prev; + } + + FOREACH_THREAD_FROM(curr,th) { + if (th->status != THREAD_STOPPED && th->status != THREAD_KILLED) { + next = th; + break; + } + } + END_FOREACH_FROM(curr,th); + + if (num_waiting_on_join) { + curr_thread->file = sourcefile; + curr_thread->line = sourceline; + FOREACH_THREAD_FROM(curr,th) { + if ((th->wait_for & WAIT_JOIN) && thread_dead(th->join)) { + th->join = 0; + th->wait_for &= ~WAIT_JOIN; + th->status = THREAD_RUNNABLE; + num_waiting_on_join--; + if (!next) next = th; + } + } + END_FOREACH_FROM(curr,th); + } + + if (num_waiting_on_fd > 0 || num_waiting_on_timer > 0) { + fd_set readfds; + struct timeval delay_tv, *delay_ptr; + double delay, now; + + int n, max; + + do { + max = 0; + FD_ZERO(&readfds); + if (num_waiting_on_fd > 0) { + FOREACH_THREAD_FROM(curr,th) { + if (th->wait_for & WAIT_FD) { + FD_SET(th->fd, &readfds); + if (th->fd > max) max = th->fd; + } + } + END_FOREACH_FROM(curr,th); + } + + delay = DELAY_INFTY; + if (num_waiting_on_timer > 0) { + now = timeofday(); + FOREACH_THREAD_FROM(curr,th) { + if (th->wait_for & WAIT_TIME) { + if (th->delay <= now) { + th->delay = 0.0; + th->wait_for &= ~WAIT_TIME; + th->status = THREAD_RUNNABLE; + num_waiting_on_timer--; + next = th; + } else if (th->delay < delay) { + delay = th->delay; + } + } + } + END_FOREACH_FROM(curr,th); + } + /* Do the select if needed */ + if (num_waiting_on_fd > 0 || !next) { + /* Convert delay to a timeval */ + /* If a thread is runnable, just poll */ + if (next) { + delay_tv.tv_sec = 0; + delay_tv.tv_usec = 0; + delay_ptr = &delay_tv; + } + else if (delay == DELAY_INFTY) { + delay_ptr = 0; + } + else { + delay -= now; + delay_tv.tv_sec = (unsigned int)delay; + delay_tv.tv_usec = (delay - (double)delay_tv.tv_sec) * 1e6; + delay_ptr = &delay_tv; + } + + n = select(max+1, &readfds, 0, 0, delay_ptr); + if (n < 0) { + if (trap_pending) rb_trap_exec(); + goto select_err; + } + if (n > 0) { + /* Some descriptors are ready. + Make the corresponding threads runnable. */ + FOREACH_THREAD_FROM(curr,th) { + if ((th->wait_for&WAIT_FD) + && FD_ISSET(th->fd, &readfds)) { + /* Wake up only one thread per fd. */ + FD_CLR(th->fd, &readfds); + th->status = THREAD_RUNNABLE; + th->fd = 0; + th->wait_for &= ~WAIT_FD; + num_waiting_on_fd--; + if (!next) next = th; /* Found one. */ + } + } + END_FOREACH_FROM(curr,th); + } + } + /* The delays for some of the threads should have expired. + Go through the loop once more, to check the delays. */ + } while (!next && delay != DELAY_INFTY); + } + + if (!next) { + FOREACH_THREAD_FROM(curr,th) { + fprintf(stderr, "%s:%d:deadlock 0x%x: %d:%d %s\n", + th->file, th->line, th->thread, th->status, + th->wait_for, th==main_thread?"(main)":""); + } + END_FOREACH_FROM(curr,th); + /* raise fatal error to main thread */ + thread_deadlock(); + } + if (next == curr_thread) { + return; + } + + /* context switch */ + if (curr == curr_thread) { + thread_save_context(curr); + if (setjmp(curr->context)) { + return; + } + } + + curr_thread = next; + if (next->status == THREAD_TO_KILL) { + /* execute ensure-clause if any */ + thread_restore_context(next, 1); + } + thread_restore_context(next, 0); +} + +void +thread_wait_fd(fd) + int fd; +{ + if (curr_thread == curr_thread->next) return; + + curr_thread->status = THREAD_STOPPED; + curr_thread->fd = fd; + num_waiting_on_fd++; + curr_thread->wait_for |= WAIT_FD; + thread_schedule(); +} + +void +thread_fd_writable(fd) + int fd; +{ + struct timeval zero; + fd_set fds; + + if (curr_thread == curr_thread->next) return; + + zero.tv_sec = zero.tv_usec = 0; + for (;;) { + FD_ZERO(&fds); + FD_SET(fd, &fds); + if (select(fd+1, 0, &fds, 0, &zero) == 1) break; + thread_schedule(); + } +} + +void +thread_wait_for(time) + struct timeval time; +{ + double date; + + if (curr_thread == curr_thread->next) { + int n; +#ifndef linux + double d, limit; + limit = timeofday()+(double)time.tv_sec+(double)time.tv_usec*1e-6; +#endif + for (;;) { + TRAP_BEG; + n = select(0, 0, 0, 0, &time); + TRAP_END; + if (n == 0) return; + +#ifndef linux + d = limit - timeofday(); + + time.tv_sec = (int)d; + time.tv_usec = (int)((d - (int)d)*1e6); + if (time.tv_usec < 0) { + time.tv_usec += 1e6; + time.tv_sec -= 1; + } + if (time.tv_sec < 0) return; +#endif + } + } + + date = timeofday() + (double)time.tv_sec + (double)time.tv_usec*1e-6; + curr_thread->status = THREAD_STOPPED; + curr_thread->delay = date; + num_waiting_on_timer++; + curr_thread->wait_for |= WAIT_TIME; + thread_schedule(); +} + +void thread_sleep_forever(); + +int +thread_alone() +{ + return curr_thread == curr_thread->next; +} + +int +thread_select(max, read, write, except, timeout) + int max; + fd_set *read, *write, *except; + struct timeval *timeout; +{ + double limit; + struct timeval zero; + fd_set r, *rp, w, *wp, x, *xp; + int n; + + if (!read && !write && !except) { + if (!timeout) { + thread_sleep_forever(); + return 0; + } + thread_wait_for(*timeout); + return 0; + } + + if (timeout) { + limit = timeofday()+ + (double)timeout->tv_sec+(double)timeout->tv_usec*1e-6; + } + + if (curr_thread == curr_thread->next) { /* no other thread */ +#ifndef linux + struct timeval tv, *tvp = timeout; + + if (timeout) { + tv = *timeout; + tvp = &tv; + } + for (;;) { + TRAP_BEG; + n = select(max, read, write, except, tvp); + TRAP_END; + if (n < 0 && errno == EINTR) { + if (timeout) { + double d = timeofday() - limit; + + tv.tv_sec = (unsigned int)d; + tv.tv_usec = (d - (double)tv.tv_sec) * 1e6; + } + continue; + } + return n; + } +#else + for (;;) { + TRAP_BEG; + n = select(max, read, write, except, timeout); + TRAP_END; + if (n < 0 && errno == EINTR) { + continue; + } + return n; + } +#endif + + } + + for (;;) { + zero.tv_sec = zero.tv_usec = 0; + if (read) {rp = &r; r = *read;} else {rp = 0;} + if (write) {wp = &w; w = *write;} else {wp = 0;} + if (except) {xp = &x; x = *except;} else {xp = 0;} + n = select(max, rp, wp, xp, &zero); + if (n > 0) { + /* write back fds */ + if (read) {*read = r;} + if (write) {*write = w;} + if (except) {*except = x;} + return n; + } + if (n < 0 && errno != EINTR) { + return n; + } + if (timeout) { + if (timeout->tv_sec == 0 && timeout->tv_usec == 0) return 0; + if (limit <= timeofday()) return 0; + } + + thread_schedule(); + CHECK_INTS; + } +} + +static VALUE +thread_join(dmy, thread) + VALUE dmy; + VALUE thread; +{ + thread_t th = thread_check(thread); + + if (thread_dead(th)) return thread; + if ((th->wait_for & WAIT_JOIN) && th->join == curr_thread) + Raise(eThreadError, "Thread.join: deadlock"); + curr_thread->status = THREAD_STOPPED; + curr_thread->join = th; + num_waiting_on_join++; + curr_thread->wait_for |= WAIT_JOIN; + thread_schedule(); + + return thread; +} + +static VALUE +thread_current() +{ + return curr_thread->thread; +} + +static VALUE +thread_main() +{ + return main_thread->thread; +} + +static VALUE +thread_wakeup(thread) + VALUE thread; +{ + thread_t th = thread_check(thread); + + if (th->status == THREAD_KILLED) Raise(eThreadError, "killed thread"); + thread_ready(th); + + return thread; +} + +static VALUE +thread_run(thread) + VALUE thread; +{ + thread_wakeup(thread); + if (!thread_critical) thread_schedule(); + + return thread; +} + +static VALUE +thread_kill(thread) + VALUE thread; +{ + thread_t th = thread_check(thread); + + if (th->status == THREAD_TO_KILL || th->status == THREAD_KILLED) + return thread; + if (th == th->next || th == main_thread) rb_exit(0); + + thread_ready(th); + th->status = THREAD_TO_KILL; + thread_schedule(); + return Qnil; /* not reached */ +} + +static VALUE +thread_s_kill(obj, th) + VALUE obj, th; +{ + return thread_kill(th); +} + +static VALUE +thread_exit() +{ + return thread_kill(curr_thread->thread); +} + +static VALUE +thread_pass() +{ + thread_schedule(); + return Qnil; +} + +static VALUE +thread_stop_method(thread) + VALUE thread; +{ + thread_t th = thread_check(thread); + + thread_critical = 0; + th->status = THREAD_STOPPED; + thread_schedule(); + + return thread; +} + +static VALUE +thread_stop() +{ + thread_stop_method(curr_thread->thread); + return Qnil; +} + +void +thread_sleep(sec) + int sec; +{ + if (curr_thread == curr_thread->next) { + TRAP_BEG; + sleep(sec); + TRAP_END; + return; + } + thread_wait_for(time_timeval(INT2FIX(sec))); +} + +void +thread_sleep_forever() +{ + if (curr_thread == curr_thread->next) { + TRAP_BEG; + sleep((32767<<16)+32767); + TRAP_END; + return; + } + + num_waiting_on_timer++; + curr_thread->delay = DELAY_INFTY; + curr_thread->wait_for |= WAIT_TIME; + curr_thread->status = THREAD_STOPPED; + thread_schedule(); +} + +static int thread_abort; + +static VALUE +thread_s_abort_exc() +{ + return thread_abort?TRUE:FALSE; +} + +static VALUE +thread_s_abort_exc_set(self, val) + VALUE self, val; +{ + thread_abort = RTEST(val); + return val; +} + +static VALUE +thread_abort_exc(thread) + VALUE thread; +{ + thread_t th = thread_check(thread); + + return th->abort?TRUE:FALSE; +} + +static VALUE +thread_abort_exc_set(thread, val) + VALUE thread, val; +{ + thread_t th = thread_check(thread); + + th->abort = RTEST(val); + return val; +} + +static thread_t +thread_alloc() +{ + thread_t th; + + th = ALLOC(struct thread); + th->status = THREAD_RUNNABLE; + + th->status = 0; + th->result = 0; + th->errinfo = Qnil; + th->errat = Qnil; + + th->stk_ptr = 0; + th->stk_len = 0; + th->stk_max = 0; + th->wait_for = 0; + th->fd = 0; + th->delay = 0.0; + th->join = 0; + + th->frame = 0; + th->scope = 0; + th->class = 0; + th->dyna_vars = 0; + th->block = 0; + th->iter = 0; + th->tag = 0; + th->errat = 0; + th->errinfo = 0; + th->last_status = 0; + th->last_line = 0; + th->last_match = 0; + th->abort = 0; + + th->thread = data_object_alloc(cThread, th, 0, thread_free); + + if (curr_thread) { + th->prev = curr_thread; + curr_thread->next->prev = th; + th->next = curr_thread->next; + curr_thread->next = th; + } + else { + curr_thread = th->prev = th->next = th; + th->status = THREAD_RUNNABLE; + } + + return th; +} + +#if defined(HAVE_SETITIMER) && !defined(__BOW__) +static void +catch_timer(sig) + int sig; +{ +#if !defined(POSIX_SIGNAL) && !defined(BSD_SIGNAL) + signal(sig, catch_timer); +#endif + if (!thread_critical) { + if (trap_immediate) { + trap_immediate = 0; + thread_schedule(); + } + else thread_pending = 1; + } +} +#else +int thread_tick = THREAD_TICK; +#endif + +VALUE +thread_create(fn, arg) + VALUE (*fn)(); + void *arg; +{ + thread_t th = thread_alloc(); + NODE *state; + +#if defined(HAVE_SETITIMER) && !defined(__BOW__) + static init = 0; + + if (!init) { + struct itimerval tval; + +#ifdef POSIX_SIGNAL + posix_signal(SIGVTALRM, catch_timer); +#else + signal(SIGVTALRM, catch_timer); +#endif + + tval.it_interval.tv_sec = 0; + tval.it_interval.tv_usec = 100000; + tval.it_value = tval.it_interval; + setitimer(ITIMER_VIRTUAL, &tval, NULL); + + init = 1; + } +#endif + + thread_save_context(curr_thread); + if (setjmp(curr_thread->context)) { + return th->thread; + } + + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + thread_save_context(th); + if (setjmp(th->context) == 0) { + curr_thread = th; + th->result = (*fn)(arg, th); + } + } + POP_TAG(); + if (state) { + if (state->nd_tag == TAG_THROW) { + char *mesg; + char *tag = rb_id2name(state->nd_tlev); + + mesg = ALLOCA_N(char, strlen(tag) + 64); + + sprintf(mesg, "uncaught throw `%s' in thread 0x%x\n", + tag, th->thread); + curr_thread->errinfo = exc_new2(eThreadError, mesg); + curr_thread->errat = make_backtrace(); + } + else if (th->status != THREAD_TO_KILL && !NIL_P(errinfo)) { + if (state->nd_tag == TAG_FATAL || + obj_is_kind_of(errinfo, eSystemExit)) { + /* fatal error or global exit within this thread */ + /* need to stop whole script */ + main_thread->errat = errat; + main_thread->errinfo = errinfo; + thread_cleanup(); + } + else if (thread_abort || curr_thread->abort) { + f_abort(); + } + else { + curr_thread->errat = errat; + curr_thread->errinfo = errinfo; + } + } + } + thread_remove(); + return 0; +} + +static void +thread_yield(arg, th) + int arg; + thread_t th; +{ + scope_dup(the_block->scope); + rb_yield(th->thread); +} + +static VALUE +thread_start() +{ + if (!iterator_p()) { + Raise(eThreadError, "must be called as iterator"); + } + return thread_create(thread_yield, 0); +} + +static VALUE +thread_value(thread) + VALUE thread; +{ + thread_t th = thread_check(thread); + + thread_join(0, thread); + if (!NIL_P(th->errinfo)) { + errat = make_backtrace(); + ary_unshift(errat, ary_entry(th->errat, 0)); + sourcefile = 0; /* kludge to print errat */ + rb_raise(th->errinfo); + } + + return th->result; +} + +static VALUE +thread_status(thread) + VALUE thread; +{ + thread_t th = thread_check(thread); + + if (thread_dead(th)) { + if (NIL_P(th->errinfo)) return FALSE; + return Qnil; + } + + return TRUE; +} + +static VALUE +thread_stopped(thread) + VALUE thread; +{ + thread_t th = thread_check(thread); + + if (thread_dead(th)) return TRUE; + if (th->status == THREAD_STOPPED) return TRUE; + return FALSE; +} + +static void +thread_wait_other_threads() +{ + /* wait other threads to terminate */ + while (curr_thread != curr_thread->next) { + thread_schedule(); + } +} + +static void +thread_cleanup() +{ + thread_t th; + + if (curr_thread != curr_thread->next->prev) { + curr_thread = curr_thread->prev; + } + + FOREACH_THREAD(th) { + if (th != curr_thread && th->status != THREAD_KILLED) { + th->status = THREAD_TO_KILL; + th->wait_for = 0; + } + } + END_FOREACH(th); +} + +int thread_critical; + +static VALUE +thread_get_critical() +{ + return thread_critical?TRUE:FALSE; +} + +static VALUE +thread_set_critical(obj, val) + VALUE obj, val; +{ + thread_critical = RTEST(val); + return val; +} + +void +thread_interrupt() +{ + thread_critical = 0; + thread_ready(main_thread); + if (curr_thread == main_thread) { + rb_interrupt(); + } + thread_save_context(curr_thread); + if (setjmp(curr_thread->context)) { + return; + } + curr_thread = main_thread; + thread_restore_context(curr_thread, 2); +} + +static VALUE +thread_raise(argc, argv, thread) + int argc; + VALUE *argv; + VALUE thread; +{ + thread_t th = thread_check(thread); + + if (thread_dead(th)) return thread; + if (curr_thread == th) { + f_raise(argc, argv); + } + + thread_save_context(curr_thread); + if (setjmp(curr_thread->context)) { + return thread; + } + + rb_scan_args(argc, argv, "11", &th_raise_argv[0], &th_raise_argv[1]); + thread_ready(th); + curr_thread = th; + + th_raise_argc = argc; + th_raise_file = sourcefile; + th_raise_line = sourceline; + thread_restore_context(curr_thread, 3); + return Qnil; /* not reached */ +} + +static thread_t loading_thread; +static int loading_nest; + +static int +thread_loading(feature) + char *feature; +{ + if (curr_thread != curr_thread->next && loading_thread) { + while (loading_thread != curr_thread) { + thread_schedule(); + CHECK_INTS; + } + if (rb_provided(feature)) return TRUE; /* no need to load */ + } + + loading_thread = curr_thread; + loading_nest++; + + return FALSE; +} + +static void +thread_loading_done() +{ + if (--loading_nest == 0) { + loading_thread = 0; + } +} + +void +Init_Thread() +{ + eThreadError = rb_define_class("ThreadError", eException); + cThread = rb_define_class("Thread", cObject); + + rb_define_singleton_method(cThread, "new", thread_start, 0); + rb_define_singleton_method(cThread, "start", thread_start, 0); + rb_define_singleton_method(cThread, "fork", thread_start, 0); + + rb_define_singleton_method(cThread, "stop", thread_stop, 0); + rb_define_singleton_method(cThread, "kill", thread_s_kill, 1); + rb_define_singleton_method(cThread, "exit", thread_exit, 0); + rb_define_singleton_method(cThread, "pass", thread_pass, 0); + rb_define_singleton_method(cThread, "join", thread_join, 1); + rb_define_singleton_method(cThread, "current", thread_current, 0); + rb_define_singleton_method(cThread, "main", thread_main, 0); + + rb_define_singleton_method(cThread, "critical", thread_get_critical, 0); + rb_define_singleton_method(cThread, "critical=", thread_set_critical, 1); + + rb_define_singleton_method(cThread, "abort_on_exception", thread_s_abort_exc, 0); + rb_define_singleton_method(cThread, "abort_on_exception=", thread_s_abort_exc_set, 1); + + rb_define_method(cThread, "run", thread_run, 0); + rb_define_method(cThread, "wakeup", thread_wakeup, 0); + rb_define_method(cThread, "stop", thread_stop_method, 0); + rb_define_method(cThread, "exit", thread_kill, 0); + rb_define_method(cThread, "value", thread_value, 0); + rb_define_method(cThread, "status", thread_status, 0); + rb_define_method(cThread, "alive?", thread_status, 0); + rb_define_method(cThread, "stop?", thread_stopped, 0); + rb_define_method(cThread, "raise", thread_raise, -1); + + rb_define_method(cThread, "abort_on_exception", thread_abort_exc, 0); + rb_define_method(cThread, "abort_on_exception=", thread_abort_exc_set, 1); + + /* allocate main thread */ + main_thread = thread_alloc(); +} +#endif diff --git a/ext/Setup b/ext/Setup new file mode 100644 index 0000000000..a5dd7978c3 --- /dev/null +++ b/ext/Setup @@ -0,0 +1,12 @@ +#option nodynamic + +#GD +#curses +#dbm +#etc +#fcntl +#kconv +#marshal +#md5 +#socket +#tkutil diff --git a/ext/Setup.dj b/ext/Setup.dj new file mode 100644 index 0000000000..25adea2035 --- /dev/null +++ b/ext/Setup.dj @@ -0,0 +1,12 @@ +option nodynamic + +#GD +#curses +dbm +#etc +fcntl +kconv +marshal +md5 +#socket +#tkutil diff --git a/ext/Setup.nt b/ext/Setup.nt new file mode 100644 index 0000000000..b469709585 --- /dev/null +++ b/ext/Setup.nt @@ -0,0 +1,12 @@ +option nodynamic + +#GD +#curses +#dbm +#etc +fcntl +kconv +#marshal +md5 +socket +#tkutil diff --git a/ext/Setup.x68 b/ext/Setup.x68 new file mode 100644 index 0000000000..25adea2035 --- /dev/null +++ b/ext/Setup.x68 @@ -0,0 +1,12 @@ +option nodynamic + +#GD +#curses +dbm +#etc +fcntl +kconv +marshal +md5 +#socket +#tkutil diff --git a/ext/aix_ld.rb b/ext/aix_ld.rb new file mode 100644 index 0000000000..1058977b88 --- /dev/null +++ b/ext/aix_ld.rb @@ -0,0 +1,73 @@ +#! /usr/local/bin/ruby + +def older(file1, file2) + if !File.exist?(file1) then + return TRUE + end + if !File.exist?(file2) then + return FALSE + end + if File.mtime(file1) < File.mtime(file2) + return TRUE + end + return FALSE +end + +target = ARGV.shift +unless target =~ /\.so/ + STDERR.printf "wrong suffix specified\n" + exit 1 +end +base = File.basename(target, ".so") +entry="Init_#{base}" +ldargs = "-e#{entry} -bI:../ruby.imp -bM:SRE -T512 -H512 -lc" + +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", "T" + line[2] + else + next + end + }.sort! + uniq(data) + exp = open(out, "w") + for line in data + exp.printf "%s\n", line + end + exp.close + nm.close +end +if older("../ruby.imp", "../../miniruby") +# nm = open("|/usr/ccs/bin/nm -Bex ../../*.o") +# nm = open("|/usr/ccs/bin/nm -Bex ../../*.o") + nm = open("|nm ../../*.o") + extract(nm, "../ruby.imp") +end + +objs = Dir["*.o"].join(" ") +#nm = open("|/usr/ccs/bin/nm -Bex #{objs}") +nm = open("|nm #{objs}") +extract(nm, "#{base}.exp") + +#system format("/usr/ccs/bin/ld %s %s ",ldargs,ARGV.join(' ')) +#system "/bin/rm -f #{base}.exp" +#system "chmod o-rwx ${base}.so" + +p format("/usr/ccs/bin/ld %s %s ",ldargs,ARGV.join(' ')) +p "/bin/rm -f #{base}.exp" +p "chmod o-rwx ${base}.so" diff --git a/ext/curses/MANIFEST b/ext/curses/MANIFEST new file mode 100644 index 0000000000..db5e54ffe8 --- /dev/null +++ b/ext/curses/MANIFEST @@ -0,0 +1,6 @@ +MANIFEST +curses.c +extconf.rb +hello.rb +rain.rb +view.rb diff --git a/ext/curses/curses.c b/ext/curses/curses.c new file mode 100644 index 0000000000..6e8e49684f --- /dev/null +++ b/ext/curses/curses.c @@ -0,0 +1,798 @@ +/* + * ext/curses/curses.c + * + * by MAEDA Shugo (ender@pic-internet.or.jp) + * modified by Yukihiro Matsumoto (matz@ruby.club.or.jp) + */ + +#ifdef HAVE_NCURSES_H +# include <ncurses.h> +#else +# ifdef HAVE_NCURSES_CURSES_H +# include <ncurses/curses.h> +# else +# include <curses.h> +# if defined(__NetBSD__) && !defined(_maxx) +# define _maxx maxx +# endif +# if defined(__NetBSD__) && !defined(_maxy) +# define _maxy maxy +# endif +# endif +#endif + +#include "ruby.h" + +static VALUE mCurses; +static VALUE cWindow; + +VALUE rb_stdscr; + +struct windata { + WINDOW *window; +}; + +#define NUM2CHAR(x) (char)NUM2INT(x) +#define CHAR2FIX(x) INT2FIX((int)x) + +static void +no_window() +{ + Fail("already closed window"); +} + +#define GetWINDOW(obj, winp) {\ + Data_Get_Struct(obj, struct windata, winp);\ + if (winp->window == 0) no_window();\ +} + +static void +curses_err() +{ + Fail("curses error"); +} + +#define CHECK(c) if ((c)==ERR) {curses_err();} + +static void +free_window(winp) + struct windata *winp; +{ + if (winp->window && winp->window != stdscr) delwin(winp->window); + winp->window = 0; +} + +static VALUE +prep_window(class, window) + VALUE class; + WINDOW *window; +{ + VALUE obj; + struct windata *winp; + + if (window == NULL) { + Fail("failed to create window"); + } + + obj = Data_Make_Struct(class, struct windata, 0, free_window, winp); + winp->window = window; + + return obj; +} + +/*-------------------------- module Curses --------------------------*/ + +/* def init_screen */ +static VALUE +curses_init_screen() +{ + initscr(); + if (stdscr == 0) { + Fail("cannot initialize curses"); + } + clear(); + rb_stdscr = prep_window(cWindow, stdscr); + return Qnil; +} + +/* def stdscr */ +static VALUE +curses_stdscr() +{ + if (!rb_stdscr) curses_init_screen(); + return rb_stdscr; +} + +/* def close_screen */ +static VALUE +curses_close_screen() +{ + CHECK(endwin()); + return Qnil; +} + +/* def closed? */ +static VALUE +curses_closed() +{ +#ifdef HAVE_ENDWIN + if (isendwin()) { + return TRUE; + } + return FALSE; +#else + rb_notimplement(); +#endif +} + +/* def clear */ +static VALUE +curses_clear(obj) + VALUE obj; +{ + wclear(stdscr); + return Qnil; +} + +/* def refresh */ +static VALUE +curses_refresh(obj) + VALUE obj; +{ + CHECK(refresh()); + return Qnil; +} + +/* def refresh */ +static VALUE +curses_doupdate(obj) + VALUE obj; +{ + CHECK(doupdate()); + return Qnil; +} + +/* def echo */ +static VALUE +curses_echo(obj) + VALUE obj; +{ + CHECK(echo()); + return Qnil; +} + +/* def noecho */ +static VALUE +curses_noecho(obj) + VALUE obj; +{ + CHECK(noecho()); + return Qnil; +} + +/* def raw */ +static VALUE +curses_raw(obj) + VALUE obj; +{ + CHECK(raw()); + return Qnil; +} + +/* def noraw */ +static VALUE +curses_noraw(obj) + VALUE obj; +{ + CHECK(noraw()); + return Qnil; +} + +/* def cbreak */ +static VALUE +curses_cbreak(obj) + VALUE obj; +{ + CHECK(cbreak()); + return Qnil; +} + +/* def nocbreak */ +static VALUE +curses_nocbreak(obj) + VALUE obj; +{ + CHECK(nocbreak()); + return Qnil; +} + +/* def nl */ +static VALUE +curses_nl(obj) + VALUE obj; +{ + CHECK(nl()); + return Qnil; +} + +/* def nonl */ +static VALUE +curses_nonl(obj) + VALUE obj; +{ + CHECK(nonl()); + return Qnil; +} + +/* def beep */ +static VALUE +curses_beep(obj) + VALUE obj; +{ +#ifdef HAVE_BEEP + beep(); +#endif + return Qnil; +} + +/* def flash */ +static VALUE +curses_flash(obj) + VALUE obj; +{ + flash(); + return Qnil; +} + +/* def ungetch */ +static VALUE +curses_ungetch(obj, ch) + VALUE obj; + VALUE ch; +{ +#ifdef HAVE_UNGETCH + CHECK(ungetch(NUM2INT(ch))); +#else + rb_notimplement(); +#endif + return Qnil; +} + +/* def setpos(y, x) */ +static VALUE +curses_setpos(obj, y, x) + VALUE obj; + VALUE y; + VALUE x; +{ + CHECK(move(NUM2INT(y), NUM2INT(x))); + return Qnil; +} + +/* def standout */ +static VALUE +curses_standout(obj) + VALUE obj; +{ + standout(); + return Qnil; +} + +/* def standend */ +static VALUE +curses_standend(obj) + VALUE obj; +{ + standend(); + return Qnil; +} + +/* def inch */ +static VALUE +curses_inch(obj) + VALUE obj; +{ + return CHAR2FIX(inch()); +} + +/* def addch(ch) */ +static VALUE +curses_addch(obj, ch) + VALUE obj; + VALUE ch; +{ + CHECK(addch(NUM2CHAR(ch))); + return Qnil; +} + +/* def insch(ch) */ +static VALUE +curses_insch(obj, ch) + VALUE obj; + VALUE ch; +{ + CHECK(insch(NUM2CHAR(ch))); + return Qnil; +} + +/* def addstr(str) */ +static VALUE +curses_addstr(obj, str) + VALUE obj; + VALUE str; +{ + addstr(RSTRING(str)->ptr); + return Qnil; +} + +/* def getch */ +static VALUE +curses_getch(obj) + VALUE obj; +{ + return CHAR2FIX(getch()); +} + +/* def getstr */ +static VALUE +curses_getstr(obj) + VALUE obj; +{ + char rtn[1024]; /* This should be big enough.. I hope */ + CHECK(getstr(rtn)); + return str_taint(str_new2(rtn)); +} + +/* def delch */ +static VALUE +curses_delch(obj) + VALUE obj; +{ + CHECK(delch()); + return Qnil; +} + +/* def delelteln */ +static VALUE +curses_deleteln(obj) + VALUE obj; +{ + CHECK(deleteln()); + return Qnil; +} + +static VALUE +curses_lines() +{ + return INT2FIX(LINES); +} + +static VALUE +curses_cols() +{ + return INT2FIX(COLS); +} + +/*-------------------------- class Window --------------------------*/ + +/* def new(lines, cols, top, left) */ +static VALUE +window_s_new(class, lines, cols, top, left) + VALUE class; + VALUE lines; + VALUE cols; + VALUE top; + VALUE left; +{ + WINDOW *window; + + window = newwin(NUM2INT(lines), NUM2INT(cols), NUM2INT(top), NUM2INT(left)); + wclear(window); + return prep_window(class, window); +} + +/* def subwin(lines, cols, top, left) */ +static VALUE +window_subwin(obj, lines, cols, top, left) + VALUE obj; + VALUE lines; + VALUE cols; + VALUE top; + VALUE left; +{ + struct windata *winp; + WINDOW *window; + + GetWINDOW(obj, winp); + window = subwin(winp->window, NUM2INT(lines), NUM2INT(cols), + NUM2INT(top), NUM2INT(left)); + return prep_window(cWindow, window); +} + +/* def close */ +static VALUE +window_close(obj) + VALUE obj; +{ + struct windata *winp; + + GetWINDOW(obj, winp); + free_window(winp); + + return Qnil; +} + +/* def clear */ +static VALUE +window_clear(obj) + VALUE obj; +{ + struct windata *winp; + + GetWINDOW(obj, winp); + wclear(winp->window); + + return Qnil; +} + +/* def refresh */ +static VALUE +window_refresh(obj) + VALUE obj; +{ + struct windata *winp; + + GetWINDOW(obj, winp); + CHECK(wrefresh(winp->window)); + + return Qnil; +} + +/* def box(vert, hor) */ +static VALUE +window_box(obj, vert, hor) + VALUE obj; + VALUE vert; + VALUE hor; +{ + struct windata *winp; + + GetWINDOW(obj, winp); + box(winp->window, NUM2CHAR(vert), NUM2CHAR(hor)); + + return Qnil; +} + + +/* def move(y, x) */ +static VALUE +window_move(obj, y, x) + VALUE obj; + VALUE y; + VALUE x; +{ + struct windata *winp; + + GetWINDOW(obj, winp); + CHECK(mvwin(winp->window, NUM2INT(y), NUM2INT(x))); + + return Qnil; +} + +/* def setpos(y, x) */ +static VALUE +window_setpos(obj, y, x) + VALUE obj; + VALUE y; + VALUE x; +{ + struct windata *winp; + + GetWINDOW(obj, winp); + CHECK(wmove(winp->window, NUM2INT(y), NUM2INT(x))); + return Qnil; +} + +/* def cury */ +static VALUE +window_cury(obj) + VALUE obj; +{ + struct windata *winp; + int x, y; + + GetWINDOW(obj, winp); + getyx(winp->window, y, x); + return INT2FIX(y); +} + +/* def curx */ +static VALUE +window_curx(obj) + VALUE obj; +{ + struct windata *winp; + int x, y; + + GetWINDOW(obj, winp); + getyx(winp->window, y, x); + return INT2FIX(x); +} + +/* def maxy */ +static VALUE +window_maxy(obj) + VALUE obj; +{ + struct windata *winp; + int x, y; + + GetWINDOW(obj, winp); +#ifdef getmaxy + return INT2FIX(getmaxy(winp->window)); +#else +#ifdef getmaxyx + getmaxyx(winp->window, y, x); + return INT2FIX(y); +#else + return INT2FIX(winp->window->_maxy+1); +#endif +#endif +} + +/* def maxx */ +static VALUE +window_maxx(obj) + VALUE obj; +{ + struct windata *winp; + int x, y; + + GetWINDOW(obj, winp); +#ifdef getmaxx + return INT2FIX(getmaxx(winp->window)); +#else +#ifdef getmaxyx + getmaxyx(winp->window, y, x); + return INT2FIX(x); +#else + return INT2FIX(winp->window->_maxx+1); +#endif +#endif +} + +/* def begy */ +static VALUE +window_begy(obj) + VALUE obj; +{ + struct windata *winp; + int x, y; + + GetWINDOW(obj, winp); +#ifdef getbegyx + getbegyx(winp->window, y, x); + return INT2FIX(y); +#else + return INT2FIX(winp->window->_begy); +#endif +} + +/* def begx */ +static VALUE +window_begx(obj) + VALUE obj; +{ + struct windata *winp; + int x, y; + + GetWINDOW(obj, winp); +#ifdef getbegyx + getbegyx(winp->window, y, x); + return INT2FIX(x); +#else + return INT2FIX(winp->window->_begx); +#endif +} + +/* def standout */ +static VALUE +window_standout(obj) + VALUE obj; +{ + struct windata *winp; + + GetWINDOW(obj, winp); + wstandout(winp->window); + return Qnil; +} + +/* def standend */ +static VALUE +window_standend(obj) + VALUE obj; +{ + struct windata *winp; + + GetWINDOW(obj, winp); + wstandend(winp->window); + return Qnil; +} + +/* def inch */ +static VALUE +window_inch(obj) + VALUE obj; +{ + struct windata *winp; + + GetWINDOW(obj, winp); + return CHAR2FIX(winch(winp->window)); +} + +/* def addch(ch) */ +static VALUE +window_addch(obj, ch) + VALUE obj; + VALUE ch; +{ + struct windata *winp; + + GetWINDOW(obj, winp); + CHECK(waddch(winp->window, NUM2CHAR(ch))); + + return Qnil; +} + +/* def insch(ch) */ +static VALUE +window_insch(obj, ch) + VALUE obj; + VALUE ch; +{ + struct windata *winp; + + GetWINDOW(obj, winp); + CHECK(winsch(winp->window, NUM2CHAR(ch))); + + return Qnil; +} + +/* def addstr(str) */ +static VALUE +window_addstr(obj, str) + VALUE obj; + VALUE str; +{ + struct windata *winp; + + GetWINDOW(obj, winp); + CHECK(waddstr(winp->window, RSTRING(str)->ptr)); + + return Qnil; +} + +/* def <<(str) */ +static VALUE +window_addstr2(obj, str) + VALUE obj; + VALUE str; +{ + window_addstr(obj, str); + return obj; +} + +/* def getch */ +static VALUE +window_getch(obj) + VALUE obj; +{ + struct windata *winp; + + GetWINDOW(obj, winp); + return CHAR2FIX(wgetch(winp->window)); +} + +/* def getstr */ +static VALUE +window_getstr(obj) + VALUE obj; +{ + struct windata *winp; + char rtn[1024]; /* This should be big enough.. I hope */ + + GetWINDOW(obj, winp); + CHECK(wgetstr(winp->window, rtn)); + return str_taint(str_new2(rtn)); +} + +/* def delch */ +static VALUE +window_delch(obj) + VALUE obj; +{ + struct windata *winp; + + GetWINDOW(obj, winp); + CHECK(wdelch(winp->window)); + return Qnil; +} + +/* def delelteln */ +static VALUE +window_deleteln(obj) + VALUE obj; +{ + struct windata *winp; + + GetWINDOW(obj, winp); + CHECK(wdeleteln(winp->window)); + return Qnil; +} + +/*------------------------- Initialization -------------------------*/ +void +Init_curses() +{ + mCurses = rb_define_module("Curses"); + rb_define_module_function(mCurses, "init_screen", curses_init_screen, 0); + rb_define_module_function(mCurses, "close_screen", curses_close_screen, 0); + rb_define_module_function(mCurses, "closed?", curses_closed, 0); + rb_define_module_function(mCurses, "stdscr", curses_stdscr, 0); + rb_define_module_function(mCurses, "refresh", curses_refresh, 0); + rb_define_module_function(mCurses, "doupdate", curses_doupdate, 0); + rb_define_module_function(mCurses, "clear", curses_clear, 0); + rb_define_module_function(mCurses, "echo", curses_echo, 0); + rb_define_module_function(mCurses, "noecho", curses_noecho, 0); + rb_define_module_function(mCurses, "raw", curses_raw, 0); + rb_define_module_function(mCurses, "noraw", curses_noraw, 0); + rb_define_module_function(mCurses, "cbreak", curses_cbreak, 0); + rb_define_module_function(mCurses, "nocbreak", curses_nocbreak, 0); + rb_define_alias(mCurses, "crmode", "cbreak"); + rb_define_alias(mCurses, "nocrmode", "nocbreak"); + rb_define_module_function(mCurses, "nl", curses_nl, 0); + rb_define_module_function(mCurses, "nonl", curses_nonl, 0); + rb_define_module_function(mCurses, "beep", curses_beep, 0); + rb_define_module_function(mCurses, "flash", curses_flash, 0); + rb_define_module_function(mCurses, "ungetch", curses_ungetch, 1); + rb_define_module_function(mCurses, "setpos", curses_setpos, 2); + rb_define_module_function(mCurses, "standout", curses_standout, 0); + rb_define_module_function(mCurses, "standend", curses_standend, 0); + rb_define_module_function(mCurses, "inch", curses_inch, 0); + rb_define_module_function(mCurses, "addch", curses_addch, 1); + rb_define_module_function(mCurses, "insch", curses_insch, 1); + rb_define_module_function(mCurses, "addstr", curses_addstr, 1); + rb_define_module_function(mCurses, "getch", curses_getch, 0); + rb_define_module_function(mCurses, "getstr", curses_getstr, 0); + rb_define_module_function(mCurses, "delch", curses_delch, 0); + rb_define_module_function(mCurses, "deleteln", curses_deleteln, 0); + rb_define_module_function(mCurses, "lines", curses_lines, 0); + rb_define_module_function(mCurses, "cols", curses_cols, 0); + + cWindow = rb_define_class_under(mCurses, "Window", cObject); + rb_define_singleton_method(cWindow, "new", window_s_new, 4); + rb_define_method(cWindow, "subwin", window_subwin, 4); + rb_define_method(cWindow, "close", window_close, 0); + rb_define_method(cWindow, "clear", window_clear, 0); + rb_define_method(cWindow, "refresh", window_refresh, 0); + rb_define_method(cWindow, "box", window_box, 2); + rb_define_method(cWindow, "move", window_move, 2); + rb_define_method(cWindow, "setpos", window_setpos, 2); + rb_define_method(cWindow, "cury", window_cury, 0); + rb_define_method(cWindow, "curx", window_curx, 0); + rb_define_method(cWindow, "maxy", window_maxy, 0); + rb_define_method(cWindow, "maxx", window_maxx, 0); + rb_define_method(cWindow, "begy", window_begy, 0); + rb_define_method(cWindow, "begx", window_begx, 0); + rb_define_method(cWindow, "standout", window_standout, 0); + rb_define_method(cWindow, "standend", window_standend, 0); + rb_define_method(cWindow, "inch", window_inch, 0); + rb_define_method(cWindow, "addch", window_addch, 1); + rb_define_method(cWindow, "insch", window_insch, 1); + rb_define_method(cWindow, "addstr", window_addstr, 1); + rb_define_method(cWindow, "<<", window_addstr2, 1); + rb_define_method(cWindow, "getch", window_getch, 0); + rb_define_method(cWindow, "getstr", window_getstr, 0); + rb_define_method(cWindow, "delch", window_delch, 0); + rb_define_method(cWindow, "deleteln", window_deleteln, 0); +} diff --git a/ext/curses/extconf.rb b/ext/curses/extconf.rb new file mode 100644 index 0000000000..9b28437843 --- /dev/null +++ b/ext/curses/extconf.rb @@ -0,0 +1,21 @@ +$CFLAGS="-I/usr/include/ncurses -I/usr/local/include/ncurses" +$LDFLAGS="-L/usr/local/lib" +make=FALSE +if have_header("ncurses.h") and have_library("ncurses", "initscr") + make=TRUE +elsif have_header("ncurses/curses.h") and have_library("ncurses", "initscr") + make=TRUE +else + $CFLAGS=nil + have_library("termcap", "tgetent") + if have_library("curses", "initscr") + make=TRUE + end +end + +if make then + for f in ["isendwin", "ungetch", "beep"] + have_func(f) + end + create_makefile("curses") +end diff --git a/ext/curses/hello.rb b/ext/curses/hello.rb new file mode 100644 index 0000000000..bed7779aac --- /dev/null +++ b/ext/curses/hello.rb @@ -0,0 +1,28 @@ +#!/usr/local/bin/ruby + +require "curses" +include Curses + +def show_message(message) + width = message.length + 6 + win = Window.new(5, width, + (lines - 5) / 2, (cols - width) / 2) + win.box(?|, ?=) + win.setpos(2, 3) + win.addstr(message) + win.getch + win.close +end + +init_screen +begin + crmode +# show_message("Hit any key") + setpos (lines - 5) / 2, (cols - 10) / 2 + addstr("Hit any key") + getch + show_message("Hello, World!") + refresh +ensure + close_screen +end diff --git a/ext/curses/rain.rb b/ext/curses/rain.rb new file mode 100644 index 0000000000..36f0f84de2 --- /dev/null +++ b/ext/curses/rain.rb @@ -0,0 +1,76 @@ +#!/usr/local/bin/ruby +# rain for a curses test + +require "curses" +include Curses + +def onsig(sig) + close_screen + exit sig +end + +def ranf + rand(32767).to_f / 32767 +end + +# main # +for i in 1 .. 15 # SIGHUP .. SIGTERM + if trap(i, "SIG_IGN") != 0 then # 0 for SIG_IGN + trap(i) {|sig| onsig(sig) } + end +end + +init_screen +nl +noecho +srand + +xpos = {} +ypos = {} +r = lines - 4 +c = cols - 4 +for i in 0 .. 4 + xpos[i] = (c * ranf).to_i + 2 + ypos[i] = (r * ranf).to_i + 2 +end + +i = 0 +while TRUE + x = (c * ranf).to_i + 2 + y = (r * ranf).to_i + 2 + + + setpos(y, x); addstr(".") + + setpos(ypos[i], xpos[i]); addstr("o") + + i = if i == 0 then 4 else i - 1 end + setpos(ypos[i], xpos[i]); addstr("O") + + i = if i == 0 then 4 else i - 1 end + setpos(ypos[i] - 1, xpos[i]); addstr("-") + setpos(ypos[i], xpos[i] - 1); addstr("|.|") + setpos(ypos[i] + 1, xpos[i]); addstr("-") + + i = if i == 0 then 4 else i - 1 end + setpos(ypos[i] - 2, xpos[i]); addstr("-") + setpos(ypos[i] - 1, xpos[i] - 1); addstr("/ \\") + setpos(ypos[i], xpos[i] - 2); addstr("| O |") + setpos(ypos[i] + 1, xpos[i] - 1); addstr("\\ /") + setpos(ypos[i] + 2, xpos[i]); addstr("-") + + i = if i == 0 then 4 else i - 1 end + setpos(ypos[i] - 2, xpos[i]); addstr(" ") + setpos(ypos[i] - 1, xpos[i] - 1); addstr(" ") + setpos(ypos[i], xpos[i] - 2); addstr(" ") + setpos(ypos[i] + 1, xpos[i] - 1); addstr(" ") + setpos(ypos[i] + 2, xpos[i]); addstr(" ") + + + xpos[i] = x + ypos[i] = y + refresh + sleep(0.5) +end + +# end of main diff --git a/ext/curses/view.rb b/ext/curses/view.rb new file mode 100644 index 0000000000..e59a74ed44 --- /dev/null +++ b/ext/curses/view.rb @@ -0,0 +1,90 @@ +#!/usr/local/bin/ruby + +require "curses" +include Curses + +# +# main +# + +if ARGV.size != 1 then + printf("usage: view file\n"); + exit +end +begin + fp = open(ARGV[0], "r") +rescue + raise "cannot open file: #{ARGV[1]}" +end + +# signal(SIGINT, finish) + +init_screen +#keypad(stdscr, TRUE) +nonl +cbreak +noecho +#scrollok(stdscr, TRUE) + +# slurp the file +data_lines = [] +fp.each_line { |l| + data_lines.push(l) +} +fp.close + + +lptr = 0 +while TRUE + i = 0 + while i < lines + setpos(i, 0) + #clrtoeol + addstr(data_lines[lptr + i]) #if data_lines[lptr + i] + i += 1 + end + + explicit = FALSE + n = 0 + while TRUE + c = getch.chr + if c =~ "[0-9]" then + n = 10 * n + c.to_i + else + break + end + end + + n = 1 if !explicit && n == 0 + + case c + when "n" #when KEY_DOWN + i = 0 + while i < n + if lptr + lines < data_lines.size then + lptr += 1 + else + break + end + i += 1 + end + #wscrl(i) + + when "p" #when KEY_UP + i = 0 + while i < n + if lptr > 0 then + lptr -= 1 + else + break + end + i += 1 + end + #wscrl(-i) + + when "q" + break + end + +end +close_screen diff --git a/ext/dbm/MANIFEST b/ext/dbm/MANIFEST new file mode 100644 index 0000000000..8beec6783d --- /dev/null +++ b/ext/dbm/MANIFEST @@ -0,0 +1,4 @@ +MANIFEST +dbm.c +depend +extconf.rb diff --git a/ext/dbm/dbm.c b/ext/dbm/dbm.c new file mode 100644 index 0000000000..ea0ac1930f --- /dev/null +++ b/ext/dbm/dbm.c @@ -0,0 +1,515 @@ +/************************************************ + + dbm.c - + + $Author$ + $Date$ + created at: Mon Jan 24 15:59:52 JST 1994 + + Copyright (C) 1995 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" + +#include <ndbm.h> +#include <fcntl.h> +#include <errno.h> + +VALUE cDBM; + +extern VALUE mEnumerable; + +struct dbmdata { + int di_size; + DBM *di_dbm; +}; + +static void +closed_dbm() +{ + Fail("closed DBM file"); +} + +#define GetDBM(obj, dbmp) {\ + Data_Get_Struct(obj, struct dbmdata, dbmp);\ + if (dbmp->di_dbm == 0) closed_dbm();\ +} + +static void +free_dbm(dbmp) + struct dbmdata *dbmp; +{ + if (dbmp->di_dbm) dbm_close(dbmp->di_dbm); +} + +static VALUE +fdbm_s_open(argc, argv, class) + int argc; + VALUE *argv; + VALUE class; +{ + VALUE file, vmode; + DBM *dbm; + struct dbmdata *dbmp; + int mode; + VALUE obj; + + if (rb_scan_args(argc, argv, "11", &file, &vmode) == 1) { + mode = 0666; /* default value */ + } + else if (NIL_P(vmode)) { + mode = -1; /* return nil if DB not exist */ + } + else { + mode = NUM2INT(vmode); + } + Check_SafeStr(file); + + dbm = 0; + if (mode >= 0) + dbm = dbm_open(RSTRING(file)->ptr, O_RDWR|O_CREAT, mode); + if (!dbm) + dbm = dbm_open(RSTRING(file)->ptr, O_RDWR, mode); + if (!dbm) + dbm = dbm_open(RSTRING(file)->ptr, O_RDONLY, mode); + + if (!dbm) { + if (mode == -1) return Qnil; + rb_sys_fail(RSTRING(file)->ptr); + } + + obj = Data_Make_Struct(class,struct dbmdata,0,free_dbm,dbmp); + dbmp->di_dbm = dbm; + dbmp->di_size = -1; + + return obj; +} + +static VALUE +fdbm_close(obj) + VALUE obj; +{ + struct dbmdata *dbmp; + + Data_Get_Struct(obj, struct dbmdata, dbmp); + if (dbmp->di_dbm == 0) closed_dbm(); + dbm_close(dbmp->di_dbm); + dbmp->di_dbm = 0; + + return Qnil; +} + +static VALUE +fdbm_fetch(obj, keystr) + VALUE obj, keystr; +{ + datum key, value; + struct dbmdata *dbmp; + DBM *dbm; + + Check_Type(keystr, T_STRING); + key.dptr = RSTRING(keystr)->ptr; + key.dsize = RSTRING(keystr)->len; + + GetDBM(obj, dbmp); + dbm = dbmp->di_dbm; + value = dbm_fetch(dbm, key); + if (value.dptr == 0) { + return Qnil; + } + return str_taint(str_new(value.dptr, value.dsize)); +} + +static VALUE +fdbm_indexes(obj, args) + VALUE obj; + struct RArray *args; +{ + VALUE *p, *pend; + VALUE new; + int i = 0; + + args = (struct RArray*)rb_to_a(args); + new = ary_new2(args->len); + + p = args->ptr; pend = p + args->len; + while (p < pend) { + ary_push(new, fdbm_fetch(obj, *p++)); + } + return new; +} + +static VALUE +fdbm_delete(obj, keystr) + VALUE obj, keystr; +{ + datum key, value; + struct dbmdata *dbmp; + DBM *dbm; + + rb_secure(4); + Check_Type(keystr, T_STRING); + key.dptr = RSTRING(keystr)->ptr; + key.dsize = RSTRING(keystr)->len; + + GetDBM(obj, dbmp); + dbm = dbmp->di_dbm; + + value = dbm_fetch(dbm, key); + if (value.dptr == 0) { + if (iterator_p()) rb_yield(keystr); + return Qnil; + } + + if (dbm_delete(dbm, key)) { + dbmp->di_size = -1; + Fail("dbm_delete failed"); + } + else if (dbmp->di_size >= 0) { + dbmp->di_size--; + } + return obj; +} + +static VALUE +fdbm_shift(obj) + VALUE obj; +{ + datum key, val; + struct dbmdata *dbmp; + DBM *dbm; + VALUE keystr, valstr; + + rb_secure(4); + GetDBM(obj, dbmp); + dbm = dbmp->di_dbm; + + key = dbm_firstkey(dbm); + if (!key.dptr) return Qnil; + val = dbm_fetch(dbm, key); + dbm_delete(dbm, key); + + keystr = str_taint(str_new(key.dptr, key.dsize)); + valstr = str_taint(str_new(val.dptr, val.dsize)); + return assoc_new(keystr, valstr); +} + +static VALUE +fdbm_delete_if(obj) + VALUE obj; +{ + datum key, val; + struct dbmdata *dbmp; + DBM *dbm; + VALUE keystr, valstr; + + rb_secure(4); + GetDBM(obj, dbmp); + dbm = dbmp->di_dbm; + for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { + val = dbm_fetch(dbm, key); + keystr = str_taint(str_new(key.dptr, key.dsize)); + valstr = str_taint(str_new(val.dptr, val.dsize)); + if (RTEST(rb_yield(assoc_new(keystr, valstr)))) { + if (dbm_delete(dbm, key)) { + Fail("dbm_delete failed"); + } + } + } + return obj; +} + +static VALUE +fdbm_clear(obj) + VALUE obj; +{ + datum key; + struct dbmdata *dbmp; + DBM *dbm; + + rb_secure(4); + GetDBM(obj, dbmp); + dbm = dbmp->di_dbm; + dbmp->di_size = -1; + for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { + if (dbm_delete(dbm, key)) { + Fail("dbm_delete failed"); + } + } + return obj; +} + +static VALUE +fdbm_store(obj, keystr, valstr) + VALUE obj, keystr, valstr; +{ + datum key, val; + struct dbmdata *dbmp; + DBM *dbm; + + if (valstr == Qnil) { + fdbm_delete(obj, keystr); + return Qnil; + } + + rb_secure(4); + keystr = obj_as_string(keystr); + + key.dptr = RSTRING(keystr)->ptr; + key.dsize = RSTRING(keystr)->len; + + if (NIL_P(valstr)) return fdbm_delete(obj, keystr); + + valstr = obj_as_string(valstr); + val.dptr = RSTRING(valstr)->ptr; + val.dsize = RSTRING(valstr)->len; + + Data_Get_Struct(obj, struct dbmdata, dbmp); + dbmp->di_size = -1; + dbm = dbmp->di_dbm; + if (dbm_store(dbm, key, val, DBM_REPLACE)) { + dbm_clearerr(dbm); + if (errno == EPERM) rb_sys_fail(Qnil); + Fail("dbm_store failed"); + } + + return valstr; +} + +static VALUE +fdbm_length(obj) + VALUE obj; +{ + datum key; + struct dbmdata *dbmp; + DBM *dbm; + int i = 0; + + Data_Get_Struct(obj, struct dbmdata, dbmp); + if (dbmp->di_size > 0) return INT2FIX(dbmp->di_size); + dbm = dbmp->di_dbm; + + for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { + i++; + } + dbmp->di_size = i; + + return INT2FIX(i); +} + +static VALUE +fdbm_empty_p(obj) + VALUE obj; +{ + datum key; + struct dbmdata *dbmp; + DBM *dbm; + int i = 0; + + Data_Get_Struct(obj, struct dbmdata, dbmp); + if (dbmp->di_size < 0) { + dbm = dbmp->di_dbm; + + for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { + i++; + } + } + else { + i = dbmp->di_size; + } + if (i == 0) return TRUE; + return FALSE; +} + +static VALUE +fdbm_each_value(obj) + VALUE obj; +{ + datum key, val; + struct dbmdata *dbmp; + DBM *dbm; + + GetDBM(obj, dbmp); + dbm = dbmp->di_dbm; + for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { + val = dbm_fetch(dbm, key); + rb_yield(str_taint(str_new(val.dptr, val.dsize))); + } + return obj; +} + +static VALUE +fdbm_each_key(obj) + VALUE obj; +{ + datum key; + struct dbmdata *dbmp; + DBM *dbm; + + GetDBM(obj, dbmp); + dbm = dbmp->di_dbm; + for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { + rb_yield(str_taint(str_new(key.dptr, key.dsize))); + } + return obj; +} + +static VALUE +fdbm_each_pair(obj) + VALUE obj; +{ + datum key, val; + DBM *dbm; + struct dbmdata *dbmp; + VALUE keystr, valstr; + + GetDBM(obj, dbmp); + dbm = dbmp->di_dbm; + + for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { + val = dbm_fetch(dbm, key); + keystr = str_taint(str_new(key.dptr, key.dsize)); + valstr = str_taint(str_new(val.dptr, val.dsize)); + rb_yield(assoc_new(keystr, valstr)); + } + + return obj; +} + +static VALUE +fdbm_keys(obj) + VALUE obj; +{ + datum key; + struct dbmdata *dbmp; + DBM *dbm; + VALUE ary; + + GetDBM(obj, dbmp); + dbm = dbmp->di_dbm; + + ary = ary_new(); + for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { + ary_push(ary, str_taint(str_new(key.dptr, key.dsize))); + } + + return ary; +} + +static VALUE +fdbm_values(obj) + VALUE obj; +{ + datum key, val; + struct dbmdata *dbmp; + DBM *dbm; + VALUE ary; + + GetDBM(obj, dbmp); + dbm = dbmp->di_dbm; + + ary = ary_new(); + for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { + val = dbm_fetch(dbm, key); + ary_push(ary, str_taint(str_new(val.dptr, val.dsize))); + } + + return ary; +} + +static VALUE +fdbm_has_key(obj, keystr) + VALUE obj, keystr; +{ + datum key, val; + struct dbmdata *dbmp; + DBM *dbm; + + Check_Type(keystr, T_STRING); + key.dptr = RSTRING(keystr)->ptr; + key.dsize = RSTRING(keystr)->len; + + GetDBM(obj, dbmp); + dbm = dbmp->di_dbm; + val = dbm_fetch(dbm, key); + if (val.dptr) return TRUE; + return FALSE; +} + +static VALUE +fdbm_has_value(obj, valstr) + VALUE obj, valstr; +{ + datum key, val; + struct dbmdata *dbmp; + DBM *dbm; + + Check_Type(valstr, T_STRING); + val.dptr = RSTRING(valstr)->ptr; + val.dsize = RSTRING(valstr)->len; + + GetDBM(obj, dbmp); + dbm = dbmp->di_dbm; + for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { + val = dbm_fetch(dbm, key); + if (val.dsize == RSTRING(valstr)->len && + memcmp(val.dptr, RSTRING(valstr)->ptr, val.dsize) == 0) + return TRUE; + } + return FALSE; +} + +static VALUE +fdbm_to_a(obj) + VALUE obj; +{ + datum key, val; + struct dbmdata *dbmp; + DBM *dbm; + VALUE ary; + + GetDBM(obj, dbmp); + dbm = dbmp->di_dbm; + + ary = ary_new(); + for (key = dbm_firstkey(dbm); key.dptr; key = dbm_nextkey(dbm)) { + val = dbm_fetch(dbm, key); + ary_push(ary, assoc_new(str_taint(str_new(key.dptr, key.dsize)), + str_taint(str_new(val.dptr, val.dsize)))); + } + + return ary; +} + +Init_dbm() +{ + cDBM = rb_define_class("DBM", cObject); + rb_include_module(cDBM, mEnumerable); + + rb_define_singleton_method(cDBM, "open", fdbm_s_open, -1); + rb_define_method(cDBM, "close", fdbm_close, 0); + rb_define_method(cDBM, "[]", fdbm_fetch, 1); + rb_define_method(cDBM, "[]=", fdbm_store, 2); + rb_define_method(cDBM, "indexes", fdbm_indexes, -2); + rb_define_method(cDBM, "length", fdbm_length, 0); + rb_define_alias(cDBM, "size", "length"); + rb_define_method(cDBM, "empty?", fdbm_empty_p, 0); + rb_define_method(cDBM, "each", fdbm_each_pair, 0); + rb_define_method(cDBM, "each_value", fdbm_each_value, 0); + rb_define_method(cDBM, "each_key", fdbm_each_key, 0); + rb_define_method(cDBM, "each_pair", fdbm_each_pair, 0); + rb_define_method(cDBM, "keys", fdbm_keys, 0); + rb_define_method(cDBM, "values", fdbm_values, 0); + rb_define_method(cDBM, "shift", fdbm_shift, 1); + rb_define_method(cDBM, "delete", fdbm_delete, 1); + rb_define_method(cDBM, "delete_if", fdbm_delete_if, 0); + rb_define_method(cDBM, "clear", fdbm_clear, 0); + rb_define_method(cDBM, "include?", fdbm_has_key, 1); + rb_define_method(cDBM, "has_key?", fdbm_has_key, 1); + rb_define_method(cDBM, "has_value?", fdbm_has_value, 1); + rb_define_method(cDBM, "key?", fdbm_has_key, 1); + rb_define_method(cDBM, "value?", fdbm_has_value, 1); + + rb_define_method(cDBM, "to_a", fdbm_to_a, 0); +} diff --git a/ext/dbm/depend b/ext/dbm/depend new file mode 100644 index 0000000000..40139962a7 --- /dev/null +++ b/ext/dbm/depend @@ -0,0 +1 @@ +dbm.o: dbm.c ../../ruby.h ../../config.h ../../defines.h diff --git a/ext/dbm/extconf.rb b/ext/dbm/extconf.rb new file mode 100644 index 0000000000..4a5d41f275 --- /dev/null +++ b/ext/dbm/extconf.rb @@ -0,0 +1,5 @@ +$LDFLAGS = "-L/usr/local/lib" +have_library("gdbm", "dbm_open") or have_library("dbm", "dbm_open") +if have_func("dbm_open") + create_makefile("dbm") +end diff --git a/ext/etc/MANIFEST b/ext/etc/MANIFEST new file mode 100644 index 0000000000..a0f521b386 --- /dev/null +++ b/ext/etc/MANIFEST @@ -0,0 +1,5 @@ +MANIFEST +etc.c +etc.doc +depend +extconf.rb diff --git a/ext/etc/depend b/ext/etc/depend new file mode 100644 index 0000000000..5c95ef117a --- /dev/null +++ b/ext/etc/depend @@ -0,0 +1 @@ +etc.o : etc.c ../../ruby.h ../../config.h ../../defines.h diff --git a/ext/etc/etc.c b/ext/etc/etc.c new file mode 100644 index 0000000000..20cd9295ed --- /dev/null +++ b/ext/etc/etc.c @@ -0,0 +1,266 @@ +/************************************************ + + etc.c - + + $Author$ + $Date$ + created at: Tue Mar 22 18:39:19 JST 1994 + +************************************************/ + +#include "ruby.h" + +#ifdef HAVE_GETPWENT +#include <pwd.h> +#endif + +#ifdef HAVE_GETGRENT +#include <grp.h> +#endif + +static VALUE sPasswd, sGroup; + +static VALUE +etc_getlogin(obj) + VALUE obj; +{ + char *getenv(); + char *login; + +#ifdef HAVE_GETLOGIN + char *getlogin(); + + login = getlogin(); + if (!login) login = getenv("USER"); +#else + login = getenv("USER"); +#endif + + if (login) + return str_new2(login); + return Qnil; +} + +#ifdef HAVE_GETPWENT +static VALUE +setup_passwd(pwd) + struct passwd *pwd; +{ + if (pwd == 0) rb_sys_fail("/etc/passwd"); + return struct_new(sPasswd, + str_new2(pwd->pw_name), + str_new2(pwd->pw_passwd), + INT2FIX(pwd->pw_uid), + INT2FIX(pwd->pw_gid), + str_new2(pwd->pw_gecos), + str_new2(pwd->pw_dir), + str_new2(pwd->pw_shell), +#ifdef PW_CHANGE + INT2FIX(pwd->pw_change), +#endif +#ifdef PW_QUOTA + INT2FIX(pwd->pw_quota), +#endif +#ifdef PW_AGE + INT2FIX(pwd->pw_age), +#endif +#ifdef PW_CLASS + str_new2(pwd->pw_class), +#endif +#ifdef PW_COMMENT + str_new2(pwd->pw_comment), +#endif +#ifdef PW_EXPIRE + INT2FIX(pwd->pw_expire), +#endif + 0 /*dummy*/ + ); +} +#endif + +static VALUE +etc_getpwuid(argc, argv, obj) + int argc; + VALUE *argv; + VALUE obj; +{ +#ifdef HAVE_GETPWENT + VALUE id; + int uid; + struct passwd *pwd; + + if (rb_scan_args(argc, argv, "01", &id) == 1) { + uid = NUM2INT(id); + } + else { + uid = getuid(); + } + pwd = getpwuid(uid); + if (pwd == 0) Fail("can't find user for %d", uid); + return setup_passwd(pwd); +#else + return Qnil; +#endif +} + +static VALUE +etc_getpwnam(obj, nam) + VALUE obj, nam; +{ +#ifdef HAVE_GETPWENT + struct passwd *pwd; + + Check_Type(nam, T_STRING); + pwd = getpwnam(RSTRING(nam)->ptr); + if (pwd == 0) Fail("can't find user for %s", RSTRING(nam)->ptr); + return setup_passwd(pwd); +#else + return Qnil; +#endif +} + +static VALUE +etc_passwd(obj) + VALUE obj; +{ +#if defined(HAVE_GETPWENT) && !defined(__CYGWIN32__) + struct passwd *pw; + + if (iterator_p()) { + setpwent(); + while (pw = getpwent()) { + rb_yield(setup_passwd(pw)); + } + endpwent(); + return obj; + } + pw = getpwent(); + if (pw == 0) Fail("can't fetch next -- /etc/passwd"); + return setup_passwd(pw); +#else + return Qnil; +#endif +} + +#ifdef HAVE_GETGRENT +static VALUE +setup_group(grp) + struct group *grp; +{ + VALUE mem; + char **tbl; + + mem = ary_new(); + tbl = grp->gr_mem; + while (*tbl) { + ary_push(mem, str_new2(*tbl)); + tbl++; + } + return struct_new(sGroup, + str_new2(grp->gr_name), + str_new2(grp->gr_passwd), + INT2FIX(grp->gr_gid), + mem); +} +#endif + +static VALUE +etc_getgrgid(obj, id) + VALUE obj, id; +{ +#ifdef HAVE_GETGRENT + int gid; + struct group *grp; + + gid = NUM2INT(id); + grp = getgrgid(gid); + if (grp == 0) Fail("can't find group for %d", gid); + return setup_group(grp); +#else + return Qnil; +#endif +} + +static VALUE +etc_getgrnam(obj, nam) + VALUE obj, nam; +{ +#ifdef HAVE_GETGRENT + struct group *grp; + + Check_Type(nam, T_STRING); + grp = getgrnam(RSTRING(nam)->ptr); + if (grp == 0) Fail("can't find group for %s", RSTRING(nam)->ptr); + return setup_group(grp); +#else + return Qnil; +#endif +} + +static VALUE +etc_group(obj) + VALUE obj; +{ +#ifdef HAVE_GETGRENT + struct group *grp; + + if (iterator_p()) { + setgrent(); + while (grp = getgrent()) { + rb_yield(setup_group(grp)); + } + endgrent(); + return obj; + } + return setup_group(getgrent()); +#else + return Qnil; +#endif +} + +static VALUE mEtc; + +void +Init_etc() +{ + mEtc = rb_define_module("Etc"); + + rb_define_module_function(mEtc, "getlogin", etc_getlogin, 0); + + rb_define_module_function(mEtc, "getpwuid", etc_getpwuid, -1); + rb_define_module_function(mEtc, "getpwnam", etc_getpwnam, 1); + rb_define_module_function(mEtc, "passwd", etc_passwd, 0); + + rb_define_module_function(mEtc, "getgrgid", etc_getgrgid, 1); + rb_define_module_function(mEtc, "getgrnam", etc_getgrnam, 1); + rb_define_module_function(mEtc, "group", etc_group, 0); + + sPasswd = struct_define("Passwd", + "name", "passwd", "uid", "gid", + "gecos", "dir", "shell", +#ifdef PW_CHANGE + "change", +#endif +#ifdef PW_QUOTA + "quota", +#endif +#ifdef PW_AGE + "age", +#endif +#ifdef PW_CLASS + "class", +#endif +#ifdef PW_COMMENT + "comment", +#endif +#ifdef PW_EXPIRE + "expire", +#endif + 0); + rb_global_variable(&sPasswd); + +#ifdef HAVE_GETGRENT + sGroup = struct_define("Group", "name", "passwd", "gid", "mem", 0); + rb_global_variable(&sGroup); +#endif +} diff --git a/ext/etc/etc.doc b/ext/etc/etc.doc new file mode 100644 index 0000000000..2af895c9de --- /dev/null +++ b/ext/etc/etc.doc @@ -0,0 +1,73 @@ +.\" etc.doc - -*- Indented-Text -*- created at: Fri Jul 14 00:47:15 JST 1995 + +** Etc(モジュール) + +/etcディレクトリ以下の情報を得るためのモジュール.クラスにインクルード +して使うこともできる. + +Methods: +Single Methods: + + getlogin + + 自分のlogin名を返す.これが失敗した場合はgetpwuid()を用いると + 良い. + + getpwnam(name) + + /etc/passwdファイル(あるいはDBMファイルやNISデータベース)を検 + 索し,nameの名前を持つpasswdエントリを返す.戻り値はpasswd構造 + 体で以下のメンバを持つ. + + struct passwd + name # ユーザ名(文字列) + passwd # パスワード(文字列) + uid # ユーザID(整数) + gid # グループID(整数) + gecos # gecosフィールド(文字列) + dir # ホームディレクトリ(文字列) + shell # ログインシェル(文字列) + # 以降のメンバはシステムによっては提供されない. + change # パスワード変更時間(整数) + quota # クォータ(整数) + age # エージ(整数) + class # ユーザアクセスクラス(文字列) + comment # コメント(文字列) + expire # アカウント有効期限(整数) + end + + 詳細はgetpwnam(3)を参照のこと. + + getpwuid([uid]) + + uidをユーザIDとするpasswdエントリを返す.戻り値はgetpwnam()と + 同様である.引数を省略した場合にはgetuid()の値を用いる.詳細は + getpwuid(3)を参照のこと. + + getgrgid(gid) + + /etc/groupファイル(あるいは…getpwnam参照)を検索し,gidをグルー + プIDとするグループエントリを返す.戻り値はgroup構造体で以下の + メンバを持つ. + + struct group + name # グループ名(文字列) + passwd # グループのパスワード(文字列) + gid # グループID(整数) + mem # グループメンバ名の配列 + end + + 詳細はgetgrgid(3)を参照のこと. + + getgrnam(name) + + nameという名前のグループエントリを返す.戻り値はgetgrgid()と同 + 様である.詳細はgetgrnam(3)を参照. + + group + + 全てのグループエントリを順にアクセスするためのイテレータ. + + passwd + + 全てのpasswdエントリを順にアクセスするためのイテレータ. diff --git a/ext/etc/extconf.rb b/ext/etc/extconf.rb new file mode 100644 index 0000000000..884de93ec8 --- /dev/null +++ b/ext/etc/extconf.rb @@ -0,0 +1,7 @@ +have_library("sun", "getpwnam") # NIS (== YP) interface for IRIX 4 +a = have_func("getlogin") +b = have_func("getpwent") +c = have_func("getgrent") +if a or b or c + create_makefile("etc") +end diff --git a/ext/extmk.rb.in b/ext/extmk.rb.in new file mode 100644 index 0000000000..78fe3070a2 --- /dev/null +++ b/ext/extmk.rb.in @@ -0,0 +1,468 @@ +#! /usr/local/bin/ruby + +if ARGV[0] == 'static' + $force_static = TRUE + ARGV.shift +elsif ARGV[0] == 'install' + $install = TRUE + ARGV.shift +elsif ARGV[0] == 'clean' + $clean = TRUE + ARGV.shift +end + +$extlist = [] + +$cache_mod = FALSE; +$lib_cache = {} +$func_cache = {} +$hdr_cache = {} +$topdir = "@top_srcdir@" +if $topdir !~ "^/" + # get absolute path + save = Dir.pwd + Dir.chdir ".." + $topdir = Dir.pwd + Dir.chdir save +end + +if File.exist?("config.cache") then + f = open("config.cache", "r") + while f.gets + case $_ + when /^lib: ([\w_]+) (yes|no)/ + $lib_cache[$1] = $2 + when /^func: ([\w_]+) (yes|no)/ + $func_cache[$1] = $2 + when /^hdr: (.+) (yes|no)/ + $hdr_cache[$1] = $2 + end + end + f.close +end + +def older(file1, file2) + if !File.exist?(file1) then + return TRUE + end + if !File.exist?(file2) then + return FALSE + end + if File.mtime(file1) < File.mtime(file2) + return TRUE + end + return FALSE +end + +if PLATFORM == "m68k-human" +CFLAGS = "@CFLAGS@".gsub(/-c..-stack=[0-9]+ */, '') +LINK = "@CC@ -o conftest -I#{$topdir} " + CFLAGS + " %s @LDFLAGS@ %s conftest.c @LIBS@ %s > nul 2>&1" +CPP = "@CPP@ @CPPFLAGS@ -I#{$topdir} " + CFLAGS + " %s conftest.c > nul 2>&1" +else +CFLAGS = "@CFLAGS@" +LINK = "@CC@ -o conftest -I#{$topdir} " + CFLAGS + " %s @LDFLAGS@ %s conftest.c %s > /dev/null 2>&1" +CPP = "@CPP@ @CPPFLAGS@ -I#{$topdir} " + CFLAGS + " %s conftest.c > /dev/null 2>&1" +end + +def try_link(libs) + system(format(LINK, $CFLAGS, $LDFLAGS, libs)) +end + +def try_cpp + system(format(CPP, $CFLAGS)) +end + +def have_library(lib, func) + if $lib_cache[lib] + if $lib_cache[lib] == "yes" + if $libs + $libs = "-l" + lib + " " + $libs + else + $libs = "-l" + lib + end + return TRUE + else + return FALSE + end + end + + cfile = open("conftest.c", "w") + cfile.printf "\ +int main() { return 0; } +int t() { %s(); return 0; } +", func + cfile.close + + begin + if $libs + libs = "-l" + lib + " " + $libs + else + libs = "-l" + lib + end + unless try_link(libs) + $lib_cache[lib] = 'no' + $cache_mod = TRUE + return FALSE + end + ensure + system "rm -f conftest*" + end + + $libs = libs + $lib_cache[lib] = 'yes' + $cache_mod = TRUE + return TRUE +end + +def have_func(func) + if $func_cache[func] + if $func_cache[func] == "yes" + $defs.push(format("-DHAVE_%s", func.upcase)) + return TRUE + else + return FALSE + end + end + + cfile = open("conftest.c", "w") + cfile.printf "\ +char %s(); +int main() { return 0; } +int t() { %s(); return 0; } +", func, func + cfile.close + + libs = $libs + libs = "" if libs == nil + + begin + unless try_link(libs) + $func_cache[func] = 'no' + $cache_mod = TRUE + return FALSE + end + ensure + system "rm -f conftest*" + end + $defs.push(format("-DHAVE_%s", func.upcase)) + $func_cache[func] = 'yes' + $cache_mod = TRUE + return TRUE +end + +def have_header(header) + if $hdr_cache[header] + if $hdr_cache[header] == "yes" + header.tr!("a-z./\055", "A-Z___") + $defs.push(format("-DHAVE_%s", header)) + return TRUE + else + return FALSE + end + end + + cfile = open("conftest.c", "w") + cfile.printf "\ +#include <%s> +", header + cfile.close + + begin + unless try_cpp + $hdr_cache[header] = 'no' + $cache_mod = TRUE + return FALSE + end + ensure + system "rm -f conftest*" + end + $hdr_cache[header] = 'yes' + header.tr!("a-z./\055", "A-Z___") + $defs.push(format("-DHAVE_%s", header)) + $cache_mod = TRUE + return TRUE +end + +def create_header() + if $defs.length > 0 + hfile = open("extconf.h", "w") + for line in $defs + line =~ /^-D(.*)/ + hfile.printf "#define %s 1\n", $1 + end + hfile.close + end +end + +def create_makefile(target) + + if $libs and "@DLEXT@" == "o" + libs = $libs.split + for lib in libs + lib.sub!(/-l(.*)/, '"lib\1.a"') + end + $defs.push(format("-DEXTLIB='%s'", libs.join(","))) + end + $libs = "" unless $libs + + $srcdir = $topdir + "/ext/" + target + mfile = open("Makefile", "w") + mfile.printf "\ +SHELL = /bin/sh + +#### Start of system configuration section. #### + +srcdir = #{$srcdir} +VPATH = #{$srcdir} + +CC = @CC@ + +CFLAGS = %s -I#{$topdir} %s #$CFLAGS %s +DLDFLAGS = @DLDFLAGS@ #$LDFLAGS +LDSHARED = @LDSHARED@ +", if $static then "" else "@CCDLFLAGS@" end, CFLAGS, $defs.join(" ") + + mfile.printf "\ + +program_transform_name = -e @program_transform_name@ +RUBY_INSTALL_NAME = `t='$(program_transform_name)'; echo ruby | sed $$t` + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +libdir = @libdir@/$(RUBY_INSTALL_NAME)/@arch@ +@SET_MAKE@ + +#### End of system configuration section. #### +" + mfile.printf "LOCAL_LIBS = %s\n", $local_libs if $local_libs + mfile.printf "LIBS = %s\n", $libs + mfile.printf "OBJS = " + if !$objs then + $objs = Dir["#{$topdir}/ext/#{target}/*.c"] + for f in $objs + f.sub!(/\.c$/, ".o") + end + end + mfile.printf $objs.join(" ") + mfile.printf "\n" + + dots = if "@INSTALL@" =~ /^\// then "" else "#{$topdir}/ext/" end + mfile.printf "\ +TARGET = %s.%s + +INSTALL = %s@INSTALL@ + +binsuffix = @binsuffix@ + +all: $(TARGET) + +clean:; @rm -f *.o *.so *.sl + @rm -f Makefile extconf.h conftest.* + @rm -f core ruby$(binsuffix) *~ + +realclean: clean +", target, + if $static then "o" else "@DLEXT@" end, dots + + if !$static + mfile.printf "\ + +install: + @test -d $(libdir) || mkdir $(libdir) + $(INSTALL) $(TARGET) $(libdir)/$(TARGET) +" + else + mfile.printf "\ + +install:; +" + end + + if !$static && "@DLEXT@" != "o" + mfile.printf "\ +$(TARGET): $(OBJS) + $(LDSHARED) $(DLDFLAGS) -o $(TARGET) $(OBJS) $(LOCAL_LIBS) $(LIBS) +" + elsif not File.exist?(target + ".c") + if PLATFORM == "m68k-human" + mfile.printf "\ +$(TARGET): $(OBJS) + ar cru $(TARGET) $(OBJS) +" + elsif PLATFORM =~ "-nextstep" + mfile.printf "\ +$(TARGET): $(OBJS) + cc -r $(CFLAGS) -o $(TARGET) $(OBJS) +" + elsif $static + mfile.printf "\ +$(TARGET): $(OBJS) + ld -r -o $(TARGET) $(OBJS) +" + else + mfile.printf "\ +$(TARGET): $(OBJS) + ld $(DLDFLAGS) -r -o $(TARGET) $(OBJS) +" + end + end + + if File.exist?("depend") + dfile = open("depend", "r") + mfile.printf "###\n" + while line = dfile.gets() + mfile.printf "%s", line + end + dfile.close + end + mfile.close + if $static + $extlist.push [$static,target] + end +end + +def extmake(target) + if $force_static or $static_ext[target] + $static = target + else + $static = FALSE + end + + return if $nodynamic and not $static + + $local_libs = nil + $libs = nil + $objs = nil + $CFLAGS = nil + $LDFLAGS = nil + + begin + system "mkdir " + target unless File.directory?(target) + Dir.chdir target + if $static_ext.size > 0 || + !File.exist?("./Makefile") || + older("./Makefile", "#{$topdir}/ext/@setup@") || + older("./Makefile", "../extmk.rb") || + older("./Makefile", "./extconf.rb") + then + $defs = [] + if File.exist?("extconf.rb") + load "extconf.rb" + else + create_makefile(target); + end + end + if File.exist?("./Makefile") + if $install + system "make install" + elsif $clean + system "make clean" + else + system "make all" + end + end + if $static + $extlibs += " " + $LDFLAGS if $LDFLAGS + $extlibs += " " + $local_libs if $local_libs + $extlibs += " " + $libs if $libs + end + ensure + Dir.chdir ".." + end +end + +# get static-link modules +$static_ext = {} +if File.file? "#{$topdir}/ext/@setup@" + f = open("#{$topdir}/ext/@setup@") + while f.gets() + $_.chop! + sub!(/#.*$/, '') + next if /^\s*$/ + if /^option +nodynamic/ + $nodynamic = TRUE + next + end + $static_ext[$_.split[0]] = TRUE + end + f.close +end + +for d in Dir["#{$topdir}/ext/*"] + File.directory?(d) || next + File.file?(d + "/MANIFEST") || next + + d = File.basename(d) + if $install + print "installing ", d, "\n" + elsif $clean + print "cleaning ", d, "\n" + else + print "compiling ", d, "\n" + end + extmake(d) +end + +if $cache_mod + f = open("config.cache", "w") + for k,v in $lib_cache + f.printf "lib: %s %s\n", k, v + end + for k,v in $func_cache + f.printf "func: %s %s\n", k, v + end + for k,v in $hdr_cache + f.printf "hdr: %s %s\n", k, v + end + f.close +end + +exit if $install or $clean +if $extlist.size > 0 + for s,t in $extlist + f = format("%s/%s.o", s, t) + if File.exist?(f) + $extinit += format("\ +\tInit_%s();\n\ +\trb_provide(\"%s.o\");\n\ +", t, t) + $extobjs += "ext/" + $extobjs += f + $extobjs += " " + else + FALSE + end + end + + if older("extinit.c", "#{$topdir}/ext/@setup@") + f = open("extinit.c", "w") + f.printf "void Init_ext() {\n" + f.printf $extinit + f.printf "}\n" + f.close + end + if older("extinit.o", "extinit.c") + cmd = "@CC@ " + CFLAGS + " -c extinit.c" + print cmd, "\n" + system cmd or exit 1 + end + + Dir.chdir ".." + + if older("ruby@binsuffix@", "#{$topdir}/ext/@setup@") or older("ruby@binsuffix@", "miniruby@binsuffix@") + `rm -f ruby@binsuffix@` + end + + $extobjs = "ext/extinit.o " + $extobjs + system format('make ruby@binsuffix@ EXTOBJS="%s" EXTLIBS="%s"', $extobjs, $extlibs) +else + Dir.chdir ".." + if older("ruby@binsuffix@", "miniruby@binsuffix@") + `rm -f ruby@binsuffix@` + `cp miniruby@binsuffix@ ruby@binsuffix@` + end +end + +#Local variables: +# mode: ruby +#end: diff --git a/ext/extmk.rb.nt b/ext/extmk.rb.nt new file mode 100644 index 0000000000..04b9e4071d --- /dev/null +++ b/ext/extmk.rb.nt @@ -0,0 +1,480 @@ +#! /usr/local/bin/ruby + +if ARGV[0] == 'static' + $force_static = TRUE + ARGV.shift +elsif ARGV[0] == 'install' + $install = TRUE + ARGV.shift +elsif ARGV[0] == 'clean' + $clean = TRUE + ARGV.shift +end + +$extlist = [] + +$cache_mod = FALSE; +$lib_cache = {} +$func_cache = {} +$hdr_cache = {} + +$dllopt = '-MD' + +if File.exist?("config.cache") then + f = open("config.cache", "r") + while f.gets + case $_ + when /^lib: ([\w_]+) (yes|no)/ + $lib_cache[$1] = $2 + when /^func: ([\w_]+) (yes|no)/ + $func_cache[$1] = $2 + when /^hdr: (.+) (yes|no)/ + $hdr_cache[$1] = $2 + end + end + f.close +end + +def older(file1, file2) + if !File.exist?(file1) then + return TRUE + end + if !File.exist?(file2) then + return FALSE + end + if File.mtime(file1) < File.mtime(file2) + return TRUE + end + return FALSE +end + +LINK = "cl -o conftest -I../.. -Zi -O -I. %s %s conftest.c %s > nul" +CPP = "cl -E -I../.. -I../../missing -I. -Zi -O %s conftest.c > nul" + +def try_link(libs) + system(format(LINK, $CFLAGS, $LDFLAGS, libs)) +end + +def try_cpp + system(format(CPP, $CFLAGS)) +end + +def have_library(lib, func) + if $lib_cache[lib] + if $lib_cache[lib] == "yes" + if $libs + $libs = "-l" + lib + " " + $libs + else + $libs = "-l" + lib + end + return TRUE + else + return FALSE + end + end + + cfile = open("conftest.c", "w") + cfile.printf "\ +int main() { return 0; } +int t() { %s(); return 0; } +", func + cfile.close + + begin + if $libs + libs = lib + ".lib " + $libs + else + libs = lib + ".lib" + end + unless try_link(libs) + $lib_cache[lib] = 'no' + $cache_mod = TRUE + return FALSE + end + ensure + system "rm -f conftest*" + end + + $libs = libs + $lib_cache[lib] = 'yes' + $cache_mod = TRUE + return TRUE +end + +def have_func(func) + if $func_cache[func] + if $func_cache[func] == "yes" + $defs.push(format("-DHAVE_%s", func.upcase)) + return TRUE + else + return FALSE + end + end + + cfile = open("conftest.c", "w") + cfile.printf "\ +char %s(); +int main() { return 0; } +int t() { %s(); return 0; } +", func, func + cfile.close + + libs = $libs + libs = "" if libs == nil + + begin + unless try_link(libs) + $func_cache[func] = 'no' + $cache_mod = TRUE + return FALSE + end + ensure + system "rm -f conftest*" + end + $defs.push(format("-DHAVE_%s", func.upcase)) + $func_cache[func] = 'yes' + $cache_mod = TRUE + return TRUE +end + +def have_header(header) + if $hdr_cache[header] + if $hdr_cache[header] == "yes" + header.tr!("a-z./\055", "A-Z___") + $defs.push(format("-DHAVE_%s", header)) + return TRUE + else + return FALSE + end + end + + cfile = open("conftest.c", "w") + cfile.printf "\ +#include <%s> +", header + cfile.close + + begin + unless try_cpp + $hdr_cache[header] = 'no' + $cache_mod = TRUE + return FALSE + end + ensure + system "rm -f conftest*" + end + $hdr_cache[header] = 'yes' + header.tr!("a-z./\055", "A-Z___") + $defs.push(format("-DHAVE_%s", header)) + $cache_mod = TRUE + return TRUE +end + +def create_header() + if $defs.length > 0 + hfile = open("extconf.h", "w") + for line in $defs + line =~ /^-D(.*)/ + hfile.printf "#define %s 1\n", $1 + end + hfile.close + end +end + +def create_makefile(target) + + if $libs and "obj" == "obj" + libs = $libs.split + for lib in libs + lib.sub!(/(.*)/, '"lib\1.lib"') + end + $defs.push(format("-DEXTLIB='%s'", libs.join(","))) + end + $libs = "" unless $libs + + mfile = open("Makefile", "w") + mfile.printf "\ +SHELL = $(COMPSEC) + +#### Start of system configuration section. #### + +srcdir = . +VPATH = . + +CC = cl + +CFLAGS = %s -I../.. -I../../missing -I. -O -DNT %s #$CFLAGS %s + +RUBYLIB = ../../ruby.lib +DLDFLAGS = /DLL +LDSHARED = +", if $static then "" else "-fpic" end, $dllopt, $defs.join(" ") + + if $force_static + print "static\n" + else + print "non static\n" + end + + mfile.printf "\ + +libdir = /usr/local/lib/ruby/i386-mswin32 + + +#### End of system configuration section. #### +" + mfile.printf "LOCAL_LIBS = %s\n", $local_libs if $local_libs + mfile.printf "LIBS = %s\n", $libs + mfile.printf "OBJS = " + if !$objs then + $objs = Dir["*.c"] + for f in $objs + f.sub!(/\.c$/, ".obj") + end + end + mfile.printf $objs.join(" ") + mfile.printf "\n" + + dots = if "ginstall -c" =~ /^\// then "" else "../" end + mfile.printf "\ +TARGET = %s.%s + +INSTALL = %sginstall -c + +DEFFILE = %s.def + +all: $(TARGET) + +clean:; @rm -f *.obj *.lib *.exp *.pdb *.bak + @rm -f Makefile extconf.h conftest.* + +realclean: clean +", target, + if $force_static then "lib" else "dll" end, dots, target + + if !$static + mfile.printf "\ + +install: $(libdir)/$(TARGET) + +$(libdir)/$(TARGET): $(TARGET) + @test -d $(libdir) || mkdir $(libdir) + $(INSTALL) $(TARGET) $(libdir)/$(TARGET) +" + else + mfile.printf "\ + +install:; +" + end + + if $force_static + mfile.printf "\ +$(TARGET): $(OBJS) + lib /OUT:$(TARGET) $(OBJS) +" + else + mfile.printf "\ +$(DEFFILE): + echo $(DEFFILE) + +$(TARGET): $(OBJS) $(DEFFILE) + cl -DLL -o$(TARGET) $(OBJS) $(RUBYLIB) -link /DEF:$(DEFFILE) +" + end + + if File.exist?("depend") + dfile = open("depend", "r") + mfile.printf "###\n" + while line = dfile.gets() + mfile.printf "%s", line + end + dfile.close + end + mfile.close + if $static + printf format("push %s,%s\n", $static, target); ##debug print## + $extlist.push [$static,target] + end +end + +def extmake(target) + if $force_static or $static_ext[target] + $static = target + else + $static = FALSE + end + + return if $nodynamic and not $static + + $local_libs = nil + $libs = nil + $objs = nil + $CFLAGS = nil + $LDFLAGS = nil + + begin + Dir.chdir target + if $static_ext.size > 0 || + !File.exist?("./Makefile") || + older("./Makefile", "../Setup") || + older("./Makefile", "../extmk.rb") || + older("./Makefile", "./extconf.rb") + then + $defs = [] + if File.exist?("extconf.rb") + load "extconf.rb" + else + create_makefile(target); + end + end + if File.exist?("./Makefile") + if $install + system "nmake install" + if File.directory? "./lib" + for i in Dir["./lib/*.rb"] + system "ginstall -c #{i} /usr/local/lib/ruby/i386-mswin32" + end + end + elsif $clean + system "nmake clean" + else + system "nmake all" + end + end + if $static + $extlibs += " " + $LDFLAGS if $LDFLAGS + $extlibs += " " + $local_libs if $local_libs + $extlibs += " " + $libs if $libs + end + ensure + Dir.chdir ".." + end +end + +# get static-link modules +$static_ext = {} +if File.file? "./Setup" + f = open("./Setup") + while f.gets() + $_.chop! + sub!(/#.*$/, '') + next if /^\s*$/ + print $_, "\n" + if /^option +nodynamic/ + $nodynamic = TRUE + next + end + $static_ext[$_.split[0]] = TRUE + end + f.close +end + +for d in Dir["*"] + File.directory?(d) || next + File.file?(d + "/MANIFEST") || next + + d = $1 if d =~ /\/([\/]*)$/ + if $install + print "installing ", d, "\n" + elsif $clean + print "cleaning ", d, "\n" + else + print "compiling ", d, "\n" + end + extmake(d) +end + +if $cache_mod + f = open("config.cache", "w") + for k,v in $lib_cache + f.printf "lib: %s %s\n", k, v + end + for k,v in $func_cache + f.printf "func: %s %s\n", k, v + end + for k,v in $hdr_cache + f.printf "hdr: %s %s\n", k, v + end + f.close +end + +exit if $install or $clean +if $extlist.size > 0 + #for s,t in $extlist + for s,t in $static_ext + #f = format("%s/%s.obj", s, t) + #f = format("%s/%s.obj", s, s) + l = format("%s/%s.lib", s, s) + #print format("%s/%s.obj\n", s, s) ##debug print## + if File.exist?(l) + $extinit += format("\ +\tInit_%s();\n\ +\trb_provide(\"%s.o\");\n\ +", s, s) + $extobjs += "ext/" + #$extobjs += f # *.obj + $extobjs += l # *.lib + $extobjs += " " + else + FALSE + end + end + + if older("extinit.c", "Setup") + f = open("extinit.c", "w") + f.printf "void Init_ext() {\n" + f.printf $extinit + f.printf "}\n" + f.close + end + if older("extinit.obj", "extinit.c") + cmd = "cl -Zi -O -I. -c extinit.c" + print cmd, "\n" + system cmd or exit 1 + end + + Dir.chdir ".." + + if older("ruby.exe", "ext/Setup") or older("ruby.exe", "miniruby.exe") + `rm -f ruby.exe` + end + + $extobjs = "ext/extinit.obj " + $extobjs + $extlibs = "" + system format('nmake ruby.exe EXTOBJS="%s" EXTLIBS="%s"', $extobjs, $extlibs) +else + Dir.chdir ".." + if older("ruby.exe", "miniruby.exe") + `rm -f ruby.exe` + `cp miniruby.exe ruby.exe` + end +end + +#template of .def file. +#LIBRARY kconv.dll +#CODE LOADONCALL +#DATA LOADONCALL +#DESCRIPTION 'win32 kconv.dll' +#EXPORTS +# +# Init_kconv +def makedef(basename) + defname = sprintf("%s.def", basename) + f = open(defname, "w") + f.printf "\ +LIBRARY %s.dll +CODE LOADONCALL +DATA LOADONCALL +DESCRIPTION 'win32 %s.dll' +EXPORTS + + Init_%s +", basename, basename + f.close + +end + +#Local variables: +# mode: ruby +#end: diff --git a/ext/fcntl/MANIFEST b/ext/fcntl/MANIFEST new file mode 100644 index 0000000000..aef7ad4ca0 --- /dev/null +++ b/ext/fcntl/MANIFEST @@ -0,0 +1,3 @@ +MANIFEST +depend +fcntl.c diff --git a/ext/fcntl/depend b/ext/fcntl/depend new file mode 100644 index 0000000000..3c7ef8485e --- /dev/null +++ b/ext/fcntl/depend @@ -0,0 +1 @@ +fcntl.o: fcntl.c ../../ruby.h ../../config.h ../../defines.h diff --git a/ext/fcntl/fcntl.c b/ext/fcntl/fcntl.c new file mode 100644 index 0000000000..17aacb13c3 --- /dev/null +++ b/ext/fcntl/fcntl.c @@ -0,0 +1,106 @@ +/************************************************ + + fcntl.c - + + $Author$ + created at: Mon Apr 7 18:53:05 JST 1997 + + Copyright (C) 1997 Yukihiro Matsumoto + +************************************************/ + +/************************************************ += NAME + +fcntl - load the C fcntl.h defines + += SYNOPSIS + + require "fcntl" + m = s.fcntl(Fcntl::F_GETFL, 0) + f.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK|m) + += DESCRIPTION + +This module is just a translation of the C <fnctl.h> file. + += NOTE + +Only #define symbols get translated; you must still correctly +pack up your own arguments to pass as args for locking functions, etc. + +************************************************/ + +#include "ruby.h" +#include <fcntl.h> + +Init_fcntl() +{ + VALUE mFcntl = rb_define_module("Fcntl"); +#ifdef F_DUPFD + rb_define_const(mFcntl, "F_DUPFD", INT2NUM(F_DUPFD)); +#endif +#ifdef F_GETFD + rb_define_const(mFcntl, "F_GETFD", INT2NUM(F_GETFD)); +#endif +#ifdef F_GETLK + rb_define_const(mFcntl, "F_GETLK", INT2NUM(F_GETLK)); +#endif +#ifdef F_SETFD + rb_define_const(mFcntl, "F_SETFD", INT2NUM(F_SETFD)); +#endif +#ifdef F_GETFL + rb_define_const(mFcntl, "F_GETFL", INT2NUM(F_GETFL)); +#endif +#ifdef F_SETFL + rb_define_const(mFcntl, "F_SETFL", INT2NUM(F_SETFL)); +#endif +#ifdef F_SETLK + rb_define_const(mFcntl, "F_SETLK", INT2NUM(F_SETLK)); +#endif +#ifdef F_SETLKW + rb_define_const(mFcntl, "F_SETLKW", INT2NUM(F_SETLKW)); +#endif +#ifdef FD_CLOEXEC + rb_define_const(mFcntl, "FD_CLOEXEC", INT2NUM(FD_CLOEXEC)); +#endif +#ifdef F_RDLCK + rb_define_const(mFcntl, "F_RDLCK", INT2NUM(F_RDLCK)); +#endif +#ifdef F_UNLCK + rb_define_const(mFcntl, "F_UNLCK", INT2NUM(F_UNLCK)); +#endif +#ifdef F_WRLCK + rb_define_const(mFcntl, "F_WRLCK", INT2NUM(F_WRLCK)); +#endif +#ifdef O_CREAT + rb_define_const(mFcntl, "O_CREAT", INT2NUM(O_CREAT)); +#endif +#ifdef O_EXCL + rb_define_const(mFcntl, "O_EXCL", INT2NUM(O_EXCL)); +#endif +#ifdef O_NOCTTY + rb_define_const(mFcntl, "O_NOCTTY", INT2NUM(O_NOCTTY)); +#endif +#ifdef O_TRUNC + rb_define_const(mFcntl, "O_TRUNC", INT2NUM(O_TRUNC)); +#endif +#ifdef O_APPEND + rb_define_const(mFcntl, "O_APPEND", INT2NUM(O_APPEND)); +#endif +#ifdef O_NONBLOCK + rb_define_const(mFcntl, "O_NONBLOCK", INT2NUM(O_NONBLOCK)); +#endif +#ifdef O_NDELAY + rb_define_const(mFcntl, "O_NDELAY", INT2NUM(O_NDELAY)); +#endif +#ifdef O_RDONLY + rb_define_const(mFcntl, "O_RDONLY", INT2NUM(O_RDONLY)); +#endif +#ifdef O_RDWR + rb_define_const(mFcntl, "O_RDWR", INT2NUM(O_RDWR)); +#endif +#ifdef O_WRONLY + rb_define_const(mFcntl, "O_WRONLY", INT2NUM(O_WRONLY)); +#endif +} diff --git a/ext/kconv/MANIFEST b/ext/kconv/MANIFEST new file mode 100644 index 0000000000..cb89e8239c --- /dev/null +++ b/ext/kconv/MANIFEST @@ -0,0 +1,3 @@ +MANIFEST +depend +kconv.c diff --git a/ext/kconv/depend b/ext/kconv/depend new file mode 100644 index 0000000000..e87b1fdea7 --- /dev/null +++ b/ext/kconv/depend @@ -0,0 +1 @@ +kconv.o: kconv.c ../../ruby.h ../../config.h ../../defines.h diff --git a/ext/kconv/kconv.c b/ext/kconv/kconv.c new file mode 100644 index 0000000000..3c59cb6acf --- /dev/null +++ b/ext/kconv/kconv.c @@ -0,0 +1,1881 @@ +/** Network Kanji Filter. (PDS Version) +************************************************************************ +** Copyright (C) 1987, Fujitsu LTD. (Itaru ICHIKAWA) +** 連絡先: (株)富士通研究所 ソフト3研 市川 至 +** (E-Mail Address: ichikawa@flab.fujitsu.co.jp) +** Copyright (C) 1996 +** 連絡先: 琉球大学情報工学科 河野 真治 mine/X0208 support +** (E-Mail Address: kono@ie.u-ryukyu.ac.jp) +** 連絡先: COW for DOS & Win16 & Win32 & OS/2 +** (E-Mail Address: GHG00637@niftyserve.or.jp) +** 営利を目的としない限り、このソースのいかなる +** 複写,改変,修正も許諾します。その際には、この部分を残すこと。 +** このプログラムについては特に何の保証もしない、悪しからず。 +** Everyone is permitted to do anything on this program +** including copying, modifying, improving +** as long as you don't try to make money off it, +** or pretend that you wrote it. +** i.e., the above copyright notice has to appear in all copies. +** THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE. +***********************************************************************/ + +/*********************************************************************** +** 1996/03/10 modified for Kconv - by Ikuo Nakagawa +***********************************************************************/ +/*********************************************************************** +** 1996/12/18 modified for kconv(ruby) - by matz@ruby.club.or.jp +***********************************************************************/ + +static char *CopyRight = + "Copyright (C) 1987, FUJITSU LTD. (I.Ichikawa),1996 S. Kono, COW"; +static char *Version = + "1.62"; +static char *Patchlevel = + "5/9612/Shinji Kono, COW matz"; + +#include "ruby.h" + +/* +** +** +** +** USAGE: nkf [flags] [file] +** +** Flags: +** b Output is bufferred (DEFAULT) +** u Output is unbufferred +** +** t no operation +** +** j Outout code is JIS 7 bit (DEFAULT SELECT) +** s Output code is MS Kanji (DEFAULT SELECT) +** e Output code is AT&T JIS (DEFAULT SELECT) +** l Output code is JIS 7bit and ISO8859-1 Latin-1 +** +** m MIME conversion for ISO-2022-JP +** i_ Output sequence to designate JIS-kanji (DEFAULT_J) +** o_ Output sequence to designate single-byte roman characters (DEFAULT_R) +** +** r {de/en}crypt ROT13/47 +** +** v display Version +** +** T Text mode output (for MS-DOS) +** +** x Do not convert X0201 kana into X0208 +** Z Convert X0208 alphabet to ASCII +** +** f60 fold option +** +** m MIME decode +** B try to fix broken JIS, missing Escape +** B[1-9] broken level +** +** O Output to 'nkf.out' file +** d Delete \r in line feed +** c Add \r in line feed +**/ +/******************************/ +/* デフォルトの出力コード選択 */ +/* Select DEFAULT_CODE */ +#define DEFAULT_CODE_JIS +/* #define DEFAULT_CODE_SJIS */ +/* #define DEFAULT_CODE_EUC */ +/******************************/ + +/* for Kconv: _AUTO, _EUC, _SJIS, _JIS */ +#define _AUTO 0 +#define _JIS 1 +#define _EUC 2 +#define _SJIS 3 + +#if (defined(__TURBOC__) || defined(LSI_C)) && !defined(MSDOS) +#define MSDOS +#endif + +#include <stdio.h> + +#if defined(MSDOS) || defined(__OS2__) +#include <stdlib.h> +#include <fcntl.h> +#include <io.h> +#endif + +#ifdef MSDOS +#ifdef LSI_C +#define setbinmode(fp) fsetbin(fp) +#else /* Microsoft C, Turbo C */ +#define setbinmode(fp) setmode(fileno(fp), O_BINARY) +#endif +#else /* UNIX,OS/2 */ +#define setbinmode(fp) +#endif + +#ifdef _IOFBF /* SysV and MSDOS */ +#define setvbuffer(fp, buf, size) setvbuf(fp, buf, _IOFBF, size) +#else /* BSD */ +#define setvbuffer(fp, buf, size) setbuffer(fp, buf, size) +#endif + +/*Borland C++ 4.5 EasyWin*/ +#if defined(__TURBOC__) && defined(_Windows) && !defined(__WIN32__) /*Easy Win */ +#define EASYWIN +#include <windows.h> +#endif + +#ifndef FALSE +#define FALSE 0 +#define TRUE 1 +#endif + +/* state of output_mode and input_mode */ + +#define ASCII 0 +#define X0208 1 +#define X0201 2 +#define NO_X0201 3 +#define JIS_INPUT 4 +#define SJIS_INPUT 5 +#define LATIN1_INPUT 6 +#define FIXED_MIME 7 +#define DOUBLE_SPACE -2 + +#define NL 0x0a +#define ESC 0x1b +#define SP 0x20 +#define AT 0x40 +#define SSP 0xa0 +#define DEL 0x7f +#define SI 0x0f +#define SO 0x0e +#define SSO 0x8e + +#define HOLD_SIZE 32 +#define IOBUF_SIZE 16384 + +#define DEFAULT_J 'B' +#define DEFAULT_R 'B' + +#define SJ0162 0x00e1 /* 01 - 62 ku offset */ +#define SJ6394 0x0161 /* 63 - 94 ku offset */ + + +/* MIME preprocessor */ + +#define _GETC() (*inptr ? (int)(*inptr++) : EOF) +#define _UNGETC(c) (*--inptr = (c)) +#define PUTCHAR(c) (outlen + 1 < outsiz ? \ + ((outptr[outlen++] = (c)), (outptr[outlen] = '\0')) : EOF) +#define GETC() ((!mime_mode)?_GETC():mime_getc()) +#define UNGETC(c) ((!mime_mode)?_UNGETC(c):mime_ungetc(c)) + +#ifdef EASYWIN /*Easy Win */ +extern POINT _BufferSize; +#endif + +/* buffers */ + +static unsigned char hold_buf[HOLD_SIZE*2]; +static int hold_count; +static unsigned char *inptr; +static char *outptr; +static int outsiz; +static int outlen; + +/* MIME preprocessor fifo */ + +#define MIME_BUF_SIZE (1024) /* 2^n ring buffer */ +#define MIME_BUF_MASK (MIME_BUF_SIZE-1) +#define Fifo(n) mime_buf[(n)&MIME_BUF_MASK] +static unsigned char mime_buf[MIME_BUF_SIZE]; +static unsigned int mime_top = 0; +static unsigned int mime_last = 0; /* decoded */ +static unsigned int mime_input = 0; /* undecoded */ + +/* flags */ +static int unbuf_f = FALSE; +static int estab_f = FALSE; +static int rot_f = FALSE; /* rot14/43 mode */ +static int input_f = FALSE; /* non fixed input code */ +static int alpha_f = FALSE; /* convert JIx0208 alphbet to ASCII */ +static int mime_f = FALSE; /* convert MIME B base64 or Q */ +static int mimebuf_f = FALSE; /* MIME buffered input */ +static int broken_f = FALSE; /* convert ESC-less broken JIS */ +static int iso8859_f = FALSE; /* ISO8859 through */ +#if defined(MSDOS) || defined(__OS2__) +static int x0201_f = TRUE; /* Assume JISX0201 kana */ +#else +static int x0201_f = NO_X0201; /* Assume NO JISX0201 */ +#endif + +/* X0208 -> ASCII converter */ + +static int c1_return; + +/* fold parameter */ +static int line = 0; /* chars in line */ +static int prev = 0; +static int fold_f = FALSE; +static int fold_len = 0; + +/* options */ +static char kanji_intro = DEFAULT_J, + ascii_intro = DEFAULT_R; + +/* Folding */ + +#define FOLD_MARGIN 10 +#define DEFAULT_FOLD 60 + +/* Global states */ +static int output_mode = ASCII, /* output kanji mode */ + input_mode = ASCII, /* input kanji mode */ + shift_mode = FALSE; /* TRUE shift out, or X0201 */ +static int mime_mode = FALSE; /* MIME mode B base64, Q hex */ + +/* X0208 -> ASCII translation table */ +/* X0201 / X0208 conversion tables */ + +/* X0201 kana conversion table */ +/* 90-9F A0-DF */ +static unsigned char cv[]= { +0x21,0x21,0x21,0x23,0x21,0x56,0x21,0x57, +0x21,0x22,0x21,0x26,0x25,0x72,0x25,0x21, +0x25,0x23,0x25,0x25,0x25,0x27,0x25,0x29, +0x25,0x63,0x25,0x65,0x25,0x67,0x25,0x43, +0x21,0x3c,0x25,0x22,0x25,0x24,0x25,0x26, +0x25,0x28,0x25,0x2a,0x25,0x2b,0x25,0x2d, +0x25,0x2f,0x25,0x31,0x25,0x33,0x25,0x35, +0x25,0x37,0x25,0x39,0x25,0x3b,0x25,0x3d, +0x25,0x3f,0x25,0x41,0x25,0x44,0x25,0x46, +0x25,0x48,0x25,0x4a,0x25,0x4b,0x25,0x4c, +0x25,0x4d,0x25,0x4e,0x25,0x4f,0x25,0x52, +0x25,0x55,0x25,0x58,0x25,0x5b,0x25,0x5e, +0x25,0x5f,0x25,0x60,0x25,0x61,0x25,0x62, +0x25,0x64,0x25,0x66,0x25,0x68,0x25,0x69, +0x25,0x6a,0x25,0x6b,0x25,0x6c,0x25,0x6d, +0x25,0x6f,0x25,0x73,0x21,0x2b,0x21,0x2c, +0x00,0x00}; + + +/* X0201 kana conversion table for daguten */ +/* 90-9F A0-DF */ +static unsigned char dv[]= { +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,0x25,0x2c,0x25,0x2e, +0x25,0x30,0x25,0x32,0x25,0x34,0x25,0x36, +0x25,0x38,0x25,0x3a,0x25,0x3c,0x25,0x3e, +0x25,0x40,0x25,0x42,0x25,0x45,0x25,0x47, +0x25,0x49,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x25,0x50,0x25,0x53, +0x25,0x56,0x25,0x59,0x25,0x5c,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}; + +/* X0201 kana conversion table for han-daguten */ +/* 90-9F A0-DF */ +static unsigned char ev[]= { +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,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,0x25,0x51,0x25,0x54, +0x25,0x57,0x25,0x5a,0x25,0x5d,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}; + + +/* X0208 kigou conversion table */ +/* 0x8140 - 0x819e */ +static unsigned char fv[] = { +0x00,0x00,0x00,0x00,0x2c,0x2e,0x00,0x3a, +0x3b,0x3f,0x21,0x00,0x00,0x27,0x60,0x00, +0x5e,0x00,0x5f,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x2d,0x00,0x2f, +0x5c,0x00,0x00,0x7c,0x00,0x00,0x60,0x27, +0x22,0x22,0x28,0x29,0x00,0x00,0x5b,0x5d, +0x7b,0x7d,0x3c,0x3e,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x2b,0x2d,0x00,0x00, +0x00,0x3d,0x00,0x3c,0x3e,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x24,0x00,0x00,0x25,0x23,0x26,0x2a,0x40, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +} ; + + +/* This converts =?ISO-2022-JP?B?HOGE HOGE?= */ + +static unsigned char *mime_pattern[] = { + (unsigned char *)"\075?ISO-8859-1?Q?", + (unsigned char *)"\075?ISO-2022-JP?B?", + (unsigned char *)"\075?ISO-2022-JP?Q?", + NULL +}; + +static int mime_encode[] = { + 'Q', 'B', 'Q', + 0 +}; + +static int add_cr = FALSE; +static int del_cr = FALSE; + +static void (*iconv) _((register int c2,register int c1)); /* s_iconv or oconv */ +static void (*oconv) _((register int c2,register int c1)); /* [ejs]_oconv */ +static int do_kconv _((char *i, char *o, int siz, int out_code, int in_code)); +static void h_conv _((register int c2,register int c1)); +static int push_hold_buf _((int c2,int c1)); +static void s_iconv _((register int c2,register int c1)); +static void e_oconv _((register int c2,register int c1)); +static void s_oconv _((register int c2,register int c1)); +static void j_oconv _((register int c2,register int c1)); +static int fold _((register int c2,register int c1)); +static int pre_convert _((register int c1,register int c2)); +static int mime_begin _((void)); +static int mime_getc _((void)); +static int mime_ungetc _((unsigned int c)); +static int mime_integrity _((unsigned char *p)); +static int base64decode _((int c)); + +#ifdef notdef +main (argc, argv) + int argc; + char **argv; +{ + register FILE *fin; + register char *cp; + +#ifdef EASYWIN /*Easy Win */ + _BufferSize.y = 400;/*Set Scroll Buffer Size*/ +#endif +#ifdef DEFAULT_CODE_JIS + oconv = j_oconv; /* DEFAULT Code is JIS */ +#endif +#ifdef DEFAULT_CODE_SJIS + oconv = s_oconv; /* DEFAULT Code is S-JIS */ +#endif +#ifdef DEFAULT_CODE_EUC + oconv = e_oconv; /* DEFAULT Code is EUC */ +#endif + + for (argc--,argv++; (argc > 0) && **argv == '-'; argc--, argv++) { + cp = *argv; + while (*cp) { + switch (*cp++) { + case 'b': /* buffered mode */ + unbuf_f = FALSE; + continue; + case 'u': /* non bufferd mode */ + unbuf_f = TRUE; + continue; + case 'j': /* JIS output */ + case 'n': + oconv = j_oconv; + continue; + case 'e': /* AT&T EUC output */ + oconv = e_oconv; + continue; + case 's': /* SJIS output */ + oconv = s_oconv; + continue; + case 'l': /* ISO8859 Latin-1 support, no conversion */ + iso8859_f = TRUE; /* Only compatible with ISO-2022-JP */ + input_f = LATIN1_INPUT; + continue; + case 'i': /* Kanji IN ESC-$-@/B */ + if(*cp=='@'||*cp=='B') + kanji_intro = *cp++; + continue; + case 'o': /* ASCII IN ESC-(-J/B */ + if(*cp=='J'||*cp=='B'||*cp=='H') + ascii_intro = *cp++; + continue; + case 'r': + rot_f = TRUE; + continue; +#if defined(MSDOS) || defined(__OS2__) + case 'T': + binmode_f = FALSE; + continue; +#endif + case 'v': + usage(); + exit(1); + break; + /* Input code assumption */ + case 'J': /* JIS input */ + case 'E': /* AT&T EUC input */ + input_f = JIS_INPUT; + continue; + case 'S': /* MS Kanji input */ + input_f = SJIS_INPUT; + if(x0201_f==NO_X0201) x0201_f=TRUE; + continue; + case 'Z': /* Convert X0208 alphabet to asii */ + /* bit:0 Convert X0208 + bit:1 Convert Kankaku to one space + bit:2 Convert Kankaku to two spaces + */ + if('9'>= *cp && *cp>='0') + alpha_f |= 1<<(*cp++ -'0'); + else + alpha_f |= TRUE; + continue; + case 'x': /* Convert X0201 kana to X0208 or X0201 Conversion */ + x0201_f = FALSE; /* No X0201->X0208 conversion */ + /* accept X0201 + ESC-(-I in JIS, EUC, MS Kanji + SI/SO in JIS, EUC, MS Kanji + SSO in EUC, JIS, not in MS Kanji + MS Kanji (0xa0-0xdf) + output X0201 + ESC-(-I in JIS (0x20-0x5f) + SSO in EUC (0xa0-0xdf) + 0xa0-0xd in MS Kanji (0xa0-0xdf) + */ + continue; + case 'X': /* Assume X0201 kana */ + /* Default value is NO_X0201 for EUC/MS-Kanji mix */ + x0201_f = TRUE; + continue; + case 'f': /* folding -f60 or -f */ + fold_f = TRUE; + fold_len = atoi(cp); + if(!(0<fold_len && fold_len<BUFSIZ)) + fold_len = DEFAULT_FOLD; + while('0'<= *cp && *cp <='9') cp++; + continue; + case 'm': /* MIME support */ + mime_f = TRUE; + if(*cp=='B'||*cp=='Q') { + mime_mode = *cp++; + mimebuf_f = FIXED_MIME; + } + continue; + case 'B': /* Broken JIS support */ + /* bit:0 no ESC JIS + bit:1 allow any x on ESC-(-x or ESC-$-x + bit:2 reset to ascii on NL + */ + if('9'>= *cp && *cp>='0') + broken_f |= 1<<(*cp++ -'0'); + else + broken_f |= TRUE; + continue; + case 'O':/* for Output file */ + file_out = TRUE; + continue; + case 'c':/* add cr code */ + add_cr = TRUE; + continue; + case 'd':/* delete cr code */ + del_cr = TRUE; + continue; + default: + /* bogus option but ignored */ + continue; + } + } + } + + if(iso8859_f && (oconv != j_oconv || !x0201_f )) { + fprintf(stderr,"Mixed ISO8859/JISX0201/SJIS/EUC output is not allowed.\n"); + exit(1); + } + + if(binmode_f == TRUE) +#ifdef __OS2__ + if(freopen("","wb",stdout) == NULL) + return (-1); +#else + setbinmode(stdout); +#endif + + if(unbuf_f) + setbuf (stdout, (char *) NULL); + else + setvbuffer (stdout, stdobuf, IOBUF_SIZE); + + if(argc == 0) { + if(binmode_f == TRUE) +#ifdef __OS2__ + if(freopen("","rb",stdin) == NULL) return (-1); +#else + setbinmode(stdin); +#endif + setvbuffer (stdin, stdibuf, IOBUF_SIZE); + convert (stdin); + } else { + while (argc--) { + if((fin = fopen (*argv++, "r")) == NULL) { + perror (*--argv); + return (-1); + } else { +/* reopen file for stdout */ + if(file_out == TRUE){ + if(argc == 1 ) { + if(freopen(*argv++, "w", stdout) == NULL) { + perror (*--argv); + return (-1); + } + argc--; + } else { + if(freopen("nkf.out", "w", stdout) == NULL) { + perror (*--argv); + return (-1); + } + } + if(binmode_f == TRUE) { +#ifdef __OS2__ + if(freopen("","wb",stdout) == NULL) + return (-1); +#else + setbinmode(stdout); +#endif + } + } + if(binmode_f == TRUE) +#ifdef __OS2__ + if(freopen("","rb",fin) == NULL) + return (-1); +#else + setbinmode(fin); +#endif + setvbuffer (fin, stdibuf, IOBUF_SIZE); + convert (fin); + fclose (fin); + } + } + } +#ifdef EASYWIN /*Easy Win */ + if(file_out == FALSE) + scanf("%d",&end_check); + else + fclose(stdout); +#else /* for Other OS */ + if(file_out == TRUE) + fclose(stdout); +#endif + return (0); +} +#endif /* notdef */ + +static int +do_kconv(i, o, siz, out_code, in_code) + char *i; + char *o; + int siz, out_code, in_code; +{ + register int c1, + c2; + + c2 = 0; + + if (siz <= 0) { + return 0; + } + *o = '\0'; + + inptr = (unsigned char *)i; /* input buffer */ + outptr = o; /* output buffer */ + outsiz = siz; /* output buffer size */ + outlen = 0; /* current length of output string */ + x0201_f = FALSE; /* don't assume JISX0201 kana */ + rot_f = FALSE; /* rot14/43 mode */ + input_f = FALSE; /* non fixed input code */ + alpha_f = FALSE; /* convert JISX0208 alphbet to ASCII */ + mime_f = TRUE; /* convert MIME base64 */ + broken_f = FALSE; /* convert ESC-less broken JIS */ + + switch (out_code) { + case _SJIS: + oconv = s_oconv; + break; + case _EUC: + oconv = e_oconv; + break; + default: + oconv = j_oconv; + break; + } + + switch (in_code) { + case _SJIS: + input_f = SJIS_INPUT; + x0201_f = TRUE; + break; + case _EUC: + case _JIS: + input_f = JIS_INPUT; + break; + default: + input_f = FALSE; + break; + } + + if(input_f == JIS_INPUT || input_f == LATIN1_INPUT) { + estab_f = TRUE; iconv = oconv; + } else if(input_f == SJIS_INPUT) { + estab_f = TRUE; iconv = s_iconv; + } else { + estab_f = FALSE; iconv = oconv; + } + input_mode = ASCII; + output_mode = ASCII; + shift_mode = FALSE; + mime_mode = FALSE; + +#define NEXT continue /* no output, get next */ +#define SEND ; /* output c1 and c2, get next */ +#define LAST break /* end of loop, go closing */ + + while ((c1 = GETC()) != EOF) { + if(!c2 && !input_mode && c1<DEL && !mime_mode && !output_mode + && !shift_mode && !fold_f && !rot_f) { + /* plain ASCII tight loop, no conversion and no fold */ + while(c1!='=' && c1!=SO && c1!=EOF && + c1!=ESC && c1!='$' && c1<DEL && c1!='\r' && c1!='\n') { + PUTCHAR(c1); + c1 = _GETC(); + } + if(c1==EOF) LAST; + } + if(c2) { + /* second byte */ + if(c2 > DEL) { + /* in case of 8th bit is on */ + if(!estab_f) { + /* in case of not established yet */ + if(c1 > SSP) { + /* It is still ambiguious */ + h_conv (c2, c1); + c2 = 0; + NEXT; + } else if(c1 < AT) { + /* ignore bogus code */ + c2 = 0; + NEXT; + } else { + /* established */ + /* it seems to be MS Kanji */ + estab_f = TRUE; + iconv = s_iconv; + SEND; + } + } else + /* in case of already established */ + if(c1 < AT) { + /* ignore bogus code */ + c2 = 0; + NEXT; + } else + SEND; + } else + /* 7 bit code */ + /* it might be kanji shitfted */ + if((c1 == DEL) || (c1 <= SP)) { + /* ignore bogus first code */ + c2 = 0; + NEXT; + } else + SEND; + } else { + /* first byte */ + if(c1 > DEL) { + /* 8 bit code */ + if(!estab_f && !iso8859_f) { + /* not established yet */ + if(c1 < SSP) { + /* it seems to be MS Kanji */ + estab_f = TRUE; + iconv = s_iconv; + } else if(c1 < 0xe0) { + /* it seems to be EUC */ + estab_f = TRUE; + iconv = oconv; + } else { + /* still ambiguious */ + } + c2 = c1; + NEXT; + } else { /* estab_f==TRUE */ + if(iso8859_f) { + SEND; + } else if(SSP<=c1 && c1<0xe0 && iconv == s_iconv) { + /* SJIS X0201 Case... */ + /* This is too arrogant, but ... */ + if(x0201_f==NO_X0201) { + iconv = oconv; + c2 = c1; + NEXT; + } else + if(x0201_f) { + if(dv[(c1-SSP)*2]||ev[(c1-SSP)*2]) { + /* look ahead for X0201/X0208conversion */ + if((c2 = GETC()) == EOF) { + (*oconv)(cv[(c1-SSP)*2],cv[(c1-SSP)*2+1]); + LAST; + } else if(c2==(0xde)) { /* 濁点 */ + (*oconv)(dv[(c1-SSP)*2],dv[(c1-SSP)*2+1]); + c2=0; + NEXT; + } else if(c2==(0xdf)&&ev[(c1-SSP)*2]) { + /* 半濁点 */ + (*oconv)(ev[(c1-SSP)*2],ev[(c1-SSP)*2+1]); + c2=0; + NEXT; + } + UNGETC(c2); c2 = 0; + } + (*oconv)(cv[(c1-SSP)*2],cv[(c1-SSP)*2+1]); + NEXT; + } else + SEND; + } else if(c1==SSO && iconv != s_iconv) { + /* EUC X0201 Case */ + /* This is too arrogant + if(x0201_f == NO_X0201) { + estab_f = FALSE; + c2 = 0; + NEXT; + } */ + c1 = GETC(); /* skip SSO */ + euc_1byte_check: + if(x0201_f && SSP<=c1 && c1<0xe0) { + if(dv[(c1-SSP)*2]||ev[(c1-SSP)*2]) { + if((c2 = GETC()) == EOF) { + (*oconv)(cv[(c1-SSP)*2],cv[(c1-SSP)*2+1]); + LAST; + } + /* forward lookup 濁点/半濁点 */ + if(c2 != SSO) { + UNGETC(c2); c2 = 0; + (*oconv)(cv[(c1-SSP)*2],cv[(c1-SSP)*2+1]); + NEXT; + } else if((c2 = GETC()) == EOF) { + (*oconv)(cv[(c1-SSP)*2],cv[(c1-SSP)*2+1]); + (*oconv)(0,SSO); + LAST; + } else if(c2==(0xde)) { /* 濁点 */ + (*oconv)(dv[(c1-SSP)*2],dv[(c1-SSP)*2+1]); + c2=0; + NEXT; + } else if(c2==(0xdf)&&ev[(c1-SSP)*2]) { + /* 半濁点 */ + (*oconv)(ev[(c1-SSP)*2],ev[(c1-SSP)*2+1]); + c2=0; + NEXT; + } else { + (*oconv)(cv[(c1-SSP)*2],cv[(c1-SSP)*2+1]); + /* we have to check this c2 */ + /* and no way to push back SSO */ + c1 = c2; c2 = 0; + goto euc_1byte_check; + } + } + (*oconv)(cv[(c1-SSP)*2],cv[(c1-SSP)*2+1]); + NEXT; + } else + SEND; + } else if(c1 < SSP && iconv != s_iconv) { + /* strange code in EUC */ + iconv = s_iconv; /* try SJIS */ + c2 = c1; + NEXT; + } else { + /* already established */ + c2 = c1; + NEXT; + } + } + } else if((c1 > SP) && (c1 != DEL)) { + /* in case of Roman characters */ + if(shift_mode) { + c1 |= 0x80; + /* output 1 shifted byte */ + if(x0201_f && (!iso8859_f||input_mode==X0201) && + SSP<=c1 && c1<0xe0 ) { + if(dv[(c1-SSP)*2]||ev[(c1-SSP)*2]) { + if((c2 = GETC()) == EOF) { + (*oconv)(cv[(c1-SSP)*2],cv[(c1-SSP)*2+1]); + LAST; + } else if(c2==(0xde&0x7f)) { /* 濁点 */ + (*oconv)(dv[(c1-SSP)*2],dv[(c1-SSP)*2+1]); + c2=0; + NEXT; + } else if(c2==(0xdf&0x7f)&&ev[(c1-SSP)*2]) { + /* 半濁点 */ + (*oconv)(ev[(c1-SSP)*2],ev[(c1-SSP)*2+1]); + c2=0; + NEXT; + } + UNGETC(c2); c2 = 0; + } + (*oconv)(cv[(c1-SSP)*2],cv[(c1-SSP)*2+1]); + NEXT; + } else + SEND; + } else if(c1 == '(' && broken_f && input_mode == X0208 + && !mime_mode ) { + /* Try to recover missing escape */ + if((c1 = GETC()) == EOF) { + (*oconv) (0, '('); + LAST; + } else { + if(c1 == 'B' || c1 == 'J' || c1 == 'H') { + input_mode = ASCII; shift_mode = FALSE; + NEXT; + } else { + (*oconv) (0, '('); + /* do not modify various input_mode */ + /* It can be vt100 sequence */ + SEND; + } + } + } else if(input_mode == X0208) { + /* in case of Kanji shifted */ + c2 = c1; + NEXT; + /* goto next_byte */ + } else if(c1 == '=' && mime_f && !mime_mode ) { + if((c1 = _GETC()) == EOF) { + (*oconv) (0, '='); + LAST; + } else if(c1 == '?') { + /* =? is mime conversion start sequence */ + if(mime_begin() == EOF) /* check in detail */ + LAST; + else + NEXT; + } else { + (*oconv) (0, '='); + _UNGETC(c1); + NEXT; + } + } else if(c1 == '$' && broken_f && !mime_mode) { + /* try to recover missing escape */ + if((c1 = GETC()) == EOF) { + (*oconv) (0, '$'); + LAST; + } else if(c1 == '@'|| c1 == 'B') { + /* in case of Kanji in ESC sequence */ + input_mode = X0208; + shift_mode = FALSE; + NEXT; + } else { + /* sorry */ + (*oconv) (0, '$'); + (*oconv) (0, c1); + NEXT; + } + } else + SEND; + } else if(c1 == SI) { + shift_mode = FALSE; + NEXT; + } else if(c1 == SO) { + shift_mode = TRUE; + NEXT; + } else if(c1 == ESC ) { + if((c1 = GETC()) == EOF) { + (*oconv) (0, ESC); + LAST; + } else if(c1 == '$') { + if((c1 = GETC()) == EOF) { + (*oconv) (0, ESC); + (*oconv) (0, '$'); + LAST; + } else if(c1 == '@'|| c1 == 'B') { + /* This is kanji introduction */ + input_mode = X0208; + shift_mode = FALSE; + NEXT; + } else if(broken_f&0x2) { + input_mode = X0208; + shift_mode = FALSE; + NEXT; + } else { + (*oconv) (0, ESC); + (*oconv) (0, '$'); + (*oconv) (0, c1); + NEXT; + } + } else if(c1 == '(') { + if((c1 = GETC()) == EOF) { + (*oconv) (0, ESC); + (*oconv) (0, '('); + LAST; + } else { + if(c1 == 'I') { + /* This is X0201 kana introduction */ + input_mode = X0201; shift_mode = X0201; + NEXT; + } else if(c1 == 'B' || c1 == 'J' || c1 == 'H') { + /* This is X0208 kanji introduction */ + input_mode = ASCII; shift_mode = FALSE; + NEXT; + } else if(broken_f&0x2) { + input_mode = ASCII; shift_mode = FALSE; + NEXT; + } else { + (*oconv) (0, ESC); + (*oconv) (0, '('); + /* maintain various input_mode here */ + SEND; + } + } + } else { + /* lonely ESC */ + (*oconv) (0, ESC); + SEND; + } + } else if(c1 == NL && broken_f&4) { + input_mode = ASCII; + SEND; + } else + SEND; + } + /* send: */ + if(input_mode == X0208) + (*oconv) (c2, c1); /* this is JIS, not SJIS/EUC case */ + else + (*iconv) (c2, c1); /* can be EUC/SJIS */ + c2 = 0; + continue; + /* goto next_word */ + } + + /* epilogue */ + (*iconv) (EOF, 0); + return outlen; +} + + +static void +h_conv (c2, c1) + register int c1, + c2; +{ + register int wc; + + + /** it must NOT be in the kanji shifte sequence */ + /** it must NOT be written in JIS7 */ + /** and it must be after 2 byte 8bit code */ + + hold_count = 0; + push_hold_buf (c2, c1); + c2 = 0; + + while ((c1 = GETC()) != EOF) { + if(c2) { + /* second byte */ + if(!estab_f) { + /* not established */ + if(c1 > SSP) { + /* it is still ambiguious yet */ + SEND; + } else if(c1 < AT) { + /* ignore bogus first byte */ + c2 = 0; + SEND; + } else { + /* now established */ + /* it seems to be MS Kanji */ + estab_f = TRUE; + iconv = s_iconv; + SEND; + } + } else + SEND; + } else { + /* First byte */ + if(c1 > DEL) { + /* 8th bit is on */ + if(c1 < SSP) { + /* it seems to be MS Kanji */ + estab_f = TRUE; + iconv = s_iconv; + } else if(c1 < 0xe0) { + /* it seems to be EUC */ + estab_f = TRUE; + iconv = oconv; + } else { + /* still ambiguious */ + } + c2 = c1; + NEXT; + } else + /* 7 bit code , then send without any process */ + SEND; + } + /* send: */ + if((push_hold_buf (c2, c1) == EOF) || estab_f) + break; + c2 = 0; + continue; + } + + /** now, + ** 1) EOF is detected, or + ** 2) Code is established, or + ** 3) Buffer is FULL (but last word is pushed) + ** + ** in 1) and 3) cases, we continue to use + ** Kanji codes by oconv and leave estab_f unchanged. + **/ + + for (wc = 0; wc < hold_count; wc += 2) { + c2 = hold_buf[wc]; + c1 = hold_buf[wc+1]; + (*iconv) (c2, c1); + } + return; +} + + + +static int +push_hold_buf (c2, c1) + int c2, + c1; +{ + if(hold_count >= HOLD_SIZE*2) + return (EOF); + hold_buf[hold_count++] = c2; + hold_buf[hold_count++] = c1; + return ((hold_count >= HOLD_SIZE*2) ? EOF : hold_count); +} + + +static void +s_iconv (c2, c1) + register int c2, + c1; +{ + if((c2 == EOF) || (c2 == 0)) { + /* NOP */ + } else { + c2 = c2 + c2 - ((c2 <= 0x9f) ? SJ0162 : SJ6394); + if(c1 < 0x9f) + c1 = c1 - ((c1 > DEL) ? SP : 0x1f); + else { + c1 = c1 - 0x7e; + c2++; + } + } + (*oconv) (c2, c1); +} + + +static void +e_oconv (c2, c1) + register int c2, + c1; +{ + c2 = pre_convert(c1,c2); c1 = c1_return; + if(fold_f) { + switch(fold(c2,c1)) { + case '\n': + if(add_cr == TRUE) { + PUTCHAR('\r'); + c1 = '\n'; + } + PUTCHAR('\n'); + break; + case 0: return; + case '\r': + c1 = '\n'; c2 = 0; + break; + case '\t': + case ' ': + c1 = ' '; c2 = 0; + break; + } + } + if(c2==DOUBLE_SPACE) { + PUTCHAR(' '); PUTCHAR(' '); + return; + } + if(c2 == EOF) + return; + else if(c2 == 0 && (c1&0x80)) { + PUTCHAR(SSO); PUTCHAR(c1); + } else if(c2 == 0) { + if(c1 == '\n' && add_cr == TRUE) + PUTCHAR('\r'); + if(c1 != '\r') + PUTCHAR(c1); + else if(del_cr == FALSE) + PUTCHAR(c1); + } else { + if((c1<0x20 || 0x7e<c1) || + (c2<0x20 || 0x7e<c2)) { + estab_f = FALSE; + return; /* too late to rescue this char */ + } + PUTCHAR(c2 | 0x080); + PUTCHAR(c1 | 0x080); + } +} + + +static void +s_oconv (c2, c1) + register int c2, + c1; +{ + c2 = pre_convert(c1,c2); c1 = c1_return; + if(fold_f) { + switch(fold(c2,c1)) { + case '\n': + if(add_cr == TRUE) { + PUTCHAR('\r'); + c1 = '\n'; + } + PUTCHAR('\n'); + break; + case '\r': + c1 = '\n'; c2 = 0; + break; + case 0: return; + case '\t': + case ' ': + c1 = ' '; c2 = 0; + break; + } + } + if(c2==DOUBLE_SPACE) { + PUTCHAR(' '); PUTCHAR(' '); + return; + } + if(c2 == EOF) + return; + else if(c2 == 0) { + if(c1 == '\n' && add_cr == TRUE) + PUTCHAR('\r'); + if(c1 != '\r') + PUTCHAR(c1); + else if(del_cr == FALSE) + PUTCHAR(c1); + } else { + if((c1<0x20 || 0x7e<c1) || + (c2<0x20 || 0x7e<c2)) { + estab_f = FALSE; + return; /* too late to rescue this char */ + } + PUTCHAR((((c2 - 1) >> 1) + ((c2 <= 0x5e) ? 0x71 : 0xb1))); + PUTCHAR((c1 + ((c2 & 1) ? ((c1 < 0x60) ? 0x1f : 0x20) : 0x7e))); + } +} + +static void +j_oconv (c2, c1) + register int c2, + c1; +{ + c2 = pre_convert(c1,c2); c1 = c1_return; + if(fold_f) { + switch(fold(c2,c1)) { + case '\n': + if(output_mode) { + PUTCHAR(ESC); + PUTCHAR('('); + PUTCHAR(ascii_intro); + } + if(add_cr == TRUE) { + PUTCHAR('\r'); + c1 = '\n'; + } + PUTCHAR('\n'); + output_mode = ASCII; + break; + case '\r': + c1 = '\n'; c2 = 0; + break; + case '\t': + case ' ': + c1 = ' '; c2 = 0; + break; + case 0: return; + } + } + if(c2 == EOF) { + if(output_mode) { + PUTCHAR(ESC); + PUTCHAR('('); + PUTCHAR(ascii_intro); + } + } else if(c2 == 0 && (c1 & 0x80)) { + if(input_mode==X0201 || !iso8859_f) { + if(output_mode!=X0201) { + PUTCHAR(ESC); + PUTCHAR('('); + PUTCHAR('I'); + output_mode = X0201; + } + c1 &= 0x7f; + } else { + /* iso8859 introduction, or 8th bit on */ + /* Can we convert in 7bit form using ESC-'-'-A ? + Is this popular? */ + } + PUTCHAR(c1); + } else if(c2 == 0) { + if(output_mode) { + PUTCHAR(ESC); + PUTCHAR('('); + PUTCHAR(ascii_intro); + output_mode = ASCII; + } + if(c1 == '\n' && add_cr == TRUE) + PUTCHAR('\r'); + if(c1 != '\r') + PUTCHAR(c1); + else if(del_cr == FALSE) + PUTCHAR(c1); + } else if(c2 == DOUBLE_SPACE) { + if(output_mode) { + PUTCHAR(ESC); + PUTCHAR('('); + PUTCHAR(ascii_intro); + output_mode = ASCII; + } + PUTCHAR(' '); + if(c1 == '\n' && add_cr == TRUE) + PUTCHAR('\r'); + if(c1 != '\r') + PUTCHAR(c1); + else if(del_cr == FALSE) + PUTCHAR(c1); + } else { + if(output_mode != X0208) { + PUTCHAR(ESC); + PUTCHAR('$'); + PUTCHAR(kanji_intro); + output_mode = X0208; + } + if(c1<0x20 || 0x7e<c1) + return; + if(c2<0x20 || 0x7e<c2) + return; + PUTCHAR(c2); + if(c1 == '\n' && add_cr == TRUE) + PUTCHAR('\r'); + if(c1 != '\r') + PUTCHAR(c1); + else if(del_cr == FALSE) + PUTCHAR(c1); + } +} + + +#define rot13(c) ( \ + ( c < 'A' ) ? c: \ + (c <= 'M') ? (c + 13): \ + (c <= 'Z') ? (c - 13): \ + (c < 'a') ? (c): \ + (c <= 'm') ? (c + 13): \ + (c <= 'z') ? (c - 13): \ + (c) \ +) + +#define rot47(c) ( \ + ( c < '!' ) ? c: \ + ( c <= 'O' ) ? (c + 47) : \ + ( c <= '~' ) ? (c - 47) : \ + c \ +) + +/* + Return value of fold() + + \n add newline and output char + \r add newline and output nothing + ' ' space + 0 skip + 1 (or else) normal output + + fold state in prev (previous character) + + >0x80 Japanese (X0208/X0201) + <0x80 ASCII + \n new line + ' ' space + + This fold algorthm does not preserve heading space in a line. + This is the main difference from fmt. +*/ + +static int +fold(c2,c1) +register int c2,c1; +{ + int prev0; + if(c1=='\r') + return 0; /* ignore cr */ + if(c1== 8) { + if(line>0) line--; + return 1; + } + if(c2==EOF && line != 0) /* close open last line */ + return '\n'; + /* new line */ + if(c1=='\n') { + if(prev == c1) { /* duplicate newline */ + if(line) { + line = 0; + return '\n'; /* output two newline */ + } else { + line = 0; + return 1; + } + } else { + if(prev&0x80) { /* Japanese? */ + prev = c1; + return 0; /* ignore given single newline */ + } else if(prev==' ') { + return 0; + } else { + prev = c1; + if(++line<=fold_len) + return ' '; + else { + line = 0; + return '\r'; /* fold and output nothing */ + } + } + } + } + if(c1=='\f') { + prev = '\n'; + if(line==0) + return 1; + line = 0; + return '\n'; /* output newline and clear */ + } + /* X0208 kankaku or ascii space */ + if( (c2==0&&c1==' ')|| + (c2==0&&c1=='\t')|| + (c2==DOUBLE_SPACE)|| + (c2=='!'&& c1=='!')) { + if(prev == ' ') { + return 0; /* remove duplicate spaces */ + } + prev = ' '; + if(++line<=fold_len) + return ' '; /* output ASCII space only */ + else { + prev = ' '; line = 0; + return '\r'; /* fold and output nothing */ + } + } + prev0 = prev; /* we still need this one... , but almost done */ + prev = c1; + if(c2 || (SSP<=c1 && c1<=0xdf)) + prev |= 0x80; /* this is Japanese */ + line += (c2==0)?1:2; + if(line<=fold_len) { /* normal case */ + return 1; + } + if(line>=fold_len+FOLD_MARGIN) { /* too many kinsou suspension */ + line = (c2==0)?1:2; + return '\n'; /* We can't wait, do fold now */ + } + /* simple kinsoku rules return 1 means no folding */ + if(c2==0) { + if(c1==0xde) return 1; /* ゛*/ + if(c1==0xdf) return 1; /* ゜*/ + if(c1==0xa4) return 1; /* 。*/ + if(c1==0xa3) return 1; /* ,*/ + if(c1==0xa1) return 1; /* 」*/ + if(c1==0xb0) return 1; /* - */ + if(SSP<=c1 && c1<=0xdf) { /* X0201 */ + line = 1; + return '\n';/* add one new line before this character */ + } + /* fold point in ASCII { [ ( */ + if(( c1!=')'&& + c1!=']'&& + c1!='}'&& + c1!='.'&& + c1!=','&& + c1!='!'&& + c1!='?'&& + c1!='/'&& + c1!=':'&& + c1!=';')&& + ((prev0=='\n')|| (prev0==' ')|| /* ignored new line */ + (prev0&0x80)) /* X0208 - ASCII */ + ) { + line = 1; + return '\n';/* add one new line before this character */ + } + return 1; /* default no fold in ASCII */ + } else { + if(c2=='!') { + if(c1=='"') return 1; /* 、 */ + if(c1=='#') return 1; /* 。 */ + if(c1=='$') return 1; /* , */ + if(c1=='%') return 1; /* . */ + if(c1=='\'') return 1; /* + */ + if(c1=='(') return 1; /* ; */ + if(c1==')') return 1; /* ? */ + if(c1=='*') return 1; /* ! */ + if(c1=='+') return 1; /* ゛ */ + if(c1==',') return 1; /* ゜ */ + } + line = 2; + return '\n'; /* add one new line before this character */ + } +} + +static int +pre_convert(c1,c2) +register int c1,c2; +{ + if(c2) c1 &= 0x7f; + c1_return = c1; + if(c2==EOF) return c2; + c2 &= 0x7f; + if(rot_f) { + if(c2) { + c1 = rot47(c1); + c2 = rot47(c2); + } else { + if(!(c1 & 0x80)) + c1 = rot13(c1); + } + c1_return = c1; + } + /* JISX0208 Alphabet */ + if(alpha_f && c2 == 0x23 ) return 0; + /* JISX0208 Kigou */ + if(alpha_f && c2 == 0x21 ) { + if(0x21==c1) { + if(alpha_f&0x2) { + c1_return = ' '; + return 0; + } else if(alpha_f&0x4) { + c1_return = ' '; + return DOUBLE_SPACE; + } else { + return c2; + } + } else if(0x20<c1 && c1<0x7f && fv[c1-0x20]) { + c1_return = fv[c1-0x20]; + return 0; + } + } + return c2; +} + + +static int iso8859_f_save; + +#define nkf_toupper(c) (('a'<=c && c<='z')?(c-('a'-'A')):c) +/* I don't trust portablity of toupper */ + +static int +mime_begin() +{ + int c1; + int i,j,k; + unsigned char *p,*q; + int r[20]; /* recovery buffer, max mime pattern lenght */ + + mime_mode = FALSE; + /* =? has been checked */ + j = 0; + p = mime_pattern[j]; + r[0]='='; r[1]='?'; + + for(i=2;p[i]>' ';i++) { /* start at =? */ + if( ((r[i] = c1 = _GETC())==EOF) || nkf_toupper(c1) != p[i] ) { + /* pattern fails, try next one */ + q = p; + while (p = mime_pattern[++j]) { + for(k=2;k<i;k++) /* assume length(p) > i */ + if(p[k]!=q[k]) break; + if(k==i && nkf_toupper(c1)==p[k]) break; + } + if(p) continue; /* found next one, continue */ + /* all fails, output from recovery buffer */ + _UNGETC(c1); + for(j=0;j<i;j++) { + (*oconv)(0,r[j]); + } + return c1; + } + } + iso8859_f_save = iso8859_f; + if(j==0) { + iso8859_f = TRUE; + } + mime_mode = mime_encode[j]; + if(mime_mode=='B') { + mimebuf_f = unbuf_f; + if(!unbuf_f) { + /* do MIME integrity check */ + return mime_integrity(mime_pattern[j]); + } + } + mimebuf_f = TRUE; + return c1; +} + +#define mime_getc0() (mimebuf_f?_GETC():Fifo(mime_input++)) +#define mime_ungetc0(c) (mimebuf_f?_UNGETC(c):mime_input--) + +static int +mime_getc() +{ + int c1, c2, c3, c4, cc; + int t1, t2, t3, t4, mode, exit_mode; + + if(mime_top != mime_last) { /* Something is in FIFO */ + return Fifo(mime_top++); + } + + if(mimebuf_f == FIXED_MIME) + exit_mode = mime_mode; + else + exit_mode = FALSE; + if(mime_mode == 'Q') { + if((c1 = mime_getc0()) == EOF) return (EOF); + if(c1=='_') return ' '; + if(c1!='=' && c1!='?') + return c1; + mime_mode = exit_mode; /* prepare for quit */ + if(c1<=' ') return c1; + if((c2 = mime_getc0()) == EOF) return (EOF); + if(c2<=' ') return c2; + if(c1=='?'&&c2=='=') { + /* end Q encoding */ + input_mode = exit_mode; + iso8859_f = iso8859_f_save; + return _GETC(); + } + if(c1=='?') { + mime_mode = 'Q'; /* still in MIME */ + mime_ungetc0(c2); + return c1; + } + if((c3 = mime_getc0()) == EOF) return (EOF); + if(c2<=' ') return c2; + mime_mode = 'Q'; /* still in MIME */ +#define hex(c) (('0'<=c&&c<='9')?(c-'0'):\ + ('A'<=c&&c<='F')?(c-'A'+10):('a'<=c&&c<='f')?(c-'a'+10):0) + return ((hex(c2)<<4) + hex(c3)); + } + + if(mime_mode != 'B') { + mime_mode = FALSE; + return _GETC(); + } + + + /* Base64 encoding */ + /* + MIME allows line break in the middle of + Base64, but we are very pessimistic in decoding + in unbuf mode because MIME encoded code may broken by + less or editor's control sequence (such as ESC-[-K in unbuffered + mode. ignore incomplete MIME. + */ + mode = mime_mode; + mime_mode = exit_mode; /* prepare for quit */ + + while ((c1 = mime_getc0())<=' ') { + if(c1==EOF) + return (EOF); + } + if((c2 = mime_getc0())<=' ') { + if(c2==EOF) + return (EOF); + if(mimebuf_f!=FIXED_MIME) input_mode = ASCII; + return c2; + } + if((c1 == '?') && (c2 == '=')) { + input_mode = ASCII; + while((c1 = _GETC())==' ' /* || c1=='\n' || c1=='\r' */); + return c1; + } + if((c3 = mime_getc0())<=' ') { + if(c3==EOF) + return (EOF); + if(mimebuf_f!=FIXED_MIME) input_mode = ASCII; + return c3; + } + if((c4 = mime_getc0())<=' ') { + if(c4==EOF) + return (EOF); + if(mimebuf_f!=FIXED_MIME) input_mode = ASCII; + return c4; + } + + mime_mode = mode; /* still in MIME sigh... */ + + /* BASE 64 decoding */ + + t1 = 0x3f & base64decode(c1); + t2 = 0x3f & base64decode(c2); + t3 = 0x3f & base64decode(c3); + t4 = 0x3f & base64decode(c4); + cc = ((t1 << 2) & 0x0fc) | ((t2 >> 4) & 0x03); + if(c2 != '=') { + Fifo(mime_last++) = cc; + cc = ((t2 << 4) & 0x0f0) | ((t3 >> 2) & 0x0f); + if(c3 != '=') { + Fifo(mime_last++) = cc; + cc = ((t3 << 6) & 0x0c0) | (t4 & 0x3f); + if(c4 != '=') + Fifo(mime_last++) = cc; + } + } else { + return c1; + } + return Fifo(mime_top++); +} + +static int +mime_ungetc(c) +unsigned int c; +{ + Fifo(mime_last++) = c; + return c; +} + + +static int +mime_integrity(p) +unsigned char *p; +{ + int c,d; + unsigned int q; + /* In buffered mode, read until =? or NL or buffer full + */ + mime_input = mime_top; + mime_last = mime_top; + while(*p) Fifo(mime_input++) = *p++; + d = 0; + q = mime_input; + while((c=_GETC())!=EOF) { + if(((mime_input-mime_top)&MIME_BUF_MASK)==0) break; + if(c=='=' && d=='?') { + /* checked. skip header, start decode */ + Fifo(mime_input++) = c; + mime_input = q; + return 1; + } + if(!( (c=='+'||c=='/'|| c=='=' || c=='?' || + ('a'<=c && c<='z')||('A'<= c && c<='Z')||('0'<=c && c<='9')))) + break; + /* Should we check length mod 4? */ + Fifo(mime_input++) = c; + d=c; + } + /* In case of Incomplete MIME, no MIME decode */ + Fifo(mime_input++) = c; + mime_last = mime_input; /* point undecoded buffer */ + mime_mode = 1; /* no decode on Fifo last in mime_getc */ + return 1; +} + +static int +base64decode(c) + int c; +{ + int i; + if(c > '@') + if(c < '[') + i = c - 'A'; /* A..Z 0-25 */ + else + i = c - 'G' /* - 'a' + 26 */ ; /* a..z 26-51 */ + else if(c > '/') + i = c - '0' + '4' /* - '0' + 52 */ ; /* 0..9 52-61 */ + else if(c == '+') + i = '>' /* 62 */ ; /* + 62 */ + else + i = '?' /* 63 */ ; /* / 63 */ + return (i); +} + +#ifdef notdef +int +usage() +{ + fprintf(stderr,"USAGE: nkf(nkf32,wnkf,nkf2) -[flags] [in file] .. [out file for -O flag]\n"); + fprintf(stderr,"Flags:\n"); + fprintf(stderr,"b,u Output is bufferred (DEFAULT),Output is unbufferred\n"); +#ifdef DEFAULT_CODE_SJIS + fprintf(stderr,"j,s,e Outout code is JIS 7 bit, Shift JIS (DEFAULT), AT&T JIS (EUC)\n"); +#endif +#ifdef DEFAULT_CODE_JIS + fprintf(stderr,"j,s,e Outout code is JIS 7 bit (DEFAULT), Shift JIS, AT&T JIS (EUC)\n"); +#endif +#ifdef DEFAULT_CODE_EUC + fprintf(stderr,"j,s,e Outout code is JIS 7 bit, Shift JIS, AT&T JIS (EUC) (DEFAULT)\n"); +#endif + fprintf(stderr,"J,S,E Input assumption is JIS 7 bit , Shift JIS, AT&T JIS (EUC)\n"); + fprintf(stderr,"t no conversion\n"); + fprintf(stderr,"i_ Output sequence to designate JIS-kanji (DEFAULT B)\n"); + fprintf(stderr,"o_ Output sequence to designate ASCII (DEFAULT B)\n"); + fprintf(stderr,"r {de/en}crypt ROT13/47\n"); + fprintf(stderr,"v Show this usage\n"); + fprintf(stderr,"m[BQ] MIME decode [B:base64 stream,Q:quoted stream]\n"); + fprintf(stderr,"l ISO8859-1 (Latin-1) support\n"); + fprintf(stderr,"f Folding: -f60 or -f\n"); + fprintf(stderr,"Z[0-2] Convert X0208 alphabet to ASCII 1: Kankaku to space,2: 2 spaces\n"); + fprintf(stderr,"X,x Assume X0201 kana in MS-Kanji, -x preserves X0201\n"); + fprintf(stderr,"B[0-2] Broken input 0: missing ESC,1: any X on ESC-[($]-X,2: ASCII on NL\n"); +#ifdef MSDOS + fprintf(stderr,"T Text mode output\n"); +#endif + fprintf(stderr,"O Output to File (DEFAULT 'nkf.out')\n"); + fprintf(stderr,"d,c Delete \\r in line feed, Add \\r in line feed\n"); + fprintf(stderr,"Network Kanji Filter Version %s (%s) " +#if defined(MSDOS) && !defined(_Windows) + "for DOS" +#endif +#if !defined(__WIN32__) && defined(_Windows) + "for Win16" +#endif +#if defined(__WIN32__) && defined(_Windows) + "for Win32" +#endif +#ifdef __OS2__ + "for OS/2" +#endif + ,Version,Patchlevel); + fprintf(stderr,"\n%s\n",CopyRight); + return 0; +} +#endif /* notdef */ + +static VALUE +kconv_kconv(argc, argv) + int argc; + VALUE *argv; +{ + struct RString *src, *dst; + VALUE in, out; + int in_code, out_code; + + rb_scan_args(argc, argv, "12", &src, &out, &in); + Check_Type(src, T_STRING); + + if (NIL_P(out)) { + out_code = _JIS; + } + else { + out_code = NUM2INT(out); + } + if (NIL_P(in)) { + in_code = _AUTO; + } + else { + in_code = NUM2INT(in); + } + + dst = RSTRING(str_new(0, src->len*3+10)); /* large enough? */ + dst->len = do_kconv(src->ptr, dst->ptr, dst->len, out_code, in_code); + + return (VALUE)dst; +} + +static VALUE +kconv_tojis(obj, src) + VALUE obj; + struct RString *src; +{ + struct RString *dst; + + Check_Type(src, T_STRING); + + dst = RSTRING(str_new(0, src->len*3+10)); /* large enough? */ + dst->len = do_kconv(src->ptr, dst->ptr, dst->len, _JIS, _AUTO); + + return (VALUE)dst; +} + +static VALUE +kconv_toeuc(obj, src) + VALUE obj; + struct RString* src; +{ + struct RString *dst; + + Check_Type(src, T_STRING); + + dst = RSTRING(str_new(0, src->len*3+10)); /* large enough? */ + dst->len = do_kconv(src->ptr, dst->ptr, dst->len, _EUC, _AUTO); + + return (VALUE)dst; +} + +static VALUE +kconv_tosjis(obj, src) + VALUE obj; + struct RString* src; +{ + struct RString *dst; + + Check_Type(src, T_STRING); + + dst = RSTRING(str_new(0, src->len*3+10)); /* large enough? */ + dst->len = do_kconv(src->ptr, dst->ptr, dst->len, _SJIS, _AUTO); + + return (VALUE)dst; +} + +void +Init_kconv() +{ + VALUE mKconv = rb_define_module("Kconv"); + + rb_define_module_function(mKconv, "kconv", kconv_kconv, -1); + rb_define_module_function(mKconv, "tojis", kconv_tojis, 1); + rb_define_module_function(mKconv, "toeuc", kconv_toeuc, 1); + rb_define_module_function(mKconv, "tosjis", kconv_tosjis, 1); + + rb_define_const(mKconv, "AUTO", INT2FIX(_AUTO)); + rb_define_const(mKconv, "JIS", INT2FIX(_JIS)); + rb_define_const(mKconv, "EUC", INT2FIX(_EUC)); + rb_define_const(mKconv, "SJIS", INT2FIX(_SJIS)); +} + +/** + ** パッチ制作者 + ** void@merope.pleiades.or.jp (Kusakabe Youichi) + ** NIDE Naoyuki <nide@ics.nara-wu.ac.jp> + ** ohta@src.ricoh.co.jp (Junn Ohta) + ** inouet@strl.nhk.or.jp (Tomoyuki Inoue) + ** kiri@pulser.win.or.jp (Tetsuaki Kiriyama) + ** Kimihiko Sato <sato@sail.t.u-tokyo.ac.jp> + ** a_kuroe@kuroe.aoba.yokohama.jp (Akihiko Kuroe) + ** kono@ie.u-ryukyu.ac.jp (Shinji Kono) + ** GHG00637@nifty-serve.or.jp (COW) + ** j_kuro@pluto.ai.kyutech.ac.jp (Jun Kuroda) + ** + ** 最終更新日 + ** 1996.12.18 + **/ + +/* end */ diff --git a/ext/marshal/MANIFEST b/ext/marshal/MANIFEST new file mode 100644 index 0000000000..54870ec71f --- /dev/null +++ b/ext/marshal/MANIFEST @@ -0,0 +1,5 @@ +MANIFEST +depend +extconf.rb +marshal.c +marshal.doc diff --git a/ext/marshal/depend b/ext/marshal/depend new file mode 100644 index 0000000000..c955eb2d59 --- /dev/null +++ b/ext/marshal/depend @@ -0,0 +1,2 @@ +marshal.o: marshal.c ../../ruby.h ../../config.h ../../defines.h ../../io.h \ + ../../st.h diff --git a/ext/marshal/extconf.rb b/ext/marshal/extconf.rb new file mode 100644 index 0000000000..ab30bd117b --- /dev/null +++ b/ext/marshal/extconf.rb @@ -0,0 +1 @@ +create_makefile("marshal") diff --git a/ext/marshal/marshal.c b/ext/marshal/marshal.c new file mode 100644 index 0000000000..99e87d0b5f --- /dev/null +++ b/ext/marshal/marshal.c @@ -0,0 +1,850 @@ +/************************************************ + + marshal.c - + + $Author$ + $Revision$ + $Date$ + created at: Thu Apr 27 16:30:01 JST 1995 + +************************************************/ + +#include "ruby.h" +#include "io.h" +#include "st.h" + +#define MARSHAL_MAJOR 4 +#define MARSHAL_MINOR 0 + +#define TYPE_NIL '0' +#define TYPE_TRUE 'T' +#define TYPE_FALSE 'F' +#define TYPE_FIXNUM 'i' + +#define TYPE_UCLASS 'C' +#define TYPE_OBJECT 'o' +#define TYPE_USERDEF 'u' +#define TYPE_FLOAT 'f' +#define TYPE_BIGNUM 'l' +#define TYPE_STRING '"' +#define TYPE_REGEXP '/' +#define TYPE_ARRAY '[' +#define TYPE_HASH '{' +#define TYPE_STRUCT 'S' +#define TYPE_MODULE 'M' + +#define TYPE_SYMBOL ':' +#define TYPE_SYMLINK ';' + +#define TYPE_LINK '@' + +extern VALUE cString; +extern VALUE cRegexp; +extern VALUE cArray; +extern VALUE cHash; + +VALUE rb_path2class(); + +static ID s_dump, s_load; + +struct dump_arg { + VALUE obj; + FILE *fp; + VALUE str; + st_table *symbol; + st_table *data; +}; + +struct dump_call_arg { + VALUE obj; + struct dump_arg *arg; + int limit; +}; + +static void w_long _((long, struct dump_arg*)); + +static void +w_byte(c, arg) + char c; + struct dump_arg *arg; +{ + if (arg->fp) putc(c, arg->fp); + else str_cat(arg->str, (UCHAR*)&c, 1); +} + +static void +w_bytes(s, n, arg) + char *s; + int n; + struct dump_arg *arg; +{ + w_long(n, arg); + if (arg->fp) { + fwrite(s, 1, n, arg->fp); + } + else { + str_cat(arg->str, s, n); + } +} + +static void +w_short(x, arg) + int x; + struct dump_arg *arg; +{ + int i; + + for (i=0; i<sizeof(USHORT); i++) { + w_byte((x >> (i*8)) & 0xff, arg); + } +} + +static void +w_long(x, arg) + long x; + struct dump_arg *arg; +{ + char buf[sizeof(long)+1]; + int i, len = 0; + + if (x == 0) { + w_byte(0, arg); + return; + } + for (i=1;i<sizeof(long)+1;i++) { + buf[i] = x & 0xff; + x = RSHIFT(x,8); + if (x == 0) { + buf[0] = i; + break; + } + if (x == -1) { + buf[0] = -i; + break; + } + } + len = i; + for (i=0;i<=len;i++) { + w_byte(buf[i], arg); + } +} + +static void +w_float(d, arg) + double d; + struct dump_arg *arg; +{ + char buf[100]; + + sprintf(buf, "%.12g", d); + w_bytes(buf, strlen(buf), arg); +} + +static void +w_symbol(id, arg) + ID id; + struct dump_arg *arg; +{ + char *sym = rb_id2name(id); + int num; + + if (st_lookup(arg->symbol, id, &num)) { + w_byte(TYPE_SYMLINK, arg); + w_long(num, arg); + } + else { + w_byte(TYPE_SYMBOL, arg); + w_bytes(sym, strlen(sym), arg); + st_insert(arg->symbol, id, arg->symbol->num_entries); + } +} + +static void +w_unique(s, arg) + char *s; + struct dump_arg *arg; +{ + w_symbol(rb_intern(s), arg); +} + +static void w_object _((VALUE,struct dump_arg*,int)); +extern VALUE cIO, cBignum, cStruct; + +static int +hash_each(key, value, arg) + VALUE key, value; + struct dump_call_arg *arg; +{ + w_object(key, arg->arg, arg->limit); + w_object(value, arg->arg, arg->limit); + return ST_CONTINUE; +} + +static int +obj_each(id, value, arg) + ID id; + VALUE value; + struct dump_call_arg *arg; +{ + w_symbol(id, arg->arg); + w_object(value, arg->arg, arg->limit); + return ST_CONTINUE; +} + +static void +w_uclass(obj, class, arg) + VALUE obj, class; + struct dump_arg *arg; +{ + if (CLASS_OF(obj) != class) { + w_byte(TYPE_UCLASS, arg); + w_unique(rb_class2name(CLASS_OF(obj)), arg); + } +} + +static void +w_object(obj, arg, limit) + VALUE obj; + struct dump_arg *arg; + int limit; +{ + int n; + struct dump_call_arg c_arg; + + if (limit == 0) { + Fail("exceed depth limit"); + } + limit--; + c_arg.limit = limit; + c_arg.arg = arg; + + if (obj == Qnil) { + w_byte(TYPE_NIL, arg); + } + else if (obj == TRUE) { + w_byte(TYPE_TRUE, arg); + } + else if (obj == FALSE) { + w_byte(TYPE_FALSE, arg); + } + else if (FIXNUM_P(obj)) { +#if SIZEOF_LONG <= 4 + w_byte(TYPE_FIXNUM, arg); + w_long(FIX2INT(obj), arg); +#else + if (RSHIFT(obj, 32) == 0 || RSHIFT(obj, 32) == -1) { + w_byte(TYPE_FIXNUM, arg); + w_long(FIX2INT(obj), arg); + } + else { + obj = int2big(FIX2INT(obj)); + goto write_bignum; + } +#endif + } + else { + int num; + + if (st_lookup(arg->data, obj, &num)) { + w_byte(TYPE_LINK, arg); + w_long(num, arg); + return; + } + st_insert(arg->data, obj, arg->data->num_entries); + if (rb_respond_to(obj, s_dump)) { + VALUE v; + + w_byte(TYPE_USERDEF, arg); + w_unique(rb_class2name(CLASS_OF(obj)), arg); + v = rb_funcall(obj, s_dump, 1, limit); + if (TYPE(v) != T_STRING) { + TypeError("_dump_to must return String"); + } + w_bytes(RSTRING(v)->ptr, RSTRING(v)->len, arg); + return; + } + + switch (BUILTIN_TYPE(obj)) { + case T_MODULE: + case T_CLASS: + w_byte(TYPE_MODULE, arg); + { + VALUE path = rb_class_path(obj); + w_bytes(RSTRING(path)->ptr, RSTRING(path)->len, arg); + } + return; + + case T_FLOAT: + w_byte(TYPE_FLOAT, arg); + w_float(RFLOAT(obj)->value, arg); + return; + + case T_BIGNUM: + write_bignum: + w_byte(TYPE_BIGNUM, arg); + { + char sign = RBIGNUM(obj)->sign?'+':'-'; + int len = RBIGNUM(obj)->len; + USHORT *d = RBIGNUM(obj)->digits; + + w_byte(sign, arg); + w_long(len, arg); + while (len--) { + w_short(*d, arg); + d++; + } + } + return; + + case T_STRING: + w_uclass(obj, cString, arg); + w_byte(TYPE_STRING, arg); + w_bytes(RSTRING(obj)->ptr, RSTRING(obj)->len, arg); + return; + + case T_REGEXP: + w_uclass(obj, cRegexp, arg); + w_byte(TYPE_REGEXP, arg); + w_bytes(RREGEXP(obj)->str, RREGEXP(obj)->len, arg); + w_byte(FL_TEST(obj, FL_USER1), arg); + return; + + case T_ARRAY: + w_uclass(obj, cArray, arg); + w_byte(TYPE_ARRAY, arg); + { + int len = RARRAY(obj)->len; + VALUE *ptr = RARRAY(obj)->ptr; + + w_long(len, arg); + while (len--) { + w_object(*ptr, arg, limit); + ptr++; + } + } + break; + + case T_HASH: + w_uclass(obj, cHash, arg); + w_byte(TYPE_HASH, arg); + w_long(RHASH(obj)->tbl->num_entries, arg); + st_foreach(RHASH(obj)->tbl, hash_each, &c_arg); + break; + + case T_STRUCT: + w_byte(TYPE_STRUCT, arg); + { + int len = RSTRUCT(obj)->len; + char *path = rb_class2name(CLASS_OF(obj)); + VALUE mem; + int i; + + w_unique(path, arg); + w_long(len, arg); + mem = rb_ivar_get(CLASS_OF(obj), rb_intern("__member__")); + if (mem == Qnil) { + Fatal("non-initialized struct"); + } + for (i=0; i<len; i++) { + w_symbol(FIX2INT(RARRAY(mem)->ptr[i]), arg); + w_object(RSTRUCT(obj)->ptr[i], arg, limit); + } + } + break; + + case T_OBJECT: + w_byte(TYPE_OBJECT, arg); + { + VALUE class = CLASS_OF(obj); + char *path; + + if (FL_TEST(class, FL_SINGLETON)) { + TypeError("singleton can't be dumped"); + } + path = rb_class2name(class); + w_unique(path, arg); + if (ROBJECT(obj)->iv_tbl) { + w_long(ROBJECT(obj)->iv_tbl->num_entries, arg); + st_foreach(ROBJECT(obj)->iv_tbl, obj_each, &c_arg); + } + else { + w_long(0, arg); + } + } + break; + + default: + TypeError("can't dump %s", rb_class2name(CLASS_OF(obj))); + break; + } + } +} + +static VALUE +dump(arg) + struct dump_call_arg *arg; +{ + w_object(arg->obj, arg->arg, arg->limit); +} + +static VALUE +dump_ensure(arg) + struct dump_arg *arg; +{ + st_free_table(arg->symbol); + st_free_table(arg->data); +} + +static VALUE +marshal_dump(argc, argv) + int argc; + VALUE argv; +{ + VALUE obj, port, a1, a2; + int limit = -1; + extern VALUE cIO; + struct dump_arg arg; + struct dump_call_arg c_arg; + + port = 0; + rb_scan_args(argc, argv, "12", &obj, &a1, &a2); + if (argc == 3) { + limit = NUM2INT(a2); + port = a1; + } + else if (argc == 2) { + if (FIXNUM_P(a1)) limit = FIX2INT(a1); + else port = a1; + } + if (port) { + if (obj_is_kind_of(port, cIO)) { + OpenFile *fptr; + + io_binmode(port); + GetOpenFile(port, fptr); + io_writable(fptr); + arg.fp = (fptr->f2) ? fptr->f2 : fptr->f; + } + else { + TypeError("instance of IO needed"); + } + } + else { + arg.fp = 0; + port = str_new(0, 0); + arg.str = port; + } + + arg.symbol = st_init_numtable(); + arg.data = st_init_numtable(); + c_arg.obj = obj; + c_arg.arg = &arg; + c_arg.limit = limit; + + w_byte(MARSHAL_MAJOR, &arg); + w_byte(MARSHAL_MINOR, &arg); + + rb_ensure(dump, &c_arg, dump_ensure, &arg); + + return port; +} + +struct load_arg { + FILE *fp; + UCHAR *ptr, *end; + st_table *symbol; + st_table *data; +}; + +static int +r_byte(arg) + struct load_arg *arg; +{ + if (arg->fp) return getc(arg->fp); + if (arg->ptr < arg->end) return *arg->ptr++; + return EOF; +} + +static USHORT +r_short(arg) + struct load_arg *arg; +{ + USHORT x; + int i; + + x = 0; + for (i=0; i<sizeof(USHORT); i++) { + x |= r_byte(arg)<<(i*8); + } + + return x; +} + +static void +long_toobig(size) + int size; +{ + TypeError("long too big for this architecture (size %d, given %d)", + sizeof(long), size); +} + +static long +r_long(arg) + struct load_arg *arg; +{ + int c = r_byte(arg), i; + register long x; + + if (c == 0) return 0; + if (c > 0) { + if (c > sizeof(long)) long_toobig((int)c); + x = 0; + for (i=0;i<c;i++) { + x |= (long)r_byte(arg) << (8*i); + } + } + else if (c < 0) { + c = -c; + if (c > sizeof(long)) long_toobig((int)c); + x = -1; + for (i=0;i<c;i++) { + x &= ~(0xff << (8*i)); + x |= (long)r_byte(arg) << (8*i); + } + } + return x; +} + +#define r_bytes(s, arg) \ + (s = (char*)r_long(arg), r_bytes0(&s,ALLOCA_N(char,(long)s),(long)s,arg)) + +static int +r_bytes0(sp, s, len, arg) + char **sp, *s; + int len; + struct load_arg *arg; +{ + if (arg->fp) { + len = fread(s, 1, len, arg->fp); + } + else { + if (arg->ptr + len > arg->end) { + len = arg->end - arg->ptr; + } + memcpy(s, arg->ptr, len); + arg->ptr += len; + } + + (s)[len] = '\0'; + *sp = s; + + return len; +} + +static ID +r_symbol(arg) + struct load_arg *arg; +{ + char *buf; + ID id; + char type; + + if (r_byte(arg) == TYPE_SYMLINK) { + int num = r_long(arg); + + if (st_lookup(arg->symbol, num, &id)) { + return id; + } + TypeError("bad symbol"); + } + r_bytes(buf, arg); + id = rb_intern(buf); + st_insert(arg->symbol, arg->symbol->num_entries, id); + + return id; +} + +static char* +r_unique(arg) + struct load_arg *arg; +{ + return rb_id2name(r_symbol(arg)); +} + +static VALUE +r_string(arg) + struct load_arg *arg; +{ + char *buf; + int len = r_bytes(buf, arg); + + return str_taint(str_new(buf, len)); +} + +static VALUE +r_regist(v, arg) + VALUE v; + struct load_arg *arg; +{ + st_insert(arg->data, arg->data->num_entries, v); + return v; +} + +static VALUE +r_object(arg) + struct load_arg *arg; +{ + VALUE v; + int type = r_byte(arg); + + switch (type) { + case EOF: + eof_error(); + return Qnil; + + case TYPE_LINK: + if (st_lookup(arg->data, r_long(arg), &v)) { + return v; + } + ArgError("dump format error (unlinked)"); + break; + + case TYPE_UCLASS: + { + VALUE c = rb_path2class(r_unique(arg)); + v = r_object(arg); + if (rb_special_const_p(v)) { + ArgError("dump format error (user class)"); + } + RBASIC(v)->class = c; + return v; + } + + case TYPE_NIL: + return Qnil; + + case TYPE_TRUE: + return TRUE; + + case TYPE_FALSE: + return FALSE; + + case TYPE_FIXNUM: + { + int i = r_long(arg); + return INT2FIX(i); + } + + case TYPE_FLOAT: + { +#ifndef atof + double atof(); +#endif + char *buf; + + r_bytes(buf, arg); + v = float_new(atof(buf)); + return r_regist(v, arg); + } + + case TYPE_BIGNUM: + { + int len; + USHORT *digits; + + NEWOBJ(big, struct RBignum); + OBJSETUP(big, cBignum, T_BIGNUM); + big->sign = (r_byte(arg) == '+'); + big->len = len = r_long(arg); + big->digits = digits = ALLOC_N(USHORT, len); + while (len--) { + *digits++ = r_short(arg); + } + big = RBIGNUM(big_norm((VALUE)big)); + if (TYPE(big) == T_BIGNUM) { + r_regist(big, arg); + } + return (VALUE)big; + } + + case TYPE_STRING: + return r_regist(r_string(arg), arg); + + case TYPE_REGEXP: + { + char *buf; + int len = r_bytes(buf, arg); + int ci = r_byte(arg); + return r_regist(reg_new(buf, len, ci), arg); + } + + case TYPE_ARRAY: + { + volatile int len = r_long(arg); + v = ary_new2(len); + r_regist(v, arg); + while (len--) { + ary_push(v, r_object(arg)); + } + return v; + } + + case TYPE_HASH: + { + int len = r_long(arg); + + v = hash_new(); + r_regist(v, arg); + while (len--) { + VALUE key = r_object(arg); + VALUE value = r_object(arg); + hash_aset(v, key, value); + } + return v; + } + + case TYPE_STRUCT: + { + VALUE class, mem, values; + int i, len; + int num = arg->data->num_entries; + + class = rb_path2class(r_unique(arg)); + mem = rb_ivar_get(class, rb_intern("__member__")); + if (mem == Qnil) { + Fatal("non-initialized struct"); + } + len = r_long(arg); + + values = ary_new2(len); + for (i=0; i<len; i++) { + ary_push(values, Qnil); + } + v = struct_alloc(class, values); + r_regist(v, arg); + for (i=0; i<len; i++) { + ID slot = r_symbol(arg); + if (RARRAY(mem)->ptr[i] != INT2FIX(slot)) + TypeError("struct not compatible"); + struct_aset(v, INT2FIX(i), r_object(arg)); + } + return v; + } + break; + + case TYPE_USERDEF: + { + VALUE class; + int len; + + class = rb_path2class(r_unique(arg)); + if (rb_respond_to(class, s_load)) { + v = rb_funcall(class, s_load, 1, r_string(arg)); + return r_regist(v, arg); + } + TypeError("class %s needs to have method `_load_from'", + rb_class2name(class)); + } + break; + + case TYPE_OBJECT: + { + VALUE class; + int len; + + class = rb_path2class(r_unique(arg)); + len = r_long(arg); + v = obj_alloc(class); + r_regist(v, arg); + if (len > 0) { + while (len--) { + ID id = r_symbol(arg); + VALUE val = r_object(arg); + rb_ivar_set(v, id, val); + } + } + return v; + } + break; + + case TYPE_MODULE: + { + char *buf; + r_bytes(buf, arg); + return rb_path2class(buf); + } + + default: + ArgError("dump format error(0x%x)", type); + break; + } +} + +static VALUE +load(arg) + struct load_arg *arg; +{ + return r_object(arg); +} + +static VALUE +load_ensure(arg) + struct load_arg *arg; +{ + st_free_table(arg->symbol); + st_free_table(arg->data); +} + +static VALUE +marshal_load(self, port) + VALUE self, port; +{ + FILE *fp; + int major; + VALUE v; + OpenFile *fptr; + struct load_arg arg; + + if (TYPE(port) == T_STRING) { + arg.fp = 0; + arg.ptr = RSTRING(port)->ptr; + arg.end = arg.ptr + RSTRING(port)->len; + } + else { + if (obj_is_kind_of(port, cIO)) { + io_binmode(port); + GetOpenFile(port, fptr); + io_readable(fptr); + arg.fp = fptr->f; + } + else { + TypeError("instance of IO needed"); + } + } + + major = r_byte(&arg); + if (major == MARSHAL_MAJOR) { + if (r_byte(&arg) != MARSHAL_MINOR) { + Warning("Old marshal file format (can be read)"); + } + arg.symbol = st_init_numtable(); + arg.data = st_init_numtable(); + v = rb_ensure(load, &arg, load_ensure, &arg); + } + else { + TypeError("Old marshal file format (can't read)"); + } + + return v; +} + +Init_marshal() +{ + VALUE mMarshal = rb_define_module("Marshal"); + + s_dump = rb_intern("_dump_to"); + s_load = rb_intern("_load_from"); + rb_define_module_function(mMarshal, "dump", marshal_dump, -1); + rb_define_module_function(mMarshal, "load", marshal_load, 1); + rb_define_module_function(mMarshal, "restore", marshal_load, 1); +} diff --git a/ext/marshal/marshal.doc b/ext/marshal/marshal.doc new file mode 100644 index 0000000000..7529e7942f --- /dev/null +++ b/ext/marshal/marshal.doc @@ -0,0 +1,48 @@ +.\" marshal.doc - -*- Indented-Text -*- created at: Tue May 16 12:18:08 JST 1995 + +** Marshal(モジュール) + +rubyオブジェクトをファイルに書き出したり,読みも度したりする機能を提供 +するモジュール.大部分のクラスのインスタンスを書き出す事ができるが,ファ +イルへの不可能なクラスも存在し(例:IO),そのようなクラスを書き出そうと +すると例外を発生させる. + +Methods: +Single Methods: + + dump(obj, port[, limit]) + + objを再帰的にファイルに書き出す.ファイルに書き出せないクラスのイ + ンスタンスをファイルに書き出そうとすると例外を発生させる.ファイル + に書き出せないクラスは以下の通り. + + Class, Module, Data + + また,これらのクラスを間接的に指すクラス(例えばIOのサブクラス)など + も書き出せない.portはIO(またはそのサブクラス)のインスタンスを指定 + する. + + 出力するオブジェクトがメソッド`_dump_to'を定義している場合には,ファ + イル出力はそのメソッドを使って行われる.メソッド`_dump_to'は引数と + して出力先のファイルオブジェクトを受け取る.インスタンスがメソッド + `_dump_to'を持つクラスは必ず同じフォーマットを読み戻す特異メソッド + `_load_from'を定義する必要がある. + + limitを指定した場合,limit段以上深くリンクしたオブジェクトをダンプ + できない(デフォルトは100レベル)。負のlimitを指定すると深さチェック + を行わない。 + + dumps(obj) + + dump()がファイルに書き出すのと同じ内容を含む文字列を返す. + + load(port) + + portからオブジェクトを読み込んで来て,元のオブジェクトと同じ状態を + もつオブジェクトを生成する.portは文字列かIO(またはそのサブクラス) + のインスタンスである. + +------------------------------------------------------- +Local variables: +fill-column: 70 +end: diff --git a/ext/md5/MANIFEST b/ext/md5/MANIFEST new file mode 100644 index 0000000000..e4f0004b4a --- /dev/null +++ b/ext/md5/MANIFEST @@ -0,0 +1,6 @@ +MANIFEST +depend +md5.doc +md5.h +md5c.c +md5init.c diff --git a/ext/md5/depend b/ext/md5/depend new file mode 100644 index 0000000000..be56da89b9 --- /dev/null +++ b/ext/md5/depend @@ -0,0 +1,2 @@ +md5c.o: md5c.c md5.h +md5init.o: md5init.c ../../ruby.h ../../config.h ../../defines.h md5.h diff --git a/ext/md5/md5.doc b/ext/md5/md5.doc new file mode 100644 index 0000000000..2203404602 --- /dev/null +++ b/ext/md5/md5.doc @@ -0,0 +1,36 @@ +.\" md5.doc - -*- Indented-Text -*- created at: Fri Aug 2 12:01:27 JST 1996 + +** MD5(クラス) + +RFC1321に記述されているRSA Data Security, Inc. の MD5 Message-Digest +Algorithmを実装するクラス. + +SuperClass: Object + +Class Methods: + + new([str]) + md5([str]) + + 新しいMD5オブジェクトを生成する.文字列引数が与えられるとそれ + を追加する(see update). + +Methods: + + clone + + MD5オブジェクトの複製を作る + + digest + + 今までに追加した文字列に対するハッシュ値を16バイト長の文字列で + 返す. + + update(str) + + keyをキーとする値を返す. + +------------------------------------------------------- +Local variables: +fill-column: 70 +end: diff --git a/ext/md5/md5.h b/ext/md5/md5.h new file mode 100644 index 0000000000..81a6d7ff36 --- /dev/null +++ b/ext/md5/md5.h @@ -0,0 +1,86 @@ +/* MD5.H - header file for MD5C.C + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +/* ========== include global.h ========== */ +/* GLOBAL.H - RSAREF types and constants + */ + +/* PROTOTYPES should be set to one if and only if the compiler supports + function argument prototyping. +The following makes PROTOTYPES default to 0 if it has not already + been defined with C compiler flags. + */ +#ifdef HAVE_PROTOTYPES +#define PROTOTYPES 1 +#endif +#ifndef PROTOTYPES +#define PROTOTYPES 0 +#endif + +/* POINTER defines a generic pointer type */ +typedef unsigned char *POINTER; + +/* UINT2 defines a two byte word */ +typedef unsigned short int UINT2; + +#ifdef HAVE_LIMITS_H +#include <limits.h> +#else +/* Wild guess */ +#define LONG_MAX 2147483647L +#endif + +/* UINT4 defines a four byte word */ +#if defined(INT_MAX) && INT_MAX == 2147483647 +typedef unsigned int UINT4; +#else +#if defined(LONG_MAX) && LONG_MAX == 2147483647L +typedef unsigned long int UINT4; +#endif +/* Too bad if neither is */ +#endif + +/* PROTO_LIST is defined depending on how PROTOTYPES is defined above. +If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it + returns an empty list. + */ +#if PROTOTYPES +#define PROTO_LIST(list) list +#else +#define PROTO_LIST(list) () +#endif +/* ========== End global.h; continue md5.h ========== */ + +/* MD5 context. */ +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +void MD5Init PROTO_LIST ((MD5_CTX *)); +void MD5Update PROTO_LIST + ((MD5_CTX *, unsigned char *, unsigned int)); +void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *)); diff --git a/ext/md5/md5c.c b/ext/md5/md5c.c new file mode 100644 index 0000000000..d7c7e4fb27 --- /dev/null +++ b/ext/md5/md5c.c @@ -0,0 +1,337 @@ +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "md5.h" + +/* Constants for MD5Transform routine. + */ + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64])); +static void Encode PROTO_LIST + ((unsigned char *, UINT4 *, unsigned int)); +static void Decode PROTO_LIST + ((UINT4 *, unsigned char *, unsigned int)); +static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int)); +static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int)); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. + */ +void MD5Init (context) +MD5_CTX *context; /* context */ +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. +*/ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. + */ +void MD5Update (context, input, inputLen) +MD5_CTX *context; /* context */ +unsigned char *input; /* input block */ +unsigned int inputLen; /* length of input block */ +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) + < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. +*/ + if (inputLen >= partLen) { + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)input, partLen); + MD5Transform (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform (context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy + ((POINTER)&context->buffer[index], (POINTER)&input[i], + inputLen-i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ +void MD5Final (digest, context) +unsigned char digest[16]; /* message digest */ +MD5_CTX *context; /* context */ +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. +*/ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update (context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update (context, bits, 8); + + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. +*/ + MD5_memset ((POINTER)context, 0, sizeof (*context)); +} + +/* MD5 basic transformation. Transforms state based on block. + */ +static void MD5Transform (state, block) +UINT4 state[4]; +unsigned char block[64]; +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. +*/ + MD5_memset ((POINTER)x, 0, sizeof (x)); +} + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void Encode (output, input, len) +unsigned char *output; +UINT4 *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + */ +static void Decode (output, input, len) +UINT4 *output; +unsigned char *input; +unsigned int len; +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} + +/* Note: Replace "for loop" with standard memcpy if possible. + */ + +static void MD5_memcpy (output, input, len) +POINTER output; +POINTER input; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + output[i] = input[i]; +} + +/* Note: Replace "for loop" with standard memset if possible. + */ +static void MD5_memset (output, value, len) +POINTER output; +int value; +unsigned int len; +{ + unsigned int i; + + for (i = 0; i < len; i++) + ((char *)output)[i] = (char)value; +} diff --git a/ext/md5/md5init.c b/ext/md5/md5init.c new file mode 100644 index 0000000000..47f913792f --- /dev/null +++ b/ext/md5/md5init.c @@ -0,0 +1,90 @@ +/************************************************ + + md5init.c - + + $Author$ + created at: Fri Aug 2 09:24:12 JST 1996 + + Copyright (C) 1995 Yukihiro Matsumoto + +************************************************/ +/* This module provides an interface to the RSA Data Security, + Inc. MD5 Message-Digest Algorithm, described in RFC 1321. + It requires the files md5c.c and md5.h (which are slightly changed + from the versions in the RFC to avoid the "global.h" file.) */ + +#include "ruby.h" +#include "md5.h" + +static VALUE cMD5; + +static VALUE +md5_update(obj, str) + VALUE obj; + struct RString *str; +{ + MD5_CTX *md5; + + Check_Type(str, T_STRING); + Data_Get_Struct(obj, MD5_CTX, md5); + MD5Update(md5, str->ptr, str->len); + + return Qnil; +} +static VALUE +md5_digest(obj) + VALUE obj; +{ + MD5_CTX *md5, ctx; + unsigned char digest[16]; + + Data_Get_Struct(obj, MD5_CTX, md5); + ctx = *md5; + MD5Final(digest, &ctx); + + return str_new(digest, 16); +} + +static VALUE +md5_clone(obj) + VALUE obj; +{ + VALUE clone; + MD5_CTX *md5, *md5_new; + + Data_Get_Struct(obj, MD5_CTX, md5); + obj = Data_Make_Struct(CLASS_OF(obj), MD5_CTX, 0, 0, md5_new); + *md5_new = *md5; + + return obj; +} + +static VALUE +md5_new(argc, argv, class) +{ + int i; + VALUE arg, obj; + MD5_CTX *md5; + + rb_scan_args(argc, argv, "01", &arg); + if (!NIL_P(arg)) Check_Type(arg, T_STRING); + + obj = Data_Make_Struct(class, MD5_CTX, 0, 0, md5); + MD5Init(md5); + if (!NIL_P(arg)) { + md5_update(obj, arg); + } + + return obj; +} + +Init_md5() +{ + cMD5 = rb_define_class("MD5", cObject); + + rb_define_singleton_method(cMD5, "new", md5_new, -1); + + rb_define_method(cMD5, "update", md5_update, 1); + rb_define_method(cMD5, "digest", md5_digest, 0); + rb_define_method(cMD5, "clone", md5_clone, 0); +} diff --git a/ext/socket/MANIFEST b/ext/socket/MANIFEST new file mode 100644 index 0000000000..d41d9e0b69 --- /dev/null +++ b/ext/socket/MANIFEST @@ -0,0 +1,4 @@ +MANIFEST +depend +extconf.rb +socket.c diff --git a/ext/socket/depend b/ext/socket/depend new file mode 100644 index 0000000000..e6ede5a411 --- /dev/null +++ b/ext/socket/depend @@ -0,0 +1 @@ +socket.o : socket.c ../../ruby.h ../../config.h ../../defines.h ../../io.h ../../sig.h diff --git a/ext/socket/extconf.rb b/ext/socket/extconf.rb new file mode 100644 index 0000000000..bbde1a0c35 --- /dev/null +++ b/ext/socket/extconf.rb @@ -0,0 +1,17 @@ +$LDFLAGS = "-L/usr/local/lib" +have_library("wsock32", "cygwin32_socket") or have_library("socket", "socket") +have_library("inet", "gethostbyname") +have_library("nsl", "gethostbyname") +have_header("sys/un.h") +if have_func("socket") or have_func("cygwin32_socket") + have_func("hsterror") + unless have_func("gethostname") + have_func("uname") + end + if ENV["SOCKS_SERVER"] # test if SOCKSsocket needed + if have_library("socks", "Rconnect") + $CFLAGS="-DSOCKS" + end + end + create_makefile("socket") +end diff --git a/ext/socket/socket.c b/ext/socket/socket.c new file mode 100644 index 0000000000..9128e26de7 --- /dev/null +++ b/ext/socket/socket.c @@ -0,0 +1,1407 @@ +/************************************************ + + socket.c - + + $Author$ + $Date$ + created at: Thu Mar 31 12:21:29 JST 1994 + +************************************************/ + +#include "ruby.h" +#include "io.h" +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <errno.h> +#ifdef HAVE_SYS_UN_H +#include <sys/un.h> +#endif + +#if defined(THREAD) && defined(HAVE_FCNTL) +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif +#include <sys/types.h> +#include <sys/time.h> +#include <fcntl.h> +#endif +#ifndef EWOULDBLOCK +#define EWOULDBLOCK EAGAIN +#endif + +extern VALUE cIO; +extern VALUE cInteger; + +VALUE cBasicSocket; +VALUE cTCPsocket; +VALUE cTCPserver; +#ifdef AF_UNIX +VALUE cUNIXsocket; +VALUE cUNIXserver; +#endif +VALUE cSocket; + +extern VALUE eException; +static VALUE eSocket; + +#ifdef SOCKS +VALUE cSOCKSsocket; +void SOCKSinit(); +int Rconnect(); +#endif + +FILE *rb_fdopen(); +char *strdup(); + +#define INET_CLIENT 0 +#define INET_SERVER 1 +#define INET_SOCKS 2 + +#ifdef NT +static void +sock_finalize(fptr) + OpenFile *fptr; +{ + SOCKET s = fileno(fptr->f); + free(fptr->f); + free(fptr->f2); + closesocket(s); +} +#endif + +static VALUE +sock_new(class, fd) + VALUE class; + int fd; +{ + OpenFile *fp; + NEWOBJ(sock, struct RFile); + OBJSETUP(sock, class, T_FILE); + + MakeOpenFile(sock, fp); + fp->f = rb_fdopen(fd, "r"); +#ifdef NT + fp->finalize = sock_finalize; +#else + setbuf(fp->f, NULL); +#endif + fp->f2 = rb_fdopen(fd, "w"); + fp->mode = FMODE_READWRITE; + io_unbuffered(fp); + + return (VALUE)sock; +} + +static VALUE +bsock_shutdown(argc, argv, sock) + int argc; + VALUE *argv; + VALUE sock; +{ + VALUE howto; + int how; + OpenFile *fptr; + + rb_scan_args(argc, argv, "01", &howto); + if (howto == Qnil) + how = 2; + else { + how = NUM2INT(howto); + if (how < 0 && how > 2) how = 2; + } + GetOpenFile(sock, fptr); + if (shutdown(fileno(fptr->f), how) == -1) + rb_sys_fail(0); + + return INT2FIX(0); +} + +static VALUE +bsock_setsockopt(sock, lev, optname, val) + VALUE sock, lev, optname; + struct RString *val; +{ + int level, option; + OpenFile *fptr; + int i; + char *v; + int vlen; + + rb_secure(2); + level = NUM2INT(lev); + option = NUM2INT(optname); + switch (TYPE(val)) { + case T_FIXNUM: + i = FIX2INT(val); + goto numval; + case T_FALSE: + i = 0; + goto numval; + case T_TRUE: + i = 1; + numval: + v = (char*)&i; vlen = sizeof(i); + break; + default: + Check_Type(val, T_STRING); + v = val->ptr; vlen = val->len; + } + + GetOpenFile(sock, fptr); + if (setsockopt(fileno(fptr->f), level, option, v, vlen) < 0) + rb_sys_fail(fptr->path); + + return INT2FIX(0); +} + +static VALUE +bsock_getsockopt(sock, lev, optname) + VALUE sock, lev, optname; +{ + int level, option, len; + char *buf; + OpenFile *fptr; + + level = NUM2INT(lev); + option = NUM2INT(optname); + len = 256; + buf = ALLOCA_N(char,len); + + GetOpenFile(sock, fptr); + if (getsockopt(fileno(fptr->f), level, option, buf, &len) < 0) + rb_sys_fail(fptr->path); + + return str_new(buf, len); +} + +static VALUE +bsock_getsockname(sock) + VALUE sock; +{ + char buf[1024]; + int len = sizeof buf; + OpenFile *fptr; + + GetOpenFile(sock, fptr); + if (getsockname(fileno(fptr->f), (struct sockaddr*)buf, &len) < 0) + rb_sys_fail("getsockname(2)"); + return str_new(buf, len); +} + +static VALUE +bsock_getpeername(sock) + VALUE sock; +{ + char buf[1024]; + int len = sizeof buf; + OpenFile *fptr; + + GetOpenFile(sock, fptr); + if (getpeername(fileno(fptr->f), (struct sockaddr*)buf, &len) < 0) + rb_sys_fail("getpeername(2)"); + return str_new(buf, len); +} + +static VALUE +bsock_send(argc, argv, sock) + int argc; + VALUE *argv; + VALUE sock; +{ + struct RString *msg, *to; + VALUE flags; + OpenFile *fptr; + FILE *f; + int fd, n; + + rb_secure(4); + rb_scan_args(argc, argv, "21", &msg, &flags, &to); + + Check_Type(msg, T_STRING); + + GetOpenFile(sock, fptr); + f = fptr->f2?fptr->f2:fptr->f; + fd = fileno(f); + retry: +#ifdef THREAD + thread_fd_writable(fd); +#endif + if (RTEST(to)) { + Check_Type(to, T_STRING); + n = sendto(fd, msg->ptr, msg->len, NUM2INT(flags), + (struct sockaddr*)to->ptr, to->len); + } + else { + n = send(fd, msg->ptr, msg->len, NUM2INT(flags)); + } + if (n < 0) { + switch (errno) { + case EINTR: + case EWOULDBLOCK: +#if EAGAIN != EWOULDBLOCK + case EAGAIN: +#endif +#ifdef THREAD + thread_schedule(); +#endif + goto retry; + } + rb_sys_fail("send(2)"); + } + return INT2FIX(n); +} + +static VALUE tcpaddr _((struct sockaddr_in*)); +#ifdef HAVE_SYS_UN_H +static VALUE unixaddr _((struct sockaddr_un*)); +#endif + +static VALUE +s_recv(sock, argc, argv, from) + VALUE sock; + int argc; + VALUE *argv; + int from; /* 0 - recv, + 1 - TCPsocket#recvfrom, + 2 - UNIXsocket#recvfrom, + 3 - Socket#recvfrom */ +{ + OpenFile *fptr; + FILE f; + struct RString *str; + char buf[1024]; + int fd, alen = sizeof buf; + VALUE len, flg; + int flags; + + rb_scan_args(argc, argv, "11", &len, &flg); + + if (flg == Qnil) flags = 0; + else flags = NUM2INT(flg); + + str = (struct RString*)str_new(0, NUM2INT(len)); + + GetOpenFile(sock, fptr); + fd = fileno(fptr->f); +#ifdef THREAD + thread_wait_fd(fd); +#endif + TRAP_BEG; + retry: + str->len = recvfrom(fd, str->ptr, str->len, flags, + (struct sockaddr*)buf, &alen); + TRAP_END; + + if (str->len < 0) { + switch (errno) { + case EINTR: + case EWOULDBLOCK: +#if EAGAIN != EWOULDBLOCK + case EAGAIN: +#endif +#ifdef THREAD + thread_schedule(); +#endif + goto retry; + } + rb_sys_fail("recvfrom(2)"); + } + str_taint(str); + switch (from) { + case 0: + return (VALUE)str; + case 1: + if (alen != sizeof(struct sockaddr_in)) { + TypeError("sockaddr size differs - should not happen"); + } + return assoc_new(str, tcpaddr((struct sockaddr_in *)buf)); +#ifdef HAVE_SYS_UN_H + case 2: + if (alen != sizeof(struct sockaddr_un)) { + TypeError("sockaddr size differs - should not happen"); + } + return assoc_new(str, unixaddr((struct sockaddr_un *)buf)); +#endif + case 3: + return assoc_new(str, str_new(buf, alen)); + } +} + +static VALUE +bsock_recv(argc, argv, sock) + int argc; + VALUE *argv; + VALUE sock; +{ + return s_recv(sock, argc, argv, 0); +} + +#if defined(THREAD) && defined(HAVE_FCNTL) +static int +thread_connect(fd, sockaddr, len, type) + int fd; + struct sockaddr *sockaddr; + int len; + int type; +{ + int status; + int mode; + fd_set fds; + + mode = fcntl(fd, F_GETFL, 0); + +#ifdef O_NDELAY +# define NONBLOCKING O_NDELAY +#else +#ifdef O_NBIO +# define NONBLOCKING O_NBIO +#else +# define NONBLOCKING O_NONBLOCK +#endif +#endif + fcntl(fd, F_SETFL, mode|NONBLOCKING); + for (;;) { +#ifdef SOCKS + if (type == INET_SOCKS) { + status = Rconnect(fd, sockaddr, len); + } + else +#endif + { + status = connect(fd, sockaddr, len); + } + if (status < 0) { + switch (errno) { +#ifdef EINPROGRESS + case EINPROGRESS: +#ifdef EAGAIN + case EAGAIN: +#endif + FD_ZERO(&fds); + FD_SET(fd, &fds); + thread_select(fd+1, 0, &fds, 0, 0, 0); + continue; +#endif + +#ifdef EISCONN + case EISCONN: +#endif +#ifdef EALREADY + case EALREADY: +#endif +#if defined(EISCONN) || defined(EALREADY) + status = 0; + errno = 0; + break; +#endif + } + } + mode &= ~NONBLOCKING; + fcntl(fd, F_SETFL, mode); + return status; + } +} +#endif + +static VALUE +open_inet(class, h, serv, type) + VALUE class, h, serv; + int type; +{ + char *host; + struct hostent *hostent, _hostent; + struct servent *servent, _servent; + struct protoent *protoent; + struct sockaddr_in sockaddr; + int fd, status; + int hostaddr, hostaddrPtr[2]; + int servport; + char *syscall; + VALUE sock; + + if (h) { + Check_SafeStr(h); + host = RSTRING(h)->ptr; + hostent = gethostbyname(host); + if (hostent == NULL) { + hostaddr = inet_addr(host); + if (hostaddr == -1) { + if (type == INET_SERVER && !strlen(host)) + hostaddr = INADDR_ANY; + else { +#ifdef HAVE_HSTRERROR + extern int h_errno; + Raise(eSocket, (char *)hstrerror(h_errno)); +#else + Raise(eSocket, "host not found"); +#endif + } + } + _hostent.h_addr_list = (char **)hostaddrPtr; + _hostent.h_addr_list[0] = (char *)&hostaddr; + _hostent.h_addr_list[1] = NULL; + _hostent.h_length = sizeof(hostaddr); + _hostent.h_addrtype = AF_INET; + hostent = &_hostent; + } + } + servent = NULL; + if (FIXNUM_P(serv)) { + servport = FIX2UINT(serv); + goto setup_servent; + } + Check_Type(serv, T_STRING); + servent = getservbyname(RSTRING(serv)->ptr, "tcp"); + if (servent == NULL) { + servport = strtoul(RSTRING(serv)->ptr, 0, 0); + if (servport == -1) { + Raise(eSocket, "no such servce %s", RSTRING(serv)->ptr); + } + setup_servent: + _servent.s_port = htons(servport); + _servent.s_proto = "tcp"; + servent = &_servent; + } + protoent = getprotobyname(servent->s_proto); + if (protoent == NULL) { + Raise(eSocket, "no such proto %s", servent->s_proto); + } + + fd = socket(PF_INET, SOCK_STREAM, protoent->p_proto); + + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + if (h) { + memcpy((char *)&(sockaddr.sin_addr.s_addr), + (char *) hostent->h_addr_list[0], + (size_t) hostent->h_length); + } + else { + sockaddr.sin_addr.s_addr = INADDR_ANY; + } + sockaddr.sin_port = servent->s_port; + + if (type == INET_SERVER) { + status = 1; + setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char*)&status,sizeof(status)); + status = bind(fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)); + syscall = "bind(2)"; + } + else { +#if defined(THREAD) && defined(HAVE_FCNTL) + status = thread_connect(fd, (struct sockaddr*)&sockaddr, + sizeof(sockaddr), type); +#else +#ifdef SOCKS + if (type == INET_SOCKS) { + status = Rconnect(fd, &sockaddr, sizeof(sockaddr)); + } + else +#endif + { + status = connect(fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)); + } +#endif + syscall = "connect(2)"; + } + + if (status < 0) { + close (fd); + rb_sys_fail(syscall); + } + if (type == INET_SERVER) listen(fd, 5); + + /* create new instance */ + sock = sock_new(class, fd); + + return sock; +} + +static VALUE +tcp_s_open(class, host, serv) + VALUE class, host, serv; +{ + Check_SafeStr(host); + return open_inet(class, host, serv, INET_CLIENT); +} + +#ifdef SOCKS +static VALUE +socks_s_open(class, host, serv) + VALUE class, host, serv; +{ + static init = 0; + + if (init == 0) { + SOCKSinit("ruby"); + init = 1; + } + + Check_SafeStr(host); + return open_inet(class, host, serv, INET_SOCKS); +} +#endif + +static VALUE +tcp_svr_s_open(argc, argv, class) + int argc; + VALUE *argv; + VALUE class; +{ + VALUE arg1, arg2; + + if (rb_scan_args(argc, argv, "11", &arg1, &arg2) == 2) + return open_inet(class, arg1, arg2, INET_SERVER); + else + return open_inet(class, 0, arg1, INET_SERVER); +} + +static VALUE +s_accept(class, fd, sockaddr, len) + VALUE class; + int fd; + struct sockaddr *sockaddr; + int *len; +{ + int fd2; + + retry: +#ifdef THREAD + thread_wait_fd(fd); +#endif + TRAP_BEG; + fd2 = accept(fd, sockaddr, len); + TRAP_END; + if (fd2 < 0) { + switch (errno) { + case EINTR: + case EWOULDBLOCK: +#if EAGAIN != EWOULDBLOCK + case EAGAIN: +#endif +#ifdef THREAD + thread_schedule(); +#endif + goto retry; + } + rb_sys_fail(0); + } + return sock_new(class, fd2); +} + +static VALUE +tcp_accept(sock) + VALUE sock; +{ + OpenFile *fptr; + struct sockaddr_in from; + int fromlen; + + GetOpenFile(sock, fptr); + fromlen = sizeof(struct sockaddr_in); + return s_accept(cTCPsocket, fileno(fptr->f), + (struct sockaddr*)&from, &fromlen); +} + +#ifdef HAVE_SYS_UN_H +static VALUE +open_unix(class, path, server) + VALUE class; + struct RString *path; + int server; +{ + struct sockaddr_un sockaddr; + int fd, status; + VALUE sock; + OpenFile *fptr; + + Check_SafeStr(path); + fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (fd < 0) rb_sys_fail("socket(2)"); + + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sun_family = AF_UNIX; + strncpy(sockaddr.sun_path, path->ptr, sizeof(sockaddr.sun_path)-1); + sockaddr.sun_path[sizeof(sockaddr.sun_path)-1] = '\0'; + + if (server) { + status = bind(fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)); + } + else { + status = connect(fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)); + } + + if (status < 0) { + close(fd); + rb_sys_fail(sockaddr.sun_path); + } + + if (server) listen(fd, 5); + + sock = sock_new(class, fd); + GetOpenFile(sock, fptr); + fptr->path = strdup(path->ptr); + + return sock; +} +#endif + +static void +setipaddr(name, addr) + char *name; + struct sockaddr_in *addr; +{ + int d1, d2, d3, d4; + char ch; + struct hostent *hp; + long x; + + if (name[0] == 0) { + addr->sin_addr.s_addr = INADDR_ANY; + } + else if (name[0] == '<' && strcmp(name, "<broadcast>") == 0) { + addr->sin_addr.s_addr = INADDR_BROADCAST; + } + else if (sscanf(name, "%d.%d.%d.%d%c", &d1, &d2, &d3, &d4, &ch) == 4 && + 0 <= d1 && d1 <= 255 && 0 <= d2 && d2 <= 255 && + 0 <= d3 && d3 <= 255 && 0 <= d4 && d4 <= 255) { + addr->sin_addr.s_addr = htonl( + ((long) d1 << 24) | ((long) d2 << 16) | + ((long) d3 << 8) | ((long) d4 << 0)); + } + else { + hp = gethostbyname(name); + if (!hp) { +#ifdef HAVE_HSTRERROR + extern int h_errno; + Raise(eSocket, (char *)hstrerror(h_errno)); +#else + Raise(eSocket, "host not found"); +#endif + } + memcpy((char *) &addr->sin_addr, hp->h_addr, hp->h_length); + } +} + +static VALUE +mkipaddr(x) + unsigned long x; +{ + char buf[16]; + + x = ntohl(x); + sprintf(buf, "%d.%d.%d.%d", + (int) (x>>24) & 0xff, (int) (x>>16) & 0xff, + (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff); + return str_new2(buf); +} + +static VALUE +tcpaddr(sockaddr) + struct sockaddr_in *sockaddr; +{ + VALUE family, port, addr1, addr2; + VALUE ary; + struct hostent *hostent; + + family = str_new2("AF_INET"); + hostent = gethostbyaddr((char*)&sockaddr->sin_addr.s_addr, + sizeof(sockaddr->sin_addr), + AF_INET); + addr1 = 0; + if (hostent) { + addr1 = str_new2(hostent->h_name); + } + addr2 = mkipaddr(sockaddr->sin_addr.s_addr); + if (!addr1) addr1 = addr2; + + port = INT2FIX(ntohs(sockaddr->sin_port)); + ary = ary_new3(4, family, port, addr1, addr2); + + return ary; +} + +static VALUE +tcp_addr(sock) + VALUE sock; +{ + OpenFile *fptr; + struct sockaddr_in addr; + int len = sizeof addr; + + GetOpenFile(sock, fptr); + + if (getsockname(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0) + rb_sys_fail("getsockname(2)"); + return tcpaddr(&addr); +} + +static VALUE +tcp_peeraddr(sock) + VALUE sock; +{ + OpenFile *fptr; + struct sockaddr_in addr; + int len = sizeof addr; + + GetOpenFile(sock, fptr); + + if (getpeername(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0) + rb_sys_fail("getpeername(2)"); + return tcpaddr(&addr); +} + +static VALUE +tcp_recvfrom(argc, argv, sock) + int argc; + VALUE *argv; + VALUE sock; +{ + return s_recv(sock, argc, argv, 1); +} + +static VALUE +tcp_s_getaddress(obj, host) + VALUE obj, host; +{ + struct sockaddr_in addr; + struct hostent *h; + + if (obj_is_kind_of(host, cInteger)) { + int i = NUM2INT(host); + addr.sin_addr.s_addr = htonl(i); + } + else { + Check_Type(host, T_STRING); + setipaddr(RSTRING(host)->ptr, &addr); + } + + return mkipaddr(addr.sin_addr.s_addr); +} + +#ifdef HAVE_SYS_UN_H +static VALUE +unix_s_sock_open(sock, path) + VALUE sock, path; +{ + return open_unix(sock, path, 0); +} + +static VALUE +unix_path(sock) + VALUE sock; +{ + OpenFile *fptr; + + GetOpenFile(sock, fptr); + if (fptr->path == 0) { + struct sockaddr_un addr; + int len = sizeof(addr); + if (getsockname(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0) + rb_sys_fail(0); + fptr->path = strdup(addr.sun_path); + } + return str_new2(fptr->path); +} + +static VALUE +unix_svr_s_open(class, path) + VALUE class, path; +{ + return open_unix(class, path, 1); +} + +static VALUE +unix_recvfrom(argc, argv, sock) + int argc; + VALUE *argv; + VALUE sock; +{ + return s_recv(sock, argc, argv, 2); +} + +static VALUE +unix_accept(sock) + VALUE sock; +{ + OpenFile *fptr; + struct sockaddr_un from; + int fromlen; + + GetOpenFile(sock, fptr); + fromlen = sizeof(struct sockaddr_un); + return s_accept(cUNIXsocket, fileno(fptr->f), + (struct sockaddr*)&from, &fromlen); +} + +static VALUE +unixaddr(sockaddr) + struct sockaddr_un *sockaddr; +{ + return assoc_new(str_new2("AF_UNIX"),str_new2(sockaddr->sun_path)); +} + +static VALUE +unix_addr(sock) + VALUE sock; +{ + OpenFile *fptr; + struct sockaddr_un addr; + int len = sizeof addr; + + GetOpenFile(sock, fptr); + + if (getsockname(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0) + rb_sys_fail("getsockname(2)"); + return unixaddr(&addr); +} + +static VALUE +unix_peeraddr(sock) + VALUE sock; +{ + OpenFile *fptr; + struct sockaddr_un addr; + int len = sizeof addr; + + GetOpenFile(sock, fptr); + + if (getpeername(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0) + rb_sys_fail("getsockname(2)"); + return unixaddr(&addr); +} +#endif + +static void +setup_domain_and_type(domain, dv, type, tv) + VALUE domain, type; + int *dv, *tv; +{ + char *ptr; + + if (TYPE(domain) == T_STRING) { + ptr = RSTRING(domain)->ptr; + if (strcmp(ptr, "PF_INET") == 0) + *dv = PF_INET; +#ifdef PF_UNIX + else if (strcmp(ptr, "PF_UNIX") == 0) + *dv = PF_UNIX; +#endif +#ifdef PF_IMPLINK + else if (strcmp(ptr, "PF_IMPLINK") == 0) + *dv = PF_IMPLINK; +#endif +#ifdef PF_AX25 + else if (strcmp(ptr, "PF_AX25") == 0) + *dv = PF_AX25; +#endif +#ifdef PF_IPX + else if (strcmp(ptr, "PF_IPX") == 0) + *dv = PF_IPX; +#endif + else + Raise(eSocket, "Unknown socket domain %s", ptr); + } + else { + *dv = NUM2INT(domain); + } + if (TYPE(type) == T_STRING) { + ptr = RSTRING(type)->ptr; + if (strcmp(ptr, "SOCK_STREAM") == 0) + *tv = SOCK_STREAM; + else if (strcmp(ptr, "SOCK_DGRAM") == 0) + *tv = SOCK_DGRAM; +#ifdef SOCK_RAW + else if (strcmp(ptr, "SOCK_RAW") == 0) + *tv = SOCK_RAW; +#endif +#ifdef SOCK_SEQPACKET + else if (strcmp(ptr, "SOCK_SEQPACKET") == 0) + *tv = SOCK_SEQPACKET; +#endif +#ifdef SOCK_RDM + else if (strcmp(ptr, "SOCK_RDM") == 0) + *tv = SOCK_RDM; +#endif +#ifdef SOCK_PACKET + else if (strcmp(ptr, "SOCK_PACKET") == 0) + *tv = SOCK_PACKET; +#endif + else + Raise(eSocket, "Unknown socket type %s", ptr); + } + else { + *tv = NUM2INT(type); + } +} + +static VALUE +sock_s_open(class, domain, type, protocol) + VALUE class, domain, type, protocol; +{ + int fd; + int d, t; + + setup_domain_and_type(domain, &d, type, &t); + fd = socket(d, t, NUM2INT(protocol)); + if (fd < 0) rb_sys_fail("socket(2)"); + return sock_new(class, fd); +} + +static VALUE +sock_s_for_fd(class, fd) + VALUE class, fd; +{ + return sock_new(class, NUM2INT(fd)); +} + +static VALUE +sock_s_socketpair(class, domain, type, protocol) + VALUE class, domain, type, protocol; +{ +#if !defined(__CYGWIN32__) && !defined(NT) + int fd; + int d, t, sp[2]; + + setup_domain_and_type(domain, &d, type, &t); + if (socketpair(d, t, NUM2INT(protocol), sp) < 0) + rb_sys_fail("socketpair(2)"); + + return assoc_new(sock_new(class, sp[0]), sock_new(class, sp[1])); +#else + rb_notimplement(); +#endif +} + +static VALUE +sock_connect(sock, addr) + VALUE sock; + struct RString *addr; +{ + OpenFile *fptr; + + Check_Type(addr, T_STRING); + str_modify(addr); + + GetOpenFile(sock, fptr); + retry: + if (connect(fileno(fptr->f), (struct sockaddr*)addr->ptr, addr->len) < 0) { + switch (errno) { + case EINTR: + case EWOULDBLOCK: +#if EAGAIN != EWOULDBLOCK + case EAGAIN: +#endif +#ifdef THREAD + thread_schedule(); +#endif + goto retry; + } + rb_sys_fail("connect(2)"); + } + + return INT2FIX(0); +} + +static VALUE +sock_bind(sock, addr) + VALUE sock; + struct RString *addr; +{ + OpenFile *fptr; + + Check_Type(addr, T_STRING); + str_modify(addr); + + GetOpenFile(sock, fptr); + if (bind(fileno(fptr->f), (struct sockaddr*)addr->ptr, addr->len) < 0) + rb_sys_fail("bind(2)"); + + return INT2FIX(0); +} + +static VALUE +sock_listen(sock, log) + VALUE sock, log; +{ + OpenFile *fptr; + + GetOpenFile(sock, fptr); + if (listen(fileno(fptr->f), NUM2INT(log)) < 0) + rb_sys_fail("listen(2)"); + + return INT2FIX(0); +} + +static VALUE +sock_recvfrom(argc, argv, sock) + int argc; + VALUE *argv; + VALUE sock; +{ + return s_recv(sock, argc, argv, 3); +} + +static VALUE +sock_accept(sock) + VALUE sock; +{ + OpenFile *fptr; + VALUE addr, sock2; + char buf[1024]; + int len = sizeof buf; + + GetOpenFile(sock, fptr); + sock2 = s_accept(cSocket,fileno(fptr->f),(struct sockaddr*)buf,&len); + + return assoc_new(sock2, str_new(buf, len)); +} + +#ifdef HAVE_GETHOSTNAME +static VALUE +sock_gethostname(obj) + VALUE obj; +{ + char buf[1024]; + + if (gethostname(buf, (int)sizeof buf - 1) < 0) + rb_sys_fail("gethostname"); + + buf[sizeof buf - 1] = '\0'; + return str_new2(buf); +} +#else +#ifdef HAVE_UNAME + +#include <sys/utsname.h> + +static VALUE +sock_gethostname(obj) + VALUE obj; +{ + struct utsname un; + + uname(&un); + return str_new2(un.nodename); +} +#else +static VALUE +sock_gethostname(obj) + VALUE obj; +{ + rb_notimplement(); +} +#endif +#endif + +static VALUE +mkhostent(h) + struct hostent *h; +{ + struct sockaddr_in addr; + char **pch; + VALUE ary, names; + + if (h == NULL) { +#ifdef HAVE_HSTRERROR + extern int h_errno; + Raise(eSocket, (char *)hstrerror(h_errno)); +#else + Raise(eSocket, "host not found"); +#endif + } + ary = ary_new(); + ary_push(ary, str_new2(h->h_name)); + names = ary_new(); + ary_push(ary, names); + for (pch = h->h_aliases; *pch; pch++) { + ary_push(names, str_new2(*pch)); + } + ary_push(ary, INT2FIX(h->h_length)); +#ifdef h_addr + for (pch = h->h_addr_list; *pch; pch++) { + ary_push(ary, str_new(*pch, h->h_length)); + } +#else + ary_push(ary, str_new(h->h_addr, h->h_length)); +#endif + + return ary; +} + +static VALUE +sock_s_gethostbyname(obj, host) + VALUE obj, host; +{ + struct sockaddr_in addr; + struct hostent *h; + + if (obj_is_kind_of(host, cInteger)) { + int i = NUM2INT(host); + addr.sin_addr.s_addr = htonl(i); + } + else { + Check_Type(host, T_STRING); + setipaddr(RSTRING(host)->ptr, &addr); + } + h = gethostbyaddr((char *)&addr.sin_addr, + sizeof(addr.sin_addr), + AF_INET); + + return mkhostent(h); +} + +static VALUE +sock_s_gethostbyaddr(argc, argv) + int argc; + VALUE *argv; +{ + VALUE vaddr, vtype; + int type; + + struct sockaddr_in *addr; + struct hostent *h; + + rb_scan_args(argc, argv, "11", &addr, &type); + Check_Type(addr, T_STRING); + if (!NIL_P(type)) { + type = NUM2INT(vtype); + } + else { + type = AF_INET; + } + + h = gethostbyaddr(RSTRING(addr)->ptr, RSTRING(addr)->len, type); + + return mkhostent(h); +} + +static VALUE +sock_s_getservbyaname(argc, argv) + int argc; + VALUE *argv; +{ + VALUE service, protocol; + char *name, *proto; + struct servent *sp; + int port; + + rb_scan_args(argc, argv, "11", &service, &protocol); + Check_Type(service, T_STRING); + if (NIL_P(protocol)) proto = "tcp"; + else proto = RSTRING(protocol)->ptr; + + sp = getservbyname(RSTRING(service)->ptr, proto); + if (!sp) { + Raise(eSocket, "service/proto not found"); + } + port = ntohs(sp->s_port); + + return INT2FIX(port); +} + +Init_socket() +{ + eSocket = rb_define_class("SocketError", eException); + + cBasicSocket = rb_define_class("BasicSocket", cIO); + rb_undef_method(CLASS_OF(cBasicSocket), "new"); + rb_define_method(cBasicSocket, "shutdown", bsock_shutdown, -1); + rb_define_method(cBasicSocket, "setsockopt", bsock_setsockopt, 3); + rb_define_method(cBasicSocket, "getsockopt", bsock_getsockopt, 2); + rb_define_method(cBasicSocket, "getsockname", bsock_getsockname, 0); + rb_define_method(cBasicSocket, "getpeername", bsock_getpeername, 0); + rb_define_method(cBasicSocket, "send", bsock_send, -1); + rb_define_method(cBasicSocket, "recv", bsock_recv, -1); + + cTCPsocket = rb_define_class("TCPsocket", cBasicSocket); + rb_define_singleton_method(cTCPsocket, "open", tcp_s_open, 2); + rb_define_singleton_method(cTCPsocket, "new", tcp_s_open, 2); + rb_define_method(cTCPsocket, "addr", tcp_addr, 0); + rb_define_method(cTCPsocket, "peeraddr", tcp_peeraddr, 0); + rb_define_singleton_method(cTCPsocket, "getaddress", tcp_s_getaddress, 1); + rb_define_method(cTCPsocket, "recvfrom", tcp_recvfrom, -1); + +#ifdef SOCKS + cSOCKSsocket = rb_define_class("SOCKSsocket", cTCPsocket); + rb_define_singleton_method(cSOCKSsocket, "open", socks_s_open, 2); + rb_define_singleton_method(cSOCKSsocket, "new", socks_s_open, 2); +#endif + + cTCPserver = rb_define_class("TCPserver", cTCPsocket); + rb_define_singleton_method(cTCPserver, "open", tcp_svr_s_open, -1); + rb_define_singleton_method(cTCPserver, "new", tcp_svr_s_open, -1); + rb_define_method(cTCPserver, "accept", tcp_accept, 0); + +#ifdef HAVE_SYS_UN_H + cUNIXsocket = rb_define_class("UNIXsocket", cBasicSocket); + rb_define_singleton_method(cUNIXsocket, "open", unix_s_sock_open, 1); + rb_define_singleton_method(cUNIXsocket, "new", unix_s_sock_open, 1); + rb_define_method(cUNIXsocket, "path", unix_path, 0); + rb_define_method(cUNIXsocket, "addr", unix_addr, 0); + rb_define_method(cUNIXsocket, "peeraddr", unix_peeraddr, 0); + rb_define_method(cUNIXsocket, "recvfrom", unix_recvfrom, -1); + + cUNIXserver = rb_define_class("UNIXserver", cUNIXsocket); + rb_define_singleton_method(cUNIXserver, "open", unix_svr_s_open, 1); + rb_define_singleton_method(cUNIXserver, "new", unix_svr_s_open, 1); + rb_define_method(cUNIXserver, "accept", unix_accept, 0); +#endif + + cSocket = rb_define_class("Socket", cBasicSocket); + rb_define_singleton_method(cSocket, "open", sock_s_open, 3); + rb_define_singleton_method(cSocket, "new", sock_s_open, 3); + rb_define_singleton_method(cSocket, "for_fd", sock_s_for_fd, 1); + + rb_define_method(cSocket, "connect", sock_connect, 1); + rb_define_method(cSocket, "bind", sock_bind, 1); + rb_define_method(cSocket, "listen", sock_listen, 1); + rb_define_method(cSocket, "accept", sock_accept, 0); + + rb_define_method(cSocket, "recvfrom", sock_recvfrom, -1); + + rb_define_singleton_method(cSocket, "socketpair", sock_s_socketpair, 3); + rb_define_singleton_method(cSocket, "pair", sock_s_socketpair, 3); + rb_define_singleton_method(cSocket, "gethostname", sock_gethostname, 0); + rb_define_singleton_method(cSocket, "gethostbyname", sock_s_gethostbyname, 1); + rb_define_singleton_method(cSocket, "gethostbyaddr", sock_s_gethostbyaddr, -1); + rb_define_singleton_method(cSocket, "getservbyname", sock_s_getservbyaname, -1); + + /* constants */ + rb_define_const(cSocket, "SOCK_STREAM", INT2FIX(SOCK_STREAM)); + rb_define_const(cSocket, "SOCK_DGRAM", INT2FIX(SOCK_DGRAM)); + rb_define_const(cSocket, "SOCK_RAW", INT2FIX(SOCK_RAW)); +#ifdef SOCK_RDM + rb_define_const(cSocket, "SOCK_RDM", INT2FIX(SOCK_RDM)); +#endif +#ifdef SOCK_SEQPACKET + rb_define_const(cSocket, "SOCK_SEQPACKET", INT2FIX(SOCK_SEQPACKET)); +#endif +#ifdef SOCK_PACKET + rb_define_const(cSocket, "SOCK_PACKET", INT2FIX(SOCK_PACKET)); +#endif + + rb_define_const(cSocket, "AF_INET", INT2FIX(AF_INET)); + rb_define_const(cSocket, "PF_INET", INT2FIX(PF_INET)); +#ifdef AF_UNIX + rb_define_const(cSocket, "AF_UNIX", INT2FIX(AF_UNIX)); + rb_define_const(cSocket, "PF_UNIX", INT2FIX(PF_UNIX)); +#endif +#ifdef AF_AX25 + rb_define_const(cSocket, "AF_AX25", INT2FIX(AF_AX25)); + rb_define_const(cSocket, "PF_AX25", INT2FIX(PF_AX25)); +#endif +#ifdef AF_IPX + rb_define_const(cSocket, "AF_IPX", INT2FIX(AF_IPX)); + rb_define_const(cSocket, "PF_IPX", INT2FIX(PF_IPX)); +#endif +#ifdef AF_APPLETALK + rb_define_const(cSocket, "AF_APPLETALK", INT2FIX(AF_APPLETALK)); + rb_define_const(cSocket, "PF_APPLETALK", INT2FIX(PF_APPLETALK)); +#endif + + rb_define_const(cSocket, "MSG_OOB", INT2FIX(MSG_OOB)); + rb_define_const(cSocket, "MSG_PEEK", INT2FIX(MSG_PEEK)); + rb_define_const(cSocket, "MSG_DONTROUTE", INT2FIX(MSG_DONTROUTE)); + + rb_define_const(cSocket, "SOL_SOCKET", INT2FIX(SOL_SOCKET)); +#ifdef SOL_IP + rb_define_const(cSocket, "SOL_IP", INT2FIX(SOL_IP)); +#endif +#ifdef SOL_IPX + rb_define_const(cSocket, "SOL_IPX", INT2FIX(SOL_IPX)); +#endif +#ifdef SOL_AX25 + rb_define_const(cSocket, "SOL_AX25", INT2FIX(SOL_AX25)); +#endif +#ifdef SOL_ATALK + rb_define_const(cSocket, "SOL_ATALK", INT2FIX(SOL_ATALK)); +#endif +#ifdef SOL_TCP + rb_define_const(cSocket, "SOL_TCP", INT2FIX(SOL_TCP)); +#endif +#ifdef SOL_UDP + rb_define_const(cSocket, "SOL_UDP", INT2FIX(SOL_UDP)); +#endif + +#ifdef SO_DEBUG + rb_define_const(cSocket, "SO_DEBUG", INT2FIX(SO_DEBUG)); +#endif + rb_define_const(cSocket, "SO_REUSEADDR", INT2FIX(SO_REUSEADDR)); +#ifdef SO_TYPE + rb_define_const(cSocket, "SO_TYPE", INT2FIX(SO_TYPE)); +#endif +#ifdef SO_ERROR + rb_define_const(cSocket, "SO_ERROR", INT2FIX(SO_ERROR)); +#endif +#ifdef SO_DONTROUTE + rb_define_const(cSocket, "SO_DONTROUTE", INT2FIX(SO_DONTROUTE)); +#endif +#ifdef SO_BROADCAST + rb_define_const(cSocket, "SO_BROADCAST", INT2FIX(SO_BROADCAST)); +#endif +#ifdef SO_SNDBUF + rb_define_const(cSocket, "SO_SNDBUF", INT2FIX(SO_SNDBUF)); +#endif +#ifdef SO_RCVBUF + rb_define_const(cSocket, "SO_RCVBUF", INT2FIX(SO_RCVBUF)); +#endif + rb_define_const(cSocket, "SO_KEEPALIVE", INT2FIX(SO_KEEPALIVE)); +#ifdef SO_OOBINLINE + rb_define_const(cSocket, "SO_OOBINLINE", INT2FIX(SO_OOBINLINE)); +#endif +#ifdef SO_NO_CHECK + rb_define_const(cSocket, "SO_NO_CHECK", INT2FIX(SO_NO_CHECK)); +#endif +#ifdef SO_PRIORITY + rb_define_const(cSocket, "SO_PRIORITY", INT2FIX(SO_PRIORITY)); +#endif + rb_define_const(cSocket, "SO_LINGER", INT2FIX(SO_LINGER)); + +#ifdef SOPRI_INTERACTIVE + rb_define_const(cSocket, "SOPRI_INTERACTIVE", INT2FIX(SOPRI_INTERACTIVE)); +#endif +#ifdef SOPRI_NORMAL + rb_define_const(cSocket, "SOPRI_NORMAL", INT2FIX(SOPRI_NORMAL)); +#endif +#ifdef SOPRI_BACKGROUND + rb_define_const(cSocket, "SOPRI_BACKGROUND", INT2FIX(SOPRI_BACKGROUND)); +#endif + +#ifdef IP_MULTICAST_IF + rb_define_const(cSocket, "IP_MULTICAST_IF", INT2FIX(IP_MULTICAST_IF)); +#endif +#ifdef IP_MULTICAST_TTL + rb_define_const(cSocket, "IP_MULTICAST_TTL", INT2FIX(IP_MULTICAST_TTL)); +#endif +#ifdef IP_MULTICAST_LOOP + rb_define_const(cSocket, "IP_MULTICAST_LOOP", INT2FIX(IP_MULTICAST_LOOP)); +#endif +#ifdef IP_ADD_MEMBERSHIP + rb_define_const(cSocket, "IP_ADD_MEMBERSHIP", INT2FIX(IP_ADD_MEMBERSHIP)); +#endif + +#ifdef IP_DEFAULT_MULTICAST_TTL + rb_define_const(cSocket, "IP_DEFAULT_MULTICAST_TTL", INT2FIX(IP_DEFAULT_MULTICAST_TTL)); +#endif +#ifdef IP_DEFAULT_MULTICAST_LOOP + rb_define_const(cSocket, "IP_DEFAULT_MULTICAST_LOOP", INT2FIX(IP_DEFAULT_MULTICAST_LOOP)); +#endif +#ifdef IP_MAX_MEMBERSHIPS + rb_define_const(cSocket, "IP_MAX_MEMBERSHIPS", INT2FIX(IP_MAX_MEMBERSHIPS)); +#endif + +#ifdef IPX_TYPE + rb_define_const(cSocket, "IPX_TYPE", INT2FIX(IPX_TYPE)); +#endif + +#ifdef TCP_NODELAY + rb_define_const(cSocket, "TCP_NODELAY", INT2FIX(TCP_NODELAY)); +#endif +#ifdef TCP_MAXSEG + rb_define_const(cSocket, "TCP_MAXSEG", INT2FIX(TCP_MAXSEG)); +#endif +} diff --git a/ext/tkutil/MANIFEST b/ext/tkutil/MANIFEST new file mode 100644 index 0000000000..870e04b586 --- /dev/null +++ b/ext/tkutil/MANIFEST @@ -0,0 +1,3 @@ +MANIFEST +tkutil.c +depend diff --git a/ext/tkutil/depend b/ext/tkutil/depend new file mode 100644 index 0000000000..ead83eda57 --- /dev/null +++ b/ext/tkutil/depend @@ -0,0 +1 @@ +tkutil.o: tkutil.c ../../ruby.h ../../config.h ../../defines.h diff --git a/ext/tkutil/tkutil.c b/ext/tkutil/tkutil.c new file mode 100644 index 0000000000..540995eb82 --- /dev/null +++ b/ext/tkutil/tkutil.c @@ -0,0 +1,46 @@ +/************************************************ + + tk.c - + + $Author$ + $Date$ + created at: Fri Nov 3 00:47:54 JST 1995 + +************************************************/ + +#include "ruby.h" + +static VALUE +tk_eval_cmd(argc, argv) + int argc; + VALUE argv[]; +{ + VALUE cmd, rest; + + rb_scan_args(argc, argv, "1*", &cmd, &rest); + rb_eval_cmd(cmd, rest); + return Qnil; +} + +static VALUE +tk_s_new(argc, argv, class) + int argc; + VALUE *argv; + VALUE class; +{ + VALUE obj = obj_alloc(class); + + rb_funcall2(obj, rb_intern("initialize"), argc, argv); + if (iterator_p()) rb_yield_0(obj, obj); + return obj; +} + +Init_tkutil() +{ + VALUE mTK = rb_define_module("TkUtil"); + VALUE cTK = rb_define_class("TkKernel", cObject); + + rb_define_singleton_method(mTK, "eval_cmd", tk_eval_cmd, -1); + + rb_define_singleton_method(cTK, "new", tk_s_new, -1); +} diff --git a/file.c b/file.c new file mode 100644 index 0000000000..0caa98adb1 --- /dev/null +++ b/file.c @@ -0,0 +1,1756 @@ +/************************************************ + + file.c - + + $Author$ + $Date$ + created at: Mon Nov 15 12:24:34 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include "io.h" + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_SYS_PARAM_H +# include <sys/param.h> +#else +# define MAXPATHLEN 1024 +#endif + +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#else +#ifndef NT +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; +#endif /* NT */ +#endif + +#ifdef HAVE_UTIME_H +#include <utime.h> +#endif + +#ifdef HAVE_PWD_H +#include <pwd.h> +#endif + +#ifdef HAVE_STRING_H +# include <string.h> +#else +char *strrchr(); +#endif + +#ifdef NT +#include <sys/stat.h> +#endif + +#ifndef NT +char *strdup(); +char *getenv(); +#endif + +extern VALUE cIO; +VALUE cFile; +VALUE mFileTest; +static VALUE sStat; + +VALUE time_new(); + +VALUE +file_open(fname, mode) + char *fname, *mode; +{ + OpenFile *fptr; + NEWOBJ(port, struct RFile); + OBJSETUP(port, cFile, T_FILE); + MakeOpenFile(port, fptr); + + fptr->mode = io_mode_flags(mode); + fptr->f = rb_fopen(fname, mode); + fptr->path = strdup(fname); + + return (VALUE)port; +} + +static VALUE +file_s_open(argc, argv, class) + int argc; + VALUE *argv; + VALUE class; +{ + VALUE fname, vmode, file; + char *mode; + + rb_scan_args(argc, argv, "11", &fname, &vmode); + Check_SafeStr(fname); + if (!NIL_P(vmode)) { + Check_Type(vmode, T_STRING); + mode = RSTRING(vmode)->ptr; + } + else { + mode = "r"; + } + file = file_open(RSTRING(fname)->ptr, mode); + + RBASIC(file)->class = class; + return file; +} + +static VALUE +file_reopen(argc, argv, file) + int argc; + VALUE *argv; + VALUE file; +{ + VALUE fname, nmode; + char *mode; + OpenFile *fptr; + + if (rb_scan_args(argc, argv, "11", &fname, &nmode) == 1) { + if (TYPE(fname) == T_FILE) { /* fname must be IO */ + return io_reopen(file, fname); + } + } + + Check_SafeStr(fname); + if (!NIL_P(nmode)) { + Check_Type(nmode, T_STRING); + mode = RSTRING(nmode)->ptr; + } + else { + mode = "r"; + } + + GetOpenFile(file, fptr); + if (fptr->path) free(fptr->path); + fptr->path = strdup(RSTRING(fname)->ptr); + fptr->mode = io_mode_flags(mode); + if (!fptr->f) { + fptr->f = rb_fopen(RSTRING(fname)->ptr, mode); + if (fptr->f2) { + fclose(fptr->f2); + fptr->f2 = NULL; + } + return file; + } + + if (freopen(RSTRING(fname)->ptr, mode, fptr->f) == NULL) { + rb_sys_fail(fptr->path); + } + if (fptr->f2) { + if (freopen(RSTRING(fname)->ptr, "w", fptr->f2) == NULL) { + rb_sys_fail(fptr->path); + } + } + + return file; +} + +static int +apply2files(func, args, arg) + int (*func)(); + struct RArray *args; + void *arg; +{ + int i; + VALUE path; + + for (i=0; i<args->len; i++) { + Check_SafeStr(args->ptr[i]); + } + + for (i=0; i<args->len; i++) { + path = args->ptr[i]; + if ((*func)(RSTRING(path)->ptr, arg) < 0) + rb_sys_fail(RSTRING(path)->ptr); + } + + return args->len; +} + +static VALUE +file_tell(obj) + VALUE obj; +{ + OpenFile *fptr; + long pos; + + GetOpenFile(obj, fptr); + + pos = ftell(fptr->f); + if (ferror(fptr->f) != 0) rb_sys_fail(0); + + return int2inum(pos); +} + +static VALUE +file_seek(obj, offset, ptrname) + VALUE obj, offset, ptrname; +{ + OpenFile *fptr; + long pos; + + GetOpenFile(obj, fptr); + + pos = fseek(fptr->f, NUM2INT(offset), NUM2INT(ptrname)); + if (pos != 0) rb_sys_fail(0); + clearerr(fptr->f); + + return obj; +} + +static VALUE +file_set_pos(obj, offset) + VALUE obj, offset; +{ + OpenFile *fptr; + long pos; + + GetOpenFile(obj, fptr); + pos = fseek(fptr->f, NUM2INT(offset), 0); + if (pos != 0) rb_sys_fail(0); + clearerr(fptr->f); + + return obj; +} + +static VALUE +file_rewind(obj) + VALUE obj; +{ + OpenFile *fptr; + + GetOpenFile(obj, fptr); + if (fseek(fptr->f, 0L, 0) != 0) rb_sys_fail(0); + clearerr(fptr->f); + + return obj; +} + +static VALUE +file_eof(obj) + VALUE obj; +{ + OpenFile *fptr; + + GetOpenFile(obj, fptr); + if (feof(fptr->f) == 0) return FALSE; + return TRUE; +} + +static VALUE +file_path(obj) + VALUE obj; +{ + OpenFile *fptr; + + GetOpenFile(obj, fptr); + return str_new2(fptr->path); +} + +static VALUE +file_isatty(obj) + VALUE obj; +{ + return FALSE; +} + +#include <sys/types.h> +#ifndef NT +#include <sys/file.h> +#else +#include "missing/file.h" +#endif +#include <sys/stat.h> + +static VALUE +stat_new(st) + struct stat *st; +{ + if (!st) Bug("stat_new() called with bad value"); + return struct_new(sStat, + INT2FIX((int)st->st_dev), + INT2FIX((int)st->st_ino), + INT2FIX((int)st->st_mode), + INT2FIX((int)st->st_nlink), + INT2FIX((int)st->st_uid), + INT2FIX((int)st->st_gid), +#ifdef HAVE_ST_RDEV + INT2FIX((int)st->st_rdev), +#else + INT2FIX(0), +#endif + INT2FIX((int)st->st_size), +#ifdef HAVE_ST_BLKSIZE + INT2FIX((int)st->st_blksize), +#else + INT2FIX(0), +#endif +#ifdef HAVE_ST_BLOCKS + INT2FIX((int)st->st_blocks), +#else + INT2FIX(0), +#endif + time_new(st->st_atime, 0), + time_new(st->st_mtime, 0), + time_new(st->st_ctime, 0)); +} + +static VALUE +file_s_stat(obj, fname) + VALUE obj; + struct RString *fname; +{ + struct stat st; + + Check_SafeStr(fname); + if (stat(fname->ptr, &st) == -1) { + rb_sys_fail(fname->ptr); + } + return stat_new(&st); +} + +static VALUE +file_stat(obj) + VALUE obj; +{ + OpenFile *fptr; + struct stat st; + + GetOpenFile(obj, fptr); + if (fstat(fileno(fptr->f), &st) == -1) { + rb_sys_fail(fptr->path); + } + return stat_new(&st); +} + +static VALUE +file_s_lstat(obj, fname) + VALUE obj; + struct RString *fname; +{ +#if !defined(MSDOS) && !defined(NT) + struct stat st; + + Check_SafeStr(fname); + if (lstat(fname->ptr, &st) == -1) { + rb_sys_fail(fname->ptr); + } + return stat_new(&st); +#else + rb_notimplement(); +#endif +} + +static VALUE +file_lstat(obj) + VALUE obj; +{ +#if !defined(MSDOS) && !defined(NT) + OpenFile *fptr; + struct stat st; + + GetOpenFile(obj, fptr); + if (lstat(fptr->path, &st) == -1) { + rb_sys_fail(fptr->path); + } + return stat_new(&st); +#else + rb_notimplement(); +#endif +} + +static int +group_member(gid) + GETGROUPS_T gid; +{ +#ifndef NT + if (getgid() == gid || getegid() == gid) + return TRUE; + +# ifdef HAVE_GETGROUPS +# ifndef NGROUPS +# define NGROUPS 32 +# endif + { + GETGROUPS_T gary[NGROUPS]; + int anum; + + anum = getgroups(NGROUPS, gary); + while (--anum >= 0) + if (gary[anum] == gid) + return TRUE; + } +# endif +#endif + return FALSE; +} + +#ifndef S_IXUGO +# define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH) +#endif + +int +eaccess(path, mode) + char *path; + int mode; +{ +#ifndef NT + struct stat st; + static int euid = -1; + + if (stat(path, &st) < 0) return (-1); + + if (euid == -1) + euid = geteuid (); + + if (euid == 0) + { + /* Root can read or write any file. */ + if (mode != X_OK) + return 0; + + /* Root can execute any file that has any one of the execute + bits set. */ + if (st.st_mode & S_IXUGO) + return 0; + } + +#if defined(DJGPP) + { + int stat_mode = 0; + if (mode & X_OK) + stat_mode |= S_IXOTH; + if (mode & W_OK) + stat_mode |= S_IWOTH; + if (mode & R_OK) + stat_mode |= S_IROTH; + mode = stat_mode; + } +#endif + + if (st.st_uid == euid) /* owner */ + mode <<= 6; + else if (group_member (st.st_gid)) + mode <<= 3; + + if (st.st_mode & mode) return 0; + + return -1; +#else /* !NT*/ + return 0; +#endif +} + +static VALUE +test_d(obj, fname) + VALUE obj; + struct RString *fname; +{ +#ifndef S_ISDIR +# define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR) +#endif + + struct stat st; + + Check_SafeStr(fname); + if (stat(fname->ptr, &st) < 0) return FALSE; + if (S_ISDIR(st.st_mode)) return TRUE; + return FALSE; +} + +static VALUE +test_p(obj, fname) + VALUE obj; + struct RString *fname; +{ +#ifdef S_IFIFO +# ifndef S_ISFIFO +# define S_ISFIFO(m) ((m & S_IFMT) == S_IFIFO) +# endif + + struct stat st; + + Check_SafeStr(fname); + if (stat(fname->ptr, &st) < 0) return FALSE; + if (S_ISFIFO(st.st_mode)) return TRUE; + +#endif + return FALSE; +} + +static VALUE +test_l(obj, fname) + VALUE obj; + struct RString *fname; +{ +#ifndef S_ISLNK +# ifdef _S_ISLNK +# define S_ISLNK(m) _S_ISLNK(m) +# else +# ifdef _S_IFLNK +# define S_ISLNK(m) ((m & S_IFMT) == _S_IFLNK) +# else +# ifdef S_IFLNK +# define S_ISLNK(m) ((m & S_IFMT) == S_IFLNK) +# endif +# endif +# endif +#endif + +#ifdef S_ISLNK + struct stat st; + + Check_SafeStr(fname); + if (lstat(fname->ptr, &st) < 0) return FALSE; + if (S_ISLNK(st.st_mode)) return TRUE; + +#endif + return FALSE; +} + +static VALUE +test_S(obj, fname) + VALUE obj; + struct RString *fname; +{ +#ifndef S_ISSOCK +# ifdef _S_ISSOCK +# define S_ISSOCK(m) _S_ISSOCK(m) +# else +# ifdef _S_IFSOCK +# define S_ISSOCK(m) ((m & S_IFMT) == _S_IFSOCK) +# else +# ifdef S_IFSOCK +# define S_ISSOCK(m) ((m & S_IFMT) == S_IFSOCK) +# endif +# endif +# endif +#endif + +#ifdef S_ISSOCK + struct stat st; + + Check_SafeStr(fname); + if (stat(fname->ptr, &st) < 0) return FALSE; + if (S_ISSOCK(st.st_mode)) return TRUE; + +#endif + return FALSE; +} + +static VALUE +test_b(obj, fname) + VALUE obj; + struct RString *fname; +{ +#ifndef S_ISBLK +# ifdef S_IFBLK +# define S_ISBLK(m) ((m & S_IFMT) == S_IFBLK) +# else +# define S_ISBLK(m) (0) /* anytime false */ +# endif +#endif + +#ifdef S_ISBLK + struct stat st; + + Check_SafeStr(fname); + if (stat(fname->ptr, &st) < 0) return FALSE; + if (S_ISBLK(st.st_mode)) return TRUE; + +#endif + return FALSE; +} + +static VALUE +test_c(obj, fname) + VALUE obj; + struct RString *fname; +{ +#ifndef S_ISCHR +# define S_ISCHR(m) ((m & S_IFMT) == S_IFCHR) +#endif + + struct stat st; + + Check_SafeStr(fname); + if (stat(fname->ptr, &st) < 0) return FALSE; + if (S_ISBLK(st.st_mode)) return TRUE; + + return FALSE; +} + +static VALUE +test_e(obj, fname) + VALUE obj; + struct RString *fname; +{ + struct stat st; + + Check_SafeStr(fname); + if (stat(fname->ptr, &st) < 0) return FALSE; + return TRUE; +} + +static VALUE +test_r(obj, fname) + VALUE obj; + struct RString *fname; +{ + Check_SafeStr(fname); + if (eaccess(fname->ptr, R_OK) < 0) return FALSE; + return TRUE; +} + +static VALUE +test_R(obj, fname) + VALUE obj; + struct RString *fname; +{ + Check_SafeStr(fname); + if (access(fname->ptr, R_OK) < 0) return FALSE; + return TRUE; +} + +static VALUE +test_w(obj, fname) + VALUE obj; + struct RString *fname; +{ + Check_SafeStr(fname); + if (eaccess(fname->ptr, W_OK) < 0) return FALSE; + return TRUE; +} + +static VALUE +test_W(obj, fname) + VALUE obj; + struct RString *fname; +{ + Check_SafeStr(fname); + if (access(fname->ptr, W_OK) < 0) return FALSE; + return TRUE; +} + +static VALUE +test_x(obj, fname) + VALUE obj; + struct RString *fname; +{ + Check_SafeStr(fname); + if (eaccess(fname->ptr, X_OK) < 0) return FALSE; + return TRUE; +} + +static VALUE +test_X(obj, fname) + VALUE obj; + struct RString *fname; +{ + Check_SafeStr(fname); + if (access(fname->ptr, X_OK) < 0) return FALSE; + return TRUE; +} + +#ifndef S_ISREG +# define S_ISREG(m) ((m & S_IFMT) == S_IFREG) +#endif + +static VALUE +test_f(obj, fname) + VALUE obj; + struct RString *fname; +{ + struct stat st; + + Check_SafeStr(fname); + if (stat(fname->ptr, &st) < 0) return FALSE; + if (S_ISREG(st.st_mode)) return TRUE; + return FALSE; +} + +static VALUE +test_z(obj, fname) + VALUE obj; + struct RString *fname; +{ + struct stat st; + + Check_SafeStr(fname); + if (stat(fname->ptr, &st) < 0) return FALSE; + if (st.st_size == 0) return TRUE; + return FALSE; +} + +static VALUE +test_s(obj, fname) + VALUE obj; + struct RString *fname; +{ + struct stat st; + + Check_SafeStr(fname); + if (stat(fname->ptr, &st) < 0) return FALSE; + if (st.st_size == 0) return FALSE; + return int2inum(st.st_size); +} + +static VALUE +test_owned(obj, fname) + VALUE obj; + struct RString *fname; +{ + struct stat st; + + Check_SafeStr(fname); + if (stat(fname->ptr, &st) < 0) return FALSE; + if (st.st_uid == geteuid()) return TRUE; + return FALSE; +} + +static VALUE +test_rowned(obj, fname) + VALUE obj; + struct RString *fname; +{ + struct stat st; + + Check_SafeStr(fname); + if (stat(fname->ptr, &st) < 0) return FALSE; + if (st.st_uid == getuid()) return TRUE; + return FALSE; +} + +static VALUE +test_grpowned(obj, fname) + VALUE obj; + struct RString *fname; +{ +#ifndef NT + struct stat st; + + Check_SafeStr(fname); + if (stat(fname->ptr, &st) < 0) return FALSE; + if (st.st_gid == getegid()) return TRUE; +#endif + return FALSE; +} + +#if defined(S_ISUID) || defined(S_ISGID) || defined(S_ISVTX) +static VALUE +check3rdbyte(file, mode) + char *file; + int mode; +{ + struct stat st; + + if (stat(file, &st) < 0) return FALSE; + if (st.st_mode & mode) return TRUE; + return FALSE; +} +#endif + +static VALUE +test_suid(obj, fname) + VALUE obj; + struct RString *fname; +{ +#ifdef S_ISUID + Check_SafeStr(fname); + return check3rdbyte(fname->ptr, S_ISUID); +#else + return FALSE; +#endif +} + +static VALUE +test_sgid(obj, fname) + VALUE obj; + struct RString *fname; +{ +#ifndef NT + Check_SafeStr(fname); + return check3rdbyte(fname->ptr, S_ISGID); +#else + return FALSE; +#endif +} + +static VALUE +test_sticky(obj, fname) + VALUE obj; + struct RString *fname; +{ + Check_Type(fname, T_STRING); +#ifdef S_ISVTX + return check3rdbyte(fname->ptr, S_ISVTX); +#else + return FALSE; +#endif +} + +static VALUE +file_s_size(obj, fname) + VALUE obj; + struct RString *fname; +{ + struct stat st; + + Check_SafeStr(fname); + if (stat(fname->ptr, &st) < 0) rb_sys_fail(fname->ptr); + return int2inum(st.st_size); +} + +static VALUE +file_s_ftype(obj, fname) + VALUE obj; + struct RString *fname; +{ + struct stat st; + char *t; + + Check_SafeStr(fname); + if (stat(fname->ptr, &st) < 0) rb_sys_fail(fname->ptr); + + if (S_ISREG(st.st_mode)) { + t = "file"; + } else if (S_ISDIR(st.st_mode)) { + t = "directory"; + } else if (S_ISCHR(st.st_mode)) { + t = "characterSpecial"; + } +#ifdef S_ISBLK + else if (S_ISBLK(st.st_mode)) { + t = "blockSpecial"; + } +#endif +#ifdef S_ISFIFO + else if (S_ISFIFO(st.st_mode)) { + t = "fifo"; + } +#endif +#ifdef S_ISLNK + else if (S_ISLNK(st.st_mode)) { + t = "link"; + } +#endif +#ifdef S_ISSOCK + else if (S_ISSOCK(st.st_mode)) { + t = "socket"; + } +#endif + else { + t = "unknown"; + } + + return str_new2(t); +} + +static VALUE +file_s_atime(obj, fname) + VALUE obj; + struct RString *fname; +{ + struct stat st; + + Check_SafeStr(fname); + if (stat(fname->ptr, &st) < 0) rb_sys_fail(fname->ptr); + return time_new(st.st_atime, 0); +} + +static VALUE +file_atime(obj) + VALUE obj; +{ + OpenFile *fptr; + struct stat st; + + GetOpenFile(obj, fptr); + if (fstat(fileno(fptr->f), &st) == -1) { + rb_sys_fail(fptr->path); + } + return time_new(st.st_atime, 0); +} + +static VALUE +file_s_mtime(obj, fname) + VALUE obj; + struct RString *fname; +{ + struct stat st; + + Check_SafeStr(fname); + if (stat(fname->ptr, &st) < 0) rb_sys_fail(fname->ptr); + return time_new(st.st_mtime, 0); +} + +static VALUE +file_mtime(obj) + VALUE obj; +{ + OpenFile *fptr; + struct stat st; + + GetOpenFile(obj, fptr); + if (fstat(fileno(fptr->f), &st) == -1) { + rb_sys_fail(fptr->path); + } + return time_new(st.st_mtime, 0); +} + +static VALUE +file_s_ctime(obj, fname) + VALUE obj; + struct RString *fname; +{ + struct stat st; + + Check_SafeStr(fname); + if (stat(fname->ptr, &st) < 0) rb_sys_fail(fname->ptr); + return time_new(st.st_ctime, 0); +} + +static VALUE +file_ctime(obj) + VALUE obj; +{ + OpenFile *fptr; + struct stat st; + + GetOpenFile(obj, fptr); + if (fstat(fileno(fptr->f), &st) == -1) { + rb_sys_fail(fptr->path); + } + return time_new(st.st_ctime, 0); +} + +static void +chmod_internal(path, mode) + char *path; + int mode; +{ + if (chmod(path, mode) == -1) + rb_sys_fail(path); +} + +static VALUE +file_s_chmod(argc, argv) + int argc; + VALUE *argv; +{ + VALUE vmode; + VALUE rest; + int mode, n; + + rb_scan_args(argc, argv, "1*", &vmode, &rest); + mode = NUM2INT(vmode); + + n = apply2files(chmod_internal, rest, mode); + return INT2FIX(n); +} + +static VALUE +file_chmod(obj, vmode) + VALUE obj, vmode; +{ + OpenFile *fptr; + int mode; + + rb_secure(2); + mode = NUM2INT(vmode); + + GetOpenFile(obj, fptr); +#if defined(DJGPP) || defined(__CYGWIN32__) || defined(NT) + if (chmod(fptr->path, mode) == -1) + rb_sys_fail(fptr->path); +#else + if (fchmod(fileno(fptr->f), mode) == -1) + rb_sys_fail(fptr->path); +#endif + + return INT2FIX(0); +} + +struct chown_args { + int owner, group; +}; + +static void +chown_internal(path, args) + char *path; + struct chown_args *args; +{ + if (chown(path, args->owner, args->group) < 0) + rb_sys_fail(path); +} + +static VALUE +file_s_chown(argc, argv) + int argc; + VALUE *argv; +{ + VALUE o, g, rest; + struct chown_args arg; + int n; + + rb_scan_args(argc, argv, "2*", &o, &g, &rest); + if (NIL_P(o)) { + arg.owner = -1; + } + else { + arg.owner = NUM2INT(o); + } + if (NIL_P(g)) { + arg.group = -1; + } + else { + arg.group = NUM2INT(g); + } + + n = apply2files(chown_internal, rest, &arg); + return INT2FIX(n); +} + +static VALUE +file_chown(obj, owner, group) + VALUE obj, owner, group; +{ + OpenFile *fptr; + + rb_secure(2); + GetOpenFile(obj, fptr); +#if defined(DJGPP) || defined(__CYGWIN32__) || defined(NT) + if (chown(fptr->path, NUM2INT(owner), NUM2INT(group)) == -1) + rb_sys_fail(fptr->path); +#else + if (fchown(fileno(fptr->f), NUM2INT(owner), NUM2INT(group)) == -1) + rb_sys_fail(fptr->path); +#endif + + return INT2FIX(0); +} + +struct timeval time_timeval(); + +#ifdef HAVE_UTIMES + +static void +utime_internal(path, tvp) + char *path; + struct timeval tvp[]; +{ + if (utimes(path, tvp) < 0) + rb_sys_fail(path); +} + +static VALUE +file_s_utime(argc, argv) + int argc; + VALUE *argv; +{ + VALUE atime, mtime, rest; + struct timeval tvp[2]; + int n; + + rb_scan_args(argc, argv, "2*", &atime, &mtime, &rest); + + tvp[0] = time_timeval(atime); + tvp[1] = time_timeval(mtime); + + n = apply2files(utime_internal, rest, tvp); + return INT2FIX(n); +} + +#else + +#ifndef HAVE_UTIME_H +# ifdef NT +# include <sys/utime.h> +# define utimbuf _utimbuf +# else +struct utimbuf { + long actime; + long modtime; +}; +# endif +#endif + +static void +utime_internal(path, utp) + char *path; + struct utimbuf *utp; +{ + if (utime(path, utp) < 0) + rb_sys_fail(path); +} + +static VALUE +file_s_utime(argc, argv) + int argc; + VALUE *argv; +{ + VALUE atime, mtime, rest; + int n; + struct timeval tv; + struct utimbuf utbuf; + + rb_scan_args(argc, argv, "2*", &atime, &mtime, &rest); + + tv = time_timeval(atime); + utbuf.actime = tv.tv_sec; + tv = time_timeval(mtime); + utbuf.modtime = tv.tv_sec; + + n = apply2files(utime_internal, rest, &utbuf); + return INT2FIX(n); +} + +#endif + +static VALUE +file_s_link(obj, from, to) + VALUE obj; + struct RString *from, *to; +{ +#ifndef __human68k__ + Check_SafeStr(from); + Check_SafeStr(to); + + if (link(from->ptr, to->ptr) < 0) + rb_sys_fail(from->ptr); + return INT2FIX(0); +#else + rb_notimplement(); +#endif +} + +static VALUE +file_s_symlink(obj, from, to) + VALUE obj; + struct RString *from, *to; +{ +#if !defined(MSDOS) && !defined(NT) + Check_SafeStr(from); + Check_SafeStr(to); + + if (symlink(from->ptr, to->ptr) < 0) + rb_sys_fail(from->ptr); + return TRUE; +#else + rb_notimplement(); +#endif +} + +static VALUE +file_s_readlink(obj, path) + VALUE obj; + struct RString *path; +{ +#if !defined(MSDOS) && !defined(NT) + char buf[MAXPATHLEN]; + int cc; + + Check_SafeStr(path); + + if ((cc = readlink(path->ptr, buf, MAXPATHLEN)) < 0) + rb_sys_fail(path->ptr); + + return str_new(buf, cc); +#else + rb_notimplement(); +#endif +} + +static void +unlink_internal(path) + char *path; +{ + if (unlink(path) < 0) + rb_sys_fail(path); +} + +static VALUE +file_s_unlink(obj, args) + VALUE obj; + struct RArray *args; +{ + int n; + + n = apply2files(unlink_internal, args, 0); + return INT2FIX(n); +} + +static VALUE +file_s_rename(obj, from, to) + VALUE obj; + struct RString *from, *to; +{ + Check_SafeStr(from); + Check_SafeStr(to); + + if (rename(from->ptr, to->ptr) == -1) + rb_sys_fail(from->ptr); + + return INT2FIX(0); +} + +static VALUE +file_s_umask(argc, argv) + int argc; + VALUE *argv; +{ + int omask = 0; + + if (argc == 0) { + int omask = umask(0); + umask(omask); + } + else if (argc == 1) { + omask = umask(NUM2INT(argv[0])); + } + else { + ArgError("wrong # of argument"); + } + return INT2FIX(omask); +} + +VALUE +file_s_expand_path(obj, fname) + VALUE obj; + struct RString *fname; +{ + char *s, *p; + char buf[MAXPATHLEN]; + + Check_Type(fname, T_STRING); + s = fname->ptr; + + p = buf; + if (s[0] == '~') { + if (s[1] == '/' || s[1] == '\0') { + char *dir = getenv("HOME"); + + if (!dir) { + Fail("couldn't find HOME environment -- expanding `%s'", s); + } + strcpy(buf, dir); + p = &buf[strlen(buf)]; + s++; + } + else { +#ifdef HAVE_PWD_H + struct passwd *pwPtr; + s++; +#endif + + while (*s && *s != '/') { + *p++ = *s++; + } + *p = '\0'; +#ifdef HAVE_PWD_H + pwPtr = getpwnam(buf); + if (!pwPtr) { + endpwent(); + Fail("user %s doesn't exist", buf); + } + strcpy(buf, pwPtr->pw_dir); + p = &buf[strlen(buf)]; + endpwent(); +#endif + } + } + else if (s[0] != '/') { +#ifdef HAVE_GETCWD + getcwd(buf, MAXPATHLEN); +#else + getwd(buf); +#endif + p = &buf[strlen(buf)]; + } + *p = '/'; + + for ( ; *s; s++) { + switch (*s) { + case '.': + if (*(s+1)) { + switch (*++s) { + case '.': + if (*(s+1) == '\0' || *(s+1) == '/') { + /* We must go back to the parent */ + if (*p == '/' && p > buf) p--; + while (p > buf && *p != '/') p--; + } + else { + *++p = '.'; + *++p = '.'; + } + break; + case '/': + if (*p != '/') *++p = '/'; + break; + default: + *++p = '.'; *++p = *s; break; + } + } + break; + case '/': + if (*p != '/') *++p = '/'; break; + default: + *++p = *s; + } + } + + /* Place a \0 at end. If path ends with a "/", delete it */ + if (p == buf || *p != '/') p++; + *p = '\0'; + + return str_taint(str_new2(buf)); +} + +static int +rmext(p, e) + char *p, *e; +{ + int l1, l2; + + l1 = strlen(p); + if (!e) return 0; + + l2 = strlen(e); + if (l1 < l2) return l1; + + if (strcmp(p+l1-l2, e) == 0) { + return l1-l2; + } + return 0; +} + +static VALUE +file_s_basename(argc, argv) + int argc; + VALUE *argv; +{ + struct RString *fname; + struct RString *ext; + char *p; + int f; + + rb_scan_args(argc, argv, "11", &fname, &ext); + Check_Type(fname, T_STRING); + if (!NIL_P(ext)) Check_Type(ext, T_STRING); + p = strrchr(fname->ptr, '/'); + if (!p) { + if (!NIL_P(ext)) { + f = rmext(fname->ptr, ext->ptr); + if (f) return str_new(fname->ptr, f); + } + return (VALUE)fname; + } + p++; /* skip last `/' */ + if (!NIL_P(ext)) { + f = rmext(p, ext->ptr); + if (f) return str_new(p, f); + } + return str_taint(str_new2(p)); +} + +static VALUE +file_s_dirname(obj, fname) + VALUE obj; + struct RString *fname; +{ + UCHAR *p; + + Check_Type(fname, T_STRING); + p = strrchr(fname->ptr, '/'); + if (!p) { + return str_new2("."); + } + if (p == fname->ptr) + p++; + return str_taint(str_new(fname->ptr, p - fname->ptr)); +} + +static VALUE +file_s_split(obj, path) + VALUE obj, path; +{ + return assoc_new(file_s_dirname(Qnil, path), file_s_basename(1,&path)); +} + +static VALUE separator; + +static VALUE +file_s_join(obj, args) + VALUE obj, args; +{ + return ary_join(args, separator); +} + +static VALUE +file_s_truncate(obj, path, len) + VALUE obj, len; + struct RString *path; +{ + Check_SafeStr(path); + +#ifdef HAVE_TRUNCATE + if (truncate(path->ptr, NUM2INT(len)) < 0) + rb_sys_fail(path->ptr); +#else +# ifdef HAVE_CHSIZE + { + int tmpfd; + +# if defined(NT) + if ((tmpfd = open(path->ptr, O_RDWR)) < 0) { + rb_sys_fail(path->ptr); + } +# else + if ((tmpfd = open(path->ptr, 0)) < 0) { + rb_sys_fail(path->ptr); + } +# endif + if (chsize(tmpfd, NUM2INT(len)) < 0) { + close(tmpfd); + rb_sys_fail(path->ptr); + } + close(tmpfd); + } +# else + rb_notimplement(); +# endif +#endif + return TRUE; +} + +static VALUE +file_truncate(obj, len) + VALUE obj, len; +{ + OpenFile *fptr; + + GetOpenFile(obj, fptr); + + rb_secure(2); + if (!(fptr->mode & FMODE_WRITABLE)) { + Fail("not opened for writing"); + } +#ifdef HAVE_TRUNCATE + if (ftruncate(fileno(fptr->f), NUM2INT(len)) < 0) + rb_sys_fail(fptr->path); +#else +# ifdef HAVE_CHSIZE + if (chsize(fileno(fptr->f), NUM2INT(len)) < 0) + rb_sys_fail(fptr->path); +# else + rb_notimplement(); +# endif +#endif + return TRUE; +} + +static VALUE +file_flock(obj, operation) + VALUE obj; + VALUE operation; +{ + OpenFile *fptr; + + GetOpenFile(obj, fptr); + + rb_secure(2); + if (flock(fileno(fptr->f), NUM2INT(operation)) < 0) { +#ifdef EWOULDBLOCK + if (errno == EWOULDBLOCK) { + return FALSE; + } +#endif + rb_sys_fail(fptr->path); + } + return obj; +} + +static void +test_check(n, argc, argv) + int n, argc; + VALUE *argv; +{ + int i; + + n+=1; + if (n < argc) ArgError("Wrong # of arguments(%d for %d)", argc, n); + for (i=1; i<n; i++) { + Check_SafeStr(argv[i]); + } +} + +#define CHECK(n) test_check((n), argc, argv) + +static VALUE +f_test(argc, argv) + int argc; + VALUE *argv; +{ + int cmd; + + if (argc == 0) ArgError("Wrong # of arguments"); + if (TYPE(argv[0]) == T_STRING && RSTRING(argv[0])->len == 1) { + cmd = RSTRING(argv[0])->ptr[0]; + } + else { + cmd = NUM2INT(argv[0]); + } + if (cmd == 0) return FALSE; + if (strchr("bcdefgGkloOprRsSuwWxXz", cmd)) { + CHECK(1); + switch (cmd) { + case 'b': + return test_b(0, argv[1]); + + case 'c': + return test_c(0, argv[1]); + + case 'd': + return test_d(0, argv[1]); + + case 'a': + case 'e': + return test_e(0, argv[1]); + + case 'f': + return test_f(0, argv[1]); + + case 'g': + return test_sgid(0, argv[1]); + + case 'G': + return test_grpowned(0, argv[1]); + + case 'k': + return test_sticky(0, argv[1]); + + case 'l': + return test_l(0, argv[1]); + + case 'o': + return test_owned(0, argv[1]); + + case 'O': + return test_rowned(0, argv[1]); + + case 'p': + return test_p(0, argv[1]); + + case 'r': + return test_r(0, argv[1]); + + case 'R': + return test_R(0, argv[1]); + + case 's': + return test_s(0, argv[1]); + + case 'S': + return test_S(0, argv[1]); + + case 'u': + return test_suid(0, argv[1]); + + case 'w': + return test_w(0, argv[1]); + + case 'W': + return test_W(0, argv[1]); + + case 'x': + return test_x(0, argv[1]); + + case 'X': + return test_X(0, argv[1]); + + case 'z': + return test_z(0, argv[1]); + } + } + + if (strchr("MAC", cmd)) { + struct stat st; + + CHECK(1); + if (stat(RSTRING(argv[1])->ptr, &st) == -1) { + rb_sys_fail(RSTRING(argv[1])->ptr); + } + + switch (cmd) { + case 'A': + return time_new(st.st_atime, 0); + case 'M': + return time_new(st.st_mtime, 0); + case 'C': + return time_new(st.st_ctime, 0); + } + } + + if (strchr("-=<>", cmd)) { + struct stat st1, st2; + + CHECK(2); + if (stat(RSTRING(argv[1])->ptr, &st1) < 0) return FALSE; + if (stat(RSTRING(argv[2])->ptr, &st2) < 0) return FALSE; + + switch (cmd) { + case '-': + if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino) + return TRUE; + break; + + case '=': + if (st1.st_mtime == st2.st_mtime) return TRUE; + break; + + case '>': + if (st1.st_mtime > st2.st_mtime) return TRUE; + break; + + case '<': + if (st1.st_mtime < st2.st_mtime) return TRUE; + break; + } + } + return FALSE; +} + +extern VALUE mKernel; + +void +Init_File() +{ + mFileTest = rb_define_module("FileTest"); + + rb_define_module_function(mFileTest, "directory?", test_d, 1); + rb_define_module_function(mFileTest, "exist?", test_e, 1); + rb_define_module_function(mFileTest, "exists?", test_e, 1); /* temporary */ + rb_define_module_function(mFileTest, "readable?", test_r, 1); + rb_define_module_function(mFileTest, "readable_real?", test_R, 1); + rb_define_module_function(mFileTest, "writable?", test_w, 1); + rb_define_module_function(mFileTest, "writable_real?", test_W, 1); + rb_define_module_function(mFileTest, "executable?", test_x, 1); + rb_define_module_function(mFileTest, "executable_real?", test_X, 1); + rb_define_module_function(mFileTest, "file?", test_f, 1); + rb_define_module_function(mFileTest, "zero?", test_z, 1); + rb_define_module_function(mFileTest, "size?", test_s, 1); + rb_define_module_function(mFileTest, "owned?", test_owned, 1); + rb_define_module_function(mFileTest, "grpowned?", test_grpowned, 1); + + rb_define_module_function(mFileTest, "pipe?", test_p, 1); + rb_define_module_function(mFileTest, "symlink?", test_l, 1); + rb_define_module_function(mFileTest, "socket?", test_S, 1); + + rb_define_module_function(mFileTest, "blockdev?", test_b, 1); + rb_define_module_function(mFileTest, "chardev?", test_c, 1); + + rb_define_module_function(mFileTest, "setuid?", test_suid, 1); + rb_define_module_function(mFileTest, "setgid?", test_sgid, 1); + rb_define_module_function(mFileTest, "sticky?", test_sticky, 1); + + cFile = rb_define_class("File", cIO); + rb_extend_object(cFile, CLASS_OF(mFileTest)); + + rb_define_singleton_method(cFile, "new", file_s_open, -1); + rb_define_singleton_method(cFile, "open", file_s_open, -1); + + rb_define_singleton_method(cFile, "stat", file_s_stat, 1); + rb_define_singleton_method(cFile, "lstat", file_s_lstat, 1); + rb_define_singleton_method(cFile, "ftype", file_s_ftype, 1); + + rb_define_singleton_method(cFile, "atime", file_s_atime, 1); + rb_define_singleton_method(cFile, "mtime", file_s_mtime, 1); + rb_define_singleton_method(cFile, "ctime", file_s_ctime, 1); + rb_define_singleton_method(cFile, "size", file_s_size, 1); + + rb_define_singleton_method(cFile, "utime", file_s_utime, -1); + rb_define_singleton_method(cFile, "chmod", file_s_chmod, -1); + rb_define_singleton_method(cFile, "chown", file_s_chown, -1); + + rb_define_singleton_method(cFile, "link", file_s_link, 2); + rb_define_singleton_method(cFile, "symlink", file_s_symlink, 2); + rb_define_singleton_method(cFile, "readlink", file_s_readlink, 1); + + rb_define_singleton_method(cFile, "unlink", file_s_unlink, -2); + rb_define_singleton_method(cFile, "delete", file_s_unlink, -2); + rb_define_singleton_method(cFile, "rename", file_s_rename, 2); + rb_define_singleton_method(cFile, "umask", file_s_umask, -1); + rb_define_singleton_method(cFile, "truncate", file_s_truncate, 2); + rb_define_singleton_method(cFile, "expand_path", file_s_expand_path, 1); + rb_define_singleton_method(cFile, "basename", file_s_basename, -1); + rb_define_singleton_method(cFile, "dirname", file_s_dirname, 1); + + separator = str_new2("/"); + rb_define_const(cFile, "Separator", separator); + rb_define_singleton_method(cFile, "split", file_s_split, 1); + rb_define_singleton_method(cFile, "join", file_s_join, -2); + + rb_define_method(cFile, "reopen", file_reopen, -1); + + rb_define_method(cFile, "stat", file_stat, 0); + rb_define_method(cFile, "lstat", file_lstat, 0); + + rb_define_method(cFile, "atime", file_atime, 0); + rb_define_method(cFile, "mtime", file_mtime, 0); + rb_define_method(cFile, "ctime", file_ctime, 0); + + rb_define_method(cFile, "chmod", file_chmod, 1); + rb_define_method(cFile, "chown", file_chown, 2); + rb_define_method(cFile, "truncate", file_truncate, 1); + + rb_define_method(cFile, "tell", file_tell, 0); + rb_define_method(cFile, "seek", file_seek, 2); + + rb_define_method(cFile, "pos", file_tell, 0); + rb_define_method(cFile, "pos=", file_set_pos, 1); + + rb_define_method(cFile, "rewind", file_rewind, 0); + rb_define_method(cFile, "isatty", file_isatty, 0); + rb_define_method(cFile, "tty?", file_isatty, 0); + rb_define_method(cFile, "eof", file_eof, 0); + rb_define_method(cFile, "eof?", file_eof, 0); + + rb_define_method(cFile, "flock", file_flock, 1); + +# ifndef LOCK_SH +# define LOCK_SH 1 +# endif +# ifndef LOCK_EX +# define LOCK_EX 2 +# endif +# ifndef LOCK_NB +# define LOCK_NB 4 +# endif +# ifndef LOCK_UN +# define LOCK_UN 8 +# endif + + rb_define_const(cFile, "LOCK_SH", INT2FIX(LOCK_SH)); + rb_define_const(cFile, "LOCK_EX", INT2FIX(LOCK_EX)); + rb_define_const(cFile, "LOCK_UN", INT2FIX(LOCK_UN)); + rb_define_const(cFile, "LOCK_NB", INT2FIX(LOCK_NB)); + + rb_define_method(cFile, "path", file_path, 0); + + rb_define_global_function("test", f_test, -1); + + sStat = struct_define("Stat", "dev", "ino", "mode", + "nlink", "uid", "gid", "rdev", + "size", "blksize", "blocks", + "atime", "mtime", "ctime", 0); +} diff --git a/fnmatch.c b/fnmatch.c new file mode 100644 index 0000000000..0847f5cafb --- /dev/null +++ b/fnmatch.c @@ -0,0 +1,190 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include "config.h" +#include <errno.h> +#include "fnmatch.h" + +#if !defined (__GNU_LIBRARY__) && !defined (STDC_HEADERS) +# if !defined (errno) +extern int errno; +# endif /* !errno */ +#endif + +/* Match STRING against the filename pattern PATTERN, returning zero if + it matches, FNM_NOMATCH if not. */ +int +fnmatch (pattern, string, flags) + char *pattern; + char *string; + int flags; +{ + register char *p = pattern, *n = string; + register char c; + + if ((flags & ~__FNM_FLAGS) != 0) + { + errno = EINVAL; + return (-1); + } + + while ((c = *p++) != '\0') + { + switch (c) + { + case '?': + if (*n == '\0') + return (FNM_NOMATCH); + else if ((flags & FNM_PATHNAME) && *n == '/') + return (FNM_NOMATCH); + else if ((flags & FNM_PERIOD) && *n == '.' && + (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/'))) + return (FNM_NOMATCH); + break; + + case '\\': + if (!(flags & FNM_NOESCAPE)) + c = *p++; + if (*n != c) + return (FNM_NOMATCH); + break; + + case '*': + if ((flags & FNM_PERIOD) && *n == '.' && + (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/'))) + return (FNM_NOMATCH); + + for (c = *p++; c == '?' || c == '*'; c = *p++, ++n) + if (((flags & FNM_PATHNAME) && *n == '/') || + (c == '?' && *n == '\0')) + return (FNM_NOMATCH); + + if (c == '\0') + return (0); + + { + char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c; + for (--p; *n != '\0'; ++n) + if ((c == '[' || *n == c1) && + fnmatch (p, n, flags & ~FNM_PERIOD) == 0) + return (0); + return (FNM_NOMATCH); + } + + case '[': + { + /* Nonzero if the sense of the character class is inverted. */ + register int not; + + if (*n == '\0') + return (FNM_NOMATCH); + + if ((flags & FNM_PERIOD) && *n == '.' && + (n == string || ((flags & FNM_PATHNAME) && n[-1] == '/'))) + return (FNM_NOMATCH); + + /* Make sure there is a closing `]'. If there isn't, the `[' + is just a character to be matched. */ + { + register char *np; + + for (np = p; np && *np && *np != ']'; np++); + + if (np && !*np) + { + if (*n != '[') + return (FNM_NOMATCH); + goto next_char; + } + } + + not = (*p == '!' || *p == '^'); + if (not) + ++p; + + c = *p++; + for (;;) + { + register char cstart = c, cend = c; + + if (!(flags & FNM_NOESCAPE) && c == '\\') + cstart = cend = *p++; + + if (c == '\0') + /* [ (unterminated) loses. */ + return (FNM_NOMATCH); + + c = *p++; + + if ((flags & FNM_PATHNAME) && c == '/') + /* [/] can never match. */ + return (FNM_NOMATCH); + + if (c == '-' && *p != ']') + { + cend = *p++; + if (!(flags & FNM_NOESCAPE) && cend == '\\') + cend = *p++; + if (cend == '\0') + return (FNM_NOMATCH); + c = *p++; + } + + if (*n >= cstart && *n <= cend) + goto matched; + + if (c == ']') + break; + } + if (!not) + return (FNM_NOMATCH); + + next_char: + break; + + matched: + /* Skip the rest of the [...] that already matched. */ + while (c != ']') + { + if (c == '\0') + /* [... (unterminated) loses. */ + return (FNM_NOMATCH); + + c = *p++; + if (!(flags & FNM_NOESCAPE) && c == '\\') + /* 1003.2d11 is unclear if this is right. %%% */ + ++p; + } + if (not) + return (FNM_NOMATCH); + } + break; + + default: + if (c != *n) + return (FNM_NOMATCH); + } + + ++n; + } + + if (*n == '\0') + return (0); + + return (FNM_NOMATCH); +} diff --git a/fnmatch.h b/fnmatch.h new file mode 100644 index 0000000000..62c8c8fa02 --- /dev/null +++ b/fnmatch.h @@ -0,0 +1,36 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#ifndef _FNMATCH_H + +#define _FNMATCH_H 1 + +/* Bits set in the FLAGS argument to `fnmatch'. */ +#define FNM_PATHNAME (1 << 0)/* No wildcard can ever match `/'. */ +#define FNM_NOESCAPE (1 << 1)/* Backslashes don't quote special chars. */ +#define FNM_PERIOD (1 << 2)/* Leading `.' is matched only explicitly. */ +#define __FNM_FLAGS (FNM_PATHNAME|FNM_NOESCAPE|FNM_PERIOD) + +/* Value returned by `fnmatch' if STRING does not match PATTERN. */ +#define FNM_NOMATCH 1 + +/* Match STRING against the filename pattern PATTERN, + returning zero if it matches, FNM_NOMATCH if not. */ +extern int fnmatch(); + +#endif /* fnmatch.h */ @@ -0,0 +1,989 @@ +/************************************************ + + gc.c - + + $Author$ + $Date$ + created at: Tue Oct 5 09:44:46 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include "sig.h" +#include "st.h" +#include "node.h" +#include "env.h" +#include "re.h" +#include <stdio.h> +#include <setjmp.h> + +#ifndef setjmp +#ifdef HAVE__SETJMP +#define setjmp(env) _setjmp(env) +#define longjmp(env,val) _longjmp(env,val) +#endif +#endif + +#ifdef _AIX +#pragma alloca +#endif + +#ifdef C_ALLOCA +void *alloca(); +#endif + +void gc(); +void gc_mark(); +static void run_final(); + +#ifndef GC_MALLOC_LIMIT +#if defined(MSDOS) || defined(__human68k__) +#define GC_MALLOC_LIMIT 200000 +#else +#define GC_MALLOC_LIMIT 400000 +#endif +#endif + +static unsigned long malloc_memories = 0; + +void * +xmalloc(size) + unsigned long size; +{ + void *mem; + + if (size == 0) size = 1; + malloc_memories += size; + if (malloc_memories > GC_MALLOC_LIMIT) { + gc(); + } + mem = malloc(size); + if (!mem) { + gc(); + mem = malloc(size); + if (!mem) + Fatal("failed to allocate memory"); + } + + return mem; +} + +void * +xcalloc(n, size) + unsigned long n, size; +{ + void *mem; + + mem = xmalloc(n * size); + memset(mem, 0, n * size); + + return mem; +} + +void * +xrealloc(ptr, size) + void *ptr; + unsigned long size; +{ + void *mem; + + if (!ptr) return xmalloc(size); + mem = realloc(ptr, size); + if (!mem) { + gc(); + mem = realloc(ptr, size); + if (!mem) + Fatal("failed to allocate memory(realloc)"); + } + + return mem; +} + +/* The way of garbage collecting which allows use of the cstack is due to */ +/* Scheme In One Defun, but in C this time. + + * COPYRIGHT (c) 1989 BY * + * PARADIGM ASSOCIATES INCORPORATED, CAMBRIDGE, MASSACHUSETTS. * + * ALL RIGHTS RESERVED * + +Permission to use, copy, modify, distribute and sell this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all copies +and that both that copyright notice and this permission notice appear +in supporting documentation, and that the name of Paradigm Associates +Inc not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + +PARADIGM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +PARADIGM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +gjc@paradigm.com + +Paradigm Associates Inc Phone: 617-492-6079 +29 Putnam Ave, Suite 6 +Cambridge, MA 02138 +*/ + +extern int rb_in_compile; +static int dont_gc; + +VALUE +gc_s_enable() +{ + int old = dont_gc; + + dont_gc = FALSE; + return old; +} + +VALUE +gc_s_disable() +{ + int old = dont_gc; + + dont_gc = TRUE; + return old; +} + +VALUE mGC; + +static struct gc_list { + VALUE *varptr; + struct gc_list *next; +} *Global_List = 0; + +void +rb_global_variable(var) + VALUE *var; +{ + struct gc_list *tmp; + + tmp = ALLOC(struct gc_list); + tmp->next = Global_List; + tmp->varptr = var; + Global_List = tmp; +} + +typedef struct RVALUE { + union { + struct { + UINT flag; /* always 0 for freed obj */ + struct RVALUE *next; + } free; + struct RBasic basic; + struct RObject object; + struct RClass class; + 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; + struct RNode node; + struct RMatch match; + struct RVarmap varmap; + struct SCOPE scope; + } as; +} RVALUE; + +RVALUE *freelist = 0; + +#define HEAPS_INCREMENT 10 +static RVALUE **heaps; +static int heaps_length = 0; +static int heaps_used = 0; + +#define HEAP_SLOTS 10000 +#define FREE_MIN 512 + +static RVALUE *himem, *lomem; + +static void +add_heap() +{ + RVALUE *p, *pend; + + if (heaps_used == heaps_length) { + /* Realloc heaps */ + heaps_length += HEAPS_INCREMENT; + heaps = (heaps_used>0)? + (RVALUE**)realloc(heaps, heaps_length*sizeof(RVALUE)): + (RVALUE**)malloc(heaps_length*sizeof(RVALUE)); + if (heaps == 0) Fatal("can't alloc memory"); + } + + p = heaps[heaps_used++] = (RVALUE*)malloc(sizeof(RVALUE)*HEAP_SLOTS); + if (p == 0) Fatal("can't alloc memory"); + pend = p + HEAP_SLOTS; + if (lomem == 0 || lomem > p) lomem = p; + if (himem < pend) himem = pend; + + while (p < pend) { + p->as.free.flag = 0; + p->as.free.next = freelist; + freelist = p; + p++; + } +} + +struct RBasic * +rb_newobj() +{ + struct RBasic *obj; + if (freelist) { + retry: + obj = (struct RBasic*)freelist; + freelist = freelist->as.free.next; + return obj; + } + if (dont_gc) add_heap(); + else gc(); + + goto retry; +} + +VALUE +data_object_alloc(class, datap, dmark, dfree) + VALUE class; + void *datap; + void (*dfree)(); + void (*dmark)(); +{ + struct RData *data = (struct RData*)rb_newobj(); + + OBJSETUP(data, class, T_DATA); + data->data = datap; + data->dfree = dfree; + data->dmark = dmark; + + return (VALUE)data; +} + +extern st_table *rb_class_tbl; +VALUE *gc_stack_start; + +static int +looks_pointerp(p) + register RVALUE *p; +{ + register RVALUE *heap_org; + register long i; + + if (p < lomem || p > himem) return FALSE; + + /* check if p looks like a pointer */ + for (i=0; i < heaps_used; i++) { + heap_org = heaps[i]; + if (heap_org <= p && p < heap_org + HEAP_SLOTS + && ((((char*)p)-((char*)heap_org))%sizeof(RVALUE)) == 0) + return TRUE; + } + return FALSE; +} + +static void +mark_locations_array(x, n) + VALUE *x; + long n; +{ + while (n--) { + if (looks_pointerp(*x)) { + gc_mark(*x); + } + x++; + } +} + +void +gc_mark_locations(start, end) + VALUE *start, *end; +{ + VALUE *tmp; + long n; + + if (start > end) { + tmp = start; + start = end; + end = tmp; + } + n = end - start; + mark_locations_array(start,n); +} + +static int +mark_entry(key, value) + ID key; + VALUE value; +{ + gc_mark(value); + return ST_CONTINUE; +} + +static void +mark_tbl(tbl) + st_table *tbl; +{ + if (!tbl) return; + st_foreach(tbl, mark_entry, 0); +} + +static int +mark_hashentry(key, value) + ID key; + VALUE value; +{ + gc_mark(key); + gc_mark(value); + return ST_CONTINUE; +} + +static void +mark_hash(tbl) + st_table *tbl; +{ + if (!tbl) return; + st_foreach(tbl, mark_hashentry, 0); +} + +void +gc_mark_maybe(obj) + void *obj; +{ + if (looks_pointerp(obj)) { + gc_mark(obj); + } +} + +void +gc_mark(obj) + register RVALUE *obj; +{ + Top: + if (FIXNUM_P(obj)) return; /* fixnum not marked */ + if (rb_special_const_p((VALUE)obj)) return; /* special const not marked */ + if (obj->as.basic.flags == 0) return; /* free cell */ + if (obj->as.basic.flags & FL_MARK) return; /* marked */ + + obj->as.basic.flags |= FL_MARK; + + switch (obj->as.basic.flags & T_MASK) { + case T_NIL: + case T_FIXNUM: + Bug("gc_mark() called for broken object"); + break; + + case T_NODE: + switch (nd_type(obj)) { + case NODE_IF: /* 1,2,3 */ + case NODE_FOR: + case NODE_ITER: + gc_mark(obj->as.node.u2.node); + /* fall through */ + case NODE_BLOCK: /* 1,3 */ + case NODE_ARRAY: + case NODE_DSTR: + case NODE_DXSTR: + case NODE_EVSTR: + case NODE_DREGX: + case NODE_DREGX_ONCE: + case NODE_FBODY: + case NODE_CALL: + gc_mark(obj->as.node.u1.node); + /* fall through */ + case NODE_SUPER: /* 3 */ + case NODE_FCALL: + case NODE_NEWLINE: + obj = (RVALUE*)obj->as.node.u3.node; + goto Top; + + case NODE_WHILE: /* 1,2 */ + case NODE_UNTIL: + gc_mark(obj->as.node.u1.node); + /* fall through */ + case NODE_METHOD: /* 2 */ + case NODE_NOT: + obj = (RVALUE*)obj->as.node.u2.node; + goto Top; + + case NODE_HASH: /* 1 */ + case NODE_LIT: + case NODE_STR: + case NODE_XSTR: + case NODE_DEFINED: + obj = (RVALUE*)obj->as.node.u1.node; + goto Top; + + case NODE_SCOPE: /* 2,3 */ + gc_mark(obj->as.node.u3.node); + obj = (RVALUE*)obj->as.node.u2.node; + goto Top; + + case NODE_ZARRAY: /* - */ + case NODE_CFUNC: + case NODE_VCALL: + case NODE_GVAR: + case NODE_LVAR: + case NODE_DVAR: + case NODE_IVAR: + case NODE_CVAR: + case NODE_NTH_REF: + case NODE_BACK_REF: + case NODE_ALIAS: + case NODE_VALIAS: + case NODE_UNDEF: + case NODE_SELF: + case NODE_NIL: + break; + + default: + if (looks_pointerp(obj->as.node.u1.node)) { + gc_mark(obj->as.node.u1.node); + } + if (looks_pointerp(obj->as.node.u2.node)) { + gc_mark(obj->as.node.u2.node); + } + if (looks_pointerp(obj->as.node.u3.node)) { + obj = (RVALUE*)obj->as.node.u3.node; + goto Top; + } + } + return; /* no need to mark class. */ + } + + gc_mark(obj->as.basic.class); + switch (obj->as.basic.flags & T_MASK) { + case T_ICLASS: + gc_mark(obj->as.class.super); + mark_tbl(obj->as.class.iv_tbl); + mark_tbl(obj->as.class.m_tbl); + break; + + case T_CLASS: + case T_MODULE: + gc_mark(obj->as.class.super); + mark_tbl(obj->as.class.m_tbl); + mark_tbl(obj->as.class.iv_tbl); + break; + + case T_ARRAY: + { + int i, len = obj->as.array.len; + VALUE *ptr = obj->as.array.ptr; + + for (i=0; i < len; i++) + gc_mark(*ptr++); + } + break; + + case T_HASH: + mark_hash(obj->as.hash.tbl); + break; + + case T_STRING: + if (obj->as.string.orig) { + obj = (RVALUE*)obj->as.string.orig; + goto Top; + } + break; + + case T_DATA: + if (obj->as.data.dmark) (*obj->as.data.dmark)(DATA_PTR(obj)); + break; + + case T_OBJECT: + mark_tbl(obj->as.object.iv_tbl); + break; + + case T_FILE: + case T_REGEXP: + case T_FLOAT: + case T_BIGNUM: + break; + + case T_MATCH: + if (obj->as.match.str) { + obj = (RVALUE*)obj->as.match.str; + goto Top; + } + break; + + case T_VARMAP: + gc_mark(obj->as.varmap.val); + obj = (RVALUE*)obj->as.varmap.next; + goto Top; + break; + + case T_SCOPE: + if (obj->as.scope.local_vars) { + int n = obj->as.scope.local_tbl[0]+1; + VALUE *vars = &obj->as.scope.local_vars[-1]; + + while (n--) { + gc_mark_maybe(*vars); + vars++; + } + } + break; + + case T_STRUCT: + { + int i, len = obj->as.rstruct.len; + VALUE *ptr = obj->as.rstruct.ptr; + + for (i=0; i < len; i++) + gc_mark(*ptr++); + } + break; + + default: + Bug("gc_mark(): unknown data type 0x%x(0x%x) %s", + obj->as.basic.flags & T_MASK, obj, + looks_pointerp(obj)?"corrupted object":"non object"); + } +} + +#define MIN_FREE_OBJ 512 + +static void obj_free(); + +static void +gc_sweep() +{ + RVALUE *p, *pend; + int freed = 0; + int i; + + if (rb_in_compile) { + for (i = 0; i < heaps_used; i++) { + p = heaps[i]; pend = p + HEAP_SLOTS; + while (p < pend) { + if (!(p->as.basic.flags&FL_MARK) && BUILTIN_TYPE(p) == T_NODE) + gc_mark(p); + p++; + } + } + } + + freelist = 0; + for (i = 0; i < heaps_used; i++) { + RVALUE *nfreelist; + int n = 0; + + nfreelist = freelist; + p = heaps[i]; pend = p + HEAP_SLOTS; + + while (p < pend) { + if (!(p->as.basic.flags & FL_MARK)) { + if (p->as.basic.flags) obj_free(p); + p->as.free.flag = 0; + p->as.free.next = nfreelist; + nfreelist = p; + n++; + } + else + RBASIC(p)->flags &= ~FL_MARK; + p++; + } + freed += n; + freelist = nfreelist; + } + if (freed < FREE_MIN) { + add_heap(); + } +} + +void +gc_force_recycle(p) + RVALUE *p; +{ + p->as.free.flag = 0; + p->as.free.next = freelist; + freelist = p; +} + +static int need_call_final = 0; + +static void +obj_free(obj) + RVALUE *obj; +{ + switch (obj->as.basic.flags & T_MASK) { + case T_NIL: + case T_FIXNUM: + case T_TRUE: + case T_FALSE: + Bug("obj_free() called for broken object"); + break; + } + + if (need_call_final) { + run_final(obj); + } + switch (obj->as.basic.flags & T_MASK) { + case T_OBJECT: + if (obj->as.object.iv_tbl) st_free_table(obj->as.object.iv_tbl); + break; + case T_MODULE: + case T_CLASS: + rb_clear_cache(); + st_free_table(obj->as.class.m_tbl); + if (obj->as.object.iv_tbl) st_free_table(obj->as.object.iv_tbl); + break; + case T_STRING: + if (!obj->as.string.orig) free(obj->as.string.ptr); + break; + case T_ARRAY: + if (obj->as.array.ptr) free(obj->as.array.ptr); + break; + case T_HASH: + st_free_table(obj->as.hash.tbl); + break; + case T_REGEXP: + reg_free(obj->as.regexp.ptr); + free(obj->as.regexp.str); + break; + case T_DATA: + if (obj->as.data.dfree && DATA_PTR(obj)) + (*obj->as.data.dfree)(DATA_PTR(obj)); + break; + case T_MATCH: + re_free_registers(obj->as.match.regs); + free(obj->as.match.regs); + break; + case T_FILE: + io_fptr_finalize(obj->as.file.fptr); + free(obj->as.file.fptr); + break; + case T_ICLASS: + /* iClass shares table with the module */ + break; + + case T_FLOAT: + case T_VARMAP: + break; + + case T_BIGNUM: + if (obj->as.bignum.digits) free(obj->as.bignum.digits); + break; + case T_NODE: + if (nd_type(obj) == NODE_SCOPE && obj->as.node.u1.tbl) { + free(obj->as.node.u1.tbl); + } + return; /* no need to free iv_tbl */ + + case T_SCOPE: + if (obj->as.scope.local_vars) { + VALUE *vars = obj->as.scope.local_vars-1; + if (vars[0] == 0) + free(obj->as.scope.local_tbl); + if (obj->as.scope.flag&SCOPE_MALLOC) + free(vars); + } + break; + + case T_STRUCT: + free(obj->as.rstruct.ptr); + break; + + default: + Bug("gc_sweep(): unknown data type %d", obj->as.basic.flags & T_MASK); + } +} + +void +gc_mark_frame(frame) + struct FRAME *frame; +{ + int n = frame->argc; + VALUE *tbl = frame->argv; + + while (n--) { + gc_mark_maybe(*tbl); + tbl++; + } + gc_mark(frame->cbase); +} + +#ifdef __GNUC__ +#if defined(__human68k__) || defined(DJGPP) +#if defined(__human68k__) +typedef unsigned long rb_jmp_buf[8]; +__asm__ (".even +_rb_setjmp: + move.l 4(sp),a0 + movem.l d3-d7/a3-a5,(a0) + moveq.l #0,d0 + rts"); +#else +#if defined(DJGPP) +typedef unsigned long rb_jmp_buf[6]; +__asm__ (".align 4 +_rb_setjmp: + pushl %ebp + movl %esp,%ebp + movl 8(%ebp),%ebp + movl %eax,(%ebp) + movl %ebx,4(%ebp) + movl %ecx,8(%ebp) + movl %edx,12(%ebp) + movl %esi,16(%ebp) + movl %edi,20(%ebp) + popl %ebp + xorl %eax,%eax + ret"); +#endif +#endif +int rb_setjmp (rb_jmp_buf); +#define jmp_buf rb_jmp_buf +#define setjmp rb_setjmp +#endif /* __human68k__ or DJGPP */ +#endif /* __GNUC__ */ + +void +gc() +{ + struct gc_list *list; + struct FRAME *frame; + jmp_buf save_regs_gc_mark; + VALUE stack_end; + + if (dont_gc) return; + dont_gc++; + + malloc_memories = 0; +#ifdef C_ALLOCA + alloca(0); +#endif + + /* mark frame stack */ + for (frame = the_frame; frame; frame = frame->prev) { + gc_mark_frame(frame); + } + gc_mark(the_scope); + gc_mark(the_dyna_vars); + + FLUSH_REGISTER_WINDOWS; + /* This assumes that all registers are saved into the jmp_buf */ + setjmp(save_regs_gc_mark); + mark_locations_array((VALUE*)&save_regs_gc_mark, sizeof(save_regs_gc_mark) / sizeof(VALUE *)); + gc_mark_locations(gc_stack_start, (VALUE*)&stack_end); +#if defined(THINK_C) || defined(__human68k__) +#ifndef __human68k__ + mark_locations_array((VALUE*)((char*)save_regs_gc_mark+2), + sizeof(save_regs_gc_mark) / sizeof(VALUE *)); +#endif + gc_mark_locations((VALUE*)((char*)gc_stack_start + 2), + (VALUE*)((char*)&stack_end + 2)); +#endif + +#ifdef THREAD + gc_mark_threads(); +#endif + + /* mark protected global variables */ + for (list = Global_List; list; list = list->next) { + gc_mark(*list->varptr); + } + + gc_mark_global_tbl(); + mark_tbl(rb_class_tbl); + gc_mark_trap_list(); + + gc_sweep(); + dont_gc--; +} + +static VALUE +gc_method() +{ + gc(); + return Qnil; +} + +void +init_stack() +{ +#ifdef __human68k__ + extern void *_SEND; + gc_stack_start = _SEND; +#else + VALUE start; + + gc_stack_start = &start; +#endif +} + +void +init_heap() +{ + init_stack(); + add_heap(); +} + +static VALUE +os_live_obj() +{ + int i; + int n = 0; + + for (i = 0; i < heaps_used; i++) { + RVALUE *p, *pend; + + p = heaps[i]; pend = p + HEAP_SLOTS; + for (;p < pend; p++) { + if (p->as.basic.flags) { + switch (TYPE(p)) { + case T_ICLASS: + case T_VARMAP: + case T_SCOPE: + case T_NODE: + continue; + case T_CLASS: + if (FL_TEST(p, FL_SINGLETON)) continue; + default: + rb_yield(p); + n++; + } + } + } + } + + return INT2FIX(n); +} + +static VALUE +os_obj_of(of) + VALUE of; +{ + int i; + int n = 0; + + for (i = 0; i < heaps_used; i++) { + RVALUE *p, *pend; + + p = heaps[i]; pend = p + HEAP_SLOTS; + for (;p < pend; p++) { + if (p->as.basic.flags) { + switch (TYPE(p)) { + case T_ICLASS: + case T_VARMAP: + case T_SCOPE: + case T_NODE: + continue; + case T_CLASS: + if (FL_TEST(p, FL_SINGLETON)) continue; + default: + if (obj_is_kind_of(p, of)) { + rb_yield(p); + n++; + } + } + } + } + } + + return INT2FIX(n); +} + +static VALUE +os_each_obj(argc, argv) + int argc; + VALUE *argv; +{ + VALUE of; + + if (rb_scan_args(argc, argv, "01", &of) == 0) { + return os_live_obj(); + } + else { + return os_obj_of(of); + } +} + +static VALUE finalizers; + +static VALUE +add_final(os, proc) + VALUE os, proc; +{ + extern VALUE cProc; + + if (!obj_is_kind_of(proc, cProc)) { + ArgError("wrong type argument %s (Proc required)", + rb_class2name(CLASS_OF(proc))); + } + ary_push(finalizers, proc); + return proc; +} + +static VALUE +rm_final(os, proc) + VALUE os, proc; +{ + ary_delete(finalizers, proc); + return proc; +} + +static VALUE +finals() +{ + return finalizers; +} + +static VALUE +call_final(os, obj) + VALUE os, obj; +{ + need_call_final = 1; + FL_SET(obj, FL_FINALIZE); + return obj; +} + +static void +run_final(obj) + VALUE obj; +{ + int i; + + if (!FL_TEST(obj, FL_FINALIZE)) return; + + obj |= FIXNUM_FLAG; /* make obj into id */ + for (i=0; i<RARRAY(finalizers)->len; i++) { + rb_eval_cmd(RARRAY(finalizers)->ptr[i], obj); + } +} + +extern VALUE cModule; + +void +Init_GC() +{ + VALUE mObSpace; + + mGC = rb_define_module("GC"); + rb_define_singleton_method(mGC, "start", gc_method, 0); + rb_define_singleton_method(mGC, "enable", gc_s_enable, 0); + rb_define_singleton_method(mGC, "disable", gc_s_disable, 0); + rb_define_method(mGC, "garbage_collect", gc_method, 0); + + mObSpace = rb_define_module("ObjectSpace"); + rb_define_module_function(mObSpace, "each_object", os_each_obj, -1); + rb_define_module_function(mObSpace, "garbage_collect", gc, 0); + rb_define_module_function(mObSpace, "add_finalizer", add_final, 1); + rb_define_module_function(mObSpace, "remove_finalizer", rm_final, 1); + rb_define_module_function(mObSpace, "finalizers", finals, 0); + rb_define_module_function(mObSpace, "call_finalizer", call_final, 1); + + rb_global_variable(&finalizers); + finalizers = ary_new(); +} diff --git a/glob.c b/glob.c new file mode 100644 index 0000000000..6c355ad260 --- /dev/null +++ b/glob.c @@ -0,0 +1,574 @@ +/* File-name wildcard pattern matching for GNU. + Copyright (C) 1985, 1988, 1989 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* To whomever it may concern: I have never seen the code which most + Unix programs use to perform this function. I wrote this from scratch + based on specifications for the pattern matching. --RMS. */ + +#include "config.h" + +#include <sys/types.h> + +#if !defined (SHELL) && (defined (_POSIX_VERSION) || defined (USGr3)) +# if !defined (HAVE_DIRENT_H) +# define HAVE_DIRENT_H +# endif /* !HAVE_DIRENT_H */ +#endif /* !SHELL && (_POSIX_VERSION || USGr3) */ + +#if defined (HAVE_DIRENT_H) +# include <dirent.h> +# if !defined (direct) +# define direct dirent +# endif /* !direct */ +# define D_NAMLEN(d) strlen ((d)->d_name) +#else /* !HAVE_DIRENT_H */ +# define D_NAMLEN(d) ((d)->d_namlen) +# if defined (USG) +# if defined (Xenix) +# include <sys/ndir.h> +# else /* !Xenix (but USG...) */ +# include "ndir.h" +# endif /* !Xenix */ +# else /* !USG */ +# if defined(NT) +# include "missing/dir.h" +# else +# include <sys/dir.h> +# endif /* !NT */ +# endif /* !USG */ +#endif /* !HAVE_DIRENT_H */ + +#if defined (_POSIX_SOURCE) || defined(DJGPP) +/* Posix does not require that the d_ino field be present, and some + systems do not provide it. */ +# define REAL_DIR_ENTRY(dp) 1 +#else +# define REAL_DIR_ENTRY(dp) (dp->d_ino != 0) +#endif /* _POSIX_SOURCE */ + +#if defined (USG) || defined (NeXT) +# if !defined (HAVE_STRING_H) +# define HAVE_STRING_H +# endif /* !HAVE_STRING_H */ +#endif /* USG || NeXT */ + +#if defined (HAVE_STRING_H) +# include <string.h> +#else /* !HAVE_STRING_H */ +# include <strings.h> +#endif /* !HAVE_STRING_H */ + +#ifndef bcopy +# define bcopy(s, d, n) (memcpy ((d), (s), (n))) +#endif + +#ifdef _AIX +#pragma alloca +#else +#if defined(HAVE_ALLOCA_H) && !defined(__GNUC__) +#include <alloca.h> +#else +char *alloca (); +#endif +#endif + +#include "fnmatch.h" + +/* If the opendir () on your system lets you open non-directory files, + then we consider that not robust. Define OPENDIR_NOT_ROBUST in the + SYSDEP_CFLAGS for your machines entry in machines.h. */ +#if defined (OPENDIR_NOT_ROBUST) +# if defined (SHELL) +# include "posixstat.h" +# else /* !SHELL */ +# include <sys/stat.h> +# endif /* !SHELL */ +#endif /* OPENDIR_NOT_ROBUST */ + +extern void *xmalloc (), *xrealloc (); +#if !defined (HAVE_STDLIB_H) +extern void free (); +#endif /* !HAVE_STDLIB_H */ + +#if !defined (NULL) +# if defined (__STDC__) +# define NULL ((void *) 0) +# else +# define NULL 0x0 +# endif /* __STDC__ */ +#endif /* !NULL */ + +#if defined (SHELL) +extern int interrupt_state; +#endif /* SHELL */ + +/* Global variable which controls whether or not * matches .*. + Non-zero means don't match .*. */ +int noglob_dot_filenames = 1; + +/* Global variable to return to signify an error in globbing. */ +char *glob_error_return; + + +/* Return nonzero if PATTERN has any special globbing chars in it. */ +int +glob_pattern_p (pattern) + char *pattern; +{ + register char *p = pattern; + register char c; + int open = 0; + + while ((c = *p++) != '\0') + switch (c) + { + case '?': + case '*': + return (1); + + case '[': /* Only accept an open brace if there is a close */ + open++; /* brace to match it. Bracket expressions must be */ + continue; /* complete, according to Posix.2 */ + case ']': + if (open) + return (1); + continue; + + case '\\': + if (*p++ == '\0') + return (0); + } + + return (0); +} + +/* Remove backslashes quoting characters in PATHNAME by modifying PATHNAME. */ +static void +dequote_pathname (pathname) + char *pathname; +{ + register int i, j; + + for (i = j = 0; pathname && pathname[i]; ) + { + if (pathname[i] == '\\') + i++; + + pathname[j++] = pathname[i++]; + + if (!pathname[i - 1]) + break; + } + pathname[j] = '\0'; +} + + +/* Return a vector of names of files in directory DIR + whose names match glob pattern PAT. + The names are not in any particular order. + Wildcards at the beginning of PAT do not match an initial period. + + The vector is terminated by an element that is a null pointer. + + To free the space allocated, first free the vector's elements, + then free the vector. + + Return 0 if cannot get enough memory to hold the pointer + and the names. + + Return -1 if cannot access directory DIR. + Look in errno for more information. */ + +char ** +glob_vector (pat, dir) + char *pat; + char *dir; +{ + struct globval + { + struct globval *next; + char *name; + }; + + DIR *d; + register struct direct *dp; + struct globval *lastlink; + register struct globval *nextlink; + register char *nextname; + unsigned int count; + int lose, skip; + register char **name_vector; + register unsigned int i; +#if defined (OPENDIR_NOT_ROBUST) + struct stat finfo; + + if (stat (dir, &finfo) < 0) + return ((char **) &glob_error_return); + + if (!S_ISDIR (finfo.st_mode)) + return ((char **) &glob_error_return); +#endif /* OPENDIR_NOT_ROBUST */ + + d = opendir (dir); + if (d == NULL) + return ((char **) &glob_error_return); + + lastlink = 0; + count = 0; + lose = 0; + skip = 0; + + /* If PAT is empty, skip the loop, but return one (empty) filename. */ + if (!pat || !*pat) + { + nextlink = (struct globval *)alloca (sizeof (struct globval)); + nextlink->next = lastlink; + nextname = (char *) xmalloc (1); + if (!nextname) + lose = 1; + else + { + lastlink = nextlink; + nextlink->name = nextname; + nextname[0] = '\0'; + count++; + } + skip = 1; + } + + /* Scan the directory, finding all names that match. + For each name that matches, allocate a struct globval + on the stack and store the name in it. + Chain those structs together; lastlink is the front of the chain. */ + while (!skip) + { + int flags; /* Flags passed to fnmatch (). */ +#if defined (SHELL) + /* Make globbing interruptible in the bash shell. */ + if (interrupt_state) + { + closedir (d); + lose = 1; + goto lost; + } +#endif /* SHELL */ + + dp = readdir (d); + if (dp == NULL) + break; + + /* If this directory entry is not to be used, try again. */ + if (!REAL_DIR_ENTRY (dp)) + continue; + + /* If a dot must be explicity matched, check to see if they do. */ + if (noglob_dot_filenames && dp->d_name[0] == '.' && pat[0] != '.') + continue; + + flags = (noglob_dot_filenames ? FNM_PERIOD : 0) | FNM_PATHNAME; + + if (fnmatch (pat, dp->d_name, flags) != FNM_NOMATCH) + { + nextlink = (struct globval *) alloca (sizeof (struct globval)); + nextlink->next = lastlink; + nextname = (char *) xmalloc (D_NAMLEN (dp) + 1); + if (nextname == NULL) + { + lose = 1; + break; + } + lastlink = nextlink; + nextlink->name = nextname; + bcopy (dp->d_name, nextname, D_NAMLEN (dp) + 1); + ++count; + } + } + (void) closedir (d); + + if (!lose) + { + name_vector = (char **) xmalloc ((count + 1) * sizeof (char *)); + lose |= name_vector == NULL; + } + + /* Have we run out of memory? */ + lost: + if (lose) + { + /* Here free the strings we have got. */ + while (lastlink) + { + free (lastlink->name); + lastlink = lastlink->next; + } +#if defined (SHELL) + if (interrupt_state) + throw_to_top_level (); +#endif /* SHELL */ + return (NULL); + } + + /* Copy the name pointers from the linked list into the vector. */ + for (i = 0; i < count; ++i) + { + name_vector[i] = lastlink->name; + lastlink = lastlink->next; + } + + name_vector[count] = NULL; + return (name_vector); +} + +/* Return a new array which is the concatenation of each string in ARRAY + to DIR. This function expects you to pass in an allocated ARRAY, and + it takes care of free()ing that array. Thus, you might think of this + function as side-effecting ARRAY. */ +static char ** +glob_dir_to_array (dir, array) + char *dir, **array; +{ + register unsigned int i, l; + int add_slash; + char **result; + + l = strlen (dir); + if (l == 0) + return (array); + + add_slash = dir[l - 1] != '/'; + + i = 0; + while (array[i] != NULL) + ++i; + + result = (char **) xmalloc ((i + 1) * sizeof (char *)); + if (result == NULL) + return (NULL); + + for (i = 0; array[i] != NULL; i++) + { + result[i] = (char *) xmalloc (l + (add_slash ? 1 : 0) + + strlen (array[i]) + 1); + if (result[i] == NULL) + return (NULL); + sprintf (result[i], "%s%s%s", dir, add_slash ? "/" : "", array[i]); + } + result[i] = NULL; + + /* Free the input array. */ + for (i = 0; array[i] != NULL; i++) + free (array[i]); + free ((char *) array); + + return (result); +} + +/* Do globbing on PATHNAME. Return an array of pathnames that match, + marking the end of the array with a null-pointer as an element. + If no pathnames match, then the array is empty (first element is null). + If there isn't enough memory, then return NULL. + If a file system error occurs, return -1; `errno' has the error code. */ +char ** +glob_filename (pathname) + char *pathname; +{ +#ifndef strrchr + char *strrchr(); +#endif + + char **result; + unsigned int result_size; + char *directory_name, *filename; + unsigned int directory_len; + + result = (char **) xmalloc (sizeof (char *)); + result_size = 1; + if (result == NULL) + return (NULL); + + result[0] = NULL; + + /* Find the filename. */ + filename = strrchr (pathname, '/'); + if (filename == NULL) + { + filename = pathname; + directory_name = ""; + directory_len = 0; + } + else + { + directory_len = (filename - pathname) + 1; + directory_name = (char *) alloca (directory_len + 1); + + bcopy (pathname, directory_name, directory_len); + directory_name[directory_len] = '\0'; + ++filename; + } + + /* If directory_name contains globbing characters, then we + have to expand the previous levels. Just recurse. */ + if (glob_pattern_p (directory_name)) + { + char **directories; + register unsigned int i; + + if (directory_name[directory_len - 1] == '/') + directory_name[directory_len - 1] = '\0'; + + directories = glob_filename (directory_name); + + if (directories == NULL) + goto memory_error; + else if (directories == (char **)&glob_error_return) + return ((char **) &glob_error_return); + else if (*directories == NULL) + { + free ((char *) directories); + return ((char **) &glob_error_return); + } + + /* We have successfully globbed the preceding directory name. + For each name in DIRECTORIES, call glob_vector on it and + FILENAME. Concatenate the results together. */ + for (i = 0; directories[i] != NULL; ++i) + { + char **temp_results; + + /* Scan directory even on a NULL pathname. That way, `*h/' + returns only directories ending in `h', instead of all + files ending in `h' with a `/' appended. */ + temp_results = glob_vector (filename, directories[i]); + + /* Handle error cases. */ + if (temp_results == NULL) + goto memory_error; + else if (temp_results == (char **)&glob_error_return) + /* This filename is probably not a directory. Ignore it. */ + ; + else + { + char **array; + register unsigned int l; + + array = glob_dir_to_array (directories[i], temp_results); + l = 0; + while (array[l] != NULL) + ++l; + + result = + (char **)xrealloc(result, (result_size + l) * sizeof (char *)); + + if (result == NULL) + goto memory_error; + + for (l = 0; array[l] != NULL; ++l) + result[result_size++ - 1] = array[l]; + + result[result_size - 1] = NULL; + + /* Note that the elements of ARRAY are not freed. */ + free ((char *) array); + } + } + /* Free the directories. */ + for (i = 0; directories[i]; i++) + free (directories[i]); + + free ((char *) directories); + + return (result); + } + + /* If there is only a directory name, return it. */ + if (*filename == '\0') + { + result = (char **) xrealloc ((char *) result, 2 * sizeof (char *)); + if (result == NULL) + return (NULL); + result[0] = (char *) xmalloc (directory_len + 1); + if (result[0] == NULL) + goto memory_error; + bcopy (directory_name, result[0], directory_len + 1); + result[1] = NULL; + return (result); + } + else + { + char **temp_results; + + /* There are no unquoted globbing characters in DIRECTORY_NAME. + Dequote it before we try to open the directory since there may + be quoted globbing characters which should be treated verbatim. */ + if (directory_len > 0) + dequote_pathname (directory_name); + + /* We allocated a small array called RESULT, which we won't be using. + Free that memory now. */ + free (result); + + /* Just return what glob_vector () returns appended to the + directory name. */ + temp_results = + glob_vector (filename, (directory_len == 0 ? "." : directory_name)); + + if (temp_results == NULL || temp_results == (char **)&glob_error_return) + return (temp_results); + + return (glob_dir_to_array (directory_name, temp_results)); + } + + /* We get to memory_error if the program has run out of memory, or + if this is the shell, and we have been interrupted. */ + memory_error: + if (result != NULL) + { + register unsigned int i; + for (i = 0; result[i] != NULL; ++i) + free (result[i]); + free ((char *) result); + } +#if defined (SHELL) + if (interrupt_state) + throw_to_top_level (); +#endif /* SHELL */ + return (NULL); +} + +#if defined (TEST) + +main (argc, argv) + int argc; + char **argv; +{ + unsigned int i; + + for (i = 1; i < argc; ++i) + { + char **value = glob_filename (argv[i]); + if (value == NULL) + puts ("Out of memory."); + else if (value == &glob_error_return) + perror (argv[i]); + else + for (i = 0; value[i] != NULL; i++) + puts (value[i]); + } + + exit (0); +} +#endif /* TEST. */ diff --git a/hash.c b/hash.c new file mode 100644 index 0000000000..fe9dfbaa26 --- /dev/null +++ b/hash.c @@ -0,0 +1,1005 @@ +/************************************************ + + hash.c - + + $Author$ + $Date$ + created at: Mon Nov 22 18:51:18 JST 1993 + + Copyright (C) 1993-1997 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include "st.h" +#include "sig.h" + +#ifdef HAVE_STRING_H +# include <string.h> +#else +char *strchr(); +#endif + +#define HASH_DELETED 0x1 +#define HASH_REHASHED 0x2 + +#ifndef NT +char *getenv(); +#endif + +VALUE cHash; + +static VALUE envtbl; +static ID hash; + +VALUE +rb_hash(obj) + VALUE obj; +{ + return rb_funcall(obj, hash, 0); +} + +static int +any_cmp(a, b) + VALUE a, b; +{ + if (FIXNUM_P(a)) { + if (FIXNUM_P(b)) return a != b; + } + else if (TYPE(a) == T_STRING) { + if (TYPE(b) == T_STRING) return str_cmp(a, b); + } + + DEFER_INTS; + a = !rb_eql(a, b); + ENABLE_INTS; + return a; +} + +static int +any_hash(a, mod) + VALUE a; + int mod; +{ + unsigned int hval; + + switch (TYPE(a)) { + case T_FIXNUM: + hval = a; + break; + + case T_STRING: + hval = str_hash(a); + break; + + default: + DEFER_INTS; + hval = rb_funcall(a, hash, 0); + if (!FIXNUM_P(hval)) { + hval = rb_funcall(hval, '%', 1, INT2FIX(65439)); + } + ENABLE_INTS; + hval = FIX2INT(hval); + } + return hval % mod; +} + +static struct st_hash_type objhash = { + any_cmp, + any_hash, +}; + +struct hash_foreach_arg { + struct RHash *hash; + enum st_retval (*func)(); + char *arg; +}; + +static int +hash_foreach_iter(key, value, arg) + VALUE key, value; + struct hash_foreach_arg *arg; +{ + int status; + + if (key == Qnil) return ST_CONTINUE; + status = (*arg->func)(key, value, arg->arg); + if (arg->hash->status & HASH_REHASHED) return ST_STOP; + return status; +} + +static VALUE +hash_foreach_call(arg) + struct hash_foreach_arg *arg; +{ + st_foreach(arg->hash->tbl, hash_foreach_iter, arg); + return Qnil; +} + +static int +hash_delete_nil(key, value) + VALUE key, value; +{ + if (key == Qnil) return ST_DELETE; + return ST_CONTINUE; +} + +static void +hash_foreach_ensure(hash) + struct RHash *hash; +{ + hash->iter_lev--; + + if (hash->iter_lev == 0) { + if (hash->status & HASH_DELETED) { + st_foreach(hash->tbl, hash_delete_nil, 0); + } + hash->status = 0; + } +} + +static int +hash_foreach(hash, func, farg) + struct RHash *hash; + enum st_retval (*func)(); + char *farg; +{ + struct hash_foreach_arg arg; + + hash->iter_lev++; + arg.hash = hash; + arg.func = func; + arg.arg = farg; + return rb_ensure(hash_foreach_call, &arg, hash_foreach_ensure, hash); +} + +static VALUE +hash_s_new(argc, argv, class) + int argc; + VALUE *argv; + VALUE class; +{ + VALUE sz; + int size; + + NEWOBJ(hash, struct RHash); + OBJSETUP(hash, class, T_HASH); + + rb_scan_args(argc, argv, "01", &sz); + if (NIL_P(sz)) size = 0; + else size = NUM2INT(sz); + + hash->iter_lev = 0; + hash->status = 0; + hash->tbl = 0; /* avoid GC crashing */ + hash->tbl = st_init_table_with_size(&objhash, size); + + return (VALUE)hash; +} + +VALUE +hash_new2(class) + VALUE class; +{ + return hash_s_new(0, 0, class); +} + +VALUE +hash_new() +{ + return hash_new2(cHash); +} + +static VALUE +hash_s_create(argc, argv, class) + int argc; + VALUE *argv; + VALUE class; +{ + struct RHash *hash; + int i; + + if (argc == 1 && TYPE(argv[0]) == T_HASH) { + if (class == CLASS_OF(argv[0])) return argv[0]; + else { + NEWOBJ(hash, struct RHash); + OBJSETUP(hash, class, T_HASH); + + hash->iter_lev = 0; + hash->status = 0; + hash->tbl = 0; /* avoid GC crashing */ + hash->tbl = (st_table*)st_copy(RHASH(argv[0])->tbl); + return (VALUE)hash; + } + } + + if (argc % 2 != 0) { + ArgError("odd number args for Hash"); + } + hash = (struct RHash*)hash_new2(class); + + for (i=0; i<argc; i+=2) { + st_insert(hash->tbl, argv[i], argv[i+1]); + } + + return (VALUE)hash; +} + +static VALUE +hash_clone(hash) + struct RHash *hash; +{ + NEWOBJ(hash2, struct RHash); + CLONESETUP(hash2, hash); + + hash2->iter_lev = 0; + hash2->status = 0; + hash2->tbl = 0; /* avoid GC crashing */ + hash2->tbl = (st_table*)st_copy(hash->tbl); + + return (VALUE)hash2; +} + +static int +hash_rehash_i(key, value, tbl) + VALUE key, value; + st_table *tbl; +{ + if (key != Qnil) { + st_insert(tbl, key, value); + } + return ST_CONTINUE; +} + +static VALUE +hash_rehash(hash) + struct RHash *hash; +{ + st_table *tbl = st_init_table_with_size(&objhash, hash->tbl->num_entries); + + st_foreach(hash->tbl, hash_rehash_i, tbl); + st_free_table(hash->tbl); + hash->tbl = tbl; + if (hash->iter_lev > 0) hash->status |= HASH_REHASHED; + + return (VALUE)hash; +} + +VALUE +hash_aref(hash, key) + struct RHash *hash; + VALUE key; +{ + VALUE val; + + if (!st_lookup(hash->tbl, key, &val)) { + return Qnil; + } + return val; +} + +static VALUE +hash_indexes(argc, argv, hash) + int argc; + VALUE *argv; + struct RHash *hash; +{ + struct RArray *indexes; + int i; + + indexes = (struct RArray*)ary_new2(argc); + for (i=0; i<argc; i++) { + indexes->ptr[i] = hash_aref(hash, argv[i]); + } + indexes->len = i; + return (VALUE)indexes; +} + +static VALUE +hash_delete(hash, key) + struct RHash *hash; + VALUE key; +{ + VALUE val; + + rb_secure(5); + if (hash->iter_lev > 0 && st_delete_safe(hash->tbl, &key, &val, Qnil)) + return val; + else if (st_delete(hash->tbl, &key, &val)) + return val; + if (iterator_p()) rb_yield(key); + return Qnil; +} + +struct shift_var { + int stop; + VALUE key; + VALUE val; +}; + +static int +shift_i(key, value, var) + VALUE key, value; + struct shift_var *var; +{ + if (key == Qnil) return ST_CONTINUE; + if (var->stop) return ST_STOP; + var->stop = 1; + var->key = key; + var->val = value; + return ST_DELETE; +} + +static VALUE +hash_shift(hash) + struct RHash *hash; +{ + struct shift_var var; + + rb_secure(5); + var.stop = 0; + st_foreach(hash->tbl, shift_i, &var); + + if (var.stop == 0) return Qnil; + return assoc_new(var.key, var.val); +} + +static int +delete_if_i(key, value) + VALUE key, value; +{ + if (key == Qnil) return ST_CONTINUE; + if (rb_yield(assoc_new(key, value))) + return ST_DELETE; + return ST_CONTINUE; +} + +static VALUE +hash_delete_if(hash) + struct RHash *hash; +{ + rb_secure(5); + hash_foreach(hash, delete_if_i, 0); + + return (VALUE)hash; +} + +static int +clear_i(key, value) + VALUE key, value; +{ + return ST_DELETE; +} + +static VALUE +hash_clear(hash) + struct RHash *hash; +{ + rb_secure(5); + st_foreach(hash->tbl, clear_i); + + return (VALUE)hash; +} + +VALUE +hash_aset(hash, key, val) + struct RHash *hash; + VALUE key, val; +{ + rb_secure(5); + if (NIL_P(val)) { + hash_delete(hash, key); + return Qnil; + } + if (TYPE(key) == T_STRING) { + key = str_dup_freezed(key); + } + st_insert(hash->tbl, key, val); + return val; +} + +static VALUE +hash_length(hash) + struct RHash *hash; +{ + return INT2FIX(hash->tbl->num_entries); +} + +VALUE +hash_empty_p(hash) + struct RHash *hash; +{ + if (hash->tbl->num_entries == 0) + return TRUE; + return FALSE; +} + +static int +each_value_i(key, value) + VALUE key, value; +{ + if (key == Qnil) return ST_CONTINUE; + rb_yield(value); + return ST_CONTINUE; +} + +static VALUE +hash_each_value(hash) + struct RHash *hash; +{ + hash_foreach(hash, each_value_i); + return (VALUE)hash; +} + +static int +each_key_i(key, value) + VALUE key, value; +{ + if (key == Qnil) return ST_CONTINUE; + rb_yield(key); + return ST_CONTINUE; +} + +static VALUE +hash_each_key(hash) + struct RHash *hash; +{ + hash_foreach(hash, each_key_i); + return (VALUE)hash; +} + +static int +each_pair_i(key, value) + VALUE key, value; +{ + if (key == Qnil) return ST_CONTINUE; + rb_yield(assoc_new(key, value)); + return ST_CONTINUE; +} + +static VALUE +hash_each_pair(hash) + struct RHash *hash; +{ + hash_foreach(hash, each_pair_i); + return (VALUE)hash; +} + +static int +to_a_i(key, value, ary) + VALUE key, value, ary; +{ + if (key == Qnil) return ST_CONTINUE; + ary_push(ary, assoc_new(key, value)); + return ST_CONTINUE; +} + +static VALUE +hash_to_a(hash) + struct RHash *hash; +{ + VALUE ary; + + ary = ary_new(); + st_foreach(hash->tbl, to_a_i, ary); + + return ary; +} + +static int +inspect_i(key, value, str) + VALUE key, value; + struct RString *str; +{ + VALUE str2; + + if (key == Qnil) return ST_CONTINUE; + if (str->len > 1) { + str_cat(str, ", ", 2); + } + str2 = rb_inspect(key); + str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len); + str_cat(str, "=>", 2); + str2 = rb_inspect(value); + str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len); + + return ST_CONTINUE; +} + +static VALUE +hash_inspect(hash) + struct RHash *hash; +{ + VALUE str; + + str = str_new2("{"); + st_foreach(hash->tbl, inspect_i, str); + str_cat(str, "}", 1); + + return str; +} + +static VALUE +hash_to_s(hash) + VALUE hash; +{ + return ary_to_s(hash_to_a(hash)); +} + +static int +keys_i(key, value, ary) + VALUE key, value, ary; +{ + if (key == Qnil) return ST_CONTINUE; + ary_push(ary, key); + return ST_CONTINUE; +} + +static VALUE +hash_keys(hash) + struct RHash *hash; +{ + VALUE ary; + + ary = ary_new(); + st_foreach(hash->tbl, keys_i, ary); + + return ary; +} + +static int +values_i(key, value, ary) + VALUE key, value, ary; +{ + if (key == Qnil) return ST_CONTINUE; + ary_push(ary, value); + return ST_CONTINUE; +} + +static VALUE +hash_values(hash) + struct RHash *hash; +{ + VALUE ary; + + ary = ary_new(); + st_foreach(hash->tbl, values_i, ary); + + return ary; +} + +static VALUE +hash_has_key(hash, key) + struct RHash *hash; + VALUE key; +{ + if (st_lookup(hash->tbl, key, 0)) { + return TRUE; + } + return FALSE; +} + +static int +hash_search_value(key, value, data) + VALUE key, value, *data; +{ + if (key == Qnil) return ST_CONTINUE; + if (rb_equal(value, data[1])) { + data[0] = TRUE; + return ST_STOP; + } + return ST_CONTINUE; +} + +static VALUE +hash_has_value(hash, val) + struct RHash *hash; + VALUE val; +{ + VALUE data[2]; + + data[0] = FALSE; + data[1] = val; + st_foreach(hash->tbl, hash_search_value, data); + return data[0]; +} + +struct equal_data { + int result; + st_table *tbl; +}; + +static int +equal_i(key, val1, data) + VALUE key, val1; + struct equal_data *data; +{ + VALUE val2; + + if (key == Qnil) return ST_CONTINUE; + if (!st_lookup(data->tbl, key, &val2)) { + data->result = FALSE; + return ST_STOP; + } + if (!rb_equal(val1, val2)) { + data->result = FALSE; + return ST_STOP; + } + return ST_CONTINUE; +} + +static VALUE +hash_equal(hash1, hash2) + struct RHash *hash1, *hash2; +{ + struct equal_data data; + + if (TYPE(hash2) != T_HASH) return FALSE; + if (hash1->tbl->num_entries != hash2->tbl->num_entries) + return FALSE; + + data.tbl = hash2->tbl; + data.result = TRUE; + st_foreach(hash1->tbl, equal_i, &data); + + return data.result; +} + +static int +hash_invert_i(key, value, hash) + VALUE key, value; + struct RHash *hash; +{ + if (key == Qnil) return ST_CONTINUE; + hash_aset(hash, value, key); + return ST_CONTINUE; +} + +static VALUE +hash_invert(hash) + struct RHash *hash; +{ + VALUE h = hash_new(); + + st_foreach(hash->tbl, hash_invert_i, h); + return h; +} + +int env_path_tainted = 0; + +#ifndef NT +extern char **environ; +#endif + +static VALUE +env_delete(obj, name) + VALUE obj; + struct RString *name; +{ + int i, len; + char *nam, *val = 0; + + rb_secure(4); + Check_Type(name, T_STRING); + nam = name->ptr; + len = strlen(nam); + if (strcmp(nam, "PATH") == 0) env_path_tainted = 0; + for(i=0; environ[i]; i++) { + if (strncmp(environ[i], nam, len) == 0 && environ[i][len] == '=') { + val = environ[i]+len+1; + break; + } + } + while (environ[i]) { + environ[i] = environ[i+1]; + i++; + } + if (val) { + return str_new2(val); + } + return Qnil; +} + +static VALUE +f_getenv(obj, name) + VALUE obj; + struct RString *name; +{ + char *env; + + Check_Type(name, T_STRING); + + if (strlen(name->ptr) != name->len) + ArgError("Bad environment name"); + + env = getenv(name->ptr); + if (env) { + if (strcmp(name->ptr, "PATH") == 0 && !env_path_tainted) + return str_new2(env); + return str_taint(str_new2(env)); + } + return Qnil; +} + +static VALUE +f_setenv(obj, name, value) + VALUE obj; + struct RString *name, *value; +{ + if (rb_safe_level() >= 4) { + extern VALUE eSecurityError; + Raise(eSecurityError, "cannot change environment variable"); + } + + Check_SafeStr(name); + if (NIL_P(value)) { + env_delete(obj, name); + return Qnil; + } + + Check_SafeStr(value); + if (strlen(name->ptr) != name->len) + ArgError("Bad environment name"); + if (strlen(value->ptr) != value->len) + ArgError("Bad environment value"); + + setenv(name->ptr, value->ptr, 1); + if (strcmp(name->ptr, "PATH") == 0) env_path_tainted = 0; + return TRUE; +} + +static VALUE +env_keys() +{ + char **env; + VALUE ary = ary_new(); + + env = environ; + while (*env) { + char *s = strchr(*env, '='); + ary_push(ary, str_taint(str_new(*env, s-*env))); + env++; + } + return ary; +} + +static VALUE +env_each_key(hash) + VALUE hash; +{ + return ary_each(env_keys()); +} + +static VALUE +env_values() +{ + char **env; + VALUE ary = ary_new(); + + env = environ; + while (*env) { + char *s = strchr(*env, '='); + ary_push(ary, str_taint(str_new2(s+1))); + env++; + } + return ary; +} + +static VALUE +env_each_value(hash) + VALUE hash; +{ + return ary_each(env_values()); +} + +static VALUE +env_each(hash) + VALUE hash; +{ + VALUE ary = env_keys(); + VALUE *ptr = RARRAY(ary)->ptr; + int len = RARRAY(ary)->len; + + while (len--) { + VALUE val = f_getenv(Qnil, *ptr); + if (!NIL_P(val)) { + rb_yield(assoc_new(*ptr, val)); + } + ptr++; + } + return hash; +} + +static VALUE +env_delete_if() +{ + VALUE ary = env_keys(); + VALUE *ptr = RARRAY(ary)->ptr; + int len = RARRAY(ary)->len; + + while (len--) { + VALUE val = f_getenv(Qnil, *ptr); + if (!NIL_P(val)) { + if (RTEST(rb_yield(assoc_new(*ptr, val)))) { + env_delete(Qnil, *ptr); + } + } + ptr++; + } + return envtbl; +} + +static VALUE +env_to_s() +{ + return str_new2("ENV"); +} + +static VALUE +env_to_a() +{ + char **env; + VALUE ary = ary_new(); + + env = environ; + while (*env) { + char *s = strchr(*env, '='); + ary_push(ary, assoc_new(str_taint(str_new(*env, s-*env)), + str_taint(str_new2(s+1)))); + env++; + } + return ary; +} + +static VALUE +env_none() +{ + return Qnil; +} + +static VALUE +env_size() +{ + int i; + + for(i=0; environ[i]; i++) + ; + return INT2FIX(i); +} + +static VALUE +env_empty_p() +{ + if (environ[0] == 0) return TRUE; + return FALSE; +} + +static VALUE +env_has_key(env, key) + VALUE env, key; +{ + if (TYPE(key) != T_STRING) return FALSE; + if (getenv(RSTRING(key)->ptr)) return TRUE; + return FALSE; +} + +static VALUE +env_has_value(dmy, value) + VALUE dmy, value; +{ + char **env; + VALUE ary; + + if (TYPE(value) != T_STRING) return FALSE; + ary = ary_new(); + env = environ; + while (*env) { + char *s = strchr(*env, '=')+1; + int len = strlen(s); + if (strncmp(s, RSTRING(value)->ptr, len) == 0) return TRUE; + env++; + } + return FALSE; +} + +static VALUE +env_indexes(argc, argv) + int argc; + VALUE *argv; +{ + int i; + VALUE indexes = ary_new2(argc); + + 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] = str_new2(v); + } + else { + RARRAY(indexes)->ptr[i] = Qnil; + } + RARRAY(indexes)->len = i+1; + } + + return indexes; +} + +void +Init_Hash() +{ + extern VALUE mEnumerable; + + hash = rb_intern("hash"); + + cHash = rb_define_class("Hash", cObject); + + rb_include_module(cHash, mEnumerable); + + rb_define_singleton_method(cHash, "new", hash_s_new, -1); + rb_define_singleton_method(cHash, "[]", hash_s_create, -1); + + rb_define_method(cHash,"clone", hash_clone, 0); + rb_define_method(cHash,"rehash", hash_rehash, 0); + + rb_define_method(cHash,"to_a", hash_to_a, 0); + rb_define_method(cHash,"to_s", hash_to_s, 0); + rb_define_method(cHash,"inspect", hash_inspect, 0); + + rb_define_method(cHash,"==", hash_equal, 1); + rb_define_method(cHash,"[]", hash_aref, 1); + rb_define_method(cHash,"[]=", hash_aset, 2); + rb_define_method(cHash,"indexes", hash_indexes, -1); + rb_define_method(cHash,"length", hash_length, 0); + rb_define_alias(cHash, "size", "length"); + rb_define_method(cHash,"empty?", hash_empty_p, 0); + + rb_define_method(cHash,"each", hash_each_pair, 0); + rb_define_method(cHash,"each_value", hash_each_value, 0); + rb_define_method(cHash,"each_key", hash_each_key, 0); + rb_define_method(cHash,"each_pair", hash_each_pair, 0); + + rb_define_method(cHash,"keys", hash_keys, 0); + rb_define_method(cHash,"values", hash_values, 0); + + rb_define_method(cHash,"shift", hash_shift, 0); + rb_define_method(cHash,"delete", hash_delete, 1); + rb_define_method(cHash,"delete_if", hash_delete_if, 0); + rb_define_method(cHash,"clear", hash_clear, 0); + rb_define_method(cHash,"invert", hash_invert, 0); + + rb_define_method(cHash,"include?", hash_has_key, 1); + rb_define_method(cHash,"has_key?", hash_has_key, 1); + rb_define_method(cHash,"has_value?", hash_has_value, 1); + rb_define_method(cHash,"key?", hash_has_key, 1); + rb_define_method(cHash,"value?", hash_has_value, 1); + + envtbl = obj_alloc(cObject); + rb_extend_object(envtbl, mEnumerable); + + rb_define_singleton_method(envtbl,"[]", f_getenv, 1); + rb_define_singleton_method(envtbl,"[]=", f_setenv, 2); + rb_define_singleton_method(envtbl,"each", env_each, 0); + rb_define_singleton_method(envtbl,"each_pair", env_each, 0); + rb_define_singleton_method(envtbl,"each_key", env_each_key, 0); + rb_define_singleton_method(envtbl,"each_value", env_each_value, 0); + rb_define_singleton_method(envtbl,"delete", env_delete, 1); + rb_define_singleton_method(envtbl,"delete_if", env_delete_if, 0); + rb_define_singleton_method(envtbl,"to_s", env_to_s, 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,"indexes", env_indexes, -1); + rb_define_singleton_method(envtbl,"length", env_size, 0); + 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,"include?", env_has_key, 1); + rb_define_singleton_method(envtbl,"has_key?", env_has_key, 1); + rb_define_singleton_method(envtbl,"has_value?", env_has_value, 1); + rb_define_singleton_method(envtbl,"key?", env_has_key, 1); + rb_define_singleton_method(envtbl,"value?", env_has_value, 1); + + rb_define_global_const("ENV", envtbl); +} diff --git a/inits.c b/inits.c new file mode 100644 index 0000000000..ebfc0f9c3a --- /dev/null +++ b/inits.c @@ -0,0 +1,48 @@ +/************************************************ + + inits.c - + + $Author$ + $Date$ + created at: Tue Dec 28 16:01:58 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" + +void +rb_call_inits() +{ + Init_sym(); + Init_var_tables(); + Init_Object(); +#ifdef THREAD + Init_Thread(); +#endif + Init_eval(); + Init_Comparable(); + Init_Enumerable(); + Init_String(); + Init_Exception(); + Init_Numeric(); + Init_Bignum(); + Init_Array(); + Init_Hash(); + Init_Struct(); + Init_Regexp(); + Init_pack(); + Init_Range(); + Init_IO(); + Init_Dir(); + Init_Time(); + Init_Random(); + Init_signal(); + Init_process(); + Init_load(); + Init_Proc(); + Init_Math(); + Init_GC(); + Init_version(); +} diff --git a/install-sh b/install-sh new file mode 100644 index 0000000000..89fc9b098b --- /dev/null +++ b/install-sh @@ -0,0 +1,238 @@ +#! /bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. +# + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +tranformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 @@ -0,0 +1,2306 @@ +/************************************************ + + io.c - + + $Author$ + $Date$ + created at: Fri Oct 15 18:08:59 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include "io.h" +#include <ctype.h> +#include <errno.h> + +#include <sys/types.h> +#if !defined(DJGPP) && !defined(NT) && !defined(__human68k__) +#include <sys/ioctl.h> +#endif +#if defined(HAVE_FCNTL) +#include <fcntl.h> +#endif + +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#else +#ifndef NT +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; +#endif +#endif +#ifdef HAVE_VFORK_H +#include <vfork.h> +#endif + +#include <sys/stat.h> + +#if defined(DJGPP) || defined(__CYGWIN32__) || defined(NT) || defined(__human68k__) +#include <fcntl.h> +#endif + +#ifdef HAVE_SYS_PARAM_H +# include <sys/param.h> +#else +# define NOFILE 64 +#endif + +VALUE rb_ad_string(); + +VALUE cIO; +extern VALUE cFile; +VALUE eEOFError; +VALUE eIOError; + +VALUE rb_stdin, rb_stdout, rb_stderr, rb_defout; + +VALUE FS, OFS; +VALUE RS, ORS; +VALUE RS_default; + +static VALUE argf; + +ID id_write; + +VALUE lastline_get(); +void lastline_set(); + +extern char *inplace; + +struct timeval time_timeval(); + +#ifdef _STDIO_USES_IOSTREAM /* GNU libc */ +# ifdef _IO_fpos_t +# define READ_DATA_PENDING(fp) ((fp)->_IO_read_ptr != (fp)->_IO_read_end) +# else +# define READ_DATA_PENDING(fp) ((fp)->_gptr < (fp)->_egptr) +# endif +#else +# ifdef FILE_COUNT +# define READ_DATA_PENDING(fp) ((fp)->FILE_COUNT > 0) +# else +/* requires systems own version of the ReadDataPending() */ +extern int ReadDataPending(); +# define READ_DATA_PENDING(fp) ReadDataPending(fp) +# endif +#endif + +#ifndef THREAD +# define READ_CHECK(fp) 0 +#else +# define READ_CHECK(fp) do {\ + if (!READ_DATA_PENDING(fp)) thread_wait_fd(fileno(fp));\ +} while(0) +#endif + +void +eof_error() +{ + Raise(eEOFError, "End of file reached"); +} + +void +io_writable(fptr) + OpenFile *fptr; +{ + if (!(fptr->mode & FMODE_WRITABLE)) { + Raise(eIOError, "not opened for writing"); + } +} + +void +io_readable(fptr) + OpenFile *fptr; +{ + if (!(fptr->mode & FMODE_READABLE)) { + Raise(eIOError, "not opened for reading"); + } +} + +static void +closed() +{ + Raise(eIOError, "closed stream"); +} + +/* writing functions */ +VALUE +io_write(io, str) + VALUE io; + struct RString *str; +{ + OpenFile *fptr; + FILE *f; + int n; + + if (TYPE(str) != T_STRING) + str = (struct RString*)obj_as_string(str); + if (str->len == 0) return INT2FIX(0); + + if (BUILTIN_TYPE(io) != T_FILE) { + return rb_funcall(io, id_write, 1, str); + } + + rb_secure(4); + GetOpenFile(io, fptr); + io_writable(fptr); + + f = GetWriteFile(fptr); + if (f == NULL) closed(); + +#ifdef __human68k__ + { + register UCHAR *ptr = str->ptr; + n = (int) str->len; + while (--n >= 0) + if (fputc(*ptr++, f) == EOF) + rb_sys_fail(fptr->path); + n = ptr - str->ptr; + } + if (ferror(f)) + rb_sys_fail(fptr->path); +#else + n = fwrite(str->ptr, 1, str->len, f); + if (n == 0 || ferror(f)) { + rb_sys_fail(fptr->path); + } +#endif + if (fptr->mode & FMODE_SYNC) { + fflush(f); + } + + return INT2FIX(n); +} + +static VALUE +io_puts(io, str) + VALUE io, str; +{ + io_write(io, str); + return io; +} + +static VALUE +io_flush(io) + VALUE io; +{ + OpenFile *fptr; + FILE *f; + + GetOpenFile(io, fptr); + io_writable(fptr); + f = GetWriteFile(fptr); + if (f == NULL) closed(); + + if (fflush(f) == EOF) rb_sys_fail(0); + + return io; +} + +static VALUE +io_eof(io) + VALUE io; +{ + OpenFile *fptr; + int ch; + + GetOpenFile(io, fptr); + io_readable(fptr); + if (fptr->f == NULL) closed(); + + if (READ_DATA_PENDING(fptr->f)) return FALSE; + if (feof(fptr->f)) return TRUE; + + TRAP_BEG; + ch = getc(fptr->f); + TRAP_END; + + if (ch != EOF) { + (void)ungetc(ch, fptr->f); + return FALSE; + } + return TRUE; +} + +static VALUE +io_sync(io) + VALUE io; +{ + OpenFile *fptr; + + GetOpenFile(io, fptr); + return (fptr->mode & FMODE_SYNC) ? TRUE : FALSE; +} + +static VALUE +io_set_sync(io, mode) + VALUE io, mode; +{ + OpenFile *fptr; + + GetOpenFile(io, fptr); + if (RTEST(mode)) { + fptr->mode |= FMODE_SYNC; + } + else { + fptr->mode &= ~FMODE_SYNC; + } + return mode; +} + +static VALUE +io_fileno(io) + VALUE io; +{ + OpenFile *fptr; + int fd; + + GetOpenFile(io, fptr); + fd = fileno(fptr->f); + return INT2FIX(fd); +} + +/* reading functions */ +static VALUE +read_all(port) + VALUE port; +{ + OpenFile *fptr; + VALUE str = Qnil; + char buf[BUFSIZ]; + int n; + + GetOpenFile(port, fptr); + io_readable(fptr); + if (fptr->f == NULL) closed(); + + for (;;) { + READ_CHECK(fptr->f); + TRAP_BEG; + n = fread(buf, 1, BUFSIZ, fptr->f); + TRAP_END; + if (n == 0) break; + if (n < 0) rb_sys_fail(0); + if (NIL_P(str)) str = str_new(buf, n); + else str_cat(str, buf, n); + } + return str_taint(str); +} + +static VALUE +io_read(argc, argv, io) + int argc; + VALUE *argv; + VALUE io; +{ + OpenFile *fptr; + int n, lgt; + VALUE len, str; + + if (rb_scan_args(argc, argv, "01", &len) == 0) { + return read_all(io); + } + + lgt = NUM2INT(len); + GetOpenFile(io, fptr); + io_readable(fptr); + if (fptr->f == NULL) closed(); + + str = str_new(0, lgt); + + READ_CHECK(fptr->f); + TRAP_BEG; + n = fread(RSTRING(str)->ptr, 1, RSTRING(str)->len, fptr->f); + TRAP_END; + if (n == 0) { + if (feof(fptr->f)) return Qnil; + rb_sys_fail(fptr->path); + } + + RSTRING(str)->len = n; + RSTRING(str)->ptr[n] = '\0'; + + return str_taint(str); +} + +static VALUE lineno; + +VALUE +io_gets_method(argc, argv, io) + int argc; + VALUE argv; + VALUE io; +{ + OpenFile *fptr; + FILE *f; + struct RString *str; + int c, newline; + char *rsptr; + int rslen, rspara = 0; + VALUE rs; + + if (argc == 0) rs = RS; + else { + rb_scan_args(argc, argv, "1", &rs); + if (!NIL_P(rs)) Check_Type(rs, T_STRING); + } + + GetOpenFile(io, fptr); + io_readable(fptr); + f = fptr->f; + if (f == NULL) closed(); + + if (!NIL_P(rs)) { + rslen = RSTRING(rs)->len; + if (rslen == 0) { + rsptr = "\n\n"; + rslen = 2; + rspara = 1; + } + else { + rsptr = RSTRING(rs)->ptr; + } + } + else { + rsptr = 0; + rslen = 0; + } + newline = rslen ? rsptr[rslen - 1] : 0777; + + if (rspara) { + do { + READ_CHECK(f); + TRAP_BEG; + c = getc(f); + TRAP_END; + if (c != '\n') { + ungetc(c,f); + break; + } + } while (c != EOF); + } + + { + char buf[8192]; + char *bp, *bpe = buf + sizeof buf - 3; + int cnt; + int append = 0; + + again: + bp = buf; + + if (rslen) { + for (;;) { + READ_CHECK(f); + TRAP_BEG; + c = getc(f); + TRAP_END; + if (c == EOF) break; + if ((*bp++ = c) == newline) break; + if (bp == bpe) break; + } + cnt = bp - buf; + } + else { + READ_CHECK(f); + TRAP_BEG; + cnt = fread(buf, 1, sizeof(buf), f); + TRAP_END; + c = cnt ? 0 : EOF; + } + + if (c == EOF) { + if (!append && cnt == 0) { + str = RSTRING(Qnil); + goto return_gets; + } + } + + if (append) + str_cat(str, buf, cnt); + else + str = (struct RString*)str_new(buf, cnt); + + if (c != EOF && + (!rslen || + str->len < rslen || + memcmp(str->ptr+str->len-rslen, rsptr, rslen))) { + append = 1; + goto again; + } + } + + return_gets: + if (rspara) { + while (c != EOF) { + READ_CHECK(f); + TRAP_BEG; + c = getc(f); + TRAP_END; + if (c != '\n') { + ungetc(c, f); + break; + } + } + } + + if (str) { + fptr->lineno++; + lineno = INT2FIX(fptr->lineno); + } + lastline_set(str); + + return str_taint(str); +} + +VALUE +io_gets(io) + VALUE io; +{ + return io_gets_method(0, 0, io); +} + +static VALUE +io_readline(argc, argv, io) + int argc; + VALUE argv; + VALUE io; +{ + VALUE line = io_gets_method(argc, argv, io); + + if (NIL_P(line)) { + eof_error(); + } + return line; +} + +static VALUE +io_readlines(argc, argv, io) + int argc; + VALUE argv; + VALUE io; +{ + VALUE line, ary; + + ary = ary_new(); + while (!NIL_P(line = io_gets_method(argc, argv, io))) { + ary_push(ary, line); + } + return ary; +} + +static VALUE +io_each_line(argc, argv, io) + int argc; + VALUE argv; + VALUE io; +{ + VALUE str; + + while (!NIL_P(str = io_gets_method(argc, argv, io))) { + rb_yield(str); + } + return Qnil; +} + +static VALUE +io_each_byte(io) + VALUE io; +{ + OpenFile *fptr; + FILE *f; + int c; + + GetOpenFile(io, fptr); + io_readable(fptr); + f = fptr->f; + if (f == NULL) closed(); + + for (;;) { + READ_CHECK(f); + TRAP_BEG; + c = getc(f); + TRAP_END; + if (c == EOF) break; + rb_yield(INT2FIX(c & 0xff)); + } + if (ferror(f) != 0) rb_sys_fail(fptr->path); + return Qnil; +} + +VALUE +io_getc(io) + VALUE io; +{ + OpenFile *fptr; + FILE *f; + int c; + + GetOpenFile(io, fptr); + io_readable(fptr); + f = fptr->f; + if (f == NULL) closed(); + + READ_CHECK(f); + TRAP_BEG; + c = getc(f); + TRAP_END; + + if (c == EOF) { + if (ferror(f) != 0) rb_sys_fail(fptr->path); + return Qnil; + } + return INT2FIX(c & 0xff); +} + +static VALUE +io_readchar(io) + VALUE io; +{ + VALUE c = io_getc(io); + + if (NIL_P(c)) { + eof_error(); + } + return c; +} + +VALUE +io_ungetc(io, c) + VALUE io, c; +{ + OpenFile *fptr; + + Check_Type(c, T_FIXNUM); + GetOpenFile(io, fptr); + io_readable(fptr); + if (fptr->f == NULL) closed(); + + if (ungetc(FIX2INT(c), fptr->f) == EOF) + rb_sys_fail(fptr->path); +} + +static VALUE +io_isatty(io) + VALUE io; +{ + OpenFile *fptr; + + GetOpenFile(io, fptr); + if (fptr->f == NULL) closed(); + if (isatty(fileno(fptr->f)) == 0) + return FALSE; + return TRUE; +} + +static void +fptr_finalize(fptr) + OpenFile *fptr; +{ + if (fptr->f != NULL) { + fclose(fptr->f); + } + if (fptr->f2 != NULL) { + fclose(fptr->f2); + } + if (fptr->path) { + free(fptr->path); + fptr->path = NULL; + } + if (fptr->pid) { + rb_syswait(fptr->pid); + fptr->pid = 0; + } +} + +void +io_fptr_finalize(fptr) + OpenFile *fptr; +{ + if (fptr->finalize) { + (*fptr->finalize)(fptr); + fptr->finalize = 0; + } + else { + fptr_finalize(fptr); + } + fptr->f = fptr->f2 = NULL; +} + +VALUE +io_close(io) + VALUE io; +{ + OpenFile *fptr; + + GetOpenFile(io, fptr); + io_fptr_finalize(fptr); + + return Qnil; +} + +static VALUE +io_closed(io) + VALUE io; +{ + OpenFile *fptr; + + GetOpenFile(io, fptr); + return fptr->f?FALSE:TRUE; +} + +static VALUE +io_syswrite(io, str) + VALUE io, str; +{ + OpenFile *fptr; + FILE *f; + int n; + + rb_secure(4); + if (TYPE(str) != T_STRING) + str = obj_as_string(str); + + GetOpenFile(io, fptr); + io_writable(fptr); + f = GetWriteFile(fptr); + if (f == NULL) closed(); + +#ifdef THREAD + thread_fd_writable(fileno(f)); +#endif + n = write(fileno(f), RSTRING(str)->ptr, RSTRING(str)->len); + + if (n == -1) rb_sys_fail(fptr->path); + + return INT2FIX(n); +} + +static VALUE +io_sysread(io, len) + VALUE io, len; +{ + OpenFile *fptr; + int n, ilen; + VALUE str; + + ilen = NUM2INT(len); + GetOpenFile(io, fptr); + io_readable(fptr); + if (fptr->f == NULL) closed(); + + str = str_new(0, ilen); + +#ifdef THREAD + thread_wait_fd(fileno(fptr->f)); +#endif + TRAP_BEG; + n = read(fileno(fptr->f), RSTRING(str)->ptr, RSTRING(str)->len); + TRAP_END; + + if (n == -1) rb_sys_fail(fptr->path); + if (n == 0) eof_error(); + + RSTRING(str)->len = n; + RSTRING(str)->ptr[n] = '\0'; + return str_taint(str); +} + +VALUE +io_binmode(io) + VALUE io; +{ +#if defined(NT) || defined(DJGPP) || defined(__CYGWIN32__) || defined(__human68k__) + OpenFile *fptr; + + GetOpenFile(io, fptr); +#ifdef __human68k__ + if (fptr->f) + fmode(fptr->f, _IOBIN); + if (fptr->f2); + fmode(fptr->f2, _IOBIN); +#else + if (fptr->f && setmode(fileno(fptr->f), O_BINARY) == -1) + rb_sys_fail(fptr->path); + if (fptr->f2 && setmode(fileno(fptr->f2), O_BINARY) == -1) + rb_sys_fail(fptr->path); +#endif + + fptr->mode |= FMODE_BINMODE; +#endif + return io; +} + +int +io_mode_flags(mode) + char *mode; +{ + int flags = 0; + + switch (mode[0]) { + case 'r': + flags |= FMODE_READABLE; + break; + case 'w': + flags |= FMODE_WRITABLE; + break; + case 'a': + flags |= FMODE_WRITABLE; + break; + default: + ArgError("illegal access mode"); + } + + if (mode[1] == 'b') { + flags |= FMODE_BINMODE; + mode++; + } + + if (mode[1] == '+') { + flags |= FMODE_READWRITE; + } + + return flags; +} + +FILE * +rb_fopen(fname, mode) + char *fname; + char *mode; +{ + FILE *f; + + f = fopen(fname, mode); + if (f == NULL) { + if (errno == EMFILE || errno == ENFILE) { + gc(); + f = fopen(fname, mode); + } + if (f == NULL) { + rb_sys_fail(fname); + } + } + return f; +} + +FILE * +rb_fdopen(fd, mode) + int fd; + char *mode; +{ + FILE *f; + + f = fdopen(fd, mode); + if (f == NULL) { + if (errno == EMFILE) { + f = fdopen(fd, mode); + } + if (f == NULL) { + rb_sys_fail(0); + } + } + return f; +} + +#if defined (NT) || defined(DJGPP) || defined(__CYGWIN32__) || defined(__human68k__) +static struct pipe_list { + OpenFile *fptr; + struct pipe_list *next; +} *pipe_list; + +static void +pipe_add_fptr(fptr) + OpenFile *fptr; +{ + struct pipe_list *list; + + list = ALLOC(struct pipe_list); + list->fptr = fptr; + list->next = pipe_list; + pipe_list = list; +} + +static void +pipe_del_fptr(fptr) + OpenFile *fptr; +{ + struct pipe_list *list = pipe_list; + struct pipe_list *tmp; + + if (list->fptr == fptr) { + pipe_list = list->next; + return; + } + + while (list->next) { + if (list->next->fptr == fptr) { + tmp = list->next; + list->next = list->next->next; + free(tmp); + return; + } + list = list->next; + } +} + +static void +pipe_atexit() +{ + struct pipe_list *list = pipe_list; + + while (list) { + io_fptr_finalize(list->fptr); + list = list->next; + } +} + +#if !defined (__CYGWIN32__) +static void +pipe_finalize(fptr) + OpenFile *fptr; +{ + if (fptr->f != NULL) { + pclose(fptr->f); + } + if (fptr->f2 != NULL) { + pclose(fptr->f2); + } + fptr->f = fptr->f2 = NULL; + pipe_del_fptr(fptr); +} +#endif +#endif + +void +io_unbuffered(fptr) + OpenFile *fptr; +{ + if (fptr->f2 == 0) TypeError("non-writable fptr"); + if (fptr->f != 0) setbuf(fptr->f, NULL); + setbuf(fptr->f2, NULL); + fptr->mode |= FMODE_SYNC; +} + +static VALUE +pipe_open(pname, mode) + char *pname, *mode; +{ + int modef = io_mode_flags(mode); + OpenFile *fptr; + +#if defined(NT) || defined(DJGPP) || defined(__human68k__) + FILE *f = popen(pname, mode); + + if (f == NULL) rb_sys_fail(pname); + else { + NEWOBJ(port, struct RFile); + OBJSETUP(port, cIO, T_FILE); + MakeOpenFile(port, fptr); + fptr->finalize = pipe_finalize; + fptr->mode = modef; + + pipe_add_fptr(fptr); + if (modef & FMODE_READABLE) fptr->f = f; + if (modef & FMODE_WRITABLE) { + fptr->f2 = f; + io_unbuffered(fptr); + } + return (VALUE)port; + } +#else + int pid, pr[2], pw[2]; + volatile int doexec; + + if (((modef & FMODE_READABLE) && pipe(pr) == -1) || + ((modef & FMODE_WRITABLE) && pipe(pw) == -1)) + rb_sys_fail(pname); + + doexec = (strcmp("-", pname) != 0); + if (!doexec) { + fflush(stdin); /* is it really needed? */ + fflush(stdout); + fflush(stderr); + } + + retry: + switch (pid = (doexec?vfork():fork())) { + case 0: /* child */ + if (modef & FMODE_READABLE) { + close(pr[0]); + if (pr[1] != 1) { + dup2(pr[1], 1); + close(pr[1]); + } + } + if (modef & FMODE_WRITABLE) { + close(pw[1]); + if (pw[0] != 0) { + dup2(pw[0], 0); + close(pw[0]); + } + } + + if (doexec) { + VALUE serr = io_fileno(rb_stderr); + int fd = FIX2INT(serr); + extern char *sourcefile; + extern int sourceline; + + if (fd != 2) { + close(2); + dup2(fd, 2); + close(fd); + } + + for (fd = 3; fd < NOFILE; fd++) + close(fd); + rb_proc_exec(pname); + fprintf(stderr, "%s:%d: command not found: %s\n", + sourcefile, sourceline, pname); + _exit(127); + } + return Qnil; + + case -1: /* fork failed */ + if (errno == EAGAIN) { +#ifdef THREAD + thread_sleep(1); +#else + sleep(1); +#endif + goto retry; + } + close(pr[0]); close(pw[1]); + rb_sys_fail(pname); + break; + + default: /* parent */ + { + NEWOBJ(port, struct RFile); + OBJSETUP(port, cIO, T_FILE); + MakeOpenFile(port, fptr); + fptr->mode = modef; + fptr->mode |= FMODE_SYNC; + fptr->pid = pid; + + if (modef & FMODE_READABLE) { + close(pr[1]); + fptr->f = rb_fdopen(pr[0], "r"); + } + if (modef & FMODE_WRITABLE) { + FILE *f = rb_fdopen(pw[1], "w"); + + close(pw[0]); + if (fptr->f) fptr->f2 = f; + else fptr->f = f; + } +#if defined (__CYGWIN32__) + pipe_add_fptr(fptr); +#endif + return (VALUE)port; + } + } +#endif +} + +static VALUE +io_s_popen(argc, argv, self) + int argc; + VALUE *argv; + VALUE self; +{ + char *mode; + VALUE pname, pmode; + + rb_scan_args(argc, argv, "11", &pname, &pmode); + Check_SafeStr(pname); + if (NIL_P(pmode)) { + mode = "r"; + } + else { + Check_Type(pmode, T_STRING); + if (RSTRING(pmode)->len == 0 || RSTRING(pmode)->len > 3) + ArgError("illegal access mode"); + mode = RSTRING(pmode)->ptr; + } + return pipe_open(RSTRING(pname)->ptr, mode); +} + +static VALUE +io_open(fname, mode) + char *fname, *mode; +{ + if (fname[0] == '|') { + return pipe_open(fname+1, mode); + } + else { + return file_open(fname, mode); + } +} + +static VALUE +f_open(argc, argv, self) + int argc; + VALUE *argv; + VALUE self; +{ + char *mode; + VALUE pname, pmode; + + rb_scan_args(argc, argv, "11", &pname, &pmode); + Check_SafeStr(pname); + if (NIL_P(pmode)) { + mode = "r"; + } + else { + Check_Type(pmode, T_STRING); + if (RSTRING(pmode)->len == 0 || RSTRING(pmode)->len > 3) + ArgError("illegal access mode"); + mode = RSTRING(pmode)->ptr; + } + return io_open(RSTRING(pname)->ptr, mode); +} + +#ifndef NT +extern char *strdup(); +#endif + +VALUE +io_reopen(io, nfile) + VALUE io, nfile; +{ + OpenFile *fptr, *orig; + char *mode; + int fd; + + GetOpenFile(io, fptr); + Check_Type(nfile, T_FILE); + GetOpenFile(nfile, orig); + + if (orig->f2) { + fflush(orig->f2); + } + else if (orig->mode & FMODE_WRITABLE) { + fflush(orig->f); + } + + /* copy OpenFile structure */ + fptr->mode = orig->mode; + fptr->pid = orig->pid; + fptr->lineno = orig->lineno; + if (fptr->path) free(fptr->path); + if (orig->path) fptr->path = strdup(orig->path); + else fptr->path = 0; + fptr->finalize = orig->finalize; + + switch (fptr->mode & FMODE_READWRITE) { + case FMODE_READABLE: + default: + mode = "r"; break; + case FMODE_WRITABLE: + mode = "w"; break; + case FMODE_READWRITE: + if (orig->f2) mode = "r"; + else mode = "r+"; + break; + } + fd = fileno(fptr->f); + fclose(fptr->f); + dup2(fileno(orig->f), fd); + fptr->f = rb_fdopen(fd, mode); + + if (fptr->f2) { + fd = fileno(fptr->f2); + fclose(fptr->f2); + if (orig->f2) { + dup2(fileno(orig->f2), fd); + fptr->f = rb_fdopen(fd, "w"); + } + else { + fptr->f2 = 0; + } + } + + if (fptr->mode & FMODE_BINMODE) { + io_binmode(io); + } + + RBASIC(io)->class = RBASIC(nfile)->class; + return io; +} + +static VALUE +io_clone(io) + VALUE io; +{ + OpenFile *fptr, *orig; + int fd; + char *mode; + + NEWOBJ(obj, struct RFile); + OBJSETUP(obj, CLASS_OF(io), T_FILE); + + GetOpenFile(io, orig); + MakeOpenFile(obj, fptr); + + if (orig->f2) { + fflush(orig->f2); + } + else if (orig->mode & FMODE_WRITABLE) { + fflush(orig->f); + } + + /* copy OpenFile structure */ + fptr->mode = orig->mode; + fptr->pid = orig->pid; + fptr->lineno = orig->lineno; + if (orig->path) fptr->path = strdup(orig->path); + fptr->finalize = orig->finalize; + + switch (fptr->mode & FMODE_READWRITE) { + case FMODE_READABLE: + default: + mode = "r"; break; + case FMODE_WRITABLE: + mode = "w"; break; + case FMODE_READWRITE: + if (orig->f2) mode = "r"; + else mode = "r+"; + break; + } + fd = dup(fileno(orig->f)); + fptr->f = rb_fdopen(fd, mode); + if (fptr->f2) { + fd = dup(fileno(orig->f2)); + fptr->f = rb_fdopen(fd, "w"); + } + if (fptr->mode & FMODE_BINMODE) { + io_binmode(obj); + } + + return (VALUE)obj; +} + +static VALUE +io_printf(argc, argv, out) + int argc; + VALUE argv[]; + VALUE out; +{ + rb_funcall(out, id_write, 1, f_sprintf(argc, argv)); + + return Qnil; +} + +static VALUE +f_printf(argc, argv) + int argc; + VALUE argv[]; +{ + VALUE out; + + if (argc == 0) return Qnil; + if (TYPE(argv[0]) == T_STRING) { + out = rb_defout; + } + else if (rb_respond_to(argv[0], id_write)) { + out = argv[0]; + argv++; + argc--; + } + else { + NameError("output must responds to `write'"); + } + rb_funcall(out, id_write, 1, f_sprintf(argc, argv)); + + return Qnil; +} + +static VALUE +io_print(argc, argv, out) + int argc; + VALUE *argv; + VALUE out; +{ + int i; + VALUE line; + + /* if no argument given, print `$_' */ + if (argc == 0) { + argc = 1; + line = lastline_get(); + argv = &line; + } + for (i=0; i<argc; i++) { + if (!NIL_P(OFS) && i>0) { + io_write(out, OFS); + } + switch (TYPE(argv[i])) { + case T_NIL: + io_write(out, str_new2("nil")); + break; + case T_ARRAY: + ary_print_on(argv[i], out); + break; + default: + io_write(out, argv[i]); + break; + } + } + if (!NIL_P(ORS)) { + io_write(out, ORS); + } + + return Qnil; +} + +static VALUE +f_print(argc, argv) + int argc; + VALUE *argv; +{ + io_print(argc, argv, rb_defout); + return Qnil; +} + +static VALUE +f_p(obj, val) + VALUE obj, val; +{ + VALUE str = rb_inspect(val); + + Check_Type(str, T_STRING); + io_write(rb_defout, str); + io_write(rb_defout, str_new2("\n")); + return Qnil; +} + +static void +io_defset(val, id) + VALUE val; + ID id; +{ + if (TYPE(val) == T_STRING) { + val = io_open(RSTRING(val)->ptr, "w"); + } + if (!rb_respond_to(val, id_write)) { + TypeError("$< must have write method, %s given", + rb_class2name(CLASS_OF(val))); + } + rb_defout = val; +} + +static VALUE +prep_stdio(f, mode) + FILE *f; + int mode; +{ + OpenFile *fp; + NEWOBJ(obj, struct RFile); + OBJSETUP(obj, cIO, T_FILE); + + MakeOpenFile(obj, fp); + fp->f = f; + fp->mode = mode; + + return (VALUE)obj; +} + +static VALUE +io_s_new(argc, argv) + int argc; + VALUE *argv; +{ + VALUE fnum, mode; + FILE *f; + char *m = "r"; + + rb_scan_args(argc, argv, "11", &fnum, &mode); + + if (!NIL_P(mode)) { + Check_SafeStr(mode); + m = RSTRING(mode)->ptr; + } + f = rb_fdopen(NUM2INT(fnum), m); + return prep_stdio(f, io_mode_flags(m)); +} + +static VALUE filename, file; +static int gets_lineno; +static int init_p = 0, next_p = 0; + +static int +next_argv() +{ + extern VALUE Argv; + char *fn; + + if (init_p == 0) { + if (RARRAY(Argv)->len > 0) { + next_p = 1; + } + else { + next_p = -1; + file = rb_stdin; + } + init_p = 1; + gets_lineno = 0; + } + + retry: + if (next_p == 1) { + next_p = 0; + if (RARRAY(Argv)->len > 0) { + filename = ary_shift(Argv); + fn = RSTRING(filename)->ptr; + if (RSTRING(filename)->len == 1 && fn[0] == '-') { + file = rb_stdin; + if (inplace) { + rb_defout = rb_stdout; + } + } + else { + FILE *fr = rb_fopen(fn, "r"); + + if (inplace) { + struct stat st, st2; + VALUE str; + FILE *fw; + + if (rb_defout != rb_stdout) { + io_close(rb_defout); + } + fstat(fileno(fr), &st); + if (*inplace) { + str = str_new2(fn); +#if defined(MSDOS) || defined(__CYGWIN32__) || defined(NT) + add_suffix(str, inplace); +#else + str_cat(str, inplace, strlen(inplace)); +#endif +#if defined(MSDOS) || defined(__BOW__) || defined(__CYGWIN32__) || defined(NT) || defined(__human68k__) + (void)fclose(fr); + (void)unlink(RSTRING(str)->ptr); + (void)rename(fn, RSTRING(str)->ptr); + fr = rb_fopen(RSTRING(str)->ptr, "r"); +#else + if (rename(fn, RSTRING(str)->ptr) < 0) { + Warning("Can't rename %s to %s: %s, skipping file", + fn, RSTRING(str)->ptr, strerror(errno)); + fclose(fr); + goto retry; + } +#endif + } + else { +#if !defined(MSDOS) && !defined(__BOW__) && !defined(__CYGWIN32__) && !defined(NT) && !defined(__human68k__) + if (unlink(fn) < 0) { + Warning("Can't remove %s: %s, skipping file", + fn, strerror(errno)); + fclose(fr); + goto retry; + } +#else + Fatal("Can't do inplace edit without backup"); +#endif + } + fw = rb_fopen(fn, "w"); +#if !defined(MSDOS) && !defined(__CYGWIN32__) && !(NT) && !defined(__human68k__) + fstat(fileno(fw), &st2); + fchmod(fileno(fw), st.st_mode); + if (st.st_uid!=st2.st_uid || st.st_gid!=st2.st_gid) { + fchown(fileno(fw), st.st_uid, st.st_gid); + } +#endif + rb_defout = prep_stdio(fw, FMODE_WRITABLE); + } + file = prep_stdio(fr, FMODE_READABLE); + } + } + else { + init_p = 0; + return FALSE; + } + } + return TRUE; +} + +static VALUE +f_gets_method(argc, argv) + int argc; + VALUE *argv; +{ + VALUE line; + + retry: + if (!next_argv()) return Qnil; + line = io_gets_method(argc, argv, file); + if (NIL_P(line) && next_p != -1) { + io_close(file); + next_p = 1; + goto retry; + } + gets_lineno++; + lineno = INT2FIX(gets_lineno); + + return line; +} + +VALUE +f_gets() +{ + return f_gets_method(0,0); +} + +static VALUE +f_readline(argc, argv) + int argc; + VALUE argv; +{ + VALUE line = f_gets_method(argc, argv); + + if (NIL_P(line)) { + eof_error(); + } + + return line; +} + +static VALUE +f_eof() +{ + if (init_p == 0 && !next_argv()) + return TRUE; + if (io_eof(file)) { + next_p = 1; + return TRUE; + } + return FALSE; +} + +static VALUE +f_getc() +{ + return io_getc(rb_stdin); +} + +static VALUE +f_ungetc(obj, c) + VALUE obj, c; +{ + if (!next_argv()) { + ArgError("no stream to ungetc"); + } + + return io_ungetc(file, c); +} + +static VALUE +f_readchar() +{ + VALUE c = f_getc(); + + if (NIL_P(c)) { + eof_error(); + } + return c; +} + +static VALUE +f_readlines(argc, argv) + int argc; + VALUE argv; +{ + VALUE line, ary; + + ary = ary_new(); + while (!NIL_P(line = f_gets_method(argc, argv))) { + ary_push(ary, line); + } + + return ary; +} + +void +rb_str_setter(val, id, var) + VALUE val; + ID id; + VALUE *var; +{ + if (!NIL_P(val) && TYPE(val) != T_STRING) { + TypeError("value of %s must be String", rb_id2name(id)); + } + *var = val; +} + +static VALUE +f_backquote(obj, str) + VALUE obj; + struct RString *str; +{ + VALUE port, result; + + Check_SafeStr(str); + port = pipe_open(str->ptr, "r"); + result = read_all(port); + + io_close(port); + + if (NIL_P(result)) return str_new(0,0); + return result; +} + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif +#ifdef NT +#define select(v, w, x, y, z) (-1) /* anytime fail */ +#endif + +static VALUE +f_select(argc, argv, obj) + int argc; + VALUE *argv; + VALUE obj; +{ + VALUE read, write, except, timeout, res, list; + fd_set rset, wset, eset, pset; + fd_set *rp, *wp, *ep; + struct timeval *tp, timerec; + OpenFile *fptr; + int i, max = 0, n; + int interrupt = 0; + int pending = 0; + + rb_scan_args(argc, argv, "13", &read, &write, &except, &timeout); + if (NIL_P(timeout)) { + tp = NULL; + } + else { + timerec = time_timeval(timeout); + tp = &timerec; + } + + FD_ZERO(&pset); + if (!NIL_P(read)) { + + Check_Type(read, T_ARRAY); + rp = &rset; + FD_ZERO(rp); + for (i=0; i<RARRAY(read)->len; i++) { + Check_Type(RARRAY(read)->ptr[i], T_FILE); + GetOpenFile(RARRAY(read)->ptr[i], fptr); + if (fptr->f == NULL) closed(); + FD_SET(fileno(fptr->f), rp); + if (READ_DATA_PENDING(fptr->f)) { /* check for buffered data */ + pending++; + FD_SET(fileno(fptr->f), &pset); + } + if (max < fileno(fptr->f)) max = fileno(fptr->f); + } + if (pending) { /* no blocking if there's buffered data */ + timerec.tv_sec = timerec.tv_usec = 0; + tp = &timerec; + } + } + else + rp = NULL; + + if (!NIL_P(write)) { + Check_Type(write, T_ARRAY); + wp = &wset; + FD_ZERO(wp); + for (i=0; i<RARRAY(write)->len; i++) { + Check_Type(RARRAY(write)->ptr[i], T_FILE); + GetOpenFile(RARRAY(write)->ptr[i], fptr); + if (fptr->f == NULL) closed(); + FD_SET(fileno(fptr->f), wp); + if (max > fileno(fptr->f)) max = fileno(fptr->f); + if (fptr->f2) { + FD_SET(fileno(fptr->f2), wp); + if (max < (int)fileno(fptr->f2)) max = fileno(fptr->f2); + } + } + } + else + wp = NULL; + + if (!NIL_P(except)) { + Check_Type(except, T_ARRAY); + ep = &eset; + FD_ZERO(ep); + for (i=0; i<RARRAY(except)->len; i++) { + Check_Type(RARRAY(except)->ptr[i], T_FILE); + GetOpenFile(RARRAY(except)->ptr[i], fptr); + if (fptr->f == NULL) closed(); + FD_SET(fileno(fptr->f), ep); + if (max < fileno(fptr->f)) max = fileno(fptr->f); + if (fptr->f2) { + FD_SET(fileno(fptr->f2), ep); + if (max > (int)fileno(fptr->f2)) max = fileno(fptr->f2); + } + } + } + else + ep = NULL; + + max++; + +#ifdef THREAD + n = thread_select(max, rp, wp, ep, tp); + if (n < 0) { + rb_sys_fail(0); + } +#else + retry: + TRAP_BEG; + n = select(max, rp, wp, ep, tp); + TRAP_END; + if (n < 0) { + if (errno != EINTR) { + rb_sys_fail(0); + } + if (tp == NULL) goto retry; + interrupt = 1; + } +#endif + if (!pending && n == 0) return Qnil; /* returns nil on timeout */ + + res = ary_new2(3); + ary_push(res, rp?ary_new():ary_new2(0)); + ary_push(res, wp?ary_new():ary_new2(0)); + ary_push(res, ep?ary_new():ary_new2(0)); + + if (interrupt == 0) { + if (rp) { + list = RARRAY(res)->ptr[0]; + for (i=0; i< RARRAY(read)->len; i++) { + GetOpenFile(RARRAY(read)->ptr[i], fptr); + if (FD_ISSET(fileno(fptr->f), rp) + || FD_ISSET(fileno(fptr->f), &pset)) { + ary_push(list, RARRAY(read)->ptr[i]); + } + } + } + + if (wp) { + list = RARRAY(res)->ptr[1]; + for (i=0; i< RARRAY(write)->len; i++) { + GetOpenFile(RARRAY(write)->ptr[i], fptr); + if (FD_ISSET(fileno(fptr->f), wp)) { + ary_push(list, RARRAY(write)->ptr[i]); + } + else if (fptr->f2 && FD_ISSET(fileno(fptr->f2), wp)) { + ary_push(list, RARRAY(write)->ptr[i]); + } + } + } + + if (ep) { + list = RARRAY(res)->ptr[2]; + for (i=0; i< RARRAY(except)->len; i++) { + GetOpenFile(RARRAY(except)->ptr[i], fptr); + if (FD_ISSET(fileno(fptr->f), ep)) { + ary_push(list, RARRAY(except)->ptr[i]); + } + else if (fptr->f2 && FD_ISSET(fileno(fptr->f2), ep)) { + ary_push(list, RARRAY(except)->ptr[i]); + } + } + } + } + + return res; /* returns an empty array on interrupt */ +} + +static VALUE +io_ctl(io, req, arg, io_p) + VALUE io, req; + struct RString *arg; + int io_p; +{ +#if !defined(MSDOS) && !defined(__human68k__) + int cmd = NUM2INT(req); + OpenFile *fptr; + int len, fd; + long narg = 0; + int retval; + + rb_secure(2); + GetOpenFile(io, fptr); + + if (NIL_P(arg) || (VALUE)arg == FALSE) { + narg = 0; + } + else if (FIXNUM_P(arg)) { + narg = FIX2INT(arg); + } + else if ((VALUE)arg == TRUE) { + narg = 1; + } + else { + Check_Type(arg, T_STRING); + +#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 */ +#else + len = 256; /* otherwise guess at what's safe */ +#endif + str_modify(arg); + + if (len < arg->len) { + len = arg->len; + } + str_resize(arg, len+1); + arg->ptr[len] = 17; /* a little sanity check here */ + narg = (long)arg->ptr; + } + fd = fileno(fptr->f); +#ifdef HAVE_FCNTL + retval = io_p?ioctl(fd, cmd, narg):fcntl(fd, cmd, narg); +#else + if (!io_p) { + rb_notimplement(); + } + retval = ioctl(fd, cmd, narg); +#endif + if (retval < 0) rb_sys_fail(fptr->path); + if (TYPE(arg) == T_STRING && arg->ptr[len] != 17) { + ArgError("return value overflowed string"); + } + return INT2NUM(retval); +#else + rb_notimplement(); +#endif +} + +static VALUE +io_ioctl(argc, argv, io) + int argc; + VALUE *argv; + VALUE io; +{ + VALUE req, arg; + + rb_scan_args(argc, argv, "11", &req, &arg); + return io_ctl(io, req, arg, 1); +} + +static VALUE +io_fcntl(argc, argv, io) + int argc; + VALUE *argv; + VALUE io; +{ +#ifdef HAVE_FCNTL + VALUE req, arg; + + rb_scan_args(argc, argv, "11", &req, &arg); + return io_ctl(io, req, arg, 0); +#else + rb_notimplement(); +#endif +} + +static VALUE +f_syscall(argc, argv) + int argc; + VALUE *argv; +{ +#ifdef HAVE_SYSCALL +#ifdef atarist + unsigned long arg[14]; /* yes, we really need that many ! */ +#else + unsigned long arg[8]; +#endif + int retval = -1; + int i = 1; + int items = argc - 1; + + /* This probably won't work on machines where sizeof(long) != sizeof(int) + * or where sizeof(long) != sizeof(char*). But such machines will + * not likely have syscall implemented either, so who cares? + */ + + rb_secure(2); + arg[0] = NUM2INT(argv[0]); argv++; + while (items--) { + if (FIXNUM_P(*argv)) { + arg[i] = (unsigned long)NUM2INT(*argv); argv++; + } + else { + Check_Type(*argv, T_STRING); + str_modify(*argv); + arg[i] = (unsigned long)RSTRING(*argv)->ptr; argv++; + } + i++; + } + switch (argc) { + case 0: + ArgError("Too few args to syscall"); + case 1: + retval = syscall(arg[0]); + break; + case 2: + retval = syscall(arg[0],arg[1]); + break; + case 3: + retval = syscall(arg[0],arg[1],arg[2]); + break; + case 4: + retval = syscall(arg[0],arg[1],arg[2],arg[3]); + break; + case 5: + retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4]); + break; + case 6: + retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5]); + break; + case 7: + retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6]); + break; + case 8: + retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6], + arg[7]); + break; +#ifdef atarist + case 9: + retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6], + arg[7], arg[8]); + break; + case 10: + retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6], + arg[7], arg[8], arg[9]); + break; + case 11: + retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6], + arg[7], arg[8], arg[9], arg[10]); + break; + case 12: + retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6], + arg[7], arg[8], arg[9], arg[10], arg[11]); + break; + case 13: + retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6], + arg[7], arg[8], arg[9], arg[10], arg[11], arg[12]); + break; + case 14: + retval = syscall(arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6], + arg[7], arg[8], arg[9], arg[10], arg[11], arg[12], arg[13]); + break; +#endif /* atarist */ + } + if (retval == -1) rb_sys_fail(0); + return INT2FIX(0); +#else + rb_notimplement(); +#endif +} + +static VALUE +io_s_pipe() +{ +#ifndef __human68k__ + int pipes[2]; + VALUE r, w, ary; + +#ifdef NT + if (_pipe(pipes, 1024, O_BINARY) == -1) +#else + if (pipe(pipes) == -1) +#endif + rb_sys_fail(0); + + r = prep_stdio(fdopen(pipes[0], "r"), FMODE_READABLE); + w = prep_stdio(fdopen(pipes[1], "w"), FMODE_WRITABLE); + + ary = ary_new2(2); + ary_push(ary, r); + ary_push(ary, w); + + return ary; +#else + rb_notimplement(); +#endif +} + +struct foreach_arg { + int argc; + VALUE sep; + VALUE io; +}; + +static VALUE +io_foreach_line(arg) + struct foreach_arg *arg; +{ + VALUE str; + + while (!NIL_P(str = io_gets_method(arg->argc, &arg->sep, arg->io))) { + rb_yield(str); + } + return Qnil; +} + +static VALUE +io_s_foreach(argc, argv, io) + int argc; + VALUE *argv; + VALUE io; +{ + struct RString *fname; + struct foreach_arg arg; + + rb_scan_args(argc, argv, "11", &fname, &arg.sep); + Check_SafeStr(fname); + + arg.argc = argc - 1; + arg.io = io_open(fname->ptr, "r"); + return rb_ensure(io_foreach_line, &arg, io_close, arg.io); +} + +static VALUE +io_readline_line(arg) + struct foreach_arg *arg; +{ + VALUE line, ary; + + ary = ary_new(); + while (!NIL_P(line = io_gets_method(arg->argc, &arg->sep, arg->io))) { + ary_push(ary, line); + } + + return ary; +} + +static VALUE +io_s_readlines(argc, argv, io) + int argc; + VALUE *argv; + VALUE io; +{ + struct RString *fname; + struct foreach_arg arg; + + rb_scan_args(argc, argv, "11", &fname, &arg.sep); + Check_SafeStr(fname); + + arg.argc = argc - 1; + arg.io = io_open(fname->ptr, "r"); + return rb_ensure(io_readline_line, &arg, io_close, arg.io); +} + +static VALUE +arg_fileno() +{ + return io_fileno(file); +} + +static VALUE +arg_read(argc, argv) + int argc; + VALUE *argv; +{ + VALUE tmp, str; + int len; + + if (argc == 1) len = NUM2INT(argv[0]); + str = Qnil; + + retry: + if (!next_argv()) return str; + tmp = io_read(argc, argv, file); + if (NIL_P(tmp) && next_p != -1) { + io_close(file); + next_p = 1; + goto retry; + } + if (NIL_P(tmp)) return str; + else if (NIL_P(str)) str = tmp; + else str_cat(str, RSTRING(tmp)->ptr, RSTRING(tmp)->len); + if (argc == 0) { + goto retry; + } + if (RSTRING(tmp)->len < len) { + len -= RSTRING(tmp)->len; + argv[0] = INT2FIX(len); + goto retry; + } + + return str; +} + +static VALUE +arg_getc() +{ + VALUE byte; + + retry: + if (!next_argv()) return Qnil; + byte = io_getc(file); + if (NIL_P(byte) && next_p != -1) { + io_close(file); + next_p = 1; + goto retry; + } + + return byte; +} + +static VALUE +arg_readchar() +{ + VALUE c = io_getc(file); + + if (NIL_P(c)) { + eof_error(); + } + return c; +} + +static VALUE +arg_each_line(argc, argv) + int argc; + VALUE argv; +{ + VALUE str; + + while (RTEST(str = f_gets_method(argc, argv))) { + rb_yield(str); + } + return Qnil; +} + +static VALUE +arg_each_byte() +{ + VALUE byte; + + while (!NIL_P(byte = arg_getc())) { + rb_yield(byte); + } + return Qnil; +} + +static VALUE +arg_filename() +{ + return filename; +} + +static VALUE +arg_file() +{ + return file; +} + +static VALUE +arg_skip() +{ + if (next_p != -1) { + io_close(file); + next_p = 1; + } + return argf; +} + +static VALUE +arg_close() +{ + io_close(file); + if (next_p != -1) { + next_p = 1; + } + gets_lineno = 0; + return argf; +} + +static VALUE +arg_closed() +{ + return io_closed(file); +} + +static VALUE +opt_i_get() +{ + if (!inplace) return Qnil; + return str_new2(inplace); +} + +static void +opt_i_set(val) + struct RString *val; +{ + if (NIL_P(val)) { + inplace = 0; + return; + } + Check_Type(val, T_STRING); + inplace = val->ptr; +} + +extern VALUE mEnumerable; + +void +Init_IO() +{ + extern VALUE mKernel; + extern VALUE eException; + + eEOFError = rb_define_class("EOFError", eException); + + id_write = rb_intern("write"); + + rb_define_global_function("syscall", f_syscall, -1); + + rb_define_global_function("open", f_open, -1); + rb_define_global_function("printf", f_printf, -1); + rb_define_global_function("print", f_print, -1); + rb_define_global_function("gets", f_gets_method, -1); + rb_define_global_function("readline", f_readline, -1); + rb_define_global_function("eof", f_eof, 0); + rb_define_global_function("eof?", f_eof, 0); + rb_define_global_function("getc", f_getc, 0); + rb_define_global_function("readchar", f_readchar, 0); + rb_define_global_function("select", f_select, -1); + rb_define_global_function("ungetc", f_ungetc, 1); + + rb_define_global_function("readlines", f_readlines, -1); + + rb_define_global_function("`", f_backquote, 1); + rb_define_global_function("pipe", io_s_pipe, 0); + + rb_define_global_function("p", f_p, 1); + + cIO = rb_define_class("IO", cObject); + rb_include_module(cIO, mEnumerable); + + rb_define_singleton_method(cIO, "new", io_s_new, -1); + rb_define_singleton_method(cIO, "popen", io_s_popen, -1); + rb_define_singleton_method(cIO, "foreach", io_s_foreach, -1); + rb_define_singleton_method(cIO, "readlines", io_s_readlines, -1); + rb_define_singleton_method(cIO, "select", f_select, -1); + + FS = OFS = Qnil; + rb_define_hooked_variable("$;", &FS, 0, rb_str_setter); + rb_define_hooked_variable("$-F", &FS, 0, rb_str_setter); + rb_define_hooked_variable("$,", &OFS, 0, rb_str_setter); + + RS = RS_default = str_new2("\n"); ORS = Qnil; + rb_global_variable(&RS_default); + rb_define_hooked_variable("$/", &RS, 0, rb_str_setter); + rb_define_hooked_variable("$-0", &RS, 0, rb_str_setter); + rb_define_hooked_variable("$\\", &ORS, 0, rb_str_setter); + + rb_define_variable("$.", &lineno); + rb_define_virtual_variable("$_", lastline_get, lastline_set); + + rb_define_method(cIO, "clone", io_clone, 0); + rb_define_method(cIO, "reopen", io_reopen, 1); + + rb_define_method(cIO, "print", io_print, -1); + rb_define_method(cIO, "printf", io_printf, -1); + + rb_define_method(cIO, "each", io_each_line, -1); + rb_define_method(cIO, "each_line", io_each_line, -1); + rb_define_method(cIO, "each_byte", io_each_byte, 0); + + rb_define_method(cIO, "syswrite", io_syswrite, 1); + rb_define_method(cIO, "sysread", io_sysread, 1); + + rb_define_method(cIO, "fileno", io_fileno, 0); + rb_define_alias(cIO, "to_i", "fileno"); + + rb_define_method(cIO, "sync", io_sync, 0); + rb_define_method(cIO, "sync=", io_set_sync, 1); + + rb_define_method(cIO, "readlines", io_readlines, -1); + + rb_define_method(cIO, "read", io_read, -1); + rb_define_method(cIO, "write", io_write, 1); + rb_define_method(cIO, "gets", io_gets_method, -1); + rb_define_method(cIO, "readline", io_readline, -1); + rb_define_method(cIO, "getc", io_getc, 0); + rb_define_method(cIO, "readchar", io_readchar, 0); + rb_define_method(cIO, "ungetc",io_ungetc, 1); + rb_define_method(cIO, "puts", io_puts, 1); + rb_define_method(cIO, "<<", io_puts, 1); + rb_define_method(cIO, "flush", io_flush, 0); + rb_define_method(cIO, "eof", io_eof, 0); + rb_define_method(cIO, "eof?", io_eof, 0); + + rb_define_method(cIO, "close", io_close, 0); + rb_define_method(cIO, "closed?", io_closed, 0); + + rb_define_method(cIO, "isatty", io_isatty, 0); + rb_define_method(cIO, "tty?", io_isatty, 0); + rb_define_method(cIO, "binmode", io_binmode, 0); + + rb_define_method(cIO, "ioctl", io_ioctl, -1); + rb_define_method(cIO, "fcntl", io_fcntl, -1); + + rb_stdin = prep_stdio(stdin, FMODE_READABLE); + rb_define_readonly_variable("$stdin", &rb_stdin); + rb_stdout = prep_stdio(stdout, FMODE_WRITABLE); + rb_define_readonly_variable("$stdout", &rb_stdout); + rb_stderr = prep_stdio(stderr, FMODE_WRITABLE); + rb_define_readonly_variable("$stderr", &rb_stderr); + rb_defout = rb_stdout; + rb_define_hooked_variable("$>", &rb_defout, 0, io_defset); + + rb_define_global_const("STDIN", rb_stdin); + rb_define_global_const("STDOUT", rb_stdout); + rb_define_global_const("STDERR", rb_stderr); + + argf = obj_alloc(cObject); + rb_extend_object(argf, mEnumerable); + + rb_define_readonly_variable("$<", &argf); + rb_define_global_const("ARGF", argf); + + rb_define_singleton_method(argf, "fileno", arg_fileno, 0); + rb_define_singleton_method(argf, "to_i", arg_fileno, 0); + rb_define_singleton_method(argf, "each", arg_each_line, -1); + rb_define_singleton_method(argf, "each_line", arg_each_line, -1); + rb_define_singleton_method(argf, "each_byte", arg_each_byte, 0); + + rb_define_singleton_method(argf, "read", arg_read, -1); + rb_define_singleton_method(argf, "readlines", f_readlines, -1); + rb_define_singleton_method(argf, "to_a", f_readlines, -1); + rb_define_singleton_method(argf, "gets", f_gets_method, -1); + rb_define_singleton_method(argf, "readline", f_readline, -1); + rb_define_singleton_method(argf, "getc", arg_getc, 0); + rb_define_singleton_method(argf, "readchar", arg_readchar, 0); + rb_define_singleton_method(argf, "eof", f_eof, 0); + rb_define_singleton_method(argf, "eof?", f_eof, 0); + rb_define_singleton_method(argf, "ungetc", f_ungetc, 1); + + rb_define_singleton_method(argf, "to_s", arg_filename, 0); + rb_define_singleton_method(argf, "filename", arg_filename, 0); + rb_define_singleton_method(argf, "file", arg_file, 0); + rb_define_singleton_method(argf, "skip", arg_skip, 0); + rb_define_singleton_method(argf, "close", arg_close, 0); + rb_define_singleton_method(argf, "closed?", arg_closed, 0); + + filename = str_new2("-"); + rb_define_readonly_variable("$FILENAME", &filename); + file = rb_stdin; + rb_global_variable(&file); + + rb_define_virtual_variable("$-i", opt_i_get, opt_i_set); + Init_File(); + +#if defined (NT) || defined(DJGPP) || defined(__CYGWIN32__) || defined(__human68k__) + atexit(pipe_atexit); +#endif +} @@ -0,0 +1,53 @@ +/************************************************ + + io.h - + + $Author$ + $Revision$ + $Date$ + created at: Fri Nov 12 16:47:09 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#ifndef IO_H +#define IO_H + +#include "sig.h" +#include <stdio.h> +#include <errno.h> + +typedef struct OpenFile { + FILE *f; /* stdio ptr for read/write */ + FILE *f2; /* additional ptr for rw pipes */ + int mode; /* mode flags */ + int pid; /* child's pid (for pipes) */ + int lineno; /* number of lines read */ + char *path; /* pathname for file */ + void (*finalize)(); /* finalize proc */ +} OpenFile; + +#define FMODE_READABLE 1 +#define FMODE_WRITABLE 2 +#define FMODE_READWRITE 3 +#define FMODE_BINMODE 4 +#define FMODE_SYNC 8 + +#define GetOpenFile(obj,fp) ((fp) = RFILE(obj)->fptr) + +#define MakeOpenFile(obj, fp) do {\ + fp = RFILE(obj)->fptr = ALLOC(OpenFile);\ + fp->f = fp->f2 = NULL;\ + fp->mode = 0;\ + fp->pid = 0;\ + fp->lineno = 0;\ + fp->path = NULL;\ + fp->finalize = 0;\ +} while (0) + +#define GetWriteFile(fptr) (((fptr)->f2) ? (fptr)->f2 : (fptr)->f) + +FILE *rb_fopen(); + +#endif diff --git a/lib/English.rb b/lib/English.rb new file mode 100644 index 0000000000..c7e13bebe6 --- /dev/null +++ b/lib/English.rb @@ -0,0 +1,28 @@ + +alias $ERROR_INFO $! +alias $ERROR_POSITION $@ +alias $LOADED_FEATURES $" +alias $FS $; +alias $FIELD_SEPARATOR $; +alias $OFS $, +alias $OUTPUT_FIELD_SEPARATOR $, +alias $RS $/ +alias $INPUT_RECORD_SEPARATOR $/ +alias $ORS $\ +alias $OUPUT_RECORD_SEPARATOR $\ +alias $INPUT_LINE_NUMBER $. +alias $NR $. +alias $LAST_READ_LINE $_ +alias $DEFAULT_OUTPUT $> +alias $DEFAULT_INPUT $< +alias $PID $$ +alias $PROCESS_ID $$ +alias $CHILD_STATUS $? +alias $LAST_MATCH_INFO $~ +alias $IGNORECASE $= +alias $PROGRAM_NAME $0 +alias $ARGV $* +alias $MATCH $& +alias $PREMATCH $` +alias $POSTMATCH $' +alias $LAST_PAREN_MATCH $+ diff --git a/lib/base64.rb b/lib/base64.rb new file mode 100644 index 0000000000..96208a634d --- /dev/null +++ b/lib/base64.rb @@ -0,0 +1,54 @@ +def decode64(str) + string = '' + for line in str.split("\n") + line.delete!('^A-Za-z0-9+/') # remove non-base64 chars + line.tr!('A-Za-z0-9+/', ' -_') # convert to uuencoded format + len = ["#{32 + line.length * 3 / 4}"].pack("c") + # compute length byte + string += "#{len}#{line}".unpack("u") # uudecode and concatenate + end + return string +end + +def j2e(str) + while str =~ /\033\$B([^\033]*)\033\(B/ + s = $1 + pre, post = $`, $' + s.gsub!(/./) { |ch| + (ch[0]|0x80).chr + } + str = pre + s + post + end +# str.gsub!(/\033\$B([^\033]*)\033\(B/) { +# $1.gsub!(/./) { |ch| +# (ch[0]|0x80).chr +# } +# } + str +end + +def decode_b(str) + str.gsub!(/=\?ISO-2022-JP\?B\?([!->@-~]+)\?=/i) { + decode64($1) + } + str.gsub!(/\n/, ' ') + str.gsub!(/\0/, '') + j2e(str) +end + +def encode64(bin) + encode = "" + pad = 0 + [bin].pack("u").each do |uu| + len = (2 + (uu[0] - 32)* 4) / 3 + encode << uu[1, len].tr('` -_', 'AA-Za-z0-9+/') + pad += uu.length - 2 - len + end + encode + "=" * (pad % 3) +end + +def b64encode(bin, len = 60) + encode64(bin).scan(/.{1,#{len}}/o) do + print $&, "\n" + end +end diff --git a/lib/cgi-lib.rb b/lib/cgi-lib.rb new file mode 100644 index 0000000000..afadbff3b6 --- /dev/null +++ b/lib/cgi-lib.rb @@ -0,0 +1,56 @@ +#!/usr/local/bin/ruby +# +# Get CGI String +# +# EXAMPLE: +# require "cgi-lib.rb" +# foo = CGI.new +# foo['field'] <== value of 'field' +# foo.keys <== array of fields +# foo.inputs <== hash of { <field> => <value> } + +class CGI + attr("inputs") + + def initialize + str = if ENV['REQUEST_METHOD'] == "GET" + ENV['QUERY_STRING'] + elsif ENV['REQUEST_METHOD'] == "POST" + $stdin.read ENV['CONTENT_LENGTH'].to_i + else + "" + end + arr = str.split(/&/) + @inputs = {} + arr.each do |x| + x.gsub!(/\+/, ' ') + key, val = x.split(/=/, 2) + val = "" unless val + + key.gsub!(/%(..)/) { [$1.hex].pack("c") } + val.gsub!(/%(..)/) { [$1.hex].pack("c") } + + @inputs[key] += "\0" if @inputs[key] + @inputs[key] += val + end + end + + def keys + @inputs.keys + end + + def [](key) + @inputs[key] + end + + def CGI.message(msg, title = "") + print "Content-type: text/html\n\n" + print "<html><head><title>" + print title + print "</title></head><body>\n" + print msg + print "</body></html>\n" + TRUE + end + +end diff --git a/lib/complex.rb b/lib/complex.rb new file mode 100644 index 0000000000..aa5d219d2f --- /dev/null +++ b/lib/complex.rb @@ -0,0 +1,490 @@ +# +# complex.rb - +# $Release Version: 0.5 $ +# $Revision: 1.1 $ +# $Date: 1996/11/11 04:25:19 $ +# by Keiju ISHITSUKA(SHL Japan Inc.) +# +# -- +# Usage: +# class Complex < Numeric +# +# Complex(x, y) --> x + yi +# y.im --> 0 + yi +# +# Complex::polar +# +# Complex::+ +# Complex::- +# Complex::* +# Complex::/ +# Complex::** +# Complex::% +# Complex::divmod +# Complex::abs +# Complex::abs2 +# Complex::arg +# Complex::polar +# Complex::conjugate +# Complex::<=> +# Complex::== +# Complex::to_i +# Complex::to_f +# Complex::to_r +# Complex::to_s +# +# Complex::I +# +# Numeric::im +# +# Math.sqrt +# Math.exp +# Math.cos +# Math.sin +# Math.tan +# Math.log +# Math.log10 +# Math.atan2 +# +# + +def Complex(a, b = 0) + if a.kind_of?(Complex) and b == 0 + a + elsif b == 0 and defined? Complex::Unify + a + else + Complex.new(a, b) + end +end + +class Complex < Numeric + + def Complex.generic?(other) + other.kind_of?(Integer) or + other.kind_of?(Float) or + (defined?(Rational) and other.kind_of?(Rational)) + end + + def Complex.polar(r, theta) + Complex(r*Math.cos(theta), r*Math.sin(theta)) + end + + def initialize(a, b = 0) + @real = a + @image = b + end + + def + (other) + if other.kind_of?(Complex) + re = @real + other.real + im = @image + other.image + Complex(re, im) + elsif Complex.generic?(other) + Complex(@real + other, @image) + else + x , y = a.coerce(self) + x + y + end + end + + def - (other) + if other.kind_of?(Complex) + re = @real - other.real + im = @image - other.image + Complex(re, im) + elsif Complex.generic?(other) + Complex(@real - other, @image) + else + x , y = a.coerce(self) + x - y + end + end + + def * (other) + if other.kind_of?(Complex) + re = @real*other.real - @image*other.image + im = @real*other.image + @image*other.real + Complex(re, im) + elsif Complex.generic?(other) + Complex(@real * other, @image * other) + else + x , y = a.coerce(self) + x * y + end + end + + def / (other) + if other.kind_of?(Complex) + self * other.conjugate / other.abs2 + elsif Complex.generic?(other) + Complex(@real / other, @image / other) + else + x , y = a.coerce(self) + x / y + end + end + + def ** (other) + if other == 0 + return Complex(1) + end + if other.kind_of?(Complex) + r, theta = polar + ore = other.real + oim = other.image + nr = Math.exp!(ore*Math.log!(r) - oim * theta) + ntheta = theta*ore + oim*Math.log!(r) + Complex.polar(nr, ntheta) + elsif other.kind_of?(Integer) + if other > 0 + x = self + z = x + n = other - 1 + while n != 0 + while (div, mod = n.divmod(2) + mod == 0) + x = Complex(x.real*x.real - x.image*x.image, 2*x.real*x.image) + n = div + end + z *= x + n -= 1 + end + z + else + if defined? Rational + (Rational(1) / self) ** -other + else + self ** Float(other) + end + end + elsif Complex.generic?(other) + r, theta = polar + Complex.polar(r.power!(other), theta * other) + else + x , y = a.coerce(self) + x / y + end + end + + def % (other) + if other.kind_of?(Complex) + Complex(@real % other.real, @image % other.image) + elsif Complex.generic?(other) + Complex(@real % other, @image % other) + else + x , y = a.coerce(self) + x % y + end + end + + def divmod(other) + if other.kind_of?(Complex) + rdiv, rmod = @real.divmod(other.real) + idiv, imod = @image.divmod(other.image) + return Complex(rdiv, idiv), Complex(rmod, rdiv) + elsif Complex.generic?(other) + Complex(@real.divmod(other), @image.divmod(other)) + else + x , y = a.coerce(self) + x.divmod(y) + end + end + + def abs + Math.sqrt!((@real*@real + @image*@image).to_f) + end + + def abs2 + @real*@real + @image*@image + end + + def arg + Math.atan2(@image.to_f, @real.to_f) + end + + def polar + return abs, arg + end + + def conjugate + Complex(@real, -@image) + end + + def <=> (other) + self.abs <=> other.abs + end + + 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 = a.coerce(self) + x == y + end + end + + def coerce(other) + if Complex.generic?(other) + return Complex.new(other), self + else + super + end + end + + def to_i + Complex(@real.to_i, @image.to_i) + end + + def to_f + Complex(@real.to_f, @image.to_f) + end + + def to_r + Complex(@real.to_r, @image.to_r) + end + + def denominator + @real.denominator.lcm(@image.denominator) + end + + def numerator + cd = denominator + Complex(@real.numerator*(cd/@real.denominator), + @image.numerator*(cd/@image.denominator)) + end + + def to_s + if @real != 0 + if defined?(Rational) and @image.kind_of?(Rational) and @image.denominator != 1 + if @image >= 0 + @real.to_s+"+("+@image.to_s+")i" + else + @real.to_s+"-("+(-@image).to_s+")i" + end + else + if @image >= 0 + @real.to_s+"+"+@image.to_s+"i" + else + @real.to_s+"-"+(-@image).to_s+"i" + end + end + else + if defined?(Rational) and @image.kind_of?(Rational) and @image.denominator != 1 + "("+@image.to_s+")i" + else + @image.to_s+"i" + end + end + end + + def hash + @real ^ @image + end + + I = Complex(0,1) + + 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 + + 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 cos! cos + alias sin! sin + alias tan! tan + alias log! log + alias log10! log10 + alias atan2! atan2 + + def sqrt(z) + if Complex.generic?(z) + if z >= 0 + sqrt!(z) + else + Complex(0,sqrt!(-z)) + end + else + z**Rational(1,2) + end + end + + def exp(z) + if Complex.generic?(z) + exp!(z) + else + Complex(exp!(z.real) * cos!(z.image), exp!(z.real) * sin!(z.image)) + end + end + + def cosh!(x) + (exp!(x) + exp!(-x))/2.0 + end + + def sinh!(x) + (exp!(x) - exp!(-x))/2.0 + end + + def cos(z) + if Complex.generic?(z) + cos!(z) + else + Complex(cos!(z.real)*cosh!(z.image), + sin!(z.real)*sinh!(z.image)) + end + end + + def sin(z) + if Complex.generic?(z) + sin!(z) + else + Complex(sin!(z.real)*cosh!(z.image), + -cos!(z.real)*sinh!(z.image)) + end + end + + def tan(z) + if Complex.generic?(z) + tan!(z) + else + sin(z)/cos(z) + end + end + + def log(z) + if Complex.generic?(z) and z >= 0 + log!(z) + else + r, theta = z.polar + Complex(log!(r.abs), theta) + end + end + + def log10(z) + if Complex.generic?(z) + log10!(z) + else + log(z)/log!(10) + end + end + + def atan2(x, y) + if Complex.generic?(x) and Complex.generic?(y) + atan2!(x, y) + else + fail "Not yet implemented." + end + end + + def atanh!(x) + log((1.0 + x.to_f) / ( 1.0 - x.to_f)) / 2.0 + end + + def atan(z) + if Complex.generic?(z) + atan2!(z, 1) + elsif z.image == 0 + atan2(z.real,1) + 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))) + end + end + + module_function :sqrt + module_function :sqrt! + module_function :exp! + module_function :exp + module_function :cosh! + module_function :cos! + module_function :cos + 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 :atan2! + module_function :atan2 +# module_function :atan! + module_function :atan + module_function :atanh! + +end + + diff --git a/lib/date.rb b/lib/date.rb new file mode 100644 index 0000000000..998c2e8152 --- /dev/null +++ b/lib/date.rb @@ -0,0 +1,227 @@ +# +# Date.rb - +# $Release Version: $ +# $Revision: 1.2 $ +# $Date: 1997/02/14 11:05:29 $ +# by Yasuo OHBA(SHL Japan Inc. Technology Dept.) +# +# -- +# +# September 1752 +# S M Tu W Th F S +# 1 2 14 15 16 +# 17 18 19 20 21 22 23 +# 24 25 26 27 28 29 30 +# + +class Date + include Comparable + + def initialize(y = 1, m = 1, d = 1) + if y.kind_of?(String) && y.size == 8 + @year = y[0,4].to_i + @month = y[4,2].to_i + @day = y[6,2].to_i + else + if m.kind_of?(String) + ml = {"jan"=>1, "feb"=>2, "mar"=>3, "apr"=>4, "may"=>5, "jun"=>6, "jul"=>7, "aug"=>8, "sep"=>9, "oct"=>10, "nov"=>11, "dec"=>12} + m = ml[m.downcase] + if m.nil? + raise ArgumentError, "Wrong argument. (month)" + end + end + @year = y.to_i + @month = m.to_i + @day = d.to_i + end + _check_date + return self + end + + def year + return @year + end + + def month + return @month + end + + def day + return @day + end + + def period + return Date.period!(@year, @month, @day) + end + + def day_of_week + dl = Date.daylist(@year) + d = Date.jan1!(@year) + for m in 1..(@month - 1) + d += dl[m] + end + d += @day - 1 + if @year == 1752 && @month == 9 && @day >= 14 + d -= (14 - 3) + end + return (d % 7) + end + + Weektag = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] + def name_of_week + return Weektag[self.day_of_week] + end + + def +(o) + if o.kind_of?(Numeric) + d = Integer(self.period + o) + elsif o.kind_of?(Date) + d = self.period + o.period + else + raise TypeError, "Illegal type. (Integer or Date)" + end + return Date.at(d) + end + + def -(o) + if o.kind_of?(Numeric) + d = Integer(self.period - o) + elsif o.kind_of?(Date) + return Integer(self.period - o.period) + else + raise TypeError, "Illegal type. (Integer or Date)" + end + if d <= 0 + raise ArgumentError, "argument out of range. (self > other)" + end + return Date.at(d) + end + + def <=>(o) + if o.kind_of?(Integer) + d = o + elsif o.kind_of?(Date) + d = o.period + else + raise TypeError, "Illegal type. (Integer or Date)" + end + return self.period <=> d + end + + def eql?(o) + self == o + end + + def hash + return @year ^ @month ^ @day + end + + def leapyear? + if Date.leapyear(@year) == 1 + return FALSE + else + return TRUE + end + end + + def _check_date + m = Date.daylist(@year) + if @month < 1 || @month > 12 + raise ArgumentError, "argument(month) out of range." + return nil + end + if @year == 1752 && @month == 9 + if @day >= 3 && @day <= 13 + raise ArgumentError, "argument(1752/09/3-13) out of range." + return nil + end + d = 30 + else + d = m[@month] + end + if @day < 1 || @day > d + raise ArgumentError, "argument(day) out of range." + return nil + end + return self + end + + private :_check_date +end + +def Date.at(d) + if d.kind_of? Time + return Date.new(1900+d.year, d.mon+1, d.mday) + end + if d.kind_of? Date + return Date.at(d.period) + end + mm = 1 + yy = (d / 366.0).to_i + if yy != 0 + dd = d - (Date.period!(yy, 1, 1) - 1) + else + dd = d + yy = 1 + end + dl = Date.daylist(yy) + while dd > dl[mm] + if dd > dl[0] + dd -= dl[0] + yy += 1 + dl = Date.daylist(yy) + else + dd -= dl[mm] + mm += 1 + end + end + if yy == 1752 && mm == 9 && dd >= 3 && dd <= 19 + dd += (14 - 3) # 1752/09/03-19 -> 1752/09/14-30 + end + + return Date.new(yy, mm, dd) +end + +def Date.period!(y, m, d) + p = d + dl = Date.daylist(y) + for mm in 1..(m - 1) + p += dl[mm] + end + p += (y - 1) * 365 + ((y - 1) / 4.0).to_i + if (y - 1) > 1752 + p -= ((y - 1 - 1752) / 100.0).to_i + p += ((y - 1 - 1752) / 400.0).to_i + p -= (14 - 3) + elsif y == 1752 && m == 9 && d >= 14 && d <= 30 + p -= (14 - 3) + end + return p +end + +def Date.leapyear(yy) + return ((Date.jan1!(yy + 1) + 7 - Date.jan1!(yy)) % 7) +end + +def Date.daylist(yy) + case (Date.leapyear(yy)) + when 1 # non-leapyear + return [365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + when 2 # leapyear + return [366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + else # 1752 + return [355, 31, 29, 31, 30, 31, 30, 31, 31, 19, 31, 30, 31] + end +end + +def Date.jan1!(y) + d = 4 + y + (y + 3) / 4 + if y > 1800 + d -= (y - 1701) / 100 + d += (y - 1601) / 400 + end + if y > 1752 + d += 3 + end + return (d % 7) +end diff --git a/lib/debug.rb b/lib/debug.rb new file mode 100644 index 0000000000..432c7b4d19 --- /dev/null +++ b/lib/debug.rb @@ -0,0 +1,262 @@ + +class DEBUGGER__ + trap("INT") { DEBUGGER__::CONTEXT.interrupt } + $DEBUG = TRUE + def initialize + @break_points = [] + @stop_next = 1 + @frames = [nil] + @frame_pos = nil + @last_file = nil + @scripts = {} + end + + def interrupt + @stop_next = 1 + end + + def debug_eval(str, binding) + begin + val = eval(str, binding) + val + rescue + at = caller(0) + printf "%s:%s\n", at.shift, $! + for i in at + break if i =~ /`debug_(eval|command)'$/ #` + printf "\tfrom %s\n", i + end + end + end + + def debug_command(file, line, id, binding) + if (ENV['EMACS'] == 't') + printf "\032\032%s:%d:\n", file, line + else + printf "%s:%d:%s", file, line, line_at(file, line) + end + @frames[-1] = binding + STDOUT.print "(rdb:-) " + STDOUT.flush + while input = STDIN.gets + input.chop! + case input + when /^b(reak)?\s+(([^:\n]+:)?.+)/ + pos = $2 + if pos.index ":" + file, pos = pos.split(":") + end + file = File.basename(file) + if pos =~ /^\d+$/ + pname = pos + pos = Integer(pos) + else + pname = pos = pos.intern.id2name + end + printf "Set breakpoint %d at %s:%s\n", @break_points.size, file, pname + @break_points.push [file, pos] + when /^b(reak)?$/, /^info b(reak)?$/ + n = 0 + for f, p in @break_points + printf "%d %s:%s\n", n, f, p + n += 1 + end + when /^del(ete)?(\s+(\d+))?$/ + pos = $3 + unless pos + STDOUT.print "clear all breakpoints? (y/n) " + STDOUT.flush + input = STDIN.gets.chop! + if input == "y" + for n in @break_points.indexes + @break_points[n] = nil + end + end + else + pos = Integer(pos) + if @break_points[pos] + bp = @break_points[pos] + printf "Clear breakpoint %d at %s:%s\n", pos, bp[0], bp[1] + @break_points[pos] = nil + else + printf "Breakpoint %d is not defined\n", pos + end + end + when /^c(ont)?$/ + return + when /^s(tep)?\s*(\d+)?$/ + if $1 + lev = Integer($1) + else + lev = 1 + end + @stop_next = lev + return + when /^n(ext)?\s*(\d+)?$/ + if $1 + lev = Integer($1) + else + lev = 1 + end + @stop_next = lev + @no_step = @frames.size + return + when /^up\s*(\d+)?$/ + if $1 + lev = Integer($1) + else + lev = 1 + end + unless @frame_pos + @frame_pos = @frames.size - 1 + end + @frame_pos -= lev + if @frame_pos < 0 + STDOUT.print "at toplevel\n" + @frame_pos = 0 + else + binding = @frames[@frame_pos] + end + when /^down\s*(\d+)??$/ + if $1 + lev = Integer($1) + else + lev = 1 + end + if lev >= @frames.size or @frame_pos and @frame_pos+lev >= @frames.size + STDOUT.print "at stack bottom\n" + @frame_pos = nil + else + @frame_pos += lev + binding = @frames[@frame_pos] + end + when /^fin(ish)?$/ + @finish_pos = @frames.size + return + when /^q(uit)?$/ + STDOUT.print "really quit? (y/n) " + STDOUT.flush + input = STDIN.gets.chop! + exit if input == "y" + when /^where$/ + at = caller(4) + for i in at + printf " %s\n", i + end + when /^l(ist)?(\s+(.*))?$/ + if $3 + b, e = $3.split(/[-,]/) + b = Integer(b)-1 + if e + e = Integer(e)-1 + else + e = b + 10 + end + end + unless b + b = line - 1 + e = line + 9 + end + p [b,e] + line_at(file, line) + if lines = @scripts[file] and lines != TRUE + n = b+1 + for l in lines[b..e] + printf "%4d %s", n, l + n += 1 + end + else + printf "no sourcefile available for %s\n", file + end + when /^p\s+/ + p debug_eval($', binding) + else + v = debug_eval(input, binding) + p v unless v == nil + end + STDOUT.print "(rdb:-) " + STDOUT.flush + end + end + + def line_at(file, line) + lines = @scripts[file] + if lines + return "\n" if lines == TRUE + line = lines[line-1] + return "\n" unless line + return line + end + begin + f = open(file) + lines = @scripts[file] = f.readlines + rescue + @scripts[file] = TRUE + return "\n" + end + line = lines[line-1] + return "\n" unless line + return line + end + + def debug_funcname(id) + if id == 0 + "toplevel" + else + id.id2name + end + end + + def check_break_points(file, pos, binding, id) + file = File.basename(file) + if @break_points.include? [file, pos] + index = @break_points.index([file, pos]) + printf "Breakpoint %d, %s at %s:%s\n", + index, debug_funcname(id), file, pos + return TRUE + end + return FALSE + end + + def trace_func(event, file, line, id, binding) + if event == 'line' + if @no_step == nil or @no_step >= @frames.size + @stop_next -= 1 + end + if @stop_next == 0 + if [file, line] == @last + @stop_next = 1 + else + @no_step = nil + debug_command(file, line, id, binding) + @last = [file, line] + end + end + if check_break_points(file, line, binding, id) + debug_command(file, line, id, binding) + end + end + if event == 'call' + @frames.push binding + if check_break_points(file, id.id2name, binding, id) + debug_command(file, line, id, binding) + end + end + if event == 'class' + @frames.push binding + end + if event == 'return' or event == 'end' + if @finish_pos == @frames.size + @stop_next = 1 + end + @frames.pop + end + @last_file = file + end + + CONTEXT = new +end + +set_trace_func proc{|event, file, line, id, binding| + DEBUGGER__::CONTEXT.trace_func event, file, line, id, binding +} diff --git a/lib/e2mmap.rb b/lib/e2mmap.rb new file mode 100644 index 0000000000..d10657bbad --- /dev/null +++ b/lib/e2mmap.rb @@ -0,0 +1,94 @@ +# +# e2mmap.rb - for ruby 1.1 +# $Release Version: 1.1$ +# $Revision: 1.4 $ +# $Date: 1997/08/18 07:12:12 $ +# by Keiju ISHITSUKA +# +# -- +# +# +if VERSION < "1.1" + require "e2mmap1_0.rb" +else + + module Exception2MessageMapper + RCS_ID='-$Header: /home/keiju/var/src/var.lib/ruby/RCS/e2mmap.rb,v 1.4 1997/08/18 07:12:12 keiju Exp keiju $-' + + E2MM = Exception2MessageMapper + + def E2MM.extend_object(cl) + super + cl.bind(self) + end + + # 以前との互換性のために残してある. + def E2MM.extend_to(b) + c = eval("self", b) + c.extend(self) + end + +# public :fail + # alias e2mm_fail fail + + def fail(err = nil, *rest) + Exception2MessageMapper.fail Exception2MessageMapper::ErrNotRegisteredException, err.to_s + end + + def bind(cl) + self.module_eval %q^ + E2MM_ErrorMSG = {} + # fail(err, *rest) + # err: 例外 + # rest: メッセージに渡すパラメータ + # + def self.fail(err = nil, *rest) + $@ = caller(0) if $@.nil? + $@.shift + if form = E2MM_ErrorMSG[err] + $! = err.new(sprintf(form, *rest)) + # e2mm_fail() + raise() +# elsif self == Exception2MessageMapper +# fail Exception2MessageMapper::ErrNotRegisteredException, err.to_s + else +# print "super\n" + super + end + end + class << self + public :fail + end + + # def_exception(c, m) + # c: exception + # m: message_form + # 例外cのメッセージをmとする. + # + def self.def_e2message(c, m) + E2MM_ErrorMSG[c] = m + end + + # def_exception(c, m) + # n: exception_name + # m: message_form + # s: 例外スーパークラス(デフォルト: Exception) + # 例外名``c''をもつ例外を定義し, そのメッセージをmとする. + # + #def def_exception(n, m) + def self.def_exception(n, m, s = Exception) + n = n.id2name if n.kind_of?(Fixnum) + e = Class.new(s) + const_set(n, e) + E2MM_ErrorMSG[e] = m + # const_get(:E2MM_ErrorMSG)[e] = m + end + ^ + end + + extend E2MM + def_exception(:ErrNotClassOrModule, "Not Class or Module") + def_exception(:ErrNotRegisteredException, "not registerd exception(%s)") + end +end + diff --git a/lib/e2mmap1_0.rb b/lib/e2mmap1_0.rb new file mode 100644 index 0000000000..d245dec975 --- /dev/null +++ b/lib/e2mmap1_0.rb @@ -0,0 +1,71 @@ +# +# e2mmap.rb - +# $Release Version: 1.0$ +# $Revision$ +# $Date$ +# by Keiju ISHITSUKA +# +# -- +# +# + +module Exception2MessageMapper + RCS_ID='-$Header$-' + E2MM = Exception2MessageMapper + + def E2MM.extend_to(b) + c = eval("self", b) + c.extend(self) + c.bind(b) + end + + def bind(b) + eval " + @binding = binding + E2MM_ErrorMSG = Hash.new + + # fail(err, *rest) + # err: 例外 + # rest: メッセージに渡すパラメータ + # + def fail!(*rest) + super + end + + def fail(err, *rest) + $! = err.new(sprintf(E2MM_ErrorMSG[err], *rest)) + super() + end + + public :fail + # def_exception(c, m) + # c: exception + # m: message_form + # 例外cのメッセージをmとする. + # + def def_e2message(c, m) + E2MM_ErrorMSG[c] = m + end + + # def_exception(c, m) + # c: exception_name + # m: message_form + # s: 例外スーパークラス(デフォルト: Exception) + # 例外名``c''をもつ例外を定義し, そのメッセージをmとする. + # + def def_exception(c, m) + + c = c.id2name if c.kind_of?(Fixnum) + eval \"class \#{c} < Exception + end + E2MM_ErrorMSG[\#{c}] = '\#{m}' + \", @binding + end +", b + + end + + E2MM.extend_to(binding) + def_exception("ErrNotClassOrModule", "Not Class or Module") +end + diff --git a/lib/finalize.rb b/lib/finalize.rb new file mode 100644 index 0000000000..e934753e19 --- /dev/null +++ b/lib/finalize.rb @@ -0,0 +1,205 @@ +# +# finalize.rb - +# $Release Version: $ +# $Revision: 1.2 $ +# $Date: 1997/07/25 02:43:00 $ +# by Keiju ISHITSUKA(SHL Japan Inc.) +# +# -- +# +# Usage: +# +# add(obj, dependant, method = :finalize, *opt) +# add_dependency(obj, dependant, method = :finalize, *opt) +# 依存関係 R_method(obj, dependant) の追加 +# +# delete(obj_or_id, dependant, method = :finalize) +# delete_dependency(obj_or_id, dependant, method = :finalize) +# 依存関係 R_method(obj, dependant) の削除 +# delete_all_dependency(obj_or_id, dependant) +# 依存関係 R_*(obj, dependant) の削除 +# delete_by_dependant(dependant, method = :finalize) +# 依存関係 R_method(*, dependant) の削除 +# delete_all_by_dependant(dependant) +# 依存関係 R_*(*, dependant) の削除 +# delete_all +# 全ての依存関係の削除. +# +# finalize(obj_or_id, dependant, method = :finalize) +# finalize_dependency(obj_or_id, dependant, method = :finalize) +# 依存関連 R_method(obj, dependtant) で結ばれるdependantを +# finalizeする. +# finalize_all_dependency(obj_or_id, dependant) +# 依存関連 R_*(obj, dependtant) で結ばれるdependantをfinalizeする. +# finalize_by_dependant(dependant, method = :finalize) +# 依存関連 R_method(*, dependtant) で結ばれるdependantをfinalizeする. +# fainalize_all_by_dependant(dependant) +# 依存関連 R_*(*, dependtant) で結ばれるdependantをfinalizeする. +# finalize_all +# Finalizerに登録される全てのdependantをfinalizeする +# +# safe{..} +# gc時にFinalizerが起動するのを止める. +# +# + +module Finalizer + RCS_ID='-$Header: /home/keiju/var/src/var.lib/ruby/RCS/finalize.rb,v 1.2 1997/07/25 02:43:00 keiju Exp keiju $-' + + # @dependency: {id => [[dependant, method, *opt], ...], ...} + + # 依存関係 R_method(obj, dependant) の追加 + def add_dependency(obj, dependant, method = :finalize, *opt) + ObjectSpace.call_finalizer(obj) + method = method.id unless method.kind_of?(Fixnum) + assoc = [dependant, method].concat(opt) + if dep = @dependency[obj.id] + dep.push assoc + else + @dependency[obj.id] = [assoc] + end + end + alias add add_dependency + + # 依存関係 R_method(obj, dependant) の削除 + def delete_dependency(id, dependant, method = :finalize) + id = id.id unless id.kind_of?(Fixnum) + method = method.id unless method.kind_of?(Fixnum) + for assoc in @dependency[id] + assoc.delete_if do + |d, m, *o| + d == dependant && m == method + end + @dependency.delete(id) if assoc.empty? + end + end + alias delete delete_dependency + + # 依存関係 R_*(obj, dependant) の削除 + def delete_all_dependency(id, dependant) + id = id.id unless id.kind_of?(Fixnum) + method = method.id unless method.kind_of?(Fixnum) + for assoc in @dependency[id] + assoc.delete_if do + |d, m, *o| + d == dependant + end + @dependency.delete(id) if assoc.empty? + end + end + + # 依存関係 R_method(*, dependant) の削除 + def delete_by_dependant(dependant, method = :finalize) + method = method.id unless method.kind_of?(Fixnum) + for id in @dependency.keys + delete(id, dependant, method) + end + end + + # 依存関係 R_*(*, dependant) の削除 + def delete_all_by_dependant(dependant) + for id in @dependency.keys + delete_all_dependency(id, dependant) + end + end + + # 依存関連 R_method(obj, dependtant) で結ばれるdependantをfinalizeす + # る. + def finalize_dependency(id, dependant, method = :finalize) + id = id.id unless id.kind_of?(Fixnum) + method = method.id unless method.kind_of?(Fixnum) + for assocs in @dependency[id] + assocs.delete_if do + |d, m, *o| + d.send(m, *o) if ret = d == dependant && m == method + ret + end + @dependency.delete(id) if assoc.empty? + end + end + alias finalize finalize_dependency + + # 依存関連 R_*(obj, dependtant) で結ばれるdependantをfinalizeする. + def finalize_all_dependency(id, dependant) + id = id.id unless id.kind_of?(Fixnum) + method = method.id unless method.kind_of?(Fixnum) + for assoc in @dependency[id] + assoc.delete_if do + |d, m, *o| + d.send(m, *o) if ret = d == dependant + end + @dependency.delete(id) if assoc.empty? + end + end + + # 依存関連 R_method(*, dependtant) で結ばれるdependantをfinalizeする. + def finalize_by_dependant(dependant, method = :finalize) + method = method.id unless method.kind_of?(Fixnum) + for id in @dependency.keys + finalize(id, dependant, method) + end + end + + # 依存関連 R_*(*, dependtant) で結ばれるdependantをfinalizeする. + def fainalize_all_by_dependant(dependant) + for id in @dependency.keys + finalize_all_dependency(id, dependant) + end + end + + # Finalizerに登録されている全てのdependantをfinalizeする + def finalize_all + for id, assocs in @dependency + for dependant, method, *opt in assocs + dependant.send(method, id, *opt) + end + assocs.clear + end + end + + # finalize_* を安全に呼び出すためのイテレータ + def safe + old_status = Thread.critical + Thread.critical = TRUE + ObjectSpace.remove_finalizer(@proc) + yield + ObjectSpace.add_finalizer(@proc) + Thread.critical = old_status + end + + # ObjectSpace#add_finalizerへの登録関数 + def final_of(id) + if assocs = @dependency.delete(id) + for dependant, method, *opt in assocs + dependant.send(method, id, *opt) + end + end + end + + @dependency = Hash.new + @proc = proc{|id| final_of(id)} + ObjectSpace.add_finalizer(@proc) + + module_function :add + module_function :add_dependency + + module_function :delete + module_function :delete_dependency + module_function :delete_all_dependency + module_function :delete_by_dependant + module_function :delete_all_by_dependant + + module_function :finalize + module_function :finalize_dependency + module_function :finalize_all_dependency + module_function :finalize_by_dependant + module_function :fainalize_all_by_dependant + module_function :finalize_all + + module_function :safe + + module_function :final_of + private_class_method :final_of + +end + diff --git a/lib/find.rb b/lib/find.rb new file mode 100644 index 0000000000..5ecc54329c --- /dev/null +++ b/lib/find.rb @@ -0,0 +1,39 @@ +# Usage: +# require "find.rb" +# +# Find.find('/foo','/bar') {|f| ...} +# or +# include Find +# find('/foo','/bar') {|f| ...} +# + +module Find + def find(*path) + while file = path.shift + catch(:prune) { + yield file + if File.directory? file and not File.symlink? file then + d = Dir.open(file) + begin + for f in d + next if f =~ /^\.\.?$/ + if file == "/" then + f = "/" + f + else + f = file + "/" + f + end + path.unshift f + end + ensure + d.close + end + end + } + end + end + + def prune + throw :prune + end + module_function :find, :prune +end diff --git a/lib/ftplib.rb b/lib/ftplib.rb new file mode 100644 index 0000000000..34ee2f8d62 --- /dev/null +++ b/lib/ftplib.rb @@ -0,0 +1,617 @@ +### ftplib.rb -*- Mode: ruby; tab-width: 8; -*- + +## $Revision: 1.5 $ +## $Date: 1997/09/16 08:03:31 $ +## by maeda shugo <shugo@po.aianet.ne.jp> + +### Code: + +require "socket" +require "sync" if defined? Thread + +class FTPError < Exception; end +class FTPReplyError < FTPError; end +class FTPTempError < FTPError; end +class FTPPermError < FTPError; end +class FTPProtoError < FTPError; end + +class FTP + + RCS_ID = '$Id: ftplib.rb,v 1.5 1997/09/16 08:03:31 shugo Exp $' + + FTP_PORT = 21 + CRLF = "\r\n" + + attr :passive, TRUE + attr :return_code, TRUE + attr :debug_mode, TRUE + attr :welcome + attr :lastresp + + THREAD_SAFE = defined?(Thread) != FALSE + + if THREAD_SAFE + def synchronize(mode = :EX) + if @sync + @sync.synchronize(mode) do + yield + end + end + end + + def sock_synchronize(mode = :EX) + if @sock + @sock.synchronize(mode) do + yield + end + end + end + else + def synchronize(mode = :EX) + yield + end + + def sock_synchronize(mode = :EX) + yield + end + end + private :sock_synchronize + + def FTP.open(host, user = nil, passwd = nil, acct = nil) + new(host, user, passwd, acct) + end + + def initialize(host = nil, user = nil, + passwd = nil, acct = nil) + if THREAD_SAFE + @sync = Sync.new + end + @passive = FALSE + @return_code = "\n" + @debug_mode = FALSE + if host + connect(host) + if user + login(user, passwd, acct) + end + end + end + + def open_socket(host, port) + if defined? SOCKSsocket and ENV["SOCKS_SERVER"] + @passive = TRUE + SOCKSsocket.open(host, port) + else + TCPsocket.open(host, port) + end + end + private :open_socket + + def connect(host, port = FTP_PORT) + if @debug_mode + print "connect: ", host, ", ", port, "\n" + end + synchronize do + @sock = open_socket(host, port) + if THREAD_SAFE + @sock.extend Sync_m + end + voidresp + end + end + + def sanitize(s) + if s =~ /^PASS /i + s[0, 5] + "*" * (s.length - 5) + else + s + end + end + private :sanitize + + def putline(line) + if @debug_mode + print "put: ", sanitize(line), "\n" + end + line = line + CRLF + @sock.write(line) + end + private :putline + + def getline + line = @sock.readline # if get EOF, raise EOFError + if line[-2, 2] == CRLF + line = line[0 .. -3] + elsif line[-1] == ?\r or + line[-1] == ?\n + line = line[0 .. -2] + end + if @debug_mode + print "get: ", sanitize(line), "\n" + end + line + end + private :getline + + def getmultiline + line = getline + buff = line + if line[3] == ?- + code = line[0, 3] + begin + line = getline + buff << "\n" << line + end until line[0, 3] == code and line[3] != ?- + end + buff << "\n" + end + 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 + else + raise FTPProtoError, resp + end + end + private :getresp + + def voidresp + resp = getresp + if resp[0] != ?2 + raise FTPReplyError, resp + end + end + private :voidresp + + def sendcmd(cmd) + synchronize do + sock_synchronize do + putline(cmd) + getresp + end + end + end + + def voidcmd(cmd) + synchronize do + sock_synchronize do + putline(cmd) + voidresp + end + end + nil + end + + def sendport(host, port) + hbytes = host.split(".") + pbytes = [port / 256, port % 256] + bytes = hbytes + pbytes + cmd = "PORT " + bytes.join(",") + voidcmd(cmd) + end + private :sendport + + def makeport + sock = TCPserver.open(0) + port = sock.addr[1] + host = TCPsocket.getaddress(@sock.addr[2]) + resp = sendport(host, port) + sock + end + private :makeport + + def transfercmd(cmd) + if @passive + host, port = parse227(sendcmd("PASV")) + conn = open_socket(host, port) + resp = sendcmd(cmd) + if resp[0] != ?1 + raise FTPReplyError, resp + end + else + sock = makeport + resp = sendcmd(cmd) + if resp[0] != ?1 + raise FTPReplyError, resp + end + conn = sock.accept + end + conn + end + private :transfercmd + + def getaddress + thishost = Socket.gethostname + if not thishost.index(".") + thishost = Socket.gethostbyname(thishost)[0] + end + if ENV.has_key?("LOGNAME") + realuser = ENV["LOGNAME"] + elsif ENV.has_key?("USER") + realuser = ENV["USER"] + else + realuser = "anonymous" + end + realuser + "@" + thishost + end + private :getaddress + + def login(user = "anonymous", passwd = nil, acct = nil) + if user == "anonymous" and passwd == nil + passwd = getaddress + end + + resp = "" + synchronize do + resp = sendcmd('USER ' + user) + if resp[0] == ?3 + resp = sendcmd('PASS ' + passwd) + end + if resp[0] == ?3 + resp = sendcmd('ACCT ' + acct) + end + end + if resp[0] != ?2 + raise FTPReplyError, resp + end + @welcome = resp + end + + def retrbinary(cmd, blocksize, callback = Proc.new) + synchronize do + voidcmd("TYPE I") + conn = transfercmd(cmd) + while TRUE + data = conn.read(blocksize) + break if data == nil + callback.call(data) + end + conn.close + voidresp + end + end + + def retrlines(cmd, callback = nil) + if iterator? + callback = Proc.new + elsif not callback.is_a?(Proc) + callback = Proc.new {|line| print line, "\n"} + end + synchronize do + voidcmd("TYPE A") + conn = transfercmd(cmd) + while TRUE + line = conn.gets + break if line == nil + if line[-2, 2] == CRLF + line = line[0 .. -3] + elsif line[-1] == ?\n + line = line[0 .. -2] + end + callback.call(line) + end + conn.close + voidresp + end + end + + def storbinary(cmd, file, blocksize, callback = nil) + if iterator? + callback = Proc.new + end + use_callback = callback.is_a?(Proc) + synchronize do + voidcmd("TYPE I") + conn = transfercmd(cmd) + while TRUE + buf = file.read(blocksize) + break if buf == nil + conn.write(buf) + if use_callback + callback.call(buf) + end + end + conn.close + voidresp + end + end + + def storlines(cmd, file, callback = nil) + if iterator? + callback = Proc.new + end + use_callback = callback.is_a?(Proc) + synchronize do + voidcmd("TYPE A") + conn = transfercmd(cmd) + while TRUE + buf = file.gets + break if buf == nil + if buf[-2, 2] != CRLF + if buf[-1] == ?\r or + buf[-1] == ?\n + buf = buf[0 .. -2] + end + buf = buf + CRLF + end + conn.write(buf) + if use_callback + callback.call(buf) + end + end + conn.close + voidresp + end + end + + def getbinaryfile(remotefile, localfile, + blocksize, callback = nil) + if iterator? + callback = Proc.new + end + use_callback = callback.is_a?(Proc) + f = open(localfile, "w") + begin + f.binmode + retrbinary("RETR " + remotefile, blocksize) do |data| + f.write(data) + if use_callback + callback.call(data) + end + end + ensure + f.close + end + end + + def gettextfile(remotefile, localfile, callback = nil) + if iterator? + callback = Proc.new + end + use_callback = callback.is_a?(Proc) + f = open(localfile, "w") + begin + retrlines("RETR " + remotefile) do |line| + line = line + @return_code + f.write(line) + if use_callback + callback.call(line) + end + end + ensure + f.close + end + end + + def putbinaryfile(localfile, remotefile, + blocksize, callback = nil) + if iterator? + callback = Proc.new + end + use_callback = callback.is_a?(Proc) + f = open(localfile) + begin + f.binmode + storbinary("STOR " + remotefile, f, blocksize) do |data| + if use_callback + callback.call(data) + end + end + ensure + f.close + end + end + + def puttextfile(localfile, remotefile, callback = nil) + if iterator? + callback = Proc.new + end + use_callback = callback.is_a?(Proc) + f = open(localfile) + begin + storlines("STOR " + remotefile, f) do |line| + if use_callback + callback.call(line) + end + end + ensure + f.close + end + end + + def acct(account) + cmd = "ACCT " + account + voidcmd(cmd) + end + + def nlst(dir = nil) + cmd = "NLST" + if dir + cmd = cmd + " " + dir + end + files = [] + retrlines(cmd) do |line| + files.push(line) + end + files + end + + def list(*args) + cmd = "LIST" + if iterator? + callback = Proc.new + elsif args[-1].is_a?(Proc) + callback = args.pop + else + callback = nil + end + args.each do |arg| + cmd = cmd + " " + arg + end + retrlines(cmd, callback) + end + alias ls list + alias dir list + + def rename(fromname, toname) + resp = sendcmd("RNFR " + fromname) + if resp[0] != ?3 + raise FTPReplyError, resp + end + voidcmd("RNTO " + toname) + end + + def delete(filename) + resp = sendcmd("DELE " + filename) + if resp[0, 3] == "250" + return + elsif resp[0] == ?5 + raise FTPPermError, resp + else + raise FTPReplyError, resp + end + end + + def chdir(dirname) + if dirname == ".." + begin + voidcmd("CDUP") + return + rescue FTPPermError + if $![0, 3] != "500" + raise FTPPermError, $! + end + end + end + cmd = "CWD " + dirname + voidcmd(cmd) + end + + def size(filename) + resp = sendcmd("SIZE " + filename) + if resp[0, 3] == "213" + return Integer(resp[3 .. -1].strip) + end + end + + def mkdir(dirname) + resp = sendcmd("MKD " + dirname) + return parse257(resp) + end + + def rmdir(dirname) + voidcmd("RMD " + dirname) + end + + def pwd + resp = sendcmd("PWD") + return parse257(resp) + end + alias getdir pwd + + def system + resp = sendcmd("SYST") + if resp[0, 3] != "215" + raise FTPReplyError, resp + end + return resp[4 .. -1] + end + + def abort + line = "ABOR" + CRLF + resp = "" + sock_synchronize do + print "put: ABOR\n" if @debug_mode + @sock.send(line, Socket::MSG_OOB) + resp = getmultiline + end + unless ["426", "226", "225"].include?(resp[0, 3]) + raise FTPProtoError, resp + end + resp + end + + def status + line = "STAT" + CRLF + resp = "" + sock_synchronize do + print "put: STAT\n" if @debug_mode + @sock.send(line, Socket::MSG_OOB) + resp = getresp + end + resp + end + + def help(arg = nil) + cmd = "HELP" + if arg + cmd = cmd + " " + arg + end + sendcmd(cmd) + end + + def quit + voidcmd("QUIT") + end + + def close + @sock.close if @sock and not @sock.closed? + end + + def closed? + @sock == nil or @sock.closed? + end + + def parse227(resp) + if resp[0, 3] != "227" + raise FTPReplyError, resp + end + left = resp.index("(") + right = resp.index(")") + if left == nil or right == nil + raise FTPProtoError, resp + end + numbers = resp[left + 1 .. right - 1].split(",") + if numbers.length != 6 + raise FTPProtoError, resp + end + host = numbers[0, 4].join(".") + port = (Integer(numbers[4]) << 8) + Integer(numbers[5]) + return host, port + end + private :parse227 + + def parse257(resp) + if resp[0, 3] != "257" + raise FTPReplyError, resp + end + if resp[3, 2] != ' "' + return "" + end + dirname = "" + i = 5 + n = resp.length + while i < n + c = resp[i, 1] + i = i + 1 + if c == '"' + if i > n or resp[i, 1] != '"' + break + end + i = i + 1 + end + dirname = dirname + c + end + return dirname + end + private :parse257 +end diff --git a/lib/getopts.rb b/lib/getopts.rb new file mode 100644 index 0000000000..6929f7e4fd --- /dev/null +++ b/lib/getopts.rb @@ -0,0 +1,142 @@ +#!/usr/local/bin/ruby +# +# getopts.rb - +# $Release Version: $ +# $Revision$ +# $Date$ +# by Yasuo OHBA(SHL Japan Inc. Technology Dept.) +# +# -- +# +# +# + +$RCS_ID="$Header$" + +def isSingle(lopt) + if lopt.index(":") + if lopt.split(":")[0].length == 1 + return TRUE + end + end + return nil +end + +def getOptionName(lopt) + return lopt.split(":")[0] +end + +def getDefaultOption(lopt) + od = lopt.split(":")[1] + if od + return od + end + return nil +end + +def setOption(name, value) + eval("$OPT_" + name + " = " + 'value') +end + +def setDefaultOption(lopt) + d = getDefaultOption(lopt) + if d + setOption(getOptionName(lopt), d) + end +end + +def setNewArgv(newargv) + ARGV.clear + for na in newargv + ARGV << na + end +end + + +def getopts(single_opts, *options) + if options + single_colon = "" + long_opts = [] + sc = 0 + for o in options + setDefaultOption(o) + if isSingle(o) + single_colon[sc, 0] = getOptionName(o) + sc += 1 + else + long_opts.push(o) + end + end + end + + opts = {} + count = 0 + newargv = [] + while ARGV.length != 0 + compare = nil + case ARGV[0] + when /^--?$/ + ARGV.shift + newargv += ARGV + break + when /^--.*/ + compare = ARGV[0][2, (ARGV[0].length - 2)] + if long_opts != "" + for lo in long_opts + if lo.index(":") && getOptionName(lo) == compare + if ARGV.length <= 1 + return nil + end + setOption(compare, ARGV[1]) + opts[compare] = TRUE + ARGV.shift + count += 1 + break + elsif lo == compare + setOption(compare, TRUE) + opts[compare] = TRUE + count += 1 + break + end + end + end + if compare.length <= 1 + return nil + end + when /^-.*/ + for idx in 1..(ARGV[0].length - 1) + compare = ARGV[0][idx, 1] + if single_opts && compare =~ "[" + single_opts + "]" + setOption(compare, TRUE) + opts[compare] = TRUE + count += 1 + elsif single_colon != "" && compare =~ "[" + single_colon + "]" + if ARGV[0][idx..-1].length > 1 + setOption(compare, ARGV[0][(idx + 1)..-1]) + opts[compare] = TRUE + count += 1 + elsif ARGV.length <= 1 + return nil + else + setOption(compare, ARGV[1]) + opts[compare] = TRUE + ARGV.shift + count += 1 + end + break + end + end + else + compare = ARGV[0] + opts[compare] = TRUE + newargv << ARGV[0] + end + + ARGV.shift + if !opts.has_key?(compare) + return nil + end + end + setNewArgv(newargv) + return count +end diff --git a/lib/jcode.rb b/lib/jcode.rb new file mode 100644 index 0000000000..40ab48ddac --- /dev/null +++ b/lib/jcode.rb @@ -0,0 +1,207 @@ +# jcode.rb - ruby code to handle japanese (EUC/SJIS) string + +$vsave, $VERBOSE = $VERBOSE, FALSE +class String + printf STDERR, "feel free for some warnings:\n" if $VERBOSE + + def jlength + self.split(//).length + end + + alias original_succ succ + private :original_succ + + def mbchar?(c) + if $KCODE =~ /^s/i + c =~ /[\x81-\x9f\xe0-\xef][\x40-\x7e\x80-\xfc]/n + elsif $KCODE =~ /^e/i + c =~ /[\xa1-\xfe][\xa1-\xfe]/n + else + FALSE + end + end + + def succ + if self[-2] && self[-2] & 0x80 != 0 + s = self.dup + s[-1] += 1 + s[-1] += 1 if !mbchar?(s) + return s + else + original_succ + end + end + + def upto(to) + return if self > to + + curr = self + tail = self[-2..-1] + if tail.length == 2 and tail =~ /^.$/ then + if self[0..-2] == to[0..-2] + first = self[-2].chr + for c in self[-1] .. to[-1] + if mbchar?(first+c.chr) + yield self[0..-2]+c.chr + end + end + end + else + loop do + yield curr + return if curr == to + curr = curr.succ + return if curr.length > to.length + end + end + return nil + end + + def _expand_ch + a = [] + self.scan(/(.|\n)-(.|\n)|(.|\n)/) do |r| + if $3 + a.push $3 + elsif $1.length != $2.length + next + elsif $1.length == 1 + $1[0].upto($2[0]) { |c| a.push c.chr } + else + $1.upto($2) { |c| a.push c } + end + end + a + end + + def tr!(from, to) + return self.delete!(from) if to.length == 0 + + if from =~ /^\^/ + comp=TRUE + from = $' + end + afrom = from._expand_ch + ato = to._expand_ch + i = 0 + if comp + self.gsub!(/(.|\n)/) do |c| + unless afrom.include?(c) + ato[-1] + else + c + end + end + else + self.gsub!(/(.|\n)/) do |c| + if i = afrom.index(c) + if i < ato.size then ato[i] else ato[-1] end + else + c + end + end + end + end + + def tr(from, to) + self.dup.tr!(from, to) + end + + def delete!(del) + if del =~ /^\^/ + comp=TRUE + del = $' + end + adel = del._expand_ch + if comp + self.gsub!(/(.|\n)/) do |c| + next unless adel.include?(c) + c + end + else + self.gsub!(/(.|\n)/) do |c| + next if adel.include?(c) + c + end + end + end + + def delete(del) + self.dup.delete!(del) + end + + def squeeze!(del=nil) + if del + if del =~ /^\^/ + comp=TRUE + del = $' + end + adel = del._expand_ch + if comp + self.gsub!(/(.|\n)\1+/) do + next unless adel.include?($1) + $& + end + else + for c in adel + cq = Regexp.quote(c) + self.gsub!(/#{cq}(#{cq})+/, cq) + end + end + self + else + self.gsub!(/(.|\n)\1+/, '\1') + end + end + + def squeeze(del=nil) + self.dup.squeeze!(del) + end + + def tr_s!(from, to) + return self.delete!(from) if to.length == 0 + if from =~ /^\^/ + comp=TRUE + from = $' + end + afrom = from._expand_ch + ato = to._expand_ch + i = 0 + c = nil + last = nil + self.gsub!(/(.|\n)/) do |c| + if comp + unless afrom.include?(c) + ato[-1] + else + c + end + elsif i = afrom.index(c) + c = if i < ato.size then ato[i] else ato[-1] end + next if c == last + last = c + else + last = nil + c + end + end + end + + def tr_s(from, to) + self.dup.tr_s!(from,to) + end + + alias original_chop! chop! + private :original_chop! + + def chop! + if self =~ /(.)$/ and $1.size == 2 + original_chop! + end + original_chop! + end + + def chop + self.dup.chop! + end +end +$VERBOSE = $vsave diff --git a/lib/mailread.rb b/lib/mailread.rb new file mode 100644 index 0000000000..d9feffbb7a --- /dev/null +++ b/lib/mailread.rb @@ -0,0 +1,48 @@ +class Mail + def Mail.new(f) + unless f.kind_of?(IO) + f = open(f, "r") + me = super(f) + f.close + else + me = super + end + return me + end + + def initialize(f) + @header = {} + @body = [] + while f.gets() + $_.chop! + next if /^From / # skip From-line + break if /^$/ # end of header + + if /^(\S+):\s*(.*)/ + @header[attr = $1.capitalize!] = $2 + elsif attr + sub!(/^\s*/, '') + @header[attr] += "\n" + $_ + end + end + + return unless $_ + + while f.gets() + break if /^From / + @body.push($_) + end + end + + def header + return @header + end + + def body + return @body + end + + def [](field) + @header[field] + end +end diff --git a/lib/mathn.rb b/lib/mathn.rb new file mode 100644 index 0000000000..fdf27f6771 --- /dev/null +++ b/lib/mathn.rb @@ -0,0 +1,308 @@ +# +# mathn.rb - +# $Release Version: 0.5 $ +# $Revision: 1.1 $ +# $Date: 1997/07/03 04:43:47 $ +# by Keiju ISHITSUKA(SHL Japan Inc.) +# +# -- +# +# +# + +require "rational.rb" +require "complex.rb" +require "matrix.rb" + +class Integer + + def gcd2(int) + a = self.abs + b = int.abs + a, b = b, a if a < b + + pd_a = a.prime_division + pd_b = b.prime_division + + gcd = 1 + for pair in pd_a + as = pd_b.assoc(pair[0]) + if as + gcd *= as[0] ** [as[1], pair[1]].min + end + end + return gcd + end + + def Integer.from_prime_division(pd) + value = 1 + for prime, index in pd + value *= prime**index + end + value + end + + def prime_division + ps = Prime.new + value = self + pv = [] + for prime in ps + count = 0 + while (value1, mod = value.divmod(prime) + mod) == 0 + value = value1 + count += 1 + end + if count != 0 + pv.push [prime, count] + end + break if prime * prime >= value + end + if value > 1 + pv.push [value, 1] + end + return pv + end +end + +class Prime + include Enumerable + + def initialize + @seed = 1 + @primes = [] + @counts = [] + end + + def succ + i = -1 + size = @primes.size + while i < size + if i == -1 + @seed += 1 + i += 1 + else + while @seed > @counts[i] + @counts[i] += @primes[i] + end + if @seed != @counts[i] + i += 1 + else + i = -1 + end + end + end + @primes.push @seed + @counts.push @seed + @seed + return @seed + end + + def each + loop do + yield succ + end + end +end + +class Fixnum + alias divmod! divmod + alias / rdiv + def divmod(other) + a = self.div(other) + b = self % other + return a,b + end +end + +class Bignum + alias divmod! divmod + alias / rdiv +end + +class Rational + Unify = TRUE + + alias power! ** + + def ** (other) + if other.kind_of?(Rational) + if self < 0 + return Complex(self, 0) ** other + elsif other == 0 + return Rational(1,1) + elsif self == 0 + return Rational(0,1) + elsif self == 1 + return Rational(1,1) + end + + npd = @numerator.prime_division + dpd = @denominator.prime_division + if other < 0 + other = -other + npd, dpd = dpd, npd + end + + for elm in npd + elm[1] = elm[1] * other + if !elm[1].kind_of?(Integer) and elm[1].denominator != 1 + return Float(self) ** other + end + elm[1] = elm[1].to_i + end + + for elm in dpd + elm[1] = elm[1] * other + if !elm[1].kind_of?(Integer) and elm[1].denominator != 1 + return Float(self) ** other + end + elm[1] = elm[1].to_i + end + + num = Integer.from_prime_division(npd) + den = Integer.from_prime_division(dpd) + + Rational(num,den) + + elsif other.kind_of?(Integer) + if other > 0 + num = @numerator ** other + den = @denominator ** other + elsif other < 0 + num = @denominator ** -other + den = @numerator ** -other + elsif other == 0 + num = 1 + den = 1 + end + Rational.new!(num, den) + elsif other.kind_of?(Float) + Float(self) ** other + else + x , y = other.coerce(self) + x ** y + end + end + + def power2(other) + if other.kind_of?(Rational) + if self < 0 + return Complex(self, 0) ** other + elsif other == 0 + return Rational(1,1) + elsif self == 0 + return Rational(0,1) + elsif self == 1 + return Rational(1,1) + end + + dem = nil + x = self.denominator.to_f.to_i + neard = self.denominator.to_f ** (1.0/other.denominator.to_f) + loop do + if (neard**other.denominator == self.denominator) + dem = neaed + break + end + end + nearn = self.numerator.to_f ** (1.0/other.denominator.to_f) + Rational(num,den) + + elsif other.kind_of?(Integer) + if other > 0 + num = @numerator ** other + den = @denominator ** other + elsif other < 0 + num = @denominator ** -other + den = @numerator ** -other + elsif other == 0 + num = 1 + den = 1 + end + Rational.new!(num, den) + elsif other.kind_of?(Float) + Float(self) ** other + else + x , y = other.coerce(self) + x ** y + end + end +end + +module Math + def sqrt(a) + if a.kind_of?(Complex) + abs = sqrt(a.real*a.real + a.image*a.image) +# if not abs.kind_of?(Rational) +# return a**Rational(1,2) +# end + x = sqrt((a.real + abs)/Rational(2)) + y = sqrt((-a.real + abs)/Rational(2)) +# if !(x.kind_of?(Rational) and y.kind_of?(Rational)) +# return a**Rational(1,2) +# end + if a.image >= 0 + Complex(x, y) + else + Complex(x, -y) + end + elsif a >= 0 + rsqrt(a) + else + Complex(0,rsqrt(-a)) + end + end + + def rsqrt(a) + if a.kind_of?(Float) + sqrt!(a) + elsif a.kind_of?(Rational) + rsqrt(a.numerator)/rsqrt(a.denominator) + else + src = a + max = 2 ** 32 + byte_a = [src & 0xffffffff] + # ruby's bug + while (src >= max) and (src >>= 32) + byte_a.unshift src & 0xffffffff + end + + answer = 0 + main = 0 + side = 0 + for elm in byte_a + main = (main << 32) + elm + side <<= 16 + if answer != 0 + if main * 4 < side * side + applo = main.div(side) + else + applo = ((sqrt!(side * side + 4 * main) - side)/2.0).to_i + 1 + end + else + applo = sqrt!(main).to_i + 1 + end + + while (x = (side + applo) * applo) > main + applo -= 1 + end + main -= x + answer = (answer << 16) + applo + side += applo * 2 + end + if main == 0 + answer + else + sqrt!(a) + end + end + end + + module_function :sqrt + module_function :rsqrt +end + +class Complex + Unify = TRUE +end + diff --git a/lib/matrix.rb b/lib/matrix.rb new file mode 100644 index 0000000000..394c66f098 --- /dev/null +++ b/lib/matrix.rb @@ -0,0 +1,777 @@ +#!/usr/local/bin/ruby +# +# matrix.rb - +# $Release Version: 1.0$ +# $Revision: 1.0 $ +# $Date: 97/05/23 11:35:28 $ +# Original Version from Smalltalk-80 version +# on July 23, 1985 at 8:37:17 am +# by Keiju ISHITSUKA +# +# -- +# +# Matrix[[1,2,3], +# : +# [3,4,5]] +# Matrix[row0, +# row1, +# : +# rown] +# +# column: 列 +# row: 行 +# + +require "e2mmap.rb" + +module ExceptionForMatrix + Exception2MessageMapper.extend_to(binding) + + def_e2message(TypeError, "wrong argument type %s (expected %s)") + def_e2message(ArgumentError, "Wrong # of arguments(%d for %d)") + + def_exception("ErrDimensionMismatch", "\#{self.type} dimemsion mismatch") + def_exception("ErrNotRegular", "Not Regular Matrix") + def_exception("ErrOperationNotDefined", "This operation(%s) can\\'t defined") +end + +class Matrix + RCS_ID='-$Header: ruby-mode,v 1.2 91/04/20 17:24:57 keiju Locked $-' + + include ExceptionForMatrix + + # instance creations + private_class_method :new + + def Matrix.[](*rows) + new(:init_rows, rows, FALSE) + end + + def Matrix.rows(rows, copy = TRUE) + new(:init_rows, rows, copy) + end + + def Matrix.columns(columns) + rows = (0 .. columns[0].size - 1).collect { + |i| + (0 .. columns.size - 1).collect { + |j| + columns[j][i] + } + } + Matrix.rows(rows, FALSE) + end + + def Matrix.diagonal(*values) + size = values.size + rows = (0 .. size - 1).collect { + |j| + row = Array.new(size).fill(0, 0, size) + row[j] = values[j] + row + } + self + rows(rows, FALSE) + end + + def Matrix.scalar(n, value) + Matrix.diagonal(*Array.new(n).fill(value, 0, n)) + end + + def Matrix.identity(n) + Matrix.scalar(n, 1) + end + class << Matrix + alias unit identity + alias I identity + end + + def Matrix.zero(n) + Matrix.scalar(n, 0) + end + + def Matrix.row_vector(row) + case row + when Vector + Matrix.rows([row.to_a], FALSE) + when Array + Matrix.rows([row.dup], FALSE) + else + Matrix.row([[row]], FALSE) + end + end + + def Matrix.column_vector(column) + case column + when Vector + Matrix.columns([column.to_a]) + when Array + Matrix.columns([column]) + else + Matrix.columns([[column]]) + end + end + + # initializing + def initialize(init_method, *argv) + self.send(init_method, *argv) + end + + def init_rows(rows, copy) + if copy + @rows = rows.collect{|row| row.dup} + else + @rows = rows + end + self + end + private :init_rows + + #accessing + def [](i, j) + @rows[i][j] + end + + def row_size + @rows.size + end + + def column_size + @rows[0].size + end + + def row(i) + if iterator? + for e in @rows[i] + yield e + end + else + Vector.elements(@rows[i]) + end + end + + def column(j) + if iterator? + 0.upto(row_size - 1) do + |i| + yield @rows[i][j] + end + else + col = (0 .. row_size - 1).collect { + |i| + @rows[i][j] + } + Vector.elements(col, FALSE) + end + end + + def collect + 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) + # + def minor(*param) + case param.size + when 2 + from_row = param[0].first + size_row = param[0].size + from_col = param[1].first + size_col = param[1].size + when 4 + from_row = param[0] + size_row = param[1] + from_col = param[2] + size_col = param[3] + else + Matrix.fail ArgumentError, param.inspect + end + + rows = @rows[from_row, size_row].collect{ + |row| + row[from_col, size_col] + } + Matrix.rows(rows, FALSE) + end + + # TESTING + def regular? + square? and rank == column_size + end + + def singular? + not regular? + end + + def square? + column_size == row_size + end + + # ARITHMETIC + + def *(m) #is matrix or vector or number" + case(m) + when Numeric + rows = @rows.collect { + |row| + row.collect { + |e| + e * m + } + } + return Matrix.rows(rows, FALSE) + when Vector + m = Matrix.column_vector(m) + r = self * m + return r.column(0) + when Matrix + Matrix.fail ErrDimensionMismatch if column_size != m.row_size + + rows = (0 .. row_size - 1).collect { + |i| + (0 .. m.column_size - 1).collect { + |j| + vij = 0 + 0.upto(column_size - 1) do + |k| + vij += self[i, k] * m[k, j] + end + vij + } + } + return Matrix.rows(rows, FALSE) + else + x, y = m.coerce(self) + return x * y + end + end + + def +(m) + case m + when Numeric + Matrix.fail ErrOperationNotDefined, "+" + when Vector + m = Matrix.column_vector(m) + when Matrix + else + x, y = m.coerce(self) + return x + y + end + + Matrix.fail ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size + + rows = (0 .. row_size - 1).collect { + |i| + (0 .. column_size - 1).collect { + |j| + self[i, j] + m[i, j] + } + } + Matrix.rows(rows, FALSE) + end + + def -(m) + case m + when Numeric + Matrix.fail ErrOperationNotDefined, "-" + when Vector + m = Matrix.column_vector(m) + when Matrix + else + x, y = m.coerce(self) + return x - y + end + + Matrix.fail ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size + + rows = (0 .. row_size - 1).collect { + |i| + (0 .. column_size - 1).collect { + |j| + self[i, j] - m[i, j] + } + } + Matrix.rows(rows, FALSE) + end + + def inverse + Matrix.fail ErrDimensionMismatch unless square? + Matrix.I(row_size).inverse_from(self) + end + alias inv inverse + + def inverse_from(src) + size = row_size - 1 + a = src.to_a + + for k in 0..size + if (akk = a[k][k]) == 0 + i = k + begin + fail ErrNotRegular if (i += 1) > size + end while a[i][k] == 0 + a[i], a[k] = a[k], a[i] + @rows[i], @rows[k] = @rows[k], @rows[i] + akk = a[k][k] + end + + for i in 0 .. size + next if i == k + q = a[i][k] / akk + a[i][k] = 0 + + (k + 1).upto(size) do + |j| + a[i][j] -= a[k][j] * q + end + 0.upto(size) do + |j| + @rows[i][j] -= @rows[k][j] * q + end + end + + (k + 1).upto(size) do + |j| + a[k][j] /= akk + end + 0.upto(size) do + |j| + @rows[k][j] /= akk + end + end + self + end + #alias reciprocal inverse + + def ** (other) + if other.kind_of?(Integer) + x = self + if other <= 0 + x = self.inverse + return Matrix.identity(self.column_size) if other == 0 + other = -other + end + z = x + n = other - 1 + while n != 0 + while (div, mod = n.divmod(2) + mod == 0) + x = x * x + n = div + end + z *= x + n -= 1 + end + z + elsif other.kind_of?(Float) || defined?(Rational) && other.kind_of?(Rational) + fail ErrOperationNotDefined, "**" + else + fail ErrOperationNotDefined, "**" + end + end + + # Matrix functions + + def determinant + return 0 unless square? + + size = row_size - 1 + a = to_a + + det = 1 + k = 0 + begin + if (akk = a[k][k]) == 0 + i = k + begin + return 0 if (i += 1) > size + end while a[i][k] == 0 + a[i], a[k] = a[k], a[i] + akk = a[k][k] + end + (k + 1).upto(size) do + |i| + q = a[i][k] / akk + (k + 1).upto(size) do + |j| + a[i][j] -= a[k][j] * q + end + end + det *= akk + end while (k += 1) <= size + det + end + alias det determinant + + def rank + if column_size > row_size + a = transpose.to_a + else + a = to_a + end + rank = 0 + k = 0 + begin + if (akk = a[k][k]) == 0 + i = -1 + nothing = FALSE + begin + if (i += 1) > column_size - 1 + nothing = TRUE + break + end + end while a[i][k] == 0 + next if nothing + a[i], a[k] = a[k], a[i] + akk = a[k][k] + end + (k + 1).upto(row_size - 1) do + |i| + q = a[i][k] / akk + (k + 1).upto(column_size - 1) do + |j| + a[i][j] -= a[k][j] * q + end + end + rank += 1 + end while (k += 1) <= column_size - 1 + return rank + end + + def trace + tr = 0 + 0.upto(column_size - 1) do + |i| + tr += @rows[i][i] + end + tr + end + alias tr trace + + def transpose + Matrix.columns(@rows) + end + alias t transpose + + # CONVERTING + + def coerce(other) + case other + when Numeric + return Scalar.new(other), self + end + end + + def row_vectors + rows = (0 .. column_size - 1).collect { + |i| + row(i) + } + rows + end + + def column_vectors + columns = (0 .. row_size - 1).collect { + |i| + column(i) + } + columns + end + + 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 + + def to_r + collect{|e| e.to_r} + end + + # PRINTING + def to_s + "Matrix[" + @rows.collect{ + |row| + "[" + row.collect{|e| e.to_s}.join(", ") + "]" + }.join(", ")+"]" + end + + def inspect + "Matrix"+@rows.inspect + end + + # Private CLASS + + class Scalar < Numeric + include ExceptionForMatrix + + def initialize(value) + @value = value + end + + # ARITHMETIC + def +(other) + case other + when Numeric + Scalar.new(@value + other) + when Vector, Matrix + Scalar.fail WrongArgType, other.type, "Numeric or Scalar" + when Scalar + Scalar.new(@value + other.value) + else + x, y = other.coerce(self) + x + y + end + end + + def -(other) + case other + when Numeric + Scalar.new(@value - other) + when Vector, Matrix + Scalar.fail WrongArgType, other.type, "Numeric or Scalar" + when Scalar + Scalar.new(@value - other.value) + else + x, y = other.coerce(self) + x - y + end + end + + def *(other) + case other + when Numeric + Scalar.new(@value * other) + when Vector, Matrix + other.collect{|e| @value * e} + else + x, y = other.coerce(self) + x * y + end + end + + def / (other) + case other + when Numeric + Scalar.new(@value / other) + when Vector + Scalar.fail WrongArgType, other.type, "Numeric or Scalar or Matrix" + when Matrix + self * _M.inverse + else + x, y = other.coerce(self) + x / y + end + end + + def ** (other) + case other + when Numeric + Scalar.new(@value ** other) + when Vector + Scalar.fail WrongArgType, other.type, "Numeric or Scalar or Matrix" + when Matrix + other.powered_by(self) + else + x, y = other.coerce(self) + x ** y + end + end + end +end + +#---------------------------------------------------------------------- +# +# - +# +#---------------------------------------------------------------------- +class Vector + include ExceptionForMatrix + + + #INSTANCE CREATION + + private_class_method :new + def Vector.[](*array) + new(:init_elements, array, copy = FALSE) + end + + def Vector.elements(array, copy = TRUE) + new(:init_elements, array, copy) + end + + def initialize(method, array, copy) + self.send(method, array, copy) + end + + def init_elements(array, copy) + if copy + @elements = array.dup + else + @elements = array + end + end + + # ACCSESSING + + def [](i) + @elements[i] + end + + def size + @elements.size + end + + # ENUMRATIONS + def each2(v) + Vector.fail ErrDimensionMismatch if size != v.size + 0.upto(size - 1) do + |i| + yield @elements[i], v[i] + end + end + + def collect2(v) + Vector.fail ErrDimensionMismatch if size != v.size + (0 .. size - 1).collect do + |i| + yield @elements[i], v[i] + end + end + + # ARITHMETIC + + def *(x) "is matrix or number" + case x + when Numeric + els = @elements.collect{|e| e * x} + Vector.elements(els, FALSE) + when Matrix + self.covector * x + else + s, x = X.corece(self) + s * x + end + end + + def +(v) + case v + when Vector + Vector.fail ErrDimensionMismatch if size != v.size + els = collect2(v) { + |v1, v2| + v1 + v2 + } + Vector.elements(els, FALSE) + when Matrix + Matrix.column_vector(self) + v + else + s, x = v.corece(self) + s + x + end + end + + def -(v) + case v + when Vector + Vector.fail ErrDimensionMismatch if size != v.size + els = collect2(v) { + |v1, v2| + v1 - v2 + } + Vector.elements(els, FALSE) + when Matrix + Matrix.column_vector(self) - v + else + s, x = v.corece(self) + s - x + end + end + + # VECTOR FUNCTIONS + + def inner_product(v) + Vector.fail ErrDimensionMismatch if size != v.size + + p = 0 + each2(v) { + |v1, v2| + p += v1 * v2 + } + p + end + + def collect + els = @elements.collect { + |v| + yield v + } + Vector.elements(els, FALSE) + end + alias map collect + + def map2(v) + els = collect2(v) { + |v1, v2| + yield v1, v2 + } + Vector.elements(els, FALSE) + end + + def r + v = 0 + for e in @elements + v += e*e + end + return v.sqrt + end + + # CONVERTING + def covector + Matrix.row_vector(self) + end + + 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 + + def coerce(other) + case other + when Numeric + return Scalar.new(other), self + end + end + + # PRINTING + + def to_s + "Vector[" + @elements.join(", ") + "]" + end + + def inspect + str = "Vector"+@elements.inspect + end +end + diff --git a/lib/mutex_m.rb b/lib/mutex_m.rb new file mode 100644 index 0000000000..823888e72f --- /dev/null +++ b/lib/mutex_m.rb @@ -0,0 +1,183 @@ +# +# mutex_m.rb - +# $Release Version: 2.0$ +# $Revision: 1.2 $ +# $Date: 1997/07/25 02:43:21 $ +# Original from mutex.rb +# by Keiju ISHITSUKA(SHL Japan Inc.) +# +# -- +# Usage: +# require "mutex_m.rb" +# obj = Object.new +# obj.extend Mutex_m +# ... +# 後はMutexと同じ使い方 +# + +require "finalize" + +module Mutex_m + def Mutex_m.extend_object(obj) + if Fixnum === obj or TRUE === obj or FALSE === obj or nil == obj + raise TypeError, "Mutex_m can't extend to this class(#{obj.type})" + else + begin + eval "class << obj + @mu_locked + end" + obj.extend(For_primitive_object) + rescue TypeError + obj.extend(For_general_object) + end + end + end + + def mu_extended + unless (defined? locked? and + defined? lock and + defined? unlock and + defined? try_lock and + defined? synchronize) + eval "class << self + alias locked mu_locked? + alias lock mu_lock + alias unlock mu_unlock + alias try_lock mu_try_lock + alias synchronize mu_synchronize + end" + end + end + + def mu_synchronize + begin + mu_lock + yield + ensure + mu_unlock + end + end + + module For_general_object + include Mutex_m + + def For_general_object.extend_object(obj) + super + obj.mu_extended + end + + def mu_extended + super + @mu_waiting = [] + @mu_locked = FALSE; + end + + def mu_locked? + @mu_locked + end + + def mu_try_lock + result = FALSE + Thread.critical = TRUE + unless @mu_locked + @mu_locked = TRUE + result = TRUE + end + Thread.critical = FALSE + result + end + + def mu_lock + while (Thread.critical = TRUE; @mu_locked) + @mu_waiting.push Thread.current + Thread.stop + end + @mu_locked = TRUE + Thread.critical = FALSE + self + end + + def mu_unlock + return unless @mu_locked + Thread.critical = TRUE + wait = @mu_waiting + @mu_waiting = [] + @mu_locked = FALSE + Thread.critical = FALSE + for w in wait + w.run + end + self + end + + end + + module For_primitive_object + include Mutex_m + Mu_Locked = Hash.new + + def For_primitive_object.extend_object(obj) + super + obj.mu_extended + Finalizer.add(obj, For_primitive_object, :mu_finalize) + end + + def For_primitive_object.mu_finalize(id) + Thread.critical = TRUE + if wait = Mu_Locked.delete(id) + # wait == [] ときだけ GCされるので, for w in wait は意味なし. + Thread.critical = FALSE + for w in wait + w.run + end + else + Thread.critical = FALSE + end + self + end + + def mu_locked? + Mu_Locked.key?(self.id) + end + + def mu_try_lock + Thread.critical = TRUE + if Mu_Locked.key?(self.id) + ret = FALSE + else + Mu_Locked[self.id] = [] + Finalizer.set(self, For_primitive_object, :mu_delete_Locked) + ret = TRUE + end + Thread.critical = FALSE + ret + end + + def mu_lock + while (Thread.critical = TRUE; w = Mu_Locked[self.id]) + w.push Thread.current + Thread.stop + end + Mu_Locked[self.id] = [] + Finalizer.add(self, For_primitive_object, :mu_delete_Locked) + Thread.critical = FALSE + self + end + + def mu_unlock + Thread.critical = TRUE + if wait = Mu_Locked.delete(self.id) + Finalizer.delete(self, For_primitive_object, :mu_finalize) + Thread.critical = FALSE + for w in wait + w.run + end + else + Thread.critical = FALSE + end + self + end + end +end + + diff --git a/lib/observer.rb b/lib/observer.rb new file mode 100644 index 0000000000..b802dac633 --- /dev/null +++ b/lib/observer.rb @@ -0,0 +1,40 @@ +# Observable Mixin +# +# Observers must respond to update + +module Observable + def add_observer(observer) + @observer_peers = [] unless @observer_peers + unless defined? observer.update + raise NameError, "observer needs to respond to `update'" + end + @observer_peers.push observer + end + def delete_observer(observer) + @observer_peers.delete observer if @observer_peers + end + def delete_observers + @observer_peers.clear if @observer_peers + end + def count_observers + if @observer_peers + @observer_peers.size + else + 0 + end + end + def changed(state=TRUE) + @observer_state = state + end + def changed? + @observer_state + end + def notify_observers(*arg) + if @observer_peers and @observer_state + for i in @observer_peers + i.update(*arg) + end + @observer_state = FALSE + end + end +end diff --git a/lib/parsearg.rb b/lib/parsearg.rb new file mode 100644 index 0000000000..a0ef90f018 --- /dev/null +++ b/lib/parsearg.rb @@ -0,0 +1,84 @@ +#!/usr/local/bin/ruby +# +# parsearg.rb - parse arguments +# $Release Version: $ +# $Revision$ +# $Date$ +# by Yasuo OHBA(SHL Japan Inc. Technology Dept.) +# +# -- +# +# +# + +$RCS_ID="$Header$" + +load("getopts.rb") + +def printUsageAndExit() + if $USAGE + eval($USAGE) + end + exit() +end + +def setParenthesis(ex, opt, c) + if opt != "" + ex = sprintf("%s$OPT_%s%s", ex, opt, c) + else + ex = sprintf("%s%s", ex, c) + end + return ex +end + +def setOrAnd(ex, opt, c) + if opt != "" + ex = sprintf("%s$OPT_%s %s%s ", ex, opt, c, c) + else + ex = sprintf("%s %s%s ", ex, c, c) + end + return ex +end + +def setExpression(ex, opt, op) + if !op + ex = sprintf("%s$OPT_%s", ex, opt) + return ex + end + case op.chr + when "(", ")" + ex = setParenthesis(ex, opt, op.chr) + when "|", "&" + ex = setOrAnd(ex, opt, op.chr) + else + return nil + end + return ex +end + +def parseArgs(argc, nopt, single_opts, *opts) + if (noOptions = getopts(single_opts, *opts)) == nil + printUsageAndExit() + end + if nopt + ex = nil + pos = 0 + for o in nopt.split(/[()|&]/) + pos += o.length + ex = setExpression(ex, o, nopt[pos]) + pos += 1 + end + begin + if !eval(ex) + printUsageAndExit() + end + rescue + print "Format Error!! : \"" + nopt + "\"\t[parseArgs]\n" + exit! -1 + end + end + if ARGV.length < argc + printUsageAndExit() + end + return noOptions +end diff --git a/lib/parsedate.rb b/lib/parsedate.rb new file mode 100644 index 0000000000..1c1dda76bc --- /dev/null +++ b/lib/parsedate.rb @@ -0,0 +1,42 @@ +module ParseDate + MONTHS = { + 'jan' => 1, 'feb' => 2, 'mar' => 3, 'apr' => 4, + 'may' => 5, 'jun' => 6, 'jul' => 7, 'aug' => 8, + 'sep' => 9, 'oct' =>10, 'nov' =>11, 'dec' =>12 } + MONTHPAT = MONTHS.keys.join('|') + DAYPAT = 'mon|tue|wed|thu|fri|sat|sun' + + def parsedate(date) + if date.sub!(/(#{DAYPAT})/i, ' ') + dayofweek = $1 + end + if date.sub!(/\s+(\d+:\d+(:\d+)?)/, ' ') + time = $1 + end + if date =~ /19(\d\d)/ + year = Integer($1) + end + if date.sub!(/\s*(\d+)\s+(#{MONTHPAT})\S*\s+/i, ' ') + dayofmonth = $1.to_i + monthname = $2 + elsif date.sub!(/\s*(#{MONTHPAT})\S*\s+(\d+)\s+/i, ' ') + monthname = $1 + dayofmonth = $2.to_i + elsif date.sub!(/\s*(#{MONTHPAT})\S*\s+(\d+)\D+/i, ' ') + monthname = $1 + dayofmonth = $2.to_i + elsif date.sub!(/\s*(\d\d?)\/(\d\d?)/, ' ') + month = $1 + dayofmonth = $2.to_i + end + if monthname + month = MONTHS[monthname.downcase] + end + if ! year && date =~ /\d\d/ + year = Integer($&) + end + return year, month, dayofmonth + end + + module_function :parsedate +end diff --git a/lib/ping.rb b/lib/ping.rb new file mode 100644 index 0000000000..d742a50f99 --- /dev/null +++ b/lib/ping.rb @@ -0,0 +1,55 @@ +# +# ping.rb -- check a host for upness +# +#= SYNOPSIS +# +# require 'ping' +# print "'jimmy' is alive and kicking\n" if Ping.pingecho('jimmy', 10) ; +# +#= DESCRIPTION +# +# This module contains routines to test for the reachability of remote hosts. +# Currently the only routine implemented is pingecho(). +# +# pingecho() uses a TCP echo (I<not> an ICMP one) to determine if the +# remote host is reachable. This is usually adequate to tell that a remote +# host is available to rsh(1), ftp(1), or telnet(1) onto. +# +#== Parameters +# +# : hostname +# +# The remote host to check, specified either as a hostname or as an +# IP address. +# +# : timeout +# +# The timeout in seconds. If not specified it will default to 5 seconds. +# +#= WARNING +# +# pingecho() uses user-level thread to implement the timeout, so it may block +# for long period if named does not respond for some reason. +# +#=end + +module Ping + require "socket" + def pingecho(host, timeout=5) + begin + x = Thread.current + y = Thread.start { + sleep timeout + x.raise RuntimeError if x.status + } + s = TCPsocket.new(host, "echo") + s.close + return TRUE + rescue + return FALSE; + ensure + Thread.kill y if y.status + end + end + module_function "pingecho" +end diff --git a/lib/rational.rb b/lib/rational.rb new file mode 100644 index 0000000000..d4112c2956 --- /dev/null +++ b/lib/rational.rb @@ -0,0 +1,361 @@ +# +# rational.rb - +# $Release Version: 0.5 $ +# $Revision: 1.1 $ +# $Date: 1996/11/11 04:25:14 $ +# by Keiju ISHITSUKA(SHL Japan Inc.) +# +# -- +# Usage: +# class Rational < Numeric +# (include Compareable) +# +# Rational(a, b) --> a/b +# +# Rational::+ +# Rational::- +# Rational::* +# Rational::/ +# Rational::** +# Rational::% +# Rational::divmod +# Rational::abs +# Rational::<=> +# Rational::to_i +# Rational::to_f +# Rational::to_s +# +# Integer::gcd +# Integer::lcm +# Integer::gcdlcm +# Integer::to_r +# +# Fixnum::** +# Bignum::** +# +# + +def Rational(a, b = 1) + if a.kind_of?(Rational) && b == 1 + a + else + Rational.reduce(a, b) + end +end + +class Rational < Numeric + def Rational.reduce(num, den = 1) + if den < 0 + num = -num + den = -den + end + gcd = num.gcd(den) + num = num.div(gcd) + den = den.div(gcd) + if den == 1 && defined?(Unify) + num + else + new!(num, den) + end + end + + def Rational.new!(num, den = 1) + new(num, den) + end + + def initialize(num, den) + if den < 0 + num = -num + den = -den + end + if num.kind_of?(Integer) and den.kind_of?(Integer) + @numerator = num + @denominator = den + else + @numerator = num.to_i + @denoninator = den.to_i + end + end + + def + (a) + if a.kind_of?(Rational) + num = @numerator * a.denominator + num_a = a.numerator * @denominator + Rational(num + num_a, @denominator * a.denominator) + elsif a.kind_of?(Integer) + self + Rational.new!(a, 1) + elsif a.kind_of?(Float) + Float(self) + a + else + x , y = a.coerce(self) + x + y + end + end + + def - (a) + if a.kind_of?(Rational) + num = @numerator * a.denominator + num_a = a.numerator * @denominator + Rational(num - num_a, @denominator*a.denominator) + elsif a.kind_of?(Integer) + self - Rational.new!(a, 1) + elsif a.kind_of?(Float) + Float(self) - a + else + x , y = a.coerce(self) + x - y + end + end + + def * (a) + if a.kind_of?(Rational) + num = @numerator * a.numerator + den = @denominator * a.denominator + Rational(num, den) + elsif a.kind_of?(Integer) + self * Rational.new!(a, 1) + elsif a.kind_of?(Float) + Float(self) * a + else + x , y = a.coerce(self) + x * y + end + end + + def / (a) + if a.kind_of?(Rational) + num = @numerator * a.denominator + den = @denominator * a.numerator + Rational(num, den) + elsif a.kind_of?(Integer) + self / Rational.new!(a, 1) + elsif a.kind_of?(Float) + Float(self) / a + else + x , y = a.coerce(self) + x / y + end + end + + def ** (other) + if other.kind_of?(Rational) + Float(self) ** other + elsif other.kind_of?(Integer) + if other > 0 + num = @numerator ** other + den = @denominator ** other + elsif other < 0 + num = @denominator ** -other + den = @numerator ** -other + elsif other == 0 + num = 1 + den = 1 + end + Rational.new!(num, den) + elsif other.kind_of?(Float) + Float(self) ** other + else + x , y = other.coerce(self) + x ** y + end + end + + def % (other) + value = (self / other).to_i + return self - other * value + end + + def divmod(other) + value = (self / other).to_i + return value, self - other * value + end + + def abs + if @numerator > 0 + Rational.new!(@numerator, @denominator) + else + Rational.new!(-@numerator, @denominator) + end + end + + def <=> (other) + if other.kind_of?(Rational) + num = @numerator * other.denominator + num_a = other.numerator * @denominator + v = num - num_a + if v > 0 + return 1 + elsif v < 0 + return -1 + else + return 0 + end + elsif other.kind_of?(Integer) + return self <=> Rational.new!(other, 1) + elsif other.kind_of?(Float) + return Float(self) <=> other + else + x , y = other.coerce(self) + return x <=> y + end + end + + def coerce(other) + if other.kind_of?(Float) + return other, self.to_f + elsif other.kind_of?(Integer) + return Rational.new!(other, 1), self + else + super + end + end + + def to_i + Integer(@numerator.div(@denominator)) + end + + def to_f + @numerator.to_f/@denominator.to_f + end + + def to_s + if @denominator == 1 + @numerator.to_s + else + @numerator.to_s+"/"+@denominator.to_s + end + end + + def to_r + self + end + + def hash + @numerator ^ @denominator + end + + attr :numerator + attr :denominator + + private :initialize +end + +class Integer + def numerator + self + end + + def denomerator + 1 + end + + def to_r + Rational(self, 1) + end + + def gcd(int) + a = self.abs + b = int.abs + + a, b = b, a if a < b + + while b != 0 + void, a = a.divmod(b) + a, b = b, a + end + return a + end + + def lcm(int) + a = self.abs + b = int.abs + gcd = a.gcd(b) + (a.div(gcd)) * b + end + + def gcdlcm(int) + a = self.abs + b = int.abs + gcd = a.gcd(b) + return gcd, (a.div(gcd)) * b + end + +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) + Rational.new!(self,1) / other + end + + def rpower (other) + if other >= 0 + self.power!(other) + else + Rational.new!(self,1)**other + end + end + + if not defined? Complex + alias ** rpower + end +end + +class Bignum + alias div! /; + alias div /; + alias divmod! divmod + + if not defined? power! + alias power! ** + end + + def rdiv(other) + Rational.new!(self,1) / other + end + + def rpower (other) + if other >= 0 + self.power!(other) + else + Rational.new!(self, 1)**other + end + end + + if not defined? Complex + alias ** rpower + end + +end + diff --git a/lib/sync.rb b/lib/sync.rb new file mode 100644 index 0000000000..3e57ed0e57 --- /dev/null +++ b/lib/sync.rb @@ -0,0 +1,376 @@ +# +# sync.rb - カウント付2-フェーズロッククラス +# $Release Version: 0.1$ +# $Revision$ +# $Date$ +# by Keiju ISHITSUKA +# +# -- +# Usage: +# Sync_m, Synchronizer_m +# +# Sync_m#sync_mode +# Sync_m#sync_locked?, locked? +# Sync_m#sync_shared?, shared? +# Sync_m#sync_exclusive?, sync_exclusive? +# Sync_m#sync_try_lock, try_lock +# Sync_m#sync_lock, lock +# Sync_m#sync_unlock, unlock +# +# Sync, Synchronicer: +# include Sync_m +# +# sync = Sync.new +# Sync#mode +# Sync#locked? +# Sync#shared? +# Sync#exclusive? +# Sync#try_lock(mode) -- mode = :EX, :SH, :UN +# Sync#lock(mode) -- mode = :EX, :SH, :UN +# Sync#unlock +# Sync#synchronize(mode) {...} +# +# + +unless defined? Thread + fail "Thread not available for this ruby interpreter" +end + +require "finalize.rb" + +module Sync_m + RCS_ID='-$Header$-' + + UN = :UN + SH = :SH + EX = :EX + + class Err < Exception + def Err.Fail(*opt) + fail self, sprintf(self::Message, *opt) + end + + class UnknownLocker < Err + Message = "Thread(%s) not locked." + def UnknownLocker.Fail(th) + super(th.inspect) + end + end + + class LockModeFailer < Err + Message = "Unknown lock mode(%s)" + def LockModeFailer.Fail(mode) + if mode.id2name + mode = id2name + end + super(mode) + end + end + end + + def Sync_m.extend_object(obj) + if Fixnum === obj or TRUE === obj or FALSE === obj or nil == obj + raise TypeError, "Sync_m can't extend to this class(#{obj.type})" + else + begin + eval "class << obj + @sync_locked + end" + obj.extend(For_primitive_object) + rescue TypeError + obj.extend(For_general_object) + end + end + end + + def sync_extended + unless (defined? locked? and + defined? shared? and + defined? exclusive? and + defined? lock and + defined? unlock and + defined? try_lock and + defined? synchronize) + eval "class << self + alias locked? sync_locked? + alias shared? sync_shared? + alias excluive? sync_exclusive? + alias lock sync_lock + alias unlock sync_unlock + alias try_lock sync_try_lock + alias synchronize sync_synchronize + end" + end + end + + def sync_locked? + sync_mode != UN + end + + def sync_shared? + sync_mode == SH + end + + def sync_exclusive? + sync_mode == EX + end + + def sync_try_lock(mode = EX) + return unlock if sync_mode == UN + + Thread.critical = TRUE + ret = sync_try_lock_sub(sync_mode) + Thread.critical = FALSE + ret + end + + def sync_lock(m = EX) + return unlock if m == UN + + until (Thread.critical = TRUE; sync_try_lock_sub(m)) + if sync_sh_locker[Thread.current] + sync_upgrade_waiting.push [Thread.current, sync_sh_locker[Thread.current]] + sync_sh_locker.delete(Thread.current) + else + sync_waiting.push Thread.current + end + Thread.stop + end + Thread.critical = FALSE + self + end + + def sync_unlock(m = EX) + Thread.critical = TRUE + if sync_mode == UN + Thread.critical = FALSE + Err::UnknownLocker.Fail(Thread.current) + end + + m = sync_mode if m == EX and sync_mode == SH + + runnable = FALSE + case m + when UN + Thread.critical = FALSE + Err::UnknownLocker.Fail(Thread.current) + + when EX + if sync_ex_locker == Thread.current + if (self.sync_ex_count = sync_ex_count - 1) == 0 + self.sync_ex_locker = nil + if sync_sh_locker.include?(Thread.current) + self.sync_mode = SH + else + self.sync_mode = UN + end + runnable = TRUE + end + else + Err::UnknownLocker.Fail(Thread.current) + end + + when SH + if (count = sync_sh_locker[Thread.current]).nil? + Err::UnknownLocker.Fail(Thread.current) + else + if (sync_sh_locker[Thread.current] = count - 1) == 0 + sync_sh_locker.delete(Thread.current) + if sync_sh_locker.empty? and sync_ex_count == 0 + self.sync_mode = UN + runnable = TRUE + end + end + end + end + + if runnable + if sync_upgrade_waiting.size > 0 + for k, v in sync_upgrade_waiting + sync_sh_locker[k] = v + end + wait = sync_upgrade_waiting + self.sync_upgrade_waiting = [] + Thread.critical = FALSE + + for w, v in wait + w.run + end + else + wait = sync_waiting + self.sync_waiting = [] + Thread.critical = FALSE + for w in wait + w.run + end + end + end + + Thread.critical = FALSE + self + end + + def sync_try_lock_sub(m) + case m + when SH + case sync_mode + when UN + self.sync_mode = m + sync_sh_locker[Thread.current] = 1 + ret = TRUE + when SH + count = 0 unless count = sync_sh_locker[Thread.current] + sync_sh_locker[Thread.current] = count + 1 + ret = TRUE + when EX + # 既に, モードがEXである時は, 必ずEXロックとなる. + if sync_ex_locker == Thread.current + self.sync_ex_count = sync_ex_count + 1 + ret = TRUE + else + ret = FALSE + end + end + when EX + if sync_mode == UN or + sync_mode == SH && sync_sh_locker.size == 1 && sync_sh_locker.include?(Thread.current) + self.sync_mode = m + self.sync_ex_locker = Thread.current + self.sync_ex_count = 1 + ret = TRUE + + elsif sync_mode == EX && sync_ex_locker == Thread.current + self.sync_ex_count = sync_ex_count + 1 + ret = TRUE + else + ret = FALSE + end + else + Thread.critical = FALSE + Err::LockModeFailer.Fail mode + end + return ret + end + private :sync_try_lock_sub + + def sync_synchronize(mode = EX) + begin + sync_lock(mode) + yield + ensure + sync_unlock + end + end + + module For_primitive_object + include Sync_m + + LockState = Struct.new("LockState", + :mode, + :waiting, + :upgrade_waiting, + :sh_locker, + :ex_locker, + :ex_count) + + Sync_Locked = Hash.new + + def For_primitive_object.extend_object(obj) + super + obj.sync_extended + Finalizer.add(obj, For_primitive_object, :sync_finalize) + end + + def sync_extended + super + Sync_Locked[id] = LockState.new(UN, [], [], Hash.new, nil, 0 ) + end + + def sync_finalize + wait = Sync_Locked.delete(id) + # waiting == [] ときだけ GCされるので, 待ち行列の解放は意味がない. + end + + def sync_mode + Sync_Locked[id].mode + end + def sync_mode=(value) + Sync_Locked[id].mode = value + end + + def sync_waiting + Sync_Locked[id].waiting + end + def sync_waiting=(v) + Sync_Locked[id].waiting = v + end + + def sync_upgrade_waiting + Sync_Locked[id].upgrade_waiting + end + def sync_upgrade_waiting=(v) + Sync_Locked[id].upgrade_waiting = v + end + + def sync_sh_locker + Sync_Locked[id].sh_locker + end + def sync_sh_locker=(v) + Sync_Locked[id].sh_locker = v + end + + def sync_ex_locker + Sync_Locked[id].ex_locker + end + def sync_ex_locker=(value) + Sync_Locked[id].ex_locker = value + end + + def sync_ex_count + Sync_Locked[id].ex_count + end + def sync_ex_count=(value) + Sync_Locked[id].ex_count = value + end + + end + + module For_general_object + include Sync_m + + def For_general_object.extend_object(obj) + super + obj.sync_extended + end + + def sync_extended + super + @sync_mode = UN + @sync_waiting = [] + @sync_upgrade_waiting = [] + @sync_sh_locker = Hash.new + @sync_ex_locker = nil + @sync_ex_count = 0 + end + + attr :sync_mode, TRUE + + attr :sync_waiting, TRUE + attr :sync_upgrade_waiting, TRUE + attr :sync_sh_locker, TRUE + attr :sync_ex_locker, TRUE + attr :sync_ex_count, TRUE + + end +end +Synchronizer_m = Sync_m + +class Sync + include Sync_m::For_general_object + + def initialize + sync_extended + end + +end +Synchronizer = Sync diff --git a/lib/thread.rb b/lib/thread.rb new file mode 100644 index 0000000000..4f294cc9a3 --- /dev/null +++ b/lib/thread.rb @@ -0,0 +1,110 @@ +# +# thread.rb - thread support classes +# $Date$ +# by Yukihiro Matsumoto <matz@caelum.co.jp> +# + +unless defined? Thread + fail "Thread not available for this ruby interpreter" +end + +unless defined? ThreadError + class ThreadError<Exception + end +end + +class Mutex + def initialize + @waiting = [] + @locked = FALSE; + end + + def locked? + @locked + end + + def try_lock + result = FALSE + Thread.critical = TRUE + unless @locked + @locked = TRUE + result = TRUE + end + Thread.critical = FALSE + result + end + + def lock + while (Thread.critical = TRUE; @locked) + @waiting.push Thread.current + Thread.stop + end + @locked = TRUE + Thread.critical = FALSE + self + end + + def unlock + return unless @locked + Thread.critical = TRUE + wait = @waiting + @waiting = [] + @locked = FALSE + Thread.critical = FALSE + for w in wait + w.run + end + self + end + + def synchronize + begin + lock + yield + ensure + unlock + end + end +end + +class Queue + def initialize + @que = [] + @waiting = [] + end + + def push(obj) + Thread.critical = TRUE + @que.push obj + t = @waiting.shift + Thread.critical = FALSE + t.run if t + end + + def pop non_block=FALSE + item = nil + until item + Thread.critical = TRUE + if @que.length == 0 + if non_block + Thread.critical = FALSE + raise ThreadError, "queue empty" + end + @waiting.push Thread.current + Thread.stop + else + item = @que.shift + end + end + Thread.critical = FALSE + item + end + + def empty? + @que.length == 0 + end + + def length + @que.length + end +end diff --git a/lib/thwait.rb b/lib/thwait.rb new file mode 100644 index 0000000000..c638335f5d --- /dev/null +++ b/lib/thwait.rb @@ -0,0 +1,128 @@ +# +# thwait.rb - +# $Release Version: $ +# $Revision: 1.1 $ +# $Date: 1997/08/18 03:13:14 $ +# by Keiju ISHITSUKA(Nippon Rational Inc.) +# +# -- +# +# +# + +require "thread.rb" +require "e2mmap.rb" + +class ThreadsWait + RCS_ID='-$Header: /home/keiju/var/src/var.lib/ruby/RCS/thwait.rb,v 1.1 1997/08/18 03:13:14 keiju Exp keiju $-' + + Exception2MessageMapper.extend_to(binding) + def_exception("ErrWaitThreadsNothing", "Wait threads nothing.") + def_exception("FinshedThreadsNothing", "finished thread nothing.") + + # class mthods + # all_waits + + # + # 指定したスレッドが全て終了するまで待つ. イテレータとして呼ばれると + # 指定したスレッドが終了するとイテレータを呼び出す. + # + def ThreadsWait.all_waits(*threads) + tw = ThreadsWait.new(th1, th2, th3, th4, th5) + if iterator? + tw.all_waits do + |th| + yield th + end + else + tw.all_waits + end + end + + # initialize and terminating: + # initialize + + # + # 初期化. 待つスレッドの指定ができる. + # + def initialize(*threads) + @threads = [] + @wait_queue = Queue.new + join_nowait(*threads) unless threads.empty? + end + + # accessing + # threads + + # 待ちスレッドの一覧を返す. + attr :threads + + # testing + # empty? + # finished? + # + + # + # 待ちスレッドが存在するかどうかを返す. + def empty? + @threads.empty? + end + + # + # すでに終了したスレッドがあるかどうか返す + def finished? + !@wait_queue.empty? + end + + # main process: + # join + # join_nowait + # next_wait + # all_wait + + # + # 待っているスレッドを追加し待ちにはいる. + # + def join(*threads) + join_nowait(*threads) + next_wait + end + + # + # 待っているスレッドを追加する. 待ちには入らない. + # + def join_nowait(*threads) + @threads.concat threads + for th in threads + Thread.start do + th = Thread.join(th) + @wait_queue.push th + end + end + end + + # + # 次の待ちにはいる. + # 待つべきスレッドがなければ, 例外ErrWaitThreadsNothing を返す. + # nonnlockが真の時には, nonblockingで調べる. 存在しなければ, 例外 + # FinishedThreadNothingを返す. + # + def next_wait(nonblock = nil) + Threads.Wait.fail ErrWaitThreadsNothing if @threads.empty? + + th = @wait_queue.pop(nonblock) + @threads.delete th + th + end + + # + # 全てのスレッドが終了するまで待つ. イテレータとして呼ばれた時は, ス + # レッドが終了する度に, イテレータを呼び出す. + # + def all_waits + until @threads.empty? + th = next_wait + yield th if iterator? + end + end +end diff --git a/lib/tk.rb b/lib/tk.rb new file mode 100644 index 0000000000..df68874b13 --- /dev/null +++ b/lib/tk.rb @@ -0,0 +1,829 @@ +# +# tk.rb - Tk interface for ruby +# $Date$ +# by Yukihiro Matsumoto <matz@caelum.co.jp> + +if defined? Thread and $tk_thread_safe + require "tkthcore" +else + require "tkcore" +end + +module TkSelection + include Tk + extend Tk + def clear(win=Tk.root) + tk_call 'selection', 'clear', win.path + end + def get(type=None) + tk_call 'selection', 'get', type + end + def TkSelection.handle(win, func, type=None, format=None) + id = install_cmd(func) + tk_call 'selection', 'handle', win.path, id, type, format + end + def handle(func, type=None, format=None) + TkSelection.handle self, func, type, format + end + def TkSelection.own(win, func=None) + id = install_cmd(func) + tk_call 'selection', 'own', win.path, id + end + def own(func=None) + TkSelection.own self, func + end + + module_function :clear, :get +end + +module TkWinfo + include Tk + extend Tk + def TkWinfo.atom(name) + tk_call 'winfo', name + end + def winfo_atom(name) + TkWinfo.atom name + end + def TkWinfo.atomname(id) + tk_call 'winfo', id + end + def winfo_atomname(id) + TkWinfo.atomname id + end + def TkWinfo.cells(window) + number(tk_call('winfo', window.path)) + end + def winfo_cells + TkWinfo.cells self + end + def TkWinfo.children(window) + c = tk_call('winfo', 'children', window.path) + list(c) + end + 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 + def TkWinfo.containing(rootX, rootY) + path = tk_call('winfo', 'class', window.path) + window(path) + end + def winfo_containing(x, y) + TkWinfo.containing x, y + end + def TkWinfo.depth(window) + number(tk_call('winfo', 'depth', window.path)) + end + def winfo_depth(window) + TkWinfo.depth self + end + def TkWinfo.exist?(window) + bool(tk_call('winfo', 'exists', window.path)) + end + def winfo_exist?(window) + TkWinfo.exist? self + end + def TkWinfo.fpixels(window, number) + number(tk_call('winfo', 'fpixels', window.path, number)) + end + def winfo_fpixels(window, number) + TkWinfo.fpixels self + end + def TkWinfo.geometry(window) + list(tk_call('winfo', 'geometry', window.path)) + end + def winfo_geometry(window) + TkWinfo.geometry self + end + def TkWinfo.height(window) + number(tk_call('winfo', 'height', window.path)) + end + def winfo_height(window) + TkWinfo.height self + end + def TkWinfo.id(window) + number(tk_call('winfo', 'id', window.path)) + end + def winfo_id(window) + TkWinfo.id self + end + def TkWinfo.mapped?(window) + bool(tk_call('winfo', 'ismapped', window.path)) + end + def winfo_mapped?(window) + TkWinfo.mapped? self + end + def TkWinfo.parent(window) + window(tk_call('winfo', 'parent', window.path)) + end + def winfo_parent(window) + TkWinfo.parent self + end + def TkWinfo.widget(id) + window(tk_call('winfo', 'pathname', id)) + end + def winfo_widget(id) + TkWinfo.widget id + end + def TkWinfo.pixels(window, number) + number(tk_call('winfo', 'pixels', window.path, number)) + end + def winfo_pixels(window, number) + TkWinfo.pixels self, number + end + def TkWinfo.reqheight(window) + number(tk_call('winfo', 'reqheight', window.path)) + end + def winfo_reqheight(window) + TkWinfo.reqheight self + end + def TkWinfo.reqwidth(window) + number(tk_call('winfo', 'reqwidth', window.path)) + end + def winfo_reqwidth(window) + TkWinfo.reqwidth self + end + def TkWinfo.rgb(window, color) + list(tk_call('winfo', 'rgb', window.path, color)) + end + def winfo_rgb(window, color) + TkWinfo.rgb self, color + end + def TkWinfo.rootx(window) + number(tk_call('winfo', 'rootx', window.path)) + end + def winfo_rootx(window) + TkWinfo.rootx self + end + def TkWinfo.rooty(window) + number(tk_call('winfo', 'rooty', window.path)) + end + def winfo_rooty(window) + TkWinfo.rooty self + end + def TkWinfo.screen(window) + tk_call 'winfo', 'screen', window.path + end + def winfo_screen(window) + TkWinfo.screen self + end + def TkWinfo.screencells(window) + number(tk_call('winfo', 'screencells', window.path)) + end + def winfo_screencells(window) + TkWinfo.screencells self + end + def TkWinfo.screendepth(window) + number(tk_call('winfo', 'screendepth', window.path)) + end + def winfo_screendepth(window) + TkWinfo.screendepth self + end + def TkWinfo.screenheight (window) + number(tk_call('winfo', 'screenheight', window.path)) + end + def winfo_screenheight(window) + TkWinfo.screenheight self + end + def TkWinfo.screenmmheight(window) + number(tk_call('winfo', 'screenmmheight', window.path)) + end + def winfo_screenmmheight(window) + TkWinfo.screenmmheight self + end + def TkWinfo.screenmmwidth(window) + number(tk_call('winfo', 'screenmmwidth', window.path)) + end + def winfo_screenmmwidth(window) + TkWinfo.screenmmwidth self + end + def TkWinfo.screenvisual(window) + tk_call 'winfo', 'screenvisual', window.path + end + def winfo_screenvisual(window) + TkWinfo.screenvisual self + end + def TkWinfo.screenwidth(window) + number(tk_call('winfo', 'screenwidth', window.path)) + end + def winfo_screenwidth(window) + TkWinfo.screenwidth self + end + def TkWinfo.toplevel(window) + window(tk_call('winfo', 'toplevel', window.path)) + end + def winfo_toplevel(window) + TkWinfo.toplevel self + end + def TkWinfo.visual(window) + tk_call 'winfo', 'visual', window.path + end + def winfo_visual(window) + TkWinfo.visual self + end + def TkWinfo.vrootheigh(window) + number(tk_call('winfo', 'vrootheight', window.path)) + end + def winfo_vrootheight(window) + TkWinfo.vrootheight self + end + def TkWinfo.vrootwidth(window) + number(tk_call('winfo', 'vrootwidth', window.path)) + end + def winfo_vrootwidth(window) + TkWinfo.vrootwidth self + end + def TkWinfo.vrootx(window) + number(tk_call('winfo', 'vrootx', window.path)) + end + def winfo_vrootx(window) + TkWinfo.vrootx self + end + def TkWinfo.vrooty(window) + number(tk_call('winfo', 'vrooty', window.path)) + end + def winfo_vrooty(window) + TkWinfo.vrooty self + end + def TkWinfo.width(window) + number(tk_call('winfo', 'width', window.path)) + end + def winfo_width(window) + TkWinfo.width self + end + def TkWinfo.x(window) + number(tk_call('winfo', 'x', window.path)) + end + def winfo_x(window) + TkWinfo.x self + end + def TkWinfo.y(window) + number(tk_call('winfo', 'y', window.path)) + end + def winfo_y(window) + TkWinfo.y self + end +end + +module TkPack + include Tk + extend Tk + def configure(win, *args) + if args[-1].kind_of?(Hash) + keys = args.pop + end + wins = [win.epath] + for i in args + wins.push i.epath + end + tk_call "pack", 'configure', *(wins+hash_kv(keys)) + end + + def forget(*args) + tk_call 'pack', 'forget' *args + end + + def propagate(master, bool=None) + bool(tk_call('pack', 'propagate', mastaer.epath, bool)) + end + module_function :configure, :forget, :propagate +end + +module TkOption + include Tk + extend Tk + def add pat, value, pri=None + tk_call 'option', 'add', pat, value, pri + end + def clear + tk_call 'option', 'clear' + end + def get win, classname, name + tk_call 'option', 'get', classname, name + end + def readfile file, pri=None + tk_call 'option', 'readfile', file, pri + end + module_function :add, :clear, :get, :readfile +end + +class TkObject<TkKernel + include Tk + + def path + return @path + end + + def epath + return @path + end + + def tk_send(cmd, *rest) + tk_call path, cmd, *rest + end + private :tk_send + + def method_missing(id, *args) + if (args.length == 1) + configure id.id2name, args[0] + else + $@ = error_at + super + end + end + + def []=(id, val) + configure id, val + end + + def configure(slot, value) + if value == FALSE + value = "0" + elsif value.kind_of? Proc + value = install_cmd(value) + end + tk_call path, 'configure', "-#{slot}", value + end + + def configure_cmd(slot, value) + configure slot, install_cmd(value) + end + + def bind(context, cmd=Proc.new, args=nil) + _bind path, context, cmd, args + end + + def tk_trace_variable(v) + unless v.kind_of?(TkVariable) + fail ArgumentError, format("requires TkVariable given %s", v.type) + end + v + end + private :tk_trace_variable + + def destroy + tk_call 'trace', 'vdelete', @tk_vn, 'w', @var_id if @var_id + end +end + + +class TkVariable + include Tk + $tk_variable_id = "v00000" + def initialize(val="") + @id = $tk_variable_id + $tk_variable_id = $tk_variable_id.succ + tk_call(format('global %s; set %s', @id, @id), val) + end + + def id + @id + end + + def value + tk_call(format('global %s; set', @id), @id) + end + + def value=(val) + tk_call(format('global %s; set %s', @id, @id), val) + end + + def to_i + Integer(number(value)) + end + + def to_f + Float(number(value)) + end + + def to_s + String(string(value)) + end + + def inspect + format "<TkVariable: %s>", @id + end + + def to_a + list(value) + end +end + +class TkWindow<TkObject + $tk_window_id = "w00000" + def initialize(parent=nil, keys=nil) + id = $tk_window_id + $tk_window_id = $tk_window_id.succ + if !parent or parent == Tk.root + @path = format(".%s", id); + else + @path = format("%s.%s", parent.path, id) + end + $tk_window_list[@path] = self + create_self + if keys + tk_call @path, 'configure', *hash_kv(keys) + end + end + + def create_self + end + private :create_self + + def pack(keys = nil) + tk_call 'pack', epath, *hash_kv(keys) + self + end + + def unpack(keys = nil) + tk_call 'pack', 'forget', epath + self + end + + def place(keys = nil) + tk_call 'place', epath, *hash_kv(keys) + self + end + + def unplace(keys = nil) + tk_call 'place', 'forget', epath, *hash_kv(keys) + self + end + alias place_forget unplace + + def place_config(keys) + tk_call "place", 'configure', epath, *hash_kv(keys) + end + + def place_info() + ilist = list(tk_call('place', 'info', epath)) + info = {} + while key = ilist.shift + info[key[1,-1]] = ilist.shift + end + return info + end + + def place_slaves() + list(tk_call('place', 'slaves', epath)).collect { |w| + window(w) + } + end + + def focus + tk_call 'focus', path + self + end + + def grab(*args) + if !args or args.length == 0 + tk_call 'grab', 'set', path + elsif args.length == 1 + case args[0] + when 'global' + tk_call 'grab', 'set', '-global', path + else + val = tk_call('grab', arg[0], path) + end + case args[0] + when 'current' + return window(val) + when 'status' + return val + end + else + fail ArgumentError, 'wrong # of args' + end + end + + def lower(below=None) + tk_call 'lower', path, below + self + end + def raise(above=None) + tk_call 'raise', path, above + self + end + + def command(cmd=Proc.new) + configure_cmd 'command', cmd + end + + def colormodel model=None + tk_call 'tk', 'colormodel', path, model + self + end + + def destroy + tk_call 'destroy', path + if @cmdtbl + for id in @cmdtbl + uninstall_cmd id + end + end + $tk_window_list[path] = nil + super + end +end + +class TkRoot<TkWindow + include Wm + def TkRoot.new + return $tk_root if $tk_root + super + end + def path + "." + end + $tk_root = TkRoot.new + $tk_window_list['.'] = $tk_root +end + +class TkToplevel<TkWindow + include Wm + def initialize(parent=nil, screen=nil, classname=nil) + @screen = screen if screen + @classname = classname if classname + super + end + + def create_self + s = [] + s.push "-screen #@screen" if @screen + s.push "-class #@classname" if @classname + tk_call 'toplevel', path, *s + end +end + +class TkFrame<TkWindow + def create_self + tk_call 'frame', @path + end +end + +class TkLabel<TkWindow + def create_self + tk_call 'label', @path + end + def textvariable(v) + configure 'textvariable', tk_trace_variable(v) + end +end + +class TkButton<TkLabel + def create_self + tk_call 'button', @path + end + def invoke + tk_send 'invoke' + end + def flash + tk_send 'flash' + end +end + +class TkRadioButton<TkButton + def create_self + tk_call 'radiobutton', @path + end + def deselect + tk_send 'deselect' + end + def select + tk_send 'select' + end + def variable(v) + configure 'variable', tk_trace_variable(v) + end +end + +class TkCheckButton<TkRadioButton + def create_self + tk_call 'checkbutton', @path + end + def toggle + tk_send 'toggle' + end +end + +class TkMessage<TkLabel + def create_self + tk_call 'message', @path + end +end + +class TkScale<TkWindow + def create_self + tk_call 'scale', path + end + + def get + number(tk_send('get')) + end + + def set(val) + tk_send "set", val + end + + def value + get + end + + def value= (val) + set val + end +end + +class TkScrollbar<TkWindow + def create_self + tk_call 'scrollbar', path + end + + def delta(deltax=None, deltay=None) + number(tk_send('delta', deltax, deltay)) + end + + def fraction(x=None, y=None) + number(tk_send('fraction', x, y)) + end + + def identify(x=None, y=None) + tk_send('fraction', x, y) + end + + def get + ary1 = tk_send('get', path).split + ary2 = [] + for i in ary1 + push number(i) + end + ary2 + end + + def set(first, last) + tk_send "set", first, last + end +end + +# abstract class for Text and Listbox +class TkTextWin<TkWindow + def bbox(index) + tk_send 'bbox', index + end + def delete(first, last=None) + tk_send 'delete', first, last + end + def get(*index) + tk_send 'get', *index + end + def insert(index, *rest) + tk_send 'insert', index, *rest + end + def index(index) + tk_send 'index', index + end + def insert(index, chars, *args) + tk_send 'insert', index, chars, *args + end + def scan_mark(x, y) + tk_send 'scan', 'mark', x, y + end + def scan_dragto(x, y) + tk_send 'scan', 'dragto', x, y + end + def see(index) + tk_send 'see', index + end +end + +class TkListbox<TkTextWin + def create_self + tk_call 'listbox', path + end + + def curselection + tk_send 'curselection' + end + def nearest(y) + tk_send 'nearest', y + end + def selection_anchor(index) + tk_send 'selection', 'anchor', index + end + def selection_clear(first, last=None) + tk_send 'selection', 'clear', first, last + end + def selection_includes + bool(tk_send('selection', 'includes')) + end + def selection_set(first, last=None) + tk_send 'selection', 'set', first, last + end + def xview(cmd, index, *more) + tk_send 'xview', cmd, index, *more + end + def yview(cmd, index, *more) + tk_send 'yview', cmd, index, *more + end +end + +class TkMenu<TkWindow + def create_self + tk_call 'menu', path + end + def activate(index) + tk_send 'activate', index + end + def add(type, keys=nil) + tk_send 'add', type, *hash_kv(keys) + end + def index(index) + tk_send 'index', index + end + def invoke + tk_send 'invoke' + end + def insert(index, type, *keys) + tk_send 'add', index, type, *hash_kv(keys) + end + def post(x, y) + tk_send 'post', x, y + end + def postcascade(index) + tk_send 'postcascade', index + end + def postcommand(cmd=Proc.new) + configure_cmd 'postcommand', cmd + end + def menutype(index) + tk_send 'type', index + end + def unpost + tk_send 'unpost' + end + def yposition(index) + number(tk_send('yposition', index)) + end +end + +class TkMenubutton<TkLabel + def create_self + tk_call 'menubutton', path + end +end + +module TkComposite + def initialize(parent=nil, *args) + @frame = TkFrame.new(parent) + @path = @epath = @frame.path + initialize_composite(*args) + end + + def epath + @epath + end + + def initialize_composite(*args) end + private :initialize_composite + + def delegate(option, *wins) + @delegates = {} if not @delegates + @delegates['DEFAULT'] = @frame + if option.kind_of?(String) + @delegates[option] = wins + else + for i in option + @delegates[i] = wins + end + end + end + + def configure(slot, value) + if @delegates and @delegates[slot] + for i in @delegates[slot] + if not i + i = @delegates['DEFALUT'] + redo + else + last = i.configure(slot, value) + end + end + last + else + super + end + end +end + +autoload :TkCanvas, 'tkcanvas' +autoload :TkImage, 'tkcanvas' +autoload :TkBitmapImage, 'tkcanvas' +autoload :TkPhotoImage, 'tkcanvas' +autoload :TkEntry, 'tkentry' +autoload :TkText, 'tktext' diff --git a/lib/tkcanvas.rb b/lib/tkcanvas.rb new file mode 100644 index 0000000000..a02db097fd --- /dev/null +++ b/lib/tkcanvas.rb @@ -0,0 +1,326 @@ +# +# tkcanvas.rb - Tk canvas classes +# $Date$ +# by Yukihiro Matsumoto <matz@caelum.co.jp> + +require "tk" + +class TkCanvas<TkWindow + def create_self + tk_call 'canvas', path + end + def tagid(tag) + if tag.kind_of?(TkcItem) + tag.id + else + tag + end + end + private :tagid + def addtag(tag, *args) + tk_send 'addtag', tagid(tag), *args + end + def addtag_above(tagOrId) + addtag('above', tagOrId) + end + def addtag_all + addtag('all') + end + def addtag_below(tagOrId) + addtag('below', tagOrId) + end + def addtag_closest(x, y, halo=None, start=None) + addtag('closest', x, y, halo, start) + end + def addtag_enclosed(x1, y1, x2, y2) + addtag('enclosed', x1, y1, x2, y2) + end + def addtag_overlapping(x1, y1, x2, y2) + addtag('overlapping', x1, y1, x2, y2) + end + def addtag_withtag(tagOrId) + addtag('withtag', tagOrId) + end + def bbox(tag) + list(tk_send('bbox', tagid(tag))) + end + def itembind(tag, seq, cmd=Proc.new) + id = install_cmd(cmd) + tk_send 'bind', tagid(tag), "<#{seq}>", id + @cmdtbl.push id + end + def canvasx(x, *args) + tk_send 'canvasx', x, *args + end + def canvasy(y, *args) + tk_send 'canvasy', y, *args + end + def coords(tag, *args) + tk_send 'coords', tagid(tag), *args + end + def dchars(tag, first, last=None) + tk_send 'dchars', tagid(tag), first, last + end + def delete(*args) + tk_send 'delete', *args + end + alias remove delete + def dtag(tag, tag_to_del=None) + tk_send 'dtag', tagid(tag), tag_to_del + end + def find(*args) + tk_send 'find', *args + end + def itemfocus(tag) + tk_send 'find', tagid(tag) + end + def gettags(tag) + tk_send 'gettags', tagid(tag) + end + def icursor(tag, index) + tk_send 'icursor', tagid(tag), index + end + def index(tag) + tk_send 'index', tagid(tag), index + end + def lower(tag, below=None) + tk_send 'lower', tagid(tag), below + end + def move(tag, x, y) + tk_send 'move', tagid(tag), x, y + end + def itemtype(tag) + tk_send 'type', tagid(tag) + end + def postscript(keys) + tk_send "postscript", *hash_kv(keys) + end + def raise(tag, above=None) + tk_send 'raise', tagid(tag), above + end + def scale(tag, x, y, xs, ys) + tk_send 'scale', tagid(tag), x, y, xs, ys + end + def scan_mark(x, y) + tk_send 'scan', 'mark', x, y + end + def scan_dragto(x, y) + tk_send 'scan', 'dragto', x, y + end + def select(*args) + tk_send 'select', *args + end + def xview(*index) + tk_send 'xview', *index + end + def yview(*index) + tk_send 'yview', *index + end +end + +class TkcItem<TkObject + def initialize(parent, *args) + if not parent.kind_of?(TkCanvas) + fail format("%s need to be TkCanvas", parent.inspect) + end + @c = parent + @path = parent.path + if args[-1].kind_of? Hash + keys = args.pop + end + @id = create_self(*args) + if keys + tk_call @path, 'itemconfigure', @id, *hash_kv(keys) + end + end + def create_self(*args) end + private :create_self + def id + return @id + end + + def configure(slot, value) + tk_call path, 'itemconfigure', @id, "-#{slot}", value + end + + def addtag(tag) + @c.addtag(tag, 'withtag', @id) + end + def bbox + @c.bbox(@id) + end + def bind(seq, cmd=Proc.new) + @c.itembind @id, seq, cmd + end + def coords(*args) + @c.coords @id, *args + end + def dchars(first, last=None) + @c.dchars @id, first, last + end + def dtag(ttd) + @c.dtag @id, ttd + end + def focus + @c.focus @id + end + def gettags + @c.gettags @id + end + def icursor + @c.icursor @id + end + def index + @c.index @id + end + def insert(beforethis, string) + @c.insert @id, beforethis, string + end + def lower(belowthis=None) + @c.lower @id, belowthis + end + def move(xamount, yamount) + @c.move @id, xamount, yamount + end + def raise(abovethis=None) + @c.raise @id, abovethis + end + def scale(xorigin, yorigin, xscale, yscale) + @c.scale @id, xorigin, yorigin, xscale, yscale + end + def itemtype + @c.itemtype @id + end + def destroy + tk_call path, 'delete', @id + end +end + +class TkcArc<TkcItem + def create_self(*args) + tk_call(@path, 'create', 'arc', *args) + end +end +class TkcBitmap<TkcItem + def create_self(*args) + tk_call(@path, 'create', 'bitmap', *args) + end +end +class TkcImage<TkcItem + def create_self(*args) + tk_call(@path, 'create', 'image', *args) + end +end +class TkcLine<TkcItem + def create_self(*args) + tk_call(@path, 'create', 'line', *args) + end +end +class TkcOval<TkcItem + def create_self(*args) + tk_call(@path, 'create', 'oval', *args) + end +end +class TkcPolygon<TkcItem + def create_self(*args) + tk_call(@path, 'create', 'polygon', *args) + end +end +class TkcRectangle<TkcItem + def create_self(*args) + tk_call(@path, 'create', 'rectangle', *args) + end +end +class TkcText<TkcItem + def create_self(*args) + tk_call(@path, 'create', 'text', *args) + end +end +class TkcWindow<TkcItem + def create_self(*args) + tk_call(@path, 'create', 'window', *args) + end +end +class TkcGroup<TkcItem + $tk_group_id = 'tkg00000' + def create_self(*args) + @id = $tk_group_id + $tk_group_id = $tk_group_id.succ + end + + def add(*tags) + for i in tags + i.addtag @id + end + end + def add(*tags) + for i in tags + i.addtag @id + end + end + def delete(*tags) + for i in tags + i.delete @id + end + end + def list + @c.find 'withtag', @id + end + alias remove delete +end + + +class TkImage<TkObject + include Tk + + $tk_image_id = 'i00000' + def initialize(keys=nil) + @path = $tk_image_id + $tk_image_id = $tk_image_id.succ + tk_call 'image', 'create', @type, @path, *hash_kv(keys) + end + + def height + number(tk_call('image', 'height', @path)) + end + def itemtype + tk_call('image', 'type', @path) + end + def width + number(tk_call('image', 'height', @path)) + end + + def TkImage.names + tk_call('image', 'names', @path).split + end + def TkImage.types + tk_call('image', 'types', @path).split + end +end + +class TkBitmapImage<TkImage + def initialize(*args) + @type = 'bitmap' + super + end +end + +class TkPhotoImage<TkImage + def initialize(*args) + @type = 'photo' + super + end + + def blank + tk_send 'blank' + end + def cget + tk_send 'cget' + end + def get(x, y) + tk_send 'get', x, y + end + def put(data, to=None) + tk_send 'put', data, to + end +end diff --git a/lib/tkclass.rb b/lib/tkclass.rb new file mode 100644 index 0000000000..0b33d4ec8b --- /dev/null +++ b/lib/tkclass.rb @@ -0,0 +1,38 @@ +# +# tkclass.rb - Tk classes +# $Date$ +# by Yukihiro Matsumoto <matz@caelum.co.jp> + +require "tk" + +TopLevel = TkToplevel +Frame = TkFrame +Label = TkLabel +Button = TkButton +Radiobutton = TkRadioButton +Checkbutton = TkCheckButton +Message = TkMessage +Entry = TkEntry +Text = TkText +Scale = TkScale +Scrollbar = TkScrollbar +Listbox = TkListbox +Menu = TkMenu +Menubutton = TkMenubutton +Canvas = TkCanvas +Arc = TkcArc +Bitmap = TkcBitmap +Line = TkcLine +Oval = TkcOval +Polygon = TkcPolygon +Rectangle = TkcRectangle +TextItem = TkcText +WindowItem = TkcWindow +Selection = TkSelection +Winfo = TkWinfo +Pack = TkPack +Variable = TkVariable + +def Mainloop + Tk.mainloop +end diff --git a/lib/tkcore.rb b/lib/tkcore.rb new file mode 100644 index 0000000000..c151b0af9e --- /dev/null +++ b/lib/tkcore.rb @@ -0,0 +1,528 @@ +# +# tkcore.rb - Tk interface modue without thread +# $Date$ +# by Yukihiro Matsumoto <matz@caelum.co.jp> + +require "tkutil" +if defined? Thread + require "thread" +end + +module Tk + include TkUtil + extend Tk + + wish_path = nil + ENV['PATH'].split(":").each {|path| + for wish in ['wish4.2', 'wish4.1', 'wish4.0', 'wish'] + if File.exist? path+'/'+wish + wish_path = path+'/'+wish + break + end + break if wish_path + end + } + fail 'can\'t find wish' if not wish_path #' + + def Tk.tk_exit + if not PORT.closed? + PORT.print "exit\n" + PORT.close + end + end + +# PORT = open(format("|%s -n %s", wish_path, File.basename($0)), "w+"); + PORT = open(format("|%s", wish_path), "w+"); + trap "EXIT", proc{Tk.tk_exit} + trap "PIPE", "" + + def tk_write(*args) + printf PORT, *args; + PORT.print "\n" + PORT.flush + end + tk_write '\ +wm withdraw . +proc rb_out args { + puts [format %%s $args] + flush stdout +} +proc rb_ans arg { + if [catch $arg var] {puts "!$var"} {puts "=$var@@"} + flush stdout +} +proc tkerror args { exit } +proc keepalive {} { rb_out alive; after 120000 keepalive} +after 120000 keepalive' + + READABLE = [] + READ_CMD = {} + + def file_readable(port, cmd) + if cmd == nil + READABLE.delete port + else + READABLE.push port + end + READ_CMD[port] = cmd + end + + WRITABLE = [] + WRITE_CMD = {} + def file_writable(port, cmd) + if cmd == nil + WRITABLE.delete port + else + WRITABLE.push port + end + WRITE_CMD[port] = cmd + end + module_function :file_readable, :file_writable + + file_readable PORT, proc { + line = PORT.gets + exit if not line + Tk.dispatch(line.chop!) + } + + def error_at + frames = caller(1) + frames.delete_if do |c| + c =~ %r!/tk(|core|thcore|canvas|text|entry|scrollbox)\.rb:\d+! + end + frames + end + + def tk_tcl2ruby(val) + case val + when /^-?\d+$/ + val.to_i + when /^\./ + $tk_window_list[val] + when /^rb_out (c\d+)/ + $tk_cmdtbl[$1] + when / / + val.split.collect{|elt| + tk_tcl2ruby(elt) + } + when /^-?\d+\.\d*$/ + val.to_f + else + val + end + end + + def tk_split_list(str) + idx = str.index('{') + return tk_tcl2ruby(str) if not idx + + list = tk_tcl2ruby(str[0,idx]) + 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 str[0, i] == ' ' + list.push ' ' + else + list.push tk_split_list(str[0, i]) + end + list += tk_split_list(str[i+1..-1]) + list + end + private :tk_tcl2ruby, :tk_split_list + + def bool(val) + case bool + when "1", 1, 'yes', 'true' + TRUE + else + FALSE + end + end + def number(val) + case val + when /^-?\d+$/ + val.to_i + when /^-?\d+\.\d*$/ + val.to_f + else + val + end + end + def string(val) + if val == "{}" + '' + elsif val[0] == ?{ + val[1..-2] + else + val + end + end + def list(val) + tk_split_list(val) + end + def window(val) + $tk_window_list[val] + end + def procedure(val) + if val =~ /^rb_out (c\d+)/ + $tk_cmdtbl[$1] + else + nil + end + end + private :bool, :number, :string, :list, :window, :procedure + + # mark for non-given arguments + None = Object.new + def None.to_s + 'None' + end + + $tk_event_queue = [] + def tk_call(str, *args) + args = args.collect{|s| + next if s == None + if s.kind_of?(Hash) + s = hash_kv(s).join(" ") + else + if not s + s = "0" + elsif s == TRUE + s = "1" + elsif s.kind_of?(TkObject) + s = s.path + elsif s.kind_of?(TkVariable) + s = s.id + else + s = s.to_s + s.gsub!(/["\\\$\[\]]/, '\\\\\0') #" + s.gsub!(/\{/, '\\\\173') + s.gsub!(/\}/, '\\\\175') + end + "\"#{s}\"" + end + } + str += " " + str += args.join(" ") + print str, "\n" if $DEBUG + tk_write 'rb_ans {%s}', str + while PORT.gets + print $_ if $DEBUG + $_.chop! + if /^=(.*)@@$/ + val = $1 + break + elsif /^=/ + val = $' + "\n" + while TRUE + PORT.readline + if ~/@@$/ + val += $' + return val + else + val += $_ + end + end + elsif /^!/ + $@ = error_at + msg = $' + if msg =~ /unknown option "-(.*)"/ + $! = NameError.new(format("undefined method `%s' for %s(%s)", + $1, self, self.type)) #`' + else + $! = RuntimeError.new(format("%s - %s", self.type, msg)) + end + fail + end + $tk_event_queue.push $_ + end + + while ev = $tk_event_queue.shift + Tk.dispatch ev + end + fail 'wish closed' if PORT.closed? +# tk_split_list(val) + val + end + + def hash_kv(keys) + conf = [] + if keys + for k, v in keys + conf.push("-#{k}") + v = install_cmd(v) if v.kind_of? Proc + conf.push(v) + end + end + conf + end + private :tk_call, :error_at, :hash_kv + + $tk_cmdid = 0 + def install_cmd(cmd) + return '' if cmd == '' # uninstall cmd + id = format("c%.4d", $tk_cmdid) + $tk_cmdid += 1 + $tk_cmdtbl[id] = cmd + @cmdtbl = [] if not @cmdtbl + @cmdtbl.push id + return format('rb_out %s', id) + end + def uninstall_cmd(id) + $tk_cmdtbl[id] = nil + end + private :install_cmd, :uninstall_cmd + + $tk_window_list = {} + class Event + def initialize(seq,b,f,h,k,s,t,w,x,y,aa,ee,kk,nn,ww,tt,xx,yy) + @serial = seq + @num = b + @focus = (f == 1) + @height = h + @keycode = k + @state = s + @time = t + @width = w + @x = x + @y = y + @char = aa + @send_event = (ee == 1) + @keysym = kk + @keysym_num = nn + @type = tt + @widget = ww + @x_root = xx + @y_root = yy + end + attr :serial + attr :num + attr :focus + attr :height + attr :keycode + attr :state + attr :time + attr :width + attr :x + attr :y + attr :char + attr :send_event + attr :keysym + attr :keysym_num + attr :type + attr :widget + attr :x_root + attr :y_root + end + + def install_bind(cmd, args=nil) + if args + id = install_cmd(proc{|arg| + TkUtil.eval_cmd cmd, *arg + }) + id + " " + args + else + id = install_cmd(proc{|arg| + TkUtil.eval_cmd cmd, Event.new(*arg) + }) + id + " %# %b %f %h %k %s %t %w %x %y %A %E %K %N %W %T %X %Y" + end + end + + def _bind(path, context, cmd, args=nil) + begin + id = install_bind(cmd, args) + tk_call 'bind', path, "<#{context}>", id + rescue + $tk_cmdtbl[id] = nil + fail + end + end + private :install_bind, :_bind + + def bind_all(context, cmd=Proc.new, args=nil) + _bind 'all', context, cmd, args + end + + def pack(*args) + TkPack.configure *args + end + + $tk_cmdtbl = {} + + def after(ms, cmd=Proc.new) + myid = format("c%.4d", $tk_cmdid) + tk_call 'after', ms, + install_cmd(proc{ + TkUtil.eval_cmd cmd + uninstall_cmd myid + }) + end + + def update(idle=nil) + if idle + tk_call 'update', 'idletasks' + else + tk_call 'update' + end + end + + def dispatch(line) + if line =~ /^c\d+/ + cmd = $& + fail "no command `#{cmd}'" if not $tk_cmdtbl[cmd] + args = tk_split_list($') + TkUtil.eval_cmd $tk_cmdtbl[cmd], *args + elsif line =~ /^alive$/ + # keep alive, do nothing + else + fail "malformed line <#{line}>" + end + end + + def mainloop + begin + tk_write 'after idle {wm deiconify .}' + while TRUE + rf, wf = select(READABLE, WRITABLE) + for f in rf + READ_CMD[f].call(f) if READ_CMD[f] + if f.closed? + READABLE.delete f + READ_CMD[f] = nil + end + end + for f in wf + WRITE_CMD[f].call(f) if WRITE_CMD[f] + if f.closed? + WRITABLE.delete f + WRITE_CMD[f] = nil + end + end + end + ensure + Tk.tk_exit + end + end + + def root + $tk_root + end + + def bell + tk_call 'bell' + end + module_function :after, :update, :dispatch, :mainloop, :root, :bell + + module Scrollable + def xscrollcommand(cmd=Proc.new) + configure_cmd 'xscrollcommand', cmd + end + def yscrollcommand(cmd=Proc.new) + configure_cmd 'yscrollcommand', cmd + end + end + + module Wm + def aspect(*args) + w = window(tk_call('wm', 'grid', path, *args)) + w.split.collect{|s|s.to_i} if args.length == 0 + end + def client(name=None) + tk_call 'wm', 'client', path, name + end + def colormapwindows(*args) + list(tk_call('wm', 'colormapwindows', path, *args)) + end + def wm_command(value=None) + string(tk_call('wm', 'command', path, value)) + end + def deiconify + tk_call 'wm', 'deiconify', path + end + def focusmodel(*args) + tk_call 'wm', 'focusmodel', path, *args + end + def frame + tk_call 'wm', 'frame', path + end + def geometry(*args) + list(tk_call('wm', 'geometry', path, *args)) + end + def grid(*args) + w = tk_call('wm', 'grid', path, *args) + list(w) if args.size == 0 + end + def group(*args) + tk_call 'wm', 'path', path, *args + end + def iconbitmap(*args) + tk_call 'wm', 'bitmap', path, *args + end + def iconify + tk_call 'wm', 'iconify' + end + def iconmask(*args) + tk_call 'wm', 'iconmask', path, *args + end + def iconname(*args) + tk_call 'wm', 'iconname', path, *args + end + def iconposition(*args) + w = tk_call('wm', 'iconposition', path, *args) + list(w) if args.size == 0 + end + def iconwindow(*args) + tk_call 'wm', 'iconwindow', path, *args + end + def maxsize(*args) + w = tk_call('wm', 'maxsize', path, *args) + list(w) if not args.size == 0 + end + def minsize(*args) + w = tk_call('wm', 'minsize', path, *args) + list(w) if args.size == 0 + end + def overrideredirect(bool=None) + if bool == None + bool(tk_call('wm', 'overrideredirect', path)) + else + tk_call 'wm', 'overrideredirect', path, bool + end + end + def positionfrom(*args) + tk_call 'wm', 'positionfrom', path, *args + end + def protocol(name, func=None) + func = install_cmd(func) if not func == None + tk_call 'wm', 'command', path, name, func + end + def resizable(*args) + w = tk_call('wm', 'resizable', path, *args) + if args.length == 0 + list(w).collect{|e| bool(e)} + end + end + def sizefrom(*args) + list(tk_call('wm', 'sizefrom', path, *args)) + end + def state + tk_call 'wm', 'state', path + end + def title(*args) + tk_call 'wm', 'title', path, *args + end + def transient(*args) + tk_call 'wm', 'transient', path, *args + end + def withdraw + tk_call 'wm', 'withdraw', path + end + end +end diff --git a/lib/tkentry.rb b/lib/tkentry.rb new file mode 100644 index 0000000000..bcf092a15c --- /dev/null +++ b/lib/tkentry.rb @@ -0,0 +1,67 @@ +# +# tkentry.rb - Tk entry classes +# $Date$ +# by Yukihiro Matsumoto <matz@caelum.co.jp> + +require 'tk.rb' + +class TkEntry<TkLabel + def create_self + tk_call 'entry', @path + end + def scrollcommand(cmd) + configure 'scrollcommand', cmd + end + + def delete(s, e=None) + tk_send 'delete', s, e + end + + def cursor + tk_send 'index', 'insert' + end + def cursor=(index) + tk_send 'icursor', index + end + def index(index) + number(tk_send('index', index)) + end + def insert(pos,text) + tk_send 'insert', pos, text + end + def mark(pos) + tk_send 'scan', 'mark', pos + end + def dragto(pos) + tk_send 'scan', 'dragto', pos + end + def select_adjust(index) + tk_send 'select', 'adjust', index + end + def select_clear + tk_send 'select', 'clear', 'end' + end + def select_from(index) + tk_send 'select', 'from', index + end + def select_present() + tk_send('select', 'present') == 1 + end + def select_range(s, e) + tk_send 'select', 'range', s, e + end + def select_to(index) + tk_send 'select', 'to', index + end + def xview(*index) + tk_send 'xview', *index + end + + def value + tk_send 'get' + end + def value= (val) + tk_send 'delete', 0, 'end' + tk_send 'insert', 0, val + end +end diff --git a/lib/tkscrollbox.rb b/lib/tkscrollbox.rb new file mode 100644 index 0000000000..8d129b2f4b --- /dev/null +++ b/lib/tkscrollbox.rb @@ -0,0 +1,27 @@ +# +# tkscrollbox.rb - Tk Listbox with Scrollbar +# as an example of Composite Widget +# $Date$ +# by Yukihiro Matsumoto <matz@caelum.co.jp> + +require 'tk.rb' + +class TkScrollbox<TkListbox + include TkComposite + def initialize_composite + list = TkListbox.new(@frame) + scroll = TkScrollbar.new(@frame) + @path = list.path + + list.configure 'yscroll', scroll.path+" set" + list.pack 'side'=>'left','fill'=>'both','expand'=>'yes' + scroll.configure 'command', list.path+" yview" + scroll.pack 'side'=>'right','fill'=>'y' + + delegate('DEFAULT', list) + delegate('foreground', list) + delegate('background', list, scroll) + delegate('borderwidth', @frame) + delegate('relief', @frame) + end +end diff --git a/lib/tktext.rb b/lib/tktext.rb new file mode 100644 index 0000000000..2488f77793 --- /dev/null +++ b/lib/tktext.rb @@ -0,0 +1,164 @@ +# +# tktext.rb - Tk text classes +# $Date$ +# by Yukihiro Matsumoto <matz@caelum.co.jp> + +require 'tk.rb' + +class TkText<TkTextWin + include Scrollable + def create_self + tk_call 'text', @path + @tags = {} + end + def index(index) + tk_send 'index', index + end + def value + tk_send 'get', "1.0", "end" + end + def value= (val) + tk_send 'delete', "1.0", 'end' + tk_send 'insert', "1.0", val + end + def _addcmd(cmd) + @cmdtbl.push id + end + def _addtag(name, obj) + @tags[name] = obj + end + def tag_names + tk_send('tag', 'names').collect{|elt| + if not @tags[elt] + elt + else + @tags[elt] + end + } + end + def window_names + tk_send('window', 'names').collect{|elt| + if not @tags[elt] + elt + else + @tags[elt] + end + } + end + + def destroy + for t in @tags + t.destroy + end + super + end + + def backspace + self.delete 'insert' + end + + def compare(idx1, op, idx2) + bool(tk_send('compare', idx1, op, idx2)) + end + + def debug + bool(tk_send('debug')) + end + def debug=(boolean) + tk_send 'debug', boolean + end + + def yview(*what) + tk_send 'yview', *what + end + def yview_pickplace(*what) + tk_send 'yview', '-pickplace', *what + end + + def xview(*what) + tk_send 'xview', *what + end + def xview_pickplace(*what) + tk_send 'xview', '-pickplace', *what + end +end + +class TkTextTag<TkObject + $tk_text_tag = 'tag0000' + 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 = $tk_text_tag + $tk_text_tag = $tk_text_tag.succ + tk_call @t.path, "tag", "configure", @id, *hash_kv(keys) + @t._addtag id, self + end + def id + return @id + end + + def add(*index) + tk_call @t.path, 'tag', 'add', @id, *index + end + + def configure(keys) + tk_call @t.path, 'tag', 'configure', @id, *hash_kv(keys) + end + + def bind(seq, cmd=Proc.new) + id = install_cmd(cmd) + tk_call @t, 'tag', 'bind', tag, "<#{seq}>", id + @t._addcmd cmd + end + + def lower(below=None) + tk_call @t.path, 'tag', 'lower', below + end + + def destroy + tk_call @t.path, 'tag', 'delete', @id + end +end + +class TkTextMark<TkObject + $tk_text_mark = 'mark0000' + 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_text_mark + $tk_text_mark = $tk_text_mark.succ + tk_call @t.path, 'set', @id, index + @t._addtag id, self + end + def id + return @id + end + + def set(where) + tk_call @t.path, 'mark', 'unset', @id, where + end + + def unset + tk_call @t.path, 'mark', 'unset', @id + end + alias destroy unset +end + +class TkTextWindow<TkObject + def initialize(parent, index, *args) + if not parent.kind_of?(TkText) + fail format("%s need to be TkText", parent.inspect) + end + @t = parent + @path = @index = index + tk_call @t.path, 'window', 'create', index, *args + end + + def configure(slot, value) + tk_call @t.path, 'window', 'configure', @index, "-#{slot}", value + end +end diff --git a/lib/tkthcore.rb b/lib/tkthcore.rb new file mode 100644 index 0000000000..a6648502bd --- /dev/null +++ b/lib/tkthcore.rb @@ -0,0 +1,550 @@ +# +# tkthcore.rb - Tk interface modue using thread +# $Date$ +# by Yukihiro Matsumoto <matz@caelum.co.jp> + +require "tkutil" +require "thread" + +module Tk + include TkUtil + extend Tk + + def Tk.tk_exit + if not PORT.closed? + tk_write "exit" + PORT.close + end + end + + trap "EXIT", proc{Tk.tk_exit} + trap "PIPE", '' + + wish_path = nil + ENV['PATH'].split(":").each {|path| + for wish in ['wish4.2', 'wish4.1', 'wish4.0', 'wish'] + if File.exist? path+'/'+wish + wish_path = path+'/'+wish + break + end + break if wish_path + end + } + fail 'can\'t find wish' if not wish_path #' + + # mark for non-given arguments + None = Object.new + def None.to_s + 'None' + end + + Qin = Queue.new + Qout = Queue.new + Qwish = Queue.new + Qcmd = Queue.new + PORT = open(format("|%s -n %s", wish_path, File.basename($0)), "w+"); + + $tk_not_init = TRUE + + def Tk.init + $tk_not_init = FALSE + Thread.start do + loop do + while line = PORT.gets + line.chop! + if line =~ /^[=!]/ + Qwish.push line + else + Qcmd.push line + end + end + exit + end + end + + Thread.start do + ary = [PORT] + loop do + str = Qin.pop + print "Qin: ", str, "\n" if $DEBUG + tk_write 'if [catch {%s} var] {puts "!$var"} {puts "=$var@@"};flush stdout', str + line = tk_recv + Qout.push(line) + end + end + end + + $tk_event_queue = [] + def tk_recv() + val = nil + $_ = Qwish.pop + loop do + if /^=(.*)@@$/ + val = $1 + break + elsif /^=/ + val = $' + "\n" + while TRUE + PORT.readline + if ~/@@$/ + val += $' + return val + else + val += $_ + end + end + elsif /^!/ + $@ = error_at + msg = $' + if msg =~ /unknown option "-(.*)"/ + fail NameError, format("undefined method `%s' for %s(%s)", $1, self, self.type) #`' + else + fail format("%s - %s", self.type, msg) + end + end + end + + fail 'wish closed' if PORT.closed? +# tk_split_list(val) + val + end + + def tk_call(str, *args) + Tk.init if $tk_not_init + args = args.collect{|s| + next if s == None + if s.kind_of?(Hash) + s = hash_kv(s).join(" ") + else + if not s + s = "0" + elsif s == TRUE + s = "1" + elsif s.kind_of?(TkObject) + s = s.path + elsif s.kind_of?(TkVariable) + s = s.id + else + s = s.to_s + s.gsub!(/["\\\$\[\]]/, '\\\\\0') #" + s.gsub!(/\{/, '\\\\173') + s.gsub!(/\}/, '\\\\175') + end + "\"#{s}\"" + end + } + str += " " + str += args.join(" ") + Qin.push str + return Qout.pop + end + + def tk_write(*args) + PORT.printf *args; PORT.print "\n" + PORT.flush + end + module_function :tk_write, :tk_recv + tk_write '\ +wm withdraw . +proc rb_out args { + puts [format %%s $args] + flush stdout +} +proc tkerror args { exit } +proc keepalive {} { rb_out alive; after 120000 keepalive} +after 120000 keepalive' + + READ_TH = {} + def file_readable(port, cmd) + if cmd == nil + if READ_TH[port].has_key? + READ_TH[port].exit + READ_TH[port] = nil + end + else + READ_TH[port] = Thread.start{ + loop do + TkUtil.eval_cmd cmd + end + } + end + end + + WRITE_TH = {} + def file_writable(port, cmd) + if cmd == nil + if WRITE_TH[port].has_key? + WRITE_TH[port].exit + end + else + WRITE_TH[port] = Thread.start{ + loop do + TkUtil.eval_cmd cmd + end + } + end + end + module_function :file_readable, :file_writable + + def tk_tcl2ruby(val) + case val + when /^-?\d+$/ + val.to_i + when /^\./ + $tk_window_list[val] + when /^rb_out (c\d+)/ + $tk_cmdtbl[$1] + when / / + val.split.collect{|elt| + tk_tcl2ruby(elt) + } + when /^-?\d+\.\d*$/ + val.to_f + else + val + end + end + + def tk_split_list(str) + idx = str.index('{') + return tk_tcl2ruby(str) if not idx + + list = tk_tcl2ruby(str[0,idx]) + 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 str[0, i] == ' ' + list.push ' ' + else + list.push tk_split_list(str[0, i]) + end + list += tk_split_list(str[i+1..-1]) + list + end + private :tk_tcl2ruby, :tk_split_list + + def dispatch(line) + if line =~ /^c\d+/ + cmd = $& + fail "no command `#{cmd}'" if not $tk_cmdtbl[cmd] + args = tk_split_list($') + TkUtil.eval_cmd $tk_cmdtbl[cmd], *args + elsif line =~ /^alive$/ + # keep alive, do nothing + else + fail "malformed line <#{line}>" + end + end + module_function :dispatch + + def error_at + frames = caller(1) + frames.delete_if do |c| + c =~ %r!/tk(|core|thcore|canvas|text|entry|scrollbox)\.rb:\d+! + end + frames + end + + def bool(val) + case bool + when "1", 1, 'yes', 'true' + TRUE + else + FALSE + end + end + def number(val) + case val + when /^-?\d+$/ + val.to_i + when /^-?\d+\.\d*$/ + val.to_f + else + val + end + end + def string(val) + if val == "{}" + '' + elsif val[0] == ?{ + val[1..-2] + else + val + end + end + def list(val) + tk_split_list(val) + end + def window(val) + $tk_window_list[val] + end + def procedure(val) + if val =~ /^rb_out (c\d+)/ + $tk_cmdtbl[$1] + else + nil + end + end + private :bool, :number, :string, :list, :window, :procedure + + def hash_kv(keys) + conf = [] + if keys + for k, v in keys + conf.push("-#{k}") + v = install_cmd(v) if v.kind_of? Proc + conf.push(v) + end + end + conf + end + private :tk_call, :error_at, :hash_kv + + $tk_cmdid = 0 + def install_cmd(cmd) + return '' if cmd == '' # uninstall cmd + id = format("c%.4d", $tk_cmdid) + $tk_cmdid += 1 + $tk_cmdtbl[id] = cmd + @cmdtbl = [] if not @cmdtbl + @cmdtbl.push id + return format('rb_out %s', id) + end + def uninstall_cmd(id) + $tk_cmdtbl[id] = nil + end + private :install_cmd, :uninstall_cmd + + $tk_window_list = {} + class Event + def initialize(seq,b,f,h,k,s,t,w,x,y,aa,ee,kk,nn,ww,tt,xx,yy) + @serial = seq + @num = b + @focus = (f == 1) + @height = h + @keycode = k + @state = s + @time = t + @width = w + @x = x + @y = y + @char = aa + @send_event = (ee == 1) + @keysym = kk + @keysym_num = nn + @type = tt + @widget = ww + @x_root = xx + @y_root = yy + end + attr :serial + attr :num + attr :focus + attr :height + attr :keycode + attr :state + attr :time + attr :width + attr :x + attr :y + attr :char + attr :send_event + attr :keysym + attr :keysym_num + attr :type + attr :widget + attr :x_root + attr :y_root + end + + def install_bind(cmd, args=nil) + if args + id = install_cmd(proc{|arg| + TkUtil.eval_cmd cmd, *arg + }) + id + " " + args + else + id = install_cmd(proc{|arg| + TkUtil.eval_cmd cmd, Event.new(*arg) + }) + id + " %# %b %f %h %k %s %t %w %x %y %A %E %K %N %W %T %X %Y" + end + end + + def _bind(path, context, cmd, args=nil) + begin + id = install_bind(cmd, args) + tk_call 'bind', path, "<#{context}>", id + rescue + $tk_cmdtbl[id] = nil + fail + end + end + private :install_bind, :_bind + + def bind_all(context, cmd=Proc.new, args=nil) + _bind 'all', context, cmd, args + end + + def pack(*args) + TkPack.configure *args + end + + $tk_cmdtbl = {} + + Qafter = Queue.new + def after(ms, cmd=Proc.new) + unless $tk_after_thread + $tk_after_thread = Thread.start{ + loop do + cmd = Qafter.pop + TkUtil.eval_cmd cmd + end + } + end + Thread.start do + sleep Float(ms)/1000 + Qafter.push cmd + end + end + + def update(idle=nil) + if idle + tk_call 'update', 'idletasks' + else + tk_call 'update' + end + end + + def root + $tk_root + end + + def bell + tk_call 'bell' + end + + def mainloop + begin + tk_call 'after', 'idle', 'wm deiconify .' + loop do + dispatch Qcmd.pop + end + ensure + Tk.tk_exit + end + end + module_function :after, :update, :dispatch, :mainloop, :root, :bell + + module Scrollable + def xscrollcommand(cmd=Proc.new) + configure_cmd 'xscrollcommand', cmd + end + def yscrollcommand(cmd=Proc.new) + configure_cmd 'yscrollcommand', cmd + end + end + + module Wm + def aspect(*args) + w = window(tk_call('wm', 'grid', path, *args)) + w.split.collect{|s|s.to_i} if args.length == 0 + end + def client(name=None) + tk_call 'wm', 'client', path, name + end + def colormapwindows(*args) + list(tk_call('wm', 'colormapwindows', path, *args)) + end + def wm_command(value=None) + string(tk_call('wm', 'command', path, value)) + end + def deiconify + tk_call 'wm', 'deiconify', path + end + def focusmodel(*args) + tk_call 'wm', 'focusmodel', path, *args + end + def frame + tk_call 'wm', 'frame', path + end + def geometry(*args) + list(tk_call('wm', 'geometry', path, *args)) + end + def grid(*args) + w = tk_call('wm', 'grid', path, *args) + list(w) if args.size == 0 + end + def group(*args) + tk_call 'wm', 'path', path, *args + end + def iconbitmap(*args) + tk_call 'wm', 'bitmap', path, *args + end + def iconify + tk_call 'wm', 'iconify' + end + def iconmask(*args) + tk_call 'wm', 'iconmask', path, *args + end + def iconname(*args) + tk_call 'wm', 'iconname', path, *args + end + def iconposition(*args) + w = tk_call('wm', 'iconposition', path, *args) + list(w) if args.size == 0 + end + def iconwindow(*args) + tk_call 'wm', 'iconwindow', path, *args + end + def maxsize(*args) + w = tk_call('wm', 'maxsize', path, *args) + list(w) if not args.size == 0 + end + def minsize(*args) + w = tk_call('wm', 'minsize', path, *args) + list(w) if args.size == 0 + end + def overrideredirect(bool=None) + if bool == None + bool(tk_call('wm', 'overrideredirect', path)) + else + tk_call 'wm', 'overrideredirect', path, bool + end + end + def positionfrom(*args) + tk_call 'wm', 'positionfrom', path, *args + end + def protocol(name, func=None) + func = install_cmd(func) if not func == None + tk_call 'wm', 'command', path, name, func + end + def resizable(*args) + w = tk_call('wm', 'resizable', path, *args) + if args.length == 0 + list(w).collect{|e| bool(e)} + end + end + def sizefrom(*args) + list(tk_call('wm', 'sizefrom', path, *args)) + end + def state + tk_call 'wm', 'state', path + end + def title(*args) + tk_call 'wm', 'title', path, *args + end + def transient(*args) + tk_call 'wm', 'transient', path, *args + end + def withdraw + tk_call 'wm', 'withdraw', path + end + end +end diff --git a/lib/tracer.rb b/lib/tracer.rb new file mode 100644 index 0000000000..d37339fd62 --- /dev/null +++ b/lib/tracer.rb @@ -0,0 +1,75 @@ +class Tracer + MY_FILE_NAME_PATTERN = /^tracer\.(rb)?/ + Threads = Hash.new + Sources = Hash.new + + EVENT_SYMBOL = { + "line" => "-", + "call" => ">", + "return" => "<", + "class" => "C", + "end" => "E"} + + def on + set_trace_func proc{|event, file, line, id, binding| + trace_func event, file, line, id, binding + } + print "Trace on\n" + end + + def off + set_trace_func nil + print "Trace off\n" + end + + def get_thread_no + unless no = Threads[Thread.current.id] + Threads[Thread.current.id] = no = Threads.size + end + no + end + + def get_line(file, line) + unless list = Sources[file] + f =open(file) + begin + Sources[file] = list = f.readlines + ensure + f.close + end + end + list[line - 1] + end + + def trace_func(event, file, line, id, binding) + return if File.basename(file) =~ MY_FILE_NAME_PATTERN + + Thread.critical = TRUE + printf("#%d:%s:%d:%s: %s", + get_thread_no, + file, + line, + EVENT_SYMBOL[event], + get_line(file, line)) + Thread.critical = FALSE + end + + Single = new + def Tracer.on + Single.on + end + + def Tracer.off + Single.off + end + +end + +if File.basename($0) =~ Tracer::MY_FILE_NAME_PATTERN + $0 = ARGV.shift + + Tracer.on + load $0 +else + Tracer.on +end diff --git a/main.c b/main.c new file mode 100644 index 0000000000..4741f646e8 --- /dev/null +++ b/main.c @@ -0,0 +1,33 @@ +/************************************************ + + main.c - + + $Author$ + $Date$ + created at: Fri Aug 19 13:19:58 JST 1994 + +************************************************/ + +#include "ruby.h" + +#ifdef DJGPP +unsigned int _stklen = 0x100000; +#endif + +#ifdef __human68k__ +int _stacksize = 131072; +#endif + +int +main(argc, argv, envp) + int argc; + char **argv, **envp; +{ +#if defined(NT) + NtInitialize(&argc, &argv); +#endif + + ruby_init(); + ruby_options(argc, argv); + ruby_run(); +} diff --git a/math.c b/math.c new file mode 100644 index 0000000000..7b75a0110e --- /dev/null +++ b/math.c @@ -0,0 +1,135 @@ +/************************************************ + + math.c - + + $Author$ + $Date$ + created at: Tue Jan 25 14:12:56 JST 1994 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include <math.h> + +VALUE mMath; +VALUE float_new(); +VALUE f_float(); + +#define Need_Float(x) \ +if (FIXNUM_P(x)) {\ + (x) = (struct RFloat*)float_new((double)FIX2INT(x));\ +} else {\ + (x) = (struct RFloat*)f_float(x, x);\ +} + +#define Need_Float2(x,y) {\ + Need_Float(x);\ + Need_Float(y);\ +} + +static VALUE +math_atan2(obj, x, y) + VALUE obj; + struct RFloat *x, *y; +{ + Need_Float2(x, y); + return float_new(atan2(x->value, y->value)); +} + +static VALUE +math_cos(obj, x) + VALUE obj; + struct RFloat *x; +{ + Need_Float(x); + + return float_new(cos(x->value)); +} + +static VALUE +math_sin(obj, x) + VALUE obj; + struct RFloat *x; +{ + Need_Float(x); + + return float_new(sin(x->value)); +} + +static VALUE +math_tan(obj, x) + VALUE obj; + struct RFloat *x; +{ + Need_Float(x); + + return float_new(tan(x->value)); +} + +static VALUE +math_exp(obj, x) + VALUE obj; + struct RFloat *x; +{ + Need_Float(x); + return float_new(exp(x->value)); +} + +static VALUE +math_log(obj, x) + VALUE obj; + struct RFloat *x; +{ + Need_Float(x); + return float_new(log(x->value)); +} + +static VALUE +math_log10(obj, x) + VALUE obj; + struct RFloat *x; +{ + Need_Float(x); + return float_new(log10(x->value)); +} + +static VALUE +math_sqrt(obj, x) + VALUE obj; + struct RFloat *x; +{ + Need_Float(x); + + if (x->value < 0.0) ArgError("square root for negative number"); + return float_new(sqrt(x->value)); +} + +void +Init_Math() +{ + mMath = rb_define_module("Math"); + +#ifdef M_PI + rb_define_const(mMath, "PI", float_new(M_PI)); +#else + rb_define_const(mMath, "PI", float_new(atan(1.0)*4.0)); +#endif + +#ifdef M_E + rb_define_const(mMath, "E", float_new(M_E)); +#else + rb_define_const(mMath, "E", float_new(exp(1.0))); +#endif + + rb_define_module_function(mMath, "atan2", math_atan2, 2); + rb_define_module_function(mMath, "cos", math_cos, 1); + rb_define_module_function(mMath, "sin", math_sin, 1); + rb_define_module_function(mMath, "tan", math_tan, 1); + + rb_define_module_function(mMath, "exp", math_exp, 1); + rb_define_module_function(mMath, "log", math_log, 1); + rb_define_module_function(mMath, "log10", math_log10, 1); + rb_define_module_function(mMath, "sqrt", math_sqrt, 1); +} diff --git a/missing/alloca.c b/missing/alloca.c new file mode 100644 index 0000000000..6879618c8a --- /dev/null +++ b/missing/alloca.c @@ -0,0 +1,189 @@ +/* alloca -- (mostly) portable public-domain implementation -- D A Gwyn + + last edit: 86/05/30 rms + include config.h, since on VMS it renames some symbols. + Use xmalloc instead of malloc. + + This implementation of the PWB library alloca() function, + which is used to allocate space off the run-time stack so + that it is automatically reclaimed upon procedure exit, + was inspired by discussions with J. Q. Johnson of Cornell. + + It should work under any C implementation that uses an + actual procedure stack (as opposed to a linked list of + frames). There are some preprocessor constants that can + be defined when compiling for your specific system, for + improved efficiency; however, the defaults should be okay. + + The general concept of this implementation is to keep + track of all alloca()-allocated blocks, and reclaim any + that are found to be deeper in the stack than the current + invocation. This heuristic does not reclaim storage as + soon as it becomes invalid, but it will do so eventually. + + As a special case, alloca(0) reclaims storage without + allocating any. It is a good idea to use alloca(0) in + your main control loop, etc. to force garbage collection. +*/ +#ifndef lint +static char SCCSid[] = "@(#)alloca.c 1.1"; /* for the "what" utility */ +#endif + +#include "config.h" +#ifdef emacs +#ifdef static +/* actually, only want this if static is defined as "" + -- this is for usg, in which emacs must undefine static + in order to make unexec workable + */ +#ifndef STACK_DIRECTION +you +lose +-- must know STACK_DIRECTION at compile-time +#endif /* STACK_DIRECTION undefined */ +#endif /* static */ +#endif /* emacs */ + +#ifdef X3J11 +typedef void *pointer; /* generic pointer type */ +#else +typedef char *pointer; /* generic pointer type */ +#endif /* X3J11 */ + +#define NULL 0 /* null pointer constant */ + +extern void free(); +extern pointer xmalloc(); + +/* + Define STACK_DIRECTION if you know the direction of stack + growth for your system; otherwise it will be automatically + deduced at run-time. + + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown +*/ + +#ifndef STACK_DIRECTION +#define STACK_DIRECTION 0 /* direction unknown */ +#endif + +#if STACK_DIRECTION != 0 + +#define STACK_DIR STACK_DIRECTION /* known at compile-time */ + +#else /* STACK_DIRECTION == 0; need run-time code */ + +static int stack_dir; /* 1 or -1 once known */ +#define STACK_DIR stack_dir + +static void +find_stack_direction (/* void */) +{ + static char *addr = NULL; /* address of first + `dummy', once known */ + auto char dummy; /* to get stack address */ + + if (addr == NULL) + { /* initial entry */ + addr = &dummy; + + find_stack_direction (); /* recurse once */ + } + else /* second entry */ + if (&dummy > addr) + stack_dir = 1; /* stack grew upward */ + else + stack_dir = -1; /* stack grew downward */ +} + +#endif /* STACK_DIRECTION == 0 */ + +/* + An "alloca header" is used to: + (a) chain together all alloca()ed blocks; + (b) keep track of stack depth. + + It is very important that sizeof(header) agree with malloc() + alignment chunk size. The following default should work okay. +*/ + +#ifndef ALIGN_SIZE +#define ALIGN_SIZE sizeof(double) +#endif + +typedef union hdr +{ + char align[ALIGN_SIZE]; /* to force sizeof(header) */ + struct + { + union hdr *next; /* for chaining headers */ + char *deep; /* for stack depth measure */ + } h; +} header; + +/* + alloca( size ) returns a pointer to at least `size' bytes of + storage which will be automatically reclaimed upon exit from + the procedure that called alloca(). Originally, this space + was supposed to be taken from the current stack frame of the + caller, but that method cannot be made to work for some + implementations of C, for example under Gould's UTX/32. +*/ + +static header *last_alloca_header = NULL; /* -> last alloca header */ + +pointer +alloca (size) /* returns pointer to storage */ + unsigned size; /* # bytes to allocate */ +{ + auto char probe; /* probes stack depth: */ + register char *depth = &probe; + +#if STACK_DIRECTION == 0 + if (STACK_DIR == 0) /* unknown growth direction */ + find_stack_direction (); +#endif + + /* Reclaim garbage, defined as all alloca()ed storage that + was allocated from deeper in the stack than currently. */ + { + register header *hp; /* traverses linked list */ + + for (hp = last_alloca_header; hp != NULL;) + if (STACK_DIR > 0 && hp->h.deep > depth + || STACK_DIR < 0 && hp->h.deep < depth) + { + register header *np = hp->h.next; + + free ((pointer) hp); /* collect garbage */ + + hp = np; /* -> next header */ + } + else + break; /* rest are not deeper */ + + last_alloca_header = hp; /* -> last valid storage */ + } + + if (size == 0) + return NULL; /* no allocation required */ + + /* Allocate combined header + user data storage. */ + + { + register pointer new = xmalloc (sizeof (header) + size); + /* address of header */ + + ((header *)new)->h.next = last_alloca_header; + ((header *)new)->h.deep = depth; + + last_alloca_header = (header *)new; + + /* User storage begins just after header. */ + + return (pointer)((char *)new + sizeof(header)); + } +} + diff --git a/missing/crypt.c b/missing/crypt.c new file mode 100644 index 0000000000..9f9b562c36 --- /dev/null +++ b/missing/crypt.c @@ -0,0 +1,276 @@ +/* From Andy Tanenbaum's book "Computer Networks", + rewritten in C +*/ + +struct block { + unsigned char b_data[64]; +}; + +struct ordering { + unsigned char o_data[64]; +}; + +static struct block key; + +static struct ordering InitialTr = { + 58,50,42,34,26,18,10, 2,60,52,44,36,28,20,12, 4, + 62,54,46,38,30,22,14, 6,64,56,48,40,32,24,16, 8, + 57,49,41,33,25,17, 9, 1,59,51,43,35,27,19,11, 3, + 61,53,45,37,29,21,13, 5,63,55,47,39,31,23,15, 7, +}; + +static struct ordering FinalTr = { + 40, 8,48,16,56,24,64,32,39, 7,47,15,55,23,63,31, + 38, 6,46,14,54,22,62,30,37, 5,45,13,53,21,61,29, + 36, 4,44,12,52,20,60,28,35, 3,43,11,51,19,59,27, + 34, 2,42,10,50,18,58,26,33, 1,41, 9,49,17,57,25, +}; + +static struct ordering swap = { + 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48, + 49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64, + 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16, + 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, +}; + +static struct ordering KeyTr1 = { + 57,49,41,33,25,17, 9, 1,58,50,42,34,26,18, + 10, 2,59,51,43,35,27,19,11, 3,60,52,44,36, + 63,55,47,39,31,23,15, 7,62,54,46,38,30,22, + 14, 6,61,53,45,37,29,21,13, 5,28,20,12, 4, +}; + +static struct ordering KeyTr2 = { + 14,17,11,24, 1, 5, 3,28,15, 6,21,10, + 23,19,12, 4,26, 8,16, 7,27,20,13, 2, + 41,52,31,37,47,55,30,40,51,45,33,48, + 44,49,39,56,34,53,46,42,50,36,29,32, +}; + +static struct ordering etr = { + 32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, + 8, 9,10,11,12,13,12,13,14,15,16,17, + 16,17,18,19,20,21,20,21,22,23,24,25, + 24,25,26,27,28,29,28,29,30,31,32, 1, +}; + +static struct ordering ptr = { + 16, 7,20,21,29,12,28,17, 1,15,23,26, 5,18,31,10, + 2, 8,24,14,32,27, 3, 9,19,13,30, 6,22,11, 4,25, +}; + +static unsigned char s_boxes[8][64] = { +{ 14, 4,13, 1, 2,15,11, 8, 3,10, 6,12, 5, 9, 0, 7, + 0,15, 7, 4,14, 2,13, 1,10, 6,12,11, 9, 5, 3, 8, + 4, 1,14, 8,13, 6, 2,11,15,12, 9, 7, 3,10, 5, 0, + 15,12, 8, 2, 4, 9, 1, 7, 5,11, 3,14,10, 0, 6,13, +}, + +{ 15, 1, 8,14, 6,11, 3, 4, 9, 7, 2,13,12, 0, 5,10, + 3,13, 4, 7,15, 2, 8,14,12, 0, 1,10, 6, 9,11, 5, + 0,14, 7,11,10, 4,13, 1, 5, 8,12, 6, 9, 3, 2,15, + 13, 8,10, 1, 3,15, 4, 2,11, 6, 7,12, 0, 5,14, 9, +}, + +{ 10, 0, 9,14, 6, 3,15, 5, 1,13,12, 7,11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6,10, 2, 8, 5,14,12,11,15, 1, + 13, 6, 4, 9, 8,15, 3, 0,11, 1, 2,12, 5,10,14, 7, + 1,10,13, 0, 6, 9, 8, 7, 4,15,14, 3,11, 5, 2,12, +}, + +{ 7,13,14, 3, 0, 6, 9,10, 1, 2, 8, 5,11,12, 4,15, + 13, 8,11, 5, 6,15, 0, 3, 4, 7, 2,12, 1,10,14, 9, + 10, 6, 9, 0,12,11, 7,13,15, 1, 3,14, 5, 2, 8, 4, + 3,15, 0, 6,10, 1,13, 8, 9, 4, 5,11,12, 7, 2,14, +}, + +{ 2,12, 4, 1, 7,10,11, 6, 8, 5, 3,15,13, 0,14, 9, + 14,11, 2,12, 4, 7,13, 1, 5, 0,15,10, 3, 9, 8, 6, + 4, 2, 1,11,10,13, 7, 8,15, 9,12, 5, 6, 3, 0,14, + 11, 8,12, 7, 1,14, 2,13, 6,15, 0, 9,10, 4, 5, 3, +}, + +{ 12, 1,10,15, 9, 2, 6, 8, 0,13, 3, 4,14, 7, 5,11, + 10,15, 4, 2, 7,12, 9, 5, 6, 1,13,14, 0,11, 3, 8, + 9,14,15, 5, 2, 8,12, 3, 7, 0, 4,10, 1,13,11, 6, + 4, 3, 2,12, 9, 5,15,10,11,14, 1, 7, 6, 0, 8,13, +}, + +{ 4,11, 2,14,15, 0, 8,13, 3,12, 9, 7, 5,10, 6, 1, + 13, 0,11, 7, 4, 9, 1,10,14, 3, 5,12, 2,15, 8, 6, + 1, 4,11,13,12, 3, 7,14,10,15, 6, 8, 0, 5, 9, 2, + 6,11,13, 8, 1, 4,10, 7, 9, 5, 0,15,14, 2, 3,12, +}, + +{ 13, 2, 8, 4, 6,15,11, 1,10, 9, 3,14, 5, 0,12, 7, + 1,15,13, 8,10, 3, 7, 4,12, 5, 6,11, 0,14, 9, 2, + 7,11, 4, 1, 9,12,14, 2, 0, 6,10,13,15, 3, 5, 8, + 2, 1,14, 7, 4,10, 8,13,15,12, 9, 0, 3, 5, 6,11, +}, +}; + +static int rots[] = { + 1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1, +}; + +static void transpose(struct block *data, struct ordering *t, int n) +{ + struct block x; + + x = *data; + + while (n-- > 0) { + data->b_data[n] = x.b_data[t->o_data[n] - 1]; + } +} + +static void rotate(struct block *key) +{ + register unsigned char *p = key->b_data; + register unsigned char *ep = &(key->b_data[55]); + int data0 = key->b_data[0], data28 = key->b_data[28]; + + while (p++ < ep) *(p-1) = *p; + key->b_data[27] = (char) data0; + key->b_data[55] = (char) data28; +} + +static struct ordering *EP = &etr; + +static void f(int i, struct block *key, struct block *a, struct block *x) +{ + struct block e, ikey, y; + int k; + register unsigned char *p, *q, *r; + + e = *a; + transpose(&e, EP, 48); + for (k = rots[i]; k; k--) rotate(key); + ikey = *key; + transpose(&ikey, &KeyTr2, 48); + p = &(y.b_data[48]); + q = &(e.b_data[48]); + r = &(ikey.b_data[48]); + while (p > y.b_data) { + *--p = *--q ^ *--r; + } + q = x->b_data; + for (k = 0; k < 8; k++) { + register int xb, r; + + r = *p++ << 5; + r += *p++ << 3; + r += *p++ << 2; + r += *p++ << 1; + r += *p++; + r += *p++ << 4; + + xb = s_boxes[k][r]; + + *q++ = (char) (xb >> 3) & 1; + *q++ = (char) (xb>>2) & 1; + *q++ = (char) (xb>>1) & 1; + *q++ = (char) (xb & 1); + } + transpose(x, &ptr, 32); +} + +void definekey(char *k) +{ + + key = *((struct block *) k); + transpose(&key, &KeyTr1, 56); +} + +void encrypt(char *blck, int edflag) +{ + register struct block *p = (struct block *) blck; + register int i; + + transpose(p, &InitialTr, 64); + for (i = 15; i>= 0; i--) { + int j = edflag ? i : 15 - i; + register int k; + struct block b, x; + + b = *p; + for (k = 31; k >= 0; k--) { + p->b_data[k] = b.b_data[k + 32]; + } + f(j, &key, p, &x); + for (k = 31; k >= 0; k--) { + p->b_data[k+32] = b.b_data[k] ^ x.b_data[k]; + } + } + transpose(p, &swap, 64); + transpose(p, &FinalTr, 64); +} + +char *crypt(char *pw, char *salt) +{ + + char pwb[66]; + static char result[16]; + register char *p = pwb; + struct ordering new_etr; + register int i; + + while (*pw && p < &pwb[64]) { + register int j = 7; + + while (j--) { + *p++ = (*pw >> j) & 01; + } + pw++; + *p++ = 0; + } + while (p < &pwb[64]) *p++ = 0; + + definekey(p = pwb); + + while (p < &pwb[66]) *p++ = 0; + + new_etr = etr; + EP = &new_etr; + for (i = 0; i < 2; i++) { + register char c = *salt++; + register int j; + + result[i] = c; + if ( c > 'Z') c -= 6 + 7 + '.'; /* c was a lower case letter */ + else if ( c > '9') c -= 7 + '.';/* c was upper case letter */ + else c -= '.'; /* c was digit, '.' or '/'. */ + /* now, 0 <= c <= 63 */ + for (j = 0; j < 6; j++) { + if ((c >> j) & 01) { + int t = 6*i + j; + int temp = new_etr.o_data[t]; + new_etr.o_data[t] = new_etr.o_data[t+24]; + new_etr.o_data[t+24] = (char) temp; + } + } + } + + if (result[1] == 0) result[1] = result[0]; + + for (i = 0; i < 25; i++) encrypt(pwb,0); + EP = &etr; + + p = pwb; + pw = result+2; + while (p < &pwb[66]) { + register int c = 0; + register int j = 6; + + while (j--) { + c <<= 1; + c |= *p++; + } + c += '.'; /* becomes >= '.' */ + if (c > '9') c += 7; /* not in [./0-9], becomes upper */ + if (c > 'Z') c += 6; /* not in [A-Z], becomes lower */ + *pw++ = (char) c; + } + *pw = 0; + return result; +} diff --git a/missing/dir.h b/missing/dir.h new file mode 100644 index 0000000000..5c6281fdc2 --- /dev/null +++ b/missing/dir.h @@ -0,0 +1,248 @@ +/* $RCSfile: dir.h,v $$Revision: 4.0.1.1 $$Date: 91/06/07 11:22:10 $ + * + * (C) Copyright 1987, 1990 Diomidis Spinellis. + * + * You may distribute under the terms of either the GNU General Public + * License or the Artistic License, as specified in the README file. + * + * $Log: dir.h,v $ + * Revision 4.0.1.1 91/06/07 11:22:10 lwall + * patch4: new copyright notice + * + * Revision 4.0 91/03/20 01:34:20 lwall + * 4.0 baseline. + * + * Revision 3.0.1.1 90/03/27 16:07:08 lwall + * patch16: MSDOS support + * + * Revision 1.1 90/03/18 20:32:29 dds + * Initial revision + * + * + */ + +/* + * defines the type returned by the directory(3) functions + */ + +#ifndef __DIR_INCLUDED +#define __DIR_INCLUDED + +/*Directory entry size */ +#ifdef DIRSIZ +#undef DIRSIZ +#endif +#define DIRSIZ(rp) (sizeof(struct direct)) + +/* + * Structure of a directory entry + */ +struct direct { + ino_t d_ino; /* inode number (not used by MS-DOS) */ + int d_namlen; /* Name length */ + char d_name[256]; /* file name */ +}; + +struct _dir_struc { /* Structure used by dir operations */ + char *start; /* Starting position */ + char *curr; /* Current position */ + long size; /* Size of string table */ + long nfiles; /* number if filenames in table */ + struct direct dirstr; /* Directory structure to return */ +}; + +typedef struct _dir_struc DIR; /* Type returned by dir operations */ + +DIR *cdecl opendir(char *filename); +struct direct *readdir(DIR *dirp); +long telldir(DIR *dirp); +void seekdir(DIR *dirp,long loc); +void rewinddir(DIR *dirp); +void closedir(DIR *dirp); + +#endif /* __DIR_INCLUDED */ +/* $RCSfile: dir.h,v $$Revision: 4.0.1.1 $$Date: 91/06/07 11:22:10 $ + * + * (C) Copyright 1987, 1990 Diomidis Spinellis. + * + * You may distribute under the terms of either the GNU General Public + * License or the Artistic License, as specified in the README file. + * + * $Log: dir.h,v $ + * Revision 4.0.1.1 91/06/07 11:22:10 lwall + * patch4: new copyright notice + * + * Revision 4.0 91/03/20 01:34:20 lwall + * 4.0 baseline. + * + * Revision 3.0.1.1 90/03/27 16:07:08 lwall + * patch16: MSDOS support + * + * Revision 1.1 90/03/18 20:32:29 dds + * Initial revision + * + * + */ + +/* + * defines the type returned by the directory(3) functions + */ + +#ifndef __DIR_INCLUDED +#define __DIR_INCLUDED + +/*Directory entry size */ +#ifdef DIRSIZ +#undef DIRSIZ +#endif +#define DIRSIZ(rp) (sizeof(struct direct)) + +/* + * Structure of a directory entry + */ +struct direct { + ino_t d_ino; /* inode number (not used by MS-DOS) */ + int d_namlen; /* Name length */ + char d_name[256]; /* file name */ +}; + +struct _dir_struc { /* Structure used by dir operations */ + char *start; /* Starting position */ + char *curr; /* Current position */ + struct direct dirstr; /* Directory structure to return */ +}; + +typedef struct _dir_struc DIR; /* Type returned by dir operations */ + +DIR *cdecl opendir(char *filename); +struct direct *readdir(DIR *dirp); +long telldir(DIR *dirp); +void seekdir(DIR *dirp,long loc); +void rewinddir(DIR *dirp); +void closedir(DIR *dirp); + +#endif /* __DIR_INCLUDED */ +/* $RCSfile: dir.h,v $$Revision: 4.0.1.1 $$Date: 91/06/07 11:22:10 $ + * + * (C) Copyright 1987, 1990 Diomidis Spinellis. + * + * You may distribute under the terms of either the GNU General Public + * License or the Artistic License, as specified in the README file. + * + * $Log: dir.h,v $ + * Revision 4.0.1.1 91/06/07 11:22:10 lwall + * patch4: new copyright notice + * + * Revision 4.0 91/03/20 01:34:20 lwall + * 4.0 baseline. + * + * Revision 3.0.1.1 90/03/27 16:07:08 lwall + * patch16: MSDOS support + * + * Revision 1.1 90/03/18 20:32:29 dds + * Initial revision + * + * + */ + +/* + * defines the type returned by the directory(3) functions + */ + +#ifndef __DIR_INCLUDED +#define __DIR_INCLUDED + +/*Directory entry size */ +#ifdef DIRSIZ +#undef DIRSIZ +#endif +#define DIRSIZ(rp) (sizeof(struct direct)) + +/* + * Structure of a directory entry + */ +struct direct { + ino_t d_ino; /* inode number (not used by MS-DOS) */ + int d_namlen; /* Name length */ + char d_name[256]; /* file name */ +}; + +struct _dir_struc { /* Structure used by dir operations */ + char *start; /* Starting position */ + char *curr; /* Current position */ + struct direct dirstr; /* Directory structure to return */ +}; + +typedef struct _dir_struc DIR; /* Type returned by dir operations */ + +DIR *cdecl opendir(char *filename); +struct direct *readdir(DIR *dirp); +long telldir(DIR *dirp); +void seekdir(DIR *dirp,long loc); +void rewinddir(DIR *dirp); +void closedir(DIR *dirp); + +#endif /* __DIR_INCLUDED */ +/* $RCSfile: dir.h,v $$Revision: 4.0.1.1 $$Date: 91/06/07 11:22:10 $ + * + * (C) Copyright 1987, 1990 Diomidis Spinellis. + * + * You may distribute under the terms of either the GNU General Public + * License or the Artistic License, as specified in the README file. + * + * $Log: dir.h,v $ + * Revision 4.0.1.1 91/06/07 11:22:10 lwall + * patch4: new copyright notice + * + * Revision 4.0 91/03/20 01:34:20 lwall + * 4.0 baseline. + * + * Revision 3.0.1.1 90/03/27 16:07:08 lwall + * patch16: MSDOS support + * + * Revision 1.1 90/03/18 20:32:29 dds + * Initial revision + * + * + */ + +/* + * defines the type returned by the directory(3) functions + */ + +#ifndef __DIR_INCLUDED +#define __DIR_INCLUDED + +/*Directory entry size */ +#ifdef DIRSIZ +#undef DIRSIZ +#endif +#define DIRSIZ(rp) (sizeof(struct direct)) + +/* + * Structure of a directory entry + */ +struct direct { + ino_t d_ino; /* inode number (not used by MS-DOS) */ + int d_namlen; /* Name length */ + char d_name[256]; /* file name */ +}; + +struct _dir_struc { /* Structure used by dir operations */ + char *start; /* Starting position */ + char *curr; /* Current position */ + long size; /* Size of string table */ + long nfiles; /* number if filenames in table */ + struct direct dirstr; /* Directory structure to return */ +}; + +typedef struct _dir_struc DIR; /* Type returned by dir operations */ + +DIR *cdecl opendir(char *filename); +struct direct *readdir(DIR *dirp); +long telldir(DIR *dirp); +void seekdir(DIR *dirp,long loc); +void rewinddir(DIR *dirp); +void closedir(DIR *dirp); + +#endif /* __DIR_INCLUDED */ diff --git a/missing/dup2.c b/missing/dup2.c new file mode 100644 index 0000000000..c541149d4b --- /dev/null +++ b/missing/dup2.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 1991, Larry Wall + * + * You may distribute under the terms of either the GNU General Public + * License or the Artistic License, as specified in the README file. + */ + +#include "defines.h" + +#if defined(HAVE_FCNTL) && defined(F_DUPFD) +# include <fcntl.h> +#endif + +int +dup2(oldfd,newfd) +int oldfd; +int newfd; +{ +#if defined(HAVE_FCNTL) && defined(F_DUPFD) + close(newfd); + return fcntl(oldfd, F_DUPFD, newfd); +#else + int fdtmp[256]; + int fdx = 0; + int fd; + + if (oldfd == newfd) + return 0; + close(newfd); + while ((fd = dup(oldfd)) != newfd) /* good enough for low fd's */ + fdtmp[fdx++] = fd; + while (fdx > 0) + close(fdtmp[--fdx]); + return 0; +#endif +} diff --git a/missing/file.h b/missing/file.h new file mode 100644 index 0000000000..0207dddb3a --- /dev/null +++ b/missing/file.h @@ -0,0 +1,32 @@ +/* This is file FILE.H */ +/* +** Copyright (C) 1991 DJ Delorie, 24 Kirsten Ave, Rochester NH 03867-2954 +** +** This file is distributed under the terms listed in the document +** "copying.dj", available from DJ Delorie at the address above. +** A copy of "copying.dj" should accompany this file; if not, a copy +** should be available from where this file was obtained. This file +** may not be distributed without a verbatim copy of "copying.dj". +** +** This file is distributed WITHOUT ANY WARRANTY; without even the implied +** warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#ifndef _FILE_H_ +#define _FILE_H_ + +#include <fcntl.h> + +#define L_SET 0 +#define L_CURR 1 +#define L_INCR 1 +#define L_XTND 2 + + +#define F_OK 0 /* does file exist */ + +#define X_OK 1 /* is it executable by caller */ +#define W_OK 2 /* is it writable by caller */ +#define R_OK 4 /* is it readable by caller */ + +#endif diff --git a/missing/flock.c b/missing/flock.c new file mode 100644 index 0000000000..a4a9544b56 --- /dev/null +++ b/missing/flock.c @@ -0,0 +1,90 @@ +#include "config.h" + +#if defined(HAVE_LOCKF) + +#include <unistd.h> +#include <errno.h> + +/* Emulate flock() with lockf() or fcntl(). This is just to increase + portability of scripts. The calls might not be completely + interchangeable. What's really needed is a good file + locking module. +*/ + +# ifndef F_ULOCK +# define F_ULOCK 0 /* Unlock a previously locked region */ +# endif +# ifndef F_LOCK +# define F_LOCK 1 /* Lock a region for exclusive use */ +# endif +# ifndef F_TLOCK +# define F_TLOCK 2 /* Test and lock a region for exclusive use */ +# endif +# ifndef F_TEST +# define F_TEST 3 /* Test a region for other processes locks */ +# endif + +/* These are the flock() constants. Since this sytems doesn't have + flock(), the values of the constants are probably not available. +*/ +# ifndef LOCK_SH +# define LOCK_SH 1 +# endif +# ifndef LOCK_EX +# define LOCK_EX 2 +# endif +# ifndef LOCK_NB +# define LOCK_NB 4 +# endif +# ifndef LOCK_UN +# define LOCK_UN 8 +# endif + +int +flock(fd, operation) + int fd; + int operation; +{ + int i; + switch (operation) { + + /* LOCK_SH - get a shared lock */ + case LOCK_SH: + /* LOCK_EX - get an exclusive lock */ + case LOCK_EX: + i = lockf (fd, F_LOCK, 0); + break; + + /* LOCK_SH|LOCK_NB - get a non-blocking shared lock */ + case LOCK_SH|LOCK_NB: + /* LOCK_EX|LOCK_NB - get a non-blocking exclusive lock */ + case LOCK_EX|LOCK_NB: + i = lockf (fd, F_TLOCK, 0); + if (i == -1) + if ((errno == EAGAIN) || (errno == EACCES)) + errno = EWOULDBLOCK; + break; + + /* LOCK_UN - unlock */ + case LOCK_UN: + i = lockf (fd, F_ULOCK, 0); + break; + + /* Default - can't decipher operation */ + default: + i = -1; + errno = EINVAL; + break; + } + return i; +} +#else +int +flock(fd, operation) + int fd; + int operation; +{ + rb_notimplement(); + return -1; +} +#endif diff --git a/missing/memmove.c b/missing/memmove.c new file mode 100644 index 0000000000..09e64702b6 --- /dev/null +++ b/missing/memmove.c @@ -0,0 +1,24 @@ +/* + * memmove --- move memories. + * + * We supply this routine for those systems that aren't standard yet. + */ + +char * +memmove (dst, src, n) + char *dst, *src; + int n; +{ + char *ret = dst; + + if (src < dst) { + src += n; + dst += n; + while (n--) + *--dst = *--src; + } + else if (dst < src) + while (n--) + *dst++ = *src++; + return ret; +} diff --git a/missing/mkdir.c b/missing/mkdir.c new file mode 100644 index 0000000000..ed1476db9a --- /dev/null +++ b/missing/mkdir.c @@ -0,0 +1,104 @@ +/* + * Written by Robert Rother, Mariah Corporation, August 1985. + * + * If you want it, it's yours. All I ask in return is that if you + * figure out how to do this in a Bourne Shell script you send me + * a copy. + * sdcsvax!rmr or rmr@uscd + * + * Severely hacked over by John Gilmore to make a 4.2BSD compatible + * subroutine. 11Mar86; hoptoad!gnu + * + * Modified by rmtodd@uokmax 6-28-87 -- when making an already existing dir, + * subroutine didn't return EEXIST. It does now. + */ + +#include <sys/stat.h> +#include <errno.h> +/* + * Make a directory. + */ +int +mkdir (dpath, dmode) + char *dpath; + int dmode; +{ + int cpid, status; + struct stat statbuf; + + if (stat (dpath, &statbuf) == 0) + { + errno = EEXIST; /* Stat worked, so it already exists */ + return -1; + } + + /* If stat fails for a reason other than non-existence, return error */ + if (errno != ENOENT) + return -1; + + switch (cpid = fork ()) + { + + case -1: /* Error in fork() */ + return (-1); /* Errno is set already */ + + case 0: /* Child process */ + /* + * Cheap hack to set mode of new directory. Since this + * child process is going away anyway, we zap its umask. + * FIXME, this won't suffice to set SUID, SGID, etc. on this + * directory. Does anybody care? + */ + status = umask (0); /* Get current umask */ + status = umask (status | (0777 & ~dmode)); /* Set for mkdir */ + execl ("/bin/mkdir", "mkdir", dpath, (char *) 0); + _exit (-1); /* Can't exec /bin/mkdir */ + + default: /* Parent process */ + while (cpid != wait (&status)); /* Wait for kid to finish */ + } + + if (WIFSIGNALED (status) || WEXITSTATUS (status) != 0) + { + errno = EIO; /* We don't know why, but */ + return -1; /* /bin/mkdir failed */ + } + + return 0; +} + +int +rmdir (dpath) + char *dpath; +{ + int cpid, status; + struct stat statbuf; + + if (stat (dpath, &statbuf) != 0) + { + /* Stat just set errno. We don't have to */ + return -1; + } + + switch (cpid = fork ()) + { + + case -1: /* Error in fork() */ + return (-1); /* Errno is set already */ + + case 0: /* Child process */ + execl ("/bin/rmdir", "rmdir", dpath, (char *) 0); + _exit (-1); /* Can't exec /bin/mkdir */ + + default: /* Parent process */ + while (cpid != wait (&status)); /* Wait for kid to finish */ + } + + if (WIFSIGNALED (status) || WEXITSTATUS (status) != 0) + { + errno = EIO; /* We don't know why, but */ + return -1; /* /bin/rmdir failed */ + } + + return 0; +} diff --git a/missing/nt.c b/missing/nt.c new file mode 100644 index 0000000000..1cd81fd28c --- /dev/null +++ b/missing/nt.c @@ -0,0 +1,2191 @@ +/* + * Copyright (c) 1993, Intergraph Corporation + * + * You may distribute under the terms of either the GNU General Public + * License or the Artistic License, as specified in the perl README file. + * + * Various Unix compatibility functions and NT specific functions. + * + * Some of this code was derived from the MSDOS port(s) and the OS/2 port. + * + */ + +#include "ruby.h" +#include <fcntl.h> +#include <process.h> +#include <sys/stat.h> +/* #include <sys/wait.h> */ +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <assert.h> + +#include <windows.h> +#include <winbase.h> +#include <wincon.h> +#include "nt.h" +#include "dir.h" +#ifndef index +#define index(x, y) strchr((x), (y)) +#endif + +#ifndef bool +#define bool int +#endif + +bool NtSyncProcess = TRUE; +#if 0 // declared in header file +extern char **environ; +#define environ _environ +#endif + +static bool NtHasRedirection (char *); +static int valid_filename(char *s); +static void StartSockets (); +static char *str_grow(struct RString *str, size_t new_size); + +char *NTLoginName; + +DWORD Win32System = (DWORD)-1; + +static DWORD +IdOS(void) +{ + static OSVERSIONINFO osver; + + if (osver.dwPlatformId != Win32System) { + memset(&osver, 0, sizeof(OSVERSIONINFO)); + osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&osver); + Win32System = osver.dwPlatformId; + } + return (Win32System); +} + +static int +IsWin95(void) { + return (IdOS() == VER_PLATFORM_WIN32_WINDOWS); +} + +static int +IsWinNT(void) { + return (IdOS() == VER_PLATFORM_WIN32_NT); +} + + +/* simulate flock by locking a range on the file */ + + +#define LK_ERR(f,i) ((f) ? (i = 0) : (errno = GetLastError())) +#define LK_LEN 0xffff0000 + +int +flock(int fd, int oper) +{ + OVERLAPPED o; + int i = -1; + HANDLE fh; + + fh = (HANDLE)_get_osfhandle(fd); + memset(&o, 0, sizeof(o)); + + if(IsWinNT()) { + switch(oper) { + case LOCK_SH: /* shared lock */ + LK_ERR(LockFileEx(fh, 0, 0, LK_LEN, 0, &o),i); + break; + case LOCK_EX: /* exclusive lock */ + LK_ERR(LockFileEx(fh, LOCKFILE_EXCLUSIVE_LOCK, 0, LK_LEN, 0, &o),i); + break; + case LOCK_SH|LOCK_NB: /* non-blocking shared lock */ + LK_ERR(LockFileEx(fh, LOCKFILE_FAIL_IMMEDIATELY, 0, LK_LEN, 0, &o),i); + break; + case LOCK_EX|LOCK_NB: /* non-blocking exclusive lock */ + LK_ERR(LockFileEx(fh, + LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY, + 0, LK_LEN, 0, &o),i); + if(errno == EDOM) errno = EWOULDBLOCK; + break; + case LOCK_UN: /* unlock lock */ + if (UnlockFileEx(fh, 0, LK_LEN, 0, &o)) { + i = 0; + } + else { + /* GetLastError() must returns `ERROR_NOT_LOCKED' */ + errno = EWOULDBLOCK; + } + if(errno == EDOM) errno = EWOULDBLOCK; + break; + default: /* unknown */ + errno = EINVAL; + break; + } + } + else if(IsWin95()) { + switch(oper) { + case LOCK_EX: + while(i == -1) { + LK_ERR(LockFile(fh, 0, 0, LK_LEN, 0), i); + if(errno != EDOM && i == -1) break; + } + break; + case LOCK_EX | LOCK_NB: + LK_ERR(LockFile(fh, 0, 0, LK_LEN, 0), i); + if(errno == EDOM) errno = EWOULDBLOCK; + break; + case LOCK_UN: + LK_ERR(UnlockFile(fh, 0, 0, LK_LEN, 0), i); + if(errno == EDOM) errno = EWOULDBLOCK; + break; + default: + errno = EINVAL; + break; + } + } + return i; +} + +#undef LK_ERR +#undef LK_LEN + + +#undef const +FILE *fdopen(int, const char *); + +#if 0 +void +sleep(unsigned int len) +{ + time_t end; + + end = time((time_t *)0) + len; + while (time((time_t *)0) < end) + ; +} +#endif + +// +// Initialization stuff +// +void +NtInitialize(int *argc, char ***argv) { + + WORD version; + int ret; + + // + // subvert cmd.exe\'s feeble attempt at command line parsing + // + *argc = NtMakeCmdVector((char *)GetCommandLine(), argv, TRUE); + + // + // Now set up the correct time stuff + // + + tzset(); + + // Initialize Winsock + // StartSockets(); +} + + +char *getlogin() +{ + char buffer[200]; + int len = 200; + extern char *NTLoginName; + + if (NTLoginName == NULL) { + if (GetUserName(buffer, &len)) { + NTLoginName = ALLOC_N(char, len+1); + strncpy(NTLoginName, buffer, len); + NTLoginName[len] = '\0'; + } + else { + NTLoginName = "<Unknown>"; + } + } + return NTLoginName; +} + + + +#if 1 +// popen stuff + +// +// use these so I can remember which index is which +// + +#define NtPipeRead 0 // index of pipe read descriptor +#define NtPipeWrite 1 // index of pipe write descriptor + +#define NtPipeSize 1024 // size of pipe buffer + +#define MYPOPENSIZE 256 // size of book keeping structure + +struct { + int inuse; + int pid; + HANDLE oshandle; + FILE *pipe; +} MyPopenRecord[MYPOPENSIZE]; + +int SafeFree(char **vec, int vecc) +{ + // vec + // | + // V ^---------------------V + // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + // | | | .... | NULL | | ..... |\0 | | ..... |\0 |... + // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + // |- elements+1 -| ^ 1st element ^ 2nd element + + char *p; + + p = (char *)(vec - (vecc * sizeof (char *) + 1)); + free(p); + + return 0; +} + + +static char *szInternalCmds[] = { + "cd", + "chdir", + "cls", + "copy", + "date", + "del", + "dir", + "echo", + "erase", + "label", + "md", + "mkdir", + "path", + "rd", + "rem", + "ren", + "rename", + "rmdir", + "set", + "start", + "time", + "type", + "ver", + "vol", +}; + +int +isInternalCmd(char *cmd) +{ + int fRet; + char **vec; + int vecc = NtMakeCmdVector(cmd, &vec, FALSE); + + SafeFree (vec, vecc); + + return 0; +} + + +FILE * +mypopen (char *cmd, char *mode) +{ + FILE *fp; + int saved, reading; + int pipemode; + int pipes[2]; + int pid; + int slot; + static initialized = 0; + + // + // if first time through, intialize our book keeping structure + // + + if (!initialized++) { + for (slot = 0; slot < MYPOPENSIZE; slot++) + MyPopenRecord[slot].inuse = FALSE; + } + + //printf("mypopen %s\n", cmd); + + // + // find a free popen slot + // + + for (slot = 0; slot < MYPOPENSIZE && MyPopenRecord[slot].inuse; slot++) + ; + + if (slot > MYPOPENSIZE) { + return NULL; + } + + // + // Figure out what we\'re doing... + // + + reading = (*mode == 'r') ? TRUE : FALSE; + pipemode = (*(mode+1) == 'b') ? O_BINARY : O_TEXT; + + // + // Now get a pipe + // + +#if 0 + if (_pipe(pipes, NtPipeSize, pipemode) == -1) { + return NULL; + } + + if (reading) { + + // + // we\'re reading from the pipe, so we must hook up the + // write end of the pipe to the new processes stdout. + // To do this we must save our file handle from stdout + // by _dup\'ing it, then setting our stdout to be the pipe\'s + // write descriptor. We must also make the write handle + // inheritable so the new process can use it. + + if ((saved = _dup(fileno(stdout))) == -1) { + _close(pipes[NtPipeRead]); + _close(pipes[NtPipeWrite]); + return NULL; + } + if (_dup2 (pipes[NtPipeWrite], fileno(stdout)) == -1) { + _close(pipes[NtPipeRead]); + _close(pipes[NtPipeWrite]); + return NULL; + } + } + else { + // + // must be writing to the new process. Do the opposite of + // the above, i.e. hook up the processes stdin to the read + // end of the pipe. + // + + if ((saved = _dup(fileno(stdin))) == -1) { + _close(pipes[NtPipeRead]); + _close(pipes[NtPipeWrite]); + return NULL; + } + if (_dup2(pipes[NtPipeRead], fileno(stdin)) == -1) { + _close(pipes[NtPipeRead]); + _close(pipes[NtPipeWrite]); + return NULL; + } + } + + // + // Start the new process. Must set _fileinfo to non-zero value + // for file descriptors to be inherited. Reset after the process + // is started. + // + + if (NtHasRedirection(cmd)) { + docmd: + pid = spawnlpe(_P_NOWAIT, "cmd.exe", "/c", cmd, 0, environ); + if (pid == -1) { + _close(pipes[NtPipeRead]); + _close(pipes[NtPipeWrite]); + return NULL; + } + } + else { + char **vec; + int vecc = NtMakeCmdVector(cmd, &vec, FALSE); + + //pid = spawnvpe (_P_NOWAIT, vec[0], vec, environ); + pid = spawnvpe (_P_WAIT, vec[0], vec, environ); + if (pid == -1) { + goto docmd; + } + Safefree (vec, vecc); + } + + if (reading) { + + // + // We need to close our instance of the inherited pipe write + // handle now that it's been inherited so that it will actually close + // when the child process ends. + // + + if (_close(pipes[NtPipeWrite]) == -1) { + _close(pipes[NtPipeRead]); + return NULL; + } + if (_dup2 (saved, fileno(stdout)) == -1) { + _close(pipes[NtPipeRead]); + return NULL; + } + _close(saved); + + // + // Now get a stream pointer to return to the calling program. + // + + if ((fp = (FILE *) fdopen(pipes[NtPipeRead], mode)) == NULL) { + return NULL; + } + } + else { + + // + // need to close our read end of the pipe so that it will go + // away when the write end is closed. + // + + if (_close(pipes[NtPipeRead]) == -1) { + _close(pipes[NtPipeWrite]); + return NULL; + } + if (_dup2 (saved, fileno(stdin)) == -1) { + _close(pipes[NtPipeWrite]); + return NULL; + } + _close(saved); + + // + // Now get a stream pointer to return to the calling program. + // + + if ((fp = (FILE *) fdopen(pipes[NtPipeWrite], mode)) == NULL) { + _close(pipes[NtPipeWrite]); + return NULL; + } + } + + // + // do the book keeping + // + + MyPopenRecord[slot].inuse = TRUE; + MyPopenRecord[slot].pipe = fp; + MyPopenRecord[slot].pid = pid; + + return fp; +#else + { + int p[2]; + + BOOL fRet; + HANDLE hInFile, hOutFile, hStdin, hStdout; + LPCSTR lpApplicationName = NULL; + LPTSTR lpCommandLine; + LPTSTR lpCmd2 = NULL; + DWORD dwCreationFlags; + STARTUPINFO aStartupInfo; + PROCESS_INFORMATION aProcessInformation; + SECURITY_ATTRIBUTES sa; + int fd; + + sa.nLength = sizeof (SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + + if (!reading) { + FILE *fp; + + fp = (_popen)(cmd, mode); + + MyPopenRecord[slot].inuse = TRUE; + MyPopenRecord[slot].pipe = fp; + MyPopenRecord[slot].pid = -1; + + if (!fp) + Fatal("cannot open pipe \"%s\" (%s)", cmd, strerror(errno)); + return fp; + } + + + fRet = CreatePipe(&hInFile, &hOutFile, &sa, 2048L); + if (!fRet) + Fatal("cannot open pipe \"%s\" (%s)", cmd, strerror(errno)); + + memset(&aStartupInfo, 0, sizeof (STARTUPINFO)); + memset(&aProcessInformation, 0, sizeof (PROCESS_INFORMATION)); + aStartupInfo.cb = sizeof (STARTUPINFO); + aStartupInfo.dwFlags = STARTF_USESTDHANDLES; + + if (reading) { + aStartupInfo.hStdInput = GetStdHandle(STD_OUTPUT_HANDLE);//hStdin; + aStartupInfo.hStdError = INVALID_HANDLE_VALUE; + //for save + DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_OUTPUT_HANDLE), + GetCurrentProcess(), &hStdout, + 0, FALSE, DUPLICATE_SAME_ACCESS + ); + //for redirect + DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE), + GetCurrentProcess(), &hStdin, + 0, TRUE, DUPLICATE_SAME_ACCESS + ); + aStartupInfo.hStdOutput = hOutFile; + } + else { + aStartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); //hStdout; + aStartupInfo.hStdError = INVALID_HANDLE_VALUE; + // for save + DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE), + GetCurrentProcess(), &hStdin, + 0, FALSE, DUPLICATE_SAME_ACCESS + ); + //for redirect + DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_OUTPUT_HANDLE), + GetCurrentProcess(), &hStdout, + 0, TRUE, DUPLICATE_SAME_ACCESS + ); + aStartupInfo.hStdInput = hInFile; + } + + dwCreationFlags = (NORMAL_PRIORITY_CLASS); + + lpCommandLine = cmd; + if (NtHasRedirection(cmd) || isInternalCmd(cmd)) { + lpApplicationName = getenv("COMSPEC"); + lpCmd2 = malloc(strlen(lpApplicationName) + 1 + strlen(cmd) + sizeof (" /c ")); + if (lpCmd2 == NULL) + Fatal("Mypopen: malloc failed"); + sprintf(lpCmd2, "%s %s%s", lpApplicationName, " /c ", cmd); + lpCommandLine = lpCmd2; + } + + fRet = CreateProcess(lpApplicationName, lpCommandLine, &sa, &sa, + sa.bInheritHandle, dwCreationFlags, NULL, NULL, &aStartupInfo, &aProcessInformation); + + if (!fRet) { + CloseHandle(hInFile); + CloseHandle(hOutFile); + Fatal("cannot fork for \"%s\" (%s)", cmd, strerror(errno)); + } + + CloseHandle(aProcessInformation.hThread); + + if (reading) { + HANDLE hDummy; + + fd = _open_osfhandle((long)hInFile, (_O_RDONLY | pipemode)); + CloseHandle(hOutFile); + DuplicateHandle(GetCurrentProcess(), hStdout, + GetCurrentProcess(), &hDummy, + 0, TRUE, (DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE) + ); + } + else { + HANDLE hDummy; + + fd = _open_osfhandle((long)hOutFile, (_O_WRONLY | pipemode)); + CloseHandle(hInFile); + DuplicateHandle(GetCurrentProcess(), hStdin, + GetCurrentProcess(), &hDummy, + 0, TRUE, (DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE) + ); + } + + if (fd == -1) + Fatal("cannot open pipe \"%s\" (%s)", cmd, strerror(errno)); + + + if ((fp = (FILE *) fdopen(fd, mode)) == NULL) + return NULL; + + if (lpCmd2) + free(lpCmd2); + + MyPopenRecord[slot].inuse = TRUE; + MyPopenRecord[slot].pipe = fp; + MyPopenRecord[slot].oshandle = (reading ? hInFile : hOutFile); + MyPopenRecord[slot].pid = (int)aProcessInformation.hProcess; + return fp; + } +#endif +} + +int +mypclose(FILE *fp) +{ + int i; + int exitcode; + + Sleep(100); + for (i = 0; i < MYPOPENSIZE; i++) { + if (MyPopenRecord[i].inuse && MyPopenRecord[i].pipe == fp) + break; + } + if (i >= MYPOPENSIZE) { + Fatal("Invalid file pointer passed to mypclose!\n"); + } + + // + // get the return status of the process + // + +#if 0 + if (_cwait(&exitcode, MyPopenRecord[i].pid, WAIT_CHILD) == -1) { + if (errno == ECHILD) { + fprintf(stderr, "mypclose: nosuch child as pid %x\n", + MyPopenRecord[i].pid); + } + } +#else + for (;;) { + if (GetExitCodeProcess((HANDLE)MyPopenRecord[i].pid, &exitcode)) { + if (exitcode == STILL_ACTIVE) { + //printf("Process is Active.\n"); + Sleep(100); + TerminateProcess((HANDLE)MyPopenRecord[i].pid, 0); // ugly... + continue; + } + else if (exitcode == 0) { + //printf("done.\n"); + break; + } + else { + //printf("never.\n"); + break; + } + } + } +#endif + + + // + // close the pipe + // + CloseHandle(MyPopenRecord[i].oshandle); + fflush(fp); + fclose(fp); + + // + // free this slot + // + + MyPopenRecord[i].inuse = FALSE; + MyPopenRecord[i].pipe = NULL; + MyPopenRecord[i].pid = 0; + + return exitcode; +} +#endif + +#if 1 + + +typedef char* CHARP; +/* + * The following code is based on the do_exec and do_aexec functions + * in file doio.c + */ + +int +do_spawn(cmd) +char *cmd; +{ + register char **a; + register char *s; + char **argv; + int status; + char *shell, *cmd2; + int mode = NtSyncProcess ? P_WAIT : P_NOWAIT; + + /* save an extra exec if possible */ + if ((shell = getenv("RUBYSHELL")) != 0) { + if (NtHasRedirection(cmd)) { + int i; + char *p; + char *argv[4]; + char *cmdline = ALLOC_N(char, (strlen(cmd) * 2 + 1)); + + p=cmdline; + *p++ = '"'; + for (s=cmd; *s;) { + if (*s == '"') + *p++ = '\\'; /* Escape d-quote */ + *p++ = *s++; + } + *p++ = '"'; + *p = '\0'; + + /* fprintf(stderr, "do_spawn: %s %s\n", shell, cmdline); */ + argv[0] = shell; + argv[1] = "-c"; + argv[2] = cmdline; + argv[4] = NULL; + status = spawnvpe(mode, argv[0], argv, environ); + /* return spawnle(mode, shell, shell, "-c", cmd, (char*)0, environ); */ + free(cmdline); + return status; + } + } + else if ((shell = getenv("COMSPEC")) != 0) { + if (NtHasRedirection(cmd) /* || isInternalCmd(cmd) */) { + do_comspec_shell: + return spawnle(mode, shell, shell, "/c", cmd, (char*)0, environ); + } + } + + argv = ALLOC_N(CHARP, (strlen(cmd) / 2 + 2)); + cmd2 = ALLOC_N(char, (strlen(cmd) + 1)); + strcpy(cmd2, cmd); + a = argv; + for (s = cmd2; *s;) { + while (*s && isspace(*s)) s++; + if (*s) + *(a++) = s; + while (*s && !isspace(*s)) s++; + if (*s) + *s++ = '\0'; + } + *a = NULL; + if (argv[0]) { + if ((status = spawnvpe(mode, argv[0], argv, environ)) == -1) { + free(argv); + free(cmd2); + return -1; + } + } + free(cmd2); + free(argv); + return status; +} + +#endif + +typedef struct _NtCmdLineElement { + struct _NtCmdLineElement *next, *prev; + char *str; + int len; + int flags; +} NtCmdLineElement; + +// +// Possible values for flags +// + +#define NTGLOB 0x1 // element contains a wildcard +#define NTMALLOC 0x2 // string in element was malloc'ed +#define NTSTRING 0x4 // element contains a quoted string + +NtCmdLineElement *NtCmdHead = NULL, *NtCmdTail = NULL; + +void +NtFreeCmdLine(void) +{ + NtCmdLineElement *ptr; + + while(NtCmdHead) { + ptr = NtCmdHead; + NtCmdHead = NtCmdHead->next; + free(ptr); + } + NtCmdHead = NtCmdTail = NULL; +} + +// +// This function expands wild card characters that were spotted +// during the parse phase. The idea here is to call FindFirstFile and +// FindNextFile with the wildcard pattern specified, and splice in the +// resulting list of new names. If the wildcard pattern doesn\'t match +// any existing files, just leave it in the list. +// + +void +NtCmdGlob (NtCmdLineElement *patt) +{ + WIN32_FIND_DATA fd; + HANDLE fh; + char buffer[512]; + NtCmdLineElement *tmphead, *tmptail, *tmpcurr; + + strncpy(buffer, patt->str, patt->len); + buffer[patt->len] = '\0'; + if ((fh = FindFirstFile (buffer, &fd)) == INVALID_HANDLE_VALUE) { + return; + } + tmphead = tmptail = NULL; + do { + tmpcurr = ALLOC(NtCmdLineElement); + if (tmpcurr == NULL) { + fprintf(stderr, "Out of Memory in globbing!\n"); + while (tmphead) { + tmpcurr = tmphead; + tmphead = tmphead->next; + free(tmpcurr->str); + free(tmpcurr); + } + return; + } + memset (tmpcurr, 0, sizeof(*tmpcurr)); + tmpcurr->len = strlen(fd.cFileName); + tmpcurr->str = ALLOC_N(char, tmpcurr->len+1); + if (tmpcurr->str == NULL) { + fprintf(stderr, "Out of Memory in globbing!\n"); + while (tmphead) { + tmpcurr = tmphead; + tmphead = tmphead->next; + free(tmpcurr->str); + free(tmpcurr); + } + return; + } + strcpy(tmpcurr->str, fd.cFileName); + tmpcurr->flags |= NTMALLOC; + if (tmptail) { + tmptail->next = tmpcurr; + tmpcurr->prev = tmptail; + tmptail = tmpcurr; + } + else { + tmptail = tmphead = tmpcurr; + } + } while(FindNextFile(fh, &fd)); + + // + // ok, now we\'ve got a list of files that matched the wildcard + // specification. Put it in place of the pattern structure. + // + + tmphead->prev = patt->prev; + tmptail->next = patt->next; + + if (tmphead->prev) + tmphead->prev->next = tmphead; + + if (tmptail->next) + tmptail->next->prev = tmptail; + + // + // Now get rid of the pattern structure + // + + if (patt->flags & NTMALLOC) + free(patt->str); + // free(patt); //TODO: memory leak occures here. we have to fix it. +} + +// +// Check a command string to determine if it has I/O redirection +// characters that require it to be executed by a command interpreter +// + +static bool +NtHasRedirection (char *cmd) +{ + int inquote = 0; + char quote = '\0'; + char *ptr ; + + // + // Scan the string, looking for redirection (< or >) or pipe + // characters (|) that are not in a quoted string + // + + for (ptr = cmd; *ptr; ptr++) { + + switch (*ptr) { + + case '\'': + case '\"': + if (inquote) { + if (quote == *ptr) { + inquote = 0; + quote = '\0'; + } + } + else { + quote = *ptr; + inquote++; + } + break; + + case '>': + case '<': + + if (!inquote) + return TRUE; + } + } + return FALSE; +} + + +int +NtMakeCmdVector (char *cmdline, char ***vec, int InputCmd) +{ + int cmdlen = strlen(cmdline); + int done, instring, globbing, quoted, len; + int newline, need_free = 0, i; + int elements, strsz; + int slashes = 0; + char *ptr, *base, *buffer; + char **vptr; + char quote; + NtCmdLineElement *curr; + + // + // just return if we don\'t have a command line + // + + if (cmdlen == 0) { + *vec = NULL; + return 0; + } + + // + // strip trailing white space + // + + ptr = cmdline+(cmdlen - 1); + while(ptr >= cmdline && isspace(*ptr)) + --ptr; + *++ptr = '\0'; + + // + // check for newlines and formfeeds. If we find any, make a new + // command string that replaces them with escaped sequences (\n or \f) + // + + for (ptr = cmdline, newline = 0; *ptr; ptr++) { + if (*ptr == '\n' || *ptr == '\f') + newline++; + } + + if (newline) { + base = ALLOC_N(char, strlen(cmdline) + 1 + newline + slashes); + if (base == NULL) { + fprintf(stderr, "malloc failed!\n"); + return 0; + } + for (i = 0, ptr = base; (unsigned) i < strlen(cmdline); i++) { + switch (cmdline[i]) { + case '\n': + *ptr++ = '\\'; + *ptr++ = 'n'; + break; + default: + *ptr++ = cmdline[i]; + } + } + *ptr = '\0'; + cmdline = base; + need_free++; + } + + // + // Ok, parse the command line, building a list of CmdLineElements. + // When we\'ve finished, and it\'s an input command (meaning that it\'s + // the processes argv), we\'ll do globing and then build the argument + // vector. + // The outer loop does one interation for each element seen. + // The inner loop does one interation for each character in the element. + // + + for (done = 0, ptr = cmdline; *ptr;) { + + // + // zap any leading whitespace + // + + while(isspace(*ptr)) + ptr++; + base = ptr; + + for (done = newline = globbing = instring = quoted = 0; + *ptr && !done; ptr++) { + + // + // Switch on the current character. We only care about the + // white-space characters, the wild-card characters, and the + // quote characters. + // + + switch (*ptr) { + case ' ': + case '\t': +#if 0 + case '/': // have to do this for NT/DOS option strings + + // + // check to see if we\'re parsing an option switch + // + + if (*ptr == '/' && base == ptr) + continue; +#endif + // + // if we\'re not in a string, then we\'re finished with this + // element + // + + if (!instring) + done++; + break; + + case '*': + case '?': + + // + // record the fact that this element has a wildcard character + // N.B. Don\'t glob if inside a single quoted string + // + + if (!(instring && quote == '\'')) + globbing++; + break; + + case '\n': + + // + // If this string contains a newline, mark it as such so + // we can replace it with the two character sequence "\n" + // (cmd.exe doesn\'t like raw newlines in strings...sigh). + // + + newline++; + break; + + case '\'': + case '\"': + + // + // if we\'re already in a string, see if this is the + // terminating close-quote. If it is, we\'re finished with + // the string, but not neccessarily with the element. + // If we\'re not already in a string, start one. + // + + if (instring) { + if (quote == *ptr) { + instring = 0; + quote = '\0'; + } + } + else { + instring++; + quote = *ptr; + quoted++; + } + break; + } + } + + // + // need to back up ptr by one due to last increment of for loop + // (if we got out by seeing white space) + // + + if (*ptr) + ptr--; + + // + // when we get here, we\'ve got a pair of pointers to the element, + // base and ptr. Base points to the start of the element while ptr + // points to the character following the element. + // + + curr = ALLOC(NtCmdLineElement); + if (curr == NULL) { + NtFreeCmdLine(); + fprintf(stderr, "Out of memory!!\n"); + *vec = NULL; + return 0; + } + memset (curr, 0, sizeof(*curr)); + + len = ptr - base; + + // + // if it\'s an input vector element and it\'s enclosed by quotes, + // we can remove them. + // + + if (InputCmd && + ((base[0] == '\"' && base[len-1] == '\"') || + (base[0] == '\'' && base[len-1] == '\''))) { + base++; + len -= 2; + } + + curr->str = base; + curr->len = len; + curr->flags |= (globbing ? NTGLOB : 0); + + // + // Now put it in the list of elements + // + if (NtCmdTail) { + NtCmdTail->next = curr; + curr->prev = NtCmdTail; + NtCmdTail = curr; + } + else { + NtCmdHead = NtCmdTail = curr; + } + } + + if (InputCmd) { + + // + // When we get here we\'ve finished parsing the command line. Now + // we need to run the list, expanding any globbing patterns. + // + + for(curr = NtCmdHead; curr; curr = curr->next) { + if (curr->flags & NTGLOB) { + NtCmdGlob(curr); + } + } + } + + // + // Almost done! + // Count up the elements, then allocate space for a vector of pointers + // (argv) and a string table for the elements. + // + + for (elements = 0, strsz = 0, curr = NtCmdHead; curr; curr = curr->next) { + elements++; + strsz += (curr->len + 1); + } + + len = (elements+1)*sizeof(char *) + strsz; + buffer = ALLOC_N(char, len); + if (buffer == NULL) { + fprintf(stderr, "Out of memory!!\n"); + NtFreeCmdLine(); + *vec = NULL; + return 0; + } + + memset (buffer, 0, len); + + // + // make vptr point to the start of the buffer + // and ptr point to the area we\'ll consider the string table. + // + // buffer (*vec) + // | + // V ^---------------------V + // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + // | | | .... | NULL | | ..... |\0 | | ..... |\0 |... + // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + // |- elements+1 -| ^ 1st element ^ 2nd element + + vptr = (char **) buffer; + + ptr = buffer + (elements+1) * sizeof(char *); + + for (curr = NtCmdHead; curr; curr = curr->next) { + strncpy (ptr, curr->str, curr->len); + ptr[curr->len] = '\0'; + *vptr++ = ptr; + ptr += curr->len + 1; + } + NtFreeCmdLine(); + *vec = (char **) buffer; + return elements; +} + + +#if 1 +// +// UNIX compatible directory access functions for NT +// + +// +// File names are converted to lowercase if the +// CONVERT_TO_LOWER_CASE variable is defined. +// + +#define CONVERT_TO_LOWER_CASE +#define PATHLEN 1024 + +// +// The idea here is to read all the directory names into a string table +// (separated by nulls) and when one of the other dir functions is called +// return the pointer to the current file name. +// + +DIR * +opendir(char *filename) +{ + DIR *p; + long len; + long idx; + char scannamespc[PATHLEN]; + char *scanname = scannamespc; + struct stat sbuf; + WIN32_FIND_DATA FindData; + HANDLE fh; + char root[PATHLEN]; + char volname[PATHLEN]; + DWORD serial, maxname, flags; + BOOL downcase; + char *dummy; + + // + // check to see if we\'ve got a directory + // + + if (stat (filename, &sbuf) < 0 || + sbuf.st_mode & _S_IFDIR == 0) { + return NULL; + } + + // + // check out the file system characteristics + // + if (GetFullPathName(filename, PATHLEN, root, &dummy)) { + if (dummy = strchr(root, '\\')) + *++dummy = '\0'; + if (GetVolumeInformation(root, volname, PATHLEN, + &serial, &maxname, &flags, 0, 0)) { + downcase = !(flags & FS_CASE_SENSITIVE); + } + } + else { + downcase = TRUE; + } + + // + // Get us a DIR structure + // + + p = xcalloc(sizeof(DIR), 1); + if (p == NULL) + return NULL; + + // + // Create the search pattern + // + + strcpy(scanname, filename); + + if (index("/\\", *(scanname + strlen(scanname) - 1)) == NULL) + strcat(scanname, "/*"); + else + strcat(scanname, "*"); + + // + // do the FindFirstFile call + // + + fh = FindFirstFile (scanname, &FindData); + if (fh == INVALID_HANDLE_VALUE) { + return NULL; + } + + // + // now allocate the first part of the string table for the + // filenames that we find. + // + + idx = strlen(FindData.cFileName)+1; + p->start = ALLOC_N(char, idx); + strcpy (p->start, FindData.cFileName); + if (downcase) + strlwr(p->start); + p->nfiles++; + + // + // loop finding all the files that match the wildcard + // (which should be all of them in this directory!). + // the variable idx should point one past the null terminator + // of the previous string found. + // + while (FindNextFile(fh, &FindData)) { + len = strlen (FindData.cFileName); + + // + // bump the string table size by enough for the + // new name and it's null terminator + // + + #define Renew(x, y, z) (x = (z *)realloc(x, y)) + + Renew (p->start, idx+len+1, char); + if (p->start == NULL) { + Fatal ("opendir: malloc failed!\n"); + } + strcpy(&p->start[idx], FindData.cFileName); + if (downcase) + strlwr(&p->start[idx]); + p->nfiles++; + idx += len+1; + } + FindClose(fh); + p->size = idx; + p->curr = p->start; + return p; +} + + +// +// Readdir just returns the current string pointer and bumps the +// string pointer to the next entry. +// + +struct direct * +readdir(DIR *dirp) +{ + int len; + static int dummy = 0; + + if (dirp->curr) { + + // + // first set up the structure to return + // + + len = strlen(dirp->curr); + strcpy(dirp->dirstr.d_name, dirp->curr); + dirp->dirstr.d_namlen = len; + + // + // Fake inode + // + dirp->dirstr.d_ino = dummy++; + + // + // Now set up for the next call to readdir + // + + dirp->curr += len + 1; + if (dirp->curr >= (dirp->start + dirp->size)) { + dirp->curr = NULL; + } + + return &(dirp->dirstr); + + } else + return NULL; +} + +// +// Telldir returns the current string pointer position +// + +long +telldir(DIR *dirp) +{ + return (long) dirp->curr; /* ouch! pointer to long cast */ +} + +// +// Seekdir moves the string pointer to a previously saved position +// (Saved by telldir). + +void +seekdir(DIR *dirp, long loc) +{ + dirp->curr = (char *) loc; /* ouch! long to pointer cast */ +} + +// +// Rewinddir resets the string pointer to the start +// + +void +rewinddir(DIR *dirp) +{ + dirp->curr = dirp->start; +} + +// +// This just free\'s the memory allocated by opendir +// + +void +closedir(DIR *dirp) +{ + free(dirp->start); + free(dirp); +} +#endif + + +// +// 98.2% of this code was lifted from the OS2 port. (JCW) +// + +#if 0 +// add_suffix is in util.c too. +/* + * Suffix appending for in-place editing under MS-DOS and OS/2 (and now NT!). + * + * Here are the rules: + * + * Style 0: Append the suffix exactly as standard perl would do it. + * If the filesystem groks it, use it. (HPFS will always + * grok it. So will NTFS. FAT will rarely accept it.) + * + * Style 1: The suffix begins with a '.'. The extension is replaced. + * If the name matches the original name, use the fallback method. + * + * Style 2: The suffix is a single character, not a '.'. Try to add the + * suffix to the following places, using the first one that works. + * [1] Append to extension. + * [2] Append to filename, + * [3] Replace end of extension, + * [4] Replace end of filename. + * If the name matches the original name, use the fallback method. + * + * Style 3: Any other case: Ignore the suffix completely and use the + * fallback method. + * + * Fallback method: Change the extension to ".$$$". If that matches the + * original name, then change the extension to ".~~~". + * + * If filename is more than 1000 characters long, we die a horrible + * death. Sorry. + * + * The filename restriction is a cheat so that we can use buf[] to store + * assorted temporary goo. + * + * Examples, assuming style 0 failed. + * + * suffix = ".bak" (style 1) + * foo.bar => foo.bak + * foo.bak => foo.$$$ (fallback) + * foo.$$$ => foo.~~~ (fallback) + * makefile => makefile.bak + * + * suffix = "~" (style 2) + * foo.c => foo.c~ + * foo.c~ => foo.c~~ + * foo.c~~ => foo~.c~~ + * foo~.c~~ => foo~~.c~~ + * foo~~~~~.c~~ => foo~~~~~.$$$ (fallback) + * + * foo.pas => foo~.pas + * makefile => makefile.~ + * longname.fil => longname.fi~ + * longname.fi~ => longnam~.fi~ + * longnam~.fi~ => longnam~.$$$ + * + */ + + +static char suffix1[] = ".$$$"; +static char suffix2[] = ".~~~"; + +#define ext (&buf[1000]) + +#define strEQ(s1,s2) (strcmp(s1,s2) == 0) + +void +add_suffix(struct RString *str, char *suffix) +{ + int baselen; + int extlen = strlen(suffix); + char *s, *t, *p; + int slen; + char buf[1024]; + + if (str->len > 1000) + Fatal("Cannot do inplace edit on long filename (%d characters)", str->len); + + /* Style 0 */ + slen = str->len; + str_cat(str, suffix, extlen); + if (valid_filename(str->ptr)) return; + + /* Fooey, style 0 failed. Fix str before continuing. */ + str->ptr[str->len = slen] = '\0'; + + slen = extlen; + t = buf; baselen = 0; s = str->ptr; + while ( (*t = *s) && *s != '.') { + baselen++; + if (*s == '\\' || *s == '/') baselen = 0; + s++; t++; + } + p = t; + + t = ext; extlen = 0; + while (*t++ = *s++) extlen++; + if (extlen == 0) { ext[0] = '.'; ext[1] = 0; extlen++; } + + if (*suffix == '.') { /* Style 1 */ + if (strEQ(ext, suffix)) goto fallback; + strcpy(p, suffix); + } else if (suffix[1] == '\0') { /* Style 2 */ + if (extlen < 4) { + ext[extlen] = *suffix; + ext[++extlen] = '\0'; + } else if (baselen < 8) { + *p++ = *suffix; + } else if (ext[3] != *suffix) { + ext[3] = *suffix; + } else if (buf[7] != *suffix) { + buf[7] = *suffix; + } else goto fallback; + strcpy(p, ext); + } else { /* Style 3: Panic */ +fallback: + (void)memcpy(p, strEQ(ext, suffix1) ? suffix2 : suffix1, 5); + } + str_grow(str, strlen(buf)); + memcpy(str->ptr, buf, str->len); +} +#endif + +static int +valid_filename(char *s) +{ + int fd; + + // + // if the file exists, then it\'s a valid filename! + // + + if (_access(s, 0) == 0) { + return 1; + } + + // + // It doesn\'t exist, so see if we can open it. + // + + if ((fd = _open(s, _O_CREAT, 0666)) >= 0) { + close(fd); + _unlink (s); // don\'t leave it laying around + return 1; + } + return 0; +} + + +// +// This is a clone of fdopen so that we can handle the +// brain damaged version of sockets that NT gets to use. +// +// The problem is that sockets are not real file handles and +// cannot be fdopen\'ed. This causes problems in the do_socket +// routine in doio.c, since it tries to create two file pointers +// for the socket just created. We\'ll fake out an fdopen and see +// if we can prevent perl from trying to do stdio on sockets. +// + +FILE * +fdopen (int fd, const char *mode) +{ + FILE *fp; + char sockbuf[80]; + int optlen; + int retval; + extern int errno; + + retval = getsockopt((SOCKET)fd, SOL_SOCKET, SO_TYPE, sockbuf, &optlen); + if (retval == SOCKET_ERROR) { + int iRet; + + iRet = WSAGetLastError(); + if (iRet == WSAENOTSOCK || iRet == WSANOTINITIALISED) + return (_fdopen(fd, mode)); + } + + // + // If we get here, then fd is actually a socket. + // + fp = xcalloc(sizeof(FILE), 1); +#if _MSC_VER < 800 + fileno(fp) = fd; +#else + fp->_file = fd; +#endif + if (*mode == 'r') + fp->_flag = _IOREAD; + else + fp->_flag = _IOWRT; + return fp; +} + + +// +// Since the errors returned by the socket error function +// WSAGetLastError() are not known by the library routine strerror +// we have to roll our own. +// + +#undef strerror + +char * +mystrerror(int e) +{ + static char buffer[512]; + extern int sys_nerr; + DWORD source = 0; + + if (e < 0 || e > sys_nerr) { + if (e < 0) + e = GetLastError(); + if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, &source, e, 0, + buffer, 512, NULL) == 0) { + strcpy (buffer, "Unknown Error"); + } + return buffer; + } + return strerror(e); + +} + +// +// various stubs +// + + +// Ownership +// +// Just pretend that everyone is a superuser. NT will let us know if +// we don\'t really have permission to do something. +// + +#define ROOT_UID 0 +#define ROOT_GID 0 + +UIDTYPE +getuid(void) +{ + return ROOT_UID; +} + +UIDTYPE +geteuid(void) +{ + return ROOT_UID; +} + +GIDTYPE +getgid(void) +{ + return ROOT_GID; +} + +GIDTYPE +getegid(void) +{ + return ROOT_GID; +} + +int +setuid(int uid) +{ + return (uid == ROOT_UID ? 0 : -1); +} + +int +setgid(int gid) +{ + return (gid == ROOT_GID ? 0 : -1); +} + +// +// File system stuff +// + +int +/* ioctl(int i, unsigned int u, char *data) */ +ioctl(int i, unsigned int u, long data) +{ + return -1; +} + + +// +// Networking trampolines +// These are used to avoid socket startup/shutdown overhead in case +// the socket routines aren\'t used. +// + +#undef select + +static int NtSocketsInitialized = 0; + +long +myselect (int nfds, fd_set *rd, fd_set *wr, fd_set *ex, + struct timeval *timeout) +{ + long r; + if (!NtSocketsInitialized++) { + StartSockets(); + } + if ((r = select (nfds, rd, wr, ex, timeout)) == SOCKET_ERROR) + errno = WSAGetLastError(); + return r; +} + +static void +StartSockets () { + WORD version; + WSADATA retdata; + int ret; + + // + // initalize the winsock interface and insure that it\'s + // cleaned up at exit. + // + version = MAKEWORD(1, 1); + if (ret = WSAStartup(version, &retdata)) + Fatal ("Unable to locate winsock library!\n"); + if (LOBYTE(retdata.wVersion) != 1) + Fatal("could not find version 1 of winsock dll\n"); + + if (HIBYTE(retdata.wVersion) != 1) + Fatal("could not find version 1 of winsock dll\n"); + + atexit((void (*)(void)) WSACleanup); +} + +#undef accept + +SOCKET +myaccept (SOCKET s, struct sockaddr *addr, int *addrlen) +{ + SOCKET r; + + if (!NtSocketsInitialized++) { + StartSockets(); + } + if ((r = accept (s, addr, addrlen)) == INVALID_SOCKET) + errno = WSAGetLastError(); + return r; +} + +#undef bind + +int +mybind (SOCKET s, struct sockaddr *addr, int addrlen) +{ + int r; + + if (!NtSocketsInitialized++) { + StartSockets(); + } + if ((r = bind (s, addr, addrlen)) == SOCKET_ERROR) + errno = WSAGetLastError(); + return r; +} + +#undef connect + +int +myconnect (SOCKET s, struct sockaddr *addr, int addrlen) +{ + int r; + if (!NtSocketsInitialized++) { + StartSockets(); + } + if ((r = connect (s, addr, addrlen)) == SOCKET_ERROR) + errno = WSAGetLastError(); + return r; +} + + +#undef getpeername + +int +mygetpeername (SOCKET s, struct sockaddr *addr, int *addrlen) +{ + int r; + if (!NtSocketsInitialized++) { + StartSockets(); + } + if ((r = getpeername (s, addr, addrlen)) == SOCKET_ERROR) + errno = WSAGetLastError(); + return r; +} + +#undef getsockname + +int +mygetsockname (SOCKET s, struct sockaddr *addr, int *addrlen) +{ + int r; + if (!NtSocketsInitialized++) { + StartSockets(); + } + if ((r = getsockname (s, addr, addrlen)) == SOCKET_ERROR) + errno = WSAGetLastError(); + return r; +} + +#undef getsockopt + +int +mygetsockopt (SOCKET s, int level, int optname, char *optval, int *optlen) +{ + int r; + if (!NtSocketsInitialized++) { + StartSockets(); + } + if ((r = getsockopt (s, level, optname, optval, optlen)) == SOCKET_ERROR) + errno = WSAGetLastError(); + return r; +} + +#undef ioctlsocket + +int +myioctlsocket (SOCKET s, long cmd, u_long *argp) +{ + int r; + if (!NtSocketsInitialized++) { + StartSockets(); + } + if ((r = ioctlsocket (s, cmd, argp)) == SOCKET_ERROR) + errno = WSAGetLastError(); + return r; +} + +#undef listen + +int +mylisten (SOCKET s, int backlog) +{ + int r; + if (!NtSocketsInitialized++) { + StartSockets(); + } + if ((r = listen (s, backlog)) == SOCKET_ERROR) + errno = WSAGetLastError(); + return r; +} + +#undef recv + +int +myrecv (SOCKET s, char *buf, int len, int flags) +{ + int r; + if (!NtSocketsInitialized++) { + StartSockets(); + } + if ((r = recv (s, buf, len, flags)) == SOCKET_ERROR) + errno = WSAGetLastError(); + return r; +} + +#undef recvfrom + +int +myrecvfrom (SOCKET s, char *buf, int len, int flags, + struct sockaddr *from, int *fromlen) +{ + int r; + if (!NtSocketsInitialized++) { + StartSockets(); + } + if ((r = recvfrom (s, buf, len, flags, from, fromlen)) == SOCKET_ERROR) + errno = WSAGetLastError(); + return r; +} + +#undef send + +int +mysend (SOCKET s, char *buf, int len, int flags) +{ + int r; + if (!NtSocketsInitialized++) { + StartSockets(); + } + if ((r = send (s, buf, len, flags)) == SOCKET_ERROR) + errno = WSAGetLastError(); + return r; +} + +#undef sendto + +int +mysendto (SOCKET s, char *buf, int len, int flags, + struct sockaddr *to, int tolen) +{ + int r; + if (!NtSocketsInitialized++) { + StartSockets(); + } + if ((r = sendto (s, buf, len, flags, to, tolen)) == SOCKET_ERROR) + errno = WSAGetLastError(); + return r; +} + +#undef setsockopt + +int +mysetsockopt (SOCKET s, int level, int optname, char *optval, int optlen) +{ + int r; + if (!NtSocketsInitialized++) { + StartSockets(); + } + if ((r = setsockopt (s, level, optname, optval, optlen)) == SOCKET_ERROR) + errno = WSAGetLastError(); + return r; +} + +#undef shutdown + +int +myshutdown (SOCKET s, int how) +{ + int r; + if (!NtSocketsInitialized++) { + StartSockets(); + } + if ((r = shutdown (s, how)) == SOCKET_ERROR) + errno = WSAGetLastError(); + return r; +} + +#undef socket + +SOCKET +mysocket (int af, int type, int protocol) +{ + SOCKET s; + if (!NtSocketsInitialized++) { + StartSockets(); + } + if ((s = socket (af, type, protocol)) == INVALID_SOCKET) + errno = WSAGetLastError(); + return s; +} + +#undef gethostbyaddr + +struct hostent * +mygethostbyaddr (char *addr, int len, int type) +{ + struct hostent *r; + if (!NtSocketsInitialized++) { + StartSockets(); + } + if ((r = gethostbyaddr (addr, len, type)) == NULL) + errno = WSAGetLastError(); + return r; +} + +#undef gethostbyname + +struct hostent * +mygethostbyname (char *name) +{ + struct hostent *r; + if (!NtSocketsInitialized++) { + StartSockets(); + } + if ((r = gethostbyname (name)) == NULL) + errno = WSAGetLastError(); + return r; +} + +#undef gethostname + +int +mygethostname (char *name, int len) +{ + int r; + if (!NtSocketsInitialized++) { + StartSockets(); + } + if ((r = gethostname (name, len)) == SOCKET_ERROR) + errno = WSAGetLastError(); + return r; +} + +#undef getprotobyname + +struct protoent * +mygetprotobyname (char *name) +{ + struct protoent *r; + if (!NtSocketsInitialized++) { + StartSockets(); + } + if ((r = getprotobyname (name)) == NULL) + errno = WSAGetLastError(); + return r; +} + +#undef getprotobynumber + +struct protoent * +mygetprotobynumber (int num) +{ + struct protoent *r; + if (!NtSocketsInitialized++) { + StartSockets(); + } + if ((r = getprotobynumber (num)) == NULL) + errno = WSAGetLastError(); + return r; +} + +#undef getservbyname + +struct servent * +mygetservbyname (char *name, char *proto) +{ + struct servent *r; + if (!NtSocketsInitialized++) { + StartSockets(); + } + if ((r = getservbyname (name, proto)) == NULL) + errno = WSAGetLastError(); + return r; +} + +#undef getservbyport + +struct servent * +mygetservbyport (int port, char *proto) +{ + struct servent *r; + if (!NtSocketsInitialized++) { + StartSockets(); + } + if ((r = getservbyport (port, proto)) == NULL) + errno = WSAGetLastError(); + return r; +} + +// +// Networking stubs +// + +void endhostent() {} +void endnetent() {} +void endprotoent() {} +void endservent() {} + +struct netent *getnetent (void) {return (struct netent *) NULL;} + +struct netent *getnetbyaddr(char *name) {return (struct netent *)NULL;} + +struct netent *getnetbyname(long net, int type) {return (struct netent *)NULL;} + +struct protoent *getprotoent (void) {return (struct protoent *) NULL;} + +struct servent *getservent (void) {return (struct servent *) NULL;} + +void sethostent (int stayopen) {} + +void setnetent (int stayopen) {} + +void setprotoent (int stayopen) {} + +void setservent (int stayopen) {} + + +#ifndef WNOHANG +#define WNOHANG -1 +#endif + +pid_t +waitpid (pid_t pid, int *stat_loc, int options) +{ + DWORD timeout; + + if (options == WNOHANG) { + timeout = 0; + } else { + timeout = INFINITE; + } + if (WaitForSingleObject((HANDLE) pid, timeout) == WAIT_OBJECT_0) { + pid = _cwait(stat_loc, pid, 0); + return pid; + } + return 0; +} + +#include <sys/timeb.h> + +void _cdecl +gettimeofday(struct timeval *tv, struct timezone *tz) +{ + struct timeb tb; + + ftime(&tb); + tv->tv_sec = tb.time; + tv->tv_usec = tb.millitm * 1000; +} + +char * +getcwd(buffer, size) + char *buffer; + int size; +{ + int length; + char *bp; + + if (_getcwd(buffer, size) == NULL) { + return NULL; + } + length = strlen(buffer); + if (length >= size) { + return NULL; + } + + for (bp = buffer; *bp != '\0'; bp++) { + if (*bp == '\\') { + *bp = '/'; + } + } + return buffer; +} + +static char * +str_grow(struct RString *str, size_t new_size) +{ + char *p; + + p = realloc(str->ptr, new_size); + if (p == NULL) + Fatal("cannot grow string\n"); + + str->len = new_size; + str->ptr = p; + + return p; +} + +int +chown(char *path, int owner, int group) +{ + return 0; +} + +int +kill(int pid, int sig) +{ +#if 1 + if (pid == GetCurrentProcessId()) + return raise(sig); + + if (sig == 2 && pid > 0) + if (GenerateConsoleCtrlEvent(CTRL_C_EVENT, (DWORD)pid)) + return 0; + + return -1; +#else + return 0; +#endif +} + +int +link(char *from, char *to) +{ + return -1; +} + +int +wait() +{ + return 0; +} + diff --git a/missing/nt.h b/missing/nt.h new file mode 100644 index 0000000000..5977a59a1d --- /dev/null +++ b/missing/nt.h @@ -0,0 +1,225 @@ +#ifndef EXT_NT_H +#define EXT_NT_H + +/* + * Copyright (c) 1993, Intergraph Corporation + * + * You may distribute under the terms of either the GNU General Public + * License or the Artistic License, as specified in the perl README file. + * + */ + +// +// Definitions for NT port of Perl +// + +// +// GRRRR!!!! Windows Nonsense. +// Define the following so we don't get tons of extra stuff +// when we include windows.h +// +#if 0 +#define NOGDICAPMASKS +#define NOVIRTUALKEYCODES +#define NOWINMESSAGES +#define NOWINSTYLES +#define NOSYSMETRICS +#define NOMENUS +#define NOICONS +#define NOKEYSTATES +#define NOSYSCOMMANDS +#define NORASTEROPS +#define NOSHOWWINDOW +#define OEMRESOURCE +#define NOATOM +#define NOCLIPBOARD +#define NOCOLOR +#define NOCTLMGR +#define NODRAWTEXT +#define NOGDI +//#define NOKERNEL +//#define NOUSER +#define NONLS +#define NOMB +#define NOMEMMGR +#define NOMETAFILE +#define NOMINMAX +#define NOMSG +#define NOOPENFILE +#define NOSCROLL +#define NOSERVICE +#define NOSOUND +#define NOTEXTMETRIC +#define NOWH +#define NOWINOFFSETS +#define NOCOMM +#define NOKANJI +#define NOHELP +#define NOPROFILER +#define NODEFERWINDOWPOS +#endif + +// +// Ok now we can include the normal include files. +// + +// #include <stdarg.h> conflict with varargs.h? +// There is function-name conflitct, so we rename it +#if !defined(IN) && !defined(FLOAT) +#define OpenFile WINAPI_OpenFile +#include <windows.h> +#include <winsock.h> +#undef OpenFile +#endif +// +// We\'re not using Microsoft\'s "extensions" to C for +// Structured Exception Handling (SEH) so we can nuke these +// +#undef try +#undef except +#undef finally +#undef leave +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <direct.h> +#include <process.h> +#include <time.h> +#include <math.h> +#include <sys/types.h> +#include <sys/utime.h> + +// +// Grrr... +// + +#define access _access +#define chmod _chmod +#define chsize _chsize +#define close _close +#define creat _creat +#define dup _dup +#define dup2 _dup2 +#define eof _eof +#define filelength _filelength +#define isatty _isatty +#define locking _locking +#define lseek _lseek +#define mktemp _mktemp +#define open _open +#define read _read +#define setmode _setmode +#define sopen _sopen +#define tell _tell +#define umask _umask +#define unlink _unlink +#define write _write +#define execl _execl +#define execle _execle +#define execlp _execlp +#define execlpe _execlpe +#define execv _execv +#define execve _execve +#define execvp _execvp +#define execvpe _execvpe +#define getpid _getpid +#define spawnl _spawnl +#define spawnle _spawnle +#define spawnlp _spawnlp +#define spawnlpe _spawnlpe +#define spawnv _spawnv +#define spawnve _spawnve +#define spawnvp _spawnvp +#define spawnvpe _spawnvpe +#if _MSC_VER < 800 +#define fileno _fileno +#endif +#define utime _utime +//#define pipe _pipe +#define perror _perror + + +/* these are defined in nt.c */ + +extern int NtMakeCmdVector(char *, char ***, int); +/* extern void NtInitialize(int *, char ***); */ +extern char *NtGetLib(void); +extern char *NtGetBin(void); +extern FILE *mypopen(char *, char *); +extern int flock(int fd, int oper); + +// +// define this so we can do inplace editing +// + +#define SUFFIX + +// +// stubs +// +// extern int ioctl (int, unsigned int, char *); +extern int ioctl (int, unsigned int, long); +#if 0 +extern void sleep (unsigned int); +#else +#define sleep(x) Sleep(x*1000) +#endif + +extern UIDTYPE getuid (void); +extern UIDTYPE geteuid (void); +extern GIDTYPE getgid (void); +extern GIDTYPE getegid (void); +extern int setuid (int); +extern int setgid (int); + + +#undef IN /* confict in parse.c */ + +#if 0 +extern int sys_nerr; +extern char *sys_errlist[]; +#endif +extern char *mystrerror(int); + +#define strerror(e) mystrerror(e) + +#define PIPE_BUF 1024 + +#define HAVE_STDLIB_H 1 +#define HAVE_GETLOGIN 1 +#define HAVE_WAITPID 1 +#define HAVE_GETCWD 1 + +#define LOCK_SH 1 +#define LOCK_EX 2 +#define LOCK_NB 4 +#define LOCK_UN 8 +#ifndef EWOULDBLOCK +#define EWOULDBLOCK 10035 /* EBASEERR + 35 (winsock.h) */ +#endif + +#ifdef popen +#undef popen +#define popen mypopen +#endif +#ifdef pclose +#undef pclose +#define pclose mypclose +#endif + +#undef va_start +#undef va_end + +#ifdef popen +#undef popen +#define popen mypopen +#endif +#ifdef pclose +#undef pclose +#define pclose mypclose +#endif + +#undef va_start +#undef va_end + +#endif diff --git a/missing/setenv.c b/missing/setenv.c new file mode 100644 index 0000000000..b7b43a674b --- /dev/null +++ b/missing/setenv.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 1991, Larry Wall + * + * You may distribute under the terms of either the GNU General Public + * License or the Artistic License, as specified in the README file. + */ + +#include "ruby.h" + +#ifndef NT +extern char **environ; +#endif +extern char **origenviron; + +#ifndef NT +char *strdup(); +#endif + +static int +envix(nam) +char *nam; +{ + register int i, len = strlen(nam); + + for (i = 0; environ[i]; i++) { + if (memcmp(environ[i],nam,len) == 0 && environ[i][len] == '=') + break; /* memcmp must come first to avoid */ + } /* potential SEGV's */ + return i; +} + +void +setenv(nam,val) +char *nam, *val; +{ + register int i=envix(nam); /* where does it go? */ + + if (environ == origenviron) { /* need we copy environment? */ + int j; + int max; + char **tmpenv; + + /*SUPPRESS 530*/ + for (max = i; environ[max]; max++) ; + tmpenv = ALLOC_N(char*, max+2); + for (j=0; j<max; j++) /* copy environment */ + tmpenv[j] = strdup(environ[j]); + tmpenv[max] = 0; + environ = tmpenv; /* tell exec where it is now */ + } + if (!val) { + while (environ[i]) { + environ[i] = environ[i+1]; + i++; + } + return; + } + if (!environ[i]) { /* does not exist yet */ + REALLOC_N(environ, char*, i+2); /* just expand it a bit */ + environ[i+1] = 0; /* make sure it's null terminated */ + } + else { + free(environ[i]); + } + environ[i] = ALLOC_N(char, strlen(nam) + strlen(val) + 2); +#ifndef MSDOS + (void)sprintf(environ[i],"%s=%s",nam,val);/* all that work just for this */ +#else + /* MS-DOS requires environment variable names to be in uppercase */ + /* [Tom Dinger, 27 August 1990: Well, it doesn't _require_ it, but + * some utilities and applications may break because they only look + * for upper case strings. (Fixed strupr() bug here.)] + */ + strcpy(environ[i],nam); strupr(environ[i]); + (void)sprintf(environ[i] + strlen(nam),"=%s",val); +#endif /* MSDOS */ +} diff --git a/missing/strcasecmp.c b/missing/strcasecmp.c new file mode 100644 index 0000000000..83aa50d9c3 --- /dev/null +++ b/missing/strcasecmp.c @@ -0,0 +1,13 @@ +#include <ctype.h> + +#define min(a,b) (((a)>(b))?(b):(a)) +int +strcasecmp(p1, p2) + char *p1, *p2; +{ + for ( ; *p1 && *p2; p1++, p2++) { + if (toupper(*p1) != toupper(*p2)) + return toupper(*p1) - toupper(*p2); + } + return strlen(p1) - strlen(p2); +} diff --git a/missing/strdup.c b/missing/strdup.c new file mode 100644 index 0000000000..2e1fe90bbd --- /dev/null +++ b/missing/strdup.c @@ -0,0 +1,25 @@ +/************************************************ + + strdup.c - + + $Author$ + $Date$ + created at: Wed Dec 7 15:34:01 JST 1994 + +************************************************/ +#include <stdio.h> + +char * +strdup(str) + char *str; +{ + extern char *xmalloc(); + char *tmp; + int len = strlen(str) + 1; + + tmp = xmalloc(len); + if (tmp == NULL) return NULL; + memcpy(tmp, str, len); + + return tmp; +} diff --git a/missing/strerror.c b/missing/strerror.c new file mode 100644 index 0000000000..44013b3892 --- /dev/null +++ b/missing/strerror.c @@ -0,0 +1,19 @@ +/* + * strerror.c --- Map an integer error number into a printable string. + */ + +extern int sys_nerr; +extern char *sys_errlist[]; + +static char msg[50]; + +char * +strerror(error) + int error; +{ + if ((error <= sys_nerr) && (error > 0)) { + return sys_errlist[error]; + } + sprintf (msg, "Unknown error (%d)", error); + return msg; +} diff --git a/missing/strftime.c b/missing/strftime.c new file mode 100644 index 0000000000..478471c37d --- /dev/null +++ b/missing/strftime.c @@ -0,0 +1,889 @@ +/* + * strftime.c + * + * Public-domain implementation of ANSI C library routine. + * + * It's written in old-style C for maximal portability. + * However, since I'm used to prototypes, I've included them too. + * + * If you want stuff in the System V ascftime routine, add the SYSV_EXT define. + * For extensions from SunOS, add SUNOS_EXT. + * For stuff needed to implement the P1003.2 date command, add POSIX2_DATE. + * For VMS dates, add VMS_EXT. + * For a an RFC822 time format, add MAILHEADER_EXT. + * For ISO week years, add ISO_DATE_EXT. + * For complete POSIX semantics, add POSIX_SEMANTICS. + * + * The code for %c, %x, and %X now follows the 1003.2 specification for + * the POSIX locale. + * This version ignores LOCALE information. + * It also doesn't worry about multi-byte characters. + * So there. + * + * This file is also shipped with GAWK (GNU Awk), gawk specific bits of + * code are included if GAWK is defined. + * + * Arnold Robbins + * January, February, March, 1991 + * Updated March, April 1992 + * Updated April, 1993 + * Updated February, 1994 + * Updated May, 1994 + * Updated January, 1995 + * Updated September, 1995 + * Updated January, 1996 + * + * Fixes from ado@elsie.nci.nih.gov + * February 1991, May 1992 + * Fixes from Tor Lillqvist tml@tik.vtt.fi + * May, 1993 + * Further fixes from ado@elsie.nci.nih.gov + * February 1994 + * %z code from chip@chinacat.unicom.com + * Applied September 1995 + * %V code fixed (again) and %G, %g added, + * January 1996 + */ + +#ifndef GAWK +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <time.h> +#endif +#if defined(TM_IN_SYS_TIME) || ! defined(GAWK) +#include <sys/types.h> +#include <sys/time.h> +#endif + +/* defaults: season to taste */ +#define SYSV_EXT 1 /* stuff in System V ascftime routine */ +#define SUNOS_EXT 1 /* stuff in SunOS strftime routine */ +#define POSIX2_DATE 1 /* stuff in Posix 1003.2 date command */ +#define VMS_EXT 1 /* include %v for VMS date format */ +#define MAILHEADER_EXT 1 /* add %z for HHMM format */ +#define ISO_DATE_EXT 1 /* %G and %g for year of ISO week */ +#ifndef GAWK +#define POSIX_SEMANTICS 1 /* call tzset() if TZ changes */ +#endif + +#if defined(ISO_DATE_EXT) +#if ! defined(POSIX2_DATE) +#define POSIX2_DATE 1 +#endif +#endif + +#if defined(POSIX2_DATE) +#if ! defined(SYSV_EXT) +#define SYSV_EXT 1 +#endif +#if ! defined(SUNOS_EXT) +#define SUNOS_EXT 1 +#endif +#endif + +#if defined(POSIX2_DATE) +#define adddecl(stuff) stuff +#else +#define adddecl(stuff) +#endif + +#undef strchr /* avoid AIX weirdness */ + +#ifndef __STDC__ +#define const /**/ +extern void *malloc(); +extern void *realloc(); +extern void tzset(); +extern char *strchr(); +extern char *getenv(); +static int weeknumber(); +adddecl(static int iso8601wknum();) +#else +extern void *malloc(unsigned count); +extern void *realloc(void *ptr, unsigned count); +extern void tzset(void); +extern char *strchr(const char *str, int ch); +extern char *getenv(const char *v); +static int weeknumber(const struct tm *timeptr, int firstweekday); +adddecl(static int iso8601wknum(const struct tm *timeptr);) +#endif + +#ifdef __GNUC__ +#define inline __inline__ +#else +#define inline /**/ +#endif + +#define range(low, item, hi) max(low, min(item, hi)) + +#if !defined(OS2) && !defined(MSDOS) && defined(HAVE_TZNAME) +extern char *tzname[2]; +extern int daylight; +#ifdef SOLARIS +extern long timezone, altzone; +#else +extern int timezone, altzone; +#endif +#endif + +#undef min /* just in case */ + +/* min --- return minimum of two numbers */ + +#ifndef __STDC__ +static inline int +min(a, b) +int a, b; +#else +static inline int +min(int a, int b) +#endif +{ + return (a < b ? a : b); +} + +#undef max /* also, just in case */ + +/* max --- return maximum of two numbers */ + +#ifndef __STDC__ +static inline int +max(a, b) +int a, b; +#else +static inline int +max(int a, int b) +#endif +{ + return (a > b ? a : b); +} + +/* strftime --- produce formatted time */ + +#ifndef __STDC__ +size_t +strftime(s, maxsize, format, timeptr) +char *s; +size_t maxsize; +const char *format; +const struct tm *timeptr; +#else +size_t +strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr) +#endif +{ + char *endp = s + maxsize; + char *start = s; + auto char tbuf[100]; + long off; + int i, w, y; + static short first = 1; +#ifdef POSIX_SEMANTICS + static char *savetz = NULL; + static int savetzlen = 0; + char *tz; +#endif /* POSIX_SEMANTICS */ +#ifndef HAVE_TM_ZONE +#ifndef HAVE_TM_NAME +#ifndef HAVE_TZNAME + extern char *timezone(); + struct timeval tv; + struct timezone zone; +#endif /* HAVE_TZNAME */ +#endif /* HAVE_TM_NAME */ +#endif /* HAVE_TM_ZONE */ + + /* various tables, useful in North America */ + static const char *days_a[] = { + "Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat", + }; + static const char *days_l[] = { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday", + }; + static const char *months_a[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + }; + static const char *months_l[] = { + "January", "February", "March", "April", + "May", "June", "July", "August", "September", + "October", "November", "December", + }; + static const char *ampm[] = { "AM", "PM", }; + + if (s == NULL || format == NULL || timeptr == NULL || maxsize == 0) + return 0; + + /* quick check if we even need to bother */ + if (strchr(format, '%') == NULL && strlen(format) + 1 >= maxsize) + return 0; + +#ifndef POSIX_SEMANTICS + if (first) { + tzset(); + first = 0; + } +#else /* POSIX_SEMANTICS */ + tz = getenv("TZ"); + if (first) { + if (tz != NULL) { + int tzlen = strlen(tz); + + savetz = (char *) malloc(tzlen + 1); + if (savetz != NULL) { + savetzlen = tzlen + 1; + strcpy(savetz, tz); + } + } + tzset(); + first = 0; + } + /* if we have a saved TZ, and it is different, recapture and reset */ + if (tz && savetz && (tz[0] != savetz[0] || strcmp(tz, savetz) != 0)) { + i = strlen(tz) + 1; + if (i > savetzlen) { + savetz = (char *) realloc(savetz, i); + if (savetz) { + savetzlen = i; + strcpy(savetz, tz); + } + } else + strcpy(savetz, tz); + tzset(); + } +#endif /* POSIX_SEMANTICS */ + + for (; *format && s < endp - 1; format++) { + tbuf[0] = '\0'; + if (*format != '%') { + *s++ = *format; + continue; + } + again: + switch (*++format) { + case '\0': + *s++ = '%'; + goto out; + + case '%': + *s++ = '%'; + continue; + + case 'a': /* abbreviated weekday name */ + if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6) + strcpy(tbuf, "?"); + else + strcpy(tbuf, days_a[timeptr->tm_wday]); + break; + + case 'A': /* full weekday name */ + if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6) + strcpy(tbuf, "?"); + else + strcpy(tbuf, days_l[timeptr->tm_wday]); + break; + +#ifdef SYSV_EXT + case 'h': /* abbreviated month name */ +#endif + case 'b': /* abbreviated month name */ + if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11) + strcpy(tbuf, "?"); + else + strcpy(tbuf, months_a[timeptr->tm_mon]); + break; + + case 'B': /* full month name */ + if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11) + strcpy(tbuf, "?"); + else + strcpy(tbuf, months_l[timeptr->tm_mon]); + break; + + case 'c': /* appropriate date and time representation */ + strftime(tbuf, sizeof tbuf, "%a %b %e %H:%M:%S %Y", timeptr); + break; + + case 'd': /* day of the month, 01 - 31 */ + i = range(1, timeptr->tm_mday, 31); + sprintf(tbuf, "%02d", i); + break; + + case 'H': /* hour, 24-hour clock, 00 - 23 */ + i = range(0, timeptr->tm_hour, 23); + sprintf(tbuf, "%02d", i); + break; + + case 'I': /* hour, 12-hour clock, 01 - 12 */ + i = range(0, timeptr->tm_hour, 23); + if (i == 0) + i = 12; + else if (i > 12) + i -= 12; + sprintf(tbuf, "%02d", i); + break; + + case 'j': /* day of the year, 001 - 366 */ + sprintf(tbuf, "%03d", timeptr->tm_yday + 1); + break; + + case 'm': /* month, 01 - 12 */ + i = range(0, timeptr->tm_mon, 11); + sprintf(tbuf, "%02d", i + 1); + break; + + case 'M': /* minute, 00 - 59 */ + i = range(0, timeptr->tm_min, 59); + sprintf(tbuf, "%02d", i); + break; + + case 'p': /* am or pm based on 12-hour clock */ + i = range(0, timeptr->tm_hour, 23); + if (i < 12) + strcpy(tbuf, ampm[0]); + else + strcpy(tbuf, ampm[1]); + break; + + case 'S': /* second, 00 - 61 */ + i = range(0, timeptr->tm_sec, 61); + sprintf(tbuf, "%02d", i); + break; + + case 'U': /* week of year, Sunday is first day of week */ + sprintf(tbuf, "%02d", weeknumber(timeptr, 0)); + break; + + case 'w': /* weekday, Sunday == 0, 0 - 6 */ + i = range(0, timeptr->tm_wday, 6); + sprintf(tbuf, "%d", i); + break; + + case 'W': /* week of year, Monday is first day of week */ + sprintf(tbuf, "%02d", weeknumber(timeptr, 1)); + break; + + case 'x': /* appropriate date representation */ + strftime(tbuf, sizeof tbuf, "%m/%d/%y", timeptr); + break; + + case 'X': /* appropriate time representation */ + strftime(tbuf, sizeof tbuf, "%H:%M:%S", timeptr); + break; + + case 'y': /* year without a century, 00 - 99 */ + i = timeptr->tm_year % 100; + sprintf(tbuf, "%02d", i); + break; + + case 'Y': /* year with century */ + sprintf(tbuf, "%d", 1900 + timeptr->tm_year); + break; + +#ifdef MAILHEADER_EXT + /* + * From: Chip Rosenthal <chip@chinacat.unicom.com> + * Date: Sun, 19 Mar 1995 00:33:29 -0600 (CST) + * + * Warning: the %z [code] is implemented by inspecting the + * timezone name conditional compile settings, and + * inferring a method to get timezone offsets. I've tried + * this code on a couple of machines, but I don't doubt + * there is some system out there that won't like it. + * Maybe the easiest thing to do would be to bracket this + * with an #ifdef that can turn it off. The %z feature + * would be an admittedly obscure one that most folks can + * live without, but it would be a great help to those of + * us that muck around with various message processors. + */ + case 'z': /* time zone offset east of GMT e.g. -0600 */ +#ifdef HAVE_TM_NAME + /* + * Systems with tm_name probably have tm_tzadj as + * secs west of GMT. Convert to mins east of GMT. + */ + off = -timeptr->tm_tzadj / 60; +#else /* !HAVE_TM_NAME */ +#ifdef HAVE_TM_ZONE + /* + * Systems with tm_zone probably have tm_gmtoff as + * secs east of GMT. Convert to mins east of GMT. + */ + off = timeptr->tm_gmtoff / 60; +#else /* !HAVE_TM_ZONE */ +#if HAVE_TZNAME + /* + * Systems with tzname[] probably have timezone as + * secs west of GMT. Convert to mins east of GMT. + */ + off = -(daylight ? timezone : altzone) / 60; +#else /* !HAVE_TZNAME */ + off = -zone.tz_minuteswest; +#endif /* !HAVE_TZNAME */ +#endif /* !HAVE_TM_ZONE */ +#endif /* !HAVE_TM_NAME */ + if (off < 0) { + tbuf[0] = '-'; + off = -off; + } else { + tbuf[0] = '+'; + } + sprintf(tbuf+1, "%02d%02d", off/60, off%60); + break; +#endif /* MAILHEADER_EXT */ + + case 'Z': /* time zone name or abbrevation */ +#ifdef HAVE_TZNAME + i = (daylight && timeptr->tm_isdst > 0); /* 0 or 1 */ + strcpy(tbuf, tzname[i]); +#else +#ifdef HAVE_TM_ZONE + strcpy(tbuf, timeptr->tm_zone); +#else +#ifdef HAVE_TM_NAME + strcpy(tbuf, timeptr->tm_name); +#else + gettimeofday(& tv, & zone); + strcpy(tbuf, timezone(zone.tz_minuteswest, + timeptr->tm_isdst > 0)); +#endif /* HAVE_TM_NAME */ +#endif /* HAVE_TM_ZONE */ +#endif /* HAVE_TZNAME */ + break; + +#ifdef SYSV_EXT + case 'n': /* same as \n */ + tbuf[0] = '\n'; + tbuf[1] = '\0'; + break; + + case 't': /* same as \t */ + tbuf[0] = '\t'; + tbuf[1] = '\0'; + break; + + case 'D': /* date as %m/%d/%y */ + strftime(tbuf, sizeof tbuf, "%m/%d/%y", timeptr); + break; + + case 'e': /* day of month, blank padded */ + sprintf(tbuf, "%2d", range(1, timeptr->tm_mday, 31)); + break; + + case 'r': /* time as %I:%M:%S %p */ + strftime(tbuf, sizeof tbuf, "%I:%M:%S %p", timeptr); + break; + + case 'R': /* time as %H:%M */ + strftime(tbuf, sizeof tbuf, "%H:%M", timeptr); + break; + + case 'T': /* time as %H:%M:%S */ + strftime(tbuf, sizeof tbuf, "%H:%M:%S", timeptr); + break; +#endif + +#ifdef SUNOS_EXT + case 'k': /* hour, 24-hour clock, blank pad */ + sprintf(tbuf, "%2d", range(0, timeptr->tm_hour, 23)); + break; + + case 'l': /* hour, 12-hour clock, 1 - 12, blank pad */ + i = range(0, timeptr->tm_hour, 23); + if (i == 0) + i = 12; + else if (i > 12) + i -= 12; + sprintf(tbuf, "%2d", i); + break; +#endif + + +#ifdef VMS_EXT + case 'v': /* date as dd-bbb-YYYY */ + sprintf(tbuf, "%02d-%3.3s-%4d", + range(1, timeptr->tm_mday, 31), + months_a[range(0, timeptr->tm_mon, 11)], + timeptr->tm_year + 1900); + for (i = 3; i < 6; i++) + if (islower(tbuf[i])) + tbuf[i] = toupper(tbuf[i]); + break; +#endif + + +#ifdef POSIX2_DATE + case 'C': + sprintf(tbuf, "%02d", (timeptr->tm_year + 1900) / 100); + break; + + + case 'E': + case 'O': + /* POSIX locale extensions, ignored for now */ + goto again; + + case 'V': /* week of year according ISO 8601 */ + sprintf(tbuf, "%02d", iso8601wknum(timeptr)); + break; + + case 'u': + /* ISO 8601: Weekday as a decimal number [1 (Monday) - 7] */ + sprintf(tbuf, "%d", timeptr->tm_wday == 0 ? 7 : + timeptr->tm_wday); + break; +#endif /* POSIX2_DATE */ + +#ifdef ISO_DATE_EXT + case 'G': + case 'g': + /* + * Year of ISO week. + * + * If it's December but the ISO week number is one, + * that week is in next year. + * If it's January but the ISO week number is 52 or + * 53, that week is in last year. + * Otherwise, it's this year. + */ + w = iso8601wknum(timeptr); + if (timeptr->tm_mon == 11 && w == 1) + y = 1900 + timeptr->tm_year + 1; + else if (timeptr->tm_mon == 0 && w >= 52) + y = 1900 + timeptr->tm_year - 1; + else + y = 1900 + timeptr->tm_year; + + if (*format == 'G') + sprintf(tbuf, "%d", y); + else + sprintf(tbuf, "%02d", y % 100); + break; +#endif ISO_DATE_EXT + default: + tbuf[0] = '%'; + tbuf[1] = *format; + tbuf[2] = '\0'; + break; + } + i = strlen(tbuf); + if (i) { + if (s + i < endp - 1) { + strcpy(s, tbuf); + s += i; + } else + return 0; + } + } +out: + if (s < endp && *format == '\0') { + *s = '\0'; + return (s - start); + } else + return 0; +} + +/* isleap --- is a year a leap year? */ + +#ifndef __STDC__ +static int +isleap(year) +int year; +#else +static int +isleap(int year) +#endif +{ + return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); +} + + +#ifdef POSIX2_DATE +/* iso8601wknum --- compute week number according to ISO 8601 */ + +#ifndef __STDC__ +static int +iso8601wknum(timeptr) +const struct tm *timeptr; +#else +static int +iso8601wknum(const struct tm *timeptr) +#endif +{ + /* + * From 1003.2: + * If the week (Monday to Sunday) containing January 1 + * has four or more days in the new year, then it is week 1; + * otherwise it is the highest numbered week of the previous + * year (52 or 53), and the next week is week 1. + * + * ADR: This means if Jan 1 was Monday through Thursday, + * it was week 1, otherwise week 52 or 53. + * + * XPG4 erroneously included POSIX.2 rationale text in the + * main body of the standard. Thus it requires week 53. + */ + + int weeknum, jan1day, diff; + + /* get week number, Monday as first day of the week */ + weeknum = weeknumber(timeptr, 1); + + /* + * With thanks and tip of the hatlo to tml@tik.vtt.fi + * + * What day of the week does January 1 fall on? + * We know that + * (timeptr->tm_yday - jan1.tm_yday) MOD 7 == + * (timeptr->tm_wday - jan1.tm_wday) MOD 7 + * and that + * jan1.tm_yday == 0 + * and that + * timeptr->tm_wday MOD 7 == timeptr->tm_wday + * from which it follows that. . . + */ + jan1day = timeptr->tm_wday - (timeptr->tm_yday % 7); + if (jan1day < 0) + jan1day += 7; + + /* + * If Jan 1 was a Monday through Thursday, it was in + * week 1. Otherwise it was last year's highest week, which is + * this year's week 0. + * + * What does that mean? + * If Jan 1 was Monday, the week number is exactly right, it can + * never be 0. + * If it was Tuesday through Thursday, the weeknumber is one + * less than it should be, so we add one. + * Otherwise, Friday, Saturday or Sunday, the week number is + * OK, but if it is 0, it needs to be 52 or 53. + */ + switch (jan1day) { + case 1: /* Monday */ + break; + case 2: /* Tuesday */ + case 3: /* Wednesday */ + case 4: /* Thursday */ + weeknum++; + break; + case 5: /* Friday */ + case 6: /* Saturday */ + case 0: /* Sunday */ + if (weeknum == 0) { +#ifdef USE_BROKEN_XPG4 + /* XPG4 (as of March 1994) says 53 unconditionally */ + weeknum = 53; +#else + /* get week number of last week of last year */ + struct tm dec31ly; /* 12/31 last year */ + dec31ly = *timeptr; + dec31ly.tm_year--; + dec31ly.tm_mon = 11; + dec31ly.tm_mday = 31; + dec31ly.tm_wday = (jan1day == 0) ? 6 : jan1day - 1; + dec31ly.tm_yday = 364 + isleap(dec31ly.tm_year + 1900); + weeknum = iso8601wknum(& dec31ly); +#endif + } + break; + } + + if (timeptr->tm_mon == 11) { + /* + * The last week of the year + * can be in week 1 of next year. + * Sigh. + * + * This can only happen if + * M T W + * 29 30 31 + * 30 31 + * 31 + */ + int wday, mday; + + wday = timeptr->tm_wday; + mday = timeptr->tm_mday; + if ( (wday == 1 && (mday >= 29 && mday <= 31)) + || (wday == 2 && (mday == 30 || mday == 31)) + || (wday == 3 && mday == 31)) + weeknum = 1; + } + + return weeknum; +} +#endif + +/* weeknumber --- figure how many weeks into the year */ + +/* With thanks and tip of the hatlo to ado@elsie.nci.nih.gov */ + +#ifndef __STDC__ +static int +weeknumber(timeptr, firstweekday) +const struct tm *timeptr; +int firstweekday; +#else +static int +weeknumber(const struct tm *timeptr, int firstweekday) +#endif +{ + int wday = timeptr->tm_wday; + int ret; + + if (firstweekday == 1) { + if (wday == 0) /* sunday */ + wday = 6; + else + wday--; + } + ret = ((timeptr->tm_yday + 7 - wday) / 7); + if (ret < 0) + ret = 0; + return ret; +} + +#if 0 +/* ADR --- I'm loathe to mess with ado's code ... */ + +Date: Wed, 24 Apr 91 20:54:08 MDT +From: Michal Jaegermann <audfax!emory!vm.ucs.UAlberta.CA!NTOMCZAK> +To: arnold@audiofax.com + +Hi Arnold, +in a process of fixing of strftime() in libraries on Atari ST I grabbed +some pieces of code from your own strftime. When doing that it came +to mind that your weeknumber() function compiles a little bit nicer +in the following form: +/* + * firstweekday is 0 if starting in Sunday, non-zero if in Monday + */ +{ + return (timeptr->tm_yday - timeptr->tm_wday + + (firstweekday ? (timeptr->tm_wday ? 8 : 1) : 7)) / 7; +} +How nicer it depends on a compiler, of course, but always a tiny bit. + + Cheers, + Michal + ntomczak@vm.ucs.ualberta.ca +#endif + +#ifdef TEST_STRFTIME + +/* + * NAME: + * tst + * + * SYNOPSIS: + * tst + * + * DESCRIPTION: + * "tst" is a test driver for the function "strftime". + * + * OPTIONS: + * None. + * + * AUTHOR: + * Karl Vogel + * Control Data Systems, Inc. + * vogelke@c-17igp.wpafb.af.mil + * + * BUGS: + * None noticed yet. + * + * COMPILE: + * cc -o tst -DTEST_STRFTIME strftime.c + */ + +/* ADR: I reformatted this to my liking, and deleted some unneeded code. */ + +#ifndef NULL +#include <stdio.h> +#endif +#include <sys/time.h> +#include <string.h> + +#define MAXTIME 132 + +/* + * Array of time formats. + */ + +static char *array[] = +{ + "(%%A) full weekday name, var length (Sunday..Saturday) %A", + "(%%B) full month name, var length (January..December) %B", + "(%%C) Century %C", + "(%%D) date (%%m/%%d/%%y) %D", + "(%%E) Locale extensions (ignored) %E", + "(%%H) hour (24-hour clock, 00..23) %H", + "(%%I) hour (12-hour clock, 01..12) %I", + "(%%M) minute (00..59) %M", + "(%%O) Locale extensions (ignored) %O", + "(%%R) time, 24-hour (%%H:%%M) %R", + "(%%S) second (00..61) %S", + "(%%T) time, 24-hour (%%H:%%M:%%S) %T", + "(%%U) week of year, Sunday as first day of week (00..53) %U", + "(%%V) week of year according to ISO 8601 %V", + "(%%W) week of year, Monday as first day of week (00..53) %W", + "(%%X) appropriate locale time representation (%H:%M:%S) %X", + "(%%Y) year with century (1970...) %Y", + "(%%Z) timezone (EDT), or blank if timezone not determinable %Z", + "(%%a) locale's abbreviated weekday name (Sun..Sat) %a", + "(%%b) locale's abbreviated month name (Jan..Dec) %b", + "(%%c) full date (Sat Nov 4 12:02:33 1989)%n%t%t%t %c", + "(%%d) day of the month (01..31) %d", + "(%%e) day of the month, blank-padded ( 1..31) %e", + "(%%h) should be same as (%%b) %h", + "(%%j) day of the year (001..366) %j", + "(%%k) hour, 24-hour clock, blank pad ( 0..23) %k", + "(%%l) hour, 12-hour clock, blank pad ( 0..12) %l", + "(%%m) month (01..12) %m", + "(%%p) locale's AM or PM based on 12-hour clock %p", + "(%%r) time, 12-hour (same as %%I:%%M:%%S %%p) %r", + "(%%u) ISO 8601: Weekday as decimal number [1 (Monday) - 7] %u", + "(%%v) VMS date (dd-bbb-YYYY) %v", + "(%%w) day of week (0..6, Sunday == 0) %w", + "(%%x) appropriate locale date representation %x", + "(%%y) last two digits of year (00..99) %y", + "(%%z) timezone offset east of GMT as HHMM (e.g. -0500) %z", + (char *) NULL +}; + +/* main routine. */ + +int +main(argc, argv) +int argc; +char **argv; +{ + long time(); + + char *next; + char string[MAXTIME]; + + int k; + int length; + + struct tm *tm; + + long clock; + + /* Call the function. */ + + clock = time((long *) 0); + tm = localtime(&clock); + + for (k = 0; next = array[k]; k++) { + length = strftime(string, MAXTIME, next, tm); + printf("%s\n", string); + } + + exit(0); +} +#endif /* TEST_STRFTIME */ diff --git a/missing/strstr.c b/missing/strstr.c new file mode 100644 index 0000000000..c54349983e --- /dev/null +++ b/missing/strstr.c @@ -0,0 +1,73 @@ +/* + * strstr.c -- + * + * Source code for the "strstr" library routine. + * + * Copyright 1988-1991 Regents of the University of California + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appears in all copies. The University of California + * makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without + * express or implied warranty. + */ + +#ifndef lint +static char rcsid[] = "$Header$ SPRITE (Berkeley)"; +#endif /* not lint */ + +/* + *---------------------------------------------------------------------- + * + * strstr -- + * + * Locate the first instance of a substring in a string. + * + * Results: + * If string contains substring, the return value is the + * location of the first matching instance of substring + * in string. If string doesn't contain substring, the + * return value is 0. Matching is done on an exact + * character-for-character basis with no wildcards or special + * characters. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +char * +strstr(string, substring) + register char *string; /* String to search. */ + char *substring; /* Substring to try to find in string. */ +{ + register char *a, *b; + + /* First scan quickly through the two strings looking for a + * single-character match. When it's found, then compare the + * rest of the substring. + */ + + b = substring; + if (*b == 0) { + return string; + } + for ( ; *string != 0; string += 1) { + if (*string != *b) { + continue; + } + a = string; + while (1) { + if (*b == 0) { + return string; + } + if (*a++ != *b++) { + break; + } + } + b = substring; + } + return (char *) 0; +} diff --git a/missing/strtol.c b/missing/strtol.c new file mode 100644 index 0000000000..4941f43b91 --- /dev/null +++ b/missing/strtol.c @@ -0,0 +1,84 @@ +/* + * strtol.c -- + * + * Source code for the "strtol" library procedure. + * + * Copyright 1988 Regents of the University of California + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies. The University of California + * makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without + * express or implied warranty. + */ + +#include <ctype.h> + + +/* + *---------------------------------------------------------------------- + * + * strtol -- + * + * Convert an ASCII string into an integer. + * + * Results: + * The return value is the integer equivalent of string. If endPtr + * is non-NULL, then *endPtr is filled in with the character + * after the last one that was part of the integer. If string + * doesn't contain a valid integer value, then zero is returned + * and *endPtr is set to string. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +long int +strtol(string, endPtr, base) + char *string; /* String of ASCII digits, possibly + * preceded by white space. For bases + * greater than 10, either lower- or + * upper-case digits may be used. + */ + char **endPtr; /* Where to store address of terminating + * character, or NULL. */ + int base; /* Base for conversion. Must be less + * than 37. If 0, then the base is chosen + * from the leading characters of string: + * "0x" means hex, "0" means octal, anything + * else means decimal. + */ +{ + register char *p; + int result; + + /* + * Skip any leading blanks. + */ + + p = string; + while (isspace(*p)) { + p += 1; + } + + /* + * Check for a sign. + */ + + if (*p == '-') { + p += 1; + result = -(strtoul(p, endPtr, base)); + } else { + if (*p == '+') { + p += 1; + } + result = strtoul(p, endPtr, base); + } + if ((result == 0) && (endPtr != 0) && (*endPtr == p)) { + *endPtr = string; + } + return result; +} diff --git a/missing/strtoul.c b/missing/strtoul.c new file mode 100644 index 0000000000..f16f2ad9cf --- /dev/null +++ b/missing/strtoul.c @@ -0,0 +1,184 @@ +/* + * strtoul.c -- + * + * Source code for the "strtoul" library procedure. + * + * Copyright 1988 Regents of the University of California + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies. The University of California + * makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without + * express or implied warranty. + */ + +#include <ctype.h> + +/* + * The table below is used to convert from ASCII digits to a + * numerical equivalent. It maps from '0' through 'z' to integers + * (100 for non-digit characters). + */ + +static char cvtIn[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, /* '0' - '9' */ + 100, 100, 100, 100, 100, 100, 100, /* punctuation */ + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, /* 'A' - 'Z' */ + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, + 100, 100, 100, 100, 100, 100, /* punctuation */ + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, /* 'a' - 'z' */ + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35}; + +/* + *---------------------------------------------------------------------- + * + * strtoul -- + * + * Convert an ASCII string into an integer. + * + * Results: + * The return value is the integer equivalent of string. If endPtr + * is non-NULL, then *endPtr is filled in with the character + * after the last one that was part of the integer. If string + * doesn't contain a valid integer value, then zero is returned + * and *endPtr is set to string. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +unsigned long int +strtoul(string, endPtr, base) + char *string; /* String of ASCII digits, possibly + * preceded by white space. For bases + * greater than 10, either lower- or + * upper-case digits may be used. + */ + char **endPtr; /* Where to store address of terminating + * character, or NULL. */ + int base; /* Base for conversion. Must be less + * than 37. If 0, then the base is chosen + * from the leading characters of string: + * "0x" means hex, "0" means octal, anything + * else means decimal. + */ +{ + register char *p; + register unsigned long int result = 0; + register unsigned digit; + int anyDigits = 0; + + /* + * Skip any leading blanks. + */ + + p = string; + while (isspace(*p)) { + p += 1; + } + + /* + * If no base was provided, pick one from the leading characters + * of the string. + */ + + if (base == 0) + { + if (*p == '0') { + p += 1; + if (*p == 'x') { + p += 1; + base = 16; + } else { + + /* + * Must set anyDigits here, otherwise "0" produces a + * "no digits" error. + */ + + anyDigits = 1; + base = 8; + } + } + else base = 10; + } else if (base == 16) { + + /* + * Skip a leading "0x" from hex numbers. + */ + + if ((p[0] == '0') && (p[1] == 'x')) { + p += 2; + } + } + + /* + * Sorry this code is so messy, but speed seems important. Do + * different things for base 8, 10, 16, and other. + */ + + if (base == 8) { + for ( ; ; p += 1) { + digit = *p - '0'; + if (digit > 7) { + break; + } + result = (result << 3) + digit; + anyDigits = 1; + } + } else if (base == 10) { + for ( ; ; p += 1) { + digit = *p - '0'; + if (digit > 9) { + break; + } + result = (10*result) + digit; + anyDigits = 1; + } + } else if (base == 16) { + for ( ; ; p += 1) { + digit = *p - '0'; + if (digit > ('z' - '0')) { + break; + } + digit = cvtIn[digit]; + if (digit > 15) { + break; + } + result = (result << 4) + digit; + anyDigits = 1; + } + } else { + for ( ; ; p += 1) { + digit = *p - '0'; + if (digit > ('z' - '0')) { + break; + } + digit = cvtIn[digit]; + if (digit >= base) { + break; + } + result = result*base + digit; + anyDigits = 1; + } + } + + /* + * See if there were any digits at all. + */ + + if (!anyDigits) { + p = string; + } + + if (endPtr != 0) { + *endPtr = p; + } + + return result; +} diff --git a/missing/x68.c b/missing/x68.c new file mode 100644 index 0000000000..8fd9c3e879 --- /dev/null +++ b/missing/x68.c @@ -0,0 +1,12 @@ +#include "config.h" + +#if !HAVE_SELECT +#include "x68/select.c" +#endif +#if MISSING__DTOS18 +#include "x68/_dtos18.c" +#endif +#if MISSING_FCONVERT +#include "x68/_round.c" +#include "x68/fconvert.c" +#endif diff --git a/node.h b/node.h new file mode 100644 index 0000000000..db5824bbc5 --- /dev/null +++ b/node.h @@ -0,0 +1,290 @@ +/************************************************ + + node.h - + + $Author$ + $Date$ + created at: Fri May 28 15:14:02 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#ifndef NODE_H +#define NODE_H + +struct global_entry *rb_global_entry(); + +enum node_type { + NODE_METHOD, + NODE_FBODY, + NODE_CFUNC, + NODE_SCOPE, + NODE_BLOCK, + NODE_IF, + NODE_CASE, + NODE_WHEN, + NODE_OPT_N, + NODE_WHILE, + NODE_UNTIL, + NODE_ITER, + NODE_FOR, + NODE_BEGIN, + NODE_RESCUE, + NODE_RESBODY, + NODE_ENSURE, + NODE_AND, + NODE_OR, + NODE_NOT, + NODE_MASGN, + NODE_LASGN, + NODE_DASGN, + NODE_GASGN, + NODE_IASGN, + NODE_CASGN, + NODE_OP_ASGN1, + NODE_OP_ASGN2, + NODE_CALL, + NODE_FCALL, + NODE_VCALL, + NODE_SUPER, + NODE_ZSUPER, + NODE_ARRAY, + NODE_ZARRAY, + NODE_HASH, + NODE_RETURN, + NODE_YIELD, + NODE_LVAR, + NODE_DVAR, + NODE_GVAR, + NODE_IVAR, + NODE_CVAR, + NODE_NTH_REF, + NODE_BACK_REF, + NODE_MATCH_REF, + NODE_LASTLINE, + NODE_MATCH, + NODE_LIT, + NODE_STR, + NODE_DSTR, + NODE_XSTR, + NODE_DXSTR, + NODE_EVSTR, + NODE_DREGX, + NODE_DREGX_ONCE, + NODE_ARGS, + NODE_DEFN, + NODE_DEFS, + NODE_ALIAS, + NODE_VALIAS, + NODE_UNDEF, + NODE_CLASS, + NODE_MODULE, + NODE_SCLASS, + NODE_COLON2, + NODE_CNAME, + NODE_CREF, + NODE_DOT2, + NODE_DOT3, + NODE_FLIP2, + NODE_FLIP3, + NODE_ATTRSET, + NODE_SELF, + NODE_NIL, + NODE_DEFINED, + NODE_TAG, + NODE_NEWLINE, +}; + +typedef struct RNode { + UINT flags; + char *file; + union { + struct RNode *node; + ID id; + VALUE value; + VALUE (*cfunc)(); + ID *tbl; + } u1; + union { + struct RNode *node; + ID id; + int argc; + VALUE value; + } u2; + union { + struct RNode *node; + ID id; + int state; + struct global_entry *entry; + int cnt; + VALUE value; + } u3; +} NODE; + +#define RNODE(obj) (R_CAST(RNode)(obj)) + +#define nd_type(n) (((RNODE(n))->flags>>FL_USHIFT)&0x7f) +#define nd_set_type(n,t) \ + RNODE(n)->flags=((RNODE(n)->flags&~FL_UMASK)|(((t)<<FL_USHIFT)&FL_UMASK)) + +#define nd_line(n) (((RNODE(n))->flags>>18)&0x3fff) +#define nd_set_line(n,l) \ + RNODE(n)->flags=((RNODE(n)->flags&~(-1<<18))|(((l)&0x7fff)<<18)) + +#define nd_head u1.node +#define nd_alen u2.argc +#define nd_next u3.node + +#define nd_cond u1.node +#define nd_body u2.node +#define nd_else u3.node + +#define nd_orig u3.value + +#define nd_resq u2.node +#define nd_ensr u3.node + +#define nd_1st u1.node +#define nd_2nd u2.node + +#define nd_stts u1.node + +#define nd_entry u3.entry +#define nd_vid u1.id +#define nd_cflag u2.id +#define nd_cval u3.value + +#define nd_cnt u3.cnt +#define nd_tbl u1.tbl + +#define nd_var u1.node +#define nd_ibdy u2.node +#define nd_iter u3.node + +#define nd_value u2.node +#define nd_aid u3.id + +#define nd_lit u1.value + +#define nd_frml u1.node +#define nd_rest u2.argc +#define nd_opt u1.node + +#define nd_recv u1.node +#define nd_mid u2.id +#define nd_args u3.node + +#define nd_noex u1.id +#define nd_defn u3.node + +#define nd_new u2.id +#define nd_old u3.id + +#define nd_cfnc u1.cfunc +#define nd_argc u2.argc + +#define nd_cname u1.id +#define nd_super u3.id + +#define nd_modl u1.id +#define nd_clss u1.value + +#define nd_beg u1.node +#define nd_end u2.node +#define nd_state u3.state +#define nd_rval u3.value + +#define nd_nth u2.argc + +#define nd_tag u1.id +#define nd_tlev u3.cnt +#define nd_tval u2.value + +#define nd_file file + +#define NEW_METHOD(n,x) node_newnode(NODE_METHOD,x,n,0) +#define NEW_FBODY(n,i,o) node_newnode(NODE_FBODY,n,i,o) +#define NEW_DEFN(i,a,d,p) node_newnode(NODE_DEFN,p,i,NEW_RFUNC(a,d)) +#define NEW_DEFS(r,i,a,d) node_newnode(NODE_DEFS,r,i,NEW_RFUNC(a,d)) +#define NEW_CFUNC(f,c) node_newnode(NODE_CFUNC,f,c,0) +#define NEW_RFUNC(b1,b2) NEW_SCOPE(block_append(b1,b2)) +#define NEW_SCOPE(b) node_newnode(NODE_SCOPE,local_tbl(),(b),cur_cref) +#define NEW_BLOCK(a) node_newnode(NODE_BLOCK,a,0,0) +#define NEW_IF(c,t,e) node_newnode(NODE_IF,c,t,e) +#define NEW_UNLESS(c,t,e) node_newnode(NODE_IF,c,e,t) +#define NEW_CASE(h,b) node_newnode(NODE_CASE,h,b,0) +#define NEW_WHEN(c,t,e) node_newnode(NODE_WHEN,c,t,e) +#define NEW_OPT_N(b) node_newnode(NODE_OPT_N,0,b,0) +#define NEW_WHILE(c,b,n) node_newnode(NODE_WHILE,c,b,n) +#define NEW_UNTIL(c,b,n) node_newnode(NODE_UNTIL,c,b,n) +#define NEW_FOR(v,i,b) node_newnode(NODE_FOR,v,b,i) +#define NEW_ITER(v,i,b) node_newnode(NODE_ITER,v,b,i) +#define NEW_BEGIN(b) node_newnode(NODE_BEGIN,0,b,0) +#define NEW_RESCUE(b,res) node_newnode(NODE_RESCUE,b,res,0) +#define NEW_RESBODY(a,ex,n) node_newnode(NODE_RESBODY,n,ex,a) +#define NEW_ENSURE(b,en) node_newnode(NODE_ENSURE,b,0,en) +#define NEW_RET(s) node_newnode(NODE_RETURN,s,0,0) +#define NEW_YIELD(a) node_newnode(NODE_YIELD,a,0,0) +#define NEW_LIST(a) NEW_ARRAY(a) +#define NEW_ARRAY(a) node_newnode(NODE_ARRAY,a,1,0) +#define NEW_ZARRAY() node_newnode(NODE_ZARRAY,0,0,0) +#define NEW_HASH(a) node_newnode(NODE_HASH,a,0,0) +#define NEW_NOT(a) node_newnode(NODE_NOT,0,a,0) +#define NEW_MASGN(l,r) node_newnode(NODE_MASGN,l,0,r) +#define NEW_GASGN(v,val) node_newnode(NODE_GASGN,v,val,rb_global_entry(v)) +#define NEW_LASGN(v,val) node_newnode(NODE_LASGN,v,val,local_cnt(v)) +#define NEW_DASGN(v,val) node_newnode(NODE_DASGN,v,val,0); +#define NEW_IASGN(v,val) node_newnode(NODE_IASGN,v,val,0) +#define NEW_CASGN(v,val) node_newnode(NODE_CASGN,v,val,0) +#define NEW_OP_ASGN1(p,id,a) node_newnode(NODE_OP_ASGN1,p,id,a) +#define NEW_OP_ASGN2(r,i,o,val) node_newnode(NODE_OP_ASGN2,r,val,NEW_OP_ASGN3(i,o)) +#define NEW_OP_ASGN3(i,o) node_newnode(NODE_OP_ASGN2,i,o,0) +#define NEW_GVAR(v) node_newnode(NODE_GVAR,v,0,rb_global_entry(v)) +#define NEW_LVAR(v) node_newnode(NODE_LVAR,v,0,local_cnt(v)) +#define NEW_DVAR(v) node_newnode(NODE_DVAR,v,0,0); +#define NEW_IVAR(v) node_newnode(NODE_IVAR,v,0,0) +#define NEW_CVAR(v) node_newnode(NODE_CVAR,v,0,0) +#define NEW_NTH_REF(n) node_newnode(NODE_NTH_REF,0,n,local_cnt('~')) +#define NEW_BACK_REF(n) node_newnode(NODE_BACK_REF,0,n,local_cnt('~')) +#define NEW_MATCH(c) node_newnode(NODE_MATCH,c,0,0) +#define NEW_LIT(l) node_newnode(NODE_LIT,l,0,0) +#define NEW_STR(s) node_newnode(NODE_STR,s,0,0) +#define NEW_DSTR(s) node_newnode(NODE_DSTR,s,0,0) +#define NEW_XSTR(s) node_newnode(NODE_XSTR,s,0,0) +#define NEW_DXSTR(s) node_newnode(NODE_DXSTR,s,0,0) +#define NEW_EVSTR(s,l) node_newnode(NODE_EVSTR,str_new(s,l),0,0) +#define NEW_CALL(r,m,a) node_newnode(NODE_CALL,r,m,a) +#define NEW_FCALL(m,a) node_newnode(NODE_FCALL,0,m,a) +#define NEW_VCALL(m) node_newnode(NODE_VCALL,0,m,0) +#define NEW_SUPER(a) node_newnode(NODE_SUPER,0,0,a) +#define NEW_ZSUPER() node_newnode(NODE_ZSUPER,0,0,0) +#define NEW_ARGS(f,o,r) node_newnode(NODE_ARGS,o,r,f) +#define NEW_ALIAS(n,o) node_newnode(NODE_ALIAS,0,n,o) +#define NEW_VALIAS(n,o) node_newnode(NODE_VALIAS,0,n,o) +#define NEW_UNDEF(i) node_newnode(NODE_UNDEF,0,i,0) +#define NEW_CLASS(n,b,s) node_newnode(NODE_CLASS,n,NEW_CBODY(b),s) +#define NEW_SCLASS(r,b) node_newnode(NODE_SCLASS,r,NEW_CBODY(b),0) +#define NEW_MODULE(n,b) node_newnode(NODE_MODULE,n,NEW_CBODY(b),0) +#define NEW_COLON2(c,i) node_newnode(NODE_COLON2,c,i,0) +#define NEW_CREF0() (cur_cref=node_newnode(NODE_CREF,RNODE(the_frame->cbase)->nd_clss,0,0)) +#define NEW_CREF() (cur_cref=node_newnode(NODE_CREF,0,0,cur_cref)) +#define NEW_CBODY(b) (cur_cref->nd_body=NEW_SCOPE(b),cur_cref) +#define NEW_DOT2(b,e) node_newnode(NODE_DOT2,b,e,0) +#define NEW_DOT3(b,e) node_newnode(NODE_DOT3,b,e,0) +#define NEW_ATTRSET(a) node_newnode(NODE_ATTRSET,a,0,0) +#define NEW_SELF() node_newnode(NODE_SELF,0,0,0) +#define NEW_NIL() node_newnode(NODE_NIL,0,0,0) +#define NEW_DEFINED(e) node_newnode(NODE_DEFINED,e,0,0) +#define NEW_NEWLINE(n) node_newnode(NODE_NEWLINE,0,0,n) + +NODE *node_newnode(); +VALUE rb_method_booundp(); + +#define NOEX_PUBLIC 0 +#define NOEX_PRIVATE 1 + +NODE *compile_string(); +NODE *compile_file(); + +#endif diff --git a/numeric.c b/numeric.c new file mode 100644 index 0000000000..242d13c14e --- /dev/null +++ b/numeric.c @@ -0,0 +1,1154 @@ +/************************************************ + + numeric.c - + + $Author$ + $Date$ + created at: Fri Aug 13 18:33:09 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include <math.h> +#if defined (HAVE_STRING_H) +# include <string.h> +#else +# include <strings.h> +#endif + +static ID coerce; +static ID to_i; + +VALUE cNumeric; +VALUE cFloat; +VALUE cInteger; +VALUE cFixnum; + +VALUE eZeroDiv; + +ID rb_frame_last_func(); +VALUE float_new(); +double big2dbl(); + +void +num_zerodiv() +{ + Raise(eZeroDiv, "divided by 0"); +} + +static VALUE +num_coerce(x, y) + VALUE x, y; +{ + return assoc_new(f_float(x,x),f_float(y,y)); +} + +VALUE +num_coerce_bin(x, y) + VALUE x, y; +{ + VALUE ary; + + ary = rb_funcall(y, coerce, 1, x); + if (TYPE(ary) != T_ARRAY || RARRAY(ary)->len != 2) { + TypeError("coerce must return [x, y]"); + } + + x = RARRAY(ary)->ptr[0]; + y = RARRAY(ary)->ptr[1]; + + return rb_funcall(x, rb_frame_last_func(), 1, y); +} + +static VALUE +num_uplus(num) + VALUE num; +{ + return num; +} + +static VALUE +num_uminus(num) + VALUE num; +{ + VALUE ary, x, y; + + ary = rb_funcall(num, coerce, 1, INT2FIX(0)); + if (TYPE(ary) != T_ARRAY || RARRAY(ary)->len != 2) { + TypeError("coerce must return [x, y]"); + } + + x = RARRAY(ary)->ptr[0]; + y = RARRAY(ary)->ptr[1]; + + return rb_funcall(x, '-', 1, y); +} + +static VALUE +num_divmod(x, y) + VALUE x, y; +{ + VALUE div, mod; + + div = rb_funcall(x, '/', 1, y); + if (TYPE(div) == T_FLOAT) { + double d = floor(RFLOAT(div)->value); + + if (RFLOAT(div)->value > d) { + div = float_new(d); + } + } + mod = rb_funcall(x, '%', 1, y); + return assoc_new(div, mod); +} + +static VALUE +num_int_p(num) + VALUE num; +{ + return FALSE; +} + +static VALUE +num_chr(num) + VALUE num; +{ + char c; + int i = NUM2INT(num); + + if (i < 0 || 0xff < i) + Fail("%d out of char range", i); + c = i; + return str_new(&c, 1); +} + +static VALUE +num_abs(num) + VALUE num; +{ + if (RTEST(rb_funcall(num, '<', 1, INT2FIX(0)))) { + return rb_funcall(num, rb_intern("-@"), 0); + } + return num; +} + +VALUE +float_new(d) + double d; +{ + NEWOBJ(flt, struct RFloat); + OBJSETUP(flt, cFloat, T_FLOAT); + + flt->value = d; + return (VALUE)flt; +} + +static VALUE +flo_to_s(flt) + struct RFloat *flt; +{ + char buf[32]; + + sprintf(buf, "%g", flt->value); + if (strchr(buf, '.') == 0) { + int len = strlen(buf); + char *ind = strchr(buf, 'e'); + + if (ind) { + memmove(ind+2, ind, len-(ind-buf)+1); + ind[0] = '.'; + ind[1] = '0'; + } else { + strcat(buf, ".0"); + } + } + + return str_new2(buf); +} + +static VALUE +flo_coerce(x, y) + VALUE x, y; +{ + return assoc_new(f_float(x, y), x); +} + +static VALUE +flo_uminus(flt) + struct RFloat *flt; +{ + return float_new(-flt->value); +} + +static VALUE +flo_plus(x, y) + struct RFloat *x, *y; +{ + switch (TYPE(y)) { + case T_FIXNUM: + return float_new(x->value + (double)FIX2INT(y)); + case T_BIGNUM: + return float_new(x->value + big2dbl(y)); + case T_FLOAT: + return float_new(x->value + y->value); + case T_STRING: + return str_plus(obj_as_string(x), y); + default: + return num_coerce_bin(x, y); + } +} + +static VALUE +flo_minus(x, y) + struct RFloat *x, *y; +{ + switch (TYPE(y)) { + case T_FIXNUM: + return float_new(x->value - (double)FIX2INT(y)); + case T_BIGNUM: + return float_new(x->value - big2dbl(y)); + case T_FLOAT: + return float_new(x->value - y->value); + default: + return num_coerce_bin(x, y); + } +} + +static VALUE +flo_mul(x, y) + struct RFloat *x, *y; +{ + switch (TYPE(y)) { + case T_FIXNUM: + return float_new(x->value * (double)FIX2INT(y)); + case T_BIGNUM: + return float_new(x->value * big2dbl(y)); + case T_FLOAT: + return float_new(x->value * y->value); + case T_STRING: + return str_times(y, INT2FIX((int)x->value)); + default: + return num_coerce_bin(x, y); + } +} + +static VALUE +flo_div(x, y) + struct RFloat *x, *y; +{ + int f_y; + double d; + + switch (TYPE(y)) { + case T_FIXNUM: + f_y = FIX2INT(y); + if (f_y == 0) num_zerodiv(); + return float_new(x->value / (double)f_y); + case T_BIGNUM: + d = big2dbl(y); + if (d == 0.0) num_zerodiv(); + return float_new(x->value / d); + case T_FLOAT: + if (y->value == 0.0) num_zerodiv(); + return float_new(x->value / y->value); + default: + return num_coerce_bin(x, y); + } +} + +static VALUE +flo_mod(x, y) + struct RFloat *x, *y; +{ + double value; + + switch (TYPE(y)) { + case T_FIXNUM: + value = (double)FIX2INT(y); + break; + case T_BIGNUM: + value = big2dbl(y); + break; + case T_FLOAT: + value = y->value; + break; + default: + return num_coerce_bin(x, y); + } +#ifdef HAVE_FMOD + value = fmod(x->value, value); +#else + { + double value1 = x->value; + double value2; + + modf(value1/value, &value2); + value = value1 - value2 * value; + } +#endif + + return float_new(value); +} + +VALUE +flo_pow(x, y) + struct RFloat *x, *y; +{ + switch (TYPE(y)) { + case T_FIXNUM: + return float_new(pow(x->value, (double)FIX2INT(y))); + case T_BIGNUM: + return float_new(pow(x->value, big2dbl(y))); + case T_FLOAT: + return float_new(pow(x->value, y->value)); + default: + return num_coerce_bin(x, y); + } +} + +static VALUE +num_eql(x, y) + VALUE x, y; +{ + if (TYPE(x) != TYPE(y)) return FALSE; + + return rb_equal(x, y); +} + + +static VALUE +num_equal(x, y) + VALUE x, y; +{ + return rb_equal(y, x); +} + +static VALUE +flo_eq(x, y) + struct RFloat *x, *y; +{ + switch (TYPE(y)) { + case T_FIXNUM: + if (x->value == FIX2INT(y)) return TRUE; + return FALSE; + case T_BIGNUM: + return (x->value == big2dbl(y))?TRUE:FALSE; + case T_FLOAT: + return (x->value == y->value)?TRUE:FALSE; + default: + return num_equal(x, y); + } +} + +static VALUE +flo_hash(num) + struct RFloat *num; +{ + double d; + char *c; + int i, hash; + + d = num->value; + c = (char*)&d; + for (hash=0, i=0; i<sizeof(double);i++) { + hash += c[i] * 971; + } + if (hash < 0) hash = -hash; + return INT2FIX(hash); +} + +static VALUE +flo_cmp(x, y) + struct RFloat *x, *y; +{ + double a, b; + + a = x->value; + switch (TYPE(y)) { + case T_FIXNUM: + b = (double)FIX2INT(y); + break; + + case T_BIGNUM: + b = big2dbl(y); + break; + + case T_FLOAT: + b = y->value; + break; + + default: + return num_coerce_bin(x, y); + } + if (a == b) return INT2FIX(0); + if (a > b) return INT2FIX(1); + return INT2FIX(-1); +} + +static VALUE +flo_eql(x, y) + struct RFloat *x, *y; +{ + if (TYPE(y) == T_FLOAT) { + if (x->value == y->value) return TRUE; + } +} + +static VALUE +flo_to_i(num) + struct RFloat *num; +{ + double f = num->value; + int val; + + if (!FIXABLE(f)) { + return dbl2big(f); + } + val = f; + return INT2FIX(val); +} + +static VALUE +flo_to_f(num) + VALUE num; +{ + return num; +} + +static VALUE +flo_abs(flt) + struct RFloat *flt; +{ + double val = fabs(flt->value); + return float_new(val); +} + +static VALUE +to_integer(val) + VALUE val; +{ + return rb_funcall(val, to_i, 0); +} + +static VALUE +fail_to_integer(val) + VALUE val; +{ + TypeError("failed to convert %s into Integer", + rb_class2name(CLASS_OF(val))); +} + +int +num2int(val) + VALUE val; +{ + if (NIL_P(val)) return 0; + + switch (TYPE(val)) { + case T_FIXNUM: + return FIX2INT(val); + + case T_FLOAT: + if (RFLOAT(val)->value <= (double) LONG_MAX + && RFLOAT(val)->value >= (double) LONG_MIN) { + return (int)(RFLOAT(val)->value); + } + else { + Fail("float %g out of rang of integer", RFLOAT(val)->value); + } + + case T_BIGNUM: + return big2int(val); + + default: + val = rb_rescue(to_integer, val, fail_to_integer, val); + return NUM2INT(val); + } +} + +VALUE +num2fix(val) + VALUE val; +{ + int v; + + if (NIL_P(val)) return INT2FIX(0); + switch (TYPE(val)) { + case T_FIXNUM: + return val; + + case T_FLOAT: + case T_BIGNUM: + default: + v = num2int(val); + if (!FIXABLE(v)) + Fail("integer %d out of range of Fixnum", v); + return INT2FIX(v); + } +} + +static VALUE +int_int_p(num) + VALUE num; +{ + return TRUE; +} + +static VALUE +int_succ(num) + VALUE num; +{ + return rb_funcall(num, '+', 1, INT2FIX(1)); +} + +static VALUE +fix_uminus(num) + VALUE num; +{ + return int2inum(-FIX2INT(num)); +} + +VALUE +fix2str(x, base) + VALUE x; + int base; +{ + char fmt[3], buf[12]; + + fmt[0] = '%'; fmt[2] = '\0'; + if (base == 10) fmt[1] = 'd'; + else if (base == 16) fmt[1] = 'x'; + else if (base == 8) fmt[1] = 'o'; + else Fatal("fixnum cannot treat base %d", base); + + sprintf(buf, fmt, FIX2INT(x)); + return str_new2(buf); +} + +VALUE +fix_to_s(in) + VALUE in; +{ + return fix2str(in, 10); +} + +static VALUE +fix_plus(x, y) + VALUE x; + struct RFloat *y; +{ + switch (TYPE(y)) { + case T_FIXNUM: + { + int a, b, c; + VALUE r; + + a = FIX2INT(x); + b = FIX2INT(y); + c = a + b; + r = INT2FIX(c); + + if (FIX2INT(r) != c) { + r = big_plus(int2big(a), int2big(b)); + } + return r; + } + case T_FLOAT: + return float_new((double)FIX2INT(x) + y->value); + default: + return num_coerce_bin(x, y); + } +} + +static VALUE +fix_minus(x, y) + VALUE x; + struct RFloat *y; +{ + switch (TYPE(y)) { + case T_FIXNUM: + { + int a, b, c; + VALUE r; + + a = FIX2INT(x); + b = FIX2INT(y); + c = a - b; + r = INT2FIX(c); + + if (FIX2INT(r) != c) { + r = big_minus(int2big(a), int2big(b)); + } + return r; + } + case T_FLOAT: + return float_new((double)FIX2INT(x) - y->value); + default: + return num_coerce_bin(x, y); + } +} + +static VALUE +fix_mul(x, y) + VALUE x; + struct RFloat *y; +{ + switch (TYPE(y)) { + case T_FIXNUM: + { + int a, b, c; + VALUE r; + + a = FIX2INT(x); + if (a == 0) return x; + + b = FIX2INT(y); + c = a * b; + r = INT2FIX(c); + + if (FIX2INT(r) != c || c/a != b) { + r = big_mul(int2big(a), int2big(b)); + } + return r; + } + case T_FLOAT: + return float_new((double)FIX2INT(x) * y->value); + default: + return num_coerce_bin(x, y); + } +} + +static VALUE +fix_div(x, y) + VALUE x; + struct RFloat *y; +{ + int i; + + if (TYPE(y) == T_FIXNUM) { + i = FIX2INT(y); + if (i == 0) num_zerodiv(); + i = FIX2INT(x)/i; + return INT2FIX(i); + } + return num_coerce_bin(x, y); +} + +static VALUE +fix_mod(x, y) + VALUE x, y; +{ + int i; + + if (TYPE(y) == T_FIXNUM) { + i = FIX2INT(y); + if (i == 0) num_zerodiv(); + i = FIX2INT(x)%i; + return INT2FIX(i); + } + return num_coerce_bin(x, y); +} + +static VALUE +fix_pow(x, y) + VALUE x, y; +{ + if (FIXNUM_P(y)) { + int a, b; + + b = FIX2INT(y); + if (b == 0) return INT2FIX(1); + a = FIX2INT(x); + if (b > 0) { + return big_pow(int2big(a), y); + } + return float_new(pow((double)a, (double)b)); + } + else if (NIL_P(y)) { + return INT2FIX(1); + } + return num_coerce_bin(x, y); +} + +static VALUE +fix_equal(x, y) + VALUE x, y; +{ + if (FIXNUM_P(y)) { + return (FIX2INT(x) == FIX2INT(y))?TRUE:FALSE; + } + else { + return num_equal(x, y); + } +} + +static VALUE +fix_cmp(x, y) + VALUE x, y; +{ + if (FIXNUM_P(y)) { + int a = FIX2INT(x), b = FIX2INT(y); + + if (a == b) return INT2FIX(0); + if (a > b) return INT2FIX(1); + return INT2FIX(-1); + } + else { + return num_coerce_bin(x, y); + } +} + +static VALUE +fix_gt(x, y) + VALUE x, y; +{ + if (FIXNUM_P(y)) { + int a = FIX2INT(x), b = FIX2INT(y); + + if (a > b) return TRUE; + return FALSE; + } + else { + return num_coerce_bin(x, y); + } +} + +static VALUE +fix_ge(x, y) + VALUE x, y; +{ + if (FIXNUM_P(y)) { + int a = FIX2INT(x), b = FIX2INT(y); + + if (a >= b) return TRUE; + return FALSE; + } + else { + return num_coerce_bin(x, y); + } +} + +static VALUE +fix_lt(x, y) + VALUE x, y; +{ + if (FIXNUM_P(y)) { + int a = FIX2INT(x), b = FIX2INT(y); + + if (a < b) return TRUE; + return FALSE; + } + else { + return num_coerce_bin(x, y); + } +} + +static VALUE +fix_le(x, y) + VALUE x, y; +{ + if (FIXNUM_P(y)) { + int a = FIX2INT(x), b = FIX2INT(y); + + if (a <= b) return TRUE; + return FALSE; + } + else { + return num_coerce_bin(x, y); + } +} + +static VALUE +fix_rev(num) + VALUE num; +{ + unsigned long val = FIX2UINT(num); + + val = ~val; + return INT2FIX(val); +} + +static VALUE +fix_and(x, y) + VALUE x, y; +{ + long val; + + if (TYPE(y) == T_BIGNUM) { + return big_and(y, x); + } + val = NUM2INT(x) & NUM2INT(y); + return int2inum(val); +} + +static VALUE +fix_or(x, y) + VALUE x, y; +{ + long val; + + if (TYPE(y) == T_BIGNUM) { + return big_or(y, x); + } + val = NUM2INT(x) | NUM2INT(y); + return INT2FIX(val); +} + +static VALUE +fix_xor(x, y) + VALUE x, y; +{ + long val; + + if (TYPE(y) == T_BIGNUM) { + return big_xor(y, x); + } + val = NUM2INT(x) ^ NUM2INT(y); + return INT2FIX(val); +} + +static VALUE +fix_lshift(x, y) + VALUE x, y; +{ + long val, width; + + val = NUM2INT(x); + width = NUM2INT(y); + if (width > (sizeof(VALUE)*CHAR_BIT-1) + || (unsigned)val>>(sizeof(VALUE)*CHAR_BIT-1-width) > 0) { + return big_lshift(int2big(val), y); + } + val = val << width; + return int2inum(val); +} + +static VALUE +fix_rshift(x, y) + VALUE x, y; +{ + long i, val; + + i = NUM2INT(y); + if (y < 32) { + val = RSHIFT(FIX2INT(x), i); + return INT2FIX(val); + } + + return INT2FIX(0); +} + +static VALUE +fix_aref(fix, idx) + VALUE fix, idx; +{ + unsigned long val = FIX2INT(fix); + int i = FIX2INT(idx); + + if (i < 0 || sizeof(VALUE)*CHAR_BIT-1 < i) + return INT2FIX(0); + if (val & (1<<i)) + return INT2FIX(1); + return INT2FIX(0); +} + +static VALUE +fix_to_i(num) + VALUE num; +{ + return num; +} + +static VALUE +fix_to_f(num) + VALUE num; +{ + double val; + + val = (double)FIX2INT(num); + + return float_new(val); +} + +static VALUE +fix_type(fix) + VALUE fix; +{ + return str_new2("Fixnum"); +} + +static VALUE +fix_abs(fix) + VALUE fix; +{ + int i = FIX2INT(fix); + + if (i < 0) i = -i; + + return int2inum(i); +} + +static VALUE +fix_id2name(fix) + VALUE fix; +{ + char *name = rb_id2name(FIX2UINT(fix)); + if (name) return str_new2(name); + return Qnil; +} + +static VALUE +fix_succ(fix) + VALUE fix; +{ + int i = FIX2INT(fix) + 1; + + return int2inum(i); +} + +static VALUE +fix_size(fix) + VALUE fix; +{ + return INT2FIX(sizeof(VALUE)); +} + +VALUE +num_upto(from, to) + VALUE from, to; +{ + VALUE i = from; + + for (;;) { + if (RTEST(rb_funcall(i, '>', 1, to))) break; + rb_yield(i); + i = rb_funcall(i, '+', 1, INT2FIX(1)); + } + return from; +} + +static VALUE +num_downto(from, to) + VALUE from, to; +{ + VALUE i = from; + + for (;;) { + if (RTEST(rb_funcall(i, '<', 1, to))) break; + rb_yield(i); + i = rb_funcall(i, '-', 1, INT2FIX(1)); + } + return from; +} + +static VALUE +num_step(from, to, step) + VALUE from, to, step; +{ + VALUE i = from; + ID cmp; + + if (step == INT2FIX(0)) { + IndexError("step cannot be 0"); + } + + if (RTEST(rb_funcall(step, '>', 1, INT2FIX(0)))) { + cmp = '>'; + } + else { + cmp = '<'; + } + for (;;) { + if (RTEST(rb_funcall(i, cmp, 1, to))) break; + rb_yield(i); + i = rb_funcall(i, '+', 1, step); + } + return from; +} + +static VALUE +num_dotimes(num) + VALUE num; +{ + VALUE i = INT2FIX(0); + + for (;;) { + if (!RTEST(rb_funcall(i, '<', 1, num))) break; + rb_yield(i); + i = rb_funcall(i, '+', 1, INT2FIX(1)); + } + return num; +} + +VALUE +fix_upto(from, to) + VALUE from, to; +{ + int i, end; + + if (!FIXNUM_P(to)) return num_upto(from, to); + end = FIX2INT(to); + for (i = FIX2INT(from); i <= end; i++) { + rb_yield(INT2FIX(i)); + } + + return from; +} + +static VALUE +fix_downto(from, to) + VALUE from, to; +{ + int i, end; + + if (!FIXNUM_P(to)) return num_downto(from, to); + end = FIX2INT(to); + for (i=FIX2INT(from); i >= end; i--) { + rb_yield(INT2FIX(i)); + } + + return from; +} + +static VALUE +fix_step(from, to, step) + VALUE from, to, step; +{ + int i, end, diff; + + if (!FIXNUM_P(to) || !FIXNUM_P(step)) + return num_step(from, to, step); + + end = FIX2INT(to); + diff = FIX2INT(step); + + if (diff == 0) { + ArgError("step cannot be 0"); + } + else if (diff > 0) { + for (i=FIX2INT(from); i <= end; i+=diff) { + rb_yield(INT2FIX(i)); + } + } + else { + for (i=FIX2INT(from); i >= end; i+=diff) { + rb_yield(INT2FIX(i)); + } + } + return from; +} + +static VALUE +fix_dotimes(num) + VALUE num; +{ + int i, end; + + end = FIX2INT(num); + for (i=0; i<end; i++) { + rb_yield(INT2FIX(i)); + } + return num; +} + +extern VALUE mComparable; +extern VALUE eException; + +void +Init_Numeric() +{ + coerce = rb_intern("coerce"); + to_i = rb_intern("to_i"); + + eZeroDiv = rb_define_class("ZeroDivisionError", eException); + cNumeric = rb_define_class("Numeric", cObject); + + rb_include_module(cNumeric, mComparable); + rb_define_method(cNumeric, "coerce", num_coerce, 1); + + rb_define_method(cNumeric, "+@", num_uplus, 0); + rb_define_method(cNumeric, "-@", num_uminus, 0); + rb_define_method(cNumeric, "eql?", num_eql, 1); + rb_define_method(cNumeric, "divmod", num_divmod, 1); + rb_define_method(cNumeric, "abs", num_abs, 0); + + rb_define_method(cNumeric, "upto", num_upto, 1); + rb_define_method(cNumeric, "downto", num_downto, 1); + rb_define_method(cNumeric, "step", num_step, 2); + rb_define_method(cNumeric, "times", num_dotimes, 0); + rb_define_method(cNumeric, "integer?", num_int_p, 0); + rb_define_method(cNumeric, "chr", num_chr, 0); + + cInteger = rb_define_class("Integer", cNumeric); + rb_define_method(cInteger, "integer?", int_int_p, 0); + rb_define_method(cInteger, "succ", int_succ, 0); + + cFixnum = rb_define_class("Fixnum", cInteger); + + rb_undef_method(CLASS_OF(cFixnum), "new"); + + rb_define_method(cFixnum, "to_s", fix_to_s, 0); + rb_define_method(cFixnum, "type", fix_type, 0); + + rb_define_method(cFixnum, "id2name", fix_id2name, 0); + + rb_define_method(cFixnum, "-@", fix_uminus, 0); + rb_define_method(cFixnum, "+", fix_plus, 1); + rb_define_method(cFixnum, "-", fix_minus, 1); + rb_define_method(cFixnum, "*", fix_mul, 1); + rb_define_method(cFixnum, "/", fix_div, 1); + rb_define_method(cFixnum, "%", fix_mod, 1); + rb_define_method(cFixnum, "**", fix_pow, 1); + + rb_define_method(cFixnum, "abs", fix_abs, 0); + + rb_define_method(cFixnum, "==", fix_equal, 1); + rb_define_method(cFixnum, "<=>", fix_cmp, 1); + rb_define_method(cFixnum, ">", fix_gt, 1); + rb_define_method(cFixnum, ">=", fix_ge, 1); + rb_define_method(cFixnum, "<", fix_lt, 1); + rb_define_method(cFixnum, "<=", fix_le, 1); + + rb_define_method(cFixnum, "~", fix_rev, 0); + rb_define_method(cFixnum, "&", fix_and, 1); + rb_define_method(cFixnum, "|", fix_or, 1); + rb_define_method(cFixnum, "^", fix_xor, 1); + rb_define_method(cFixnum, "[]", fix_aref, 1); + + rb_define_method(cFixnum, "<<", fix_lshift, 1); + rb_define_method(cFixnum, ">>", fix_rshift, 1); + + rb_define_method(cFixnum, "to_i", fix_to_i, 0); + rb_define_method(cFixnum, "to_f", fix_to_f, 0); + + rb_define_method(cFixnum, "succ", fix_succ, 0); + rb_define_method(cFixnum, "size", fix_size, 0); + + rb_define_method(cFixnum, "upto", fix_upto, 1); + rb_define_method(cFixnum, "downto", fix_downto, 1); + rb_define_method(cFixnum, "step", fix_step, 2); + rb_define_method(cFixnum, "times", fix_dotimes, 0); + + cFloat = rb_define_class("Float", cNumeric); + + rb_undef_method(CLASS_OF(cFloat), "new"); + + rb_define_method(cFloat, "to_s", flo_to_s, 0); + rb_define_method(cFloat, "coerce", flo_coerce, 1); + rb_define_method(cFloat, "-@", flo_uminus, 0); + rb_define_method(cFloat, "+", flo_plus, 1); + rb_define_method(cFloat, "-", flo_minus, 1); + rb_define_method(cFloat, "*", flo_mul, 1); + rb_define_method(cFloat, "/", flo_div, 1); + rb_define_method(cFloat, "%", flo_mod, 1); + rb_define_method(cFloat, "**", flo_pow, 1); + rb_define_method(cFloat, "==", flo_eq, 1); + rb_define_method(cFloat, "<=>", flo_cmp, 1); + rb_define_method(cFloat, "eql?", flo_eql, 1); + rb_define_method(cFloat, "hash", flo_hash, 0); + rb_define_method(cFloat, "to_i", flo_to_i, 0); + rb_define_method(cFloat, "to_f", flo_to_f, 0); + rb_define_method(cFloat, "abs", flo_abs, 0); +} diff --git a/object.c b/object.c new file mode 100644 index 0000000000..64304404c6 --- /dev/null +++ b/object.c @@ -0,0 +1,697 @@ +/************************************************ + + object.c - + + $Author$ + $Date$ + created at: Thu Jul 15 12:01:24 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include "st.h" +#include <stdio.h> + +VALUE mKernel; +VALUE cObject; +VALUE cModule; +VALUE cClass; +VALUE cFixnum; +VALUE cData; + +static VALUE cNilClass; +static VALUE cTrueClass; +static VALUE cFalseClass; + +struct st_table *new_idhash(); + +VALUE f_sprintf(); + +VALUE obj_alloc(); + +static ID eq, eql; +static ID inspect; + +VALUE +rb_equal(obj1, obj2) + VALUE obj1, obj2; +{ + VALUE result; + + result = rb_funcall(obj1, eq, 1, obj2); + if (result == FALSE || NIL_P(result)) + return FALSE; + return TRUE; +} + +int +rb_eql(obj1, obj2) + VALUE obj1, obj2; +{ + return rb_funcall(obj1, eql, 1, obj2); +} + +VALUE +obj_equal(obj1, obj2) + VALUE obj1, obj2; +{ + if (obj1 == obj2) return TRUE; + return FALSE; +} + +static VALUE +any_to_a(obj) + VALUE obj; +{ + return ary_new3(1, obj); +} + +static VALUE +obj_id(obj) + VALUE obj; +{ + return obj | FIXNUM_FLAG; +} + +static VALUE +obj_type(obj) + struct RBasic *obj; +{ + return rb_class_path(CLASS_OF(obj)); +} + +static VALUE +obj_clone(obj) + VALUE obj; +{ + VALUE clone; + + if (TYPE(obj) != T_OBJECT) { + TypeError("can't clone %s", rb_class2name(CLASS_OF(obj))); + } + + clone = obj_alloc(RBASIC(obj)->class); + if (ROBJECT(obj)->iv_tbl) { + ROBJECT(clone)->iv_tbl = st_copy(ROBJECT(obj)->iv_tbl); + } + RBASIC(clone)->class = singleton_class_clone(RBASIC(obj)->class); + RBASIC(clone)->flags = RBASIC(obj)->flags; + + return clone; +} + +static VALUE +obj_dup(obj) + VALUE obj; +{ + return rb_funcall(obj, rb_intern("clone"), 0, 0); +} + +VALUE +any_to_s(obj) + VALUE obj; +{ + char buf[256]; + + sprintf(buf, "#<%s:0x%x>", rb_class2name(CLASS_OF(obj)), obj); + return str_new2(buf); +} + +VALUE +rb_inspect(obj) + VALUE obj; +{ + return obj_as_string(rb_funcall(obj, inspect, 0, 0)); +} + +static int +inspect_i(id, value, str) + ID id; + VALUE value; + struct RString *str; +{ + VALUE str2; + char *ivname; + + /* need not to show internal data */ + if (CLASS_OF(value) == 0) return ST_CONTINUE; + if (str->ptr[0] == '-') { + str->ptr[0] = '#'; + str_cat(str, ": ", 2); + } + else { + str_cat(str, ", ", 2); + } + ivname = rb_id2name(id); + str_cat(str, ivname, strlen(ivname)); + str_cat(str, "=", 1); + if (TYPE(value) == T_OBJECT) { + str2 = any_to_s(value); + } + else { + str2 = rb_inspect(value); + } + str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len); + + return ST_CONTINUE; +} + +static VALUE +obj_inspect(obj) + struct RObject *obj; +{ + if (TYPE(obj) == T_OBJECT + && obj->iv_tbl && obj->iv_tbl->num_entries > 0) { + VALUE str; + char *b; + + str = str_new2("-<"); + b = rb_class2name(CLASS_OF(obj)); + str_cat(str, b, strlen(b)); + st_foreach(obj->iv_tbl, inspect_i, str); + str_cat(str, ">", 1); + + return str; + } + return rb_funcall(obj, rb_intern("to_s"), 0, 0); +} + +VALUE +obj_is_instance_of(obj, c) + VALUE obj, c; +{ + struct RClass *class = (struct RClass*)CLASS_OF(obj); + + switch (TYPE(c)) { + case T_MODULE: + case T_CLASS: + break; + + case T_NIL: + if (NIL_P(obj)) return TRUE; + return FALSE; + + case T_FALSE: + if (obj) return FALSE; + return TRUE; + + case T_TRUE: + if (obj) return TRUE; + return FALSE; + + default: + TypeError("class or module required"); + } + + while (FL_TEST(class, FL_SINGLETON)) { + class = class->super; + } + if (c == (VALUE)class) return TRUE; + return FALSE; +} + +VALUE +obj_is_kind_of(obj, c) + VALUE obj, c; +{ + struct RClass *class = (struct RClass*)CLASS_OF(obj); + + switch (TYPE(c)) { + case T_MODULE: + case T_CLASS: + break; + + case T_NIL: + if (NIL_P(obj)) return TRUE; + return FALSE; + + case T_FALSE: + if (obj) return FALSE; + return TRUE; + + case T_TRUE: + if (obj) return TRUE; + return FALSE; + + default: + TypeError("class or module required"); + } + + while (class) { + if ((VALUE)class == c || RCLASS(class)->m_tbl == RCLASS(c)->m_tbl) + return TRUE; + class = class->super; + } + return FALSE; +} + +static VALUE +obj_initialize(obj) + VALUE obj; +{ + return Qnil; +} + +static VALUE +obj_s_added(obj, id) + VALUE obj, id; +{ + return Qnil; +} + +static VALUE +nil_to_s(obj) + VALUE obj; +{ + return str_new2(""); +} + +static VALUE +nil_inspect(obj) + VALUE obj; +{ + return str_new2("nil"); +} + +static VALUE +nil_type(obj) + VALUE obj; +{ + return str_new2("nil"); +} + +static VALUE +nil_plus(x, y) + VALUE x, y; +{ + switch (TYPE(y)) { + case T_FIXNUM: + case T_FLOAT: + case T_BIGNUM: + case T_STRING: + case T_ARRAY: + return y; + default: + TypeError("tried to add %s(%s) to nil", + RSTRING(obj_as_string(y))->ptr, rb_class2name(CLASS_OF(y))); + } + /* not reached */ +} + +static VALUE +main_to_s(obj) + VALUE obj; +{ + return str_new2("main"); +} + +static VALUE +true_to_s(obj) + VALUE obj; +{ + return str_new2("TRUE"); +} + +static VALUE +true_type(obj) + VALUE obj; +{ + return str_new2("TRUE"); +} + +static VALUE +false_to_s(obj) + VALUE obj; +{ + return str_new2("FALSE"); +} + +static VALUE +false_type(obj) + VALUE obj; +{ + return str_new2("FALSE"); +} + +static VALUE +rb_true(obj) + VALUE obj; +{ + return TRUE; +} + +static VALUE +rb_false(obj) + VALUE obj; +{ + return FALSE; +} + +VALUE +obj_alloc(class) + VALUE class; +{ + NEWOBJ(obj, struct RObject); + OBJSETUP(obj, class, T_OBJECT); + obj->iv_tbl = 0; + + return (VALUE)obj; +} + +static VALUE +mod_clone(module) + struct RClass *module; +{ + NEWOBJ(clone, struct RClass); + OBJSETUP(clone, CLASS_OF(module), TYPE(module)); + + clone->super = module->super; + clone->iv_tbl = 0; + clone->m_tbl = 0; /* avoid GC crashing */ + clone->m_tbl = st_copy(module->m_tbl); + + return (VALUE)clone; +} + +static VALUE +mod_to_s(class) + VALUE class; +{ + return rb_class_path(class); +} + +static VALUE +mod_eqq(mod, arg) + VALUE mod, arg; +{ + return obj_is_kind_of(arg, mod); +} + +VALUE module_new(); +VALUE class_new_instance(); + +static VALUE +class_s_new(argc, argv, class) + int argc; + VALUE *argv; +{ + VALUE super, cls; + + rb_scan_args(argc, argv, "01", &super); + if (NIL_P(super)) super = cObject; + Check_Type(super, T_CLASS); + if (FL_TEST(super, FL_SINGLETON)) { + TypeError("can't make subclass of virtual class"); + } + cls = class_new(super); + /* make metaclass */ + RBASIC(cls)->class = singleton_class_new(RBASIC(super)->class); + + return cls; +} + +static VALUE +class_superclass(class) + struct RClass *class; +{ + struct RClass *super = class->super; + + while (TYPE(super) == T_ICLASS) { + super = super->super; + } + if (!super) { + return Qnil; + } + return (VALUE)super; +} + +ID +rb_to_id(name) + VALUE name; +{ + if (TYPE(name) == T_STRING) { + return rb_intern(RSTRING(name)->ptr); + } + Check_Type(name, T_FIXNUM); + return FIX2UINT(name); +} + +static VALUE +mod_attr(argc, argv, class) + int argc; + VALUE *argv; + VALUE class; +{ + VALUE name, pub; + + rb_scan_args(argc, argv, "11", &name, &pub); + rb_define_attr(class, rb_to_id(name), RTEST(pub)); + return Qnil; +} + +static VALUE +f_integer(obj, arg) + VALUE obj, arg; +{ + int i; + + switch (TYPE(arg)) { + case T_FLOAT: + if (RFLOAT(arg)->value <= (double)FIXNUM_MAX + && RFLOAT(arg)->value >= (double)FIXNUM_MIN) { + i = (int)RFLOAT(arg)->value; + break; + } + return dbl2big(RFLOAT(arg)->value); + + case T_BIGNUM: + return arg; + + case T_STRING: + return str2inum(RSTRING(arg)->ptr, 0); + + default: + i = NUM2INT(arg); + } + return INT2NUM(i); +} + +static VALUE +to_flo(val) + VALUE val; +{ + return rb_funcall(val, rb_intern("to_f"), 0); +} + +static VALUE +fail_to_flo(val) + VALUE val; +{ + TypeError("failed to convert %s into Float", rb_class2name(CLASS_OF(val))); +} + +double big2dbl(); + +VALUE +f_float(obj, arg) + VALUE obj, arg; +{ + + switch (TYPE(arg)) { + case T_FLOAT: + return arg; + + case T_BIGNUM: + return float_new(big2dbl(arg)); + + default: + return rb_rescue(to_flo, arg, fail_to_flo, arg); + } +} + +static VALUE +f_string(obj, arg) + VALUE obj, arg; +{ + return rb_funcall(arg, rb_intern("to_s"), 0); +} + +static VALUE +f_array(obj, arg) + VALUE obj, arg; +{ + return rb_funcall(arg, rb_intern("to_a"), 0); +} + +static VALUE +boot_defclass(name, super) + char *name; + VALUE super; +{ + extern st_table *rb_class_tbl; + struct RClass *obj = (struct RClass*)class_new(super); + ID id = rb_intern(name); + + rb_name_class(obj, id); + st_add_direct(rb_class_tbl, id, obj); + return (VALUE)obj; +} + +VALUE +rb_class_of(obj) + VALUE obj; +{ + if (FIXNUM_P(obj)) return cFixnum; + if (obj == Qnil) return cNilClass; + if (obj == FALSE) return cFalseClass; + if (obj == TRUE) return cTrueClass; + + return RBASIC(obj)->class; +} + +int +rb_type(obj) + VALUE obj; +{ + if (FIXNUM_P(obj)) return T_FIXNUM; + if (obj == Qnil) return T_NIL; + if (obj == FALSE) return T_FALSE; + if (obj == TRUE) return T_TRUE; + + return BUILTIN_TYPE(obj); +} + +int +rb_special_const_p(obj) + VALUE obj; +{ + if (FIXNUM_P(obj)) return TRUE; + if (obj == Qnil) return TRUE; + if (obj == FALSE) return TRUE; + if (obj == TRUE) return TRUE; + + return FALSE; +} + +VALUE TopSelf; + +void +Init_Object() +{ + VALUE metaclass; + + cObject = boot_defclass("Object", 0); + cModule = boot_defclass("Module", cObject); + cClass = boot_defclass("Class", cModule); + + metaclass = RBASIC(cObject)->class = singleton_class_new(cClass); + metaclass = RBASIC(cModule)->class = singleton_class_new(metaclass); + metaclass = RBASIC(cClass)->class = singleton_class_new(metaclass); + + mKernel = rb_define_module("Kernel"); + rb_include_module(cObject, mKernel); + + /* + * Ruby's Class Hierarchy Chart + * + * +------------------+ + * | | + * Object---->(Object) | + * ^ ^ ^ ^ | + * | | | | | + * | | +-----+ +---------+ | + * | | | | | + * | +-----------+ | | + * | | | | | + * +------+ | Module--->(Module) | + * | | ^ ^ | + * OtherClass-->(OtherClass) | | | + * | | | + * Class---->(Class) | + * ^ | + * | | + * +----------------+ + * + * + All metaclasses are instances of the class `Class'. + */ + + rb_define_method(mKernel, "nil?", rb_false, 0); + rb_define_method(mKernel, "==", obj_equal, 1); + rb_define_alias(mKernel, "equal?", "=="); + rb_define_alias(mKernel, "===", "=="); + + rb_define_method(mKernel, "eql?", obj_equal, 1); + + rb_define_method(mKernel, "hash", obj_id, 0); + rb_define_method(mKernel, "id", obj_id, 0); + rb_define_method(mKernel, "type", obj_type, 0); + + rb_define_method(mKernel, "clone", obj_clone, 0); + rb_define_method(mKernel, "dup", obj_dup, 0); + + rb_define_method(mKernel, "to_a", any_to_a, 0); + rb_define_method(mKernel, "to_s", any_to_s, 0); + rb_define_method(mKernel, "inspect", obj_inspect, 0); + + rb_define_method(mKernel, "instance_of?", obj_is_instance_of, 1); + rb_define_method(mKernel, "kind_of?", obj_is_kind_of, 1); + rb_define_method(mKernel, "is_a?", obj_is_kind_of, 1); + + rb_define_global_function("sprintf", f_sprintf, -1); + rb_define_alias(mKernel, "format", "sprintf"); + + rb_define_global_function("Integer", f_integer, 1); + rb_define_global_function("Float", f_float, 1); + + rb_define_global_function("String", f_string, 1); + rb_define_global_function("Array", f_array, 1); + + cNilClass = rb_define_class("NilClass", cObject); + rb_define_method(cNilClass, "type", nil_type, 0); + rb_define_method(cNilClass, "to_s", nil_to_s, 0); + rb_define_method(cNilClass, "inspect", nil_inspect, 0); + rb_define_method(cNilClass, "=~", rb_equal, 1); + + rb_define_method(cNilClass, "nil?", rb_true, 0); + rb_undef_method(CLASS_OF(cNilClass), "new"); + + /* default addition */ + rb_define_method(cNilClass, "+", nil_plus, 1); + + rb_define_global_function("initialize", obj_initialize, -1); + rb_define_global_function("singleton_method_added", obj_s_added, 1); + + rb_define_method(cModule, "===", mod_eqq, 1); + rb_define_method(cModule, "to_s", mod_to_s, 0); + + rb_define_private_method(cModule, "attr", mod_attr, -1); + rb_define_singleton_method(cModule, "new", module_new, 0); + + rb_define_method(cClass, "new", class_new_instance, -1); + rb_define_method(cClass, "superclass", class_superclass, 0); + rb_undef_method(cClass, "extend_object"); + + cData = rb_define_class("Data", cObject); + + TopSelf = obj_alloc(cObject); + rb_global_variable(&TopSelf); + rb_define_singleton_method(TopSelf, "to_s", main_to_s, 0); + + cTrueClass = rb_define_class("TrueClass", cObject); + rb_define_method(cTrueClass, "to_s", true_to_s, 0); + rb_define_method(cTrueClass, "type", true_type, 0); + rb_undef_method(CLASS_OF(cTrueClass), "new"); + rb_define_global_const("TRUE", TRUE); + + cFalseClass = rb_define_class("FalseClass", cObject); + rb_define_method(cFalseClass, "to_s", false_to_s, 0); + rb_define_method(cFalseClass, "type", false_type, 0); + rb_undef_method(CLASS_OF(cFalseClass), "new"); + rb_define_global_const("FALSE", FALSE); + + eq = rb_intern("=="); + eql = rb_intern("eql?"); + inspect = rb_intern("inspect"); +} diff --git a/pack.c b/pack.c new file mode 100644 index 0000000000..35052e3406 --- /dev/null +++ b/pack.c @@ -0,0 +1,883 @@ +/************************************************ + + pack.c - + + $Author$ + $Date$ + created at: Thu Feb 10 15:17:05 JST 1994 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include <ctype.h> +#include <sys/types.h> + +#define swaps(x) ((((x)&0xFF)<<8) + (((x)>>8)&0xFF)) +#define swapl(x) ((((x)&0xFF)<<24) \ + +(((x)>>24)&0xFF) \ + +(((x)&0x0000FF00)<<8) \ + +(((x)&0x00FF0000)>>8) ) + +#ifdef DYNAMIC_ENDIAN +#ifdef ntohs +#undef ntohs +#undef ntohl +#undef htons +#undef htonl +#endif +static int +endian() +{ + static int init = 0; + static int endian_value; + char *p; + + if (init) return endian_value; + init = 1; + p = (char*)&init; + return endian_value = p[0]?0:1; +} + +#define ntohs(x) (endian()?(x):swaps(x)) +#define ntohl(x) (endian()?(x):swapl(x)) +#define htons(x) (endian()?(x):swaps(x)) +#define htonl(x) (endian()?(x):swapl(x)) +#define htovs(x) (endian()?swaps(x):(x)) +#define htovl(x) (endian()?swapl(x):(x)) +#define vtohs(x) (endian()?swaps(x):(x)) +#define vtohl(x) (endian()?swapl(x):(x)) +#else +#ifdef WORDS_BIGENDIAN +#ifndef ntohs +#define ntohs(x) (x) +#define ntohl(x) (x) +#define htons(x) (x) +#define htonl(x) (x) +#endif +#define htovs(x) swaps(x) +#define htovl(x) swapl(x) +#define vtohs(x) swaps(x) +#define vtohl(x) swapl(x) +#else /* LITTLE ENDIAN */ +#ifndef ntohs +#define ntohs(x) swaps(x) +#define ntohl(x) swapl(x) +#define htons(x) swaps(x) +#define htonl(x) swapl(x) +#endif +#define htovs(x) (x) +#define htovl(x) (x) +#define vtohs(x) (x) +#define vtohl(x) (x) +#endif +#endif + +extern VALUE cString, cArray; +#ifndef atof +double atof(); +#endif + +static char *toofew = "too few arguments"; + +static void encodes(); + +static VALUE +pack_pack(ary, fmt) + struct RArray *ary; + struct RString *fmt; +{ + static char *nul10 = "\0\0\0\0\0\0\0\0\0\0"; + static char *spc10 = " "; + UCHAR *p, *pend; + VALUE res, from; + char type; + int items, len, idx; + UCHAR *ptr; + int plen; + + Check_Type(fmt, T_STRING); + + p = fmt->ptr; + pend = fmt->ptr + fmt->len; + res = str_new(0, 0); + + items = ary->len; + idx = 0; + +#define NEXTFROM (items-- > 0 ? ary->ptr[idx++] : (ArgError(toofew),0)) + + while (p < pend) { + type = *p++; /* get data type */ + + if (*p == '*') { /* set data length */ + len = strchr("@Xxu", type) ? 0 : items; + p++; + } + else if (isdigit(*p)) { + len = strtoul(p, (char**)&p, 10); + } + else { + len = 1; + } + + switch (type) { + case 'A': case 'a': + case 'B': case 'b': + case 'H': case 'h': + from = NEXTFROM; + if (NIL_P(from)) { + ptr = 0; + plen = 0; + } + else { + from = obj_as_string(from); + ptr = RSTRING(from)->ptr; + plen = RSTRING(from)->len; + } + + if (p[-1] == '*') + len = plen; + + switch (type) { + case 'a': + case 'A': + if (plen >= len) + str_cat(res, ptr, len); + else { + str_cat(res, ptr, plen); + len -= plen; + while (len >= 10) { + str_cat(res, (type == 'A')?spc10:nul10, 10); + len -= 10; + } + str_cat(res, (type == 'A')?spc10:nul10, len); + } + break; + + case 'b': + { + int byte = 0; + int i; + + for (i=0; i++ < len; ptr++) { + if (*ptr & 1) + byte |= 128; + if (i & 7) + byte >>= 1; + else { + char c = byte & 0xff; + str_cat(res, &c, 1); + byte = 0; + } + } + if (len & 7) { + char c; + byte >>= 7 - (len & 7); + c = byte & 0xff; + str_cat(res, &c, 1); + } + } + break; + + case 'B': + { + int byte = 0; + int i; + + for (i=0; i++ < len; ptr++) { + byte |= *ptr & 1; + if (i & 7) + byte <<= 1; + else { + char c = byte & 0xff; + str_cat(res, &c, 1); + byte = 0; + } + } + if (len & 7) { + char c; + byte <<= 7 - (len & 7); + c = byte & 0xff; + str_cat(res, &c, 1); + } + } + break; + + case 'h': + { + int byte = 0; + int i; + + for (i=0; i++ < len; ptr++) { + if (isxdigit(*ptr)) { + if (isalpha(*ptr)) + byte |= (((*ptr & 15) + 9) & 15) << 4; + else + byte |= (*ptr & 15) << 4; + if (i & 1) + byte >>= 4; + else { + char c = byte & 0xff; + str_cat(res, &c, 1); + byte = 0; + } + } + } + if (len & 1) { + char c = byte & 0xff; + str_cat(res, &c, 1); + } + } + break; + + case 'H': + { + int byte = 0; + int i; + + for (i=0; i++ < len; ptr++) { + if (isxdigit(*ptr)) { + if (isalpha(*ptr)) + byte |= ((*ptr & 15) + 9) & 15; + else + byte |= *ptr & 15; + if (i & 1) + byte <<= 4; + else { + char c = byte & 0xff; + str_cat(res, &c, 1); + byte = 0; + } + } + } + if (len & 1) { + char c = byte & 0xff; + str_cat(res, &c, 1); + } + } + break; + } + break; + + case 'c': + case 'C': + while (len-- > 0) { + char c; + + from = NEXTFROM; + if (NIL_P(from)) c = 0; + else { + c = NUM2INT(from); + } + str_cat(res, &c, sizeof(char)); + } + break; + + case 's': + case 'S': + while (len-- > 0) { + short s; + + from = NEXTFROM; + if (NIL_P(from)) s = 0; + else { + s = NUM2INT(from); + } + str_cat(res, &s, sizeof(short)); + } + break; + + case 'i': + case 'I': + while (len-- > 0) { + int i; + + from = NEXTFROM; + if (NIL_P(from)) i = 0; + else { + i = NUM2INT(from); + } + str_cat(res, &i, sizeof(int)); + } + break; + + case 'l': + case 'L': + while (len-- > 0) { + long l; + + from = NEXTFROM; + if (NIL_P(from)) l = 0; + else { + l = NUM2INT(from); + } + str_cat(res, &l, sizeof(long)); + } + break; + + case 'n': + while (len-- > 0) { + unsigned short s; + + from = NEXTFROM; + if (NIL_P(from)) s = 0; + else { + s = NUM2INT(from); + } + s = htons(s); + str_cat(res, &s, sizeof(short)); + } + break; + + case 'N': + while (len-- > 0) { + unsigned long l; + + from = NEXTFROM; + if (NIL_P(from)) l = 0; + else { + l = NUM2INT(from); + } + l = htonl(l); + str_cat(res, &l, sizeof(long)); + } + break; + + case 'v': + while (len-- > 0) { + unsigned short s; + + from = NEXTFROM; + if (NIL_P(from)) s = 0; + else { + s = NUM2INT(from); + } + s = htovs(s); + str_cat(res, &s, sizeof(short)); + } + break; + + case 'V': + while (len-- > 0) { + unsigned long l; + + from = NEXTFROM; + if (NIL_P(from)) l = 0; + else { + l = NUM2INT(from); + } + l = htovl(l); + str_cat(res, &l, sizeof(long)); + } + break; + + case 'f': + case 'F': + while (len-- > 0) { + float f; + + from = NEXTFROM; + switch (TYPE(from)) { + case T_FLOAT: + f = RFLOAT(from)->value; + break; + case T_STRING: + f = atof(RSTRING(from)->ptr); + default: + f = (float)NUM2INT(from); + break; + } + str_cat(res, &f, sizeof(float)); + } + break; + + case 'd': + case 'D': + while (len-- > 0) { + double d; + + from = NEXTFROM; + switch (TYPE(from)) { + case T_FLOAT: + d = RFLOAT(from)->value; + break; + case T_STRING: + d = atof(RSTRING(from)->ptr); + default: + d = (double)NUM2INT(from); + break; + } + str_cat(res, &d, sizeof(double)); + } + break; + + case 'x': + grow: + while (len >= 10) { + str_cat(res, nul10, 10); + len -= 10; + } + str_cat(res, nul10, len); + break; + + case 'X': + shrink: + if (RSTRING(res)->len < len) + ArgError("X outside of string"); + RSTRING(res)->len -= len; + RSTRING(res)->ptr[RSTRING(res)->len] = '\0'; + break; + + case '@': + len -= RSTRING(res)->len; + if (len > 0) goto grow; + len = -len; + if (len > 0) goto shrink; + break; + + case '%': + ArgError("% may only be used in unpack"); + break; + + case 'u': + from = obj_as_string(NEXTFROM); + ptr = RSTRING(from)->ptr; + plen = RSTRING(from)->len; + + if (len <= 1) + len = 45; + else + len = len / 3 * 3; + while (plen > 0) { + int todo; + + if (plen > len) + todo = len; + else + todo = plen; + encodes(res, ptr, todo); + plen -= todo; + ptr += todo; + } + break; + + default: + break; + } + } + + return res; +} + +static void +encodes(str, s, len) + struct RString *str; + UCHAR *s; + int len; +{ + char hunk[4]; + UCHAR *p, *pend; + + *hunk = len + ' '; + str_cat(str, hunk, 1); + while (len > 0) { + hunk[0] = ' ' + (077 & (*s >> 2)); + hunk[1] = ' ' + (077 & (((*s << 4) & 060) | ((s[1] >> 4) & 017))); + hunk[2] = ' ' + (077 & (((s[1] << 2) & 074) | ((s[2] >> 6) & 03))); + hunk[3] = ' ' + (077 & (s[2] & 077)); + str_cat(str, hunk, 4); + s += 3; + len -= 3; + } + p = str->ptr; + pend = str->ptr + str->len; + while (p < pend) { + if (*p == ' ') + *p = '`'; + p++; + } + str_cat(str, "\n", 1); +} + +static VALUE +pack_unpack(str, fmt) + struct RString *str, *fmt; +{ + static char *hexdigits = "0123456789abcdef0123456789ABCDEFx"; + UCHAR *s, *send; + UCHAR *p, *pend; + VALUE ary; + char type; + int len; + + Check_Type(fmt, T_STRING); + + s = str->ptr; + send = s + str->len; + p = fmt->ptr; + pend = p + fmt->len; + + ary = ary_new(); + while (p < pend) { + type = *p++; + if (*p == '*') { + len = send - s; + p++; + } + else if (isdigit(*p)) { + len = strtoul(p, (char**)&p, 10); + } + else { + len = (type != '@'); + } + + switch (type) { + case '%': + ArgError("% is not supported(yet)"); + break; + + case 'A': + if (len > send - s) len = send - s; + { + int end = len; + UCHAR *t = s + len - 1; + + while (t >= s) { + if (*t != ' ' && *t != '\0') break; + t--; + len--; + } + ary_push(ary, str_new(s, len)); + s += end; + } + break; + + case 'a': + if (len > send - s) len = send - s; + ary_push(ary, str_new(s, len)); + s += len; + break; + + case 'b': + { + VALUE bitstr; + UCHAR *t; + int bits, i; + + if (p[-1] == '*' || len > (send - s) * 8) + len = (send - s) * 8; + bits = 0; + ary_push(ary, bitstr = str_new(0, len)); + t = RSTRING(bitstr)->ptr; + for (i=0; i<len; i++) { + if (i & 7) bits >>= 1; + else bits = *s++; + *t++ = (bits & 1) ? '1' : '0'; + } + } + break; + + case 'B': + { + VALUE bitstr; + UCHAR *t; + int bits, i; + + if (p[-1] == '*' || len > (send - s) * 8) + len = (send - s) * 8; + bits = 0; + ary_push(ary, bitstr = str_new(0, len)); + t = RSTRING(bitstr)->ptr; + for (i=0; i<len; i++) { + if (i & 7) bits <<= 1; + else bits = *s++; + *t++ = (bits & 128) ? '1' : '0'; + } + } + break; + + case 'h': + { + VALUE bitstr; + UCHAR *t; + int bits, i; + + if (p[-1] == '*' || len > (send - s) * 2) + len = (send - s) * 2; + bits = 0; + ary_push(ary, bitstr = str_new(0, len)); + t = RSTRING(bitstr)->ptr; + for (i=0; i<len; i++) { + if (i & 1) + bits >>= 4; + else + bits = *s++; + *t++ = hexdigits[bits & 15]; + } + } + break; + + case 'H': + { + VALUE bitstr; + UCHAR *t; + int bits, i; + + if (p[-1] == '*' || len > (send - s) * 2) + len = (send - s) * 2; + bits = 0; + ary_push(ary, bitstr = str_new(0, len)); + t = RSTRING(bitstr)->ptr; + for (i=0; i<len; i++) { + if (i & 1) + bits <<= 4; + else + bits = *s++; + *t++ = hexdigits[(bits >> 4) & 15]; + } + } + break; + + case 'c': + if (len > send - s) + len = send - s; + while (len-- > 0) { + int c = *s++; + if (c > (char)127) c-=256; + ary_push(ary, INT2FIX(c)); + } + break; + + case 'C': + if (len > send - s) + len = send - s; + while (len-- > 0) { + UCHAR c = *s++; + ary_push(ary, INT2FIX(c)); + } + break; + + case 's': + if (len >= (send - s) / sizeof(short)) + len = (send - s) / sizeof(short); + while (len-- > 0) { + short tmp; + memcpy(&tmp, s, sizeof(short)); + s += sizeof(short); + ary_push(ary, INT2FIX(tmp)); + } + break; + + case 'S': + if (len >= (send - s) / sizeof(short)) + len = (send - s) / sizeof(short); + while (len-- > 0) { + unsigned short tmp; + memcpy(&tmp, s, sizeof(short)); + s += sizeof(short); + ary_push(ary, INT2FIX(tmp)); + } + break; + + case 'i': + if (len >= (send - s) / sizeof(int)) + len = (send - s) / sizeof(int); + while (len-- > 0) { + int tmp; + memcpy(&tmp, s, sizeof(int)); + s += sizeof(int); + ary_push(ary, int2inum(tmp)); + } + break; + + case 'I': + if (len >= (send - s) / sizeof(int)) + len = (send - s) / sizeof(int); + while (len-- > 0) { + unsigned int tmp; + memcpy(&tmp, s, sizeof(int)); + s += sizeof(int); + ary_push(ary, int2inum(tmp)); + } + break; + + case 'l': + if (len >= (send - s) / sizeof(long)) + len = (send - s) / sizeof(long); + while (len-- > 0) { + long tmp; + memcpy(&tmp, s, sizeof(long)); + s += sizeof(long); + ary_push(ary, int2inum(tmp)); + } + break; + + case 'L': + if (len >= (send - s) / sizeof(long)) + len = (send - s) / sizeof(long); + while (len-- > 0) { + unsigned long tmp; + memcpy(&tmp, s, sizeof(long)); + s += sizeof(long); + ary_push(ary, uint2inum(tmp)); + } + break; + + case 'n': + if (len >= (send - s) / sizeof(short)) + len = (send - s) / sizeof(short); + while (len-- > 0) { + unsigned short tmp; + memcpy(&tmp, s, sizeof(short)); + s += sizeof(short); + tmp = ntohs(tmp); + ary_push(ary, uint2inum(tmp)); + } + break; + + case 'N': + if (len >= (send - s) / sizeof(long)) + len = (send - s) / sizeof(long); + while (len-- > 0) { + unsigned long tmp; + memcpy(&tmp, s, sizeof(long)); + s += sizeof(long); + tmp = ntohl(tmp); + ary_push(ary, uint2inum(tmp)); + } + break; + + case 'v': + if (len >= (send - s) / sizeof(short)) + len = (send - s) / sizeof(short); + while (len-- > 0) { + unsigned short tmp; + memcpy(&tmp, s, sizeof(short)); + s += sizeof(short); + tmp = vtohs(tmp); + ary_push(ary, uint2inum(tmp)); + } + break; + + case 'V': + if (len >= (send - s) / sizeof(long)) + len = (send - s) / sizeof(long); + while (len-- > 0) { + unsigned long tmp; + memcpy(&tmp, s, sizeof(long)); + s += sizeof(long); + tmp = vtohl(tmp); + ary_push(ary, uint2inum(tmp)); + } + break; + + case 'f': + case 'F': + if (len >= (send - s) / sizeof(float)) + len = (send - s) / sizeof(float); + while (len-- > 0) { + float tmp; + memcpy(&tmp, s, sizeof(float)); + s += sizeof(float); + ary_push(ary, float_new((double)tmp)); + } + break; + + case 'D': + case 'd': + if (len >= (send - s) / sizeof(double)) + len = (send - s) / sizeof(double); + while (len-- > 0) { + double tmp; + memcpy(&tmp, s, sizeof(double)); + s += sizeof(double); + ary_push(ary, float_new(tmp)); + } + break; + + case 'u': + { + VALUE str = str_new(0, (send - s)*3/4); + UCHAR *ptr = RSTRING(str)->ptr; + int total = 0; + + while (s < send && *s > ' ' && *s < 'a') { + long a,b,c,d; + char hunk[4]; + + hunk[3] = '\0'; + len = (*s++ - ' ') & 077; + total += len; + if (total > RSTRING(str)->len) { + len -= total - RSTRING(str)->len; + total = RSTRING(str)->len; + } + + while (len > 0) { + int mlen = len > 3 ? 3 : len; + + if (s < send && *s >= ' ') + a = (*s++ - ' ') & 077; + else + a = 0; + if (s < send && *s >= ' ') + b = (*s++ - ' ') & 077; + else + b = 0; + if (s < send && *s >= ' ') + c = (*s++ - ' ') & 077; + else + c = 0; + if (s < send && *s >= ' ') + d = (*s++ - ' ') & 077; + else + d = 0; + hunk[0] = a << 2 | b >> 4; + hunk[1] = b << 4 | c >> 2; + hunk[2] = c << 6 | d; + memcpy(ptr, hunk, mlen); + ptr += mlen; + len -= mlen; + } + if (*s == '\r') s++; + if (*s == '\n') s++; + else if (s < send && (s+1 == send || s[1] == '\n')) + s += 2; /* possible checksum byte */ + } + RSTRING(str)->len = total; + ary_push(ary, str); + } + break; + + case '@': + s = str->ptr + len; + break; + + case 'X': + if (len > s - str->ptr) + ArgError("X outside of string"); + s -= len; + break; + + case 'x': + if (len > send - s) + ArgError("x outside of string"); + s += len; + break; + + default: + break; + } + } + + return ary; +} + +void +Init_pack() +{ + rb_define_method(cArray, "pack", pack_pack, 1); + rb_define_method(cString, "unpack", pack_unpack, 1); +} diff --git a/parse.y b/parse.y new file mode 100644 index 0000000000..02ff720bb3 --- /dev/null +++ b/parse.y @@ -0,0 +1,3717 @@ +/************************************************ + + parse.y - + + $Author$ + $Date$ + created at: Fri May 28 18:02:42 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +%{ + +#define YYDEBUG 1 +#include "ruby.h" +#include "env.h" +#include "node.h" +#include "st.h" +#include <stdio.h> + +/* hack for bison */ +#ifdef const +# undef const +#endif + +#define ID_SCOPE_SHIFT 3 +#define ID_SCOPE_MASK 0x07 +#define ID_LOCAL 0x01 +#define ID_INSTANCE 0x02 +#define ID_GLOBAL 0x03 +#define ID_ATTRSET 0x04 +#define ID_CONST 0x05 + +#define is_id_nonop(id) ((id)>LAST_TOKEN) +#define is_local_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_LOCAL) +#define is_global_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_GLOBAL) +#define is_instance_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_INSTANCE) +#define is_attrset_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_ATTRSET) +#define is_const_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_CONST) + +struct op_tbl { + ID token; + char *name; +}; + +NODE *eval_tree = 0; + +char *sourcefile; /* current source file */ +int sourceline; /* current line no. */ + +static int yylex(); +static int yyerror(); + +static enum lex_state { + EXPR_BEG, /* ignore newline, +/- is a sign. */ + EXPR_MID, /* newline significant, +/- is a sign. */ + EXPR_END, /* newline significant, +/- is a operator. */ + EXPR_ARG, /* newline significant, +/- may be a sign. */ + EXPR_FNAME, /* ignore newline, +/- is a operator. */ +} lex_state; + +static int class_nest = 0; +static int in_single = 0; +static ID cur_mid = 0; + +static int value_expr(); +static NODE *cond(); +static NODE *logop(); + +static NODE *newline_node(); +static void fixpos(); + +static NODE *block_append(); +static NODE *list_append(); +static NODE *list_concat(); +static NODE *call_op(); +static int in_defined = 0; + +static NODE *gettable(); +static NODE *assignable(); +static NODE *aryset(); +static NODE *attrset(); +static void backref_error(); + +static void local_push(); +static void local_pop(); +static int local_cnt(); +static int local_id(); +static ID *local_tbl(); + +static struct RVarmap *dyna_push(); +static void dyna_pop(); +static int dyna_in_block(); + +VALUE dyna_var_asgn(); +VALUE dyna_var_defined(); + +#define cref_push() NEW_CREF() +static void cref_pop(); +static NODE *cur_cref; + +static void top_local_init(); +static void top_local_setup(); +%} + +%union { + NODE *node; + VALUE val; + ID id; + int num; + struct RVarmap *vars; +} + +%token CLASS + MODULE + DEF + UNDEF + BEGIN + RESCUE + ENSURE + END + IF + UNLESS + THEN + ELSIF + ELSE + CASE + WHEN + WHILE + UNTIL + FOR + IN + DO + RETURN + YIELD + SUPER + SELF + NIL + AND + OR + NOT + IF_MOD + UNLESS_MOD + WHILE_MOD + UNTIL_MOD + ALIAS + DEFINED + +%token <id> IDENTIFIER FID GVAR IVAR CONSTANT +%token <val> INTEGER FLOAT STRING XSTRING REGEXP +%token <node> DSTRING DXSTRING DREGEXP NTH_REF BACK_REF + +%type <node> singleton +%type <val> literal numeric +%type <node> compexpr exprs expr arg primary command_call method_call +%type <node> if_tail opt_else case_body cases rescue ensure iterator +%type <node> call_args call_args0 ret_args args mrhs opt_args var_ref +%type <node> superclass f_arglist f_args f_optarg f_opt +%type <node> array assoc_list assocs assoc undef_list +%type <node> iter_var opt_iter_var iter_block iter_do_block +%type <node> mlhs mlhs_head mlhs_tail lhs backref +%type <id> variable symbol operation +%type <id> cname fname op rest_arg +%type <num> f_arg +%token UPLUS /* unary+ */ +%token UMINUS /* unary- */ +%token POW /* ** */ +%token CMP /* <=> */ +%token EQ /* == */ +%token EQQ /* === */ +%token NEQ /* != <> */ +%token GEQ /* >= */ +%token LEQ /* <= */ +%token ANDOP OROP /* && and || */ +%token MATCH NMATCH /* =~ and !~ */ +%token DOT2 DOT3 /* .. and ... */ +%token AREF ASET /* [] and []= */ +%token LSHFT RSHFT /* << and >> */ +%token COLON2 /* :: */ +%token <id> OP_ASGN /* +=, -= etc. */ +%token ASSOC /* => */ +%token LPAREN /* ( */ +%token LBRACK /* [ */ +%token LBRACE /* { */ +%token STAR /* * */ +%token SYMBEG + +/* + * precedence table + */ + +%left IF_MOD UNLESS_MOD WHILE_MOD UNTIL_MOD +%left OR AND +%right NOT +%nonassoc DEFINED +%right '=' OP_ASGN +%nonassoc DOT2 DOT3 +%left OROP +%left ANDOP +%nonassoc CMP EQ EQQ NEQ MATCH NMATCH +%left '>' GEQ '<' LEQ +%left '|' '^' +%left '&' +%left LSHFT RSHFT +%left '+' '-' +%left '*' '/' '%' +%right '!' '~' UPLUS UMINUS +%right POW + +%token LAST_TOKEN + +%% +program : { + lex_state = EXPR_BEG; + top_local_init(); + NEW_CREF0(); /* initialize constant c-ref */ + if ((VALUE)the_class == cObject) class_nest = 0; + else class_nest = 1; + } + compexpr + { + eval_tree = block_append(eval_tree, $2); + top_local_setup(); + cur_cref = 0; + class_nest = 0; + } + +compexpr : exprs opt_terms + +exprs : /* none */ + { + $$ = 0; + } + | expr + { + $$ = newline_node($1); + } + | exprs terms expr + { + $$ = block_append($1, newline_node($3)); + } + | error expr + { + $$ = $2; + } + +expr : mlhs '=' mrhs + { + value_expr($3); + $1->nd_value = $3; + $$ = $1; + } + | assocs + { + $$ = NEW_HASH($1); + } + | RETURN ret_args + { + value_expr($2); + if (!cur_mid && !in_single) + yyerror("return appeared outside of method"); + $$ = NEW_RET($2); + } + | YIELD ret_args + { + value_expr($2); + $$ = NEW_YIELD($2); + } + | command_call + | iterator iter_do_block + { + $2->nd_iter = $1; + $$ = $2; + fixpos($$, $2); + } + | ALIAS fname {lex_state = EXPR_FNAME;} fname + { + if (cur_mid || in_single) + yyerror("alias within method"); + $$ = NEW_ALIAS($2, $4); + } + | ALIAS GVAR GVAR + { + if (cur_mid || in_single) + yyerror("alias within method"); + $$ = NEW_VALIAS($2, $3); + } + | ALIAS GVAR BACK_REF + { + char buf[3]; + + if (cur_mid || in_single) + yyerror("alias within method"); + sprintf(buf, "$%c", $3->nd_nth); + $$ = NEW_VALIAS($2, rb_intern(buf)); + } + | ALIAS GVAR NTH_REF + { + yyerror("can't make alias for the number variables"); + $$ = 0; + } + | UNDEF undef_list + { + if (cur_mid || in_single) + yyerror("undef within method"); + $$ = $2; + } + | expr IF_MOD expr + { + value_expr($3); + $$ = NEW_IF(cond($3), $1, 0); + } + | expr UNLESS_MOD expr + { + value_expr($3); + $$ = NEW_UNLESS(cond($3), $1, 0); + } + | expr WHILE_MOD expr + { + value_expr($3); + if (nd_type($1) == NODE_BEGIN) { + $$ = NEW_WHILE(cond($3), $1->nd_body, 0); + } + else { + $$ = NEW_WHILE(cond($3), $1, 1); + } + } + | expr UNTIL_MOD expr + { + value_expr($3); + if (nd_type($1) == NODE_BEGIN) { + $$ = NEW_UNTIL(cond($3), $1->nd_body, 0); + } + else { + $$ = NEW_UNTIL(cond($3), $1, 1); + } + } + | expr AND expr + { + $$ = logop(NODE_AND, $1, $3); + } + | expr OR expr + { + $$ = logop(NODE_OR, $1, $3); + } + | NOT expr + { + value_expr($2); + $$ = NEW_NOT(cond($2)); + } + | '!' command_call + { + value_expr($2); + $$ = NEW_NOT(cond($2)); + } + | arg + +command_call : operation call_args0 + { + $$ = NEW_FCALL($1, $2); + fixpos($$, $2); + } + | primary '.' operation call_args0 + { + value_expr($1); + $$ = NEW_CALL($1, $3, $4); + fixpos($$, $1); + } + | SUPER call_args0 + { + if (!cur_mid && !in_single && !in_defined) + yyerror("super called outside of method"); + $$ = NEW_SUPER($2); + fixpos($$, $2); + } + +mlhs : mlhs_head + { + $$ = NEW_MASGN(NEW_LIST($1), 0); + } + | mlhs_head STAR lhs + { + $$ = NEW_MASGN(NEW_LIST($1), $3); + } + | mlhs_head mlhs_tail + { + $$ = NEW_MASGN(list_concat(NEW_LIST($1),$2), 0); + } + | mlhs_head mlhs_tail ',' STAR lhs + { + $$ = NEW_MASGN(list_concat(NEW_LIST($1),$2),$5); + } + | STAR lhs + { + $$ = NEW_MASGN(0, $2); + } + +mlhs_head : lhs ',' + +mlhs_tail : lhs + { + $$ = NEW_LIST($1); + } + | mlhs_tail ',' lhs + { + $$ = list_append($1, $3); + } + +lhs : variable + { + $$ = assignable($1, 0); + } + | primary '[' opt_args opt_nl ']' + { + $$ = aryset($1, $3, 0); + } + | primary '.' IDENTIFIER + { + $$ = attrset($1, $3, 0); + } + | primary '.' CONSTANT + { + $$ = attrset($1, $3, 0); + } + | backref + { + backref_error($1); + $$ = 0; + } + +cname : IDENTIFIER + { + yyerror("class/module name must be CONSTANT"); + } + | CONSTANT + +fname : IDENTIFIER + | CONSTANT + | FID + | op + { + lex_state = EXPR_END; + $$ = $1; + } + +undef_list : fname + { + $$ = NEW_UNDEF($1); + } + | undef_list ',' {lex_state = EXPR_FNAME;} fname + { + $$ = block_append($1, NEW_UNDEF($4)); + } + +op : DOT2 { $$ = DOT2; } + | '|' { $$ = '|'; } + | '^' { $$ = '^'; } + | '&' { $$ = '&'; } + | CMP { $$ = CMP; } + | EQ { $$ = EQ; } + | EQQ { $$ = EQQ; } + | MATCH { $$ = MATCH; } + | '>' { $$ = '>'; } + | GEQ { $$ = GEQ; } + | '<' { $$ = '<'; } + | LEQ { $$ = LEQ; } + | LSHFT { $$ = LSHFT; } + | RSHFT { $$ = RSHFT; } + | '+' { $$ = '+'; } + | '-' { $$ = '-'; } + | '*' { $$ = '*'; } + | STAR { $$ = '*'; } + | '/' { $$ = '/'; } + | '%' { $$ = '%'; } + | POW { $$ = POW; } + | '~' { $$ = '~'; } + | UPLUS { $$ = UMINUS; } + | UMINUS { $$ = UPLUS; } + | AREF { $$ = AREF; } + | ASET { $$ = ASET; } + | '`' { $$ = '`'; } + +arg : variable '=' arg + { + value_expr($3); + $$ = assignable($1, $3); + fixpos($$, $3); + } + | primary '[' opt_args opt_nl ']' '=' arg + { + $$ = aryset($1, $3, $7); + fixpos($$, $7); + } + | primary '.' IDENTIFIER '=' arg + { + $$ = attrset($1, $3, $5); + fixpos($$, $5); + } + | primary '.' CONSTANT '=' arg + { + $$ = attrset($1, $3, $5); + fixpos($$, $5); + } + | backref '=' arg + { + value_expr($3); + backref_error($1); + $$ = 0; + } + | variable OP_ASGN arg + { + value_expr($3); + if (is_local_id($1)&&!local_id($1)&&dyna_in_block()) + dyna_var_asgn($1, TRUE); + $$ = assignable($1, call_op(gettable($1), $2, 1, $3)); + fixpos($$, $3); + } + | primary '[' opt_args opt_nl ']' OP_ASGN arg + { + NODE *args = NEW_LIST($7); + + list_append($3, NEW_NIL()); + list_concat(args, $3); + $$ = NEW_OP_ASGN1($1, $6, args); + fixpos($$, $7); + } + | primary '.' IDENTIFIER OP_ASGN arg + { + $$ = NEW_OP_ASGN2($1, $3, $4, $5); + fixpos($$, $5); + } + | backref OP_ASGN arg + { + backref_error($1); + $$ = 0; + } + | arg DOT2 arg + { + $$ = NEW_DOT2($1, $3); + } + | arg DOT3 arg + { + $$ = NEW_DOT3($1, $3); + } + | arg '+' arg + { + $$ = call_op($1, '+', 1, $3); + } + | arg '-' arg + { + $$ = call_op($1, '-', 1, $3); + } + | arg '*' arg + { + $$ = call_op($1, '*', 1, $3); + } + | arg '/' arg + { + $$ = call_op($1, '/', 1, $3); + } + | arg '%' arg + { + $$ = call_op($1, '%', 1, $3); + } + | arg POW arg + { + $$ = call_op($1, POW, 1, $3); + } + | UPLUS arg + { + $$ = call_op($2, UPLUS, 0); + } + | UMINUS arg + { + $$ = call_op($2, UMINUS, 0); + } + | arg '|' arg + { + $$ = call_op($1, '|', 1, $3); + } + | arg '^' arg + { + $$ = call_op($1, '^', 1, $3); + } + | arg '&' arg + { + $$ = call_op($1, '&', 1, $3); + } + | arg CMP arg + { + $$ = call_op($1, CMP, 1, $3); + } + | arg '>' arg + { + $$ = call_op($1, '>', 1, $3); + } + | arg GEQ arg + { + $$ = call_op($1, GEQ, 1, $3); + } + | arg '<' arg + { + $$ = call_op($1, '<', 1, $3); + } + | arg LEQ arg + { + $$ = call_op($1, LEQ, 1, $3); + } + | arg EQ arg + { + $$ = call_op($1, EQ, 1, $3); + } + | arg EQQ arg + { + $$ = call_op($1, EQQ, 1, $3); + } + | arg NEQ arg + { + $$ = NEW_NOT(call_op($1, EQ, 1, $3)); + } + | arg MATCH arg + { + local_cnt('~'); + $$ = NEW_CALL($1, MATCH, NEW_LIST($3)); + } + | arg NMATCH arg + { + local_cnt('~'); + $$ = NEW_NOT(NEW_CALL($1, MATCH, NEW_LIST($3))); + } + | '!' arg + { + value_expr($2); + $$ = NEW_NOT(cond($2)); + } + | '~' arg + { + $$ = call_op($2, '~', 0); + } + | arg LSHFT arg + { + $$ = call_op($1, LSHFT, 1, $3); + } + | arg RSHFT arg + { + $$ = call_op($1, RSHFT, 1, $3); + } + | arg ANDOP arg + { + $$ = logop(NODE_AND, $1, $3); + } + | arg OROP arg + { + $$ = logop(NODE_OR, $1, $3); + } + | DEFINED opt_nl {in_defined = 1;} arg + { + in_defined = 0; + $$ = NEW_DEFINED($4); + } + | primary + { + $$ = $1; + } + +call_args : /* none */ + { + $$ = 0; + } + | call_args0 opt_nl + +call_args0 : args + | command_call + { + value_expr($1); + $$ = NEW_LIST($1); + } + | assocs + { + $$ = NEW_LIST(NEW_HASH($1)); + } + | args ',' assocs + { + $$ = list_append($1, NEW_HASH($3)); + } + | args ',' assocs ',' STAR arg + { + $$ = list_append($1, NEW_HASH($3)); + $$ = call_op($$, '+', 1, $6); + } + | args ',' STAR arg + { + $$ = call_op($1, '+', 1, $4); + } + | STAR arg + { + value_expr($2); + $$ = $2; + } + +opt_args : /* none */ + { + $$ = 0; + } + | args + +args : arg + { + value_expr($1); + $$ = NEW_LIST($1); + } + | args ',' arg + { + value_expr($3); + $$ = list_append($1, $3); + } + +mrhs : args + { + if ($1 && $1->nd_next == 0) { + $$ = $1->nd_head; + } + else { + $$ = $1; + } + } + | args ',' STAR arg + { + $$ = call_op($1, '+', 1, $4); + } + | STAR arg + { + value_expr($2); + $$ = $2; + } + +ret_args : call_args0 + { + if ($1 && + nd_type($1) == NODE_ARRAY && + $1->nd_next == 0) { + $$ = $1->nd_head; + } + else { + $$ = $1; + } + } + +array : /* none */ + { + $$ = 0; + } + | args trailer + +primary : literal + { + $$ = NEW_LIT($1); + } + | primary COLON2 cname + { + $$ = NEW_COLON2($1, $3); + } + | STRING + { + $$ = NEW_STR($1); + } + | DSTRING + | XSTRING + { + $$ = NEW_XSTR($1); + } + | DXSTRING + | DREGEXP + | var_ref + | backref + | SUPER '(' call_args ')' + { + if (!cur_mid && !in_single && !in_defined) + yyerror("super called outside of method"); + $$ = NEW_SUPER($3); + } + | SUPER + { + if (!cur_mid && !in_single && !in_defined) + yyerror("super called outside of method"); + $$ = NEW_ZSUPER(); + } + | primary '[' opt_args opt_nl ']' + { + value_expr($1); + $$ = NEW_CALL($1, AREF, $3); + } + | LBRACK array ']' + { + if ($2 == 0) + $$ = NEW_ZARRAY(); /* zero length array*/ + else { + $$ = $2; + } + } + | LBRACE assoc_list '}' + { + $$ = NEW_HASH($2); + } + | RETURN '(' ret_args ')' + { + if (!cur_mid && !in_single) + yyerror("return appeared outside of method"); + value_expr($3); + $$ = NEW_RET($3); + } + | RETURN '(' ')' + { + if (!cur_mid && !in_single) + yyerror("return appeared outside of method"); + $$ = NEW_RET(0); + } + | RETURN + { + if (!cur_mid && !in_single) + yyerror("return appeared outside of method"); + $$ = NEW_RET(0); + } + | YIELD '(' ret_args ')' + { + value_expr($3); + $$ = NEW_YIELD($3); + } + | YIELD '(' ')' + { + $$ = NEW_YIELD(0); + } + | YIELD + { + $$ = NEW_YIELD(0); + } + | DEFINED opt_nl '(' {in_defined = 1;} expr ')' + { + in_defined = 0; + $$ = NEW_DEFINED($5); + } + | FID + { + $$ = NEW_FCALL($1, 0); + } + | operation iter_block + { + $2->nd_iter = NEW_FCALL($1, 0); + $$ = $2; + } + | method_call + | method_call iter_block + { + $2->nd_iter = $1; + $$ = $2; + fixpos($$, $1); + } + | IF expr then + compexpr + if_tail + END + { + value_expr($2); + $$ = NEW_IF(cond($2), $4, $5); + fixpos($$, $2); + } + | UNLESS expr then + compexpr + opt_else + END + { + value_expr($2); + $$ = NEW_UNLESS(cond($2), $4, $5); + fixpos($$, $2); + } + | WHILE expr term compexpr END + { + value_expr($2); + $$ = NEW_WHILE(cond($2), $4, 1); + fixpos($$, $2); + } + | UNTIL expr term compexpr END + { + value_expr($2); + $$ = NEW_UNTIL(cond($2), $4, 1); + fixpos($$, $2); + } + | CASE compexpr + case_body + END + { + value_expr($2); + $$ = NEW_CASE($2, $3); + fixpos($$, $2); + } + | FOR iter_var IN expr term compexpr END + { + value_expr($2); + $$ = NEW_FOR($2, $4, $6); + fixpos($$, $2); + } + | BEGIN + compexpr + rescue + ensure + END + { + if (!$3 && !$4) + $$ = NEW_BEGIN($2); + else { + if ($3) $2 = NEW_RESCUE($2, $3); + if ($4) $2 = NEW_ENSURE($2, $4); + $$ = $2; + } + fixpos($$, $2); + } + | LPAREN compexpr ')' + { + $$ = $2; + } + | CLASS cname superclass + { + if (cur_mid || in_single) + yyerror("class definition in method body"); + + class_nest++; + cref_push(); + local_push(); + } + compexpr + END + { + $$ = NEW_CLASS($2, $5, $3); + fixpos($$, $3); + local_pop(); + cref_pop(); + class_nest--; + } + | CLASS LSHFT expr term + { + if (cur_mid || in_single) + yyerror("class definition in method body"); + + class_nest++; + cref_push(); + local_push(); + } + compexpr + END + { + $$ = NEW_SCLASS($3, $6); + fixpos($$, $3); + local_pop(); + cref_pop(); + class_nest--; + } + | MODULE cname + { + if (cur_mid || in_single) + yyerror("module definition in method body"); + class_nest++; + cref_push(); + local_push(); + } + compexpr + END + { + $$ = NEW_MODULE($2, $4); + fixpos($$, $4); + local_pop(); + cref_pop(); + class_nest--; + } + | DEF fname + { + if (cur_mid || in_single) + yyerror("nested method definition"); + cur_mid = $2; + local_push(); + } + f_arglist + compexpr + END + { + $$ = NEW_DEFN($2, $4, $5, class_nest?0:1); + fixpos($$, $4); + local_pop(); + cur_mid = 0; + } + | DEF singleton '.' {lex_state = EXPR_FNAME;} fname + { + value_expr($2); + in_single++; + local_push(); + lex_state = EXPR_END; /* force for args */ + } + f_arglist + compexpr + END + { + $$ = NEW_DEFS($2, $5, $7, $8); + fixpos($$, $2); + local_pop(); + in_single--; + } + +then : term + | THEN + | term THEN + +if_tail : opt_else + | ELSIF expr then + compexpr + if_tail + { + value_expr($2); + $$ = NEW_IF(cond($2), $4, $5); + fixpos($$, $2); + } + +opt_else : /* none */ + { + $$ = 0; + } + | ELSE compexpr + { + $$ = $2; + } + +iter_var : lhs + | mlhs + +opt_iter_var : /* node */ + { + $$ = 0; + } + | '|' /* none */ '|' + { + $$ = 0; + } + | OROP + { + $$ = 0; + } + | '|' iter_var '|' + { + $$ = $2; + } + +iter_do_block : DO + { + $<vars>$ = dyna_push(); + } + opt_iter_var + compexpr + END + { + $$ = NEW_ITER($3, 0, $4); + fixpos($$, $3?$3:$4); + dyna_pop($<vars>2); + } + +iter_block : '{' + { + $<vars>$ = dyna_push(); + } + opt_iter_var + compexpr '}' + { + $$ = NEW_ITER($3, 0, $4); + fixpos($$, $3?$3:$4); + dyna_pop($<vars>2); + } + +iterator : IDENTIFIER + { + $$ = NEW_FCALL($1, 0); + } + | CONSTANT + { + $$ = NEW_FCALL($1, 0); + } + | FID + { + $$ = NEW_FCALL($1, 0); + } + | method_call + | command_call + +method_call : operation '(' call_args ')' + { + $$ = NEW_FCALL($1, $3); + fixpos($$, $3); + } + | primary '.' operation '(' call_args ')' + { + value_expr($1); + $$ = NEW_CALL($1, $3, $5); + fixpos($$, $1); + } + | primary '.' operation + { + value_expr($1); + $$ = NEW_CALL($1, $3, 0); + } + | primary COLON2 operation '(' call_args ')' + { + value_expr($1); + $$ = NEW_CALL($1, $3, $5); + fixpos($$, $1); + } + +case_body : WHEN args then + compexpr + cases + { + $$ = NEW_WHEN($2, $4, $5); + } + +cases : opt_else + | case_body + +rescue : RESCUE opt_args term compexpr + rescue + { + $$ = NEW_RESBODY($2, $4, $5); + fixpos($$, $2?$2:$4); + } + | /* none */ + { + $$ = 0; + } + +ensure : /* none */ + { + $$ = 0; + } + | ENSURE compexpr + { + $$ = $2; + } + +literal : numeric + | SYMBEG {lex_state = EXPR_FNAME;} symbol + { + $$ = INT2FIX($3); + } + | REGEXP + +symbol : fname + | IVAR + | GVAR + +numeric : INTEGER + | FLOAT + +variable : IDENTIFIER + | IVAR + | GVAR + | CONSTANT + | NIL + { + $$ = NIL; + } + | SELF + { + $$ = SELF; + } + +var_ref : variable + { + $$ = gettable($1); + } + +backref : NTH_REF + | BACK_REF + +superclass : term + { + $$ = 0; + } + | '<' + { + lex_state = EXPR_BEG; + } + expr term + { + $$ = $3; + } + | error term {yyerrok;} + +f_arglist : '(' f_args ')' + { + $$ = $2; + lex_state = EXPR_BEG; + } + | f_args term + { + $$ = $1; + } + +f_args : /* no arg */ + { + $$ = NEW_ARGS(0, 0, -1); + } + | f_arg + { + $$ = NEW_ARGS($1, 0, -1); + } + | f_arg ',' rest_arg + { + $$ = NEW_ARGS($1, 0, $3); + } + | f_arg ',' f_optarg + { + $$ = NEW_ARGS($1, $3, -1); + } + | f_arg ',' f_optarg ',' rest_arg + { + $$ = NEW_ARGS($1, $3, $5); + } + | f_optarg + { + $$ = NEW_ARGS(0, $1, -1); + } + | f_optarg ',' rest_arg + { + $$ = NEW_ARGS(0, $1, $3); + } + | rest_arg + { + $$ = NEW_ARGS(0, 0, $1); + } + +f_arg : IDENTIFIER + { + if (!is_local_id($1)) + yyerror("formal argument must be local variable"); + local_cnt($1); + $$ = 1; + } + | f_arg ',' IDENTIFIER + { + if (!is_local_id($3)) + yyerror("formal argument must be local variable"); + local_cnt($3); + $$ += 1; + } + +f_opt : IDENTIFIER '=' arg + { + if (!is_local_id($1)) + yyerror("formal argument must be local variable"); + $$ = assignable($1, $3); + } + +f_optarg : f_opt + { + $$ = NEW_BLOCK($1); + $$->nd_end = $$; + } + | f_optarg ',' f_opt + { + $$ = block_append($1, $3); + } + +rest_arg : STAR IDENTIFIER + { + if (!is_local_id($2)) + yyerror("rest argument must be local variable"); + $$ = local_cnt($2); + } + +singleton : var_ref + { + if (nd_type($1) == NODE_SELF) { + $$ = NEW_SELF(); + } + else if (nd_type($1) == NODE_NIL) { + yyerror("Can't define single method for nil."); + $$ = 0; + } + else { + $$ = $1; + } + } + | LPAREN expr opt_nl ')' + { + switch (nd_type($2)) { + case NODE_STR: + case NODE_DSTR: + case NODE_XSTR: + case NODE_DXSTR: + case NODE_DREGX: + case NODE_LIT: + case NODE_ARRAY: + case NODE_ZARRAY: + yyerror("Can't define single method for literals."); + default: + break; + } + $$ = $2; + } + +assoc_list : /* none */ + { + $$ = 0; + } + | assocs trailer + { + $$ = $1; + } + | args trailer + { + if ($1->nd_alen%2 != 0) { + yyerror("odd number list for Hash"); + } + $$ = $1; + } + +assocs : assoc + | assocs ',' assoc + { + $$ = list_concat($1, $3); + } + +assoc : arg ASSOC arg + { + $$ = list_append(NEW_LIST($1), $3); + } + +operation : IDENTIFIER + | CONSTANT + | FID + +opt_terms : /* none */ + | terms + +opt_nl : /* none */ + | '\n' + +trailer : /* none */ + | '\n' + | ',' + +term : ';' {yyerrok;} + | '\n' + +terms : term + | terms ';' {yyerrok;} +%% +#include <ctype.h> +#include <sys/types.h> +#include "regex.h" +#include "util.h" + +#define is_identchar(c) ((c)!=-1&&(isalnum(c) || (c) == '_' || ismbchar(c))) + +static char *tokenbuf = NULL; +static int tokidx, toksiz = 0; + +VALUE newregexp(); +VALUE newstring(); +VALUE newfloat(); +VALUE newinteger(); +char *strdup(); + +static NODE *str_extend(); + +#define LEAVE_BS 1 + +static VALUE lex_input; /* non-nil if File */ +static char *lex_pbeg; +static char *lex_p; +static char *lex_pend; + +static int +yyerror(msg) + char *msg; +{ + char *p, *pe, *buf; + int len, i; + + Error("%s", msg); + p = lex_p; + while (lex_pbeg <= p) { + if (*p == '\n') break; + p--; + } + p++; + + pe = lex_p; + while (pe < lex_pend) { + if (*pe == '\n') break; + pe++; + } + + len = pe - p; + if (len > 4) { + buf = ALLOCA_N(char, len+2); + MEMCPY(buf, p, char, len); + buf[len] = '\0'; + Error_Append("%s", buf); + + i = lex_p - p; + p = buf; pe = p + len; + + while (p < pe) { + if (*p != '\t') *p = ' '; + p++; + } + buf[i] = '^'; + buf[i+1] = '\0'; + Error_Append("%s", buf); + } + + return 0; +} + +static int newline_seen; + +int rb_in_compile = 0; + +static NODE* +yycompile(f) + char *f; +{ + int n; + + newline_seen = 0; + sourcefile = strdup(f); + eval_tree = 0; + rb_in_compile = 1; + n = yyparse(); + rb_in_compile = 0; + if (n == 0) return eval_tree; + + return 0; +} + +NODE* +compile_string(f, s, len) + char *f, *s; + int len; +{ + lex_pbeg = lex_p = s; + lex_pend = s + len; + lex_input = 0; + if (!sourcefile || strcmp(f, sourcefile)) /* not in eval() */ + sourceline = 1; + + return yycompile(f); +} + +NODE* +compile_file(f, file, start) + char *f; + VALUE file; + int start; +{ + lex_input = file; + lex_pbeg = lex_p = lex_pend = 0; + sourceline = start; + + return yycompile(f); +} + +static int +nextc() +{ + int c; + + if (lex_p == lex_pend) { + if (lex_input) { + VALUE v = io_gets(lex_input); + + if (NIL_P(v)) return -1; + lex_pbeg = lex_p = RSTRING(v)->ptr; + lex_pend = lex_p + RSTRING(v)->len; + } + else { + return -1; + } + } + c = (unsigned char)*lex_p++; + + return c; +} + +void +pushback(c) + int c; +{ + if (c == -1) return; + lex_p--; +} + +#define tokfix() (tokenbuf[tokidx]='\0') +#define tok() tokenbuf +#define toklen() tokidx +#define toklast() (tokidx>0?tokenbuf[tokidx-1]:0) + +static char* +newtok() +{ + tokidx = 0; + if (!tokenbuf) { + toksiz = 60; + tokenbuf = ALLOC_N(char, 60); + } + if (toksiz > 1024) { + toksiz = 60; + REALLOC_N(tokenbuf, char, 60); + } + return tokenbuf; +} + +static void +tokadd(c) + char c; +{ + tokenbuf[tokidx++] = c; + if (tokidx >= toksiz) { + toksiz *= 2; + REALLOC_N(tokenbuf, char, toksiz); + } +} + +static int +read_escape() +{ + int c; + + switch (c = nextc()) { + case '\\': /* Backslash */ + return c; + + case 'n': /* newline */ + return '\n'; + + case 't': /* horizontal tab */ + return '\t'; + + case 'r': /* carriage-return */ + return '\r'; + + case 'f': /* form-feed */ + return '\f'; + + case 'v': /* vertical tab */ + return '\13'; + + case 'a': /* alarm(bell) */ + return '\007'; + + case 'e': /* escape */ + return 033; + + case '0': case '1': case '2': case '3': /* octal constant */ + case '4': case '5': case '6': case '7': + { + char buf[3]; + int i; + + pushback(c); + for (i=0; i<3; i++) { + c = nextc(); + if (c == -1) goto eof; + if (c < '0' || '7' < c) { + pushback(c); + break; + } + buf[i] = c; + } + c = scan_oct(buf, i+1, &i); + } + return c; + + case 'x': /* hex constant */ + { + char buf[2]; + int i; + + for (i=0; i<2; i++) { + buf[i] = nextc(); + if (buf[i] == -1) goto eof; + if (!isxdigit(buf[i])) { + pushback(buf[i]); + break; + } + } + c = scan_hex(buf, i+1, &i); + } + return c; + + case 'b': /* backspace */ + return '\b'; + + case 'M': + if ((c = nextc()) != '-') { + yyerror("Invalid escape character syntax"); + pushback(c); + return '\0'; + } + if ((c = nextc()) == '\\') { + return read_escape() | 0x80; + } + else if (c == -1) goto eof; + else { + return ((c & 0xff) | 0x80); + } + + case 'C': + if ((c = nextc()) != '-') { + yyerror("Invalid escape character syntax"); + pushback(c); + return '\0'; + } + case 'c': + case '^': + if ((c = nextc())== '\\') { + c = read_escape(); + } + else if (c == '?') + return 0177; + else if (c == -1) goto eof; + return c & 0x9f; + + eof: + case -1: + yyerror("Invalid escape character syntax"); + return '\0'; + + default: + return c; + } +} + +static int +parse_regx(term) + int term; +{ + register int c; + char kcode = 0; + int once = 0; + int casefold = 0; + int in_brack = 0; + int re_start = sourceline; + NODE *list = 0; + + newtok(); + while ((c = nextc()) != -1) { + if (!in_brack && c == term) { + goto regx_end; + } + + switch (c) { + case '[': + in_brack = 1; + break; + case ']': + in_brack = 0; + break; + + case '#': + list = str_extend(list, term); + if (list == (NODE*)-1) return 0; + continue; + + case '\\': + switch (c = nextc()) { + case -1: + sourceline = re_start; + Error("unterminated regexp meets end of file"); + return 0; + + case '\n': + sourceline++; + break; + + case '\\': + tokadd('\\'); + tokadd('\\'); + break; + + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + case '0': case 'x': + tokadd('\\'); + tokadd(c); + break; + + case '^': /* no \^ escape in regexp */ + tokadd('\\'); + tokadd('^'); + break; + + case 'b': + if (!in_brack) { + tokadd('\\'); + tokadd('b'); + break; + } + /* fall through */ + default: + if (c == '\n') { + sourceline++; + } + else if (c == term) { + tokadd(c); + } + else { + pushback(c); + tokadd('\\'); + tokadd(read_escape()); + } + } + continue; + + case -1: + Error("unterminated regexp"); + return 0; + + default: + if (ismbchar(c)) { + tokadd(c); + c = nextc(); + } + break; + + regx_end: + for (;;) { + switch (c = nextc()) { + case 'i': + casefold = 1; + break; + case 'o': + once = 1; + break; + case 'n': + kcode = 2; + break; + case 'e': + kcode = 4; + break; + case 's': + kcode = 6; + break; + default: + pushback(c); + goto end_options; + } + } + + end_options: + tokfix(); + lex_state = EXPR_END; + if (list) { + if (toklen() > 0) { + VALUE ss = str_new(tok(), toklen()); + list_append(list, NEW_STR(ss)); + } + nd_set_type(list, once?NODE_DREGX_ONCE:NODE_DREGX); + list->nd_cflag = kcode | casefold; + yylval.node = list; + return DREGEXP; + } + else { + yylval.val = reg_new(tok(), toklen(), kcode | casefold); + return REGEXP; + } + } + tokadd(c); + } + Error("unterminated regexp"); + return 0; +} + +static int +parse_string(func,term) + int func, term; +{ + int c; + NODE *list = 0; + int strstart; + + strstart = sourceline; + newtok(); + + while ((c = nextc()) != term) { + str_retry: + if (c == -1) { + unterm_str: + sourceline = strstart; + Error("unterminated string meets end of file"); + return 0; + } + if (ismbchar(c)) { + tokadd(c); + c = nextc(); + } + else if (c == '\n') { + sourceline++; + } + else if (c == '#') { + list = str_extend(list, term); + if (list == (NODE*)-1) goto unterm_str; + continue; + } + else if (c == '\\') { + c = nextc(); + if (c == '\n') { + sourceline++; + } + else if (c == term) { + tokadd(c); + } + else { + pushback(c); + if (func != '"') tokadd('\\'); + tokadd(read_escape()); + } + continue; + } + tokadd(c); + } + + tokfix(); + lex_state = EXPR_END; + if (list) { + if (toklen() > 0) { + VALUE ss = str_new(tok(), toklen()); + list_append(list, NEW_STR(ss)); + } + yylval.node = list; + if (func == '`') { + nd_set_type(list, NODE_DXSTR); + return DXSTRING; + } + else { + return DSTRING; + } + } + else { + yylval.val = str_new(tok(), toklen()); + return (func == '`') ? XSTRING : STRING; + } +} + +static int +parse_qstring(term) + int term; +{ + int strstart; + int c; + + strstart = sourceline; + newtok(); + while ((c = nextc()) != term) { + if (c == -1) { + sourceline = strstart; + Error("unterminated string meets end of file"); + return 0; + } + if (ismbchar(c)) { + tokadd(c); + c = nextc(); + } + else if (c == '\n') { + sourceline++; + } + else if (c == '\\') { + c = nextc(); + switch (c) { + case '\n': + sourceline++; + continue; + + case '\\': + c = '\\'; + break; + + case '\'': + if (term == '\'') { + c = '\''; + break; + } + /* fall through */ + default: + tokadd('\\'); + } + } + tokadd(c); + } + + tokfix(); + yylval.val = str_new(tok(), toklen()); + lex_state = EXPR_END; + return STRING; +} + +#define LAST(v) ((v)-1 + sizeof(v)/sizeof(v[0])) + +static struct kwtable { + char *name; + int id; + enum lex_state state; +} kwtable [] = { + "__END__", 0, EXPR_BEG, + "alias", ALIAS, EXPR_FNAME, + "and", AND, EXPR_BEG, + "begin", BEGIN, EXPR_BEG, + "case", CASE, EXPR_BEG, + "class", CLASS, EXPR_BEG, + "def", DEF, EXPR_FNAME, + "defined?", DEFINED, EXPR_END, + "do", DO, EXPR_BEG, + "else", ELSE, EXPR_BEG, + "elsif", ELSIF, EXPR_BEG, + "end", END, EXPR_END, + "ensure", ENSURE, EXPR_BEG, + "for", FOR, EXPR_BEG, + "if", IF, EXPR_BEG, + "in", IN, EXPR_BEG, + "module", MODULE, EXPR_BEG, + "nil", NIL, EXPR_END, + "not", NOT, EXPR_BEG, + "or", OR, EXPR_BEG, + "rescue", RESCUE, EXPR_MID, + "return", RETURN, EXPR_MID, + "self", SELF, EXPR_END, + "super", SUPER, EXPR_END, + "then", THEN, EXPR_BEG, + "undef", UNDEF, EXPR_FNAME, + "unless", UNLESS, EXPR_BEG, + "until", UNTIL, EXPR_BEG, + "when", WHEN, EXPR_BEG, + "while", WHILE, EXPR_BEG, + "yield", YIELD, EXPR_END, +}; + +static void +arg_ambiguous() +{ + Warning("ambiguous first argument; make sure"); +} + +#ifndef atof +double atof(); +#endif + +static int +yylex() +{ + register int c; + int space_seen = 0; + struct kwtable *low = kwtable, *mid, *high = LAST(kwtable); + + if (newline_seen) { + sourceline+=newline_seen; + newline_seen = 0; + } + +retry: + switch (c = nextc()) { + case '\0': /* NUL */ + case '\004': /* ^D */ + case '\032': /* ^Z */ + case -1: /* end of script. */ + return 0; + + /* white spaces */ + case ' ': case '\t': case '\f': case '\r': + case '\13': /* '\v' */ + space_seen = 1; + goto retry; + + case '#': /* it's a comment */ + while ((c = nextc()) != '\n') { + if (c == -1) + return 0; + if (c == '\\') { /* skip a char */ + c = nextc(); + if (c == '\n') sourceline++; + } + if (ismbchar(c)) { + c = nextc(); + if (c == '\n') + break; + } + } + /* fall through */ + case '\n': + /* skip embedded rd document */ + if ((c = nextc()) == '=' && + strncmp(lex_p, "begin", 5) == 0 && + (lex_p[5] == '\n' || lex_p[5] == '\r')) { + for (;;) { + if (c == -1) return 0; + c = nextc(); + if (c != '\n') continue; + c = nextc(); + if (c != '=') continue; + if (strncmp(lex_p, "end", 3) == 0 && + (lex_p[3] == '\n' || lex_p[3] == '\r')) { + lex_p += 3; /* sizeof "end" */ + break; + } + } + } + else { + pushback(c); + } + + if (lex_state == EXPR_BEG || lex_state == EXPR_FNAME) { + sourceline++; + goto retry; + } + newline_seen++; + lex_state = EXPR_BEG; + return '\n'; + + case '*': + if ((c = nextc()) == '*') { + lex_state = EXPR_BEG; + if (nextc() == '=') { + yylval.id = POW; + return OP_ASGN; + } + pushback(c); + return POW; + } + if (c == '=') { + yylval.id = '*'; + lex_state = EXPR_BEG; + return OP_ASGN; + } + pushback(c); + if (lex_state == EXPR_ARG && space_seen && !isspace(c)){ + arg_ambiguous(); + lex_state = EXPR_BEG; + return STAR; + } + if (lex_state == EXPR_BEG || lex_state == EXPR_MID) { + return STAR; + } + lex_state = EXPR_BEG; + return '*'; + + case '!': + lex_state = EXPR_BEG; + if ((c = nextc()) == '=') { + return NEQ; + } + if (c == '~') { + return NMATCH; + } + pushback(c); + return '!'; + + case '=': + if (lex_p == lex_pbeg + 1) { + /* skip embedded rd document */ + if (strncmp(lex_p, "begin", 5) == 0 && isspace(lex_p[5])) { + lex_p = lex_pend; + for (;;) { + if (c == -1) return 0; + c = nextc(); + if (c != '\n') continue; + c = nextc(); + if (c != '=') continue; + if (strncmp(lex_p, "end", 3) == 0 && isspace(lex_p[3])) { + lex_p = lex_pend; + break; + } + } + goto retry; + } + } + + lex_state = EXPR_BEG; + if ((c = nextc()) == '=') { + if ((c = nextc()) == '=') { + return EQQ; + } + pushback(c); + return EQ; + } + if (c == '~') { + return MATCH; + } + else if (c == '>') { + return ASSOC; + } + pushback(c); + return '='; + + case '<': + lex_state = EXPR_BEG; + if ((c = nextc()) == '=') { + if ((c = nextc()) == '>') { + return CMP; + } + pushback(c); + return LEQ; + } + if (c == '<') { + if (nextc() == '=') { + yylval.id = LSHFT; + return OP_ASGN; + } + pushback(c); + return LSHFT; + } + pushback(c); + return '<'; + + case '>': + lex_state = EXPR_BEG; + if ((c = nextc()) == '=') { + return GEQ; + } + if (c == '>') { + if ((c = nextc()) == '=') { + yylval.id = RSHFT; + return OP_ASGN; + } + pushback(c); + return RSHFT; + } + pushback(c); + return '>'; + + case '"': + return parse_string(c,c); + case '`': + if (lex_state == EXPR_FNAME) return c; + return parse_string(c,c); + + case '\'': + return parse_qstring(c); + + case '?': + if ((c = nextc()) == '\\') { + c = read_escape(); + } + c &= 0xff; + yylval.val = INT2FIX(c); + lex_state = EXPR_END; + return INTEGER; + + case '&': + lex_state = EXPR_BEG; + if ((c = nextc()) == '&') { + return ANDOP; + } + else if (c == '=') { + yylval.id = '&'; + return OP_ASGN; + } + pushback(c); + return '&'; + + case '|': + lex_state = EXPR_BEG; + if ((c = nextc()) == '|') { + return OROP; + } + else if (c == '=') { + yylval.id = '|'; + return OP_ASGN; + } + pushback(c); + return '|'; + + case '+': + c = nextc(); + if (lex_state == EXPR_FNAME) { + if (c == '@') { + return UPLUS; + } + pushback(c); + return '+'; + } + if (c == '=') { + lex_state = EXPR_BEG; + yylval.id = '+'; + return OP_ASGN; + } + if (lex_state == EXPR_ARG) { + if (space_seen && !isspace(c)) { + arg_ambiguous(); + } + else { + lex_state = EXPR_END; + } + } + if (lex_state != EXPR_END) { + if (isdigit(c)) { + goto start_num; + } + pushback(c); + lex_state = EXPR_BEG; + return UPLUS; + } + lex_state = EXPR_BEG; + pushback(c); + return '+'; + + case '-': + c = nextc(); + if (lex_state == EXPR_FNAME) { + if (c == '@') { + return UMINUS; + } + pushback(c); + return '-'; + } + if (c == '=') { + lex_state = EXPR_BEG; + yylval.id = '-'; + return OP_ASGN; + } + if (lex_state == EXPR_ARG) { + if (space_seen && !isspace(c)) { + arg_ambiguous(); + } + else { + lex_state = EXPR_END; + } + } + if (lex_state != EXPR_END) { + if (isdigit(c)) { + pushback(c); + c = '-'; + goto start_num; + } + lex_state = EXPR_BEG; + pushback(c); + return UMINUS; + } + lex_state = EXPR_BEG; + pushback(c); + return '-'; + + case '.': + lex_state = EXPR_BEG; + if ((c = nextc()) == '.') { + if ((c = nextc()) == '.') { + return DOT3; + } + pushback(c); + return DOT2; + } + pushback(c); + if (!isdigit(c)) { + return '.'; + } + c = '.'; + /* fall through */ + + start_num: + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + int is_float, seen_point, seen_e; + + is_float = seen_point = seen_e = 0; + lex_state = EXPR_END; + newtok(); + if (c == '-' || c == '+') { + tokadd(c); + c = nextc(); + } + if (c == '0') { + c = nextc(); + if (c == 'x' || c == 'X') { + /* hexadecimal */ + while (c = nextc()) { + if (c == '_') continue; + if (!isxdigit(c)) break; + tokadd(c); + } + pushback(c); + tokfix(); + yylval.val = str2inum(tok(), 16); + return INTEGER; + } + else if (c >= '0' && c <= '7') { + /* octal */ + do { + tokadd(c); + while ((c = nextc()) == '_') + ; + } while (c >= '0' && c <= '9'); + pushback(c); + tokfix(); + yylval.val = str2inum(tok(), 8); + return INTEGER; + } + else if (c > '7' && c <= '9') { + Error("Illegal octal digit"); + } + else if (c == '.') { + tokadd('0'); + } + else { + pushback(c); + yylval.val = INT2FIX(0); + return INTEGER; + } + } + + for (;;) { + switch (c) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + tokadd(c); + break; + + case '.': + if (seen_point) { + goto decode_num; + } + else { + int c0 = nextc(); + if (!isdigit(c0)) { + pushback(c0); + goto decode_num; + } + c = c0; + } + tokadd('.'); + tokadd(c); + is_float++; + seen_point++; + break; + + case 'e': + case 'E': + if (seen_e) { + goto decode_num; + } + tokadd(c); + seen_e++; + is_float++; + if ((c = nextc()) == '-' || c == '+') + tokadd(c); + else + continue; + break; + + case '_': /* `_' in decimal just ignored */ + break; + + default: + goto decode_num; + } + c = nextc(); + } + + decode_num: + pushback(c); + tokfix(); + if (is_float) { + yylval.val = float_new(atof(tok())); + return FLOAT; + } + yylval.val = str2inum(tok(), 10); + return INTEGER; + } + + case ']': + case '}': + case ')': + lex_state = EXPR_END; + return c; + + case ':': + c = nextc(); + if (c == ':') { + lex_state = EXPR_BEG; + return COLON2; + } + pushback(c); + if (isspace(c)) + return ':'; + return SYMBEG; + + case '/': + if (lex_state == EXPR_BEG || lex_state == EXPR_MID) { + return parse_regx('/'); + } + if ((c = nextc()) == '=') { + lex_state = EXPR_BEG; + yylval.id = '/'; + return OP_ASGN; + } + if (lex_state == EXPR_ARG) { + if (space_seen && !isspace(c)) { + pushback(c); + arg_ambiguous(); + return parse_regx('/'); + } + } + lex_state = EXPR_BEG; + pushback(c); + return '/'; + + case '^': + lex_state = EXPR_BEG; + if (nextc() == '=') { + yylval.id = '^'; + return OP_ASGN; + } + pushback(c); + return c; + + case ',': + lex_state = EXPR_BEG; + return c; + + case ';': + lex_state = EXPR_BEG; + return c; + + case '~': + if (lex_state == EXPR_FNAME) { + if ((c = nextc()) != '@') { + pushback(c); + } + } + lex_state = EXPR_BEG; + return '~'; + + case '(': + if (lex_state == EXPR_BEG || lex_state == EXPR_MID) { + c = LPAREN; + lex_state = EXPR_BEG; + } + else if (lex_state == EXPR_ARG && space_seen) { + arg_ambiguous(); + c = LPAREN; + lex_state = EXPR_BEG; + } + else { + lex_state = EXPR_BEG; + } + return c; + + case '[': + if (lex_state == EXPR_FNAME) { + if ((c = nextc()) == ']') { + if ((c = nextc()) == '=') { + return ASET; + } + pushback(c); + return AREF; + } + pushback(c); + return '['; + } + else if (lex_state == EXPR_BEG || lex_state == EXPR_MID) { + c = LBRACK; + } + else if (lex_state == EXPR_ARG && space_seen) { + arg_ambiguous(); + c = LBRACK; + } + lex_state = EXPR_BEG; + return c; + + case '{': + if (lex_state != EXPR_END && lex_state != EXPR_ARG) + c = LBRACE; + lex_state = EXPR_BEG; + return c; + + case '\\': + c = nextc(); + if (c == '\n') { + sourceline++; + space_seen = 1; + goto retry; /* skip \\n */ + } + pushback(c); + return '\\'; + + case '%': + if (lex_state == EXPR_BEG || lex_state == EXPR_MID) { + int term; + + c = nextc(); + quotation: + if (!isalnum(c)) { + term = c; + switch (c) { + case '\'': + c = 'q'; break; + case '/': + c = 'r'; break; + case '`': + c = 'x'; break; + default: + c = 'Q';break; + } + } + else { + term = nextc(); + } + if (c == -1 || term == -1) { + Error("unterminated quoted string meets end of file"); + return 0; + } + if (term == '(') term = ')'; + else if (term == '[') term = ']'; + else if (term == '{') term = '}'; + else if (term == '<') term = '>'; + + switch (c) { + case 'Q': + return parse_string('"', term); + + case 'q': + return parse_qstring(term); + + case 'x': + return parse_string('`', term); + + case 'r': + return parse_regx(term); + + default: + Error("unknown type of string `%c'", c); + return 0; + } + } + if ((c = nextc()) == '=') { + yylval.id = '%'; + return OP_ASGN; + } + if (lex_state == EXPR_ARG) { + if (space_seen && !isspace(c)) { + arg_ambiguous(); + goto quotation; + } + } + lex_state = EXPR_BEG; + pushback(c); + return '%'; + + case '$': + lex_state = EXPR_END; + newtok(); + c = nextc(); + switch (c) { + case '_': /* $_: last read line string */ + case '~': /* $~: match-data */ + local_cnt(c); + /* fall through */ + case '*': /* $*: argv */ + case '$': /* $$: pid */ + case '?': /* $?: last status */ + case '!': /* $!: error string */ + case '@': /* $@: error position */ + case '/': /* $/: input record separator */ + case '\\': /* $\: output record separator */ + case ';': /* $;: field separator */ + case ',': /* $,: output field separator */ + case '.': /* $.: last read line number */ + case '=': /* $=: ignorecase */ + case ':': /* $:: load path */ + case '<': /* $<: reading filename */ + case '>': /* $>: default output handle */ + case '\"': /* $": already loaded files */ + tokadd('$'); + tokadd(c); + tokfix(); + yylval.id = rb_intern(tok()); + return GVAR; + + case '-': + tokadd('$'); + tokadd(c); + c = nextc(); + tokadd(c); + tokfix(); + yylval.id = rb_intern(tok()); + return GVAR; + + case '&': /* $&: last match */ + case '`': /* $`: string before last match */ + case '\'': /* $': string after last match */ + case '+': /* $+: string matches last paren. */ + yylval.node = NEW_BACK_REF(c); + return BACK_REF; + + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + while (isdigit(c)) { + tokadd(c); + c = nextc(); + } + pushback(c); + tokfix(); + yylval.node = NEW_NTH_REF(atoi(tok())); + return NTH_REF; + + default: + if (!is_identchar(c)) { + pushback(c); + return '$'; + } + case '0': + tokadd('$'); + } + break; + + case '@': + c = nextc(); + if (!is_identchar(c)) { + pushback(c); + return '@'; + } + newtok(); + tokadd('@'); + break; + + default: + if (c != '_' && !isalpha(c) && !ismbchar(c)) { + Error("Invalid char '%c' in expression", c); + goto retry; + } + + newtok(); + break; + } + + while (is_identchar(c)) { + tokadd(c); + if (ismbchar(c)) { + c = nextc(); + tokadd(c); + } + c = nextc(); + } + if (c == '!' || c == '?') { + tokadd(c); + } + else { + pushback(c); + } + tokfix(); + + { + int result; + + switch (tok()[0]) { + case '$': + lex_state = EXPR_END; + result = GVAR; + break; + case '@': + lex_state = EXPR_END; + result = IVAR; + break; + default: + /* See if it is a reserved word. */ + while (low <= high) { + mid = low + (high - low)/2; + if ((c = strcmp(mid->name, tok())) == 0) { + enum lex_state state = lex_state; + lex_state = mid->state; + if (state != EXPR_BEG) { + if (mid->id == IF) return IF_MOD; + if (mid->id == UNLESS) return UNLESS_MOD; + if (mid->id == WHILE) return WHILE_MOD; + if (mid->id == UNTIL) return UNTIL_MOD; + } + return mid->id; + } + else if (c < 0) { + low = mid + 1; + } + else { + high = mid - 1; + } + } + + if (lex_state == EXPR_FNAME) { + lex_state = EXPR_END; + if ((c = nextc()) == '=') { + tokadd(c); + } + else { + pushback(c); + } + } + else if (lex_state == EXPR_BEG){ + lex_state = EXPR_ARG; + } + else { + lex_state = EXPR_END; + } + if (isupper(tok()[0])) { + result = CONSTANT; + } + else if (toklast() == '!' || toklast() == '?') { + result = FID; + } else { + result = IDENTIFIER; + } + } + tokfix(); + yylval.id = rb_intern(tok()); + return result; + } +} + +static NODE* +str_extend(list, term) + NODE *list; + char term; +{ + int c; + VALUE ss; + NODE *node; + int nest; + + c = nextc(); + switch (c) { + case '$': + case '@': + case '{': + break; + default: + tokadd('#'); + pushback(c); + return list; + } + + ss = str_new(tok(), toklen()); + if (list == 0) { + list = NEW_DSTR(ss); + } + else if (toklen() > 0) { + list_append(list, NEW_STR(ss)); + } + newtok(); + + switch (c) { + case '$': + tokadd('$'); + c = nextc(); + if (c == -1) return (NODE*)-1; + switch (c) { + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + while (isdigit(c)) { + tokadd(c); + c = nextc(); + } + pushback(c); + goto fetch_id; + + case '&': case '+': + case '_': case '~': + case '*': case '$': case '?': + case '!': case '@': case ',': + case '.': case '=': case ':': + case '<': case '>': case '\\': + refetch: + tokadd(c); + goto fetch_id; + + default: + if (c == term) { + list_append(list, NEW_STR(str_new2("#$"))); + pushback(c); + newtok(); + return list; + } + switch (c) { + case '\"': + case '/': + case '\'': + case '`': + goto refetch; + } + if (!is_identchar(c)) { + yyerror("bad global variable in string"); + newtok(); + return list; + } + } + /* through */ + + case '@': + tokadd(c); + c = nextc(); + while (is_identchar(c)) { + tokadd(c); + if (ismbchar(c)) { + c = nextc(); + tokadd(c); + } + c = nextc(); + } + pushback(c); + break; + + case '{': + nest = 0; + do { + loop_again: + c = nextc(); + switch (c) { + case -1: + if (nest > 0) { + Error("bad substitution in string"); + newtok(); + return list; + } + return (NODE*)-1; + case '}': + if (nest == 0) break; + nest--; + tokadd(c); + goto loop_again; + case '\\': + c = read_escape(); + tokadd(c); + goto loop_again; + case '{': + nest++; + case '\"': + case '/': + case '`': + if (c == term) { + pushback(c); + list_append(list, NEW_STR(str_new2("#"))); + Warning("bad substitution in string"); + tokfix(); + list_append(list, NEW_STR(str_new(tok(), toklen()))); + newtok(); + return list; + } + default: + tokadd(c); + break; + } + } while (c != '}'); + } + + fetch_id: + tokfix(); + node = NEW_EVSTR(tok(),toklen()); + list_append(list, node); + newtok(); + + return list; +} + +NODE* +node_newnode(type, a0, a1, a2) + enum node_type type; + NODE *a0, *a1, *a2; +{ + NODE *n = (NODE*)rb_newobj(); + + n->flags |= T_NODE; + nd_set_type(n, type); + nd_set_line(n, sourceline); + n->file = sourcefile; + + n->u1.node = a0; + n->u2.node = a1; + n->u3.node = a2; + + return n; +} + +enum node_type +nodetype(node) /* for debug */ + NODE *node; +{ + return (enum node_type)nd_type(node); +} + +int +nodeline(node) + NODE *node; +{ + return nd_line(node); +} + +static NODE* +newline_node(node) + NODE *node; +{ + NODE *nl = 0; + if (node) { + nl = NEW_NEWLINE(node); + fixpos(nl, node); + nl->nd_nth = nd_line(node); + } + return nl; +} + +static void +fixpos(node, orig) + NODE *node, *orig; +{ + if (!node) return; + if (!orig) return; + node->file = orig->file; + nd_set_line(node, nd_line(orig)); +} + +static NODE* +block_append(head, tail) + NODE *head, *tail; +{ + extern int verbose; + NODE *end; + + if (tail == 0) return head; + if (head == 0) return tail; + + if (nd_type(head) != NODE_BLOCK) { + end = NEW_BLOCK(head); + end->nd_end = end; + fixpos(end, head); + head = end; + } + else { + end = head->nd_end; + } + + if (verbose) { + NODE *nd = end->nd_head; + newline: + switch (nd_type(nd)) { + case NODE_RETURN: + Warning("statement not reached"); + break; + + case NODE_NEWLINE: + nd = nd->nd_next; + goto newline; + + default: + break; + } + } + + if (nd_type(tail) != NODE_BLOCK) { + tail = NEW_BLOCK(tail); + tail->nd_end = tail; + } + end->nd_next = tail; + head->nd_end = tail->nd_end; + return head; +} + +static NODE* +list_append(head, tail) + NODE *head, *tail; +{ + NODE *last; + + if (head == 0) return NEW_LIST(tail); + + last = head; + while (last->nd_next) { + last = last->nd_next; + } + + last->nd_next = NEW_LIST(tail); + head->nd_alen += 1; + return head; +} + +static NODE* +list_concat(head, tail) + NODE *head, *tail; +{ + NODE *last; + + last = head; + while (last->nd_next) { + last = last->nd_next; + } + + last->nd_next = tail; + head->nd_alen += tail->nd_alen; + + return head; +} + +static NODE * +call_op(recv, id, narg, arg1) + NODE *recv; + ID id; + int narg; + NODE *arg1; +{ + value_expr(recv); + if (narg == 1) { + value_expr(arg1); + } + + return NEW_CALL(recv, id, narg==1?NEW_LIST(arg1):0); +} + +static NODE* +gettable(id) + ID id; +{ + if (id == SELF) { + return NEW_SELF(); + } + else if (id == NIL) { + return NEW_NIL(); + } + else if (is_local_id(id)) { + if (local_id(id)) return NEW_LVAR(id); + if (dyna_var_defined(id)) return NEW_DVAR(id); + /* method call without arguments */ + return NEW_VCALL(id); + } + else if (is_global_id(id)) { + return NEW_GVAR(id); + } + else if (is_instance_id(id)) { + return NEW_IVAR(id); + } + else if (is_const_id(id)) { + return NEW_CVAR(id); + } + Bug("invalid id for gettable"); + return 0; +} + +static NODE* +assignable(id, val) + ID id; + NODE *val; +{ + NODE *lhs = 0; + + if (id == SELF) { + yyerror("Can't change the value of self"); + } + else if (id == NIL) { + yyerror("Can't assign to nil"); + } + else if (is_local_id(id)) { + if (local_id(id) || !dyna_in_block()) { + lhs = NEW_LASGN(id, val); + } + else{ + dyna_var_asgn(id, TRUE); + lhs = NEW_DASGN(id, val); + } + } + else if (is_global_id(id)) { + lhs = NEW_GASGN(id, val); + } + else if (is_instance_id(id)) { + lhs = NEW_IASGN(id, val); + } + else if (is_const_id(id)) { + if (cur_mid || in_single) + yyerror("dynamic constant assignment"); + lhs = NEW_CASGN(id, val); + } + else { + Bug("bad id for variable"); + } + return lhs; +} + +static NODE * +aryset(recv, idx, val) + NODE *recv, *idx, *val; +{ + value_expr(recv); + value_expr(val); + return NEW_CALL(recv, ASET, list_append(idx, val)); +} + +ID +id_attrset(id) + ID id; +{ + id &= ~ID_SCOPE_MASK; + id |= ID_ATTRSET; + return id; +} + +static NODE * +attrset(recv, id, val) + NODE *recv, *val; + ID id; +{ + value_expr(recv); + value_expr(val); + + id &= ~ID_SCOPE_MASK; + id |= ID_ATTRSET; + + return NEW_CALL(recv, id, NEW_LIST(val)); +} + +static void +backref_error(node) + NODE *node; +{ + switch (nd_type(node)) { + case NODE_NTH_REF: + Error("Can't set variable $%d", node->nd_nth); + break; + case NODE_BACK_REF: + Error("Can't set variable $%c", node->nd_nth); + break; + } +} + +static int +value_expr(node) + NODE *node; +{ + if (node == 0) return TRUE; + + switch (nd_type(node)) { + case NODE_RETURN: + case NODE_WHILE: + case NODE_UNTIL: + case NODE_CLASS: + case NODE_MODULE: + case NODE_DEFN: + case NODE_DEFS: + yyerror("void value expression"); + return FALSE; + break; + + case NODE_BLOCK: + while (node->nd_next) { + node = node->nd_next; + } + return value_expr(node->nd_head); + + case NODE_IF: + return value_expr(node->nd_body) && value_expr(node->nd_else); + + case NODE_NEWLINE: + return value_expr(node->nd_next); + + default: + return TRUE; + } +} + +static NODE *cond2(); + +static NODE* +cond0(node) + NODE *node; +{ + enum node_type type = nd_type(node); + + switch (type) { + case NODE_DREGX: + case NODE_DREGX_ONCE: + local_cnt('_'); + local_cnt('~'); + return call_op(NEW_GVAR(rb_intern("$_")),MATCH,1,node); + + case NODE_DOT2: + case NODE_DOT3: + node->nd_beg = cond2(node->nd_beg); + node->nd_end = cond2(node->nd_end); + if (type == NODE_DOT2) nd_set_type(node,NODE_FLIP2); + else if (type == NODE_DOT3) nd_set_type(node, NODE_FLIP3); + return node; + + case NODE_LIT: + if (TYPE(node->nd_lit) == T_REGEXP) { + local_cnt('_'); + local_cnt('~'); + return NEW_MATCH(node); + } + default: + return node; + } +} + +static NODE* +cond(node) + NODE *node; +{ + enum node_type type = nd_type(node); + + switch (type) { + case NODE_MASGN: + case NODE_LASGN: + case NODE_DASGN: + case NODE_GASGN: + case NODE_IASGN: + case NODE_CASGN: + Warning("assignment in condition"); + break; + case NODE_NEWLINE: + node->nd_next = cond0(node->nd_next); + return node; + default: + break; + } + + return cond0(node); +} + +static NODE* +cond2(node) + NODE *node; +{ + enum node_type type; + + node = cond(node); + type = nd_type(node); + if (type == NODE_NEWLINE) node = node->nd_next; + if (type == NODE_LIT && FIXNUM_P(node->nd_lit)) { + return call_op(node,EQ,1,NEW_GVAR(rb_intern("$."))); + } + return node; +} + +static NODE* +logop(type, left, right) + enum node_type type; + NODE *left, *right; +{ + value_expr(left); + + return node_newnode(type, cond(left), cond(right)); +} + +st_table *new_idhash(); + +static struct local_vars { + ID *tbl; + int nofree; + int cnt; + int dlev; + struct local_vars *prev; +} *lvtbl; + +static void +local_push() +{ + struct local_vars *local; + + local = ALLOC(struct local_vars); + local->prev = lvtbl; + local->nofree = 0; + local->cnt = 0; + local->tbl = 0; + local->dlev = 0; + lvtbl = local; +} + +static void +local_pop() +{ + struct local_vars *local = lvtbl; + + lvtbl = local->prev; + if (local->tbl) { + local->tbl[0] = local->cnt; + if (!local->nofree) free(local->tbl); + } + free(local); +} + +static ID* +local_tbl() +{ + lvtbl->nofree = 1; + return lvtbl->tbl; +} + +static int +local_cnt(id) + ID id; +{ + int cnt, max; + + if (id == 0) return lvtbl->cnt; + + for (cnt=1, max=lvtbl->cnt+1; cnt<max ;cnt++) { + if (lvtbl->tbl[cnt] == id) return cnt-1; + } + + + if (lvtbl->tbl == 0) { + lvtbl->tbl = ALLOC_N(ID, 2); + lvtbl->tbl[0] = 0; + } + else { + REALLOC_N(lvtbl->tbl, ID, lvtbl->cnt+2); + } + + lvtbl->tbl[lvtbl->cnt+1] = id; + return lvtbl->cnt++; +} + +static int +local_id(id) + ID id; +{ + int i, max; + + if (lvtbl == 0) return FALSE; + for (i=1, max=lvtbl->cnt+1; i<max; i++) { + if (lvtbl->tbl[i] == id) return TRUE; + } + return FALSE; +} + +static void +top_local_init() +{ + local_push(); + lvtbl->cnt = the_scope->local_tbl?the_scope->local_tbl[0]:0; + if (lvtbl->cnt > 0) { + lvtbl->tbl = ALLOC_N(ID, lvtbl->cnt+1); + MEMCPY(lvtbl->tbl, the_scope->local_tbl, ID, lvtbl->cnt+1); + } + else { + lvtbl->tbl = 0; + } + if (the_dyna_vars && the_dyna_vars->id) + lvtbl->dlev = 1; + else + lvtbl->dlev = 0; +} + +static void +top_local_setup() +{ + int len = lvtbl->cnt; + int i; + + if (len > 0) { + i = lvtbl->tbl[0]; + + if (i < len) { + if (i == 0 || the_scope->flag == SCOPE_ALLOCA) { + VALUE *vars = ALLOC_N(VALUE, len+1); + if (the_scope->local_vars) { + *vars++ = the_scope->local_vars[-1]; + MEMCPY(vars, the_scope->local_vars, VALUE, i); + memclear(vars+i, len-i); + } + else { + *vars++ = 0; + memclear(vars, len); + } + the_scope->local_vars = vars; + the_scope->flag |= SCOPE_MALLOC; + } + else { + VALUE *vars = the_scope->local_vars-1; + REALLOC_N(vars, VALUE, len+1); + the_scope->local_vars = vars+1; + memclear(the_scope->local_vars+i, len-i); + } + lvtbl->tbl[0] = len; + if (the_scope->local_tbl && the_scope->local_vars[-1] == 0) { + free(the_scope->local_tbl); + } + the_scope->local_vars[-1] = 0; + the_scope->local_tbl = lvtbl->tbl; + lvtbl->nofree = 1; + } + } + local_pop(); +} + +static struct RVarmap* +dyna_push() +{ + lvtbl->dlev++; + return the_dyna_vars; +} + +static void +dyna_pop(vars) + struct RVarmap* vars; +{ + lvtbl->dlev--; + the_dyna_vars = vars; +} + +static int +dyna_in_block() +{ + return (lvtbl->dlev > 0); +} + +static void +cref_pop() +{ + cur_cref = cur_cref->nd_next; +} + +void +yyappend_print() +{ + eval_tree = + block_append(eval_tree, NEW_FCALL(rb_intern("print"), + NEW_ARRAY(NEW_GVAR(rb_intern("$_"))))); +} + +void +yywhile_loop(chop, split) + int chop, split; +{ + if (split) { + eval_tree = + block_append(NEW_GASGN(rb_intern("$F"), + NEW_CALL(NEW_GVAR(rb_intern("$_")), + rb_intern("split"), 0)), + eval_tree); + } + if (chop) { + eval_tree = + block_append(NEW_CALL(NEW_GVAR(rb_intern("$_")), + rb_intern("chop!"), 0), eval_tree); + } + eval_tree = NEW_OPT_N(eval_tree); +} + +static struct op_tbl rb_op_tbl[] = { + DOT2, "..", + '+', "+", + '-', "-", + '+', "+(binary)", + '-', "-(binary)", + '*', "*", + '/', "/", + '%', "%", + POW, "**", + UPLUS, "+@", + UMINUS, "-@", + UPLUS, "+(unary)", + UMINUS, "-(unary)", + '|', "|", + '^', "^", + '&', "&", + CMP, "<=>", + '>', ">", + GEQ, ">=", + '<', "<", + LEQ, "<=", + EQ, "==", + EQQ, "===", + NEQ, "!=", + MATCH, "=~", + NMATCH, "!~", + '!', "!", + '~', "~", + '!', "!(unary)", + '~', "~(unary)", + '!', "!@", + '~', "~@", + AREF, "[]", + ASET, "[]=", + LSHFT, "<<", + RSHFT, ">>", + COLON2, "::", + '`', "`", + 0, 0, +}; + +char *rb_id2name(); +char *rb_class2name(); + +st_table *rb_symbol_tbl; + +#define sym_tbl rb_symbol_tbl + +void +Init_sym() +{ + int strcmp(); + + sym_tbl = st_init_strtable(); + rb_global_variable(&cur_cref); +} + +ID +rb_intern(name) + char *name; +{ + static ID last_id = LAST_TOKEN; + int id; + int last; + + if (st_lookup(sym_tbl, name, &id)) + return id; + + id = ++last_id; + id <<= ID_SCOPE_SHIFT; + switch (name[0]) { + case '$': + id |= ID_GLOBAL; + break; + case '@': + id |= ID_INSTANCE; + break; + /* fall through */ + default: + if (name[0] != '_' && !isalpha(name[0]) && !ismbchar(name[0])) { + /* operator */ + int i; + + id = 0; + for (i=0; rb_op_tbl[i].token; i++) { + if (strcmp(rb_op_tbl[i].name, name) == 0) { + id = rb_op_tbl[i].token; + break; + } + } + if (id == 0) NameError("Unknown operator `%s'", name); + break; + } + + last = strlen(name)-1; + if (name[last] == '=') { + /* attribute assignment */ + char *buf = ALLOCA_N(char,last+1); + + strncpy(buf, name, last); + buf[last] = '\0'; + id = rb_intern(buf); + id &= ~ID_SCOPE_MASK; + id |= ID_ATTRSET; + } + else if (isupper(name[0])) { + id |= ID_CONST; + } + else { + id |= ID_LOCAL; + } + break; + } + st_add_direct(sym_tbl, strdup(name), id); + return id; +} + +struct find_ok { + ID id; + char *name; +}; + +static int +id_find(name, id1, ok) + char *name; + ID id1; + struct find_ok *ok; +{ + if (id1 == ok->id) { + ok->name = name; + return ST_STOP; + } + return ST_CONTINUE; +} + +char * +rb_id2name(id) + ID id; +{ + struct find_ok ok; + + if (id < LAST_TOKEN) { + int i = 0; + + for (i=0; rb_op_tbl[i].token; i++) { + if (rb_op_tbl[i].token == id) + return rb_op_tbl[i].name; + } + } + + ok.name = 0; + ok.id = id; + st_foreach(sym_tbl, id_find, &ok); + if (!ok.name && is_attrset_id(id)) { + char *res; + ID id2; + + id2 = (id & ~ID_SCOPE_MASK) | ID_LOCAL; + res = rb_id2name(id2); + + if (res) { + char *buf = ALLOCA_N(char,strlen(res)+2); + + strcpy(buf, res); + strcat(buf, "="); + rb_intern(buf); + return rb_id2name(id); + } + } + return ok.name; +} + +int +rb_is_const_id(id) + ID id; +{ + if (is_const_id(id)) return TRUE; + return FALSE; +} + +int +rb_is_instance_id(id) + ID id; +{ + if (is_instance_id(id)) return TRUE; + return FALSE; +} + +void +local_var_append(id) + ID id; +{ + struct local_vars tmp; + struct local_vars *save = lvtbl; + + if (the_scope->local_tbl) { + tmp.cnt = the_scope->local_tbl[0]; + tmp.tbl = the_scope->local_tbl; + lvtbl->dlev = 0; + } + lvtbl = &tmp; + local_cnt(id); + lvtbl = save; +} + +static VALUE +special_local_get(c) + char c; +{ + int cnt, max; + + if (!the_scope->local_vars) return Qnil; + for (cnt=1, max=the_scope->local_tbl[0]+1; cnt<max ;cnt++) { + if (the_scope->local_tbl[cnt] == c) { + return the_scope->local_vars[cnt-1]; + } + } + return Qnil; +} + +static void +special_local_set(c, val) + char c; + VALUE val; +{ + int cnt, max; + + if (the_scope->local_tbl) { + for (cnt=1, max=the_scope->local_tbl[0]+1; cnt<max ;cnt++) { + if (the_scope->local_tbl[cnt] == c) { + the_scope->local_vars[cnt-1] = val; + return; + } + } + } + top_local_init(); + cnt = local_cnt(c); + top_local_setup(); + the_scope->local_vars[cnt] = val; +} + +VALUE +backref_get() +{ + return special_local_get('~'); +} + +void +backref_set(val) + VALUE val; +{ + special_local_set('~', val); +} + +VALUE +lastline_get() +{ + VALUE v = special_local_get('_'); + if (v == 1) return Qnil; /* $_ undefined */ + return v; +} + +void +lastline_set(val) + VALUE val; +{ + special_local_set('_', val); +} diff --git a/process.c b/process.c new file mode 100644 index 0000000000..ac7a7465e8 --- /dev/null +++ b/process.c @@ -0,0 +1,998 @@ +/************************************************ + + process.c - + + $Author$ + $Date$ + created at: Tue Aug 10 14:30:50 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include "sig.h" +#include <stdio.h> +#include <errno.h> +#include <ctype.h> +#include <signal.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifndef NT +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#else +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; +#endif +#endif /* NT */ + +struct timeval time_timeval(); + +#ifdef HAVE_SYS_WAIT_H +# include <sys/wait.h> +#endif +#ifdef HAVE_GETPRIORITY +# include <sys/resource.h> +#endif +#ifdef HAVE_VFORK_H +#include <vfork.h> +#endif +#include "st.h" + +static VALUE +get_pid() +{ + return INT2FIX(getpid()); +} + +static VALUE +get_ppid() +{ +#ifdef NT + return INT2FIX(0); +#else + return INT2FIX(getppid()); +#endif +} + +VALUE last_status = Qnil; + +#if !defined(HAVE_WAITPID) && !defined(HAVE_WAIT4) +static st_table *pid_tbl; +#else +# define WAIT_CALL +#endif + +static int +rb_waitpid(pid, flags, st) + int pid; + int flags; + int *st; +{ + int result; +#if defined(THREAD) && (defined(HAVE_WAITPID) || defined(HAVE_WAIT4)) + int oflags = flags; + if (!thread_alone()) { /* there're other threads to run */ + flags |= WNOHANG; + } +#endif + +#ifdef HAVE_WAITPID + retry: + result = waitpid(pid, st, flags); + if (result < 0) { + if (errno == EINTR) { +#ifdef THREAD + thread_schedule(); +#endif + goto retry; + } + return -1; + } +#ifdef THREAD + if (result == 0) { + if (oflags & WNOHANG) return 0; + thread_schedule(); + if (thread_alone()) flags = oflags; + goto retry; + } +#endif +#else +#ifdef HAVE_WAIT4 + retry: + + result = wait4(pid, st, flags, NULL); + if (result < 0) { + if (errno == EINTR) { +#ifdef THREAD + thread_schedule(); +#endif + goto retry; + } + return -1; + } +#ifdef THREAD + if (result == 0) { + if (oflags & WNOHANG) return 0; + thread_schedule(); + if (thread_alone()) flags = oflags; + goto retry; + } +#endif +#else + if (pid_tbl && st_lookup(pid_tbl, pid, st)) { + last_status = INT2FIX(*st); + st_delete(pid_tbl, &pid, NULL); + return pid; + } + + if (flags) { + ArgError("Can't do waitpid with flags"); + } + + for (;;) { + result = wait(st); + if (result < 0) { + if (errno == EINTR) { +#ifdef THREAD + thread_schedule(); +#endif + continue; + } + return -1; + } + if (result == pid) { + break; + } + if (!pid_tbl) + pid_tbl = st_init_numtable(); + st_insert(pid_tbl, pid, st); + } +#endif +#endif + last_status = INT2FIX(*st); + return result; +} + +#ifndef WAIT_CALL +struct wait_data { + int pid; + int status; +}; + +static int +wait_each(key, value, data) + int key, value; + struct wait_data *data; +{ + if (data->status != -1) return ST_STOP; + + data->pid = key; + data->status = value; + return ST_DELETE; +} +#endif + +static VALUE +f_wait() +{ + int pid, state; +#ifndef WAIT_CALL + struct wait_data data; + + data.status = -1; + st_foreach(pid_tbl, wait_each, &data); + if (data.status != -1) { + last_status = data.status; + return INT2FIX(data.pid); + } +#endif + + while ((pid = wait(&state)) < 0) { + if (errno == EINTR) { +#ifdef THREAD + thread_schedule(); +#endif + continue; + } + if (errno == ECHILD) return Qnil; + rb_sys_fail(0); + } + last_status = INT2FIX(state); + return INT2FIX(pid); +} + +static VALUE +f_waitpid(obj, vpid, vflags) + VALUE obj, vpid, vflags; +{ + int pid, flags, status; + + if (NIL_P(vflags)) flags = 0; + else flags = FIX2UINT(vflags); + + if ((pid = rb_waitpid(FIX2UINT(vpid), flags, &status)) < 0) + rb_sys_fail(0); + return INT2FIX(pid); +} + +char *strtok(); + +#if defined(THREAD) && defined(HAVE_SETITIMER) +static void +before_exec() +{ + struct itimerval tval; + + tval.it_interval.tv_sec = 0; + tval.it_interval.tv_usec = 0; + tval.it_value = tval.it_interval; + setitimer(ITIMER_VIRTUAL, &tval, NULL); +} + +static void +after_exec() +{ + struct itimerval tval; + + tval.it_interval.tv_sec = 0; + tval.it_interval.tv_usec = 100000; + tval.it_value = tval.it_interval; + setitimer(ITIMER_VIRTUAL, &tval, NULL); +} +#else +#define before_exec() +#define after_exec() +#endif + +extern char *dln_find_exe(); + +static void +security(str) + char *str; +{ + extern int env_path_tainted; + extern VALUE eSecurityError; + + if (rb_safe_level() > 0 && env_path_tainted) { + Raise(eSecurityError, "Insecure PATH - %s", str); + } +} + +static int +proc_exec_v(argv) + char **argv; +{ + char *prog; + + security(argv[0]); + prog = dln_find_exe(argv[0], 0); + if (!prog) { + errno = ENOENT; + return -1; + } +#if (defined(MSDOS) && !defined(DJGPP)) || defined(__human68k__) + { +#if defined(__human68k__) +#define COMMAND "command.x" +#else +#define COMMAND "command.com" +#endif + char *extension; + + if ((extension = strrchr(prog, '.')) != NULL && strcasecmp(extension, ".bat") == 0) { + char **new_argv; + char *p; + int n; + + for (n = 0; argv[n]; n++) + /* no-op */; + new_argv = ALLOCA_N(char *, n + 2); + for (; n > 0; n--) + new_argv[n + 1] = argv[n]; + new_argv[1] = strcpy(ALLOCA_N(char, strlen(argv[0]) + 1), argv[0]); + for (p = new_argv[1]; *p != '\0'; p++) + if (*p == '/') + *p = '\\'; + new_argv[0] = COMMAND; + argv = new_argv; + prog = dln_find_exe(argv[0], 0); + if (!prog) { + errno = ENOENT; + return -1; + } + } + } +#endif /* MSDOS or __human68k__ */ + before_exec(); + execv(prog, argv); + after_exec(); + return -1; +} + +static int +proc_exec_n(argc, argv) + int argc; + VALUE *argv; +{ + char **args; + int i; + + args = ALLOCA_N(char*, argc+1); + for (i=0; i<argc; i++) { + Check_SafeStr(argv[i]); + args[i] = RSTRING(argv[i])->ptr; + } + args[i] = 0; + if (args[0]) { + return proc_exec_v(args); + } + return -1; +} + +int +rb_proc_exec(str) + char *str; +{ + char *s = str, *t; + char **argv, **a; + + security(str); + for (s=str; *s; s++) { + if (*s != ' ' && !isalpha(*s) && strchr("*?{}[]<>()~&|\\$;'`\"\n",*s)) { +#if defined(MSDOS) + int state; + before_exec(); + state = system(str); + after_exec(); + if (state != -1) + exit(state); +#else +#if defined(__human68k__) + char *shell = dln_find_exe("sh", 0); + int state = -1; + before_exec(); + if (shell) + execl(shell, "sh", "-c", str, (char *) NULL); + else + state = system(str); + after_exec(); + if (state != -1) + exit(state); +#else + before_exec(); + execl("/bin/sh", "sh", "-c", str, (char *)NULL); + after_exec(); +#endif +#endif + return -1; + } + } + a = argv = ALLOCA_N(char*, (s-str)/2+2); + s = ALLOCA_N(char, s-str+1); + strcpy(s, str); + if (*a++ = strtok(s, " \t")) { + while (t = strtok(NULL, " \t")) { + *a++ = t; + } + *a = NULL; + } + if (argv[0]) { + return proc_exec_v(argv); + } + errno = ENOENT; + return -1; +} + +#if defined(__human68k__) +static int +proc_spawn_v(argv) + char **argv; +{ + char *prog; + char *extension; + int state; + + prog = dln_find_exe(argv[0], 0); + if (!prog) + return -1; + + if ((extension = strrchr(prog, '.')) != NULL && strcasecmp(extension, ".bat") == 0) { + char **new_argv; + char *p; + int n; + + for (n = 0; argv[n]; n++) + /* no-op */; + new_argv = ALLOCA_N(char *, n + 2); + for (; n > 0; n--) + new_argv[n + 1] = argv[n]; + new_argv[1] = strcpy(ALLOCA_N(char, strlen(argv[0]) + 1), argv[0]); + for (p = new_argv[1]; *p != '\0'; p++) + if (*p == '/') + *p = '\\'; + new_argv[0] = COMMAND; + argv = new_argv; + prog = dln_find_exe(argv[0], 0); + if (!prog) { + errno = ENOENT; + return -1; + } + } + before_exec(); + state = spawnv(P_WAIT, prog, argv); + after_exec(); + return state; +} + +static int +proc_spawn_n(argc, argv) + int argc; + VALUE *argv; +{ + char **args; + int i; + + args = ALLOCA_N(char *, argc + 1); + for (i = 0; i < argc; i++) { + Check_SafeStr(argv[i]); + args[i] = RSTRING(argv[i])->ptr; + } + args[i] = (char *) 0; + if (args[0]) + return proc_exec_v(args); + return -1; +} + +static int +proc_spawn(str) + char *str; +{ + char *s = str, *t; + char **argv, **a; + int state; + + for (s = str; *s; s++) { + if (*s != ' ' && !isalpha(*s) && strchr("*?{}[]<>()~&|\\$;'`\"\n",*s)) { + char *shell = dln_find_exe("sh", 0); + before_exec(); + state = shell ? spawnl(P_WAIT, shell, "sh", "-c", str, (char *) NULL) : system(str) ; + after_exec(); + return state; + } + } + a = argv = ALLOCA_N(char *, (s - str) / 2 + 2); + s = ALLOCA_N(char, s - str + 1); + strcpy(s, str); + if (*a++ = strtok(s, " \t")) { + while (t = strtok(NULL, " \t")) + *a++ = t; + *a = NULL; + } + return argv[0] ? proc_spawn_v(argv) : -1 ; +} +#endif /* __human68k__ */ + +static VALUE +f_exec(argc, argv) + int argc; + VALUE *argv; +{ + if (argc == 1) { + Check_SafeStr(argv[0]); + rb_proc_exec(RSTRING(argv[0])->ptr); + } + else { + proc_exec_n(argc, argv); + } + rb_sys_fail(RSTRING(argv[0])->ptr); +} + +static VALUE +f_fork(obj) + VALUE obj; +{ +#if !defined(__human68k__) + int pid; + + rb_secure(2); + switch (pid = fork()) { + case 0: +#ifdef linux + after_exec(); +#endif + if (iterator_p()) { + rb_yield(Qnil); + _exit(0); + } + return Qnil; + + case -1: + rb_sys_fail("fork(2)"); + return Qnil; + + default: + return INT2FIX(pid); + } +#else + rb_notimplement(); +#endif +} + +static VALUE +f_exit_bang(obj, status) + VALUE obj, status; +{ + int code = -1; + + rb_secure(2); + if (FIXNUM_P(status)) { + code = INT2FIX(status); + } + + _exit(code); + + /* not reached */ +} + +void +rb_syswait(pid) + int pid; +{ + RETSIGTYPE (*hfunc)(), (*qfunc)(), (*ifunc)(); + int status; + +#ifdef SIGHUP + hfunc = signal(SIGHUP, SIG_IGN); +#endif +#ifdef SIGQUIT + qfunc = signal(SIGQUIT, SIG_IGN); +#endif + ifunc = signal(SIGINT, SIG_IGN); + + if (rb_waitpid(pid, 0, &status) < 0) rb_sys_fail("wait"); + +#ifdef SIGHUP + signal(SIGHUP, hfunc); +#endif +#ifdef SIGQUIT + signal(SIGQUIT, qfunc); +#endif + signal(SIGINT, ifunc); +} + +static VALUE +f_system(argc, argv) + int argc; + VALUE *argv; +{ +#ifdef NT + VALUE cmd; + int state; + + cmd = ary_join(ary_new4(argc, argv), str_new2(" ")); + + Check_SafeStr(cmd); + state = do_spawn(RSTRING(cmd)->ptr); + last_status = INT2FIX(state); + + if (state == 0) return TRUE; + return FALSE; +#else +#if defined(DJGPP) + VALUE cmd; + int state; + + cmd = ary_join(ary_new4(argc, argv), str_new2(" ")); + + Check_SafeStr(cmd); + state = system(RSTRING(cmd)->ptr); + last_status = INT2FIX(state); + + if (state == 0) return TRUE; + return FALSE; +#else +#if defined(__human68k__) + int i; + int state; + + fflush(stdin); + fflush(stdout); + fflush(stderr); + if (argc == 0) { + last_status = INT2FIX(0); + return INT2FIX(0); + } + + for (i = 0; i < argc; i++) + Check_SafeStr(argv[i]); + + state = argc == 1 ? proc_spawn(RSTRING(argv[0])->ptr) : proc_spawn_n(argc, argv) ; + last_status = state == -1 ? INT2FIX(127) : INT2FIX(state); + + return state == 0 ? TRUE : FALSE ; +#else + int i; + int pid; + + fflush(stdin); /* is it really needed? */ + fflush(stdout); + fflush(stderr); + if (argc == 0) { + last_status = INT2FIX(0); + return INT2FIX(0); + } + + for (i=0; i<argc; i++) { + Check_SafeStr(argv[i]); + } + + retry: + switch (pid = vfork()) { + case 0: + if (argc == 1) { + rb_proc_exec(RSTRING(argv[0])->ptr); + } + else { + proc_exec_n(argc, argv); + } + _exit(127); + break; /* not reached */ + + case -1: + if (errno == EAGAIN) { +#ifdef THREAD + thread_sleep(1); +#else + sleep(1); +#endif + goto retry; + } + rb_sys_fail(0); + break; + + default: + rb_syswait(pid); + } + + if (last_status == INT2FIX(0)) return TRUE; + return FALSE; +#endif +#endif +#endif +} + +static VALUE +f_sleep(argc, argv) + int argc; + VALUE *argv; +{ + int beg, end; + + beg = time(0); +#ifdef THREAD + if (argc == 0) { + thread_sleep_forever(); + } + else if (argc == 1) { + thread_wait_for(time_timeval(argv[0])); + } +#else + if (argc == 0) { + TRAP_BEG; + sleep((32767<<16)+32767); + TRAP_END; + } + else if (argc == 1) { + struct timeval tv; + int n; + + tv = time_timeval(argv[0]); + TRAP_BEG; + n = select(0, 0, 0, 0, &tv); + TRAP_END; + if (n<0) rb_sys_fail(0); + } +#endif + else { + ArgError("wrong # of arguments"); + } + + end = time(0) - beg; + + return INT2FIX(end); +} + +#if !defined(NT) && !defined(DJGPP) && !defined(__human68k__) +static VALUE +proc_getpgrp(argc, argv) + int argc; + VALUE *argv; +{ + int pgrp; +#ifdef BSD_GETPGRP + VALUE vpid; + int pid; + + rb_scan_args(argc, argv, "01", &vpid); + pid = NUM2INT(vpid); + pgrp = BSD_GETPGRP(pid); +#else + rb_scan_args(argc, argv, "0"); + pgrp = getpgrp(); +#endif + if (pgrp < 0) rb_sys_fail(0); + return INT2FIX(pgrp); +} + +static VALUE +proc_setpgrp(argc, argv) + int argc; + VALUE *argv; +{ +#ifdef BSD_SETPGRP + VALUE pid, pgrp; + int ipid, ipgrp; + + rb_scan_args(argc, argv, "02", &pid, &pgrp); + + ipid = NUM2INT(pid); + ipgrp = NUM2INT(pgrp); + if (BSD_SETPGRP(ipid, ipgrp) < 0) rb_sys_fail(0); +#else + rb_scan_args(argc, argv, "0"); + if (setpgrp() < 0) rb_sys_fail(0); +#endif + return Qnil; +} + +#ifdef HAVE_SETPGID +static VALUE +proc_setpgid(obj, pid, pgrp) + VALUE obj, pid, pgrp; +{ + int ipid, ipgrp; + + ipid = NUM2INT(pid); + ipgrp = NUM2INT(pgrp); + + if (setpgid(ipid, ipgrp) < 0) rb_sys_fail(0); + return Qnil; +} +#endif + +static VALUE +proc_getpriority(obj, which, who) + VALUE obj, which, who; +{ +#ifdef HAVE_GETPRIORITY + int prio, iwhich, iwho; + + iwhich = NUM2INT(which); + iwho = NUM2INT(who); + + prio = getpriority(iwhich, iwho); + if (prio < 0) rb_sys_fail(0); + return INT2FIX(prio); +#else + rb_notimplement(); +#endif +} + +static VALUE +proc_setpriority(obj, which, who, prio) + VALUE obj, which, who, prio; +{ +#ifdef HAVE_GETPRIORITY + int iwhich, iwho, iprio; + + iwhich = NUM2INT(which); + iwho = NUM2INT(who); + iprio = NUM2INT(prio); + + if (setpriority(iwhich, iwho, iprio) < 0) + rb_sys_fail(0); + return INT2FIX(0); +#else + rb_notimplement(); +#endif +} +#endif + +static VALUE +proc_getuid(obj) + VALUE obj; +{ + int uid = getuid(); + return INT2FIX(uid); +} + +static VALUE +proc_setuid(obj, id) + VALUE obj, id; +{ + int uid; + + uid = NUM2INT(id); +#ifdef HAVE_SETRUID + setruid(uid); +#else +#ifdef HAVE_SETREUID + setreuid(uid, -1); +#else + { + if (geteuid() == uid) + setuid(uid); + else + rb_notimplement(); + } +#endif +#endif + return INT2FIX(uid); +} + +static VALUE +proc_getgid(obj) + VALUE obj; +{ + int gid = getgid(); + return INT2FIX(gid); +} + +static VALUE +proc_setgid(obj, id) + VALUE obj, id; +{ + int gid; + + gid = NUM2INT(id); +#ifdef HAS_SETRGID + setrgid((GIDTYPE)gid); +#else +#ifdef HAVE_SETREGID + setregid(gid, -1); +#else + { + if (getegid() == gid) + setgid(gid); + else + rb_notimplement(); + } +#endif +#endif + return INT2FIX(gid); +} + +static VALUE +proc_geteuid(obj) + VALUE obj; +{ + int euid = geteuid(); + return INT2FIX(euid); +} + +static VALUE +proc_seteuid(obj, euid) + VALUE obj, euid; +{ +#ifdef HAVE_SETEUID + if (seteuid(NUM2INT(euid)) < 0) rb_sys_fail(0); +#else +#ifdef HAVE_SETREUID + if (setreuid(-1, NUM2INT(euid)) < 0) rb_sys_fail(0); +#else + euid = NUM2INT(euid); + if (euid == getuid()) + setuid(euid); + else + rb_notimplement(); +#endif +#endif + return euid; +} + +static VALUE +proc_getegid(obj) + VALUE obj; +{ + int egid = getegid(); + return INT2FIX(egid); +} + +static VALUE +proc_setegid(obj, egid) + VALUE obj, egid; +{ +#ifdef HAVE_SETEGID + if (setegid(NUM2INT(egid)) < 0) rb_sys_fail(0); +#else +#ifdef HAVE_SETREGID + if (setregid(-1, NUM2INT(egid)) < 0) rb_sys_fail(0); +#else + egid = NUM2INT(egid); + if (egid == getgid()) + setgid(egid); + else + rb_notimplement(); +#endif +#endif + return egid; +} + +VALUE mProcess; + +extern VALUE f_kill(); + +void +Init_process() +{ + extern VALUE mKernel; + + rb_define_virtual_variable("$$", get_pid, 0); + rb_define_readonly_variable("$?", &last_status); + rb_define_global_function("exec", f_exec, -1); +#ifndef NT + rb_define_global_function("fork", f_fork, 0); +#endif + rb_define_global_function("exit!", f_exit_bang, 1); + rb_define_global_function("system", f_system, -1); + rb_define_global_function("sleep", f_sleep, -1); + + mProcess = rb_define_module("Process"); + +#if !defined(NT) && !defined(DJGPP) +#ifdef WNOHANG + rb_define_const(mProcess, "WNOHANG", INT2FIX(WNOHANG)); +#else + rb_define_const(mProcess, "WNOHANG", INT2FIX(0)); +#endif +#ifdef WUNTRACED + rb_define_const(mProcess, "WUNTRACED", INT2FIX(WUNTRACED)); +#else + rb_define_const(mProcess, "WUNTRACED", INT2FIX(0)); +#endif +#endif + +#ifndef NT + rb_define_singleton_method(mProcess, "fork", f_fork, 0); +#endif + rb_define_singleton_method(mProcess, "exit!", f_exit_bang, 1); + rb_define_module_function(mProcess, "kill", f_kill, -1); +#ifndef NT + rb_define_module_function(mProcess, "wait", f_wait, 0); + rb_define_module_function(mProcess, "waitpid", f_waitpid, 2); + + rb_define_module_function(mProcess, "pid", get_pid, 0); + rb_define_module_function(mProcess, "ppid", get_ppid, 0); +#endif + +#if !defined(NT) && !defined(DJGPP) && !defined(__human68k__) + rb_define_module_function(mProcess, "getpgrp", proc_getpgrp, -1); + rb_define_module_function(mProcess, "setpgrp", proc_setpgrp, -1); +#ifdef HAVE_SETPGID + rb_define_module_function(mProcess, "setpgid", proc_setpgid, 2); +#endif + +#ifdef HAVE_GETPRIORITY + rb_define_module_function(mProcess, "getpriority", proc_getpriority, 2); + rb_define_module_function(mProcess, "setpriority", proc_setpriority, 3); + + rb_define_const(mProcess, "PRIO_PROCESS", INT2FIX(PRIO_PROCESS)); + rb_define_const(mProcess, "PRIO_PGRP", INT2FIX(PRIO_PGRP)); + rb_define_const(mProcess, "PRIO_USER", INT2FIX(PRIO_USER)); +#endif + + rb_define_module_function(mProcess, "uid", proc_getuid, 0); + rb_define_module_function(mProcess, "uid=", proc_setuid, 1); + rb_define_module_function(mProcess, "gid", proc_getgid, 0); + rb_define_module_function(mProcess, "gid=", proc_setgid, 1); + rb_define_module_function(mProcess, "euid", proc_geteuid, 0); + rb_define_module_function(mProcess, "euid=", proc_seteuid, 1); + rb_define_module_function(mProcess, "egid", proc_getegid, 0); + rb_define_module_function(mProcess, "egid=", proc_setegid, 1); +#endif +} diff --git a/random.c b/random.c new file mode 100644 index 0000000000..3fc9a0003f --- /dev/null +++ b/random.c @@ -0,0 +1,100 @@ +/************************************************ + + random.c - + + $Author$ + $Date$ + created at: Fri Dec 24 16:39:21 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" + +static int first = 1; +static char state[256]; + +static VALUE +f_srand(argc, argv, obj) + int argc; + VALUE *argv; + VALUE obj; +{ + int seed, old; + static int saved_seed; + + if (rb_scan_args(argc, argv, "01", &seed) == 0) { + seed = time(0); + } + else { + seed = NUM2INT(seed); + } + +#ifdef HAVE_RANDOM + if (first == 1) { + initstate(1, state, sizeof state); + first = 0; + } + else { + setstate(state); + } + + srandom(seed); + old = saved_seed; + saved_seed = seed; + + return int2inum(old); +#else + srand(seed); + old = saved_seed; + saved_seed = seed; + + return int2inum(old); +#endif +} + +static VALUE +f_rand(obj, vmax) + VALUE obj, vmax; +{ + int val, max; + +#ifdef HAVE_RANDOM + if (first == 1) { + initstate(1, state, sizeof state); + first = 0; + } +#endif + + switch (TYPE(vmax)) { + case T_BIGNUM: + return big_rand(vmax); + + case T_FLOAT: + if (RFLOAT(vmax)->value > LONG_MAX || RFLOAT(vmax)->value < LONG_MIN) + return big_rand(dbl2big(RFLOAT(vmax)->value)); + break; + } + + max = NUM2INT(vmax); + if (max == 0) ArgError("rand(0)"); + +#ifdef HAVE_RANDOM + val = random() % max; +#else + val = rand() % max; +#endif + + if (val < 0) val = -val; + return int2inum(val); +} + +void +Init_Random() +{ + extern VALUE mKernel; + + rb_define_global_function("srand", f_srand, -1); + rb_define_global_function("rand", f_rand, 1); +} diff --git a/range.c b/range.c new file mode 100644 index 0000000000..b7406a1e7a --- /dev/null +++ b/range.c @@ -0,0 +1,211 @@ +/************************************************ + + range.c - + + $Author$ + $Date$ + created at: Thu Aug 19 17:46:47 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" + +VALUE mComparable; +static VALUE cRange; +extern VALUE cNumeric; + +static ID upto; + +static VALUE +range_s_new(class, first, last) + VALUE class, first, last; +{ + VALUE obj; + + if (!(FIXNUM_P(first) && FIXNUM_P(last)) + && (TYPE(first) != TYPE(last) + || CLASS_OF(first) != CLASS_OF(last) + || !rb_respond_to(first, upto)) + && !(obj_is_kind_of(first, cNumeric) + && obj_is_kind_of(last, cNumeric))) { + ArgError("bad value for range"); + } + + obj = obj_alloc(class); + + rb_iv_set(obj, "first", first); + rb_iv_set(obj, "last", last); + + return obj; +} + +VALUE +range_new(first, last) + VALUE first, last; +{ + return range_s_new(cRange, first, last); +} + +static VALUE +range_eqq(rng, obj) + VALUE rng, obj; +{ + VALUE first, last; + + first = rb_iv_get(rng, "first"); + last = rb_iv_get(rng, "last"); + + if (FIXNUM_P(first) && FIXNUM_P(obj) && FIXNUM_P(last)) { + if (FIX2INT(first) <= FIX2INT(obj) && FIX2INT(obj) <= FIX2INT(last)) { + return TRUE; + } + return FALSE; + } + else { + if (RTEST(rb_funcall(first, rb_intern("<="), 1, obj)) && + RTEST(rb_funcall(last, rb_intern(">="), 1, obj))) { + return TRUE; + } + return FALSE; + } +} + +struct upto_data { + VALUE first; + VALUE last; +}; + +static VALUE +range_upto(data) + struct upto_data *data; +{ + return rb_funcall(data->first, upto, 1, data->last); +} + +static VALUE +range_each(obj) + VALUE obj; +{ + VALUE b, e; + + b = rb_iv_get(obj, "first"); + e = rb_iv_get(obj, "last"); + + if (FIXNUM_P(b)) { /* fixnum is a special case(for performance) */ + num_upto(b, e); + } + else { + struct upto_data data; + + data.first = b; + data.last = e; + + rb_iterate(range_upto, &data, rb_yield, 0); + } + + return Qnil; +} + +static VALUE +range_first(obj) + VALUE obj; +{ + VALUE b; + + b = rb_iv_get(obj, "first"); + return b; +} + +static VALUE +range_last(obj) + VALUE obj; +{ + VALUE e; + + e = rb_iv_get(obj, "last"); + return e; +} + +VALUE +range_beg_end(range, begp, endp) + VALUE range; + int *begp, *endp; +{ + VALUE first, last; + + if (!obj_is_kind_of(range, cRange)) return FALSE; + + first = rb_iv_get(range, "first"); *begp = NUM2INT(first); + last = rb_iv_get(range, "last"); *endp = NUM2INT(last); + return TRUE; +} + +static VALUE +range_to_s(range) + VALUE range; +{ + VALUE str, str2; + + str = obj_as_string(rb_iv_get(range, "first")); + str2 = obj_as_string(rb_iv_get(range, "last")); + str_cat(str, "..", 2); + str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len); + + return str; +} + +static VALUE +range_inspect(range) + VALUE range; +{ + VALUE str, str2; + + str = rb_inspect(rb_iv_get(range, "first")); + str2 = rb_inspect(rb_iv_get(range, "last")); + str_cat(str, "..", 2); + str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len); + + return str; +} + +static VALUE +range_length(rng) + VALUE rng; +{ + VALUE first, last; + VALUE size; + + first = rb_iv_get(rng, "first"); + last = rb_iv_get(rng, "last"); + + if (!obj_is_kind_of(first, cNumeric)) { + return enum_length(rng); + } + size = rb_funcall(last, '-', 1, first); + size = rb_funcall(size, '+', 1, INT2FIX(1)); + + return size; +} + +extern VALUE mEnumerable; + +void +Init_Range() +{ + cRange = rb_define_class("Range", cObject); + rb_include_module(cRange, mEnumerable); + rb_define_singleton_method(cRange, "new", range_s_new, 2); + rb_define_method(cRange, "===", range_eqq, 1); + rb_define_method(cRange, "each", range_each, 0); + rb_define_method(cRange, "first", range_first, 0); + rb_define_method(cRange, "last", range_last, 0); + rb_define_method(cRange, "to_s", range_to_s, 0); + rb_define_method(cRange, "inspect", range_inspect, 0); + + rb_define_method(cRange, "length", range_length, 0); + rb_define_method(cRange, "size", range_length, 0); + + upto = rb_intern("upto"); +} @@ -0,0 +1,886 @@ +/************************************************ + + re.c - + + $Author$ + $Date$ + created at: Mon Aug 9 18:24:49 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include "re.h" + +static VALUE eRegxpError; + +#define BEG(no) regs->beg[no] +#define END(no) regs->end[no] + +#if 'a' == 97 /* it's ascii */ +static char casetable[] = { + '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007', + '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017', + '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027', + '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037', + /* ' ' '!' '"' '#' '$' '%' '&' ''' */ + '\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047', + /* '(' ')' '*' '+' ',' '-' '.' '/' */ + '\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057', + /* '0' '1' '2' '3' '4' '5' '6' '7' */ + '\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067', + /* '8' '9' ':' ';' '<' '=' '>' '?' */ + '\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077', + /* '@' 'A' 'B' 'C' 'D' 'E' 'F' 'G' */ + '\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147', + /* 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O' */ + '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157', + /* 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W' */ + '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167', + /* 'X' 'Y' 'Z' '[' '\' ']' '^' '_' */ + '\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137', + /* '`' 'a' 'b' 'c' 'd' 'e' 'f' 'g' */ + '\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147', + /* 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' */ + '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157', + /* 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' */ + '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167', + /* 'x' 'y' 'z' '{' '|' '}' '~' */ + '\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177', + '\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207', + '\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217', + '\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227', + '\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237', + '\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247', + '\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257', + '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267', + '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277', + '\300', '\301', '\302', '\303', '\304', '\305', '\306', '\307', + '\310', '\311', '\312', '\313', '\314', '\315', '\316', '\317', + '\320', '\321', '\322', '\323', '\324', '\325', '\326', '\327', + '\330', '\331', '\332', '\333', '\334', '\335', '\336', '\337', + '\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347', + '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357', + '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367', + '\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377', +}; +#else +>>> "You lose. You will need a translation table for your character set." <<< +#endif + +#define min(a,b) (((a)>(b))?(b):(a)) + +int +str_cicmp(str1, str2) + struct RString *str1, *str2; +{ + int len, i; + char *p1, *p2; + + len = min(str1->len, str2->len); + p1 = str1->ptr; p2 = str2->ptr; + + for (i = 0; i < len; i++, p1++, p2++) { + if (casetable[(unsigned)*p1] != casetable[(unsigned)*p2]) + return casetable[(unsigned)*p1] - casetable[(unsigned)*p2]; + } + return str1->len - str2->len; +} + +#define REG_IGNORECASE FL_USER0 + +#define KCODE_NONE 0 +#define KCODE_EUC FL_USER2 +#define KCODE_SJIS FL_USER3 +#define KCODE_FIXED FL_USER4 +#define KCODE_MASK (KCODE_EUC|KCODE_SJIS) + +static int reg_kcode = +#ifdef EUC + KCODE_EUC; +#else +# ifdef SJIS + KCODE_SJIS; +# else + KCODE_NONE; +# endif +#endif + +static void +kcode_euc(reg) + struct RRegexp *reg; +{ + FL_UNSET(reg, KCODE_MASK); + FL_SET(reg, KCODE_EUC); + FL_SET(reg, KCODE_FIXED); +} + +static void +kcode_sjis(reg) + struct RRegexp *reg; +{ + FL_UNSET(reg, KCODE_MASK); + FL_SET(reg, KCODE_SJIS); + FL_SET(reg, KCODE_FIXED); +} + +static void +kcode_none(reg) + struct RRegexp *reg; +{ + FL_UNSET(reg, KCODE_MASK); + FL_SET(reg, KCODE_FIXED); +} + +static void +kcode_set_option(reg) + struct RRegexp *reg; +{ + if (!FL_TEST(reg, KCODE_FIXED)) return; + + re_syntax_options &= ~RE_MBCTYPE_MASK; + switch ((RBASIC(reg)->flags & KCODE_MASK)) { + case KCODE_NONE: + break; + case KCODE_EUC: + re_syntax_options |= RE_MBCTYPE_EUC; + break; + case KCODE_SJIS: + re_syntax_options |= RE_MBCTYPE_SJIS; + break; + } + re_set_syntax(re_syntax_options); +} + +static void +kcode_reset_option() +{ + re_syntax_options &= ~RE_MBCTYPE_MASK; + switch (reg_kcode) { + case KCODE_NONE: + break; + case KCODE_EUC: + re_syntax_options |= RE_MBCTYPE_EUC; + break; + case KCODE_SJIS: + re_syntax_options |= RE_MBCTYPE_SJIS; + break; + } + re_set_syntax(re_syntax_options); +} + +extern int rb_in_eval; + +static VALUE +reg_desc(s, len, re) + char *s; + int len; + VALUE re; +{ + VALUE str = str_new2("/"); + char *p, *pend; + int slash = 0; + + p = s; pend = p + len; + while (p<pend) { + if (*p == '/') { + slash = 1; + break; + } + p++; + } + if (!slash) { + str_cat(str, s, len); + } + else { + p = s; + while (p<pend) { + if (*p == '/') { + char c = '\\'; + str_cat(str, &c, 1); + str_cat(str, p, 1); + } + else { + str_cat(str, p, 1); + } + p++; + } + } + str_cat(str, "/", 1); + if (re) { + if (FL_TEST(re, REG_IGNORECASE)) { + str_cat(str, "i", 1); + } + } + return str; +} + +static VALUE +reg_inspect(re) + struct RRegexp *re; +{ + return reg_desc(re->str, re->len, re); +} + +static void +reg_raise(s, len, err, re) + char *s; + int len; + char *err; + VALUE re; +{ + VALUE desc = reg_desc(s, len, re); + + if (rb_in_eval) + Raise(eRegxpError, "%s: %s", err, RSTRING(desc)->ptr); + else + Error("%s: %s", err, RSTRING(desc)->ptr); +} + +static Regexp* +make_regexp(s, len, flag) + char *s; + int len, flag; +{ + Regexp *rp; + char *err; + + /* Handle escaped characters first. */ + + /* Build a copy of the string (in dest) with the + escaped characters translated, and generate the regex + from that. + */ + + rp = ALLOC(Regexp); + MEMZERO((char *)rp, Regexp, 1); + rp->buffer = ALLOC_N(char, 16); + rp->allocated = 16; + rp->fastmap = ALLOC_N(char, 256); + if (flag) { + rp->translate = casetable; + } + err = re_compile_pattern(s, (size_t)len, rp); + kcode_reset_option(); + if (err != NULL) { + reg_raise(s, len, err, 0); + } + + return rp; +} + +extern VALUE cData; +static VALUE cMatch; + +static VALUE +match_alloc() +{ + NEWOBJ(match, struct RMatch); + OBJSETUP(match, cMatch, T_MATCH); + + match->str = 0; + match->regs = ALLOC(struct re_registers); + MEMZERO(match->regs, struct re_registers, 1); + + return (VALUE)match; +} + +VALUE ignorecase; + +int +reg_search(reg, str, start, regs) + struct RRegexp *reg; + struct RString *str; + int start; + struct re_registers *regs; +{ + int result; + int casefold = RTEST(ignorecase); + VALUE match = 0; + struct re_registers *regs0 = 0; + int need_recompile = 0; + + if (start > str->len) return -1; + + /* case-flag set for the object */ + if (FL_TEST(reg, REG_IGNORECASE)) { + casefold = TRUE; + } + if (casefold) { + if (reg->ptr->translate != casetable) { + reg->ptr->translate = casetable; + reg->ptr->fastmap_accurate = 0; + need_recompile = 1; + } + } + else if (reg->ptr->translate) { + reg->ptr->translate = NULL; + reg->ptr->fastmap_accurate = 0; + need_recompile = 1; + } + + if (regs == (struct re_registers*)-1) { + regs = 0; + } + else { + match = match_alloc(); + regs0 = RMATCH(match)->regs; + } + + if (regs && !match) regs0 = regs; + + if (FL_TEST(reg, KCODE_FIXED)) { + kcode_set_option(reg); + } + else if ((RBASIC(reg)->flags & KCODE_MASK) != reg_kcode) { + need_recompile = 1; + RBASIC(reg)->flags = RBASIC(reg)->flags & ~KCODE_MASK; + RBASIC(reg)->flags |= reg_kcode; + } + + if (need_recompile) { + char *err; + + err = re_compile_pattern(reg->str, reg->len, reg->ptr); + if (err != NULL) { + kcode_reset_option(); + reg_raise(reg->str, reg->len, err, reg); + } + } + + result = re_search(reg->ptr, str->ptr, str->len, + start, str->len - start, regs0); + kcode_reset_option(); + + if (start == -2) { + reg_raise(reg->str, reg->len, "Stack overfow in regexp matcher", reg); + } + if (result < 0) { + backref_set(Qnil); + } + else if (match) { + RMATCH(match)->str = str_new4(str); + backref_set(match); + } + if (regs && regs0 && regs0 != regs) re_copy_registers(regs, regs0); + + return result; +} + +VALUE +reg_nth_defined(nth, match) + int nth; + struct RMatch *match; +{ + if (NIL_P(match)) return Qnil; + if (nth >= match->regs->num_regs) { + return FALSE; + } + if (match->BEG(nth) == -1) return FALSE; + return TRUE; +} + +VALUE +reg_nth_match(nth, match) + int nth; + struct RMatch *match; +{ + int start, end, len; + + if (NIL_P(match)) return Qnil; + if (nth >= match->regs->num_regs) { + return Qnil; + } + start = match->BEG(nth); + if (start == -1) return Qnil; + end = match->END(nth); + len = end - start; + return str_new(RSTRING(match->str)->ptr + start, len); +} + +VALUE +reg_last_match(match) + struct RMatch *match; +{ + return reg_nth_match(0, match); +} + +VALUE +reg_match_pre(match) + struct RMatch *match; +{ + if (NIL_P(match)) return Qnil; + if (match->BEG(0) == -1) return Qnil; + return str_new(RSTRING(match->str)->ptr, match->BEG(0)); +} + +VALUE +reg_match_post(match) + struct RMatch *match; +{ + if (NIL_P(match)) return Qnil; + if (match->BEG(0) == -1) return Qnil; + return str_new(RSTRING(match->str)->ptr+match->END(0), + RSTRING(match->str)->len-match->END(0)); +} + +VALUE +reg_match_last(match) + struct RMatch *match; +{ + int i; + + if (NIL_P(match)) return Qnil; + if (match->BEG(0) == -1) return Qnil; + + for (i=match->regs->num_regs-1; match->BEG(i) == -1 && i > 0; i--) + ; + if (i == 0) return Qnil; + return reg_nth_match(i, match); +} + +static VALUE +last_match_getter() +{ + return reg_last_match(backref_get()); +} + +static VALUE +prematch_getter() +{ + return reg_match_pre(backref_get()); +} + +static VALUE +postmatch_getter() +{ + return reg_match_post(backref_get()); +} + +static VALUE +last_paren_match_getter() +{ + return reg_match_last(backref_get()); +} + +static VALUE +match_to_a(match) + struct RMatch *match; +{ + struct re_registers *regs = match->regs; + VALUE ary = ary_new(regs->num_regs); + char *ptr = RSTRING(match->str)->ptr; + int i; + + for (i=0; i<regs->num_regs; i++) { + if (regs->beg[0] == -1) ary_push(ary, Qnil); + else ary_push(ary, str_new(ptr+regs->beg[i], + regs->end[i]-regs->beg[i])); + } + return ary; +} + +static VALUE +match_to_s(match) + struct RMatch *match; +{ + VALUE str = reg_last_match(match); + + if (NIL_P(str)) return str_new(0,0); + return str; +} + +void +reg_free(rp) +Regexp *rp; +{ + free(rp->buffer); + free(rp->fastmap); + free(rp); +} + +VALUE cRegexp; + +static VALUE +reg_new_1(class, s, len, flag) + VALUE class; + char *s; + int len; + int flag; /* CASEFOLD = 0x1 */ + /* CODE_NONE = 0x2 */ + /* CODE_EUC = 0x4 */ + /* CODE_SJIS = 0x6 */ +{ + NEWOBJ(re, struct RRegexp); + OBJSETUP(re, class, T_REGEXP); + + if (flag & 0x1) { + FL_SET(re, REG_IGNORECASE); + } + switch (flag & ~0x1) { + case 0: + default: + FL_SET(re, reg_kcode); + break; + case 2: + kcode_none(re); + break; + case 4: + kcode_euc(re); + break; + case 6: + kcode_sjis(re); + break; + } + + kcode_set_option(re); + re->ptr = make_regexp(s, len, flag & 0x1); + re->str = ALLOC_N(char, len+1); + memcpy(re->str, s, len); + re->str[len] = '\0'; + re->len = len; + + return (VALUE)re; +} + +VALUE +reg_new(s, len, flag) + char *s; + int len, flag; +{ + return reg_new_1(cRegexp, s, len, flag); +} + +static int ign_cache; +static VALUE reg_cache; + +VALUE +reg_regcomp(str) + struct RString *str; +{ + int ignc = RTEST(ignorecase); + + if (reg_cache && RREGEXP(reg_cache)->len == str->len + && ign_cache == ignc + && memcmp(RREGEXP(reg_cache)->str, str->ptr, str->len) == 0) + return reg_cache; + + ign_cache = ignc; + return reg_cache = reg_new(str->ptr, str->len, ignc); +} + +VALUE +reg_match(re, str) + struct RRegexp *re; + struct RString *str; +{ + int start; + + if (TYPE(str) != T_STRING) return FALSE; + start = reg_search(re, str, 0, 0); + if (start < 0) { + return FALSE; + } + return INT2FIX(start); +} + +VALUE +reg_match2(re) + struct RRegexp *re; +{ + int start; + VALUE line = lastline_get(); + + if (TYPE(line) != T_STRING) + return FALSE; + + start = reg_search(re, line, 0, 0); + if (start < 0) { + return FALSE; + } + return INT2FIX(start); +} + +static VALUE +reg_s_new(argc, argv, self) + int argc; + VALUE *argv; + VALUE self; +{ + VALUE src; + int flag = 0; + + if (argc == 0 || argc > 3) { + ArgError("wrong # of argument"); + } + if (argc >= 2 && RTEST(argv[1])) { + flag = 1; + } + if (argc == 3) { + Check_Type(argv[2], T_STRING); + switch (RSTRING(argv[2])->ptr[0]) { + case 'n': case 'N': + flag |= 2; + break; + case 'e': case 'E': + flag |= 4; + break; + case 's': case 'S': + flag |= 6; + break; + default: + break; + } + } + + src = argv[0]; + switch (TYPE(src)) { + case T_STRING: + return reg_new_1(self, RSTRING(src)->ptr, RSTRING(src)->len, flag); + break; + + case T_REGEXP: + return reg_new_1(self, RREGEXP(src)->str, RREGEXP(src)->len, flag); + break; + + default: + Check_Type(src, T_STRING); + } + + return Qnil; +} + +static VALUE +reg_s_quote(re, str) + VALUE re; + struct RString *str; +{ + char *s, *send, *t; + char *tmp; + + Check_Type(str, T_STRING); + + tmp = ALLOCA_N(char, str->len*2); + + s = str->ptr; send = s + str->len; + t = tmp; + + for (; s != send; s++) { + if (*s == '[' || *s == ']' + || *s == '{' || *s == '}' + || *s == '(' || *s == ')' + || *s == '|' + || *s == '*' || *s == '.' || *s == '\\' + || *s == '?' || *s == '+' + || *s == '^' || *s == '$') { + *t++ = '\\'; + } + *t++ = *s; + } + + return str_new(tmp, t - tmp); +} + +static VALUE +reg_clone(re) + struct RRegexp *re; +{ + int flag = FL_TEST(re, REG_IGNORECASE); + if (FL_TEST(re, KCODE_FIXED)) { + switch (RBASIC(re)->flags & KCODE_MASK) { + case KCODE_NONE: + flag |= 2; break; + case KCODE_EUC: + flag |= 4; break; + case KCODE_SJIS: + flag |= 6; break; + default: + break; + } + } + return reg_new_1(CLASS_OF(re), re->str, re->len, flag); +} + +VALUE +reg_regsub(str, src, regs) + struct RString *str; + struct RString *src; + struct re_registers *regs; +{ + VALUE val = 0; + VALUE tmp; + char *p, *s, *e, c; + int no; + + p = s = str->ptr; + e = s + str->len; + + while (s < e) { + char *ss = s; + + c = *s++; + if (c != '\\') continue; + + if (!val) val = str_new(p, ss-p); + else str_cat(val, p, ss-p); + + c = *s++; + p = s; + switch (c) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + no = c - '0'; + break; + case '&': + no = 0; + break; + + case '`': + str_cat(val, src->ptr, BEG(0)); + continue; + + case '\'': + str_cat(val, src->ptr+END(0), src->len-END(0)); + continue; + + case '+': + no = regs->num_regs-1; + while (BEG(no) == -1 && no > 0) no--; + if (no == 0) continue; + break; + + case '\\': + str_cat(val, s-1, 1); + continue; + + default: + str_cat(val, s-2, 2); + continue; + } + + if (no >= 0) { + if (BEG(no) == -1) continue; + str_cat(val, src->ptr+BEG(no), END(no)-BEG(no)); + } + } + + if (p < e) { + if (!val) val = str_new(p, e-p); + else str_cat(val, p, e-p); + } + if (!val) return (VALUE)str; + + return val; +} + +static VALUE +kcode_getter() +{ + switch (reg_kcode) { + case KCODE_SJIS: + return str_new2("SJIS"); + case KCODE_EUC: + return str_new2("EUC"); + default: + return str_new2("NONE"); + } +} + +void +rb_set_kcode(code) + char *code; +{ + re_syntax_options &= ~RE_MBCTYPE_MASK; + if (code == 0) goto set_no_conversion; + + switch (code[0]) { + case 'E': + case 'e': + reg_kcode = KCODE_EUC; + re_syntax_options |= RE_MBCTYPE_EUC; + break; + case 'S': + case 's': + reg_kcode = KCODE_SJIS; + re_syntax_options |= RE_MBCTYPE_SJIS; + break; + default: + case 'N': + case 'n': + set_no_conversion: + reg_kcode = KCODE_NONE; + break; + } + re_set_syntax(re_syntax_options); +} + +static void +kcode_setter(val) + struct RString *val; +{ + Check_Type(val, T_STRING); + rb_set_kcode(val->ptr); +} + +static VALUE +match_getter() +{ + return backref_get(); +} + +static void +match_setter(val) + VALUE val; +{ + Check_Type(val, T_MATCH); + backref_set(val); +} + +VALUE any_to_s(); + +void +Init_Regexp() +{ + extern VALUE eException; + + eRegxpError = rb_define_class("RegxpError", eException); + + re_set_syntax(RE_NO_BK_PARENS | RE_NO_BK_VBAR + | RE_INTERVALS + | RE_NO_BK_BRACES + | RE_CONTEXTUAL_INVALID_OPS + | RE_BACKSLASH_ESCAPE_IN_LISTS +#ifdef DEFAULT_MBCTYPE + | DEFAULT_MBCTYPE +#endif +); + + rb_define_virtual_variable("$~", match_getter, match_setter); + rb_define_virtual_variable("$&", last_match_getter, 0); + rb_define_virtual_variable("$`", prematch_getter, 0); + rb_define_virtual_variable("$'", postmatch_getter, 0); + rb_define_virtual_variable("$+", last_paren_match_getter, 0); + + rb_define_variable("$=", &ignorecase); + rb_define_virtual_variable("$KCODE", kcode_getter, kcode_setter); + rb_define_virtual_variable("$-K", kcode_getter, kcode_setter); + + cRegexp = rb_define_class("Regexp", cObject); + rb_define_singleton_method(cRegexp, "new", reg_s_new, -1); + rb_define_singleton_method(cRegexp, "compile", reg_s_new, -1); + rb_define_singleton_method(cRegexp, "quote", reg_s_quote, 1); + + rb_define_method(cRegexp, "clone", reg_clone, 0); + rb_define_method(cRegexp, "=~", reg_match, 1); + rb_define_method(cRegexp, "===", reg_match, 1); + rb_define_method(cRegexp, "~", reg_match2, 0); + rb_define_method(cRegexp, "inspect", reg_inspect, 0); + + rb_global_variable(®_cache); + + cMatch = rb_define_class("MatchingData", cData); + rb_define_method(cMatch, "to_a", match_to_a, 0); + rb_define_method(cMatch, "to_s", match_to_s, 0); + rb_define_method(cMatch, "inspect", any_to_s, 0); +} @@ -0,0 +1,34 @@ +/************************************************ + + re.h - + + $Author$ + $Revision$ + $Date$ + created at: Thu Sep 30 14:18:32 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#ifndef RE_H +#define RE_H + +#include <sys/types.h> +#include <stdio.h> + +#include "regex.h" + +typedef struct re_pattern_buffer Regexp; + +struct RMatch { + struct RBasic basic; + VALUE str; + struct re_registers *regs; +}; + +#define RMATCH(obj) (R_CAST(RMatch)(obj)) + +VALUE re_regcomp(); +VALUE re_regsub(); +#endif diff --git a/regex.c b/regex.c new file mode 100644 index 0000000000..686695cbe2 --- /dev/null +++ b/regex.c @@ -0,0 +1,2807 @@ +/* Extended regular expression matching and search library. + Copyright (C) 1985, 1989-90 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* Multi-byte extension added May, 1993 by t^2 (Takahiro Tanimoto) + Last change: May 21, 1993 by t^2 */ + + +/* To test, compile with -Dtest. This Dtestable feature turns this into + a self-contained program which reads a pattern, describes how it + compiles, then reads a string and searches for it. + + On the other hand, if you compile with both -Dtest and -Dcanned you + can run some tests we've already thought of. */ + +/* We write fatal error messages on standard error. */ +#include <stdio.h> + +/* isalpha(3) etc. are used for the character classes. */ +#include <ctype.h> +#include <sys/types.h> + +#include "config.h" +#include "defines.h" + +#ifdef __STDC__ +#define P(s) s +#define MALLOC_ARG_T size_t +#else +#define P(s) () +#define MALLOC_ARG_T unsigned +#define volatile +#define const +#endif + +/* #define NO_ALLOCA /* try it out for now */ +#ifndef NO_ALLOCA +/* Make alloca work the best possible way. */ +#ifdef __GNUC__ +#ifndef atarist +#ifndef alloca +#define alloca __builtin_alloca +#endif +#endif /* atarist */ +#else +#if defined(HAVE_ALLOCA_H) && !defined(__GNUC__) +#include <alloca.h> +#else +char *alloca(); +#endif +#endif /* __GNUC__ */ + +#ifdef _AIX +#pragma alloca +#endif + +#define RE_ALLOCATE alloca +#define FREE_VARIABLES() alloca(0) + +#define FREE_AND_RETURN_VOID(stackb) return +#define FREE_AND_RETURN(stackb,val) return(val) +#define DOUBLE_STACK(stackx,stackb,len) \ + (stackx = (unsigned char **) alloca(2 * len \ + * sizeof(unsigned char *)),\ + /* Only copy what is in use. */ \ + (unsigned char **) memcpy(stackx, stackb, len * sizeof (char *))) +#else /* NO_ALLOCA defined */ + +#define RE_ALLOCATE malloc + +#define FREE_VAR(var) if (var) free(var); var = NULL +#define FREE_VARIABLES() \ + do { \ + FREE_VAR(regstart); \ + FREE_VAR(regend); \ + FREE_VAR(best_regstart); \ + FREE_VAR(best_regend); \ + FREE_VAR(reg_info); \ + } while (0) + +#define FREE_AND_RETURN_VOID(stackb) free(stackb);return +#define FREE_AND_RETURN(stackb,val) free(stackb);return(val) +#define DOUBLE_STACK(stackx,stackb,len) \ + (unsigned char **)xrealloc(stackb, 2 * len * sizeof(unsigned char *)) +#endif /* NO_ALLOCA */ + +#define RE_TALLOC(n,t) ((t*)RE_ALLOCATE((n)*sizeof(t))) +#define TMALLOC(n,t) ((t*)xmalloc((n)*sizeof(t))) +#define TREALLOC(s,n,t) (s=((t*)xrealloc(s,(n)*sizeof(t)))) + +/* Get the interface, including the syntax bits. */ +#include "regex.h" + +static void store_jump P((char *, int, char *)); +static void insert_jump P((int, char *, char *, char *)); +static void store_jump_n P((char *, int, char *, unsigned)); +static void insert_jump_n P((int, char *, char *, char *, unsigned)); +static void insert_op_2 P((int, char *, char *, int, int )); +static int memcmp_translate P((unsigned char *, unsigned char *, + int, unsigned char *)); + +/* Define the syntax stuff, so we can do the \<, \>, etc. */ + +/* This must be nonzero for the wordchar and notwordchar pattern + commands in re_match_2. */ +#ifndef Sword +#define Sword 1 +#endif + +#define SYNTAX(c) re_syntax_table[c] + +static char re_syntax_table[256]; +static void init_syntax_once P((void)); + +#undef P + +#include "util.h" + +static void +init_syntax_once() +{ + register int c; + static int done = 0; + + if (done) + return; + + memset(re_syntax_table, 0, sizeof re_syntax_table); + + for (c = 'a'; c <= 'z'; c++) + re_syntax_table[c] = Sword; + + for (c = 'A'; c <= 'Z'; c++) + re_syntax_table[c] = Sword; + + for (c = '0'; c <= '9'; c++) + re_syntax_table[c] = Sword; + + re_syntax_table['_'] = Sword; + + /* Add specific syntax for ISO Latin-1. */ + for (c = 0300; c <= 0377; c++) + re_syntax_table[c] = Sword; + re_syntax_table[0327] = 0; + re_syntax_table[0367] = 0; + + done = 1; +} + +/* Sequents are missing isgraph. */ +#ifndef isgraph +#define isgraph(c) (isprint((c)) && !isspace((c))) +#endif + +/* These are the command codes that appear in compiled regular + expressions, one per byte. Some command codes are followed by + argument bytes. A command code can specify any interpretation + whatsoever for its arguments. Zero-bytes may appear in the compiled + regular expression. + + The value of `exactn' is needed in search.c (search_buffer) in emacs. + So regex.h defines a symbol `RE_EXACTN_VALUE' to be 1; the value of + `exactn' we use here must also be 1. */ + +enum regexpcode + { + unused=0, + exactn=1, /* Followed by one byte giving n, then by n literal bytes. */ + begline, /* Fail unless at beginning of line. */ + endline, /* Fail unless at end of line. */ + jump, /* Followed by two bytes giving relative address to jump to. */ + on_failure_jump, /* Followed by two bytes giving relative address of + place to resume at in case of failure. */ + finalize_jump, /* Throw away latest failure point and then jump to + address. */ + maybe_finalize_jump, /* Like jump but finalize if safe to do so. + This is used to jump back to the beginning + of a repeat. If the command that follows + this jump is clearly incompatible with the + one at the beginning of the repeat, such that + we can be sure that there is no use backtracking + out of repetitions already completed, + then we finalize. */ + dummy_failure_jump, /* Jump, and push a dummy failure point. This + failure point will be thrown away if an attempt + is made to use it for a failure. A + construct + makes this before the first repeat. Also + use it as an intermediary kind of jump when + compiling an or construct. */ + succeed_n, /* Used like on_failure_jump except has to succeed n times; + then gets turned into an on_failure_jump. The relative + address following it is useless until then. The + address is followed by two bytes containing n. */ + jump_n, /* Similar to jump, but jump n times only; also the relative + address following is in turn followed by yet two more bytes + containing n. */ + set_number_at, /* Set the following relative location to the + subsequent number. */ + anychar, /* Matches any (more or less) one character. */ + charset, /* Matches any one char belonging to specified set. + First following byte is number of bitmap bytes. + Then come bytes for a bitmap saying which chars are in. + Bits in each byte are ordered low-bit-first. + A character is in the set if its bit is 1. + A character too large to have a bit in the map + is automatically not in the set. */ + charset_not, /* Same parameters as charset, but match any character + that is not one of those specified. */ + start_memory, /* Start remembering the text that is matched, for + storing in a memory register. Followed by one + byte containing the register number. Register numbers + must be in the range 0 through RE_NREGS. */ + stop_memory, /* Stop remembering the text that is matched + and store it in a memory register. Followed by + one byte containing the register number. Register + numbers must be in the range 0 through RE_NREGS. */ + duplicate, /* Match a duplicate of something remembered. + Followed by one byte containing the index of the memory + register. */ + wordchar, /* Matches any word-constituent character. */ + notwordchar, /* Matches any char that is not a word-constituent. */ + wordbound, /* Succeeds if at a word boundary. */ + notwordbound,/* Succeeds if not at a word boundary. */ + }; + + +/* Number of failure points to allocate space for initially, + when matching. If this number is exceeded, more space is allocated, + so it is not a hard limit. */ + +#ifndef NFAILURES +#define NFAILURES 80 +#endif + +#if defined(CHAR_UNSIGNED) || defined(__CHAR_UNSIGNED__) +#define SIGN_EXTEND_CHAR(c) ((c)>(char)127?(c)-256:(c)) /* for IBM RT */ +#endif +#ifndef SIGN_EXTEND_CHAR +#define SIGN_EXTEND_CHAR(x) (x) +#endif + + +/* Store NUMBER in two contiguous bytes starting at DESTINATION. */ +#define STORE_NUMBER(destination, number) \ + { (destination)[0] = (number) & 0377; \ + (destination)[1] = (number) >> 8; } + +/* Same as STORE_NUMBER, except increment the destination pointer to + the byte after where the number is stored. Watch out that values for + DESTINATION such as p + 1 won't work, whereas p will. */ +#define STORE_NUMBER_AND_INCR(destination, number) \ + { STORE_NUMBER(destination, number); \ + (destination) += 2; } + + +/* Put into DESTINATION a number stored in two contingous bytes starting + at SOURCE. */ +#define EXTRACT_NUMBER(destination, source) \ + { (destination) = *(source) & 0377; \ + (destination) += SIGN_EXTEND_CHAR (*(char *)((source) + 1)) << 8; } + +/* Same as EXTRACT_NUMBER, except increment the pointer for source to + point to second byte of SOURCE. Note that SOURCE has to be a value + such as p, not, e.g., p + 1. */ +#define EXTRACT_NUMBER_AND_INCR(destination, source) \ + { EXTRACT_NUMBER(destination, source); \ + (source) += 2; } + + +/* Specify the precise syntax of regexps for compilation. This provides + for compatibility for various utilities which historically have + different, incompatible syntaxes. + + The argument SYNTAX is a bit-mask comprised of the various bits + defined in regex.h. */ + +long +re_set_syntax(syntax) + long syntax; +{ + long ret; + + ret = re_syntax_options; + re_syntax_options = syntax; + return ret; +} + +/* Set by re_set_syntax to the current regexp syntax to recognize. */ +long re_syntax_options = DEFAULT_MBCTYPE; + + +/* Macros for re_compile_pattern, which is found below these definitions. */ + +/* Fetch the next character in the uncompiled pattern---translating it + if necessary. Also cast from a signed character in the constant + string passed to us by the user to an unsigned char that we can use + as an array index (in, e.g., `translate'). */ +#define PATFETCH(c) \ + do {if (p == pend) goto end_of_pattern; \ + c = (unsigned char) *p++; \ + if (translate) c = (unsigned char)translate[c]; \ + } while (0) + +/* Fetch the next character in the uncompiled pattern, with no + translation. */ +#define PATFETCH_RAW(c) \ + do {if (p == pend) goto end_of_pattern; \ + c = (unsigned char) *p++; \ + } while (0) + +/* Go backwards one character in the pattern. */ +#define PATUNFETCH p-- + + +/* If the buffer isn't allocated when it comes in, use this. */ +#define INIT_BUF_SIZE 28 + +/* Make sure we have at least N more bytes of space in buffer. */ +#define GET_BUFFER_SPACE(n) \ + { \ + while (b - bufp->buffer + (n) >= bufp->allocated) \ + EXTEND_BUFFER; \ + } + +/* Make sure we have one more byte of buffer space and then add CH to it. */ +#define BUFPUSH(ch) \ + { \ + GET_BUFFER_SPACE(1); \ + *b++ = (char)(ch); \ + } + +/* Extend the buffer by twice its current size via reallociation and + reset the pointers that pointed into the old allocation to point to + the correct places in the new allocation. If extending the buffer + results in it being larger than 1 << 16, then flag memory exhausted. */ +#define EXTEND_BUFFER \ + { char *old_buffer = bufp->buffer; \ + if (bufp->allocated == (1L<<16)) goto too_big; \ + bufp->allocated *= 2; \ + if (bufp->allocated > (1L<<16)) bufp->allocated = (1L<<16); \ + bufp->buffer = (char *) xrealloc (bufp->buffer, bufp->allocated); \ + if (bufp->buffer == 0) \ + goto memory_exhausted; \ + b = (b - old_buffer) + bufp->buffer; \ + if (fixup_jump) \ + fixup_jump = (fixup_jump - old_buffer) + bufp->buffer; \ + if (laststart) \ + laststart = (laststart - old_buffer) + bufp->buffer; \ + begalt = (begalt - old_buffer) + bufp->buffer; \ + if (pending_exact) \ + pending_exact = (pending_exact - old_buffer) + bufp->buffer; \ + } + + +/* Set the bit for character C in a character set list. */ +#define SET_LIST_BIT(c) \ + (b[(unsigned char)(c) / BYTEWIDTH] \ + |= 1 << ((unsigned char)(c) % BYTEWIDTH)) + +/* Get the next unsigned number in the uncompiled pattern. */ +#define GET_UNSIGNED_NUMBER(num) \ + { if (p != pend) \ + { \ + PATFETCH(c); \ + while (isdigit(c)) \ + { \ + if (num < 0) \ + num = 0; \ + num = num * 10 + c - '0'; \ + if (p == pend) \ + break; \ + PATFETCH(c); \ + } \ + } \ + } + +/* Subroutines for re_compile_pattern. */ +/* static void store_jump(), insert_jump(), store_jump_n(), + insert_jump_n(), insert_op_2(); */ + +#define STORE_MBC(p, c) \ + ((p)[0] = (unsigned char)(c >> 8), (p)[1] = (unsigned char)(c)) +#define STORE_MBC_AND_INCR(p, c) \ + (*(p)++ = (unsigned char)(c >> 8), *(p)++ = (unsigned char)(c)) + +#define EXTRACT_MBC(p) \ + ((unsigned short)((unsigned char)(p)[0] << 8 | (unsigned char)(p)[1])) +#define EXTRACT_MBC_AND_INCR(p) \ + ((unsigned short)((p) += 2, (unsigned char)(p)[-2] << 8 | (unsigned char)(p)[-1])) + +#define EXTRACT_UNSIGNED(p) \ + ((unsigned char)(p)[0] | (unsigned char)(p)[1] << 8) +#define EXTRACT_UNSIGNED_AND_INCR(p) \ + ((p) += 2, (unsigned char)(p)[-2] | (unsigned char)(p)[-1] << 8) + +/* Handle (mb)?charset(_not)?. + + Structure of mbcharset(_not)? in compiled pattern. + + struct { + unsinged char id; mbcharset(_not)? + unsigned char sbc_size; + unsigned char sbc_map[sbc_size]; same as charset(_not)? up to here. + unsigned short mbc_size; number of intervals. + struct { + unsigned short beg; beginning of interval. + unsigned short end; end of interval. + } intervals[mbc_size]; + }; */ + +static void +set_list_bits(c1, c2, b) + unsigned short c1, c2; + unsigned char *b; +{ + unsigned char sbc_size = b[-1]; + unsigned short mbc_size = EXTRACT_UNSIGNED(&b[sbc_size]); + unsigned short beg, end, upb; + + if (c1 > c2) + return; + if ((int)c1 < 1 << BYTEWIDTH) { + upb = c2; + if (1 << BYTEWIDTH <= (int)upb) + upb = (1 << BYTEWIDTH) - 1; /* The last single-byte char */ + if (sbc_size <= (unsigned short)(upb / BYTEWIDTH)) { + /* Allocate maximum size so it never happens again. */ + /* NOTE: memcpy() would not work here. */ + memmove(&b[(1 << BYTEWIDTH) / BYTEWIDTH], &b[sbc_size], 2 + mbc_size*4); + memset(&b[sbc_size], 0, (1 << BYTEWIDTH) / BYTEWIDTH - sbc_size); + b[-1] = sbc_size = (1 << BYTEWIDTH) / BYTEWIDTH; + } + for (; c1 <= upb; c1++) + if (!ismbchar(c1)) + SET_LIST_BIT(c1); + if ((int)c2 < 1 << BYTEWIDTH) + return; + c1 = 0x8000; /* The first wide char */ + } + b = &b[sbc_size + 2]; + + for (beg = 0, upb = mbc_size; beg < upb; ) { + unsigned short mid = (unsigned short)(beg + upb) >> 1; + + if ((int)c1 - 1 > (int)EXTRACT_MBC(&b[mid*4 + 2])) + beg = mid + 1; + else + upb = mid; + } + + for (end = beg, upb = mbc_size; end < upb; ) { + unsigned short mid = (unsigned short)(end + upb) >> 1; + + if ((int)c2 >= (int)EXTRACT_MBC(&b[mid*4]) - 1) + end = mid + 1; + else + upb = mid; + } + + if (beg != end) { + if (c1 > EXTRACT_MBC(&b[beg*4])) + c1 = EXTRACT_MBC(&b[beg*4]); + if (c2 < EXTRACT_MBC(&b[(end - 1)*4])) + c2 = EXTRACT_MBC(&b[(end - 1)*4]); + } + if (end < mbc_size && end != beg + 1) + /* NOTE: memcpy() would not work here. */ + memmove(&b[(beg + 1)*4], &b[end*4], (mbc_size - end)*4); + STORE_MBC(&b[beg*4 + 0], c1); + STORE_MBC(&b[beg*4 + 2], c2); + mbc_size += beg - end + 1; + STORE_NUMBER(&b[-2], mbc_size); +} + +static int +is_in_list(c, b) + unsigned short c; + const unsigned char *b; +{ + unsigned short size; + unsigned short i, j; + int result = 0; + + size = *b++; + if ((int)c < 1<<BYTEWIDTH) { + 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; + + if (b[(size-1)*4] == 0xff) { + i = c; + if ((int)c >= 1<<BYTEWIDTH) { + i = i>>BYTEWIDTH; + } + while (size>0 && b[size*4-2] == 0xff) { + size--; + if (b[size*4+1] <= i && i <= b[size*4+3]) { + result = 2; + break; + } + } + } + for (i = 0, j = size; i < j; ) { + unsigned short k = (unsigned short)(i + j) >> 1; + + if (c > EXTRACT_MBC(&b[k*4+2])) + i = k + 1; + else + j = k; + } + if (i < size && EXTRACT_MBC(&b[i*4]) <= c + && ((unsigned char)c != '\n' && (unsigned char)c != '\0')) + return 1; + return result; +} + +/* re_compile_pattern takes a regular-expression string + and converts it into a buffer full of byte commands for matching. + + PATTERN is the address of the pattern string + SIZE is the length of it. + BUFP is a struct re_pattern_buffer * which points to the info + on where to store the byte commands. + This structure contains a char * which points to the + actual space, which should have been obtained with malloc. + re_compile_pattern may use realloc to grow the buffer space. + + The number of bytes of commands can be found out by looking in + the `struct re_pattern_buffer' that bufp pointed to, after + re_compile_pattern returns. */ + +char * +re_compile_pattern(pattern, size, bufp) + char *pattern; + size_t size; + struct re_pattern_buffer *bufp; +{ + register char *b = bufp->buffer; + register char *p = pattern; + char *pend = pattern + size; + register unsigned c, c1; + char *p0; + int numlen; + + /* Address of the count-byte of the most recently inserted `exactn' + command. This makes it possible to tell whether a new exact-match + character can be added to that command or requires a new `exactn' + command. */ + + char *pending_exact = 0; + + /* Address of the place where a forward-jump should go to the end of + the containing expression. Each alternative of an `or', except the + last, ends with a forward-jump of this sort. */ + + char *fixup_jump = 0; + + /* Address of start of the most recently finished expression. + This tells postfix * where to find the start of its operand. */ + + char *laststart = 0; + + /* In processing a repeat, 1 means zero matches is allowed. */ + + char zero_times_ok; + + /* In processing a repeat, 1 means many matches is allowed. */ + + char many_times_ok; + + /* Address of beginning of regexp, or inside of last \(. */ + + char *begalt = b; + + /* In processing an interval, at least this many matches must be made. */ + int lower_bound; + + /* In processing an interval, at most this many matches can be made. */ + int upper_bound; + + /* Place in pattern (i.e., the {) to which to go back if the interval + is invalid. */ + char *beg_interval = 0; + + /* Stack of information saved by \( and restored by \). + Four stack elements are pushed by each \(: + First, the value of b. + Second, the value of fixup_jump. + Third, the value of regnum. + Fourth, the value of begalt. */ + + int stackb[40]; + int *stackp = stackb; + int *stacke = stackb + 40; + int *stackt; + + /* Counts \('s as they are encountered. Remembered for the matching \), + where it becomes the register number to put in the stop_memory + command. */ + + int regnum = 1; + int range = 0; + + /* How to translate the characters in the pattern. */ + char *translate = bufp->translate; + + bufp->fastmap_accurate = 0; + + /* Initialize the syntax table. */ + init_syntax_once(); + + if (bufp->allocated == 0) + { + bufp->allocated = INIT_BUF_SIZE; + if (bufp->buffer) + /* EXTEND_BUFFER loses when bufp->allocated is 0. */ + bufp->buffer = (char *) xrealloc (bufp->buffer, INIT_BUF_SIZE); + else + /* Caller did not allocate a buffer. Do it for them. */ + bufp->buffer = (char *) xmalloc(INIT_BUF_SIZE); + if (!bufp->buffer) goto memory_exhausted; + begalt = b = bufp->buffer; + } + + while (p != pend) + { + PATFETCH(c); + + switch (c) + { + case '$': + { + char *p1 = p; + /* When testing what follows the $, + look past the \-constructs that don't consume anything. */ + if (! (re_syntax_options & RE_CONTEXT_INDEP_OPS)) + while (p1 != pend) + { + if (*p1 == '\\' && p1 + 1 != pend + && (p1[1] == 'b' || p1[1] == 'B')) + p1 += 2; + else + break; + } + if (re_syntax_options & RE_TIGHT_VBAR) + { + if (! (re_syntax_options & RE_CONTEXT_INDEP_OPS) && p1 != pend) + goto normal_char; + /* Make operand of last vbar end before this `$'. */ + if (fixup_jump) + store_jump(fixup_jump, jump, b); + fixup_jump = 0; + BUFPUSH(endline); + break; + } + /* $ means succeed if at end of line, but only in special contexts. + If validly in the middle of a pattern, it is a normal character. */ + +#if 0 + /* not needed for perl4 compatible */ + if ((re_syntax_options & RE_CONTEXTUAL_INVALID_OPS) && p1 != pend) + goto invalid_pattern; +#endif + if (p1 == pend || *p1 == '\n' + || (re_syntax_options & RE_CONTEXT_INDEP_OPS) + || (re_syntax_options & RE_NO_BK_PARENS + ? *p1 == ')' + : *p1 == '\\' && p1[1] == ')') + || (re_syntax_options & RE_NO_BK_VBAR + ? *p1 == '|' + : *p1 == '\\' && p1[1] == '|')) + { + BUFPUSH(endline); + break; + } + goto normal_char; + } + case '^': + /* ^ means succeed if at beg of line, but only if no preceding + pattern. */ + + if ((re_syntax_options & RE_CONTEXTUAL_INVALID_OPS) && laststart) + goto invalid_pattern; + if (laststart && p - 2 >= pattern && p[-2] != '\n' + && !(re_syntax_options & RE_CONTEXT_INDEP_OPS)) + goto normal_char; + if (re_syntax_options & RE_TIGHT_VBAR) + { + if (p != pattern + 1 + && ! (re_syntax_options & RE_CONTEXT_INDEP_OPS)) + goto normal_char; + BUFPUSH(begline); + begalt = b; + } + else + { + BUFPUSH(begline); + } + break; + + case '+': + case '?': + if ((re_syntax_options & RE_BK_PLUS_QM) + || (re_syntax_options & RE_LIMITED_OPS)) + goto normal_char; + handle_plus: + case '*': + /* If there is no previous pattern, char not special. */ + if (!laststart) + { + if (re_syntax_options & RE_CONTEXTUAL_INVALID_OPS) + goto invalid_pattern; + else if (! (re_syntax_options & RE_CONTEXT_INDEP_OPS)) + goto normal_char; + } + /* If there is a sequence of repetition chars, + collapse it down to just one. */ + zero_times_ok = 0; + many_times_ok = 0; + while (1) + { + zero_times_ok |= c != '+'; + many_times_ok |= c != '?'; + if (p == pend) + break; + PATFETCH(c); + if (c == '*') + ; + else if (!(re_syntax_options & RE_BK_PLUS_QM) + && (c == '+' || c == '?')) + ; + else if ((re_syntax_options & RE_BK_PLUS_QM) + && c == '\\') + { + /* int c1; */ + PATFETCH(c1); + if (!(c1 == '+' || c1 == '?')) + { + PATUNFETCH; + PATUNFETCH; + break; + } + c = c1; + } + else + { + PATUNFETCH; + break; + } + } + + /* Star, etc. applied to an empty pattern is equivalent + to an empty pattern. */ + if (!laststart) + break; + + /* Now we know whether or not zero matches is allowed + and also whether or not two or more matches is allowed. */ + if (many_times_ok) + { + /* If more than one repetition is allowed, put in at the + end a backward relative jump from b to before the next + jump we're going to put in below (which jumps from + laststart to after this jump). */ + GET_BUFFER_SPACE(3); + store_jump(b, maybe_finalize_jump, laststart - 3); + b += 3; /* Because store_jump put stuff here. */ + } + /* On failure, jump from laststart to b + 3, which will be the + end of the buffer after this jump is inserted. */ + GET_BUFFER_SPACE(3); + insert_jump(on_failure_jump, laststart, b + 3, b); + pending_exact = 0; + b += 3; + if (!zero_times_ok) + { + /* At least one repetition is required, so insert a + dummy-failure before the initial on-failure-jump + instruction of the loop. This effects a skip over that + instruction the first time we hit that loop. */ + GET_BUFFER_SPACE(6); + insert_jump(dummy_failure_jump, laststart, laststart + 6, b); + b += 3; + } + break; + + case '.': + laststart = b; + BUFPUSH(anychar); + break; + + case '[': + if (p == pend) + goto invalid_pattern; + while (b - bufp->buffer + > bufp->allocated - 9 - (1 << BYTEWIDTH) / BYTEWIDTH) + EXTEND_BUFFER; + + laststart = b; + if (*p == '^') + { + BUFPUSH(charset_not); + p++; + } + else + BUFPUSH(charset); + p0 = p; + + BUFPUSH((1 << BYTEWIDTH) / BYTEWIDTH); + /* Clear the whole map */ + memset(b, 0, (1 << BYTEWIDTH) / BYTEWIDTH + 2); + + if ((re_syntax_options & RE_HAT_NOT_NEWLINE) && b[-2] == charset_not) + SET_LIST_BIT('\n'); + + + /* Read in characters and ranges, setting map bits. */ + while (1) + { + int size; + unsigned last = (unsigned)-1; + + if ((size = EXTRACT_UNSIGNED(&b[(1 << BYTEWIDTH) / BYTEWIDTH]))) { + /* Ensure the space is enough to hold another interval + of multi-byte chars in charset(_not)?. */ + size = (1 << BYTEWIDTH) / BYTEWIDTH + 2 + size*4 + 4; + while (b + size + 1 > bufp->buffer + bufp->allocated) + EXTEND_BUFFER; + } + range_retry: + PATFETCH(c); + + if (c == ']') { + if (p == p0 + 1) { + /* If this is an empty bracket expression. */ + if ((re_syntax_options & RE_NO_EMPTY_BRACKETS) + && p == pend) + goto invalid_pattern; + } + else + /* Stop if this isn't merely a ] inside a bracket + expression, but rather the end of a bracket + expression. */ + break; + } + if (ismbchar(c)) { + PATFETCH(c1); + c = c << BYTEWIDTH | c1; + } + + /* \ escapes characters when inside [...]. */ + if (c == '\\') { + PATFETCH(c); + switch (c) { + case 'w': + for (c = 0; c < (1 << BYTEWIDTH); c++) + if (SYNTAX(c) == Sword) + SET_LIST_BIT(c); + last = -1; + continue; + + case 'W': + for (c = 0; c < (1 << BYTEWIDTH); c++) + if (SYNTAX(c) != Sword) + SET_LIST_BIT(c); + if (re_syntax_options & RE_MBCTYPE_MASK) { + set_list_bits(0x8000, 0xffff, (unsigned char*)b); + } + last = -1; + continue; + + case 's': + for (c = 0; c < 256; c++) + if (isspace(c)) + SET_LIST_BIT(c); + last = -1; + continue; + + case 'S': + for (c = 0; c < 256; c++) + if (!isspace(c)) + SET_LIST_BIT(c); + if (re_syntax_options & RE_MBCTYPE_MASK) { + set_list_bits(0x8000, 0xffff, (unsigned char*)b); + } + last = -1; + continue; + + case 'd': + for (c = '0'; c <= '9'; c++) + SET_LIST_BIT(c); + last = -1; + continue; + + case 'D': + for (c = 0; c < 256; c++) + if (!isdigit(c)) + SET_LIST_BIT(c); + if (re_syntax_options & RE_MBCTYPE_MASK) { + set_list_bits(0x8000, 0xffff, (unsigned char*)b); + } + last = -1; + continue; + + case 'x': + c = scan_hex(p, 2, &numlen); + if ((re_syntax_options & RE_MBCTYPE_MASK) && c > 0x7f) + c = 0xff00 | c; + p += numlen; + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + PATUNFETCH; + c = scan_oct(p, 3, &numlen); + if ((re_syntax_options & RE_MBCTYPE_MASK) && ismbchar(c)) + c = 0xff00 | c; + p += numlen; + break; + + default: + if (ismbchar(c)) { + PATFETCH(c1); + c = c << 8 | c1; + } + break; + } + } + + /* Get a range. */ + if (range) { + if (last > c) + goto invalid_pattern; + + if ((re_syntax_options & RE_NO_HYPHEN_RANGE_END) + && c == '-' && *p != ']') + goto invalid_pattern; + + range = 0; + if (last < 1 << BYTEWIDTH && c < 1 << BYTEWIDTH) { + for (;last<=c;last++) + SET_LIST_BIT(last); + } + else { + set_list_bits(last, c, (unsigned char*)b); + } + } + else if (p[0] == '-' && p[1] != ']') { + last = c; + PATFETCH(c1); + range = 1; + goto range_retry; + } + else if (c < 1 << BYTEWIDTH) + SET_LIST_BIT(c); + else + set_list_bits(c, c, (unsigned char*)b); + } + + /* Discard any character set/class bitmap bytes that are all + 0 at the end of the map. Decrement the map-length byte too. */ + while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) + b[-1]--; + if (b[-1] != (1 << BYTEWIDTH) / BYTEWIDTH) + memmove(&b[b[-1]], &b[(1 << BYTEWIDTH) / BYTEWIDTH], + 2 + EXTRACT_UNSIGNED (&b[(1 << BYTEWIDTH) / BYTEWIDTH])*4); + b += b[-1] + 2 + EXTRACT_UNSIGNED (&b[b[-1]])*4; + break; + + case '(': + if (! (re_syntax_options & RE_NO_BK_PARENS)) + goto normal_char; + else + goto handle_open; + + case ')': + if (! (re_syntax_options & RE_NO_BK_PARENS)) + goto normal_char; + else + goto handle_close; + + case '\n': + if (! (re_syntax_options & RE_NEWLINE_OR)) + goto normal_char; + else + goto handle_bar; + + case '|': +#if 0 + /* not needed for perl4 compatible */ + if ((re_syntax_options & RE_CONTEXTUAL_INVALID_OPS) + && (! laststart || p == pend)) + goto invalid_pattern; + else + if (! (re_syntax_options & RE_NO_BK_VBAR)) + goto normal_char; + else +#endif + goto handle_bar; + + case '{': + if (! ((re_syntax_options & RE_NO_BK_CURLY_BRACES) + && (re_syntax_options & RE_INTERVALS))) + goto normal_char; + else + goto handle_interval; + + case '\\': + if (p == pend) goto invalid_pattern; + /* Do not translate the character after the \, so that we can + distinguish, e.g., \B from \b, even if we normally would + translate, e.g., B to b. */ + PATFETCH_RAW(c); + switch (c) + { + case '(': + if (re_syntax_options & RE_NO_BK_PARENS) + goto normal_backsl; + handle_open: + if (stackp == stacke) goto nesting_too_deep; + + /* Laststart should point to the start_memory that we are about + to push (unless the pattern has RE_NREGS or more ('s). */ + /* obsolete: now RE_NREGS is just a default register size. */ + *stackp++ = b - bufp->buffer; + BUFPUSH(start_memory); + BUFPUSH(regnum); + *stackp++ = fixup_jump ? fixup_jump - bufp->buffer + 1 : 0; + *stackp++ = regnum++; + *stackp++ = begalt - bufp->buffer; + fixup_jump = 0; + laststart = 0; + begalt = b; + /* too many ()'s to fit in a byte. */ + if (regnum >= (1<<BYTEWIDTH)) goto too_big; + break; + + case ')': + if (re_syntax_options & RE_NO_BK_PARENS) + goto normal_backsl; + handle_close: + if (stackp == stackb) goto unmatched_close; + begalt = *--stackp + bufp->buffer; + if (fixup_jump) + store_jump(fixup_jump, jump, b); + BUFPUSH(stop_memory); + BUFPUSH(stackp[-1]); + stackp -= 2; + fixup_jump = *stackp ? *stackp + bufp->buffer - 1 : 0; + laststart = *--stackp + bufp->buffer; + break; + + case '|': + if ((re_syntax_options & RE_LIMITED_OPS) + || (re_syntax_options & RE_NO_BK_VBAR)) + goto normal_backsl; + handle_bar: + if (re_syntax_options & RE_LIMITED_OPS) + goto normal_char; + /* Insert before the previous alternative a jump which + jumps to this alternative if the former fails. */ + GET_BUFFER_SPACE(6); + insert_jump(on_failure_jump, begalt, b + 6, b); + pending_exact = 0; + b += 3; + /* The alternative before the previous alternative has a + jump after it which gets executed if it gets matched. + Adjust that jump so it will jump to the previous + alternative's analogous jump (put in below, which in + turn will jump to the next (if any) alternative's such + jump, etc.). The last such jump jumps to the correct + final destination. */ + if (fixup_jump) + store_jump(fixup_jump, jump, b); + + /* Leave space for a jump after previous alternative---to be + filled in later. */ + fixup_jump = b; + b += 3; + + laststart = 0; + begalt = b; + break; + + case '{': + if (! (re_syntax_options & RE_INTERVALS) + /* Let \{ be a literal. */ + || ((re_syntax_options & RE_INTERVALS) + && (re_syntax_options & RE_NO_BK_CURLY_BRACES)) + /* If it's the string "\{". */ + || (p - 2 == pattern && p == pend)) + goto normal_backsl; + handle_interval: + beg_interval = p - 1; /* The {. */ + /* If there is no previous pattern, this isn't an interval. */ + if (!laststart) + { + if (re_syntax_options & RE_CONTEXTUAL_INVALID_OPS) + goto invalid_pattern; + else + goto normal_backsl; + } + /* It also isn't an interval if not preceded by an re + matching a single character or subexpression, or if + the current type of intervals can't handle back + references and the previous thing is a back reference. */ + if (! (*laststart == anychar + || *laststart == charset + || *laststart == charset_not + || *laststart == wordchar + || *laststart == notwordchar + || *laststart == start_memory + || (*laststart == exactn + && (laststart[1] == 1 + || laststart[1] == 2 && ismbchar(laststart[2]))) + || (! (re_syntax_options & RE_NO_BK_REFS) + && *laststart == duplicate))) + { + if (re_syntax_options & RE_NO_BK_CURLY_BRACES) + goto normal_char; + + /* Posix extended syntax is handled in previous + statement; this is for Posix basic syntax. */ + if (re_syntax_options & RE_INTERVALS) + goto invalid_pattern; + + goto normal_backsl; + } + lower_bound = -1; /* So can see if are set. */ + upper_bound = -1; + GET_UNSIGNED_NUMBER(lower_bound); + if (c == ',') + { + GET_UNSIGNED_NUMBER(upper_bound); + if (upper_bound < 0) + upper_bound = RE_DUP_MAX; + } + if (upper_bound < 0) + upper_bound = lower_bound; + if (! (re_syntax_options & RE_NO_BK_CURLY_BRACES)) + { + if (c != '\\') + goto invalid_pattern; + PATFETCH(c); + } + if (c != '}' || lower_bound < 0 || upper_bound > RE_DUP_MAX + || lower_bound > upper_bound + || ((re_syntax_options & RE_NO_BK_CURLY_BRACES) + && p != pend && *p == '{')) + { + if (re_syntax_options & RE_NO_BK_CURLY_BRACES) + goto unfetch_interval; + else + goto invalid_pattern; + } + + /* If upper_bound is zero, don't want to succeed at all; + jump from laststart to b + 3, which will be the end of + the buffer after this jump is inserted. */ + + if (upper_bound == 0) + { + GET_BUFFER_SPACE(3); + insert_jump(jump, laststart, b + 3, b); + b += 3; + } + + /* Otherwise, after lower_bound number of succeeds, jump + to after the jump_n which will be inserted at the end + of the buffer, and insert that jump_n. */ + else + { /* Set to 5 if only one repetition is allowed and + hence no jump_n is inserted at the current end of + the buffer; then only space for the succeed_n is + needed. Otherwise, need space for both the + succeed_n and the jump_n. */ + + unsigned slots_needed = upper_bound == 1 ? 5 : 10; + + GET_BUFFER_SPACE(slots_needed); + /* Initialize the succeed_n to n, even though it will + be set by its attendant set_number_at, because + re_compile_fastmap will need to know it. Jump to + what the end of buffer will be after inserting + this succeed_n and possibly appending a jump_n. */ + insert_jump_n(succeed_n, laststart, b + slots_needed, + b, lower_bound); + b += 5; /* Just increment for the succeed_n here. */ + + /* When hit this when matching, set the succeed_n's n. */ + GET_BUFFER_SPACE(5); + insert_op_2(set_number_at, laststart, b, 5, lower_bound); + b += 5; + + /* More than one repetition is allowed, so put in at + the end of the buffer a backward jump from b to the + succeed_n we put in above. By the time we've gotten + to this jump when matching, we'll have matched once + already, so jump back only upper_bound - 1 times. */ + + if (upper_bound > 1) + { + GET_BUFFER_SPACE(15); + store_jump_n(b, jump_n, laststart+5, upper_bound - 1); + b += 5; + /* When hit this when matching, reset the + preceding jump_n's n to upper_bound - 1. */ + insert_op_2(set_number_at, laststart, b, b - laststart, upper_bound - 1); + b += 5; + + BUFPUSH(set_number_at); + STORE_NUMBER_AND_INCR(b, -5); + STORE_NUMBER_AND_INCR(b, upper_bound - 1); + } + } + pending_exact = 0; + beg_interval = 0; + break; + + + unfetch_interval: + /* If an invalid interval, match the characters as literals. */ + if (beg_interval) + p = beg_interval; + else + { + fprintf(stderr, + "regex: no interval beginning to which to backtrack.\n"); + exit (1); + } + + beg_interval = 0; + PATFETCH(c); /* normal_char expects char in `c'. */ + goto normal_char; + break; + + case 's': + case 'S': + case 'd': + case 'D': + while (b - bufp->buffer + > bufp->allocated - 9 - (1 << BYTEWIDTH) / BYTEWIDTH) + EXTEND_BUFFER; + + laststart = b; + if (c == 's' || c == 'd') { + BUFPUSH(charset); + } + else { + BUFPUSH(charset_not); + } + + BUFPUSH((1 << BYTEWIDTH) / BYTEWIDTH); + memset(b, 0, (1 << BYTEWIDTH) / BYTEWIDTH + 2); + if (c == 's' || c == 'S') { + SET_LIST_BIT(' '); + SET_LIST_BIT('\t'); + SET_LIST_BIT('\n'); + SET_LIST_BIT('\r'); + SET_LIST_BIT('\f'); + } + else { + char cc; + + for (cc = '0'; cc <= '9'; cc++) { + SET_LIST_BIT(cc); + } + } + + while ((int) b[-1] > 0 && b[b[-1] - 1] == 0) + b[-1]--; + if (b[-1] != (1 << BYTEWIDTH) / BYTEWIDTH) + memmove(&b[b[-1]], &b[(1 << BYTEWIDTH) / BYTEWIDTH], + 2 + EXTRACT_UNSIGNED(&b[(1 << BYTEWIDTH) / BYTEWIDTH])*4); + b += b[-1] + 2 + EXTRACT_UNSIGNED(&b[b[-1]])*4; + break; + + case 'w': + laststart = b; + BUFPUSH(wordchar); + break; + + case 'W': + laststart = b; + BUFPUSH(notwordchar); + break; + + case 'b': + BUFPUSH(wordbound); + break; + + case 'B': + BUFPUSH(notwordbound); + break; + + /* hex */ + case 'x': + c1 = 0; + c = scan_hex(p, 2, &numlen); + p += numlen; + if ((re_syntax_options & RE_MBCTYPE_MASK) && c > 0x7f) + c1 = 0xff; + goto numeric_char; + + /* octal */ + case '0': + c1 = 0; + c = scan_oct(p, 3, &numlen); + p += numlen; + if ((re_syntax_options & RE_MBCTYPE_MASK) && c > 0x7f) + c1 = 0xff; + goto numeric_char; + + /* back-ref or octal */ + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + { + char *p_save; + + PATUNFETCH; + p_save = p; + + c1 = 0; + GET_UNSIGNED_NUMBER(c1); + if (p < pend) PATUNFETCH; + + if (c1 >= regnum) { + /* need to get octal */ + p = p_save; + c = scan_oct(p_save, 3, &numlen); + p = p_save + numlen; + c1 = 0; + if ((re_syntax_options & RE_MBCTYPE_MASK) && c > 0x7f) + c1 = 0xff; + goto numeric_char; + } + } + + /* Can't back reference to a subexpression if inside of it. */ + for (stackt = stackp - 2; stackt > stackb; stackt -= 4) + if (*stackt == c1) + goto normal_char; + laststart = b; + BUFPUSH(duplicate); + BUFPUSH(c1); + break; + + case '+': + case '?': + if (re_syntax_options & RE_BK_PLUS_QM) + goto handle_plus; + else + goto normal_backsl; + break; + + default: + normal_backsl: + goto normal_char; + } + break; + + default: + normal_char: /* Expects the character in `c'. */ + c1 = 0; + if (ismbchar(c)) { + c1 = c; + PATFETCH(c); + } + else if (c > 0x7f) { + c1 = 0xff; + } + numeric_char: + if (!pending_exact || pending_exact + *pending_exact + 1 != b + || *pending_exact >= (c1 ? 0176 : 0177) + || *p == '*' || *p == '^' + || ((re_syntax_options & RE_BK_PLUS_QM) + ? *p == '\\' && (p[1] == '+' || p[1] == '?') + : (*p == '+' || *p == '?')) + || ((re_syntax_options & RE_INTERVALS) + && ((re_syntax_options & RE_NO_BK_CURLY_BRACES) + ? *p == '{' + : (p[0] == '\\' && p[1] == '{')))) + { + laststart = b; + BUFPUSH(exactn); + pending_exact = b; + BUFPUSH(0); + } + if (c1) { + BUFPUSH(c1); + (*pending_exact)++; + } + BUFPUSH(c); + (*pending_exact)++; + } + } + + if (fixup_jump) + store_jump(fixup_jump, jump, b); + + if (stackp != stackb) goto unmatched_open; + + bufp->used = b - bufp->buffer; + bufp->re_nsub = regnum; + return 0; + + invalid_char: + return "Invalid character in regular expression"; + + invalid_pattern: + return "Invalid regular expression"; + + unmatched_open: + return "Unmatched ("; + + unmatched_close: + return "Unmatched )"; + + end_of_pattern: + return "Premature end of regular expression"; + + nesting_too_deep: + return "Nesting too deep"; + + too_big: + return "Regular expression too big"; + + memory_exhausted: + return "Memory exhausted"; +} + + +/* Store a jump of the form <OPCODE> <relative address>. + Store in the location FROM a jump operation to jump to relative + address FROM - TO. OPCODE is the opcode to store. */ + +static void +store_jump(from, opcode, to) + char *from, *to; + int opcode; +{ + from[0] = (char)opcode; + STORE_NUMBER(from + 1, to - (from + 3)); +} + + +/* Open up space before char FROM, and insert there a jump to TO. + CURRENT_END gives the end of the storage not in use, so we know + how much data to copy up. OP is the opcode of the jump to insert. + + If you call this function, you must zero out pending_exact. */ + +static void +insert_jump(op, from, to, current_end) + int op; + char *from, *to, *current_end; +{ + register char *pfrom = current_end; /* Copy from here... */ + register char *pto = current_end + 3; /* ...to here. */ + + while (pfrom != from) + *--pto = *--pfrom; + store_jump(from, op, to); +} + + +/* Store a jump of the form <opcode> <relative address> <n> . + + Store in the location FROM a jump operation to jump to relative + address FROM - TO. OPCODE is the opcode to store, N is a number the + jump uses, say, to decide how many times to jump. + + If you call this function, you must zero out pending_exact. */ + +static void +store_jump_n(from, opcode, to, n) + char *from, *to; + int opcode; + unsigned n; +{ + from[0] = (char)opcode; + STORE_NUMBER(from + 1, to - (from + 3)); + STORE_NUMBER(from + 3, n); +} + + +/* Similar to insert_jump, but handles a jump which needs an extra + number to handle minimum and maximum cases. Open up space at + location FROM, and insert there a jump to TO. CURRENT_END gives the + end of the storage in use, so we know how much data to copy up. OP is + the opcode of the jump to insert. + + If you call this function, you must zero out pending_exact. */ + +static void +insert_jump_n(op, from, to, current_end, n) + int op; + char *from, *to, *current_end; + unsigned n; +{ + register char *pfrom = current_end; /* Copy from here... */ + register char *pto = current_end + 5; /* ...to here. */ + + while (pfrom != from) + *--pto = *--pfrom; + store_jump_n(from, op, to, n); +} + + +/* Open up space at location THERE, and insert operation OP followed by + NUM_1 and NUM_2. CURRENT_END gives the end of the storage in use, so + we know how much data to copy up. + + If you call this function, you must zero out pending_exact. */ + +static void +insert_op_2(op, there, current_end, num_1, num_2) + int op; + char *there, *current_end; + int num_1, num_2; +{ + register char *pfrom = current_end; /* Copy from here... */ + register char *pto = current_end + 5; /* ...to here. */ + + while (pfrom != there) + *--pto = *--pfrom; + + there[0] = (char)op; + STORE_NUMBER(there + 1, num_1); + STORE_NUMBER(there + 3, num_2); +} + + + +/* Given a pattern, compute a fastmap from it. The fastmap records + which of the (1 << BYTEWIDTH) possible characters can start a string + that matches the pattern. This fastmap is used by re_search to skip + quickly over totally implausible text. + + The caller must supply the address of a (1 << BYTEWIDTH)-byte data + area as bufp->fastmap. + The other components of bufp describe the pattern to be used. */ + +void +re_compile_fastmap(bufp) + struct re_pattern_buffer *bufp; +{ + unsigned char *pattern = (unsigned char *) bufp->buffer; + int size = bufp->used; + register char *fastmap = bufp->fastmap; + register unsigned char *p = pattern; + register unsigned char *pend = pattern + size; + register int j, k; + unsigned char *translate = (unsigned char *)bufp->translate; + unsigned is_a_succeed_n; + + unsigned char **stackb; + unsigned char **stackp; + stackb = RE_TALLOC(NFAILURES, unsigned char*); + stackp = stackb; + + memset(fastmap, 0, (1 << BYTEWIDTH)); + bufp->fastmap_accurate = 1; + bufp->can_be_null = 0; + + while (p) + { + is_a_succeed_n = 0; + if (p == pend) + { + bufp->can_be_null = 1; + break; + } +#ifdef SWITCH_ENUM_BUG + switch ((int) ((enum regexpcode)*p++)) +#else + switch ((enum regexpcode)*p++) +#endif + { + case exactn: + if (p[1] == 0xff) { + if (translate) + fastmap[translate[p[2]]] = 2; + else + fastmap[p[2]] = 2; + } + else if (translate) + fastmap[translate[p[1]]] = 1; + else + fastmap[p[1]] = 1; + break; + + case begline: + case wordbound: + case notwordbound: + continue; + + case endline: + if (translate) + fastmap[translate['\n']] = 1; + else + fastmap['\n'] = 1; + + if (bufp->can_be_null == 0) + bufp->can_be_null = 2; + break; + + case jump_n: + case finalize_jump: + case maybe_finalize_jump: + case jump: + case dummy_failure_jump: + EXTRACT_NUMBER_AND_INCR(j, p); + p += j; + if (j > 0) + continue; + /* Jump backward reached implies we just went through + the body of a loop and matched nothing. + Opcode jumped to should be an on_failure_jump. + Just treat it like an ordinary jump. + For a * loop, it has pushed its failure point already; + If so, discard that as redundant. */ + + if ((enum regexpcode) *p != on_failure_jump + && (enum regexpcode) *p != succeed_n) + continue; + p++; + EXTRACT_NUMBER_AND_INCR(j, p); + p += j; + if (stackp != stackb && *stackp == p) + stackp--; + continue; + + case on_failure_jump: + handle_on_failure_jump: + EXTRACT_NUMBER_AND_INCR(j, p); + *++stackp = p + j; + if (is_a_succeed_n) + EXTRACT_NUMBER_AND_INCR(k, p); /* Skip the n. */ + continue; + + case succeed_n: + is_a_succeed_n = 1; + /* Get to the number of times to succeed. */ + p += 2; + /* Increment p past the n for when k != 0. */ + EXTRACT_NUMBER_AND_INCR(k, p); + if (k == 0) + { + p -= 4; + goto handle_on_failure_jump; + } + continue; + + case set_number_at: + p += 4; + continue; + + case start_memory: + case stop_memory: + p++; + continue; + + case duplicate: + bufp->can_be_null = 1; + fastmap['\n'] = 1; + case anychar: + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (j != '\n') + fastmap[j] = 1; + if (bufp->can_be_null) + { + FREE_AND_RETURN_VOID(stackb); + } + /* Don't return; check the alternative paths + so we can set can_be_null if appropriate. */ + break; + + case wordchar: + for (j = 0; j < (1 << BYTEWIDTH); j++) + if (SYNTAX(j) == Sword) + fastmap[j] = 1; + break; + + case notwordchar: + for (j = 0; j < 0x80; j++) + if (SYNTAX(j) != Sword) + fastmap[j] = 1; + for (j = 0x80; j < (1 << BYTEWIDTH); j++) + fastmap[j] = 1; + break; + + case charset: + /* NOTE: Charset for single-byte chars never contain + multi-byte char. See set_list_bits(). */ + for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) + if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) + { + if (translate) + fastmap[translate[j]] = 1; + else + fastmap[j] = 1; + } + { + unsigned short size; + unsigned c, end; + + p += p[-1] + 2; + size = EXTRACT_UNSIGNED(&p[-2]); + for (j = 0; j < (int)size; j++) { + if ((unsigned char)p[j*4] == 0xff) { + for (c = (unsigned char)p[j*4+1], + end = (unsigned char)p[j*4+3]; + c <= end; c++) { + fastmap[c] = 2; + } + } + else { + /* set bits for 1st bytes of multi-byte chars. */ + for (c = (unsigned char)p[j*4], + end = (unsigned char)p[j*4 + 2]; + c <= end; c++) { + /* NOTE: Charset for multi-byte chars might contain + single-byte chars. We must reject them. */ + if (ismbchar(c)) + fastmap[c] = 1; + } + } + } + } + break; + + case charset_not: + /* S: set of all single-byte chars. + M: set of all first bytes that can start multi-byte chars. + s: any set of single-byte chars. + m: any set of first bytes that can start multi-byte chars. + + We assume S+M = U. + ___ _ _ + s+m = (S*s+M*m). */ + /* Chars beyond end of map must be allowed */ + /* NOTE: Charset_not for single-byte chars might contain + multi-byte chars. See set_list_bits(). */ + for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++) + if (!ismbchar(j)) + fastmap[j] = 1; + + for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--) + if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))) + { + if (!ismbchar(j)) + fastmap[j] = 1; + } + { + unsigned short size; + unsigned char c, beg; + + p += p[-1] + 2; + size = EXTRACT_UNSIGNED(&p[-2]); + if (size == 0) { + for (j = 0x80; j < (1 << BYTEWIDTH); j++) + if (ismbchar(j)) + fastmap[j] = 1; + } + for (j = 0,c = 0x80;j < (int)size; j++) { + if ((unsigned char)p[j*4] == 0xff) { + for (beg = (unsigned char)p[j*4+1]; c < beg; c++) + fastmap[c] = 2; + c = (unsigned char)p[j*4+3] + 1; + } + else { + for (beg = (unsigned char)p[j*4 + 0]; c < beg; c++) + if (ismbchar(c)) + fastmap[c] = 1; + c = (unsigned char)p[j*4 + 2] + 1; + } + } + } + break; + + case unused: /* pacify gcc -Wall */ + break; + } + + /* Get here means we have successfully found the possible starting + characters of one path of the pattern. We need not follow this + path any farther. Instead, look at the next alternative + remembered in the stack. */ + if (stackp != stackb) + p = *stackp--; + else + break; + } + FREE_AND_RETURN_VOID(stackb); +} + + + + +/* 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 + RANGE is negative, it searches backwards, i.e., the starting + positions tried are STARTPOS, STARTPOS - 1, etc. STRING is of SIZE. + In REGS, return the indices of STRING that matched the entire + BUFP->buffer and its contained subexpressions. + + The value returned is the position in the strings at which the match + was found, or -1 if no match was found, or -2 if error (such as + failure stack overflow). */ + +int +re_search(bufp, string, size, startpos, range, regs) + struct re_pattern_buffer *bufp; + char *string; + int size, startpos, range; + struct re_registers *regs; +{ + register char *fastmap = bufp->fastmap; + register unsigned char *translate = (unsigned char *) bufp->translate; + int val, anchor = 0; + + /* Check for out-of-range starting position. */ + if (startpos < 0 || startpos > size) + return -1; + + /* Update the fastmap now if not correct already. */ + if (fastmap && !bufp->fastmap_accurate) { + re_compile_fastmap (bufp); + } + + if (bufp->used > 0 && (enum regexpcode)bufp->buffer[0] == begline) + anchor = 1; + + for (;;) + { + /* If a fastmap is supplied, skip quickly over characters that + cannot possibly be the start of a match. Note, however, that + if the pattern can possibly match the null string, we must + test it at each starting point so that we take the first null + string we get. */ + + if (fastmap && startpos < size + && bufp->can_be_null != 1 && !(anchor && startpos == 0)) + { + if (range > 0) /* Searching forwards. */ + { + register int lim = 0; + register unsigned char *p, c; + int irange = range; + + lim = range - (size - startpos); + p = (unsigned char *)&(string[startpos]); + + while (range > lim) { + c = *p++; + if (ismbchar(c)) { + if (fastmap[c]) + break; + c = *p++; + range--; + if (fastmap[c] == 2) + break; + } + else + if (fastmap[translate ? translate[c] : c]) + break; + range--; + } + startpos += irange - range; + } + else /* Searching backwards. */ + { + register unsigned char c; + + c = string[startpos]; + c &= 0xff; + if (translate ? !fastmap[translate[c]] : !fastmap[c]) + goto advance; + } + } + + if (anchor && startpos > 0 && startpos < size + && string[startpos-1] != '\n') goto advance; + + if (fastmap && startpos == size && range >= 0 + && (bufp->can_be_null == 0 || + (bufp->can_be_null == 2 && size > 0 + && string[startpos-1] == '\n'))) + return -1; + + val = re_match(bufp, string, size, startpos, regs); + if (val >= 0) + return startpos; + if (val == -2) + return -2; + +#ifndef NO_ALLOCA +#ifdef cALLOCA + alloca(0); +#endif /* cALLOCA */ + +#endif /* NO_ALLOCA */ + advance: + if (!range) + break; + else if (range > 0) { + const char *d = string + startpos; + + if (ismbchar(*d)) { + range--, startpos++; + if (!range) + break; + } + range--, startpos++; + } + else { + range++, startpos--; + { + const char *s, *d, *p; + + s = string; d = string + startpos; + for (p = d; p-- > s && ismbchar(*p); ) + /* --p >= s would not work on 80[12]?86. + (when the offset of s equals 0 other than huge model.) */ + ; + if (!((d - p) & 1)) { + if (!range) + break; + range++, startpos--; + } + } + } + } + return -1; +} + + + + +/* The following are used for re_match, defined below: */ + +/* Roughly the maximum number of failure points on the stack. Would be + exactly that if always pushed MAX_NUM_FAILURE_ITEMS each time we failed. */ + +int re_max_failures = 2000; + +/* Routine used by re_match. */ +/* static int memcmp_translate(); *//* already declared */ + + +/* Structure and accessing macros used in re_match: */ + +struct register_info +{ + unsigned is_active : 1; + unsigned matched_something : 1; +}; + +#define IS_ACTIVE(R) ((R).is_active) +#define MATCHED_SOMETHING(R) ((R).matched_something) + + +/* Macros used by re_match: */ + +/* I.e., regstart, regend, and reg_info. */ + +#define NUM_REG_ITEMS 3 + +/* We push at most this many things on the stack whenever we + fail. The `+ 2' refers to PATTERN_PLACE and STRING_PLACE, which are + arguments to the PUSH_FAILURE_POINT macro. */ + +#define MAX_NUM_FAILURE_ITEMS (num_regs * NUM_REG_ITEMS + 2) + + +/* We push this many things on the stack whenever we fail. */ + +#define NUM_FAILURE_ITEMS (last_used_reg * NUM_REG_ITEMS + 2) + + +/* This pushes most of the information about the current state we will want + if we ever fail back to it. */ + +#define PUSH_FAILURE_POINT(pattern_place, string_place) \ + { \ + long last_used_reg, this_reg; \ + \ + /* Find out how many registers are active or have been matched. \ + (Aside from register zero, which is only set at the end.) */ \ + for (last_used_reg = num_regs - 1; last_used_reg > 0; last_used_reg--)\ + if (regstart[last_used_reg] != (unsigned char *)(-1L)) \ + break; \ + \ + if (stacke - stackp <= NUM_FAILURE_ITEMS) \ + { \ + unsigned char **stackx; \ + unsigned int len = stacke - stackb; \ + if (len > re_max_failures * MAX_NUM_FAILURE_ITEMS) \ + { \ + FREE_VARIABLES(); \ + FREE_AND_RETURN(stackb,(-2)); \ + } \ + \ + /* Roughly double the size of the stack. */ \ + stackx = DOUBLE_STACK(stackx,stackb,len); \ + /* Rearrange the pointers. */ \ + stackp = stackx + (stackp - stackb); \ + stackb = stackx; \ + stacke = stackb + 2 * len; \ + } \ + \ + /* Now push the info for each of those registers. */ \ + for (this_reg = 1; this_reg <= last_used_reg; this_reg++) \ + { \ + *stackp++ = regstart[this_reg]; \ + *stackp++ = regend[this_reg]; \ + *stackp++ = (unsigned char *)®_info[this_reg]; \ + } \ + \ + /* Push how many registers we saved. */ \ + *stackp++ = (unsigned char *)last_used_reg; \ + \ + *stackp++ = pattern_place; \ + *stackp++ = string_place; \ + } + + +/* This pops what PUSH_FAILURE_POINT pushes. */ + +#define POP_FAILURE_POINT() \ + { \ + int temp; \ + stackp -= 2; /* Remove failure points. */ \ + temp = (int) *--stackp; /* How many regs pushed. */ \ + temp *= NUM_REG_ITEMS; /* How much to take off the stack. */ \ + stackp -= temp; /* Remove the register info. */ \ + } + +#define PREFETCH if (d == dend) goto fail + +/* Call this when have matched something; it sets `matched' flags for the + registers corresponding to the subexpressions of which we currently + are inside. */ +#define SET_REGS_MATCHED \ + { unsigned this_reg; \ + for (this_reg = 0; this_reg < num_regs; this_reg++) \ + { \ + if (IS_ACTIVE(reg_info[this_reg])) \ + MATCHED_SOMETHING(reg_info[this_reg]) = 1; \ + else \ + MATCHED_SOMETHING(reg_info[this_reg]) = 0; \ + } \ + } + +#define AT_STRINGS_BEG (d == string) +#define AT_STRINGS_END (d == dend) + +#define AT_WORD_BOUNDARY \ + (AT_STRINGS_BEG || AT_STRINGS_END || IS_A_LETTER (d - 1) != IS_A_LETTER (d)) + +/* We have two special cases to check for: + 1) if we're past the end of string1, we have to look at the first + character in string2; + 2) if we're before the beginning of string2, we have to look at the + last character in string1; we assume there is a string1, so use + this in conjunction with AT_STRINGS_BEG. */ +#define IS_A_LETTER(d) (SYNTAX(*(d)) == Sword) + +static void +init_regs(regs, num_regs) + struct re_registers *regs; + unsigned num_regs; +{ + int i; + + regs->num_regs = num_regs; + if (num_regs < RE_NREGS) + num_regs = RE_NREGS; + + if (regs->allocated == 0) { + regs->beg = TMALLOC(num_regs, int); + regs->end = TMALLOC(num_regs, int); + regs->allocated = num_regs; + } + else if (regs->allocated < num_regs) { + TREALLOC(regs->beg, num_regs, int); + TREALLOC(regs->end, num_regs, int); + } + for (i=0; i<num_regs; i++) { + regs->beg[i] = regs->end[i] = -1; + } +} + +/* Match the pattern described by BUFP against STRING, which is of + SIZE. Start the match at index POS in STRING. In REGS, return the + indices of STRING that matched the entire BUFP->buffer and its + contained subexpressions. + + If bufp->fastmap is nonzero, then it had better be up to date. + + The reason that the data to match are specified as two components + which are to be regarded as concatenated is so this function can be + used directly on the contents of an Emacs buffer. + + -1 is returned if there is no match. -2 is returned if there is an + error (such as match stack overflow). Otherwise the value is the + length of the substring which was matched. */ + +int +re_match(bufp, string_arg, size, pos, regs) + struct re_pattern_buffer *bufp; + char *string_arg; + int size, pos; + struct re_registers *regs; +{ + register unsigned char *p = (unsigned char *) bufp->buffer; + + /* Pointer to beyond end of buffer. */ + register unsigned char *pend = p + bufp->used; + + unsigned num_regs = bufp->re_nsub; + + unsigned char *string = (unsigned char *) string_arg; + + register unsigned char *d, *dend; + register int mcnt; /* Multipurpose. */ + unsigned char *translate = (unsigned char *) bufp->translate; + unsigned is_a_jump_n = 0; + + /* Failure point stack. Each place that can handle a failure further + down the line pushes a failure point on this stack. It consists of + restart, regend, and reg_info for all registers corresponding to the + subexpressions we're currently inside, plus the number of such + registers, and, finally, two char *'s. The first char * is where to + resume scanning the pattern; the second one is where to resume + scanning the strings. If the latter is zero, the failure point is a + ``dummy''; if a failure happens and the failure point is a dummy, it + gets discarded and the next next one is tried. */ + + unsigned char **stackb; + unsigned char **stackp; + unsigned char **stacke; + + + /* Information on the contents of registers. These are pointers into + the input strings; they record just what was matched (on this + attempt) by a subexpression part of the pattern, that is, the + regnum-th regstart pointer points to where in the pattern we began + matching and the regnum-th regend points to right after where we + stopped matching the regnum-th subexpression. (The zeroth register + keeps track of what the whole pattern matches.) */ + + unsigned char **regstart = RE_TALLOC(num_regs, unsigned char*); + unsigned char **regend = RE_TALLOC(num_regs, unsigned char*); + + /* The is_active field of reg_info helps us keep track of which (possibly + nested) subexpressions we are currently in. The matched_something + field of reg_info[reg_num] helps us tell whether or not we have + matched any of the pattern so far this time through the reg_num-th + subexpression. These two fields get reset each time through any + loop their register is in. */ + + struct register_info *reg_info = RE_TALLOC(num_regs, struct register_info); + + /* The following record the register info as found in the above + variables when we find a match better than any we've seen before. + This happens as we backtrack through the failure points, which in + turn happens only if we have not yet matched the entire string. */ + + unsigned best_regs_set = 0; + unsigned char **best_regstart = RE_TALLOC(num_regs, unsigned char*); + unsigned char **best_regend = RE_TALLOC(num_regs, unsigned char*); + + if (regs) { + init_regs(regs, num_regs); + } + + + /* Initialize the stack. */ + stackb = RE_TALLOC(MAX_NUM_FAILURE_ITEMS * NFAILURES, unsigned char*); + stackp = stackb; + stacke = &stackb[MAX_NUM_FAILURE_ITEMS * NFAILURES]; + +#ifdef DEBUG_REGEX + fprintf (stderr, "Entering re_match(%s%s)\n", string1_arg, string2_arg); +#endif + + /* Initialize subexpression text positions to -1 to mark ones that no + \( or ( and \) or ) has been seen for. Also set all registers to + inactive and mark them as not having matched anything or ever + failed. */ + for (mcnt = 0; mcnt < num_regs; mcnt++) { + regstart[mcnt] = regend[mcnt] = (unsigned char *) (-1L); + IS_ACTIVE(reg_info[mcnt]) = 0; + MATCHED_SOMETHING(reg_info[mcnt]) = 0; + } + + /* Set up pointers to ends of strings. + Don't allow the second string to be empty unless both are empty. */ + + + /* `p' scans through the pattern as `d' scans through the data. `dend' + is the end of the input string that `d' points within. `d' is + advanced into the following input string whenever necessary, but + this happens before fetching; therefore, at the beginning of the + loop, `d' can be pointing at the end of a string, but it cannot + equal string2. */ + + d = string + pos, dend = string + size; + + + /* This loops over pattern commands. It exits by returning from the + function if match is complete, or it drops through if match fails + at this starting point in the input data. */ + + while (1) + { +#ifdef DEBUG_REGEX + fprintf(stderr, + "regex loop(%d): matching 0x%02d\n", + p - (unsigned char *) bufp->buffer, + *p); +#endif + is_a_jump_n = 0; + /* End of pattern means we might have succeeded. */ + if (p == pend) + { + /* If not end of string, try backtracking. Otherwise done. */ + if (d != dend) + { + if (stackp != stackb) + { + /* More failure points to try. */ + + /* If exceeds best match so far, save it. */ + if (! best_regs_set || (d > best_regend[0])) + { + best_regs_set = 1; + best_regend[0] = d; /* Never use regstart[0]. */ + + for (mcnt = 1; mcnt < num_regs; mcnt++) + { + best_regstart[mcnt] = regstart[mcnt]; + best_regend[mcnt] = regend[mcnt]; + } + } + goto fail; + } + /* If no failure points, don't restore garbage. */ + else if (best_regs_set) + { + restore_best_regs: + /* Restore best match. */ + d = best_regend[0]; + + for (mcnt = 0; mcnt < num_regs; mcnt++) + { + regstart[mcnt] = best_regstart[mcnt]; + regend[mcnt] = best_regend[mcnt]; + } + } + } + + /* If caller wants register contents data back, convert it + to indices. */ + if (regs) + { + regs->beg[0] = pos; + regs->end[0] = d - string; + for (mcnt = 1; mcnt < num_regs; mcnt++) + { + if (regend[mcnt] == (unsigned char *)(-1L)) + { + regs->beg[mcnt] = -1; + regs->end[mcnt] = -1; + continue; + } + regs->beg[mcnt] = regstart[mcnt] - string; + regs->end[mcnt] = regend[mcnt] - string; + } + } + FREE_VARIABLES(); + FREE_AND_RETURN(stackb, (d - pos - string)); + } + + /* Otherwise match next pattern command. */ +#ifdef SWITCH_ENUM_BUG + switch ((int)((enum regexpcode)*p++)) +#else + switch ((enum regexpcode)*p++) +#endif + { + + /* \( [or `(', as appropriate] is represented by start_memory, + \) by stop_memory. Both of those commands are followed by + a register number in the next byte. The text matched + within the \( and \) is recorded under that number. */ + case start_memory: + regstart[*p] = d; + IS_ACTIVE(reg_info[*p]) = 1; + MATCHED_SOMETHING(reg_info[*p]) = 0; + p++; + break; + + case stop_memory: + regend[*p] = d; + IS_ACTIVE(reg_info[*p]) = 0; + + /* If just failed to match something this time around with a sub- + expression that's in a loop, try to force exit from the loop. */ + if ((! MATCHED_SOMETHING(reg_info[*p]) + || (enum regexpcode) p[-3] == start_memory) + && (p + 1) != pend) + { + register unsigned char *p2 = p + 1; + mcnt = 0; + switch (*p2++) + { + case jump_n: + is_a_jump_n = 1; + case finalize_jump: + case maybe_finalize_jump: + case jump: + case dummy_failure_jump: + EXTRACT_NUMBER_AND_INCR(mcnt, p2); + if (is_a_jump_n) + p2 += 2; + break; + } + p2 += mcnt; + + /* If the next operation is a jump backwards in the pattern + to an on_failure_jump, exit from the loop by forcing a + failure after pushing on the stack the on_failure_jump's + jump in the pattern, and d. */ + if (mcnt < 0 && (enum regexpcode) *p2++ == on_failure_jump) + { + EXTRACT_NUMBER_AND_INCR(mcnt, p2); + PUSH_FAILURE_POINT(p2 + mcnt, d); + goto fail; + } + } + p++; + break; + + /* \<digit> has been turned into a `duplicate' command which is + followed by the numeric value of <digit> as the register number. */ + case duplicate: + { + int regno = *p++; /* Get which register to match against */ + register unsigned char *d2, *dend2; + + /* Where in input to try to start matching. */ + d2 = regstart[regno]; + + /* Where to stop matching; if both the place to start and + the place to stop matching are in the same string, then + set to the place to stop, otherwise, for now have to use + the end of the first string. */ + + dend2 = regend[regno]; + while (1) + { + /* At end of register contents => success */ + if (d2 == dend2) break; + + /* If necessary, advance to next segment in data. */ + PREFETCH; + + /* How many characters left in this segment to match. */ + mcnt = dend - d; + + /* Want how many consecutive characters we can match in + one shot, so, if necessary, adjust the count. */ + if (mcnt > dend2 - d2) + mcnt = dend2 - d2; + + /* Compare that many; failure if mismatch, else move + past them. */ + if (translate + ? memcmp_translate(d, d2, mcnt, translate) + : memcmp((char *)d, (char *)d2, mcnt)) + goto fail; + d += mcnt, d2 += mcnt; + } + } + break; + + case anychar: + PREFETCH; + /* Match anything but a newline, maybe even a null. */ + if (ismbchar(*d)) { + if (d + 1 == dend || d[1] == '\n' || d[1] == '\0') + goto fail; + SET_REGS_MATCHED; + d += 2; + break; + } + if ((translate ? translate[*d] : *d) == '\n' + || ((re_syntax_options & RE_DOT_NOT_NULL) + && (translate ? translate[*d] : *d) == '\000')) + goto fail; + SET_REGS_MATCHED; + d++; + break; + + case charset: + case charset_not: + { + int not; /* Nonzero for charset_not. */ + int half; /* 2 if need to match latter half of mbc */ + int c; + + PREFETCH; + c = (unsigned char)*d; + if (ismbchar(c)) { + if (d + 1 != dend) { + c <<= 8; + c |= (unsigned char)d[1]; + } + } + else if (translate) + c = (unsigned char)translate[c]; + + half = not = is_in_list(c, p); + if (*(p - 1) == (unsigned char)charset_not) { + not = !not; + } + + p += 1 + *p + 2 + EXTRACT_UNSIGNED(&p[1 + *p])*4; + + if (!not) goto fail; + SET_REGS_MATCHED; + + d++; + if (half != 2 && d != dend && c >= 1 << BYTEWIDTH) + d++; + break; + } + + case begline: + if (size == 0 + || d == string + || (d && d[-1] == '\n')) + break; + else + goto fail; + + case endline: + if (d == dend || *d == '\n') + break; + goto fail; + + /* `or' constructs are handled by starting each alternative with + an on_failure_jump that points to the start of the next + alternative. Each alternative except the last ends with a + jump to the joining point. (Actually, each jump except for + the last one really jumps to the following jump, because + tensioning the jumps is a hassle.) */ + + /* The start of a stupid repeat has an on_failure_jump that points + past the end of the repeat text. This makes a failure point so + that on failure to match a repetition, matching restarts past + as many repetitions have been found with no way to fail and + look for another one. */ + + /* A smart repeat is similar but loops back to the on_failure_jump + so that each repetition makes another failure point. */ + + case on_failure_jump: + on_failure: + EXTRACT_NUMBER_AND_INCR(mcnt, p); + PUSH_FAILURE_POINT(p + mcnt, d); + break; + + /* The end of a smart repeat has a maybe_finalize_jump back. + Change it either to a finalize_jump or an ordinary jump. */ + case maybe_finalize_jump: + EXTRACT_NUMBER_AND_INCR(mcnt, p); + { + register unsigned char *p2 = p; + /* Compare what follows with the beginning of the repeat. + If we can establish that there is nothing that they would + both match, we can change to finalize_jump. */ + while (p2 + 1 != pend + && (*p2 == (unsigned char)stop_memory + || *p2 == (unsigned char)start_memory)) + p2 += 2; /* Skip over reg number. */ + if (p2 == pend) + p[-3] = (unsigned char)finalize_jump; + else if (*p2 == (unsigned char)exactn + || *p2 == (unsigned char)endline) + { + register int c = *p2 == (unsigned char)endline ? '\n' : p2[2]; + register unsigned char *p1 = p + mcnt; + /* p1[0] ... p1[2] are an on_failure_jump. + Examine what follows that. */ + if (p1[3] == (unsigned char)exactn && p1[5] != c) + p[-3] = (unsigned char)finalize_jump; + else if (p1[3] == (unsigned char)charset + || p1[3] == (unsigned char)charset_not) { + int not; + if (ismbchar(c)) + c = c << 8 | p2[3]; + /* `is_in_list()' is TRUE if c would match */ + /* That means it is not safe to finalize. */ + not = is_in_list(c, p1 + 4); + if (p1[3] == (unsigned char)charset_not) + not = !not; + if (!not) + p[-3] = (unsigned char)finalize_jump; + } + } + } + p -= 2; /* Point at relative address again. */ + if (p[-1] != (unsigned char)finalize_jump) + { + p[-1] = (unsigned char)jump; + goto nofinalize; + } + /* Note fall through. */ + + /* The end of a stupid repeat has a finalize_jump back to the + start, where another failure point will be made which will + point to after all the repetitions found so far. */ + + /* Take off failure points put on by matching on_failure_jump + because didn't fail. Also remove the register information + put on by the on_failure_jump. */ + case finalize_jump: + POP_FAILURE_POINT(); + /* Note fall through. */ + + /* Jump without taking off any failure points. */ + case jump: + nofinalize: + EXTRACT_NUMBER_AND_INCR(mcnt, p); + p += mcnt; + break; + + case dummy_failure_jump: + /* Normally, the on_failure_jump pushes a failure point, which + then gets popped at finalize_jump. We will end up at + finalize_jump, also, and with a pattern of, say, `a+', we + are skipping over the on_failure_jump, so we have to push + something meaningless for finalize_jump to pop. */ + PUSH_FAILURE_POINT(0, 0); + goto nofinalize; + + + /* Have to succeed matching what follows at least n times. Then + just handle like an on_failure_jump. */ + case succeed_n: + EXTRACT_NUMBER(mcnt, p + 2); + /* Originally, this is how many times we HAVE to succeed. */ + if (mcnt) + { + mcnt--; + p += 2; + STORE_NUMBER_AND_INCR(p, mcnt); + } + else if (mcnt == 0) + { + p[2] = unused; + p[3] = unused; + goto on_failure; + } + else + { + fprintf(stderr, "regex: the succeed_n's n is not set.\n"); + exit(1); + } + break; + + case jump_n: + EXTRACT_NUMBER(mcnt, p + 2); + /* Originally, this is how many times we CAN jump. */ + if (mcnt) + { + mcnt--; + STORE_NUMBER(p + 2, mcnt); + goto nofinalize; /* Do the jump without taking off + any failure points. */ + } + /* If don't have to jump any more, skip over the rest of command. */ + else + p += 4; + break; + + case set_number_at: + { + register unsigned char *p1; + + EXTRACT_NUMBER_AND_INCR(mcnt, p); + p1 = p + mcnt; + EXTRACT_NUMBER_AND_INCR(mcnt, p); + STORE_NUMBER(p1, mcnt); + break; + } + + /* Ignore these. Used to ignore the n of succeed_n's which + currently have n == 0. */ + case unused: + break; + + case wordbound: + if (AT_WORD_BOUNDARY) + break; + goto fail; + + case notwordbound: + if (AT_WORD_BOUNDARY) + goto fail; + break; + + case wordchar: + PREFETCH; + if (!IS_A_LETTER(d)) + goto fail; + d++; + SET_REGS_MATCHED; + break; + + case notwordchar: + PREFETCH; + if (IS_A_LETTER(d)) + goto fail; + if (ismbchar(*d) && d + 1 != dend) + d++; + d++; + SET_REGS_MATCHED; + break; + + case exactn: + /* Match the next few pattern characters exactly. + mcnt is how many characters to match. */ + mcnt = *p++; + /* This is written out as an if-else so we don't waste time + testing `translate' inside the loop. */ + if (translate) + { + do + { + unsigned char c; + + PREFETCH; + c = *d++; + if (*p == 0xff) { + p++; + if (!--mcnt + || d == dend + || (unsigned char)*d++ != (unsigned char)*p++) + goto fail; + continue; + } + else if (ismbchar(c)) { + if (c != (unsigned char)*p++ + || !--mcnt /* redundant check if pattern was + compiled properly. */ + || d == dend + || (unsigned char)*d++ != (unsigned char)*p++) + goto fail; + continue; + } + /* compiled code translation needed for ruby */ + if ((unsigned char)translate[c] + != (unsigned char)translate[*p++]) + goto fail; + } + while (--mcnt); + } + else + { + do + { + PREFETCH; + if (*p == 0xff) {p++; mcnt--;} + if (*d++ != *p++) goto fail; + } + while (--mcnt); + } + SET_REGS_MATCHED; + break; + } + continue; /* Successfully executed one pattern command; keep going. */ + + /* Jump here if any matching operation fails. */ + fail: + if (stackp != stackb) + /* A restart point is known. Restart there and pop it. */ + { + short last_used_reg, this_reg; + + /* If this failure point is from a dummy_failure_point, just + skip it. */ + if (!stackp[-2]) + { + POP_FAILURE_POINT(); + goto fail; + } + + d = *--stackp; + p = *--stackp; + /* Restore register info. */ + last_used_reg = (long) *--stackp; + + /* Make the ones that weren't saved -1 or 0 again. */ + for (this_reg = num_regs - 1; this_reg > last_used_reg; this_reg--) + { + regend[this_reg] = (unsigned char *)(-1L); + regstart[this_reg] = (unsigned char *)(-1L); + IS_ACTIVE(reg_info[this_reg]) = 0; + MATCHED_SOMETHING(reg_info[this_reg]) = 0; + } + + /* And restore the rest from the stack. */ + for ( ; this_reg > 0; this_reg--) + { + reg_info[this_reg] = *(struct register_info *) *--stackp; + regend[this_reg] = *--stackp; + regstart[this_reg] = *--stackp; + } + } + else + break; /* Matching at this starting point really fails. */ + } + + if (best_regs_set) + goto restore_best_regs; + + FREE_AND_RETURN(stackb,(-1)); /* Failure to match. */ +} + + +static int +memcmp_translate(s1, s2, len, translate) + unsigned char *s1, *s2; + register int len; + unsigned char *translate; +{ + register unsigned char *p1 = s1, *p2 = s2, c; + while (len) + { + c = *p1++; + if (ismbchar(c)) { + if (c != *p2++ || !--len || *p1++ != *p2++) + return 1; + } + else + if (translate[c] != translate[*p2++]) + return 1; + len--; + } + return 0; +} + +void +re_copy_registers(regs1, regs2) + struct re_registers *regs1, *regs2; +{ + int i; + + if (regs1 == regs2) return; + if (regs1->allocated == 0) { + regs1->beg = TMALLOC(regs2->num_regs, int); + regs1->end = TMALLOC(regs2->num_regs, int); + regs1->allocated = regs2->num_regs; + } + else if (regs1->allocated < regs2->num_regs) { + TREALLOC(regs1->beg, regs2->num_regs, int); + TREALLOC(regs1->end, regs2->num_regs, int); + regs1->allocated = regs2->num_regs; + } + for (i=0; i<regs2->num_regs; i++) { + regs1->beg[i] = regs2->beg[i]; + regs1->end[i] = regs2->end[i]; + } + regs1->num_regs = regs2->num_regs; +} + +void +re_free_registers(regs) + struct re_registers *regs; +{ + if (regs->allocated == 0) return; + if (regs->beg) free(regs->beg); + if (regs->end) free(regs->end); +} diff --git a/regex.h b/regex.h new file mode 100644 index 0000000000..32b4cf10d4 --- /dev/null +++ b/regex.h @@ -0,0 +1,291 @@ +/* Definitions for data structures callers pass the regex library. + + Copyright (C) 1985, 1989-90 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* Multi-byte extension added May, 1993 by t^2 (Takahiro Tanimoto) + Last change: May 21, 1993 by t^2 */ +/* modifis for Ruby by matz@caelum.co.jp */ + +#ifndef __REGEXP_LIBRARY +#define __REGEXP_LIBRARY + +/* Define number of parens for which we record the beginnings and ends. + This affects how much space the `struct re_registers' type takes up. */ +#ifndef RE_NREGS +#define RE_NREGS 10 +#endif + +#define BYTEWIDTH 8 + + +/* Maximum number of duplicates an interval can allow. */ +#ifndef RE_DUP_MAX +#define RE_DUP_MAX ((1 << 15) - 1) +#endif + + +/* This defines the various regexp syntaxes. */ +extern long re_syntax_options; + + +/* The following bits are used in the re_syntax_options variable to choose among + alternative regexp syntaxes. */ + +/* If this bit is set, plain parentheses serve as grouping, and backslash + parentheses are needed for literal searching. + If not set, backslash-parentheses are grouping, and plain parentheses + are for literal searching. */ +#define RE_NO_BK_PARENS 1L + +/* If this bit is set, plain | serves as the `or'-operator, and \| is a + literal. + If not set, \| serves as the `or'-operator, and | is a literal. */ +#define RE_NO_BK_VBAR (1L << 1) + +/* If this bit is not set, plain + or ? serves as an operator, and \+, \? are + literals. + If set, \+, \? are operators and plain +, ? are literals. */ +#define RE_BK_PLUS_QM (1L << 2) + +/* If this bit is set, | binds tighter than ^ or $. + If not set, the contrary. */ +#define RE_TIGHT_VBAR (1L << 3) + +/* If this bit is set, then treat newline as an OR operator. + If not set, treat it as a normal character. */ +#define RE_NEWLINE_OR (1L << 4) + +/* If this bit is set, then special characters may act as normal + characters in some contexts. Specifically, this applies to: + ^ -- only special at the beginning, or after ( or |; + $ -- only special at the end, or before ) or |; + *, +, ? -- only special when not after the beginning, (, or |. + If this bit is not set, special characters (such as *, ^, and $) + always have their special meaning regardless of the surrounding + context. */ +#define RE_CONTEXT_INDEP_OPS (1L << 5) + +/* If this bit is not set, then \ before anything inside [ and ] is taken as + a real \. + If set, then such a \ escapes the following character. This is a + special case for awk. */ +#define RE_AWK_CLASS_HACK (1L << 6) + +/* If this bit is set, then \{ and \} or { and } serve as interval operators. + If not set, then \{ and \} and { and } are treated as literals. */ +#define RE_INTERVALS (1L << 7) + +/* If this bit is not set, then \{ and \} serve as interval operators and + { and } are literals. + If set, then { and } serve as interval operators and \{ and \} are + literals. */ +#define RE_NO_BK_CURLY_BRACES (1L << 8) +#define RE_NO_BK_BRACES RE_NO_BK_CURLY_BRACES + +/* If this bit is set, then character classes are supported; they are: + [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:], + [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:]. + If not set, then character classes are not supported. */ +#define RE_CHAR_CLASSES (1L << 9) + +/* If this bit is set, then the dot re doesn't match a null byte. + If not set, it does. */ +#define RE_DOT_NOT_NULL (1L << 10) + +/* If this bit is set, then [^...] doesn't match a newline. + If not set, it does. */ +#define RE_HAT_NOT_NEWLINE (1L << 11) + +/* If this bit is set, back references are recognized. + If not set, they aren't. */ +#define RE_NO_BK_REFS (1L << 12) + +/* If this bit is set, back references must refer to a preceding + subexpression. If not set, a back reference to a nonexistent + subexpression is treated as literal characters. */ +#define RE_NO_EMPTY_BK_REF (1L << 13) + +/* If this bit is set, bracket expressions can't be empty. + If it is set, they can be empty. */ +#define RE_NO_EMPTY_BRACKETS (1L << 14) + +/* If this bit is set, then *, +, ? and { cannot be first in an re or + immediately after a |, or a (. Furthermore, a | cannot be first or + last in an re, or immediately follow another | or a (. Also, a ^ + cannot appear in a nonleading position and a $ cannot appear in a + nontrailing position (outside of bracket expressions, that is). */ +#define RE_CONTEXTUAL_INVALID_OPS (1L << 15) + +/* If this bit is set, then +, ? and | aren't recognized as operators. + If it's not, they are. */ +#define RE_LIMITED_OPS (1L << 16) + +/* If this bit is set, then an ending range point has to collate higher + or equal to the starting range point. + If it's not set, then when the ending range point collates higher + than the starting range point, the range is just considered empty. */ +#define RE_NO_EMPTY_RANGES (1L << 17) + +/* If this bit is set, then a hyphen (-) can't be an ending range point. + If it isn't, then it can. */ +#define RE_NO_HYPHEN_RANGE_END (1L << 18) + +/* If this bit is not set, then \ inside a bracket expression is literal. + If set, then such a \ quotes the following character. */ +#define RE_BACKSLASH_ESCAPE_IN_LISTS (1L << 19) + +/* Define combinations of bits for the standard possibilities. */ +#define RE_SYNTAX_POSIX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_CONTEXT_INDEP_OPS) +#define RE_SYNTAX_AWK (RE_NO_BK_PARENS | RE_NO_BK_VBAR | RE_AWK_CLASS_HACK) +#define RE_SYNTAX_EGREP (RE_NO_BK_PARENS | RE_NO_BK_VBAR \ + | RE_CONTEXT_INDEP_OPS | RE_NEWLINE_OR) +#define RE_SYNTAX_GREP (RE_BK_PLUS_QM | RE_NEWLINE_OR) +#define RE_SYNTAX_EMACS 0 +#define RE_SYNTAX_POSIX_BASIC (RE_INTERVALS | RE_BK_PLUS_QM \ + | RE_CHAR_CLASSES | RE_DOT_NOT_NULL \ + | RE_HAT_NOT_NEWLINE | RE_NO_EMPTY_BK_REF \ + | RE_NO_EMPTY_BRACKETS | RE_LIMITED_OPS \ + | RE_NO_EMPTY_RANGES | RE_NO_HYPHEN_RANGE_END) + +#define RE_SYNTAX_POSIX_EXTENDED (RE_INTERVALS | RE_NO_BK_CURLY_BRACES \ + | RE_NO_BK_VBAR | RE_NO_BK_PARENS \ + | RE_HAT_NOT_NEWLINE | RE_CHAR_CLASSES \ + | RE_NO_EMPTY_BRACKETS | RE_CONTEXTUAL_INVALID_OPS \ + | RE_NO_BK_REFS | RE_NO_EMPTY_RANGES \ + | RE_NO_HYPHEN_RANGE_END) + +/* For multi-byte char support */ +#define RE_MBCTYPE_EUC (1L << 20) +#define RE_MBCTYPE_SJIS (1L << 21) +#define RE_MBCTYPE_MASK (RE_MBCTYPE_EUC | RE_MBCTYPE_SJIS) + +#ifdef EUC +#define DEFAULT_MBCTYPE RE_MBCTYPE_EUC +#else +#ifdef SJIS +#define DEFAULT_MBCTYPE RE_MBCTYPE_SJIS +#else +#define DEFAULT_MBCTYPE 0 +#endif +#endif + +#undef ismbchar +#define ismbchar(c) \ + (re_syntax_options & RE_MBCTYPE_EUC \ + ? ((unsigned char) (c) >= 0x80) \ + : (re_syntax_options & RE_MBCTYPE_SJIS \ + ? (( 0x80 <= (unsigned char) (c) \ + && (unsigned char) (c) <= 0x9f) \ + || (0xe0 <= (unsigned char) (c))) \ + : 0)) + +/* This data structure is used to represent a compiled pattern. */ + +struct re_pattern_buffer + { + char *buffer; /* Space holding the compiled pattern commands. */ + long allocated; /* Size of space that `buffer' points to. */ + long used; /* Length of portion of buffer actually occupied */ + char *fastmap; /* Pointer to fastmap, if any, or zero if none. */ + /* re_search uses the fastmap, if there is one, + to skip over totally implausible characters. */ + char *translate; /* Translate table to apply to all characters before + comparing, or zero for no translation. + The translation is applied to a pattern when it is + compiled and to data when it is matched. */ + + + long re_nsub; /* Number of subexpressions found by the compiler. */ + char fastmap_accurate; + /* Set to zero when a new pattern is stored, + set to one when the fastmap is updated from it. */ + char can_be_null; /* Set to one by compiling fastmap + if this pattern might match the null string. + It does not necessarily match the null string + in that case, but if this is zero, it cannot. + 2 as value means can match null string + but at end of range or before a character + listed in the fastmap. */ + }; + + +/* search.c (search_buffer) needs this one value. It is defined both in + regex.c and here. */ +#define RE_EXACTN_VALUE 1 + + +/* Structure to store register contents data in. + + Pass the address of such a structure as an argument to re_match, etc., + if you want this information back. + + For i from 1 to RE_NREGS - 1, start[i] records the starting index in + the string of where the ith subexpression matched, and end[i] records + one after the ending index. start[0] and end[0] are analogous, for + the entire pattern. */ + +struct re_registers + { + unsigned allocated; + unsigned num_regs; + int *beg; + int *end; + }; + + + +#ifdef NeXT +#define re_match rre_match +#endif + +#ifdef __STDC__ + +extern char *re_compile_pattern (char *, size_t, struct re_pattern_buffer *); +/* Is this really advertised? */ +extern void re_compile_fastmap (struct re_pattern_buffer *); +extern int re_search (struct re_pattern_buffer *, char*, int, int, int, + struct re_registers *); +extern int re_match (struct re_pattern_buffer *, char *, int, int, + struct re_registers *); +extern long re_set_syntax (long syntax); +extern void re_copy_registers (struct re_registers*, struct re_registers*); + +#ifndef RUBY +/* 4.2 bsd compatibility. */ +extern char *re_comp (char *); +extern int re_exec (char *); +#endif + +#else /* !__STDC__ */ + +extern char *re_compile_pattern (); +/* Is this really advertised? */ +extern void re_compile_fastmap (); +extern int re_search (); +extern int re_match (); +extern long re_set_syntax(); +extern void re_copy_registers (); +extern void re_free_registers (); + +#endif /* __STDC__ */ + + +#ifdef SYNTAX_TABLE +extern char *re_syntax_table; +#endif + +#endif /* !__REGEXP_LIBRARY */ diff --git a/ruby.1 b/ruby.1 new file mode 100644 index 0000000000..fe1e76007a --- /dev/null +++ b/ruby.1 @@ -0,0 +1,329 @@ +.\"Ruby is copyrighted by Yukihiro Matsumoto <matz@ruby.club.co.jp>. +.\" +.\"This source is distributed under the conditions blow: +.\" +.\" 1. You may make and give away verbatim copies of the source form of +.\" the software without restriction, provided that you do not modify +.\" the original distribution files. +.\" +.\" If you want to distribute the modified version in any way, contact +.\" the author. +.\" +.\" 2. You may distribute the software in object code or executable +.\" form, provided that you distribute it with instructions on where +.\" to get the software. +.\" +.\" 3. You may modify the software in any way, provided that you do not +.\" distribute the modified version. +.\" +.\" 4. You may modify and include the part of the software into any other +.\" software (possibly commercial). But some files in the distribution +.\" are not written by the author, so that they are not under this terms. +.\" They are gc.c(partly)$B!$(Butils.c(partly), regex.[ch]$B!$(Bfnmatch.[ch]$B!$(B +.\" glob.c, st.[ch] and somme files under the ./missing directory. See +.\" each files for the copying condition. +.\" +.\" 5. The scripts and library files supplied as input to or produced as +.\" output from the software do not automatically fall under the +.\" copyright of the software, but belong to whomever generated them, +.\" and may be sold commercially, and may be aggregated with this +.\" software. +.\" +.\" 6. 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. +.\" +.\" $Id$ +.\" +.na +.TH RUBY 1 "ruby 1.0" "19/Sep/97" "Ruby Programmers Reference Guide" +.SH NAME +ruby - Interpreted scripting language +.SH SYNOPSIS +.B ruby \c + [ \c +.BI -version \c + ] [ \c +.BI -c \c + ] [ \c +.BI -w \c + ] [ \c +.BI -d \c + ] [ \c +.BI -l \c + ] + [ \c +.BI -p \c + ] [ \c +.BI -n \c + ] [ \c +.BI -a \c + ] [ \c +.BI -s \c + ] [ \c +.BI -0 "[digit]"\c + ] + [ \c +.BI -K "c"\c + ] [ \c +.BI -e "script"\c + ] [ \c +.BI -F "pattern"\c + ] + [ \c +.BI -i "[extension]"\c + ] [ \c +.BI -I "dir"\c + ] [ \c +.BI -r "filename"\c + ] + [ \c +.BI -S \c + ] [ \c +.BI -v \c + ] [ \c +.BI -x "[dir]"\c + ] [ \c +.BI -X "[dir]"\c + ] [ \c +.BI -y \c +] + [ \c +.BI -- \c + ] [ programfile ] [ argument ] ... + +.SH PREFACE +Ruby is the interpreted scripting language for quick and easy +object-oriented programming. It has many features to process text +files and to do system management tasks (as in perl). It is simple, +straight-forward, and extensible. +.PP +If you want a language for easy object-oriented programming, or you +don't like the PERL ugliness, or you do like the concept of lisp, but +don't like too much parentheses, ruby may be the language of the +choice. +.SH DESCRIPTION +Ruby's features are as follows: +.TP +.B "\(bu Interpretive" +Ruby is the interpreted language, so you don't have to +recompile to execute the program written in ruby. +.TP +.B "\(bu Variables have no type (dynamic typing)" +Variables in ruby can contain data of any type. You don't have +to worry about variable typing. Consequently, it has weaker +compile time check. +.TP +.B "\(bu No declaration needed" +You can use variables in your ruby programs without any +declarations. Variable name itself denotes its scope (local, +global, instance, etc.) +.TP +.B "\(bu Simple syntax" +Ruby has simple syntax influenced slightly from Eiffel. +.TP +.B "\(bu No user-level memory management" +Ruby has automatic memory management. Objects no longer +referenced from anywhere are automatically collected by the +garbage collector built in the interpreter. +.TP +.B "\(bu Everything is object" +Ruby is the pure object-oriented language from the beginning. +Even basic data like integers are treated uniformly as objects. +.TP +.B "\(bu Class, inheritance, methods" +Of course, as a O-O language, ruby has basic features like +classes, inheritance, methods, etc. +.TP +.B "\(bu Singleton methods" +Ruby has the feature to define methods for certain specified +object. For example, you can define a press-button action for +certain GUI button by defining a singleton method for the +button. Or, you can make up your own prototype based object +system using singleton methods (if you want to). +.TP +.B "\(bu Mix-in by modules" +Ruby does not have the multiple inheritance intentionally. IMO, +It is the source of confusion. Instead, ruby has modules to +share the implementation across the inheritance tree. It is +often called "Mix-in." +.TP +.B "\(bu Iterators" +Ruby has iterators for loop abstraction. +.TP +.B "\(bu Closures" +In ruby, you can objectify the procedure. +.TP +.B "\(bu Text processing and regular expression" +Ruby has bunch of text processing features like in perl. +.TP +.B "\(bu Bignums" +With bu ilt-in bignums, you can calculate factorial(400), for +example. +.TP +.B "\(bu Exception handling" +As in Java(tm). +.TP +.B "\(bu Direct access to OS" +Ruby can call most of system calls on UNIX boxes. It can be +used in system programming. +.TP +.B "\(bu Dynamic loading" +You can load object files into ruby interpreter on-the-fly, on +most of UNIXes. +.PP +.SH Command line options +Ruby interpreter accepts following command-line options (switches). +Basically they are quite similar to those of Perl. +.TP +.B -0digit +specifies the input record separator ($/) as an octal number. +If no digits given, the null character is the separator. Other +switches may follow the digits. -00 turns ruby into paragraph +mode. -0777 makes ruby read whole file at once as a single +string, since there is no legal character with that value. +.TP +.B -a +turns on auto-split mode when used with -n or -p. In auto-split +mode, ruby executes +.nf +.ne 1 +\& $F = $_.split +at beginning of each loop. +.fi +.TP +.B -c +causes ruby to check the syntax of the script and exit without +executing. If there is no syntax error, ruby will print "Syntax +OK" to the standard output. +.TP +.B -Kc +specifies KANJI (Japanese character) code-set. +.TP +.B +-d --debug +turns on debug mode. $DEBUG will set TRUE. +.TP +.B -e script +specifies script from command-line. if -e switch specified, +ruby will not look for a script filename in the arguments. +.TP +.B -F regexp +specifies input field separator ($;). +.TP +.B -i extension +specifies in-place-edit mode. The extension, if specified, is +added to old filename to make a backup copy. +example: +.nf +.ne 8 +\& % echo matz > /tmp/junk +\& % cat /tmp/junk +\& matz +\& % ruby -p -i.bak -e '$_.upcase!' /tmp/junk +\& % cat /tmp/junk +\& MATZ +\& % cat /tmp/junk.bak +\& matz +.fi +.TP +.B -I directory +used to tell ruby where to load the library scripts. Directory +path will be added to the load-path variable ($:'). +.TP +.B -l +enables automatic line-ending processing, which means firstly +set $\ to the value of $/, and secondly chops every line read +using chop!. +.TP +.B -n +causes ruby to assume the following loop around your script, +which makes it iterate over filename arguments somewhat like +sed -n or awk. +.nf +.ne 3 +\& while gets +\& ... +\& end +.fi +.TP +.B -p +acts mostly same as -n switch, but print the value of variable +$_ at the each end of the loop. +example: +.nf +.ne 2 +\& % echo matz | ruby -p -e '$_.tr! "a-z", "A-Z"' +\& MATZ +.fi +.TP +.B -r filename +causes ruby to load the file using [4]require. It is useful +with switches -n or -p. +.TP +.B -s +enables some switch parsing for switches after script name but +before any filename arguments (or before a --). Any switches +found there is removed from ARGV and set the corresponding +variable in the script. +example: +.nf +.ne 3 +\& #! /usr/local/bin/ruby -s +\& # prints "true" if invoked with `-xyz' switch. +\& print "true\n" if $xyz +.fi +.TP +.B -S +makes ruby uses the PATH environment variable to search for +script, unless if its name begins with a slash. This is used to +emulate #! on machines that don't support it, in the following +manner: +.nf +.ne 2 +\& #! /usr/local/bin/ruby +\& # This line makes the next one a comment in ruby \\ +\& exec /usr/local/bin/ruby -S $0 $* +.fi +On some systems $0 does not always contain the full pathname, +so you need -S switch to tell ruby to search for the script if +necessary. +To handle embedded spaces or such, A better construct than $* +would be ${1+"$@"}, but it does not work if the script is being +interpreted by csh. +.TP +.B -v --verbose +enables verbose mode. Ruby will prints its version at the +beginning, and set the variable `$VERBOSE' to TRUE. Some +methods prints extra messages if this variable is TRUE. If this +switch is given, and no other switches present, ruby quits +after printing its version. +.TP +.B --version +prints the version of ruby executable. +.TP +.B -w +enables verbose mode without printing version message at the +beginning. It set the variable `$VERBOSE' to TRUE. +.TP +.B -x[directory] +tells ruby that the script is embedded in a message. Leading +garbage will be discarded until the first that starts with "#!" +and contains string "ruby". Any meaningful switches on that +line will applied. The end of script must be specified with +either EOF, ^D (control-D), ^Z (control-Z), or reserved word +__END__.If the directory name is specified, ruby will switch to +that directory before executing script. +.TP +.B -X directory +causes ruby to switch to the directory. +.TP +.B -y --yydebug +turns on compiler debug mode. ruby will print bunch of internal +state messages during compiling scripts. You don't have to +specify this switch, unless you are going to debug the ruby +interpreter itself. +.PP +.SH AUTHOR + Ruby is designed and implemented by Yukihiro Matsumoto <matz@ruby.club.co.jp>. diff --git a/ruby.c b/ruby.c new file mode 100644 index 0000000000..5d8a028089 --- /dev/null +++ b/ruby.c @@ -0,0 +1,670 @@ +/************************************************ + + ruby.c - + + $Author$ + $Date$ + created at: Tue Aug 10 12:47:31 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include "re.h" +#include "dln.h" +#include <stdio.h> +#include <ctype.h> +#include <sys/types.h> +#include <fcntl.h> + +#ifdef HAVE_STRING_H +# include <string.h> +#else +char *strchr(); +char *strrchr(); +char *strstr(); +#endif + +char *getenv(); + +static int version, copyright; + +int debug = FALSE; +int verbose = FALSE; +int tainting = FALSE; +static int sflag = FALSE; + +char *inplace = FALSE; +char *strdup(); + +extern char *sourcefile; +extern int yydebug; +extern int nerrs; + +static int xflag = FALSE; +extern VALUE RS, RS_default, ORS, FS; + +static void load_stdin(); +static void load_file _((char *, int)); +static void forbid_setid _((char *)); + +static int do_loop = FALSE, do_print = FALSE; +static int do_check = FALSE, do_line = FALSE; +static int do_split = FALSE; + +static char *script; + +#ifndef RUBY_LIB +#define RUBY_LIB "/usr/local/lib/ruby" +#endif + +#if defined(MSDOS) || defined(NT) +#define RUBY_LIB_SEP ';' +#else +#define RUBY_LIB_SEP ':' +#endif + +extern VALUE rb_load_path; +VALUE Frequire(); + +static void +addpath(path) + char *path; +{ + if (path == 0) return; + if (strchr(path, RUBY_LIB_SEP)) { + char *p, *s; + VALUE ary = ary_new(); + + p = path; + while (*p) { + while (*p == RUBY_LIB_SEP) p++; + if (s = strchr(p, RUBY_LIB_SEP)) { + ary_push(ary, str_new(p, (int)(s-p))); + p = s + 1; + } + else { + ary_push(ary, str_new2(p)); + break; + } + } + rb_load_path = ary_plus(ary, rb_load_path); + } + else { + ary_unshift(rb_load_path, str_new2(path)); + } +} + +struct req_list { + char *name; + struct req_list *next; +} *req_list; + +static void +add_modules(mod) + char *mod; +{ + struct req_list *list; + + list = ALLOC(struct req_list); + list->name = mod; + list->next = req_list; + req_list = list; +} + +void +rb_require_modules() +{ + struct req_list *list = req_list; + struct req_list *tmp; + extern void *eval_tree; /* hack to save syntax tree */ + void *save; + + req_list = 0; + save = eval_tree; + while (list) { + f_require(Qnil, str_new2(list->name)); + tmp = list->next; + free(list); + list = tmp; + } + eval_tree = save; +} + +static void +proc_options(argcp, argvp) + int *argcp; + char ***argvp; +{ + int argc = *argcp; + char **argv = *argvp; + int script_given, do_search; + char *s; + + if (argc == 0) return; + + version = FALSE; + do_search = FALSE; + script_given = 0; + + for (argc--,argv++; argc > 0; argc--,argv++) { + if (argv[0][0] != '-' || !argv[0][1]) break; + + s = argv[0]+1; + reswitch: + switch (*s) { + case 'a': + do_split = TRUE; + s++; + goto reswitch; + + case 'p': + do_print = TRUE; + /* through */ + case 'n': + do_loop = TRUE; + s++; + goto reswitch; + + case 'd': + debug = TRUE; + s++; + goto reswitch; + + case 'y': + yydebug = 1; + s++; + goto reswitch; + + case 'v': + show_version(); + verbose = 2; + case 'w': + verbose |= 1; + s++; + goto reswitch; + + case 'c': + do_check = TRUE; + s++; + goto reswitch; + + case 's': + forbid_setid("-s"); + sflag = TRUE; + s++; + goto reswitch; + + case 'l': + do_line = TRUE; + ORS = RS; + s++; + goto reswitch; + + case 'S': + forbid_setid("-S"); + do_search = TRUE; + s++; + goto reswitch; + + case 'e': + forbid_setid("-e"); + script_given++; + if (script == 0) script = "-e"; + if (argv[1]) { + compile_string("-e", argv[1], strlen(argv[1])); + argc--,argv++; + } + else { + compile_string("-e", "", 0); + } + break; + + case 'r': + forbid_setid("-r"); + if (*++s) { + add_modules(s); + } + else if (argv[1]) { + add_modules(argv[1]); + argc--,argv++; + } + break; + + case 'i': + forbid_setid("-i"); + if (inplace) free(inplace); + inplace = strdup(s+1); + break; + + case 'x': + xflag = TRUE; + s++; + if (*s && chdir(s) < 0) { + Fatal("Can't chdir to %s", s); + } + break; + + case 'X': + s++; + if (!*s) { + s = argv[1]; + argc--,argv++; + } + if (*s && chdir(s) < 0) { + Fatal("Can't chdir to %s", s); + } + break; + + case 'F': + FS = str_new2(s+1); + break; + + case 'K': + s++; + rb_set_kcode(s); + s++; + goto reswitch; + + case 'T': + { + int numlen; + int v = 1; + + if (*++s) { + v = scan_oct(s, 2, &numlen); + if (numlen == 0) v = 1; + } + rb_set_safe_level(v); + tainting = TRUE; + } + break; + + case 'I': + forbid_setid("-I"); + if (*++s) + addpath(s); + else if (argv[1]) { + addpath(argv[1]); + argc--,argv++; + } + break; + + case '0': + { + int numlen; + int v; + char c; + + v = scan_oct(s, 4, &numlen); + s += numlen; + if (v > 0377) RS = Qnil; + else if (v == 0 && numlen >= 2) { + RS = str_new2("\n\n"); + } + else { + c = v & 0xff; + RS = str_new(&c, 1); + } + } + goto reswitch; + + case '-': + if (!s[1]) { + argc--,argv++; + goto switch_end; + } + s++; + if (strcmp("copyright", s) == 0) + copyright = 1; + else if (strcmp("debug", s) == 0) + debug = 1; + else if (strcmp("version", s) == 0) + version = 1; + else if (strcmp("verbose", s) == 0) + verbose = 2; + else if (strcmp("yydebug", s) == 0) + yydebug = 1; + else { + Fatal("Unrecognized long option: --%s",s); + } + break; + + default: + Fatal("Unrecognized switch: -%s",s); + + case 0: + break; + } + } + + switch_end: + if (*argvp[0] == 0) return; + + if (version) { + show_version(); + exit(0); + } + if (copyright) { + show_copyright(); + } + + if (script_given == FALSE) { + if (argc == 0) { /* no more args */ + if (verbose == 3) exit(0); + script = "-"; + load_stdin(); + } + else { + script = argv[0]; + if (script[0] == '\0') { + script = "-"; + load_stdin(); + } + else { + if (do_search) { + char *path = getenv("RUBYPATH"); + + if (path) { + script = dln_find_file(script, path); + } + if (!script) { + script = dln_find_file(script, getenv("PATH")); + } + if (!script) script = argv[0]; + } + load_file(script, 1); + } + argc--; argv++; + } + } + if (verbose) verbose = TRUE; + + xflag = FALSE; + *argvp = argv; + *argcp = argc; + + if (sflag) { + char *s; + + argc = *argcp; argv = *argvp; + for (; argc > 0 && argv[0][0] == '-'; argc--,argv++) { + if (argv[0][1] == '-') { + argc--,argv++; + break; + } + argv[0][0] = '$'; + if (s = strchr(argv[0], '=')) { + *s++ = '\0'; + rb_gvar_set2(argv[0], str_new2(s)); + } + else { + rb_gvar_set2(argv[0], TRUE); + } + } + *argcp = argc; *argvp = argv; + } + +} + +static void +load_file(fname, script) + char *fname; + int script; +{ + extern VALUE rb_stdin; + VALUE f; + int line_start = 1; + + if (strcmp(fname, "-") == 0) { + f = rb_stdin; + } + else { + f = file_open(fname, "r"); + } + + if (script) { + VALUE c; + VALUE line; + VALUE rs = RS; + + RS = RS_default; + if (xflag) { + forbid_setid("-x"); + xflag = FALSE; + while (!NIL_P(line = io_gets(f))) { + line_start++; + if (RSTRING(line)->len > 2 + && RSTRING(line)->ptr[0] == '#' + && RSTRING(line)->ptr[1] == '!') { + if (strstr(RSTRING(line)->ptr, "ruby")) { + goto start_read; + } + } + } + RS = rs; + LoadError("No Ruby script found in input"); + } + + c = io_getc(f); + if (c == INT2FIX('#')) { + line = io_gets(f); + line_start++; + + if (RSTRING(line)->len > 2 + && RSTRING(line)->ptr[0] == '!') { + + char *p; + + start_read: + if (p = strstr(RSTRING(line)->ptr, "ruby -")) { + int argc; char *argv[2]; char **argvp = argv; + UCHAR *s; + + s = RSTRING(line)->ptr; + while (isspace(*s++)) + ; + *s = '\0'; + RSTRING(line)->ptr[RSTRING(line)->len-1] = '\0'; + if (RSTRING(line)->ptr[RSTRING(line)->len-2] == '\r') + RSTRING(line)->ptr[RSTRING(line)->len-2] = '\0'; + argc = 2; argv[0] = 0; argv[1] = p + 5; + proc_options(&argc, &argvp); + } + } + } + else if (!NIL_P(c)) { + io_ungetc(f, c); + } + RS = rs; + } + compile_file(fname, f, line_start); + if (f != rb_stdin) io_close(f); +} + +void +rb_load_file(fname) + char *fname; +{ + load_file(fname, 0); +} + +static void +load_stdin() +{ + forbid_setid("program input from stdin"); + load_file("-", 1); +} + +VALUE Progname; +VALUE Argv; + +static int origargc; +static char **origargv; + +static void +set_arg0(val, id) + VALUE val; + ID id; +{ + char *s; + int i; + static int len; + + if (origargv == 0) Fail("$0 not initialized"); + Check_Type(val, T_STRING); + if (len == 0) { + s = origargv[0]; + s += strlen(s); + /* See if all the arguments are contiguous in memory */ + for (i = 1; i < origargc; i++) { + if (origargv[i] == s + 1) + s += strlen(++s); /* this one is ok too */ + } + len = s - origargv[0]; + } + s = RSTRING(val)->ptr; + i = RSTRING(val)->len; + if (i > len) { + memcpy(origargv[0], s, len); + origargv[0][len] = '\0'; + } + else { + memcpy(origargv[0], s, i); + s = origargv[0]+i; + *s++ = '\0'; + while (++i < len) + *s++ = ' '; + } + Progname = str_taint(str_new2(origargv[0])); +} + +void +ruby_script(name) + char *name; +{ + if (name) { + Progname = str_taint(str_new2(name)); + sourcefile = name; + } +} + +static int uid, euid, gid, egid; + +static void +init_ids() +{ + uid = (int)getuid(); + euid = (int)geteuid(); + gid = (int)getgid(); + egid = (int)getegid(); +#ifdef VMS + uid |= gid << 16; + euid |= egid << 16; +#endif + if (uid && (euid != uid || egid != gid)) { + rb_set_safe_level(1); + } +} + +static void +forbid_setid(s) + char *s; +{ + if (euid != uid) + Fatal("No %s allowed while running setuid", s); + if (egid != gid) + Fatal("No %s allowed while running setgid", s); +} + +void +ruby_prog_init() +{ + init_ids(); + + sourcefile = "ruby"; + rb_define_variable("$VERBOSE", &verbose); + rb_define_variable("$-v", &verbose); + rb_define_variable("$DEBUG", &debug); + rb_define_variable("$-d", &debug); + rb_define_readonly_variable("$-p", &do_print); + rb_define_readonly_variable("$-l", &do_line); + + if (rb_safe_level() == 0) { + addpath(getenv("RUBYLIB")); + } + +#ifdef RUBY_THIN_ARCHLIB + addpath(RUBY_THIN_ARCHLIB); +#endif + +#ifdef RUBY_ARCHLIB + addpath(RUBY_ARCHLIB); +#endif + addpath(RUBY_LIB); + if (rb_safe_level() == 0) { + addpath("."); + } + + rb_define_hooked_variable("$0", &Progname, 0, set_arg0); + + Argv = ary_new(); + rb_define_readonly_variable("$*", &Argv); + rb_define_global_const("ARGV", Argv); + rb_define_readonly_variable("$-a", &do_split); + +#ifdef MSDOS + /* + * There is no way we can refer to them from ruby, so close them to save + * space. + */ + (void)fclose(stdaux); + (void)fclose(stdprn); +#endif +} + +void +ruby_set_argv(argc, argv) + int argc; + char **argv; +{ + int i; + +#if defined(USE_DLN_A_OUT) + if (origargv) dln_argv0 = origargv[0]; + else dln_argv0 = argv[0]; +#endif + for (i=0; i < argc; i++) { + ary_push(Argv, str_taint(str_new2(argv[i]))); + } +} + +void +ruby_process_options(argc, argv) + int argc; + char **argv; +{ + extern VALUE errat; + int i; + + origargc = argc; origargv = argv; + ruby_script(argv[0]); /* for the time being */ +#if defined(USE_DLN_A_OUT) + dln_argv0 = argv[0]; +#endif + proc_options(&argc, &argv); + ruby_script(script); + ruby_set_argv(argc, argv); + + if (do_check && nerrs == 0) { + printf("Syntax OK\n"); + exit(0); + } + if (do_print) { + yyappend_print(); + } + if (do_loop) { + yywhile_loop(do_line, do_split); + } +} diff --git a/ruby.h b/ruby.h new file mode 100644 index 0000000000..b477c9e775 --- /dev/null +++ b/ruby.h @@ -0,0 +1,380 @@ +/************************************************ + + ruby.h - + + $Author$ + $Date$ + created at: Thu Jun 10 14:26:32 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +*************************************************/ + +#ifndef RUBY_H +#define RUBY_H + +#include "config.h" + +#include "defines.h" + +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#endif + +#ifndef __STDC__ +# define volatile +# ifdef __GNUC__ +# define const __const__ +# else +# define const +# endif +# define _(args) () +#else +# define _(args) args +#endif + +#if defined(HAVE_ALLOCA_H) && !defined(__GNUC__) +#include <alloca.h> +#endif + +#ifdef _AIX +#pragma alloca +#endif + +typedef unsigned int UINT; +typedef UINT VALUE; +typedef UINT ID; + +typedef unsigned short USHORT; +typedef unsigned char UCHAR; + +#ifdef __STDC__ +# include <limits.h> +#else +# ifndef LONG_MAX +# ifdef HAVE_LIMITS_H +# include <limits.h> +# else +# define LONG_MAX 2147483647 /* assuming 32bit(2's compliment) LONG */ +# endif +# endif +# ifndef LONG_MIN +# if (0 != ~0) +# define LONG_MIN (-LONG_MAX-1) +# else +# define LONG_MIN (-LONG_MAX) +# endif +# endif +# ifndef CHAR_BIT +# define CHAR_BIT 8 +# endif +#endif + +#define FIXNUM_MAX (LONG_MAX>>1) +#define FIXNUM_MIN RSHIFT((long)LONG_MIN,1) + +#define FIXNUM_FLAG 0x01 +#define INT2FIX(i) (VALUE)(((int)(i))<<1 | FIXNUM_FLAG) +VALUE int2inum _((int)); +#define INT2NUM(v) int2inum(v) + +#if (-1==(((-1)<<1)&FIXNUM_FLAG)>>1) +# define RSHIFT(x,y) ((x)>>y) +#else +# define RSHIFT(x,y) (((x)<0) ? ~((~(x))>>y) : (x)>>y) +#endif +#define FIX2INT(x) RSHIFT((int)x,1) + +#define FIX2UINT(f) ((UINT)(f)>>1) +#define FIXNUM_P(f) (((int)(f))&FIXNUM_FLAG) +#define POSFIXABLE(f) ((f) <= FIXNUM_MAX) +#define NEGFIXABLE(f) ((f) >= FIXNUM_MIN) +#define FIXABLE(f) (POSFIXABLE(f) && NEGFIXABLE(f)) + +/* special contants - i.e. non-zero and non-fixnum constants */ +#undef FALSE +#define FALSE 0 +#undef TRUE +#define TRUE 2 +#define Qnil 4 + +int rb_test_false_or_nil _((VALUE)); +# define RTEST(v) rb_test_false_or_nil((VALUE)(v)) +#define NIL_P(v) ((VALUE)(v) == Qnil) + +extern VALUE cObject; + +VALUE rb_class_of _((VALUE)); +#define CLASS_OF(v) rb_class_of((VALUE)(v)) + +#define T_NIL 0x00 +#define T_OBJECT 0x01 +#define T_CLASS 0x02 +#define T_ICLASS 0x03 +#define T_MODULE 0x04 +#define T_FLOAT 0x05 +#define T_STRING 0x06 +#define T_REGEXP 0x07 +#define T_ARRAY 0x08 +#define T_FIXNUM 0x09 +#define T_HASH 0x0a +#define T_STRUCT 0x0b +#define T_BIGNUM 0x0c +#define T_FILE 0x0d + +#define T_TRUE 0x20 +#define T_FALSE 0x21 +#define T_DATA 0x22 +#define T_MATCH 0x23 + +#define T_VARMAP 0xfd +#define T_SCOPE 0xfe +#define T_NODE 0xff + +#define T_MASK 0xff + +#define BUILTIN_TYPE(x) (((struct RBasic*)(x))->flags & T_MASK) + +int rb_type _((VALUE)); +#define TYPE(x) rb_type((VALUE)(x)) + +void rb_check_type _((VALUE,int)); +#define Check_Type(v,t) rb_check_type((VALUE)(v),t) +void rb_check_safe_str _((VALUE)); +#define Check_SafeStr(v) rb_check_safe_str((VALUE)(v)) +void rb_secure _((int)); + +#define NUM2INT(x) (FIXNUM_P(x)?FIX2INT(x):num2int(x)) +VALUE num2fix _((VALUE)); +int num2int _((VALUE)); + +#define NEWOBJ(obj,type) type *obj = (type*)rb_newobj() +#define OBJSETUP(obj,c,t) {\ + RBASIC(obj)->class = (c);\ + RBASIC(obj)->flags = (t);\ +} +#define CLONESETUP(obj1,obj2) \ + OBJSETUP(obj1,RBASIC(obj2)->class,RBASIC(obj2)->flags); + +struct RBasic { + UINT flags; + VALUE class; +}; + +struct RObject { + struct RBasic basic; + struct st_table *iv_tbl; +}; + +struct RClass { + struct RBasic basic; + struct st_table *iv_tbl; + struct st_table *m_tbl; + struct RClass *super; +}; + +struct RFloat { + struct RBasic basic; + double value; +}; + +struct RString { + struct RBasic basic; + UINT len; + UCHAR *ptr; + struct RString *orig; +}; + +struct RArray { + struct RBasic basic; + UINT len, capa; + VALUE *ptr; +}; + +struct RRegexp { + struct RBasic basic; + struct re_pattern_buffer *ptr; + UINT len; + UCHAR *str; +}; + +struct RHash { + struct RBasic basic; + struct st_table *tbl; + int iter_lev; + UINT status; +}; + +struct RFile { + struct RBasic basic; + struct OpenFile *fptr; +}; + +struct RData { + struct RBasic basic; + void (*dmark)(); + void (*dfree)(); + void *data; +}; + +#define DATA_PTR(dta) (RDATA(dta)->data) + +VALUE data_object_alloc _((VALUE,void*,void (*)(),void (*)())); +#define Data_Make_Struct(class,type,mark,free,sval) (\ + sval = ALLOC(type),\ + memset(sval, 0, sizeof(type)),\ + data_object_alloc(class,sval,mark,free)\ +) + +#define Data_Wrap_Struct(class,mark,free,sval) (\ + data_object_alloc(class,sval,mark,free)\ +) + +#define Data_Get_Struct(obj,type,sval) {\ + Check_Type(obj, T_DATA); \ + sval = (type*)DATA_PTR(obj);\ +} + +struct RStruct { + struct RBasic basic; + UINT len; + VALUE *ptr; +}; + +struct RBignum { + struct RBasic basic; + char sign; + UINT len; + USHORT *digits; +}; + +#define R_CAST(st) (struct st*) +#define RBASIC(obj) (R_CAST(RBasic)(obj)) +#define ROBJECT(obj) (R_CAST(RObject)(obj)) +#define RCLASS(obj) (R_CAST(RClass)(obj)) +#define RFLOAT(obj) (R_CAST(RFloat)(obj)) +#define RSTRING(obj) (R_CAST(RString)(obj)) +#define RREGEXP(obj) (R_CAST(RRegexp)(obj)) +#define RARRAY(obj) (R_CAST(RArray)(obj)) +#define RHASH(obj) (R_CAST(RHash)(obj)) +#define RDATA(obj) (R_CAST(RData)(obj)) +#define RSTRUCT(obj) (R_CAST(RStruct)(obj)) +#define RBIGNUM(obj) (R_CAST(RBignum)(obj)) +#define RFILE(obj) (R_CAST(RFile)(obj)) + +#define FL_SINGLETON (1<<8) +#define FL_MARK (1<<9) +#define FL_FINALIZE (1<<10) + +#define FL_USHIFT 11 + +#define FL_USER0 (1<<(FL_USHIFT+0)) +#define FL_USER1 (1<<(FL_USHIFT+1)) +#define FL_USER2 (1<<(FL_USHIFT+2)) +#define FL_USER3 (1<<(FL_USHIFT+3)) +#define FL_USER4 (1<<(FL_USHIFT+4)) +#define FL_USER5 (1<<(FL_USHIFT+5)) +#define FL_USER6 (1<<(FL_USHIFT+6)) + +#define FL_UMASK (0x7f<<FL_USHIFT) + +int rb_special_const_p _((VALUE)); +#define FL_ABLE(x) (!(FIXNUM_P(x)||rb_special_const_p((VALUE)(x)))) +#define FL_TEST(x,f) (FL_ABLE(x)?(RBASIC(x)->flags&(f)):0) +#define FL_SET(x,f) if (FL_ABLE(x)) {RBASIC(x)->flags |= (f);} +#define FL_UNSET(x,f) if(FL_ABLE(x)){RBASIC(x)->flags &= ~(f);} +#define FL_REVERSE(x,f) if(FL_ABLE(x)){RBASIC(x)->flags ^= f;} + +#define ALLOC_N(type,n) (type*)xmalloc(sizeof(type)*(n)) +#define ALLOC(type) (type*)xmalloc(sizeof(type)) +#define REALLOC_N(var,type,n) (var)=(type*)xrealloc((char*)(var),sizeof(type)*(n)) + +#define ALLOCA_N(type,n) (type*)alloca(sizeof(type)*(n)) + +#define MEMZERO(p,type,n) memset((p), 0, sizeof(type)*(n)) +#define MEMCPY(p1,p2,type,n) memcpy((p1), (p2), sizeof(type)*(n)) +#define MEMMOVE(p1,p2,type,n) memmove((p1), (p2), sizeof(type)*(n)) + +void *xmalloc _((unsigned long)); +void *xcalloc _((unsigned long,unsigned long)); +void *xrealloc _((void*,unsigned long)); + +VALUE rb_define_class _((char*,VALUE)); +VALUE rb_define_module _((char*)); +void rb_include_module _((VALUE,VALUE)); +void rb_extend_object _((VALUE,VALUE)); + +void rb_define_variable _((char*,VALUE*)); +void rb_define_virtual_variable _((char*,VALUE(*)(),void(*)())); +void rb_define_hooked_variable _((char*,VALUE*,VALUE(*)(),void(*)())); +void rb_define_const _((VALUE,char*,VALUE)); +void rb_define_global_const _((char*,VALUE)); + +void rb_define_method _((VALUE,char*,VALUE(*)(),int)); +void rb_define_singleton_method _((VALUE,char*,VALUE(*)(),int)); +void rb_undef_method _((VALUE,char*)); +void rb_define_alias _((VALUE,char*,char*)); +void rb_define_attr _((VALUE,ID,int)); + +ID rb_intern _((char*)); +char *rb_id2name _((ID)); +ID rb_to_id _((VALUE)); + +char *rb_class2name _((VALUE)); +int rb_method_boundp _((VALUE,ID,int)); + +VALUE rb_eval_string _((char*)); +VALUE rb_funcall(); +int rb_scan_args(); + +VALUE rb_iv_get(); +VALUE rb_iv_set(); +void rb_const_set(); +VALUE rb_const_get(); + +VALUE rb_yield(); +int iterator_p(); + +VALUE rb_equal _((VALUE,VALUE)); + +extern int verbose, debug; + +int rb_safe_level(); +void rb_set_safe_level _((int)); + +#ifdef __GNUC__ +typedef void voidfn (); +volatile voidfn Raise; +volatile voidfn Fail; +volatile voidfn Fatal; +volatile voidfn Bug; +volatile voidfn WrongType; +volatile voidfn rb_sys_fail; +volatile voidfn rb_break; +volatile voidfn rb_exit; +volatile voidfn rb_fatal; +volatile voidfn rb_raise; +volatile voidfn rb_notimplement; +#else +void Raise(); +void Fail(); +void Fatal(); +void Bug(); +void WrongType(); +void rb_sys_fail(); +void rb_break(); +void rb_exit(); +void rb_fatal(); +void rb_raise(); +void rb_notimplement(); +#endif + +void Error(); +void Warning(); + +#if defined(EXTLIB) && defined(USE_DLN_A_OUT) +/* hook for external modules */ +static char *libs_to_be_linked[] = { EXTLIB, 0 }; +#endif + +#endif diff --git a/sample/biorhythm.rb b/sample/biorhythm.rb new file mode 100644 index 0000000000..50ad1f2ef7 --- /dev/null +++ b/sample/biorhythm.rb @@ -0,0 +1,138 @@ +#!/usr/local/bin/ruby +# +# biorhythm.rb - +# $Release Version: $ +# $Revision$ +# $Date$ +# by Yasuo OHBA(STAFS Development Room) +# +# -- +# +# +# + +include Math +require "date.rb" +require "parsearg.rb" + +def usage() + print "Usage:\n" + print "biorhythm.rb [options]\n" + print " options...\n" + print " -D YYYYMMDD(birthday) : すべて default 値を使う. \n" + print " --sdate | --date YYYYMMDD : system date もしくは指定した日付を使う.\n" + print " --birthday YYYYMMDD : 誕生日の指定をする. \n" + print " -v | -g : Values or Graph の指定. \n" + print " --days DAYS : 期間の指定をする(Graph の時のみ有効). \n" + print " --help : help\n" +end +$USAGE = 'usage' + +def printHeader(y, m, d, p, w) + print "\n>>> Biorhythm <<<\n" + printf "The birthday %04d.%02d.%02d is a %s\n", y, m, d, w + printf "Age in days: [%d]\n\n", p +end + +def getPosition(z) + pi = 3.14159265 + phys = (50.0 * (1.0 + sin((z / 23.0 - (z / 23)) * 360.0 * pi / 180.0))).to_i + emot = (50.0 * (1.0 + sin((z / 28.0 - (z / 28)) * 360.0 * pi / 180.0))).to_i + geist =(50.0 * (1.0 + sin((z / 33.0 - (z / 33)) * 360.0 * pi / 180.0))).to_i + return phys, emot, geist +end + +# +# main program +# +parseArgs(0, nil, "vg", "D:", "sdate", "date:", "birthday:", "days:") + +if $OPT_D + dd = Date.new(Time.now.strftime("%Y%m%d")) + bd = Date.new($OPT_D) + ausgabeart = "g" +else + if $OPT_birthday + bd = Date.new($OPT_birthday) + else + printf(STDERR, "Birthday (YYYYMMDD) : ") + if (si = STDIN.gets.chop) != "" + bd = Date.new(si) + end + end + if !bd + printf STDERR, "BAD Input Birthday!!\n" + exit() + end + + if $OPT_sdate + dd = Date.new(Time.now.strftime("%Y%m%d")) + elsif $OPT_date + dd = Date.new($OPT_date) + else + printf(STDERR, "Date [<RETURN> for Systemdate] (YYYYMMDD) : ") + if (si = STDIN.gets.chop) != "" + dd = Date.new(si) + end + end + if !dd + dd = Date.new(Time.now.strftime("%Y%m%d")) + end + + if $OPT_v + ausgabeart = "v" + elsif $OPT_g + ausgabeart = "g" + else + printf(STDERR, "Values for today or Graph (v/g) [default g] : ") + ausgabeart = STDIN.gets.chop + end +end +if (ausgabeart == "v") + printHeader(bd.year, bd.month, bd.day, dd.period - bd.period, bd.name_of_week) + print "\n" + + phys, emot, geist = getPosition(dd.period - bd.period) + printf "Biorhythm: %04d.%02d.%02d\n", dd.year, dd.month, dd.day + printf "Physical: %d%%\n", phys + printf "Emotional: %d%%\n", emot + printf "Mental: %d%%\n", geist + print "\n" +else + if $OPT_days + display_period = $OPT_days.to_i + elsif $OPT_D + display_period = 9 + else + printf(STDERR, "Graph for how many days [default 10] : ") + display_period = STDIN.gets.chop + if (display_period == "") + display_period = 9 + else + display_period = display_period.to_i - 1 + end + end + + printHeader(bd.year, bd.month, bd.day, dd.period - bd.period, bd.name_of_week) + print " P=physical, E=emotional, M=mental\n" + print " -------------------------+-------------------------\n" + print " Bad Condition | Good Condition\n" + print " -------------------------+-------------------------\n" + + for z in (dd.period - bd.period)..(dd.period - bd.period + display_period) + phys, emot, geist = getPosition(z) + + printf "%04d.%02d.%02d : ", dd.year, dd.month, dd.day + p = (phys / 2.0 + 0.5).to_i + e = (emot / 2.0 + 0.5).to_i + g = (geist / 2.0 + 0.5).to_i + graph = "." * 51 + graph[25] = ?| + graph[p] = ?P + graph[e] = ?E + graph[g] = ?M + print graph, "\n" + dd = dd + 1 + end + print " -------------------------+-------------------------\n\n" +end diff --git a/sample/clnt.rb b/sample/clnt.rb new file mode 100644 index 0000000000..7998379aa2 --- /dev/null +++ b/sample/clnt.rb @@ -0,0 +1,17 @@ +# socket example - client side +# usage: ruby clnt.rb [host] port + +require "socket" + +host=(if ARGV.length == 2; ARGV.shift; else "localhost"; end) +print("Trying ", host, " ...") +STDOUT.flush +s = TCPsocket.open(host, ARGV.shift) +print(" done\n") +print("addr: ", s.addr.join(":"), "\n") +print("peer: ", s.peeraddr.join(":"), "\n") +while gets() + s.write($_) + print(s.readline) +end +s.close diff --git a/sample/dbmtest.rb b/sample/dbmtest.rb new file mode 100644 index 0000000000..c77cc2065b --- /dev/null +++ b/sample/dbmtest.rb @@ -0,0 +1,14 @@ +# ruby dbm acess +require "dbm" + +d = DBM.open("test") +keys = d.keys +if keys.length > 0 then + for k in keys; print k, "\n"; end + for v in d.values; print v, "\n"; end +else + d['foobar'] = 'FB' + d['baz'] = 'BZ' + d['quux'] = 'QX' +end + diff --git a/sample/dir.rb b/sample/dir.rb new file mode 100644 index 0000000000..2465c4d68e --- /dev/null +++ b/sample/dir.rb @@ -0,0 +1,10 @@ +# directory access +# list all files but .*/*~/*.o +dirp = Dir.open(".") +for f in dirp + $_ = f + unless (~/^\./ || ~/~$/ || ~/\.o/) + print f, "\n" + end +end +dirp.close diff --git a/sample/eval.rb b/sample/eval.rb new file mode 100644 index 0000000000..216bf8ca39 --- /dev/null +++ b/sample/eval.rb @@ -0,0 +1,42 @@ +line = '' +indent=0 +$stdout.sync = TRUE +print "ruby> " +while TRUE + l = gets + unless l + break if line == '' + else + line = line + l + if l =~ /,\s*$/ + print "ruby| " + next + end + if l =~ /^\s*(class|module|def|if|case|while|for|begin)\b[^_]/ + indent += 1 + end + if l =~ /^\s*end\b[^_]/ + indent -= 1 + end + if l =~ /\{\s*(\|.*\|)?\s*$/ + indent += 1 + end + if l =~ /^\s*\}/ + indent -= 1 + end + if indent > 0 + print "ruby| " + next + end + end + begin + print eval(line).inspect, "\n" + rescue + $! = 'exception raised' unless $! + print "ERR: ", $!, "\n" + end + break if not l + line = '' + print "ruby> " +end +print "\n" diff --git a/sample/export.rb b/sample/export.rb new file mode 100644 index 0000000000..949e5b10bf --- /dev/null +++ b/sample/export.rb @@ -0,0 +1,40 @@ +# method access permission +# output: +# foobar +# Foo + +class Foo + public :printf + def baz + print "baz\n" + end + private :baz + + def quux + print "in QUUX " + baz() + end +end + +def foobar + print "foobar\n" +end + +f = Foo.new +#Foo.private :printf +class Foo # redefines foobar's scope + public :foobar +end +f.foobar +f.printf "%s\n", Foo + +f.quux + +class Bar<Foo + def quux + super + baz() + end +end + +Bar.new.quux diff --git a/sample/exyacc.rb b/sample/exyacc.rb new file mode 100644 index 0000000000..dbe0dee710 --- /dev/null +++ b/sample/exyacc.rb @@ -0,0 +1,22 @@ +#! /usr/local/bin/ruby -Kn +# usage: exyacc.rb [yaccfiles] +# this is coverted from exyacc.pl in the camel book + +$/ = nil + +while gets() + sbeg = $_.index("\n%%") + 1 + send = $_.rindex("\n%%") + 1 + $_ = $_[sbeg, send-sbeg] + sub!(/.*\n/, "") + gsub!(/'{'/, "'\001'") + gsub!(/'}'/, "'\002'") + gsub!('\*/', "\003\003") + gsub!("/\\*[^\003]*\003\003", '') + while gsub!(/\{[^{}]*}/, ''); end + gsub!(/'\001'/, "'{'") + gsub!(/'\002'/, "'}'") + while gsub!(/^[ \t]*\n(\s)/, '\1'); end + gsub!(/([:|])[ \t\n]+(\w)/, '\1 \2') + print $_ +end diff --git a/sample/fact.rb b/sample/fact.rb new file mode 100644 index 0000000000..49678bc9d0 --- /dev/null +++ b/sample/fact.rb @@ -0,0 +1,8 @@ +def fact(n) + if n == 0 + 1 + else + n * fact(n-1) + end +end +print fact(ARGV[0].to_i), "\n" diff --git a/sample/fib.awk b/sample/fib.awk new file mode 100644 index 0000000000..7ebe8930f5 --- /dev/null +++ b/sample/fib.awk @@ -0,0 +1,5 @@ + function fib(n) { + if ( n<2 ) return n; else return fib(n-2) + fib(n-1) + } + + BEGIN { print fib(20); } diff --git a/sample/fib.pl b/sample/fib.pl new file mode 100644 index 0000000000..c5593764aa --- /dev/null +++ b/sample/fib.pl @@ -0,0 +1,10 @@ + sub fib { + local($n)=@_; + if( $n<2 ){ + return $n; + } { + return &fib($n-2)+&fib($n-1) + } + } + + print &fib(20), "\n"; diff --git a/sample/fib.rb b/sample/fib.rb new file mode 100644 index 0000000000..cde4fba082 --- /dev/null +++ b/sample/fib.rb @@ -0,0 +1,10 @@ +# calculate Fibonacci(20) +# for benchmark +def fib(n) + if n<2 + n + else + fib(n-2)+fib(n-1) + end +end +print(fib(20), "\n"); diff --git a/sample/fib.scm b/sample/fib.scm new file mode 100644 index 0000000000..8eba75bb9e --- /dev/null +++ b/sample/fib.scm @@ -0,0 +1,6 @@ +(define (fib n) + (if (< n 2) + n + (+ (fib (- n 2)) (fib (- n 1))))) + +(fib 20) diff --git a/sample/freq.rb b/sample/freq.rb new file mode 100644 index 0000000000..d951591735 --- /dev/null +++ b/sample/freq.rb @@ -0,0 +1,13 @@ +# word occurrence listing +# usege: ruby freq.rb file.. +freq = {} +while gets + while sub!(/\w+/, '') + word = $& + freq[word] +=1 + end +end + +for word in freq.keys.sort + printf("%s -- %d\n", word, freq[word]) +end diff --git a/sample/from.rb b/sample/from.rb new file mode 100644 index 0000000000..d39bb70084 --- /dev/null +++ b/sample/from.rb @@ -0,0 +1,87 @@ +#! /usr/local/bin/ruby + +require "parsedate" +require "kconv" +require "mailread" + +include ParseDate +include Kconv + +class String + + public :kconv + + def kconv(code = Kconv::EUC) + Kconv.kconv(self, code, Kconv::AUTO) + end + + def kjust(len) + len += 1 + me = self[0, len].ljust(len) + if me =~ /.$/ and $&.size == 2 + me[-2, 2] = ' ' + end + me.chop! + end + +end + +if ARGV[0] == '-w' + wait = TRUE + ARGV.shift +end + +if ARGV.length == 0 + user = ENV['USER'] +else + user = ARGV[0] +end + +[ENV['SPOOLDIR'], '/usr/spool', '/var/spool', '/usr', '/var'].each do |m| + break if File.exist? ARGV[0] = "#{m}/mail/#{user}" +end + +$outcount = 0; +def fromout(date, from, subj) + return if !date + y = m = d = 0 + y, m, d = parsedate(date) if date + if from + from.gsub! /\n/, "" + else + from = "sombody@somewhere" + end + if subj + subj.gsub! /\n/, "" + else + subj = "(nil)" + end + if ENV['LANG'] =~ /sjis/i + lang = Kconv::SJIS + else + lang = Kconv::EUC + end + from = from.kconv(lang).kjust(28) + subj = subj.kconv(lang).kjust(40) + printf "%02d/%02d/%02d [%s] %s\n",y,m,d,from,subj + $outcount += 1 +end + +for file in ARGV + next if !File.exist?(file) + f = open(file, "r") + while !f.eof? + mail = Mail.new(f) + fromout mail.header['Date'], mail.header['From'], mail.header['Subject'] + end + f.close +end + +if $outcount == 0 + print "You have no mail.\n" + sleep 2 if wait +elsif wait + system "stty cbreak -echo" + getc() + system "stty cooked echo" +end diff --git a/sample/fullpath.rb b/sample/fullpath.rb new file mode 100644 index 0000000000..ce268e20b9 --- /dev/null +++ b/sample/fullpath.rb @@ -0,0 +1,23 @@ +#! /usr/local/bin/ruby +# convert ls-lR filename into fullpath. + +if ARGV[0] =~ /-p/ + ARGV.shift + path = ARGV.shift +end + +if path == nil + path = "" +elsif path !~ /\/$/ + path += "/" +end + +while gets() + if /:$/ + path = $_.chop.chop + "/" + elsif /^total/ || /^d/ + elsif /^(.*\d )(.+)$/ + print($1, path, $2, "\n") + end +end + diff --git a/sample/getopts.test b/sample/getopts.test new file mode 100644 index 0000000000..2866bccea8 --- /dev/null +++ b/sample/getopts.test @@ -0,0 +1,36 @@ +#! /usr/local/bin/ruby + +load("parsearg.rb") + +def usage() + printf "Usage:\n" + printf "%s -d [-x x] [-y y] [--geometry geom] [--version] [string ...]\n", $0 +end + +$USAGE = 'usage' +parseArgs(0, "d&(x|y)", "dfg", "x:", "y:", "geometry:800x600", "version") +if ($OPT_d) + if $OPT_version + printf "version 1.0\n" + end + if ($OPT_x) + printf("x = %d\n", $OPT_x.to_i) + end + if ($OPT_y) + printf("y = %d\n", $OPT_y.to_i) + end + if ($OPT_geometry) + printf("geometry = %s\n", $OPT_geometry) + end + if $OPT_f + printf "f = TRUE\n" + end + if $OPT_g + printf "g = TRUE\n" + end +end + +while (ARGV.length != 0) + print "other = ", ARGV[0], "\n" + ARGV.shift +end diff --git a/sample/io.rb b/sample/io.rb new file mode 100644 index 0000000000..0b38d2112d --- /dev/null +++ b/sample/io.rb @@ -0,0 +1,44 @@ +# IO test +# usage: ruby io.rb file.. + +home = ENV["HOME"] +home.sub("m", "&&") +print(home, "\n") +print(home.reverse, "\n") + +if File.s("io.rb") + print(File.s("io.rb"), ": io.rb\n") +end + +$/="f\n" +for i in "abc\n\ndef\nghi\n" + print("tt: ", i) +end + +printf("%s:(%d)%s\n", $0, ARGV.length, ARGV[0]) +passwd = open(ARGV[0], "r") +#printf("%s", passwd.find{i|i =~ /\*/}) + +n = 1 +for i in passwd #.grep(/^\*/) + printf("%6d: %s", n, i) + n = n + 1; +end + +fp = open("|-", "r") + +if fp == nil + for i in 1..5 + print(i, "\n") + end +else + for line in fp + print(line) + end +end + +def printUsage() + if $USAGE + apply($USAGE); + end +end diff --git a/sample/less.rb b/sample/less.rb new file mode 100644 index 0000000000..8be359108f --- /dev/null +++ b/sample/less.rb @@ -0,0 +1,17 @@ +#! /usr/local/bin/ruby + +ZCAT = "/usr/local/bin/zcat" +LESS = "/usr/local/bin/less" + +FILE = ARGV.pop +OPTION = (if ARGV.length == 0; "" else ARGV.join(" "); end) + +if FILE =~ /\.(Z|gz)$/ + exec(format("%s %s | %s %s", ZCAT, FILE, LESS, OPTION)) +elsif FILE == nil + exec(format("%s %s", LESS, OPTION)) +else + print(format("%s %s %s", LESS, OPTION, FILE), "\n") + exec(format("%s %s %s", LESS, OPTION, FILE)) +end +exit() diff --git a/sample/list.rb b/sample/list.rb new file mode 100644 index 0000000000..76035e67d6 --- /dev/null +++ b/sample/list.rb @@ -0,0 +1,80 @@ +# Linked list example +class MyElem + # オブジェクト生成時に自動的に呼ばれるメソッド + def initialize(item) + # @変数はインスタンス変数(宣言は要らない) + @data = item + @succ = nil + end + + def data + @data + end + + def succ + @succ + end + + # 「obj.data = val」としたときに暗黙に呼ばれるメソッド + def succ=(new) + @succ = new + end +end + +class MyList + def add_to_list(obj) + elt = MyElem.new(obj) + if @head + @tail.succ = elt + else + @head = elt + end + @tail = elt + end + + def each + elt = @head + while elt + yield elt + elt = elt.succ + end + end + + # オブジェクトを文字列に変換するメソッド + # これを再定義するとprintでの表現が変わる + def to_s + str = "<MyList:\n"; + for elt in self + # 「str = str + elt.data.to_s + "\n"」の省略形 + str += elt.data.to_s + "\n" + end + str += ">" + str + end +end + +class Point + def initialize(x, y) + @x = x; @y = y + self + end + + def to_s + sprintf("%d@%d", @x, @y) + end +end + +# 大域変数は`$'で始まる. +$list1 = MyList.new +$list1.add_to_list(10) +$list1.add_to_list(20) +$list1.add_to_list(Point.new(2, 3)) +$list1.add_to_list(Point.new(4, 5)) +$list2 = MyList.new +$list2.add_to_list(20) +$list2.add_to_list(Point.new(4, 5)) +$list2.add_to_list($list1) + +# 曖昧でない限りメソッド呼び出しの括弧は省略できる +print "list1:\n", $list1, "\n" +print "list2:\n", $list2, "\n" diff --git a/sample/list2.rb b/sample/list2.rb new file mode 100644 index 0000000000..914cb8996e --- /dev/null +++ b/sample/list2.rb @@ -0,0 +1,16 @@ +# Linked list example -- short version +class Point + def initialize(x, y) + @x = x; @y = y + self + end + + def to_s + sprintf("%d@%d", @x, @y) + end +end + +list1 = [10, 20, Point.new(2, 3), Point.new(4, 5)] +list2 = [20, Point.new(4, 5), list1] +print("list1:\n", list1.join("\n"), "\n") +print("list2:\n", list2.join("\n"), "\n") diff --git a/sample/list3.rb b/sample/list3.rb new file mode 100644 index 0000000000..1d756fdff0 --- /dev/null +++ b/sample/list3.rb @@ -0,0 +1,18 @@ +# Linked list example -- short version +# using inspect + +class Point + def initialize(x, y) + @x = x; @y = y + self + end + + def to_s + sprintf("%d@%d", @x, @y) + end +end + +list1 = [10, 20, Point.new(2, 3), Point.new(4, 5)] +list2 = [20, Point.new(4, 5), list1] +print("list1: ", list1.inspect, "\n") +print("list2: ", list2.inspect, "\n") diff --git a/sample/mkproto.rb b/sample/mkproto.rb new file mode 100644 index 0000000000..1d9c9faccb --- /dev/null +++ b/sample/mkproto.rb @@ -0,0 +1,27 @@ +$/ = nil +while gets() + if /^((void|VALUE|int|char *\*|ID|struct [\w_]+ *\*|st_table *\*) *)?\n([\w\d_]+)\(.*\)\n\s*((.+;\n)*){/ + $_ = $' + printf "%s %s(", $2, $3 + args = [] + for arg in $4.split(/;\n\s*/) + arg.gsub! ' +', ' ' + if arg =~ /,/ + if arg =~ /(([^*]+) *\** *[\w\d_]+),/ + type = $2.strip! + args.push $1.strip! + arg = $' + else + type = "" + end + while arg.sub!(/(\** *[\w\d_]+)(,|$)/, "") + args.push type + " " + $1.strip! + end + else + args.push arg.strip! + end + end + printf "%s);\n", args.join(', ') + redo + end +end diff --git a/sample/mpart.rb b/sample/mpart.rb new file mode 100644 index 0000000000..6c40d50e18 --- /dev/null +++ b/sample/mpart.rb @@ -0,0 +1,44 @@ +#! ./ruby +# split into multi part +# usage: mpart.rb [-nnn] file.. + +lines = 1000 + +if (ARGV[0] =~ /^-(\d+)$/ ) + lines = $1.to_i; + ARGV.shift; +end + +basename = ARGV[0] +extname = "part" + +part = 1 +line = 0 + +fline = 0 +for i in ifp = open(basename) + fline = fline + 1 +end +ifp.close + +parts = fline / lines + 1 + +for i in ifp = open(basename) + if line == 0 + ofp = open(sprintf("%s.%s%02d", basename, extname, part), "w") + printf(ofp, "%s part%02d/%02d\n", basename, part, parts) + ofp.write("BEGIN--cut here--cut here\n") + end + ofp.write(i) + line = line + 1 + if line >= lines + ofp.write("END--cut here--cut here\n") + ofp.close + part = part + 1 + line = 0 + end +end +ofp.write("END--cut here--cut here\n") +ofp.close + +ifp.close diff --git a/sample/mrshtest.rb b/sample/mrshtest.rb new file mode 100644 index 0000000000..402b35ad55 --- /dev/null +++ b/sample/mrshtest.rb @@ -0,0 +1,14 @@ +require "marshal" +include Marshal +a = 25.6; +pt = Struct.new('Point', :x,:y); +x = pt.new(10, 10) +y = pt.new(20, 20) +rt = Struct.new('Rectangle', :origin,:corner); +z = rt.new(x, y) +c = Object.new +s = [a, x, z, c, c, "fff"]; +p s +d = dump(s); +p d +p load(d) diff --git a/sample/observ.rb b/sample/observ.rb new file mode 100644 index 0000000000..f7b1e73137 --- /dev/null +++ b/sample/observ.rb @@ -0,0 +1,31 @@ +#! /usr/local/bin/ruby + +require "thread" +require "observer" + +class Tick + include Observable + def initialize + Thread.start do + while TRUE + sleep 0.999 + changed + notify_observers(Time.now.strftime("%H:%M:%S")) + end + end + end +end + +class Clock + def initialize + @tick = Tick.new + @tick.add_observer(self) + end + def update(time) + print "\e[8D", time + STDOUT.flush + end +end + +clock = Clock.new +sleep diff --git a/sample/occur.pl b/sample/occur.pl new file mode 100644 index 0000000000..1f5fcf27a4 --- /dev/null +++ b/sample/occur.pl @@ -0,0 +1,9 @@ +while (<>) { + for (split(/\W+/)) { + $freq{$_}++; + } +} + +for (sort keys %freq) { + print "$_ -- $freq{$_}\n"; +} diff --git a/sample/occur.rb b/sample/occur.rb new file mode 100644 index 0000000000..2141fade38 --- /dev/null +++ b/sample/occur.rb @@ -0,0 +1,12 @@ +# word occurrence listing +# usege: ruby occur.rb file.. +freq = {} +while gets() + for word in $_.split(/\W+/) + freq[word] +=1 + end +end + +for word in freq.keys.sort + printf("%s -- %d\n", word, freq[word]) +end diff --git a/sample/occur2.rb b/sample/occur2.rb new file mode 100644 index 0000000000..c450c30b0f --- /dev/null +++ b/sample/occur2.rb @@ -0,0 +1,16 @@ +# word occurrence listing +# usege: ruby occur2.rb file.. +freq = {} +while gets() + for word in $_.split(/\W+/) + begin + freq[word] = freq[word] + 1 + rescue + freq[word] = 1 + end + end +end + +for word in freq.keys.sort + printf("%s -- %d\n", word, freq[word]) +end diff --git a/sample/philos.rb b/sample/philos.rb new file mode 100644 index 0000000000..3ccb052532 --- /dev/null +++ b/sample/philos.rb @@ -0,0 +1,54 @@ +# +# The Dining Philosophers - thread example +# +require "thread" + +srand +#srand +N=9 # number of philosophers +$forks = [] +for i in 0..N-1 + $forks[i] = Mutex.new +end +$state = "-o"*N + +def wait + sleep rand(20)/10.0 +end + +def think(n) + wait +end + +def eat(n) + wait +end + +def philosopher(n) + while TRUE + think n + $forks[n].lock + if not $forks[(n+1)%N].try_lock + $forks[n].unlock # avoid deadlock + next + end + $state[n*2] = ?|; + $state[(n+1)%N*2] = ?|; + $state[n*2+1] = ?*; + print $state, "\n" + eat(n) + $state[n*2] = ?-; + $state[(n+1)%N*2] = ?-; + $state[n*2+1] = ?o; + print $state, "\n" + $forks[n].unlock + $forks[(n+1)%N].unlock + end +end + +for i in 0..N-1 + Thread.start{philosopher(i)} + sleep 0.1 +end + +sleep diff --git a/sample/pi.rb b/sample/pi.rb new file mode 100644 index 0000000000..49067cc347 --- /dev/null +++ b/sample/pi.rb @@ -0,0 +1,18 @@ +#!/usr/local/bin/ruby + +k, a, b, a1, b1 = 2, 4, 1, 12, 4 + +while TRUE + # Next approximation + p, q, k = k*k, 2*k+1, k+1 + a, b, a1, b1 = a1, b1, p*a+q*a1, p*b+q*b1 + # Print common digits + d = a / b + d1 = a1 / b1 + while d == d1 + print d + $stdout.flush + a, a1 = 10*(a%b), 10*(a1%b1) + d, d1 = a/b, a1/b1 + end +end diff --git a/sample/rcs.awk b/sample/rcs.awk new file mode 100644 index 0000000000..08979285c9 --- /dev/null +++ b/sample/rcs.awk @@ -0,0 +1,33 @@ +BEGIN { + sw = 40.0; + dw = 78.0; + hdw = dw / 2.0; + w = 20.0; + h =1.0; + d = 0.2; + ss="abcdefghijklmnopqrstuvwxyz0123456789!#$%^&*()-=\\[];'`,./"; + rnd = srand(); +} + +{ + xr = -hdw; y = h * 1.0; maxxl = -999; + s = ""; + while (xr < hdw) { + x = xr * (1 + y) - y * w / 2; + i = (x / (1 + h) + sw /2); + c = (0 < i && i < length($0)) ? substr($0, i, 1) : "0"; + y = h - d * c; + xl = xr - w * y / (1 + y); + if (xl < -hdw || xl >= hdw || xl <= maxxl) { + t = rand() * length(ss); + c = substr(ss, t, 1); + } + else { + c = substr(s, xl + hdw, 1); + maxxl = xl; + } + s = s c; + xr = xr + 1; + } + print s; +} diff --git a/sample/rcs.dat b/sample/rcs.dat new file mode 100644 index 0000000000..61c88bff89 --- /dev/null +++ b/sample/rcs.dat @@ -0,0 +1,17 @@ +0000000000000000220000000000000000 +0000000000000111221110000000000000 +0000000000111112222111110000000000 +0000000111111112222111111110000000 +0000111111111122222211111111110000 +0111111111111222222221111111111110 +2222222222222222222222222222222222 +1122222222222222222222222222222211 +0111122222222222222222222222211110 +0011111122222222222222222211111100 +0001111111222222222222221111111000 +0000111112222222222222222111110000 +0000011122222222112222222211100000 +0000001122222221111222222211000000 +0000000122221111111111222210000000 +0000000221111111111111111220000000 +0000000000000000000000000000000000 diff --git a/sample/rcs.rb b/sample/rcs.rb new file mode 100644 index 0000000000..3f74da9ef2 --- /dev/null +++ b/sample/rcs.rb @@ -0,0 +1,49 @@ +# random dot steraogram +# usage: rcs.rb rcs.dat + +sw = 40.0 # p^[ +dw = 78.0 # Random Character Streogram +hdw = dw / 2.0 +w = 20.0 # +h =1.0 # +d = 0.2 # P +ss="abcdefghijklmnopqrstuvwxyz0123456789#!$%^&*()-=\\[];'`,./" +rnd = srand() + +while gets() +# print($_) + xr = -hdw; y = h * 1.0; maxxl = -999 + s = ""; + while xr < hdw + x = xr * (1 + y) - y * w / 2 + i = (x / (1 + h) + sw / 2) + if (1 < i && i < $_.length); + c = $_[i, 1].to_i + else + c = 0 + end + y = h - d * c + xl = xr - w * y / (1 + y); + if xl < -hdw || xl >= hdw || xl <= maxxl + tt = rand(ss.length) + c = ss[tt, 1] + else + c = s[xl + hdw, 1] + maxxl = xl + end + s += c + xr += 1 + end + print(s, "\n") +end + + + + + + + + + + + diff --git a/sample/regx.rb b/sample/regx.rb new file mode 100644 index 0000000000..aaf4b5f1ee --- /dev/null +++ b/sample/regx.rb @@ -0,0 +1,23 @@ +st = "\033[7m" +en = "\033[m" +#st = "<<" +#en = ">>" + +while TRUE + print "str> " + STDOUT.flush + input = gets + break if not input + if input != "" + str = input + str.chop! + end + print "pat> " + STDOUT.flush + re = gets + break if not re + re.chop! + str.gsub! re, "#{st}\\&#{en}" + print str, "\n" +end +print "\n" diff --git a/sample/ruby-mode.el b/sample/ruby-mode.el new file mode 100644 index 0000000000..d8165bd23a --- /dev/null +++ b/sample/ruby-mode.el @@ -0,0 +1,644 @@ +;;; +;;; ruby-mode.el - +;;; +;;; $Author$ +;;; Time-stamp: <97/03/21 01:16:05 matz> +;;; created at: Fri Feb 4 14:49:13 JST 1994 +;;; + +(defconst ruby-mode-version "1.0.7") + +(defconst ruby-block-beg-re + "class\\|module\\|def\\|if\\|unless\\|case\\|while\\|until\\|for\\|begin\\|do" + ) + +(defconst ruby-indent-beg-re + "\\(\\s *\\(class\\|module\\|def\\)\\)\\|if\\|unless\\|case\\|while\\|until\\|for\\|begin" + ) + +(defconst ruby-modifier-re + "if\\|unless\\|while\\|until" + ) + +(defconst ruby-block-mid-re + "then\\|else\\|elsif\\|when\\|rescue\\|ensure" + ) + +(defconst ruby-block-op-re + "and\\|or\\|not" + ) + +(defconst ruby-block-end-re "end") + +(defconst ruby-delimiter + (concat "[?$/%(){}#\"'`]\\|\\[\\|\\]\\|\\<\\(" + ruby-block-beg-re + "\\|" ruby-block-end-re + "\\)\\>\\|^=begin") + ) + +(defconst ruby-negative + (concat "^[ \t]*\\(\\(" ruby-block-mid-re "\\)\\>\\|\\(" + ruby-block-end-re "\\)\\>\\|\\}\\|\\]\\)") + ) + +(defconst ruby-operator-chars "[,.+*/%-&|^~=<>:]") +(defconst ruby-symbol-chars "[a-zA-Z0-9_]") + +(defvar ruby-mode-abbrev-table nil + "Abbrev table in use in ruby-mode buffers.") + +(define-abbrev-table 'ruby-mode-abbrev-table ()) + +(defvar ruby-mode-map nil "Keymap used in ruby mode.") + +(if ruby-mode-map + nil + (setq ruby-mode-map (make-sparse-keymap)) + (define-key ruby-mode-map "{" 'ruby-electric-brace) + (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-p" 'ruby-beginning-of-block) + (define-key ruby-mode-map "\e\C-n" 'ruby-end-of-block) + (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) + (define-key ruby-mode-map "\C-m" 'newline)) + +(defvar ruby-mode-syntax-table nil + "Syntax table in use in ruby-mode buffers.") + +(if ruby-mode-syntax-table + () + (setq ruby-mode-syntax-table (make-syntax-table)) + (modify-syntax-entry ?\' "\"" ruby-mode-syntax-table) + (modify-syntax-entry ?\" "\"" ruby-mode-syntax-table) + (modify-syntax-entry ?\` "\"" ruby-mode-syntax-table) + (modify-syntax-entry ?# "<" ruby-mode-syntax-table) + (modify-syntax-entry ?\n ">" ruby-mode-syntax-table) + (modify-syntax-entry ?\\ "'" ruby-mode-syntax-table) + (modify-syntax-entry ?$ "/" ruby-mode-syntax-table) + (modify-syntax-entry ?? "_" ruby-mode-syntax-table) + (modify-syntax-entry ?_ "_" ruby-mode-syntax-table) + (modify-syntax-entry ?< "." ruby-mode-syntax-table) + (modify-syntax-entry ?> "." ruby-mode-syntax-table) + (modify-syntax-entry ?& "." ruby-mode-syntax-table) + (modify-syntax-entry ?| "." ruby-mode-syntax-table) + (modify-syntax-entry ?% "." ruby-mode-syntax-table) + (modify-syntax-entry ?= "." ruby-mode-syntax-table) + (modify-syntax-entry ?/ "." ruby-mode-syntax-table) + (modify-syntax-entry ?+ "." ruby-mode-syntax-table) + (modify-syntax-entry ?* "." ruby-mode-syntax-table) + (modify-syntax-entry ?- "." ruby-mode-syntax-table) + (modify-syntax-entry ?\; "." ruby-mode-syntax-table) + (modify-syntax-entry ?\( "()" ruby-mode-syntax-table) + (modify-syntax-entry ?\) ")(" ruby-mode-syntax-table) + (modify-syntax-entry ?\{ "(}" ruby-mode-syntax-table) + (modify-syntax-entry ?\} "){" ruby-mode-syntax-table) + (modify-syntax-entry ?\[ "(]" ruby-mode-syntax-table) + (modify-syntax-entry ?\] ")[" ruby-mode-syntax-table) + ) + +(defvar ruby-indent-level 2 + "*Indentation of ruby statements.") + +(defun ruby-mode () + "Major mode for editing ruby scripts. +\\[ruby-indent-command] properly indents subexpressions of multi-line +class, module, def, if, while, for, do, and case statements, taking +nesting into account. + +The variable ruby-indent-level controls the amount of indentation. +\\{ruby-mode-map}" + (interactive) + (kill-all-local-variables) + (use-local-map ruby-mode-map) + (setq mode-name "ruby") + (setq major-mode 'ruby-mode) + (set-syntax-table ruby-mode-syntax-table) + (setq local-abbrev-table ruby-mode-abbrev-table) + (make-local-variable 'indent-line-function) + (setq indent-line-function 'ruby-indent-line) + (make-local-variable 'require-final-newline) + (setq require-final-newline t) + (make-variable-buffer-local 'comment-start) + (setq comment-start "# ") + (make-variable-buffer-local 'comment-end) + (setq comment-end "") + (make-variable-buffer-local 'comment-column) + (setq comment-column 32) + (make-variable-buffer-local 'comment-start-skip) + (setq comment-start-skip "#+ *") + (make-local-variable 'parse-sexp-ignore-comments) + (setq parse-sexp-ignore-comments t) + (run-hooks 'ruby-mode-hook)) + +(defun ruby-current-indentation () + (save-excursion + (beginning-of-line) + (back-to-indentation) + (current-column))) + +(defun ruby-indent-line (&optional flag) + "Correct indentation of the current ruby line." + (ruby-indent-to (ruby-calculate-indent))) + +(defun ruby-indent-command () + (interactive) + (ruby-indent-line t)) + +(defun ruby-indent-to (x) + (if x + (let (shift top beg) + (and (< x 0) + (error "invalid nest")) + (setq shift (current-column)) + (beginning-of-line) + (setq beg (point)) + (back-to-indentation) + (setq top (current-column)) + (skip-chars-backward " \t") + (cond + ((>= x shift) + (setq shift 0)) + ((>= shift top) + (setq shift (- shift top))) + (t (setq shift 0))) + (if (and (bolp) + (= x top)) + (move-to-column (+ x shift)) + (move-to-column top) + (delete-region beg (point)) + (beginning-of-line) + (indent-to x) + (move-to-column (+ x shift)))))) + +(defun ruby-expr-beg (&optional modifier) + (save-excursion + (if (looking-at "\\?") + (progn + (or (bolp) (forward-char -1)) + (not (looking-at "\\sw"))) + (skip-chars-backward " \t") + (or (bolp) (forward-char -1)) + (or (looking-at ruby-operator-chars) + (looking-at "[\\[({!?]") + (bolp) + (and (looking-at ruby-symbol-chars) + (forward-word -1) + (or + (and modifier (bolp)) + (looking-at ruby-block-beg-re) + (looking-at ruby-block-op-re) + (looking-at ruby-block-mid-re) + (and modifier + (save-excursion + (forward-char -1) + (let ((c (char-after (point)))) + (or (eq c ?.) + (eq c ? ) + (eq c ?\t)))))) + (goto-char (match-end 0)) + (looking-at "[^_]")))))) + +(defun ruby-parse-region (start end) + (let ((indent-point end) + (indent 0) + (in-string nil) + (in-paren nil) + (depth 0) + (nest nil) + (pcol nil)) + (save-excursion + (if start + (goto-char start) + (ruby-beginning-of-indent)) + (save-restriction + (narrow-to-region (point) end) + (while (and (> indent-point (point)) + (re-search-forward ruby-delimiter indent-point t)) + (let ((pnt (point)) w) + (goto-char (match-beginning 0)) + (cond + ((or (looking-at "\"") ;skip string + (looking-at "'") + (looking-at "`")) + (setq w (char-after (point))) + (cond + ((and (not (eobp)) + (re-search-forward (format "[^\\]%c" w) indent-point t)) + nil) + (t + (setq in-string (point)) + (goto-char indent-point)))) + ((looking-at "/") + (cond + ((and (not (eobp)) (ruby-expr-beg)) + (if (re-search-forward "[^\\]/" indent-point t) + nil + (setq in-string (point)) + (goto-char indent-point))) + (t + (goto-char pnt)))) + ((looking-at "%") + (cond + ((and (not (eobp)) (ruby-expr-beg) + (looking-at "%[Qqrx]?\\(.\\)")) + (setq w (buffer-substring (match-beginning 1) + (match-end 1))) + (cond + ((string= w "[") (setq w "]")) + ((string= w "{") (setq w "}")) + ((string= w "(") (setq w ")")) + ((string= w "<") (setq w ">"))) + (goto-char (match-end 0)) + (if (search-forward w indent-point t) + nil + (setq in-string (point)) + (goto-char indent-point))) + (t + (goto-char pnt)))) + ((looking-at "\\?") ;skip ?char + (cond + ((ruby-expr-beg) + (looking-at "?\\(\\\\C-\\|\\\\M-\\)*.") + (goto-char (match-end 0))) + (t + (goto-char pnt)))) + ((looking-at "\\$") ;skip $char + (goto-char pnt) + (forward-char 1)) + ((looking-at "#") ;skip comment + (forward-line 1) + (goto-char (point)) + ) + ((looking-at "(") + (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)) + (goto-char pnt) + ) + ((looking-at ")") + (setq nest (cdr nest)) + (setq depth (cdr (car pcol))) + (setq pcol (cdr pcol)) + (goto-char pnt)) + ((looking-at "[])}]") + (setq nest (cdr nest)) + (setq depth (1- depth)) + (goto-char pnt)) + ((looking-at ruby-block-end-re) + (if (or (and (not (bolp)) + (progn + (forward-char -1) + (eq ?_ (char-after (point))))) + (progn + (goto-char pnt) + (setq w (char-after (point))) + (or (eq ?_ w) + (eq ?! w) + (eq ?? w)))) + nil + (setq nest (cdr nest)) + (setq depth (1- depth))) + (goto-char pnt)) + ((looking-at ruby-block-beg-re) + (and + (or (bolp) + (progn + (forward-char -1) + (not (eq ?_ (char-after (point)))))) + (progn + (goto-char pnt) + (setq w (char-after (point))) + (and (not (eq ?_ w)) + (not (eq ?! w)) + (not (eq ?? w)))) + (progn + (goto-char (match-beginning 0)) + (if (looking-at ruby-modifier-re) + (ruby-expr-beg) + t)) + (progn + (setq nest (cons (cons nil pnt) nest)) + (setq depth (1+ depth)))) + (if (looking-at "def\\s *[/`]") + (goto-char (match-end 0)) + (goto-char pnt))) + ((looking-at "^=begin") + (if (re-search-forward "^=end" indent-point t) + (forward-line 1) + (setq in-string (match-end 0)) + (goto-char indent-point))) + (t + (error (format "bad string %s" + (buffer-substring (point) pnt) + ))))))) + (list in-string (car nest) depth (car (car pcol)))))) + +(defun ruby-calculate-indent (&optional parse-start) + (save-excursion + (beginning-of-line) + (let ((indent-point (point)) + (case-fold-search nil) + state bol eol + (indent 0)) + (if parse-start + (goto-char parse-start) + (ruby-beginning-of-indent) + (setq parse-start (point))) + (back-to-indentation) + (setq indent (current-column)) + (setq state (ruby-parse-region parse-start indent-point)) + (cond + ((nth 0 state) ; within string + (setq indent nil)) ; do nothing + + ((car (nth 1 state)) ; in paren + (goto-char (cdr (nth 1 state))) + (if (eq (car (nth 1 state)) ?\( ) + (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 (+ (current-column) ruby-indent-level))) + (t + (setq indent (current-column))))) + (cond + ((nth 3 state) + (goto-char (nth 3 state)) + (setq indent (+ (current-column) ruby-indent-level))) + (t + (goto-char parse-start) + (back-to-indentation) + (setq indent (+ (current-column) (* (nth 2 state) ruby-indent-level))))) + )) + + ((and (nth 2 state)(> (nth 2 state) 0)) ; in nest + (goto-char (cdr (nth 1 state))) + (forward-word -1) ; skip back a keyword + (cond + ((looking-at "do") ; iter block is a special case + (cond + ((nth 3 state) + (goto-char (nth 3 state)) + (setq indent (+ (current-column) ruby-indent-level))) + (t + (goto-char parse-start) + (back-to-indentation) + (setq indent (+ (current-column) (* (nth 2 state) ruby-indent-level)))))) + (t + (setq indent (+ (current-column) ruby-indent-level))))) + + ((and (nth 2 state) (< (nth 2 state) 0)) ; in negative nest + (setq indent (+ (current-column) (* (nth 2 state) ruby-indent-level))))) + + (cond + (indent + (goto-char indent-point) + (end-of-line) + (setq eol (point)) + (beginning-of-line) + (cond + ((re-search-forward ruby-negative eol t) + (and (not (eq ?_ (char-after (match-end 0)))) + (setq indent (- indent ruby-indent-level)))) + ;;operator terminated lines + ((and + (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)))))) + ;; goto beginning of non-empty no-comment line + (let (end done) + (while (not done) + (skip-chars-backward " \t\n") + (setq end (point)) + (beginning-of-line) + (if (re-search-forward "^\\s *#" end t) + (beginning-of-line) + (setq done t)))) + (setq bol (point)) + (end-of-line) + (skip-chars-backward " \t") + (or (bobp) (forward-char -1)) + (and + (or (and (looking-at ruby-symbol-chars) + (skip-chars-backward ruby-symbol-chars) + (looking-at ruby-block-op-re)) + (and (looking-at ruby-operator-chars) + (or (not (or (eq ?/ (char-after (point))))) + (null (nth 0 (ruby-parse-region parse-start (point))))) + (not (eq (char-after (1- (point))) ?$)) + (or (not (eq ?| (char-after (point)))) + (save-excursion + (or (eolp) (forward-char -1)) + (and (search-backward "|") + (skip-chars-backward " \t\n") + (and (not (eolp)) + (progn + (forward-char -1) + (not (looking-at "\\{"))) + (progn + (forward-word -1) + (not (looking-at "do\\>[^_]"))))))))) + (setq indent (+ 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) + "Move backward to next beginning-of-defun. +With argument, do this that many times. +Returns t unless search stops due to end of buffer." + (interactive "p") + (and (re-search-backward (concat "^\\(" ruby-block-beg-re "\\)\\b") + nil 'move (or arg 1)) + (progn (beginning-of-line) t))) + +(defun ruby-beginning-of-indent () + (and (re-search-backward (concat "^\\(" ruby-indent-beg-re "\\)\\b") + nil 'move) + (progn + (beginning-of-line) + t))) + +(defun 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") + (and (re-search-forward (concat "^\\(" ruby-block-end-re "\\)\\b[^_]") + nil 'move (or arg 1)) + (progn (beginning-of-line) t)) + (forward-line 1)) + +(defun ruby-move-to-block (n) + (let (start pos done down) + (setq start (ruby-calculate-indent)) + (if (eobp) + nil + (while (and (not (bobp)) (not done)) + (forward-line n) + (cond + ((looking-at "^$")) + ((looking-at "^\\s *#")) + (t + (setq pos (current-indentation)) + (cond + ((< start pos) + (setq down t)) + ((and down (= pos start)) + (setq done t)) + ((> start pos) + (setq done t))))) + (if done + (progn + (back-to-indentation) + (if (looking-at ruby-block-mid-re) + (setq done nil))))))) + (back-to-indentation)) + +(defun ruby-beginning-of-block () + "Move backward to next beginning-of-block" + (interactive) + (ruby-move-to-block -1)) + +(defun ruby-end-of-block () + "Move forward to next beginning-of-block" + (interactive) + (ruby-move-to-block 1)) + +(defun ruby-reindent-then-newline-and-indent () + (interactive "*") + (save-excursion + (delete-region (point) (progn (skip-chars-backward " \t") (point)))) + (newline) + (save-excursion + (forward-line -1) + (indent-according-to-mode)) + (indent-according-to-mode)) + +(fset 'ruby-encomment-region (symbol-function 'comment-region)) + +(defun ruby-decomment-region (beg end) + (interactive "r") + (save-excursion + (goto-char beg) + (while (re-search-forward "^\\([ \t]*\\)#" end t) + (replace-match "\\1" nil nil) + (save-excursion + (ruby-indent-line))))) + +(defun ruby-insert-end () + (interactive) + (insert "end") + (ruby-indent-line t) + (end-of-line)) + +(cond + ((featurep 'hilit19) + (hilit-set-mode-patterns + 'ruby-mode + '(("[^$\\?]\\(\"[^\\\"]*\\(\\\\\\(.\\|\n\\)[^\\\"]*\\)*\"\\)" 1 string) + ("[^$\\?]\\('[^\\']*\\(\\\\\\(.\\|\n\\)[^\\']*\\)*'\\)" 1 string) + ("[^$\\?]\\(`[^\\`]*\\(\\\\\\(.\\|\n\\)[^\\`]*\\)*`\\)" 1 string) + ("^\\s *#.*$" nil comment) + ("[^$@?\\]\\(#[^$@{].*$\\)" 1 comment) + ("[^a-zA-Z_]\\(\\?\\(\\\\[CM]-\\)*.\\)" 1 string) + ("^\\s *\\(require\\|load\\).*$" nil include) + ("^\\s *\\(include\\|alias\\|undef\\).*$" nil decl) + ("^\\s *\\<\\(class\\|def\\|module\\)\\>" "[)\n;]" defun) + ("[^_]\\<\\(begin\\|case\\|else\\|elsif\\|end\\|ensure\\|for\\|if\\|unless\\|rescue\\|then\\|when\\|while\\|until\\|do\\)\\>[^_]" 1 defun) + ("[^_]\\<\\(and\\|break\\|next\\|raise\\|fail\\|in\\|not\\|or\\|redo\\|retry\\|return\\|super\\|yield\\|self\\|nil\\)\\>[^_]" 1 keyword) + ("\\$\\(.\\|\\sw+\\)" nil type) + ("[$@].[a-zA-Z_0-9]*" nil struct) + ("^__END__" nil label)))) + + ((featurep 'font-lock) + (or (boundp 'font-lock-variable-name-face) + (setq font-lock-variable-name-face font-lock-type-face)) + (defvar ruby-font-lock-keywords + (list + (cons (concat + "\\(^\\|[^_]\\)\\b\\(" + (mapconcat + 'identity + '("alias" + "and" + "begin" + "break" + "case" + "class" + "do" + "elsif" + "else" + "fail" + "ensure" + "for" + "end" + "if" + "in" + "module" + "next" + "not" + "or" + "raise" + "redo" + "rescue" + "retry" + "return" + "then" + "self" + "super" + "unless" + "undef" + "until" + "when" + "while" + ) + "\\|") + "\\)[ \n\t()]") + 2) + ;; variables + '("\\(^\\|[^_]\\)\\b\\(nil\\|self\\|true\\|false\\)\\b[^_]" + 2 font-lock-variable-name-face) + ;; variables + '("\\[$@].\\([a-zA-Z0-9_]\\)" + 0 font-lock-variable-name-face) + ;; constants + '("\\(^\\|[^_]\\)\\b\\([A-Z]+[a-zA-Z0-9_]*\\)" + 2 font-lock-type-face) + ;; functions + '("\\bdef[ \t]+\\([a-zA-Z_]+[a-zA-Z0-9_]*[?!=]?\\|\\[\\]=?\\)" + 0 font-lock-function-name-face)) + "*Additional expressions to highlight in ruby mode.") + (if (and (>= (string-to-int emacs-version) 20) + (not (featurep 'xemacs))) + (add-hook + 'ruby-mode-hook + (lambda () + (make-local-variable 'font-lock-defaults) + (setq font-lock-defaults + '((ruby-font-lock-keywords) nil nil ((?\_ . "w")))))) + (add-hook 'ruby-mode-hook + (lambda () + (setq font-lock-keywords ruby-font-lock-keywords) + (font-lock-mode 1)))))) diff --git a/sample/rubydb2x.el b/sample/rubydb2x.el new file mode 100644 index 0000000000..a74265fb0e --- /dev/null +++ b/sample/rubydb2x.el @@ -0,0 +1,104 @@ +(require 'gud) +(provide 'rubydb) + +;; ====================================================================== +;; rubydb functions + +;;; History of argument lists passed to rubydb. +(defvar gud-rubydb-history nil) + +(defun gud-rubydb-massage-args (file args) + (cons "-I" (cons "." (cons "-r" (cons "debug" (cons file args)))))) + +;; There's no guarantee that Emacs will hand the filter the entire +;; marker at once; it could be broken up across several strings. We +;; might even receive a big chunk with several markers in it. If we +;; receive a chunk of text which looks like it might contain the +;; beginning of a marker, we save it here between calls to the +;; filter. +(defvar gud-rubydb-marker-acc "") + +(defun gud-rubydb-marker-filter (string) + (save-match-data + (setq gud-marker-acc (concat gud-marker-acc string)) + (let ((output "")) + + ;; Process all the complete markers in this chunk. + (while (string-match "\032\032\\([^:\n]*\\):\\([0-9]*\\):.*\n" + gud-marker-acc) + (setq + + ;; Extract the frame position from the marker. + gud-last-frame + (cons (substring gud-marker-acc (match-beginning 1) (match-end 1)) + (string-to-int (substring gud-marker-acc + (match-beginning 2) + (match-end 2)))) + + ;; Append any text before the marker to the output we're going + ;; to return - we don't include the marker in this text. + output (concat output + (substring gud-marker-acc 0 (match-beginning 0))) + + ;; Set the accumulator to the remaining text. + gud-marker-acc (substring gud-marker-acc (match-end 0)))) + + ;; Does the remaining text look like it might end with the + ;; beginning of another marker? If it does, then keep it in + ;; gud-marker-acc until we receive the rest of it. Since we + ;; know the full marker regexp above failed, it's pretty simple to + ;; test for marker starts. + (if (string-match "\032.*\\'" gud-marker-acc) + (progn + ;; Everything before the potential marker start can be output. + (setq output (concat output (substring gud-marker-acc + 0 (match-beginning 0)))) + + ;; Everything after, we save, to combine with later input. + (setq gud-marker-acc + (substring gud-marker-acc (match-beginning 0)))) + + (setq output (concat output gud-marker-acc) + gud-marker-acc "")) + + output))) + +(defun gud-rubydb-find-file (f) + (find-file-noselect f)) + +(defvar rubydb-command-name "ruby" + "File name for executing ruby.") + +;;;###autoload +(defun rubydb (command-line) + "Run rubydb on program FILE in buffer *gud-FILE*. +The directory containing FILE becomes the initial working directory +and source-file directory for your debugger." + (interactive + (list (read-from-minibuffer "Run rubydb (like this): " + (if (consp gud-rubydb-history) + (car gud-rubydb-history) + (concat rubydb-command-name " ")) + nil nil + '(gud-rubydb-history . 1)))) + + (gud-overload-functions '((gud-massage-args . gud-rubydb-massage-args) + (gud-marker-filter . gud-rubydb-marker-filter) + (gud-find-file . gud-rubydb-find-file) + )) + (gud-common-init command-line) + + (gud-def gud-break "b %l" "\C-b" "Set breakpoint at current line.") +; (gud-def gud-remove "clear %l" "\C-d" "Remove breakpoint at current line") + (gud-def gud-step "s" "\C-s" "Step one source line with display.") + (gud-def gud-next "n" "\C-n" "Step one line (skip functions).") + (gud-def gud-cont "c" "\C-r" "Continue with display.") + (gud-def gud-finish "finish" "\C-f" "Finish executing current function.") + (gud-def gud-up "up %p" "<" "Up N stack frames (numeric arg).") + (gud-def gud-down "down %p" ">" "Down N stack frames (numeric arg).") + (gud-def gud-print "p %e" "\C-p" "Evaluate ruby expression at point.") + + (setq comint-prompt-regexp "^(rdb:-) ") + (setq paragraph-start comint-prompt-regexp) + (run-hooks 'rubydb-mode-hook) + ) diff --git a/sample/rubydb3x.el b/sample/rubydb3x.el new file mode 100644 index 0000000000..9d4e31f90e --- /dev/null +++ b/sample/rubydb3x.el @@ -0,0 +1,104 @@ +(require 'gud) +(provide 'rubydb) + +;; ====================================================================== +;; rubydb functions + +;;; History of argument lists passed to rubydb. +(defvar gud-rubydb-history nil) + +(defun gud-rubydb-massage-args (file args) + (cons "-r" (cons "debug" args))) + +;; There's no guarantee that Emacs will hand the filter the entire +;; marker at once; it could be broken up across several strings. We +;; might even receive a big chunk with several markers in it. If we +;; receive a chunk of text which looks like it might contain the +;; beginning of a marker, we save it here between calls to the +;; filter. +(defvar gud-rubydb-marker-acc "") + +(defun gud-rubydb-marker-filter (string) + (setq gud-marker-acc (concat gud-marker-acc string)) + (let ((output "")) + + ;; Process all the complete markers in this chunk. + (while (string-match "\032\032\\([^:\n]*\\):\\([0-9]*\\):.*\n" + gud-marker-acc) + (setq + + ;; Extract the frame position from the marker. + gud-last-frame + (cons (substring gud-marker-acc (match-beginning 1) (match-end 1)) + (string-to-int (substring gud-marker-acc + (match-beginning 2) + (match-end 2)))) + + ;; Append any text before the marker to the output we're going + ;; to return - we don't include the marker in this text. + output (concat output + (substring gud-marker-acc 0 (match-beginning 0))) + + ;; Set the accumulator to the remaining text. + gud-marker-acc (substring gud-marker-acc (match-end 0)))) + + ;; Does the remaining text look like it might end with the + ;; beginning of another marker? If it does, then keep it in + ;; gud-marker-acc until we receive the rest of it. Since we + ;; know the full marker regexp above failed, it's pretty simple to + ;; test for marker starts. + (if (string-match "\032.*\\'" gud-marker-acc) + (progn + ;; Everything before the potential marker start can be output. + (setq output (concat output (substring gud-marker-acc + 0 (match-beginning 0)))) + + ;; Everything after, we save, to combine with later input. + (setq gud-marker-acc + (substring gud-marker-acc (match-beginning 0)))) + + (setq output (concat output gud-marker-acc) + gud-marker-acc "")) + + output)) + +(defun gud-rubydb-find-file (f) + (save-excursion + (let ((buf (find-file-noselect f))) + (set-buffer buf) + (gud-make-debug-menu) + buf))) + +(defvar rubydb-command-name "ruby" + "File name for executing ruby.") + +;;;###autoload +(defun rubydb (command-line) + "Run rubydb on program FILE in buffer *gud-FILE*. +The directory containing FILE becomes the initial working directory +and source-file directory for your debugger." + (interactive + (list (read-from-minibuffer "Run rubydb (like this): " + (if (consp gud-rubydb-history) + (car gud-rubydb-history) + (concat rubydb-command-name " ")) + nil nil + '(gud-rubydb-history . 1)))) + + (gud-common-init command-line 'gud-rubydb-massage-args + 'gud-rubydb-marker-filter 'gud-rubydb-find-file) + + (gud-def gud-break "b %l" "\C-b" "Set breakpoint at current line.") +; (gud-def gud-remove "clear %l" "\C-d" "Remove breakpoint at current line") + (gud-def gud-step "s" "\C-s" "Step one source line with display.") + (gud-def gud-next "n" "\C-n" "Step one line (skip functions).") + (gud-def gud-cont "c" "\C-r" "Continue with display.") + (gud-def gud-finish "finish" "\C-f" "Finish executing current function.") + (gud-def gud-up "up %p" "<" "Up N stack frames (numeric arg).") + (gud-def gud-down "down %p" ">" "Down N stack frames (numeric arg).") + (gud-def gud-print "p %e" "\C-p" "Evaluate ruby expression at point.") + + (setq comint-prompt-regexp "^(rdb:-) ") + (setq paragraph-start comint-prompt-regexp) + (run-hooks 'rubydb-mode-hook) + ) diff --git a/sample/sieve.rb b/sample/sieve.rb new file mode 100644 index 0000000000..03ff8a67f4 --- /dev/null +++ b/sample/sieve.rb @@ -0,0 +1,18 @@ +# sieve of Eratosthenes +sieve = [] +if ! max = ARGV.shift; max = 100; end +max = max.to_i + +print "1" +for i in 2 .. max + begin + for d in sieve + fail if i % d == 0 + end + print ", " + print i + sieve.push(i) + rescue + end +end +print "\n" diff --git a/sample/svr.rb b/sample/svr.rb new file mode 100644 index 0000000000..c866407eb0 --- /dev/null +++ b/sample/svr.rb @@ -0,0 +1,32 @@ +# socket example - server side +# usage: ruby svr.rb + +require "socket" + +gs = TCPserver.open(0) +addr = gs.addr +addr.shift +printf("server is on %d\n", addr.join(":")) +socks = [gs] + +while TRUE + nsock = select(socks); + next if nsock == nil + for s in nsock[0] + if s == gs + ns = s.accept + socks.push(ns) + print(s, " is accepted\n") + else + if s.eof? + print(s, " is gone\n") + s.close + socks.delete(s) + else + if str = s.gets; + s.write(str) + end + end + end + end +end diff --git a/sample/test.rb b/sample/test.rb new file mode 100644 index 0000000000..382ca9428a --- /dev/null +++ b/sample/test.rb @@ -0,0 +1,943 @@ +#! /usr/local/bin/ruby + +$testnum=0 +$ntest=0 +$failed = 0 + +def check(what) + printf "%s\n", what + $what = what + $testnum = 0 +end + +def ok(cond) + $testnum+=1 + $ntest+=1 + if cond + printf "ok %d\n", $testnum + else + where = caller[0] + printf "not ok %s %d -- %s\n", $what, $testnum, where + $failed+=1 + end +end + +# make sure conditional operators work + +check "condition" + +$x = '0'; + +$x == $x && ok(TRUE) +$x != $x && ok(FALSE) +$x == $x || ok(FALSE) +$x != $x || ok(TRUE) + +# first test to see if we can run the tests. + +check "if/unless"; + +$x = 'test'; +ok(if $x == $x then TRUE else FALSE end) +$bad = FALSE +unless $x == $x + $bad = TRUE +end +ok(!$bad) +ok(unless $x != $x then TRUE else FALSE end) + +check "case" + +case 5 +when 1, 2, 3, 4, 6, 7, 8 + ok(FALSE) +when 5 + ok(TRUE) +end + +case 5 +when 5 + ok(TRUE) +when 1..10 + ok(FALSE) +end + +case 5 +when 1..10 + ok(TRUE) +else + ok(FALSE) +end + +case 5 +when 5 + ok(TRUE) +else + ok(FALSE) +end + +case "foobar" +when /^f.*r$/ + ok(TRUE) +else + ok(FALSE) +end + +check "while/until"; + +tmp = open("while_tmp", "w") +tmp.print "tvi925\n"; +tmp.print "tvi920\n"; +tmp.print "vt100\n"; +tmp.print "Amiga\n"; +tmp.print "paper\n"; +tmp.close + +# test break + +tmp = open("while_tmp", "r") +ok(tmp.kind_of?(File)) + +while tmp.gets() + break if /vt100/ +end + +ok(!tmp.eof? && /vt100/) +tmp.close + +# test next +$bad = FALSE +tmp = open("while_tmp", "r") +while tmp.gets() + next if /vt100/; + $bad = 1 if /vt100/; +end +ok(!(!tmp.eof? || /vt100/ || $bad)) +tmp.close + +# test redo +$bad = FALSE +tmp = open("while_tmp", "r") +while tmp.gets() + if gsub!('vt100', 'VT100') + gsub!('VT100', 'Vt100') + redo; + end + $bad = 1 if /vt100/; + $bad = 1 if /VT100/; +end +ok(tmp.eof? && !$bad) +tmp.close + +sum=0 +for i in 1..10 + sum += i + i -= 1 + if i > 0 + redo + end +end +ok(sum == 220) + +# test interval +$bad = FALSE +tmp = open("while_tmp", "r") +while tmp.gets() + break unless 1..2 + if /vt100/ || /Amiga/ || /paper/ + $bad = TRUE + break + end +end +ok(!$bad) +tmp.close + +File.unlink "while_tmp" or `/bin/rm -f "while_tmp"` +ok(!File.exist?("while_tmp")) + +i = 0 +until i>4 + i+=1 +end +ok(i>4) + +# exception handling +check "exception"; + +begin + fail "this must be handled" + ok(FALSE) +rescue + ok(TRUE) +end + +$bad = TRUE +begin + fail "this must be handled no.2" +rescue + if $bad + $bad = FALSE + retry + ok(FALSE) + end +end +ok(TRUE) + +# exception in rescue clause +$string = "this must be handled no.3" +begin + begin + fail "exception in rescue clause" + rescue + fail $string + end + ok(FALSE) +rescue + ok(TRUE) if $! == $string +end + +# exception in ensure clause +begin + begin + fail "this must be handled no.4" + ensure + fail "exception in ensure clause" + end + ok(FALSE) +rescue + ok(TRUE) +end + +$bad = TRUE +begin + begin + fail "this must be handled no.5" + ensure + $bad = FALSE + end +rescue +end +ok(!$bad) + +$bad = TRUE +begin + begin + fail "this must be handled no.6" + ensure + $bad = FALSE + end +rescue +end +ok(!$bad) + +$bad = TRUE +while TRUE + begin + break + ensure + $bad = FALSE + end +end +ok(!$bad) + +check "array" +ok([1, 2] + [3, 4] == [1, 2, 3, 4]) +ok([1, 2] * 2 == [1, 2, 1, 2]) +ok([1, 2] * ":" == "1:2") + +ok([1, 2].hash == [1, 2].hash) + +ok([1,2,3] & [2,3,4] == [2,3]) +ok([1,2,3] | [2,3,4] == [1,2,3,4]) +ok([1,2,3] - [2,3] == [1]) + +$x = [0, 1, 2, 3, 4, 5] +ok($x[2] == 2) +ok($x[1..3] == [1, 2, 3]) +ok($x[1,3] == [1, 2, 3]) + +$x[0, 2] = 10 +ok($x[0] == 10 && $x[1] == 2) + +$x[0, 0] = -1 +ok($x[0] == -1 && $x[1] == 10) + +$x[-1, 1] = 20 +ok($x[-1] == 20 && $x.pop == 20) + +# compact +$x = [nil, 1, nil, nil, 5, nil, nil] +$x.compact! +ok($x == [1, 5]) + +# empty? +ok(!$x.empty?) +$x = [] +ok($x.empty?) + +# sort +$x = ["it", "came", "to", "pass", "that", "..."] +$x = $x.sort.join(" ") +ok($x == "... came it pass that to") +$x = [2,5,3,1,7] +$x.sort!{|a,b| a<=>b} # sort with condition +ok($x == [1,2,3,5,7]) +$x.sort!{|a,b| b-a} # reverse sort +ok($x == [7,5,3,2,1]) + +# split test +$x = "The Book of Mormon" +ok($x.split(//).reverse!.join == "nomroM fo kooB ehT") +ok("1 byte string".split(//).reverse.join(":") == "g:n:i:r:t:s: :e:t:y:b: :1") +$x = "a b c d" +ok($x.split == ['a', 'b', 'c', 'd']) +ok($x.split(' ') == ['a', 'b', 'c', 'd']) + +$x = [1] +ok(($x * 5).join(":") == '1:1:1:1:1') +ok(($x * 1).join(":") == '1') +ok(($x * 0).join(":") == '') + +*$x = 1..7 +ok($x.size == 7) +ok($x == [1, 2, 3, 4, 5, 6, 7]) + +check "hash" +$x = {1=>2, 2=>4, 3=>6} +$y = {1, 2, 2, 4, 3, 6} + +ok($x[1] == 2) + +ok(begin + for k,v in $y + fail if k*2 != v + end + TRUE + rescue + FALSE + end) + +ok($x.length == 3) +ok($x.has_key?(1)) +ok($x.has_value?(4)) +ok($x.indexes(2,3) == [4,6]) +ok($x == (1=>2, 2=>4, 3=>6)) + +$z = $y.keys.join(":") +ok($z == "1:2:3") + +$z = $y.values.join(":") +ok($z == "2:4:6") +ok($x == $y) + +$y.shift +ok($y.length == 2) + +$z = [1,2] +$y[$z] = 256 +ok($y[$z] == 256) + +check "iterator" + +ok(!iterator?) + +def ttt + ok(iterator?) +end +ttt{} + +# yield at top level +ok(!defined?(yield)) + +$x = [1, 2, 3, 4] +$y = [] + +# iterator over array +for i in $x + $y.push i +end +ok($x == $y) + +# nested iterator +def tt + 1.upto(10) {|i| + yield i + } +end + +tt{|i| break if i == 5} +ok(i == 5) + +# iterator break/redo/next/retry +unless defined? loop + def loop + while TRUE + yield + end + end + ok(FALSE) +else + ok(TRUE) +end + +done = TRUE +loop{ + break + done = FALSE +} +ok(done) + +done = FALSE +$bad = FALSE +loop { + break if done + done = TRUE + next + $bad = TRUE +} +ok(!$bad) + +done = FALSE +$bad = FALSE +loop { + break if done + done = TRUE + redo + $bad = TRUE +} +ok(!$bad) + +$x = [] +for i in 1 .. 7 + $x.push i +end +ok($x.size == 7) +ok($x == [1, 2, 3, 4, 5, 6, 7]) + +$done = FALSE +$x = [] +for i in 1 .. 7 # see how retry works in iterator loop + if i == 4 and not $done + $done = TRUE + retry + end + $x.push(i) +end +ok($x.size == 10) +ok($x == [1, 2, 3, 1, 2, 3, 4, 5, 6, 7]) + +check "bignum" +def fact(n) + return 1 if n == 0 + f = 1 + while n>0 + f *= n + n -= 1 + end + return f +end +fact(3) +$x = fact(40) +ok($x == $x) +ok($x == fact(40)) +ok($x < $x+2) +ok($x > $x-2) +ok($x == 815915283247897734345611269596115894272000000000) +ok($x != 815915283247897734345611269596115894272000000001) +ok($x+1 == 815915283247897734345611269596115894272000000001) +ok($x/fact(20) == 335367096786357081410764800000) +$x = -$x +ok($x == -815915283247897734345611269596115894272000000000) +ok(2-(2**32) == -(2**32-2)) +ok(2**32 - 5 == (2**32-3)-2) + +$good = TRUE; +for i in 1000..1024 + $good = FALSE if ((1<<i) != (2**i)) +end +ok($good) + +$good = TRUE; +n1=1<<1000 +for i in 1000..1024 + $good = FALSE if ((1<<i) != n1) + n1 *= 2 +end +ok($good) + +$good = TRUE; +n2=n1 +for i in 1..10 + n1 = n1 / 2 + n2 = n2 >> 1 + $good = FALSE if (n1 != n2) +end +ok($good) + +$good = TRUE; +for i in 4000..4192 + n1 = 1 << i; + $good = FALSE if ((n1**2-1) / (n1+1) != (n1-1)) +end +ok($good) + +check "string & char" + +ok("abcd" == "abcd") +ok("abcd" =~ "abcd") +ok("abcd" === "abcd") +ok(("abc" =~ /^$/) == FALSE) +ok(("abc\n" =~ /^$/) == FALSE) +ok(("abc" =~ /^d*$/) == FALSE) +ok(("abc" =~ /d*$/) == 3) +ok("" =~ /^$/) +ok("\n" =~ /^$/) +ok("a\n\n" =~ /^$/) + +$foo = "abc" +ok("#$foo = abc" == "abc = abc") +ok("#{$foo} = abc" == "abc = abc") + +foo = "abc" +ok("#{foo} = abc" == "abc = abc") + +ok('-' * 5 == '-----') +ok('-' * 1 == '-') +ok('-' * 0 == '') + +foo = '-' +ok(foo * 5 == '-----') +ok(foo * 1 == '-') +ok(foo * 0 == '') + +$x = "a.gif" +ok($x.sub(/.*\.([^\.]+)$/, '\1') == "gif") +ok($x.sub(/.*\.([^\.]+)$/, 'b.\1') == "b.gif") +ok($x.sub(/.*\.([^\.]+)$/, '\2') == "") +ok($x.sub(/.*\.([^\.]+)$/, 'a\2b') == "ab") +ok($x.sub(/.*\.([^\.]+)$/, '<\&>') == "<a.gif>") + +# character constants(assumes ASCII) +ok("a"[0] == ?a) +ok(?a == ?a) +ok(?\C-a == 1) +ok(?\M-a == 225) +ok(?\M-\C-a == 129) + +$x = "abcdef" +$y = [ ?a, ?b, ?c, ?d, ?e, ?f ] +$bad = FALSE +$x.each_byte {|i| + if i != $y.shift + $bad = TRUE + break + end +} +ok(!$bad) + +check "asignment" +a = nil +ok(defined?(a)) +ok(a == nil) + +# multiple asignment +a, b = 1, 2 +ok(a == 1 && b == 2) + +a, b = b, a +ok(a == 2 && b == 1) + +a, = 1,2 +ok(a == 1) + +a, *b = 1, 2, 3 +ok(a == 1 && b == [2, 3]) + +*a = 1, 2, 3 +ok(a == [1, 2, 3]) + +check "call" +def aaa(a, b=100, *rest) + res = [a, b] + res += rest if rest + return res +end + +# not enough argument +begin + aaa() # need at least 1 arg + ok(FALSE) +rescue + ok(TRUE) +end + +begin + aaa # no arg given (exception raised) + ok(FALSE) +rescue + ok(TRUE) +end + +begin + if aaa(1) == [1, 100] + ok(TRUE) + else + fail + end +rescue + ok(FALSE) +end + +begin + if aaa(1, 2) == [1, 2] + ok(TRUE) + else + fail + end +rescue + ok(FALSE) +end + +ok(aaa(1, 2, 3, 4) == [1, 2, 3, 4]) +ok(aaa(1, *[2, 3, 4]) == [1, 2, 3, 4]) + +check "proc" +$proc = proc{|i| i} +ok($proc.call(2) == 2) +ok($proc.call(3) == 3) + +$proc = proc{|i| i*2} +ok($proc.call(2) == 4) +ok($proc.call(3) == 6) + +proc{ + iii=5 # dynamic local variable + $proc = proc{|i| + iii = i + } + $proc2 = proc { + $x = iii # dynamic variables shared by procs + } + # scope of dynamic variables + ok(defined?(iii)) +}.call +ok(!defined?(iii)) # out of scope + +$x=0 +$proc.call(5) +$proc2.call +ok($x == 5) + +if defined? Process.kill + check "signal" + + $x = 0 + trap "SIGINT", proc{|sig| $x = 2} + Process.kill "SIGINT", $$ + sleep 0.1 + ok($x == 2) + + trap "SIGINT", proc{fail "Interrupt"} + + x = FALSE + begin + Process.kill "SIGINT", $$ + sleep 0.1 + rescue + x = $! + end + ok(x && x =~ /Interrupt/) +else + ok(FALSE) +end + +check "eval" +ok(eval("") == nil) +$bad=FALSE +eval 'while FALSE; $bad = TRUE; print "foo\n" end' +ok(!$bad) + +ok(eval('TRUE')) + +$foo = 'ok(TRUE)' +begin + eval $foo +rescue + ok(FALSE) +end + +ok(eval("$foo") == 'ok(TRUE)') +ok(eval("TRUE") == TRUE) +i = 5 +ok(eval("i == 5")) +ok(eval("i") == 5) +ok(eval("defined? i")) + +# eval with binding +def test_ev + local1 = "local1" + lambda { + local2 = "local2" + return binding + }.call +end + +$x = test_ev +ok(eval("local1", $x) == "local1") # static local var +ok(eval("local2", $x) == "local2") # dynamic local var +$bad = TRUE +begin + p eval("local1") +rescue NameError # must raise error + $bad = FALSE +end +ok(!$bad) + +module EvTest + EVTEST1 = 25 + evtest2 = 125 + $x = binding +end +ok(eval("EVTEST1", $x) == 25) # constant in module +ok(eval("evtest2", $x) == 125) # local var in module +$bad = TRUE +begin + eval("EVTEST1") +rescue NameError # must raise error + $bad = FALSE +end +ok(!$bad) + +check "system" +ok(`echo foobar` == "foobar\n") +ok(`./ruby -e 'print "foobar"'` == 'foobar') + +tmp = open("script_tmp", "w") +tmp.print "print $zzz\n"; +tmp.close + +ok(`./ruby -s script_tmp -zzz` == 'TRUE') +ok(`./ruby -s script_tmp -zzz=555` == '555') + +tmp = open("script_tmp", "w") +tmp.print "#! /usr/local/bin/ruby -s\n"; +tmp.print "print $zzz\n"; +tmp.close + +ok(`./ruby script_tmp -zzz=678` == '678') + +tmp = open("script_tmp", "w") +tmp.print "this is a leading junk\n"; +tmp.print "#! /usr/local/bin/ruby -s\n"; +tmp.print "print $zzz\n"; +tmp.print "__END__\n"; +tmp.print "this is a trailing junk\n"; +tmp.close + +ok(`./ruby -x script_tmp` == 'nil') +ok(`./ruby -x script_tmp -zzz=555` == '555') + +tmp = open("script_tmp", "w") +for i in 1..5 + tmp.print i, "\n" +end +tmp.close + +`./ruby -i.bak -pe 'sub(/^[0-9]+$/){$&.to_i * 5}' script_tmp` +done = TRUE +tmp = open("script_tmp", "r") +while tmp.gets + if $_.to_i % 5 != 0 + done = FALSE + break + end +end +tmp.close +ok(done) + +File.unlink "script_tmp" or `/bin/rm -f "script_tmp"` +File.unlink "script_tmp.bak" or `/bin/rm -f "script_tmp.bak"` + +check "const" +TEST1 = 1 +TEST2 = 2 + +module Const + TEST3 = 3 + TEST4 = 4 +end + +module Const2 + TEST3 = 6 + TEST4 = 8 +end + +include Const + +ok([TEST1,TEST2,TEST3,TEST4] == [1,2,3,4]) + +include Const2 +STDERR.print "intentionally redefines TEST3, TEST4\n" if $VERBOSE +ok([TEST1,TEST2,TEST3,TEST4] == [1,2,6,8]) + +check "clone" +foo = Object.new +def foo.test + "test" +end +bar = foo.clone +def bar.test2 + "test2" +end + +ok(bar.test2 == "test2") +ok(bar.test == "test") +ok(foo.test == "test") + +begin + foo.test2 + ok FALSE +rescue + ok TRUE +end + +check "pack" + +$format = "c2x5CCxsdila6"; +# Need the expression in here to force ary[5] to be numeric. This avoids +# test2 failing because ary2 goes str->numeric->str and ary does not. +ary = [1,-100,127,128,32767,987.654321098 / 100.0,12345,123456,"abcdef"] +$x = ary.pack($format) +ary2 = $x.unpack($format) + +ok(ary.length == ary2.length) +ok(ary.join(':') == ary2.join(':')) +ok($x =~ /def/) + +check "math" +ok(Math.sqrt(4) == 2) + +include Math +ok(sqrt(4) == 2) + +check "struct" +struct_test = Struct.new("Test", :foo, :bar) +ok(struct_test == Struct::Test) + +test = struct_test.new(1, 2) +ok(test.foo == 1 && test.bar == 2) +ok(test[0] == 1 && test[1] == 2) + +a, b = test +ok(a == 1 && b == 2) + +test[0] = 22 +ok(test.foo == 22) + +test.bar = 47 +ok(test.bar == 47) + +check "variable" +ok($$.instance_of?(Fixnum)) + +# read-only variable +begin + $$ = 5 + ok FALSE +rescue + ok TRUE +end + +foobar = "foobar" +$_ = foobar +ok($_ == foobar) + +check "trace" +$x = 1234 +$y = 0 +trace_var :$x, proc{$y = $x} +$x = 40414 +ok($y == $x) + +untrace_var :$x +$x = 19660208 +ok($y != $x) + +trace_var :$x, proc{$x *= 2} +$x = 5 +ok($x == 10) + +untrace_var :$x + +check "defined?" + +ok(defined?($x)) # global variable +ok(defined?($x) == 'global-variable')# returns description + +foo=5 +ok(defined?(foo)) # local variable + +ok(defined?(Array)) # constant +ok(defined?(Object.new)) # method +ok(!defined?(Object.print)) # private method +ok(defined?(1 == 2)) # operator expression + +def defined_test + return !defined?(yield) +end + +ok(defined_test) # not iterator +ok(!defined_test{}) # called as iterator + +check "alias" +class Alias0 + def foo; "foo" end +end +class Alias1<Alias0 + alias bar foo + def foo; "foo+" + super end +end +class Alias2<Alias1 + alias baz foo + undef foo +end + +x = Alias2.new +ok(x.bar == "foo") +ok(x.baz == "foo+foo") + +# check for cache +ok(x.baz == "foo+foo") + +class Alias3<Alias2 + def foo + defined? super + end + def bar + defined? super + end + def quux + defined? super + end +end +x = Alias3.new +ok(!x.foo) +ok(x.bar) +ok(!x.quux) + +check "gc" +begin + 1.upto(10000) { + tmp = [0,1,2,3,4,5,6,7,8,9] + } + tmp = nil + ok TRUE +rescue + ok FALSE +end + +if $failed > 0 + printf "test: %d failed %d\n", $ntest, $failed +else + printf "end of test(test: %d)\n", $ntest +end diff --git a/sample/time.rb b/sample/time.rb new file mode 100644 index 0000000000..f4f4ec4883 --- /dev/null +++ b/sample/time.rb @@ -0,0 +1,8 @@ +#! /usr/local/bin/ruby +cmd = ARGV.join(" ") +b = Time.now +system(cmd) +e = Time.now +ut, st, cut, cst = Time.times +total = (e - b).to_f +printf STDERR, "%11.1f real %11.1f user %11.1f sys\n", total, cut, cst diff --git a/sample/tkbiff.rb b/sample/tkbiff.rb new file mode 100644 index 0000000000..d2d7bf7beb --- /dev/null +++ b/sample/tkbiff.rb @@ -0,0 +1,149 @@ +#! /usr/local/bin/ruby + +if ARGV[0] != '-d' + unless $DEBUG + exit if fork + end +else + ARGV.shift +end + +if ARGV.length == 0 + if ENV['MAIL'] + $spool = ENV['MAIL'] + else + $spool = '/usr/spool/mail/' + ENV['USER'] + end +else + $spool = ARGV[0] +end + +require "parsedate" +require "base64" + +include ParseDate + +class Mail + def Mail.new(f) + if !f.kind_of?(IO) + f = open(f, "r") + me = super + f.close + else + me = super + end + return me + end + + def initialize(f) + @header = {} + @body = [] + while f.gets() + $_.chop! + next if /^From / # skip From-line + break if /^$/ # end of header + if /^(\S+):\s*(.*)/ + @header[attr = $1.capitalize] = $2 + elsif attr + sub(/^\s*/, '') + @header[attr] += "\n" + $_ + end + end + + return if ! $_ + + while f.gets() + break if /^From / + @body.push($_) + end + end + + def header + return @header + end + + def body + return @body + end + +end + +require "tkscrollbox" + +$top = TkRoot.new +$top.withdraw +$list = TkScrollbox.new($top) { + relief 'raised' + width 80 + height 8 + setgrid 'yes' + pack +} +TkButton.new($top) { + text 'Dismiss' + command proc {$top.withdraw} + pack('fill'=>'both','expand'=>'yes') +} +$top.bind "Control-c", proc{exit} +$top.bind "Control-q", proc{exit} +$top.bind "space", proc{exit} + +$spool_size = 0 +$check_time = Time.now + +def check + $check_time = Time.now + size = File.size($spool) + if size and size != $spool_size + $spool_size = size + pop_up if size > 0 + end + Tk.after 5000, proc{check} +end + +if defined? Thread + Thread.start do + loop do + sleep 600 + if Time.now - $check_time > 200 + Tk.after 5000, proc{check} + end + end + end +end + +def pop_up + outcount = 0; + $list.delete 0, 'end' + f = open($spool, "r") + while !f.eof? + mail = Mail.new(f) + date, from, subj = mail.header['Date'], mail.header['From'], mail.header['Subject'] + next if !date + y = m = d = 0 + y, m, d = parsedate(date) if date + from = "sombody@somewhere" if ! from + subj = "(nil)" if ! subj + from = decode_b(from) + subj = decode_b(subj) + $list.insert 'end', format('%-02d/%02d/%02d [%-28.28s] %s',y,m,d,from,subj) + outcount += 1 + end + f.close + if outcount == 0 + $list.insert 'end', "You have no mail." + else + $list.see 'end' + end + $top.deiconify + Tk.after 2000, proc{$top.withdraw} +end + +$list.insert 'end', "You have no mail." +check +Tk.after 2000, proc{$top.withdraw} +begin + Tk.mainloop +rescue + `echo #$! > /tmp/tkbiff` +end diff --git a/sample/tkbrowse.rb b/sample/tkbrowse.rb new file mode 100644 index 0000000000..d127996173 --- /dev/null +++ b/sample/tkbrowse.rb @@ -0,0 +1,69 @@ +#!/usr/local/bin/ruby +# +# This script generates a directory browser, which lists the working +# directory and allows you to open files or subdirectories by +# double-clicking. + +# Create a scrollbar on the right side of the main window and a listbox +# on the left side. + +require "tkscrollbox" + +list = TkScrollbox.new { + relief 'raised' + width 20 + height 20 + setgrid 'yes' + pack +} + +# 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) + if dir != "." + file="#{dir}/#{file}" + if File.directory? file + system "browse #{file} &" + else + if File.file? file + if ENV['EDITOR'] + system format("%s %s&", ENV['EDITOR'], file) + else + sysmte "xedit #{file}&" + end + else + STDERR.print "\"#{file}\" isn't a directory or regular file" + end + end + end +end + +# Fill the listbox with a list of all the files in the directory (run +# the "ls" command to get that information). + +if ARGV.length>0 + dir = ARGV[0] +else + dir="." +end +list.insert 'end', *`ls #{dir}`.split + +# Set up bindings for the browser. + +list.focus +list.bind "Control-q", proc{exit} +list.bind "Control-c", proc{exit} +list.bind "Control-p", proc{ + print "selection <", TkSelection.get, ">\n" +} + +list.bind "Double-Button-1", proc{ + for i in TkSelection.get.split + print "clicked ", i, "\n" + browse dir, i + end +} +Tk.mainloop diff --git a/sample/tkdialog.rb b/sample/tkdialog.rb new file mode 100644 index 0000000000..e83e16d0a8 --- /dev/null +++ b/sample/tkdialog.rb @@ -0,0 +1,62 @@ +#! /usr/local/bin/ruby +require "tk" + +root = TkFrame.new +top = TkFrame.new(root) { + relief 'raised' + border 1 +} +msg = TkMessage.new(top) { + text "File main.c hasn't been saved to disk since \ +it was last modified. What should I do?" + justify 'center' + aspect 200 + font '-Adobe-helvetica-medium-r-normal--*-240*' + pack('padx'=>5, 'pady'=>5, 'expand'=>'yes') +} +top.pack('fill'=>'both') +root.pack + +bot = TkFrame.new(root) { + relief 'raised' + border 1 +} + +TkFrame.new(bot) { |left| + relief 'sunken' + border 1 + pack('side'=>'left', 'expand'=>'yes', 'padx'=>10, 'pady'=> 10) + TkButton.new(left) { + text "Save File" + command "quit 'save'" + pack('expand'=>'yes','padx'=>6,'pady'=> 6) + top.bind "Enter", proc{state 'active'} + msg.bind "Enter", proc{state 'active'} + bot.bind "Enter", proc{state 'active'} + top.bind "Leave", proc{state 'normal'} + msg.bind "Leave", proc{state 'normal'} + bot.bind "Leave", proc{state 'normal'} + Tk.root.bind "ButtonRelease-1", proc{quit 'save'} + Tk.root.bind "Return", proc{quit 'save'} + } +} +TkButton.new(bot) { + text "Quit Anyway" + command "quit 'quit'" + pack('side'=>'left', 'expand'=>'yes', 'padx'=>10) +} +TkButton.new(bot) { + text "Return To Editor" + command "quit 'return'" + pack('side'=>'left', 'expand'=>'yes', 'padx'=>10) +} +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 + +Tk.mainloop diff --git a/sample/tkfrom.rb b/sample/tkfrom.rb new file mode 100644 index 0000000000..b0ef8995ca --- /dev/null +++ b/sample/tkfrom.rb @@ -0,0 +1,126 @@ +#! /usr/local/bin/ruby + +require "parsedate" +require "base64" + +include ParseDate + +class Mail + def Mail.new(f) + if !f.kind_of?(IO) + f = open(f, "r") + me = super(f) + f.close + else + me = super + end + return me + end + + def initialize(f) + @header = {} + @body = [] + while f.gets() + $_.chop! + next if /^From / # skip From-line + break if /^$/ # end of header + if /^(\S+):\s*(.*)/ + @header[attr = $1.capitalize] = $2 + elsif attr + sub(/^\s*/, '') + @header[attr] += "\n" + $_ + end + end + + return if ! $_ + + while f.gets() + break if /^From / + @body.push($_) + end + end + + def header + return @header + end + + def body + return @body + end + +end + +if ARGV.length == 0 + if ENV['MAIL'] + ARGV[0] = ENV['MAIL'] + elsif ENV['USER'] + ARGV[0] = '/usr/spool/mail/' + ENV['USER'] + elsif ENV['LOGNAME'] + ARGV[0] = '/usr/spool/mail/' + ENV['LOGNAME'] + end +end + +require "tk" +list = scroll = nil +TkFrame.new{|f| + list = TkListbox.new(f) { + yscroll proc{|idx| + scroll.set *idx + } + relief 'raised' +# geometry "80x5" + width 80 + height 5 + setgrid 'yes' + pack('side'=>'left','fill'=>'both','expand'=>'yes') + } + scroll = TkScrollbar.new(f) { + command proc{|idx| + list.yview *idx + } + pack('side'=>'right','fill'=>'y') + } + pack +} +root = Tk.root +TkButton.new(root) { + text 'Dismiss' + command proc {exit} + pack('fill'=>'both','expand'=>'yes') +} +root.bind "Control-c", proc{exit} +root.bind "Control-q", proc{exit} +root.bind "space", proc{exit} + +$outcount = 0; +for file in ARGV + next if !File.exist?(file) + f = open(file, "r") + while !f.eof + mail = Mail.new(f) + date = mail.header['Date'] + next if !date + from = mail.header['From'] + subj = mail.header['Subject'] + y = m = d = 0 + y, m, d = parsedate(date) if date + from = "sombody@somewhere" if ! from + subj = "(nil)" if ! subj + from = decode_b(from) + subj = decode_b(subj) + list.insert 'end', format('%-02d/%02d/%02d [%-28.28s] %s',y,m,d,from,subj) + $outcount += 1 + end + f.close + list.see 'end' +end + +limit = 10000 +if $outcount == 0 + list.insert 'end', "You have no mail." + limit = 2000 +end +Tk.after limit, proc{ + exit +} +Tk.mainloop diff --git a/sample/tkhello.rb b/sample/tkhello.rb new file mode 100644 index 0000000000..1ff1403e71 --- /dev/null +++ b/sample/tkhello.rb @@ -0,0 +1,13 @@ +require "tk" + +TkButton.new { + text 'hello' + command proc{print "hello\n"} + pack('fill'=>'x') +} +TkButton.new { + text 'quit' + command 'exit' + pack('fill'=>'x') +} +Tk.mainloop diff --git a/sample/tkline.rb b/sample/tkline.rb new file mode 100644 index 0000000000..63d763a680 --- /dev/null +++ b/sample/tkline.rb @@ -0,0 +1,46 @@ + +$tk_thread_safe = TRUE +require "tkclass" + +$tkline_init = FALSE +def start_random + return if $tkline_init + $tkline_init = TRUE + if defined? Thread + Thread.start do + loop do + sleep 2 + Line.new($c, rand(400), rand(200), rand(400), rand(200)) + end + end + end +end + +$c = Canvas.new +$c.pack +$start_x = start_y = 0 + +def do_press(x, y) + $start_x = x + $start_y = y + $current_line = Line.new($c, x, y, x, y) + start_random +end +def do_motion(x, y) + if $current_line + $current_line.coords $start_x, $start_y, x, y + end +end + +def do_release(x, y) + if $current_line + $current_line.coords $start_x, $start_y, x, y + $current_line.fill 'black' + $current_line = nil + end +end + +$c.bind("1", proc{|e| do_press e.x, e.y}) +$c.bind("B1-Motion", proc{|x, y| do_motion x, y}, "%x %y") +$c.bind("ButtonRelease-1", proc{|x, y| do_release x, y}, "%x %y") +Tk.mainloop diff --git a/sample/tktimer.rb b/sample/tktimer.rb new file mode 100644 index 0000000000..34377e2f39 --- /dev/null +++ b/sample/tktimer.rb @@ -0,0 +1,50 @@ +#!/usr/local/bin/ruby +# This script generates a counter with start and stop buttons. + +require "tk" +$label = TkLabel.new { + text '0.00' + relief 'raised' + width 10 + pack('side'=>'bottom', 'fill'=>'both') +} + +TkButton.new { + text 'Start' + command proc { + if $stopped + $stopped = FALSE + tick + end + } + pack('side'=>'left','fill'=>'both','expand'=>'yes') +} +TkButton.new { + text 'Stop' + command proc{ + exit if $stopped + $stopped = TRUE + } + pack('side'=>'right','fill'=>'both','expand'=>'yes') +} + +$seconds=0 +$hundredths=0 +$stopped=TRUE + +def tick + if $stopped then return end + Tk.after 50, proc{tick} + $hundredths+=5 + if $hundredths >= 100 + $hundredths=0 + $seconds+=1 + end + $label.text format("%d.%02d", $seconds, $hundredths) +end + +root = Tk.root +root.bind "Control-c", proc{root.destroy} +root.bind "Control-q", proc{root.destroy} +Tk.root.focus +Tk.mainloop diff --git a/sample/trojan.rb b/sample/trojan.rb new file mode 100644 index 0000000000..2024da0908 --- /dev/null +++ b/sample/trojan.rb @@ -0,0 +1,14 @@ +#! /usr/local/bin/ruby +path = ENV['PATH'].split(/:/) + +for dir in path + if File.d(dir) + for f in d = Dir.open(dir) + fpath = dir+"/"+f + if File.f(fpath) && (File.stat(fpath).mode & 022) != 0 + printf("file %s is writable from other users\n", fpath) + end + end + d.close + end +end diff --git a/sample/tsvr.rb b/sample/tsvr.rb new file mode 100644 index 0000000000..fbc6545bb5 --- /dev/null +++ b/sample/tsvr.rb @@ -0,0 +1,23 @@ +# socket example - server side using thread +# usage: ruby tsvr.rb + +require "socket" +require "thread" + +gs = TCPserver.open(0) +addr = gs.addr +addr.shift +printf("server is on %d\n", addr.join(":")) + +while TRUE + ns = gs.accept + print(ns, " is accepted\n") + Thread.start do + s = ns # save to dynamic variable + while s.gets + s.write($_) + end + print(s, " is gone\n") + s.close + end +end diff --git a/sample/uumerge.rb b/sample/uumerge.rb new file mode 100644 index 0000000000..297b08f26a --- /dev/null +++ b/sample/uumerge.rb @@ -0,0 +1,37 @@ +#!/usr/local/bin/ruby + +if ARGV[0] == "-c" + out_stdout = 1; + ARGV.shift +end + +while gets() + if /^begin\s*(\d*)\s*(\S*)/ + $mode, $file = $1, $2 + $sawbegin+=1 + break + end +end + +fail "missing begin" if ! $sawbegin; + +if out_stdout + out = STDOUT +else + out = open($file, "w") if $file != ""; +end + +while gets() + if /^end/ + $sawend+=1 + break + end + sub(/[a-z]+$/, ""); # handle stupid trailing lowercase letters + next if /[a-z]/ + next if !(((($_[0] - 32) & 077) + 2) / 3 == $_.length / 4) + out << $_.unpack("u"); +end + +fail "missing end" if !$sawend; +File.chmod $mode.oct, $file if ! out_stdout +exit 0; @@ -0,0 +1,53 @@ +/************************************************ + + sig.h - + + $Author$ + $Date$ + created at: Wed Aug 16 01:15:38 JST 1995 + +************************************************/ +#ifndef SIG_H +#define SIG_H + +extern int trap_immediate; +#define TRAP_BEG (trap_immediate=1) +#define TRAP_END (trap_immediate=0) + +extern int prohibit_interrupt; +#define DEFER_INTS {prohibit_interrupt++;} +#define ALLOW_INTS {prohibit_interrupt--; CHECK_INTS;} +#define ENABLE_INTS {prohibit_interrupt--;} + +extern int trap_pending; +#ifdef THREAD +extern int thread_critical; +#if defined(HAVE_SETITIMER) && !defined(__BOW__) +extern int thread_pending; +void thread_schedule(); +# define CHECK_INTS if (!prohibit_interrupt) {\ + if (trap_pending) rb_trap_exec();\ + if (thread_pending && !thread_critical) thread_schedule();\ +} +# else +/* pseudo preemptive thread switching */ +extern int thread_tick; +#define THREAD_TICK 500 +void thread_schedule(); +# define CHECK_INTS if (!prohibit_interrupt) {\ + if (trap_pending) rb_trap_exec();\ + if (!thread_critical) {\ + if (thread_tick-- <= 0) {\ + thread_tick = THREAD_TICK;\ + thread_schedule();\ + }\ + }\ +} +# endif +#else +# define CHECK_INTS if (!prohibit_interrupt) {\ + if (trap_pending) rb_trap_exec();\ +} +#endif + +#endif diff --git a/signal.c b/signal.c new file mode 100644 index 0000000000..e5621b4ec1 --- /dev/null +++ b/signal.c @@ -0,0 +1,534 @@ +/************************************************ + + signal.c - + + $Author$ + $Date$ + created at: Tue Dec 20 10:13:44 JST 1994 + +************************************************/ + +#include "ruby.h" +#include "sig.h" +#include <signal.h> +#include <stdio.h> + +#ifndef NSIG +#ifdef DJGPP +#define NSIG SIGMAX +#else +#define NSIG (_SIGMAX + 1) /* For QNX */ +#endif +#endif + +static struct signals { + char *signm; + int signo; +} siglist [] = { +#ifdef SIGHUP + "HUP", SIGHUP, +#endif +#ifdef SIGINT + "INT", SIGINT, +#endif +#ifdef SIGQUIT + "QUIT", SIGQUIT, +#endif +#ifdef SIGILL + "ILL", SIGILL, +#endif +#ifdef SIGTRAP + "TRAP", SIGTRAP, +#endif +#ifdef SIGIOT + "IOT", SIGIOT, +#endif +#ifdef SIGABRT + "ABRT", SIGABRT, +#endif +#ifdef SIGEMT + "EMT", SIGEMT, +#endif +#ifdef SIGFPE + "FPE", SIGFPE, +#endif +#ifdef SIGKILL + "KILL", SIGKILL, +#endif +#ifdef SIGBUS + "BUS", SIGBUS, +#endif +#ifdef SIGSEGV + "SEGV", SIGSEGV, +#endif +#ifdef SIGSYS + "SYS", SIGSYS, +#endif +#ifdef SIGPIPE + "PIPE", SIGPIPE, +#endif +#ifdef SIGALRM + "ALRM", SIGALRM, +#endif +#ifdef SIGTERM + "TERM", SIGTERM, +#endif +#ifdef SIGURG + "URG", SIGURG, +#endif +#ifdef SIGSTOP + "STOP", SIGSTOP, +#endif +#ifdef SIGTSTP + "TSTP", SIGTSTP, +#endif +#ifdef SIGCONT + "CONT", SIGCONT, +#endif +#ifdef SIGCHLD + "CHLD", SIGCHLD, +#endif +#ifdef SIGCLD + "CLD", SIGCLD, +#else +# ifdef SIGCHLD + "CLD", SIGCHLD, +# endif +#endif +#ifdef SIGTTIN + "TTIN", SIGTTIN, +#endif +#ifdef SIGTTOU + "TTOU", SIGTTOU, +#endif +#ifdef SIGIO + "IO", SIGIO, +#endif +#ifdef SIGXCPU + "XCPU", SIGXCPU, +#endif +#ifdef SIGXFSZ + "XFSZ", SIGXFSZ, +#endif +#ifdef SIGVTALRM + "VTALRM", SIGVTALRM, +#endif +#ifdef SIGPROF + "PROF", SIGPROF, +#endif +#ifdef SIGWINCH + "WINCH", SIGWINCH, +#endif +#ifdef SIGUSR1 + "USR1", SIGUSR1, +#endif +#ifdef SIGUSR2 + "USR2", SIGUSR2, +#endif +#ifdef SIGLOST + "LOST", SIGLOST, +#endif +#ifdef SIGMSG + "MSG", SIGMSG, +#endif +#ifdef SIGPWR + "PWR", SIGPWR, +#endif +#ifdef SIGPOLL + "POLL", SIGPOLL, +#endif +#ifdef SIGDANGER + "DANGER", SIGDANGER, +#endif +#ifdef SIGMIGRATE + "MIGRATE", SIGMIGRATE, +#endif +#ifdef SIGPRE + "PRE", SIGPRE, +#endif +#ifdef SIGGRANT + "GRANT", SIGGRANT, +#endif +#ifdef SIGRETRACT + "RETRACT", SIGRETRACT, +#endif +#ifdef SIGSOUND + "SOUND", SIGSOUND, +#endif + NULL, 0, +}; + +static int +signm2signo(nm) + char *nm; +{ + struct signals *sigs; + + for (sigs = siglist; sigs->signm; sigs++) + if (strcmp(sigs->signm, nm) == 0) + return sigs->signo; + return 0; +} + +VALUE +f_kill(argc, argv) + int argc; + VALUE *argv; +{ + int sig; + int i; + char *s; + + rb_secure(2); + if (argc < 2) + ArgError("wrong # of arguments -- kill(sig, pid...)"); + switch (TYPE(argv[0])) { + case T_FIXNUM: + sig = FIX2UINT(argv[0]); + if (sig >= NSIG) { + s = rb_id2name(sig); + if (!s) ArgError("Bad signal"); + goto str_signal; + } + break; + + case T_STRING: + { + int negative = 0; + + s = RSTRING(argv[0])->ptr; + if (s[0] == '-') { + negative++; + s++; + } + str_signal: + if (strncmp("SIG", s, 3) == 0) + s += 3; + if((sig = signm2signo(s)) == 0) + ArgError("Unrecognized signal name `%s'", s); + + if (negative) + sig = -sig; + } + break; + + default: + ArgError("bad signal type %s", rb_class2name(CLASS_OF(argv[0]))); + break; + } + + if (sig < 0) { + sig = -sig; + for (i=1; i<argc; i++) { + int pid = NUM2INT(argv[i]); +#ifdef HAS_KILLPG + if (killpg(pid, sig) < 0) +#else + if (kill(-pid, sig) < 0) +#endif + rb_sys_fail(0); + } + } + else { + for (i=1; i<argc; i++) { + Check_Type(argv[i], T_FIXNUM); + if (kill(FIX2UINT(argv[i]), sig) < 0) + rb_sys_fail(0); + } + } + return INT2FIX(i-1); +} + +static VALUE trap_list[NSIG]; +static int trap_pending_list[NSIG]; +int trap_pending; +int trap_immediate; +int prohibit_interrupt; + +void +gc_mark_trap_list() +{ + int i; + + for (i=0; i<NSIG; i++) { + if (trap_list[i]) + gc_mark(trap_list[i]); + } +} + +#ifdef POSIX_SIGNAL +void +posix_signal(signum, handler) + int signum; + RETSIGTYPE (*handler)(); +{ + struct sigaction sigact; + + sigact.sa_handler = handler; + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigaction(signum, &sigact, 0); +} +#endif + +static RETSIGTYPE +sighandle(sig) + int sig; +{ + if (sig >= NSIG ||(sig != SIGINT && !trap_list[sig])) + Bug("trap_handler: Bad signal %d", sig); + +#if !defined(POSIX_SIGNAL) && !defined(BSD_SIGNAL) + signal(sig, sighandle); +#endif + + if (trap_immediate) { + trap_immediate = 0; + if (sig == SIGINT && !trap_list[SIGINT]) { +#ifdef THREAD + thread_interrupt(); +#else + rb_interrupt(); +#endif + } + rb_trap_eval(trap_list[sig], sig); + trap_immediate = 1; + } + else { + trap_pending++; + trap_pending_list[sig]++; + } +} + +#ifdef SIGBUS +static RETSIGTYPE +sigbus(sig) + int sig; +{ + Bug("Bus Error"); +} +#endif + +#ifdef SIGSEGV +static RETSIGTYPE +sigsegv(sig) + int sig; +{ + Bug("Segmentation fault"); +} +#endif + +void +rb_trap_exit() +{ + if (trap_list[0]) + rb_eval_cmd(trap_list[0], ary_new3(1, INT2FIX(0))); +} + +void +rb_trap_exec() +{ + int i; + + for (i=0; i<NSIG; i++) { + if (trap_pending_list[i]) { + trap_pending_list[i] = 0; + if (i == SIGINT && trap_list[SIGINT] == 0) { +#ifdef THREAD + thread_interrupt(); +#else + rb_interrupt(); +#endif + return; + } + rb_trap_eval(trap_list[i], i); + } + } + trap_pending = 0; +} + +struct trap_arg { +#ifndef NT +# ifdef HAVE_SIGPROCMASK + sigset_t mask; +# else + int mask; +# endif +#endif + VALUE sig, cmd; +}; + +static RETSIGTYPE +sigexit() +{ + rb_exit(0); +} + +static VALUE +trap(arg) + struct trap_arg *arg; +{ + RETSIGTYPE (*func)(); + VALUE command, old; + int sig; + + func = sighandle; + command = arg->cmd; + if (NIL_P(command)) { + func = SIG_IGN; + } + else if (TYPE(command) == T_STRING) { + Check_SafeStr(command); /* taint check */ + if (RSTRING(command)->len == 0) { + func = SIG_IGN; + } + else if (RSTRING(command)->len == 7) { + if (strncmp(RSTRING(command)->ptr, "SIG_IGN", 7) == 0) { + func = SIG_IGN; + } + else if (strncmp(RSTRING(command)->ptr, "SIG_DFL", 7) == 0) { + func = SIG_DFL; + } + else if (strncmp(RSTRING(command)->ptr, "DEFAULT", 7) == 0) { + func = SIG_DFL; + } + } + else if (RSTRING(command)->len == 6) { + if (strncmp(RSTRING(command)->ptr, "IGNORE", 6) == 0) { + func = SIG_IGN; + } + } + else if (RSTRING(command)->len == 4) { + if (strncmp(RSTRING(command)->ptr, "EXIT", 4) == 0) { + func = sigexit; + } + } + } + if (func == SIG_IGN || func == SIG_DFL) { + command = 0; + } + + if (TYPE(arg->sig) == T_STRING) { + char *s = RSTRING(arg->sig)->ptr; + + if (strncmp("SIG", s, 3) == 0) + s += 3; + sig = signm2signo(s); + if (sig == 0 && strcmp(s, "EXIT") != 0) + ArgError("Invalid signal SIG%s", s); + } + else { + sig = NUM2INT(arg->sig); + } + if (sig < 0 || sig > NSIG) { + ArgError("Invalid signal no %d", sig); + } +#if defined(THREAD) && defined(HAVE_SETITIMER) && !defined(__BOW__) + if (sig == SIGVTALRM) { + ArgError("SIGVTALRM reserved for Thread; cannot set handler"); + } +#endif + if (func == SIG_DFL) { + switch (sig) { + case SIGINT: + func = sighandle; + break; +#ifdef SIGBUS + case SIGBUS: + func = sigbus; + break; +#endif +#ifdef SIGSEGV + case SIGSEGV: + func = sigsegv; + break; +#endif + } + } +#ifdef POSIX_SIGNAL + posix_signal(sig, func); +#else + signal(sig, func); +#endif + old = trap_list[sig]; + if (!old) old = Qnil; + + trap_list[sig] = command; + /* enable at least specified signal. */ +#ifndef NT +#ifdef HAVE_SIGPROCMASK + sigdelset(&arg->mask, sig); +#else + arg->mask &= ~sigmask(sig); +#endif +#endif + return old; +} + +#ifndef NT +static void +trap_ensure(arg) + struct trap_arg *arg; +{ + /* enable interrupt */ +#ifdef HAVE_SIGPROCMASK + sigprocmask(SIG_SETMASK, &arg->mask, NULL); +#else + sigsetmask(arg->mask); +#endif +} +#endif + +static VALUE +f_trap(argc, argv) + int argc; + VALUE *argv; +{ + struct trap_arg arg; + + rb_secure(2); + if (argc == 0 || argc > 2) { + ArgError("wrong # of arguments -- trap(sig, cmd)/trap(sig){...}"); + } + + arg.sig = argv[0]; + if (argc == 1) { + arg.cmd = f_lambda(); + } + else if (argc == 2) { + arg.cmd = argv[1]; + } + +#ifndef NT + /* disable interrupt */ +# ifdef HAVE_SIGPROCMASK + sigfillset(&arg.mask); + sigprocmask(SIG_BLOCK, &arg.mask, &arg.mask); +# else + arg.mask = sigblock(~0); +# endif + + return rb_ensure(trap, &arg, trap_ensure, &arg); +#else + return trap(&arg); +#endif +} + +void +Init_signal() +{ + extern VALUE mKernel; + + rb_define_global_function("trap", f_trap, -1); +#ifdef POSIX_SIGNAL + posix_signal(SIGINT, sighandle); +#else + signal(SIGINT, sighandle); +#endif +#ifdef SIGBUS + signal(SIGBUS, sigbus); +#endif +#ifdef SIGSEGV + signal(SIGSEGV, sigsegv); +#endif +} diff --git a/sprintf.c b/sprintf.c new file mode 100644 index 0000000000..0dd998b5a5 --- /dev/null +++ b/sprintf.c @@ -0,0 +1,632 @@ +/************************************************ + + sprintf.c - + + $Author$ + $Date$ + created at: Fri Oct 15 10:39:26 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include <ctype.h> + +static void fmt_setup(); + +static char* +remove_sign_bits(str, base) + char *str; + int base; +{ + char *s, *t, *end; + + s = t = str; + + if (base == 16) { + x_retry: + switch (*t) { + case 'c': + *t = '8'; + break; + case 'd': + *t = '9'; + break; + case 'e': + *t = '2'; + break; + case 'f': + if (t[1] > '8') { + t++; + goto x_retry; + } + *t = '1'; + break; + case '1': + case '3': + case '7': + if (t[1] > '8') { + t++; + goto x_retry; + } + break; + } + switch (*t) { + case '1': *t = 'f'; break; + case '2': *t = 'e'; break; + case '3': *t = 'f'; break; + case '4': *t = 'c'; break; + case '5': *t = 'd'; break; + case '6': *t = 'e'; break; + case '7': *t = 'f'; break; + } + } + else if (base == 8) { + o_retry: + switch (*t) { + case '6': + *t = '2'; + break; + case '7': + if (t[1] > '3') { + t++; + goto o_retry; + } + *t = '1'; + break; + case '1': + case '3': + if (t[1] > '3') { + t++; + goto o_retry; + } + break; + } + switch (*t) { + case '1': *t = '7'; break; + case '2': *t = '6'; break; + case '3': *t = '7'; break; + } + } + else if (base == 2) { + while (t<end && *t == '1') t++; + t--; + } + while (*t) *s++ = *t++; + *s = '\0'; + + return str; +} + +double big2dbl(); +#ifndef atof +double atof(); +#endif + +VALUE +f_sprintf(argc, argv) + int argc; + VALUE *argv; +{ + struct RString *fmt; + char *buf, *p, *end; + int blen, bsiz; + VALUE result; + +#define FNONE 0 +#define FSHARP 1 +#define FMINUS 2 +#define FPLUS 4 +#define FZERO 8 +#define FWIDTH 16 +#define FPREC 32 + + int width, prec, flags = FNONE; + VALUE str; + + +#define CHECK(l) {\ + while (blen + (l) >= bsiz) {\ + REALLOC_N(buf, char, bsiz*2);\ + bsiz*=2;\ + }\ +} + +#define PUSH(s, l) { \ + CHECK(l);\ + memcpy(&buf[blen], s, l);\ + blen += (l);\ +} + +#define GETARG() \ + ((argc == 0)?(ArgError("too few argument."),0):(argc--,((argv++)[0]))) + + fmt = (struct RString*)GETARG(); + Check_Type(fmt, T_STRING); + + blen = 0; + bsiz = 120; + buf = ALLOC_N(char, bsiz); + end = fmt->ptr + fmt->len; + + for (p = fmt->ptr; p < end; p++) { + char *t; + + for (t = p; t < end && *t != '%'; t++) ; + CHECK(t - p); + PUSH(p, t - p); + if (t >= end) { + /* end of fmt string */ + goto sprint_exit; + } + p = t + 1; /* skip `%' */ + + retry: + switch (*p) { + default: + if (isprint(*p)) + ArgError("malformed format string - %%%c", *p); + else + ArgError("malformed format string"); + break; + + case ' ': + p++; + goto retry; + + case '#': + flags |= FSHARP; + p++; + goto retry; + + case '+': + flags |= FPLUS; + p++; + goto retry; + + case '-': + flags |= FMINUS; + p++; + goto retry; + + case '0': + flags |= FZERO; + p++; + goto retry; + + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + flags |= FWIDTH; + width = 0; + for (; p < end && isdigit(*p); p++) { + width = 10 * width + (*p - '0'); + } + if (p >= end) { + ArgError("malformed format string - %%[0-9]"); + } + goto retry; + + case '*': + if (flags & FWIDTH) { + ArgError("width given twice"); + } + + flags |= FWIDTH; + width = GETARG(); + width = NUM2INT(width); + if (width < 0) { + flags |= FMINUS; + width = - width; + } + p++; + goto retry; + + case '.': + if (flags & FPREC) { + ArgError("precision given twice"); + } + + prec = 0; + p++; + if (*p == '0') flags |= FZERO; + if (*p == '*') { + prec = GETARG(); + prec = NUM2INT(prec); + if (prec > 0) + flags |= FPREC; + p++; + goto retry; + } + + for (; p < end && isdigit(*p); p++) { + prec = 10 * prec + (*p - '0'); + } + if (p >= end) { + ArgError("malformed format string - %%.[0-9]"); + } + if (prec > 0) + flags |= FPREC; + goto retry; + + case '\n': + p--; + case '\0': + case '%': + PUSH("%", 1); + break; + + case 'c': + { + VALUE val = GETARG(); + char c; + + c = NUM2INT(val) & 0xff; + PUSH(&c, 1); + } + break; + + case 's': + { + VALUE arg = GETARG(); + int len; + + str = obj_as_string(arg); + len = RSTRING(str)->len; + if (flags&FPREC) { + if (prec < len) { + CHECK(prec); + memcpy(&buf[blen], RSTRING(str)->ptr, prec); + blen += prec; + break; + } + } + if (flags&FWIDTH) { + if (width > len) { + width -= len; + CHECK(width); + if (!(flags&FMINUS)) { + while (width--) { + buf[blen++] = ' '; + } + } + memcpy(&buf[blen], RSTRING(str)->ptr, len); + blen += len; + if (flags&FMINUS) { + while (width--) { + buf[blen++] = ' '; + } + } + break; + } + } + CHECK(len); + memcpy(&buf[blen], RSTRING(str)->ptr, len); + blen += len; + } + break; + + case 'b': + case 'B': + case 'o': + case 'x': + case 'u': + { + volatile VALUE val = GETARG(); + char fbuf[32], nbuf[64], *s, *t; + int v, base, bignum = 0; + int len, slen, pos; + + bin_retry: + switch (TYPE(val)) { + case T_FIXNUM: + v = FIX2INT(val); + break; + case T_FLOAT: + val = dbl2big(RFLOAT(val)->value); + bignum = 1; + break; + case T_STRING: + val = str2inum(RSTRING(val)->ptr, 0); + goto bin_retry; + case T_BIGNUM: + bignum = 1; + break; + default: + Check_Type(val, T_FIXNUM); + break; + } + + if (*p == 'x') base = 16; + else if (*p == 'o') base = 8; + else if (*p == 'u') base = 10; + else if (*p == 'b' || *p == 'B') base = 2; + if (!bignum) { + if (*p == 'b' || *p == 'B') { + val = int2big(v); + } + else { + s = nbuf; + if (v < 0) { + strcpy(s, ".."); + s += 2; + bignum = 2; + } + sprintf(fbuf, "%%%c", *p); + sprintf(s, fbuf, v); + if (v < 0) { + char d = 0; + + remove_sign_bits(s, base); + switch (base) { + case 16: + d = 'f'; break; + case 8: + d = '7'; break; + } + if (d && *s != d) { + memmove(s+1, s, strlen(s)+1); + *s = d; + } + } + s = nbuf; + goto unsigned_format; + } + } + if (*p != 'B' && !RBIGNUM(val)->sign) { + val = big_clone(val); + big_2comp(val); + } + val = big2str(val, base); + s = RSTRING(val)->ptr; + if (*s == '-' && *p != 'B') { + remove_sign_bits(++s, base); + val = str_new(0, 3+strlen(s)); + t = RSTRING(val)->ptr; + strcpy(t, ".."); + t += 2; + switch (base) { + case 16: + if (s[0] != 'f') strcpy(t++, "f"); break; + case 8: + if (s[0] != '7') strcpy(t++, "7"); break; + } + strcpy(t, s); + bignum = 2; + } + s = RSTRING(val)->ptr; + + unsigned_format: + slen = len = strlen(s); + pos = blen; + if (flags&FWIDTH) { + if (width <= len) flags &= ~FWIDTH; + else { + slen = width; + } + } + if (flags&FPREC) { + if (prec <= len) flags &= ~FPREC; + else { + if (prec >= slen) { + flags &= ~FWIDTH; + slen = prec; + } + } + } + if (slen > len) { + int n = slen-len; + char d = ' '; + if (flags & FZERO) d = '0'; + CHECK(n); + while (n--) { + buf[blen++] = d; + } + } + if ((flags&(FWIDTH|FPREC)) == (FWIDTH|FPREC)) { + if (prec < width) { + pos = width - prec; + } + } + CHECK(len); + strcpy(&buf[blen], s); + blen += len; + t = &buf[pos]; + if (bignum == 2) { + char d = '.'; + + switch (base) { + case 16: + d = 'f'; break; + case 8: + d = '7'; break; + case '2': + d = '1'; break; + } + + if ((flags & FPREC) == 0 || prec <= len-2) { + *t++ = '.'; *t++ = '.'; + } + while (*t == ' ' || *t == '.') { + *t++ = d; + } + } + else if (flags & (FPREC|FZERO)) { + while (*t == ' ') { + *t++ = '0'; + } + } + } + break; + + case 'd': + case 'D': + case 'O': + case 'X': + { + volatile VALUE val = GETARG(); + char fbuf[32], c = *p; + int bignum = 0, base; + int v; + + if (c == 'D') c = 'd'; + int_retry: + switch (TYPE(val)) { + case T_FIXNUM: + v = FIX2INT(val); + break; + case T_FLOAT: + v = RFLOAT(val)->value; + break; + case T_STRING: + val = str2inum(RSTRING(val)->ptr, 0); + goto int_retry; + case T_BIGNUM: + if (c == 'd') base = 10; + else if (c == 'X') base = 16; + else if (c == 'O') base = 8; + val = big2str(val, base); + bignum = 1; + break; + default: + val = num2fix(val); + goto int_retry; + } + + if (bignum) { + char *s = RSTRING(val)->ptr; + int slen, len, pos_b, pos; + + slen = len = strlen(s); + pos = pos_b = blen; + if (flags&FWIDTH) { + if (width <= len) flags &= ~FWIDTH; + else { + slen = width; + } + } + if (flags&FPREC) { + if (prec <= len) flags &= ~FPREC; + else { + if (prec >= slen) { + flags &= ~FWIDTH; + slen = prec; + } + } + } + if (slen > len) { + int n = slen-len; + CHECK(n); + while (n--) { + buf[blen++] = ' '; + } + } + if ((flags&(FWIDTH|FPREC)) == (FWIDTH|FPREC)) { + if (prec < width) { + pos = width - prec; + } + } + CHECK(len); + strcpy(&buf[blen], s); + blen += len; + if (flags & (FPREC|FZERO)) { + char *t = &buf[pos]; + char *b = &buf[pos_b]; + + if (s[0] == '-') { + if (slen > len && t != b ) t[-1] = '-'; + else *t++ = '-'; + } + while (*t == ' ' || *t == '-') { + *t++ = '0'; + } + } + } + else { + int max = 11; + + if ((flags & FPREC) && prec > max) max = prec; + if ((flags & FWIDTH) && width > max) max = width; + CHECK(max); + if (v < 0 && (c == 'X' || c == 'O')) { + v = -v; + PUSH("-", 1); + } + fmt_setup(fbuf, c, flags, width, prec); + sprintf(&buf[blen], fbuf, v); + blen += strlen(&buf[blen]); + } + } + break; + + case 'f': + case 'g': + case 'e': + { + VALUE val = GETARG(); + double fval; + char fbuf[32]; + + switch (TYPE(val)) { + case T_FIXNUM: + fval = FIX2INT(val); + break; + case T_FLOAT: + fval = RFLOAT(val)->value; + break; + case T_BIGNUM: + fval = big2dbl(val); + break; + case T_STRING: + fval = atof(RSTRING(val)->ptr); + break; + default: + Check_Type(val, T_FLOAT); + break; + } + + fmt_setup(fbuf, *p, flags, width, prec); + + CHECK(22); + sprintf(&buf[blen], fbuf, fval); + blen += strlen(&buf[blen]); + } + break; + } + flags = FNONE; + } + + sprint_exit: + if (verbose && argc > 1) { + ArgError("too many argument for format string"); + } + result = str_new(buf, blen); + free(buf); + + return result; +} + +static void +fmt_setup(buf, c, flags, width, prec) + char *buf, c; + int flags, width, prec; +{ + *buf++ = '%'; + if (flags & FSHARP) *buf++ = '#'; + if (flags & FPLUS) *buf++ = '+'; + if (flags & FMINUS) *buf++ = '-'; + if (flags & FZERO) *buf++ = '0'; + + if (flags & FWIDTH) { + sprintf(buf, "%d", width); + buf += strlen(buf); + } + + if (flags & FPREC) { + sprintf(buf, ".%d", prec); + buf += strlen(buf); + } + + *buf++ = c; + *buf = '\0'; +} @@ -0,0 +1,435 @@ +/* This is a general purpose hash table package written by Peter Moore @ UCB. */ + +static char sccsid[] = "@(#) st.c 5.1 89/12/14 Crucible"; + +#include "config.h" +#include <stdio.h> +#include "st.h" + +#define ST_DEFAULT_MAX_DENSITY 5 +#define ST_DEFAULT_INIT_TABLE_SIZE 11 + + /* + * DEFAULT_MAX_DENSITY is the default for the largest we allow the + * average number of items per bin before increasing the number of + * bins + * + * DEFAULT_INIT_TABLE_SIZE is the default for the number of bins + * allocated initially + * + */ +static int numcmp(); +static int numhash(); +static struct st_hash_type type_numhash = { + numcmp, + numhash, +}; + +extern int strcmp(); +static int strhash(); +static struct st_hash_type type_strhash = { + strcmp, + strhash, +}; + +void *xmalloc(); +void *xcalloc(); +void *xrealloc(); +static void rehash(); + +#define max(a,b) ((a) > (b) ? (a) : (b)) +#define nil(type) ((type*)0) +#define alloc(type) (type*)xmalloc((unsigned)sizeof(type)) +#define Calloc(n,s) (char*)xcalloc((n),(s)) + +#define EQUAL(table, x, y) ((*table->type->compare)(x, y) == 0) + +#define do_hash(key, table) (*(table)->type->hash)((key), (table)->num_bins) + +st_table* +st_init_table_with_size(type, size) + struct st_hash_type *type; + int size; +{ + st_table *tbl; + + if (size == 0) size = ST_DEFAULT_INIT_TABLE_SIZE; + else size /= ST_DEFAULT_MAX_DENSITY*0.87; + + if (size < ST_DEFAULT_INIT_TABLE_SIZE) + size = ST_DEFAULT_INIT_TABLE_SIZE; + + tbl = alloc(st_table); + tbl->type = type; + tbl->num_entries = 0; + tbl->num_bins = size; + tbl->bins = (st_table_entry **)Calloc(size, sizeof(st_table_entry*)); + return tbl; +} + +st_table* +st_init_table(type) + struct st_hash_type *type; +{ + return st_init_table_with_size(type, 0); +} + +st_table* +st_init_numtable() +{ + return st_init_table(&type_numhash); +} + +st_table* +st_init_strtable() +{ + return st_init_table(&type_strhash); +} + +void +st_free_table(table) + st_table *table; +{ + register st_table_entry *ptr, *next; + int i; + + for(i = 0; i < table->num_bins ; i++) { + ptr = table->bins[i]; + while (ptr != nil(st_table_entry)) { + next = ptr->next; + free((char*)ptr); + ptr = next; + } + } + free((char*)table->bins); + free((char*)table); +} + +#define PTR_NOT_EQUAL(table, ptr, key) \ +(ptr != nil(st_table_entry) && !EQUAL(table, key, (ptr)->key)) + +#define FIND_ENTRY(table, ptr, hash_val) \ +ptr = (table)->bins[hash_val];\ +if (PTR_NOT_EQUAL(table, ptr, key)) {\ + while (PTR_NOT_EQUAL(table, ptr->next, key)) {\ + ptr = ptr->next;\ + }\ + ptr = ptr->next;\ +} + +int +st_lookup(table, key, value) + st_table *table; + register char *key; + char **value; +{ + int hash_val; + register st_table_entry *ptr; + + hash_val = do_hash(key, table); + + FIND_ENTRY(table, ptr, hash_val); + + if (ptr == nil(st_table_entry)) { + return 0; + } else { + if (value != nil(char*)) *value = ptr->record; + return 1; + } +} + +#define ADD_DIRECT(table, key, value, hash_val, tbl)\ +{\ + if (table->num_entries/table->num_bins > ST_DEFAULT_MAX_DENSITY) {\ + rehash(table);\ + hash_val = do_hash(key, table);\ + }\ + \ + tbl = alloc(st_table_entry);\ + \ + tbl->key = key;\ + tbl->record = value;\ + tbl->next = table->bins[hash_val];\ + table->bins[hash_val] = tbl;\ + table->num_entries++;\ +} + +int +st_insert(table, key, value) + register st_table *table; + register char *key; + char *value; +{ + int hash_val; + st_table_entry *tbl; + register st_table_entry *ptr; + + hash_val = do_hash(key, table); + + FIND_ENTRY(table, ptr, hash_val); + + if (ptr == nil(st_table_entry)) { + ADD_DIRECT(table,key,value,hash_val,tbl); + return 0; + } else { + ptr->record = value; + return 1; + } +} + +void +st_add_direct(table, key, value) + st_table *table; + char *key; + char *value; +{ + int hash_val; + st_table_entry *tbl; + + hash_val = do_hash(key, table); + ADD_DIRECT(table, key, value, hash_val, tbl); +} + +int +st_find_or_add(table, key, slot) + st_table *table; + char *key; + char ***slot; +{ + int hash_val; + st_table_entry *tbl, *ptr; + + hash_val = do_hash(key, table); + + FIND_ENTRY(table, ptr, hash_val); + + if (ptr == nil(st_table_entry)) { + ADD_DIRECT(table, key, (char*)0, hash_val, tbl) + if (slot != nil(char**)) *slot = &tbl->record; + return 0; + } else { + if (slot != nil(char**)) *slot = &ptr->record; + return 1; + } +} + +static void +rehash(table) + register st_table *table; +{ + register st_table_entry *ptr, *next, **old_bins = table->bins; + int i, old_num_bins = table->num_bins, hash_val; + + table->num_bins = 1.79*old_num_bins; + + if (table->num_bins%2 == 0) { + table->num_bins += 1; + } + + table->num_entries = 0; + table->bins = (st_table_entry **) + Calloc((unsigned)table->num_bins, sizeof(st_table_entry*)); + + for(i = 0; i < old_num_bins ; i++) { + ptr = old_bins[i]; + while (ptr != nil(st_table_entry)) { + next = ptr->next; + hash_val = do_hash(ptr->key, table); + ptr->next = table->bins[hash_val]; + table->bins[hash_val] = ptr; + table->num_entries++; + ptr = next; + } + } + free((char*)old_bins); +} + +st_table* +st_copy(old_table) + st_table *old_table; +{ + st_table *new_table; + st_table_entry *ptr, *tbl; + int i, num_bins = old_table->num_bins; + + new_table = alloc(st_table); + if (new_table == nil(st_table)) { + return nil(st_table); + } + + *new_table = *old_table; + new_table->bins = (st_table_entry**) + Calloc((unsigned)num_bins, sizeof(st_table_entry*)); + + if (new_table->bins == nil(st_table_entry*)) { + free((char*)new_table); + return nil(st_table); + } + + for(i = 0; i < num_bins ; i++) { + new_table->bins[i] = nil(st_table_entry); + ptr = old_table->bins[i]; + while (ptr != nil(st_table_entry)) { + tbl = alloc(st_table_entry); + if (tbl == nil(st_table_entry)) { + free((char*)new_table->bins); + free((char*)new_table); + return nil(st_table); + } + *tbl = *ptr; + tbl->next = new_table->bins[i]; + new_table->bins[i] = tbl; + ptr = ptr->next; + } + } + return new_table; +} + +int +st_delete(table, key, value) + register st_table *table; + register char **key; + char **value; +{ + int hash_val; + st_table_entry *tmp; + register st_table_entry *ptr; + + hash_val = do_hash(*key, table); + + ptr = table->bins[hash_val]; + + if (ptr == nil(st_table_entry)) { + if (value != nil(char*)) *value = nil(char); + return 0; + } + + if (EQUAL(table, *key, ptr->key)) { + table->bins[hash_val] = ptr->next; + table->num_entries--; + if (value != nil(char*)) *value = ptr->record; + *key = ptr->key; + free((char*)ptr); + return 1; + } + + for(; ptr->next != nil(st_table_entry); ptr = ptr->next) { + if (EQUAL(table, ptr->next->key, *key)) { + tmp = ptr->next; + ptr->next = ptr->next->next; + table->num_entries--; + if (value != nil(char*)) *value = tmp->record; + *key = tmp->key; + free((char*)tmp); + return 1; + } + } + + return 0; +} + +int +st_delete_safe(table, key, value, never) + register st_table *table; + register char **key; + char **value; + char *never; +{ + int hash_val; + register st_table_entry *ptr; + + hash_val = do_hash(*key, table); + + ptr = table->bins[hash_val]; + + if (ptr == nil(st_table_entry)) { + if (value != nil(char*)) *value = nil(char); + return 0; + } + + if (EQUAL(table, *key, ptr->key)) { + table->num_entries--; + *key = ptr->key; + if (value != nil(char*)) *value = ptr->record; + ptr->key = ptr->record = never; + return 1; + } + + for(; ptr->next != nil(st_table_entry); ptr = ptr->next) { + if (EQUAL(table, ptr->next->key, *key)) { + table->num_entries--; + *key = ptr->key; + if (value != nil(char*)) *value = ptr->record; + ptr->key = ptr->record = never; + return 1; + } + } + + return 0; +} + +void +st_foreach(table, func, arg) + st_table *table; + enum st_retval (*func)(); + char *arg; +{ + st_table_entry *ptr, *last, *tmp; + enum st_retval retval; + int i; + + for(i = 0; i < table->num_bins; i++) { + last = nil(st_table_entry); + for(ptr = table->bins[i]; ptr != nil(st_table_entry);) { + retval = (*func)(ptr->key, ptr->record, arg); + switch (retval) { + case ST_CONTINUE: + last = ptr; + ptr = ptr->next; + break; + case ST_STOP: + return; + case ST_DELETE: + tmp = ptr; + if (last == nil(st_table_entry)) { + table->bins[i] = ptr->next; + } else { + last->next = ptr->next; + } + ptr = ptr->next; + free((char*)tmp); + table->num_entries--; + } + } + } +} + +static int +strhash(string, modulus) + register char *string; + int modulus; +{ + register int val = 0; + register int c; + + while ((c = *string++) != '\0') { + val = val*997 + c; + } + + return ((val < 0) ? -val : val)%modulus; +} + +static int +numcmp(x, y) + int x, y; +{ + return x != y; +} + +static int +numhash(n, modulus) + int n; + int modulus; +{ + return n % modulus; +} @@ -0,0 +1,52 @@ +/* This is a general purpose hash table package written by Peter Moore @ UCB. */ + +/* @(#) st.h 5.1 89/12/14 */ + +#ifndef ST_INCLUDED + +#define ST_INCLUDED + +typedef struct st_table_entry st_table_entry; + +struct st_table_entry { + char *key; + char *record; + st_table_entry *next; +}; + +typedef struct st_table st_table; + +struct st_hash_type { + int (*compare)(); + int (*hash)(); +}; + +struct st_table { + struct st_hash_type *type; + int num_bins; + int num_entries; + st_table_entry **bins; +}; + +#define st_is_member(table,key) st_lookup(table,key,(char **) 0) + +enum st_retval {ST_CONTINUE, ST_STOP, ST_DELETE}; + +st_table *st_init_table(); +st_table *st_init_table_with_size(); +st_table *st_init_numtable(); +st_table *st_init_strtable(); +int st_delete(), st_delete_safe(), st_insert(); +int st_lookup(), st_find_or_add(); +void st_foreach(), st_add_direct(), st_free_table(); +st_table *st_copy(); + +#define ST_NUMCMP ((int (*)()) 0) +#define ST_NUMHASH ((int (*)()) -2) + +#define st_numcmp ST_NUMCMP +#define st_numhash ST_NUMHASH + +int st_strhash(); + +#endif /* ST_INCLUDED */ diff --git a/string.c b/string.c new file mode 100644 index 0000000000..64b5347833 --- /dev/null +++ b/string.c @@ -0,0 +1,2302 @@ +/************************************************ + + string.c - + + $Author$ + $Date$ + created at: Mon Aug 9 17:12:58 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include "re.h" + +#define BEG(no) regs->beg[no] +#define END(no) regs->end[no] + +#include <stdio.h> +#include <ctype.h> + +VALUE cString; + +#define STRLEN(s) RSTRING(s)->len + +#define STR_FREEZE FL_USER1 +#define STR_TAINT FL_USER2 +VALUE str_taint(); +VALUE str_tainted(); + +VALUE +str_new(ptr, len) + UCHAR *ptr; + UINT len; +{ + NEWOBJ(str, struct RString); + OBJSETUP(str, cString, T_STRING); + + if (rb_safe_level() >= 3) { + FL_SET(str, STR_TAINT); + } + str->len = len; + str->orig = 0; + str->ptr = ALLOC_N(char,len+1); + if (ptr) { + memcpy(str->ptr, ptr, len); + } + str->ptr[len] = '\0'; + return (VALUE)str; +} + +VALUE +str_new2(ptr) + UCHAR *ptr; +{ + return str_new(ptr, strlen(ptr)); +} + +VALUE +str_new3(str) + struct RString *str; +{ + NEWOBJ(str2, struct RString); + OBJSETUP(str2, cString, T_STRING); + + str2->len = str->len; + str2->ptr = str->ptr; + str2->orig = str; + + if (rb_safe_level() >= 3) { + FL_SET(str2, STR_TAINT); + } + + return (VALUE)str2; +} + +VALUE +str_new4(orig) + struct RString *orig; +{ + NEWOBJ(str, struct RString); + OBJSETUP(str, cString, T_STRING); + + str->len = orig->len; + str->ptr = orig->ptr; + if (orig->orig) { + str->orig = orig->orig; + } + else { + orig->orig = str; + str->orig = 0; + } + if (rb_safe_level() >= 3) { + FL_SET(str, STR_TAINT); + } + + return (VALUE)str; +} + +#define as_str(str) (struct RString*)obj_as_string(str) + +static ID pr_str; + +VALUE +obj_as_string(obj) + VALUE obj; +{ + VALUE str; + + if (TYPE(obj) == T_STRING) { + return obj; + } + str = rb_funcall(obj, pr_str, 0); + if (TYPE(str) != T_STRING) + return any_to_s(obj); + return str; +} + +static VALUE +str_clone(orig) + struct RString *orig; +{ + VALUE str; + + if (orig->orig) + str = str_new3(orig->orig); + else + str = str_new(orig->ptr, orig->len); + CLONESETUP(str, orig); + return str; +} + +VALUE +str_dup(str) + struct RString *str; +{ + VALUE s = str_new(str->ptr, str->len); + if (str_tainted(str)) s = str_taint(s); + return s; +} + +static VALUE +str_s_new(class, orig) + VALUE class; + struct RString *orig; +{ + NEWOBJ(str, struct RString); + OBJSETUP(str, class, T_STRING); + + orig = as_str(orig); + str->len = orig->len; + str->ptr = ALLOC_N(char, orig->len+1); + if (str->ptr) { + memcpy(str->ptr, orig->ptr, orig->len); + } + str->ptr[orig->len] = '\0'; + str->orig = 0; + + if (rb_safe_level() >= 3) { + FL_SET(str, STR_TAINT); + } + + return (VALUE)str; +} + +static VALUE +str_length(str) + struct RString *str; +{ + return INT2FIX(str->len); +} + +VALUE +str_plus(str1, str2) + struct RString *str1, *str2; +{ + struct RString *str3; + + str2 = as_str(str2); + str3 = (struct RString*)str_new(0, str1->len+str2->len); + memcpy(str3->ptr, str1->ptr, str1->len); + memcpy(str3->ptr+str1->len, str2->ptr, str2->len); + str3->ptr[str3->len] = '\0'; + + if (str_tainted(str1) || str_tainted(str2)) + return str_taint(str3); + return (VALUE)str3; +} + +VALUE +str_times(str, times) + struct RString *str; + VALUE times; +{ + struct RString *str2; + int i, len; + + len = NUM2INT(times); + if (len < 0) { + ArgError("negative argument"); + } + + str2 = (struct RString*)str_new(0, str->len*len); + for (i=0; i<len; i++) { + memcpy(str2->ptr+(i*str->len), str->ptr, str->len); + } + str2->ptr[str2->len] = '\0'; + + if (str_tainted(str)) + return str_taint(str2); + + return (VALUE)str2; +} + +VALUE +str_substr(str, start, len) + struct RString *str; + int start, len; +{ + struct RString *str2; + + if (start < 0) { + start = str->len + start; + } + if (str->len <= start || len < 0) { + return str_new(0,0); + } + if (str->len < start + len) { + len = str->len - start; + } + + str2 = (struct RString*)str_new(str->ptr+start, len); + + return (VALUE)str2; +} + +static VALUE +str_subseq(str, beg, end) + struct RString *str; + int beg, end; +{ + int len; + + if ((beg > 0 && end > 0 || beg < 0 && end < 0) && beg > end) { + IndexError("end smaller than beg [%d..%d]", beg, end); + } + + if (beg < 0) { + beg = str->len + beg; + if (beg < 0) beg = 0; + } + if (end < 0) { + end = str->len + end; + if (end < 0) end = -1; + else if (str->len < end) { + end = str->len; + } + } + + if (beg >= str->len) { + return str_new(0, 0); + } + + len = end - beg + 1; + if (len < 0) { + len = 0; + } + + return str_substr(str, beg, len); +} + +extern VALUE ignorecase; + +void +str_modify(str) + struct RString *str; +{ + UCHAR *ptr; + + if (rb_safe_level() >= 5) { + extern VALUE eSecurityError; + Raise(eSecurityError, "cannot change string status"); + } + if (FL_TEST(str, STR_FREEZE)) + TypeError("can't modify frozen string"); + if (!str->orig) return; + ptr = str->ptr; + str->ptr = ALLOC_N(char, str->len+1); + if (str->ptr) { + memcpy(str->ptr, ptr, str->len); + str->ptr[str->len] = 0; + } + str->orig = 0; +} + +VALUE +str_freeze(str) + VALUE str; +{ + FL_SET(str, STR_FREEZE); + return str; +} + +static VALUE +str_frozen_p(str) + VALUE str; +{ + if (FL_TEST(str, STR_FREEZE)) + return TRUE; + return FALSE; +} + +VALUE +str_dup_freezed(str) + VALUE str; +{ + str = str_dup(str); + str_freeze(str); + return str; +} + +VALUE +str_taint(str) + VALUE str; +{ + if (TYPE(str) == T_STRING) { + FL_SET(str, STR_TAINT); + } + return str; +} + +VALUE +str_tainted(str) + VALUE str; +{ + if (FL_TEST(str, STR_TAINT)) + return TRUE; + return FALSE; +} + +VALUE +str_resize(str, len) + struct RString *str; + int len; +{ + str_modify(str); + + if (len >= 0) { + if (str->len < len || str->len - len > 1024) { + REALLOC_N(str->ptr, char, len + 1); + } + str->len = len; + str->ptr[len] = '\0'; /* sentinel */ + } + return (VALUE)str; +} + +VALUE +str_cat(str, ptr, len) + struct RString *str; + UCHAR *ptr; + UINT len; +{ + if (len > 0) { + str_modify(str); + REALLOC_N(str->ptr, char, str->len + len + 1); + if (ptr) + memcpy(str->ptr + str->len, ptr, len); + str->len += len; + str->ptr[str->len] = '\0'; /* sentinel */ + } + return (VALUE)str; +} + +static VALUE +str_concat(str1, str2) + struct RString *str1, *str2; +{ + str2 = as_str(str2); + str_cat(str1, str2->ptr, str2->len); + return (VALUE)str1; +} + +int +str_hash(str) + struct RString *str; +{ + int len = str->len; + UCHAR *p = str->ptr; + int key = 0; + + if (ignorecase) { + while (len--) { + key = key*65599 + toupper(*p); + p++; + } + } + else { + while (len--) { + key = key*65599 + *p; + p++; + } + } + return key; +} + +static VALUE +str_hash_method(str) + VALUE str; +{ + int key = str_hash(str); + return INT2FIX(key); +} + +#define min(a,b) (((a)>(b))?(b):(a)) + +int +str_cmp(str1, str2) + struct RString *str1, *str2; +{ + UINT len; + int retval; + + if (ignorecase != FALSE) { + return str_cicmp(str1, str2); + } + + len = min(str1->len, str2->len); + retval = memcmp(str1->ptr, str2->ptr, len); + if (retval == 0) { + return str1->ptr[len] - str2->ptr[len]; + } + return retval; +} + +static VALUE +str_equal(str1, str2) + struct RString *str1, *str2; +{ + if (TYPE(str2) != T_STRING) + return FALSE; + + if (str1->len == str2->len + && str_cmp(str1, str2) == 0) { + return TRUE; + } + return FALSE; +} + +static VALUE +str_cmp_method(str1, str2) + VALUE str1, str2; +{ + int result; + + str2 = obj_as_string(str2); + result = str_cmp(str1, str2); + return INT2FIX(result); +} + +VALUE Freg_match(); + +static VALUE +str_match(x, y) + struct RString *x, *y; +{ + VALUE reg; + int start; + + switch (TYPE(y)) { + case T_REGEXP: + return reg_match(y, x); + + case T_STRING: + reg = reg_regcomp(y); + start = reg_search(reg, x, 0, 0); + if (start == -1) { + return FALSE; + } + return INT2FIX(start); + + default: + TypeError("type mismatch"); + break; + } +} + +static VALUE +str_match2(str) + struct RString *str; +{ + return reg_match2(reg_regcomp(str)); +} + +static int +str_index(str, sub, offset) + struct RString *str, *sub; + int offset; +{ + UCHAR *s, *e, *p; + int len; + + if (str->len - offset < sub->len) return -1; + s = str->ptr+offset; + p = sub->ptr; + len = sub->len; + e = s + str->len - len + 1; + while (s < e) { + if (*s == *(sub->ptr) && memcmp(s, p, len) == 0) { + return (s-(str->ptr)); + } + s++; + } + return -1; +} + +static VALUE +str_index_method(argc, argv, str) + int argc; + VALUE *argv; + struct RString *str; +{ + struct RString *sub; + VALUE initpos; + int pos; + + if (rb_scan_args(argc, argv, "11", &sub, &initpos) == 2) { + pos = NUM2INT(initpos); + } + else { + pos = 0; + } + + switch (TYPE(sub)) { + case T_REGEXP: + pos = reg_search(sub, str, pos, (struct re_registers *)-1); + break; + + case T_STRING: + pos = str_index(str, sub, pos); + break; + + default: + TypeError("Type mismatch: %s given", rb_class2name(CLASS_OF(sub))); + } + + if (pos == -1) return Qnil; + return INT2FIX(pos); +} + +static VALUE +str_rindex(argc, argv, str) + int argc; + VALUE *argv; + struct RString *str; +{ + struct RString *sub; + VALUE initpos; + int pos, len; + UCHAR *s, *sbeg, *t; + + if (rb_scan_args(argc, argv, "11", &sub, &initpos) == 2) { + pos = NUM2INT(initpos); + if (pos >= str->len) pos = str->len; + } + else { + pos = str->len; + } + + Check_Type(sub, T_STRING); + if (pos > str->len) return Qnil; /* substring longer than string */ + sbeg = str->ptr; s = sbeg + pos - sub->len; + t = sub->ptr; + len = sub->len; + while (sbeg <= s) { + if (*s == *t && memcmp(s, t, len) == 0) { + return INT2FIX(s - sbeg); + } + s--; + } + return Qnil; +} + +static UCHAR +succ_char(s) + UCHAR *s; +{ + char c = *s; + + /* numerics */ + if ('0' <= c && c < '9') (*s)++; + else if (c == '9') { + *s = '0'; + return '1'; + } + /* small alphabets */ + else if ('a' <= c && c < 'z') (*s)++; + else if (c == 'z') { + return *s = 'a'; + } + /* capital alphabets */ + else if ('A' <= c && c < 'Z') (*s)++; + else if (c == 'Z') { + return *s = 'A'; + } + return 0; +} + +static VALUE +str_succ(orig) + struct RString *orig; +{ + struct RString *str, *str2; + UCHAR *sbeg, *s; + char c = -1; + + str = (struct RString*)str_new(orig->ptr, orig->len); + + sbeg = str->ptr; s = sbeg + str->len - 1; + + while (sbeg <= s) { + if (isalnum(*s) && (c = succ_char(s)) == 0) break; + s--; + } + if (s < sbeg) { + if (c == -1 && str->len > 0) { + str->ptr[str->len-1] += 1; + } + else { + str2 = (struct RString*)str_new(0, str->len+1); + str2->ptr[0] = c; + memcpy(str2->ptr+1, str->ptr, str->len); + str = str2; + } + } + + if (str_tainted(orig)) + return str_taint(str); + + return (VALUE)str; +} + +VALUE +str_upto(beg, end) + VALUE beg, end; +{ + VALUE current; + + Check_Type(end, T_STRING); + if (RTEST(rb_funcall(beg, '>', 1, end))) + return Qnil; + + current = beg; + for (;;) { + rb_yield(current); + if (str_equal(current, end)) break; + current = str_succ(current); + if (RSTRING(current)->len > RSTRING(end)->len) + break; + } + + return Qnil; +} + +static VALUE +str_aref(str, indx) + struct RString *str; + VALUE indx; +{ + int idx; + + switch (TYPE(indx)) { + case T_FIXNUM: + idx = FIX2INT(indx); + + if (idx < 0) { + idx = str->len + idx; + } + if (idx < 0 || str->len <= idx) { + return Qnil; + } + return (VALUE)INT2FIX(str->ptr[idx] & 0xff); + + case T_REGEXP: + if (str_match(str, indx)) + return reg_last_match(0); + return Qnil; + + case T_STRING: + if (str_index(str, indx, 0) != -1) return indx; + return Qnil; + + default: + /* check if indx is Range */ + { + int beg, end; + if (range_beg_end(indx, &beg, &end)) { + return str_subseq(str, beg, end); + } + } + IndexError("Invalid index for string"); + } +} + +static VALUE +str_aref_method(argc, argv, str) + int argc; + VALUE *argv; + struct RString *str; +{ + VALUE arg1, arg2; + + if (rb_scan_args(argc, argv, "11", &arg1, &arg2) == 2) { + return str_substr(str, NUM2INT(arg1), NUM2INT(arg2)); + } + return str_aref(str, arg1); +} + +static void +str_replace(str, beg, len, val) + struct RString *str, *val; + int beg, len; +{ + if (len < val->len) { + /* expand string */ + REALLOC_N(str->ptr, char, str->len+val->len-len+1); + } + + if (len != val->len) { + memmove(str->ptr+beg+val->len, str->ptr+beg+len, str->len-(beg+len)); + } + memcpy(str->ptr+beg, val->ptr, val->len); + str->len += val->len - len; + str->ptr[str->len] = '\0'; +} + +/* str_replace2() understands negatice offset */ +static void +str_replace2(str, beg, end, val) + struct RString *str, *val; + int beg, end; +{ + int len; + + if ((beg > 0 && end > 0 || beg < 0 && end < 0) && beg > end) { + IndexError("end smaller than beg [%d..%d]", beg, end); + } + + if (beg < 0) { + beg = str->len + beg; + if (beg < 0) { + beg = 0; + } + } + if (str->len <= beg) { + beg = str->len; + } + if (end < 0) { + end = str->len + end; + if (end < 0) { + end = 0; + } + } + if (str->len <= end) { + end = str->len - 1; + } + len = end - beg + 1; /* length of substring */ + if (len < 0) { + len = 0; + } + + str_replace(str, beg, len, val); +} + +static VALUE +str_sub_s(str, pat, val, once) + struct RString *str; + struct RRegexp *pat; + VALUE val; + int once; +{ + VALUE result, repl; + int beg, offset, n; + struct re_registers *regs; + + switch (TYPE(pat)) { + case T_REGEXP: + break; + + case T_STRING: + pat = (struct RRegexp*)reg_regcomp(pat); + break; + + default: + /* type failed */ + Check_Type(pat, T_REGEXP); + } + + val = obj_as_string(val); + result = str_new(0,0); + offset=0; n=0; + while ((beg=reg_search(pat, str, offset, 0)) >= 0) { + n++; + + regs = RMATCH(backref_get())->regs; + str_cat(result, str->ptr+offset, beg-offset); + + repl = reg_regsub(val, str, regs); + str_cat(result, RSTRING(repl)->ptr, RSTRING(repl)->len); + if (END(0) == offset) { + /* + * Always consume at least one character of the input string + * in order to prevent infinite loops. + */ + if (str->len) str_cat(result, str->ptr+END(0), 1); + offset = END(0)+1; + } + else { + offset = END(0); + } + + if (once) break; + if (offset >= STRLEN(str)) break; + } + if (n == 0) return Qnil; + if (str->len > offset) { + str_cat(result, str->ptr+offset, str->len-offset); + } + + if (str_tainted(val)) str_taint(result); + return result; +} + +static VALUE +str_sub_f(str, pat, val, once) + struct RString *str; + VALUE pat; + VALUE val; + int once; +{ + VALUE result; + + str_modify(str); + result = str_sub_s(str, pat, val, once); + + if (NIL_P(result)) return Qnil; + str_resize(str, RSTRING(result)->len); + memcpy(str->ptr, RSTRING(result)->ptr, RSTRING(result)->len); + if (str_tainted(result)) str_taint(str); + + return (VALUE)str; +} + +static VALUE +str_sub_iter_s(str, pat, once) + struct RString *str; + VALUE pat; + int once; +{ + VALUE val, result; + int beg, offset, n, null; + struct re_registers *regs; + + if (!iterator_p()) { + ArgError("Wrong # of arguments(1 for 2)"); + } + + switch (TYPE(pat)) { + case T_REGEXP: + break; + + case T_STRING: + pat = reg_regcomp(pat); + break; + + default: + /* type failed */ + Check_Type(pat, T_REGEXP); + } + + result = str_new(0,0); + n = 0; offset = 0; + while ((beg=reg_search(pat, str, offset, 0)) >= 0) { + n++; + + null = 0; + regs = RMATCH(backref_get())->regs; + str_cat(result, str->ptr+offset, beg-offset); + + if (END(0) == offset) { + null = 1; + offset = END(0)+1; + } + else { + offset = END(0); + } + + val = rb_yield(reg_nth_match(0, backref_get())); + val = obj_as_string(val); + str_cat(result, RSTRING(val)->ptr, RSTRING(val)->len); + if (null && str->len) { + str_cat(result, str->ptr+offset-1, 1); + } + + if (once) break; + if (offset >= STRLEN(str)) break; + } + if (n == 0) return Qnil; + if (str->len > offset) { + str_cat(result, str->ptr+offset, str->len-offset); + } + + return result; +} + +static VALUE +str_sub_iter_f(str, pat, once) + struct RString *str; + VALUE pat; + int once; +{ + VALUE result; + + str_modify(str); + result = str_sub_iter_s(str, pat, once); + + if (NIL_P(result)) return Qnil; + str_resize(str, RSTRING(result)->len); + memcpy(str->ptr, RSTRING(result)->ptr, RSTRING(result)->len); + + return (VALUE)str; +} + +static VALUE +str_aset(str, indx, val) + struct RString *str; + VALUE indx, val; +{ + int idx, beg, end, offset; + + switch (TYPE(indx)) { + case T_FIXNUM: + idx = NUM2INT(indx); + if (idx < 0) { + idx = str->len + idx; + } + if (idx < 0 || str->len <= idx) { + IndexError("index %d out of range [0..%d]", idx, str->len-1); + } + str->ptr[idx] = FIX2INT(val) & 0xff; + return val; + + case T_REGEXP: + str_sub_f(str, indx, val, 0); + return val; + + case T_STRING: + for (offset=0; + (beg=str_index(str, indx, offset)) >= 0; + offset=beg+STRLEN(val)) { + end = beg + STRLEN(indx) - 1; + str_replace2(str, beg, end, val); + } + if (offset == 0) return Qnil; + return val; + + default: + /* check if indx is Range */ + { + int beg, end; + if (range_beg_end(indx, &beg, &end)) { + str_replace2(str, beg, end, val); + return val; + } + } + IndexError("Invalid index for string"); + } +} + +static VALUE +str_aset_method(argc, argv, str) + int argc; + VALUE *argv; + struct RString *str; +{ + VALUE arg1, arg2, arg3; + + str_modify(str); + + if (rb_scan_args(argc, argv, "21", &arg1, &arg2, &arg3) == 3) { + int beg, len; + + Check_Type(arg3, T_STRING); + + beg = NUM2INT(arg1); + if (beg < 0) { + beg = str->len + beg; + if (beg < 0) beg = 0; + } + len = NUM2INT(arg2); + if (len < 0) IndexError("negative length %d", len); + if (beg + len > str->len) { + len = str->len - beg; + } + str_replace(str, beg, len, arg3); + return arg3; + } + return str_aset(str, arg1, arg2); +} + +static VALUE +str_sub_bang(argc, argv, str) + int argc; + VALUE *argv; + VALUE str; +{ + VALUE pat, val; + + if (rb_scan_args(argc, argv, "11", &pat, &val) == 1) { + return str_sub_iter_f(str, pat, 1); + } + return str_sub_f(str, pat, val, 1); +} + +static VALUE +str_sub(argc, argv, str) + int argc; + VALUE *argv; + VALUE str; +{ + VALUE pat, val, v; + + if (rb_scan_args(argc, argv, "11", &pat, &val) == 1) { + v = str_sub_iter_s(str, pat, 1); + } + else { + v = str_sub_s(str, pat, val, 1); + } + if (NIL_P(v)) return str; + return v; +} + +static VALUE +str_gsub_bang(argc, argv, str) + int argc; + VALUE *argv; + VALUE str; +{ + VALUE pat, val; + + if (rb_scan_args(argc, argv, "11", &pat, &val) == 1) { + return str_sub_iter_f(str, pat, 0); + } + return str_sub_f(str, pat, val, 0); +} + +static VALUE +str_gsub(argc, argv, str) + int argc; + VALUE *argv; + VALUE str; +{ + VALUE pat, val, v; + + if (rb_scan_args(argc, argv, "11", &pat, &val) == 1) { + v = str_sub_iter_s(str, pat, 0); + } + else { + v = str_sub_s(str, pat, val, 0); + } + if (NIL_P(v)) return str; + return v; +} + +static VALUE +uscore_get() +{ + VALUE line; + + line = lastline_get(); + if (TYPE(line) != T_STRING) { + TypeError("$_ value need to be String"); + } + return line; +} + +static VALUE +f_sub_bang(argc, argv) + int argc; + VALUE *argv; +{ + VALUE pat, val, line; + + line = uscore_get(); + if (rb_scan_args(argc, argv, "11", &pat, &val) == 1) { + return str_sub_iter_f(line, pat, 1); + } + return str_sub_f(line, pat, val, 1); +} + +static VALUE +f_sub(argc, argv) + int argc; + VALUE *argv; +{ + VALUE pat, val, line, v; + + line = uscore_get(); + if (rb_scan_args(argc, argv, "11", &pat, &val) == 1) { + v = str_sub_iter_s(line, pat, 1); + } + else { + v = str_sub_s(line, pat, val, 1); + } + if (!NIL_P(v)) { + lastline_set(v); + return v; + } + return line; +} + +static VALUE +f_gsub_bang(argc, argv) + int argc; + VALUE *argv; +{ + VALUE pat, val, line; + + line = uscore_get(); + if (rb_scan_args(argc, argv, "11", &pat, &val) == 1) { + return str_sub_iter_f(line, pat, 0); + } + return str_sub_f(line, pat, val, 0); +} + +static VALUE +f_gsub(argc, argv) + int argc; + VALUE *argv; +{ + VALUE pat, val, line, v; + + line = uscore_get(); + if (rb_scan_args(argc, argv, "11", &pat, &val) == 1) { + v = str_sub_iter_s(line, pat, 0); + } + else { + v = str_sub_s(line, pat, val, 0); + } + if (!NIL_P(v)) { + lastline_set(v); + return v; + } + return line; +} + +static VALUE +str_reverse_bang(str) + struct RString *str; +{ + UCHAR *s, *e, *p; + + s = str->ptr; + e = s + str->len - 1; + p = ALLOCA_N(char, str->len); + + while (e >= s) { + *p++ = *e--; + } + MEMCPY(str->ptr, p, char, str->len); + + return (VALUE)str; +} + +static VALUE +str_reverse(str) + struct RString *str; +{ + VALUE obj = str_new(0, str->len); + UCHAR *s, *e, *p; + + s = str->ptr; e = s + str->len - 1; + p = RSTRING(obj)->ptr; + + while (e >= s) { + *p++ = *e--; + } + + return obj; +} + +static VALUE +str_to_i(str) + struct RString *str; +{ + return str2inum(str->ptr, 10); +} + +#ifndef atof +double atof(); +#endif + +static VALUE +str_to_f(str) + struct RString *str; +{ + double f = atof(str->ptr); + + return float_new(f); +} + +static VALUE +str_to_s(str) + VALUE str; +{ + return str; +} + +VALUE +str_inspect(str) + struct RString *str; +{ +#define STRMAX 80 + UCHAR buf[STRMAX]; + UCHAR *p, *pend; + UCHAR *b; + + p = str->ptr; pend = p + str->len; + b = buf; + *b++ = '"'; + +#define CHECK(n) {\ + if (b - buf + n > STRMAX - 4) {\ + strcpy(b, "...");\ + b += 3;\ + break;\ + }\ +} + + while (p < pend) { + UCHAR c = *p++; + if (ismbchar(c) && p < pend) { + CHECK(2); + *b++ = c; + *b++ = *p++; + } + else if (c == '"') { + CHECK(2); + *b++ = '\\'; + *b++ = '"'; + } + else if (c == '\\') { + CHECK(2); + *b++ = '\\'; + *b++ = '\\'; + } + else if (isprint(c)) { + CHECK(1); + *b++ = c; + } + else if (c == '\n') { + CHECK(2); + *b++ = '\\'; + *b++ = 'n'; + } + else if (c == '\r') { + CHECK(2); + *b++ = '\\'; + *b++ = 'r'; + } + else if (c == '\t') { + CHECK(2); + *b++ = '\\'; + *b++ = 't'; + } + else if (c == '\f') { + CHECK(2); + *b++ = '\\'; + *b++ = 'f'; + } + else if (c == '\13') { + CHECK(2); + *b++ = '\\'; + *b++ = 'v'; + } + else if (c == '\007') { + CHECK(2); + *b++ = '\\'; + *b++ = 'a'; + } + else if (c == 033) { + CHECK(2); + *b++ = '\\'; + *b++ = 'e'; + } + else { + CHECK(4); + *b++ = '\\'; + sprintf(b, "%03o", c); + b += 3; + } + } + *b++ = '"'; + return str_new(buf, b - buf); +} + +static VALUE +str_upcase_bang(str) + struct RString *str; +{ + UCHAR *s, *send; + + str_modify(str); + s = str->ptr; send = s + str->len; + while (s < send) { + if (islower(*s)) { + *s = toupper(*s); + } + s++; + } + + return (VALUE)str; +} + +static VALUE +str_upcase(str) + struct RString *str; +{ + return str_upcase_bang(str_dup(str)); +} + +static VALUE +str_downcase_bang(str) + struct RString *str; +{ + UCHAR *s, *send; + + str_modify(str); + s = str->ptr; send = s + str->len; + while (s < send) { + if (isupper(*s)) { + *s = tolower(*s); + } + s++; + } + + return (VALUE)str; +} + +static VALUE +str_downcase(str) + struct RString *str; +{ + return str_downcase_bang(str_dup(str)); +} + +static VALUE +str_capitalize_bang(str) + struct RString *str; +{ + UCHAR *s, *send; + + str_modify(str); + s = str->ptr; send = s + str->len; + if (islower(*s)) + *s = toupper(*s); + while (++s < send) { + if (isupper(*s)) { + *s = tolower(*s); + } + } + return (VALUE)str; +} + +static VALUE +str_capitalize(str) + struct RString *str; +{ + return str_capitalize_bang(str_dup(str)); +} + +static VALUE +str_swapcase_bang(str) + struct RString *str; +{ + UCHAR *s, *send; + + str_modify(str); + s = str->ptr; send = s + str->len; + while (s < send) { + if (isupper(*s)) { + *s = tolower(*s); + } + else if (islower(*s)) { + *s = toupper(*s); + } + s++; + } + + return (VALUE)str; +} + +static VALUE +str_swapcase(str) + struct RString *str; +{ + return str_swapcase_bang(str_dup(str)); +} + +typedef UCHAR *USTR; + +static struct tr { + int gen, now, max; + UCHAR *p, *pend; +} trsrc, trrepl; + +static int +trnext(t) + struct tr *t; +{ + for (;;) { + if (!t->gen) { + if (t->p == t->pend) return -1; + t->now = *t->p++; + if (t->p < t->pend && *t->p == '-') { + t->p++; + if (t->p < t->pend) { + if (t->now > *(USTR)t->p) { + t->p++; + continue; + } + t->gen = 1; + t->max = *(USTR)t->p++; + } + } + return t->now; + } + else if (++t->now < t->max) { + return t->now; + } + else { + t->gen = 0; + return t->max; + } + } +} + +static VALUE str_delete_bang(); + +static VALUE +tr_trans(str, src, repl, sflag) + struct RString *str, *src, *repl; + int sflag; +{ + struct tr trsrc, trrepl; + int cflag = 0; + UCHAR trans[256]; + int i, c, c0; + UCHAR *s, *send, *t; + + Check_Type(src, T_STRING); + trsrc.p = src->ptr; trsrc.pend = trsrc.p + src->len; + if (src->len > 2 && src->ptr[0] == '^') { + cflag++; + trsrc.p++; + } + Check_Type(repl, T_STRING); + if (repl->len == 0) return str_delete_bang(str, src); + trrepl.p = repl->ptr; trrepl.pend = trrepl.p + repl->len; + trsrc.gen = trrepl.gen = 0; + trsrc.now = trrepl.now = 0; + trsrc.max = trrepl.max = 0; + + if (cflag) { + for (i=0; i<256; i++) { + trans[i] = 1; + } + while ((c = trnext(&trsrc)) >= 0) { + trans[c & 0xff] = 0; + } + for (i=0; i<256; i++) { + if (trans[i] == 0) { + trans[i] = i; + } + else { + c = trnext(&trrepl); + if (c == -1) { + trans[i] = trrepl.now; + } + else { + trans[i] = c; + } + } + } + } + else { + char r; + + for (i=0; i<256; i++) { + trans[i] = i; + } + while ((c = trnext(&trsrc)) >= 0) { + r = trnext(&trrepl); + if (r == -1) r = trrepl.now; + trans[c & 0xff] = r; + } + } + + str_modify(str); + t = s = str->ptr; send = s + str->len; + c0 = -1; + if (sflag) { + while (s < send) { + c = trans[*s++ & 0xff] & 0xff; + if (s[-1] == c || c != c0) { + c0 = (s[-1] == c)?-1:c; + *t++ = c; + } + } + } + else { + while (s < send) { + c = trans[*s++ & 0xff] & 0xff; + *t++ = c; + } + } + *t = '\0'; + if (sflag) str->len = (t - str->ptr); + + return (VALUE)str; +} + +static VALUE +str_tr_bang(str, src, repl) + VALUE str, src, repl; +{ + return tr_trans(str, src, repl, 0); +} + +static VALUE +str_tr(str, src, repl) + VALUE str, src, repl; +{ + return tr_trans(str_dup(str), src, repl, 0); +} + +static void +tr_setup_table(str, table) + struct RString *str; + UCHAR table[256]; +{ + struct tr tr; + int i, cflag = 0; + int c; + + tr.p = str->ptr; tr.pend = tr.p + str->len; + tr.gen = tr.now = tr.max = 0; + if (str->len > 1 && str->ptr[0] == '^') { + cflag++; + tr.p++; + } + + for (i=0; i<256; i++) { + table[i] = cflag ? 1 : 0; + } + while ((c = trnext(&tr)) >= 0) { + table[c & 0xff] = cflag ? 0 : 1; + } +} + +static VALUE +str_delete_bang(str1, str2) + struct RString *str1, *str2; +{ + UCHAR *s, *send, *t; + UCHAR squeez[256]; + + Check_Type(str2, T_STRING); + tr_setup_table(str2, squeez); + + str_modify(str1); + + s = t = str1->ptr; + send = s + str1->len; + while (s < send) { + if (!squeez[*s & 0xff]) { + *t++ = *s; + } + s++; + } + *t = '\0'; + str1->len = t - str1->ptr; + + return (VALUE)str1; +} + +static VALUE +str_delete(str1, str2) + struct RString *str1, *str2; +{ + return str_delete_bang(str_dup(str1), str2); +} + +static VALUE +tr_squeeze(str1, str2) + struct RString *str1, *str2; +{ + UCHAR squeez[256]; + UCHAR *s, *send, *t; + char c, save; + + if (!NIL_P(str2)) { + tr_setup_table(str2, squeez); + } + else { + int i; + + for (i=0; i<256; i++) { + squeez[i] = 1; + } + } + + str_modify(str1); + + s = t = str1->ptr; + send = s + str1->len; + save = -1; + while (s < send) { + c = *s++ & 0xff; + if (c != save || !squeez[c & 0xff]) { + *t++ = save = c; + } + } + *t = '\0'; + str1->len = t - str1->ptr; + + return (VALUE)str1; +} + +static VALUE +str_squeeze_bang(argc, argv, str1) + int argc; + VALUE *argv; + VALUE str1; +{ + VALUE str2; + + rb_scan_args(argc, argv, "01", &str2); + if (!NIL_P(str2)) { + Check_Type(str2, T_STRING); + } + return tr_squeeze(str1, str2); +} + +static VALUE +str_squeeze(argc, argv, str) + int argc; + VALUE *argv; + VALUE str; +{ + return str_squeeze_bang(argc, argv, str_dup(str)); +} + +static VALUE +str_tr_s_bang(str, src, repl) + VALUE str, src, repl; +{ + Check_Type(src, T_STRING); + Check_Type(repl, T_STRING); + + return tr_trans(str, src, repl, 1); +} + +static VALUE +str_tr_s(str, src, repl) + VALUE str, src, repl; +{ + return str_tr_s_bang(str_dup(str), src, repl); +} + +static VALUE +str_split_method(argc, argv, str) + int argc; + VALUE *argv; + struct RString *str; +{ + extern VALUE FS; + struct RRegexp *spat; + VALUE limit; + char char_sep = 0; + int beg, end, lim, i; + VALUE result, tmp; + + rb_scan_args(argc, argv, "02", &spat, &limit); + if (!NIL_P(limit)) { + lim = NUM2INT(limit); + if (lim == 0) limit = Qnil; + else if (lim == 1) return ary_new3(1, str); + i = 1; + } + + if (NIL_P(spat)) { + if (!NIL_P(FS)) { + spat = (struct RRegexp*)FS; + goto fs_set; + } + char_sep = ' '; + } + else { + switch (TYPE(spat)) { + case T_STRING: + fs_set: + if (STRLEN(spat) == 1) { + char_sep = RSTRING(spat)->ptr[0]; + } + else { + spat = (struct RRegexp*)reg_regcomp(spat); + } + break; + case T_REGEXP: + break; + default: + ArgError("split(): bad separator"); + } + } + + result = ary_new(); + beg = 0; + if (char_sep != 0) { + UCHAR *ptr = str->ptr; + int len = str->len; + UCHAR *eptr = ptr + len; + + if (char_sep == ' ') { /* AWK emulation */ + int skip = 1; + + for (end = beg = 0; ptr<eptr; ptr++) { + if (skip) { + if (isspace(*ptr)) { + beg++; + } + else { + end = beg+1; + skip = 0; + } + } + else { + if (isspace(*ptr)) { + ary_push(result, str_substr(str, beg, end-beg)); + skip = 1; + beg = end + 1; + if (!NIL_P(limit) && lim <= ++i) break; + } + else { + end++; + } + } + } + } + else { + for (end = beg = 0; ptr<eptr; ptr++) { + if (*ptr == char_sep) { + ary_push(result, str_substr(str, beg, end-beg)); + beg = end + 1; + if (!NIL_P(limit) && lim <= ++i) break; + } + end++; + } + } + } + else { + int start = beg; + int last_null = 0; + int idx; + struct re_registers *regs; + + while ((end = reg_search(spat, str, start, 0)) >= 0) { + regs = RMATCH(backref_get())->regs; + if (start == end && BEG(0) == END(0)) { + if (last_null == 1) { + if (ismbchar(str->ptr[beg])) + ary_push(result, str_substr(str, beg, 2)); + else + ary_push(result, str_substr(str, beg, 1)); + beg = start; + } + else { + start += ismbchar(str->ptr[start])?2:1; + last_null = 1; + continue; + } + } + else { + ary_push(result, str_substr(str, beg, end-beg)); + beg = start = END(0); + } + last_null = 0; + + for (idx=1; idx < regs->num_regs; idx++) { + if (BEG(idx) == -1) continue; + if (BEG(idx) == END(idx)) + tmp = str_new(0, 0); + else + tmp = str_subseq(str, BEG(idx), END(idx)-1); + ary_push(result, tmp); + } + if (!NIL_P(limit) && lim <= ++i) break; + } + } + if (str->len > beg) { + ary_push(result, str_subseq(str, beg, -1)); + } + + return result; +} + +VALUE +str_split(str, sep0) + struct RString* str; + char *sep0; +{ + VALUE sep; + + Check_Type(str, T_STRING); + sep = str_new2(sep0); + return str_split_method(1, &sep, str); +} + +static VALUE +f_split(argc, argv) + int argc; + VALUE *argv; +{ + return str_split_method(argc, argv, uscore_get()); +} + +static VALUE +str_each_line(argc, argv, str) + int argc; + VALUE *argv; + struct RString* str; +{ + extern VALUE RS; + VALUE rs; + int newline; + int rslen; + UCHAR *p = str->ptr, *pend = p + str->len, *s; + UCHAR *ptr = p; + int len = str->len; + VALUE line; + + if (rb_scan_args(argc, argv, "01", &rs) == 1) { + if (!NIL_P(rs)) Check_Type(rs, T_STRING); + } + else { + rs = RS; + } + + if (NIL_P(rs)) { + rb_yield(str); + return Qnil; + } + + rslen = RSTRING(rs)->len; + if (rslen == 0) { + newline = '\n'; + } + else { + newline = RSTRING(rs)->ptr[rslen-1]; + } + + for (s = p, p += rslen; p < pend; p++) { + if (rslen == 0 && *p == '\n') { + if (*(p+1) != '\n') continue; + while (*p == '\n') p++; + p--; + } + if (*p == newline && + (rslen <= 1 || + memcmp(RSTRING(rs)->ptr, p-rslen+1, rslen) == 0)) { + line = str_new(s, p - s + 1); + lastline_set(line); + rb_yield(line); + if (str->ptr != ptr || str->len != len) + Fail("string modified"); + s = p + 1; + } + } + + if (s != pend) { + line = str_new(s, p - s); + lastline_set(line); + rb_yield(line); + } + + return Qnil; +} + +static VALUE +str_each_byte(str) + struct RString* str; +{ + int i; + + for (i=0; i<str->len; i++) { + rb_yield(INT2FIX(str->ptr[i] & 0xff)); + } + return Qnil; +} + +static VALUE +str_chop_bang(str) + struct RString *str; +{ + str_modify(str); + + if (str->len > 0) { + str->len--; + if (str->ptr[str->len] == '\n') { + if (str->len > 0 && str->ptr[str->len-1] == '\r') { + str->len--; + } + } + str->ptr[str->len] = '\0'; + } + + return (VALUE)str; +} + +static VALUE +str_chop(str) + struct RString *str; +{ + return str_chop_bang(str_dup(str)); +} + +static VALUE +f_chop_bang(str) + struct RString *str; +{ + return str_chop_bang(uscore_get()); +} + +static VALUE +f_chop() +{ + return str_chop_bang(str_dup(uscore_get())); +} + +static VALUE +str_strip_bang(str) + struct RString *str; +{ + UCHAR *s, *t, *e; + + str_modify(str); + + s = str->ptr; + e = t = s + str->len; + /* remove spaces at head */ + while (s < t && isspace(*s)) s++; + + /* remove trailing spaces */ + t--; + while (s <= t && isspace(*t)) t--; + t++; + + str->len = t-s; + if (s > str->ptr) { + UCHAR *p = str->ptr; + + str->ptr = ALLOC_N(char, str->len+1); + memcpy(str->ptr, s, str->len); + str->ptr[str->len] = '\0'; + free(p); + } + else if (t < e) { + str->ptr[str->len] = '\0'; + } + + return (VALUE)str; +} + +static VALUE +scan_once(str, pat, start) + struct RString *str; + struct RRegexp *pat; + int *start; +{ + VALUE result; + struct re_registers *regs; + int idx; + + if (reg_search(pat, str, *start, 0) >= 0) { + regs = RMATCH(backref_get())->regs; + result = ary_new2(regs->num_regs); + for (idx=1; idx < regs->num_regs; idx++) { + if (BEG(idx) == -1) { + ary_push(result, Qnil); + } + else if (BEG(idx) == END(idx)) { + ary_push(result, str_new(0, 0)); + } + else { + ary_push(result, str_subseq(str, BEG(idx), END(idx)-1)); + } + } + if (END(0) == *start) { + *start = END(0)+1; + } + else { + *start = END(0); + } + + return result; + } + return Qnil; +} + +static VALUE +str_scan(str, pat) + struct RString *str; + struct RRegexp *pat; +{ + VALUE result; + int start = 0; + + switch (TYPE(pat)) { + case T_STRING: + pat = (struct RRegexp*)reg_regcomp(pat); + break; + case T_REGEXP: + break; + default: + Check_Type(pat, T_REGEXP); + } + + if (!iterator_p()) { + return scan_once(str, pat, &start); + } + + while (!NIL_P(result = scan_once(str, pat, &start))) { + rb_yield(result); + } + return Qnil; +} + +static VALUE +str_strip(str) + struct RString *str; +{ + return str_strip_bang(str_dup(str)); +} + +static VALUE +str_hex(str) + struct RString *str; +{ + return str2inum(str->ptr, 16); +} + +static VALUE +str_oct(str) + struct RString *str; +{ + return str2inum(str->ptr, 8); +} + +static VALUE +str_crypt(str, salt) + struct RString *str, *salt; +{ + salt = as_str(salt); + if (salt->len < 2) + ArgError("salt too short(need >2 bytes)"); + return str_new2(crypt(str->ptr, salt->ptr)); +} + +static VALUE +str_intern(str) + struct RString *str; +{ + ID id; + + if (strlen(str->ptr) != str->len) + ArgError("string contains `\0'"); + id = rb_intern(str->ptr); + return INT2FIX(id); +} + +static VALUE +str_sum(argc, argv, str) + int argc; + VALUE *argv; + struct RString *str; +{ + VALUE vbits; + int bits; + UCHAR *p, *pend; + + rb_scan_args(argc, argv, "01", &vbits); + if (NIL_P(vbits)) bits = 16; + else bits = NUM2INT(vbits); + + p = str->ptr; pend = p + str->len; + if (bits > 32) { + VALUE res = INT2FIX(0); + VALUE mod; + + mod = rb_funcall(INT2FIX(1), rb_intern("<<"), 1, INT2FIX(bits)); + mod = rb_funcall(mod, '-', 1, INT2FIX(1)); + + while (p < pend) { + res = rb_funcall(res, '+', 1, INT2FIX((UINT)*p)); + res = rb_funcall(res, '%', 1, mod); + p++; + } + return res; + } + else { + UINT res = 0; + UINT mod = (1<<bits)-1; + + while (p < pend) { + res += (UINT)*p; + res %= mod; + p++; + } + return int2inum(res); + } +} + +static VALUE +str_ljust(str, w) + struct RString *str; + VALUE w; +{ + int width = NUM2INT(w); + struct RString *res; + UCHAR *p, *pend; + + if (str->len >= width) return (VALUE)str; + res = (struct RString*)str_new(0, width); + memcpy(res->ptr, str->ptr, str->len); + p = res->ptr + str->len; pend = res->ptr + width; + while (p < pend) { + *p++ = ' '; + } + return (VALUE)res; +} + +static VALUE +str_rjust(str, w) + struct RString *str; + VALUE w; +{ + int width = NUM2INT(w); + struct RString *res; + UCHAR *p, *pend; + + if (str->len >= width) return (VALUE)str; + res = (struct RString*)str_new(0, width); + p = res->ptr; pend = p + width - str->len; + while (p < pend) { + *p++ = ' '; + } + memcpy(pend, str->ptr, str->len); + return (VALUE)res; +} + +static VALUE +str_center(str, w) + struct RString *str; + VALUE w; +{ + int width = NUM2INT(w); + struct RString *res; + UCHAR *p, *pend; + int n; + + if (str->len >= width) return (VALUE)str; + res = (struct RString*)str_new(0, width); + n = (width - str->len)/2; + p = res->ptr; pend = p + n; + while (p < pend) { + *p++ = ' '; + } + memcpy(pend, str->ptr, str->len); + p = pend + str->len; pend = res->ptr + width; + while (p < pend) { + *p++ = ' '; + } + return (VALUE)res; +} + +extern VALUE mKernel; +extern VALUE mComparable; +extern VALUE mEnumerable; + +void +Init_String() +{ + cString = rb_define_class("String", cObject); + rb_include_module(cString, mComparable); + rb_include_module(cString, mEnumerable); + rb_define_singleton_method(cString, "new", str_s_new, 1); + rb_define_method(cString, "clone", str_clone, 0); + rb_define_method(cString, "dup", str_dup, 0); + rb_define_method(cString, "<=>", str_cmp_method, 1); + rb_define_method(cString, "==", str_equal, 1); + rb_define_method(cString, "===", str_equal, 1); + rb_define_method(cString, "eql?", str_equal, 1); + rb_define_method(cString, "hash", str_hash_method, 0); + rb_define_method(cString, "+", str_plus, 1); + rb_define_method(cString, "*", str_times, 1); + rb_define_method(cString, "[]", str_aref_method, -1); + rb_define_method(cString, "[]=", str_aset_method, -1); + rb_define_method(cString, "length", str_length, 0); + rb_define_alias(cString, "size", "length"); + rb_define_method(cString, "=~", str_match, 1); + rb_define_method(cString, "~", str_match2, 0); + rb_define_method(cString, "succ", str_succ, 0); + rb_define_method(cString, "upto", str_upto, 1); + rb_define_method(cString, "index", str_index_method, -1); + rb_define_method(cString, "rindex", str_rindex, -1); + + rb_define_method(cString, "freeze", str_freeze, 0); + rb_define_method(cString, "frozen?", str_frozen_p, 0); + + rb_define_method(cString, "taint", str_taint, 0); + rb_define_method(cString, "tainted?", str_tainted, 0); + + rb_define_method(cString, "to_i", str_to_i, 0); + rb_define_method(cString, "to_f", str_to_f, 0); + rb_define_method(cString, "to_s", str_to_s, 0); + rb_define_method(cString, "inspect", str_inspect, 0); + + rb_define_method(cString, "upcase", str_upcase, 0); + rb_define_method(cString, "downcase", str_downcase, 0); + rb_define_method(cString, "capitalize", str_capitalize, 0); + rb_define_method(cString, "swapcase", str_swapcase, 0); + + rb_define_method(cString, "upcase!", str_upcase_bang, 0); + rb_define_method(cString, "downcase!", str_downcase_bang, 0); + rb_define_method(cString, "capitalize!", str_capitalize_bang, 0); + rb_define_method(cString, "swapcase!", str_swapcase_bang, 0); + + rb_define_method(cString, "hex", str_hex, 0); + rb_define_method(cString, "oct", str_oct, 0); + rb_define_method(cString, "split", str_split_method, -1); + rb_define_method(cString, "reverse", str_reverse, 0); + rb_define_method(cString, "reverse!", str_reverse_bang, 0); + rb_define_method(cString, "concat", str_concat, 1); + rb_define_method(cString, "<<", str_concat, 1); + rb_define_method(cString, "crypt", str_crypt, 1); + rb_define_method(cString, "intern", str_intern, 0); + + rb_define_method(cString, "scan", str_scan, 1); + + rb_define_method(cString, "ljust", str_ljust, 1); + rb_define_method(cString, "rjust", str_rjust, 1); + rb_define_method(cString, "center", str_center, 1); + + rb_define_method(cString, "sub", str_sub, -1); + rb_define_method(cString, "gsub", str_gsub, -1); + rb_define_method(cString, "chop", str_chop, 0); + rb_define_method(cString, "strip", str_strip, 0); + + rb_define_method(cString, "sub!", str_sub_bang, -1); + rb_define_method(cString, "gsub!", str_gsub_bang, -1); + rb_define_method(cString, "strip!", str_strip_bang, 0); + rb_define_method(cString, "chop!", str_chop_bang, 0); + + rb_define_method(cString, "tr", str_tr, 2); + rb_define_method(cString, "tr_s", str_tr_s, 2); + rb_define_method(cString, "delete", str_delete, 1); + rb_define_method(cString, "squeeze", str_squeeze, -1); + + rb_define_method(cString, "tr!", str_tr_bang, 2); + rb_define_method(cString, "tr_s!", str_tr_s_bang, 2); + rb_define_method(cString, "delete!", str_delete_bang, 1); + rb_define_method(cString, "squeeze!", str_squeeze_bang, -1); + + rb_define_method(cString, "each_line", str_each_line, -1); + rb_define_method(cString, "each", str_each_line, -1); + rb_define_method(cString, "each_byte", str_each_byte, 0); + + rb_define_method(cString, "sum", str_sum, -1); + + rb_define_global_function("sub", f_sub, -1); + rb_define_global_function("gsub", f_gsub, -1); + + rb_define_global_function("sub!", f_sub_bang, -1); + rb_define_global_function("gsub!", f_gsub_bang, -1); + + rb_define_global_function("chop", f_chop, 0); + rb_define_global_function("chop!", f_chop_bang, 0); + + rb_define_global_function("split", f_split, -1); + + pr_str = rb_intern("to_s"); +} diff --git a/struct.c b/struct.c new file mode 100644 index 0000000000..52822d9c55 --- /dev/null +++ b/struct.c @@ -0,0 +1,437 @@ +/************************************************ + + struct.c - + + $Author$ + $Date$ + created at: Tue Mar 22 18:44:30 JST 1995 + +************************************************/ + +#include "ruby.h" + +ID rb_frame_last_func(); +VALUE cStruct; +extern VALUE mEnumerable; + +static VALUE +struct_s_members(obj) + VALUE obj; +{ + struct RArray *member; + VALUE ary, *p, *pend; + + member = RARRAY(rb_iv_get(obj, "__member__")); + if (NIL_P(member)) { + Fatal("non-initialized struct"); + } + ary = ary_new2(member->len); + p = member->ptr; pend = p + member->len; + while (p < pend) { + ary_push(ary, str_new2(rb_id2name(FIX2INT(*p)))); + p++; + } + + return ary; +} + +static VALUE +struct_members(obj) + VALUE obj; +{ + return struct_s_members(CLASS_OF(obj)); +} + +VALUE +struct_getmember(obj, id) + struct RStruct *obj; + ID id; +{ + VALUE nstr, member, slot; + int i; + + nstr = CLASS_OF(obj); + member = rb_iv_get(nstr, "__member__"); + if (NIL_P(member)) { + Bug("non-initialized struct"); + } + slot = INT2FIX(id); + for (i=0; i<RARRAY(member)->len; i++) { + if (RARRAY(member)->ptr[i] == slot) { + return obj->ptr[i]; + } + } + NameError("%s is not struct member", rb_id2name(id)); + /* not reached */ +} + +static VALUE +struct_ref(obj) + VALUE obj; +{ + return struct_getmember(obj, rb_frame_last_func()); +} + +static VALUE struct_ref0(obj) struct RStruct *obj; {return obj->ptr[0];} +static VALUE struct_ref1(obj) struct RStruct *obj; {return obj->ptr[1];} +static VALUE struct_ref2(obj) struct RStruct *obj; {return obj->ptr[2];} +static VALUE struct_ref3(obj) struct RStruct *obj; {return obj->ptr[3];} +static VALUE struct_ref4(obj) struct RStruct *obj; {return obj->ptr[4];} +static VALUE struct_ref5(obj) struct RStruct *obj; {return obj->ptr[5];} +static VALUE struct_ref6(obj) struct RStruct *obj; {return obj->ptr[6];} +static VALUE struct_ref7(obj) struct RStruct *obj; {return obj->ptr[7];} +static VALUE struct_ref8(obj) struct RStruct *obj; {return obj->ptr[8];} +static VALUE struct_ref9(obj) struct RStruct *obj; {return obj->ptr[9];} + +VALUE (*ref_func[10])() = { + struct_ref0, + struct_ref1, + struct_ref2, + struct_ref3, + struct_ref4, + struct_ref5, + struct_ref6, + struct_ref7, + struct_ref8, + struct_ref9, +}; + +static VALUE +struct_set(obj, val) + struct RStruct *obj; + VALUE val; +{ + VALUE nstr, member, slot; + int i; + + nstr = CLASS_OF(obj); + member = rb_iv_get(nstr, "__member__"); + if (NIL_P(member)) { + Fatal("non-initialized struct"); + } + for (i=0; i<RARRAY(member)->len; i++) { + slot = RARRAY(member)->ptr[i]; + if (id_attrset(FIX2INT(slot)) == rb_frame_last_func()) { + return obj->ptr[i] = val; + } + } + NameError("not struct member"); + /* not reached */ +} + +VALUE struct_alloc(); + +static VALUE +make_struct(name, member) + struct RString *name; + struct RArray *member; +{ + VALUE nstr; + ID id; + int i; + + id = rb_intern(name->ptr); + if (!rb_is_const_id(id)) { + NameError("identifier %s needs to be constant", name->ptr); + } + nstr = rb_define_class_under(cStruct, name->ptr, cStruct); + rb_iv_set(nstr, "__size__", INT2FIX(member->len)); + rb_iv_set(nstr, "__member__", member); + + rb_define_singleton_method(nstr, "new", struct_alloc, -2); + rb_define_singleton_method(nstr, "[]", struct_alloc, -2); + rb_define_singleton_method(nstr, "members", struct_s_members, 0); + for (i=0; i< member->len; i++) { + ID id = FIX2INT(member->ptr[i]); + if (i<10) { + rb_define_method_id(nstr, id, ref_func[i], 0); + } + else { + rb_define_method_id(nstr, id, struct_ref, 0); + } + rb_define_method_id(nstr, id_attrset(id), struct_set, 1); + } + + return nstr; +} + +#include <varargs.h> + +VALUE +struct_define(name, va_alist) + char *name; + va_dcl +{ + va_list ar; + VALUE nm, ary; + char *mem; + + nm = str_new2(name); + ary = ary_new(); + + va_start(ar); + while (mem = va_arg(ar, char*)) { + ID slot = rb_intern(mem); + ary_push(ary, INT2FIX(slot)); + } + va_end(ar); + + return make_struct(nm, ary); +} + +static VALUE +struct_s_def(argc, argv) + int argc; + VALUE *argv; +{ + struct RString *name; + struct RArray *rest; + int i; + + rb_scan_args(argc, argv, "1*", &name, &rest); + Check_Type(name, T_STRING); + for (i=0; i<rest->len; i++) { + ID id = rb_to_id(rest->ptr[i]); + rest->ptr[i] = INT2FIX(id); + } + return make_struct(name, rest); +} + +VALUE +struct_alloc(class, values) + VALUE class; + struct RArray *values; +{ + VALUE size; + int n; + + size = rb_iv_get(class, "__size__"); + n = FIX2INT(size); + if (n < values->len) { + ArgError("struct size differs"); + } + else { + NEWOBJ(st, struct RStruct); + OBJSETUP(st, class, T_STRUCT); + st->len = n; + st->ptr = 0; /* avoid GC crashing */ + st->ptr = ALLOC_N(VALUE, n); + MEMCPY(st->ptr, values->ptr, VALUE, values->len); + memclear(st->ptr+values->len, n - values->len); + + return (VALUE)st; + } + /* not reached */ +} + +VALUE +struct_new(class, va_alist) + VALUE class; + va_dcl +{ + VALUE val, mem; + int size; + va_list args; + + val = rb_iv_get(class, "__size__"); + size = FIX2INT(val); + mem = ary_new(); + va_start(args); + while (size--) { + val = va_arg(args, VALUE); + ary_push(mem, val); + } + va_end(args); + + return struct_alloc(class, mem); +} + +static VALUE +struct_each(s) + struct RStruct *s; +{ + int i; + + for (i=0; i<s->len; i++) { + rb_yield(s->ptr[i]); + } + return Qnil; +} + +char *rb_class2name(); + +static VALUE +struct_to_s(s) + struct RStruct *s; +{ + char *name, *buf; + + name = rb_class2name(CLASS_OF(s)); + buf = ALLOCA_N(char, strlen(name)+1); + sprintf(buf, "%s", name); + return str_new2(buf); +} + +static VALUE +struct_inspect(s) + struct RStruct *s; +{ + char *name = rb_class2name(CLASS_OF(s)); + VALUE str, member; + char buf[256]; + int i; + + member = rb_iv_get(CLASS_OF(s), "__member__"); + if (NIL_P(member)) { + Fatal("non-initialized struct"); + } + + sprintf(buf, "#<%s ", name); + str = str_new2(buf); + for (i=0; i<s->len; i++) { + VALUE str2, slot; + char *p; + + if (i > 0) { + str_cat(str, ", ", 2); + } + slot = RARRAY(member)->ptr[i]; + p = rb_id2name(FIX2INT(slot)); + str_cat(str, p, strlen(p)); + str_cat(str, "=", 1); + str2 = rb_inspect(s->ptr[i]); + str2 = obj_as_string(str2); + str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len); + } + str_cat(str, ">", 1); + + return str; +} + +static VALUE +struct_to_a(s) + struct RStruct *s; +{ + return ary_new4(s->len, s->ptr); +} + +static VALUE +struct_clone(s) + struct RStruct *s; +{ + NEWOBJ(st, struct RStruct); + CLONESETUP(st, s); + st->len = s->len; + st->ptr = 0; /* avoid GC crashing */ + st->ptr = ALLOC_N(VALUE, s->len); + MEMCPY(st->ptr, s->ptr, VALUE, st->len); + + return (VALUE)st; +} + +VALUE +struct_aref(s, idx) + struct RStruct *s; + VALUE idx; +{ + int i; + + i = NUM2INT(idx); + if (i < 0) i = s->len - i; + if (i < 0) + IndexError("offset %d too small for struct(size:%d)", i, s->len); + if (s->len <= i) + IndexError("offset %d too large for struct(size:%d)", i, s->len); + return s->ptr[i]; +} + +VALUE +struct_aset(s, idx, val) + struct RStruct *s; + VALUE idx, val; +{ + int i; + + i = NUM2INT(idx); + if (i < 0) i = s->len - i; + if (i < 0) + IndexError("offset %d too small for struct(size:%d)", i, s->len); + if (s->len <= i) + IndexError("offset %d too large for struct(size:%d)", i, s->len); + return s->ptr[i] = val; +} + +static VALUE +struct_equal(s, s2) + struct RStruct *s, *s2; +{ + int i; + + if (TYPE(s2) != T_STRUCT) return FALSE; + if (CLASS_OF(s) != CLASS_OF(s2)) return FALSE; + if (s->len != s2->len) { + Bug("inconsistent struct"); /* should never happen */ + } + + for (i=0; i<s->len; i++) { + if (!rb_equal(s->ptr[i], s2->ptr[i])) return FALSE; + } + return TRUE; +} + +static VALUE +struct_eql(s, s2) + struct RStruct *s, *s2; +{ + int i; + + if (TYPE(s2) != T_STRUCT) return FALSE; + if (CLASS_OF(s) != CLASS_OF(s2)) return FALSE; + if (s->len != s2->len) { + Bug("inconsistent struct"); /* should never happen */ + } + + for (i=0; i<s->len; i++) { + if (!rb_eql(s->ptr[i], s2->ptr[i])) return FALSE; + } + return TRUE; +} + +static VALUE +struct_hash(s) + struct RStruct *s; +{ + int i, h; + + h = CLASS_OF(s); + for (i=0; i<s->len; i++) { + h ^= rb_hash(s->ptr[i]); + } + return INT2FIX(h); +} + +void +Init_Struct() +{ + cStruct = rb_define_class("Struct", cObject); + rb_include_module(cStruct, mEnumerable); + + rb_define_singleton_method(cStruct, "new", struct_s_def, -1); + + rb_define_method(cStruct, "clone", struct_clone, 0); + + rb_define_method(cStruct, "==", struct_equal, 1); + rb_define_method(cStruct, "eql?", struct_eql, 1); + rb_define_method(cStruct, "hash", struct_hash, 0); + + rb_define_method(cStruct, "to_s", struct_to_s, 0); + rb_define_method(cStruct, "inspect", struct_inspect, 0); + rb_define_method(cStruct, "to_a", struct_to_a, 0); + rb_define_method(cStruct, "values", struct_to_a, 0); + + rb_define_method(cStruct, "each", struct_each, 0); + rb_define_method(cStruct, "[]", struct_aref, 1); + rb_define_method(cStruct, "[]=", struct_aset, 2); + + rb_define_method(cStruct, "members", struct_members, 0); +} diff --git a/time.c b/time.c new file mode 100644 index 0000000000..b1184fbe98 --- /dev/null +++ b/time.c @@ -0,0 +1,795 @@ +/************************************************ + + time.c - + + $Author$ + $Date$ + created at: Tue Dec 28 14:31:59 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include <sys/types.h> + +#ifdef HAVE_STRING_H +# include <string.h> +#endif + +#include <time.h> +#ifndef NT +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#else +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; +#endif +#endif /* NT */ + +#ifdef HAVE_SYS_TIMES_H +#include <sys/times.h> +#endif +#include <math.h> + +static VALUE cTime; +#if defined(HAVE_TIMES) || defined(NT) +static VALUE S_Tms; +#endif +extern VALUE mComparable; + +struct time_object { + struct timeval tv; + struct tm tm; +#ifndef HAVE_TM_ZONE + int gmt; +#endif + int tm_got; +}; + +#define GetTimeval(obj, tobj) {\ + Data_Get_Struct(obj, struct time_object, tobj);\ +} + +static VALUE +time_s_now(class) + VALUE class; +{ + VALUE obj; + struct time_object *tobj; + + obj = Data_Make_Struct(class, struct time_object, 0, 0, tobj); + tobj->tm_got=0; + + if (gettimeofday(&(tobj->tv), 0) == -1) { + rb_sys_fail("gettimeofday"); + } + + return obj; +} + +static VALUE +time_new_internal(class, sec, usec) + VALUE class; + int sec, usec; +{ + VALUE obj; + struct time_object *tobj; + + obj = Data_Make_Struct(class, struct time_object, 0, 0, tobj); + tobj->tm_got = 0; + tobj->tv.tv_sec = sec; + tobj->tv.tv_usec = usec; + + return obj; +} + +VALUE +time_new(sec, usec) + int sec, usec; +{ + return time_new_internal(cTime, sec, usec); +} + +struct timeval +time_timeval(time) + VALUE time; +{ + struct time_object *tobj; + struct timeval t; + + switch (TYPE(time)) { + case T_FIXNUM: + t.tv_sec = FIX2UINT(time); + if (t.tv_sec < 0) + ArgError("time must be positive"); + t.tv_usec = 0; + break; + + case T_FLOAT: + { + double seconds, microseconds; + + if (RFLOAT(time)->value < 0.0) + ArgError("time must be positive"); + seconds = floor(RFLOAT(time)->value); + microseconds = (RFLOAT(time)->value - seconds) * 1000000.0; + t.tv_sec = seconds; + t.tv_usec = microseconds; + } + break; + + case T_BIGNUM: + t.tv_sec = NUM2INT(time); + t.tv_usec = 0; + break; + + default: + if (!obj_is_kind_of(time, cTime)) { + TypeError("Can't convert %s into Time", + rb_class2name(CLASS_OF(time))); + } + GetTimeval(time, tobj); + t = tobj->tv; + break; + } + return t; +} + +static VALUE +time_s_at(class, time) + VALUE class, time; +{ + struct timeval tv; + + tv = time_timeval(time); + return time_new_internal(class, tv.tv_sec, tv.tv_usec); +} + +static char *months [12] = { + "jan", "feb", "mar", "apr", "may", "jun", + "jul", "aug", "sep", "oct", "nov", "dec", +}; + +static void +time_arg(argc, argv, args) + int argc; + VALUE *argv; + int *args; +{ + VALUE v[6]; + int i; + + rb_scan_args(argc, argv, "15", &v[0], &v[1], &v[2], &v[3], &v[4], &v[5]); + + args[0] = NUM2INT(v[0]); + if (args[0] < 70) args[0] += 100; + if (args[0] > 1900) args[0] -= 1900; + if (v[1] == Qnil) { + args[1] = 0; + } + else if (TYPE(v[1]) == T_STRING) { + args[1] = -1; + for (i=0; i<12; i++) { + if (strcasecmp(months[i], RSTRING(v[1])->ptr) == 0) { + args[1] = i; + break; + } + } + if (args[1] == -1) { + char c = RSTRING(v[1])->ptr[0]; + + if ('0' <= c && c <= '9') { + args[1] = NUM2INT(v[1])-1; + } + } + } + else { + args[1] = NUM2INT(v[1]); + } + if (v[2] == Qnil) { + args[2] = 1; + } + else { + args[2] = NUM2INT(v[2]); + } + for (i=3;i<6;i++) { + if (v[i] == Qnil) { + args[i] = 0; + } + else { + args[i] = NUM2INT(v[i]); + } + } + + /* value validation */ + if ( args[0] < 70|| args[1] > 137 + || args[1] < 0 || args[1] > 11 + || args[2] < 1 || args[2] > 31 + || args[3] < 0 || args[3] > 23 + || args[4] < 0 || args[4] > 60 + || args[5] < 0 || args[5] > 61) + ArgError("argument out of range"); +} + +static VALUE +time_gm_or_local(argc, argv, gm_or_local, class) + int argc; + VALUE *argv; + int gm_or_local; + VALUE class; +{ + int args[6]; + struct timeval tv; + struct tm *tm; + time_t guess, t; + int diff; + struct tm *(*fn)(); + + fn = (gm_or_local) ? gmtime : localtime; + time_arg(argc, argv, args); + + gettimeofday(&tv, 0); + guess = tv.tv_sec; + + tm = (*fn)(&guess); + if (!tm) goto error; + t = args[0]; + while (diff = t - tm->tm_year) { + guess += diff * 364 * 24 * 3600; + if (guess < 0) ArgError("too far future"); + tm = (*fn)(&guess); + if (!tm) goto error; + } + t = args[1]; + while (diff = t - tm->tm_mon) { + guess += diff * 27 * 24 * 3600; + tm = (*fn)(&guess); + if (!tm) goto error; + } + guess += (args[2] - tm->tm_mday) * 3600 * 24; + guess += (args[3] - tm->tm_hour) * 3600; + guess += (args[4] - tm->tm_min) * 60; + guess += args[5] - tm->tm_sec; + + return time_new_internal(class, guess, 0); + + error: + ArgError("gmtime error"); +} + +static VALUE +time_s_timegm(argc, argv, class) + int argc; + VALUE *argv; + VALUE class; +{ + return time_gm_or_local(argc, argv, 1, class); +} + +static VALUE +time_s_timelocal(argc, argv, class) + int argc; + VALUE *argv; + VALUE class; +{ + return time_gm_or_local(argc, argv, 0, class); +} + +static VALUE +time_to_i(time) + VALUE time; +{ + struct time_object *tobj; + + GetTimeval(time, tobj); + return int2inum(tobj->tv.tv_sec); +} + +static VALUE +time_to_f(time) + VALUE time; +{ + struct time_object *tobj; + + GetTimeval(time, tobj); + return float_new((double)tobj->tv.tv_sec+(double)tobj->tv.tv_usec/1000000); +} + +static VALUE +time_usec(time) + VALUE time; +{ + struct time_object *tobj; + + GetTimeval(time, tobj); + return INT2FIX(tobj->tv.tv_usec); +} + +static VALUE +time_cmp(time1, time2) + VALUE time1, time2; +{ + struct time_object *tobj1, *tobj2; + int i; + + GetTimeval(time1, tobj1); + switch (TYPE(time2)) { + case T_FIXNUM: + i = FIX2INT(time2); + if (tobj1->tv.tv_sec == i) return INT2FIX(0); + if (tobj1->tv.tv_sec > i) return INT2FIX(1); + return FIX2INT(-1); + + case T_FLOAT: + { + double t; + + if (tobj1->tv.tv_sec == (int)RFLOAT(time2)->value) return INT2FIX(0); + t = (double)tobj1->tv.tv_sec + (double)tobj1->tv.tv_usec*1e-6; + if (tobj1->tv.tv_sec == RFLOAT(time2)->value) return INT2FIX(0); + if (tobj1->tv.tv_sec > RFLOAT(time2)->value) return INT2FIX(1); + return FIX2INT(-1); + } + } + + if (obj_is_instance_of(time2, cTime)) { + GetTimeval(time2, tobj2); + if (tobj1->tv.tv_sec == tobj2->tv.tv_sec) { + if (tobj1->tv.tv_usec == tobj2->tv.tv_usec) return INT2FIX(0); + if (tobj1->tv.tv_usec > tobj2->tv.tv_usec) return INT2FIX(1); + return FIX2INT(-1); + } + if (tobj1->tv.tv_sec > tobj2->tv.tv_sec) return INT2FIX(1); + return FIX2INT(-1); + } + i = NUM2INT(time2); + if (tobj1->tv.tv_sec == i) return INT2FIX(0); + if (tobj1->tv.tv_sec > i) return INT2FIX(1); + return FIX2INT(-1); +} + +static VALUE +time_eql(time1, time2) + VALUE time1, time2; +{ + struct time_object *tobj1, *tobj2; + + GetTimeval(time1, tobj1); + if (obj_is_instance_of(time2, cTime)) { + GetTimeval(time2, tobj2); + if (tobj1->tv.tv_sec == tobj2->tv.tv_sec) { + if (tobj1->tv.tv_usec == tobj2->tv.tv_usec) return TRUE; + } + } + return FALSE; +} + +static VALUE +time_hash(time) + VALUE time; +{ + struct time_object *tobj; + int hash; + + GetTimeval(time, tobj); + hash = tobj->tv.tv_sec ^ tobj->tv.tv_usec; + return INT2FIX(hash); +} + +static VALUE +time_localtime(time) + VALUE time; +{ + struct time_object *tobj; + struct tm *tm_tmp; + + GetTimeval(time, tobj); + tm_tmp = localtime((const time_t*)&tobj->tv.tv_sec); + tobj->tm = *tm_tmp; + tobj->tm_got = 1; +#ifndef HAVE_TM_ZONE + tobj->gmt = 0; +#endif + return time; +} + +static VALUE +time_gmtime(time) + VALUE time; +{ + struct time_object *tobj; + struct tm *tm_tmp; + + GetTimeval(time, tobj); + tm_tmp = gmtime((const time_t*)&tobj->tv.tv_sec); + tobj->tm = *tm_tmp; + tobj->tm_got = 1; +#ifndef HAVE_TM_ZONE + tobj->gmt = 1; +#endif + return time; +} + +static VALUE +time_asctime(time) + VALUE time; +{ + struct time_object *tobj; + char buf[64]; + int len; + + GetTimeval(time, tobj); + if (tobj->tm_got == 0) { + time_localtime(time); + } +#ifndef HAVE_TM_ZONE + if (tobj->gmt == 1) { + len = strftime(buf, 64, "%a %b %d %H:%M:%S GMT %Y", &(tobj->tm)); + } + else +#endif + { + len = strftime(buf, 64, "%a %b %d %H:%M:%S %Z %Y", &(tobj->tm)); + } + return str_new(buf, len); +} + +static VALUE +time_coerce(time1, time2) + VALUE time1, time2; +{ + if (TYPE(time2) == T_FLOAT) { + double d = RFLOAT(time2)->value; + unsigned int i = (unsigned int) d; + + return assoc_new(time_new(i, (int)((d - (double)i)*1e6)),time1); + } + + return assoc_new(time_new(NUM2INT(time2), 0), time1); +} + +static VALUE +time_plus(time1, time2) + VALUE time1, time2; +{ + struct time_object *tobj1, *tobj2; + int sec, usec; + + GetTimeval(time1, tobj1); + if (TYPE(time2) == T_FLOAT) { + unsigned int nsec = (unsigned int)RFLOAT(time2)->value; + sec = tobj1->tv.tv_sec + nsec; + usec = tobj1->tv.tv_usec + (RFLOAT(time2)->value - (double)nsec)*1e6; + } + else if (obj_is_instance_of(time2, cTime)) { + GetTimeval(time2, tobj2); + sec = tobj1->tv.tv_sec + tobj2->tv.tv_sec; + usec = tobj1->tv.tv_usec + tobj2->tv.tv_usec; + } + else { + sec = tobj1->tv.tv_sec + NUM2INT(time2); + usec = tobj1->tv.tv_usec; + } + + if (usec >= 1000000) { /* usec overflow */ + sec++; + usec -= 1000000; + } + return time_new(sec, usec); +} + +static VALUE +time_minus(time1, time2) + VALUE time1, time2; +{ + struct time_object *tobj1, *tobj2; + int sec, usec; + + GetTimeval(time1, tobj1); + if (obj_is_instance_of(time2, cTime)) { + double f; + + GetTimeval(time2, tobj2); + f = tobj1->tv.tv_sec - tobj2->tv.tv_sec; + + f += (tobj1->tv.tv_usec - tobj2->tv.tv_usec)*1e-6; + + return float_new(f); + } + else if (TYPE(time2) == T_FLOAT) { + sec = tobj1->tv.tv_sec - (int)RFLOAT(time2)->value; + usec = tobj1->tv.tv_usec - (RFLOAT(time2)->value - (double)sec)*1e6; + } + else { + sec = tobj1->tv.tv_sec - NUM2INT(time2); + usec = tobj1->tv.tv_usec; + } + + if (usec < 0) { /* usec underflow */ + sec--; + usec += 1000000; + } + return time_new(sec, usec); +} + +static VALUE +time_sec(time) + VALUE time; +{ + struct time_object *tobj; + + GetTimeval(time, tobj); + if (tobj->tm_got == 0) { + time_localtime(time); + } + return INT2FIX(tobj->tm.tm_sec); +} + +static VALUE +time_min(time) + VALUE time; +{ + struct time_object *tobj; + + GetTimeval(time, tobj); + if (tobj->tm_got == 0) { + time_localtime(time); + } + return INT2FIX(tobj->tm.tm_min); +} + +static VALUE +time_hour(time) + VALUE time; +{ + struct time_object *tobj; + + GetTimeval(time, tobj); + if (tobj->tm_got == 0) { + time_localtime(time); + } + return INT2FIX(tobj->tm.tm_hour); +} + +static VALUE +time_mday(time) + VALUE time; +{ + struct time_object *tobj; + + GetTimeval(time, tobj); + if (tobj->tm_got == 0) { + time_localtime(time); + } + return INT2FIX(tobj->tm.tm_mday); +} + +static VALUE +time_mon(time) + VALUE time; +{ + struct time_object *tobj; + + GetTimeval(time, tobj); + if (tobj->tm_got == 0) { + time_localtime(time); + } + return INT2FIX(tobj->tm.tm_mon); +} + +static VALUE +time_year(time) + VALUE time; +{ + struct time_object *tobj; + + GetTimeval(time, tobj); + if (tobj->tm_got == 0) { + time_localtime(time); + } + return INT2FIX(tobj->tm.tm_year); +} + +static VALUE +time_wday(time) + VALUE time; +{ + struct time_object *tobj; + + GetTimeval(time, tobj); + if (tobj->tm_got == 0) { + time_localtime(time); + } + return INT2FIX(tobj->tm.tm_wday); +} + +static VALUE +time_yday(time) + VALUE time; +{ + struct time_object *tobj; + + GetTimeval(time, tobj); + if (tobj->tm_got == 0) { + time_localtime(time); + } + return INT2FIX(tobj->tm.tm_yday); +} + +static VALUE +time_isdst(time) + VALUE time; +{ + struct time_object *tobj; + + GetTimeval(time, tobj); + if (tobj->tm_got == 0) { + time_localtime(time); + } + return INT2FIX(tobj->tm.tm_isdst); +} + +static VALUE +time_zone(time) + VALUE time; +{ + struct time_object *tobj; + char buf[10]; + int len; + + GetTimeval(time, tobj); + if (tobj->tm_got == 0) { + time_localtime(time); + } + + len = strftime(buf, 10, "%Z", &(tobj->tm)); + return str_new(buf, len); +} + +static VALUE +time_to_a(time) + VALUE time; +{ + struct time_object *tobj; + VALUE ary; + + GetTimeval(time, tobj); + if (tobj->tm_got == 0) { + time_localtime(time); + } + ary = ary_new3(9, + INT2FIX(tobj->tm.tm_sec), + INT2FIX(tobj->tm.tm_min), + INT2FIX(tobj->tm.tm_hour), + INT2FIX(tobj->tm.tm_mday), + INT2FIX(tobj->tm.tm_mon), + INT2FIX(tobj->tm.tm_year), + INT2FIX(tobj->tm.tm_wday), + INT2FIX(tobj->tm.tm_yday), + INT2FIX(tobj->tm.tm_isdst)); + return ary; +} + +static VALUE +time_strftime(time, format) + VALUE time, format; +{ + struct time_object *tobj; + char buf[100]; + int len; + + Check_Type(format, T_STRING); + GetTimeval(time, tobj); + if (tobj->tm_got == 0) { + time_localtime(time); + } + if (strlen(RSTRING(format)->ptr) < RSTRING(format)->len) { + /* Ruby string contains \0. */ + VALUE str; + int l; + char *p = RSTRING(format)->ptr, *pe = p + RSTRING(format)->len; + + str = str_new(0, 0); + while (p < pe) { + len = strftime(buf, 100, p, &(tobj->tm)); + str_cat(str, buf, len); + l = strlen(p); + p += l + 1; + } + return str; + } + len = strftime(buf, 100, RSTRING(format)->ptr, &(tobj->tm)); + return str_new(buf, len); +} + +static VALUE +time_s_times(obj) + VALUE obj; +{ +#ifdef HAVE_TIMES +#ifndef HZ +#define HZ 60 /* Universal constant :-) */ +#endif /* HZ */ + struct tms buf; + + if (times(&buf) == -1) rb_sys_fail(0); + return struct_new(S_Tms, + float_new((double)buf.tms_utime / HZ), + float_new((double)buf.tms_stime / HZ), + float_new((double)buf.tms_cutime / HZ), + float_new((double)buf.tms_cstime / HZ)); +#else +#ifdef NT + FILETIME create, exit, kernel, user; + HANDLE hProc; + + hProc = GetCurrentProcess(); + GetProcessTimes(hProc,&create, &exit, &kernel, &user); + return struct_new(S_Tms, + float_new((double)(kernel.dwHighDateTime*2e32+kernel.dwLowDateTime)/2e6), + float_new((double)(user.dwHighDateTime*2e32+user.dwLowDateTime)/2e6), + float_new((double)0), + float_new((double)0)); +#else + rb_notimplement(); +#endif +#endif +} + +void +Init_Time() +{ + cTime = rb_define_class("Time", cObject); + rb_include_module(cTime, mComparable); + + rb_define_singleton_method(cTime, "now", time_s_now, 0); + rb_define_singleton_method(cTime, "new", time_s_now, 0); + rb_define_singleton_method(cTime, "at", time_s_at, 1); + rb_define_singleton_method(cTime, "gm", time_s_timegm, -1); + rb_define_singleton_method(cTime, "local", time_s_timelocal, -1); + rb_define_singleton_method(cTime, "mktime", time_s_timelocal, -1); + + rb_define_singleton_method(cTime, "times", time_s_times, 0); + + rb_define_method(cTime, "to_i", time_to_i, 0); + rb_define_method(cTime, "to_f", time_to_f, 0); + rb_define_method(cTime, "<=>", time_cmp, 1); + rb_define_method(cTime, "eql?", time_eql, 0); + rb_define_method(cTime, "hash", time_hash, 0); + + rb_define_method(cTime, "localtime", time_localtime, 0); + rb_define_method(cTime, "gmtime", time_gmtime, 0); + rb_define_method(cTime, "ctime", time_asctime, 0); + rb_define_method(cTime, "asctime", time_asctime, 0); + rb_define_method(cTime, "to_s", time_asctime, 0); + rb_define_method(cTime, "inspect", time_asctime, 0); + rb_define_method(cTime, "to_a", time_to_a, 0); + rb_define_method(cTime, "coerce", time_coerce, 1); + + rb_define_method(cTime, "+", time_plus, 1); + rb_define_method(cTime, "-", time_minus, 1); + + rb_define_method(cTime, "sec", time_sec, 0); + rb_define_method(cTime, "min", time_min, 0); + rb_define_method(cTime, "hour", time_hour, 0); + rb_define_method(cTime, "mday", time_mday, 0); + rb_define_method(cTime, "mon", time_mon, 0); + rb_define_method(cTime, "year", time_year, 0); + rb_define_method(cTime, "wday", time_wday, 0); + rb_define_method(cTime, "yday", time_yday, 0); + rb_define_method(cTime, "isdst", time_isdst, 0); + rb_define_method(cTime, "zone", time_zone, 0); + + rb_define_method(cTime, "tv_sec", time_to_i, 0); + rb_define_method(cTime, "tv_usec", time_usec, 0); + rb_define_method(cTime, "usec", time_usec, 0); + + rb_define_method(cTime, "strftime", time_strftime, 1); + +#if defined(HAVE_TIMES) || defined(NT) + S_Tms = struct_define("Tms", "utime", "stime", "cutime", "cstime", 0); +#endif +} diff --git a/top.sed b/top.sed new file mode 100644 index 0000000000..d91a80eb0a --- /dev/null +++ b/top.sed @@ -0,0 +1,52 @@ +/^SHELL/s,/bin/sh,$(COMPSEC), +/^VPATH/s/:/;/g +s/@srcdir@/./g +s/@CC@/gcc/ +s/@CPP@/gcc -E/ +s/@CPPFLAGS@// +s/@AR@/ar/ +s/@RANLIB@/ranlib/ +s/@YACC@/bison -y/ +s/@INSTALL@/ginstall -c/ +s/@INSTALL_PROGRAM@/${INSTALL}/ +s/@INSTALL_DATA@/${INSTALL} -m 644/ +s/@SET_MAKE@// +s/@CFLAGS@/-g -O -I./ +s/@STATIC@// +s/@LDFLAGS@// +s/@LIBS@// +s/@LIBOBJS@/crypt.o flock.o/ +s/@ALLOCA@// +s!@prefix@!/usr/local! +s/@exec_prefix@/${prefix}/ +s!@bindir@!${exec_prefix}/bin! +s!@libdir@!${exec_prefix}/lib! +s/@STRIP@/strip/ +s!/bin/rm!rm! +s/@DLEXT@/o/ +s/@CCDLFLAGS@/-fpic/ +s/@DLDFLAGS@// +s/@LDSHARED@// +s/@binsuffix@/.exe/g +s/@setup@/Setup/g +s/|| true// +s!@archlib@!/usr/local/lib/ruby/i386-djgpp! +/\/dev\/null/ { +s,/dev/null 2>&1, nul, +s,2> /dev/null,, +} +s/y\.tab\.c/y_tab.c/ +#/if older/s/"ruby"/"ruby.exe"/g +#/`rm -f ruby`/s//`rm -f ruby.exe`/ +#/`cp miniruby ruby`/s//`cp miniruby.exe ruby.exe`/ +/^all:.*miniruby/ { + n;N;N;N;c\ + cd ext\ + ../miniruby ./extmk.rb\ + cd .. +} +/^clean:;/ { + n;n;s!cd.*!cd ext\ + ../miniruby ./extmk.rb clean\ + cd ..! +} diff --git a/util.c b/util.c new file mode 100644 index 0000000000..8e822631ed --- /dev/null +++ b/util.c @@ -0,0 +1,458 @@ +/************************************************ + + util.c - + + $Author$ + $Date$ + created at: Fri Mar 10 17:22:34 JST 1995 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include "util.h" +#ifdef HAVE_STRING_H +# include <string.h> +#else +char *strchr(); +#endif + +unsigned long +scan_oct(start, len, retlen) +char *start; +int len; +int *retlen; +{ + register char *s = start; + register unsigned long retval = 0; + + while (len-- && *s >= '0' && *s <= '7') { + retval <<= 3; + retval |= *s++ - '0'; + } + *retlen = s - start; + return retval; +} + +unsigned long +scan_hex(start, len, retlen) +char *start; +int len; +int *retlen; +{ + static char hexdigit[] = "0123456789abcdef0123456789ABCDEFx"; + register char *s = start; + register unsigned long retval = 0; + char *tmp; + + while (len-- && *s && (tmp = strchr(hexdigit, *s))) { + retval <<= 4; + retval |= (tmp - hexdigit) & 15; + s++; + } + *retlen = s - start; + return retval; +} + +#if defined(MSDOS) || defined(__CYGWIN32__) || defined(NT) +#include <fcntl.h> +/* + * Copyright (c) 1993, Intergraph Corporation + * + * You may distribute under the terms of either the GNU General Public + * License or the Artistic License, as specified in the perl README file. + * + * Various Unix compatibility functions and NT specific functions. + * + * Some of this code was derived from the MSDOS port(s) and the OS/2 port. + * + */ + + +/* + * Suffix appending for in-place editing under MS-DOS and OS/2 (and now NT!). + * + * Here are the rules: + * + * Style 0: Append the suffix exactly as standard perl would do it. + * If the filesystem groks it, use it. (HPFS will always + * grok it. So will NTFS. FAT will rarely accept it.) + * + * Style 1: The suffix begins with a '.'. The extension is replaced. + * If the name matches the original name, use the fallback method. + * + * Style 2: The suffix is a single character, not a '.'. Try to add the + * suffix to the following places, using the first one that works. + * [1] Append to extension. + * [2] Append to filename, + * [3] Replace end of extension, + * [4] Replace end of filename. + * If the name matches the original name, use the fallback method. + * + * Style 3: Any other case: Ignore the suffix completely and use the + * fallback method. + * + * Fallback method: Change the extension to ".$$$". If that matches the + * original name, then change the extension to ".~~~". + * + * If filename is more than 1000 characters long, we die a horrible + * death. Sorry. + * + * The filename restriction is a cheat so that we can use buf[] to store + * assorted temporary goo. + * + * Examples, assuming style 0 failed. + * + * suffix = ".bak" (style 1) + * foo.bar => foo.bak + * foo.bak => foo.$$$ (fallback) + * foo.$$$ => foo.~~~ (fallback) + * makefile => makefile.bak + * + * suffix = "~" (style 2) + * foo.c => foo.c~ + * foo.c~ => foo.c~~ + * foo.c~~ => foo~.c~~ + * foo~.c~~ => foo~~.c~~ + * foo~~~~~.c~~ => foo~~~~~.$$$ (fallback) + * + * foo.pas => foo~.pas + * makefile => makefile.~ + * longname.fil => longname.fi~ + * longname.fi~ => longnam~.fi~ + * longnam~.fi~ => longnam~.$$$ + * + */ + + +static int valid_filename(char *s); + +static char suffix1[] = ".$$$"; +static char suffix2[] = ".~~~"; + +#define ext (&buf[1000]) + +#define strEQ(s1,s2) (strcmp(s1,s2) == 0) + +void +add_suffix(struct RString *str, char *suffix) +{ + int baselen; + int extlen = strlen(suffix); + char *s, *t, *p; + int slen; + char buf[1024]; + + if (str->len > 1000) + Fatal("Cannot do inplace edit on long filename (%d characters)", str->len); + +#if defined(DJGPP) || defined(__CYGWIN32__) || defined(NT) + /* Style 0 */ + slen = str->len; + str_cat(str, suffix, extlen); +#if defined(DJGPP) + if (_USE_LFN) return; +#else + if (valid_filename(str->ptr)) return; +#endif + + /* Fooey, style 0 failed. Fix str before continuing. */ + str->ptr[str->len = slen] = '\0'; +#endif + + slen = extlen; + t = buf; baselen = 0; s = str->ptr; + while ( (*t = *s) && *s != '.') { + baselen++; + if (*s == '\\' || *s == '/') baselen = 0; + s++; t++; + } + p = t; + + t = ext; extlen = 0; + while (*t++ = *s++) extlen++; + if (extlen == 0) { ext[0] = '.'; ext[1] = 0; extlen++; } + + if (*suffix == '.') { /* Style 1 */ + if (strEQ(ext, suffix)) goto fallback; + strcpy(p, suffix); + } else if (suffix[1] == '\0') { /* Style 2 */ + if (extlen < 4) { + ext[extlen] = *suffix; + ext[++extlen] = '\0'; + } else if (baselen < 8) { + *p++ = *suffix; + } else if (ext[3] != *suffix) { + ext[3] = *suffix; + } else if (buf[7] != *suffix) { + buf[7] = *suffix; + } else goto fallback; + strcpy(p, ext); + } else { /* Style 3: Panic */ +fallback: + (void)memcpy(p, strEQ(ext, suffix1) ? suffix2 : suffix1, 5); + } + str_resize(str, strlen(buf)); + memcpy(str->ptr, buf, str->len); +} + +#if defined(__CYGWIN32__) || defined(NT) +static int +valid_filename(char *s) +{ + int fd; + + /* + // if the file exists, then it's a valid filename! + */ + + if (_access(s, 0) == 0) { + return 1; + } + + /* + // It doesn't exist, so see if we can open it. + */ + + if ((fd = _open(s, O_CREAT, 0666)) >= 0) { + _close(fd); + _unlink (s); /* don't leave it laying around */ + return 1; + } + return 0; +} +#endif +#endif + +#ifdef DJGPP +/* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */ +/* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */ +#include <libc/stubs.h> +#include <stdio.h> /* For FILENAME_MAX */ +#include <errno.h> /* For errno */ +#include <ctype.h> /* For tolower */ +#include <string.h> /* For strlen() */ +#include <fcntl.h> /* For LFN stuff */ +#include <go32.h> +#include <dpmi.h> /* For dpmisim */ +#include <crt0.h> /* For crt0 flags */ +#include <sys/stat.h> +#include <libc/dosio.h> + +static unsigned use_lfn; + +static char *__get_current_directory(char *out, int drive_number); + +static char * +__get_current_directory(char *out, int drive_number) +{ + __dpmi_regs r; + char tmpbuf[FILENAME_MAX]; + + memset(&r, 0, sizeof(r)); + if(use_lfn) + r.x.ax = 0x7147; + else + r.h.ah = 0x47; + r.h.dl = drive_number + 1; + r.x.si = __tb_offset; + r.x.ds = __tb_segment; + __dpmi_int(0x21, &r); + + if (r.x.flags & 1) + { + errno = r.x.ax; + return out; + } + else + { + dosmemget(__tb, sizeof(tmpbuf), tmpbuf); + strcpy(out+1,tmpbuf); + + /* Root path, don't insert "/", it'll be added later */ + if (*(out + 1) != '\0') + *out = '/'; + else + *out = '\0'; + return out + strlen(out); + } +} + +__inline__ static int +is_slash(int c) +{ + return c == '/' || c == '\\'; +} + +__inline__ static int +is_term(int c) +{ + return c == '/' || c == '\\' || c == '\0'; +} + +#ifdef SJIS +__inline__ static int +is_sjis1(int c) +{ + return 0x81 <= c && (c <= 0x9f || 0xe0 <= c); +} +#endif + +/* Takes as input an arbitrary path. Fixes up the path by: + 1. Removing consecutive slashes + 2. Removing trailing slashes + 3. Making the path absolute if it wasn't already + 4. Removing "." in the path + 5. Removing ".." entries in the path (and the directory above them) + 6. Adding a drive specification if one wasn't there + 7. Converting all slashes to '/' + */ +void +_fixpath(const char *in, char *out) +{ + int drive_number; + const char *ip = in; + char *op = out; + int preserve_case = _preserve_fncase(); + char *name_start; + + use_lfn = _USE_LFN; + + /* Add drive specification to output string */ + if (((*ip >= 'a' && *ip <= 'z') || + (*ip >= 'A' && *ip <= 'Z')) + && (*(ip + 1) == ':')) + { + if (*ip >= 'a' && *ip <= 'z') + { + drive_number = *ip - 'a'; + *op++ = *ip++; + } + else + { + drive_number = *ip - 'A'; + if (*ip <= 'Z') + *op++ = drive_number + 'a'; + else + *op++ = *ip; + ++ip; + } + *op++ = *ip++; + } + else + { + __dpmi_regs r; + r.h.ah = 0x19; + __dpmi_int(0x21, &r); + drive_number = r.h.al; + *op++ = drive_number + (drive_number < 26 ? 'a' : 'A'); + *op++ = ':'; + } + + /* Convert relative path to absolute */ + if (!is_slash(*ip)) + op = __get_current_directory(op, drive_number); + + /* Step through the input path */ + while (*ip) + { + /* Skip input slashes */ + if (is_slash(*ip)) + { + ip++; + continue; + } + + /* Skip "." and output nothing */ + if (*ip == '.' && is_term(*(ip + 1))) + { + ip++; + continue; + } + + /* Skip ".." and remove previous output directory */ + if (*ip == '.' && *(ip + 1) == '.' && is_term(*(ip + 2))) + { + ip += 2; + /* Don't back up over drive spec */ + if (op > out + 2) + /* This requires "/" to follow drive spec */ + while (!is_slash(*--op)); + continue; + } + + /* Copy path component from in to out */ + *op++ = '/'; +#ifndef SJIS + while (!is_term(*ip)) *op++ = *ip++; +#else + while (!is_term(*ip)) { + if (is_sjis1((unsigned char)*ip)) + *op++ = *ip++; + *op++ = *ip++; + } +#endif + } + + /* If root directory, insert trailing slash */ + if (op == out + 2) *op++ = '/'; + + /* Null terminate the output */ + *op = '\0'; + + /* switch FOO\BAR to foo/bar, downcase where appropriate */ + for (op = out + 3, name_start = op - 1; *name_start; op++) + { + char long_name[FILENAME_MAX], short_name[13]; + +#ifdef SJIS + if (is_sjis1((unsigned char)*op)) { + op++; + continue; + } +#endif + if (*op == '\\') + *op = '/'; + if (!preserve_case && (*op == '/' || *op == '\0')) + { + memcpy(long_name, name_start+1, op - name_start - 1); + long_name[op - name_start - 1] = '\0'; + if (!strcmp(_lfn_gen_short_fname(long_name, short_name), long_name)) + { +#ifndef SJIS + while (++name_start < op) + if (*name_start >= 'A' && *name_start <= 'Z') + *name_start += 'a' - 'A'; +#else + while (++name_start < op) { + if (is_sjis1((unsigned char)*name_start)) + name_start++; + else if (*name_start >= 'A' && *name_start <= 'Z') + *name_start += 'a' - 'A'; + } +#endif + } + else + name_start = op; + } + else if (*op == '\0') + break; + } +} + +#ifdef TEST + +int main (int argc, char *argv[]) +{ + char fixed[FILENAME_MAX]; + if (argc > 1) + { + _fixpath (argv[1], fixed); + printf ("You mean %s?\n", fixed); + } + return 0; +} + +#endif +#endif diff --git a/util.h b/util.h new file mode 100644 index 0000000000..570d894ccb --- /dev/null +++ b/util.h @@ -0,0 +1,18 @@ +/************************************************ + + util.h - + + $Author$ + $Date$ + created at: Thu Mar 9 11:55:53 JST 1995 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ +#ifndef UTIL_H +#define UTIL_H + +unsigned long scan_hex(); +unsigned long scan_oct(); + +#endif /* UTIL_H */ diff --git a/variable.c b/variable.c new file mode 100644 index 0000000000..fc514f902a --- /dev/null +++ b/variable.c @@ -0,0 +1,921 @@ +/************************************************ + + variable.c - + + $Author$ + $Date$ + created at: Tue Apr 19 23:55:15 JST 1994 + +************************************************/ + +#include "ruby.h" +#include "env.h" +#include "node.h" +#include "st.h" + +#ifdef HAVE_STRING_H +# include <string.h> +#endif + +static st_table *rb_global_tbl; +st_table *rb_class_tbl; +#define global_tbl rb_global_tbl +#define class_tbl rb_class_tbl + +int rb_const_defined(); + +st_table* +new_idhash() +{ + return st_init_numtable(); +} + +void +Init_var_tables() +{ + global_tbl = new_idhash(); + class_tbl = new_idhash(); +} + +struct fc_result { + ID name; + VALUE class; + VALUE path; + VALUE track; + struct fc_result *prev; +}; + +extern VALUE cModule; + +static int +fc_i(key, value, res) + ID key; + VALUE value; + struct fc_result *res; +{ + VALUE path; + char *name; + + if (!rb_is_const_id(key)) return ST_CONTINUE; + + name = rb_id2name(key); + if (res->path) { + path = str_dup(res->path); + str_cat(path, "::", 2); + str_cat(path, name, strlen(name)); + } + else { + path = str_new2(name); + } + if (value == res->class) { + res->name = key; + res->path = path; + return ST_STOP; + } + if (obj_is_kind_of(value, cModule)) { + struct fc_result arg; + struct fc_result *list; + + + if (!RCLASS(value)->iv_tbl) return ST_CONTINUE; + + list = res; + while (list) { + if (list->track == value) return ST_CONTINUE; + list = list->prev; + } + + arg.name = 0; + arg.path = path; + arg.class = res->class; + arg.track = value; + arg.prev = res; + st_foreach(RCLASS(value)->iv_tbl, fc_i, &arg); + if (arg.name) { + res->name = arg.name; + res->path = arg.path; + return ST_STOP; + } + } + return ST_CONTINUE; +} + +static VALUE +find_class_path(class) + VALUE class; +{ + VALUE c; + struct fc_result arg; + + arg.name = 0; + arg.path = 0; + arg.class = class; + arg.track = cObject; + arg.prev = 0; + if (RCLASS(cObject)->iv_tbl) { + st_foreach(RCLASS(cObject)->iv_tbl, fc_i, &arg); + } + if (arg.name == 0) { + st_foreach(class_tbl, fc_i, &arg); + } + if (arg.name) { + rb_iv_set(class, "__classpath__", arg.path); + return arg.path; + } + return Qnil; +} + +static VALUE +classname(class) + VALUE class; +{ + VALUE path; + + while (TYPE(class) == T_ICLASS || FL_TEST(class, FL_SINGLETON)) { + class = (VALUE)RCLASS(class)->super; + } + path = rb_iv_get(class, "__classpath__"); + if (NIL_P(path)) { + path = rb_iv_get(class, "__classid__"); + if (!NIL_P(path)) { + path = str_new2(rb_id2name(FIX2INT(path))); + } + } + if (NIL_P(path)) { + path = find_class_path(class); + if (NIL_P(path)) { + return 0; + } + return path; + } + if (TYPE(path) != T_STRING) Bug("class path is not set properly"); + return path; +} + +VALUE +rb_class_path(class) + VALUE class; +{ + VALUE path = classname(class); + + if (path) return path; + else { + char buf[256]; + char *s = "Class"; + + if (TYPE(class) == T_MODULE) s = "Module"; + sprintf(buf, "#<%s 0x%x>", s, class); + return str_new2(buf); + } +} + +void +rb_set_class_path(class, under, name) + VALUE class, under; + char *name; +{ + VALUE str; + char *s; + + if (under == cObject) { + str = str_new2(name); + } + else { + str = str_dup(rb_class_path(under)); + str_cat(str, "::", 2); + str_cat(str, name, strlen(name)); + } + rb_iv_set(class, "__classpath__", str); +} + +VALUE +rb_path2class(path) + char *path; +{ + if (path[0] == '#') { + ArgError("can't retrieve anonymous class %s", path); + } + return rb_eval_string(path); +} + +void +rb_name_class(class, id) + VALUE class; + ID id; +{ + extern VALUE cString; + + if (cString) { + rb_iv_set(class, "__classpath__", str_new2(rb_id2name(id))); + } + else { + rb_iv_set(class, "__classid__", INT2FIX(id)); + } +} + +static st_table *autoload_tbl = 0; + +static void +rb_autoload_id(id, filename) + ID id; + char *filename; +{ + if (!rb_is_const_id(id)) { + NameError("autoload must be constant name", rb_id2name(id)); + } + + if (!autoload_tbl) { + autoload_tbl = new_idhash(); + } + st_insert(autoload_tbl, id, strdup(filename)); +} + +void +rb_autoload(class, filename) + char *class, *filename; +{ + rb_autoload_id(rb_intern(class), filename); +} + +VALUE +f_autoload(obj, class, file) + VALUE obj, class; + struct RString *file; +{ + ID id = rb_to_id(class); + + Check_Type(file, T_STRING); + rb_autoload_id(id, file->ptr); + return Qnil; +} + +char * +rb_class2name(class) + VALUE class; +{ + return RSTRING(rb_class_path(class))->ptr; +} + +struct trace_var { + void (*func)(); + void *data; + struct trace_var *next; +}; + +VALUE f_untrace_var(); + +struct global_entry { + ID id; + void *data; + VALUE (*getter)(); + void (*setter)(); + void (*marker)(); + int block_trace; + struct trace_var *trace; +}; + +static VALUE undef_getter(); +static void undef_setter(); +static void undef_marker(); + +static VALUE val_getter(); +static void val_setter(); +static void val_marker(); + +static VALUE var_getter(); +static void var_setter(); +static void var_marker(); + +struct global_entry* +rb_global_entry(id) + ID id; +{ + struct global_entry *entry; + + if (!st_lookup(global_tbl, id, &entry)) { + entry = ALLOC(struct global_entry); + st_insert(global_tbl, id, entry); + entry->id = id; + entry->data = 0; + entry->getter = undef_getter; + entry->setter = undef_setter; + entry->marker = undef_marker; + + entry->block_trace = 0; + entry->trace = 0; + } + return entry; +} + +static VALUE +undef_getter(id) + ID id; +{ + Warning("global variable `%s' not initialized", rb_id2name(id)); + return Qnil; +} + +static void +undef_setter(val, id, data, entry) + VALUE val; + ID id; + void *data; + struct global_entry *entry; +{ + entry->getter = val_getter; + entry->setter = val_setter; + entry->marker = val_marker; + + entry->data = (void*)val; +} + +static void +undef_marker() +{ +} + +static VALUE +val_getter(id, val) + ID id; + VALUE val; +{ + return val; +} + +static void +val_setter(val, id, data, entry) + VALUE val; + ID id; + void *data; + struct global_entry *entry; +{ + entry->data = (void*)val; +} + +static void +val_marker(data) + void *data; +{ + if (data) gc_mark_maybe(data); +} + +static VALUE +var_getter(id, var) + ID id; + VALUE *var; +{ + if (!var || !*var) return Qnil; + return *var; +} + +static void +var_setter(val, id, var) + VALUE val; + ID id; + VALUE *var; +{ + *var = val; +} + +static void +var_marker(var) + VALUE **var; +{ + if (var) gc_mark_maybe(*var); +} + +static void +readonly_setter(val, id, var) + VALUE val; + ID id; + void *var; +{ + NameError("Can't set variable %s", rb_id2name(id)); +} + +static int +mark_global_entry(key, entry) + ID key; + struct global_entry *entry; +{ + struct trace_var *trace; + + (*entry->marker)(entry->data); + trace = entry->trace; + while (trace) { + if (trace->data) gc_mark_maybe(trace->data); + trace = trace->next; + } + return ST_CONTINUE; +} + +void +gc_mark_global_tbl() +{ + st_foreach(global_tbl, mark_global_entry, 0); +} + +static ID +global_id(name) + char *name; +{ + ID id; + + if (name[0] == '$') id = rb_intern(name); + else { + char *buf = ALLOCA_N(char, strlen(name)+2); + buf[0] = '$'; + strcpy(buf+1, name); + id = rb_intern(buf); + } + return id; +} + +void +rb_define_hooked_variable(name, var, getter, setter) + char *name; + VALUE *var; + VALUE (*getter)(); + void (*setter)(); +{ + struct global_entry *entry; + ID id = global_id(name); + + entry = rb_global_entry(id); + entry->data = (void*)var; + entry->getter = getter?getter:var_getter; + entry->setter = setter?setter:var_setter; + entry->marker = var_marker; +} + +void +rb_define_variable(name, var) + char *name; + VALUE *var; +{ + rb_define_hooked_variable(name, var, 0, 0); +} + +void +rb_define_readonly_variable(name, var) + char *name; + VALUE *var; +{ + rb_define_hooked_variable(name, var, 0, readonly_setter); +} + +void +rb_define_virtual_variable(name, getter, setter) + char *name; + VALUE (*getter)(); + void (*setter)(); +{ + if (!getter) getter = val_getter; + if (!setter) setter = readonly_setter; + rb_define_hooked_variable(name, 0, getter, setter); +} + +static void +rb_trace_eval(cmd, val) + VALUE cmd, val; +{ + rb_eval_cmd(cmd, ary_new3(1, val)); +} + +VALUE +f_trace_var(argc, argv) + int argc; + VALUE *argv; +{ + VALUE var, cmd; + ID id; + struct global_entry *entry; + struct trace_var *trace; + + if (rb_scan_args(argc, argv, "11", &var, &cmd) == 1) { + cmd = f_lambda(); + } + if (NIL_P(cmd)) { + return f_untrace_var(argc, argv, Qnil); + } + id = rb_to_id(var); + if (!st_lookup(global_tbl, id, &entry)) { + NameError("undefined global variable %s", rb_id2name(id)); + } + trace = ALLOC(struct trace_var); + trace->next = entry->trace; + trace->func = rb_trace_eval; + trace->data = (void*)cmd; + entry->trace = trace; + + return Qnil; +} + +VALUE +f_untrace_var(argc, argv) + int argc; + VALUE *argv; +{ + VALUE var, cmd; + ID id; + struct global_entry *entry; + struct trace_var *trace; + + rb_scan_args(argc, argv, "11", &var, &cmd); + id = rb_to_id(var); + if (!st_lookup(global_tbl, id, &entry)) { + NameError("undefined global variable %s", rb_id2name(id)); + } + if (NIL_P(cmd)) { + VALUE ary = ary_new(); + + trace = entry->trace; + while (trace) { + struct trace_var *next = trace->next; + ary_push(ary, trace->data); + free(trace); + trace = next; + } + entry->trace = 0; + + return ary; + } + else { + struct trace_var t; + struct trace_var *next; + + t.next = entry->trace; + trace = &t; + while (trace->next) { + next = trace->next; + if (next->data == (void*)cmd) { + trace->next = next->next; + free(next); + entry->trace = t.next; + return ary_new3(1, cmd); + } + trace = next; + } + } + return Qnil; +} + +VALUE +rb_gvar_get(entry) + struct global_entry *entry; +{ + return (*entry->getter)(entry->id, entry->data, entry); +} + +struct trace_data { + struct trace_var *trace; + VALUE val; +}; + +static void +trace_ev(data) + struct trace_data *data; +{ + struct trace_var *trace = data->trace; + + while (trace) { + (*trace->func)(trace->data, data->val); + trace = trace->next; + } +} + +static void +trace_en(entry) + struct global_entry *entry; +{ + entry->block_trace = 0; +} + +VALUE +rb_gvar_set(entry, val) + struct global_entry *entry; + VALUE val; +{ + struct trace_data trace; + + if (rb_safe_level() >= 4) { + extern VALUE eSecurityError; + Raise(eSecurityError, "cannot change global variable value"); + } + (*entry->setter)(val, entry->id, entry->data, entry); + + if (!entry->block_trace) { + entry->block_trace = 1; + trace.trace = entry->trace; + trace.val = val; + rb_ensure(trace_ev, &trace, trace_en, entry); + } + return val; +} + +VALUE +rb_gvar_set2(name, val) + char *name; + VALUE val; +{ + struct global_entry *entry; + + entry = rb_global_entry(global_id(name)); + return rb_gvar_set(entry, val); +} + +VALUE +rb_gvar_defined(entry) + struct global_entry *entry; +{ + if (entry->getter == undef_getter) return FALSE; + return TRUE; +} + +void +rb_alias_variable(name1, name2) + ID name1; + ID name2; +{ + struct global_entry *entry1, *entry2; + + entry1 = rb_global_entry(name1); + entry2 = rb_global_entry(name2); + + entry1->data = entry2->data; + entry1->getter = entry2->getter; + entry1->setter = entry2->setter; + entry1->marker = entry2->marker; +} + +VALUE +rb_ivar_get(obj, id) + struct RObject *obj; + ID id; +{ + VALUE val; + + switch (TYPE(obj)) { + case T_OBJECT: + case T_CLASS: + case T_MODULE: + if (obj->iv_tbl && st_lookup(obj->iv_tbl, id, &val)) + return val; + return Qnil; + default: + TypeError("class %s can not have instance variables", + rb_class2name(CLASS_OF(obj))); + break; + } + Warning("instance var %s not initialized", rb_id2name(id)); + return Qnil; +} + +VALUE +rb_ivar_set(obj, id, val) + struct RObject *obj; + ID id; + VALUE val; +{ + if (rb_safe_level() >= 5) { + extern VALUE eSecurityError; + Raise(eSecurityError, "cannot change object status"); + } + switch (TYPE(obj)) { + case T_OBJECT: + case T_CLASS: + case T_MODULE: + if (!obj->iv_tbl) obj->iv_tbl = new_idhash(); + st_insert(obj->iv_tbl, id, val); + break; + default: + TypeError("class %s can not have instance variables", + rb_class2name(CLASS_OF(obj))); + break; + } + return val; +} + +VALUE +rb_ivar_defined(obj, id) + struct RObject *obj; + ID id; +{ + switch (TYPE(obj)) { + case T_OBJECT: + case T_CLASS: + case T_MODULE: + if (obj->iv_tbl && st_lookup(obj->iv_tbl, id, 0)) + return TRUE; + break; + } + return FALSE; +} + +VALUE +rb_const_get_at(class, id) + struct RClass *class; + ID id; +{ + VALUE value; + + if (class->iv_tbl && st_lookup(class->iv_tbl, id, &value)) { + return value; + } + if ((VALUE)class == cObject) { + return rb_const_get(class, id); + } + NameError("Uninitialized constant %s::%s", + RSTRING(rb_class_path(class))->ptr, + rb_id2name(id)); + /* not reached */ +} + +VALUE +rb_const_get(class, id) + struct RClass *class; + ID id; +{ + VALUE value; + struct RClass *tmp; + + tmp = class; + while (tmp) { + if (tmp->iv_tbl && st_lookup(tmp->iv_tbl, id, &value)) { + return value; + } + tmp = tmp->super; + } + if (BUILTIN_TYPE(class) == T_MODULE) { + return rb_const_get(cObject, id); + } + + /* pre-defined class */ + if (st_lookup(class_tbl, id, &value)) return value; + + /* autoload */ + if (autoload_tbl && st_lookup(autoload_tbl, id, 0)) { + char *modname; + VALUE module; + + st_delete(autoload_tbl, &id, &modname); + module = str_new2(modname); + free(modname); + f_require(0, module); + return rb_const_get(class, id); + } + + /* Uninitialized constant */ + if (class && (VALUE)class != cObject) + NameError("Uninitialized constant %s::%s", + RSTRING(rb_class_path(class))->ptr, + rb_id2name(id)); + else { + NameError("Uninitialized constant %s",rb_id2name(id)); + } + /* not reached */ +} + +int +const_i(key, value, hash) + ID key; + VALUE value; + VALUE hash; +{ + if (rb_is_const_id(key)) { + hash_aset(hash, str_new2(rb_id2name(key)), value); + } + return ST_CONTINUE; +} + +VALUE +mod_constants(argc, argv, mod) + int argc; + VALUE *argv; + struct RClass *mod; +{ + VALUE option; + VALUE hash; + + rb_scan_args(argc, argv, "01", &option); + hash = hash_new(); + st_foreach(mod->iv_tbl, const_i, hash); + if (!FL_TEST(mod, FL_SINGLETON)) { + mod = mod->super; + if (!mod) { + Bug("no superclass for singleton class"); + } + st_foreach(mod->iv_tbl, const_i, hash); + } + if (RTEST(option)) { + for (;;) { + mod = mod->super; + if (!mod) break; + st_foreach(mod->iv_tbl, const_i, hash); + } + st_foreach(class_tbl, const_i, hash); + if (autoload_tbl) { + st_foreach(autoload_tbl, const_i, hash); + } + } + return hash; +} + +int +rb_const_defined_at(class, id) + struct RClass *class; + ID id; +{ + if (class->iv_tbl && st_lookup(class->iv_tbl, id, 0)) { + return TRUE; + } + if ((VALUE)class == cObject) { + return rb_const_defined(class, id); + } + return FALSE; +} + +int +rb_autoload_defined(id) + ID id; +{ + if (autoload_tbl && st_lookup(autoload_tbl, id, 0)) + return TRUE; + return FALSE; +} + +int +rb_const_defined(class, id) + struct RClass *class; + ID id; +{ + while (class) { + if (class->iv_tbl && st_lookup(class->iv_tbl, id, 0)) { + return TRUE; + } + class = class->super; + } + if (st_lookup(class_tbl, id, 0)) + return TRUE; + return rb_autoload_defined(id); +} + +void +rb_const_set(class, id, val) + struct RClass *class; + ID id; + VALUE val; +{ + if (!class->iv_tbl) { + class->iv_tbl = new_idhash(); + } + else if (st_lookup(class->iv_tbl, id, 0)) { + NameError("already initialized constant %s", rb_id2name(id)); + } + + st_insert(class->iv_tbl, id, val); +} + +void +rb_define_const(class, name, val) + VALUE class; + char *name; + VALUE val; +{ + ID id = rb_intern(name); + if (!rb_is_const_id(id)) { + NameError("wrong constant name %s", name); + } + rb_const_set(class, id, val); +} + +extern VALUE mKernel; + +void +rb_define_global_const(name, val) + char *name; + VALUE val; +{ + rb_define_const(mKernel, name, val); +} + +VALUE +rb_iv_get(obj, name) + VALUE obj; + char *name; +{ + ID id = rb_intern(name); + + return rb_ivar_get(obj, id); +} + +VALUE +rb_iv_set(obj, name, val) + VALUE obj; + char *name; + VALUE val; +{ + ID id = rb_intern(name); + + return rb_ivar_set(obj, id, val); +} diff --git a/version.c b/version.c new file mode 100644 index 0000000000..49bdc7e33e --- /dev/null +++ b/version.c @@ -0,0 +1,36 @@ +/************************************************ + + version.c - + + $Author$ + $Revision$ + $Date$ + created at: Thu Sep 30 20:08:01 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include "version.h" +#include <stdio.h> + +void +Init_version() +{ + rb_define_global_const("VERSION", str_new2(RUBY_VERSION)); + rb_define_global_const("PLATFORM", str_new2(RUBY_PLATFORM)); +} + +void +show_version() +{ + fprintf(stderr, "ruby - version %s (%s)\n", RUBY_VERSION, RUBY_PLATFORM); +} + +void +show_copyright() +{ + fprintf(stderr, "ruby - Copyright (C) 1993-1997 Yukihiro Matsumoto\n"); + exit(0); +} diff --git a/version.h b/version.h new file mode 100644 index 0000000000..a4aaff6e93 --- /dev/null +++ b/version.h @@ -0,0 +1,2 @@ +#define RUBY_VERSION "1.0-971225" +#define VERSION_DATE "97/12/25" diff --git a/win32/Makefile b/win32/Makefile new file mode 100644 index 0000000000..29d2101479 --- /dev/null +++ b/win32/Makefile @@ -0,0 +1,233 @@ +SHELL = $(COMPSEC) + +#### Start of system configuration section. #### + +srcdir = . +VPATH = .:./missing + +CC = cl +YACC = byacc +RANLIB = +AR = +INSTALL = ginstall -c +INSTALL_PROGRAM = $(INSTALL) +INSTALL_DATA = $(INSTALL) -m 644 +PURIFY = + + +#CFLAGS = -nologo -DNT=1 -Ox +#LDFLAGS = $(CFLAGS) -Fm +CFLAGS = -nologo -DNT=1 -Zi -MD +LDFLAGS = $(CFLAGS) -Fm -MD +LIBS = $(EXTLIBS) advapi32.lib wsock32.lib +MISSING = crypt.obj setenv.obj alloca.obj nt.obj + +prefix = +binprefix = +exec_prefix = +bindir = +libdir = + +STACK = 0x200000 + +#### End of system configuration section. #### + + +LIBRUBY = libruby.lib + +EXTOBJS = dmyext.obj + +MAINOBJ = main.obj + +OBJS = array.obj \ + bignum.obj \ + class.obj \ + compar.obj \ + dir.obj \ + dln.obj \ + enum.obj \ + error.obj \ + eval.obj \ + file.obj \ + fnmatch.obj \ + gc.obj \ + glob.obj \ + hash.obj \ + inits.obj \ + io.obj \ + math.obj \ + numeric.obj \ + object.obj \ + pack.obj \ + parse.obj \ + process.obj \ + random.obj \ + range.obj \ + re.obj \ + regex.obj \ + ruby.obj \ + signal.obj \ + sprintf.obj \ + st.obj \ + string.obj \ + struct.obj \ + time.obj \ + util.obj \ + variable.obj \ + version.obj \ + $(MISSING) + +all: miniruby.exe ext/Setup + cd ext + ..\miniruby .\extmk.rb static + cd .. + +miniruby.exe: $(OBJS) $(MAINOBJ) $(EXTOBJS) + @echo $(EXTOBJS) + @echo $(LIBS) + @rm -f miniruby.exe +# $(PURIFY) $(CC) $(LDFLAGS) $(MAINOBJ) $(OBJS) $(EXTOBJS) $(LIBS) -o miniruby.exe + $(PURIFY) $(CC) $(LDFLAGS) $(MAINOBJ) $(OBJS) $(EXTOBJS) $(LIBS) -o miniruby.exe -link /NOD:LIBC + +ruby.exe: $(LIBRUBY) $(MAINOBJ) $(EXTOBJS) ruby.dll + @echo $(EXTOBJS) + @echo $(LIBS) + @rm -f ruby.exe +# $(PURIFY) $(CC) $(LDFLAGS) $(MAINOBJ) $(EXTOBJS) $(LIBRUBY) $(LIBS) -o ruby.exe +# $(PURIFY) $(CC) $(LDFLAGS) $(MAINOBJ) $(EXTOBJS) $(LIBRUBY) $(LIBS) -o ruby.exe -link /DEF:rubyexe.def /NOD:LIBC + $(CC) $(LDFLAGS) $(MAINOBJ) -o ruby.exe ruby.lib -link /NOD:LIBC /STACK:$(STACK) + +ruby.dll: $(LIBRUBY) $(EXTOBJS) + @echo $(EXTOBJS) + @echo $(LIBS) + @rm -f ruby.dll + $(PURIFY) $(CC) $(LDFLAGS) $(MAINOBJ) $(EXTOBJS) $(LIBRUBY) $(LIBS) -o ruby.dll -link /DLL /DEF:ruby.def /NOD:LIBC + +$(LIBRUBY): $(OBJS) + lib /OUT:$(LIBRUBY) $(OBJS) + +install:; $(INSTALL_PROGRAM) ruby.exe $(bindir)/ruby.exe + @-strip $(bindir)/ruby + @test -d $(libdir) || mkdir $(libdir) + cd ext; ../miniruby ./extmk.rb install + @for rb in `grep '^lib/' MANIFEST`; do \ + $(INSTALL_DATA) $$rb $(libdir); \ + done + +clean:; @rm -f $(OBJS) $(LIBRUBY) main.obj dmyext.obj *.pdb *.map *.exp + @rm -f ext/extinit.c ext/extinit.obj + cd ext + ..\miniruby .\extmk.rb clean + cd .. + +realclean: clean + @rm -f Makefile ext/extmk.rb + @rm -f config.cache config.h config.log config.status + @rm -f core ruby miniruby *~ + +test:; @-./ruby sample/test.rb > ./ruby_test 2>&1; \ + if grep '^end of test' ./ruby_test > /dev/null; then \ + echo "test succeeded"; \ + else \ + grep '^sample/test.rb' ./ruby_test; \ + grep '^not' ./ruby_test; \ + echo "test failed";\ + fi;\ + rm -f ./ruby_test + +.c.obj: + $(CC) $(CFLAGS) $(CPPFLAGS) -c $< + +alloca.obj: missing/alloca.c + $(CC) -I. $(CFLAGS) $(CPPFLAGS) -c missing/alloca.c + +crypt.obj: missing/crypt.c + $(CC) -I. $(CFLAGS) $(CPPFLAGS) -c missing/crypt.c + +dup2.obj: missing/dup2.c + $(CC) -I. $(CFLAGS) $(CPPFLAGS) -c missing/dup2.c + +flock.obj: missing/flock.c + $(CC) -I. $(CFLAGS) $(CPPFLAGS) -c missing/flock.c + +memmove.obj: missing/memmove.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c missing/memmove.c + +mkdir.obj: missing/mkdir.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c missing/mkdir.c + +setenv.obj: missing/setenv.c + $(CC) -I. $(CFLAGS) $(CPPFLAGS) -c missing/setenv.c + +strerror.obj: missing/strerror.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c missing/strerror.c + +strdup.obj: missing/strdup.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c missing/strdup.c + +strftime.obj: missing/strftime.c + $(CC) -I. $(CFLAGS) $(CPPFLAGS) -c missing/strftime.c + +strstr.obj: missing/strstr.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c missing/strstr.c + +strtol.obj: missing/strtol.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c missing/strtol.c + +strtoul.obj: missing/strtoul.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c missing/strtoul.c + +# when I use -I., there is confliction at "OpenFile" +# so, set . into environment varible "include" +nt.obj: missing/nt.c + @set include=$(INCLUDE);. + $(CC) $(CFLAGS) $(CPPFLAGS) -c missing/nt.c + +parse.c: parse.y + $(YACC) $(YFLAGS) parse.y + sed -e "s!^extern char \*getenv();!/* & */!" y.tab.c > parse.c + @rm y.tab.c + +# Prevent GNU make v3 from overflowing arg limit on SysV. +.NOEXPORT: +### +parse.obj : parse.y ruby.h defines.h config.h env.h node.h st.h regex.h +### +array.obj: array.c ruby.h config.h defines.h +bignum.obj: bignum.c ruby.h config.h defines.h +class.obj: class.c ruby.h config.h defines.h node.h st.h +compar.obj: compar.c ruby.h config.h defines.h +dir.obj: dir.c ruby.h config.h defines.h +dln.obj: dln.c config.h defines.h dln.h st.h +dmyext.obj: dmyext.c +enum.obj: enum.c ruby.h config.h defines.h +error.obj: error.c ruby.h config.h defines.h env.h +eval.obj: eval.c ruby.h config.h defines.h env.h node.h sig.h st.h dln.h +file.obj: file.c ruby.h config.h defines.h io.h sig.h +fnmatch.obj: fnmatch.c config.h fnmatch.h +gc.obj: gc.c ruby.h config.h defines.h env.h sig.h st.h node.h re.h regex.h +glob.obj: glob.c config.h fnmatch.h +hash.obj: hash.c ruby.h config.h defines.h st.h +inits.obj: inits.c ruby.h config.h defines.h +io.obj: io.c ruby.h config.h defines.h io.h sig.h +main.obj: main.c +math.obj: math.c ruby.h config.h defines.h +numeric.obj: numeric.c ruby.h config.h defines.h +object.obj: object.c ruby.h config.h defines.h st.h +pack.obj: pack.c ruby.h config.h defines.h +process.obj: process.c ruby.h config.h defines.h sig.h st.h +random.obj: random.c ruby.h config.h defines.h +range.obj: range.c ruby.h config.h defines.h +re.obj: re.c ruby.h config.h defines.h re.h regex.h +regex.obj: regex.c config.h defines.h regex.h util.h +ruby.obj: ruby.c ruby.h config.h defines.h re.h regex.h dln.h +signal.obj: signal.c ruby.h config.h defines.h sig.h +sprintf.obj: sprintf.c ruby.h config.h defines.h +st.obj: st.c config.h st.h +string.obj: string.c ruby.h config.h defines.h re.h regex.h +struct.obj: struct.c ruby.h config.h defines.h +time.obj: time.c ruby.h config.h defines.h +util.obj: util.c defines.h config.h util.h +variable.obj: variable.c ruby.h config.h defines.h env.h st.h +version.obj: version.c ruby.h config.h defines.h version.h +director.obj : director.c dir.h diff --git a/win32/config.h b/win32/config.h new file mode 100644 index 0000000000..cf5cb332bf --- /dev/null +++ b/win32/config.h @@ -0,0 +1,60 @@ +#define THREAD 1 +/* #define HAVE_DIRENT_H 1 */ +/* #define HAVE_UNISTD_H 1 */ +#define HAVE_STDLIB_H 1 +#define HAVE_LIMITS_H 1 +#define HAVE_SYS_FILE_H 1 +/* #define HAVE_PWD_H 1 */ +/* #define HAVE_SYS_TIME_H 1 */ +/* #define HAVE_SYS_TIMES_H 1 */ +/* #define HAVE_SYS_PARAM_H 1 */ +/* #define HAVE_SYS_WAIT_H 1 */ +#define HAVE_STRING_H 1 +/* #define HAVE_UTIME_H 1 */ +#define HAVE_MEMORY_H 1 +/* #define HAVE_ST_BLKSIZE 1 */ +#define HAVE_ST_RDEV 1 +/* #define GETGROUPS_T gid_t */ +#define GETGROUPS_T int +#define RETSIGTYPE void +#define HAVE_ALLOCA 1 +#define vfork fork +#define HAVE_FMOD 1 +/* #define HAVE_RANDOM 1 */ +/* #define HAVE_WAITPID 1 */ +#define HAVE_GETCWD 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_SIGPROCMASK 1 */ +#define FILE_COUNT _cnt +#define DLEXT ".dll" +#define RUBY_LIB ";/usr/local/lib/ruby;." +#define RUBY_ARCHLIB "/usr/local/lib/ruby/i386-mswin32" +#define RUBY_PLATFORM "i386-mswin32" + +/* NNN */ +#define strcasecmp _strcmpi +#define popen _popen +#define pclose _pclose +#define pipe _pipe +#define bzero(x, y) memset(x, 0, y) + +#define S_IFMT _S_IFMT +#define S_IFDIR _S_IFDIR +#define S_IFCHR _S_IFCHR +#define S_IFREG _S_IFREG +#define S_IREAD _S_IREAD +#define S_IWRITE _S_IWRITE +#define S_IEXEC _S_IEXEC +#define S_IFIFO _S_IFIFO + +#define UIDTYPE int +#define GIDTYPE int +#define pid_t int +#define WNOHANG -1 +//#define NT diff --git a/win32/ntsetup.bat b/win32/ntsetup.bat new file mode 100755 index 0000000000..ee6294fada --- /dev/null +++ b/win32/ntsetup.bat @@ -0,0 +1,10 @@ +@echo off +copy config.h .. +copy Makefile .. +copy ruby.def .. +cd ..\ext +copy Setup.nt Setup +copy extmk.rb.nt extmk.rb + +cd .. +echo type `nmake' to make ruby for mswin32. diff --git a/win32/ruby.def b/win32/ruby.def new file mode 100644 index 0000000000..44372d2e76 --- /dev/null +++ b/win32/ruby.def @@ -0,0 +1,55 @@ +LIBRARY ruby.dll +CODE LOADONCALL +DATA LOADONCALL +DESCRIPTION 'win32 ruby.dll' +EXPORTS + +;missing/nt.c + NtInitialize +;eval.c + ruby_init + ruby_options + ruby_run +;class.c + ;;rb_define_module_function + ;;rb_define_const + ;;rb_define_singleton_method + ;;rb_define_alias + rb_define_alias + rb_define_attr + rb_define_class + rb_define_class_id + rb_define_class_under + rb_define_global_function + rb_define_method + rb_define_method_id + rb_define_module + rb_define_module_function + rb_define_module_id + rb_define_module_under + rb_define_private_method + rb_define_singleton_method + rb_scan_args +;variable.c + rb_define_const + rb_define_hooked_variable + rb_define_global_const + rb_define_readonly_variable + rb_define_variable + rb_define_virtual_variable +;string.c + str_new + str_new2 + str_new3 + str_new4 +;numeric.c + num2int + +;error.c + rb_check_type + Fatal + +;gc.c + xmalloc + xcalloc + diff --git a/x68/_dtos18.c b/x68/_dtos18.c new file mode 100644 index 0000000000..4712a66bf7 --- /dev/null +++ b/x68/_dtos18.c @@ -0,0 +1,250 @@ +/* + * PROJECT C Library, X68000 PROGRAMMING INTERFACE DEFINITION + * -------------------------------------------------------------------- + * This file is written by the Project C Library Group, and completely + * in public domain. You can freely use, copy, modify, and redistribute + * the whole contents, without this notice. + * -------------------------------------------------------------------- + * $Id$ + */ + +/* System headers */ +#include <stdlib.h> +#include <sys/xstdlib.h> + +/* +** 本関数は浮動小数点を倍精度整数に変換してから文字列にするため、精度的には +** 倍精度整数に格納できる桁数までしか扱うことができない。したがって最高精度 +** は18桁である。 +*/ + +/* File scope variables */ +static double _pos1[32] = { + 1.0e+17, /* + 0 */ + 1.0e+18, /* + 1 */ + 1.0e+19, /* + 2 */ + 1.0e+20, /* + 3 */ + 1.0e+21, /* + 4 */ + 1.0e+22, /* + 5 */ + 1.0e+23, /* + 6 */ + 1.0e+24, /* + 7 */ + 1.0e+25, /* + 8 */ + 1.0e+26, /* + 9 */ + 1.0e+27, /* +10 */ + 1.0e+28, /* +11 */ + 1.0e+29, /* +12 */ + 1.0e+30, /* +13 */ + 1.0e+31, /* +14 */ + 1.0e+32, /* +15 */ + 1.0e+33, /* +16 */ + 1.0e+34, /* +17 */ + 1.0e+35, /* +18 */ + 1.0e+36, /* +19 */ + 1.0e+37, /* +20 */ + 1.0e+38, /* +21 */ + 1.0e+39, /* +22 */ + 1.0e+40, /* +23 */ + 1.0e+41, /* +24 */ + 1.0e+42, /* +25 */ + 1.0e+43, /* +26 */ + 1.0e+44, /* +27 */ + 1.0e+45, /* +28 */ + 1.0e+46, /* +29 */ + 1.0e+47, /* +30 */ + 1.0e+48, /* +31 */ +}; + +/* File scope variables */ +static double _neg1[32] = { + 1.0e+17, /* - 0 */ + 1.0e+16, /* - 1 */ + 1.0e+15, /* - 2 */ + 1.0e+14, /* - 3 */ + 1.0e+13, /* - 4 */ + 1.0e+12, /* - 5 */ + 1.0e+11, /* - 6 */ + 1.0e+10, /* - 7 */ + 1.0e+9, /* - 8 */ + 1.0e+8, /* - 9 */ + 1.0e+7, /* -10 */ + 1.0e+6, /* -11 */ + 1.0e+5, /* -12 */ + 1.0e+4, /* -13 */ + 1.0e+3, /* -14 */ + 1.0e+2, /* -15 */ + 1.0e+1, /* -16 */ + 1.0e+0, /* -17 */ + 1.0e-1, /* -18 */ + 1.0e-2, /* -19 */ + 1.0e-3, /* -20 */ + 1.0e-4, /* -21 */ + 1.0e-5, /* -22 */ + 1.0e-6, /* -23 */ + 1.0e-7, /* -24 */ + 1.0e-8, /* -25 */ + 1.0e-9, /* -26 */ + 1.0e-10, /* -27 */ + 1.0e-11, /* -28 */ + 1.0e-12, /* -29 */ + 1.0e-13, /* -30 */ + 1.0e-14, /* -31 */ +}; + +/* File scope variables */ +static double _pos2[10] = { + 1.0e+0, /* 000 */ + 1.0e+32, /* 001 */ + 1.0e+64, /* 010 */ + 1.0e+96, /* 011 */ + 1.0e+128, /* 100 */ + 1.0e+160, /* 101 */ + 1.0e+192, /* 110 */ + 1.0e+224, /* 111 */ + 1.0e+256, /* 1000 */ + 1.0e+288, /* 1001 */ +}; + +/* File scope variables */ +static double _neg2[10] = { + 1.0e-0, /* 000 */ + 1.0e-32, /* 001 */ + 1.0e-64, /* 010 */ + 1.0e-96, /* 011 */ + 1.0e-128, /* 100 */ + 1.0e-160, /* 101 */ + 1.0e-192, /* 110 */ + 1.0e-224, /* 111 */ + 1.0e-256, /* 1000 */ + 1.0e-288, /* 1001 */ +}; + +/* File scope functions */ +static int _cmpd (double x, double y) +{ + unsigned long vx, vy, rc; + unsigned long *x_ptr = (unsigned long *) &x; + unsigned long *y_ptr = (unsigned long *) &y; + + /* xの指数ビットを取り出す */ + vx = x_ptr[0] & 0x7FF00000; + + /* yの指数ビットを取り出す */ + vy = y_ptr[0] & 0x7FF00000; + + /* 指数ビットだけで判断する */ + if ((rc = vy - vx) != 0) + return rc; + + /* xの有効数字の上位ビットを取り出す */ + vx = x_ptr[0] & 0x000FFFFF; + + /* yの有効数字の上位ビットを取り出す */ + vy = y_ptr[0] & 0x000FFFFF; + + /* 上位ビットだけで判断する */ + if ((rc = vy - vx) != 0) + return rc; + + /* xの有効数字の下位ビットを取り出す */ + vx = x_ptr[1]; + + /* yの有効数字の下位ビットを取り出す */ + vy = y_ptr[1]; + + /* 最終判断 */ + return vy - vx; +} + +/* Functions */ +void _dtos18 (double x, int *decpt, int *sign, char *buffer) +{ + short e2; + int e, n; + + /* 基数2の指数を求める(バイアスなしの状態) */ + e2 = (((unsigned short *) &x)[0] & 0x7FF0U) >> 4; + + /* 指数が0の場合は±0.0チェック */ + if (e2 == 0) { + + unsigned long hi = ((unsigned long *) &x)[0] & 0xFFFFF; + unsigned long lo = ((unsigned long *) &x)[1]; + + /* 有効数字が全部0かどうか */ + if (hi == 0 && lo == 0) { + + /* 文字列を設定 */ + buffer[0] = '0'; + + /* NULを設定 */ + buffer[1] = '\0'; + + /* 小数点位置を計算 */ + *decpt = 1; + + /* 符号を計算 */ + /* *sign = hi & 0x80000000UL; */ + *sign = 0; + + /* 確定 */ + return; + + } + + } + + /* 2の指数にバイアスをかけてから10の指数を概算 (approx. log10(2)) */ + e = ((int) ((e2 - 1023) * 77)) >> 8; + + /* 指数が正の場合 */ + if (e >= 0) { + + /* 指数が32より小さい場合はテーブル1から */ + if (e < 32) + x *= _neg1[e]; + + /* 指数が32より大きい場合はテーブル1,2から */ + else + x *= _neg1[e & 31] * _neg2[e >> 5]; + + } + + /* 指数が負の場合 */ + else { + + /* 絶対値を計算 */ + n = -e; + + /* 絶対値が32より小さい場合はテーブル1から */ + if (n < 32) + x *= _pos1[n]; + + /* 絶対値が32より大きい場合はテーブル1,2から */ + else { + x *= _pos1[n & 31]; + x *= _pos2[n >> 5]; + } + + } + + /* スケーリングしすぎた場合は戻す */ + if (_cmpd (1.0e+18, x) >= 0) { + e++; + x *= 1.0e-1; + } + + /* スケーリングし足りない場合は追加 */ + else if (_cmpd (1.0e+17, x) < 0) { + e--; + x *= 1.0e+1; + } + + /* 小数点位置を計算 */ + *decpt = e + 1; + + /* 符号を計算 */ + *sign = ((unsigned char *) &x)[0] & 0x80U; + + /* 文字列に変換 */ + _ulltoa ((unsigned long long) x, buffer); +} diff --git a/x68/_round.c b/x68/_round.c new file mode 100644 index 0000000000..761930fb8c --- /dev/null +++ b/x68/_round.c @@ -0,0 +1,45 @@ +/* + * PROJECT C Library, X68000 PROGRAMMING INTERFACE DEFINITION + * -------------------------------------------------------------------- + * This file is written by the Project C Library Group, and completely + * in public domain. You can freely use, copy, modify, and redistribute + * the whole contents, without this notice. + * -------------------------------------------------------------------- + * $Id$ + */ +/* changed 1997.2.2 by K.Okabe */ + +/* System headers */ +#include <stdlib.h> +#include <sys/xstdlib.h> + +/* Functions */ +int _round (char *top, char *cur, int undig) +{ + char *ptr; + + /* 最後が5未満なら丸めは必要ない */ + if (undig < '5') + return 0; + + /* ポインタ設定 */ + ptr = cur - 1; + + /* 先頭まで戻りながら丸め処理 */ + while (ptr >= top) { + + /* 繰り上がらなければそれで終わり */ + if (++(*ptr) <= '9') + return 0; + + /* その桁を0に戻す */ + *ptr-- = '0'; + + } + + /* 先頭を1にする */ + *++ptr = '1'; + + /* 繰り上がりをしらせる */ + return 1; +} diff --git a/x68/fconvert.c b/x68/fconvert.c new file mode 100644 index 0000000000..9a0bc0e088 --- /dev/null +++ b/x68/fconvert.c @@ -0,0 +1,81 @@ +/* + * PROJECT C Library, X68000 PROGRAMMING INTERFACE DEFINITION + * -------------------------------------------------------------------- + * This file is written by the Project C Library Group, and completely + * in public domain. You can freely use, copy, modify, and redistribute + * the whole contents, without this notice. + * -------------------------------------------------------------------- + * $Id$ + */ +/* changed 1997.2.3 by K.Okabe */ + +/* System headers */ +#include <stdlib.h> +#include <sys/xstdlib.h> + +/* Functions */ +char *fconvert (double x, int ndigit, int *decpt, int *sign, char *buffer) +{ + int pos, n; + char *src, *dst; + char string[24]; + int figup; + + /* 18桁の文字列に変換 */ + _dtos18 (x, decpt, sign, string); + + /* コピー元アドレスを設定 */ + src = string; + + /* コピー先アドレスを設定 */ + dst = buffer; + + /* 小数点位置を得る */ + pos = *decpt; + + /* 小数点位置が負なら */ + if (pos < 0) { + + /* 埋める桁数を計算 */ + n = min (-pos, ndigit); + + /* 先頭を0で埋める */ + while (n-- > 0) + *dst++ = '0'; + + /* 小数点位置は0になる */ + *decpt = 0; + + } + + /* 残りのコピー桁数 */ + n = ndigit + pos; + + /* 格納先にコピー */ + while (n-- > 0) { + + /* 足りない部分は0で埋める */ + if (*src == '\0') { + while (n-- >= 0) + *dst++ = '0'; + break; + } + + /* 変換文字列からコピー */ + *dst++ = *src++; + + } + + /* 丸める */ + *decpt += (figup = _round (buffer, dst, *src)); + + /* 繰り上がりがあれば末尾に0を追加する */ + if (figup) + *dst++ = '0'; + + /* 終端に NUL を打つ */ + *dst = '\0'; + + /* アドレスを返す */ + return buffer; +} diff --git a/x68/select.c b/x68/select.c new file mode 100644 index 0000000000..b4bf464032 --- /dev/null +++ b/x68/select.c @@ -0,0 +1,167 @@ +/* + * PROJECT C Library, X68000 PROGRAMMING INTERFACE DEFINITION + * -------------------------------------------------------------------- + * This file is written by the Project C Library Group, and completely + * in public domain. You can freely use, copy, modify, and redistribute + * the whole contents, without this notice. + * -------------------------------------------------------------------- + * $Id$ + */ + +#ifndef __IOCS_INLINE__ +#define __IOCS_INLINE__ +#define __DOS_INLINE__ +#define __DOS_DOSCALL__ +#endif + +/* System headers */ +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <sys/dos.h> +#include <sys/iocs.h> +#include <sys/time.h> +#include <sys/types.h> +#if 0 +#include <sys/select.h> +#include <sys/xsocket.h> +#endif +#include <sys/xunistd.h> + +/* Macros */ +#define XFD_ISSET(fd,fds) ((fds) && FD_ISSET ((fd), (fds))) +#define isreadable(mode) ((mode) == O_RDONLY || (mode) == O_RDWR) +#define iswritable(mode) ((mode) == O_WRONLY || (mode) == O_RDWR) +#ifndef _POSIX_FD_SETSIZE +#define _POSIX_FD_SETSIZE OPEN_MAX +#endif + +/* Functions */ +int +select (int fds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *timeout) +{ + fd_set oread, owrite, oexcept; + int ticks, start; + int nfds; + + if (fds > _POSIX_FD_SETSIZE) + { + errno = EINVAL; + return -1; + } + + FD_ZERO (&oread); + FD_ZERO (&owrite); + FD_ZERO (&oexcept); + + nfds = 0; + ticks = -1; + + if (timeout) + { + ticks = timeout->tv_sec * 100 + timeout->tv_usec / 10000; + if (ticks < 0) + { + errno = EINVAL; + return -1; + } + } + + start = _iocs_ontime (); + for (;;) + { + { + int fd; + + for (fd = 0; fd < fds; fd++) + { + int accmode; + + if (_fddb[fd].inuse == _FD_NOTUSED) + continue; + + accmode = _fddb[fd].oflag & O_ACCMODE; + + if (isatty (fd)) + { + if (XFD_ISSET (fd, rfds) && isreadable (accmode) && _dos_k_keysns ()) + { + FD_SET (fd, &oread); + nfds++; + } + + if (XFD_ISSET (fd, wfds) && iswritable (accmode)) + { + FD_SET (fd, &owrite); + nfds++; + } + } +#if 0 + else if (_fddb[fd].sockno >= 0) + { + if (XFD_ISSET (fd, rfds) && _socklen (_fddb[fd].sockno, 0)) + { + FD_SET (fd, &oread); + nfds++; + } + + if (XFD_ISSET (fd, wfds) /* && _socklen (_fddb[fd].sockno, 1) == 0 */) + { + FD_SET (fd, &owrite); + nfds++; + } + } +#endif + else + { + if (XFD_ISSET (fd, rfds) && isreadable (accmode) && _dos_ioctrlis (fd)) + { + FD_SET (fd, &oread); + nfds++; + } + + if (XFD_ISSET (fd, wfds) && iswritable (accmode) && _dos_ioctrlos (fd)) + { + FD_SET (fd, &owrite); + nfds++; + } + } + } + } + + { + int rest; + + if ((rest = (_iocs_ontime () - start) % 8640000) < 0) + rest += 8640000; + + if (nfds != 0) + { + if (ticks >= 0) + { + int left; + + if ((left = ticks - rest) < 0) + left = 0; + + timeout->tv_sec = left / 100; + timeout->tv_usec = (left % 100) * 10000; + } + + if (rfds) + *rfds = oread; + if (wfds) + *wfds = owrite; + if (efds) + *efds = oexcept; + + return nfds; + } + + if (ticks >= 0 && rest > ticks) + return 0; + } + + _dos_change_pr (); + } +} |