diff --git a/include/chibi/features.h b/include/chibi/features.h index 35730dca..ff9567d8 100644 --- a/include/chibi/features.h +++ b/include/chibi/features.h @@ -641,6 +641,10 @@ #define SEXP_USE_MAIN_HELP ! SEXP_USE_NO_FEATURES #endif +#ifndef SEXP_USE_SEND_FILE +#define SEXP_USE_SEND_FILE (!defined(_WIN32) && !defined(PLAN9)) +#endif + #if SEXP_USE_NATIVE_X86 #undef SEXP_USE_BOEHM #define SEXP_USE_BOEHM 1 diff --git a/lib/chibi/io.sld b/lib/chibi/io.sld index ba6e5a9a..feae0e1d 100644 --- a/lib/chibi/io.sld +++ b/lib/chibi/io.sld @@ -11,7 +11,8 @@ make-filtered-input-port string-count open-input-bytevector open-output-bytevector get-output-bytevector string->utf8 utf8->string - write-string write-u8 read-u8 peek-u8) + write-string write-u8 read-u8 peek-u8 send-file + is-a-socket?) (import (chibi) (chibi ast)) (include-shared "io/io") (include "io/io.scm")) diff --git a/lib/chibi/io/io.scm b/lib/chibi/io/io.scm index bc6cd6c6..a1bdfbd1 100644 --- a/lib/chibi/io/io.scm +++ b/lib/chibi/io/io.scm @@ -164,6 +164,27 @@ (port-line-set! in (+ (string-count #\newline str 0 n) (port-line in))) res)) +;;> Sends the entire contents of a file or input port to an output port. + +(define (send-file fd-port-or-filename . o) + (let* ((in (if (string? fd-port-or-filename) + (open-input-file fd-port-or-filename) + fd-port-or-filename)) + (out (if (pair? o) (car o) (current-output-port))) + (fd (if (port? in) (port-fileno in) in)) + (sock (if (port? out) (port-fileno out) out))) + (if (and fd sock (is-a-socket? sock)) + (let lp ((start 0)) + (let ((res (%send-file fd sock start))) + (cond + ((not res) (lp start)) + ((not (zero? res)) (lp (+ start res)))))) + (let lp () + (let ((str (read-string 8192 in))) + (cond ((not (eof-object? str)) + (display str out) + (lp)))))))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; higher order port operations diff --git a/lib/chibi/io/io.stub b/lib/chibi/io/io.stub index e5ed4f0e..04b239a1 100644 --- a/lib/chibi/io/io.stub +++ b/lib/chibi/io/io.stub @@ -20,6 +20,11 @@ (c-include "port.c") +(define-c boolean (is-a-socket? "sexp_is_a_socket_p") (fileno)) + +(define-c errno (%send-file "sexp_send_file") + (fileno fileno off_t (default 0 off_t) (result off_t))) + (define-c sexp (%make-custom-input-port "sexp_make_custom_input_port") ((value ctx sexp) (value self sexp) sexp sexp sexp)) diff --git a/lib/chibi/io/port.c b/lib/chibi/io/port.c index a1b5483b..7116588c 100644 --- a/lib/chibi/io/port.c +++ b/lib/chibi/io/port.c @@ -375,3 +375,23 @@ sexp sexp_peek_u8 (sexp ctx, sexp self, sexp in) { sexp_push_char(ctx, sexp_unbox_fixnum(res), in); return res; } + +int sexp_is_a_socket_p (int fd) { +#if defined(PLAN9) || defined(_WIN32) + return 0; +#else + struct stat statbuf; + fstat(fd, &statbuf); + return S_ISSOCK(statbuf.st_mode); +#endif +} + +int sexp_send_file (int fd, int s, off_t offset, off_t len, off_t* res) { +#if SEXP_USE_SEND_FILE + *res = len; + return sendfile(fd, s, offset, res, NULL, 0); +#else + *res = 0; + return 22; /* EINVAL */ +#endif +}