Input and Output
11.1 Ports
By definition, ports in MzScheme produce and consume bytes. When a
port is provided to a character-based operation, such as
read, the port's bytes are read and interpreted as a UTF-8
encoding of characters (see also section 1.2.3). Thus, reading a
single character may require reading multiple bytes, and a procedure
like char-ready? may need to peek several bytes into the
stream to determine whether a character is available. In the case of
a byte stream that does not correspond to a valid UTF-8 encoding,
functions such as read-char may need to peek one byte ahead
in the stream to discover that the stream is not a valid encoding.
When an input port produces a sequence of bytes that is not a valid UTF-8 encoding in a character-reading context, then bytes that constitute an invalid sequence are converted to the character ``?''. Specifically, bytes 255 and 254 are always converted to ``?'', bytes in the range 192 to 253 produce ``?'' when they are not followed by bytes that form a valid UTF-8 encoding, and bytes in the range 128 to 191 are converted to ``?'' when they are not part of a valid encoding that was started by a preceding byte in the range 192 to 253. To put it another way, when reading a sequence of bytes as characters, a minimal set of bytes are changed to 6327 so that the entire sequence of bytes is a valid UTF-8 encoding.
See section 3.6 for procedures that facilitate conversions
using UTF-8 or other encodings. See also
reencode-input-port and reencode-output-port
in Chapter 32
in PLT MzLib: Libraries Manual for obtaining a UTF-8-based port from one
that uses a different encoding of characters.
(port? v) returns #t if either
( or input-port? v)( is output-port? v)#t,
#f otherwise.
(file-stream-port? port) returns #t if the given port is
a file-stream port (see section 11.1.6, #f otherwise.
(terminal-port? port) returns #t if the given port is
attached to an interactive terminal, #f otherwise.
11.1.1 End-of-File Constant
The global variable eof is bound to the end-of-file
value. The standard Scheme predicate eof-object? returns
#t only when applied to this value.
Reading from a port produces an end-of-file result when the port has no more data, but some ports may also return end-of-file mid-stream. For example, a port connected to a Unix terminal returns an end-of-file when the user types control-d; if the user provides more input, the port returns additional bytes after the end-of-file.
11.1.2 Current Ports
The standard Scheme procedures current-input-port and
current-output-port are implemented as parameters in
MzScheme. See section 7.9.1.2 for more information.
11.1.3 Opening File Ports
The open-input-file and open-output-file
procedures accept an optional flag argument after the filename that
specifies a mode for the file:
'binary-- bytes are returned from the port exactly as they are read from the file. Binary mode is the default mode.'text-- return and linefeed bytes (10 and 13) are written to and read from the file are filtered by the port in a platform specific manner:Unix and Mac OS X: no filtering occurs.
Windows reading: a return-linefeed combination from a file is returned by the port as a single linefeed; no filtering occurs for return bytes that are not followed by a linefeed, or for a linefeed that is not preceded by a return.
Windows writing: a linefeed written to the port is translated into a return-linefeed combination in the file; no filtering occurs for returns.
In Windows,
'textmode works only with regular files; attempting to use'textwith other kinds of files triggers anexn:fail:filesystemexception.
The open-output-file procedure can also take a flag
argument that specifies how to proceed when a file with the specified
name already exists:
'truncate/replace-- try'truncate; if it fails, try'replace'append-- append to the end of the file under Unix and Mac OS X; under Windows,'appendis equivalent to'update, except that the file position is immediately set to the end of the file after opening it'update-- open an existing file without truncating it; if the file does not exist, theexn:fail:filesystemexception is raised
The open-input-output-file procedure takes the same
arguments as , but it produces two values:
an input port and an output port. The two ports are connected in that
they share the underlying file device. This procedure is intended for
use with special devices that can be opened by only one process, such
as COM1 in Windows. For regular files, sharing the device can
be confusing. For example, using one port does not automatically
flush the other port's buffer (see section 11.1.6 for more
information about buffers), and reading or writing in one port moves
the file position (if any) for the other port. For regular files, use
separate open-output-fileopen-input-file and open-output-file calls
to avoid confusion.
Extra flag arguments are passed to in any
order. Appropriate flag arguments can also be passed as the last
argument(s) to open-output-filecall-with-input-file,
with-input-from-file, call-with-output-file,
and with-output-to-file. When conflicting flag arguments
(e.g., both 'error and 'replace) are provided to
, open-output-filewith-output-to-file, or
call-with-output-file, the
exn:fail:contract exception is raised.
Both with-input-from-file and
with-output-to-file close the port they create if control
jumps out of the supplied thunk (either through a continuation or an
exception), and the port remains closed if control jumps back into
the thunk. The current input or output port is installed and restored
with parameterize (see section 7.9.2).
See section 11.1.6 for more information on file ports. When an input or output file-stream port is created, it is placed into the management of the current custodian (see section 9.2).
11.1.4 Pipes
(make-pipe [limit-k input-name-v output-name-v]) returns two port
values (see section 2.2): the first port is an input port and the
second is an output port. Data written to the output port is read
from the input port. The ports do not need to be explicitly closed.
The optional limit-k argument can be #f or a positive
exact integer. If limit-k is omitted or #f, the new
pipe holds an unlimited number of unread bytes (i.e., limited only by
the available memory). If limit-k is a positive number, then
the pipe will hold at most limit-k unread/unpeeked bytes;
writing to the pipe's output port thereafter will block until a read
or peek from the input port makes more space available. (Peeks
effectively extend the port's capacity until the peeked bytes are
read.)
The optional input-name-v and output-name-v are used as
the names for the returned input and out ports, respectively, if they
are supplied. Otherwise, the name of each port is 'pipe.
(pipe-content-length pipe-port) returns the number of bytes
contained in a pipe, where pipe-port is either of the pipe's
ports produced by make-pipe. The pipe's content length
counts all bytes that have been written to the pipe and not yet read
(though possibly peeked).
11.1.5 String Ports
Scheme input and output can be read from or collected into a string or byte string:
(open-input-bytesbytes[name-v])creates an input port that reads characters frombytes(see section 3.6). Modifyingbytesafterward does not affect the byte stream produced by the port. The optionalname-vargument is used as the name for the returned port; the default is'string.(open-input-stringstring[name-v])creates an input port that reads bytes from the UTF-8 encoding (see section 1.2.3) ofstring. The optionalname-vargument is used as the name for the returned port; the default is'string.(open-output-bytes[name-v])creates an output port that accumulates the output into a byte string. The optionalname-vargument is used as the name for the returned port; the default is'string.(open-output-string[name-v])creates an output port that accumulates the output into a byte string. This procedure is the same asopen-output-bytes.(get-output-bytesstring-output-port)returns the bytes accumulated instring-output-portso far in a freshly-allocated byte string. The bytes also remain in the port for further accumulation or for later calls toget-output-bytesorget-output-string.(get-output-stringstring-output-port)returns(; see also section 3.6.bytes->string/utf-8(get-output-bytesstring-output-port) #\?)
String input and output ports do not need to be explicitly closed. The
procedure, described in section 11.1.6,
works for string ports in position-setting mode.file-position
Example:
(define i (open-input-string"hello world")) (define o (open-output-string)) (write(readi) o) (get-output-stringo) ; =>"hello"
11.1.6 File-Stream Ports
A port created by , open-input-file,
open-output-file, and related functions is a file-stream
port. The initial input, output, and error ports in stand-alone
MzScheme are also file-stream ports. The subprocessfile-stream-port?
predicate recognizes file-stream ports.
An input port is block buffered by default, which means that on any
read, the buffer is filled with immediately-available bytes to speed
up future reads. Thus, if a file is modified between a pair of reads
to the file, the second read can produce stale data. Calling
to set an input port's file position flushes
its buffer.file-position
Most output ports are block buffered by default, but a terminal output port is line buffered, and the error output port is unbuffered. An output buffer is filled with a sequence of written bytes to be committed as a group, either when the buffer is full (in block mode) or when a newline is written (in line mode).
A port's buffering can be changed via
(described below). The two ports produced by
file-stream-buffer-modeopen-input-output-file have independent buffers.
The following procedures work primarily on file-stream ports:
(flush-output[output-port])forces all buffered data in the given output port to be physically written. Ifoutput-portis omitted, then the current output port is flushed. Only file-stream ports and custom ports (see section 11.1.7) use buffers; when called on a port without a buffer,has no effect.flush-outputBy default, a file-stream port is block-buffered, but this behavior can be modified with
file-stream-buffer-mode. In addition, the initial current output and error ports are automatically flushed when28,read,read-line,read-bytes, etc. are performed on the initial standard input port.read-string(file-stream-buffer-modeport[mode-symbol])gets or sets the buffer mode forport, if possible. All file-stream ports support setting the buffer mode, TCP ports (see section 11.4) support setting and getting the buffer mode, and custom ports (see section 11.1.7) may support getting and setting buffer modes.If
mode-symbolis provided, it must be one of'none,'line(output only), or'block, and the port's buffering is set accordingly. If the port does not support setting the mode, theexn:failexception is raised.If
mode-symbolis not provided, the current mode is returned, or#fis returned if the mode cannot be determined. Iffile-stream-portis an input port andmode-symbolis'line, theexn:fail:contractexception is raised.For an input port, peeking always places peeked bytes into the port's buffer, even when the port's buffer mode is
'none; furthermore, on some platforms, testing the port for input (viaorchar-ready?sync) may be implemented with a peek. If an input port's buffer mode is'none, then at most one byte is read for,read-bytes-avail!*,read-bytes-avail!, orpeek-bytes-avail!*; if any bytes are buffered in the port (e.g., to satisfy a previous peek), the procedures may access multiple buffered bytes, but no further bytes are read.peek-bytes-avail!(file-positionport)returns the current read/write position ofport. For file-stream and string ports,(file-positionport k-or-eof)sets the read/write position tok-or-eofrelative to the beginning of the file/string ifk-or-eofis a number, or to the current end of the file/string ifk-or-eofiseof. In position-setting mode,file-positionraises theexn:fail:contractexception for port kinds other than file-stream and string ports. Callingwithout a position on a non-file/non-string input port returns the number of bytes that have been read from that port if the position is known (see section 11.2.1.1), otherwise thefile-positionexn:fail:filesystemexception is raised.When
(file-positionsets the positionportk)kbeyond the current size of an output file or string, the file/string is enlarged to sizekand the new region is filled with#\nul. Ifkis beyond the end of an input file or string, then reading thereafter returnswithout changing the port's position.eofNot all file-stream ports support setting the position. If
is called with a position argument on such a file-stream port, thefile-positionexn:fail:filesystemexception is raised.When changing the file position for an output port, the port is first flushed if its buffer is not empty. Similarly, setting the position for an input port clears the port's buffer (even if the new position is the same as the old position). However, although input and output ports produced by
open-input-output-fileshare the file position, setting the position via one port does not flush the other port's buffer.(port-file-identityfile-stream-port)returns an exact positive integer that represents the identity of the device and file read or written byfile-stream-port. For two ports whose open times overlap, the result ofport-file-identityis the same for both ports if and only if the ports access the same device and file. For ports whose open times do not overlap, no guarantee is provided for the port identities (even if the ports actually access the same file) -- except as can be inferred through relationships with other ports. Iffile-stream-portis closed, theexn:failexception is raised. Under Windows 95, 98, and Me, iffile-stream-portis connected to a pipe instead of a file, theexn:fail:filesystemexception is raised.
11.1.7 Custom Ports
The and make-input-port procedures
create custom ports with arbitrary control procedures. Correctly
implementing a custom port can be tricky, because it amounts to
implementing a device driver. Custom ports are mainly useful to
obtain fine control over the action of committing bytes as read or
written.make-output-port
Many simple port variations can be implemented using threads and
pipes. For example, if get-next-char is a function that
produces either a character or , it can be turned into an
input port as follows
eof
(let-values ([(r w) (make-pipe4096)]) ;; Create a thread to move chars fromget-next-charto the pipe (thread(lambda () (let loop () (let ([v (get-next-char)]) (if (eof-object? v) (close-output-portw) (begin (write-charv w) (loop))))))) ;; Return the read end of the pipe r)
The port.ss in MzLib provides several other port constructors; see Chapter 32 in PLT MzLib: Libraries Manual.
11.1.7.1 Custom Input
(make-input-port name-v read-proc optional-peek-proc close-proc [optional-progress-evt-proc optional-commit-proc optional-location-proc count-lines!-proc init-position optional-buffer-mode-proc])
creates an input port. The port is immediately open for reading. If
close-proc procedure has no side effects, then the port need
not be explicitly closed.
name-v-- the name for the input port, which is reported byobject-name(see section 6.2.4).read-proc-- a procedure that takes a single argument: a mutable byte string to receive read bytes. The procedure's result is one of the following:the number of bytes read, as an exact, non-negative integer;
;eofa procedure of arity four (representing a ``special'' result, as discussed further below) and optionally of arity two, but a procedure result is allowed only when
optional-peek-procis not#f; ora synchronizable event (see section 7.7) that becomes ready when the read is complete (roughly): the event's value can one of the above three results or another event like itself; in the last case, a reading process loops with
until it gets a non-event result.sync
The
read-procprocedure must not block indefinitely. If no bytes are immediately available for reading, theread-procmust return0or an event, and preferably an event (to avoid busy waits). Theread-procshould not return0(or an event whose value is0) when data is available in the port, otherwise polling the port will behave incorrectly. An event result from an event can also break polling.If the result of a
read-proccall is not one of the above values, theexn:fail:contractexception is raised. If a returned integer is larger than the supplied byte string's length, theexn:fail:contractexception is raised. Ifoptional-peek-procis#fand a procedure for a special result is returned, theexn:fail:contractexception is raised.The
read-procprocedure can report an error by raising an exception, but only if no bytes are read. Similarly, no bytes should be read if, an event, or a procedure is returned. In other words, no bytes should be lost due to spurious exceptions or non-byte data.eofA port's reading procedure may be called in multiple threads simultaneously (if the port is accessible in multiple threads), and the port is responsible for its own internal synchronization. Note that improper implementation of such synchronization mechanisms might cause a non-blocking read procedure to block indefinitely.
If
optional-peek-proc,optional-progress-evt-proc, andoptional-commit-procare all provided and non-#f, then the following is an acceptable implementation ofread-proc:(lambda (bstr) (let* ([progress-evt (progress-evt-proc)] [v (peek-proc bstr 0 progress-evt)]) (cond [(
sync/timeout0 progress-evt) 0] ; try again [(evt?v) (wrap-evt v (lambda (x) 0))] ; sync, then try again [(and (number?v) (zero?v)) 0] ; try again [else (if (optional-commit-proc (if (number?v) v 1) progress-evt always-evt) v ; got a result 0)]))) ; try againAn implementor may choose not to implement the
optional-procedures, however, and even an implementor who does supplyoptional-procedures may provide a differentread-procthat uses a fast path for non-blocking reads.optional-peek-proc-- either#for a procedure that takes three arguments:a mutable byte string to receive peeked bytes;
a non-negative number of bytes (or specials) to skip before peeking; and
either
#for a progress event produced byoptional-progress-evt-proc.
The results and conventions for
optional-peek-procare mostly the same as forread-proc. The main difference is in the handling of the progress event, if it is not#f. If the given progress event becomes ready, theoptional-peek-procmust abort any skip attempts and not peek any values. In particular,optional-peek-procmust not peek any values if the progress event is initially ready.Unlike
read-proc,optional-peek-procshould produce#f(or an event whose value is#f) if no bytes were peeked because the progress event became ready. Likeread-proc, a0result indicates that another attempt is likely to succeed, so0is inappropriate when the progress event is ready. Also likeread-proc,optional-peek-procmust not block indefinitely.The skip count provided to
optional-peek-procis a number of bytes (or specials) that must remain present in the port -- in addition to the peek results -- when the peek results are reported. If a progress event is supplied, then the peek is effectively canceled when another process reads data before the given number can be skipped. If a progress event is not supplied and data is read, then the peek must effectively restart with the original skip count.The system does not check that multiple peeks return consistent results, or that peeking and reading produce consistent results.
If
optional-peek-procis#f, then peeking for the port is implemented automatically in terms of reads, but with several limitations. First, the automatic implementation is not thread-safe. Second, the automatic implementation cannot handle special results (non-byte and non-eof), soread-proccannot return a procedure for a special whenoptional-peek-procis#f. Finally, the automatic peek implementation is incompatible with progress events, so ifoptional-peek-procis#f, thenprogress-evt-procandoptional-commit-procmust be#f. See alsomake-input-port/peek-to-readin Chapter 32 in PLT MzLib: Libraries Manual.close-proc-- a procedure of zero arguments that is called to close the port. The port is not considered closed until the closing procedure returns. The port's procedures will never be used again via the port after it is closed. However, the closing procedure can be called simultaneously in multiple threads (if the port is accessible in multiple threads), and it may be called during a call to the other procedures in another thread; in the latter case, any outstanding reads and peeks should be terminated with an error.optional-progress-evt-proc-- either#f(the default), or a procedure that takes no arguments and returns an event. The event must become ready only after data is next read from the port or the port is closed. After the event becomes ready, it must remain so. (See alsosemaphore-peek-evtin section 7.4.)If
optional-progress-evt-procis#f, thenport-provides-progress-evts?applied to the port will produce#f, and the port will not be a valid argument toport-progress-evt.optional-commit-proc-- either#f(the default), or a procedure that takes three arguments:an exact, positive integer kr;
a progress event produced by
optional-progress-evt-proc;an event,
done-evt, that is either a channel-put event, channel, semaphore, semaphore-peek event, always event, or never event.
A commit corresponds to removing data from the stream that was previously peeked, but only if no other process removed data first. (The removed data does not need to be reported, because it has been peeked already.) More precisely, assuming that kp bytes, specials, and mid-stream
s have been previously peeked or skipped at the start of the port's stream,eofoptional-commit-procmust satisfy the following constraints:It must return only when the commit is complete or when the given progress event becomes ready.
It must commit only if kp is positive.
If it commits, then it must do so with either kr items or kp items, whichever is smaller, and only if kp is positive.
It must never choose
done-evtin a synchronization after the given progress event is ready, or afterdone-evthas been synchronized once.It must not treat any data as read from the port unless
done-evtis chosen in a synchronization.It must not block indefinitely if
done-evtis ready; it must return soon after the read completes or soon after the given progress event is ready, whichever is first.It can report an error by raising an exception, but only if no data is committed. In other words, no data should be lost due to an exception, including a break exception.
It must return a true value if data is committed,
#fotherwise. When it returns a value, the given progress event must be ready (perhaps because data was just committed).It must raise an exception if no data (including
) has been peeked from the beginning of the port's stream, or if it would have to block indefinitely to wait for the given progress event to become ready.eof
A call to
optional-commit-procisparameterize-breaked to disable breaks.optional-location-proc-- either#f(the default), or a procedure that takes no arguments and returns three values: the line number for the next item in the port's stream (a positive number or#f), the column number for the next item in the port's stream (a non-negative number or#f), and the position for the next item in the port's stream (a positive number or#f). See also section 11.2.1.1.This procedure is only called if line counting is enabled for the port via
port-count-lines!(in which casecount-lines!-procis called). The,read,read-syntaxread-honu, andread-honu-syntaxprocedures assume that reading a non-whitespace character increments the column and position by one.count-lines!-proc-- a procedure of no arguments that is called if and when line counting is enabled for the port. The default procedure isvoid.init-position-- an exact, positive integer that determines the position of the port's first item, used when line counting is not enabled for the port. The default is1.optional-buffer-mode-proc-- either#f(the default) or a procedure that accepts zero or one arguments. Ifoptional-buffer-mode-procis#f, then the resulting port does not support a buffer-mode setting. Otherwise, the procedure is called with one symbol argument ('blockor'none) to set the buffer mode, and it is called with zero arguments to get the current buffer mode. In the latter case, the result must be'block,'none, or#f(unknown). See section 11.1.6 for more information on buffer modes.
When read-proc or optional-peek-proc (or an event
produced by one of these) returns a procedure, and the procedure is
used to obtain a non-byte result.29 The procedure is called by
,30
read, read-syntaxread-honu, read-honu-syntax,
read-byte-or-special, read-char-or-special,
peek-byte-or-special, or peek-char-or-special. The
special-value procedure can return an arbitrary value, and it will be
called zero or one times (not necessarily before further reads or
peeks from the port). See section 11.2.9 for more details on
the procedure's arguments and result.
If read-proc or optional-peek-proc returns a special
procedure when called by any reading procedure other than
, read, read-syntaxread-honu,
read-honu-syntax, ,
read-char-or-special, peek-char-or-special, or
read-byte-or-special, then the peek-byte-or-specialexn:fail:contract exception is raised.
Examples:
;; A port with no input... ;; Easy:(;; Hard: (define /dev/null-in (open-input-bytes#"")make-input-port'null(lambda (s)eof) (lambda (skip s progress-evt)eof)void(lambda () never-evt) (lambda (k progress-evt done-evt) (error"no successful peeks!")))) (read-char/dev/null-in) ; =>(eofpeek-char/dev/null-in) ; =>(eofread-byte-or-special/dev/null-in) ; =>(eofpeek-byte-or-special/dev/null-in 100) ; =>;; A port that produces a stream of 1s: (define infinite-ones (eofmake-input-port'ones (lambda (s) (bytes-set!s 0 (char->integer#\1)) 1) #fvoid)) (read-string5 infinite-ones) ; =>"11111";; But we can't peek ahead arbitrarily far, because the ;; automatic peek must record the skipped bytes: (peek-string5 (expt2 5000) infinite-ones) ; => error: out of memory ;; An infinite stream of 1s with a specific peek procedure: (define infinite-ones (let ([one! (lambda (s) (bytes-set!s 0 (char->integer#\1)) 1)]) (make-input-port'ones one! (lambda (s skip progress-evt) (one! s))void))) (read-string5 infinite-ones) ; =>"11111";; Now we can peek ahead arbitrarily far: (peek-string5 (expt2 5000) infinite-ones) ; =>"11111";; The port doesn't supply procedures to implement progress events: (port-provides-progress-evts?infinite-ones) ; =>#f(port-progress-evtinfinite-ones) ; error: no progress events ;; Non-byte port results: (define infinite-voids (make-input-port'voids (lambda (s) (lambda args 'void)) (lambda (skip s) (lambda args 'void))void)) (read-charinfinite-voids) ; => error: non-char in an unsupported context (read-char-or-specialinfinite-voids) ; =>';; This port produces 0, 1, 2, 0, 1, 2, etc., but it is not ;; thread-safe, because multiple threads might read and changevoidn. (define mod3-cycle/one-thread (let* ([n 2] [mod! (lambda (s delta) (bytes-set!s 0 (+ 48 (modulo(+ n delta) 3))) 1)]) (make-input-port'mod3-cycle/not-thread-safe (lambda (s) (set! n (modulo(add1n) 3)) (mod! s 0)) (lambda (s skip) (mod! s skip))void))) (read-string5 mod3-cycle/one-thread) ; =>"01201"(peek-string5 (expt2 5000) mod3-cycle/one-thread) ; =>"20120";; Same thing, but thread-safe and kill-safe, and with progress ;; events. Only the server thread touches the stateful part ;; directly. (See the output port examples for a simpler thread-safe ;; example, but this one is more general.) (define (make-mod3-cycle) (define read-req-ch (make-channel)) (define peek-req-ch (make-channel)) (define progress-req-ch (make-channel)) (define commit-req-ch (make-channel)) (define close-req-ch (make-channel)) (define closed? #f) (define n 0) (define progress-sema #f) (define (mod! s delta) (bytes-set!s 0 (+ 48 (modulo(+ n delta) 3))) 1) ;; ---------------------------------------- ;; The server has a list of outstanding commit requests, ;; and it also must service each port operation (read, ;; progress-evt, etc.) (define (serve commit-reqs response-evts) (applysync(handle-evt read-req-ch (handle-read commit-reqs response-evts)) (handle-evt progress-req-ch (handle-progress commit-reqs response-evts)) (handle-evt commit-req-ch (add-commit commit-reqs response-evts)) (handle-evt close-req-ch (handle-close commit-reqs response-evts)) (append(map(make-handle-response commit-reqs response-evts) response-evts) (map(make-handle-commit commit-reqs response-evts) commit-reqs)))) ;; Read/peek request: fill in the string and commit (define ((handle-read commit-reqs response-evts) r) (let ([s (carr)] [skip (cadrr)] [ch (caddrr)] [nack (cadddrr)] [peek? (cddddrr)]) (unless closed? (mod! s skip) (unless peek? (commit! 1))) ;; Add an event to respond: (serve commit-reqs (cons(choice-evtnack (channel-put-evtch (if closed? 0 1))) response-evts)))) ;; Progress request: send a peek evt for the current ;; progress-sema (define ((handle-progress commit-reqs response-evts) r) (let ([ch (carr)] [nack (cdrr)]) (unless progress-sema (set! progress-sema (make-semaphore(if closed? 1 0)))) ;; Add an event to respond: (serve commit-reqs (cons(choice-evtnack (channel-put-evtch (semaphore-peek-evt progress-sema))) response-evts)))) ;; Commit request: add the request to the list (define ((add-commit commit-reqs response-evts) r) (serve (consr commit-reqs) response-evts)) ;; Commit handling: watch out for progress, in which case ;; the response is a commit failure; otherwise, try ;; to sync for a commit. In either event, remove the ;; request from the list (define ((make-handle-commit commit-reqs response-evts) r) (let ([k (carr)] [progress-evt (cadrr)] [done-evt (caddrr)] [ch (cadddrr)] [nack (cddddrr)]) ;; Note: we don't check that k is < the sum of ;; previous peeks, because the entire stream is actually ;; known, but we could send an exception in that case. (choice-evt(handle-evt progress-evt (lambda (x) (syncnack (channel-put-evtch #f)) (serve (remqr commit-reqs) response-evts))) ;; Only create an event to satisfy done-evt if progress-evt ;; isn't already ready. ;; Afterward, if progress-evt becomes ready, then this ;; event-making function will be called again, because ;; the server controls all posts to progress-evt. (if (sync/timeout0 progress-evt) never-evt (handle-evt done-evt (lambda (v) (commit! k) (syncnack (channel-put-evtch #t)) (serve (remqr commit-reqs) response-evts))))))) ;; Response handling: as soon as the respondee listens, ;; remove the response (define ((make-handle-response commit-reqs response-evts) evt) (handle-evt evt (lambda (x) (serve commit-reqs (remqevt response-evts))))) ;; Close handling: post the progress sema, if any, and set ;; theclosed?flag (define ((handle-close commit-reqs response-evts) r) (let ([ch (carr)] [nack (cdrr)]) (set! closed? #t) (when progress-sema (semaphore-postprogress-sema)) (serve commit-reqs (cons(choice-evtnack (channel-put-evtch (void))) response-evts)))) ;; Helper for reads and post-peek commits: (define (commit! k) (when progress-sema (semaphore-postprogress-sema) (set! progress-sema #f)) (set! n (+ n k))) ;; Start the server thread: (define server-thread (thread(lambda () (servenullnull)))) ;; ---------------------------------------- ;; Client-side helpers: (define (req-evt f) (nack-guard-evt(lambda (nack) ;; Be sure that the server thread is running: (thread-resumeserver-thread (current-thread)) ;; Create a channel to hold the reply: (let ([ch (make-channel)]) (f ch nack) ch)))) (define (read-or-peek-evt s skip peek?) (req-evt (lambda (ch nack) (channel-putread-req-ch (list*s skip ch nack peek?))))) ;; Make the port: (make-input-port'mod3-cycle ;; Each handler for the port just sends ;; a request to the server (lambda (s) (read-or-peek-evt s 0 #f)) (lambda (s skip) (read-or-peek-evt s skip #t)) (lambda () ; close (sync(req-evt (lambda (ch nack) (channel-putprogress-req-ch (list*ch nack)))))) (lambda () ; progress-evt (sync(req-evt (lambda (ch nack) (channel-putprogress-req-ch (list*ch nack)))))) (lambda (k progress-evt done-evt) ; commit (sync(req-evt (lambda (ch nack) (channel-putcommit-req-ch (list*k progress-evt done-evt ch nack)))))))) (let ([mod3-cycle (make-mod3-cycle)]) (let ([result1 #f] [result2 #f]) (let ([t1 (thread(lambda () (set! result1 (read-string5 mod3-cycle))))] [t2 (thread(lambda () (set! result2 (read-string5 mod3-cycle))))]) (thread-waitt1) (thread-waitt2) (string-appendresult1 "," result2))) ; =>"02120,10201", maybe (let ([s (make-bytes1)] [progress-evt (port-progress-evtmod3-cycle)]) (peek-bytes-avail!s 0 progress-evt mod3-cycle) ; =>1s ; =>#"1"(port-commit-peeked1 progress-evt (make-semaphore1) mod3-cycle) ; =>#t(sync/timeout0 progress-evt) ; =>progress-evt(peek-bytes-avail!s 0 progress-evt mod3-cycle) ; =>0(port-commit-peeked1 progress-evt (make-semaphore1) mod3-cycle)) ; =>#f(close-input-portmod3-cycle))
11.1.7.2 Custom Output
(make-output-port name-v evt write-proc close-proc [optional-write-special-proc optional-write-evt-proc optional-special-evt-proc optional-location-proc count-lines!-proc init-position optional-buffer-mode-proc])
creates an output port. The port is immediately open for writing. If
close-proc procedure has no side effects, then the port need
not be explicitly closed. The port can buffer data within its
write-proc and optional-write-special-proc procedures.
name-v-- the name for the output port, which is reported byobject-name(see section 6.2.4).evt-- a synchronization event (see section 7.7; e.g., a semaphore or another port). The event is used in place of the output port when the port is supplied to synchronization procedures likesync. Thus, the event should be unblocked when the port is ready for writing at least one byte without blocking, or ready to make progress in flushing an internal buffer without blocking. The event must not unblock unless the port is ready for writing; otherwise, the guarantees ofwill be broken for the output port. Usesyncalways-evtif writes to the port always succeed without blocking.write-proc-- a procedure of five arguments:an immutable byte string containing bytes to write;
a non-negative exact integer for a starting offset (inclusive) into the byte string;
a non-negative exact integer for an ending offset (exclusive) into the byte string;
a boolean;
#findicates that the port is allowed to keep the written bytes in a buffer, and that it is allowed to block indefinitely;#tindicates that the write should not block, and that the port should attempt to flush its buffer and completely write new bytes instead of buffering them;a boolean;
#tindicates that if the port blocks for a write, then it should enable breaks while blocking (e.g., usingsync/enable-break; this argument is always#fif the fourth argument is#t.
The procedure returns one of the following:
a non-negative exact integer representing the number of bytes written or buffered;
#fif no bytes could be written, perhaps because the internal buffer could not be completely flushed;a synchronizable event (see section 7.7) that acts like the result of
write-bytes-avail-evtto complete the write.
Since
write-proccan produce an event, an acceptable implementation ofwrite-procis to pass its first three arguments to the port'soptional-write-evt-proc. Some port implementors, however, may choose not to provideoptional-write-evt-proc(perhaps because writes cannot be made atomic), or may implementwrite-procto enable a fast path for non-blocking writes or to enable buffering.From a user's perspective, the difference between buffered and completely written data is (1) buffered data can be lost in the future due to a failed write, and (2)
flush-outputforces all buffered data to be completely written. Under no circumstances is buffering required.If the start and end indices are the same, then the fourth argument to
write-procwill be#f, and the write request is actually a flush request for the port's buffer (if any), and the result should be0for a successful flush (or if there is no buffer).The result should never be
0if the start and end indices are different, otherwise theexn:fail:contractexception is raised. If a returned integer is larger than the supplied byte-string range, theexn:fail:contractexception is raised.The
#fresult should be avoided, unless the next write attempt is likely to work. Otherwise, if data cannot be written, return an event instead.An event returned by
write-proccan return#for another event like itself, in contrast to events produced bywrite-bytes-avail-evtoroptional-write-evt-proc. A writing process loops withuntil it obtains a non-event result.syncThe
write-procprocedure is always called with breaks disabled, independent of whether breaks were enabled when the write was requested by a client of the port. If breaks were enabled for a blocking operation, then the fifth argument towrite-procwill be#t, which indicates thatwrite-procshould re-enable breaks while blocking.If the writing procedure raises an exception, due either to write or commit operations, it must not have committed any bytes (though it may have committed previously buffered bytes).
A port's writing procedure may be called in multiple threads simultaneously (if the port is accessible in multiple threads). The port is responsible for its own internal synchronization. Note that improper implementation of such synchronization mechanisms might cause a non-blocking write procedure to block.
close-proc-- a procedure of zero arguments that is called to close the port. The port is not considered closed until the closing procedure returns. The port's procedures will never be used again via the port after it is closed. However, the closing procedure can be called simultaneously in multiple threads (if the port is accessible in multiple threads), and it may be called during a call to the other procedures in another thread; in the latter case, any outstanding writes or flushes should be terminated immediately with an error.optional-write-special-proc-- either#f(the default), or a procedure to handlewrite-specialcalls for the port. If#f, then the port does not support special output, andport-writes-special?will return#fwhen applied to the port.If a procedure is supplied, it takes three arguments: the special value to write, a boolean that is
#fif the procedure can buffer the special value and block indefinitely, and a boolean that is#tif the procedure should enable breaks while blocking. The result is one of the following:a non-event true value, which indicates that the special is written;
#fif the special could not be written, perhaps because an internal buffer could not be completely flushed;a synchronizable event (see section 7.7) that acts like the result of
write-special-evtto complete the write.
Since
optional-write-special-proccan return an event, passing the first argument to an implementation ofoption-write-special-evt-procis acceptable as anoptional-write-special-proc.As for
write-proc, the#fresult is discouraged, since it can lead to busy waiting. Also as forwrite-proc, an event produced byoptional-write-special-procis allowed to produce#for another event like itself. Theoptional-write-special-procprocedure is always called with breaks disabled, independent of whether breaks were enabled when the write was requested by a client of the port.optional-write-evt-proc-- either#f(the default) or a procedure of three arguments:an immutable byte string containing bytes to write;
a non-negative exact integer for a starting offset (inclusive) into the byte string, and
a non-negative exact integer for an ending offset (exclusive) into the byte string.
The result is a synchronizable event (see section 7.7) to act as the result of
write-bytes-avail-evtfor the port (i.e., to complete a write or flush), which becomes available only as data is committed to the port's underlying device, and whose result is the number of bytes written.If
optional-write-evt-procis#f, thenport-writes-atomic?will produce#fwith applied to the port, and the port will not be a valid argument to procedures such aswrite-bytes-avail-evt.Otherwise, an event returned by
optional-write-evt-procmust not cause data to be written to the port unless the event is chosen in a synchronization, and it must write to the port if the event is chosen (i.e., the write must appear atomic with respect to the synchronization).If the event's result integer is larger than the supplied byte-string range, the
exn:fail:contractexception is raised by a wrapper on the event. If the start and end indices are the same (i.e., no bytes are to be written), then the event should produce0when the buffer is completely flushed. (If the port has no buffer, then it is effectively always flushed.)If the event raises an exception, due either to write or commit operations, it must not have committed any new bytes (though it may have committed previously buffered bytes).
Naturally, a port's events may be used in multiple threads simultaneously (if the port is accessible in multiple threads). The port is responsible for its own internal synchronization.
optional-write-special-evt-proc-- either#f(the default), or a procedure to handlewrite-special-evtcalls for the port. This argument must be#fif eitheroptional-write-special-procoroptional-write-evt-procis#f, and it must be a procedure if both of those arguments are procedures.If it is a procedure, it takes one argument: the special value to write. The resulting event (with its constraints) is analogous to the result of
optional-write-evt-proc.If the event raises an exception, due either to write or commit operations, it must not have committed the special value (though it may have committed previously buffered bytes and values).
optional-location-proc-- either#f(the default), or a procedure that takes no arguments and returns three values: the line number for the next item written to the port's stream (a positive number or#f), the column number for the next item written to port's stream (a non-negative number or#f), and the position for the next item written to port's stream (a positive number or#f). See also section 11.2.1.1.This procedure is only called if line counting is enabled for the port via
port-count-lines!(in which casecount-lines!-procis called).count-lines!-proc-- a procedure of no arguments that is called if and when line counting is enabled for the port. The default procedure isvoid.init-position-- an exact, positive integer that determines the position of the port's first output item, used when line counting is not enabled for the port. The default is1.optional-buffer-mode-proc-- either#f(the default) or a procedure that accepts zero or one arguments. Ifoptional-buffer-mode-procis#f, then the resulting port does not support a buffer-mode setting. Otherwise, the procedure is called with one symbol argument ('block,'line, or'none) to set the buffer mode, and it is called with zero arguments to get the current buffer mode. In the latter case, the result must be'block,'line,'none, or#f(unknown). See section 11.1.6 for more information on buffer modes.
Examples:
;; A port that writes anything to nowhere: (define /dev/null-out (make-output-port'nullalways-evt (lambda (s start end non-block? breakable?) (- end start))void(lambda (special non-block? breakable?) #t) (lambda (s start end) (wrap-evt always-evt (lambda (x) (- end start)))) (lambda (special) always-evt))) (display"hello" /dev/null-out) ; => void (write-bytes-avail #"hello" /dev/null-out) ; =>5(write-special 'hello /dev/null-out) ; =>#t(sync(write-bytes-avail-evt #"hello" /dev/null-out)) ; =>5;; A part that accumulates bytes as characters in a list, ;; but not in a thread-safe way: (define accum-listnull) (define accumulator/not-thread-safe (make-output-port'accum/not-thread-safe always-evt (lambda (s start end non-block? breakable?) (set! accum-list (appendaccum-list (mapinteger->char(bytes->list (subbytes s start end))))) (- end start))void)) (display"hello" accumulator/not-thread-safe) accum-list ; =>'(#\h #\e #\l #\l #\o);; Same as before, but with simple thread-safety: (define accum-listnull) (define accumulator (let* ([lock (make-semaphore1)] [lock-peek-evt (semaphore-peek-evt lock)]) (make-output-port'accum lock-peek-evt (lambda (s start end non-block? breakable?) (if (semaphore-try-wait?lock) (begin (set! accum-list (appendaccum-list (mapinteger->char(bytes->list (subbytes s start end))))) (semaphore-postlock) (- end start)) ;; Cheap strategy: block until the list is unlocked, ;; then return 0, so we get called again (wrap-evt lock-peek (lambda (x) 0))))void))) (display"hello" accumulator) accum-list ; =>'(#\h #\e #\l #\l #\o);; A port that transforms data before sending it on ;; to another port. Atomic writes exploit the ;; underlying port's ability for atomic writes. (define (make-latin-1-capitalize port) (define (byte-upcase s start end) (list->bytes (map(lambda (b) (char->integer(char-upcase(integer->charb)))) (bytes->list (subbytes s start end))))) (make-output-port'byte-upcase ;; This port is ready when the original is ready: port ;; Writing procedure: (lambda (s start end non-block? breakable?) (let ([s (byte-upcase s start end)]) (if non-block? (write-bytes-avail* s port) (begin (displays port) (bytes-lengths))))) ;; Close procedure --- close original port: (lambda () (close-output-portport)) #f ;; Write event: (and (port-writes-atomic? port) (lambda (s start end) (write-bytes-avail-evt (byte-upcase s start end) port))))) (define orig-port (open-output-string)) (define cap-port (make-latin-1-capitalize orig-port)) (display"Hello" cap-port) (get-output-stringorig-port) ; =>"HELLO"(sync(write-bytes-avail-evt #"Bye" cap-port)) ; =>3(get-output-stringorig-port) ; =>"HELLOBYE"
11.2 Reading and Writing
MzScheme's support for reading and writing includes many extensions compared to R5RS, both at the level of individual bytes and characters and at the level of S-expressions.
11.2.1 Reading Bytes, Characters, and Strings
In addition to the standard reading procedures, MzScheme provides
byte-reading procedure, block-reading procedures such as
, and more.
read-line
(read-line[input-port mode-symbol])returns a string containing the next line of bytes frominput-port. Ifinput-portis omitted, the current input port is used.Characters are read from
input-portuntil a line separator or an end-of-file is read. The line separator is not included in the result string (but it is removed from the port's stream). If no characters are read before an end-of-file is encountered,is returned.eofThe
mode-symbolargument determines the line separator(s). It must be one of the following symbols:'linefeedbreaks lines on linefeed characters; this is the default.'returnbreaks lines on return characters.'return-linefeedbreaks lines on return-linefeed combinations. If a return character is not followed by a linefeed character, it is included in the result string; similarly, a linefeed that is not preceded by a return is included in the result string.'anybreaks lines on any of a return character, linefeed character, or return-linefeed combination. If a return character is followed by a linefeed character, the two are treated as a combination.'any-onebreaks lines on either a return or linefeed character, without recognizing return-linefeed combinations.
Return and linefeed characters are detected after the conversions that are automatically performed when reading a file in text mode. For example, reading a file in text mode under Windows automatically changes return-linefeed combinations to a linefeed. Thus, when a file is opened in text mode,
'linefeedis usually the appropriatemode.read-line(read-bytes-line[input-port mode-symbol])is analogous toread-line, but it reads bytes and produces a byte string.(read-stringk[input-port])returns a string containing the nextkcharacters frominput-port. The default value ofinput-portis the current input port.If
kis0, then the empty string is returned. Otherwise, if fewer thankcharacters are available before an end-of-file is encountered, then the returned string will contain only those characters before the end-of-file (i.e., the returned string's length will be less thank). 31 If no characters are available before an end-of-file, thenis returned.eofIf an error occurs during reading, some characters may be lost (i.e., if
successfully reads some characters before encountering an error, the characters are dropped.)read-string(read-bytesk[input-port])is analogous toread-string, but it reads bytes and produces a byte string.(read-string!string[input-port start-k end-k])reads characters frominput-portlikeread-string, but puts them intostringstarting from indexstart-k(inclusive) up toend-k(exclusive). The default value ofinput-portis the current input port. The default value ofstart-kis0. The default value ofend-kis the length of thestring. Like, thesubstringexn:fail:contractexception is raised ifstart-korend-kis out-of-range forstring.If the difference between
start-kandend-kis0, then0is returned andbytesis not modified. If no bytes are available before an end-of-file, thenis returned. Otherwise, the return value is the number of bytes read. Ifeofmbytes are read andm<end-k-start-k, thenbytesis not modified at indicesstart-k+mthoughend-k.(read-bytes!string[input-port start-k end-k])is analogous toread-string!, but it reads bytes and puts them into a byte string.(read-bytes-avail!bytes[input-port start-k end-k])is likeread-bytes!, but returns without blocking after reading immediately-available bytes, and it may return a procedure for a ``special'' result. Theprocedure blocks only if no bytes (or specials) are yet available. Also unlikeread-bytes-avail!read-bytes!,never drops bytes; ifread-bytes-avail!successfully reads some bytes and then encounters an error, it suppresses the error (treating it roughly like an end-of-file) and returns the read bytes. (The error will be triggered by future reads.) If an error is encountered before any bytes have been read, an exception is raised.read-bytes-avail!When
input-portproduces a special value, as described in section 11.1.7, the result is a procedure of four arguments. The four arguments correspond to the location of the special value within the port, as described in section 11.1.7. If the procedure is called more than once with valid arguments, theexn:fail:contractexception is raised. Ifread-bytes-availreturns a special-producing procedure, then it does not place characters inbytes. Similarly,read-bytes-availplaces only as many bytes intobytesas are available before a special value in the port's stream.(read-bytes-avail!*bytes[input-port start-k end-k])is like, except that it returnsread-bytes-avail!0immediately if no bytes (or specials) are available for reading and the end-of-file is not reached.(read-bytes-avail!/enable-breakbytes[input-port start-k end-k])is like, except that breaks are enabled during the read (see also section 6.6). If breaking is disabled whenread-bytes-avail!is called, and if theread-bytes-avail!/enable-breakexn:breakexception is raised as a result of the call, then no bytes will have been read frominput-port.(peek-stringk skip-k[input-port])is similar toread-string, except that the returned characters are preserved in the port for future reads. (More precisely, undecoded bytes are left for future reads.) Theskip-kargument indicates a number of bytes (not characters) in the input stream to skip before collecting characters to return; thus, in total, the nextskip-kbytes pluskcharacters are inspected.For most kinds of ports, inspecting
skip-kbytes andkcharacters requires at leastskip-k+kbytes of memory overhead associated with the port, at least until the bytes/characters are read. No such overhead is required when peeking into a string port (see section 11.1.5), a pipe port (see section 11.1.4), or a custom port with a specific peek procedure (depending on how the peek procedure is implemented; see section 11.1.7).If a port produces
mid-stream, peek skips beyond theeofalways produceeofuntil theeofis read.eof(peek-bytesk skip-k[input-port])is analogous topeek-string, but it peeks bytes and produces a byte string.(peek-string!string skip-k[input-port start-k end-k])is likeread-string!, but for peeking, and with askip-kargument like.peek-string(peek-bytes!bytes skip-k[input-port start-k end-k])is analogous topeek-string!, but it peeks bytes and puts them into a byte string.(peek-bytes-avail!bytes skip-k[progress-evt input-port start-k end-k])is like, but for peeking, and with two extra arguments. Theread-bytes-avail!skip-kargument is as in. Thepeek-bytesprogress-evtargument must be either#f(the default) or an event produced byforport-progress-evtinput-port.To peek,
blocks until finding an end-of-file, at least one byte (or special) past the skipped bytes, or until a non-peek-bytes-avail!#fprogress-evtbecomes ready. Furthermore, ifprogress-evtis ready before bytes are peeked, no bytes are peeked or skipped, andprogress-evtmay cut short the skipping process if it becomes available during the peek attempt.The result of
ispeek-bytes-avail!0only in the case thatprogress-evtbecomes ready before bytes are peeked.(peek-bytes-avail!*bytes skip-k[progress-evt input-port start-k end-k])is like, but for peeking, and withread-bytes-avail!*skip-kandprogress-evtarguments like. Since this procedure never blocks, it may return before evenpeek-bytes-avail!skip-kbytes are available from the port.(peek-bytes-avail!/enable-breakbytes skip-k[progress-evt input-port start-k end-k])is the peeking version of, withread-bytes-avail!/enable-breakskip-kandprogress-evtarguments like.peek-bytes-avail!(read-byte[input-port])is analogous to, but it reads and returns a byte (orread-chareof) instead of a character.(read-char-or-special[input-port])is the same as, except that if the input port returns a non-byte value (through a value-generating procedure in a custom port; see section 11.1.7 and section 11.2.9.1 for details), the non-byte value is returned.read-char(read-byte-or-special[input-port])is analogous to, but it reads and returns a byte instead of a character.read-char-or-special(peek-char[input-port skip-k])extends the standardwith an optional argument (defaulting topeek-char0) that represents the number of bytes (not characters) to skip.(peek-byte[input-port skip-k])is analogous to, but it reads and returns a byte instead of a character.peek-char(peek-char-or-special[input-port skip-k])is the same as, except that if the input port returns a non-byte value afterpeek-charskip-kbyte positions, it is returned.(peek-byte-or-special[input-port skip-k progress-evt])is analogous to, but it reads and returns a byte instead of a character, and it supports apeek-char-or-specialprogress-evtargument (which is#fby default) like.peek-bytes-avail!(port-progress-evt[input-port])returns an event that becomes ready after any subsequent read frominput-port, or afterinput-portis closed. After the event becomes ready, it remains ready. If progress events are unavailable forinput-port(as reported byport-provides-progress-evts?) theexn:fail:contractexception is raised.(port-provides-progress-evts?input-port)returns#tifport-progress-evtcan return an event forinput-port. All built-in kinds of ports support progress events, but ports created withmake-input-port(see section 11.1.7) may not.(port-commit-peekedk progress-evt evt[input-port])attempts to commit as read the firstkpreviously peeked bytes, non-byte specials, ands fromeofinput-port, or the firstor special value peeked fromeofinput-port.32 The read commits only ifprogress-evtdoes not become ready first (i.e., if no other process reads frominput-portfirst), and only ifevtis chosen by awithinsyncport-commit-peeked(in which case the event result is ignored); theevtmust be either a channel-put event, channel, semaphore, semaphore-peek event, always event, or never event. Suspending the thread that callsport-commit-peekedmay or may not prevent the commit from proceeding. The result fromport-commit-peekedis#tif data is committed, and#fotherwise.If no data has been peeked from
input-portandprogress-evtis not ready, thenexn:fail:contractexception is raised. If fewer thankitems have been peeked at the current start ofinput-port's stream, then only the peeked items are committed as read. Ifinput-port's stream currently starts at anor a non-byte special value, then only theeofor special value is committed as read.eofIf
progress-evtis not a result ofport-progress-evtapplied toinput-port, thenexn:fail:contractexception is raised.
11.2.1.1 Counting Positions, Lines, and Columns
By default, MzScheme keeps track of the position in a port
as the number of bytes that have been read from or written to any
port (independent of the read/write position, which is accessed or
changed with ). Optionally, however, MzScheme
can track the position in terms of characters (after UTF-8 decoding),
instead of bytes, and it can track line locations and
column locations; this optional tracking must be
specifically enabled for a port via file-position or
the port-count-lines! parameter (see
section 7.9.1.2). Position, line, and column
locations for a port are used by port-count-lines-enabled (see
section 12.2 for more information) and
read-syntaxread-honu-syntax. Position and line locations are numbered
from 1; column locations are numbered from 0.
(port-count-lines!port)turns on line and column counting for a port. Counting can be turned on at any time, though generally it is turned on before any data is read from or written to a port. When a port is created, if the value of theport-count-lines-enabledparameter is true (see section 7.9.1.2), then line counting is automatically enabled for the port. Line counting cannot be disabled for a port after it is enabled.
When counting lines, MzScheme treats linefeed, return, and return-linefeed combinations as a line terminator and as a single position (on all platforms). Each tab advances the column count to one before the next multiple of 8. When a sequence of bytes in the range 128 to 253 forms a UTF-8 encoding of a character, the position/column is incremented is incremented once for each byte, and then decremented appropriately when a complete encoding sequence is discovered. See also section 11.1 for more information on UTF-8 decoding for ports.
A position is known for any port as long as its value can be expressed as a fixnum (which is more than enough tracking for realistic applications in, say, syntax-error reporting). If the position for a port exceeds the value of the largest fixnum, then the position for the port becomes unknown, and line and column tacking is disabled. Return-linefeed combinations are treated as a single character position only when line and column counting is enabled.
(port-next-locationport)returns three values: a positive exact integer or#ffor the line number of the next read/written item, a non-negative exact integer or#ffor the next item's column, and a positive exact integer or#ffor the next item's position. The next column and position normally increases as bytes are read from or written to the port, but if line/character counting is enabled forport, the column and position results can decrease after reading or writing a byte that ends a UTF-8 encoding sequence.
Certain kinds of exceptions (see section 6.1) encapsulate
source-location information using a srcloc structure, which
has five fields:
source-- An arbitrary value identifying the source, often a path (see section 11.3.1).line-- The line number, a positive exact integer (counts from 1) or#f(unknown).column-- The column number, a non-negative exact integer (counts from 0) or#f(unknown).position-- The starting position, a positive exact integer (counts from 1) or#f(unknown).span-- The number of covered positions, a non-negative exact integer (counts from 0) or#f(unknown).
The fields of a srcloc structure are immutable, so no
field-mutator procedures are defined for srcloc. The
srcloc structure type is transparent to all inspectors (see
section 4.5).
11.2.2 Writing Bytes, Characters, and Strings
In addition to the standard printing procedures, MzScheme provides
byte-writing procedures, block-writing procedures such as
write-string, and more.
(write-stringstring[output-port start-k end-k])write characters tooutput-portfromstringstarting from indexstart-k(inclusive) up toend-k(exclusive). The default value ofoutput-portis the current output port. The default value ofstart-kis0. The default value ofend-kis the length of thestring. Like, thesubstringexn:fail:contractexception is raised ifstart-korend-kis out-of-range forstring.The result is the number of characters written to
output-port, which is always(- end-k start-k).(write-bytesbytes[output-port start-k end-k])is analogous towrite-string, but it writes a byte string.(write-bytes-availbytes[output-port start-k end-k])is likewrite-bytes, but it returns without blocking after writing as many bytes as it can immediately flush. It blocks only if no bytes can be flushed immediately. The result is the number of bytes written and flushed tooutput-port; ifstart-kis the same asend-k, then the result can be0(indicating a successful flush of any buffered data), otherwise the result is at least1but possibly less than(- end-k start-k).The
write-bytes-availprocedure never drops bytes; ifwrite-bytes-availsuccessfully writes some bytes and then encounters an error, it suppresses the error and returns the number of written bytes. (The error will be triggered by future writes.) If an error is encountered before any bytes have been written, an exception is raised.(write-bytes-avail*bytes[output-port start-k end-k])is likewrite-bytes-avail, except that it never blocks, it returns#fif the port contains buffered data that cannot be written immediately, and it returns0if the port's internal buffer (if any) is flushed but no additional bytes can be written immediately.(write-bytes-avail/enable-breakbytes[input-port start-k end-k])is likewrite-bytes-avail, except that breaks are enabled during the write. The procedure provides a guarantee about the interaction of writing and breaks: if breaking is disabled whenwrite-bytes-avail/enable-breakis called, and if theexn:breakexception is raised as a result of the call, then no bytes will have been written tooutput-port. See also section 6.6.(write-bytebyte[output-port])is analogous towrite-char, but for writing a byte instead of a character.(write-specialv[output-port])writesvdirectly tooutput-portif it supports special writes, or raisesexn:fail:contractif the port does not support special write. The result is always#t, indicating that the write succeeded.(write-special-avail*v[output-port])is likewrite-special, but without blocking. Ifvcannot be written immediately, the result is#fwithout writingv, otherwise the result is#tandvis written.(write-bytes-avail-evtbytes[output-port start-k end-k])is similar towrite-bytes-avail, but instead of writing bytes immediately, it returns a synchronizable event (see section 7.7). Theoutput-portmust support atomic writes, as indicated byport-writes-atomic?.Synchronizing on the object starts a write from
bytes, and the event becomes ready when bytes are written (unbuffered) to the port. Ifstart-kandend-kare the same, then the synchronization result is0when the port's internal buffer (if any) is flushed, otherwise the result is a positive exact integer. If the event is not selected in a synchronization, then no bytes will have been written tooutput-port.(write-special-evtv[output-port])is similar towrite-special, but instead of writing the special value immediately, it returns a synchronizable event (see section 7.7). Theoutput-portmust support atomic writes, as indicated byport-writes-atomic?.Synchronizing on the object starts a write of the special value, and the event becomes ready when the value is written (unbuffered) to the port. If the event is not selected in a synchronization, then no value will have been written to
output-port.(port-writes-atomic?output-port)returns#tifwrite-bytes-avail/enable-breakcan provide an exclusive-or guarantee (break or write, but not both) foroutput-port, and if the port can be used with procedures likewrite-bytes-avail-evt. MzScheme's file-stream ports, pipes, string ports, and TCP ports all support atomic writes; ports created withmake-output-port(see section 11.1.7) may support atomic writes.(port-writes-special?output-port)returns#tif procedures likewrite-specialcan write arbitrary values to the port. MzScheme's file-stream ports, pipes, string ports, and TCP ports all reject special values, but ports created withmake-output-port(see section 11.1.7) may support them.
11.2.3 Writing Structured Data
The procedure is used to print Scheme values in a
context where a programmer expects to see a value:
print
(printv[output-port])outputsvtooutput-port. The default value ofoutput-portis the current output port.
The rationale for providing is that print
and display both have standard output conventions, and this
standardization restricts the ways that an environment can change the
behavior of these procedures. No output conventions should be assumed
for write so that environments are free to modify the actual
output generated by print in any way. Unlike the port
display and write handlers, a global port print handler can be
installed through the printglobal-port-print-handler parameter
(see section 7.9.1.2).
The , fprintf, and printf procedures
create formatted output:
format
(fprintfoutput-port format-string v···)prints formatted output tooutput-port, whereformat-stringis a string that is printed;format-stringcan contain special formatting tags:~n or~% prints a newline~a or~As the next argument among thedisplayvs~s or~Ss the next argument among thewritevs~v or~Vs the next argument among theprintvs~e or~E outputs the next argument among thevs using the current error value conversion handler (see section 7.9.1.7) and current error printing width~c or~Cs the next argument inwrite-charvs; if the next argument is not a character, theexn:fail:contractexception is raised~b or~B prints the next argument among thevs in binary; if the next argument is not an exact number, theexn:fail:contractexception is raised~o or~O prints the next argument among thevs in octal; if the next argument is not an exact number, theexn:fail:contractexception is raised~x or~X prints the next argument among thevs in hexadecimal; if the next argument is not an exact number, theexn:fail:contractexception is raised~~ prints a tilde (~)~w, wherewis a whitespace character, skips characters informat-stringuntil a non-whitespace character is encountered or until a second end-of-line is encountered (whichever happens first). An end-of-line is either#\return,#\newline, or#\returnfollowed immediately by#\newline(on all platforms).
The return value is void.
(printfformat-string v···)same aswith the current output port.fprintf(formatformat-string v···)same aswith a string output port where the final string is returned as the result.fprintf
When an illegal format string is supplied to one of these procedures,
the exn:fail:contract exception is raised. When the format string requires more
additional arguments than are supplied, the
exn:fail:contract exception is raised. When more additional arguments are supplied
than are used by the format string, the exn:fail:contract exception is raised.
For example,
(fprintf port "~a as a string is ~s.~n" '(3 4) "(3 4)")
prints this message to port:33
(3 4) as a string is "(3 4)".
followed by a newline.
11.2.4 Default Reader
MzScheme's input parser obeys the following non-standard rules. See also section 11.2.8 for information on configuring the input parser through a readtable.
Square brackets (``['' and ``]'') and curly braces (``{'' and ``}'') can be used in place of parentheses. An open square bracket must be closed by a closing square bracket and an open curly brace must be closed by a closing curly brace. Whether square brackets are treated as parentheses is controlled by the
read-square-bracket-as-parenparameter (see section 7.9.1.3). Similarly, the parsing of curly braces is controlled with theread-curly-brace-as-parenparameter. When square brackets and curly braces are not treated as parentheses, they are disallowed as input. By default, square brackets and curly braces are treated as parentheses.Vector constants can be unquoted, and a vector size can be specified with a decimal integer between the
#and opening parenthesis. If the specified size is larger than the number of vector elements that are provided, the last specified element is used to fill the remaining vector slots. For example,#4(1 2)is equivalent to#(1 2 2 2). If no vector elements are specified, the vector is filled with0. If a vector size is provided and it is smaller than the number of elements provided, theexn:fail:readexception is raised.Boxed constants can be created using
#&. The datum following#&is treated as a quoted constant and put into the new box. (Space and comments following the#&are ignored.) Box reading is controlled with theread-accept-boxboolean parameter (see section 7.9.1.3). Box reading is enabled by default. When box reading is disabled and#&is provided as input, theexn:fail:readexception is raised.Expressions beginning with
#'are wrapped withsyntaxin the same way that expressions starting with'are wrapped withquote. Similarly,#`generatesquasisyntax,#,generatesunsyntax, and#,@generatesunsyntax-splicing. See also section 12.2.1.2.The following character constants are recognized:
Whenever
#\is followed by at least two alphabetic characters, characters are read from the input port until the next non-alphabetic character is returned. If the resulting string of letters does not match one of the above constants (case-insensitively), theexn:fail:readexception is raised.Character constants can also be specified through direct Unicode values in octal notation (up to 255):
#\wheren1n2n3n1is in the range [0,3] andn2andn3are in the range [0,7]. Whenever#\is followed by at least two characters in the range [0,7], the next character must also be in this range, and the resulting octal number must be in the range 0008 to 3778.Finally, character constants can be specified through direct Unicode values in hexadecimal notation:
#\uorn1...nk#\U, where eachn1...nkniis a hexadecimal digit (0-9, a-f, or A-F), and k is no more than 4 for#\uor 6 for#\U. Whenever#\is followed by auorUand one hexadecimal digit, the character constant is terminated by either the first non-hexadecimal character in the stream, or the fourth/sixth hexadecimal character, whichever comes first. The resulting hexadecimal number must be a valid argument tointeger->char, otherwise theexn:fail:readexception is raised.Unless otherwise specified above, character-constants are terminated after the character following
#\. For example, if#\is followed by an alphabetic character other thanuand then a non-alphabetic character, then the character constant is terminated. If#\is followed by a8or9, then the constant is terminated. If#\is followed by a non-alphabetic, non-decimal-digit character then the constant is terminated.Within string constants, the following escape sequences are recognized in addition to \" and \\:
-
\': quote (i.e., the backslash has no effect)
\
o, \oo, or \ooo: Unicode for octalo,oo, orooo, where eachois0,1,2,3,4,5,6, or7. The \oooform takes precedence over the \ooform, and \ootakes precedence over \o.\x
hor \xhh: Unicode for hexadecimalhorhh, where eachhis0,1,2,3,4,5,6,7,a,A,b,B,c,C,d,D,e,E,f, orF. The \xhhform takes precedence over the \xhform.\u
h, \uhh, \uhhh, or \uhhhh: like \x, but with up to four hexadecimal digits (longer sequences take precedence). The resulting hexadecimal number must be a valid argument tointeger->char, otherwise theexn:fail:readexception is raised.\U
h, \Uhh, \Uhhh, \Uhhhh, \Uhhhhh, \Uhhhhhh, \Uhhhhhhh, or \Uhhhhhhhh: like \x, but with up to eight hexadecimal digits (longer sequences take precedence). The resulting hexadecimal number must be a valid argument tointeger->char, otherwise theexn:fail:readexception is raised.
Furthermore, a backslash followed by a linefeed, carriage return or return-linefeed combination is elided, allowing string constants to span lines. Any other use of backslash within a string constant is an error.
A string constant preceded by
#is a byte-string constant. Byte string constants support the same escape sequences as character strings except \u and \U.The sequence
#<<starts a here string. The characters following#<<until a newline character define a terminator for the string. The content of the string includes all characters between the#<<line and a line whose only content is the specified terminator. More precisely, the content of the string starts after a newline following#<<, and it ends before a newline that is followed by the terminator, where the terminator is itself followed by either a newline or end-of-file. No escape sequences are recognized between the starting and terminating lines; all characters are included in the string (and terminator) literally. A return character is not treated as a line separator in this context. If no characters appear between#<<and a newline or end-of-file, or if an end-of-file is encountered before a terminating line, theexn:fail:readexception is raised.The syntax for numbers is extended as described in section 3.3. Numbers containing a decimal point or exponent (e.g.,
1.3,2e78) are normally read as inexact. If theread-decimal-as-inexactparameter is set to#f, then such numbers are instead read as exact. The parameter does not affect the parsing of numbers with an explicit exactness tag (#eor#i).A parenthesized sequence containing two delimited dots (``
.'') triggers infix parsing. A singledatummust appear between the dots, and one or moredatums must appear before the first dot and after the last dot:The resulting list consists of the(left-datum ···1 . first-datum . right-datum ···1)
datumbetween the dots, followed by the remainingdatums in order:Consequently, the input expression(first-datum left-datum ···1 right-datum ···1)
(1 . < . 2)produces#t, and(1 2 . + . 3 4 5)produces15.When the
read-accept-dotparameter is set to#f, then a delimited dot (``.'') is disallowed in input. When theread-accept-quasiquoteparameter is set to#f, then a backquote or comma is disallowed in input. These modes simplify Scheme's input model for students.MzScheme's identifier and symbol syntax is considerably more liberal than the syntax specified by R5RS. When input is scanned for tokens, the following characters delimit an identifier in addition to whitespace:
" , ' ` ; ( ) [ ] { }
In addition, an identifier cannot start with a hash mark (``
#'') unless the hash mark is immediately followed by a percent sign (``%''). The only other special characters are backslash (``\'') and quoting vertical bars (``|''); any other character is used as part of an identifier.Symbols containing special characters (including delimiters) are expressed using an escaping backslash (``\'') or quoting vertical bars (``|''):
A backslash preceding any character includes that character in the symbol literally; double backslashes produce a single backslash in the symbol.
Characters between a pair of vertical bars are included in the symbol literally. Quoting bars can be used for any part of a symbol, or the whole symbol can be quoted. Backslashes and quoting bars can be mixed within a symbol, but a backslash is not a special character within a pair of quoting bars.
Characters quoted with a backslash or a vertical bar always preserve their case, even when identifiers are read case-insensitively.
An input token constructed in this way is an identifier when it is not a numerical constant (following the extended number syntax described in section 3.3). A token containing a backslash or vertical bars is never treated as a numerical constant.
Examples:
(produces the same symbol asquotea\(b)(.string->symbol"a(b")(produces the same symbol asquoteA\B)(when identifiers are read without case-sensitivity.string->symbol"aB")(,quotea\ b)(, andquote|a b|)(all produce the same symbol asquotea| |b)(.string->symbol"a b")(is the same asquote|a||b|)(, which produces the same symbol asquote|ab|)(.string->symbol"ab")(is the number 10, butquote10)(produces the same symbol asquote|10|)(.string->symbol"10")
Whether a vertical bar is used as a special or normal symbol character is controlled with the
read-accept-bar-quoteboolean parameter (see section 7.9.1.3). Vertical bar quotes are enabled by default. Quoting backslashes cannot be disabled.By default, symbols are read case-sensitively. Case sensitivity for reading can be controlled in three ways:
Quoting part of a symbol with an escaping backslash (``\'') or quoting vertical bar (``|'') always preserves the case of the quoted portion, as described above.
The sequence
#cscan be used as a prefix for any expression to make reading symbols within the expression case-sensitive. A#ciprefix similarly makes reading symbols in an expression case-insensitive. Whitespace can appear between a#csor#ciprefix and its expression, and prefixes can be nested. Backslash and vertical-bar quotes override a#ciprefix.When the
read-case-sensitiveparameter (see section 7.9.1.3) is set to#t, then case is preserved when reading symbols. The default is#t, and it is set to#twhile loading a module (see section 5.8). A#csor#ciprefix overrides the parameter setting, as does backslash or vertical-bar quoting.
Symbol case conversions are not sensitive to the current locale (see section 1.2.2).
A symbol-like expression that starts with an unquoted hash and colon (``#:'') is parsed as a keyword constant. After the leading colon, backslashes, vertical bars, and case sensitivity are handled as for symbols, except that a keyword expression can never be interpreted as a number.
Expressions of the form
#rxare literal regexp values (see section 10) wherestringstringis a string constant. The regexp produced by#rxis the same as produced bystring(regexp. Ifstring)stringis not a valid pattern, theexn:fail:readexception is raised.Expressions of the form
#rx#are similarly literal byte-regexp values. The regexp produced bystring#rx#is the same as produced bystring(byte-regexp #.string)Expressions of the form
#hash((are literal immutable hash tables. The hash table maps eachkey-datum.val-datum) ···)key-datumto itsval-datum, comparing keys with. The table is constructed by adding eachequal?key-datummapping from left to right, so later mappings can hide earlier mappings if thekey-datums are. An expression of the formequal?#hasheq((produces an immutable hash table with keys compared usingkey-datum.val-datum) ···). If the value ofeq?read-square-bracket-as-parenparameter (see section 7.9.1.3) is true, matching parentheses in a#hashor#hasheqconstant can be replaced by matching square brackets. Similarly, matching curly braces can be used ifread-curly-brace-as-parenis true.Values with shared structure are expressed using
#andn=#, wheren#nis a decimal integer. See section 11.2.5.1.Expressions of the form
#%are symbols, wherexxcan be a symbol or a number.Expressions beginning with
#~are interpreted as compiled MzScheme code. See section 14.3.Multi-line comments are started with
#|and terminated with|#. Comments of this form can be nested arbitrarily.A
#;comments out the next datum. Whitespace and comments (including#;comments) may appear between the#;and the commented-out datum. Graph-structure annotations with#andn=#work within the comment as if the datum were not commented out (e.g., bindings can be introduced withn##for use in parts of the datum that are not commented out). Whenn=#;appears at the beginning of a top-level datum, however, graph-structure bindings are discarded (along with the first following datum) before reading the second following datum.If the first line of a
ed file begins withload#!, it is ignored by the default load handler. If an ignored line ends with a backslash (``\''), then the next line is also ignored. (The#!convention is for shell scripts; see Chapter 18 for details.)A
#hxshifts the reader into H-expression mode (see section 19) for one H-expression. A#sxhas no effect in normal mode, but in H-expression mode, it shifts the reader back to (normal) S-expression mode. Theread-honuandread-honu-syntaxprocedures read as if the stream starts with#hx.A
#honushifts the reader into H-expression mode (see section 19) and reads repeatedly until an end-of-file is encountered. The H-expression results are wrapped in a module-formed S-expression, as described in section 19.A
#readermust be followed by a datum. The datum is passed to the procedure that is the value of thecurrent-reader-guardparameter (see section 7.9.1.3), and the result is used as a module path. The module path is passed todynamic-require(see section 5.5) with either'reador'read-syntax(depending on whether parsing started withreadorread-syntax). The resulting procedure should accept the same arguments asreadorread-syntax(with all optional arguments as required). The procedure is given the port whose stream contained#reader, and it should produce a datum result. If the result is a syntax object inreadmode it is converted to a datum usingsyntax-object->datum; if the result is not a syntax object inread-syntaxmode, it is converted to one usingdatum->syntax-object. See also section 11.2.9.1 and section 11.2.9.2 for information on special-comment results and recursive reads. If theread-accept-readerparameter is set to#f, then#readeris disallowed as input.
Reading from a custom port can produce arbitrary values generated by
the port; see section 11.1.7 for details. If the port generates
a non-character value in a position where a character is required
(e.g., within a string), the exn:fail:read:non-char exception is raised.
11.2.5 Default Printer
MzScheme's printer obeys the following non-standard rules (though the
rules for do not apply when the printprint-honu
parameter is set to #t; see section 7.9.1.4).
A vector can be printed by
andwriteusing the shorthand described in section 11.2.4, where the vector's length is printed between the leadingprint#and the opening parenthesis and repeated tail elements are omitted. For example,#(1 2 2 2)is printed as#4(1 2). Theprocedure does not output vectors using this shorthand. Shorthand vector printing is controlled with thedisplayprint-vector-lengthboolean parameter (see section 7.9.1.4). Shorthand vector printing is enabled by default.Boxes (see section 3.11) can be printed with the
#¬ation (see section 11.2.4). When box printing is disabled, all boxes are printed unably asread#<box>. Box printing is controlled with theprint-boxboolean parameter (see section 7.9.1.4). Box printing is enabled by default.Structures (see Chapter 4) can be printed using either a custom-write procedure or vector notation. See section 11.2.10 for information on custom-write procedures; the following information applies only when no custom-write procedure is specified. In the vector form of output, the first item is a symbol of the form
struct:-- wheressis the name of the structure -- and the remaining elements are the elements of the structure, but the vector exposes only as much information about the structure as the current inspector can access (see section 4.5). When structure printing is disabled, or when no part of the structure is accessible to the current inspector, a structure is printed unably asread#<struct:. Structure printing is controlled with thes>print-structboolean parameter (see section 7.9.1.4). Structure printing is disabled by default.Symbols containing spaces or special characters
using escaping backslashes and quoting vertical bars. When thewriteread-case-sensitiveparameter is set to#f, then symbols containing uppercase characters also use escaping backslashes or quoting vertical bars. In addition, symbols are quoted with vertical bars or a leading backslash when they would otherwise print the same as a numerical constant. If the value of theread-accept-bar-quoteboolean parameter is#f(see section 7.9.1.3), then backslashes are always used to escape special characters instead of quoting them with vertical bars, and a vertical bar is not treated as a special character. Otherwise, quoting bars are used in printing when bar at the beginning and one at the end suffices to correctly print the symbol. See section 11.2.4 for more information about symbol parsing. Symbolswithout escaping or quoting special characters.displayKeywords
writeanddisplaythe same as symbols, except with a leading hash and colon, and without special handing when the printed form matches a number (since the leading#:distinguishes the keyword).Characters with the special names described in section 11.2.4
using the same name. (Some characters have multiple names; thewrite#\newlineand#\nulnames are used instead of#\linefeedand#\null). Other graphic characters (according tochar-graphic?; see section 3.4)aswrite#\followed by the single character, and all others characters are written in#\unotation with four digits or#\Unotation with eight digits (using the latter only if the character value it does not fit in four digits). All charactersas a single character.displayStrings containing non-graphic, non-blank characters (according to
char-graphic?andchar-blank?; see section 3.4)using the escape sequences described in section 11.2.4, using \a, \b, \t, \n, \v, \f, \r, or \e if possible, otherwise using \u with four hexadecimal digits or \U with eight hexadecimal digits (using the latter only if the character value does not fit into four digits). All stringswriteas their literal character sequences.displayByte strings
usingwrite#", where each byte in the string content is written using the corresponding ASCII decoding if the byte is between 0 and 127 and the character is graphic or blank (according tochar-graphic?andchar-blank?; see section 3.4). Otherwise, the byte is written using \a, \b, \t, \n, \v, \f, \r, or \e if possible, otherwise using \o with one to three octal digits (only as many as necessary). All stringsas their literal byte sequence; this byte sequence may not be a valid UTF-8 encoding, so it may not correspond to a sequence of characters.displayRegexp values print using the form
#rx, wherestringstringis theform of the regexp's source character string or byte string. Similarly, byte-regexp values print starting withwrite#rx#.Paths
like other unwriteable values usingread#<path:...>, but a paths like the result ofdisplaypath->stringapplied to the path.When the
print-hash-tableparameter is set to true (see section 7.9.1.4), hash tables print using the form#hash((orkey.val) ···)#hasheq((for tables usingkey.val) ···)orequal?key comparisons, respectively. If theeq?print-hash-tableparameter's value is#f, hash tables print unably asread#<hash-table>. Hash tables with weakly held keys always print unably asread#<hash-table>.Values with shared structure can be printed using
#andn=#, wheren#nis a decimal integer. See section 11.2.5.1.A value with no
able format prints asread#<...>, but only when theprint-unreadableparameter is set to#t(the default; see also section 7.9.1.4). When the parameter's value is#f, attempting to print an unable value raisesreadexn:fail:contract.
11.2.5.1 Sharing Structure in Input and Output
MzScheme can read and print Common LISP-style graphs, values
with shared structure (including cycles). Graphs are described by
tagging the shared structure once with # (using
some decimal integer n=n with no more than eight digits) and then
referencing it later with # (using the same number
n#n). For example, the following datum represents the infinite
list of ones:
#0=(1 . #0#)
If this graph is entered into MzScheme's read-eval-print loop, MzScheme's compiler
will loop forever, trying to compile an infinite expression. In
contrast, the following expression defines ones to the
infinite list of ones, using quote to hide the infinite list
from the compiler:
(define ones (quote #0=(1 . #0#)))
A tagged structure can be referenced multiple
times. Here, v is defined to be a vector containing the same
cell in all three slots:
cons
(define v #(#1=(cons 1 2) #1# #1#))
A tag # must appear to the left of all references
n=#, and all references must appear in the same
top-level datum as the tag. By default, MzScheme's printer will
display a value without showing the shared structure:
n#
#((1 . 2) (1 . 2) (1 . 2))
Graph reading and printing are controlled with the
read-accept-graph and print-graph boolean
parameters (see section 7.9.1.4). Graph reading is enabled by
default, and graph printing is disabled by default. However, when the
printer encounters a graph containing a cycle, graph
printing is automatically enabled, temporarily. (For this reason, the
display, write, and print procedures
require memory proportional to the depth of the value being printed.)
When graph reading is disabled and a graph is provided as input, the
exn:fail:read exception is raised.
If the n in a # form or a n=# form
contains more than eight digits, the n#exn:fail:read exception is raised. If a
# form is not preceded by a n## form
using the same n=n, the exn:fail:read exception is raised. If two
# forms are in the same expression for the same
n=n, the exn:fail:read exception is raised.
11.2.6 Replacing the Reader
Each input port has its own port read handler. This handler
is invoked to read from the port when the built-in or
read procedure is applied to the port.34 A port read handler is applied to
either one argument or two arguments:
read-syntax
A single argument is supplied when the port is used with
; the argument is the port being read. The return value is the value that was read from the port.readTwo arguments are supplied when the port is used with
; the first argument is the port being read, and the second argument is a value indicating the source. The return value is a syntax object that was read from the port.read-syntax
A port's read handler is configured with :
port-read-handler
(port-read-handlerinput-port)returns the current port read handler forinput-port.(port-read-handlerinput-port proc)sets the handler forinput-porttoproc.
The default port read handler reads standard Scheme expressions with MzScheme's built-in parser (see section 11.2.4). It handles a special result from a custom input port (see section 11.1.7.1) by treating it as a single expression, except that special-comment values (see section 11.2.9.1) are treated as whitespace.
The read and read-syntax procedures themselves can
be customized through a readtable; see section 11.2.8 for more
information.
11.2.7 Replacing the Printer
Each output port has its own port display handler,
port write handler, and port print
handler. These handlers are invoked to output to the port when the
standard , display or write procedure
is applied to the port. A port display/write/print handler takes a
two arguments: the value to be printed and the destination port. The
handler's return value is ignored.print
(port-display-handleroutput-port)returns the current port display handler foroutput-port.(port-display-handleroutput-port proc)sets the display handler foroutput-porttoproc.(port-write-handleroutput-port)returns the current port write handler foroutput-port.(port-write-handleroutput-port proc)sets the write handler foroutput-porttoproc.(port-print-handleroutput-port)returns the current port print handler foroutput-port.(port-print-handleroutput-port proc)sets the print handler foroutput-porttoproc.
The default port display and write handlers print Scheme expressions
with MzScheme's built-in printer (see section 11.2.5). The default
print handler calls the global port print handler (the value of the
global-port-print-handler parameter; see
section 7.9.1.2); the default global port print
handler is the same as the default write handler.
11.2.8 Customizing the Reader through Readtables
A readtable configures MzScheme's built-in reader by adjusting the way that individual characters are parsed. MzScheme readtables are just like readtables in Common LISP, except that an individual readtable is immutable, and the procedures for creating and inspecting readtables are somewhat different than the Common LISP procedures.
In many contexts, #f identifies the default readtable for
MzScheme. In particular, #f is the initial value for the
current-readtable parameter (see
section 7.9.1.3), which causes the reader to behave as
described in section 11.2.4. Adjust MzScheme's default reader by
setting the current-readtable parameter to a readtable
created with make-readtable.
(make-readtable readtable [char-or-false symbol-or-char
readtable-or-proc ···1]) creates a new readtable that is like
readtable (which can be #f), except that the reader's
behavior is modified for each char according to the given
symbol-or-char and readtable-or-proc. The ···1 for
make-readtable applies to all three of char,
symbol-or-char, and readtable-or-proc; in other words,
the total number of arguments to make-readtable must be one
modulo three.
The possible combinations for char-or-false, symbol-or-char, and
readtable-or-proc are as follows:
char'terminating-macroproc-- causescharto be parsed as a delimiter, and an unquoted/uncommentedcharin the input string triggers a call to the reader macroproc; the activity ofprocis described further below. Conceptually, characters like semi-colon (``;'') and parentheses are mapped to terminating reader macros in the default readtable.char'non-terminating-macroproc-- like the'terminating-macrovariant, butcharis not treated as a delimiter, so it can be used in the middle of an identifier. Conceptually, hash (``#'') is mapped to a non-terminating macro in the default readtable.char'dispatch-macroproc-- like the'non-terminating-macrovariant, butcharonly when it follows a hash (``#'') -- or, more precisely, when the character follows one that has been mapped to the behavior of hash in the default readtable.charlike-charreadtable-- causescharto be parsed in the same way thatlike-charis parsed inreadtable, wherereadtablecan be#fto indicate the default readtable. Mapping a character to the same actions as vertical bar (``|'') in the default reader means that the character starts quoting for symbols, and the same character terminates the quote; in contrast, mapping a character to the same action as a parenthesis or double quote means that the character starts a list or string, but the list or string is still terminated with a closing parenthesis or double quote. Finally, mapping a character to an action in the default readtable means that the character's behavior is sensitive to parameters that affect the original character; for example, mapping a character to the same action is a curly brace (``{'') in the default readtable means that the character is disallowed when theread-curly-brace-as-parenparameter is set to#f.#f'non-terminating-macroproc-- replaces the macro used to parse characters with no specific mapping: i.e., characters (other than hash or vertical bar) that can start a symbol or number with the default readtable.
If multiple 'dispatch-macro mappings are provided for a
single char-or-false, all but the last one are
ignored. Similarly, if multiple non-'dispatch-macro mappings
are provided for a single char-or-false, all but the last one
are ignored.
A reader macro proc must accept six arguments, and it can
optionally accept two arguments. See section 11.2.9 for
information on the procedure's arguments and results.
A reader macro normally reads characters from the given input port to
produce a value to be used as the ``reader macro-expansion'' of the
consumed characters. The reader macro might produce a special-comment
value to cause the consumed character to be treated as whitespace,
and it might use read/recursive or
read-syntax/recursive; see section 11.2.9.1 and
section 11.2.9.2 for more information on these topics.
(readtable-mapping readtable char), where readtable is not
#f, produces information about the mappings in
readtable for char. The result is three values:
either a character (mapping is to same behavior as the character in the default readtable),
'terminating-macro, or'non-terminating-macro; this result reports the main (i.e., non-'dispatch-macro) mapping forchar. When the result is a character, thencharis mapped to the same behavior as the returned character in the default readtable.either
#for a reader-macro procedure; the result is a procedure when the first result is'terminating-macroor'non-terminating-macro.either
#for a reader-macro procedure; the result is a procedure when the character has a'dispatch-macromapping inreadtableto override the default dispatch behavior.
Note that reader-macro procedures for the default readtable are not
directly accessible. To invoke default behaviors, use
read/recursive or read-syntax/recursive (see
section 11.2.9.2) with a character and the #f
readtable.
Extended example:
;; Providesraise-read-errorandraise-read-eof-error(require (lib "readerr.ss" "syntax")) (define (skip-whitespace port) ;; Skips whitespace characters, sensitive to the current ;; readtable's definition of whitespace (let ([ch (peek-charport)]) (unless (eof-object? ch) ;; Consult current readtable: (let-values ([(like-ch/sym proc dispatch-proc) (readtable-mapping (current-readtable) ch)]) ;; If like-ch/sym is whitespace, then ch is whitespace (when (and (char? like-ch/sym) (char-whitespace?like-ch/sym)) (read-charport) (skip-whitespace port)))))) (define (skip-comments read-one port src) ;; Recursive read, but skip comments and detect EOF (let loop () (let ([v (read-one)]) (cond [(special-comment? v) (loop)] [(eof-object? v) (let-values ([(l c p) (port-next-locationport)]) (raise-read-eof-error "unexpected EOF in tuple" src l c p 1))] [else v])))) (define (parse port read-one src) ;; First, check for empty tuple (skip-whitespace port) (if (eq?#\> (peek-charport))null(let ([elem (read-one)]) (if (special-comment? elem) ;; Found a comment, so look for > again (parse port read-one src) ;; Non-empty tuple: (conselem (parse-nonempty port read-one src)))))) (define (parse-nonempty port read-one src) ;; Need a comma or closer (skip-whitespace port) (case (peek-charport) [(#\>) (read-charport) ;; Donenull] [(#\,) (read-charport) ;; Read next element and recur (cons(skip-comments read-one port src) (parse-nonempty port read-one src))] [else ;; Either a comment or an error; grab location (in case ;; of error) and read recursively to detect comments (let-values ([(l c p) (port-next-locationport)] [(v) (read-one)]) (cond [(special-comment? v) ;; It was a comment, so try again (parse-nonempty port read-one src)] [else ;; Wasn't a comment, comma, or closer; error ((if (eof-object? v) raise-read-eof-error raise-read-error) "expected `,' or `>'" src l c p 1)]))])) (define (make-delims-table) ;; Table to use for recursive reads to disallow delimiters ;; (except those in sub-expressions) (letrec ([misplaced-delimiter (case-lambda [(ch port) (unexpected-delimiter ch port #f #f #f #f)] [(ch port src line col pos) (raise-read-error (format"misplaced `~a' in tuple" ch) src line col pos 1)])]) (make-readtable(current-readtable) #\, 'terminating-macro misplaced-delimiter #\> 'terminating-macro misplaced-delimiter))) (define (wrap l) `(make-tuple (list,@l))) (define parse-open-tuple (case-lambda [(ch port) ;; `read' mode (wrap (parse port (lambda () (read/recursiveport #f (make-delims-table))) (object-nameport)))] [(ch port src line col pos) ;; `read-syntax' mode (datum->syntax-object#f (wrap (parse port (lambda () (read-syntax/recursivesrc port #f (make-delims-table))) src)) (let-values ([(l c p) (port-next-locationport)]) (listsrc line col pos (and pos (- p pos)))))])) (define tuple-readtable (make-readtable#f #\< 'terminating-macro parse-open-tuple)) (parameterize ([current-readtabletuple-readtable]) (read(open-input-string"<1 , 2 , \"a\">"))) ;; =>'(make-tuple ((parameterize ([list1 2 "a"))current-readtabletuple-readtable]) (read(open-input-string"< #||# 1 #||# , #||# 2 #||# , #||# \"a\" #||# >"))) ;; =>'(make-tuple ((define tuple-readtable+ (list1 2 "a"))make-readtabletuple-readtable #\* 'terminating-macro (lambda a (make-special-comment #f)) #\_ #\space #f)) (parameterize ([current-readtabletuple-readtable+]) (read(open-input-string"< * 1 __,__ 2 __,__ * \"a\" * >"))) ;; =>'(make-tuple (list1 2 "a"))
11.2.9 Reader-Extension Procedures
MzScheme's reader can be extended in three ways: through a
reader-macro procedure in a readtable (see section 11.2.8),
through a #reader form (see section 11.2.4), or through a
custom-port byte reader that returns a ``special'' result procedure
(see section 11.1.7.1). All three kinds of procedures accept
similar arguments, and their results are treated in the same way by
read and read-syntax (or, more precisely, by the
default read handler; see section 11.2.6).
Calls to these reader-extension procedures can be triggered through
read, read/recursive, read-syntax, or
read-honu-syntax. In addition, a special-read procedure can
be triggered by calls to read-honu,
read-honu/recursive, read-honu-syntax,
read-honu-syntax/recursive, read-char-or-special,
or by the context of read-bytes-avail!,
read-bytes-avail!*, read-bytes-avail!, and
peek-bytes-avail!*.
Optional arities for reader-macro and special-result procedures allow
them to distinguish reads via read, etc. from reads via
read-syntax, etc. in the case that the source value is
#f and no other location information is available.
Procedure arguments
A reader-macro procedure must accept six arguments, and it can
optionally accept two arguments. The first two arguments are always
the character that triggered the reader macro and the input port for
reading. When the reader macro is triggered by read-syntax
(or read-syntax/recursive), the procedure is passed four
additional arguments that represent a source location. When the
reader macro is triggered by read (or
read/recursive), the procedure is passed only two arguments
if it accepts two arguments, otherwise it is passed six arguments
where the last four are all #f.
A #reader-loaded procedure accepts the same arguments as
either read or read-syntax, depending on whether
the procedure was loaded through read, etc. or through
read-syntax, etc.
A special-result procedure must accept four arguments, and it can
optionally accept zero arguments. When the special read is triggered
by read-syntax (or read-honu-syntax,
read-syntax/recursive, etc.), the procedure is passed four
arguments that represent a source location. When the special read is
triggered by read (or read-char-or-special,
read-honu, read/syntax, etc.), the procedure is
passed no arguments if it accepts zero arguments, otherwise it is
passed four arguments that are all #f.
Procedure result
When a reader-extension procedure is called in syntax-reading mode
(via read-syntax, etc.), it should generally return a syntax
object that has no lexical context (e.g., a syntax object created
using with datum->syntax-object#f as the first
argument and with the given location information as the third
argument). Another possible result is a special-comment value (see
section 11.2.9.1). If the procedure's result is not a syntax
object and not a special-comment value, it is converted to one using
.datum->syntax-object
When a reader-extension procedure is called in non-syntax-reading
modes, it should generally not return a syntax object. If a syntax
object is returned, it is converted to a plain value using
.syntax-object->datum
In either context, when the result from a reader-extension procedure
is a special-comment value (see section 11.2.9.1), then
read, read-syntax, etc. treat the value as a
delimiting comment and otherwise ignore it.
Also in either context, the result may be copied to prevent mutation to pairs, vectors, or boxes before the read result is completed, and to support the construction of graphs with cycles. Mutable pairs, boxes, and vectors are copied, along with any pairs, boxes, or vectors that lead to such mutable values, to placeholders produced by a recursive read (see section 11.2.9.2), or to references of a shared value. Graph structure (including cycles) is preserved in the copy.
11.2.9.1 Special Comments
(make-special-comment v) creates a special-comment value that
encapsulates v. The read, read-syntax, etc.
procedures treat values constructed with
make-special-comment as delimiting whitespace when returned
by a reader-extension procedure (see section 11.2.9).
(special-comment? v) returns #t if
v is the result of make-special-comment, #f
otherwise.
(special-comment-value sc) returns the value encapsulated by the
special-comment value sc. This value is never used directly by
a reader, but it might be used by the context of a
read-char-or-special, etc. call that detects a special
comment.
11.2.9.2 Recursive Reads
(read/recursive [input-port char-or-false readtable]) is similar
to calling , but it is normally used during the dynamic
extent of readread within a reader-extension procedure (see
section 11.2.9). The main effect of using
read/recursive instead of read is that
graph-structure annotations (see section 11.2.5.1) in the nested
read are considered part of the overall read. Since the result is wrapped in a
placeholder, however, it is not directly inspectable.
If char-or-false is provided and not #f, it is
effectively prefixed to the beginning of input-port's stream
for the read. (To prefix multiple characters, use
input-port-append from MzLib's port library; see
Chapter 32
in PLT MzLib: Libraries Manual.)
The readtable argument, which defaults to
(current-readtable), is used for top-level parsing to
satisfy the read request; recursive parsing within the read (e.g., to
read the elements of a list) instead uses the current readtable as
determined by the current-readtable parameter. A reader
macro might call read/recursive with a character and
readtable to effectively invoke the readtable's behavior for the
character. If readtable is #f, the default readtable
is used for top-level parsing.
When called within the dynamic extent of read, the
read/recursive procedure produces either an opaque
placeholder value, a special-comment value, or an end-of-file. The
result is a special-comment value (see section 11.2.9.1)
when the input stream's first non-whitespace content parses as a
comment. The result is end-of-file when read/recursive
encounters an end-of-file. Otherwise, the result is a placeholder
that protects graph references that are not yet resolved. When this
placeholder is returned within an S-expression that is produced by
any reader-extension procedure (see section 11.2.9) for the
same outermost , it will be replaced with the actual
read value before the outermost readread returns.
(read-syntax/recursive [source-name-v input-port char-or-false readtable])
is analogous to calling read/recursive, but the resulting
value encapsulates S-expression structure with source-location
information. As with read/recursive, when
read-syntax/recursive is used within the dynamic extent of
read-syntax, the result of from
read-syntax/recursive is either a special-comment value,
end-of-file, or opaque graph-structure
placeholder (not a syntax object). The placeholder can be embedded
in an S-expression or syntax object returned by a reader macro, etc.,
and it will be replaced with the actual syntax object before the
outermost read-syntax returns.
Using read/recursive within the dynamic extent of
read-syntax does not allow graph structure for reading to be
included in the outer read-syntax parsing, and neither does
using read-syntax/recursive within the dynamic extent of
read. In those cases, read/recursive and
read-syntax/recursive produce results like read and
read-syntax.
See section 11.2.8 for an extended example that uses
read/recursive and read-syntax/recursive.
11.2.10 Customizing the Printer through Custom-Write Procedures
The built-in
prop:custom-write
structure type property associates a procedures to a structure
type. The procedure is used by the default printer to
display or write (or print) instances of
the structure type.
See section 4.4 for general information on structure type properties.
The procedure for a prop:custom-write value takes three
arguments: the structure to be printed, the target port, and a
boolean that is #t for write mode and #f
for display mode. The procedure should print the value to
the given port using write, display,
fprintf, write-special, etc.
The write handler, display handler, and print handler are specially
configured for a port given to a custom-write procedure. Printing to
the port through display, write, or print
prints a value recursively with sharing annotations. To avoid a
recursive print (i.e., to print without regard to sharing with a
value currently being printed), print instead to a string or pipe and
transfer the result to the target port using write-string
and write-special. To recursively print but to a port other
than the one given to the custom-write procedure, copy the given
port's write handler, display handler, and print handler to the other
port.
The port given to a custom-write handler is not necessarily the actual target port. In particular, to detect cycles and sharing, the printer invokes a custom-write procedure with a port that records recursive prints, and does not retain any other output.
Recursive print operations may trigger an escape from the call to the custom-write procedure (e.g., for pretty-printing where a tentative print attempt overflows the line, or for printing error output of a limited width).
The following example definition of a tuple type includes
custom-write procedures that print the tuple's list content using
angle brackets in mode and no brackets in
write mode. Elements of the tuple are printed recursively,
so that graph and cycle structure can be represented.
display
(define (tuple-print tuple port write?) (when write? (write-string "<" port)) (let ([l (tuple-ref tuple 0)]) (unless (null?l) ((if write?writedisplay) (carl) port) (for-each(lambda (e) (write-string ", " port) ((if write?writedisplay)eport)) (cdrl)))) (when write? (write-string ">" port))) (define-values (s:tuple make-tuple tuple? tuple-ref tuple-set!) (make-struct-type'tuple #f 1 0 #f (list(consprop:custom-write tuple-print)))) (display(make-tuple '(1 2 "a"))) ; prints1, 2, a(let ([t (make-tuple (list1 2 "a"))]) (set-car!(tuple-ref t 0) t) (writet)) ; prints#0=<#0#, 2, "a">
11.3 Filesystem Utilities
MzScheme provides many operations for accessing and modifying filesystems in a (mostly) platform-independent manner. Additional filesystem utilities are in MzLib; see also Chapter 17 in PLT MzLib: Libraries Manual.
11.3.1 Paths
The format of a filesystem path varies across platforms. For example,
under Unix, directories are separated by ``/'' while Windows uses
both ``/'' and ``\''. (See section 20 for more
information on Windows paths in MzScheme.) Furthermore, for most Unix
filesystems, the true name of a file is a byte string, but users
prefer to see the bytes decoded in a locale-specific way when the
filename is printed. MzScheme therefore provides a path
datatype for managing filesystem paths, and procedures such
as build-path, path->string,
and bytes->path for manipulating paths. Two paths
are when their byte-string representations
are equal?.equal?
When a MzScheme procedure takes a filesystem path as an argument, the
path can be provided either as a string or as an instance of the
path datatype. If a string is provided, it is converted to a
path using string->path. A MzScheme procedure that generates
a filesystem path always generates a path value.
Most MzScheme primitives that take path perform an expansion on the path before using it. (Procedures that build paths or merely check the form of a path do not perform this expansion.) Under Unix and Mac OS X, a user directory specification using ``~'' is expanded and multiple adjacent slashes are replaced with a single slash.35 Under Windows, paths that start \\?\, certain redundant backslashes are removed; see section 20 for more information. Under Windows for other paths, multiple slashes are converted to single slashes (except at the beginning of a shared folder name), a slash is inserted after the colon in a drive specification if it is missing, and trailing spaces and periods are removed from the last path element unless that element consists of only spaces and periods.
A path string (or byte string) cannot be empty, and it cannot contain a nul
character or byte. When an empty string or a string containing nul is
provided as a path to any procedure except ,
absolute-path?, or relative-path? the
complete-path?exn:fail:contract exception is raised.
The basic path utilities are as follows:
(path?v)returns#tifvis a path value (not a string),#fotherwise.(path-string?v)returns#tifvis either a path value or a non-empty string without nul characters,#fotherwise.(string->pathstring)produces a path whose byte-string name is(; see section 3.6 for more information onstring->bytes/localestring (char->integer#\?)). Beware that the current locale might not encode every string, in which casestring->bytes/localestring->pathcan produce the same path for differentstrings.(bytes->pathbytes)produces a path whose byte-string name isbytes.(path->stringpath)produces a string that representspathby decodingpath's byte-string name using the current locale's encoding; ``?'' is used in the result string where encoding fails, and if the encoding result is the empty string, then the result is"?". In addition, under Windows, if the path is relative and the byte-string version of the path starts with \\?\REL, this prefix and the immediately following backslashes (one or two) are removed from the resulting string. The resulting string is suitable for displaying to a user, string-ordering comparisons, etc., but it is not suitable for re-creating the path throughstring->path, since decoding and re-encoding the path's byte string may lose information.(path->bytespath)producespath's byte string representation. No information is lost in this translation, so that(always produces a path is that isbytes->path(path->bytespath))toequal?path. For any reasonable locale, consecutive ASCII characters in the printed form ofpathare mapped to consecutive byte values inpaththat match each character's code-point value, and a leading or trailing ASCII character is mapped to a leading or trailing byte, respectively.(build-pathbase-path sub-path···)creates a path given a base path and any number of sub-path extensions. Ifbase-pathis an absolute path, the result is an absolute path; ifbaseis a relative path, the result is a relative path. Eachsub-pathmust be either a relative path, a directory name, the symbol'up(indicating the relative parent directory), or the symbol'same(indicating the relative current directory). Under Windows, ifbase-pathis a drive specification (with or without a trailing slash) the firstsub-pathcan be an absolute (driveless) path. The lastsub-pathcan be a filename.Under Windows, trailing spaces and periods are removed from the last element of
base-pathand all but the lastsub-path(unless the element consists of only spaces and peroids), except for those that start with \\?\. Ifbase-pathstarts \\?\, then after each non-\\?\REL\sub-pathis added, all slashes in the addition are converted to backslashes, multiple consecutive backslashes are converted to a single backslash, added . elements are removed, and added .. elements are removed along with the preceding element; these conversions are not performed on the originalbase-pathpart of the result or on any \\?\REL\sub-path. In other cases under Windows, a backslash may be added or removed before combining paths to avoid changing the root meaning of the path (e.g., combining //x and y produces /x/y, because //x/y would be a UNC path instead of a drive-relative path).Each
sub-pathandbase-pathcan optionally end in a directory separator. If the lastsub-pathends in a separator, it is included in the resulting path.If
base-pathorsub-pathis an illegal path string (because it is empty or contains a nul character), theexn:fail:contractexception is raised.The
procedure builds a path without checking the validity of the path or accessing the filesystem.build-pathThe following examples assume that the current directory is /home/joeuser for Unix examples and C:\Joe's Files for Windows examples.
(define p1 (
build-path(current-directory) "src" "scheme")) ; Unix: p1 =>"/home/joeuser/src/scheme"; Windows: p1 =>"C:\Joe's Files\src\scheme"(define p2 (build-path'up 'up "docs" "MzScheme")) ; Unix: p2 =>"../../docs/MzScheme"; Windows: p2 =>"..\..\docs\MzScheme"(build-pathp2 p1) ; Unix and Windows: raisesexn:fail:contractbecausep1is absolute (build-pathp1 p2) ; Unix: =>"/home/joeuser/src/scheme/../../docs/MzScheme"; Windows: =>"C:\Joe's Files\src\scheme\..\..\docs\MzScheme"(absolute-path?path)returns#tifpathis an absolute path,#fotherwise. Ifpathis not a legal path string (e.g., it contains a nul character),#fis returned. This procedure does not access the filesystem.(relative-path?path)returns#tifpathis a relative path,#fotherwise. Ifpathis not a legal path string (e.g., it contains a nul character),#fis returned. This procedure does not access the filesystem.(complete-path?path)returns#tifpathis a completely determined path (not relative to a directory or drive),#fotherwise. Note that under Windows, an absolute path can omit the drive specification, in which case the path is neither relative nor complete. Ifpathis not a legal path string (e.g., it contains a nul character),#fis returned. This procedure does not access the filesystem.(path->complete-pathpath[base-path])returnspathas a complete path. Ifpathis already a complete path, it is returned as the result. Otherwise,pathis resolved with respect to the complete pathbase-path. Ifbase-pathis omitted,pathis resolved with respect to the current directory. Ifbase-pathis provided and it is not a complete path, theexn:fail:contractexception is raised. This procedure does not access the filesystem.(resolve-pathpath)expandspathand returns a path that references the same file or directory aspath. Under Unix and Mac OS X, ifpathis a soft link to another path, then the referenced path is returned (this may be a relative path with respect to the directory owningpath) otherwisepathis returned (after expansion).(expand-pathpath)returns the expanded version ofpath(as described at the beginning of this section). The filesystem might be accessed, but the source or expanded path might be a non-existent path.(simplify-pathpath[use-filesystem?])eliminates up-directory (``..'') and same-directory (``.'') indicators inpath, such that the result accesses the same file or directory (if it exists) aspath; under Windows, ifpathstarts \\?\, no conversion is performed. If no indicators are inpath, thenpathis returned. Otherwise, whenuse-filesystem?is true (the default), a complete path is returned; ifpathis relative, it is resolved with respect to the current directory, and up-directory indicators are removed taking into account soft links (so that the resulting path refers to the same directory as before). Whenuse-filesystem?is#f, then up-directory indicators are removed by deleting a preceding path element, and the result can be a relative path with up-directory indicators remaining at the beginning of the path; similarly, the result can be the same as(if eliminating up-directory indicators leaves only same-directory indicators. For a complete path, up-directory indicators are dropped when they refer to the parent of a root directory. The filesystem might be accessed whenbuild-path'same)use-filesystem?is true, but the source or expanded path might be a non-existent path. Ifpathcannot be simplified due to a cycle of links, theexn:fail:filesystemexception is raised (but a successfully simplified path may still involve a cycle of links if the cycle did not inhibit the simplification).(normal-case-pathpath)returnspathwith ``normalized'' case letters. Under Unix and Mac OS X and Mac OS X, this procedure always returns the input path, because filesystems for these platforms can be case-sensitive. Under Windows, if the path does not start \\?\, the resulting string uses only lowercase letters, based on the current locale. In addition, under Windows when the path does not start \\?\, all forward slashes (``/'') are converted to backward slashes (``\''), and trailing spaces and periods are removed. This procedure does not access the filesystem.(split-pathpath)deconstructspathinto a smaller path and an immediate directory or file name. Three values are returned (see section 2.2):baseis eithera path,
'relativeifpathis an immediate relative directory or filename, or#fifpathis a root directory.
nameis eithera directory-name path,
a filename,
'upif the last part ofpathspecifies the parent directory of the preceding path (e.g., ``..'' under Unix), or'sameif the last part ofpathspecifies the same directory as the preceding path (e.g., ``.'' under Unix).
must-be-dir?is#tifpathexplicitly specifies a directory (e.g., with a trailing separator),#fotherwise. Note thatmust-be-dir?does not specify whethernameis actually a directory or not, but whetherpathsyntactically specified a directory.
If
baseis#f, thennamecannot be'upor'same. This procedure does not access the filesystem.Under Windows, splitting a path that does not start with \\?\ can produce parts that start with \\?\. For example, splitting C:/x /aux/ produces \\?\C:\x \ and \\?\REL\\aux; the \\?\ is needed in these cases to preserve a trailing space after x and to avoid referring to the AUX devide instead of an aux file.
(path-replace-suffixpath string-or-bytes)returns a path that is the same aspath, except that the suffix is changed tostring-or-bytes. Ifpathas no suffix, thenstring-or-bytesis added to the path. A suffix is defined as a period followed by any number of non-period characters/bytes at the end of the pathname. Ifpathrepresents a root, theexn:fail:contractexception is raised.
11.3.2 Locating Paths
The find-system-path and find-executable-path
procedures locate useful files and directories:
(find-system-pathkind-symbol)returns a machine-specific path for a standard type of path specified bykind-symbol, which must be one of the following:'home-dir-- the current user's home directory.Under Unix and Mac OS X, this directory is determined by expanding the path ~, which is expanded by first checking for a HOME environment variable. If none is defined, the USER and LOGNAME environment variables are consulted (in that order) to find a user name, and then system files are consulted to locate the user's home directory.
Under Windows, the user's home directory is the user-specific profile directory as determined by the Windows registry. If the registry cannot provide a directory for some reason, the value of the USERPROFILE environment variable is used instead, as long as it refers to a directory that exists. If USERPROFILE also fails, the directory is the one specified by the HOMEDRIVE and HOMEPATH environment variables. If those environment variables are not defined, or if the indicated directory still does not exist, the directory containing the MzScheme executable is used as the home directory.
'pref-dir-- the standard directory for storing the current user's preferences. Under Unix, the directory is .plt-scheme in the user's home directory. Under Windows, it is PLT Scheme in the user's application-data folder as specified by the Windows registry; the application-data folder is usually Application Data in the user's profile directory. Under Mac OS X, it is Library/Preferences in the user's home directory. This directory might not exist.'pref-file-- a file that contains a symbol-keyed association list of preference values. The file's directory path always matches the result returned for'pref-dir. The file name is plt-prefs.ss under Unix and Windows, and it is org.plt-scheme.prefs.ss under Mac OS X. The file's directory might not exist. See alsoget-preferencein Chapter 17 in PLT MzLib: Libraries Manual.'temp-dir-- the standard directory for storing temporary files. Under Unix and Mac OS X, this is the directory specified by the TMPDIR environment variable, if it is defined.'init-dir-- the directory containing the initialization file used by stand-alone MzScheme application. It is the same as the current user's home directory.'init-file-- the file loaded at start-up by the stand-alone MzScheme application. The directory part of the path is the same path as returned for'init-dir. The file name is platform-specific:'addon-dir-- a directory for installing PLT Scheme extensions. It's the same as'pref-dir, except under Mac OS X, where it's Library/PLT Scheme in the user's home directory. This directory might not exist.'doc-dir-- the standard directory for storing the current user's documents. It's the same as'home-dirunder Unix and Mac OS X. Under Windows, it is the user's documents folder as specified by the Windows registry; the documents folder is usually My Documents in the user's home directory.'desk-dir-- the directory for the current user's desktop. Under Unix, it's the same as'home-dir. Under Windows, it is the user's desktop folder as specified by the Windows registry; the documents folder is usually Desktop in the user's home directory. Under Mac OS X, it is the desktop directory, which is specifically ~/Desktop under Mac OS X.'sys-dir-- the directory containing the operating system for Windows. Under Unix and Mac OS X, the result is"/".'exec-file-- the path of the MzScheme executable as provided by the operating system for the current invocation.36'run-file-- the path of the current executable; this may be different from result for'exec-filebecause an alternate path was provided through a --name or -N command-line flag to stand-alone MzScheme (or MrEd), or because an embedding executable installed an alternate path. In particular a ``launcher'' script created bymake-mzscheme-launchersets this path to the script's path. In the stand-alone MzScheme application, this path is also bound initially toprogram.'collects-dir-- a path to the main collection of libraries (see section 16). If this path is relative, it's relative to the directory of(. This path is normally embedded in a stand-alone MzScheme executable, but it can be overridden by the --collects or -X command-line flag.find-system-path'exec-file)'orig-dir-- the current directory at start-up, which can be useful in converting a relative-path result from(orfind-system-path'exec-file)(to a complete path.find-system-path'run-file)
(path-list-string->path-liststring default-path-list)parses a string or byte string containing a list of paths, and returns a list of path strings. Under Unix and Mac OS X, paths in a path list are separated by a colon (``:''); under Windows, paths are separated by a semi-colon (``;''). Whenever the path list contains an empty path, the listdefault-path-listis spliced into the returned list of paths. Parts ofstringthat do not form a valid path are not included in the returned list.(find-executable-pathprogram-sub-path related-sub-path)finds a path for the executableprogram-sub-path, returning#fif the path cannot be found.If
related-sub-pathis not#f, then it must be a relative path string, and the path found forprogram-sub-pathmust be such that the file or directoryrelated-sub-pathexists in the same directory as the executable. The result is then the full path for the foundrelated-sub-path, instead of the path for the executable.This procedure is used by MzScheme (as a stand-alone executable) to find the standard library collection directory (see Chapter 16). In this case,
programis the name used to start MzScheme andrelatedis"collects". Therelated-sub-pathargument is used because, under Unix and Mac OS X,program-sub-pathmay involve to a sequence of soft links; in this case,related-sub-pathdetermines which link in the chain is relevant.If
program-sub-pathhas a directory path, exists as a file or link to a file, andrelated-sub-pathis not#f,determines whetherfind-executable-pathrelated-sub-pathexists relative to the directory ofprogram-sub-path. If so, the complete path forprogram-sub-pathis returned. Otherwise, ifprogram-sub-pathis a link to another file path, the destination directory of the link is checked forrelated-sub-path. Further links are inspected untilrelated-sub-pathis found or the end of the chain of links is reached.If
program-sub-pathis a pathless name,gets the value of the PATH environment variable; if this environment variable is defined,find-executable-pathtries each path in PATH as a prefix forfind-executable-pathprogram-sub-pathusing the search algorithm described above for path-containingprogram-sub-paths. If the PATH environment variable is not defined,program-sub-pathis prefixed with the current directory and used in the search algorithm above. (Under Windows, the current directory is always implicitly the first item in PATH, sochecks the current directory first under Windows.)find-executable-path
11.3.3 Files
The file management utilities are:
(file-exists?path)returns#tif a file (not a directory)pathexists,#fotherwise.37(link-exists?path)returns#tif a linkpathexists (Unix and Mac OS X),#fotherwise. Note that the predicatesorfile-exists?work on the final destination of a link or series of links, whiledirectory-exists?only follows links to resolve the base part oflink-exists?path(i.e., everything except the last name in the path). This procedure never raises theexn:fail:filesystemexception.(delete-filepath)deletes the file with pathpathif it exists, returning void if a file was deleted successfully, otherwise theexn:fail:filesystemexception is raised. Ifpathis a link, the link is deleted rather than the destination of the link.(rename-file-or-directoryold-path new-path[exists-ok?])renames the file or directory with pathold-path-- if it exists -- to the pathnew-path. If the file or directory is renamed successfully, void is returned, otherwise theexn:fail:filesystemexception is raised.This procedure can be used to move a file/directory to a different directory (on the same disk) as well as rename a file/directory within a directory. Unless
exists-ok?is provided as a true value,new-pathcannot refer to an existing file or directory. Even ifexists-ok?is true,new-pathcannot refer to an existing file whenold-pathis a directory, and vice versa. (Ifnew-pathexists and is replaced, the replacement is atomic in the filesystem, except under Windows 95, 98, or Me. However, the check for existence is not included in the atomic action, which means that race conditions are possible whenexists-ok?is false or not supplied.)If
old-pathis a link, the link is renamed rather than the destination of the link, and it counts as a file for replacing any existingnew-path.(file-or-directory-modify-secondspath[secs-n fail-thunk])returns the file or directory's last modification date as platform-specific seconds (see also section 15.1) whensecs-nis not provided or is#f.38 Ifsecs-nis provided and not#f, the access and modification times ofpathare set to the given time. On error (e.g., if no such file exists),fail-thunkis called if it is provided, otherwise theexn:fail:filesystemexception is raised(file-or-directory-permissionspath)returns a list containing'read,'write, and/or'executefor the given file or directory path. On error (e.g., if no such file exists), theexn:fail:filesystemexception is raised. Under Unix and Mac OS X, permissions are checked for the current effective user instead of the real user.(file-sizepath)returns the (logical) size of the specified file in bytes. (Under Mac OS X, this size excludes the resource-fork size.) On error (e.g., if no such file exists), theexn:fail:filesystemexception is raised.(copy-filesrc-path dest-path)creates the filedest-pathas a copy ofsrc-path. If the file is successfully copied, void is returned, otherwise theexn:fail:filesystemexception is raised. Ifdest-pathalready exists, the copy will fail. File permissions are preserved in the copy. Under Mac OS X, the resource fork is also preserved in the copy. Ifsrc-pathrefers to a link, the target of the link is copied, rather than the link itself.(make-file-or-directory-linkto-path path)creates a linkpathtoto-pathunder Unix and Mac OS X. The creation will fail ifpathalready exists. Theto-pathneed not refer to an existing file or directory. If the link is created successfully, void is returned, otherwise theexn:fail:filesystemexception is raised. Under Windows, theexn:fail:unsupportedexception is raised always.
11.3.4 Directories
The directory management utilities are:
(returns the current directory andcurrent-directory)(current-directorysets the current directory topath)path. This procedure is actually a parameter, as described in section 7.9.1.1.(current-drive)returns the current drive name under Windows. For other platforms, theexn:fail:unsupportedexception is raised. The current drive is always the drive of the current directory.(directory-exists?path)returns#tifpathrefers to a directory,#fotherwise.(make-directorypath)creates a new directory with the pathpath. If the directory is created successfully, void is returned, otherwise theexn:fail:filesystemexception is raised.(delete-directorypath)deletes an existing directory with the pathpath. If the directory is deleted successfully, void is returned, otherwise theexn:fail:filesystemexception is raised.(, as described in the previous section, renames directories.rename-file-or-directoryold-pathnew-pathexists-ok?)(, as described in the previous section, gets directory dates.file-or-directory-modify-secondspath)(, as described in the previous section, gets directory permissions.file-or-directory-permissionspath)(directory-list[path])returns a list of all files and directories in the directory specified bypath. Ifpathis omitted, a list of files and directories in the current directory is returned.(filesystem-root-list)returns a list of all current root directories. Obtaining this list can be particularly slow under Windows.
11.4 Networking
MzScheme supports networking with the TCP and UDP protocols.
11.4.1 TCP
For information about TCP in general, see TCP/IP Illustrated, Volume 1 by W. Richard Stevens.
(tcp-listenport-k[max-allow-wait-k reuse? hostname-string-or-false])creates a ``listening'' server on the local machine at the specified port number (whereport-kis an exact integer between1and65535inclusive). Themax-allow-wait-kargument determines the maximum number of client connections that can be waiting for acceptance. (Whenmax-allow-wait-kclients are waiting acceptance, no new client connections can be made.) The default value formax-allow-wait-kargument is4.If the
reuse?argument is true, thenwill create a listener even if the port is involved in a TIME_WAIT state. Such a use oftcp-listenreuse?defeats certain guarantees of the TCP protocol; see Stevens's book for details. Furthermore, on many modern platforms, a true value forreuse?overrides TIME_WAIT only if the listener was previously created with a true value forreuse?. The default forreuse?is#f.If
hostname-string-or-falseis#f(the default), then the listener accepts connections to all of the listening machine's addresses.39 Otherwise, the listener accepts connections only at the interface(s) associated with the given hostname. For example, providing"127.0.0.1"ashostname-string-or-falsecreates a listener that accepts only connections to"127.0.0.1"(the loopback interface) from the local machine.The return value of
is a TCP listener value. This value can be used in future calls totcp-listen,tcp-accept, andtcp-accept-ready?. Each new TCP listener value is placed into the management of the current custodian (see section 9.2).tcp-closeIf the server cannot be started by
, thetcp-listenexn:fail:networkexception is raised.(tcp-connecthostname-string port-k[local-hostname-string-or-false local-port-k-or-false])attempts to connect as a client to a listening server. Thehostname-stringargument is the server host's Internet address name40 (e.g.,"www.plt-scheme.org"), andport-k(an exact integer between1and65535) is the port where the server is listening.The optional
local-hostname-string-or-falseandlocal-port-k-or-falsespecify the client's address and port. If both are#f(the default), the client's address and port are selected automatically. Iflocal-hostname-string-or-falseis not#f, thenlocal-port-k-or-falsemust be non-#f. Iflocal-port-k-or-falseis non-#fandlocal-hostname-string-or-falseis#f, then the given port is used but the address is selected automatically.Two values (see section 2.2) are returned by
: an input port and an output port. Data can be received from the server through the input port and sent to the server through the output port. If the server is a MzScheme process, it can obtain ports to communicate to the client withtcp-connect. These ports are placed into the management of the current custodian (see section 9.2); if a custodian shuts down the connection, it must abort the connection.tcp-acceptInitially, the returned input port is block-buffered, and the returned output port is block-buffered. Change the buffer mode using
file-stream-buffer-mode(see section 11.1.6).Both of the returned ports must be closed to terminate the TCP connection. When both ports are still open, closing the output port with
close-output-portsends a TCP close to the server (which is seen as an end-of-file if the server reads the connection through a port). In contrast,tcp-abandon-port(see below) closes the output port, but does not send a TCP close until the input port is also closed.If a connection cannot be established by
, thetcp-connectexn:fail:networkexception is raised.(tcp-connect/enable-breakhostname-string port-k[local-hostname-string local-port-k])is like, but breaking is enabled (see section 6.6) while trying to connect. If breaking is disabled whentcp-connectis called, then either ports are returned or thetcp-connect/enable-breakexn:breakexception is raised, but not both.(tcp-accepttcp-listener)accepts a client connection for the server associated withtcp-listener. Thetcp-listenerargument is a TCP listener value returned by. If no client connection is waiting on the listening port, the call totcp-listenwill block. (See alsotcp-accept, below.)tcp-accept-ready?Two values (see section 2.2) are returned by
: an input port and an output port. Data can be received from the client through the input port and sent to the client through the output port. These ports are placed into the management of the current custodian (see section 9.2).tcp-acceptInitially, the returned input port is block-buffered, and the returned output port is block-buffered. Change the buffer mode using
file-stream-buffer-mode(see section 11.1.6).Both of the returned ports must be closed to terminate the connection. When both ports are still open, closing the output port with
close-output-portsends a TCP close to the client (which is seen as an end-of-file if the client reads the connection through a port). In contrast,tcp-abandon-port(see below) closes the output port, but does not send a TCP close until the input port is also closed.If a connection cannot be accepted by
, or if the listener has been closed, thetcp-acceptexn:fail:networkexception is raised.(tcp-accept-ready?tcp-listener)tests whether an unaccepted client has connected to the server associated withtcp-listener. Thetcp-listenerargument is a TCP listener value returned by. If a client is waiting, the return value istcp-listen#t, otherwise it is#f. A client is accepted with theprocedure, which returns ports for communicating with the client and removes the client from the list of unaccepted clients.tcp-acceptIf the listener has been closed, the
exn:fail:networkexception is raised.(tcp-accept/enable-breaktcp-listener)is like, but breaking is enabled (see section 6.6) while trying to accept a connection. If breaking is disabled whentcp-acceptis called, then either ports are returned or thetcp-accept/enable-breakexn:breakexception is raised, but not both.(tcp-closetcp-listener)shuts down the server associated withtcp-listener. Thetcp-listenerargument is a TCP listener value returned by. All unaccepted clients receive an end-of-file from the server; connections to accepted clients are unaffected.tcp-listenIf the listener has already been closed, the
exn:fail:networkexception is raised.The listener's port number may not become immediately available for new listeners (with the default
reuse?argument of). For further information, see Stevens's explanation of the TIME_WAIT TCP state.tcp-listen(tcp-listener?v)returns#tifvis a TCP listener value created by,tcp-listen#fotherwise.(tcp-accept-evttcp-listener)returns a synchronizable event (see section 7.7) that is in a blocking state whentcp-acceptontcp-listenerwould block. If the event is chosen in a synchronization, the result is a list of two items, which correspond to the two results oftcp-accept. (If the event is not chosen, no connections are accepted.)(tcp-abandon-porttcp-port)is likeclose-output-portorclose-input-port(depending on whethertcp-portis an input or output port), but iftcp-portis an output port and its associated input port is not yet closed, then the other end of the TCP connection does not receive a TCP close message until the input port is also closed.41(tcp-addressestcp-port)returns two strings. The first string is the Internet address for the local machine a viewed by the given TCP port's connection.42 The second string is the Internet address for the other end of the connection.If the given port has been closed, the
exn:fail:networkexception is raised.(tcp-port?v)returns#tifvis a port returned bytcp-accept,tcp-connect,tcp-accept/enable-break, ortcp-connect/enable-break,#fotherwise.
11.4.2 UDP
For information about UDP in general, see TCP/IP Illustrated, Volume 1 by W. Richard Stevens (which discusses UDP in addition to TCP).
(udp-open-socket[family-hostname-string-or-false family-port-k-or-false])creates and returns a UDP socket to send and receive datagrams (broadcasting is allowed). Initially, the socket is not bound or connected to any address or port.If
family-hostname-string-or-falseorfamily-port-k-or-falseis provided and not#f, then the socket's protocol family is determined from these arguments. The socket is not bound to the hostname or port number. For example, the arguments might be the hostname and port to which messages will be sent through the socket, which ensures that the socket's protocol family is consistent with the destination. Alternately, the arguments might be the same as for a future call toudp-bind!, which ensures that the socket's protocol family is consistent with the binding. If neitherfamily-hostname-string-or-falsenorfamily-port-k-or-falseis provided as non-#f, then the socket's protocol family is IPv4.(udp-bind!udp-socket hostname-string-or-false port-k)binds an unboundudp-socketto the local port numberport-k(an exact integer between1and65535). The result is always void.If
hostname-string-or-falseis#f, then the socket accepts connections to all of the listening machine's IP addresses atport-k. Otherwise, the socket accepts connections only at the IP address associated with the given name. For example, providing"127.0.0.1"ashostname-string-or-falsetypically creates a listener that accepts only connections to"127.0.0.1"from the local machine.A socket cannot receive datagrams until it is bound to a local address and port. If a socket is not bound before it is used with a sending procedure
udp-send,udp-send-to, etc., the sending procedure binds the socket to a random local port. Similarly, if an event fromudp-send-evtorudp-send-to-evtis chosen for a synchronization (see section 7.7), the socket is bound; if the event is not chosen, the socket may or may not become bound. The binding of a bound socket cannot be changed.If
udp-socketis already bound or closed, theexn:fail:networkexception is raised.(udp-connect!udp-socket hostname-string-or-false port-k-or-false)connects the socket to the indicated remote address and port ifhostname-string-or-falseis a string andport-k-or-falseis an exact integer between1and65535. The result is always void.If
hostname-string-or-falseis#f, thenport-k-or-falsealso must be#f, and the port is disconnected (if connected). If one ofhostname-string-or-falseorport-k-or-falseis#fand the other is not, theexn:fail:contractexception is raised.A connected socket can be used with
udp-send(notudp-send-to), and it accepts datagrams only from the connected address and port. A socket need not be connected to receive datagrams. A socket can be connected, re-connected, and disconnected any number of times.If
udp-socketis closed, theexn:fail:networkexception is raised.(udp-send-toudp-socket hostname-address port-k bytes[start-k end-k])sends(subbytesas a datagram from the unconnectedbytesstart-kend-k)udp-socketto the socket at the remote machinehostname-addresson the portport-k. Theudp-socketneed not be bound or connected; if it is not bound,udp-send-tobinds it to a random local port. If the socket's outgoing datagram queue is too full to support the send,udp-send-toblocks until the datagram can be queued. The result is always void.The optional
start-kargument defaults to0, and the optionalend-kargument defaults to the length ofbytes. Ifstart-kis greater than the length ofbytes, or ifend-kis less thanstart-kor greater than the length ofbytes, theexn:fail:contractexception is raised.If
udp-socketis closed or connected, theexn:fail:networkexception is raised.(udp-sendudp-socket bytes[start-k end-k])is likeudp-send-to, except thatudp-socketmust be connected, and the datagram goes to the connection target. Ifudp-socketis closed or unconnected, theexn:fail:networkexception is raised.(udp-send-to*udp-socket hostname-address port-k bytes[start-k end-k])is likeudp-send-to, except that it never blocks; if the socket's outgoing queue is too full to support the send,#fis returned, otherwise the datagram is queued and the result is#t.(udp-send*udp-socket bytes[start-k end-k])is likeudp-send, except that (likeudp-send-to) it never blocks and returns#for#t.(udp-send-to/enable-breakudp-socket hostname-address port-k bytes[start-k end-k])is likeudp-send-to, but breaking is enabled (see section 6.6) while trying to send the datagram. If breaking is disabled whenudp-send-to/enable-breakis called, then either the datagram is sent or theexn:breakexception is raised, but not both.(udp-send/enable-breakudp-socket bytes[start-k end-k])is likeudp-send, except that breaks are enabled likeudp-send-to/enable-break.(udp-receive!udp-socket mutable-bytes[start-k end-k])accepts up toend-k-start-kbytes ofudp-socket's next incoming datagram intomutable-bytes, writing the datagram bytes starting at positionstart-kwithinmutable-bytes. Theudp-socketmust be bound to a local address and port (but need not be connected). If no incoming datagram is immediately available,udp-receive!blocks until one is available.Three values are returned: an exact integer that indicates the number of received bytes (between
0andend-k-start-k), a hostname string indicating the source address of the datagram, and an exact integer between1and65535indicating the source port of the datagram. If the received datagram is longer thanend-k-start-kbytes, the remainder is discarded.The optional
start-kargument defaults to0, and the optionalend-kargument defaults to the length ofmutable-bytes. Ifstart-kis greater than the length ofmutable-bytes, or ifend-kis less thanstart-kor greater than the length ofmutable-bytes, theexn:fail:contractexception is raised.(udp-receive!*udp-socket mutable-bytes[start-k end-k])is likeudp-receive!, except that it never blocks. If no datagram is available, the three result values are all#f.(udp-receive!/enable-breakudp-socket mutable-bytes[start-k end-k])is likeudp-receive!, but breaking is enabled (see section 6.6) while trying to receive the datagram. If breaking is disabled whenudp-receive!/enable-breakis called, then either a datagram is received or theexn:breakexception is raised, but not both.(udp-closeudp-socket)closesudp-socket, discarding unreceived datagrams. If the socket is already closed, theexn:fail:networkexception is raised.(udp?v)returns#tifvis a socket created byudp-open-socket,#fotherwise.(udp-bound?udp-socket)returns#tifudp-socketis bound to a local address and port,#fotherwise.(udp-connected?udp-socket)returns#tifudp-socketis connected to a remote address and port,#fotherwise.(udp-send-ready-evtudp-socket)returns a synchronizable event (see section 7.7) that is in a blocking state whenudp-send-toonudp-socketwould block.(udp-receive-ready-evtudp-socket)returns a synchronizable event (see section 7.7) that is in a blocking state whenudp-receive!onudp-socketwould block.(udp-send-to-evtudp-socket hostname-address port-k bytes[start-k end-k])returns a synchronizable event. The event is in a blocking state whenudp-sendonudp-socketwould block. Otherwise, if the event is chosen in a synchronization, data is sent as for(udp-send-to, and the synchronization result is void. (No bytes are sent if the event is not chosen.)udp-sockethostname-addressport-kbytesstart-kend-k)(udp-send-evtudp-socket bytes[start-k end-k])is likeudp-send-to-evt, except thatudp-socketmust be connected when the event is synchronized, and if the event is chosen in a synchronization, the datagram goes to the connection target. Ifudp-socketis closed or unconnected, theexn:fail:networkexception is raised during a synchronization attempt.(udp-receive!-evtudp-socket bytes[start-k end-k])returns a synchronizable event. The event is in a blocking state whenudp-receiveonudp-socketwould block. Otherwise, if the event is chosen in a synchronization, data is receive intobytesas for(udp-receive!, and the synchronization result is a list of three values, corresponding to the three results fromudp-socketbytesstart-kend-k)udp-receive!. (No bytes are received and thebytescontent is not modified if the event is not chosen.)
27 63 is
the same as (.char->integer #\?)
28 Flushing is performed by the default port
read handler (see section 11.2.6) rather than by
itself.read
29 This non-byte result is
not intended to return a character or ; in
particular, eofread-char raises an exception if it encounters a
non-byte from a port.
30 More precisely, the procedure is used by the default port read handler; see also section 11.2.6.
31 A
temporary string of size k is allocated while reading the
input, even if the size of the result is less than k
characters.
32 Only mid-stream s can be
committed. A eof when the port is exhausted does not
correspond to data in the stream.eof
33 Assuming that the current port display and write handlers are the default ones; see section 11.2.7 for more information.
34 The
port read handler is not used for read/recursive or
read-syntax/recursive.
35 Under Mac OS X, Finder aliases are zero-length files.
36 For MrEd, the executable path is the name of a MrEd executable.
37 Under Windows, reports
file-exists?#t for all variations of the special filenames (e.g.,
"LPT1", "x:/baddir/LPT1").
38 For FAT filesystems under Windows, directories do not have modification dates. Therefore, the creation date is returned for a directory (but the modification date is returned for a file).
39 MzScheme implements a listener with multiple
sockets, if necessary, to accomodate multiple addresses with
different protocol families. Under Linux,
if hostname-string-or-false maps to both IPv4 and IPv6
addresses, then the behavior depends on whether IPv6 is supported and
IPv6 sockets can be configured to listen to only IPv6 connections: if
IPv6 is not supported or IPv6 sockets are not configurable, then the
IPv6 addresses are ignored; otherwise, each IPv6 listener accepts
only IPv6 connections.
40 If hostname-string is associated with
multiple addresses, they are tried one at a time until a connection
succeeds. The name "localhost" generally specifies the local
machine.
41 The TCP protocol does not include a ``no longer
reading'' state on connections, so tcp-abandon-port is
equivalent to close-input-port on input TCP ports.
42 For most machines, the answer corresponds to the current machine's only Internet address. But when a machine serves multiple addresses, the result is connection-specific.