From 1b821cb4a32b40ba6392abbba6c941aceb47e54d Mon Sep 17 00:00:00 2001 From: akr Date: Mon, 18 Aug 2008 14:28:45 +0000 Subject: [PATCH] * io.c (io_ungetbyte): renamed from io_ungetc. (rb_io_ungetbyte): new method. (rb_io_ungetc): push back into character buffer if enc2 is set. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@18694 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 6 ++++ io.c | 65 ++++++++++++++++++++++++++++++++++----- test/ruby/test_io_m17n.rb | 32 ++++++++++++++----- 3 files changed, 88 insertions(+), 15 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3336f3fd31..9585754ae8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +Mon Aug 18 23:27:07 2008 Tanaka Akira + + * io.c (io_ungetbyte): renamed from io_ungetc. + (rb_io_ungetbyte): new method. + (rb_io_ungetc): push back into character buffer if enc2 is set. + Mon Aug 18 22:41:46 2008 Tanaka Akira * io.c (id_encode): removed. diff --git a/io.c b/io.c index 33b1045274..df1ae247dd 100644 --- a/io.c +++ b/io.c @@ -326,14 +326,10 @@ io_unread(rb_io_t *fptr) static rb_encoding *io_input_encoding(rb_io_t *fptr); static void -io_ungetc(VALUE str, rb_io_t *fptr) +io_ungetbyte(VALUE str, rb_io_t *fptr) { int len = RSTRING_LEN(str); - if (rb_enc_dummy_p(io_input_encoding(fptr))) { - rb_raise(rb_eNotImpError, "ungetc against dummy encoding is not currently supported"); - } - if (fptr->rbuf == NULL) { fptr->rbuf_off = 0; fptr->rbuf_len = 0; @@ -344,7 +340,7 @@ io_ungetc(VALUE str, rb_io_t *fptr) fptr->rbuf = ALLOC_N(char, fptr->rbuf_capa); } if (fptr->rbuf_capa < len + fptr->rbuf_len) { - rb_raise(rb_eIOError, "ungetc failed"); + rb_raise(rb_eIOError, "ungetbyte failed"); } if (fptr->rbuf_off < len) { MEMMOVE(fptr->rbuf+fptr->rbuf_capa-fptr->rbuf_len, @@ -2713,6 +2709,42 @@ rb_io_readbyte(VALUE io) return c; } +/* + * call-seq: + * ios.ungetbyte(string) => nil + * ios.ungetbyte(integer) => nil + * + * Pushes back bytes (passed as a parameter) onto ios, + * such that a subsequent buffered read will return it. Only one byte + * may be pushed back before a subsequent read operation (that is, + * you will be able to read only the last of several bytes that have been pushed + * back). Has no effect with unbuffered reads (such as IO#sysread). + * + * f = File.new("testfile") #=> # + * b = f.getbyte #=> 0x38 + * f.ungetbyte(b) #=> nil + * f.getbyte #=> 0x38 + */ + +VALUE +rb_io_ungetbyte(VALUE io, VALUE b) +{ + rb_io_t *fptr; + + GetOpenFile(io, fptr); + rb_io_check_readable(fptr); + if (NIL_P(b)) return Qnil; + if (FIXNUM_P(b)) { + char cc = FIX2INT(b); + b = rb_str_new(&cc, 1); + } + else { + SafeStringValue(b); + } + io_ungetbyte(b, fptr); + return Qnil; +} + /* * call-seq: * ios.ungetc(string) => nil @@ -2733,6 +2765,7 @@ VALUE rb_io_ungetc(VALUE io, VALUE c) { rb_io_t *fptr; + long len; GetOpenFile(io, fptr); rb_io_check_readable(fptr); @@ -2747,7 +2780,24 @@ rb_io_ungetc(VALUE io, VALUE c) else { SafeStringValue(c); } - io_ungetc(c, fptr); + if (fptr->enc2) { + make_readconv(fptr); + len = RSTRING_LEN(c); + if (fptr->crbuf_capa - fptr->crbuf_len < len) + rb_raise(rb_eIOError, "ungetc failed"); + if (fptr->crbuf_off < len) { + MEMMOVE(fptr->crbuf+fptr->crbuf_capa-fptr->crbuf_len, + fptr->crbuf+fptr->crbuf_off, + char, fptr->crbuf_len); + fptr->crbuf_off = fptr->crbuf_capa-fptr->crbuf_len; + } + fptr->crbuf_off -= len; + fptr->crbuf_len += len; + MEMMOVE(fptr->crbuf+fptr->crbuf_off, RSTRING_PTR(c), char, len); + } + else { + io_ungetbyte(c, fptr); + } return Qnil; } @@ -8019,6 +8069,7 @@ Init_IO(void) rb_define_method(rb_cIO, "getbyte", rb_io_getbyte, 0); rb_define_method(rb_cIO, "readchar", rb_io_readchar, 0); rb_define_method(rb_cIO, "readbyte", rb_io_readbyte, 0); + rb_define_method(rb_cIO, "ungetbyte",rb_io_ungetbyte, 1); rb_define_method(rb_cIO, "ungetc",rb_io_ungetc, 1); rb_define_method(rb_cIO, "<<", rb_io_addstr, 1); rb_define_method(rb_cIO, "flush", rb_io_flush, 0); diff --git a/test/ruby/test_io_m17n.rb b/test/ruby/test_io_m17n.rb index 070987ad3c..b059f70ff3 100644 --- a/test/ruby/test_io_m17n.rb +++ b/test/ruby/test_io_m17n.rb @@ -239,14 +239,30 @@ EOT with_tmpdir { src = "before \e$B\x23\x30\x23\x31\e(B after".force_encoding("iso-2022-jp") generate_file('tmp', src) - assert_raise(NotImplementedError) do - s = open("tmp", "r:iso-2022-jp:euc-jp") {|f| - f.ungetc("0".force_encoding("euc-jp")) - f.read - } - assert_equal(Encoding.find("euc-jp"), s.encoding) - assert_str_equal(("0" + src).encode("euc-jp"), s) - end + s = open("tmp", "r:iso-2022-jp:euc-jp") {|f| + f.ungetc("0".force_encoding("euc-jp")) + f.read + } + assert_equal(Encoding.find("euc-jp"), s.encoding) + assert_str_equal("0" + src.encode("euc-jp"), s) + } + end + + def test_ungetc_stateful_conversion2 + with_tmpdir { + src = "before \e$B\x23\x30\x23\x31\e(B after".force_encoding("iso-2022-jp") + former = "before \e$B\x23\x30\e(B".force_encoding("iso-2022-jp") + rs = "\e$B\x23\x30\e(B".force_encoding("iso-2022-jp") + latter = "\e$B\x23\x31\e(B after".force_encoding("iso-2022-jp") + generate_file('tmp', src) + s = open("tmp", "r:iso-2022-jp:euc-jp") {|f| + assert_equal(former.encode("euc-jp", "iso-2022-jp"), + f.gets(rs.encode("euc-jp", "iso-2022-jp"))) + f.ungetc("0") + f.read + } + assert_equal(Encoding.find("euc-jp"), s.encoding) + assert_str_equal("0" + latter.encode("euc-jp"), s) } end