ed-precompiled_stringio 3.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.rdoc_options +2 -0
- data/COPYING +56 -0
- data/LICENSE.txt +22 -0
- data/NEWS.md +243 -0
- data/README.md +45 -0
- data/docs/io.rb +8 -0
- data/ext/stringio/.document +1 -0
- data/ext/stringio/extconf.rb +9 -0
- data/ext/stringio/stringio.c +2051 -0
- data/lib/stringio.rb +7 -0
- metadata +64 -0
@@ -0,0 +1,2051 @@
|
|
1
|
+
/* -*- mode: c; indent-tabs-mode: t -*- */
|
2
|
+
/**********************************************************************
|
3
|
+
|
4
|
+
stringio.c -
|
5
|
+
|
6
|
+
$Author$
|
7
|
+
$RoughId: stringio.c,v 1.13 2002/03/14 03:24:18 nobu Exp $
|
8
|
+
created at: Tue Feb 19 04:10:38 JST 2002
|
9
|
+
|
10
|
+
All the files in this distribution are covered under the Ruby's
|
11
|
+
license (see the file COPYING).
|
12
|
+
|
13
|
+
**********************************************************************/
|
14
|
+
|
15
|
+
static const char *const
|
16
|
+
STRINGIO_VERSION = "3.1.8";
|
17
|
+
|
18
|
+
#include <stdbool.h>
|
19
|
+
|
20
|
+
#include "ruby.h"
|
21
|
+
#include "ruby/io.h"
|
22
|
+
#include "ruby/encoding.h"
|
23
|
+
#include "ruby/version.h"
|
24
|
+
#if defined(HAVE_FCNTL_H) || defined(_WIN32)
|
25
|
+
#include <fcntl.h>
|
26
|
+
#elif defined(HAVE_SYS_FCNTL_H)
|
27
|
+
#include <sys/fcntl.h>
|
28
|
+
#endif
|
29
|
+
|
30
|
+
#ifndef RB_INTEGER_TYPE_P
|
31
|
+
# define RB_INTEGER_TYPE_P(c) (FIXNUM_P(c) || RB_TYPE_P(c, T_BIGNUM))
|
32
|
+
#endif
|
33
|
+
|
34
|
+
#ifndef RB_PASS_CALLED_KEYWORDS
|
35
|
+
# define rb_funcallv_kw(recv, mid, arg, argv, kw_splat) rb_funcallv(recv, mid, arg, argv)
|
36
|
+
# define rb_class_new_instance_kw(argc, argv, klass, kw_splat) rb_class_new_instance(argc, argv, klass)
|
37
|
+
#endif
|
38
|
+
|
39
|
+
static inline bool
|
40
|
+
str_chilled_p(VALUE str)
|
41
|
+
{
|
42
|
+
#if (RUBY_API_VERSION_MAJOR == 3 && RUBY_API_VERSION_MINOR >= 4) || RUBY_API_VERSION_MAJOR >= 4
|
43
|
+
// Do not attempt to modify chilled strings on Ruby 3.4+
|
44
|
+
// RUBY_FL_USER2 == STR_CHILLED_LITERAL
|
45
|
+
// RUBY_FL_USER3 == STR_CHILLED_SYMBOL_TO_S
|
46
|
+
return FL_TEST_RAW(str, RUBY_FL_USER2 | RUBY_FL_USER3);
|
47
|
+
#else
|
48
|
+
return false;
|
49
|
+
#endif
|
50
|
+
}
|
51
|
+
|
52
|
+
#ifndef HAVE_TYPE_RB_IO_MODE_T
|
53
|
+
typedef int rb_io_mode_t;
|
54
|
+
#endif
|
55
|
+
|
56
|
+
struct StringIO {
|
57
|
+
VALUE string;
|
58
|
+
rb_encoding *enc;
|
59
|
+
long pos;
|
60
|
+
long lineno;
|
61
|
+
rb_io_mode_t flags;
|
62
|
+
int count;
|
63
|
+
};
|
64
|
+
|
65
|
+
static VALUE strio_init(int, VALUE *, struct StringIO *, VALUE);
|
66
|
+
static VALUE strio_unget_bytes(struct StringIO *, const char *, long);
|
67
|
+
static long strio_write(VALUE self, VALUE str);
|
68
|
+
|
69
|
+
#define IS_STRIO(obj) (rb_typeddata_is_kind_of((obj), &strio_data_type))
|
70
|
+
#define error_inval(msg) (rb_syserr_fail(EINVAL, msg))
|
71
|
+
#define get_enc(ptr) ((ptr)->enc ? (ptr)->enc : !NIL_P((ptr)->string) ? rb_enc_get((ptr)->string) : NULL)
|
72
|
+
|
73
|
+
static bool
|
74
|
+
readonly_string_p(VALUE string)
|
75
|
+
{
|
76
|
+
return OBJ_FROZEN_RAW(string);
|
77
|
+
}
|
78
|
+
|
79
|
+
static struct StringIO *
|
80
|
+
strio_alloc(void)
|
81
|
+
{
|
82
|
+
struct StringIO *ptr = ALLOC(struct StringIO);
|
83
|
+
ptr->string = Qnil;
|
84
|
+
ptr->pos = 0;
|
85
|
+
ptr->lineno = 0;
|
86
|
+
ptr->flags = 0;
|
87
|
+
ptr->count = 1;
|
88
|
+
return ptr;
|
89
|
+
}
|
90
|
+
|
91
|
+
static void
|
92
|
+
strio_mark(void *p)
|
93
|
+
{
|
94
|
+
struct StringIO *ptr = p;
|
95
|
+
|
96
|
+
rb_gc_mark(ptr->string);
|
97
|
+
}
|
98
|
+
|
99
|
+
static void
|
100
|
+
strio_free(void *p)
|
101
|
+
{
|
102
|
+
struct StringIO *ptr = p;
|
103
|
+
if (--ptr->count <= 0) {
|
104
|
+
xfree(ptr);
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
static size_t
|
109
|
+
strio_memsize(const void *p)
|
110
|
+
{
|
111
|
+
return sizeof(struct StringIO);
|
112
|
+
}
|
113
|
+
|
114
|
+
static const rb_data_type_t strio_data_type = {
|
115
|
+
"strio",
|
116
|
+
{
|
117
|
+
strio_mark,
|
118
|
+
strio_free,
|
119
|
+
strio_memsize,
|
120
|
+
},
|
121
|
+
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
|
122
|
+
};
|
123
|
+
|
124
|
+
#define check_strio(self) ((struct StringIO*)rb_check_typeddata((self), &strio_data_type))
|
125
|
+
|
126
|
+
static struct StringIO*
|
127
|
+
get_strio(VALUE self)
|
128
|
+
{
|
129
|
+
struct StringIO *ptr = check_strio(rb_io_taint_check(self));
|
130
|
+
|
131
|
+
if (!ptr) {
|
132
|
+
rb_raise(rb_eIOError, "uninitialized stream");
|
133
|
+
}
|
134
|
+
return ptr;
|
135
|
+
}
|
136
|
+
|
137
|
+
static VALUE
|
138
|
+
enc_subseq(VALUE str, long pos, long len, rb_encoding *enc)
|
139
|
+
{
|
140
|
+
str = rb_str_subseq(str, pos, len);
|
141
|
+
rb_enc_associate(str, enc);
|
142
|
+
return str;
|
143
|
+
}
|
144
|
+
|
145
|
+
static VALUE
|
146
|
+
strio_substr(struct StringIO *ptr, long pos, long len, rb_encoding *enc)
|
147
|
+
{
|
148
|
+
VALUE str = ptr->string;
|
149
|
+
long rlen = RSTRING_LEN(str) - pos;
|
150
|
+
|
151
|
+
if (len > rlen) len = rlen;
|
152
|
+
if (len < 0) len = 0;
|
153
|
+
if (len == 0) return rb_enc_str_new(0, 0, enc);
|
154
|
+
return enc_subseq(str, pos, len, enc);
|
155
|
+
}
|
156
|
+
|
157
|
+
#define StringIO(obj) get_strio(obj)
|
158
|
+
|
159
|
+
#define STRIO_READABLE FL_USER4
|
160
|
+
#define STRIO_WRITABLE FL_USER5
|
161
|
+
#define STRIO_READWRITE (STRIO_READABLE|STRIO_WRITABLE)
|
162
|
+
typedef char strio_flags_check[(STRIO_READABLE/FMODE_READABLE == STRIO_WRITABLE/FMODE_WRITABLE) * 2 - 1];
|
163
|
+
#define STRIO_MODE_SET_P(strio, mode) \
|
164
|
+
((RBASIC(strio)->flags & STRIO_##mode) && \
|
165
|
+
((struct StringIO*)DATA_PTR(strio))->flags & FMODE_##mode)
|
166
|
+
#define CLOSED(strio) (!STRIO_MODE_SET_P(strio, READWRITE))
|
167
|
+
#define READABLE(strio) STRIO_MODE_SET_P(strio, READABLE)
|
168
|
+
#define WRITABLE(strio) STRIO_MODE_SET_P(strio, WRITABLE)
|
169
|
+
|
170
|
+
static VALUE sym_exception;
|
171
|
+
|
172
|
+
static struct StringIO*
|
173
|
+
readable(VALUE strio)
|
174
|
+
{
|
175
|
+
struct StringIO *ptr = StringIO(strio);
|
176
|
+
if (!READABLE(strio)) {
|
177
|
+
rb_raise(rb_eIOError, "not opened for reading");
|
178
|
+
}
|
179
|
+
return ptr;
|
180
|
+
}
|
181
|
+
|
182
|
+
static struct StringIO*
|
183
|
+
writable(VALUE strio)
|
184
|
+
{
|
185
|
+
struct StringIO *ptr = StringIO(strio);
|
186
|
+
if (!WRITABLE(strio)) {
|
187
|
+
rb_raise(rb_eIOError, "not opened for writing");
|
188
|
+
}
|
189
|
+
return ptr;
|
190
|
+
}
|
191
|
+
|
192
|
+
static void
|
193
|
+
check_modifiable(struct StringIO *ptr)
|
194
|
+
{
|
195
|
+
if (NIL_P(ptr->string)) {
|
196
|
+
/* Null device StringIO */
|
197
|
+
}
|
198
|
+
else if (OBJ_FROZEN_RAW(ptr->string)) {
|
199
|
+
rb_raise(rb_eIOError, "not modifiable string");
|
200
|
+
}
|
201
|
+
else {
|
202
|
+
rb_str_modify(ptr->string);
|
203
|
+
}
|
204
|
+
}
|
205
|
+
|
206
|
+
static inline bool
|
207
|
+
outside_p(struct StringIO *ptr, long pos)
|
208
|
+
{
|
209
|
+
return NIL_P(ptr->string) || pos >= RSTRING_LEN(ptr->string);
|
210
|
+
}
|
211
|
+
|
212
|
+
static inline bool
|
213
|
+
eos_p(struct StringIO *ptr)
|
214
|
+
{
|
215
|
+
return outside_p(ptr, ptr->pos);
|
216
|
+
}
|
217
|
+
|
218
|
+
static VALUE
|
219
|
+
strio_s_allocate(VALUE klass)
|
220
|
+
{
|
221
|
+
return TypedData_Wrap_Struct(klass, &strio_data_type, 0);
|
222
|
+
}
|
223
|
+
|
224
|
+
/*
|
225
|
+
* call-seq:
|
226
|
+
* StringIO.new(string = '', mode = 'r+') -> new_stringio
|
227
|
+
*
|
228
|
+
* Note that +mode+ defaults to <tt>'r'</tt> if +string+ is frozen.
|
229
|
+
*
|
230
|
+
* Returns a new \StringIO instance formed from +string+ and +mode+;
|
231
|
+
* see {Access Modes}[rdoc-ref:File@Access+Modes]:
|
232
|
+
*
|
233
|
+
* strio = StringIO.new # => #<StringIO>
|
234
|
+
* strio.close
|
235
|
+
*
|
236
|
+
* The instance should be closed when no longer needed.
|
237
|
+
*
|
238
|
+
* Related: StringIO.open (accepts block; closes automatically).
|
239
|
+
*/
|
240
|
+
static VALUE
|
241
|
+
strio_initialize(int argc, VALUE *argv, VALUE self)
|
242
|
+
{
|
243
|
+
struct StringIO *ptr = check_strio(self);
|
244
|
+
|
245
|
+
if (!ptr) {
|
246
|
+
DATA_PTR(self) = ptr = strio_alloc();
|
247
|
+
}
|
248
|
+
rb_call_super(0, 0);
|
249
|
+
return strio_init(argc, argv, ptr, self);
|
250
|
+
}
|
251
|
+
|
252
|
+
static int
|
253
|
+
detect_bom(VALUE str, int *bomlen)
|
254
|
+
{
|
255
|
+
const char *p;
|
256
|
+
long len;
|
257
|
+
|
258
|
+
RSTRING_GETMEM(str, p, len);
|
259
|
+
if (len < 1) return 0;
|
260
|
+
switch ((unsigned char)p[0]) {
|
261
|
+
case 0xEF:
|
262
|
+
if (len < 2) break;
|
263
|
+
if ((unsigned char)p[1] == 0xBB && len > 2) {
|
264
|
+
if ((unsigned char)p[2] == 0xBF) {
|
265
|
+
*bomlen = 3;
|
266
|
+
return rb_utf8_encindex();
|
267
|
+
}
|
268
|
+
}
|
269
|
+
break;
|
270
|
+
|
271
|
+
case 0xFE:
|
272
|
+
if (len < 2) break;
|
273
|
+
if ((unsigned char)p[1] == 0xFF) {
|
274
|
+
*bomlen = 2;
|
275
|
+
return rb_enc_find_index("UTF-16BE");
|
276
|
+
}
|
277
|
+
break;
|
278
|
+
|
279
|
+
case 0xFF:
|
280
|
+
if (len < 2) break;
|
281
|
+
if ((unsigned char)p[1] == 0xFE) {
|
282
|
+
if (len >= 4 && (unsigned char)p[2] == 0 && (unsigned char)p[3] == 0) {
|
283
|
+
*bomlen = 4;
|
284
|
+
return rb_enc_find_index("UTF-32LE");
|
285
|
+
}
|
286
|
+
*bomlen = 2;
|
287
|
+
return rb_enc_find_index("UTF-16LE");
|
288
|
+
}
|
289
|
+
break;
|
290
|
+
|
291
|
+
case 0:
|
292
|
+
if (len < 4) break;
|
293
|
+
if ((unsigned char)p[1] == 0 && (unsigned char)p[2] == 0xFE && (unsigned char)p[3] == 0xFF) {
|
294
|
+
*bomlen = 4;
|
295
|
+
return rb_enc_find_index("UTF-32BE");
|
296
|
+
}
|
297
|
+
break;
|
298
|
+
}
|
299
|
+
return 0;
|
300
|
+
}
|
301
|
+
|
302
|
+
static rb_encoding *
|
303
|
+
set_encoding_by_bom(struct StringIO *ptr)
|
304
|
+
{
|
305
|
+
int bomlen, idx = detect_bom(ptr->string, &bomlen);
|
306
|
+
rb_encoding *extenc = NULL;
|
307
|
+
|
308
|
+
if (idx) {
|
309
|
+
extenc = rb_enc_from_index(idx);
|
310
|
+
ptr->pos = bomlen;
|
311
|
+
if (ptr->flags & FMODE_WRITABLE) {
|
312
|
+
rb_enc_associate_index(ptr->string, idx);
|
313
|
+
}
|
314
|
+
}
|
315
|
+
ptr->enc = extenc;
|
316
|
+
return extenc;
|
317
|
+
}
|
318
|
+
|
319
|
+
static VALUE
|
320
|
+
strio_init(int argc, VALUE *argv, struct StringIO *ptr, VALUE self)
|
321
|
+
{
|
322
|
+
VALUE string, vmode, opt;
|
323
|
+
int oflags;
|
324
|
+
rb_io_enc_t convconfig;
|
325
|
+
|
326
|
+
argc = rb_scan_args(argc, argv, "02:", &string, &vmode, &opt);
|
327
|
+
rb_io_extract_modeenc(&vmode, 0, opt, &oflags, &ptr->flags, &convconfig);
|
328
|
+
if (!NIL_P(string)) {
|
329
|
+
StringValue(string);
|
330
|
+
}
|
331
|
+
else if (!argc) {
|
332
|
+
string = rb_enc_str_new("", 0, rb_default_external_encoding());
|
333
|
+
}
|
334
|
+
|
335
|
+
if (!NIL_P(string) && readonly_string_p(string)) {
|
336
|
+
if (ptr->flags & FMODE_WRITABLE) {
|
337
|
+
rb_syserr_fail(EACCES, 0);
|
338
|
+
}
|
339
|
+
}
|
340
|
+
else {
|
341
|
+
if (NIL_P(vmode)) {
|
342
|
+
ptr->flags |= FMODE_WRITABLE;
|
343
|
+
}
|
344
|
+
}
|
345
|
+
if (!NIL_P(string) && (ptr->flags & FMODE_TRUNC)) {
|
346
|
+
rb_str_resize(string, 0);
|
347
|
+
}
|
348
|
+
RB_OBJ_WRITE(self, &ptr->string, string);
|
349
|
+
if (argc == 1 && !NIL_P(string)) {
|
350
|
+
ptr->enc = rb_enc_get(string);
|
351
|
+
}
|
352
|
+
else {
|
353
|
+
ptr->enc = convconfig.enc;
|
354
|
+
}
|
355
|
+
ptr->pos = 0;
|
356
|
+
ptr->lineno = 0;
|
357
|
+
if (ptr->flags & FMODE_SETENC_BY_BOM) set_encoding_by_bom(ptr);
|
358
|
+
RBASIC(self)->flags |= (ptr->flags & FMODE_READWRITE) * (STRIO_READABLE / FMODE_READABLE);
|
359
|
+
return self;
|
360
|
+
}
|
361
|
+
|
362
|
+
static VALUE
|
363
|
+
strio_finalize(VALUE self)
|
364
|
+
{
|
365
|
+
struct StringIO *ptr = StringIO(self);
|
366
|
+
RB_OBJ_WRITE(self, &ptr->string, Qnil);
|
367
|
+
ptr->flags &= ~FMODE_READWRITE;
|
368
|
+
return self;
|
369
|
+
}
|
370
|
+
|
371
|
+
/*
|
372
|
+
* call-seq:
|
373
|
+
* StringIO.open(string = '', mode = 'r+') {|strio| ... }
|
374
|
+
*
|
375
|
+
* Note that +mode+ defaults to <tt>'r'</tt> if +string+ is frozen.
|
376
|
+
*
|
377
|
+
* Creates a new \StringIO instance formed from +string+ and +mode+;
|
378
|
+
* see {Access Modes}[rdoc-ref:File@Access+Modes].
|
379
|
+
*
|
380
|
+
* With no block, returns the new instance:
|
381
|
+
*
|
382
|
+
* strio = StringIO.open # => #<StringIO>
|
383
|
+
*
|
384
|
+
* With a block, calls the block with the new instance
|
385
|
+
* and returns the block's value;
|
386
|
+
* closes the instance on block exit.
|
387
|
+
*
|
388
|
+
* StringIO.open {|strio| p strio }
|
389
|
+
* # => #<StringIO>
|
390
|
+
*
|
391
|
+
* Related: StringIO.new.
|
392
|
+
*/
|
393
|
+
static VALUE
|
394
|
+
strio_s_open(int argc, VALUE *argv, VALUE klass)
|
395
|
+
{
|
396
|
+
VALUE obj = rb_class_new_instance_kw(argc, argv, klass, RB_PASS_CALLED_KEYWORDS);
|
397
|
+
if (!rb_block_given_p()) return obj;
|
398
|
+
return rb_ensure(rb_yield, obj, strio_finalize, obj);
|
399
|
+
}
|
400
|
+
|
401
|
+
/* :nodoc: */
|
402
|
+
static VALUE
|
403
|
+
strio_s_new(int argc, VALUE *argv, VALUE klass)
|
404
|
+
{
|
405
|
+
if (rb_block_given_p()) {
|
406
|
+
VALUE cname = rb_obj_as_string(klass);
|
407
|
+
|
408
|
+
rb_warn("%"PRIsVALUE"::new() does not take block; use %"PRIsVALUE"::open() instead",
|
409
|
+
cname, cname);
|
410
|
+
}
|
411
|
+
return rb_class_new_instance_kw(argc, argv, klass, RB_PASS_CALLED_KEYWORDS);
|
412
|
+
}
|
413
|
+
|
414
|
+
/*
|
415
|
+
* Returns +false+. Just for compatibility to IO.
|
416
|
+
*/
|
417
|
+
static VALUE
|
418
|
+
strio_false(VALUE self)
|
419
|
+
{
|
420
|
+
StringIO(self);
|
421
|
+
return Qfalse;
|
422
|
+
}
|
423
|
+
|
424
|
+
/*
|
425
|
+
* Returns +nil+. Just for compatibility to IO.
|
426
|
+
*/
|
427
|
+
static VALUE
|
428
|
+
strio_nil(VALUE self)
|
429
|
+
{
|
430
|
+
StringIO(self);
|
431
|
+
return Qnil;
|
432
|
+
}
|
433
|
+
|
434
|
+
/*
|
435
|
+
* Returns an object itself. Just for compatibility to IO.
|
436
|
+
*/
|
437
|
+
static VALUE
|
438
|
+
strio_self(VALUE self)
|
439
|
+
{
|
440
|
+
StringIO(self);
|
441
|
+
return self;
|
442
|
+
}
|
443
|
+
|
444
|
+
/*
|
445
|
+
* Returns 0. Just for compatibility to IO.
|
446
|
+
*/
|
447
|
+
static VALUE
|
448
|
+
strio_0(VALUE self)
|
449
|
+
{
|
450
|
+
StringIO(self);
|
451
|
+
return INT2FIX(0);
|
452
|
+
}
|
453
|
+
|
454
|
+
/*
|
455
|
+
* Returns the argument unchanged. Just for compatibility to IO.
|
456
|
+
*/
|
457
|
+
static VALUE
|
458
|
+
strio_first(VALUE self, VALUE arg)
|
459
|
+
{
|
460
|
+
StringIO(self);
|
461
|
+
return arg;
|
462
|
+
}
|
463
|
+
|
464
|
+
/*
|
465
|
+
* Raises NotImplementedError.
|
466
|
+
*/
|
467
|
+
static VALUE
|
468
|
+
strio_unimpl(int argc, VALUE *argv, VALUE self)
|
469
|
+
{
|
470
|
+
StringIO(self);
|
471
|
+
rb_notimplement();
|
472
|
+
|
473
|
+
UNREACHABLE;
|
474
|
+
}
|
475
|
+
|
476
|
+
/*
|
477
|
+
* call-seq:
|
478
|
+
* string -> string
|
479
|
+
*
|
480
|
+
* Returns underlying string:
|
481
|
+
*
|
482
|
+
* StringIO.open('foo') do |strio|
|
483
|
+
* p strio.string
|
484
|
+
* strio.string = 'bar'
|
485
|
+
* p strio.string
|
486
|
+
* end
|
487
|
+
*
|
488
|
+
* Output:
|
489
|
+
*
|
490
|
+
* "foo"
|
491
|
+
* "bar"
|
492
|
+
*
|
493
|
+
* Related: StringIO#string= (assigns the underlying string).
|
494
|
+
*/
|
495
|
+
static VALUE
|
496
|
+
strio_get_string(VALUE self)
|
497
|
+
{
|
498
|
+
return StringIO(self)->string;
|
499
|
+
}
|
500
|
+
|
501
|
+
/*
|
502
|
+
* call-seq:
|
503
|
+
* string = other_string -> other_string
|
504
|
+
*
|
505
|
+
* Assigns the underlying string as +other_string+, and sets position to zero;
|
506
|
+
* returns +other_string+:
|
507
|
+
*
|
508
|
+
* StringIO.open('foo') do |strio|
|
509
|
+
* p strio.string
|
510
|
+
* strio.string = 'bar'
|
511
|
+
* p strio.string
|
512
|
+
* end
|
513
|
+
*
|
514
|
+
* Output:
|
515
|
+
*
|
516
|
+
* "foo"
|
517
|
+
* "bar"
|
518
|
+
*
|
519
|
+
* Related: StringIO#string (returns the underlying string).
|
520
|
+
*/
|
521
|
+
static VALUE
|
522
|
+
strio_set_string(VALUE self, VALUE string)
|
523
|
+
{
|
524
|
+
struct StringIO *ptr = StringIO(self);
|
525
|
+
|
526
|
+
rb_io_taint_check(self);
|
527
|
+
ptr->flags &= ~FMODE_READWRITE;
|
528
|
+
StringValue(string);
|
529
|
+
ptr->flags = readonly_string_p(string) ? FMODE_READABLE : FMODE_READWRITE;
|
530
|
+
ptr->pos = 0;
|
531
|
+
ptr->lineno = 0;
|
532
|
+
RB_OBJ_WRITE(self, &ptr->string, string);
|
533
|
+
return string;
|
534
|
+
}
|
535
|
+
|
536
|
+
/*
|
537
|
+
* call-seq:
|
538
|
+
* close -> nil
|
539
|
+
*
|
540
|
+
* Closes +self+ for both reading and writing.
|
541
|
+
*
|
542
|
+
* Raises IOError if reading or writing is attempted.
|
543
|
+
*
|
544
|
+
* Related: StringIO#close_read, StringIO#close_write.
|
545
|
+
*/
|
546
|
+
static VALUE
|
547
|
+
strio_close(VALUE self)
|
548
|
+
{
|
549
|
+
StringIO(self);
|
550
|
+
RBASIC(self)->flags &= ~STRIO_READWRITE;
|
551
|
+
return Qnil;
|
552
|
+
}
|
553
|
+
|
554
|
+
/*
|
555
|
+
* call-seq:
|
556
|
+
* close_read -> nil
|
557
|
+
*
|
558
|
+
* Closes +self+ for reading; closed-write setting remains unchanged.
|
559
|
+
*
|
560
|
+
* Raises IOError if reading is attempted.
|
561
|
+
*
|
562
|
+
* Related: StringIO#close, StringIO#close_write.
|
563
|
+
*/
|
564
|
+
static VALUE
|
565
|
+
strio_close_read(VALUE self)
|
566
|
+
{
|
567
|
+
struct StringIO *ptr = StringIO(self);
|
568
|
+
if (!(ptr->flags & FMODE_READABLE)) {
|
569
|
+
rb_raise(rb_eIOError, "closing non-duplex IO for reading");
|
570
|
+
}
|
571
|
+
RBASIC(self)->flags &= ~STRIO_READABLE;
|
572
|
+
return Qnil;
|
573
|
+
}
|
574
|
+
|
575
|
+
/*
|
576
|
+
* call-seq:
|
577
|
+
* close_write -> nil
|
578
|
+
*
|
579
|
+
* Closes +self+ for writing; closed-read setting remains unchanged.
|
580
|
+
*
|
581
|
+
* Raises IOError if writing is attempted.
|
582
|
+
*
|
583
|
+
* Related: StringIO#close, StringIO#close_read.
|
584
|
+
*/
|
585
|
+
static VALUE
|
586
|
+
strio_close_write(VALUE self)
|
587
|
+
{
|
588
|
+
struct StringIO *ptr = StringIO(self);
|
589
|
+
if (!(ptr->flags & FMODE_WRITABLE)) {
|
590
|
+
rb_raise(rb_eIOError, "closing non-duplex IO for writing");
|
591
|
+
}
|
592
|
+
RBASIC(self)->flags &= ~STRIO_WRITABLE;
|
593
|
+
return Qnil;
|
594
|
+
}
|
595
|
+
|
596
|
+
/*
|
597
|
+
* call-seq:
|
598
|
+
* closed? -> true or false
|
599
|
+
*
|
600
|
+
* Returns +true+ if +self+ is closed for both reading and writing,
|
601
|
+
* +false+ otherwise.
|
602
|
+
*/
|
603
|
+
static VALUE
|
604
|
+
strio_closed(VALUE self)
|
605
|
+
{
|
606
|
+
StringIO(self);
|
607
|
+
if (!CLOSED(self)) return Qfalse;
|
608
|
+
return Qtrue;
|
609
|
+
}
|
610
|
+
|
611
|
+
/*
|
612
|
+
* call-seq:
|
613
|
+
* closed_read? -> true or false
|
614
|
+
*
|
615
|
+
* Returns +true+ if +self+ is closed for reading, +false+ otherwise.
|
616
|
+
*/
|
617
|
+
static VALUE
|
618
|
+
strio_closed_read(VALUE self)
|
619
|
+
{
|
620
|
+
StringIO(self);
|
621
|
+
if (READABLE(self)) return Qfalse;
|
622
|
+
return Qtrue;
|
623
|
+
}
|
624
|
+
|
625
|
+
/*
|
626
|
+
* call-seq:
|
627
|
+
* closed_write? -> true or false
|
628
|
+
*
|
629
|
+
* Returns +true+ if +self+ is closed for writing, +false+ otherwise.
|
630
|
+
*/
|
631
|
+
static VALUE
|
632
|
+
strio_closed_write(VALUE self)
|
633
|
+
{
|
634
|
+
StringIO(self);
|
635
|
+
if (WRITABLE(self)) return Qfalse;
|
636
|
+
return Qtrue;
|
637
|
+
}
|
638
|
+
|
639
|
+
static struct StringIO *
|
640
|
+
strio_to_read(VALUE self)
|
641
|
+
{
|
642
|
+
struct StringIO *ptr = readable(self);
|
643
|
+
if (eos_p(ptr)) return NULL;
|
644
|
+
return ptr;
|
645
|
+
}
|
646
|
+
|
647
|
+
/*
|
648
|
+
* call-seq:
|
649
|
+
* eof? -> true or false
|
650
|
+
*
|
651
|
+
* Returns +true+ if positioned at end-of-stream, +false+ otherwise;
|
652
|
+
* see {Position}[rdoc-ref:IO@Position].
|
653
|
+
*
|
654
|
+
* Raises IOError if the stream is not opened for reading.
|
655
|
+
*/
|
656
|
+
static VALUE
|
657
|
+
strio_eof(VALUE self)
|
658
|
+
{
|
659
|
+
if (strio_to_read(self)) return Qfalse;
|
660
|
+
return Qtrue;
|
661
|
+
}
|
662
|
+
|
663
|
+
/* :nodoc: */
|
664
|
+
static VALUE
|
665
|
+
strio_copy(VALUE copy, VALUE orig)
|
666
|
+
{
|
667
|
+
struct StringIO *ptr, *old_ptr;
|
668
|
+
VALUE old_string = Qundef;
|
669
|
+
|
670
|
+
orig = rb_convert_type(orig, T_DATA, "StringIO", "to_strio");
|
671
|
+
if (copy == orig) return copy;
|
672
|
+
ptr = StringIO(orig);
|
673
|
+
old_ptr = check_strio(copy);
|
674
|
+
if (old_ptr) {
|
675
|
+
old_string = old_ptr->string;
|
676
|
+
strio_free(old_ptr);
|
677
|
+
}
|
678
|
+
DATA_PTR(copy) = ptr;
|
679
|
+
RB_OBJ_WRITTEN(copy, old_string, ptr->string);
|
680
|
+
RBASIC(copy)->flags &= ~STRIO_READWRITE;
|
681
|
+
RBASIC(copy)->flags |= RBASIC(orig)->flags & STRIO_READWRITE;
|
682
|
+
++ptr->count;
|
683
|
+
return copy;
|
684
|
+
}
|
685
|
+
|
686
|
+
/*
|
687
|
+
* call-seq:
|
688
|
+
* lineno -> current_line_number
|
689
|
+
*
|
690
|
+
* Returns the current line number in +self+;
|
691
|
+
* see {Line Number}[rdoc-ref:IO@Line+Number].
|
692
|
+
*/
|
693
|
+
static VALUE
|
694
|
+
strio_get_lineno(VALUE self)
|
695
|
+
{
|
696
|
+
return LONG2NUM(StringIO(self)->lineno);
|
697
|
+
}
|
698
|
+
|
699
|
+
/*
|
700
|
+
* call-seq:
|
701
|
+
* lineno = new_line_number -> new_line_number
|
702
|
+
*
|
703
|
+
* Sets the current line number in +self+ to the given +new_line_number+;
|
704
|
+
* see {Line Number}[rdoc-ref:IO@Line+Number].
|
705
|
+
*/
|
706
|
+
static VALUE
|
707
|
+
strio_set_lineno(VALUE self, VALUE lineno)
|
708
|
+
{
|
709
|
+
StringIO(self)->lineno = NUM2LONG(lineno);
|
710
|
+
return lineno;
|
711
|
+
}
|
712
|
+
|
713
|
+
/*
|
714
|
+
* call-seq:
|
715
|
+
* binmode -> self
|
716
|
+
*
|
717
|
+
* Sets the data mode in +self+ to binary mode;
|
718
|
+
* see {Data Mode}[rdoc-ref:File@Data+Mode].
|
719
|
+
*
|
720
|
+
*/
|
721
|
+
static VALUE
|
722
|
+
strio_binmode(VALUE self)
|
723
|
+
{
|
724
|
+
struct StringIO *ptr = StringIO(self);
|
725
|
+
rb_encoding *enc = rb_ascii8bit_encoding();
|
726
|
+
|
727
|
+
ptr->enc = enc;
|
728
|
+
if (WRITABLE(self)) {
|
729
|
+
rb_enc_associate(ptr->string, enc);
|
730
|
+
}
|
731
|
+
return self;
|
732
|
+
}
|
733
|
+
|
734
|
+
#define strio_fcntl strio_unimpl
|
735
|
+
|
736
|
+
#define strio_flush strio_self
|
737
|
+
|
738
|
+
#define strio_fsync strio_0
|
739
|
+
|
740
|
+
/*
|
741
|
+
* call-seq:
|
742
|
+
* reopen(other, mode = 'r+') -> self
|
743
|
+
*
|
744
|
+
* Reinitializes the stream with the given +other+ (string or StringIO) and +mode+;
|
745
|
+
* see IO.new:
|
746
|
+
*
|
747
|
+
* StringIO.open('foo') do |strio|
|
748
|
+
* p strio.string
|
749
|
+
* strio.reopen('bar')
|
750
|
+
* p strio.string
|
751
|
+
* other_strio = StringIO.new('baz')
|
752
|
+
* strio.reopen(other_strio)
|
753
|
+
* p strio.string
|
754
|
+
* other_strio.close
|
755
|
+
* end
|
756
|
+
*
|
757
|
+
* Output:
|
758
|
+
*
|
759
|
+
* "foo"
|
760
|
+
* "bar"
|
761
|
+
* "baz"
|
762
|
+
*
|
763
|
+
*/
|
764
|
+
static VALUE
|
765
|
+
strio_reopen(int argc, VALUE *argv, VALUE self)
|
766
|
+
{
|
767
|
+
rb_io_taint_check(self);
|
768
|
+
if (argc == 1 && !RB_TYPE_P(*argv, T_STRING)) {
|
769
|
+
return strio_copy(self, *argv);
|
770
|
+
}
|
771
|
+
return strio_init(argc, argv, StringIO(self), self);
|
772
|
+
}
|
773
|
+
|
774
|
+
/*
|
775
|
+
* call-seq:
|
776
|
+
* pos -> stream_position
|
777
|
+
*
|
778
|
+
* Returns the current position (in bytes);
|
779
|
+
* see {Position}[rdoc-ref:IO@Position].
|
780
|
+
*/
|
781
|
+
static VALUE
|
782
|
+
strio_get_pos(VALUE self)
|
783
|
+
{
|
784
|
+
return LONG2NUM(StringIO(self)->pos);
|
785
|
+
}
|
786
|
+
|
787
|
+
/*
|
788
|
+
* call-seq:
|
789
|
+
* pos = new_position -> new_position
|
790
|
+
*
|
791
|
+
* Sets the current position (in bytes);
|
792
|
+
* see {Position}[rdoc-ref:IO@Position].
|
793
|
+
*/
|
794
|
+
static VALUE
|
795
|
+
strio_set_pos(VALUE self, VALUE pos)
|
796
|
+
{
|
797
|
+
struct StringIO *ptr = StringIO(self);
|
798
|
+
long p = NUM2LONG(pos);
|
799
|
+
if (p < 0) {
|
800
|
+
error_inval(0);
|
801
|
+
}
|
802
|
+
ptr->pos = p;
|
803
|
+
return pos;
|
804
|
+
}
|
805
|
+
|
806
|
+
/*
|
807
|
+
* call-seq:
|
808
|
+
* rewind -> 0
|
809
|
+
*
|
810
|
+
* Sets the current position and line number to zero;
|
811
|
+
* see {Position}[rdoc-ref:IO@Position]
|
812
|
+
* and {Line Number}[rdoc-ref:IO@Line+Number].
|
813
|
+
*/
|
814
|
+
static VALUE
|
815
|
+
strio_rewind(VALUE self)
|
816
|
+
{
|
817
|
+
struct StringIO *ptr = StringIO(self);
|
818
|
+
ptr->pos = 0;
|
819
|
+
ptr->lineno = 0;
|
820
|
+
return INT2FIX(0);
|
821
|
+
}
|
822
|
+
|
823
|
+
/*
|
824
|
+
* call-seq:
|
825
|
+
* seek(offset, whence = SEEK_SET) -> 0
|
826
|
+
*
|
827
|
+
* Sets the current position to the given integer +offset+ (in bytes),
|
828
|
+
* with respect to a given constant +whence+;
|
829
|
+
* see {Position}[rdoc-ref:IO@Position].
|
830
|
+
*/
|
831
|
+
static VALUE
|
832
|
+
strio_seek(int argc, VALUE *argv, VALUE self)
|
833
|
+
{
|
834
|
+
VALUE whence;
|
835
|
+
struct StringIO *ptr = StringIO(self);
|
836
|
+
long amount, offset;
|
837
|
+
|
838
|
+
rb_scan_args(argc, argv, "11", NULL, &whence);
|
839
|
+
amount = NUM2LONG(argv[0]);
|
840
|
+
if (CLOSED(self)) {
|
841
|
+
rb_raise(rb_eIOError, "closed stream");
|
842
|
+
}
|
843
|
+
switch (NIL_P(whence) ? 0 : NUM2LONG(whence)) {
|
844
|
+
case 0:
|
845
|
+
offset = 0;
|
846
|
+
break;
|
847
|
+
case 1:
|
848
|
+
offset = ptr->pos;
|
849
|
+
break;
|
850
|
+
case 2:
|
851
|
+
if (NIL_P(ptr->string)) {
|
852
|
+
offset = 0;
|
853
|
+
} else {
|
854
|
+
offset = RSTRING_LEN(ptr->string);
|
855
|
+
}
|
856
|
+
break;
|
857
|
+
default:
|
858
|
+
error_inval("invalid whence");
|
859
|
+
}
|
860
|
+
if (amount > LONG_MAX - offset || amount + offset < 0) {
|
861
|
+
error_inval(0);
|
862
|
+
}
|
863
|
+
ptr->pos = amount + offset;
|
864
|
+
return INT2FIX(0);
|
865
|
+
}
|
866
|
+
|
867
|
+
/*
|
868
|
+
* call-seq:
|
869
|
+
* sync -> true
|
870
|
+
*
|
871
|
+
* Returns +true+; implemented only for compatibility with other stream classes.
|
872
|
+
*/
|
873
|
+
static VALUE
|
874
|
+
strio_get_sync(VALUE self)
|
875
|
+
{
|
876
|
+
StringIO(self);
|
877
|
+
return Qtrue;
|
878
|
+
}
|
879
|
+
|
880
|
+
#define strio_set_sync strio_first
|
881
|
+
|
882
|
+
#define strio_tell strio_get_pos
|
883
|
+
|
884
|
+
/*
|
885
|
+
* call-seq:
|
886
|
+
* each_byte {|byte| ... } -> self
|
887
|
+
*
|
888
|
+
* With a block given, calls the block with each remaining byte in the stream;
|
889
|
+
* see {Byte IO}[rdoc-ref:IO@Byte+IO].
|
890
|
+
*
|
891
|
+
* With no block given, returns an enumerator.
|
892
|
+
*/
|
893
|
+
static VALUE
|
894
|
+
strio_each_byte(VALUE self)
|
895
|
+
{
|
896
|
+
struct StringIO *ptr;
|
897
|
+
|
898
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
899
|
+
|
900
|
+
while ((ptr = strio_to_read(self)) != NULL) {
|
901
|
+
char c = RSTRING_PTR(ptr->string)[ptr->pos++];
|
902
|
+
rb_yield(CHR2FIX(c));
|
903
|
+
}
|
904
|
+
return self;
|
905
|
+
}
|
906
|
+
|
907
|
+
/*
|
908
|
+
* call-seq:
|
909
|
+
* getc -> character or nil
|
910
|
+
*
|
911
|
+
* Reads and returns the next character from the stream;
|
912
|
+
* see {Character IO}[rdoc-ref:IO@Character+IO].
|
913
|
+
*/
|
914
|
+
static VALUE
|
915
|
+
strio_getc(VALUE self)
|
916
|
+
{
|
917
|
+
struct StringIO *ptr = readable(self);
|
918
|
+
rb_encoding *enc = get_enc(ptr);
|
919
|
+
VALUE str = ptr->string;
|
920
|
+
long pos = ptr->pos;
|
921
|
+
int len;
|
922
|
+
char *p;
|
923
|
+
|
924
|
+
if (eos_p(ptr)) {
|
925
|
+
return Qnil;
|
926
|
+
}
|
927
|
+
p = RSTRING_PTR(str)+pos;
|
928
|
+
len = rb_enc_mbclen(p, RSTRING_END(str), enc);
|
929
|
+
ptr->pos += len;
|
930
|
+
return enc_subseq(str, pos, len, enc);
|
931
|
+
}
|
932
|
+
|
933
|
+
/*
|
934
|
+
* call-seq:
|
935
|
+
* getbyte -> byte or nil
|
936
|
+
*
|
937
|
+
* Reads and returns the next 8-bit byte from the stream;
|
938
|
+
* see {Byte IO}[rdoc-ref:IO@Byte+IO].
|
939
|
+
*/
|
940
|
+
static VALUE
|
941
|
+
strio_getbyte(VALUE self)
|
942
|
+
{
|
943
|
+
struct StringIO *ptr = readable(self);
|
944
|
+
int c;
|
945
|
+
if (eos_p(ptr)) {
|
946
|
+
return Qnil;
|
947
|
+
}
|
948
|
+
c = RSTRING_PTR(ptr->string)[ptr->pos++];
|
949
|
+
return CHR2FIX(c);
|
950
|
+
}
|
951
|
+
|
952
|
+
static void
|
953
|
+
strio_extend(struct StringIO *ptr, long pos, long len)
|
954
|
+
{
|
955
|
+
long olen;
|
956
|
+
|
957
|
+
if (len > LONG_MAX - pos)
|
958
|
+
rb_raise(rb_eArgError, "string size too big");
|
959
|
+
|
960
|
+
check_modifiable(ptr);
|
961
|
+
olen = RSTRING_LEN(ptr->string);
|
962
|
+
if (pos + len > olen) {
|
963
|
+
rb_str_resize(ptr->string, pos + len);
|
964
|
+
if (pos > olen)
|
965
|
+
MEMZERO(RSTRING_PTR(ptr->string) + olen, char, pos - olen);
|
966
|
+
}
|
967
|
+
}
|
968
|
+
|
969
|
+
static void
|
970
|
+
strio_unget_string(struct StringIO *ptr, VALUE c)
|
971
|
+
{
|
972
|
+
const char *cp = NULL;
|
973
|
+
long cl = RSTRING_LEN(c);
|
974
|
+
if (cl > 0) {
|
975
|
+
if (c != ptr->string) cp = RSTRING_PTR(c);
|
976
|
+
strio_unget_bytes(ptr, cp, cl);
|
977
|
+
RB_GC_GUARD(c);
|
978
|
+
}
|
979
|
+
}
|
980
|
+
|
981
|
+
/*
|
982
|
+
* call-seq:
|
983
|
+
* ungetc(character) -> nil
|
984
|
+
*
|
985
|
+
* Pushes back ("unshifts") a character or integer onto the stream;
|
986
|
+
* see {Character IO}[rdoc-ref:IO@Character+IO].
|
987
|
+
*/
|
988
|
+
static VALUE
|
989
|
+
strio_ungetc(VALUE self, VALUE c)
|
990
|
+
{
|
991
|
+
struct StringIO *ptr = readable(self);
|
992
|
+
rb_encoding *enc, *enc2;
|
993
|
+
|
994
|
+
check_modifiable(ptr);
|
995
|
+
if (NIL_P(ptr->string)) return Qnil;
|
996
|
+
if (NIL_P(c)) return Qnil;
|
997
|
+
if (RB_INTEGER_TYPE_P(c)) {
|
998
|
+
int len, cc = NUM2INT(c);
|
999
|
+
char buf[16];
|
1000
|
+
|
1001
|
+
enc = rb_enc_get(ptr->string);
|
1002
|
+
len = rb_enc_codelen(cc, enc);
|
1003
|
+
if (len <= 0) {
|
1004
|
+
rb_enc_uint_chr(cc, enc); /* to raise an exception */
|
1005
|
+
UNREACHABLE;
|
1006
|
+
}
|
1007
|
+
rb_enc_mbcput(cc, buf, enc);
|
1008
|
+
return strio_unget_bytes(ptr, buf, len);
|
1009
|
+
}
|
1010
|
+
else {
|
1011
|
+
StringValue(c);
|
1012
|
+
if (RSTRING_LEN(c) == 0) return Qnil;
|
1013
|
+
enc = rb_enc_get(ptr->string);
|
1014
|
+
enc2 = rb_enc_get(c);
|
1015
|
+
if (enc != enc2 && enc != rb_ascii8bit_encoding()) {
|
1016
|
+
c = rb_str_conv_enc(c, enc2, enc);
|
1017
|
+
}
|
1018
|
+
strio_unget_string(ptr, c);
|
1019
|
+
return Qnil;
|
1020
|
+
}
|
1021
|
+
}
|
1022
|
+
|
1023
|
+
/*
|
1024
|
+
* call-seq:
|
1025
|
+
* ungetbyte(byte) -> nil
|
1026
|
+
*
|
1027
|
+
* Pushes back ("unshifts") an 8-bit byte onto the stream;
|
1028
|
+
* see {Byte IO}[rdoc-ref:IO@Byte+IO].
|
1029
|
+
*/
|
1030
|
+
static VALUE
|
1031
|
+
strio_ungetbyte(VALUE self, VALUE c)
|
1032
|
+
{
|
1033
|
+
struct StringIO *ptr = readable(self);
|
1034
|
+
|
1035
|
+
check_modifiable(ptr);
|
1036
|
+
if (NIL_P(ptr->string)) return Qnil;
|
1037
|
+
if (NIL_P(c)) return Qnil;
|
1038
|
+
if (RB_INTEGER_TYPE_P(c)) {
|
1039
|
+
/* rb_int_and() not visible from exts */
|
1040
|
+
VALUE v = rb_funcall(c, '&', 1, INT2FIX(0xff));
|
1041
|
+
const char cc = NUM2INT(v) & 0xFF;
|
1042
|
+
strio_unget_bytes(ptr, &cc, 1);
|
1043
|
+
}
|
1044
|
+
else {
|
1045
|
+
StringValue(c);
|
1046
|
+
strio_unget_string(ptr, c);
|
1047
|
+
}
|
1048
|
+
return Qnil;
|
1049
|
+
}
|
1050
|
+
|
1051
|
+
static VALUE
|
1052
|
+
strio_unget_bytes(struct StringIO *ptr, const char *cp, long cl)
|
1053
|
+
{
|
1054
|
+
long pos = ptr->pos, len, rest;
|
1055
|
+
VALUE str = ptr->string;
|
1056
|
+
char *s;
|
1057
|
+
|
1058
|
+
len = RSTRING_LEN(str);
|
1059
|
+
rest = pos - len;
|
1060
|
+
if (cl > pos) {
|
1061
|
+
long ex = cl - (rest < 0 ? pos : len);
|
1062
|
+
rb_str_modify_expand(str, ex);
|
1063
|
+
rb_str_set_len(str, len + ex);
|
1064
|
+
s = RSTRING_PTR(str);
|
1065
|
+
if (rest < 0) memmove(s + cl, s + pos, -rest);
|
1066
|
+
pos = 0;
|
1067
|
+
}
|
1068
|
+
else {
|
1069
|
+
if (rest > 0) {
|
1070
|
+
rb_str_modify_expand(str, rest);
|
1071
|
+
rb_str_set_len(str, len + rest);
|
1072
|
+
}
|
1073
|
+
s = RSTRING_PTR(str);
|
1074
|
+
if (rest > cl) memset(s + len, 0, rest - cl);
|
1075
|
+
pos -= cl;
|
1076
|
+
}
|
1077
|
+
memcpy(s + pos, (cp ? cp : s), cl);
|
1078
|
+
ptr->pos = pos;
|
1079
|
+
return Qnil;
|
1080
|
+
}
|
1081
|
+
|
1082
|
+
/*
|
1083
|
+
* call-seq:
|
1084
|
+
* readchar -> string
|
1085
|
+
*
|
1086
|
+
* Like +getc+, but raises an exception if already at end-of-stream;
|
1087
|
+
* see {Character IO}[rdoc-ref:IO@Character+IO].
|
1088
|
+
*/
|
1089
|
+
static VALUE
|
1090
|
+
strio_readchar(VALUE self)
|
1091
|
+
{
|
1092
|
+
VALUE c = rb_funcallv(self, rb_intern("getc"), 0, 0);
|
1093
|
+
if (NIL_P(c)) rb_eof_error();
|
1094
|
+
return c;
|
1095
|
+
}
|
1096
|
+
|
1097
|
+
/*
|
1098
|
+
* call-seq:
|
1099
|
+
* readbyte -> byte
|
1100
|
+
*
|
1101
|
+
* Like +getbyte+, but raises an exception if already at end-of-stream;
|
1102
|
+
* see {Byte IO}[rdoc-ref:IO@Byte+IO].
|
1103
|
+
*/
|
1104
|
+
static VALUE
|
1105
|
+
strio_readbyte(VALUE self)
|
1106
|
+
{
|
1107
|
+
VALUE c = rb_funcallv(self, rb_intern("getbyte"), 0, 0);
|
1108
|
+
if (NIL_P(c)) rb_eof_error();
|
1109
|
+
return c;
|
1110
|
+
}
|
1111
|
+
|
1112
|
+
/*
|
1113
|
+
* call-seq:
|
1114
|
+
* each_char {|c| ... } -> self
|
1115
|
+
*
|
1116
|
+
* With a block given, calls the block with each remaining character in the stream;
|
1117
|
+
* see {Character IO}[rdoc-ref:IO@Character+IO].
|
1118
|
+
*
|
1119
|
+
* With no block given, returns an enumerator.
|
1120
|
+
*/
|
1121
|
+
static VALUE
|
1122
|
+
strio_each_char(VALUE self)
|
1123
|
+
{
|
1124
|
+
VALUE c;
|
1125
|
+
|
1126
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
1127
|
+
|
1128
|
+
while (!NIL_P(c = strio_getc(self))) {
|
1129
|
+
rb_yield(c);
|
1130
|
+
}
|
1131
|
+
return self;
|
1132
|
+
}
|
1133
|
+
|
1134
|
+
/*
|
1135
|
+
* call-seq:
|
1136
|
+
* each_codepoint {|codepoint| ... } -> self
|
1137
|
+
*
|
1138
|
+
* With a block given, calls the block with each remaining codepoint in the stream;
|
1139
|
+
* see {Codepoint IO}[rdoc-ref:IO@Codepoint+IO].
|
1140
|
+
*
|
1141
|
+
* With no block given, returns an enumerator.
|
1142
|
+
*/
|
1143
|
+
static VALUE
|
1144
|
+
strio_each_codepoint(VALUE self)
|
1145
|
+
{
|
1146
|
+
struct StringIO *ptr;
|
1147
|
+
rb_encoding *enc;
|
1148
|
+
unsigned int c;
|
1149
|
+
int n;
|
1150
|
+
|
1151
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
1152
|
+
|
1153
|
+
ptr = readable(self);
|
1154
|
+
enc = get_enc(ptr);
|
1155
|
+
while ((ptr = strio_to_read(self)) != NULL) {
|
1156
|
+
c = rb_enc_codepoint_len(RSTRING_PTR(ptr->string)+ptr->pos,
|
1157
|
+
RSTRING_END(ptr->string), &n, enc);
|
1158
|
+
ptr->pos += n;
|
1159
|
+
rb_yield(UINT2NUM(c));
|
1160
|
+
}
|
1161
|
+
return self;
|
1162
|
+
}
|
1163
|
+
|
1164
|
+
/* Boyer-Moore search: copied from regex.c */
|
1165
|
+
static void
|
1166
|
+
bm_init_skip(long *skip, const char *pat, long m)
|
1167
|
+
{
|
1168
|
+
int c;
|
1169
|
+
|
1170
|
+
for (c = 0; c < (1 << CHAR_BIT); c++) {
|
1171
|
+
skip[c] = m;
|
1172
|
+
}
|
1173
|
+
while (--m) {
|
1174
|
+
skip[(unsigned char)*pat++] = m;
|
1175
|
+
}
|
1176
|
+
}
|
1177
|
+
|
1178
|
+
static long
|
1179
|
+
bm_search(const char *little, long llen, const char *big, long blen, const long *skip)
|
1180
|
+
{
|
1181
|
+
long i, j, k;
|
1182
|
+
|
1183
|
+
i = llen - 1;
|
1184
|
+
while (i < blen) {
|
1185
|
+
k = i;
|
1186
|
+
j = llen - 1;
|
1187
|
+
while (j >= 0 && big[k] == little[j]) {
|
1188
|
+
k--;
|
1189
|
+
j--;
|
1190
|
+
}
|
1191
|
+
if (j < 0) return k + 1;
|
1192
|
+
i += skip[(unsigned char)big[i]];
|
1193
|
+
}
|
1194
|
+
return -1;
|
1195
|
+
}
|
1196
|
+
|
1197
|
+
struct getline_arg {
|
1198
|
+
VALUE rs;
|
1199
|
+
long limit;
|
1200
|
+
unsigned int chomp: 1;
|
1201
|
+
};
|
1202
|
+
|
1203
|
+
static struct getline_arg *
|
1204
|
+
prepare_getline_args(struct StringIO *ptr, struct getline_arg *arg, int argc, VALUE *argv)
|
1205
|
+
{
|
1206
|
+
VALUE rs, lim, opts;
|
1207
|
+
long limit = -1;
|
1208
|
+
int respect_chomp;
|
1209
|
+
|
1210
|
+
argc = rb_scan_args(argc, argv, "02:", &rs, &lim, &opts);
|
1211
|
+
respect_chomp = argc == 0 || !NIL_P(rs);
|
1212
|
+
switch (argc) {
|
1213
|
+
case 0:
|
1214
|
+
rs = rb_rs;
|
1215
|
+
break;
|
1216
|
+
|
1217
|
+
case 1:
|
1218
|
+
if (!NIL_P(rs) && !RB_TYPE_P(rs, T_STRING)) {
|
1219
|
+
VALUE tmp = rb_check_string_type(rs);
|
1220
|
+
if (NIL_P(tmp)) {
|
1221
|
+
limit = NUM2LONG(rs);
|
1222
|
+
rs = rb_rs;
|
1223
|
+
}
|
1224
|
+
else {
|
1225
|
+
rs = tmp;
|
1226
|
+
}
|
1227
|
+
}
|
1228
|
+
break;
|
1229
|
+
|
1230
|
+
case 2:
|
1231
|
+
if (!NIL_P(rs)) StringValue(rs);
|
1232
|
+
if (!NIL_P(lim)) limit = NUM2LONG(lim);
|
1233
|
+
break;
|
1234
|
+
}
|
1235
|
+
if (!NIL_P(ptr->string) && !NIL_P(rs)) {
|
1236
|
+
rb_encoding *enc_rs, *enc_io;
|
1237
|
+
enc_rs = rb_enc_get(rs);
|
1238
|
+
enc_io = get_enc(ptr);
|
1239
|
+
if (enc_rs != enc_io &&
|
1240
|
+
(rb_enc_str_coderange(rs) != ENC_CODERANGE_7BIT ||
|
1241
|
+
(RSTRING_LEN(rs) > 0 && !rb_enc_asciicompat(enc_io)))) {
|
1242
|
+
if (rs == rb_rs) {
|
1243
|
+
rs = rb_enc_str_new(0, 0, enc_io);
|
1244
|
+
rb_str_buf_cat_ascii(rs, "\n");
|
1245
|
+
rs = rs;
|
1246
|
+
}
|
1247
|
+
else {
|
1248
|
+
rb_raise(rb_eArgError, "encoding mismatch: %s IO with %s RS",
|
1249
|
+
rb_enc_name(enc_io),
|
1250
|
+
rb_enc_name(enc_rs));
|
1251
|
+
}
|
1252
|
+
}
|
1253
|
+
}
|
1254
|
+
arg->rs = rs;
|
1255
|
+
arg->limit = limit;
|
1256
|
+
arg->chomp = 0;
|
1257
|
+
if (!NIL_P(opts)) {
|
1258
|
+
static ID keywords[1];
|
1259
|
+
VALUE vchomp;
|
1260
|
+
if (!keywords[0]) {
|
1261
|
+
keywords[0] = rb_intern_const("chomp");
|
1262
|
+
}
|
1263
|
+
rb_get_kwargs(opts, keywords, 0, 1, &vchomp);
|
1264
|
+
if (respect_chomp) {
|
1265
|
+
arg->chomp = (vchomp != Qundef) && RTEST(vchomp);
|
1266
|
+
}
|
1267
|
+
}
|
1268
|
+
return arg;
|
1269
|
+
}
|
1270
|
+
|
1271
|
+
static inline int
|
1272
|
+
chomp_newline_width(const char *s, const char *e)
|
1273
|
+
{
|
1274
|
+
if (e > s && *--e == '\n') {
|
1275
|
+
if (e > s && *--e == '\r') return 2;
|
1276
|
+
return 1;
|
1277
|
+
}
|
1278
|
+
return 0;
|
1279
|
+
}
|
1280
|
+
|
1281
|
+
static VALUE
|
1282
|
+
strio_getline(struct getline_arg *arg, struct StringIO *ptr)
|
1283
|
+
{
|
1284
|
+
const char *s, *e, *p;
|
1285
|
+
long n, limit = arg->limit;
|
1286
|
+
VALUE str = arg->rs;
|
1287
|
+
long w = 0;
|
1288
|
+
rb_encoding *enc = get_enc(ptr);
|
1289
|
+
|
1290
|
+
if (NIL_P(ptr->string) || ptr->pos >= (n = RSTRING_LEN(ptr->string))) {
|
1291
|
+
return Qnil;
|
1292
|
+
}
|
1293
|
+
s = RSTRING_PTR(ptr->string);
|
1294
|
+
e = s + RSTRING_LEN(ptr->string);
|
1295
|
+
s += ptr->pos;
|
1296
|
+
if (limit > 0 && (size_t)limit < (size_t)(e - s)) {
|
1297
|
+
e = rb_enc_right_char_head(s, s + limit, e, get_enc(ptr));
|
1298
|
+
}
|
1299
|
+
if (NIL_P(str)) {
|
1300
|
+
if (arg->chomp) {
|
1301
|
+
w = chomp_newline_width(s, e);
|
1302
|
+
}
|
1303
|
+
str = strio_substr(ptr, ptr->pos, e - s - w, enc);
|
1304
|
+
}
|
1305
|
+
else if ((n = RSTRING_LEN(str)) == 0) {
|
1306
|
+
const char *paragraph_end = NULL;
|
1307
|
+
p = s;
|
1308
|
+
while (p[(p + 1 < e) && (*p == '\r') && 0] == '\n') {
|
1309
|
+
p += *p == '\r';
|
1310
|
+
if (++p == e) {
|
1311
|
+
return Qnil;
|
1312
|
+
}
|
1313
|
+
}
|
1314
|
+
s = p;
|
1315
|
+
while ((p = memchr(p, '\n', e - p)) && (p != e)) {
|
1316
|
+
p++;
|
1317
|
+
if (!((p < e && *p == '\n') ||
|
1318
|
+
(p + 1 < e && *p == '\r' && *(p+1) == '\n'))) {
|
1319
|
+
continue;
|
1320
|
+
}
|
1321
|
+
paragraph_end = p - ((*(p-2) == '\r') ? 2 : 1);
|
1322
|
+
while ((p < e && *p == '\n') ||
|
1323
|
+
(p + 1 < e && *p == '\r' && *(p+1) == '\n')) {
|
1324
|
+
p += (*p == '\r') ? 2 : 1;
|
1325
|
+
}
|
1326
|
+
e = p;
|
1327
|
+
break;
|
1328
|
+
}
|
1329
|
+
if (arg->chomp && paragraph_end) {
|
1330
|
+
w = e - paragraph_end;
|
1331
|
+
}
|
1332
|
+
str = strio_substr(ptr, s - RSTRING_PTR(ptr->string), e - s - w, enc);
|
1333
|
+
}
|
1334
|
+
else if (n == 1) {
|
1335
|
+
if ((p = memchr(s, RSTRING_PTR(str)[0], e - s)) != 0) {
|
1336
|
+
e = p + 1;
|
1337
|
+
w = (arg->chomp ? (p > s && *(p-1) == '\r') + 1 : 0);
|
1338
|
+
}
|
1339
|
+
str = strio_substr(ptr, ptr->pos, e - s - w, enc);
|
1340
|
+
}
|
1341
|
+
else {
|
1342
|
+
if (n < e - s + arg->chomp) {
|
1343
|
+
/* unless chomping, RS at the end does not matter */
|
1344
|
+
if (e - s < 1024 || n == e - s) {
|
1345
|
+
for (p = s; p + n <= e; ++p) {
|
1346
|
+
if (MEMCMP(p, RSTRING_PTR(str), char, n) == 0) {
|
1347
|
+
e = p + n;
|
1348
|
+
w = (arg->chomp ? n : 0);
|
1349
|
+
break;
|
1350
|
+
}
|
1351
|
+
}
|
1352
|
+
}
|
1353
|
+
else {
|
1354
|
+
long skip[1 << CHAR_BIT], pos;
|
1355
|
+
p = RSTRING_PTR(str);
|
1356
|
+
bm_init_skip(skip, p, n);
|
1357
|
+
if ((pos = bm_search(p, n, s, e - s, skip)) >= 0) {
|
1358
|
+
e = s + pos + (arg->chomp ? 0 : n);
|
1359
|
+
}
|
1360
|
+
}
|
1361
|
+
}
|
1362
|
+
str = strio_substr(ptr, ptr->pos, e - s - w, enc);
|
1363
|
+
}
|
1364
|
+
ptr->pos = e - RSTRING_PTR(ptr->string);
|
1365
|
+
ptr->lineno++;
|
1366
|
+
return str;
|
1367
|
+
}
|
1368
|
+
|
1369
|
+
/*
|
1370
|
+
* call-seq:
|
1371
|
+
* gets(sep = $/, chomp: false) -> string or nil
|
1372
|
+
* gets(limit, chomp: false) -> string or nil
|
1373
|
+
* gets(sep, limit, chomp: false) -> string or nil
|
1374
|
+
*
|
1375
|
+
* Reads and returns a line from the stream;
|
1376
|
+
* assigns the return value to <tt>$_</tt>;
|
1377
|
+
* see {Line IO}[rdoc-ref:IO@Line+IO].
|
1378
|
+
*/
|
1379
|
+
static VALUE
|
1380
|
+
strio_gets(int argc, VALUE *argv, VALUE self)
|
1381
|
+
{
|
1382
|
+
struct StringIO *ptr = readable(self);
|
1383
|
+
struct getline_arg arg;
|
1384
|
+
VALUE str;
|
1385
|
+
|
1386
|
+
if (prepare_getline_args(ptr, &arg, argc, argv)->limit == 0) {
|
1387
|
+
if (NIL_P(ptr->string)) return Qnil;
|
1388
|
+
return rb_enc_str_new(0, 0, get_enc(ptr));
|
1389
|
+
}
|
1390
|
+
|
1391
|
+
str = strio_getline(&arg, ptr);
|
1392
|
+
rb_lastline_set(str);
|
1393
|
+
return str;
|
1394
|
+
}
|
1395
|
+
|
1396
|
+
/*
|
1397
|
+
* call-seq:
|
1398
|
+
* readline(sep = $/, chomp: false) -> string
|
1399
|
+
* readline(limit, chomp: false) -> string
|
1400
|
+
* readline(sep, limit, chomp: false) -> string
|
1401
|
+
*
|
1402
|
+
* Reads a line as with IO#gets, but raises EOFError if already at end-of-file;
|
1403
|
+
* see {Line IO}[rdoc-ref:IO@Line+IO].
|
1404
|
+
*/
|
1405
|
+
static VALUE
|
1406
|
+
strio_readline(int argc, VALUE *argv, VALUE self)
|
1407
|
+
{
|
1408
|
+
VALUE line = rb_funcallv_kw(self, rb_intern("gets"), argc, argv, RB_PASS_CALLED_KEYWORDS);
|
1409
|
+
if (NIL_P(line)) rb_eof_error();
|
1410
|
+
return line;
|
1411
|
+
}
|
1412
|
+
|
1413
|
+
/*
|
1414
|
+
* call-seq:
|
1415
|
+
* each_line(sep = $/, chomp: false) {|line| ... } -> self
|
1416
|
+
* each_line(limit, chomp: false) {|line| ... } -> self
|
1417
|
+
* each_line(sep, limit, chomp: false) {|line| ... } -> self
|
1418
|
+
*
|
1419
|
+
* Calls the block with each remaining line read from the stream;
|
1420
|
+
* does nothing if already at end-of-file;
|
1421
|
+
* returns +self+.
|
1422
|
+
* See {Line IO}[rdoc-ref:IO@Line+IO].
|
1423
|
+
*/
|
1424
|
+
static VALUE
|
1425
|
+
strio_each(int argc, VALUE *argv, VALUE self)
|
1426
|
+
{
|
1427
|
+
VALUE line;
|
1428
|
+
struct StringIO *ptr = readable(self);
|
1429
|
+
struct getline_arg arg;
|
1430
|
+
|
1431
|
+
RETURN_ENUMERATOR(self, argc, argv);
|
1432
|
+
|
1433
|
+
if (prepare_getline_args(ptr, &arg, argc, argv)->limit == 0) {
|
1434
|
+
rb_raise(rb_eArgError, "invalid limit: 0 for each_line");
|
1435
|
+
}
|
1436
|
+
|
1437
|
+
while (!NIL_P(line = strio_getline(&arg, ptr))) {
|
1438
|
+
rb_yield(line);
|
1439
|
+
}
|
1440
|
+
return self;
|
1441
|
+
}
|
1442
|
+
|
1443
|
+
/*
|
1444
|
+
* call-seq:
|
1445
|
+
* strio.readlines(sep=$/, chomp: false) -> array
|
1446
|
+
* strio.readlines(limit, chomp: false) -> array
|
1447
|
+
* strio.readlines(sep, limit, chomp: false) -> array
|
1448
|
+
*
|
1449
|
+
* See IO#readlines.
|
1450
|
+
*/
|
1451
|
+
static VALUE
|
1452
|
+
strio_readlines(int argc, VALUE *argv, VALUE self)
|
1453
|
+
{
|
1454
|
+
VALUE ary, line;
|
1455
|
+
struct StringIO *ptr = readable(self);
|
1456
|
+
struct getline_arg arg;
|
1457
|
+
|
1458
|
+
if (prepare_getline_args(ptr, &arg, argc, argv)->limit == 0) {
|
1459
|
+
rb_raise(rb_eArgError, "invalid limit: 0 for readlines");
|
1460
|
+
}
|
1461
|
+
|
1462
|
+
ary = rb_ary_new();
|
1463
|
+
while (!NIL_P(line = strio_getline(&arg, ptr))) {
|
1464
|
+
rb_ary_push(ary, line);
|
1465
|
+
}
|
1466
|
+
return ary;
|
1467
|
+
}
|
1468
|
+
|
1469
|
+
/*
|
1470
|
+
* call-seq:
|
1471
|
+
* strio.write(string, ...) -> integer
|
1472
|
+
* strio.syswrite(string) -> integer
|
1473
|
+
*
|
1474
|
+
* Appends the given string to the underlying buffer string.
|
1475
|
+
* The stream must be opened for writing. If the argument is not a
|
1476
|
+
* string, it will be converted to a string using <code>to_s</code>.
|
1477
|
+
* Returns the number of bytes written. See IO#write.
|
1478
|
+
*/
|
1479
|
+
static VALUE
|
1480
|
+
strio_write_m(int argc, VALUE *argv, VALUE self)
|
1481
|
+
{
|
1482
|
+
long len = 0;
|
1483
|
+
while (argc-- > 0) {
|
1484
|
+
/* StringIO can't exceed long limit */
|
1485
|
+
len += strio_write(self, *argv++);
|
1486
|
+
}
|
1487
|
+
return LONG2NUM(len);
|
1488
|
+
}
|
1489
|
+
|
1490
|
+
static long
|
1491
|
+
strio_write(VALUE self, VALUE str)
|
1492
|
+
{
|
1493
|
+
struct StringIO *ptr = writable(self);
|
1494
|
+
long len, olen;
|
1495
|
+
rb_encoding *enc, *enc2;
|
1496
|
+
rb_encoding *const ascii8bit = rb_ascii8bit_encoding();
|
1497
|
+
rb_encoding *usascii = 0;
|
1498
|
+
|
1499
|
+
if (!RB_TYPE_P(str, T_STRING))
|
1500
|
+
str = rb_obj_as_string(str);
|
1501
|
+
enc = get_enc(ptr);
|
1502
|
+
if (!enc) return 0;
|
1503
|
+
enc2 = rb_enc_get(str);
|
1504
|
+
if (enc != enc2 && enc != ascii8bit && enc != (usascii = rb_usascii_encoding())) {
|
1505
|
+
VALUE converted = rb_str_conv_enc(str, enc2, enc);
|
1506
|
+
if (converted == str && enc2 != ascii8bit && enc2 != usascii) { /* conversion failed */
|
1507
|
+
rb_enc_check(rb_enc_from_encoding(enc), str);
|
1508
|
+
}
|
1509
|
+
str = converted;
|
1510
|
+
}
|
1511
|
+
len = RSTRING_LEN(str);
|
1512
|
+
if (len == 0) return 0;
|
1513
|
+
check_modifiable(ptr);
|
1514
|
+
olen = RSTRING_LEN(ptr->string);
|
1515
|
+
if (ptr->flags & FMODE_APPEND) {
|
1516
|
+
ptr->pos = olen;
|
1517
|
+
}
|
1518
|
+
if (ptr->pos == olen) {
|
1519
|
+
if (enc == ascii8bit || enc2 == ascii8bit) {
|
1520
|
+
rb_enc_str_buf_cat(ptr->string, RSTRING_PTR(str), len, enc);
|
1521
|
+
}
|
1522
|
+
else {
|
1523
|
+
rb_str_buf_append(ptr->string, str);
|
1524
|
+
}
|
1525
|
+
}
|
1526
|
+
else {
|
1527
|
+
strio_extend(ptr, ptr->pos, len);
|
1528
|
+
rb_str_modify(ptr->string);
|
1529
|
+
memmove(RSTRING_PTR(ptr->string)+ptr->pos, RSTRING_PTR(str), len);
|
1530
|
+
}
|
1531
|
+
RB_GC_GUARD(str);
|
1532
|
+
ptr->pos += len;
|
1533
|
+
return len;
|
1534
|
+
}
|
1535
|
+
|
1536
|
+
/*
|
1537
|
+
* call-seq:
|
1538
|
+
* strio << obj -> strio
|
1539
|
+
*
|
1540
|
+
* See IO#<<.
|
1541
|
+
*/
|
1542
|
+
#define strio_addstr rb_io_addstr
|
1543
|
+
|
1544
|
+
/*
|
1545
|
+
* call-seq:
|
1546
|
+
* strio.print() -> nil
|
1547
|
+
* strio.print(obj, ...) -> nil
|
1548
|
+
*
|
1549
|
+
* See IO#print.
|
1550
|
+
*/
|
1551
|
+
#define strio_print rb_io_print
|
1552
|
+
|
1553
|
+
/*
|
1554
|
+
* call-seq:
|
1555
|
+
* strio.printf(format_string [, obj, ...] ) -> nil
|
1556
|
+
*
|
1557
|
+
* See IO#printf.
|
1558
|
+
*/
|
1559
|
+
#define strio_printf rb_io_printf
|
1560
|
+
|
1561
|
+
/*
|
1562
|
+
* call-seq:
|
1563
|
+
* strio.putc(obj) -> obj
|
1564
|
+
*
|
1565
|
+
* See IO#putc.
|
1566
|
+
*/
|
1567
|
+
static VALUE
|
1568
|
+
strio_putc(VALUE self, VALUE ch)
|
1569
|
+
{
|
1570
|
+
struct StringIO *ptr = writable(self);
|
1571
|
+
VALUE str;
|
1572
|
+
|
1573
|
+
check_modifiable(ptr);
|
1574
|
+
if (RB_TYPE_P(ch, T_STRING)) {
|
1575
|
+
if (NIL_P(ptr->string)) return ch;
|
1576
|
+
str = rb_str_substr(ch, 0, 1);
|
1577
|
+
}
|
1578
|
+
else {
|
1579
|
+
char c = NUM2CHR(ch);
|
1580
|
+
if (NIL_P(ptr->string)) return ch;
|
1581
|
+
str = rb_str_new(&c, 1);
|
1582
|
+
}
|
1583
|
+
strio_write(self, str);
|
1584
|
+
return ch;
|
1585
|
+
}
|
1586
|
+
|
1587
|
+
/*
|
1588
|
+
* call-seq:
|
1589
|
+
* strio.puts(obj, ...) -> nil
|
1590
|
+
*
|
1591
|
+
* See IO#puts.
|
1592
|
+
*/
|
1593
|
+
#define strio_puts rb_io_puts
|
1594
|
+
|
1595
|
+
/*
|
1596
|
+
* call-seq:
|
1597
|
+
* strio.read([length [, outbuf]]) -> string, outbuf, or nil
|
1598
|
+
*
|
1599
|
+
* See IO#read.
|
1600
|
+
*/
|
1601
|
+
static VALUE
|
1602
|
+
strio_read(int argc, VALUE *argv, VALUE self)
|
1603
|
+
{
|
1604
|
+
struct StringIO *ptr = readable(self);
|
1605
|
+
VALUE str = Qnil;
|
1606
|
+
long len;
|
1607
|
+
int binary = 0;
|
1608
|
+
|
1609
|
+
switch (argc) {
|
1610
|
+
case 2:
|
1611
|
+
str = argv[1];
|
1612
|
+
if (!NIL_P(str)) {
|
1613
|
+
StringValue(str);
|
1614
|
+
rb_str_modify(str);
|
1615
|
+
}
|
1616
|
+
/* fall through */
|
1617
|
+
case 1:
|
1618
|
+
if (!NIL_P(argv[0])) {
|
1619
|
+
len = NUM2LONG(argv[0]);
|
1620
|
+
if (len < 0) {
|
1621
|
+
rb_raise(rb_eArgError, "negative length %ld given", len);
|
1622
|
+
}
|
1623
|
+
if (eos_p(ptr)) {
|
1624
|
+
if (!NIL_P(str)) rb_str_resize(str, 0);
|
1625
|
+
return len > 0 ? Qnil : rb_str_new(0, 0);
|
1626
|
+
}
|
1627
|
+
binary = 1;
|
1628
|
+
break;
|
1629
|
+
}
|
1630
|
+
/* fall through */
|
1631
|
+
case 0:
|
1632
|
+
if (NIL_P(ptr->string)) return Qnil;
|
1633
|
+
len = RSTRING_LEN(ptr->string);
|
1634
|
+
if (len <= ptr->pos) {
|
1635
|
+
rb_encoding *enc = get_enc(ptr);
|
1636
|
+
if (NIL_P(str)) {
|
1637
|
+
str = rb_str_new(0, 0);
|
1638
|
+
}
|
1639
|
+
else {
|
1640
|
+
rb_str_resize(str, 0);
|
1641
|
+
}
|
1642
|
+
rb_enc_associate(str, enc);
|
1643
|
+
return str;
|
1644
|
+
}
|
1645
|
+
else {
|
1646
|
+
len -= ptr->pos;
|
1647
|
+
}
|
1648
|
+
break;
|
1649
|
+
default:
|
1650
|
+
rb_error_arity(argc, 0, 2);
|
1651
|
+
}
|
1652
|
+
if (NIL_P(str)) {
|
1653
|
+
rb_encoding *enc = binary ? rb_ascii8bit_encoding() : get_enc(ptr);
|
1654
|
+
str = strio_substr(ptr, ptr->pos, len, enc);
|
1655
|
+
}
|
1656
|
+
else {
|
1657
|
+
long rest = RSTRING_LEN(ptr->string) - ptr->pos;
|
1658
|
+
if (len > rest) len = rest;
|
1659
|
+
rb_str_resize(str, len);
|
1660
|
+
MEMCPY(RSTRING_PTR(str), RSTRING_PTR(ptr->string) + ptr->pos, char, len);
|
1661
|
+
if (!binary) {
|
1662
|
+
rb_enc_copy(str, ptr->string);
|
1663
|
+
}
|
1664
|
+
}
|
1665
|
+
ptr->pos += RSTRING_LEN(str);
|
1666
|
+
return str;
|
1667
|
+
}
|
1668
|
+
|
1669
|
+
/*
|
1670
|
+
* call-seq:
|
1671
|
+
* pread(maxlen, offset) -> string
|
1672
|
+
* pread(maxlen, offset, out_string) -> string
|
1673
|
+
*
|
1674
|
+
* See IO#pread.
|
1675
|
+
*/
|
1676
|
+
static VALUE
|
1677
|
+
strio_pread(int argc, VALUE *argv, VALUE self)
|
1678
|
+
{
|
1679
|
+
VALUE rb_len, rb_offset, rb_buf;
|
1680
|
+
rb_scan_args(argc, argv, "21", &rb_len, &rb_offset, &rb_buf);
|
1681
|
+
long len = NUM2LONG(rb_len);
|
1682
|
+
long offset = NUM2LONG(rb_offset);
|
1683
|
+
|
1684
|
+
if (len < 0) {
|
1685
|
+
rb_raise(rb_eArgError, "negative string size (or size too big): %" PRIsVALUE, rb_len);
|
1686
|
+
}
|
1687
|
+
|
1688
|
+
if (len == 0) {
|
1689
|
+
if (NIL_P(rb_buf)) {
|
1690
|
+
return rb_str_new("", 0);
|
1691
|
+
}
|
1692
|
+
return rb_buf;
|
1693
|
+
}
|
1694
|
+
|
1695
|
+
if (offset < 0) {
|
1696
|
+
rb_syserr_fail_str(EINVAL, rb_sprintf("pread: Invalid offset argument: %" PRIsVALUE, rb_offset));
|
1697
|
+
}
|
1698
|
+
|
1699
|
+
struct StringIO *ptr = readable(self);
|
1700
|
+
|
1701
|
+
if (outside_p(ptr, offset)) {
|
1702
|
+
rb_eof_error();
|
1703
|
+
}
|
1704
|
+
|
1705
|
+
if (NIL_P(rb_buf)) {
|
1706
|
+
return strio_substr(ptr, offset, len, rb_ascii8bit_encoding());
|
1707
|
+
}
|
1708
|
+
|
1709
|
+
long rest = RSTRING_LEN(ptr->string) - offset;
|
1710
|
+
if (len > rest) len = rest;
|
1711
|
+
rb_str_resize(rb_buf, len);
|
1712
|
+
rb_enc_associate(rb_buf, rb_ascii8bit_encoding());
|
1713
|
+
MEMCPY(RSTRING_PTR(rb_buf), RSTRING_PTR(ptr->string) + offset, char, len);
|
1714
|
+
return rb_buf;
|
1715
|
+
}
|
1716
|
+
|
1717
|
+
|
1718
|
+
/*
|
1719
|
+
* call-seq:
|
1720
|
+
* strio.sysread(integer[, outbuf]) -> string
|
1721
|
+
* strio.readpartial(integer[, outbuf]) -> string
|
1722
|
+
*
|
1723
|
+
* Similar to #read, but raises +EOFError+ at end of string instead of
|
1724
|
+
* returning +nil+, as well as IO#sysread does.
|
1725
|
+
*/
|
1726
|
+
static VALUE
|
1727
|
+
strio_sysread(int argc, VALUE *argv, VALUE self)
|
1728
|
+
{
|
1729
|
+
VALUE val = rb_funcallv_kw(self, rb_intern("read"), argc, argv, RB_PASS_CALLED_KEYWORDS);
|
1730
|
+
if (NIL_P(val)) {
|
1731
|
+
rb_eof_error();
|
1732
|
+
}
|
1733
|
+
return val;
|
1734
|
+
}
|
1735
|
+
|
1736
|
+
/*
|
1737
|
+
* call-seq:
|
1738
|
+
* strio.read_nonblock(integer[, outbuf [, opts]]) -> string
|
1739
|
+
*
|
1740
|
+
* Similar to #read, but raises +EOFError+ at end of string unless the
|
1741
|
+
* +exception: false+ option is passed in.
|
1742
|
+
*/
|
1743
|
+
static VALUE
|
1744
|
+
strio_read_nonblock(int argc, VALUE *argv, VALUE self)
|
1745
|
+
{
|
1746
|
+
VALUE opts = Qnil, val;
|
1747
|
+
|
1748
|
+
rb_scan_args(argc, argv, "11:", NULL, NULL, &opts);
|
1749
|
+
|
1750
|
+
if (!NIL_P(opts)) {
|
1751
|
+
argc--;
|
1752
|
+
}
|
1753
|
+
|
1754
|
+
val = strio_read(argc, argv, self);
|
1755
|
+
if (NIL_P(val)) {
|
1756
|
+
if (!NIL_P(opts) &&
|
1757
|
+
rb_hash_lookup2(opts, sym_exception, Qundef) == Qfalse)
|
1758
|
+
return Qnil;
|
1759
|
+
else
|
1760
|
+
rb_eof_error();
|
1761
|
+
}
|
1762
|
+
|
1763
|
+
return val;
|
1764
|
+
}
|
1765
|
+
|
1766
|
+
/*
|
1767
|
+
* See IO#write
|
1768
|
+
*/
|
1769
|
+
#define strio_syswrite rb_io_write
|
1770
|
+
|
1771
|
+
/*
|
1772
|
+
* See IO#write_nonblock
|
1773
|
+
*/
|
1774
|
+
static VALUE
|
1775
|
+
strio_syswrite_nonblock(int argc, VALUE *argv, VALUE self)
|
1776
|
+
{
|
1777
|
+
VALUE str;
|
1778
|
+
|
1779
|
+
rb_scan_args(argc, argv, "10:", &str, NULL);
|
1780
|
+
return strio_syswrite(self, str);
|
1781
|
+
}
|
1782
|
+
|
1783
|
+
#define strio_isatty strio_false
|
1784
|
+
|
1785
|
+
#define strio_pid strio_nil
|
1786
|
+
|
1787
|
+
#define strio_fileno strio_nil
|
1788
|
+
|
1789
|
+
/*
|
1790
|
+
* call-seq:
|
1791
|
+
* strio.length -> integer
|
1792
|
+
* strio.size -> integer
|
1793
|
+
*
|
1794
|
+
* Returns the size of the buffer string.
|
1795
|
+
*/
|
1796
|
+
static VALUE
|
1797
|
+
strio_size(VALUE self)
|
1798
|
+
{
|
1799
|
+
VALUE string = StringIO(self)->string;
|
1800
|
+
if (NIL_P(string)) {
|
1801
|
+
return INT2FIX(0);
|
1802
|
+
}
|
1803
|
+
return ULONG2NUM(RSTRING_LEN(string));
|
1804
|
+
}
|
1805
|
+
|
1806
|
+
/*
|
1807
|
+
* call-seq:
|
1808
|
+
* strio.truncate(integer) -> 0
|
1809
|
+
*
|
1810
|
+
* Truncates the buffer string to at most _integer_ bytes. The stream
|
1811
|
+
* must be opened for writing.
|
1812
|
+
*/
|
1813
|
+
static VALUE
|
1814
|
+
strio_truncate(VALUE self, VALUE len)
|
1815
|
+
{
|
1816
|
+
VALUE string = writable(self)->string;
|
1817
|
+
long l = NUM2LONG(len);
|
1818
|
+
long plen;
|
1819
|
+
if (l < 0) {
|
1820
|
+
error_inval("negative length");
|
1821
|
+
}
|
1822
|
+
if (NIL_P(string)) return 0;
|
1823
|
+
plen = RSTRING_LEN(string);
|
1824
|
+
rb_str_resize(string, l);
|
1825
|
+
if (plen < l) {
|
1826
|
+
MEMZERO(RSTRING_PTR(string) + plen, char, l - plen);
|
1827
|
+
}
|
1828
|
+
return INT2FIX(0);
|
1829
|
+
}
|
1830
|
+
|
1831
|
+
/*
|
1832
|
+
* call-seq:
|
1833
|
+
* strio.external_encoding => encoding
|
1834
|
+
*
|
1835
|
+
* Returns the Encoding object that represents the encoding of the file.
|
1836
|
+
* If the stream is write mode and no encoding is specified, returns
|
1837
|
+
* +nil+.
|
1838
|
+
*/
|
1839
|
+
|
1840
|
+
static VALUE
|
1841
|
+
strio_external_encoding(VALUE self)
|
1842
|
+
{
|
1843
|
+
struct StringIO *ptr = StringIO(self);
|
1844
|
+
return rb_enc_from_encoding(get_enc(ptr));
|
1845
|
+
}
|
1846
|
+
|
1847
|
+
/*
|
1848
|
+
* call-seq:
|
1849
|
+
* strio.internal_encoding => encoding
|
1850
|
+
*
|
1851
|
+
* Returns the Encoding of the internal string if conversion is
|
1852
|
+
* specified. Otherwise returns +nil+.
|
1853
|
+
*/
|
1854
|
+
|
1855
|
+
static VALUE
|
1856
|
+
strio_internal_encoding(VALUE self)
|
1857
|
+
{
|
1858
|
+
return Qnil;
|
1859
|
+
}
|
1860
|
+
|
1861
|
+
/*
|
1862
|
+
* call-seq:
|
1863
|
+
* strio.set_encoding(ext_enc, [int_enc[, opt]]) => strio
|
1864
|
+
*
|
1865
|
+
* Specify the encoding of the StringIO as <i>ext_enc</i>.
|
1866
|
+
* Use the default external encoding if <i>ext_enc</i> is nil.
|
1867
|
+
* 2nd argument <i>int_enc</i> and optional hash <i>opt</i> argument
|
1868
|
+
* are ignored; they are for API compatibility to IO.
|
1869
|
+
*/
|
1870
|
+
|
1871
|
+
static VALUE
|
1872
|
+
strio_set_encoding(int argc, VALUE *argv, VALUE self)
|
1873
|
+
{
|
1874
|
+
rb_encoding* enc;
|
1875
|
+
struct StringIO *ptr = StringIO(self);
|
1876
|
+
VALUE ext_enc, int_enc, opt;
|
1877
|
+
|
1878
|
+
argc = rb_scan_args(argc, argv, "11:", &ext_enc, &int_enc, &opt);
|
1879
|
+
|
1880
|
+
if (NIL_P(ext_enc)) {
|
1881
|
+
enc = rb_default_external_encoding();
|
1882
|
+
}
|
1883
|
+
else {
|
1884
|
+
enc = rb_find_encoding(ext_enc);
|
1885
|
+
if (!enc) {
|
1886
|
+
rb_io_enc_t convconfig;
|
1887
|
+
int oflags;
|
1888
|
+
rb_io_mode_t fmode;
|
1889
|
+
VALUE vmode = rb_str_append(rb_str_new_cstr("r:"), ext_enc);
|
1890
|
+
rb_io_extract_modeenc(&vmode, 0, Qnil, &oflags, &fmode, &convconfig);
|
1891
|
+
enc = convconfig.enc2;
|
1892
|
+
}
|
1893
|
+
}
|
1894
|
+
ptr->enc = enc;
|
1895
|
+
if (!NIL_P(ptr->string) && WRITABLE(self) && !str_chilled_p(ptr->string)) {
|
1896
|
+
rb_enc_associate(ptr->string, enc);
|
1897
|
+
}
|
1898
|
+
|
1899
|
+
return self;
|
1900
|
+
}
|
1901
|
+
|
1902
|
+
/*
|
1903
|
+
* call-seq:
|
1904
|
+
* strio.set_encoding_by_bom => strio or nil
|
1905
|
+
*
|
1906
|
+
* Sets the encoding according to the BOM (Byte Order Mark) in the
|
1907
|
+
* string.
|
1908
|
+
*
|
1909
|
+
* Returns +self+ if the BOM is found, otherwise +nil.
|
1910
|
+
*/
|
1911
|
+
static VALUE
|
1912
|
+
strio_set_encoding_by_bom(VALUE self)
|
1913
|
+
{
|
1914
|
+
struct StringIO *ptr = StringIO(self);
|
1915
|
+
|
1916
|
+
if (!set_encoding_by_bom(ptr)) return Qnil;
|
1917
|
+
return rb_enc_from_encoding(ptr->enc);
|
1918
|
+
}
|
1919
|
+
|
1920
|
+
/*
|
1921
|
+
* \IO streams for strings, with access similar to
|
1922
|
+
* {IO}[rdoc-ref:IO];
|
1923
|
+
* see {IO}[rdoc-ref:IO].
|
1924
|
+
*
|
1925
|
+
* === About the Examples
|
1926
|
+
*
|
1927
|
+
* Examples on this page assume that \StringIO has been required:
|
1928
|
+
*
|
1929
|
+
* require 'stringio'
|
1930
|
+
*
|
1931
|
+
*/
|
1932
|
+
void
|
1933
|
+
Init_stringio(void)
|
1934
|
+
{
|
1935
|
+
#undef rb_intern
|
1936
|
+
|
1937
|
+
#ifdef HAVE_RB_EXT_RACTOR_SAFE
|
1938
|
+
rb_ext_ractor_safe(true);
|
1939
|
+
#endif
|
1940
|
+
|
1941
|
+
VALUE StringIO = rb_define_class("StringIO", rb_cObject);
|
1942
|
+
|
1943
|
+
/* The version string */
|
1944
|
+
rb_define_const(StringIO, "VERSION", rb_str_new_cstr(STRINGIO_VERSION));
|
1945
|
+
|
1946
|
+
rb_include_module(StringIO, rb_mEnumerable);
|
1947
|
+
rb_define_alloc_func(StringIO, strio_s_allocate);
|
1948
|
+
|
1949
|
+
/* Maximum length that a StringIO instance can hold */
|
1950
|
+
rb_define_const(StringIO, "MAX_LENGTH", LONG2NUM(LONG_MAX));
|
1951
|
+
|
1952
|
+
rb_define_singleton_method(StringIO, "new", strio_s_new, -1);
|
1953
|
+
rb_define_singleton_method(StringIO, "open", strio_s_open, -1);
|
1954
|
+
rb_define_method(StringIO, "initialize", strio_initialize, -1);
|
1955
|
+
rb_define_method(StringIO, "initialize_copy", strio_copy, 1);
|
1956
|
+
rb_define_method(StringIO, "reopen", strio_reopen, -1);
|
1957
|
+
|
1958
|
+
rb_define_method(StringIO, "string", strio_get_string, 0);
|
1959
|
+
rb_define_method(StringIO, "string=", strio_set_string, 1);
|
1960
|
+
rb_define_method(StringIO, "lineno", strio_get_lineno, 0);
|
1961
|
+
rb_define_method(StringIO, "lineno=", strio_set_lineno, 1);
|
1962
|
+
|
1963
|
+
|
1964
|
+
/* call-seq: strio.binmode -> true */
|
1965
|
+
rb_define_method(StringIO, "binmode", strio_binmode, 0);
|
1966
|
+
rb_define_method(StringIO, "close", strio_close, 0);
|
1967
|
+
rb_define_method(StringIO, "close_read", strio_close_read, 0);
|
1968
|
+
rb_define_method(StringIO, "close_write", strio_close_write, 0);
|
1969
|
+
rb_define_method(StringIO, "closed?", strio_closed, 0);
|
1970
|
+
rb_define_method(StringIO, "closed_read?", strio_closed_read, 0);
|
1971
|
+
rb_define_method(StringIO, "closed_write?", strio_closed_write, 0);
|
1972
|
+
rb_define_method(StringIO, "eof", strio_eof, 0);
|
1973
|
+
rb_define_method(StringIO, "eof?", strio_eof, 0);
|
1974
|
+
/* call-seq: strio.fcntl */
|
1975
|
+
rb_define_method(StringIO, "fcntl", strio_fcntl, -1);
|
1976
|
+
/* call-seq: strio.flush -> strio */
|
1977
|
+
rb_define_method(StringIO, "flush", strio_flush, 0);
|
1978
|
+
/* call-seq: strio.fsync -> 0 */
|
1979
|
+
rb_define_method(StringIO, "fsync", strio_fsync, 0);
|
1980
|
+
rb_define_method(StringIO, "pos", strio_get_pos, 0);
|
1981
|
+
rb_define_method(StringIO, "pos=", strio_set_pos, 1);
|
1982
|
+
rb_define_method(StringIO, "rewind", strio_rewind, 0);
|
1983
|
+
rb_define_method(StringIO, "seek", strio_seek, -1);
|
1984
|
+
rb_define_method(StringIO, "sync", strio_get_sync, 0);
|
1985
|
+
/* call-seq: strio.sync = boolean -> boolean */
|
1986
|
+
rb_define_method(StringIO, "sync=", strio_set_sync, 1);
|
1987
|
+
rb_define_method(StringIO, "tell", strio_tell, 0);
|
1988
|
+
|
1989
|
+
rb_define_method(StringIO, "each", strio_each, -1);
|
1990
|
+
rb_define_method(StringIO, "each_line", strio_each, -1);
|
1991
|
+
rb_define_method(StringIO, "each_byte", strio_each_byte, 0);
|
1992
|
+
rb_define_method(StringIO, "each_char", strio_each_char, 0);
|
1993
|
+
rb_define_method(StringIO, "each_codepoint", strio_each_codepoint, 0);
|
1994
|
+
rb_define_method(StringIO, "getc", strio_getc, 0);
|
1995
|
+
rb_define_method(StringIO, "ungetc", strio_ungetc, 1);
|
1996
|
+
rb_define_method(StringIO, "ungetbyte", strio_ungetbyte, 1);
|
1997
|
+
rb_define_method(StringIO, "getbyte", strio_getbyte, 0);
|
1998
|
+
rb_define_method(StringIO, "gets", strio_gets, -1);
|
1999
|
+
rb_define_method(StringIO, "readlines", strio_readlines, -1);
|
2000
|
+
rb_define_method(StringIO, "read", strio_read, -1);
|
2001
|
+
rb_define_method(StringIO, "pread", strio_pread, -1);
|
2002
|
+
|
2003
|
+
rb_define_method(StringIO, "write", strio_write_m, -1);
|
2004
|
+
rb_define_method(StringIO, "putc", strio_putc, 1);
|
2005
|
+
|
2006
|
+
/*
|
2007
|
+
* call-seq:
|
2008
|
+
* strio.isatty -> nil
|
2009
|
+
* strio.tty? -> nil
|
2010
|
+
*
|
2011
|
+
*/
|
2012
|
+
rb_define_method(StringIO, "isatty", strio_isatty, 0);
|
2013
|
+
rb_define_method(StringIO, "tty?", strio_isatty, 0);
|
2014
|
+
|
2015
|
+
/* call-seq: strio.pid -> nil */
|
2016
|
+
rb_define_method(StringIO, "pid", strio_pid, 0);
|
2017
|
+
|
2018
|
+
/* call-seq: strio.fileno -> nil */
|
2019
|
+
rb_define_method(StringIO, "fileno", strio_fileno, 0);
|
2020
|
+
rb_define_method(StringIO, "size", strio_size, 0);
|
2021
|
+
rb_define_method(StringIO, "length", strio_size, 0);
|
2022
|
+
rb_define_method(StringIO, "truncate", strio_truncate, 1);
|
2023
|
+
|
2024
|
+
rb_define_method(StringIO, "external_encoding", strio_external_encoding, 0);
|
2025
|
+
rb_define_method(StringIO, "internal_encoding", strio_internal_encoding, 0);
|
2026
|
+
rb_define_method(StringIO, "set_encoding", strio_set_encoding, -1);
|
2027
|
+
rb_define_method(StringIO, "set_encoding_by_bom", strio_set_encoding_by_bom, 0);
|
2028
|
+
|
2029
|
+
{
|
2030
|
+
VALUE mReadable = rb_define_module_under(rb_cIO, "generic_readable");
|
2031
|
+
rb_define_method(mReadable, "readchar", strio_readchar, 0);
|
2032
|
+
rb_define_method(mReadable, "readbyte", strio_readbyte, 0);
|
2033
|
+
rb_define_method(mReadable, "readline", strio_readline, -1);
|
2034
|
+
rb_define_method(mReadable, "sysread", strio_sysread, -1);
|
2035
|
+
rb_define_method(mReadable, "readpartial", strio_sysread, -1);
|
2036
|
+
rb_define_method(mReadable, "read_nonblock", strio_read_nonblock, -1);
|
2037
|
+
rb_include_module(StringIO, mReadable);
|
2038
|
+
}
|
2039
|
+
{
|
2040
|
+
VALUE mWritable = rb_define_module_under(rb_cIO, "generic_writable");
|
2041
|
+
rb_define_method(mWritable, "<<", strio_addstr, 1);
|
2042
|
+
rb_define_method(mWritable, "print", strio_print, -1);
|
2043
|
+
rb_define_method(mWritable, "printf", strio_printf, -1);
|
2044
|
+
rb_define_method(mWritable, "puts", strio_puts, -1);
|
2045
|
+
rb_define_method(mWritable, "syswrite", strio_syswrite, 1);
|
2046
|
+
rb_define_method(mWritable, "write_nonblock", strio_syswrite_nonblock, -1);
|
2047
|
+
rb_include_module(StringIO, mWritable);
|
2048
|
+
}
|
2049
|
+
|
2050
|
+
sym_exception = ID2SYM(rb_intern("exception"));
|
2051
|
+
}
|