summaryrefslogtreecommitdiff
path: root/ext/stringio/stringio.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/stringio/stringio.c')
-rw-r--r--ext/stringio/stringio.c1278
1 files changed, 844 insertions, 434 deletions
diff --git a/ext/stringio/stringio.c b/ext/stringio/stringio.c
index fcc596d5f0..f537054b5d 100644
--- a/ext/stringio/stringio.c
+++ b/ext/stringio/stringio.c
@@ -2,8 +2,7 @@
stringio.c -
- $Author: nobu $
- $Date: 2005/08/13 09:36:12 $
+ $Author$
$RoughId: stringio.c,v 1.13 2002/03/14 03:24:18 nobu Exp $
created at: Tue Feb 19 04:10:38 JST 2002
@@ -13,37 +12,37 @@
**********************************************************************/
#include "ruby.h"
-#include "rubyio.h"
+#include "ruby/io.h"
+#include "ruby/encoding.h"
#if defined(HAVE_FCNTL_H) || defined(_WIN32)
#include <fcntl.h>
#elif defined(HAVE_SYS_FCNTL_H)
#include <sys/fcntl.h>
#endif
-#define STRIO_EOF FMODE_SYNC
+#ifndef RB_INTEGER_TYPE_P
+# define RB_INTEGER_TYPE_P(c) (FIXNUM_P(c) || RB_TYPE_P(c, T_BIGNUM))
+#endif
struct StringIO {
VALUE string;
+ rb_encoding *enc;
long pos;
long lineno;
int flags;
int count;
};
-static struct StringIO* strio_alloc _((void));
-static void strio_mark _((struct StringIO *));
-static void strio_free _((struct StringIO *));
-static struct StringIO* check_strio _((VALUE));
-static struct StringIO* get_strio _((VALUE));
-static struct StringIO* readable _((struct StringIO *));
-static struct StringIO* writable _((struct StringIO *));
-static void check_modifiable _((struct StringIO *));
+static VALUE strio_init(int, VALUE *, struct StringIO *, VALUE);
+static VALUE strio_unget_bytes(struct StringIO *, const char *, long);
+static long strio_write(VALUE self, VALUE str);
-#define IS_STRIO(obj) (RDATA(obj)->dmark == (RUBY_DATA_FUNC)strio_mark)
-#define error_inval(msg) (errno = EINVAL, rb_sys_fail(msg))
+#define IS_STRIO(obj) (rb_typeddata_is_kind_of((obj), &strio_data_type))
+#define error_inval(msg) (rb_syserr_fail(EINVAL, msg))
+#define get_enc(ptr) ((ptr)->enc ? (ptr)->enc : rb_enc_get((ptr)->string))
static struct StringIO *
-strio_alloc()
+strio_alloc(void)
{
struct StringIO *ptr = ALLOC(struct StringIO);
ptr->string = Qnil;
@@ -55,40 +54,44 @@ strio_alloc()
}
static void
-strio_mark(ptr)
- struct StringIO *ptr;
+strio_mark(void *p)
{
- if (ptr) {
- rb_gc_mark(ptr->string);
- }
+ struct StringIO *ptr = p;
+
+ rb_gc_mark(ptr->string);
}
static void
-strio_free(ptr)
- struct StringIO *ptr;
+strio_free(void *p)
{
+ struct StringIO *ptr = p;
if (--ptr->count <= 0) {
xfree(ptr);
}
}
-static struct StringIO*
-check_strio(self)
- VALUE self;
+static size_t
+strio_memsize(const void *p)
{
- Check_Type(self, T_DATA);
- if (!IS_STRIO(self)) {
- rb_raise(rb_eTypeError, "wrong argument type %s (expected StringIO)",
- rb_class2name(CLASS_OF(self)));
- }
- return DATA_PTR(self);
+ return sizeof(struct StringIO);
}
+static const rb_data_type_t strio_data_type = {
+ "strio",
+ {
+ strio_mark,
+ strio_free,
+ strio_memsize,
+ },
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+};
+
+#define check_strio(self) ((struct StringIO*)rb_check_typeddata((self), &strio_data_type))
+
static struct StringIO*
-get_strio(self)
- VALUE self;
+get_strio(VALUE self)
{
- struct StringIO *ptr = check_strio(self);
+ struct StringIO *ptr = check_strio(rb_io_taint_check(self));
if (!ptr) {
rb_raise(rb_eIOError, "uninitialized stream");
@@ -96,114 +99,73 @@ get_strio(self)
return ptr;
}
+static VALUE
+enc_subseq(VALUE str, long pos, long len, rb_encoding *enc)
+{
+ str = rb_str_subseq(str, pos, len);
+ rb_enc_associate(str, enc);
+ return str;
+}
+
+static VALUE
+strio_substr(struct StringIO *ptr, long pos, long len, rb_encoding *enc)
+{
+ VALUE str = ptr->string;
+ long rlen = RSTRING_LEN(str) - pos;
+
+ if (len > rlen) len = rlen;
+ if (len < 0) len = 0;
+ if (len == 0) return rb_enc_str_new(0, 0, enc);
+ return enc_subseq(str, pos, len, enc);
+}
+
#define StringIO(obj) get_strio(obj)
-#define CLOSED(ptr) (!((ptr)->flags & FMODE_READWRITE))
-#define READABLE(ptr) ((ptr)->flags & FMODE_READABLE)
-#define WRITABLE(ptr) ((ptr)->flags & FMODE_WRITABLE)
+#define STRIO_READABLE FL_USER4
+#define STRIO_WRITABLE FL_USER5
+#define STRIO_READWRITE (STRIO_READABLE|STRIO_WRITABLE)
+typedef char strio_flags_check[(STRIO_READABLE/FMODE_READABLE == STRIO_WRITABLE/FMODE_WRITABLE) * 2 - 1];
+#define STRIO_MODE_SET_P(strio, mode) \
+ ((RBASIC(strio)->flags & STRIO_##mode) && \
+ ((struct StringIO*)DATA_PTR(strio))->flags & FMODE_##mode)
+#define CLOSED(strio) (!STRIO_MODE_SET_P(strio, READWRITE))
+#define READABLE(strio) STRIO_MODE_SET_P(strio, READABLE)
+#define WRITABLE(strio) STRIO_MODE_SET_P(strio, WRITABLE)
+
+static VALUE sym_exception;
static struct StringIO*
-readable(ptr)
- struct StringIO *ptr;
+readable(VALUE strio)
{
- if (!READABLE(ptr)) {
+ struct StringIO *ptr = StringIO(strio);
+ if (!READABLE(strio)) {
rb_raise(rb_eIOError, "not opened for reading");
}
return ptr;
}
static struct StringIO*
-writable(ptr)
- struct StringIO *ptr;
+writable(VALUE strio)
{
- if (!WRITABLE(ptr)) {
+ struct StringIO *ptr = StringIO(strio);
+ if (!WRITABLE(strio)) {
rb_raise(rb_eIOError, "not opened for writing");
}
- if (!OBJ_TAINTED(ptr->string)) {
- rb_secure(4);
- }
return ptr;
}
static void
-check_modifiable(ptr)
- struct StringIO *ptr;
+check_modifiable(struct StringIO *ptr)
{
if (OBJ_FROZEN(ptr->string)) {
rb_raise(rb_eIOError, "not modifiable string");
}
}
-static VALUE strio_s_allocate _((VALUE));
-static VALUE strio_s_open _((int, VALUE *, VALUE));
-static void strio_init _((int, VALUE *, struct StringIO *));
-static VALUE strio_initialize _((int, VALUE *, VALUE));
-static VALUE strio_finalize _((VALUE));
-static VALUE strio_self _((VALUE));
-static VALUE strio_false _((VALUE));
-static VALUE strio_nil _((VALUE));
-static VALUE strio_0 _((VALUE));
-static VALUE strio_first _((VALUE, VALUE));
-static VALUE strio_unimpl _((int, VALUE *, VALUE));
-static VALUE strio_get_string _((VALUE));
-static VALUE strio_set_string _((VALUE, VALUE));
-static VALUE strio_close _((VALUE));
-static VALUE strio_close_read _((VALUE));
-static VALUE strio_close_write _((VALUE));
-static VALUE strio_closed _((VALUE));
-static VALUE strio_closed_read _((VALUE));
-static VALUE strio_closed_write _((VALUE));
-static VALUE strio_eof _((VALUE));
-static VALUE strio_get_lineno _((VALUE));
-static VALUE strio_set_lineno _((VALUE, VALUE));
-static VALUE strio_get_pos _((VALUE));
-static VALUE strio_set_pos _((VALUE, VALUE));
-static VALUE strio_rewind _((VALUE));
-static VALUE strio_seek _((int, VALUE *, VALUE));
-static VALUE strio_get_sync _((VALUE));
-static VALUE strio_each_byte _((VALUE));
-static VALUE strio_getc _((VALUE));
-static VALUE strio_ungetc _((VALUE, VALUE));
-static VALUE strio_readchar _((VALUE));
-static VALUE strio_getline _((int, VALUE *, struct StringIO *));
-static VALUE strio_gets _((int, VALUE *, VALUE));
-static VALUE strio_readline _((int, VALUE *, VALUE));
-static VALUE strio_each _((int, VALUE *, VALUE));
-static VALUE strio_readlines _((int, VALUE *, VALUE));
-static VALUE strio_write _((VALUE, VALUE));
-static VALUE strio_putc _((VALUE, VALUE));
-static VALUE strio_read _((int, VALUE *, VALUE));
-static VALUE strio_size _((VALUE));
-static VALUE strio_truncate _((VALUE, VALUE));
-void Init_stringio _((void));
-
-/* Boyer-Moore search: copied from regex.c */
-static void bm_init_skip _((long *, const char *, long));
-static long bm_search _((const char *, long, const char *, long, const long *));
-
-static VALUE
-strio_s_allocate(klass)
- VALUE klass;
-{
- return Data_Wrap_Struct(klass, strio_mark, strio_free, 0);
-}
-
-/*
- * call-seq: StringIO.open(string=""[, mode]) {|strio| ...}
- *
- * Equivalent to StringIO.new except that when it is called with a block, it
- * yields with the new instance and closes it, and returns the result which
- * returned from the block.
- */
static VALUE
-strio_s_open(argc, argv, klass)
- int argc;
- VALUE *argv;
- VALUE klass;
+strio_s_allocate(VALUE klass)
{
- VALUE obj = rb_class_new_instance(argc, argv, klass);
- if (!rb_block_given_p()) return obj;
- return rb_ensure(rb_yield, obj, strio_finalize, obj);
+ return TypedData_Wrap_Struct(klass, &strio_data_type, 0);
}
/*
@@ -212,10 +174,7 @@ strio_s_open(argc, argv, klass)
* Creates new StringIO instance from with _string_ and _mode_.
*/
static VALUE
-strio_initialize(argc, argv, self)
- int argc;
- VALUE *argv;
- VALUE self;
+strio_initialize(int argc, VALUE *argv, VALUE self)
{
struct StringIO *ptr = check_strio(self);
@@ -223,35 +182,30 @@ strio_initialize(argc, argv, self)
DATA_PTR(self) = ptr = strio_alloc();
}
rb_call_super(0, 0);
- strio_init(argc, argv, ptr);
- return self;
+ return strio_init(argc, argv, ptr, self);
}
-static void
-strio_init(argc, argv, ptr)
- int argc;
- VALUE *argv;
- struct StringIO *ptr;
+static VALUE
+strio_init(int argc, VALUE *argv, struct StringIO *ptr, VALUE self)
{
VALUE string, mode;
- int trunc = Qfalse;
+ int trunc = 0;
switch (rb_scan_args(argc, argv, "02", &string, &mode)) {
case 2:
if (FIXNUM_P(mode)) {
int flags = FIX2INT(mode);
- ptr->flags = rb_io_modenum_flags(flags);
+ ptr->flags = rb_io_oflags_fmode(flags);
trunc = flags & O_TRUNC;
}
else {
const char *m = StringValueCStr(mode);
- ptr->flags = rb_io_mode_flags(m);
+ ptr->flags = rb_io_modestr_fmode(m);
trunc = *m == 'w';
}
StringValue(string);
if ((ptr->flags & FMODE_WRITABLE) && OBJ_FROZEN(string)) {
- errno = EACCES;
- rb_sys_fail(0);
+ rb_syserr_fail(EACCES, 0);
}
if (trunc) {
rb_str_resize(string, 0);
@@ -262,16 +216,20 @@ strio_init(argc, argv, ptr)
ptr->flags = OBJ_FROZEN(string) ? FMODE_READABLE : FMODE_READWRITE;
break;
case 0:
- string = rb_str_new("", 0);
+ string = rb_enc_str_new("", 0, rb_default_external_encoding());
ptr->flags = FMODE_READWRITE;
break;
}
ptr->string = string;
+ ptr->enc = 0;
+ ptr->pos = 0;
+ ptr->lineno = 0;
+ RBASIC(self)->flags |= (ptr->flags & FMODE_READWRITE) * (STRIO_READABLE / FMODE_READABLE);
+ return self;
}
static VALUE
-strio_finalize(self)
- VALUE self;
+strio_finalize(VALUE self)
{
struct StringIO *ptr = StringIO(self);
ptr->string = Qnil;
@@ -280,11 +238,38 @@ strio_finalize(self)
}
/*
+ * call-seq: StringIO.open(string=""[, mode]) {|strio| ...}
+ *
+ * Equivalent to StringIO.new except that when it is called with a block, it
+ * yields with the new instance and closes it, and returns the result which
+ * returned from the block.
+ */
+static VALUE
+strio_s_open(int argc, VALUE *argv, VALUE klass)
+{
+ VALUE obj = rb_class_new_instance(argc, argv, klass);
+ if (!rb_block_given_p()) return obj;
+ return rb_ensure(rb_yield, obj, strio_finalize, obj);
+}
+
+/* :nodoc: */
+static VALUE
+strio_s_new(int argc, VALUE *argv, VALUE klass)
+{
+ if (rb_block_given_p()) {
+ VALUE cname = rb_obj_as_string(klass);
+
+ rb_warn("%"PRIsVALUE"::new() does not take block; use %"PRIsVALUE"::open() instead",
+ cname, cname);
+ }
+ return rb_class_new_instance(argc, argv, klass);
+}
+
+/*
* Returns +false+. Just for compatibility to IO.
*/
static VALUE
-strio_false(self)
- VALUE self;
+strio_false(VALUE self)
{
StringIO(self);
return Qfalse;
@@ -294,8 +279,7 @@ strio_false(self)
* Returns +nil+. Just for compatibility to IO.
*/
static VALUE
-strio_nil(self)
- VALUE self;
+strio_nil(VALUE self)
{
StringIO(self);
return Qnil;
@@ -305,8 +289,7 @@ strio_nil(self)
* Returns *strio* itself. Just for compatibility to IO.
*/
static VALUE
-strio_self(self)
- VALUE self;
+strio_self(VALUE self)
{
StringIO(self);
return self;
@@ -316,8 +299,7 @@ strio_self(self)
* Returns 0. Just for compatibility to IO.
*/
static VALUE
-strio_0(self)
- VALUE self;
+strio_0(VALUE self)
{
StringIO(self);
return INT2FIX(0);
@@ -327,8 +309,7 @@ strio_0(self)
* Returns the argument unchanged. Just for compatibility to IO.
*/
static VALUE
-strio_first(self, arg)
- VALUE self, arg;
+strio_first(VALUE self, VALUE arg)
{
StringIO(self);
return arg;
@@ -338,14 +319,12 @@ strio_first(self, arg)
* Raises NotImplementedError.
*/
static VALUE
-strio_unimpl(argc, argv, self)
- int argc;
- VALUE *argv;
- VALUE self;
+strio_unimpl(int argc, VALUE *argv, VALUE self)
{
StringIO(self);
rb_notimplement();
- return Qnil; /* not reached */
+
+ UNREACHABLE;
}
/*
@@ -354,8 +333,7 @@ strio_unimpl(argc, argv, self)
* Returns underlying String object, the subject of IO.
*/
static VALUE
-strio_get_string(self)
- VALUE self;
+strio_get_string(VALUE self)
{
return StringIO(self)->string;
}
@@ -367,12 +345,11 @@ strio_get_string(self)
* Changes underlying String object, the subject of IO.
*/
static VALUE
-strio_set_string(self, string)
- VALUE self, string;
+strio_set_string(VALUE self, VALUE string)
{
struct StringIO *ptr = StringIO(self);
- if (!OBJ_TAINTED(self)) rb_secure(4);
+ rb_io_taint_check(self);
ptr->flags &= ~FMODE_READWRITE;
StringValue(string);
ptr->flags = OBJ_FROZEN(string) ? FMODE_READABLE : FMODE_READWRITE;
@@ -385,18 +362,14 @@ strio_set_string(self, string)
* call-seq:
* strio.close -> nil
*
- * Closes strio. The *strio* is unavailable for any further data
+ * Closes strio. The *strio* is unavailable for any further data
* operations; an +IOError+ is raised if such an attempt is made.
*/
static VALUE
-strio_close(self)
- VALUE self;
+strio_close(VALUE self)
{
- struct StringIO *ptr = StringIO(self);
- if (CLOSED(ptr)) {
- rb_raise(rb_eIOError, "closed stream");
- }
- ptr->flags &= ~FMODE_READWRITE;
+ StringIO(self);
+ RBASIC(self)->flags &= ~STRIO_READWRITE;
return Qnil;
}
@@ -408,14 +381,13 @@ strio_close(self)
* *strio* is not readable.
*/
static VALUE
-strio_close_read(self)
- VALUE self;
+strio_close_read(VALUE self)
{
struct StringIO *ptr = StringIO(self);
- if (!READABLE(ptr)) {
+ if (!(ptr->flags & FMODE_READABLE)) {
rb_raise(rb_eIOError, "closing non-duplex IO for reading");
}
- ptr->flags &= ~FMODE_READABLE;
+ RBASIC(self)->flags &= ~STRIO_READABLE;
return Qnil;
}
@@ -427,14 +399,13 @@ strio_close_read(self)
* *strio* is not writeable.
*/
static VALUE
-strio_close_write(self)
- VALUE self;
+strio_close_write(VALUE self)
{
struct StringIO *ptr = StringIO(self);
- if (!WRITABLE(ptr)) {
+ if (!(ptr->flags & FMODE_WRITABLE)) {
rb_raise(rb_eIOError, "closing non-duplex IO for writing");
}
- ptr->flags &= ~FMODE_WRITABLE;
+ RBASIC(self)->flags &= ~STRIO_WRITABLE;
return Qnil;
}
@@ -445,11 +416,10 @@ strio_close_write(self)
* Returns +true+ if *strio* is completely closed, +false+ otherwise.
*/
static VALUE
-strio_closed(self)
- VALUE self;
+strio_closed(VALUE self)
{
- struct StringIO *ptr = StringIO(self);
- if (!CLOSED(ptr)) return Qfalse;
+ StringIO(self);
+ if (!CLOSED(self)) return Qfalse;
return Qtrue;
}
@@ -460,11 +430,10 @@ strio_closed(self)
* Returns +true+ if *strio* is not readable, +false+ otherwise.
*/
static VALUE
-strio_closed_read(self)
- VALUE self;
+strio_closed_read(VALUE self)
{
- struct StringIO *ptr = StringIO(self);
- if (READABLE(ptr)) return Qfalse;
+ StringIO(self);
+ if (READABLE(self)) return Qfalse;
return Qtrue;
}
@@ -475,11 +444,10 @@ strio_closed_read(self)
* Returns +true+ if *strio* is not writable, +false+ otherwise.
*/
static VALUE
-strio_closed_write(self)
- VALUE self;
+strio_closed_write(VALUE self)
{
- struct StringIO *ptr = StringIO(self);
- if (WRITABLE(ptr)) return Qfalse;
+ StringIO(self);
+ if (WRITABLE(self)) return Qfalse;
return Qtrue;
}
@@ -488,22 +456,20 @@ strio_closed_write(self)
* strio.eof -> true or false
* strio.eof? -> true or false
*
- * Returns true if *strio* is at end of file. The stringio must be
+ * Returns true if *strio* is at end of file. The stringio must be
* opened for reading or an +IOError+ will be raised.
*/
static VALUE
-strio_eof(self)
- VALUE self;
+strio_eof(VALUE self)
{
- struct StringIO *ptr = readable(StringIO(self));
- if (ptr->pos < RSTRING(ptr->string)->len) return Qfalse;
+ struct StringIO *ptr = readable(self);
+ if (ptr->pos < RSTRING_LEN(ptr->string)) return Qfalse;
return Qtrue;
}
/* :nodoc: */
static VALUE
-strio_copy(copy, orig)
- VALUE copy, orig;
+strio_copy(VALUE copy, VALUE orig)
{
struct StringIO *ptr;
@@ -515,6 +481,8 @@ strio_copy(copy, orig)
}
DATA_PTR(copy) = ptr;
OBJ_INFECT(copy, orig);
+ RBASIC(copy)->flags &= ~STRIO_READWRITE;
+ RBASIC(copy)->flags |= RBASIC(orig)->flags & STRIO_READWRITE;
++ptr->count;
return copy;
}
@@ -530,8 +498,7 @@ strio_copy(copy, orig)
* newline. See also the <code>$.</code> variable.
*/
static VALUE
-strio_get_lineno(self)
- VALUE self;
+strio_get_lineno(VALUE self)
{
return LONG2NUM(StringIO(self)->lineno);
}
@@ -544,23 +511,29 @@ strio_get_lineno(self)
* <code>$.</code> is updated only on the next read.
*/
static VALUE
-strio_set_lineno(self, lineno)
- VALUE self, lineno;
+strio_set_lineno(VALUE self, VALUE lineno)
{
StringIO(self)->lineno = NUM2LONG(lineno);
return lineno;
}
-/* call-seq: strio.binmode -> true */
-#define strio_binmode strio_self
+static VALUE
+strio_binmode(VALUE self)
+{
+ struct StringIO *ptr = StringIO(self);
+ rb_encoding *enc = rb_ascii8bit_encoding();
+
+ ptr->enc = enc;
+ if (WRITABLE(self)) {
+ rb_enc_associate(ptr->string, enc);
+ }
+ return self;
+}
-/* call-seq: strio.fcntl */
#define strio_fcntl strio_unimpl
-/* call-seq: strio.flush -> strio */
#define strio_flush strio_self
-/* call-seq: strio.fsync -> 0 */
#define strio_fsync strio_0
/*
@@ -568,21 +541,17 @@ strio_set_lineno(self, lineno)
* strio.reopen(other_StrIO) -> strio
* strio.reopen(string, mode) -> strio
*
- * Reinitializes *strio* with the given <i>other_StrIO</i> or _string_
+ * Reinitializes *strio* with the given <i>other_StrIO</i> or _string_
* and _mode_ (see StringIO#new).
*/
static VALUE
-strio_reopen(argc, argv, self)
- int argc;
- VALUE *argv;
- VALUE self;
+strio_reopen(int argc, VALUE *argv, VALUE self)
{
- if (!OBJ_TAINTED(self)) rb_secure(4);
- if (argc == 1 && TYPE(*argv) != T_STRING) {
+ rb_io_taint_check(self);
+ if (argc == 1 && !RB_TYPE_P(*argv, T_STRING)) {
return strio_copy(self, *argv);
}
- strio_init(argc, argv, StringIO(self));
- return self;
+ return strio_init(argc, argv, StringIO(self), self);
}
/*
@@ -593,8 +562,7 @@ strio_reopen(argc, argv, self)
* Returns the current offset (in bytes) of *strio*.
*/
static VALUE
-strio_get_pos(self)
- VALUE self;
+strio_get_pos(VALUE self)
{
return LONG2NUM(StringIO(self)->pos);
}
@@ -606,9 +574,7 @@ strio_get_pos(self)
* Seeks to the given position (in bytes) in *strio*.
*/
static VALUE
-strio_set_pos(self, pos)
- VALUE self;
- VALUE pos;
+strio_set_pos(VALUE self, VALUE pos)
{
struct StringIO *ptr = StringIO(self);
long p = NUM2LONG(pos);
@@ -616,7 +582,6 @@ strio_set_pos(self, pos)
error_inval(0);
}
ptr->pos = p;
- ptr->flags &= ~STRIO_EOF;
return pos;
}
@@ -628,13 +593,11 @@ strio_set_pos(self, pos)
* +lineno+ to zero.
*/
static VALUE
-strio_rewind(self)
- VALUE self;
+strio_rewind(VALUE self)
{
struct StringIO *ptr = StringIO(self);
ptr->pos = 0;
ptr->lineno = 0;
- ptr->flags &= ~STRIO_EOF;
return INT2FIX(0);
}
@@ -646,34 +609,34 @@ strio_rewind(self)
* the value of _whence_ (see IO#seek).
*/
static VALUE
-strio_seek(argc, argv, self)
- int argc;
- VALUE *argv;
- VALUE self;
+strio_seek(int argc, VALUE *argv, VALUE self)
{
VALUE whence;
struct StringIO *ptr = StringIO(self);
- long offset;
+ long amount, offset;
rb_scan_args(argc, argv, "11", NULL, &whence);
- offset = NUM2LONG(argv[0]);
+ amount = NUM2LONG(argv[0]);
+ if (CLOSED(self)) {
+ rb_raise(rb_eIOError, "closed stream");
+ }
switch (NIL_P(whence) ? 0 : NUM2LONG(whence)) {
case 0:
+ offset = 0;
break;
case 1:
- offset += ptr->pos;
+ offset = ptr->pos;
break;
case 2:
- offset += RSTRING(ptr->string)->len;
+ offset = RSTRING_LEN(ptr->string);
break;
default:
- rb_raise(rb_eArgError, "invalid whence %ld", NUM2LONG(whence));
+ error_inval("invalid whence");
}
- if (offset < 0) {
+ if (amount > LONG_MAX - offset || amount + offset < 0) {
error_inval(0);
}
- ptr->pos = offset;
- ptr->flags &= ~STRIO_EOF;
+ ptr->pos = amount + offset;
return INT2FIX(0);
}
@@ -684,14 +647,12 @@ strio_seek(argc, argv, self)
* Returns +true+ always.
*/
static VALUE
-strio_get_sync(self)
- VALUE self;
+strio_get_sync(VALUE self)
{
StringIO(self);
return Qtrue;
}
-/* call-seq: strio.sync = boolean -> boolean */
#define strio_set_sync strio_first
#define strio_tell strio_get_pos
@@ -699,54 +660,93 @@ strio_get_sync(self)
/*
* call-seq:
* strio.each_byte {|byte| block } -> strio
+ * strio.each_byte -> anEnumerator
*
* See IO#each_byte.
*/
static VALUE
-strio_each_byte(self)
- VALUE self;
+strio_each_byte(VALUE self)
{
- struct StringIO *ptr = readable(StringIO(self));
- while (ptr->pos < RSTRING(ptr->string)->len) {
- char c = RSTRING(ptr->string)->ptr[ptr->pos++];
+ struct StringIO *ptr = readable(self);
+
+ RETURN_ENUMERATOR(self, 0, 0);
+
+ while (ptr->pos < RSTRING_LEN(ptr->string)) {
+ char c = RSTRING_PTR(ptr->string)[ptr->pos++];
rb_yield(CHR2FIX(c));
}
- return Qnil;
+ return self;
+}
+
+/*
+ * This is a deprecated alias for #each_byte.
+ */
+static VALUE
+strio_bytes(VALUE self)
+{
+ rb_warn("StringIO#bytes is deprecated; use #each_byte instead");
+ if (!rb_block_given_p())
+ return rb_enumeratorize(self, ID2SYM(rb_intern("each_byte")), 0, 0);
+ return strio_each_byte(self);
}
/*
* call-seq:
- * strio.getc -> fixnum or nil
+ * strio.getc -> string or nil
*
* See IO#getc.
*/
static VALUE
-strio_getc(self)
- VALUE self;
+strio_getc(VALUE self)
+{
+ struct StringIO *ptr = readable(self);
+ rb_encoding *enc = get_enc(ptr);
+ VALUE str = ptr->string;
+ long pos = ptr->pos;
+ int len;
+ char *p;
+
+ if (pos >= RSTRING_LEN(str)) {
+ return Qnil;
+ }
+ p = RSTRING_PTR(str)+pos;
+ len = rb_enc_mbclen(p, RSTRING_END(str), enc);
+ ptr->pos += len;
+ return enc_subseq(str, pos, len, enc);
+}
+
+/*
+ * call-seq:
+ * strio.getbyte -> fixnum or nil
+ *
+ * See IO#getbyte.
+ */
+static VALUE
+strio_getbyte(VALUE self)
{
- struct StringIO *ptr = readable(StringIO(self));
+ struct StringIO *ptr = readable(self);
int c;
- if (ptr->pos >= RSTRING(ptr->string)->len) {
- ptr->flags |= STRIO_EOF;
+ if (ptr->pos >= RSTRING_LEN(ptr->string)) {
return Qnil;
}
- c = RSTRING(ptr->string)->ptr[ptr->pos++];
+ c = RSTRING_PTR(ptr->string)[ptr->pos++];
return CHR2FIX(c);
}
static void
-strio_extend(ptr, pos, len)
- struct StringIO *ptr;
- long pos, len;
+strio_extend(struct StringIO *ptr, long pos, long len)
{
long olen;
+ if (len > LONG_MAX - pos)
+ rb_raise(rb_eArgError, "string size too big");
+
check_modifiable(ptr);
- olen = RSTRING(ptr->string)->len;
+ olen = RSTRING_LEN(ptr->string);
if (pos + len > olen) {
rb_str_resize(ptr->string, pos + len);
if (pos > olen)
- MEMZERO(RSTRING(ptr->string)->ptr + olen, char, pos - olen);
+ MEMZERO(RSTRING_PTR(ptr->string) + olen, char, pos - olen);
}
else {
rb_str_modify(ptr->string);
@@ -755,56 +755,212 @@ strio_extend(ptr, pos, len)
/*
* call-seq:
- * strio.ungetc(integer) -> nil
+ * strio.ungetc(string) -> nil
*
* Pushes back one character (passed as a parameter) onto *strio*
- * such that a subsequent buffered read will return it. Pushing back
- * behind the beginning of the buffer string is not possible. Nothing
- * will be done if such an attempt is made.
- * In other case, there is no limitation for multiple pushbacks.
+ * such that a subsequent buffered read will return it. There is no
+ * limitation for multiple pushbacks including pushing back behind the
+ * beginning of the buffer string.
*/
static VALUE
-strio_ungetc(self, ch)
- VALUE self, ch;
-{
- struct StringIO *ptr = readable(StringIO(self));
- int cc = NUM2INT(ch);
- long len, pos = ptr->pos;
-
- if (cc != EOF && pos > 0) {
- if ((len = RSTRING(ptr->string)->len) < pos-- ||
- (unsigned char)RSTRING(ptr->string)->ptr[pos] !=
- (unsigned char)cc) {
- strio_extend(ptr, pos, 1);
- RSTRING(ptr->string)->ptr[pos] = cc;
- OBJ_INFECT(ptr->string, self);
+strio_ungetc(VALUE self, VALUE c)
+{
+ struct StringIO *ptr = readable(self);
+ rb_encoding *enc, *enc2;
+
+ check_modifiable(ptr);
+ if (NIL_P(c)) return Qnil;
+ if (RB_INTEGER_TYPE_P(c)) {
+ int len, cc = NUM2INT(c);
+ char buf[16];
+
+ enc = rb_enc_get(ptr->string);
+ len = rb_enc_codelen(cc, enc);
+ if (len <= 0) rb_enc_uint_chr(cc, enc);
+ rb_enc_mbcput(cc, buf, enc);
+ return strio_unget_bytes(ptr, buf, len);
+ }
+ else {
+ SafeStringValue(c);
+ enc = rb_enc_get(ptr->string);
+ enc2 = rb_enc_get(c);
+ if (enc != enc2 && enc != rb_ascii8bit_encoding()) {
+ c = rb_str_conv_enc(c, enc2, enc);
}
- --ptr->pos;
- ptr->flags &= ~STRIO_EOF;
+ strio_unget_bytes(ptr, RSTRING_PTR(c), RSTRING_LEN(c));
+ RB_GC_GUARD(c);
+ return Qnil;
}
+}
+
+/*
+ * call-seq:
+ * strio.ungetbyte(fixnum) -> nil
+ *
+ * See IO#ungetbyte
+ */
+static VALUE
+strio_ungetbyte(VALUE self, VALUE c)
+{
+ struct StringIO *ptr = readable(self);
+ char buf[1], *cp = buf;
+ long cl = 1;
+
+ check_modifiable(ptr);
+ if (NIL_P(c)) return Qnil;
+ if (FIXNUM_P(c)) {
+ buf[0] = (char)FIX2INT(c);
+ return strio_unget_bytes(ptr, buf, 1);
+ }
+ else {
+ SafeStringValue(c);
+ cp = RSTRING_PTR(c);
+ cl = RSTRING_LEN(c);
+ if (cl == 0) return Qnil;
+ strio_unget_bytes(ptr, cp, cl);
+ RB_GC_GUARD(c);
+ return Qnil;
+ }
+}
+
+static VALUE
+strio_unget_bytes(struct StringIO *ptr, const char *cp, long cl)
+{
+ long pos = ptr->pos, len, rest;
+ VALUE str = ptr->string;
+ char *s;
+
+ len = RSTRING_LEN(str);
+ rest = pos - len;
+ if (cl > pos) {
+ long ex = (rest < 0 ? cl-pos : cl+rest);
+ rb_str_modify_expand(str, ex);
+ rb_str_set_len(str, len + ex);
+ s = RSTRING_PTR(str);
+ if (rest < 0) memmove(s + cl, s + pos, -rest);
+ pos = 0;
+ }
+ else {
+ if (rest > 0) {
+ rb_str_modify_expand(str, rest);
+ rb_str_set_len(str, len + rest);
+ }
+ s = RSTRING_PTR(str);
+ if (rest > cl) memset(s + len, 0, rest - cl);
+ pos -= cl;
+ }
+ memcpy(s + pos, cp, cl);
+ ptr->pos = pos;
return Qnil;
}
/*
* call-seq:
- * strio.readchar -> fixnum
+ * strio.readchar -> string
*
* See IO#readchar.
*/
static VALUE
-strio_readchar(self)
- VALUE self;
+strio_readchar(VALUE self)
+{
+ VALUE c = rb_funcall2(self, rb_intern("getc"), 0, 0);
+ if (NIL_P(c)) rb_eof_error();
+ return c;
+}
+
+/*
+ * call-seq:
+ * strio.readbyte -> fixnum
+ *
+ * See IO#readbyte.
+ */
+static VALUE
+strio_readbyte(VALUE self)
{
- VALUE c = strio_getc(self);
+ VALUE c = rb_funcall2(self, rb_intern("getbyte"), 0, 0);
if (NIL_P(c)) rb_eof_error();
return c;
}
+/*
+ * call-seq:
+ * strio.each_char {|char| block } -> strio
+ * strio.each_char -> anEnumerator
+ *
+ * See IO#each_char.
+ */
+static VALUE
+strio_each_char(VALUE self)
+{
+ VALUE c;
+
+ RETURN_ENUMERATOR(self, 0, 0);
+
+ while (!NIL_P(c = strio_getc(self))) {
+ rb_yield(c);
+ }
+ return self;
+}
+
+/*
+ * This is a deprecated alias for <code>each_char</code>.
+ */
+static VALUE
+strio_chars(VALUE self)
+{
+ rb_warn("StringIO#chars is deprecated; use #each_char instead");
+ if (!rb_block_given_p())
+ return rb_enumeratorize(self, ID2SYM(rb_intern("each_char")), 0, 0);
+ return strio_each_char(self);
+}
+
+/*
+ * call-seq:
+ * strio.each_codepoint {|c| block } -> strio
+ * strio.each_codepoint -> anEnumerator
+ *
+ * See IO#each_codepoint.
+ */
+static VALUE
+strio_each_codepoint(VALUE self)
+{
+ struct StringIO *ptr;
+ rb_encoding *enc;
+ unsigned int c;
+ int n;
+
+ RETURN_ENUMERATOR(self, 0, 0);
+
+ ptr = readable(self);
+ enc = get_enc(ptr);
+ for (;;) {
+ if (ptr->pos >= RSTRING_LEN(ptr->string)) {
+ return self;
+ }
+
+ c = rb_enc_codepoint_len(RSTRING_PTR(ptr->string)+ptr->pos,
+ RSTRING_END(ptr->string), &n, enc);
+ rb_yield(UINT2NUM(c));
+ ptr->pos += n;
+ }
+ return self;
+}
+
+/*
+ * This is a deprecated alias for <code>each_codepoint</code>.
+ */
+static VALUE
+strio_codepoints(VALUE self)
+{
+ rb_warn("StringIO#codepoints is deprecated; use #each_codepoint instead");
+ if (!rb_block_given_p())
+ return rb_enumeratorize(self, ID2SYM(rb_intern("each_codepoint")), 0, 0);
+ return strio_each_codepoint(self);
+}
+
+/* Boyer-Moore search: copied from regex.c */
static void
-bm_init_skip(skip, pat, m)
- long *skip;
- const char *pat;
- long m;
+bm_init_skip(long *skip, const char *pat, long m)
{
int c;
@@ -817,12 +973,7 @@ bm_init_skip(skip, pat, m)
}
static long
-bm_search(little, llen, big, blen, skip)
- const char *little;
- long llen;
- const char *big;
- long blen;
- const long *skip;
+bm_search(const char *little, long llen, const char *big, long blen, const long *skip)
{
long i, j, k;
@@ -840,155 +991,256 @@ bm_search(little, llen, big, blen, skip)
return -1;
}
-static VALUE
-strio_getline(argc, argv, ptr)
- int argc;
- VALUE *argv;
- struct StringIO *ptr;
+struct getline_arg {
+ VALUE rs;
+ long limit;
+ unsigned int chomp: 1;
+};
+
+static struct getline_arg *
+prepare_getline_args(struct getline_arg *arg, int argc, VALUE *argv)
{
- const char *s, *e, *p;
- long n;
- VALUE str;
+ VALUE str, lim, opts;
+ long limit = -1;
- if (argc == 0) {
+ argc = rb_scan_args(argc, argv, "02:", &str, &lim, &opts);
+ switch (argc) {
+ case 0:
str = rb_rs;
- }
- else {
- rb_scan_args(argc, argv, "1", &str);
+ break;
+
+ case 1:
+ if (!NIL_P(str) && !RB_TYPE_P(str, T_STRING)) {
+ VALUE tmp = rb_check_string_type(str);
+ if (NIL_P(tmp)) {
+ limit = NUM2LONG(str);
+ str = rb_rs;
+ }
+ else {
+ str = tmp;
+ }
+ }
+ break;
+
+ case 2:
if (!NIL_P(str)) StringValue(str);
+ if (!NIL_P(lim)) limit = NUM2LONG(lim);
+ break;
+ }
+ arg->rs = str;
+ arg->limit = limit;
+ arg->chomp = 0;
+ if (!NIL_P(opts)) {
+ static ID keywords[1];
+ VALUE vchomp;
+ if (!keywords[0]) {
+ keywords[0] = rb_intern_const("chomp");
+ }
+ rb_get_kwargs(opts, keywords, 0, 1, &vchomp);
+ arg->chomp = (vchomp != Qundef) && RTEST(vchomp);
+ }
+ return arg;
+}
+
+static inline int
+chomp_newline_width(const char *s, const char *e)
+{
+ if (e > s && *--e == '\n') {
+ if (e > s && *--e == '\r') return 2;
+ return 1;
}
+ return 0;
+}
- if (ptr->pos >= (n = RSTRING(ptr->string)->len)) {
- ptr->flags |= STRIO_EOF;
+static VALUE
+strio_getline(struct getline_arg *arg, struct StringIO *ptr)
+{
+ const char *s, *e, *p;
+ long n, limit = arg->limit;
+ VALUE str = arg->rs;
+ int w = 0;
+ rb_encoding *enc = get_enc(ptr);
+
+ if (ptr->pos >= (n = RSTRING_LEN(ptr->string))) {
return Qnil;
}
- s = RSTRING(ptr->string)->ptr;
- e = s + RSTRING(ptr->string)->len;
+ s = RSTRING_PTR(ptr->string);
+ e = s + RSTRING_LEN(ptr->string);
s += ptr->pos;
+ if (limit > 0 && (size_t)limit < (size_t)(e - s)) {
+ e = rb_enc_right_char_head(s, s + limit, e, get_enc(ptr));
+ }
if (NIL_P(str)) {
- str = rb_str_substr(ptr->string, ptr->pos, e - s);
+ if (arg->chomp) {
+ w = chomp_newline_width(s, e);
+ }
+ str = strio_substr(ptr, ptr->pos, e - s - w, enc);
}
- else if ((n = RSTRING(str)->len) == 0) {
+ else if ((n = RSTRING_LEN(str)) == 0) {
p = s;
- while (*p == '\n') {
+ while (p[(p + 1 < e) && (*p == '\r') && 0] == '\n') {
+ p += *p == '\r';
if (++p == e) {
- ptr->flags |= STRIO_EOF;
return Qnil;
}
}
s = p;
while ((p = memchr(p, '\n', e - p)) && (p != e)) {
if (*++p == '\n') {
- e = p;
+ e = p + 1;
+ w = (arg->chomp ? 1 : 0);
+ break;
+ }
+ else if (*p == '\r' && p < e && p[1] == '\n') {
+ e = p + 2;
+ w = (arg->chomp ? 2 : 0);
break;
}
}
- str = rb_str_substr(ptr->string, s - RSTRING(ptr->string)->ptr, e - s);
+ if (!w && arg->chomp) {
+ w = chomp_newline_width(s, e);
+ }
+ str = strio_substr(ptr, s - RSTRING_PTR(ptr->string), e - s - w, enc);
}
else if (n == 1) {
- if ((p = memchr(s, RSTRING(str)->ptr[0], e - s)) != 0) {
+ if ((p = memchr(s, RSTRING_PTR(str)[0], e - s)) != 0) {
e = p + 1;
+ w = (arg->chomp ? (p > s && *(p-1) == '\r') + 1 : 0);
}
- str = rb_str_substr(ptr->string, ptr->pos, e - s);
+ str = strio_substr(ptr, ptr->pos, e - s - w, enc);
}
else {
if (n < e - s) {
if (e - s < 1024) {
for (p = s; p + n <= e; ++p) {
- if (MEMCMP(p, RSTRING(str)->ptr, char, n) == 0) {
- e = p + n;
+ if (MEMCMP(p, RSTRING_PTR(str), char, n) == 0) {
+ e = p + (arg->chomp ? 0 : n);
break;
}
}
}
else {
long skip[1 << CHAR_BIT], pos;
- p = RSTRING(str)->ptr;
+ p = RSTRING_PTR(str);
bm_init_skip(skip, p, n);
if ((pos = bm_search(p, n, s, e - s, skip)) >= 0) {
- e = s + pos + n;
+ e = s + pos + (arg->chomp ? 0 : n);
}
}
}
- str = rb_str_substr(ptr->string, ptr->pos, e - s);
+ str = strio_substr(ptr, ptr->pos, e - s - w, enc);
}
- ptr->pos = e - RSTRING(ptr->string)->ptr;
+ ptr->pos = e - RSTRING_PTR(ptr->string);
ptr->lineno++;
return str;
}
/*
* 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.
*/
static VALUE
-strio_gets(argc, argv, self)
- int argc;
- VALUE *argv;
- VALUE self;
+strio_gets(int argc, VALUE *argv, VALUE self)
{
- VALUE str = strio_getline(argc, argv, readable(StringIO(self)));
+ struct getline_arg arg;
+ VALUE str;
+ if (prepare_getline_args(&arg, argc, argv)->limit == 0) {
+ struct StringIO *ptr = readable(self);
+ return rb_enc_str_new(0, 0, get_enc(ptr));
+ }
+
+ str = strio_getline(&arg, readable(self));
rb_lastline_set(str);
return str;
}
/*
* 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.
*/
static VALUE
-strio_readline(argc, argv, self)
- int argc;
- VALUE *argv;
- VALUE self;
+strio_readline(int argc, VALUE *argv, VALUE self)
{
- VALUE line = strio_getline(argc, argv, readable(StringIO(self)));
+ VALUE line = rb_funcall2(self, rb_intern("gets"), argc, argv);
if (NIL_P(line)) rb_eof_error();
return line;
}
/*
* 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(...) -> anEnumerator
+ *
+ * strio.each_line(sep=$/) {|line| block } -> strio
+ * strio.each_line(limit) {|line| block } -> strio
+ * strio.each_line(sep,limit) {|line| block } -> strio
+ * strio.each_line(...) -> anEnumerator
*
* See IO#each.
*/
static VALUE
-strio_each(argc, argv, self)
- int argc;
- VALUE *argv;
- VALUE self;
+strio_each(int argc, VALUE *argv, VALUE self)
{
- struct StringIO *ptr = StringIO(self);
VALUE line;
+ struct getline_arg arg;
+
+ StringIO(self);
+ RETURN_ENUMERATOR(self, argc, argv);
+
+ if (prepare_getline_args(&arg, argc, argv)->limit == 0) {
+ rb_raise(rb_eArgError, "invalid limit: 0 for each_line");
+ }
- while (!NIL_P(line = strio_getline(argc, argv, readable(ptr)))) {
+ while (!NIL_P(line = strio_getline(&arg, readable(self)))) {
rb_yield(line);
}
return self;
}
/*
+ * This is a deprecated alias for <code>each_line</code>.
+ */
+static VALUE
+strio_lines(int argc, VALUE *argv, VALUE self)
+{
+ rb_warn("StringIO#lines is deprecated; use #each_line instead");
+ if (!rb_block_given_p())
+ return rb_enumeratorize(self, ID2SYM(rb_intern("each_line")), argc, argv);
+ return strio_each(argc, argv, self);
+}
+
+/*
* call-seq:
- * strio.readlines(sep_string=$/) -> array
+ * strio.readlines(sep=$/) -> array
+ * strio.readlines(limit) -> array
+ * strio.readlines(sep,limit) -> array
*
* See IO#readlines.
*/
static VALUE
-strio_readlines(argc, argv, self)
- int argc;
- VALUE *argv;
- VALUE self;
+strio_readlines(int argc, VALUE *argv, VALUE self)
{
- struct StringIO *ptr = StringIO(self);
- VALUE ary = rb_ary_new(), line;
- while (!NIL_P(line = strio_getline(argc, argv, readable(ptr)))) {
+ VALUE ary, line;
+ struct getline_arg arg;
+
+ StringIO(self);
+ ary = rb_ary_new();
+ if (prepare_getline_args(&arg, argc, argv)->limit == 0) {
+ rb_raise(rb_eArgError, "invalid limit: 0 for readlines");
+ }
+
+ while (!NIL_P(line = strio_getline(&arg, readable(self)))) {
rb_ary_push(ary, line);
}
return ary;
@@ -996,8 +1248,8 @@ strio_readlines(argc, argv, self)
/*
* call-seq:
- * strio.write(string) -> integer
- * strio.syswrite(string) -> integer
+ * strio.write(string, ...) -> integer
+ * strio.syswrite(string) -> integer
*
* Appends the given string to the underlying buffer string of *strio*.
* The stream must be opened for writing. If the argument is not a
@@ -1005,31 +1257,56 @@ strio_readlines(argc, argv, self)
* Returns the number of bytes written. See IO#write.
*/
static VALUE
-strio_write(self, str)
- VALUE self, str;
+strio_write_m(int argc, VALUE *argv, VALUE self)
{
- struct StringIO *ptr = writable(StringIO(self));
+ long len = 0;
+ while (argc-- > 0) {
+ /* StringIO can't exceed long limit */
+ len += strio_write(self, *argv++);
+ }
+ return LONG2NUM(len);
+}
+
+static long
+strio_write(VALUE self, VALUE str)
+{
+ struct StringIO *ptr = writable(self);
long len, olen;
+ rb_encoding *enc, *enc2;
+ rb_encoding *const ascii8bit = rb_ascii8bit_encoding();
- if (TYPE(str) != T_STRING)
+ if (!RB_TYPE_P(str, T_STRING))
str = rb_obj_as_string(str);
- len = RSTRING(str)->len;
- if (!len) return INT2FIX(0);
+ enc = get_enc(ptr);
+ enc2 = rb_enc_get(str);
+ if (enc != enc2 && enc != ascii8bit) {
+ str = rb_str_conv_enc(str, enc2, enc);
+ }
+ len = RSTRING_LEN(str);
+ if (len == 0) return 0;
check_modifiable(ptr);
- olen = RSTRING(ptr->string)->len;
+ olen = RSTRING_LEN(ptr->string);
if (ptr->flags & FMODE_APPEND) {
ptr->pos = olen;
}
if (ptr->pos == olen) {
- rb_str_cat(ptr->string, RSTRING(str)->ptr, len);
+ if (enc == ascii8bit || enc2 == ascii8bit) {
+ rb_enc_str_buf_cat(ptr->string, RSTRING_PTR(str), len, enc);
+ OBJ_INFECT(ptr->string, str);
+ }
+ else {
+ rb_str_buf_append(ptr->string, str);
+ }
}
else {
strio_extend(ptr, ptr->pos, len);
- rb_str_update(ptr->string, ptr->pos, len, str);
+ memmove(RSTRING_PTR(ptr->string)+ptr->pos, RSTRING_PTR(str), len);
+ OBJ_INFECT(ptr->string, str);
}
OBJ_INFECT(ptr->string, self);
+ RB_GC_GUARD(str);
ptr->pos += len;
- return LONG2NUM(len);
+ return len;
}
/*
@@ -1064,21 +1341,20 @@ strio_write(self, str)
* See IO#putc.
*/
static VALUE
-strio_putc(self, ch)
- VALUE self, ch;
+strio_putc(VALUE self, VALUE ch)
{
- struct StringIO *ptr = writable(StringIO(self));
- int c = NUM2CHR(ch);
- long olen;
+ struct StringIO *ptr = writable(self);
+ VALUE str;
check_modifiable(ptr);
- olen = RSTRING(ptr->string)->len;
- if (ptr->flags & FMODE_APPEND) {
- ptr->pos = olen;
+ if (RB_TYPE_P(ch, T_STRING)) {
+ str = rb_str_substr(ch, 0, 1);
}
- strio_extend(ptr, ptr->pos, 1);
- RSTRING(ptr->string)->ptr[ptr->pos++] = c;
- OBJ_INFECT(ptr->string, self);
+ else {
+ char c = NUM2CHR(ch);
+ str = rb_str_new(&c, 1);
+ }
+ strio_write(self, str);
return ch;
}
@@ -1092,137 +1368,157 @@ strio_putc(self, ch)
/*
* call-seq:
- * strio.read([length [, buffer]]) -> string, buffer, or nil
+ * strio.read([length [, outbuf]]) -> string, outbuf, or nil
*
* See IO#read.
*/
static VALUE
-strio_read(argc, argv, self)
- int argc;
- VALUE *argv;
- VALUE self;
+strio_read(int argc, VALUE *argv, VALUE self)
{
- struct StringIO *ptr = readable(StringIO(self));
+ struct StringIO *ptr = readable(self);
VALUE str = Qnil;
- long len, olen;
+ long len;
+ int binary = 0;
+ rb_check_arity(argc, 0, 2);
switch (argc) {
case 2:
str = argv[1];
- StringValue(str);
- rb_str_modify(str);
+ if (!NIL_P(str)) {
+ StringValue(str);
+ rb_str_modify(str);
+ }
+ /* fall through */
case 1:
if (!NIL_P(argv[0])) {
- len = olen = NUM2LONG(argv[0]);
+ len = NUM2LONG(argv[0]);
if (len < 0) {
rb_raise(rb_eArgError, "negative length %ld given", len);
}
- if (len > 0 && ptr->pos >= RSTRING(ptr->string)->len) {
- ptr->flags |= STRIO_EOF;
- if (!NIL_P(str)) rb_str_resize(str, 0);
- return Qnil;
- }
- else if (ptr->flags & STRIO_EOF) {
+ if (len > 0 && ptr->pos >= RSTRING_LEN(ptr->string)) {
if (!NIL_P(str)) rb_str_resize(str, 0);
return Qnil;
}
+ binary = 1;
break;
}
/* fall through */
case 0:
- olen = -1;
- len = RSTRING(ptr->string)->len;
+ len = RSTRING_LEN(ptr->string);
if (len <= ptr->pos) {
- ptr->flags |= STRIO_EOF;
+ rb_encoding *enc = binary ? rb_ascii8bit_encoding() : get_enc(ptr);
if (NIL_P(str)) {
str = rb_str_new(0, 0);
}
else {
rb_str_resize(str, 0);
}
+ rb_enc_associate(str, enc);
return str;
}
else {
len -= ptr->pos;
}
break;
- default:
- rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)", argc);
}
if (NIL_P(str)) {
- str = rb_str_substr(ptr->string, ptr->pos, len);
+ rb_encoding *enc = binary ? rb_ascii8bit_encoding() : get_enc(ptr);
+ str = strio_substr(ptr, ptr->pos, len, enc);
}
else {
- long rest = RSTRING(ptr->string)->len - ptr->pos;
+ long rest = RSTRING_LEN(ptr->string) - ptr->pos;
if (len > rest) len = rest;
rb_str_resize(str, len);
- MEMCPY(RSTRING(str)->ptr, RSTRING(ptr->string)->ptr + ptr->pos, char, len);
- }
- if (NIL_P(str)) {
- if (!(ptr->flags & STRIO_EOF)) str = rb_str_new(0, 0);
- len = 0;
+ MEMCPY(RSTRING_PTR(str), RSTRING_PTR(ptr->string) + ptr->pos, char, len);
+ if (binary)
+ rb_enc_associate(str, rb_ascii8bit_encoding());
+ else
+ rb_enc_copy(str, ptr->string);
}
- else {
- ptr->pos += len = RSTRING(str)->len;
- }
- if (olen < 0 || olen > len) ptr->flags |= STRIO_EOF;
+ ptr->pos += RSTRING_LEN(str);
return str;
}
/*
* call-seq:
* strio.sysread(integer[, outbuf]) -> string
+ * strio.readpartial(integer[, outbuf]) -> string
*
* Similar to #read, but raises +EOFError+ at end of string instead of
* returning +nil+, as well as IO#sysread does.
*/
static VALUE
-strio_sysread(argc, argv, self)
- int argc;
- VALUE *argv;
- VALUE self;
+strio_sysread(int argc, VALUE *argv, VALUE self)
{
- VALUE val = strio_read(argc, argv, self);
- if (NIL_P(val) || RSTRING(val)->len == 0) {
+ VALUE val = rb_funcall2(self, rb_intern("read"), argc, argv);
+ if (NIL_P(val)) {
rb_eof_error();
}
return val;
}
-#define strio_syswrite strio_write
-
-/* call-seq: strio.path -> nil */
-#define strio_path strio_nil
-
/*
* call-seq:
- * strio.isatty -> nil
- * strio.tty? -> nil
+ * strio.read_nonblock(integer[, outbuf [, opts]]) -> string
*
+ * Similar to #read, but raises +EOFError+ at end of string unless the
+ * +exception: false+ option is passed in.
*/
+static VALUE
+strio_read_nonblock(int argc, VALUE *argv, VALUE self)
+{
+ VALUE opts = Qnil, val;
+
+ rb_scan_args(argc, argv, "11:", NULL, NULL, &opts);
+
+ if (!NIL_P(opts)) {
+ argc--;
+ }
+
+ val = strio_read(argc, argv, self);
+ if (NIL_P(val)) {
+ if (!NIL_P(opts) &&
+ rb_hash_lookup2(opts, sym_exception, Qundef) == Qfalse)
+ return Qnil;
+ else
+ rb_eof_error();
+ }
+
+ return val;
+}
+
+#define strio_syswrite rb_io_write
+
+static VALUE
+strio_syswrite_nonblock(int argc, VALUE *argv, VALUE self)
+{
+ VALUE str;
+
+ rb_scan_args(argc, argv, "10:", &str, NULL);
+ return strio_syswrite(self, str);
+}
+
#define strio_isatty strio_false
-/* call-seq: strio.pid -> nil */
#define strio_pid strio_nil
-/* call-seq: strio.fileno -> nil */
#define strio_fileno strio_nil
/*
* call-seq:
+ * strio.length -> integer
* strio.size -> integer
*
* Returns the size of the buffer string.
*/
static VALUE
-strio_size(self)
- VALUE self;
+strio_size(VALUE self)
{
VALUE string = StringIO(self)->string;
if (NIL_P(string)) {
rb_raise(rb_eIOError, "not opened");
}
- return ULONG2NUM(RSTRING(string)->len);
+ return ULONG2NUM(RSTRING_LEN(string));
}
/*
@@ -1233,32 +1529,104 @@ strio_size(self)
* must be opened for writing.
*/
static VALUE
-strio_truncate(self, len)
- VALUE self, len;
+strio_truncate(VALUE self, VALUE len)
{
- VALUE string = writable(StringIO(self))->string;
+ VALUE string = writable(self)->string;
long l = NUM2LONG(len);
- long plen = RSTRING(string)->len;
+ long plen = RSTRING_LEN(string);
if (l < 0) {
- error_inval("negative legnth");
+ error_inval("negative length");
}
rb_str_resize(string, l);
if (plen < l) {
- MEMZERO(RSTRING(string)->ptr + plen, char, l - plen);
+ MEMZERO(RSTRING_PTR(string) + plen, char, l - plen);
}
return len;
}
/*
+ * call-seq:
+ * strio.external_encoding => encoding
+ *
+ * Returns the Encoding object that represents the encoding of the file.
+ * If strio is write mode and no encoding is specified, returns <code>nil</code>.
+ */
+
+static VALUE
+strio_external_encoding(VALUE self)
+{
+ struct StringIO *ptr = StringIO(self);
+ return rb_enc_from_encoding(get_enc(ptr));
+}
+
+/*
+ * call-seq:
+ * strio.internal_encoding => encoding
+ *
+ * Returns the Encoding of the internal string if conversion is
+ * specified. Otherwise returns nil.
+ */
+
+static VALUE
+strio_internal_encoding(VALUE self)
+{
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * strio.set_encoding(ext_enc, [int_enc[, opt]]) => strio
+ *
+ * Specify the encoding of the StringIO as <i>ext_enc</i>.
+ * Use the default external encoding if <i>ext_enc</i> is nil.
+ * 2nd argument <i>int_enc</i> and optional hash <i>opt</i> argument
+ * are ignored; they are for API compatibility to IO.
+ */
+
+static VALUE
+strio_set_encoding(int argc, VALUE *argv, VALUE self)
+{
+ rb_encoding* enc;
+ struct StringIO *ptr = StringIO(self);
+ VALUE ext_enc, int_enc, opt;
+
+ argc = rb_scan_args(argc, argv, "11:", &ext_enc, &int_enc, &opt);
+
+ if (NIL_P(ext_enc)) {
+ enc = rb_default_external_encoding();
+ }
+ else {
+ enc = rb_to_encoding(ext_enc);
+ }
+ ptr->enc = enc;
+ if (WRITABLE(self)) {
+ rb_enc_associate(ptr->string, enc);
+ }
+
+ return self;
+}
+
+/*
* Pseudo I/O on String object.
+ *
+ * Commonly used to simulate `$stdio` or `$stderr`
+ *
+ * === Examples
+ *
+ * require 'stringio'
+ *
+ * io = StringIO.new
+ * io.puts "Hello World"
+ * io.string #=> "Hello World\n"
*/
void
-Init_stringio()
+Init_stringio(void)
{
VALUE StringIO = rb_define_class("StringIO", rb_cData);
rb_include_module(StringIO, rb_mEnumerable);
rb_define_alloc_func(StringIO, strio_s_allocate);
+ rb_define_singleton_method(StringIO, "new", strio_s_new, -1);
rb_define_singleton_method(StringIO, "open", strio_s_open, -1);
rb_define_method(StringIO, "initialize", strio_initialize, -1);
rb_define_method(StringIO, "initialize_copy", strio_copy, 1);
@@ -1269,6 +1637,8 @@ Init_stringio()
rb_define_method(StringIO, "lineno", strio_get_lineno, 0);
rb_define_method(StringIO, "lineno=", strio_set_lineno, 1);
+
+ /* call-seq: strio.binmode -> true */
rb_define_method(StringIO, "binmode", strio_binmode, 0);
rb_define_method(StringIO, "close", strio_close, 0);
rb_define_method(StringIO, "close_read", strio_close_read, 0);
@@ -1278,43 +1648,83 @@ Init_stringio()
rb_define_method(StringIO, "closed_write?", strio_closed_write, 0);
rb_define_method(StringIO, "eof", strio_eof, 0);
rb_define_method(StringIO, "eof?", strio_eof, 0);
+ /* call-seq: strio.fcntl */
rb_define_method(StringIO, "fcntl", strio_fcntl, -1);
+ /* call-seq: strio.flush -> strio */
rb_define_method(StringIO, "flush", strio_flush, 0);
+ /* call-seq: strio.fsync -> 0 */
rb_define_method(StringIO, "fsync", strio_fsync, 0);
rb_define_method(StringIO, "pos", strio_get_pos, 0);
rb_define_method(StringIO, "pos=", strio_set_pos, 1);
rb_define_method(StringIO, "rewind", strio_rewind, 0);
rb_define_method(StringIO, "seek", strio_seek, -1);
rb_define_method(StringIO, "sync", strio_get_sync, 0);
+ /* call-seq: strio.sync = boolean -> boolean */
rb_define_method(StringIO, "sync=", strio_set_sync, 1);
rb_define_method(StringIO, "tell", strio_tell, 0);
- rb_define_method(StringIO, "path", strio_path, 0);
rb_define_method(StringIO, "each", strio_each, -1);
- rb_define_method(StringIO, "each_byte", strio_each_byte, 0);
rb_define_method(StringIO, "each_line", strio_each, -1);
+ rb_define_method(StringIO, "lines", strio_lines, -1);
+ rb_define_method(StringIO, "each_byte", strio_each_byte, 0);
+ rb_define_method(StringIO, "bytes", strio_bytes, 0);
+ rb_define_method(StringIO, "each_char", strio_each_char, 0);
+ rb_define_method(StringIO, "chars", strio_chars, 0);
+ rb_define_method(StringIO, "each_codepoint", strio_each_codepoint, 0);
+ rb_define_method(StringIO, "codepoints", strio_codepoints, 0);
rb_define_method(StringIO, "getc", strio_getc, 0);
rb_define_method(StringIO, "ungetc", strio_ungetc, 1);
- rb_define_method(StringIO, "readchar", strio_readchar, 0);
+ rb_define_method(StringIO, "ungetbyte", strio_ungetbyte, 1);
+ rb_define_method(StringIO, "getbyte", strio_getbyte, 0);
rb_define_method(StringIO, "gets", strio_gets, -1);
- rb_define_method(StringIO, "readline", strio_readline, -1);
rb_define_method(StringIO, "readlines", strio_readlines, -1);
rb_define_method(StringIO, "read", strio_read, -1);
- rb_define_method(StringIO, "sysread", strio_sysread, -1);
- rb_define_method(StringIO, "write", strio_write, 1);
- rb_define_method(StringIO, "<<", strio_addstr, 1);
- rb_define_method(StringIO, "print", strio_print, -1);
- rb_define_method(StringIO, "printf", strio_printf, -1);
+ rb_define_method(StringIO, "write", strio_write_m, -1);
rb_define_method(StringIO, "putc", strio_putc, 1);
- rb_define_method(StringIO, "puts", strio_puts, -1);
- rb_define_method(StringIO, "syswrite", strio_syswrite, 1);
+ /*
+ * call-seq:
+ * strio.isatty -> nil
+ * strio.tty? -> nil
+ *
+ */
rb_define_method(StringIO, "isatty", strio_isatty, 0);
rb_define_method(StringIO, "tty?", strio_isatty, 0);
+
+ /* call-seq: strio.pid -> nil */
rb_define_method(StringIO, "pid", strio_pid, 0);
+
+ /* call-seq: strio.fileno -> nil */
rb_define_method(StringIO, "fileno", strio_fileno, 0);
rb_define_method(StringIO, "size", strio_size, 0);
rb_define_method(StringIO, "length", strio_size, 0);
rb_define_method(StringIO, "truncate", strio_truncate, 1);
+
+ rb_define_method(StringIO, "external_encoding", strio_external_encoding, 0);
+ rb_define_method(StringIO, "internal_encoding", strio_internal_encoding, 0);
+ rb_define_method(StringIO, "set_encoding", strio_set_encoding, -1);
+
+ {
+ VALUE mReadable = rb_define_module_under(rb_cIO, "generic_readable");
+ rb_define_method(mReadable, "readchar", strio_readchar, 0);
+ rb_define_method(mReadable, "readbyte", strio_readbyte, 0);
+ rb_define_method(mReadable, "readline", strio_readline, -1);
+ rb_define_method(mReadable, "sysread", strio_sysread, -1);
+ rb_define_method(mReadable, "readpartial", strio_sysread, -1);
+ rb_define_method(mReadable, "read_nonblock", strio_read_nonblock, -1);
+ rb_include_module(StringIO, mReadable);
+ }
+ {
+ VALUE mWritable = rb_define_module_under(rb_cIO, "generic_writable");
+ rb_define_method(mWritable, "<<", strio_addstr, 1);
+ rb_define_method(mWritable, "print", strio_print, -1);
+ rb_define_method(mWritable, "printf", strio_printf, -1);
+ rb_define_method(mWritable, "puts", strio_puts, -1);
+ rb_define_method(mWritable, "syswrite", strio_syswrite, 1);
+ rb_define_method(mWritable, "write_nonblock", strio_syswrite_nonblock, -1);
+ rb_include_module(StringIO, mWritable);
+ }
+
+ sym_exception = ID2SYM(rb_intern("exception"));
}