Project

General

Profile

Backport #6179 ยป pos_fix.patch

h.shirosaki (Hiroshi Shirosaki), 03/22/2012 04:49 AM

View differences:

io.c
377 377
#define rb_sys_fail_path(path) rb_sys_fail_str(path)
378 378

  
379 379
static int io_fflush(rb_io_t *);
380
static rb_io_t *flush_before_seek(rb_io_t *fptr);
380 381

  
381 382
#define NEED_NEWLINE_DECORATOR_ON_READ(fptr) ((fptr)->mode & FMODE_TEXTMODE)
382 383
#define NEED_NEWLINE_DECORATOR_ON_WRITE(fptr) ((fptr)->mode & FMODE_TEXTMODE)
......
412 413
	(ecflags) |= ECONV_UNIVERSAL_NEWLINE_DECORATOR;\
413 414
    }\
414 415
} while(0)
416

  
415 417
/*
416
 * We use io_seek to back cursor position when changing mode from text to binary,
417
 * but stdin and pipe cannot seek back. Stdin and pipe read should use encoding
418
 * conversion for working properly with mode change.
419
 */
420
/*
421
 * Return previous translation mode.
418
 *  IO unread with taking care of removed '\r' in text mode.
422 419
 */
423
static inline int
424
set_binary_mode_with_seek_cur(rb_io_t *fptr)
420
static void
421
io_unread(rb_io_t *fptr)
425 422
{
426 423
    off_t r, pos;
427 424
    ssize_t read_size;
......
429 426
    long newlines = 0;
430 427
    long extra_max;
431 428
    char *p;
429
    char *buf;
432 430

  
433
    if (!rb_w32_fd_is_text(fptr->fd)) return O_BINARY;
434

  
431
    rb_io_check_closed(fptr);
435 432
    if (fptr->rbuf.len == 0 || fptr->mode & FMODE_DUPLEX) {
436
	return setmode(fptr->fd, O_BINARY);
433
	return;
437 434
    }
438 435

  
439
    if (io_fflush(fptr) < 0) {
440
	rb_sys_fail(0);
441
    }
442 436
    errno = 0;
437
    if (!rb_w32_fd_is_text(fptr->fd)) {
438
	r = lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
439
	if (r < 0 && errno) {
440
	    if (errno == ESPIPE)
441
		fptr->mode |= FMODE_DUPLEX;
442
	    return;
443
	}
444

  
445
	fptr->rbuf.off = 0;
446
	fptr->rbuf.len = 0;
447
	return;
448
    }
449

  
443 450
    pos = lseek(fptr->fd, 0, SEEK_CUR);
444 451
    if (pos < 0 && errno) {
445 452
	if (errno == ESPIPE)
446 453
	    fptr->mode |= FMODE_DUPLEX;
447
	return setmode(fptr->fd, O_BINARY);
454
	return;
448 455
    }
456

  
449 457
    /* add extra offset for removed '\r' in rbuf */
450 458
    extra_max = pos - fptr->rbuf.len;
451 459
    p = fptr->rbuf.ptr + fptr->rbuf.off;
......
454 462
	if (extra_max == newlines) break;
455 463
	p++;
456 464
    }
465

  
466
    buf = ALLOC_N(char, fptr->rbuf.len + newlines);
457 467
    while (newlines >= 0) {
458 468
	r = lseek(fptr->fd, pos - fptr->rbuf.len - newlines, SEEK_SET);
459 469
	if (newlines == 0) break;
......
461 471
	    newlines--;
462 472
	    continue;
463 473
	}
464
	read_size = _read(fptr->fd, fptr->rbuf.ptr, fptr->rbuf.len + newlines);
474
	read_size = _read(fptr->fd, buf, fptr->rbuf.len + newlines);
465 475
	if (read_size < 0) {
466 476
	    rb_sys_fail_path(fptr->pathv);
467 477
	}
......
475 485
    }
476 486
    fptr->rbuf.off = 0;
477 487
    fptr->rbuf.len = 0;
488
    return;
489
}
490

  
491
/*
492
 * We use io_seek to back cursor position when changing mode from text to binary,
493
 * but stdin and pipe cannot seek back. Stdin and pipe read should use encoding
494
 * conversion for working properly with mode change.
495
 *
496
 * Return previous translation mode.
497
 */
498
static inline int
499
set_binary_mode_with_seek_cur(rb_io_t *fptr)
500
{
501
    if (!rb_w32_fd_is_text(fptr->fd)) return O_BINARY;
502

  
503
    if (fptr->rbuf.len == 0 || fptr->mode & FMODE_DUPLEX) {
504
	return setmode(fptr->fd, O_BINARY);
505
    }
506
    flush_before_seek(fptr);
478 507
    return setmode(fptr->fd, O_BINARY);
479 508
}
480 509
#define SET_BINARY_MODE_WITH_SEEK_CUR(fptr) set_binary_mode_with_seek_cur(fptr)
......
605 634
    return rb_io_check_io(io);
606 635
}
607 636

  
637
#if !(defined(RUBY_TEST_CRLF_ENVIRONMENT) || defined(_WIN32))
608 638
static void
609 639
io_unread(rb_io_t *fptr)
610 640
{
......
624 654
    fptr->rbuf.len = 0;
625 655
    return;
626 656
}
657
#endif
627 658

  
628 659
static rb_encoding *io_input_encoding(rb_io_t *fptr);
629 660

  
test/ruby/test_io_m17n.rb
2415 2415
    }
2416 2416
    assert_equal(paths.map(&:encoding), encs, bug6072)
2417 2417
  end
2418

  
2419
  def test_pos_dont_move_cursor_position
2420
    bug6179 = '[ruby-core:43497]'
2421
    with_tmpdir {
2422
      str = "line one\r\nline two\r\nline three\r\n"
2423
      generate_file("tmp", str)
2424
      open("tmp", "r") do |f|
2425
        assert_equal("line one\n", f.readline)
2426
        assert_equal(10, f.pos, bug6179)
2427
        assert_equal("line two\n", f.readline, bug6179)
2428
        assert_equal(20, f.pos, bug6179)
2429
        assert_equal("line three\n", f.readline, bug6179)
2430
      end
2431
    }
2432
  end if /mswin|mingw/ =~ RUBY_PLATFORM
2418 2433
end