Re: Random File Access in Scheme48?
Taylor R Campbell <[email protected]> Fri, 6 Aug 2010 21:13:07 +0000
| Newsgroups | gmane.lisp.scheme.scheme48 |
|---|---|
| Message-ID | <[email protected]> |
Date: Fri, 6 Aug 2010 15:01:06 -0400 From: Jake Bartlett <[email protected]> Is there any way to do random file access in Scheme48? I'm looking for something like C's fseek function. I didn't see anything in the manual. Nothing built-in. Here's a quick hack with the FFI. The tricky part is handling the port's buffer, which I don't think is possible to synchronize safely between multiple threads given what Scheme48 has, so don't use this hack with multiple threads operating on a single port. Unfortunately, on systems with 32-bit long and 64-bit off_t, this loses, because Scheme48 has no way to handle C integers wider than long. Lightly tested. /* seek.c */ /* Run `gcc -shared -o seek.so seek.c' or whatever. */ #include <errno.h> #include <scheme48.h> #include <unistd.h> static s48_value s48_channel_seek (s48_value fd_s48, s48_value offset_s48, s48_value command_s48) { int fd; off_t offset; int command; off_t result; s48_value result_s48 = S48_UNSPECIFIC; S48_DECLARE_GC_PROTECT (4); S48_GC_PROTECT_4 (fd_s48, offset_s48, command_s48, result_s48); fd = (s48_extract_integer (fd_s48)); offset = (s48_extract_integer (offset_s48)); switch (s48_extract_integer (command_s48)) { case 0: command = SEEK_SET; break; case 1: command = SEEK_CUR; break; case 2: command = SEEK_END; break; default: { s48_value min_s48, max_s48; S48_DECLARE_GC_PROTECT (2); S48_GC_PROTECT_2 (min_s48, max_s48); min_s48 = (s48_enter_integer (0)); max_s48 = (s48_enter_integer (2)); s48_raise_range_error (command_s48, min_s48, max_s48); /* Not reached. */ S48_GC_UNPROTECT (); goto out; } } while (0 > (result = (lseek (fd, offset, command)))) if (errno != EINTR) s48_raise_os_error (errno); result_s48 = (s48_enter_integer (result)); out: S48_GC_UNPROTECT (); return (result_s48); } void s48_on_load (void) { S48_EXPORT_FUNCTION (s48_channel_seek); } void s48_on_reload (void) { s48_on_load (); } ;;; seek.scm ; ,open channel-ports channels external-calls i/o ports ; ,open load-dynamic-externals ; (import-dynamic-externals "./seek.so") (import-lambda-definition s48-channel-seek (fd offset command)) (define (channel-position channel) (s48-channel-seek (channel-os-index channel) 0 1)) (define (set-channel-position! channel position) (s48-channel-seek (channel-os-index channel) position 0)) (define (advance-channel-position! channel position) (s48-channel-seek (channel-os-index channel) position 1)) (define (set-channel-position-from-end! channel position) (s48-channel-seek (channel-os-index channel) position 2)) (define (port-position port) (let ((channel (port->channel port))) (if (not channel) (error "Not a channel port:" port)) (+ (channel-position channel) (port-buffer-position-adjustment port)))) (define (port-buffer-position-adjustment port) (cond ((input-port? port) (if (port-buffer port) (- (port-index port) (port-limit port)) 0)) ((output-port? port) (if (port-limit port) (port-index port) 0)) (else (error "What is it, if not an input port or an output port?" port)))) (define (with-port-repositioning port procedure) (if (output-port? port) (force-output port)) (call-with-values procedure (lambda results (set-port-index! port 0) (set-port-limit! port 0) (apply values results)))) (define (set-port-position! port position) (let ((channel (port->channel port))) (if (not channel) (error "Not a channel port:" port)) (with-port-repositioning port (lambda () (set-channel-position! channel position))))) (define (advance-port-position! port position) (let ((channel (port->channel port))) (if (not channel) (error "Not a channel port:" port)) (with-port-repositioning port (lambda () (advance-channel-position! channel position))))) (define (set-port-position-from-end! port position) (let ((channel (port->channel port))) (if (not channel) (error "Not a channel port:" port)) (with-port-repositioning port (lambda () (set-channel-position-from-end! channel position)))))