diff --git a/pkgs/racket-pkgs/racket-test/tests/racket/port.rktl b/pkgs/racket-pkgs/racket-test/tests/racket/port.rktl index 1d69611d85..63849cc0cb 100644 --- a/pkgs/racket-pkgs/racket-test/tests/racket/port.rktl +++ b/pkgs/racket-pkgs/racket-test/tests/racket/port.rktl @@ -841,6 +841,48 @@ (test #f file-position* p2) (err/rt-test (file-position p2) exn:fail:filesystem?)) +;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Text mode, file positions, and buffers + +(let () + (define path (build-path (find-system-path 'temp-dir) "test.txt")) + + (define ofile (open-output-file path #:mode 'text #:exists 'replace)) + (fprintf ofile "abc\ndef\nghi\n") + (close-output-port ofile) + + (let () + (define ifile (open-input-file path #:mode 'text)) + (test "abc" read-line ifile) + (define pos (file-position ifile)) + (test "def" read-line ifile) + (file-position ifile pos) + (test "def" read-line ifile)) + + (let () + (define ifile (open-input-file path #:mode 'text)) + (file-stream-buffer-mode ifile 'none) + (test "abc" read-line ifile) + (define pos (file-position ifile)) + (test "def" read-line ifile) + (file-position ifile pos) + (test "def" read-line ifile)) + + (let* ([bs (call-with-input-file path + #:mode 'text + (lambda (p) (read-bytes (file-size path) p)))]) + ;; Check text-mode conversion at every boundary: + (for ([i (in-range (add1 (bytes-length bs)))]) + (define p (open-input-file path #:mode 'text)) + (file-stream-buffer-mode p 'none) + (define a (read-bytes i p)) + (define b (read-bytes (- (bytes-length bs) i) p)) + (test #t eof-object? (read-byte p)) + (close-input-port p) + (test bs bytes-append a b))) + + (delete-file path)) + ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (report-errs) diff --git a/racket/src/racket/include/scheme.h b/racket/src/racket/include/scheme.h index 3519cb2524..2590789410 100644 --- a/racket/src/racket/include/scheme.h +++ b/racket/src/racket/include/scheme.h @@ -1484,6 +1484,9 @@ struct Scheme_Input_Port Scheme_Object *special, *ungotten_special; Scheme_Object *unless, *unless_cache; struct Scheme_Output_Port *output_half; +#ifdef WINDOWS_FILE_HANDLES + char *bufwidths; /* to track CRLF => LF conversions in the buffer */ +#endif }; struct Scheme_Output_Port diff --git a/racket/src/racket/src/mzmarksrc.c b/racket/src/racket/src/mzmarksrc.c index e1d88289b9..e5ffe59825 100644 --- a/racket/src/racket/src/mzmarksrc.c +++ b/racket/src/racket/src/mzmarksrc.c @@ -646,6 +646,9 @@ input_port { gcMARK2(ip->input_extras_ready, gc); gcMARK2(ip->unless, gc); gcMARK2(ip->unless_cache, gc); +#ifdef WINDOWS_FILE_HANDLES + gcMARK2(ip->bufwidths, gc); +#endif size: gcBYTES_TO_WORDS(sizeof(Scheme_Input_Port)); diff --git a/racket/src/racket/src/port.c b/racket/src/racket/src/port.c index 25fa42c3ed..1ac388e602 100644 --- a/racket/src/racket/src/port.c +++ b/racket/src/racket/src/port.c @@ -313,7 +313,7 @@ int scheme_get_serialized_fd_flags(Scheme_Object* p, Scheme_Serialized_File_FD * so->name = ((Scheme_Output_Port *)p)->name; } so->regfile = fds->regfile; - so->textmode = fds->textmode; + so->textmode = (fds->textmode ? 1 : 0); so->flush_mode = fds->flush; return 1; } @@ -5463,6 +5463,22 @@ do_file_position(const char *who, int argc, Scheme_Object *argv[], int can_false Scheme_Input_Port *ip; ip = scheme_input_port_record(argv[0]); pll -= ((Scheme_FD *)ip->port_data)->bufcount; +# ifdef WINDOWS_FILE_HANDLES + if (((Scheme_FD *)ip->port_data)->textmode) { + int bp, bd; + bd = ((Scheme_FD *)ip->port_data)->buffpos; + for (bp = ((Scheme_FD *)ip->port_data)->bufcount; bp--; ) { + if (ip->bufwidths[bp + bd]) { + /* this is a LF converted from CRLF */ + pll--; + } + } + if (((Scheme_FD *)ip->port_data)->textmode > 1) { + /* one more for leftover CR */ + pll--; + } + } +# endif } else { Scheme_Output_Port *op; op = scheme_output_port_record(argv[0]); @@ -6699,7 +6715,7 @@ static intptr_t fd_get_string_slow(Scheme_Input_Port *port, rgot = target_size; /* Pending CR in text mode? */ - if (fip->textmode == 2) { + if (fip->textmode > 1) { delta = 1; if (rgot > 1) rgot--; @@ -6723,27 +6739,33 @@ static intptr_t fd_get_string_slow(Scheme_Input_Port *port, int i, j; unsigned char *buf; - if (fip->textmode == 2) { + if (fip->textmode > 1) { /* we had added a CR */ bc++; fip->textmode = 1; } - /* If bc is only 1, then we've reached the end, and + /* If bc is only 1 in buffer mode, then we've reached the end, and any leftover CR there should stay. */ - if (bc > 1) { + if ((bc > 1) || (bc && (target_size == 1))) { /* Collapse CR-LF: */ + char *bufwidths = port->bufwidths; /* for file-position */ buf = fip->buffer; for (i = 0, j = 0; i < bc - 1; i++) { if ((buf[i] == '\r') && (buf[i+1] == '\n')) { + bufwidths[j] = 1; buf[j++] = '\n'; i++; - } else + } else { + bufwidths[j] = 0; buf[j++] = buf[i]; + } } - if (i < bc) /* common case: didn't end with CRLF */ + if (i < bc) { /* common case: didn't end with CRLF */ + bufwidths[j] = 0; buf[j++] = buf[i]; + } bc = j; /* Check for CR at end; if there, save it to maybe get a LF on the next read: */ @@ -6817,8 +6839,14 @@ static intptr_t fd_get_string_slow(Scheme_Input_Port *port, } if (!fip->bufcount) { - fip->buffpos = 0; - return EOF; + if (fip->textmode > 1) { + /* have a CR pending, so maybe keep trying */ + if (nonblock > 0) + return 0; + } else { + fip->buffpos = 0; + return EOF; + } } else { bc = ((size <= fip->bufcount) ? size @@ -7023,7 +7051,7 @@ make_fd_input_port(intptr_t fd, Scheme_Object *name, int regfile, int win_textmo if (regfile || isatty(fd)) fip->textmode = 1; #else - fip->textmode = win_textmode; + fip->textmode = (win_textmode ? 1 : 0); #endif if (refcount) { @@ -7053,6 +7081,14 @@ make_fd_input_port(intptr_t fd, Scheme_Object *name, int regfile, int win_textmo ip->pending_eof = 1; /* means that pending EOFs should be tracked */ +#ifdef WINDOWS_FILE_HANDLES + if (fip->textmode) { + char *bw; + bw = (char *)scheme_malloc_atomic(MZPORT_FD_BUFFSIZE); + ip->bufwidths = bw; + } +#endif + #ifdef WINDOWS_FILE_HANDLES if (!regfile && !start_closed) { /* To get non-blocking I/O for anything that can block, we create