Project

General

Profile

Bug #5714 ยป read_binmode_fix_r34024.patch

h.shirosaki (Hiroshi Shirosaki), 12/13/2011 04:44 PM

View differences:

include/ruby/win32.h
304 304
extern int rb_w32_ustati64(const char *, struct stati64 *);
305 305
extern int rb_w32_access(const char *, int);
306 306
extern int rb_w32_uaccess(const char *, int);
307
extern char rb_w32_fd_is_text(int);
307 308

  
308 309
#ifdef __BORLANDC__
309 310
extern int rb_w32_fstati64(int, struct stati64 *);
io.c
374 374
#  endif
375 375
#endif
376 376

  
377
static int io_fflush(rb_io_t *);
378

  
377 379
#define NEED_NEWLINE_DECORATOR_ON_READ(fptr) ((fptr)->mode & FMODE_TEXTMODE)
378 380
#define NEED_NEWLINE_DECORATOR_ON_WRITE(fptr) ((fptr)->mode & FMODE_TEXTMODE)
379 381
#if defined(RUBY_TEST_CRLF_ENVIRONMENT) || defined(_WIN32)
......
413 415
 * but stdin and pipe cannot seek back. Stdin and pipe read should use encoding
414 416
 * conversion for working properly with mode change.
415 417
 */
416
#define SET_BINARY_MODE_WITH_SEEK_CUR(fptr) do {\
417
    if ((fptr)->rbuf.len > 0 && !((fptr)->mode & FMODE_DUPLEX)) {\
418
	off_t r;\
419
	errno = 0;\
420
	r = io_seek((fptr), -(fptr)->rbuf.len, SEEK_CUR);\
421
	if (r < 0 && errno) {\
422
	    if (errno == ESPIPE)\
423
		(fptr)->mode |= FMODE_DUPLEX;\
424
	}\
425
	else {\
426
	    (fptr)->rbuf.off = 0;\
427
	    (fptr)->rbuf.len = 0;\
428
	}\
429
    }\
430
    setmode((fptr)->fd, O_BINARY);\
431
} while(0)
418
inline static void set_binary_mode_with_seek_cur(rb_io_t *fptr) {
419
    off_t r, pos;
420
    ssize_t read_size;
421
    long i;
422
    long newlines = 0;
423
    char *p;
424

  
425
    if (!rb_w32_fd_is_text((fptr)->fd)) return;
426

  
427
    if ((fptr)->rbuf.len == 0 || (fptr)->mode & FMODE_DUPLEX) {
428
	setmode((fptr)->fd, O_BINARY);
429
	return;
430
    }
431

  
432
    if (io_fflush(fptr) < 0) {
433
	rb_sys_fail(0);
434
    }
435
    errno = 0;
436
    pos = lseek((fptr)->fd, 0, SEEK_CUR);
437
    if (pos < 0 && errno) {
438
	if (errno == ESPIPE)
439
	    (fptr)->mode |= FMODE_DUPLEX;
440
	setmode((fptr)->fd, O_BINARY);
441
	return;
442
    }
443
    /* add extra offset for '\r' */
444
    p = (fptr)->rbuf.ptr+(fptr)->rbuf.off;
445
    for (i = 0; i < (fptr)->rbuf.len; i++) {
446
	if (*p == '\n') newlines++;
447
	p++;
448
    }
449
    while (newlines >= 0) {
450
	r = lseek((fptr)->fd, pos - (fptr)->rbuf.len - newlines, SEEK_SET);
451
	if (newlines == 0) break;
452
	if (read_size = _read((fptr)->fd, (fptr)->rbuf.ptr, (fptr)->rbuf.len + newlines)) {
453
	    if (read_size == (fptr)->rbuf.len) {
454
		lseek((fptr)->fd, r, SEEK_SET);
455
		break;
456
	    }
457
	    else {
458
		newlines--;
459
	    }
460
	}
461
    }
462
    (fptr)->rbuf.off = 0;
463
    (fptr)->rbuf.len = 0;
464
    setmode((fptr)->fd, O_BINARY);
465
}
466
#define SET_BINARY_MODE_WITH_SEEK_CUR(fptr) set_binary_mode_with_seek_cur(fptr)
432 467

  
433 468
#else
434 469
/* Unix */
......
494 529
    }
495 530
}
496 531

  
497
static int io_fflush(rb_io_t *);
498 532

  
499 533
VALUE
500 534
rb_io_get_io(VALUE io)
......
1142 1176
	    !(fptr->encs.ecflags & ECONV_NEWLINE_DECORATOR_MASK)) {
1143 1177
	    setmode(fptr->fd, O_BINARY);
1144 1178
	}
1179
	else {
1180
	    setmode(fptr->fd, O_TEXT);
1181
	}
1145 1182
	if (!rb_enc_asciicompat(rb_enc_get(str))) {
1146 1183
	    rb_raise(rb_eArgError, "ASCII incompatible string written for text mode IO without encoding conversion: %s",
1147 1184
	    rb_enc_name(rb_enc_get(str)));
......
2482 2519
    if (len == 0) return str;
2483 2520

  
2484 2521
    READ_CHECK(fptr);
2522
    SET_BINARY_MODE_WITH_SEEK_CUR(fptr);
2485 2523
    n = io_fread(str, 0, fptr);
2486 2524
    if (n == 0) {
2487 2525
	if (fptr->fd < 0) return Qnil;
test/ruby/test_io_m17n.rb
2222 2222
      end
2223 2223
    end
2224 2224
  end if /mswin|mingw/ =~ RUBY_PLATFORM
2225

  
2226
  def test_read_with_length_binmode
2227
    with_tmpdir {
2228
      str = "a\r\nb\r\nc\r\n\r\n"
2229
      generate_file("tmp", str)
2230
      open("tmp", "r") do |f|
2231
        # read with length should be binary mode
2232
        assert_equal("a\r\n", f.read(3)) # binary
2233
        assert_equal("b\nc\n\n", f.read) # text
2234
      end
2235
    }
2236
  end if /mswin|mingw/ =~ RUBY_PLATFORM
2237

  
2238
  def test_gets_and_read_with_binmode
2239
    with_tmpdir {
2240
      str = "a\r\nb\r\nc\n\r\n"
2241
      generate_file("tmp", str)
2242
      open("tmp", "r") do |f|
2243
        assert_equal("a\n", f.gets)      # text
2244
        assert_equal("b\r\n", f.read(3)) # binary
2245
        assert_equal("c\n\n", f.read)    # text
2246
      end
2247
    }
2248
  end if /mswin|mingw/ =~ RUBY_PLATFORM
2249

  
2250
  def test_getc_and_read_with_binmode
2251
    with_tmpdir {
2252
      str = "a\r\nb\r\nc\n\n\r\n\r\n"
2253
      generate_file("tmp", str)
2254
      open("tmp", "r") do |f|
2255
        assert_equal("a", f.getc)         # text
2256
        assert_equal("\n", f.getc)        # text
2257
        assert_equal("b\r\n", f.read(3))  # binary
2258
        assert_equal("c\n\n\n\n", f.read) # text
2259
      end
2260
    }
2261
  end if /mswin|mingw/ =~ RUBY_PLATFORM
2262

  
2263
  def test_read_write_with_binmode
2264
    with_tmpdir {
2265
      str = "a\r\n"
2266
      generate_file("tmp", str)
2267
      open("tmp", "r+") do |f|
2268
        assert_equal("a\r\n", f.read(3)) # binary
2269
        f.write("b\n\n");                # text
2270
        f.rewind
2271
        assert_equal("a\nb\n\n", f.read) # text
2272
        f.rewind
2273
        assert_equal("a\r\nb\r\n\r\n", f.binmode.read) # binary
2274
      end
2275
    }
2276
  end if /mswin|mingw/ =~ RUBY_PLATFORM
2225 2277
end
win32/win32.c
2259 2259
    if (fileno(stdin) < 0) {
2260 2260
	stdin->_file = open_null(0);
2261 2261
    }
2262
    else {
2263
	setmode(fileno(stdin), O_BINARY);
2264
    }
2262 2265
    if (fileno(stdout) < 0) {
2263 2266
	stdout->_file = open_null(1);
2264 2267
    }
......
6137 6140
    }
6138 6141
    return numaddr;
6139 6142
}
6143

  
6144
char
6145
rb_w32_fd_is_text(int fd) {
6146
    return _osfile(fd) & FTEXT;
6147
}