Module: streams-internals Synopsis: Abstract classes and generic definitions for external streams Author: Scott McKay, Marc Ferguson, Eliot Miranda 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 open abstract class () end class ; define open abstract class () keyword locator:; keyword if-exists:; keyword if-does-not-exist:; end class ; define open generic accessor (stream :: ) => (the-accessor :: false-or()); define open generic accessor-setter (the-accessor :: false-or(), stream :: ) => (the-accessor :: false-or()); define constant $open-external-streams :: = make(
); define function close-external-streams () => () for (external-stream in $open-external-streams) close(external-stream); end for; end function; define open method initialize (the-stream :: , #rest keys, #key locator = #f, already-registered? = #f) => () // This method has registered the open stream for automatic closing // on application exit. Let other methods know. apply(next-method, the-stream, already-registered?: #t, keys); // Use the stream as its own key. unless (already-registered?) $open-external-streams[the-stream] := the-stream; end unless; end method; /// Basic stream protocol define method close (stream :: , #rest keys, #key abort? = #f, wait? = #t, synchronize? = #f, already-unregistered? = #f) => () if (stream-open?(stream)) unless (abort?) if (synchronize?) force-output(stream, synchronize?: synchronize?); elseif (wait?) force-output(stream, synchronize?: #f); else do-force-output(stream) end if; end unless; if (stream.accessor) accessor-close(stream.accessor, abort?: abort?, wait?: wait?); end if; unless (already-unregistered?) remove-key!($open-external-streams, stream); end unless; // Let other methods know that this method has unregistered the // stream for automatic closing on application exit. apply(next-method, stream, already-unregistered?: #t, keys); end if; // what to do if it isn't open? warn? error? nothing? end method close; // Force-output always blocks. Do-force-output is the non-blocking form define method force-output (stream :: , #key synchronize? :: = #f) => () do-force-output(stream); wait-for-io-completion(stream); if(synchronize?) accessor-force-output(stream.accessor, stream); end if; end method force-output; define method synchronize-output (stream :: ) => (); force-output(stream, synchronize?: #t); end method; define method wait-for-io-completion (stream :: ) => () if (stream.accessor) accessor-wait-for-completion(stream.accessor); end; end method; define method newline-sequence (stream :: ) => (elements :: ) accessor-newline-sequence(stream.accessor) end method newline-sequence; /// Accessor Protocol // An attempt at a portable flexible interface to OS read/write/seek // functionality. Legal values for TYPE might include #"file", #"pipe", // #"tcp", #"udp". //--- Dubious idea Eliot, we can't dispatch on this. // Legal values for LOCATOR depend on TYPE. For TYPE == #"file", // LOCATOR is a string or naming a file. define open generic platform-accessor-class (type :: , locator :: ) => (class /* ---*** :: subclass() */); // Returns the file-handle/file-descriptor/file-pointer from the platform // accessor class, whichever it is. Value has to be a // because win32 file-handles set the high bits. So once again the lowest // common denominator wins. define open generic accessor-fd ( the-accessor :: ) => (the-fd :: false-or()); // Legal values for direction are #"input", #"output", #"input-output" // Legal values for if-exists are #"new-version", #"overwrite", #"replace", // #"truncate", #"signal", #"append" // NB #"append" does _not_ imply unix open(2) append semantics, _only_ // that writing is likely to continue from the end. So it's merely a // hint as to where to go first. // Legal values for if-does-not-exist are #"signal", #"create" // #all-keys to allow the stream using the accessor to pass through any // old junk without complaint. define method new-accessor (type :: , #rest initargs, #key locator, #all-keys) => (accessor :: ) let new-one = apply(make, platform-accessor-class(type, locator), initargs); apply(accessor-open, new-one, initargs); new-one end method new-accessor; define open generic accessor-open (accessor :: , #key direction, if-exists, if-does-not-exist, #all-keys) => (); define open generic accessor-close (accessor :: , #key abort?, wait?) => (closed? :: ); define open generic accessor-open? (accessor :: ) => (open? :: ); define open generic accessor-preferred-buffer-size (accessor :: ) => (preferred-buffer-size :: ); define open generic accessor-read-into! (accessor :: , stream :: , offset :: , count :: , #key buffer) => (nread :: ); define open generic accessor-write-from (accessor :: , stream :: , offset :: , count :: , #key buffer, return-fresh-buffer?) => (nwritten :: , new-buffer :: ); define open generic accessor-force-output (accessor :: , stream :: ) => (); define open generic accessor-wait-for-completion (accessor :: ) => (); // default method does nothing define method accessor-wait-for-completion (accessor :: ) => () ignore(accessor); end method; define open generic accessor-synchronize (accessor :: , stream :: ) => (); define open generic accessor-newline-sequence (accessor :: ) => (newline-sequence :: ); // Provide default methods for 'accessor-force-output' and 'accessor-synchronize' // that do nothing, since that is correct for most filesystem interfaces. define method accessor-force-output (accessor :: , stream :: ) => () #f end method accessor-force-output; define method accessor-synchronize (accessor :: , stream :: ) => () #f end method accessor-synchronize; // Simple scheme for tracking open accessors for reclaiming file descriptors // whilst debugging. define variable *open-accessors* = make(
, size: 10); /*---*** andrewa: this isn't used... define method close-open-accessors () => () map(accessor-close, key-sequence(*open-accessors*)) end method close-open-accessors; */