From 424f8ec44fa2ee315f72c1f9384d0c8a4796f8ac Mon Sep 17 00:00:00 2001 From: matz Date: Fri, 29 Dec 2006 19:21:50 +0000 Subject: [PATCH] * ext/stringio/stringio.c (strio_gets): accepts limit argument. * ext/stringio/stringio.c (strio_readline, strio_each, strio_readlines): ditto. * ext/stringio/stringio.c (strio_getline): add limit capability. * io.c (rb_io_gets_m): accepts limit argument. [ruby-talk:231563] * io.c (rb_io_readline, rb_io_readlines, rb_io_each_line, argf_getline): ditto. * io.c (appendline): add limit capability. * io.c (rb_io_getline_fast, rb_io_getline): ditto. * io.c (rb_io_getline): small refactoring for DRY. * io.c (rb_io_s_foreach, rb_io_s_readlines): small refactoring. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11428 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 24 +++++ ext/stringio/stringio.c | 47 ++++++-- io.c | 231 +++++++++++++++++++++++----------------- version.h | 6 +- 4 files changed, 198 insertions(+), 110 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4d32153cc4..9a5e32ebda 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,27 @@ +Sat Dec 30 02:54:22 2006 Yukihiro Matsumoto + + * ext/stringio/stringio.c (strio_gets): accepts limit argument. + + * ext/stringio/stringio.c (strio_readline, strio_each, + strio_readlines): ditto. + +Sat Dec 30 02:22:32 2006 Yukihiro Matsumoto + + * ext/stringio/stringio.c (strio_getline): add limit capability. + + * io.c (rb_io_gets_m): accepts limit argument. [ruby-talk:231563] + + * io.c (rb_io_readline, rb_io_readlines, rb_io_each_line, argf_getline): + ditto. + + * io.c (appendline): add limit capability. + + * io.c (rb_io_getline_fast, rb_io_getline): ditto. + + * io.c (rb_io_getline): small refactoring for DRY. + + * io.c (rb_io_s_foreach, rb_io_s_readlines): small refactoring. + Thu Dec 28 15:27:38 2006 Yukihiro Matsumoto * lib/cgi.rb (CGI::Cookie::initialize): use Array() again. diff --git a/ext/stringio/stringio.c b/ext/stringio/stringio.c index d0c97037e4..969d007f8b 100644 --- a/ext/stringio/stringio.c +++ b/ext/stringio/stringio.c @@ -766,15 +766,33 @@ static VALUE strio_getline(int argc, VALUE *argv, struct StringIO *ptr) { const char *s, *e, *p; - long n; + long n, limit; VALUE str; if (argc == 0) { str = rb_rs; + limit = 0; } else { - rb_scan_args(argc, argv, "1", &str); - if (!NIL_P(str)) StringValue(str); + VALUE lim, tmp; + + rb_scan_args(argc, argv, "11", &str, &lim); + if (!NIL_P(lim)) limit = NUM2LONG(lim); + else if (!NIL_P(str) && TYPE(str) != T_STRING) { + tmp = rb_check_string_type(str); + if (NIL_P(tmp)) { + limit = NUM2LONG(str); + if (limit == 0) return rb_str_new(0,0); + str = rb_rs; + } + else { + str = tmp; + limit = 0; + } + } + else { + StringValue(str); + } } if (ptr->pos >= (n = RSTRING_LEN(ptr->string))) { @@ -783,6 +801,9 @@ strio_getline(int argc, VALUE *argv, struct StringIO *ptr) s = RSTRING_PTR(ptr->string); e = s + RSTRING_LEN(ptr->string); s += ptr->pos; + if (limit > 0 && s + limit < e) { + e = s + limit; + } if (NIL_P(str)) { str = rb_str_substr(ptr->string, ptr->pos, e - s); } @@ -836,7 +857,9 @@ strio_getline(int argc, VALUE *argv, struct StringIO *ptr) /* * call-seq: - * strio.gets(sep_string=$/) -> string or nil + * strio.gets(sep=$/) -> string or nil + * strio.gets(limit) -> string or nil + * strio.gets(sep, limit) -> string or nil * * See IO#gets. */ @@ -851,7 +874,9 @@ strio_gets(int argc, VALUE *argv, VALUE self) /* * call-seq: - * strio.readline(sep_string=$/) -> string + * strio.readline(sep=$/) -> string + * strio.readline(limit) -> string or nil + * strio.readline(sep, limit) -> string or nil * * See IO#readline. */ @@ -865,8 +890,12 @@ strio_readline(int argc, VALUE *argv, VALUE self) /* * call-seq: - * strio.each(sep_string=$/) {|line| block } -> strio - * strio.each_line(sep_string=$/) {|line| block } -> strio + * strio.each(sep=$/) {|line| block } -> strio + * strio.each(limit) {|line| block } -> strio + * strio.each(sep, limit) {|line| block } -> strio + * strio.each_line(sep=$/) {|line| block } -> strio + * strio.each_line(limit) {|line| block } -> strio + * strio.each_line(sep,limit) {|line| block } -> strio * * See IO#each. */ @@ -884,7 +913,9 @@ strio_each(int argc, VALUE *argv, VALUE self) /* * call-seq: - * strio.readlines(sep_string=$/) -> array + * strio.readlines(sep=$/) -> array + * strio.readlines(limit) -> array + * strio.readlines(sep,limit) -> array * * See IO#readlines. */ diff --git a/io.c b/io.c index a78259ba51..639f40b068 100644 --- a/io.c +++ b/io.c @@ -1535,17 +1535,21 @@ io_read(int argc, VALUE *argv, VALUE io) } static int -appendline(OpenFile *fptr, int delim, VALUE *strp) +appendline(OpenFile *fptr, int delim, VALUE *strp, long *lp) { VALUE str = *strp; int c = EOF; + long limit = *lp; do { long pending = READ_DATA_PENDING_COUNT(fptr); if (pending > 0) { const char *p = READ_DATA_PENDING_PTR(fptr); - const char *e = memchr(p, delim, pending); + const char *e; long last = 0, len = (c != EOF); + + if (limit > 0 && pending > limit) pending = limit; + e = memchr(p, delim, pending); if (e) pending = e - p + 1; len += pending; if (!NIL_P(str)) { @@ -1560,6 +1564,9 @@ appendline(OpenFile *fptr, int delim, VALUE *strp) RSTRING_PTR(str)[last++] = c; } read_buffered_data(RSTRING_PTR(str) + last, pending, fptr); /* must not fail */ + limit -= pending; + *lp = limit; + if (limit == 0) return RSTRING_PTR(str)[RSTRING_LEN(str)-1]; if (e) return delim; } else if (c != EOF) { @@ -1576,7 +1583,9 @@ appendline(OpenFile *fptr, int delim, VALUE *strp) rb_thread_wait_fd(fptr->fd); rb_io_check_closed(fptr); c = io_getc(fptr); + limit--; if (c < 0) { + *lp = limit; return c; } } while (c != delim); @@ -1624,16 +1633,21 @@ swallow(OpenFile *fptr, int term) } static VALUE -rb_io_getline_fast(OpenFile *fptr, unsigned char delim) +rb_io_getline_fast(OpenFile *fptr, unsigned char delim, long limit) { VALUE str = Qnil; int c; - while ((c = appendline(fptr, delim, &str)) != EOF && c != delim); + for (;;) { + c = appendline(fptr, delim, &str, &limit); + if (c == EOF || c == delim || limit == 0) break; + } if (!NIL_P(str)) { - fptr->lineno++; - lineno = INT2FIX(fptr->lineno); + if (limit > 0) { + fptr->lineno++; + lineno = INT2FIX(fptr->lineno); + } OBJ_TAINT(str); } @@ -1649,10 +1663,31 @@ rscheck(const char *rsptr, long rslen, VALUE rs) } static VALUE -rb_io_getline(VALUE rs, VALUE io) +rb_io_getline(int argc, VALUE *argv, VALUE io) { - VALUE str = Qnil; + VALUE rs, lim, str = Qnil; OpenFile *fptr; + long limit; + + if (argc == 0) { + rs = rb_rs; + lim = Qnil; + } + else { + rb_scan_args(argc, argv, "11", &rs, &lim); + if (NIL_P(lim) && !NIL_P(rs) && TYPE(rs) != T_STRING) { + VALUE tmp = rb_check_string_type(rs); + + if (NIL_P(tmp)) { + lim = rs; + rs = rb_rs; + } + else { + rs = tmp; + } + } + } + limit = NIL_P(lim) ? 0 : NUM2LONG(lim); GetOpenFile(io, fptr); rb_io_check_readable(fptr); @@ -1660,8 +1695,11 @@ rb_io_getline(VALUE rs, VALUE io) str = read_all(fptr, 0, Qnil); if (RSTRING_LEN(str) == 0) return Qnil; } + else if (!NIL_P(lim) && limit == 0) { + return rb_str_new(0,0); + } else if (rs == rb_default_rs) { - return rb_io_getline_fast(fptr, '\n'); + return rb_io_getline_fast(fptr, '\n', limit); } else { int c, newline; @@ -1677,20 +1715,21 @@ rb_io_getline(VALUE rs, VALUE io) swallow(fptr, '\n'); } else if (rslen == 1) { - return rb_io_getline_fast(fptr, (unsigned char)RSTRING_PTR(rs)[0]); + return rb_io_getline_fast(fptr, (unsigned char)RSTRING_PTR(rs)[0], limit); } else { rsptr = RSTRING_PTR(rs); } newline = rsptr[rslen - 1]; - while ((c = appendline(fptr, newline, &str)) != EOF) { + while ((c = appendline(fptr, newline, &str, &limit)) != EOF) { if (c == newline) { if (RSTRING_LEN(str) < rslen) continue; if (!rspara) rscheck(rsptr, rslen, rs); if (memcmp(RSTRING_PTR(str) + RSTRING_LEN(str) - rslen, rsptr, rslen) == 0) break; } + if (limit == 0) break; } if (rspara) { @@ -1701,8 +1740,10 @@ rb_io_getline(VALUE rs, VALUE io) } if (!NIL_P(str)) { - fptr->lineno++; - lineno = INT2FIX(fptr->lineno); + if (limit > 0) { + fptr->lineno++; + lineno = INT2FIX(fptr->lineno); + } OBJ_TAINT(str); } @@ -1716,21 +1757,25 @@ rb_io_gets(VALUE io) GetOpenFile(io, fptr); rb_io_check_readable(fptr); - return rb_io_getline_fast(fptr, '\n'); + return rb_io_getline_fast(fptr, '\n', 0); } /* * call-seq: - * ios.gets(sep_string=$/) => string or nil + * ios.gets(sep=$/) => string or nil + * ios.gets(limit) => string or nil + * ios.gets(sep, limit) => string or nil * * Reads the next ``line'' from the I/O stream; lines are separated by - * sep_string. A separator of nil reads the entire + * sep. A separator of nil reads the entire * contents, and a zero-length separator reads the input a paragraph at * a time (two successive newlines in the input separate paragraphs). * The stream must be opened for reading or an IOError * will be raised. The line read in will be returned and also assigned * to $_. Returns nil if called at end of - * file. + * file. If the first argument is an integer, or optional second + * argument is given, the returning string would not be longer than the + * given value. * * File.new("testfile").gets #=> "This is line one\n" * $_ #=> "This is line one\n" @@ -1739,16 +1784,9 @@ rb_io_gets(VALUE io) static VALUE rb_io_gets_m(int argc, VALUE *argv, VALUE io) { - VALUE rs, str; + VALUE str; - if (argc == 0) { - rs = rb_rs; - } - else { - rb_scan_args(argc, argv, "1", &rs); - if (!NIL_P(rs)) StringValue(rs); - } - str = rb_io_getline(rs, io); + str = rb_io_getline(argc, argv, io); rb_lastline_set(str); return str; @@ -1834,7 +1872,9 @@ argf_lineno(void) /* * call-seq: - * ios.readline(sep_string=$/) => string + * ios.readline(sep=$/) => string + * ios.readline(limit) => string + * ios.readline(sep, limit) => string * * Reads a line as with IO#gets, but raises an * EOFError on end of file. @@ -1853,14 +1893,17 @@ rb_io_readline(int argc, VALUE *argv, VALUE io) /* * call-seq: - * ios.readlines(sep_string=$/) => array + * ios.readlines(sep=$/) => array + * ios.readlines(limit) => array + * ios.readlines(sep, limit) => array * * Reads all of the lines in ios, and returns them in - * anArray. Lines are separated by the optional - * sep_string. If sep_string is nil, the - * rest of the stream is returned as a single record. - * The stream must be opened for reading or an - * IOError will be raised. + * anArray. Lines are separated by the optional sep. If + * sep is nil, the rest of the stream is returned + * as a single record. If the first argument is an integer, or + * optional second argument is given, the returning string would not be + * longer than the given value. The stream must be opened for reading + * or an IOError will be raised. * * f = File.new("testfile") * f.readlines[0] #=> "This is line one\n" @@ -1870,17 +1913,9 @@ static VALUE rb_io_readlines(int argc, VALUE *argv, VALUE io) { VALUE line, ary; - VALUE rs; - if (argc == 0) { - rs = rb_rs; - } - else { - rb_scan_args(argc, argv, "1", &rs); - if (!NIL_P(rs)) StringValue(rs); - } ary = rb_ary_new(); - while (!NIL_P(line = rb_io_getline(rs, io))) { + while (!NIL_P(line = rb_io_getline(argc, argv, io))) { rb_ary_push(ary, line); } return ary; @@ -1888,11 +1923,15 @@ rb_io_readlines(int argc, VALUE *argv, VALUE io) /* * call-seq: - * ios.each(sep_string=$/) {|line| block } => ios - * ios.each_line(sep_string=$/) {|line| block } => ios + * ios.each(sep=$/) {|line| block } => ios + * ios.each(limit) {|line| block } => ios + * ios.each(sep,limit) {|line| block } => ios + * ios.each_line(sep=$/) {|line| block } => ios + * ios.each_line(limit) {|line| block } => ios + * ios.each_line(sep,limit) {|line| block } => ios * * Executes the block for every line in ios, where lines are - * separated by sep_string. ios must be opened for + * separated by sep. ios must be opened for * reading or an IOError will be raised. * * f = File.new("testfile") @@ -1910,17 +1949,9 @@ static VALUE rb_io_each_line(int argc, VALUE *argv, VALUE io) { VALUE str; - VALUE rs; RETURN_ENUMERATOR(io, argc, argv); - if (argc == 0) { - rs = rb_rs; - } - else { - rb_scan_args(argc, argv, "1", &rs); - if (!NIL_P(rs)) StringValue(rs); - } - while (!NIL_P(str = rb_io_getline(rs, io))) { + while (!NIL_P(str = rb_io_getline(argc, argv, io))) { rb_yield(str); } return io; @@ -1963,7 +1994,9 @@ rb_io_each_byte(VALUE io) /* * call-seq: - * str.lines(separator=$/) => anEnumerator + * str.lines(sep=$/) => anEnumerator + * str.lines(limit) => anEnumerator + * str.lines(sep, limit) => anEnumerator * * Returns an enumerator that gives each line in the string. * @@ -4371,16 +4404,7 @@ argf_getline(int argc, VALUE *argv) line = rb_io_gets(current_file); } else { - VALUE rs; - - if (argc == 0) { - rs = rb_rs; - } - else { - rb_scan_args(argc, argv, "1", &rs); - if (!NIL_P(rs)) StringValue(rs); - } - line = rb_io_getline(rs, current_file); + line = rb_io_getline(argc, argv, current_file); } if (NIL_P(line) && next_p != -1) { argf_close(current_file); @@ -4396,18 +4420,22 @@ argf_getline(int argc, VALUE *argv) /* * call-seq: - * gets(separator=$/) => string or nil + * gets(sep=$/) => string or nil + * gets(limit) => string or nil + * gets(sep,limit) => string or nil * * Returns (and assigns to $_) the next line from the list - * of files in +ARGV+ (or $*), or from standard - * input if no files are present on the command line. Returns - * +nil+ at end of file. The optional argument specifies the - * record separator. The separator is included with the contents of - * each record. A separator of +nil+ reads the entire - * contents, and a zero-length separator reads the input one paragraph - * at a time, where paragraphs are divided by two consecutive newlines. - * If multiple filenames are present in +ARGV+, - * +gets(nil)+ will read the contents one file at a time. + * of files in +ARGV+ (or $*), or from standard input if + * no files are present on the command line. Returns +nil+ at end of + * file. The optional argument specifies the record separator. The + * separator is included with the contents of each record. A separator + * of +nil+ reads the entire contents, and a zero-length separator + * reads the input one paragraph at a time, where paragraphs are + * divided by two consecutive newlines. If the first argument is an + * integer, or optional second argument is given, the returning string + * would not be longer than the given value. If multiple filenames are + * present in +ARGV+, +gets(nil)+ will read the contents one file at a + * time. * * ARGV << "testfile" * print while gets @@ -4467,7 +4495,9 @@ rb_gets(void) /* * call-seq: - * readline(separator=$/) => string + * readline(sep=$/) => string + * readline(limit) => string + * readline(sep, limit) => string * * Equivalent to Kernel::gets, except * +readline+ raises +EOFError+ at end of file. @@ -4503,10 +4533,12 @@ rb_f_getc(void) /* * call-seq: - * readlines(separator=$/) => array + * readlines(sep=$/) => array + * readlines(limit) => array + * readlines(sep,limit) => array * * Returns an array containing the lines returned by calling - * Kernel.gets(separator) until the end of file. + * Kernel.gets(sep) until the end of file. */ static VALUE @@ -5079,7 +5111,7 @@ rb_io_s_pipe(VALUE klass) struct foreach_arg { int argc; - VALUE sep; + VALUE *argv; VALUE io; }; @@ -5088,7 +5120,7 @@ io_s_foreach(struct foreach_arg *arg) { VALUE str; - while (!NIL_P(str = rb_io_getline(arg->sep, arg->io))) { + while (!NIL_P(str = rb_io_gets_m(arg->argc, arg->argv, arg->io))) { rb_yield(str); } return Qnil; @@ -5096,10 +5128,12 @@ io_s_foreach(struct foreach_arg *arg) /* * call-seq: - * IO.foreach(name, sep_string=$/) {|line| block } => nil + * IO.foreach(name, sep=$/) {|line| block } => nil + * IO.foreach(name, limit) {|line| block } => nil + * IO.foreach(name, sep, limit) {|line| block } => nil * * Executes the block for every line in the named I/O port, where lines - * are separated by sep_string. + * are separated by sep. * * IO.foreach("testfile") {|x| print "GOT ", x } * @@ -5118,33 +5152,30 @@ rb_io_s_foreach(int argc, VALUE *argv, VALUE self) struct foreach_arg arg; RETURN_ENUMERATOR(self, argc, argv); - rb_scan_args(argc, argv, "11", &fname, &arg.sep); + rb_scan_args(argc, argv, "12", &fname, NULL, NULL); FilePathValue(fname); - if (argc == 1) { - arg.sep = rb_default_rs; - } - else if (!NIL_P(arg.sep)) { - StringValue(arg.sep); - } arg.io = rb_io_open(RSTRING_PTR(fname), "r"); if (NIL_P(arg.io)) return Qnil; - + arg.argc = argc - 1; + arg.argv = argv + 1; return rb_ensure(io_s_foreach, (VALUE)&arg, rb_io_close, arg.io); } static VALUE io_s_readlines(struct foreach_arg *arg) { - return rb_io_readlines(arg->argc, &arg->sep, arg->io); + return rb_io_readlines(arg->argc, arg->argv, arg->io); } /* * call-seq: - * IO.readlines(name, sep_string=$/) => array + * IO.readlines(name, sep=$/) => array + * IO.readlines(name, limit) => array + * IO.readlines(name, sep, limit) => array * * Reads the entire file specified by name as individual * lines, and returns those lines in an array. Lines are separated by - * sep_string. + * sep. * * a = IO.readlines("testfile") * a[0] #=> "This is line one\n" @@ -5157,18 +5188,19 @@ rb_io_s_readlines(int argc, VALUE *argv, VALUE io) VALUE fname; struct foreach_arg arg; - rb_scan_args(argc, argv, "11", &fname, &arg.sep); + rb_scan_args(argc, argv, "12", &fname, NULL, NULL); FilePathValue(fname); - arg.argc = argc - 1; arg.io = rb_io_open(RSTRING_PTR(fname), "r"); if (NIL_P(arg.io)) return Qnil; + arg.argc = argc - 1; + arg.argv = argv + 1; return rb_ensure(io_s_readlines, (VALUE)&arg, rb_io_close, arg.io); } static VALUE io_s_read(struct foreach_arg *arg) { - return io_read(arg->argc, &arg->sep, arg->io); + return io_read(arg->argc, arg->argv, arg->io); } /* @@ -5190,9 +5222,10 @@ rb_io_s_read(int argc, VALUE *argv, VALUE io) VALUE fname, offset; struct foreach_arg arg; - rb_scan_args(argc, argv, "12", &fname, &arg.sep, &offset); + rb_scan_args(argc, argv, "12", &fname, NULL, &offset); FilePathValue(fname); - arg.argc = argc ? 1 : 0; + arg.argc = argc > 1 ? 1 : 0; + arg.argv = argv + 1; arg.io = rb_io_open(RSTRING_PTR(fname), "r"); if (NIL_P(arg.io)) return Qnil; if (!NIL_P(offset)) { @@ -5278,7 +5311,7 @@ argf_read(int argc, VALUE *argv) VALUE tmp, str, length; long len = 0; - rb_scan_args(argc, argv, "02", &length, &str); + rb_scan_args(argc, argv, "11", &length, &str); if (!NIL_P(length)) { len = NUM2LONG(argv[0]); } diff --git a/version.h b/version.h index 8780e54287..7d7c82b366 100644 --- a/version.h +++ b/version.h @@ -1,7 +1,7 @@ #define RUBY_VERSION "1.9.0" -#define RUBY_RELEASE_DATE "2006-12-28" +#define RUBY_RELEASE_DATE "2006-12-30" #define RUBY_VERSION_CODE 190 -#define RUBY_RELEASE_CODE 20061228 +#define RUBY_RELEASE_CODE 20061230 #define RUBY_PATCHLEVEL 0 #define RUBY_VERSION_MAJOR 1 @@ -9,7 +9,7 @@ #define RUBY_VERSION_TEENY 0 #define RUBY_RELEASE_YEAR 2006 #define RUBY_RELEASE_MONTH 12 -#define RUBY_RELEASE_DAY 28 +#define RUBY_RELEASE_DAY 30 RUBY_EXTERN const char ruby_version[]; RUBY_EXTERN const char ruby_release_date[];