From: Nobuyoshi Nakada Date: 2008-03-03T15:56:33+09:00 Subject: [ruby-dev:33969] ungetc and encoding (Re: Re: --encoding affects script encoding) なかだです。 At Mon, 3 Mar 2008 00:54:33 +0900, Tanaka Akira wrote in [ruby-dev:33960]: > > コミットしましたが、IO#ungetcでのエンコーディングの扱いが抜けて > > いることに気づきました。 > > > else { > > SafeStringValue(c); > > + enc = io_input_encoding(fptr); > > + } > > + if (enc && enc != rb_enc_get(c)) { > > + c = rb_funcall(c, id_encode, 1, rb_enc_from_encoding(enc)); > > } > > io_ungetc(c, fptr); > > encdetect を実装するには、IO からバイト列で読み出したものを > バイト列として ungetc することが必要なんですが、それはできる > でしょうか。 それは現状でもできないですね。?xもStringになったことでもあるし、 0..255の整数の並びはバイト列として戻す、というのでもいいかも。 > 別メソッドかなぁ? getcとgetbyteも分けたことを考えれば、こっちですかねぇ。 ungetbyteというのはいまいちな気がしますが。 Index: io.c =================================================================== --- io.c (revision 15671) +++ io.c (working copy) @@ -312,9 +312,7 @@ io_unread(rb_io_t *fptr) } -static void -io_ungetc(VALUE str, rb_io_t *fptr) +static char * +io_unget_space(long len, rb_io_t *fptr) { - int len = RSTRING_LEN(str); - if (fptr->rbuf == NULL) { fptr->rbuf_off = 0; @@ -337,5 +335,11 @@ io_ungetc(VALUE str, rb_io_t *fptr) fptr->rbuf_off-=len; fptr->rbuf_len+=len; - MEMMOVE(fptr->rbuf+fptr->rbuf_off, RSTRING_PTR(str), char, len); + return fptr->rbuf+fptr->rbuf_off; +} + +static void +io_ungetc(const char *ptr, long len, rb_io_t *fptr) +{ + MEMMOVE(io_unget_space(len, fptr), ptr, char, len); } @@ -680,10 +684,10 @@ io_fwrite(VALUE str, rb_io_t *fptr) /* Can't use encode! because puts writes a frozen newline */ if (fptr->enc2) { - str = rb_funcall(str, id_encode, 2, + str = rb_funcall(str, id_encode, 2, rb_enc_from_encoding(fptr->enc2), rb_enc_from_encoding(fptr->enc)); } else { - str = rb_funcall(str, id_encode, 1, + str = rb_funcall(str, id_encode, 1, rb_enc_from_encoding(fptr->enc)); } @@ -1348,5 +1352,5 @@ io_enc_str(VALUE str, rb_io_t *fptr) /* two encodings, so transcode from enc2 to enc */ /* the methods in transcode.c are static, so call indirectly */ - str = rb_funcall(str, id_encode, 2, + str = rb_funcall(str, id_encode, 2, rb_enc_from_encoding(fptr->enc), rb_enc_from_encoding(fptr->enc2)); @@ -1896,5 +1900,5 @@ prepare_getline_args(int argc, VALUE *ar if (fptr->enc2) { VALUE rs2; - rs2 = rb_funcall(rs, id_encode, 2, + rs2 = rb_funcall(rs, id_encode, 2, rb_enc_from_encoding(fptr->enc2), rb_enc_from_encoding(fptr->enc)); @@ -2414,10 +2418,11 @@ rb_io_readbyte(VALUE io) * call-seq: * ios.ungetc(string) => nil + * ios.ungetc(byte) => nil + * ios.ungetc(byte_ary) => nil * - * Pushes back one character (passed as a parameter) onto ios, - * such that a subsequent buffered read will return it. Only one character - * may be pushed back before a subsequent read operation (that is, - * you will be able to read only the last of several characters that have been pushed - * back). Has no effect with unbuffered reads (such as IO#sysread). + * Pushes back character string or byte sequence (passed as a + * parameter) onto ios, such that a subsequent buffered read + * will return it. Has no effect with unbuffered reads (such as + * IO#sysread). * * f = File.new("testfile") #=> # @@ -2425,25 +2430,89 @@ rb_io_readbyte(VALUE io) * f.ungetc(c) #=> nil * f.getc #=> "8" + * f.ungetc(0x39, 10) #=> nil + * f.gets #=> "9\n" */ VALUE -rb_io_ungetc(VALUE io, VALUE c) +rb_io_unget(int argc, VALUE *argv, VALUE io) { rb_io_t *fptr; + VALUE c; GetOpenFile(io, fptr); rb_io_check_readable(fptr); + if (argc < 1) return Qnil; + if (argc > 1) { /* expects byte array */ + /* use temporary buffer to get rid of race condition */ + volatile VALUE str = rb_str_tmp_new(argc); + char *ptr = RSTRING_PTR(str); + int i; + + for (i = 0; i < argc; ++i) { + c = argv[i]; + ptr[i] = (char)NUM2INT(c); + } + io_ungetc(ptr, argc, fptr); + rb_str_resize(str, 0); + return Qnil; + } + c = argv[0]; if (NIL_P(c)) return Qnil; if (FIXNUM_P(c)) { - int cc = FIX2INT(c); - rb_encoding *enc = io_read_encoding(fptr); - char buf[16]; + char cc = (char)FIX2INT(c); - c = rb_str_new(buf, rb_enc_mbcput(cc, buf, enc)); + io_ungetc(&cc, 1, fptr); } else { + rb_encoding *enc = io_input_encoding(fptr); SafeStringValue(c); + if (rb_enc_get_index(c) && enc != rb_enc_get(c)) { + c = rb_funcall(c, id_encode, 1, rb_enc_from_encoding(enc)); + } + io_ungetc(RSTRING_PTR(c), RSTRING_LEN(c), fptr); + RB_GC_GUARD(c); + } + return Qnil; +} + +VALUE +rb_io_ungetc(VALUE io, VALUE c) +{ + VALUE ary = rb_check_array_type(c); + if (!NIL_P(ary)) { + rb_io_unget(RARRAY_LEN(ary), RARRAY_PTR(ary), io); + RB_GC_GUARD(ary); } - io_ungetc(c, fptr); + else { + rb_io_unget(1, &c, io); + } + return Qnil; +} + +/* + * call-seq: + * ios.ungetbyte(fixnum) => nil + * + * Pushes back one character (passed as a parameter) onto + * ios, such that a subsequent buffered read will return it. + * See also IO#ungetc. + * + * f = File.new("testfile") + * c = f.getbyte #=> 84 + * f.ungetbyte(c) #=> nil + * f.getbyte #=> 84 + */ + +VALUE +rb_io_ungetbyte(VALUE io, VALUE c) +{ + rb_io_t *fptr; + int cc; + + GetOpenFile(io, fptr); + rb_io_check_readable(fptr); + if (NIL_P(c)) return Qnil; + cc = NUM2INT(c); + *io_unget_space(1, fptr) = (char)cc; return Qnil; } @@ -6789,5 +6858,6 @@ Init_IO(void) 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, "ungetc",rb_io_ungetc, 1); + rb_define_method(rb_cIO, "ungetc", rb_io_unget, -1); + rb_define_method(rb_cIO, "ungetbyte", rb_io_ungetbyte, 1); rb_define_method(rb_cIO, "<<", rb_io_addstr, 1); rb_define_method(rb_cIO, "flush", rb_io_flush, 0); -- --- 僕の前にBugはない。 --- 僕の後ろにBugはできる。 中田 伸悦