Module: io-internals Synopsis: *standard-input*, *standard-output*, *standard-error* Author: Gary Palter and Jonathan Bachrach Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND define constant $EINTR = 4; define macro with-interrupt-repeat { with-interrupt-repeat ?:body end } => { iterate loop() let result = ?body; if(result < 0 & unix-errno-value() == $EINTR) loop() else result end if; end iterate } end macro; define function unix-read (fd :: , data :: , offset :: , count :: ) => (nread :: ) with-interrupt-repeat raw-as-integer (%call-c-function ("read") (fd :: , address :: , size :: ) => (result :: ) (integer-as-raw(fd), primitive-cast-raw-as-pointer (primitive-machine-word-add (primitive-cast-pointer-as-raw (primitive-repeated-slot-as-raw(data, primitive-repeated-slot-offset(data))), primitive-cast-pointer-as-raw(integer-as-raw(offset)))), integer-as-raw(count)) end) end end function; define function unix-write (fd :: , data :: , offset :: , count :: ) => (nwritten :: ) with-interrupt-repeat raw-as-integer (%call-c-function ("write") (fd :: , address :: , size :: ) => (result :: ) (integer-as-raw(fd), primitive-cast-raw-as-pointer (primitive-machine-word-add (primitive-cast-pointer-as-raw (primitive-repeated-slot-as-raw(data, primitive-repeated-slot-offset(data))), primitive-cast-pointer-as-raw(integer-as-raw(offset)))), integer-as-raw(count)) end) end end function unix-write; // standard unix error definitions define function get-unix-error (errno :: ) => (message :: ) let message :: = primitive-raw-as-string (%call-c-function ("strerror") (errno :: ) => (message :: ) (integer-as-raw(errno)) end); // Make a copy to avoid it being overwritten ... copy-sequence(message) end function get-unix-error; define function unix-errno-value () => (errno :: ) unix-errno() end function unix-errno-value; define function unix-error (syscall :: , #key errno = #f) => () let message :: = get-unix-error (if (~errno) unix-errno-value() else errno end); error("%s %s", syscall, message); end function unix-error; /// Here's the actual console implementation ... define class () end class ; define class (, , ) inherited slot stream-element-type = ; constant slot file-descriptor :: , required-init-keyword: file-descriptor:; slot accessor :: false-or() = #f, init-keyword: accessor:; // inherited from end class ; define method initialize (stream :: , #rest initargs, #key) => () next-method(); stream.accessor := make(); if (stream-direction(stream) = #"input") stream-input-buffer(stream) := make(, size: 128) else stream-output-buffer(stream) := make(, size: 1024); // Force first attempt to get an output buffer to find the console stream-output-buffer(stream).buffer-next := stream-output-buffer(stream).buffer-size end end method initialize; define variable *standard-input* = make(, file-descriptor: 0, direction: #"input"); define variable *standard-output* = make(, file-descriptor: 1, direction: #"output"); define variable *standard-error* = make(, file-descriptor: 2, direction: #"output"); define function flush-stdout () => () with-stream-locked(*standard-output*) let ob = stream-output-buffer(*standard-output*); if (ob & ~(ob.buffer-start = ob.buffer-end)) force-output(*standard-output*) end end end function flush-stdout; // Doesn't really close the stream cause you can't really close // console streams for unix. Just flushes standard output. This // is only expected to be called on application exit. define method close (stream :: , #key abort? = #f) => () with-stream-locked(stream) unless (abort?) if (stream.stream-direction == #"output") let ob = stream-output-buffer(stream); if (ob & ~(ob.buffer-start = ob.buffer-end)) force-output(stream); end if; end if; end; end // DON'T CALL NEXT-METHOD()!! Lower level close methods shouldn't // be called on console streams. end method close; /// Input protocol ... define method stream-input-available? (stream :: ) => (input-available? :: ) with-stream-locked(stream) stream-direction(stream) = #"input" & (stream-input-buffer(stream).buffer-next < stream-input-buffer(stream).buffer-end | do-input-available-at-source?(stream)) end end method stream-input-available?; define method do-input-available-at-source? (stream :: ) => (input-available? :: ) // Need something here (PeekConsole)? #t end method do-input-available-at-source?; define method read-element (stream :: , #key on-end-of-stream) => (element :: ) flush-stdout(); next-method() end method read-element; define method peek (stream :: , #key on-end-of-stream) => (element :: ) flush-stdout(); next-method() end method peek; define method read (stream :: , n :: , #key on-end-of-stream) => (elements-or-eof :: ) flush-stdout(); next-method() end method read; define method read-into! (stream :: , n :: , sequence :: , #key start = 0, on-end-of-stream) => (count-or-eof :: ) flush-stdout(); next-method() end method read-into!; define method read-line (stream :: , #key on-end-of-stream) => (string-or-eof :: , newline? :: ) flush-stdout(); next-method() end method read-line; define method accessor-read-into! (accessor :: , stream :: , offset :: , count :: , #key buffer) => (nread :: ) with-stream-locked(stream) ignore(accessor); let bufv = as(, buffer | stream-input-buffer(stream)); // N.B. No checking for sufficient length, e.g. // if (offset + count > buffer.size) error "Argh!!" end; let nread = unix-read(stream.file-descriptor, bufv, offset, count); if (nread < 0) unix-error("read"); end if; nread end end method accessor-read-into!; /// Output protocol ... define method write-element (stream :: , element :: ) => () with-stream-locked(stream) next-method(); if (element == '\n' | element == '\r') force-output(stream) end end end method write-element; define method write-line (stream :: , elements :: , #key start: _start = 0, end: _end = #f) => () with-stream-locked(stream) next-method(); force-output(stream); end end method write-line; define method new-line (stream :: ) => () with-stream-locked(stream) next-method(); force-output(stream) end end method new-line; define method accessor-write-from (accessor :: , stream :: , offset :: , count :: , #key buffer, return-fresh-buffer? = #f) => (number-of-bytes-written :: , new-buffer :: ) with-stream-locked(stream) ignore(accessor); let buffer = buffer | stream-output-buffer(stream); let bufv = as(, buffer); // N.B. No checking for sufficient length, e.g. // if (offset + count > buffer.size) error "Argh!!" end; let total-written = 0; local method try-writing () let nwritten = unix-write(stream.file-descriptor, bufv, offset, count); total-written := total-written + nwritten; if (nwritten < 0) unix-error("write") elseif (nwritten < count) count := count - nwritten; offset := offset + nwritten; try-writing(); end if; end; try-writing(); values(total-written, buffer) end end method accessor-write-from; define method accessor-force-output (accessor :: , stream :: ) => () ignore(accessor); end method accessor-force-output; define method do-force-output-buffers (stream :: ) => () with-stream-locked(stream) next-method(); let sb :: = stream-output-buffer(stream); sb.buffer-next := 0; sb.buffer-end := 0; values() end end method do-force-output-buffers; define method accessor-newline-sequence (accessor :: ) => (newline-sequence :: ); "\n" end method accessor-newline-sequence; define method accessor-close (accessor :: , #key abort? = #f, wait? = #t) => (closed? :: ) #f end method;