Project

General

Profile

Bug #5714 ยป read_binmode_fix_r34035.patch

h.shirosaki (Hiroshi Shirosaki), 12/14/2011 05:04 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
/*
419
 * Return previous translation mode.
420
 */
421
inline static int set_binary_mode_with_seek_cur(rb_io_t *fptr) {
422
    off_t r, pos;
423
    ssize_t read_size;
424
    long i;
425
    long newlines = 0;
426
    char *p;
427

  
428
    if (!rb_w32_fd_is_text((fptr)->fd)) return O_BINARY;
429

  
430
    if ((fptr)->rbuf.len == 0 || (fptr)->mode & FMODE_DUPLEX) {
431
	setmode((fptr)->fd, O_BINARY);
432
	return O_TEXT;
433
    }
434

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

  
433 472
#else
434 473
/* Unix */
......
494 533
    }
495 534
}
496 535

  
497
static int io_fflush(rb_io_t *);
498 536

  
499 537
VALUE
500 538
rb_io_get_io(VALUE io)
......
1142 1180
	    !(fptr->encs.ecflags & ECONV_NEWLINE_DECORATOR_MASK)) {
1143 1181
	    setmode(fptr->fd, O_BINARY);
1144 1182
	}
1183
	else {
1184
	    setmode(fptr->fd, O_TEXT);
1185
	}
1145 1186
	if (!rb_enc_asciicompat(rb_enc_get(str))) {
1146 1187
	    rb_raise(rb_eArgError, "ASCII incompatible string written for text mode IO without encoding conversion: %s",
1147 1188
	    rb_enc_name(rb_enc_get(str)));
......
2462 2503
    rb_io_t *fptr;
2463 2504
    long n, len;
2464 2505
    VALUE length, str;
2506
#if defined(RUBY_TEST_CRLF_ENVIRONMENT) || defined(_WIN32)
2507
    int previous_mode;
2508
#endif
2465 2509

  
2466 2510
    rb_scan_args(argc, argv, "02", &length, &str);
2467 2511

  
......
2482 2526
    if (len == 0) return str;
2483 2527

  
2484 2528
    READ_CHECK(fptr);
2529
#if defined(RUBY_TEST_CRLF_ENVIRONMENT) || defined(_WIN32)
2530
    previous_mode = set_binary_mode_with_seek_cur(fptr);
2531
#endif
2485 2532
    n = io_fread(str, 0, fptr);
2533
#if defined(RUBY_TEST_CRLF_ENVIRONMENT) || defined(_WIN32)
2534
    if (previous_mode == O_TEXT) {
2535
	setmode(fptr->fd, O_TEXT);
2536
    }
2537
#endif
2486 2538
    if (n == 0) {
2487 2539
	if (fptr->fd < 0) return Qnil;
2488 2540
        rb_str_resize(str, 0);
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
2227
    with_tmpdir {
2228
      str = "a\nb"
2229
      generate_file("tmp", str)
2230
      open("tmp", "r") do |f|
2231
        assert_equal(str, f.read(3))
2232
      end
2233
    }
2234
  end if /mswin|mingw/ =~ RUBY_PLATFORM
2235

  
2236
  def test_read_with_length_binmode
2237
    with_tmpdir {
2238
      str = "a\r\nb\r\nc\r\n\r\n"
2239
      generate_file("tmp", str)
2240
      open("tmp", "r") do |f|
2241
        # read with length should be binary mode
2242
        assert_equal("a\r\n", f.read(3)) # binary
2243
        assert_equal("b\nc\n\n", f.read) # text
2244
      end
2245
    }
2246
  end if /mswin|mingw/ =~ RUBY_PLATFORM
2247

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

  
2261
  def test_getc_and_read_with_binmode
2262
    with_tmpdir {
2263
      str = "a\r\nb\r\nc\n\n\r\n\r\n"
2264
      generate_file("tmp", str)
2265
      open("tmp", "r") do |f|
2266
        assert_equal("a", f.getc)         # text
2267
        assert_equal("\n", f.getc)        # text
2268
        assert_equal("b\r\n", f.read(3))  # binary
2269
        assert_equal("c\n\n\n\n", f.read) # text
2270
      end
2271
    }
2272
  end if /mswin|mingw/ =~ RUBY_PLATFORM
2273

  
2274
  def test_read_with_binmode_and_gets
2275
    with_tmpdir {
2276
      str = "a\r\nb\r\nc\r\n\r\n"
2277
      open("tmp", "wb") { |f| f.write str }
2278
      open("tmp", "r") do |f|
2279
        assert_equal("a", f.getc)         # text
2280
        assert_equal("\n", f.getc)        # text
2281
        assert_equal("b\r\n", f.read(3))  # binary
2282
        assert_equal("c\n", f.gets)       # text
2283
        assert_equal("\n", f.gets)        # text
2284
      end
2285
    }
2286
  end
2287

  
2288
  def test_read_with_binmode_and_getc
2289
    with_tmpdir {
2290
      str = "a\r\nb\r\nc\r\n\r\n"
2291
      open("tmp", "wb") { |f| f.write str }
2292
      open("tmp", "r") do |f|
2293
        assert_equal("a", f.getc)         # text
2294
        assert_equal("\n", f.getc)        # text
2295
        assert_equal("b\r\n", f.read(3))  # binary
2296
        assert_equal("c", f.getc)         # text
2297
        assert_equal("\n", f.getc)        # text
2298
        assert_equal("\n", f.getc)        # text
2299
      end
2300
    }
2301
  end
2302

  
2303
  def test_read_write_with_binmode
2304
    with_tmpdir {
2305
      str = "a\r\n"
2306
      generate_file("tmp", str)
2307
      open("tmp", "r+") do |f|
2308
        assert_equal("a\r\n", f.read(3))  # binary
2309
        f.write("b\n\n");                 # text
2310
        f.rewind
2311
        assert_equal("a\nb\n\n", f.read)  # text
2312
        f.rewind
2313
        assert_equal("a\r\nb\r\n\r\n", f.binmode.read) # binary
2314
      end
2315
    }
2316
  end if /mswin|mingw/ =~ RUBY_PLATFORM
2225 2317
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
}