diff options
author | Mark H Weaver <mhw@netris.org> | 2013-04-14 02:48:33 -0400 |
---|---|---|
committer | Mark H Weaver <mhw@netris.org> | 2013-04-14 02:48:33 -0400 |
commit | f6f4feb0a2222efcb297e634603621126542e63f (patch) | |
tree | 0b590c025f688ab625827c4f692fa7783716b558 /libguile/filesys.c | |
parent | 1e051065628a7f1bd4398fcc11cd181f86084629 (diff) | |
parent | f5b2eea6a39507ecf6a8ecc62cc1c796c45c2d1d (diff) | |
download | guile-f6f4feb0a2222efcb297e634603621126542e63f.tar.gz |
Merge remote-tracking branch 'origin/stable-2.0'
Conflicts:
GUILE-VERSION
libguile/array-map.c
libguile/fports.h
libguile/gc.h
libguile/inline.h
libguile/ports.c
libguile/ports.h
libguile/print.c
libguile/r6rs-ports.c
libguile/read.c
test-suite/tests/00-socket.test
Diffstat (limited to 'libguile/filesys.c')
-rw-r--r-- | libguile/filesys.c | 61 |
1 files changed, 44 insertions, 17 deletions
diff --git a/libguile/filesys.c b/libguile/filesys.c index 09a0de0e9..1893c02cd 100644 --- a/libguile/filesys.c +++ b/libguile/filesys.c @@ -1111,9 +1111,10 @@ SCM_DEFINE (scm_copy_file, "copy-file", 2, 0, 0, SCM_DEFINE (scm_sendfile, "sendfile", 3, 1, 0, (SCM out, SCM in, SCM count, SCM offset), "Send @var{count} bytes from @var{in} to @var{out}, both of which " - "are either open file ports or file descriptors. When " + "must be either open file ports or file descriptors. When " "@var{offset} is omitted, start reading from @var{in}'s current " - "position; otherwise, start reading at @var{offset}.") + "position; otherwise, start reading at @var{offset}. Return " + "the number of bytes actually sent.") #define FUNC_NAME s_scm_sendfile { #define VALIDATE_FD_OR_PORT(cvar, svar, pos) \ @@ -1126,9 +1127,9 @@ SCM_DEFINE (scm_sendfile, "sendfile", 3, 1, 0, cvar = SCM_FPORT_FDES (svar); \ } - size_t c_count; + ssize_t result SCM_UNUSED; + size_t c_count, total = 0; scm_t_off c_offset; - ssize_t result; int in_fd, out_fd; VALIDATE_FD_OR_PORT (out_fd, out, 1); @@ -1139,9 +1140,30 @@ SCM_DEFINE (scm_sendfile, "sendfile", 3, 1, 0, #if defined HAVE_SYS_SENDFILE_H && defined HAVE_SENDFILE /* The Linux-style sendfile(2), which is different from the BSD-style. */ - result = sendfile_or_sendfile64 (out_fd, in_fd, - SCM_UNBNDP (offset) ? NULL : &c_offset, - c_count); + { + off_t *offset_ptr; + + offset_ptr = SCM_UNBNDP (offset) ? NULL : &c_offset; + + /* On Linux, when OUT_FD is a file, everything is transferred at once and + RESULT == C_COUNT. However, when OUT_FD is a pipe or other "slow" + device, fewer bytes may be transferred, hence the loop. RESULT == 0 + means EOF on IN_FD, so leave the loop in that case. */ + do + { + result = sendfile_or_sendfile64 (out_fd, in_fd, offset_ptr, + c_count - total); + if (result > 0) + /* At this point, either OFFSET_PTR is non-NULL and it has been + updated to the current offset in IN_FD, or it is NULL and IN_FD's + offset has been updated. */ + total += result; + else if (result < 0 && (errno == EINTR || errno == EAGAIN)) + /* Keep going. */ + result = 1; + } + while (total < c_count && result > 0); + } /* Quoting the Linux man page: "In Linux kernels before 2.6.33, out_fd must refer to a socket. Since Linux 2.6.33 it can be any file." @@ -1152,12 +1174,13 @@ SCM_DEFINE (scm_sendfile, "sendfile", 3, 1, 0, #endif { char buf[8192]; - size_t result, left; + size_t left; + int reached_eof = 0; if (!SCM_UNBNDP (offset)) { if (SCM_PORTP (in)) - scm_seek (in, offset, scm_from_int (SEEK_SET)); + scm_seek (in, scm_from_off_t (c_offset), scm_from_int (SEEK_SET)); else { if (lseek_or_lseek64 (in_fd, c_offset, SEEK_SET) < 0) @@ -1165,28 +1188,32 @@ SCM_DEFINE (scm_sendfile, "sendfile", 3, 1, 0, } } - for (result = 0, left = c_count; result < c_count; ) + for (total = 0, left = c_count; total < c_count && !reached_eof; ) { - size_t asked, obtained; + size_t asked, obtained, written; asked = SCM_MIN (sizeof buf, left); obtained = full_read (in_fd, buf, asked); if (obtained < asked) - SCM_SYSERROR; + { + if (errno == 0) + reached_eof = 1; + else + SCM_SYSERROR; + } left -= obtained; - obtained = full_write (out_fd, buf, asked); - if (obtained < asked) + written = full_write (out_fd, buf, obtained); + if (written < obtained) SCM_SYSERROR; - result += obtained; + total += written; } - return scm_from_size_t (result); } - return scm_from_ssize_t (result); + return scm_from_size_t (total); #undef VALIDATE_FD_OR_PORT } |