fix problems with text-mode ports on Windows

Closes PR 14198

Merge to v6.0
(cherry picked from commit 335177f1bc)
This commit is contained in:
Matthew Flatt 2013-11-25 08:53:20 -07:00 committed by Ryan Culpepper
parent d6cecaaaad
commit d13dd0b351
4 changed files with 94 additions and 10 deletions

View File

@ -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)

View File

@ -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

View File

@ -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));

View File

@ -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